VirtualBox

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

Last change on this file since 84207 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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

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