VirtualBox

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

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

IPRT: More ISO maker code (import + booting).

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 217.1 KB
 
1/* $Id: isomaker.cpp 67605 2017-06-26 12:00:45Z 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, 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, &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, &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 * Enables or disable syslinux boot info table patching of a file.
2620 *
2621 * @returns IPRT status code.
2622 * @param hIsoMaker The ISO maker handle.
2623 * @param idxObj The configuration index.
2624 * @param fEnable Whether to enable or disable patching.
2625 */
2626RTDECL(int) RTFsIsoMakerObjEnableBootInfoTablePatching(RTFSISOMAKER hIsoMaker, uint32_t idxObj, bool fEnable)
2627{
2628 /*
2629 * Validate and translate input.
2630 */
2631 PRTFSISOMAKERINT pThis = hIsoMaker;
2632 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2633 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2634 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2635 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2636 AssertReturn(pObj->enmType == RTFSISOMAKEROBJTYPE_FILE, VERR_WRONG_TYPE);
2637 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
2638 AssertReturn( pFile->enmSrcType == RTFSISOMAKERSRCTYPE_PATH
2639 || pFile->enmSrcType == RTFSISOMAKERSRCTYPE_VFS_FILE
2640 || pFile->enmSrcType == RTFSISOMAKERSRCTYPE_COMMON,
2641 VERR_WRONG_TYPE);
2642
2643 /*
2644 * Do the job.
2645 */
2646 if (fEnable)
2647 {
2648 if (!pFile->pBootInfoTable)
2649 {
2650 pFile->pBootInfoTable = (PISO9660SYSLINUXINFOTABLE)RTMemAllocZ(sizeof(*pFile->pBootInfoTable));
2651 AssertReturn(pFile->pBootInfoTable, VERR_NO_MEMORY);
2652 }
2653 }
2654 else if (pFile->pBootInfoTable)
2655 {
2656 RTMemFree(pFile->pBootInfoTable);
2657 pFile->pBootInfoTable = NULL;
2658 }
2659 return VINF_SUCCESS;
2660}
2661
2662
2663/**
2664 * Gets the data size of an object.
2665 *
2666 * Currently only supported on file objects.
2667 *
2668 * @returns IPRT status code.
2669 * @param hIsoMaker The ISO maker handle.
2670 * @param idxObj The configuration index.
2671 * @param pcbData Where to return the size.
2672 */
2673RTDECL(int) RTFsIsoMakerObjQueryDataSize(RTFSISOMAKER hIsoMaker, uint32_t idxObj, uint64_t *pcbData)
2674{
2675 /*
2676 * Validate and translate input.
2677 */
2678 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
2679 *pcbData = UINT64_MAX;
2680 PRTFSISOMAKERINT pThis = hIsoMaker;
2681 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2682 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2683 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2684
2685 /*
2686 * Do the job.
2687 */
2688 if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
2689 {
2690 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
2691 if (pFile->enmSrcType != RTFSISOMAKERSRCTYPE_TRANS_TBL)
2692 {
2693 *pcbData = ((PRTFSISOMAKERFILE)pObj)->cbData;
2694 return VINF_SUCCESS;
2695 }
2696 }
2697 return VERR_WRONG_TYPE;
2698}
2699
2700
2701/**
2702 * Initalizes the common part of a file system object and links it into global
2703 * chain.
2704 *
2705 * @returns IPRT status code
2706 * @param pThis The ISO maker instance.
2707 * @param pObj The common object.
2708 * @param enmType The object type.
2709 * @param pObjInfo The object information (typically source).
2710 * Optional.
2711 */
2712static int rtFsIsoMakerInitCommonObj(PRTFSISOMAKERINT pThis, PRTFSISOMAKEROBJ pObj,
2713 RTFSISOMAKEROBJTYPE enmType, PCRTFSOBJINFO pObjInfo)
2714{
2715 Assert(!pThis->fFinalized);
2716 AssertReturn(pThis->cObjects < RTFSISOMAKER_MAX_OBJECTS, VERR_OUT_OF_RANGE);
2717
2718 pObj->enmType = enmType;
2719 pObj->pPrimaryName = NULL;
2720 pObj->pJolietName = NULL;
2721 pObj->pUdfName = NULL;
2722 pObj->pHfsName = NULL;
2723 pObj->idxObj = pThis->cObjects++;
2724 pObj->cNotOrphan = 0;
2725 if (pObjInfo)
2726 {
2727 pObj->BirthTime = pObjInfo->BirthTime;
2728 pObj->ChangeTime = pObjInfo->ChangeTime;
2729 pObj->ModificationTime = pObjInfo->ModificationTime;
2730 pObj->AccessedTime = pObjInfo->AccessTime;
2731 pObj->fMode = pObjInfo->Attr.fMode;
2732 pObj->uid = pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : pThis->uidDefault;
2733 pObj->gid = pObjInfo->Attr.u.Unix.gid != NIL_RTGID ? pObjInfo->Attr.u.Unix.gid : pThis->gidDefault;
2734 }
2735 else
2736 {
2737 pObj->BirthTime = pThis->ImageCreationTime;
2738 pObj->ChangeTime = pThis->ImageCreationTime;
2739 pObj->ModificationTime = pThis->ImageCreationTime;
2740 pObj->AccessedTime = pThis->ImageCreationTime;
2741 pObj->fMode = enmType == RTFSISOMAKEROBJTYPE_DIR ? pThis->fDefaultDirMode : pThis->fDefaultFileMode;
2742 pObj->uid = pThis->uidDefault;
2743 pObj->gid = pThis->gidDefault;
2744 }
2745
2746 RTListAppend(&pThis->ObjectHead, &pObj->Entry);
2747 return VINF_SUCCESS;
2748}
2749
2750
2751/**
2752 * Internal function for adding an unnamed directory.
2753 *
2754 * @returns IPRT status code.
2755 * @param pThis The ISO make instance.
2756 * @param ppDir Where to return the directory.
2757 */
2758static int rtFsIsoMakerAddUnnamedDirWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKERDIR *ppDir)
2759{
2760 PRTFSISOMAKERDIR pDir = (PRTFSISOMAKERDIR)RTMemAllocZ(sizeof(*pDir));
2761 AssertReturn(pDir, VERR_NO_MEMORY);
2762 int rc = rtFsIsoMakerInitCommonObj(pThis, &pDir->Core, RTFSISOMAKEROBJTYPE_DIR, NULL);
2763 if (RT_SUCCESS(rc))
2764 {
2765 *ppDir = pDir;
2766 return VINF_SUCCESS;
2767 }
2768 RTMemFree(pDir);
2769 return rc;
2770
2771}
2772
2773
2774/**
2775 * Adds an unnamed directory to the image.
2776 *
2777 * The directory must explictly be entered into the desired namespaces.
2778 *
2779 * @returns IPRT status code
2780 * @param hIsoMaker The ISO maker handle.
2781 * @param pidxObj Where to return the configuration index of the
2782 * directory.
2783 * @sa RTFsIsoMakerAddDir, RTFsIsoMakerObjSetPath
2784 */
2785RTDECL(int) RTFsIsoMakerAddUnnamedDir(RTFSISOMAKER hIsoMaker, uint32_t *pidxObj)
2786{
2787 PRTFSISOMAKERINT pThis = hIsoMaker;
2788 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2789 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2790 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2791
2792 PRTFSISOMAKERDIR pDir;
2793 int rc = rtFsIsoMakerAddUnnamedDirWorker(pThis, &pDir);
2794 *pidxObj = RT_SUCCESS(rc) ? pDir->Core.idxObj : UINT32_MAX;
2795 return rc;
2796}
2797
2798
2799/**
2800 * Adds a directory to the image in all namespaces and default attributes.
2801 *
2802 * @returns IPRT status code
2803 * @param hIsoMaker The ISO maker handle.
2804 * @param pszDir The path (UTF-8) to the directory in the ISO.
2805 *
2806 * @param pidxObj Where to return the configuration index of the
2807 * directory. Optional.
2808 * @sa RTFsIsoMakerAddUnnamedDir, RTFsIsoMakerObjSetPath
2809 */
2810RTDECL(int) RTFsIsoMakerAddDir(RTFSISOMAKER hIsoMaker, const char *pszDir, uint32_t *pidxObj)
2811{
2812 PRTFSISOMAKERINT pThis = hIsoMaker;
2813 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2814 AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
2815 AssertReturn(RTPATH_IS_SLASH(*pszDir), VERR_INVALID_NAME);
2816
2817 uint32_t idxObj;
2818 int rc = RTFsIsoMakerAddUnnamedDir(hIsoMaker, &idxObj);
2819 if (RT_SUCCESS(rc))
2820 {
2821 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszDir);
2822 if (RT_SUCCESS(rc))
2823 {
2824 if (pidxObj)
2825 *pidxObj = idxObj;
2826 }
2827 else
2828 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
2829 }
2830 return rc;
2831}
2832
2833
2834/**
2835 * Internal function for adding an unnamed file.
2836 *
2837 * @returns IPRT status code.
2838 * @param pThis The ISO make instance.
2839 * @param pObjInfo Object information. Optional.
2840 * @param cbExtra Extra space for additional data (e.g. source
2841 * path string copy).
2842 * @param ppFile Where to return the file.
2843 */
2844static int rtFsIsoMakerAddUnnamedFileWorker(PRTFSISOMAKERINT pThis, PCRTFSOBJINFO pObjInfo, size_t cbExtra,
2845 PRTFSISOMAKERFILE *ppFile)
2846{
2847 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)RTMemAllocZ(sizeof(*pFile) + cbExtra);
2848 AssertReturn(pFile, VERR_NO_MEMORY);
2849 int rc = rtFsIsoMakerInitCommonObj(pThis, &pFile->Core, RTFSISOMAKEROBJTYPE_FILE, pObjInfo);
2850 if (RT_SUCCESS(rc))
2851 {
2852 pFile->cbData = pObjInfo ? pObjInfo->cbObject : 0;
2853 pThis->cbData += RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE);
2854 pFile->offData = UINT64_MAX;
2855 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_INVALID;
2856 pFile->u.pszSrcPath = NULL;
2857 pFile->pBootInfoTable = NULL;
2858 RTListInit(&pFile->FinalizedEntry);
2859
2860 *ppFile = pFile;
2861 return VINF_SUCCESS;
2862 }
2863 RTMemFree(pFile);
2864 return rc;
2865
2866}
2867
2868
2869/**
2870 * Adds an unnamed file to the image that's backed by a host file.
2871 *
2872 * The file must explictly be entered into the desired namespaces.
2873 *
2874 * @returns IPRT status code
2875 * @param hIsoMaker The ISO maker handle.
2876 * @param pszSrcFile The source file path. VFS chain spec allowed.
2877 * @param pidxObj Where to return the configuration index of the
2878 * directory.
2879 * @sa RTFsIsoMakerAddFile, RTFsIsoMakerObjSetPath
2880 */
2881RTDECL(int) RTFsIsoMakerAddUnnamedFileWithSrcPath(RTFSISOMAKER hIsoMaker, const char *pszSrcFile, uint32_t *pidxObj)
2882{
2883 PRTFSISOMAKERINT pThis = hIsoMaker;
2884 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2885 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2886 *pidxObj = UINT32_MAX;
2887 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2888
2889 /*
2890 * Check that the source file exists and is a file.
2891 */
2892 uint32_t offError = 0;
2893 RTFSOBJINFO ObjInfo;
2894 int rc = RTVfsChainQueryInfo(pszSrcFile, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK, &offError, NULL);
2895 AssertMsgRCReturn(rc, ("%s -> %Rrc offError=%u\n", pszSrcFile, rc, offError), rc);
2896 AssertMsgReturn(RTFS_IS_FILE(ObjInfo.Attr.fMode), ("%#x - %s\n", ObjInfo.Attr.fMode, pszSrcFile), VERR_NOT_A_FILE);
2897
2898 /*
2899 * Create a file object for it.
2900 */
2901 size_t const cbSrcFile = strlen(pszSrcFile) + 1;
2902 PRTFSISOMAKERFILE pFile;
2903 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, &ObjInfo, cbSrcFile, &pFile);
2904 if (RT_SUCCESS(rc))
2905 {
2906 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_PATH;
2907 pFile->u.pszSrcPath = (char *)memcpy(pFile + 1, pszSrcFile, cbSrcFile);
2908
2909 *pidxObj = pFile->Core.idxObj;
2910 }
2911 return rc;
2912}
2913
2914
2915/**
2916 * Adds an unnamed file to the image that's backed by a VFS file.
2917 *
2918 * The file must explictly be entered into the desired namespaces.
2919 *
2920 * @returns IPRT status code
2921 * @param hIsoMaker The ISO maker handle.
2922 * @param hVfsFileSrc The source file handle.
2923 * @param pidxObj Where to return the configuration index of the
2924 * directory.
2925 * @sa RTFsIsoMakerAddUnnamedFileWithSrcPath, RTFsIsoMakerObjSetPath
2926 */
2927RTDECL(int) RTFsIsoMakerAddUnnamedFileWithVfsFile(RTFSISOMAKER hIsoMaker, RTVFSFILE hVfsFileSrc, uint32_t *pidxObj)
2928{
2929 PRTFSISOMAKERINT pThis = hIsoMaker;
2930 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2931 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2932 *pidxObj = UINT32_MAX;
2933 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2934
2935 /*
2936 * Get the VFS file info. This implicitly validates the handle.
2937 */
2938 RTFSOBJINFO ObjInfo;
2939 int rc = RTVfsFileQueryInfo(hVfsFileSrc, &ObjInfo, RTFSOBJATTRADD_UNIX);
2940 AssertMsgRCReturn(rc, ("RTVfsFileQueryInfo(%p) -> %Rrc\n", hVfsFileSrc, rc), rc);
2941
2942 /*
2943 * Retain a reference to the file.
2944 */
2945 uint32_t cRefs = RTVfsFileRetain(hVfsFileSrc);
2946 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2947
2948 /*
2949 * Create a file object for it.
2950 */
2951 PRTFSISOMAKERFILE pFile;
2952 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, &ObjInfo, 0, &pFile);
2953 if (RT_SUCCESS(rc))
2954 {
2955 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_VFS_FILE;
2956 pFile->u.hVfsFile = hVfsFileSrc;
2957
2958 *pidxObj = pFile->Core.idxObj;
2959 }
2960 else
2961 RTVfsFileRelease(hVfsFileSrc);
2962 return rc;
2963}
2964
2965
2966/**
2967 * Adds an unnamed file to the image that's backed by a portion of a common
2968 * source file.
2969 *
2970 * The file must explictly be entered into the desired namespaces.
2971 *
2972 * @returns IPRT status code
2973 * @param hIsoMaker The ISO maker handle.
2974 * @param idxCommonSrc The common source file index.
2975 * @param offData The offset of the data in the source file.
2976 * @param cbData The file size.
2977 * @param pObjInfo Pointer to file info. Optional.
2978 * @param pidxObj Where to return the configuration index of the
2979 * directory.
2980 * @sa RTFsIsoMakerAddUnnamedFileWithSrcPath, RTFsIsoMakerObjSetPath
2981 */
2982RTDECL(int) RTFsIsoMakerAddUnnamedFileWithCommonSrc(RTFSISOMAKER hIsoMaker, uint32_t idxCommonSrc,
2983 uint64_t offData, uint64_t cbData, PCRTFSOBJINFO pObjInfo, uint32_t *pidxObj)
2984{
2985 /*
2986 * Validate and fake input.
2987 */
2988 PRTFSISOMAKERINT pThis = hIsoMaker;
2989 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2990 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2991 *pidxObj = UINT32_MAX;
2992 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2993 AssertReturn(idxCommonSrc < pThis->cCommonSources, VERR_INVALID_PARAMETER);
2994 AssertReturn(offData < (uint64_t)RTFOFF_MAX, VERR_OUT_OF_RANGE);
2995 AssertReturn(cbData < (uint64_t)RTFOFF_MAX, VERR_OUT_OF_RANGE);
2996 AssertReturn(offData + cbData < (uint64_t)RTFOFF_MAX, VERR_OUT_OF_RANGE);
2997 RTFSOBJINFO ObjInfo;
2998 if (!pObjInfo)
2999 {
3000 ObjInfo.cbObject = cbData;
3001 ObjInfo.cbAllocated = cbData;
3002 ObjInfo.BirthTime = pThis->ImageCreationTime;
3003 ObjInfo.ChangeTime = pThis->ImageCreationTime;
3004 ObjInfo.ModificationTime = pThis->ImageCreationTime;
3005 ObjInfo.AccessTime = pThis->ImageCreationTime;
3006 ObjInfo.Attr.fMode = pThis->fDefaultFileMode;
3007 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
3008 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
3009 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
3010 ObjInfo.Attr.u.Unix.cHardlinks = 1;
3011 ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
3012 ObjInfo.Attr.u.Unix.INodeId = 0;
3013 ObjInfo.Attr.u.Unix.fFlags = 0;
3014 ObjInfo.Attr.u.Unix.GenerationId = 0;
3015 ObjInfo.Attr.u.Unix.Device = 0;
3016 pObjInfo = &ObjInfo;
3017 }
3018 else
3019 {
3020 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
3021 AssertReturn(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX, VERR_WRONG_TYPE);
3022 AssertReturn((uint64_t)pObjInfo->cbObject == cbData, VERR_INVALID_PARAMETER);
3023 }
3024
3025 /*
3026 * Create a file object for it.
3027 */
3028 PRTFSISOMAKERFILE pFile;
3029 int rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, pObjInfo, 0, &pFile);
3030 if (RT_SUCCESS(rc))
3031 {
3032 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_COMMON;
3033 pFile->u.Common.idxSrc = idxCommonSrc;
3034 pFile->u.Common.offData = offData;
3035
3036 *pidxObj = pFile->Core.idxObj;
3037 }
3038 return rc;
3039}
3040
3041
3042/**
3043 * Adds a common source file.
3044 *
3045 * Using RTFsIsoMakerAddUnnamedFileWithCommonSrc a sections common source file
3046 * can be referenced to make up other files. The typical use case is when
3047 * importing data from an existing ISO.
3048 *
3049 * @returns IPRT status code
3050 * @param hIsoMaker The ISO maker handle.
3051 * @param hVfsFile VFS handle of the common source. (A reference
3052 * is added, none consumed.)
3053 * @param pidxCommonSrc Where to return the assigned common source
3054 * index. This is used to reference the file.
3055 * @sa RTFsIsoMakerAddUnnamedFileWithCommonSrc
3056 */
3057RTDECL(int) RTFsIsoMakerAddCommonSourceFile(RTFSISOMAKER hIsoMaker, RTVFSFILE hVfsFile, uint32_t *pidxCommonSrc)
3058{
3059 /*
3060 * Validate input.
3061 */
3062 PRTFSISOMAKERINT pThis = hIsoMaker;
3063 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3064 AssertPtrReturn(pidxCommonSrc, VERR_INVALID_POINTER);
3065 *pidxCommonSrc = UINT32_MAX;
3066 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
3067
3068 /*
3069 * Resize the common source array if necessary.
3070 */
3071 if ((pThis->cCommonSources & 15) == 0)
3072 {
3073 void *pvNew = RTMemRealloc(pThis->paCommonSources, (pThis->cCommonSources + 16) * sizeof(pThis->paCommonSources[0]));
3074 AssertReturn(pvNew, VERR_NO_MEMORY);
3075 pThis->paCommonSources = (PRTVFSFILE)pvNew;
3076 }
3077
3078 /*
3079 * Retain a reference to the source file, thereby validating the handle.
3080 * Then add it to the array.
3081 */
3082 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
3083 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
3084
3085 uint32_t idx = pThis->cCommonSources++;
3086 pThis->paCommonSources[idx] = hVfsFile;
3087
3088 *pidxCommonSrc = idx;
3089 return VINF_SUCCESS;
3090}
3091
3092
3093/**
3094 * Adds a file that's backed by a host file to the image in all namespaces and
3095 * with attributes taken from the source file.
3096 *
3097 * @returns IPRT status code
3098 * @param hIsoMaker The ISO maker handle.
3099 * @param pszFile The path to the file in the image.
3100 * @param pszSrcFile The source file path. VFS chain spec allowed.
3101 * @param pidxObj Where to return the configuration index of the file.
3102 * Optional
3103 * @sa RTFsIsoMakerAddFileWithVfsFile,
3104 * RTFsIsoMakerAddUnnamedFileWithSrcPath
3105 */
3106RTDECL(int) RTFsIsoMakerAddFileWithSrcPath(RTFSISOMAKER hIsoMaker, const char *pszFile, const char *pszSrcFile, uint32_t *pidxObj)
3107{
3108 PRTFSISOMAKERINT pThis = hIsoMaker;
3109 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3110 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
3111 AssertReturn(RTPATH_IS_SLASH(*pszFile), VERR_INVALID_NAME);
3112
3113 uint32_t idxObj;
3114 int rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(hIsoMaker, pszSrcFile, &idxObj);
3115 if (RT_SUCCESS(rc))
3116 {
3117 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszFile);
3118 if (RT_SUCCESS(rc))
3119 {
3120 if (pidxObj)
3121 *pidxObj = idxObj;
3122 }
3123 else
3124 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
3125 }
3126 return rc;
3127}
3128
3129
3130/**
3131 * Adds a file that's backed by a VFS file to the image in all namespaces and
3132 * with attributes taken from the source file.
3133 *
3134 * @returns IPRT status code
3135 * @param hIsoMaker The ISO maker handle.
3136 * @param pszFile The path to the file in the image.
3137 * @param hVfsFileSrc The source file handle.
3138 * @param pidxObj Where to return the configuration index of the file.
3139 * Optional.
3140 * @sa RTFsIsoMakerAddUnnamedFileWithVfsFile,
3141 * RTFsIsoMakerAddFileWithSrcPath
3142 */
3143RTDECL(int) RTFsIsoMakerAddFileWithVfsFile(RTFSISOMAKER hIsoMaker, const char *pszFile, RTVFSFILE hVfsFileSrc, uint32_t *pidxObj)
3144{
3145 PRTFSISOMAKERINT pThis = hIsoMaker;
3146 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3147 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
3148 AssertReturn(RTPATH_IS_SLASH(*pszFile), VERR_INVALID_NAME);
3149
3150 uint32_t idxObj;
3151 int rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(hIsoMaker, hVfsFileSrc, &idxObj);
3152 if (RT_SUCCESS(rc))
3153 {
3154 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszFile);
3155 if (RT_SUCCESS(rc))
3156 {
3157 if (pidxObj)
3158 *pidxObj = idxObj;
3159 }
3160 else
3161 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
3162 }
3163 return rc;
3164}
3165
3166
3167
3168
3169/*
3170 *
3171 * El Torito Booting.
3172 * El Torito Booting.
3173 * El Torito Booting.
3174 * El Torito Booting.
3175 *
3176 */
3177
3178/**
3179 * Ensures that we've got a boot catalog file.
3180 *
3181 * @returns IPRT status code.
3182 * @param pThis The ISO maker instance.
3183 */
3184static int rtFsIsoMakerEnsureBootCatFile(PRTFSISOMAKERINT pThis)
3185{
3186 if (pThis->pBootCatFile)
3187 return VINF_SUCCESS;
3188
3189 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
3190
3191 /* Create a VFS memory file for backing up the file. */
3192 RTVFSFILE hVfsFile;
3193 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, RTFSISOMAKER_SECTOR_SIZE, &hVfsFile);
3194 if (RT_SUCCESS(rc))
3195 {
3196 /* Create an unnamed VFS backed file and mark it as non-orphaned. */
3197 PRTFSISOMAKERFILE pFile;
3198 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, NULL, 0, &pFile);
3199 if (RT_SUCCESS(rc))
3200 {
3201 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_VFS_FILE;
3202 pFile->u.hVfsFile = hVfsFile;
3203 pFile->Core.cNotOrphan = 1;
3204
3205 /* Save file pointer and allocate a volume descriptor. */
3206 pThis->pBootCatFile = pFile;
3207 pThis->cVolumeDescriptors++;
3208
3209 return VINF_SUCCESS;
3210 }
3211 RTVfsFileRelease(hVfsFile);
3212 }
3213 return rc;
3214}
3215
3216
3217/**
3218 * Queries the configuration index of the boot catalog file object.
3219 *
3220 * The boot catalog file is created as necessary, thus this have to be a query
3221 * rather than a getter since object creation may fail.
3222 *
3223 * @returns IPRT status code.
3224 * @param hIsoMaker The ISO maker handle.
3225 * @param pidxObj Where to return the configuration index.
3226 */
3227RTDECL(int) RTFsIsoMakerQueryObjIdxForBootCatalog(RTFSISOMAKER hIsoMaker, uint32_t *pidxObj)
3228{
3229 /*
3230 * Validate input.
3231 */
3232 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
3233 *pidxObj = UINT32_MAX;
3234 PRTFSISOMAKERINT pThis = hIsoMaker;
3235 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3236
3237 /*
3238 * Do the job.
3239 */
3240 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3241 if (RT_SUCCESS(rc))
3242 *pidxObj = pThis->pBootCatFile->Core.idxObj;
3243 return rc;
3244}
3245
3246
3247/**
3248 * Sets the boot catalog backing file.
3249 *
3250 * The content of the given file will be discarded and replaced with the boot
3251 * catalog, the naming and file attributes (other than size) will be retained.
3252 *
3253 * This API exists mainly to assist when importing ISOs.
3254 *
3255 * @returns IPRT status code.
3256 * @param hIsoMaker The ISO maker handle.
3257 * @param idxObj The configuration index of the file.
3258 */
3259RTDECL(int) RTFsIsoMakerBootCatSetFile(RTFSISOMAKER hIsoMaker, uint32_t idxObj)
3260{
3261 /*
3262 * Validate and translate input.
3263 */
3264 PRTFSISOMAKERINT pThis = hIsoMaker;
3265 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3266
3267 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
3268 AssertReturn(pObj, VERR_OUT_OF_RANGE);
3269 AssertReturn(pObj->enmType == RTFSISOMAKEROBJTYPE_FILE, VERR_WRONG_TYPE);
3270 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
3271 AssertReturn( pFile->enmSrcType == RTFSISOMAKERSRCTYPE_PATH
3272 || pFile->enmSrcType == RTFSISOMAKERSRCTYPE_COMMON
3273 || pFile->enmSrcType == RTFSISOMAKERSRCTYPE_VFS_FILE,
3274 VERR_WRONG_TYPE);
3275
3276 /*
3277 * To reduce the possible combinations here, make sure there is a boot cat
3278 * file that we're "replacing".
3279 */
3280 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3281 if (RT_SUCCESS(rc))
3282 {
3283 /*
3284 * Grab a reference to the boot cat memory VFS so we can destroy it
3285 * later using regular destructors.
3286 */
3287 PRTFSISOMAKERFILE pOldFile = pThis->pBootCatFile;
3288 RTVFSFILE hVfsFile = pOldFile->u.hVfsFile;
3289 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
3290 if (cRefs != UINT32_MAX)
3291 {
3292 /*
3293 * Try remove the existing boot file.
3294 */
3295 pOldFile->Core.cNotOrphan--;
3296 pThis->pBootCatFile = NULL;
3297 rc = rtFsIsoMakerObjRemoveWorker(pThis, &pOldFile->Core);
3298 if (RT_SUCCESS(rc))
3299 {
3300 /*
3301 * Just morph pFile into a boot catalog file.
3302 */
3303 if (pFile->enmSrcType == RTFSISOMAKERSRCTYPE_VFS_FILE)
3304 {
3305 RTVfsFileRelease(pFile->u.hVfsFile);
3306 pFile->u.hVfsFile = NIL_RTVFSFILE;
3307 }
3308
3309 pThis->cbData -= RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE);
3310 pFile->cbData = 0;
3311 pFile->Core.cNotOrphan++;
3312 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_VFS_FILE;
3313 pFile->u.hVfsFile = hVfsFile;
3314
3315 pThis->pBootCatFile = pFile;
3316
3317 return VINF_SUCCESS;
3318 }
3319
3320 pThis->pBootCatFile = pOldFile;
3321 pOldFile->Core.cNotOrphan++;
3322 RTVfsFileRelease(hVfsFile);
3323 }
3324 else
3325 rc = VERR_INTERNAL_ERROR_2;
3326 }
3327 return rc;
3328}
3329
3330
3331/**
3332 * Set the validation entry of the boot catalog (this is the first entry).
3333 *
3334 * @returns IPRT status code.
3335 * @param hIsoMaker The ISO maker handle.
3336 * @param idPlatform The platform ID
3337 * (ISO9660_ELTORITO_PLATFORM_ID_XXX).
3338 * @param pszString CD/DVD-ROM identifier. Optional.
3339 */
3340RTDECL(int) RTFsIsoMakerBootCatSetValidationEntry(RTFSISOMAKER hIsoMaker, uint8_t idPlatform, const char *pszString)
3341{
3342 /*
3343 * Validate input.
3344 */
3345 PRTFSISOMAKERINT pThis = hIsoMaker;
3346 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3347 size_t cchString = 0;
3348 if (pszString)
3349 {
3350 cchString = RTStrCalcLatin1Len(pszString);
3351 AssertReturn(cchString < RT_SIZEOFMEMB(ISO9660ELTORITOVALIDATIONENTRY, achId), VERR_OUT_OF_RANGE);
3352 }
3353
3354 /*
3355 * Make sure we've got a boot file.
3356 */
3357 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3358 if (RT_SUCCESS(rc))
3359 {
3360 /*
3361 * Construct the entry data.
3362 */
3363 ISO9660ELTORITOVALIDATIONENTRY Entry;
3364 Entry.bHeaderId = ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY;
3365 Entry.bPlatformId = idPlatform;
3366 Entry.u16Reserved = 0;
3367 RT_ZERO(Entry.achId);
3368 if (cchString)
3369 {
3370 char *pszTmp = Entry.achId;
3371 rc = RTStrToLatin1Ex(pszString, RTSTR_MAX, &pszTmp, sizeof(Entry.achId), NULL);
3372 AssertRC(rc);
3373 }
3374 Entry.u16Checksum = 0;
3375 Entry.bKey1 = ISO9660_ELTORITO_KEY_BYTE_1;
3376 Entry.bKey2 = ISO9660_ELTORITO_KEY_BYTE_2;
3377
3378 /* Calc checksum. */
3379 uint16_t uSum = 0;
3380 uint16_t const *pu16Src = (uint16_t const *)&Entry;
3381 uint16_t cLeft = sizeof(Entry) / sizeof(uint16_t);
3382 while (cLeft-- > 0)
3383 {
3384 uSum += RT_LE2H_U16(*pu16Src);
3385 pu16Src++;
3386 }
3387 Entry.u16Checksum = RT_H2LE_U16((uint16_t)0 - uSum);
3388
3389 /*
3390 * Write the entry and update our internal tracker.
3391 */
3392 rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, 0, &Entry, sizeof(Entry), NULL);
3393 if (RT_SUCCESS(rc))
3394 {
3395 pThis->aBootCatEntries[0].bType = ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY;
3396 pThis->aBootCatEntries[0].cEntries = 2;
3397 }
3398 }
3399 return rc;
3400}
3401
3402
3403/**
3404 * Set the validation entry of the boot catalog (this is the first entry).
3405 *
3406 * @returns IPRT status code.
3407 * @param hIsoMaker The ISO maker handle.
3408 * @param idxBootCat The boot catalog entry. Zero and two are
3409 * invalid. Must be less than 63.
3410 * @param idxImageObj The configuration index of the boot image.
3411 * @param bBootMediaType The media type and flag (not for entry 1)
3412 * (ISO9660_ELTORITO_BOOT_MEDIA_TYPE_XXX,
3413 * ISO9660_ELTORITO_BOOT_MEDIA_F_XXX).
3414 * @param bSystemType The partitiona table system ID.
3415 * @param fBootable Whether it's a bootable entry or if we just want
3416 * the BIOS to setup the emulation without booting
3417 * it.
3418 * @param uLoadSeg The load address divided by 0x10 (i.e. the real
3419 * mode segment number).
3420 * @param cSectorsToLoad Number of emulated sectors to load.
3421 * @param bSelCritType The selection criteria type, if none pass
3422 * ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE.
3423 * @param pvSelCritData Pointer to the selection criteria data.
3424 * @param cbSelCritData Size of the selection criteria data.
3425 */
3426RTDECL(int) RTFsIsoMakerBootCatSetSectionEntry(RTFSISOMAKER hIsoMaker, uint32_t idxBootCat, uint32_t idxImageObj,
3427 uint8_t bBootMediaType, uint8_t bSystemType, bool fBootable,
3428 uint16_t uLoadSeg, uint16_t cSectorsToLoad,
3429 uint8_t bSelCritType, void const *pvSelCritData, size_t cbSelCritData)
3430{
3431 /*
3432 * Validate input.
3433 */
3434 PRTFSISOMAKERINT pThis = hIsoMaker;
3435 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3436 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)rtFsIsoMakerIndexToObj(pThis, idxImageObj);
3437 AssertReturn(pFile, VERR_OUT_OF_RANGE);
3438 AssertReturn((bBootMediaType & ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK) <= ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK,
3439 VERR_INVALID_PARAMETER);
3440 AssertReturn(!(bBootMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_MASK) || idxBootCat != 1,
3441 VERR_INVALID_PARAMETER);
3442
3443 AssertReturn(idxBootCat != 0 && idxBootCat != 2 && idxBootCat < RT_ELEMENTS(pThis->aBootCatEntries) - 1U, VERR_OUT_OF_RANGE);
3444
3445 size_t cExtEntries = 0;
3446 if (bSelCritType == ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE)
3447 AssertReturn(cbSelCritData == 0, VERR_INVALID_PARAMETER);
3448 else
3449 {
3450 AssertReturn(idxBootCat > 2, VERR_INVALID_PARAMETER);
3451 if (cbSelCritData > 0)
3452 {
3453 AssertPtrReturn(pvSelCritData, VERR_INVALID_POINTER);
3454
3455 if (cbSelCritData <= RT_SIZEOFMEMB(ISO9660ELTORITOSECTIONENTRY, abSelectionCriteria))
3456 cExtEntries = 0;
3457 else
3458 {
3459 cExtEntries = (cbSelCritData - RT_SIZEOFMEMB(ISO9660ELTORITOSECTIONENTRY, abSelectionCriteria)
3460 + RT_SIZEOFMEMB(ISO9660ELTORITOSECTIONENTRYEXT, abSelectionCriteria) - 1)
3461 / RT_SIZEOFMEMB(ISO9660ELTORITOSECTIONENTRYEXT, abSelectionCriteria);
3462 AssertReturn(cExtEntries + 1 < RT_ELEMENTS(pThis->aBootCatEntries) - 1, VERR_TOO_MUCH_DATA);
3463 }
3464 }
3465 }
3466
3467 /*
3468 * Make sure we've got a boot file.
3469 */
3470 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3471 if (RT_SUCCESS(rc))
3472 {
3473 /*
3474 * Construct the entry.
3475 */
3476 union
3477 {
3478 ISO9660ELTORITOSECTIONENTRY Entry;
3479 ISO9660ELTORITOSECTIONENTRYEXT ExtEntry;
3480 } u;
3481 u.Entry.bBootIndicator = fBootable ? ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
3482 : ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE;
3483 u.Entry.bBootMediaType = bBootMediaType;
3484 u.Entry.uLoadSeg = RT_H2LE_U16(uLoadSeg);
3485 u.Entry.bSystemType = cExtEntries == 0
3486 ? bSystemType & ~ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION
3487 : bSystemType | ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION;
3488 u.Entry.bUnused = 0;
3489 u.Entry.cEmulatedSectorsToLoad = RT_H2LE_U16(cSectorsToLoad);
3490 u.Entry.offBootImage = 0;
3491 u.Entry.bSelectionCriteriaType = bSelCritType;
3492 RT_ZERO(u.Entry.abSelectionCriteria);
3493 if (cbSelCritData > 0)
3494 memcpy(u.Entry.abSelectionCriteria, pvSelCritData, RT_MIN(cbSelCritData, sizeof(u.Entry.abSelectionCriteria)));
3495
3496 /*
3497 * Write it and update our internal tracker.
3498 */
3499 rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, ISO9660_ELTORITO_ENTRY_SIZE * idxBootCat,
3500 &u.Entry, sizeof(u.Entry), NULL);
3501 if (RT_SUCCESS(rc))
3502 {
3503 if (pThis->aBootCatEntries[idxBootCat].pBootFile != pFile)
3504 {
3505 if (pThis->aBootCatEntries[idxBootCat].pBootFile)
3506 pThis->aBootCatEntries[idxBootCat].pBootFile->Core.cNotOrphan--;
3507 pFile->Core.cNotOrphan++;
3508 pThis->aBootCatEntries[idxBootCat].pBootFile = pFile;
3509 }
3510
3511 pThis->aBootCatEntries[idxBootCat].bType = u.Entry.bBootIndicator;
3512 pThis->aBootCatEntries[idxBootCat].cEntries = 1;
3513 }
3514
3515 /*
3516 * Do add further extension entries with selection criteria.
3517 */
3518 if (cExtEntries)
3519 {
3520 uint8_t const *pbSrc = (uint8_t const *)pvSelCritData;
3521 size_t cbSrc = cbSelCritData;
3522 pbSrc += sizeof(u.Entry.abSelectionCriteria);
3523 cbSrc -= sizeof(u.Entry.abSelectionCriteria);
3524
3525 while (cbSrc > 0)
3526 {
3527 u.ExtEntry.bExtensionId = ISO9660_ELTORITO_SECTION_ENTRY_EXT_ID;
3528 if (cbSrc > sizeof(u.ExtEntry.abSelectionCriteria))
3529 {
3530 u.ExtEntry.fFlags = ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_MORE;
3531 memcpy(u.ExtEntry.abSelectionCriteria, pbSrc, sizeof(u.ExtEntry.abSelectionCriteria));
3532 pbSrc += sizeof(u.ExtEntry.abSelectionCriteria);
3533 cbSrc -= sizeof(u.ExtEntry.abSelectionCriteria);
3534 }
3535 else
3536 {
3537 u.ExtEntry.fFlags = 0;
3538 RT_ZERO(u.ExtEntry.abSelectionCriteria);
3539 memcpy(u.ExtEntry.abSelectionCriteria, pbSrc, cbSrc);
3540 cbSrc = 0;
3541 }
3542
3543 idxBootCat++;
3544 rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, ISO9660_ELTORITO_ENTRY_SIZE * idxBootCat,
3545 &u.Entry, sizeof(u.Entry), NULL);
3546 if (RT_FAILURE(rc))
3547 break;
3548
3549 /* update the internal tracker. */
3550 if (pThis->aBootCatEntries[idxBootCat].pBootFile)
3551 {
3552 pThis->aBootCatEntries[idxBootCat].pBootFile->Core.cNotOrphan--;
3553 pThis->aBootCatEntries[idxBootCat].pBootFile = NULL;
3554 }
3555
3556 pThis->aBootCatEntries[idxBootCat].bType = ISO9660_ELTORITO_SECTION_ENTRY_EXT_ID;
3557 pThis->aBootCatEntries[idxBootCat].cEntries = 1;
3558 }
3559 }
3560 }
3561 return rc;
3562}
3563
3564
3565/**
3566 * Set the validation entry of the boot catalog (this is the first entry).
3567 *
3568 * @returns IPRT status code.
3569 * @param hIsoMaker The ISO maker handle.
3570 * @param idxBootCat The boot catalog entry.
3571 * @param cEntries Number of entries in the section.
3572 * @param idPlatform The platform ID
3573 * (ISO9660_ELTORITO_PLATFORM_ID_XXX).
3574 * @param pszString Section identifier or something. Optional.
3575 */
3576RTDECL(int) RTFsIsoMakerBootCatSetSectionHeaderEntry(RTFSISOMAKER hIsoMaker, uint32_t idxBootCat, uint32_t cEntries,
3577 uint8_t idPlatform, const char *pszString)
3578{
3579 /*
3580 * Validate input.
3581 */
3582 PRTFSISOMAKERINT pThis = hIsoMaker;
3583 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3584
3585 AssertReturn(idxBootCat >= 2 && idxBootCat < RT_ELEMENTS(pThis->aBootCatEntries) - 1U, VERR_OUT_OF_RANGE);
3586 AssertReturn(cEntries < RT_ELEMENTS(pThis->aBootCatEntries) - 2U - 1U, VERR_OUT_OF_RANGE);
3587 AssertReturn(idxBootCat + cEntries + 1 < RT_ELEMENTS(pThis->aBootCatEntries), VERR_OUT_OF_RANGE);
3588
3589 size_t cchString = 0;
3590 if (pszString)
3591 {
3592 cchString = RTStrCalcLatin1Len(pszString);
3593 AssertReturn(cchString < RT_SIZEOFMEMB(ISO9660ELTORITOVALIDATIONENTRY, achId), VERR_OUT_OF_RANGE);
3594 }
3595
3596 /*
3597 * Make sure we've got a boot file.
3598 */
3599 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3600 if (RT_SUCCESS(rc))
3601 {
3602 /*
3603 * Construct the entry data.
3604 */
3605 ISO9660ELTORITOSECTIONHEADER Entry;
3606 Entry.bHeaderId = ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER;
3607 Entry.bPlatformId = idPlatform;
3608 Entry.cEntries = RT_H2LE_U16(cEntries);
3609 RT_ZERO(Entry.achSectionId);
3610 if (cchString)
3611 {
3612 char *pszTmp = Entry.achSectionId;
3613 rc = RTStrToLatin1Ex(pszString, RTSTR_MAX, &pszTmp, sizeof(Entry.achSectionId), NULL);
3614 AssertRC(rc);
3615 }
3616
3617 /*
3618 * Write the entry and update our internal tracker.
3619 */
3620 rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, ISO9660_ELTORITO_ENTRY_SIZE * idxBootCat,
3621 &Entry, sizeof(Entry), NULL);
3622 if (RT_SUCCESS(rc))
3623 {
3624 if (pThis->aBootCatEntries[idxBootCat].pBootFile != NULL)
3625 {
3626 pThis->aBootCatEntries[idxBootCat].pBootFile->Core.cNotOrphan--;
3627 pThis->aBootCatEntries[idxBootCat].pBootFile = NULL;
3628 }
3629
3630 pThis->aBootCatEntries[idxBootCat].bType = ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER;
3631 pThis->aBootCatEntries[idxBootCat].cEntries = cEntries + 1;
3632 }
3633 }
3634 return rc;
3635}
3636
3637
3638
3639
3640
3641/*
3642 *
3643 * Image finalization.
3644 * Image finalization.
3645 * Image finalization.
3646 *
3647 */
3648
3649
3650/**
3651 * Remove any orphaned object from the disk.
3652 *
3653 * @returns IPRT status code.
3654 * @param pThis The ISO maker instance.
3655 */
3656static int rtFsIsoMakerFinalizeRemoveOrphans(PRTFSISOMAKERINT pThis)
3657{
3658 for (;;)
3659 {
3660 uint32_t cRemoved = 0;
3661 PRTFSISOMAKEROBJ pCur;
3662 PRTFSISOMAKEROBJ pNext;
3663 RTListForEachSafe(&pThis->ObjectHead, pCur, pNext, RTFSISOMAKEROBJ, Entry)
3664 {
3665 if ( pCur->pPrimaryName
3666 || pCur->pJolietName
3667 || pCur->pUdfName
3668 || pCur->pHfsName
3669 || pCur->cNotOrphan > 0)
3670 { /* likely */ }
3671 else
3672 {
3673 Log4(("rtFsIsoMakerFinalizeRemoveOrphans: %#x cbData=%#RX64\n", pCur->idxObj,
3674 pCur->enmType == RTFSISOMAKEROBJTYPE_FILE ? ((PRTFSISOMAKERFILE)(pCur))->cbData : 0));
3675 int rc = rtFsIsoMakerObjRemoveWorker(pThis, pCur);
3676 if (RT_SUCCESS(rc))
3677 cRemoved++;
3678 else
3679 return rc;
3680 }
3681 }
3682 if (!cRemoved)
3683 return VINF_SUCCESS;
3684 }
3685}
3686
3687
3688/**
3689 * Finalizes the El Torito boot stuff, part 1.
3690 *
3691 * This includes generating the boot catalog data and fixing the location of all
3692 * related image files.
3693 *
3694 * @returns IPRT status code.
3695 * @param pThis The ISO maker instance.
3696 */
3697static int rtFsIsoMakerFinalizeBootStuffPart1(PRTFSISOMAKERINT pThis)
3698{
3699 /*
3700 * Anything?
3701 */
3702 if (!pThis->pBootCatFile)
3703 return VINF_SUCCESS;
3704
3705 /*
3706 * Validate the boot catalog file.
3707 */
3708 AssertReturn(pThis->aBootCatEntries[0].bType == ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY,
3709 VERR_ISOMK_BOOT_CAT_NO_VALIDATION_ENTRY);
3710 AssertReturn(pThis->aBootCatEntries[1].pBootFile != NULL, VERR_ISOMK_BOOT_CAT_NO_DEFAULT_ENTRY);
3711
3712 /* Check any sections following the default one. */
3713 uint32_t cEntries = 2;
3714 while ( cEntries < RT_ELEMENTS(pThis->aBootCatEntries) - 1U
3715 && pThis->aBootCatEntries[cEntries].cEntries > 0)
3716 {
3717 AssertReturn(pThis->aBootCatEntries[cEntries].bType == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER,
3718 VERR_ISOMK_BOOT_CAT_EXPECTED_SECTION_HEADER);
3719 for (uint32_t i = 1; i < pThis->aBootCatEntries[cEntries].cEntries; i++)
3720 AssertReturn(pThis->aBootCatEntries[cEntries].pBootFile != NULL,
3721 pThis->aBootCatEntries[cEntries].cEntries == 0
3722 ? VERR_ISOMK_BOOT_CAT_EMPTY_ENTRY : VERR_ISOMK_BOOT_CAT_INVALID_SECTION_SIZE);
3723 cEntries += pThis->aBootCatEntries[cEntries].cEntries;
3724 }
3725
3726 /* Save for size setting. */
3727 uint32_t const cEntriesInFile = cEntries + 1;
3728
3729 /* Check that the remaining entries are empty. */
3730 while (cEntries < RT_ELEMENTS(pThis->aBootCatEntries))
3731 {
3732 AssertReturn(pThis->aBootCatEntries[cEntries].cEntries == 0, VERR_ISOMK_BOOT_CAT_ERRATIC_ENTRY);
3733 cEntries++;
3734 }
3735
3736 /*
3737 * Fixate the size of the boot catalog file.
3738 */
3739 pThis->pBootCatFile->cbData = cEntriesInFile * ISO9660_ELTORITO_ENTRY_SIZE;
3740 pThis->cbData += RT_ALIGN_32(cEntriesInFile * ISO9660_ELTORITO_ENTRY_SIZE, RTFSISOMAKER_SECTOR_SIZE);
3741
3742 /*
3743 * Move up the boot images and boot catalog to the start of the image.
3744 */
3745 for (uint32_t i = RT_ELEMENTS(pThis->aBootCatEntries) - 2; i > 0; i--)
3746 if (pThis->aBootCatEntries[i].pBootFile)
3747 {
3748 RTListNodeRemove(&pThis->aBootCatEntries[i].pBootFile->Core.Entry);
3749 RTListPrepend(&pThis->ObjectHead, &pThis->aBootCatEntries[i].pBootFile->Core.Entry);
3750 }
3751
3752 /* The boot catalog comes first. */
3753 RTListNodeRemove(&pThis->pBootCatFile->Core.Entry);
3754 RTListPrepend(&pThis->ObjectHead, &pThis->pBootCatFile->Core.Entry);
3755
3756 return VINF_SUCCESS;
3757}
3758
3759
3760/**
3761 * Finalizes the El Torito boot stuff, part 1.
3762 *
3763 * This includes generating the boot catalog data and fixing the location of all
3764 * related image files.
3765 *
3766 * @returns IPRT status code.
3767 * @param pThis The ISO maker instance.
3768 */
3769static int rtFsIsoMakerFinalizeBootStuffPart2(PRTFSISOMAKERINT pThis)
3770{
3771 /*
3772 * Anything?
3773 */
3774 if (!pThis->pBootCatFile)
3775 return VINF_SUCCESS;
3776
3777 /*
3778 * Fill in the descriptor.
3779 */
3780 PISO9660BOOTRECORDELTORITO pDesc = pThis->pElToritoDesc;
3781 pDesc->Hdr.bDescType = ISO9660VOLDESC_TYPE_BOOT_RECORD;
3782 pDesc->Hdr.bDescVersion = ISO9660PRIMARYVOLDESC_VERSION;
3783 memcpy(pDesc->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pDesc->Hdr.achStdId));
3784 memcpy(pDesc->achBootSystemId, RT_STR_TUPLE(ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID));
3785 pDesc->offBootCatalog = RT_H2LE_U32((uint32_t)(pThis->pBootCatFile->offData / RTFSISOMAKER_SECTOR_SIZE));
3786
3787 /*
3788 * Update the image file locations.
3789 */
3790 uint32_t cEntries = 2;
3791 for (uint32_t i = 1; i < RT_ELEMENTS(pThis->aBootCatEntries) - 1; i++)
3792 if (pThis->aBootCatEntries[i].pBootFile)
3793 {
3794 uint32_t off = pThis->aBootCatEntries[i].pBootFile->offData / RTFSISOMAKER_SECTOR_SIZE;
3795 off = RT_H2LE_U32(off);
3796 int rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile,
3797 i * ISO9660_ELTORITO_ENTRY_SIZE + RT_UOFFSETOF(ISO9660ELTORITOSECTIONENTRY, offBootImage),
3798 &off, sizeof(off), NULL /*pcbWritten*/);
3799 AssertRCReturn(rc, rc);
3800 if (i == cEntries)
3801 cEntries = i + 1;
3802 }
3803
3804 /*
3805 * Write end section.
3806 */
3807 ISO9660ELTORITOSECTIONHEADER Entry;
3808 Entry.bHeaderId = ISO9660_ELTORITO_HEADER_ID_FINAL_SECTION_HEADER;
3809 Entry.bPlatformId = ISO9660_ELTORITO_PLATFORM_ID_X86;
3810 Entry.cEntries = 0;
3811 RT_ZERO(Entry.achSectionId);
3812 int rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, cEntries * ISO9660_ELTORITO_ENTRY_SIZE,
3813 &Entry, sizeof(Entry), NULL /*pcbWritten*/);
3814 AssertRCReturn(rc, rc);
3815
3816 return VINF_SUCCESS;
3817}
3818
3819
3820/**
3821 * Gathers the dirs for an ISO-9660 namespace (e.g. primary or joliet).
3822 *
3823 * @param pNamespace The namespace.
3824 * @param pFinalizedDirs The finalized directory structure. The
3825 * FinalizedDirs will be worked here.
3826 */
3827static void rtFsIsoMakerFinalizeGatherDirs(PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs)
3828{
3829 RTListInit(&pFinalizedDirs->FinalizedDirs);
3830
3831 /*
3832 * Enter the root directory (if we got one).
3833 */
3834 if (!pNamespace->pRoot)
3835 return;
3836 PRTFSISOMAKERNAMEDIR pCurDir = pNamespace->pRoot->pDir;
3837 RTListAppend(&pFinalizedDirs->FinalizedDirs, &pCurDir->FinalizedEntry);
3838 do
3839 {
3840 /*
3841 * Scan pCurDir and add directories. We don't need to sort anything
3842 * here because the directory is already in path table compatible order.
3843 */
3844 uint32_t cLeft = pCurDir->cChildren;
3845 PPRTFSISOMAKERNAME ppChild = pCurDir->papChildren;
3846 while (cLeft-- > 0)
3847 {
3848 PRTFSISOMAKERNAME pChild = *ppChild++;
3849 if (pChild->pDir)
3850 RTListAppend(&pFinalizedDirs->FinalizedDirs, &pChild->pDir->FinalizedEntry);
3851 }
3852
3853 /*
3854 * Advance to the next directory.
3855 */
3856 pCurDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pCurDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
3857 } while (pCurDir);
3858}
3859
3860
3861/**
3862 * Finalizes a directory entry (i.e. namespace node).
3863 *
3864 * This calculates the directory record size.
3865 *
3866 * @returns IPRT status code.
3867 * @param pFinalizedDirs .
3868 * @param pName The directory entry to finalize.
3869 * @param offInDir The offset in the directory of this record.
3870 * @param uRootRockRidge This is the rock ridge level when
3871 * root, otherwise it's zero.
3872 */
3873static int rtFsIsoMakerFinalizeIsoDirectoryEntry(PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs, PRTFSISOMAKERNAME pName,
3874 uint32_t offInDir, uint8_t uRootRockRidge)
3875{
3876 /* Set directory and translation table offsets. (These are for
3877 helping generating data blocks later.) */
3878 pName->offDirRec = offInDir;
3879
3880 /* Calculate the minimal directory record size. */
3881 size_t cbDirRec = RT_UOFFSETOF(ISO9660DIRREC, achFileId) + pName->cbNameInDirRec + !(pName->cbNameInDirRec & 1);
3882 AssertReturn(cbDirRec <= UINT8_MAX, VERR_FILENAME_TOO_LONG);
3883
3884 pName->cbDirRec = (uint8_t)cbDirRec;
3885 pName->cDirRecs = 1;
3886 if (pName->pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
3887 {
3888 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pName->pObj;
3889 if (pFile->cbData > UINT32_MAX)
3890 pName->cDirRecs = (pFile->cbData + RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE - 1) / RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE;
3891 }
3892
3893 /*
3894 * Calculate additional rock ridge stuff, if it doesn't all fit write it
3895 * to the spill file.
3896 */
3897 if (pFinalizedDirs->pRRSpillFile)
3898 {
3899 /** @todo rock ridge */
3900 RT_NOREF(uRootRockRidge);
3901 }
3902
3903 pName->cbDirRecTotal = pName->cbDirRec * pName->cDirRecs;
3904 return VINF_SUCCESS;
3905}
3906
3907
3908/**
3909 * Finalizes either a primary and secondary ISO namespace.
3910 *
3911 * @returns IPRT status code
3912 * @param pThis The ISO maker instance.
3913 * @param pNamespace The namespace.
3914 * @param pFinalizedDirs The finalized directories structure for the
3915 * namespace.
3916 * @param poffData The data offset. We will allocate blocks for the
3917 * directories and the path tables.
3918 */
3919static int rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
3920 PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs, uint64_t *poffData)
3921{
3922 /* The directory data comes first, so take down it's offset. */
3923 pFinalizedDirs->offDirs = *poffData;
3924
3925 /*
3926 * Reset the rock ridge spill file (in case we allow finalizing more than once)
3927 * and create a new spill file if rock ridge is enabled. The directory entry
3928 * finalize function uses this as a clue that rock ridge is enabled.
3929 */
3930 if (pFinalizedDirs->pRRSpillFile)
3931 {
3932 rtFsIsoMakerObjRemoveWorker(pThis, &pFinalizedDirs->pRRSpillFile->Core);
3933 pFinalizedDirs->pRRSpillFile = NULL;
3934 }
3935 if (pNamespace->uRockRidgeLevel > 0)
3936 {
3937 /** @todo create rock ridge spill file to indicate rock ridge */
3938 }
3939
3940 int rc;
3941 uint16_t idPathTable = 1;
3942 uint32_t cbPathTable = 0;
3943 if (pNamespace->pRoot)
3944 {
3945 /*
3946 * Precalc the directory record size for the root directory.
3947 */
3948 rc = rtFsIsoMakerFinalizeIsoDirectoryEntry(pFinalizedDirs, pNamespace->pRoot, 0 /*offInDir*/, pNamespace->uRockRidgeLevel);
3949 AssertRCReturn(rc, rc);
3950
3951 /*
3952 * Work thru the directories.
3953 */
3954 PRTFSISOMAKERNAMEDIR pCurDir;
3955 RTListForEach(&pFinalizedDirs->FinalizedDirs, pCurDir, RTFSISOMAKERNAMEDIR, FinalizedEntry)
3956 {
3957 PRTFSISOMAKERNAME pCurName = pCurDir->pName;
3958 PRTFSISOMAKERNAME pParentName = pCurName->pParent ? pCurName->pParent : pCurName;
3959
3960 /* We don't do anything special for the special '.' and '..' directory
3961 entries, instead we use the directory entry in the parent directory
3962 with a 1 byte name (00 or 01). */
3963 Assert(pCurName->cbDirRec != 0);
3964 Assert(pParentName->cbDirRec != 0);
3965 pCurDir->cbDirRec00 = pCurName->cbDirRec - pCurName->cbNameInDirRec - !(pCurName->cbNameInDirRec & 1) + 1;
3966 pCurDir->cbDirRec01 = pParentName->cbDirRec - pParentName->cbNameInDirRec - !(pParentName->cbNameInDirRec & 1) + 1;
3967
3968 uint32_t offInDir = (uint32_t)pCurDir->cbDirRec00 + pCurDir->cbDirRec01;
3969
3970 /* Finalize the directory entries. */
3971 uint32_t cbTransTbl = 0;
3972 uint32_t cLeft = pCurDir->cChildren;
3973 PPRTFSISOMAKERNAME ppChild = pCurDir->papChildren;
3974 while (cLeft-- > 0)
3975 {
3976 PRTFSISOMAKERNAME pChild = *ppChild++;
3977 rc = rtFsIsoMakerFinalizeIsoDirectoryEntry(pFinalizedDirs, pChild, offInDir, 0 /*uRootRockRidge*/);
3978 AssertRCReturn(rc, rc);
3979
3980 if ((RTFSISOMAKER_SECTOR_SIZE - (offInDir & RTFSISOMAKER_SECTOR_OFFSET_MASK)) < pChild->cbDirRecTotal)
3981 {
3982 Assert(ppChild[-1] == pChild && &ppChild[-1] != pCurDir->papChildren);
3983 if ( pChild->cDirRecs == 1
3984 || pChild->cDirRecs <= RTFSISOMAKER_SECTOR_SIZE / pChild->cbDirRec)
3985 {
3986 ppChild[-2]->cbDirRecTotal += RTFSISOMAKER_SECTOR_SIZE - (offInDir & RTFSISOMAKER_SECTOR_OFFSET_MASK);
3987 offInDir = (offInDir | RTFSISOMAKER_SECTOR_OFFSET_MASK) + 1; /* doesn't fit, skip to next sector. */
3988 Log4(("rtFsIsoMakerFinalizeDirectoriesInIsoNamespace: zero padding dir rec @%#x: %#x -> %#x; offset %#x -> %#x\n",
3989 ppChild[-2]->offDirRec, ppChild[-2]->cbDirRec, ppChild[-2]->cbDirRecTotal, pChild->offDirRec, offInDir));
3990 pChild->offDirRec = offInDir;
3991 }
3992 /* else: too complicated and ulikely, so whatever. */
3993 }
3994
3995 offInDir += pChild->cbDirRecTotal;
3996 if (pChild->cchTransNm)
3997 cbTransTbl += 2 /* type & space*/
3998 + RT_MAX(pChild->cchName, RTFSISOMAKER_TRANS_TBL_LEFT_PAD)
3999 + 1 /* tab */
4000 + pChild->cchTransNm
4001 + 1 /* newline */;
4002 }
4003
4004 /* Set the directory size and location, advancing the data offset. */
4005 pCurDir->cbDir = offInDir;
4006 pCurDir->offDir = *poffData;
4007 *poffData += RT_ALIGN_32(offInDir, RTFSISOMAKER_SECTOR_SIZE);
4008
4009 /* Set the translation table file size. */
4010 if (pCurDir->pTransTblFile)
4011 {
4012 pCurDir->pTransTblFile->cbData = cbTransTbl;
4013 pThis->cbData += RT_ALIGN_32(cbTransTbl, RTFSISOMAKER_SECTOR_SIZE);
4014 }
4015
4016 /* Add to the path table size calculation. */
4017 pCurDir->offPathTable = cbPathTable;
4018 pCurDir->idPathTable = idPathTable++;
4019 cbPathTable += RTFSISOMAKER_CALC_PATHREC_SIZE(pCurName->cbNameInDirRec);
4020
4021 Log4(("rtFsIsoMakerFinalizeDirectoriesInIsoNamespace: idxObj=#%#x cbDir=%#08x cChildren=%#05x %s\n",
4022 pCurDir->pName->pObj->idxObj, pCurDir->cbDir, pCurDir->cChildren, pCurDir->pName->szName));
4023 }
4024 }
4025
4026 /*
4027 * Update the rock ridge spill file size.
4028 */
4029 if (pFinalizedDirs->pRRSpillFile)
4030 {
4031 rc = RTVfsFileGetSize(pFinalizedDirs->pRRSpillFile->u.hVfsFile, &pFinalizedDirs->pRRSpillFile->cbData);
4032 AssertRCReturn(rc, rc);
4033 pThis->cbData += pFinalizedDirs->pRRSpillFile->cbData;
4034 }
4035
4036 /*
4037 * Calculate the path table offsets and move past them.
4038 */
4039 pFinalizedDirs->cbPathTable = cbPathTable;
4040 pFinalizedDirs->offPathTableL = *poffData;
4041 *poffData += RT_ALIGN_64(cbPathTable, RTFSISOMAKER_SECTOR_SIZE);
4042
4043 pFinalizedDirs->offPathTableM = *poffData;
4044 *poffData += RT_ALIGN_64(cbPathTable, RTFSISOMAKER_SECTOR_SIZE);
4045
4046 return VINF_SUCCESS;
4047}
4048
4049
4050
4051/**
4052 * Finalizes directories and related stuff.
4053 *
4054 * This will not generate actual directory data, but calculate the size of it
4055 * once it's generated. Ditto for the path tables. The exception is the rock
4056 * ridge spill file, which will be generated in memory.
4057 *
4058 * @returns IPRT status code.
4059 * @param pThis The ISO maker instance.
4060 * @param poffData The data offset (in/out).
4061 */
4062static int rtFsIsoMakerFinalizeDirectories(PRTFSISOMAKERINT pThis, uint64_t *poffData)
4063{
4064 /*
4065 * Locate the directories, width first, inserting them in the finalized lists so
4066 * we can process them efficiently.
4067 */
4068 rtFsIsoMakerFinalizeGatherDirs(&pThis->PrimaryIso, &pThis->PrimaryIsoDirs);
4069 rtFsIsoMakerFinalizeGatherDirs(&pThis->Joliet, &pThis->JolietDirs);
4070
4071 /*
4072 * Process the primary ISO and joliet namespaces.
4073 */
4074 int rc = rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(pThis, &pThis->PrimaryIso, &pThis->PrimaryIsoDirs, poffData);
4075 if (RT_SUCCESS(rc))
4076 rc = rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(pThis, &pThis->Joliet, &pThis->JolietDirs, poffData);
4077 if (RT_SUCCESS(rc))
4078 {
4079 /*
4080 * Later: UDF, HFS.
4081 */
4082 }
4083 return rc;
4084}
4085
4086
4087/**
4088 * Finalizes data allocations.
4089 *
4090 * This will set the RTFSISOMAKERFILE::offData members.
4091 *
4092 * @returns IPRT status code.
4093 * @param pThis The ISO maker instance.
4094 * @param poffData The data offset (in/out).
4095 */
4096static int rtFsIsoMakerFinalizeData(PRTFSISOMAKERINT pThis, uint64_t *poffData)
4097{
4098 pThis->offFirstFile = *poffData;
4099
4100 /*
4101 * We currently does not have any ordering prioritizing implemented, so we
4102 * just store files in the order they were added.
4103 */
4104 PRTFSISOMAKEROBJ pCur;
4105 RTListForEach(&pThis->ObjectHead, pCur, RTFSISOMAKEROBJ, Entry)
4106 {
4107 if (pCur->enmType == RTFSISOMAKEROBJTYPE_FILE)
4108 {
4109 PRTFSISOMAKERFILE pCurFile = (PRTFSISOMAKERFILE)pCur;
4110 if (pCurFile->offData == UINT64_MAX)
4111 {
4112 pCurFile->offData = *poffData;
4113 *poffData += RT_ALIGN_64(pCurFile->cbData, RTFSISOMAKER_SECTOR_SIZE);
4114 RTListAppend(&pThis->FinalizedFiles, &pCurFile->FinalizedEntry);
4115 Log4(("rtFsIsoMakerFinalizeData: %#x @%#RX64 cbData=%#RX64\n", pCurFile->Core.idxObj, pCurFile->offData, pCurFile->cbData));
4116 }
4117
4118 /*
4119 * Create the boot info table.
4120 */
4121 if (pCurFile->pBootInfoTable)
4122 {
4123 /*
4124 * Checksum the file.
4125 */
4126 int rc;
4127 RTVFSFILE hVfsFile;
4128 uint64_t offBase;
4129 switch (pCurFile->enmSrcType)
4130 {
4131 case RTFSISOMAKERSRCTYPE_PATH:
4132 rc = RTVfsChainOpenFile(pCurFile->u.pszSrcPath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
4133 &hVfsFile, NULL, NULL);
4134 AssertMsgRCReturn(rc, ("%s -> %Rrc\n", pCurFile->u.pszSrcPath, rc), rc);
4135 offBase = 0;
4136 break;
4137 case RTFSISOMAKERSRCTYPE_VFS_FILE:
4138 hVfsFile = pCurFile->u.hVfsFile;
4139 offBase = 0;
4140 rc = VINF_SUCCESS;
4141 break;
4142 case RTFSISOMAKERSRCTYPE_COMMON:
4143 hVfsFile = pThis->paCommonSources[pCurFile->u.Common.idxSrc];
4144 offBase = pCurFile->u.Common.offData;
4145 rc = VINF_SUCCESS;
4146 break;
4147 default:
4148 AssertMsgFailedReturn(("enmSrcType=%d\n", pCurFile->enmSrcType), VERR_INTERNAL_ERROR_3);
4149 }
4150
4151 uint32_t uChecksum = 0;
4152 uint32_t off = 64;
4153 uint32_t cbLeft = RT_MAX(64, (uint32_t)pCurFile->cbData) - 64;
4154 while (cbLeft > 0)
4155 {
4156 union
4157 {
4158 uint8_t ab[_16K];
4159 uint32_t au32[_16K / sizeof(uint32_t)];
4160 } uBuf;
4161 uint32_t cbRead = RT_MIN(sizeof(uBuf), cbLeft);
4162 if (cbRead & 3)
4163 RT_ZERO(uBuf);
4164 rc = RTVfsFileReadAt(hVfsFile, offBase + off, &uBuf, cbRead, NULL);
4165 if (RT_FAILURE(rc))
4166 break;
4167
4168 size_t i = RT_ALIGN_Z(cbRead, sizeof(uint32_t)) / sizeof(uint32_t);
4169 while (i-- > 0)
4170 uChecksum += RT_LE2H_U32(uBuf.au32[i]);
4171
4172 off += cbRead;
4173 cbLeft -= cbRead;
4174 }
4175
4176 if (pCurFile->enmSrcType == RTFSISOMAKERSRCTYPE_PATH)
4177 RTVfsFileRelease(hVfsFile);
4178 if (RT_FAILURE(rc))
4179 return rc;
4180
4181 /*
4182 * Populate the structure.
4183 */
4184 pCurFile->pBootInfoTable->offPrimaryVolDesc = RT_H2LE_U32(16);
4185 pCurFile->pBootInfoTable->offBootFile = RT_H2LE_U32((uint32_t)(pCurFile->offData / RTFSISOMAKER_SECTOR_SIZE));
4186 pCurFile->pBootInfoTable->cbBootFile = RT_H2LE_U32((uint32_t)pCurFile->cbData);
4187 pCurFile->pBootInfoTable->uChecksum = RT_H2LE_U32(uChecksum);
4188 RT_ZERO(pCurFile->pBootInfoTable->auReserved);
4189 }
4190 }
4191 }
4192
4193 return VINF_SUCCESS;
4194}
4195
4196
4197/**
4198 * Copies the given string as UTF-16 and pad unused space in the destination
4199 * with spaces.
4200 *
4201 * @param pachDst The destination field. C type is char, but real life
4202 * type is UTF-16 / UCS-2.
4203 * @param cchDst The size of the destination field.
4204 * @param pszSrc The source string. NULL is treated like empty string.
4205 */
4206static void rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(char *pachDst, size_t cchDst, const char *pszSrc)
4207{
4208 size_t cwcSrc = 0;
4209 if (pszSrc)
4210 {
4211 RTUTF16 wszSrc[256];
4212 PRTUTF16 pwszSrc = wszSrc;
4213 int rc = RTStrToUtf16BigEx(pszSrc, RTSTR_MAX, &pwszSrc, RT_ELEMENTS(wszSrc), &cwcSrc);
4214 AssertRCStmt(rc, cwcSrc = 0);
4215
4216 if (cwcSrc > cchDst / sizeof(RTUTF16))
4217 cwcSrc = cchDst / sizeof(RTUTF16);
4218 memcpy(pachDst, wszSrc, cwcSrc * sizeof(RTUTF16));
4219 }
4220
4221 /* Space padding. Note! cchDst can be an odd number. */
4222 size_t cchWritten = cwcSrc * sizeof(RTUTF16);
4223 if (cchWritten < cchDst)
4224 {
4225 while (cchWritten + 2 <= cchDst)
4226 {
4227 pachDst[cchWritten++] = '\0';
4228 pachDst[cchWritten++] = ' ';
4229 }
4230 if (cchWritten < cchDst)
4231 pachDst[cchWritten] = '\0';
4232 }
4233}
4234
4235
4236/**
4237 * Copies the given string and pad unused space in the destination with spaces.
4238 *
4239 * @param pachDst The destination field.
4240 * @param cchDst The size of the destination field.
4241 * @param pszSrc The source string. NULL is treated like empty string.
4242 */
4243static void rtFsIsoMakerFinalizeCopyAndSpacePad(char *pachDst, size_t cchDst, const char *pszSrc)
4244{
4245 size_t cchSrc;
4246 if (!pszSrc)
4247 cchSrc = 0;
4248 else
4249 {
4250 cchSrc = strlen(pszSrc);
4251 if (cchSrc > cchDst)
4252 cchSrc = cchDst;
4253 memcpy(pachDst, pszSrc, cchSrc);
4254 }
4255 if (cchSrc < cchDst)
4256 memset(&pachDst[cchSrc], ' ', cchDst - cchSrc);
4257}
4258
4259
4260/**
4261 * Formats a timespec as an ISO-9660 ascii timestamp.
4262 *
4263 * @param pTime The timespec to format.
4264 * @param pIsoTs The ISO-9660 timestamp destination buffer.
4265 */
4266static void rtFsIsoMakerTimespecToIso9660Timestamp(PCRTTIMESPEC pTime, PISO9660TIMESTAMP pIsoTs)
4267{
4268 RTTIME Exploded;
4269 RTTimeExplode(&Exploded, pTime);
4270
4271 char szTmp[64];
4272#define FORMAT_FIELD(a_achDst, a_uSrc) \
4273 do { \
4274 RTStrFormatU32(szTmp, sizeof(szTmp), a_uSrc, 10, sizeof(a_achDst), sizeof(a_achDst), \
4275 RTSTR_F_ZEROPAD | RTSTR_F_WIDTH | RTSTR_F_PRECISION); \
4276 memcpy(a_achDst, szTmp, sizeof(a_achDst)); \
4277 } while (0)
4278 FORMAT_FIELD(pIsoTs->achYear, Exploded.i32Year);
4279 FORMAT_FIELD(pIsoTs->achMonth, Exploded.u8Month);
4280 FORMAT_FIELD(pIsoTs->achDay, Exploded.u8MonthDay);
4281 FORMAT_FIELD(pIsoTs->achHour, Exploded.u8Hour);
4282 FORMAT_FIELD(pIsoTs->achMinute, Exploded.u8Minute);
4283 FORMAT_FIELD(pIsoTs->achSecond, Exploded.u8Second);
4284 FORMAT_FIELD(pIsoTs->achCentisecond, Exploded.u32Nanosecond / RT_NS_10MS);
4285#undef FORMAT_FIELD
4286 pIsoTs->offUtc = 0;
4287}
4288
4289/**
4290 * Formats zero ISO-9660 ascii timestamp (treated as not specified).
4291 *
4292 * @param pIsoTs The ISO-9660 timestamp destination buffer.
4293 */
4294static void rtFsIsoMakerZero9660Timestamp(PISO9660TIMESTAMP pIsoTs)
4295{
4296 memset(pIsoTs, '0', RT_OFFSETOF(ISO9660TIMESTAMP, offUtc));
4297 pIsoTs->offUtc = 0;
4298}
4299
4300
4301/**
4302 * Formats a timespec as an ISO-9660 record timestamp.
4303 *
4304 * @param pTime The timespec to format.
4305 * @param pIsoTs The ISO-9660 timestamp destination buffer.
4306 */
4307static void rtFsIsoMakerTimespecToIso9660RecTimestamp(PCRTTIMESPEC pTime, PISO9660RECTIMESTAMP pIsoRecTs)
4308{
4309 RTTIME Exploded;
4310 RTTimeExplode(&Exploded, pTime);
4311
4312 pIsoRecTs->bYear = Exploded.i32Year >= 1900 ? Exploded.i32Year - 1900 : 0;
4313 pIsoRecTs->bMonth = Exploded.u8Month;
4314 pIsoRecTs->bDay = Exploded.u8MonthDay;
4315 pIsoRecTs->bHour = Exploded.u8Hour;
4316 pIsoRecTs->bMinute = Exploded.u8Minute;
4317 pIsoRecTs->bSecond = Exploded.u8Second;
4318 pIsoRecTs->offUtc = 0;
4319}
4320
4321
4322/**
4323 * Allocate and prepare the volume descriptors.
4324 *
4325 * What's not done here gets done later by rtFsIsoMakerFinalizeBootStuffPart2,
4326 * or at teh very end of the finalization by
4327 * rtFsIsoMakerFinalizeVolumeDescriptors.
4328 *
4329 * @returns IPRT status code
4330 * @param pThis The ISO maker instance.
4331 */
4332static int rtFsIsoMakerFinalizePrepVolumeDescriptors(PRTFSISOMAKERINT pThis)
4333{
4334 /*
4335 * Allocate and calc pointers.
4336 */
4337 RTMemFree(pThis->pbVolDescs);
4338 pThis->pbVolDescs = (uint8_t *)RTMemAllocZ(pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE);
4339 AssertReturn(pThis->pbVolDescs, VERR_NO_MEMORY);
4340
4341 uint32_t offVolDescs = 0;
4342
4343 pThis->pPrimaryVolDesc = (PISO9660PRIMARYVOLDESC)&pThis->pbVolDescs[offVolDescs];
4344 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4345
4346 if (!pThis->pBootCatFile)
4347 pThis->pElToritoDesc = NULL;
4348 else
4349 {
4350 pThis->pElToritoDesc = (PISO9660BOOTRECORDELTORITO)&pThis->pbVolDescs[offVolDescs];
4351 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4352 }
4353
4354 if (!pThis->Joliet.uLevel)
4355 pThis->pJolietVolDesc = NULL;
4356 else
4357 {
4358 pThis->pJolietVolDesc = (PISO9660SUPVOLDESC)&pThis->pbVolDescs[offVolDescs];
4359 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4360 }
4361
4362 pThis->pTerminatorVolDesc = (PISO9660VOLDESCHDR)&pThis->pbVolDescs[offVolDescs];
4363 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4364
4365 if (pThis->Udf.uLevel > 0)
4366 {
4367 /** @todo UDF descriptors. */
4368 }
4369 AssertReturn(offVolDescs == pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE, VERR_INTERNAL_ERROR_2);
4370
4371 /*
4372 * This may be needed later.
4373 */
4374 char szImageCreationTime[42];
4375 RTTimeSpecToString(&pThis->ImageCreationTime, szImageCreationTime, sizeof(szImageCreationTime));
4376
4377 /*
4378 * Initialize the primary descriptor.
4379 */
4380 PISO9660PRIMARYVOLDESC pPrimary = pThis->pPrimaryVolDesc;
4381
4382 pPrimary->Hdr.bDescType = ISO9660VOLDESC_TYPE_PRIMARY;
4383 pPrimary->Hdr.bDescVersion = ISO9660PRIMARYVOLDESC_VERSION;
4384 memcpy(pPrimary->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pPrimary->Hdr.achStdId));
4385 //pPrimary->bPadding8 = 0;
4386 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achSystemId, sizeof(pPrimary->achSystemId), pThis->PrimaryIso.pszSystemId);
4387 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achVolumeId, sizeof(pPrimary->achVolumeId),
4388 pThis->PrimaryIso.pszVolumeId ? pThis->PrimaryIso.pszVolumeId : szImageCreationTime);
4389 //pPrimary->Unused73 = {0}
4390 //pPrimary->VolumeSpaceSize = later
4391 //pPrimary->abUnused89 = {0}
4392 pPrimary->cVolumesInSet.be = RT_H2BE_U16_C(1);
4393 pPrimary->cVolumesInSet.le = RT_H2LE_U16_C(1);
4394 pPrimary->VolumeSeqNo.be = RT_H2BE_U16_C(1);
4395 pPrimary->VolumeSeqNo.le = RT_H2LE_U16_C(1);
4396 pPrimary->cbLogicalBlock.be = RT_H2BE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4397 pPrimary->cbLogicalBlock.le = RT_H2LE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4398 //pPrimary->cbPathTable = later
4399 //pPrimary->offTypeLPathTable = later
4400 //pPrimary->offOptionalTypeLPathTable = {0}
4401 //pPrimary->offTypeMPathTable = later
4402 //pPrimary->offOptionalTypeMPathTable = {0}
4403 //pPrimary->RootDir = later
4404 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achVolumeSetId, sizeof(pPrimary->achVolumeSetId),
4405 pThis->PrimaryIso.pszVolumeSetId);
4406 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achPublisherId, sizeof(pPrimary->achPublisherId),
4407 pThis->PrimaryIso.pszPublisherId);
4408 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achDataPreparerId, sizeof(pPrimary->achDataPreparerId),
4409 pThis->PrimaryIso.pszDataPreparerId);
4410 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achApplicationId, sizeof(pPrimary->achApplicationId),
4411 pThis->PrimaryIso.pszApplicationId);
4412 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achCopyrightFileId, sizeof(pPrimary->achCopyrightFileId),
4413 pThis->PrimaryIso.pszCopyrightFileId);
4414 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achAbstractFileId, sizeof(pPrimary->achAbstractFileId),
4415 pThis->PrimaryIso.pszAbstractFileId);
4416 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achBibliographicFileId, sizeof(pPrimary->achBibliographicFileId),
4417 pThis->PrimaryIso.pszBibliographicFileId);
4418 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pPrimary->BirthTime);
4419 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pPrimary->ModifyTime);
4420 rtFsIsoMakerZero9660Timestamp(&pPrimary->ExpireTime);
4421 rtFsIsoMakerZero9660Timestamp(&pPrimary->EffectiveTime);
4422 pPrimary->bFileStructureVersion = ISO9660_FILE_STRUCTURE_VERSION;
4423 //pPrimary->bReserved883 = 0;
4424 //RT_ZERO(pPrimary->abAppUse);
4425 //RT_ZERO(pPrimary->abReserved1396);
4426
4427 /*
4428 * Initialize the joliet descriptor if included.
4429 */
4430 PISO9660SUPVOLDESC pJoliet = pThis->pJolietVolDesc;
4431 if (pJoliet)
4432 {
4433 pJoliet->Hdr.bDescType = ISO9660VOLDESC_TYPE_SUPPLEMENTARY;
4434 pJoliet->Hdr.bDescVersion = ISO9660SUPVOLDESC_VERSION;
4435 memcpy(pJoliet->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pJoliet->Hdr.achStdId));
4436 pJoliet->fVolumeFlags = ISO9660SUPVOLDESC_VOL_F_ESC_ONLY_REG;
4437 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achSystemId, sizeof(pJoliet->achSystemId), pThis->Joliet.pszSystemId);
4438 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achVolumeId, sizeof(pJoliet->achVolumeId),
4439 pThis->Joliet.pszVolumeId ? pThis->Joliet.pszVolumeId : szImageCreationTime);
4440 //pJoliet->Unused73 = {0}
4441 //pJoliet->VolumeSpaceSize = later
4442 memset(pJoliet->abEscapeSequences, ' ', sizeof(pJoliet->abEscapeSequences));
4443 pJoliet->abEscapeSequences[0] = ISO9660_JOLIET_ESC_SEQ_0;
4444 pJoliet->abEscapeSequences[1] = ISO9660_JOLIET_ESC_SEQ_1;
4445 pJoliet->abEscapeSequences[2] = pThis->Joliet.uLevel == 1 ? ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
4446 : pThis->Joliet.uLevel == 2 ? ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
4447 : ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3;
4448 pJoliet->cVolumesInSet.be = RT_H2BE_U16_C(1);
4449 pJoliet->cVolumesInSet.le = RT_H2LE_U16_C(1);
4450 pJoliet->VolumeSeqNo.be = RT_H2BE_U16_C(1);
4451 pJoliet->VolumeSeqNo.le = RT_H2LE_U16_C(1);
4452 pJoliet->cbLogicalBlock.be = RT_H2BE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4453 pJoliet->cbLogicalBlock.le = RT_H2LE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4454 //pJoliet->cbPathTable = later
4455 //pJoliet->offTypeLPathTable = later
4456 //pJoliet->offOptionalTypeLPathTable = {0}
4457 //pJoliet->offTypeMPathTable = later
4458 //pJoliet->offOptionalTypeMPathTable = {0}
4459 //pJoliet->RootDir = later
4460 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achVolumeSetId, sizeof(pJoliet->achVolumeSetId),
4461 pThis->Joliet.pszVolumeSetId);
4462 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achPublisherId, sizeof(pJoliet->achPublisherId),
4463 pThis->Joliet.pszPublisherId);
4464 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achDataPreparerId, sizeof(pJoliet->achDataPreparerId),
4465 pThis->Joliet.pszDataPreparerId);
4466 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achApplicationId, sizeof(pJoliet->achApplicationId),
4467 pThis->Joliet.pszApplicationId);
4468 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achCopyrightFileId, sizeof(pJoliet->achCopyrightFileId),
4469 pThis->Joliet.pszCopyrightFileId);
4470 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achAbstractFileId, sizeof(pJoliet->achAbstractFileId),
4471 pThis->Joliet.pszAbstractFileId);
4472 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achBibliographicFileId, sizeof(pJoliet->achBibliographicFileId),
4473 pThis->Joliet.pszBibliographicFileId);
4474 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pJoliet->BirthTime);
4475 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pJoliet->ModifyTime);
4476 rtFsIsoMakerZero9660Timestamp(&pJoliet->ExpireTime);
4477 rtFsIsoMakerZero9660Timestamp(&pJoliet->EffectiveTime);
4478 pJoliet->bFileStructureVersion = ISO9660_FILE_STRUCTURE_VERSION;
4479 //pJoliet->bReserved883 = 0;
4480 //RT_ZERO(pJoliet->abAppUse);
4481 //RT_ZERO(pJoliet->abReserved1396);
4482 }
4483
4484 /*
4485 * The ISO-9660 terminator descriptor.
4486 */
4487 pThis->pTerminatorVolDesc->bDescType = ISO9660VOLDESC_TYPE_TERMINATOR;
4488 pThis->pTerminatorVolDesc->bDescVersion = 1;
4489 memcpy(pThis->pTerminatorVolDesc->achStdId, ISO9660VOLDESC_STD_ID, sizeof(pThis->pTerminatorVolDesc->achStdId));
4490
4491 return VINF_SUCCESS;
4492}
4493
4494
4495/**
4496 * Finalizes the volume descriptors.
4497 *
4498 * This will set the RTFSISOMAKERFILE::offData members.
4499 *
4500 * @returns IPRT status code.
4501 * @param pThis The ISO maker instance.
4502 */
4503static int rtFsIsoMakerFinalizeVolumeDescriptors(PRTFSISOMAKERINT pThis)
4504{
4505 AssertReturn(pThis->pbVolDescs && pThis->pPrimaryVolDesc && pThis->pTerminatorVolDesc, VERR_INTERNAL_ERROR_3);
4506
4507 /*
4508 * Primary descriptor.
4509 */
4510 PISO9660PRIMARYVOLDESC pPrimary = pThis->pPrimaryVolDesc;
4511
4512 pPrimary->VolumeSpaceSize.be = RT_H2BE_U32(pThis->cbFinalizedImage / RTFSISOMAKER_SECTOR_SIZE);
4513 pPrimary->VolumeSpaceSize.le = RT_H2LE_U32(pThis->cbFinalizedImage / RTFSISOMAKER_SECTOR_SIZE);
4514 pPrimary->cbPathTable.be = RT_H2BE_U32(pThis->PrimaryIsoDirs.cbPathTable);
4515 pPrimary->cbPathTable.le = RT_H2LE_U32(pThis->PrimaryIsoDirs.cbPathTable);
4516 pPrimary->offTypeLPathTable = RT_H2LE_U32(pThis->PrimaryIsoDirs.offPathTableL / RTFSISOMAKER_SECTOR_SIZE);
4517 pPrimary->offTypeMPathTable = RT_H2BE_U32(pThis->PrimaryIsoDirs.offPathTableM / RTFSISOMAKER_SECTOR_SIZE);
4518 pPrimary->RootDir.DirRec.cbDirRec = sizeof(pPrimary->RootDir);
4519 pPrimary->RootDir.DirRec.cExtAttrBlocks = 0;
4520 pPrimary->RootDir.DirRec.offExtent.be = RT_H2BE_U32(pThis->PrimaryIso.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4521 pPrimary->RootDir.DirRec.offExtent.le = RT_H2LE_U32(pThis->PrimaryIso.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4522 pPrimary->RootDir.DirRec.cbData.be = RT_H2BE_U32(pThis->PrimaryIso.pRoot->pDir->cbDir);
4523 pPrimary->RootDir.DirRec.cbData.le = RT_H2LE_U32(pThis->PrimaryIso.pRoot->pDir->cbDir);
4524 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pThis->PrimaryIso.pRoot->pObj->BirthTime, &pPrimary->RootDir.DirRec.RecTime);
4525 pPrimary->RootDir.DirRec.fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
4526 pPrimary->RootDir.DirRec.bFileUnitSize = 0;
4527 pPrimary->RootDir.DirRec.bInterleaveGapSize = 0;
4528 pPrimary->RootDir.DirRec.VolumeSeqNo.be = RT_H2BE_U16_C(1);
4529 pPrimary->RootDir.DirRec.VolumeSeqNo.le = RT_H2LE_U16_C(1);
4530 pPrimary->RootDir.DirRec.bFileIdLength = 1;
4531 pPrimary->RootDir.DirRec.achFileId[0] = 0x00;
4532
4533 /*
4534 * Initialize the joliet descriptor if included.
4535 */
4536 PISO9660SUPVOLDESC pJoliet = pThis->pJolietVolDesc;
4537 if (pJoliet)
4538 {
4539 pJoliet->VolumeSpaceSize = pPrimary->VolumeSpaceSize;
4540 pJoliet->cbPathTable.be = RT_H2BE_U32(pThis->JolietDirs.cbPathTable);
4541 pJoliet->cbPathTable.le = RT_H2LE_U32(pThis->JolietDirs.cbPathTable);
4542 pJoliet->offTypeLPathTable = RT_H2LE_U32(pThis->JolietDirs.offPathTableL / RTFSISOMAKER_SECTOR_SIZE);
4543 pJoliet->offTypeMPathTable = RT_H2BE_U32(pThis->JolietDirs.offPathTableM / RTFSISOMAKER_SECTOR_SIZE);
4544 pJoliet->RootDir.DirRec.cbDirRec = sizeof(pJoliet->RootDir);
4545 pJoliet->RootDir.DirRec.cExtAttrBlocks = 0;
4546 pJoliet->RootDir.DirRec.offExtent.be = RT_H2BE_U32(pThis->Joliet.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4547 pJoliet->RootDir.DirRec.offExtent.le = RT_H2LE_U32(pThis->Joliet.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4548 pJoliet->RootDir.DirRec.cbData.be = RT_H2BE_U32(pThis->Joliet.pRoot->pDir->cbDir);
4549 pJoliet->RootDir.DirRec.cbData.le = RT_H2LE_U32(pThis->Joliet.pRoot->pDir->cbDir);
4550 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pThis->Joliet.pRoot->pObj->BirthTime, &pJoliet->RootDir.DirRec.RecTime);
4551 pJoliet->RootDir.DirRec.fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
4552 pJoliet->RootDir.DirRec.bFileUnitSize = 0;
4553 pJoliet->RootDir.DirRec.bInterleaveGapSize = 0;
4554 pJoliet->RootDir.DirRec.VolumeSeqNo.be = RT_H2BE_U16_C(1);
4555 pJoliet->RootDir.DirRec.VolumeSeqNo.le = RT_H2LE_U16_C(1);
4556 pJoliet->RootDir.DirRec.bFileIdLength = 1;
4557 pJoliet->RootDir.DirRec.achFileId[0] = 0x00;
4558 }
4559
4560 return VINF_SUCCESS;
4561}
4562
4563
4564/**
4565 * Finalizes the image.
4566 *
4567 * @returns IPRT status code.
4568 * @param hIsoMaker The ISO maker handle.
4569 */
4570RTDECL(int) RTFsIsoMakerFinalize(RTFSISOMAKER hIsoMaker)
4571{
4572 PRTFSISOMAKERINT pThis = hIsoMaker;
4573 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
4574 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
4575
4576 /*
4577 * Remove orphaned objects and allocate volume descriptors.
4578 */
4579 int rc = rtFsIsoMakerFinalizeRemoveOrphans(pThis);
4580 if (RT_FAILURE(rc))
4581 return rc;
4582 AssertReturn(pThis->cObjects > 0, VERR_NO_DATA);
4583 AssertReturn(pThis->PrimaryIso.pRoot || pThis->PrimaryIso.uLevel == 0, VERR_NO_DATA);
4584 AssertReturn(pThis->Joliet.pRoot || pThis->Joliet.uLevel == 0, VERR_NO_DATA);
4585
4586 rc = rtFsIsoMakerFinalizePrepVolumeDescriptors(pThis);
4587 if (RT_FAILURE(rc))
4588 return rc;
4589
4590 /*
4591 * If there is any boot related stuff to be included, it ends up right after
4592 * the descriptors.
4593 */
4594 uint64_t offData = _32K + pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE;
4595 rc = rtFsIsoMakerFinalizeBootStuffPart1(pThis);
4596 if (RT_SUCCESS(rc))
4597 {
4598 /*
4599 * Directories and path tables comes next.
4600 */
4601 rc = rtFsIsoMakerFinalizeDirectories(pThis, &offData);
4602 if (RT_SUCCESS(rc))
4603 {
4604 /*
4605 * Then we store the file data.
4606 */
4607 rc = rtFsIsoMakerFinalizeData(pThis, &offData);
4608 if (RT_SUCCESS(rc))
4609 {
4610 pThis->cbFinalizedImage = offData;
4611
4612 /*
4613 * Do a 2nd pass over the boot stuff to finalize locations.
4614 */
4615 rc = rtFsIsoMakerFinalizeBootStuffPart2(pThis);
4616 if (RT_SUCCESS(rc))
4617 {
4618 /*
4619 * Finally, finalize the volume descriptors as they depend on some of the
4620 * block allocations done in the previous steps.
4621 */
4622 rc = rtFsIsoMakerFinalizeVolumeDescriptors(pThis);
4623 if (RT_SUCCESS(rc))
4624 {
4625 pThis->fFinalized = true;
4626 return VINF_SUCCESS;
4627 }
4628 }
4629 }
4630 }
4631 }
4632 return rc;
4633}
4634
4635
4636
4637
4638
4639/*
4640 *
4641 * Image I/O.
4642 * Image I/O.
4643 * Image I/O.
4644 *
4645 */
4646
4647/**
4648 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
4649 */
4650static DECLCALLBACK(int) rtFsIsoMakerOutFile_Close(void *pvThis)
4651{
4652 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4653
4654 RTFsIsoMakerRelease(pThis->pIsoMaker);
4655 pThis->pIsoMaker = NULL;
4656
4657 return VINF_SUCCESS;
4658}
4659
4660
4661/**
4662 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
4663 */
4664static DECLCALLBACK(int) rtFsIsoMakerOutFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4665{
4666 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4667 PRTFSISOMAKERINT pIsoMaker = pThis->pIsoMaker;
4668
4669
4670 pObjInfo->cbObject = pIsoMaker->cbFinalizedImage;
4671 pObjInfo->cbAllocated = pIsoMaker->cbFinalizedImage;
4672 pObjInfo->AccessTime = pIsoMaker->ImageCreationTime;
4673 pObjInfo->ModificationTime = pIsoMaker->ImageCreationTime;
4674 pObjInfo->ChangeTime = pIsoMaker->ImageCreationTime;
4675 pObjInfo->BirthTime = pIsoMaker->ImageCreationTime;
4676 pObjInfo->Attr.fMode = 0444 | RTFS_TYPE_FILE | RTFS_DOS_READONLY;
4677
4678 switch (enmAddAttr)
4679 {
4680 case RTFSOBJATTRADD_NOTHING:
4681 enmAddAttr = RTFSOBJATTRADD_UNIX;
4682 /* fall thru */
4683 case RTFSOBJATTRADD_UNIX:
4684 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
4685 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
4686 pObjInfo->Attr.u.Unix.cHardlinks = 1;
4687 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
4688 pObjInfo->Attr.u.Unix.INodeId = 0;
4689 pObjInfo->Attr.u.Unix.fFlags = 0;
4690 pObjInfo->Attr.u.Unix.GenerationId = 0;
4691 pObjInfo->Attr.u.Unix.Device = 0;
4692 break;
4693
4694 case RTFSOBJATTRADD_UNIX_OWNER:
4695 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
4696 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
4697 break;
4698
4699 case RTFSOBJATTRADD_UNIX_GROUP:
4700 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
4701 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
4702 break;
4703
4704 case RTFSOBJATTRADD_EASIZE:
4705 pObjInfo->Attr.u.EASize.cb = 0;
4706 break;
4707
4708 default:
4709 AssertFailedReturn(VERR_INVALID_PARAMETER);
4710 }
4711 pObjInfo->Attr.enmAdditional = enmAddAttr;
4712
4713 return VINF_SUCCESS;
4714}
4715
4716
4717/**
4718 * Produces the content of a TRANS.TBL file as a memory file.
4719 *
4720 * @returns IPRT status code.
4721 * @param pThis The ISO maker output file instance. The file is
4722 * returned as pThis->hVfsSrcFile.
4723 * @param pFile The TRANS.TBL file.
4724 */
4725static int rtFsIsoMakerOutFile_ProduceTransTbl(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERFILE pFile)
4726{
4727 /*
4728 * Create memory file instance.
4729 */
4730 RTVFSFILE hVfsFile;
4731 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, pFile->cbData, &hVfsFile);
4732 AssertRCReturn(rc, rc);
4733
4734 /*
4735 * Produce the file content.
4736 */
4737 PRTFSISOMAKERNAME *ppChild = pFile->u.pTransTblDir->pDir->papChildren;
4738 uint32_t cLeft = pFile->u.pTransTblDir->pDir->cChildren;
4739 while (cLeft-- > 0)
4740 {
4741 PRTFSISOMAKERNAME pChild = *ppChild++;
4742 if (pChild->cchTransNm)
4743 {
4744 /** @todo TRANS.TBL codeset, currently using UTF-8 which is probably not it.
4745 * However, nobody uses this stuff any more, so who cares. */
4746 char szEntry[RTFSISOMAKER_MAX_NAME_BUF * 2 + 128];
4747 size_t cchEntry = RTStrPrintf(szEntry, sizeof(szEntry), "%c %-*s\t%s\n", pChild->pDir ? 'D' : 'F',
4748 RTFSISOMAKER_TRANS_TBL_LEFT_PAD, pChild->szName, pChild->pszTransNm);
4749 rc = RTVfsFileWrite(hVfsFile, szEntry, cchEntry, NULL);
4750 if (RT_FAILURE(rc))
4751 {
4752 RTVfsFileRelease(hVfsFile);
4753 return rc;
4754 }
4755 }
4756 }
4757
4758 /*
4759 * Check that the size matches our estimate.
4760 */
4761 uint64_t cbResult = 0;
4762 rc = RTVfsFileGetSize(hVfsFile, &cbResult);
4763 if (RT_SUCCESS(rc) && cbResult == pFile->cbData)
4764 {
4765 pThis->hVfsSrcFile = hVfsFile;
4766 return VINF_SUCCESS;
4767 }
4768
4769 AssertMsgFailed(("rc=%Rrc, cbResult=%#RX64 cbData=%#RX64\n", rc, cbResult, pFile->cbData));
4770 RTVfsFileRelease(hVfsFile);
4771 return VERR_INTERNAL_ERROR_4;
4772}
4773
4774
4775
4776/**
4777 * Reads file data.
4778 *
4779 * @returns IPRT status code
4780 * @param pThis The instance data for the VFS file. We use this to
4781 * keep hints about where we are and we which source
4782 * file we've opened/created.
4783 * @param pIsoMaker The ISO maker instance.
4784 * @param offUnsigned The ISO image byte offset of the requested data.
4785 * @param pbBuf The output buffer.
4786 * @param cbBuf How much to read.
4787 * @param pcbDone Where to return how much was read.
4788 */
4789static int rtFsIsoMakerOutFile_ReadFileData(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERINT pIsoMaker, uint64_t offUnsigned,
4790 uint8_t *pbBuf, size_t cbBuf, size_t *pcbDone)
4791{
4792 *pcbDone = 0;
4793
4794 /*
4795 * Figure out which file. We keep a hint in the instance.
4796 */
4797 uint64_t offInFile;
4798 PRTFSISOMAKERFILE pFile = pThis->pFileHint;
4799 if (!pFile)
4800 {
4801 pFile = RTListGetFirst(&pIsoMaker->FinalizedFiles, RTFSISOMAKERFILE, FinalizedEntry);
4802 AssertReturn(pFile, VERR_INTERNAL_ERROR_2);
4803 }
4804 if ((offInFile = offUnsigned - pFile->offData) < RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE))
4805 { /* hit */ }
4806 else if (offUnsigned >= pFile->offData)
4807 {
4808 /* Seek forwards. */
4809 do
4810 {
4811 pFile = RTListGetNext(&pIsoMaker->FinalizedFiles, pFile, RTFSISOMAKERFILE, FinalizedEntry);
4812 AssertReturn(pFile, VERR_INTERNAL_ERROR_3);
4813 } while ((offInFile = offUnsigned - pFile->offData) >= RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE));
4814 }
4815 else
4816 {
4817 /* Seek backwards. */
4818 do
4819 {
4820 pFile = RTListGetPrev(&pIsoMaker->FinalizedFiles, pFile, RTFSISOMAKERFILE, FinalizedEntry);
4821 AssertReturn(pFile, VERR_INTERNAL_ERROR_3);
4822 } while ((offInFile = offUnsigned - pFile->offData) >= RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE));
4823 }
4824
4825 /*
4826 * Update the hint/current file.
4827 */
4828 if (pThis->pFileHint != pFile)
4829 {
4830 pThis->pFileHint = pFile;
4831 if (pThis->hVfsSrcFile != NIL_RTVFSFILE)
4832 {
4833 RTVfsFileRelease(pThis->hVfsSrcFile);
4834 pThis->hVfsSrcFile = NIL_RTVFSFILE;
4835 }
4836 }
4837
4838 /*
4839 * Produce data bits according to the source type.
4840 */
4841 if (offInFile < pFile->cbData)
4842 {
4843 int rc;
4844 size_t cbToRead = RT_MIN(cbBuf, pFile->cbData - offInFile);
4845
4846 switch (pFile->enmSrcType)
4847 {
4848 case RTFSISOMAKERSRCTYPE_PATH:
4849 if (pThis->hVfsSrcFile == NIL_RTVFSFILE)
4850 {
4851 rc = RTVfsChainOpenFile(pFile->u.pszSrcPath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
4852 &pThis->hVfsSrcFile, NULL, NULL);
4853 AssertMsgRCReturn(rc, ("%s -> %Rrc\n", pFile->u.pszSrcPath, rc), rc);
4854 }
4855 rc = RTVfsFileReadAt(pThis->hVfsSrcFile, offInFile, pbBuf, cbToRead, NULL);
4856 AssertRC(rc);
4857 break;
4858
4859 case RTFSISOMAKERSRCTYPE_VFS_FILE:
4860 rc = RTVfsFileReadAt(pFile->u.hVfsFile, offInFile, pbBuf, cbToRead, NULL);
4861 AssertRC(rc);
4862 break;
4863
4864 case RTFSISOMAKERSRCTYPE_COMMON:
4865 rc = RTVfsFileReadAt(pIsoMaker->paCommonSources[pFile->u.Common.idxSrc],
4866 pFile->u.Common.offData + offInFile, pbBuf, cbToRead, NULL);
4867 AssertRC(rc);
4868 break;
4869
4870 case RTFSISOMAKERSRCTYPE_TRANS_TBL:
4871 if (pThis->hVfsSrcFile == NIL_RTVFSFILE)
4872 {
4873 rc = rtFsIsoMakerOutFile_ProduceTransTbl(pThis, pFile);
4874 AssertRCReturn(rc, rc);
4875 }
4876 rc = RTVfsFileReadAt(pThis->hVfsSrcFile, offInFile, pbBuf, cbToRead, NULL);
4877 AssertRC(rc);
4878 break;
4879
4880 default:
4881 AssertFailedReturn(VERR_INTERNAL_ERROR_5);
4882 }
4883 if (RT_FAILURE(rc))
4884 return rc;
4885 *pcbDone = cbToRead;
4886
4887 /*
4888 * Do boot info table patching.
4889 */
4890 if ( pFile->pBootInfoTable
4891 && offInFile < 64
4892 && offInFile + cbToRead > 8)
4893 {
4894 size_t offInBuf = offInFile < 8 ? 8 - (size_t)offInFile : 0;
4895 size_t offInTab = offInFile <= 8 ? 0 : (size_t)offInFile - 8;
4896 size_t cbToCopy = RT_MIN(sizeof(*pFile->pBootInfoTable) - offInTab, cbToRead - offInBuf);
4897 memcpy(&pbBuf[offInBuf], (uint8_t *)pFile->pBootInfoTable + offInTab, cbToCopy);
4898 }
4899
4900 /*
4901 * Check if we're into the zero padding at the end of the file now.
4902 */
4903 if ( cbToRead < cbBuf
4904 && (pFile->cbData & RTFSISOMAKER_SECTOR_OFFSET_MASK)
4905 && offInFile + cbToRead == pFile->cbData)
4906 {
4907 cbBuf -= cbToRead;
4908 pbBuf += cbToRead;
4909 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - (pFile->cbData & RTFSISOMAKER_SECTOR_OFFSET_MASK));
4910 memset(pbBuf, 0, cbZeros);
4911 *pcbDone += cbZeros;
4912 }
4913 }
4914 else
4915 {
4916 size_t cbZeros = RT_MIN(cbBuf, RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE) - offInFile);
4917 memset(pbBuf, 0, cbZeros);
4918 *pcbDone = cbZeros;
4919 }
4920 return VINF_SUCCESS;
4921}
4922
4923
4924/**
4925 * Generates ISO-9660 path table record into the specified buffer.
4926 *
4927 * @returns Number of bytes copied into the buffer.
4928 * @param pName The directory namespace node.
4929 * @param fUnicode Set if the name should be translated to big endian
4930 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4931 * @param pbBuf The buffer. This is large enough to hold the path
4932 * record (use RTFSISOMAKER_CALC_PATHREC_SIZE) and a zero
4933 * RTUTF16 terminator if @a fUnicode is true.
4934 */
4935static uint32_t rtFsIsoMakerOutFile_GeneratePathRec(PRTFSISOMAKERNAME pName, bool fUnicode, bool fLittleEndian, uint8_t *pbBuf)
4936{
4937 PISO9660PATHREC pPathRec = (PISO9660PATHREC)pbBuf;
4938 pPathRec->cbDirId = pName->cbNameInDirRec;
4939 pPathRec->cbExtAttr = 0;
4940 if (fLittleEndian)
4941 {
4942 pPathRec->offExtent = RT_H2LE_U32(pName->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4943 pPathRec->idParentRec = RT_H2LE_U16(pName->pParent ? pName->pParent->pDir->idPathTable : 1);
4944 }
4945 else
4946 {
4947 pPathRec->offExtent = RT_H2BE_U32(pName->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4948 pPathRec->idParentRec = RT_H2BE_U16(pName->pParent ? pName->pParent->pDir->idPathTable : 1);
4949 }
4950 if (!fUnicode)
4951 {
4952 memcpy(&pPathRec->achDirId[0], pName->szName, pName->cbNameInDirRec);
4953 if (pName->cbNameInDirRec & 1)
4954 pPathRec->achDirId[pName->cbNameInDirRec] = '\0';
4955 }
4956 else
4957 {
4958 /* Caller made sure there is space for a zero terminator character. */
4959 PRTUTF16 pwszTmp = (PRTUTF16)&pPathRec->achDirId[0];
4960 size_t cwcResult = 0;
4961 int rc = RTStrToUtf16BigEx(pName->szName, RTSTR_MAX, &pwszTmp, pName->cbNameInDirRec / sizeof(RTUTF16) + 1, &cwcResult);
4962 AssertRC(rc);
4963 Assert( cwcResult * sizeof(RTUTF16) == pName->cbNameInDirRec
4964 || (!pName->pParent && cwcResult == 0 && pName->cbNameInDirRec == 1) );
4965
4966 }
4967 return RTFSISOMAKER_CALC_PATHREC_SIZE(pName->cbNameInDirRec);
4968}
4969
4970
4971/**
4972 * Deals with situations where the destination buffer doesn't cover the whole
4973 * path table record.
4974 *
4975 * @returns Number of bytes copied into the buffer.
4976 * @param pName The directory namespace node.
4977 * @param fUnicode Set if the name should be translated to big endian
4978 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4979 * @param offInRec The offset into the path table record.
4980 * @param pbBuf The buffer.
4981 * @param cbBuf The buffer size.
4982 */
4983static uint32_t rtFsIsoMakerOutFile_GeneratePathRecPartial(PRTFSISOMAKERNAME pName, bool fUnicode, bool fLittleEndian,
4984 uint32_t offInRec, uint8_t *pbBuf, size_t cbBuf)
4985{
4986 uint8_t abTmpRec[256];
4987 size_t cbToCopy = rtFsIsoMakerOutFile_GeneratePathRec(pName, fUnicode, fLittleEndian, abTmpRec);
4988 cbToCopy = RT_MIN(cbBuf, cbToCopy - offInRec);
4989 memcpy(pbBuf, &abTmpRec[offInRec], cbToCopy);
4990 return (uint32_t)cbToCopy;
4991}
4992
4993
4994/**
4995 * Generate path table records.
4996 *
4997 * This will generate record up to the end of the table. However, it will not
4998 * supply the zero padding in the last sector, the caller is expected to take
4999 * care of that.
5000 *
5001 * @returns Number of bytes written to the buffer.
5002 * @param ppDirHint Pointer to the directory hint for the namespace.
5003 * @param pFinalizedDirs The finalized directory data for the namespace.
5004 * @param fUnicode Set if the name should be translated to big endian
5005 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
5006 * @param fLittleEndian Set if we're generating little endian records, clear
5007 * if big endian records.
5008 * @param offInTable Offset into the path table.
5009 * @param pbBuf The output buffer.
5010 * @param cbBuf The buffer size.
5011 */
5012static size_t rtFsIsoMakerOutFile_ReadPathTable(PRTFSISOMAKERNAMEDIR *ppDirHint, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
5013 bool fUnicode, bool fLittleEndian, uint32_t offInTable,
5014 uint8_t *pbBuf, size_t cbBuf)
5015{
5016 /*
5017 * Figure out which directory to start with. We keep a hint in the instance.
5018 */
5019 PRTFSISOMAKERNAMEDIR pDir = *ppDirHint;
5020 if (!pDir)
5021 {
5022 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5023 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5024 }
5025 if (offInTable - pDir->offPathTable < RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec))
5026 { /* hit */ }
5027 /* Seek forwards: */
5028 else if (offInTable > pDir->offPathTable)
5029 do
5030 {
5031 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5032 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5033 } while (offInTable - pDir->offPathTable >= RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec));
5034 /* Back to the start: */
5035 else if (offInTable == 0)
5036 {
5037 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5038 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5039 }
5040 /* Seek backwards: */
5041 else
5042 do
5043 {
5044 pDir = RTListGetPrev(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5045 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5046 } while (offInTable - pDir->offPathTable >= RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec));
5047
5048 /*
5049 * Generate content.
5050 */
5051 size_t cbDone = 0;
5052 while ( cbBuf > 0
5053 && pDir)
5054 {
5055 PRTFSISOMAKERNAME pName = pDir->pName;
5056 uint8_t cbRec = RTFSISOMAKER_CALC_PATHREC_SIZE(pName->cbNameInDirRec);
5057 uint32_t cbCopied;
5058 if ( offInTable == pDir->offPathTable
5059 && cbBuf >= cbRec + fUnicode * 2U)
5060 cbCopied = rtFsIsoMakerOutFile_GeneratePathRec(pName, fUnicode, fLittleEndian, pbBuf);
5061 else
5062 cbCopied = rtFsIsoMakerOutFile_GeneratePathRecPartial(pName, fUnicode, fLittleEndian,
5063 offInTable - pDir->offPathTable, pbBuf, cbBuf);
5064 cbDone += cbCopied;
5065 offInTable += cbCopied;
5066 pbBuf += cbCopied;
5067 cbBuf -= cbCopied;
5068 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5069 }
5070
5071 /*
5072 * Update the hint.
5073 */
5074 *ppDirHint = pDir;
5075
5076 return cbDone;
5077}
5078
5079
5080/**
5081 * Generates ISO-9660 directory record into the specified buffer.
5082 *
5083 * The caller must deal with multi-extent copying and end of sector zero
5084 * padding.
5085 *
5086 * @returns Number of bytes copied into the buffer (pName->cbDirRec).
5087 * @param pName The namespace node.
5088 * @param fUnicode Set if the name should be translated to big endian
5089 * UTF-16BE / UCS-2BE, i.e. we're in the joliet namespace.
5090 * @param pbBuf The buffer. This is at least pName->cbDirRec bytes big
5091 * (i.e. at most 256 bytes).
5092 */
5093static uint32_t rtFsIsoMakerOutFile_GenerateDirRec(PRTFSISOMAKERNAME pName, bool fUnicode, uint8_t *pbBuf)
5094{
5095 /*
5096 * Emit a standard ISO-9660 directory record.
5097 */
5098 PISO9660DIRREC pDirRec = (PISO9660DIRREC)pbBuf;
5099 PCRTFSISOMAKEROBJ pObj = pName->pObj;
5100 PCRTFSISOMAKERNAMEDIR pDir = pName->pDir;
5101 if (pDir)
5102 {
5103 pDirRec->offExtent.be = RT_H2BE_U32(pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
5104 pDirRec->offExtent.le = RT_H2LE_U32(pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
5105 pDirRec->cbData.be = RT_H2BE_U32(pDir->cbDir);
5106 pDirRec->cbData.le = RT_H2LE_U32(pDir->cbDir);
5107 pDirRec->fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
5108 }
5109 else if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
5110 {
5111 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
5112 pDirRec->offExtent.be = RT_H2BE_U32(pFile->offData / RTFSISOMAKER_SECTOR_SIZE);
5113 pDirRec->offExtent.le = RT_H2LE_U32(pFile->offData / RTFSISOMAKER_SECTOR_SIZE);
5114 pDirRec->cbData.be = RT_H2BE_U32(pFile->cbData);
5115 pDirRec->cbData.le = RT_H2LE_U32(pFile->cbData);
5116 pDirRec->fFileFlags = 0;
5117 }
5118 else
5119 {
5120 pDirRec->offExtent.be = 0;
5121 pDirRec->offExtent.le = 0;
5122 pDirRec->cbData.be = 0;
5123 pDirRec->cbData.le = 0;
5124 pDirRec->fFileFlags = 0;
5125 }
5126 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pObj->BirthTime, &pDirRec->RecTime);
5127
5128 pDirRec->cbDirRec = pName->cbDirRec;
5129 pDirRec->cExtAttrBlocks = 0;
5130 pDirRec->bFileUnitSize = 0;
5131 pDirRec->bInterleaveGapSize = 0;
5132 pDirRec->VolumeSeqNo.be = RT_H2BE_U16_C(1);
5133 pDirRec->VolumeSeqNo.le = RT_H2LE_U16_C(1);
5134 pDirRec->bFileIdLength = pName->cbNameInDirRec;
5135
5136 if (!fUnicode)
5137 {
5138 memcpy(&pDirRec->achFileId[0], pName->szName, pName->cbNameInDirRec);
5139 if (!(pName->cbNameInDirRec & 1))
5140 pDirRec->achFileId[pName->cbNameInDirRec] = '\0';
5141 }
5142 else
5143 {
5144 /* Convert to big endian UTF-16. We're using a separate buffer here
5145 because of zero terminator (none in pDirRec) and misalignment. */
5146 RTUTF16 wszTmp[128];
5147 PRTUTF16 pwszTmp = &wszTmp[0];
5148 size_t cwcResult = 0;
5149 int rc = RTStrToUtf16BigEx(pName->szName, RTSTR_MAX, &pwszTmp, RT_ELEMENTS(wszTmp), &cwcResult);
5150 AssertRC(rc);
5151 Assert( cwcResult * sizeof(RTUTF16) == pName->cbNameInDirRec
5152 || (!pName->pParent && cwcResult == 0 && pName->cbNameInDirRec == 1) );
5153 memcpy(&pDirRec->achFileId[0], pwszTmp, pName->cbNameInDirRec);
5154 pDirRec->achFileId[pName->cbNameInDirRec] = '\0';
5155 }
5156
5157 /*
5158 * Rock ridge fields if enabled.
5159 */
5160 /** @todo rock ridge. */
5161
5162 return pName->cbDirRec;
5163}
5164
5165
5166/**
5167 * Generates ISO-9660 directory records into the specified buffer.
5168 *
5169 * @returns Number of bytes copied into the buffer.
5170 * @param pName The namespace node.
5171 * @param fUnicode Set if the name should be translated to big endian
5172 * UTF-16BE / UCS-2BE, i.e. we're in the joliet namespace.
5173 * @param pbBuf The buffer. This is at least pName->cbDirRecTotal bytes
5174 * big.
5175 */
5176static uint32_t rtFsIsoMakerOutFile_GenerateDirRecDirect(PRTFSISOMAKERNAME pName, bool fUnicode, uint8_t *pbBuf)
5177{
5178 /*
5179 * Normally there is just a single record without any zero padding.
5180 */
5181 uint32_t cbReturn = rtFsIsoMakerOutFile_GenerateDirRec(pName, fUnicode, pbBuf);
5182 if (RT_LIKELY(pName->cbDirRecTotal == cbReturn))
5183 return cbReturn;
5184 Assert(cbReturn < pName->cbDirRecTotal);
5185
5186 /*
5187 * Deal with multiple records.
5188 */
5189 if (pName->cDirRecs > 1)
5190 {
5191 Assert(pName->pObj->enmType == RTFSISOMAKEROBJTYPE_FILE);
5192 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pName->pObj;
5193
5194 /* Set max size and duplicate the first directory record cDirRecs - 1 times. */
5195 uint32_t const cbOne = cbReturn;
5196 PISO9660DIRREC pDirRec = (PISO9660DIRREC)pbBuf;
5197 pDirRec->cbData.be = RT_H2BE_U32_C(RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE);
5198 pDirRec->cbData.le = RT_H2LE_U32_C(RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE);
5199 pDirRec->fFileFlags |= ISO9660_FILE_FLAGS_MULTI_EXTENT;
5200
5201 PISO9660DIRREC pCurDirRec = pDirRec;
5202 uint32_t offExtent = (uint32_t)(pFile->offData / RTFSISOMAKER_SECTOR_SIZE);
5203 Assert(offExtent == ISO9660_GET_ENDIAN(&pDirRec->offExtent));
5204 for (uint32_t iDirRec = 1; iDirRec < pName->cDirRecs; iDirRec++)
5205 {
5206 pCurDirRec = (PISO9660DIRREC)memcpy(&pbBuf[cbReturn], pDirRec, cbOne);
5207
5208 offExtent += RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE / RTFSISOMAKER_SECTOR_SIZE;
5209 pCurDirRec->offExtent.le = RT_H2LE_U32(offExtent);
5210
5211 cbReturn += cbOne;
5212 iDirRec++;
5213 }
5214 Assert(cbReturn <= pName->cbDirRecTotal);
5215
5216 /* Adjust the size in the final record. */
5217 uint32_t cbDataLast = (uint32_t)(pFile->cbData % RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE);
5218 pCurDirRec->cbData.be = RT_H2BE_U32(cbDataLast);
5219 pCurDirRec->cbData.le = RT_H2LE_U32(cbDataLast);
5220 pCurDirRec->fFileFlags &= ~ISO9660_FILE_FLAGS_MULTI_EXTENT;
5221 }
5222
5223 /*
5224 * Do end of sector zero padding.
5225 */
5226 if (cbReturn < pName->cbDirRecTotal)
5227 memset(&pbBuf[cbReturn], 0, (uint32_t)pName->cbDirRecTotal - cbReturn);
5228
5229 return pName->cbDirRecTotal;
5230}
5231
5232
5233/**
5234 * Deals with situations where the destination buffer doesn't cover the whole
5235 * directory record.
5236 *
5237 * @returns Number of bytes copied into the buffer.
5238 * @param pName The namespace node.
5239 * @param fUnicode Set if the name should be translated to big endian
5240 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
5241 * @param off The offset into the directory record.
5242 * @param pbBuf The buffer.
5243 * @param cbBuf The buffer size.
5244 */
5245static uint32_t rtFsIsoMakerOutFile_GenerateDirRecPartial(PRTFSISOMAKERNAME pName, bool fUnicode,
5246 uint32_t off, uint8_t *pbBuf, size_t cbBuf)
5247{
5248 Assert(off < pName->cbDirRecTotal);
5249
5250 /*
5251 * This is reasonably simple when there is only one directory record and
5252 * without any padding.
5253 */
5254 uint8_t abTmpBuf[256];
5255 Assert(pName->cbDirRec <= sizeof(abTmpBuf));
5256 uint32_t const cbOne = rtFsIsoMakerOutFile_GenerateDirRec(pName, fUnicode, pbBuf);
5257 Assert(cbOne == pName->cbDirRec);
5258 if (cbOne == pName->cbDirRecTotal)
5259 {
5260 uint32_t cbToCopy = RT_MIN((uint32_t)cbBuf, cbOne - off);
5261 memcpy(pbBuf, &abTmpBuf[off], cbToCopy);
5262 return cbToCopy;
5263 }
5264 Assert(cbOne < pName->cbDirRecTotal);
5265
5266 /*
5267 * Single record and zero padding?
5268 */
5269 uint32_t cbCopied = 0;
5270 if (pName->cDirRecs == 1)
5271 {
5272 /* Anything from the record to copy? */
5273 if (off < cbOne)
5274 {
5275 cbCopied = RT_MIN((uint32_t)cbBuf, cbOne - off);
5276 memcpy(pbBuf, &abTmpBuf[off], cbCopied);
5277 pbBuf += cbCopied;
5278 cbBuf -= cbCopied;
5279 off += cbCopied;
5280 }
5281
5282 /* Anything from the zero padding? */
5283 if (off >= cbOne && cbBuf > 0)
5284 {
5285 uint32_t cbToZero = RT_MIN((uint32_t)cbBuf, (uint32_t)pName->cbDirRecTotal - off);
5286 memset(pbBuf, 0, cbToZero);
5287 cbCopied += cbToZero;
5288 }
5289 }
5290 /*
5291 * Multi-extent stuff. Need to modify the cbData member as we copy.
5292 */
5293 else
5294 {
5295 Assert(pName->pObj->enmType == RTFSISOMAKEROBJTYPE_FILE);
5296 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pName->pObj;
5297
5298 /* Max out the size. */
5299 PISO9660DIRREC pDirRec = (PISO9660DIRREC)abTmpBuf;
5300 pDirRec->cbData.be = RT_H2BE_U32_C(RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE);
5301 pDirRec->cbData.le = RT_H2LE_U32_C(RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE);
5302 pDirRec->fFileFlags |= ISO9660_FILE_FLAGS_MULTI_EXTENT;
5303
5304 /* Copy directory records. */
5305 uint32_t offDirRec = pName->offDirRec;
5306 uint32_t offExtent = pFile->offData / RTFSISOMAKER_SECTOR_SIZE;
5307 for (uint32_t i = 0; i < pName->cDirRecs && cbBuf > 0; i++)
5308 {
5309 uint32_t const offInRec = off - offDirRec;
5310 if (offInRec < cbOne)
5311 {
5312 /* Update the record. */
5313 pDirRec->offExtent.be = RT_H2BE_U32(offExtent);
5314 pDirRec->offExtent.le = RT_H2LE_U32(offExtent);
5315 if (i + 1 == pName->cDirRecs)
5316 {
5317 uint32_t cbDataLast = pFile->cbData % RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE;
5318 pDirRec->cbData.be = RT_H2BE_U32(cbDataLast);
5319 pDirRec->cbData.le = RT_H2LE_U32(cbDataLast);
5320 pDirRec->fFileFlags &= ~ISO9660_FILE_FLAGS_MULTI_EXTENT;
5321 }
5322
5323 /* Copy chunk. */
5324 uint32_t cbToCopy = RT_MIN((uint32_t)cbBuf, cbOne - offInRec);
5325 memcpy(pbBuf, &abTmpBuf[offInRec], cbToCopy);
5326 cbCopied += cbToCopy;
5327 pbBuf += cbToCopy;
5328 cbBuf -= cbToCopy;
5329 off += cbToCopy;
5330 }
5331
5332 offDirRec += cbOne;
5333 offExtent += RTFSISOMAKER_MAX_ISO9660_EXTENT_SIZE / RTFSISOMAKER_SECTOR_SIZE;
5334 }
5335
5336 /* Anything from the zero padding? */
5337 if (off >= offDirRec && cbBuf > 0)
5338 {
5339 uint32_t cbToZero = RT_MIN((uint32_t)cbBuf, (uint32_t)pName->cbDirRecTotal - offDirRec);
5340 memset(pbBuf, 0, cbToZero);
5341 cbCopied += cbToZero;
5342 }
5343 }
5344
5345 return cbCopied;
5346}
5347
5348
5349/**
5350 * Generate a '.' or '..' directory record.
5351 *
5352 * This is the same as rtFsIsoMakerOutFile_GenerateDirRec, but with the filename
5353 * reduced to 1 byte.
5354 *
5355 * @returns Number of bytes copied into the buffer.
5356 * @param pName The directory namespace node.
5357 * @param fUnicode Set if the name should be translated to big endian
5358 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
5359 * @param bDirId The directory ID (0x00 or 0x01).
5360 * @param off The offset into the directory record.
5361 * @param pbBuf The buffer.
5362 * @param cbBuf The buffer size.
5363 */
5364static uint32_t rtFsIsoMakerOutFile_GenerateSpecialDirRec(PRTFSISOMAKERNAME pName, bool fUnicode, uint8_t bDirId,
5365 uint32_t off, uint8_t *pbBuf, size_t cbBuf)
5366{
5367 Assert(off < pName->cbDirRec);
5368 Assert(pName->pDir);
5369
5370 /* Generate a regular directory record. */
5371 uint8_t abTmpBuf[256];
5372 Assert(off < pName->cbDirRec);
5373 size_t cbToCopy = rtFsIsoMakerOutFile_GenerateDirRec(pName, fUnicode, abTmpBuf);
5374 Assert(cbToCopy == pName->cbDirRec);
5375
5376 /* Replace the filename part. */
5377 PISO9660DIRREC pDirRec = (PISO9660DIRREC)abTmpBuf;
5378 if (pDirRec->bFileIdLength != 1)
5379 {
5380 uint8_t offSysUse = pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1) + RT_OFFSETOF(ISO9660DIRREC, achFileId);
5381 uint8_t cbSysUse = pDirRec->cbDirRec - offSysUse;
5382 if (cbSysUse > 0)
5383 memmove(&pDirRec->achFileId[1], &pbBuf[offSysUse], cbSysUse);
5384 pDirRec->bFileIdLength = 1;
5385 cbToCopy = RT_OFFSETOF(ISO9660DIRREC, achFileId) + 1 + cbSysUse;
5386 pDirRec->cbDirRec = (uint8_t)cbToCopy;
5387 }
5388 pDirRec->achFileId[0] = bDirId;
5389
5390 /* Do the copying. */
5391 cbToCopy = RT_MIN(cbBuf, cbToCopy - off);
5392 memcpy(pbBuf, &abTmpBuf[off], cbToCopy);
5393 return (uint32_t)cbToCopy;
5394}
5395
5396
5397/**
5398 * Read directory records.
5399 *
5400 * This locates the directory at @a offUnsigned and generates directory records
5401 * for it. Caller must repeat the call to get directory entries for the next
5402 * directory should there be desire for that.
5403 *
5404 * @returns Number of bytes copied into @a pbBuf.
5405 * @param ppDirHint Pointer to the directory hint for the namespace.
5406 * @param pIsoMaker The ISO maker instance.
5407 * @param pFinalizedDirs The finalized directory data for the namespace.
5408 * @param fUnicode Set if the name should be translated to big endian
5409 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
5410 * @param offUnsigned The ISO image byte offset of the requested data.
5411 * @param pbBuf The output buffer.
5412 * @param cbBuf How much to read.
5413 */
5414static size_t rtFsIsoMakerOutFile_ReadDirRecords(PRTFSISOMAKERNAMEDIR *ppDirHint, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
5415 bool fUnicode, uint64_t offUnsigned, uint8_t *pbBuf, size_t cbBuf)
5416{
5417 /*
5418 * Figure out which directory. We keep a hint in the instance.
5419 */
5420 uint64_t offInDir64;
5421 PRTFSISOMAKERNAMEDIR pDir = *ppDirHint;
5422 if (!pDir)
5423 {
5424 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5425 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5426 }
5427 if ((offInDir64 = offUnsigned - pDir->offDir) < RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE))
5428 { /* hit */ }
5429 /* Seek forwards: */
5430 else if (offUnsigned > pDir->offDir)
5431 do
5432 {
5433 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5434 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5435 } while ((offInDir64 = offUnsigned - pDir->offDir) >= RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE));
5436 /* Back to the start: */
5437 else if (pFinalizedDirs->offDirs / RTFSISOMAKER_SECTOR_SIZE == offUnsigned / RTFSISOMAKER_SECTOR_SIZE)
5438 {
5439 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5440 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5441 }
5442 /* Seek backwards: */
5443 else
5444 do
5445 {
5446 pDir = RTListGetPrev(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
5447 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
5448 } while ((offInDir64 = offUnsigned - pDir->offDir) >= RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE));
5449
5450 /*
5451 * Update the hint.
5452 */
5453 *ppDirHint = pDir;
5454
5455 /*
5456 * Generate content.
5457 */
5458 size_t cbDone = 0;
5459 uint32_t offInDir = (uint32_t)offInDir64;
5460 if (offInDir < pDir->cbDir)
5461 {
5462 PRTFSISOMAKERNAME pDirName = pDir->pName;
5463 PRTFSISOMAKERNAME pParentName = pDirName->pParent ? pDirName->pParent : pDirName;
5464 uint32_t cbSpecialRecs = (uint32_t)pDir->cbDirRec00 + pDir->cbDirRec01;
5465
5466 /*
5467 * Special '.' and/or '..' entries requested.
5468 */
5469 uint32_t iChild;
5470 if (offInDir < cbSpecialRecs)
5471 {
5472 /* do '.' */
5473 if (offInDir < pDir->cbDirRec00)
5474 {
5475 uint32_t cbCopied = rtFsIsoMakerOutFile_GenerateSpecialDirRec(pDirName, fUnicode, 0, offInDir, pbBuf, cbBuf);
5476 cbDone += cbCopied;
5477 offInDir += cbCopied;
5478 pbBuf += cbCopied;
5479 cbBuf -= cbCopied;
5480 }
5481
5482 /* do '..' */
5483 if (cbBuf > 0)
5484 {
5485 uint32_t cbCopied = rtFsIsoMakerOutFile_GenerateSpecialDirRec(pParentName, fUnicode, 1,
5486 offInDir - pDir->cbDirRec00, pbBuf, cbBuf);
5487 cbDone += cbCopied;
5488 offInDir += cbCopied;
5489 pbBuf += cbCopied;
5490 cbBuf -= cbCopied;
5491 }
5492
5493 iChild = 0;
5494 }
5495 /*
5496 * Locate the directory entry we should start with. We can do this
5497 * using binary searching on offInDir.
5498 */
5499 else
5500 {
5501 /** @todo binary search */
5502 iChild = 0;
5503 while (iChild < pDir->cChildren)
5504 {
5505 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
5506 if ((offInDir - pChild->offDirRec) < pChild->cbDirRecTotal)
5507 break;
5508 iChild++;
5509 }
5510 AssertReturnStmt(iChild < pDir->cChildren, *pbBuf = 0xff, 1);
5511 }
5512
5513 /*
5514 * Normal directory entries.
5515 */
5516 while ( cbBuf > 0
5517 && iChild < pDir->cChildren)
5518 {
5519 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
5520 uint32_t cbCopied;
5521 if ( offInDir == pChild->offDirRec
5522 && cbBuf >= pChild->cbDirRecTotal)
5523 cbCopied = rtFsIsoMakerOutFile_GenerateDirRecDirect(pChild, fUnicode, pbBuf);
5524 else
5525 cbCopied = rtFsIsoMakerOutFile_GenerateDirRecPartial(pChild, fUnicode, offInDir - pChild->offDirRec, pbBuf, cbBuf);
5526
5527 cbDone += cbCopied;
5528 offInDir += cbCopied;
5529 pbBuf += cbCopied;
5530 cbBuf -= cbCopied;
5531 iChild++;
5532 }
5533
5534 /*
5535 * Check if we're into the zero padding at the end of the directory now.
5536 */
5537 if ( cbBuf > 0
5538 && iChild >= pDir->cChildren)
5539 {
5540 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - (pDir->cbDir & RTFSISOMAKER_SECTOR_OFFSET_MASK));
5541 memset(pbBuf, 0, cbZeros);
5542 cbDone += cbZeros;
5543 }
5544 }
5545 else
5546 {
5547 cbDone = RT_MIN(cbBuf, RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE) - offInDir);
5548 memset(pbBuf, 0, cbDone);
5549 }
5550
5551 return cbDone;
5552}
5553
5554
5555/**
5556 * Read directory records or path table records.
5557 *
5558 * Will not necessarily fill the entire buffer. Caller must call again to get
5559 * more.
5560 *
5561 * @returns Number of bytes copied into @a pbBuf.
5562 * @param ppDirHint Pointer to the directory hint for the namespace.
5563 * @param pIsoMaker The ISO maker instance.
5564 * @param pNamespace The namespace.
5565 * @param pFinalizedDirs The finalized directory data for the namespace.
5566 * @param offUnsigned The ISO image byte offset of the requested data.
5567 * @param pbBuf The output buffer.
5568 * @param cbBuf How much to read.
5569 */
5570static size_t rtFsIsoMakerOutFile_ReadDirStructures(PRTFSISOMAKERNAMEDIR *ppDirHint, PRTFSISOMAKERNAMESPACE pNamespace,
5571 PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
5572 uint64_t offUnsigned, uint8_t *pbBuf, size_t cbBuf)
5573{
5574 if (offUnsigned < pFinalizedDirs->offPathTableL)
5575 return rtFsIsoMakerOutFile_ReadDirRecords(ppDirHint, pFinalizedDirs,
5576 pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
5577 offUnsigned, pbBuf, cbBuf);
5578
5579 uint64_t offInTable;
5580 if ((offInTable = offUnsigned - pFinalizedDirs->offPathTableL) < pFinalizedDirs->cbPathTable)
5581 return rtFsIsoMakerOutFile_ReadPathTable(ppDirHint, pFinalizedDirs,
5582 pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
5583 true /*fLittleEndian*/, (uint32_t)offInTable, pbBuf, cbBuf);
5584
5585 if ((offInTable = offUnsigned - pFinalizedDirs->offPathTableM) < pFinalizedDirs->cbPathTable)
5586 return rtFsIsoMakerOutFile_ReadPathTable(ppDirHint, pFinalizedDirs,
5587 pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
5588 false /*fLittleEndian*/, (uint32_t)offInTable, pbBuf, cbBuf);
5589
5590 /* ASSUME we're in the zero padding at the end of a path table. */
5591 Assert( offUnsigned - pFinalizedDirs->offPathTableL < RT_ALIGN_32(pFinalizedDirs->cbPathTable, RTFSISOMAKER_SECTOR_SIZE)
5592 || offUnsigned - pFinalizedDirs->offPathTableM < RT_ALIGN_32(pFinalizedDirs->cbPathTable, RTFSISOMAKER_SECTOR_SIZE));
5593 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - ((size_t)offUnsigned & RTFSISOMAKER_SECTOR_OFFSET_MASK));
5594 memset(pbBuf, 0, cbZeros);
5595 return cbZeros;
5596}
5597
5598
5599
5600/**
5601 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
5602 */
5603static DECLCALLBACK(int) rtFsIsoMakerOutFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
5604{
5605 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5606 PRTFSISOMAKERINT pIsoMaker = pThis->pIsoMaker;
5607 size_t cbBuf = pSgBuf->paSegs[0].cbSeg;
5608 uint8_t *pbBuf = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
5609
5610 Assert(pSgBuf->cSegs == 1);
5611 RT_NOREF(fBlocking);
5612
5613 /*
5614 * Process the offset, checking for end-of-file.
5615 */
5616 uint64_t offUnsigned;
5617 if (off < 0)
5618 offUnsigned = pThis->offCurPos;
5619 else
5620 offUnsigned = (uint64_t)off;
5621 if (offUnsigned >= pIsoMaker->cbFinalizedImage)
5622 {
5623 if (*pcbRead)
5624 {
5625 *pcbRead = 0;
5626 return VINF_EOF;
5627 }
5628 return VERR_EOF;
5629 }
5630 if ( !pcbRead
5631 && pIsoMaker->cbFinalizedImage - offUnsigned < cbBuf)
5632 return VERR_EOF;
5633
5634 /*
5635 * Produce the bytes.
5636 */
5637 int rc = VINF_SUCCESS;
5638 size_t cbRead = 0;
5639 while (cbBuf > 0)
5640 {
5641 size_t cbDone;
5642
5643 /* Betting on there being more file data than metadata, thus doing the
5644 offset switch in decending order. */
5645 if (offUnsigned >= pIsoMaker->offFirstFile)
5646 {
5647 if (offUnsigned < pIsoMaker->cbFinalizedImage)
5648 {
5649 rc = rtFsIsoMakerOutFile_ReadFileData(pThis, pIsoMaker, offUnsigned, pbBuf, cbBuf, &cbDone);
5650 if (RT_FAILURE(rc))
5651 break;
5652 }
5653 else
5654 {
5655 rc = pcbRead ? VINF_EOF : VERR_EOF;
5656 break;
5657 }
5658 }
5659 /*
5660 * Joliet directory structures.
5661 */
5662 else if ( offUnsigned >= pIsoMaker->JolietDirs.offDirs
5663 && pIsoMaker->JolietDirs.offDirs < pIsoMaker->JolietDirs.offPathTableL)
5664 cbDone = rtFsIsoMakerOutFile_ReadDirStructures(&pThis->pDirHintJoliet, &pIsoMaker->Joliet, &pIsoMaker->JolietDirs,
5665 offUnsigned, pbBuf, cbBuf);
5666 /*
5667 * Primary ISO directory structures.
5668 */
5669 else if (offUnsigned >= pIsoMaker->PrimaryIsoDirs.offDirs)
5670 cbDone = rtFsIsoMakerOutFile_ReadDirStructures(&pThis->pDirHintPrimaryIso, &pIsoMaker->PrimaryIso,
5671 &pIsoMaker->PrimaryIsoDirs, offUnsigned, pbBuf, cbBuf);
5672 /** @todo Insert El Torito stuff here? Probably okay to let it be in the file
5673 * area, right? */
5674 /*
5675 * Volume descriptors.
5676 */
5677 else if (offUnsigned >= _32K)
5678 {
5679 size_t offVolDescs = (size_t)offUnsigned - _32K;
5680 cbDone = RT_MIN(cbBuf, (pIsoMaker->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE) - offVolDescs);
5681 memcpy(pbBuf, &pIsoMaker->pbVolDescs[offVolDescs], cbDone);
5682 }
5683 /*
5684 * Zeros in the system area.
5685 */
5686 else if (offUnsigned >= pIsoMaker->cbSysArea)
5687 {
5688 cbDone = RT_MIN(cbBuf, _32K - (size_t)offUnsigned);
5689 memset(pbBuf, 0, cbDone);
5690 }
5691 /*
5692 * Actual data in the system area.
5693 */
5694 else
5695 {
5696 cbDone = RT_MIN(cbBuf, pIsoMaker->cbSysArea - (size_t)offUnsigned);
5697 memcpy(pbBuf, &pIsoMaker->pbSysArea[(size_t)offUnsigned], cbDone);
5698 }
5699
5700 /*
5701 * Common advance.
5702 */
5703 cbRead += cbDone;
5704 offUnsigned += cbDone;
5705 pbBuf += cbDone;
5706 cbBuf -= cbDone;
5707 }
5708
5709 if (pcbRead)
5710 *pcbRead = cbRead;
5711 return rc;
5712}
5713
5714
5715/**
5716 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
5717 */
5718static DECLCALLBACK(int) rtFsIsoMakerOutFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
5719{
5720 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
5721 return VERR_WRITE_PROTECT;
5722}
5723
5724
5725/**
5726 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
5727 */
5728static DECLCALLBACK(int) rtFsIsoMakerOutFile_Flush(void *pvThis)
5729{
5730 RT_NOREF(pvThis);
5731 return VINF_SUCCESS;
5732}
5733
5734
5735/**
5736 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
5737 */
5738static DECLCALLBACK(int) rtFsIsoMakerOutFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
5739 uint32_t *pfRetEvents)
5740{
5741 NOREF(pvThis);
5742 return RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
5743}
5744
5745
5746/**
5747 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
5748 */
5749static DECLCALLBACK(int) rtFsIsoMakerOutFile_Tell(void *pvThis, PRTFOFF poffActual)
5750{
5751 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5752 *poffActual = pThis->offCurPos;
5753 return VINF_SUCCESS;
5754}
5755
5756
5757/**
5758 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
5759 */
5760static DECLCALLBACK(int) rtFsIsoMakerOutFile_Skip(void *pvThis, RTFOFF cb)
5761{
5762 RTFOFF offIgnored;
5763 return rtFsIsoMakerOutFile_Seek(pvThis, cb, RTFILE_SEEK_CURRENT, &offIgnored);
5764}
5765
5766
5767/**
5768 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
5769 */
5770static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
5771{
5772 RT_NOREF(pvThis, fMode, fMask);
5773 return VERR_WRITE_PROTECT;
5774}
5775
5776
5777/**
5778 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
5779 */
5780static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
5781 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
5782{
5783 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
5784 return VERR_WRITE_PROTECT;
5785}
5786
5787
5788/**
5789 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
5790 */
5791static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
5792{
5793 RT_NOREF(pvThis, uid, gid);
5794 return VERR_WRITE_PROTECT;
5795}
5796
5797
5798/**
5799 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
5800 */
5801static DECLCALLBACK(int) rtFsIsoMakerOutFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
5802{
5803 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5804
5805 /*
5806 * Seek relative to which position.
5807 */
5808 uint64_t offWrt;
5809 switch (uMethod)
5810 {
5811 case RTFILE_SEEK_BEGIN:
5812 offWrt = 0;
5813 break;
5814
5815 case RTFILE_SEEK_CURRENT:
5816 offWrt = pThis->offCurPos;
5817 break;
5818
5819 case RTFILE_SEEK_END:
5820 offWrt = pThis->pIsoMaker->cbFinalizedImage;
5821 break;
5822
5823 default:
5824 return VERR_INTERNAL_ERROR_5;
5825 }
5826
5827 /*
5828 * Calc new position, take care to stay within RTFOFF type bounds.
5829 */
5830 uint64_t offNew;
5831 if (offSeek == 0)
5832 offNew = offWrt;
5833 else if (offSeek > 0)
5834 {
5835 offNew = offWrt + offSeek;
5836 if ( offNew < offWrt
5837 || offNew > RTFOFF_MAX)
5838 offNew = RTFOFF_MAX;
5839 }
5840 else if ((uint64_t)-offSeek < offWrt)
5841 offNew = offWrt + offSeek;
5842 else
5843 offNew = 0;
5844 pThis->offCurPos = offNew;
5845
5846 *poffActual = offNew;
5847 return VINF_SUCCESS;
5848}
5849
5850
5851/**
5852 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
5853 */
5854static DECLCALLBACK(int) rtFsIsoMakerOutFile_QuerySize(void *pvThis, uint64_t *pcbFile)
5855{
5856 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5857 *pcbFile = pThis->pIsoMaker->cbFinalizedImage;
5858 return VINF_SUCCESS;
5859}
5860
5861
5862/**
5863 * Standard file operations.
5864 */
5865DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIsoMakerOutputFileOps =
5866{
5867 { /* Stream */
5868 { /* Obj */
5869 RTVFSOBJOPS_VERSION,
5870 RTVFSOBJTYPE_FILE,
5871 "ISO Maker Output File",
5872 rtFsIsoMakerOutFile_Close,
5873 rtFsIsoMakerOutFile_QueryInfo,
5874 RTVFSOBJOPS_VERSION
5875 },
5876 RTVFSIOSTREAMOPS_VERSION,
5877 RTVFSIOSTREAMOPS_FEAT_NO_SG,
5878 rtFsIsoMakerOutFile_Read,
5879 rtFsIsoMakerOutFile_Write,
5880 rtFsIsoMakerOutFile_Flush,
5881 rtFsIsoMakerOutFile_PollOne,
5882 rtFsIsoMakerOutFile_Tell,
5883 rtFsIsoMakerOutFile_Skip,
5884 NULL /*ZeroFill*/,
5885 RTVFSIOSTREAMOPS_VERSION,
5886 },
5887 RTVFSFILEOPS_VERSION,
5888 0,
5889 { /* ObjSet */
5890 RTVFSOBJSETOPS_VERSION,
5891 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
5892 rtFsIsoMakerOutFile_SetMode,
5893 rtFsIsoMakerOutFile_SetTimes,
5894 rtFsIsoMakerOutFile_SetOwner,
5895 RTVFSOBJSETOPS_VERSION
5896 },
5897 rtFsIsoMakerOutFile_Seek,
5898 rtFsIsoMakerOutFile_QuerySize,
5899 RTVFSFILEOPS_VERSION
5900};
5901
5902
5903
5904/**
5905 * Creates a VFS file for a finalized ISO maker instanced.
5906 *
5907 * The file can be used to access the image. Both sequential and random access
5908 * are supported, so that this could in theory be hooked up to a CD/DVD-ROM
5909 * drive emulation and used as a virtual ISO image.
5910 *
5911 * @returns IRPT status code.
5912 * @param hIsoMaker The ISO maker handle.
5913 * @param phVfsFile Where to return the handle.
5914 */
5915RTDECL(int) RTFsIsoMakerCreateVfsOutputFile(RTFSISOMAKER hIsoMaker, PRTVFSFILE phVfsFile)
5916{
5917 PRTFSISOMAKERINT pThis = hIsoMaker;
5918 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
5919 AssertReturn(pThis->fFinalized, VERR_WRONG_ORDER);
5920 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
5921
5922 uint32_t cRefs = RTFsIsoMakerRetain(pThis);
5923 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5924
5925 PRTFSISOMAKEROUTPUTFILE pFileData;
5926 RTVFSFILE hVfsFile;
5927 int rc = RTVfsNewFile(&g_rtFsIsoMakerOutputFileOps, sizeof(*pFileData), RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_CREATE,
5928 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pFileData);
5929 if (RT_SUCCESS(rc))
5930 {
5931 pFileData->pIsoMaker = pThis;
5932 pFileData->offCurPos = 0;
5933 pFileData->pFileHint = NULL;
5934 pFileData->hVfsSrcFile = NIL_RTVFSFILE;
5935 pFileData->pDirHintPrimaryIso = NULL;
5936 pFileData->pDirHintJoliet = NULL;
5937 *phVfsFile = hVfsFile;
5938 return VINF_SUCCESS;
5939 }
5940
5941 RTFsIsoMakerRelease(pThis);
5942 *phVfsFile = NIL_RTVFSFILE;
5943 return rc;
5944}
5945
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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