VirtualBox

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

Last change on this file since 78198 was 77734, checked in by vboxsync, 6 years ago

IPRT/isomaker: Preserve ISO-9660 names during import, don't enforce our normalization rules on them. This fixes booting debian 6 with kernel residing in a INSTALL.AMD directory (old ISO specs forbids '.' in directory names).

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