VirtualBox

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

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

IPRT: ISO maker updates (import related).

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