VirtualBox

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

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

IPRT: More ISO maker code (booting related).

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