VirtualBox

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

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

IPRT: More ISO maker code (import + booting).

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette