VirtualBox

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

最後變更 在這個檔案從67794是 67794,由 vboxsync 提交於 8 年 前

isomaker: Added API for setting the rock ridge name and adjusted RTFsIsoMakerAddUnnamedDir to take additional attributes.

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

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