VirtualBox

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

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

isomaker: Adding --push-iso / --pop options for adding select files and/or directories from another ISO. Implemented adding directories from VFSs (real directories are not yet implemented). [build fix]

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