VirtualBox

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

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