VirtualBox

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

Last change on this file was 103360, checked in by vboxsync, 3 months ago

IPRT/isomaker.cpp: Zero terminate wszTmp in rtFsIsoMakerOutFile_GenerateDirRec to be on the safe side and make parfait happy. bugref:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use