VirtualBox

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

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

isomaker: Some fixes for bugs found when testing different buffer read sizes and read ordering. [build fix]

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