VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/isomaker.cpp@ 67346

Last change on this file since 67346 was 67346, 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: 166.3 KB
Line 
1/* $Id: isomaker.cpp 67346 2017-06-12 17:25:33Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker.
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/asm.h>
36#include <iprt/assert.h>
37#include <iprt/buildconfig.h>
38#include <iprt/err.h>
39#include <iprt/ctype.h>
40#include <iprt/file.h>
41#include <iprt/list.h>
42#include <iprt/log.h>
43#include <iprt/mem.h>
44#include <iprt/path.h>
45#include <iprt/string.h>
46#include <iprt/vfs.h>
47#include <iprt/vfslowlevel.h>
48#include <iprt/formats/iso9660.h>
49
50#include <internal/magics.h>
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56/** Asserts valid handle, returns @a a_rcRet if not. */
57#define RTFSISOMAKER_ASSER_VALID_HANDLE_RET_EX(a_pThis, a_rcRet) \
58 do { AssertPtrReturn(a_pThis, a_rcRet); \
59 AssertPtrReturn((a_pThis)->uMagic == RTFSISOMAKERINT_MAGIC, a_rcRet); \
60 } while (0)
61
62/** Asserts valid handle, returns VERR_INVALID_HANDLE if not. */
63#define RTFSISOMAKER_ASSER_VALID_HANDLE_RET(a_pThis) RTFSISOMAKER_ASSER_VALID_HANDLE_RET_EX(a_pThis, VERR_INVALID_HANDLE)
64
65/** The sector size. */
66#define RTFSISOMAKER_SECTOR_SIZE _2K
67/** The sector offset mask. */
68#define RTFSISOMAKER_SECTOR_OFFSET_MASK (_2K - 1)
69/** Maximum number of objects. */
70#define RTFSISOMAKER_MAX_OBJECTS _16M
71/** Maximum number of objects per directory. */
72#define RTFSISOMAKER_MAX_OBJECTS_PER_DIR _256K /**< @todo check limit */
73
74/** UTF-8 name buffer. */
75#define RTFSISOMAKER_MAX_NAME_BUF 768
76
77/** TRANS.TBL left padding length.
78 * We keep the amount of padding low to avoid wasing memory when generating
79 * these long obsolete files. */
80#define RTFSISOMAKER_TRANS_TBL_LEFT_PAD 12
81
82/** Tests if @a a_ch is in the set of d-characters. */
83#define RTFSISOMAKER_IS_IN_D_CHARS(a_ch) (RT_C_IS_UPPER(a_ch) || RT_C_IS_DIGIT(a_ch) || (a_ch) == '_')
84
85/** Tests if @a a_ch is in the set of d-characters when uppercased. */
86#define RTFSISOMAKER_IS_UPPER_IN_D_CHARS(a_ch) (RT_C_IS_ALNUM(a_ch) || (a_ch) == '_')
87
88
89/** Calculates the path table record size given the name length. */
90#define RTFSISOMAKER_CALC_PATHREC_SIZE(a_cbNameInDirRec) \
91 ( RT_UOFFSETOF(ISO9660PATHREC, achDirId[(a_cbNameInDirRec)]) + ((a_cbNameInDirRec) & 1) )
92
93
94/*********************************************************************************************************************************
95* Structures and Typedefs *
96*********************************************************************************************************************************/
97/** Pointer to an ISO maker object name space node. */
98typedef struct RTFSISOMAKERNAME *PRTFSISOMAKERNAME;
99/** Pointer to a const ISO maker object name space node. */
100typedef struct RTFSISOMAKERNAME const *PCRTFSISOMAKERNAME;
101/** Pointer to an ISO maker object name space node pointer. */
102typedef PRTFSISOMAKERNAME *PPRTFSISOMAKERNAME;
103
104/** Pointer to a common ISO image maker file system object. */
105typedef struct RTFSISOMAKEROBJ *PRTFSISOMAKEROBJ;
106/** Pointer to a const common ISO image maker file system object. */
107typedef struct RTFSISOMAKEROBJ const *PCRTFSISOMAKEROBJ;
108
109/** Pointer to a ISO maker file object. */
110typedef struct RTFSISOMAKERFILE *PRTFSISOMAKERFILE;
111/** Pointer to a const ISO maker file object. */
112typedef struct RTFSISOMAKERFILE const *PCRTFSISOMAKERFILE;
113
114
115/** @name RTFSISOMAKER_NAMESPACE_XXX - Namespace selector.
116 * @{
117 */
118#define RTFSISOMAKER_NAMESPACE_ISO_9660 RT_BIT_32(0) /**< The primary ISO-9660 namespace. */
119#define RTFSISOMAKER_NAMESPACE_JOLIET RT_BIT_32(1) /**< The joliet namespace. */
120#define RTFSISOMAKER_NAMESPACE_UDF RT_BIT_32(2) /**< The UDF namespace. */
121#define RTFSISOMAKER_NAMESPACE_HFS RT_BIT_32(3) /**< The HFS namespace */
122#define RTFSISOMAKER_NAMESPACE_ALL UINT32_C(0x0000000f) /**< All namespaces. */
123#define RTFSISOMAKER_NAMESPACE_VALID_MASK UINT32_C(0x0000000f) /**< Valid namespace bits. */
124/** @} */
125
126
127/**
128 * Filesystem object type.
129 */
130typedef enum RTFSISOMAKEROBJTYPE
131{
132 RTFSISOMAKEROBJTYPE_INVALID = 0,
133 RTFSISOMAKEROBJTYPE_DIR,
134 RTFSISOMAKEROBJTYPE_FILE,
135 //RTFSISOMAKEROBJTYPE_SYMLINK,
136 RTFSISOMAKEROBJTYPE_END
137} RTFSISOMAKEROBJTYPE;
138
139/**
140 * Extra name space information required for directories.
141 */
142typedef struct RTFSISOMAKERNAMEDIR
143{
144 /** The location of the directory data. */
145 uint64_t offDir;
146 /** The size of the directory. */
147 uint32_t cbDir;
148 /** Number of children. */
149 uint32_t cChildren;
150 /** Sorted array of children. */
151 PPRTFSISOMAKERNAME papChildren;
152 /** The translate table file. */
153 PRTFSISOMAKERFILE pTransTblFile;
154
155 /** The offset in the path table (ISO-9660).
156 * This is set when finalizing the image. */
157 uint32_t offPathTable;
158 /** The path table identifier of this directory (ISO-9660).
159 * This is set when finalizing the image. */
160 uint16_t idPathTable;
161 /** The size of the first directory record (0x00 - '.'). */
162 uint8_t cbDirRec00;
163 /** The size of the second directory record (0x01 - '..'). */
164 uint8_t cbDirRec01;
165 /** Pointer to back to the namespace node this belongs to (for the finalized
166 * entry list). */
167 PRTFSISOMAKERNAME pName;
168 /** Entry in the list of finalized directories. */
169 RTLISTNODE FinalizedEntry;
170} RTFSISOMAKERNAMEDIR;
171/** Pointer to directory specfic namespace node info. */
172typedef RTFSISOMAKERNAMEDIR *PRTFSISOMAKERNAMEDIR;
173/** Pointer to const directory specfic namespace node info. */
174typedef const RTFSISOMAKERNAMEDIR *PCRTFSISOMAKERNAMEDIR;
175
176
177/**
178 * ISO maker object namespace node.
179 */
180typedef struct RTFSISOMAKERNAME
181{
182 /** Pointer to the file system object. */
183 PRTFSISOMAKEROBJ pObj;
184 /** Pointer to the partent directory, NULL if root dir. */
185 PRTFSISOMAKERNAME pParent;
186
187 /** Pointer to the directory information if this is a directory, NULL if not a
188 * directory. This is allocated together with this structure, so it doesn't need
189 * freeing. */
190 PRTFSISOMAKERNAMEDIR pDir;
191
192 /** The name specified when creating this namespace node. Helps navigating
193 * the namespace when we mangle or otherwise change the names.
194 * Allocated together with of this structure, no spearate free necessary. */
195 const char *pszSpecNm;
196
197 /** Alternative rock ridge name. */
198 char *pszRockRidgeNm;
199 /** Alternative TRANS.TBL name. */
200 char *pszTransNm;
201 /** Length of pszSpecNm. */
202 uint16_t cchSpecNm;
203 /** Length of pszRockRidgeNm. */
204 uint16_t cchRockRidgeNm;
205 /** Length of pszTransNm. */
206 uint16_t cchTransNm;
207
208 /** The depth in the namespace tree of this name. */
209 uint8_t uDepth;
210 /** Set if pszTransNm is allocated separately. Normally same as pszSpecNm. */
211 bool fRockRidgeNmAlloced : 1;
212 /** Set if pszTransNm is allocated separately. Normally same as pszSpecNm. */
213 bool fTransNmAlloced : 1;
214
215 /** The mode mask.
216 * Starts out as a copy of RTFSISOMAKEROBJ::fMode. */
217 RTFMODE fMode;
218 /** The owner ID.
219 * Starts out as a copy of RTFSISOMAKEROBJ::uid. */
220 RTUID uid;
221 /** The group ID.
222 * Starts out as a copy of RTFSISOMAKEROBJ::gid. */
223 RTGID gid;
224 /** The device number if a character or block device.
225 * This is for Rock Ridge. */
226 RTDEV Device;
227 /** The inode/serial number.
228 * This is for Rock Ridge. */
229 uint64_t INode;
230 /** The number of hardlinks to report in the file stats.
231 * This is for Rock Ridge. */
232 uint32_t cHardlinks;
233
234 /** The offset of the directory entry in the parent directory. */
235 uint32_t offDirRec;
236 /** Size of the directory record (ISO-9660).
237 * This is set when the image is being finalized. */
238 uint16_t cbDirRec;
239
240 /** The number of bytes the name requires in the directory record. */
241 uint16_t cbNameInDirRec;
242 /** The name length. */
243 uint16_t cchName;
244 /** The name. */
245 char szName[RT_FLEXIBLE_ARRAY];
246} RTFSISOMAKERNAME;
247
248/**
249 * A ISO maker namespace.
250 */
251typedef struct RTFSISOMAKERNAMESPACE
252{
253 /** The namespace root. */
254 PRTFSISOMAKERNAME pRoot;
255 /** Total number of name nodes in the namespace. */
256 uint32_t cNames;
257 /** Total number of directories in the namespace. */
258 uint32_t cDirs;
259 /** The namespace selector (RTFSISOMAKER_NAMESPACE_XXX). */
260 uint32_t fNamespace;
261 /** Offset into RTFSISOMAKERNAMESPACE of the name member. */
262 uint32_t offName;
263 /** The configuration level for this name space.
264 * - For UDF and HFS namespaces this is either @c true or @c false.
265 * - For the primary ISO-9660 namespace this is 1, 2, or 3.
266 * - For the joliet namespace this 0 (joliet disabled), 1, 2, or 3. */
267 uint8_t uLevel;
268 /** The rock ridge level: 1 - enabled; 2 - with ER tag.
269 * Linux behaves a little different when seeing the ER tag. */
270 uint8_t uRockRidgeLevel;
271 /** The TRANS.TBL filename if enabled, NULL if disabled.
272 * When not NULL, this may be pointing to heap or g_szTransTbl. */
273 char *pszTransTbl;
274 /** The system ID (ISO9660PRIMARYVOLDESC::achSystemId).
275 * Empty if NULL. */
276 char *pszSystemId;
277 /** The volume ID / label (ISO9660PRIMARYVOLDESC::achVolumeId).
278 * A string representation of RTFSISOMAKERINT::ImageCreationTime if NULL. */
279 char *pszVolumeId;
280 /** The volume set ID (ISO9660PRIMARYVOLDESC::achVolumeSetId). Empty if NULL. */
281 char *pszVolumeSetId;
282 /** The publisher ID or (root) file reference (ISO9660PRIMARYVOLDESC::achPublisherId). Empty if NULL. */
283 char *pszPublisherId;
284 /** The data preperer ID or (root) file reference (ISO9660PRIMARYVOLDESC::achDataPreparerId).
285 * Defaults to g_szPreparerIdPrimaryIso or g_szPreparerIdJoliet. */
286 char *pszDataPreparerId;
287 /** The application ID or (root) file reference (ISO9660PRIMARYVOLDESC::achApplicationId). None if NULL. */
288 char *pszApplicationId;
289 /** The copyright (root) file identifier (ISO9660PRIMARYVOLDESC::achCopyrightFileId). None if NULL. */
290 char *pszCopyrightFileId;
291 /** The abstract (root) file identifier (ISO9660PRIMARYVOLDESC::achAbstractFileId). None if NULL. */
292 char *pszAbstractFileId;
293 /** The bibliographic (root) file identifier (ISO9660PRIMARYVOLDESC::achBibliographicFileId). None if NULL. */
294 char *pszBibliographicFileId;
295} RTFSISOMAKERNAMESPACE;
296/** Pointer to a namespace. */
297typedef RTFSISOMAKERNAMESPACE *PRTFSISOMAKERNAMESPACE;
298/** Pointer to a const namespace. */
299typedef RTFSISOMAKERNAMESPACE const *PCRTFSISOMAKERNAMESPACE;
300
301
302/**
303 * Common base structure for the file system objects.
304 *
305 * The times are shared across all namespaces, while the uid, gid and mode are
306 * duplicates in each namespace.
307 */
308typedef struct RTFSISOMAKEROBJ
309{
310 /** The linear list entry of the image content. */
311 RTLISTNODE Entry;
312 /** The object index. */
313 uint32_t idxObj;
314 /** The type of this object. */
315 RTFSISOMAKEROBJTYPE enmType;
316
317 /** The primary ISO-9660 name space name. */
318 PRTFSISOMAKERNAME pPrimaryName;
319 /** The joliet name space name. */
320 PRTFSISOMAKERNAME pJolietName;
321 /** The UDF name space name. */
322 PRTFSISOMAKERNAME pUdfName;
323 /** The HFS name space name. */
324 PRTFSISOMAKERNAME pHfsName;
325
326 /** Birth (creation) time. */
327 RTTIMESPEC BirthTime;
328 /** Attribute change time. */
329 RTTIMESPEC ChangeTime;
330 /** Modification time. */
331 RTTIMESPEC ModificationTime;
332 /** Accessed time. */
333 RTTIMESPEC AccessedTime;
334
335 /** Owner ID. */
336 RTUID uid;
337 /** Group ID. */
338 RTGID gid;
339 /** Attributes (unix permissions bits mainly). */
340 RTFMODE fMode;
341
342 /** Used to make sure things like the boot catalog stays in the image even if
343 * it's not mapped into any of the namespaces. */
344 bool fNotOrphan;
345} RTFSISOMAKEROBJ;
346
347
348/**
349 * File source type.
350 */
351typedef enum RTFSISOMAKERSRCTYPE
352{
353 RTFSISOMAKERSRCTYPE_INVALID = 0,
354 RTFSISOMAKERSRCTYPE_PATH,
355 RTFSISOMAKERSRCTYPE_VFS_FILE,
356 RTFSISOMAKERSRCTYPE_TRANS_TBL,
357 RTFSISOMAKERSRCTYPE_END
358} RTFSISOMAKERSRCTYPE;
359
360/**
361 * ISO maker file object.
362 */
363typedef struct RTFSISOMAKERFILE
364{
365 /** The common bit. */
366 RTFSISOMAKEROBJ Core;
367 /** The file data size. */
368 uint64_t cbData;
369 /** Byte offset of the data in the image.
370 * UINT64_MAX until the location is finalized. */
371 uint64_t offData;
372
373 /** The type of source object. */
374 RTFSISOMAKERSRCTYPE enmSrcType;
375 /** The source data. */
376 union
377 {
378 /** Path to the source file.
379 * Allocated together with this structure. */
380 const char *pszSrcPath;
381 /** Source VFS file. */
382 RTVFSFILE hVfsFile;
383 /** The directory the translation table belongs to. */
384 PRTFSISOMAKERNAME pTransTblDir;
385 } u;
386
387 /** Entry in the list of finalized directories. */
388 RTLISTNODE FinalizedEntry;
389} RTFSISOMAKERFILE;
390
391
392/**
393 * ISO maker directory object.
394 *
395 * Unlike files, the allocation info is name space specific and lives in the
396 * corresponding RTFSISOMAKERNAMEDIR structures.
397 */
398typedef struct RTFSISOMAKERDIR
399{
400 /** The common bit. */
401 RTFSISOMAKEROBJ Core;
402} RTFSISOMAKERDIR;
403/** Pointer to an ISO maker directory object. */
404typedef RTFSISOMAKERDIR *PRTFSISOMAKERDIR;
405
406
407
408/**
409 * Instance data for a ISO image maker.
410 */
411typedef struct RTFSISOMAKERINT
412{
413 /** Magic value (RTFSISOMAKERINT_MAGIC). */
414 uint32_t uMagic;
415 /** Reference counter. */
416 uint32_t volatile cRefs;
417
418 /** Set after we've been fed the first bit of content.
419 * This means that the namespace configuration has been finalized and can no
420 * longer be changed because it's simply too much work to do adjustments
421 * after having started to add files. */
422 bool fSeenContent;
423 /** Set once we've finalized the image structures.
424 * After this no more changes are allowed. */
425 bool fFinalized;
426
427 /** The primary ISO-9660 namespace. */
428 RTFSISOMAKERNAMESPACE PrimaryIso;
429 /** The joliet namespace. */
430 RTFSISOMAKERNAMESPACE Joliet;
431 /** The UDF namespace. */
432 RTFSISOMAKERNAMESPACE Udf;
433 /** The hybrid HFS+ namespace. */
434 RTFSISOMAKERNAMESPACE Hfs;
435
436 /** The list of objects (RTFSISOMAKEROBJ). */
437 RTLISTANCHOR ObjectHead;
438 /** Number of objects in the image (ObjectHead).
439 * This is used to number them, i.e. create RTFSISOMAKEROBJ::idxObj. */
440 uint32_t cObjects;
441
442 /** Amount of file data. */
443 uint64_t cbData;
444 /** Number of volume descriptors. */
445 uint32_t cVolumeDescriptors;
446
447 /** The 'now' timestamp we use for the whole image.
448 * This way we'll save lots of RTTimeNow calls and have similar timestamps
449 * over the whole image. */
450 RTTIMESPEC ImageCreationTime;
451 /** The default owner ID. */
452 RTUID uidDefault;
453 /** The default group ID. */
454 RTGID gidDefault;
455 /** The default file mode mask. */
456 RTFMODE fDefaultFileMode;
457 /** The default file mode mask. */
458 RTFMODE fDefaultDirMode;
459
460 /** @name Finalized image stuff
461 * @{ */
462 /** The finalized image size. */
463 uint64_t cbFinalizedImage;
464 /** System area content (sectors 0 thur 15). This is NULL if the system area
465 * are all zeros, which is often the case. Hybrid ISOs have an MBR followed by
466 * a GUID partition table here, helping making the image bootable when
467 * transfered to a USB stick. */
468 uint8_t *pbSysArea;
469 /** Number of non-zero system area bytes pointed to by pbSysArea. */
470 size_t cbSysArea;
471
472 /** Pointer to the buffer holding the volume descriptors. */
473 uint8_t *pbVolDescs;
474 /** Pointer to the primary volume descriptor. */
475 PISO9660PRIMARYVOLDESC pPrimaryVolDesc;
476 /** El Torito volume descriptor.
477 * @todo fix type */
478 PISO9660BOOTRECORD pElToritoDesc;
479 /** Pointer to the primary volume descriptor. */
480 PISO9660SUPVOLDESC pJolietVolDesc;
481 /** Terminating ISO-9660 volume descriptor. */
482 PISO9660VOLDESCHDR pTerminatorVolDesc;
483
484 /** Finalized ISO-9660 directory structures. */
485 struct RTFSISOMAKERFINALIZEDDIRS
486 {
487 /** The image byte offset of the first directory. */
488 uint64_t offDirs;
489 /** The image byte offset of the little endian path table.
490 * This always follows offDirs. */
491 uint64_t offPathTableL;
492 /** The image byte offset of the big endian path table.
493 * This always follows offPathTableL. */
494 uint64_t offPathTableM;
495 /** The size of the path table. */
496 uint32_t cbPathTable;
497 /** List of finalized directories for this namespace.
498 * The list is in path table order so it can be generated on the fly. The
499 * directories will be ordered in the same way. */
500 RTLISTANCHOR FinalizedDirs;
501 /** Rock ridge spill file. */
502 PRTFSISOMAKERFILE pRRSpillFile;
503 }
504 /** The finalized directory data for the primary ISO-9660 namespace. */
505 PrimaryIsoDirs,
506 /** The finalized directory data for the joliet namespace. */
507 JolietDirs;
508
509 /** The image byte offset of the first file. */
510 uint64_t offFirstFile;
511 /** Finalized file head (RTFSISOMAKERFILE).
512 * The list is ordered by disk location. Files are following the
513 * directories and path tables. */
514 RTLISTANCHOR FinalizedFiles;
515 /** @} */
516
517} RTFSISOMAKERINT;
518/** Pointer to an ISO maker instance. */
519typedef RTFSISOMAKERINT *PRTFSISOMAKERINT;
520
521/** Pointer to the data for finalized ISO-9660 (primary / joliet) dirs. */
522typedef struct RTFSISOMAKERINT::RTFSISOMAKERFINALIZEDDIRS *PRTFSISOMAKERFINALIZEDDIRS;
523
524
525/**
526 * Instance data of an ISO maker output file.
527 */
528typedef struct RTFSISOMAKEROUTPUTFILE
529{
530 /** The ISO maker (owns a reference). */
531 PRTFSISOMAKERINT pIsoMaker;
532 /** The current file position. */
533 uint64_t offCurPos;
534 /** Current file hint. */
535 PRTFSISOMAKERFILE pFileHint;
536 /** Source file corresponding to pFileHint.
537 * This is used when dealing with a RTFSISOMAKERSRCTYPE_VFS_FILE or
538 * RTFSISOMAKERSRCTYPE_TRANS_TBL file. */
539 RTVFSFILE hVfsSrcFile;
540 /** Current directory hint. */
541 PRTFSISOMAKERNAMEDIR pDirHint;
542} RTFSISOMAKEROUTPUTFILE;
543/** Pointer to the instance data of an ISO maker output file. */
544typedef RTFSISOMAKEROUTPUTFILE *PRTFSISOMAKEROUTPUTFILE;
545
546
547
548/*********************************************************************************************************************************
549* Structures and Typedefs *
550*********************************************************************************************************************************/
551/**
552 * Help for iterating over namespaces.
553 */
554static const struct
555{
556 /** The RTFSISOMAKER_NAMESPACE_XXX indicator. */
557 uint32_t fNamespace;
558 /** Offset into RTFSISOMAKERINT of the namespace member. */
559 uintptr_t offNamespace;
560 /** Offset into RTFSISOMAKERNAMESPACE of the name member. */
561 uintptr_t offName;
562 /** Namespace name for debugging purposes. */
563 const char *pszName;
564} g_aRTFsIsoNamespaces[] =
565{
566 { RTFSISOMAKER_NAMESPACE_ISO_9660, RT_OFFSETOF(RTFSISOMAKERINT, PrimaryIso), RT_OFFSETOF(RTFSISOMAKEROBJ, pPrimaryName), "iso-9660" },
567 { RTFSISOMAKER_NAMESPACE_JOLIET, RT_OFFSETOF(RTFSISOMAKERINT, Joliet), RT_OFFSETOF(RTFSISOMAKEROBJ, pJolietName), "joliet" },
568 { RTFSISOMAKER_NAMESPACE_UDF, RT_OFFSETOF(RTFSISOMAKERINT, Udf), RT_OFFSETOF(RTFSISOMAKEROBJ, pUdfName), "udf" },
569 { RTFSISOMAKER_NAMESPACE_HFS, RT_OFFSETOF(RTFSISOMAKERINT, Hfs), RT_OFFSETOF(RTFSISOMAKEROBJ, pHfsName), "hfs" },
570};
571
572/**
573 * Translates a single namespace flag (RTFSISOMAKER_NAMESPACE_XXX) to an
574 * index into g_aRTFsIsoNamespaces.
575 */
576static const uint8_t g_aidxRTFsIsoNamespaceFlagToIdx[] =
577{
578 /*[0] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
579 /*[RTFSISOMAKER_NAMESPACE_ISO_9660] = */ 0,
580 /*[2] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
581 /*[3] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
582 /*[RTFSISOMAKER_NAMESPACE_JOLIET] = */ 1,
583 /*[5] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
584 /*[6] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
585 /*[7] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
586 /*[RTFSISOMAKER_NAMESPACE_UDF] = */ 2,
587 /*[9] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
588 /*[10] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
589 /*[11] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
590 /*[12] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
591 /*[13] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
592 /*[14] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
593 /*[15] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
594 /*[RTFSISOMAKER_NAMESPACE_HFS] = */ 3,
595 /*[17] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
596 /*[18] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
597 /*[19] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
598 /*[20] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
599 /*[21] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
600 /*[22] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
601 /*[23] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
602 /*[24] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
603 /*[25] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
604 /*[26] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
605 /*[27] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
606 /*[28] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
607 /*[29] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
608 /*[30] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
609 /*[31] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
610};
611
612/** The default translation table filename. */
613static const char g_szTransTbl[] = "TRANS.TBL";
614/** The default data preparer ID for the primary ISO-9660 volume descriptor. */
615static char g_szPreparerIdPrimaryIso[64] = "";
616/** The default data preparer ID for the joliet volume descriptor. */
617static char g_szPreparerIdJoliet[64] = "";
618
619
620/*********************************************************************************************************************************
621* Internal Functions *
622*********************************************************************************************************************************/
623static int rtFsIsoMakerObjSetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj,
624 PRTFSISOMAKERNAME pParent, const char *pchSpec, size_t cchSpec, PPRTFSISOMAKERNAME ppNewName);
625static int rtFsIsoMakerObjUnsetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj);
626static int rtFsIsoMakerAddUnnamedDirWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKERDIR *ppDir);
627static int rtFsIsoMakerAddUnnamedFileWorker(PRTFSISOMAKERINT pThis, PCRTFSOBJINFO pObjInfo, size_t cbExtra,
628 PRTFSISOMAKERFILE *ppFile);
629static int rtFsIsoMakerObjRemoveWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKEROBJ pObj);
630
631static DECLCALLBACK(int) rtFsIsoMakerOutFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual);
632
633
634
635/**
636 * Creates an ISO maker instance.
637 *
638 * @returns IPRT status code.
639 * @param phIsoMaker Where to return the handle to the new ISO maker.
640 */
641RTDECL(int) RTFsIsoMakerCreate(PRTFSISOMAKER phIsoMaker)
642{
643 /*
644 * Do some integrity checks first.
645 */
646 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_ISO_9660]].fNamespace == RTFSISOMAKER_NAMESPACE_ISO_9660,
647 VERR_INTERNAL_ERROR_5);
648 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_JOLIET]].fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
649 VERR_INTERNAL_ERROR_5);
650 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_UDF]].fNamespace == RTFSISOMAKER_NAMESPACE_UDF,
651 VERR_INTERNAL_ERROR_5);
652 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_HFS]].fNamespace == RTFSISOMAKER_NAMESPACE_HFS,
653 VERR_INTERNAL_ERROR_5);
654
655 if (g_szPreparerIdPrimaryIso[0] == '\0')
656 RTStrPrintf(g_szPreparerIdPrimaryIso, sizeof(g_szPreparerIdPrimaryIso), "IPRT ISO MAKER V%u.%u.%u R%s",
657 RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild(), RTBldCfgRevision());
658 if (g_szPreparerIdJoliet[0] == '\0')
659 RTStrPrintf(g_szPreparerIdJoliet, sizeof(g_szPreparerIdJoliet),
660 "IPRT ISO Maker v%s r%s", RTBldCfgVersion(), RTBldCfgRevision());
661
662 /*
663 * Create the instance with defaults.
664 */
665 PRTFSISOMAKERINT pThis = (PRTFSISOMAKERINT)RTMemAllocZ(sizeof(*pThis));
666 if (pThis)
667 {
668 pThis->uMagic = RTFSISOMAKERINT_MAGIC;
669 pThis->cRefs = 1;
670 //pThis->fSeenContent = false;
671 //pThis->fFinalized = false;
672
673 pThis->PrimaryIso.fNamespace = RTFSISOMAKER_NAMESPACE_ISO_9660;
674 pThis->PrimaryIso.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pPrimaryName);
675 pThis->PrimaryIso.uLevel = 3; /* 30 char names, large files */
676 pThis->PrimaryIso.uRockRidgeLevel = 1;
677 pThis->PrimaryIso.pszTransTbl = (char *)g_szTransTbl;
678 //pThis->PrimaryIso.pszSystemId = NULL;
679 //pThis->PrimaryIso.pszVolumeId = NULL;
680 //pThis->PrimaryIso.pszSetVolumeId = NULL;
681 //pThis->PrimaryIso.pszPublisherId = NULL;
682 pThis->PrimaryIso.pszDataPreparerId = g_szPreparerIdPrimaryIso;
683 //pThis->PrimaryIso.pszApplicationId = NULL;
684 //pThis->PrimaryIso.pszCopyrightFileId= NULL;
685 //pThis->PrimaryIso.pszAbstractFileId = NULL;
686 //pThis->PrimaryIso.pszBibliographicFileId = NULL;
687
688 pThis->Joliet.fNamespace = RTFSISOMAKER_NAMESPACE_JOLIET;
689 pThis->Joliet.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pJolietName);
690 pThis->Joliet.uLevel = 3;
691 //pThis->Joliet.uRockRidgeLevel = 0;
692 //pThis->Joliet.pszTransTbl = NULL;
693 //pThis->Joliet.pszSystemId = NULL;
694 //pThis->Joliet.pszVolumeId = NULL;
695 //pThis->Joliet.pszSetVolumeId = NULL;
696 //pThis->Joliet.pszPublisherId = NULL;
697 pThis->Joliet.pszDataPreparerId = g_szPreparerIdJoliet;
698 //pThis->Joliet.pszApplicationId = NULL;
699 //pThis->Joliet.pszCopyrightFileId = NULL;
700 //pThis->Joliet.pszAbstractFileId = NULL;
701 //pThis->Joliet.pszBibliographicFileId = NULL;
702
703 pThis->Udf.fNamespace = RTFSISOMAKER_NAMESPACE_UDF;
704 pThis->Udf.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pUdfName);
705 //pThis->Udf.uLevel = 0;
706 //pThis->Udf.uRockRidgeLevel = 0;
707 //pThis->Udf.pszTransTbl = NULL;
708 //pThis->Udf.uRockRidgeLevel = 0;
709 //pThis->Udf.pszTransTbl = NULL;
710 //pThis->Udf.pszSystemId = NULL;
711 //pThis->Udf.pszVolumeId = NULL;
712 //pThis->Udf.pszSetVolumeId = NULL;
713 //pThis->Udf.pszPublisherId = NULL;
714 //pThis->Udf.pszDataPreparerId = NULL;
715 //pThis->Udf.pszApplicationId = NULL;
716 //pThis->Udf.pszCopyrightFileId = NULL;
717 //pThis->Udf.pszAbstractFileId = NULL;
718 //pThis->Udf.pszBibliographicFileId = NULL;
719
720 pThis->Hfs.fNamespace = RTFSISOMAKER_NAMESPACE_HFS;
721 pThis->Hfs.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pHfsName);
722 //pThis->Hfs.uLevel = 0;
723 //pThis->Hfs.uRockRidgeLevel = 0;
724 //pThis->Hfs.pszTransTbl = NULL;
725 //pThis->Hfs.pszSystemId = NULL;
726 //pThis->Hfs.pszVolumeId = NULL;
727 //pThis->Hfs.pszSetVolumeId = NULL;
728 //pThis->Hfs.pszPublisherId = NULL;
729 //pThis->Hfs.pszDataPreparerId = NULL;
730 //pThis->Hfs.pszApplicationId = NULL;
731 //pThis->Hfs.pszCopyrightFileId = NULL;
732 //pThis->Hfs.pszAbstractFileId = NULL;
733 //pThis->Hfs.pszBibliographicFileId = NULL;
734
735 RTListInit(&pThis->ObjectHead);
736 //pThis->cObjects = 0;
737 //pThis->cbData = 0;
738
739 pThis->cVolumeDescriptors = 3; /* primary, secondary joliet, terminator. */
740
741 //pThis->uidDefault = 0;
742 //pThis->gidDefault = 0;
743 pThis->fDefaultFileMode = 0444 | RTFS_TYPE_FILE | RTFS_DOS_ARCHIVED | RTFS_DOS_READONLY;
744 pThis->fDefaultDirMode = 0555 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_DOS_READONLY;
745
746 pThis->cbFinalizedImage = UINT64_MAX;
747 //pThis->pbSysArea = NULL;
748 //pThis->cbSysArea = 0;
749 //pThis->pbVolDescs = NULL;
750 //pThis->pPrimaryVolDesc = NULL;
751 //pThis->pElToritoDesc = NULL;
752 //pThis->pJolietVolDesc = NULL;
753
754 pThis->PrimaryIsoDirs.offDirs = UINT64_MAX;
755 pThis->PrimaryIsoDirs.offPathTableL = UINT64_MAX;
756 pThis->PrimaryIsoDirs.offPathTableM = UINT64_MAX;
757 pThis->PrimaryIsoDirs.cbPathTable = 0;
758 RTListInit(&pThis->PrimaryIsoDirs.FinalizedDirs);
759 //pThis->PrimaryIsoDirs.pRRSpillFile = NULL;
760
761 pThis->JolietDirs.offDirs = UINT64_MAX;
762 pThis->JolietDirs.offPathTableL = UINT64_MAX;
763 pThis->JolietDirs.offPathTableM = UINT64_MAX;
764 pThis->JolietDirs.cbPathTable = 0;
765 RTListInit(&pThis->JolietDirs.FinalizedDirs);
766 //pThis->JolietDirs.pRRSpillFile = NULL;
767
768 pThis->offFirstFile = UINT64_MAX;
769 RTListInit(&pThis->FinalizedFiles);
770
771 RTTimeNow(&pThis->ImageCreationTime);
772 *phIsoMaker = pThis;
773 return VINF_SUCCESS;
774 }
775 return VERR_NO_MEMORY;
776}
777
778
779/**
780 * Frees an object.
781 *
782 * This is a worker for rtFsIsoMakerDestroy and RTFsIsoMakerObjRemove.
783 *
784 * @param pObj The object to free.
785 */
786DECLINLINE(void) rtFsIsoMakerObjDestroy(PRTFSISOMAKEROBJ pObj)
787{
788 if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
789 {
790 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
791 switch (pFile->enmSrcType)
792 {
793 case RTFSISOMAKERSRCTYPE_PATH:
794 pFile->u.pszSrcPath = NULL;
795 break;
796
797 case RTFSISOMAKERSRCTYPE_TRANS_TBL:
798 pFile->u.pTransTblDir = NULL;
799 break;
800
801 case RTFSISOMAKERSRCTYPE_VFS_FILE:
802 RTVfsFileRelease(pFile->u.hVfsFile);
803 pFile->u.hVfsFile = NIL_RTVFSFILE;
804 break;
805
806 case RTFSISOMAKERSRCTYPE_INVALID:
807 case RTFSISOMAKERSRCTYPE_END:
808 AssertFailed();
809 break;
810
811 /* no default, want warnings */
812 }
813 }
814
815 RTMemFree(pObj);
816}
817
818
819/**
820 * Frees a namespace node.
821 *
822 * This is a worker for rtFsIsoMakerDestroyTree and rtFsIsoMakerObjUnsetName.
823 *
824 * @param pName The node to free.
825 */
826DECLINLINE(void) rtFsIsoMakerDestroyName(PRTFSISOMAKERNAME pName)
827{
828 if (pName->fRockRidgeNmAlloced)
829 {
830 RTMemFree(pName->pszRockRidgeNm);
831 pName->pszRockRidgeNm = NULL;
832 }
833 if (pName->fTransNmAlloced)
834 {
835 RTMemFree(pName->pszTransNm);
836 pName->pszTransNm = NULL;
837 }
838 RTMemFree(pName);
839}
840
841
842/**
843 * Destroys a namespace.
844 *
845 * @param pNamespace The namespace to destroy.
846 */
847static void rtFsIsoMakerDestroyTree(PRTFSISOMAKERNAMESPACE pNamespace)
848{
849 /*
850 * Recursively destroy the tree first.
851 */
852 PRTFSISOMAKERNAME pCur = pNamespace->pRoot;
853 if (pCur)
854 {
855 Assert(!pCur->pParent);
856 for (;;)
857 {
858 if ( pCur->pDir
859 && pCur->pDir->cChildren)
860 pCur = pCur->pDir->papChildren[pCur->pDir->cChildren - 1];
861 else
862 {
863 PRTFSISOMAKERNAME pNext = pCur->pParent;
864 rtFsIsoMakerDestroyName(pCur);
865
866 /* Unlink from parent, we're the last entry. */
867 if (pNext)
868 {
869 Assert(pNext->pDir->cChildren > 0);
870 pNext->pDir->cChildren--;
871 Assert(pNext->pDir->papChildren[pNext->pDir->cChildren] == pCur);
872 pNext->pDir->papChildren[pNext->pDir->cChildren] = NULL;
873 pCur = pNext;
874 }
875 else
876 {
877 Assert(pNamespace->pRoot == pCur);
878 break;
879 }
880 }
881 }
882 pNamespace->pRoot = NULL;
883 }
884
885 /*
886 * Free the translation table filename if allocated.
887 */
888 if (pNamespace->pszTransTbl)
889 {
890 if (pNamespace->pszTransTbl != g_szTransTbl)
891 RTMemFree(pNamespace->pszTransTbl);
892 pNamespace->pszTransTbl = NULL;
893 }
894
895 /*
896 * Free string IDs.
897 */
898 if (pNamespace->pszSystemId)
899 {
900 RTMemFree(pNamespace->pszSystemId);
901 pNamespace->pszSystemId = NULL;
902 }
903
904 if (pNamespace->pszVolumeId)
905 {
906 RTMemFree(pNamespace->pszVolumeId);
907 pNamespace->pszVolumeId = NULL;
908 }
909
910 if (pNamespace->pszVolumeSetId)
911 {
912 RTMemFree(pNamespace->pszVolumeSetId);
913 pNamespace->pszVolumeSetId = NULL;
914 }
915
916 if (pNamespace->pszPublisherId)
917 {
918 RTMemFree(pNamespace->pszPublisherId);
919 pNamespace->pszPublisherId = NULL;
920 }
921
922 if (pNamespace->pszDataPreparerId)
923 {
924 if ( pNamespace->pszDataPreparerId != g_szPreparerIdPrimaryIso
925 && pNamespace->pszDataPreparerId != g_szPreparerIdJoliet)
926 RTMemFree(pNamespace->pszDataPreparerId);
927 pNamespace->pszDataPreparerId = NULL;
928 }
929
930 if (pNamespace->pszApplicationId)
931 {
932 RTMemFree(pNamespace->pszApplicationId);
933 pNamespace->pszApplicationId = NULL;
934 }
935
936 if (pNamespace->pszCopyrightFileId)
937 {
938 RTMemFree(pNamespace->pszCopyrightFileId);
939 pNamespace->pszCopyrightFileId = NULL;
940 }
941
942 if (pNamespace->pszAbstractFileId)
943 {
944 RTMemFree(pNamespace->pszAbstractFileId);
945 pNamespace->pszAbstractFileId = NULL;
946 }
947
948 if (pNamespace->pszBibliographicFileId)
949 {
950 RTMemFree(pNamespace->pszBibliographicFileId);
951 pNamespace->pszBibliographicFileId = NULL;
952 }
953}
954
955
956/**
957 * Destroys an ISO maker instance.
958 *
959 * @param pThis The ISO maker instance to destroy.
960 */
961static void rtFsIsoMakerDestroy(PRTFSISOMAKERINT pThis)
962{
963 rtFsIsoMakerDestroyTree(&pThis->PrimaryIso);
964 rtFsIsoMakerDestroyTree(&pThis->Joliet);
965 rtFsIsoMakerDestroyTree(&pThis->Udf);
966 rtFsIsoMakerDestroyTree(&pThis->Hfs);
967
968 PRTFSISOMAKEROBJ pCur;
969 PRTFSISOMAKEROBJ pNext;
970 RTListForEachSafe(&pThis->ObjectHead, pCur, pNext, RTFSISOMAKEROBJ, Entry)
971 {
972 RTListNodeRemove(&pCur->Entry);
973 rtFsIsoMakerObjDestroy(pCur);
974 }
975}
976
977
978/**
979 * Retains a references to an ISO maker instance.
980 *
981 * @returns New reference count on success, UINT32_MAX if invalid handle.
982 * @param hIsoMaker The ISO maker handle.
983 */
984RTDECL(uint32_t) RTFsIsoMakerRetain(RTFSISOMAKER hIsoMaker)
985{
986 PRTFSISOMAKERINT pThis = hIsoMaker;
987 AssertPtrReturn(pThis, UINT32_MAX);
988 AssertReturn(pThis->uMagic == RTFSISOMAKERINT_MAGIC, UINT32_MAX);
989 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
990 Assert(cRefs > 1);
991 Assert(cRefs < _64K);
992 return cRefs;
993}
994
995
996/**
997 * Releases a references to an ISO maker instance.
998 *
999 * @returns New reference count on success, UINT32_MAX if invalid handle.
1000 * @param hIsoMaker The ISO maker handle. NIL is ignored.
1001 */
1002RTDECL(uint32_t) RTFsIsoMakerRelease(RTFSISOMAKER hIsoMaker)
1003{
1004 PRTFSISOMAKERINT pThis = hIsoMaker;
1005 uint32_t cRefs;
1006 if (pThis == NIL_RTFSISOMAKER)
1007 cRefs = 0;
1008 else
1009 {
1010 AssertPtrReturn(pThis, UINT32_MAX);
1011 AssertReturn(pThis->uMagic == RTFSISOMAKERINT_MAGIC, UINT32_MAX);
1012 cRefs = ASMAtomicDecU32(&pThis->cRefs);
1013 Assert(cRefs < _64K);
1014 if (!cRefs)
1015 rtFsIsoMakerDestroy(pThis);
1016 }
1017 return cRefs;
1018}
1019
1020
1021/**
1022 * Sets the ISO-9660 level.
1023 *
1024 * @returns IPRT status code
1025 * @param hIsoMaker The ISO maker handle.
1026 * @param uIsoLevel The level, 1-3.
1027 */
1028RTDECL(int) RTFsIsoMakerSetIso9660Level(RTFSISOMAKER hIsoMaker, uint8_t uIsoLevel)
1029{
1030 PRTFSISOMAKERINT pThis = hIsoMaker;
1031 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
1032 AssertReturn(uIsoLevel <= 3, VERR_INVALID_PARAMETER);
1033 AssertReturn(uIsoLevel > 0, VERR_INVALID_PARAMETER); /* currently not possible to disable this */
1034 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1035
1036 pThis->PrimaryIso.uLevel = uIsoLevel;
1037 return VINF_SUCCESS;
1038}
1039
1040
1041/**
1042 * Sets the joliet level.
1043 *
1044 * @returns IPRT status code
1045 * @param hIsoMaker The ISO maker handle.
1046 * @param uJolietLevel The joliet UCS-2 level 1-3, or 0 to disable
1047 * joliet.
1048 */
1049RTDECL(int) RTFsIsoMakerSetJolietUcs2Level(RTFSISOMAKER hIsoMaker, uint8_t uJolietLevel)
1050{
1051 PRTFSISOMAKERINT pThis = hIsoMaker;
1052 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
1053 AssertReturn(uJolietLevel <= 3, VERR_INVALID_PARAMETER);
1054 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1055
1056 if (pThis->Joliet.uLevel != uJolietLevel)
1057 {
1058 if (uJolietLevel == 0)
1059 pThis->cVolumeDescriptors--;
1060 else if (pThis->Joliet.uLevel == 0)
1061 pThis->cVolumeDescriptors++;
1062 pThis->Joliet.uLevel = uJolietLevel;
1063 }
1064 return VINF_SUCCESS;
1065}
1066
1067
1068/**
1069 * Sets the rock ridge support level (on the primary ISO-9660 namespace).
1070 *
1071 * @returns IPRT status code
1072 * @param hIsoMaker The ISO maker handle.
1073 * @param uLevel 0 if disabled, 1 to just enable, 2 to enable and
1074 * write the ER tag.
1075 */
1076RTDECL(int) RTFsIsoMakerSetRockRidgeLevel(RTFSISOMAKER hIsoMaker, uint8_t uLevel)
1077{
1078 PRTFSISOMAKERINT pThis = hIsoMaker;
1079 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
1080 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1081 AssertReturn(uLevel <= 2, VERR_INVALID_PARAMETER);
1082
1083 pThis->PrimaryIso.uRockRidgeLevel = uLevel;
1084 return VINF_SUCCESS;
1085}
1086
1087
1088/**
1089 * Sets the rock ridge support level on the joliet namespace (experimental).
1090 *
1091 * @returns IPRT status code
1092 * @param hIsoMaker The ISO maker handle.
1093 * @param uLevel 0 if disabled, 1 to just enable, 2 to enable and
1094 * write the ER tag.
1095 */
1096RTDECL(int) RTFsIsoMakerSetJolietRockRidgeLevel(RTFSISOMAKER hIsoMaker, uint8_t uLevel)
1097{
1098 PRTFSISOMAKERINT pThis = hIsoMaker;
1099 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
1100 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1101 AssertReturn(uLevel <= 2, VERR_INVALID_PARAMETER);
1102
1103 pThis->Joliet.uRockRidgeLevel = uLevel;
1104 return VINF_SUCCESS;
1105}
1106
1107
1108
1109/*
1110 *
1111 * Name space related internals.
1112 * Name space related internals.
1113 * Name space related internals.
1114 *
1115 */
1116
1117
1118/**
1119 * Gets the pointer to the name member for the given namespace.
1120 *
1121 * @returns Pointer to name member.
1122 * @param pObj The object to find a name member in.
1123 * @param pNamespace The namespace which name to calculate.
1124 */
1125DECLINLINE(PPRTFSISOMAKERNAME) rtFsIsoMakerObjGetNameForNamespace(PRTFSISOMAKEROBJ pObj, PCRTFSISOMAKERNAMESPACE pNamespace)
1126{
1127 return (PPRTFSISOMAKERNAME)((uintptr_t)pObj + pNamespace->offName);
1128}
1129
1130
1131/**
1132 * Locates a child object by its namespace name.
1133 *
1134 * @returns Pointer to the child if found, NULL if not.
1135 * @param pDirObj The directory object to search.
1136 * @param pszEntry The (namespace) entry name.
1137 * @param cchEntry The length of the name.
1138 */
1139static PRTFSISOMAKERNAME rtFsIsoMakerFindObjInDir(PRTFSISOMAKERNAME pDirObj, const char *pszEntry, size_t cchEntry)
1140{
1141 if (pDirObj)
1142 {
1143 PRTFSISOMAKERNAMEDIR pDir = pDirObj->pDir;
1144 AssertReturn(pDir, NULL);
1145
1146 uint32_t i = pDir->cChildren;
1147 while (i-- > 0)
1148 {
1149 PRTFSISOMAKERNAME pChild = pDir->papChildren[i];
1150 if ( pChild->cchName == cchEntry
1151 && RTStrNICmp(pChild->szName, pszEntry, cchEntry) == 0)
1152 return pChild;
1153 }
1154 }
1155 return NULL;
1156}
1157
1158
1159/**
1160 * Compares the two names according to ISO-9660 directory sorting rules.
1161 *
1162 * As long as we don't want to do case insensitive joliet sorting, this works
1163 * for joliet names to, I think.
1164 *
1165 * @returns 0 if equal, -1 if pszName1 comes first, 1 if pszName2 comes first.
1166 * @param pszName1 The first name.
1167 * @param pszName2 The second name.
1168 */
1169DECLINLINE(int) rtFsIsoMakerCompareIso9660Names(const char *pszName1, const char *pszName2)
1170{
1171 for (;;)
1172 {
1173 char const ch1 = *pszName1++;
1174 char const ch2 = *pszName2++;
1175 if (ch1 == ch2)
1176 {
1177 if (ch1)
1178 { /* likely */ }
1179 else
1180 return 0;
1181 }
1182 else if (ch1 == ';' || ch2 == ';')
1183 return ch1 == ';' ? -1 : 1;
1184 else if (ch1 == '.' || ch2 == '.')
1185 return ch1 == '.' ? -1 : 1;
1186 else
1187 return (unsigned char)ch1 < (unsigned char)ch2 ? -1 : 1;
1188 }
1189}
1190
1191
1192/**
1193 * Finds the index into papChildren where the given name should be inserted.
1194 *
1195 * @returns Index of the given name.
1196 * @param pNamespace The namspace.
1197 * @param pParent The parent namespace node.
1198 * @param pszName The name.
1199 */
1200static uint32_t rtFsIsoMakerFindInsertIndex(PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKERNAME pParent, const char *pszName)
1201{
1202 uint32_t idxRet = pParent->pDir->cChildren;
1203 if (idxRet > 0)
1204 {
1205 /*
1206 * The idea is to do binary search using a namespace specific compare
1207 * function. However, it looks like we can get away with using the
1208 * same compare function for all namespaces.
1209 */
1210 uint32_t idxStart = 0;
1211 uint32_t idxEnd = idxRet;
1212 PPRTFSISOMAKERNAME papChildren = pParent->pDir->papChildren;
1213 switch (pNamespace->fNamespace)
1214 {
1215 case RTFSISOMAKER_NAMESPACE_ISO_9660:
1216 case RTFSISOMAKER_NAMESPACE_JOLIET:
1217 case RTFSISOMAKER_NAMESPACE_UDF:
1218 case RTFSISOMAKER_NAMESPACE_HFS:
1219 for (;;)
1220 {
1221 idxRet = idxStart + (idxEnd - idxStart) / 2;
1222 PRTFSISOMAKERNAME pCur = papChildren[idxRet];
1223 int iDiff = rtFsIsoMakerCompareIso9660Names(pszName, pCur->szName);
1224 if (iDiff < 0)
1225 {
1226 if (idxRet > idxStart)
1227 idxEnd = idxRet;
1228 else
1229 break;
1230 }
1231 else
1232 {
1233 idxRet++;
1234 if ( iDiff != 0
1235 && idxRet < idxEnd)
1236 idxStart = idxRet;
1237 else
1238 break;
1239 }
1240 }
1241 break;
1242
1243 default:
1244 AssertFailed();
1245 break;
1246 }
1247 }
1248 return idxRet;
1249}
1250
1251
1252
1253/**
1254 * Locates a child entry by its specified name.
1255 *
1256 * @returns Pointer to the child if found, NULL if not.
1257 * @param pDirName The directory name to search.
1258 * @param pszEntry The (specified) entry name.
1259 * @param cchEntry The length of the name.
1260 */
1261static PRTFSISOMAKERNAME rtFsIsoMakerFindEntryInDirBySpec(PRTFSISOMAKERNAME pDirName, const char *pszEntry, size_t cchEntry)
1262{
1263 if (pDirName)
1264 {
1265 PRTFSISOMAKERNAMEDIR pDir = pDirName->pDir;
1266 AssertReturn(pDir, NULL);
1267
1268 uint32_t i = pDir->cChildren;
1269 while (i-- > 0)
1270 {
1271 PRTFSISOMAKERNAME pChild = pDir->papChildren[i];
1272 if ( pChild->cchSpecNm == cchEntry
1273 && RTStrNICmp(pChild->pszSpecNm, pszEntry, cchEntry) == 0)
1274 return pChild;
1275 }
1276 }
1277 return NULL;
1278}
1279
1280
1281/**
1282 * Locates a subdir object in any namespace by its specified name.
1283 *
1284 * This is used to avoid having one instance of RTFSISOMAKERDIR in each
1285 * namespace for the same directory.
1286 *
1287 * @returns Pointer to the subdir object if found, NULL if not.
1288 * @param pDirObj The directory object to search.
1289 * @param pszEntry The (specified) entry name.
1290 * @param cchEntry The length of the name.
1291 * @param fSkipNamespaces Namespaces to skip.
1292 * @sa rtFsIsoMakerFindEntryInDirBySpec
1293 */
1294static PRTFSISOMAKERDIR rtFsIsoMakerFindSubdirBySpec(PRTFSISOMAKERDIR pDirObj, const char *pszEntry, size_t cchEntry,
1295 uint32_t fSkipNamespaces)
1296{
1297 AssertReturn(pDirObj, NULL);
1298 AssertReturn(pDirObj->Core.enmType == RTFSISOMAKEROBJTYPE_DIR, NULL);
1299 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
1300 if (!(fSkipNamespaces & g_aRTFsIsoNamespaces[i].fNamespace))
1301 {
1302 PRTFSISOMAKERNAME pDirName = *(PPRTFSISOMAKERNAME)((uintptr_t)pDirObj + g_aRTFsIsoNamespaces[i].offName);
1303 if (pDirName)
1304 {
1305 PRTFSISOMAKERNAMEDIR pDir = pDirName->pDir;
1306 AssertStmt(pDir, continue);
1307
1308 uint32_t iChild = pDir->cChildren;
1309 while (iChild-- > 0)
1310 {
1311 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
1312 if ( pChild->cchSpecNm == cchEntry
1313 && pChild->pDir != NULL
1314 && RTStrNICmp(pChild->pszSpecNm, pszEntry, cchEntry) == 0)
1315 return (PRTFSISOMAKERDIR)pChild->pObj;
1316 }
1317 }
1318 }
1319 return NULL;
1320}
1321
1322
1323/**
1324 * Walks the given path by specified object names in a namespace.
1325 *
1326 * @returns IPRT status code.
1327 * @param pNamespace The namespace to walk the path in.
1328 * @param pszPath The path to walk.
1329 * @param ppName Where to return the name node that the path ends with.
1330 */
1331static int rtFsIsoMakerWalkPathBySpec(PRTFSISOMAKERNAMESPACE pNamespace, const char *pszPath, PPRTFSISOMAKERNAME ppName)
1332{
1333 *ppName = NULL;
1334 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INVALID_NAME);
1335
1336 /*
1337 * Deal with the special case of the root.
1338 */
1339 while (RTPATH_IS_SLASH(*pszPath))
1340 pszPath++;
1341 AssertReturn(*pszPath, VERR_INTERNAL_ERROR_4);
1342
1343 PRTFSISOMAKERNAME pCur = pNamespace->pRoot;
1344 if (!pCur)
1345 return *pszPath ? VERR_PATH_NOT_FOUND : VERR_FILE_NOT_FOUND;
1346 if (!*pszPath)
1347 {
1348 *ppName = pCur;
1349 return VINF_SUCCESS;
1350 }
1351
1352 /*
1353 * Now, do the rest of the path.
1354 */
1355 for (;;)
1356 {
1357 /*
1358 * Find the end of the component.
1359 */
1360 char ch;
1361 size_t cchComponent = 0;
1362 while ((ch = pszPath[cchComponent]) != '\0' && RTPATH_IS_SLASH(ch))
1363 cchComponent++;
1364 if (!cchComponent)
1365 {
1366 *ppName = pCur;
1367 return VINF_SUCCESS;
1368 }
1369
1370 size_t offNext = cchComponent;
1371 while (RTPATH_IS_SLASH(ch))
1372 ch = pszPath[offNext++];
1373
1374 /*
1375 * Deal with dot and dot-dot.
1376 */
1377 if (cchComponent == 1 && pszPath[0] == '.')
1378 { /* nothing to do */ }
1379 else if (cchComponent == 2 && pszPath[0] == '.' && pszPath[1] == '.')
1380 {
1381 if (pCur->pParent)
1382 pCur = pCur->pParent;
1383 }
1384 /*
1385 * Look up the name.
1386 */
1387 else
1388 {
1389 PRTFSISOMAKERNAME pChild = rtFsIsoMakerFindEntryInDirBySpec(pCur, pszPath, cchComponent);
1390 if (!pChild)
1391 return pszPath[offNext] ? VERR_PATH_NOT_FOUND : VERR_FILE_NOT_FOUND;
1392 if ( (offNext > cchComponent)
1393 && !pChild->pDir)
1394 return VERR_NOT_A_DIRECTORY;
1395 pCur = pChild;
1396 }
1397
1398 /*
1399 * Skip ahead in the path.
1400 */
1401 pszPath += offNext;
1402 }
1403}
1404
1405
1406/**
1407 * Copy and convert a name to valid ISO-9660 (d-characters only).
1408 *
1409 * Worker for rtFsIsoMakerNormalizeNameForNamespace. ASSUMES it deals with
1410 * dots.
1411 *
1412 * @returns Length of the resulting string.
1413 * @param pszDst The output buffer.
1414 * @param cchDstMax The maximum number of (d-chars) to put in the output
1415 * buffer.
1416 * @param pchSrc The UTF-8 source string (not neccessarily terminated).
1417 * @param cchSrc The maximum number of chars to copy from the source
1418 * string.
1419 */
1420static size_t rtFsIsoMakerCopyIso9660Name(char *pszDst, size_t cchDstMax, const char *pchSrc, size_t cchSrc)
1421{
1422 const char *pchSrcIn = pchSrc;
1423 size_t offDst = 0;
1424 while ((size_t)(pchSrc - pchSrcIn) < cchSrc)
1425 {
1426 RTUNICP uc;
1427 int rc = RTStrGetCpEx(&pchSrc, &uc);
1428 if (RT_SUCCESS(rc))
1429 {
1430 if ( uc < 128
1431 && RTFSISOMAKER_IS_UPPER_IN_D_CHARS((char)uc))
1432 {
1433 pszDst[offDst++] = RT_C_TO_UPPER((char)uc);
1434 if (offDst >= cchDstMax)
1435 break;
1436 }
1437 }
1438 }
1439 pszDst[offDst] = '\0';
1440 return offDst;
1441}
1442
1443
1444/**
1445 * Normalizes a name for the primary ISO-9660 namespace.
1446 *
1447 * @returns IPRT status code.
1448 * @param pThis The ISO maker instance.
1449 * @param pParent The parent directory. NULL if root.
1450 * @param pchSrc The specified name to normalize (not necessarily zero
1451 * terminated).
1452 * @param cchSrc The length of the specified name.
1453 * @param fIsDir Indicates whether it's a directory or file (like).
1454 * @param pszDst The output buffer. Must be at least 32 bytes.
1455 * @param cbDst The size of the output buffer.
1456 * @param pcchDst Where to return the length of the returned string (i.e.
1457 * not counting the terminator).
1458 * @param pcbInDirRec Where to return the name size in the directory record.
1459 */
1460static int rtFsIsoMakerNormalizeNameForPrimaryIso9660(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAME pParent,
1461 const char *pchSrc, size_t cchSrc, bool fIsDir,
1462 char *pszDst, size_t cbDst, size_t *pcchDst, size_t *pcbInDirRec)
1463{
1464 AssertReturn(cbDst > ISO9660_MAX_NAME_LEN, VERR_INTERNAL_ERROR_3);
1465
1466 /* Skip leading dots. */
1467 while (cchSrc > 0 && *pchSrc == '.')
1468 pchSrc++, cchSrc--;
1469 if (!cchSrc)
1470 {
1471 pchSrc = "DOTS";
1472 cchSrc = 4;
1473 }
1474
1475 /*
1476 * Produce a first name.
1477 */
1478 uint8_t const uIsoLevel = pThis->PrimaryIso.uLevel;
1479 size_t cchDst;
1480 size_t offDstDot;
1481 if (fIsDir)
1482 offDstDot = cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, uIsoLevel >= 2 ? ISO9660_MAX_NAME_LEN : 8,
1483 pchSrc, cchSrc);
1484 else
1485 {
1486 /* Look for the last dot and try preserve the extension when doing the conversion. */
1487 size_t offLastDot = cchSrc;
1488 for (size_t off = 0; off < cchSrc; off++)
1489 if (pchSrc[off] == '.')
1490 offLastDot = off;
1491
1492 if (offLastDot == cchSrc)
1493 offDstDot = cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, uIsoLevel >= 2 ? ISO9660_MAX_NAME_LEN : 8,
1494 pchSrc, cchSrc);
1495 else
1496 {
1497 const char * const pchSrcExt = &pchSrc[offLastDot + 1];
1498 size_t const cchSrcExt = cchSrc - offLastDot - 1;
1499 if (uIsoLevel < 2)
1500 {
1501 cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, 8, pchSrc, cchSrc);
1502 offDstDot = cchDst;
1503 pszDst[cchDst++] = '.';
1504 cchDst += rtFsIsoMakerCopyIso9660Name(&pszDst[cchDst], 3, pchSrcExt, cchSrcExt);
1505 }
1506 else
1507 {
1508 size_t cchDstExt = rtFsIsoMakerCopyIso9660Name(pszDst, ISO9660_MAX_NAME_LEN - 2, pchSrcExt, cchSrcExt);
1509 if (cchDstExt > 0)
1510 {
1511 size_t cchBasename = rtFsIsoMakerCopyIso9660Name(pszDst, ISO9660_MAX_NAME_LEN - 2,
1512 pchSrc, offLastDot);
1513 if (cchBasename + 1 + cchDstExt <= ISO9660_MAX_NAME_LEN)
1514 cchDst = cchBasename;
1515 else
1516 cchDst = ISO9660_MAX_NAME_LEN - 1 - RT_MIN(cchDstExt, 4);
1517 offDstDot = cchDst;
1518 pszDst[cchDst++] = '.';
1519 cchDst += rtFsIsoMakerCopyIso9660Name(pszDst, ISO9660_MAX_NAME_LEN - 1 - cchDst,
1520 pchSrcExt, cchSrcExt);
1521 }
1522 else
1523 offDstDot = cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, ISO9660_MAX_NAME_LEN, pchSrc, cchSrc);
1524 }
1525 }
1526 }
1527
1528 /*
1529 * Unique name?
1530 */
1531 if (!rtFsIsoMakerFindObjInDir(pParent, pszDst, cchDst))
1532 {
1533 *pcchDst = cchDst;
1534 *pcbInDirRec = cchDst;
1535 return VINF_SUCCESS;
1536 }
1537
1538 /*
1539 * Mangle the name till we've got a unique one.
1540 */
1541 size_t const cchMaxBasename = (uIsoLevel >= 2 ? ISO9660_MAX_NAME_LEN : 8) - (cchDst - offDstDot);
1542 size_t cchInserted = 0;
1543 for (uint32_t i = 0; i < _32K; i++)
1544 {
1545 /* Add a numberic infix. */
1546 char szOrd[64];
1547 size_t cchOrd = RTStrFormatU32(szOrd, sizeof(szOrd), i + 1, 10, -1, -1, 0 /*fFlags*/);
1548 Assert((ssize_t)cchOrd > 0);
1549
1550 /* Do we need to shuffle the suffix? */
1551 if (cchOrd > cchInserted)
1552 {
1553 if (offDstDot < cchMaxBasename)
1554 {
1555 memmove(&pszDst[offDstDot + 1], &pszDst[offDstDot], cchDst + 1 - offDstDot);
1556 cchDst++;
1557 offDstDot++;
1558 }
1559 cchInserted = cchOrd;
1560 }
1561
1562 /* Insert the new infix and try again. */
1563 memcpy(&pszDst[offDstDot - cchOrd], szOrd, cchOrd);
1564 if (!rtFsIsoMakerFindObjInDir(pParent, pszDst, cchDst))
1565 {
1566 *pcchDst = cchDst;
1567 *pcbInDirRec = cchDst;
1568 return VINF_SUCCESS;
1569 }
1570 }
1571 AssertFailed();
1572 return VERR_DUPLICATE;
1573}
1574
1575
1576/**
1577 * Normalizes a name for the specified name space.
1578 *
1579 * @returns IPRT status code.
1580 * @param pThis The ISO maker instance.
1581 * @param pNamespace The namespace which rules to normalize it according to.
1582 * @param pParent The parent directory. NULL if root.
1583 * @param pchSrc The specified name to normalize (not necessarily zero
1584 * terminated).
1585 * @param cchSrc The length of the specified name.
1586 * @param fIsDir Indicates whether it's a directory or file (like).
1587 * @param pszDst The output buffer. Must be at least 32 bytes.
1588 * @param cbDst The size of the output buffer.
1589 * @param pcchDst Where to return the length of the returned string (i.e.
1590 * not counting the terminator).
1591 * @param pcbInDirRec Where to return the name size in the directory record.
1592 */
1593static int rtFsIsoMakerNormalizeNameForNamespace(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
1594 PRTFSISOMAKERNAME pParent, const char *pchSrc, size_t cchSrc, bool fIsDir,
1595 char *pszDst, size_t cbDst, size_t *pcchDst, size_t *pcbInDirRec)
1596{
1597 if (cchSrc > 0)
1598 {
1599 /*
1600 * Check that the object doesn't already exist.
1601 */
1602 AssertReturn(!rtFsIsoMakerFindEntryInDirBySpec(pParent, pchSrc, cchSrc), VERR_ALREADY_EXISTS);
1603 switch (pNamespace->fNamespace)
1604 {
1605 /*
1606 * This one is a lot of work, so separate function.
1607 */
1608 case RTFSISOMAKER_NAMESPACE_ISO_9660:
1609 return rtFsIsoMakerNormalizeNameForPrimaryIso9660(pThis, pParent, pchSrc, cchSrc, fIsDir,
1610 pszDst, cbDst, pcchDst, pcbInDirRec);
1611
1612 /*
1613 * At the moment we don't give darn about UCS-2 limitations here...
1614 */
1615 case RTFSISOMAKER_NAMESPACE_JOLIET:
1616 {
1617/** @todo Joliet name limit and check for duplicates. */
1618 AssertReturn(cbDst > cchSrc, VERR_BUFFER_OVERFLOW);
1619 memcpy(pszDst, pchSrc, cchSrc);
1620 pszDst[cchSrc] = '\0';
1621 *pcchDst = cchSrc;
1622 *pcbInDirRec = RTStrCalcUtf16Len(pszDst);
1623 return VINF_SUCCESS;
1624 }
1625
1626 case RTFSISOMAKER_NAMESPACE_UDF:
1627 case RTFSISOMAKER_NAMESPACE_HFS:
1628 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
1629
1630 default:
1631 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
1632 }
1633 }
1634 else
1635 {
1636 /*
1637 * Root special case.
1638 */
1639 *pszDst = '\0';
1640 *pcchDst = 0;
1641 *pcbInDirRec = 0;
1642 AssertReturn(pParent, VERR_INTERNAL_ERROR_3);
1643 return VINF_SUCCESS;
1644 }
1645}
1646
1647
1648/**
1649 * Creates a TRANS.TBL file object for a newly named directory.
1650 *
1651 * The file is associated with the namespace node for the directory. The file
1652 * will be generated on the fly from the directory object.
1653 *
1654 * @returns IPRT status code.
1655 * @param pThis The ISO maker instance.
1656 * @param pNamespace The namespace.
1657 * @param pDirName The new name space node for the directory.
1658 */
1659static int rtFsIsoMakerAddTransTblFileToNewDir(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
1660 PRTFSISOMAKERNAME pDirName)
1661{
1662 /*
1663 * Create a file object for it.
1664 */
1665 PRTFSISOMAKERFILE pFile;
1666 int rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, NULL, 0, &pFile);
1667 if (RT_SUCCESS(rc))
1668 {
1669 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_TRANS_TBL;
1670 pFile->u.pTransTblDir = pDirName;
1671 pDirName->pDir->pTransTblFile = pFile;
1672
1673 /*
1674 * Add it to the directory.
1675 */
1676 PRTFSISOMAKERNAME pTransTblNm;
1677 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pFile->Core, pDirName,
1678 pNamespace->pszTransTbl, strlen(pNamespace->pszTransTbl), &pTransTblNm);
1679 if (RT_SUCCESS(rc))
1680 {
1681 pTransTblNm->cchTransNm = 0;
1682 return VINF_SUCCESS;
1683 }
1684
1685 /*
1686 * Bail.
1687 */
1688 pDirName->pDir->pTransTblFile = NULL;
1689 rtFsIsoMakerObjRemoveWorker(pThis, &pFile->Core);
1690 }
1691 return rc;
1692}
1693
1694
1695/**
1696 * Sets the name of an object in a namespace.
1697 *
1698 * If the object is already named in the name space, it will first be removed
1699 * from that namespace. Should we run out of memory or into normalization
1700 * issues after removing it, its original state will _not_ be restored.
1701 *
1702 * @returns IPRT status code.
1703 * @param pThis The ISO maker instance.
1704 * @param pNamespace The namespace.
1705 * @param pObj The object to name.
1706 * @param pParent The parent namespace entry
1707 * @param pchSpec The specified name (not necessarily terminated).
1708 * @param cchSpec The specified name length.
1709 * @param ppNewName Where to return the name entry. Optional.
1710 */
1711static int rtFsIsoMakerObjSetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj,
1712 PRTFSISOMAKERNAME pParent, const char *pchSpec, size_t cchSpec, PPRTFSISOMAKERNAME ppNewName)
1713{
1714 Assert(cchSpec < _32K);
1715
1716 /*
1717 * If the object is already named, unset that name before continuing.
1718 */
1719 if (*rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace))
1720 {
1721 int rc = rtFsIsoMakerObjUnsetName(pThis, pNamespace, pObj);
1722 if (RT_FAILURE(rc))
1723 return rc;
1724 }
1725
1726 /*
1727 * To avoid need to revert anything, make sure papChildren in the parent is
1728 * large enough. If root object, make sure we haven't got a root already.
1729 */
1730 if (pParent)
1731 {
1732 AssertReturn(pParent->pDir, VERR_INTERNAL_ERROR_5);
1733 uint32_t cChildren = pParent->pDir->cChildren;
1734 if (cChildren & 31)
1735 { /* likely */ }
1736 else
1737 {
1738 AssertReturn(cChildren < RTFSISOMAKER_MAX_OBJECTS_PER_DIR, VERR_TOO_MUCH_DATA);
1739 void *pvNew = RTMemRealloc(pParent->pDir->papChildren, (cChildren + 32) * sizeof(pParent->pDir->papChildren[0]));
1740 AssertReturn(pvNew, VERR_NO_MEMORY);
1741 pParent->pDir->papChildren = (PPRTFSISOMAKERNAME)pvNew;
1742 }
1743 }
1744 else
1745 AssertReturn(pNamespace->pRoot == NULL, VERR_INTERNAL_ERROR_5);
1746
1747 /*
1748 * Normalize the name for this namespace.
1749 */
1750 size_t cchName;
1751 size_t cbNameInDirRec;
1752 char szName[RTFSISOMAKER_MAX_NAME_BUF];
1753 int rc = rtFsIsoMakerNormalizeNameForNamespace(pThis, pNamespace, pParent, pchSpec, cchSpec,
1754 pObj->enmType == RTFSISOMAKEROBJTYPE_DIR,
1755 szName, sizeof(szName), &cchName, &cbNameInDirRec);
1756 if (RT_SUCCESS(rc))
1757 {
1758 size_t cbName = sizeof(RTFSISOMAKERNAME)
1759 + cchName + 1
1760 + cchSpec + 1;
1761 if (pObj->enmType == RTFSISOMAKEROBJTYPE_DIR)
1762 cbName = RT_ALIGN_Z(cbName, 8) + sizeof(RTFSISOMAKERNAMEDIR);
1763 PRTFSISOMAKERNAME pName = (PRTFSISOMAKERNAME)RTMemAllocZ(cbName);
1764 if (pName)
1765 {
1766 pName->pObj = pObj;
1767 pName->pParent = pParent;
1768 pName->cbNameInDirRec = (uint16_t)cbNameInDirRec;
1769 pName->cchName = (uint16_t)cchName;
1770
1771 char *pszDst = &pName->szName[cchName + 1];
1772 memcpy(pszDst, pchSpec, cchSpec);
1773 pszDst[cchSpec] = '\0';
1774 pName->pszSpecNm = pszDst;
1775 pName->pszRockRidgeNm = pszDst;
1776 pName->pszTransNm = pszDst;
1777 pName->cchSpecNm = (uint16_t)cchSpec;
1778 pName->cchRockRidgeNm = (uint16_t)cchSpec;
1779 pName->cchTransNm = (uint16_t)cchSpec;
1780 pName->uDepth = pParent->uDepth + 1;
1781 pName->fRockRidgeNmAlloced = false;
1782 pName->fTransNmAlloced = false;
1783
1784 pName->fMode = pObj->fMode;
1785 pName->uid = pObj->uid;
1786 pName->gid = pObj->gid;
1787 pName->Device = 0;
1788 pName->INode = pObj->idxObj;
1789 pName->cHardlinks = 1;
1790 pName->offDirRec = UINT32_MAX;
1791 pName->cbDirRec = 0;
1792
1793 memcpy(pName->szName, szName, cchName);
1794 pName->szName[cchName] = '\0';
1795
1796 if (pObj->enmType != RTFSISOMAKEROBJTYPE_DIR)
1797 pName->pDir = NULL;
1798 else
1799 {
1800 size_t offDir = RT_UOFFSETOF(RTFSISOMAKERNAME, szName) + cchName + 1 + cchSpec + 1;
1801 offDir = RT_ALIGN_Z(offDir, 8);
1802 PRTFSISOMAKERNAMEDIR pDir = (PRTFSISOMAKERNAMEDIR)((uintptr_t)pName + offDir);
1803 pDir->offDir = UINT64_MAX;
1804 pDir->cbDir = 0;
1805 pDir->cChildren = 0;
1806 pDir->papChildren = NULL;
1807 pDir->pTransTblFile = NULL;
1808 pDir->pName = pName;
1809 pDir->offPathTable = UINT32_MAX;
1810 pDir->idPathTable = UINT16_MAX;
1811 pDir->cbDirRec00 = 0;
1812 pDir->cbDirRec01 = 0;
1813 RTListInit(&pDir->FinalizedEntry);
1814 pName->pDir = pDir;
1815
1816 /* Create the TRANS.TBL file object and enter it into this directory as the first entry. */
1817 if (pNamespace->pszTransTbl)
1818 {
1819 rc = rtFsIsoMakerAddTransTblFileToNewDir(pThis, pNamespace, pName);
1820 if (RT_FAILURE(rc))
1821 {
1822 RTMemFree(pName);
1823 return rc;
1824 }
1825 }
1826 }
1827
1828 /*
1829 * Do the linking and stats. We practice insertion sorting.
1830 */
1831 if (pParent)
1832 {
1833 uint32_t idxName = rtFsIsoMakerFindInsertIndex(pNamespace, pParent, pName->szName);
1834 uint32_t cChildren = pParent->pDir->cChildren;
1835 if (idxName < cChildren)
1836 memmove(&pParent->pDir->papChildren[idxName + 1], &pParent->pDir->papChildren[idxName],
1837 (cChildren - idxName) * sizeof(pParent->pDir->papChildren[0]));
1838 pParent->pDir->papChildren[idxName] = pName;
1839 pParent->pDir->cChildren++;
1840 }
1841 else
1842 pNamespace->pRoot = pName;
1843 *rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace) = pName;
1844 pNamespace->cNames++;
1845
1846 /*
1847 * Done.
1848 */
1849 if (ppNewName)
1850 *ppNewName = pName;
1851 return VINF_SUCCESS;
1852 }
1853 }
1854 return rc;
1855}
1856
1857
1858/**
1859 * Walks the path up to the parent, creating missing directories as needed.
1860 *
1861 * As usual, we walk the specified names rather than the mangled ones.
1862 *
1863 * @returns IPRT status code.
1864 * @param pThis The ISO maker instance.
1865 * @param pNamespace The namespace to walk.
1866 * @param pszPath The path to walk.
1867 * @param ppParent Where to return the pointer to the parent
1868 * namespace node.
1869 * @param ppszEntry Where to return the pointer to the final name component.
1870 * @param pcchEntry Where to return the length of the final name component.
1871 */
1872static int rtFsIsoMakerCreatePathToParent(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, const char *pszPath,
1873 PPRTFSISOMAKERNAME ppParent, const char **ppszEntry, size_t *pcchEntry)
1874{
1875 int rc;
1876 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INTERNAL_ERROR_4);
1877
1878 /*
1879 * Deal with the special case of the root.
1880 */
1881 while (RTPATH_IS_SLASH(*pszPath))
1882 pszPath++;
1883 AssertReturn(*pszPath, VERR_INTERNAL_ERROR_4); /* We should not be called on a root path. */
1884
1885 PRTFSISOMAKERNAME pParent = pNamespace->pRoot;
1886 if (!pParent)
1887 {
1888 PRTFSISOMAKERDIR pDir;
1889 if (pThis->cObjects == 0)
1890 {
1891 rc = rtFsIsoMakerAddUnnamedDirWorker(pThis, &pDir);
1892 AssertRCReturn(rc, rc);
1893 }
1894 else
1895 {
1896 pDir = RTListGetFirst(&pThis->ObjectHead, RTFSISOMAKERDIR, Core.Entry);
1897#ifdef RT_STRICT
1898 Assert(pDir);
1899 Assert(pDir->Core.idxObj == 0);
1900 Assert(pDir->Core.enmType == RTFSISOMAKEROBJTYPE_DIR);
1901 Assert(*rtFsIsoMakerObjGetNameForNamespace(&pDir->Core, pNamespace) == NULL);
1902#endif
1903 }
1904
1905 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pDir->Core, NULL /*pParent*/, "", 0, &pParent);
1906 AssertRCReturn(rc, rc);
1907 pParent = pNamespace->pRoot;
1908 AssertReturn(pParent, VERR_INTERNAL_ERROR_4);
1909 }
1910
1911 /*
1912 * Now, do the rest of the path.
1913 */
1914 for (;;)
1915 {
1916 /*
1917 * Find the end of the component and see if its the final one or not.
1918 */
1919 char ch;
1920 size_t cchComponent = 0;
1921 while ((ch = pszPath[cchComponent]) != '\0' && RTPATH_IS_SLASH(ch))
1922 cchComponent++;
1923 AssertReturn(cchComponent > 0, VERR_INTERNAL_ERROR_4);
1924
1925 size_t offNext = cchComponent;
1926 while (RTPATH_IS_SLASH(ch))
1927 ch = pszPath[offNext++];
1928
1929 if (ch == '\0')
1930 {
1931 /*
1932 * Final component. Make sure it is not dot or dot-dot before returning.
1933 */
1934 AssertReturn( pszPath[0] != '.'
1935 || cchComponent > 2
1936 || ( cchComponent == 2
1937 && pszPath[1] != '.'),
1938 VERR_INVALID_NAME);
1939
1940 *ppParent = pParent;
1941 *ppszEntry = pszPath;
1942 *pcchEntry = cchComponent;
1943 return VINF_SUCCESS;
1944 }
1945
1946 /*
1947 * Deal with dot and dot-dot.
1948 */
1949 if (cchComponent == 1 && pszPath[0] == '.')
1950 { /* nothing to do */ }
1951 else if (cchComponent == 2 && pszPath[0] == '.' && pszPath[1] == '.')
1952 {
1953 if (pParent->pParent)
1954 pParent = pParent->pParent;
1955 }
1956 /*
1957 * Look it up.
1958 */
1959 else
1960 {
1961 PRTFSISOMAKERNAME pChild = rtFsIsoMakerFindEntryInDirBySpec(pParent, pszPath, cchComponent);
1962 if (pChild)
1963 {
1964 if (pChild->pDir)
1965 pParent = pChild;
1966 else
1967 return VERR_NOT_A_DIRECTORY;
1968 }
1969 else
1970 {
1971 /* Try see if we've got a directory with the same spec name in a different namespace.
1972 (We don't want to waste heap by creating a directory instance per namespace.) */
1973 PRTFSISOMAKERDIR pChildObj = rtFsIsoMakerFindSubdirBySpec((PRTFSISOMAKERDIR)pParent->pObj,
1974 pszPath, cchComponent, pNamespace->fNamespace);
1975 if (pChildObj)
1976 {
1977 PPRTFSISOMAKERNAME ppChildName = rtFsIsoMakerObjGetNameForNamespace(&pChildObj->Core, pNamespace);
1978 if (!*ppChildName)
1979 {
1980 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pChildObj->Core, pParent, pszPath, cchComponent, &pChild);
1981 if (RT_FAILURE(rc))
1982 return rc;
1983 AssertReturn(pChild != NULL, VERR_INTERNAL_ERROR_4);
1984 }
1985 }
1986 /* If we didn't have luck in other namespaces, create a new directory. */
1987 if (!pChild)
1988 {
1989 rc = rtFsIsoMakerAddUnnamedDirWorker(pThis, &pChildObj);
1990 if (RT_SUCCESS(rc))
1991 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pChildObj->Core, pParent, pszPath, cchComponent, &pChild);
1992 if (RT_FAILURE(rc))
1993 return rc;
1994 AssertReturn(pChild != NULL, VERR_INTERNAL_ERROR_4);
1995 }
1996 }
1997 }
1998
1999 /*
2000 * Skip ahead in the path.
2001 */
2002 pszPath += offNext;
2003 }
2004}
2005
2006
2007/**
2008 * Worker for RTFsIsoMakerObjSetPath that operates on a single namespace.
2009 *
2010 * @returns IPRT status code.
2011 * @param pThis The ISO maker instance.
2012 * @param pNamespace The namespace to name it in.
2013 * @param pObj The filesystem object to name.
2014 * @param pszPath The path to the entry in the namespace.
2015 */
2016static int rtFsIsoMakerObjSetPathInOne(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
2017 PRTFSISOMAKEROBJ pObj, const char *pszPath)
2018{
2019 AssertReturn(*rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace) == NULL, VERR_WRONG_ORDER);
2020 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INTERNAL_ERROR_5);
2021
2022 /*
2023 * Figure out where the parent is.
2024 * This will create missing parent name space entries and directory nodes.
2025 */
2026 PRTFSISOMAKERNAME pParent;
2027 const char *pszEntry;
2028 size_t cchEntry;
2029 int rc;
2030 if (pszPath[1] != '\0')
2031 rc = rtFsIsoMakerCreatePathToParent(pThis, pNamespace, pszPath, &pParent, &pszEntry, &cchEntry);
2032 else
2033 {
2034 /*
2035 * Special case for the root directory.
2036 */
2037 Assert(pObj->enmType == RTFSISOMAKEROBJTYPE_DIR);
2038 AssertReturn(pNamespace->pRoot == NULL, VERR_WRONG_ORDER);
2039 pszEntry = "/";
2040 cchEntry = 0;
2041 pParent = NULL;
2042 rc = VINF_SUCCESS;
2043 }
2044
2045 /*
2046 * Do the job on the final path component.
2047 */
2048 if (RT_SUCCESS(rc))
2049 {
2050 AssertReturn(!RTPATH_IS_SLASH(pszEntry[cchEntry]) || pObj->enmType == RTFSISOMAKEROBJTYPE_DIR,
2051 VERR_NOT_A_DIRECTORY);
2052 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, pObj, pParent, pszEntry, cchEntry, NULL);
2053 }
2054 return rc;
2055}
2056
2057
2058/**
2059 * Removes an object from the given namespace.
2060 *
2061 * @returns IPRT status code.
2062 * @param pThis The ISO maker instance.
2063 * @param pNamespace The namespace.
2064 * @param pObj The object to name.
2065 */
2066static int rtFsIsoMakerObjUnsetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj)
2067{
2068 /*
2069 * First check if there is anything to do here at all.
2070 */
2071 PPRTFSISOMAKERNAME ppName = rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace);
2072 PRTFSISOMAKERNAME pName = *ppName;
2073 if (!pName)
2074 return VINF_SUCCESS;
2075
2076 /*
2077 * We don't support this on the root.
2078 */
2079 AssertReturn(pName->pParent, VERR_ACCESS_DENIED);
2080
2081 /*
2082 * If this is a directory, we're in for some real fun here as we need to
2083 * unset the names of all the children too.
2084 */
2085 PRTFSISOMAKERNAMEDIR pDir = pName->pDir;
2086 if (pDir)
2087 {
2088 uint32_t iChild = pDir->cChildren;
2089 while (iChild-- > 0)
2090 {
2091 int rc = rtFsIsoMakerObjUnsetName(pThis, pNamespace, pDir->papChildren[iChild]->pObj);
2092 if (RT_FAILURE(rc))
2093 return rc;
2094 }
2095 AssertReturn(pDir->cChildren == 0, VERR_DIR_NOT_EMPTY);
2096 }
2097
2098 /*
2099 * Unlink the pName from the parent.
2100 */
2101 pDir = pName->pParent->pDir;
2102 uint32_t iChild = pDir->cChildren;
2103 while (iChild-- > 0)
2104 if (pDir->papChildren[iChild] == pName)
2105 {
2106 uint32_t cToMove = pDir->cChildren - iChild - 1;
2107 if (cToMove > 0)
2108 memmove(&pDir->papChildren[iChild], &pDir->papChildren[iChild + 1], cToMove * sizeof(pDir->papChildren[0]));
2109 pDir->cChildren--;
2110 pNamespace->cNames--;
2111
2112 /*
2113 * NULL the name member in the object and free the structure.
2114 */
2115 *ppName = NULL;
2116 RTMemFree(pName);
2117
2118 return VINF_SUCCESS;
2119 }
2120
2121 /* Not found. This can't happen. */
2122 AssertFailed();
2123 return VERR_INTERNAL_ERROR_2;
2124}
2125
2126
2127
2128
2129
2130
2131/*
2132 *
2133 * Object level config
2134 * Object level config
2135 * Object level config
2136 *
2137 */
2138
2139
2140/**
2141 * Translates an object index number to an object pointer, slow path.
2142 *
2143 * @returns Pointer to object, NULL if not found.
2144 * @param pThis The ISO maker instance.
2145 * @param idxObj The object index too resolve.
2146 */
2147DECL_NO_INLINE(static, PRTFSISOMAKEROBJ) rtFsIsoMakerIndexToObjSlow(PRTFSISOMAKERINT pThis, uint32_t idxObj)
2148{
2149 PRTFSISOMAKEROBJ pObj;
2150 RTListForEachReverse(&pThis->ObjectHead, pObj, RTFSISOMAKEROBJ, Entry)
2151 {
2152 if (pObj->idxObj == idxObj)
2153 return pObj;
2154 }
2155 return NULL;
2156}
2157
2158
2159/**
2160 * Translates an object index number to an object pointer.
2161 *
2162 * @returns Pointer to object, NULL if not found.
2163 * @param pThis The ISO maker instance.
2164 * @param idxObj The object index too resolve.
2165 */
2166DECLINLINE(PRTFSISOMAKEROBJ) rtFsIsoMakerIndexToObj(PRTFSISOMAKERINT pThis, uint32_t idxObj)
2167{
2168 PRTFSISOMAKEROBJ pObj = RTListGetLast(&pThis->ObjectHead, RTFSISOMAKEROBJ, Entry);
2169 if (!pObj || RT_LIKELY(pObj->idxObj == idxObj))
2170 return pObj;
2171 return rtFsIsoMakerIndexToObjSlow(pThis, idxObj);
2172}
2173
2174
2175/**
2176 * Resolves a path into a object ID.
2177 *
2178 * This will be doing the looking up using the specified object names rather
2179 * than the version adjusted and mangled according to the namespace setup.
2180 *
2181 * @returns The object ID corresponding to @a pszPath, or UINT32_MAX if not
2182 * found or invalid parameters.
2183 * @param hIsoMaker The ISO maker instance.
2184 * @param fNamespaces The namespace to resolve @a pszPath in. It's
2185 * possible to specify multiple namespaces here, of
2186 * course, but that's inefficient.
2187 * @param pszPath The path to the object.
2188 */
2189RTDECL(uint32_t) RTFsIsoMakerGetObjIdxForPath(RTFSISOMAKER hIsoMaker, uint32_t fNamespaces, const char *pszPath)
2190{
2191 /*
2192 * Validate input.
2193 */
2194 PRTFSISOMAKERINT pThis = hIsoMaker;
2195 RTFSISOMAKER_ASSER_VALID_HANDLE_RET_EX(pThis, UINT32_MAX);
2196
2197 /*
2198 * Do the searching.
2199 */
2200 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2201 if (fNamespaces & g_aRTFsIsoNamespaces[i].fNamespace)
2202 {
2203 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2204 if (pNamespace->pRoot)
2205 {
2206 PRTFSISOMAKERNAME pName;
2207 int rc = rtFsIsoMakerWalkPathBySpec(pNamespace, pszPath, &pName);
2208 if (RT_SUCCESS(rc))
2209 return pName->pObj->idxObj;
2210 }
2211 }
2212
2213 return UINT32_MAX;
2214}
2215
2216
2217/**
2218 * Removes the specified object from the image.
2219 *
2220 * This is a worker for RTFsIsoMakerObjRemove and
2221 * rtFsIsoMakerFinalizeRemoveOrphans.
2222 *
2223 * @returns IPRT status code.
2224 * @param hIsoMaker The ISO maker instance.
2225 * @param pObj The object to remove from the image.
2226 */
2227static int rtFsIsoMakerObjRemoveWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKEROBJ pObj)
2228{
2229 /*
2230 * Remove the object from all name spaces.
2231 */
2232 int rc = VINF_SUCCESS;
2233 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2234 {
2235 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2236 int rc2 = rtFsIsoMakerObjUnsetName(pThis, pNamespace, pObj);
2237 if (RT_SUCCESS(rc2) || RT_FAILURE(rc))
2238 continue;
2239 rc = rc2;
2240 }
2241
2242 /*
2243 * If that succeeded, remove the object itself.
2244 */
2245 if (RT_SUCCESS(rc))
2246 {
2247 RTListNodeRemove(&pObj->Entry);
2248 if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
2249 {
2250 uint64_t cbData = ((PRTFSISOMAKERFILE)pObj)->cbData;
2251 pThis->cbData -= RT_ALIGN_64(cbData, RTFSISOMAKER_SECTOR_SIZE);
2252 }
2253 pThis->cObjects--;
2254 rtFsIsoMakerObjDestroy(pObj);
2255 }
2256 return rc;
2257}
2258
2259
2260/**
2261 * Removes the specified object from the image.
2262 *
2263 * @returns IPRT status code.
2264 * @param hIsoMaker The ISO maker instance.
2265 * @param idxObj The index of the object to remove.
2266 */
2267RTDECL(int) RTFsIsoMakerObjRemove(RTFSISOMAKER hIsoMaker, uint32_t idxObj)
2268{
2269 /*
2270 * Validate and translate input.
2271 */
2272 PRTFSISOMAKERINT pThis = hIsoMaker;
2273 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2274 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2275 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2276 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2277
2278 /*
2279 * Call worker.
2280 */
2281 return rtFsIsoMakerObjRemoveWorker(pThis, pObj);
2282}
2283
2284
2285/**
2286 * Sets the path (name) of an object in the selected namespaces.
2287 *
2288 * The name will be transformed as necessary.
2289 *
2290 * The initial implementation does not allow this function to be called more
2291 * than once on an object.
2292 *
2293 * @returns IPRT status code.
2294 * @param hIsoMaker The ISO maker handle.
2295 * @param idxObj The configuration index of to name.
2296 * @param fNamespaces The namespaces to apply the path to
2297 * (RTFSISOMAKER_NAMESPACE_XXX).
2298 * @param pszPath The path.
2299 */
2300RTDECL(int) RTFsIsoMakerObjSetPath(RTFSISOMAKER hIsoMaker, uint32_t idxObj, uint32_t fNamespaces, const char *pszPath)
2301{
2302 /*
2303 * Validate and translate input.
2304 */
2305 PRTFSISOMAKERINT pThis = hIsoMaker;
2306 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2307 AssertReturn(!(fNamespaces & ~RTFSISOMAKER_NAMESPACE_VALID_MASK), VERR_INVALID_FLAGS);
2308 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
2309 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INVALID_NAME);
2310 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2311 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2312 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2313
2314 /*
2315 * Execute requested actions.
2316 */
2317 int rc = VINF_SUCCESS;
2318 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2319 if (fNamespaces & g_aRTFsIsoNamespaces[i].fNamespace)
2320 {
2321 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2322 if (pNamespace->uLevel > 0)
2323 {
2324 int rc2 = rtFsIsoMakerObjSetPathInOne(pThis, pNamespace, pObj, pszPath);
2325 if (RT_SUCCESS(rc2) || RT_FAILURE(rc))
2326 continue;
2327 rc = rc2;
2328 }
2329 }
2330 return rc;
2331}
2332
2333
2334/**
2335 * Sets the name of an object in the selected namespaces, placing it under the
2336 * given directory.
2337 *
2338 * The name will be transformed as necessary.
2339 *
2340 * @returns IPRT status code.
2341 * @param hIsoMaker The ISO maker handle.
2342 * @param idxObj The configuration index of to name.
2343 * @param idxParentObj The parent directory object.
2344 * @param fNamespaces The namespaces to apply the path to
2345 * (RTFSISOMAKER_NAMESPACE_XXX).
2346 * @param pszName The name.
2347 */
2348RTDECL(int) RTFsIsoMakerObjSetNameAndParent(RTFSISOMAKER hIsoMaker, uint32_t idxObj, uint32_t idxParentObj,
2349 uint32_t fNamespaces, const char *pszName)
2350{
2351 /*
2352 * Validate and translate input.
2353 */
2354 PRTFSISOMAKERINT pThis = hIsoMaker;
2355 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2356 AssertReturn(!(fNamespaces & ~RTFSISOMAKER_NAMESPACE_VALID_MASK), VERR_INVALID_FLAGS);
2357 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
2358 size_t cchName = strlen(pszName);
2359 AssertReturn(cchName > 0, VERR_INVALID_NAME);
2360 AssertReturn(memchr(pszName, '/', cchName) == NULL, VERR_INVALID_NAME);
2361 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2362 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2363 PRTFSISOMAKEROBJ pParentObj = rtFsIsoMakerIndexToObj(pThis, idxParentObj);
2364 AssertReturn(pParentObj, VERR_OUT_OF_RANGE);
2365 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2366
2367 /*
2368 * Execute requested actions.
2369 */
2370 int rc = VINF_SUCCESS;
2371 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2372 if (fNamespaces & g_aRTFsIsoNamespaces[i].fNamespace)
2373 {
2374 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2375 if (pNamespace->uLevel > 0)
2376 {
2377 PRTFSISOMAKERNAME pParentName = *rtFsIsoMakerObjGetNameForNamespace(pParentObj, pNamespace);
2378 if (pParentName)
2379 {
2380 int rc2 = rtFsIsoMakerObjSetName(pThis, pNamespace, pObj, pParentName, pszName, cchName, NULL /*ppNewName*/);
2381 if (RT_SUCCESS(rc2) || RT_FAILURE(rc))
2382 continue;
2383 rc = rc2;
2384 }
2385 }
2386 }
2387 return rc;
2388}
2389
2390
2391/**
2392 * Initalizes the common part of a file system object and links it into global
2393 * chain.
2394 *
2395 * @returns IPRT status code
2396 * @param pThis The ISO maker instance.
2397 * @param pObj The common object.
2398 * @param enmType The object type.
2399 * @param pObjInfo The object information (typically source).
2400 * Optional.
2401 */
2402static int rtFsIsoMakerInitCommonObj(PRTFSISOMAKERINT pThis, PRTFSISOMAKEROBJ pObj,
2403 RTFSISOMAKEROBJTYPE enmType, PCRTFSOBJINFO pObjInfo)
2404{
2405 Assert(!pThis->fFinalized);
2406 AssertReturn(pThis->cObjects < RTFSISOMAKER_MAX_OBJECTS, VERR_OUT_OF_RANGE);
2407
2408 pObj->enmType = enmType;
2409 pObj->pPrimaryName = NULL;
2410 pObj->pJolietName = NULL;
2411 pObj->pUdfName = NULL;
2412 pObj->pHfsName = NULL;
2413 pObj->idxObj = pThis->cObjects++;
2414 pObj->fNotOrphan = false;
2415 if (pObjInfo)
2416 {
2417 pObj->BirthTime = pObjInfo->BirthTime;
2418 pObj->ChangeTime = pObjInfo->ChangeTime;
2419 pObj->ModificationTime = pObjInfo->ModificationTime;
2420 pObj->AccessedTime = pObjInfo->AccessTime;
2421 pObj->fMode = pObjInfo->Attr.fMode;
2422 pObj->uid = pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : pThis->uidDefault;
2423 pObj->gid = pObjInfo->Attr.u.Unix.gid != NIL_RTGID ? pObjInfo->Attr.u.Unix.gid : pThis->gidDefault;
2424 }
2425 else
2426 {
2427 pObj->BirthTime = pThis->ImageCreationTime;
2428 pObj->ChangeTime = pThis->ImageCreationTime;
2429 pObj->ModificationTime = pThis->ImageCreationTime;
2430 pObj->AccessedTime = pThis->ImageCreationTime;
2431 pObj->fMode = enmType == RTFSISOMAKEROBJTYPE_DIR ? pThis->fDefaultDirMode : pThis->fDefaultFileMode;
2432 pObj->uid = pThis->uidDefault;
2433 pObj->gid = pThis->gidDefault;
2434 }
2435
2436 RTListAppend(&pThis->ObjectHead, &pObj->Entry);
2437 return VINF_SUCCESS;
2438}
2439
2440
2441/**
2442 * Internal function for adding an unnamed directory.
2443 *
2444 * @returns IPRT status code.
2445 * @param pThis The ISO make instance.
2446 * @param ppDir Where to return the directory.
2447 */
2448static int rtFsIsoMakerAddUnnamedDirWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKERDIR *ppDir)
2449{
2450 PRTFSISOMAKERDIR pDir = (PRTFSISOMAKERDIR)RTMemAllocZ(sizeof(*pDir));
2451 AssertReturn(pDir, VERR_NO_MEMORY);
2452 int rc = rtFsIsoMakerInitCommonObj(pThis, &pDir->Core, RTFSISOMAKEROBJTYPE_DIR, NULL);
2453 if (RT_SUCCESS(rc))
2454 {
2455 *ppDir = pDir;
2456 return VINF_SUCCESS;
2457 }
2458 RTMemFree(pDir);
2459 return rc;
2460
2461}
2462
2463
2464/**
2465 * Adds an unnamed directory to the image.
2466 *
2467 * The directory must explictly be entered into the desired namespaces.
2468 *
2469 * @returns IPRT status code
2470 * @param hIsoMaker The ISO maker handle.
2471 * @param pidxObj Where to return the configuration index of the
2472 * directory.
2473 * @sa RTFsIsoMakerAddDir, RTFsIsoMakerObjSetPath
2474 */
2475RTDECL(int) RTFsIsoMakerAddUnnamedDir(RTFSISOMAKER hIsoMaker, uint32_t *pidxObj)
2476{
2477 PRTFSISOMAKERINT pThis = hIsoMaker;
2478 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2479 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2480 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2481
2482 PRTFSISOMAKERDIR pDir;
2483 int rc = rtFsIsoMakerAddUnnamedDirWorker(pThis, &pDir);
2484 *pidxObj = RT_SUCCESS(rc) ? pDir->Core.idxObj : UINT32_MAX;
2485 return rc;
2486}
2487
2488
2489/**
2490 * Adds a directory to the image in all namespaces and default attributes.
2491 *
2492 * @returns IPRT status code
2493 * @param hIsoMaker The ISO maker handle.
2494 * @param pszDir The path (UTF-8) to the directory in the ISO.
2495 *
2496 * @param pidxObj Where to return the configuration index of the
2497 * directory. Optional.
2498 * @sa RTFsIsoMakerAddUnnamedDir, RTFsIsoMakerObjSetPath
2499 */
2500RTDECL(int) RTFsIsoMakerAddDir(RTFSISOMAKER hIsoMaker, const char *pszDir, uint32_t *pidxObj)
2501{
2502 PRTFSISOMAKERINT pThis = hIsoMaker;
2503 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2504 AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
2505 AssertReturn(RTPATH_IS_SLASH(*pszDir), VERR_INVALID_NAME);
2506
2507 uint32_t idxObj;
2508 int rc = RTFsIsoMakerAddUnnamedDir(hIsoMaker, &idxObj);
2509 if (RT_SUCCESS(rc))
2510 {
2511 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszDir);
2512 if (RT_SUCCESS(rc))
2513 {
2514 if (pidxObj)
2515 *pidxObj = idxObj;
2516 }
2517 else
2518 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
2519 }
2520 return rc;
2521}
2522
2523
2524/**
2525 * Internal function for adding an unnamed file.
2526 *
2527 * @returns IPRT status code.
2528 * @param pThis The ISO make instance.
2529 * @param cbExtra Extra space for additional data (e.g. source
2530 * path string copy).
2531 * @param ppFile Where to return the file.
2532 */
2533static int rtFsIsoMakerAddUnnamedFileWorker(PRTFSISOMAKERINT pThis, PCRTFSOBJINFO pObjInfo, size_t cbExtra,
2534 PRTFSISOMAKERFILE *ppFile)
2535{
2536 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)RTMemAllocZ(sizeof(*pFile) + cbExtra);
2537 AssertReturn(pFile, VERR_NO_MEMORY);
2538 int rc = rtFsIsoMakerInitCommonObj(pThis, &pFile->Core, RTFSISOMAKEROBJTYPE_FILE, pObjInfo);
2539 if (RT_SUCCESS(rc))
2540 {
2541 pFile->cbData = pObjInfo->cbObject;
2542 pThis->cbData += RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE);
2543 pFile->offData = UINT64_MAX;
2544 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_INVALID;
2545 pFile->u.pszSrcPath = NULL;
2546 RTListInit(&pFile->FinalizedEntry);
2547
2548 *ppFile = pFile;
2549 return VINF_SUCCESS;
2550 }
2551 RTMemFree(pFile);
2552 return rc;
2553
2554}
2555
2556
2557/**
2558 * Adds an unnamed file to the image that's backed by a host file.
2559 *
2560 * The file must explictly be entered into the desired namespaces.
2561 *
2562 * @returns IPRT status code
2563 * @param hIsoMaker The ISO maker handle.
2564 * @param pszSrcFile The source file path. VFS chain spec allowed.
2565 * @param pidxObj Where to return the configuration index of the
2566 * directory.
2567 * @sa RTFsIsoMakerAddFile, RTFsIsoMakerObjSetPath
2568 */
2569RTDECL(int) RTFsIsoMakerAddUnnamedFileWithSrcPath(RTFSISOMAKER hIsoMaker, const char *pszSrcFile, uint32_t *pidxObj)
2570{
2571 PRTFSISOMAKERINT pThis = hIsoMaker;
2572 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2573 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2574 *pidxObj = UINT32_MAX;
2575 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2576
2577 /*
2578 * Check that the source file exists and is a file.
2579 */
2580 uint32_t offError = 0;
2581 RTFSOBJINFO ObjInfo;
2582 int rc = RTVfsChainQueryInfo(pszSrcFile, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK, &offError, NULL);
2583 AssertMsgRCReturn(rc, ("%s -> %Rrc offError=%u\n", pszSrcFile, rc, offError), rc);
2584 AssertMsgReturn(RTFS_IS_FILE(ObjInfo.Attr.fMode), ("%#x - %s\n", ObjInfo.Attr.fMode, pszSrcFile), VERR_NOT_A_FILE);
2585
2586 /*
2587 * Create a file object for it.
2588 */
2589 size_t const cbSrcFile = strlen(pszSrcFile) + 1;
2590 PRTFSISOMAKERFILE pFile;
2591 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, &ObjInfo, cbSrcFile, &pFile);
2592 if (RT_SUCCESS(rc))
2593 {
2594 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_PATH;
2595 pFile->u.pszSrcPath = (char *)memcpy(pFile + 1, pszSrcFile, cbSrcFile);
2596
2597 *pidxObj = pFile->Core.idxObj;
2598 }
2599 return rc;
2600}
2601
2602
2603/**
2604 * Adds an unnamed file to the image that's backed by a VFS file.
2605 *
2606 * The file must explictly be entered into the desired namespaces.
2607 *
2608 * @returns IPRT status code
2609 * @param hIsoMaker The ISO maker handle.
2610 * @param hVfsFileSrc The source file handle.
2611 * @param pidxObj Where to return the configuration index of the
2612 * directory.
2613 * @sa RTFsIsoMakerAddUnnamedFileWithSrcPath, RTFsIsoMakerObjSetPath
2614 */
2615RTDECL(int) RTFsIsoMakerAddUnnamedFileWithVfsFile(RTFSISOMAKER hIsoMaker, RTVFSFILE hVfsFileSrc, uint32_t *pidxObj)
2616{
2617 PRTFSISOMAKERINT pThis = hIsoMaker;
2618 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2619 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2620 *pidxObj = UINT32_MAX;
2621 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2622
2623 /*
2624 * Get the VFS file info. This implicitly validates the handle.
2625 */
2626 RTFSOBJINFO ObjInfo;
2627 int rc = RTVfsFileQueryInfo(hVfsFileSrc, &ObjInfo, RTFSOBJATTRADD_UNIX);
2628 AssertMsgRCReturn(rc, ("RTVfsFileQueryInfo(%p) -> %Rrc\n", hVfsFileSrc, rc), rc);
2629
2630 /*
2631 * Retain a reference to the file.
2632 */
2633 uint32_t cRefs = RTVfsFileRetain(hVfsFileSrc);
2634 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2635
2636 /*
2637 * Create a file object for it.
2638 */
2639 PRTFSISOMAKERFILE pFile;
2640 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, &ObjInfo, 0, &pFile);
2641 if (RT_SUCCESS(rc))
2642 {
2643 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_VFS_FILE;
2644 pFile->u.hVfsFile = hVfsFileSrc;
2645
2646 *pidxObj = pFile->Core.idxObj;
2647 }
2648 else
2649 RTVfsFileRelease(hVfsFileSrc);
2650 return rc;
2651}
2652
2653
2654/**
2655 * Adds a file that's backed by a host file to the image in all namespaces and
2656 * with attributes taken from the source file.
2657 *
2658 * @returns IPRT status code
2659 * @param hIsoMaker The ISO maker handle.
2660 * @param pszFile The path to the file in the image.
2661 * @param pszSrcFile The source file path. VFS chain spec allowed.
2662 * @param pidxObj Where to return the configuration index of the file.
2663 * Optional
2664 * @sa RTFsIsoMakerAddFileWithVfsFile,
2665 * RTFsIsoMakerAddUnnamedFileWithSrcPath
2666 */
2667RTDECL(int) RTFsIsoMakerAddFileWithSrcPath(RTFSISOMAKER hIsoMaker, const char *pszFile, const char *pszSrcFile, uint32_t *pidxObj)
2668{
2669 PRTFSISOMAKERINT pThis = hIsoMaker;
2670 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2671 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
2672 AssertReturn(RTPATH_IS_SLASH(*pszFile), VERR_INVALID_NAME);
2673
2674 uint32_t idxObj;
2675 int rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(hIsoMaker, pszSrcFile, &idxObj);
2676 if (RT_SUCCESS(rc))
2677 {
2678 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszFile);
2679 if (RT_SUCCESS(rc))
2680 {
2681 if (pidxObj)
2682 *pidxObj = idxObj;
2683 }
2684 else
2685 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
2686 }
2687 return rc;
2688}
2689
2690
2691/**
2692 * Adds a file that's backed by a VFS file to the image in all namespaces and
2693 * with attributes taken from the source file.
2694 *
2695 * @returns IPRT status code
2696 * @param hIsoMaker The ISO maker handle.
2697 * @param pszFile The path to the file in the image.
2698 * @param hVfsFileSrc The source file handle.
2699 * @param pidxObj Where to return the configuration index of the file.
2700 * Optional.
2701 * @sa RTFsIsoMakerAddUnnamedFileWithVfsFile,
2702 * RTFsIsoMakerAddFileWithSrcPath
2703 */
2704RTDECL(int) RTFsIsoMakerAddFileWithVfsFile(RTFSISOMAKER hIsoMaker, const char *pszFile, RTVFSFILE hVfsFileSrc, uint32_t *pidxObj)
2705{
2706 PRTFSISOMAKERINT pThis = hIsoMaker;
2707 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
2708 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
2709 AssertReturn(RTPATH_IS_SLASH(*pszFile), VERR_INVALID_NAME);
2710
2711 uint32_t idxObj;
2712 int rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(hIsoMaker, hVfsFileSrc, &idxObj);
2713 if (RT_SUCCESS(rc))
2714 {
2715 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszFile);
2716 if (RT_SUCCESS(rc))
2717 {
2718 if (pidxObj)
2719 *pidxObj = idxObj;
2720 }
2721 else
2722 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
2723 }
2724 return rc;
2725}
2726
2727
2728
2729
2730
2731/*
2732 *
2733 * Image finalization.
2734 * Image finalization.
2735 * Image finalization.
2736 *
2737 */
2738
2739
2740/**
2741 * Remove any orphaned object from the disk.
2742 *
2743 * @returns IPRT status code.
2744 * @param pThis The ISO maker instance.
2745 */
2746static int rtFsIsoMakerFinalizeRemoveOrphans(PRTFSISOMAKERINT pThis)
2747{
2748 for (;;)
2749 {
2750 uint32_t cRemoved = 0;
2751 PRTFSISOMAKEROBJ pCur;
2752 PRTFSISOMAKEROBJ pNext;
2753 RTListForEachSafe(&pThis->ObjectHead, pCur, pNext, RTFSISOMAKEROBJ, Entry)
2754 {
2755 if ( pCur->pPrimaryName
2756 || pCur->pJolietName
2757 || pCur->pUdfName
2758 || pCur->pHfsName
2759 || pCur->fNotOrphan)
2760 { /* likely */ }
2761 else
2762 {
2763 int rc = rtFsIsoMakerObjRemoveWorker(pThis, pCur);
2764 if (RT_SUCCESS(rc))
2765 cRemoved++;
2766 else
2767 return rc;
2768 }
2769 }
2770 if (!cRemoved)
2771 return VINF_SUCCESS;
2772 }
2773}
2774
2775
2776/**
2777 * Finalizes the El Torito boot stuff.
2778 *
2779 * This includes generating the boot catalog data and fixing the location of all
2780 * related image files.
2781 *
2782 * @returns IPRT status code.
2783 * @param pThis The ISO maker instance.
2784 * @param poffData The data offset (in/out).
2785 */
2786static int rtFsIsoMakerFinalizeBootStuff(PRTFSISOMAKERINT pThis, uint64_t *poffData)
2787{
2788 RT_NOREF(pThis, poffData);
2789 return VINF_SUCCESS;
2790}
2791
2792
2793/**
2794 * Gathers the dirs for an ISO-9660 namespace (e.g. primary or joliet).
2795 *
2796 * @param pNamespace The namespace.
2797 * @param pFinalizedDirs The finalized directory structure. The
2798 * FinalizedDirs will be worked here.
2799 */
2800static void rtFsIsoMakerFinalizeGatherDirs(PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs)
2801{
2802 RTListInit(&pFinalizedDirs->FinalizedDirs);
2803
2804 /*
2805 * Enter the root directory (if we got one).
2806 */
2807 if (!pNamespace->pRoot)
2808 return;
2809 PRTFSISOMAKERNAMEDIR pCurDir = pNamespace->pRoot->pDir;
2810 RTListAppend(&pFinalizedDirs->FinalizedDirs, &pCurDir->FinalizedEntry);
2811 do
2812 {
2813 /*
2814 * Scan pCurDir and add directories. We don't need to sort anything
2815 * here because the directory is already in path table compatible order.
2816 */
2817 uint32_t cLeft = pCurDir->cChildren;
2818 PPRTFSISOMAKERNAME ppChild = pCurDir->papChildren;
2819 while (cLeft-- > 0)
2820 {
2821 PRTFSISOMAKERNAME pChild = *ppChild++;
2822 if (pChild->pDir)
2823 RTListAppend(&pFinalizedDirs->FinalizedDirs, &pChild->pDir->FinalizedEntry);
2824 }
2825
2826 /*
2827 * Advance to the next directory.
2828 */
2829 pCurDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pCurDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
2830 } while (pCurDir);
2831}
2832
2833
2834/**
2835 * Finalizes a directory entry (i.e. namespace node).
2836 *
2837 * This calculates the directory record size.
2838 *
2839 * @returns IPRT status code.
2840 * @param pFinalizedDirs .
2841 * @param pName The directory entry to finalize.
2842 * @param offInDir The offset in the directory of this record.
2843 * @param uRootRockRidge This is the rock ridge level when
2844 * root, otherwise it's zero.
2845 */
2846static int rtFsIsoMakerFinalizeIsoDirectoryEntry(PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs, PRTFSISOMAKERNAME pName,
2847 uint32_t offInDir, uint8_t uRootRockRidge)
2848{
2849 /* Set directory and translation table offsets. (These are for
2850 helping generating data blocks later.) */
2851 pName->offDirRec = offInDir;
2852
2853 /* Calculate the minimal directory record size. */
2854 size_t cbDirRec = RT_UOFFSETOF(ISO9660DIRREC, achFileId) + pName->cbNameInDirRec + !(pName->cbNameInDirRec & 1);
2855 AssertReturn(cbDirRec <= UINT8_MAX, VERR_FILENAME_TOO_LONG);
2856 pName->cbDirRec = (uint8_t)cbDirRec;
2857
2858 /*
2859 * Calculate additional rock ridge stuff, if it doesn't all fit write it
2860 * to the spill file.
2861 */
2862 if (pFinalizedDirs->pRRSpillFile)
2863 {
2864 /** @todo rock ridge */
2865 RT_NOREF(uRootRockRidge);
2866 }
2867
2868 return VINF_SUCCESS;
2869}
2870
2871
2872/**
2873 * Finalizes either a primary and secondary ISO namespace.
2874 *
2875 * @returns IPRT status code
2876 * @param pThis The ISO maker instance.
2877 * @param pNamespace The namespace.
2878 * @param pFinalizedDirs The finalized directories structure for the
2879 * namespace.
2880 * @param poffData The data offset. We will allocate blocks for the
2881 * directories and the path tables.
2882 */
2883static int rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
2884 PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs, uint64_t *poffData)
2885{
2886 /* The directory data comes first, so take down it's offset. */
2887 pFinalizedDirs->offDirs = *poffData;
2888
2889 /*
2890 * Reset the rock ridge spill file (in case we allow finalizing more than once)
2891 * and create a new spill file if rock ridge is enabled. The directory entry
2892 * finalize function uses this as a clue that rock ridge is enabled.
2893 */
2894 if (pFinalizedDirs->pRRSpillFile)
2895 {
2896 rtFsIsoMakerObjRemoveWorker(pThis, &pFinalizedDirs->pRRSpillFile->Core);
2897 pFinalizedDirs->pRRSpillFile = NULL;
2898 }
2899 if (pNamespace->uRockRidgeLevel > 0)
2900 {
2901 /** @todo create rock ridge spill file to indicate rock ridge */
2902 }
2903
2904 int rc;
2905 uint16_t idPathTable = 0;
2906 uint32_t cbPathTable = 0;
2907 if (pNamespace->pRoot)
2908 {
2909 /*
2910 * Precalc the directory record size for the root directory.
2911 */
2912 rc = rtFsIsoMakerFinalizeIsoDirectoryEntry(pFinalizedDirs, pNamespace->pRoot, 0 /*offInDir*/, pNamespace->uRockRidgeLevel);
2913 AssertRCReturn(rc, rc);
2914
2915 /*
2916 * Work thru the directories.
2917 */
2918 PRTFSISOMAKERNAMEDIR pCurDir;
2919 RTListForEach(&pThis->PrimaryIsoDirs.FinalizedDirs, pCurDir, RTFSISOMAKERNAMEDIR, FinalizedEntry)
2920 {
2921 PRTFSISOMAKERNAME pCurName = pCurDir->pName;
2922 PRTFSISOMAKERNAME pParentName = pCurName->pParent ? pCurName->pParent : pCurName;
2923
2924 /* We don't do anything special for the special '.' and '..' directory
2925 entries, instead we use the directory entry in the parent directory
2926 with a 1 byte name (00 or 01). */
2927 Assert(pCurName->cbDirRec != 0);
2928 Assert(pParentName->cbDirRec != 0);
2929 pCurDir->cbDirRec00 = pCurName->cbDirRec - pCurName->cbNameInDirRec - !(pCurName->cbNameInDirRec & 1) + 1;
2930 pCurDir->cbDirRec01 = pParentName->cbDirRec - pParentName->cbNameInDirRec - !(pParentName->cbNameInDirRec & 1) + 1;
2931
2932 uint32_t offInDir = (uint32_t)pCurDir->cbDirRec00 + pCurDir->cbDirRec01;
2933
2934 /* Finalize the directory entries. */
2935 uint32_t cbTransTbl = 0;
2936 uint32_t cLeft = pCurDir->cChildren;
2937 PPRTFSISOMAKERNAME ppChild = pCurDir->papChildren;
2938 while (cLeft-- > 0)
2939 {
2940 PRTFSISOMAKERNAME pChild = *ppChild++;
2941 rc = rtFsIsoMakerFinalizeIsoDirectoryEntry(pFinalizedDirs, pChild, offInDir, 0 /*uRootRockRidge*/);
2942 AssertRCReturn(rc, rc);
2943
2944 offInDir += pChild->cbDirRec;
2945 if (pChild->cchTransNm)
2946 cbTransTbl += 2 /* type & space*/
2947 + RT_MAX(pChild->cchName, RTFSISOMAKER_TRANS_TBL_LEFT_PAD)
2948 + 1 /* tab */
2949 + pChild->cchTransNm
2950 + 1 /* newline */;
2951 }
2952
2953 /* Set the directory size and location, advancing the data offset. */
2954 pCurDir->cbDir = offInDir;
2955 pCurDir->offDir = *poffData;
2956 *poffData += RT_ALIGN_32(offInDir, RTFSISOMAKER_SECTOR_SIZE);
2957
2958 /* Set the translation table file size. */
2959 if (pCurDir->pTransTblFile)
2960 pCurDir->pTransTblFile->cbData = cbTransTbl;
2961
2962 /* Add to the path table size calculation. */
2963 pCurDir->offPathTable = cbPathTable;
2964 pCurDir->idPathTable = idPathTable++;
2965 cbPathTable += RTFSISOMAKER_CALC_PATHREC_SIZE(pCurName->cbNameInDirRec);
2966 }
2967 }
2968
2969 /*
2970 * Update the rock ridge spill file size.
2971 */
2972 if (pFinalizedDirs->pRRSpillFile)
2973 {
2974 rc = RTVfsFileGetSize(pFinalizedDirs->pRRSpillFile->u.hVfsFile, &pFinalizedDirs->pRRSpillFile->cbData);
2975 AssertRCReturn(rc, rc);
2976 pThis->cbData += pFinalizedDirs->pRRSpillFile->cbData;
2977 }
2978
2979 /*
2980 * Calculate the path table offsets and move past them.
2981 */
2982 pFinalizedDirs->cbPathTable = cbPathTable;
2983 pFinalizedDirs->offPathTableL = *poffData;
2984 *poffData += RT_ALIGN_64(cbPathTable, RTFSISOMAKER_SECTOR_SIZE);
2985
2986 pFinalizedDirs->offPathTableM = *poffData;
2987 *poffData += RT_ALIGN_64(cbPathTable, RTFSISOMAKER_SECTOR_SIZE);
2988
2989 return VINF_SUCCESS;
2990}
2991
2992
2993
2994/**
2995 * Finalizes directories and related stuff.
2996 *
2997 * This will not generate actual directory data, but calculate the size of it
2998 * once it's generated. Ditto for the path tables. The exception is the rock
2999 * ridge spill file, which will be generated in memory.
3000 *
3001 * @returns IPRT status code.
3002 * @param pThis The ISO maker instance.
3003 * @param poffData The data offset (in/out).
3004 */
3005static int rtFsIsoMakerFinalizeDirectories(PRTFSISOMAKERINT pThis, uint64_t *poffData)
3006{
3007 /*
3008 * Locate the directories, width first, inserting them in the finalized lists so
3009 * we can process them efficiently.
3010 */
3011 rtFsIsoMakerFinalizeGatherDirs(&pThis->PrimaryIso, &pThis->PrimaryIsoDirs);
3012 rtFsIsoMakerFinalizeGatherDirs(&pThis->Joliet, &pThis->JolietDirs);
3013
3014 /*
3015 * Process the primary ISO and joliet namespaces.
3016 */
3017 int rc = rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(pThis, &pThis->PrimaryIso, &pThis->PrimaryIsoDirs, poffData);
3018 if (RT_SUCCESS(rc))
3019 rc = rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(pThis, &pThis->Joliet, &pThis->JolietDirs, poffData);
3020 if (RT_SUCCESS(rc))
3021 {
3022 /*
3023 * Later: UDF, HFS.
3024 */
3025 }
3026 return rc;
3027}
3028
3029
3030/**
3031 * Finalizes data allocations.
3032 *
3033 * This will set the RTFSISOMAKERFILE::offData members.
3034 *
3035 * @returns IPRT status code.
3036 * @param pThis The ISO maker instance.
3037 * @param poffData The data offset (in/out).
3038 */
3039static int rtFsIsoMakerFinalizeData(PRTFSISOMAKERINT pThis, uint64_t *poffData)
3040{
3041 /*
3042 * We currently does not have any ordering prioritizing implemented, so we
3043 * just store files in the order they were added.
3044 */
3045 PRTFSISOMAKEROBJ pCur;
3046 RTListForEach(&pThis->ObjectHead, pCur, RTFSISOMAKEROBJ, Entry)
3047 {
3048 if (pCur->enmType == RTFSISOMAKEROBJTYPE_FILE)
3049 {
3050 PRTFSISOMAKERFILE pCurFile = (PRTFSISOMAKERFILE)pCur;
3051 if (pCurFile->offData == UINT64_MAX)
3052 {
3053 pCurFile->offData = *poffData;
3054 *poffData += RT_ALIGN_64(pCurFile->cbData, RTFSISOMAKER_SECTOR_SIZE);
3055 RTListAppend(&pThis->FinalizedFiles, &pCurFile->FinalizedEntry);
3056 }
3057 }
3058 }
3059
3060 return VINF_SUCCESS;
3061}
3062
3063
3064/**
3065 * Copies the given string as UTF-16 and pad unused space in the destination
3066 * with spaces.
3067 *
3068 * @param pachDst The destination field. C type is char, but real life
3069 * type is UTF-16 / UCS-2.
3070 * @param cchDst The size of the destination field.
3071 * @param pszSrc The source string. NULL is treated like empty string.
3072 */
3073static void rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(char *pachDst, size_t cchDst, const char *pszSrc)
3074{
3075 size_t const cwcDst = cchDst / sizeof(RTUTF16);
3076 size_t cwcSrc = 0;
3077 if (pszSrc)
3078 {
3079 RTUTF16 wszSrc[256];
3080 PRTUTF16 pwszSrc = wszSrc;
3081 int rc = RTStrToUtf16Ex(pszSrc, RTSTR_MAX, &pwszSrc, RT_ELEMENTS(wszSrc), &cwcSrc);
3082 AssertRCStmt(rc, cwcSrc = 0);
3083
3084 if (cwcSrc > cchDst / sizeof(RTUTF16))
3085 cwcSrc = cchDst / sizeof(RTUTF16);
3086 memcpy(pachDst, wszSrc, cwcSrc * sizeof(RTUTF16));
3087 }
3088 if (cwcSrc < cwcDst)
3089 {
3090 PRTUTF16 pwcDst = (PRTUTF16)pachDst;
3091 pwcDst += cwcSrc;
3092 while (cwcSrc < cwcDst)
3093 {
3094 *pwcDst++ = ' ';
3095 cwcSrc++;
3096 }
3097 }
3098}
3099
3100
3101/**
3102 * Copies the given string and pad unused space in the destination with spaces.
3103 *
3104 * @param pachDst The destination field.
3105 * @param cchDst The size of the destination field.
3106 * @param pszSrc The source string. NULL is treated like empty string.
3107 */
3108static void rtFsIsoMakerFinalizeCopyAndSpacePad(char *pachDst, size_t cchDst, const char *pszSrc)
3109{
3110 size_t cchSrc;
3111 if (!pszSrc)
3112 cchSrc = 0;
3113 else
3114 {
3115 cchSrc = strlen(pszSrc);
3116 if (cchSrc > cchDst)
3117 cchSrc = cchDst;
3118 memcpy(pachDst, pszSrc, cchSrc);
3119 }
3120 if (cchSrc < cchDst)
3121 memset(&pachDst[cchSrc], ' ', cchDst - cchSrc);
3122}
3123
3124
3125/**
3126 * Formats a timespec as an ISO-9660 ascii timestamp.
3127 *
3128 * @param pTime The timespec to format.
3129 * @param pIsoTs The ISO-9660 timestamp destination buffer.
3130 */
3131static void rtFsIsoMakerTimespecToIso9660Timestamp(PCRTTIMESPEC pTime, PISO9660TIMESTAMP pIsoTs)
3132{
3133 RTTIME Exploded;
3134 RTTimeExplode(&Exploded, pTime);
3135
3136 char szTmp[64];
3137#define FORMAT_FIELD(a_achDst, a_uSrc) \
3138 do { \
3139 RTStrFormatU32(szTmp, sizeof(szTmp), a_uSrc, 10, sizeof(a_achDst), sizeof(a_achDst), \
3140 RTSTR_F_ZEROPAD | RTSTR_F_WIDTH | RTSTR_F_PRECISION); \
3141 memcpy(a_achDst, szTmp, sizeof(a_achDst)); \
3142 } while (0)
3143 FORMAT_FIELD(pIsoTs->achYear, Exploded.i32Year);
3144 FORMAT_FIELD(pIsoTs->achMonth, Exploded.u8Month);
3145 FORMAT_FIELD(pIsoTs->achDay, Exploded.u8MonthDay);
3146 FORMAT_FIELD(pIsoTs->achHour, Exploded.u8Hour);
3147 FORMAT_FIELD(pIsoTs->achMinute, Exploded.u8Minute);
3148 FORMAT_FIELD(pIsoTs->achSecond, Exploded.u8Second);
3149 FORMAT_FIELD(pIsoTs->achCentisecond, Exploded.u32Nanosecond / RT_NS_10MS);
3150#undef FORMAT_FIELD
3151 pIsoTs->offUtc = 0;
3152}
3153
3154
3155/**
3156 * Formats a timespec as an ISO-9660 record timestamp.
3157 *
3158 * @param pTime The timespec to format.
3159 * @param pIsoTs The ISO-9660 timestamp destination buffer.
3160 */
3161static void rtFsIsoMakerTimespecToIso9660RecTimestamp(PCRTTIMESPEC pTime, PISO9660RECTIMESTAMP pIsoRecTs)
3162{
3163 RTTIME Exploded;
3164 RTTimeExplode(&Exploded, pTime);
3165
3166 pIsoRecTs->bYear = Exploded.i32Year >= 1900 ? Exploded.i32Year - 1900 : 0;
3167 pIsoRecTs->bMonth = Exploded.u8Month;
3168 pIsoRecTs->bDay = Exploded.u8MonthDay;
3169 pIsoRecTs->bHour = Exploded.u8Hour;
3170 pIsoRecTs->bMinute = Exploded.u8Minute;
3171 pIsoRecTs->bSecond = Exploded.u8Second;
3172 pIsoRecTs->offUtc = 0;
3173}
3174
3175
3176/**
3177 * Allocate and prepare the volume descriptors.
3178 *
3179 * What's not done here gets done later by rtFsIsoMakerFinalizeBootStuff, or at
3180 * teh very end of the finalization by rtFsIsoMakerFinalizeVolumeDescriptors.
3181 *
3182 * @returns IPRT status code
3183 * @param pThis The ISO maker instance.
3184 */
3185static int rtFsIsoMakerFinalizePrepVolumeDescriptors(PRTFSISOMAKERINT pThis)
3186{
3187 /*
3188 * Allocate and calc pointers.
3189 */
3190 RTMemFree(pThis->pbVolDescs);
3191 pThis->pbVolDescs = (uint8_t *)RTMemAllocZ(pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE);
3192 AssertReturn(pThis->pbVolDescs, VERR_NO_MEMORY);
3193
3194 uint32_t offVolDescs = 0;
3195
3196 pThis->pPrimaryVolDesc = (PISO9660PRIMARYVOLDESC)&pThis->pbVolDescs[offVolDescs];
3197 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
3198
3199 if (true)
3200 pThis->pElToritoDesc = NULL;
3201 else
3202 {
3203 pThis->pElToritoDesc = (PISO9660BOOTRECORD)&pThis->pbVolDescs[offVolDescs];
3204 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
3205 }
3206
3207 if (!pThis->Joliet.uLevel)
3208 pThis->pJolietVolDesc = NULL;
3209 else
3210 {
3211 pThis->pJolietVolDesc = (PISO9660SUPVOLDESC)&pThis->pbVolDescs[offVolDescs];
3212 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
3213 }
3214
3215 pThis->pTerminatorVolDesc = (PISO9660VOLDESCHDR)&pThis->pbVolDescs[offVolDescs];
3216 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
3217
3218 if (pThis->Udf.uLevel > 0)
3219 {
3220 /** @todo UDF descriptors. */
3221 }
3222 AssertReturn(offVolDescs == pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE, VERR_INTERNAL_ERROR_2);
3223
3224 /*
3225 * This may be needed later.
3226 */
3227 char szImageCreationTime[42];
3228 RTTimeSpecToString(&pThis->ImageCreationTime, szImageCreationTime, sizeof(szImageCreationTime));
3229
3230 /*
3231 * Initialize the primary descriptor.
3232 */
3233 PISO9660PRIMARYVOLDESC pPrimary = pThis->pPrimaryVolDesc;
3234
3235 pPrimary->Hdr.bDescType = ISO9660VOLDESC_TYPE_PRIMARY;
3236 pPrimary->Hdr.bDescVersion = ISO9660PRIMARYVOLDESC_VERSION;
3237 memcpy(pPrimary->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pPrimary->Hdr.achStdId));
3238 //pPrimary->bPadding8 = 0;
3239 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achSystemId, sizeof(pPrimary->achSystemId), pThis->PrimaryIso.pszSystemId);
3240 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achVolumeId, sizeof(pPrimary->achVolumeId),
3241 pThis->PrimaryIso.pszVolumeId ? pThis->PrimaryIso.pszVolumeId : szImageCreationTime);
3242 //pPrimary->Unused73 = {0}
3243 //pPrimary->VolumeSpaceSize = later
3244 //pPrimary->abUnused89 = {0}
3245 pPrimary->cVolumesInSet.be = RT_H2BE_U16_C(1);
3246 pPrimary->cVolumesInSet.le = RT_H2LE_U16_C(1);
3247 pPrimary->VolumeSeqNo.be = RT_H2BE_U16_C(1);
3248 pPrimary->VolumeSeqNo.le = RT_H2LE_U16_C(1);
3249 pPrimary->cbLogicalBlock.be = RT_H2BE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
3250 pPrimary->cbLogicalBlock.le = RT_H2LE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
3251 //pPrimary->cbPathTable = later
3252 //pPrimary->offTypeLPathTable = later
3253 //pPrimary->offOptionalTypeLPathTable = {0}
3254 //pPrimary->offTypeMPathTable = later
3255 //pPrimary->offOptionalTypeMPathTable = {0}
3256 //pPrimary->RootDir = later
3257 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achVolumeSetId, sizeof(pPrimary->achVolumeSetId),
3258 pThis->PrimaryIso.pszVolumeSetId);
3259 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achPublisherId, sizeof(pPrimary->achPublisherId),
3260 pThis->PrimaryIso.pszPublisherId);
3261 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achDataPreparerId, sizeof(pPrimary->achDataPreparerId),
3262 pThis->PrimaryIso.pszDataPreparerId);
3263 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achApplicationId, sizeof(pPrimary->achApplicationId),
3264 pThis->PrimaryIso.pszApplicationId);
3265 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achCopyrightFileId, sizeof(pPrimary->achCopyrightFileId),
3266 pThis->PrimaryIso.pszCopyrightFileId);
3267 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achAbstractFileId, sizeof(pPrimary->achAbstractFileId),
3268 pThis->PrimaryIso.pszAbstractFileId);
3269 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achBibliographicFileId, sizeof(pPrimary->achBibliographicFileId),
3270 pThis->PrimaryIso.pszBibliographicFileId);
3271 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pPrimary->BirthTime);
3272 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pPrimary->ModifyTime);
3273 //RT_ZERO(pPrimary->ExpireTime);
3274 //RT_ZERO(pPrimary->EffectiveTime)
3275 pPrimary->bFileStructureVersion = ISO9660_FILE_STRUCTURE_VERSION;
3276 //pPrimary->bReserved883 = 0;
3277 //RT_ZERO(pPrimary->abAppUse);
3278 //RT_ZERO(pPrimary->abReserved1396);
3279
3280 /*
3281 * Initialize the joliet descriptor if included.
3282 */
3283 PISO9660SUPVOLDESC pJoliet = pThis->pJolietVolDesc;
3284 if (pJoliet)
3285 {
3286 pJoliet->Hdr.bDescType = ISO9660VOLDESC_TYPE_SUPPLEMENTARY;
3287 pJoliet->Hdr.bDescVersion = ISO9660SUPVOLDESC_VERSION;
3288 memcpy(pJoliet->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pJoliet->Hdr.achStdId));
3289 pJoliet->fVolumeFlags = ISO9660SUPVOLDESC_VOL_F_ESC_ONLY_REG;
3290 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achSystemId, sizeof(pJoliet->achSystemId), pThis->Joliet.pszSystemId);
3291 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achVolumeId, sizeof(pJoliet->achVolumeId),
3292 pThis->Joliet.pszVolumeId ? pThis->Joliet.pszVolumeId : szImageCreationTime);
3293 //pJoliet->Unused73 = {0}
3294 //pJoliet->VolumeSpaceSize = later
3295 pJoliet->abEscapeSequences[0] = ISO9660_JOLIET_ESC_SEQ_0;
3296 pJoliet->abEscapeSequences[1] = ISO9660_JOLIET_ESC_SEQ_1;
3297 pJoliet->abEscapeSequences[1] = pThis->Joliet.uLevel == 1 ? ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
3298 : pThis->Joliet.uLevel == 2 ? ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
3299 : ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3;
3300 pJoliet->cVolumesInSet.be = RT_H2BE_U16_C(1);
3301 pJoliet->cVolumesInSet.le = RT_H2LE_U16_C(1);
3302 pJoliet->VolumeSeqNo.be = RT_H2BE_U16_C(1);
3303 pJoliet->VolumeSeqNo.le = RT_H2LE_U16_C(1);
3304 pJoliet->cbLogicalBlock.be = RT_H2BE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
3305 pJoliet->cbLogicalBlock.le = RT_H2LE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
3306 //pJoliet->cbPathTable = later
3307 //pJoliet->offTypeLPathTable = later
3308 //pJoliet->offOptionalTypeLPathTable = {0}
3309 //pJoliet->offTypeMPathTable = later
3310 //pJoliet->offOptionalTypeMPathTable = {0}
3311 //pJoliet->RootDir = later
3312 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achVolumeSetId, sizeof(pJoliet->achVolumeSetId),
3313 pThis->Joliet.pszVolumeSetId);
3314 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achPublisherId, sizeof(pJoliet->achPublisherId),
3315 pThis->Joliet.pszPublisherId);
3316 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achDataPreparerId, sizeof(pJoliet->achDataPreparerId),
3317 pThis->Joliet.pszDataPreparerId);
3318 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achApplicationId, sizeof(pJoliet->achApplicationId),
3319 pThis->Joliet.pszApplicationId);
3320 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achCopyrightFileId, sizeof(pJoliet->achCopyrightFileId),
3321 pThis->Joliet.pszCopyrightFileId);
3322 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achAbstractFileId, sizeof(pJoliet->achAbstractFileId),
3323 pThis->Joliet.pszAbstractFileId);
3324 rtFsIsoMakerFinalizeCopyAsUtf16AndSpacePad(pJoliet->achBibliographicFileId, sizeof(pJoliet->achBibliographicFileId),
3325 pThis->Joliet.pszBibliographicFileId);
3326 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pJoliet->BirthTime);
3327 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pJoliet->ModifyTime);
3328 //RT_ZERO(pJoliet->ExpireTime);
3329 //RT_ZERO(pJoliet->EffectiveTime)
3330 pJoliet->bFileStructureVersion = ISO9660_FILE_STRUCTURE_VERSION;
3331 //pJoliet->bReserved883 = 0;
3332 //RT_ZERO(pJoliet->abAppUse);
3333 //RT_ZERO(pJoliet->abReserved1396);
3334 }
3335
3336 /*
3337 * The ISO-9660 terminator descriptor.
3338 */
3339 pThis->pTerminatorVolDesc->bDescType = ISO9660VOLDESC_TYPE_TERMINATOR;
3340 pThis->pTerminatorVolDesc->bDescVersion = 1;
3341 memcpy(pThis->pTerminatorVolDesc->achStdId, ISO9660VOLDESC_STD_ID, sizeof(pThis->pTerminatorVolDesc->achStdId));
3342
3343 return VINF_SUCCESS;
3344}
3345
3346
3347/**
3348 * Finalizes the volume descriptors.
3349 *
3350 * This will set the RTFSISOMAKERFILE::offData members.
3351 *
3352 * @returns IPRT status code.
3353 * @param pThis The ISO maker instance.
3354 */
3355static int rtFsIsoMakerFinalizeVolumeDescriptors(PRTFSISOMAKERINT pThis)
3356{
3357 AssertReturn(pThis->pbVolDescs && pThis->pPrimaryVolDesc && pThis->pTerminatorVolDesc, VERR_INTERNAL_ERROR_3);
3358
3359 /*
3360 * Primary descriptor.
3361 */
3362 PISO9660PRIMARYVOLDESC pPrimary = pThis->pPrimaryVolDesc;
3363
3364 pPrimary->VolumeSpaceSize.be = RT_H2BE_U32(pThis->cbFinalizedImage / RTFSISOMAKER_SECTOR_SIZE);
3365 pPrimary->VolumeSpaceSize.le = RT_H2LE_U32(pThis->cbFinalizedImage / RTFSISOMAKER_SECTOR_SIZE);
3366 pPrimary->cbPathTable.be = RT_H2BE_U32(pThis->JolietDirs.cbPathTable);
3367 pPrimary->cbPathTable.le = RT_H2LE_U32(pThis->JolietDirs.cbPathTable);
3368 pPrimary->offTypeLPathTable = RT_H2LE_U32(pThis->JolietDirs.offPathTableL);
3369 pPrimary->offTypeMPathTable = RT_H2BE_U32(pThis->JolietDirs.offPathTableM);
3370 pPrimary->RootDir.DirRec.cbDirRec = sizeof(pPrimary->RootDir);
3371 pPrimary->RootDir.DirRec.cExtAttrBlocks = 0;
3372 pPrimary->RootDir.DirRec.offExtent.be = RT_H2BE_U32(pThis->PrimaryIso.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3373 pPrimary->RootDir.DirRec.offExtent.le = RT_H2LE_U32(pThis->PrimaryIso.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3374 pPrimary->RootDir.DirRec.cbData.be = RT_H2BE_U32(pThis->PrimaryIso.pRoot->pDir->cbDir);
3375 pPrimary->RootDir.DirRec.cbData.le = RT_H2LE_U32(pThis->PrimaryIso.pRoot->pDir->cbDir);
3376 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pThis->PrimaryIso.pRoot->pObj->BirthTime, &pPrimary->RootDir.DirRec.RecTime);
3377 pPrimary->RootDir.DirRec.fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
3378 pPrimary->RootDir.DirRec.bFileUnitSize = 0;
3379 pPrimary->RootDir.DirRec.bInterleaveGapSize = 0;
3380 pPrimary->RootDir.DirRec.VolumeSeqNo.be = RT_H2BE_U16_C(1);
3381 pPrimary->RootDir.DirRec.VolumeSeqNo.le = RT_H2BE_U16_C(1);
3382 pPrimary->RootDir.DirRec.bFileIdLength = 1;
3383 pPrimary->RootDir.DirRec.achFileId[1] = 0x00;
3384
3385 /*
3386 * Initialize the joliet descriptor if included.
3387 */
3388 PISO9660SUPVOLDESC pJoliet = pThis->pJolietVolDesc;
3389 if (pJoliet)
3390 {
3391 pJoliet->VolumeSpaceSize = pPrimary->VolumeSpaceSize;
3392 pJoliet->cbPathTable.be = RT_H2BE_U32(pThis->JolietDirs.cbPathTable);
3393 pJoliet->cbPathTable.le = RT_H2LE_U32(pThis->JolietDirs.cbPathTable);
3394 pJoliet->offTypeLPathTable = RT_H2LE_U32(pThis->JolietDirs.offPathTableL);
3395 pJoliet->offTypeMPathTable = RT_H2BE_U32(pThis->JolietDirs.offPathTableM);
3396 pJoliet->RootDir.DirRec.cbDirRec = sizeof(pJoliet->RootDir);
3397 pJoliet->RootDir.DirRec.cExtAttrBlocks = 0;
3398 pJoliet->RootDir.DirRec.offExtent.be = RT_H2BE_U32(pThis->Joliet.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3399 pJoliet->RootDir.DirRec.offExtent.le = RT_H2LE_U32(pThis->Joliet.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3400 pJoliet->RootDir.DirRec.cbData.be = RT_H2BE_U32(pThis->Joliet.pRoot->pDir->cbDir);
3401 pJoliet->RootDir.DirRec.cbData.le = RT_H2LE_U32(pThis->Joliet.pRoot->pDir->cbDir);
3402 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pThis->Joliet.pRoot->pObj->BirthTime, &pJoliet->RootDir.DirRec.RecTime);
3403 pJoliet->RootDir.DirRec.fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
3404 pJoliet->RootDir.DirRec.bFileUnitSize = 0;
3405 pJoliet->RootDir.DirRec.bInterleaveGapSize = 0;
3406 pJoliet->RootDir.DirRec.VolumeSeqNo.be = RT_H2BE_U16_C(1);
3407 pJoliet->RootDir.DirRec.VolumeSeqNo.le = RT_H2BE_U16_C(1);
3408 pJoliet->RootDir.DirRec.bFileIdLength = 1;
3409 pJoliet->RootDir.DirRec.achFileId[1] = 0x00;
3410 }
3411
3412 return VINF_SUCCESS;
3413}
3414
3415
3416/**
3417 * Finalizes the image.
3418 *
3419 * @returns IPRT status code.
3420 * @param hIsoMaker The ISO maker handle.
3421 */
3422RTDECL(int) RTFsIsoMakerFinalize(RTFSISOMAKER hIsoMaker)
3423{
3424 PRTFSISOMAKERINT pThis = hIsoMaker;
3425 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
3426 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
3427
3428 /*
3429 * Remove orphaned objects and allocate volume descriptors.
3430 */
3431 int rc = rtFsIsoMakerFinalizeRemoveOrphans(pThis);
3432 if (RT_FAILURE(rc))
3433 return rc;
3434 rc = rtFsIsoMakerFinalizePrepVolumeDescriptors(pThis);
3435 if (RT_FAILURE(rc))
3436 return rc;
3437
3438 /*
3439 * If there is any boot related stuff to be included, it ends up right after
3440 * the descriptors.
3441 */
3442 uint64_t offData = _32K + pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE;
3443 rc = rtFsIsoMakerFinalizeBootStuff(pThis, &offData);
3444 if (RT_SUCCESS(rc))
3445 {
3446 /*
3447 * Directories and path tables comes next.
3448 */
3449 rc = rtFsIsoMakerFinalizeDirectories(pThis, &offData);
3450 if (RT_SUCCESS(rc))
3451 {
3452 /*
3453 * Then we store the file data.
3454 */
3455 rc = rtFsIsoMakerFinalizeData(pThis, &offData);
3456 if (RT_SUCCESS(rc))
3457 {
3458 pThis->cbFinalizedImage = offData;
3459
3460 /*
3461 * Finally, finalize the volume descriptors as they depend on some of the
3462 * block allocations done in the previous steps.
3463 */
3464 rc = rtFsIsoMakerFinalizeVolumeDescriptors(pThis);
3465 if (RT_SUCCESS(rc))
3466 {
3467 pThis->fFinalized = true;
3468 return VINF_SUCCESS;
3469 }
3470 }
3471 }
3472 }
3473 return rc;
3474}
3475
3476
3477
3478
3479
3480/*
3481 *
3482 * Image I/O.
3483 * Image I/O.
3484 * Image I/O.
3485 *
3486 */
3487
3488/**
3489 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
3490 */
3491static DECLCALLBACK(int) rtFsIsoMakerOutFile_Close(void *pvThis)
3492{
3493 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
3494
3495 RTFsIsoMakerRelease(pThis->pIsoMaker);
3496 pThis->pIsoMaker = NULL;
3497
3498 return VINF_SUCCESS;
3499}
3500
3501
3502/**
3503 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
3504 */
3505static DECLCALLBACK(int) rtFsIsoMakerOutFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3506{
3507 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
3508 PRTFSISOMAKERINT pIsoMaker = pThis->pIsoMaker;
3509
3510
3511 pObjInfo->cbObject = pIsoMaker->cbFinalizedImage;
3512 pObjInfo->cbAllocated = pIsoMaker->cbFinalizedImage;
3513 pObjInfo->AccessTime = pIsoMaker->ImageCreationTime;
3514 pObjInfo->ModificationTime = pIsoMaker->ImageCreationTime;
3515 pObjInfo->ChangeTime = pIsoMaker->ImageCreationTime;
3516 pObjInfo->BirthTime = pIsoMaker->ImageCreationTime;
3517 pObjInfo->Attr.fMode = 0444 | RTFS_TYPE_FILE | RTFS_DOS_READONLY;
3518
3519 switch (enmAddAttr)
3520 {
3521 case RTFSOBJATTRADD_NOTHING:
3522 enmAddAttr = RTFSOBJATTRADD_UNIX;
3523 /* fall thru */
3524 case RTFSOBJATTRADD_UNIX:
3525 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
3526 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
3527 pObjInfo->Attr.u.Unix.cHardlinks = 1;
3528 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
3529 pObjInfo->Attr.u.Unix.INodeId = 0;
3530 pObjInfo->Attr.u.Unix.fFlags = 0;
3531 pObjInfo->Attr.u.Unix.GenerationId = 0;
3532 pObjInfo->Attr.u.Unix.Device = 0;
3533 break;
3534
3535 case RTFSOBJATTRADD_UNIX_OWNER:
3536 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
3537 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
3538 break;
3539
3540 case RTFSOBJATTRADD_UNIX_GROUP:
3541 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
3542 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
3543 break;
3544
3545 case RTFSOBJATTRADD_EASIZE:
3546 pObjInfo->Attr.u.EASize.cb = 0;
3547 break;
3548
3549 default:
3550 AssertFailedReturn(VERR_INVALID_PARAMETER);
3551 }
3552 pObjInfo->Attr.enmAdditional = enmAddAttr;
3553
3554 return VINF_SUCCESS;
3555}
3556
3557
3558/**
3559 * Produces the content of a TRANS.TBL file as a memory file.
3560 *
3561 * @returns IPRT status code.
3562 * @param pThis The ISO maker output file instance. The file is
3563 * returned as pThis->hVfsSrcFile.
3564 * @param pFile The TRANS.TBL file.
3565 */
3566static int rtFsIsoMakerOutFile_ProduceTransTbl(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERFILE pFile)
3567{
3568 /*
3569 * Create memory file instance.
3570 */
3571 RTVFSFILE hVfsFile;
3572 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, pFile->cbData, &hVfsFile);
3573 AssertRCReturn(rc, rc);
3574
3575 /*
3576 * Produce the file content.
3577 */
3578 PRTFSISOMAKERNAME *ppChild = pFile->u.pTransTblDir->pDir->papChildren;
3579 uint32_t cLeft = pFile->u.pTransTblDir->pDir->cChildren;
3580 while (cLeft-- > 0)
3581 {
3582 PRTFSISOMAKERNAME pChild = *ppChild++;
3583 if (pChild->pszTransNm)
3584 {
3585 /** @todo TRANS.TBL codeset, currently using UTF-8 which is probably not it.
3586 * However, nobody uses this stuff any more, so who cares. */
3587 char szEntry[RTFSISOMAKER_MAX_NAME_BUF * 2 + 128];
3588 size_t cchEntry = RTStrPrintf(szEntry, sizeof(szEntry), "%c %-*s\t%s\n", pChild->pDir ? 'D' : 'F',
3589 RTFSISOMAKER_TRANS_TBL_LEFT_PAD, pChild->szName, pChild->pszTransNm);
3590 rc = RTVfsFileWrite(hVfsFile, szEntry, cchEntry, NULL);
3591 if (RT_FAILURE(rc))
3592 {
3593 RTVfsFileRelease(hVfsFile);
3594 return rc;
3595 }
3596 }
3597 }
3598
3599 /*
3600 * Check that the size matches our estimate.
3601 */
3602 uint64_t cbResult = 0;
3603 rc = RTVfsFileGetSize(hVfsFile, &cbResult);
3604 if (RT_SUCCESS(rc) && cbResult == pFile->cbData)
3605 {
3606 pThis->hVfsSrcFile = hVfsFile;
3607 return VINF_SUCCESS;
3608 }
3609
3610 AssertMsgFailed(("rc=%Rrc, cbResult=%#RX64 cbData=%#RX64\n", rc, cbResult, pFile->cbData));
3611 RTVfsFileRelease(hVfsFile);
3612 return VERR_INTERNAL_ERROR_4;
3613}
3614
3615
3616
3617/**
3618 * Reads file data.
3619 *
3620 * @returns IPRT status code
3621 * @param pThis The instance data for the VFS file. We use this to
3622 * keep hints about where we are and we which source
3623 * file we've opened/created.
3624 * @param pIsoMaker The ISO maker instance.
3625 * @param offUnsigned The ISO image byte offset of the requested data.
3626 * @param pbBuf The output buffer.
3627 * @param cbBuf How much to read.
3628 * @param pcbDone Where to return how much was read.
3629 */
3630static int rtFsIsoMakerOutFile_ReadFileData(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERINT pIsoMaker, uint64_t offUnsigned,
3631 uint8_t *pbBuf, size_t cbBuf, size_t *pcbDone)
3632{
3633 *pcbDone = 0;
3634
3635 /*
3636 * Figure out which file. We keep a hint in the instance.
3637 */
3638 uint64_t offInFile;
3639 PRTFSISOMAKERFILE pFile = pThis->pFileHint;
3640 if (!pFile)
3641 {
3642 pFile = RTListGetFirst(&pIsoMaker->FinalizedFiles, RTFSISOMAKERFILE, FinalizedEntry);
3643 AssertReturn(pFile, VERR_INTERNAL_ERROR_2);
3644 }
3645 if ((offInFile = offUnsigned - pFile->offData) < RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE))
3646 { /* hit */ }
3647 else if (offUnsigned > pFile->offData)
3648 {
3649 /* Seek forwards. */
3650 do
3651 {
3652 pFile = RTListGetNext(&pIsoMaker->FinalizedFiles, pFile, RTFSISOMAKERFILE, FinalizedEntry);
3653 AssertReturn(pFile, VERR_INTERNAL_ERROR_3);
3654 } while ((offInFile = offUnsigned - pFile->offData) < RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE));
3655 }
3656 else
3657 {
3658 /* Seek backwards. */
3659 do
3660 {
3661 pFile = RTListGetPrev(&pIsoMaker->FinalizedFiles, pFile, RTFSISOMAKERFILE, FinalizedEntry);
3662 AssertReturn(pFile, VERR_INTERNAL_ERROR_3);
3663 } while ((offInFile = offUnsigned - pFile->offData) < RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE));
3664 }
3665
3666 /*
3667 * Update the hint/current file.
3668 */
3669 if (pThis->pFileHint != pFile)
3670 {
3671 pThis->pFileHint = pFile;
3672 if (pThis->hVfsSrcFile != NIL_RTVFSFILE)
3673 {
3674 RTVfsFileRelease(pThis->hVfsSrcFile);
3675 pThis->hVfsSrcFile = NIL_RTVFSFILE;
3676 }
3677 }
3678
3679 /*
3680 * Produce data bits according to the source type.
3681 */
3682 if (offInFile < pFile->cbData)
3683 {
3684 int rc;
3685 size_t cbToRead = RT_MIN(cbBuf, pFile->cbData - offInFile);
3686 switch (pFile->enmSrcType)
3687 {
3688 case RTFSISOMAKERSRCTYPE_PATH:
3689 if (pThis->hVfsSrcFile == NIL_RTVFSFILE)
3690 {
3691 rc = RTVfsChainOpenFile(pFile->u.pszSrcPath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
3692 &pThis->hVfsSrcFile, NULL, NULL);
3693 if (RT_FAILURE(rc))
3694 return rc;
3695 }
3696 rc = RTVfsFileReadAt(pThis->hVfsSrcFile, offInFile, pbBuf, cbToRead, NULL);
3697 break;
3698
3699 case RTFSISOMAKERSRCTYPE_VFS_FILE:
3700 rc = RTVfsFileReadAt(pFile->u.hVfsFile, offInFile, pbBuf, cbToRead, NULL);
3701 break;
3702
3703 case RTFSISOMAKERSRCTYPE_TRANS_TBL:
3704 if (pThis->hVfsSrcFile == NIL_RTVFSFILE)
3705 {
3706 rc = rtFsIsoMakerOutFile_ProduceTransTbl(pThis, pFile);
3707 if (RT_FAILURE(rc))
3708 return rc;
3709 }
3710 rc = RTVfsFileReadAt(pFile->u.hVfsFile, offInFile, pbBuf, cbToRead, NULL);
3711 break;
3712
3713 default:
3714 AssertFailedReturn(VERR_INTERNAL_ERROR_5);
3715 }
3716 if (RT_FAILURE(rc))
3717 return rc;
3718 *pcbDone = cbToRead;
3719
3720 /*
3721 * Check if we're into the zero padding at the end of the file now.
3722 */
3723 if ( cbToRead < cbBuf
3724 && offInFile + cbToRead == pFile->cbData)
3725 {
3726 cbBuf -= cbToRead;
3727 pbBuf += cbToRead;
3728 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - (pFile->cbData & RTFSISOMAKER_SECTOR_OFFSET_MASK));
3729 memset(pbBuf, 0, cbZeros);
3730 *pcbDone += cbZeros;
3731 }
3732 }
3733 else
3734 {
3735 size_t cbZeros = RT_MIN(cbBuf, RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE) - offInFile);
3736 memset(pbBuf, 0, cbZeros);
3737 *pcbDone = cbZeros;
3738 }
3739 return VINF_SUCCESS;
3740}
3741
3742
3743/**
3744 * Generates ISO-9660 path table record into the specified buffer.
3745 *
3746 * @returns Number of bytes copied into the buffer.
3747 * @param pName The directory namespace node.
3748 * @param fUnicode Set if the name should be translated to big endian
3749 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
3750 * @param pbBuf The buffer. This is large enough to hold the path
3751 * record (use RTFSISOMAKER_CALC_PATHREC_SIZE) and a zero
3752 * RTUTF16 terminator if @a fUnicode is true.
3753 */
3754static uint32_t rtFsIsoMakerOutFile_GeneratePathRec(PRTFSISOMAKERNAME pName, bool fUnicode, bool fLittleEndian, uint8_t *pbBuf)
3755{
3756 PISO9660PATHREC pPathRec = (PISO9660PATHREC)pbBuf;
3757 pPathRec->cbDirId = pName->cbNameInDirRec;
3758 pPathRec->cbExtAttr = 0;
3759 if (fLittleEndian)
3760 {
3761 pPathRec->offExtent = RT_H2LE_U32(pName->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3762 pPathRec->idParentRec = RT_H2LE_U16(pName->pParent ? pName->pParent->pDir->idPathTable : 0);
3763 }
3764 else
3765 {
3766 pPathRec->offExtent = RT_H2BE_U32(pName->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3767 pPathRec->idParentRec = RT_H2BE_U16(pName->pParent ? pName->pParent->pDir->idPathTable : 0);
3768 }
3769 if (!fUnicode)
3770 {
3771 memcpy(&pPathRec->achDirId[0], pName->szName, pName->cbNameInDirRec);
3772 if (pName->cbNameInDirRec & 1)
3773 pPathRec->achDirId[pName->cbNameInDirRec] = '\0';
3774 }
3775 else
3776 {
3777 /* Caller made sure there is space for a zero terminator character. */
3778 PRTUTF16 pwszTmp = (PRTUTF16)&pPathRec->achDirId[0];
3779 size_t cwcResult = 0;
3780 int rc = RTStrToUtf16BigEx(pName->szName, RTSTR_MAX, &pwszTmp, pName->cbNameInDirRec / sizeof(RTUTF16) + 1, &cwcResult);
3781 AssertRC(rc);
3782 Assert(cwcResult * sizeof(RTUTF16) == pName->cbNameInDirRec);
3783 }
3784 return RTFSISOMAKER_CALC_PATHREC_SIZE(pName->cbNameInDirRec);
3785}
3786
3787
3788/**
3789 * Deals with situations where the destination buffer doesn't cover the whole
3790 * path table record.
3791 *
3792 * @returns Number of bytes copied into the buffer.
3793 * @param pName The directory namespace node.
3794 * @param fUnicode Set if the name should be translated to big endian
3795 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
3796 * @param offInRec The offset into the path table record.
3797 * @param pbBuf The buffer.
3798 * @param cbBuf The buffer size.
3799 */
3800static uint32_t rtFsIsoMakerOutFile_GeneratePathRecPartial(PRTFSISOMAKERNAME pName, bool fUnicode, bool fLittleEndian,
3801 uint32_t offInRec, uint8_t *pbBuf, size_t cbBuf)
3802{
3803 uint8_t abTmpRec[256];
3804 size_t cbToCopy = rtFsIsoMakerOutFile_GeneratePathRec(pName, fUnicode, fLittleEndian, abTmpRec);
3805 cbToCopy = RT_MIN(cbBuf, cbToCopy - offInRec);
3806 memcpy(pbBuf, &abTmpRec[offInRec], cbToCopy);
3807 return (uint32_t)cbToCopy;
3808}
3809
3810
3811/**
3812 * Generate path table records.
3813 *
3814 * This will generate record up to the end of the table. However, it will not
3815 * supply the zero padding in the last sector, the caller is expected to take
3816 * care of that.
3817 *
3818 * @returns Number of bytes written to the buffer.
3819 * @param pThis The instance data for the VFS file. We use this to
3820 * keep hints about where we are and we which source
3821 * file we've opened/created.
3822 * @param pFinalizedDirs The finalized directory data for the namespace.
3823 * @param fUnicode Set if the name should be translated to big endian
3824 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
3825 * @param fLittleEndian Set if we're generating little endian records, clear
3826 * if big endian records.
3827 * @param offInTable Offset into the path table.
3828 * @param pbBuf The output buffer.
3829 * @param cbBuf The buffer size.
3830 */
3831static size_t rtFsIsoMakerOutFile_ReadPathTable(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
3832 bool fUnicode, bool fLittleEndian, uint32_t offInTable,
3833 uint8_t *pbBuf, size_t cbBuf)
3834{
3835 /*
3836 * Figure out which directory to start with. We keep a hint in the instance.
3837 */
3838 PRTFSISOMAKERNAMEDIR pDir = pThis->pDirHint;
3839 if (!pDir)
3840 {
3841 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
3842 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
3843 }
3844 if (offInTable - pDir->offPathTable < RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec))
3845 { /* hit */ }
3846 /* Seek forwards: */
3847 else if (offInTable > pDir->offPathTable)
3848 do
3849 {
3850 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
3851 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
3852 } while (offInTable - pDir->offPathTable < RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec));
3853 /* Back to the start: */
3854 else if (offInTable == 0)
3855 {
3856 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
3857 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
3858 }
3859 /* Seek backwards: */
3860 else
3861 do
3862 {
3863 pDir = RTListGetPrev(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
3864 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
3865 } while (offInTable - pDir->offPathTable < RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec));
3866
3867 /*
3868 * Generate content.
3869 */
3870 size_t cbDone = 0;
3871 while ( cbBuf > 0
3872 && pDir)
3873 {
3874 PRTFSISOMAKERNAME pName = pDir->pName;
3875 uint8_t cbRec = RTFSISOMAKER_CALC_PATHREC_SIZE(pName->cbNameInDirRec);
3876 uint32_t cbCopied;
3877 if ( offInTable == pDir->offPathTable
3878 && cbBuf >= cbRec + fUnicode * 2U)
3879 cbCopied = rtFsIsoMakerOutFile_GeneratePathRec(pName, fUnicode, fLittleEndian, pbBuf);
3880 else
3881 cbCopied = rtFsIsoMakerOutFile_GeneratePathRecPartial(pName, fUnicode, fLittleEndian,
3882 offInTable - pDir->offPathTable, pbBuf, cbBuf);
3883 cbDone += cbCopied;
3884 offInTable += cbCopied;
3885 pbBuf += cbCopied;
3886 cbBuf -= cbCopied;
3887 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
3888 }
3889
3890 /*
3891 * Update the hint.
3892 */
3893 pThis->pDirHint = pDir;
3894
3895 return cbDone;
3896}
3897
3898
3899/**
3900 * Generates ISO-9660 directory record into the specified buffer.
3901 *
3902 * @returns Number of bytes copied into the buffer.
3903 * @param pName The namespace node.
3904 * @param fUnicode Set if the name should be translated to big endian
3905 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
3906 * @param pbBuf The buffer. This is at least pName->cbDirRec bytes big.
3907 */
3908static uint32_t rtFsIsoMakerOutFile_GenerateDirRec(PRTFSISOMAKERNAME pName, bool fUnicode, uint8_t *pbBuf)
3909{
3910 /*
3911 * Emit a standard ISO-9660 directory record.
3912 */
3913 PISO9660DIRREC pDirRec = (PISO9660DIRREC)pbBuf;
3914 PCRTFSISOMAKEROBJ pObj = pName->pObj;
3915 PCRTFSISOMAKERNAMEDIR pDir = pName->pDir;
3916 if (pDir)
3917 {
3918 pDirRec->offExtent.be = RT_H2BE_U32(pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3919 pDirRec->offExtent.le = RT_H2LE_U32(pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
3920 pDirRec->cbData.be = RT_H2BE_U32(pDir->cbDir);
3921 pDirRec->cbData.le = RT_H2LE_U32(pDir->cbDir);
3922 pDirRec->fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
3923 }
3924 else if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
3925 {
3926 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
3927 pDirRec->offExtent.be = RT_H2BE_U32(pFile->offData / RTFSISOMAKER_SECTOR_SIZE);
3928 pDirRec->offExtent.le = RT_H2LE_U32(pFile->offData / RTFSISOMAKER_SECTOR_SIZE);
3929 pDirRec->cbData.be = RT_H2BE_U32(pFile->cbData);
3930 pDirRec->cbData.le = RT_H2LE_U32(pFile->cbData);
3931 pDirRec->fFileFlags = 0;
3932 }
3933 else
3934 {
3935 pDirRec->offExtent.be = 0;
3936 pDirRec->offExtent.le = 0;
3937 pDirRec->cbData.be = 0;
3938 pDirRec->cbData.le = 0;
3939 pDirRec->fFileFlags = 0;
3940 }
3941 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pObj->BirthTime, &pDirRec->RecTime);
3942
3943 pDirRec->cbDirRec = pName->cbDirRec;
3944 pDirRec->cExtAttrBlocks = 0;
3945 pDirRec->bFileUnitSize = 0;
3946 pDirRec->bInterleaveGapSize = 0;
3947 pDirRec->VolumeSeqNo.be = RT_H2BE_U16_C(1);
3948 pDirRec->VolumeSeqNo.le = RT_H2BE_U16_C(1);
3949 pDirRec->bFileIdLength = pName->cbNameInDirRec;
3950
3951 if (!fUnicode)
3952 {
3953 memcpy(&pDirRec->achFileId[0], pName->szName, pName->cbNameInDirRec);
3954 if (!(pName->cbNameInDirRec & 1))
3955 pDirRec->achFileId[pName->cbNameInDirRec] = '\0';
3956 }
3957 else
3958 {
3959 /* Convert to big endian UTF-16. We're using a separate buffer here
3960 because of zero terminator (none in pDirRec) and misalignment. */
3961 RTUTF16 wszTmp[128];
3962 PRTUTF16 pwszTmp = &wszTmp[0];
3963 size_t cwcResult = 0;
3964 int rc = RTStrToUtf16BigEx(pName->szName, RTSTR_MAX, &pwszTmp, RT_ELEMENTS(wszTmp), &cwcResult);
3965 AssertRC(rc);
3966 Assert(cwcResult * sizeof(RTUTF16) == pName->cbNameInDirRec);
3967 memcpy(&pDirRec->achFileId[0], pwszTmp, pName->cbNameInDirRec);
3968 pDirRec->achFileId[pName->cbNameInDirRec] = '\0';
3969 }
3970
3971 /*
3972 * Rock ridge fields if enabled.
3973 */
3974 /** @todo rock ridge. */
3975
3976 return pName->cbDirRec;
3977}
3978
3979
3980/**
3981 * Deals with situations where the destination buffer doesn't cover the whole
3982 * directory record.
3983 *
3984 * @returns Number of bytes copied into the buffer.
3985 * @param pName The namespace node.
3986 * @param fUnicode Set if the name should be translated to big endian
3987 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
3988 * @param off The offset into the directory record.
3989 * @param pbBuf The buffer.
3990 * @param cbBuf The buffer size.
3991 */
3992static uint32_t rtFsIsoMakerOutFile_GenerateDirRecPartial(PRTFSISOMAKERNAME pName, bool fUnicode,
3993 uint32_t off, uint8_t *pbBuf, size_t cbBuf)
3994{
3995 Assert(off < pName->cbDirRec);
3996
3997 uint8_t abTmpBuf[256];
3998 size_t cbToCopy = rtFsIsoMakerOutFile_GenerateDirRec(pName, fUnicode, pbBuf);
3999 cbToCopy = RT_MIN(cbBuf, cbToCopy - off);
4000 memcpy(pbBuf, &abTmpBuf[off], cbToCopy);
4001 return (uint32_t)cbToCopy;
4002}
4003
4004
4005/**
4006 * Generate a '.' or '..' directory record.
4007 *
4008 * This is the same as rtFsIsoMakerOutFile_GenerateDirRec, but with the filename
4009 * reduced to 1 byte.
4010 *
4011 * @returns Number of bytes copied into the buffer.
4012 * @param pName The directory namespace node.
4013 * @param fUnicode Set if the name should be translated to big endian
4014 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4015 * @param bDirId The directory ID (0x00 or 0x01).
4016 * @param off The offset into the directory record.
4017 * @param pbBuf The buffer.
4018 * @param cbBuf The buffer size.
4019 */
4020static uint32_t rtFsIsoMakerOutFile_GenerateSpecialDirRec(PRTFSISOMAKERNAME pName, bool fUnicode, uint8_t bDirId,
4021 uint32_t off, uint8_t *pbBuf, size_t cbBuf)
4022{
4023 Assert(off < pName->cbDirRec);
4024 Assert(pName->pDir);
4025
4026 /* Generate a regular directory record. */
4027 uint8_t abTmpBuf[256];
4028 size_t cbToCopy = rtFsIsoMakerOutFile_GenerateDirRec(pName, fUnicode, pbBuf);
4029
4030 /* Replace the filename part. */
4031 PISO9660DIRREC pDirRec = (PISO9660DIRREC)abTmpBuf;
4032 if (pDirRec->bFileIdLength != 1)
4033 {
4034 uint8_t offSysUse = pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1) + RT_OFFSETOF(ISO9660DIRREC, achFileId);
4035 uint8_t cbSysUse = pDirRec->cbDirRec - offSysUse;
4036 if (cbSysUse > 0)
4037 memmove(&pDirRec->achFileId[1], &pbBuf[offSysUse], cbSysUse);
4038 pDirRec->bFileIdLength = 1;
4039 cbToCopy = RT_OFFSETOF(ISO9660DIRREC, achFileId) + 1 + cbSysUse;
4040 pDirRec->cbDirRec = (uint8_t)cbToCopy;
4041 }
4042 pDirRec->achFileId[0] = bDirId;
4043
4044 /* Do the copying. */
4045 cbToCopy = RT_MIN(cbBuf, cbToCopy - off);
4046 memcpy(pbBuf, &abTmpBuf[off], cbToCopy);
4047 return (uint32_t)cbToCopy;
4048}
4049
4050
4051/**
4052 * Read directory records.
4053 *
4054 * This locates the directory at @a offUnsigned and generates directory records
4055 * for it. Caller must repeat the call to get directory entries for the next
4056 * directory should there be desire for that.
4057 *
4058 * @returns Number of bytes copied into @a pbBuf.
4059 * @param pThis The instance data for the VFS file. We use this to
4060 * keep hints about where we are and we which source
4061 * file we've opened/created.
4062 * @param pIsoMaker The ISO maker instance.
4063 * @param pFinalizedDirs The finalized directory data for the namespace.
4064 * @param fUnicode Set if the name should be translated to big endian
4065 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4066 * @param offUnsigned The ISO image byte offset of the requested data.
4067 * @param pbBuf The output buffer.
4068 * @param cbBuf How much to read.
4069 */
4070static size_t rtFsIsoMakerOutFile_ReadDirRecords(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
4071 bool fUnicode, uint64_t offUnsigned, uint8_t *pbBuf, size_t cbBuf)
4072{
4073 /*
4074 * Figure out which directory. We keep a hint in the instance.
4075 */
4076 uint64_t offInDir64;
4077 PRTFSISOMAKERNAMEDIR pDir = pThis->pDirHint;
4078 if (!pDir)
4079 {
4080 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4081 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4082 }
4083 if ((offInDir64 = offUnsigned - pDir->offDir) < RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE))
4084 { /* hit */ }
4085 /* Seek forwards: */
4086 else if (offUnsigned > pDir->offDir)
4087 do
4088 {
4089 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4090 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4091 } while ((offInDir64 = offUnsigned - pDir->offDir) < RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE));
4092 /* Back to the start: */
4093 else if (pFinalizedDirs->offDirs / RTFSISOMAKER_SECTOR_SIZE == offUnsigned / RTFSISOMAKER_SECTOR_SIZE)
4094 {
4095 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4096 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4097 }
4098 /* Seek backwards: */
4099 else
4100 do
4101 {
4102 pDir = RTListGetPrev(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4103 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4104 } while ((offInDir64 = offUnsigned - pDir->offDir) < RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE));
4105
4106 /*
4107 * Update the hint.
4108 */
4109 pThis->pDirHint = pDir;
4110
4111 /*
4112 * Generate content.
4113 */
4114 size_t cbDone = 0;
4115 uint32_t offInDir = (uint32_t)offInDir64;
4116 if (offInDir < pDir->cbDir)
4117 {
4118 PRTFSISOMAKERNAME pDirName = pDir->pName;
4119 PRTFSISOMAKERNAME pParentName = pDirName->pParent ? pDirName->pParent : pDirName;
4120 uint32_t cbSpecialRecs = (uint32_t)pDir->cbDirRec00 + pDir->cbDirRec01;
4121
4122 /*
4123 * Special '.' and/or '..' entries requested.
4124 */
4125 uint32_t iChild;
4126 if (offInDir < cbSpecialRecs)
4127 {
4128 /* do '.' */
4129 if (offInDir < pDir->cbDirRec00)
4130 {
4131 uint32_t cbCopied = rtFsIsoMakerOutFile_GenerateSpecialDirRec(pDirName, fUnicode, 0, offInDir, pbBuf, cbBuf);
4132 cbDone += cbCopied;
4133 offInDir += cbCopied;
4134 pbBuf += cbCopied;
4135 cbBuf -= cbCopied;
4136 }
4137
4138 /* do '..' */
4139 if (cbBuf > 0)
4140 {
4141 uint32_t cbCopied = rtFsIsoMakerOutFile_GenerateSpecialDirRec(pParentName, fUnicode, 1,
4142 offInDir - pDir->cbDirRec00, pbBuf, cbBuf);
4143 cbDone += cbCopied;
4144 offInDir += cbCopied;
4145 pbBuf += cbCopied;
4146 cbBuf -= cbCopied;
4147 }
4148
4149 iChild = 0;
4150 }
4151 /*
4152 * Locate the directory entry we should start with. We can do this
4153 * using binary searching on offInDir.
4154 */
4155 else
4156 {
4157 /** @todo binary search */
4158 iChild = 0;
4159 while (iChild < pDir->cChildren)
4160 {
4161 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
4162 if ((offInDir - pChild->offDirRec) < pChild->cbDirRec)
4163 break;
4164 iChild++;
4165 }
4166 AssertReturnStmt(iChild < pDir->cChildren, *pbBuf = 0xff, 1);
4167 }
4168
4169 /*
4170 * Normal directory entries.
4171 */
4172 while ( cbBuf > 0
4173 && iChild < pDir->cChildren)
4174 {
4175 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
4176 uint32_t cbCopied;
4177 if ( offInDir == pChild->offDirRec
4178 && cbBuf >= pChild->cbDirRec)
4179 cbCopied = rtFsIsoMakerOutFile_GenerateDirRec(pChild, fUnicode, pbBuf);
4180 else
4181 cbCopied = rtFsIsoMakerOutFile_GenerateDirRecPartial(pChild, fUnicode, offInDir - pChild->offDirRec, pbBuf, cbBuf);
4182 cbDone += cbCopied;
4183 offInDir += cbCopied;
4184 pbBuf += cbCopied;
4185 cbBuf -= cbCopied;
4186 iChild++;
4187 }
4188
4189 /*
4190 * Check if we're into the zero padding at the end of the directory now.
4191 */
4192 if ( cbBuf > 0
4193 && iChild >= pDir->cChildren)
4194 {
4195 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - (pDir->cbDir & RTFSISOMAKER_SECTOR_OFFSET_MASK));
4196 memset(pbBuf, 0, cbZeros);
4197 cbDone += cbZeros;
4198 }
4199 }
4200 else
4201 {
4202 cbDone = RT_MIN(cbBuf, RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE) - offInDir);
4203 memset(pbBuf, 0, cbDone);
4204 }
4205
4206 return cbDone;
4207}
4208
4209
4210/**
4211 * Read directory records or path table records.
4212 *
4213 * Will not necessarily fill the entire buffer. Caller must call again to get
4214 * more.
4215 *
4216 * @returns Number of bytes copied into @a pbBuf.
4217 * @param pThis The instance data for the VFS file. We use this to
4218 * keep hints about where we are and we which source
4219 * file we've opened/created.
4220 * @param pIsoMaker The ISO maker instance.
4221 * @param pNamespace The namespace.
4222 * @param pFinalizedDirs The finalized directory data for the namespace.
4223 * @param offUnsigned The ISO image byte offset of the requested data.
4224 * @param pbBuf The output buffer.
4225 * @param cbBuf How much to read.
4226 */
4227static size_t rtFsIsoMakerOutFile_ReadDirStructures(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERNAMESPACE pNamespace,
4228 PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
4229 uint64_t offUnsigned, uint8_t *pbBuf, size_t cbBuf)
4230{
4231 if (offUnsigned < pFinalizedDirs->offPathTableL)
4232 return rtFsIsoMakerOutFile_ReadDirRecords(pThis, pFinalizedDirs, pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
4233 offUnsigned, pbBuf, cbBuf);
4234
4235 uint64_t offInTable;
4236 if ((offInTable = offUnsigned - pFinalizedDirs->offPathTableL) < pFinalizedDirs->cbPathTable)
4237 return rtFsIsoMakerOutFile_ReadPathTable(pThis, pFinalizedDirs, pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
4238 true /*fLittleEndian*/, (uint32_t)offInTable, pbBuf, cbBuf);
4239
4240 if ((offInTable = offUnsigned - pFinalizedDirs->offPathTableM) < pFinalizedDirs->cbPathTable)
4241 return rtFsIsoMakerOutFile_ReadPathTable(pThis, pFinalizedDirs, pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
4242 false /*fLittleEndian*/, (uint32_t)offInTable, pbBuf, cbBuf);
4243
4244 /* ASSUME we're in the zero padding at the end of a path table. */
4245 Assert( offUnsigned - pFinalizedDirs->offPathTableL < RT_ALIGN_32(pFinalizedDirs->cbPathTable, RTFSISOMAKER_SECTOR_SIZE)
4246 || offUnsigned - pFinalizedDirs->offPathTableM < RT_ALIGN_32(pFinalizedDirs->cbPathTable, RTFSISOMAKER_SECTOR_SIZE));
4247 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - ((size_t)offUnsigned & RTFSISOMAKER_SECTOR_OFFSET_MASK));
4248 memset(pbBuf, 0, cbZeros);
4249 return cbZeros;
4250}
4251
4252
4253
4254/**
4255 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
4256 */
4257static DECLCALLBACK(int) rtFsIsoMakerOutFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
4258{
4259 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4260 PRTFSISOMAKERINT pIsoMaker = pThis->pIsoMaker;
4261 size_t cbBuf = pSgBuf->paSegs[0].cbSeg;
4262 uint8_t *pbBuf = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
4263
4264 Assert(pSgBuf->cSegs == 1);
4265 RT_NOREF(fBlocking);
4266
4267 /*
4268 * Process the offset, checking for end-of-file.
4269 */
4270 uint64_t offUnsigned;
4271 if (off < 0)
4272 offUnsigned = pThis->offCurPos;
4273 else
4274 offUnsigned = (uint64_t)off;
4275 if (offUnsigned >= pIsoMaker->cbFinalizedImage)
4276 {
4277 if (*pcbRead)
4278 {
4279 *pcbRead = 0;
4280 return VINF_EOF;
4281 }
4282 return VERR_EOF;
4283 }
4284 if ( !pcbRead
4285 && pIsoMaker->cbFinalizedImage - offUnsigned < cbBuf)
4286 return VERR_EOF;
4287
4288 /*
4289 * Produce the bytes.
4290 */
4291 int rc = VINF_SUCCESS;
4292 size_t cbRead = 0;
4293 while (cbBuf > 0)
4294 {
4295 size_t cbDone;
4296
4297 /* Betting on there being more file data than metadata, thus doing the
4298 offset switch in decending order. */
4299 if (offUnsigned >= pIsoMaker->offFirstFile)
4300 {
4301 if (offUnsigned < pIsoMaker->cbFinalizedImage)
4302 {
4303 rc = rtFsIsoMakerOutFile_ReadFileData(pThis, pIsoMaker, offUnsigned, pbBuf, cbBuf, &cbDone);
4304 if (RT_FAILURE(rc))
4305 break;
4306 }
4307 else
4308 {
4309 rc = pcbRead ? VINF_EOF : VERR_EOF;
4310 break;
4311 }
4312 }
4313 /*
4314 * Joliet directory structures.
4315 */
4316 else if ( offUnsigned >= pIsoMaker->JolietDirs.offDirs
4317 && pIsoMaker->JolietDirs.offDirs < pIsoMaker->JolietDirs.offPathTableL)
4318 cbDone = rtFsIsoMakerOutFile_ReadDirStructures(pThis, &pIsoMaker->Joliet, &pIsoMaker->JolietDirs,
4319 offUnsigned, pbBuf, cbBuf);
4320 /*
4321 * Primary ISO directory structures.
4322 */
4323 else if (offUnsigned >= pIsoMaker->PrimaryIsoDirs.offDirs)
4324 cbDone = rtFsIsoMakerOutFile_ReadDirStructures(pThis, &pIsoMaker->PrimaryIso, &pIsoMaker->PrimaryIsoDirs,
4325 offUnsigned, pbBuf, cbBuf);
4326 /** @todo Insert El Torito stuff here? Probably okay to let it be in the file
4327 * area, right? */
4328 /*
4329 * Volume descriptors.
4330 */
4331 else if (offUnsigned >= _32K)
4332 {
4333 size_t offVolDescs = (size_t)offUnsigned - _32K;
4334 cbDone = RT_MIN(cbBuf, (pIsoMaker->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE) - offVolDescs);
4335 memcpy(pbBuf, &pIsoMaker->pbVolDescs[offVolDescs], cbDone);
4336 }
4337 /*
4338 * Zeros in the system area.
4339 */
4340 else if (offUnsigned >= pIsoMaker->cbSysArea)
4341 {
4342 cbDone = RT_MIN(cbBuf, _32K - (size_t)offUnsigned);
4343 memset(pbBuf, 0, cbDone);
4344 }
4345 /*
4346 * Actual data in the system area.
4347 */
4348 else
4349 {
4350 cbDone = RT_MIN(cbBuf, pIsoMaker->cbSysArea - (size_t)offUnsigned);
4351 memcpy(pbBuf, &pIsoMaker->pbSysArea[(size_t)offUnsigned], cbDone);
4352 }
4353
4354 /*
4355 * Common advance.
4356 */
4357 cbRead += cbDone;
4358 offUnsigned += cbDone;
4359 pbBuf += cbDone;
4360 cbBuf -= cbDone;
4361 }
4362
4363 if (pcbRead)
4364 *pcbRead = cbRead;
4365 return rc;
4366}
4367
4368
4369/**
4370 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
4371 */
4372static DECLCALLBACK(int) rtFsIsoMakerOutFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
4373{
4374 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
4375 return VERR_WRITE_PROTECT;
4376}
4377
4378
4379/**
4380 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
4381 */
4382static DECLCALLBACK(int) rtFsIsoMakerOutFile_Flush(void *pvThis)
4383{
4384 RT_NOREF(pvThis);
4385 return VINF_SUCCESS;
4386}
4387
4388
4389/**
4390 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
4391 */
4392static DECLCALLBACK(int) rtFsIsoMakerOutFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
4393 uint32_t *pfRetEvents)
4394{
4395 NOREF(pvThis);
4396 return RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
4397}
4398
4399
4400/**
4401 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
4402 */
4403static DECLCALLBACK(int) rtFsIsoMakerOutFile_Tell(void *pvThis, PRTFOFF poffActual)
4404{
4405 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4406 *poffActual = pThis->offCurPos;
4407 return VINF_SUCCESS;
4408}
4409
4410
4411/**
4412 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
4413 */
4414static DECLCALLBACK(int) rtFsIsoMakerOutFile_Skip(void *pvThis, RTFOFF cb)
4415{
4416 RTFOFF offIgnored;
4417 return rtFsIsoMakerOutFile_Seek(pvThis, cb, RTFILE_SEEK_CURRENT, &offIgnored);
4418}
4419
4420
4421/**
4422 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
4423 */
4424static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
4425{
4426 RT_NOREF(pvThis, fMode, fMask);
4427 return VERR_WRITE_PROTECT;
4428}
4429
4430
4431/**
4432 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
4433 */
4434static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
4435 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
4436{
4437 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
4438 return VERR_WRITE_PROTECT;
4439}
4440
4441
4442/**
4443 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
4444 */
4445static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
4446{
4447 RT_NOREF(pvThis, uid, gid);
4448 return VERR_WRITE_PROTECT;
4449}
4450
4451
4452/**
4453 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
4454 */
4455static DECLCALLBACK(int) rtFsIsoMakerOutFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
4456{
4457 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4458
4459 /*
4460 * Seek relative to which position.
4461 */
4462 uint64_t offWrt;
4463 switch (uMethod)
4464 {
4465 case RTFILE_SEEK_BEGIN:
4466 offWrt = 0;
4467 break;
4468
4469 case RTFILE_SEEK_CURRENT:
4470 offWrt = pThis->offCurPos;
4471 break;
4472
4473 case RTFILE_SEEK_END:
4474 offWrt = pThis->pIsoMaker->cbFinalizedImage;
4475 break;
4476
4477 default:
4478 return VERR_INTERNAL_ERROR_5;
4479 }
4480
4481 /*
4482 * Calc new position, take care to stay within RTFOFF type bounds.
4483 */
4484 uint64_t offNew;
4485 if (offSeek == 0)
4486 offNew = offWrt;
4487 else if (offSeek > 0)
4488 {
4489 offNew = offWrt + offSeek;
4490 if ( offNew < offWrt
4491 || offNew > RTFOFF_MAX)
4492 offNew = RTFOFF_MAX;
4493 }
4494 else if ((uint64_t)-offSeek < offWrt)
4495 offNew = offWrt + offSeek;
4496 else
4497 offNew = 0;
4498 pThis->offCurPos = offNew;
4499
4500 *poffActual = offNew;
4501 return VINF_SUCCESS;
4502}
4503
4504
4505/**
4506 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
4507 */
4508static DECLCALLBACK(int) rtFsIsoMakerOutFile_QuerySize(void *pvThis, uint64_t *pcbFile)
4509{
4510 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4511 *pcbFile = pThis->pIsoMaker->cbFinalizedImage;
4512 return VINF_SUCCESS;
4513}
4514
4515
4516/**
4517 * Standard file operations.
4518 */
4519DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIsoMakerOutputFileOps =
4520{
4521 { /* Stream */
4522 { /* Obj */
4523 RTVFSOBJOPS_VERSION,
4524 RTVFSOBJTYPE_FILE,
4525 "ISO Maker Output File",
4526 rtFsIsoMakerOutFile_Close,
4527 rtFsIsoMakerOutFile_QueryInfo,
4528 RTVFSOBJOPS_VERSION
4529 },
4530 RTVFSIOSTREAMOPS_VERSION,
4531 RTVFSIOSTREAMOPS_FEAT_NO_SG,
4532 rtFsIsoMakerOutFile_Read,
4533 rtFsIsoMakerOutFile_Write,
4534 rtFsIsoMakerOutFile_Flush,
4535 rtFsIsoMakerOutFile_PollOne,
4536 rtFsIsoMakerOutFile_Tell,
4537 rtFsIsoMakerOutFile_Skip,
4538 NULL /*ZeroFill*/,
4539 RTVFSIOSTREAMOPS_VERSION,
4540 },
4541 RTVFSFILEOPS_VERSION,
4542 0,
4543 { /* ObjSet */
4544 RTVFSOBJSETOPS_VERSION,
4545 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
4546 rtFsIsoMakerOutFile_SetMode,
4547 rtFsIsoMakerOutFile_SetTimes,
4548 rtFsIsoMakerOutFile_SetOwner,
4549 RTVFSOBJSETOPS_VERSION
4550 },
4551 rtFsIsoMakerOutFile_Seek,
4552 rtFsIsoMakerOutFile_QuerySize,
4553 RTVFSFILEOPS_VERSION
4554};
4555
4556
4557
4558/**
4559 * Creates a VFS file for a finalized ISO maker instanced.
4560 *
4561 * The file can be used to access the image. Both sequential and random access
4562 * are supported, so that this could in theory be hooked up to a CD/DVD-ROM
4563 * drive emulation and used as a virtual ISO image.
4564 *
4565 * @returns IRPT status code.
4566 * @param hIsoMaker The ISO maker handle.
4567 * @param phVfsFile Where to return the handle.
4568 */
4569RTDECL(int) RTFsIsoMakerCreateVfsOutputFile(RTFSISOMAKER hIsoMaker, PRTVFSFILE phVfsFile)
4570{
4571 PRTFSISOMAKERINT pThis = hIsoMaker;
4572 RTFSISOMAKER_ASSER_VALID_HANDLE_RET(pThis);
4573 AssertReturn(pThis->fFinalized, VERR_WRONG_ORDER);
4574 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
4575
4576 uint32_t cRefs = RTFsIsoMakerRetain(pThis);
4577 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
4578
4579 PRTFSISOMAKEROUTPUTFILE pFileData;
4580 RTVFSFILE hVfsFile;
4581 int rc = RTVfsNewFile(&g_rtFsIsoMakerOutputFileOps, sizeof(*pFileData), RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_CREATE,
4582 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pFileData);
4583 if (RT_SUCCESS(rc))
4584 {
4585 pFileData->pIsoMaker = pThis;
4586 pFileData->offCurPos = 0;
4587 pFileData->pFileHint = NULL;
4588 pFileData->hVfsSrcFile = NIL_RTVFSFILE;
4589 pFileData->pDirHint = NULL;
4590 return VINF_SUCCESS;
4591 }
4592
4593 RTFsIsoMakerRelease(pThis);
4594 *phVfsFile = NIL_RTVFSFILE;
4595 return rc;
4596}
4597
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