VirtualBox

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

Last change on this file since 67512 was 67512, checked in by vboxsync, 7 years ago

IPRT: ISO maker updates (import related).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 198.5 KB
Line 
1/* $Id: isomaker.cpp 67512 2017-06-20 14:51:54Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsisomaker.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/buildconfig.h>
38#include <iprt/err.h>
39#include <iprt/ctype.h>
40#include <iprt/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/** UTF-8 name buffer. */
75#define RTFSISOMAKER_MAX_NAME_BUF 768
76
77/** TRANS.TBL left padding length.
78 * We keep the amount of padding low to avoid wasing memory when generating
79 * these long obsolete files. */
80#define RTFSISOMAKER_TRANS_TBL_LEFT_PAD 12
81
82/** Tests if @a a_ch is in the set of d-characters. */
83#define RTFSISOMAKER_IS_IN_D_CHARS(a_ch) (RT_C_IS_UPPER(a_ch) || RT_C_IS_DIGIT(a_ch) || (a_ch) == '_')
84
85/** Tests if @a a_ch is in the set of d-characters when uppercased. */
86#define RTFSISOMAKER_IS_UPPER_IN_D_CHARS(a_ch) (RT_C_IS_ALNUM(a_ch) || (a_ch) == '_')
87
88
89/** Calculates the path table record size given the name length.
90 * @note The root directory length is 1 (name byte is 0x00), we make sure this
91 * is the case in rtFsIsoMakerNormalizeNameForNamespace. */
92#define RTFSISOMAKER_CALC_PATHREC_SIZE(a_cbNameInDirRec) \
93 ( RT_UOFFSETOF(ISO9660PATHREC, achDirId[(a_cbNameInDirRec) + ((a_cbNameInDirRec) & 1)]) )
94
95
96/** No validation entry in the boot catalog. */
97#define VERR_ISOMK_BOOT_CAT_NO_VALIDATION_ENTRY (-24900)
98/** No default entry in the boot catalog. */
99#define VERR_ISOMK_BOOT_CAT_NO_DEFAULT_ENTRY (-24901)
100/** Expected section header. */
101#define VERR_ISOMK_BOOT_CAT_EXPECTED_SECTION_HEADER (-24902)
102/** Entry in a boot catalog section is empty. */
103#define VERR_ISOMK_BOOT_CAT_EMPTY_ENTRY (-24903)
104/** Entry in a boot catalog section is another section. */
105#define VERR_ISOMK_BOOT_CAT_INVALID_SECTION_SIZE (-24904)
106/** Unsectioned boot catalog entry. */
107#define VERR_ISOMK_BOOT_CAT_ERRATIC_ENTRY (-24905)
108
109
110/*********************************************************************************************************************************
111* Structures and Typedefs *
112*********************************************************************************************************************************/
113/** Pointer to an ISO maker object name space node. */
114typedef struct RTFSISOMAKERNAME *PRTFSISOMAKERNAME;
115/** Pointer to a const ISO maker object name space node. */
116typedef struct RTFSISOMAKERNAME const *PCRTFSISOMAKERNAME;
117/** Pointer to an ISO maker object name space node pointer. */
118typedef PRTFSISOMAKERNAME *PPRTFSISOMAKERNAME;
119
120/** Pointer to a common ISO image maker file system object. */
121typedef struct RTFSISOMAKEROBJ *PRTFSISOMAKEROBJ;
122/** Pointer to a const common ISO image maker file system object. */
123typedef struct RTFSISOMAKEROBJ const *PCRTFSISOMAKEROBJ;
124
125/** Pointer to a ISO maker file object. */
126typedef struct RTFSISOMAKERFILE *PRTFSISOMAKERFILE;
127/** Pointer to a const ISO maker file object. */
128typedef struct RTFSISOMAKERFILE const *PCRTFSISOMAKERFILE;
129
130/**
131 * Filesystem object type.
132 */
133typedef enum RTFSISOMAKEROBJTYPE
134{
135 RTFSISOMAKEROBJTYPE_INVALID = 0,
136 RTFSISOMAKEROBJTYPE_DIR,
137 RTFSISOMAKEROBJTYPE_FILE,
138 //RTFSISOMAKEROBJTYPE_SYMLINK,
139 RTFSISOMAKEROBJTYPE_END
140} RTFSISOMAKEROBJTYPE;
141
142/**
143 * Extra name space information required for directories.
144 */
145typedef struct RTFSISOMAKERNAMEDIR
146{
147 /** The location of the directory data. */
148 uint64_t offDir;
149 /** The size of the directory. */
150 uint32_t cbDir;
151 /** Number of children. */
152 uint32_t cChildren;
153 /** Sorted array of children. */
154 PPRTFSISOMAKERNAME papChildren;
155 /** The translate table file. */
156 PRTFSISOMAKERFILE pTransTblFile;
157
158 /** The offset in the path table (ISO-9660).
159 * This is set when finalizing the image. */
160 uint32_t offPathTable;
161 /** The path table identifier of this directory (ISO-9660).
162 * This is set when finalizing the image. */
163 uint16_t idPathTable;
164 /** The size of the first directory record (0x00 - '.'). */
165 uint8_t cbDirRec00;
166 /** The size of the second directory record (0x01 - '..'). */
167 uint8_t cbDirRec01;
168 /** Pointer to back to the namespace node this belongs to (for the finalized
169 * entry list). */
170 PRTFSISOMAKERNAME pName;
171 /** Entry in the list of finalized directories. */
172 RTLISTNODE FinalizedEntry;
173} RTFSISOMAKERNAMEDIR;
174/** Pointer to directory specfic namespace node info. */
175typedef RTFSISOMAKERNAMEDIR *PRTFSISOMAKERNAMEDIR;
176/** Pointer to const directory specfic namespace node info. */
177typedef const RTFSISOMAKERNAMEDIR *PCRTFSISOMAKERNAMEDIR;
178
179
180/**
181 * ISO maker object namespace node.
182 */
183typedef struct RTFSISOMAKERNAME
184{
185 /** Pointer to the file system object. */
186 PRTFSISOMAKEROBJ pObj;
187 /** Pointer to the partent directory, NULL if root dir. */
188 PRTFSISOMAKERNAME pParent;
189
190 /** Pointer to the directory information if this is a directory, NULL if not a
191 * directory. This is allocated together with this structure, so it doesn't need
192 * freeing. */
193 PRTFSISOMAKERNAMEDIR pDir;
194
195 /** The name specified when creating this namespace node. Helps navigating
196 * the namespace when we mangle or otherwise change the names.
197 * Allocated together with of this structure, no spearate free necessary. */
198 const char *pszSpecNm;
199
200 /** Alternative rock ridge name. */
201 char *pszRockRidgeNm;
202 /** Alternative TRANS.TBL name. */
203 char *pszTransNm;
204 /** Length of pszSpecNm. */
205 uint16_t cchSpecNm;
206 /** Length of pszRockRidgeNm. */
207 uint16_t cchRockRidgeNm;
208 /** Length of pszTransNm. */
209 uint16_t cchTransNm;
210
211 /** The depth in the namespace tree of this name. */
212 uint8_t uDepth;
213 /** Set if pszTransNm is allocated separately. Normally same as pszSpecNm. */
214 bool fRockRidgeNmAlloced : 1;
215 /** Set if pszTransNm is allocated separately. Normally same as pszSpecNm. */
216 bool fTransNmAlloced : 1;
217
218 /** The mode mask.
219 * Starts out as a copy of RTFSISOMAKEROBJ::fMode. */
220 RTFMODE fMode;
221 /** The owner ID.
222 * Starts out as a copy of RTFSISOMAKEROBJ::uid. */
223 RTUID uid;
224 /** The group ID.
225 * Starts out as a copy of RTFSISOMAKEROBJ::gid. */
226 RTGID gid;
227 /** The device number if a character or block device.
228 * This is for Rock Ridge. */
229 RTDEV Device;
230 /** The inode/serial number.
231 * This is for Rock Ridge. */
232 uint64_t INode;
233 /** The number of hardlinks to report in the file stats.
234 * This is for Rock Ridge. */
235 uint32_t cHardlinks;
236
237 /** The offset of the directory entry in the parent directory. */
238 uint32_t offDirRec;
239 /** Size of the directory record (ISO-9660).
240 * This is set when the image is being finalized. */
241 uint16_t cbDirRec;
242
243 /** The number of bytes the name requires in the directory record. */
244 uint16_t cbNameInDirRec;
245 /** The name length. */
246 uint16_t cchName;
247 /** The name. */
248 char szName[RT_FLEXIBLE_ARRAY];
249} RTFSISOMAKERNAME;
250
251/**
252 * A ISO maker namespace.
253 */
254typedef struct RTFSISOMAKERNAMESPACE
255{
256 /** The namespace root. */
257 PRTFSISOMAKERNAME pRoot;
258 /** Total number of name nodes in the namespace. */
259 uint32_t cNames;
260 /** Total number of directories in the namespace. */
261 uint32_t cDirs;
262 /** The namespace selector (RTFSISOMAKER_NAMESPACE_XXX). */
263 uint32_t fNamespace;
264 /** Offset into RTFSISOMAKERNAMESPACE of the name member. */
265 uint32_t offName;
266 /** The configuration level for this name space.
267 * - For UDF and HFS namespaces this is either @c true or @c false.
268 * - For the primary ISO-9660 namespace this is 1, 2, or 3.
269 * - For the joliet namespace this 0 (joliet disabled), 1, 2, or 3. */
270 uint8_t uLevel;
271 /** The rock ridge level: 1 - enabled; 2 - with ER tag.
272 * Linux behaves a little different when seeing the ER tag. */
273 uint8_t uRockRidgeLevel;
274 /** The TRANS.TBL filename if enabled, NULL if disabled.
275 * When not NULL, this may be pointing to heap or g_szTransTbl. */
276 char *pszTransTbl;
277 /** The system ID (ISO9660PRIMARYVOLDESC::achSystemId). Empty if NULL.
278 * When not NULL, this may be pointing to heap of g_szSystemId. */
279 char *pszSystemId;
280 /** The volume ID / label (ISO9660PRIMARYVOLDESC::achVolumeId).
281 * A string representation of RTFSISOMAKERINT::ImageCreationTime if NULL. */
282 char *pszVolumeId;
283 /** The volume set ID (ISO9660PRIMARYVOLDESC::achVolumeSetId). Empty if NULL. */
284 char *pszVolumeSetId;
285 /** The publisher ID or (root) file reference (ISO9660PRIMARYVOLDESC::achPublisherId). Empty if NULL. */
286 char *pszPublisherId;
287 /** The data preperer ID or (root) file reference (ISO9660PRIMARYVOLDESC::achDataPreparerId).
288 * Defaults to g_szPreparerIdPrimaryIso or g_szPreparerIdJoliet. */
289 char *pszDataPreparerId;
290 /** The application ID or (root) file reference (ISO9660PRIMARYVOLDESC::achApplicationId). None if NULL. */
291 char *pszApplicationId;
292 /** The copyright (root) file identifier (ISO9660PRIMARYVOLDESC::achCopyrightFileId). None if NULL. */
293 char *pszCopyrightFileId;
294 /** The abstract (root) file identifier (ISO9660PRIMARYVOLDESC::achAbstractFileId). None if NULL. */
295 char *pszAbstractFileId;
296 /** The bibliographic (root) file identifier (ISO9660PRIMARYVOLDESC::achBibliographicFileId). None if NULL. */
297 char *pszBibliographicFileId;
298} RTFSISOMAKERNAMESPACE;
299/** Pointer to a namespace. */
300typedef RTFSISOMAKERNAMESPACE *PRTFSISOMAKERNAMESPACE;
301/** Pointer to a const namespace. */
302typedef RTFSISOMAKERNAMESPACE const *PCRTFSISOMAKERNAMESPACE;
303
304
305/**
306 * Common base structure for the file system objects.
307 *
308 * The times are shared across all namespaces, while the uid, gid and mode are
309 * duplicates in each namespace.
310 */
311typedef struct RTFSISOMAKEROBJ
312{
313 /** The linear list entry of the image content. */
314 RTLISTNODE Entry;
315 /** The object index. */
316 uint32_t idxObj;
317 /** The type of this object. */
318 RTFSISOMAKEROBJTYPE enmType;
319
320 /** The primary ISO-9660 name space name. */
321 PRTFSISOMAKERNAME pPrimaryName;
322 /** The joliet name space name. */
323 PRTFSISOMAKERNAME pJolietName;
324 /** The UDF name space name. */
325 PRTFSISOMAKERNAME pUdfName;
326 /** The HFS name space name. */
327 PRTFSISOMAKERNAME pHfsName;
328
329 /** Birth (creation) time. */
330 RTTIMESPEC BirthTime;
331 /** Attribute change time. */
332 RTTIMESPEC ChangeTime;
333 /** Modification time. */
334 RTTIMESPEC ModificationTime;
335 /** Accessed time. */
336 RTTIMESPEC AccessedTime;
337
338 /** Owner ID. */
339 RTUID uid;
340 /** Group ID. */
341 RTGID gid;
342 /** Attributes (unix permissions bits mainly). */
343 RTFMODE fMode;
344
345 /** Used to make sure things like the boot catalog stays in the image even if
346 * it's not mapped into any of the namespaces. */
347 uint32_t cNotOrphan;
348} RTFSISOMAKEROBJ;
349
350
351/**
352 * File source type.
353 */
354typedef enum RTFSISOMAKERSRCTYPE
355{
356 RTFSISOMAKERSRCTYPE_INVALID = 0,
357 RTFSISOMAKERSRCTYPE_PATH,
358 RTFSISOMAKERSRCTYPE_VFS_FILE,
359 RTFSISOMAKERSRCTYPE_COMMON,
360 RTFSISOMAKERSRCTYPE_TRANS_TBL,
361 RTFSISOMAKERSRCTYPE_END
362} RTFSISOMAKERSRCTYPE;
363
364/**
365 * ISO maker file object.
366 */
367typedef struct RTFSISOMAKERFILE
368{
369 /** The common bit. */
370 RTFSISOMAKEROBJ Core;
371 /** The file data size. */
372 uint64_t cbData;
373 /** Byte offset of the data in the image.
374 * UINT64_MAX until the location is finalized. */
375 uint64_t offData;
376
377 /** The type of source object. */
378 RTFSISOMAKERSRCTYPE enmSrcType;
379 /** The source data. */
380 union
381 {
382 /** Path to the source file.
383 * Allocated together with this structure. */
384 const char *pszSrcPath;
385 /** Source VFS file. */
386 RTVFSFILE hVfsFile;
387 /** Source is a part of a common VFS file. */
388 struct
389 {
390 /** The offset into the file */
391 uint64_t offData;
392 /** The index of the common file. */
393 uint32_t idxSrc;
394 } Common;
395 /** The directory the translation table belongs to. */
396 PRTFSISOMAKERNAME pTransTblDir;
397 } u;
398
399 /** Boot info table to patch into the file.
400 * This is calculated during file finalization as it needs the file location. */
401 PISO9660SYSLINUXINFOTABLE pBootInfoTable;
402
403 /** Entry in the list of finalized directories. */
404 RTLISTNODE FinalizedEntry;
405} RTFSISOMAKERFILE;
406
407
408/**
409 * ISO maker directory object.
410 *
411 * Unlike files, the allocation info is name space specific and lives in the
412 * corresponding RTFSISOMAKERNAMEDIR structures.
413 */
414typedef struct RTFSISOMAKERDIR
415{
416 /** The common bit. */
417 RTFSISOMAKEROBJ Core;
418} RTFSISOMAKERDIR;
419/** Pointer to an ISO maker directory object. */
420typedef RTFSISOMAKERDIR *PRTFSISOMAKERDIR;
421
422
423
424/**
425 * Instance data for a ISO image maker.
426 */
427typedef struct RTFSISOMAKERINT
428{
429 /** Magic value (RTFSISOMAKERINT_MAGIC). */
430 uint32_t uMagic;
431 /** Reference counter. */
432 uint32_t volatile cRefs;
433
434 /** Set after we've been fed the first bit of content.
435 * This means that the namespace configuration has been finalized and can no
436 * longer be changed because it's simply too much work to do adjustments
437 * after having started to add files. */
438 bool fSeenContent;
439 /** Set once we've finalized the image structures.
440 * After this no more changes are allowed. */
441 bool fFinalized;
442
443 /** The primary ISO-9660 namespace. */
444 RTFSISOMAKERNAMESPACE PrimaryIso;
445 /** The joliet namespace. */
446 RTFSISOMAKERNAMESPACE Joliet;
447 /** The UDF namespace. */
448 RTFSISOMAKERNAMESPACE Udf;
449 /** The hybrid HFS+ namespace. */
450 RTFSISOMAKERNAMESPACE Hfs;
451
452 /** The list of objects (RTFSISOMAKEROBJ). */
453 RTLISTANCHOR ObjectHead;
454 /** Number of objects in the image (ObjectHead).
455 * This is used to number them, i.e. create RTFSISOMAKEROBJ::idxObj. */
456 uint32_t cObjects;
457
458 /** Amount of file data. */
459 uint64_t cbData;
460 /** Number of volume descriptors. */
461 uint32_t cVolumeDescriptors;
462
463 /** The 'now' timestamp we use for the whole image.
464 * This way we'll save lots of RTTimeNow calls and have similar timestamps
465 * over the whole image. */
466 RTTIMESPEC ImageCreationTime;
467 /** The default owner ID. */
468 RTUID uidDefault;
469 /** The default group ID. */
470 RTGID gidDefault;
471 /** The default file mode mask. */
472 RTFMODE fDefaultFileMode;
473 /** The default file mode mask. */
474 RTFMODE fDefaultDirMode;
475
476 /** Number of common source files. */
477 uint32_t cCommonSources;
478 /** Array of common source file handles. */
479 PRTVFSFILE paCommonSources;
480
481 /** @name Boot related stuff
482 * @{ */
483 /** The boot catalog file. */
484 PRTFSISOMAKERFILE pBootCatFile;
485 /** Per boot catalog entry data needed for updating offsets when finalizing. */
486 struct
487 {
488 /** The type (ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY,
489 * ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER,
490 * ISO9660_ELTORITO_HEADER_ID_FINAL_SECTION_HEADER,
491 * ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE or
492 * ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE). */
493 uint8_t bType;
494 /** Number of entries related to this one. This is zero for unused entries,
495 * 2 for the validation entry, 2+ for section headers, and 1 for images. */
496 uint8_t cEntries;
497 /** The boot file. */
498 PRTFSISOMAKERFILE pBootFile;
499 } aBootCatEntries[64];
500 /** @} */
501
502 /** @name Finalized image stuff
503 * @{ */
504 /** The finalized image size. */
505 uint64_t cbFinalizedImage;
506 /** System area content (sectors 0 thur 15). This is NULL if the system area
507 * are all zeros, which is often the case. Hybrid ISOs have an MBR followed by
508 * a GUID partition table here, helping making the image bootable when
509 * transfered to a USB stick. */
510 uint8_t *pbSysArea;
511 /** Number of non-zero system area bytes pointed to by pbSysArea. */
512 size_t cbSysArea;
513
514 /** Pointer to the buffer holding the volume descriptors. */
515 uint8_t *pbVolDescs;
516 /** Pointer to the primary volume descriptor. */
517 PISO9660PRIMARYVOLDESC pPrimaryVolDesc;
518 /** El Torito volume descriptor. */
519 PISO9660BOOTRECORDELTORITO pElToritoDesc;
520 /** Pointer to the primary volume descriptor. */
521 PISO9660SUPVOLDESC pJolietVolDesc;
522 /** Terminating ISO-9660 volume descriptor. */
523 PISO9660VOLDESCHDR pTerminatorVolDesc;
524
525 /** Finalized ISO-9660 directory structures. */
526 struct RTFSISOMAKERFINALIZEDDIRS
527 {
528 /** The image byte offset of the first directory. */
529 uint64_t offDirs;
530 /** The image byte offset of the little endian path table.
531 * This always follows offDirs. */
532 uint64_t offPathTableL;
533 /** The image byte offset of the big endian path table.
534 * This always follows offPathTableL. */
535 uint64_t offPathTableM;
536 /** The size of the path table. */
537 uint32_t cbPathTable;
538 /** List of finalized directories for this namespace.
539 * The list is in path table order so it can be generated on the fly. The
540 * directories will be ordered in the same way. */
541 RTLISTANCHOR FinalizedDirs;
542 /** Rock ridge spill file. */
543 PRTFSISOMAKERFILE pRRSpillFile;
544 }
545 /** The finalized directory data for the primary ISO-9660 namespace. */
546 PrimaryIsoDirs,
547 /** The finalized directory data for the joliet namespace. */
548 JolietDirs;
549
550 /** The image byte offset of the first file. */
551 uint64_t offFirstFile;
552 /** Finalized file head (RTFSISOMAKERFILE).
553 * The list is ordered by disk location. Files are following the
554 * directories and path tables. */
555 RTLISTANCHOR FinalizedFiles;
556 /** @} */
557
558} RTFSISOMAKERINT;
559/** Pointer to an ISO maker instance. */
560typedef RTFSISOMAKERINT *PRTFSISOMAKERINT;
561
562/** Pointer to the data for finalized ISO-9660 (primary / joliet) dirs. */
563typedef struct RTFSISOMAKERINT::RTFSISOMAKERFINALIZEDDIRS *PRTFSISOMAKERFINALIZEDDIRS;
564
565
566/**
567 * Instance data of an ISO maker output file.
568 */
569typedef struct RTFSISOMAKEROUTPUTFILE
570{
571 /** The ISO maker (owns a reference). */
572 PRTFSISOMAKERINT pIsoMaker;
573 /** The current file position. */
574 uint64_t offCurPos;
575 /** Current file hint. */
576 PRTFSISOMAKERFILE pFileHint;
577 /** Source file corresponding to pFileHint.
578 * This is used when dealing with a RTFSISOMAKERSRCTYPE_VFS_FILE or
579 * RTFSISOMAKERSRCTYPE_TRANS_TBL file. */
580 RTVFSFILE hVfsSrcFile;
581 /** Current directory hint for the primary ISO namespace. */
582 PRTFSISOMAKERNAMEDIR pDirHintPrimaryIso;
583 /** Current directory hint for the joliet namespace. */
584 PRTFSISOMAKERNAMEDIR pDirHintJoliet;
585} RTFSISOMAKEROUTPUTFILE;
586/** Pointer to the instance data of an ISO maker output file. */
587typedef RTFSISOMAKEROUTPUTFILE *PRTFSISOMAKEROUTPUTFILE;
588
589
590
591/*********************************************************************************************************************************
592* Structures and Typedefs *
593*********************************************************************************************************************************/
594/**
595 * Help for iterating over namespaces.
596 */
597static const struct
598{
599 /** The RTFSISOMAKER_NAMESPACE_XXX indicator. */
600 uint32_t fNamespace;
601 /** Offset into RTFSISOMAKERINT of the namespace member. */
602 uintptr_t offNamespace;
603 /** Offset into RTFSISOMAKERNAMESPACE of the name member. */
604 uintptr_t offName;
605 /** Namespace name for debugging purposes. */
606 const char *pszName;
607} g_aRTFsIsoNamespaces[] =
608{
609 { RTFSISOMAKER_NAMESPACE_ISO_9660, RT_OFFSETOF(RTFSISOMAKERINT, PrimaryIso), RT_OFFSETOF(RTFSISOMAKEROBJ, pPrimaryName), "iso-9660" },
610 { RTFSISOMAKER_NAMESPACE_JOLIET, RT_OFFSETOF(RTFSISOMAKERINT, Joliet), RT_OFFSETOF(RTFSISOMAKEROBJ, pJolietName), "joliet" },
611 { RTFSISOMAKER_NAMESPACE_UDF, RT_OFFSETOF(RTFSISOMAKERINT, Udf), RT_OFFSETOF(RTFSISOMAKEROBJ, pUdfName), "udf" },
612 { RTFSISOMAKER_NAMESPACE_HFS, RT_OFFSETOF(RTFSISOMAKERINT, Hfs), RT_OFFSETOF(RTFSISOMAKEROBJ, pHfsName), "hfs" },
613};
614
615/**
616 * Translates a single namespace flag (RTFSISOMAKER_NAMESPACE_XXX) to an
617 * index into g_aRTFsIsoNamespaces.
618 */
619static const uint8_t g_aidxRTFsIsoNamespaceFlagToIdx[] =
620{
621 /*[0] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
622 /*[RTFSISOMAKER_NAMESPACE_ISO_9660] = */ 0,
623 /*[RTFSISOMAKER_NAMESPACE_JOLIET] = */ 1,
624 /*[3] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
625 /*[RTFSISOMAKER_NAMESPACE_UDF] = */ 2,
626 /*[5] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
627 /*[6] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
628 /*[7] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
629 /*[RTFSISOMAKER_NAMESPACE_HFS] = */ 3,
630 /*[9] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
631 /*[10] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
632 /*[11] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
633 /*[12] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
634 /*[13] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
635 /*[14] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
636 /*[15] = */ RT_ELEMENTS(g_aRTFsIsoNamespaces),
637};
638
639/** The default translation table filename. */
640static const char g_szTransTbl[] = "TRANS.TBL";
641/** The default data preparer ID for the primary ISO-9660 volume descriptor. */
642static char g_szPreparerIdPrimaryIso[64] = "";
643/** The default data preparer ID for the joliet volume descriptor. */
644static char g_szPreparerIdJoliet[64] = "";
645/** The default system ID the primary ISO-9660 volume descriptor. */
646static char g_szSystemId[64] = "";
647
648
649
650/*********************************************************************************************************************************
651* Internal Functions *
652*********************************************************************************************************************************/
653static int rtFsIsoMakerObjSetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj,
654 PRTFSISOMAKERNAME pParent, const char *pchSpec, size_t cchSpec, PPRTFSISOMAKERNAME ppNewName);
655static int rtFsIsoMakerObjUnsetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj);
656static int rtFsIsoMakerAddUnnamedDirWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKERDIR *ppDir);
657static int rtFsIsoMakerAddUnnamedFileWorker(PRTFSISOMAKERINT pThis, PCRTFSOBJINFO pObjInfo, size_t cbExtra,
658 PRTFSISOMAKERFILE *ppFile);
659static int rtFsIsoMakerObjRemoveWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKEROBJ pObj);
660
661static DECLCALLBACK(int) rtFsIsoMakerOutFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual);
662
663
664
665/**
666 * Creates an ISO maker instance.
667 *
668 * @returns IPRT status code.
669 * @param phIsoMaker Where to return the handle to the new ISO maker.
670 */
671RTDECL(int) RTFsIsoMakerCreate(PRTFSISOMAKER phIsoMaker)
672{
673 /*
674 * Do some integrity checks first.
675 */
676 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_ISO_9660]].fNamespace == RTFSISOMAKER_NAMESPACE_ISO_9660,
677 VERR_INTERNAL_ERROR_5);
678 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_JOLIET]].fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
679 VERR_INTERNAL_ERROR_5);
680 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_UDF]].fNamespace == RTFSISOMAKER_NAMESPACE_UDF,
681 VERR_INTERNAL_ERROR_5);
682 AssertReturn(g_aRTFsIsoNamespaces[g_aidxRTFsIsoNamespaceFlagToIdx[RTFSISOMAKER_NAMESPACE_HFS]].fNamespace == RTFSISOMAKER_NAMESPACE_HFS,
683 VERR_INTERNAL_ERROR_5);
684
685 if (g_szPreparerIdPrimaryIso[0] == '\0')
686 RTStrPrintf(g_szPreparerIdPrimaryIso, sizeof(g_szPreparerIdPrimaryIso), "IPRT ISO MAKER V%u.%u.%u R%s",
687 RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild(), RTBldCfgRevisionStr());
688 if (g_szPreparerIdJoliet[0] == '\0')
689 RTStrPrintf(g_szPreparerIdJoliet, sizeof(g_szPreparerIdJoliet),
690 "IPRT ISO Maker v%s r%s", RTBldCfgVersion(), RTBldCfgRevisionStr());
691 if (g_szSystemId[0] == '\0')
692 {
693 RTStrCopy(g_szSystemId, sizeof(g_szSystemId), RTBldCfgTargetDotArch());
694 RTStrToUpper(g_szSystemId);
695 }
696
697 /*
698 * Create the instance with defaults.
699 */
700 int rc;
701 PRTFSISOMAKERINT pThis = (PRTFSISOMAKERINT)RTMemAllocZ(sizeof(*pThis));
702 if (pThis)
703 {
704 pThis->uMagic = RTFSISOMAKERINT_MAGIC;
705 pThis->cRefs = 1;
706 //pThis->fSeenContent = false;
707 //pThis->fFinalized = false;
708
709 pThis->PrimaryIso.fNamespace = RTFSISOMAKER_NAMESPACE_ISO_9660;
710 pThis->PrimaryIso.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pPrimaryName);
711 pThis->PrimaryIso.uLevel = 3; /* 30 char names, large files */
712 pThis->PrimaryIso.uRockRidgeLevel = 1;
713 pThis->PrimaryIso.pszTransTbl = (char *)g_szTransTbl;
714 pThis->PrimaryIso.pszSystemId = g_szSystemId;
715 //pThis->PrimaryIso.pszVolumeId = NULL;
716 //pThis->PrimaryIso.pszSetVolumeId = NULL;
717 //pThis->PrimaryIso.pszPublisherId = NULL;
718 pThis->PrimaryIso.pszDataPreparerId = g_szPreparerIdPrimaryIso;
719 //pThis->PrimaryIso.pszApplicationId = NULL;
720 //pThis->PrimaryIso.pszCopyrightFileId= NULL;
721 //pThis->PrimaryIso.pszAbstractFileId = NULL;
722 //pThis->PrimaryIso.pszBibliographicFileId = NULL;
723
724 pThis->Joliet.fNamespace = RTFSISOMAKER_NAMESPACE_JOLIET;
725 pThis->Joliet.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pJolietName);
726 pThis->Joliet.uLevel = 3;
727 //pThis->Joliet.uRockRidgeLevel = 0;
728 //pThis->Joliet.pszTransTbl = NULL;
729 //pThis->Joliet.pszSystemId = NULL;
730 //pThis->Joliet.pszVolumeId = NULL;
731 //pThis->Joliet.pszSetVolumeId = NULL;
732 //pThis->Joliet.pszPublisherId = NULL;
733 pThis->Joliet.pszDataPreparerId = g_szPreparerIdJoliet;
734 //pThis->Joliet.pszApplicationId = NULL;
735 //pThis->Joliet.pszCopyrightFileId = NULL;
736 //pThis->Joliet.pszAbstractFileId = NULL;
737 //pThis->Joliet.pszBibliographicFileId = NULL;
738
739 pThis->Udf.fNamespace = RTFSISOMAKER_NAMESPACE_UDF;
740 pThis->Udf.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pUdfName);
741 //pThis->Udf.uLevel = 0;
742 //pThis->Udf.uRockRidgeLevel = 0;
743 //pThis->Udf.pszTransTbl = NULL;
744 //pThis->Udf.uRockRidgeLevel = 0;
745 //pThis->Udf.pszTransTbl = NULL;
746 //pThis->Udf.pszSystemId = NULL;
747 //pThis->Udf.pszVolumeId = NULL;
748 //pThis->Udf.pszSetVolumeId = NULL;
749 //pThis->Udf.pszPublisherId = NULL;
750 //pThis->Udf.pszDataPreparerId = NULL;
751 //pThis->Udf.pszApplicationId = NULL;
752 //pThis->Udf.pszCopyrightFileId = NULL;
753 //pThis->Udf.pszAbstractFileId = NULL;
754 //pThis->Udf.pszBibliographicFileId = NULL;
755
756 pThis->Hfs.fNamespace = RTFSISOMAKER_NAMESPACE_HFS;
757 pThis->Hfs.offName = RT_OFFSETOF(RTFSISOMAKEROBJ, pHfsName);
758 //pThis->Hfs.uLevel = 0;
759 //pThis->Hfs.uRockRidgeLevel = 0;
760 //pThis->Hfs.pszTransTbl = NULL;
761 //pThis->Hfs.pszSystemId = NULL;
762 //pThis->Hfs.pszVolumeId = NULL;
763 //pThis->Hfs.pszSetVolumeId = NULL;
764 //pThis->Hfs.pszPublisherId = NULL;
765 //pThis->Hfs.pszDataPreparerId = NULL;
766 //pThis->Hfs.pszApplicationId = NULL;
767 //pThis->Hfs.pszCopyrightFileId = NULL;
768 //pThis->Hfs.pszAbstractFileId = NULL;
769 //pThis->Hfs.pszBibliographicFileId = NULL;
770
771 RTListInit(&pThis->ObjectHead);
772 //pThis->cObjects = 0;
773 //pThis->cbData = 0;
774
775 pThis->cVolumeDescriptors = 3; /* primary, secondary joliet, terminator. */
776
777 //pThis->uidDefault = 0;
778 //pThis->gidDefault = 0;
779 pThis->fDefaultFileMode = 0444 | RTFS_TYPE_FILE | RTFS_DOS_ARCHIVED | RTFS_DOS_READONLY;
780 pThis->fDefaultDirMode = 0555 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_DOS_READONLY;
781
782 //pThis->cCommonSources = 0;
783 //pThis->paCommonSources = NULL;
784
785 //pThis->pBootCatFile = NULL;
786
787 pThis->cbFinalizedImage = UINT64_MAX;
788 //pThis->pbSysArea = NULL;
789 //pThis->cbSysArea = 0;
790 //pThis->pbVolDescs = NULL;
791 //pThis->pPrimaryVolDesc = NULL;
792 //pThis->pElToritoDesc = NULL;
793 //pThis->pJolietVolDesc = NULL;
794
795 pThis->PrimaryIsoDirs.offDirs = UINT64_MAX;
796 pThis->PrimaryIsoDirs.offPathTableL = UINT64_MAX;
797 pThis->PrimaryIsoDirs.offPathTableM = UINT64_MAX;
798 pThis->PrimaryIsoDirs.cbPathTable = 0;
799 RTListInit(&pThis->PrimaryIsoDirs.FinalizedDirs);
800 //pThis->PrimaryIsoDirs.pRRSpillFile = NULL;
801
802 pThis->JolietDirs.offDirs = UINT64_MAX;
803 pThis->JolietDirs.offPathTableL = UINT64_MAX;
804 pThis->JolietDirs.offPathTableM = UINT64_MAX;
805 pThis->JolietDirs.cbPathTable = 0;
806 RTListInit(&pThis->JolietDirs.FinalizedDirs);
807 //pThis->JolietDirs.pRRSpillFile = NULL;
808
809 pThis->offFirstFile = UINT64_MAX;
810 RTListInit(&pThis->FinalizedFiles);
811
812 RTTimeNow(&pThis->ImageCreationTime);
813
814 /*
815 * Add the root directory node with idObj == 0.
816 */
817 PRTFSISOMAKERDIR pDirRoot;
818 rc = rtFsIsoMakerAddUnnamedDirWorker(pThis, &pDirRoot);
819 if (RT_SUCCESS(rc))
820 {
821 *phIsoMaker = pThis;
822 return VINF_SUCCESS;
823 }
824
825 RTMemFree(pThis);
826 }
827 else
828 rc = VERR_NO_MEMORY;
829 return rc;
830}
831
832
833/**
834 * Frees an object.
835 *
836 * This is a worker for rtFsIsoMakerDestroy and RTFsIsoMakerObjRemove.
837 *
838 * @param pObj The object to free.
839 */
840DECLINLINE(void) rtFsIsoMakerObjDestroy(PRTFSISOMAKEROBJ pObj)
841{
842 if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
843 {
844 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
845 switch (pFile->enmSrcType)
846 {
847 case RTFSISOMAKERSRCTYPE_PATH:
848 pFile->u.pszSrcPath = NULL;
849 break;
850
851 case RTFSISOMAKERSRCTYPE_TRANS_TBL:
852 pFile->u.pTransTblDir = NULL;
853 break;
854
855 case RTFSISOMAKERSRCTYPE_VFS_FILE:
856 RTVfsFileRelease(pFile->u.hVfsFile);
857 pFile->u.hVfsFile = NIL_RTVFSFILE;
858 break;
859
860 case RTFSISOMAKERSRCTYPE_COMMON:
861 break;
862
863 case RTFSISOMAKERSRCTYPE_INVALID:
864 case RTFSISOMAKERSRCTYPE_END:
865 AssertFailed();
866 break;
867
868 /* no default, want warnings */
869 }
870 if (pFile->pBootInfoTable)
871 {
872 RTMemFree(pFile->pBootInfoTable);
873 pFile->pBootInfoTable = NULL;
874 }
875 }
876
877 RTMemFree(pObj);
878}
879
880
881/**
882 * Frees a namespace node.
883 *
884 * This is a worker for rtFsIsoMakerDestroyTree and rtFsIsoMakerObjUnsetName.
885 *
886 * @param pName The node to free.
887 */
888DECLINLINE(void) rtFsIsoMakerDestroyName(PRTFSISOMAKERNAME pName)
889{
890 if (pName->fRockRidgeNmAlloced)
891 {
892 RTMemFree(pName->pszRockRidgeNm);
893 pName->pszRockRidgeNm = NULL;
894 }
895 if (pName->fTransNmAlloced)
896 {
897 RTMemFree(pName->pszTransNm);
898 pName->pszTransNm = NULL;
899 }
900 RTMemFree(pName);
901}
902
903
904/**
905 * Destroys a namespace.
906 *
907 * @param pNamespace The namespace to destroy.
908 */
909static void rtFsIsoMakerDestroyTree(PRTFSISOMAKERNAMESPACE pNamespace)
910{
911 /*
912 * Recursively destroy the tree first.
913 */
914 PRTFSISOMAKERNAME pCur = pNamespace->pRoot;
915 if (pCur)
916 {
917 Assert(!pCur->pParent);
918 for (;;)
919 {
920 if ( pCur->pDir
921 && pCur->pDir->cChildren)
922 pCur = pCur->pDir->papChildren[pCur->pDir->cChildren - 1];
923 else
924 {
925 PRTFSISOMAKERNAME pNext = pCur->pParent;
926 rtFsIsoMakerDestroyName(pCur);
927
928 /* Unlink from parent, we're the last entry. */
929 if (pNext)
930 {
931 Assert(pNext->pDir->cChildren > 0);
932 pNext->pDir->cChildren--;
933 Assert(pNext->pDir->papChildren[pNext->pDir->cChildren] == pCur);
934 pNext->pDir->papChildren[pNext->pDir->cChildren] = NULL;
935 pCur = pNext;
936 }
937 else
938 {
939 Assert(pNamespace->pRoot == pCur);
940 break;
941 }
942 }
943 }
944 pNamespace->pRoot = NULL;
945 }
946
947 /*
948 * Free the translation table filename if allocated.
949 */
950 if (pNamespace->pszTransTbl)
951 {
952 if (pNamespace->pszTransTbl != g_szTransTbl)
953 RTMemFree(pNamespace->pszTransTbl);
954 pNamespace->pszTransTbl = NULL;
955 }
956
957 /*
958 * Free string IDs.
959 */
960 if (pNamespace->pszSystemId)
961 {
962 if (pNamespace->pszSystemId != g_szSystemId)
963 RTMemFree(pNamespace->pszSystemId);
964 pNamespace->pszSystemId = NULL;
965 }
966
967 if (pNamespace->pszVolumeId)
968 {
969 RTMemFree(pNamespace->pszVolumeId);
970 pNamespace->pszVolumeId = NULL;
971 }
972
973 if (pNamespace->pszVolumeSetId)
974 {
975 RTMemFree(pNamespace->pszVolumeSetId);
976 pNamespace->pszVolumeSetId = NULL;
977 }
978
979 if (pNamespace->pszPublisherId)
980 {
981 RTMemFree(pNamespace->pszPublisherId);
982 pNamespace->pszPublisherId = NULL;
983 }
984
985 if (pNamespace->pszDataPreparerId)
986 {
987 if ( pNamespace->pszDataPreparerId != g_szPreparerIdPrimaryIso
988 && pNamespace->pszDataPreparerId != g_szPreparerIdJoliet)
989 RTMemFree(pNamespace->pszDataPreparerId);
990 pNamespace->pszDataPreparerId = NULL;
991 }
992
993 if (pNamespace->pszApplicationId)
994 {
995 RTMemFree(pNamespace->pszApplicationId);
996 pNamespace->pszApplicationId = NULL;
997 }
998
999 if (pNamespace->pszCopyrightFileId)
1000 {
1001 RTMemFree(pNamespace->pszCopyrightFileId);
1002 pNamespace->pszCopyrightFileId = NULL;
1003 }
1004
1005 if (pNamespace->pszAbstractFileId)
1006 {
1007 RTMemFree(pNamespace->pszAbstractFileId);
1008 pNamespace->pszAbstractFileId = NULL;
1009 }
1010
1011 if (pNamespace->pszBibliographicFileId)
1012 {
1013 RTMemFree(pNamespace->pszBibliographicFileId);
1014 pNamespace->pszBibliographicFileId = NULL;
1015 }
1016}
1017
1018
1019/**
1020 * Destroys an ISO maker instance.
1021 *
1022 * @param pThis The ISO maker instance to destroy.
1023 */
1024static void rtFsIsoMakerDestroy(PRTFSISOMAKERINT pThis)
1025{
1026 rtFsIsoMakerDestroyTree(&pThis->PrimaryIso);
1027 rtFsIsoMakerDestroyTree(&pThis->Joliet);
1028 rtFsIsoMakerDestroyTree(&pThis->Udf);
1029 rtFsIsoMakerDestroyTree(&pThis->Hfs);
1030
1031 PRTFSISOMAKEROBJ pCur;
1032 PRTFSISOMAKEROBJ pNext;
1033 RTListForEachSafe(&pThis->ObjectHead, pCur, pNext, RTFSISOMAKEROBJ, Entry)
1034 {
1035 RTListNodeRemove(&pCur->Entry);
1036 rtFsIsoMakerObjDestroy(pCur);
1037 }
1038
1039 if (pThis->paCommonSources)
1040 {
1041 RTMemFree(pThis->paCommonSources);
1042 pThis->paCommonSources = NULL;
1043 }
1044
1045 pThis->uMagic = ~RTFSISOMAKERINT_MAGIC;
1046 RTMemFree(pThis);
1047}
1048
1049
1050/**
1051 * Retains a references to an ISO maker instance.
1052 *
1053 * @returns New reference count on success, UINT32_MAX if invalid handle.
1054 * @param hIsoMaker The ISO maker handle.
1055 */
1056RTDECL(uint32_t) RTFsIsoMakerRetain(RTFSISOMAKER hIsoMaker)
1057{
1058 PRTFSISOMAKERINT pThis = hIsoMaker;
1059 AssertPtrReturn(pThis, UINT32_MAX);
1060 AssertReturn(pThis->uMagic == RTFSISOMAKERINT_MAGIC, UINT32_MAX);
1061 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1062 Assert(cRefs > 1);
1063 Assert(cRefs < _64K);
1064 return cRefs;
1065}
1066
1067
1068/**
1069 * Releases a references to an ISO maker instance.
1070 *
1071 * @returns New reference count on success, UINT32_MAX if invalid handle.
1072 * @param hIsoMaker The ISO maker handle. NIL is ignored.
1073 */
1074RTDECL(uint32_t) RTFsIsoMakerRelease(RTFSISOMAKER hIsoMaker)
1075{
1076 PRTFSISOMAKERINT pThis = hIsoMaker;
1077 uint32_t cRefs;
1078 if (pThis == NIL_RTFSISOMAKER)
1079 cRefs = 0;
1080 else
1081 {
1082 AssertPtrReturn(pThis, UINT32_MAX);
1083 AssertReturn(pThis->uMagic == RTFSISOMAKERINT_MAGIC, UINT32_MAX);
1084 cRefs = ASMAtomicDecU32(&pThis->cRefs);
1085 Assert(cRefs < _64K);
1086 if (!cRefs)
1087 rtFsIsoMakerDestroy(pThis);
1088 }
1089 return cRefs;
1090}
1091
1092
1093/**
1094 * Sets the ISO-9660 level.
1095 *
1096 * @returns IPRT status code
1097 * @param hIsoMaker The ISO maker handle.
1098 * @param uIsoLevel The level, 1-3.
1099 */
1100RTDECL(int) RTFsIsoMakerSetIso9660Level(RTFSISOMAKER hIsoMaker, uint8_t uIsoLevel)
1101{
1102 PRTFSISOMAKERINT pThis = hIsoMaker;
1103 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
1104 AssertReturn(uIsoLevel <= 3, VERR_INVALID_PARAMETER);
1105 AssertReturn(uIsoLevel > 0, VERR_INVALID_PARAMETER); /* currently not possible to disable this */
1106 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1107
1108 pThis->PrimaryIso.uLevel = uIsoLevel;
1109 return VINF_SUCCESS;
1110}
1111
1112
1113/**
1114 * Sets the joliet level.
1115 *
1116 * @returns IPRT status code
1117 * @param hIsoMaker The ISO maker handle.
1118 * @param uJolietLevel The joliet UCS-2 level 1-3, or 0 to disable
1119 * joliet.
1120 */
1121RTDECL(int) RTFsIsoMakerSetJolietUcs2Level(RTFSISOMAKER hIsoMaker, uint8_t uJolietLevel)
1122{
1123 PRTFSISOMAKERINT pThis = hIsoMaker;
1124 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
1125 AssertReturn(uJolietLevel <= 3, VERR_INVALID_PARAMETER);
1126 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1127
1128 if (pThis->Joliet.uLevel != uJolietLevel)
1129 {
1130 if (uJolietLevel == 0)
1131 pThis->cVolumeDescriptors--;
1132 else if (pThis->Joliet.uLevel == 0)
1133 pThis->cVolumeDescriptors++;
1134 pThis->Joliet.uLevel = uJolietLevel;
1135 }
1136 return VINF_SUCCESS;
1137}
1138
1139
1140/**
1141 * Sets the rock ridge support level (on the primary ISO-9660 namespace).
1142 *
1143 * @returns IPRT status code
1144 * @param hIsoMaker The ISO maker handle.
1145 * @param uLevel 0 if disabled, 1 to just enable, 2 to enable and
1146 * write the ER tag.
1147 */
1148RTDECL(int) RTFsIsoMakerSetRockRidgeLevel(RTFSISOMAKER hIsoMaker, uint8_t uLevel)
1149{
1150 PRTFSISOMAKERINT pThis = hIsoMaker;
1151 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
1152 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1153 AssertReturn(uLevel <= 2, VERR_INVALID_PARAMETER);
1154
1155 pThis->PrimaryIso.uRockRidgeLevel = uLevel;
1156 return VINF_SUCCESS;
1157}
1158
1159
1160/**
1161 * Sets the rock ridge support level on the joliet namespace (experimental).
1162 *
1163 * @returns IPRT status code
1164 * @param hIsoMaker The ISO maker handle.
1165 * @param uLevel 0 if disabled, 1 to just enable, 2 to enable and
1166 * write the ER tag.
1167 */
1168RTDECL(int) RTFsIsoMakerSetJolietRockRidgeLevel(RTFSISOMAKER hIsoMaker, uint8_t uLevel)
1169{
1170 PRTFSISOMAKERINT pThis = hIsoMaker;
1171 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
1172 AssertReturn(!pThis->fSeenContent, VERR_WRONG_ORDER);
1173 AssertReturn(uLevel <= 2, VERR_INVALID_PARAMETER);
1174
1175 pThis->Joliet.uRockRidgeLevel = uLevel;
1176 return VINF_SUCCESS;
1177}
1178
1179
1180/**
1181 * Sets the content of the system area, i.e. the first 32KB of the image.
1182 *
1183 * This can be used to put generic boot related stuff.
1184 *
1185 * @note Other settings may overwrite parts of the content (yet to be
1186 * determined which).
1187 *
1188 * @returns IPRT status code
1189 * @param hIsoMaker The ISO maker handle.
1190 * @param pvContent The content to put in the system area.
1191 * @param cbContent The size of the content.
1192 * @param off The offset into the system area.
1193 */
1194RTDECL(int) RTFsIsoMakerSetSysAreaContent(RTFSISOMAKER hIsoMaker, void const *pvContent, size_t cbContent, uint32_t off)
1195{
1196 /*
1197 * Validate input.
1198 */
1199 PRTFSISOMAKERINT pThis = hIsoMaker;
1200 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
1201 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
1202 AssertReturn(cbContent > 0, VERR_OUT_OF_RANGE);
1203 AssertReturn(cbContent <= _32K, VERR_OUT_OF_RANGE);
1204 AssertReturn(off < _32K, VERR_OUT_OF_RANGE);
1205 size_t cbSysArea = off + cbContent;
1206 AssertReturn(cbSysArea <= _32K, VERR_OUT_OF_RANGE);
1207
1208 /*
1209 * Adjust the allocation and copy over the new/additional content.
1210 */
1211 if (pThis->cbSysArea < cbSysArea)
1212 {
1213 void *pvNew = RTMemRealloc(pThis->pbSysArea, cbSysArea);
1214 AssertReturn(pvNew, VERR_NO_MEMORY);
1215 pThis->pbSysArea = (uint8_t *)pvNew;
1216 memset(&pThis->pbSysArea[pThis->cbSysArea], 0, cbSysArea - pThis->cbSysArea);
1217 }
1218
1219 memcpy(&pThis->pbSysArea[off], pvContent, cbContent);
1220
1221 return VINF_SUCCESS;
1222}
1223
1224
1225/*
1226 *
1227 * Name space related internals.
1228 * Name space related internals.
1229 * Name space related internals.
1230 *
1231 */
1232
1233
1234/**
1235 * Gets the pointer to the name member for the given namespace.
1236 *
1237 * @returns Pointer to name member.
1238 * @param pObj The object to find a name member in.
1239 * @param pNamespace The namespace which name to calculate.
1240 */
1241DECLINLINE(PPRTFSISOMAKERNAME) rtFsIsoMakerObjGetNameForNamespace(PRTFSISOMAKEROBJ pObj, PCRTFSISOMAKERNAMESPACE pNamespace)
1242{
1243 return (PPRTFSISOMAKERNAME)((uintptr_t)pObj + pNamespace->offName);
1244}
1245
1246
1247/**
1248 * Locates a child object by its namespace name.
1249 *
1250 * @returns Pointer to the child if found, NULL if not.
1251 * @param pDirObj The directory object to search.
1252 * @param pszEntry The (namespace) entry name.
1253 * @param cchEntry The length of the name.
1254 */
1255static PRTFSISOMAKERNAME rtFsIsoMakerFindObjInDir(PRTFSISOMAKERNAME pDirObj, const char *pszEntry, size_t cchEntry)
1256{
1257 if (pDirObj)
1258 {
1259 PRTFSISOMAKERNAMEDIR pDir = pDirObj->pDir;
1260 AssertReturn(pDir, NULL);
1261
1262 uint32_t i = pDir->cChildren;
1263 while (i-- > 0)
1264 {
1265 PRTFSISOMAKERNAME pChild = pDir->papChildren[i];
1266 if ( pChild->cchName == cchEntry
1267 && RTStrNICmp(pChild->szName, pszEntry, cchEntry) == 0)
1268 return pChild;
1269 }
1270 }
1271 return NULL;
1272}
1273
1274
1275/**
1276 * Compares the two names according to ISO-9660 directory sorting rules.
1277 *
1278 * As long as we don't want to do case insensitive joliet sorting, this works
1279 * for joliet names to, I think.
1280 *
1281 * @returns 0 if equal, -1 if pszName1 comes first, 1 if pszName2 comes first.
1282 * @param pszName1 The first name.
1283 * @param pszName2 The second name.
1284 */
1285DECLINLINE(int) rtFsIsoMakerCompareIso9660Names(const char *pszName1, const char *pszName2)
1286{
1287 for (;;)
1288 {
1289 char const ch1 = *pszName1++;
1290 char const ch2 = *pszName2++;
1291 if (ch1 == ch2)
1292 {
1293 if (ch1)
1294 { /* likely */ }
1295 else
1296 return 0;
1297 }
1298 else if (ch1 == ';' || ch2 == ';')
1299 return ch1 == ';' ? -1 : 1;
1300 else if (ch1 == '.' || ch2 == '.')
1301 return ch1 == '.' ? -1 : 1;
1302 else
1303 return (unsigned char)ch1 < (unsigned char)ch2 ? -1 : 1;
1304 }
1305}
1306
1307
1308/**
1309 * Finds the index into papChildren where the given name should be inserted.
1310 *
1311 * @returns Index of the given name.
1312 * @param pNamespace The namspace.
1313 * @param pParent The parent namespace node.
1314 * @param pszName The name.
1315 */
1316static uint32_t rtFsIsoMakerFindInsertIndex(PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKERNAME pParent, const char *pszName)
1317{
1318 uint32_t idxRet = pParent->pDir->cChildren;
1319 if (idxRet > 0)
1320 {
1321 /*
1322 * The idea is to do binary search using a namespace specific compare
1323 * function. However, it looks like we can get away with using the
1324 * same compare function for all namespaces.
1325 */
1326 uint32_t idxStart = 0;
1327 uint32_t idxEnd = idxRet;
1328 PPRTFSISOMAKERNAME papChildren = pParent->pDir->papChildren;
1329 switch (pNamespace->fNamespace)
1330 {
1331 case RTFSISOMAKER_NAMESPACE_ISO_9660:
1332 case RTFSISOMAKER_NAMESPACE_JOLIET:
1333 case RTFSISOMAKER_NAMESPACE_UDF:
1334 case RTFSISOMAKER_NAMESPACE_HFS:
1335 for (;;)
1336 {
1337 idxRet = idxStart + (idxEnd - idxStart) / 2;
1338 PRTFSISOMAKERNAME pCur = papChildren[idxRet];
1339 int iDiff = rtFsIsoMakerCompareIso9660Names(pszName, pCur->szName);
1340 if (iDiff < 0)
1341 {
1342 if (idxRet > idxStart)
1343 idxEnd = idxRet;
1344 else
1345 break;
1346 }
1347 else
1348 {
1349 idxRet++;
1350 if ( iDiff != 0
1351 && idxRet < idxEnd)
1352 idxStart = idxRet;
1353 else
1354 break;
1355 }
1356 }
1357 break;
1358
1359 default:
1360 AssertFailed();
1361 break;
1362 }
1363 }
1364 return idxRet;
1365}
1366
1367
1368
1369/**
1370 * Locates a child entry by its specified name.
1371 *
1372 * @returns Pointer to the child if found, NULL if not.
1373 * @param pDirName The directory name to search.
1374 * @param pszEntry The (specified) entry name.
1375 * @param cchEntry The length of the name.
1376 */
1377static PRTFSISOMAKERNAME rtFsIsoMakerFindEntryInDirBySpec(PRTFSISOMAKERNAME pDirName, const char *pszEntry, size_t cchEntry)
1378{
1379 if (pDirName)
1380 {
1381 PRTFSISOMAKERNAMEDIR pDir = pDirName->pDir;
1382 AssertReturn(pDir, NULL);
1383
1384 uint32_t i = pDir->cChildren;
1385 while (i-- > 0)
1386 {
1387 PRTFSISOMAKERNAME pChild = pDir->papChildren[i];
1388 if ( pChild->cchSpecNm == cchEntry
1389 && RTStrNICmp(pChild->pszSpecNm, pszEntry, cchEntry) == 0)
1390 return pChild;
1391 }
1392 }
1393 return NULL;
1394}
1395
1396
1397/**
1398 * Locates a subdir object in any namespace by its specified name.
1399 *
1400 * This is used to avoid having one instance of RTFSISOMAKERDIR in each
1401 * namespace for the same directory.
1402 *
1403 * @returns Pointer to the subdir object if found, NULL if not.
1404 * @param pDirObj The directory object to search.
1405 * @param pszEntry The (specified) entry name.
1406 * @param cchEntry The length of the name.
1407 * @param fSkipNamespaces Namespaces to skip.
1408 * @sa rtFsIsoMakerFindEntryInDirBySpec
1409 */
1410static PRTFSISOMAKERDIR rtFsIsoMakerFindSubdirBySpec(PRTFSISOMAKERDIR pDirObj, const char *pszEntry, size_t cchEntry,
1411 uint32_t fSkipNamespaces)
1412{
1413 AssertReturn(pDirObj, NULL);
1414 AssertReturn(pDirObj->Core.enmType == RTFSISOMAKEROBJTYPE_DIR, NULL);
1415 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
1416 if (!(fSkipNamespaces & g_aRTFsIsoNamespaces[i].fNamespace))
1417 {
1418 PRTFSISOMAKERNAME pDirName = *(PPRTFSISOMAKERNAME)((uintptr_t)pDirObj + g_aRTFsIsoNamespaces[i].offName);
1419 if (pDirName)
1420 {
1421 PRTFSISOMAKERNAMEDIR pDir = pDirName->pDir;
1422 AssertStmt(pDir, continue);
1423
1424 uint32_t iChild = pDir->cChildren;
1425 while (iChild-- > 0)
1426 {
1427 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
1428 if ( pChild->cchSpecNm == cchEntry
1429 && pChild->pDir != NULL
1430 && RTStrNICmp(pChild->pszSpecNm, pszEntry, cchEntry) == 0)
1431 return (PRTFSISOMAKERDIR)pChild->pObj;
1432 }
1433 }
1434 }
1435 return NULL;
1436}
1437
1438
1439/**
1440 * Walks the given path by specified object names in a namespace.
1441 *
1442 * @returns IPRT status code.
1443 * @param pNamespace The namespace to walk the path in.
1444 * @param pszPath The path to walk.
1445 * @param ppName Where to return the name node that the path ends with.
1446 */
1447static int rtFsIsoMakerWalkPathBySpec(PRTFSISOMAKERNAMESPACE pNamespace, const char *pszPath, PPRTFSISOMAKERNAME ppName)
1448{
1449 *ppName = NULL;
1450 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INVALID_NAME);
1451
1452 /*
1453 * Deal with the special case of the root.
1454 */
1455 while (RTPATH_IS_SLASH(*pszPath))
1456 pszPath++;
1457 AssertReturn(*pszPath, VERR_INTERNAL_ERROR_4);
1458
1459 PRTFSISOMAKERNAME pCur = pNamespace->pRoot;
1460 if (!pCur)
1461 return *pszPath ? VERR_PATH_NOT_FOUND : VERR_FILE_NOT_FOUND;
1462 if (!*pszPath)
1463 {
1464 *ppName = pCur;
1465 return VINF_SUCCESS;
1466 }
1467
1468 /*
1469 * Now, do the rest of the path.
1470 */
1471 for (;;)
1472 {
1473 /*
1474 * Find the end of the component.
1475 */
1476 char ch;
1477 size_t cchComponent = 0;
1478 while ((ch = pszPath[cchComponent]) != '\0' && !RTPATH_IS_SLASH(ch))
1479 cchComponent++;
1480 if (!cchComponent)
1481 {
1482 *ppName = pCur;
1483 return VINF_SUCCESS;
1484 }
1485
1486 size_t offNext = cchComponent;
1487 while (RTPATH_IS_SLASH(ch))
1488 ch = pszPath[offNext++];
1489
1490 /*
1491 * Deal with dot and dot-dot.
1492 */
1493 if (cchComponent == 1 && pszPath[0] == '.')
1494 { /* nothing to do */ }
1495 else if (cchComponent == 2 && pszPath[0] == '.' && pszPath[1] == '.')
1496 {
1497 if (pCur->pParent)
1498 pCur = pCur->pParent;
1499 }
1500 /*
1501 * Look up the name.
1502 */
1503 else
1504 {
1505 PRTFSISOMAKERNAME pChild = rtFsIsoMakerFindEntryInDirBySpec(pCur, pszPath, cchComponent);
1506 if (!pChild)
1507 return pszPath[offNext] ? VERR_PATH_NOT_FOUND : VERR_FILE_NOT_FOUND;
1508 if ( (offNext > cchComponent)
1509 && !pChild->pDir)
1510 return VERR_NOT_A_DIRECTORY;
1511 pCur = pChild;
1512 }
1513
1514 /*
1515 * Skip ahead in the path.
1516 */
1517 pszPath += offNext;
1518 }
1519}
1520
1521
1522/**
1523 * Copy and convert a name to valid ISO-9660 (d-characters only).
1524 *
1525 * Worker for rtFsIsoMakerNormalizeNameForNamespace. ASSUMES it deals with
1526 * dots.
1527 *
1528 * @returns Length of the resulting string.
1529 * @param pszDst The output buffer.
1530 * @param cchDstMax The maximum number of (d-chars) to put in the output
1531 * buffer.
1532 * @param pchSrc The UTF-8 source string (not neccessarily terminated).
1533 * @param cchSrc The maximum number of chars to copy from the source
1534 * string.
1535 */
1536static size_t rtFsIsoMakerCopyIso9660Name(char *pszDst, size_t cchDstMax, const char *pchSrc, size_t cchSrc)
1537{
1538 const char *pchSrcIn = pchSrc;
1539 size_t offDst = 0;
1540 while ((size_t)(pchSrc - pchSrcIn) < cchSrc)
1541 {
1542 RTUNICP uc;
1543 int rc = RTStrGetCpEx(&pchSrc, &uc);
1544 if (RT_SUCCESS(rc))
1545 {
1546 if ( uc < 128
1547 && RTFSISOMAKER_IS_UPPER_IN_D_CHARS((char)uc))
1548 {
1549 pszDst[offDst++] = RT_C_TO_UPPER((char)uc);
1550 if (offDst >= cchDstMax)
1551 break;
1552 }
1553 }
1554 }
1555 pszDst[offDst] = '\0';
1556 return offDst;
1557}
1558
1559
1560/**
1561 * Normalizes a name for the primary ISO-9660 namespace.
1562 *
1563 * @returns IPRT status code.
1564 * @param pThis The ISO maker instance.
1565 * @param pParent The parent directory. NULL if root.
1566 * @param pchSrc The specified name to normalize (not necessarily zero
1567 * terminated).
1568 * @param cchSrc The length of the specified name.
1569 * @param fIsDir Indicates whether it's a directory or file (like).
1570 * @param pszDst The output buffer. Must be at least 32 bytes.
1571 * @param cbDst The size of the output buffer.
1572 * @param pcchDst Where to return the length of the returned string (i.e.
1573 * not counting the terminator).
1574 * @param pcbInDirRec Where to return the name size in the directory record.
1575 */
1576static int rtFsIsoMakerNormalizeNameForPrimaryIso9660(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAME pParent,
1577 const char *pchSrc, size_t cchSrc, bool fIsDir,
1578 char *pszDst, size_t cbDst, size_t *pcchDst, size_t *pcbInDirRec)
1579{
1580 AssertReturn(cbDst > ISO9660_MAX_NAME_LEN + 2, VERR_INTERNAL_ERROR_3);
1581
1582 /* Skip leading dots. */
1583 while (cchSrc > 0 && *pchSrc == '.')
1584 pchSrc++, cchSrc--;
1585 if (!cchSrc)
1586 {
1587 pchSrc = "DOTS";
1588 cchSrc = 4;
1589 }
1590
1591 /*
1592 * Produce a first name.
1593 */
1594 uint8_t const uIsoLevel = pThis->PrimaryIso.uLevel;
1595 size_t cchDst;
1596 size_t offDstDot;
1597 if (fIsDir)
1598 offDstDot = cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, uIsoLevel >= 2 ? ISO9660_MAX_NAME_LEN : 8,
1599 pchSrc, cchSrc);
1600 else
1601 {
1602 /* Look for the last dot and try preserve the extension when doing the conversion. */
1603 size_t offLastDot = cchSrc;
1604 for (size_t off = 0; off < cchSrc; off++)
1605 if (pchSrc[off] == '.')
1606 offLastDot = off;
1607
1608 if (offLastDot == cchSrc)
1609 offDstDot = cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, uIsoLevel >= 2 ? ISO9660_MAX_NAME_LEN : 8,
1610 pchSrc, cchSrc);
1611 else
1612 {
1613 const char * const pchSrcExt = &pchSrc[offLastDot + 1];
1614 size_t const cchSrcExt = cchSrc - offLastDot - 1;
1615 if (uIsoLevel < 2)
1616 {
1617 cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, 8, pchSrc, cchSrc);
1618 offDstDot = cchDst;
1619 pszDst[cchDst++] = '.';
1620 cchDst += rtFsIsoMakerCopyIso9660Name(&pszDst[cchDst], 3, pchSrcExt, cchSrcExt);
1621 }
1622 else
1623 {
1624 size_t cchDstExt = rtFsIsoMakerCopyIso9660Name(pszDst, ISO9660_MAX_NAME_LEN - 2, pchSrcExt, cchSrcExt);
1625 if (cchDstExt > 0)
1626 {
1627 size_t cchBasename = rtFsIsoMakerCopyIso9660Name(pszDst, ISO9660_MAX_NAME_LEN - 2,
1628 pchSrc, offLastDot);
1629 if (cchBasename + 1 + cchDstExt <= ISO9660_MAX_NAME_LEN)
1630 cchDst = cchBasename;
1631 else
1632 cchDst = ISO9660_MAX_NAME_LEN - 1 - RT_MIN(cchDstExt, 4);
1633 offDstDot = cchDst;
1634 pszDst[cchDst++] = '.';
1635 cchDst += rtFsIsoMakerCopyIso9660Name(&pszDst[cchDst], ISO9660_MAX_NAME_LEN - 1 - cchDst,
1636 pchSrcExt, cchSrcExt);
1637 }
1638 else
1639 offDstDot = cchDst = rtFsIsoMakerCopyIso9660Name(pszDst, ISO9660_MAX_NAME_LEN, pchSrc, cchSrc);
1640 }
1641 }
1642 }
1643
1644 /* Append version if not directory */
1645 if (!fIsDir)
1646 {
1647 pszDst[cchDst++] = ';';
1648 pszDst[cchDst++] = '1';
1649 pszDst[cchDst] = '\0';
1650 }
1651
1652 /*
1653 * Unique name?
1654 */
1655 if (!rtFsIsoMakerFindObjInDir(pParent, pszDst, cchDst))
1656 {
1657 *pcchDst = cchDst;
1658 *pcbInDirRec = cchDst;
1659 return VINF_SUCCESS;
1660 }
1661
1662 /*
1663 * Mangle the name till we've got a unique one.
1664 */
1665 size_t const cchMaxBasename = (uIsoLevel >= 2 ? ISO9660_MAX_NAME_LEN : 8) - (cchDst - offDstDot);
1666 size_t cchInserted = 0;
1667 for (uint32_t i = 0; i < _32K; i++)
1668 {
1669 /* Add a numberic infix. */
1670 char szOrd[64];
1671 size_t cchOrd = RTStrFormatU32(szOrd, sizeof(szOrd), i + 1, 10, -1, -1, 0 /*fFlags*/);
1672 Assert((ssize_t)cchOrd > 0);
1673
1674 /* Do we need to shuffle the suffix? */
1675 if (cchOrd > cchInserted)
1676 {
1677 if (offDstDot < cchMaxBasename)
1678 {
1679 memmove(&pszDst[offDstDot + 1], &pszDst[offDstDot], cchDst + 1 - offDstDot);
1680 cchDst++;
1681 offDstDot++;
1682 }
1683 cchInserted = cchOrd;
1684 }
1685
1686 /* Insert the new infix and try again. */
1687 memcpy(&pszDst[offDstDot - cchOrd], szOrd, cchOrd);
1688 if (!rtFsIsoMakerFindObjInDir(pParent, pszDst, cchDst))
1689 {
1690 *pcchDst = cchDst;
1691 *pcbInDirRec = cchDst;
1692 return VINF_SUCCESS;
1693 }
1694 }
1695 AssertFailed();
1696 return VERR_DUPLICATE;
1697}
1698
1699
1700/**
1701 * Normalizes a name for the specified name space.
1702 *
1703 * @returns IPRT status code.
1704 * @param pThis The ISO maker instance.
1705 * @param pNamespace The namespace which rules to normalize it according to.
1706 * @param pParent The parent directory. NULL if root.
1707 * @param pchSrc The specified name to normalize (not necessarily zero
1708 * terminated).
1709 * @param cchSrc The length of the specified name.
1710 * @param fIsDir Indicates whether it's a directory or file (like).
1711 * @param pszDst The output buffer. Must be at least 32 bytes.
1712 * @param cbDst The size of the output buffer.
1713 * @param pcchDst Where to return the length of the returned string (i.e.
1714 * not counting the terminator).
1715 * @param pcbInDirRec Where to return the name size in the directory record.
1716 */
1717static int rtFsIsoMakerNormalizeNameForNamespace(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
1718 PRTFSISOMAKERNAME pParent, const char *pchSrc, size_t cchSrc, bool fIsDir,
1719 char *pszDst, size_t cbDst, size_t *pcchDst, size_t *pcbInDirRec)
1720{
1721 if (cchSrc > 0)
1722 {
1723 /*
1724 * Check that the object doesn't already exist.
1725 */
1726 AssertReturn(!rtFsIsoMakerFindEntryInDirBySpec(pParent, pchSrc, cchSrc), VERR_ALREADY_EXISTS);
1727 switch (pNamespace->fNamespace)
1728 {
1729 /*
1730 * This one is a lot of work, so separate function.
1731 */
1732 case RTFSISOMAKER_NAMESPACE_ISO_9660:
1733 return rtFsIsoMakerNormalizeNameForPrimaryIso9660(pThis, pParent, pchSrc, cchSrc, fIsDir,
1734 pszDst, cbDst, pcchDst, pcbInDirRec);
1735
1736 /*
1737 * At the moment we don't give darn about UCS-2 limitations here...
1738 */
1739 case RTFSISOMAKER_NAMESPACE_JOLIET:
1740 {
1741/** @todo Joliet name limit and check for duplicates. */
1742 AssertReturn(cbDst > cchSrc, VERR_BUFFER_OVERFLOW);
1743 memcpy(pszDst, pchSrc, cchSrc);
1744 pszDst[cchSrc] = '\0';
1745 *pcchDst = cchSrc;
1746 *pcbInDirRec = RTStrCalcUtf16Len(pszDst) * sizeof(RTUTF16);
1747 return VINF_SUCCESS;
1748 }
1749
1750 case RTFSISOMAKER_NAMESPACE_UDF:
1751 case RTFSISOMAKER_NAMESPACE_HFS:
1752 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
1753
1754 default:
1755 AssertFailedReturn(VERR_INTERNAL_ERROR_2);
1756 }
1757 }
1758 else
1759 {
1760 /*
1761 * Root special case.
1762 *
1763 * For ISO-9660 and joliet, we enter it with a length of 1 byte. The
1764 * value byte value is zero. The path tables we generate won't be
1765 * accepted by windows unless we do this.
1766 */
1767 *pszDst = '\0';
1768 *pcchDst = 0;
1769 *pcbInDirRec = pNamespace->fNamespace & (RTFSISOMAKER_NAMESPACE_ISO_9660 | RTFSISOMAKER_NAMESPACE_JOLIET) ? 1 : 0;
1770 AssertReturn(!pParent, VERR_INTERNAL_ERROR_3);
1771 return VINF_SUCCESS;
1772 }
1773}
1774
1775
1776/**
1777 * Creates a TRANS.TBL file object for a newly named directory.
1778 *
1779 * The file is associated with the namespace node for the directory. The file
1780 * will be generated on the fly from the directory object.
1781 *
1782 * @returns IPRT status code.
1783 * @param pThis The ISO maker instance.
1784 * @param pNamespace The namespace.
1785 * @param pDirName The new name space node for the directory.
1786 */
1787static int rtFsIsoMakerAddTransTblFileToNewDir(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
1788 PRTFSISOMAKERNAME pDirName)
1789{
1790 /*
1791 * Create a file object for it.
1792 */
1793 PRTFSISOMAKERFILE pFile;
1794 int rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, NULL, 0, &pFile);
1795 if (RT_SUCCESS(rc))
1796 {
1797 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_TRANS_TBL;
1798 pFile->u.pTransTblDir = pDirName;
1799 pFile->pBootInfoTable = NULL;
1800 pDirName->pDir->pTransTblFile = pFile;
1801
1802 /*
1803 * Add it to the directory.
1804 */
1805 PRTFSISOMAKERNAME pTransTblNm;
1806 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pFile->Core, pDirName,
1807 pNamespace->pszTransTbl, strlen(pNamespace->pszTransTbl), &pTransTblNm);
1808 if (RT_SUCCESS(rc))
1809 {
1810 pTransTblNm->cchTransNm = 0;
1811 return VINF_SUCCESS;
1812 }
1813
1814 /*
1815 * Bail.
1816 */
1817 pDirName->pDir->pTransTblFile = NULL;
1818 rtFsIsoMakerObjRemoveWorker(pThis, &pFile->Core);
1819 }
1820 return rc;
1821}
1822
1823
1824/**
1825 * Sets the name of an object in a namespace.
1826 *
1827 * If the object is already named in the name space, it will first be removed
1828 * from that namespace. Should we run out of memory or into normalization
1829 * issues after removing it, its original state will _not_ be restored.
1830 *
1831 * @returns IPRT status code.
1832 * @param pThis The ISO maker instance.
1833 * @param pNamespace The namespace.
1834 * @param pObj The object to name.
1835 * @param pParent The parent namespace entry
1836 * @param pchSpec The specified name (not necessarily terminated).
1837 * @param cchSpec The specified name length.
1838 * @param ppNewName Where to return the name entry. Optional.
1839 */
1840static int rtFsIsoMakerObjSetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj,
1841 PRTFSISOMAKERNAME pParent, const char *pchSpec, size_t cchSpec, PPRTFSISOMAKERNAME ppNewName)
1842{
1843 Assert(cchSpec < _32K);
1844
1845 /*
1846 * If the object is already named, unset that name before continuing.
1847 */
1848 if (*rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace))
1849 {
1850 int rc = rtFsIsoMakerObjUnsetName(pThis, pNamespace, pObj);
1851 if (RT_FAILURE(rc))
1852 return rc;
1853 }
1854
1855 /*
1856 * To avoid need to revert anything, make sure papChildren in the parent is
1857 * large enough. If root object, make sure we haven't got a root already.
1858 */
1859 if (pParent)
1860 {
1861 AssertReturn(pParent->pDir, VERR_INTERNAL_ERROR_5);
1862 uint32_t cChildren = pParent->pDir->cChildren;
1863 if (cChildren & 31)
1864 { /* likely */ }
1865 else
1866 {
1867 AssertReturn(cChildren < RTFSISOMAKER_MAX_OBJECTS_PER_DIR, VERR_TOO_MUCH_DATA);
1868 void *pvNew = RTMemRealloc(pParent->pDir->papChildren, (cChildren + 32) * sizeof(pParent->pDir->papChildren[0]));
1869 AssertReturn(pvNew, VERR_NO_MEMORY);
1870 pParent->pDir->papChildren = (PPRTFSISOMAKERNAME)pvNew;
1871 }
1872 }
1873 else
1874 AssertReturn(pNamespace->pRoot == NULL, VERR_INTERNAL_ERROR_5);
1875
1876 /*
1877 * Normalize the name for this namespace.
1878 */
1879 size_t cchName = 0;
1880 size_t cbNameInDirRec = 0;
1881 char szName[RTFSISOMAKER_MAX_NAME_BUF];
1882 int rc = rtFsIsoMakerNormalizeNameForNamespace(pThis, pNamespace, pParent, pchSpec, cchSpec,
1883 pObj->enmType == RTFSISOMAKEROBJTYPE_DIR,
1884 szName, sizeof(szName), &cchName, &cbNameInDirRec);
1885 if (RT_SUCCESS(rc))
1886 {
1887 Assert(cbNameInDirRec > 0);
1888
1889 size_t cbName = sizeof(RTFSISOMAKERNAME)
1890 + cchName + 1
1891 + cchSpec + 1;
1892 if (pObj->enmType == RTFSISOMAKEROBJTYPE_DIR)
1893 cbName = RT_ALIGN_Z(cbName, 8) + sizeof(RTFSISOMAKERNAMEDIR);
1894 PRTFSISOMAKERNAME pName = (PRTFSISOMAKERNAME)RTMemAllocZ(cbName);
1895 if (pName)
1896 {
1897 pName->pObj = pObj;
1898 pName->pParent = pParent;
1899 pName->cbNameInDirRec = (uint16_t)cbNameInDirRec;
1900 pName->cchName = (uint16_t)cchName;
1901
1902 char *pszDst = &pName->szName[cchName + 1];
1903 memcpy(pszDst, pchSpec, cchSpec);
1904 pszDst[cchSpec] = '\0';
1905 pName->pszSpecNm = pszDst;
1906 pName->pszRockRidgeNm = pszDst;
1907 pName->pszTransNm = pszDst;
1908 pName->cchSpecNm = (uint16_t)cchSpec;
1909 pName->cchRockRidgeNm = (uint16_t)cchSpec;
1910 pName->cchTransNm = (uint16_t)cchSpec;
1911 pName->uDepth = pParent ? pParent->uDepth + 1 : 0;
1912 pName->fRockRidgeNmAlloced = false;
1913 pName->fTransNmAlloced = false;
1914
1915 pName->fMode = pObj->fMode;
1916 pName->uid = pObj->uid;
1917 pName->gid = pObj->gid;
1918 pName->Device = 0;
1919 pName->INode = pObj->idxObj;
1920 pName->cHardlinks = 1;
1921 pName->offDirRec = UINT32_MAX;
1922 pName->cbDirRec = 0;
1923
1924 memcpy(pName->szName, szName, cchName);
1925 pName->szName[cchName] = '\0';
1926
1927 if (pObj->enmType != RTFSISOMAKEROBJTYPE_DIR)
1928 pName->pDir = NULL;
1929 else
1930 {
1931 size_t offDir = RT_UOFFSETOF(RTFSISOMAKERNAME, szName) + cchName + 1 + cchSpec + 1;
1932 offDir = RT_ALIGN_Z(offDir, 8);
1933 PRTFSISOMAKERNAMEDIR pDir = (PRTFSISOMAKERNAMEDIR)((uintptr_t)pName + offDir);
1934 pDir->offDir = UINT64_MAX;
1935 pDir->cbDir = 0;
1936 pDir->cChildren = 0;
1937 pDir->papChildren = NULL;
1938 pDir->pTransTblFile = NULL;
1939 pDir->pName = pName;
1940 pDir->offPathTable = UINT32_MAX;
1941 pDir->idPathTable = UINT16_MAX;
1942 pDir->cbDirRec00 = 0;
1943 pDir->cbDirRec01 = 0;
1944 RTListInit(&pDir->FinalizedEntry);
1945 pName->pDir = pDir;
1946
1947 /* Create the TRANS.TBL file object and enter it into this directory as the first entry. */
1948 if (pNamespace->pszTransTbl)
1949 {
1950 rc = rtFsIsoMakerAddTransTblFileToNewDir(pThis, pNamespace, pName);
1951 if (RT_FAILURE(rc))
1952 {
1953 RTMemFree(pName);
1954 return rc;
1955 }
1956 }
1957 }
1958
1959 /*
1960 * Do the linking and stats. We practice insertion sorting.
1961 */
1962 if (pParent)
1963 {
1964 uint32_t idxName = rtFsIsoMakerFindInsertIndex(pNamespace, pParent, pName->szName);
1965 uint32_t cChildren = pParent->pDir->cChildren;
1966 if (idxName < cChildren)
1967 memmove(&pParent->pDir->papChildren[idxName + 1], &pParent->pDir->papChildren[idxName],
1968 (cChildren - idxName) * sizeof(pParent->pDir->papChildren[0]));
1969 pParent->pDir->papChildren[idxName] = pName;
1970 pParent->pDir->cChildren++;
1971 }
1972 else
1973 pNamespace->pRoot = pName;
1974 *rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace) = pName;
1975 pNamespace->cNames++;
1976
1977 /*
1978 * Done.
1979 */
1980 if (ppNewName)
1981 *ppNewName = pName;
1982 return VINF_SUCCESS;
1983 }
1984 }
1985 return rc;
1986}
1987
1988
1989/**
1990 * Walks the path up to the parent, creating missing directories as needed.
1991 *
1992 * As usual, we walk the specified names rather than the mangled ones.
1993 *
1994 * @returns IPRT status code.
1995 * @param pThis The ISO maker instance.
1996 * @param pNamespace The namespace to walk.
1997 * @param pszPath The path to walk.
1998 * @param ppParent Where to return the pointer to the parent
1999 * namespace node.
2000 * @param ppszEntry Where to return the pointer to the final name component.
2001 * @param pcchEntry Where to return the length of the final name component.
2002 */
2003static int rtFsIsoMakerCreatePathToParent(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, const char *pszPath,
2004 PPRTFSISOMAKERNAME ppParent, const char **ppszEntry, size_t *pcchEntry)
2005{
2006 *ppParent = NULL; /* shut up gcc */
2007 *ppszEntry = NULL; /* shut up gcc */
2008 *pcchEntry = 0; /* shut up gcc */
2009
2010 int rc;
2011 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INTERNAL_ERROR_4);
2012
2013 /*
2014 * Deal with the special case of the root.
2015 */
2016 while (RTPATH_IS_SLASH(*pszPath))
2017 pszPath++;
2018 AssertReturn(*pszPath, VERR_INTERNAL_ERROR_4); /* We should not be called on a root path. */
2019
2020 PRTFSISOMAKERNAME pParent = pNamespace->pRoot;
2021 if (!pParent)
2022 {
2023 PRTFSISOMAKERDIR pDir = RTListGetFirst(&pThis->ObjectHead, RTFSISOMAKERDIR, Core.Entry);
2024#ifdef RT_STRICT
2025 Assert(pDir);
2026 Assert(pDir->Core.idxObj == 0);
2027 Assert(pDir->Core.enmType == RTFSISOMAKEROBJTYPE_DIR);
2028 Assert(*rtFsIsoMakerObjGetNameForNamespace(&pDir->Core, pNamespace) == NULL);
2029#endif
2030
2031 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pDir->Core, NULL /*pParent*/, "", 0, &pParent);
2032 AssertRCReturn(rc, rc);
2033 pParent = pNamespace->pRoot;
2034 AssertReturn(pParent, VERR_INTERNAL_ERROR_4);
2035 }
2036
2037 /*
2038 * Now, do the rest of the path.
2039 */
2040 for (;;)
2041 {
2042 /*
2043 * Find the end of the component and see if its the final one or not.
2044 */
2045 char ch;
2046 size_t cchComponent = 0;
2047 while ((ch = pszPath[cchComponent]) != '\0' && !RTPATH_IS_SLASH(ch))
2048 cchComponent++;
2049 AssertReturn(cchComponent > 0, VERR_INTERNAL_ERROR_4);
2050
2051 size_t offNext = cchComponent;
2052 while (RTPATH_IS_SLASH(ch))
2053 ch = pszPath[++offNext];
2054
2055 if (ch == '\0')
2056 {
2057 /*
2058 * Final component. Make sure it is not dot or dot-dot before returning.
2059 */
2060 AssertReturn( pszPath[0] != '.'
2061 || cchComponent > 2
2062 || ( cchComponent == 2
2063 && pszPath[1] != '.'),
2064 VERR_INVALID_NAME);
2065
2066 *ppParent = pParent;
2067 *ppszEntry = pszPath;
2068 *pcchEntry = cchComponent;
2069 return VINF_SUCCESS;
2070 }
2071
2072 /*
2073 * Deal with dot and dot-dot.
2074 */
2075 if (cchComponent == 1 && pszPath[0] == '.')
2076 { /* nothing to do */ }
2077 else if (cchComponent == 2 && pszPath[0] == '.' && pszPath[1] == '.')
2078 {
2079 if (pParent->pParent)
2080 pParent = pParent->pParent;
2081 }
2082 /*
2083 * Look it up.
2084 */
2085 else
2086 {
2087 PRTFSISOMAKERNAME pChild = rtFsIsoMakerFindEntryInDirBySpec(pParent, pszPath, cchComponent);
2088 if (pChild)
2089 {
2090 if (pChild->pDir)
2091 pParent = pChild;
2092 else
2093 return VERR_NOT_A_DIRECTORY;
2094 }
2095 else
2096 {
2097 /* Try see if we've got a directory with the same spec name in a different namespace.
2098 (We don't want to waste heap by creating a directory instance per namespace.) */
2099 PRTFSISOMAKERDIR pChildObj = rtFsIsoMakerFindSubdirBySpec((PRTFSISOMAKERDIR)pParent->pObj,
2100 pszPath, cchComponent, pNamespace->fNamespace);
2101 if (pChildObj)
2102 {
2103 PPRTFSISOMAKERNAME ppChildName = rtFsIsoMakerObjGetNameForNamespace(&pChildObj->Core, pNamespace);
2104 if (!*ppChildName)
2105 {
2106 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pChildObj->Core, pParent, pszPath, cchComponent, &pChild);
2107 if (RT_FAILURE(rc))
2108 return rc;
2109 AssertReturn(pChild != NULL, VERR_INTERNAL_ERROR_4);
2110 }
2111 }
2112 /* If we didn't have luck in other namespaces, create a new directory. */
2113 if (!pChild)
2114 {
2115 rc = rtFsIsoMakerAddUnnamedDirWorker(pThis, &pChildObj);
2116 if (RT_SUCCESS(rc))
2117 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, &pChildObj->Core, pParent, pszPath, cchComponent, &pChild);
2118 if (RT_FAILURE(rc))
2119 return rc;
2120 AssertReturn(pChild != NULL, VERR_INTERNAL_ERROR_4);
2121 }
2122 pParent = pChild;
2123 }
2124 }
2125
2126 /*
2127 * Skip ahead in the path.
2128 */
2129 pszPath += offNext;
2130 }
2131}
2132
2133
2134/**
2135 * Worker for RTFsIsoMakerObjSetPath that operates on a single namespace.
2136 *
2137 * @returns IPRT status code.
2138 * @param pThis The ISO maker instance.
2139 * @param pNamespace The namespace to name it in.
2140 * @param pObj The filesystem object to name.
2141 * @param pszPath The path to the entry in the namespace.
2142 */
2143static int rtFsIsoMakerObjSetPathInOne(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
2144 PRTFSISOMAKEROBJ pObj, const char *pszPath)
2145{
2146 AssertReturn(*rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace) == NULL, VERR_WRONG_ORDER);
2147 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INTERNAL_ERROR_5);
2148
2149 /*
2150 * Figure out where the parent is.
2151 * This will create missing parent name space entries and directory nodes.
2152 */
2153 PRTFSISOMAKERNAME pParent;
2154 const char *pszEntry;
2155 size_t cchEntry;
2156 int rc;
2157 if (pszPath[1] != '\0')
2158 rc = rtFsIsoMakerCreatePathToParent(pThis, pNamespace, pszPath, &pParent, &pszEntry, &cchEntry);
2159 else
2160 {
2161 /*
2162 * Special case for the root directory.
2163 */
2164 Assert(pObj->enmType == RTFSISOMAKEROBJTYPE_DIR);
2165 AssertReturn(pNamespace->pRoot == NULL, VERR_WRONG_ORDER);
2166 pszEntry = "/";
2167 cchEntry = 0;
2168 pParent = NULL;
2169 rc = VINF_SUCCESS;
2170 }
2171
2172 /*
2173 * Do the job on the final path component.
2174 */
2175 if (RT_SUCCESS(rc))
2176 {
2177 AssertReturn(!RTPATH_IS_SLASH(pszEntry[cchEntry]) || pObj->enmType == RTFSISOMAKEROBJTYPE_DIR,
2178 VERR_NOT_A_DIRECTORY);
2179 rc = rtFsIsoMakerObjSetName(pThis, pNamespace, pObj, pParent, pszEntry, cchEntry, NULL);
2180 }
2181 return rc;
2182}
2183
2184
2185/**
2186 * Removes an object from the given namespace.
2187 *
2188 * @returns IPRT status code.
2189 * @param pThis The ISO maker instance.
2190 * @param pNamespace The namespace.
2191 * @param pObj The object to name.
2192 */
2193static int rtFsIsoMakerObjUnsetName(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKEROBJ pObj)
2194{
2195 /*
2196 * First check if there is anything to do here at all.
2197 */
2198 PPRTFSISOMAKERNAME ppName = rtFsIsoMakerObjGetNameForNamespace(pObj, pNamespace);
2199 PRTFSISOMAKERNAME pName = *ppName;
2200 if (!pName)
2201 return VINF_SUCCESS;
2202
2203 /*
2204 * We don't support this on the root.
2205 */
2206 AssertReturn(pName->pParent, VERR_ACCESS_DENIED);
2207
2208 /*
2209 * If this is a directory, we're in for some real fun here as we need to
2210 * unset the names of all the children too.
2211 */
2212 PRTFSISOMAKERNAMEDIR pDir = pName->pDir;
2213 if (pDir)
2214 {
2215 uint32_t iChild = pDir->cChildren;
2216 while (iChild-- > 0)
2217 {
2218 int rc = rtFsIsoMakerObjUnsetName(pThis, pNamespace, pDir->papChildren[iChild]->pObj);
2219 if (RT_FAILURE(rc))
2220 return rc;
2221 }
2222 AssertReturn(pDir->cChildren == 0, VERR_DIR_NOT_EMPTY);
2223 }
2224
2225 /*
2226 * Unlink the pName from the parent.
2227 */
2228 pDir = pName->pParent->pDir;
2229 uint32_t iChild = pDir->cChildren;
2230 while (iChild-- > 0)
2231 if (pDir->papChildren[iChild] == pName)
2232 {
2233 uint32_t cToMove = pDir->cChildren - iChild - 1;
2234 if (cToMove > 0)
2235 memmove(&pDir->papChildren[iChild], &pDir->papChildren[iChild + 1], cToMove * sizeof(pDir->papChildren[0]));
2236 pDir->cChildren--;
2237 pNamespace->cNames--;
2238
2239 /*
2240 * NULL the name member in the object and free the structure.
2241 */
2242 *ppName = NULL;
2243 RTMemFree(pName);
2244
2245 return VINF_SUCCESS;
2246 }
2247
2248 /* Not found. This can't happen. */
2249 AssertFailed();
2250 return VERR_INTERNAL_ERROR_2;
2251}
2252
2253
2254
2255
2256
2257
2258/*
2259 *
2260 * Object level config
2261 * Object level config
2262 * Object level config
2263 *
2264 */
2265
2266
2267/**
2268 * Translates an object index number to an object pointer, slow path.
2269 *
2270 * @returns Pointer to object, NULL if not found.
2271 * @param pThis The ISO maker instance.
2272 * @param idxObj The object index too resolve.
2273 */
2274DECL_NO_INLINE(static, PRTFSISOMAKEROBJ) rtFsIsoMakerIndexToObjSlow(PRTFSISOMAKERINT pThis, uint32_t idxObj)
2275{
2276 PRTFSISOMAKEROBJ pObj;
2277 RTListForEachReverse(&pThis->ObjectHead, pObj, RTFSISOMAKEROBJ, Entry)
2278 {
2279 if (pObj->idxObj == idxObj)
2280 return pObj;
2281 }
2282 return NULL;
2283}
2284
2285
2286/**
2287 * Translates an object index number to an object pointer.
2288 *
2289 * @returns Pointer to object, NULL if not found.
2290 * @param pThis The ISO maker instance.
2291 * @param idxObj The object index too resolve.
2292 */
2293DECLINLINE(PRTFSISOMAKEROBJ) rtFsIsoMakerIndexToObj(PRTFSISOMAKERINT pThis, uint32_t idxObj)
2294{
2295 PRTFSISOMAKEROBJ pObj = RTListGetLast(&pThis->ObjectHead, RTFSISOMAKEROBJ, Entry);
2296 if (!pObj || RT_LIKELY(pObj->idxObj == idxObj))
2297 return pObj;
2298 return rtFsIsoMakerIndexToObjSlow(pThis, idxObj);
2299}
2300
2301
2302/**
2303 * Resolves a path into a object ID.
2304 *
2305 * This will be doing the looking up using the specified object names rather
2306 * than the version adjusted and mangled according to the namespace setup.
2307 *
2308 * @returns The object ID corresponding to @a pszPath, or UINT32_MAX if not
2309 * found or invalid parameters.
2310 * @param hIsoMaker The ISO maker instance.
2311 * @param fNamespaces The namespace to resolve @a pszPath in. It's
2312 * possible to specify multiple namespaces here, of
2313 * course, but that's inefficient.
2314 * @param pszPath The path to the object.
2315 */
2316RTDECL(uint32_t) RTFsIsoMakerGetObjIdxForPath(RTFSISOMAKER hIsoMaker, uint32_t fNamespaces, const char *pszPath)
2317{
2318 /*
2319 * Validate input.
2320 */
2321 PRTFSISOMAKERINT pThis = hIsoMaker;
2322 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET_EX(pThis, UINT32_MAX);
2323
2324 /*
2325 * Do the searching.
2326 */
2327 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2328 if (fNamespaces & g_aRTFsIsoNamespaces[i].fNamespace)
2329 {
2330 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2331 if (pNamespace->pRoot)
2332 {
2333 PRTFSISOMAKERNAME pName;
2334 int rc = rtFsIsoMakerWalkPathBySpec(pNamespace, pszPath, &pName);
2335 if (RT_SUCCESS(rc))
2336 return pName->pObj->idxObj;
2337 }
2338 }
2339
2340 return UINT32_MAX;
2341}
2342
2343
2344/**
2345 * Removes the specified object from the image.
2346 *
2347 * This is a worker for RTFsIsoMakerObjRemove and
2348 * rtFsIsoMakerFinalizeRemoveOrphans.
2349 *
2350 * @returns IPRT status code.
2351 * @param hIsoMaker The ISO maker instance.
2352 * @param pObj The object to remove from the image.
2353 */
2354static int rtFsIsoMakerObjRemoveWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKEROBJ pObj)
2355{
2356 /*
2357 * Remove the object from all name spaces.
2358 */
2359 int rc = VINF_SUCCESS;
2360 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2361 {
2362 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2363 int rc2 = rtFsIsoMakerObjUnsetName(pThis, pNamespace, pObj);
2364 if (RT_SUCCESS(rc2) || RT_FAILURE(rc))
2365 continue;
2366 rc = rc2;
2367 }
2368
2369 /*
2370 * If that succeeded, remove the object itself.
2371 */
2372 if (RT_SUCCESS(rc))
2373 {
2374 RTListNodeRemove(&pObj->Entry);
2375 if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
2376 {
2377 uint64_t cbData = ((PRTFSISOMAKERFILE)pObj)->cbData;
2378 pThis->cbData -= RT_ALIGN_64(cbData, RTFSISOMAKER_SECTOR_SIZE);
2379 }
2380 pThis->cObjects--;
2381 rtFsIsoMakerObjDestroy(pObj);
2382 }
2383 return rc;
2384}
2385
2386
2387/**
2388 * Removes the specified object from the image.
2389 *
2390 * @returns IPRT status code.
2391 * @param hIsoMaker The ISO maker instance.
2392 * @param idxObj The index of the object to remove.
2393 */
2394RTDECL(int) RTFsIsoMakerObjRemove(RTFSISOMAKER hIsoMaker, uint32_t idxObj)
2395{
2396 /*
2397 * Validate and translate input.
2398 */
2399 PRTFSISOMAKERINT pThis = hIsoMaker;
2400 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2401 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2402 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2403 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2404
2405 /*
2406 * Call worker.
2407 */
2408 return rtFsIsoMakerObjRemoveWorker(pThis, pObj);
2409}
2410
2411
2412/**
2413 * Sets the path (name) of an object in the selected namespaces.
2414 *
2415 * The name will be transformed as necessary.
2416 *
2417 * The initial implementation does not allow this function to be called more
2418 * than once on an object.
2419 *
2420 * @returns IPRT status code.
2421 * @param hIsoMaker The ISO maker handle.
2422 * @param idxObj The configuration index of to name.
2423 * @param fNamespaces The namespaces to apply the path to
2424 * (RTFSISOMAKER_NAMESPACE_XXX).
2425 * @param pszPath The path.
2426 */
2427RTDECL(int) RTFsIsoMakerObjSetPath(RTFSISOMAKER hIsoMaker, uint32_t idxObj, uint32_t fNamespaces, const char *pszPath)
2428{
2429 /*
2430 * Validate and translate input.
2431 */
2432 PRTFSISOMAKERINT pThis = hIsoMaker;
2433 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2434 AssertReturn(!(fNamespaces & ~RTFSISOMAKER_NAMESPACE_VALID_MASK), VERR_INVALID_FLAGS);
2435 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
2436 AssertReturn(RTPATH_IS_SLASH(*pszPath), VERR_INVALID_NAME);
2437 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2438 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2439 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2440
2441 /*
2442 * Execute requested actions.
2443 */
2444 int rc = VINF_SUCCESS;
2445 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2446 if (fNamespaces & g_aRTFsIsoNamespaces[i].fNamespace)
2447 {
2448 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2449 if (pNamespace->uLevel > 0)
2450 {
2451 int rc2 = rtFsIsoMakerObjSetPathInOne(pThis, pNamespace, pObj, pszPath);
2452 if (RT_SUCCESS(rc2) || RT_FAILURE(rc))
2453 continue;
2454 rc = rc2;
2455 }
2456 }
2457 return rc;
2458}
2459
2460
2461/**
2462 * Sets the name of an object in the selected namespaces, placing it under the
2463 * given directory.
2464 *
2465 * The name will be transformed as necessary.
2466 *
2467 * @returns IPRT status code.
2468 * @param hIsoMaker The ISO maker handle.
2469 * @param idxObj The configuration index of to name.
2470 * @param idxParentObj The parent directory object.
2471 * @param fNamespaces The namespaces to apply the path to
2472 * (RTFSISOMAKER_NAMESPACE_XXX).
2473 * @param pszName The name.
2474 */
2475RTDECL(int) RTFsIsoMakerObjSetNameAndParent(RTFSISOMAKER hIsoMaker, uint32_t idxObj, uint32_t idxParentObj,
2476 uint32_t fNamespaces, const char *pszName)
2477{
2478 /*
2479 * Validate and translate input.
2480 */
2481 PRTFSISOMAKERINT pThis = hIsoMaker;
2482 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2483 AssertReturn(!(fNamespaces & ~RTFSISOMAKER_NAMESPACE_VALID_MASK), VERR_INVALID_FLAGS);
2484 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
2485 size_t cchName = strlen(pszName);
2486 AssertReturn(cchName > 0, VERR_INVALID_NAME);
2487 AssertReturn(memchr(pszName, '/', cchName) == NULL, VERR_INVALID_NAME);
2488 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2489 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2490 PRTFSISOMAKEROBJ pParentObj = rtFsIsoMakerIndexToObj(pThis, idxParentObj);
2491 AssertReturn(pParentObj, VERR_OUT_OF_RANGE);
2492 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2493
2494 /*
2495 * Execute requested actions.
2496 */
2497 int rc = VINF_SUCCESS;
2498 for (uint32_t i = 0; i < RT_ELEMENTS(g_aRTFsIsoNamespaces); i++)
2499 if (fNamespaces & g_aRTFsIsoNamespaces[i].fNamespace)
2500 {
2501 PRTFSISOMAKERNAMESPACE pNamespace = (PRTFSISOMAKERNAMESPACE)((uintptr_t)pThis + g_aRTFsIsoNamespaces[i].offNamespace);
2502 if (pNamespace->uLevel > 0)
2503 {
2504 PRTFSISOMAKERNAME pParentName = *rtFsIsoMakerObjGetNameForNamespace(pParentObj, pNamespace);
2505 if (pParentName)
2506 {
2507 int rc2 = rtFsIsoMakerObjSetName(pThis, pNamespace, pObj, pParentName, pszName, cchName, NULL /*ppNewName*/);
2508 if (RT_SUCCESS(rc2) || RT_FAILURE(rc))
2509 continue;
2510 rc = rc2;
2511 }
2512 }
2513 }
2514 return rc;
2515}
2516
2517
2518/**
2519 * Enables or disable syslinux boot info table patching of a file.
2520 *
2521 * @returns IPRT status code.
2522 * @param hIsoMaker The ISO maker handle.
2523 * @param idxObj The configuration index.
2524 * @param fEnable Whether to enable or disable patching.
2525 */
2526RTDECL(int) RTFsIsoMakerObjEnableBootInfoTablePatching(RTFSISOMAKER hIsoMaker, uint32_t idxObj, bool fEnable)
2527{
2528 /*
2529 * Validate and translate input.
2530 */
2531 PRTFSISOMAKERINT pThis = hIsoMaker;
2532 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2533 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2534 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2535 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2536 AssertReturn(pObj->enmType == RTFSISOMAKEROBJTYPE_FILE, VERR_WRONG_TYPE);
2537 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
2538 AssertReturn( pFile->enmSrcType == RTFSISOMAKERSRCTYPE_PATH
2539 || pFile->enmSrcType == RTFSISOMAKERSRCTYPE_VFS_FILE
2540 || pFile->enmSrcType == RTFSISOMAKERSRCTYPE_COMMON,
2541 VERR_WRONG_TYPE);
2542
2543 /*
2544 * Do the job.
2545 */
2546 if (fEnable)
2547 {
2548 if (!pFile->pBootInfoTable)
2549 {
2550 pFile->pBootInfoTable = (PISO9660SYSLINUXINFOTABLE)RTMemAllocZ(sizeof(*pFile->pBootInfoTable));
2551 AssertReturn(pFile->pBootInfoTable, VERR_NO_MEMORY);
2552 }
2553 }
2554 else if (pFile->pBootInfoTable)
2555 {
2556 RTMemFree(pFile->pBootInfoTable);
2557 pFile->pBootInfoTable = NULL;
2558 }
2559 return VINF_SUCCESS;
2560}
2561
2562
2563/**
2564 * Gets the data size of an object.
2565 *
2566 * Currently only supported on file objects.
2567 *
2568 * @returns IPRT status code.
2569 * @param hIsoMaker The ISO maker handle.
2570 * @param idxObj The configuration index.
2571 * @param pcbData Where to return the size.
2572 */
2573RTDECL(int) RTFsIsoMakerObjQueryDataSize(RTFSISOMAKER hIsoMaker, uint32_t idxObj, uint64_t *pcbData)
2574{
2575 /*
2576 * Validate and translate input.
2577 */
2578 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
2579 *pcbData = UINT64_MAX;
2580 PRTFSISOMAKERINT pThis = hIsoMaker;
2581 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2582 PRTFSISOMAKEROBJ pObj = rtFsIsoMakerIndexToObj(pThis, idxObj);
2583 AssertReturn(pObj, VERR_OUT_OF_RANGE);
2584
2585 /*
2586 * Do the job.
2587 */
2588 if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
2589 {
2590 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
2591 if (pFile->enmSrcType != RTFSISOMAKERSRCTYPE_TRANS_TBL)
2592 {
2593 *pcbData = ((PRTFSISOMAKERFILE)pObj)->cbData;
2594 return VINF_SUCCESS;
2595 }
2596 }
2597 return VERR_WRONG_TYPE;
2598}
2599
2600
2601/**
2602 * Initalizes the common part of a file system object and links it into global
2603 * chain.
2604 *
2605 * @returns IPRT status code
2606 * @param pThis The ISO maker instance.
2607 * @param pObj The common object.
2608 * @param enmType The object type.
2609 * @param pObjInfo The object information (typically source).
2610 * Optional.
2611 */
2612static int rtFsIsoMakerInitCommonObj(PRTFSISOMAKERINT pThis, PRTFSISOMAKEROBJ pObj,
2613 RTFSISOMAKEROBJTYPE enmType, PCRTFSOBJINFO pObjInfo)
2614{
2615 Assert(!pThis->fFinalized);
2616 AssertReturn(pThis->cObjects < RTFSISOMAKER_MAX_OBJECTS, VERR_OUT_OF_RANGE);
2617
2618 pObj->enmType = enmType;
2619 pObj->pPrimaryName = NULL;
2620 pObj->pJolietName = NULL;
2621 pObj->pUdfName = NULL;
2622 pObj->pHfsName = NULL;
2623 pObj->idxObj = pThis->cObjects++;
2624 pObj->cNotOrphan = 0;
2625 if (pObjInfo)
2626 {
2627 pObj->BirthTime = pObjInfo->BirthTime;
2628 pObj->ChangeTime = pObjInfo->ChangeTime;
2629 pObj->ModificationTime = pObjInfo->ModificationTime;
2630 pObj->AccessedTime = pObjInfo->AccessTime;
2631 pObj->fMode = pObjInfo->Attr.fMode;
2632 pObj->uid = pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : pThis->uidDefault;
2633 pObj->gid = pObjInfo->Attr.u.Unix.gid != NIL_RTGID ? pObjInfo->Attr.u.Unix.gid : pThis->gidDefault;
2634 }
2635 else
2636 {
2637 pObj->BirthTime = pThis->ImageCreationTime;
2638 pObj->ChangeTime = pThis->ImageCreationTime;
2639 pObj->ModificationTime = pThis->ImageCreationTime;
2640 pObj->AccessedTime = pThis->ImageCreationTime;
2641 pObj->fMode = enmType == RTFSISOMAKEROBJTYPE_DIR ? pThis->fDefaultDirMode : pThis->fDefaultFileMode;
2642 pObj->uid = pThis->uidDefault;
2643 pObj->gid = pThis->gidDefault;
2644 }
2645
2646 RTListAppend(&pThis->ObjectHead, &pObj->Entry);
2647 return VINF_SUCCESS;
2648}
2649
2650
2651/**
2652 * Internal function for adding an unnamed directory.
2653 *
2654 * @returns IPRT status code.
2655 * @param pThis The ISO make instance.
2656 * @param ppDir Where to return the directory.
2657 */
2658static int rtFsIsoMakerAddUnnamedDirWorker(PRTFSISOMAKERINT pThis, PRTFSISOMAKERDIR *ppDir)
2659{
2660 PRTFSISOMAKERDIR pDir = (PRTFSISOMAKERDIR)RTMemAllocZ(sizeof(*pDir));
2661 AssertReturn(pDir, VERR_NO_MEMORY);
2662 int rc = rtFsIsoMakerInitCommonObj(pThis, &pDir->Core, RTFSISOMAKEROBJTYPE_DIR, NULL);
2663 if (RT_SUCCESS(rc))
2664 {
2665 *ppDir = pDir;
2666 return VINF_SUCCESS;
2667 }
2668 RTMemFree(pDir);
2669 return rc;
2670
2671}
2672
2673
2674/**
2675 * Adds an unnamed directory to the image.
2676 *
2677 * The directory must explictly be entered into the desired namespaces.
2678 *
2679 * @returns IPRT status code
2680 * @param hIsoMaker The ISO maker handle.
2681 * @param pidxObj Where to return the configuration index of the
2682 * directory.
2683 * @sa RTFsIsoMakerAddDir, RTFsIsoMakerObjSetPath
2684 */
2685RTDECL(int) RTFsIsoMakerAddUnnamedDir(RTFSISOMAKER hIsoMaker, uint32_t *pidxObj)
2686{
2687 PRTFSISOMAKERINT pThis = hIsoMaker;
2688 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2689 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2690 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2691
2692 PRTFSISOMAKERDIR pDir;
2693 int rc = rtFsIsoMakerAddUnnamedDirWorker(pThis, &pDir);
2694 *pidxObj = RT_SUCCESS(rc) ? pDir->Core.idxObj : UINT32_MAX;
2695 return rc;
2696}
2697
2698
2699/**
2700 * Adds a directory to the image in all namespaces and default attributes.
2701 *
2702 * @returns IPRT status code
2703 * @param hIsoMaker The ISO maker handle.
2704 * @param pszDir The path (UTF-8) to the directory in the ISO.
2705 *
2706 * @param pidxObj Where to return the configuration index of the
2707 * directory. Optional.
2708 * @sa RTFsIsoMakerAddUnnamedDir, RTFsIsoMakerObjSetPath
2709 */
2710RTDECL(int) RTFsIsoMakerAddDir(RTFSISOMAKER hIsoMaker, const char *pszDir, uint32_t *pidxObj)
2711{
2712 PRTFSISOMAKERINT pThis = hIsoMaker;
2713 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2714 AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
2715 AssertReturn(RTPATH_IS_SLASH(*pszDir), VERR_INVALID_NAME);
2716
2717 uint32_t idxObj;
2718 int rc = RTFsIsoMakerAddUnnamedDir(hIsoMaker, &idxObj);
2719 if (RT_SUCCESS(rc))
2720 {
2721 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszDir);
2722 if (RT_SUCCESS(rc))
2723 {
2724 if (pidxObj)
2725 *pidxObj = idxObj;
2726 }
2727 else
2728 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
2729 }
2730 return rc;
2731}
2732
2733
2734/**
2735 * Internal function for adding an unnamed file.
2736 *
2737 * @returns IPRT status code.
2738 * @param pThis The ISO make instance.
2739 * @param pObjInfo Object information. Optional.
2740 * @param cbExtra Extra space for additional data (e.g. source
2741 * path string copy).
2742 * @param ppFile Where to return the file.
2743 */
2744static int rtFsIsoMakerAddUnnamedFileWorker(PRTFSISOMAKERINT pThis, PCRTFSOBJINFO pObjInfo, size_t cbExtra,
2745 PRTFSISOMAKERFILE *ppFile)
2746{
2747 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)RTMemAllocZ(sizeof(*pFile) + cbExtra);
2748 AssertReturn(pFile, VERR_NO_MEMORY);
2749 int rc = rtFsIsoMakerInitCommonObj(pThis, &pFile->Core, RTFSISOMAKEROBJTYPE_FILE, pObjInfo);
2750 if (RT_SUCCESS(rc))
2751 {
2752 pFile->cbData = pObjInfo ? pObjInfo->cbObject : 0;
2753 pThis->cbData += RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE);
2754 pFile->offData = UINT64_MAX;
2755 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_INVALID;
2756 pFile->u.pszSrcPath = NULL;
2757 pFile->pBootInfoTable = NULL;
2758 RTListInit(&pFile->FinalizedEntry);
2759
2760 *ppFile = pFile;
2761 return VINF_SUCCESS;
2762 }
2763 RTMemFree(pFile);
2764 return rc;
2765
2766}
2767
2768
2769/**
2770 * Adds an unnamed file to the image that's backed by a host file.
2771 *
2772 * The file must explictly be entered into the desired namespaces.
2773 *
2774 * @returns IPRT status code
2775 * @param hIsoMaker The ISO maker handle.
2776 * @param pszSrcFile The source file path. VFS chain spec allowed.
2777 * @param pidxObj Where to return the configuration index of the
2778 * directory.
2779 * @sa RTFsIsoMakerAddFile, RTFsIsoMakerObjSetPath
2780 */
2781RTDECL(int) RTFsIsoMakerAddUnnamedFileWithSrcPath(RTFSISOMAKER hIsoMaker, const char *pszSrcFile, uint32_t *pidxObj)
2782{
2783 PRTFSISOMAKERINT pThis = hIsoMaker;
2784 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2785 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2786 *pidxObj = UINT32_MAX;
2787 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2788
2789 /*
2790 * Check that the source file exists and is a file.
2791 */
2792 uint32_t offError = 0;
2793 RTFSOBJINFO ObjInfo;
2794 int rc = RTVfsChainQueryInfo(pszSrcFile, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK, &offError, NULL);
2795 AssertMsgRCReturn(rc, ("%s -> %Rrc offError=%u\n", pszSrcFile, rc, offError), rc);
2796 AssertMsgReturn(RTFS_IS_FILE(ObjInfo.Attr.fMode), ("%#x - %s\n", ObjInfo.Attr.fMode, pszSrcFile), VERR_NOT_A_FILE);
2797
2798 /*
2799 * Create a file object for it.
2800 */
2801 size_t const cbSrcFile = strlen(pszSrcFile) + 1;
2802 PRTFSISOMAKERFILE pFile;
2803 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, &ObjInfo, cbSrcFile, &pFile);
2804 if (RT_SUCCESS(rc))
2805 {
2806 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_PATH;
2807 pFile->u.pszSrcPath = (char *)memcpy(pFile + 1, pszSrcFile, cbSrcFile);
2808
2809 *pidxObj = pFile->Core.idxObj;
2810 }
2811 return rc;
2812}
2813
2814
2815/**
2816 * Adds an unnamed file to the image that's backed by a VFS file.
2817 *
2818 * The file must explictly be entered into the desired namespaces.
2819 *
2820 * @returns IPRT status code
2821 * @param hIsoMaker The ISO maker handle.
2822 * @param hVfsFileSrc The source file handle.
2823 * @param pidxObj Where to return the configuration index of the
2824 * directory.
2825 * @sa RTFsIsoMakerAddUnnamedFileWithSrcPath, RTFsIsoMakerObjSetPath
2826 */
2827RTDECL(int) RTFsIsoMakerAddUnnamedFileWithVfsFile(RTFSISOMAKER hIsoMaker, RTVFSFILE hVfsFileSrc, uint32_t *pidxObj)
2828{
2829 PRTFSISOMAKERINT pThis = hIsoMaker;
2830 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2831 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2832 *pidxObj = UINT32_MAX;
2833 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2834
2835 /*
2836 * Get the VFS file info. This implicitly validates the handle.
2837 */
2838 RTFSOBJINFO ObjInfo;
2839 int rc = RTVfsFileQueryInfo(hVfsFileSrc, &ObjInfo, RTFSOBJATTRADD_UNIX);
2840 AssertMsgRCReturn(rc, ("RTVfsFileQueryInfo(%p) -> %Rrc\n", hVfsFileSrc, rc), rc);
2841
2842 /*
2843 * Retain a reference to the file.
2844 */
2845 uint32_t cRefs = RTVfsFileRetain(hVfsFileSrc);
2846 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2847
2848 /*
2849 * Create a file object for it.
2850 */
2851 PRTFSISOMAKERFILE pFile;
2852 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, &ObjInfo, 0, &pFile);
2853 if (RT_SUCCESS(rc))
2854 {
2855 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_VFS_FILE;
2856 pFile->u.hVfsFile = hVfsFileSrc;
2857
2858 *pidxObj = pFile->Core.idxObj;
2859 }
2860 else
2861 RTVfsFileRelease(hVfsFileSrc);
2862 return rc;
2863}
2864
2865
2866/**
2867 * Adds an unnamed file to the image that's backed by a portion of a common
2868 * source file.
2869 *
2870 * The file must explictly be entered into the desired namespaces.
2871 *
2872 * @returns IPRT status code
2873 * @param hIsoMaker The ISO maker handle.
2874 * @param idxCommonSrc The common source file index.
2875 * @param offData The offset of the data in the source file.
2876 * @param cbData The file size.
2877 * @param pObjInfo Pointer to file info. Optional.
2878 * @param pidxObj Where to return the configuration index of the
2879 * directory.
2880 * @sa RTFsIsoMakerAddUnnamedFileWithSrcPath, RTFsIsoMakerObjSetPath
2881 */
2882RTDECL(int) RTFsIsoMakerAddUnnamedFileWithCommonSrc(RTFSISOMAKER hIsoMaker, uint32_t idxCommonSrc,
2883 uint64_t offData, uint64_t cbData, PCRTFSOBJINFO pObjInfo, uint32_t *pidxObj)
2884{
2885 /*
2886 * Validate and fake input.
2887 */
2888 PRTFSISOMAKERINT pThis = hIsoMaker;
2889 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2890 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
2891 *pidxObj = UINT32_MAX;
2892 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2893 AssertReturn(idxCommonSrc < pThis->cCommonSources, VERR_INVALID_PARAMETER);
2894 AssertReturn(offData < (uint64_t)RTFOFF_MAX, VERR_OUT_OF_RANGE);
2895 AssertReturn(cbData < (uint64_t)RTFOFF_MAX, VERR_OUT_OF_RANGE);
2896 AssertReturn(offData + cbData < (uint64_t)RTFOFF_MAX, VERR_OUT_OF_RANGE);
2897 RTFSOBJINFO ObjInfo;
2898 if (!pObjInfo)
2899 {
2900 ObjInfo.cbObject = cbData;
2901 ObjInfo.cbAllocated = cbData;
2902 ObjInfo.BirthTime = pThis->ImageCreationTime;
2903 ObjInfo.ChangeTime = pThis->ImageCreationTime;
2904 ObjInfo.ModificationTime = pThis->ImageCreationTime;
2905 ObjInfo.AccessTime = pThis->ImageCreationTime;
2906 ObjInfo.Attr.fMode = pThis->fDefaultFileMode;
2907 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
2908 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
2909 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
2910 ObjInfo.Attr.u.Unix.cHardlinks = 1;
2911 ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
2912 ObjInfo.Attr.u.Unix.INodeId = 0;
2913 ObjInfo.Attr.u.Unix.fFlags = 0;
2914 ObjInfo.Attr.u.Unix.GenerationId = 0;
2915 ObjInfo.Attr.u.Unix.Device = 0;
2916 pObjInfo = &ObjInfo;
2917 }
2918 else
2919 {
2920 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
2921 AssertReturn(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX, VERR_WRONG_TYPE);
2922 AssertReturn((uint64_t)pObjInfo->cbObject == cbData, VERR_INVALID_PARAMETER);
2923 }
2924
2925 /*
2926 * Create a file object for it.
2927 */
2928 PRTFSISOMAKERFILE pFile;
2929 int rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, pObjInfo, 0, &pFile);
2930 if (RT_SUCCESS(rc))
2931 {
2932 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_COMMON;
2933 pFile->u.Common.idxSrc = idxCommonSrc;
2934 pFile->u.Common.offData = offData;
2935
2936 *pidxObj = pFile->Core.idxObj;
2937 }
2938 return rc;
2939}
2940
2941
2942/**
2943 * Adds a common source file.
2944 *
2945 * Using RTFsIsoMakerAddUnnamedFileWithCommonSrc a sections common source file
2946 * can be referenced to make up other files. The typical use case is when
2947 * importing data from an existing ISO.
2948 *
2949 * @returns IPRT status code
2950 * @param hIsoMaker The ISO maker handle.
2951 * @param hVfsFile VFS handle of the common source. (A reference
2952 * is added, none consumed.)
2953 * @param pidxCommonSrc Where to return the assigned common source
2954 * index. This is used to reference the file.
2955 * @sa RTFsIsoMakerAddUnnamedFileWithCommonSrc
2956 */
2957RTDECL(int) RTFsIsoMakerAddCommonSourceFile(RTFSISOMAKER hIsoMaker, RTVFSFILE hVfsFile, uint32_t *pidxCommonSrc)
2958{
2959 /*
2960 * Validate input.
2961 */
2962 PRTFSISOMAKERINT pThis = hIsoMaker;
2963 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
2964 AssertPtrReturn(pidxCommonSrc, VERR_INVALID_POINTER);
2965 *pidxCommonSrc = UINT32_MAX;
2966 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
2967
2968 /*
2969 * Resize the common source array if necessary.
2970 */
2971 if ((pThis->cCommonSources & 15) == 0)
2972 {
2973 void *pvNew = RTMemRealloc(pThis->paCommonSources, (pThis->cCommonSources + 16) * sizeof(pThis->paCommonSources[0]));
2974 AssertReturn(pvNew, VERR_NO_MEMORY);
2975 pThis->paCommonSources = (PRTVFSFILE)pvNew;
2976 }
2977
2978 /*
2979 * Retain a reference to the source file, thereby validating the handle.
2980 * Then add it to the array.
2981 */
2982 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
2983 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2984
2985 uint32_t idx = pThis->cCommonSources++;
2986 pThis->paCommonSources[idx] = hVfsFile;
2987
2988 *pidxCommonSrc = idx;
2989 return VINF_SUCCESS;
2990}
2991
2992
2993/**
2994 * Adds a file that's backed by a host file to the image in all namespaces and
2995 * with attributes taken from the source file.
2996 *
2997 * @returns IPRT status code
2998 * @param hIsoMaker The ISO maker handle.
2999 * @param pszFile The path to the file in the image.
3000 * @param pszSrcFile The source file path. VFS chain spec allowed.
3001 * @param pidxObj Where to return the configuration index of the file.
3002 * Optional
3003 * @sa RTFsIsoMakerAddFileWithVfsFile,
3004 * RTFsIsoMakerAddUnnamedFileWithSrcPath
3005 */
3006RTDECL(int) RTFsIsoMakerAddFileWithSrcPath(RTFSISOMAKER hIsoMaker, const char *pszFile, const char *pszSrcFile, uint32_t *pidxObj)
3007{
3008 PRTFSISOMAKERINT pThis = hIsoMaker;
3009 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3010 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
3011 AssertReturn(RTPATH_IS_SLASH(*pszFile), VERR_INVALID_NAME);
3012
3013 uint32_t idxObj;
3014 int rc = RTFsIsoMakerAddUnnamedFileWithSrcPath(hIsoMaker, pszSrcFile, &idxObj);
3015 if (RT_SUCCESS(rc))
3016 {
3017 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszFile);
3018 if (RT_SUCCESS(rc))
3019 {
3020 if (pidxObj)
3021 *pidxObj = idxObj;
3022 }
3023 else
3024 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
3025 }
3026 return rc;
3027}
3028
3029
3030/**
3031 * Adds a file that's backed by a VFS file to the image in all namespaces and
3032 * with attributes taken from the source file.
3033 *
3034 * @returns IPRT status code
3035 * @param hIsoMaker The ISO maker handle.
3036 * @param pszFile The path to the file in the image.
3037 * @param hVfsFileSrc The source file handle.
3038 * @param pidxObj Where to return the configuration index of the file.
3039 * Optional.
3040 * @sa RTFsIsoMakerAddUnnamedFileWithVfsFile,
3041 * RTFsIsoMakerAddFileWithSrcPath
3042 */
3043RTDECL(int) RTFsIsoMakerAddFileWithVfsFile(RTFSISOMAKER hIsoMaker, const char *pszFile, RTVFSFILE hVfsFileSrc, uint32_t *pidxObj)
3044{
3045 PRTFSISOMAKERINT pThis = hIsoMaker;
3046 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3047 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
3048 AssertReturn(RTPATH_IS_SLASH(*pszFile), VERR_INVALID_NAME);
3049
3050 uint32_t idxObj;
3051 int rc = RTFsIsoMakerAddUnnamedFileWithVfsFile(hIsoMaker, hVfsFileSrc, &idxObj);
3052 if (RT_SUCCESS(rc))
3053 {
3054 rc = RTFsIsoMakerObjSetPath(hIsoMaker, idxObj, RTFSISOMAKER_NAMESPACE_ALL, pszFile);
3055 if (RT_SUCCESS(rc))
3056 {
3057 if (pidxObj)
3058 *pidxObj = idxObj;
3059 }
3060 else
3061 RTFsIsoMakerObjRemove(hIsoMaker, idxObj);
3062 }
3063 return rc;
3064}
3065
3066
3067
3068
3069/*
3070 *
3071 * El Torito Booting.
3072 * El Torito Booting.
3073 * El Torito Booting.
3074 * El Torito Booting.
3075 *
3076 */
3077
3078/**
3079 * Ensures that we've got a boot catalog file.
3080 *
3081 * @returns IPRT status code.
3082 * @param pThis The ISO maker instance.
3083 */
3084static int rtFsIsoMakerEnsureBootCatFile(PRTFSISOMAKERINT pThis)
3085{
3086 if (pThis->pBootCatFile)
3087 return VINF_SUCCESS;
3088
3089 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
3090
3091 /* Create a VFS memory file for backing up the file. */
3092 RTVFSFILE hVfsFile;
3093 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, RTFSISOMAKER_SECTOR_SIZE, &hVfsFile);
3094 if (RT_SUCCESS(rc))
3095 {
3096 /* Create an unnamed VFS backed file and mark it as non-orphaned. */
3097 PRTFSISOMAKERFILE pFile;
3098 rc = rtFsIsoMakerAddUnnamedFileWorker(pThis, NULL, 0, &pFile);
3099 if (RT_SUCCESS(rc))
3100 {
3101 pFile->enmSrcType = RTFSISOMAKERSRCTYPE_VFS_FILE;
3102 pFile->u.hVfsFile = hVfsFile;
3103 pFile->Core.cNotOrphan = 1;
3104
3105 /* Save file pointer and allocate a volume descriptor. */
3106 pThis->pBootCatFile = pFile;
3107 pThis->cVolumeDescriptors++;
3108
3109 return VINF_SUCCESS;
3110 }
3111 RTVfsFileRelease(hVfsFile);
3112 }
3113 return rc;
3114}
3115
3116
3117/**
3118 * Queries the configuration index of the boot catalog file object.
3119 *
3120 * The boot catalog file is created as necessary, thus this have to be a query
3121 * rather than a getter since object creation may fail.
3122 *
3123 * @returns IPRT status code.
3124 * @param hIsoMaker The ISO maker handle.
3125 * @param pidxObj Where to return the configuration index.
3126 */
3127RTDECL(int) RTFsIsoMakerQueryObjIdxForBootCatalog(RTFSISOMAKER hIsoMaker, uint32_t *pidxObj)
3128{
3129 /*
3130 * Validate input.
3131 */
3132 AssertPtrReturn(pidxObj, VERR_INVALID_POINTER);
3133 *pidxObj = UINT32_MAX;
3134 PRTFSISOMAKERINT pThis = hIsoMaker;
3135 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3136
3137 /*
3138 * Do the job.
3139 */
3140 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3141 if (RT_SUCCESS(rc))
3142 *pidxObj = pThis->pBootCatFile->Core.idxObj;
3143 return rc;
3144}
3145
3146
3147/**
3148 * Set the validation entry of the boot catalog (this is the first entry).
3149 *
3150 * @returns IPRT status code.
3151 * @param hIsoMaker The ISO maker handle.
3152 * @param idPlatform The platform ID
3153 * (ISO9660_ELTORITO_PLATFORM_ID_XXX).
3154 * @param pszString CD/DVD-ROM identifier. Optional.
3155 */
3156RTDECL(int) RTFsIsoMakerBootCatSetValidationEntry(RTFSISOMAKER hIsoMaker, uint8_t idPlatform, const char *pszString)
3157{
3158 /*
3159 * Validate input.
3160 */
3161 PRTFSISOMAKERINT pThis = hIsoMaker;
3162 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3163 size_t cchString = 0;
3164 if (pszString)
3165 {
3166 cchString = RTStrCalcLatin1Len(pszString);
3167 AssertReturn(cchString < RT_SIZEOFMEMB(ISO9660ELTORITOVALIDATIONENTRY, achId), VERR_OUT_OF_RANGE);
3168 }
3169
3170 /*
3171 * Make sure we've got a boot file.
3172 */
3173 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3174 if (RT_SUCCESS(rc))
3175 {
3176 /*
3177 * Construct the entry data.
3178 */
3179 ISO9660ELTORITOVALIDATIONENTRY Entry;
3180 Entry.bHeaderId = ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY;
3181 Entry.bPlatformId = idPlatform;
3182 Entry.u16Reserved = 0;
3183 RT_ZERO(Entry.achId);
3184 if (cchString)
3185 {
3186 char *pszTmp = Entry.achId;
3187 rc = RTStrToLatin1Ex(pszString, RTSTR_MAX, &pszTmp, sizeof(Entry.achId), NULL);
3188 AssertRC(rc);
3189 }
3190 Entry.u16Checksum = 0;
3191 Entry.bKey1 = ISO9660_ELTORITO_KEY_BYTE_1;
3192 Entry.bKey2 = ISO9660_ELTORITO_KEY_BYTE_2;
3193
3194 /* Calc checksum. */
3195 uint16_t uSum = 0;
3196 uint16_t const *pu16Src = (uint16_t const *)&Entry;
3197 uint16_t cLeft = sizeof(Entry) / sizeof(uint16_t);
3198 while (cLeft-- > 0)
3199 {
3200 uSum += RT_LE2H_U16(*pu16Src);
3201 pu16Src++;
3202 }
3203 Entry.u16Checksum = RT_H2LE_U16((uint16_t)0 - uSum);
3204
3205 /*
3206 * Write the entry and update our internal tracker.
3207 */
3208 rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, 0, &Entry, sizeof(Entry), NULL);
3209 if (RT_SUCCESS(rc))
3210 {
3211 pThis->aBootCatEntries[0].bType = ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY;
3212 pThis->aBootCatEntries[0].cEntries = 2;
3213 }
3214 }
3215 return rc;
3216}
3217
3218
3219/**
3220 * Set the validation entry of the boot catalog (this is the first entry).
3221 *
3222 * @returns IPRT status code.
3223 * @param hIsoMaker The ISO maker handle.
3224 * @param idxBootCat The boot catalog entry. Zero and two are
3225 * invalid. Must be less than 63.
3226 * @param idxImageObj The configuration index of the boot image.
3227 * @param bBootMediaType The media type and flag (not for entry 1)
3228 * (ISO9660_ELTORITO_BOOT_MEDIA_TYPE_XXX,
3229 * ISO9660_ELTORITO_BOOT_MEDIA_F_XXX).
3230 * @param bSystemType The partitiona table system ID.
3231 * @param fBootable Whether it's a bootable entry or if we just want
3232 * the BIOS to setup the emulation without booting
3233 * it.
3234 * @param uLoadSeg The load address divided by 0x10 (i.e. the real
3235 * mode segment number).
3236 * @param cSectorsToLoad Number of emulated sectors to load.
3237 */
3238RTDECL(int) RTFsIsoMakerBootCatSetSectionEntry(RTFSISOMAKER hIsoMaker, uint32_t idxBootCat, uint32_t idxImageObj,
3239 uint8_t bBootMediaType, uint8_t bSystemType, bool fBootable,
3240 uint16_t uLoadSeg, uint16_t cSectorsToLoad)
3241{
3242 /*
3243 * Validate input.
3244 */
3245 PRTFSISOMAKERINT pThis = hIsoMaker;
3246 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3247 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)rtFsIsoMakerIndexToObj(pThis, idxImageObj);
3248 AssertReturn(pFile, VERR_OUT_OF_RANGE);
3249 AssertReturn((bBootMediaType & ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK) <= ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK,
3250 VERR_INVALID_PARAMETER);
3251 AssertReturn(!(bBootMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_MASK) || idxBootCat != 1,
3252 VERR_INVALID_PARAMETER);
3253
3254 AssertReturn(idxBootCat != 0 && idxBootCat != 2 && idxBootCat < RT_ELEMENTS(pThis->aBootCatEntries) - 1U, VERR_OUT_OF_RANGE);
3255
3256 /*
3257 * Make sure we've got a boot file.
3258 */
3259 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3260 if (RT_SUCCESS(rc))
3261 {
3262 /*
3263 * Construct the entry.
3264 */
3265 ISO9660ELTORITOSECTIONENTRY Entry;
3266 Entry.bBootIndicator = fBootable ? ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
3267 : ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE;
3268 Entry.bBootMediaType = bBootMediaType;
3269 Entry.uLoadSeg = RT_H2LE_U16(uLoadSeg);
3270 Entry.bSystemType = bSystemType;
3271 Entry.bUnused = 0;
3272 Entry.cEmulatedSectorsToLoad = RT_H2LE_U16(cSectorsToLoad);
3273 Entry.offBootImage = 0;
3274 Entry.bSelectionCriteriaType = ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE;
3275 RT_ZERO(Entry.abSelectionCriteria);
3276
3277 /*
3278 * Write it and update our internal tracker.
3279 */
3280 rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, 32 * idxBootCat, &Entry, sizeof(Entry), NULL);
3281 if (RT_SUCCESS(rc))
3282 {
3283 if (pThis->aBootCatEntries[idxBootCat].pBootFile != pFile)
3284 {
3285 if (pThis->aBootCatEntries[idxBootCat].pBootFile)
3286 pThis->aBootCatEntries[idxBootCat].pBootFile->Core.cNotOrphan--;
3287 pFile->Core.cNotOrphan++;
3288 pThis->aBootCatEntries[idxBootCat].pBootFile = pFile;
3289 }
3290
3291 pThis->aBootCatEntries[idxBootCat].bType = Entry.bBootIndicator;
3292 pThis->aBootCatEntries[idxBootCat].cEntries = 1;
3293 }
3294 }
3295 return rc;
3296}
3297
3298
3299/**
3300 * Set the validation entry of the boot catalog (this is the first entry).
3301 *
3302 * @returns IPRT status code.
3303 * @param hIsoMaker The ISO maker handle.
3304 * @param idxBootCat The boot catalog entry.
3305 * @param cEntries Number of entries in the section.
3306 * @param idPlatform The platform ID
3307 * (ISO9660_ELTORITO_PLATFORM_ID_XXX).
3308 * @param pszString Section identifier or something. Optional.
3309 */
3310RTDECL(int) RTFsIsoMakerBootCatSetSectionHeaderEntry(RTFSISOMAKER hIsoMaker, uint32_t idxBootCat, uint32_t cEntries,
3311 uint8_t idPlatform, const char *pszString)
3312{
3313 /*
3314 * Validate input.
3315 */
3316 PRTFSISOMAKERINT pThis = hIsoMaker;
3317 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
3318
3319 AssertReturn(idxBootCat >= 2 && idxBootCat < RT_ELEMENTS(pThis->aBootCatEntries) - 1U, VERR_OUT_OF_RANGE);
3320 AssertReturn(cEntries < RT_ELEMENTS(pThis->aBootCatEntries) - 2U - 1U, VERR_OUT_OF_RANGE);
3321 AssertReturn(idxBootCat + cEntries + 1 < RT_ELEMENTS(pThis->aBootCatEntries), VERR_OUT_OF_RANGE);
3322
3323 size_t cchString = 0;
3324 if (pszString)
3325 {
3326 cchString = RTStrCalcLatin1Len(pszString);
3327 AssertReturn(cchString < RT_SIZEOFMEMB(ISO9660ELTORITOVALIDATIONENTRY, achId), VERR_OUT_OF_RANGE);
3328 }
3329
3330 /*
3331 * Make sure we've got a boot file.
3332 */
3333 int rc = rtFsIsoMakerEnsureBootCatFile(pThis);
3334 if (RT_SUCCESS(rc))
3335 {
3336 /*
3337 * Construct the entry data.
3338 */
3339 ISO9660ELTORITOSECTIONHEADER Entry;
3340 Entry.bHeaderId = ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER;
3341 Entry.bPlatformId = idPlatform;
3342 Entry.cEntries = RT_H2LE_U16(cEntries);
3343 RT_ZERO(Entry.achSectionId);
3344 if (cchString)
3345 {
3346 char *pszTmp = Entry.achSectionId;
3347 rc = RTStrToLatin1Ex(pszString, RTSTR_MAX, &pszTmp, sizeof(Entry.achSectionId), NULL);
3348 AssertRC(rc);
3349 }
3350
3351 /*
3352 * Write the entry and update our internal tracker.
3353 */
3354 rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, 32 * idxBootCat, &Entry, sizeof(Entry), NULL);
3355 if (RT_SUCCESS(rc))
3356 {
3357 if (pThis->aBootCatEntries[idxBootCat].pBootFile != NULL)
3358 {
3359 pThis->aBootCatEntries[idxBootCat].pBootFile->Core.cNotOrphan--;
3360 pThis->aBootCatEntries[idxBootCat].pBootFile = NULL;
3361 }
3362
3363 pThis->aBootCatEntries[idxBootCat].bType = ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER;
3364 pThis->aBootCatEntries[idxBootCat].cEntries = cEntries + 1;
3365 }
3366 }
3367 return rc;
3368}
3369
3370
3371
3372
3373
3374/*
3375 *
3376 * Image finalization.
3377 * Image finalization.
3378 * Image finalization.
3379 *
3380 */
3381
3382
3383/**
3384 * Remove any orphaned object from the disk.
3385 *
3386 * @returns IPRT status code.
3387 * @param pThis The ISO maker instance.
3388 */
3389static int rtFsIsoMakerFinalizeRemoveOrphans(PRTFSISOMAKERINT pThis)
3390{
3391 for (;;)
3392 {
3393 uint32_t cRemoved = 0;
3394 PRTFSISOMAKEROBJ pCur;
3395 PRTFSISOMAKEROBJ pNext;
3396 RTListForEachSafe(&pThis->ObjectHead, pCur, pNext, RTFSISOMAKEROBJ, Entry)
3397 {
3398 if ( pCur->pPrimaryName
3399 || pCur->pJolietName
3400 || pCur->pUdfName
3401 || pCur->pHfsName
3402 || pCur->cNotOrphan > 0)
3403 { /* likely */ }
3404 else
3405 {
3406 Log4(("rtFsIsoMakerFinalizeRemoveOrphans: %#x cbData=%#RX64\n", pCur->idxObj,
3407 pCur->enmType == RTFSISOMAKEROBJTYPE_FILE ? ((PRTFSISOMAKERFILE)(pCur))->cbData : 0));
3408 int rc = rtFsIsoMakerObjRemoveWorker(pThis, pCur);
3409 if (RT_SUCCESS(rc))
3410 cRemoved++;
3411 else
3412 return rc;
3413 }
3414 }
3415 if (!cRemoved)
3416 return VINF_SUCCESS;
3417 }
3418}
3419
3420
3421/**
3422 * Finalizes the El Torito boot stuff, part 1.
3423 *
3424 * This includes generating the boot catalog data and fixing the location of all
3425 * related image files.
3426 *
3427 * @returns IPRT status code.
3428 * @param pThis The ISO maker instance.
3429 */
3430static int rtFsIsoMakerFinalizeBootStuffPart1(PRTFSISOMAKERINT pThis)
3431{
3432 /*
3433 * Anything?
3434 */
3435 if (!pThis->pBootCatFile)
3436 return VINF_SUCCESS;
3437
3438 /*
3439 * Validate the boot catalog file.
3440 */
3441 AssertReturn(pThis->aBootCatEntries[0].bType == ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY,
3442 VERR_ISOMK_BOOT_CAT_NO_VALIDATION_ENTRY);
3443 AssertReturn(pThis->aBootCatEntries[1].pBootFile != NULL, VERR_ISOMK_BOOT_CAT_NO_DEFAULT_ENTRY);
3444
3445 /* Check any sections following the default one. */
3446 uint32_t cEntries = 2;
3447 while ( cEntries < RT_ELEMENTS(pThis->aBootCatEntries) - 1U
3448 && pThis->aBootCatEntries[cEntries].cEntries > 0)
3449 {
3450 AssertReturn(pThis->aBootCatEntries[cEntries].bType == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER,
3451 VERR_ISOMK_BOOT_CAT_EXPECTED_SECTION_HEADER);
3452 for (uint32_t i = 1; i < pThis->aBootCatEntries[cEntries].cEntries; i++)
3453 AssertReturn(pThis->aBootCatEntries[cEntries].pBootFile != NULL,
3454 pThis->aBootCatEntries[cEntries].cEntries == 0
3455 ? VERR_ISOMK_BOOT_CAT_EMPTY_ENTRY : VERR_ISOMK_BOOT_CAT_INVALID_SECTION_SIZE);
3456 cEntries += pThis->aBootCatEntries[cEntries].cEntries;
3457 }
3458
3459 /* Save for size setting. */
3460 uint32_t const cEntriesInFile = cEntries + 1;
3461
3462 /* Check that the remaining entries are empty. */
3463 while (cEntries < RT_ELEMENTS(pThis->aBootCatEntries))
3464 {
3465 AssertReturn(pThis->aBootCatEntries[cEntries].cEntries == 0, VERR_ISOMK_BOOT_CAT_ERRATIC_ENTRY);
3466 cEntries++;
3467 }
3468
3469 /*
3470 * Fixate the size of the boot catalog file.
3471 */
3472 pThis->pBootCatFile->cbData = cEntriesInFile * 32;
3473 pThis->cbData += RT_ALIGN_32(cEntriesInFile * 32, RTFSISOMAKER_SECTOR_SIZE);
3474
3475 /*
3476 * Move up the boot images and boot catalog to the start of the image.
3477 */
3478 for (uint32_t i = RT_ELEMENTS(pThis->aBootCatEntries) - 2; i > 0; i--)
3479 if (pThis->aBootCatEntries[i].pBootFile)
3480 {
3481 RTListNodeRemove(&pThis->aBootCatEntries[i].pBootFile->Core.Entry);
3482 RTListPrepend(&pThis->ObjectHead, &pThis->aBootCatEntries[i].pBootFile->Core.Entry);
3483 }
3484
3485 /* The boot catalog comes first. */
3486 RTListNodeRemove(&pThis->pBootCatFile->Core.Entry);
3487 RTListPrepend(&pThis->ObjectHead, &pThis->pBootCatFile->Core.Entry);
3488
3489 return VINF_SUCCESS;
3490}
3491
3492
3493/**
3494 * Finalizes the El Torito boot stuff, part 1.
3495 *
3496 * This includes generating the boot catalog data and fixing the location of all
3497 * related image files.
3498 *
3499 * @returns IPRT status code.
3500 * @param pThis The ISO maker instance.
3501 */
3502static int rtFsIsoMakerFinalizeBootStuffPart2(PRTFSISOMAKERINT pThis)
3503{
3504 /*
3505 * Anything?
3506 */
3507 if (!pThis->pBootCatFile)
3508 return VINF_SUCCESS;
3509
3510 /*
3511 * Fill in the descriptor.
3512 */
3513 PISO9660BOOTRECORDELTORITO pDesc = pThis->pElToritoDesc;
3514 pDesc->Hdr.bDescType = ISO9660VOLDESC_TYPE_BOOT_RECORD;
3515 pDesc->Hdr.bDescVersion = ISO9660PRIMARYVOLDESC_VERSION;
3516 memcpy(pDesc->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pDesc->Hdr.achStdId));
3517 memcpy(pDesc->achBootSystemId, RT_STR_TUPLE(ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID));
3518 pDesc->offBootCatalog = RT_H2LE_U32((uint32_t)(pThis->pBootCatFile->offData / RTFSISOMAKER_SECTOR_SIZE));
3519
3520 /*
3521 * Update the image file locations.
3522 */
3523 uint32_t cEntries = 2;
3524 for (uint32_t i = 1; i < RT_ELEMENTS(pThis->aBootCatEntries) - 1; i++)
3525 if (pThis->aBootCatEntries[i].pBootFile)
3526 {
3527 uint32_t off = pThis->aBootCatEntries[i].pBootFile->offData / RTFSISOMAKER_SECTOR_SIZE;
3528 off = RT_H2LE_U32(off);
3529 int rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile,
3530 i * 32 + RT_UOFFSETOF(ISO9660ELTORITOSECTIONENTRY, offBootImage),
3531 &off, sizeof(off), NULL /*pcbWritten*/);
3532 AssertRCReturn(rc, rc);
3533 if (i == cEntries)
3534 cEntries = i + 1;
3535 }
3536
3537 /*
3538 * Write end section.
3539 */
3540 ISO9660ELTORITOSECTIONHEADER Entry;
3541 Entry.bHeaderId = ISO9660_ELTORITO_HEADER_ID_FINAL_SECTION_HEADER;
3542 Entry.bPlatformId = ISO9660_ELTORITO_PLATFORM_ID_X86;
3543 Entry.cEntries = 0;
3544 RT_ZERO(Entry.achSectionId);
3545 int rc = RTVfsFileWriteAt(pThis->pBootCatFile->u.hVfsFile, cEntries * 32, &Entry, sizeof(Entry), NULL /*pcbWritten*/);
3546 AssertRCReturn(rc, rc);
3547
3548 return VINF_SUCCESS;
3549}
3550
3551
3552/**
3553 * Gathers the dirs for an ISO-9660 namespace (e.g. primary or joliet).
3554 *
3555 * @param pNamespace The namespace.
3556 * @param pFinalizedDirs The finalized directory structure. The
3557 * FinalizedDirs will be worked here.
3558 */
3559static void rtFsIsoMakerFinalizeGatherDirs(PRTFSISOMAKERNAMESPACE pNamespace, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs)
3560{
3561 RTListInit(&pFinalizedDirs->FinalizedDirs);
3562
3563 /*
3564 * Enter the root directory (if we got one).
3565 */
3566 if (!pNamespace->pRoot)
3567 return;
3568 PRTFSISOMAKERNAMEDIR pCurDir = pNamespace->pRoot->pDir;
3569 RTListAppend(&pFinalizedDirs->FinalizedDirs, &pCurDir->FinalizedEntry);
3570 do
3571 {
3572 /*
3573 * Scan pCurDir and add directories. We don't need to sort anything
3574 * here because the directory is already in path table compatible order.
3575 */
3576 uint32_t cLeft = pCurDir->cChildren;
3577 PPRTFSISOMAKERNAME ppChild = pCurDir->papChildren;
3578 while (cLeft-- > 0)
3579 {
3580 PRTFSISOMAKERNAME pChild = *ppChild++;
3581 if (pChild->pDir)
3582 RTListAppend(&pFinalizedDirs->FinalizedDirs, &pChild->pDir->FinalizedEntry);
3583 }
3584
3585 /*
3586 * Advance to the next directory.
3587 */
3588 pCurDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pCurDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
3589 } while (pCurDir);
3590}
3591
3592
3593/**
3594 * Finalizes a directory entry (i.e. namespace node).
3595 *
3596 * This calculates the directory record size.
3597 *
3598 * @returns IPRT status code.
3599 * @param pFinalizedDirs .
3600 * @param pName The directory entry to finalize.
3601 * @param offInDir The offset in the directory of this record.
3602 * @param uRootRockRidge This is the rock ridge level when
3603 * root, otherwise it's zero.
3604 */
3605static int rtFsIsoMakerFinalizeIsoDirectoryEntry(PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs, PRTFSISOMAKERNAME pName,
3606 uint32_t offInDir, uint8_t uRootRockRidge)
3607{
3608 /* Set directory and translation table offsets. (These are for
3609 helping generating data blocks later.) */
3610 pName->offDirRec = offInDir;
3611
3612 /* Calculate the minimal directory record size. */
3613 size_t cbDirRec = RT_UOFFSETOF(ISO9660DIRREC, achFileId) + pName->cbNameInDirRec + !(pName->cbNameInDirRec & 1);
3614 AssertReturn(cbDirRec <= UINT8_MAX, VERR_FILENAME_TOO_LONG);
3615 pName->cbDirRec = (uint8_t)cbDirRec;
3616
3617 /*
3618 * Calculate additional rock ridge stuff, if it doesn't all fit write it
3619 * to the spill file.
3620 */
3621 if (pFinalizedDirs->pRRSpillFile)
3622 {
3623 /** @todo rock ridge */
3624 RT_NOREF(uRootRockRidge);
3625 }
3626
3627 return VINF_SUCCESS;
3628}
3629
3630
3631/**
3632 * Finalizes either a primary and secondary ISO namespace.
3633 *
3634 * @returns IPRT status code
3635 * @param pThis The ISO maker instance.
3636 * @param pNamespace The namespace.
3637 * @param pFinalizedDirs The finalized directories structure for the
3638 * namespace.
3639 * @param poffData The data offset. We will allocate blocks for the
3640 * directories and the path tables.
3641 */
3642static int rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(PRTFSISOMAKERINT pThis, PRTFSISOMAKERNAMESPACE pNamespace,
3643 PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs, uint64_t *poffData)
3644{
3645 /* The directory data comes first, so take down it's offset. */
3646 pFinalizedDirs->offDirs = *poffData;
3647
3648 /*
3649 * Reset the rock ridge spill file (in case we allow finalizing more than once)
3650 * and create a new spill file if rock ridge is enabled. The directory entry
3651 * finalize function uses this as a clue that rock ridge is enabled.
3652 */
3653 if (pFinalizedDirs->pRRSpillFile)
3654 {
3655 rtFsIsoMakerObjRemoveWorker(pThis, &pFinalizedDirs->pRRSpillFile->Core);
3656 pFinalizedDirs->pRRSpillFile = NULL;
3657 }
3658 if (pNamespace->uRockRidgeLevel > 0)
3659 {
3660 /** @todo create rock ridge spill file to indicate rock ridge */
3661 }
3662
3663 int rc;
3664 uint16_t idPathTable = 1;
3665 uint32_t cbPathTable = 0;
3666 if (pNamespace->pRoot)
3667 {
3668 /*
3669 * Precalc the directory record size for the root directory.
3670 */
3671 rc = rtFsIsoMakerFinalizeIsoDirectoryEntry(pFinalizedDirs, pNamespace->pRoot, 0 /*offInDir*/, pNamespace->uRockRidgeLevel);
3672 AssertRCReturn(rc, rc);
3673
3674 /*
3675 * Work thru the directories.
3676 */
3677 PRTFSISOMAKERNAMEDIR pCurDir;
3678 RTListForEach(&pFinalizedDirs->FinalizedDirs, pCurDir, RTFSISOMAKERNAMEDIR, FinalizedEntry)
3679 {
3680 PRTFSISOMAKERNAME pCurName = pCurDir->pName;
3681 PRTFSISOMAKERNAME pParentName = pCurName->pParent ? pCurName->pParent : pCurName;
3682
3683 /* We don't do anything special for the special '.' and '..' directory
3684 entries, instead we use the directory entry in the parent directory
3685 with a 1 byte name (00 or 01). */
3686 Assert(pCurName->cbDirRec != 0);
3687 Assert(pParentName->cbDirRec != 0);
3688 pCurDir->cbDirRec00 = pCurName->cbDirRec - pCurName->cbNameInDirRec - !(pCurName->cbNameInDirRec & 1) + 1;
3689 pCurDir->cbDirRec01 = pParentName->cbDirRec - pParentName->cbNameInDirRec - !(pParentName->cbNameInDirRec & 1) + 1;
3690
3691 uint32_t offInDir = (uint32_t)pCurDir->cbDirRec00 + pCurDir->cbDirRec01;
3692
3693 /* Finalize the directory entries. */
3694 uint32_t cbTransTbl = 0;
3695 uint32_t cLeft = pCurDir->cChildren;
3696 PPRTFSISOMAKERNAME ppChild = pCurDir->papChildren;
3697 while (cLeft-- > 0)
3698 {
3699 PRTFSISOMAKERNAME pChild = *ppChild++;
3700 rc = rtFsIsoMakerFinalizeIsoDirectoryEntry(pFinalizedDirs, pChild, offInDir, 0 /*uRootRockRidge*/);
3701 AssertRCReturn(rc, rc);
3702
3703 offInDir += pChild->cbDirRec;
3704 if (pChild->cchTransNm)
3705 cbTransTbl += 2 /* type & space*/
3706 + RT_MAX(pChild->cchName, RTFSISOMAKER_TRANS_TBL_LEFT_PAD)
3707 + 1 /* tab */
3708 + pChild->cchTransNm
3709 + 1 /* newline */;
3710 }
3711
3712 /* Set the directory size and location, advancing the data offset. */
3713 pCurDir->cbDir = offInDir;
3714 pCurDir->offDir = *poffData;
3715 *poffData += RT_ALIGN_32(offInDir, RTFSISOMAKER_SECTOR_SIZE);
3716
3717 /* Set the translation table file size. */
3718 if (pCurDir->pTransTblFile)
3719 {
3720 pCurDir->pTransTblFile->cbData = cbTransTbl;
3721 pThis->cbData += RT_ALIGN_32(cbTransTbl, RTFSISOMAKER_SECTOR_SIZE);
3722 }
3723
3724 /* Add to the path table size calculation. */
3725 pCurDir->offPathTable = cbPathTable;
3726 pCurDir->idPathTable = idPathTable++;
3727 cbPathTable += RTFSISOMAKER_CALC_PATHREC_SIZE(pCurName->cbNameInDirRec);
3728 }
3729 }
3730
3731 /*
3732 * Update the rock ridge spill file size.
3733 */
3734 if (pFinalizedDirs->pRRSpillFile)
3735 {
3736 rc = RTVfsFileGetSize(pFinalizedDirs->pRRSpillFile->u.hVfsFile, &pFinalizedDirs->pRRSpillFile->cbData);
3737 AssertRCReturn(rc, rc);
3738 pThis->cbData += pFinalizedDirs->pRRSpillFile->cbData;
3739 }
3740
3741 /*
3742 * Calculate the path table offsets and move past them.
3743 */
3744 pFinalizedDirs->cbPathTable = cbPathTable;
3745 pFinalizedDirs->offPathTableL = *poffData;
3746 *poffData += RT_ALIGN_64(cbPathTable, RTFSISOMAKER_SECTOR_SIZE);
3747
3748 pFinalizedDirs->offPathTableM = *poffData;
3749 *poffData += RT_ALIGN_64(cbPathTable, RTFSISOMAKER_SECTOR_SIZE);
3750
3751 return VINF_SUCCESS;
3752}
3753
3754
3755
3756/**
3757 * Finalizes directories and related stuff.
3758 *
3759 * This will not generate actual directory data, but calculate the size of it
3760 * once it's generated. Ditto for the path tables. The exception is the rock
3761 * ridge spill file, which will be generated in memory.
3762 *
3763 * @returns IPRT status code.
3764 * @param pThis The ISO maker instance.
3765 * @param poffData The data offset (in/out).
3766 */
3767static int rtFsIsoMakerFinalizeDirectories(PRTFSISOMAKERINT pThis, uint64_t *poffData)
3768{
3769 /*
3770 * Locate the directories, width first, inserting them in the finalized lists so
3771 * we can process them efficiently.
3772 */
3773 rtFsIsoMakerFinalizeGatherDirs(&pThis->PrimaryIso, &pThis->PrimaryIsoDirs);
3774 rtFsIsoMakerFinalizeGatherDirs(&pThis->Joliet, &pThis->JolietDirs);
3775
3776 /*
3777 * Process the primary ISO and joliet namespaces.
3778 */
3779 int rc = rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(pThis, &pThis->PrimaryIso, &pThis->PrimaryIsoDirs, poffData);
3780 if (RT_SUCCESS(rc))
3781 rc = rtFsIsoMakerFinalizeDirectoriesInIsoNamespace(pThis, &pThis->Joliet, &pThis->JolietDirs, poffData);
3782 if (RT_SUCCESS(rc))
3783 {
3784 /*
3785 * Later: UDF, HFS.
3786 */
3787 }
3788 return rc;
3789}
3790
3791
3792/**
3793 * Finalizes data allocations.
3794 *
3795 * This will set the RTFSISOMAKERFILE::offData members.
3796 *
3797 * @returns IPRT status code.
3798 * @param pThis The ISO maker instance.
3799 * @param poffData The data offset (in/out).
3800 */
3801static int rtFsIsoMakerFinalizeData(PRTFSISOMAKERINT pThis, uint64_t *poffData)
3802{
3803 pThis->offFirstFile = *poffData;
3804
3805 /*
3806 * We currently does not have any ordering prioritizing implemented, so we
3807 * just store files in the order they were added.
3808 */
3809 PRTFSISOMAKEROBJ pCur;
3810 RTListForEach(&pThis->ObjectHead, pCur, RTFSISOMAKEROBJ, Entry)
3811 {
3812 if (pCur->enmType == RTFSISOMAKEROBJTYPE_FILE)
3813 {
3814 PRTFSISOMAKERFILE pCurFile = (PRTFSISOMAKERFILE)pCur;
3815 if (pCurFile->offData == UINT64_MAX)
3816 {
3817 pCurFile->offData = *poffData;
3818 *poffData += RT_ALIGN_64(pCurFile->cbData, RTFSISOMAKER_SECTOR_SIZE);
3819 RTListAppend(&pThis->FinalizedFiles, &pCurFile->FinalizedEntry);
3820 Log4(("rtFsIsoMakerFinalizeData: %#x @%#RX64 cbData=%#RX64\n", pCurFile->Core.idxObj, pCurFile->offData, pCurFile->cbData));
3821 }
3822
3823 /*
3824 * Create the boot info table.
3825 */
3826 if (pCurFile->pBootInfoTable)
3827 {
3828 /*
3829 * Checksum the file.
3830 */
3831 int rc;
3832 RTVFSFILE hVfsFile;
3833 uint64_t offBase;
3834 switch (pCurFile->enmSrcType)
3835 {
3836 case RTFSISOMAKERSRCTYPE_PATH:
3837 rc = RTVfsChainOpenFile(pCurFile->u.pszSrcPath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
3838 &hVfsFile, NULL, NULL);
3839 AssertMsgRCReturn(rc, ("%s -> %Rrc\n", pCurFile->u.pszSrcPath, rc), rc);
3840 offBase = 0;
3841 break;
3842 case RTFSISOMAKERSRCTYPE_VFS_FILE:
3843 hVfsFile = pCurFile->u.hVfsFile;
3844 offBase = 0;
3845 rc = VINF_SUCCESS;
3846 break;
3847 case RTFSISOMAKERSRCTYPE_COMMON:
3848 hVfsFile = pThis->paCommonSources[pCurFile->u.Common.idxSrc];
3849 offBase = pCurFile->u.Common.offData;
3850 rc = VINF_SUCCESS;
3851 break;
3852 default:
3853 AssertMsgFailedReturn(("enmSrcType=%d\n", pCurFile->enmSrcType), VERR_INTERNAL_ERROR_3);
3854 }
3855
3856 uint32_t uChecksum = 0;
3857 uint32_t off = 64;
3858 uint32_t cbLeft = RT_MAX(64, (uint32_t)pCurFile->cbData) - 64;
3859 while (cbLeft > 0)
3860 {
3861 union
3862 {
3863 uint8_t ab[_16K];
3864 uint32_t au32[_16K / sizeof(uint32_t)];
3865 } uBuf;
3866 uint32_t cbRead = RT_MIN(sizeof(uBuf), cbLeft);
3867 if (cbRead & 3)
3868 RT_ZERO(uBuf);
3869 rc = RTVfsFileReadAt(hVfsFile, offBase + off, &uBuf, cbRead, NULL);
3870 if (RT_FAILURE(rc))
3871 break;
3872
3873 size_t i = RT_ALIGN_Z(cbRead, sizeof(uint32_t)) / sizeof(uint32_t);
3874 while (i-- > 0)
3875 uChecksum += RT_LE2H_U32(uBuf.au32[i]);
3876
3877 off += cbRead;
3878 cbLeft -= cbRead;
3879 }
3880
3881 if (pCurFile->enmSrcType == RTFSISOMAKERSRCTYPE_PATH)
3882 RTVfsFileRelease(hVfsFile);
3883 if (RT_FAILURE(rc))
3884 return rc;
3885
3886 /*
3887 * Populate the structure.
3888 */
3889 pCurFile->pBootInfoTable->offPrimaryVolDesc = RT_H2LE_U32(16 * RTFSISOMAKER_SECTOR_SIZE);
3890 pCurFile->pBootInfoTable->offBootFile = RT_H2LE_U32((uint32_t)(pCurFile->offData / RTFSISOMAKER_SECTOR_SIZE));
3891 pCurFile->pBootInfoTable->cbBootFile = RT_H2LE_U32((uint32_t)pCurFile->cbData);
3892 pCurFile->pBootInfoTable->uChecksum = RT_H2LE_U32(uChecksum);
3893 RT_ZERO(pCurFile->pBootInfoTable->auReserved);
3894 }
3895 }
3896 }
3897
3898 return VINF_SUCCESS;
3899}
3900
3901
3902/**
3903 * Copies the given string as UTF-16 and pad unused space in the destination
3904 * with spaces.
3905 *
3906 * @param pachDst The destination field. C type is char, but real life
3907 * type is UTF-16 / UCS-2.
3908 * @param cchDst The size of the destination field.
3909 * @param pszSrc The source string. NULL is treated like empty string.
3910 */
3911static void rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(char *pachDst, size_t cchDst, const char *pszSrc)
3912{
3913 size_t cwcSrc = 0;
3914 if (pszSrc)
3915 {
3916 RTUTF16 wszSrc[256];
3917 PRTUTF16 pwszSrc = wszSrc;
3918 int rc = RTStrToUtf16BigEx(pszSrc, RTSTR_MAX, &pwszSrc, RT_ELEMENTS(wszSrc), &cwcSrc);
3919 AssertRCStmt(rc, cwcSrc = 0);
3920
3921 if (cwcSrc > cchDst / sizeof(RTUTF16))
3922 cwcSrc = cchDst / sizeof(RTUTF16);
3923 memcpy(pachDst, wszSrc, cwcSrc * sizeof(RTUTF16));
3924 }
3925
3926 /* Space padding. Note! cchDst can be an odd number. */
3927 size_t cchWritten = cwcSrc * sizeof(RTUTF16);
3928 if (cchWritten < cchDst)
3929 {
3930 while (cchWritten + 2 <= cchDst)
3931 {
3932 pachDst[cchWritten++] = '\0';
3933 pachDst[cchWritten++] = ' ';
3934 }
3935 if (cchWritten < cchDst)
3936 pachDst[cchWritten] = '\0';
3937 }
3938}
3939
3940
3941/**
3942 * Copies the given string and pad unused space in the destination with spaces.
3943 *
3944 * @param pachDst The destination field.
3945 * @param cchDst The size of the destination field.
3946 * @param pszSrc The source string. NULL is treated like empty string.
3947 */
3948static void rtFsIsoMakerFinalizeCopyAndSpacePad(char *pachDst, size_t cchDst, const char *pszSrc)
3949{
3950 size_t cchSrc;
3951 if (!pszSrc)
3952 cchSrc = 0;
3953 else
3954 {
3955 cchSrc = strlen(pszSrc);
3956 if (cchSrc > cchDst)
3957 cchSrc = cchDst;
3958 memcpy(pachDst, pszSrc, cchSrc);
3959 }
3960 if (cchSrc < cchDst)
3961 memset(&pachDst[cchSrc], ' ', cchDst - cchSrc);
3962}
3963
3964
3965/**
3966 * Formats a timespec as an ISO-9660 ascii timestamp.
3967 *
3968 * @param pTime The timespec to format.
3969 * @param pIsoTs The ISO-9660 timestamp destination buffer.
3970 */
3971static void rtFsIsoMakerTimespecToIso9660Timestamp(PCRTTIMESPEC pTime, PISO9660TIMESTAMP pIsoTs)
3972{
3973 RTTIME Exploded;
3974 RTTimeExplode(&Exploded, pTime);
3975
3976 char szTmp[64];
3977#define FORMAT_FIELD(a_achDst, a_uSrc) \
3978 do { \
3979 RTStrFormatU32(szTmp, sizeof(szTmp), a_uSrc, 10, sizeof(a_achDst), sizeof(a_achDst), \
3980 RTSTR_F_ZEROPAD | RTSTR_F_WIDTH | RTSTR_F_PRECISION); \
3981 memcpy(a_achDst, szTmp, sizeof(a_achDst)); \
3982 } while (0)
3983 FORMAT_FIELD(pIsoTs->achYear, Exploded.i32Year);
3984 FORMAT_FIELD(pIsoTs->achMonth, Exploded.u8Month);
3985 FORMAT_FIELD(pIsoTs->achDay, Exploded.u8MonthDay);
3986 FORMAT_FIELD(pIsoTs->achHour, Exploded.u8Hour);
3987 FORMAT_FIELD(pIsoTs->achMinute, Exploded.u8Minute);
3988 FORMAT_FIELD(pIsoTs->achSecond, Exploded.u8Second);
3989 FORMAT_FIELD(pIsoTs->achCentisecond, Exploded.u32Nanosecond / RT_NS_10MS);
3990#undef FORMAT_FIELD
3991 pIsoTs->offUtc = 0;
3992}
3993
3994/**
3995 * Formats zero ISO-9660 ascii timestamp (treated as not specified).
3996 *
3997 * @param pIsoTs The ISO-9660 timestamp destination buffer.
3998 */
3999static void rtFsIsoMakerZero9660Timestamp(PISO9660TIMESTAMP pIsoTs)
4000{
4001 memset(pIsoTs, '0', RT_OFFSETOF(ISO9660TIMESTAMP, offUtc));
4002 pIsoTs->offUtc = 0;
4003}
4004
4005
4006/**
4007 * Formats a timespec as an ISO-9660 record timestamp.
4008 *
4009 * @param pTime The timespec to format.
4010 * @param pIsoTs The ISO-9660 timestamp destination buffer.
4011 */
4012static void rtFsIsoMakerTimespecToIso9660RecTimestamp(PCRTTIMESPEC pTime, PISO9660RECTIMESTAMP pIsoRecTs)
4013{
4014 RTTIME Exploded;
4015 RTTimeExplode(&Exploded, pTime);
4016
4017 pIsoRecTs->bYear = Exploded.i32Year >= 1900 ? Exploded.i32Year - 1900 : 0;
4018 pIsoRecTs->bMonth = Exploded.u8Month;
4019 pIsoRecTs->bDay = Exploded.u8MonthDay;
4020 pIsoRecTs->bHour = Exploded.u8Hour;
4021 pIsoRecTs->bMinute = Exploded.u8Minute;
4022 pIsoRecTs->bSecond = Exploded.u8Second;
4023 pIsoRecTs->offUtc = 0;
4024}
4025
4026
4027/**
4028 * Allocate and prepare the volume descriptors.
4029 *
4030 * What's not done here gets done later by rtFsIsoMakerFinalizeBootStuffPart2,
4031 * or at teh very end of the finalization by
4032 * rtFsIsoMakerFinalizeVolumeDescriptors.
4033 *
4034 * @returns IPRT status code
4035 * @param pThis The ISO maker instance.
4036 */
4037static int rtFsIsoMakerFinalizePrepVolumeDescriptors(PRTFSISOMAKERINT pThis)
4038{
4039 /*
4040 * Allocate and calc pointers.
4041 */
4042 RTMemFree(pThis->pbVolDescs);
4043 pThis->pbVolDescs = (uint8_t *)RTMemAllocZ(pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE);
4044 AssertReturn(pThis->pbVolDescs, VERR_NO_MEMORY);
4045
4046 uint32_t offVolDescs = 0;
4047
4048 pThis->pPrimaryVolDesc = (PISO9660PRIMARYVOLDESC)&pThis->pbVolDescs[offVolDescs];
4049 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4050
4051 if (!pThis->pBootCatFile)
4052 pThis->pElToritoDesc = NULL;
4053 else
4054 {
4055 pThis->pElToritoDesc = (PISO9660BOOTRECORDELTORITO)&pThis->pbVolDescs[offVolDescs];
4056 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4057 }
4058
4059 if (!pThis->Joliet.uLevel)
4060 pThis->pJolietVolDesc = NULL;
4061 else
4062 {
4063 pThis->pJolietVolDesc = (PISO9660SUPVOLDESC)&pThis->pbVolDescs[offVolDescs];
4064 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4065 }
4066
4067 pThis->pTerminatorVolDesc = (PISO9660VOLDESCHDR)&pThis->pbVolDescs[offVolDescs];
4068 offVolDescs += RTFSISOMAKER_SECTOR_SIZE;
4069
4070 if (pThis->Udf.uLevel > 0)
4071 {
4072 /** @todo UDF descriptors. */
4073 }
4074 AssertReturn(offVolDescs == pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE, VERR_INTERNAL_ERROR_2);
4075
4076 /*
4077 * This may be needed later.
4078 */
4079 char szImageCreationTime[42];
4080 RTTimeSpecToString(&pThis->ImageCreationTime, szImageCreationTime, sizeof(szImageCreationTime));
4081
4082 /*
4083 * Initialize the primary descriptor.
4084 */
4085 PISO9660PRIMARYVOLDESC pPrimary = pThis->pPrimaryVolDesc;
4086
4087 pPrimary->Hdr.bDescType = ISO9660VOLDESC_TYPE_PRIMARY;
4088 pPrimary->Hdr.bDescVersion = ISO9660PRIMARYVOLDESC_VERSION;
4089 memcpy(pPrimary->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pPrimary->Hdr.achStdId));
4090 //pPrimary->bPadding8 = 0;
4091 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achSystemId, sizeof(pPrimary->achSystemId), pThis->PrimaryIso.pszSystemId);
4092 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achVolumeId, sizeof(pPrimary->achVolumeId),
4093 pThis->PrimaryIso.pszVolumeId ? pThis->PrimaryIso.pszVolumeId : szImageCreationTime);
4094 //pPrimary->Unused73 = {0}
4095 //pPrimary->VolumeSpaceSize = later
4096 //pPrimary->abUnused89 = {0}
4097 pPrimary->cVolumesInSet.be = RT_H2BE_U16_C(1);
4098 pPrimary->cVolumesInSet.le = RT_H2LE_U16_C(1);
4099 pPrimary->VolumeSeqNo.be = RT_H2BE_U16_C(1);
4100 pPrimary->VolumeSeqNo.le = RT_H2LE_U16_C(1);
4101 pPrimary->cbLogicalBlock.be = RT_H2BE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4102 pPrimary->cbLogicalBlock.le = RT_H2LE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4103 //pPrimary->cbPathTable = later
4104 //pPrimary->offTypeLPathTable = later
4105 //pPrimary->offOptionalTypeLPathTable = {0}
4106 //pPrimary->offTypeMPathTable = later
4107 //pPrimary->offOptionalTypeMPathTable = {0}
4108 //pPrimary->RootDir = later
4109 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achVolumeSetId, sizeof(pPrimary->achVolumeSetId),
4110 pThis->PrimaryIso.pszVolumeSetId);
4111 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achPublisherId, sizeof(pPrimary->achPublisherId),
4112 pThis->PrimaryIso.pszPublisherId);
4113 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achDataPreparerId, sizeof(pPrimary->achDataPreparerId),
4114 pThis->PrimaryIso.pszDataPreparerId);
4115 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achApplicationId, sizeof(pPrimary->achApplicationId),
4116 pThis->PrimaryIso.pszApplicationId);
4117 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achCopyrightFileId, sizeof(pPrimary->achCopyrightFileId),
4118 pThis->PrimaryIso.pszCopyrightFileId);
4119 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achAbstractFileId, sizeof(pPrimary->achAbstractFileId),
4120 pThis->PrimaryIso.pszAbstractFileId);
4121 rtFsIsoMakerFinalizeCopyAndSpacePad(pPrimary->achBibliographicFileId, sizeof(pPrimary->achBibliographicFileId),
4122 pThis->PrimaryIso.pszBibliographicFileId);
4123 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pPrimary->BirthTime);
4124 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pPrimary->ModifyTime);
4125 rtFsIsoMakerZero9660Timestamp(&pPrimary->ExpireTime);
4126 rtFsIsoMakerZero9660Timestamp(&pPrimary->EffectiveTime);
4127 pPrimary->bFileStructureVersion = ISO9660_FILE_STRUCTURE_VERSION;
4128 //pPrimary->bReserved883 = 0;
4129 //RT_ZERO(pPrimary->abAppUse);
4130 //RT_ZERO(pPrimary->abReserved1396);
4131
4132 /*
4133 * Initialize the joliet descriptor if included.
4134 */
4135 PISO9660SUPVOLDESC pJoliet = pThis->pJolietVolDesc;
4136 if (pJoliet)
4137 {
4138 pJoliet->Hdr.bDescType = ISO9660VOLDESC_TYPE_SUPPLEMENTARY;
4139 pJoliet->Hdr.bDescVersion = ISO9660SUPVOLDESC_VERSION;
4140 memcpy(pJoliet->Hdr.achStdId, ISO9660VOLDESC_STD_ID, sizeof(pJoliet->Hdr.achStdId));
4141 pJoliet->fVolumeFlags = ISO9660SUPVOLDESC_VOL_F_ESC_ONLY_REG;
4142 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achSystemId, sizeof(pJoliet->achSystemId), pThis->Joliet.pszSystemId);
4143 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achVolumeId, sizeof(pJoliet->achVolumeId),
4144 pThis->Joliet.pszVolumeId ? pThis->Joliet.pszVolumeId : szImageCreationTime);
4145 //pJoliet->Unused73 = {0}
4146 //pJoliet->VolumeSpaceSize = later
4147 memset(pJoliet->abEscapeSequences, ' ', sizeof(pJoliet->abEscapeSequences));
4148 pJoliet->abEscapeSequences[0] = ISO9660_JOLIET_ESC_SEQ_0;
4149 pJoliet->abEscapeSequences[1] = ISO9660_JOLIET_ESC_SEQ_1;
4150 pJoliet->abEscapeSequences[2] = pThis->Joliet.uLevel == 1 ? ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
4151 : pThis->Joliet.uLevel == 2 ? ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
4152 : ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3;
4153 pJoliet->cVolumesInSet.be = RT_H2BE_U16_C(1);
4154 pJoliet->cVolumesInSet.le = RT_H2LE_U16_C(1);
4155 pJoliet->VolumeSeqNo.be = RT_H2BE_U16_C(1);
4156 pJoliet->VolumeSeqNo.le = RT_H2LE_U16_C(1);
4157 pJoliet->cbLogicalBlock.be = RT_H2BE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4158 pJoliet->cbLogicalBlock.le = RT_H2LE_U16_C(RTFSISOMAKER_SECTOR_SIZE);
4159 //pJoliet->cbPathTable = later
4160 //pJoliet->offTypeLPathTable = later
4161 //pJoliet->offOptionalTypeLPathTable = {0}
4162 //pJoliet->offTypeMPathTable = later
4163 //pJoliet->offOptionalTypeMPathTable = {0}
4164 //pJoliet->RootDir = later
4165 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achVolumeSetId, sizeof(pJoliet->achVolumeSetId),
4166 pThis->Joliet.pszVolumeSetId);
4167 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achPublisherId, sizeof(pJoliet->achPublisherId),
4168 pThis->Joliet.pszPublisherId);
4169 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achDataPreparerId, sizeof(pJoliet->achDataPreparerId),
4170 pThis->Joliet.pszDataPreparerId);
4171 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achApplicationId, sizeof(pJoliet->achApplicationId),
4172 pThis->Joliet.pszApplicationId);
4173 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achCopyrightFileId, sizeof(pJoliet->achCopyrightFileId),
4174 pThis->Joliet.pszCopyrightFileId);
4175 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achAbstractFileId, sizeof(pJoliet->achAbstractFileId),
4176 pThis->Joliet.pszAbstractFileId);
4177 rtFsIsoMakerFinalizeCopyAsUtf16BigAndSpacePad(pJoliet->achBibliographicFileId, sizeof(pJoliet->achBibliographicFileId),
4178 pThis->Joliet.pszBibliographicFileId);
4179 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pJoliet->BirthTime);
4180 rtFsIsoMakerTimespecToIso9660Timestamp(&pThis->ImageCreationTime, &pJoliet->ModifyTime);
4181 rtFsIsoMakerZero9660Timestamp(&pJoliet->ExpireTime);
4182 rtFsIsoMakerZero9660Timestamp(&pJoliet->EffectiveTime);
4183 pJoliet->bFileStructureVersion = ISO9660_FILE_STRUCTURE_VERSION;
4184 //pJoliet->bReserved883 = 0;
4185 //RT_ZERO(pJoliet->abAppUse);
4186 //RT_ZERO(pJoliet->abReserved1396);
4187 }
4188
4189 /*
4190 * The ISO-9660 terminator descriptor.
4191 */
4192 pThis->pTerminatorVolDesc->bDescType = ISO9660VOLDESC_TYPE_TERMINATOR;
4193 pThis->pTerminatorVolDesc->bDescVersion = 1;
4194 memcpy(pThis->pTerminatorVolDesc->achStdId, ISO9660VOLDESC_STD_ID, sizeof(pThis->pTerminatorVolDesc->achStdId));
4195
4196 return VINF_SUCCESS;
4197}
4198
4199
4200/**
4201 * Finalizes the volume descriptors.
4202 *
4203 * This will set the RTFSISOMAKERFILE::offData members.
4204 *
4205 * @returns IPRT status code.
4206 * @param pThis The ISO maker instance.
4207 */
4208static int rtFsIsoMakerFinalizeVolumeDescriptors(PRTFSISOMAKERINT pThis)
4209{
4210 AssertReturn(pThis->pbVolDescs && pThis->pPrimaryVolDesc && pThis->pTerminatorVolDesc, VERR_INTERNAL_ERROR_3);
4211
4212 /*
4213 * Primary descriptor.
4214 */
4215 PISO9660PRIMARYVOLDESC pPrimary = pThis->pPrimaryVolDesc;
4216
4217 pPrimary->VolumeSpaceSize.be = RT_H2BE_U32(pThis->cbFinalizedImage / RTFSISOMAKER_SECTOR_SIZE);
4218 pPrimary->VolumeSpaceSize.le = RT_H2LE_U32(pThis->cbFinalizedImage / RTFSISOMAKER_SECTOR_SIZE);
4219 pPrimary->cbPathTable.be = RT_H2BE_U32(pThis->PrimaryIsoDirs.cbPathTable);
4220 pPrimary->cbPathTable.le = RT_H2LE_U32(pThis->PrimaryIsoDirs.cbPathTable);
4221 pPrimary->offTypeLPathTable = RT_H2LE_U32(pThis->PrimaryIsoDirs.offPathTableL / RTFSISOMAKER_SECTOR_SIZE);
4222 pPrimary->offTypeMPathTable = RT_H2BE_U32(pThis->PrimaryIsoDirs.offPathTableM / RTFSISOMAKER_SECTOR_SIZE);
4223 pPrimary->RootDir.DirRec.cbDirRec = sizeof(pPrimary->RootDir);
4224 pPrimary->RootDir.DirRec.cExtAttrBlocks = 0;
4225 pPrimary->RootDir.DirRec.offExtent.be = RT_H2BE_U32(pThis->PrimaryIso.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4226 pPrimary->RootDir.DirRec.offExtent.le = RT_H2LE_U32(pThis->PrimaryIso.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4227 pPrimary->RootDir.DirRec.cbData.be = RT_H2BE_U32(pThis->PrimaryIso.pRoot->pDir->cbDir);
4228 pPrimary->RootDir.DirRec.cbData.le = RT_H2LE_U32(pThis->PrimaryIso.pRoot->pDir->cbDir);
4229 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pThis->PrimaryIso.pRoot->pObj->BirthTime, &pPrimary->RootDir.DirRec.RecTime);
4230 pPrimary->RootDir.DirRec.fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
4231 pPrimary->RootDir.DirRec.bFileUnitSize = 0;
4232 pPrimary->RootDir.DirRec.bInterleaveGapSize = 0;
4233 pPrimary->RootDir.DirRec.VolumeSeqNo.be = RT_H2BE_U16_C(1);
4234 pPrimary->RootDir.DirRec.VolumeSeqNo.le = RT_H2LE_U16_C(1);
4235 pPrimary->RootDir.DirRec.bFileIdLength = 1;
4236 pPrimary->RootDir.DirRec.achFileId[0] = 0x00;
4237
4238 /*
4239 * Initialize the joliet descriptor if included.
4240 */
4241 PISO9660SUPVOLDESC pJoliet = pThis->pJolietVolDesc;
4242 if (pJoliet)
4243 {
4244 pJoliet->VolumeSpaceSize = pPrimary->VolumeSpaceSize;
4245 pJoliet->cbPathTable.be = RT_H2BE_U32(pThis->JolietDirs.cbPathTable);
4246 pJoliet->cbPathTable.le = RT_H2LE_U32(pThis->JolietDirs.cbPathTable);
4247 pJoliet->offTypeLPathTable = RT_H2LE_U32(pThis->JolietDirs.offPathTableL / RTFSISOMAKER_SECTOR_SIZE);
4248 pJoliet->offTypeMPathTable = RT_H2BE_U32(pThis->JolietDirs.offPathTableM / RTFSISOMAKER_SECTOR_SIZE);
4249 pJoliet->RootDir.DirRec.cbDirRec = sizeof(pJoliet->RootDir);
4250 pJoliet->RootDir.DirRec.cExtAttrBlocks = 0;
4251 pJoliet->RootDir.DirRec.offExtent.be = RT_H2BE_U32(pThis->Joliet.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4252 pJoliet->RootDir.DirRec.offExtent.le = RT_H2LE_U32(pThis->Joliet.pRoot->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4253 pJoliet->RootDir.DirRec.cbData.be = RT_H2BE_U32(pThis->Joliet.pRoot->pDir->cbDir);
4254 pJoliet->RootDir.DirRec.cbData.le = RT_H2LE_U32(pThis->Joliet.pRoot->pDir->cbDir);
4255 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pThis->Joliet.pRoot->pObj->BirthTime, &pJoliet->RootDir.DirRec.RecTime);
4256 pJoliet->RootDir.DirRec.fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
4257 pJoliet->RootDir.DirRec.bFileUnitSize = 0;
4258 pJoliet->RootDir.DirRec.bInterleaveGapSize = 0;
4259 pJoliet->RootDir.DirRec.VolumeSeqNo.be = RT_H2BE_U16_C(1);
4260 pJoliet->RootDir.DirRec.VolumeSeqNo.le = RT_H2LE_U16_C(1);
4261 pJoliet->RootDir.DirRec.bFileIdLength = 1;
4262 pJoliet->RootDir.DirRec.achFileId[0] = 0x00;
4263 }
4264
4265 return VINF_SUCCESS;
4266}
4267
4268
4269/**
4270 * Finalizes the image.
4271 *
4272 * @returns IPRT status code.
4273 * @param hIsoMaker The ISO maker handle.
4274 */
4275RTDECL(int) RTFsIsoMakerFinalize(RTFSISOMAKER hIsoMaker)
4276{
4277 PRTFSISOMAKERINT pThis = hIsoMaker;
4278 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
4279 AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
4280
4281 /*
4282 * Remove orphaned objects and allocate volume descriptors.
4283 */
4284 int rc = rtFsIsoMakerFinalizeRemoveOrphans(pThis);
4285 if (RT_FAILURE(rc))
4286 return rc;
4287 AssertReturn(pThis->cObjects > 0, VERR_NO_DATA);
4288 AssertReturn(pThis->PrimaryIso.pRoot || pThis->PrimaryIso.uLevel == 0, VERR_NO_DATA);
4289 AssertReturn(pThis->Joliet.pRoot || pThis->Joliet.uLevel == 0, VERR_NO_DATA);
4290
4291 rc = rtFsIsoMakerFinalizePrepVolumeDescriptors(pThis);
4292 if (RT_FAILURE(rc))
4293 return rc;
4294
4295 /*
4296 * If there is any boot related stuff to be included, it ends up right after
4297 * the descriptors.
4298 */
4299 uint64_t offData = _32K + pThis->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE;
4300 rc = rtFsIsoMakerFinalizeBootStuffPart1(pThis);
4301 if (RT_SUCCESS(rc))
4302 {
4303 /*
4304 * Directories and path tables comes next.
4305 */
4306 rc = rtFsIsoMakerFinalizeDirectories(pThis, &offData);
4307 if (RT_SUCCESS(rc))
4308 {
4309 /*
4310 * Then we store the file data.
4311 */
4312 rc = rtFsIsoMakerFinalizeData(pThis, &offData);
4313 if (RT_SUCCESS(rc))
4314 {
4315 pThis->cbFinalizedImage = offData;
4316
4317 /*
4318 * Do a 2nd pass over the boot stuff to finalize locations.
4319 */
4320 rc = rtFsIsoMakerFinalizeBootStuffPart2(pThis);
4321 if (RT_SUCCESS(rc))
4322 {
4323 /*
4324 * Finally, finalize the volume descriptors as they depend on some of the
4325 * block allocations done in the previous steps.
4326 */
4327 rc = rtFsIsoMakerFinalizeVolumeDescriptors(pThis);
4328 if (RT_SUCCESS(rc))
4329 {
4330 pThis->fFinalized = true;
4331 return VINF_SUCCESS;
4332 }
4333 }
4334 }
4335 }
4336 }
4337 return rc;
4338}
4339
4340
4341
4342
4343
4344/*
4345 *
4346 * Image I/O.
4347 * Image I/O.
4348 * Image I/O.
4349 *
4350 */
4351
4352/**
4353 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
4354 */
4355static DECLCALLBACK(int) rtFsIsoMakerOutFile_Close(void *pvThis)
4356{
4357 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4358
4359 RTFsIsoMakerRelease(pThis->pIsoMaker);
4360 pThis->pIsoMaker = NULL;
4361
4362 return VINF_SUCCESS;
4363}
4364
4365
4366/**
4367 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
4368 */
4369static DECLCALLBACK(int) rtFsIsoMakerOutFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4370{
4371 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
4372 PRTFSISOMAKERINT pIsoMaker = pThis->pIsoMaker;
4373
4374
4375 pObjInfo->cbObject = pIsoMaker->cbFinalizedImage;
4376 pObjInfo->cbAllocated = pIsoMaker->cbFinalizedImage;
4377 pObjInfo->AccessTime = pIsoMaker->ImageCreationTime;
4378 pObjInfo->ModificationTime = pIsoMaker->ImageCreationTime;
4379 pObjInfo->ChangeTime = pIsoMaker->ImageCreationTime;
4380 pObjInfo->BirthTime = pIsoMaker->ImageCreationTime;
4381 pObjInfo->Attr.fMode = 0444 | RTFS_TYPE_FILE | RTFS_DOS_READONLY;
4382
4383 switch (enmAddAttr)
4384 {
4385 case RTFSOBJATTRADD_NOTHING:
4386 enmAddAttr = RTFSOBJATTRADD_UNIX;
4387 /* fall thru */
4388 case RTFSOBJATTRADD_UNIX:
4389 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
4390 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
4391 pObjInfo->Attr.u.Unix.cHardlinks = 1;
4392 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
4393 pObjInfo->Attr.u.Unix.INodeId = 0;
4394 pObjInfo->Attr.u.Unix.fFlags = 0;
4395 pObjInfo->Attr.u.Unix.GenerationId = 0;
4396 pObjInfo->Attr.u.Unix.Device = 0;
4397 break;
4398
4399 case RTFSOBJATTRADD_UNIX_OWNER:
4400 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
4401 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
4402 break;
4403
4404 case RTFSOBJATTRADD_UNIX_GROUP:
4405 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
4406 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
4407 break;
4408
4409 case RTFSOBJATTRADD_EASIZE:
4410 pObjInfo->Attr.u.EASize.cb = 0;
4411 break;
4412
4413 default:
4414 AssertFailedReturn(VERR_INVALID_PARAMETER);
4415 }
4416 pObjInfo->Attr.enmAdditional = enmAddAttr;
4417
4418 return VINF_SUCCESS;
4419}
4420
4421
4422/**
4423 * Produces the content of a TRANS.TBL file as a memory file.
4424 *
4425 * @returns IPRT status code.
4426 * @param pThis The ISO maker output file instance. The file is
4427 * returned as pThis->hVfsSrcFile.
4428 * @param pFile The TRANS.TBL file.
4429 */
4430static int rtFsIsoMakerOutFile_ProduceTransTbl(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERFILE pFile)
4431{
4432 /*
4433 * Create memory file instance.
4434 */
4435 RTVFSFILE hVfsFile;
4436 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, pFile->cbData, &hVfsFile);
4437 AssertRCReturn(rc, rc);
4438
4439 /*
4440 * Produce the file content.
4441 */
4442 PRTFSISOMAKERNAME *ppChild = pFile->u.pTransTblDir->pDir->papChildren;
4443 uint32_t cLeft = pFile->u.pTransTblDir->pDir->cChildren;
4444 while (cLeft-- > 0)
4445 {
4446 PRTFSISOMAKERNAME pChild = *ppChild++;
4447 if (pChild->cchTransNm)
4448 {
4449 /** @todo TRANS.TBL codeset, currently using UTF-8 which is probably not it.
4450 * However, nobody uses this stuff any more, so who cares. */
4451 char szEntry[RTFSISOMAKER_MAX_NAME_BUF * 2 + 128];
4452 size_t cchEntry = RTStrPrintf(szEntry, sizeof(szEntry), "%c %-*s\t%s\n", pChild->pDir ? 'D' : 'F',
4453 RTFSISOMAKER_TRANS_TBL_LEFT_PAD, pChild->szName, pChild->pszTransNm);
4454 rc = RTVfsFileWrite(hVfsFile, szEntry, cchEntry, NULL);
4455 if (RT_FAILURE(rc))
4456 {
4457 RTVfsFileRelease(hVfsFile);
4458 return rc;
4459 }
4460 }
4461 }
4462
4463 /*
4464 * Check that the size matches our estimate.
4465 */
4466 uint64_t cbResult = 0;
4467 rc = RTVfsFileGetSize(hVfsFile, &cbResult);
4468 if (RT_SUCCESS(rc) && cbResult == pFile->cbData)
4469 {
4470 pThis->hVfsSrcFile = hVfsFile;
4471 return VINF_SUCCESS;
4472 }
4473
4474 AssertMsgFailed(("rc=%Rrc, cbResult=%#RX64 cbData=%#RX64\n", rc, cbResult, pFile->cbData));
4475 RTVfsFileRelease(hVfsFile);
4476 return VERR_INTERNAL_ERROR_4;
4477}
4478
4479
4480
4481/**
4482 * Reads file data.
4483 *
4484 * @returns IPRT status code
4485 * @param pThis The instance data for the VFS file. We use this to
4486 * keep hints about where we are and we which source
4487 * file we've opened/created.
4488 * @param pIsoMaker The ISO maker instance.
4489 * @param offUnsigned The ISO image byte offset of the requested data.
4490 * @param pbBuf The output buffer.
4491 * @param cbBuf How much to read.
4492 * @param pcbDone Where to return how much was read.
4493 */
4494static int rtFsIsoMakerOutFile_ReadFileData(PRTFSISOMAKEROUTPUTFILE pThis, PRTFSISOMAKERINT pIsoMaker, uint64_t offUnsigned,
4495 uint8_t *pbBuf, size_t cbBuf, size_t *pcbDone)
4496{
4497 *pcbDone = 0;
4498
4499 /*
4500 * Figure out which file. We keep a hint in the instance.
4501 */
4502 uint64_t offInFile;
4503 PRTFSISOMAKERFILE pFile = pThis->pFileHint;
4504 if (!pFile)
4505 {
4506 pFile = RTListGetFirst(&pIsoMaker->FinalizedFiles, RTFSISOMAKERFILE, FinalizedEntry);
4507 AssertReturn(pFile, VERR_INTERNAL_ERROR_2);
4508 }
4509 if ((offInFile = offUnsigned - pFile->offData) < RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE))
4510 { /* hit */ }
4511 else if (offUnsigned >= pFile->offData)
4512 {
4513 /* Seek forwards. */
4514 do
4515 {
4516 pFile = RTListGetNext(&pIsoMaker->FinalizedFiles, pFile, RTFSISOMAKERFILE, FinalizedEntry);
4517 AssertReturn(pFile, VERR_INTERNAL_ERROR_3);
4518 } while ((offInFile = offUnsigned - pFile->offData) >= RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE));
4519 }
4520 else
4521 {
4522 /* Seek backwards. */
4523 do
4524 {
4525 pFile = RTListGetPrev(&pIsoMaker->FinalizedFiles, pFile, RTFSISOMAKERFILE, FinalizedEntry);
4526 AssertReturn(pFile, VERR_INTERNAL_ERROR_3);
4527 } while ((offInFile = offUnsigned - pFile->offData) >= RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE));
4528 }
4529
4530 /*
4531 * Update the hint/current file.
4532 */
4533 if (pThis->pFileHint != pFile)
4534 {
4535 pThis->pFileHint = pFile;
4536 if (pThis->hVfsSrcFile != NIL_RTVFSFILE)
4537 {
4538 RTVfsFileRelease(pThis->hVfsSrcFile);
4539 pThis->hVfsSrcFile = NIL_RTVFSFILE;
4540 }
4541 }
4542
4543 /*
4544 * Produce data bits according to the source type.
4545 */
4546 if (offInFile < pFile->cbData)
4547 {
4548 int rc;
4549 size_t cbToRead = RT_MIN(cbBuf, pFile->cbData - offInFile);
4550
4551 switch (pFile->enmSrcType)
4552 {
4553 case RTFSISOMAKERSRCTYPE_PATH:
4554 if (pThis->hVfsSrcFile == NIL_RTVFSFILE)
4555 {
4556 rc = RTVfsChainOpenFile(pFile->u.pszSrcPath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
4557 &pThis->hVfsSrcFile, NULL, NULL);
4558 AssertMsgRCReturn(rc, ("%s -> %Rrc\n", pFile->u.pszSrcPath, rc), rc);
4559 }
4560 rc = RTVfsFileReadAt(pThis->hVfsSrcFile, offInFile, pbBuf, cbToRead, NULL);
4561 AssertRC(rc);
4562 break;
4563
4564 case RTFSISOMAKERSRCTYPE_VFS_FILE:
4565 rc = RTVfsFileReadAt(pFile->u.hVfsFile, offInFile, pbBuf, cbToRead, NULL);
4566 AssertRC(rc);
4567 break;
4568
4569 case RTFSISOMAKERSRCTYPE_COMMON:
4570 rc = RTVfsFileReadAt(pIsoMaker->paCommonSources[pFile->u.Common.idxSrc],
4571 pFile->u.Common.offData + offInFile, pbBuf, cbToRead, NULL);
4572 AssertRC(rc);
4573 break;
4574
4575 case RTFSISOMAKERSRCTYPE_TRANS_TBL:
4576 if (pThis->hVfsSrcFile == NIL_RTVFSFILE)
4577 {
4578 rc = rtFsIsoMakerOutFile_ProduceTransTbl(pThis, pFile);
4579 AssertRCReturn(rc, rc);
4580 }
4581 rc = RTVfsFileReadAt(pThis->hVfsSrcFile, offInFile, pbBuf, cbToRead, NULL);
4582 AssertRC(rc);
4583 break;
4584
4585 default:
4586 AssertFailedReturn(VERR_INTERNAL_ERROR_5);
4587 }
4588 if (RT_FAILURE(rc))
4589 return rc;
4590 *pcbDone = cbToRead;
4591
4592 /*
4593 * Do boot info table patching.
4594 */
4595 if ( pFile->pBootInfoTable
4596 && offInFile < 64
4597 && offInFile + cbToRead > 8)
4598 {
4599 size_t offInBuf = offInFile < 8 ? 8 - (size_t)offInFile : 0;
4600 size_t offInTab = offInFile <= 8 ? 0 : (size_t)offInFile - 8;
4601 size_t cbToCopy = RT_MIN(sizeof(*pFile->pBootInfoTable) - offInTab, cbToRead - offInBuf);
4602 memcpy(&pbBuf[offInBuf], (uint8_t *)pFile->pBootInfoTable + offInTab, cbToCopy);
4603 }
4604
4605 /*
4606 * Check if we're into the zero padding at the end of the file now.
4607 */
4608 if ( cbToRead < cbBuf
4609 && (pFile->cbData & RTFSISOMAKER_SECTOR_OFFSET_MASK)
4610 && offInFile + cbToRead == pFile->cbData)
4611 {
4612 cbBuf -= cbToRead;
4613 pbBuf += cbToRead;
4614 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - (pFile->cbData & RTFSISOMAKER_SECTOR_OFFSET_MASK));
4615 memset(pbBuf, 0, cbZeros);
4616 *pcbDone += cbZeros;
4617 }
4618 }
4619 else
4620 {
4621 size_t cbZeros = RT_MIN(cbBuf, RT_ALIGN_64(pFile->cbData, RTFSISOMAKER_SECTOR_SIZE) - offInFile);
4622 memset(pbBuf, 0, cbZeros);
4623 *pcbDone = cbZeros;
4624 }
4625 return VINF_SUCCESS;
4626}
4627
4628
4629/**
4630 * Generates ISO-9660 path table record into the specified buffer.
4631 *
4632 * @returns Number of bytes copied into the buffer.
4633 * @param pName The directory namespace node.
4634 * @param fUnicode Set if the name should be translated to big endian
4635 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4636 * @param pbBuf The buffer. This is large enough to hold the path
4637 * record (use RTFSISOMAKER_CALC_PATHREC_SIZE) and a zero
4638 * RTUTF16 terminator if @a fUnicode is true.
4639 */
4640static uint32_t rtFsIsoMakerOutFile_GeneratePathRec(PRTFSISOMAKERNAME pName, bool fUnicode, bool fLittleEndian, uint8_t *pbBuf)
4641{
4642 PISO9660PATHREC pPathRec = (PISO9660PATHREC)pbBuf;
4643 pPathRec->cbDirId = pName->cbNameInDirRec;
4644 pPathRec->cbExtAttr = 0;
4645 if (fLittleEndian)
4646 {
4647 pPathRec->offExtent = RT_H2LE_U32(pName->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4648 pPathRec->idParentRec = RT_H2LE_U16(pName->pParent ? pName->pParent->pDir->idPathTable : 1);
4649 }
4650 else
4651 {
4652 pPathRec->offExtent = RT_H2BE_U32(pName->pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4653 pPathRec->idParentRec = RT_H2BE_U16(pName->pParent ? pName->pParent->pDir->idPathTable : 1);
4654 }
4655 if (!fUnicode)
4656 {
4657 memcpy(&pPathRec->achDirId[0], pName->szName, pName->cbNameInDirRec);
4658 if (pName->cbNameInDirRec & 1)
4659 pPathRec->achDirId[pName->cbNameInDirRec] = '\0';
4660 }
4661 else
4662 {
4663 /* Caller made sure there is space for a zero terminator character. */
4664 PRTUTF16 pwszTmp = (PRTUTF16)&pPathRec->achDirId[0];
4665 size_t cwcResult = 0;
4666 int rc = RTStrToUtf16BigEx(pName->szName, RTSTR_MAX, &pwszTmp, pName->cbNameInDirRec / sizeof(RTUTF16) + 1, &cwcResult);
4667 AssertRC(rc);
4668 Assert( cwcResult * sizeof(RTUTF16) == pName->cbNameInDirRec
4669 || (!pName->pParent && cwcResult == 0 && pName->cbNameInDirRec == 1) );
4670
4671 }
4672 return RTFSISOMAKER_CALC_PATHREC_SIZE(pName->cbNameInDirRec);
4673}
4674
4675
4676/**
4677 * Deals with situations where the destination buffer doesn't cover the whole
4678 * path table record.
4679 *
4680 * @returns Number of bytes copied into the buffer.
4681 * @param pName The directory namespace node.
4682 * @param fUnicode Set if the name should be translated to big endian
4683 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4684 * @param offInRec The offset into the path table record.
4685 * @param pbBuf The buffer.
4686 * @param cbBuf The buffer size.
4687 */
4688static uint32_t rtFsIsoMakerOutFile_GeneratePathRecPartial(PRTFSISOMAKERNAME pName, bool fUnicode, bool fLittleEndian,
4689 uint32_t offInRec, uint8_t *pbBuf, size_t cbBuf)
4690{
4691 uint8_t abTmpRec[256];
4692 size_t cbToCopy = rtFsIsoMakerOutFile_GeneratePathRec(pName, fUnicode, fLittleEndian, abTmpRec);
4693 cbToCopy = RT_MIN(cbBuf, cbToCopy - offInRec);
4694 memcpy(pbBuf, &abTmpRec[offInRec], cbToCopy);
4695 return (uint32_t)cbToCopy;
4696}
4697
4698
4699/**
4700 * Generate path table records.
4701 *
4702 * This will generate record up to the end of the table. However, it will not
4703 * supply the zero padding in the last sector, the caller is expected to take
4704 * care of that.
4705 *
4706 * @returns Number of bytes written to the buffer.
4707 * @param ppDirHint Pointer to the directory hint for the namespace.
4708 * @param pFinalizedDirs The finalized directory data for the namespace.
4709 * @param fUnicode Set if the name should be translated to big endian
4710 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4711 * @param fLittleEndian Set if we're generating little endian records, clear
4712 * if big endian records.
4713 * @param offInTable Offset into the path table.
4714 * @param pbBuf The output buffer.
4715 * @param cbBuf The buffer size.
4716 */
4717static size_t rtFsIsoMakerOutFile_ReadPathTable(PRTFSISOMAKERNAMEDIR *ppDirHint, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
4718 bool fUnicode, bool fLittleEndian, uint32_t offInTable,
4719 uint8_t *pbBuf, size_t cbBuf)
4720{
4721 /*
4722 * Figure out which directory to start with. We keep a hint in the instance.
4723 */
4724 PRTFSISOMAKERNAMEDIR pDir = *ppDirHint;
4725 if (!pDir)
4726 {
4727 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4728 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4729 }
4730 if (offInTable - pDir->offPathTable < RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec))
4731 { /* hit */ }
4732 /* Seek forwards: */
4733 else if (offInTable > pDir->offPathTable)
4734 do
4735 {
4736 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4737 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4738 } while (offInTable - pDir->offPathTable >= RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec));
4739 /* Back to the start: */
4740 else if (offInTable == 0)
4741 {
4742 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4743 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4744 }
4745 /* Seek backwards: */
4746 else
4747 do
4748 {
4749 pDir = RTListGetPrev(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4750 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4751 } while (offInTable - pDir->offPathTable >= RTFSISOMAKER_CALC_PATHREC_SIZE(pDir->pName->cbNameInDirRec));
4752
4753 /*
4754 * Generate content.
4755 */
4756 size_t cbDone = 0;
4757 while ( cbBuf > 0
4758 && pDir)
4759 {
4760 PRTFSISOMAKERNAME pName = pDir->pName;
4761 uint8_t cbRec = RTFSISOMAKER_CALC_PATHREC_SIZE(pName->cbNameInDirRec);
4762 uint32_t cbCopied;
4763 if ( offInTable == pDir->offPathTable
4764 && cbBuf >= cbRec + fUnicode * 2U)
4765 cbCopied = rtFsIsoMakerOutFile_GeneratePathRec(pName, fUnicode, fLittleEndian, pbBuf);
4766 else
4767 cbCopied = rtFsIsoMakerOutFile_GeneratePathRecPartial(pName, fUnicode, fLittleEndian,
4768 offInTable - pDir->offPathTable, pbBuf, cbBuf);
4769 cbDone += cbCopied;
4770 offInTable += cbCopied;
4771 pbBuf += cbCopied;
4772 cbBuf -= cbCopied;
4773 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4774 }
4775
4776 /*
4777 * Update the hint.
4778 */
4779 *ppDirHint = pDir;
4780
4781 return cbDone;
4782}
4783
4784
4785/**
4786 * Generates ISO-9660 directory record into the specified buffer.
4787 *
4788 * @returns Number of bytes copied into the buffer.
4789 * @param pName The namespace node.
4790 * @param fUnicode Set if the name should be translated to big endian
4791 * UTF-16BE / UCS-2BE, i.e. we're in the joliet namespace.
4792 * @param pbBuf The buffer. This is at least pName->cbDirRec bytes big.
4793 */
4794static uint32_t rtFsIsoMakerOutFile_GenerateDirRec(PRTFSISOMAKERNAME pName, bool fUnicode, uint8_t *pbBuf)
4795{
4796 /*
4797 * Emit a standard ISO-9660 directory record.
4798 */
4799 PISO9660DIRREC pDirRec = (PISO9660DIRREC)pbBuf;
4800 PCRTFSISOMAKEROBJ pObj = pName->pObj;
4801 PCRTFSISOMAKERNAMEDIR pDir = pName->pDir;
4802 if (pDir)
4803 {
4804 pDirRec->offExtent.be = RT_H2BE_U32(pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4805 pDirRec->offExtent.le = RT_H2LE_U32(pDir->offDir / RTFSISOMAKER_SECTOR_SIZE);
4806 pDirRec->cbData.be = RT_H2BE_U32(pDir->cbDir);
4807 pDirRec->cbData.le = RT_H2LE_U32(pDir->cbDir);
4808 pDirRec->fFileFlags = ISO9660_FILE_FLAGS_DIRECTORY;
4809 }
4810 else if (pObj->enmType == RTFSISOMAKEROBJTYPE_FILE)
4811 {
4812 PRTFSISOMAKERFILE pFile = (PRTFSISOMAKERFILE)pObj;
4813 pDirRec->offExtent.be = RT_H2BE_U32(pFile->offData / RTFSISOMAKER_SECTOR_SIZE);
4814 pDirRec->offExtent.le = RT_H2LE_U32(pFile->offData / RTFSISOMAKER_SECTOR_SIZE);
4815 pDirRec->cbData.be = RT_H2BE_U32(pFile->cbData);
4816 pDirRec->cbData.le = RT_H2LE_U32(pFile->cbData);
4817 pDirRec->fFileFlags = 0;
4818 }
4819 else
4820 {
4821 pDirRec->offExtent.be = 0;
4822 pDirRec->offExtent.le = 0;
4823 pDirRec->cbData.be = 0;
4824 pDirRec->cbData.le = 0;
4825 pDirRec->fFileFlags = 0;
4826 }
4827 rtFsIsoMakerTimespecToIso9660RecTimestamp(&pObj->BirthTime, &pDirRec->RecTime);
4828
4829 pDirRec->cbDirRec = pName->cbDirRec;
4830 pDirRec->cExtAttrBlocks = 0;
4831 pDirRec->bFileUnitSize = 0;
4832 pDirRec->bInterleaveGapSize = 0;
4833 pDirRec->VolumeSeqNo.be = RT_H2BE_U16_C(1);
4834 pDirRec->VolumeSeqNo.le = RT_H2LE_U16_C(1);
4835 pDirRec->bFileIdLength = pName->cbNameInDirRec;
4836
4837 if (!fUnicode)
4838 {
4839 memcpy(&pDirRec->achFileId[0], pName->szName, pName->cbNameInDirRec);
4840 if (!(pName->cbNameInDirRec & 1))
4841 pDirRec->achFileId[pName->cbNameInDirRec] = '\0';
4842 }
4843 else
4844 {
4845 /* Convert to big endian UTF-16. We're using a separate buffer here
4846 because of zero terminator (none in pDirRec) and misalignment. */
4847 RTUTF16 wszTmp[128];
4848 PRTUTF16 pwszTmp = &wszTmp[0];
4849 size_t cwcResult = 0;
4850 int rc = RTStrToUtf16BigEx(pName->szName, RTSTR_MAX, &pwszTmp, RT_ELEMENTS(wszTmp), &cwcResult);
4851 AssertRC(rc);
4852 Assert( cwcResult * sizeof(RTUTF16) == pName->cbNameInDirRec
4853 || (!pName->pParent && cwcResult == 0 && pName->cbNameInDirRec == 1) );
4854 memcpy(&pDirRec->achFileId[0], pwszTmp, pName->cbNameInDirRec);
4855 pDirRec->achFileId[pName->cbNameInDirRec] = '\0';
4856 }
4857
4858 /*
4859 * Rock ridge fields if enabled.
4860 */
4861 /** @todo rock ridge. */
4862
4863 return pName->cbDirRec;
4864}
4865
4866
4867/**
4868 * Deals with situations where the destination buffer doesn't cover the whole
4869 * directory record.
4870 *
4871 * @returns Number of bytes copied into the buffer.
4872 * @param pName The namespace node.
4873 * @param fUnicode Set if the name should be translated to big endian
4874 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4875 * @param off The offset into the directory record.
4876 * @param pbBuf The buffer.
4877 * @param cbBuf The buffer size.
4878 */
4879static uint32_t rtFsIsoMakerOutFile_GenerateDirRecPartial(PRTFSISOMAKERNAME pName, bool fUnicode,
4880 uint32_t off, uint8_t *pbBuf, size_t cbBuf)
4881{
4882 Assert(off < pName->cbDirRec);
4883
4884 uint8_t abTmpBuf[256];
4885 size_t cbToCopy = rtFsIsoMakerOutFile_GenerateDirRec(pName, fUnicode, pbBuf);
4886 cbToCopy = RT_MIN(cbBuf, cbToCopy - off);
4887 memcpy(pbBuf, &abTmpBuf[off], cbToCopy);
4888 return (uint32_t)cbToCopy;
4889}
4890
4891
4892/**
4893 * Generate a '.' or '..' directory record.
4894 *
4895 * This is the same as rtFsIsoMakerOutFile_GenerateDirRec, but with the filename
4896 * reduced to 1 byte.
4897 *
4898 * @returns Number of bytes copied into the buffer.
4899 * @param pName The directory namespace node.
4900 * @param fUnicode Set if the name should be translated to big endian
4901 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4902 * @param bDirId The directory ID (0x00 or 0x01).
4903 * @param off The offset into the directory record.
4904 * @param pbBuf The buffer.
4905 * @param cbBuf The buffer size.
4906 */
4907static uint32_t rtFsIsoMakerOutFile_GenerateSpecialDirRec(PRTFSISOMAKERNAME pName, bool fUnicode, uint8_t bDirId,
4908 uint32_t off, uint8_t *pbBuf, size_t cbBuf)
4909{
4910 Assert(off < pName->cbDirRec);
4911 Assert(pName->pDir);
4912
4913 /* Generate a regular directory record. */
4914 uint8_t abTmpBuf[256];
4915 size_t cbToCopy = rtFsIsoMakerOutFile_GenerateDirRec(pName, fUnicode, abTmpBuf);
4916
4917 /* Replace the filename part. */
4918 PISO9660DIRREC pDirRec = (PISO9660DIRREC)abTmpBuf;
4919 if (pDirRec->bFileIdLength != 1)
4920 {
4921 uint8_t offSysUse = pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1) + RT_OFFSETOF(ISO9660DIRREC, achFileId);
4922 uint8_t cbSysUse = pDirRec->cbDirRec - offSysUse;
4923 if (cbSysUse > 0)
4924 memmove(&pDirRec->achFileId[1], &pbBuf[offSysUse], cbSysUse);
4925 pDirRec->bFileIdLength = 1;
4926 cbToCopy = RT_OFFSETOF(ISO9660DIRREC, achFileId) + 1 + cbSysUse;
4927 pDirRec->cbDirRec = (uint8_t)cbToCopy;
4928 }
4929 pDirRec->achFileId[0] = bDirId;
4930
4931 /* Do the copying. */
4932 cbToCopy = RT_MIN(cbBuf, cbToCopy - off);
4933 memcpy(pbBuf, &abTmpBuf[off], cbToCopy);
4934 return (uint32_t)cbToCopy;
4935}
4936
4937
4938/**
4939 * Read directory records.
4940 *
4941 * This locates the directory at @a offUnsigned and generates directory records
4942 * for it. Caller must repeat the call to get directory entries for the next
4943 * directory should there be desire for that.
4944 *
4945 * @returns Number of bytes copied into @a pbBuf.
4946 * @param ppDirHint Pointer to the directory hint for the namespace.
4947 * @param pIsoMaker The ISO maker instance.
4948 * @param pFinalizedDirs The finalized directory data for the namespace.
4949 * @param fUnicode Set if the name should be translated to big endian
4950 * UTF-16 / UCS-2, i.e. we're in the joliet namespace.
4951 * @param offUnsigned The ISO image byte offset of the requested data.
4952 * @param pbBuf The output buffer.
4953 * @param cbBuf How much to read.
4954 */
4955static size_t rtFsIsoMakerOutFile_ReadDirRecords(PRTFSISOMAKERNAMEDIR *ppDirHint, PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
4956 bool fUnicode, uint64_t offUnsigned, uint8_t *pbBuf, size_t cbBuf)
4957{
4958 /*
4959 * Figure out which directory. We keep a hint in the instance.
4960 */
4961 uint64_t offInDir64;
4962 PRTFSISOMAKERNAMEDIR pDir = *ppDirHint;
4963 if (!pDir)
4964 {
4965 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4966 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4967 }
4968 if ((offInDir64 = offUnsigned - pDir->offDir) < RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE))
4969 { /* hit */ }
4970 /* Seek forwards: */
4971 else if (offUnsigned > pDir->offDir)
4972 do
4973 {
4974 pDir = RTListGetNext(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4975 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4976 } while ((offInDir64 = offUnsigned - pDir->offDir) >= RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE));
4977 /* Back to the start: */
4978 else if (pFinalizedDirs->offDirs / RTFSISOMAKER_SECTOR_SIZE == offUnsigned / RTFSISOMAKER_SECTOR_SIZE)
4979 {
4980 pDir = RTListGetFirst(&pFinalizedDirs->FinalizedDirs, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4981 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4982 }
4983 /* Seek backwards: */
4984 else
4985 do
4986 {
4987 pDir = RTListGetPrev(&pFinalizedDirs->FinalizedDirs, pDir, RTFSISOMAKERNAMEDIR, FinalizedEntry);
4988 AssertReturnStmt(pDir, *pbBuf = 0xff, 1);
4989 } while ((offInDir64 = offUnsigned - pDir->offDir) >= RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE));
4990
4991 /*
4992 * Update the hint.
4993 */
4994 *ppDirHint = pDir;
4995
4996 /*
4997 * Generate content.
4998 */
4999 size_t cbDone = 0;
5000 uint32_t offInDir = (uint32_t)offInDir64;
5001 if (offInDir < pDir->cbDir)
5002 {
5003 PRTFSISOMAKERNAME pDirName = pDir->pName;
5004 PRTFSISOMAKERNAME pParentName = pDirName->pParent ? pDirName->pParent : pDirName;
5005 uint32_t cbSpecialRecs = (uint32_t)pDir->cbDirRec00 + pDir->cbDirRec01;
5006
5007 /*
5008 * Special '.' and/or '..' entries requested.
5009 */
5010 uint32_t iChild;
5011 if (offInDir < cbSpecialRecs)
5012 {
5013 /* do '.' */
5014 if (offInDir < pDir->cbDirRec00)
5015 {
5016 uint32_t cbCopied = rtFsIsoMakerOutFile_GenerateSpecialDirRec(pDirName, fUnicode, 0, offInDir, pbBuf, cbBuf);
5017 cbDone += cbCopied;
5018 offInDir += cbCopied;
5019 pbBuf += cbCopied;
5020 cbBuf -= cbCopied;
5021 }
5022
5023 /* do '..' */
5024 if (cbBuf > 0)
5025 {
5026 uint32_t cbCopied = rtFsIsoMakerOutFile_GenerateSpecialDirRec(pParentName, fUnicode, 1,
5027 offInDir - pDir->cbDirRec00, pbBuf, cbBuf);
5028 cbDone += cbCopied;
5029 offInDir += cbCopied;
5030 pbBuf += cbCopied;
5031 cbBuf -= cbCopied;
5032 }
5033
5034 iChild = 0;
5035 }
5036 /*
5037 * Locate the directory entry we should start with. We can do this
5038 * using binary searching on offInDir.
5039 */
5040 else
5041 {
5042 /** @todo binary search */
5043 iChild = 0;
5044 while (iChild < pDir->cChildren)
5045 {
5046 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
5047 if ((offInDir - pChild->offDirRec) < pChild->cbDirRec)
5048 break;
5049 iChild++;
5050 }
5051 AssertReturnStmt(iChild < pDir->cChildren, *pbBuf = 0xff, 1);
5052 }
5053
5054 /*
5055 * Normal directory entries.
5056 */
5057 while ( cbBuf > 0
5058 && iChild < pDir->cChildren)
5059 {
5060 PRTFSISOMAKERNAME pChild = pDir->papChildren[iChild];
5061 uint32_t cbCopied;
5062 if ( offInDir == pChild->offDirRec
5063 && cbBuf >= pChild->cbDirRec)
5064 cbCopied = rtFsIsoMakerOutFile_GenerateDirRec(pChild, fUnicode, pbBuf);
5065 else
5066 cbCopied = rtFsIsoMakerOutFile_GenerateDirRecPartial(pChild, fUnicode, offInDir - pChild->offDirRec, pbBuf, cbBuf);
5067 cbDone += cbCopied;
5068 offInDir += cbCopied;
5069 pbBuf += cbCopied;
5070 cbBuf -= cbCopied;
5071 iChild++;
5072 }
5073
5074 /*
5075 * Check if we're into the zero padding at the end of the directory now.
5076 */
5077 if ( cbBuf > 0
5078 && iChild >= pDir->cChildren)
5079 {
5080 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - (pDir->cbDir & RTFSISOMAKER_SECTOR_OFFSET_MASK));
5081 memset(pbBuf, 0, cbZeros);
5082 cbDone += cbZeros;
5083 }
5084 }
5085 else
5086 {
5087 cbDone = RT_MIN(cbBuf, RT_ALIGN_32(pDir->cbDir, RTFSISOMAKER_SECTOR_SIZE) - offInDir);
5088 memset(pbBuf, 0, cbDone);
5089 }
5090
5091 return cbDone;
5092}
5093
5094
5095/**
5096 * Read directory records or path table records.
5097 *
5098 * Will not necessarily fill the entire buffer. Caller must call again to get
5099 * more.
5100 *
5101 * @returns Number of bytes copied into @a pbBuf.
5102 * @param ppDirHint Pointer to the directory hint for the namespace.
5103 * @param pIsoMaker The ISO maker instance.
5104 * @param pNamespace The namespace.
5105 * @param pFinalizedDirs The finalized directory data for the namespace.
5106 * @param offUnsigned The ISO image byte offset of the requested data.
5107 * @param pbBuf The output buffer.
5108 * @param cbBuf How much to read.
5109 */
5110static size_t rtFsIsoMakerOutFile_ReadDirStructures(PRTFSISOMAKERNAMEDIR *ppDirHint, PRTFSISOMAKERNAMESPACE pNamespace,
5111 PRTFSISOMAKERFINALIZEDDIRS pFinalizedDirs,
5112 uint64_t offUnsigned, uint8_t *pbBuf, size_t cbBuf)
5113{
5114 if (offUnsigned < pFinalizedDirs->offPathTableL)
5115 return rtFsIsoMakerOutFile_ReadDirRecords(ppDirHint, pFinalizedDirs,
5116 pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
5117 offUnsigned, pbBuf, cbBuf);
5118
5119 uint64_t offInTable;
5120 if ((offInTable = offUnsigned - pFinalizedDirs->offPathTableL) < pFinalizedDirs->cbPathTable)
5121 return rtFsIsoMakerOutFile_ReadPathTable(ppDirHint, pFinalizedDirs,
5122 pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
5123 true /*fLittleEndian*/, (uint32_t)offInTable, pbBuf, cbBuf);
5124
5125 if ((offInTable = offUnsigned - pFinalizedDirs->offPathTableM) < pFinalizedDirs->cbPathTable)
5126 return rtFsIsoMakerOutFile_ReadPathTable(ppDirHint, pFinalizedDirs,
5127 pNamespace->fNamespace == RTFSISOMAKER_NAMESPACE_JOLIET,
5128 false /*fLittleEndian*/, (uint32_t)offInTable, pbBuf, cbBuf);
5129
5130 /* ASSUME we're in the zero padding at the end of a path table. */
5131 Assert( offUnsigned - pFinalizedDirs->offPathTableL < RT_ALIGN_32(pFinalizedDirs->cbPathTable, RTFSISOMAKER_SECTOR_SIZE)
5132 || offUnsigned - pFinalizedDirs->offPathTableM < RT_ALIGN_32(pFinalizedDirs->cbPathTable, RTFSISOMAKER_SECTOR_SIZE));
5133 size_t cbZeros = RT_MIN(cbBuf, RTFSISOMAKER_SECTOR_SIZE - ((size_t)offUnsigned & RTFSISOMAKER_SECTOR_OFFSET_MASK));
5134 memset(pbBuf, 0, cbZeros);
5135 return cbZeros;
5136}
5137
5138
5139
5140/**
5141 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
5142 */
5143static DECLCALLBACK(int) rtFsIsoMakerOutFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
5144{
5145 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5146 PRTFSISOMAKERINT pIsoMaker = pThis->pIsoMaker;
5147 size_t cbBuf = pSgBuf->paSegs[0].cbSeg;
5148 uint8_t *pbBuf = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
5149
5150 Assert(pSgBuf->cSegs == 1);
5151 RT_NOREF(fBlocking);
5152
5153 /*
5154 * Process the offset, checking for end-of-file.
5155 */
5156 uint64_t offUnsigned;
5157 if (off < 0)
5158 offUnsigned = pThis->offCurPos;
5159 else
5160 offUnsigned = (uint64_t)off;
5161 if (offUnsigned >= pIsoMaker->cbFinalizedImage)
5162 {
5163 if (*pcbRead)
5164 {
5165 *pcbRead = 0;
5166 return VINF_EOF;
5167 }
5168 return VERR_EOF;
5169 }
5170 if ( !pcbRead
5171 && pIsoMaker->cbFinalizedImage - offUnsigned < cbBuf)
5172 return VERR_EOF;
5173
5174 /*
5175 * Produce the bytes.
5176 */
5177 int rc = VINF_SUCCESS;
5178 size_t cbRead = 0;
5179 while (cbBuf > 0)
5180 {
5181 size_t cbDone;
5182
5183 /* Betting on there being more file data than metadata, thus doing the
5184 offset switch in decending order. */
5185 if (offUnsigned >= pIsoMaker->offFirstFile)
5186 {
5187 if (offUnsigned < pIsoMaker->cbFinalizedImage)
5188 {
5189 rc = rtFsIsoMakerOutFile_ReadFileData(pThis, pIsoMaker, offUnsigned, pbBuf, cbBuf, &cbDone);
5190 if (RT_FAILURE(rc))
5191 break;
5192 }
5193 else
5194 {
5195 rc = pcbRead ? VINF_EOF : VERR_EOF;
5196 break;
5197 }
5198 }
5199 /*
5200 * Joliet directory structures.
5201 */
5202 else if ( offUnsigned >= pIsoMaker->JolietDirs.offDirs
5203 && pIsoMaker->JolietDirs.offDirs < pIsoMaker->JolietDirs.offPathTableL)
5204 cbDone = rtFsIsoMakerOutFile_ReadDirStructures(&pThis->pDirHintJoliet, &pIsoMaker->Joliet, &pIsoMaker->JolietDirs,
5205 offUnsigned, pbBuf, cbBuf);
5206 /*
5207 * Primary ISO directory structures.
5208 */
5209 else if (offUnsigned >= pIsoMaker->PrimaryIsoDirs.offDirs)
5210 cbDone = rtFsIsoMakerOutFile_ReadDirStructures(&pThis->pDirHintPrimaryIso, &pIsoMaker->PrimaryIso,
5211 &pIsoMaker->PrimaryIsoDirs, offUnsigned, pbBuf, cbBuf);
5212 /** @todo Insert El Torito stuff here? Probably okay to let it be in the file
5213 * area, right? */
5214 /*
5215 * Volume descriptors.
5216 */
5217 else if (offUnsigned >= _32K)
5218 {
5219 size_t offVolDescs = (size_t)offUnsigned - _32K;
5220 cbDone = RT_MIN(cbBuf, (pIsoMaker->cVolumeDescriptors * RTFSISOMAKER_SECTOR_SIZE) - offVolDescs);
5221 memcpy(pbBuf, &pIsoMaker->pbVolDescs[offVolDescs], cbDone);
5222 }
5223 /*
5224 * Zeros in the system area.
5225 */
5226 else if (offUnsigned >= pIsoMaker->cbSysArea)
5227 {
5228 cbDone = RT_MIN(cbBuf, _32K - (size_t)offUnsigned);
5229 memset(pbBuf, 0, cbDone);
5230 }
5231 /*
5232 * Actual data in the system area.
5233 */
5234 else
5235 {
5236 cbDone = RT_MIN(cbBuf, pIsoMaker->cbSysArea - (size_t)offUnsigned);
5237 memcpy(pbBuf, &pIsoMaker->pbSysArea[(size_t)offUnsigned], cbDone);
5238 }
5239
5240 /*
5241 * Common advance.
5242 */
5243 cbRead += cbDone;
5244 offUnsigned += cbDone;
5245 pbBuf += cbDone;
5246 cbBuf -= cbDone;
5247 }
5248
5249 if (pcbRead)
5250 *pcbRead = cbRead;
5251 return rc;
5252}
5253
5254
5255/**
5256 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
5257 */
5258static DECLCALLBACK(int) rtFsIsoMakerOutFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
5259{
5260 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
5261 return VERR_WRITE_PROTECT;
5262}
5263
5264
5265/**
5266 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
5267 */
5268static DECLCALLBACK(int) rtFsIsoMakerOutFile_Flush(void *pvThis)
5269{
5270 RT_NOREF(pvThis);
5271 return VINF_SUCCESS;
5272}
5273
5274
5275/**
5276 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
5277 */
5278static DECLCALLBACK(int) rtFsIsoMakerOutFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
5279 uint32_t *pfRetEvents)
5280{
5281 NOREF(pvThis);
5282 return RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
5283}
5284
5285
5286/**
5287 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
5288 */
5289static DECLCALLBACK(int) rtFsIsoMakerOutFile_Tell(void *pvThis, PRTFOFF poffActual)
5290{
5291 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5292 *poffActual = pThis->offCurPos;
5293 return VINF_SUCCESS;
5294}
5295
5296
5297/**
5298 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
5299 */
5300static DECLCALLBACK(int) rtFsIsoMakerOutFile_Skip(void *pvThis, RTFOFF cb)
5301{
5302 RTFOFF offIgnored;
5303 return rtFsIsoMakerOutFile_Seek(pvThis, cb, RTFILE_SEEK_CURRENT, &offIgnored);
5304}
5305
5306
5307/**
5308 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
5309 */
5310static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
5311{
5312 RT_NOREF(pvThis, fMode, fMask);
5313 return VERR_WRITE_PROTECT;
5314}
5315
5316
5317/**
5318 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
5319 */
5320static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
5321 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
5322{
5323 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
5324 return VERR_WRITE_PROTECT;
5325}
5326
5327
5328/**
5329 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
5330 */
5331static DECLCALLBACK(int) rtFsIsoMakerOutFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
5332{
5333 RT_NOREF(pvThis, uid, gid);
5334 return VERR_WRITE_PROTECT;
5335}
5336
5337
5338/**
5339 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
5340 */
5341static DECLCALLBACK(int) rtFsIsoMakerOutFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
5342{
5343 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5344
5345 /*
5346 * Seek relative to which position.
5347 */
5348 uint64_t offWrt;
5349 switch (uMethod)
5350 {
5351 case RTFILE_SEEK_BEGIN:
5352 offWrt = 0;
5353 break;
5354
5355 case RTFILE_SEEK_CURRENT:
5356 offWrt = pThis->offCurPos;
5357 break;
5358
5359 case RTFILE_SEEK_END:
5360 offWrt = pThis->pIsoMaker->cbFinalizedImage;
5361 break;
5362
5363 default:
5364 return VERR_INTERNAL_ERROR_5;
5365 }
5366
5367 /*
5368 * Calc new position, take care to stay within RTFOFF type bounds.
5369 */
5370 uint64_t offNew;
5371 if (offSeek == 0)
5372 offNew = offWrt;
5373 else if (offSeek > 0)
5374 {
5375 offNew = offWrt + offSeek;
5376 if ( offNew < offWrt
5377 || offNew > RTFOFF_MAX)
5378 offNew = RTFOFF_MAX;
5379 }
5380 else if ((uint64_t)-offSeek < offWrt)
5381 offNew = offWrt + offSeek;
5382 else
5383 offNew = 0;
5384 pThis->offCurPos = offNew;
5385
5386 *poffActual = offNew;
5387 return VINF_SUCCESS;
5388}
5389
5390
5391/**
5392 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
5393 */
5394static DECLCALLBACK(int) rtFsIsoMakerOutFile_QuerySize(void *pvThis, uint64_t *pcbFile)
5395{
5396 PRTFSISOMAKEROUTPUTFILE pThis = (PRTFSISOMAKEROUTPUTFILE)pvThis;
5397 *pcbFile = pThis->pIsoMaker->cbFinalizedImage;
5398 return VINF_SUCCESS;
5399}
5400
5401
5402/**
5403 * Standard file operations.
5404 */
5405DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIsoMakerOutputFileOps =
5406{
5407 { /* Stream */
5408 { /* Obj */
5409 RTVFSOBJOPS_VERSION,
5410 RTVFSOBJTYPE_FILE,
5411 "ISO Maker Output File",
5412 rtFsIsoMakerOutFile_Close,
5413 rtFsIsoMakerOutFile_QueryInfo,
5414 RTVFSOBJOPS_VERSION
5415 },
5416 RTVFSIOSTREAMOPS_VERSION,
5417 RTVFSIOSTREAMOPS_FEAT_NO_SG,
5418 rtFsIsoMakerOutFile_Read,
5419 rtFsIsoMakerOutFile_Write,
5420 rtFsIsoMakerOutFile_Flush,
5421 rtFsIsoMakerOutFile_PollOne,
5422 rtFsIsoMakerOutFile_Tell,
5423 rtFsIsoMakerOutFile_Skip,
5424 NULL /*ZeroFill*/,
5425 RTVFSIOSTREAMOPS_VERSION,
5426 },
5427 RTVFSFILEOPS_VERSION,
5428 0,
5429 { /* ObjSet */
5430 RTVFSOBJSETOPS_VERSION,
5431 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
5432 rtFsIsoMakerOutFile_SetMode,
5433 rtFsIsoMakerOutFile_SetTimes,
5434 rtFsIsoMakerOutFile_SetOwner,
5435 RTVFSOBJSETOPS_VERSION
5436 },
5437 rtFsIsoMakerOutFile_Seek,
5438 rtFsIsoMakerOutFile_QuerySize,
5439 RTVFSFILEOPS_VERSION
5440};
5441
5442
5443
5444/**
5445 * Creates a VFS file for a finalized ISO maker instanced.
5446 *
5447 * The file can be used to access the image. Both sequential and random access
5448 * are supported, so that this could in theory be hooked up to a CD/DVD-ROM
5449 * drive emulation and used as a virtual ISO image.
5450 *
5451 * @returns IRPT status code.
5452 * @param hIsoMaker The ISO maker handle.
5453 * @param phVfsFile Where to return the handle.
5454 */
5455RTDECL(int) RTFsIsoMakerCreateVfsOutputFile(RTFSISOMAKER hIsoMaker, PRTVFSFILE phVfsFile)
5456{
5457 PRTFSISOMAKERINT pThis = hIsoMaker;
5458 RTFSISOMAKER_ASSERT_VALID_HANDLE_RET(pThis);
5459 AssertReturn(pThis->fFinalized, VERR_WRONG_ORDER);
5460 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
5461
5462 uint32_t cRefs = RTFsIsoMakerRetain(pThis);
5463 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5464
5465 PRTFSISOMAKEROUTPUTFILE pFileData;
5466 RTVFSFILE hVfsFile;
5467 int rc = RTVfsNewFile(&g_rtFsIsoMakerOutputFileOps, sizeof(*pFileData), RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_CREATE,
5468 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pFileData);
5469 if (RT_SUCCESS(rc))
5470 {
5471 pFileData->pIsoMaker = pThis;
5472 pFileData->offCurPos = 0;
5473 pFileData->pFileHint = NULL;
5474 pFileData->hVfsSrcFile = NIL_RTVFSFILE;
5475 pFileData->pDirHintPrimaryIso = NULL;
5476 pFileData->pDirHintJoliet = NULL;
5477 *phVfsFile = hVfsFile;
5478 return VINF_SUCCESS;
5479 }
5480
5481 RTFsIsoMakerRelease(pThis);
5482 *phVfsFile = NIL_RTVFSFILE;
5483 return rc;
5484}
5485
Note: See TracBrowser for help on using the repository browser.

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