VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/isomakercmd.cpp@ 67864

Last change on this file since 67864 was 67864, checked in by vboxsync, 8 years ago

isomaker: doxygen updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 117.3 KB
Line 
1/* $Id: isomakercmd.cpp 67864 2017-07-07 17:49:07Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker Command.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsisomaker.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/buildconfig.h>
38#include <iprt/ctype.h>
39#include <iprt/file.h>
40#include <iprt/err.h>
41#include <iprt/getopt.h>
42#include <iprt/log.h>
43#include <iprt/mem.h>
44#include <iprt/message.h>
45#include <iprt/path.h>
46#include <iprt/rand.h>
47#include <iprt/stream.h>
48#include <iprt/string.h>
49#include <iprt/vfs.h>
50#include <iprt/formats/iso9660.h>
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56/** Maximum number of name specifiers we allow. */
57#define RTFSISOMAKERCMD_MAX_NAMES 8
58
59/** @name Name specifiers
60 * @{ */
61#define RTFSISOMAKERCMDNAME_PRIMARY_ISO RTFSISOMAKER_NAMESPACE_ISO_9660
62#define RTFSISOMAKERCMDNAME_JOLIET RTFSISOMAKER_NAMESPACE_JOLIET
63#define RTFSISOMAKERCMDNAME_UDF RTFSISOMAKER_NAMESPACE_UDF
64#define RTFSISOMAKERCMDNAME_HFS RTFSISOMAKER_NAMESPACE_HFS
65
66#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE RT_BIT_32(16)
67#define RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE RT_BIT_32(17)
68
69#define RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL RT_BIT_32(20)
70#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL RT_BIT_32(21)
71#define RTFSISOMAKERCMDNAME_UDF_TRANS_TBL RT_BIT_32(22)
72#define RTFSISOMAKERCMDNAME_HFS_TRANS_TBL RT_BIT_32(23)
73
74#define RTFSISOMAKERCMDNAME_MAJOR_MASK \
75 (RTFSISOMAKERCMDNAME_PRIMARY_ISO | RTFSISOMAKERCMDNAME_JOLIET | RTFSISOMAKERCMDNAME_UDF | RTFSISOMAKERCMDNAME_HFS)
76
77#define RTFSISOMAKERCMDNAME_MINOR_MASK \
78 ( RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE | RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL \
79 | RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE | RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL \
80 | RTFSISOMAKERCMDNAME_UDF_TRANS_TBL \
81 | RTFSISOMAKERCMDNAME_HFS_TRANS_TBL)
82AssertCompile((RTFSISOMAKERCMDNAME_MAJOR_MASK & RTFSISOMAKERCMDNAME_MINOR_MASK) == 0);
83/** @} */
84
85
86/*********************************************************************************************************************************
87* Structures and Typedefs *
88*********************************************************************************************************************************/
89typedef enum RTFSISOMAKERCMDOPT
90{
91 RTFSISOMAKERCMD_OPT_FIRST = 1000,
92
93 RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER,
94 RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE,
95 RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE,
96 RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION,
97 RTFSISOMAKERCMD_OPT_NAME_SETUP,
98 RTFSISOMAKERCMD_OPT_NO_JOLIET,
99 RTFSISOMAKERCMD_OPT_IMPORT_ISO,
100
101 RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY,
102 RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE,
103 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12,
104 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144,
105 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288,
106
107 /*
108 * Compatibility options:
109 */
110 RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID,
111 RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS,
112 RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE,
113 RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE,
114 RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT,
115 RTFSISOMAKERCMD_OPT_ALPHA_BOOT,
116 RTFSISOMAKERCMD_OPT_APPLE,
117 RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID,
118 RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES,
119 RTFSISOMAKERCMD_OPT_CHECK_SESSION,
120 RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID,
121 RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS,
122 RTFSISOMAKERCMD_OPT_DIR_MODE,
123 RTFSISOMAKERCMD_OPT_DVD_VIDEO,
124 RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID,
125 RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT,
126 RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE,
127 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG,
128 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE,
129 RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT,
130 RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT,
131 RTFSISOMAKERCMD_OPT_EXCLUDE_LIST,
132 RTFSISOMAKERCMD_OPT_FILE_MODE,
133 RTFSISOMAKERCMD_OPT_FORCE_RR,
134 RTFSISOMAKERCMD_OPT_GID,
135 RTFSISOMAKERCMD_OPT_GRAFT_POINTS,
136 RTFSISOMAKERCMD_OPT_GUI,
137 RTFSISOMAKERCMD_OPT_HFS_AUTO,
138 RTFSISOMAKERCMD_OPT_HFS_BLESS,
139 RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE,
140 RTFSISOMAKERCMD_OPT_HFS_CAP,
141 RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT,
142 RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE,
143 RTFSISOMAKERCMD_OPT_HFS_CREATOR,
144 RTFSISOMAKERCMD_OPT_HFS_DAVE,
145 RTFSISOMAKERCMD_OPT_HFS_DOUBLE,
146 RTFSISOMAKERCMD_OPT_HFS_ENABLE,
147 RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE,
148 RTFSISOMAKERCMD_OPT_HFS_EXCHANGE,
149 RTFSISOMAKERCMD_OPT_HFS_HIDE,
150 RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST,
151 RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION,
152 RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET,
153 RTFSISOMAKERCMD_OPT_HFS_MAC_NAME,
154 RTFSISOMAKERCMD_OPT_HFS_MACBIN,
155 RTFSISOMAKERCMD_OPT_HFS_MAGIC,
156 RTFSISOMAKERCMD_OPT_HFS_MAP,
157 RTFSISOMAKERCMD_OPT_HFS_NETATALK,
158 RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP,
159 RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE,
160 RTFSISOMAKERCMD_OPT_HFS_OSX_HFS,
161 RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET,
162 RTFSISOMAKERCMD_OPT_HFS_PARMS,
163 RTFSISOMAKERCMD_OPT_HFS_PART,
164 RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT,
165 RTFSISOMAKERCMD_OPT_HFS_PROBE,
166 RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO,
167 RTFSISOMAKERCMD_OPT_HFS_SFM,
168 RTFSISOMAKERCMD_OPT_HFS_SGI,
169 RTFSISOMAKERCMD_OPT_HFS_SINGLE,
170 RTFSISOMAKERCMD_OPT_HFS_TYPE,
171 RTFSISOMAKERCMD_OPT_HFS_UNLOCK,
172 RTFSISOMAKERCMD_OPT_HFS_USHARE,
173 RTFSISOMAKERCMD_OPT_HFS_VOL_ID,
174 RTFSISOMAKERCMD_OPT_HFS_XINET,
175 RTFSISOMAKERCMD_OPT_HIDDEN,
176 RTFSISOMAKERCMD_OPT_HIDDEN_LIST,
177 RTFSISOMAKERCMD_OPT_HIDE,
178 RTFSISOMAKERCMD_OPT_HIDE_JOLIET,
179 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST,
180 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL,
181 RTFSISOMAKERCMD_OPT_HIDE_LIST,
182 RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED,
183 RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER,
184 RTFSISOMAKERCMD_OPT_HPPA_CMDLINE,
185 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32,
186 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64,
187 RTFSISOMAKERCMD_OPT_HPPA_RAMDISK,
188 RTFSISOMAKERCMD_OPT_INPUT_CHARSET,
189 RTFSISOMAKERCMD_OPT_ISO_LEVEL,
190 RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS,
191 RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE,
192 RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5,
193 RTFSISOMAKERCMD_OPT_JIGDO_JIGDO,
194 RTFSISOMAKERCMD_OPT_JIGDO_MAP,
195 RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST,
196 RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE,
197 RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE,
198 RTFSISOMAKERCMD_OPT_JOLIET_CHARSET,
199 RTFSISOMAKERCMD_OPT_JOLIET_LEVEL,
200 RTFSISOMAKERCMD_OPT_JOLIET_LONG,
201 RTFSISOMAKERCMD_OPT_LOG_FILE,
202 RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES,
203 RTFSISOMAKERCMD_OPT_MIPS_BOOT,
204 RTFSISOMAKERCMD_OPT_MIPSEL_BOOT,
205 RTFSISOMAKERCMD_OPT_NEW_DIR_MODE,
206 RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES,
207 RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS,
208 RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE,
209 RTFSISOMAKERCMD_OPT_NO_PAD,
210 RTFSISOMAKERCMD_OPT_NO_RR,
211 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS,
212 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS,
213 RTFSISOMAKERCMD_OPT_OLD_ROOT,
214 RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET,
215 RTFSISOMAKERCMD_OPT_PAD,
216 RTFSISOMAKERCMD_OPT_PATH_LIST,
217 RTFSISOMAKERCMD_OPT_PRINT_SIZE,
218 RTFSISOMAKERCMD_OPT_QUIET,
219 RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES,
220 RTFSISOMAKERCMD_OPT_ROOT,
221 RTFSISOMAKERCMD_OPT_SORT,
222 RTFSISOMAKERCMD_OPT_SPARC_BOOT,
223 RTFSISOMAKERCMD_OPT_SPARC_LABEL,
224 RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT,
225 RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME,
226 RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE,
227 RTFSISOMAKERCMD_OPT_SUNX86_BOOT,
228 RTFSISOMAKERCMD_OPT_SUNX86_LABEL,
229 RTFSISOMAKERCMD_OPT_SYSTEM_ID,
230 RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME,
231 RTFSISOMAKERCMD_OPT_UDF,
232 RTFSISOMAKERCMD_OPT_UID,
233 RTFSISOMAKERCMD_OPT_USE_FILE_VERSION,
234 RTFSISOMAKERCMD_OPT_VOLUME_ID,
235 RTFSISOMAKERCMD_OPT_VOLUME_SET_ID,
236 RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO,
237 RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE,
238 RTFSISOMAKERCMD_OPT_END
239} RTFSISOMAKERCMDOPT;
240
241
242/**
243 * El Torito boot entry.
244 */
245typedef struct RTFSISOMKCMDELTORITOENTRY
246{
247 /** The type of this entry. */
248 enum
249 {
250 kEntryType_Invalid = 0,
251 kEntryType_Validation, /**< Same as kEntryType_SectionHeader, just hardcoded #0. */
252 kEntryType_SectionHeader,
253 kEntryType_Default, /**< Same as kEntryType_Section, just hardcoded #1. */
254 kEntryType_Section
255 } enmType;
256 /** Type specific data. */
257 union
258 {
259 struct
260 {
261 /** The platform ID (ISO9660_ELTORITO_PLATFORM_ID_XXX). */
262 uint8_t idPlatform;
263 /** Some string for the header. */
264 const char *pszString;
265 } Validation,
266 SectionHeader;
267 struct
268 {
269 /** The name of the boot image wihtin the ISO (-b option). */
270 const char *pszImageNameInIso;
271 /** The object ID of the image in the ISO. This is set to UINT32_MAX when
272 * pszImageNameInIso is used (i.e. -b option) and we've delayed everything
273 * boot related till after all files have been added to the image. */
274 uint32_t idxImageObj;
275 /** Whether to insert boot info table into the image. */
276 bool fInsertBootInfoTable;
277 /** Bootble or not. Possible to make BIOS set up emulation w/o booting it. */
278 bool fBootable;
279 /** The media type (ISO9660_ELTORITO_BOOT_MEDIA_TYPE_XXX). */
280 uint8_t bBootMediaType;
281 /** File system / partition type. */
282 uint8_t bSystemType;
283 /** Load address divided by 0x10. */
284 uint16_t uLoadSeg;
285 /** Number of sectors (512) to load. */
286 uint16_t cSectorsToLoad;
287 } Section,
288 Default;
289 } u;
290} RTFSISOMKCMDELTORITOENTRY;
291/** Pointer to an el torito boot entry. */
292typedef RTFSISOMKCMDELTORITOENTRY *PRTFSISOMKCMDELTORITOENTRY;
293
294/**
295 * ISO maker command options & state.
296 */
297typedef struct RTFSISOMAKERCMDOPTS
298{
299 /** The handle to the ISO maker. */
300 RTFSISOMAKER hIsoMaker;
301 /** Set if we're creating a virtual image maker, i.e. producing something
302 * that is going to be read from only and not written to disk. */
303 bool fVirtualImageMaker;
304 /** Extended error info. This is a stderr alternative for the
305 * fVirtualImageMaker case (stdout goes to LogRel). */
306 PRTERRINFO pErrInfo;
307
308 /** The output file.
309 * This is NULL when fVirtualImageMaker is set. */
310 const char *pszOutFile;
311 /** Special buffer size to use for testing the ISO maker code reading. */
312 uint32_t cbOutputReadBuffer;
313 /** Use random output read buffer size. cbOutputReadBuffer works as maximum
314 * when this is enabled. */
315 bool fRandomOutputReadBufferSize;
316 /** Do output verification, but do it in random order if non-zero. The
317 * values gives the block size to use. */
318 uint32_t cbRandomOrderVerifciationBlock;
319
320 /** @name Processing of inputs
321 * @{ */
322 /** The namespaces (RTFSISOMAKER_NAMESPACE_XXX) we're currently adding
323 * input to. */
324 uint32_t fDstNamespaces;
325 /** The number of name specifiers we're currently operating with. */
326 uint32_t cNameSpecifiers;
327 /** Name specifier configurations.
328 * For instance given "name0=name1=name2=name3=source-file" we will add
329 * source-file to the image with name0 as the name in the namespace and
330 * sub-name specified by aNameSpecifiers[0], name1 in aNameSpecifiers[1],
331 * and so on. This allows exact control over which names a file will
332 * have in each namespace (primary-iso, joliet, udf, hfs) and sub-namespace
333 * (rock-ridge, trans.tbl).
334 */
335 uint32_t afNameSpecifiers[RTFSISOMAKERCMD_MAX_NAMES];
336 /** @} */
337
338 /** @name Booting related options and state.
339 * @{ */
340 /** Number of boot catalog entries (aBootCatEntries). */
341 uint32_t cBootCatEntries;
342 /** Boot catalog entries. */
343 RTFSISOMKCMDELTORITOENTRY aBootCatEntries[64];
344 /** @} */
345
346 /** Number of items (files, directories, images, whatever) we've added. */
347 uint32_t cItemsAdded;
348} RTFSISOMAKERCMDOPTS;
349typedef RTFSISOMAKERCMDOPTS *PRTFSISOMAKERCMDOPTS;
350typedef RTFSISOMAKERCMDOPTS const *PCRTFSISOMAKERCMDOPTS;
351
352
353/**
354 * One parsed name.
355 */
356typedef struct RTFSISOMKCMDPARSEDNAME
357{
358 /** Copy of the corresponding RTFSISOMAKERCMDOPTS::afNameSpecifiers
359 * value. */
360 uint32_t fNameSpecifiers;
361 /** The length of the specified path. */
362 uint32_t cchPath;
363 /** Specified path. */
364 char szPath[RTPATH_MAX];
365} RTFSISOMKCMDPARSEDNAME;
366/** Pointer to a parsed name. */
367typedef RTFSISOMKCMDPARSEDNAME *PRTFSISOMKCMDPARSEDNAME;
368/** Pointer to a const parsed name. */
369typedef RTFSISOMKCMDPARSEDNAME const *PCRTFSISOMKCMDPARSEDNAME;
370
371
372/**
373 * Parsed names.
374 */
375typedef struct RTFSISOMKCMDPARSEDNAMES
376{
377 /** Number of names. */
378 uint32_t cNames;
379 /** Number of names with the source. */
380 uint32_t cNamesWithSrc;
381 /** Special source types.
382 * Used for conveying commands to do on names intead of adding a source.
383 * Only used when adding generic stuff w/o any options involved. */
384 enum
385 {
386 kSrcType_None,
387 kSrcType_Normal,
388 kSrcType_Remove,
389 kSrcType_MustRemove
390 } enmSrcType;
391 /** The parsed names. */
392 RTFSISOMKCMDPARSEDNAME aNames[RTFSISOMAKERCMD_MAX_NAMES + 1];
393} RTFSISOMKCMDPARSEDNAMES;
394/** Pointer to parsed names. */
395typedef RTFSISOMKCMDPARSEDNAMES *PRTFSISOMKCMDPARSEDNAMES;
396/** Pointer to const parsed names. */
397typedef RTFSISOMKCMDPARSEDNAMES *PCRTFSISOMKCMDPARSEDNAMES;
398
399
400
401/*********************************************************************************************************************************
402* Global Variables *
403*********************************************************************************************************************************/
404/*
405 * Parse the command line. This is similar to genisoimage and mkisofs,
406 * thus the single dash long name aliases.
407 */
408static const RTGETOPTDEF g_aRtFsIsoMakerOptions[] =
409{
410 /*
411 * Unquie IPRT ISO maker options.
412 */
413 { "--iprt-iso-maker-file-marker", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
414 { "--iprt-iso-maker-file-marker-ms", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
415 { "--iprt-iso-maker-file-marker-ms-crt", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
416 { "--iprt-iso-maker-file-marker-bourne", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
417 { "--iprt-iso-maker-file-marker-bourne-sh", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
418
419 { "--output-buffer-size", RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE, RTGETOPT_REQ_UINT32 },
420 { "--random-output-buffer-size", RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE, RTGETOPT_REQ_NOTHING },
421 { "--random-order-verficiation", RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION, RTGETOPT_REQ_UINT32 },
422 { "--name-setup", RTFSISOMAKERCMD_OPT_NAME_SETUP, RTGETOPT_REQ_STRING },
423 { "--no-joliet", RTFSISOMAKERCMD_OPT_NO_JOLIET, RTGETOPT_REQ_NOTHING },
424 { "--import-iso", RTFSISOMAKERCMD_OPT_IMPORT_ISO, RTGETOPT_REQ_STRING },
425
426 { "--eltorito-new-entry", RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY, RTGETOPT_REQ_NOTHING },
427 { "--eltorito-add-image", RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE, RTGETOPT_REQ_STRING },
428 { "--eltorito-floppy-12", RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12, RTGETOPT_REQ_NOTHING },
429 { "--eltorito-floppy-144", RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144, RTGETOPT_REQ_NOTHING },
430 { "--eltorito-floppy-288", RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288, RTGETOPT_REQ_NOTHING },
431
432#define DD(a_szLong, a_chShort, a_fFlags) { a_szLong, a_chShort, a_fFlags }, { "-" a_szLong, a_chShort, a_fFlags }
433
434 /*
435 * genisoimage/mkisofs compatibility options we've implemented:
436 */
437 /* booting: */
438 { "--generic-boot", 'G', RTGETOPT_REQ_STRING },
439 DD("-eltorito-boot", 'b', RTGETOPT_REQ_STRING ),
440 DD("-eltorito-alt-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY, RTGETOPT_REQ_NOTHING ),
441 DD("-eltorito-platform-id", RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID, RTGETOPT_REQ_STRING ),
442 DD("-hard-disk-boot", RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT, RTGETOPT_REQ_NOTHING ),
443 DD("-no-emulation-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT, RTGETOPT_REQ_NOTHING ),
444 DD("-no-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT, RTGETOPT_REQ_NOTHING ),
445 DD("-boot-load-seg", RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG, RTGETOPT_REQ_UINT16 ),
446 DD("-boot-load-size", RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE, RTGETOPT_REQ_UINT16 ),
447 DD("-boot-info-table", RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE, RTGETOPT_REQ_NOTHING ),
448 { "--boot-catalog", 'c', RTGETOPT_REQ_STRING },
449
450 /* String props: */
451 DD("-abstract", RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID, RTGETOPT_REQ_STRING ),
452 { "--application-id", 'A', RTGETOPT_REQ_STRING },
453 DD("-biblio", RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID, RTGETOPT_REQ_STRING ),
454 DD("-copyright", RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID, RTGETOPT_REQ_STRING ),
455 DD("-publisher", 'P', RTGETOPT_REQ_STRING ),
456 { "--preparer", 'p', RTGETOPT_REQ_STRING },
457 DD("-sysid", RTFSISOMAKERCMD_OPT_SYSTEM_ID, RTGETOPT_REQ_STRING ),
458 { "--volume-id", RTFSISOMAKERCMD_OPT_VOLUME_ID, RTGETOPT_REQ_STRING }, /* should've been '-V' */
459 DD("-volset", RTFSISOMAKERCMD_OPT_VOLUME_SET_ID, RTGETOPT_REQ_STRING ),
460
461 /*
462 * genisoimage/mkisofs compatibility:
463 */
464 DD("-allow-limited-size", RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE, RTGETOPT_REQ_NOTHING ),
465 DD("-allow-leading-dots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
466 DD("-ldots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
467 DD("-allow-lowercase", RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE, RTGETOPT_REQ_NOTHING ),
468 DD("-allow-multidot", RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT, RTGETOPT_REQ_NOTHING ),
469 DD("-cache-inodes", RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
470 DD("-no-cache-inodes", RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
471 DD("-alpha-boot", RTFSISOMAKERCMD_OPT_ALPHA_BOOT, RTGETOPT_REQ_STRING ),
472 DD("-hppa-bootloader", RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER, RTGETOPT_REQ_STRING ),
473 DD("-hppa-cmdline", RTFSISOMAKERCMD_OPT_HPPA_CMDLINE, RTGETOPT_REQ_STRING ),
474 DD("-hppa-kernel-32", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32, RTGETOPT_REQ_STRING ),
475 DD("-hppa-kernel-64", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64, RTGETOPT_REQ_STRING ),
476 DD("-hppa-ramdisk", RTFSISOMAKERCMD_OPT_HPPA_RAMDISK, RTGETOPT_REQ_STRING ),
477 DD("-mips-boot", RTFSISOMAKERCMD_OPT_MIPS_BOOT, RTGETOPT_REQ_STRING ),
478 DD("-mipsel-boot", RTFSISOMAKERCMD_OPT_MIPSEL_BOOT, RTGETOPT_REQ_STRING ),
479 DD("-sparc-boot", 'B', RTGETOPT_REQ_STRING ),
480 { "--cd-extra", 'C', RTGETOPT_REQ_STRING },
481 DD("-check-oldnames", RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES, RTGETOPT_REQ_NOTHING ),
482 DD("-check-session", RTFSISOMAKERCMD_OPT_CHECK_SESSION, RTGETOPT_REQ_STRING ),
483 { "--dont-append-dot", 'd', RTGETOPT_REQ_NOTHING },
484 { "--deep-directories", 'D', RTGETOPT_REQ_NOTHING },
485 DD("-dir-mode", RTFSISOMAKERCMD_OPT_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
486 DD("-dvd-video", RTFSISOMAKERCMD_OPT_DVD_VIDEO, RTGETOPT_REQ_NOTHING ),
487 DD("-follow-symlinks", 'f', RTGETOPT_REQ_NOTHING ),
488 DD("-file-mode", RTFSISOMAKERCMD_OPT_FILE_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
489 DD("-gid", RTFSISOMAKERCMD_OPT_GID, RTGETOPT_REQ_UINT32 ),
490 DD("-gui", RTFSISOMAKERCMD_OPT_GUI, RTGETOPT_REQ_NOTHING ),
491 DD("-graft-points", RTFSISOMAKERCMD_OPT_GRAFT_POINTS, RTGETOPT_REQ_NOTHING ),
492 DD("-hide", RTFSISOMAKERCMD_OPT_HIDE, RTGETOPT_REQ_STRING ),
493 DD("-hide-list", RTFSISOMAKERCMD_OPT_HIDE_LIST, RTGETOPT_REQ_STRING ),
494 DD("-hidden", RTFSISOMAKERCMD_OPT_HIDDEN, RTGETOPT_REQ_STRING ),
495 DD("-hidden-list", RTFSISOMAKERCMD_OPT_HIDDEN_LIST, RTGETOPT_REQ_STRING ),
496 DD("-hide-joliet", RTFSISOMAKERCMD_OPT_HIDE_JOLIET, RTGETOPT_REQ_STRING ),
497 DD("-hide-joliet-list", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST, RTGETOPT_REQ_STRING ),
498 DD("-hide-joliet-trans-tbl", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL, RTGETOPT_REQ_NOTHING ),
499 DD("-hide-rr-moved", RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED, RTGETOPT_REQ_NOTHING ),
500 DD("-input-charset", RTFSISOMAKERCMD_OPT_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
501 DD("-output-charset", RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
502 { "--iso-level", RTFSISOMAKERCMD_OPT_ISO_LEVEL, RTGETOPT_REQ_UINT8 },
503 { "--joliet", 'J', RTGETOPT_REQ_NOTHING },
504 DD("-joliet-long", RTFSISOMAKERCMD_OPT_JOLIET_LONG, RTGETOPT_REQ_NOTHING ),
505 DD("-jcharset", RTFSISOMAKERCMD_OPT_JOLIET_CHARSET, RTGETOPT_REQ_STRING ),
506 { "--long-names", 'l', RTGETOPT_REQ_NOTHING },
507 { "--leading-dot", 'L', RTGETOPT_REQ_NOTHING },
508 DD("-jigdo-jigdo", RTFSISOMAKERCMD_OPT_JIGDO_JIGDO, RTGETOPT_REQ_STRING ),
509 DD("-jigdo-template", RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE, RTGETOPT_REQ_STRING ),
510 DD("-jigdo-min-file-size", RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE, RTGETOPT_REQ_UINT64 ),
511 DD("-jigdo-force-md5", RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5, RTGETOPT_REQ_STRING ),
512 DD("-jigdo-exclude", RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE, RTGETOPT_REQ_STRING ),
513 DD("-jigdo-map", RTFSISOMAKERCMD_OPT_JIGDO_MAP, RTGETOPT_REQ_STRING ),
514 DD("-md5-list", RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST, RTGETOPT_REQ_STRING ),
515 DD("-jigdo-template-compress", RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS, RTGETOPT_REQ_STRING ),
516 DD("-log-file", RTFSISOMAKERCMD_OPT_LOG_FILE, RTGETOPT_REQ_STRING ),
517 { "--exclude", 'm', RTGETOPT_REQ_STRING },
518 { "--exclude", 'x', RTGETOPT_REQ_STRING },
519 DD("-exclude-list", RTFSISOMAKERCMD_OPT_EXCLUDE_LIST, RTGETOPT_REQ_STRING ),
520 DD("-max-iso9660-filenames", RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES, RTGETOPT_REQ_NOTHING ),
521 { "--merge", 'M', RTGETOPT_REQ_STRING },
522 DD("-dev", 'M', RTGETOPT_REQ_STRING ),
523 { "--omit-version-numbers", 'N', RTGETOPT_REQ_NOTHING },
524 DD("-new-dir-mode", RTFSISOMAKERCMD_OPT_NEW_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
525 DD("-nobak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
526 DD("-no-bak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
527 DD("-force-rr", RTFSISOMAKERCMD_OPT_FORCE_RR, RTGETOPT_REQ_NOTHING ),
528 DD("-no-rr", RTFSISOMAKERCMD_OPT_NO_RR, RTGETOPT_REQ_NOTHING ),
529 DD("-no-split-symlink-components", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS, RTGETOPT_REQ_NOTHING ),
530 DD("-no-split-symlink-fields", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS, RTGETOPT_REQ_NOTHING ),
531 { "--output", 'o', RTGETOPT_REQ_STRING },
532 DD("-pad", RTFSISOMAKERCMD_OPT_PAD, RTGETOPT_REQ_NOTHING ),
533 DD("-no-pad", RTFSISOMAKERCMD_OPT_NO_PAD, RTGETOPT_REQ_NOTHING ),
534 DD("-path-list", RTFSISOMAKERCMD_OPT_PATH_LIST, RTGETOPT_REQ_STRING ),
535 DD("-print-size", RTFSISOMAKERCMD_OPT_PRINT_SIZE, RTGETOPT_REQ_NOTHING ),
536 DD("-quiet", RTFSISOMAKERCMD_OPT_QUIET, RTGETOPT_REQ_NOTHING ),
537 { "--rock-ridge", 'R', RTGETOPT_REQ_NOTHING },
538 { "--rock-ridge-relaxed", 'r', RTGETOPT_REQ_NOTHING },
539 DD("-relaxed-filenames", RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES, RTGETOPT_REQ_NOTHING ),
540 DD("-root", RTFSISOMAKERCMD_OPT_ROOT, RTGETOPT_REQ_STRING ),
541 DD("-old-root", RTFSISOMAKERCMD_OPT_OLD_ROOT, RTGETOPT_REQ_STRING ),
542 DD("-sort", RTFSISOMAKERCMD_OPT_SORT, RTGETOPT_REQ_STRING ),
543 DD("-sparc-boot", RTFSISOMAKERCMD_OPT_SPARC_BOOT, RTGETOPT_REQ_STRING ),
544 DD("-sparc-label", RTFSISOMAKERCMD_OPT_SPARC_LABEL, RTGETOPT_REQ_STRING ),
545 DD("-split-output", RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT, RTGETOPT_REQ_NOTHING ),
546 DD("-stream-media-size", RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE, RTGETOPT_REQ_UINT64 ),
547 DD("-stream-file-name", RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME, RTGETOPT_REQ_STRING ),
548 DD("-sunx86-boot", RTFSISOMAKERCMD_OPT_SUNX86_BOOT, RTGETOPT_REQ_STRING ),
549 DD("-sunx86-label", RTFSISOMAKERCMD_OPT_SUNX86_LABEL, RTGETOPT_REQ_STRING ),
550 { "--trans-tbl", 'T', RTGETOPT_REQ_NOTHING },
551 DD("-table-name", RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME, RTGETOPT_REQ_STRING ),
552 DD("-ucs-level", RTFSISOMAKERCMD_OPT_JOLIET_LEVEL, RTGETOPT_REQ_UINT8 ),
553 DD("-udf", RTFSISOMAKERCMD_OPT_UDF, RTGETOPT_REQ_NOTHING ),
554 DD("-uid", RTFSISOMAKERCMD_OPT_UID, RTGETOPT_REQ_UINT32 ),
555 DD("-use-fileversion", RTFSISOMAKERCMD_OPT_USE_FILE_VERSION, RTGETOPT_REQ_NOTHING ),
556 { "--untranslated-filenames", 'U', RTGETOPT_REQ_NOTHING },
557 DD("-no-iso-translate", RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE, RTGETOPT_REQ_NOTHING ),
558 DD("-volset-size", RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE, RTGETOPT_REQ_UINT32 ),
559 DD("-volset-seqno", RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO, RTGETOPT_REQ_UINT32 ),
560 { "--transpared-compression", 'z', RTGETOPT_REQ_NOTHING },
561
562 /* HFS and ISO-9660 apple extensions. */
563 DD("-hfs", RTFSISOMAKERCMD_OPT_HFS_ENABLE, RTGETOPT_REQ_NOTHING ),
564 DD("-apple", RTFSISOMAKERCMD_OPT_APPLE, RTGETOPT_REQ_NOTHING ),
565 DD("-map", RTFSISOMAKERCMD_OPT_HFS_MAP, RTGETOPT_REQ_STRING ),
566 DD("-magic", RTFSISOMAKERCMD_OPT_HFS_MAGIC, RTGETOPT_REQ_STRING ),
567 DD("-hfs-creator", RTFSISOMAKERCMD_OPT_HFS_CREATOR, RTGETOPT_REQ_STRING ),
568 DD("-hfs-type", RTFSISOMAKERCMD_OPT_HFS_TYPE, RTGETOPT_REQ_STRING ),
569 DD("-probe", RTFSISOMAKERCMD_OPT_HFS_PROBE, RTGETOPT_REQ_NOTHING ),
570 DD("-no-desktop", RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP, RTGETOPT_REQ_NOTHING ),
571 DD("-mac-name", RTFSISOMAKERCMD_OPT_HFS_MAC_NAME, RTGETOPT_REQ_NOTHING ),
572 DD("-boot-hfs-file", RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE, RTGETOPT_REQ_STRING ),
573 DD("-part", RTFSISOMAKERCMD_OPT_HFS_PART, RTGETOPT_REQ_NOTHING ),
574 DD("-auto", RTFSISOMAKERCMD_OPT_HFS_AUTO, RTGETOPT_REQ_STRING ),
575 DD("-cluster-size", RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE, RTGETOPT_REQ_UINT32 ),
576 DD("-hide-hfs", RTFSISOMAKERCMD_OPT_HFS_HIDE, RTGETOPT_REQ_STRING ),
577 DD("-hide-hfs-list", RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST, RTGETOPT_REQ_STRING ),
578 DD("-hfs-volid", RTFSISOMAKERCMD_OPT_HFS_VOL_ID, RTGETOPT_REQ_STRING ),
579 DD("-icon-position", RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION, RTGETOPT_REQ_NOTHING ),
580 DD("-root-info", RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO, RTGETOPT_REQ_STRING ),
581 DD("-prep-boot", RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT, RTGETOPT_REQ_STRING ),
582 DD("-chrp-boot", RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT, RTGETOPT_REQ_NOTHING ),
583 DD("-input-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
584 DD("-output-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
585 DD("-hfs-unlock", RTFSISOMAKERCMD_OPT_HFS_UNLOCK, RTGETOPT_REQ_NOTHING ),
586 DD("-hfs-bless", RTFSISOMAKERCMD_OPT_HFS_BLESS, RTGETOPT_REQ_STRING ),
587 DD("-hfs-parms", RTFSISOMAKERCMD_OPT_HFS_PARMS, RTGETOPT_REQ_STRING ),
588 { "--cap", RTFSISOMAKERCMD_OPT_HFS_CAP, RTGETOPT_REQ_NOTHING },
589 { "--netatalk", RTFSISOMAKERCMD_OPT_HFS_NETATALK, RTGETOPT_REQ_NOTHING },
590 { "--double", RTFSISOMAKERCMD_OPT_HFS_DOUBLE, RTGETOPT_REQ_NOTHING },
591 { "--ethershare", RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE, RTGETOPT_REQ_NOTHING },
592 { "--ushare", RTFSISOMAKERCMD_OPT_HFS_USHARE, RTGETOPT_REQ_NOTHING },
593 { "--exchange", RTFSISOMAKERCMD_OPT_HFS_EXCHANGE, RTGETOPT_REQ_NOTHING },
594 { "--sgi", RTFSISOMAKERCMD_OPT_HFS_SGI, RTGETOPT_REQ_NOTHING },
595 { "--xinet", RTFSISOMAKERCMD_OPT_HFS_XINET, RTGETOPT_REQ_NOTHING },
596 { "--macbin", RTFSISOMAKERCMD_OPT_HFS_MACBIN, RTGETOPT_REQ_NOTHING },
597 { "--single", RTFSISOMAKERCMD_OPT_HFS_SINGLE, RTGETOPT_REQ_NOTHING },
598 { "--dave", RTFSISOMAKERCMD_OPT_HFS_DAVE, RTGETOPT_REQ_NOTHING },
599 { "--sfm", RTFSISOMAKERCMD_OPT_HFS_SFM, RTGETOPT_REQ_NOTHING },
600 { "--osx-double", RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE, RTGETOPT_REQ_NOTHING },
601 { "--osx-hfs", RTFSISOMAKERCMD_OPT_HFS_OSX_HFS, RTGETOPT_REQ_NOTHING },
602#undef DD
603};
604
605
606/*********************************************************************************************************************************
607* Internal Functions *
608*********************************************************************************************************************************/
609static int rtFsIsoMakerCmdParse(PRTFSISOMAKERCMDOPTS pOpts, unsigned cArgs, char **papszArgs, unsigned cDepth);
610
611
612/**
613 * Wrapper around RTErrInfoSetV / RTMsgErrorV.
614 *
615 * @returns @a rc
616 * @param pOpts The ISO maker command instance.
617 * @param rc The return code.
618 * @param pszFormat The message format.
619 * @param ... The message format arguments.
620 */
621static int rtFsIsoMakerCmdErrorRc(PRTFSISOMAKERCMDOPTS pOpts, int rc, const char *pszFormat, ...)
622{
623 va_list va;
624 va_start(va, pszFormat);
625 if (pOpts->pErrInfo)
626 RTErrInfoSetV(pOpts->pErrInfo, rc, pszFormat, va);
627 else
628 RTMsgErrorV(pszFormat, va);
629 va_end(va);
630 return rc;
631}
632
633
634/**
635 * Wrapper around RTErrInfoSetV / RTMsgErrorV for doing the job of
636 * RTVfsChainMsgError.
637 *
638 * @returns @a rc
639 * @param pOpts The ISO maker command instance.
640 * @param pszFunction The API called.
641 * @param pszSpec The VFS chain specification or file path passed to the.
642 * @param rc The return code.
643 * @param offError The error offset value returned (0 if not captured).
644 * @param pErrInfo Additional error information. Optional.
645 */
646static int rtFsIsoMakerCmdChainError(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFunction, const char *pszSpec, int rc,
647 uint32_t offError, PRTERRINFO pErrInfo)
648{
649 if (RTErrInfoIsSet(pErrInfo))
650 {
651 if (offError > 0)
652 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
653 "%s failed with rc=%Rrc: %s\n"
654 " '%s'\n"
655 " %*s^",
656 pszFunction, rc, pErrInfo->pszMsg, pszSpec, offError, "");
657 else
658 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s failed to open '%s': %Rrc: %s",
659 pszFunction, pszSpec, rc, pErrInfo->pszMsg);
660 }
661 else
662 {
663 if (offError > 0)
664 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
665 "%s failed with rc=%Rrc:\n"
666 " '%s'\n"
667 " %*s^",
668 pszFunction, rc, pszSpec, offError, "");
669 else
670 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s failed to open '%s': %Rrc", pszFunction, pszSpec, rc);
671 }
672 return rc;
673}
674
675
676/**
677 * Wrapper around RTErrInfoSetV / RTMsgErrorV for displaying syntax errors.
678 *
679 * @returns VERR_INVALID_PARAMETER
680 * @param pOpts The ISO maker command instance.
681 * @param pszFormat The message format.
682 * @param ... The message format arguments.
683 */
684static int rtFsIsoMakerCmdSyntaxError(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
685{
686 va_list va;
687 va_start(va, pszFormat);
688 if (pOpts->pErrInfo)
689 RTErrInfoSetV(pOpts->pErrInfo, VERR_INVALID_PARAMETER, pszFormat, va);
690 else
691 RTMsgErrorV(pszFormat, va);
692 va_end(va);
693 return VERR_INVALID_PARAMETER;
694}
695
696
697/**
698 * Wrapper around RTPrintfV / RTLogRelPrintfV.
699 *
700 * @param pOpts The ISO maker command instance.
701 * @param pszFormat The message format.
702 * @param ... The message format arguments.
703 */
704static void rtFsIsoMakerPrintf(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
705{
706 va_list va;
707 va_start(va, pszFormat);
708 if (pOpts->pErrInfo)
709 RTLogRelPrintfV(pszFormat, va);
710 else
711 RTPrintfV(pszFormat, va);
712 va_end(va);
713}
714
715/**
716 * Deletes the state and returns @a rc.
717 *
718 * @returns @a rc.
719 * @param pOpts The ISO maker command instance to delete.
720 * @param rc The status code to return.
721 */
722static int rtFsIsoMakerCmdDeleteState(PRTFSISOMAKERCMDOPTS pOpts, int rc)
723{
724 if (pOpts->hIsoMaker != NIL_RTFSISOMAKER)
725 {
726 RTFsIsoMakerRelease(pOpts->hIsoMaker);
727 pOpts->hIsoMaker = NIL_RTFSISOMAKER;
728 }
729
730 return rc;
731}
732
733
734static void rtFsIsoMakerCmdUsage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszProgName)
735{
736 rtFsIsoMakerPrintf(pOpts, "usage: %s [options] <file>=<cdfile>\n", pszProgName);
737}
738
739
740/**
741 * Verifies the image content by reading blocks in random order.
742 *
743 * This is for exercise the virtual ISO code better and test that we get the
744 * same data when reading something twice.
745 *
746 * @returns IPRT status code.
747 * @param pOpts The ISO maker command instance.
748 * @param hVfsSrcFile The source file (virtual ISO).
749 * @param hVfsDstFile The destination file (image file on disk).
750 * @param cbImage The size of the ISO.
751 */
752static int rtFsIsoMakerCmdVerifyImageInRandomOrder(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile,
753 RTVFSFILE hVfsDstFile, uint64_t cbImage)
754{
755 /*
756 * Figure the buffer (block) size and allocate a bitmap for noting down blocks we've covered.
757 */
758 int rc;
759 size_t cbBuf = RT_MAX(pOpts->cbRandomOrderVerifciationBlock, 1);
760 uint64_t cBlocks64 = (cbImage + cbBuf - 1) / cbBuf;
761 if (cBlocks64 > _512M)
762 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_OUT_OF_RANGE,
763 "verification block count too high: cBlocks=%#RX64 (cbBuf=%#zx), max 512M", cBlocks64, cbBuf);
764 uint32_t cBlocks = (uint32_t)cBlocks64;
765 uint32_t cbBitmap = (cBlocks + 63) / 8;
766 if (cbBitmap > _64M)
767 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_OUT_OF_RANGE,
768 "verification bitmap too big: cbBitmap=%#RX32 (cbBuf=%#zx), max 64MB", cbBitmap, cbBuf);
769 void *pvSrcBuf = RTMemTmpAlloc(cbBuf);
770 void *pvDstBuf = RTMemTmpAlloc(cbBuf);
771 void *pvBitmap = RTMemTmpAllocZ(cbBitmap);
772 if (pvSrcBuf && pvDstBuf && pvBitmap)
773 {
774 /* Must set the unused bits in the top qword. */
775 for (uint32_t i = RT_ALIGN_32(cBlocks, 64) - 1; i >= cBlocks; i--)
776 ASMBitSet(pvBitmap, i);
777
778 /*
779 * Do the verification.
780 */
781 rtFsIsoMakerPrintf(pOpts, "Verifying image in random order using %zu (%#zx) byte blocks: %#zx in blocks\n",
782 cbBuf, cbBuf, cBlocks);
783
784 rc = VINF_SUCCESS;
785 uint64_t cLeft = cBlocks;
786 while (cLeft-- > 0)
787 {
788 /*
789 * Figure out which block to check next.
790 */
791 uint32_t iBlock = RTRandU32Ex(0, cBlocks - 1);
792 if (!ASMBitTestAndSet(pvBitmap, iBlock))
793 Assert(iBlock < cBlocks);
794 else
795 {
796 /* try 32 other random numbers. */
797 bool fBitSet;
798 unsigned cTries = 0;
799 do
800 {
801 iBlock = RTRandU32Ex(0, cBlocks - 1);
802 fBitSet = ASMBitTestAndSet(pvBitmap, iBlock);
803 } while (fBitSet && ++cTries < 32);
804 if (fBitSet)
805 {
806 /* Look for the next clear bit after it (with wrap around). */
807 int iHit = ASMBitNextClear(pvBitmap, cBlocks, iBlock);
808 Assert(iHit < (int32_t)cBlocks);
809 if (iHit < 0)
810 {
811 iHit = ASMBitNextClear(pvBitmap, iBlock, 0);
812 Assert(iHit < (int32_t)cBlocks);
813 }
814 if (iHit >= 0)
815 {
816 fBitSet = ASMBitTestAndSet(pvBitmap, iHit);
817 if (!fBitSet)
818 iBlock = iHit;
819 else
820 {
821 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_INTERNAL_ERROR_3,
822 "Bitmap weirdness: iHit=%#x iBlock=%#x cBlocks=%#x",
823 iHit, iBlock, cBlocks);
824 break;
825 }
826 }
827 else
828 {
829 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_INTERNAL_ERROR_2, "Bitmap weirdness: iBlock=%#x cBlocks=%#x",
830 iBlock, cBlocks);
831 break;
832 }
833 }
834 }
835 Assert(ASMBitTest(pvBitmap, iBlock));
836
837 /*
838 * Figure out how much and where to read (last block fun).
839 */
840 uint64_t offBlock = iBlock * (uint64_t)cbBuf;
841 size_t cbToRead = cbBuf;
842 if (iBlock + 1 < cBlocks)
843 { /* likely */ }
844 else if (cbToRead > cbImage - offBlock)
845 cbToRead = (size_t)(cbImage - offBlock);
846 Assert(offBlock + cbToRead <= cbImage);
847
848 /*
849 * Read the blocks.
850 */
851 //RTPrintf("Reading block #%#x at %#RX64\n", iBlock, offBlock);
852 rc = RTVfsFileReadAt(hVfsDstFile, offBlock, pvDstBuf, cbToRead, NULL);
853 if (RT_SUCCESS(rc))
854 {
855 memset(pvSrcBuf, 0xdd, cbBuf);
856 rc = RTVfsFileReadAt(hVfsSrcFile, offBlock, pvSrcBuf, cbToRead, NULL);
857 if (RT_SUCCESS(rc))
858 {
859 if (memcmp(pvDstBuf, pvSrcBuf, cbToRead) == 0)
860 continue;
861 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_MISMATCH,
862 "Block #%#x differs! offBlock=%#RX64 cbToRead=%#zu\n"
863 "Virtual ISO (source):\n%.*Rhxd\nWritten ISO (destination):\n%.*Rhxd",
864 iBlock, offBlock, cbToRead, cbToRead, pvSrcBuf, cbToRead, pvDstBuf);
865 }
866 else
867 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
868 "Error reading %#zx bytes source (virtual ISO) block #%#x at %#RX64: %Rrc",
869 cbToRead, iBlock, offBlock, rc);
870 }
871 else
872 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
873 "Error reading %#zx bytes destination (written ISO) block #%#x at %#RX64: %Rrc",
874 cbToRead, iBlock, offBlock, rc);
875 break;
876 }
877
878 if (RT_SUCCESS(rc))
879 rtFsIsoMakerPrintf(pOpts, "Written image verified fine!\n");
880 }
881 else if (!pvSrcBuf || !pvDstBuf)
882 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbBuf);
883 else
884 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbBuf);
885 RTMemTmpFree(pvBitmap);
886 RTMemTmpFree(pvDstBuf);
887 RTMemTmpFree(pvSrcBuf);
888 return rc;
889}
890
891
892/**
893 * Writes the image to file, no checking, no special buffering.
894 *
895 * @returns IPRT status code.
896 * @param pOpts The ISO maker command instance.
897 * @param hVfsSrcFile The source file from the ISO maker.
898 * @param hVfsDstFile The destination file (image file on disk).
899 * @param cbImage The size of the ISO.
900 * @param ppvBuf Pointer to the buffer pointer. The buffer will
901 * be reallocated, but we want the luxary of the
902 * caller freeing it.
903 */
904static int rtFsIsoMakerCmdWriteImageRandomBufferSize(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile, RTVFSFILE hVfsDstFile,
905 uint64_t cbImage, void **ppvBuf)
906{
907 /*
908 * Copy the virtual image bits to the destination file.
909 */
910 void *pvBuf = *ppvBuf;
911 uint32_t cbMaxBuf = pOpts->cbOutputReadBuffer > 0 ? pOpts->cbOutputReadBuffer : _64K;
912 uint64_t offImage = 0;
913 while (offImage < cbImage)
914 {
915 /* Figure out how much to copy this time. */
916 size_t cbToCopy = RTRandU32Ex(1, cbMaxBuf - 1);
917 if (offImage + cbToCopy < cbImage)
918 { /* likely */ }
919 else
920 cbToCopy = (size_t)(cbImage - offImage);
921 RTMemFree(pvBuf);
922 *ppvBuf = pvBuf = RTMemTmpAlloc(cbToCopy);
923 if (pvBuf)
924 {
925 /* Do the copying. */
926 int rc = RTVfsFileReadAt(hVfsSrcFile, offImage, pvBuf, cbToCopy, NULL);
927 if (RT_SUCCESS(rc))
928 {
929 rc = RTVfsFileWriteAt(hVfsDstFile, offImage, pvBuf, cbToCopy, NULL);
930 if (RT_SUCCESS(rc))
931 offImage += cbToCopy;
932 else
933 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc writing %#zx bytes at offset %#RX64 to '%s'",
934 rc, cbToCopy, offImage, pOpts->pszOutFile);
935 }
936 else
937 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc read %#zx bytes at offset %#RX64", rc, cbToCopy, offImage);
938 }
939 else
940 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbToCopy);
941 }
942 return VINF_SUCCESS;
943}
944
945
946/**
947 * Writes the image to file, no checking, no special buffering.
948 *
949 * @returns IPRT status code.
950 * @param pOpts The ISO maker command instance.
951 * @param hVfsSrcFile The source file from the ISO maker.
952 * @param hVfsDstFile The destination file (image file on disk).
953 * @param cbImage The size of the ISO.
954 * @param pvBuf Pointer to read buffer.
955 * @param cbBuf The buffer size.
956 */
957static int rtFsIsoMakerCmdWriteImageSimple(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile, RTVFSFILE hVfsDstFile,
958 uint64_t cbImage, void *pvBuf, size_t cbBuf)
959{
960 /*
961 * Copy the virtual image bits to the destination file.
962 */
963 uint64_t offImage = 0;
964 while (offImage < cbImage)
965 {
966 /* Figure out how much to copy this time. */
967 size_t cbToCopy = cbBuf;
968 if (offImage + cbToCopy < cbImage)
969 { /* likely */ }
970 else
971 cbToCopy = (size_t)(cbImage - offImage);
972
973 /* Do the copying. */
974 int rc = RTVfsFileReadAt(hVfsSrcFile, offImage, pvBuf, cbToCopy, NULL);
975 if (RT_SUCCESS(rc))
976 {
977 rc = RTVfsFileWriteAt(hVfsDstFile, offImage, pvBuf, cbToCopy, NULL);
978 if (RT_SUCCESS(rc))
979 offImage += cbToCopy;
980 else
981 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc writing %#zx bytes at offset %#RX64 to '%s'",
982 rc, cbToCopy, offImage, pOpts->pszOutFile);
983 }
984 else
985 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc read %#zx bytes at offset %#RX64", rc, cbToCopy, offImage);
986 }
987 return VINF_SUCCESS;
988}
989
990
991/**
992 * Writes the image to file.
993 *
994 * @returns IPRT status code.
995 * @param pOpts The ISO maker command instance.
996 * @param hVfsSrcFile The source file from the ISO maker.
997 */
998static int rtFsIsoMakerCmdWriteImage(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile)
999{
1000 /*
1001 * Get the image size and setup the copy buffer.
1002 */
1003 uint64_t cbImage;
1004 int rc = RTVfsFileGetSize(hVfsSrcFile, &cbImage);
1005 if (RT_SUCCESS(rc))
1006 {
1007 rtFsIsoMakerPrintf(pOpts, "Image size: %'RU64 (%#RX64) bytes\n", cbImage, cbImage);
1008
1009 uint32_t cbBuf = pOpts->cbOutputReadBuffer == 0 ? _1M : pOpts->cbOutputReadBuffer;
1010 void *pvBuf = RTMemTmpAlloc(cbBuf);
1011 if (pvBuf)
1012 {
1013 /*
1014 * Open the output file.
1015 */
1016 RTVFSFILE hVfsDstFile;
1017 uint32_t offError;
1018 RTERRINFOSTATIC ErrInfo;
1019 rc = RTVfsChainOpenFile(pOpts->pszOutFile, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE,
1020 &hVfsDstFile, &offError, RTErrInfoInitStatic(&ErrInfo));
1021 if (RT_SUCCESS(rc))
1022 {
1023 /*
1024 * Apply the desired writing method.
1025 */
1026 if (!pOpts->fRandomOutputReadBufferSize)
1027 rc = rtFsIsoMakerCmdWriteImageRandomBufferSize(pOpts, hVfsSrcFile, hVfsDstFile, cbImage, &pvBuf);
1028 else
1029 rc = rtFsIsoMakerCmdWriteImageSimple(pOpts, hVfsSrcFile, hVfsDstFile, cbImage, pvBuf, cbBuf);
1030 RTMemTmpFree(pvBuf);
1031
1032 if (RT_SUCCESS(rc) && pOpts->cbRandomOrderVerifciationBlock > 0)
1033 rc = rtFsIsoMakerCmdVerifyImageInRandomOrder(pOpts, hVfsSrcFile, hVfsDstFile, cbImage);
1034
1035 /*
1036 * Flush the output file before releasing it.
1037 */
1038 if (RT_SUCCESS(rc))
1039 {
1040 rc = RTVfsFileFlush(hVfsDstFile);
1041 if (RT_FAILURE(rc))
1042 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileFlush failed on '%s': %Rrc", pOpts->pszOutFile, rc);
1043 }
1044
1045 RTVfsFileRelease(hVfsDstFile);
1046 }
1047 else
1048 {
1049 RTMemTmpFree(pvBuf);
1050 rc = rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pOpts->pszOutFile, rc, offError, &ErrInfo.Core);
1051 }
1052 }
1053 else
1054 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%zu) failed", cbBuf);
1055 }
1056 else
1057 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileGetSize failed: %Rrc", rc);
1058 return rc;
1059}
1060
1061
1062/**
1063 * Formats @a fNameSpecifiers into a '+' separated list of names.
1064 *
1065 * @returns pszDst
1066 * @param fNameSpecifiers The name specifiers.
1067 * @param pszDst The destination bufer.
1068 * @param cbDst The size of the destination buffer.
1069 */
1070static char *rtFsIsoMakerCmdNameSpecifiersToString(uint32_t fNameSpecifiers, char *pszDst, size_t cbDst)
1071{
1072 static struct { const char *pszName; uint32_t cchName; uint32_t fSpec; } const s_aSpecs[] =
1073 {
1074 { RT_STR_TUPLE("primary"), RTFSISOMAKERCMDNAME_PRIMARY_ISO },
1075 { RT_STR_TUPLE("primary-rock"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE },
1076 { RT_STR_TUPLE("primary-trans-tbl"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL },
1077 { RT_STR_TUPLE("joliet"), RTFSISOMAKERCMDNAME_JOLIET },
1078 { RT_STR_TUPLE("joliet-rock"), RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE },
1079 { RT_STR_TUPLE("joliet-trans-tbl"), RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL },
1080 { RT_STR_TUPLE("udf"), RTFSISOMAKERCMDNAME_UDF },
1081 { RT_STR_TUPLE("udf-trans-tbl"), RTFSISOMAKERCMDNAME_UDF_TRANS_TBL },
1082 { RT_STR_TUPLE("hfs"), RTFSISOMAKERCMDNAME_HFS },
1083 { RT_STR_TUPLE("hfs-trans-tbl"), RTFSISOMAKERCMDNAME_HFS_TRANS_TBL },
1084 };
1085
1086 Assert(cbDst > 0);
1087 char *pszRet = pszDst;
1088 for (uint32_t i = 0; i < RT_ELEMENTS(s_aSpecs); i++)
1089 if (s_aSpecs[i].fSpec & fNameSpecifiers)
1090 {
1091 if (pszDst != pszRet && cbDst > 1)
1092 {
1093 *pszDst++ = '+';
1094 cbDst--;
1095 }
1096 if (cbDst > s_aSpecs[i].cchName)
1097 {
1098 memcpy(pszDst, s_aSpecs[i].pszName, s_aSpecs[i].cchName);
1099 cbDst -= s_aSpecs[i].cchName;
1100 pszDst += s_aSpecs[i].cchName;
1101 }
1102 else if (cbDst > 1)
1103 {
1104 memcpy(pszDst, s_aSpecs[i].pszName, cbDst - 1);
1105 pszDst += cbDst - 1;
1106 cbDst = 1;
1107 }
1108
1109 fNameSpecifiers &= ~s_aSpecs[i].fSpec;
1110 if (!fNameSpecifiers)
1111 break;
1112 }
1113 *pszDst = '\0';
1114 return pszRet;
1115}
1116
1117
1118/**
1119 * Parses the --name-setup option.
1120 *
1121 * @returns IPRT status code.
1122 * @param pOpts The ISO maker command instance.
1123 * @param pszSpec The name setup specification.
1124 */
1125static int rtFsIsoMakerCmdOptNameSetup(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
1126{
1127 /*
1128 * Comma separated list of one or more specifiers.
1129 */
1130 uint32_t fNamespaces = 0;
1131 uint32_t fPrevMajor = 0;
1132 uint32_t iNameSpecifier = 0;
1133 uint32_t offSpec = 0;
1134 do
1135 {
1136 /*
1137 * Parse up to the next colon or end of string.
1138 */
1139 uint32_t fNameSpecifier = 0;
1140 char ch;
1141 while ( (ch = pszSpec[offSpec]) != '\0'
1142 && ch != ',')
1143 {
1144 if (RT_C_IS_SPACE(ch) || ch == '+' || ch == '|') /* space, '+' and '|' are allowed as name separators. */
1145 offSpec++;
1146 else
1147 {
1148 /* Find the end of the name. */
1149 uint32_t offEndSpec = offSpec + 1;
1150 while ( (ch = pszSpec[offEndSpec]) != '\0'
1151 && ch != ','
1152 && ch != '+'
1153 && ch != '|'
1154 && !RT_C_IS_SPACE(ch))
1155 offEndSpec++;
1156
1157#define IS_EQUAL(a_sz) (cchName == sizeof(a_sz) - 1U && strncmp(pchName, a_sz, sizeof(a_sz) - 1U) == 0)
1158 const char * const pchName = &pszSpec[offSpec];
1159 uint32_t const cchName = offEndSpec - offSpec;
1160 /* major namespaces */
1161 if ( IS_EQUAL("iso")
1162 || IS_EQUAL("primary")
1163 || IS_EQUAL("iso9660")
1164 || IS_EQUAL("iso-9660")
1165 || IS_EQUAL("primary-iso")
1166 || IS_EQUAL("iso-primary") )
1167 {
1168 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO;
1169 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_ISO_9660;
1170 }
1171 else if (IS_EQUAL("joliet"))
1172 {
1173 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET;
1174 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_JOLIET;
1175 }
1176 else if (IS_EQUAL("udf"))
1177 {
1178#if 0
1179 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF;
1180 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_UDF;
1181#else
1182 return rtFsIsoMakerCmdSyntaxError(pOpts, "UDF support is currently not implemented");
1183#endif
1184 }
1185 else if (IS_EQUAL("hfs") || IS_EQUAL("hfsplus"))
1186 {
1187#if 0
1188 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS;
1189 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_HFS;
1190#else
1191 return rtFsIsoMakerCmdSyntaxError(pOpts, "Hybrid HFS+ support is currently not implemented");
1192#endif
1193 }
1194 /* rock ridge */
1195 else if ( IS_EQUAL("rr")
1196 || IS_EQUAL("rock")
1197 || IS_EQUAL("rock-ridge"))
1198 {
1199 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
1200 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
1201 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
1202 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
1203 else
1204 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified rock-ridge name specifier");
1205 }
1206 else if ( IS_EQUAL("iso-rr") || IS_EQUAL("iso-rock") || IS_EQUAL("iso-rock-ridge")
1207 || IS_EQUAL("primary-rr") || IS_EQUAL("primary-rock") || IS_EQUAL("primary-rock-ridge")
1208 || IS_EQUAL("iso9660-rr") || IS_EQUAL("iso9660-rock") || IS_EQUAL("iso9660-rock-ridge")
1209 || IS_EQUAL("iso-9660-rr") || IS_EQUAL("iso-9660-rock") || IS_EQUAL("iso-9660-rock-ridge")
1210 || IS_EQUAL("primaryiso-rr") || IS_EQUAL("primaryiso-rock") || IS_EQUAL("primaryiso-rock-ridge")
1211 || IS_EQUAL("primary-iso-rr") || IS_EQUAL("primary-iso-rock") || IS_EQUAL("primary-iso-rock-ridge") )
1212 {
1213 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
1214 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
1215 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-rock-ridge must come after the iso-9660 name specifier");
1216 }
1217 else if (IS_EQUAL("joliet-rr") || IS_EQUAL("joliet-rock") || IS_EQUAL("joliet-rock-ridge"))
1218 {
1219 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
1220 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
1221 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-rock-ridge must come after the joliet name specifier");
1222 }
1223 /* trans.tbl */
1224 else if (IS_EQUAL("trans") || IS_EQUAL("trans-tbl"))
1225 {
1226 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
1227 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
1228 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
1229 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
1230 else
1231 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified trans-tbl name specifier");
1232 }
1233 else if ( IS_EQUAL("iso-trans") || IS_EQUAL("iso-trans-tbl")
1234 || IS_EQUAL("primary-trans") || IS_EQUAL("primary-trans-tbl")
1235 || IS_EQUAL("iso9660-trans") || IS_EQUAL("iso9660-trans-tbl")
1236 || IS_EQUAL("iso-9660-trans") || IS_EQUAL("iso-9660-trans-tbl")
1237 || IS_EQUAL("primaryiso-trans") || IS_EQUAL("primaryiso-trans-tbl")
1238 || IS_EQUAL("primary-iso-trans") || IS_EQUAL("primary-iso-trans-tbl") )
1239 {
1240 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
1241 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
1242 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-trans-tbl must come after the iso-9660 name specifier");
1243 }
1244 else if (IS_EQUAL("joliet-trans") || IS_EQUAL("joliet-trans-tbl"))
1245 {
1246 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
1247 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
1248 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-trans-tbl must come after the joliet name specifier");
1249 }
1250 else if (IS_EQUAL("udf-trans") || IS_EQUAL("udf-trans-tbl"))
1251 {
1252 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF_TRANS_TBL;
1253 if (!(fNamespaces & RTFSISOMAKERCMDNAME_UDF))
1254 return rtFsIsoMakerCmdSyntaxError(pOpts, "udf-trans-tbl must come after the udf name specifier");
1255 }
1256 else if (IS_EQUAL("hfs-trans") || IS_EQUAL("hfs-trans-tbl"))
1257 {
1258 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS_TRANS_TBL;
1259 if (!(fNamespaces & RTFSISOMAKERCMDNAME_HFS))
1260 return rtFsIsoMakerCmdSyntaxError(pOpts, "hfs-trans-tbl must come after the hfs name specifier");
1261 }
1262 else
1263 return rtFsIsoMakerCmdSyntaxError(pOpts, "unknown name specifier '%.*s'", cchName, pchName);
1264#undef IS_EQUAL
1265 offSpec = offEndSpec;
1266 }
1267 } /* while same specifier */
1268
1269 /*
1270 * Check that it wasn't empty.
1271 */
1272 if (fNameSpecifier == 0)
1273 return rtFsIsoMakerCmdSyntaxError(pOpts, "name specifier #%u (0-based) is empty ", iNameSpecifier);
1274
1275 /*
1276 * Complain if a major namespace name is duplicated. The rock-ridge and
1277 * trans.tbl names are simple to replace, the others affect the two former
1278 * names and are therefore not allowed twice in the list.
1279 */
1280 uint32_t i = iNameSpecifier;
1281 while (i-- > 0)
1282 {
1283 uint32_t fRepeated = (fNameSpecifier & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1284 & (pOpts->afNameSpecifiers[i] & RTFSISOMAKERCMDNAME_MAJOR_MASK);
1285 if (fRepeated)
1286 {
1287 char szTmp[128];
1288 return rtFsIsoMakerCmdSyntaxError(pOpts, "repeating name specifier%s: %s", RT_IS_POWER_OF_TWO(fRepeated) ? "" : "s",
1289 rtFsIsoMakerCmdNameSpecifiersToString(fRepeated, szTmp, sizeof(szTmp)));
1290 }
1291 }
1292
1293 /*
1294 * Add it.
1295 */
1296 if (iNameSpecifier >= RT_ELEMENTS(pOpts->afNameSpecifiers))
1297 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many name specifiers (max %d)", RT_ELEMENTS(pOpts->afNameSpecifiers));
1298 pOpts->afNameSpecifiers[iNameSpecifier] = fNameSpecifier;
1299 iNameSpecifier++;
1300
1301 /*
1302 * Next, if any.
1303 */
1304 if (pszSpec[offSpec] == ',')
1305 offSpec++;
1306 } while (pszSpec[offSpec] != '\0');
1307
1308 pOpts->cNameSpecifiers = iNameSpecifier;
1309 pOpts->fDstNamespaces = fNamespaces;
1310
1311 return VINF_SUCCESS;
1312}
1313
1314
1315/**
1316 * Processes a non-option argument.
1317 *
1318 * @returns IPRT status code.
1319 * @param pOpts The ISO maker command instance.
1320 * @param pszSpec The specification of what to add.
1321 * @param fWithSrc Whether the specification includes a source path
1322 * or not.
1323 * @param pParsed Where to return the parsed name specification.
1324 */
1325static int rtFsIsoMakerCmdParseNameSpec(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec, bool fWithSrc,
1326 PRTFSISOMKCMDPARSEDNAMES pParsed)
1327{
1328 const char * const pszSpecIn = pszSpec;
1329 uint32_t const cMaxNames = pOpts->cNameSpecifiers + fWithSrc;
1330
1331 /*
1332 * Split it up by '='.
1333 */
1334 pParsed->cNames = 0;
1335 pParsed->cNamesWithSrc = 0;
1336 pParsed->enmSrcType = fWithSrc ? RTFSISOMKCMDPARSEDNAMES::kSrcType_Normal : RTFSISOMKCMDPARSEDNAMES::kSrcType_None;
1337 for (;;)
1338 {
1339 const char *pszEqual = strchr(pszSpec, '=');
1340 size_t cchName = pszEqual ? pszEqual - pszSpec : strlen(pszSpec);
1341 bool fNeedSlash = (pszEqual || !fWithSrc) && !RTPATH_IS_SLASH(*pszSpec) && cchName > 0;
1342 if (cchName + fNeedSlash >= sizeof(pParsed->aNames[pParsed->cNamesWithSrc].szPath))
1343 return rtFsIsoMakerCmdSyntaxError(pOpts, "name #%u (0-based) is too long: %s", pParsed->cNamesWithSrc, pszSpecIn);
1344 if (pParsed->cNamesWithSrc >= cMaxNames)
1345 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many names specified (max %u%s): %s",
1346 pOpts->cNameSpecifiers, fWithSrc ? " + source" : "", pszSpecIn);
1347 if (!fNeedSlash)
1348 memcpy(pParsed->aNames[pParsed->cNamesWithSrc].szPath, pszSpec, cchName);
1349 else
1350 {
1351 memcpy(&pParsed->aNames[pParsed->cNamesWithSrc].szPath[1], pszSpec, cchName);
1352 pParsed->aNames[pParsed->cNamesWithSrc].szPath[0] = RTPATH_SLASH;
1353 cchName++;
1354 }
1355 pParsed->aNames[pParsed->cNamesWithSrc].szPath[cchName] = '\0';
1356 pParsed->aNames[pParsed->cNamesWithSrc].cchPath = (uint32_t)cchName;
1357 pParsed->cNamesWithSrc++;
1358
1359 if (!pszEqual)
1360 {
1361 if (fWithSrc)
1362 {
1363 if (!cchName)
1364 return rtFsIsoMakerCmdSyntaxError(pOpts, "empty source file name: %s", pszSpecIn);
1365 if (cchName == 8 && strcmp(pszSpec, ":remove:") == 0)
1366 pParsed->enmSrcType = RTFSISOMKCMDPARSEDNAMES::kSrcType_Remove;
1367 else if (cchName == 13 && strcmp(pszSpec, ":must-remove:") == 0)
1368 pParsed->enmSrcType = RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove;
1369 }
1370 break;
1371 }
1372 pszSpec = pszEqual + 1;
1373 }
1374
1375 /*
1376 * If there are too few names specified, move the source and repeat the
1377 * last non-source name. If only source, convert source into a name spec.
1378 */
1379 if (pParsed->cNamesWithSrc < cMaxNames)
1380 {
1381 uint32_t iSrc;
1382 if (!fWithSrc)
1383 iSrc = pParsed->cNamesWithSrc - 1;
1384 else
1385 {
1386 pParsed->aNames[pOpts->cNameSpecifiers] = pParsed->aNames[pParsed->cNamesWithSrc - 1];
1387 iSrc = pParsed->cNamesWithSrc >= 2 ? pParsed->cNamesWithSrc - 2 : 0;
1388 }
1389
1390 /* If the source is a input file name specifier, reduce it to something that starts with a slash. */
1391 if (pParsed->cNamesWithSrc == 1 && fWithSrc)
1392 {
1393 if (RTVfsChainIsSpec(pParsed->aNames[iSrc].szPath))
1394 {
1395 uint32_t offError;
1396 char *pszFinalPath;
1397 int rc = RTVfsChainQueryFinalPath(pParsed->aNames[iSrc].szPath, &pszFinalPath, &offError);
1398 if (RT_FAILURE(rc))
1399 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryFinalPath",
1400 pParsed->aNames[iSrc].szPath, rc, offError, NULL);
1401 pParsed->aNames[iSrc].cchPath = (uint32_t)strlen(pszFinalPath);
1402 if (RTPATH_IS_SLASH(*pszFinalPath))
1403 memcpy(pParsed->aNames[iSrc].szPath, pszFinalPath, pParsed->aNames[iSrc].cchPath + 1);
1404 else
1405 {
1406 memcpy(&pParsed->aNames[iSrc].szPath[1], pszFinalPath, pParsed->aNames[iSrc].cchPath + 1);
1407 pParsed->aNames[iSrc].szPath[0] = RTPATH_SLASH;
1408 pParsed->aNames[iSrc].cchPath++;
1409 }
1410 RTStrFree(pszFinalPath);
1411 }
1412#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
1413 else if ( RTPATH_IS_VOLSEP(pParsed->aNames[iSrc].szPath[1])
1414 && RT_C_IS_ALPHA(pParsed->aNames[iSrc].szPath[0]))
1415 {
1416 if (RTPATH_IS_SLASH(pParsed->aNames[iSrc].szPath[2]))
1417 {
1418 memmove(&pParsed->aNames[iSrc].szPath[0], &pParsed->aNames[iSrc].szPath[2], pParsed->aNames[iSrc].cchPath - 1);
1419 pParsed->aNames[iSrc].cchPath -= 2;
1420 }
1421 else
1422 {
1423 memmove(&pParsed->aNames[iSrc].szPath[1], &pParsed->aNames[iSrc].szPath[2], pParsed->aNames[iSrc].cchPath - 1);
1424 pParsed->aNames[iSrc].szPath[0] = RTPATH_SLASH;
1425 pParsed->aNames[iSrc].cchPath -= 1;
1426 }
1427 }
1428#endif
1429 else if (!RTPATH_IS_SLASH(pParsed->aNames[iSrc].szPath[0]))
1430 {
1431 if (pParsed->aNames[iSrc].cchPath + 2 > sizeof(pParsed->aNames[iSrc].szPath))
1432 return rtFsIsoMakerCmdSyntaxError(pOpts, "name too long: %s", pszSpecIn);
1433 memmove(&pParsed->aNames[iSrc].szPath[1], &pParsed->aNames[iSrc].szPath[0], pParsed->aNames[iSrc].cchPath + 1);
1434 pParsed->aNames[iSrc].szPath[0] = RTPATH_SLASH;
1435 pParsed->aNames[iSrc].cchPath++;
1436 }
1437 }
1438
1439 for (uint32_t iDst = iSrc + 1; iDst < pOpts->cNameSpecifiers; iDst++)
1440 pParsed->aNames[iDst] = pParsed->aNames[iSrc];
1441
1442 pParsed->cNamesWithSrc = cMaxNames;
1443 }
1444 pParsed->cNames = pOpts->cNameSpecifiers;
1445
1446 /*
1447 * Copy the specifier flags and check that the paths all starts with slashes.
1448 */
1449 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
1450 {
1451 pParsed->aNames[i].fNameSpecifiers = pOpts->afNameSpecifiers[i];
1452 Assert( pParsed->aNames[i].cchPath == 0
1453 || RTPATH_IS_SLASH(pParsed->aNames[i].szPath[0]));
1454 }
1455
1456 return VINF_SUCCESS;
1457}
1458
1459
1460/**
1461 * Enteres an object into the namespace by full paths.
1462 *
1463 * This is used by rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath and
1464 * rtFsIsoMakerCmdAddFile.
1465 *
1466 * @returns IPRT status code.
1467 * @param pOpts The ISO maker command instance.
1468 * @param idxObj The configuration index of the object to be named.
1469 * @param pParsed The parsed names.
1470 * @param pszSrcOrName Source file or name.
1471 */
1472static int rtFsIsoMakerCmdSetObjPaths(PRTFSISOMAKERCMDOPTS pOpts, uint32_t idxObj, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1473 const char *pszSrcOrName)
1474{
1475 int rc = VINF_SUCCESS;
1476 for (uint32_t i = 0; i < pParsed->cNames; i++)
1477 if (pParsed->aNames[i].cchPath > 0)
1478 {
1479 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1480 {
1481 rc = RTFsIsoMakerObjSetPath(pOpts->hIsoMaker, idxObj,
1482 pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1483 pParsed->aNames[i].szPath);
1484 if (RT_FAILURE(rc))
1485 {
1486 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting name '%s' on '%s': %Rrc",
1487 pParsed->aNames[i].szPath, pszSrcOrName, rc);
1488 break;
1489 }
1490 }
1491 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MINOR_MASK)
1492 {
1493 /** @todo add APIs for this. */
1494 }
1495 }
1496 return rc;
1497}
1498
1499
1500/**
1501 * Adds a file.
1502 *
1503 * @returns IPRT status code.
1504 * @param pOpts The ISO maker command instance.
1505 * @param pszSrc The path to the source file.
1506 * @param pParsed The parsed names.
1507 * @param pidxObj Where to return the configuration index for the
1508 * added file. Optional.
1509 */
1510static int rtFsIsoMakerCmdAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1511 uint32_t *pidxObj)
1512{
1513 uint32_t idxObj;
1514 int rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(pOpts->hIsoMaker, pszSrc, &idxObj);
1515 if (RT_SUCCESS(rc))
1516 {
1517 pOpts->cItemsAdded++;
1518 if (pidxObj)
1519 *pidxObj = idxObj;
1520
1521 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxObj, pParsed, pszSrc);
1522 }
1523 else
1524 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s': %Rrc", pszSrc, rc);
1525 return rc;
1526}
1527
1528
1529/**
1530 * Adds a file after first making sure it's a file.
1531 *
1532 * @returns IPRT status code
1533 * @param pOpts The ISO maker command instance.
1534 * @param pszSrc The path to the source file.
1535 * @param pParsed The parsed names.
1536 * @param pidxObj Where to return the configuration index for the
1537 * added file. Optional.
1538 */
1539static int rtFsIsoMakerCmdStatAndAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1540 uint32_t *pidxObj)
1541{
1542 RTFSOBJINFO ObjInfo;
1543 uint32_t offError;
1544 RTERRINFOSTATIC ErrInfo;
1545 int rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
1546 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
1547 if (RT_FAILURE(rc))
1548 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
1549
1550 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1551 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, pParsed, pidxObj);
1552 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_A_FILE, "Not a file: %s", pszSrc);
1553}
1554
1555
1556/**
1557 * Processes a non-option argument.
1558 *
1559 * @returns IPRT status code.
1560 * @param pOpts The ISO maker command instance.
1561 * @param pszSpec The specification of what to add.
1562 */
1563static int rtFsIsoMakerCmdAddSomething(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
1564{
1565 /*
1566 * Parse the name spec.
1567 */
1568 RTFSISOMKCMDPARSEDNAMES Parsed;
1569 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszSpec, true /*fWithSrc*/, &Parsed);
1570 if (RT_FAILURE(rc))
1571 return rc;
1572
1573 /*
1574 * Deal with special source filenames used to remove/change stuff.
1575 */
1576 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_Remove
1577 || Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove)
1578 {
1579 const char *pszFirstNm = NULL;
1580 uint32_t cRemoved = 0;
1581 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
1582 if ( Parsed.aNames[i].cchPath > 0
1583 && (Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK))
1584 {
1585 pszFirstNm = Parsed.aNames[i].szPath;
1586 uint32_t idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
1587 Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1588 Parsed.aNames[i].szPath);
1589 if (idxObj != UINT32_MAX)
1590 {
1591 rc = RTFsIsoMakerObjRemove(pOpts->hIsoMaker, idxObj);
1592 if (RT_FAILURE(rc))
1593 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to remove '%s': %Rrc", pszSpec, rc);
1594 cRemoved++;
1595 }
1596 }
1597 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove
1598 && cRemoved == 0)
1599 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "Failed to locate '%s' for removal", pszSpec);
1600 }
1601 /*
1602 * Add regular source.
1603 */
1604 else
1605 {
1606 const char *pszSrc = Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath;
1607 RTFSOBJINFO ObjInfo;
1608 uint32_t offError;
1609 RTERRINFOSTATIC ErrInfo;
1610 rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
1611 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
1612 if (RT_FAILURE(rc))
1613 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
1614
1615 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1616 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, &Parsed, NULL /*pidxObj*/);
1617
1618 if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1619 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding directory '%s' failed: not implemented", pszSpec);
1620
1621 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
1622 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding symlink '%s' failed: not implemented", pszSpec);
1623
1624 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding special file '%s' failed: not implemented", pszSpec);
1625 }
1626
1627 return VINF_SUCCESS;
1628}
1629
1630
1631/**
1632 * Deals with the --import-iso {iso-file-spec} options.
1633 *
1634 * @returns IPRT status code
1635 * @param pOpts The ISO maker command instance.
1636 * @param pszIsoSpec The ISO path specifier.
1637 */
1638static int rtFsIsoMakerCmdOptImportIso(PRTFSISOMAKERCMDOPTS pOpts, const char *pszIsoSpec)
1639{
1640 RTFSISOMAKERIMPORTRESULTS Results;
1641 RTERRINFOSTATIC ErrInfo;
1642 int rc = RTFsIsoMakerImport(pOpts->hIsoMaker, pszIsoSpec, 0 /*fFlags*/, &Results, RTErrInfoInitStatic(&ErrInfo));
1643
1644 pOpts->cItemsAdded += Results.cAddedFiles;
1645 pOpts->cItemsAdded += Results.cAddedSymlinks;
1646 pOpts->cItemsAdded += Results.cAddedDirs;
1647 pOpts->cItemsAdded += Results.cBootCatEntries != UINT32_MAX ? Results.cBootCatEntries : 0;
1648 pOpts->cItemsAdded += Results.cbSysArea != 0 ? 1 : 0;
1649
1650 rtFsIsoMakerPrintf(pOpts, "ISO imported statistics for '%s'\n", pszIsoSpec);
1651 rtFsIsoMakerPrintf(pOpts, " cAddedNames: %'14RU32\n", Results.cAddedNames);
1652 rtFsIsoMakerPrintf(pOpts, " cAddedDirs: %'14RU32\n", Results.cAddedDirs);
1653 rtFsIsoMakerPrintf(pOpts, " cbAddedDataBlocks: %'14RU64 bytes\n", Results.cbAddedDataBlocks);
1654 rtFsIsoMakerPrintf(pOpts, " cAddedFiles: %'14RU32\n", Results.cAddedFiles);
1655 rtFsIsoMakerPrintf(pOpts, " cAddedSymlinks: %'14RU32\n", Results.cAddedSymlinks);
1656 if (Results.cBootCatEntries == UINT32_MAX)
1657 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: none\n");
1658 else
1659 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: %'14RU32\n", Results.cBootCatEntries);
1660 rtFsIsoMakerPrintf(pOpts, " cbSysArea: %'14RU32\n", Results.cbSysArea);
1661 rtFsIsoMakerPrintf(pOpts, " cErrors: %'14RU32\n", Results.cErrors);
1662
1663 if (RT_SUCCESS(rc))
1664 return rc;
1665 if (Results.offError != UINT32_MAX)
1666 return rtFsIsoMakerCmdChainError(pOpts, "RTFsIsoMakerImport", pszIsoSpec, rc, Results.offError, &ErrInfo.Core);
1667 if (RTErrInfoIsSet(&ErrInfo.Core))
1668 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc - %s", rc, ErrInfo.Core.pszMsg);
1669 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc", rc);
1670}
1671
1672
1673/**
1674 * Deals with: -G|--generic-boot {file}
1675 *
1676 * This concers content the first 16 sectors of the image. We start loading the
1677 * file at byte 0 in the image and stops at 32KB.
1678 *
1679 * @returns IPRT status code
1680 * @param pOpts The ISO maker command instance.
1681 * @param pszGenericBootImage The generic boot image source.
1682 */
1683static int rtFsIsoMakerCmdOptGenericBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszGenericBootImage)
1684{
1685 RTERRINFOSTATIC ErrInfo;
1686 uint32_t offError;
1687 RTVFSFILE hVfsFile;
1688 int rc = RTVfsChainOpenFile(pszGenericBootImage, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
1689 &offError, RTErrInfoInitStatic(&ErrInfo));
1690 if (RT_FAILURE(rc))
1691 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszGenericBootImage, rc, offError, &ErrInfo.Core);
1692
1693 uint8_t abBuf[_32K];
1694 size_t cbRead;
1695 rc = RTVfsFileReadAt(hVfsFile, 0, abBuf, sizeof(abBuf), &cbRead);
1696 RTVfsFileRelease(hVfsFile);
1697 if (RT_FAILURE(rc))
1698 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error reading 32KB from generic boot image '%s': %Rrc", pszGenericBootImage, rc);
1699
1700 rc = RTFsIsoMakerSetSysAreaContent(pOpts->hIsoMaker, abBuf, cbRead, 0);
1701 if (RT_FAILURE(rc))
1702 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerSetSysAreaContent failed with a %zu bytes input: %Rrc", cbRead, rc);
1703
1704 return VINF_SUCCESS;
1705}
1706
1707
1708/**
1709 * Helper that makes sure we've got a validation boot entry.
1710 *
1711 * @returns IPRT status code
1712 * @param pOpts The ISO maker command instance.
1713 */
1714static void rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(PRTFSISOMAKERCMDOPTS pOpts)
1715{
1716 if (pOpts->cBootCatEntries == 0)
1717 {
1718 pOpts->aBootCatEntries[0].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation;
1719 pOpts->aBootCatEntries[0].u.Validation.idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
1720 pOpts->aBootCatEntries[0].u.Validation.pszString = NULL;
1721 pOpts->cBootCatEntries = 1;
1722 }
1723}
1724
1725
1726/**
1727 * Helper that makes sure we've got a current boot entry.
1728 *
1729 * @returns IPRT status code
1730 * @param pOpts The ISO maker command instance.
1731 * @param fForceNew Whether to force a new entry.
1732 * @param pidxBootCat Where to return the boot catalog index.
1733 */
1734static int rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(PRTFSISOMAKERCMDOPTS pOpts, bool fForceNew, uint32_t *pidxBootCat)
1735{
1736 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
1737
1738 uint32_t i = pOpts->cBootCatEntries;
1739 if (i == 2 && fForceNew)
1740 {
1741 pOpts->aBootCatEntries[i].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
1742 pOpts->aBootCatEntries[i].u.SectionHeader.idPlatform = pOpts->aBootCatEntries[0].u.Validation.idPlatform;
1743 pOpts->aBootCatEntries[i].u.SectionHeader.pszString = NULL;
1744 pOpts->cBootCatEntries = ++i;
1745 }
1746
1747 if ( i == 1
1748 || fForceNew
1749 || pOpts->aBootCatEntries[i - 1].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
1750 {
1751 if (i >= RT_ELEMENTS(pOpts->aBootCatEntries))
1752 {
1753 *pidxBootCat = UINT32_MAX;
1754 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
1755 }
1756
1757 pOpts->aBootCatEntries[i].enmType = i == 1 ? RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
1758 : RTFSISOMKCMDELTORITOENTRY::kEntryType_Section;
1759 pOpts->aBootCatEntries[i].u.Section.pszImageNameInIso = NULL;
1760 pOpts->aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
1761 pOpts->aBootCatEntries[i].u.Section.fInsertBootInfoTable = false;
1762 pOpts->aBootCatEntries[i].u.Section.fBootable = true;
1763 pOpts->aBootCatEntries[i].u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK;
1764 pOpts->aBootCatEntries[i].u.Section.bSystemType = 1 /*FAT12*/;
1765 pOpts->aBootCatEntries[i].u.Section.uLoadSeg = 0x7c0;
1766 pOpts->aBootCatEntries[i].u.Section.cSectorsToLoad = 4;
1767 pOpts->cBootCatEntries = ++i;
1768 }
1769
1770 *pidxBootCat = i - 1;
1771 return VINF_SUCCESS;
1772}
1773
1774
1775/**
1776 * Deals with: --boot-catalog <path-spec>
1777 *
1778 * This enters the boot catalog into the namespaces of the image. The path-spec
1779 * is similar to what rtFsIsoMakerCmdAddSomething processes, only there isn't a
1780 * source file part.
1781 *
1782 * @returns IPRT status code
1783 * @param pOpts The ISO maker command instance.
1784 * @param pszGenericBootImage The generic boot image source.
1785 */
1786static int rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootCat)
1787{
1788 /* Make sure we'll fail later if no other boot options are present. */
1789 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
1790
1791 /* Parse the name spec. */
1792 RTFSISOMKCMDPARSEDNAMES Parsed;
1793 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootCat, false /*fWithSrc*/, &Parsed);
1794 if (RT_SUCCESS(rc))
1795 {
1796 /* Query/create the boot catalog and enter it into the name spaces. */
1797 uint32_t idxBootCatObj;
1798 rc = RTFsIsoMakerQueryObjIdxForBootCatalog(pOpts->hIsoMaker, &idxBootCatObj);
1799 if (RT_SUCCESS(rc))
1800 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxBootCatObj, &Parsed, "boot catalog");
1801 else
1802 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerQueryBootCatalogPathObjIdx failed: %Rrc", rc);
1803 }
1804 return rc;
1805}
1806
1807
1808/**
1809 * Deals with: --eltorito-add-image {file-spec}
1810 *
1811 * This differs from -b|--eltorito-boot in that it takes a source file
1812 * specification identical to what rtFsIsoMakerCmdAddSomething processes instead
1813 * of a reference to a file in the image.
1814 *
1815 * This operates on the current eltorito boot catalog entry.
1816 *
1817 * @returns IPRT status code
1818 * @param pOpts The ISO maker command instance.
1819 * @param pszGenericBootImage The generic boot image source.
1820 */
1821static int rtFsIsoMakerCmdOptEltoritoAddImage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImageSpec)
1822{
1823 /* Parse the name spec. */
1824 RTFSISOMKCMDPARSEDNAMES Parsed;
1825 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootImageSpec, true /*fWithSrc*/, &Parsed);
1826 if (RT_SUCCESS(rc))
1827 {
1828 uint32_t idxBootCat;
1829 rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
1830 if (RT_SUCCESS(rc))
1831 {
1832 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
1833 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
1834 rc = rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
1835 else
1836 {
1837 uint32_t idxImageObj;
1838 rc = rtFsIsoMakerCmdStatAndAddFile(pOpts, Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath, &Parsed, &idxImageObj);
1839 if (RT_SUCCESS(rc))
1840 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
1841 }
1842 }
1843 }
1844
1845 return rc;
1846}
1847
1848
1849/**
1850 * Deals with: -b|--eltorito-boot {file-in-iso}
1851 *
1852 * This operates on the current eltorito boot catalog entry.
1853 *
1854 * @returns IPRT status code
1855 * @param pOpts The ISO maker command instance.
1856 * @param pszGenericBootImage The generic boot image source.
1857 */
1858static int rtFsIsoMakerCmdOptEltoritoBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImage)
1859{
1860 uint32_t idxBootCat;
1861 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
1862 if (RT_SUCCESS(rc))
1863 {
1864 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
1865 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
1866 return rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
1867
1868 uint32_t idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
1869 if (idxImageObj == UINT32_MAX)
1870 pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso = pszBootImage;
1871 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
1872 }
1873 return rc;
1874}
1875
1876
1877/**
1878 * Deals with: --eltorito-platform-id {x86|PPC|Mac|efi|number}
1879 *
1880 * Operates on the validation entry or a section header.
1881 *
1882 * @returns IPRT status code
1883 * @param pOpts The ISO maker command instance.
1884 * @param pszPlatformId The platform ID.
1885 */
1886static int rtFsIsoMakerCmdOptEltoritoPlatformId(PRTFSISOMAKERCMDOPTS pOpts, const char *pszPlatformId)
1887{
1888 /* Decode it. */
1889 uint8_t idPlatform;
1890 if (strcmp(pszPlatformId, "x86") == 0)
1891 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
1892 else if (strcmp(pszPlatformId, "PPC") == 0)
1893 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_PPC;
1894 else if (strcmp(pszPlatformId, "Mac") == 0)
1895 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_MAC;
1896 else if (strcmp(pszPlatformId, "efi") == 0)
1897 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_EFI;
1898 else
1899 {
1900 int rc = RTStrToUInt8Full(pszPlatformId, 0, &idPlatform);
1901 if (rc != VINF_SUCCESS)
1902 return rtFsIsoMakerCmdSyntaxError(pOpts, "invalid or unknown platform ID: %s", pszPlatformId);
1903 }
1904
1905 /* If this option comes before anything related to the default entry, work
1906 on the validation entry. */
1907 if (pOpts->cBootCatEntries <= 1)
1908 {
1909 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
1910 pOpts->aBootCatEntries[0].u.Validation.idPlatform = idPlatform;
1911 }
1912 /* Otherwise, work on the current section header, creating a new one if necessary. */
1913 else
1914 {
1915 uint32_t idxBootCat = pOpts->cBootCatEntries - 1;
1916 if (pOpts->aBootCatEntries[idxBootCat].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
1917 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
1918 else
1919 {
1920 idxBootCat++;
1921 if (idxBootCat + 2 > RT_ELEMENTS(pOpts->aBootCatEntries))
1922 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
1923
1924 pOpts->aBootCatEntries[idxBootCat].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
1925 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
1926 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.pszString = NULL;
1927 pOpts->cBootCatEntries = idxBootCat + 1;
1928 }
1929 }
1930 return VINF_SUCCESS;
1931}
1932
1933
1934/**
1935 * Deals with: -no-boot
1936 *
1937 * This operates on the current eltorito boot catalog entry.
1938 *
1939 * @returns IPRT status code
1940 * @param pOpts The ISO maker command instance.
1941 */
1942static int rtFsIsoMakerCmdOptEltoritoSetNotBootable(PRTFSISOMAKERCMDOPTS pOpts)
1943{
1944 uint32_t idxBootCat;
1945 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
1946 if (RT_SUCCESS(rc))
1947 pOpts->aBootCatEntries[idxBootCat].u.Section.fBootable = false;
1948 return rc;
1949}
1950
1951
1952/**
1953 * Deals with: -hard-disk-boot, -no-emulation-boot, --eltorito-floppy-12,
1954 * --eltorito-floppy-144, --eltorito-floppy-288
1955 *
1956 * This operates on the current eltorito boot catalog entry.
1957 *
1958 * @returns IPRT status code
1959 * @param pOpts The ISO maker command instance.
1960 * @param bMediaType The media type.
1961 */
1962static int rtFsIsoMakerCmdOptEltoritoSetMediaType(PRTFSISOMAKERCMDOPTS pOpts, uint8_t bMediaType)
1963{
1964 uint32_t idxBootCat;
1965 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
1966 if (RT_SUCCESS(rc))
1967 pOpts->aBootCatEntries[idxBootCat].u.Section.bBootMediaType = bMediaType;
1968 return rc;
1969}
1970
1971
1972/**
1973 * Deals with: -boot-load-seg {seg}
1974 *
1975 * This operates on the current eltorito boot catalog entry.
1976 *
1977 * @returns IPRT status code
1978 * @param pOpts The ISO maker command instance.
1979 * @param uSeg The load segment.
1980 */
1981static int rtFsIsoMakerCmdOptEltoritoSetLoadSegment(PRTFSISOMAKERCMDOPTS pOpts, uint16_t uSeg)
1982{
1983 uint32_t idxBootCat;
1984 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
1985 if (RT_SUCCESS(rc))
1986 pOpts->aBootCatEntries[idxBootCat].u.Section.uLoadSeg = uSeg;
1987 return rc;
1988}
1989
1990
1991/**
1992 * Deals with: -boot-load-size {sectors}
1993 *
1994 * This operates on the current eltorito boot catalog entry.
1995 *
1996 * @returns IPRT status code
1997 * @param pOpts The ISO maker command instance.
1998 * @param cSectors Number of emulated sectors to load
1999 */
2000static int rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(PRTFSISOMAKERCMDOPTS pOpts, uint16_t cSectors)
2001{
2002 uint32_t idxBootCat;
2003 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2004 if (RT_SUCCESS(rc))
2005 pOpts->aBootCatEntries[idxBootCat].u.Section.cSectorsToLoad = cSectors;
2006 return rc;
2007}
2008
2009
2010/**
2011 * Deals with: -boot-info-table
2012 *
2013 * This operates on the current eltorito boot catalog entry.
2014 *
2015 * @returns IPRT status code
2016 * @param pOpts The ISO maker command instance.
2017 */
2018static int rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(PRTFSISOMAKERCMDOPTS pOpts)
2019{
2020 uint32_t idxBootCat;
2021 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2022 if (RT_SUCCESS(rc))
2023 pOpts->aBootCatEntries[idxBootCat].u.Section.fInsertBootInfoTable = true;
2024 return rc;
2025}
2026
2027
2028/**
2029 * Validates and commits the boot catalog stuff.
2030 *
2031 * ASSUMING this is called after all options are parsed and there is only this
2032 * one call.
2033 *
2034 * @returns IPRT status code
2035 * @param pOpts The ISO maker command instance.
2036 */
2037static int rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(PRTFSISOMAKERCMDOPTS pOpts)
2038{
2039 if (pOpts->cBootCatEntries == 0)
2040 return VINF_SUCCESS;
2041
2042 /*
2043 * Locate and configure the boot images first.
2044 */
2045 int rc;
2046 PRTFSISOMKCMDELTORITOENTRY pBootCatEntry = &pOpts->aBootCatEntries[1];
2047 for (uint32_t idxBootCat = 1; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2048 if ( pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
2049 || pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Section)
2050 {
2051 /* Make sure we've got a boot image. */
2052 uint32_t idxImageObj = pBootCatEntry->u.Section.idxImageObj;
2053 if (idxImageObj == UINT32_MAX)
2054 {
2055 const char *pszBootImage = pBootCatEntry->u.Section.pszImageNameInIso;
2056 if (pszBootImage == NULL)
2057 return rtFsIsoMakerCmdSyntaxError(pOpts, "No image name given for boot catalog entry #%u", idxBootCat);
2058
2059 idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
2060 if (idxImageObj == UINT32_MAX)
2061 return rtFsIsoMakerCmdSyntaxError(pOpts, "Unable to locate image for boot catalog entry #%u: %s",
2062 idxBootCat, pszBootImage);
2063 pBootCatEntry->u.Section.idxImageObj = idxImageObj;
2064 }
2065
2066 /* Enable patching it? */
2067 if (pBootCatEntry->u.Section.fInsertBootInfoTable)
2068 {
2069 rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pOpts->hIsoMaker, idxImageObj, true);
2070 if (RT_FAILURE(rc))
2071 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2072 "RTFsIsoMakerObjEnableBootInfoTablePatching failed on entry #%u: %Rrc",
2073 idxBootCat, rc);
2074 }
2075
2076 /* Figure out the floppy type given the object size. */
2077 if (pBootCatEntry->u.Section.bBootMediaType == ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
2078 {
2079 uint64_t cbImage;
2080 rc = RTFsIsoMakerObjQueryDataSize(pOpts->hIsoMaker, idxImageObj, &cbImage);
2081 if (RT_FAILURE(rc))
2082 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerObjGetDataSize failed on entry #%u: %Rrc",
2083 idxBootCat, rc);
2084 if (cbImage == 1228800)
2085 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB;
2086 else if (cbImage <= 1474560)
2087 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB;
2088 else if (cbImage <= 2949120)
2089 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB;
2090 else
2091 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK;
2092 }
2093 }
2094
2095 /*
2096 * Add the boot catalog entries.
2097 */
2098 pBootCatEntry = &pOpts->aBootCatEntries[0];
2099 for (uint32_t idxBootCat = 0; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2100 switch (pBootCatEntry->enmType)
2101 {
2102 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation:
2103 Assert(idxBootCat == 0);
2104 rc = RTFsIsoMakerBootCatSetValidationEntry(pOpts->hIsoMaker, pBootCatEntry->u.Validation.idPlatform,
2105 pBootCatEntry->u.Validation.pszString);
2106 if (RT_FAILURE(rc))
2107 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetValidationEntry failed: %Rrc", rc);
2108 break;
2109
2110 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Default:
2111 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Section:
2112 Assert(pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default ? idxBootCat == 1 : idxBootCat > 2);
2113 rc = RTFsIsoMakerBootCatSetSectionEntry(pOpts->hIsoMaker, idxBootCat,
2114 pBootCatEntry->u.Section.idxImageObj,
2115 pBootCatEntry->u.Section.bBootMediaType,
2116 pBootCatEntry->u.Section.bSystemType,
2117 pBootCatEntry->u.Section.fBootable,
2118 pBootCatEntry->u.Section.uLoadSeg,
2119 pBootCatEntry->u.Section.cSectorsToLoad,
2120 ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE, NULL, 0);
2121 if (RT_FAILURE(rc))
2122 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetSectionEntry failed on entry #%u: %Rrc",
2123 idxBootCat, rc);
2124 break;
2125
2126 case RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader:
2127 {
2128 uint32_t cEntries = 1;
2129 while ( idxBootCat + cEntries < pOpts->cBootCatEntries
2130 && pBootCatEntry[cEntries].enmType != RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2131 cEntries++;
2132 cEntries--;
2133
2134 Assert(idxBootCat > 1);
2135 rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pOpts->hIsoMaker, idxBootCat, cEntries,
2136 pBootCatEntry->u.SectionHeader.idPlatform,
2137 pBootCatEntry->u.SectionHeader.pszString);
2138 if (RT_FAILURE(rc))
2139 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2140 "RTFsIsoMakerBootCatSetSectionHeaderEntry failed on entry #%u: %Rrc",
2141 idxBootCat, rc);
2142 break;
2143 }
2144
2145 default:
2146 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
2147 }
2148
2149 return VINF_SUCCESS;
2150}
2151
2152
2153/**
2154 * Deals with: --eltorito-new-entry, --eltorito-alt-boot
2155 *
2156 * This operates on the current eltorito boot catalog entry.
2157 *
2158 * @returns IPRT status code
2159 * @param pOpts The ISO maker command instance.
2160 */
2161static int rtFsIsoMakerCmdOptEltoritoNewEntry(PRTFSISOMAKERCMDOPTS pOpts)
2162{
2163 uint32_t idxBootCat;
2164 return rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, true /*fForceNew*/, &idxBootCat);
2165}
2166
2167
2168/**
2169 * Sets a string property in all namespaces.
2170 *
2171 * @returns IPRT status code.
2172 * @param pOpts The ISO maker command instance.
2173 * @param pszValue The new string value.
2174 * @param enmStringProp The string property.
2175 */
2176static int rtFsIsoMakerCmdOptSetStringProp(PRTFSISOMAKERCMDOPTS pOpts, const char *pszValue, RTFSISOMAKERSTRINGPROP enmStringProp)
2177{
2178 int rc = RTFsIsoMakerSetStringProp(pOpts->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_ALL, pszValue);
2179 if (RT_FAILURE(rc))
2180 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set string property %d to '%s': %Rrc", enmStringProp, pszValue, rc);
2181 return rc;
2182}
2183
2184
2185/**
2186 * Loads an argument file (e.g. a .iso-file) and parses it.
2187 *
2188 * @returns IPRT status code.
2189 * @param pOpts The ISO maker command instance.
2190 * @param pszFileSpec The file to parse.
2191 * @param cDepth The current nesting depth.
2192 */
2193static int rtFsIsoMakerCmdParseArgumentFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFileSpec, unsigned cDepth)
2194{
2195 if (cDepth > 2)
2196 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_INVALID_PARAMETER, "Too many nested argument files!");
2197
2198 /*
2199 * Read the file into memory.
2200 */
2201 RTERRINFOSTATIC ErrInfo;
2202 uint32_t offError;
2203 RTVFSFILE hVfsFile;
2204 int rc = RTVfsChainOpenFile(pszFileSpec, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
2205 &offError, RTErrInfoInitStatic(&ErrInfo));
2206 if (RT_FAILURE(rc))
2207 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszFileSpec, rc, offError, &ErrInfo.Core);
2208
2209 uint64_t cbFile = 0;
2210 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
2211 if (RT_SUCCESS(rc))
2212 {
2213 if (cbFile < _2M)
2214 {
2215 char *pszContent = (char *)RTMemTmpAllocZ((size_t)cbFile + 1);
2216 if (pszContent)
2217 {
2218 rc = RTVfsFileRead(hVfsFile, pszContent, (size_t)cbFile, NULL);
2219 if (RT_SUCCESS(rc))
2220 {
2221 /*
2222 * Check that it's valid UTF-8 and turn it into an argument vector.
2223 */
2224 rc = RTStrValidateEncodingEx(pszContent, (size_t)cbFile + 1,
2225 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
2226 if (RT_SUCCESS(rc))
2227 {
2228 uint32_t fGetOpt = strstr(pszContent, "--iprt-iso-maker-file-marker-ms") == NULL
2229 ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
2230 fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
2231 char **papszArgs;
2232 int cArgs;
2233 rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
2234 if (RT_SUCCESS(rc))
2235 {
2236 /*
2237 * Parse them.
2238 */
2239 rc = rtFsIsoMakerCmdParse(pOpts, cArgs, papszArgs, cDepth + 1);
2240
2241 RTGetOptArgvFreeEx(papszArgs, fGetOpt);
2242 }
2243 else
2244 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTGetOptArgvFromString failed: %Rrc", pszFileSpec, rc);
2245
2246 }
2247 else
2248 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: invalid encoding", pszFileSpec);
2249 }
2250 else
2251 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: error to read it into memory: %Rrc", pszFileSpec, rc);
2252 RTMemTmpFree(pszContent);
2253 }
2254 else
2255 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "%s: failed to allocte %zu bytes for reading",
2256 pszFileSpec, (size_t)cbFile + 1);
2257 }
2258 else
2259 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_FILE_TOO_BIG, "%s: file is too big: %'RU64 bytes, max 2MB", pszFileSpec, cbFile);
2260 }
2261 else
2262 rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTVfsFileGetSize failed: %Rrc", pszFileSpec, rc);
2263 RTVfsFileRelease(hVfsFile);
2264 return rc;
2265}
2266
2267
2268/**
2269 * Parses the given command line options.
2270 *
2271 * @returns IPRT status code.
2272 * @param pOpts The ISO maker command instance.
2273 * @param cArgs Number of arguments in papszArgs.
2274 * @param papszArgs The argument vector to parse.
2275 */
2276static int rtFsIsoMakerCmdParse(PRTFSISOMAKERCMDOPTS pOpts, unsigned cArgs, char **papszArgs, unsigned cDepth)
2277{
2278 /* Setup option parsing. */
2279 RTGETOPTSTATE GetState;
2280 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, g_aRtFsIsoMakerOptions, RT_ELEMENTS(g_aRtFsIsoMakerOptions),
2281 cDepth == 0 ? 1 : 0 /*iFirst*/, 0 /*fFlags*/);
2282 if (RT_FAILURE(rc))
2283 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTGetOpt failed: %Rrc", rc);
2284
2285 /*
2286 * Parse parameters. Parameters are position dependent.
2287 */
2288 RTGETOPTUNION ValueUnion;
2289 while ( RT_SUCCESS(rc)
2290 && (rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2291 {
2292 switch (rc)
2293 {
2294 /*
2295 * Files and directories.
2296 */
2297 case VINF_GETOPT_NOT_OPTION:
2298 if ( *ValueUnion.psz != '@'
2299 || strchr(ValueUnion.psz, '='))
2300 rc = rtFsIsoMakerCmdAddSomething(pOpts, ValueUnion.psz);
2301 else
2302 rc = rtFsIsoMakerCmdParseArgumentFile(pOpts, ValueUnion.psz + 1, cDepth);
2303 break;
2304
2305 /*
2306 * Options specific to our ISO maker.
2307 */
2308 case RTFSISOMAKERCMD_OPT_NAME_SETUP:
2309 rc = rtFsIsoMakerCmdOptNameSetup(pOpts, ValueUnion.psz);
2310 break;
2311
2312 case RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER:
2313 /* ignored */
2314 break;
2315
2316 case RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE: /* --output-buffer-size {cb} */
2317 pOpts->cbOutputReadBuffer = ValueUnion.u32;
2318 break;
2319
2320 case RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE: /* --random-output-buffer-size */
2321 pOpts->fRandomOutputReadBufferSize = true;
2322 break;
2323
2324 case RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION: /* --random-order-verficiation {cb} */
2325 pOpts->cbRandomOrderVerifciationBlock = ValueUnion.u32;
2326 break;
2327
2328 case RTFSISOMAKERCMD_OPT_IMPORT_ISO:
2329 rc = rtFsIsoMakerCmdOptImportIso(pOpts, ValueUnion.psz);
2330 break;
2331
2332 /*
2333 * Joliet related options.
2334 */
2335 case RTFSISOMAKERCMD_OPT_NO_JOLIET:
2336 rc = RTFsIsoMakerSetJolietUcs2Level(pOpts->hIsoMaker, 0);
2337 if (RT_FAILURE(rc))
2338 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to disable joliet: %Rrc", rc);
2339 break;
2340
2341
2342 /*
2343 * Boot related options.
2344 */
2345 case 'G': /* --generic-boot <file> */
2346 rc = rtFsIsoMakerCmdOptGenericBoot(pOpts, ValueUnion.psz);
2347 break;
2348
2349 case RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE:
2350 rc = rtFsIsoMakerCmdOptEltoritoAddImage(pOpts, ValueUnion.psz);
2351 break;
2352
2353 case 'b': /* --eltorito-boot <boot.img> */
2354 rc = rtFsIsoMakerCmdOptEltoritoBoot(pOpts, ValueUnion.psz);
2355 break;
2356
2357 case RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY:
2358 rc = rtFsIsoMakerCmdOptEltoritoNewEntry(pOpts);
2359 break;
2360
2361 case RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID:
2362 rc = rtFsIsoMakerCmdOptEltoritoPlatformId(pOpts, ValueUnion.psz);
2363 break;
2364
2365 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT:
2366 rc = rtFsIsoMakerCmdOptEltoritoSetNotBootable(pOpts);
2367 break;
2368
2369 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12:
2370 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB);
2371 break;
2372 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144:
2373 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB);
2374 break;
2375 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288:
2376 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB);
2377 break;
2378 case RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT:
2379 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK);
2380 break;
2381 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT:
2382 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION);
2383 break;
2384
2385 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG:
2386 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSegment(pOpts, ValueUnion.u16);
2387 break;
2388
2389 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE:
2390 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(pOpts, ValueUnion.u16);
2391 break;
2392
2393 case RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE:
2394 rc = rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(pOpts);
2395 break;
2396
2397 case 'c': /* --boot-catalog <cd-path> */
2398 rc = rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(pOpts, ValueUnion.psz);
2399 break;
2400
2401 /*
2402 * Image/namespace property related options.
2403 */
2404 case RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID:
2405 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
2406 break;
2407
2408 case 'A': /* --application-id */
2409 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
2410 break;
2411
2412 case RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID:
2413 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
2414 break;
2415
2416 case RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID:
2417 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
2418 break;
2419
2420 case 'P': /* -publisher */
2421 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
2422 break;
2423
2424 case 'p': /* --preparer*/
2425 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
2426 break;
2427
2428 case RTFSISOMAKERCMD_OPT_SYSTEM_ID:
2429 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
2430 break;
2431
2432 case RTFSISOMAKERCMD_OPT_VOLUME_ID: /* (should've been '-V') */
2433 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_ID);
2434 break;
2435
2436 case RTFSISOMAKERCMD_OPT_VOLUME_SET_ID:
2437 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
2438 break;
2439
2440 /*
2441 * Options compatible with other ISO makers.
2442 */
2443 case 'o':
2444 if (pOpts->fVirtualImageMaker)
2445 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is not allowed");
2446 if (pOpts->pszOutFile)
2447 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is specified more than once");
2448 pOpts->pszOutFile = ValueUnion.psz;
2449 break;
2450
2451 /*
2452 * Standard bits.
2453 */
2454 case 'h':
2455 rtFsIsoMakerCmdUsage(pOpts, papszArgs[0]);
2456 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_SUCCESS;
2457
2458 case 'V':
2459 rtFsIsoMakerPrintf(pOpts, "%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
2460 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_SUCCESS;
2461
2462 default:
2463 if (rc > 0 && RT_C_IS_GRAPH(rc))
2464 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: -%c", rc);
2465 else if (rc > 0)
2466 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: %i (%#x)", rc, rc);
2467 else if (rc == VERR_GETOPT_UNKNOWN_OPTION)
2468 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Unknown option: '%s'", ValueUnion.psz);
2469 else if (ValueUnion.pDef)
2470 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: %Rrs", ValueUnion.pDef->pszLong, rc);
2471 else
2472 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%Rrs", rc);
2473 return rc;
2474 }
2475 if (RT_FAILURE(rc))
2476 return rc;
2477 }
2478 return VINF_SUCCESS;
2479}
2480
2481
2482/**
2483 * Extended ISO maker command.
2484 *
2485 * This can be used as a ISO maker command that produces a image file, or
2486 * alternatively for setting up a virtual ISO in memory.
2487 *
2488 * @returns IPRT status code
2489 * @param cArgs Number of arguments.
2490 * @param papszArgs Pointer to argument array.
2491 * @param phVfsFile Where to return the virtual ISO. Pass NULL to
2492 * for normal operation (creates file on disk).
2493 * @param pErrInfo Where to return extended error information in
2494 * the virtual ISO mode.
2495 */
2496RTDECL(int) RTFsIsoMakerCmdEx(unsigned cArgs, char **papszArgs, PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo)
2497{
2498 /*
2499 * Create instance.
2500 */
2501 RTFSISOMAKERCMDOPTS Opts;
2502 RT_ZERO(Opts);
2503 Opts.hIsoMaker = NIL_RTFSISOMAKER;
2504 Opts.pErrInfo = pErrInfo;
2505 Opts.fVirtualImageMaker = phVfsFile != NULL;
2506 Opts.cNameSpecifiers = 1;
2507 Opts.afNameSpecifiers[0] = RTFSISOMAKERCMDNAME_MAJOR_MASK;
2508 Opts.fDstNamespaces = RTFSISOMAKERCMDNAME_MAJOR_MASK;
2509 if (phVfsFile)
2510 *phVfsFile = NIL_RTVFSFILE;
2511 for (uint32_t i = 0; i < RT_ELEMENTS(Opts.aBootCatEntries); i++)
2512 Opts.aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
2513
2514 /* Create the ISO creator instance. */
2515 int rc = RTFsIsoMakerCreate(&Opts.hIsoMaker);
2516 if (RT_FAILURE(rc))
2517 return rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreate failed: %Rrc", rc);
2518
2519 /*
2520 * Parse the command line and check for mandatory options.
2521 */
2522 rc = rtFsIsoMakerCmdParse(&Opts, cArgs, papszArgs, 0);
2523 if (RT_SUCCESS(rc))
2524 {
2525 if (!Opts.cItemsAdded)
2526 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_NO_DATA, "Cowardly refuses to create empty ISO image");
2527 else if (!Opts.pszOutFile && !Opts.fVirtualImageMaker)
2528 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_INVALID_PARAMETER, "No output file specified (--output <file>)");
2529 }
2530
2531 /*
2532 * Final actions.
2533 */
2534 if (RT_SUCCESS(rc))
2535 rc = rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(&Opts);
2536 if (RT_SUCCESS(rc))
2537 {
2538 /*
2539 * Finalize the image and get the virtual file.
2540 */
2541 rc = RTFsIsoMakerFinalize(Opts.hIsoMaker);
2542 if (RT_SUCCESS(rc))
2543 {
2544 RTVFSFILE hVfsFile;
2545 rc = RTFsIsoMakerCreateVfsOutputFile(Opts.hIsoMaker, &hVfsFile);
2546 if (RT_SUCCESS(rc))
2547 {
2548 /*
2549 * We're done now if we're only setting up a virtual image.
2550 */
2551 if (Opts.fVirtualImageMaker)
2552 *phVfsFile = hVfsFile;
2553 else
2554 {
2555 rc = rtFsIsoMakerCmdWriteImage(&Opts, hVfsFile);
2556 RTVfsFileRelease(hVfsFile);
2557 }
2558 }
2559 else
2560 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreateVfsOutputFile failed: %Rrc", rc);
2561 }
2562 else
2563 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerFinalize failed: %Rrc", rc);
2564 }
2565
2566 return rtFsIsoMakerCmdDeleteState(&Opts, rc);
2567}
2568
2569
2570/**
2571 * ISO maker command (creates image file on disk).
2572 *
2573 * @returns IPRT status code
2574 * @param cArgs Number of arguments.
2575 * @param papszArgs Pointer to argument array.
2576 */
2577RTDECL(RTEXITCODE) RTFsIsoMakerCmd(unsigned cArgs, char **papszArgs)
2578{
2579 int rc = RTFsIsoMakerCmdEx(cArgs, papszArgs, NULL, NULL);
2580 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2581}
2582
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette