VirtualBox

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

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

iprt/isomaker: Added file & dir mode manipulation APIs and options.

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