VirtualBox

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

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

isomaker: fixed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 171.3 KB
Line 
1/* $Id: isomakercmd.cpp 68842 2017-09-23 13:46:08Z 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/asm.h>
36#include <iprt/assert.h>
37#include <iprt/buildconfig.h>
38#include <iprt/ctype.h>
39#include <iprt/file.h>
40#include <iprt/fsvfs.h>
41#include <iprt/err.h>
42#include <iprt/getopt.h>
43#include <iprt/log.h>
44#include <iprt/mem.h>
45#include <iprt/message.h>
46#include <iprt/path.h>
47#include <iprt/rand.h>
48#include <iprt/stream.h>
49#include <iprt/string.h>
50#include <iprt/vfs.h>
51#include <iprt/formats/iso9660.h>
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57/** Maximum number of name specifiers we allow. */
58#define RTFSISOMAKERCMD_MAX_NAMES 8
59
60/** Maximum directory recursions when adding a directory tree. */
61#define RTFSISOMAKERCMD_MAX_DIR_RECURSIONS 32
62
63/** @name Name specifiers
64 * @{ */
65#define RTFSISOMAKERCMDNAME_PRIMARY_ISO RTFSISOMAKER_NAMESPACE_ISO_9660
66#define RTFSISOMAKERCMDNAME_JOLIET RTFSISOMAKER_NAMESPACE_JOLIET
67#define RTFSISOMAKERCMDNAME_UDF RTFSISOMAKER_NAMESPACE_UDF
68#define RTFSISOMAKERCMDNAME_HFS RTFSISOMAKER_NAMESPACE_HFS
69
70#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE RT_BIT_32(16)
71#define RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE RT_BIT_32(17)
72
73#define RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL RT_BIT_32(20)
74#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL RT_BIT_32(21)
75#define RTFSISOMAKERCMDNAME_UDF_TRANS_TBL RT_BIT_32(22)
76#define RTFSISOMAKERCMDNAME_HFS_TRANS_TBL RT_BIT_32(23)
77
78#define RTFSISOMAKERCMDNAME_MAJOR_MASK \
79 (RTFSISOMAKERCMDNAME_PRIMARY_ISO | RTFSISOMAKERCMDNAME_JOLIET | RTFSISOMAKERCMDNAME_UDF | RTFSISOMAKERCMDNAME_HFS)
80
81#define RTFSISOMAKERCMDNAME_MINOR_MASK \
82 ( RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE | RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL \
83 | RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE | RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL \
84 | RTFSISOMAKERCMDNAME_UDF_TRANS_TBL \
85 | RTFSISOMAKERCMDNAME_HFS_TRANS_TBL)
86AssertCompile((RTFSISOMAKERCMDNAME_MAJOR_MASK & RTFSISOMAKERCMDNAME_MINOR_MASK) == 0);
87/** @} */
88
89
90/*********************************************************************************************************************************
91* Structures and Typedefs *
92*********************************************************************************************************************************/
93typedef enum RTFSISOMAKERCMDOPT
94{
95 RTFSISOMAKERCMD_OPT_FIRST = 1000,
96
97 RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER,
98 RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE,
99 RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE,
100 RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION,
101 RTFSISOMAKERCMD_OPT_NAME_SETUP,
102
103 RTFSISOMAKERCMD_OPT_ROCK_RIDGE,
104 RTFSISOMAKERCMD_OPT_LIMITED_ROCK_RIDGE,
105 RTFSISOMAKERCMD_OPT_NO_ROCK_RIDGE,
106 RTFSISOMAKERCMD_OPT_NO_JOLIET,
107
108 RTFSISOMAKERCMD_OPT_IMPORT_ISO,
109 RTFSISOMAKERCMD_OPT_PUSH_ISO,
110 RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_JOLIET,
111 RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK,
112 RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK_NO_JOLIET,
113 RTFSISOMAKERCMD_OPT_POP,
114
115 RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY,
116 RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE,
117 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12,
118 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144,
119 RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288,
120
121 RTFSISOMAKERCMD_OPT_RATIONAL_ATTRIBS,
122 RTFSISOMAKERCMD_OPT_STRICT_ATTRIBS,
123 RTFSISOMAKERCMD_OPT_NO_FILE_MODE,
124 RTFSISOMAKERCMD_OPT_NO_DIR_MODE,
125 RTFSISOMAKERCMD_OPT_CHMOD,
126 RTFSISOMAKERCMD_OPT_CHOWN,
127 RTFSISOMAKERCMD_OPT_CHGRP,
128
129 /*
130 * Compatibility options:
131 */
132 RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID,
133 RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS,
134 RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE,
135 RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE,
136 RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT,
137 RTFSISOMAKERCMD_OPT_ALPHA_BOOT,
138 RTFSISOMAKERCMD_OPT_APPLE,
139 RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID,
140 RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES,
141 RTFSISOMAKERCMD_OPT_CHECK_SESSION,
142 RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID,
143 RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS,
144 RTFSISOMAKERCMD_OPT_DIR_MODE,
145 RTFSISOMAKERCMD_OPT_DVD_VIDEO,
146 RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID,
147 RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT,
148 RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE,
149 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG,
150 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE,
151 RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT,
152 RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT,
153 RTFSISOMAKERCMD_OPT_EXCLUDE_LIST,
154 RTFSISOMAKERCMD_OPT_FILE_MODE,
155 RTFSISOMAKERCMD_OPT_FORCE_RR,
156 RTFSISOMAKERCMD_OPT_GID,
157 RTFSISOMAKERCMD_OPT_GRAFT_POINTS,
158 RTFSISOMAKERCMD_OPT_GUI,
159 RTFSISOMAKERCMD_OPT_HFS_AUTO,
160 RTFSISOMAKERCMD_OPT_HFS_BLESS,
161 RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE,
162 RTFSISOMAKERCMD_OPT_HFS_CAP,
163 RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT,
164 RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE,
165 RTFSISOMAKERCMD_OPT_HFS_CREATOR,
166 RTFSISOMAKERCMD_OPT_HFS_DAVE,
167 RTFSISOMAKERCMD_OPT_HFS_DOUBLE,
168 RTFSISOMAKERCMD_OPT_HFS_ENABLE,
169 RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE,
170 RTFSISOMAKERCMD_OPT_HFS_EXCHANGE,
171 RTFSISOMAKERCMD_OPT_HFS_HIDE,
172 RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST,
173 RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION,
174 RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET,
175 RTFSISOMAKERCMD_OPT_HFS_MAC_NAME,
176 RTFSISOMAKERCMD_OPT_HFS_MACBIN,
177 RTFSISOMAKERCMD_OPT_HFS_MAGIC,
178 RTFSISOMAKERCMD_OPT_HFS_MAP,
179 RTFSISOMAKERCMD_OPT_HFS_NETATALK,
180 RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP,
181 RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE,
182 RTFSISOMAKERCMD_OPT_HFS_OSX_HFS,
183 RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET,
184 RTFSISOMAKERCMD_OPT_HFS_PARMS,
185 RTFSISOMAKERCMD_OPT_HFS_PART,
186 RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT,
187 RTFSISOMAKERCMD_OPT_HFS_PROBE,
188 RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO,
189 RTFSISOMAKERCMD_OPT_HFS_SFM,
190 RTFSISOMAKERCMD_OPT_HFS_SGI,
191 RTFSISOMAKERCMD_OPT_HFS_SINGLE,
192 RTFSISOMAKERCMD_OPT_HFS_TYPE,
193 RTFSISOMAKERCMD_OPT_HFS_UNLOCK,
194 RTFSISOMAKERCMD_OPT_HFS_USHARE,
195 RTFSISOMAKERCMD_OPT_HFS_VOL_ID,
196 RTFSISOMAKERCMD_OPT_HFS_XINET,
197 RTFSISOMAKERCMD_OPT_HIDDEN,
198 RTFSISOMAKERCMD_OPT_HIDDEN_LIST,
199 RTFSISOMAKERCMD_OPT_HIDE,
200 RTFSISOMAKERCMD_OPT_HIDE_JOLIET,
201 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST,
202 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL,
203 RTFSISOMAKERCMD_OPT_HIDE_LIST,
204 RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED,
205 RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER,
206 RTFSISOMAKERCMD_OPT_HPPA_CMDLINE,
207 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32,
208 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64,
209 RTFSISOMAKERCMD_OPT_HPPA_RAMDISK,
210 RTFSISOMAKERCMD_OPT_INPUT_CHARSET,
211 RTFSISOMAKERCMD_OPT_ISO_LEVEL,
212 RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS,
213 RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE,
214 RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5,
215 RTFSISOMAKERCMD_OPT_JIGDO_JIGDO,
216 RTFSISOMAKERCMD_OPT_JIGDO_MAP,
217 RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST,
218 RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE,
219 RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE,
220 RTFSISOMAKERCMD_OPT_JOLIET_CHARSET,
221 RTFSISOMAKERCMD_OPT_JOLIET_LEVEL,
222 RTFSISOMAKERCMD_OPT_JOLIET_LONG,
223 RTFSISOMAKERCMD_OPT_LOG_FILE,
224 RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES,
225 RTFSISOMAKERCMD_OPT_MIPS_BOOT,
226 RTFSISOMAKERCMD_OPT_MIPSEL_BOOT,
227 RTFSISOMAKERCMD_OPT_NEW_DIR_MODE,
228 RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES,
229 RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS,
230 RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE,
231 RTFSISOMAKERCMD_OPT_NO_PAD,
232 RTFSISOMAKERCMD_OPT_NO_RR,
233 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS,
234 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS,
235 RTFSISOMAKERCMD_OPT_OLD_ROOT,
236 RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET,
237 RTFSISOMAKERCMD_OPT_PAD,
238 RTFSISOMAKERCMD_OPT_PATH_LIST,
239 RTFSISOMAKERCMD_OPT_PRINT_SIZE,
240 RTFSISOMAKERCMD_OPT_QUIET,
241 RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES,
242 RTFSISOMAKERCMD_OPT_ROOT,
243 RTFSISOMAKERCMD_OPT_SORT,
244 RTFSISOMAKERCMD_OPT_SPARC_BOOT,
245 RTFSISOMAKERCMD_OPT_SPARC_LABEL,
246 RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT,
247 RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME,
248 RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE,
249 RTFSISOMAKERCMD_OPT_SUNX86_BOOT,
250 RTFSISOMAKERCMD_OPT_SUNX86_LABEL,
251 RTFSISOMAKERCMD_OPT_SYSTEM_ID,
252 RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME,
253 RTFSISOMAKERCMD_OPT_UDF,
254 RTFSISOMAKERCMD_OPT_UID,
255 RTFSISOMAKERCMD_OPT_USE_FILE_VERSION,
256 RTFSISOMAKERCMD_OPT_VOLUME_ID,
257 RTFSISOMAKERCMD_OPT_VOLUME_SET_ID,
258 RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO,
259 RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE,
260 RTFSISOMAKERCMD_OPT_END
261} RTFSISOMAKERCMDOPT;
262
263
264/**
265 * El Torito boot entry.
266 */
267typedef struct RTFSISOMKCMDELTORITOENTRY
268{
269 /** The type of this entry. */
270 enum
271 {
272 kEntryType_Invalid = 0,
273 kEntryType_Validation, /**< Same as kEntryType_SectionHeader, just hardcoded #0. */
274 kEntryType_SectionHeader,
275 kEntryType_Default, /**< Same as kEntryType_Section, just hardcoded #1. */
276 kEntryType_Section
277 } enmType;
278 /** Type specific data. */
279 union
280 {
281 struct
282 {
283 /** The platform ID (ISO9660_ELTORITO_PLATFORM_ID_XXX). */
284 uint8_t idPlatform;
285 /** Some string for the header. */
286 const char *pszString;
287 } Validation,
288 SectionHeader;
289 struct
290 {
291 /** The name of the boot image wihtin the ISO (-b option). */
292 const char *pszImageNameInIso;
293 /** The object ID of the image in the ISO. This is set to UINT32_MAX when
294 * pszImageNameInIso is used (i.e. -b option) and we've delayed everything
295 * boot related till after all files have been added to the image. */
296 uint32_t idxImageObj;
297 /** Whether to insert boot info table into the image. */
298 bool fInsertBootInfoTable;
299 /** Bootble or not. Possible to make BIOS set up emulation w/o booting it. */
300 bool fBootable;
301 /** The media type (ISO9660_ELTORITO_BOOT_MEDIA_TYPE_XXX). */
302 uint8_t bBootMediaType;
303 /** File system / partition type. */
304 uint8_t bSystemType;
305 /** Load address divided by 0x10. */
306 uint16_t uLoadSeg;
307 /** Number of sectors (512) to load. */
308 uint16_t cSectorsToLoad;
309 } Section,
310 Default;
311 } u;
312} RTFSISOMKCMDELTORITOENTRY;
313/** Pointer to an el torito boot entry. */
314typedef RTFSISOMKCMDELTORITOENTRY *PRTFSISOMKCMDELTORITOENTRY;
315
316/**
317 * ISO maker command options & state.
318 */
319typedef struct RTFSISOMAKERCMDOPTS
320{
321 /** The handle to the ISO maker. */
322 RTFSISOMAKER hIsoMaker;
323 /** Set if we're creating a virtual image maker, i.e. producing something
324 * that is going to be read from only and not written to disk. */
325 bool fVirtualImageMaker;
326 /** Extended error info. This is a stderr alternative for the
327 * fVirtualImageMaker case (stdout goes to LogRel). */
328 PRTERRINFO pErrInfo;
329
330 /** The output file.
331 * This is NULL when fVirtualImageMaker is set. */
332 const char *pszOutFile;
333 /** Special buffer size to use for testing the ISO maker code reading. */
334 uint32_t cbOutputReadBuffer;
335 /** Use random output read buffer size. cbOutputReadBuffer works as maximum
336 * when this is enabled. */
337 bool fRandomOutputReadBufferSize;
338 /** Do output verification, but do it in random order if non-zero. The
339 * values gives the block size to use. */
340 uint32_t cbRandomOrderVerifciationBlock;
341
342 /** The current source VFS, NIL_RTVFS if regular file system is used. */
343 RTVFS hSrcVfs;
344 /** The specifier for hSrcVfs (error messages). */
345 const char *pszSrcVfs;
346 /** The option for hSrcVfs. */
347 const char *pszSrcVfsOption;
348
349 /** @name Processing of inputs
350 * @{ */
351 /** The namespaces (RTFSISOMAKER_NAMESPACE_XXX) we're currently adding
352 * input to. */
353 uint32_t fDstNamespaces;
354 /** The number of name specifiers we're currently operating with. */
355 uint32_t cNameSpecifiers;
356 /** Name specifier configurations.
357 * For instance given "name0=name1=name2=name3=source-file" we will add
358 * source-file to the image with name0 as the name in the namespace and
359 * sub-name specified by aNameSpecifiers[0], name1 in aNameSpecifiers[1],
360 * and so on. This allows exact control over which names a file will
361 * have in each namespace (primary-iso, joliet, udf, hfs) and sub-namespace
362 * (rock-ridge, trans.tbl).
363 */
364 uint32_t afNameSpecifiers[RTFSISOMAKERCMD_MAX_NAMES];
365 /** The forced directory mode. */
366 RTFMODE fDirMode;
367 /** Set if fDirMode should be applied. */
368 bool fDirModeActive;
369 /** Set if fFileMode should be applied. */
370 bool fFileModeActive;
371 /** The force file mode. */
372 RTFMODE fFileMode;
373 /** @} */
374
375 /** @name Booting related options and state.
376 * @{ */
377 /** Number of boot catalog entries (aBootCatEntries). */
378 uint32_t cBootCatEntries;
379 /** Boot catalog entries. */
380 RTFSISOMKCMDELTORITOENTRY aBootCatEntries[64];
381 /** @} */
382
383 /** @name Filteringer
384 * @{ */
385 /** The trans.tbl filename when enabled. We must not import these files. */
386 const char *pszTransTbl;
387 /** @} */
388
389 /** Number of items (files, directories, images, whatever) we've added. */
390 uint32_t cItemsAdded;
391} RTFSISOMAKERCMDOPTS;
392typedef RTFSISOMAKERCMDOPTS *PRTFSISOMAKERCMDOPTS;
393typedef RTFSISOMAKERCMDOPTS const *PCRTFSISOMAKERCMDOPTS;
394
395
396/**
397 * One parsed name.
398 */
399typedef struct RTFSISOMKCMDPARSEDNAME
400{
401 /** Copy of the corresponding RTFSISOMAKERCMDOPTS::afNameSpecifiers
402 * value. */
403 uint32_t fNameSpecifiers;
404 /** The length of the specified path. */
405 uint32_t cchPath;
406 /** Specified path. */
407 char szPath[RTPATH_MAX];
408} RTFSISOMKCMDPARSEDNAME;
409/** Pointer to a parsed name. */
410typedef RTFSISOMKCMDPARSEDNAME *PRTFSISOMKCMDPARSEDNAME;
411/** Pointer to a const parsed name. */
412typedef RTFSISOMKCMDPARSEDNAME const *PCRTFSISOMKCMDPARSEDNAME;
413
414
415/**
416 * Parsed names.
417 */
418typedef struct RTFSISOMKCMDPARSEDNAMES
419{
420 /** Number of names. */
421 uint32_t cNames;
422 /** Number of names with the source. */
423 uint32_t cNamesWithSrc;
424 /** Special source types.
425 * Used for conveying commands to do on names intead of adding a source.
426 * Only used when adding generic stuff w/o any options involved. */
427 enum
428 {
429 kSrcType_None,
430 kSrcType_Normal,
431 kSrcType_Remove,
432 kSrcType_MustRemove
433 } enmSrcType;
434 /** The parsed names. */
435 RTFSISOMKCMDPARSEDNAME aNames[RTFSISOMAKERCMD_MAX_NAMES + 1];
436} RTFSISOMKCMDPARSEDNAMES;
437/** Pointer to parsed names. */
438typedef RTFSISOMKCMDPARSEDNAMES *PRTFSISOMKCMDPARSEDNAMES;
439/** Pointer to const parsed names. */
440typedef RTFSISOMKCMDPARSEDNAMES *PCRTFSISOMKCMDPARSEDNAMES;
441
442
443/*********************************************************************************************************************************
444* Global Variables *
445*********************************************************************************************************************************/
446/*
447 * Parse the command line. This is similar to genisoimage and mkisofs,
448 * thus the single dash long name aliases.
449 */
450static const RTGETOPTDEF g_aRtFsIsoMakerOptions[] =
451{
452 /*
453 * Unique IPRT ISO maker options.
454 */
455 { "--name-setup", RTFSISOMAKERCMD_OPT_NAME_SETUP, RTGETOPT_REQ_STRING },
456 { "--import-iso", RTFSISOMAKERCMD_OPT_IMPORT_ISO, RTGETOPT_REQ_STRING },
457 { "--push-iso", RTFSISOMAKERCMD_OPT_PUSH_ISO, RTGETOPT_REQ_STRING },
458 { "--push-iso-no-joliet", RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_JOLIET, RTGETOPT_REQ_STRING },
459 { "--push-iso-no-rock", RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK, RTGETOPT_REQ_STRING },
460 { "--push-iso-no-rock-no-joliet", RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK_NO_JOLIET, RTGETOPT_REQ_STRING },
461 { "--pop", RTFSISOMAKERCMD_OPT_POP, RTGETOPT_REQ_NOTHING },
462
463 { "--rock-ridge", RTFSISOMAKERCMD_OPT_ROCK_RIDGE, RTGETOPT_REQ_NOTHING },
464 { "--limited-rock-ridge", RTFSISOMAKERCMD_OPT_LIMITED_ROCK_RIDGE, RTGETOPT_REQ_NOTHING },
465 { "--no-rock-ridge", RTFSISOMAKERCMD_OPT_NO_ROCK_RIDGE, RTGETOPT_REQ_NOTHING },
466 { "--no-joliet", RTFSISOMAKERCMD_OPT_NO_JOLIET, RTGETOPT_REQ_NOTHING },
467 { "--joliet-ucs-level", RTFSISOMAKERCMD_OPT_JOLIET_LEVEL, RTGETOPT_REQ_UINT8 },
468
469 { "--rational-attribs", RTFSISOMAKERCMD_OPT_RATIONAL_ATTRIBS, RTGETOPT_REQ_NOTHING },
470 { "--strict-attribs", RTFSISOMAKERCMD_OPT_STRICT_ATTRIBS, RTGETOPT_REQ_NOTHING },
471 { "--no-file-mode", RTFSISOMAKERCMD_OPT_NO_FILE_MODE, RTGETOPT_REQ_NOTHING },
472 { "--no-dir-mode", RTFSISOMAKERCMD_OPT_NO_DIR_MODE, RTGETOPT_REQ_NOTHING },
473 { "--chmod", RTFSISOMAKERCMD_OPT_CHMOD, RTGETOPT_REQ_STRING },
474 { "--chown", RTFSISOMAKERCMD_OPT_CHOWN, RTGETOPT_REQ_STRING },
475 { "--chgrp", RTFSISOMAKERCMD_OPT_CHGRP, RTGETOPT_REQ_STRING },
476
477 { "--eltorito-new-entry", RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY, RTGETOPT_REQ_NOTHING },
478 { "--eltorito-add-image", RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE, RTGETOPT_REQ_STRING },
479 { "--eltorito-floppy-12", RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12, RTGETOPT_REQ_NOTHING },
480 { "--eltorito-floppy-144", RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144, RTGETOPT_REQ_NOTHING },
481 { "--eltorito-floppy-288", RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288, RTGETOPT_REQ_NOTHING },
482
483 { "--iprt-iso-maker-file-marker", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
484 { "--iprt-iso-maker-file-marker-ms", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
485 { "--iprt-iso-maker-file-marker-ms-crt", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
486 { "--iprt-iso-maker-file-marker-bourne", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
487 { "--iprt-iso-maker-file-marker-bourne-sh", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_STRING },
488
489 { "--output-buffer-size", RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE, RTGETOPT_REQ_UINT32 },
490 { "--random-output-buffer-size", RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE, RTGETOPT_REQ_NOTHING },
491 { "--random-order-verification", RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION, RTGETOPT_REQ_UINT32 },
492
493#define DD(a_szLong, a_chShort, a_fFlags) { a_szLong, a_chShort, a_fFlags }, { "-" a_szLong, a_chShort, a_fFlags }
494
495 /*
496 * genisoimage/mkisofs compatibility options we've implemented:
497 */
498 /* booting: */
499 { "--generic-boot", 'G', RTGETOPT_REQ_STRING },
500 DD("-eltorito-boot", 'b', RTGETOPT_REQ_STRING ),
501 DD("-eltorito-alt-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY, RTGETOPT_REQ_NOTHING ),
502 DD("-eltorito-platform-id", RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID, RTGETOPT_REQ_STRING ),
503 DD("-hard-disk-boot", RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT, RTGETOPT_REQ_NOTHING ),
504 DD("-no-emulation-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT, RTGETOPT_REQ_NOTHING ),
505 DD("-no-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT, RTGETOPT_REQ_NOTHING ),
506 DD("-boot-load-seg", RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG, RTGETOPT_REQ_UINT16 ),
507 DD("-boot-load-size", RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE, RTGETOPT_REQ_UINT16 ),
508 DD("-boot-info-table", RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE, RTGETOPT_REQ_NOTHING ),
509 { "--boot-catalog", 'c', RTGETOPT_REQ_STRING },
510
511 /* String props: */
512 DD("-abstract", RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID, RTGETOPT_REQ_STRING ),
513 { "--application-id", 'A', RTGETOPT_REQ_STRING },
514 DD("-biblio", RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID, RTGETOPT_REQ_STRING ),
515 DD("-copyright", RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID, RTGETOPT_REQ_STRING ),
516 DD("-publisher", 'P', RTGETOPT_REQ_STRING ),
517 { "--preparer", 'p', RTGETOPT_REQ_STRING },
518 DD("-sysid", RTFSISOMAKERCMD_OPT_SYSTEM_ID, RTGETOPT_REQ_STRING ),
519 { "--volume-id", RTFSISOMAKERCMD_OPT_VOLUME_ID, RTGETOPT_REQ_STRING }, /* should've been '-V' */
520 DD("-volid", RTFSISOMAKERCMD_OPT_VOLUME_ID, RTGETOPT_REQ_STRING ),
521 DD("-volset", RTFSISOMAKERCMD_OPT_VOLUME_SET_ID, RTGETOPT_REQ_STRING ),
522
523 /* Other: */
524 DD("-file-mode", RTFSISOMAKERCMD_OPT_FILE_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
525 DD("-dir-mode", RTFSISOMAKERCMD_OPT_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
526 DD("-new-dir-mode", RTFSISOMAKERCMD_OPT_NEW_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
527 DD("-graft-points", RTFSISOMAKERCMD_OPT_GRAFT_POINTS, RTGETOPT_REQ_NOTHING ),
528 DD("--iso-level", RTFSISOMAKERCMD_OPT_ISO_LEVEL, RTGETOPT_REQ_UINT8 ),
529 { "--long-names", 'l', RTGETOPT_REQ_NOTHING },
530 { "--output", 'o', RTGETOPT_REQ_STRING },
531 { "--joliet", 'J', RTGETOPT_REQ_NOTHING },
532 DD("-ucs-level", RTFSISOMAKERCMD_OPT_JOLIET_LEVEL, RTGETOPT_REQ_UINT8 ),
533 DD("-rock", 'R', RTGETOPT_REQ_NOTHING ),
534 DD("-rational-rock", 'r', RTGETOPT_REQ_NOTHING ),
535 DD("-pad", RTFSISOMAKERCMD_OPT_PAD, RTGETOPT_REQ_NOTHING ),
536 DD("-no-pad", RTFSISOMAKERCMD_OPT_NO_PAD, RTGETOPT_REQ_NOTHING ),
537
538 /*
539 * genisoimage/mkisofs compatibility:
540 */
541 DD("-allow-limited-size", RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE, RTGETOPT_REQ_NOTHING ),
542 DD("-allow-leading-dots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
543 DD("-ldots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
544 DD("-allow-lowercase", RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE, RTGETOPT_REQ_NOTHING ),
545 DD("-allow-multidot", RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT, RTGETOPT_REQ_NOTHING ),
546 DD("-cache-inodes", RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
547 DD("-no-cache-inodes", RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
548 DD("-alpha-boot", RTFSISOMAKERCMD_OPT_ALPHA_BOOT, RTGETOPT_REQ_STRING ),
549 DD("-hppa-bootloader", RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER, RTGETOPT_REQ_STRING ),
550 DD("-hppa-cmdline", RTFSISOMAKERCMD_OPT_HPPA_CMDLINE, RTGETOPT_REQ_STRING ),
551 DD("-hppa-kernel-32", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32, RTGETOPT_REQ_STRING ),
552 DD("-hppa-kernel-64", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64, RTGETOPT_REQ_STRING ),
553 DD("-hppa-ramdisk", RTFSISOMAKERCMD_OPT_HPPA_RAMDISK, RTGETOPT_REQ_STRING ),
554 DD("-mips-boot", RTFSISOMAKERCMD_OPT_MIPS_BOOT, RTGETOPT_REQ_STRING ),
555 DD("-mipsel-boot", RTFSISOMAKERCMD_OPT_MIPSEL_BOOT, RTGETOPT_REQ_STRING ),
556 DD("-sparc-boot", 'B', RTGETOPT_REQ_STRING ),
557 { "--cd-extra", 'C', RTGETOPT_REQ_STRING },
558 DD("-check-oldnames", RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES, RTGETOPT_REQ_NOTHING ),
559 DD("-check-session", RTFSISOMAKERCMD_OPT_CHECK_SESSION, RTGETOPT_REQ_STRING ),
560 { "--dont-append-dot", 'd', RTGETOPT_REQ_NOTHING },
561 { "--deep-directories", 'D', RTGETOPT_REQ_NOTHING },
562 DD("-dvd-video", RTFSISOMAKERCMD_OPT_DVD_VIDEO, RTGETOPT_REQ_NOTHING ),
563 DD("-follow-symlinks", 'f', RTGETOPT_REQ_NOTHING ),
564 DD("-gid", RTFSISOMAKERCMD_OPT_GID, RTGETOPT_REQ_UINT32 ),
565 DD("-gui", RTFSISOMAKERCMD_OPT_GUI, RTGETOPT_REQ_NOTHING ),
566 DD("-hide", RTFSISOMAKERCMD_OPT_HIDE, RTGETOPT_REQ_STRING ),
567 DD("-hide-list", RTFSISOMAKERCMD_OPT_HIDE_LIST, RTGETOPT_REQ_STRING ),
568 DD("-hidden", RTFSISOMAKERCMD_OPT_HIDDEN, RTGETOPT_REQ_STRING ),
569 DD("-hidden-list", RTFSISOMAKERCMD_OPT_HIDDEN_LIST, RTGETOPT_REQ_STRING ),
570 DD("-hide-joliet", RTFSISOMAKERCMD_OPT_HIDE_JOLIET, RTGETOPT_REQ_STRING ),
571 DD("-hide-joliet-list", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST, RTGETOPT_REQ_STRING ),
572 DD("-hide-joliet-trans-tbl", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL, RTGETOPT_REQ_NOTHING ),
573 DD("-hide-rr-moved", RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED, RTGETOPT_REQ_NOTHING ),
574 DD("-input-charset", RTFSISOMAKERCMD_OPT_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
575 DD("-output-charset", RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
576 DD("-joliet-long", RTFSISOMAKERCMD_OPT_JOLIET_LONG, RTGETOPT_REQ_NOTHING ),
577 DD("-jcharset", RTFSISOMAKERCMD_OPT_JOLIET_CHARSET, RTGETOPT_REQ_STRING ),
578 { "--leading-dot", 'L', RTGETOPT_REQ_NOTHING },
579 DD("-jigdo-jigdo", RTFSISOMAKERCMD_OPT_JIGDO_JIGDO, RTGETOPT_REQ_STRING ),
580 DD("-jigdo-template", RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE, RTGETOPT_REQ_STRING ),
581 DD("-jigdo-min-file-size", RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE, RTGETOPT_REQ_UINT64 ),
582 DD("-jigdo-force-md5", RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5, RTGETOPT_REQ_STRING ),
583 DD("-jigdo-exclude", RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE, RTGETOPT_REQ_STRING ),
584 DD("-jigdo-map", RTFSISOMAKERCMD_OPT_JIGDO_MAP, RTGETOPT_REQ_STRING ),
585 DD("-md5-list", RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST, RTGETOPT_REQ_STRING ),
586 DD("-jigdo-template-compress", RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS, RTGETOPT_REQ_STRING ),
587 DD("-log-file", RTFSISOMAKERCMD_OPT_LOG_FILE, RTGETOPT_REQ_STRING ),
588 { "--exclude", 'm', RTGETOPT_REQ_STRING },
589 { "--exclude", 'x', RTGETOPT_REQ_STRING },
590 DD("-exclude-list", RTFSISOMAKERCMD_OPT_EXCLUDE_LIST, RTGETOPT_REQ_STRING ),
591 DD("-max-iso9660-filenames", RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES, RTGETOPT_REQ_NOTHING ),
592 { "--merge", 'M', RTGETOPT_REQ_STRING },
593 DD("-dev", 'M', RTGETOPT_REQ_STRING ),
594 { "--omit-version-numbers", 'N', RTGETOPT_REQ_NOTHING },
595 DD("-nobak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
596 DD("-no-bak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
597 DD("-force-rr", RTFSISOMAKERCMD_OPT_FORCE_RR, RTGETOPT_REQ_NOTHING ),
598 DD("-no-rr", RTFSISOMAKERCMD_OPT_NO_RR, RTGETOPT_REQ_NOTHING ),
599 DD("-no-split-symlink-components", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS, RTGETOPT_REQ_NOTHING ),
600 DD("-no-split-symlink-fields", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS, RTGETOPT_REQ_NOTHING ),
601 DD("-path-list", RTFSISOMAKERCMD_OPT_PATH_LIST, RTGETOPT_REQ_STRING ),
602 DD("-print-size", RTFSISOMAKERCMD_OPT_PRINT_SIZE, RTGETOPT_REQ_NOTHING ),
603 DD("-quiet", RTFSISOMAKERCMD_OPT_QUIET, RTGETOPT_REQ_NOTHING ),
604 DD("-relaxed-filenames", RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES, RTGETOPT_REQ_NOTHING ),
605 DD("-root", RTFSISOMAKERCMD_OPT_ROOT, RTGETOPT_REQ_STRING ),
606 DD("-old-root", RTFSISOMAKERCMD_OPT_OLD_ROOT, RTGETOPT_REQ_STRING ),
607 DD("-sort", RTFSISOMAKERCMD_OPT_SORT, RTGETOPT_REQ_STRING ),
608 DD("-sparc-boot", RTFSISOMAKERCMD_OPT_SPARC_BOOT, RTGETOPT_REQ_STRING ),
609 DD("-sparc-label", RTFSISOMAKERCMD_OPT_SPARC_LABEL, RTGETOPT_REQ_STRING ),
610 DD("-split-output", RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT, RTGETOPT_REQ_NOTHING ),
611 DD("-stream-media-size", RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE, RTGETOPT_REQ_UINT64 ),
612 DD("-stream-file-name", RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME, RTGETOPT_REQ_STRING ),
613 DD("-sunx86-boot", RTFSISOMAKERCMD_OPT_SUNX86_BOOT, RTGETOPT_REQ_STRING ),
614 DD("-sunx86-label", RTFSISOMAKERCMD_OPT_SUNX86_LABEL, RTGETOPT_REQ_STRING ),
615 { "--trans-tbl", 'T', RTGETOPT_REQ_NOTHING },
616 DD("-table-name", RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME, RTGETOPT_REQ_STRING ),
617 DD("-udf", RTFSISOMAKERCMD_OPT_UDF, RTGETOPT_REQ_NOTHING ),
618 DD("-uid", RTFSISOMAKERCMD_OPT_UID, RTGETOPT_REQ_UINT32 ),
619 DD("-use-fileversion", RTFSISOMAKERCMD_OPT_USE_FILE_VERSION, RTGETOPT_REQ_NOTHING ),
620 { "--untranslated-filenames", 'U', RTGETOPT_REQ_NOTHING },
621 DD("-no-iso-translate", RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE, RTGETOPT_REQ_NOTHING ),
622 DD("-volset-size", RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE, RTGETOPT_REQ_UINT32 ),
623 DD("-volset-seqno", RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO, RTGETOPT_REQ_UINT32 ),
624 { "--transpared-compression", 'z', RTGETOPT_REQ_NOTHING },
625
626 /* HFS and ISO-9660 apple extensions. */
627 DD("-hfs", RTFSISOMAKERCMD_OPT_HFS_ENABLE, RTGETOPT_REQ_NOTHING ),
628 DD("-apple", RTFSISOMAKERCMD_OPT_APPLE, RTGETOPT_REQ_NOTHING ),
629 DD("-map", RTFSISOMAKERCMD_OPT_HFS_MAP, RTGETOPT_REQ_STRING ),
630 DD("-magic", RTFSISOMAKERCMD_OPT_HFS_MAGIC, RTGETOPT_REQ_STRING ),
631 DD("-hfs-creator", RTFSISOMAKERCMD_OPT_HFS_CREATOR, RTGETOPT_REQ_STRING ),
632 DD("-hfs-type", RTFSISOMAKERCMD_OPT_HFS_TYPE, RTGETOPT_REQ_STRING ),
633 DD("-probe", RTFSISOMAKERCMD_OPT_HFS_PROBE, RTGETOPT_REQ_NOTHING ),
634 DD("-no-desktop", RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP, RTGETOPT_REQ_NOTHING ),
635 DD("-mac-name", RTFSISOMAKERCMD_OPT_HFS_MAC_NAME, RTGETOPT_REQ_NOTHING ),
636 DD("-boot-hfs-file", RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE, RTGETOPT_REQ_STRING ),
637 DD("-part", RTFSISOMAKERCMD_OPT_HFS_PART, RTGETOPT_REQ_NOTHING ),
638 DD("-auto", RTFSISOMAKERCMD_OPT_HFS_AUTO, RTGETOPT_REQ_STRING ),
639 DD("-cluster-size", RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE, RTGETOPT_REQ_UINT32 ),
640 DD("-hide-hfs", RTFSISOMAKERCMD_OPT_HFS_HIDE, RTGETOPT_REQ_STRING ),
641 DD("-hide-hfs-list", RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST, RTGETOPT_REQ_STRING ),
642 DD("-hfs-volid", RTFSISOMAKERCMD_OPT_HFS_VOL_ID, RTGETOPT_REQ_STRING ),
643 DD("-icon-position", RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION, RTGETOPT_REQ_NOTHING ),
644 DD("-root-info", RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO, RTGETOPT_REQ_STRING ),
645 DD("-prep-boot", RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT, RTGETOPT_REQ_STRING ),
646 DD("-chrp-boot", RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT, RTGETOPT_REQ_NOTHING ),
647 DD("-input-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
648 DD("-output-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
649 DD("-hfs-unlock", RTFSISOMAKERCMD_OPT_HFS_UNLOCK, RTGETOPT_REQ_NOTHING ),
650 DD("-hfs-bless", RTFSISOMAKERCMD_OPT_HFS_BLESS, RTGETOPT_REQ_STRING ),
651 DD("-hfs-parms", RTFSISOMAKERCMD_OPT_HFS_PARMS, RTGETOPT_REQ_STRING ),
652 { "--cap", RTFSISOMAKERCMD_OPT_HFS_CAP, RTGETOPT_REQ_NOTHING },
653 { "--netatalk", RTFSISOMAKERCMD_OPT_HFS_NETATALK, RTGETOPT_REQ_NOTHING },
654 { "--double", RTFSISOMAKERCMD_OPT_HFS_DOUBLE, RTGETOPT_REQ_NOTHING },
655 { "--ethershare", RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE, RTGETOPT_REQ_NOTHING },
656 { "--ushare", RTFSISOMAKERCMD_OPT_HFS_USHARE, RTGETOPT_REQ_NOTHING },
657 { "--exchange", RTFSISOMAKERCMD_OPT_HFS_EXCHANGE, RTGETOPT_REQ_NOTHING },
658 { "--sgi", RTFSISOMAKERCMD_OPT_HFS_SGI, RTGETOPT_REQ_NOTHING },
659 { "--xinet", RTFSISOMAKERCMD_OPT_HFS_XINET, RTGETOPT_REQ_NOTHING },
660 { "--macbin", RTFSISOMAKERCMD_OPT_HFS_MACBIN, RTGETOPT_REQ_NOTHING },
661 { "--single", RTFSISOMAKERCMD_OPT_HFS_SINGLE, RTGETOPT_REQ_NOTHING },
662 { "--dave", RTFSISOMAKERCMD_OPT_HFS_DAVE, RTGETOPT_REQ_NOTHING },
663 { "--sfm", RTFSISOMAKERCMD_OPT_HFS_SFM, RTGETOPT_REQ_NOTHING },
664 { "--osx-double", RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE, RTGETOPT_REQ_NOTHING },
665 { "--osx-hfs", RTFSISOMAKERCMD_OPT_HFS_OSX_HFS, RTGETOPT_REQ_NOTHING },
666#undef DD
667};
668
669
670/*********************************************************************************************************************************
671* Internal Functions *
672*********************************************************************************************************************************/
673static int rtFsIsoMakerCmdParse(PRTFSISOMAKERCMDOPTS pOpts, unsigned cArgs, char **papszArgs, unsigned cDepth);
674
675
676/**
677 * Wrapper around RTErrInfoSetV / RTMsgErrorV.
678 *
679 * @returns @a rc
680 * @param pOpts The ISO maker command instance.
681 * @param rc The return code.
682 * @param pszFormat The message format.
683 * @param ... The message format arguments.
684 */
685static int rtFsIsoMakerCmdErrorRc(PRTFSISOMAKERCMDOPTS pOpts, int rc, const char *pszFormat, ...)
686{
687 va_list va;
688 va_start(va, pszFormat);
689 if (pOpts->pErrInfo)
690 RTErrInfoSetV(pOpts->pErrInfo, rc, pszFormat, va);
691 else
692 RTMsgErrorV(pszFormat, va);
693 va_end(va);
694 return rc;
695}
696
697
698/**
699 * Wrapper around RTErrInfoSetV / RTMsgErrorV for doing the job of
700 * RTVfsChainMsgError.
701 *
702 * @returns @a rc
703 * @param pOpts The ISO maker command instance.
704 * @param pszFunction The API called.
705 * @param pszSpec The VFS chain specification or file path passed to the.
706 * @param rc The return code.
707 * @param offError The error offset value returned (0 if not captured).
708 * @param pErrInfo Additional error information. Optional.
709 */
710static int rtFsIsoMakerCmdChainError(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFunction, const char *pszSpec, int rc,
711 uint32_t offError, PRTERRINFO pErrInfo)
712{
713 if (RTErrInfoIsSet(pErrInfo))
714 {
715 if (offError > 0)
716 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
717 "%s failed with rc=%Rrc: %s\n"
718 " '%s'\n"
719 " %*s^",
720 pszFunction, rc, pErrInfo->pszMsg, pszSpec, offError, "");
721 else
722 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s failed to open '%s': %Rrc: %s",
723 pszFunction, pszSpec, rc, pErrInfo->pszMsg);
724 }
725 else
726 {
727 if (offError > 0)
728 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
729 "%s failed with rc=%Rrc:\n"
730 " '%s'\n"
731 " %*s^",
732 pszFunction, rc, pszSpec, offError, "");
733 else
734 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s failed to open '%s': %Rrc", pszFunction, pszSpec, rc);
735 }
736 return rc;
737}
738
739
740/**
741 * Wrapper around RTErrInfoSetV / RTMsgErrorV for displaying syntax errors.
742 *
743 * @returns VERR_INVALID_PARAMETER
744 * @param pOpts The ISO maker command instance.
745 * @param pszFormat The message format.
746 * @param ... The message format arguments.
747 */
748static int rtFsIsoMakerCmdSyntaxError(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
749{
750 va_list va;
751 va_start(va, pszFormat);
752 if (pOpts->pErrInfo)
753 RTErrInfoSetV(pOpts->pErrInfo, VERR_INVALID_PARAMETER, pszFormat, va);
754 else
755 RTMsgErrorV(pszFormat, va);
756 va_end(va);
757 return VERR_INVALID_PARAMETER;
758}
759
760
761/**
762 * Wrapper around RTPrintfV / RTLogRelPrintfV.
763 *
764 * @param pOpts The ISO maker command instance.
765 * @param pszFormat The message format.
766 * @param ... The message format arguments.
767 */
768static void rtFsIsoMakerPrintf(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
769{
770 va_list va;
771 va_start(va, pszFormat);
772 if (pOpts->pErrInfo)
773 RTLogRelPrintfV(pszFormat, va);
774 else
775 RTPrintfV(pszFormat, va);
776 va_end(va);
777}
778
779/**
780 * Deletes the state and returns @a rc.
781 *
782 * @returns @a rc.
783 * @param pOpts The ISO maker command instance to delete.
784 * @param rc The status code to return.
785 */
786static int rtFsIsoMakerCmdDeleteState(PRTFSISOMAKERCMDOPTS pOpts, int rc)
787{
788 if (pOpts->hIsoMaker != NIL_RTFSISOMAKER)
789 {
790 RTFsIsoMakerRelease(pOpts->hIsoMaker);
791 pOpts->hIsoMaker = NIL_RTFSISOMAKER;
792 }
793
794 if (pOpts->hSrcVfs != NIL_RTVFS)
795 {
796 RTVfsRelease(pOpts->hSrcVfs);
797 pOpts->hSrcVfs = NIL_RTVFS;
798 }
799
800 return rc;
801}
802
803
804/**
805 * Print the usage.
806 *
807 * @param pOpts Options for print metho.
808 * @param pszProgName The program name.
809 */
810static void rtFsIsoMakerCmdUsage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszProgName)
811{
812 rtFsIsoMakerPrintf(pOpts,
813 "Usage: %s [options] [@commands.rsp] <filespec1> [filespec2 [..]]\n"
814 "\n"
815 "File specifications and --name-setup:\n"
816 "\n"
817 " All non-options that does not start with '@' are taken to indicate a file,\n"
818 " directory, or similar that is should be added to the ISO image. Directories\n"
819 " are added recursively and content is subject to filtering options.\n"
820 "\n"
821 " Since there can be up to six different namespaces on an ISO, it is handy\n"
822 " to be able to control the names used in each and be able to exclude an\n"
823 " object from one or more namespaces. The --name-setup option specifies the\n"
824 " file specification format to use forthwith.\n"
825 "\n"
826 " The default setup is:\n"
827 "\n"
828 " --name-setup iso+joliet+udf+hfs\n"
829 "\n"
830 " Which means you specify one on-ISO name for all namespaces followed by '='\n"
831 " and the source file system name. Only specifying the source file system\n"
832 " will add the file/dir/whatever to the root of the ISO image.\n"
833 "\n"
834 " Lets look at the following two examples:\n"
835 "\n"
836 " /docs/readme.txt=/home/user/Documents/product-x-readme.txt\n"
837 " /home/user/Documents/product-x-readme.txt\n"
838 "\n"
839 " In the first case the file '/home/user/Documents/product-x-readme.txt' is\n"
840 " added to the ISO image as '/docs/readme.txt' in all enabled namespaces.\n"
841 " In the primary ISO 9660 namespace, the filename will by default be converted\n"
842 " to upper case because it's required by the spec.\n"
843 "\n"
844 " In the second case the file is added to the root under the name\n"
845 " 'product-x-readme.txt' in all namespaces. Though, in the primary ISO 9660\n"
846 " namespace the name will be transformed to apply with the current ISO level,\n"
847 " probably uppercased, possibly truncated too.\n"
848 "\n"
849 " Given --name-setup iso,joliet,udf you can specify the name individually\n"
850 " for each of the three namespace, if you like. If you omit any, they will\n"
851 " use last name given. Any names left blank (==) will be considered omitted.\n"
852 "\n"
853 " A different name in each namespace:\n"
854 " /ISO.TXT=/Joliet.TxT=/UDF.txt=/tmp/iso/real.txt\n"
855 " Specific name in the ISO 9660 namespace, same in the rest:\n"
856 " /ISO.TXT=/OtherNamespaces.TxT=/tmp/iso/real.txt\n"
857 " Omit the file from the ISO 9660 namespace:\n"
858 " =/OtherNamespaces.TxT=/tmp/iso/real.txt\n"
859 " Omit the file from the joliet namespace:\n"
860 " /ISO.TXT==/UDF.TxT=/tmp/iso/real.txt\n"
861 " Use the same filename as the source everywhere:\n"
862 " /tmp/iso/real.txt\n"
863 "\n"
864 " Using for instance --name-setup udf you can add a files/dirs/whatever to\n"
865 " select namespace(s) without the more complicated empty name syntax above.\n"
866 "\n"
867 " When adding directories, you can only control the naming and omitting of the\n"
868 " directory itself, not any recursively added files and directories below it.\n"
869 "\n"
870 "\n"
871 "Options - General:\n"
872 "\n"
873 " -o <output-file>\n"
874 " --output <output-file>\n"
875 " The output filename. This option is not supported in VISO mode.\n"
876 "\n"
877 " --name-setup <spec>\n"
878 " Configures active namespaces and how file specifications are to be\n"
879 " interpreted. The specification is a comma separated list. Each element\n"
880 " in the list is a sub-list separated by space, '+' or '|' giving the\n"
881 " namespaces that elements controls. Namespaces are divied into two major\n"
882 " and minor ones, you cannot specifying a minor before the major it\n"
883 " belongs to.\n"
884 " Major namespaces and aliases in parentheses:\n"
885 " - iso (primary, iso9660, iso-9660, primary-iso, iso-primary)\n"
886 " - joliet\n"
887 " - udf\n"
888 " - hfs (hfs-plus)\n"
889 " Minor namespaces:\n"
890 " - rock: rock ridge on previous major namespace (iso / joliet)\n"
891 " - iso-rock: rock ridge extensions on primary ISO 9660 namespace\n"
892 " - joliet-rock: rock ridge on joliet namespace (just for fun)\n"
893 " - trans-tbl: translation table file on previous major namespace\n"
894 " - iso-trans-tbl\n"
895 " - joliet-trans-tbl\n"
896 " - udf-trans-tbl\n"
897 " - hfs-trans-tbl\n"
898 "\n"
899 " --push-iso <iso-file>\n"
900 " --push-iso-no-joliet <iso-file>\n"
901 " --push-iso-no-rock <iso-file>\n"
902 " --push-iso-no-rock-no-joliet <iso-file>\n"
903 " Open the specified ISO file and use it as source file system until the\n"
904 " corresponding --pop options is encountered. The variations are for\n"
905 " selecting which namespace on the ISO to (not) access. These options\n"
906 " are handy for copying files/directories/stuff from an ISO without\n"
907 " having to extract them first or using the :iprtvfs: syntax.\n"
908 "\n"
909 " --pop\n"
910 " Pops a --push-iso of the source file system stack.\n"
911 "\n"
912 " --import-iso <iso-file>\n"
913 " Imports everything on the given ISO file. You can use --name-setup to\n"
914 " omit namespaces.\n"
915 "\n"
916 "\n"
917 "Options - Namespaces:\n"
918 "\n"
919 " --iso-level <0|1|2|3>\n"
920 " Sets the ISO level:\n"
921 " - 0: Disable primary ISO namespace.\n"
922 " - 1: ISO level 1: Filenames 8.3 format and limited to 4GB - 1.\n"
923 " - 2: ISO level 2: 31 char long names and limited to 4GB - 1.\n"
924 " - 3: ISO level 3: 31 char long names and support for >=4GB files.\n"
925 " - 4: Fictive level used by other tools. Not yet implemented.\n"
926 " Default: 3\n"
927 "\n"
928 " --rock-ridge\n"
929 " --limited-rock-ridge\n"
930 " --no-rock-ridge\n"
931 " Enables or disables rock ridge support for the primary ISO 9660\n"
932 " namespace. The --limited-rock-ridge option omits a couple of bits in\n"
933 " the root directory that would make Linux pick rock ridge over joliet.\n"
934 " Default: --limited-rock-ridge"
935 "\n"
936 " -J\n"
937 " --joliet\n"
938 " --no-joliet\n"
939 " Enables or disable the joliet namespace. This option must precede any\n"
940 " file specifications. Default: --joliet\n"
941 "\n"
942 " --joliet-ucs-level <1|2|3>\n"
943 " --ucs-level <1|2|3>\n"
944 " Set the Joliet UCS support level. This is currently only flagged in the\n"
945 " image but not enforced on the actual path names. Default level: 3\n"
946 "\n"
947 "\n"
948 "Options - File attributes:\n"
949 "\n"
950 " --rational-attribs\n"
951 " Enables rational file attribute handling:\n"
952 " * Owner ID is set to zero\n"
953 " * Group ID is set to zero\n"
954 " * Mode is set to 0444 for non-executable files.\n"
955 " * Mode is set to 0555 for executable files.\n"
956 " * Mode is set to 0555 for directories, preserving stick bits.\n"
957 " This is default.\n"
958 "\n"
959 " --strict-attribs\n"
960 " Counters --rational-attribs and causes attributes to be recorded\n"
961 " exactly as they appear in the source.\n"
962 "\n"
963 " --file-mode <mode>\n"
964 " --no-file-mode\n"
965 " Controls the forced file mode mask for rock ridge, UDF and HFS.\n"
966 "\n"
967 " --dir-mode <mode>\n"
968 " --no-dir-mode\n"
969 " Controls the forced directory mode mask for rock ridge, UDF and HFS.\n"
970 "\n"
971 " --new-dir-mode <mode>\n"
972 " Controls the default mode mask (rock ridge, UDF, HFS) for directories\n"
973 " that are created implicitly. The --dir-mode option overrides this.\n"
974 "\n"
975 " --chmod <mode>:<on-iso-file>\n"
976 " Explictily sets the rock ridge, UDF and HFS file mode for a\n"
977 " file/dir/whatever that has already been added to the ISO. The mode can\n"
978 " be octal, a+x, a+r, or a+rx. (Support for more complicated mode\n"
979 " specifications may be implemented at a later point.)\n"
980 " Note that only namespaces in the current --name-setup are affected.\n"
981 "\n"
982 " --chown <owner-id>:<on-iso-file>\n"
983 " Explictily sets the rock ridge, UDF and HFS file owner ID (numeric) for a\n"
984 " file/dir/whatever that has already been added to the ISO.\n"
985 " Note that only namespaces in the current --name-setup are affected.\n"
986 "\n"
987 " --chgrp <group-id>:<on-iso-file>\n"
988 " Explictily sets the rock ridge, UDF and HFS file group ID (numeric) for a\n"
989 " file/dir/whatever that has already been added to the ISO.\n"
990 " Note that only namespaces in the current --name-setup are affected.\n"
991 "\n"
992 "\n"
993 "Options - Booting:\n"
994 "\n"
995 " --eltorito-new-entry\n"
996 " --eltorito-alt-boot\n"
997 " Starts a new El Torito boot entry.\n"
998 "\n"
999 " --eltorito-add-image <filespec>\n"
1000 " File specification of a file that should be added to the image and used\n"
1001 " as the El Torito boot image of the current boot entry.\n"
1002 "\n"
1003 " -b <on-iso-file>\n"
1004 " --eltorito-boot <on-iso-file>\n"
1005 " Specifies a file on the ISO as the El Torito boot image for the current\n"
1006 " boot entry.\n"
1007 "\n"
1008 " --eltorito-floppy-12\n"
1009 " --eltorito-floppy-144\n"
1010 " --eltorito-floppy-288\n"
1011 " --no-emulation-boot\n"
1012 " --hard-disk-boot\n"
1013 " Sets the boot image emulation type of the current El Torito boot entry.\n"
1014 "\n"
1015 " --boot-load-seg <seg>\n"
1016 " Specify the image load segment for the current El Torito boot entry.\n"
1017 " Default: 0x7c0\n"
1018 "\n"
1019 " --boot-load-size <seg>\n"
1020 " Specify the image load size in emulated sectors for the current El Torito\n"
1021 " boot entry. Default: 4 (sectors of 512 bytes)\n"
1022 "\n"
1023 " --no-boot\n"
1024 " Indicates that the current El Torito boot entry isn't bootable. (The\n"
1025 " BIOS will allegedly configure the emulation, but not attempt booting.)\n"
1026 "\n"
1027 " --boot-info-table\n"
1028 " Write a isolinux/syslinux boot info table into the boot image for the\n"
1029 " current El Torito boot entry.\n"
1030 "\n"
1031 " --eltorito-platform-id <id>\n"
1032 " Set the El Torito platform ID of the current entry, a new entry of the\n"
1033 " verification entry depending on when it's used. The ID must be one\n"
1034 " of: x86, PPC, Mac, efi\n"
1035 "\n"
1036 " -c <namespec>\n"
1037 " --boot-catalog <namespec>\n"
1038 " Enters the El Torito boot catalog into the namespaces as a file. The\n"
1039 " 'namespec' uses the same format as a 'filespec', but omits the final\n"
1040 " source file system name component.\n"
1041 "\n"
1042 " -G <file>\n"
1043 " --generic-boot <file>\n"
1044 " Specifies a file that should be loaded at offset 0 in the ISO image.\n"
1045 " The file must not be larger than 32KB. When creating a hybrid image,\n"
1046 " parts of this may be regenerated by partition tables and such.\n"
1047 "\n"
1048 "\n"
1049 "Options - String properties (applied to active namespaces only):\n"
1050 "\n"
1051 " --abstract <file-id>\n"
1052 " The name of the abstract file in the root dir.\n"
1053 "\n"
1054 " -A <text|_file-id>\n"
1055 " --application-id <text|_file-id>\n"
1056 " Application ID string or root file name. The latter must be prefixed\n"
1057 " with an underscore.\n"
1058 "\n"
1059 " --biblio <file-id>\n"
1060 " The name of the bibliographic file in the root dir.\n"
1061 "\n"
1062 " --copyright <file-id>\n"
1063 " The name of the copyright file in the root dir.\n"
1064 "\n"
1065 " -P <text|_file-id>\n"
1066 " --publisher <text|_file-id>\n"
1067 " Publisher ID string or root file name. The latter must be prefixed\n"
1068 " with an underscore.\n"
1069 "\n"
1070 " -p <text|_file-id>\n"
1071 " --preparer <text|_file-id>\n"
1072 " Data preparer ID string or root file name. The latter must be prefixed\n"
1073 " with an underscore.\n"
1074 "\n"
1075 " --sysid <text>\n"
1076 " System ID string.\n"
1077 "\n"
1078 " --volid <text>\n"
1079 " --volume-id <text>\n"
1080 " Volume ID string (label). (It is possible to set different labels for\n"
1081 " primary ISO 9660, joliet, UDF and HFS by changing the active namespaces\n"
1082 " using the --name-setup option between --volume-id occurences.)\n"
1083 "\n"
1084 " --volset <text>\n"
1085 " Volume set ID string.\n"
1086 "\n"
1087 "\n"
1088 "Options - Compatibility:\n"
1089 "\n"
1090 " --graft-points\n"
1091 " Alias for --name-setup iso+joliet+udf+hfs.\n"
1092 "\n"
1093 " -l\n"
1094 " --long-names\n"
1095 " Allow 31 charater filenames. Just ensure ISO level >= 2 here.\n"
1096 "\n"
1097 " -R\n"
1098 " --rock\n"
1099 " Same as --rock-ridge and --strict-attribs.\n"
1100 "\n"
1101 " -r\n"
1102 " --rational-rock\n"
1103 " Same as --rock-ridge and --rational-attribs.\n"
1104 "\n"
1105 "\n"
1106 "Options - VISO specific:\n"
1107 "\n"
1108 " --iprt-iso-maker-file-marker <UUID>\n"
1109 " --iprt-iso-maker-file-marker-bourne <UUID>\n"
1110 " --iprt-iso-maker-file-marker-bourne-sh <UUID>\n"
1111 " Used as first option in a VISO file to specify the file UUID and that\n"
1112 " it is formatted using bourne-shell argument quoting & escaping style.\n"
1113 "\n"
1114 " --iprt-iso-maker-file-marker-ms <UUID>\n"
1115 " --iprt-iso-maker-file-marker-ms-sh <UUID>\n"
1116 " Used as first option in a VISO file to specify the file UUID and that\n"
1117 " it is formatted using microsoft CRT argument quoting & escaping style.\n"
1118 "\n"
1119 "\n"
1120 "Options - Testing (not applicable to VISO):\n"
1121 "\n"
1122 " --output-buffer-size <bytes>\n"
1123 " Selects a specific output buffer size for testing virtual image reads.\n"
1124 "\n"
1125 " --random-output-buffer-size\n"
1126 " Enables randomized buffer size for each virtual image read, using the\n"
1127 " current output buffer size (--output-buffer-size) as maximum.\n"
1128 "\n"
1129 " --random-order-verification <size>\n"
1130 " Enables verification pass of the image that compares blocks of the given\n"
1131 " size in random order from the virtual and output images\n"
1132 "\n"
1133 , RTPathFilename(pszProgName));
1134
1135
1136}
1137
1138
1139/**
1140 * Verifies the image content by reading blocks in random order.
1141 *
1142 * This is for exercise the virtual ISO code better and test that we get the
1143 * same data when reading something twice.
1144 *
1145 * @returns IPRT status code.
1146 * @param pOpts The ISO maker command instance.
1147 * @param hVfsSrcFile The source file (virtual ISO).
1148 * @param hVfsDstFile The destination file (image file on disk).
1149 * @param cbImage The size of the ISO.
1150 */
1151static int rtFsIsoMakerCmdVerifyImageInRandomOrder(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile,
1152 RTVFSFILE hVfsDstFile, uint64_t cbImage)
1153{
1154 /*
1155 * Figure the buffer (block) size and allocate a bitmap for noting down blocks we've covered.
1156 */
1157 int rc;
1158 size_t cbBuf = RT_MAX(pOpts->cbRandomOrderVerifciationBlock, 1);
1159 uint64_t cBlocks64 = (cbImage + cbBuf - 1) / cbBuf;
1160 if (cBlocks64 > _512M)
1161 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_OUT_OF_RANGE,
1162 "verification block count too high: cBlocks=%#RX64 (cbBuf=%#zx), max 512M", cBlocks64, cbBuf);
1163 uint32_t cBlocks = (uint32_t)cBlocks64;
1164 uint32_t cbBitmap = (cBlocks + 63) / 8;
1165 if (cbBitmap > _64M)
1166 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_OUT_OF_RANGE,
1167 "verification bitmap too big: cbBitmap=%#RX32 (cbBuf=%#zx), max 64MB", cbBitmap, cbBuf);
1168 void *pvSrcBuf = RTMemTmpAlloc(cbBuf);
1169 void *pvDstBuf = RTMemTmpAlloc(cbBuf);
1170 void *pvBitmap = RTMemTmpAllocZ(cbBitmap);
1171 if (pvSrcBuf && pvDstBuf && pvBitmap)
1172 {
1173 /* Must set the unused bits in the top qword. */
1174 for (uint32_t i = RT_ALIGN_32(cBlocks, 64) - 1; i >= cBlocks; i--)
1175 ASMBitSet(pvBitmap, i);
1176
1177 /*
1178 * Do the verification.
1179 */
1180 rtFsIsoMakerPrintf(pOpts, "Verifying image in random order using %zu (%#zx) byte blocks: %#RX32 in blocks\n",
1181 cbBuf, cbBuf, cBlocks);
1182
1183 rc = VINF_SUCCESS;
1184 uint64_t cLeft = cBlocks;
1185 while (cLeft-- > 0)
1186 {
1187 /*
1188 * Figure out which block to check next.
1189 */
1190 uint32_t iBlock = RTRandU32Ex(0, cBlocks - 1);
1191 if (!ASMBitTestAndSet(pvBitmap, iBlock))
1192 Assert(iBlock < cBlocks);
1193 else
1194 {
1195 /* try 32 other random numbers. */
1196 bool fBitSet;
1197 unsigned cTries = 0;
1198 do
1199 {
1200 iBlock = RTRandU32Ex(0, cBlocks - 1);
1201 fBitSet = ASMBitTestAndSet(pvBitmap, iBlock);
1202 } while (fBitSet && ++cTries < 32);
1203 if (fBitSet)
1204 {
1205 /* Look for the next clear bit after it (with wrap around). */
1206 int iHit = ASMBitNextClear(pvBitmap, cBlocks, iBlock);
1207 Assert(iHit < (int32_t)cBlocks);
1208 if (iHit < 0)
1209 {
1210 iHit = ASMBitFirstClear(pvBitmap, iBlock);
1211 Assert(iHit < (int32_t)cBlocks);
1212 }
1213 if (iHit >= 0)
1214 {
1215 fBitSet = ASMBitTestAndSet(pvBitmap, iHit);
1216 if (!fBitSet)
1217 iBlock = iHit;
1218 else
1219 {
1220 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_INTERNAL_ERROR_3,
1221 "Bitmap weirdness: iHit=%#x iBlock=%#x cLeft=%#x cBlocks=%#x",
1222 iHit, iBlock, cLeft, cBlocks);
1223 if (!pOpts->pErrInfo)
1224 RTMsgInfo("Bitmap: %#RX32 bytes\n%.*Rhxd", cbBitmap, cbBitmap, pvBitmap);
1225 break;
1226 }
1227 }
1228 else
1229 {
1230 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_INTERNAL_ERROR_2,
1231 "Bitmap weirdness: iBlock=%#x cLeft=%#x cBlocks=%#x",
1232 iBlock, cLeft, cBlocks);
1233 if (!pOpts->pErrInfo)
1234 RTMsgInfo("Bitmap: %#RX32 bytes\n%.*Rhxd", cbBitmap, cbBitmap, pvBitmap);
1235 break;
1236 }
1237 }
1238 }
1239 Assert(ASMBitTest(pvBitmap, iBlock));
1240
1241 /*
1242 * Figure out how much and where to read (last block fun).
1243 */
1244 uint64_t offBlock = iBlock * (uint64_t)cbBuf;
1245 size_t cbToRead = cbBuf;
1246 if (iBlock + 1 < cBlocks)
1247 { /* likely */ }
1248 else if (cbToRead > cbImage - offBlock)
1249 cbToRead = (size_t)(cbImage - offBlock);
1250 Assert(offBlock + cbToRead <= cbImage);
1251
1252 /*
1253 * Read the blocks.
1254 */
1255 //RTPrintf("Reading block #%#x at %#RX64\n", iBlock, offBlock);
1256 rc = RTVfsFileReadAt(hVfsDstFile, offBlock, pvDstBuf, cbToRead, NULL);
1257 if (RT_SUCCESS(rc))
1258 {
1259 memset(pvSrcBuf, 0xdd, cbBuf);
1260 rc = RTVfsFileReadAt(hVfsSrcFile, offBlock, pvSrcBuf, cbToRead, NULL);
1261 if (RT_SUCCESS(rc))
1262 {
1263 if (memcmp(pvDstBuf, pvSrcBuf, cbToRead) == 0)
1264 continue;
1265 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_MISMATCH,
1266 "Block #%#x differs! offBlock=%#RX64 cbToRead=%#zu\n"
1267 "Virtual ISO (source):\n%.*Rhxd\nWritten ISO (destination):\n%.*Rhxd",
1268 iBlock, offBlock, cbToRead, cbToRead, pvSrcBuf, cbToRead, pvDstBuf);
1269 }
1270 else
1271 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
1272 "Error reading %#zx bytes source (virtual ISO) block #%#x at %#RX64: %Rrc",
1273 cbToRead, iBlock, offBlock, rc);
1274 }
1275 else
1276 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
1277 "Error reading %#zx bytes destination (written ISO) block #%#x at %#RX64: %Rrc",
1278 cbToRead, iBlock, offBlock, rc);
1279 break;
1280 }
1281
1282 if (RT_SUCCESS(rc))
1283 rtFsIsoMakerPrintf(pOpts, "Written image verified fine!\n");
1284 }
1285 else if (!pvSrcBuf || !pvDstBuf)
1286 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbBuf);
1287 else
1288 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbBuf);
1289 RTMemTmpFree(pvBitmap);
1290 RTMemTmpFree(pvDstBuf);
1291 RTMemTmpFree(pvSrcBuf);
1292 return rc;
1293}
1294
1295
1296/**
1297 * Writes the image to file, no checking, no special buffering.
1298 *
1299 * @returns IPRT status code.
1300 * @param pOpts The ISO maker command instance.
1301 * @param hVfsSrcFile The source file from the ISO maker.
1302 * @param hVfsDstFile The destination file (image file on disk).
1303 * @param cbImage The size of the ISO.
1304 * @param ppvBuf Pointer to the buffer pointer. The buffer will
1305 * be reallocated, but we want the luxary of the
1306 * caller freeing it.
1307 */
1308static int rtFsIsoMakerCmdWriteImageRandomBufferSize(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile, RTVFSFILE hVfsDstFile,
1309 uint64_t cbImage, void **ppvBuf)
1310{
1311 /*
1312 * Copy the virtual image bits to the destination file.
1313 */
1314 void *pvBuf = *ppvBuf;
1315 uint32_t cbMaxBuf = pOpts->cbOutputReadBuffer > 0 ? pOpts->cbOutputReadBuffer : _64K;
1316 uint64_t offImage = 0;
1317 while (offImage < cbImage)
1318 {
1319 /* Figure out how much to copy this time. */
1320 size_t cbToCopy = RTRandU32Ex(1, cbMaxBuf - 1);
1321 if (offImage + cbToCopy < cbImage)
1322 { /* likely */ }
1323 else
1324 cbToCopy = (size_t)(cbImage - offImage);
1325 RTMemFree(pvBuf);
1326 *ppvBuf = pvBuf = RTMemTmpAlloc(cbToCopy);
1327 if (pvBuf)
1328 {
1329 /* Do the copying. */
1330 int rc = RTVfsFileReadAt(hVfsSrcFile, offImage, pvBuf, cbToCopy, NULL);
1331 if (RT_SUCCESS(rc))
1332 {
1333 rc = RTVfsFileWriteAt(hVfsDstFile, offImage, pvBuf, cbToCopy, NULL);
1334 if (RT_SUCCESS(rc))
1335 offImage += cbToCopy;
1336 else
1337 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc writing %#zx bytes at offset %#RX64 to '%s'",
1338 rc, cbToCopy, offImage, pOpts->pszOutFile);
1339 }
1340 else
1341 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc read %#zx bytes at offset %#RX64", rc, cbToCopy, offImage);
1342 }
1343 else
1344 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%#zx) failed", cbToCopy);
1345 }
1346 return VINF_SUCCESS;
1347}
1348
1349
1350/**
1351 * Writes the image to file, no checking, no special buffering.
1352 *
1353 * @returns IPRT status code.
1354 * @param pOpts The ISO maker command instance.
1355 * @param hVfsSrcFile The source file from the ISO maker.
1356 * @param hVfsDstFile The destination file (image file on disk).
1357 * @param cbImage The size of the ISO.
1358 * @param pvBuf Pointer to read buffer.
1359 * @param cbBuf The buffer size.
1360 */
1361static int rtFsIsoMakerCmdWriteImageSimple(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile, RTVFSFILE hVfsDstFile,
1362 uint64_t cbImage, void *pvBuf, size_t cbBuf)
1363{
1364 /*
1365 * Copy the virtual image bits to the destination file.
1366 */
1367 uint64_t offImage = 0;
1368 while (offImage < cbImage)
1369 {
1370 /* Figure out how much to copy this time. */
1371 size_t cbToCopy = cbBuf;
1372 if (offImage + cbToCopy < cbImage)
1373 { /* likely */ }
1374 else
1375 cbToCopy = (size_t)(cbImage - offImage);
1376
1377 /* Do the copying. */
1378 int rc = RTVfsFileReadAt(hVfsSrcFile, offImage, pvBuf, cbToCopy, NULL);
1379 if (RT_SUCCESS(rc))
1380 {
1381 rc = RTVfsFileWriteAt(hVfsDstFile, offImage, pvBuf, cbToCopy, NULL);
1382 if (RT_SUCCESS(rc))
1383 offImage += cbToCopy;
1384 else
1385 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc writing %#zx bytes at offset %#RX64 to '%s'",
1386 rc, cbToCopy, offImage, pOpts->pszOutFile);
1387 }
1388 else
1389 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc read %#zx bytes at offset %#RX64", rc, cbToCopy, offImage);
1390 }
1391 return VINF_SUCCESS;
1392}
1393
1394
1395/**
1396 * Writes the image to file.
1397 *
1398 * @returns IPRT status code.
1399 * @param pOpts The ISO maker command instance.
1400 * @param hVfsSrcFile The source file from the ISO maker.
1401 */
1402static int rtFsIsoMakerCmdWriteImage(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile)
1403{
1404 /*
1405 * Get the image size and setup the copy buffer.
1406 */
1407 uint64_t cbImage;
1408 int rc = RTVfsFileGetSize(hVfsSrcFile, &cbImage);
1409 if (RT_SUCCESS(rc))
1410 {
1411 rtFsIsoMakerPrintf(pOpts, "Image size: %'RU64 (%#RX64) bytes\n", cbImage, cbImage);
1412
1413 uint32_t cbBuf = pOpts->cbOutputReadBuffer == 0 ? _1M : pOpts->cbOutputReadBuffer;
1414 void *pvBuf = RTMemTmpAlloc(cbBuf);
1415 if (pvBuf)
1416 {
1417 /*
1418 * Open the output file.
1419 */
1420 RTVFSFILE hVfsDstFile;
1421 uint32_t offError;
1422 RTERRINFOSTATIC ErrInfo;
1423 rc = RTVfsChainOpenFile(pOpts->pszOutFile, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE,
1424 &hVfsDstFile, &offError, RTErrInfoInitStatic(&ErrInfo));
1425 if (RT_SUCCESS(rc))
1426 {
1427 /*
1428 * Apply the desired writing method.
1429 */
1430 if (!pOpts->fRandomOutputReadBufferSize)
1431 rc = rtFsIsoMakerCmdWriteImageRandomBufferSize(pOpts, hVfsSrcFile, hVfsDstFile, cbImage, &pvBuf);
1432 else
1433 rc = rtFsIsoMakerCmdWriteImageSimple(pOpts, hVfsSrcFile, hVfsDstFile, cbImage, pvBuf, cbBuf);
1434 RTMemTmpFree(pvBuf);
1435
1436 if (RT_SUCCESS(rc) && pOpts->cbRandomOrderVerifciationBlock > 0)
1437 rc = rtFsIsoMakerCmdVerifyImageInRandomOrder(pOpts, hVfsSrcFile, hVfsDstFile, cbImage);
1438
1439 /*
1440 * Flush the output file before releasing it.
1441 */
1442 if (RT_SUCCESS(rc))
1443 {
1444 rc = RTVfsFileFlush(hVfsDstFile);
1445 if (RT_FAILURE(rc))
1446 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileFlush failed on '%s': %Rrc", pOpts->pszOutFile, rc);
1447 }
1448
1449 RTVfsFileRelease(hVfsDstFile);
1450 }
1451 else
1452 {
1453 RTMemTmpFree(pvBuf);
1454 rc = rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pOpts->pszOutFile, rc, offError, &ErrInfo.Core);
1455 }
1456 }
1457 else
1458 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "RTMemTmpAlloc(%zu) failed", cbBuf);
1459 }
1460 else
1461 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileGetSize failed: %Rrc", rc);
1462 return rc;
1463}
1464
1465
1466/**
1467 * Formats @a fNameSpecifiers into a '+' separated list of names.
1468 *
1469 * @returns pszDst
1470 * @param fNameSpecifiers The name specifiers.
1471 * @param pszDst The destination bufer.
1472 * @param cbDst The size of the destination buffer.
1473 */
1474static char *rtFsIsoMakerCmdNameSpecifiersToString(uint32_t fNameSpecifiers, char *pszDst, size_t cbDst)
1475{
1476 static struct { const char *pszName; uint32_t cchName; uint32_t fSpec; } const s_aSpecs[] =
1477 {
1478 { RT_STR_TUPLE("primary"), RTFSISOMAKERCMDNAME_PRIMARY_ISO },
1479 { RT_STR_TUPLE("primary-rock"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE },
1480 { RT_STR_TUPLE("primary-trans-tbl"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL },
1481 { RT_STR_TUPLE("joliet"), RTFSISOMAKERCMDNAME_JOLIET },
1482 { RT_STR_TUPLE("joliet-rock"), RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE },
1483 { RT_STR_TUPLE("joliet-trans-tbl"), RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL },
1484 { RT_STR_TUPLE("udf"), RTFSISOMAKERCMDNAME_UDF },
1485 { RT_STR_TUPLE("udf-trans-tbl"), RTFSISOMAKERCMDNAME_UDF_TRANS_TBL },
1486 { RT_STR_TUPLE("hfs"), RTFSISOMAKERCMDNAME_HFS },
1487 { RT_STR_TUPLE("hfs-trans-tbl"), RTFSISOMAKERCMDNAME_HFS_TRANS_TBL },
1488 };
1489
1490 Assert(cbDst > 0);
1491 char *pszRet = pszDst;
1492 for (uint32_t i = 0; i < RT_ELEMENTS(s_aSpecs); i++)
1493 if (s_aSpecs[i].fSpec & fNameSpecifiers)
1494 {
1495 if (pszDst != pszRet && cbDst > 1)
1496 {
1497 *pszDst++ = '+';
1498 cbDst--;
1499 }
1500 if (cbDst > s_aSpecs[i].cchName)
1501 {
1502 memcpy(pszDst, s_aSpecs[i].pszName, s_aSpecs[i].cchName);
1503 cbDst -= s_aSpecs[i].cchName;
1504 pszDst += s_aSpecs[i].cchName;
1505 }
1506 else if (cbDst > 1)
1507 {
1508 memcpy(pszDst, s_aSpecs[i].pszName, cbDst - 1);
1509 pszDst += cbDst - 1;
1510 cbDst = 1;
1511 }
1512
1513 fNameSpecifiers &= ~s_aSpecs[i].fSpec;
1514 if (!fNameSpecifiers)
1515 break;
1516 }
1517 *pszDst = '\0';
1518 return pszRet;
1519}
1520
1521
1522/**
1523 * Parses the --name-setup option.
1524 *
1525 * @returns IPRT status code.
1526 * @param pOpts The ISO maker command instance.
1527 * @param pszSpec The name setup specification.
1528 */
1529static int rtFsIsoMakerCmdOptNameSetup(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
1530{
1531 /*
1532 * Comma separated list of one or more specifiers.
1533 */
1534 uint32_t fNamespaces = 0;
1535 uint32_t fPrevMajor = 0;
1536 uint32_t iNameSpecifier = 0;
1537 uint32_t offSpec = 0;
1538 do
1539 {
1540 /*
1541 * Parse up to the next colon or end of string.
1542 */
1543 uint32_t fNameSpecifier = 0;
1544 char ch;
1545 while ( (ch = pszSpec[offSpec]) != '\0'
1546 && ch != ',')
1547 {
1548 if (RT_C_IS_SPACE(ch) || ch == '+' || ch == '|') /* space, '+' and '|' are allowed as name separators. */
1549 offSpec++;
1550 else
1551 {
1552 /* Find the end of the name. */
1553 uint32_t offEndSpec = offSpec + 1;
1554 while ( (ch = pszSpec[offEndSpec]) != '\0'
1555 && ch != ','
1556 && ch != '+'
1557 && ch != '|'
1558 && !RT_C_IS_SPACE(ch))
1559 offEndSpec++;
1560
1561#define IS_EQUAL(a_sz) (cchName == sizeof(a_sz) - 1U && strncmp(pchName, a_sz, sizeof(a_sz) - 1U) == 0)
1562 const char * const pchName = &pszSpec[offSpec];
1563 uint32_t const cchName = offEndSpec - offSpec;
1564 /* major namespaces */
1565 if ( IS_EQUAL("iso")
1566 || IS_EQUAL("primary")
1567 || IS_EQUAL("iso9660")
1568 || IS_EQUAL("iso-9660")
1569 || IS_EQUAL("primary-iso")
1570 || IS_EQUAL("iso-primary") )
1571 {
1572 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO;
1573 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_ISO_9660;
1574 }
1575 else if (IS_EQUAL("joliet"))
1576 {
1577 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET;
1578 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_JOLIET;
1579 }
1580 else if (IS_EQUAL("udf"))
1581 {
1582#if 0
1583 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF;
1584 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_UDF;
1585#else
1586 return rtFsIsoMakerCmdSyntaxError(pOpts, "UDF support is currently not implemented");
1587#endif
1588 }
1589 else if (IS_EQUAL("hfs") || IS_EQUAL("hfsplus"))
1590 {
1591#if 0
1592 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS;
1593 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_HFS;
1594#else
1595 return rtFsIsoMakerCmdSyntaxError(pOpts, "Hybrid HFS+ support is currently not implemented");
1596#endif
1597 }
1598 /* rock ridge */
1599 else if ( IS_EQUAL("rr")
1600 || IS_EQUAL("rock")
1601 || IS_EQUAL("rock-ridge"))
1602 {
1603 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
1604 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
1605 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
1606 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
1607 else
1608 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified rock-ridge name specifier");
1609 }
1610 else if ( IS_EQUAL("iso-rr") || IS_EQUAL("iso-rock") || IS_EQUAL("iso-rock-ridge")
1611 || IS_EQUAL("primary-rr") || IS_EQUAL("primary-rock") || IS_EQUAL("primary-rock-ridge")
1612 || IS_EQUAL("iso9660-rr") || IS_EQUAL("iso9660-rock") || IS_EQUAL("iso9660-rock-ridge")
1613 || IS_EQUAL("iso-9660-rr") || IS_EQUAL("iso-9660-rock") || IS_EQUAL("iso-9660-rock-ridge")
1614 || IS_EQUAL("primaryiso-rr") || IS_EQUAL("primaryiso-rock") || IS_EQUAL("primaryiso-rock-ridge")
1615 || IS_EQUAL("primary-iso-rr") || IS_EQUAL("primary-iso-rock") || IS_EQUAL("primary-iso-rock-ridge") )
1616 {
1617 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
1618 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
1619 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-rock-ridge must come after the iso-9660 name specifier");
1620 }
1621 else if (IS_EQUAL("joliet-rr") || IS_EQUAL("joliet-rock") || IS_EQUAL("joliet-rock-ridge"))
1622 {
1623 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
1624 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
1625 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-rock-ridge must come after the joliet name specifier");
1626 }
1627 /* trans.tbl */
1628 else if (IS_EQUAL("trans") || IS_EQUAL("trans-tbl"))
1629 {
1630 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
1631 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
1632 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
1633 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
1634 else
1635 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified trans-tbl name specifier");
1636 }
1637 else if ( IS_EQUAL("iso-trans") || IS_EQUAL("iso-trans-tbl")
1638 || IS_EQUAL("primary-trans") || IS_EQUAL("primary-trans-tbl")
1639 || IS_EQUAL("iso9660-trans") || IS_EQUAL("iso9660-trans-tbl")
1640 || IS_EQUAL("iso-9660-trans") || IS_EQUAL("iso-9660-trans-tbl")
1641 || IS_EQUAL("primaryiso-trans") || IS_EQUAL("primaryiso-trans-tbl")
1642 || IS_EQUAL("primary-iso-trans") || IS_EQUAL("primary-iso-trans-tbl") )
1643 {
1644 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
1645 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
1646 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-trans-tbl must come after the iso-9660 name specifier");
1647 }
1648 else if (IS_EQUAL("joliet-trans") || IS_EQUAL("joliet-trans-tbl"))
1649 {
1650 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
1651 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
1652 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-trans-tbl must come after the joliet name specifier");
1653 }
1654 else if (IS_EQUAL("udf-trans") || IS_EQUAL("udf-trans-tbl"))
1655 {
1656 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF_TRANS_TBL;
1657 if (!(fNamespaces & RTFSISOMAKERCMDNAME_UDF))
1658 return rtFsIsoMakerCmdSyntaxError(pOpts, "udf-trans-tbl must come after the udf name specifier");
1659 }
1660 else if (IS_EQUAL("hfs-trans") || IS_EQUAL("hfs-trans-tbl"))
1661 {
1662 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS_TRANS_TBL;
1663 if (!(fNamespaces & RTFSISOMAKERCMDNAME_HFS))
1664 return rtFsIsoMakerCmdSyntaxError(pOpts, "hfs-trans-tbl must come after the hfs name specifier");
1665 }
1666 else
1667 return rtFsIsoMakerCmdSyntaxError(pOpts, "unknown name specifier '%.*s'", cchName, pchName);
1668#undef IS_EQUAL
1669 offSpec = offEndSpec;
1670 }
1671 } /* while same specifier */
1672
1673 /*
1674 * Check that it wasn't empty.
1675 */
1676 if (fNameSpecifier == 0)
1677 return rtFsIsoMakerCmdSyntaxError(pOpts, "name specifier #%u (0-based) is empty ", iNameSpecifier);
1678
1679 /*
1680 * Complain if a major namespace name is duplicated. The rock-ridge and
1681 * trans.tbl names are simple to replace, the others affect the two former
1682 * names and are therefore not allowed twice in the list.
1683 */
1684 uint32_t i = iNameSpecifier;
1685 while (i-- > 0)
1686 {
1687 uint32_t fRepeated = (fNameSpecifier & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1688 & (pOpts->afNameSpecifiers[i] & RTFSISOMAKERCMDNAME_MAJOR_MASK);
1689 if (fRepeated)
1690 {
1691 char szTmp[128];
1692 return rtFsIsoMakerCmdSyntaxError(pOpts, "repeating name specifier%s: %s", RT_IS_POWER_OF_TWO(fRepeated) ? "" : "s",
1693 rtFsIsoMakerCmdNameSpecifiersToString(fRepeated, szTmp, sizeof(szTmp)));
1694 }
1695 }
1696
1697 /*
1698 * Add it.
1699 */
1700 if (iNameSpecifier >= RT_ELEMENTS(pOpts->afNameSpecifiers))
1701 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many name specifiers (max %d)", RT_ELEMENTS(pOpts->afNameSpecifiers));
1702 pOpts->afNameSpecifiers[iNameSpecifier] = fNameSpecifier;
1703 iNameSpecifier++;
1704
1705 /*
1706 * Next, if any.
1707 */
1708 if (pszSpec[offSpec] == ',')
1709 offSpec++;
1710 } while (pszSpec[offSpec] != '\0');
1711
1712 pOpts->cNameSpecifiers = iNameSpecifier;
1713 pOpts->fDstNamespaces = fNamespaces;
1714
1715 return VINF_SUCCESS;
1716}
1717
1718
1719/**
1720 * Processes a non-option argument.
1721 *
1722 * @returns IPRT status code.
1723 * @param pOpts The ISO maker command instance.
1724 * @param pszSpec The specification of what to add.
1725 * @param fWithSrc Whether the specification includes a source path
1726 * or not.
1727 * @param pParsed Where to return the parsed name specification.
1728 */
1729static int rtFsIsoMakerCmdParseNameSpec(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec, bool fWithSrc,
1730 PRTFSISOMKCMDPARSEDNAMES pParsed)
1731{
1732 const char * const pszSpecIn = pszSpec;
1733 uint32_t const cMaxNames = pOpts->cNameSpecifiers + fWithSrc;
1734
1735 /*
1736 * Split it up by '='.
1737 */
1738 pParsed->cNames = 0;
1739 pParsed->cNamesWithSrc = 0;
1740 pParsed->enmSrcType = fWithSrc ? RTFSISOMKCMDPARSEDNAMES::kSrcType_Normal : RTFSISOMKCMDPARSEDNAMES::kSrcType_None;
1741 for (;;)
1742 {
1743 const char *pszEqual = strchr(pszSpec, '=');
1744 size_t cchName = pszEqual ? pszEqual - pszSpec : strlen(pszSpec);
1745 bool fNeedSlash = (pszEqual || !fWithSrc) && !RTPATH_IS_SLASH(*pszSpec) && cchName > 0;
1746 if (cchName + fNeedSlash >= sizeof(pParsed->aNames[pParsed->cNamesWithSrc].szPath))
1747 return rtFsIsoMakerCmdSyntaxError(pOpts, "name #%u (0-based) is too long: %s", pParsed->cNamesWithSrc, pszSpecIn);
1748 if (pParsed->cNamesWithSrc >= cMaxNames)
1749 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many names specified (max %u%s): %s",
1750 pOpts->cNameSpecifiers, fWithSrc ? " + source" : "", pszSpecIn);
1751 if (!fNeedSlash)
1752 memcpy(pParsed->aNames[pParsed->cNamesWithSrc].szPath, pszSpec, cchName);
1753 else
1754 {
1755 memcpy(&pParsed->aNames[pParsed->cNamesWithSrc].szPath[1], pszSpec, cchName);
1756 pParsed->aNames[pParsed->cNamesWithSrc].szPath[0] = RTPATH_SLASH;
1757 cchName++;
1758 }
1759 pParsed->aNames[pParsed->cNamesWithSrc].szPath[cchName] = '\0';
1760 pParsed->aNames[pParsed->cNamesWithSrc].cchPath = (uint32_t)cchName;
1761 pParsed->cNamesWithSrc++;
1762
1763 if (!pszEqual)
1764 {
1765 if (fWithSrc)
1766 {
1767 if (!cchName)
1768 return rtFsIsoMakerCmdSyntaxError(pOpts, "empty source file name: %s", pszSpecIn);
1769 if (cchName == 8 && strcmp(pszSpec, ":remove:") == 0)
1770 pParsed->enmSrcType = RTFSISOMKCMDPARSEDNAMES::kSrcType_Remove;
1771 else if (cchName == 13 && strcmp(pszSpec, ":must-remove:") == 0)
1772 pParsed->enmSrcType = RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove;
1773 }
1774 break;
1775 }
1776 pszSpec = pszEqual + 1;
1777 }
1778
1779 /*
1780 * If there are too few names specified, move the source and repeat the
1781 * last non-source name. If only source, convert source into a name spec.
1782 */
1783 if (pParsed->cNamesWithSrc < cMaxNames)
1784 {
1785 uint32_t iSrc;
1786 if (!fWithSrc)
1787 iSrc = pParsed->cNamesWithSrc - 1;
1788 else
1789 {
1790 pParsed->aNames[pOpts->cNameSpecifiers] = pParsed->aNames[pParsed->cNamesWithSrc - 1];
1791 iSrc = pParsed->cNamesWithSrc >= 2 ? pParsed->cNamesWithSrc - 2 : 0;
1792 }
1793
1794 /* If the source is a input file name specifier, reduce it to something that starts with a slash. */
1795 if (pParsed->cNamesWithSrc == 1 && fWithSrc)
1796 {
1797 const char *pszSrc = pParsed->aNames[iSrc].szPath;
1798 char *pszFinalPath = NULL;
1799 if (RTVfsChainIsSpec(pParsed->aNames[iSrc].szPath))
1800 {
1801 uint32_t offError;
1802 int rc = RTVfsChainQueryFinalPath(pParsed->aNames[iSrc].szPath, &pszFinalPath, &offError);
1803 if (RT_FAILURE(rc))
1804 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryFinalPath",
1805 pParsed->aNames[iSrc].szPath, rc, offError, NULL);
1806 pszSrc = pszFinalPath;
1807 }
1808
1809 /* Find the start of the last component, ignoring trailing slashes. */
1810 size_t cchSrc = strlen(pszSrc);
1811 size_t offLast = cchSrc;
1812 while (offLast > 0 && RTPATH_IS_SLASH(pszSrc[offLast - 1]))
1813 offLast--;
1814 while (offLast > 0 && !RTPATH_IS_SLASH(pszSrc[offLast - 1]))
1815 offLast--;
1816
1817 /* Move it up front with a leading slash. */
1818 if (offLast > 0 || !RTPATH_IS_SLASH(*pszSrc))
1819 {
1820 pParsed->aNames[iSrc].cchPath = 1 + (uint32_t)(cchSrc - offLast);
1821 if (pParsed->aNames[iSrc].cchPath >= sizeof(pParsed->aNames[iSrc].szPath))
1822 return rtFsIsoMakerCmdSyntaxError(pOpts, "name too long: %s", pszSpecIn);
1823
1824 memmove(&pParsed->aNames[iSrc].szPath[1], &pszSrc[offLast], pParsed->aNames[iSrc].cchPath);
1825 }
1826 else
1827 pParsed->aNames[iSrc].cchPath = 1;
1828 pParsed->aNames[iSrc].szPath[0] = RTPATH_SLASH;
1829
1830 if (pszFinalPath)
1831 RTStrFree(pszFinalPath);
1832 }
1833
1834 for (uint32_t iDst = iSrc + 1; iDst < pOpts->cNameSpecifiers; iDst++)
1835 pParsed->aNames[iDst] = pParsed->aNames[iSrc];
1836
1837 pParsed->cNamesWithSrc = cMaxNames;
1838 }
1839 pParsed->cNames = pOpts->cNameSpecifiers;
1840
1841 /*
1842 * Copy the specifier flags and check that the paths all starts with slashes.
1843 */
1844 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
1845 {
1846 pParsed->aNames[i].fNameSpecifiers = pOpts->afNameSpecifiers[i];
1847 Assert( pParsed->aNames[i].cchPath == 0
1848 || RTPATH_IS_SLASH(pParsed->aNames[i].szPath[0]));
1849 }
1850
1851 return VINF_SUCCESS;
1852}
1853
1854
1855/**
1856 * Enteres an object into the namespace by full paths.
1857 *
1858 * This is used by rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath and
1859 * rtFsIsoMakerCmdAddFile.
1860 *
1861 * @returns IPRT status code.
1862 * @param pOpts The ISO maker command instance.
1863 * @param idxObj The configuration index of the object to be named.
1864 * @param pParsed The parsed names.
1865 * @param pszSrcOrName Source file or name.
1866 */
1867static int rtFsIsoMakerCmdSetObjPaths(PRTFSISOMAKERCMDOPTS pOpts, uint32_t idxObj, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1868 const char *pszSrcOrName)
1869{
1870 int rc = VINF_SUCCESS;
1871 for (uint32_t i = 0; i < pParsed->cNames; i++)
1872 if (pParsed->aNames[i].cchPath > 0)
1873 {
1874 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
1875 {
1876 rc = RTFsIsoMakerObjSetPath(pOpts->hIsoMaker, idxObj,
1877 pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1878 pParsed->aNames[i].szPath);
1879 if (RT_FAILURE(rc))
1880 {
1881 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting name '%s' on '%s': %Rrc",
1882 pParsed->aNames[i].szPath, pszSrcOrName, rc);
1883 break;
1884 }
1885 }
1886 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MINOR_MASK)
1887 {
1888 /** @todo add APIs for this. */
1889 }
1890 }
1891 return rc;
1892}
1893
1894
1895/**
1896 * Adds a file.
1897 *
1898 * @returns IPRT status code.
1899 * @param pOpts The ISO maker command instance.
1900 * @param pszSrc The path to the source file.
1901 * @param pParsed The parsed names.
1902 * @param pidxObj Where to return the configuration index for the
1903 * added file. Optional.
1904 */
1905static int rtFsIsoMakerCmdAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
1906 uint32_t *pidxObj)
1907{
1908 int rc;
1909 uint32_t idxObj = UINT32_MAX;
1910 if ( pOpts->hSrcVfs == NIL_RTVFS
1911 || RTVfsChainIsSpec(pszSrc))
1912 {
1913 rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(pOpts->hIsoMaker, pszSrc, &idxObj);
1914 if (RT_FAILURE(rc))
1915 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s': %Rrc", pszSrc, rc);
1916 }
1917 else
1918 {
1919 RTVFSFILE hVfsFileSrc;
1920 rc = RTVfsFileOpen(pOpts->hSrcVfs, pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSrc);
1921 if (RT_FAILURE(rc))
1922 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening '%s' (inside '%s'): %Rrc", pszSrc, pOpts->pszSrcVfs, rc);
1923
1924 rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(pOpts->hIsoMaker, hVfsFileSrc, &idxObj);
1925 RTVfsFileRelease(hVfsFileSrc);
1926 if (RT_FAILURE(rc))
1927 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s' (VFS): %Rrc", pszSrc, rc);
1928 }
1929
1930 pOpts->cItemsAdded++;
1931 if (pidxObj)
1932 *pidxObj = idxObj;
1933
1934 return rtFsIsoMakerCmdSetObjPaths(pOpts, idxObj, pParsed, pszSrc);
1935}
1936
1937
1938/**
1939 * Applies filtering rules.
1940 *
1941 * @returns true if filtered out, false if included.
1942 * @param pOpts The ISO maker command instance.
1943 * @param pszSrc The source source.
1944 * @param pszName The name part (maybe different buffer from pszSrc).
1945 * @param fIsDir Set if directory, clear if not.
1946 */
1947static bool rtFsIsoMakerCmdIsFilteredOut(PRTFSISOMAKERCMDOPTS pOpts, const char *pszDir, const char *pszName, bool fIsDir)
1948{
1949 /* Ignore trans.tbl files. */
1950 if ( !fIsDir
1951 && RTStrICmp(pszName, pOpts->pszTransTbl) == 0)
1952 return true;
1953
1954 RT_NOREF(pOpts, pszDir, pszName, fIsDir);
1955 return false;
1956}
1957
1958
1959/**
1960 * Adds a directory, from a VFS chain or real file system.
1961 *
1962 * @returns IPRT status code.
1963 * @param pOpts The ISO maker command instance.
1964 * @param pszSrc The path to the source directory.
1965 * @param pParsed The parsed names.
1966 */
1967static int rtFsIsoMakerCmdAddDir(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed)
1968{
1969 RT_NOREF(pParsed);
1970 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding directory '%s' failed: not implemented", pszSrc);
1971}
1972
1973
1974/**
1975 * Worker for rtFsIsoMakerCmdAddVfsDir that does the recursion.
1976 *
1977 * @returns IPRT status code.
1978 * @param pOpts The ISO maker command instance.
1979 * @param hVfsDir The directory to process.
1980 * @param idxDirObj The configuration index of the directory.
1981 * @param pszSrc Pointer to the source path buffer. RTPATH_MAX
1982 * in size. Okay to modify beyond @a cchSrc.
1983 * @param cchSrc Length of the path corresponding to @a hVfsDir.
1984 * @param fNamespaces Which ISO maker namespaces to add the names to.
1985 * @param cDepth Number of recursions. Used to deal with loopy
1986 * directories.
1987 */
1988static int rtFsIsoMakerCmdAddVfsDirRecursive(PRTFSISOMAKERCMDOPTS pOpts, RTVFSDIR hVfsDir, uint32_t idxDirObj,
1989 char *pszSrc, size_t cchSrc, uint32_t fNamespaces, uint8_t cDepth)
1990{
1991 /*
1992 * Check that we're not in too deep.
1993 */
1994 if (cDepth >= RTFSISOMAKERCMD_MAX_DIR_RECURSIONS)
1995 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE,
1996 "Recursive (VFS) dir add too deep (depth=%u): %.*s", cDepth, cchSrc, pszSrc);
1997 /*
1998 * Enumerate the directory.
1999 */
2000 int rc;
2001 size_t cbDirEntryAlloced = sizeof(RTDIRENTRYEX);
2002 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
2003 if (pDirEntry)
2004 {
2005 for (;;)
2006 {
2007 /*
2008 * Read the next entry.
2009 */
2010 size_t cbDirEntry = cbDirEntryAlloced;
2011 rc = RTVfsDirReadEx(hVfsDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
2012 if (RT_FAILURE(rc))
2013 {
2014 if (rc == VERR_NO_MORE_FILES)
2015 rc = VINF_SUCCESS;
2016 else if (rc == VERR_BUFFER_OVERFLOW)
2017 {
2018 RTMemTmpFree(pDirEntry);
2019 cbDirEntryAlloced = RT_ALIGN_Z(RT_MIN(cbDirEntry, cbDirEntryAlloced) + 64, 64);
2020 pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
2021 if (pDirEntry)
2022 continue;
2023 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "Out of memory (direntry buffer)");
2024 }
2025 else
2026 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsDirReadEx failed on %.*s: %Rrc", cchSrc, pszSrc, rc);
2027 break;
2028 }
2029
2030 /* Ignore '.' and '..' entries. */
2031 if (RTDirEntryExIsStdDotLink(pDirEntry))
2032 continue;
2033
2034 /*
2035 * Process the entry.
2036 */
2037
2038 /* Update the name. */
2039 if (cchSrc + 1 + pDirEntry->cbName < RTPATH_MAX)
2040 {
2041 pszSrc[cchSrc] = '/'; /* VFS only groks unix slashes */
2042 memcpy(&pszSrc[cchSrc + 1], pDirEntry->szName, pDirEntry->cbName);
2043 pszSrc[cchSrc + 1 + pDirEntry->cbName] = '\0';
2044 }
2045 else
2046 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_FILENAME_TOO_LONG, "Filename is too long (depth %u): '%.*s/%s'",
2047 cDepth, cchSrc, pszSrc, pDirEntry->szName);
2048
2049 /* Okay? Check name filtering. */
2050 if ( RT_SUCCESS(rc)
2051 && !rtFsIsoMakerCmdIsFilteredOut(pOpts, pszSrc, pDirEntry->szName, RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode)))
2052 {
2053 /* Do type specific adding. */
2054 uint32_t idxObj = UINT32_MAX;
2055 if (RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
2056 {
2057 /*
2058 * Files are added with VFS file sources.
2059 * The ASSUMPTION is that we're working with a virtual file system
2060 * here and won't be wasting native file descriptors.
2061 */
2062 RTVFSFILE hVfsFileSrc;
2063 rc = RTVfsDirOpenFile(hVfsDir, pDirEntry->szName,
2064 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSrc);
2065 if (RT_SUCCESS(rc))
2066 {
2067 rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(pOpts->hIsoMaker, hVfsFileSrc, &idxObj);
2068 RTVfsFileRelease(hVfsFileSrc);
2069 if (RT_SUCCESS(rc))
2070 {
2071 pOpts->cItemsAdded++;
2072 rc = RTFsIsoMakerObjSetNameAndParent(pOpts->hIsoMaker, idxObj, idxDirObj, fNamespaces,
2073 pDirEntry->szName);
2074 if (RT_FAILURE(rc))
2075 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting parent & name on file '%s' to '%s': %Rrc",
2076 pszSrc, pDirEntry->szName, rc);
2077 }
2078 else
2079 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding file '%s' (VFS recursive): %Rrc", pszSrc, rc);
2080 }
2081 else
2082 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening file '%s' (VFS recursive): %Rrc", pszSrc, rc);
2083 }
2084 else if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
2085 {
2086 /*
2087 * Open and add the sub-directory.
2088 */
2089 RTVFSDIR hVfsSubDirSrc;
2090 rc = RTVfsDirOpenDir(hVfsDir, pDirEntry->szName, 0 /*fFlags*/, &hVfsSubDirSrc);
2091 if (RT_SUCCESS(rc))
2092 {
2093 rc = RTFsIsoMakerAddUnnamedDir(pOpts->hIsoMaker, &pDirEntry->Info, &idxObj);
2094 if (RT_SUCCESS(rc))
2095 {
2096 pOpts->cItemsAdded++;
2097 rc = RTFsIsoMakerObjSetNameAndParent(pOpts->hIsoMaker, idxObj, idxDirObj, fNamespaces,
2098 pDirEntry->szName);
2099 if (RT_SUCCESS(rc))
2100 /* Recurse into the sub-directory. */
2101 rc = rtFsIsoMakerCmdAddVfsDirRecursive(pOpts, hVfsSubDirSrc, idxObj, pszSrc,
2102 cchSrc + 1 + pDirEntry->cbName, fNamespaces, cDepth + 1);
2103 else
2104 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc,
2105 "Error setting parent & name on directory '%s' to '%s': %Rrc",
2106 pszSrc, pDirEntry->szName, rc);
2107 }
2108 else
2109 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding directory '%s' (VFS recursive): %Rrc", pszSrc, rc);
2110 RTVfsDirRelease(hVfsSubDirSrc);
2111 }
2112 else
2113 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening directory '%s' (VFS recursive): %Rrc", pszSrc, rc);
2114 }
2115 else if (RTFS_IS_SYMLINK(pDirEntry->Info.Attr.fMode))
2116 {
2117 /*
2118 * TODO: ISO FS symlink support.
2119 */
2120 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
2121 "Adding symlink '%s' failed: not yet implemented", pszSrc);
2122 }
2123 else
2124 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
2125 "Adding special file '%s' failed: not implemented", pszSrc);
2126 }
2127 if (RT_FAILURE(rc))
2128 break;
2129 }
2130
2131 RTMemTmpFree(pDirEntry);
2132 }
2133 else
2134 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "Out of memory! (direntry buffer)");
2135 return rc;
2136}
2137
2138
2139/**
2140 * Adds a directory, from the source VFS.
2141 *
2142 * @returns IPRT status code.
2143 * @param pOpts The ISO maker command instance.
2144 * @param pParsed The parsed names.
2145 * @param pidxObj Where to return the configuration index for the
2146 * added file. Optional.
2147 */
2148static int rtFsIsoMakerCmdAddVfsDir(PRTFSISOMAKERCMDOPTS pOpts, PCRTFSISOMKCMDPARSEDNAMES pParsed, PCRTFSOBJINFO pObjInfo)
2149{
2150 Assert(pParsed->cNames < pParsed->cNamesWithSrc);
2151
2152 /*
2153 * Open the directory.
2154 */
2155 char *pszDir = pParsed->aNames[pParsed->cNamesWithSrc - 1].szPath;
2156 RTPathChangeToUnixSlashes(pszDir, true /*fForce*/); /* VFS currently only understand unix slashes. */
2157 RTVFSDIR hVfsDirSrc;
2158 int rc = RTVfsDirOpen(pOpts->hSrcVfs, pszDir, 0 /*fFlags*/, &hVfsDirSrc);
2159 if (RT_FAILURE(rc))
2160 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error opening directory '%s' (inside '%s'): %Rrc",
2161 pszDir, pOpts->pszSrcVfs, rc);
2162
2163 /*
2164 * Add the directory if it doesn't exist.
2165 */
2166 uint32_t idxObj = UINT32_MAX;
2167 for (uint32_t i = 0; i < pParsed->cNames; i++)
2168 if (pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
2169 {
2170 idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
2171 pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
2172 pParsed->aNames[i].szPath);
2173 if (idxObj != UINT32_MAX)
2174 {
2175 /** @todo make sure the directory is present in the other namespace. */
2176 break;
2177 }
2178 }
2179 if (idxObj == UINT32_MAX)
2180 {
2181 rc = RTFsIsoMakerAddUnnamedDir(pOpts->hIsoMaker, pObjInfo, &idxObj);
2182 if (RT_SUCCESS(rc))
2183 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxObj, pParsed, pParsed->aNames[pParsed->cNames - 1].szPath);
2184 else
2185 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerAddUnnamedDir failed: %Rrc", rc);
2186 }
2187 if (RT_SUCCESS(rc))
2188 {
2189 /*
2190 * Add the directory content.
2191 */
2192 uint32_t fNamespaces = 0;
2193 for (uint32_t i = 0; i < pParsed->cNames; i++)
2194 fNamespaces |= pParsed->aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK;
2195 rc = rtFsIsoMakerCmdAddVfsDirRecursive(pOpts, hVfsDirSrc, idxObj, pszDir,
2196 pParsed->aNames[pParsed->cNamesWithSrc - 1].cchPath, fNamespaces, 0 /*cDepth*/);
2197 }
2198 RTVfsDirRelease(hVfsDirSrc);
2199 return rc;
2200}
2201
2202
2203
2204
2205/**
2206 * Adds a file after first making sure it's a file.
2207 *
2208 * @returns IPRT status code
2209 * @param pOpts The ISO maker command instance.
2210 * @param pszSrc The path to the source file.
2211 * @param pParsed The parsed names.
2212 * @param pidxObj Where to return the configuration index for the
2213 * added file. Optional.
2214 */
2215static int rtFsIsoMakerCmdStatAndAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMKCMDPARSEDNAMES pParsed,
2216 uint32_t *pidxObj)
2217{
2218 int rc;
2219 RTFSOBJINFO ObjInfo;
2220 if ( pOpts->hSrcVfs == NIL_RTVFS
2221 || RTVfsChainIsSpec(pszSrc))
2222 {
2223 uint32_t offError;
2224 RTERRINFOSTATIC ErrInfo;
2225 rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
2226 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
2227 if (RT_FAILURE(rc))
2228 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
2229 }
2230 else
2231 {
2232 rc = RTVfsQueryPathInfo(pOpts->hSrcVfs, pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
2233 if (RT_FAILURE(rc))
2234 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsQueryPathInfo failed on %s (inside %s): %Rrc",
2235 pszSrc, pOpts->pszSrcVfs, rc);
2236 }
2237
2238 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
2239 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, pParsed, pidxObj);
2240 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_A_FILE, "Not a file: %s", pszSrc);
2241}
2242
2243
2244/**
2245 * Processes a non-option argument.
2246 *
2247 * @returns IPRT status code.
2248 * @param pOpts The ISO maker command instance.
2249 * @param pszSpec The specification of what to add.
2250 */
2251static int rtFsIsoMakerCmdAddSomething(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
2252{
2253 /*
2254 * Parse the name spec.
2255 */
2256 RTFSISOMKCMDPARSEDNAMES Parsed;
2257 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszSpec, true /*fWithSrc*/, &Parsed);
2258 if (RT_FAILURE(rc))
2259 return rc;
2260
2261 /*
2262 * Deal with special source filenames used to remove/change stuff.
2263 */
2264 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_Remove
2265 || Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove)
2266 {
2267 const char *pszFirstNm = NULL;
2268 uint32_t cRemoved = 0;
2269 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
2270 if ( Parsed.aNames[i].cchPath > 0
2271 && (Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK))
2272 {
2273 pszFirstNm = Parsed.aNames[i].szPath;
2274 uint32_t idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
2275 Parsed.aNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
2276 Parsed.aNames[i].szPath);
2277 if (idxObj != UINT32_MAX)
2278 {
2279 rc = RTFsIsoMakerObjRemove(pOpts->hIsoMaker, idxObj);
2280 if (RT_FAILURE(rc))
2281 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to remove '%s': %Rrc", pszSpec, rc);
2282 cRemoved++;
2283 }
2284 }
2285 if ( Parsed.enmSrcType == RTFSISOMKCMDPARSEDNAMES::kSrcType_MustRemove
2286 && cRemoved == 0)
2287 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "Failed to locate '%s' for removal", pszSpec);
2288 }
2289 /*
2290 * Add regular source.
2291 */
2292 else
2293 {
2294 const char *pszSrc = Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath;
2295 RTFSOBJINFO ObjInfo;
2296 if ( pOpts->hSrcVfs == NIL_RTVFS
2297 || RTVfsChainIsSpec(pszSrc))
2298 {
2299 uint32_t offError;
2300 RTERRINFOSTATIC ErrInfo;
2301 rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
2302 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
2303 if (RT_FAILURE(rc))
2304 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
2305 }
2306 else
2307 {
2308 rc = RTVfsQueryPathInfo(pOpts->hSrcVfs, pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
2309 if (RT_FAILURE(rc))
2310 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsQueryPathInfo failed on %s (in %s): %Rrc",
2311 pszSrc, pOpts->pszSrcVfs, rc);
2312 }
2313
2314 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
2315 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, &Parsed, NULL /*pidxObj*/);
2316
2317 if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
2318 {
2319 if ( pOpts->hSrcVfs == NIL_RTVFS
2320 || RTVfsChainIsSpec(pszSrc))
2321 return rtFsIsoMakerCmdAddDir(pOpts, pszSrc, &Parsed);
2322 return rtFsIsoMakerCmdAddVfsDir(pOpts, &Parsed, &ObjInfo);
2323 }
2324
2325 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
2326 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding symlink '%s' failed: not yet implemented", pszSpec);
2327
2328 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding special file '%s' failed: not implemented", pszSpec);
2329 }
2330
2331 return VINF_SUCCESS;
2332}
2333
2334
2335/**
2336 * Opens an ISO and use it for subsequent file system accesses.
2337 *
2338 * This is handy for duplicating a part of an ISO in the new image.
2339 *
2340 * @returns IPRT status code.
2341 * @param pOpts The ISO maker command instance.
2342 * @param pszIsoSpec The ISO path specifier.
2343 * @param pszOption The option we're being called on.
2344 * @param fFlags RTFSISO9660_F_XXX
2345 */
2346static int rtFsIsoMakerCmdOptPushIso(PRTFSISOMAKERCMDOPTS pOpts, const char *pszIsoSpec, const char *pszOption, uint32_t fFlags)
2347{
2348 if (pOpts->hSrcVfs != NIL_RTVFS)
2349 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED,
2350 "Nested %s usage is not supported (previous: %s %s)",
2351 pszOption, pOpts->pszSrcVfsOption, pOpts->pszSrcVfs);
2352
2353 /*
2354 * Try open the file.
2355 */
2356 RTVFSFILE hVfsFileIso;
2357 uint32_t offError;
2358 RTERRINFOSTATIC ErrInfo;
2359 int rc = RTVfsChainOpenFile(pszIsoSpec, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE,
2360 &hVfsFileIso, &offError, RTErrInfoInitStatic(&ErrInfo));
2361 if (RT_SUCCESS(rc))
2362 {
2363 RTVFS hSrcVfs;
2364 rc = RTFsIso9660VolOpen(hVfsFileIso, fFlags, &hSrcVfs, RTErrInfoInitStatic(&ErrInfo));
2365 RTVfsFileRelease(hVfsFileIso);
2366 if (RT_SUCCESS(rc))
2367 {
2368 pOpts->hSrcVfs = hSrcVfs;
2369 pOpts->pszSrcVfs = pszIsoSpec;
2370 pOpts->pszSrcVfsOption = pszOption;
2371 return VINF_SUCCESS;
2372 }
2373
2374 if (RTErrInfoIsSet(&ErrInfo.Core))
2375 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to open '%s' as ISO FS: %Rrc - %s",
2376 pszIsoSpec, rc, ErrInfo.Core.pszMsg);
2377 else
2378 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to open '%s' as ISO FS: %Rrc", pszIsoSpec, rc);
2379 }
2380 else
2381 rc = rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszIsoSpec, rc, offError, &ErrInfo.Core);
2382 return rc;
2383}
2384
2385
2386/**
2387 * Counter part to --push-iso and friends.
2388 *
2389 * @returns IPRT status code.
2390 * @param pOpts The ISO maker command instance.
2391 */
2392static int rtFsIsoMakerCmdOptPop(PRTFSISOMAKERCMDOPTS pOpts)
2393{
2394 if (pOpts->hSrcVfs != NIL_RTVFS)
2395 {
2396 RTVfsRelease(pOpts->hSrcVfs);
2397 pOpts->hSrcVfs = NIL_RTVFS;
2398 pOpts->pszSrcVfs = NULL;
2399 pOpts->pszSrcVfsOption = NULL;
2400 return VINF_SUCCESS;
2401 }
2402 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "--pop without --push-xxx");
2403}
2404
2405
2406/**
2407 * Deals with the --import-iso {iso-file-spec} options.
2408 *
2409 * @returns IPRT status code
2410 * @param pOpts The ISO maker command instance.
2411 * @param pszIsoSpec The ISO path specifier.
2412 */
2413static int rtFsIsoMakerCmdOptImportIso(PRTFSISOMAKERCMDOPTS pOpts, const char *pszIsoSpec)
2414{
2415 RTFSISOMAKERIMPORTRESULTS Results;
2416 RTERRINFOSTATIC ErrInfo;
2417 int rc = RTFsIsoMakerImport(pOpts->hIsoMaker, pszIsoSpec, 0 /*fFlags*/, &Results, RTErrInfoInitStatic(&ErrInfo));
2418
2419 pOpts->cItemsAdded += Results.cAddedFiles;
2420 pOpts->cItemsAdded += Results.cAddedSymlinks;
2421 pOpts->cItemsAdded += Results.cAddedDirs;
2422 pOpts->cItemsAdded += Results.cBootCatEntries != UINT32_MAX ? Results.cBootCatEntries : 0;
2423 pOpts->cItemsAdded += Results.cbSysArea != 0 ? 1 : 0;
2424
2425 rtFsIsoMakerPrintf(pOpts, "ISO imported statistics for '%s'\n", pszIsoSpec);
2426 rtFsIsoMakerPrintf(pOpts, " cAddedNames: %'14RU32\n", Results.cAddedNames);
2427 rtFsIsoMakerPrintf(pOpts, " cAddedDirs: %'14RU32\n", Results.cAddedDirs);
2428 rtFsIsoMakerPrintf(pOpts, " cbAddedDataBlocks: %'14RU64 bytes\n", Results.cbAddedDataBlocks);
2429 rtFsIsoMakerPrintf(pOpts, " cAddedFiles: %'14RU32\n", Results.cAddedFiles);
2430 rtFsIsoMakerPrintf(pOpts, " cAddedSymlinks: %'14RU32\n", Results.cAddedSymlinks);
2431 if (Results.cBootCatEntries == UINT32_MAX)
2432 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: none\n");
2433 else
2434 rtFsIsoMakerPrintf(pOpts, " cBootCatEntries: %'14RU32\n", Results.cBootCatEntries);
2435 rtFsIsoMakerPrintf(pOpts, " cbSysArea: %'14RU32\n", Results.cbSysArea);
2436 rtFsIsoMakerPrintf(pOpts, " cErrors: %'14RU32\n", Results.cErrors);
2437
2438 if (RT_SUCCESS(rc))
2439 return rc;
2440 if (Results.offError != UINT32_MAX)
2441 return rtFsIsoMakerCmdChainError(pOpts, "RTFsIsoMakerImport", pszIsoSpec, rc, Results.offError, &ErrInfo.Core);
2442 if (RTErrInfoIsSet(&ErrInfo.Core))
2443 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc - %s", rc, ErrInfo.Core.pszMsg);
2444 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerImport failed: %Rrc", rc);
2445}
2446
2447
2448/**
2449 * Deals with: --iso-level, -l
2450 *
2451 * @returns IPRT status code
2452 * @param pOpts The ISO maker command instance.
2453 * @param uLevel The new ISO level.
2454 */
2455static int rtFsIsoMakerCmdOptSetIsoLevel(PRTFSISOMAKERCMDOPTS pOpts, uint8_t uLevel)
2456{
2457 int rc = RTFsIsoMakerSetIso9660Level(pOpts->hIsoMaker, uLevel);
2458 if (RT_SUCCESS(rc))
2459 return rc;
2460 if (rc == VERR_WRONG_ORDER)
2461 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Cannot change ISO level to %d after having added files!", uLevel);
2462 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set ISO level to %d: %Rrc", uLevel, rc);
2463}
2464
2465
2466/**
2467 * Deals with: --rock-ridge, --limited-rock-ridge, --no-rock-ridge
2468 *
2469 * @returns IPRT status code
2470 * @param pOpts The ISO maker command instance.
2471 * @param uLevel The new rock ridge level.
2472 */
2473static int rtFsIsoMakerCmdOptSetPrimaryRockLevel(PRTFSISOMAKERCMDOPTS pOpts, uint8_t uLevel)
2474{
2475 int rc = RTFsIsoMakerSetRockRidgeLevel(pOpts->hIsoMaker, uLevel);
2476 if (RT_SUCCESS(rc))
2477 return rc;
2478 if (rc == VERR_WRONG_ORDER)
2479 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Cannot change rock ridge level to %d after having added files!", uLevel);
2480 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set rock ridge level to %d: %Rrc", uLevel, rc);
2481}
2482
2483
2484/**
2485 * Deals with: --joliet, --no-joliet, --joliet-ucs-level, --ucs-level
2486 *
2487 * @returns IPRT status code
2488 * @param pOpts The ISO maker command instance.
2489 * @param uLevel The new rock ridge level.
2490 */
2491static int rtFsIsoMakerCmdOptSetJolietUcs2Level(PRTFSISOMAKERCMDOPTS pOpts, uint8_t uLevel)
2492{
2493 int rc = RTFsIsoMakerSetJolietUcs2Level(pOpts->hIsoMaker, uLevel);
2494 if (RT_SUCCESS(rc))
2495 return rc;
2496 if (rc == VERR_WRONG_ORDER)
2497 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Cannot change joliet UCS level to %d after having added files!", uLevel);
2498 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set joliet UCS level to %d: %Rrc", uLevel, rc);
2499}
2500
2501
2502/**
2503 * Deals with: --rational-attribs, --strict-attribs, -R, -r
2504 *
2505 * @returns IPRT status code
2506 * @param pOpts The ISO maker command instance.
2507 * @param uLevel The new rock ridge level.
2508 */
2509static int rtFsIsoMakerCmdOptSetAttribInheritStyle(PRTFSISOMAKERCMDOPTS pOpts, bool fStrict)
2510{
2511 int rc = RTFsIsoMakerSetAttribInheritStyle(pOpts->hIsoMaker, fStrict);
2512 if (RT_SUCCESS(rc))
2513 return rc;
2514 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to change attributes handling style to %s: %Rrc",
2515 fStrict ? "strict" : "rational", rc);
2516}
2517
2518
2519/**
2520 * Deals with: -G|--generic-boot {file}
2521 *
2522 * This concers content the first 16 sectors of the image. We start loading the
2523 * file at byte 0 in the image and stops at 32KB.
2524 *
2525 * @returns IPRT status code
2526 * @param pOpts The ISO maker command instance.
2527 * @param pszGenericBootImage The generic boot image source.
2528 */
2529static int rtFsIsoMakerCmdOptGenericBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszGenericBootImage)
2530{
2531 RTERRINFOSTATIC ErrInfo;
2532 uint32_t offError;
2533 RTVFSFILE hVfsFile;
2534 int rc = RTVfsChainOpenFile(pszGenericBootImage, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
2535 &offError, RTErrInfoInitStatic(&ErrInfo));
2536 if (RT_FAILURE(rc))
2537 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszGenericBootImage, rc, offError, &ErrInfo.Core);
2538
2539 uint8_t abBuf[_32K];
2540 size_t cbRead;
2541 rc = RTVfsFileReadAt(hVfsFile, 0, abBuf, sizeof(abBuf), &cbRead);
2542 RTVfsFileRelease(hVfsFile);
2543 if (RT_FAILURE(rc))
2544 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error reading 32KB from generic boot image '%s': %Rrc", pszGenericBootImage, rc);
2545
2546 rc = RTFsIsoMakerSetSysAreaContent(pOpts->hIsoMaker, abBuf, cbRead, 0);
2547 if (RT_FAILURE(rc))
2548 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerSetSysAreaContent failed with a %zu bytes input: %Rrc", cbRead, rc);
2549
2550 return VINF_SUCCESS;
2551}
2552
2553
2554/**
2555 * Helper that makes sure we've got a validation boot entry.
2556 *
2557 * @returns IPRT status code
2558 * @param pOpts The ISO maker command instance.
2559 */
2560static void rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(PRTFSISOMAKERCMDOPTS pOpts)
2561{
2562 if (pOpts->cBootCatEntries == 0)
2563 {
2564 pOpts->aBootCatEntries[0].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation;
2565 pOpts->aBootCatEntries[0].u.Validation.idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
2566 pOpts->aBootCatEntries[0].u.Validation.pszString = NULL;
2567 pOpts->cBootCatEntries = 1;
2568 }
2569}
2570
2571
2572/**
2573 * Helper that makes sure we've got a current boot entry.
2574 *
2575 * @returns IPRT status code
2576 * @param pOpts The ISO maker command instance.
2577 * @param fForceNew Whether to force a new entry.
2578 * @param pidxBootCat Where to return the boot catalog index.
2579 */
2580static int rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(PRTFSISOMAKERCMDOPTS pOpts, bool fForceNew, uint32_t *pidxBootCat)
2581{
2582 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2583
2584 uint32_t i = pOpts->cBootCatEntries;
2585 if (i == 2 && fForceNew)
2586 {
2587 pOpts->aBootCatEntries[i].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
2588 pOpts->aBootCatEntries[i].u.SectionHeader.idPlatform = pOpts->aBootCatEntries[0].u.Validation.idPlatform;
2589 pOpts->aBootCatEntries[i].u.SectionHeader.pszString = NULL;
2590 pOpts->cBootCatEntries = ++i;
2591 }
2592
2593 if ( i == 1
2594 || fForceNew
2595 || pOpts->aBootCatEntries[i - 1].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2596 {
2597 if (i >= RT_ELEMENTS(pOpts->aBootCatEntries))
2598 {
2599 *pidxBootCat = UINT32_MAX;
2600 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
2601 }
2602
2603 pOpts->aBootCatEntries[i].enmType = i == 1 ? RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
2604 : RTFSISOMKCMDELTORITOENTRY::kEntryType_Section;
2605 pOpts->aBootCatEntries[i].u.Section.pszImageNameInIso = NULL;
2606 pOpts->aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
2607 pOpts->aBootCatEntries[i].u.Section.fInsertBootInfoTable = false;
2608 pOpts->aBootCatEntries[i].u.Section.fBootable = true;
2609 pOpts->aBootCatEntries[i].u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK;
2610 pOpts->aBootCatEntries[i].u.Section.bSystemType = 1 /*FAT12*/;
2611 pOpts->aBootCatEntries[i].u.Section.uLoadSeg = 0x7c0;
2612 pOpts->aBootCatEntries[i].u.Section.cSectorsToLoad = 4;
2613 pOpts->cBootCatEntries = ++i;
2614 }
2615
2616 *pidxBootCat = i - 1;
2617 return VINF_SUCCESS;
2618}
2619
2620
2621/**
2622 * Deals with: --boot-catalog <path-spec>
2623 *
2624 * This enters the boot catalog into the namespaces of the image. The path-spec
2625 * is similar to what rtFsIsoMakerCmdAddSomething processes, only there isn't a
2626 * source file part.
2627 *
2628 * @returns IPRT status code
2629 * @param pOpts The ISO maker command instance.
2630 * @param pszGenericBootImage The generic boot image source.
2631 */
2632static int rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootCat)
2633{
2634 /* Make sure we'll fail later if no other boot options are present. */
2635 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2636
2637 /* Parse the name spec. */
2638 RTFSISOMKCMDPARSEDNAMES Parsed;
2639 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootCat, false /*fWithSrc*/, &Parsed);
2640 if (RT_SUCCESS(rc))
2641 {
2642 /* Query/create the boot catalog and enter it into the name spaces. */
2643 uint32_t idxBootCatObj;
2644 rc = RTFsIsoMakerQueryObjIdxForBootCatalog(pOpts->hIsoMaker, &idxBootCatObj);
2645 if (RT_SUCCESS(rc))
2646 rc = rtFsIsoMakerCmdSetObjPaths(pOpts, idxBootCatObj, &Parsed, "boot catalog");
2647 else
2648 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerQueryBootCatalogPathObjIdx failed: %Rrc", rc);
2649 }
2650 return rc;
2651}
2652
2653
2654/**
2655 * Deals with: --eltorito-add-image {file-spec}
2656 *
2657 * This differs from -b|--eltorito-boot in that it takes a source file
2658 * specification identical to what rtFsIsoMakerCmdAddSomething processes instead
2659 * of a reference to a file in the image.
2660 *
2661 * This operates on the current eltorito boot catalog entry.
2662 *
2663 * @returns IPRT status code
2664 * @param pOpts The ISO maker command instance.
2665 * @param pszGenericBootImage The generic boot image source.
2666 */
2667static int rtFsIsoMakerCmdOptEltoritoAddImage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImageSpec)
2668{
2669 /* Parse the name spec. */
2670 RTFSISOMKCMDPARSEDNAMES Parsed;
2671 int rc = rtFsIsoMakerCmdParseNameSpec(pOpts, pszBootImageSpec, true /*fWithSrc*/, &Parsed);
2672 if (RT_SUCCESS(rc))
2673 {
2674 uint32_t idxBootCat;
2675 rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2676 if (RT_SUCCESS(rc))
2677 {
2678 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
2679 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
2680 rc = rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
2681 else
2682 {
2683 uint32_t idxImageObj;
2684 rc = rtFsIsoMakerCmdStatAndAddFile(pOpts, Parsed.aNames[Parsed.cNamesWithSrc - 1].szPath, &Parsed, &idxImageObj);
2685 if (RT_SUCCESS(rc))
2686 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
2687 }
2688 }
2689 }
2690
2691 return rc;
2692}
2693
2694
2695/**
2696 * Deals with: -b|--eltorito-boot {file-in-iso}
2697 *
2698 * This operates on the current eltorito boot catalog entry.
2699 *
2700 * @returns IPRT status code
2701 * @param pOpts The ISO maker command instance.
2702 * @param pszGenericBootImage The generic boot image source.
2703 */
2704static int rtFsIsoMakerCmdOptEltoritoBoot(PRTFSISOMAKERCMDOPTS pOpts, const char *pszBootImage)
2705{
2706 uint32_t idxBootCat;
2707 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2708 if (RT_SUCCESS(rc))
2709 {
2710 if ( pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj != UINT32_MAX
2711 || pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso != NULL)
2712 return rtFsIsoMakerCmdSyntaxError(pOpts, "boot image already given for current El Torito entry (%#u)", idxBootCat);
2713
2714 uint32_t idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
2715 if (idxImageObj == UINT32_MAX)
2716 pOpts->aBootCatEntries[idxBootCat].u.Section.pszImageNameInIso = pszBootImage;
2717 pOpts->aBootCatEntries[idxBootCat].u.Section.idxImageObj = idxImageObj;
2718 }
2719 return rc;
2720}
2721
2722
2723/**
2724 * Deals with: --eltorito-platform-id {x86|PPC|Mac|efi|number}
2725 *
2726 * Operates on the validation entry or a section header.
2727 *
2728 * @returns IPRT status code
2729 * @param pOpts The ISO maker command instance.
2730 * @param pszPlatformId The platform ID.
2731 */
2732static int rtFsIsoMakerCmdOptEltoritoPlatformId(PRTFSISOMAKERCMDOPTS pOpts, const char *pszPlatformId)
2733{
2734 /* Decode it. */
2735 uint8_t idPlatform;
2736 if (strcmp(pszPlatformId, "x86") == 0)
2737 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_X86;
2738 else if (strcmp(pszPlatformId, "PPC") == 0)
2739 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_PPC;
2740 else if (strcmp(pszPlatformId, "Mac") == 0)
2741 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_MAC;
2742 else if (strcmp(pszPlatformId, "efi") == 0)
2743 idPlatform = ISO9660_ELTORITO_PLATFORM_ID_EFI;
2744 else
2745 {
2746 int rc = RTStrToUInt8Full(pszPlatformId, 0, &idPlatform);
2747 if (rc != VINF_SUCCESS)
2748 return rtFsIsoMakerCmdSyntaxError(pOpts, "invalid or unknown platform ID: %s", pszPlatformId);
2749 }
2750
2751 /* If this option comes before anything related to the default entry, work
2752 on the validation entry. */
2753 if (pOpts->cBootCatEntries <= 1)
2754 {
2755 rtFsIsoMakerCmdOptEltoritoEnsureValidationEntry(pOpts);
2756 pOpts->aBootCatEntries[0].u.Validation.idPlatform = idPlatform;
2757 }
2758 /* Otherwise, work on the current section header, creating a new one if necessary. */
2759 else
2760 {
2761 uint32_t idxBootCat = pOpts->cBootCatEntries - 1;
2762 if (pOpts->aBootCatEntries[idxBootCat].enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2763 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
2764 else
2765 {
2766 idxBootCat++;
2767 if (idxBootCat + 2 > RT_ELEMENTS(pOpts->aBootCatEntries))
2768 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_BUFFER_OVERFLOW, "Too many boot catalog entries");
2769
2770 pOpts->aBootCatEntries[idxBootCat].enmType = RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader;
2771 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.idPlatform = idPlatform;
2772 pOpts->aBootCatEntries[idxBootCat].u.SectionHeader.pszString = NULL;
2773 pOpts->cBootCatEntries = idxBootCat + 1;
2774 }
2775 }
2776 return VINF_SUCCESS;
2777}
2778
2779
2780/**
2781 * Deals with: -no-boot
2782 *
2783 * This operates on the current eltorito boot catalog entry.
2784 *
2785 * @returns IPRT status code
2786 * @param pOpts The ISO maker command instance.
2787 */
2788static int rtFsIsoMakerCmdOptEltoritoSetNotBootable(PRTFSISOMAKERCMDOPTS pOpts)
2789{
2790 uint32_t idxBootCat;
2791 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2792 if (RT_SUCCESS(rc))
2793 pOpts->aBootCatEntries[idxBootCat].u.Section.fBootable = false;
2794 return rc;
2795}
2796
2797
2798/**
2799 * Deals with: -hard-disk-boot, -no-emulation-boot, --eltorito-floppy-12,
2800 * --eltorito-floppy-144, --eltorito-floppy-288
2801 *
2802 * This operates on the current eltorito boot catalog entry.
2803 *
2804 * @returns IPRT status code
2805 * @param pOpts The ISO maker command instance.
2806 * @param bMediaType The media type.
2807 */
2808static int rtFsIsoMakerCmdOptEltoritoSetMediaType(PRTFSISOMAKERCMDOPTS pOpts, uint8_t bMediaType)
2809{
2810 uint32_t idxBootCat;
2811 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2812 if (RT_SUCCESS(rc))
2813 pOpts->aBootCatEntries[idxBootCat].u.Section.bBootMediaType = bMediaType;
2814 return rc;
2815}
2816
2817
2818/**
2819 * Deals with: -boot-load-seg {seg}
2820 *
2821 * This operates on the current eltorito boot catalog entry.
2822 *
2823 * @returns IPRT status code
2824 * @param pOpts The ISO maker command instance.
2825 * @param uSeg The load segment.
2826 */
2827static int rtFsIsoMakerCmdOptEltoritoSetLoadSegment(PRTFSISOMAKERCMDOPTS pOpts, uint16_t uSeg)
2828{
2829 uint32_t idxBootCat;
2830 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2831 if (RT_SUCCESS(rc))
2832 pOpts->aBootCatEntries[idxBootCat].u.Section.uLoadSeg = uSeg;
2833 return rc;
2834}
2835
2836
2837/**
2838 * Deals with: -boot-load-size {sectors}
2839 *
2840 * This operates on the current eltorito boot catalog entry.
2841 *
2842 * @returns IPRT status code
2843 * @param pOpts The ISO maker command instance.
2844 * @param cSectors Number of emulated sectors to load
2845 */
2846static int rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(PRTFSISOMAKERCMDOPTS pOpts, uint16_t cSectors)
2847{
2848 uint32_t idxBootCat;
2849 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2850 if (RT_SUCCESS(rc))
2851 pOpts->aBootCatEntries[idxBootCat].u.Section.cSectorsToLoad = cSectors;
2852 return rc;
2853}
2854
2855
2856/**
2857 * Deals with: -boot-info-table
2858 *
2859 * This operates on the current eltorito boot catalog entry.
2860 *
2861 * @returns IPRT status code
2862 * @param pOpts The ISO maker command instance.
2863 */
2864static int rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(PRTFSISOMAKERCMDOPTS pOpts)
2865{
2866 uint32_t idxBootCat;
2867 int rc = rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, false /*fForceNew*/, &idxBootCat);
2868 if (RT_SUCCESS(rc))
2869 pOpts->aBootCatEntries[idxBootCat].u.Section.fInsertBootInfoTable = true;
2870 return rc;
2871}
2872
2873
2874/**
2875 * Validates and commits the boot catalog stuff.
2876 *
2877 * ASSUMING this is called after all options are parsed and there is only this
2878 * one call.
2879 *
2880 * @returns IPRT status code
2881 * @param pOpts The ISO maker command instance.
2882 */
2883static int rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(PRTFSISOMAKERCMDOPTS pOpts)
2884{
2885 if (pOpts->cBootCatEntries == 0)
2886 return VINF_SUCCESS;
2887
2888 /*
2889 * Locate and configure the boot images first.
2890 */
2891 int rc;
2892 PRTFSISOMKCMDELTORITOENTRY pBootCatEntry = &pOpts->aBootCatEntries[1];
2893 for (uint32_t idxBootCat = 1; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2894 if ( pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default
2895 || pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Section)
2896 {
2897 /* Make sure we've got a boot image. */
2898 uint32_t idxImageObj = pBootCatEntry->u.Section.idxImageObj;
2899 if (idxImageObj == UINT32_MAX)
2900 {
2901 const char *pszBootImage = pBootCatEntry->u.Section.pszImageNameInIso;
2902 if (pszBootImage == NULL)
2903 return rtFsIsoMakerCmdSyntaxError(pOpts, "No image name given for boot catalog entry #%u", idxBootCat);
2904
2905 idxImageObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker, RTFSISOMAKER_NAMESPACE_ALL, pszBootImage);
2906 if (idxImageObj == UINT32_MAX)
2907 return rtFsIsoMakerCmdSyntaxError(pOpts, "Unable to locate image for boot catalog entry #%u: %s",
2908 idxBootCat, pszBootImage);
2909 pBootCatEntry->u.Section.idxImageObj = idxImageObj;
2910 }
2911
2912 /* Enable patching it? */
2913 if (pBootCatEntry->u.Section.fInsertBootInfoTable)
2914 {
2915 rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pOpts->hIsoMaker, idxImageObj, true);
2916 if (RT_FAILURE(rc))
2917 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2918 "RTFsIsoMakerObjEnableBootInfoTablePatching failed on entry #%u: %Rrc",
2919 idxBootCat, rc);
2920 }
2921
2922 /* Figure out the floppy type given the object size. */
2923 if (pBootCatEntry->u.Section.bBootMediaType == ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
2924 {
2925 uint64_t cbImage;
2926 rc = RTFsIsoMakerObjQueryDataSize(pOpts->hIsoMaker, idxImageObj, &cbImage);
2927 if (RT_FAILURE(rc))
2928 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerObjGetDataSize failed on entry #%u: %Rrc",
2929 idxBootCat, rc);
2930 if (cbImage == 1228800)
2931 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB;
2932 else if (cbImage <= 1474560)
2933 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB;
2934 else if (cbImage <= 2949120)
2935 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB;
2936 else
2937 pBootCatEntry->u.Section.bBootMediaType = ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK;
2938 }
2939 }
2940
2941 /*
2942 * Add the boot catalog entries.
2943 */
2944 pBootCatEntry = &pOpts->aBootCatEntries[0];
2945 for (uint32_t idxBootCat = 0; idxBootCat < pOpts->cBootCatEntries; idxBootCat++, pBootCatEntry++)
2946 switch (pBootCatEntry->enmType)
2947 {
2948 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Validation:
2949 Assert(idxBootCat == 0);
2950 rc = RTFsIsoMakerBootCatSetValidationEntry(pOpts->hIsoMaker, pBootCatEntry->u.Validation.idPlatform,
2951 pBootCatEntry->u.Validation.pszString);
2952 if (RT_FAILURE(rc))
2953 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetValidationEntry failed: %Rrc", rc);
2954 break;
2955
2956 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Default:
2957 case RTFSISOMKCMDELTORITOENTRY::kEntryType_Section:
2958 Assert(pBootCatEntry->enmType == RTFSISOMKCMDELTORITOENTRY::kEntryType_Default ? idxBootCat == 1 : idxBootCat > 2);
2959 rc = RTFsIsoMakerBootCatSetSectionEntry(pOpts->hIsoMaker, idxBootCat,
2960 pBootCatEntry->u.Section.idxImageObj,
2961 pBootCatEntry->u.Section.bBootMediaType,
2962 pBootCatEntry->u.Section.bSystemType,
2963 pBootCatEntry->u.Section.fBootable,
2964 pBootCatEntry->u.Section.uLoadSeg,
2965 pBootCatEntry->u.Section.cSectorsToLoad,
2966 ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE, NULL, 0);
2967 if (RT_FAILURE(rc))
2968 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerBootCatSetSectionEntry failed on entry #%u: %Rrc",
2969 idxBootCat, rc);
2970 break;
2971
2972 case RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader:
2973 {
2974 uint32_t cEntries = 1;
2975 while ( idxBootCat + cEntries < pOpts->cBootCatEntries
2976 && pBootCatEntry[cEntries].enmType != RTFSISOMKCMDELTORITOENTRY::kEntryType_SectionHeader)
2977 cEntries++;
2978 cEntries--;
2979
2980 Assert(idxBootCat > 1);
2981 rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pOpts->hIsoMaker, idxBootCat, cEntries,
2982 pBootCatEntry->u.SectionHeader.idPlatform,
2983 pBootCatEntry->u.SectionHeader.pszString);
2984 if (RT_FAILURE(rc))
2985 return rtFsIsoMakerCmdErrorRc(pOpts, rc,
2986 "RTFsIsoMakerBootCatSetSectionHeaderEntry failed on entry #%u: %Rrc",
2987 idxBootCat, rc);
2988 break;
2989 }
2990
2991 default:
2992 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
2993 }
2994
2995 return VINF_SUCCESS;
2996}
2997
2998
2999/**
3000 * Deals with: --eltorito-new-entry, --eltorito-alt-boot
3001 *
3002 * This operates on the current eltorito boot catalog entry.
3003 *
3004 * @returns IPRT status code
3005 * @param pOpts The ISO maker command instance.
3006 */
3007static int rtFsIsoMakerCmdOptEltoritoNewEntry(PRTFSISOMAKERCMDOPTS pOpts)
3008{
3009 uint32_t idxBootCat;
3010 return rtFsIsoMakerCmdOptEltoritoEnsureSectionEntry(pOpts, true /*fForceNew*/, &idxBootCat);
3011}
3012
3013
3014/**
3015 * Sets a string property in all namespaces.
3016 *
3017 * @returns IPRT status code.
3018 * @param pOpts The ISO maker command instance.
3019 * @param pszValue The new string value.
3020 * @param enmStringProp The string property.
3021 */
3022static int rtFsIsoMakerCmdOptSetStringProp(PRTFSISOMAKERCMDOPTS pOpts, const char *pszValue, RTFSISOMAKERSTRINGPROP enmStringProp)
3023{
3024 int rc = RTFsIsoMakerSetStringProp(pOpts->hIsoMaker, enmStringProp, pOpts->fDstNamespaces, pszValue);
3025 if (RT_FAILURE(rc))
3026 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set string property %d to '%s': %Rrc", enmStringProp, pszValue, rc);
3027 return rc;
3028}
3029
3030
3031/**
3032 * Handles the --dir-mode and --file-mode options.
3033 *
3034 * @returns IPRT status code.
3035 * @param pOpts The ISO maker command instance.
3036 * @param fDir True if applies to dir, false if applies to
3037 * files.
3038 * @param fMode The forced mode.
3039 */
3040static int rtFsIsoMakerCmdOptSetFileOrDirMode(PRTFSISOMAKERCMDOPTS pOpts, bool fDir, RTFMODE fMode)
3041{
3042 /* Change the mode masks. */
3043 int rc;
3044 if (fDir)
3045 rc = RTFsIsoMakerSetForcedDirMode(pOpts->hIsoMaker, fMode, true /*fForced*/);
3046 else
3047 rc = RTFsIsoMakerSetForcedFileMode(pOpts->hIsoMaker, fMode, true /*fForced*/);
3048 if (RT_SUCCESS(rc))
3049 {
3050 /* Then enable rock.*/
3051 rc = RTFsIsoMakerSetRockRidgeLevel(pOpts->hIsoMaker, 2);
3052 if (RT_SUCCESS(rc))
3053 return VINF_SUCCESS;
3054 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to enable rock ridge: %Rrc", rc);
3055 }
3056 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set %s force & default mode mask to %04o: %Rrc",
3057 fMode, fDir ? "directory" : "file", rc);
3058}
3059
3060
3061/**
3062 * Handles the --no-dir-mode and --no-file-mode options that counters
3063 * --dir-mode and --file-mode.
3064 *
3065 * @returns IPRT status code.
3066 * @param pOpts The ISO maker command instance.
3067 * @param fDir True if applies to dir, false if applies to
3068 * files.
3069 */
3070static int rtFsIsoMakerCmdOptDisableFileOrDirMode(PRTFSISOMAKERCMDOPTS pOpts, bool fDir)
3071{
3072 int rc;
3073 if (fDir)
3074 rc = RTFsIsoMakerSetForcedDirMode(pOpts->hIsoMaker, 0, false /*fForced*/);
3075 else
3076 rc = RTFsIsoMakerSetForcedFileMode(pOpts->hIsoMaker, 0, true /*fForced*/);
3077 if (RT_SUCCESS(rc))
3078 return VINF_SUCCESS;
3079 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to disable forced %s mode mask: %Rrc", fDir ? "directory" : "file", rc);
3080}
3081
3082
3083
3084/**
3085 * Handles the --new-dir-mode option.
3086 *
3087 * @returns IPRT status code.
3088 * @param pOpts The ISO maker command instance.
3089 * @param fMode The forced mode.
3090 */
3091static int rtFsIsoMakerCmdOptSetNewDirMode(PRTFSISOMAKERCMDOPTS pOpts, RTFMODE fMode)
3092{
3093 int rc = RTFsIsoMakerSetDefaultDirMode(pOpts->hIsoMaker, fMode);
3094 if (RT_SUCCESS(rc))
3095 return VINF_SUCCESS;
3096 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to set default dir mode mask to %04o: %Rrc", fMode, rc);
3097}
3098
3099
3100/**
3101 * Handles the --chmod option.
3102 *
3103 * @returns IPRT status code
3104 * @param pOpts The ISO maker command instance.
3105 * @param pszSpec The option value.
3106 */
3107static int rtFsIsoMakerCmdOptChmod(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
3108{
3109 /*
3110 * Parse the mode part.
3111 */
3112 int rc;
3113 uint32_t fUnset = 07777;
3114 uint32_t fSet = 0;
3115 const char *pszPath = pszSpec;
3116 if (RT_C_IS_DIGIT(*pszPath))
3117 {
3118 rc = RTStrToUInt32Ex(pszSpec, (char **)&pszPath, 8, &fSet);
3119 if (rc != VWRN_TRAILING_CHARS)
3120 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --chmod, octal mode parse failed: %s (%Rrc)", pszSpec, rc);
3121 if (fSet & ~07777)
3122 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --chmod, invalid mode mask: 0%o, max 07777", fSet);
3123 if (*pszPath != ':')
3124 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --chmod, expected colon after mode: %s", pszSpec);
3125 }
3126 else
3127 {
3128 pszPath = strchr(pszPath, ':');
3129 if (pszPath == NULL)
3130 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --chmod, expected colon after mode: %s", pszSpec);
3131 size_t const cchMode = pszPath - pszSpec;
3132
3133 /* We currently only matches certain patterns. Later this needs to be generalized into a RTFile or RTPath method. */
3134 fUnset = 0;
3135#define MATCH_MODE_STR(a_szMode) (cchMode == sizeof(a_szMode) - 1U && memcmp(pszSpec, a_szMode, sizeof(a_szMode) - 1) == 0)
3136 if (MATCH_MODE_STR("a+x"))
3137 fSet = 0111;
3138 else if (MATCH_MODE_STR("a+r"))
3139 fSet = 0444;
3140 else if (MATCH_MODE_STR("a+rx"))
3141 fSet = 0555;
3142 else
3143 return rtFsIsoMakerCmdSyntaxError(pOpts, "Sorry, --chmod doesn't understand complicated mode expressions: %s", pszSpec);
3144#undef MATCH_MODE_STR
3145 }
3146
3147 /*
3148 * Check that the file starts with a slash.
3149 */
3150 pszPath++;
3151 if (!RTPATH_IS_SLASH(*pszPath))
3152 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --chmod, path must start with a slash: %s", pszSpec);
3153
3154 /*
3155 * Do the job.
3156 */
3157 rc = RTFsIsoMakerSetPathMode(pOpts->hIsoMaker, pszPath, pOpts->fDstNamespaces, fSet, fUnset, 0 /*fFlags*/, NULL /*pcHits*/);
3158 if (rc == VWRN_NOT_FOUND)
3159 return rtFsIsoMakerCmdSyntaxError(pOpts, "Could not find --chmod path: %s", pszPath);
3160 if (RT_SUCCESS(rc))
3161 return VINF_SUCCESS;
3162 return rtFsIsoMakerCmdSyntaxError(pOpts, "RTFsIsoMakerSetPathMode(,%s,%#x,%o,%o,0,) failed: %Rrc",
3163 pszPath, pOpts->fDstNamespaces, fSet, fUnset, rc);
3164}
3165
3166
3167/**
3168 * Handles the --chown and --chgrp options.
3169 *
3170 * @returns IPRT status code
3171 * @param pOpts The ISO maker command instance.
3172 * @param pszSpec The option value.
3173 * @param fIsChOwn Set if 'chown', clear if 'chgrp'.
3174 */
3175static int rtFsIsoMakerCmdOptChangeOwnerGroup(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec, bool fIsChOwn)
3176{
3177 const char * const pszOpt = fIsChOwn ? "chown" : "chgrp";
3178
3179 /*
3180 * Parse out the ID and path .
3181 */
3182 uint32_t idValue;
3183 const char *pszPath = pszSpec;
3184 int rc = RTStrToUInt32Ex(pszSpec, (char **)&pszPath, 0, &idValue);
3185 if (rc != VWRN_TRAILING_CHARS)
3186 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --%s, numeric ID parse failed: %s (%Rrc)", pszOpt, pszSpec, rc);
3187 if (*pszPath != ':')
3188 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --%s, expected colon after ID: %s", pszOpt, pszSpec);
3189 pszPath++;
3190 if (!RTPATH_IS_SLASH(*pszPath))
3191 return rtFsIsoMakerCmdSyntaxError(pOpts, "Malformed --%s, path must start with a slash: %s", pszOpt, pszSpec);
3192
3193 /*
3194 * Do the job.
3195 */
3196 if (fIsChOwn)
3197 rc = RTFsIsoMakerSetPathOwnerId(pOpts->hIsoMaker, pszPath, pOpts->fDstNamespaces, idValue, NULL /*pcHits*/);
3198 else
3199 rc = RTFsIsoMakerSetPathGroupId(pOpts->hIsoMaker, pszPath, pOpts->fDstNamespaces, idValue, NULL /*pcHits*/);
3200 if (rc == VWRN_NOT_FOUND)
3201 return rtFsIsoMakerCmdSyntaxError(pOpts, "Could not find --%s path: %s", pszOpt, pszPath);
3202 if (RT_SUCCESS(rc))
3203 return VINF_SUCCESS;
3204 return rtFsIsoMakerCmdSyntaxError(pOpts, "RTFsIsoMakerSetPath%sId(,%s,%#x,%u,) failed: %Rrc",
3205 fIsChOwn ? "Owner" : "Group", pszPath, pOpts->fDstNamespaces, idValue, rc);
3206}
3207
3208
3209/**
3210 * Loads an argument file (e.g. a .iso-file) and parses it.
3211 *
3212 * @returns IPRT status code.
3213 * @param pOpts The ISO maker command instance.
3214 * @param pszFileSpec The file to parse.
3215 * @param cDepth The current nesting depth.
3216 */
3217static int rtFsIsoMakerCmdParseArgumentFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFileSpec, unsigned cDepth)
3218{
3219 if (cDepth > 2)
3220 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_INVALID_PARAMETER, "Too many nested argument files!");
3221
3222 /*
3223 * Read the file into memory.
3224 */
3225 RTERRINFOSTATIC ErrInfo;
3226 uint32_t offError;
3227 RTVFSFILE hVfsFile;
3228 int rc = RTVfsChainOpenFile(pszFileSpec, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile,
3229 &offError, RTErrInfoInitStatic(&ErrInfo));
3230 if (RT_FAILURE(rc))
3231 return rtFsIsoMakerCmdChainError(pOpts, "RTVfsChainOpenFile", pszFileSpec, rc, offError, &ErrInfo.Core);
3232
3233 uint64_t cbFile = 0;
3234 rc = RTVfsFileGetSize(hVfsFile, &cbFile);
3235 if (RT_SUCCESS(rc))
3236 {
3237 if (cbFile < _2M)
3238 {
3239 char *pszContent = (char *)RTMemTmpAllocZ((size_t)cbFile + 1);
3240 if (pszContent)
3241 {
3242 rc = RTVfsFileRead(hVfsFile, pszContent, (size_t)cbFile, NULL);
3243 if (RT_SUCCESS(rc))
3244 {
3245 /*
3246 * Check that it's valid UTF-8 and turn it into an argument vector.
3247 */
3248 rc = RTStrValidateEncodingEx(pszContent, (size_t)cbFile + 1,
3249 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
3250 if (RT_SUCCESS(rc))
3251 {
3252 uint32_t fGetOpt = strstr(pszContent, "--iprt-iso-maker-file-marker-ms") == NULL
3253 ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
3254 fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
3255 char **papszArgs;
3256 int cArgs;
3257 rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
3258 if (RT_SUCCESS(rc))
3259 {
3260 /*
3261 * Parse them.
3262 */
3263 rc = rtFsIsoMakerCmdParse(pOpts, cArgs, papszArgs, cDepth + 1);
3264
3265 RTGetOptArgvFreeEx(papszArgs, fGetOpt);
3266 }
3267 else
3268 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTGetOptArgvFromString failed: %Rrc", pszFileSpec, rc);
3269
3270 }
3271 else
3272 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: invalid encoding", pszFileSpec);
3273 }
3274 else
3275 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: error to read it into memory: %Rrc", pszFileSpec, rc);
3276 RTMemTmpFree(pszContent);
3277 }
3278 else
3279 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_NO_TMP_MEMORY, "%s: failed to allocte %zu bytes for reading",
3280 pszFileSpec, (size_t)cbFile + 1);
3281 }
3282 else
3283 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_FILE_TOO_BIG, "%s: file is too big: %'RU64 bytes, max 2MB", pszFileSpec, cbFile);
3284 }
3285 else
3286 rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: RTVfsFileGetSize failed: %Rrc", pszFileSpec, rc);
3287 RTVfsFileRelease(hVfsFile);
3288 return rc;
3289}
3290
3291
3292/**
3293 * Parses the given command line options.
3294 *
3295 * @returns IPRT status code.
3296 * @retval VINF_CALLBACK_RETURN if exit successfully (help, version).
3297 * @param pOpts The ISO maker command instance.
3298 * @param cArgs Number of arguments in papszArgs.
3299 * @param papszArgs The argument vector to parse.
3300 */
3301static int rtFsIsoMakerCmdParse(PRTFSISOMAKERCMDOPTS pOpts, unsigned cArgs, char **papszArgs, unsigned cDepth)
3302{
3303 /* Setup option parsing. */
3304 RTGETOPTSTATE GetState;
3305 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, g_aRtFsIsoMakerOptions, RT_ELEMENTS(g_aRtFsIsoMakerOptions),
3306 cDepth == 0 ? 1 : 0 /*iFirst*/, 0 /*fFlags*/);
3307 if (RT_FAILURE(rc))
3308 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTGetOpt failed: %Rrc", rc);
3309
3310 /*
3311 * Parse parameters. Parameters are position dependent.
3312 */
3313 RTGETOPTUNION ValueUnion;
3314 while ( RT_SUCCESS(rc)
3315 && (rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
3316 {
3317 switch (rc)
3318 {
3319 /*
3320 * Files and directories.
3321 */
3322 case VINF_GETOPT_NOT_OPTION:
3323 if ( *ValueUnion.psz != '@'
3324 || strchr(ValueUnion.psz, '='))
3325 rc = rtFsIsoMakerCmdAddSomething(pOpts, ValueUnion.psz);
3326 else
3327 rc = rtFsIsoMakerCmdParseArgumentFile(pOpts, ValueUnion.psz + 1, cDepth);
3328 break;
3329
3330
3331 /*
3332 * General options
3333 */
3334 case 'o':
3335 if (pOpts->fVirtualImageMaker)
3336 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is not allowed");
3337 if (pOpts->pszOutFile)
3338 return rtFsIsoMakerCmdSyntaxError(pOpts, "The --output option is specified more than once");
3339 pOpts->pszOutFile = ValueUnion.psz;
3340 break;
3341
3342 case RTFSISOMAKERCMD_OPT_NAME_SETUP:
3343 rc = rtFsIsoMakerCmdOptNameSetup(pOpts, ValueUnion.psz);
3344 break;
3345
3346 case RTFSISOMAKERCMD_OPT_PUSH_ISO:
3347 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso", 0);
3348 break;
3349
3350 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_JOLIET:
3351 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-joliet", RTFSISO9660_F_NO_JOLIET);
3352 break;
3353
3354 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK:
3355 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-rock", RTFSISO9660_F_NO_ROCK);
3356 break;
3357
3358 case RTFSISOMAKERCMD_OPT_PUSH_ISO_NO_ROCK_NO_JOLIET:
3359 rc = rtFsIsoMakerCmdOptPushIso(pOpts, ValueUnion.psz, "--push-iso-no-rock-no-joliet",
3360 RTFSISO9660_F_NO_ROCK | RTFSISO9660_F_NO_JOLIET);
3361 break;
3362
3363 case RTFSISOMAKERCMD_OPT_POP:
3364 rc = rtFsIsoMakerCmdOptPop(pOpts);
3365 break;
3366
3367 case RTFSISOMAKERCMD_OPT_IMPORT_ISO:
3368 rc = rtFsIsoMakerCmdOptImportIso(pOpts, ValueUnion.psz);
3369 break;
3370
3371
3372 /*
3373 * Namespace configuration.
3374 */
3375 case RTFSISOMAKERCMD_OPT_ISO_LEVEL:
3376 rc = rtFsIsoMakerCmdOptSetIsoLevel(pOpts, ValueUnion.u8);
3377 break;
3378
3379 case RTFSISOMAKERCMD_OPT_ROCK_RIDGE:
3380 rc = rtFsIsoMakerCmdOptSetPrimaryRockLevel(pOpts, 2);
3381 break;
3382
3383 case RTFSISOMAKERCMD_OPT_LIMITED_ROCK_RIDGE:
3384 rc = rtFsIsoMakerCmdOptSetPrimaryRockLevel(pOpts, 1);
3385 break;
3386
3387 case RTFSISOMAKERCMD_OPT_NO_ROCK_RIDGE:
3388 rc = rtFsIsoMakerCmdOptSetPrimaryRockLevel(pOpts, 0);
3389 break;
3390
3391 case 'J':
3392 rc = rtFsIsoMakerCmdOptSetJolietUcs2Level(pOpts, 3);
3393 break;
3394
3395 case RTFSISOMAKERCMD_OPT_NO_JOLIET:
3396 rc = rtFsIsoMakerCmdOptSetJolietUcs2Level(pOpts, 0);
3397 break;
3398
3399 case RTFSISOMAKERCMD_OPT_JOLIET_LEVEL:
3400 rc = rtFsIsoMakerCmdOptSetJolietUcs2Level(pOpts, ValueUnion.u8);
3401 break;
3402
3403
3404 /*
3405 * File attributes.
3406 */
3407 case RTFSISOMAKERCMD_OPT_RATIONAL_ATTRIBS:
3408 rc = rtFsIsoMakerCmdOptSetAttribInheritStyle(pOpts, false /*fStrict*/);
3409 break;
3410
3411 case RTFSISOMAKERCMD_OPT_STRICT_ATTRIBS:
3412 rc = rtFsIsoMakerCmdOptSetAttribInheritStyle(pOpts, true /*fStrict*/);
3413 break;
3414
3415 case RTFSISOMAKERCMD_OPT_FILE_MODE:
3416 rc = rtFsIsoMakerCmdOptSetFileOrDirMode(pOpts, false /*fDir*/, ValueUnion.u32);
3417 break;
3418
3419 case RTFSISOMAKERCMD_OPT_NO_FILE_MODE:
3420 rc = rtFsIsoMakerCmdOptDisableFileOrDirMode(pOpts, false /*fDir*/);
3421 break;
3422
3423 case RTFSISOMAKERCMD_OPT_DIR_MODE:
3424 rc = rtFsIsoMakerCmdOptSetFileOrDirMode(pOpts, true /*fDir*/, ValueUnion.u32);
3425 break;
3426
3427 case RTFSISOMAKERCMD_OPT_NO_DIR_MODE:
3428 rc = rtFsIsoMakerCmdOptDisableFileOrDirMode(pOpts, true /*fDir*/);
3429 break;
3430
3431 case RTFSISOMAKERCMD_OPT_NEW_DIR_MODE:
3432 rc = rtFsIsoMakerCmdOptSetNewDirMode(pOpts, ValueUnion.u32);
3433 break;
3434
3435 case RTFSISOMAKERCMD_OPT_CHMOD:
3436 rc = rtFsIsoMakerCmdOptChmod(pOpts, ValueUnion.psz);
3437 break;
3438
3439 case RTFSISOMAKERCMD_OPT_CHOWN:
3440 rc = rtFsIsoMakerCmdOptChangeOwnerGroup(pOpts, ValueUnion.psz, true /*fIsChOwn*/);
3441 break;
3442
3443 case RTFSISOMAKERCMD_OPT_CHGRP:
3444 rc = rtFsIsoMakerCmdOptChangeOwnerGroup(pOpts, ValueUnion.psz, false /*fIsChOwn*/);
3445 break;
3446
3447
3448 /*
3449 * Boot related options.
3450 */
3451 case 'G': /* --generic-boot <file> */
3452 rc = rtFsIsoMakerCmdOptGenericBoot(pOpts, ValueUnion.psz);
3453 break;
3454
3455 case RTFSISOMAKERCMD_OPT_ELTORITO_ADD_IMAGE:
3456 rc = rtFsIsoMakerCmdOptEltoritoAddImage(pOpts, ValueUnion.psz);
3457 break;
3458
3459 case 'b': /* --eltorito-boot <boot.img> */
3460 rc = rtFsIsoMakerCmdOptEltoritoBoot(pOpts, ValueUnion.psz);
3461 break;
3462
3463 case RTFSISOMAKERCMD_OPT_ELTORITO_NEW_ENTRY:
3464 rc = rtFsIsoMakerCmdOptEltoritoNewEntry(pOpts);
3465 break;
3466
3467 case RTFSISOMAKERCMD_OPT_ELTORITO_PLATFORM_ID:
3468 rc = rtFsIsoMakerCmdOptEltoritoPlatformId(pOpts, ValueUnion.psz);
3469 break;
3470
3471 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT:
3472 rc = rtFsIsoMakerCmdOptEltoritoSetNotBootable(pOpts);
3473 break;
3474
3475 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_12:
3476 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB);
3477 break;
3478 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_144:
3479 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB);
3480 break;
3481 case RTFSISOMAKERCMD_OPT_ELTORITO_FLOPPY_288:
3482 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB);
3483 break;
3484 case RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT:
3485 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK);
3486 break;
3487 case RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT:
3488 rc = rtFsIsoMakerCmdOptEltoritoSetMediaType(pOpts, ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION);
3489 break;
3490
3491 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG:
3492 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSegment(pOpts, ValueUnion.u16);
3493 break;
3494
3495 case RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE:
3496 rc = rtFsIsoMakerCmdOptEltoritoSetLoadSectorCount(pOpts, ValueUnion.u16);
3497 break;
3498
3499 case RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE:
3500 rc = rtFsIsoMakerCmdOptEltoritoEnableBootInfoTablePatching(pOpts);
3501 break;
3502
3503 case 'c': /* --boot-catalog <cd-path> */
3504 rc = rtFsIsoMakerCmdOptEltoritoSetBootCatalogPath(pOpts, ValueUnion.psz);
3505 break;
3506
3507
3508 /*
3509 * Image/namespace property related options.
3510 */
3511 case RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID:
3512 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
3513 break;
3514
3515 case 'A': /* --application-id */
3516 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
3517 break;
3518
3519 case RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID:
3520 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
3521 break;
3522
3523 case RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID:
3524 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
3525 break;
3526
3527 case 'P': /* -publisher */
3528 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
3529 break;
3530
3531 case 'p': /* --preparer*/
3532 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
3533 break;
3534
3535 case RTFSISOMAKERCMD_OPT_SYSTEM_ID:
3536 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
3537 break;
3538
3539 case RTFSISOMAKERCMD_OPT_VOLUME_ID: /* (should've been '-V') */
3540 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_ID);
3541 break;
3542
3543 case RTFSISOMAKERCMD_OPT_VOLUME_SET_ID:
3544 rc = rtFsIsoMakerCmdOptSetStringProp(pOpts, ValueUnion.psz, RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
3545 break;
3546
3547
3548 /*
3549 * Compatibility.
3550 */
3551 case RTFSISOMAKERCMD_OPT_GRAFT_POINTS:
3552 rc = rtFsIsoMakerCmdOptNameSetup(pOpts, "iso+joliet+udf+hfs");
3553 break;
3554
3555 case 'l':
3556 if (RTFsIsoMakerGetIso9660Level(pOpts->hIsoMaker) >= 2)
3557 rc = rtFsIsoMakerCmdOptSetIsoLevel(pOpts, 2);
3558 break;
3559
3560 case 'R':
3561 rc = rtFsIsoMakerCmdOptSetPrimaryRockLevel(pOpts, 2);
3562 if (RT_SUCCESS(rc))
3563 rc = rtFsIsoMakerCmdOptSetAttribInheritStyle(pOpts, true /*fStrict*/);
3564 break;
3565
3566 case 'r':
3567 rc = rtFsIsoMakerCmdOptSetPrimaryRockLevel(pOpts, 2);
3568 if (RT_SUCCESS(rc))
3569 rc = rtFsIsoMakerCmdOptSetAttribInheritStyle(pOpts, false /*fStrict*/);
3570 break;
3571
3572 case RTFSISOMAKERCMD_OPT_PAD:
3573 rc = RTFsIsoMakerSetImagePadding(pOpts->hIsoMaker, 150);
3574 if (RT_FAILURE(rc))
3575 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerSetImagePadding failed: %Rrc", rc);
3576 break;
3577
3578 case RTFSISOMAKERCMD_OPT_NO_PAD:
3579 rc = RTFsIsoMakerSetImagePadding(pOpts->hIsoMaker, 0);
3580 if (RT_FAILURE(rc))
3581 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTFsIsoMakerSetImagePadding failed: %Rrc", rc);
3582 break;
3583
3584
3585 /*
3586 * VISO specific
3587 */
3588 case RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER:
3589 /* ignored */
3590 break;
3591
3592
3593 /*
3594 * Testing.
3595 */
3596 case RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE: /* --output-buffer-size {cb} */
3597 pOpts->cbOutputReadBuffer = ValueUnion.u32;
3598 break;
3599
3600 case RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE: /* --random-output-buffer-size */
3601 pOpts->fRandomOutputReadBufferSize = true;
3602 break;
3603
3604 case RTFSISOMAKERCMD_OPT_RANDOM_ORDER_VERIFICATION: /* --random-order-verification {cb} */
3605 pOpts->cbRandomOrderVerifciationBlock = ValueUnion.u32;
3606 break;
3607
3608
3609 /*
3610 * Standard bits.
3611 */
3612 case 'h':
3613 rtFsIsoMakerCmdUsage(pOpts, papszArgs[0]);
3614 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_CALLBACK_RETURN;
3615
3616 case 'V':
3617 rtFsIsoMakerPrintf(pOpts, "%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
3618 return pOpts->fVirtualImageMaker ? VERR_NOT_FOUND : VINF_CALLBACK_RETURN;
3619
3620 default:
3621 if (rc > 0 && RT_C_IS_GRAPH(rc))
3622 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: -%c", rc);
3623 else if (rc > 0)
3624 rc = rtFsIsoMakerCmdErrorRc(pOpts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: %i (%#x)", rc, rc);
3625 else if (rc == VERR_GETOPT_UNKNOWN_OPTION)
3626 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Unknown option: '%s'", ValueUnion.psz);
3627 else if (ValueUnion.pDef)
3628 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%s: %Rrs", ValueUnion.pDef->pszLong, rc);
3629 else
3630 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "%Rrs", rc);
3631 return rc;
3632 }
3633 if (RT_FAILURE(rc))
3634 return rc;
3635 }
3636 return VINF_SUCCESS;
3637}
3638
3639
3640/**
3641 * Extended ISO maker command.
3642 *
3643 * This can be used as a ISO maker command that produces a image file, or
3644 * alternatively for setting up a virtual ISO in memory.
3645 *
3646 * @returns IPRT status code
3647 * @param cArgs Number of arguments.
3648 * @param papszArgs Pointer to argument array.
3649 * @param phVfsFile Where to return the virtual ISO. Pass NULL to
3650 * for normal operation (creates file on disk).
3651 * @param pErrInfo Where to return extended error information in
3652 * the virtual ISO mode.
3653 */
3654RTDECL(int) RTFsIsoMakerCmdEx(unsigned cArgs, char **papszArgs, PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo)
3655{
3656 /*
3657 * Create instance.
3658 */
3659 RTFSISOMAKERCMDOPTS Opts;
3660 RT_ZERO(Opts);
3661 Opts.hIsoMaker = NIL_RTFSISOMAKER;
3662 Opts.pErrInfo = pErrInfo;
3663 Opts.fVirtualImageMaker = phVfsFile != NULL;
3664 Opts.hSrcVfs = NIL_RTVFS;
3665 Opts.cNameSpecifiers = 1;
3666 Opts.afNameSpecifiers[0] = RTFSISOMAKERCMDNAME_MAJOR_MASK;
3667 Opts.fDstNamespaces = RTFSISOMAKERCMDNAME_MAJOR_MASK;
3668 Opts.pszTransTbl = "TRANS.TBL"; /** @todo query this below */
3669 if (phVfsFile)
3670 *phVfsFile = NIL_RTVFSFILE;
3671 for (uint32_t i = 0; i < RT_ELEMENTS(Opts.aBootCatEntries); i++)
3672 Opts.aBootCatEntries[i].u.Section.idxImageObj = UINT32_MAX;
3673
3674 /* Create the ISO creator instance. */
3675 int rc = RTFsIsoMakerCreate(&Opts.hIsoMaker);
3676 if (RT_FAILURE(rc))
3677 return rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreate failed: %Rrc", rc);
3678
3679 /*
3680 * Parse the command line and check for mandatory options.
3681 */
3682 rc = rtFsIsoMakerCmdParse(&Opts, cArgs, papszArgs, 0);
3683 if (RT_SUCCESS(rc) && rc != VINF_CALLBACK_RETURN)
3684 {
3685 if (!Opts.cItemsAdded)
3686 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_NO_DATA, "Cowardly refuses to create empty ISO image");
3687 else if (!Opts.pszOutFile && !Opts.fVirtualImageMaker)
3688 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_INVALID_PARAMETER, "No output file specified (--output <file>)");
3689
3690 /*
3691 * Final actions.
3692 */
3693 if (RT_SUCCESS(rc))
3694 rc = rtFsIsoMakerCmdOptEltoritoCommitBootCatalog(&Opts);
3695 if (RT_SUCCESS(rc))
3696 {
3697 /*
3698 * Finalize the image and get the virtual file.
3699 */
3700 rc = RTFsIsoMakerFinalize(Opts.hIsoMaker);
3701 if (RT_SUCCESS(rc))
3702 {
3703 RTVFSFILE hVfsFile;
3704 rc = RTFsIsoMakerCreateVfsOutputFile(Opts.hIsoMaker, &hVfsFile);
3705 if (RT_SUCCESS(rc))
3706 {
3707 /*
3708 * We're done now if we're only setting up a virtual image.
3709 */
3710 if (Opts.fVirtualImageMaker)
3711 *phVfsFile = hVfsFile;
3712 else
3713 {
3714 rc = rtFsIsoMakerCmdWriteImage(&Opts, hVfsFile);
3715 RTVfsFileRelease(hVfsFile);
3716 }
3717 }
3718 else
3719 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreateVfsOutputFile failed: %Rrc", rc);
3720 }
3721 else
3722 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerFinalize failed: %Rrc", rc);
3723 }
3724 }
3725
3726 return rtFsIsoMakerCmdDeleteState(&Opts, rc);
3727}
3728
3729
3730/**
3731 * ISO maker command (creates image file on disk).
3732 *
3733 * @returns IPRT status code
3734 * @param cArgs Number of arguments.
3735 * @param papszArgs Pointer to argument array.
3736 */
3737RTDECL(RTEXITCODE) RTFsIsoMakerCmd(unsigned cArgs, char **papszArgs)
3738{
3739 int rc = RTFsIsoMakerCmdEx(cArgs, papszArgs, NULL, NULL);
3740 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
3741}
3742
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