VirtualBox

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

Last change on this file since 76306 was 73603, checked in by vboxsync, 6 years ago

iprt/rtFsIsoMakerOutFile_GenerateDirRecDirect: fix for files ~8GB and larger.

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