VirtualBox

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

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

isomaker: Importing rock ridge stuff.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.9 KB
Line 
1/* $Id: isomakerimport.cpp 67775 2017-07-04 13:06: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 /** The SUSP skip into system area offset. */
162 uint32_t offSuspSkip;
163 /** The source file byte offset of the abRockBuf content. */
164 uint64_t offRockBuf;
165 /** Name buffer for rock ridge. */
166 char szRockNameBuf[_2K];
167 /** A buffer for reading rock ridge continuation blocks into */
168 uint8_t abRockBuf[ISO9660_SECTOR_SIZE];
169} RTFSISOMKIMPORTER;
170/** Pointer to an ISO maker ISO importer state. */
171typedef RTFSISOMKIMPORTER *PRTFSISOMKIMPORTER;
172
173
174/*
175 * The following is also found in iso9660vfs.cpp:
176 * The following is also found in iso9660vfs.cpp:
177 * The following is also found in iso9660vfs.cpp:
178 */
179
180/**
181 * Converts a ISO 9660 binary timestamp into an IPRT timesspec.
182 *
183 * @param pTimeSpec Where to return the IRPT time.
184 * @param pIso9660 The ISO 9660 binary timestamp.
185 */
186static void rtFsIsoImpIso9660DateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
187{
188 RTTIME Time;
189 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
190 Time.offUTC = 0;
191 Time.i32Year = pIso9660->bYear + 1900;
192 Time.u8Month = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
193 Time.u8MonthDay = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
194 Time.u8WeekDay = UINT8_MAX;
195 Time.u16YearDay = 0;
196 Time.u8Hour = RT_MIN(pIso9660->bHour, 23);
197 Time.u8Minute = RT_MIN(pIso9660->bMinute, 59);
198 Time.u8Second = RT_MIN(pIso9660->bSecond, 59);
199 Time.u32Nanosecond = 0;
200 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
201
202 /* Only apply the UTC offset if it's within reasons. */
203 if (RT_ABS(pIso9660->offUtc) <= 13*4)
204 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
205}
206
207/* end of duplicated static functions. */
208
209
210/**
211 * Wrapper around RTErrInfoSetV.
212 *
213 * @returns rc
214 * @param pThis The importer instance.
215 * @param rc The status code to set.
216 * @param pszFormat The format string detailing the error.
217 * @param va Argument to the format string.
218 */
219static int rtFsIsoImpErrorV(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, va_list va)
220{
221 va_list vaCopy;
222 va_copy(vaCopy, va);
223 LogRel(("RTFsIsoMkImport error %Rrc: %N\n", rc, pszFormat, &vaCopy));
224 va_end(vaCopy);
225
226 if (RT_SUCCESS(pThis->rc))
227 {
228 pThis->rc = rc;
229 rc = RTErrInfoSetV(pThis->pErrInfo, rc, pszFormat, va);
230 }
231
232 pThis->pResults->cErrors++;
233 return rc;
234}
235
236
237/**
238 * Wrapper around RTErrInfoSetF.
239 *
240 * @returns rc
241 * @param pThis The importer instance.
242 * @param rc The status code to set.
243 * @param pszFormat The format string detailing the error.
244 * @param ... Argument to the format string.
245 */
246static int rtFsIsoImpError(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, ...)
247{
248 va_list va;
249 va_start(va, pszFormat);
250 rc = rtFsIsoImpErrorV(pThis, rc, pszFormat, va);
251 va_end(va);
252 return rc;
253}
254
255
256/**
257 * Callback for destroying a RTFSISOMKIMPBLOCK2FILE node.
258 *
259 * @returns VINF_SUCCESS
260 * @param pNode The node to destroy.
261 * @param pvUser Ignored.
262 */
263static DECLCALLBACK(int) rtFsIsoMakerImportDestroyData2File(PAVLU32NODECORE pNode, void *pvUser)
264{
265 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)pNode;
266 if (pBlock2File)
267 {
268 PRTFSISOMKIMPBLOCK2FILE pNext;
269 while ((pNext = pBlock2File->pNext) != NULL)
270 {
271 pBlock2File->pNext = pNext->pNext;
272 pNext->pNext = NULL;
273 RTMemFree(pNext);
274 }
275 RTMemFree(pNode);
276 }
277
278 RT_NOREF(pvUser);
279 return VINF_SUCCESS;
280}
281
282
283/**
284 * Adds a directory and names it given its ISO-9660 directory record and parent.
285 *
286 * @returns IPRT status code (safe to ignore).
287 * @param pThis The importer instance.
288 * @param pDirRec The directory record.
289 * @param cbData The actual directory data size. (Always same as in the
290 * directory record, but this what we do for files below.)
291 * @param fNamespace The namespace flag.
292 * @param idxParent Parent directory.
293 * @param pszName The name.
294 * @param cDepth The depth to add it with.
295 * @param pTodoList The todo list (for directories).
296 */
297static int rtFsIsoImportProcessIso9660AddAndNameDirectory(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint64_t cbData,
298 uint32_t fNamespace, uint32_t idxParent, const char *pszName,
299 uint8_t cDepth, PRTLISTANCHOR pTodoList)
300{
301 Assert(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY);
302 uint32_t idxObj;
303 int rc = RTFsIsoMakerAddUnnamedDir(pThis->hIsoMaker, &idxObj);
304 if (RT_SUCCESS(rc))
305 {
306 Log3((" --> added directory #%#x\n", idxObj));
307 pThis->pResults->cAddedDirs++;
308
309 /*
310 * Enter the object into the namespace.
311 */
312 rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName);
313 if (RT_SUCCESS(rc))
314 {
315 pThis->pResults->cAddedNames++;
316
317 /*
318 * Push it onto the traversal stack.
319 */
320 PRTFSISOMKIMPDIR pImpDir = (PRTFSISOMKIMPDIR)RTMemAlloc(sizeof(*pImpDir));
321 if (pImpDir)
322 {
323 Assert((uint32_t)cbData == cbData /* no multi-extents for dirs makes it this far */);
324 pImpDir->cbDir = (uint32_t)cbData;
325 pImpDir->offDirBlock = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
326 pImpDir->idxObj = idxObj;
327 pImpDir->cDepth = cDepth;
328 RTListAppend(pTodoList, &pImpDir->Entry);
329 }
330 else
331 rc = rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPDIR");
332 }
333 else
334 rc = rtFsIsoImpError(pThis, rc, "Error naming directory '%s'", pszName);
335 }
336 else
337 rc = rtFsIsoImpError(pThis, rc, "Error adding directory '%s': %Rrc", pszName, pDirRec->fFileFlags, rc);
338 return rc;
339}
340
341
342/**
343 * Adds a file and names it given its ISO-9660 directory record and parent.
344 *
345 * @returns IPRT status code (safe to ignore).
346 * @param pThis The importer instance.
347 * @param pDirRec The directory record.
348 * @param cbData The actual file data size.
349 * @param fNamespace The namespace flag.
350 * @param idxParent Parent directory.
351 * @param pszName The name.
352 */
353static int rtFsIsoImportProcessIso9660AddAndNameFile(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint64_t cbData,
354 uint32_t fNamespace, uint32_t idxParent, const char *pszName)
355{
356 int rc;
357
358 /*
359 * First we must make sure the common source file has been added.
360 */
361 if (pThis->idxSrcFile != UINT32_MAX)
362 { /* likely */ }
363 else
364 {
365 rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
366 if (RT_FAILURE(rc))
367 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
368 Assert(pThis->idxSrcFile != UINT32_MAX);
369 }
370
371 /*
372 * Lookup the data block if the file has a non-zero length. The aim is to
373 * find files across namespaces while bearing in mind that files in the same
374 * namespace may share data storage, i.e. what in a traditional unix file
375 * system would be called hardlinked. Problem is that the core engine doesn't
376 * do hardlinking yet and assume each file has exactly one name per namespace.
377 */
378 uint32_t idxObj = UINT32_MAX;
379 PRTFSISOMKIMPBLOCK2FILE pBlock2File = NULL;
380 PRTFSISOMKIMPBLOCK2FILE pBlock2FilePrev = NULL;
381 if (cbData > 0) /* no data tracking for zero byte files */
382 {
383 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, ISO9660_GET_ENDIAN(&pDirRec->offExtent));
384 if (pBlock2File)
385 {
386 if (!(pBlock2File->fNamespaces & fNamespace))
387 {
388 pBlock2File->fNamespaces |= fNamespace;
389 idxObj = pBlock2File->idxObj;
390 }
391 else
392 {
393 do
394 {
395 pBlock2FilePrev = pBlock2File;
396 pBlock2File = pBlock2File->pNext;
397 } while (pBlock2File && (pBlock2File->fNamespaces & fNamespace));
398 if (pBlock2File)
399 {
400 pBlock2File->fNamespaces |= fNamespace;
401 idxObj = pBlock2File->idxObj;
402 }
403 }
404 }
405 }
406
407 /*
408 * If the above lookup didn't succeed, add a new file with a lookup record.
409 */
410 if (idxObj == UINT32_MAX)
411 {
412 rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
413 ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)ISO9660_SECTOR_SIZE,
414 cbData, NULL /*pObjInfo*/, &idxObj);
415 if (RT_FAILURE(rc))
416 return rtFsIsoImpError(pThis, rc, "Error adding file '%s': %Rrc", pszName, rc);
417 Assert(idxObj != UINT32_MAX);
418
419 /* Update statistics. */
420 pThis->pResults->cAddedFiles++;
421 if (cbData > 0)
422 {
423 pThis->pResults->cbAddedDataBlocks += RT_ALIGN_64(cbData, ISO9660_SECTOR_SIZE);
424
425 /* Lookup record. */
426 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTMemAlloc(sizeof(*pBlock2File));
427 AssertReturn(pBlock2File, rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPBLOCK2FILE"));
428
429 pBlock2File->idxObj = idxObj;
430 pBlock2File->Core.Key = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
431 pBlock2File->fNamespaces = fNamespace;
432 pBlock2File->pNext = NULL;
433 if (!pBlock2FilePrev)
434 {
435 bool fRc = RTAvlU32Insert(&pThis->Block2FileRoot, &pBlock2File->Core);
436 Assert(fRc); RT_NOREF(fRc);
437 }
438 else
439 {
440 pBlock2FilePrev->pNext = pBlock2File;
441 pBlock2FilePrev->Core.pLeft = NULL;
442 pBlock2FilePrev->Core.pRight = NULL;
443 }
444 }
445 }
446
447 /*
448 * Enter the object into the namespace.
449 */
450 rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName);
451 if (RT_SUCCESS(rc))
452 pThis->pResults->cAddedNames++;
453 else
454 return rtFsIsoImpError(pThis, rc, "Error adding directory '%s': %Rrc", pszName, pDirRec->fFileFlags, rc);
455 return VINF_SUCCESS;
456}
457
458
459/**
460 * Parses rock ridge information if present in the directory entry.
461 *
462 * @param pThis The importer instance.
463 * @param pObjInfo The object information to improve upon.
464 * @param pbSys The system area of the directory record.
465 * @param cbSys The number of bytes present in the sys area.
466 * @param fContinuationRecord Set if we're processing a continuation record in
467 * living in the abRockBuf.
468 * @param fIsFirstDirRec Set if this is the '.' directory entry in the
469 * root directory. (Some entries applies only to
470 * it.)
471 */
472static void rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(PRTFSISOMKIMPORTER pThis, PRTFSOBJINFO pObjInfo,
473 uint8_t const *pbSys, size_t cbSys, bool fContinuationRecord,
474 bool fIsFirstDirRec)
475{
476 RT_NOREF(pObjInfo);
477
478 /*
479 * Do skipping if specified.
480 */
481 if (pThis->offSuspSkip)
482 {
483 if (cbSys <= pThis->offSuspSkip)
484 return;
485 pbSys += pThis->offSuspSkip;
486 cbSys -= pThis->offSuspSkip;
487 }
488
489 while (cbSys >= 4)
490 {
491 /*
492 * Check header length and advance the sys variables.
493 */
494 PCISO9660SUSPUNION pUnion = (PCISO9660SUSPUNION)pbSys;
495 if (pUnion->Hdr.cbEntry > cbSys)
496 {
497 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: cbEntry=%#x cbSys=%#x (%#x %#x)\n",
498 pUnion->Hdr.cbEntry, cbSys, pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
499 return;
500 }
501 pbSys += pUnion->Hdr.cbEntry;
502 cbSys += pUnion->Hdr.cbEntry;
503
504 /*
505 * Process fields.
506 */
507#define MAKE_SIG(a_bSig1, a_bSig2) \
508 ( ((uint16_t)(a_bSig1) & 0x1f) \
509 | (((uint16_t)(a_bSig2) ^ 0x40) << 5) \
510 | ((((uint16_t)(a_bSig1) ^ 0x40) & 0xe0) << (5+8)) )
511
512 uint16_t const uSig = MAKE_SIG(pUnion->Hdr.bSig1, pUnion->Hdr.bSig2);
513 switch (uSig)
514 {
515 /*
516 * General SUSP stuff.
517 */
518 case MAKE_SIG(ISO9660SUSPCE_SIG1, ISO9660SUSPCE_SIG2):
519 {
520 if (RT_BE2H_U32(pUnion->CE.offBlock.be) != RT_LE2H_U32(pUnion->CE.offBlock.le))
521 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: Invalid CE offBlock field: be=%#x vs le=%#x\n",
522 RT_BE2H_U32(pUnion->CE.offBlock.be), RT_LE2H_U32(pUnion->CE.offBlock.le)));
523 else if (RT_BE2H_U32(pUnion->CE.cbData.be) != RT_LE2H_U32(pUnion->CE.cbData.le))
524 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: Invalid CE cbData field: be=%#x vs le=%#x\n",
525 RT_BE2H_U32(pUnion->CE.cbData.be), RT_LE2H_U32(pUnion->CE.cbData.le)));
526 else if (RT_BE2H_U32(pUnion->CE.offData.be) != RT_LE2H_U32(pUnion->CE.offData.le))
527 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: Invalid CE offData field: be=%#x vs le=%#x\n",
528 RT_BE2H_U32(pUnion->CE.offData.be), RT_LE2H_U32(pUnion->CE.offData.le)));
529 else if (!fContinuationRecord)
530 {
531 uint64_t offData = ISO9660_GET_ENDIAN(&pUnion->CE.offBlock) * (uint64_t)ISO9660_SECTOR_SIZE;
532 offData += ISO9660_GET_ENDIAN(&pUnion->CE.offData);
533 uint32_t cbData = ISO9660_GET_ENDIAN(&pUnion->CE.cbData);
534 if (cbData <= sizeof(pThis->abRockBuf) - (uint32_t)(offData & ISO9660_SECTOR_OFFSET_MASK))
535 {
536 AssertCompile(sizeof(pThis->abRockBuf) == ISO9660_SECTOR_SIZE);
537 uint64_t offDataBlock = offData & ~(uint64_t)ISO9660_SECTOR_OFFSET_MASK;
538 if (pThis->offRockBuf == offDataBlock)
539 rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, pObjInfo,
540 &pThis->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
541 cbData, true /*fContinuationRecord*/, fIsFirstDirRec);
542 else
543 {
544 int rc = RTVfsFileReadAt(pThis->hSrcFile, offDataBlock, pThis->abRockBuf, sizeof(pThis->abRockBuf), NULL);
545 if (RT_SUCCESS(rc))
546 rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, pObjInfo,
547 &pThis->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
548 cbData, true /*fContinuationRecord*/, fIsFirstDirRec);
549 else
550 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: Error reading continuation record at %#RX64: %Rrc\n",
551 offDataBlock, rc));
552 }
553 }
554 else
555 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: continuation record isn't within a sector! offData=%#RX64 cbData=%#RX32\n",
556 cbData, offData));
557 }
558 else
559 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: nested continuation record!\n"));
560 break;
561 }
562
563 case MAKE_SIG(ISO9660SUSPPD_SIG1, ISO9660SUSPPD_SIG2): /* PD - ignored */
564 case MAKE_SIG(ISO9660SUSPST_SIG1, ISO9660SUSPST_SIG2): /* ST - ignore for now */
565 case MAKE_SIG(ISO9660SUSPES_SIG1, ISO9660SUSPES_SIG2): /* ES - ignore for now */
566 break;
567
568 case MAKE_SIG(ISO9660SUSPSP_SIG1, ISO9660SUSPSP_SIG2): /* SP */
569 if (!fIsFirstDirRec)
570 {
571 LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: \n"));
572 break;
573 }
574 break;
575
576 case MAKE_SIG(ISO9660SUSPER_SIG1, ISO9660SUSPER_SIG2): /* ER */
577 break;
578
579 default:
580 Log(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: Unknown SUSP entry: %#x %#x\n",
581 pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
582 break;
583 }
584#if 0
585 /*
586 SUSP: */
587 if (pbSys[0] == 'C' && pbSys[1] == 'E')
588 {
589 }
590 else if (pbSys[0] == 'P' && pbSys[1] == 'D')
591 else if (pbSys[0] == 'S' && pbSys[1] == 'P')
592 else if (pbSys[0] == 'S' && pbSys[1] == 'T')
593 else if (pbSys[0] == 'E' && pbSys[1] == 'R')
594 else if (pbSys[0] == 'E' && pbSys[1] == 'S')
595 /* RRIP */
596 else if (pbSys[0] == 'R' && pbSys[1] == 'R')
597 else if (pbSys[0] == 'P' && pbSys[1] == 'X')
598 else if (pbSys[0] == 'P' && pbSys[1] == 'N')
599 else if (pbSys[0] == 'S' && pbSys[1] == 'L')
600 else if (pbSys[0] == 'N' && pbSys[1] == 'M')
601 else if (pbSys[0] == 'C' && pbSys[1] == 'L')
602 else if (pbSys[0] == 'P' && pbSys[1] == 'L')
603 else if (pbSys[0] == 'R' && pbSys[1] == 'E')
604 else if (pbSys[0] == 'T' && pbSys[1] == 'F')
605 else if (pbSys[0] == 'S' && pbSys[1] == 'F')
606 /* other stuff */
607 else if (pbSys[0] == 'A' && pbSys[1] == 'A')
608 else if (pbSys[0] == 'A' && pbSys[1] == 'B')
609 else if (pbSys[0] == 'A' && pbSys[1] == 'S')
610#endif
611
612 }
613}
614
615
616/**
617 * Validates a directory record.
618 *
619 * @returns IPRT status code (safe to ignore, see pThis->rc).
620 * @param pThis The importer instance.
621 * @param pDirRec The root directory record to validate.
622 * @param cbMax The maximum size.
623 */
624static int rtFsIsoImportValidateDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax)
625{
626 /*
627 * Validate dual fields.
628 */
629 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
630 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
631 "Invalid dir rec size field: {%#RX32,%#RX32}",
632 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
633
634 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
635 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
636 "Invalid dir rec extent field: {%#RX32,%#RX32}",
637 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
638
639 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
640 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
641 "Invalid dir rec volume sequence ID field: {%#RX16,%#RX16}",
642 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
643
644 /*
645 * Check values.
646 */
647 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
648 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_VOLUME_SEQ_NO,
649 "Expected dir rec to have same volume sequence number as primary volume: %#x, expected %#x",
650 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
651
652 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
653 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_EXTENT_OUT_OF_BOUNDS,
654 "Invalid dir rec extent: %#RX32, max %#RX32",
655 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
656
657 if (pDirRec->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength)
658 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
659 "Dir record size is too small: %#x (min %#x)",
660 pDirRec->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength);
661 if (pDirRec->cbDirRec > cbMax)
662 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
663 "Dir record size is too big: %#x (max %#x)", pDirRec->cbDirRec, cbMax);
664
665 if ( (pDirRec->fFileFlags & (ISO9660_FILE_FLAGS_MULTI_EXTENT | ISO9660_FILE_FLAGS_DIRECTORY))
666 == (ISO9660_FILE_FLAGS_MULTI_EXTENT | ISO9660_FILE_FLAGS_DIRECTORY))
667 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_WITH_MORE_EXTENTS,
668 "Multi-extent directories are not supported (cbData=%#RX32 offExtent=%#RX32)",
669 ISO9660_GET_ENDIAN(&pDirRec->cbData), ISO9660_GET_ENDIAN(&pDirRec->offExtent));
670
671 return VINF_SUCCESS;
672}
673
674
675/**
676 * Validates a dot or dot-dot directory record.
677 *
678 * @returns IPRT status code (safe to ignore, see pThis->rc).
679 * @param pThis The importer instance.
680 * @param pDirRec The dot directory record to validate.
681 * @param cbMax The maximum size.
682 * @param bName The name byte (0x00: '.', 0x01: '..').
683 */
684static int rtFsIsoImportValidateDotDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax, uint8_t bName)
685{
686 int rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbMax);
687 if (RT_SUCCESS(rc))
688 {
689 if (pDirRec->bFileIdLength != 1)
690 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME_LENGTH,
691 "Invalid dot dir rec file id length: %u", pDirRec->bFileIdLength);
692 if ((uint8_t)pDirRec->achFileId[0] != bName)
693 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME,
694 "Invalid dot dir rec file id: %#x, expected %#x", pDirRec->achFileId[0], bName);
695 }
696 return rc;
697}
698
699
700/**
701 * rtFsIsoImportProcessIso9660TreeWorker helper that reads more data.
702 *
703 * @returns IPRT status code.
704 * @param pThis The importer instance.
705 * @param ppDirRec Pointer to the directory record pointer (in/out).
706 * @param pcbChunk Pointer to the cbChunk variable (in/out).
707 * @param pcbDir Pointer to the cbDir variable (in/out). This indicates
708 * how much we've left to read from the directory.
709 * @param poffNext Pointer to the offNext variable (in/out). This
710 * indicates where the next chunk of directory data is in
711 * the input file.
712 */
713static int rtFsIsoImportProcessIso9660TreeWorkerReadMore(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC *ppDirRec,
714 uint32_t *pcbChunk, uint32_t *pcbDir, uint64_t *poffNext)
715{
716 uint32_t cbChunk = *pcbChunk;
717 *ppDirRec = (PCISO9660DIRREC)memmove(&pThis->abBuf[ISO9660_SECTOR_SIZE - cbChunk], *ppDirRec, cbChunk);
718
719 Assert(!(*poffNext & (ISO9660_SECTOR_SIZE - 1)));
720 uint32_t cbToRead = RT_MIN(*pcbDir, sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE);
721 int rc = RTVfsFileReadAt(pThis->hSrcFile, *poffNext, &pThis->abBuf[ISO9660_SECTOR_SIZE], cbToRead, NULL);
722 if (RT_SUCCESS(rc))
723 {
724 Log3(("rtFsIsoImportProcessIso9660TreeWorker: Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
725 cbToRead, *poffNext, *poffNext - cbChunk, cbChunk + cbToRead));
726 *poffNext += cbToRead;
727 *pcbDir -= cbToRead;
728 *pcbChunk = cbChunk + cbToRead;
729 return VINF_SUCCESS;
730 }
731 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", *poffNext, cbToRead);
732}
733
734
735/**
736 * rtFsIsoImportProcessIso9660TreeWorker helper that deals with skipping to the
737 * next sector when cbDirRec is zero.
738 *
739 * @returns IPRT status code.
740 * @retval VERR_NO_MORE_FILES when we reaches the end of the directory.
741 * @param pThis The importer instance.
742 * @param ppDirRec Pointer to the directory record pointer (in/out).
743 * @param pcbChunk Pointer to the cbChunk variable (in/out). Indicates how
744 * much we've left to process starting and pDirRec.
745 * @param pcbDir Pointer to the cbDir variable (in/out). This indicates
746 * how much we've left to read from the directory.
747 * @param poffNext Pointer to the offNext variable (in/out). This
748 * indicates where the next chunk of directory data is in
749 * the input file.
750 */
751static int rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC *ppDirRec,
752 uint32_t *pcbChunk, uint32_t *pcbDir, uint64_t *poffNext)
753{
754 uint32_t cbChunk = *pcbChunk;
755 uint64_t offChunk = *poffNext - cbChunk;
756 uint32_t cbSkip = ISO9660_SECTOR_SIZE - ((uint32_t)offChunk & (ISO9660_SECTOR_SIZE - 1));
757 if (cbSkip < cbChunk)
758 {
759 *ppDirRec = (PCISO9660DIRREC)((uintptr_t)*ppDirRec + cbSkip);
760 *pcbChunk = cbChunk -= cbSkip;
761 if ( cbChunk > UINT8_MAX
762 || *pcbDir == 0)
763 {
764 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> jumped %#RX32 to @%#RX64 LB %#RX32\n",
765 cbSkip, *poffNext - cbChunk, cbChunk));
766 return VINF_SUCCESS;
767 }
768 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> jumped %#RX32 to @%#RX64 LB %#RX32, but needs to read more\n",
769 cbSkip, *poffNext - cbChunk, cbChunk));
770 return rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, ppDirRec, pcbChunk, pcbDir, poffNext);
771 }
772
773 /* ASSUMES we're working in multiples of sectors! */
774 if (*pcbDir == 0)
775 {
776 *pcbChunk = 0;
777 return VERR_NO_MORE_FILES;
778 }
779
780 /* End of chunk, read the next sectors. */
781 Assert(!(*poffNext & (ISO9660_SECTOR_SIZE - 1)));
782 uint32_t cbToRead = RT_MIN(*pcbDir, sizeof(pThis->abBuf));
783 int rc = RTVfsFileReadAt(pThis->hSrcFile, *poffNext, pThis->abBuf, cbToRead, NULL);
784 if (RT_SUCCESS(rc))
785 {
786 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
787 cbToRead, *poffNext, *poffNext - cbChunk, cbChunk + cbToRead));
788 *poffNext += cbToRead;
789 *pcbDir -= cbToRead;
790 *pcbChunk = cbChunk + cbToRead;
791 *ppDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
792 return VINF_SUCCESS;
793 }
794 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", *poffNext, cbToRead);
795}
796
797
798/**
799 * Deals with a single directory.
800 *
801 * @returns IPRT status code (safe to ignore, see pThis->rc).
802 * @param pThis The importer instance.
803 * @param idxDir The configuration index for the directory.
804 * @param offDirBlock The offset of the directory data.
805 * @param cbDir The size of the directory data.
806 * @param cDepth The depth of the directory.
807 * @param fUnicode Set if it's a unicode (UTF-16BE) encoded
808 * directory.
809 * @param pTodoList The todo-list to add sub-directories to.
810 */
811static int rtFsIsoImportProcessIso9660TreeWorker(PRTFSISOMKIMPORTER pThis, uint32_t idxDir,
812 uint32_t offDirBlock, uint32_t cbDir, uint8_t cDepth, bool fUnicode,
813 PRTLISTANCHOR pTodoList)
814{
815 /*
816 * Restrict the depth to try avoid loops.
817 */
818 if (cDepth > RTFSISOMK_IMPORT_MAX_DEPTH)
819 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE, "Dir at %#x LB %#x is too deep", offDirBlock, cbDir);
820
821 /*
822 * Read the first chunk into the big buffer.
823 */
824 uint32_t cbChunk = RT_MIN(cbDir, sizeof(pThis->abBuf));
825 uint64_t offNext = (uint64_t)offDirBlock * ISO9660_SECTOR_SIZE;
826 int rc = RTVfsFileReadAt(pThis->hSrcFile, offNext, pThis->abBuf, cbChunk, NULL);
827 if (RT_FAILURE(rc))
828 return rtFsIsoImpError(pThis, rc, "Error reading directory at %#RX64 (%#RX32 / %#RX32): %Rrc", offNext, cbChunk, cbDir);
829
830 cbDir -= cbChunk;
831 offNext += cbChunk;
832
833 /*
834 * Skip the current and parent directory entries.
835 */
836 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
837 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x00);
838 if (RT_FAILURE(rc))
839 return rc;
840
841 cbChunk -= pDirRec->cbDirRec;
842 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
843 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x01);
844 if (RT_FAILURE(rc))
845 return rc;
846
847 cbChunk -= pDirRec->cbDirRec;
848 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
849
850 /*
851 * Work our way thru all the directory records.
852 */
853 Log3(("rtFsIsoImportProcessIso9660TreeWorker: Starting at @%#RX64 LB %#RX32 (out of %#RX32) in %#x\n",
854 offNext - cbChunk, cbChunk, cbChunk + cbDir, idxDir));
855 const uint32_t fNamespace = fUnicode ? RTFSISOMAKER_NAMESPACE_JOLIET : RTFSISOMAKER_NAMESPACE_ISO_9660;
856 while ( cbChunk > 0
857 || cbDir > 0)
858 {
859 /*
860 * Do we need to read some more?
861 */
862 if ( cbChunk > UINT8_MAX
863 || cbDir == 0)
864 { /* No, we don't. */ }
865 else
866 {
867 rc = rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, &pDirRec, &cbChunk, &cbDir, &offNext);
868 if (RT_FAILURE(rc))
869 return rc;
870 }
871
872 /* If null length, skip to the next sector. May have to read some then. */
873 if (pDirRec->cbDirRec != 0)
874 { /* likely */ }
875 else
876 {
877 rc = rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(pThis, &pDirRec, &cbChunk, &cbDir, &offNext);
878 if (RT_FAILURE(rc))
879 {
880 if (rc == VERR_NO_MORE_FILES)
881 break;
882 return rc;
883 }
884 if (pDirRec->cbDirRec == 0)
885 continue;
886 }
887
888 /*
889 * Validate the directory record. Give up if not valid since we're
890 * likely to get error with subsequent record too.
891 */
892 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
893 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
894 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
895 Log3(("pDirRec=&abBuf[%#07zx]: @%#010RX64 cb=%#04x ff=%#04x off=%#010RX32 cb=%#010RX32 cbSys=%#x id=%.*Rhxs\n",
896 (uintptr_t)pDirRec - (uintptr_t)&pThis->abBuf[0], offNext - cbChunk, pDirRec->cbDirRec, pDirRec->fFileFlags,
897 ISO9660_GET_ENDIAN(&pDirRec->offExtent), ISO9660_GET_ENDIAN(&pDirRec->cbData), cbSys,
898 pDirRec->bFileIdLength, pDirRec->achFileId));
899 rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbChunk);
900 if (RT_FAILURE(rc))
901 return rc;
902
903 /* This early calculation of the next record is due to multi-extent
904 handling further down. */
905 uint32_t cbChunkNew = cbChunk - pDirRec->cbDirRec;
906 PCISO9660DIRREC pDirRecNext = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
907
908 /* Start Collecting object info. */
909 RTFSOBJINFO ObjInfo;
910 ObjInfo.cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
911 ObjInfo.cbAllocated = ObjInfo.cbObject;
912 rtFsIsoImpIso9660DateTime2TimeSpec(&ObjInfo.AccessTime, &pDirRec->RecTime);
913 ObjInfo.ModificationTime = ObjInfo.AccessTime;
914 ObjInfo.ChangeTime = ObjInfo.AccessTime;
915 ObjInfo.BirthTime = ObjInfo.AccessTime;
916 ObjInfo.Attr.fMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
917 ? RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | 0555
918 : RTFS_TYPE_FILE | RTFS_DOS_ARCHIVED | 0444;
919 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
920 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
921 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
922 ObjInfo.Attr.u.Unix.cHardlinks = 1;
923 ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
924 ObjInfo.Attr.u.Unix.INodeId = 0;
925 ObjInfo.Attr.u.Unix.fFlags = 0;
926 ObjInfo.Attr.u.Unix.GenerationId = 0;
927 ObjInfo.Attr.u.Unix.Device = 0;
928
929 /*
930 * Convert the name into the name buffer (szNameBuf).
931 */
932 if (!fUnicode)
933 {
934 memcpy(pThis->szNameBuf, pDirRec->achFileId, pDirRec->bFileIdLength);
935 pThis->szNameBuf[pDirRec->bFileIdLength] = '\0';
936 rc = RTStrValidateEncoding(pThis->szNameBuf);
937 }
938 else
939 {
940 char *pszDst = pThis->szNameBuf;
941 rc = RTUtf16BigToUtf8Ex((PRTUTF16)pDirRec->achFileId, pDirRec->bFileIdLength / sizeof(RTUTF16),
942 &pszDst, sizeof(pThis->szNameBuf), NULL);
943 }
944 if (RT_SUCCESS(rc))
945 {
946 /* Drop the version from the name. */
947 size_t cchName = strlen(pThis->szNameBuf);
948 if ( !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
949 && cchName > 2
950 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - 1]))
951 {
952 uint32_t offName = 2;
953 while ( offName <= 5
954 && offName + 1 < cchName
955 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - offName]))
956 offName++;
957 if ( offName + 1 < cchName
958 && pThis->szNameBuf[cchName - offName] == ';')
959 {
960 RTStrToUInt32Full(&pThis->szNameBuf[cchName - offName + 1], 10, &ObjInfo.Attr.u.Unix.GenerationId);
961 pThis->szNameBuf[cchName - offName] = '\0';
962 }
963 }
964 Log3((" --> name='%s'\n", pThis->szNameBuf));
965
966 pThis->szRockNameBuf[0] = '\0';
967 if (cbSys > 0 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ROCK_RIDGE))
968 rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, &ObjInfo, pbSys, cbSys,
969 false /*fContinuationRecord*/, false /*fIsFirstDirRec*/);
970
971 /*
972 * Deal with multi-extent files (usually large ones). We currently only
973 * handle files where the data is in single continuous chunk and only split
974 * up into multiple directory records because of data type limitations.
975 */
976 uint8_t abDirRecCopy[256];
977 uint64_t cbData = ISO9660_GET_ENDIAN(&pDirRec->cbData);
978 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
979 { /* likely */ }
980 else
981 {
982 if (cbData & (ISO9660_SECTOR_SIZE - 1))
983 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISALIGNED_MULTI_EXTENT,
984 "The size of non-final multi-extent record #0x0 isn't block aligned: %#RX64", cbData);
985
986 /* Make a copy of the first directory record so we don't overwrite
987 it when reading in more records below. */
988 pDirRec = (PCISO9660DIRREC)memcpy(abDirRecCopy, pDirRec, pDirRec->cbDirRec);
989
990 /* Process extent records. */
991 uint32_t cDirRecs = 1;
992 uint32_t offNextBlock = ISO9660_GET_ENDIAN(&pDirRec->offExtent)
993 + ISO9660_GET_ENDIAN(&pDirRec->cbData) / ISO9660_SECTOR_SIZE;
994 while ( cbChunkNew > 0
995 || cbDir > 0)
996 {
997 /* Read more? Skip? */
998 if ( cbChunkNew <= UINT8_MAX
999 && cbDir != 0)
1000 {
1001 rc = rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, &pDirRecNext, &cbChunkNew, &cbDir, &offNext);
1002 if (RT_FAILURE(rc))
1003 return rc;
1004 }
1005 if (pDirRecNext->cbDirRec == 0)
1006 {
1007 rc = rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(pThis, &pDirRecNext, &cbChunkNew,
1008 &cbDir, &offNext);
1009 if (RT_FAILURE(rc))
1010 {
1011 if (rc == VERR_NO_MORE_FILES)
1012 break;
1013 return rc;
1014 }
1015 if (pDirRecNext->cbDirRec == 0)
1016 continue;
1017 }
1018
1019 /* Check the next record. */
1020 rc = rtFsIsoImportValidateDirRec(pThis, pDirRecNext, cbChunkNew);
1021 if (RT_FAILURE(rc))
1022 return rc;
1023 if (pDirRecNext->bFileIdLength != pDirRec->bFileIdLength)
1024 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
1025 "Multi-extent record #%#x differs from the first: bFileIdLength is %#x, expected %#x",
1026 cDirRecs, pDirRecNext->bFileIdLength, pDirRec->bFileIdLength);
1027 if (memcmp(pDirRecNext->achFileId, pDirRec->achFileId, pDirRec->bFileIdLength) != 0)
1028 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
1029 "Multi-extent record #%#x differs from the first: achFileId is %.*Rhxs, expected %.*Rhxs",
1030 cDirRecs, pDirRecNext->bFileIdLength, pDirRecNext->achFileId,
1031 pDirRec->bFileIdLength, pDirRec->achFileId);
1032 if (ISO9660_GET_ENDIAN(&pDirRecNext->VolumeSeqNo) != ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo))
1033 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
1034 "Multi-extent record #%#x differs from the first: VolumeSeqNo is %#x, expected %#x",
1035 cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->VolumeSeqNo),
1036 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo));
1037 if ( (pDirRecNext->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
1038 && (ISO9660_GET_ENDIAN(&pDirRecNext->cbData) & (ISO9660_SECTOR_SIZE - 1)) )
1039 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISALIGNED_MULTI_EXTENT,
1040 "The size of non-final multi-extent record #%#x isn't block aligned: %#RX32",
1041 cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->cbData));
1042
1043 /* Check that the data is contiguous, then add the data. */
1044 if (ISO9660_GET_ENDIAN(&pDirRecNext->offExtent) == offNextBlock)
1045 cbData += ISO9660_GET_ENDIAN(&pDirRecNext->cbData);
1046 else
1047 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_NON_CONTIGUOUS_MULTI_EXTENT,
1048 "Multi-extent record #%#x isn't contiguous: offExtent=%#RX32, expected %#RX32",
1049 cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->offExtent), offNextBlock);
1050
1051 /* Advance. */
1052 cDirRecs++;
1053 bool fDone = !(pDirRecNext->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT);
1054 offNext += ISO9660_GET_ENDIAN(&pDirRecNext->cbData) / ISO9660_SECTOR_SIZE;
1055 cbChunkNew -= pDirRecNext->cbDirRec;
1056 pDirRecNext = (PCISO9660DIRREC)((uintptr_t)pDirRecNext + pDirRecNext->cbDirRec);
1057 if (fDone)
1058 break;
1059 }
1060 }
1061 if (RT_SUCCESS(rc))
1062 {
1063 /*
1064 * Add the object.
1065 */
1066 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
1067 rtFsIsoImportProcessIso9660AddAndNameDirectory(pThis, pDirRec, cbData, fNamespace, idxDir,
1068 pThis->szNameBuf, cDepth + 1, pTodoList);
1069 else
1070 rtFsIsoImportProcessIso9660AddAndNameFile(pThis, pDirRec, cbData, fNamespace, idxDir, pThis->szNameBuf);
1071 }
1072 }
1073 else
1074 rtFsIsoImpError(pThis, rc, "Invalid name at %#RX64: %.Rhxs",
1075 offNext - cbChunk, pDirRec->bFileIdLength, pDirRec->achFileId);
1076
1077 /*
1078 * Advance to the next directory record.
1079 */
1080 cbChunk = cbChunkNew;
1081 pDirRec = pDirRecNext;
1082 }
1083
1084 return VINF_SUCCESS;
1085}
1086
1087
1088/**
1089 * Deals with a directory tree.
1090 *
1091 * This is implemented by tracking directories that needs to be processed in a
1092 * todo list, so no recursive calls, however it uses a bit of heap.
1093 *
1094 * @returns IPRT status code (safe to ignore, see pThis->rc).
1095 * @param pThis The importer instance.
1096 * @param offDirBlock The offset of the root directory data.
1097 * @param cbDir The size of the root directory data.
1098 * @param fUnicode Set if it's a unicode (UTF-16BE) encoded
1099 * directory.
1100 */
1101static int rtFsIsoImportProcessIso9660Tree(PRTFSISOMKIMPORTER pThis, uint32_t offDirBlock, uint32_t cbDir, bool fUnicode)
1102{
1103 pThis->offSuspSkip = 0;
1104
1105 /*
1106 * Make sure we've got a root in the namespace.
1107 */
1108 uint32_t idxDir = RTFsIsoMakerGetObjIdxForPath(pThis->hIsoMaker,
1109 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET,
1110 "/");
1111 if (idxDir == UINT32_MAX)
1112 {
1113 idxDir = RTFSISOMAKER_CFG_IDX_ROOT;
1114 int rc = RTFsIsoMakerObjSetPath(pThis->hIsoMaker, RTFSISOMAKER_CFG_IDX_ROOT,
1115 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET, "/");
1116 if (RT_FAILURE(rc))
1117 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjSetPath failed on root dir: %Rrc", rc);
1118 }
1119 Assert(idxDir == RTFSISOMAKER_CFG_IDX_ROOT);
1120
1121 /*
1122 * Directories.
1123 */
1124 int rc = VINF_SUCCESS;
1125 uint8_t cDepth = 0;
1126 RTLISTANCHOR TodoList;
1127 RTListInit(&TodoList);
1128 for (;;)
1129 {
1130 int rc2 = rtFsIsoImportProcessIso9660TreeWorker(pThis, idxDir, offDirBlock, cbDir, cDepth, fUnicode, &TodoList);
1131 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1132 rc = rc2;
1133
1134 /*
1135 * Pop the next directory.
1136 */
1137 PRTFSISOMKIMPDIR pNext = RTListRemoveLast(&TodoList, RTFSISOMKIMPDIR, Entry);
1138 if (!pNext)
1139 break;
1140 idxDir = pNext->idxObj;
1141 offDirBlock = pNext->offDirBlock;
1142 cbDir = pNext->cbDir;
1143 cDepth = pNext->cDepth;
1144 RTMemFree(pNext);
1145 }
1146
1147 return rc;
1148}
1149
1150
1151/**
1152 * Imports a UTF-16BE string property from the joliet volume descriptor.
1153 *
1154 * The fields are normally space filled and padded, but we also consider zero
1155 * bytes are fillers. If the field only contains padding, the string property
1156 * will remain unchanged.
1157 *
1158 * @returns IPRT status code (ignorable).
1159 * @param pThis The importer instance.
1160 * @param pachField Pointer to the field. The structure type
1161 * is 'char' for hysterical raisins, while the
1162 * real type is 'RTUTF16'.
1163 * @param cchField The field length.
1164 * @param enmStringProp The corresponding string property.
1165 *
1166 * @note Clobbers pThis->pbBuf!
1167 */
1168static int rtFsIsoImportUtf16BigStringField(PRTFSISOMKIMPORTER pThis, const char *pachField, size_t cchField,
1169 RTFSISOMAKERSTRINGPROP enmStringProp)
1170{
1171 /*
1172 * Scan the field from the end as this way we know the result length if we find anything.
1173 */
1174 PCRTUTF16 pwcField = (PCRTUTF16)pachField;
1175 size_t cwcField = cchField / sizeof(RTUTF16); /* ignores any odd field byte */
1176 size_t off = cwcField;
1177 while (off-- > 0)
1178 {
1179 RTUTF16 wc = RT_BE2H_U16(pwcField[off]);
1180 if (wc == ' ' || wc == '\0')
1181 { /* likely */ }
1182 else
1183 {
1184 /*
1185 * Convert to UTF-16.
1186 */
1187 char *pszCopy = (char *)pThis->abBuf;
1188 int rc = RTUtf16BigToUtf8Ex(pwcField, off + 1, &pszCopy, sizeof(pThis->abBuf), NULL);
1189 if (RT_SUCCESS(rc))
1190 {
1191 rc = RTFsIsoMakerSetStringProp(pThis->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_JOLIET, pszCopy);
1192 if (RT_SUCCESS(rc))
1193 return VINF_SUCCESS;
1194 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetStringProp failed setting field %d to '%s': %Rrc",
1195 enmStringProp, pszCopy, rc);
1196 }
1197 return rtFsIsoImpError(pThis, rc, "RTUtf16BigToUtf8Ex failed converting field %d to UTF-8: %Rrc - %.*Rhxs",
1198 enmStringProp, rc, off * sizeof(RTUTF16), pwcField);
1199 }
1200 }
1201 return VINF_SUCCESS;
1202}
1203
1204
1205/**
1206 * Imports a string property from the primary volume descriptor.
1207 *
1208 * The fields are normally space filled and padded, but we also consider zero
1209 * bytes are fillers. If the field only contains padding, the string property
1210 * will remain unchanged.
1211 *
1212 * @returns IPRT status code (ignorable).
1213 * @param pThis The importer instance.
1214 * @param pachField Pointer to the field.
1215 * @param cchField The field length.
1216 * @param enmStringProp The corresponding string property.
1217 *
1218 * @note Clobbers pThis->pbBuf!
1219 */
1220static int rtFsIsoImportAsciiStringField(PRTFSISOMKIMPORTER pThis, const char *pachField, size_t cchField,
1221 RTFSISOMAKERSTRINGPROP enmStringProp)
1222{
1223 /*
1224 * Scan the field from the end as this way we know the result length if we find anything.
1225 */
1226 size_t off = cchField;
1227 while (off-- > 0)
1228 {
1229 char ch = pachField[off];
1230 if (ch == ' ' || ch == '\0')
1231 { /* likely */ }
1232 else
1233 {
1234 /*
1235 * Make a copy of the string in abBuf, purge the encoding.
1236 */
1237 off++;
1238 char *pszCopy = (char *)pThis->abBuf;
1239 memcpy(pszCopy, pachField, off);
1240 pszCopy[off] = '\0';
1241 RTStrPurgeEncoding(pszCopy);
1242
1243 int rc = RTFsIsoMakerSetStringProp(pThis->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_ISO_9660, pszCopy);
1244 if (RT_SUCCESS(rc))
1245 return VINF_SUCCESS;
1246 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetStringProp failed setting field %d to '%s': %Rrc",
1247 enmStringProp, pszCopy, rc);
1248 }
1249 }
1250 return VINF_SUCCESS;
1251}
1252
1253
1254/**
1255 * Validates a root directory record.
1256 *
1257 * @returns IPRT status code (safe to ignore, see pThis->rc).
1258 * @param pThis The importer instance.
1259 * @param pDirRec The root directory record to validate.
1260 */
1261static int rtFsIsoImportValidateRootDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec)
1262{
1263 /*
1264 * Validate dual fields.
1265 */
1266 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
1267 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
1268 "Invalid root dir size: {%#RX32,%#RX32}",
1269 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
1270
1271 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
1272 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
1273 "Invalid root dir extent: {%#RX32,%#RX32}",
1274 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
1275
1276 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
1277 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
1278 "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
1279 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
1280
1281 /*
1282 * Check values.
1283 */
1284 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
1285 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_VOLUME_SEQ_NO,
1286 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
1287 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
1288
1289 if (ISO9660_GET_ENDIAN(&pDirRec->cbData) == 0)
1290 return RTErrInfoSet(pThis->pErrInfo, VERR_ISOMK_IMPORT_ZERO_SIZED_ROOT_DIR, "Zero sized root dir");
1291
1292 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
1293 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_EXTENT_OUT_OF_BOUNDS,
1294 "Invalid root dir extent: %#RX32, max %#RX32",
1295 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
1296
1297 if (pDirRec->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
1298 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC_LENGTH,
1299 "Root dir record size is too small: %#x (min %#x)",
1300 pDirRec->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
1301
1302 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
1303 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_WITHOUT_DIR_FLAG,
1304 "Root dir is not flagged as directory: %#x", pDirRec->fFileFlags);
1305 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
1306 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_IS_MULTI_EXTENT,
1307 "Root dir is cannot be multi-extent: %#x", pDirRec->fFileFlags);
1308
1309 return VINF_SUCCESS;
1310}
1311
1312
1313/**
1314 * Processes a primary volume descriptor, importing all files and stuff.
1315 *
1316 * @returns IPRT status code (safe to ignore, see pThis->rc).
1317 * @param pThis The importer instance.
1318 * @param pVolDesc The primary volume descriptor.
1319 */
1320static int rtFsIsoImportProcessPrimaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660PRIMARYVOLDESC pVolDesc)
1321{
1322 /*
1323 * Validate dual fields first.
1324 */
1325 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1326 return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_PRIMARY_VOL_DESC_VER,
1327 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1328
1329 if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
1330 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1331 "Mismatching logical block size: {%#RX16,%#RX16}",
1332 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
1333 if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
1334 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1335 "Mismatching volume space size: {%#RX32,%#RX32}",
1336 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
1337 if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
1338 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1339 "Mismatching volumes in set: {%#RX16,%#RX16}",
1340 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
1341 if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
1342 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1343 "Mismatching volume sequence no.: {%#RX16,%#RX16}",
1344 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
1345 if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
1346 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
1347 "Mismatching path table size: {%#RX32,%#RX32}",
1348 RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
1349
1350 /*
1351 * Validate field values against our expectations.
1352 */
1353 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
1354 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
1355 "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
1356
1357 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != 1)
1358 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MORE_THAN_ONE_VOLUME_IN_SET,
1359 "Volumes in set: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet));
1360
1361 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != 1)
1362 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
1363 "Unexpected volume sequence number: %#x", ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo));
1364
1365 /*
1366 * Gather info we need.
1367 */
1368 pThis->cBlocksInPrimaryVolumeSpace = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize);
1369 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)ISO9660_SECTOR_SIZE;
1370 pThis->cVolumesInSet = ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet);
1371 pThis->idPrimaryVol = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo);
1372
1373 /*
1374 * Validate the root directory record.
1375 */
1376 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
1377 if (RT_SUCCESS(rc))
1378 {
1379 /*
1380 * Import stuff if present and not opted out.
1381 */
1382 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_SYSTEM_ID))
1383 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId),
1384 RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
1385 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_VOLUME_ID))
1386 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId),
1387 RTFSISOMAKERSTRINGPROP_VOLUME_ID);
1388 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_VOLUME_SET_ID))
1389 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId),
1390 RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
1391 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PUBLISHER_ID))
1392 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId),
1393 RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
1394 if (pThis->fFlags & RTFSISOMK_IMPORT_F_DATA_PREPARER_ID)
1395 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId),
1396 RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
1397 if (pThis->fFlags & RTFSISOMK_IMPORT_F_APPLICATION_ID)
1398 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId),
1399 RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
1400 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_COPYRIGHT_FID))
1401 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId),
1402 RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
1403 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ABSTRACT_FID))
1404 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId),
1405 RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
1406 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BIBLIO_FID))
1407 rtFsIsoImportAsciiStringField(pThis, pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId),
1408 RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
1409
1410 /*
1411 * Process the directory tree.
1412 */
1413 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PRIMARY_ISO))
1414 rc = rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
1415 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), false /*fUnicode*/);
1416 }
1417
1418 return rc;
1419}
1420
1421
1422/**
1423 * Processes a secondary volume descriptor, if it is joliet we'll importing all
1424 * the files and stuff.
1425 *
1426 * @returns IPRT status code (safe to ignore, see pThis->rc).
1427 * @param pThis The importer instance.
1428 * @param pVolDesc The primary volume descriptor.
1429 */
1430static int rtFsIsoImportProcessSupplementaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660SUPVOLDESC pVolDesc)
1431{
1432 /*
1433 * Validate dual fields first.
1434 */
1435 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1436 return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_SUP_VOL_DESC_VER,
1437 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1438
1439 if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
1440 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1441 "Mismatching logical block size: {%#RX16,%#RX16}",
1442 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
1443 if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
1444 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1445 "Mismatching volume space size: {%#RX32,%#RX32}",
1446 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
1447 if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
1448 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1449 "Mismatching volumes in set: {%#RX16,%#RX16}",
1450 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
1451 if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
1452 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1453 "Mismatching volume sequence no.: {%#RX16,%#RX16}",
1454 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
1455 if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
1456 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
1457 "Mismatching path table size: {%#RX32,%#RX32}",
1458 RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
1459
1460 /*
1461 * Validate field values against our expectations.
1462 */
1463 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
1464 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
1465 "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
1466
1467 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
1468 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_VOLUME_IN_SET_MISMATCH, "Volumes in set: %#x, expected %#x",
1469 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
1470
1471 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
1472 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
1473 "Unexpected volume sequence number: %#x (expected %#x)",
1474 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
1475
1476 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
1477 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
1478 "Volume space size differs between primary and supplementary descriptors: %#x, primary %#x",
1479 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
1480
1481 /*
1482 * Validate the root directory record.
1483 */
1484 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
1485 if (RT_FAILURE(rc))
1486 return rc;
1487
1488 /*
1489 * Is this a joliet descriptor? Ignore if not.
1490 */
1491 uint8_t uJolietLevel = 0;
1492 if ( pVolDesc->abEscapeSequences[0] == ISO9660_JOLIET_ESC_SEQ_0
1493 && pVolDesc->abEscapeSequences[1] == ISO9660_JOLIET_ESC_SEQ_1)
1494 switch (pVolDesc->abEscapeSequences[2])
1495 {
1496 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1: uJolietLevel = 1; break;
1497 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2: uJolietLevel = 2; break;
1498 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3: uJolietLevel = 3; break;
1499 default: Log(("rtFsIsoImportProcessSupplementaryDesc: last joliet escape sequence byte doesn't match: %#x\n",
1500 pVolDesc->abEscapeSequences[2]));
1501 }
1502 if (uJolietLevel == 0)
1503 return VINF_SUCCESS;
1504
1505 /*
1506 * Only one joliet descriptor.
1507 */
1508 if (pThis->fSeenJoliet)
1509 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_JOLIET_VOL_DESCS,
1510 "More than one Joliet volume descriptor is not supported");
1511 pThis->fSeenJoliet = true;
1512
1513 /*
1514 * Import stuff if present and not opted out.
1515 */
1516 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_SYSTEM_ID))
1517 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId),
1518 RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
1519 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_ID))
1520 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId),
1521 RTFSISOMAKERSTRINGPROP_VOLUME_ID);
1522 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_SET_ID))
1523 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId),
1524 RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
1525 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_PUBLISHER_ID))
1526 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId),
1527 RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
1528 if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_DATA_PREPARER_ID)
1529 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId),
1530 RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
1531 if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_APPLICATION_ID)
1532 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId),
1533 RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
1534 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_COPYRIGHT_FID))
1535 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId),
1536 RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
1537 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_ABSTRACT_FID))
1538 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId),
1539 RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
1540 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_BIBLIO_FID))
1541 rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId),
1542 RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
1543
1544 /*
1545 * Process the directory tree.
1546 */
1547 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_JOLIET))
1548 return rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
1549 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), true /*fUnicode*/);
1550 return VINF_SUCCESS;
1551}
1552
1553
1554/**
1555 * Checks out an El Torito boot image to see if it requires info table patching.
1556 *
1557 * @returns IPRT status code (ignored).
1558 * @param pThis The ISO importer instance.
1559 * @param idxImageObj The configuration index of the image.
1560 * @param offBootImage The block offset of the image.
1561 */
1562static int rtFsIsoImportProcessElToritoImage(PRTFSISOMKIMPORTER pThis, uint32_t idxImageObj, uint32_t offBootImage)
1563{
1564 ISO9660SYSLINUXINFOTABLE InfoTable;
1565 int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootImage * (uint64_t)ISO9660_SECTOR_SIZE + ISO9660SYSLINUXINFOTABLE_OFFSET,
1566 &InfoTable, sizeof(InfoTable), NULL);
1567 if (RT_SUCCESS(rc))
1568 {
1569 if ( RT_LE2H_U32(InfoTable.offBootFile) == offBootImage
1570 && RT_LE2H_U32(InfoTable.offPrimaryVolDesc) == pThis->offPrimaryVolDesc
1571 && ASMMemIsAllU8(&InfoTable.auReserved[0], sizeof(InfoTable.auReserved), 0) )
1572 {
1573 rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pThis->hIsoMaker, idxImageObj, true /*fEnable*/);
1574 if (RT_FAILURE(rc))
1575 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjEnableBootInfoTablePatching failed: %Rrc", rc);
1576 }
1577 }
1578 return VINF_SUCCESS;
1579}
1580
1581
1582/**
1583 * Processes a boot catalog default or section entry.
1584 *
1585 * @returns IPRT status code (ignored).
1586 * @param pThis The ISO importer instance.
1587 * @param iEntry The boot catalog entry number. This is 1 for
1588 * the default entry, and 3+ for section entries.
1589 * @param cMaxEntries Maximum number of entries.
1590 * @param pEntry The entry to process.
1591 * @param pcSkip Where to return the number of extension entries to skip.
1592 */
1593static int rtFsIsoImportProcessElToritoSectionEntry(PRTFSISOMKIMPORTER pThis, uint32_t iEntry, uint32_t cMaxEntries,
1594 PCISO9660ELTORITOSECTIONENTRY pEntry, uint32_t *pcSkip)
1595{
1596 *pcSkip = 0;
1597
1598 /*
1599 * Check the boot indicator type for entry 1.
1600 */
1601 if ( pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
1602 && pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
1603 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_BOOT_IND,
1604 "Default boot catalog entry has an invalid boot indicator: %#x", pEntry->bBootIndicator);
1605
1606 /*
1607 * Check the media type and flags.
1608 */
1609 uint32_t cbDefaultSize;
1610 uint8_t bMediaType = pEntry->bBootMediaType;
1611 switch (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
1612 {
1613 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB:
1614 cbDefaultSize = 512 * 80 * 15 * 2;
1615 break;
1616
1617 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB:
1618 cbDefaultSize = 512 * 80 * 18 * 2;
1619 break;
1620
1621 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB:
1622 cbDefaultSize = 512 * 80 * 36 * 2;
1623 break;
1624
1625 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION:
1626 case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK:
1627 cbDefaultSize = 0;
1628 break;
1629
1630 default:
1631 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_INVALID_BOOT_MEDIA_TYPE,
1632 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
1633 }
1634
1635 if (iEntry == 1)
1636 {
1637 if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_MASK)
1638 {
1639 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_FLAGS,
1640 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
1641 bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_MASK;
1642 }
1643 }
1644 else
1645 {
1646 if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED)
1647 {
1648 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_RESERVED_FLAG,
1649 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
1650 bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED;
1651 }
1652 }
1653
1654 /*
1655 * Complain if bUnused is used.
1656 */
1657 if (pEntry->bUnused != 0)
1658 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_USES_UNUSED_FIELD,
1659 "Boot catalog entry #%#x has a non-zero unused field: %#x", pEntry->bUnused);
1660
1661 /*
1662 * Check out the boot image offset and turn that into an index of a file
1663 */
1664 uint32_t offBootImage = RT_LE2H_U32(pEntry->offBootImage);
1665 if (offBootImage >= pThis->cBlocksInSrcFile)
1666 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_IMAGE_OUT_OF_BOUNDS,
1667 "Boot catalog entry #%#x has an out of bound boot image block number: %#RX32, max %#RX32",
1668 offBootImage, pThis->cBlocksInPrimaryVolumeSpace);
1669
1670 int rc;
1671 uint32_t idxImageObj;
1672 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootImage);
1673 if (pBlock2File)
1674 idxImageObj = pBlock2File->idxObj;
1675 else
1676 {
1677 if (cbDefaultSize == 0)
1678 {
1679 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32GetBestFit(&pThis->Block2FileRoot, offBootImage, true /*fAbove*/);
1680 if (pBlock2File)
1681 cbDefaultSize = RT_MIN(pBlock2File->Core.Key - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
1682 * ISO9660_SECTOR_SIZE;
1683 else if (offBootImage < pThis->cBlocksInSrcFile)
1684 cbDefaultSize = RT_MIN(pThis->cBlocksInSrcFile - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
1685 * ISO9660_SECTOR_SIZE;
1686 else
1687 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_UNKNOWN_IMAGE_SIZE,
1688 "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
1689 }
1690
1691 if (pThis->idxSrcFile != UINT32_MAX)
1692 {
1693 rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
1694 if (RT_FAILURE(rc))
1695 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
1696 Assert(pThis->idxSrcFile != UINT32_MAX);
1697 }
1698
1699 rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
1700 offBootImage * (uint64_t)ISO9660_SECTOR_SIZE,
1701 cbDefaultSize, NULL, &idxImageObj);
1702 if (RT_FAILURE(rc))
1703 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddUnnamedFileWithCommonSrc failed on boot entry #%#x: %Rrc",
1704 iEntry, rc);
1705 }
1706
1707 /*
1708 * Deal with selection criteria. Use the last sector of abBuf to gather it
1709 * into a single data chunk.
1710 */
1711 size_t cbSelCrit = 0;
1712 uint8_t *pbSelCrit = &pThis->abBuf[sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE];
1713 if (pEntry->bSelectionCriteriaType != ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE)
1714 {
1715 memcpy(pbSelCrit, pEntry->abSelectionCriteria, sizeof(pEntry->abSelectionCriteria));
1716 cbSelCrit = sizeof(pEntry->abSelectionCriteria);
1717
1718 if ( (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
1719 && iEntry + 1 < cMaxEntries)
1720 {
1721 uint32_t iExtEntry = iEntry + 1;
1722 PCISO9660ELTORITOSECTIONENTRYEXT pExtEntry = (PCISO9660ELTORITOSECTIONENTRYEXT)pEntry;
1723 for (;;)
1724 {
1725 pExtEntry++;
1726
1727 if (pExtEntry->bExtensionId != ISO9660_ELTORITO_SECTION_ENTRY_EXT_ID)
1728 {
1729 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_INVALID_ID,
1730 "Invalid header ID for extension entry #%#x: %#x", iExtEntry, pExtEntry->bExtensionId);
1731 break;
1732 }
1733 *pcSkip += 1;
1734
1735 memcpy(&pbSelCrit[cbSelCrit], pExtEntry->abSelectionCriteria, sizeof(pExtEntry->abSelectionCriteria));
1736 cbSelCrit += sizeof(pExtEntry->abSelectionCriteria);
1737
1738 if (pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_UNUSED_MASK)
1739 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_UNDEFINED_FLAGS,
1740 "Boot catalog extension entry #%#x uses undefined flags: %#x", iExtEntry, pExtEntry->fFlags);
1741
1742 iExtEntry++;
1743 if (!(pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_MORE))
1744 break;
1745 if (iExtEntry >= cMaxEntries)
1746 {
1747 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_END_OF_SECTOR,
1748 "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
1749 break;
1750 }
1751 }
1752 Assert(*pcSkip = iExtEntry - iEntry);
1753 }
1754 else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
1755 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_EOS,
1756 "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
1757 }
1758 else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
1759 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_WITH_NONE,
1760 "Boot catalog entry #%#x uses the continuation flag with selection criteria NONE", iEntry);
1761
1762 /*
1763 * Add the entry.
1764 */
1765 rc = RTFsIsoMakerBootCatSetSectionEntry(pThis->hIsoMaker, iEntry, idxImageObj, bMediaType, pEntry->bSystemType,
1766 pEntry->bBootIndicator == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE,
1767 pEntry->uLoadSeg, pEntry->cEmulatedSectorsToLoad,
1768 pEntry->bSelectionCriteriaType, pbSelCrit, cbSelCrit);
1769 if (RT_SUCCESS(rc))
1770 {
1771 pThis->pResults->cBootCatEntries += 1 + *pcSkip;
1772 rc = rtFsIsoImportProcessElToritoImage(pThis, idxImageObj, offBootImage);
1773 }
1774 else
1775 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetSectionEntry failed for entry #%#x: %Rrc", iEntry, rc);
1776 return rc;
1777}
1778
1779
1780
1781/**
1782 * Processes a boot catalog section header entry.
1783 *
1784 * @returns IPRT status code (ignored).
1785 * @param pThis The ISO importer instance.
1786 * @param iEntry The boot catalog entry number.
1787 * @param pEntry The entry to process.
1788 */
1789static int rtFsIsoImportProcessElToritoSectionHeader(PRTFSISOMKIMPORTER pThis, uint32_t iEntry,
1790 PCISO9660ELTORITOSECTIONHEADER pEntry, char pszId[32])
1791{
1792 Assert(pEntry->bHeaderId == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER);
1793
1794 /* Deal with the string. ASSUME it doesn't contain zeros in non-terminal positions. */
1795 if (pEntry->achSectionId[0] == '\0')
1796 pszId = NULL;
1797 else
1798 {
1799 memcpy(pszId, pEntry->achSectionId, sizeof(pEntry->achSectionId));
1800 pszId[sizeof(pEntry->achSectionId)] = '\0';
1801 }
1802
1803 int rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pThis->hIsoMaker, iEntry, RT_LE2H_U16(pEntry->cEntries),
1804 pEntry->bPlatformId, pszId);
1805 if (RT_SUCCESS(rc))
1806 pThis->pResults->cBootCatEntries++;
1807 else
1808 rtFsIsoImpError(pThis, rc,
1809 "RTFsIsoMakerBootCatSetSectionHeaderEntry failed for entry #%#x (bPlatformId=%#x cEntries=%#x): %Rrc",
1810 iEntry, RT_LE2H_U16(pEntry->cEntries), pEntry->bPlatformId, rc);
1811 return rc;
1812}
1813
1814
1815/**
1816 * Processes a El Torito volume descriptor.
1817 *
1818 * @returns IPRT status code (ignorable).
1819 * @param pThis The ISO importer instance.
1820 * @param pVolDesc The volume descriptor to process.
1821 */
1822static int rtFsIsoImportProcessElToritoDesc(PRTFSISOMKIMPORTER pThis, PISO9660BOOTRECORDELTORITO pVolDesc)
1823{
1824 /*
1825 * Read the boot catalog into the abBuf.
1826 */
1827 uint32_t offBootCatalog = RT_LE2H_U32(pVolDesc->offBootCatalog);
1828 if (offBootCatalog >= pThis->cBlocksInPrimaryVolumeSpace)
1829 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_OUT_OF_BOUNDS,
1830 "Boot catalog block number is out of bounds: %#RX32, max %#RX32",
1831 offBootCatalog, pThis->cBlocksInPrimaryVolumeSpace);
1832
1833 int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootCatalog * (uint64_t)ISO9660_SECTOR_SIZE,
1834 pThis->abBuf, ISO9660_SECTOR_SIZE, NULL);
1835 if (RT_FAILURE(rc))
1836 return rtFsIsoImpError(pThis, rc, "Error reading boot catalog at block #%#RX32: %Rrc", offBootCatalog, rc);
1837
1838
1839 /*
1840 * Process the 'validation entry'.
1841 */
1842 PCISO9660ELTORITOVALIDATIONENTRY pValEntry = (PCISO9660ELTORITOVALIDATIONENTRY)&pThis->abBuf[0];
1843 if (pValEntry->bHeaderId != ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY)
1844 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_HEADER_ID,
1845 "Invalid boot catalog validation entry header ID: %#x, expected %#x",
1846 pValEntry->bHeaderId, ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY);
1847
1848 if ( pValEntry->bKey1 != ISO9660_ELTORITO_KEY_BYTE_1
1849 || pValEntry->bKey2 != ISO9660_ELTORITO_KEY_BYTE_2)
1850 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_KEYS,
1851 "Invalid boot catalog validation entry keys: %#x %#x, expected %#x %#x",
1852 pValEntry->bKey1, pValEntry->bKey2, ISO9660_ELTORITO_KEY_BYTE_1, ISO9660_ELTORITO_KEY_BYTE_2);
1853
1854 /* Check the checksum (should sum up to be zero). */
1855 uint16_t uChecksum = 0;
1856 uint16_t const *pu16 = (uint16_t const *)pValEntry;
1857 size_t cLeft = sizeof(*pValEntry) / sizeof(uint16_t);
1858 while (cLeft-- > 0)
1859 {
1860 uChecksum += RT_LE2H_U16(*pu16);
1861 pu16++;
1862 }
1863 if (uChecksum != 0)
1864 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_CHECKSUM,
1865 "Invalid boot catalog validation entry checksum: %#x, expected 0", uChecksum);
1866
1867 /* The string ID. ASSUME no leading zeros in valid strings. */
1868 const char *pszId = NULL;
1869 char szId[32];
1870 if (pValEntry->achId[0] != '\0')
1871 {
1872 memcpy(szId, pValEntry->achId, sizeof(pValEntry->achId));
1873 szId[sizeof(pValEntry->achId)] = '\0';
1874 pszId = szId;
1875 }
1876
1877 /*
1878 * Before we tell the ISO maker about the validation entry, we need to sort
1879 * out the file backing the boot catalog. This isn't fatal if it fails.
1880 */
1881 PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootCatalog);
1882 if (pBlock2File)
1883 {
1884 rc = RTFsIsoMakerBootCatSetFile(pThis->hIsoMaker, pBlock2File->idxObj);
1885 if (RT_FAILURE(rc))
1886 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetFile failed: %Rrc", rc);
1887 }
1888
1889 /*
1890 * Set the validation entry.
1891 */
1892 rc = RTFsIsoMakerBootCatSetValidationEntry(pThis->hIsoMaker, pValEntry->bPlatformId, pszId);
1893 if (RT_FAILURE(rc))
1894 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetValidationEntry(,%#x,%s) failed: %Rrc",
1895 pValEntry->bPlatformId, pszId);
1896 Assert(pThis->pResults->cBootCatEntries == UINT32_MAX);
1897 pThis->pResults->cBootCatEntries = 0;
1898
1899 /*
1900 * Process the default entry and any subsequent entries.
1901 */
1902 bool fSeenFinal = false;
1903 uint32_t const cMaxEntries = ISO9660_SECTOR_SIZE / ISO9660_ELTORITO_ENTRY_SIZE;
1904 for (uint32_t iEntry = 1; iEntry < cMaxEntries; iEntry++)
1905 {
1906 uint8_t const *pbEntry = &pThis->abBuf[iEntry * ISO9660_ELTORITO_ENTRY_SIZE];
1907 uint8_t const idHeader = *pbEntry;
1908 if ( iEntry == 1 /* default*/
1909 || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
1910 || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
1911 {
1912 uint32_t cSkip = 0;
1913 rtFsIsoImportProcessElToritoSectionEntry(pThis, iEntry, cMaxEntries, (PCISO9660ELTORITOSECTIONENTRY)pbEntry, &cSkip);
1914 iEntry += cSkip;
1915 }
1916 else if (idHeader == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER)
1917 rtFsIsoImportProcessElToritoSectionHeader(pThis, iEntry, (PCISO9660ELTORITOSECTIONHEADER)pbEntry, szId);
1918 else if (idHeader == ISO9660_ELTORITO_HEADER_ID_FINAL_SECTION_HEADER)
1919 {
1920 fSeenFinal = true;
1921 break;
1922 }
1923 else
1924 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_UNKNOWN_HEADER_ID,
1925 "Unknown boot catalog header ID for entry #%#x: %#x", iEntry, idHeader);
1926 }
1927
1928 if (!fSeenFinal)
1929 rc = rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_MISSING_FINAL_OR_TOO_BIG,
1930 "Boot catalog is probably larger than a sector, or it's missing the final section header entry");
1931 return rc;
1932}
1933
1934
1935/**
1936 * Imports an existing ISO.
1937 *
1938 * Just like other source files, the existing image must remain present and
1939 * unmodified till the ISO maker is done with it.
1940 *
1941 * @returns IRPT status code.
1942 * @param hIsoMaker The ISO maker handle.
1943 * @param pszIso Path to the existing image to import / clone.
1944 * This is fed to RTVfsChainOpenFile.
1945 * @param fFlags Reserved for the future, MBZ.
1946 * @param poffError Where to return the position in @a pszIso
1947 * causing trouble when opening it for reading.
1948 * Optional.
1949 * @param pErrInfo Where to return additional error information.
1950 * Optional.
1951 */
1952RTDECL(int) RTFsIsoMakerImport(RTFSISOMAKER hIsoMaker, const char *pszIso, uint32_t fFlags,
1953 PRTFSISOMAKERIMPORTRESULTS pResults, PRTERRINFO pErrInfo)
1954{
1955 /*
1956 * Validate input.
1957 */
1958 AssertPtrReturn(pResults, VERR_INVALID_POINTER);
1959 pResults->cAddedNames = 0;
1960 pResults->cAddedDirs = 0;
1961 pResults->cbAddedDataBlocks = 0;
1962 pResults->cAddedFiles = 0;
1963 pResults->cBootCatEntries = UINT32_MAX;
1964 pResults->cbSysArea = 0;
1965 pResults->cErrors = 0;
1966 pResults->offError = UINT32_MAX;
1967 AssertReturn(!(fFlags & ~RTFSISOMK_IMPORT_F_VALID_MASK), VERR_INVALID_FLAGS);
1968
1969 /*
1970 * Open the input file and start working on it.
1971 */
1972 RTVFSFILE hSrcFile;
1973 int rc = RTVfsChainOpenFile(pszIso, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE,
1974 &hSrcFile, &pResults->offError, pErrInfo);
1975 if (RT_FAILURE(rc))
1976 return rc;
1977 pResults->offError = UINT32_MAX;
1978
1979 /*
1980 * Get the file size.
1981 */
1982 uint64_t cbSrcFile = 0;
1983 rc = RTVfsFileGetSize(hSrcFile, &cbSrcFile);
1984 if (RT_SUCCESS(rc))
1985 {
1986 /*
1987 * Allocate and init the importer state.
1988 */
1989 PRTFSISOMKIMPORTER pThis = (PRTFSISOMKIMPORTER)RTMemAllocZ(sizeof(*pThis));
1990 if (pThis)
1991 {
1992 pThis->hIsoMaker = hIsoMaker;
1993 pThis->fFlags = fFlags;
1994 pThis->rc = VINF_SUCCESS;
1995 pThis->pErrInfo = pErrInfo;
1996 pThis->hSrcFile = hSrcFile;
1997 pThis->cbSrcFile = cbSrcFile;
1998 pThis->cBlocksInSrcFile = cbSrcFile / ISO9660_SECTOR_SIZE;
1999 pThis->idxSrcFile = UINT32_MAX;
2000 //pThis->Block2FileRoot = NULL;
2001 //pThis->cBlocksInPrimaryVolumeSpace = 0;
2002 //pThis->cbPrimaryVolumeSpace = 0
2003 //pThis->cVolumesInSet = 0;
2004 //pThis->idPrimaryVol = 0;
2005 //pThis->fSeenJoliet = false;
2006 pThis->pResults = pResults;
2007 pThis->offSuspSkip = 0;
2008 pThis->offRockBuf = UINT64_MAX;
2009
2010 /*
2011 * Check if this looks like a plausible ISO by checking out the first volume descriptor.
2012 */
2013 rc = RTVfsFileReadAt(hSrcFile, _32K, &pThis->uSectorBuf.PrimVolDesc, sizeof(pThis->uSectorBuf.PrimVolDesc), NULL);
2014 if (RT_SUCCESS(rc))
2015 {
2016 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] == ISO9660VOLDESC_STD_ID_0
2017 && pThis->uSectorBuf.VolDescHdr.achStdId[1] == ISO9660VOLDESC_STD_ID_1
2018 && pThis->uSectorBuf.VolDescHdr.achStdId[2] == ISO9660VOLDESC_STD_ID_2
2019 && pThis->uSectorBuf.VolDescHdr.achStdId[3] == ISO9660VOLDESC_STD_ID_3
2020 && pThis->uSectorBuf.VolDescHdr.achStdId[4] == ISO9660VOLDESC_STD_ID_4
2021 && ( pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY
2022 || pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD) )
2023 {
2024 /*
2025 * Process the volume descriptors using the sector buffer, starting
2026 * with the one we've already got sitting there. We postpone processing
2027 * the el torito one till after the others, so we can name files and size
2028 * referenced in it.
2029 */
2030 uint32_t cPrimaryVolDescs = 0;
2031 uint32_t iElTorito = UINT32_MAX;
2032 uint32_t iVolDesc = 0;
2033 for (;;)
2034 {
2035 switch (pThis->uSectorBuf.VolDescHdr.bDescType)
2036 {
2037 case ISO9660VOLDESC_TYPE_PRIMARY:
2038 cPrimaryVolDescs++;
2039 if (cPrimaryVolDescs == 1)
2040 {
2041 pThis->offPrimaryVolDesc = _32K / ISO9660_SECTOR_SIZE + iVolDesc;
2042 rtFsIsoImportProcessPrimaryDesc(pThis, &pThis->uSectorBuf.PrimVolDesc);
2043 }
2044 else
2045 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS,
2046 "Only a single primary volume descriptor is currently supported");
2047 break;
2048
2049 case ISO9660VOLDESC_TYPE_SUPPLEMENTARY:
2050 if (cPrimaryVolDescs > 0)
2051 rtFsIsoImportProcessSupplementaryDesc(pThis, &pThis->uSectorBuf.SupVolDesc);
2052 else
2053 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY,
2054 "Primary volume descriptor expected before any supplementary descriptors!");
2055 break;
2056
2057 case ISO9660VOLDESC_TYPE_BOOT_RECORD:
2058 if (strcmp(pThis->uSectorBuf.ElToritoDesc.achBootSystemId,
2059 ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID) == 0)
2060 {
2061 if (iElTorito == UINT32_MAX)
2062 iElTorito = iVolDesc;
2063 else
2064 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS,
2065 "Only a single El Torito descriptor exepcted!");
2066 }
2067 break;
2068
2069 case ISO9660VOLDESC_TYPE_PARTITION:
2070 /* ignore for now */
2071 break;
2072
2073 case ISO9660VOLDESC_TYPE_TERMINATOR:
2074 AssertFailed();
2075 break;
2076 }
2077
2078
2079 /*
2080 * Read the next volume descriptor and check the signature.
2081 */
2082 iVolDesc++;
2083 if (iVolDesc >= 32)
2084 {
2085 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS, "Parses at most 32 volume descriptors");
2086 break;
2087 }
2088
2089 rc = RTVfsFileReadAt(hSrcFile, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
2090 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
2091 if (RT_FAILURE(rc))
2092 {
2093 rtFsIsoImpError(pThis, rc, "Error reading the volume descriptor #%u at %#RX32: %Rrc",
2094 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE, rc);
2095 break;
2096 }
2097
2098 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
2099 || pThis->uSectorBuf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
2100 || pThis->uSectorBuf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
2101 || pThis->uSectorBuf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
2102 || pThis->uSectorBuf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
2103 {
2104 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR,
2105 "Invalid volume descriptor header #%u at %#RX32: %.*Rhxs",
2106 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
2107 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
2108 break;
2109 }
2110 /** @todo UDF support. */
2111 if (pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
2112 break;
2113 }
2114
2115 /*
2116 * Process the system area.
2117 */
2118 if (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX)
2119 {
2120 rc = RTVfsFileReadAt(hSrcFile, 0, pThis->abBuf, _32K, NULL);
2121 if (RT_SUCCESS(rc))
2122 {
2123 if (!ASMMemIsAllU8(pThis->abBuf, _32K, 0))
2124 {
2125 /* Drop zero sectors from the end. */
2126 uint32_t cbSysArea = _32K;
2127 while ( cbSysArea >= ISO9660_SECTOR_SIZE
2128 && ASMMemIsAllU8(&pThis->abBuf[cbSysArea - ISO9660_SECTOR_SIZE], ISO9660_SECTOR_SIZE, 0))
2129 cbSysArea -= ISO9660_SECTOR_SIZE;
2130
2131 /** @todo HFS */
2132 pThis->pResults->cbSysArea = cbSysArea;
2133 rc = RTFsIsoMakerSetSysAreaContent(hIsoMaker, pThis->abBuf, cbSysArea, 0);
2134 if (RT_FAILURE(rc))
2135 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetSysAreaContent failed: %Rrc", rc);
2136 }
2137 }
2138 else
2139 rtFsIsoImpError(pThis, rc, "Error reading the system area (0..32KB): %Rrc", rc);
2140 }
2141
2142 /*
2143 * Do the El Torito descriptor.
2144 */
2145 if ( iElTorito != UINT32_MAX
2146 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BOOT)
2147 && (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX))
2148 {
2149 rc = RTVfsFileReadAt(hSrcFile, _32K + iElTorito * ISO9660_SECTOR_SIZE,
2150 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
2151 if (RT_SUCCESS(rc))
2152 rtFsIsoImportProcessElToritoDesc(pThis, &pThis->uSectorBuf.ElToritoDesc);
2153 else
2154 rtFsIsoImpError(pThis, rc, "Error reading the El Torito volume descriptor at %#RX32: %Rrc",
2155 _32K + iElTorito * ISO9660_SECTOR_SIZE, rc);
2156 }
2157
2158 /*
2159 * Return the first error status.
2160 */
2161 rc = pThis->rc;
2162 }
2163 else
2164 rc = RTErrInfoSetF(pErrInfo, VERR_ISOMK_IMPORT_UNKNOWN_FORMAT, "Invalid volume descriptor header: %.*Rhxs",
2165 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
2166 }
2167
2168 /*
2169 * Destroy the state.
2170 */
2171 RTAvlU32Destroy(&pThis->Block2FileRoot, rtFsIsoMakerImportDestroyData2File, NULL);
2172 RTMemFree(pThis);
2173 }
2174 else
2175 rc = VERR_NO_MEMORY;
2176 }
2177 RTVfsFileRelease(hSrcFile);
2178 return rc;
2179
2180}
2181
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