VirtualBox

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

Last change on this file since 67867 was 67867, 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).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 136.9 KB
Line 
1/* $Id: isomakercmd.cpp 67867 2017-07-08 18:22:12Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker Command.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsisomaker.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/buildconfig.h>
38#include <iprt/ctype.h>
39#include <iprt/file.h>
40#include <iprt/fsvfs.h>
41#include <iprt/err.h>
42#include <iprt/getopt.h>
43#include <iprt/log.h>
44#include <iprt/mem.h>
45#include <iprt/message.h>
46#include <iprt/path.h>
47#include <iprt/rand.h>
48#include <iprt/stream.h>
49#include <iprt/string.h>
50#include <iprt/vfs.h>
51#include <iprt/formats/iso9660.h>
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57/** Maximum number of name specifiers we allow. */
58#define RTFSISOMAKERCMD_MAX_NAMES 8
59
60/** Maximum directory recursions when adding a directory tree. */
61#define RTFSISOMAKERCMD_MAX_DIR_RECURSIONS 32
62
63/** @name Name specifiers
64 * @{ */
65#define RTFSISOMAKERCMDNAME_PRIMARY_ISO RTFSISOMAKER_NAMESPACE_ISO_9660
66#define RTFSISOMAKERCMDNAME_JOLIET RTFSISOMAKER_NAMESPACE_JOLIET
67#define RTFSISOMAKERCMDNAME_UDF RTFSISOMAKER_NAMESPACE_UDF
68#define RTFSISOMAKERCMDNAME_HFS RTFSISOMAKER_NAMESPACE_HFS
69
70#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE RT_BIT_32(16)
71#define RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE RT_BIT_32(17)
72
73#define RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL RT_BIT_32(20)
74#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL RT_BIT_32(21)
75#define RTFSISOMAKERCMDNAME_UDF_TRANS_TBL RT_BIT_32(22)
76#define RTFSISOMAKERCMDNAME_HFS_TRANS_TBL RT_BIT_32(23)
77
78#define RTFSISOMAKERCMDNAME_MAJOR_MASK \
79 (RTFSISOMAKERCMDNAME_PRIMARY_ISO | RTFSISOMAKERCMDNAME_JOLIET | RTFSISOMAKERCMDNAME_UDF | RTFSISOMAKERCMDNAME_HFS)
80
81#define RTFSISOMAKERCMDNAME_MINOR_MASK \
82 ( RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE | RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL \
83 | RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE | RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL \
84 | RTFSISOMAKERCMDNAME_UDF_TRANS_TBL \
85 | RTFSISOMAKERCMDNAME_HFS_TRANS_TBL)
86AssertCompile((RTFSISOMAKERCMDNAME_MAJOR_MASK & RTFSISOMAKERCMDNAME_MINOR_MASK) == 0);
87/** @} */
88
89
90/*********************************************************************************************************************************
91* Structures and Typedefs *
92*********************************************************************************************************************************/
93typedef enum RTFSISOMAKERCMDOPT
94{
95 RTFSISOMAKERCMD_OPT_FIRST = 1000,
96
97 RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER,
98 RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE,
99 RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE,
100 RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION,
101 RTFSISOMAKERCMD_OPT_NAME_SETUP,
102 RTFSISOMAKERCMD_OPT_NO_JOLIET,
103 RTFSISOMAKERCMD_OPT_IMPORT_ISO,
104 RTFSISOMAKERCMD_OPT_PUSH_ISO,
105 RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_JOLIET,
106 RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK,
107 RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK_NO_JOLIET,
108 RTFSISOMAKERCMD_OPT_POP,
109
110 RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY,
111 RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE,
112 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12,
113 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144,
114 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288,
115
116 /*
117 * Compatibility options:
118 */
119 RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID,
120 RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS,
121 RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE,
122 RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE,
123 RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT,
124 RTFSISOMAKERCMD_OPT_ALPHA_BOOT,
125 RTFSISOMAKERCMD_OPT_APPLE,
126 RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID,
127 RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES,
128 RTFSISOMAKERCMD_OPT_CHECK_SESSION,
129 RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID,
130 RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS,
131 RTFSISOMAKERCMD_OPT_DIR_MODE,
132 RTFSISOMAKERCMD_OPT_DVD_VIDEO,
133 RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID,
134 RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT,
135 RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE,
136 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG,
137 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE,
138 RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT,
139 RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT,
140 RTFSISOMAKERCMD_OPT_EXCLUDE_LIST,
141 RTFSISOMAKERCMD_OPT_FILE_MODE,
142 RTFSISOMAKERCMD_OPT_FORCE_RR,
143 RTFSISOMAKERCMD_OPT_GID,
144 RTFSISOMAKERCMD_OPT_GRAFT_POINTS,
145 RTFSISOMAKERCMD_OPT_GUI,
146 RTFSISOMAKERCMD_OPT_HFS_AUTO,
147 RTFSISOMAKERCMD_OPT_HFS_BLESS,
148 RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE,
149 RTFSISOMAKERCMD_OPT_HFS_CAP,
150 RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT,
151 RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE,
152 RTFSISOMAKERCMD_OPT_HFS_CREATOR,
153 RTFSISOMAKERCMD_OPT_HFS_DAVE,
154 RTFSISOMAKERCMD_OPT_HFS_DOUBLE,
155 RTFSISOMAKERCMD_OPT_HFS_ENABLE,
156 RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE,
157 RTFSISOMAKERCMD_OPT_HFS_EXCHANGE,
158 RTFSISOMAKERCMD_OPT_HFS_HIDE,
159 RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST,
160 RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION,
161 RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET,
162 RTFSISOMAKERCMD_OPT_HFS_MAC_NAME,
163 RTFSISOMAKERCMD_OPT_HFS_MACBIN,
164 RTFSISOMAKERCMD_OPT_HFS_MAGIC,
165 RTFSISOMAKERCMD_OPT_HFS_MAP,
166 RTFSISOMAKERCMD_OPT_HFS_NETATALK,
167 RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP,
168 RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE,
169 RTFSISOMAKERCMD_OPT_HFS_OSX_HFS,
170 RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET,
171 RTFSISOMAKERCMD_OPT_HFS_PARMS,
172 RTFSISOMAKERCMD_OPT_HFS_PART,
173 RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT,
174 RTFSISOMAKERCMD_OPT_HFS_PROBE,
175 RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO,
176 RTFSISOMAKERCMD_OPT_HFS_SFM,
177 RTFSISOMAKERCMD_OPT_HFS_SGI,
178 RTFSISOMAKERCMD_OPT_HFS_SINGLE,
179 RTFSISOMAKERCMD_OPT_HFS_TYPE,
180 RTFSISOMAKERCMD_OPT_HFS_UNLOCK,
181 RTFSISOMAKERCMD_OPT_HFS_USHARE,
182 RTFSISOMAKERCMD_OPT_HFS_VOL_ID,
183 RTFSISOMAKERCMD_OPT_HFS_XINET,
184 RTFSISOMAKERCMD_OPT_HIDDEN,
185 RTFSISOMAKERCMD_OPT_HIDDEN_LIST,
186 RTFSISOMAKERCMD_OPT_HIDE,
187 RTFSISOMAKERCMD_OPT_HIDE_JOLIET,
188 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST,
189 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL,
190 RTFSISOMAKERCMD_OPT_HIDE_LIST,
191 RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED,
192 RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER,
193 RTFSISOMAKERCMD_OPT_HPPA_CMDLINE,
194 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32,
195 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64,
196 RTFSISOMAKERCMD_OPT_HPPA_RAMDISK,
197 RTFSISOMAKERCMD_OPT_INPUT_CHARSET,
198 RTFSISOMAKERCMD_OPT_ISO_LEVEL,
199 RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS,
200 RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE,
201 RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5,
202 RTFSISOMAKERCMD_OPT_JIGDO_JIGDO,
203 RTFSISOMAKERCMD_OPT_JIGDO_MAP,
204 RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST,
205 RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE,
206 RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE,
207 RTFSISOMAKERCMD_OPT_JOLIET_CHARSET,
208 RTFSISOMAKERCMD_OPT_JOLIET_LEVEL,
209 RTFSISOMAKERCMD_OPT_JOLIET_LONG,
210 RTFSISOMAKERCMD_OPT_LOG_FILE,
211 RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES,
212 RTFSISOMAKERCMD_OPT_MIPS_BOOT,
213 RTFSISOMAKERCMD_OPT_MIPSEL_BOOT,
214 RTFSISOMAKERCMD_OPT_NEW_DIR_MODE,
215 RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES,
216 RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS,
217 RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE,
218 RTFSISOMAKERCMD_OPT_NO_PAD,
219 RTFSISOMAKERCMD_OPT_NO_RR,
220 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS,
221 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS,
222 RTFSISOMAKERCMD_OPT_OLD_ROOT,
223 RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET,
224 RTFSISOMAKERCMD_OPT_PAD,
225 RTFSISOMAKERCMD_OPT_PATH_LIST,
226 RTFSISOMAKERCMD_OPT_PRINT_SIZE,
227 RTFSISOMAKERCMD_OPT_QUIET,
228 RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES,
229 RTFSISOMAKERCMD_OPT_ROOT,
230 RTFSISOMAKERCMD_OPT_SORT,
231 RTFSISOMAKERCMD_OPT_SPARC_BOOT,
232 RTFSISOMAKERCMD_OPT_SPARC_LABEL,
233 RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT,
234 RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME,
235 RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE,
236 RTFSISOMAKERCMD_OPT_SUNX86_BOOT,
237 RTFSISOMAKERCMD_OPT_SUNX86_LABEL,
238 RTFSISOMAKERCMD_OPT_SYSTEM_ID,
239 RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME,
240 RTFSISOMAKERCMD_OPT_UDF,
241 RTFSISOMAKERCMD_OPT_UID,
242 RTFSISOMAKERCMD_OPT_USE_FILE_VERSION,
243 RTFSISOMAKERCMD_OPT_VOLUME_ID,
244 RTFSISOMAKERCMD_OPT_VOLUME_SET_ID,
245 RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO,
246 RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE,
247 RTFSISOMAKERCMD_OPT_END
248} RTFSISOMAKERCMDOPT;
249
250
251/**
252 * El Torito boot entry.
253 */
254typedef struct RTFSISOMKCMDELTORITOENTRY
255{
256 /** The type of this entry. */
257 enum
258 {
259 kEntryType_Invalid = 0,
260 kEntryType_Validation, /**< Same as kEntryType_SectionHeader, just hardcoded #0. */
261 kEntryType_SectionHeader,
262 kEntryType_Default, /**< Same as kEntryType_Section, just hardcoded #1. */
263 kEntryType_Section
264 } enmType;
265 /** Type specific data. */
266 union
267 {
268 struct
269 {
270 /** The platform ID (ISO9660_ELTORITO_PLATFORM_ID_XXX). */
271 uint8_t idPlatform;
272 /** Some string for the header. */
273 const char *pszString;
274 } Validation,
275 SectionHeader;
276 struct
277 {
278 /** The name of the boot image wihtin the ISO (-b option). */
279 const char *pszImageNameInIso;
280 /** The object ID of the image in the ISO. This is set to UINT32_MAX when
281 * pszImageNameInIso is used (i.e. -b option) and we've delayed everything
282 * boot related till after all files have been added to the image. */
283 uint32_t idxImageObj;
284 /** Whether to insert boot info table into the image. */
285 bool fInsertBootInfoTable;
286 /** Bootble or not. Possible to make BIOS set up emulation w/o booting it. */
287 bool fBootable;
288 /** The media type (ISO9660_ELTORITO_BOOT_MEDIA_TYPE_XXX). */
289 uint8_t bBootMediaType;
290 /** File system / partition type. */
291 uint8_t bSystemType;
292 /** Load address divided by 0x10. */
293 uint16_t uLoadSeg;
294 /** Number of sectors (512) to load. */
295 uint16_t cSectorsToLoad;
296 } Section,
297 Default;
298 } u;
299} RTFSISOMKCMDELTORITOENTRY;
300/** Pointer to an el torito boot entry. */
301typedef RTFSISOMKCMDELTORITOENTRY *PRTFSISOMKCMDELTORITOENTRY;
302
303/**
304 * ISO maker command options & state.
305 */
306typedef struct RTFSISOMAKERCMDOPTS
307{
308 /** The handle to the ISO maker. */
309 RTFSISOMAKER hIsoMaker;
310 /** Set if we're creating a virtual image maker, i.e. producing something
311 * that is going to be read from only and not written to disk. */
312 bool fVirtualImageMaker;
313 /** Extended error info. This is a stderr alternative for the
314 * fVirtualImageMaker case (stdout goes to LogRel). */
315 PRTERRINFO pErrInfo;
316
317 /** The output file.
318 * This is NULL when fVirtualImageMaker is set. */
319 const char *pszOutFile;
320 /** Special buffer size to use for testing the ISO maker code reading. */
321 uint32_t cbOutputReadBuffer;
322 /** Use random output read buffer size. cbOutputReadBuffer works as maximum
323 * when this is enabled. */
324 bool fRandomOutputReadBufferSize;
325 /** Do output verification, but do it in random order if non-zero. The
326 * values gives the block size to use. */
327 uint32_t cbRandomOrderVerifciationBlock;
328
329 /** The current source VFS, NIL_RTVFS if regular file system is used. */
330 RTVFS hSrcVfs;
331 /** The specifier for hSrcVfs (error messages). */
332 const char *pszSrcVfs;
333 /** The option for hSrcVfs. */
334 const char *pszSrcVfsOption;
335
336 /** @name Processing of inputs
337 * @{ */
338 /** The namespaces (RTFSISOMAKER_NAMESPACE_XXX) we're currently adding
339 * input to. */
340 uint32_t fDstNamespaces;
341 /** The number of name specifiers we're currently operating with. */
342 uint32_t cNameSpecifiers;
343 /** Name specifier configurations.
344 * For instance given "name0=name1=name2=name3=source-file" we will add
345 * source-file to the image with name0 as the name in the namespace and
346 * sub-name specified by aNameSpecifiers[0], name1 in aNameSpecifiers[1],
347 * and so on. This allows exact control over which names a file will
348 * have in each namespace (primary-iso, joliet, udf, hfs) and sub-namespace
349 * (rock-ridge, trans.tbl).
350 */
351 uint32_t afNameSpecifiers[RTFSISOMAKERCMD_MAX_NAMES];
352 /** @} */
353
354 /** @name Booting related options and state.
355 * @{ */
356 /** Number of boot catalog entries (aBootCatEntries). */
357 uint32_t cBootCatEntries;
358 /** Boot catalog entries. */
359 RTFSISOMKCMDELTORITOENTRY aBootCatEntries[64];
360 /** @} */
361
362 /** 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 * Enteres an object into the namespaces given the parent and single name.
1529 *
1530 * This is used by the directory tree recursion code.
1531 *
1532 * @returns IPRT status code.
1533 * @param pOpts The ISO maker command instance.
1534 * @param idxObj The configuration index of the object to be named.
1535 * @param idxParentObj The configuration index of the parent directory.
1536 * @param pszName The name of the object.
1537 * @param pParsed The parsed names - only fNameSpecifies used, no
1538 * names.
1539 * @param pszSrc The full source path for error reporting.
1540 */
1541static int rtFsIsoMakerCmdSetNameAndParent(PRTFSISOMAKERCMDOPTS pOpts, uint32_t idxObj, uint32_t idxParentObj,
1542 const char *pszName, PCRTFSISOMKCMDPARSEDNAMES pParsed, const char *pszSrc)
1543{
1544 int rc = VINF_SUCCESS;
1545 for (uint32_t i = 0; i < pParsed->cNames; i++)
1546 if (pParsed->aNames[i].cchPath > 0)
1547 {
1548 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1549 {
1550 rc = RTFsIsoMakerObjSetNameAndParent(pOpts->hIsoMaker, idxObj, idxParentObj,
1551 pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1552 pszName);
1553 if (RT_FAILURE(rc))
1554 {
1555 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting parent & name '%s' on '%s': %Rrc",
1556 pszName, pszSrc, rc);
1557 break;
1558 }
1559 }
1560 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MINOR_MASK)
1561 {
1562 /** @todo add APIs for this. */
1563 }
1564 }
1565 return rc;
1566}
1567
1568
1569/**
1570 * Adds a file.
1571 *
1572 * @returns IPRT status code.
1573 * @param pOpts The ISO maker command instance.
1574 * @param pszSrc The path to the source file.
1575 * @param pParsed The parsed names.
1576 * @param pidxObj Where to return the configuration index for the
1577 * added file. Optional.
1578 */
1579static int rtFsIsoMakerCmdAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1580 uint32_t *pidxObj)
1581{
1582 int rc;
1583 uint32_t idxObj = UINT32_MAX;
1584 if ( pOpts->hSrcVfs == NIL_RTVFS
1585 || RTVfsChainIsSpec(pszSrc))
1586 {
1587 rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(pOpts->hIsoMaker, pszSrc, &idxObj);
1588 if (RT_FAILURE(rc))
1589 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s': %Rrc", pszSrc, rc);
1590 }
1591 else
1592 {
1593 RTVFSFILE hVfsFileSrc;
1594 rc = RTVfsFileOpen(pOpts->hSrcVfs, pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSrc);
1595 if (RT_FAILURE(rc))
1596 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening '%s' (inside '%s'): %Rrc", pszSrc, pOpts->pszSrcVfs, rc);
1597
1598 rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(pOpts->hIsoMaker, hVfsFileSrc, &idxObj);
1599 RTVfsFileRelease(hVfsFileSrc);
1600 if (RT_FAILURE(rc))
1601 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s' (VFS): %Rrc", pszSrc, rc);
1602 }
1603
1604 pOpts->cItemsAdded++;
1605 if (pidxObj)
1606 *pidxObj = idxObj;
1607
1608 return rtFsIsoMakerCmdSetObjPaths(pOpts, idxObj, pParsed, pszSrc);
1609}
1610
1611static bool rtFsIsoMakerCmdIsFilteredOut(PRTFSISOMAKERCMDOPTS pOpts, const char *pszDir, const char *pszName, bool fIsDir)
1612{
1613 RT_NOREF(pOpts, pszDir, pszName, fIsDir);
1614 return false;
1615}
1616
1617/**
1618 * Adds a directory, from a VFS chain or real file system.
1619 *
1620 * @returns IPRT status code.
1621 * @param pOpts The ISO maker command instance.
1622 * @param pszSrc The path to the source directory.
1623 * @param pParsed The parsed names.
1624 */
1625static int rtFsIsoMakerCmdAddDir(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed)
1626{
1627 RT_NOREF(pParsed);
1628 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding directory '%s' failed: not implemented", pszSrc);
1629}
1630
1631
1632/**
1633 * Worker for rtFsIsoMakerCmdAddVfsDir that does the recursion.
1634 *
1635 * @returns IPRT status code.
1636 * @param pOpts The ISO maker command instance.
1637 * @param hVfsDir The directory to process.
1638 * @param idxDirObj The configuration index of the directory.
1639 * @param pszSrc Pointer to the source path buffer. RTPATH_MAX
1640 * in size. Okay to modify beyond @a cchSrc.
1641 * @param cchSrc Length of the path corresponding to @a hVfsDir.
1642 * @param fNamespaces Which ISO maker namespaces to add the names to.
1643 * @param cDepth Number of recursions. Used to deal with loopy
1644 * directories.
1645 */
1646static int rtFsIsoMakerCmdAddVfsDirRecursive(PRTFSISOMAKERCMDOPTS pOpts, RTVFSDIR hVfsDir, uint32_t idxDirObj,
1647 char *pszSrc, size_t cchSrc, uint32_t fNamespaces, uint8_t cDepth)
1648{
1649 /*
1650 * Check that we're not in too deep.
1651 */
1652 if (cDepth >= RTFSISOMAKERCMD_MAX_DIR_RECURSIONS)
1653 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE,
1654 "Recursive (VFS) dir add too deep (depth=%u): %.*s", cDepth, cchSrc, pszSrc);
1655 /*
1656 * Enumerate the directory.
1657 */
1658 int rc;
1659 size_t cbDirEntryAlloced = sizeof(RTDIRENTRYEX);
1660 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1661 if (pDirEntry)
1662 {
1663 for (;;)
1664 {
1665 /*
1666 * Read the next entry.
1667 */
1668 size_t cbDirEntry = cbDirEntryAlloced;
1669 rc = RTVfsDirReadEx(hVfsDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1670 if (RT_FAILURE(rc))
1671 {
1672 if (rc == VERR_NO_MORE_FILES)
1673 rc = VINF_SUCCESS;
1674 else if (rc == VERR_BUFFER_OVERFLOW)
1675 {
1676 RTMemTmpFree(pDirEntry);
1677 cbDirEntryAlloced = RT_ALIGN_Z(RT_MIN(cbDirEntry, cbDirEntryAlloced) + 64, 64);
1678 pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1679 if (pDirEntry)
1680 continue;
1681 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "Out of memory (direntry buffer)");
1682 }
1683 else
1684 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsDirReadEx failed on %.*s: %Rrc", cchSrc, pszSrc, rc);
1685 break;
1686 }
1687
1688 /* Ignore '.' and '..' entries. */
1689 if (RTDirEntryExIsStdDotLink(pDirEntry))
1690 continue;
1691
1692 /*
1693 * Process the entry.
1694 */
1695
1696 /* Update the name. */
1697 if (cchSrc + 1 + pDirEntry->cbName < RTPATH_MAX)
1698 {
1699 pszSrc[cchSrc] = '/'; /* VFS only groks unix slashes */
1700 memcpy(&pszSrc[cchSrc + 1], pDirEntry->szName, pDirEntry->cbName);
1701 pszSrc[cchSrc + 1 + pDirEntry->cbName] = '\0';
1702 }
1703 else
1704 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_FILENAME_TOO_LONG, "Filename is too long (depth %u): '%.*s/%s'",
1705 cDepth, cchSrc, pszSrc, pDirEntry->szName);
1706
1707 /* Okay? Check name filtering. */
1708 if ( RT_SUCCESS(rc)
1709 && !rtFsIsoMakerCmdIsFilteredOut(pOpts, pszSrc, pDirEntry->szName, RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode)))
1710 {
1711 /* Do type specific adding. */
1712 uint32_t idxObj = UINT32_MAX;
1713 if (RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
1714 {
1715 /*
1716 * Files are added with VFS file sources.
1717 * The ASSUMPTION is that we're working with a virtual file system
1718 * here and won't be wasting native file descriptors.
1719 */
1720 RTVFSFILE hVfsFileSrc;
1721 rc = RTVfsDirOpenFile(hVfsDir, pDirEntry->szName,
1722 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSrc);
1723 if (RT_SUCCESS(rc))
1724 {
1725 rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(pOpts->hIsoMaker, hVfsFileSrc, &idxObj);
1726 RTVfsFileRelease(hVfsFileSrc);
1727 if (RT_SUCCESS(rc))
1728 {
1729 pOpts->cItemsAdded++;
1730 rc = RTFsIsoMakerObjSetNameAndParent(pOpts->hIsoMaker, idxObj, idxDirObj, fNamespaces,
1731 pDirEntry->szName);
1732 if (RT_FAILURE(rc))
1733 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting parent & name on file '%s' to '%s': %Rrc",
1734 pszSrc, pDirEntry->szName, rc);
1735 }
1736 else
1737 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding file '%s' (VFS recursive): %Rrc", pszSrc, rc);
1738 }
1739 else
1740 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening file '%s' (VFS recursive): %Rrc", pszSrc, rc);
1741 }
1742 else if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
1743 {
1744 /*
1745 * Open and add the sub-directory.
1746 */
1747 RTVFSDIR hVfsSubDirSrc;
1748 rc = RTVfsDirOpenDir(hVfsDir, pDirEntry->szName, 0 /*fFlags*/, &hVfsSubDirSrc);
1749 if (RT_SUCCESS(rc))
1750 {
1751 rc = RTFsIsoMakerAddUnnamedDir(pOpts->hIsoMaker, &pDirEntry->Info, &idxObj);
1752 if (RT_SUCCESS(rc))
1753 {
1754 pOpts->cItemsAdded++;
1755 rc = RTFsIsoMakerObjSetNameAndParent(pOpts->hIsoMaker, idxObj, idxDirObj, fNamespaces,
1756 pDirEntry->szName);
1757 if (RT_SUCCESS(rc))
1758 /* Recurse into the sub-directory. */
1759 rc = rtFsIsoMakerCmdAddVfsDirRecursive(pOpts, hVfsSubDirSrc, idxObj, pszSrc,
1760 cchSrc + 1 + pDirEntry->cbName, fNamespaces, cDepth + 1);
1761 else
1762 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
1763 "Error setting parent & name on directory '%s' to '%s': %Rrc",
1764 pszSrc, pDirEntry->szName, rc);
1765 }
1766 else
1767 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding directory '%s' (VFS recursive): %Rrc", pszSrc, rc);
1768 RTVfsDirRelease(hVfsSubDirSrc);
1769 }
1770 else
1771 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening directory '%s' (VFS recursive): %Rrc", pszSrc, rc);
1772 }
1773 else if (RTFS_IS_SYMLINK(pDirEntry->Info.Attr.fMode))
1774 {
1775 /*
1776 * TODO: ISO FS symlink support.
1777 */
1778 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
1779 "Adding symlink '%s' failed: not yet implemented", pszSrc);
1780 }
1781 else
1782 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
1783 "Adding special file '%s' failed: not implemented", pszSrc);
1784 }
1785 if (RT_FAILURE(rc))
1786 break;
1787 }
1788
1789 RTMemTmpFree(pDirEntry);
1790 }
1791 else
1792 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "Out of memory! (direntry buffer)");
1793 return rc;
1794}
1795
1796
1797/**
1798 * Adds a directory, from the source VFS.
1799 *
1800 * @returns IPRT status code.
1801 * @param pOpts The ISO maker command instance.
1802 * @param pParsed The parsed names.
1803 * @param pidxObj Where to return the configuration index for the
1804 * added file. Optional.
1805 */
1806static int rtFsIsoMakerCmdAddVfsDir(PRTFSISOMAKERCMDOPTS pOpts, PCRTFSISOMKCMDPARSEDNAMES pParsed, PCRTFSOBJINFO pObjInfo)
1807{
1808 Assert(pParsed->cNames < pParsed->cNamesWithSrc);
1809
1810 /*
1811 * Open the directory.
1812 */
1813 char *pszDir = pParsed->aNames[pParsed->cNamesWithSrc - 1].szPath;
1814 RTPathChangeToUnixSlashes(pszDir, true /*fForce*/); /* VFS currently only understand unix slashes. */
1815 RTVFSDIR hVfsDirSrc;
1816 int rc = RTVfsDirOpen(pOpts->hSrcVfs, pszDir, 0 /*fFlags*/, &hVfsDirSrc);
1817 if (RT_FAILURE(rc))
1818 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening directory '%s' (inside '%s'): %Rrc",
1819 pszDir, pOpts->pszSrcVfs, rc);
1820
1821 /*
1822 * Add the directory if it doesn't exist.
1823 */
1824 uint32_t idxObj = UINT32_MAX;
1825 for (uint32_t i = 0; i < pParsed->cNames; i++)
1826 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1827 {
1828 idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
1829 pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1830 pParsed->aNames[i].szPath);
1831 if (idxObj != UINT32_MAX)
1832 {
1833 /** @todo make sure the directory is present in the other namespace. */
1834 break;
1835 }
1836 }
1837 if (idxObj == UINT32_MAX)
1838 {
1839 rc = RTFsIsoMakerAddUnnamedDir(pOpts->hIsoMaker, pObjInfo, &idxObj);
1840 if (RT_SUCCESS(rc))
1841 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxObj, pParsed, pParsed->aNames[pParsed->cNames - 1].szPath);
1842 else
1843 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerAddUnnamedDir failed: %Rrc", rc);
1844 }
1845 if (RT_SUCCESS(rc))
1846 {
1847 /*
1848 * Add the directory content.
1849 */
1850 uint32_t fNamespaces = 0;
1851 for (uint32_t i = 0; i < pParsed->cNames; i++)
1852 fNamespaces |= pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK;
1853 rc = rtFsIsoMakerCmdAddVfsDirRecursive(pOpts, hVfsDirSrc, idxObj, pszDir,
1854 pParsed->aNames[pParsed->cNamesWithSrc - 1].cchPath, fNamespaces, 0 /*cDepth*/);
1855 }
1856 RTVfsDirRelease(hVfsDirSrc);
1857 return rc;
1858}
1859
1860
1861
1862
1863/**
1864 * Adds a file after first making sure it's a file.
1865 *
1866 * @returns IPRT status code
1867 * @param pOpts The ISO maker command instance.
1868 * @param pszSrc The path to the source file.
1869 * @param pParsed The parsed names.
1870 * @param pidxObj Where to return the configuration index for the
1871 * added file. Optional.
1872 */
1873static int rtFsIsoMakerCmdStatAndAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1874 uint32_t *pidxObj)
1875{
1876 int rc;
1877 RTFSOBJINFO ObjInfo;
1878 if ( pOpts->hSrcVfs == NIL_RTVFS
1879 || RTVfsChainIsSpec(pszSrc))
1880 {
1881 uint32_t offError;
1882 RTERRINFOSTATIC ErrInfo;
1883 rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
1884 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
1885 if (RT_FAILURE(rc))
1886 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
1887 }
1888 else
1889 {
1890 rc = RTVfsQueryPathInfo(pOpts->hSrcVfs, pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
1891 if (RT_FAILURE(rc))
1892 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsQueryPathInfo failed on %s (inside %s): %Rrc",
1893 pszSrc, pOpts->pszSrcVfs, rc);
1894 }
1895
1896 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1897 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, pParsed, pidxObj);
1898 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_A_FILE, "Not a file: %s", pszSrc);
1899}
1900
1901
1902/**
1903 * Processes a non-option argument.
1904 *
1905 * @returns IPRT status code.
1906 * @param pOpts The ISO maker command instance.
1907 * @param pszSpec The specification of what to add.
1908 */
1909static int rtFsIsoMakerCmdAddSomething(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
1910{
1911 /*
1912 * Parse the name spec.
1913 */
1914 RTFSISOMKCMDPARSEDNAMES Parsed;
1915 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszSpec, true /*fWithSrc*/, &Parsed);
1916 if (RT_FAILURE(rc))
1917 return rc;
1918
1919 /*
1920 * Deal with special source filenames used to remove/change stuff.
1921 */
1922 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_Remove
1923 || Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove)
1924 {
1925 const char *pszFirstNm = NULL;
1926 uint32_t cRemoved = 0;
1927 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
1928 if ( Parsed.aNames[i].cchPath > 0
1929 && (Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK))
1930 {
1931 pszFirstNm = Parsed.aNames[i].szPath;
1932 uint32_t idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
1933 Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1934 Parsed.aNames[i].szPath);
1935 if (idxObj != UINT32_MAX)
1936 {
1937 rc = RTFsIsoMakerObjRemove(pOpts->hIsoMaker, idxObj);
1938 if (RT_FAILURE(rc))
1939 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to remove '%s': %Rrc", pszSpec, rc);
1940 cRemoved++;
1941 }
1942 }
1943 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove
1944 && cRemoved == 0)
1945 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "Failed to locate '%s' for removal", pszSpec);
1946 }
1947 /*
1948 * Add regular source.
1949 */
1950 else
1951 {
1952 const char *pszSrc = Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath;
1953 RTFSOBJINFO ObjInfo;
1954 if ( pOpts->hSrcVfs == NIL_RTVFS
1955 || RTVfsChainIsSpec(pszSrc))
1956 {
1957 uint32_t offError;
1958 RTERRINFOSTATIC ErrInfo;
1959 rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
1960 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
1961 if (RT_FAILURE(rc))
1962 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
1963 }
1964 else
1965 {
1966 rc = RTVfsQueryPathInfo(pOpts->hSrcVfs, pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
1967 if (RT_FAILURE(rc))
1968 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsQueryPathInfo failed on %s (in %s): %Rrc",
1969 pszSrc, pOpts->pszSrcVfs, rc);
1970 }
1971
1972 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1973 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, &Parsed, NULL /*pidxObj*/);
1974
1975 if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1976 {
1977 if ( pOpts->hSrcVfs == NIL_RTVFS
1978 || RTVfsChainIsSpec(pszSrc))
1979 return rtFsIsoMakerCmdAddDir(pOpts, pszSrc, &Parsed);
1980 return rtFsIsoMakerCmdAddVfsDir(pOpts, &Parsed, &ObjInfo);
1981 }
1982
1983 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
1984 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding symlink '%s' failed: not yet implemented", pszSpec);
1985
1986 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding special file '%s' failed: not implemented", pszSpec);
1987 }
1988
1989 return VINF_SUCCESS;
1990}
1991
1992
1993/**
1994 * Opens an ISO and use it for subsequent file system accesses.
1995 *
1996 * This is handy for duplicating a part of an ISO in the new image.
1997 *
1998 * @returns IPRT status code.
1999 * @param pOpts The ISO maker command instance.
2000 * @param pszIsoSpec The ISO path specifier.
2001 * @param pszOption The option we're being called on.
2002 * @param fFlags RTFSISO9660_F_XXX
2003 */
2004static int rtFsIsoMakerCmdOptPushIso(PRTFSISOMAKERCMDOPTS pOpts, const char *pszIsoSpec, const char *pszOption, uint32_t fFlags)
2005{
2006 if (pOpts->hSrcVfs != NIL_RTVFS)
2007 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
2008 "Nested %s usage is not supported (previous: %s %s)",
2009 pszOption, pOpts->pszSrcVfsOption, pOpts->pszSrcVfs);
2010
2011 /*
2012 * Try open the file.
2013 */
2014 RTVFSFILE hVfsFileIso;
2015 uint32_t offError;
2016 RTERRINFOSTATIC ErrInfo;
2017 int rc = RTVfsChainOpenFile(pszIsoSpec, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE,
2018 &hVfsFileIso, &offError, RTErrInfoInitStatic(&ErrInfo));
2019 if (RT_SUCCESS(rc))
2020 {
2021 RTVFS hSrcVfs;
2022 rc = RTFsIso9660VolOpen(hVfsFileIso, fFlags, &hSrcVfs, RTErrInfoInitStatic(&ErrInfo));
2023 RTVfsFileRelease(hVfsFileIso);
2024 if (RT_SUCCESS(rc))
2025 {
2026 pOpts->hSrcVfs = hSrcVfs;
2027 pOpts->pszSrcVfs = pszIsoSpec;
2028 pOpts->pszSrcVfsOption = pszOption;
2029 return VINF_SUCCESS;
2030 }
2031
2032 if (RTErrInfoIsSet(&ErrInfo.Core))
2033 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to open '%s' as ISO FS: %Rrc - %s",
2034 pszIsoSpec, rc, ErrInfo.Core.pszMsg);
2035 else
2036 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to open '%s' as ISO FS: %Rrc", pszIsoSpec, rc);
2037 }
2038 else
2039 rc = rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszIsoSpec, rc, offError, &ErrInfo.Core);
2040 return rc;
2041}
2042
2043
2044/**
2045 * Counter part to --push-iso and friends.
2046 *
2047 * @returns IPRT status code.
2048 * @param pOpts The ISO maker command instance.
2049 */
2050static int rtFsIsoMakerCmdOptPop(PRTFSISOMAKERCMDOPTS pOpts)
2051{
2052 if (pOpts->hSrcVfs != NIL_RTVFS)
2053 {
2054 RTVfsRelease(pOpts->hSrcVfs);
2055 pOpts->hSrcVfs = NIL_RTVFS;
2056 pOpts->pszSrcVfs = NULL;
2057 pOpts->pszSrcVfsOption = NULL;
2058 return VINF_SUCCESS;
2059 }
2060 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "--pop without --push-xxx");
2061}
2062
2063
2064/**
2065 * Deals with the --import-iso {iso-file-spec} options.
2066 *
2067 * @returns IPRT status code
2068 * @param pOpts The ISO maker command instance.
2069 * @param pszIsoSpec The ISO path specifier.
2070 */
2071static int rtFsIsoMakerCmdOptImportIso(PRTFSISOMAKERCMDOPTS pOpts, const char *pszIsoSpec)
2072{
2073 RTFSISOMAKERIMPORTRESULTS Results;
2074 RTERRINFOSTATIC ErrInfo;
2075 int rc = RTFsIsoMakerImport(pOpts->hIsoMaker, pszIsoSpec, 0 /*fFlags*/, &Results, RTErrInfoInitStatic(&ErrInfo));
2076
2077 pOpts->cItemsAdded += Results.cAddedFiles;
2078 pOpts->cItemsAdded += Results.cAddedSymlinks;
2079 pOpts->cItemsAdded += Results.cAddedDirs;
2080 pOpts->cItemsAdded += Results.cBootCatEntries != UINT32_MAX ? Results.cBootCatEntries : 0;
2081 pOpts->cItemsAdded += Results.cbSysArea != 0 ? 1 : 0;
2082
2083 rtFsIsoMakerPrintf(pOpts, "ISO imported statistics for '%s'\n", pszIsoSpec);
2084 rtFsIsoMakerPrintf(pOpts, " cAddedNames: %'14RU32\n", Results.cAddedNames);
2085 rtFsIsoMakerPrintf(pOpts, " cAddedDirs: %'14RU32\n", Results.cAddedDirs);
2086 rtFsIsoMakerPrintf(pOpts, " cbAddedDataBlocks: %'14RU64 bytes\n", Results.cbAddedDataBlocks);
2087 rtFsIsoMakerPrintf(pOpts, " cAddedFiles: %'14RU32\n", Results.cAddedFiles);
2088 rtFsIsoMakerPrintf(pOpts, " cAddedSymlinks: %'14RU32\n", Results.cAddedSymlinks);
2089 if (Results.cBootCatEntries == UINT32_MAX)
2090 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: none\n");
2091 else
2092 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: %'14RU32\n", Results.cBootCatEntries);
2093 rtFsIsoMakerPrintf(pOpts, " cbSysArea: %'14RU32\n", Results.cbSysArea);
2094 rtFsIsoMakerPrintf(pOpts, " cErrors: %'14RU32\n", Results.cErrors);
2095
2096 if (RT_SUCCESS(rc))
2097 return rc;
2098 if (Results.offError != UINT32_MAX)
2099 return rtFsIsoMakerCmdChainError(pOpts, "RTFsIsoMakerImport", pszIsoSpec, rc, Results.offError, &ErrInfo.Core);
2100 if (RTErrInfoIsSet(&ErrInfo.Core))
2101 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc - %s", rc, ErrInfo.Core.pszMsg);
2102 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc", rc);
2103}
2104
2105
2106/**
2107 * Deals with: -G|--generic-boot {file}
2108 *
2109 * This concers content the first 16 sectors of the image. We start loading the
2110 * file at byte 0 in the image and stops at 32KB.
2111 *
2112 * @returns IPRT status code
2113 * @param pOpts The ISO maker command instance.
2114 * @param pszGenericBootImage The generic boot image source.
2115 */
2116static int rtFsIsoMakerCmdOptGenericBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszGenericBootImage)
2117{
2118 RTERRINFOSTATIC ErrInfo;
2119 uint32_t offError;
2120 RTVFSFILE hVfsFile;
2121 int rc = RTVfsChainOpenFile(pszGenericBootImage, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
2122 &offError, RTErrInfoInitStatic(&ErrInfo));
2123 if (RT_FAILURE(rc))
2124 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszGenericBootImage, rc, offError, &ErrInfo.Core);
2125
2126 uint8_t abBuf[_32K];
2127 size_t cbRead;
2128 rc = RTVfsFileReadAt(hVfsFile, 0, abBuf, sizeof(abBuf), &cbRead);
2129 RTVfsFileRelease(hVfsFile);
2130 if (RT_FAILURE(rc))
2131 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error reading 32KB from generic boot image '%s': %Rrc", pszGenericBootImage, rc);
2132
2133 rc = RTFsIsoMakerSetSysAreaContent(pOpts->hIsoMaker, abBuf, cbRead, 0);
2134 if (RT_FAILURE(rc))
2135 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerSetSysAreaContent failed with a %zu bytes input: %Rrc", cbRead, rc);
2136
2137 return VINF_SUCCESS;
2138}
2139
2140
2141/**
2142 * Helper that makes sure we've got a validation boot entry.
2143 *
2144 * @returns IPRT status code
2145 * @param pOpts The ISO maker command instance.
2146 */
2147static void rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(PRTFSISOMAKERCMDOPTS pOpts)
2148{
2149 if (pOpts->cBootCatEntries == 0)
2150 {
2151 pOpts->aBootCatEntries[0].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation;
2152 pOpts->aBootCatEntries[0].u.Validation.idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
2153 pOpts->aBootCatEntries[0].u.Validation.pszString = NULL;
2154 pOpts->cBootCatEntries = 1;
2155 }
2156}
2157
2158
2159/**
2160 * Helper that makes sure we've got a current boot entry.
2161 *
2162 * @returns IPRT status code
2163 * @param pOpts The ISO maker command instance.
2164 * @param fForceNew Whether to force a new entry.
2165 * @param pidxBootCat Where to return the boot catalog index.
2166 */
2167static int rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(PRTFSISOMAKERCMDOPTS pOpts, bool fForceNew, uint32_t *pidxBootCat)
2168{
2169 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2170
2171 uint32_t i = pOpts->cBootCatEntries;
2172 if (i == 2 && fForceNew)
2173 {
2174 pOpts->aBootCatEntries[i].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
2175 pOpts->aBootCatEntries[i].u.SectionHeader.idPlatform = pOpts->aBootCatEntries[0].u.Validation.idPlatform;
2176 pOpts->aBootCatEntries[i].u.SectionHeader.pszString = NULL;
2177 pOpts->cBootCatEntries = ++i;
2178 }
2179
2180 if ( i == 1
2181 || fForceNew
2182 || pOpts->aBootCatEntries[i - 1].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2183 {
2184 if (i >= RT_ELEMENTS(pOpts->aBootCatEntries))
2185 {
2186 *pidxBootCat = UINT32_MAX;
2187 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
2188 }
2189
2190 pOpts->aBootCatEntries[i].enmType = i == 1 ? RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
2191 : RTFSISOMKCMDELTORITOENTRY::kEntryType_Section;
2192 pOpts->aBootCatEntries[i].u.Section.pszImageNameInIso = NULL;
2193 pOpts->aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
2194 pOpts->aBootCatEntries[i].u.Section.fInsertBootInfoTable = false;
2195 pOpts->aBootCatEntries[i].u.Section.fBootable = true;
2196 pOpts->aBootCatEntries[i].u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK;
2197 pOpts->aBootCatEntries[i].u.Section.bSystemType = 1 /*FAT12*/;
2198 pOpts->aBootCatEntries[i].u.Section.uLoadSeg = 0x7c0;
2199 pOpts->aBootCatEntries[i].u.Section.cSectorsToLoad = 4;
2200 pOpts->cBootCatEntries = ++i;
2201 }
2202
2203 *pidxBootCat = i - 1;
2204 return VINF_SUCCESS;
2205}
2206
2207
2208/**
2209 * Deals with: --boot-catalog <path-spec>
2210 *
2211 * This enters the boot catalog into the namespaces of the image. The path-spec
2212 * is similar to what rtFsIsoMakerCmdAddSomething processes, only there isn't a
2213 * source file part.
2214 *
2215 * @returns IPRT status code
2216 * @param pOpts The ISO maker command instance.
2217 * @param pszGenericBootImage The generic boot image source.
2218 */
2219static int rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootCat)
2220{
2221 /* Make sure we'll fail later if no other boot options are present. */
2222 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2223
2224 /* Parse the name spec. */
2225 RTFSISOMKCMDPARSEDNAMES Parsed;
2226 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootCat, false /*fWithSrc*/, &Parsed);
2227 if (RT_SUCCESS(rc))
2228 {
2229 /* Query/create the boot catalog and enter it into the name spaces. */
2230 uint32_t idxBootCatObj;
2231 rc = RTFsIsoMakerQueryObjIdxForBootCatalog(pOpts->hIsoMaker, &idxBootCatObj);
2232 if (RT_SUCCESS(rc))
2233 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxBootCatObj, &Parsed, "boot catalog");
2234 else
2235 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerQueryBootCatalogPathObjIdx failed: %Rrc", rc);
2236 }
2237 return rc;
2238}
2239
2240
2241/**
2242 * Deals with: --eltorito-add-image {file-spec}
2243 *
2244 * This differs from -b|--eltorito-boot in that it takes a source file
2245 * specification identical to what rtFsIsoMakerCmdAddSomething processes instead
2246 * of a reference to a file in the image.
2247 *
2248 * This operates on the current eltorito boot catalog entry.
2249 *
2250 * @returns IPRT status code
2251 * @param pOpts The ISO maker command instance.
2252 * @param pszGenericBootImage The generic boot image source.
2253 */
2254static int rtFsIsoMakerCmdOptEltoritoAddImage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImageSpec)
2255{
2256 /* Parse the name spec. */
2257 RTFSISOMKCMDPARSEDNAMES Parsed;
2258 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootImageSpec, true /*fWithSrc*/, &Parsed);
2259 if (RT_SUCCESS(rc))
2260 {
2261 uint32_t idxBootCat;
2262 rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2263 if (RT_SUCCESS(rc))
2264 {
2265 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
2266 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
2267 rc = rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
2268 else
2269 {
2270 uint32_t idxImageObj;
2271 rc = rtFsIsoMakerCmdStatAndAddFile(pOpts, Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath, &Parsed, &idxImageObj);
2272 if (RT_SUCCESS(rc))
2273 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
2274 }
2275 }
2276 }
2277
2278 return rc;
2279}
2280
2281
2282/**
2283 * Deals with: -b|--eltorito-boot {file-in-iso}
2284 *
2285 * This operates on the current eltorito boot catalog entry.
2286 *
2287 * @returns IPRT status code
2288 * @param pOpts The ISO maker command instance.
2289 * @param pszGenericBootImage The generic boot image source.
2290 */
2291static int rtFsIsoMakerCmdOptEltoritoBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImage)
2292{
2293 uint32_t idxBootCat;
2294 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2295 if (RT_SUCCESS(rc))
2296 {
2297 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
2298 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
2299 return rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
2300
2301 uint32_t idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
2302 if (idxImageObj == UINT32_MAX)
2303 pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso = pszBootImage;
2304 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
2305 }
2306 return rc;
2307}
2308
2309
2310/**
2311 * Deals with: --eltorito-platform-id {x86|PPC|Mac|efi|number}
2312 *
2313 * Operates on the validation entry or a section header.
2314 *
2315 * @returns IPRT status code
2316 * @param pOpts The ISO maker command instance.
2317 * @param pszPlatformId The platform ID.
2318 */
2319static int rtFsIsoMakerCmdOptEltoritoPlatformId(PRTFSISOMAKERCMDOPTS pOpts, const char *pszPlatformId)
2320{
2321 /* Decode it. */
2322 uint8_t idPlatform;
2323 if (strcmp(pszPlatformId, "x86") == 0)
2324 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
2325 else if (strcmp(pszPlatformId, "PPC") == 0)
2326 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_PPC;
2327 else if (strcmp(pszPlatformId, "Mac") == 0)
2328 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_MAC;
2329 else if (strcmp(pszPlatformId, "efi") == 0)
2330 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_EFI;
2331 else
2332 {
2333 int rc = RTStrToUInt8Full(pszPlatformId, 0, &idPlatform);
2334 if (rc != VINF_SUCCESS)
2335 return rtFsIsoMakerCmdSyntaxError(pOpts, "invalid or unknown platform ID: %s", pszPlatformId);
2336 }
2337
2338 /* If this option comes before anything related to the default entry, work
2339 on the validation entry. */
2340 if (pOpts->cBootCatEntries <= 1)
2341 {
2342 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2343 pOpts->aBootCatEntries[0].u.Validation.idPlatform = idPlatform;
2344 }
2345 /* Otherwise, work on the current section header, creating a new one if necessary. */
2346 else
2347 {
2348 uint32_t idxBootCat = pOpts->cBootCatEntries - 1;
2349 if (pOpts->aBootCatEntries[idxBootCat].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2350 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
2351 else
2352 {
2353 idxBootCat++;
2354 if (idxBootCat + 2 > RT_ELEMENTS(pOpts->aBootCatEntries))
2355 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
2356
2357 pOpts->aBootCatEntries[idxBootCat].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
2358 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
2359 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.pszString = NULL;
2360 pOpts->cBootCatEntries = idxBootCat + 1;
2361 }
2362 }
2363 return VINF_SUCCESS;
2364}
2365
2366
2367/**
2368 * Deals with: -no-boot
2369 *
2370 * This operates on the current eltorito boot catalog entry.
2371 *
2372 * @returns IPRT status code
2373 * @param pOpts The ISO maker command instance.
2374 */
2375static int rtFsIsoMakerCmdOptEltoritoSetNotBootable(PRTFSISOMAKERCMDOPTS pOpts)
2376{
2377 uint32_t idxBootCat;
2378 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2379 if (RT_SUCCESS(rc))
2380 pOpts->aBootCatEntries[idxBootCat].u.Section.fBootable = false;
2381 return rc;
2382}
2383
2384
2385/**
2386 * Deals with: -hard-disk-boot, -no-emulation-boot, --eltorito-floppy-12,
2387 * --eltorito-floppy-144, --eltorito-floppy-288
2388 *
2389 * This operates on the current eltorito boot catalog entry.
2390 *
2391 * @returns IPRT status code
2392 * @param pOpts The ISO maker command instance.
2393 * @param bMediaType The media type.
2394 */
2395static int rtFsIsoMakerCmdOptEltoritoSetMediaType(PRTFSISOMAKERCMDOPTS pOpts, uint8_t bMediaType)
2396{
2397 uint32_t idxBootCat;
2398 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2399 if (RT_SUCCESS(rc))
2400 pOpts->aBootCatEntries[idxBootCat].u.Section.bBootMediaType = bMediaType;
2401 return rc;
2402}
2403
2404
2405/**
2406 * Deals with: -boot-load-seg {seg}
2407 *
2408 * This operates on the current eltorito boot catalog entry.
2409 *
2410 * @returns IPRT status code
2411 * @param pOpts The ISO maker command instance.
2412 * @param uSeg The load segment.
2413 */
2414static int rtFsIsoMakerCmdOptEltoritoSetLoadSegment(PRTFSISOMAKERCMDOPTS pOpts, uint16_t uSeg)
2415{
2416 uint32_t idxBootCat;
2417 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2418 if (RT_SUCCESS(rc))
2419 pOpts->aBootCatEntries[idxBootCat].u.Section.uLoadSeg = uSeg;
2420 return rc;
2421}
2422
2423
2424/**
2425 * Deals with: -boot-load-size {sectors}
2426 *
2427 * This operates on the current eltorito boot catalog entry.
2428 *
2429 * @returns IPRT status code
2430 * @param pOpts The ISO maker command instance.
2431 * @param cSectors Number of emulated sectors to load
2432 */
2433static int rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(PRTFSISOMAKERCMDOPTS pOpts, uint16_t cSectors)
2434{
2435 uint32_t idxBootCat;
2436 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2437 if (RT_SUCCESS(rc))
2438 pOpts->aBootCatEntries[idxBootCat].u.Section.cSectorsToLoad = cSectors;
2439 return rc;
2440}
2441
2442
2443/**
2444 * Deals with: -boot-info-table
2445 *
2446 * This operates on the current eltorito boot catalog entry.
2447 *
2448 * @returns IPRT status code
2449 * @param pOpts The ISO maker command instance.
2450 */
2451static int rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(PRTFSISOMAKERCMDOPTS pOpts)
2452{
2453 uint32_t idxBootCat;
2454 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2455 if (RT_SUCCESS(rc))
2456 pOpts->aBootCatEntries[idxBootCat].u.Section.fInsertBootInfoTable = true;
2457 return rc;
2458}
2459
2460
2461/**
2462 * Validates and commits the boot catalog stuff.
2463 *
2464 * ASSUMING this is called after all options are parsed and there is only this
2465 * one call.
2466 *
2467 * @returns IPRT status code
2468 * @param pOpts The ISO maker command instance.
2469 */
2470static int rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(PRTFSISOMAKERCMDOPTS pOpts)
2471{
2472 if (pOpts->cBootCatEntries == 0)
2473 return VINF_SUCCESS;
2474
2475 /*
2476 * Locate and configure the boot images first.
2477 */
2478 int rc;
2479 PRTFSISOMKCMDELTORITOENTRY pBootCatEntry = &pOpts->aBootCatEntries[1];
2480 for (uint32_t idxBootCat = 1; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2481 if ( pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
2482 || pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Section)
2483 {
2484 /* Make sure we've got a boot image. */
2485 uint32_t idxImageObj = pBootCatEntry->u.Section.idxImageObj;
2486 if (idxImageObj == UINT32_MAX)
2487 {
2488 const char *pszBootImage = pBootCatEntry->u.Section.pszImageNameInIso;
2489 if (pszBootImage == NULL)
2490 return rtFsIsoMakerCmdSyntaxError(pOpts, "No image name given for boot catalog entry #%u", idxBootCat);
2491
2492 idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
2493 if (idxImageObj == UINT32_MAX)
2494 return rtFsIsoMakerCmdSyntaxError(pOpts, "Unable to locate image for boot catalog entry #%u: %s",
2495 idxBootCat, pszBootImage);
2496 pBootCatEntry->u.Section.idxImageObj = idxImageObj;
2497 }
2498
2499 /* Enable patching it? */
2500 if (pBootCatEntry->u.Section.fInsertBootInfoTable)
2501 {
2502 rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pOpts->hIsoMaker, idxImageObj, true);
2503 if (RT_FAILURE(rc))
2504 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2505 "RTFsIsoMakerObjEnableBootInfoTablePatching failed on entry #%u: %Rrc",
2506 idxBootCat, rc);
2507 }
2508
2509 /* Figure out the floppy type given the object size. */
2510 if (pBootCatEntry->u.Section.bBootMediaType == ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
2511 {
2512 uint64_t cbImage;
2513 rc = RTFsIsoMakerObjQueryDataSize(pOpts->hIsoMaker, idxImageObj, &cbImage);
2514 if (RT_FAILURE(rc))
2515 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerObjGetDataSize failed on entry #%u: %Rrc",
2516 idxBootCat, rc);
2517 if (cbImage == 1228800)
2518 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB;
2519 else if (cbImage <= 1474560)
2520 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB;
2521 else if (cbImage <= 2949120)
2522 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB;
2523 else
2524 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK;
2525 }
2526 }
2527
2528 /*
2529 * Add the boot catalog entries.
2530 */
2531 pBootCatEntry = &pOpts->aBootCatEntries[0];
2532 for (uint32_t idxBootCat = 0; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2533 switch (pBootCatEntry->enmType)
2534 {
2535 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation:
2536 Assert(idxBootCat == 0);
2537 rc = RTFsIsoMakerBootCatSetValidationEntry(pOpts->hIsoMaker, pBootCatEntry->u.Validation.idPlatform,
2538 pBootCatEntry->u.Validation.pszString);
2539 if (RT_FAILURE(rc))
2540 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetValidationEntry failed: %Rrc", rc);
2541 break;
2542
2543 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Default:
2544 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Section:
2545 Assert(pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default ? idxBootCat == 1 : idxBootCat > 2);
2546 rc = RTFsIsoMakerBootCatSetSectionEntry(pOpts->hIsoMaker, idxBootCat,
2547 pBootCatEntry->u.Section.idxImageObj,
2548 pBootCatEntry->u.Section.bBootMediaType,
2549 pBootCatEntry->u.Section.bSystemType,
2550 pBootCatEntry->u.Section.fBootable,
2551 pBootCatEntry->u.Section.uLoadSeg,
2552 pBootCatEntry->u.Section.cSectorsToLoad,
2553 ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE, NULL, 0);
2554 if (RT_FAILURE(rc))
2555 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetSectionEntry failed on entry #%u: %Rrc",
2556 idxBootCat, rc);
2557 break;
2558
2559 case RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader:
2560 {
2561 uint32_t cEntries = 1;
2562 while ( idxBootCat + cEntries < pOpts->cBootCatEntries
2563 && pBootCatEntry[cEntries].enmType != RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2564 cEntries++;
2565 cEntries--;
2566
2567 Assert(idxBootCat > 1);
2568 rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pOpts->hIsoMaker, idxBootCat, cEntries,
2569 pBootCatEntry->u.SectionHeader.idPlatform,
2570 pBootCatEntry->u.SectionHeader.pszString);
2571 if (RT_FAILURE(rc))
2572 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2573 "RTFsIsoMakerBootCatSetSectionHeaderEntry failed on entry #%u: %Rrc",
2574 idxBootCat, rc);
2575 break;
2576 }
2577
2578 default:
2579 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
2580 }
2581
2582 return VINF_SUCCESS;
2583}
2584
2585
2586/**
2587 * Deals with: --eltorito-new-entry, --eltorito-alt-boot
2588 *
2589 * This operates on the current eltorito boot catalog entry.
2590 *
2591 * @returns IPRT status code
2592 * @param pOpts The ISO maker command instance.
2593 */
2594static int rtFsIsoMakerCmdOptEltoritoNewEntry(PRTFSISOMAKERCMDOPTS pOpts)
2595{
2596 uint32_t idxBootCat;
2597 return rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, true /*fForceNew*/, &idxBootCat);
2598}
2599
2600
2601/**
2602 * Sets a string property in all namespaces.
2603 *
2604 * @returns IPRT status code.
2605 * @param pOpts The ISO maker command instance.
2606 * @param pszValue The new string value.
2607 * @param enmStringProp The string property.
2608 */
2609static int rtFsIsoMakerCmdOptSetStringProp(PRTFSISOMAKERCMDOPTS pOpts, const char *pszValue, RTFSISOMAKERSTRINGPROP enmStringProp)
2610{
2611 int rc = RTFsIsoMakerSetStringProp(pOpts->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_ALL, pszValue);
2612 if (RT_FAILURE(rc))
2613 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set string property %d to '%s': %Rrc", enmStringProp, pszValue, rc);
2614 return rc;
2615}
2616
2617
2618/**
2619 * Loads an argument file (e.g. a .iso-file) and parses it.
2620 *
2621 * @returns IPRT status code.
2622 * @param pOpts The ISO maker command instance.
2623 * @param pszFileSpec The file to parse.
2624 * @param cDepth The current nesting depth.
2625 */
2626static int rtFsIsoMakerCmdParseArgumentFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFileSpec, unsigned cDepth)
2627{
2628 if (cDepth > 2)
2629 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_INVALID_PARAMETER, "Too many nested argument files!");
2630
2631 /*
2632 * Read the file into memory.
2633 */
2634 RTERRINFOSTATIC ErrInfo;
2635 uint32_t offError;
2636 RTVFSFILE hVfsFile;
2637 int rc = RTVfsChainOpenFile(pszFileSpec, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
2638 &offError, RTErrInfoInitStatic(&ErrInfo));
2639 if (RT_FAILURE(rc))
2640 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszFileSpec, rc, offError, &ErrInfo.Core);
2641
2642 uint64_t cbFile = 0;
2643 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
2644 if (RT_SUCCESS(rc))
2645 {
2646 if (cbFile < _2M)
2647 {
2648 char *pszContent = (char *)RTMemTmpAllocZ((size_t)cbFile + 1);
2649 if (pszContent)
2650 {
2651 rc = RTVfsFileRead(hVfsFile, pszContent, (size_t)cbFile, NULL);
2652 if (RT_SUCCESS(rc))
2653 {
2654 /*
2655 * Check that it's valid UTF-8 and turn it into an argument vector.
2656 */
2657 rc = RTStrValidateEncodingEx(pszContent, (size_t)cbFile + 1,
2658 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
2659 if (RT_SUCCESS(rc))
2660 {
2661 uint32_t fGetOpt = strstr(pszContent, "--iprt-iso-maker-file-marker-ms") == NULL
2662 ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
2663 fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
2664 char **papszArgs;
2665 int cArgs;
2666 rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
2667 if (RT_SUCCESS(rc))
2668 {
2669 /*
2670 * Parse them.
2671 */
2672 rc = rtFsIsoMakerCmdParse(pOpts, cArgs, papszArgs, cDepth + 1);
2673
2674 RTGetOptArgvFreeEx(papszArgs, fGetOpt);
2675 }
2676 else
2677 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTGetOptArgvFromString failed: %Rrc", pszFileSpec, rc);
2678
2679 }
2680 else
2681 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: invalid encoding", pszFileSpec);
2682 }
2683 else
2684 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: error to read it into memory: %Rrc", pszFileSpec, rc);
2685 RTMemTmpFree(pszContent);
2686 }
2687 else
2688 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "%s: failed to allocte %zu bytes for reading",
2689 pszFileSpec, (size_t)cbFile + 1);
2690 }
2691 else
2692 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_FILE_TOO_BIG, "%s: file is too big: %'RU64 bytes, max 2MB", pszFileSpec, cbFile);
2693 }
2694 else
2695 rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTVfsFileGetSize failed: %Rrc", pszFileSpec, rc);
2696 RTVfsFileRelease(hVfsFile);
2697 return rc;
2698}
2699
2700
2701/**
2702 * Parses the given command line options.
2703 *
2704 * @returns IPRT status code.
2705 * @param pOpts The ISO maker command instance.
2706 * @param cArgs Number of arguments in papszArgs.
2707 * @param papszArgs The argument vector to parse.
2708 */
2709static int rtFsIsoMakerCmdParse(PRTFSISOMAKERCMDOPTS pOpts, unsigned cArgs, char **papszArgs, unsigned cDepth)
2710{
2711 /* Setup option parsing. */
2712 RTGETOPTSTATE GetState;
2713 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, g_aRtFsIsoMakerOptions, RT_ELEMENTS(g_aRtFsIsoMakerOptions),
2714 cDepth == 0 ? 1 : 0 /*iFirst*/, 0 /*fFlags*/);
2715 if (RT_FAILURE(rc))
2716 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTGetOpt failed: %Rrc", rc);
2717
2718 /*
2719 * Parse parameters. Parameters are position dependent.
2720 */
2721 RTGETOPTUNION ValueUnion;
2722 while ( RT_SUCCESS(rc)
2723 && (rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2724 {
2725 switch (rc)
2726 {
2727 /*
2728 * Files and directories.
2729 */
2730 case VINF_GETOPT_NOT_OPTION:
2731 if ( *ValueUnion.psz != '@'
2732 || strchr(ValueUnion.psz, '='))
2733 rc = rtFsIsoMakerCmdAddSomething(pOpts, ValueUnion.psz);
2734 else
2735 rc = rtFsIsoMakerCmdParseArgumentFile(pOpts, ValueUnion.psz + 1, cDepth);
2736 break;
2737
2738 /*
2739 * Options specific to our ISO maker.
2740 */
2741 case RTFSISOMAKERCMD_OPT_NAME_SETUP:
2742 rc = rtFsIsoMakerCmdOptNameSetup(pOpts, ValueUnion.psz);
2743 break;
2744
2745 case RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER:
2746 /* ignored */
2747 break;
2748
2749 case RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE: /* --output-buffer-size {cb} */
2750 pOpts->cbOutputReadBuffer = ValueUnion.u32;
2751 break;
2752
2753 case RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE: /* --random-output-buffer-size */
2754 pOpts->fRandomOutputReadBufferSize = true;
2755 break;
2756
2757 case RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION: /* --random-order-verficiation {cb} */
2758 pOpts->cbRandomOrderVerifciationBlock = ValueUnion.u32;
2759 break;
2760
2761 case RTFSISOMAKERCMD_OPT_PUSH_ISO:
2762 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso", 0);
2763 break;
2764
2765 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_JOLIET:
2766 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-joliet", RTFSISO9660_F_NO_JOLIET);
2767 break;
2768
2769 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK:
2770 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-rock", RTFSISO9660_F_NO_ROCK);
2771 break;
2772
2773 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK_NO_JOLIET:
2774 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-rock-no-joliet",
2775 RTFSISO9660_F_NO_ROCK | RTFSISO9660_F_NO_JOLIET);
2776 break;
2777
2778 case RTFSISOMAKERCMD_OPT_POP:
2779 rc = rtFsIsoMakerCmdOptPop(pOpts);
2780 break;
2781
2782
2783 case RTFSISOMAKERCMD_OPT_IMPORT_ISO:
2784 rc = rtFsIsoMakerCmdOptImportIso(pOpts, ValueUnion.psz);
2785 break;
2786
2787 /*
2788 * Joliet related options.
2789 */
2790 case RTFSISOMAKERCMD_OPT_NO_JOLIET:
2791 rc = RTFsIsoMakerSetJolietUcs2Level(pOpts->hIsoMaker, 0);
2792 if (RT_FAILURE(rc))
2793 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to disable joliet: %Rrc", rc);
2794 break;
2795
2796
2797 /*
2798 * Boot related options.
2799 */
2800 case 'G': /* --generic-boot <file> */
2801 rc = rtFsIsoMakerCmdOptGenericBoot(pOpts, ValueUnion.psz);
2802 break;
2803
2804 case RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE:
2805 rc = rtFsIsoMakerCmdOptEltoritoAddImage(pOpts, ValueUnion.psz);
2806 break;
2807
2808 case 'b': /* --eltorito-boot <boot.img> */
2809 rc = rtFsIsoMakerCmdOptEltoritoBoot(pOpts, ValueUnion.psz);
2810 break;
2811
2812 case RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY:
2813 rc = rtFsIsoMakerCmdOptEltoritoNewEntry(pOpts);
2814 break;
2815
2816 case RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID:
2817 rc = rtFsIsoMakerCmdOptEltoritoPlatformId(pOpts, ValueUnion.psz);
2818 break;
2819
2820 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT:
2821 rc = rtFsIsoMakerCmdOptEltoritoSetNotBootable(pOpts);
2822 break;
2823
2824 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12:
2825 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB);
2826 break;
2827 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144:
2828 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB);
2829 break;
2830 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288:
2831 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB);
2832 break;
2833 case RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT:
2834 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK);
2835 break;
2836 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT:
2837 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION);
2838 break;
2839
2840 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG:
2841 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSegment(pOpts, ValueUnion.u16);
2842 break;
2843
2844 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE:
2845 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(pOpts, ValueUnion.u16);
2846 break;
2847
2848 case RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE:
2849 rc = rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(pOpts);
2850 break;
2851
2852 case 'c': /* --boot-catalog <cd-path> */
2853 rc = rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(pOpts, ValueUnion.psz);
2854 break;
2855
2856 /*
2857 * Image/namespace property related options.
2858 */
2859 case RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID:
2860 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
2861 break;
2862
2863 case 'A': /* --application-id */
2864 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
2865 break;
2866
2867 case RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID:
2868 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
2869 break;
2870
2871 case RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID:
2872 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
2873 break;
2874
2875 case 'P': /* -publisher */
2876 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
2877 break;
2878
2879 case 'p': /* --preparer*/
2880 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
2881 break;
2882
2883 case RTFSISOMAKERCMD_OPT_SYSTEM_ID:
2884 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
2885 break;
2886
2887 case RTFSISOMAKERCMD_OPT_VOLUME_ID: /* (should've been '-V') */
2888 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_ID);
2889 break;
2890
2891 case RTFSISOMAKERCMD_OPT_VOLUME_SET_ID:
2892 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
2893 break;
2894
2895 /*
2896 * Options compatible with other ISO makers.
2897 */
2898 case 'o':
2899 if (pOpts->fVirtualImageMaker)
2900 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is not allowed");
2901 if (pOpts->pszOutFile)
2902 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is specified more than once");
2903 pOpts->pszOutFile = ValueUnion.psz;
2904 break;
2905
2906 /*
2907 * Standard bits.
2908 */
2909 case 'h':
2910 rtFsIsoMakerCmdUsage(pOpts, papszArgs[0]);
2911 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_SUCCESS;
2912
2913 case 'V':
2914 rtFsIsoMakerPrintf(pOpts, "%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
2915 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_SUCCESS;
2916
2917 default:
2918 if (rc > 0 && RT_C_IS_GRAPH(rc))
2919 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: -%c", rc);
2920 else if (rc > 0)
2921 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: %i (%#x)", rc, rc);
2922 else if (rc == VERR_GETOPT_UNKNOWN_OPTION)
2923 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Unknown option: '%s'", ValueUnion.psz);
2924 else if (ValueUnion.pDef)
2925 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: %Rrs", ValueUnion.pDef->pszLong, rc);
2926 else
2927 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%Rrs", rc);
2928 return rc;
2929 }
2930 if (RT_FAILURE(rc))
2931 return rc;
2932 }
2933 return VINF_SUCCESS;
2934}
2935
2936
2937/**
2938 * Extended ISO maker command.
2939 *
2940 * This can be used as a ISO maker command that produces a image file, or
2941 * alternatively for setting up a virtual ISO in memory.
2942 *
2943 * @returns IPRT status code
2944 * @param cArgs Number of arguments.
2945 * @param papszArgs Pointer to argument array.
2946 * @param phVfsFile Where to return the virtual ISO. Pass NULL to
2947 * for normal operation (creates file on disk).
2948 * @param pErrInfo Where to return extended error information in
2949 * the virtual ISO mode.
2950 */
2951RTDECL(int) RTFsIsoMakerCmdEx(unsigned cArgs, char **papszArgs, PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo)
2952{
2953 /*
2954 * Create instance.
2955 */
2956 RTFSISOMAKERCMDOPTS Opts;
2957 RT_ZERO(Opts);
2958 Opts.hIsoMaker = NIL_RTFSISOMAKER;
2959 Opts.pErrInfo = pErrInfo;
2960 Opts.fVirtualImageMaker = phVfsFile != NULL;
2961 Opts.hSrcVfs = NIL_RTVFS;
2962 Opts.cNameSpecifiers = 1;
2963 Opts.afNameSpecifiers[0] = RTFSISOMAKERCMDNAME_MAJOR_MASK;
2964 Opts.fDstNamespaces = RTFSISOMAKERCMDNAME_MAJOR_MASK;
2965 if (phVfsFile)
2966 *phVfsFile = NIL_RTVFSFILE;
2967 for (uint32_t i = 0; i < RT_ELEMENTS(Opts.aBootCatEntries); i++)
2968 Opts.aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
2969
2970 /* Create the ISO creator instance. */
2971 int rc = RTFsIsoMakerCreate(&Opts.hIsoMaker);
2972 if (RT_FAILURE(rc))
2973 return rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreate failed: %Rrc", rc);
2974
2975 /*
2976 * Parse the command line and check for mandatory options.
2977 */
2978 rc = rtFsIsoMakerCmdParse(&Opts, cArgs, papszArgs, 0);
2979 if (RT_SUCCESS(rc))
2980 {
2981 if (!Opts.cItemsAdded)
2982 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_NO_DATA, "Cowardly refuses to create empty ISO image");
2983 else if (!Opts.pszOutFile && !Opts.fVirtualImageMaker)
2984 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_INVALID_PARAMETER, "No output file specified (--output <file>)");
2985 }
2986
2987 /*
2988 * Final actions.
2989 */
2990 if (RT_SUCCESS(rc))
2991 rc = rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(&Opts);
2992 if (RT_SUCCESS(rc))
2993 {
2994 /*
2995 * Finalize the image and get the virtual file.
2996 */
2997 rc = RTFsIsoMakerFinalize(Opts.hIsoMaker);
2998 if (RT_SUCCESS(rc))
2999 {
3000 RTVFSFILE hVfsFile;
3001 rc = RTFsIsoMakerCreateVfsOutputFile(Opts.hIsoMaker, &hVfsFile);
3002 if (RT_SUCCESS(rc))
3003 {
3004 /*
3005 * We're done now if we're only setting up a virtual image.
3006 */
3007 if (Opts.fVirtualImageMaker)
3008 *phVfsFile = hVfsFile;
3009 else
3010 {
3011 rc = rtFsIsoMakerCmdWriteImage(&Opts, hVfsFile);
3012 RTVfsFileRelease(hVfsFile);
3013 }
3014 }
3015 else
3016 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreateVfsOutputFile failed: %Rrc", rc);
3017 }
3018 else
3019 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerFinalize failed: %Rrc", rc);
3020 }
3021
3022 return rtFsIsoMakerCmdDeleteState(&Opts, rc);
3023}
3024
3025
3026/**
3027 * ISO maker command (creates image file on disk).
3028 *
3029 * @returns IPRT status code
3030 * @param cArgs Number of arguments.
3031 * @param papszArgs Pointer to argument array.
3032 */
3033RTDECL(RTEXITCODE) RTFsIsoMakerCmd(unsigned cArgs, char **papszArgs)
3034{
3035 int rc = RTFsIsoMakerCmdEx(cArgs, papszArgs, NULL, NULL);
3036 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
3037}
3038
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