VirtualBox

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

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

IPRT: More ISO maker code.

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

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