VirtualBox

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

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

scm --update-copyright-year

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