VirtualBox

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

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

IPRT/isomaker.cpp: A bunch of TODOs following from the fix in r152039, as it seems to me the fix isn't addressing the real issue.

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