VirtualBox

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

Last change on this file since 83743 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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