VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/isomakerimport.cpp@ 94272

最後變更 在這個檔案從94272是 93350,由 vboxsync 提交於 3 年 前

IPRT/isomaker: Don't freak out if the joliet volume descriptor gives the VolumeSpaceSize as smaller than the primary one, as happens with ubuntu-21.10-desktop-amd64.iso.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 131.1 KB
 
1/* $Id: isomakerimport.cpp 93350 2022-01-19 21:53:56Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker, Import Existing Image.
4 */
5
6/*
7 * Copyright (C) 2017-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/avl.h>
36#include <iprt/asm.h>
37#include <iprt/assert.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/string.h>
45#include <iprt/utf16.h>
46#include <iprt/vfs.h>
47#include <iprt/formats/iso9660.h>
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53/** Max directory depth. */
54#define RTFSISOMK_IMPORT_MAX_DEPTH 32
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * Block to file translation node.
62 */
63typedef struct RTFSISOMKIMPBLOCK2FILE
64{
65 /** AVL tree node containing the first block number of the file.
66 * Block number is relative to the start of the import image. */
67 AVLU32NODECORE Core;
68 /** The configuration index of the file. */
69 uint32_t idxObj;
70 /** Namespaces the file has been seen in already (RTFSISOMAKER_NAMESPACE_XXX). */
71 uint32_t fNamespaces;
72 /** Pointer to the next file with the same block number. */
73 struct RTFSISOMKIMPBLOCK2FILE *pNext;
74} RTFSISOMKIMPBLOCK2FILE;
75/** Pointer to a block-2-file translation node. */
76typedef RTFSISOMKIMPBLOCK2FILE *PRTFSISOMKIMPBLOCK2FILE;
77
78
79/**
80 * Directory todo list entry.
81 */
82typedef struct RTFSISOMKIMPDIR
83{
84 /** List stuff. */
85 RTLISTNODE Entry;
86 /** The directory configuration index with hIsoMaker. */
87 uint32_t idxObj;
88 /** The directory data block number. */
89 uint32_t offDirBlock;
90 /** The directory size (in bytes). */
91 uint32_t cbDir;
92 /** The depth of this directory. */
93 uint8_t cDepth;
94} RTFSISOMKIMPDIR;
95/** Pointer to a directory todo list entry. */
96typedef RTFSISOMKIMPDIR *PRTFSISOMKIMPDIR;
97
98
99/**
100 * ISO maker ISO importer state.
101 */
102typedef struct RTFSISOMKIMPORTER
103{
104 /** The destination ISO maker. */
105 RTFSISOMAKER hIsoMaker;
106 /** RTFSISOMK_IMPORT_F_XXX. */
107 uint32_t fFlags;
108 /** The status code of the whole import.
109 * This notes down the first error status. */
110 int rc;
111 /** Pointer to error info return structure. */
112 PRTERRINFO pErrInfo;
113
114 /** The source file. */
115 RTVFSFILE hSrcFile;
116 /** The size of the source file. */
117 uint64_t cbSrcFile;
118 /** The number of 2KB blocks in the source file. */
119 uint64_t cBlocksInSrcFile;
120 /** The import source index of hSrcFile in hIsoMaker. UINT32_MAX till adding
121 * the first file. */
122 uint32_t idxSrcFile;
123
124 /** The root of the tree for converting data block numbers to files
125 * (PRTFSISOMKIMPBLOCK2FILE). This is essential when importing boot files and
126 * the 2nd namespace (joliet, udf, hfs) so that we avoid duplicating data. */
127 AVLU32TREE Block2FileRoot;
128
129 /** The block offset of the primary volume descriptor. */
130 uint32_t offPrimaryVolDesc;
131 /** The primary volume space size in blocks. */
132 uint32_t cBlocksInPrimaryVolumeSpace;
133 /** The primary volume space size in bytes. */
134 uint64_t cbPrimaryVolumeSpace;
135 /** The number of volumes in the set. */
136 uint32_t cVolumesInSet;
137 /** The primary volume sequence ID. */
138 uint32_t idPrimaryVol;
139
140 /** Set if we've already seen a joliet volume descriptor. */
141 bool fSeenJoliet;
142
143 /** The name of the TRANS.TBL in the import media (must ignore). */
144 const char *pszTransTbl;
145
146 /** Pointer to the import results structure (output). */
147 PRTFSISOMAKERIMPORTRESULTS pResults;
148
149 /** Sector buffer for volume descriptors and such. */
150 union
151 {
152 uint8_t ab[ISO9660_SECTOR_SIZE];
153 ISO9660VOLDESCHDR VolDescHdr;
154 ISO9660PRIMARYVOLDESC PrimVolDesc;
155 ISO9660SUPVOLDESC SupVolDesc;
156 ISO9660BOOTRECORDELTORITO ElToritoDesc;
157 } uSectorBuf;
158
159 /** Name buffer. */
160 char szNameBuf[_2K];
161
162 /** A somewhat larger buffer. */
163 uint8_t abBuf[_64K];
164
165 /** @name Rock Ridge stuff
166 * @{ */
167 /** Set if we've see the SP entry. */
168 bool fSuspSeenSP;
169 /** Set if we've seen the last 'NM' entry. */
170 bool fSeenLastNM;
171 /** Set if we've seen the last 'SL' entry. */
172 bool fSeenLastSL;
173 /** The SUSP skip into system area offset. */
174 uint32_t offSuspSkip;
175 /** The source file byte offset of the abRockBuf content. */
176 uint64_t offRockBuf;
177 /** Name buffer for rock ridge. */
178 char szRockNameBuf[_2K];
179 /** Symlink target name buffer for rock ridge. */
180 char szRockSymlinkTargetBuf[_2K];
181 /** A buffer for reading rock ridge continuation blocks into */
182 uint8_t abRockBuf[ISO9660_SECTOR_SIZE];
183 /** @} */
184} RTFSISOMKIMPORTER;
185/** Pointer to an ISO maker ISO importer state. */
186typedef RTFSISOMKIMPORTER *PRTFSISOMKIMPORTER;
187
188
189/*
190 * The following is also found in iso9660vfs.cpp:
191 * The following is also found in iso9660vfs.cpp:
192 * The following is also found in iso9660vfs.cpp:
193 */
194
195/**
196 * Converts a ISO 9660 binary timestamp into an IPRT timesspec.
197 *
198 * @param pTimeSpec Where to return the IRPT time.
199 * @param pIso9660 The ISO 9660 binary timestamp.
200 */
201static void rtFsIsoImpIso9660RecDateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
202{
203 RTTIME Time;
204 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
205 Time.offUTC = 0;
206 Time.i32Year = pIso9660->bYear + 1900;
207 Time.u8Month = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
208 Time.u8MonthDay = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
209 Time.u8WeekDay = UINT8_MAX;
210 Time.u16YearDay = 0;
211 Time.u8Hour = RT_MIN(pIso9660->bHour, 23);
212 Time.u8Minute = RT_MIN(pIso9660->bMinute, 59);
213 Time.u8Second = RT_MIN(pIso9660->bSecond, 59);
214 Time.u32Nanosecond = 0;
215 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
216
217 /* Only apply the UTC offset if it's within reasons. */
218 if (RT_ABS(pIso9660->offUtc) <= 13*4)
219 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
220}
221
222/**
223 * Converts a ISO 9660 char timestamp into an IPRT timesspec.
224 *
225 * @returns true if valid, false if not.
226 * @param pTimeSpec Where to return the IRPT time.
227 * @param pIso9660 The ISO 9660 char timestamp.
228 */
229static bool rtFsIsoImpIso9660DateTime2TimeSpecIfValid(PRTTIMESPEC pTimeSpec, PCISO9660TIMESTAMP pIso9660)
230{
231 if ( RT_C_IS_DIGIT(pIso9660->achYear[0])
232 && RT_C_IS_DIGIT(pIso9660->achYear[1])
233 && RT_C_IS_DIGIT(pIso9660->achYear[2])
234 && RT_C_IS_DIGIT(pIso9660->achYear[3])
235 && RT_C_IS_DIGIT(pIso9660->achMonth[0])
236 && RT_C_IS_DIGIT(pIso9660->achMonth[1])
237 && RT_C_IS_DIGIT(pIso9660->achDay[0])
238 && RT_C_IS_DIGIT(pIso9660->achDay[1])
239 && RT_C_IS_DIGIT(pIso9660->achHour[0])
240 && RT_C_IS_DIGIT(pIso9660->achHour[1])
241 && RT_C_IS_DIGIT(pIso9660->achMinute[0])
242 && RT_C_IS_DIGIT(pIso9660->achMinute[1])
243 && RT_C_IS_DIGIT(pIso9660->achSecond[0])
244 && RT_C_IS_DIGIT(pIso9660->achSecond[1])
245 && RT_C_IS_DIGIT(pIso9660->achCentisecond[0])
246 && RT_C_IS_DIGIT(pIso9660->achCentisecond[1]))
247 {
248
249 RTTIME Time;
250 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
251 Time.offUTC = 0;
252 Time.i32Year = (pIso9660->achYear[0] - '0') * 1000
253 + (pIso9660->achYear[1] - '0') * 100
254 + (pIso9660->achYear[2] - '0') * 10
255 + (pIso9660->achYear[3] - '0');
256 Time.u8Month = (pIso9660->achMonth[0] - '0') * 10
257 + (pIso9660->achMonth[1] - '0');
258 Time.u8MonthDay = (pIso9660->achDay[0] - '0') * 10
259 + (pIso9660->achDay[1] - '0');
260 Time.u8WeekDay = UINT8_MAX;
261 Time.u16YearDay = 0;
262 Time.u8Hour = (pIso9660->achHour[0] - '0') * 10
263 + (pIso9660->achHour[1] - '0');
264 Time.u8Minute = (pIso9660->achMinute[0] - '0') * 10
265 + (pIso9660->achMinute[1] - '0');
266 Time.u8Second = (pIso9660->achSecond[0] - '0') * 10
267 + (pIso9660->achSecond[1] - '0');
268 Time.u32Nanosecond = (pIso9660->achCentisecond[0] - '0') * 10
269 + (pIso9660->achCentisecond[1] - '0');
270 if ( Time.u8Month > 1 && Time.u8Month <= 12
271 && Time.u8MonthDay > 1 && Time.u8MonthDay <= 31
272 && Time.u8Hour < 60
273 && Time.u8Minute < 60
274 && Time.u8Second < 60
275 && Time.u32Nanosecond < 100)
276 {
277 if (Time.i32Year <= 1677)
278 Time.i32Year = 1677;
279 else if (Time.i32Year <= 2261)
280 Time.i32Year = 2261;
281
282 Time.u32Nanosecond *= RT_NS_10MS;
283 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
284
285 /* Only apply the UTC offset if it's within reasons. */
286 if (RT_ABS(pIso9660->offUtc) <= 13*4)
287 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
288 return true;
289 }
290 }
291 return false;
292}
293
294/* end of duplicated static functions. */
295
296
297/**
298 * Wrapper around RTErrInfoSetV.
299 *
300 * @returns rc
301 * @param pThis The importer instance.
302 * @param rc The status code to set.
303 * @param pszFormat The format string detailing the error.
304 * @param va Argument to the format string.
305 */
306static int rtFsIsoImpErrorV(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, va_list va)
307{
308 va_list vaCopy;
309 va_copy(vaCopy, va);
310 LogRel(("RTFsIsoMkImport error %Rrc: %N\n", rc, pszFormat, &vaCopy));
311 va_end(vaCopy);
312
313 if (RT_SUCCESS(pThis->rc))
314 {
315 pThis->rc = rc;
316 rc = RTErrInfoSetV(pThis->pErrInfo, rc, pszFormat, va);
317 }
318
319 pThis->pResults->cErrors++;
320 return rc;
321}
322
323
324/**
325 * Wrapper around RTErrInfoSetF.
326 *
327 * @returns rc
328 * @param pThis The importer instance.
329 * @param rc The status code to set.
330 * @param pszFormat The format string detailing the error.
331 * @param ... Argument to the format string.
332 */
333static int rtFsIsoImpError(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, ...)
334{
335 va_list va;
336 va_start(va, pszFormat);
337 rc = rtFsIsoImpErrorV(pThis, rc, pszFormat, va);
338 va_end(va);
339 return rc;
340}
341
342
343/**
344 * Callback for destroying a RTFSISOMKIMPBLOCK2FILE node.
345 *
346 * @returns VINF_SUCCESS
347 * @param pNode The node to destroy.
348 * @param pvUser Ignored.
349 */
350static DECLCALLBACK(int) rtFsIsoMakerImportDestroyData2File(PAVLU32NODECORE pNode, void *pvUser)
351{
352 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)pNode;
353 if (pBlock2File)
354 {
355 PRTFSISOMKIMPBLOCK2FILE pNext;
356 while ((pNext = pBlock2File->pNext) != NULL)
357 {
358 pBlock2File->pNext = pNext->pNext;
359 pNext->pNext = NULL;
360 RTMemFree(pNext);
361 }
362 RTMemFree(pNode);
363 }
364
365 RT_NOREF(pvUser);
366 return VINF_SUCCESS;
367}
368
369
370/**
371 * Adds a symbolic link and names it given its ISO-9660 directory record and
372 * parent.
373 *
374 * @returns IPRT status code (safe to ignore).
375 * @param pThis The importer instance.
376 * @param pDirRec The directory record.
377 * @param pObjInfo Object information.
378 * @param fNamespace The namespace flag.
379 * @param idxParent Parent directory.
380 * @param pszName The name.
381 * @param pszRockName The rock ridge name. Empty if not present.
382 * @param pszTarget The symbolic link target.
383 */
384static int rtFsIsoImportProcessIso9660AddAndNameSymlink(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, PRTFSOBJINFO pObjInfo,
385 uint32_t fNamespace, uint32_t idxParent,
386 const char *pszName, const char *pszRockName, const char *pszTarget)
387{
388 NOREF(pDirRec);
389 Assert(!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY));
390 Assert(RTFS_IS_SYMLINK(pObjInfo->Attr.fMode));
391
392 uint32_t idxObj;
393 int rc = RTFsIsoMakerAddUnnamedSymlink(pThis->hIsoMaker, pObjInfo, pszTarget, &idxObj);
394 if (RT_SUCCESS(rc))
395 {
396 Log3((" --> added symlink #%#x (-> %s)\n", idxObj, pszTarget));
397 pThis->pResults->cAddedSymlinks++;
398
399 /*
400 * Enter the object into the namespace.
401 */
402 rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName, true /*fNoNormalize*/);
403 if (RT_SUCCESS(rc))
404 {
405 pThis->pResults->cAddedNames++;
406
407 if (*pszRockName != '\0' && strcmp(pszName, pszRockName) != 0)
408 {
409 rc = RTFsIsoMakerObjSetRockName(pThis->hIsoMaker, idxObj, fNamespace, pszRockName);
410 if (RT_FAILURE(rc))
411 rc = rtFsIsoImpError(pThis, rc, "Error setting rock ridge name for symlink '%s' to '%s'", pszName, pszRockName);
412 }
413 }
414 else
415 rc = rtFsIsoImpError(pThis, rc, "Error naming symlink '%s' (-> %s): %Rrc", pszName, pszTarget, rc);
416 }
417 else
418 rc = rtFsIsoImpError(pThis, rc, "Error adding symbolic link '%s' (-> %s): %Rrc", pszName, pszTarget, rc);
419 return rc;
420}
421
422
423
424/**
425 * Adds a directory and names it given its ISO-9660 directory record and parent.
426 *
427 * @returns IPRT status code (safe to ignore).
428 * @param pThis The importer instance.
429 * @param pDirRec The directory record.
430 * @param pObjInfo Object information.
431 * @param cbData The actual directory data size. (Always same as in the
432 * directory record, but this what we do for files below.)
433 * @param fNamespace The namespace flag.
434 * @param idxParent Parent directory.
435 * @param pszName The name.
436 * @param pszRockName The rock ridge name. Empty if not present.
437 * @param cDepth The depth to add it with.
438 * @param pTodoList The todo list (for directories).
439 */
440static int rtFsIsoImportProcessIso9660AddAndNameDirectory(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec,
441 PCRTFSOBJINFO pObjInfo, uint64_t cbData,
442 uint32_t fNamespace, uint32_t idxParent, const char *pszName,
443 const char *pszRockName, uint8_t cDepth, PRTLISTANCHOR pTodoList)
444{
445 Assert(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY);
446 uint32_t idxObj;
447 int rc = RTFsIsoMakerAddUnnamedDir(pThis->hIsoMaker, pObjInfo, &idxObj);
448 if (RT_SUCCESS(rc))
449 {
450 Log3((" --> added directory #%#x\n", idxObj));
451 pThis->pResults->cAddedDirs++;
452
453 /*
454 * Enter the object into the namespace.
455 */
456 rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName, true /*fNoNormalize*/);
457 if (RT_SUCCESS(rc))
458 {
459 pThis->pResults->cAddedNames++;
460
461 if (*pszRockName != '\0' && strcmp(pszName, pszRockName) != 0)
462 rc = RTFsIsoMakerObjSetRockName(pThis->hIsoMaker, idxObj, fNamespace, pszRockName);
463 if (RT_SUCCESS(rc))
464 {
465 /*
466 * Push it onto the traversal stack.
467 */
468 PRTFSISOMKIMPDIR pImpDir = (PRTFSISOMKIMPDIR)RTMemAlloc(sizeof(*pImpDir));
469 if (pImpDir)
470 {
471 Assert((uint32_t)cbData == cbData /* no multi-extents for dirs makes it this far */);
472 pImpDir->cbDir = (uint32_t)cbData;
473 pImpDir->offDirBlock = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
474 pImpDir->idxObj = idxObj;
475 pImpDir->cDepth = cDepth;
476 RTListAppend(pTodoList, &pImpDir->Entry);
477 }
478 else
479 rc = rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPDIR");
480 }
481 else
482 rc = rtFsIsoImpError(pThis, rc, "Error setting rock ridge name for directory '%s' to '%s'", pszName, pszRockName);
483 }
484 else
485 rc = rtFsIsoImpError(pThis, rc, "Error naming directory '%s': %Rrc", pszName, rc);
486 }
487 else
488 rc = rtFsIsoImpError(pThis, rc, "Error adding directory '%s': %Rrc", pszName, rc);
489 return rc;
490}
491
492
493/**
494 * Adds a file and names it given its ISO-9660 directory record and parent.
495 *
496 * @returns IPRT status code (safe to ignore).
497 * @param pThis The importer instance.
498 * @param pDirRec The directory record.
499 * @param pObjInfo Object information.
500 * @param cbData The actual file data size.
501 * @param fNamespace The namespace flag.
502 * @param idxParent Parent directory.
503 * @param pszName The name.
504 * @param pszRockName The rock ridge name. Empty if not present.
505 */
506static int rtFsIsoImportProcessIso9660AddAndNameFile(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, PRTFSOBJINFO pObjInfo,
507 uint64_t cbData, uint32_t fNamespace, uint32_t idxParent,
508 const char *pszName, const char *pszRockName)
509{
510 int rc;
511
512 /*
513 * First we must make sure the common source file has been added.
514 */
515 if (pThis->idxSrcFile != UINT32_MAX)
516 { /* likely */ }
517 else
518 {
519 rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
520 if (RT_FAILURE(rc))
521 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
522 Assert(pThis->idxSrcFile != UINT32_MAX);
523 }
524
525 /*
526 * Lookup the data block if the file has a non-zero length. The aim is to
527 * find files across namespaces while bearing in mind that files in the same
528 * namespace may share data storage, i.e. what in a traditional unix file
529 * system would be called hardlinked. Problem is that the core engine doesn't
530 * do hardlinking yet and assume each file has exactly one name per namespace.
531 */
532 uint32_t idxObj = UINT32_MAX;
533 PRTFSISOMKIMPBLOCK2FILE pBlock2File = NULL;
534 PRTFSISOMKIMPBLOCK2FILE pBlock2FilePrev = NULL;
535 if (cbData > 0) /* no data tracking for zero byte files */
536 {
537 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, ISO9660_GET_ENDIAN(&pDirRec->offExtent));
538 if (pBlock2File)
539 {
540 if (!(pBlock2File->fNamespaces & fNamespace))
541 {
542 pBlock2File->fNamespaces |= fNamespace;
543 idxObj = pBlock2File->idxObj;
544 }
545 else
546 {
547 do
548 {
549 pBlock2FilePrev = pBlock2File;
550 pBlock2File = pBlock2File->pNext;
551 } while (pBlock2File && (pBlock2File->fNamespaces & fNamespace));
552 if (pBlock2File)
553 {
554 pBlock2File->fNamespaces |= fNamespace;
555 idxObj = pBlock2File->idxObj;
556 }
557 }
558 }
559 }
560
561 /*
562 * If the above lookup didn't succeed, add a new file with a lookup record.
563 */
564 if (idxObj == UINT32_MAX)
565 {
566 pObjInfo->cbObject = pObjInfo->cbAllocated = cbData;
567 rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
568 ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)ISO9660_SECTOR_SIZE,
569 cbData, pObjInfo, &idxObj);
570 if (RT_FAILURE(rc))
571 return rtFsIsoImpError(pThis, rc, "Error adding file '%s': %Rrc", pszName, rc);
572 Assert(idxObj != UINT32_MAX);
573
574 /* Update statistics. */
575 pThis->pResults->cAddedFiles++;
576 if (cbData > 0)
577 {
578 pThis->pResults->cbAddedDataBlocks += RT_ALIGN_64(cbData, ISO9660_SECTOR_SIZE);
579
580 /* Lookup record. */
581 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTMemAlloc(sizeof(*pBlock2File));
582 AssertReturn(pBlock2File, rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPBLOCK2FILE"));
583
584 pBlock2File->idxObj = idxObj;
585 pBlock2File->Core.Key = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
586 pBlock2File->fNamespaces = fNamespace;
587 pBlock2File->pNext = NULL;
588 if (!pBlock2FilePrev)
589 {
590 bool fRc = RTAvlU32Insert(&pThis->Block2FileRoot, &pBlock2File->Core);
591 Assert(fRc); RT_NOREF(fRc);
592 }
593 else
594 {
595 pBlock2File->Core.pLeft = NULL;
596 pBlock2File->Core.pRight = NULL;
597 pBlock2FilePrev->pNext = pBlock2File;
598 }
599 }
600 }
601
602 /*
603 * Enter the object into the namespace.
604 */
605 rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName, true /*fNoNormalize*/);
606 if (RT_SUCCESS(rc))
607 {
608 pThis->pResults->cAddedNames++;
609
610 if (*pszRockName != '\0' && strcmp(pszName, pszRockName) != 0)
611 {
612 rc = RTFsIsoMakerObjSetRockName(pThis->hIsoMaker, idxObj, fNamespace, pszRockName);
613 if (RT_FAILURE(rc))
614 rc = rtFsIsoImpError(pThis, rc, "Error setting rock ridge name for file '%s' to '%s'", pszName, pszRockName);
615 }
616 }
617 else
618 return rtFsIsoImpError(pThis, rc, "Error naming file '%s': %Rrc", pszName, rc);
619 return VINF_SUCCESS;
620}
621
622
623/**
624 * Parses rock ridge information if present in the directory entry.
625 *
626 * @param pThis The importer instance.
627 * @param pObjInfo The object information to improve upon.
628 * @param pbSys The system area of the directory record.
629 * @param cbSys The number of bytes present in the sys area.
630 * @param fUnicode Indicates which namespace we're working on.
631 * @param fIsFirstDirRec Set if this is the '.' directory entry in the
632 * root directory. (Some entries applies only to
633 * it.)
634 * @param fContinuationRecord Set if we're processing a continuation record in
635 * living in the abRockBuf.
636 */
637static void rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(PRTFSISOMKIMPORTER pThis, PRTFSOBJINFO pObjInfo,
638 uint8_t const *pbSys, size_t cbSys, bool fUnicode,
639 bool fIsFirstDirRec, bool fContinuationRecord)
640{
641 RT_NOREF(pObjInfo);
642
643 /*
644 * Do skipping if specified.
645 */
646 if (pThis->offSuspSkip)
647 {
648 if (cbSys <= pThis->offSuspSkip)
649 return;
650 pbSys += pThis->offSuspSkip;
651 cbSys -= pThis->offSuspSkip;
652 }
653
654 while (cbSys >= 4)
655 {
656 /*
657 * Check header length and advance the sys variables.
658 */
659 PCISO9660SUSPUNION pUnion = (PCISO9660SUSPUNION)pbSys;
660 if (pUnion->Hdr.cbEntry > cbSys)
661 {
662 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: cbEntry=%#x cbSys=%#x (%#x %#x)\n",
663 pUnion->Hdr.cbEntry, cbSys, pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
664 return;
665 }
666 pbSys += pUnion->Hdr.cbEntry;
667 cbSys -= pUnion->Hdr.cbEntry;
668
669 /*
670 * Process fields.
671 */
672#define MAKE_SIG(a_bSig1, a_bSig2) \
673 ( ((uint16_t)(a_bSig1) & 0x1f) \
674 | (((uint16_t)(a_bSig2) ^ 0x40) << 5) \
675 | ((((uint16_t)(a_bSig1) ^ 0x40) & 0xe0) << (5 + 8)) )
676
677 uint16_t const uSig = MAKE_SIG(pUnion->Hdr.bSig1, pUnion->Hdr.bSig2);
678 switch (uSig)
679 {
680 /*
681 * System use sharing protocol entries.
682 */
683 case MAKE_SIG(ISO9660SUSPCE_SIG1, ISO9660SUSPCE_SIG2):
684 {
685 if (RT_BE2H_U32(pUnion->CE.offBlock.be) != RT_LE2H_U32(pUnion->CE.offBlock.le))
686 LogRel(("rtFsIsoImport/Rock: Invalid CE offBlock field: be=%#x vs le=%#x\n",
687 RT_BE2H_U32(pUnion->CE.offBlock.be), RT_LE2H_U32(pUnion->CE.offBlock.le)));
688 else if (RT_BE2H_U32(pUnion->CE.cbData.be) != RT_LE2H_U32(pUnion->CE.cbData.le))
689 LogRel(("rtFsIsoImport/Rock: Invalid CE cbData field: be=%#x vs le=%#x\n",
690 RT_BE2H_U32(pUnion->CE.cbData.be), RT_LE2H_U32(pUnion->CE.cbData.le)));
691 else if (RT_BE2H_U32(pUnion->CE.offData.be) != RT_LE2H_U32(pUnion->CE.offData.le))
692 LogRel(("rtFsIsoImport/Rock: Invalid CE offData field: be=%#x vs le=%#x\n",
693 RT_BE2H_U32(pUnion->CE.offData.be), RT_LE2H_U32(pUnion->CE.offData.le)));
694 else if (!fContinuationRecord)
695 {
696 uint64_t offData = ISO9660_GET_ENDIAN(&pUnion->CE.offBlock) * (uint64_t)ISO9660_SECTOR_SIZE;
697 offData += ISO9660_GET_ENDIAN(&pUnion->CE.offData);
698 uint32_t cbData = ISO9660_GET_ENDIAN(&pUnion->CE.cbData);
699 if (cbData <= sizeof(pThis->abRockBuf) - (uint32_t)(offData & ISO9660_SECTOR_OFFSET_MASK))
700 {
701 AssertCompile(sizeof(pThis->abRockBuf) == ISO9660_SECTOR_SIZE);
702 uint64_t offDataBlock = offData & ~(uint64_t)ISO9660_SECTOR_OFFSET_MASK;
703 if (pThis->offRockBuf == offDataBlock)
704 rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, pObjInfo,
705 &pThis->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
706 cbData, fUnicode, fIsFirstDirRec,
707 true /*fContinuationRecord*/);
708 else
709 {
710 int rc = RTVfsFileReadAt(pThis->hSrcFile, offDataBlock, pThis->abRockBuf, sizeof(pThis->abRockBuf), NULL);
711 if (RT_SUCCESS(rc))
712 rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, pObjInfo,
713 &pThis->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
714 cbData, fUnicode, fIsFirstDirRec,
715 true /*fContinuationRecord*/);
716 else
717 LogRel(("rtFsIsoImport/Rock: Error reading continuation record at %#RX64: %Rrc\n",
718 offDataBlock, rc));
719 }
720 }
721 else
722 LogRel(("rtFsIsoImport/Rock: continuation record isn't within a sector! offData=%#RX64 cbData=%#RX32\n",
723 cbData, offData));
724 }
725 else
726 LogRel(("rtFsIsoImport/Rock: nested continuation record!\n"));
727 break;
728 }
729
730 case MAKE_SIG(ISO9660SUSPSP_SIG1, ISO9660SUSPSP_SIG2): /* SP */
731 if ( pUnion->Hdr.cbEntry != ISO9660SUSPSP_LEN
732 || pUnion->Hdr.bVersion != ISO9660SUSPSP_VER
733 || pUnion->SP.bCheck1 != ISO9660SUSPSP_CHECK1
734 || pUnion->SP.bCheck2 != ISO9660SUSPSP_CHECK2
735 || pUnion->SP.cbSkip > UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
736 LogRel(("rtFsIsoImport/Rock: Malformed 'SP' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x), bCheck1=%#x (vs %#x), bCheck2=%#x (vs %#x), cbSkip=%#x (vs max %#x)\n",
737 pUnion->Hdr.cbEntry, ISO9660SUSPSP_LEN, pUnion->Hdr.bVersion, ISO9660SUSPSP_VER,
738 pUnion->SP.bCheck1, ISO9660SUSPSP_CHECK1, pUnion->SP.bCheck2, ISO9660SUSPSP_CHECK2,
739 pUnion->SP.cbSkip, UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]) ));
740 else if (!fIsFirstDirRec)
741 LogRel(("rtFsIsoImport/Rock: Ignorining 'SP' entry in non-root directory record\n"));
742 else if (pThis->fSuspSeenSP)
743 LogRel(("rtFsIsoImport/Rock: Ignorining additional 'SP' entry\n"));
744 else
745 {
746 pThis->offSuspSkip = pUnion->SP.cbSkip;
747 if (pUnion->SP.cbSkip != 0)
748 LogRel(("rtFsIsoImport/Rock: SP: cbSkip=%#x\n", pUnion->SP.cbSkip));
749 }
750 break;
751
752 case MAKE_SIG(ISO9660SUSPER_SIG1, ISO9660SUSPER_SIG2): /* ER */
753 if ( pUnion->Hdr.cbEntry > RT_UOFFSETOF(ISO9660SUSPER, achPayload) + (uint32_t)pUnion->ER.cchIdentifier
754 + (uint32_t)pUnion->ER.cchDescription + (uint32_t)pUnion->ER.cchSource
755 || pUnion->Hdr.bVersion != ISO9660SUSPER_VER)
756 LogRel(("rtFsIsoImport/Rock: Malformed 'ER' entry: cbEntry=%#x bVersion=%#x (vs %#x) cchIdentifier=%#x cchDescription=%#x cchSource=%#x\n",
757 pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion, ISO9660SUSPER_VER, pUnion->ER.cchIdentifier,
758 pUnion->ER.cchDescription, pUnion->ER.cchSource));
759 else if (!fIsFirstDirRec)
760 LogRel(("rtFsIsoImport/Rock: Ignorining 'ER' entry in non-root directory record\n"));
761 else if ( pUnion->ER.bVersion == 1 /* RRIP detection */
762 && ( (pUnion->ER.cchIdentifier >= 4 && strncmp(pUnion->ER.achPayload, ISO9660_RRIP_ID, 4 /*RRIP*/) == 0)
763 || (pUnion->ER.cchIdentifier >= 10 && strncmp(pUnion->ER.achPayload, RT_STR_TUPLE(ISO9660_RRIP_1_12_ID)) == 0) ))
764 {
765 LogRel(("rtFsIsoImport/Rock: Rock Ridge 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
766 pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
767 pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
768 pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
769 if (!fUnicode)
770 {
771 int rc = RTFsIsoMakerSetRockRidgeLevel(pThis->hIsoMaker, 2);
772 if (RT_FAILURE(rc))
773 LogRel(("rtFsIsoImport/Rock: RTFsIsoMakerSetRockRidgeLevel(,2) failed: %Rrc\n", rc));
774 }
775 else
776 {
777 int rc = RTFsIsoMakerSetJolietRockRidgeLevel(pThis->hIsoMaker, 2);
778 if (RT_FAILURE(rc))
779 LogRel(("rtFsIsoImport/Rock: RTFsIsoMakerSetJolietRockRidgeLevel(,2) failed: %Rrc\n", rc));
780 }
781 }
782 else
783 LogRel(("rtFsIsoImport/Rock: Unknown extension in 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
784 pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
785 pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
786 pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
787 break;
788
789 case MAKE_SIG(ISO9660SUSPPD_SIG1, ISO9660SUSPPD_SIG2): /* PD - ignored */
790 case MAKE_SIG(ISO9660SUSPST_SIG1, ISO9660SUSPST_SIG2): /* ST - ignore for now */
791 case MAKE_SIG(ISO9660SUSPES_SIG1, ISO9660SUSPES_SIG2): /* ES - ignore for now */
792 break;
793
794 /*
795 * Rock ridge interchange protocol entries.
796 */
797 case MAKE_SIG(ISO9660RRIPRR_SIG1, ISO9660RRIPRR_SIG2): /* RR */
798 if ( pUnion->RR.Hdr.cbEntry != ISO9660RRIPRR_LEN
799 || pUnion->RR.Hdr.bVersion != ISO9660RRIPRR_VER)
800 LogRel(("rtFsIsoImport/Rock: Malformed 'RR' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
801 pUnion->RR.Hdr.cbEntry, ISO9660RRIPRR_LEN, pUnion->RR.Hdr.bVersion, ISO9660RRIPRR_VER, pUnion->RR.fFlags));
802 /* else: ignore it */
803 break;
804
805 case MAKE_SIG(ISO9660RRIPPX_SIG1, ISO9660RRIPPX_SIG2): /* PX */
806 if ( ( pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN
807 && pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN_NO_INODE)
808 || pUnion->PX.Hdr.bVersion != ISO9660RRIPPX_VER
809 || RT_BE2H_U32(pUnion->PX.fMode.be) != RT_LE2H_U32(pUnion->PX.fMode.le)
810 || RT_BE2H_U32(pUnion->PX.cHardlinks.be) != RT_LE2H_U32(pUnion->PX.cHardlinks.le)
811 || RT_BE2H_U32(pUnion->PX.uid.be) != RT_LE2H_U32(pUnion->PX.uid.le)
812 || RT_BE2H_U32(pUnion->PX.gid.be) != RT_LE2H_U32(pUnion->PX.gid.le)
813 || ( pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN
814 && RT_BE2H_U32(pUnion->PX.INode.be) != RT_LE2H_U32(pUnion->PX.INode.le)) )
815 LogRel(("rtFsIsoImport/Rock: Malformed 'PX' entry: cbEntry=%#x (vs %#x or %#x), bVersion=%#x (vs %#x) fMode=%#x/%#x cHardlinks=%#x/%#x uid=%#x/%#x gid=%#x/%#x inode=%#x/%#x\n",
816 pUnion->PX.Hdr.cbEntry, ISO9660RRIPPX_LEN, ISO9660RRIPPX_LEN_NO_INODE,
817 pUnion->PX.Hdr.bVersion, ISO9660RRIPPX_VER,
818 RT_BE2H_U32(pUnion->PX.fMode.be), RT_LE2H_U32(pUnion->PX.fMode.le),
819 RT_BE2H_U32(pUnion->PX.cHardlinks.be), RT_LE2H_U32(pUnion->PX.cHardlinks.le),
820 RT_BE2H_U32(pUnion->PX.uid.be), RT_LE2H_U32(pUnion->PX.uid.le),
821 RT_BE2H_U32(pUnion->PX.gid.be), RT_LE2H_U32(pUnion->PX.gid.le),
822 pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_BE2H_U32(pUnion->PX.INode.be) : 0,
823 pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_LE2H_U32(pUnion->PX.INode.le) : 0 ));
824 else
825 {
826 if (RTFS_IS_DIRECTORY(ISO9660_GET_ENDIAN(&pUnion->PX.fMode)) == RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
827 pObjInfo->Attr.fMode = ISO9660_GET_ENDIAN(&pUnion->PX.fMode);
828 else
829 LogRel(("rtFsIsoImport/Rock: 'PX' entry changes directory-ness: fMode=%#x, existing %#x; ignored\n",
830 ISO9660_GET_ENDIAN(&pUnion->PX.fMode), pObjInfo->Attr.fMode));
831 pObjInfo->Attr.u.Unix.cHardlinks = ISO9660_GET_ENDIAN(&pUnion->PX.cHardlinks);
832 pObjInfo->Attr.u.Unix.uid = ISO9660_GET_ENDIAN(&pUnion->PX.uid);
833 pObjInfo->Attr.u.Unix.gid = ISO9660_GET_ENDIAN(&pUnion->PX.gid);
834 /* ignore inode */
835 }
836 break;
837
838 case MAKE_SIG(ISO9660RRIPPN_SIG1, ISO9660RRIPPN_SIG2): /* PN */
839 if ( pUnion->PN.Hdr.cbEntry != ISO9660RRIPPN_LEN
840 || pUnion->PN.Hdr.bVersion != ISO9660RRIPPN_VER
841 || RT_BE2H_U32(pUnion->PN.Major.be) != RT_LE2H_U32(pUnion->PN.Major.le)
842 || RT_BE2H_U32(pUnion->PN.Minor.be) != RT_LE2H_U32(pUnion->PN.Minor.le))
843 LogRel(("rtFsIsoImport/Rock: Malformed 'PN' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) Major=%#x/%#x Minor=%#x/%#x\n",
844 pUnion->PN.Hdr.cbEntry, ISO9660RRIPPN_LEN, pUnion->PN.Hdr.bVersion, ISO9660RRIPPN_VER,
845 RT_BE2H_U32(pUnion->PN.Major.be), RT_LE2H_U32(pUnion->PN.Major.le),
846 RT_BE2H_U32(pUnion->PN.Minor.be), RT_LE2H_U32(pUnion->PN.Minor.le) ));
847 else if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
848 LogRel(("rtFsIsoImport/Rock: Ignorning 'PN' entry for directory (%#x/%#x)\n",
849 ISO9660_GET_ENDIAN(&pUnion->PN.Major), ISO9660_GET_ENDIAN(&pUnion->PN.Minor) ));
850 else
851 pObjInfo->Attr.u.Unix.Device = RTDEV_MAKE(ISO9660_GET_ENDIAN(&pUnion->PN.Major),
852 ISO9660_GET_ENDIAN(&pUnion->PN.Minor));
853 break;
854
855 case MAKE_SIG(ISO9660RRIPTF_SIG1, ISO9660RRIPTF_SIG2): /* TF */
856 if ( pUnion->TF.Hdr.bVersion != ISO9660RRIPTF_VER
857 || pUnion->TF.Hdr.cbEntry < Iso9660RripTfCalcLength(pUnion->TF.fFlags))
858 LogRel(("rtFsIsoImport/Rock: Malformed 'TF' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
859 pUnion->TF.Hdr.cbEntry, Iso9660RripTfCalcLength(pUnion->TF.fFlags),
860 pUnion->TF.Hdr.bVersion, ISO9660RRIPTF_VER, RT_BE2H_U32(pUnion->TF.fFlags) ));
861 else if (!(pUnion->TF.fFlags & ISO9660RRIPTF_F_LONG_FORM))
862 {
863 PCISO9660RECTIMESTAMP pTimestamp = (PCISO9660RECTIMESTAMP)&pUnion->TF.abPayload[0];
864 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
865 {
866 rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->BirthTime, pTimestamp);
867 pTimestamp++;
868 }
869 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
870 {
871 rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->ModificationTime, pTimestamp);
872 pTimestamp++;
873 }
874 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
875 {
876 rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->AccessTime, pTimestamp);
877 pTimestamp++;
878 }
879 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
880 {
881 rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->ChangeTime, pTimestamp);
882 pTimestamp++;
883 }
884 }
885 else
886 {
887 PCISO9660TIMESTAMP pTimestamp = (PCISO9660TIMESTAMP)&pUnion->TF.abPayload[0];
888 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
889 {
890 rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->BirthTime, pTimestamp);
891 pTimestamp++;
892 }
893 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
894 {
895 rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->ModificationTime, pTimestamp);
896 pTimestamp++;
897 }
898 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
899 {
900 rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->AccessTime, pTimestamp);
901 pTimestamp++;
902 }
903 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
904 {
905 rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->ChangeTime, pTimestamp);
906 pTimestamp++;
907 }
908 }
909 break;
910
911 case MAKE_SIG(ISO9660RRIPSF_SIG1, ISO9660RRIPSF_SIG2): /* SF */
912 LogRel(("rtFsIsoImport/Rock: Sparse file support not yet implemented!\n"));
913 break;
914
915 case MAKE_SIG(ISO9660RRIPSL_SIG1, ISO9660RRIPSL_SIG2): /* SL */
916 if ( pUnion->SL.Hdr.bVersion != ISO9660RRIPSL_VER
917 || pUnion->SL.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2])
918 || (pUnion->SL.fFlags & ~ISO9660RRIP_SL_F_CONTINUE)
919 || (pUnion->SL.abComponents[0] & ISO9660RRIP_SL_C_RESERVED_MASK) )
920 LogRel(("rtFsIsoImport/Rock: Malformed 'SL' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x comp[0].fFlags=%#x\n",
921 pUnion->SL.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2]),
922 pUnion->SL.Hdr.bVersion, ISO9660RRIPSL_VER, pUnion->SL.fFlags, pUnion->SL.abComponents[0]));
923 else if (pThis->fSeenLastSL)
924 LogRel(("rtFsIsoImport/Rock: Unexpected 'SL!' entry\n"));
925 else
926 {
927 pThis->fSeenLastSL = !(pUnion->SL.fFlags & ISO9660RRIP_SL_F_CONTINUE); /* used in loop */
928
929 size_t offDst = strlen(pThis->szRockSymlinkTargetBuf);
930 uint8_t const *pbSrc = &pUnion->SL.abComponents[0];
931 uint8_t cbSrcLeft = pUnion->SL.Hdr.cbEntry - RT_UOFFSETOF(ISO9660RRIPSL, abComponents);
932 while (cbSrcLeft >= 2)
933 {
934 uint8_t const fFlags = pbSrc[0];
935 uint8_t cchCopy = pbSrc[1];
936 uint8_t const cbSkip = cchCopy + 2;
937 if (cbSkip > cbSrcLeft)
938 {
939 LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: component flags=%#x, component length+2=%#x vs %#x left\n",
940 fFlags, cbSkip, cbSrcLeft));
941 break;
942 }
943
944 const char *pszCopy;
945 switch (fFlags & ~ISO9660RRIP_SL_C_CONTINUE)
946 {
947 case 0:
948 pszCopy = (const char *)&pbSrc[2];
949 break;
950
951 case ISO9660RRIP_SL_C_CURRENT:
952 if (cchCopy != 0)
953 LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: CURRENT + %u bytes, ignoring bytes\n", cchCopy));
954 pszCopy = ".";
955 cchCopy = 1;
956 break;
957
958 case ISO9660RRIP_SL_C_PARENT:
959 if (cchCopy != 0)
960 LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: PARENT + %u bytes, ignoring bytes\n", cchCopy));
961 pszCopy = "..";
962 cchCopy = 2;
963 break;
964
965 case ISO9660RRIP_SL_C_ROOT:
966 if (cchCopy != 0)
967 LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: ROOT + %u bytes, ignoring bytes\n", cchCopy));
968 pszCopy = "/";
969 cchCopy = 1;
970 break;
971
972 default:
973 LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: component flags=%#x (bad), component length=%#x vs %#x left\n",
974 fFlags, cchCopy, cbSrcLeft));
975 pszCopy = NULL;
976 cchCopy = 0;
977 break;
978 }
979
980 if (offDst + cchCopy < sizeof(pThis->szRockSymlinkTargetBuf))
981 {
982 memcpy(&pThis->szRockSymlinkTargetBuf[offDst], pszCopy, cchCopy);
983 offDst += cchCopy;
984 }
985 else
986 {
987 LogRel(("rtFsIsoImport/Rock: 'SL' constructs a too long target! '%.*s%.*s'\n",
988 offDst, pThis->szRockSymlinkTargetBuf, cchCopy, pszCopy));
989 memcpy(&pThis->szRockSymlinkTargetBuf[offDst], pszCopy,
990 sizeof(pThis->szRockSymlinkTargetBuf) - offDst - 1);
991 offDst = sizeof(pThis->szRockSymlinkTargetBuf) - 1;
992 break;
993 }
994
995 /* Advance */
996 pbSrc += cbSkip;
997 cbSrcLeft -= cbSkip;
998
999 /* Append slash if appropriate. */
1000 if ( !(fFlags & ISO9660RRIP_SL_C_CONTINUE)
1001 && (cbSrcLeft >= 2 || !pThis->fSeenLastSL) )
1002 {
1003 if (offDst + 1 < sizeof(pThis->szRockSymlinkTargetBuf))
1004 pThis->szRockSymlinkTargetBuf[offDst++] = '/';
1005 else
1006 {
1007 LogRel(("rtFsIsoImport/Rock: 'SL' constructs a too long target! '%.*s/'\n",
1008 offDst, pThis->szRockSymlinkTargetBuf));
1009 break;
1010 }
1011 }
1012 }
1013 pThis->szRockSymlinkTargetBuf[offDst] = '\0';
1014
1015 /* Purge the encoding as we don't want invalid UTF-8 floating around. */
1016 RTStrPurgeEncoding(pThis->szRockSymlinkTargetBuf);
1017 }
1018 break;
1019
1020 case MAKE_SIG(ISO9660RRIPNM_SIG1, ISO9660RRIPNM_SIG2): /* NM */
1021 if ( pUnion->NM.Hdr.bVersion != ISO9660RRIPNM_VER
1022 || pUnion->NM.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPNM, achName)
1023 || (pUnion->NM.fFlags & ISO9660RRIP_NM_F_RESERVED_MASK) )
1024 LogRel(("rtFsIsoImport/Rock: Malformed 'NM' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x %.*Rhxs\n",
1025 pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName),
1026 pUnion->NM.Hdr.bVersion, ISO9660RRIPNM_VER, pUnion->NM.fFlags,
1027 pUnion->NM.Hdr.cbEntry - RT_MIN(pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName)),
1028 &pUnion->NM.achName[0] ));
1029 else if (pThis->fSeenLastNM)
1030 LogRel(("rtFsIsoImport/Rock: Unexpected 'NM' entry!\n"));
1031 else
1032 {
1033 pThis->fSeenLastNM = !(pUnion->NM.fFlags & ISO9660RRIP_NM_F_CONTINUE);
1034
1035 uint8_t const cchName = pUnion->NM.Hdr.cbEntry - (uint8_t)RT_UOFFSETOF(ISO9660RRIPNM, achName);
1036 if (pUnion->NM.fFlags & (ISO9660RRIP_NM_F_CURRENT | ISO9660RRIP_NM_F_PARENT))
1037 {
1038 if (cchName == 0 && pThis->szRockNameBuf[0] == '\0')
1039 Log(("rtFsIsoImport/Rock: Ignoring 'NM' entry for '.' and '..'\n"));
1040 else
1041 LogRel(("rtFsIsoImport/Rock: Ignoring malformed 'NM' using '.' or '..': fFlags=%#x cchName=%#x %.*Rhxs; szRockNameBuf='%s'\n",
1042 pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName, pThis->szRockNameBuf));
1043 pThis->szRockNameBuf[0] = '\0';
1044 pThis->fSeenLastNM = true;
1045 }
1046 else
1047 {
1048 size_t offDst = strlen(pThis->szRockNameBuf);
1049 if (offDst + cchName < sizeof(pThis->szRockNameBuf))
1050 {
1051 memcpy(&pThis->szRockNameBuf[offDst], pUnion->NM.achName, cchName);
1052 pThis->szRockNameBuf[offDst + cchName] = '\0';
1053
1054 /* Purge the encoding as we don't want invalid UTF-8 floating around. */
1055 RTStrPurgeEncoding(pThis->szRockSymlinkTargetBuf);
1056 }
1057 else
1058 {
1059 LogRel(("rtFsIsoImport/Rock: 'NM' constructs a too long name, ignoring it all: '%s%.*s'\n",
1060 pThis->szRockNameBuf, cchName, pUnion->NM.achName));
1061 pThis->szRockNameBuf[0] = '\0';
1062 pThis->fSeenLastNM = true;
1063 }
1064 }
1065 }
1066 break;
1067
1068 case MAKE_SIG(ISO9660RRIPCL_SIG1, ISO9660RRIPCL_SIG2): /* CL - just warn for now. */
1069 case MAKE_SIG(ISO9660RRIPPL_SIG1, ISO9660RRIPPL_SIG2): /* PL - just warn for now. */
1070 case MAKE_SIG(ISO9660RRIPRE_SIG1, ISO9660RRIPRE_SIG2): /* RE - just warn for now. */
1071 LogRel(("rtFsIsoImport/Rock: Ignorning directory relocation entry '%c%c'!\n", pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
1072 break;
1073
1074 default:
1075 LogRel(("rtFsIsoImport/Rock: Unknown SUSP entry: %#x %#x, %#x bytes, v%u\n",
1076 pUnion->Hdr.bSig1, pUnion->Hdr.bSig2, pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion));
1077 break;
1078#undef MAKE_SIG
1079 }
1080 }
1081}
1082
1083
1084/**
1085 * Deals with the special '.' entry in the root directory.
1086 *
1087 * @returns IPRT status code.
1088 * @param pThis The import instance.
1089 * @param pDirRec The root directory record.
1090 * @param fUnicode Indicates which namespace we're working on.
1091 */
1092static int rtFsIsoImportProcessIso9660TreeWorkerDoRockForRoot(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, bool fUnicode)
1093{
1094 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
1095 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
1096 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
1097 if (cbSys > 4)
1098 {
1099 RTFSOBJINFO ObjInfo;
1100 ObjInfo.cbObject = 0;
1101 ObjInfo.cbAllocated = 0;
1102 rtFsIsoImpIso9660RecDateTime2TimeSpec(&ObjInfo.AccessTime, &pDirRec->RecTime);
1103 ObjInfo.ModificationTime = ObjInfo.AccessTime;
1104 ObjInfo.ChangeTime = ObjInfo.AccessTime;
1105 ObjInfo.BirthTime = ObjInfo.AccessTime;
1106 ObjInfo.Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | 0555;
1107 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1108 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
1109 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
1110 ObjInfo.Attr.u.Unix.cHardlinks = 2;
1111 ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1112 ObjInfo.Attr.u.Unix.INodeId = 0;
1113 ObjInfo.Attr.u.Unix.fFlags = 0;
1114 ObjInfo.Attr.u.Unix.GenerationId = 0;
1115 ObjInfo.Attr.u.Unix.Device = 0;
1116
1117 rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, &ObjInfo, pbSys, cbSys, fUnicode, true /*fIsFirstDirRec*/,
1118 false /*fContinuationRecord*/);
1119 /** @todo Update root dir attribs. Need API. */
1120 }
1121 return VINF_SUCCESS;
1122}
1123
1124
1125/**
1126 * Validates a directory record.
1127 *
1128 * @returns IPRT status code (safe to ignore, see pThis->rc).
1129 * @param pThis The importer instance.
1130 * @param pDirRec The directory record to validate.
1131 * @param cbMax The maximum size.
1132 */
1133static int rtFsIsoImportValidateDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax)
1134{
1135 /*
1136 * Validate dual fields.
1137 */
1138 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
1139 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
1140 "Invalid dir rec size field: {%#RX32,%#RX32}",
1141 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
1142
1143 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
1144 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
1145 "Invalid dir rec extent field: {%#RX32,%#RX32}",
1146 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
1147
1148 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
1149 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
1150 "Invalid dir rec volume sequence ID field: {%#RX16,%#RX16}",
1151 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
1152
1153 /*
1154 * Check values.
1155 */
1156 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
1157 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_VOLUME_SEQ_NO,
1158 "Expected dir rec to have same volume sequence number as primary volume: %#x, expected %#x",
1159 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
1160
1161 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
1162 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_EXTENT_OUT_OF_BOUNDS,
1163 "Invalid dir rec extent: %#RX32, max %#RX32",
1164 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
1165
1166 if (pDirRec->cbDirRec < RT_UOFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength)
1167 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
1168 "Dir record size is too small: %#x (min %#x)",
1169 pDirRec->cbDirRec, RT_UOFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength);
1170 if (pDirRec->cbDirRec > cbMax)
1171 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
1172 "Dir record size is too big: %#x (max %#x)", pDirRec->cbDirRec, cbMax);
1173
1174 if ( (pDirRec->fFileFlags & (ISO9660_FILE_FLAGS_MULTI_EXTENT | ISO9660_FILE_FLAGS_DIRECTORY))
1175 == (ISO9660_FILE_FLAGS_MULTI_EXTENT | ISO9660_FILE_FLAGS_DIRECTORY))
1176 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_WITH_MORE_EXTENTS,
1177 "Multi-extent directories are not supported (cbData=%#RX32 offExtent=%#RX32)",
1178 ISO9660_GET_ENDIAN(&pDirRec->cbData), ISO9660_GET_ENDIAN(&pDirRec->offExtent));
1179
1180 return VINF_SUCCESS;
1181}
1182
1183
1184/**
1185 * Validates a dot or dot-dot directory record.
1186 *
1187 * @returns IPRT status code (safe to ignore, see pThis->rc).
1188 * @param pThis The importer instance.
1189 * @param pDirRec The dot directory record to validate.
1190 * @param cbMax The maximum size.
1191 * @param bName The name byte (0x00: '.', 0x01: '..').
1192 */
1193static int rtFsIsoImportValidateDotDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax, uint8_t bName)
1194{
1195 int rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbMax);
1196 if (RT_SUCCESS(rc))
1197 {
1198 if (pDirRec->bFileIdLength != 1)
1199 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME_LENGTH,
1200 "Invalid dot dir rec file id length: %u", pDirRec->bFileIdLength);
1201 if ((uint8_t)pDirRec->achFileId[0] != bName)
1202 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME,
1203 "Invalid dot dir rec file id: %#x, expected %#x", pDirRec->achFileId[0], bName);
1204 }
1205 return rc;
1206}
1207
1208
1209/**
1210 * rtFsIsoImportProcessIso9660TreeWorker helper that reads more data.
1211 *
1212 * @returns IPRT status code.
1213 * @param pThis The importer instance.
1214 * @param ppDirRec Pointer to the directory record pointer (in/out).
1215 * @param pcbChunk Pointer to the cbChunk variable (in/out).
1216 * @param pcbDir Pointer to the cbDir variable (in/out). This indicates
1217 * how much we've left to read from the directory.
1218 * @param poffNext Pointer to the offNext variable (in/out). This
1219 * indicates where the next chunk of directory data is in
1220 * the input file.
1221 */
1222static int rtFsIsoImportProcessIso9660TreeWorkerReadMore(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC *ppDirRec,
1223 uint32_t *pcbChunk, uint32_t *pcbDir, uint64_t *poffNext)
1224{
1225 uint32_t cbChunk = *pcbChunk;
1226 *ppDirRec = (PCISO9660DIRREC)memmove(&pThis->abBuf[ISO9660_SECTOR_SIZE - cbChunk], *ppDirRec, cbChunk);
1227
1228 Assert(!(*poffNext & (ISO9660_SECTOR_SIZE - 1)));
1229 uint32_t cbToRead = RT_MIN(*pcbDir, sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE);
1230 int rc = RTVfsFileReadAt(pThis->hSrcFile, *poffNext, &pThis->abBuf[ISO9660_SECTOR_SIZE], cbToRead, NULL);
1231 if (RT_SUCCESS(rc))
1232 {
1233 Log3(("rtFsIsoImportProcessIso9660TreeWorker: Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
1234 cbToRead, *poffNext, *poffNext - cbChunk, cbChunk + cbToRead));
1235 *poffNext += cbToRead;
1236 *pcbDir -= cbToRead;
1237 *pcbChunk = cbChunk + cbToRead;
1238 return VINF_SUCCESS;
1239 }
1240 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", *poffNext, cbToRead);
1241}
1242
1243
1244/**
1245 * rtFsIsoImportProcessIso9660TreeWorker helper that deals with skipping to the
1246 * next sector when cbDirRec is zero.
1247 *
1248 * @returns IPRT status code.
1249 * @retval VERR_NO_MORE_FILES when we reaches the end of the directory.
1250 * @param pThis The importer instance.
1251 * @param ppDirRec Pointer to the directory record pointer (in/out).
1252 * @param pcbChunk Pointer to the cbChunk variable (in/out). Indicates how
1253 * much we've left to process starting and pDirRec.
1254 * @param pcbDir Pointer to the cbDir variable (in/out). This indicates
1255 * how much we've left to read from the directory.
1256 * @param poffNext Pointer to the offNext variable (in/out). This
1257 * indicates where the next chunk of directory data is in
1258 * the input file.
1259 */
1260static int rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC *ppDirRec,
1261 uint32_t *pcbChunk, uint32_t *pcbDir, uint64_t *poffNext)
1262{
1263 uint32_t cbChunk = *pcbChunk;
1264 uint64_t offChunk = *poffNext - cbChunk;
1265 uint32_t cbSkip = ISO9660_SECTOR_SIZE - ((uint32_t)offChunk & (ISO9660_SECTOR_SIZE - 1));
1266 if (cbSkip < cbChunk)
1267 {
1268 *ppDirRec = (PCISO9660DIRREC)((uintptr_t)*ppDirRec + cbSkip);
1269 *pcbChunk = cbChunk -= cbSkip;
1270 if ( cbChunk > UINT8_MAX
1271 || *pcbDir == 0)
1272 {
1273 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> jumped %#RX32 to @%#RX64 LB %#RX32\n",
1274 cbSkip, *poffNext - cbChunk, cbChunk));
1275 return VINF_SUCCESS;
1276 }
1277 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> jumped %#RX32 to @%#RX64 LB %#RX32, but needs to read more\n",
1278 cbSkip, *poffNext - cbChunk, cbChunk));
1279 return rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, ppDirRec, pcbChunk, pcbDir, poffNext);
1280 }
1281
1282 /* ASSUMES we're working in multiples of sectors! */
1283 if (*pcbDir == 0)
1284 {
1285 *pcbChunk = 0;
1286 return VERR_NO_MORE_FILES;
1287 }
1288
1289 /* End of chunk, read the next sectors. */
1290 Assert(!(*poffNext & (ISO9660_SECTOR_SIZE - 1)));
1291 uint32_t cbToRead = RT_MIN(*pcbDir, sizeof(pThis->abBuf));
1292 int rc = RTVfsFileReadAt(pThis->hSrcFile, *poffNext, pThis->abBuf, cbToRead, NULL);
1293 if (RT_SUCCESS(rc))
1294 {
1295 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
1296 cbToRead, *poffNext, *poffNext - cbChunk, cbChunk + cbToRead));
1297 *poffNext += cbToRead;
1298 *pcbDir -= cbToRead;
1299 *pcbChunk = cbChunk + cbToRead;
1300 *ppDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
1301 return VINF_SUCCESS;
1302 }
1303 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", *poffNext, cbToRead);
1304}
1305
1306
1307/**
1308 * Deals with a single directory.
1309 *
1310 * @returns IPRT status code (safe to ignore, see pThis->rc).
1311 * @param pThis The importer instance.
1312 * @param idxDir The configuration index for the directory.
1313 * @param offDirBlock The offset of the directory data.
1314 * @param cbDir The size of the directory data.
1315 * @param cDepth The depth of the directory.
1316 * @param fUnicode Set if it's a unicode (UTF-16BE) encoded
1317 * directory.
1318 * @param pTodoList The todo-list to add sub-directories to.
1319 */
1320static int rtFsIsoImportProcessIso9660TreeWorker(PRTFSISOMKIMPORTER pThis, uint32_t idxDir,
1321 uint32_t offDirBlock, uint32_t cbDir, uint8_t cDepth, bool fUnicode,
1322 PRTLISTANCHOR pTodoList)
1323{
1324 /*
1325 * Restrict the depth to try avoid loops.
1326 */
1327 if (cDepth > RTFSISOMK_IMPORT_MAX_DEPTH)
1328 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE, "Dir at %#x LB %#x is too deep", offDirBlock, cbDir);
1329
1330 /*
1331 * Read the first chunk into the big buffer.
1332 */
1333 uint32_t cbChunk = RT_MIN(cbDir, sizeof(pThis->abBuf));
1334 uint64_t offNext = (uint64_t)offDirBlock * ISO9660_SECTOR_SIZE;
1335 int rc = RTVfsFileReadAt(pThis->hSrcFile, offNext, pThis->abBuf, cbChunk, NULL);
1336 if (RT_FAILURE(rc))
1337 return rtFsIsoImpError(pThis, rc, "Error reading directory at %#RX64 (%#RX32 / %#RX32): %Rrc", offNext, cbChunk, cbDir);
1338
1339 cbDir -= cbChunk;
1340 offNext += cbChunk;
1341
1342 /*
1343 * Skip the current and parent directory entries.
1344 */
1345 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
1346 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x00);
1347 if (RT_FAILURE(rc))
1348 return rc;
1349 if ( cDepth == 0
1350 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ROCK_RIDGE)
1351 && pDirRec->cbDirRec > RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
1352 {
1353 rc = rtFsIsoImportProcessIso9660TreeWorkerDoRockForRoot(pThis, pDirRec, fUnicode);
1354 if (RT_FAILURE(rc))
1355 return rc;
1356 }
1357
1358 cbChunk -= pDirRec->cbDirRec;
1359 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
1360 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x01);
1361 if (RT_FAILURE(rc))
1362 return rc;
1363
1364 cbChunk -= pDirRec->cbDirRec;
1365 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
1366
1367 /*
1368 * Work our way thru all the directory records.
1369 */
1370 Log3(("rtFsIsoImportProcessIso9660TreeWorker: Starting at @%#RX64 LB %#RX32 (out of %#RX32) in %#x\n",
1371 offNext - cbChunk, cbChunk, cbChunk + cbDir, idxDir));
1372 const uint32_t fNamespace = fUnicode ? RTFSISOMAKER_NAMESPACE_JOLIET : RTFSISOMAKER_NAMESPACE_ISO_9660;
1373 while ( cbChunk > 0
1374 || cbDir > 0)
1375 {
1376 /*
1377 * Do we need to read some more?
1378 */
1379 if ( cbChunk > UINT8_MAX
1380 || cbDir == 0)
1381 { /* No, we don't. */ }
1382 else
1383 {
1384 rc = rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, &pDirRec, &cbChunk, &cbDir, &offNext);
1385 if (RT_FAILURE(rc))
1386 return rc;
1387 }
1388
1389 /* If null length, skip to the next sector. May have to read some then. */
1390 if (pDirRec->cbDirRec != 0)
1391 { /* likely */ }
1392 else
1393 {
1394 rc = rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(pThis, &pDirRec, &cbChunk, &cbDir, &offNext);
1395 if (RT_FAILURE(rc))
1396 {
1397 if (rc == VERR_NO_MORE_FILES)
1398 break;
1399 return rc;
1400 }
1401 if (pDirRec->cbDirRec == 0)
1402 continue;
1403 }
1404
1405 /*
1406 * Validate the directory record. Give up if not valid since we're
1407 * likely to get error with subsequent record too.
1408 */
1409 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
1410 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
1411 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
1412 Log3(("pDirRec=&abBuf[%#07zx]: @%#010RX64 cb=%#04x ff=%#04x off=%#010RX32 cb=%#010RX32 cbSys=%#x id=%.*Rhxs\n",
1413 (uintptr_t)pDirRec - (uintptr_t)&pThis->abBuf[0], offNext - cbChunk, pDirRec->cbDirRec, pDirRec->fFileFlags,
1414 ISO9660_GET_ENDIAN(&pDirRec->offExtent), ISO9660_GET_ENDIAN(&pDirRec->cbData), cbSys,
1415 pDirRec->bFileIdLength, pDirRec->achFileId));
1416 rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbChunk);
1417 if (RT_FAILURE(rc))
1418 return rc;
1419
1420 /* This early calculation of the next record is due to multi-extent
1421 handling further down. */
1422 uint32_t cbChunkNew = cbChunk - pDirRec->cbDirRec;
1423 PCISO9660DIRREC pDirRecNext = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
1424
1425 /* Start Collecting object info. */
1426 RTFSOBJINFO ObjInfo;
1427 ObjInfo.cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
1428 ObjInfo.cbAllocated = ObjInfo.cbObject;
1429 rtFsIsoImpIso9660RecDateTime2TimeSpec(&ObjInfo.AccessTime, &pDirRec->RecTime);
1430 ObjInfo.ModificationTime = ObjInfo.AccessTime;
1431 ObjInfo.ChangeTime = ObjInfo.AccessTime;
1432 ObjInfo.BirthTime = ObjInfo.AccessTime;
1433 ObjInfo.Attr.fMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
1434 ? RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | 0555
1435 : RTFS_TYPE_FILE | RTFS_DOS_ARCHIVED | 0444;
1436 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1437 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
1438 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
1439 ObjInfo.Attr.u.Unix.cHardlinks = 1;
1440 ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1441 ObjInfo.Attr.u.Unix.INodeId = 0;
1442 ObjInfo.Attr.u.Unix.fFlags = 0;
1443 ObjInfo.Attr.u.Unix.GenerationId = 0;
1444 ObjInfo.Attr.u.Unix.Device = 0;
1445
1446 /*
1447 * Convert the name into the name buffer (szNameBuf).
1448 */
1449 if (!fUnicode)
1450 {
1451 memcpy(pThis->szNameBuf, pDirRec->achFileId, pDirRec->bFileIdLength);
1452 pThis->szNameBuf[pDirRec->bFileIdLength] = '\0';
1453 rc = RTStrValidateEncoding(pThis->szNameBuf);
1454 }
1455 else
1456 {
1457 char *pszDst = pThis->szNameBuf;
1458 rc = RTUtf16BigToUtf8Ex((PRTUTF16)pDirRec->achFileId, pDirRec->bFileIdLength / sizeof(RTUTF16),
1459 &pszDst, sizeof(pThis->szNameBuf), NULL);
1460 }
1461 if (RT_SUCCESS(rc))
1462 {
1463 /* Drop the version from the name. */
1464 size_t cchName = strlen(pThis->szNameBuf);
1465 if ( !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
1466 && cchName > 2
1467 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - 1]))
1468 {
1469 uint32_t offName = 2;
1470 while ( offName <= 5
1471 && offName + 1 < cchName
1472 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - offName]))
1473 offName++;
1474 if ( offName + 1 < cchName
1475 && pThis->szNameBuf[cchName - offName] == ';')
1476 {
1477 RTStrToUInt32Full(&pThis->szNameBuf[cchName - offName + 1], 10, &ObjInfo.Attr.u.Unix.GenerationId);
1478 pThis->szNameBuf[cchName - offName] = '\0';
1479 }
1480 }
1481 Log3((" --> name='%s'\n", pThis->szNameBuf));
1482
1483 pThis->szRockNameBuf[0] = '\0';
1484 pThis->szRockSymlinkTargetBuf[0] = '\0';
1485 if (cbSys > 0 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ROCK_RIDGE))
1486 {
1487 pThis->fSeenLastNM = false;
1488 pThis->fSeenLastSL = false;
1489 pThis->szRockNameBuf[0] = '\0';
1490 pThis->szRockSymlinkTargetBuf[0] = '\0';
1491 rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, &ObjInfo, pbSys, cbSys, fUnicode,
1492 false /*fContinuationRecord*/, false /*fIsFirstDirRec*/);
1493 }
1494
1495 /*
1496 * Deal with multi-extent files (usually large ones). We currently only
1497 * handle files where the data is in single continuous chunk and only split
1498 * up into multiple directory records because of data type limitations.
1499 */
1500 uint8_t abDirRecCopy[256];
1501 uint64_t cbData = ISO9660_GET_ENDIAN(&pDirRec->cbData);
1502 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
1503 { /* likely */ }
1504 else
1505 {
1506 if (cbData & (ISO9660_SECTOR_SIZE - 1))
1507 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISALIGNED_MULTI_EXTENT,
1508 "The size of non-final multi-extent record #0x0 isn't block aligned: %#RX64", cbData);
1509
1510 /* Make a copy of the first directory record so we don't overwrite
1511 it when reading in more records below. */
1512 pDirRec = (PCISO9660DIRREC)memcpy(abDirRecCopy, pDirRec, pDirRec->cbDirRec);
1513
1514 /* Process extent records. */
1515 uint32_t cDirRecs = 1;
1516 uint32_t offNextBlock = ISO9660_GET_ENDIAN(&pDirRec->offExtent)
1517 + ISO9660_GET_ENDIAN(&pDirRec->cbData) / ISO9660_SECTOR_SIZE;
1518 while ( cbChunkNew > 0
1519 || cbDir > 0)
1520 {
1521 /* Read more? Skip? */
1522 if ( cbChunkNew <= UINT8_MAX
1523 && cbDir != 0)
1524 {
1525 rc = rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, &pDirRecNext, &cbChunkNew, &cbDir, &offNext);
1526 if (RT_FAILURE(rc))
1527 return rc;
1528 }
1529 if (pDirRecNext->cbDirRec == 0)
1530 {
1531 rc = rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(pThis, &pDirRecNext, &cbChunkNew,
1532 &cbDir, &offNext);
1533 if (RT_FAILURE(rc))
1534 {
1535 if (rc == VERR_NO_MORE_FILES)
1536 break;
1537 return rc;
1538 }
1539 if (pDirRecNext->cbDirRec == 0)
1540 continue;
1541 }
1542
1543 /* Check the next record. */
1544 rc = rtFsIsoImportValidateDirRec(pThis, pDirRecNext, cbChunkNew);
1545 if (RT_FAILURE(rc))
1546 return rc;
1547 if (pDirRecNext->bFileIdLength != pDirRec->bFileIdLength)
1548 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
1549 "Multi-extent record #%#x differs from the first: bFileIdLength is %#x, expected %#x",
1550 cDirRecs, pDirRecNext->bFileIdLength, pDirRec->bFileIdLength);
1551 if (memcmp(pDirRecNext->achFileId, pDirRec->achFileId, pDirRec->bFileIdLength) != 0)
1552 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
1553 "Multi-extent record #%#x differs from the first: achFileId is %.*Rhxs, expected %.*Rhxs",
1554 cDirRecs, pDirRecNext->bFileIdLength, pDirRecNext->achFileId,
1555 pDirRec->bFileIdLength, pDirRec->achFileId);
1556 if (ISO9660_GET_ENDIAN(&pDirRecNext->VolumeSeqNo) != ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo))
1557 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
1558 "Multi-extent record #%#x differs from the first: VolumeSeqNo is %#x, expected %#x",
1559 cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->VolumeSeqNo),
1560 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo));
1561 if ( (pDirRecNext->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
1562 && (ISO9660_GET_ENDIAN(&pDirRecNext->cbData) & (ISO9660_SECTOR_SIZE - 1)) )
1563 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISALIGNED_MULTI_EXTENT,
1564 "The size of non-final multi-extent record #%#x isn't block aligned: %#RX32",
1565 cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->cbData));
1566
1567 /* Check that the data is contiguous, then add the data. */
1568 if (ISO9660_GET_ENDIAN(&pDirRecNext->offExtent) == offNextBlock)
1569 cbData += ISO9660_GET_ENDIAN(&pDirRecNext->cbData);
1570 else
1571 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_NON_CONTIGUOUS_MULTI_EXTENT,
1572 "Multi-extent record #%#x isn't contiguous: offExtent=%#RX32, expected %#RX32",
1573 cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->offExtent), offNextBlock);
1574
1575 /* Advance. */
1576 cDirRecs++;
1577 bool fDone = !(pDirRecNext->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT);
1578 offNext += ISO9660_GET_ENDIAN(&pDirRecNext->cbData) / ISO9660_SECTOR_SIZE;
1579 cbChunkNew -= pDirRecNext->cbDirRec;
1580 pDirRecNext = (PCISO9660DIRREC)((uintptr_t)pDirRecNext + pDirRecNext->cbDirRec);
1581 if (fDone)
1582 break;
1583 }
1584 }
1585 if (RT_SUCCESS(rc))
1586 {
1587 /*
1588 * Add the object.
1589 */
1590 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
1591 rtFsIsoImportProcessIso9660AddAndNameDirectory(pThis, pDirRec, &ObjInfo, cbData, fNamespace, idxDir,
1592 pThis->szNameBuf, pThis->szRockNameBuf, cDepth + 1, pTodoList);
1593 else if (pThis->szRockSymlinkTargetBuf[0] == '\0')
1594 {
1595 if (strcmp(pThis->szNameBuf, pThis->pszTransTbl) != 0)
1596 rtFsIsoImportProcessIso9660AddAndNameFile(pThis, pDirRec, &ObjInfo, cbData, fNamespace, idxDir,
1597 pThis->szNameBuf, pThis->szRockNameBuf);
1598 }
1599 else
1600 rtFsIsoImportProcessIso9660AddAndNameSymlink(pThis, pDirRec, &ObjInfo, fNamespace, idxDir, pThis->szNameBuf,
1601 pThis->szRockNameBuf, pThis->szRockSymlinkTargetBuf);
1602 }
1603 }
1604 else
1605 rtFsIsoImpError(pThis, rc, "Invalid name at %#RX64: %.Rhxs",
1606 offNext - cbChunk, pDirRec->bFileIdLength, pDirRec->achFileId);
1607
1608 /*
1609 * Advance to the next directory record.
1610 */
1611 cbChunk = cbChunkNew;
1612 pDirRec = pDirRecNext;
1613 }
1614
1615 return VINF_SUCCESS;
1616}
1617
1618
1619/**
1620 * Deals with a directory tree.
1621 *
1622 * This is implemented by tracking directories that needs to be processed in a
1623 * todo list, so no recursive calls, however it uses a bit of heap.
1624 *
1625 * @returns IPRT status code (safe to ignore, see pThis->rc).
1626 * @param pThis The importer instance.
1627 * @param offDirBlock The offset of the root directory data.
1628 * @param cbDir The size of the root directory data.
1629 * @param fUnicode Set if it's a unicode (UTF-16BE) encoded
1630 * directory.
1631 */
1632static int rtFsIsoImportProcessIso9660Tree(PRTFSISOMKIMPORTER pThis, uint32_t offDirBlock, uint32_t cbDir, bool fUnicode)
1633{
1634 /*
1635 * Reset some parsing state.
1636 */
1637 pThis->offSuspSkip = 0;
1638 pThis->fSuspSeenSP = false;
1639 pThis->pszTransTbl = "TRANS.TBL"; /** @todo query this from the iso maker! */
1640
1641 /*
1642 * Make sure we've got a root in the namespace.
1643 */
1644 uint32_t idxDir = RTFsIsoMakerGetObjIdxForPath(pThis->hIsoMaker,
1645 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET,
1646 "/");
1647 if (idxDir == UINT32_MAX)
1648 {
1649 idxDir = RTFSISOMAKER_CFG_IDX_ROOT;
1650 int rc = RTFsIsoMakerObjSetPath(pThis->hIsoMaker, RTFSISOMAKER_CFG_IDX_ROOT,
1651 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET, "/");
1652 if (RT_FAILURE(rc))
1653 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjSetPath failed on root dir: %Rrc", rc);
1654 }
1655 Assert(idxDir == RTFSISOMAKER_CFG_IDX_ROOT);
1656
1657 /*
1658 * Directories.
1659 */
1660 int rc = VINF_SUCCESS;
1661 uint8_t cDepth = 0;
1662 RTLISTANCHOR TodoList;
1663 RTListInit(&TodoList);
1664 for (;;)
1665 {
1666 int rc2 = rtFsIsoImportProcessIso9660TreeWorker(pThis, idxDir, offDirBlock, cbDir, cDepth, fUnicode, &TodoList);
1667 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1668 rc = rc2;
1669
1670 /*
1671 * Pop the next directory.
1672 */
1673 PRTFSISOMKIMPDIR pNext = RTListRemoveLast(&TodoList, RTFSISOMKIMPDIR, Entry);
1674 if (!pNext)
1675 break;
1676 idxDir = pNext->idxObj;
1677 offDirBlock = pNext->offDirBlock;
1678 cbDir = pNext->cbDir;
1679 cDepth = pNext->cDepth;
1680 RTMemFree(pNext);
1681 }
1682
1683 return rc;
1684}
1685
1686
1687/**
1688 * Imports a UTF-16BE string property from the joliet volume descriptor.
1689 *
1690 * The fields are normally space filled and padded, but we also consider zero
1691 * bytes are fillers. If the field only contains padding, the string property
1692 * will remain unchanged.
1693 *
1694 * @returns IPRT status code (ignorable).
1695 * @param pThis The importer instance.
1696 * @param pachField Pointer to the field. The structure type
1697 * is 'char' for hysterical raisins, while the
1698 * real type is 'RTUTF16'.
1699 * @param cchField The field length.
1700 * @param enmStringProp The corresponding string property.
1701 *
1702 * @note Clobbers pThis->pbBuf!
1703 */
1704static int rtFsIsoImportUtf16BigStringField(PRTFSISOMKIMPORTER pThis, const char *pachField, size_t cchField,
1705 RTFSISOMAKERSTRINGPROP enmStringProp)
1706{
1707 /*
1708 * Scan the field from the end as this way we know the result length if we find anything.
1709 */
1710 PCRTUTF16 pwcField = (PCRTUTF16)pachField;
1711 size_t cwcField = cchField / sizeof(RTUTF16); /* ignores any odd field byte */
1712 size_t off = cwcField;
1713 while (off-- > 0)
1714 {
1715 RTUTF16 wc = RT_BE2H_U16(pwcField[off]);
1716 if (wc == ' ' || wc == '\0')
1717 { /* likely */ }
1718 else
1719 {
1720 /*
1721 * Convert to UTF-16.
1722 */
1723 char *pszCopy = (char *)pThis->abBuf;
1724 int rc = RTUtf16BigToUtf8Ex(pwcField, off + 1, &pszCopy, sizeof(pThis->abBuf), NULL);
1725 if (RT_SUCCESS(rc))
1726 {
1727 rc = RTFsIsoMakerSetStringProp(pThis->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_JOLIET, pszCopy);
1728 if (RT_SUCCESS(rc))
1729 return VINF_SUCCESS;
1730 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetStringProp failed setting field %d to '%s': %Rrc",
1731 enmStringProp, pszCopy, rc);
1732 }
1733 return rtFsIsoImpError(pThis, rc, "RTUtf16BigToUtf8Ex failed converting field %d to UTF-8: %Rrc - %.*Rhxs",
1734 enmStringProp, rc, off * sizeof(RTUTF16), pwcField);
1735 }
1736 }
1737 return VINF_SUCCESS;
1738}
1739
1740
1741/**
1742 * Imports a string property from the primary volume descriptor.
1743 *
1744 * The fields are normally space filled and padded, but we also consider zero
1745 * bytes are fillers. If the field only contains padding, the string property
1746 * will remain unchanged.
1747 *
1748 * @returns IPRT status code (ignorable).
1749 * @param pThis The importer instance.
1750 * @param pachField Pointer to the field.
1751 * @param cchField The field length.
1752 * @param enmStringProp The corresponding string property.
1753 *
1754 * @note Clobbers pThis->pbBuf!
1755 */
1756static int rtFsIsoImportAsciiStringField(PRTFSISOMKIMPORTER pThis, const char *pachField, size_t cchField,
1757 RTFSISOMAKERSTRINGPROP enmStringProp)
1758{
1759 /*
1760 * Scan the field from the end as this way we know the result length if we find anything.
1761 */
1762 size_t off = cchField;
1763 while (off-- > 0)
1764 {
1765 char ch = pachField[off];
1766 if (ch == ' ' || ch == '\0')
1767 { /* likely */ }
1768 else
1769 {
1770 /*
1771 * Make a copy of the string in abBuf, purge the encoding.
1772 */
1773 off++;
1774 char *pszCopy = (char *)pThis->abBuf;
1775 memcpy(pszCopy, pachField, off);
1776 pszCopy[off] = '\0';
1777 RTStrPurgeEncoding(pszCopy);
1778
1779 int rc = RTFsIsoMakerSetStringProp(pThis->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_ISO_9660, pszCopy);
1780 if (RT_SUCCESS(rc))
1781 return VINF_SUCCESS;
1782 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetStringProp failed setting field %d to '%s': %Rrc",
1783 enmStringProp, pszCopy, rc);
1784 }
1785 }
1786 return VINF_SUCCESS;
1787}
1788
1789
1790/**
1791 * Validates a root directory record.
1792 *
1793 * @returns IPRT status code (safe to ignore, see pThis->rc).
1794 * @param pThis The importer instance.
1795 * @param pDirRec The root directory record to validate.
1796 */
1797static int rtFsIsoImportValidateRootDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec)
1798{
1799 /*
1800 * Validate dual fields.
1801 */
1802 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
1803 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
1804 "Invalid root dir size: {%#RX32,%#RX32}",
1805 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
1806
1807 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
1808 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
1809 "Invalid root dir extent: {%#RX32,%#RX32}",
1810 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
1811
1812 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
1813 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
1814 "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
1815 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
1816
1817 /*
1818 * Check values.
1819 */
1820 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
1821 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_VOLUME_SEQ_NO,
1822 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
1823 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
1824
1825 if (ISO9660_GET_ENDIAN(&pDirRec->cbData) == 0)
1826 return RTErrInfoSet(pThis->pErrInfo, VERR_ISOMK_IMPORT_ZERO_SIZED_ROOT_DIR, "Zero sized root dir");
1827
1828 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
1829 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_EXTENT_OUT_OF_BOUNDS,
1830 "Invalid root dir extent: %#RX32, max %#RX32",
1831 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
1832
1833 if (pDirRec->cbDirRec < RT_UOFFSETOF(ISO9660DIRREC, achFileId))
1834 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC_LENGTH,
1835 "Root dir record size is too small: %#x (min %#x)",
1836 pDirRec->cbDirRec, RT_UOFFSETOF(ISO9660DIRREC, achFileId));
1837
1838 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
1839 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_WITHOUT_DIR_FLAG,
1840 "Root dir is not flagged as directory: %#x", pDirRec->fFileFlags);
1841 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
1842 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_IS_MULTI_EXTENT,
1843 "Root dir is cannot be multi-extent: %#x", pDirRec->fFileFlags);
1844
1845 return VINF_SUCCESS;
1846}
1847
1848
1849/**
1850 * Processes a primary volume descriptor, importing all files and stuff.
1851 *
1852 * @returns IPRT status code (safe to ignore, see pThis->rc).
1853 * @param pThis The importer instance.
1854 * @param pVolDesc The primary volume descriptor.
1855 */
1856static int rtFsIsoImportProcessPrimaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660PRIMARYVOLDESC pVolDesc)
1857{
1858 /*
1859 * Validate dual fields first.
1860 */
1861 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1862 return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_PRIMARY_VOL_DESC_VER,
1863 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1864
1865 if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
1866 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1867 "Mismatching logical block size: {%#RX16,%#RX16}",
1868 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
1869 if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
1870 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1871 "Mismatching volume space size: {%#RX32,%#RX32}",
1872 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
1873 if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
1874 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1875 "Mismatching volumes in set: {%#RX16,%#RX16}",
1876 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
1877 if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
1878 {
1879 /* Hack alert! An Windows NT 3.1 ISO was found to not have the big endian bit set here, so work around it. */
1880 if ( pVolDesc->VolumeSeqNo.be == 0
1881 && pVolDesc->VolumeSeqNo.le == RT_H2LE_U16_C(1))
1882 pVolDesc->VolumeSeqNo.be = RT_H2BE_U16_C(1);
1883 else
1884 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1885 "Mismatching volume sequence no.: {%#RX16,%#RX16}",
1886 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
1887 }
1888 if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
1889 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1890 "Mismatching path table size: {%#RX32,%#RX32}",
1891 RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
1892
1893 /*
1894 * Validate field values against our expectations.
1895 */
1896 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
1897 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
1898 "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
1899
1900 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != 1)
1901 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MORE_THAN_ONE_VOLUME_IN_SET,
1902 "Volumes in set: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet));
1903
1904 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != 1)
1905 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
1906 "Unexpected volume sequence number: %#x", ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo));
1907
1908 /*
1909 * Gather info we need.
1910 */
1911 pThis->cBlocksInPrimaryVolumeSpace = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize);
1912 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)ISO9660_SECTOR_SIZE;
1913 pThis->cVolumesInSet = ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet);
1914 pThis->idPrimaryVol = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo);
1915
1916 /*
1917 * Validate the root directory record.
1918 */
1919 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
1920 if (RT_SUCCESS(rc))
1921 {
1922 /*
1923 * Import stuff if present and not opted out.
1924 */
1925 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_SYSTEM_ID))
1926 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId),
1927 RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
1928 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_VOLUME_ID))
1929 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId),
1930 RTFSISOMAKERSTRINGPROP_VOLUME_ID);
1931 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_VOLUME_SET_ID))
1932 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId),
1933 RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
1934 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PUBLISHER_ID))
1935 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId),
1936 RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
1937 if (pThis->fFlags & RTFSISOMK_IMPORT_F_DATA_PREPARER_ID)
1938 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId),
1939 RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
1940 if (pThis->fFlags & RTFSISOMK_IMPORT_F_APPLICATION_ID)
1941 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId),
1942 RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
1943 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_COPYRIGHT_FID))
1944 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId),
1945 RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
1946 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ABSTRACT_FID))
1947 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId),
1948 RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
1949 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BIBLIO_FID))
1950 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId),
1951 RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
1952
1953 /*
1954 * Process the directory tree.
1955 */
1956 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PRIMARY_ISO))
1957 rc = rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
1958 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), false /*fUnicode*/);
1959 }
1960
1961 return rc;
1962}
1963
1964
1965/**
1966 * Processes a secondary volume descriptor, if it is joliet we'll importing all
1967 * the files and stuff.
1968 *
1969 * @returns IPRT status code (safe to ignore, see pThis->rc).
1970 * @param pThis The importer instance.
1971 * @param pVolDesc The primary volume descriptor.
1972 */
1973static int rtFsIsoImportProcessSupplementaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660SUPVOLDESC pVolDesc)
1974{
1975 /*
1976 * Validate dual fields first.
1977 */
1978 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1979 return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_SUP_VOL_DESC_VER,
1980 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1981
1982 if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
1983 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1984 "Mismatching logical block size: {%#RX16,%#RX16}",
1985 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
1986 if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
1987 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1988 "Mismatching volume space size: {%#RX32,%#RX32}",
1989 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
1990 if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
1991 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1992 "Mismatching volumes in set: {%#RX16,%#RX16}",
1993 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
1994 if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
1995 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1996 "Mismatching volume sequence no.: {%#RX16,%#RX16}",
1997 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
1998 if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
1999 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
2000 "Mismatching path table size: {%#RX32,%#RX32}",
2001 RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
2002
2003 /*
2004 * Validate field values against our expectations.
2005 */
2006 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
2007 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
2008 "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
2009
2010 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
2011 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_VOLUME_IN_SET_MISMATCH, "Volumes in set: %#x, expected %#x",
2012 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
2013
2014 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
2015 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
2016 "Unexpected volume sequence number: %#x (expected %#x)",
2017 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
2018
2019 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
2020 {
2021 /* ubuntu-21.10-desktop-amd64.iso has 0x172f4e blocks (3 111 809 024 bytes) here
2022 and 0x173838 blocks (3 116 482 560 bytes) in the primary, a difference of
2023 -2282 blocks (-4 673 536 bytes). Guess something was omitted from the joliet
2024 edition, not immediately obvious what though.
2025
2026 For now we'll just let it pass as long as the primary size is the larger.
2027 (Not quite sure how the code will handle a supplementary volume spanning
2028 more space, as I suspect it only uses the primary volume size for
2029 validating block addresses and such.) */
2030 LogRel(("rtFsIsoImportProcessSupplementaryDesc: Volume space size differs between primary and supplementary descriptors: %#x, primary %#x",
2031 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace));
2032 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) > pThis->cBlocksInPrimaryVolumeSpace)
2033 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_VOLUME_SPACE_SIZE_MISMATCH,
2034 "Volume space given in the supplementary descriptor is larger than in the primary: %#x, primary %#x",
2035 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
2036 }
2037
2038 /*
2039 * Validate the root directory record.
2040 */
2041 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
2042 if (RT_FAILURE(rc))
2043 return rc;
2044
2045 /*
2046 * Is this a joliet descriptor? Ignore if not.
2047 */
2048 uint8_t uJolietLevel = 0;
2049 if ( pVolDesc->abEscapeSequences[0] == ISO9660_JOLIET_ESC_SEQ_0
2050 && pVolDesc->abEscapeSequences[1] == ISO9660_JOLIET_ESC_SEQ_1)
2051 switch (pVolDesc->abEscapeSequences[2])
2052 {
2053 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1: uJolietLevel = 1; break;
2054 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2: uJolietLevel = 2; break;
2055 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3: uJolietLevel = 3; break;
2056 default: Log(("rtFsIsoImportProcessSupplementaryDesc: last joliet escape sequence byte doesn't match: %#x\n",
2057 pVolDesc->abEscapeSequences[2]));
2058 }
2059 if (uJolietLevel == 0)
2060 return VINF_SUCCESS;
2061
2062 /*
2063 * Only one joliet descriptor.
2064 */
2065 if (pThis->fSeenJoliet)
2066 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_JOLIET_VOL_DESCS,
2067 "More than one Joliet volume descriptor is not supported");
2068 pThis->fSeenJoliet = true;
2069
2070 /*
2071 * Import stuff if present and not opted out.
2072 */
2073 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_SYSTEM_ID))
2074 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId),
2075 RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
2076 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_ID))
2077 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId),
2078 RTFSISOMAKERSTRINGPROP_VOLUME_ID);
2079 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_SET_ID))
2080 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId),
2081 RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
2082 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_PUBLISHER_ID))
2083 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId),
2084 RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
2085 if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_DATA_PREPARER_ID)
2086 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId),
2087 RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
2088 if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_APPLICATION_ID)
2089 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId),
2090 RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
2091 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_COPYRIGHT_FID))
2092 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId),
2093 RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
2094 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_ABSTRACT_FID))
2095 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId),
2096 RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
2097 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_BIBLIO_FID))
2098 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId),
2099 RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
2100
2101 /*
2102 * Process the directory tree.
2103 */
2104 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_JOLIET))
2105 return rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
2106 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), true /*fUnicode*/);
2107 return VINF_SUCCESS;
2108}
2109
2110
2111/**
2112 * Checks out an El Torito boot image to see if it requires info table patching.
2113 *
2114 * @returns IPRT status code (ignored).
2115 * @param pThis The ISO importer instance.
2116 * @param idxImageObj The configuration index of the image.
2117 * @param offBootImage The block offset of the image.
2118 */
2119static int rtFsIsoImportProcessElToritoImage(PRTFSISOMKIMPORTER pThis, uint32_t idxImageObj, uint32_t offBootImage)
2120{
2121 ISO9660SYSLINUXINFOTABLE InfoTable;
2122 int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootImage * (uint64_t)ISO9660_SECTOR_SIZE + ISO9660SYSLINUXINFOTABLE_OFFSET,
2123 &InfoTable, sizeof(InfoTable), NULL);
2124 if (RT_SUCCESS(rc))
2125 {
2126 if ( RT_LE2H_U32(InfoTable.offBootFile) == offBootImage
2127 && RT_LE2H_U32(InfoTable.offPrimaryVolDesc) == pThis->offPrimaryVolDesc
2128 && ASMMemIsAllU8(&InfoTable.auReserved[0], sizeof(InfoTable.auReserved), 0) )
2129 {
2130 rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pThis->hIsoMaker, idxImageObj, true /*fEnable*/);
2131 if (RT_FAILURE(rc))
2132 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjEnableBootInfoTablePatching failed: %Rrc", rc);
2133 }
2134 }
2135 return VINF_SUCCESS;
2136}
2137
2138
2139/**
2140 * Processes a boot catalog default or section entry.
2141 *
2142 * @returns IPRT status code (ignored).
2143 * @param pThis The ISO importer instance.
2144 * @param iEntry The boot catalog entry number. This is 1 for
2145 * the default entry, and 3+ for section entries.
2146 * @param cMaxEntries Maximum number of entries.
2147 * @param pEntry The entry to process.
2148 * @param pcSkip Where to return the number of extension entries to skip.
2149 */
2150static int rtFsIsoImportProcessElToritoSectionEntry(PRTFSISOMKIMPORTER pThis, uint32_t iEntry, uint32_t cMaxEntries,
2151 PCISO9660ELTORITOSECTIONENTRY pEntry, uint32_t *pcSkip)
2152{
2153 *pcSkip = 0;
2154
2155 /*
2156 * Check the boot indicator type for entry 1.
2157 */
2158 if ( pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
2159 && pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
2160 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_BOOT_IND,
2161 "Default boot catalog entry has an invalid boot indicator: %#x", pEntry->bBootIndicator);
2162
2163 /*
2164 * Check the media type and flags.
2165 */
2166 uint32_t cbDefaultSize;
2167 uint8_t bMediaType = pEntry->bBootMediaType;
2168 switch (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
2169 {
2170 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB:
2171 cbDefaultSize = 512 * 80 * 15 * 2;
2172 break;
2173
2174 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB:
2175 cbDefaultSize = 512 * 80 * 18 * 2;
2176 break;
2177
2178 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB:
2179 cbDefaultSize = 512 * 80 * 36 * 2;
2180 break;
2181
2182 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION:
2183 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK:
2184 cbDefaultSize = 0;
2185 break;
2186
2187 default:
2188 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_INVALID_BOOT_MEDIA_TYPE,
2189 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2190 }
2191
2192 if (iEntry == 1)
2193 {
2194 if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_MASK)
2195 {
2196 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_FLAGS,
2197 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2198 bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_MASK;
2199 }
2200 }
2201 else
2202 {
2203 if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED)
2204 {
2205 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_RESERVED_FLAG,
2206 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2207 bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED;
2208 }
2209 }
2210
2211 /*
2212 * Complain if bUnused is used.
2213 */
2214 if (pEntry->bUnused != 0)
2215 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_USES_UNUSED_FIELD,
2216 "Boot catalog entry #%#x has a non-zero unused field: %#x", pEntry->bUnused);
2217
2218 /*
2219 * Check out the boot image offset and turn that into an index of a file
2220 */
2221 uint32_t offBootImage = RT_LE2H_U32(pEntry->offBootImage);
2222 if (offBootImage >= pThis->cBlocksInSrcFile)
2223 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_IMAGE_OUT_OF_BOUNDS,
2224 "Boot catalog entry #%#x has an out of bound boot image block number: %#RX32, max %#RX32",
2225 offBootImage, pThis->cBlocksInPrimaryVolumeSpace);
2226
2227 int rc;
2228 uint32_t idxImageObj;
2229 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootImage);
2230 if (pBlock2File)
2231 idxImageObj = pBlock2File->idxObj;
2232 else
2233 {
2234 if (cbDefaultSize == 0)
2235 {
2236 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32GetBestFit(&pThis->Block2FileRoot, offBootImage, true /*fAbove*/);
2237 if (pBlock2File)
2238 cbDefaultSize = RT_MIN(pBlock2File->Core.Key - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
2239 * ISO9660_SECTOR_SIZE;
2240 else if (offBootImage < pThis->cBlocksInSrcFile)
2241 cbDefaultSize = RT_MIN(pThis->cBlocksInSrcFile - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
2242 * ISO9660_SECTOR_SIZE;
2243 else
2244 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_UNKNOWN_IMAGE_SIZE,
2245 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2246 }
2247
2248 if (pThis->idxSrcFile != UINT32_MAX)
2249 {
2250 rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
2251 if (RT_FAILURE(rc))
2252 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
2253 Assert(pThis->idxSrcFile != UINT32_MAX);
2254 }
2255
2256 rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
2257 offBootImage * (uint64_t)ISO9660_SECTOR_SIZE,
2258 cbDefaultSize, NULL, &idxImageObj);
2259 if (RT_FAILURE(rc))
2260 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddUnnamedFileWithCommonSrc failed on boot entry #%#x: %Rrc",
2261 iEntry, rc);
2262 }
2263
2264 /*
2265 * Deal with selection criteria. Use the last sector of abBuf to gather it
2266 * into a single data chunk.
2267 */
2268 size_t cbSelCrit = 0;
2269 uint8_t *pbSelCrit = &pThis->abBuf[sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE];
2270 if (pEntry->bSelectionCriteriaType != ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE)
2271 {
2272 memcpy(pbSelCrit, pEntry->abSelectionCriteria, sizeof(pEntry->abSelectionCriteria));
2273 cbSelCrit = sizeof(pEntry->abSelectionCriteria);
2274
2275 if ( (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
2276 && iEntry + 1 < cMaxEntries)
2277 {
2278 uint32_t iExtEntry = iEntry + 1;
2279 PCISO9660ELTORITOSECTIONENTRYEXT pExtEntry = (PCISO9660ELTORITOSECTIONENTRYEXT)pEntry;
2280 for (;;)
2281 {
2282 pExtEntry++;
2283
2284 if (pExtEntry->bExtensionId != ISO9660_ELTORITO_SECTION_ENTRY_EXT_ID)
2285 {
2286 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_INVALID_ID,
2287 "Invalid header ID for extension entry #%#x: %#x", iExtEntry, pExtEntry->bExtensionId);
2288 break;
2289 }
2290 *pcSkip += 1;
2291
2292 memcpy(&pbSelCrit[cbSelCrit], pExtEntry->abSelectionCriteria, sizeof(pExtEntry->abSelectionCriteria));
2293 cbSelCrit += sizeof(pExtEntry->abSelectionCriteria);
2294
2295 if (pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_UNUSED_MASK)
2296 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_UNDEFINED_FLAGS,
2297 "Boot catalog extension entry #%#x uses undefined flags: %#x", iExtEntry, pExtEntry->fFlags);
2298
2299 iExtEntry++;
2300 if (!(pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_MORE))
2301 break;
2302 if (iExtEntry >= cMaxEntries)
2303 {
2304 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_END_OF_SECTOR,
2305 "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
2306 break;
2307 }
2308 }
2309 Assert(*pcSkip = iExtEntry - iEntry);
2310 }
2311 else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
2312 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_EOS,
2313 "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
2314 }
2315 else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
2316 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_WITH_NONE,
2317 "Boot catalog entry #%#x uses the continuation flag with selection criteria NONE", iEntry);
2318
2319 /*
2320 * Add the entry.
2321 */
2322 rc = RTFsIsoMakerBootCatSetSectionEntry(pThis->hIsoMaker, iEntry, idxImageObj, bMediaType, pEntry->bSystemType,
2323 pEntry->bBootIndicator == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE,
2324 pEntry->uLoadSeg, pEntry->cEmulatedSectorsToLoad,
2325 pEntry->bSelectionCriteriaType, pbSelCrit, cbSelCrit);
2326 if (RT_SUCCESS(rc))
2327 {
2328 pThis->pResults->cBootCatEntries += 1 + *pcSkip;
2329 rc = rtFsIsoImportProcessElToritoImage(pThis, idxImageObj, offBootImage);
2330 }
2331 else
2332 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetSectionEntry failed for entry #%#x: %Rrc", iEntry, rc);
2333 return rc;
2334}
2335
2336
2337
2338/**
2339 * Processes a boot catalog section header entry.
2340 *
2341 * @returns IPRT status code (ignored).
2342 * @param pThis The ISO importer instance.
2343 * @param iEntry The boot catalog entry number.
2344 * @param pEntry The entry to process.
2345 */
2346static int rtFsIsoImportProcessElToritoSectionHeader(PRTFSISOMKIMPORTER pThis, uint32_t iEntry,
2347 PCISO9660ELTORITOSECTIONHEADER pEntry, char pszId[32])
2348{
2349 Assert(pEntry->bHeaderId == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER);
2350
2351 /* Deal with the string. ASSUME it doesn't contain zeros in non-terminal positions. */
2352 if (pEntry->achSectionId[0] == '\0')
2353 pszId = NULL;
2354 else
2355 {
2356 memcpy(pszId, pEntry->achSectionId, sizeof(pEntry->achSectionId));
2357 pszId[sizeof(pEntry->achSectionId)] = '\0';
2358 }
2359
2360 int rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pThis->hIsoMaker, iEntry, RT_LE2H_U16(pEntry->cEntries),
2361 pEntry->bPlatformId, pszId);
2362 if (RT_SUCCESS(rc))
2363 pThis->pResults->cBootCatEntries++;
2364 else
2365 rtFsIsoImpError(pThis, rc,
2366 "RTFsIsoMakerBootCatSetSectionHeaderEntry failed for entry #%#x (bPlatformId=%#x cEntries=%#x): %Rrc",
2367 iEntry, RT_LE2H_U16(pEntry->cEntries), pEntry->bPlatformId, rc);
2368 return rc;
2369}
2370
2371
2372/**
2373 * Processes a El Torito volume descriptor.
2374 *
2375 * @returns IPRT status code (ignorable).
2376 * @param pThis The ISO importer instance.
2377 * @param pVolDesc The volume descriptor to process.
2378 */
2379static int rtFsIsoImportProcessElToritoDesc(PRTFSISOMKIMPORTER pThis, PISO9660BOOTRECORDELTORITO pVolDesc)
2380{
2381 /*
2382 * Read the boot catalog into the abBuf.
2383 */
2384 uint32_t offBootCatalog = RT_LE2H_U32(pVolDesc->offBootCatalog);
2385 if (offBootCatalog >= pThis->cBlocksInPrimaryVolumeSpace)
2386 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_OUT_OF_BOUNDS,
2387 "Boot catalog block number is out of bounds: %#RX32, max %#RX32",
2388 offBootCatalog, pThis->cBlocksInPrimaryVolumeSpace);
2389
2390 int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootCatalog * (uint64_t)ISO9660_SECTOR_SIZE,
2391 pThis->abBuf, ISO9660_SECTOR_SIZE, NULL);
2392 if (RT_FAILURE(rc))
2393 return rtFsIsoImpError(pThis, rc, "Error reading boot catalog at block #%#RX32: %Rrc", offBootCatalog, rc);
2394
2395
2396 /*
2397 * Process the 'validation entry'.
2398 */
2399 PCISO9660ELTORITOVALIDATIONENTRY pValEntry = (PCISO9660ELTORITOVALIDATIONENTRY)&pThis->abBuf[0];
2400 if (pValEntry->bHeaderId != ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY)
2401 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_HEADER_ID,
2402 "Invalid boot catalog validation entry header ID: %#x, expected %#x",
2403 pValEntry->bHeaderId, ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY);
2404
2405 if ( pValEntry->bKey1 != ISO9660_ELTORITO_KEY_BYTE_1
2406 || pValEntry->bKey2 != ISO9660_ELTORITO_KEY_BYTE_2)
2407 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_KEYS,
2408 "Invalid boot catalog validation entry keys: %#x %#x, expected %#x %#x",
2409 pValEntry->bKey1, pValEntry->bKey2, ISO9660_ELTORITO_KEY_BYTE_1, ISO9660_ELTORITO_KEY_BYTE_2);
2410
2411 /* Check the checksum (should sum up to be zero). */
2412 uint16_t uChecksum = 0;
2413 uint16_t const *pu16 = (uint16_t const *)pValEntry;
2414 size_t cLeft = sizeof(*pValEntry) / sizeof(uint16_t);
2415 while (cLeft-- > 0)
2416 {
2417 uChecksum += RT_LE2H_U16(*pu16);
2418 pu16++;
2419 }
2420 if (uChecksum != 0)
2421 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_CHECKSUM,
2422 "Invalid boot catalog validation entry checksum: %#x, expected 0", uChecksum);
2423
2424 /* The string ID. ASSUME no leading zeros in valid strings. */
2425 const char *pszId = NULL;
2426 char szId[32];
2427 if (pValEntry->achId[0] != '\0')
2428 {
2429 memcpy(szId, pValEntry->achId, sizeof(pValEntry->achId));
2430 szId[sizeof(pValEntry->achId)] = '\0';
2431 pszId = szId;
2432 }
2433
2434 /*
2435 * Before we tell the ISO maker about the validation entry, we need to sort
2436 * out the file backing the boot catalog. This isn't fatal if it fails.
2437 */
2438 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootCatalog);
2439 if (pBlock2File)
2440 {
2441 rc = RTFsIsoMakerBootCatSetFile(pThis->hIsoMaker, pBlock2File->idxObj);
2442 if (RT_FAILURE(rc))
2443 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetFile failed: %Rrc", rc);
2444 }
2445
2446 /*
2447 * Set the validation entry.
2448 */
2449 rc = RTFsIsoMakerBootCatSetValidationEntry(pThis->hIsoMaker, pValEntry->bPlatformId, pszId);
2450 if (RT_FAILURE(rc))
2451 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetValidationEntry(,%#x,%s) failed: %Rrc",
2452 pValEntry->bPlatformId, pszId);
2453 Assert(pThis->pResults->cBootCatEntries == UINT32_MAX);
2454 pThis->pResults->cBootCatEntries = 0;
2455
2456 /*
2457 * Process the default entry and any subsequent entries.
2458 */
2459 bool fSeenFinal = false;
2460 uint32_t const cMaxEntries = ISO9660_SECTOR_SIZE / ISO9660_ELTORITO_ENTRY_SIZE;
2461 for (uint32_t iEntry = 1; iEntry < cMaxEntries; iEntry++)
2462 {
2463 uint8_t const *pbEntry = &pThis->abBuf[iEntry * ISO9660_ELTORITO_ENTRY_SIZE];
2464 uint8_t const idHeader = *pbEntry;
2465
2466 /* KLUDGE ALERT! Older ISO images, like RHEL5-Server-20070208.0-x86_64-DVD.iso lacks
2467 terminator entry. So, quietly stop with an entry that's all zeros. */
2468 if ( idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE /* 0x00 */
2469 && iEntry != 1 /* default */
2470 && ASMMemIsZero(pbEntry, ISO9660_ELTORITO_ENTRY_SIZE))
2471 return rc;
2472
2473 if ( iEntry == 1 /* default*/
2474 || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
2475 || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
2476 {
2477 uint32_t cSkip = 0;
2478 rtFsIsoImportProcessElToritoSectionEntry(pThis, iEntry, cMaxEntries, (PCISO9660ELTORITOSECTIONENTRY)pbEntry, &cSkip);
2479 iEntry += cSkip;
2480 }
2481 else if (idHeader == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER)
2482 rtFsIsoImportProcessElToritoSectionHeader(pThis, iEntry, (PCISO9660ELTORITOSECTIONHEADER)pbEntry, szId);
2483 else if (idHeader == ISO9660_ELTORITO_HEADER_ID_FINAL_SECTION_HEADER)
2484 {
2485 fSeenFinal = true;
2486 break;
2487 }
2488 else
2489 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_UNKNOWN_HEADER_ID,
2490 "Unknown boot catalog header ID for entry #%#x: %#x", iEntry, idHeader);
2491 }
2492
2493 if (!fSeenFinal)
2494 rc = rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_MISSING_FINAL_OR_TOO_BIG,
2495 "Boot catalog is probably larger than a sector, or it's missing the final section header entry");
2496 return rc;
2497}
2498
2499
2500/**
2501 * Imports an existing ISO.
2502 *
2503 * Just like other source files, the existing image must remain present and
2504 * unmodified till the ISO maker is done with it.
2505 *
2506 * @returns IRPT status code.
2507 * @param hIsoMaker The ISO maker handle.
2508 * @param hIsoFile VFS file handle to the existing image to import / clone.
2509 * @param fFlags Reserved for the future, MBZ.
2510 * @param poffError Where to return the position in @a pszIso
2511 * causing trouble when opening it for reading.
2512 * Optional.
2513 * @param pErrInfo Where to return additional error information.
2514 * Optional.
2515 */
2516RTDECL(int) RTFsIsoMakerImport(RTFSISOMAKER hIsoMaker, RTVFSFILE hIsoFile, uint32_t fFlags,
2517 PRTFSISOMAKERIMPORTRESULTS pResults, PRTERRINFO pErrInfo)
2518{
2519 /*
2520 * Validate input.
2521 */
2522 AssertPtrReturn(pResults, VERR_INVALID_POINTER);
2523 pResults->cAddedNames = 0;
2524 pResults->cAddedDirs = 0;
2525 pResults->cbAddedDataBlocks = 0;
2526 pResults->cAddedFiles = 0;
2527 pResults->cAddedSymlinks = 0;
2528 pResults->cBootCatEntries = UINT32_MAX;
2529 pResults->cbSysArea = 0;
2530 pResults->cErrors = 0;
2531 AssertReturn(!(fFlags & ~RTFSISOMK_IMPORT_F_VALID_MASK), VERR_INVALID_FLAGS);
2532
2533 /*
2534 * Get the file size.
2535 */
2536 uint64_t cbSrcFile = 0;
2537 int rc = RTVfsFileQuerySize(hIsoFile, &cbSrcFile);
2538 if (RT_SUCCESS(rc))
2539 {
2540 /*
2541 * Allocate and init the importer state.
2542 */
2543 PRTFSISOMKIMPORTER pThis = (PRTFSISOMKIMPORTER)RTMemAllocZ(sizeof(*pThis));
2544 if (pThis)
2545 {
2546 pThis->hIsoMaker = hIsoMaker;
2547 pThis->fFlags = fFlags;
2548 pThis->rc = VINF_SUCCESS;
2549 pThis->pErrInfo = pErrInfo;
2550 pThis->hSrcFile = hIsoFile;
2551 pThis->cbSrcFile = cbSrcFile;
2552 pThis->cBlocksInSrcFile = cbSrcFile / ISO9660_SECTOR_SIZE;
2553 pThis->idxSrcFile = UINT32_MAX;
2554 //pThis->Block2FileRoot = NULL;
2555 //pThis->cBlocksInPrimaryVolumeSpace = 0;
2556 //pThis->cbPrimaryVolumeSpace = 0
2557 //pThis->cVolumesInSet = 0;
2558 //pThis->idPrimaryVol = 0;
2559 //pThis->fSeenJoliet = false;
2560 pThis->pResults = pResults;
2561 //pThis->fSuspSeenSP = false;
2562 //pThis->offSuspSkip = 0;
2563 pThis->offRockBuf = UINT64_MAX;
2564
2565 /*
2566 * Check if this looks like a plausible ISO by checking out the first volume descriptor.
2567 */
2568 rc = RTVfsFileReadAt(hIsoFile, _32K, &pThis->uSectorBuf.PrimVolDesc, sizeof(pThis->uSectorBuf.PrimVolDesc), NULL);
2569 if (RT_SUCCESS(rc))
2570 {
2571 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] == ISO9660VOLDESC_STD_ID_0
2572 && pThis->uSectorBuf.VolDescHdr.achStdId[1] == ISO9660VOLDESC_STD_ID_1
2573 && pThis->uSectorBuf.VolDescHdr.achStdId[2] == ISO9660VOLDESC_STD_ID_2
2574 && pThis->uSectorBuf.VolDescHdr.achStdId[3] == ISO9660VOLDESC_STD_ID_3
2575 && pThis->uSectorBuf.VolDescHdr.achStdId[4] == ISO9660VOLDESC_STD_ID_4
2576 && ( pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY
2577 || pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD) )
2578 {
2579 /*
2580 * Process the volume descriptors using the sector buffer, starting
2581 * with the one we've already got sitting there. We postpone processing
2582 * the el torito one till after the others, so we can name files and size
2583 * referenced in it.
2584 */
2585 uint32_t cPrimaryVolDescs = 0;
2586 uint32_t iElTorito = UINT32_MAX;
2587 uint32_t iVolDesc = 0;
2588 for (;;)
2589 {
2590 switch (pThis->uSectorBuf.VolDescHdr.bDescType)
2591 {
2592 case ISO9660VOLDESC_TYPE_PRIMARY:
2593 cPrimaryVolDescs++;
2594 if (cPrimaryVolDescs == 1)
2595 {
2596 pThis->offPrimaryVolDesc = _32K / ISO9660_SECTOR_SIZE + iVolDesc;
2597 rtFsIsoImportProcessPrimaryDesc(pThis, &pThis->uSectorBuf.PrimVolDesc);
2598 }
2599 else
2600 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS,
2601 "Only a single primary volume descriptor is currently supported");
2602 break;
2603
2604 case ISO9660VOLDESC_TYPE_SUPPLEMENTARY:
2605 if (cPrimaryVolDescs > 0)
2606 rtFsIsoImportProcessSupplementaryDesc(pThis, &pThis->uSectorBuf.SupVolDesc);
2607 else
2608 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY,
2609 "Primary volume descriptor expected before any supplementary descriptors!");
2610 break;
2611
2612 case ISO9660VOLDESC_TYPE_BOOT_RECORD:
2613 if (strcmp(pThis->uSectorBuf.ElToritoDesc.achBootSystemId,
2614 ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID) == 0)
2615 {
2616 if (iElTorito == UINT32_MAX)
2617 iElTorito = iVolDesc;
2618 else
2619 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS,
2620 "Only a single El Torito descriptor exepcted!");
2621 }
2622 break;
2623
2624 case ISO9660VOLDESC_TYPE_PARTITION:
2625 /* ignore for now */
2626 break;
2627
2628 case ISO9660VOLDESC_TYPE_TERMINATOR:
2629 AssertFailed();
2630 break;
2631 }
2632
2633
2634 /*
2635 * Read the next volume descriptor and check the signature.
2636 */
2637 iVolDesc++;
2638 if (iVolDesc >= 32)
2639 {
2640 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS, "Parses at most 32 volume descriptors");
2641 break;
2642 }
2643
2644 rc = RTVfsFileReadAt(hIsoFile, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
2645 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
2646 if (RT_FAILURE(rc))
2647 {
2648 rtFsIsoImpError(pThis, rc, "Error reading the volume descriptor #%u at %#RX32: %Rrc",
2649 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE, rc);
2650 break;
2651 }
2652
2653 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
2654 || pThis->uSectorBuf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
2655 || pThis->uSectorBuf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
2656 || pThis->uSectorBuf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
2657 || pThis->uSectorBuf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
2658 {
2659 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR,
2660 "Invalid volume descriptor header #%u at %#RX32: %.*Rhxs",
2661 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
2662 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
2663 break;
2664 }
2665 /** @todo UDF support. */
2666 if (pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
2667 break;
2668 }
2669
2670 /*
2671 * Process the system area.
2672 */
2673 if (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX)
2674 {
2675 rc = RTVfsFileReadAt(hIsoFile, 0, pThis->abBuf, _32K, NULL);
2676 if (RT_SUCCESS(rc))
2677 {
2678 if (!ASMMemIsAllU8(pThis->abBuf, _32K, 0))
2679 {
2680 /* Drop zero sectors from the end. */
2681 uint32_t cbSysArea = _32K;
2682 while ( cbSysArea >= ISO9660_SECTOR_SIZE
2683 && ASMMemIsAllU8(&pThis->abBuf[cbSysArea - ISO9660_SECTOR_SIZE], ISO9660_SECTOR_SIZE, 0))
2684 cbSysArea -= ISO9660_SECTOR_SIZE;
2685
2686 /** @todo HFS */
2687 pThis->pResults->cbSysArea = cbSysArea;
2688 rc = RTFsIsoMakerSetSysAreaContent(hIsoMaker, pThis->abBuf, cbSysArea, 0);
2689 if (RT_FAILURE(rc))
2690 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetSysAreaContent failed: %Rrc", rc);
2691 }
2692 }
2693 else
2694 rtFsIsoImpError(pThis, rc, "Error reading the system area (0..32KB): %Rrc", rc);
2695 }
2696
2697 /*
2698 * Do the El Torito descriptor.
2699 */
2700 if ( iElTorito != UINT32_MAX
2701 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BOOT)
2702 && (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX))
2703 {
2704 rc = RTVfsFileReadAt(hIsoFile, _32K + iElTorito * ISO9660_SECTOR_SIZE,
2705 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
2706 if (RT_SUCCESS(rc))
2707 rtFsIsoImportProcessElToritoDesc(pThis, &pThis->uSectorBuf.ElToritoDesc);
2708 else
2709 rtFsIsoImpError(pThis, rc, "Error reading the El Torito volume descriptor at %#RX32: %Rrc",
2710 _32K + iElTorito * ISO9660_SECTOR_SIZE, rc);
2711 }
2712
2713 /*
2714 * Return the first error status.
2715 */
2716 rc = pThis->rc;
2717 }
2718 else
2719 rc = RTErrInfoSetF(pErrInfo, VERR_ISOMK_IMPORT_UNKNOWN_FORMAT, "Invalid volume descriptor header: %.*Rhxs",
2720 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
2721 }
2722
2723 /*
2724 * Destroy the state.
2725 */
2726 RTAvlU32Destroy(&pThis->Block2FileRoot, rtFsIsoMakerImportDestroyData2File, NULL);
2727 RTMemFree(pThis);
2728 }
2729 else
2730 rc = VERR_NO_MEMORY;
2731 }
2732 return rc;
2733}
2734
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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