VirtualBox

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

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

IPRT: ISO maker updates.

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