VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 130.0 KB
Line 
1/* $Id: isomakerimport.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - ISO Image Maker, Import Existing Image.
4 */
5
6/*
7 * Copyright (C) 2017-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsisomaker.h>
34
35#include <iprt/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);
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);
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);
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 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
2021 "Volume space size differs between primary and supplementary descriptors: %#x, primary %#x",
2022 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
2023
2024 /*
2025 * Validate the root directory record.
2026 */
2027 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
2028 if (RT_FAILURE(rc))
2029 return rc;
2030
2031 /*
2032 * Is this a joliet descriptor? Ignore if not.
2033 */
2034 uint8_t uJolietLevel = 0;
2035 if ( pVolDesc->abEscapeSequences[0] == ISO9660_JOLIET_ESC_SEQ_0
2036 && pVolDesc->abEscapeSequences[1] == ISO9660_JOLIET_ESC_SEQ_1)
2037 switch (pVolDesc->abEscapeSequences[2])
2038 {
2039 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1: uJolietLevel = 1; break;
2040 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2: uJolietLevel = 2; break;
2041 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3: uJolietLevel = 3; break;
2042 default: Log(("rtFsIsoImportProcessSupplementaryDesc: last joliet escape sequence byte doesn't match: %#x\n",
2043 pVolDesc->abEscapeSequences[2]));
2044 }
2045 if (uJolietLevel == 0)
2046 return VINF_SUCCESS;
2047
2048 /*
2049 * Only one joliet descriptor.
2050 */
2051 if (pThis->fSeenJoliet)
2052 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_JOLIET_VOL_DESCS,
2053 "More than one Joliet volume descriptor is not supported");
2054 pThis->fSeenJoliet = true;
2055
2056 /*
2057 * Import stuff if present and not opted out.
2058 */
2059 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_SYSTEM_ID))
2060 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId),
2061 RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
2062 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_ID))
2063 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId),
2064 RTFSISOMAKERSTRINGPROP_VOLUME_ID);
2065 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_SET_ID))
2066 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId),
2067 RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
2068 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_PUBLISHER_ID))
2069 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId),
2070 RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
2071 if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_DATA_PREPARER_ID)
2072 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId),
2073 RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
2074 if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_APPLICATION_ID)
2075 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId),
2076 RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
2077 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_COPYRIGHT_FID))
2078 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId),
2079 RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
2080 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_ABSTRACT_FID))
2081 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId),
2082 RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
2083 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_BIBLIO_FID))
2084 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId),
2085 RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
2086
2087 /*
2088 * Process the directory tree.
2089 */
2090 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_JOLIET))
2091 return rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
2092 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), true /*fUnicode*/);
2093 return VINF_SUCCESS;
2094}
2095
2096
2097/**
2098 * Checks out an El Torito boot image to see if it requires info table patching.
2099 *
2100 * @returns IPRT status code (ignored).
2101 * @param pThis The ISO importer instance.
2102 * @param idxImageObj The configuration index of the image.
2103 * @param offBootImage The block offset of the image.
2104 */
2105static int rtFsIsoImportProcessElToritoImage(PRTFSISOMKIMPORTER pThis, uint32_t idxImageObj, uint32_t offBootImage)
2106{
2107 ISO9660SYSLINUXINFOTABLE InfoTable;
2108 int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootImage * (uint64_t)ISO9660_SECTOR_SIZE + ISO9660SYSLINUXINFOTABLE_OFFSET,
2109 &InfoTable, sizeof(InfoTable), NULL);
2110 if (RT_SUCCESS(rc))
2111 {
2112 if ( RT_LE2H_U32(InfoTable.offBootFile) == offBootImage
2113 && RT_LE2H_U32(InfoTable.offPrimaryVolDesc) == pThis->offPrimaryVolDesc
2114 && ASMMemIsAllU8(&InfoTable.auReserved[0], sizeof(InfoTable.auReserved), 0) )
2115 {
2116 rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pThis->hIsoMaker, idxImageObj, true /*fEnable*/);
2117 if (RT_FAILURE(rc))
2118 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjEnableBootInfoTablePatching failed: %Rrc", rc);
2119 }
2120 }
2121 return VINF_SUCCESS;
2122}
2123
2124
2125/**
2126 * Processes a boot catalog default or section entry.
2127 *
2128 * @returns IPRT status code (ignored).
2129 * @param pThis The ISO importer instance.
2130 * @param iEntry The boot catalog entry number. This is 1 for
2131 * the default entry, and 3+ for section entries.
2132 * @param cMaxEntries Maximum number of entries.
2133 * @param pEntry The entry to process.
2134 * @param pcSkip Where to return the number of extension entries to skip.
2135 */
2136static int rtFsIsoImportProcessElToritoSectionEntry(PRTFSISOMKIMPORTER pThis, uint32_t iEntry, uint32_t cMaxEntries,
2137 PCISO9660ELTORITOSECTIONENTRY pEntry, uint32_t *pcSkip)
2138{
2139 *pcSkip = 0;
2140
2141 /*
2142 * Check the boot indicator type for entry 1.
2143 */
2144 if ( pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
2145 && pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
2146 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_BOOT_IND,
2147 "Default boot catalog entry has an invalid boot indicator: %#x", pEntry->bBootIndicator);
2148
2149 /*
2150 * Check the media type and flags.
2151 */
2152 uint32_t cbDefaultSize;
2153 uint8_t bMediaType = pEntry->bBootMediaType;
2154 switch (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
2155 {
2156 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB:
2157 cbDefaultSize = 512 * 80 * 15 * 2;
2158 break;
2159
2160 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB:
2161 cbDefaultSize = 512 * 80 * 18 * 2;
2162 break;
2163
2164 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB:
2165 cbDefaultSize = 512 * 80 * 36 * 2;
2166 break;
2167
2168 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION:
2169 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK:
2170 cbDefaultSize = 0;
2171 break;
2172
2173 default:
2174 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_INVALID_BOOT_MEDIA_TYPE,
2175 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2176 }
2177
2178 if (iEntry == 1)
2179 {
2180 if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_MASK)
2181 {
2182 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_FLAGS,
2183 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2184 bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_MASK;
2185 }
2186 }
2187 else
2188 {
2189 if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED)
2190 {
2191 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_RESERVED_FLAG,
2192 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2193 bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED;
2194 }
2195 }
2196
2197 /*
2198 * Complain if bUnused is used.
2199 */
2200 if (pEntry->bUnused != 0)
2201 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_USES_UNUSED_FIELD,
2202 "Boot catalog entry #%#x has a non-zero unused field: %#x", pEntry->bUnused);
2203
2204 /*
2205 * Check out the boot image offset and turn that into an index of a file
2206 */
2207 uint32_t offBootImage = RT_LE2H_U32(pEntry->offBootImage);
2208 if (offBootImage >= pThis->cBlocksInSrcFile)
2209 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_IMAGE_OUT_OF_BOUNDS,
2210 "Boot catalog entry #%#x has an out of bound boot image block number: %#RX32, max %#RX32",
2211 offBootImage, pThis->cBlocksInPrimaryVolumeSpace);
2212
2213 int rc;
2214 uint32_t idxImageObj;
2215 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootImage);
2216 if (pBlock2File)
2217 idxImageObj = pBlock2File->idxObj;
2218 else
2219 {
2220 if (cbDefaultSize == 0)
2221 {
2222 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32GetBestFit(&pThis->Block2FileRoot, offBootImage, true /*fAbove*/);
2223 if (pBlock2File)
2224 cbDefaultSize = RT_MIN(pBlock2File->Core.Key - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
2225 * ISO9660_SECTOR_SIZE;
2226 else if (offBootImage < pThis->cBlocksInSrcFile)
2227 cbDefaultSize = RT_MIN(pThis->cBlocksInSrcFile - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
2228 * ISO9660_SECTOR_SIZE;
2229 else
2230 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_UNKNOWN_IMAGE_SIZE,
2231 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
2232 }
2233
2234 if (pThis->idxSrcFile != UINT32_MAX)
2235 {
2236 rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
2237 if (RT_FAILURE(rc))
2238 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
2239 Assert(pThis->idxSrcFile != UINT32_MAX);
2240 }
2241
2242 rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
2243 offBootImage * (uint64_t)ISO9660_SECTOR_SIZE,
2244 cbDefaultSize, NULL, &idxImageObj);
2245 if (RT_FAILURE(rc))
2246 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddUnnamedFileWithCommonSrc failed on boot entry #%#x: %Rrc",
2247 iEntry, rc);
2248 }
2249
2250 /*
2251 * Deal with selection criteria. Use the last sector of abBuf to gather it
2252 * into a single data chunk.
2253 */
2254 size_t cbSelCrit = 0;
2255 uint8_t *pbSelCrit = &pThis->abBuf[sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE];
2256 if (pEntry->bSelectionCriteriaType != ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE)
2257 {
2258 memcpy(pbSelCrit, pEntry->abSelectionCriteria, sizeof(pEntry->abSelectionCriteria));
2259 cbSelCrit = sizeof(pEntry->abSelectionCriteria);
2260
2261 if ( (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
2262 && iEntry + 1 < cMaxEntries)
2263 {
2264 uint32_t iExtEntry = iEntry + 1;
2265 PCISO9660ELTORITOSECTIONENTRYEXT pExtEntry = (PCISO9660ELTORITOSECTIONENTRYEXT)pEntry;
2266 for (;;)
2267 {
2268 pExtEntry++;
2269
2270 if (pExtEntry->bExtensionId != ISO9660_ELTORITO_SECTION_ENTRY_EXT_ID)
2271 {
2272 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_INVALID_ID,
2273 "Invalid header ID for extension entry #%#x: %#x", iExtEntry, pExtEntry->bExtensionId);
2274 break;
2275 }
2276 *pcSkip += 1;
2277
2278 memcpy(&pbSelCrit[cbSelCrit], pExtEntry->abSelectionCriteria, sizeof(pExtEntry->abSelectionCriteria));
2279 cbSelCrit += sizeof(pExtEntry->abSelectionCriteria);
2280
2281 if (pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_UNUSED_MASK)
2282 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_UNDEFINED_FLAGS,
2283 "Boot catalog extension entry #%#x uses undefined flags: %#x", iExtEntry, pExtEntry->fFlags);
2284
2285 iExtEntry++;
2286 if (!(pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_MORE))
2287 break;
2288 if (iExtEntry >= cMaxEntries)
2289 {
2290 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_END_OF_SECTOR,
2291 "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
2292 break;
2293 }
2294 }
2295 Assert(*pcSkip = iExtEntry - iEntry);
2296 }
2297 else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
2298 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_EOS,
2299 "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
2300 }
2301 else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
2302 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_WITH_NONE,
2303 "Boot catalog entry #%#x uses the continuation flag with selection criteria NONE", iEntry);
2304
2305 /*
2306 * Add the entry.
2307 */
2308 rc = RTFsIsoMakerBootCatSetSectionEntry(pThis->hIsoMaker, iEntry, idxImageObj, bMediaType, pEntry->bSystemType,
2309 pEntry->bBootIndicator == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE,
2310 pEntry->uLoadSeg, pEntry->cEmulatedSectorsToLoad,
2311 pEntry->bSelectionCriteriaType, pbSelCrit, cbSelCrit);
2312 if (RT_SUCCESS(rc))
2313 {
2314 pThis->pResults->cBootCatEntries += 1 + *pcSkip;
2315 rc = rtFsIsoImportProcessElToritoImage(pThis, idxImageObj, offBootImage);
2316 }
2317 else
2318 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetSectionEntry failed for entry #%#x: %Rrc", iEntry, rc);
2319 return rc;
2320}
2321
2322
2323
2324/**
2325 * Processes a boot catalog section header entry.
2326 *
2327 * @returns IPRT status code (ignored).
2328 * @param pThis The ISO importer instance.
2329 * @param iEntry The boot catalog entry number.
2330 * @param pEntry The entry to process.
2331 */
2332static int rtFsIsoImportProcessElToritoSectionHeader(PRTFSISOMKIMPORTER pThis, uint32_t iEntry,
2333 PCISO9660ELTORITOSECTIONHEADER pEntry, char pszId[32])
2334{
2335 Assert(pEntry->bHeaderId == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER);
2336
2337 /* Deal with the string. ASSUME it doesn't contain zeros in non-terminal positions. */
2338 if (pEntry->achSectionId[0] == '\0')
2339 pszId = NULL;
2340 else
2341 {
2342 memcpy(pszId, pEntry->achSectionId, sizeof(pEntry->achSectionId));
2343 pszId[sizeof(pEntry->achSectionId)] = '\0';
2344 }
2345
2346 int rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pThis->hIsoMaker, iEntry, RT_LE2H_U16(pEntry->cEntries),
2347 pEntry->bPlatformId, pszId);
2348 if (RT_SUCCESS(rc))
2349 pThis->pResults->cBootCatEntries++;
2350 else
2351 rtFsIsoImpError(pThis, rc,
2352 "RTFsIsoMakerBootCatSetSectionHeaderEntry failed for entry #%#x (bPlatformId=%#x cEntries=%#x): %Rrc",
2353 iEntry, RT_LE2H_U16(pEntry->cEntries), pEntry->bPlatformId, rc);
2354 return rc;
2355}
2356
2357
2358/**
2359 * Processes a El Torito volume descriptor.
2360 *
2361 * @returns IPRT status code (ignorable).
2362 * @param pThis The ISO importer instance.
2363 * @param pVolDesc The volume descriptor to process.
2364 */
2365static int rtFsIsoImportProcessElToritoDesc(PRTFSISOMKIMPORTER pThis, PISO9660BOOTRECORDELTORITO pVolDesc)
2366{
2367 /*
2368 * Read the boot catalog into the abBuf.
2369 */
2370 uint32_t offBootCatalog = RT_LE2H_U32(pVolDesc->offBootCatalog);
2371 if (offBootCatalog >= pThis->cBlocksInPrimaryVolumeSpace)
2372 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_OUT_OF_BOUNDS,
2373 "Boot catalog block number is out of bounds: %#RX32, max %#RX32",
2374 offBootCatalog, pThis->cBlocksInPrimaryVolumeSpace);
2375
2376 int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootCatalog * (uint64_t)ISO9660_SECTOR_SIZE,
2377 pThis->abBuf, ISO9660_SECTOR_SIZE, NULL);
2378 if (RT_FAILURE(rc))
2379 return rtFsIsoImpError(pThis, rc, "Error reading boot catalog at block #%#RX32: %Rrc", offBootCatalog, rc);
2380
2381
2382 /*
2383 * Process the 'validation entry'.
2384 */
2385 PCISO9660ELTORITOVALIDATIONENTRY pValEntry = (PCISO9660ELTORITOVALIDATIONENTRY)&pThis->abBuf[0];
2386 if (pValEntry->bHeaderId != ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY)
2387 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_HEADER_ID,
2388 "Invalid boot catalog validation entry header ID: %#x, expected %#x",
2389 pValEntry->bHeaderId, ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY);
2390
2391 if ( pValEntry->bKey1 != ISO9660_ELTORITO_KEY_BYTE_1
2392 || pValEntry->bKey2 != ISO9660_ELTORITO_KEY_BYTE_2)
2393 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_KEYS,
2394 "Invalid boot catalog validation entry keys: %#x %#x, expected %#x %#x",
2395 pValEntry->bKey1, pValEntry->bKey2, ISO9660_ELTORITO_KEY_BYTE_1, ISO9660_ELTORITO_KEY_BYTE_2);
2396
2397 /* Check the checksum (should sum up to be zero). */
2398 uint16_t uChecksum = 0;
2399 uint16_t const *pu16 = (uint16_t const *)pValEntry;
2400 size_t cLeft = sizeof(*pValEntry) / sizeof(uint16_t);
2401 while (cLeft-- > 0)
2402 {
2403 uChecksum += RT_LE2H_U16(*pu16);
2404 pu16++;
2405 }
2406 if (uChecksum != 0)
2407 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_CHECKSUM,
2408 "Invalid boot catalog validation entry checksum: %#x, expected 0", uChecksum);
2409
2410 /* The string ID. ASSUME no leading zeros in valid strings. */
2411 const char *pszId = NULL;
2412 char szId[32];
2413 if (pValEntry->achId[0] != '\0')
2414 {
2415 memcpy(szId, pValEntry->achId, sizeof(pValEntry->achId));
2416 szId[sizeof(pValEntry->achId)] = '\0';
2417 pszId = szId;
2418 }
2419
2420 /*
2421 * Before we tell the ISO maker about the validation entry, we need to sort
2422 * out the file backing the boot catalog. This isn't fatal if it fails.
2423 */
2424 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootCatalog);
2425 if (pBlock2File)
2426 {
2427 rc = RTFsIsoMakerBootCatSetFile(pThis->hIsoMaker, pBlock2File->idxObj);
2428 if (RT_FAILURE(rc))
2429 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetFile failed: %Rrc", rc);
2430 }
2431
2432 /*
2433 * Set the validation entry.
2434 */
2435 rc = RTFsIsoMakerBootCatSetValidationEntry(pThis->hIsoMaker, pValEntry->bPlatformId, pszId);
2436 if (RT_FAILURE(rc))
2437 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetValidationEntry(,%#x,%s) failed: %Rrc",
2438 pValEntry->bPlatformId, pszId);
2439 Assert(pThis->pResults->cBootCatEntries == UINT32_MAX);
2440 pThis->pResults->cBootCatEntries = 0;
2441
2442 /*
2443 * Process the default entry and any subsequent entries.
2444 */
2445 bool fSeenFinal = false;
2446 uint32_t const cMaxEntries = ISO9660_SECTOR_SIZE / ISO9660_ELTORITO_ENTRY_SIZE;
2447 for (uint32_t iEntry = 1; iEntry < cMaxEntries; iEntry++)
2448 {
2449 uint8_t const *pbEntry = &pThis->abBuf[iEntry * ISO9660_ELTORITO_ENTRY_SIZE];
2450 uint8_t const idHeader = *pbEntry;
2451
2452 /* KLUDGE ALERT! Older ISO images, like RHEL5-Server-20070208.0-x86_64-DVD.iso lacks
2453 terminator entry. So, quietly stop with an entry that's all zeros. */
2454 if ( idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE /* 0x00 */
2455 && iEntry != 1 /* default */
2456 && ASMMemIsZero(pbEntry, ISO9660_ELTORITO_ENTRY_SIZE))
2457 return rc;
2458
2459 if ( iEntry == 1 /* default*/
2460 || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
2461 || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
2462 {
2463 uint32_t cSkip = 0;
2464 rtFsIsoImportProcessElToritoSectionEntry(pThis, iEntry, cMaxEntries, (PCISO9660ELTORITOSECTIONENTRY)pbEntry, &cSkip);
2465 iEntry += cSkip;
2466 }
2467 else if (idHeader == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER)
2468 rtFsIsoImportProcessElToritoSectionHeader(pThis, iEntry, (PCISO9660ELTORITOSECTIONHEADER)pbEntry, szId);
2469 else if (idHeader == ISO9660_ELTORITO_HEADER_ID_FINAL_SECTION_HEADER)
2470 {
2471 fSeenFinal = true;
2472 break;
2473 }
2474 else
2475 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_UNKNOWN_HEADER_ID,
2476 "Unknown boot catalog header ID for entry #%#x: %#x", iEntry, idHeader);
2477 }
2478
2479 if (!fSeenFinal)
2480 rc = rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_MISSING_FINAL_OR_TOO_BIG,
2481 "Boot catalog is probably larger than a sector, or it's missing the final section header entry");
2482 return rc;
2483}
2484
2485
2486/**
2487 * Imports an existing ISO.
2488 *
2489 * Just like other source files, the existing image must remain present and
2490 * unmodified till the ISO maker is done with it.
2491 *
2492 * @returns IRPT status code.
2493 * @param hIsoMaker The ISO maker handle.
2494 * @param hIsoFile VFS file handle to the existing image to import / clone.
2495 * @param fFlags Reserved for the future, MBZ.
2496 * @param poffError Where to return the position in @a pszIso
2497 * causing trouble when opening it for reading.
2498 * Optional.
2499 * @param pErrInfo Where to return additional error information.
2500 * Optional.
2501 */
2502RTDECL(int) RTFsIsoMakerImport(RTFSISOMAKER hIsoMaker, RTVFSFILE hIsoFile, uint32_t fFlags,
2503 PRTFSISOMAKERIMPORTRESULTS pResults, PRTERRINFO pErrInfo)
2504{
2505 /*
2506 * Validate input.
2507 */
2508 AssertPtrReturn(pResults, VERR_INVALID_POINTER);
2509 pResults->cAddedNames = 0;
2510 pResults->cAddedDirs = 0;
2511 pResults->cbAddedDataBlocks = 0;
2512 pResults->cAddedFiles = 0;
2513 pResults->cAddedSymlinks = 0;
2514 pResults->cBootCatEntries = UINT32_MAX;
2515 pResults->cbSysArea = 0;
2516 pResults->cErrors = 0;
2517 AssertReturn(!(fFlags & ~RTFSISOMK_IMPORT_F_VALID_MASK), VERR_INVALID_FLAGS);
2518
2519 /*
2520 * Get the file size.
2521 */
2522 uint64_t cbSrcFile = 0;
2523 int rc = RTVfsFileGetSize(hIsoFile, &cbSrcFile);
2524 if (RT_SUCCESS(rc))
2525 {
2526 /*
2527 * Allocate and init the importer state.
2528 */
2529 PRTFSISOMKIMPORTER pThis = (PRTFSISOMKIMPORTER)RTMemAllocZ(sizeof(*pThis));
2530 if (pThis)
2531 {
2532 pThis->hIsoMaker = hIsoMaker;
2533 pThis->fFlags = fFlags;
2534 pThis->rc = VINF_SUCCESS;
2535 pThis->pErrInfo = pErrInfo;
2536 pThis->hSrcFile = hIsoFile;
2537 pThis->cbSrcFile = cbSrcFile;
2538 pThis->cBlocksInSrcFile = cbSrcFile / ISO9660_SECTOR_SIZE;
2539 pThis->idxSrcFile = UINT32_MAX;
2540 //pThis->Block2FileRoot = NULL;
2541 //pThis->cBlocksInPrimaryVolumeSpace = 0;
2542 //pThis->cbPrimaryVolumeSpace = 0
2543 //pThis->cVolumesInSet = 0;
2544 //pThis->idPrimaryVol = 0;
2545 //pThis->fSeenJoliet = false;
2546 pThis->pResults = pResults;
2547 //pThis->fSuspSeenSP = false;
2548 //pThis->offSuspSkip = 0;
2549 pThis->offRockBuf = UINT64_MAX;
2550
2551 /*
2552 * Check if this looks like a plausible ISO by checking out the first volume descriptor.
2553 */
2554 rc = RTVfsFileReadAt(hIsoFile, _32K, &pThis->uSectorBuf.PrimVolDesc, sizeof(pThis->uSectorBuf.PrimVolDesc), NULL);
2555 if (RT_SUCCESS(rc))
2556 {
2557 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] == ISO9660VOLDESC_STD_ID_0
2558 && pThis->uSectorBuf.VolDescHdr.achStdId[1] == ISO9660VOLDESC_STD_ID_1
2559 && pThis->uSectorBuf.VolDescHdr.achStdId[2] == ISO9660VOLDESC_STD_ID_2
2560 && pThis->uSectorBuf.VolDescHdr.achStdId[3] == ISO9660VOLDESC_STD_ID_3
2561 && pThis->uSectorBuf.VolDescHdr.achStdId[4] == ISO9660VOLDESC_STD_ID_4
2562 && ( pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY
2563 || pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD) )
2564 {
2565 /*
2566 * Process the volume descriptors using the sector buffer, starting
2567 * with the one we've already got sitting there. We postpone processing
2568 * the el torito one till after the others, so we can name files and size
2569 * referenced in it.
2570 */
2571 uint32_t cPrimaryVolDescs = 0;
2572 uint32_t iElTorito = UINT32_MAX;
2573 uint32_t iVolDesc = 0;
2574 for (;;)
2575 {
2576 switch (pThis->uSectorBuf.VolDescHdr.bDescType)
2577 {
2578 case ISO9660VOLDESC_TYPE_PRIMARY:
2579 cPrimaryVolDescs++;
2580 if (cPrimaryVolDescs == 1)
2581 {
2582 pThis->offPrimaryVolDesc = _32K / ISO9660_SECTOR_SIZE + iVolDesc;
2583 rtFsIsoImportProcessPrimaryDesc(pThis, &pThis->uSectorBuf.PrimVolDesc);
2584 }
2585 else
2586 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS,
2587 "Only a single primary volume descriptor is currently supported");
2588 break;
2589
2590 case ISO9660VOLDESC_TYPE_SUPPLEMENTARY:
2591 if (cPrimaryVolDescs > 0)
2592 rtFsIsoImportProcessSupplementaryDesc(pThis, &pThis->uSectorBuf.SupVolDesc);
2593 else
2594 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY,
2595 "Primary volume descriptor expected before any supplementary descriptors!");
2596 break;
2597
2598 case ISO9660VOLDESC_TYPE_BOOT_RECORD:
2599 if (strcmp(pThis->uSectorBuf.ElToritoDesc.achBootSystemId,
2600 ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID) == 0)
2601 {
2602 if (iElTorito == UINT32_MAX)
2603 iElTorito = iVolDesc;
2604 else
2605 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS,
2606 "Only a single El Torito descriptor exepcted!");
2607 }
2608 break;
2609
2610 case ISO9660VOLDESC_TYPE_PARTITION:
2611 /* ignore for now */
2612 break;
2613
2614 case ISO9660VOLDESC_TYPE_TERMINATOR:
2615 AssertFailed();
2616 break;
2617 }
2618
2619
2620 /*
2621 * Read the next volume descriptor and check the signature.
2622 */
2623 iVolDesc++;
2624 if (iVolDesc >= 32)
2625 {
2626 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS, "Parses at most 32 volume descriptors");
2627 break;
2628 }
2629
2630 rc = RTVfsFileReadAt(hIsoFile, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
2631 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
2632 if (RT_FAILURE(rc))
2633 {
2634 rtFsIsoImpError(pThis, rc, "Error reading the volume descriptor #%u at %#RX32: %Rrc",
2635 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE, rc);
2636 break;
2637 }
2638
2639 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
2640 || pThis->uSectorBuf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
2641 || pThis->uSectorBuf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
2642 || pThis->uSectorBuf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
2643 || pThis->uSectorBuf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
2644 {
2645 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR,
2646 "Invalid volume descriptor header #%u at %#RX32: %.*Rhxs",
2647 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
2648 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
2649 break;
2650 }
2651 /** @todo UDF support. */
2652 if (pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
2653 break;
2654 }
2655
2656 /*
2657 * Process the system area.
2658 */
2659 if (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX)
2660 {
2661 rc = RTVfsFileReadAt(hIsoFile, 0, pThis->abBuf, _32K, NULL);
2662 if (RT_SUCCESS(rc))
2663 {
2664 if (!ASMMemIsAllU8(pThis->abBuf, _32K, 0))
2665 {
2666 /* Drop zero sectors from the end. */
2667 uint32_t cbSysArea = _32K;
2668 while ( cbSysArea >= ISO9660_SECTOR_SIZE
2669 && ASMMemIsAllU8(&pThis->abBuf[cbSysArea - ISO9660_SECTOR_SIZE], ISO9660_SECTOR_SIZE, 0))
2670 cbSysArea -= ISO9660_SECTOR_SIZE;
2671
2672 /** @todo HFS */
2673 pThis->pResults->cbSysArea = cbSysArea;
2674 rc = RTFsIsoMakerSetSysAreaContent(hIsoMaker, pThis->abBuf, cbSysArea, 0);
2675 if (RT_FAILURE(rc))
2676 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetSysAreaContent failed: %Rrc", rc);
2677 }
2678 }
2679 else
2680 rtFsIsoImpError(pThis, rc, "Error reading the system area (0..32KB): %Rrc", rc);
2681 }
2682
2683 /*
2684 * Do the El Torito descriptor.
2685 */
2686 if ( iElTorito != UINT32_MAX
2687 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BOOT)
2688 && (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX))
2689 {
2690 rc = RTVfsFileReadAt(hIsoFile, _32K + iElTorito * ISO9660_SECTOR_SIZE,
2691 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
2692 if (RT_SUCCESS(rc))
2693 rtFsIsoImportProcessElToritoDesc(pThis, &pThis->uSectorBuf.ElToritoDesc);
2694 else
2695 rtFsIsoImpError(pThis, rc, "Error reading the El Torito volume descriptor at %#RX32: %Rrc",
2696 _32K + iElTorito * ISO9660_SECTOR_SIZE, rc);
2697 }
2698
2699 /*
2700 * Return the first error status.
2701 */
2702 rc = pThis->rc;
2703 }
2704 else
2705 rc = RTErrInfoSetF(pErrInfo, VERR_ISOMK_IMPORT_UNKNOWN_FORMAT, "Invalid volume descriptor header: %.*Rhxs",
2706 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
2707 }
2708
2709 /*
2710 * Destroy the state.
2711 */
2712 RTAvlU32Destroy(&pThis->Block2FileRoot, rtFsIsoMakerImportDestroyData2File, NULL);
2713 RTMemFree(pThis);
2714 }
2715 else
2716 rc = VERR_NO_MEMORY;
2717 }
2718 return rc;
2719}
2720
Note: See TracBrowser for help on using the repository browser.

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