VirtualBox

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

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

IPRT: Added ISO maker tool frontend (will use for building later). Started documenting its usage. Implemented a couple of compatibility options.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 156.9 KB
Line 
1/* $Id: isomakercmd.cpp 68813 2017-09-21 19:53:07Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker Command.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsisomaker.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/buildconfig.h>
38#include <iprt/ctype.h>
39#include <iprt/file.h>
40#include <iprt/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("-volid-id", RTFSISOMAKERCMD_OPT_VOLUME_ID, RTGETOPT_REQ_STRING ),
501 DD("-volset", RTFSISOMAKERCMD_OPT_VOLUME_SET_ID, RTGETOPT_REQ_STRING ),
502
503 /* Other: */
504 DD("-file-mode", RTFSISOMAKERCMD_OPT_FILE_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
505 DD("-dir-mode", RTFSISOMAKERCMD_OPT_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
506 DD("-new-dir-mode", RTFSISOMAKERCMD_OPT_NEW_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
507 DD("-graft-points", RTFSISOMAKERCMD_OPT_GRAFT_POINTS, RTGETOPT_REQ_NOTHING ),
508 DD("--iso-level", RTFSISOMAKERCMD_OPT_ISO_LEVEL, RTGETOPT_REQ_UINT8 ),
509 { "--long-names", 'l', RTGETOPT_REQ_NOTHING },
510
511 /*
512 * genisoimage/mkisofs compatibility:
513 */
514 DD("-allow-limited-size", RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE, RTGETOPT_REQ_NOTHING ),
515 DD("-allow-leading-dots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
516 DD("-ldots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
517 DD("-allow-lowercase", RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE, RTGETOPT_REQ_NOTHING ),
518 DD("-allow-multidot", RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT, RTGETOPT_REQ_NOTHING ),
519 DD("-cache-inodes", RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
520 DD("-no-cache-inodes", RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
521 DD("-alpha-boot", RTFSISOMAKERCMD_OPT_ALPHA_BOOT, RTGETOPT_REQ_STRING ),
522 DD("-hppa-bootloader", RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER, RTGETOPT_REQ_STRING ),
523 DD("-hppa-cmdline", RTFSISOMAKERCMD_OPT_HPPA_CMDLINE, RTGETOPT_REQ_STRING ),
524 DD("-hppa-kernel-32", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32, RTGETOPT_REQ_STRING ),
525 DD("-hppa-kernel-64", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64, RTGETOPT_REQ_STRING ),
526 DD("-hppa-ramdisk", RTFSISOMAKERCMD_OPT_HPPA_RAMDISK, RTGETOPT_REQ_STRING ),
527 DD("-mips-boot", RTFSISOMAKERCMD_OPT_MIPS_BOOT, RTGETOPT_REQ_STRING ),
528 DD("-mipsel-boot", RTFSISOMAKERCMD_OPT_MIPSEL_BOOT, RTGETOPT_REQ_STRING ),
529 DD("-sparc-boot", 'B', RTGETOPT_REQ_STRING ),
530 { "--cd-extra", 'C', RTGETOPT_REQ_STRING },
531 DD("-check-oldnames", RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES, RTGETOPT_REQ_NOTHING ),
532 DD("-check-session", RTFSISOMAKERCMD_OPT_CHECK_SESSION, RTGETOPT_REQ_STRING ),
533 { "--dont-append-dot", 'd', RTGETOPT_REQ_NOTHING },
534 { "--deep-directories", 'D', RTGETOPT_REQ_NOTHING },
535 DD("-dvd-video", RTFSISOMAKERCMD_OPT_DVD_VIDEO, RTGETOPT_REQ_NOTHING ),
536 DD("-follow-symlinks", 'f', RTGETOPT_REQ_NOTHING ),
537 DD("-gid", RTFSISOMAKERCMD_OPT_GID, RTGETOPT_REQ_UINT32 ),
538 DD("-gui", RTFSISOMAKERCMD_OPT_GUI, RTGETOPT_REQ_NOTHING ),
539 DD("-hide", RTFSISOMAKERCMD_OPT_HIDE, RTGETOPT_REQ_STRING ),
540 DD("-hide-list", RTFSISOMAKERCMD_OPT_HIDE_LIST, RTGETOPT_REQ_STRING ),
541 DD("-hidden", RTFSISOMAKERCMD_OPT_HIDDEN, RTGETOPT_REQ_STRING ),
542 DD("-hidden-list", RTFSISOMAKERCMD_OPT_HIDDEN_LIST, RTGETOPT_REQ_STRING ),
543 DD("-hide-joliet", RTFSISOMAKERCMD_OPT_HIDE_JOLIET, RTGETOPT_REQ_STRING ),
544 DD("-hide-joliet-list", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST, RTGETOPT_REQ_STRING ),
545 DD("-hide-joliet-trans-tbl", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL, RTGETOPT_REQ_NOTHING ),
546 DD("-hide-rr-moved", RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED, RTGETOPT_REQ_NOTHING ),
547 DD("-input-charset", RTFSISOMAKERCMD_OPT_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
548 DD("-output-charset", RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
549 { "--joliet", 'J', RTGETOPT_REQ_NOTHING },
550 DD("-joliet-long", RTFSISOMAKERCMD_OPT_JOLIET_LONG, RTGETOPT_REQ_NOTHING ),
551 DD("-jcharset", RTFSISOMAKERCMD_OPT_JOLIET_CHARSET, RTGETOPT_REQ_STRING ),
552 { "--leading-dot", 'L', RTGETOPT_REQ_NOTHING },
553 DD("-jigdo-jigdo", RTFSISOMAKERCMD_OPT_JIGDO_JIGDO, RTGETOPT_REQ_STRING ),
554 DD("-jigdo-template", RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE, RTGETOPT_REQ_STRING ),
555 DD("-jigdo-min-file-size", RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE, RTGETOPT_REQ_UINT64 ),
556 DD("-jigdo-force-md5", RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5, RTGETOPT_REQ_STRING ),
557 DD("-jigdo-exclude", RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE, RTGETOPT_REQ_STRING ),
558 DD("-jigdo-map", RTFSISOMAKERCMD_OPT_JIGDO_MAP, RTGETOPT_REQ_STRING ),
559 DD("-md5-list", RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST, RTGETOPT_REQ_STRING ),
560 DD("-jigdo-template-compress", RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS, RTGETOPT_REQ_STRING ),
561 DD("-log-file", RTFSISOMAKERCMD_OPT_LOG_FILE, RTGETOPT_REQ_STRING ),
562 { "--exclude", 'm', RTGETOPT_REQ_STRING },
563 { "--exclude", 'x', RTGETOPT_REQ_STRING },
564 DD("-exclude-list", RTFSISOMAKERCMD_OPT_EXCLUDE_LIST, RTGETOPT_REQ_STRING ),
565 DD("-max-iso9660-filenames", RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES, RTGETOPT_REQ_NOTHING ),
566 { "--merge", 'M', RTGETOPT_REQ_STRING },
567 DD("-dev", 'M', RTGETOPT_REQ_STRING ),
568 { "--omit-version-numbers", 'N', RTGETOPT_REQ_NOTHING },
569 DD("-nobak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
570 DD("-no-bak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
571 DD("-force-rr", RTFSISOMAKERCMD_OPT_FORCE_RR, RTGETOPT_REQ_NOTHING ),
572 DD("-no-rr", RTFSISOMAKERCMD_OPT_NO_RR, RTGETOPT_REQ_NOTHING ),
573 DD("-no-split-symlink-components", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS, RTGETOPT_REQ_NOTHING ),
574 DD("-no-split-symlink-fields", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS, RTGETOPT_REQ_NOTHING ),
575 { "--output", 'o', RTGETOPT_REQ_STRING },
576 DD("-pad", RTFSISOMAKERCMD_OPT_PAD, RTGETOPT_REQ_NOTHING ),
577 DD("-no-pad", RTFSISOMAKERCMD_OPT_NO_PAD, RTGETOPT_REQ_NOTHING ),
578 DD("-path-list", RTFSISOMAKERCMD_OPT_PATH_LIST, RTGETOPT_REQ_STRING ),
579 DD("-print-size", RTFSISOMAKERCMD_OPT_PRINT_SIZE, RTGETOPT_REQ_NOTHING ),
580 DD("-quiet", RTFSISOMAKERCMD_OPT_QUIET, RTGETOPT_REQ_NOTHING ),
581 { "--rock-ridge", 'R', RTGETOPT_REQ_NOTHING },
582 { "--rock-ridge-relaxed", 'r', RTGETOPT_REQ_NOTHING },
583 DD("-relaxed-filenames", RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES, RTGETOPT_REQ_NOTHING ),
584 DD("-root", RTFSISOMAKERCMD_OPT_ROOT, RTGETOPT_REQ_STRING ),
585 DD("-old-root", RTFSISOMAKERCMD_OPT_OLD_ROOT, RTGETOPT_REQ_STRING ),
586 DD("-sort", RTFSISOMAKERCMD_OPT_SORT, RTGETOPT_REQ_STRING ),
587 DD("-sparc-boot", RTFSISOMAKERCMD_OPT_SPARC_BOOT, RTGETOPT_REQ_STRING ),
588 DD("-sparc-label", RTFSISOMAKERCMD_OPT_SPARC_LABEL, RTGETOPT_REQ_STRING ),
589 DD("-split-output", RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT, RTGETOPT_REQ_NOTHING ),
590 DD("-stream-media-size", RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE, RTGETOPT_REQ_UINT64 ),
591 DD("-stream-file-name", RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME, RTGETOPT_REQ_STRING ),
592 DD("-sunx86-boot", RTFSISOMAKERCMD_OPT_SUNX86_BOOT, RTGETOPT_REQ_STRING ),
593 DD("-sunx86-label", RTFSISOMAKERCMD_OPT_SUNX86_LABEL, RTGETOPT_REQ_STRING ),
594 { "--trans-tbl", 'T', RTGETOPT_REQ_NOTHING },
595 DD("-table-name", RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME, RTGETOPT_REQ_STRING ),
596 DD("-ucs-level", RTFSISOMAKERCMD_OPT_JOLIET_LEVEL, RTGETOPT_REQ_UINT8 ),
597 DD("-udf", RTFSISOMAKERCMD_OPT_UDF, RTGETOPT_REQ_NOTHING ),
598 DD("-uid", RTFSISOMAKERCMD_OPT_UID, RTGETOPT_REQ_UINT32 ),
599 DD("-use-fileversion", RTFSISOMAKERCMD_OPT_USE_FILE_VERSION, RTGETOPT_REQ_NOTHING ),
600 { "--untranslated-filenames", 'U', RTGETOPT_REQ_NOTHING },
601 DD("-no-iso-translate", RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE, RTGETOPT_REQ_NOTHING ),
602 DD("-volset-size", RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE, RTGETOPT_REQ_UINT32 ),
603 DD("-volset-seqno", RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO, RTGETOPT_REQ_UINT32 ),
604 { "--transpared-compression", 'z', RTGETOPT_REQ_NOTHING },
605
606 /* HFS and ISO-9660 apple extensions. */
607 DD("-hfs", RTFSISOMAKERCMD_OPT_HFS_ENABLE, RTGETOPT_REQ_NOTHING ),
608 DD("-apple", RTFSISOMAKERCMD_OPT_APPLE, RTGETOPT_REQ_NOTHING ),
609 DD("-map", RTFSISOMAKERCMD_OPT_HFS_MAP, RTGETOPT_REQ_STRING ),
610 DD("-magic", RTFSISOMAKERCMD_OPT_HFS_MAGIC, RTGETOPT_REQ_STRING ),
611 DD("-hfs-creator", RTFSISOMAKERCMD_OPT_HFS_CREATOR, RTGETOPT_REQ_STRING ),
612 DD("-hfs-type", RTFSISOMAKERCMD_OPT_HFS_TYPE, RTGETOPT_REQ_STRING ),
613 DD("-probe", RTFSISOMAKERCMD_OPT_HFS_PROBE, RTGETOPT_REQ_NOTHING ),
614 DD("-no-desktop", RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP, RTGETOPT_REQ_NOTHING ),
615 DD("-mac-name", RTFSISOMAKERCMD_OPT_HFS_MAC_NAME, RTGETOPT_REQ_NOTHING ),
616 DD("-boot-hfs-file", RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE, RTGETOPT_REQ_STRING ),
617 DD("-part", RTFSISOMAKERCMD_OPT_HFS_PART, RTGETOPT_REQ_NOTHING ),
618 DD("-auto", RTFSISOMAKERCMD_OPT_HFS_AUTO, RTGETOPT_REQ_STRING ),
619 DD("-cluster-size", RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE, RTGETOPT_REQ_UINT32 ),
620 DD("-hide-hfs", RTFSISOMAKERCMD_OPT_HFS_HIDE, RTGETOPT_REQ_STRING ),
621 DD("-hide-hfs-list", RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST, RTGETOPT_REQ_STRING ),
622 DD("-hfs-volid", RTFSISOMAKERCMD_OPT_HFS_VOL_ID, RTGETOPT_REQ_STRING ),
623 DD("-icon-position", RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION, RTGETOPT_REQ_NOTHING ),
624 DD("-root-info", RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO, RTGETOPT_REQ_STRING ),
625 DD("-prep-boot", RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT, RTGETOPT_REQ_STRING ),
626 DD("-chrp-boot", RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT, RTGETOPT_REQ_NOTHING ),
627 DD("-input-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
628 DD("-output-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
629 DD("-hfs-unlock", RTFSISOMAKERCMD_OPT_HFS_UNLOCK, RTGETOPT_REQ_NOTHING ),
630 DD("-hfs-bless", RTFSISOMAKERCMD_OPT_HFS_BLESS, RTGETOPT_REQ_STRING ),
631 DD("-hfs-parms", RTFSISOMAKERCMD_OPT_HFS_PARMS, RTGETOPT_REQ_STRING ),
632 { "--cap", RTFSISOMAKERCMD_OPT_HFS_CAP, RTGETOPT_REQ_NOTHING },
633 { "--netatalk", RTFSISOMAKERCMD_OPT_HFS_NETATALK, RTGETOPT_REQ_NOTHING },
634 { "--double", RTFSISOMAKERCMD_OPT_HFS_DOUBLE, RTGETOPT_REQ_NOTHING },
635 { "--ethershare", RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE, RTGETOPT_REQ_NOTHING },
636 { "--ushare", RTFSISOMAKERCMD_OPT_HFS_USHARE, RTGETOPT_REQ_NOTHING },
637 { "--exchange", RTFSISOMAKERCMD_OPT_HFS_EXCHANGE, RTGETOPT_REQ_NOTHING },
638 { "--sgi", RTFSISOMAKERCMD_OPT_HFS_SGI, RTGETOPT_REQ_NOTHING },
639 { "--xinet", RTFSISOMAKERCMD_OPT_HFS_XINET, RTGETOPT_REQ_NOTHING },
640 { "--macbin", RTFSISOMAKERCMD_OPT_HFS_MACBIN, RTGETOPT_REQ_NOTHING },
641 { "--single", RTFSISOMAKERCMD_OPT_HFS_SINGLE, RTGETOPT_REQ_NOTHING },
642 { "--dave", RTFSISOMAKERCMD_OPT_HFS_DAVE, RTGETOPT_REQ_NOTHING },
643 { "--sfm", RTFSISOMAKERCMD_OPT_HFS_SFM, RTGETOPT_REQ_NOTHING },
644 { "--osx-double", RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE, RTGETOPT_REQ_NOTHING },
645 { "--osx-hfs", RTFSISOMAKERCMD_OPT_HFS_OSX_HFS, RTGETOPT_REQ_NOTHING },
646#undef DD
647};
648
649
650/*********************************************************************************************************************************
651* Internal Functions *
652*********************************************************************************************************************************/
653static int rtFsIsoMakerCmdParse(PRTFSISOMAKERCMDOPTS pOpts, unsigned cArgs, char **papszArgs, unsigned cDepth);
654
655
656/**
657 * Wrapper around RTErrInfoSetV / RTMsgErrorV.
658 *
659 * @returns @a rc
660 * @param pOpts The ISO maker command instance.
661 * @param rc The return code.
662 * @param pszFormat The message format.
663 * @param ... The message format arguments.
664 */
665static int rtFsIsoMakerCmdErrorRc(PRTFSISOMAKERCMDOPTS pOpts, int rc, const char *pszFormat, ...)
666{
667 va_list va;
668 va_start(va, pszFormat);
669 if (pOpts->pErrInfo)
670 RTErrInfoSetV(pOpts->pErrInfo, rc, pszFormat, va);
671 else
672 RTMsgErrorV(pszFormat, va);
673 va_end(va);
674 return rc;
675}
676
677
678/**
679 * Wrapper around RTErrInfoSetV / RTMsgErrorV for doing the job of
680 * RTVfsChainMsgError.
681 *
682 * @returns @a rc
683 * @param pOpts The ISO maker command instance.
684 * @param pszFunction The API called.
685 * @param pszSpec The VFS chain specification or file path passed to the.
686 * @param rc The return code.
687 * @param offError The error offset value returned (0 if not captured).
688 * @param pErrInfo Additional error information. Optional.
689 */
690static int rtFsIsoMakerCmdChainError(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFunction, const char *pszSpec, int rc,
691 uint32_t offError, PRTERRINFO pErrInfo)
692{
693 if (RTErrInfoIsSet(pErrInfo))
694 {
695 if (offError > 0)
696 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
697 "%s failed with rc=%Rrc: %s\n"
698 " '%s'\n"
699 " %*s^",
700 pszFunction, rc, pErrInfo->pszMsg, pszSpec, offError, "");
701 else
702 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s failed to open '%s': %Rrc: %s",
703 pszFunction, pszSpec, rc, pErrInfo->pszMsg);
704 }
705 else
706 {
707 if (offError > 0)
708 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
709 "%s failed with rc=%Rrc:\n"
710 " '%s'\n"
711 " %*s^",
712 pszFunction, rc, pszSpec, offError, "");
713 else
714 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s failed to open '%s': %Rrc", pszFunction, pszSpec, rc);
715 }
716 return rc;
717}
718
719
720/**
721 * Wrapper around RTErrInfoSetV / RTMsgErrorV for displaying syntax errors.
722 *
723 * @returns VERR_INVALID_PARAMETER
724 * @param pOpts The ISO maker command instance.
725 * @param pszFormat The message format.
726 * @param ... The message format arguments.
727 */
728static int rtFsIsoMakerCmdSyntaxError(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
729{
730 va_list va;
731 va_start(va, pszFormat);
732 if (pOpts->pErrInfo)
733 RTErrInfoSetV(pOpts->pErrInfo, VERR_INVALID_PARAMETER, pszFormat, va);
734 else
735 RTMsgErrorV(pszFormat, va);
736 va_end(va);
737 return VERR_INVALID_PARAMETER;
738}
739
740
741/**
742 * Wrapper around RTPrintfV / RTLogRelPrintfV.
743 *
744 * @param pOpts The ISO maker command instance.
745 * @param pszFormat The message format.
746 * @param ... The message format arguments.
747 */
748static void rtFsIsoMakerPrintf(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
749{
750 va_list va;
751 va_start(va, pszFormat);
752 if (pOpts->pErrInfo)
753 RTLogRelPrintfV(pszFormat, va);
754 else
755 RTPrintfV(pszFormat, va);
756 va_end(va);
757}
758
759/**
760 * Deletes the state and returns @a rc.
761 *
762 * @returns @a rc.
763 * @param pOpts The ISO maker command instance to delete.
764 * @param rc The status code to return.
765 */
766static int rtFsIsoMakerCmdDeleteState(PRTFSISOMAKERCMDOPTS pOpts, int rc)
767{
768 if (pOpts->hIsoMaker != NIL_RTFSISOMAKER)
769 {
770 RTFsIsoMakerRelease(pOpts->hIsoMaker);
771 pOpts->hIsoMaker = NIL_RTFSISOMAKER;
772 }
773
774 if (pOpts->hSrcVfs != NIL_RTVFS)
775 {
776 RTVfsRelease(pOpts->hSrcVfs);
777 pOpts->hSrcVfs = NIL_RTVFS;
778 }
779
780 return rc;
781}
782
783
784/**
785 * Print the usage.
786 *
787 * @param pOpts Options for print metho.
788 * @param pszProgName The program name.
789 */
790static void rtFsIsoMakerCmdUsage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszProgName)
791{
792 rtFsIsoMakerPrintf(pOpts,
793 "Usage: %s [options] [@commands.rsp] <filespec1> [filespec2 [..]]\n"
794 "\n"
795 "File specifications and --name-setup:\n"
796 "\n"
797 " All non-options that does not start with '@' are taken to indicate a file,\n"
798 " directory, or similar that is should be added to the ISO image. Directories\n"
799 " are added recursively and content is subject to filtering options.\n"
800 "\n"
801 " Since there can be up to six different namespaces on an ISO, it is handy\n"
802 " to be able to control the names used in each and be able to exclude an\n"
803 " object from one or more namespaces. The --name-setup option specifies the\n"
804 " file specification format to use forthwith.\n"
805 "\n"
806 " The default setup is:\n"
807 "\n"
808 " --name-setup iso+joliet+udf+hfs\n"
809 "\n"
810 " Which means you specify one on-ISO name for all namespaces followed by '='\n"
811 " and the source file system name. Only specifying the source file system\n"
812 " will add the file/dir/whatever to the root of the ISO image.\n"
813 "\n"
814 " Lets look at the following two examples:\n"
815 "\n"
816 " /docs/readme.txt=/home/user/Documents/product-x-readme.txt\n"
817 " /home/user/Documents/product-x-readme.txt\n"
818 "\n"
819 " In the first case the file '/home/user/Documents/product-x-readme.txt' is\n"
820 " added to the ISO image as '/docs/readme.txt' in all enabled namespaces.\n"
821 " In the primary ISO 9660 namespace, the filename will by default be converted\n"
822 " to upper case because it's required by the spec.\n"
823 "\n"
824 " In the second case the file is added to the root under the name\n"
825 " 'product-x-readme.txt' in all namespaces. Though, in the primary ISO 9660\n"
826 " namespace the name will be transformed to apply with the current ISO level,\n"
827 " probably uppercased, possibly truncated too.\n"
828 "\n"
829 " Given --name-setup iso,joliet,udf you can specify the name individually\n"
830 " for each of the three namespace, if you like. If you omit any, they will\n"
831 " use last name given. Any names left blank (==) will be considered omitted.\n"
832 "\n"
833 " A different name in each namespace:\n"
834 " /ISO.TXT=/Joliet.TxT=/UDF.txt=/tmp/iso/real.txt\n"
835 " Specific name in the ISO 9660 namespace, same in the rest:\n"
836 " /ISO.TXT=/OtherNamespaces.TxT=/tmp/iso/real.txt\n"
837 " Omit the file from the ISO 9660 namespace:\n"
838 " =/OtherNamespaces.TxT=/tmp/iso/real.txt\n"
839 " Omit the file from the joliet namespace:\n"
840 " /ISO.TXT==/UDF.TxT=/tmp/iso/real.txt\n"
841 " Use the same filename as the source everywhere:\n"
842 " /tmp/iso/real.txt\n"
843 "\n"
844 " Using for instance --name-setup udf you can add a files/dirs/whatever to\n"
845 " select namespace(s) without the more complicated empty name syntax above.\n"
846 "\n"
847 " When adding directories, you can only control the naming and omitting of the\n"
848 " directory itself, not any recursively added files and directories below it.\n"
849 "\n"
850 "\n"
851 "Options:\n"
852 "\n"
853 " --name-setup <spec>\n"
854 " Configures active namespaces and how file specifications are to be\n"
855 " interpreted. The specification is a comma separated list. Each element\n"
856 " in the list is a sub-list separated by space, '+' or '|' giving the\n"
857 " namespaces that elements controls. Namespaces are divied into two major\n"
858 " and minor ones, you cannot specifying a minor before the major it\n"
859 " belongs to.\n"
860 " Major namespaces and aliases in parentheses:\n"
861 " - iso (primary, iso9660, iso-9660, primary-iso, iso-primary)\n"
862 " - joliet\n"
863 " - udf\n"
864 " - hfs (hfs-plus)\n"
865 " Minor namespaces:\n"
866 " - rock: rock ridge on previous major namespace (iso / joliet)\n"
867 " - iso-rock: rock ridge extensions on primary ISO 9660 namespace\n"
868 " - joliet-rock: rock ridge on joliet namespace (just for fun)\n"
869 " - trans-tbl: translation table file on previous major namespace\n"
870 " - iso-trans-tbl\n"
871 " - joliet-trans-tbl\n"
872 " - udf-trans-tbl\n"
873 " - hfs-trans-tbl\n"
874 "\n"
875 " --iso-level <0|1|2|3>\n"
876 " Sets the ISO level:\n"
877 " - 0: Disable primary ISO namespace.\n"
878 " - 1: ISO level 1: Filenames 8.3 format and limited to 4GB - 1.\n"
879 " - 2: ISO level 2: 31 char long names and limited to 4GB - 1.\n"
880 " - 3: ISO level 3: 31 char long names and support for >=4GB files.\n"
881 " - 4: Fictive level used by other tools. Not yet implemented.\n"
882 " Default: 3\n"
883 "\n"
884 " --no-joliet\n"
885 " Disable the joliet namespace entirely. This option must be specified\n"
886 " before any file specifications\n"
887 "\n"
888 " --push-iso <iso-file>\n"
889 " --push-iso-no-joliet <iso-file>\n"
890 " --push-iso-no-rock <iso-file>\n"
891 " --push-iso-no-rock-no-joliet <iso-file>\n"
892 " Open the specified ISO file and use it as source file system until the\n"
893 " corresponding --pop options is encountered. The variations are for\n"
894 " selecting which namespace on the ISO to (not) access. These options\n"
895 " are handy for copying files/directories/stuff from an ISO without\n"
896 " having to extract them first or using the :iprtvfs: syntax.\n"
897 "\n"
898 " --pop\n"
899 " Pops a --push-iso of the source file system stack.\n"
900 "\n"
901 " --import-iso <iso-file>\n"
902 " Imports everything on the given ISO file. You can use --name-setup to\n"
903 " omit namespaces.\n"
904 "\n"
905 " --file-mode <mode>\n"
906 " --no-file-mode\n"
907 " Controls the forced file mode mask.\n"
908 "\n"
909 " --dir-mode <mode>\n"
910 " --no-dir-mode\n"
911 " Controls the forced directory mode mask.\n"
912 "\n"
913 " --new-dir-mode <mode>\n"
914 " Controls the default mode mask for directories that are created "
915 " implicitly. The --dir-mode option overrides this.\n"
916 "\n"
917 "\n"
918 "Options - Booting:\n"
919 "\n"
920 " --eltorito-new-entry\n"
921 " --eltorito-alt-boot\n"
922 " Starts a new El Torito boot entry.\n"
923 "\n"
924 " --eltorito-add-image <filespec>\n"
925 " File specification of a file that should be added to the image and used\n"
926 " as the El Torito boot image of the current boot entry.\n"
927 "\n"
928 " -b <on-ISO-file>\n"
929 " --eltorito-boot <on-ISO-file>\n"
930 " Specifies a file on the ISO as the El Torito boot image for the current\n"
931 " boot entry.\n"
932 "\n"
933 " --eltorito-floppy-12\n"
934 " --eltorito-floppy-144\n"
935 " --eltorito-floppy-288\n"
936 " --no-emulation-boot\n"
937 " --hard-disk-boot\n"
938 " Sets the boot image emulation type of the current El Torito boot entry.\n"
939 "\n"
940 " --boot-load-seg <seg>\n"
941 " Specify the image load segment for the current El Torito boot entry.\n"
942 " Default: 0x7c0\n"
943 "\n"
944 " --boot-load-size <seg>\n"
945 " Specify the image load size in emulated sectors for the current El Torito\n"
946 " boot entry. Default: 4 (sectors of 512 bytes)\n"
947 "\n"
948 " --no-boot\n"
949 " Indicates that the current El Torito boot entry isn't bootable. (The\n"
950 " BIOS will allegedly configure the emulation, but not attempt booting.)\n"
951 "\n"
952 " --boot-info-table\n"
953 " Write a isolinux/syslinux boot info table into the boot image for the\n"
954 " current El Torito boot entry.\n"
955 "\n"
956 " --eltorito-platform-id <id>\n"
957 " Set the El Torito platform ID of the current entry, a new entry of the\n"
958 " verification entry depending on when it's used. The ID must be one\n"
959 " of: x86, PPC, Mac, efi\n"
960 "\n"
961 " -c <namespec>\n"
962 " --boot-catalog <namespec>\n"
963 " Enters the El Torito boot catalog into the namespaces as a file. The\n"
964 " 'namespec' uses the same format as a 'filespec', but omits the final\n"
965 " source file system name component.\n"
966 "\n"
967 " -G <file>\n"
968 " --generic-boot <file>\n"
969 " Specifies a file that should be loaded at offset 0 in the ISO image.\n"
970 " The file must not be larger than 32KB. When creating a hybrid image,\n"
971 " parts of this may be regenerated by partition tables and such.\n"
972 "\n"
973 "\n"
974 "Options - String properties:\n"
975 "\n"
976 " --abstract <file-id>\n"
977 " The name of the abstract file in the root dir.\n"
978 "\n"
979 " -A <text|_file-id>\n"
980 " --application-id <text|_file-id>\n"
981 " Application ID string or root file name. The latter must be prefixed\n"
982 " with an underscore.\n"
983 "\n"
984 " --biblio <file-id>\n"
985 " The name of the bibliographic file in the root dir.\n"
986 "\n"
987 " --copyright <file-id>\n"
988 " The name of the copyright file in the root dir.\n"
989 "\n"
990 " -P <text|_file-id>\n"
991 " --publisher <text|_file-id>\n"
992 " Publisher ID string or root file name. The latter must be prefixed\n"
993 " with an underscore.\n"
994 "\n"
995 " -p <text|_file-id>\n"
996 " --preparer <text|_file-id>\n"
997 " Data preparer ID string or root file name. The latter must be prefixed\n"
998 " with an underscore.\n"
999 "\n"
1000 " --sysid <text>\n"
1001 " System ID string.\n"
1002 "\n"
1003 " --volid <text>\n"
1004 " --volume-id <text>\n"
1005 " Volume ID string.\n"
1006 "\n"
1007 " --volset <text>\n"
1008 " Volume set ID string.\n"
1009 "\n"
1010 "\n"
1011 "Options - Compatibility:\n"
1012 "\n"
1013 " --graft-points\n"
1014 " Alias for --name-setup iso+joliet+udf+hfs.\n"
1015 "\n"
1016 " -l\n"
1017 " --long-names\n"
1018 " Allow 31 charater filenames. Just ensure ISO level >= 2 here.\n"
1019 "\n"
1020 "Options - VISO specific:\n"
1021 "\n"
1022 " --iprt-iso-maker-file-marker <UUID>\n"
1023 " --iprt-iso-maker-file-marker-bourne <UUID>\n"
1024 " --iprt-iso-maker-file-marker-bourne-sh <UUID>\n"
1025 " Used as first option in a VISO file to specify the file UUID and that\n"
1026 " it is formatted using bourne-shell argument quoting & escaping style.\n"
1027 "\n"
1028 " --iprt-iso-maker-file-marker-ms <UUID>\n"
1029 " --iprt-iso-maker-file-marker-ms-sh <UUID>\n"
1030 " Used as first option in a VISO file to specify the file UUID and that\n"
1031 " it is formatted using microsoft CRT argument quoting & escaping style.\n"
1032 "\n"
1033 "\n"
1034 "Options - Testing:\n"
1035 "\n"
1036 " --output-buffer-size <bytes>\n"
1037 " Selects a specific output buffer size for testing virtual image reads.\n"
1038 "\n"
1039 " --random-output-buffer-size\n"
1040 " Enables randomized buffer size for each virtual image read, using the\n"
1041 " current output buffer size (--output-buffer-size) as maximum.\n"
1042 "\n"
1043 " --random-order-verification <size>\n"
1044 " Enables verification pass of the image that compares blocks of the given\n"
1045 " size in random order from the virtual and output images\n"
1046 "\n"
1047 , RTPathFilename(pszProgName));
1048
1049
1050}
1051
1052
1053/**
1054 * Verifies the image content by reading blocks in random order.
1055 *
1056 * This is for exercise the virtual ISO code better and test that we get the
1057 * same data when reading something twice.
1058 *
1059 * @returns IPRT status code.
1060 * @param pOpts The ISO maker command instance.
1061 * @param hVfsSrcFile The source file (virtual ISO).
1062 * @param hVfsDstFile The destination file (image file on disk).
1063 * @param cbImage The size of the ISO.
1064 */
1065static int rtFsIsoMakerCmdVerifyImageInRandomOrder(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile,
1066 RTVFSFILE hVfsDstFile, uint64_t cbImage)
1067{
1068 /*
1069 * Figure the buffer (block) size and allocate a bitmap for noting down blocks we've covered.
1070 */
1071 int rc;
1072 size_t cbBuf = RT_MAX(pOpts->cbRandomOrderVerifciationBlock, 1);
1073 uint64_t cBlocks64 = (cbImage + cbBuf - 1) / cbBuf;
1074 if (cBlocks64 > _512M)
1075 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_OUT_OF_RANGE,
1076 "verification block count too high: cBlocks=%#RX64 (cbBuf=%#zx), max 512M", cBlocks64, cbBuf);
1077 uint32_t cBlocks = (uint32_t)cBlocks64;
1078 uint32_t cbBitmap = (cBlocks + 63) / 8;
1079 if (cbBitmap > _64M)
1080 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_OUT_OF_RANGE,
1081 "verification bitmap too big: cbBitmap=%#RX32 (cbBuf=%#zx), max 64MB", cbBitmap, cbBuf);
1082 void *pvSrcBuf = RTMemTmpAlloc(cbBuf);
1083 void *pvDstBuf = RTMemTmpAlloc(cbBuf);
1084 void *pvBitmap = RTMemTmpAllocZ(cbBitmap);
1085 if (pvSrcBuf && pvDstBuf && pvBitmap)
1086 {
1087 /* Must set the unused bits in the top qword. */
1088 for (uint32_t i = RT_ALIGN_32(cBlocks, 64) - 1; i >= cBlocks; i--)
1089 ASMBitSet(pvBitmap, i);
1090
1091 /*
1092 * Do the verification.
1093 */
1094 rtFsIsoMakerPrintf(pOpts, "Verifying image in random order using %zu (%#zx) byte blocks: %#zx in blocks\n",
1095 cbBuf, cbBuf, cBlocks);
1096
1097 rc = VINF_SUCCESS;
1098 uint64_t cLeft = cBlocks;
1099 while (cLeft-- > 0)
1100 {
1101 /*
1102 * Figure out which block to check next.
1103 */
1104 uint32_t iBlock = RTRandU32Ex(0, cBlocks - 1);
1105 if (!ASMBitTestAndSet(pvBitmap, iBlock))
1106 Assert(iBlock < cBlocks);
1107 else
1108 {
1109 /* try 32 other random numbers. */
1110 bool fBitSet;
1111 unsigned cTries = 0;
1112 do
1113 {
1114 iBlock = RTRandU32Ex(0, cBlocks - 1);
1115 fBitSet = ASMBitTestAndSet(pvBitmap, iBlock);
1116 } while (fBitSet && ++cTries < 32);
1117 if (fBitSet)
1118 {
1119 /* Look for the next clear bit after it (with wrap around). */
1120 int iHit = ASMBitNextClear(pvBitmap, cBlocks, iBlock);
1121 Assert(iHit < (int32_t)cBlocks);
1122 if (iHit < 0)
1123 {
1124 iHit = ASMBitNextClear(pvBitmap, iBlock, 0);
1125 Assert(iHit < (int32_t)cBlocks);
1126 }
1127 if (iHit >= 0)
1128 {
1129 fBitSet = ASMBitTestAndSet(pvBitmap, iHit);
1130 if (!fBitSet)
1131 iBlock = iHit;
1132 else
1133 {
1134 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_INTERNAL_ERROR_3,
1135 "Bitmap weirdness: iHit=%#x iBlock=%#x cBlocks=%#x",
1136 iHit, iBlock, cBlocks);
1137 break;
1138 }
1139 }
1140 else
1141 {
1142 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_INTERNAL_ERROR_2, "Bitmap weirdness: iBlock=%#x cBlocks=%#x",
1143 iBlock, cBlocks);
1144 break;
1145 }
1146 }
1147 }
1148 Assert(ASMBitTest(pvBitmap, iBlock));
1149
1150 /*
1151 * Figure out how much and where to read (last block fun).
1152 */
1153 uint64_t offBlock = iBlock * (uint64_t)cbBuf;
1154 size_t cbToRead = cbBuf;
1155 if (iBlock + 1 < cBlocks)
1156 { /* likely */ }
1157 else if (cbToRead > cbImage - offBlock)
1158 cbToRead = (size_t)(cbImage - offBlock);
1159 Assert(offBlock + cbToRead <= cbImage);
1160
1161 /*
1162 * Read the blocks.
1163 */
1164 //RTPrintf("Reading block #%#x at %#RX64\n", iBlock, offBlock);
1165 rc = RTVfsFileReadAt(hVfsDstFile, offBlock, pvDstBuf, cbToRead, NULL);
1166 if (RT_SUCCESS(rc))
1167 {
1168 memset(pvSrcBuf, 0xdd, cbBuf);
1169 rc = RTVfsFileReadAt(hVfsSrcFile, offBlock, pvSrcBuf, cbToRead, NULL);
1170 if (RT_SUCCESS(rc))
1171 {
1172 if (memcmp(pvDstBuf, pvSrcBuf, cbToRead) == 0)
1173 continue;
1174 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_MISMATCH,
1175 "Block #%#x differs! offBlock=%#RX64 cbToRead=%#zu\n"
1176 "Virtual ISO (source):\n%.*Rhxd\nWritten ISO (destination):\n%.*Rhxd",
1177 iBlock, offBlock, cbToRead, cbToRead, pvSrcBuf, cbToRead, pvDstBuf);
1178 }
1179 else
1180 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
1181 "Error reading %#zx bytes source (virtual ISO) block #%#x at %#RX64: %Rrc",
1182 cbToRead, iBlock, offBlock, rc);
1183 }
1184 else
1185 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
1186 "Error reading %#zx bytes destination (written ISO) block #%#x at %#RX64: %Rrc",
1187 cbToRead, iBlock, offBlock, rc);
1188 break;
1189 }
1190
1191 if (RT_SUCCESS(rc))
1192 rtFsIsoMakerPrintf(pOpts, "Written image verified fine!\n");
1193 }
1194 else if (!pvSrcBuf || !pvDstBuf)
1195 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbBuf);
1196 else
1197 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbBuf);
1198 RTMemTmpFree(pvBitmap);
1199 RTMemTmpFree(pvDstBuf);
1200 RTMemTmpFree(pvSrcBuf);
1201 return rc;
1202}
1203
1204
1205/**
1206 * Writes the image to file, no checking, no special buffering.
1207 *
1208 * @returns IPRT status code.
1209 * @param pOpts The ISO maker command instance.
1210 * @param hVfsSrcFile The source file from the ISO maker.
1211 * @param hVfsDstFile The destination file (image file on disk).
1212 * @param cbImage The size of the ISO.
1213 * @param ppvBuf Pointer to the buffer pointer. The buffer will
1214 * be reallocated, but we want the luxary of the
1215 * caller freeing it.
1216 */
1217static int rtFsIsoMakerCmdWriteImageRandomBufferSize(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile, RTVFSFILE hVfsDstFile,
1218 uint64_t cbImage, void **ppvBuf)
1219{
1220 /*
1221 * Copy the virtual image bits to the destination file.
1222 */
1223 void *pvBuf = *ppvBuf;
1224 uint32_t cbMaxBuf = pOpts->cbOutputReadBuffer > 0 ? pOpts->cbOutputReadBuffer : _64K;
1225 uint64_t offImage = 0;
1226 while (offImage < cbImage)
1227 {
1228 /* Figure out how much to copy this time. */
1229 size_t cbToCopy = RTRandU32Ex(1, cbMaxBuf - 1);
1230 if (offImage + cbToCopy < cbImage)
1231 { /* likely */ }
1232 else
1233 cbToCopy = (size_t)(cbImage - offImage);
1234 RTMemFree(pvBuf);
1235 *ppvBuf = pvBuf = RTMemTmpAlloc(cbToCopy);
1236 if (pvBuf)
1237 {
1238 /* Do the copying. */
1239 int rc = RTVfsFileReadAt(hVfsSrcFile, offImage, pvBuf, cbToCopy, NULL);
1240 if (RT_SUCCESS(rc))
1241 {
1242 rc = RTVfsFileWriteAt(hVfsDstFile, offImage, pvBuf, cbToCopy, NULL);
1243 if (RT_SUCCESS(rc))
1244 offImage += cbToCopy;
1245 else
1246 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc writing %#zx bytes at offset %#RX64 to '%s'",
1247 rc, cbToCopy, offImage, pOpts->pszOutFile);
1248 }
1249 else
1250 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc read %#zx bytes at offset %#RX64", rc, cbToCopy, offImage);
1251 }
1252 else
1253 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbToCopy);
1254 }
1255 return VINF_SUCCESS;
1256}
1257
1258
1259/**
1260 * Writes the image to file, no checking, no special buffering.
1261 *
1262 * @returns IPRT status code.
1263 * @param pOpts The ISO maker command instance.
1264 * @param hVfsSrcFile The source file from the ISO maker.
1265 * @param hVfsDstFile The destination file (image file on disk).
1266 * @param cbImage The size of the ISO.
1267 * @param pvBuf Pointer to read buffer.
1268 * @param cbBuf The buffer size.
1269 */
1270static int rtFsIsoMakerCmdWriteImageSimple(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile, RTVFSFILE hVfsDstFile,
1271 uint64_t cbImage, void *pvBuf, size_t cbBuf)
1272{
1273 /*
1274 * Copy the virtual image bits to the destination file.
1275 */
1276 uint64_t offImage = 0;
1277 while (offImage < cbImage)
1278 {
1279 /* Figure out how much to copy this time. */
1280 size_t cbToCopy = cbBuf;
1281 if (offImage + cbToCopy < cbImage)
1282 { /* likely */ }
1283 else
1284 cbToCopy = (size_t)(cbImage - offImage);
1285
1286 /* Do the copying. */
1287 int rc = RTVfsFileReadAt(hVfsSrcFile, offImage, pvBuf, cbToCopy, NULL);
1288 if (RT_SUCCESS(rc))
1289 {
1290 rc = RTVfsFileWriteAt(hVfsDstFile, offImage, pvBuf, cbToCopy, NULL);
1291 if (RT_SUCCESS(rc))
1292 offImage += cbToCopy;
1293 else
1294 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc writing %#zx bytes at offset %#RX64 to '%s'",
1295 rc, cbToCopy, offImage, pOpts->pszOutFile);
1296 }
1297 else
1298 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc read %#zx bytes at offset %#RX64", rc, cbToCopy, offImage);
1299 }
1300 return VINF_SUCCESS;
1301}
1302
1303
1304/**
1305 * Writes the image to file.
1306 *
1307 * @returns IPRT status code.
1308 * @param pOpts The ISO maker command instance.
1309 * @param hVfsSrcFile The source file from the ISO maker.
1310 */
1311static int rtFsIsoMakerCmdWriteImage(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile)
1312{
1313 /*
1314 * Get the image size and setup the copy buffer.
1315 */
1316 uint64_t cbImage;
1317 int rc = RTVfsFileGetSize(hVfsSrcFile, &cbImage);
1318 if (RT_SUCCESS(rc))
1319 {
1320 rtFsIsoMakerPrintf(pOpts, "Image size: %'RU64 (%#RX64) bytes\n", cbImage, cbImage);
1321
1322 uint32_t cbBuf = pOpts->cbOutputReadBuffer == 0 ? _1M : pOpts->cbOutputReadBuffer;
1323 void *pvBuf = RTMemTmpAlloc(cbBuf);
1324 if (pvBuf)
1325 {
1326 /*
1327 * Open the output file.
1328 */
1329 RTVFSFILE hVfsDstFile;
1330 uint32_t offError;
1331 RTERRINFOSTATIC ErrInfo;
1332 rc = RTVfsChainOpenFile(pOpts->pszOutFile, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE,
1333 &hVfsDstFile, &offError, RTErrInfoInitStatic(&ErrInfo));
1334 if (RT_SUCCESS(rc))
1335 {
1336 /*
1337 * Apply the desired writing method.
1338 */
1339 if (!pOpts->fRandomOutputReadBufferSize)
1340 rc = rtFsIsoMakerCmdWriteImageRandomBufferSize(pOpts, hVfsSrcFile, hVfsDstFile, cbImage, &pvBuf);
1341 else
1342 rc = rtFsIsoMakerCmdWriteImageSimple(pOpts, hVfsSrcFile, hVfsDstFile, cbImage, pvBuf, cbBuf);
1343 RTMemTmpFree(pvBuf);
1344
1345 if (RT_SUCCESS(rc) && pOpts->cbRandomOrderVerifciationBlock > 0)
1346 rc = rtFsIsoMakerCmdVerifyImageInRandomOrder(pOpts, hVfsSrcFile, hVfsDstFile, cbImage);
1347
1348 /*
1349 * Flush the output file before releasing it.
1350 */
1351 if (RT_SUCCESS(rc))
1352 {
1353 rc = RTVfsFileFlush(hVfsDstFile);
1354 if (RT_FAILURE(rc))
1355 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileFlush failed on '%s': %Rrc", pOpts->pszOutFile, rc);
1356 }
1357
1358 RTVfsFileRelease(hVfsDstFile);
1359 }
1360 else
1361 {
1362 RTMemTmpFree(pvBuf);
1363 rc = rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pOpts->pszOutFile, rc, offError, &ErrInfo.Core);
1364 }
1365 }
1366 else
1367 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%zu) failed", cbBuf);
1368 }
1369 else
1370 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileGetSize failed: %Rrc", rc);
1371 return rc;
1372}
1373
1374
1375/**
1376 * Formats @a fNameSpecifiers into a '+' separated list of names.
1377 *
1378 * @returns pszDst
1379 * @param fNameSpecifiers The name specifiers.
1380 * @param pszDst The destination bufer.
1381 * @param cbDst The size of the destination buffer.
1382 */
1383static char *rtFsIsoMakerCmdNameSpecifiersToString(uint32_t fNameSpecifiers, char *pszDst, size_t cbDst)
1384{
1385 static struct { const char *pszName; uint32_t cchName; uint32_t fSpec; } const s_aSpecs[] =
1386 {
1387 { RT_STR_TUPLE("primary"), RTFSISOMAKERCMDNAME_PRIMARY_ISO },
1388 { RT_STR_TUPLE("primary-rock"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE },
1389 { RT_STR_TUPLE("primary-trans-tbl"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL },
1390 { RT_STR_TUPLE("joliet"), RTFSISOMAKERCMDNAME_JOLIET },
1391 { RT_STR_TUPLE("joliet-rock"), RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE },
1392 { RT_STR_TUPLE("joliet-trans-tbl"), RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL },
1393 { RT_STR_TUPLE("udf"), RTFSISOMAKERCMDNAME_UDF },
1394 { RT_STR_TUPLE("udf-trans-tbl"), RTFSISOMAKERCMDNAME_UDF_TRANS_TBL },
1395 { RT_STR_TUPLE("hfs"), RTFSISOMAKERCMDNAME_HFS },
1396 { RT_STR_TUPLE("hfs-trans-tbl"), RTFSISOMAKERCMDNAME_HFS_TRANS_TBL },
1397 };
1398
1399 Assert(cbDst > 0);
1400 char *pszRet = pszDst;
1401 for (uint32_t i = 0; i < RT_ELEMENTS(s_aSpecs); i++)
1402 if (s_aSpecs[i].fSpec & fNameSpecifiers)
1403 {
1404 if (pszDst != pszRet && cbDst > 1)
1405 {
1406 *pszDst++ = '+';
1407 cbDst--;
1408 }
1409 if (cbDst > s_aSpecs[i].cchName)
1410 {
1411 memcpy(pszDst, s_aSpecs[i].pszName, s_aSpecs[i].cchName);
1412 cbDst -= s_aSpecs[i].cchName;
1413 pszDst += s_aSpecs[i].cchName;
1414 }
1415 else if (cbDst > 1)
1416 {
1417 memcpy(pszDst, s_aSpecs[i].pszName, cbDst - 1);
1418 pszDst += cbDst - 1;
1419 cbDst = 1;
1420 }
1421
1422 fNameSpecifiers &= ~s_aSpecs[i].fSpec;
1423 if (!fNameSpecifiers)
1424 break;
1425 }
1426 *pszDst = '\0';
1427 return pszRet;
1428}
1429
1430
1431/**
1432 * Parses the --name-setup option.
1433 *
1434 * @returns IPRT status code.
1435 * @param pOpts The ISO maker command instance.
1436 * @param pszSpec The name setup specification.
1437 */
1438static int rtFsIsoMakerCmdOptNameSetup(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
1439{
1440 /*
1441 * Comma separated list of one or more specifiers.
1442 */
1443 uint32_t fNamespaces = 0;
1444 uint32_t fPrevMajor = 0;
1445 uint32_t iNameSpecifier = 0;
1446 uint32_t offSpec = 0;
1447 do
1448 {
1449 /*
1450 * Parse up to the next colon or end of string.
1451 */
1452 uint32_t fNameSpecifier = 0;
1453 char ch;
1454 while ( (ch = pszSpec[offSpec]) != '\0'
1455 && ch != ',')
1456 {
1457 if (RT_C_IS_SPACE(ch) || ch == '+' || ch == '|') /* space, '+' and '|' are allowed as name separators. */
1458 offSpec++;
1459 else
1460 {
1461 /* Find the end of the name. */
1462 uint32_t offEndSpec = offSpec + 1;
1463 while ( (ch = pszSpec[offEndSpec]) != '\0'
1464 && ch != ','
1465 && ch != '+'
1466 && ch != '|'
1467 && !RT_C_IS_SPACE(ch))
1468 offEndSpec++;
1469
1470#define IS_EQUAL(a_sz) (cchName == sizeof(a_sz) - 1U && strncmp(pchName, a_sz, sizeof(a_sz) - 1U) == 0)
1471 const char * const pchName = &pszSpec[offSpec];
1472 uint32_t const cchName = offEndSpec - offSpec;
1473 /* major namespaces */
1474 if ( IS_EQUAL("iso")
1475 || IS_EQUAL("primary")
1476 || IS_EQUAL("iso9660")
1477 || IS_EQUAL("iso-9660")
1478 || IS_EQUAL("primary-iso")
1479 || IS_EQUAL("iso-primary") )
1480 {
1481 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO;
1482 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_ISO_9660;
1483 }
1484 else if (IS_EQUAL("joliet"))
1485 {
1486 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET;
1487 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_JOLIET;
1488 }
1489 else if (IS_EQUAL("udf"))
1490 {
1491#if 0
1492 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF;
1493 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_UDF;
1494#else
1495 return rtFsIsoMakerCmdSyntaxError(pOpts, "UDF support is currently not implemented");
1496#endif
1497 }
1498 else if (IS_EQUAL("hfs") || IS_EQUAL("hfsplus"))
1499 {
1500#if 0
1501 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS;
1502 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_HFS;
1503#else
1504 return rtFsIsoMakerCmdSyntaxError(pOpts, "Hybrid HFS+ support is currently not implemented");
1505#endif
1506 }
1507 /* rock ridge */
1508 else if ( IS_EQUAL("rr")
1509 || IS_EQUAL("rock")
1510 || IS_EQUAL("rock-ridge"))
1511 {
1512 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
1513 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
1514 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
1515 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
1516 else
1517 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified rock-ridge name specifier");
1518 }
1519 else if ( IS_EQUAL("iso-rr") || IS_EQUAL("iso-rock") || IS_EQUAL("iso-rock-ridge")
1520 || IS_EQUAL("primary-rr") || IS_EQUAL("primary-rock") || IS_EQUAL("primary-rock-ridge")
1521 || IS_EQUAL("iso9660-rr") || IS_EQUAL("iso9660-rock") || IS_EQUAL("iso9660-rock-ridge")
1522 || IS_EQUAL("iso-9660-rr") || IS_EQUAL("iso-9660-rock") || IS_EQUAL("iso-9660-rock-ridge")
1523 || IS_EQUAL("primaryiso-rr") || IS_EQUAL("primaryiso-rock") || IS_EQUAL("primaryiso-rock-ridge")
1524 || IS_EQUAL("primary-iso-rr") || IS_EQUAL("primary-iso-rock") || IS_EQUAL("primary-iso-rock-ridge") )
1525 {
1526 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
1527 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
1528 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-rock-ridge must come after the iso-9660 name specifier");
1529 }
1530 else if (IS_EQUAL("joliet-rr") || IS_EQUAL("joliet-rock") || IS_EQUAL("joliet-rock-ridge"))
1531 {
1532 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
1533 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
1534 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-rock-ridge must come after the joliet name specifier");
1535 }
1536 /* trans.tbl */
1537 else if (IS_EQUAL("trans") || IS_EQUAL("trans-tbl"))
1538 {
1539 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
1540 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
1541 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
1542 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
1543 else
1544 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified trans-tbl name specifier");
1545 }
1546 else if ( IS_EQUAL("iso-trans") || IS_EQUAL("iso-trans-tbl")
1547 || IS_EQUAL("primary-trans") || IS_EQUAL("primary-trans-tbl")
1548 || IS_EQUAL("iso9660-trans") || IS_EQUAL("iso9660-trans-tbl")
1549 || IS_EQUAL("iso-9660-trans") || IS_EQUAL("iso-9660-trans-tbl")
1550 || IS_EQUAL("primaryiso-trans") || IS_EQUAL("primaryiso-trans-tbl")
1551 || IS_EQUAL("primary-iso-trans") || IS_EQUAL("primary-iso-trans-tbl") )
1552 {
1553 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
1554 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
1555 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-trans-tbl must come after the iso-9660 name specifier");
1556 }
1557 else if (IS_EQUAL("joliet-trans") || IS_EQUAL("joliet-trans-tbl"))
1558 {
1559 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
1560 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
1561 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-trans-tbl must come after the joliet name specifier");
1562 }
1563 else if (IS_EQUAL("udf-trans") || IS_EQUAL("udf-trans-tbl"))
1564 {
1565 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF_TRANS_TBL;
1566 if (!(fNamespaces & RTFSISOMAKERCMDNAME_UDF))
1567 return rtFsIsoMakerCmdSyntaxError(pOpts, "udf-trans-tbl must come after the udf name specifier");
1568 }
1569 else if (IS_EQUAL("hfs-trans") || IS_EQUAL("hfs-trans-tbl"))
1570 {
1571 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS_TRANS_TBL;
1572 if (!(fNamespaces & RTFSISOMAKERCMDNAME_HFS))
1573 return rtFsIsoMakerCmdSyntaxError(pOpts, "hfs-trans-tbl must come after the hfs name specifier");
1574 }
1575 else
1576 return rtFsIsoMakerCmdSyntaxError(pOpts, "unknown name specifier '%.*s'", cchName, pchName);
1577#undef IS_EQUAL
1578 offSpec = offEndSpec;
1579 }
1580 } /* while same specifier */
1581
1582 /*
1583 * Check that it wasn't empty.
1584 */
1585 if (fNameSpecifier == 0)
1586 return rtFsIsoMakerCmdSyntaxError(pOpts, "name specifier #%u (0-based) is empty ", iNameSpecifier);
1587
1588 /*
1589 * Complain if a major namespace name is duplicated. The rock-ridge and
1590 * trans.tbl names are simple to replace, the others affect the two former
1591 * names and are therefore not allowed twice in the list.
1592 */
1593 uint32_t i = iNameSpecifier;
1594 while (i-- > 0)
1595 {
1596 uint32_t fRepeated = (fNameSpecifier & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1597 & (pOpts->afNameSpecifiers[i] & RTFSISOMAKERCMDNAME_MAJOR_MASK);
1598 if (fRepeated)
1599 {
1600 char szTmp[128];
1601 return rtFsIsoMakerCmdSyntaxError(pOpts, "repeating name specifier%s: %s", RT_IS_POWER_OF_TWO(fRepeated) ? "" : "s",
1602 rtFsIsoMakerCmdNameSpecifiersToString(fRepeated, szTmp, sizeof(szTmp)));
1603 }
1604 }
1605
1606 /*
1607 * Add it.
1608 */
1609 if (iNameSpecifier >= RT_ELEMENTS(pOpts->afNameSpecifiers))
1610 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many name specifiers (max %d)", RT_ELEMENTS(pOpts->afNameSpecifiers));
1611 pOpts->afNameSpecifiers[iNameSpecifier] = fNameSpecifier;
1612 iNameSpecifier++;
1613
1614 /*
1615 * Next, if any.
1616 */
1617 if (pszSpec[offSpec] == ',')
1618 offSpec++;
1619 } while (pszSpec[offSpec] != '\0');
1620
1621 pOpts->cNameSpecifiers = iNameSpecifier;
1622 pOpts->fDstNamespaces = fNamespaces;
1623
1624 return VINF_SUCCESS;
1625}
1626
1627
1628/**
1629 * Processes a non-option argument.
1630 *
1631 * @returns IPRT status code.
1632 * @param pOpts The ISO maker command instance.
1633 * @param pszSpec The specification of what to add.
1634 * @param fWithSrc Whether the specification includes a source path
1635 * or not.
1636 * @param pParsed Where to return the parsed name specification.
1637 */
1638static int rtFsIsoMakerCmdParseNameSpec(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec, bool fWithSrc,
1639 PRTFSISOMKCMDPARSEDNAMES pParsed)
1640{
1641 const char * const pszSpecIn = pszSpec;
1642 uint32_t const cMaxNames = pOpts->cNameSpecifiers + fWithSrc;
1643
1644 /*
1645 * Split it up by '='.
1646 */
1647 pParsed->cNames = 0;
1648 pParsed->cNamesWithSrc = 0;
1649 pParsed->enmSrcType = fWithSrc ? RTFSISOMKCMDPARSEDNAMES::kSrcType_Normal : RTFSISOMKCMDPARSEDNAMES::kSrcType_None;
1650 for (;;)
1651 {
1652 const char *pszEqual = strchr(pszSpec, '=');
1653 size_t cchName = pszEqual ? pszEqual - pszSpec : strlen(pszSpec);
1654 bool fNeedSlash = (pszEqual || !fWithSrc) && !RTPATH_IS_SLASH(*pszSpec) && cchName > 0;
1655 if (cchName + fNeedSlash >= sizeof(pParsed->aNames[pParsed->cNamesWithSrc].szPath))
1656 return rtFsIsoMakerCmdSyntaxError(pOpts, "name #%u (0-based) is too long: %s", pParsed->cNamesWithSrc, pszSpecIn);
1657 if (pParsed->cNamesWithSrc >= cMaxNames)
1658 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many names specified (max %u%s): %s",
1659 pOpts->cNameSpecifiers, fWithSrc ? " + source" : "", pszSpecIn);
1660 if (!fNeedSlash)
1661 memcpy(pParsed->aNames[pParsed->cNamesWithSrc].szPath, pszSpec, cchName);
1662 else
1663 {
1664 memcpy(&pParsed->aNames[pParsed->cNamesWithSrc].szPath[1], pszSpec, cchName);
1665 pParsed->aNames[pParsed->cNamesWithSrc].szPath[0] = RTPATH_SLASH;
1666 cchName++;
1667 }
1668 pParsed->aNames[pParsed->cNamesWithSrc].szPath[cchName] = '\0';
1669 pParsed->aNames[pParsed->cNamesWithSrc].cchPath = (uint32_t)cchName;
1670 pParsed->cNamesWithSrc++;
1671
1672 if (!pszEqual)
1673 {
1674 if (fWithSrc)
1675 {
1676 if (!cchName)
1677 return rtFsIsoMakerCmdSyntaxError(pOpts, "empty source file name: %s", pszSpecIn);
1678 if (cchName == 8 && strcmp(pszSpec, ":remove:") == 0)
1679 pParsed->enmSrcType = RTFSISOMKCMDPARSEDNAMES::kSrcType_Remove;
1680 else if (cchName == 13 && strcmp(pszSpec, ":must-remove:") == 0)
1681 pParsed->enmSrcType = RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove;
1682 }
1683 break;
1684 }
1685 pszSpec = pszEqual + 1;
1686 }
1687
1688 /*
1689 * If there are too few names specified, move the source and repeat the
1690 * last non-source name. If only source, convert source into a name spec.
1691 */
1692 if (pParsed->cNamesWithSrc < cMaxNames)
1693 {
1694 uint32_t iSrc;
1695 if (!fWithSrc)
1696 iSrc = pParsed->cNamesWithSrc - 1;
1697 else
1698 {
1699 pParsed->aNames[pOpts->cNameSpecifiers] = pParsed->aNames[pParsed->cNamesWithSrc - 1];
1700 iSrc = pParsed->cNamesWithSrc >= 2 ? pParsed->cNamesWithSrc - 2 : 0;
1701 }
1702
1703 /* If the source is a input file name specifier, reduce it to something that starts with a slash. */
1704 if (pParsed->cNamesWithSrc == 1 && fWithSrc)
1705 {
1706 /** @todo just take the final component and prepend a slash (or whatever
1707 * directory in the ISO we import relative to). */
1708 if (RTVfsChainIsSpec(pParsed->aNames[iSrc].szPath))
1709 {
1710 uint32_t offError;
1711 char *pszFinalPath;
1712 int rc = RTVfsChainQueryFinalPath(pParsed->aNames[iSrc].szPath, &pszFinalPath, &offError);
1713 if (RT_FAILURE(rc))
1714 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryFinalPath",
1715 pParsed->aNames[iSrc].szPath, rc, offError, NULL);
1716 pParsed->aNames[iSrc].cchPath = (uint32_t)strlen(pszFinalPath);
1717 if (RTPATH_IS_SLASH(*pszFinalPath))
1718 memcpy(pParsed->aNames[iSrc].szPath, pszFinalPath, pParsed->aNames[iSrc].cchPath + 1);
1719 else
1720 {
1721 memcpy(&pParsed->aNames[iSrc].szPath[1], pszFinalPath, pParsed->aNames[iSrc].cchPath + 1);
1722 pParsed->aNames[iSrc].szPath[0] = RTPATH_SLASH;
1723 pParsed->aNames[iSrc].cchPath++;
1724 }
1725 RTStrFree(pszFinalPath);
1726 }
1727#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
1728 else if ( RTPATH_IS_VOLSEP(pParsed->aNames[iSrc].szPath[1])
1729 && RT_C_IS_ALPHA(pParsed->aNames[iSrc].szPath[0]))
1730 {
1731 if (RTPATH_IS_SLASH(pParsed->aNames[iSrc].szPath[2]))
1732 {
1733 memmove(&pParsed->aNames[iSrc].szPath[0], &pParsed->aNames[iSrc].szPath[2], pParsed->aNames[iSrc].cchPath - 1);
1734 pParsed->aNames[iSrc].cchPath -= 2;
1735 }
1736 else
1737 {
1738 memmove(&pParsed->aNames[iSrc].szPath[1], &pParsed->aNames[iSrc].szPath[2], pParsed->aNames[iSrc].cchPath - 1);
1739 pParsed->aNames[iSrc].szPath[0] = RTPATH_SLASH;
1740 pParsed->aNames[iSrc].cchPath -= 1;
1741 }
1742 }
1743#endif
1744 else if (!RTPATH_IS_SLASH(pParsed->aNames[iSrc].szPath[0]))
1745 {
1746 if (pParsed->aNames[iSrc].cchPath + 2 > sizeof(pParsed->aNames[iSrc].szPath))
1747 return rtFsIsoMakerCmdSyntaxError(pOpts, "name too long: %s", pszSpecIn);
1748 memmove(&pParsed->aNames[iSrc].szPath[1], &pParsed->aNames[iSrc].szPath[0], pParsed->aNames[iSrc].cchPath + 1);
1749 pParsed->aNames[iSrc].szPath[0] = RTPATH_SLASH;
1750 pParsed->aNames[iSrc].cchPath++;
1751 }
1752 }
1753
1754 for (uint32_t iDst = iSrc + 1; iDst < pOpts->cNameSpecifiers; iDst++)
1755 pParsed->aNames[iDst] = pParsed->aNames[iSrc];
1756
1757 pParsed->cNamesWithSrc = cMaxNames;
1758 }
1759 pParsed->cNames = pOpts->cNameSpecifiers;
1760
1761 /*
1762 * Copy the specifier flags and check that the paths all starts with slashes.
1763 */
1764 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
1765 {
1766 pParsed->aNames[i].fNameSpecifiers = pOpts->afNameSpecifiers[i];
1767 Assert( pParsed->aNames[i].cchPath == 0
1768 || RTPATH_IS_SLASH(pParsed->aNames[i].szPath[0]));
1769 }
1770
1771 return VINF_SUCCESS;
1772}
1773
1774
1775/**
1776 * Enteres an object into the namespace by full paths.
1777 *
1778 * This is used by rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath and
1779 * rtFsIsoMakerCmdAddFile.
1780 *
1781 * @returns IPRT status code.
1782 * @param pOpts The ISO maker command instance.
1783 * @param idxObj The configuration index of the object to be named.
1784 * @param pParsed The parsed names.
1785 * @param pszSrcOrName Source file or name.
1786 */
1787static int rtFsIsoMakerCmdSetObjPaths(PRTFSISOMAKERCMDOPTS pOpts, uint32_t idxObj, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1788 const char *pszSrcOrName)
1789{
1790 int rc = VINF_SUCCESS;
1791 for (uint32_t i = 0; i < pParsed->cNames; i++)
1792 if (pParsed->aNames[i].cchPath > 0)
1793 {
1794 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1795 {
1796 rc = RTFsIsoMakerObjSetPath(pOpts->hIsoMaker, idxObj,
1797 pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1798 pParsed->aNames[i].szPath);
1799 if (RT_FAILURE(rc))
1800 {
1801 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting name '%s' on '%s': %Rrc",
1802 pParsed->aNames[i].szPath, pszSrcOrName, rc);
1803 break;
1804 }
1805 }
1806 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MINOR_MASK)
1807 {
1808 /** @todo add APIs for this. */
1809 }
1810 }
1811 return rc;
1812}
1813
1814
1815/**
1816 * Adds a file.
1817 *
1818 * @returns IPRT status code.
1819 * @param pOpts The ISO maker command instance.
1820 * @param pszSrc The path to the source file.
1821 * @param pParsed The parsed names.
1822 * @param pidxObj Where to return the configuration index for the
1823 * added file. Optional.
1824 */
1825static int rtFsIsoMakerCmdAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1826 uint32_t *pidxObj)
1827{
1828 int rc;
1829 uint32_t idxObj = UINT32_MAX;
1830 if ( pOpts->hSrcVfs == NIL_RTVFS
1831 || RTVfsChainIsSpec(pszSrc))
1832 {
1833 rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(pOpts->hIsoMaker, pszSrc, &idxObj);
1834 if (RT_FAILURE(rc))
1835 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s': %Rrc", pszSrc, rc);
1836 }
1837 else
1838 {
1839 RTVFSFILE hVfsFileSrc;
1840 rc = RTVfsFileOpen(pOpts->hSrcVfs, pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSrc);
1841 if (RT_FAILURE(rc))
1842 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening '%s' (inside '%s'): %Rrc", pszSrc, pOpts->pszSrcVfs, rc);
1843
1844 rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(pOpts->hIsoMaker, hVfsFileSrc, &idxObj);
1845 RTVfsFileRelease(hVfsFileSrc);
1846 if (RT_FAILURE(rc))
1847 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s' (VFS): %Rrc", pszSrc, rc);
1848 }
1849
1850 pOpts->cItemsAdded++;
1851 if (pidxObj)
1852 *pidxObj = idxObj;
1853
1854 return rtFsIsoMakerCmdSetObjPaths(pOpts, idxObj, pParsed, pszSrc);
1855}
1856
1857
1858/**
1859 * Applies filtering rules.
1860 *
1861 * @returns true if filtered out, false if included.
1862 * @param pOpts The ISO maker command instance.
1863 * @param pszSrc The source source.
1864 * @param pszName The name part (maybe different buffer from pszSrc).
1865 * @param fIsDir Set if directory, clear if not.
1866 */
1867static bool rtFsIsoMakerCmdIsFilteredOut(PRTFSISOMAKERCMDOPTS pOpts, const char *pszDir, const char *pszName, bool fIsDir)
1868{
1869 /* Ignore trans.tbl files. */
1870 if ( !fIsDir
1871 && RTStrICmp(pszName, pOpts->pszTransTbl) == 0)
1872 return true;
1873
1874 RT_NOREF(pOpts, pszDir, pszName, fIsDir);
1875 return false;
1876}
1877
1878
1879/**
1880 * Adds a directory, from a VFS chain or real file system.
1881 *
1882 * @returns IPRT status code.
1883 * @param pOpts The ISO maker command instance.
1884 * @param pszSrc The path to the source directory.
1885 * @param pParsed The parsed names.
1886 */
1887static int rtFsIsoMakerCmdAddDir(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed)
1888{
1889 RT_NOREF(pParsed);
1890 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding directory '%s' failed: not implemented", pszSrc);
1891}
1892
1893
1894/**
1895 * Worker for rtFsIsoMakerCmdAddVfsDir that does the recursion.
1896 *
1897 * @returns IPRT status code.
1898 * @param pOpts The ISO maker command instance.
1899 * @param hVfsDir The directory to process.
1900 * @param idxDirObj The configuration index of the directory.
1901 * @param pszSrc Pointer to the source path buffer. RTPATH_MAX
1902 * in size. Okay to modify beyond @a cchSrc.
1903 * @param cchSrc Length of the path corresponding to @a hVfsDir.
1904 * @param fNamespaces Which ISO maker namespaces to add the names to.
1905 * @param cDepth Number of recursions. Used to deal with loopy
1906 * directories.
1907 */
1908static int rtFsIsoMakerCmdAddVfsDirRecursive(PRTFSISOMAKERCMDOPTS pOpts, RTVFSDIR hVfsDir, uint32_t idxDirObj,
1909 char *pszSrc, size_t cchSrc, uint32_t fNamespaces, uint8_t cDepth)
1910{
1911 /*
1912 * Check that we're not in too deep.
1913 */
1914 if (cDepth >= RTFSISOMAKERCMD_MAX_DIR_RECURSIONS)
1915 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE,
1916 "Recursive (VFS) dir add too deep (depth=%u): %.*s", cDepth, cchSrc, pszSrc);
1917 /*
1918 * Enumerate the directory.
1919 */
1920 int rc;
1921 size_t cbDirEntryAlloced = sizeof(RTDIRENTRYEX);
1922 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1923 if (pDirEntry)
1924 {
1925 for (;;)
1926 {
1927 /*
1928 * Read the next entry.
1929 */
1930 size_t cbDirEntry = cbDirEntryAlloced;
1931 rc = RTVfsDirReadEx(hVfsDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1932 if (RT_FAILURE(rc))
1933 {
1934 if (rc == VERR_NO_MORE_FILES)
1935 rc = VINF_SUCCESS;
1936 else if (rc == VERR_BUFFER_OVERFLOW)
1937 {
1938 RTMemTmpFree(pDirEntry);
1939 cbDirEntryAlloced = RT_ALIGN_Z(RT_MIN(cbDirEntry, cbDirEntryAlloced) + 64, 64);
1940 pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1941 if (pDirEntry)
1942 continue;
1943 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "Out of memory (direntry buffer)");
1944 }
1945 else
1946 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsDirReadEx failed on %.*s: %Rrc", cchSrc, pszSrc, rc);
1947 break;
1948 }
1949
1950 /* Ignore '.' and '..' entries. */
1951 if (RTDirEntryExIsStdDotLink(pDirEntry))
1952 continue;
1953
1954 /*
1955 * Process the entry.
1956 */
1957
1958 /* Update the name. */
1959 if (cchSrc + 1 + pDirEntry->cbName < RTPATH_MAX)
1960 {
1961 pszSrc[cchSrc] = '/'; /* VFS only groks unix slashes */
1962 memcpy(&pszSrc[cchSrc + 1], pDirEntry->szName, pDirEntry->cbName);
1963 pszSrc[cchSrc + 1 + pDirEntry->cbName] = '\0';
1964 }
1965 else
1966 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_FILENAME_TOO_LONG, "Filename is too long (depth %u): '%.*s/%s'",
1967 cDepth, cchSrc, pszSrc, pDirEntry->szName);
1968
1969 /* Okay? Check name filtering. */
1970 if ( RT_SUCCESS(rc)
1971 && !rtFsIsoMakerCmdIsFilteredOut(pOpts, pszSrc, pDirEntry->szName, RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode)))
1972 {
1973 /* Do type specific adding. */
1974 uint32_t idxObj = UINT32_MAX;
1975 if (RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
1976 {
1977 /*
1978 * Files are added with VFS file sources.
1979 * The ASSUMPTION is that we're working with a virtual file system
1980 * here and won't be wasting native file descriptors.
1981 */
1982 RTVFSFILE hVfsFileSrc;
1983 rc = RTVfsDirOpenFile(hVfsDir, pDirEntry->szName,
1984 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSrc);
1985 if (RT_SUCCESS(rc))
1986 {
1987 rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(pOpts->hIsoMaker, hVfsFileSrc, &idxObj);
1988 RTVfsFileRelease(hVfsFileSrc);
1989 if (RT_SUCCESS(rc))
1990 {
1991 pOpts->cItemsAdded++;
1992 rc = RTFsIsoMakerObjSetNameAndParent(pOpts->hIsoMaker, idxObj, idxDirObj, fNamespaces,
1993 pDirEntry->szName);
1994 if (RT_FAILURE(rc))
1995 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting parent & name on file '%s' to '%s': %Rrc",
1996 pszSrc, pDirEntry->szName, rc);
1997 }
1998 else
1999 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding file '%s' (VFS recursive): %Rrc", pszSrc, rc);
2000 }
2001 else
2002 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening file '%s' (VFS recursive): %Rrc", pszSrc, rc);
2003 }
2004 else if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
2005 {
2006 /*
2007 * Open and add the sub-directory.
2008 */
2009 RTVFSDIR hVfsSubDirSrc;
2010 rc = RTVfsDirOpenDir(hVfsDir, pDirEntry->szName, 0 /*fFlags*/, &hVfsSubDirSrc);
2011 if (RT_SUCCESS(rc))
2012 {
2013 rc = RTFsIsoMakerAddUnnamedDir(pOpts->hIsoMaker, &pDirEntry->Info, &idxObj);
2014 if (RT_SUCCESS(rc))
2015 {
2016 pOpts->cItemsAdded++;
2017 rc = RTFsIsoMakerObjSetNameAndParent(pOpts->hIsoMaker, idxObj, idxDirObj, fNamespaces,
2018 pDirEntry->szName);
2019 if (RT_SUCCESS(rc))
2020 /* Recurse into the sub-directory. */
2021 rc = rtFsIsoMakerCmdAddVfsDirRecursive(pOpts, hVfsSubDirSrc, idxObj, pszSrc,
2022 cchSrc + 1 + pDirEntry->cbName, fNamespaces, cDepth + 1);
2023 else
2024 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
2025 "Error setting parent & name on directory '%s' to '%s': %Rrc",
2026 pszSrc, pDirEntry->szName, rc);
2027 }
2028 else
2029 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding directory '%s' (VFS recursive): %Rrc", pszSrc, rc);
2030 RTVfsDirRelease(hVfsSubDirSrc);
2031 }
2032 else
2033 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening directory '%s' (VFS recursive): %Rrc", pszSrc, rc);
2034 }
2035 else if (RTFS_IS_SYMLINK(pDirEntry->Info.Attr.fMode))
2036 {
2037 /*
2038 * TODO: ISO FS symlink support.
2039 */
2040 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
2041 "Adding symlink '%s' failed: not yet implemented", pszSrc);
2042 }
2043 else
2044 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
2045 "Adding special file '%s' failed: not implemented", pszSrc);
2046 }
2047 if (RT_FAILURE(rc))
2048 break;
2049 }
2050
2051 RTMemTmpFree(pDirEntry);
2052 }
2053 else
2054 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "Out of memory! (direntry buffer)");
2055 return rc;
2056}
2057
2058
2059/**
2060 * Adds a directory, from the source VFS.
2061 *
2062 * @returns IPRT status code.
2063 * @param pOpts The ISO maker command instance.
2064 * @param pParsed The parsed names.
2065 * @param pidxObj Where to return the configuration index for the
2066 * added file. Optional.
2067 */
2068static int rtFsIsoMakerCmdAddVfsDir(PRTFSISOMAKERCMDOPTS pOpts, PCRTFSISOMKCMDPARSEDNAMES pParsed, PCRTFSOBJINFO pObjInfo)
2069{
2070 Assert(pParsed->cNames < pParsed->cNamesWithSrc);
2071
2072 /*
2073 * Open the directory.
2074 */
2075 char *pszDir = pParsed->aNames[pParsed->cNamesWithSrc - 1].szPath;
2076 RTPathChangeToUnixSlashes(pszDir, true /*fForce*/); /* VFS currently only understand unix slashes. */
2077 RTVFSDIR hVfsDirSrc;
2078 int rc = RTVfsDirOpen(pOpts->hSrcVfs, pszDir, 0 /*fFlags*/, &hVfsDirSrc);
2079 if (RT_FAILURE(rc))
2080 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening directory '%s' (inside '%s'): %Rrc",
2081 pszDir, pOpts->pszSrcVfs, rc);
2082
2083 /*
2084 * Add the directory if it doesn't exist.
2085 */
2086 uint32_t idxObj = UINT32_MAX;
2087 for (uint32_t i = 0; i < pParsed->cNames; i++)
2088 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
2089 {
2090 idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
2091 pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
2092 pParsed->aNames[i].szPath);
2093 if (idxObj != UINT32_MAX)
2094 {
2095 /** @todo make sure the directory is present in the other namespace. */
2096 break;
2097 }
2098 }
2099 if (idxObj == UINT32_MAX)
2100 {
2101 rc = RTFsIsoMakerAddUnnamedDir(pOpts->hIsoMaker, pObjInfo, &idxObj);
2102 if (RT_SUCCESS(rc))
2103 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxObj, pParsed, pParsed->aNames[pParsed->cNames - 1].szPath);
2104 else
2105 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerAddUnnamedDir failed: %Rrc", rc);
2106 }
2107 if (RT_SUCCESS(rc))
2108 {
2109 /*
2110 * Add the directory content.
2111 */
2112 uint32_t fNamespaces = 0;
2113 for (uint32_t i = 0; i < pParsed->cNames; i++)
2114 fNamespaces |= pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK;
2115 rc = rtFsIsoMakerCmdAddVfsDirRecursive(pOpts, hVfsDirSrc, idxObj, pszDir,
2116 pParsed->aNames[pParsed->cNamesWithSrc - 1].cchPath, fNamespaces, 0 /*cDepth*/);
2117 }
2118 RTVfsDirRelease(hVfsDirSrc);
2119 return rc;
2120}
2121
2122
2123
2124
2125/**
2126 * Adds a file after first making sure it's a file.
2127 *
2128 * @returns IPRT status code
2129 * @param pOpts The ISO maker command instance.
2130 * @param pszSrc The path to the source file.
2131 * @param pParsed The parsed names.
2132 * @param pidxObj Where to return the configuration index for the
2133 * added file. Optional.
2134 */
2135static int rtFsIsoMakerCmdStatAndAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
2136 uint32_t *pidxObj)
2137{
2138 int rc;
2139 RTFSOBJINFO ObjInfo;
2140 if ( pOpts->hSrcVfs == NIL_RTVFS
2141 || RTVfsChainIsSpec(pszSrc))
2142 {
2143 uint32_t offError;
2144 RTERRINFOSTATIC ErrInfo;
2145 rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
2146 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
2147 if (RT_FAILURE(rc))
2148 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
2149 }
2150 else
2151 {
2152 rc = RTVfsQueryPathInfo(pOpts->hSrcVfs, pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
2153 if (RT_FAILURE(rc))
2154 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsQueryPathInfo failed on %s (inside %s): %Rrc",
2155 pszSrc, pOpts->pszSrcVfs, rc);
2156 }
2157
2158 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
2159 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, pParsed, pidxObj);
2160 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_A_FILE, "Not a file: %s", pszSrc);
2161}
2162
2163
2164/**
2165 * Processes a non-option argument.
2166 *
2167 * @returns IPRT status code.
2168 * @param pOpts The ISO maker command instance.
2169 * @param pszSpec The specification of what to add.
2170 */
2171static int rtFsIsoMakerCmdAddSomething(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
2172{
2173 /*
2174 * Parse the name spec.
2175 */
2176 RTFSISOMKCMDPARSEDNAMES Parsed;
2177 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszSpec, true /*fWithSrc*/, &Parsed);
2178 if (RT_FAILURE(rc))
2179 return rc;
2180
2181 /*
2182 * Deal with special source filenames used to remove/change stuff.
2183 */
2184 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_Remove
2185 || Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove)
2186 {
2187 const char *pszFirstNm = NULL;
2188 uint32_t cRemoved = 0;
2189 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
2190 if ( Parsed.aNames[i].cchPath > 0
2191 && (Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK))
2192 {
2193 pszFirstNm = Parsed.aNames[i].szPath;
2194 uint32_t idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
2195 Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
2196 Parsed.aNames[i].szPath);
2197 if (idxObj != UINT32_MAX)
2198 {
2199 rc = RTFsIsoMakerObjRemove(pOpts->hIsoMaker, idxObj);
2200 if (RT_FAILURE(rc))
2201 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to remove '%s': %Rrc", pszSpec, rc);
2202 cRemoved++;
2203 }
2204 }
2205 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove
2206 && cRemoved == 0)
2207 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "Failed to locate '%s' for removal", pszSpec);
2208 }
2209 /*
2210 * Add regular source.
2211 */
2212 else
2213 {
2214 const char *pszSrc = Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath;
2215 RTFSOBJINFO ObjInfo;
2216 if ( pOpts->hSrcVfs == NIL_RTVFS
2217 || RTVfsChainIsSpec(pszSrc))
2218 {
2219 uint32_t offError;
2220 RTERRINFOSTATIC ErrInfo;
2221 rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
2222 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
2223 if (RT_FAILURE(rc))
2224 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
2225 }
2226 else
2227 {
2228 rc = RTVfsQueryPathInfo(pOpts->hSrcVfs, pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
2229 if (RT_FAILURE(rc))
2230 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsQueryPathInfo failed on %s (in %s): %Rrc",
2231 pszSrc, pOpts->pszSrcVfs, rc);
2232 }
2233
2234 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
2235 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, &Parsed, NULL /*pidxObj*/);
2236
2237 if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
2238 {
2239 if ( pOpts->hSrcVfs == NIL_RTVFS
2240 || RTVfsChainIsSpec(pszSrc))
2241 return rtFsIsoMakerCmdAddDir(pOpts, pszSrc, &Parsed);
2242 return rtFsIsoMakerCmdAddVfsDir(pOpts, &Parsed, &ObjInfo);
2243 }
2244
2245 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
2246 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding symlink '%s' failed: not yet implemented", pszSpec);
2247
2248 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding special file '%s' failed: not implemented", pszSpec);
2249 }
2250
2251 return VINF_SUCCESS;
2252}
2253
2254
2255/**
2256 * Opens an ISO and use it for subsequent file system accesses.
2257 *
2258 * This is handy for duplicating a part of an ISO in the new image.
2259 *
2260 * @returns IPRT status code.
2261 * @param pOpts The ISO maker command instance.
2262 * @param pszIsoSpec The ISO path specifier.
2263 * @param pszOption The option we're being called on.
2264 * @param fFlags RTFSISO9660_F_XXX
2265 */
2266static int rtFsIsoMakerCmdOptPushIso(PRTFSISOMAKERCMDOPTS pOpts, const char *pszIsoSpec, const char *pszOption, uint32_t fFlags)
2267{
2268 if (pOpts->hSrcVfs != NIL_RTVFS)
2269 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
2270 "Nested %s usage is not supported (previous: %s %s)",
2271 pszOption, pOpts->pszSrcVfsOption, pOpts->pszSrcVfs);
2272
2273 /*
2274 * Try open the file.
2275 */
2276 RTVFSFILE hVfsFileIso;
2277 uint32_t offError;
2278 RTERRINFOSTATIC ErrInfo;
2279 int rc = RTVfsChainOpenFile(pszIsoSpec, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE,
2280 &hVfsFileIso, &offError, RTErrInfoInitStatic(&ErrInfo));
2281 if (RT_SUCCESS(rc))
2282 {
2283 RTVFS hSrcVfs;
2284 rc = RTFsIso9660VolOpen(hVfsFileIso, fFlags, &hSrcVfs, RTErrInfoInitStatic(&ErrInfo));
2285 RTVfsFileRelease(hVfsFileIso);
2286 if (RT_SUCCESS(rc))
2287 {
2288 pOpts->hSrcVfs = hSrcVfs;
2289 pOpts->pszSrcVfs = pszIsoSpec;
2290 pOpts->pszSrcVfsOption = pszOption;
2291 return VINF_SUCCESS;
2292 }
2293
2294 if (RTErrInfoIsSet(&ErrInfo.Core))
2295 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to open '%s' as ISO FS: %Rrc - %s",
2296 pszIsoSpec, rc, ErrInfo.Core.pszMsg);
2297 else
2298 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to open '%s' as ISO FS: %Rrc", pszIsoSpec, rc);
2299 }
2300 else
2301 rc = rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszIsoSpec, rc, offError, &ErrInfo.Core);
2302 return rc;
2303}
2304
2305
2306/**
2307 * Counter part to --push-iso and friends.
2308 *
2309 * @returns IPRT status code.
2310 * @param pOpts The ISO maker command instance.
2311 */
2312static int rtFsIsoMakerCmdOptPop(PRTFSISOMAKERCMDOPTS pOpts)
2313{
2314 if (pOpts->hSrcVfs != NIL_RTVFS)
2315 {
2316 RTVfsRelease(pOpts->hSrcVfs);
2317 pOpts->hSrcVfs = NIL_RTVFS;
2318 pOpts->pszSrcVfs = NULL;
2319 pOpts->pszSrcVfsOption = NULL;
2320 return VINF_SUCCESS;
2321 }
2322 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "--pop without --push-xxx");
2323}
2324
2325
2326/**
2327 * Deals with the --import-iso {iso-file-spec} options.
2328 *
2329 * @returns IPRT status code
2330 * @param pOpts The ISO maker command instance.
2331 * @param pszIsoSpec The ISO path specifier.
2332 */
2333static int rtFsIsoMakerCmdOptImportIso(PRTFSISOMAKERCMDOPTS pOpts, const char *pszIsoSpec)
2334{
2335 RTFSISOMAKERIMPORTRESULTS Results;
2336 RTERRINFOSTATIC ErrInfo;
2337 int rc = RTFsIsoMakerImport(pOpts->hIsoMaker, pszIsoSpec, 0 /*fFlags*/, &Results, RTErrInfoInitStatic(&ErrInfo));
2338
2339 pOpts->cItemsAdded += Results.cAddedFiles;
2340 pOpts->cItemsAdded += Results.cAddedSymlinks;
2341 pOpts->cItemsAdded += Results.cAddedDirs;
2342 pOpts->cItemsAdded += Results.cBootCatEntries != UINT32_MAX ? Results.cBootCatEntries : 0;
2343 pOpts->cItemsAdded += Results.cbSysArea != 0 ? 1 : 0;
2344
2345 rtFsIsoMakerPrintf(pOpts, "ISO imported statistics for '%s'\n", pszIsoSpec);
2346 rtFsIsoMakerPrintf(pOpts, " cAddedNames: %'14RU32\n", Results.cAddedNames);
2347 rtFsIsoMakerPrintf(pOpts, " cAddedDirs: %'14RU32\n", Results.cAddedDirs);
2348 rtFsIsoMakerPrintf(pOpts, " cbAddedDataBlocks: %'14RU64 bytes\n", Results.cbAddedDataBlocks);
2349 rtFsIsoMakerPrintf(pOpts, " cAddedFiles: %'14RU32\n", Results.cAddedFiles);
2350 rtFsIsoMakerPrintf(pOpts, " cAddedSymlinks: %'14RU32\n", Results.cAddedSymlinks);
2351 if (Results.cBootCatEntries == UINT32_MAX)
2352 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: none\n");
2353 else
2354 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: %'14RU32\n", Results.cBootCatEntries);
2355 rtFsIsoMakerPrintf(pOpts, " cbSysArea: %'14RU32\n", Results.cbSysArea);
2356 rtFsIsoMakerPrintf(pOpts, " cErrors: %'14RU32\n", Results.cErrors);
2357
2358 if (RT_SUCCESS(rc))
2359 return rc;
2360 if (Results.offError != UINT32_MAX)
2361 return rtFsIsoMakerCmdChainError(pOpts, "RTFsIsoMakerImport", pszIsoSpec, rc, Results.offError, &ErrInfo.Core);
2362 if (RTErrInfoIsSet(&ErrInfo.Core))
2363 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc - %s", rc, ErrInfo.Core.pszMsg);
2364 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc", rc);
2365}
2366
2367
2368/**
2369 * Deals with: -G|--generic-boot {file}
2370 *
2371 * This concers content the first 16 sectors of the image. We start loading the
2372 * file at byte 0 in the image and stops at 32KB.
2373 *
2374 * @returns IPRT status code
2375 * @param pOpts The ISO maker command instance.
2376 * @param pszGenericBootImage The generic boot image source.
2377 */
2378static int rtFsIsoMakerCmdOptGenericBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszGenericBootImage)
2379{
2380 RTERRINFOSTATIC ErrInfo;
2381 uint32_t offError;
2382 RTVFSFILE hVfsFile;
2383 int rc = RTVfsChainOpenFile(pszGenericBootImage, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
2384 &offError, RTErrInfoInitStatic(&ErrInfo));
2385 if (RT_FAILURE(rc))
2386 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszGenericBootImage, rc, offError, &ErrInfo.Core);
2387
2388 uint8_t abBuf[_32K];
2389 size_t cbRead;
2390 rc = RTVfsFileReadAt(hVfsFile, 0, abBuf, sizeof(abBuf), &cbRead);
2391 RTVfsFileRelease(hVfsFile);
2392 if (RT_FAILURE(rc))
2393 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error reading 32KB from generic boot image '%s': %Rrc", pszGenericBootImage, rc);
2394
2395 rc = RTFsIsoMakerSetSysAreaContent(pOpts->hIsoMaker, abBuf, cbRead, 0);
2396 if (RT_FAILURE(rc))
2397 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerSetSysAreaContent failed with a %zu bytes input: %Rrc", cbRead, rc);
2398
2399 return VINF_SUCCESS;
2400}
2401
2402
2403/**
2404 * Helper that makes sure we've got a validation boot entry.
2405 *
2406 * @returns IPRT status code
2407 * @param pOpts The ISO maker command instance.
2408 */
2409static void rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(PRTFSISOMAKERCMDOPTS pOpts)
2410{
2411 if (pOpts->cBootCatEntries == 0)
2412 {
2413 pOpts->aBootCatEntries[0].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation;
2414 pOpts->aBootCatEntries[0].u.Validation.idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
2415 pOpts->aBootCatEntries[0].u.Validation.pszString = NULL;
2416 pOpts->cBootCatEntries = 1;
2417 }
2418}
2419
2420
2421/**
2422 * Helper that makes sure we've got a current boot entry.
2423 *
2424 * @returns IPRT status code
2425 * @param pOpts The ISO maker command instance.
2426 * @param fForceNew Whether to force a new entry.
2427 * @param pidxBootCat Where to return the boot catalog index.
2428 */
2429static int rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(PRTFSISOMAKERCMDOPTS pOpts, bool fForceNew, uint32_t *pidxBootCat)
2430{
2431 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2432
2433 uint32_t i = pOpts->cBootCatEntries;
2434 if (i == 2 && fForceNew)
2435 {
2436 pOpts->aBootCatEntries[i].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
2437 pOpts->aBootCatEntries[i].u.SectionHeader.idPlatform = pOpts->aBootCatEntries[0].u.Validation.idPlatform;
2438 pOpts->aBootCatEntries[i].u.SectionHeader.pszString = NULL;
2439 pOpts->cBootCatEntries = ++i;
2440 }
2441
2442 if ( i == 1
2443 || fForceNew
2444 || pOpts->aBootCatEntries[i - 1].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2445 {
2446 if (i >= RT_ELEMENTS(pOpts->aBootCatEntries))
2447 {
2448 *pidxBootCat = UINT32_MAX;
2449 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
2450 }
2451
2452 pOpts->aBootCatEntries[i].enmType = i == 1 ? RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
2453 : RTFSISOMKCMDELTORITOENTRY::kEntryType_Section;
2454 pOpts->aBootCatEntries[i].u.Section.pszImageNameInIso = NULL;
2455 pOpts->aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
2456 pOpts->aBootCatEntries[i].u.Section.fInsertBootInfoTable = false;
2457 pOpts->aBootCatEntries[i].u.Section.fBootable = true;
2458 pOpts->aBootCatEntries[i].u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK;
2459 pOpts->aBootCatEntries[i].u.Section.bSystemType = 1 /*FAT12*/;
2460 pOpts->aBootCatEntries[i].u.Section.uLoadSeg = 0x7c0;
2461 pOpts->aBootCatEntries[i].u.Section.cSectorsToLoad = 4;
2462 pOpts->cBootCatEntries = ++i;
2463 }
2464
2465 *pidxBootCat = i - 1;
2466 return VINF_SUCCESS;
2467}
2468
2469
2470/**
2471 * Deals with: --boot-catalog <path-spec>
2472 *
2473 * This enters the boot catalog into the namespaces of the image. The path-spec
2474 * is similar to what rtFsIsoMakerCmdAddSomething processes, only there isn't a
2475 * source file part.
2476 *
2477 * @returns IPRT status code
2478 * @param pOpts The ISO maker command instance.
2479 * @param pszGenericBootImage The generic boot image source.
2480 */
2481static int rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootCat)
2482{
2483 /* Make sure we'll fail later if no other boot options are present. */
2484 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2485
2486 /* Parse the name spec. */
2487 RTFSISOMKCMDPARSEDNAMES Parsed;
2488 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootCat, false /*fWithSrc*/, &Parsed);
2489 if (RT_SUCCESS(rc))
2490 {
2491 /* Query/create the boot catalog and enter it into the name spaces. */
2492 uint32_t idxBootCatObj;
2493 rc = RTFsIsoMakerQueryObjIdxForBootCatalog(pOpts->hIsoMaker, &idxBootCatObj);
2494 if (RT_SUCCESS(rc))
2495 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxBootCatObj, &Parsed, "boot catalog");
2496 else
2497 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerQueryBootCatalogPathObjIdx failed: %Rrc", rc);
2498 }
2499 return rc;
2500}
2501
2502
2503/**
2504 * Deals with: --eltorito-add-image {file-spec}
2505 *
2506 * This differs from -b|--eltorito-boot in that it takes a source file
2507 * specification identical to what rtFsIsoMakerCmdAddSomething processes instead
2508 * of a reference to a file in the image.
2509 *
2510 * This operates on the current eltorito boot catalog entry.
2511 *
2512 * @returns IPRT status code
2513 * @param pOpts The ISO maker command instance.
2514 * @param pszGenericBootImage The generic boot image source.
2515 */
2516static int rtFsIsoMakerCmdOptEltoritoAddImage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImageSpec)
2517{
2518 /* Parse the name spec. */
2519 RTFSISOMKCMDPARSEDNAMES Parsed;
2520 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootImageSpec, true /*fWithSrc*/, &Parsed);
2521 if (RT_SUCCESS(rc))
2522 {
2523 uint32_t idxBootCat;
2524 rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2525 if (RT_SUCCESS(rc))
2526 {
2527 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
2528 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
2529 rc = rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
2530 else
2531 {
2532 uint32_t idxImageObj;
2533 rc = rtFsIsoMakerCmdStatAndAddFile(pOpts, Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath, &Parsed, &idxImageObj);
2534 if (RT_SUCCESS(rc))
2535 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
2536 }
2537 }
2538 }
2539
2540 return rc;
2541}
2542
2543
2544/**
2545 * Deals with: -b|--eltorito-boot {file-in-iso}
2546 *
2547 * This operates on the current eltorito boot catalog entry.
2548 *
2549 * @returns IPRT status code
2550 * @param pOpts The ISO maker command instance.
2551 * @param pszGenericBootImage The generic boot image source.
2552 */
2553static int rtFsIsoMakerCmdOptEltoritoBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImage)
2554{
2555 uint32_t idxBootCat;
2556 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2557 if (RT_SUCCESS(rc))
2558 {
2559 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
2560 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
2561 return rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
2562
2563 uint32_t idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
2564 if (idxImageObj == UINT32_MAX)
2565 pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso = pszBootImage;
2566 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
2567 }
2568 return rc;
2569}
2570
2571
2572/**
2573 * Deals with: --eltorito-platform-id {x86|PPC|Mac|efi|number}
2574 *
2575 * Operates on the validation entry or a section header.
2576 *
2577 * @returns IPRT status code
2578 * @param pOpts The ISO maker command instance.
2579 * @param pszPlatformId The platform ID.
2580 */
2581static int rtFsIsoMakerCmdOptEltoritoPlatformId(PRTFSISOMAKERCMDOPTS pOpts, const char *pszPlatformId)
2582{
2583 /* Decode it. */
2584 uint8_t idPlatform;
2585 if (strcmp(pszPlatformId, "x86") == 0)
2586 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
2587 else if (strcmp(pszPlatformId, "PPC") == 0)
2588 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_PPC;
2589 else if (strcmp(pszPlatformId, "Mac") == 0)
2590 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_MAC;
2591 else if (strcmp(pszPlatformId, "efi") == 0)
2592 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_EFI;
2593 else
2594 {
2595 int rc = RTStrToUInt8Full(pszPlatformId, 0, &idPlatform);
2596 if (rc != VINF_SUCCESS)
2597 return rtFsIsoMakerCmdSyntaxError(pOpts, "invalid or unknown platform ID: %s", pszPlatformId);
2598 }
2599
2600 /* If this option comes before anything related to the default entry, work
2601 on the validation entry. */
2602 if (pOpts->cBootCatEntries <= 1)
2603 {
2604 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2605 pOpts->aBootCatEntries[0].u.Validation.idPlatform = idPlatform;
2606 }
2607 /* Otherwise, work on the current section header, creating a new one if necessary. */
2608 else
2609 {
2610 uint32_t idxBootCat = pOpts->cBootCatEntries - 1;
2611 if (pOpts->aBootCatEntries[idxBootCat].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2612 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
2613 else
2614 {
2615 idxBootCat++;
2616 if (idxBootCat + 2 > RT_ELEMENTS(pOpts->aBootCatEntries))
2617 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
2618
2619 pOpts->aBootCatEntries[idxBootCat].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
2620 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
2621 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.pszString = NULL;
2622 pOpts->cBootCatEntries = idxBootCat + 1;
2623 }
2624 }
2625 return VINF_SUCCESS;
2626}
2627
2628
2629/**
2630 * Deals with: -no-boot
2631 *
2632 * This operates on the current eltorito boot catalog entry.
2633 *
2634 * @returns IPRT status code
2635 * @param pOpts The ISO maker command instance.
2636 */
2637static int rtFsIsoMakerCmdOptEltoritoSetNotBootable(PRTFSISOMAKERCMDOPTS pOpts)
2638{
2639 uint32_t idxBootCat;
2640 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2641 if (RT_SUCCESS(rc))
2642 pOpts->aBootCatEntries[idxBootCat].u.Section.fBootable = false;
2643 return rc;
2644}
2645
2646
2647/**
2648 * Deals with: -hard-disk-boot, -no-emulation-boot, --eltorito-floppy-12,
2649 * --eltorito-floppy-144, --eltorito-floppy-288
2650 *
2651 * This operates on the current eltorito boot catalog entry.
2652 *
2653 * @returns IPRT status code
2654 * @param pOpts The ISO maker command instance.
2655 * @param bMediaType The media type.
2656 */
2657static int rtFsIsoMakerCmdOptEltoritoSetMediaType(PRTFSISOMAKERCMDOPTS pOpts, uint8_t bMediaType)
2658{
2659 uint32_t idxBootCat;
2660 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2661 if (RT_SUCCESS(rc))
2662 pOpts->aBootCatEntries[idxBootCat].u.Section.bBootMediaType = bMediaType;
2663 return rc;
2664}
2665
2666
2667/**
2668 * Deals with: -boot-load-seg {seg}
2669 *
2670 * This operates on the current eltorito boot catalog entry.
2671 *
2672 * @returns IPRT status code
2673 * @param pOpts The ISO maker command instance.
2674 * @param uSeg The load segment.
2675 */
2676static int rtFsIsoMakerCmdOptEltoritoSetLoadSegment(PRTFSISOMAKERCMDOPTS pOpts, uint16_t uSeg)
2677{
2678 uint32_t idxBootCat;
2679 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2680 if (RT_SUCCESS(rc))
2681 pOpts->aBootCatEntries[idxBootCat].u.Section.uLoadSeg = uSeg;
2682 return rc;
2683}
2684
2685
2686/**
2687 * Deals with: -boot-load-size {sectors}
2688 *
2689 * This operates on the current eltorito boot catalog entry.
2690 *
2691 * @returns IPRT status code
2692 * @param pOpts The ISO maker command instance.
2693 * @param cSectors Number of emulated sectors to load
2694 */
2695static int rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(PRTFSISOMAKERCMDOPTS pOpts, uint16_t cSectors)
2696{
2697 uint32_t idxBootCat;
2698 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2699 if (RT_SUCCESS(rc))
2700 pOpts->aBootCatEntries[idxBootCat].u.Section.cSectorsToLoad = cSectors;
2701 return rc;
2702}
2703
2704
2705/**
2706 * Deals with: -boot-info-table
2707 *
2708 * This operates on the current eltorito boot catalog entry.
2709 *
2710 * @returns IPRT status code
2711 * @param pOpts The ISO maker command instance.
2712 */
2713static int rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(PRTFSISOMAKERCMDOPTS pOpts)
2714{
2715 uint32_t idxBootCat;
2716 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2717 if (RT_SUCCESS(rc))
2718 pOpts->aBootCatEntries[idxBootCat].u.Section.fInsertBootInfoTable = true;
2719 return rc;
2720}
2721
2722
2723/**
2724 * Validates and commits the boot catalog stuff.
2725 *
2726 * ASSUMING this is called after all options are parsed and there is only this
2727 * one call.
2728 *
2729 * @returns IPRT status code
2730 * @param pOpts The ISO maker command instance.
2731 */
2732static int rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(PRTFSISOMAKERCMDOPTS pOpts)
2733{
2734 if (pOpts->cBootCatEntries == 0)
2735 return VINF_SUCCESS;
2736
2737 /*
2738 * Locate and configure the boot images first.
2739 */
2740 int rc;
2741 PRTFSISOMKCMDELTORITOENTRY pBootCatEntry = &pOpts->aBootCatEntries[1];
2742 for (uint32_t idxBootCat = 1; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2743 if ( pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
2744 || pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Section)
2745 {
2746 /* Make sure we've got a boot image. */
2747 uint32_t idxImageObj = pBootCatEntry->u.Section.idxImageObj;
2748 if (idxImageObj == UINT32_MAX)
2749 {
2750 const char *pszBootImage = pBootCatEntry->u.Section.pszImageNameInIso;
2751 if (pszBootImage == NULL)
2752 return rtFsIsoMakerCmdSyntaxError(pOpts, "No image name given for boot catalog entry #%u", idxBootCat);
2753
2754 idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
2755 if (idxImageObj == UINT32_MAX)
2756 return rtFsIsoMakerCmdSyntaxError(pOpts, "Unable to locate image for boot catalog entry #%u: %s",
2757 idxBootCat, pszBootImage);
2758 pBootCatEntry->u.Section.idxImageObj = idxImageObj;
2759 }
2760
2761 /* Enable patching it? */
2762 if (pBootCatEntry->u.Section.fInsertBootInfoTable)
2763 {
2764 rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pOpts->hIsoMaker, idxImageObj, true);
2765 if (RT_FAILURE(rc))
2766 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2767 "RTFsIsoMakerObjEnableBootInfoTablePatching failed on entry #%u: %Rrc",
2768 idxBootCat, rc);
2769 }
2770
2771 /* Figure out the floppy type given the object size. */
2772 if (pBootCatEntry->u.Section.bBootMediaType == ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
2773 {
2774 uint64_t cbImage;
2775 rc = RTFsIsoMakerObjQueryDataSize(pOpts->hIsoMaker, idxImageObj, &cbImage);
2776 if (RT_FAILURE(rc))
2777 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerObjGetDataSize failed on entry #%u: %Rrc",
2778 idxBootCat, rc);
2779 if (cbImage == 1228800)
2780 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB;
2781 else if (cbImage <= 1474560)
2782 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB;
2783 else if (cbImage <= 2949120)
2784 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB;
2785 else
2786 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK;
2787 }
2788 }
2789
2790 /*
2791 * Add the boot catalog entries.
2792 */
2793 pBootCatEntry = &pOpts->aBootCatEntries[0];
2794 for (uint32_t idxBootCat = 0; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2795 switch (pBootCatEntry->enmType)
2796 {
2797 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation:
2798 Assert(idxBootCat == 0);
2799 rc = RTFsIsoMakerBootCatSetValidationEntry(pOpts->hIsoMaker, pBootCatEntry->u.Validation.idPlatform,
2800 pBootCatEntry->u.Validation.pszString);
2801 if (RT_FAILURE(rc))
2802 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetValidationEntry failed: %Rrc", rc);
2803 break;
2804
2805 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Default:
2806 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Section:
2807 Assert(pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default ? idxBootCat == 1 : idxBootCat > 2);
2808 rc = RTFsIsoMakerBootCatSetSectionEntry(pOpts->hIsoMaker, idxBootCat,
2809 pBootCatEntry->u.Section.idxImageObj,
2810 pBootCatEntry->u.Section.bBootMediaType,
2811 pBootCatEntry->u.Section.bSystemType,
2812 pBootCatEntry->u.Section.fBootable,
2813 pBootCatEntry->u.Section.uLoadSeg,
2814 pBootCatEntry->u.Section.cSectorsToLoad,
2815 ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE, NULL, 0);
2816 if (RT_FAILURE(rc))
2817 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetSectionEntry failed on entry #%u: %Rrc",
2818 idxBootCat, rc);
2819 break;
2820
2821 case RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader:
2822 {
2823 uint32_t cEntries = 1;
2824 while ( idxBootCat + cEntries < pOpts->cBootCatEntries
2825 && pBootCatEntry[cEntries].enmType != RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2826 cEntries++;
2827 cEntries--;
2828
2829 Assert(idxBootCat > 1);
2830 rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pOpts->hIsoMaker, idxBootCat, cEntries,
2831 pBootCatEntry->u.SectionHeader.idPlatform,
2832 pBootCatEntry->u.SectionHeader.pszString);
2833 if (RT_FAILURE(rc))
2834 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2835 "RTFsIsoMakerBootCatSetSectionHeaderEntry failed on entry #%u: %Rrc",
2836 idxBootCat, rc);
2837 break;
2838 }
2839
2840 default:
2841 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
2842 }
2843
2844 return VINF_SUCCESS;
2845}
2846
2847
2848/**
2849 * Deals with: --eltorito-new-entry, --eltorito-alt-boot
2850 *
2851 * This operates on the current eltorito boot catalog entry.
2852 *
2853 * @returns IPRT status code
2854 * @param pOpts The ISO maker command instance.
2855 */
2856static int rtFsIsoMakerCmdOptEltoritoNewEntry(PRTFSISOMAKERCMDOPTS pOpts)
2857{
2858 uint32_t idxBootCat;
2859 return rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, true /*fForceNew*/, &idxBootCat);
2860}
2861
2862
2863/**
2864 * Sets a string property in all namespaces.
2865 *
2866 * @returns IPRT status code.
2867 * @param pOpts The ISO maker command instance.
2868 * @param pszValue The new string value.
2869 * @param enmStringProp The string property.
2870 */
2871static int rtFsIsoMakerCmdOptSetStringProp(PRTFSISOMAKERCMDOPTS pOpts, const char *pszValue, RTFSISOMAKERSTRINGPROP enmStringProp)
2872{
2873 int rc = RTFsIsoMakerSetStringProp(pOpts->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_ALL, pszValue);
2874 if (RT_FAILURE(rc))
2875 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set string property %d to '%s': %Rrc", enmStringProp, pszValue, rc);
2876 return rc;
2877}
2878
2879
2880/**
2881 * Handles the --dir-mode and --file-mode options.
2882 *
2883 * @returns IPRT status code.
2884 * @param pOpts The ISO maker command instance.
2885 * @param fDir True if applies to dir, false if applies to
2886 * files.
2887 * @param fMode The forced mode.
2888 */
2889static int rtFsIsoMakerCmdOptSetFileOrDirMode(PRTFSISOMAKERCMDOPTS pOpts, bool fDir, RTFMODE fMode)
2890{
2891 /* Change the mode masks. */
2892 int rc;
2893 if (fDir)
2894 rc = RTFsIsoMakerSetForcedDirMode(pOpts->hIsoMaker, fMode, true /*fForced*/);
2895 else
2896 rc = RTFsIsoMakerSetForcedFileMode(pOpts->hIsoMaker, fMode, true /*fForced*/);
2897 if (RT_SUCCESS(rc))
2898 {
2899 /* Then enable rock.*/
2900 rc = RTFsIsoMakerSetRockRidgeLevel(pOpts->hIsoMaker, 2);
2901 if (RT_SUCCESS(rc))
2902 return VINF_SUCCESS;
2903 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to enable rock ridge: %Rrc", rc);
2904 }
2905 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set %s force & default mode mask to %04o: %Rrc",
2906 fMode, fDir ? "directory" : "file", rc);
2907}
2908
2909
2910/**
2911 * Handles the --no-dir-mode and --no-file-mode options that counters
2912 * --dir-mode and --file-mode.
2913 *
2914 * @returns IPRT status code.
2915 * @param pOpts The ISO maker command instance.
2916 * @param fDir True if applies to dir, false if applies to
2917 * files.
2918 */
2919static int rtFsIsoMakerCmdOptDisableFileOrDirMode(PRTFSISOMAKERCMDOPTS pOpts, bool fDir)
2920{
2921 int rc;
2922 if (fDir)
2923 rc = RTFsIsoMakerSetForcedDirMode(pOpts->hIsoMaker, 0, false /*fForced*/);
2924 else
2925 rc = RTFsIsoMakerSetForcedFileMode(pOpts->hIsoMaker, 0, true /*fForced*/);
2926 if (RT_SUCCESS(rc))
2927 return VINF_SUCCESS;
2928 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to disable forced %s mode mask: %Rrc", fDir ? "directory" : "file", rc);
2929}
2930
2931
2932
2933/**
2934 * Handles the --new-dir-mode option.
2935 *
2936 * @returns IPRT status code.
2937 * @param pOpts The ISO maker command instance.
2938 * @param fMode The forced mode.
2939 */
2940static int rtFsIsoMakerCmdOptSetNewDirMode(PRTFSISOMAKERCMDOPTS pOpts, RTFMODE fMode)
2941{
2942 int rc = RTFsIsoMakerSetDefaultDirMode(pOpts->hIsoMaker, fMode);
2943 if (RT_SUCCESS(rc))
2944 return VINF_SUCCESS;
2945 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set default dir mode mask to %04o: %Rrc", fMode, rc);
2946}
2947
2948
2949/**
2950 * Loads an argument file (e.g. a .iso-file) and parses it.
2951 *
2952 * @returns IPRT status code.
2953 * @param pOpts The ISO maker command instance.
2954 * @param pszFileSpec The file to parse.
2955 * @param cDepth The current nesting depth.
2956 */
2957static int rtFsIsoMakerCmdParseArgumentFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFileSpec, unsigned cDepth)
2958{
2959 if (cDepth > 2)
2960 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_INVALID_PARAMETER, "Too many nested argument files!");
2961
2962 /*
2963 * Read the file into memory.
2964 */
2965 RTERRINFOSTATIC ErrInfo;
2966 uint32_t offError;
2967 RTVFSFILE hVfsFile;
2968 int rc = RTVfsChainOpenFile(pszFileSpec, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
2969 &offError, RTErrInfoInitStatic(&ErrInfo));
2970 if (RT_FAILURE(rc))
2971 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszFileSpec, rc, offError, &ErrInfo.Core);
2972
2973 uint64_t cbFile = 0;
2974 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
2975 if (RT_SUCCESS(rc))
2976 {
2977 if (cbFile < _2M)
2978 {
2979 char *pszContent = (char *)RTMemTmpAllocZ((size_t)cbFile + 1);
2980 if (pszContent)
2981 {
2982 rc = RTVfsFileRead(hVfsFile, pszContent, (size_t)cbFile, NULL);
2983 if (RT_SUCCESS(rc))
2984 {
2985 /*
2986 * Check that it's valid UTF-8 and turn it into an argument vector.
2987 */
2988 rc = RTStrValidateEncodingEx(pszContent, (size_t)cbFile + 1,
2989 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
2990 if (RT_SUCCESS(rc))
2991 {
2992 uint32_t fGetOpt = strstr(pszContent, "--iprt-iso-maker-file-marker-ms") == NULL
2993 ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
2994 fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
2995 char **papszArgs;
2996 int cArgs;
2997 rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
2998 if (RT_SUCCESS(rc))
2999 {
3000 /*
3001 * Parse them.
3002 */
3003 rc = rtFsIsoMakerCmdParse(pOpts, cArgs, papszArgs, cDepth + 1);
3004
3005 RTGetOptArgvFreeEx(papszArgs, fGetOpt);
3006 }
3007 else
3008 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTGetOptArgvFromString failed: %Rrc", pszFileSpec, rc);
3009
3010 }
3011 else
3012 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: invalid encoding", pszFileSpec);
3013 }
3014 else
3015 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: error to read it into memory: %Rrc", pszFileSpec, rc);
3016 RTMemTmpFree(pszContent);
3017 }
3018 else
3019 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "%s: failed to allocte %zu bytes for reading",
3020 pszFileSpec, (size_t)cbFile + 1);
3021 }
3022 else
3023 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_FILE_TOO_BIG, "%s: file is too big: %'RU64 bytes, max 2MB", pszFileSpec, cbFile);
3024 }
3025 else
3026 rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTVfsFileGetSize failed: %Rrc", pszFileSpec, rc);
3027 RTVfsFileRelease(hVfsFile);
3028 return rc;
3029}
3030
3031
3032/**
3033 * Parses the given command line options.
3034 *
3035 * @returns IPRT status code.
3036 * @retval VINF_CALLBACK_RETURN if exit successfully (help, version).
3037 * @param pOpts The ISO maker command instance.
3038 * @param cArgs Number of arguments in papszArgs.
3039 * @param papszArgs The argument vector to parse.
3040 */
3041static int rtFsIsoMakerCmdParse(PRTFSISOMAKERCMDOPTS pOpts, unsigned cArgs, char **papszArgs, unsigned cDepth)
3042{
3043 /* Setup option parsing. */
3044 RTGETOPTSTATE GetState;
3045 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, g_aRtFsIsoMakerOptions, RT_ELEMENTS(g_aRtFsIsoMakerOptions),
3046 cDepth == 0 ? 1 : 0 /*iFirst*/, 0 /*fFlags*/);
3047 if (RT_FAILURE(rc))
3048 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTGetOpt failed: %Rrc", rc);
3049
3050 /*
3051 * Parse parameters. Parameters are position dependent.
3052 */
3053 RTGETOPTUNION ValueUnion;
3054 while ( RT_SUCCESS(rc)
3055 && (rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
3056 {
3057 switch (rc)
3058 {
3059 /*
3060 * Files and directories.
3061 */
3062 case VINF_GETOPT_NOT_OPTION:
3063 if ( *ValueUnion.psz != '@'
3064 || strchr(ValueUnion.psz, '='))
3065 rc = rtFsIsoMakerCmdAddSomething(pOpts, ValueUnion.psz);
3066 else
3067 rc = rtFsIsoMakerCmdParseArgumentFile(pOpts, ValueUnion.psz + 1, cDepth);
3068 break;
3069
3070 /*
3071 * Options specific to our ISO maker.
3072 */
3073 case RTFSISOMAKERCMD_OPT_NAME_SETUP:
3074 rc = rtFsIsoMakerCmdOptNameSetup(pOpts, ValueUnion.psz);
3075 break;
3076
3077 case RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER:
3078 /* ignored */
3079 break;
3080
3081 case RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE: /* --output-buffer-size {cb} */
3082 pOpts->cbOutputReadBuffer = ValueUnion.u32;
3083 break;
3084
3085 case RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE: /* --random-output-buffer-size */
3086 pOpts->fRandomOutputReadBufferSize = true;
3087 break;
3088
3089 case RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION: /* --random-order-verficiation {cb} */
3090 pOpts->cbRandomOrderVerifciationBlock = ValueUnion.u32;
3091 break;
3092
3093 case RTFSISOMAKERCMD_OPT_PUSH_ISO:
3094 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso", 0);
3095 break;
3096
3097 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_JOLIET:
3098 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-joliet", RTFSISO9660_F_NO_JOLIET);
3099 break;
3100
3101 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK:
3102 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-rock", RTFSISO9660_F_NO_ROCK);
3103 break;
3104
3105 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK_NO_JOLIET:
3106 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-rock-no-joliet",
3107 RTFSISO9660_F_NO_ROCK | RTFSISO9660_F_NO_JOLIET);
3108 break;
3109
3110 case RTFSISOMAKERCMD_OPT_POP:
3111 rc = rtFsIsoMakerCmdOptPop(pOpts);
3112 break;
3113
3114
3115 case RTFSISOMAKERCMD_OPT_IMPORT_ISO:
3116 rc = rtFsIsoMakerCmdOptImportIso(pOpts, ValueUnion.psz);
3117 break;
3118
3119 case RTFSISOMAKERCMD_OPT_NO_FILE_MODE:
3120 rc = rtFsIsoMakerCmdOptDisableFileOrDirMode(pOpts, false /*fDir*/);
3121 break;
3122 case RTFSISOMAKERCMD_OPT_NO_DIR_MODE:
3123 rc = rtFsIsoMakerCmdOptDisableFileOrDirMode(pOpts, true /*fDir*/);
3124 break;
3125
3126
3127 /*
3128 * Joliet related options.
3129 */
3130 case RTFSISOMAKERCMD_OPT_NO_JOLIET:
3131 rc = RTFsIsoMakerSetJolietUcs2Level(pOpts->hIsoMaker, 0);
3132 if (RT_FAILURE(rc))
3133 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to disable joliet: %Rrc", rc);
3134 break;
3135
3136
3137 /*
3138 * Boot related options.
3139 */
3140 case 'G': /* --generic-boot <file> */
3141 rc = rtFsIsoMakerCmdOptGenericBoot(pOpts, ValueUnion.psz);
3142 break;
3143
3144 case RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE:
3145 rc = rtFsIsoMakerCmdOptEltoritoAddImage(pOpts, ValueUnion.psz);
3146 break;
3147
3148 case 'b': /* --eltorito-boot <boot.img> */
3149 rc = rtFsIsoMakerCmdOptEltoritoBoot(pOpts, ValueUnion.psz);
3150 break;
3151
3152 case RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY:
3153 rc = rtFsIsoMakerCmdOptEltoritoNewEntry(pOpts);
3154 break;
3155
3156 case RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID:
3157 rc = rtFsIsoMakerCmdOptEltoritoPlatformId(pOpts, ValueUnion.psz);
3158 break;
3159
3160 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT:
3161 rc = rtFsIsoMakerCmdOptEltoritoSetNotBootable(pOpts);
3162 break;
3163
3164 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12:
3165 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB);
3166 break;
3167 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144:
3168 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB);
3169 break;
3170 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288:
3171 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB);
3172 break;
3173 case RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT:
3174 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK);
3175 break;
3176 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT:
3177 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION);
3178 break;
3179
3180 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG:
3181 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSegment(pOpts, ValueUnion.u16);
3182 break;
3183
3184 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE:
3185 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(pOpts, ValueUnion.u16);
3186 break;
3187
3188 case RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE:
3189 rc = rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(pOpts);
3190 break;
3191
3192 case 'c': /* --boot-catalog <cd-path> */
3193 rc = rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(pOpts, ValueUnion.psz);
3194 break;
3195
3196 /*
3197 * Image/namespace property related options.
3198 */
3199 case RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID:
3200 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
3201 break;
3202
3203 case 'A': /* --application-id */
3204 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
3205 break;
3206
3207 case RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID:
3208 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
3209 break;
3210
3211 case RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID:
3212 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
3213 break;
3214
3215 case 'P': /* -publisher */
3216 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
3217 break;
3218
3219 case 'p': /* --preparer*/
3220 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
3221 break;
3222
3223 case RTFSISOMAKERCMD_OPT_SYSTEM_ID:
3224 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
3225 break;
3226
3227 case RTFSISOMAKERCMD_OPT_VOLUME_ID: /* (should've been '-V') */
3228 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_ID);
3229 break;
3230
3231 case RTFSISOMAKERCMD_OPT_VOLUME_SET_ID:
3232 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
3233 break;
3234
3235 /*
3236 * Options compatible with other ISO makers.
3237 */
3238 case 'o':
3239 if (pOpts->fVirtualImageMaker)
3240 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is not allowed");
3241 if (pOpts->pszOutFile)
3242 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is specified more than once");
3243 pOpts->pszOutFile = ValueUnion.psz;
3244 break;
3245
3246 case RTFSISOMAKERCMD_OPT_DIR_MODE:
3247 rc = rtFsIsoMakerCmdOptSetFileOrDirMode(pOpts, true /*fDir*/, ValueUnion.u32);
3248 break;
3249
3250 case RTFSISOMAKERCMD_OPT_FILE_MODE:
3251 rc = rtFsIsoMakerCmdOptSetFileOrDirMode(pOpts, false /*fDir*/, ValueUnion.u32);
3252 break;
3253
3254 case RTFSISOMAKERCMD_OPT_NEW_DIR_MODE:
3255 rc = rtFsIsoMakerCmdOptSetNewDirMode(pOpts, ValueUnion.u32);
3256 break;
3257
3258 case RTFSISOMAKERCMD_OPT_GRAFT_POINTS:
3259 rc = rtFsIsoMakerCmdOptNameSetup(pOpts, "iso+joliet+udf+hfs");
3260 break;
3261
3262 case 'l':
3263 if (RTFsIsoMakerGetIso9660Level(pOpts->hIsoMaker) >= 2)
3264 break;
3265 ValueUnion.u8 = 2;
3266 /* fall thru */
3267 case RTFSISOMAKERCMD_OPT_ISO_LEVEL:
3268 rc = RTFsIsoMakerSetIso9660Level(pOpts->hIsoMaker, ValueUnion.u8);
3269 if (RT_FAILURE(rc))
3270 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set ISO level to %d: %Rrc", ValueUnion.u8, rc);
3271 break;
3272
3273
3274 /*
3275 * Standard bits.
3276 */
3277 case 'h':
3278 rtFsIsoMakerCmdUsage(pOpts, papszArgs[0]);
3279 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_CALLBACK_RETURN;
3280
3281 case 'V':
3282 rtFsIsoMakerPrintf(pOpts, "%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
3283 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_CALLBACK_RETURN;
3284
3285 default:
3286 if (rc > 0 && RT_C_IS_GRAPH(rc))
3287 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: -%c", rc);
3288 else if (rc > 0)
3289 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: %i (%#x)", rc, rc);
3290 else if (rc == VERR_GETOPT_UNKNOWN_OPTION)
3291 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Unknown option: '%s'", ValueUnion.psz);
3292 else if (ValueUnion.pDef)
3293 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: %Rrs", ValueUnion.pDef->pszLong, rc);
3294 else
3295 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%Rrs", rc);
3296 return rc;
3297 }
3298 if (RT_FAILURE(rc))
3299 return rc;
3300 }
3301 return VINF_SUCCESS;
3302}
3303
3304
3305/**
3306 * Extended ISO maker command.
3307 *
3308 * This can be used as a ISO maker command that produces a image file, or
3309 * alternatively for setting up a virtual ISO in memory.
3310 *
3311 * @returns IPRT status code
3312 * @param cArgs Number of arguments.
3313 * @param papszArgs Pointer to argument array.
3314 * @param phVfsFile Where to return the virtual ISO. Pass NULL to
3315 * for normal operation (creates file on disk).
3316 * @param pErrInfo Where to return extended error information in
3317 * the virtual ISO mode.
3318 */
3319RTDECL(int) RTFsIsoMakerCmdEx(unsigned cArgs, char **papszArgs, PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo)
3320{
3321 /*
3322 * Create instance.
3323 */
3324 RTFSISOMAKERCMDOPTS Opts;
3325 RT_ZERO(Opts);
3326 Opts.hIsoMaker = NIL_RTFSISOMAKER;
3327 Opts.pErrInfo = pErrInfo;
3328 Opts.fVirtualImageMaker = phVfsFile != NULL;
3329 Opts.hSrcVfs = NIL_RTVFS;
3330 Opts.cNameSpecifiers = 1;
3331 Opts.afNameSpecifiers[0] = RTFSISOMAKERCMDNAME_MAJOR_MASK;
3332 Opts.fDstNamespaces = RTFSISOMAKERCMDNAME_MAJOR_MASK;
3333 Opts.pszTransTbl = "TRANS.TBL"; /** @todo query this below */
3334 if (phVfsFile)
3335 *phVfsFile = NIL_RTVFSFILE;
3336 for (uint32_t i = 0; i < RT_ELEMENTS(Opts.aBootCatEntries); i++)
3337 Opts.aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
3338
3339 /* Create the ISO creator instance. */
3340 int rc = RTFsIsoMakerCreate(&Opts.hIsoMaker);
3341 if (RT_FAILURE(rc))
3342 return rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreate failed: %Rrc", rc);
3343
3344 /*
3345 * Parse the command line and check for mandatory options.
3346 */
3347 rc = rtFsIsoMakerCmdParse(&Opts, cArgs, papszArgs, 0);
3348 if (RT_SUCCESS(rc) && rc != VINF_CALLBACK_RETURN)
3349 {
3350 if (!Opts.cItemsAdded)
3351 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_NO_DATA, "Cowardly refuses to create empty ISO image");
3352 else if (!Opts.pszOutFile && !Opts.fVirtualImageMaker)
3353 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_INVALID_PARAMETER, "No output file specified (--output <file>)");
3354
3355 /*
3356 * Final actions.
3357 */
3358 if (RT_SUCCESS(rc))
3359 rc = rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(&Opts);
3360 if (RT_SUCCESS(rc))
3361 {
3362 /*
3363 * Finalize the image and get the virtual file.
3364 */
3365 rc = RTFsIsoMakerFinalize(Opts.hIsoMaker);
3366 if (RT_SUCCESS(rc))
3367 {
3368 RTVFSFILE hVfsFile;
3369 rc = RTFsIsoMakerCreateVfsOutputFile(Opts.hIsoMaker, &hVfsFile);
3370 if (RT_SUCCESS(rc))
3371 {
3372 /*
3373 * We're done now if we're only setting up a virtual image.
3374 */
3375 if (Opts.fVirtualImageMaker)
3376 *phVfsFile = hVfsFile;
3377 else
3378 {
3379 rc = rtFsIsoMakerCmdWriteImage(&Opts, hVfsFile);
3380 RTVfsFileRelease(hVfsFile);
3381 }
3382 }
3383 else
3384 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreateVfsOutputFile failed: %Rrc", rc);
3385 }
3386 else
3387 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerFinalize failed: %Rrc", rc);
3388 }
3389 }
3390
3391 return rtFsIsoMakerCmdDeleteState(&Opts, rc);
3392}
3393
3394
3395/**
3396 * ISO maker command (creates image file on disk).
3397 *
3398 * @returns IPRT status code
3399 * @param cArgs Number of arguments.
3400 * @param papszArgs Pointer to argument array.
3401 */
3402RTDECL(RTEXITCODE) RTFsIsoMakerCmd(unsigned cArgs, char **papszArgs)
3403{
3404 int rc = RTFsIsoMakerCmdEx(cArgs, papszArgs, NULL, NULL);
3405 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
3406}
3407
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