VirtualBox

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

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

isomaker: Some fixes for bugs found when testing different buffer read sizes and read ordering.

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