VirtualBox

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

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

IPRT: More ISO maker code. (path table fixes)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.2 KB
Line 
1/* $Id: isomakercmd.cpp 67413 2017-06-14 21:25:14Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker Command.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsisomaker.h>
34
35#include <iprt/assert.h>
36#include <iprt/buildconfig.h>
37#include <iprt/ctype.h>
38#include <iprt/file.h>
39#include <iprt/err.h>
40#include <iprt/getopt.h>
41#include <iprt/log.h>
42#include <iprt/mem.h>
43#include <iprt/message.h>
44#include <iprt/path.h>
45#include <iprt/rand.h>
46#include <iprt/stream.h>
47#include <iprt/string.h>
48#include <iprt/vfs.h>
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54/** Maximum number of name specifiers we allow. */
55#define RTFSISOMAKERCMD_MAX_NAMES 8
56
57/** @name Name specifiers
58 * @{ */
59#define RTFSISOMAKERCMDNAME_PRIMARY_ISO RTFSISOMAKER_NAMESPACE_ISO_9660
60#define RTFSISOMAKERCMDNAME_JOLIET RTFSISOMAKER_NAMESPACE_JOLIET
61#define RTFSISOMAKERCMDNAME_UDF RTFSISOMAKER_NAMESPACE_UDF
62#define RTFSISOMAKERCMDNAME_HFS RTFSISOMAKER_NAMESPACE_HFS
63
64#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE RT_BIT_32(16)
65#define RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE RT_BIT_32(17)
66
67#define RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL RT_BIT_32(20)
68#define RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL RT_BIT_32(21)
69#define RTFSISOMAKERCMDNAME_UDF_TRANS_TBL RT_BIT_32(22)
70#define RTFSISOMAKERCMDNAME_HFS_TRANS_TBL RT_BIT_32(23)
71
72#define RTFSISOMAKERCMDNAME_MAJOR_MASK \
73 (RTFSISOMAKERCMDNAME_PRIMARY_ISO | RTFSISOMAKERCMDNAME_JOLIET | RTFSISOMAKERCMDNAME_UDF | RTFSISOMAKERCMDNAME_HFS)
74
75#define RTFSISOMAKERCMDNAME_MINOR_MASK \
76 ( RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE | RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL \
77 | RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE | RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL \
78 | RTFSISOMAKERCMDNAME_UDF_TRANS_TBL \
79 | RTFSISOMAKERCMDNAME_HFS_TRANS_TBL)
80AssertCompile((RTFSISOMAKERCMDNAME_MAJOR_MASK & RTFSISOMAKERCMDNAME_MINOR_MASK) == 0);
81/** @} */
82
83
84/*********************************************************************************************************************************
85* Structures and Typedefs *
86*********************************************************************************************************************************/
87typedef enum RTFSISOMAKERCMDOPT
88{
89 RTFSISOMAKERCMD_OPT_FIRST = 1000,
90
91 RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER,
92 RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE,
93 RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE,
94 RTFSISOMAKERCMD_OPT_NAME_SETUP,
95 RTFSISOMAKERCMD_OPT_NO_JOLIET,
96
97 /*
98 * Compatibility options:
99 */
100 RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID,
101 RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS,
102 RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE,
103 RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE,
104 RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT,
105 RTFSISOMAKERCMD_OPT_ALPHA_BOOT,
106 RTFSISOMAKERCMD_OPT_APPLE,
107 RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID,
108 RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES,
109 RTFSISOMAKERCMD_OPT_CHECK_SESSION,
110 RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID,
111 RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS,
112 RTFSISOMAKERCMD_OPT_DIR_MODE,
113 RTFSISOMAKERCMD_OPT_DVD_VIDEO,
114 RTFSISOMAKERCMD_OPT_ELTORITO_ALT_BOOT,
115 RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT,
116 RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE,
117 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG,
118 RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE,
119 RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT,
120 RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT,
121 RTFSISOMAKERCMD_OPT_EXCLUDE_LIST,
122 RTFSISOMAKERCMD_OPT_FILE_MODE,
123 RTFSISOMAKERCMD_OPT_FORCE_RR,
124 RTFSISOMAKERCMD_OPT_GID,
125 RTFSISOMAKERCMD_OPT_GRAFT_POINTS,
126 RTFSISOMAKERCMD_OPT_GUI,
127 RTFSISOMAKERCMD_OPT_HFS_AUTO,
128 RTFSISOMAKERCMD_OPT_HFS_BLESS,
129 RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE,
130 RTFSISOMAKERCMD_OPT_HFS_CAP,
131 RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT,
132 RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE,
133 RTFSISOMAKERCMD_OPT_HFS_CREATOR,
134 RTFSISOMAKERCMD_OPT_HFS_DAVE,
135 RTFSISOMAKERCMD_OPT_HFS_DOUBLE,
136 RTFSISOMAKERCMD_OPT_HFS_ENABLE,
137 RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE,
138 RTFSISOMAKERCMD_OPT_HFS_EXCHANGE,
139 RTFSISOMAKERCMD_OPT_HFS_HIDE,
140 RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST,
141 RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION,
142 RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET,
143 RTFSISOMAKERCMD_OPT_HFS_MAC_NAME,
144 RTFSISOMAKERCMD_OPT_HFS_MACBIN,
145 RTFSISOMAKERCMD_OPT_HFS_MAGIC,
146 RTFSISOMAKERCMD_OPT_HFS_MAP,
147 RTFSISOMAKERCMD_OPT_HFS_NETATALK,
148 RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP,
149 RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE,
150 RTFSISOMAKERCMD_OPT_HFS_OSX_HFS,
151 RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET,
152 RTFSISOMAKERCMD_OPT_HFS_PARMS,
153 RTFSISOMAKERCMD_OPT_HFS_PART,
154 RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT,
155 RTFSISOMAKERCMD_OPT_HFS_PROBE,
156 RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO,
157 RTFSISOMAKERCMD_OPT_HFS_SFM,
158 RTFSISOMAKERCMD_OPT_HFS_SGI,
159 RTFSISOMAKERCMD_OPT_HFS_SINGLE,
160 RTFSISOMAKERCMD_OPT_HFS_TYPE,
161 RTFSISOMAKERCMD_OPT_HFS_UNLOCK,
162 RTFSISOMAKERCMD_OPT_HFS_USHARE,
163 RTFSISOMAKERCMD_OPT_HFS_VOL_ID,
164 RTFSISOMAKERCMD_OPT_HFS_XINET,
165 RTFSISOMAKERCMD_OPT_HIDDEN,
166 RTFSISOMAKERCMD_OPT_HIDDEN_LIST,
167 RTFSISOMAKERCMD_OPT_HIDE,
168 RTFSISOMAKERCMD_OPT_HIDE_JOLIET,
169 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST,
170 RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL,
171 RTFSISOMAKERCMD_OPT_HIDE_LIST,
172 RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED,
173 RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER,
174 RTFSISOMAKERCMD_OPT_HPPA_CMDLINE,
175 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32,
176 RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64,
177 RTFSISOMAKERCMD_OPT_HPPA_RAMDISK,
178 RTFSISOMAKERCMD_OPT_INPUT_CHARSET,
179 RTFSISOMAKERCMD_OPT_ISO_LEVEL,
180 RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS,
181 RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE,
182 RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5,
183 RTFSISOMAKERCMD_OPT_JIGDO_JIGDO,
184 RTFSISOMAKERCMD_OPT_JIGDO_MAP,
185 RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST,
186 RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE,
187 RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE,
188 RTFSISOMAKERCMD_OPT_JOLIET_CHARSET,
189 RTFSISOMAKERCMD_OPT_JOLIET_LEVEL,
190 RTFSISOMAKERCMD_OPT_JOLIET_LONG,
191 RTFSISOMAKERCMD_OPT_LOG_FILE,
192 RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES,
193 RTFSISOMAKERCMD_OPT_MIPS_BOOT,
194 RTFSISOMAKERCMD_OPT_MIPSEL_BOOT,
195 RTFSISOMAKERCMD_OPT_NEW_DIR_MODE,
196 RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES,
197 RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS,
198 RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE,
199 RTFSISOMAKERCMD_OPT_NO_PAD,
200 RTFSISOMAKERCMD_OPT_NO_RR,
201 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS,
202 RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS,
203 RTFSISOMAKERCMD_OPT_OLD_ROOT,
204 RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET,
205 RTFSISOMAKERCMD_OPT_PAD,
206 RTFSISOMAKERCMD_OPT_PATH_LIST,
207 RTFSISOMAKERCMD_OPT_PRINT_SIZE,
208 RTFSISOMAKERCMD_OPT_QUIET,
209 RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES,
210 RTFSISOMAKERCMD_OPT_ROOT,
211 RTFSISOMAKERCMD_OPT_SORT,
212 RTFSISOMAKERCMD_OPT_SPARC_BOOT,
213 RTFSISOMAKERCMD_OPT_SPARC_LABEL,
214 RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT,
215 RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME,
216 RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE,
217 RTFSISOMAKERCMD_OPT_SUNX86_BOOT,
218 RTFSISOMAKERCMD_OPT_SUNX86_LABEL,
219 RTFSISOMAKERCMD_OPT_SYSTEM_ID,
220 RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME,
221 RTFSISOMAKERCMD_OPT_UDF,
222 RTFSISOMAKERCMD_OPT_UID,
223 RTFSISOMAKERCMD_OPT_USE_FILE_VERSION,
224 RTFSISOMAKERCMD_OPT_VOLUME_SET_ID,
225 RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO,
226 RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE,
227 RTFSISOMAKERCMD_OPT_END
228} RTFSISOMAKERCMDOPT;
229
230/**
231 * ISO maker command options & state.
232 */
233typedef struct RTFSISOMAKERCMDOPTS
234{
235 /** The handle to the ISO maker. */
236 RTFSISOMAKER hIsoMaker;
237 /** Set if we're creating a virtual image maker, i.e. producing something
238 * that is going to be read from only and not written to disk. */
239 bool fVirtualImageMaker;
240 /** Extended error info. This is a stderr alternative for the
241 * fVirtualImageMaker case (stdout goes to LogRel). */
242 PRTERRINFO pErrInfo;
243
244 /** The output file.
245 * This is NULL when fVirtualImageMaker is set. */
246 const char *pszOutFile;
247 /** Special buffer size to use for testing the ISO maker code reading. */
248 uint32_t cbOutputReadBuffer;
249 /** Use random output read buffer size. cbOutputReadBuffer works as maximum
250 * when this is enabled. */
251 bool fRandomOutputReadBufferSize;
252
253 /** @name Processing of inputs
254 * @{ */
255 /** The namespaces (RTFSISOMAKER_NAMESPACE_XXX) we're currently adding
256 * input to. */
257 uint32_t fDstNamespaces;
258 /** The number of name specifiers we're currently operating with. */
259 uint32_t cNameSpecifiers;
260 /** Name specifier configurations.
261 * For instance given "name0=name1=name2=name3=source-file" we will add
262 * source-file to the image with name0 as the name in the namespace and
263 * sub-name specified by aNameSpecifiers[0], name1 in aNameSpecifiers[1],
264 * and so on. This allows exact control over which names a file will
265 * have in each namespace (primary-iso, joliet, udf, hfs) and sub-namespace
266 * (rock-ridge, trans.tbl).
267 */
268 uint32_t afNameSpecifiers[RTFSISOMAKERCMD_MAX_NAMES];
269 /** @} */
270
271 /** Number of items (files, directories, images, whatever) we've added. */
272 uint32_t cItemsAdded;
273} RTFSISOMAKERCMDOPTS;
274typedef RTFSISOMAKERCMDOPTS *PRTFSISOMAKERCMDOPTS;
275typedef RTFSISOMAKERCMDOPTS const *PCRTFSISOMAKERCMDOPTS;
276
277
278/**
279 * Parsed name.
280 */
281typedef struct RTFSISOMAKERCMDPARSEDNAME
282{
283 /** Copy of the corresponding RTFSISOMAKERCMDOPTS::afNameSpecifiers
284 * value. */
285 uint32_t fNameSpecifiers;
286 /** The length of the specified path. */
287 uint32_t cchPath;
288 /** Specified path. */
289 char szPath[RTPATH_MAX];
290} RTFSISOMAKERCMDPARSEDNAME;
291/** Pointer to a parsed name. */
292typedef RTFSISOMAKERCMDPARSEDNAME *PRTFSISOMAKERCMDPARSEDNAME;
293/** Pointer to a const parsed name. */
294typedef RTFSISOMAKERCMDPARSEDNAME const *PCRTFSISOMAKERCMDPARSEDNAME;
295
296
297/*********************************************************************************************************************************
298* Global Variables *
299*********************************************************************************************************************************/
300/*
301 * Parse the command line. This is similar to genisoimage and mkisofs,
302 * thus the single dash long name aliases.
303 */
304static const RTGETOPTDEF g_aRtFsIsoMakerOptions[] =
305{
306 /*
307 * Unquie IPRT ISO maker options.
308 */
309 { "--iprt-iso-maker-file-marker", RTFSISOMAKERCMD_OPT_IPRT_ISO_MAKER_FILE_MARKER, RTGETOPT_REQ_NOTHING },
310 { "--output-buffer-size", RTFSISOMAKERCMD_OPT_OUTPUT_BUFFER_SIZE, RTGETOPT_REQ_UINT32 },
311 { "--random-output-buffer-size", RTFSISOMAKERCMD_OPT_RANDOM_OUTPUT_BUFFER_SIZE, RTGETOPT_REQ_NOTHING },
312 { "--name-setup", RTFSISOMAKERCMD_OPT_NAME_SETUP, RTGETOPT_REQ_STRING },
313 { "--no-joliet", RTFSISOMAKERCMD_OPT_NO_JOLIET, RTGETOPT_REQ_NOTHING },
314
315
316 /*
317 * genisoimage/mkisofs compatibility:
318 */
319#define DD(a_szLong, a_chShort, a_fFlags) { a_szLong, a_chShort, a_fFlags }, { "-" a_szLong, a_chShort, a_fFlags }
320 DD("-abstract", RTFSISOMAKERCMD_OPT_ABSTRACT_FILE_ID, RTGETOPT_REQ_STRING ),
321 { "--application-id", 'A', RTGETOPT_REQ_STRING },
322 DD("-allow-limited-size", RTFSISOMAKERCMD_OPT_ALLOW_LIMITED_SIZE, RTGETOPT_REQ_NOTHING ),
323 DD("-allow-leading-dots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
324 DD("-ldots", RTFSISOMAKERCMD_OPT_ALLOW_LEADING_DOTS, RTGETOPT_REQ_NOTHING ),
325 DD("-allow-lowercase", RTFSISOMAKERCMD_OPT_ALLOW_LOWERCASE, RTGETOPT_REQ_NOTHING ),
326 DD("-allow-multidot", RTFSISOMAKERCMD_OPT_ALLOW_MULTI_DOT, RTGETOPT_REQ_NOTHING ),
327 DD("-biblio", RTFSISOMAKERCMD_OPT_BIBLIOGRAPHIC_FILE_ID, RTGETOPT_REQ_STRING ),
328 DD("-cache-inodes", RTFSISOMAKERCMD_OPT_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
329 DD("-no-cache-inodes", RTFSISOMAKERCMD_OPT_NO_DETECT_HARDLINKS, RTGETOPT_REQ_NOTHING ),
330 DD("-alpha-boot", RTFSISOMAKERCMD_OPT_ALPHA_BOOT, RTGETOPT_REQ_STRING ),
331 DD("-hppa-bootloader", RTFSISOMAKERCMD_OPT_HPPA_BOOTLOADER, RTGETOPT_REQ_STRING ),
332 DD("-hppa-cmdline", RTFSISOMAKERCMD_OPT_HPPA_CMDLINE, RTGETOPT_REQ_STRING ),
333 DD("-hppa-kernel-32", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_32, RTGETOPT_REQ_STRING ),
334 DD("-hppa-kernel-64", RTFSISOMAKERCMD_OPT_HPPA_KERNEL_64, RTGETOPT_REQ_STRING ),
335 DD("-hppa-ramdisk", RTFSISOMAKERCMD_OPT_HPPA_RAMDISK, RTGETOPT_REQ_STRING ),
336 DD("-mips-boot", RTFSISOMAKERCMD_OPT_MIPS_BOOT, RTGETOPT_REQ_STRING ),
337 DD("-mipsel-boot", RTFSISOMAKERCMD_OPT_MIPSEL_BOOT, RTGETOPT_REQ_STRING ),
338 DD("-sparc-boot", 'B', RTGETOPT_REQ_STRING ),
339 { "--generic-boot", 'G', RTGETOPT_REQ_STRING },
340 { "--eltorito-boot", 'b', RTGETOPT_REQ_STRING },
341 DD("-eltorito-alt-boot", RTFSISOMAKERCMD_OPT_ELTORITO_ALT_BOOT, RTGETOPT_REQ_NOTHING ),
342 DD("-hard-disk-boot", RTFSISOMAKERCMD_OPT_ELTORITO_HARD_DISK_BOOT, RTGETOPT_REQ_NOTHING ),
343 DD("-no-emulation-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NO_EMULATION_BOOT, RTGETOPT_REQ_NOTHING ),
344 DD("-no-boot", RTFSISOMAKERCMD_OPT_ELTORITO_NO_BOOT, RTGETOPT_REQ_NOTHING ),
345 DD("-boot-load-seg", RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SEG, RTGETOPT_REQ_UINT32 ),
346 DD("-boot-load-size", RTFSISOMAKERCMD_OPT_ELTORITO_LOAD_SIZE, RTGETOPT_REQ_UINT32 ),
347 DD("-boot-info-table", RTFSISOMAKERCMD_OPT_ELTORITO_INFO_TABLE, RTGETOPT_REQ_STRING ),
348 { "--cd-extra", 'C', RTGETOPT_REQ_STRING },
349 { "--boot-catalog", 'c', RTGETOPT_REQ_STRING },
350 DD("-check-oldnames", RTFSISOMAKERCMD_OPT_CHECK_OLD_NAMES, RTGETOPT_REQ_NOTHING ),
351 DD("-check-session", RTFSISOMAKERCMD_OPT_CHECK_SESSION, RTGETOPT_REQ_STRING ),
352 DD("-copyright", RTFSISOMAKERCMD_OPT_COPYRIGHT_FILE_ID, RTGETOPT_REQ_STRING ),
353 { "--dont-append-dot", 'd', RTGETOPT_REQ_NOTHING },
354 { "--deep-directories", 'D', RTGETOPT_REQ_NOTHING },
355 DD("-dir-mode", RTFSISOMAKERCMD_OPT_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
356 DD("-dvd-video", RTFSISOMAKERCMD_OPT_DVD_VIDEO, RTGETOPT_REQ_NOTHING ),
357 DD("-follow-symlinks", 'f', RTGETOPT_REQ_NOTHING ),
358 DD("-file-mode", RTFSISOMAKERCMD_OPT_FILE_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
359 DD("-gid", RTFSISOMAKERCMD_OPT_GID, RTGETOPT_REQ_UINT32 ),
360 DD("-gui", RTFSISOMAKERCMD_OPT_GUI, RTGETOPT_REQ_NOTHING ),
361 DD("-graft-points", RTFSISOMAKERCMD_OPT_GRAFT_POINTS, RTGETOPT_REQ_NOTHING ),
362 DD("-hide", RTFSISOMAKERCMD_OPT_HIDE, RTGETOPT_REQ_STRING ),
363 DD("-hide-list", RTFSISOMAKERCMD_OPT_HIDE_LIST, RTGETOPT_REQ_STRING ),
364 DD("-hidden", RTFSISOMAKERCMD_OPT_HIDDEN, RTGETOPT_REQ_STRING ),
365 DD("-hidden-list", RTFSISOMAKERCMD_OPT_HIDDEN_LIST, RTGETOPT_REQ_STRING ),
366 DD("-hide-joliet", RTFSISOMAKERCMD_OPT_HIDE_JOLIET, RTGETOPT_REQ_STRING ),
367 DD("-hide-joliet-list", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_LIST, RTGETOPT_REQ_STRING ),
368 DD("-hide-joliet-trans-tbl", RTFSISOMAKERCMD_OPT_HIDE_JOLIET_TRANS_TBL, RTGETOPT_REQ_NOTHING ),
369 DD("-hide-rr-moved", RTFSISOMAKERCMD_OPT_HIDE_RR_MOVED, RTGETOPT_REQ_NOTHING ),
370 DD("-input-charset", RTFSISOMAKERCMD_OPT_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
371 DD("-output-charset", RTFSISOMAKERCMD_OPT_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
372 { "--iso-level", RTFSISOMAKERCMD_OPT_ISO_LEVEL, RTGETOPT_REQ_UINT8 },
373 { "--joliet", 'J', RTGETOPT_REQ_NOTHING },
374 DD("-joliet-long", RTFSISOMAKERCMD_OPT_JOLIET_LONG, RTGETOPT_REQ_NOTHING ),
375 DD("-jcharset", RTFSISOMAKERCMD_OPT_JOLIET_CHARSET, RTGETOPT_REQ_STRING ),
376 { "--long-names", 'l', RTGETOPT_REQ_NOTHING },
377 { "--leading-dot", 'L', RTGETOPT_REQ_NOTHING },
378 DD("-jigdo-jigdo", RTFSISOMAKERCMD_OPT_JIGDO_JIGDO, RTGETOPT_REQ_STRING ),
379 DD("-jigdo-template", RTFSISOMAKERCMD_OPT_JIGDO_TEMPLATE, RTGETOPT_REQ_STRING ),
380 DD("-jigdo-min-file-size", RTFSISOMAKERCMD_OPT_JIGDO_MIN_FILE_SIZE, RTGETOPT_REQ_UINT64 ),
381 DD("-jigdo-force-md5", RTFSISOMAKERCMD_OPT_JIGDO_FORCE_MD5, RTGETOPT_REQ_STRING ),
382 DD("-jigdo-exclude", RTFSISOMAKERCMD_OPT_JIGDO_EXCLUDE, RTGETOPT_REQ_STRING ),
383 DD("-jigdo-map", RTFSISOMAKERCMD_OPT_JIGDO_MAP, RTGETOPT_REQ_STRING ),
384 DD("-md5-list", RTFSISOMAKERCMD_OPT_JIGDO_MD5_LIST, RTGETOPT_REQ_STRING ),
385 DD("-jigdo-template-compress", RTFSISOMAKERCMD_OPT_JIGDO_COMPRESS, RTGETOPT_REQ_STRING ),
386 DD("-log-file", RTFSISOMAKERCMD_OPT_LOG_FILE, RTGETOPT_REQ_STRING ),
387 { "--exclude", 'm', RTGETOPT_REQ_STRING },
388 { "--exclude", 'x', RTGETOPT_REQ_STRING },
389 DD("-exclude-list", RTFSISOMAKERCMD_OPT_EXCLUDE_LIST, RTGETOPT_REQ_STRING ),
390 DD("-max-iso9660-filenames", RTFSISOMAKERCMD_OPT_MAX_ISO9660_FILENAMES, RTGETOPT_REQ_NOTHING ),
391 { "--merge", 'M', RTGETOPT_REQ_STRING },
392 DD("-dev", 'M', RTGETOPT_REQ_STRING ),
393 { "--omit-version-numbers", 'N', RTGETOPT_REQ_NOTHING },
394 DD("-new-dir-mode", RTFSISOMAKERCMD_OPT_NEW_DIR_MODE, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT ),
395 DD("-nobak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
396 DD("-no-bak", RTFSISOMAKERCMD_OPT_NO_BACKUP_FILES, RTGETOPT_REQ_NOTHING ),
397 DD("-force-rr", RTFSISOMAKERCMD_OPT_FORCE_RR, RTGETOPT_REQ_NOTHING ),
398 DD("-no-rr", RTFSISOMAKERCMD_OPT_NO_RR, RTGETOPT_REQ_NOTHING ),
399 DD("-no-split-symlink-components", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_COMPONENTS, RTGETOPT_REQ_NOTHING ),
400 DD("-no-split-symlink-fields", RTFSISOMAKERCMD_OPT_NO_SPLIT_SYMLINK_FIELDS, RTGETOPT_REQ_NOTHING ),
401 { "--output", 'o', RTGETOPT_REQ_STRING },
402 DD("-pad", RTFSISOMAKERCMD_OPT_PAD, RTGETOPT_REQ_NOTHING ),
403 DD("-no-pad", RTFSISOMAKERCMD_OPT_NO_PAD, RTGETOPT_REQ_NOTHING ),
404 DD("-path-list", RTFSISOMAKERCMD_OPT_PATH_LIST, RTGETOPT_REQ_STRING ),
405 DD("-publisher", 'P', RTGETOPT_REQ_STRING ),
406 { "--preparer", 'p', RTGETOPT_REQ_STRING },
407 DD("-print-size", RTFSISOMAKERCMD_OPT_PRINT_SIZE, RTGETOPT_REQ_NOTHING ),
408 DD("-quiet", RTFSISOMAKERCMD_OPT_QUIET, RTGETOPT_REQ_NOTHING ),
409 { "--rock-ridge", 'R', RTGETOPT_REQ_NOTHING },
410 { "--rock-ridge-relaxed", 'r', RTGETOPT_REQ_NOTHING },
411 DD("-relaxed-filenames", RTFSISOMAKERCMD_OPT_RELAXED_FILENAMES, RTGETOPT_REQ_NOTHING ),
412 DD("-root", RTFSISOMAKERCMD_OPT_ROOT, RTGETOPT_REQ_STRING ),
413 DD("-old-root", RTFSISOMAKERCMD_OPT_OLD_ROOT, RTGETOPT_REQ_STRING ),
414 DD("-sort", RTFSISOMAKERCMD_OPT_SORT, RTGETOPT_REQ_STRING ),
415 DD("-sparc-boot", RTFSISOMAKERCMD_OPT_SPARC_BOOT, RTGETOPT_REQ_STRING ),
416 DD("-sparc-label", RTFSISOMAKERCMD_OPT_SPARC_LABEL, RTGETOPT_REQ_STRING ),
417 DD("-split-output", RTFSISOMAKERCMD_OPT_SPLIT_OUTPUT, RTGETOPT_REQ_NOTHING ),
418 DD("-stream-media-size", RTFSISOMAKERCMD_OPT_STREAM_MEDIA_SIZE, RTGETOPT_REQ_UINT64 ),
419 DD("-stream-file-name", RTFSISOMAKERCMD_OPT_STREAM_FILE_NAME, RTGETOPT_REQ_STRING ),
420 DD("-sunx86-boot", RTFSISOMAKERCMD_OPT_SUNX86_BOOT, RTGETOPT_REQ_STRING ),
421 DD("-sunx86-label", RTFSISOMAKERCMD_OPT_SUNX86_LABEL, RTGETOPT_REQ_STRING ),
422 DD("-sysid", RTFSISOMAKERCMD_OPT_SYSTEM_ID, RTGETOPT_REQ_STRING ),
423 { "--trans-tbl", 'T', RTGETOPT_REQ_NOTHING },
424 DD("-table-name", RTFSISOMAKERCMD_OPT_TRANS_TBL_NAME, RTGETOPT_REQ_STRING ),
425 DD("-ucs-level", RTFSISOMAKERCMD_OPT_JOLIET_LEVEL, RTGETOPT_REQ_UINT8 ),
426 DD("-udf", RTFSISOMAKERCMD_OPT_UDF, RTGETOPT_REQ_NOTHING ),
427 DD("-uid", RTFSISOMAKERCMD_OPT_UID, RTGETOPT_REQ_UINT32 ),
428 DD("-use-fileversion", RTFSISOMAKERCMD_OPT_USE_FILE_VERSION, RTGETOPT_REQ_NOTHING ),
429 { "--untranslated-filenames", 'U', RTGETOPT_REQ_NOTHING },
430 DD("-no-iso-translate", RTFSISOMAKERCMD_OPT_NO_ISO_TRANSLATE, RTGETOPT_REQ_NOTHING ),
431 { "--volume-id", 'V', RTGETOPT_REQ_STRING },
432 DD("-volset", RTFSISOMAKERCMD_OPT_VOLUME_SET_ID, RTGETOPT_REQ_STRING ),
433 DD("-volset-size", RTFSISOMAKERCMD_OPT_VOLUME_SET_SIZE, RTGETOPT_REQ_UINT32 ),
434 DD("-volset-seqno", RTFSISOMAKERCMD_OPT_VOLUME_SET_SEQ_NO, RTGETOPT_REQ_UINT32 ),
435 { "--transpared-compression", 'z', RTGETOPT_REQ_NOTHING },
436
437 /* HFS and ISO-9660 apple extensions. */
438 DD("-hfs", RTFSISOMAKERCMD_OPT_HFS_ENABLE, RTGETOPT_REQ_NOTHING ),
439 DD("-apple", RTFSISOMAKERCMD_OPT_APPLE, RTGETOPT_REQ_NOTHING ),
440 DD("-map", RTFSISOMAKERCMD_OPT_HFS_MAP, RTGETOPT_REQ_STRING ),
441 DD("-magic", RTFSISOMAKERCMD_OPT_HFS_MAGIC, RTGETOPT_REQ_STRING ),
442 DD("-hfs-creator", RTFSISOMAKERCMD_OPT_HFS_CREATOR, RTGETOPT_REQ_STRING ),
443 DD("-hfs-type", RTFSISOMAKERCMD_OPT_HFS_TYPE, RTGETOPT_REQ_STRING ),
444 DD("-probe", RTFSISOMAKERCMD_OPT_HFS_PROBE, RTGETOPT_REQ_NOTHING ),
445 DD("-no-desktop", RTFSISOMAKERCMD_OPT_HFS_NO_DESKTOP, RTGETOPT_REQ_NOTHING ),
446 DD("-mac-name", RTFSISOMAKERCMD_OPT_HFS_MAC_NAME, RTGETOPT_REQ_NOTHING ),
447 DD("-boot-hfs-file", RTFSISOMAKERCMD_OPT_HFS_BOOT_FILE, RTGETOPT_REQ_STRING ),
448 DD("-part", RTFSISOMAKERCMD_OPT_HFS_PART, RTGETOPT_REQ_NOTHING ),
449 DD("-auto", RTFSISOMAKERCMD_OPT_HFS_AUTO, RTGETOPT_REQ_STRING ),
450 DD("-cluster-size", RTFSISOMAKERCMD_OPT_HFS_CLUSTER_SIZE, RTGETOPT_REQ_UINT32 ),
451 DD("-hide-hfs", RTFSISOMAKERCMD_OPT_HFS_HIDE, RTGETOPT_REQ_STRING ),
452 DD("-hide-hfs-list", RTFSISOMAKERCMD_OPT_HFS_HIDE_LIST, RTGETOPT_REQ_STRING ),
453 DD("-hfs-volid", RTFSISOMAKERCMD_OPT_HFS_VOL_ID, RTGETOPT_REQ_STRING ),
454 DD("-icon-position", RTFSISOMAKERCMD_OPT_HFS_ICON_POSITION, RTGETOPT_REQ_NOTHING ),
455 DD("-root-info", RTFSISOMAKERCMD_OPT_HFS_ROOT_INFO, RTGETOPT_REQ_STRING ),
456 DD("-prep-boot", RTFSISOMAKERCMD_OPT_HFS_PREP_BOOT, RTGETOPT_REQ_STRING ),
457 DD("-chrp-boot", RTFSISOMAKERCMD_OPT_HFS_CHRP_BOOT, RTGETOPT_REQ_NOTHING ),
458 DD("-input-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_INPUT_CHARSET, RTGETOPT_REQ_STRING ),
459 DD("-output-hfs-charset", RTFSISOMAKERCMD_OPT_HFS_OUTPUT_CHARSET, RTGETOPT_REQ_STRING ),
460 DD("-hfs-unlock", RTFSISOMAKERCMD_OPT_HFS_UNLOCK, RTGETOPT_REQ_NOTHING ),
461 DD("-hfs-bless", RTFSISOMAKERCMD_OPT_HFS_BLESS, RTGETOPT_REQ_STRING ),
462 DD("-hfs-parms", RTFSISOMAKERCMD_OPT_HFS_PARMS, RTGETOPT_REQ_STRING ),
463 { "--cap", RTFSISOMAKERCMD_OPT_HFS_CAP, RTGETOPT_REQ_NOTHING },
464 { "--netatalk", RTFSISOMAKERCMD_OPT_HFS_NETATALK, RTGETOPT_REQ_NOTHING },
465 { "--double", RTFSISOMAKERCMD_OPT_HFS_DOUBLE, RTGETOPT_REQ_NOTHING },
466 { "--ethershare", RTFSISOMAKERCMD_OPT_HFS_ETHERSHARE, RTGETOPT_REQ_NOTHING },
467 { "--ushare", RTFSISOMAKERCMD_OPT_HFS_USHARE, RTGETOPT_REQ_NOTHING },
468 { "--exchange", RTFSISOMAKERCMD_OPT_HFS_EXCHANGE, RTGETOPT_REQ_NOTHING },
469 { "--sgi", RTFSISOMAKERCMD_OPT_HFS_SGI, RTGETOPT_REQ_NOTHING },
470 { "--xinet", RTFSISOMAKERCMD_OPT_HFS_XINET, RTGETOPT_REQ_NOTHING },
471 { "--macbin", RTFSISOMAKERCMD_OPT_HFS_MACBIN, RTGETOPT_REQ_NOTHING },
472 { "--single", RTFSISOMAKERCMD_OPT_HFS_SINGLE, RTGETOPT_REQ_NOTHING },
473 { "--dave", RTFSISOMAKERCMD_OPT_HFS_DAVE, RTGETOPT_REQ_NOTHING },
474 { "--sfm", RTFSISOMAKERCMD_OPT_HFS_SFM, RTGETOPT_REQ_NOTHING },
475 { "--osx-double", RTFSISOMAKERCMD_OPT_HFS_OSX_DOUBLE, RTGETOPT_REQ_NOTHING },
476 { "--osx-hfs", RTFSISOMAKERCMD_OPT_HFS_OSX_HFS, RTGETOPT_REQ_NOTHING },
477#undef DD
478};
479
480
481/**
482 * Wrapper around RTErrInfoSetV / RTMsgErrorV.
483 *
484 * @returns @a rc
485 * @param pOpts The ISO maker command instance.
486 * @param rc The return code.
487 * @param pszFormat The message format.
488 * @param ... The message format arguments.
489 */
490static int rtFsIsoMakerCmdErrorRc(PRTFSISOMAKERCMDOPTS pOpts, int rc, const char *pszFormat, ...)
491{
492 va_list va;
493 va_start(va, pszFormat);
494 if (pOpts->pErrInfo)
495 RTErrInfoSetV(pOpts->pErrInfo, rc, pszFormat, va);
496 else
497 RTMsgErrorV(pszFormat, va);
498 va_end(va);
499 return rc;
500}
501
502
503/**
504 * Wrapper around RTErrInfoSetV / RTMsgErrorV for displaying syntax errors.
505 *
506 * @returns VERR_INVALID_PARAMETER
507 * @param pOpts The ISO maker command instance.
508 * @param pszFormat The message format.
509 * @param ... The message format arguments.
510 */
511static int rtFsIsoMakerCmdSyntaxError(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
512{
513 va_list va;
514 va_start(va, pszFormat);
515 if (pOpts->pErrInfo)
516 RTErrInfoSetV(pOpts->pErrInfo, VERR_INVALID_PARAMETER, pszFormat, va);
517 else
518 RTMsgErrorV(pszFormat, va);
519 va_end(va);
520 return VERR_INVALID_PARAMETER;
521}
522
523
524/**
525 * Wrapper around RTPrintfV / RTLogRelPrintfV.
526 *
527 * @param pOpts The ISO maker command instance.
528 * @param pszFormat The message format.
529 * @param ... The message format arguments.
530 */
531static void rtFsIsoMakerPrintf(PRTFSISOMAKERCMDOPTS pOpts, const char *pszFormat, ...)
532{
533 va_list va;
534 va_start(va, pszFormat);
535 if (pOpts->pErrInfo)
536 RTLogRelPrintfV(pszFormat, va);
537 else
538 RTPrintfV(pszFormat, va);
539 va_end(va);
540}
541
542/**
543 * Deletes the state and returns @a rc.
544 *
545 * @returns @a rc.
546 * @param pOpts The ISO maker command instance to delete.
547 * @param rc The status code to return.
548 */
549static int rtFsIsoMakerCmdDeleteState(PRTFSISOMAKERCMDOPTS pOpts, int rc)
550{
551 if (pOpts->hIsoMaker != NIL_RTFSISOMAKER)
552 {
553 RTFsIsoMakerRelease(pOpts->hIsoMaker);
554 pOpts->hIsoMaker = NIL_RTFSISOMAKER;
555 }
556
557 return rc;
558}
559
560
561static void rtFsIsoMakerCmdUsage(PRTFSISOMAKERCMDOPTS pOpts, const char *pszProgName)
562{
563 rtFsIsoMakerPrintf(pOpts, "usage: %s [options] <file>=<cdfile>\n", pszProgName);
564}
565
566
567/**
568 * Writes the image to file.
569 *
570 * @returns IPRT status code.
571 * @param pOpts The ISO maker command instance.
572 * @param hVfsSrcFile The source file from the ISO maker.
573 */
574static int rtFsIsoMakerCmdWriteImage(PRTFSISOMAKERCMDOPTS pOpts, RTVFSFILE hVfsSrcFile)
575{
576 /*
577 * Get the image size and setup the copy buffer.
578 */
579 uint64_t cbImage;
580 int rc = RTVfsFileGetSize(hVfsSrcFile, &cbImage);
581 if (RT_SUCCESS(rc))
582 {
583 rtFsIsoMakerPrintf(pOpts, "Image size: %'RU64 (%#RX64) bytes\n", cbImage, cbImage);
584
585 uint32_t cbBuf = pOpts->cbOutputReadBuffer == 0 ? _1M : pOpts->cbOutputReadBuffer;
586 void *pvBuf = RTMemTmpAlloc(cbBuf);
587 if (pvBuf)
588 {
589 /*
590 * Open the output file.
591 */
592 RTVFSFILE hVfsDstFile;
593 uint32_t offError;
594 RTERRINFOSTATIC ErrInfo;
595 rc = RTVfsChainOpenFile(pOpts->pszOutFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE,
596 &hVfsDstFile, &offError, RTErrInfoInitStatic(&ErrInfo));
597 if (RT_SUCCESS(rc))
598 {
599 /*
600 * Copy the virtual image bits to the destination file.
601 */
602 uint64_t offImage = 0;
603 while (offImage < cbImage)
604 {
605 /* Figure out how much to copy this time. */
606 size_t cbToCopy = cbBuf;
607 if (pOpts->fRandomOutputReadBufferSize)
608 cbToCopy = RTRandU32Ex(1, (uint32_t)cbBuf - 1);
609 if (offImage + cbToCopy < cbImage)
610 { /* likely */ }
611 else
612 cbToCopy = (size_t)(cbImage - offImage);
613
614 /* Do the copying. */
615 rc = RTVfsFileReadAt(hVfsSrcFile, offImage, pvBuf, cbToCopy, NULL);
616 if (RT_SUCCESS(rc))
617 {
618 rc = RTVfsFileWriteAt(hVfsDstFile, offImage, pvBuf, cbToCopy, NULL);
619 if (RT_SUCCESS(rc))
620 offImage += cbToCopy;
621 else
622 {
623 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc writing %#zx bytes at offset %#RX64 to '%s'",
624 rc, cbToCopy, offImage, pOpts->pszOutFile);
625 break;
626 }
627 }
628 else
629 {
630 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error %Rrc read %#zx bytes at offset %#RX64", rc, cbToCopy, offImage);
631 break;
632 }
633 }
634
635 /*
636 * Flush the output file before releasing it.
637 */
638 if (RT_SUCCESS(rc))
639 {
640 rc = RTVfsFileFlush(hVfsDstFile);
641 if (RT_FAILURE(rc))
642 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileFlush failed on '%s': %Rrc", pOpts->pszOutFile, rc);
643 }
644
645 RTVfsFileRelease(hVfsDstFile);
646 }
647 else if (RTErrInfoIsSet(&ErrInfo.Core))
648 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsChainOpenFile(%s) failed: %Rrc - %s",
649 pOpts->cbOutputReadBuffer, rc, ErrInfo.Core.pszMsg);
650 else
651 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsChainOpenFile(%s) failed: %Rrc", pOpts->cbOutputReadBuffer, rc);
652
653 RTMemTmpFree(pvBuf);
654 }
655 else
656 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTMemTmpAlloc(%zu) failed: %Rrc", pOpts->cbOutputReadBuffer, rc);
657 }
658 else
659 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsFileGetSize failed: %Rrc", rc);
660 return rc;
661}
662
663
664/**
665 * Formats @a fNameSpecifiers into a '+' separated list of names.
666 *
667 * @returns pszDst
668 * @param fNameSpecifiers The name specifiers.
669 * @param pszDst The destination bufer.
670 * @param cbDst The size of the destination buffer.
671 */
672static char *rtFsIsoMakerCmdNameSpecifiersToString(uint32_t fNameSpecifiers, char *pszDst, size_t cbDst)
673{
674 static struct { const char *pszName; uint32_t cchName; uint32_t fSpec; } const s_aSpecs[] =
675 {
676 { RT_STR_TUPLE("primary"), RTFSISOMAKERCMDNAME_PRIMARY_ISO },
677 { RT_STR_TUPLE("primary-rock"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE },
678 { RT_STR_TUPLE("primary-trans-tbl"), RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL },
679 { RT_STR_TUPLE("joliet"), RTFSISOMAKERCMDNAME_JOLIET },
680 { RT_STR_TUPLE("joliet-rock"), RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE },
681 { RT_STR_TUPLE("joliet-trans-tbl"), RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL },
682 { RT_STR_TUPLE("udf"), RTFSISOMAKERCMDNAME_UDF },
683 { RT_STR_TUPLE("udf-trans-tbl"), RTFSISOMAKERCMDNAME_UDF_TRANS_TBL },
684 { RT_STR_TUPLE("hfs"), RTFSISOMAKERCMDNAME_HFS },
685 { RT_STR_TUPLE("hfs-trans-tbl"), RTFSISOMAKERCMDNAME_HFS_TRANS_TBL },
686 };
687
688 Assert(cbDst > 0);
689 char *pszRet = pszDst;
690 for (uint32_t i = 0; i < RT_ELEMENTS(s_aSpecs); i++)
691 if (s_aSpecs[i].fSpec & fNameSpecifiers)
692 {
693 if (pszDst != pszRet && cbDst > 1)
694 {
695 *pszDst++ = '+';
696 cbDst--;
697 }
698 if (cbDst > s_aSpecs[i].cchName)
699 {
700 memcpy(pszDst, s_aSpecs[i].pszName, s_aSpecs[i].cchName);
701 cbDst -= s_aSpecs[i].cchName;
702 pszDst += s_aSpecs[i].cchName;
703 }
704 else if (cbDst > 1)
705 {
706 memcpy(pszDst, s_aSpecs[i].pszName, cbDst - 1);
707 pszDst += cbDst - 1;
708 cbDst = 1;
709 }
710
711 fNameSpecifiers &= ~s_aSpecs[i].fSpec;
712 if (!fNameSpecifiers)
713 break;
714 }
715 *pszDst = '\0';
716 return pszRet;
717}
718
719
720/**
721 * Parses the --name-setup option.
722 *
723 * @returns IPRT status code.
724 * @param pOpts The ISO maker command instance.
725 * @param pszSpec The name setup specification.
726 */
727static int rtFsIsoMakerCmdOptNameSetup(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
728{
729 /*
730 * Comma separated list of one or more specifiers.
731 */
732 uint32_t fNamespaces = 0;
733 uint32_t fPrevMajor = 0;
734 uint32_t iNameSpecifier = 0;
735 uint32_t offSpec = 0;
736 do
737 {
738 /*
739 * Parse up to the next colon or end of string.
740 */
741 uint32_t fNameSpecifier = 0;
742 char ch;
743 while ( (ch = pszSpec[offSpec]) != '\0'
744 && ch != ',')
745 {
746 if (RT_C_IS_SPACE(ch) || ch == '+' || ch == '|') /* space, '+' and '|' are allowed as name separators. */
747 offSpec++;
748 else
749 {
750 /* Find the end of the name. */
751 uint32_t offEndSpec = offSpec + 1;
752 while ( (ch = pszSpec[offEndSpec]) != '\0'
753 && ch != ','
754 && ch != '+'
755 && ch != '|'
756 && !RT_C_IS_SPACE(ch))
757 offEndSpec++;
758
759#define IS_EQUAL(a_sz) (cchName == sizeof(a_sz) - 1U && strncmp(pchName, a_sz, sizeof(a_sz) - 1U) == 0)
760 const char * const pchName = &pszSpec[offSpec];
761 uint32_t const cchName = offEndSpec - offSpec;
762 /* major namespaces */
763 if ( IS_EQUAL("iso")
764 || IS_EQUAL("primary")
765 || IS_EQUAL("iso9660")
766 || IS_EQUAL("iso-9660")
767 || IS_EQUAL("primary-iso")
768 || IS_EQUAL("iso-primary") )
769 {
770 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO;
771 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_ISO_9660;
772 }
773 else if (IS_EQUAL("joliet"))
774 {
775 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET;
776 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_JOLIET;
777 }
778 else if (IS_EQUAL("udf"))
779 {
780#if 0
781 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF;
782 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_UDF;
783#else
784 return rtFsIsoMakerCmdSyntaxError(pOpts, "UDF support is currently not implemented");
785#endif
786 }
787 else if (IS_EQUAL("hfs") || IS_EQUAL("hfsplus"))
788 {
789#if 0
790 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS;
791 fNamespaces |= fPrevMajor = RTFSISOMAKER_NAMESPACE_HFS;
792#else
793 return rtFsIsoMakerCmdSyntaxError(pOpts, "Hybrid HFS+ support is currently not implemented");
794#endif
795 }
796 /* rock ridge */
797 else if ( IS_EQUAL("rr")
798 || IS_EQUAL("rock")
799 || IS_EQUAL("rock-ridge"))
800 {
801 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
802 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
803 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
804 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
805 else
806 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified rock-ridge name specifier");
807 }
808 else if ( IS_EQUAL("iso-rr") || IS_EQUAL("iso-rock") || IS_EQUAL("iso-rock-ridge")
809 || IS_EQUAL("primary-rr") || IS_EQUAL("primary-rock") || IS_EQUAL("primary-rock-ridge")
810 || IS_EQUAL("iso9660-rr") || IS_EQUAL("iso9660-rock") || IS_EQUAL("iso9660-rock-ridge")
811 || IS_EQUAL("iso-9660-rr") || IS_EQUAL("iso-9660-rock") || IS_EQUAL("iso-9660-rock-ridge")
812 || IS_EQUAL("primaryiso-rr") || IS_EQUAL("primaryiso-rock") || IS_EQUAL("primaryiso-rock-ridge")
813 || IS_EQUAL("primary-iso-rr") || IS_EQUAL("primary-iso-rock") || IS_EQUAL("primary-iso-rock-ridge") )
814 {
815 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_ROCK_RIDGE;
816 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
817 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-rock-ridge must come after the iso-9660 name specifier");
818 }
819 else if (IS_EQUAL("joliet-rr") || IS_EQUAL("joliet-rock") || IS_EQUAL("joliet-rock-ridge"))
820 {
821 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_ROCK_RIDGE;
822 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
823 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-rock-ridge must come after the joliet name specifier");
824 }
825 /* trans.tbl */
826 else if (IS_EQUAL("trans") || IS_EQUAL("trans-tbl"))
827 {
828 if (fPrevMajor == RTFSISOMAKERCMDNAME_PRIMARY_ISO)
829 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
830 else if (fPrevMajor == RTFSISOMAKERCMDNAME_JOLIET)
831 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
832 else
833 return rtFsIsoMakerCmdSyntaxError(pOpts, "unqualified trans-tbl name specifier");
834 }
835 else if ( IS_EQUAL("iso-trans") || IS_EQUAL("iso-trans-tbl")
836 || IS_EQUAL("primary-trans") || IS_EQUAL("primary-trans-tbl")
837 || IS_EQUAL("iso9660-trans") || IS_EQUAL("iso9660-trans-tbl")
838 || IS_EQUAL("iso-9660-trans") || IS_EQUAL("iso-9660-trans-tbl")
839 || IS_EQUAL("primaryiso-trans") || IS_EQUAL("primaryiso-trans-tbl")
840 || IS_EQUAL("primary-iso-trans") || IS_EQUAL("primary-iso-trans-tbl") )
841 {
842 fNameSpecifier |= RTFSISOMAKERCMDNAME_PRIMARY_ISO_TRANS_TBL;
843 if (!(fNamespaces & RTFSISOMAKERCMDNAME_PRIMARY_ISO))
844 return rtFsIsoMakerCmdSyntaxError(pOpts, "iso-9660-trans-tbl must come after the iso-9660 name specifier");
845 }
846 else if (IS_EQUAL("joliet-trans") || IS_EQUAL("joliet-trans-tbl"))
847 {
848 fNameSpecifier |= RTFSISOMAKERCMDNAME_JOLIET_TRANS_TBL;
849 if (!(fNamespaces & RTFSISOMAKERCMDNAME_JOLIET))
850 return rtFsIsoMakerCmdSyntaxError(pOpts, "joliet-trans-tbl must come after the joliet name specifier");
851 }
852 else if (IS_EQUAL("udf-trans") || IS_EQUAL("udf-trans-tbl"))
853 {
854 fNameSpecifier |= RTFSISOMAKERCMDNAME_UDF_TRANS_TBL;
855 if (!(fNamespaces & RTFSISOMAKERCMDNAME_UDF))
856 return rtFsIsoMakerCmdSyntaxError(pOpts, "udf-trans-tbl must come after the udf name specifier");
857 }
858 else if (IS_EQUAL("hfs-trans") || IS_EQUAL("hfs-trans-tbl"))
859 {
860 fNameSpecifier |= RTFSISOMAKERCMDNAME_HFS_TRANS_TBL;
861 if (!(fNamespaces & RTFSISOMAKERCMDNAME_HFS))
862 return rtFsIsoMakerCmdSyntaxError(pOpts, "hfs-trans-tbl must come after the hfs name specifier");
863 }
864 else
865 return rtFsIsoMakerCmdSyntaxError(pOpts, "unknown name specifier '%.*s'", cchName, pchName);
866#undef IS_EQUAL
867 offSpec = offEndSpec;
868 }
869 } /* while same specifier */
870
871 /*
872 * Check that it wasn't empty.
873 */
874 if (fNameSpecifier == 0)
875 return rtFsIsoMakerCmdSyntaxError(pOpts, "name specifier #%u (0-based) is empty ", iNameSpecifier);
876
877 /*
878 * Complain if a major namespace name is duplicated. The rock-ridge and
879 * trans.tbl names are simple to replace, the others affect the two former
880 * names and are therefore not allowed twice in the list.
881 */
882 uint32_t i = iNameSpecifier;
883 while (i-- > 0)
884 {
885 uint32_t fRepeated = (fNameSpecifier & RTFSISOMAKERCMDNAME_MAJOR_MASK)
886 & (pOpts->afNameSpecifiers[i] & RTFSISOMAKERCMDNAME_MAJOR_MASK);
887 if (fRepeated)
888 {
889 char szTmp[128];
890 return rtFsIsoMakerCmdSyntaxError(pOpts, "repeating name specifier%s: %s", RT_IS_POWER_OF_TWO(fRepeated) ? "" : "s",
891 rtFsIsoMakerCmdNameSpecifiersToString(fRepeated, szTmp, sizeof(szTmp)));
892 }
893 }
894
895 /*
896 * Add it.
897 */
898 if (iNameSpecifier >= RT_ELEMENTS(pOpts->afNameSpecifiers))
899 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many name specifiers (max %d)", RT_ELEMENTS(pOpts->afNameSpecifiers));
900 pOpts->afNameSpecifiers[iNameSpecifier] = fNameSpecifier;
901 iNameSpecifier++;
902
903 /*
904 * Next, if any.
905 */
906 if (pszSpec[offSpec] == ',')
907 offSpec++;
908 } while (pszSpec[offSpec] != '\0');
909
910 pOpts->cNameSpecifiers = iNameSpecifier;
911 pOpts->fDstNamespaces = fNamespaces;
912
913 return VINF_SUCCESS;
914}
915
916
917/**
918 * Adds a file.
919 *
920 * @returns IPRT status code.
921 * @param pOpts The ISO maker command instance.
922 * @param pszSrc The path to the source file.
923 * @param paParsedNames Array of parsed names, there are
924 * pOpts->cNameSpecifiers entries in the array.
925 */
926static int rtFsIsoMakerCmdAddFile(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSrc, PCRTFSISOMAKERCMDPARSEDNAME paParsedNames)
927{
928 uint32_t idxObj;
929 int rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(pOpts->hIsoMaker, pszSrc, &idxObj);
930 if (RT_SUCCESS(rc))
931 {
932 pOpts->cItemsAdded++;
933
934 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
935 if (paParsedNames[i].cchPath > 0)
936 {
937 if (paParsedNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK)
938 {
939 rc = RTFsIsoMakerObjSetPath(pOpts->hIsoMaker, idxObj,
940 paParsedNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
941 paParsedNames[i].szPath);
942 if (RT_FAILURE(rc))
943 {
944 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error setting name '%s' on '%s': %Rrc",
945 paParsedNames[i].szPath, pszSrc, rc);
946 break;
947 }
948 }
949 if (paParsedNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MINOR_MASK)
950 {
951 /** @todo add APIs for this. */
952 }
953 }
954 }
955 else
956 rc = rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error adding '%s': %Rrc", pszSrc, rc);
957 return rc;
958}
959
960
961/**
962 * Processes a non-option argument.
963 *
964 * @returns IPRT status code.
965 * @param pOpts The ISO maker command instance.
966 * @param pszSpec The specification of what to add.
967 */
968static int rtFsIsoMakerCmdAddSomething(PRTFSISOMAKERCMDOPTS pOpts, const char *pszSpec)
969{
970 const char * const pszSpecIn = pszSpec;
971 enum { kSpecialSrc_Normal, kSpecialSrc_Remove, kSpecialSrc_MustRemove } enmSpecialSrc = kSpecialSrc_Normal;
972
973 /*
974 * Split it up by '='. Because of the source, which comes last,
975 */
976 RTFSISOMAKERCMDPARSEDNAME aParsedNames[RTFSISOMAKERCMD_MAX_NAMES + 1];
977 uint32_t cParsedNames = 0;
978 for (;;)
979 {
980 const char *pszEqual = strchr(pszSpec, '=');
981 size_t cchName = pszEqual ? pszEqual - pszSpec : strlen(pszSpec);
982 bool fNeedSlash = pszEqual && !RTPATH_IS_SLASH(*pszSpec) && cchName > 0;
983 if (cchName + fNeedSlash >= sizeof(aParsedNames[cParsedNames].szPath))
984 return rtFsIsoMakerCmdSyntaxError(pOpts, "name #%u (0-based) is too long: %s", cParsedNames, pszSpecIn);
985 if (cParsedNames >= pOpts->cNameSpecifiers + 1)
986 return rtFsIsoMakerCmdSyntaxError(pOpts, "too many names specified (max %u + source): %s",
987 pOpts->cNameSpecifiers, pszSpecIn);
988 if (!fNeedSlash)
989 memcpy(aParsedNames[cParsedNames].szPath, pszSpec, cchName);
990 else
991 {
992 memcpy(&aParsedNames[cParsedNames].szPath[1], pszSpec, cchName);
993 aParsedNames[cParsedNames].szPath[0] = RTPATH_SLASH;
994 cchName++;
995 }
996 aParsedNames[cParsedNames].szPath[cchName] = '\0';
997 aParsedNames[cParsedNames].cchPath = (uint32_t)cchName;
998 cParsedNames++;
999
1000 if (!pszEqual)
1001 {
1002 if (!cchName)
1003 return rtFsIsoMakerCmdSyntaxError(pOpts, "empty source file name: %s", pszSpecIn);
1004 if (cchName == 8 && strcmp(pszSpec, ":remove:") == 0)
1005 enmSpecialSrc = kSpecialSrc_Remove;
1006 else if (cchName == 13 && strcmp(pszSpec, ":must-remove:") == 0)
1007 enmSpecialSrc = kSpecialSrc_MustRemove;
1008 break;
1009 }
1010 pszSpec = pszEqual + 1;
1011 }
1012
1013 /*
1014 * If there are too few names specified, move the source and repeat the penultimate name.
1015 */
1016 if (cParsedNames < pOpts->cNameSpecifiers + 1)
1017 {
1018 aParsedNames[pOpts->cNameSpecifiers] = aParsedNames[cParsedNames - 1];
1019 uint32_t iSrc = cParsedNames >= 2 ? cParsedNames - 2 : 0;
1020
1021 /* If the source is a input file name specifier, reduce it to something that starts with a slash. */
1022 if (cParsedNames == 1)
1023 {
1024 if (RTVfsChainIsSpec(aParsedNames[iSrc].szPath))
1025 {
1026 char *pszFinalPath;
1027 int rc = RTVfsChainQueryFinalPath(aParsedNames[iSrc].szPath, &pszFinalPath, NULL);
1028 if (RT_FAILURE(rc))
1029 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "RTVfsChainQueryFinalPath failed with %Rrc on: %s", rc, pszSpecIn);
1030 aParsedNames[iSrc].cchPath = (uint32_t)strlen(pszFinalPath);
1031 if (RTPATH_IS_SLASH(*pszFinalPath))
1032 memcpy(aParsedNames[iSrc].szPath, pszFinalPath, aParsedNames[iSrc].cchPath + 1);
1033 else
1034 {
1035 memcpy(&aParsedNames[iSrc].szPath[1], pszFinalPath, aParsedNames[iSrc].cchPath + 1);
1036 aParsedNames[iSrc].szPath[0] = RTPATH_SLASH;
1037 aParsedNames[iSrc].cchPath++;
1038 }
1039 RTStrFree(pszFinalPath);
1040 }
1041#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
1042 else if ( RTPATH_IS_VOLSEP(aParsedNames[iSrc].szPath[1])
1043 && RT_C_IS_ALPHA(aParsedNames[iSrc].szPath[0]))
1044 {
1045 if (RTPATH_IS_SLASH(aParsedNames[iSrc].szPath[2]))
1046 {
1047 memmove(&aParsedNames[iSrc].szPath[0], &aParsedNames[iSrc].szPath[2], aParsedNames[iSrc].cchPath - 1);
1048 aParsedNames[iSrc].cchPath -= 2;
1049 }
1050 else
1051 {
1052 memmove(&aParsedNames[iSrc].szPath[1], &aParsedNames[iSrc].szPath[2], aParsedNames[iSrc].cchPath - 1);
1053 aParsedNames[iSrc].szPath[0] = RTPATH_SLASH;
1054 aParsedNames[iSrc].cchPath -= 1;
1055 }
1056 }
1057#endif
1058 else if (!RTPATH_IS_SLASH(aParsedNames[iSrc].szPath[0]))
1059 {
1060 if (aParsedNames[iSrc].cchPath + 2 > sizeof(aParsedNames[iSrc].szPath))
1061 return rtFsIsoMakerCmdSyntaxError(pOpts, "name too long: %s", pszSpecIn);
1062 memmove(&aParsedNames[iSrc].szPath[1], &aParsedNames[iSrc].szPath[0], aParsedNames[iSrc].cchPath + 1);
1063 aParsedNames[iSrc].szPath[0] = RTPATH_SLASH;
1064 aParsedNames[iSrc].cchPath++;
1065 }
1066 }
1067
1068 for (uint32_t iDst = iSrc + 1; iDst < pOpts->cNameSpecifiers; iDst++)
1069 aParsedNames[iDst] = aParsedNames[iSrc];
1070 cParsedNames = pOpts->cNameSpecifiers + 1;
1071 }
1072
1073 /*
1074 * Copy the specifier flags and check that the paths all starts with slashes.
1075 */
1076 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
1077 {
1078 aParsedNames[i].fNameSpecifiers = pOpts->afNameSpecifiers[i];
1079 Assert( aParsedNames[i].cchPath == 0
1080 || RTPATH_IS_SLASH(aParsedNames[i].szPath[0]));
1081 }
1082
1083 /*
1084 * Deal with special source filenames used to remove/change stuff.
1085 */
1086 if ( enmSpecialSrc == kSpecialSrc_Remove
1087 || enmSpecialSrc == kSpecialSrc_MustRemove)
1088 {
1089 const char *pszFirstNm = NULL;
1090 uint32_t cRemoved = 0;
1091 for (uint32_t i = 0; i < pOpts->cNameSpecifiers; i++)
1092 if ( aParsedNames[i].cchPath > 0
1093 && (aParsedNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK))
1094 {
1095 pszFirstNm = aParsedNames[i].szPath;
1096 uint32_t idxObj = RTFsIsoMakerGetObjIdxForPath(pOpts->hIsoMaker,
1097 aParsedNames[i].fNameSpecifiers & RTFSISOMAKERCMDNAME_MAJOR_MASK,
1098 aParsedNames[i].szPath);
1099 if (idxObj != UINT32_MAX)
1100 {
1101 int rc = RTFsIsoMakerObjRemove(pOpts->hIsoMaker, idxObj);
1102 if (RT_FAILURE(rc))
1103 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Failed to remove '%s': %Rrc", pszSpecIn, rc);
1104 cRemoved++;
1105 }
1106 }
1107 if ( enmSpecialSrc == kSpecialSrc_MustRemove
1108 && cRemoved == 0)
1109 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_FOUND, "Failed to locate '%s' for removal", pszSpecIn);
1110 }
1111 /*
1112 * Add regular source.
1113 */
1114 else
1115 {
1116 const char *pszSrc = aParsedNames[cParsedNames - 1].szPath;
1117 RTFSOBJINFO ObjInfo;
1118 uint32_t offError;
1119 RTERRINFOSTATIC ErrInfo;
1120 int rc = RTVfsChainQueryInfo(pszSrc, &ObjInfo, RTFSOBJATTRADD_UNIX,
1121 RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo));
1122 if (RT_FAILURE(rc))
1123 return rtFsIsoMakerCmdErrorRc(pOpts, rc, "Error query info on '%s': %Rrc", pszSrc, rc);
1124 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1125 return rtFsIsoMakerCmdAddFile(pOpts, pszSrc, aParsedNames);
1126 if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1127 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding directory '%s' failed: not implemented", pszSpecIn);
1128 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
1129 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding symlink '%s' failed: not implemented", pszSpecIn);
1130 return rtFsIsoMakerCmdErrorRc(pOpts, VERR_NOT_IMPLEMENTED, "Adding special file '%s' failed: not implemented", pszSpecIn);
1131 }
1132
1133 return VINF_SUCCESS;
1134}
1135
1136
1137/**
1138 * Extended ISO maker command.
1139 *
1140 * This can be used as a ISO maker command that produces a image file, or
1141 * alternatively for setting up a virtual ISO in memory.
1142 *
1143 * @returns IPRT status code
1144 * @param cArgs Number of arguments.
1145 * @param papszArgs Pointer to argument array.
1146 * @param phVfsFile Where to return the virtual ISO. Pass NULL to
1147 * for normal operation (creates file on disk).
1148 * @param pErrInfo Where to return extended error information in
1149 * the virtual ISO mode.
1150 */
1151RTDECL(int) RTFsIsoMakerCmdEx(unsigned cArgs, char **papszArgs, PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo)
1152{
1153 /*
1154 * Create instance.
1155 */
1156 RTFSISOMAKERCMDOPTS Opts;
1157 RT_ZERO(Opts);
1158 Opts.hIsoMaker = NIL_RTFSISOMAKER;
1159 Opts.pErrInfo = pErrInfo;
1160 Opts.fVirtualImageMaker = phVfsFile != NULL;
1161 Opts.cNameSpecifiers = 1;
1162 Opts.afNameSpecifiers[0] = RTFSISOMAKERCMDNAME_MAJOR_MASK;
1163 Opts.fDstNamespaces = RTFSISOMAKERCMDNAME_MAJOR_MASK;
1164 if (phVfsFile)
1165 *phVfsFile = NIL_RTVFSFILE;
1166
1167 /* Setup option parsing. */
1168 RTGETOPTSTATE GetState;
1169 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, g_aRtFsIsoMakerOptions, RT_ELEMENTS(g_aRtFsIsoMakerOptions),
1170 1 /*iFirst*/, 0 /*fFlags*/);
1171 if (RT_FAILURE(rc))
1172 return rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTGetOpt failed: %Rrc", rc);
1173
1174 /* Create the ISO creator instance. */
1175 rc = RTFsIsoMakerCreate(&Opts.hIsoMaker);
1176 if (RT_FAILURE(rc))
1177 return rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreate failed: %Rrc", rc);
1178
1179 /*
1180 * Parse parameters. Parameters are position dependent.
1181 */
1182 RTGETOPTUNION ValueUnion;
1183 while ( RT_SUCCESS(rc)
1184 && (rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
1185 {
1186 switch (rc)
1187 {
1188 /*
1189 * Files and directories.
1190 */
1191 case VINF_GETOPT_NOT_OPTION:
1192 rc = rtFsIsoMakerCmdAddSomething(&Opts, ValueUnion.psz);
1193 break;
1194
1195 /*
1196 * Options specific to our ISO maker.
1197 */
1198 case RTFSISOMAKERCMD_OPT_NAME_SETUP:
1199 rc = rtFsIsoMakerCmdOptNameSetup(&Opts, ValueUnion.psz);
1200 break;
1201 case RTFSISOMAKERCMD_OPT_NO_JOLIET:
1202 rc = RTFsIsoMakerSetJolietUcs2Level(Opts.hIsoMaker, 0);
1203 if (RT_FAILURE(rc))
1204 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "Failed to disable joliet: %Rrc", rc);
1205 break;
1206
1207
1208 /*
1209 * Options compatible with other ISO makers.
1210 */
1211 case 'o':
1212 if (Opts.fVirtualImageMaker)
1213 return rtFsIsoMakerCmdSyntaxError(&Opts, "The --output option is not allowed");
1214 if (Opts.pszOutFile)
1215 return rtFsIsoMakerCmdSyntaxError(&Opts, "The --output option is specified more than once");
1216 Opts.pszOutFile = ValueUnion.psz;
1217 break;
1218
1219 /*
1220 * Standard bits.
1221 */
1222 case 'h':
1223 rtFsIsoMakerCmdUsage(&Opts, papszArgs[0]);
1224 return rtFsIsoMakerCmdDeleteState(&Opts, Opts.fVirtualImageMaker ? VERR_NOT_FOUND : VINF_SUCCESS);
1225
1226 case 'V':
1227 rtFsIsoMakerPrintf(&Opts, "%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1228 return rtFsIsoMakerCmdDeleteState(&Opts, Opts.fVirtualImageMaker ? VERR_NOT_FOUND : VINF_SUCCESS);
1229
1230 default:
1231 if (rc > 0 && RT_C_IS_GRAPH(rc))
1232 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: -%c", rc);
1233 else if (rc > 0)
1234 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_GETOPT_UNKNOWN_OPTION, "Unhandled option: %i (%#x)", rc, rc);
1235 else if (rc == VERR_GETOPT_UNKNOWN_OPTION)
1236 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "Unknown option: '%s'", ValueUnion.psz);
1237 else if (ValueUnion.pDef)
1238 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "%s: %Rrs\n", ValueUnion.pDef->pszLong, rc);
1239 else
1240 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "%Rrs\n", rc);
1241 return rtFsIsoMakerCmdDeleteState(&Opts, rc);
1242 }
1243 }
1244
1245 /*
1246 * Check for mandatory options.
1247 */
1248 if (RT_SUCCESS(rc))
1249 {
1250 if (!Opts.cItemsAdded)
1251 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_NO_DATA, "Cowardly refuses to create empty ISO image");
1252 else if (!Opts.pszOutFile && !Opts.fVirtualImageMaker)
1253 rc = rtFsIsoMakerCmdErrorRc(&Opts, VERR_INVALID_PARAMETER, "No output file specified (--output <file>)");
1254 }
1255 if (RT_SUCCESS(rc))
1256 {
1257 /*
1258 * Finalize the image and get the virtual file.
1259 */
1260 rc = RTFsIsoMakerFinalize(Opts.hIsoMaker);
1261 if (RT_SUCCESS(rc))
1262 {
1263 RTVFSFILE hVfsFile;
1264 rc = RTFsIsoMakerCreateVfsOutputFile(Opts.hIsoMaker, &hVfsFile);
1265 if (RT_SUCCESS(rc))
1266 {
1267 /*
1268 * We're done now if we're only setting up a virtual image.
1269 */
1270 if (Opts.fVirtualImageMaker)
1271 *phVfsFile = hVfsFile;
1272 else
1273 {
1274 rc = rtFsIsoMakerCmdWriteImage(&Opts, hVfsFile);
1275 RTVfsFileRelease(hVfsFile);
1276 }
1277 }
1278 else
1279 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerCreateVfsOutputFile failed: %Rrc", rc);
1280 }
1281 else
1282 rc = rtFsIsoMakerCmdErrorRc(&Opts, rc, "RTFsIsoMakerFinalize failed: %Rrc", rc);
1283 }
1284
1285 return rtFsIsoMakerCmdDeleteState(&Opts, rc);
1286}
1287
1288
1289/**
1290 * ISO maker command (creates image file on disk).
1291 *
1292 * @returns IPRT status code
1293 * @param cArgs Number of arguments.
1294 * @param papszArgs Pointer to argument array.
1295 */
1296RTDECL(RTEXITCODE) RTFsIsoMakerCmd(unsigned cArgs, char **papszArgs)
1297{
1298 int rc = RTFsIsoMakerCmdEx(cArgs, papszArgs, NULL, NULL);
1299 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1300}
1301
Note: See TracBrowser for help on using the repository browser.

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