VirtualBox

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

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

IPRT: More ISO maker code (booting related).

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