VirtualBox

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

Last change on this file since 67511 was 67511, checked in by vboxsync, 7 years ago

IPRT: ISO maker updates (import related).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.1 KB
Line 
1/* $Id: isomakerimport.cpp 67511 2017-06-20 14:30:30Z 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} RTFSISOMKIMPBLOCK2FILE;
70/** Pointer to a block-2-file translation node. */
71typedef RTFSISOMKIMPBLOCK2FILE *PRTFSISOMKIMPBLOCK2FILE;
72
73
74/**
75 * Directory todo list entry.
76 */
77typedef struct RTFSISOMKIMPDIR
78{
79 /** List stuff. */
80 RTLISTNODE Entry;
81 /** The directory configuration index with hIsoMaker. */
82 uint32_t idxObj;
83 /** The directory data block number. */
84 uint32_t offDirBlock;
85 /** The directory size (in bytes). */
86 uint32_t cbDir;
87 /** The depth of this directory. */
88 uint8_t cDepth;
89} RTFSISOMKIMPDIR;
90/** Pointer to a directory todo list entry. */
91typedef RTFSISOMKIMPDIR *PRTFSISOMKIMPDIR;
92
93
94/**
95 * ISO maker ISO importer state.
96 */
97typedef struct RTFSISOMKIMPORTER
98{
99 /** The destination ISO maker. */
100 RTFSISOMAKER hIsoMaker;
101 /** RTFSISOMK_IMPORT_F_XXX. */
102 uint32_t fFlags;
103 /** The status code of the whole import.
104 * This notes down the first error status. */
105 int rc;
106 /** Pointer to error info return structure. */
107 PRTERRINFO pErrInfo;
108
109 /** The source file. */
110 RTVFSFILE hSrcFile;
111 /** The import source index of hSrcFile in hIsoMaker. UINT32_MAX till adding
112 * the first file. */
113 uint32_t idxSrcFile;
114
115 /** The root of the tree for converting data block numbers to files
116 * (PRTFSISOMKIMPBLOCK2FILE). This is essential when importing boot files and
117 * the 2nd namespace (joliet, udf, hfs) so that we avoid duplicating data. */
118 AVLU32TREE Block2FileRoot;
119
120 /** The primary volume space size in blocks. */
121 uint32_t cBlocksInPrimaryVolumeSpace;
122 /** The primary volume space size in bytes. */
123 uint64_t cbPrimaryVolumeSpace;
124 /** The number of volumes in the set. */
125 uint32_t cVolumesInSet;
126 /** The primary volume sequence ID. */
127 uint32_t idPrimaryVol;
128
129 /** Set if we've already seen a joliet volume descriptor. */
130 bool fSeenJoliet;
131
132 /** Pointer to the import results structure (output). */
133 PRTFSISOMAKERIMPORTRESULTS pResults;
134
135 /** Sector buffer for volume descriptors and such. */
136 union
137 {
138 uint8_t ab[ISO9660_SECTOR_SIZE];
139 ISO9660VOLDESCHDR VolDescHdr;
140 ISO9660PRIMARYVOLDESC PrimVolDesc;
141 ISO9660SUPVOLDESC SupVolDesc;
142 ISO9660BOOTRECORDELTORITO ElToritoDesc;
143 } uSectorBuf;
144
145 /** Name buffer. */
146 char szNameBuf[_2K];
147
148 /** A somewhat larger buffer. */
149 uint8_t abBuf[_64K];
150} RTFSISOMKIMPORTER;
151/** Pointer to an ISO maker ISO importer state. */
152typedef RTFSISOMKIMPORTER *PRTFSISOMKIMPORTER;
153
154
155/** Requested to import an unknown ISO format. */
156#define VERR_ISOMK_IMPORT_UNKNOWN_FORMAT (-24906)
157/** Too many volume descriptors in the import ISO. */
158#define VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS (-24907)
159/** Import ISO contains a bad volume descriptor header. */
160#define VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR (-24907)
161/** Import ISO contains more than one primary volume descriptor. */
162#define VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS (-24908)
163/** Import ISO contains more than one el torito descriptor. */
164#define VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS (-24909)
165/** Import ISO contains more than one joliet volume descriptor. */
166#define VERR_ISOMK_IMPORT_MULTIPLE_JOLIET_VOL_DESCS (-24908)
167/** Import ISO starts with supplementary volume descriptor before any
168 * primary ones. */
169#define VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY (-24909)
170/** Import ISO contains an unsupported primary volume descriptor version. */
171#define VERR_IOSMK_IMPORT_PRIMARY_VOL_DESC_VER (-24909)
172/** Import ISO contains a bad primary volume descriptor. */
173#define VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC (-24910)
174/** Import ISO contains an unsupported supplementary volume descriptor
175 * version. */
176#define VERR_IOSMK_IMPORT_SUP_VOL_DESC_VER (-24909)
177/** Import ISO contains a bad supplementary volume descriptor. */
178#define VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC (-24910)
179/** Import ISO uses a logical block size other than 2KB. */
180#define VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB (-24911)
181/** Import ISO contains more than volume. */
182#define VERR_ISOMK_IMPORT_MORE_THAN_ONE_VOLUME_IN_SET (-24912)
183/** Import ISO uses invalid volume sequence number. */
184#define VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO (-24913)
185/** Import ISO has different volume space sizes of primary and supplementary
186 * volume descriptors. */
187#define VERR_ISOMK_IMPORT_VOLUME_SPACE_SIZE_MISMATCH (-24913)
188/** Import ISO has different volume set sizes of primary and supplementary
189 * volume descriptors. */
190#define VERR_ISOMK_IMPORT_VOLUME_IN_SET_MISMATCH (-24913)
191/** Import ISO contains a bad root directory record. */
192#define VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC (-24914)
193/** Import ISO contains a zero sized root directory. */
194#define VERR_ISOMK_IMPORT_ZERO_SIZED_ROOT_DIR (-24915)
195/** Import ISO contains a root directory with a mismatching volume sequence
196 * number. */
197#define VERR_ISOMK_IMPORT_ROOT_VOLUME_SEQ_NO (-24916)
198/** Import ISO contains a root directory with an out of bounds data extent. */
199#define VERR_ISOMK_IMPORT_ROOT_DIR_EXTENT_OUT_OF_BOUNDS (-24917)
200/** Import ISO contains a root directory with a bad record length. */
201#define VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC_LENGTH (-24918)
202/** Import ISO contains a root directory without the directory flag set. */
203#define VERR_ISOMK_IMPORT_ROOT_DIR_WITHOUT_DIR_FLAG (-24919)
204/** Import ISO contains a root directory with multiple extents. */
205#define VERR_ISOMK_IMPORT_ROOT_DIR_IS_MULTI_EXTENT (-24920)
206/** Import ISO contains a too deep directory subtree. */
207#define VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE (-24921)
208
209/** Import ISO contains a bad directory record. */
210#define VERR_ISOMK_IMPORT_BAD_DIR_REC (-24922)
211/** Import ISO directory record with a mismatching volume sequence number. */
212#define VERR_ISOMK_IMPORT_DIR_REC_VOLUME_SEQ_NO (-24923)
213/** Import ISO directory with an extent that is out of bounds. */
214#define VERR_ISOMK_IMPORT_DIR_REC_EXTENT_OUT_OF_BOUNDS (-24924)
215/** Import ISO directory with a bad record length. */
216#define VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH (-24925)
217/** Import ISO directory with a bad name length. */
218#define VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME_LENGTH (-24926)
219/** Import ISO directory with a bad name. */
220#define VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME (-24927)
221
222
223
224/**
225 * Wrapper around RTErrInfoSetV.
226 *
227 * @returns rc
228 * @param pThis The importer instance.
229 * @param rc The status code to set.
230 * @param pszFormat The format string detailing the error.
231 * @param va Argument to the format string.
232 */
233static int rtFsIsoImpErrorV(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, va_list va)
234{
235 va_list vaCopy;
236 va_copy(vaCopy, va);
237 LogRel(("RTFsIsoMkImport error %Rrc: %N\n", rc, pszFormat, &vaCopy));
238 va_end(vaCopy);
239
240 if (RT_SUCCESS(pThis->rc))
241 {
242 pThis->rc = rc;
243 rc = RTErrInfoSetV(pThis->pErrInfo, rc, pszFormat, va);
244 }
245
246 pThis->pResults->cErrors++;
247 return rc;
248}
249
250
251/**
252 * Wrapper around RTErrInfoSetF.
253 *
254 * @returns rc
255 * @param pThis The importer instance.
256 * @param rc The status code to set.
257 * @param pszFormat The format string detailing the error.
258 * @param ... Argument to the format string.
259 */
260static int rtFsIsoImpError(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, ...)
261{
262 va_list va;
263 va_start(va, pszFormat);
264 rc = rtFsIsoImpErrorV(pThis, rc, pszFormat, va);
265 va_end(va);
266 return rc;
267}
268
269
270/**
271 * Callback for destroying a RTFSISOMKIMPBLOCK2FILE node.
272 *
273 * @returns VINF_SUCCESS
274 * @param pNode The node to destroy.
275 * @param pvUser Ignored.
276 */
277static DECLCALLBACK(int) rtFsIsoMakerImportDestroyData2File(PAVLU32NODECORE pNode, void *pvUser)
278{
279 RT_NOREF(pvUser);
280 RTMemFree(pNode);
281 return VINF_SUCCESS;
282}
283
284
285/**
286 * Validates a directory record.
287 *
288 * @returns IPRT status code (safe to ignore, see pThis->rc).
289 * @param pThis The importer instance.
290 * @param pDirRec The root directory record to validate.
291 * @param cbMax The maximum size.
292 */
293static int rtFsIsoImportValidateDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax)
294{
295 /*
296 * Validate dual fields.
297 */
298 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
299 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
300 "Invalid dir rec size field: {%#RX32,%#RX32}",
301 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
302
303 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
304 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
305 "Invalid dir rec extent field: {%#RX32,%#RX32}",
306 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
307
308 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
309 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
310 "Invalid dir rec volume sequence ID field: {%#RX16,%#RX16}",
311 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
312
313 /*
314 * Check values.
315 */
316 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
317 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_VOLUME_SEQ_NO,
318 "Expected dir rec to have same volume sequence number as primary volume: %#x, expected %#x",
319 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
320
321 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
322 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_EXTENT_OUT_OF_BOUNDS,
323 "Invalid dir rec extent: %#RX32, max %#RX32",
324 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
325
326 if (pDirRec->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength)
327 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
328 "Root dir record size is too small: %#x (min %#x)",
329 pDirRec->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength);
330 if (pDirRec->cbDirRec > cbMax)
331 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
332 "Root dir record size is too big: %#x (max %#x)", pDirRec->cbDirRec, cbMax);
333 return VINF_SUCCESS;
334}
335
336
337/**
338 * Validates a dot or dot-dot directory record.
339 *
340 * @returns IPRT status code (safe to ignore, see pThis->rc).
341 * @param pThis The importer instance.
342 * @param pDirRec The root directory record to validate.
343 * @param cbMax The maximum size.
344 * @param bName The name byte (0x00: '.', 0x01: '..').
345 */
346static int rtFsIsoImportValidateDotDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax, uint8_t bName)
347{
348 int rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbMax);
349 if (RT_SUCCESS(rc))
350 {
351 if (pDirRec->bFileIdLength != 1)
352 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME_LENGTH,
353 "Invalid dot dir rec file id length: %u", pDirRec->bFileIdLength);
354 if ((uint8_t)pDirRec->achFileId[0] != bName)
355 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME,
356 "Invalid dot dir rec file id: %#x, expected %#x", pDirRec->achFileId[0], bName);
357 }
358 return rc;
359}
360
361
362static int rtFsIsoImportProcessIso9660TreeWorker(PRTFSISOMKIMPORTER pThis, uint32_t idxDir,
363 uint32_t offDirBlock, uint32_t cbDir, uint8_t cDepth, bool fUnicode,
364 PRTLISTANCHOR pTodoList)
365{
366 /*
367 * Restrict the depth to try avoid loops.
368 */
369 if (cDepth > RTFSISOMK_IMPORT_MAX_DEPTH)
370 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE, "Dir at %#x LB %#x is too deep", offDirBlock, cbDir);
371
372 /*
373 * Read the first chunk into the big buffer.
374 */
375 uint32_t cbChunk = RT_MIN(cbDir, sizeof(pThis->abBuf));
376 uint64_t off = (uint64_t)offDirBlock * ISO9660_SECTOR_SIZE;
377 int rc = RTVfsFileReadAt(pThis->hSrcFile, off, pThis->abBuf, cbChunk, NULL);
378 if (RT_FAILURE(rc))
379 return rtFsIsoImpError(pThis, rc, "Error reading directory at %#RX64 (%#RX32 / %#RX32): %Rrc", off, cbChunk, cbDir);
380
381 cbDir -= cbChunk;
382 off += cbChunk;
383
384 /*
385 * Skip the current and parent directory entries.
386 */
387 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
388 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x00);
389 if (RT_FAILURE(rc))
390 return rc;
391
392 cbChunk -= pDirRec->cbDirRec;
393 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
394 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x01);
395 if (RT_FAILURE(rc))
396 return rc;
397
398 cbChunk -= pDirRec->cbDirRec;
399 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
400
401 /*
402 * Work our way thru all the directory records.
403 */
404 Log3(("rtFsIsoImportProcessIso9660TreeWorker: Starting at @%#RX64 LB %#RX32 (out of %#RX32) in %#x\n",
405 off - cbChunk, cbChunk, cbChunk + cbDir, idxDir));
406 while (cbChunk > 0)
407 {
408 /*
409 * Do we need to read some more?
410 */
411 if ( cbChunk > UINT8_MAX
412 || cbDir == 0)
413 { /* No, we don't. */ }
414 else
415 {
416 pDirRec = (PCISO9660DIRREC)memmove(&pThis->abBuf[ISO9660_SECTOR_SIZE - cbChunk], pDirRec, cbChunk);
417
418 Assert(!(off & (ISO9660_SECTOR_SIZE - 1)));
419 uint32_t cbToRead = RT_MIN(cbDir, sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE);
420 rc = RTVfsFileReadAt(pThis->hSrcFile, off, &pThis->abBuf[ISO9660_SECTOR_SIZE], cbToRead, NULL);
421 if (RT_FAILURE(rc))
422 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", off, cbToRead);
423
424 Log3(("rtFsIsoImportProcessIso9660TreeWorker: Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
425 cbToRead, off, off - cbChunk, cbChunk + cbToRead));
426 off += cbToRead;
427 cbDir -= cbToRead;
428 cbChunk += cbToRead;
429 }
430
431 /* If null length, skip to the next sector. May have to read some then. */
432 if (pDirRec->cbDirRec == 0)
433 {
434 uint64_t offChunk = off - cbChunk;
435 uint32_t cbSkip = ISO9660_SECTOR_SIZE - ((uint32_t)offChunk & (ISO9660_SECTOR_SIZE - 1));
436 if (cbSkip < cbChunk)
437 {
438 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + cbSkip);
439 cbChunk -= cbSkip;
440 if ( cbChunk <= UINT8_MAX
441 && cbDir == 0)
442 {
443 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> Restart loop\n"));
444 continue;
445 }
446 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> jumped %#RX32 to @%#RX64 LB %#RX32\n",
447 cbSkip, off - cbChunk, cbChunk));
448 }
449 /* ASSUMES we're working in multiples of sectors! */
450 else if (cbDir == 0)
451 break;
452 else
453 {
454 Assert(!(off & (ISO9660_SECTOR_SIZE - 1)));
455 uint32_t cbToRead = RT_MIN(cbDir, sizeof(pThis->abBuf));
456 rc = RTVfsFileReadAt(pThis->hSrcFile, off, pThis->abBuf, cbToRead, NULL);
457 if (RT_FAILURE(rc))
458 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", off, cbToRead);
459
460 Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
461 cbToRead, off, off - cbChunk, cbChunk + cbToRead));
462 off += cbToRead;
463 cbDir -= cbToRead;
464 cbChunk += cbToRead;
465 pDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
466 }
467 }
468
469 /*
470 * Validate the directory record. Give up if not valid since we're
471 * likely to get error with subsequent record too.
472 */
473 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
474 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
475 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
476 Log3(("pDirRec=&abBuf[%#07zx]: @%#010RX64 cb=%#04x ff=%#04x off=%#010RX32 cb=%#010RX32 cbSys=%#x id=%.*Rhxs\n",
477 (uintptr_t)pDirRec - (uintptr_t)&pThis->abBuf[0], off - cbChunk, pDirRec->cbDirRec, pDirRec->fFileFlags,
478 ISO9660_GET_ENDIAN(&pDirRec->offExtent), ISO9660_GET_ENDIAN(&pDirRec->cbData), cbSys,
479 pDirRec->bFileIdLength, pDirRec->achFileId));
480 rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbChunk);
481 if (RT_FAILURE(rc))
482 return rc;
483
484 /*
485 * Convert the name into the name buffer (szNameBuf).
486 */
487 if (!fUnicode)
488 {
489 memcpy(pThis->szNameBuf, pDirRec->achFileId, pDirRec->bFileIdLength);
490 pThis->szNameBuf[pDirRec->bFileIdLength] = '\0';
491 rc = RTStrValidateEncoding(pThis->szNameBuf);
492 }
493 else
494 {
495 char *pszDst = pThis->szNameBuf;
496 rc = RTUtf16BigToUtf8Ex((PRTUTF16)pDirRec->achFileId, pDirRec->bFileIdLength / sizeof(RTUTF16),
497 &pszDst, sizeof(pThis->szNameBuf), NULL);
498 }
499 if (RT_SUCCESS(rc))
500 {
501 /* Drop the version from the name. */
502 /** @todo preserve the file version on import. */
503 size_t cchName = strlen(pThis->szNameBuf);
504 if ( !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
505 && cchName > 2
506 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - 1]))
507 {
508 uint32_t offName = 2;
509 while ( offName <= 5
510 && offName + 1 < cchName
511 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - offName]))
512 offName++;
513 if ( offName + 1 < cchName
514 && pThis->szNameBuf[cchName - offName] == ';')
515 pThis->szNameBuf[cchName - offName] = '\0';
516 }
517 Log3((" --> name='%s'\n", pThis->szNameBuf));
518
519 /** @todo rock ridge. */
520 if (cbSys > 0)
521 {
522 RT_NOREF(pbSys);
523 }
524 /*
525 * Add the object and enter it into the namespace.
526 */
527 PRTFSISOMKIMPBLOCK2FILE pBlock2File = NULL;
528 uint32_t idxObj = UINT32_MAX;
529 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
530 {
531 rc = RTFsIsoMakerAddUnnamedDir(pThis->hIsoMaker, &idxObj);
532 Log3((" --> added directory #%#x'\n", idxObj));
533 if (RT_SUCCESS(rc))
534 pThis->pResults->cAddedDirs++;
535 }
536 else
537 {
538 /* Add the common source file if we haven't done that already. */
539 if (pThis->idxSrcFile != UINT32_MAX)
540 { /* likely */ }
541 else
542 {
543 rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
544 if (RT_FAILURE(rc))
545 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
546 Assert(pThis->idxSrcFile != UINT32_MAX);
547 }
548
549 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, ISO9660_GET_ENDIAN(&pDirRec->offExtent));
550 if (!pBlock2File)
551 {
552 rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
553 ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)ISO9660_SECTOR_SIZE,
554 ISO9660_GET_ENDIAN(&pDirRec->cbData), NULL /*pObjInfo*/, &idxObj);
555 Log3((" --> added new file #%#x\n", idxObj));
556 if (RT_SUCCESS(rc))
557 {
558 pThis->pResults->cAddedFiles++;
559 pThis->pResults->cbAddedDataBlocks += RT_ALIGN_32(ISO9660_GET_ENDIAN(&pDirRec->cbData), ISO9660_SECTOR_SIZE);
560 }
561 }
562 else
563 {
564 idxObj = pBlock2File->idxObj;
565 Log3((" --> existing file #%#x'\n", idxObj));
566 rc = VINF_SUCCESS;
567 }
568 }
569 if (RT_SUCCESS(rc))
570 {
571 rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxDir,
572 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET,
573 pThis->szNameBuf);
574 if (RT_SUCCESS(rc))
575 {
576 pThis->pResults->cAddedNames++;
577
578 /*
579 * Remember the data location if this is a file, if it's a
580 * directory push it onto the traversal stack.
581 */
582 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
583 {
584 PRTFSISOMKIMPDIR pImpDir = (PRTFSISOMKIMPDIR)RTMemAlloc(sizeof(*pImpDir));
585 AssertReturn(pImpDir, rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPDIR"));
586 pImpDir->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
587 pImpDir->offDirBlock = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
588 pImpDir->idxObj = idxObj;
589 pImpDir->cDepth = cDepth + 1;
590 RTListAppend(pTodoList, &pImpDir->Entry);
591 }
592 else if (!pBlock2File)
593 {
594 pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTMemAlloc(sizeof(*pBlock2File));
595 AssertReturn(pBlock2File, rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPBLOCK2FILE"));
596 pBlock2File->idxObj = idxObj;
597 pBlock2File->Core.Key = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
598 bool fRc = RTAvlU32Insert(&pThis->Block2FileRoot, &pBlock2File->Core);
599 Assert(fRc); RT_NOREF(fRc);
600 }
601 }
602 else
603 rtFsIsoImpError(pThis, rc, "Invalid name at %#RX64: %.Rhxs",
604 off - cbChunk, pDirRec->bFileIdLength, pDirRec->achFileId);
605 }
606 else
607 rtFsIsoImpError(pThis, rc, "Error adding '%s' (fFileFlags=%#x): %Rrc",
608 pThis->szNameBuf, pDirRec->fFileFlags, rc);
609 }
610 else
611 rtFsIsoImpError(pThis, rc, "Invalid name at %#RX64: %.Rhxs",
612 off - cbChunk, pDirRec->bFileIdLength, pDirRec->achFileId);
613
614 /*
615 * Advance to the next directory record.
616 */
617 cbChunk -= pDirRec->cbDirRec;
618 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
619 }
620
621 return VINF_SUCCESS;
622}
623
624
625static int rtFsIsoImportProcessIso9660Tree(PRTFSISOMKIMPORTER pThis, uint32_t offDirBlock, uint32_t cbDir, bool fUnicode)
626{
627 /*
628 * Make sure we've got a root in the namespace.
629 */
630 uint32_t idxDir = RTFsIsoMakerGetObjIdxForPath(pThis->hIsoMaker,
631 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET,
632 "/");
633 if (idxDir == UINT32_MAX)
634 {
635 idxDir = RTFSISOMAKER_CFG_IDX_ROOT;
636 int rc = RTFsIsoMakerObjSetPath(pThis->hIsoMaker, RTFSISOMAKER_CFG_IDX_ROOT,
637 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET, "/");
638 if (RT_FAILURE(rc))
639 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjSetPath failed on root dir: %Rrc", rc);
640 }
641 Assert(idxDir == RTFSISOMAKER_CFG_IDX_ROOT);
642
643 /*
644 * Directories.
645 */
646 int rc = VINF_SUCCESS;
647 uint8_t cDepth = 0;
648 RTLISTANCHOR TodoList;
649 RTListInit(&TodoList);
650 for (;;)
651 {
652 int rc2 = rtFsIsoImportProcessIso9660TreeWorker(pThis, idxDir, offDirBlock, cbDir, cDepth, fUnicode, &TodoList);
653 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
654 rc = rc2;
655
656 /*
657 * Pop the next directory.
658 */
659 PRTFSISOMKIMPDIR pNext = RTListRemoveLast(&TodoList, RTFSISOMKIMPDIR, Entry);
660 if (!pNext)
661 break;
662 idxDir = pNext->idxObj;
663 offDirBlock = pNext->offDirBlock;
664 cbDir = pNext->cbDir;
665 cDepth = pNext->cDepth;
666 RTMemFree(pNext);
667 }
668
669 return rc;
670}
671
672
673/**
674 * Validates a root directory record.
675 *
676 * @returns IPRT status code (safe to ignore, see pThis->rc).
677 * @param pThis The importer instance.
678 * @param pDirRec The root directory record to validate.
679 */
680static int rtFsIsoImportValidateRootDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec)
681{
682 /*
683 * Validate dual fields.
684 */
685 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
686 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
687 "Invalid root dir size: {%#RX32,%#RX32}",
688 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
689
690 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
691 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
692 "Invalid root dir extent: {%#RX32,%#RX32}",
693 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
694
695 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
696 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
697 "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
698 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
699
700 /*
701 * Check values.
702 */
703 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
704 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_VOLUME_SEQ_NO,
705 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
706 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
707
708 if (ISO9660_GET_ENDIAN(&pDirRec->cbData) == 0)
709 return RTErrInfoSet(pThis->pErrInfo, VERR_ISOMK_IMPORT_ZERO_SIZED_ROOT_DIR, "Zero sized root dir");
710
711 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
712 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_EXTENT_OUT_OF_BOUNDS,
713 "Invalid root dir extent: %#RX32, max %#RX32",
714 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
715
716 if (pDirRec->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
717 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC_LENGTH,
718 "Root dir record size is too small: %#x (min %#x)",
719 pDirRec->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
720
721 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
722 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_WITHOUT_DIR_FLAG,
723 "Root dir is not flagged as directory: %#x", pDirRec->fFileFlags);
724 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
725 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_IS_MULTI_EXTENT,
726 "Root dir is cannot be multi-extent: %#x", pDirRec->fFileFlags);
727
728 return VINF_SUCCESS;
729}
730
731
732/**
733 * Processes a primary volume descriptor, importing all files described by its
734 * namespace.
735 *
736 * @returns IPRT status code (safe to ignore, see pThis->rc).
737 * @param pThis The importer instance.
738 * @param pVolDesc The primary volume descriptor.
739 */
740static int rtFsIsoImportProcessPrimaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660PRIMARYVOLDESC pVolDesc)
741{
742 /*
743 * Validate dual fields first.
744 */
745 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
746 return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_PRIMARY_VOL_DESC_VER,
747 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
748
749 if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
750 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
751 "Mismatching logical block size: {%#RX16,%#RX16}",
752 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
753 if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
754 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
755 "Mismatching volume space size: {%#RX32,%#RX32}",
756 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
757 if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
758 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
759 "Mismatching volumes in set: {%#RX16,%#RX16}",
760 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
761 if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
762 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
763 "Mismatching volume sequence no.: {%#RX16,%#RX16}",
764 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
765 if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
766 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
767 "Mismatching path table size: {%#RX32,%#RX32}",
768 RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
769
770 /*
771 * Validate field values against our expectations.
772 */
773 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
774 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
775 "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
776
777 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != 1)
778 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MORE_THAN_ONE_VOLUME_IN_SET,
779 "Volumes in set: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet));
780
781 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != 1)
782 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
783 "Unexpected volume sequence number: %#x", ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo));
784
785 /*
786 * Gather info we need.
787 */
788 pThis->cBlocksInPrimaryVolumeSpace = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize);
789 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)ISO9660_SECTOR_SIZE;
790 pThis->cVolumesInSet = ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet);
791 pThis->idPrimaryVol = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo);
792
793 /*
794 * Validate the root directory record.
795 */
796 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
797 if (RT_SUCCESS(rc))
798 {
799 /*
800 * Process the directory tree. Start by establishing a root directory.
801 */
802 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PRIMARY_ISO))
803 rc = rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
804 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), false /*fUnicode*/);
805 }
806
807 return rc;
808}
809
810
811static int rtFsIsoImportProcessSupplementaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660SUPVOLDESC pVolDesc)
812{
813 /*
814 * Validate dual fields first.
815 */
816 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
817 return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_SUP_VOL_DESC_VER,
818 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
819
820 if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
821 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
822 "Mismatching logical block size: {%#RX16,%#RX16}",
823 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
824 if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
825 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
826 "Mismatching volume space size: {%#RX32,%#RX32}",
827 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
828 if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
829 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
830 "Mismatching volumes in set: {%#RX16,%#RX16}",
831 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
832 if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
833 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
834 "Mismatching volume sequence no.: {%#RX16,%#RX16}",
835 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
836 if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
837 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
838 "Mismatching path table size: {%#RX32,%#RX32}",
839 RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
840
841 /*
842 * Validate field values against our expectations.
843 */
844 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
845 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
846 "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
847
848 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
849 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_VOLUME_IN_SET_MISMATCH, "Volumes in set: %#x, expected %#x",
850 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
851
852 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
853 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
854 "Unexpected volume sequence number: %#x (expected %#x)",
855 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
856
857 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
858 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
859 "Volume space size differs between primary and supplementary descriptors: %#x, primary %#x",
860 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
861
862 /*
863 * Validate the root directory record.
864 */
865 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
866 if (RT_FAILURE(rc))
867 return rc;
868
869 /*
870 * Is this a joliet descriptor? Ignore if not.
871 */
872 uint8_t uJolietLevel = 0;
873 if ( pVolDesc->abEscapeSequences[0] == ISO9660_JOLIET_ESC_SEQ_0
874 && pVolDesc->abEscapeSequences[1] == ISO9660_JOLIET_ESC_SEQ_1)
875 switch (pVolDesc->abEscapeSequences[2])
876 {
877 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1: uJolietLevel = 1; break;
878 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2: uJolietLevel = 2; break;
879 case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3: uJolietLevel = 3; break;
880 default: Log(("rtFsIsoImportProcessSupplementaryDesc: last joliet escape sequence byte doesn't match: %#x\n",
881 pVolDesc->abEscapeSequences[2]));
882 }
883 if (uJolietLevel == 0)
884 return VINF_SUCCESS;
885
886 /*
887 * Only one joliet descriptor.
888 */
889 if (pThis->fSeenJoliet)
890 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_JOLIET_VOL_DESCS,
891 "More than one Joliet volume descriptor is not supported");
892 pThis->fSeenJoliet = true;
893
894 /*
895 * Process the directory tree.
896 */
897 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_JOLIET))
898 return rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
899 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), true /*fUnicode*/);
900 return VINF_SUCCESS;
901}
902
903
904static int rtFsIsoImportProcessElToritoDesc(PRTFSISOMKIMPORTER pThis, PISO9660BOOTRECORDELTORITO pElTorito)
905{
906 RT_NOREF(pThis, pElTorito);
907 return VINF_SUCCESS;
908}
909
910
911/**
912 * Imports an existing ISO.
913 *
914 * Just like other source files, the existing image must remain present and
915 * unmodified till the ISO maker is done with it.
916 *
917 * @returns IRPT status code.
918 * @param hIsoMaker The ISO maker handle.
919 * @param pszIso Path to the existing image to import / clone.
920 * This is fed to RTVfsChainOpenFile.
921 * @param fFlags Reserved for the future, MBZ.
922 * @param poffError Where to return the position in @a pszIso
923 * causing trouble when opening it for reading.
924 * Optional.
925 * @param pErrInfo Where to return additional error information.
926 * Optional.
927 */
928RTDECL(int) RTFsIsoMakerImport(RTFSISOMAKER hIsoMaker, const char *pszIso, uint32_t fFlags,
929 PRTFSISOMAKERIMPORTRESULTS pResults, PRTERRINFO pErrInfo)
930{
931 /*
932 * Validate input.
933 */
934 AssertPtrReturn(pResults, VERR_INVALID_POINTER);
935 pResults->cAddedNames = 0;
936 pResults->cAddedDirs = 0;
937 pResults->cbAddedDataBlocks = 0;
938 pResults->cAddedFiles = 0;
939 pResults->cBootCatEntries = UINT32_MAX;
940 pResults->cbSysArea = 0;
941 pResults->cErrors = 0;
942 pResults->offError = UINT32_MAX;
943 AssertReturn(!(fFlags & ~RTFSISOMK_IMPORT_F_VALID_MASK), VERR_INVALID_FLAGS);
944
945 /*
946 * Open the input file and start working on it.
947 */
948 RTVFSFILE hSrcFile;
949 int rc = RTVfsChainOpenFile(pszIso, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE,
950 &hSrcFile, &pResults->offError, pErrInfo);
951 if (RT_FAILURE(rc))
952 return rc;
953 pResults->offError = UINT32_MAX;
954
955
956 /*
957 * Allocate and init the importer state.
958 */
959 PRTFSISOMKIMPORTER pThis = (PRTFSISOMKIMPORTER)RTMemAllocZ(sizeof(*pThis));
960 if (pThis)
961 {
962 pThis->hIsoMaker = hIsoMaker;
963 pThis->fFlags = fFlags;
964 pThis->rc = VINF_SUCCESS;
965 pThis->pErrInfo = pErrInfo;
966 pThis->hSrcFile = hSrcFile;
967 pThis->idxSrcFile = UINT32_MAX;
968 //pThis->Block2FileRoot = NULL;
969 //pThis->cBlocksInPrimaryVolumeSpace = 0;
970 //pThis->cbPrimaryVolumeSpace = 0
971 //pThis->cVolumesInSet = 0;
972 //pThis->idPrimaryVol = 0;
973 //pThis->fSeenJoliet = false;
974 pThis->pResults = pResults;
975
976 /*
977 * Check if this looks like a plausible ISO by checking out the first volume descriptor.
978 */
979 rc = RTVfsFileReadAt(hSrcFile, _32K, &pThis->uSectorBuf.PrimVolDesc, sizeof(pThis->uSectorBuf.PrimVolDesc), NULL);
980 if (RT_SUCCESS(rc))
981 {
982 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] == ISO9660VOLDESC_STD_ID_0
983 && pThis->uSectorBuf.VolDescHdr.achStdId[1] == ISO9660VOLDESC_STD_ID_1
984 && pThis->uSectorBuf.VolDescHdr.achStdId[2] == ISO9660VOLDESC_STD_ID_2
985 && pThis->uSectorBuf.VolDescHdr.achStdId[3] == ISO9660VOLDESC_STD_ID_3
986 && pThis->uSectorBuf.VolDescHdr.achStdId[4] == ISO9660VOLDESC_STD_ID_4
987 && ( pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY
988 || pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD) )
989 {
990 /*
991 * Process the volume descriptors using the sector buffer, starting
992 * with the one we've already got sitting there. We postpone processing
993 * the el torito one till after the others, so we can name files and size
994 * referenced in it.
995 */
996 uint32_t cPrimaryVolDescs = 0;
997 uint32_t iElTorito = UINT32_MAX;
998 uint32_t iVolDesc = 0;
999 for (;;)
1000 {
1001 switch (pThis->uSectorBuf.VolDescHdr.bDescType)
1002 {
1003 case ISO9660VOLDESC_TYPE_PRIMARY:
1004 cPrimaryVolDescs++;
1005 if (cPrimaryVolDescs == 1)
1006 rtFsIsoImportProcessPrimaryDesc(pThis, &pThis->uSectorBuf.PrimVolDesc);
1007 else
1008 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS,
1009 "Only a single primary volume descriptor is currently supported");
1010 break;
1011
1012 case ISO9660VOLDESC_TYPE_SUPPLEMENTARY:
1013 if (cPrimaryVolDescs > 0)
1014 rtFsIsoImportProcessSupplementaryDesc(pThis, &pThis->uSectorBuf.SupVolDesc);
1015 else
1016 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY,
1017 "Primary volume descriptor expected before any supplementary descriptors!");
1018 break;
1019
1020 case ISO9660VOLDESC_TYPE_BOOT_RECORD:
1021 if (strcmp(pThis->uSectorBuf.ElToritoDesc.achBootSystemId,
1022 ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID) == 0)
1023 {
1024 if (iElTorito == UINT32_MAX)
1025 iElTorito = iVolDesc;
1026 else
1027 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS,
1028 "Only a single El Torito descriptor exepcted!");
1029 }
1030 break;
1031
1032 case ISO9660VOLDESC_TYPE_PARTITION:
1033 /* ignore for now */
1034 break;
1035
1036 case ISO9660VOLDESC_TYPE_TERMINATOR:
1037 AssertFailed();
1038 break;
1039 }
1040
1041
1042 /*
1043 * Read the next volume descriptor and check the signature.
1044 */
1045 iVolDesc++;
1046 if (iVolDesc >= 32)
1047 {
1048 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS, "Parses at most 32 volume descriptors");
1049 break;
1050 }
1051
1052 rc = RTVfsFileReadAt(hSrcFile, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
1053 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
1054 if (RT_FAILURE(rc))
1055 {
1056 rtFsIsoImpError(pThis, rc, "Error reading the volume descriptor #%u at %#RX32: %Rrc",
1057 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE, rc);
1058 break;
1059 }
1060
1061 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
1062 || pThis->uSectorBuf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
1063 || pThis->uSectorBuf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
1064 || pThis->uSectorBuf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
1065 || pThis->uSectorBuf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
1066 {
1067 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR,
1068 "Invalid volume descriptor header #%u at %#RX32: %.*Rhxs",
1069 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
1070 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
1071 break;
1072 }
1073 /** @todo UDF support. */
1074 if (pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
1075 break;
1076 }
1077
1078 /*
1079 * Process the system area.
1080 */
1081 if (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX)
1082 {
1083 rc = RTVfsFileReadAt(hSrcFile, 0, pThis->abBuf, _32K, NULL);
1084 if (RT_SUCCESS(rc))
1085 {
1086 if (!ASMMemIsAllU8(pThis->abBuf, _32K, 0))
1087 {
1088 /* Drop zero sectors from the end. */
1089 uint32_t cbSysArea = _32K;
1090 while ( cbSysArea >= ISO9660_SECTOR_SIZE
1091 && ASMMemIsAllU8(&pThis->abBuf[cbSysArea - ISO9660_SECTOR_SIZE], ISO9660_SECTOR_SIZE, 0))
1092 cbSysArea -= ISO9660_SECTOR_SIZE;
1093
1094 /** @todo HFS */
1095 pThis->pResults->cbSysArea = cbSysArea;
1096 rc = RTFsIsoMakerSetSysAreaContent(hIsoMaker, pThis->abBuf, cbSysArea, 0);
1097 if (RT_FAILURE(rc))
1098 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetSysAreaContent failed: %Rrc", rc);
1099 }
1100 }
1101 else
1102 rtFsIsoImpError(pThis, rc, "Error reading the system area (0..32KB): %Rrc", rc);
1103 }
1104
1105 /*
1106 * Do the El Torito descriptor.
1107 */
1108 if ( iElTorito != UINT32_MAX
1109 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BOOT)
1110 && (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX))
1111 {
1112 rc = RTVfsFileReadAt(hSrcFile, _32K + iElTorito * ISO9660_SECTOR_SIZE,
1113 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
1114 if (RT_SUCCESS(rc))
1115 rtFsIsoImportProcessElToritoDesc(pThis, &pThis->uSectorBuf.ElToritoDesc);
1116 else
1117 rtFsIsoImpError(pThis, rc, "Error reading the El Torito volume descriptor at %#RX32: %Rrc",
1118 _32K + iElTorito * ISO9660_SECTOR_SIZE, rc);
1119 }
1120
1121 /*
1122 * Return the first error status.
1123 */
1124 rc = pThis->rc;
1125 }
1126 else
1127 rc = RTErrInfoSetF(pErrInfo, VERR_ISOMK_IMPORT_UNKNOWN_FORMAT, "Invalid volume descriptor header: %.*Rhxs",
1128 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
1129 }
1130
1131 /*
1132 * Destroy the state.
1133 */
1134 RTAvlU32Destroy(&pThis->Block2FileRoot, rtFsIsoMakerImportDestroyData2File, NULL);
1135 RTMemFree(pThis);
1136 }
1137 else
1138 rc = VERR_NO_MEMORY;
1139 RTVfsFileRelease(hSrcFile);
1140 return rc;
1141
1142}
1143
Note: See TracBrowser for help on using the repository browser.

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