VirtualBox

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

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

IPRT: More ISO maker code (import + booting).

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