VirtualBox

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

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

isomakercmd: Must ignore TRANS.TBL files too.

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