VirtualBox

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

Last change on this file since 67797 was 67797, checked in by vboxsync, 8 years ago

IPRT/isomaker: Added RTFsIsoMakerAddSymlink & RTFsIsoMakerAddUnnamedSymlink.

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

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