VirtualBox

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

Last change on this file since 67849 was 67849, checked in by vboxsync, 8 years ago

isomaker: Symlink fix and mode bit fix.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette