VirtualBox

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

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

IPRT,Storage: Adding RTVfsQueryLabel and internally a generic pfnQueryInfoEx method to the RTVFSOBJOPS function table. Untested implementation of the latter for iso/udf. bugref:9781

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

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