VirtualBox

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

Last change on this file since 67381 was 67381, checked in by vboxsync, 7 years ago

IPRT: More ISO maker code.

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