VirtualBox

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

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

IPRT: More ISO maker code (booting related).

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

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