VirtualBox

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

Last change on this file since 94145 was 93118, checked in by vboxsync, 3 years ago

IPRT/RTIsoMaker: Added an --name-setup-from-import option to better handle importing old ISOs without joliet present. We get into trouble when adding our files to the ISO in both primary iso and joliet space, since the guest might be inclined to show the joliet view (which is missing the files from the imported ISO).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 280.6 KB
Line 
1/* $Id: isomaker.cpp 93118 2022-01-04 01:41:47Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsisomaker.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/buildconfig.h>
38#include <iprt/err.h>
39#include <iprt/ctype.h>
40#include <iprt/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 RTVFSOBJOPS_VERSION
7457 },
7458 RTVFSIOSTREAMOPS_VERSION,
7459 RTVFSIOSTREAMOPS_FEAT_NO_SG,
7460 rtFsIsoMakerOutFile_Read,
7461 NULL /*Write*/,
7462 rtFsIsoMakerOutFile_Flush,
7463 NULL /*PollOne*/,
7464 rtFsIsoMakerOutFile_Tell,
7465 rtFsIsoMakerOutFile_Skip,
7466 NULL /*ZeroFill*/,
7467 RTVFSIOSTREAMOPS_VERSION,
7468 },
7469 RTVFSFILEOPS_VERSION,
7470 0,
7471 { /* ObjSet */
7472 RTVFSOBJSETOPS_VERSION,
7473 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
7474 NULL /*SetMode*/,
7475 NULL /*SetTimes*/,
7476 NULL /*SetOwner*/,
7477 RTVFSOBJSETOPS_VERSION
7478 },
7479 rtFsIsoMakerOutFile_Seek,
7480 rtFsIsoMakerOutFile_QuerySize,
7481 NULL /*SetSize*/,
7482 NULL /*QueryMaxSize*/,
7483 RTVFSFILEOPS_VERSION
7484};
7485
7486
7487
7488/**
7489 * Creates a VFS file for a finalized ISO maker instanced.
7490 *
7491 * The file can be used to access the image. Both sequential and random access
7492 * are supported, so that this could in theory be hooked up to a CD/DVD-ROM
7493 * drive emulation and used as a virtual ISO image.
7494 *
7495 * @returns IRPT status code.
7496 * @param hIsoMaker The ISO maker handle.
7497 * @param phVfsFile Where to return the handle.
7498 */
7499RTDECL(int) RTFsIsoMakerCreateVfsOutputFile(RTFSISOMAKER hIsoMaker, PRTVFSFILE phVfsFile)
7500{
7501 PRTFSISOMAKERINT pThis = hIsoMaker;
7502 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
7503 AssertReturn(pThis->fFinalized, VERR_WRONG_ORDER);
7504 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
7505
7506 uint32_t cRefs = RTFsIsoMakerRetain(pThis);
7507 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
7508
7509 PRTFSISOMAKEROUTPUTFILE pFileData;
7510 RTVFSFILE hVfsFile;
7511 int rc = RTVfsNewFile(&g_rtFsIsoMakerOutputFileOps, sizeof(*pFileData), RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_CREATE,
7512 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pFileData);
7513 if (RT_SUCCESS(rc))
7514 {
7515 pFileData->pIsoMaker = pThis;
7516 pFileData->offCurPos = 0;
7517 pFileData->pFileHint = NULL;
7518 pFileData->hVfsSrcFile = NIL_RTVFSFILE;
7519 pFileData->pDirHintPrimaryIso = NULL;
7520 pFileData->pDirHintJoliet = NULL;
7521 pFileData->iChildPrimaryIso = UINT32_MAX;
7522 pFileData->iChildJoliet = UINT32_MAX;
7523 *phVfsFile = hVfsFile;
7524 return VINF_SUCCESS;
7525 }
7526
7527 RTFsIsoMakerRelease(pThis);
7528 *phVfsFile = NIL_RTVFSFILE;
7529 return rc;
7530}
7531
Note: See TracBrowser for help on using the repository browser.

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