VirtualBox

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

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

IPRT: More ISO maker code (import related).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.8 KB
Line 
1/* $Id: isomakerimport.cpp 67482 2017-06-19 16:57:58Z 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 /** Sector buffer for volume descriptors and such. */
130 union
131 {
132 uint8_t ab[ISO9660_SECTOR_SIZE];
133 ISO9660VOLDESCHDR VolDescHdr;
134 ISO9660PRIMARYVOLDESC PrimVolDesc;
135 ISO9660SUPVOLDESC SupVolDesc;
136 ISO9660BOOTRECORDELTORITO ElToritoDesc;
137 } uSectorBuf;
138
139 /** Name buffer. */
140 char szNameBuf[_2K];
141
142 /** A somewhat larger buffer. */
143 uint8_t abBuf[_64K];
144} RTFSISOMKIMPORTER;
145/** Pointer to an ISO maker ISO importer state. */
146typedef RTFSISOMKIMPORTER *PRTFSISOMKIMPORTER;
147
148
149/** Requested to import an unknown ISO format. */
150#define VERR_ISOMK_IMPORT_UNKNOWN_FORMAT (-24906)
151/** Too many volume descriptors in the import ISO. */
152#define VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS (-24907)
153/** Import ISO contains a bad volume descriptor header. */
154#define VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR (-24907)
155/** Import ISO contains more than one primary volume descriptor. */
156#define VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS (-24908)
157/** Import ISO contains more than one el torito descriptor. */
158#define VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS (-24909)
159/** Import ISO starts with supplementary volume descriptor before any
160 * primary ones. */
161#define VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY (-24909)
162/** Import ISO contains a bad primary volume descriptor. */
163#define VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC (-24910)
164/** Import ISO uses a logical block size other than 2KB. */
165#define VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB (-24911)
166/** Import ISO contains more than volume. */
167#define VERR_ISOMK_IMPORT_MORE_THAN_ONE_VOLUME_IN_SET (-24912)
168/** Import ISO uses invalid volume sequence number. */
169#define VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO (-24913)
170/** Import ISO contains a bad root directory record. */
171#define VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC (-24914)
172/** Import ISO contains a zero sized root directory. */
173#define VERR_ISOMK_IMPORT_ZERO_SIZED_ROOT_DIR (-24915)
174/** Import ISO contains a root directory with a mismatching volume sequence
175 * number. */
176#define VERR_ISOMK_IMPORT_ROOT_VOLUME_SEQ_NO (-24916)
177/** Import ISO contains a root directory with an out of bounds data extent. */
178#define VERR_ISOMK_IMPORT_ROOT_DIR_EXTENT_OUT_OF_BOUNDS (-24917)
179/** Import ISO contains a root directory with a bad record length. */
180#define VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC_LENGTH (-24918)
181/** Import ISO contains a root directory without the directory flag set. */
182#define VERR_ISOMK_IMPORT_ROOT_DIR_WITHOUT_DIR_FLAG (-24919)
183/** Import ISO contains a root directory with multiple extents. */
184#define VERR_ISOMK_IMPORT_ROOT_DIR_IS_MULTI_EXTENT (-24920)
185/** Import ISO contains a too deep directory subtree. */
186#define VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE (-24921)
187
188#define VERR_ISOMK_IMPORT_BAD_DIR_REC (-24922)
189#define VERR_ISOMK_IMPORT_DIR_REC_VOLUME_SEQ_NO (-24923)
190#define VERR_ISOMK_IMPORT_DIR_REC_EXTENT_OUT_OF_BOUNDS (-24924)
191#define VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH (-24925)
192#define VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME_LENGTH (-24926)
193#define VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME (-24927)
194
195
196
197/**
198 * Wrapper around RTErrInfoSetV.
199 *
200 * @returns rc
201 * @param pThis The importer instance.
202 * @param rc The status code to set.
203 * @param pszFormat The format string detailing the error.
204 * @param va Argument to the format string.
205 */
206static int rtFsIsoImpErrorV(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, va_list va)
207{
208 if (RT_SUCCESS(pThis->rc))
209 {
210 pThis->rc = rc;
211 rc = RTErrInfoSetV(pThis->pErrInfo, rc, pszFormat, va);
212 }
213 return rc;
214}
215
216
217/**
218 * Wrapper around RTErrInfoSetF.
219 *
220 * @returns rc
221 * @param pThis The importer instance.
222 * @param rc The status code to set.
223 * @param pszFormat The format string detailing the error.
224 * @param ... Argument to the format string.
225 */
226static int rtFsIsoImpError(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, ...)
227{
228 va_list va;
229 va_start(va, pszFormat);
230 rc = rtFsIsoImpErrorV(pThis, rc, pszFormat, va);
231 va_end(va);
232 return rc;
233}
234
235
236/**
237 * Callback for destroying a RTFSISOMKIMPBLOCK2FILE node.
238 *
239 * @returns VINF_SUCCESS
240 * @param pNode The node to destroy.
241 * @param pvUser Ignored.
242 */
243static DECLCALLBACK(int) rtFsIsoMakerImportDestroyData2File(PAVLU32NODECORE pNode, void *pvUser)
244{
245 RT_NOREF(pvUser);
246 RTMemFree(pNode);
247 return VINF_SUCCESS;
248}
249
250
251/**
252 * Validates a directory record.
253 *
254 * @returns IPRT status code (safe to ignore, see pThis->rc).
255 * @param pThis The importer instance.
256 * @param pDirRec The root directory record to validate.
257 * @param cbMax The maximum size.
258 */
259static int rtFsIsoImportValidateDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax)
260{
261 /*
262 * Validate dual fields.
263 */
264 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
265 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
266 "Invalid dir rec size field: {%#RX32,%#RX32}",
267 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
268
269 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
270 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
271 "Invalid dir rec extent field: {%#RX32,%#RX32}",
272 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
273
274 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
275 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
276 "Invalid dir rec volume sequence ID field: {%#RX16,%#RX16}",
277 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
278
279 /*
280 * Check values.
281 */
282 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
283 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_VOLUME_SEQ_NO,
284 "Expected dir rec to have same volume sequence number as primary volume: %#x, expected %#x",
285 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
286
287 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
288 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_EXTENT_OUT_OF_BOUNDS,
289 "Invalid dir rec extent: %#RX32, max %#RX32",
290 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
291
292 if (pDirRec->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength)
293 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
294 "Root dir record size is too small: %#x (min %#x)",
295 pDirRec->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength);
296 if (pDirRec->cbDirRec > cbMax)
297 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
298 "Root dir record size is too big: %#x (max %#x)", pDirRec->cbDirRec, cbMax);
299 return VINF_SUCCESS;
300}
301
302
303/**
304 * Validates a dot or dot-dot directory record.
305 *
306 * @returns IPRT status code (safe to ignore, see pThis->rc).
307 * @param pThis The importer instance.
308 * @param pDirRec The root directory record to validate.
309 * @param cbMax The maximum size.
310 * @param bName The name byte (0x00: '.', 0x01: '..').
311 */
312static int rtFsIsoImportValidateDotDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax, uint8_t bName)
313{
314 int rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbMax);
315 if (RT_SUCCESS(rc))
316 {
317 if (pDirRec->bFileIdLength != 1)
318 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME_LENGTH,
319 "Invalid dot dir rec file id length: %u", pDirRec->bFileIdLength);
320 if ((uint8_t)pDirRec->achFileId[0] != bName)
321 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME,
322 "Invalid dot dir rec file id: %#x, expected %#x", pDirRec->achFileId[0], bName);
323 }
324 return rc;
325}
326
327
328static int rtFsIsoImportProcessIso9660TreeWorker(PRTFSISOMKIMPORTER pThis, uint32_t idxDir,
329 uint32_t offDirBlock, uint32_t cbDir, uint8_t cDepth, bool fUnicode)
330{
331 /*
332 * Restrict the depth to try avoid loops.
333 */
334 if (cDepth > RTFSISOMK_IMPORT_MAX_DEPTH)
335 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE, "Dir at %#x LB %#x is too deep", offDirBlock, cbDir);
336
337 /*
338 * Read the first chunk into the big buffer.
339 */
340 uint32_t cbChunk = RT_MIN(cbDir, sizeof(pThis->abBuf));
341 uint64_t off = (uint64_t)offDirBlock * ISO9660_SECTOR_SIZE;
342 int rc = RTVfsFileReadAt(pThis->hSrcFile, off, pThis->abBuf, cbChunk, NULL);
343 if (RT_FAILURE(rc))
344 return rtFsIsoImpError(pThis, rc, "Error reading directory at %#RX64 (%#RX32 / %#RX32): %Rrc", off, cbChunk, cbDir);
345
346 cbDir -= cbChunk;
347 off += cbChunk;
348
349 /*
350 * Skip the current and parent directory entries.
351 */
352 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
353 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x00);
354 if (RT_FAILURE(rc))
355 return rc;
356
357 cbChunk -= pDirRec->cbDirRec;
358 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
359 rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x01);
360 if (RT_FAILURE(rc))
361 return rc;
362
363 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
364 cbChunk -= pDirRec->cbDirRec;
365
366 /*
367 * Work our way thru all the directory records.
368 */
369 while (cbChunk > 0)
370 {
371 /*
372 * Do we need to read some more?
373 */
374 if ( cbChunk > UINT8_MAX
375 || cbDir == 0)
376 { /* No, we don't. */ }
377 else
378 {
379 pDirRec = (PCISO9660DIRREC)memmove(&pThis->abBuf[ISO9660_SECTOR_SIZE - cbChunk], pDirRec, cbChunk);
380
381 uint32_t cbToRead = RT_MIN(cbDir, sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE);
382 rc = RTVfsFileReadAt(pThis->hSrcFile, off, &pThis->abBuf[ISO9660_SECTOR_SIZE], cbToRead, NULL);
383 if (RT_FAILURE(rc))
384 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", off, cbToRead);
385
386 off += cbToRead;
387 cbDir -= cbToRead;
388 cbChunk += cbToRead;
389 }
390
391 /* If null length, skip to the next sector. May have to read some then. */
392 if (pDirRec->cbDirRec == 0)
393 {
394 uint64_t offChunk = off - cbChunk;
395 uint32_t cbSkip = ISO9660_SECTOR_SIZE - ((uint32_t)offChunk & (ISO9660_SECTOR_SIZE - 1));
396 if (cbSkip < cbChunk)
397 {
398 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + cbSkip);
399 cbChunk -= cbSkip;
400 if ( cbChunk <= UINT8_MAX
401 && cbDir == 0)
402 continue;
403 }
404 else
405 {
406 uint32_t cbToRead = RT_MIN(cbDir, sizeof(pThis->abBuf));
407 rc = RTVfsFileReadAt(pThis->hSrcFile, off, pThis->abBuf, cbToRead, NULL);
408 if (RT_FAILURE(rc))
409 return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", off, cbToRead);
410
411 off += cbToRead;
412 cbDir -= cbToRead;
413 cbChunk += cbToRead;
414 pDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
415 }
416 }
417
418 /*
419 * Validate the directory record. Give up if not valid since we're
420 * likely to get error with subsequent record too.
421 */
422 rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbChunk);
423 if (RT_FAILURE(rc))
424 return rc;
425
426 /*
427 * Convert the name into the name buffer (szNameBuf).
428 */
429 if (!fUnicode)
430 {
431 memcpy(pThis->szNameBuf, pDirRec->achFileId, pDirRec->bFileIdLength);
432 pThis->szNameBuf[pDirRec->bFileIdLength] = '\0';
433 rc = RTStrValidateEncoding(pThis->szNameBuf);
434 }
435 else
436 {
437 char *pszDst = pThis->szNameBuf;
438 rc = RTUtf16BigToUtf8Ex((PRTUTF16)pDirRec->achFileId, pDirRec->bFileIdLength / sizeof(RTUTF16),
439 &pszDst, sizeof(pThis->szNameBuf), NULL);
440 }
441 if (RT_SUCCESS(rc))
442 {
443 /* Drop the version from the name. */
444 /** @todo preserve the file version on import. */
445 size_t cchName = strlen(pThis->szNameBuf);
446 if ( !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
447 && cchName > 2
448 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - 1]))
449 {
450 uint32_t offName = 2;
451 while ( offName <= 5
452 && offName + 1 < cchName
453 && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - offName]))
454 offName++;
455 if ( offName + 1 < cchName
456 && pThis->szNameBuf[cchName - offName] == ';')
457 pThis->szNameBuf[cchName - offName] = '\0';
458 }
459
460 /** @todo rock ridge. */
461
462 /*
463 * Add the object and enter it into the namespace.
464 */
465 uint32_t idxObj = UINT32_MAX;
466 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
467 rc = RTFsIsoMakerAddUnnamedDir(pThis->hIsoMaker, &idxObj);
468 else
469 {
470 rc = VERR_NOT_IMPLEMENTED; /** @todo need new file add function */
471 }
472 if (RT_SUCCESS(rc))
473 {
474 rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxDir,
475 !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET,
476 pThis->szNameBuf);
477 }
478 }
479 else
480 rtFsIsoImpError(pThis, rc, "Invalid name at %#RX64: %.Rhxs",
481 off - cbChunk, pDirRec->bFileIdLength, pDirRec->achFileId);
482 }
483
484 return VINF_SUCCESS;
485}
486
487
488static int rtFsIsoImportProcessIso9660Tree(PRTFSISOMKIMPORTER pThis, uint32_t offDirBlock, uint32_t cbDir, bool fUnicode)
489{
490 /*
491 * Make sure we've got a root in the namespace.
492 */
493 uint32_t idxDir = RTFsIsoMakerGetObjIdxForPath(pThis->hIsoMaker, RTFSISOMAKER_NAMESPACE_ISO_9660, "/");
494 if (idxDir == UINT32_MAX)
495 {
496 int rc = RTFsIsoMakerObjSetPath(pThis->hIsoMaker, RTFSISOMAKER_CFG_IDX_ROOT, RTFSISOMAKER_NAMESPACE_ISO_9660, "/");
497 if (RT_FAILURE(rc))
498 return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjSetPath failed on root dir: %Rrc", rc);
499 }
500 Assert(idxDir == RTFSISOMAKER_CFG_IDX_ROOT);
501
502 /*
503 * Directories.
504 */
505 int rc = VINF_SUCCESS;
506 uint8_t cDepth = 0;
507 RTLISTANCHOR TodoList;
508 RTListInit(&TodoList);
509 for (;;)
510 {
511 int rc2 = rtFsIsoImportProcessIso9660TreeWorker(pThis, idxDir, offDirBlock, cbDir, cDepth, fUnicode);
512 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
513 rc = rc2;
514
515 /*
516 * Pop the next directory.
517 */
518 PRTFSISOMKIMPDIR pNext = RTListRemoveLast(&TodoList, RTFSISOMKIMPDIR, Entry);
519 if (!pNext)
520 break;
521 cDepth = pNext->cDepth;
522 cbDir = pNext->cbDir;
523 offDirBlock = pNext->offDirBlock;
524 RTMemFree(pNext);
525 }
526
527 return rc;
528}
529
530
531/**
532 * Validates a root directory record.
533 *
534 * @returns IPRT status code (safe to ignore, see pThis->rc).
535 * @param pThis The importer instance.
536 * @param pDirRec The root directory record to validate.
537 */
538static int rtFsIsoImportValidateRootDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec)
539{
540 /*
541 * Validate dual fields.
542 */
543 if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
544 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
545 "Invalid root dir size: {%#RX32,%#RX32}",
546 RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
547
548 if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
549 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
550 "Invalid root dir extent: {%#RX32,%#RX32}",
551 RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
552
553 if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
554 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
555 "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
556 RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
557
558 /*
559 * Check values.
560 */
561 if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
562 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_VOLUME_SEQ_NO,
563 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
564 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
565
566 if (ISO9660_GET_ENDIAN(&pDirRec->cbData) == 0)
567 return RTErrInfoSet(pThis->pErrInfo, VERR_ISOMK_IMPORT_ZERO_SIZED_ROOT_DIR, "Zero sized root dir");
568
569 if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
570 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_EXTENT_OUT_OF_BOUNDS,
571 "Invalid root dir extent: %#RX32, max %#RX32",
572 ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
573
574 if (pDirRec->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
575 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC_LENGTH,
576 "Root dir record size is too small: %#x (min %#x)",
577 pDirRec->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
578
579 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
580 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_WITHOUT_DIR_FLAG,
581 "Root dir is not flagged as directory: %#x", pDirRec->fFileFlags);
582 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
583 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_IS_MULTI_EXTENT,
584 "Root dir is cannot be multi-extent: %#x", pDirRec->fFileFlags);
585
586 return VINF_SUCCESS;
587}
588
589
590/**
591 * Processes a primary volume descriptor, importing all files described by its
592 * namespace.
593 *
594 * @returns IPRT status code (safe to ignore, see pThis->rc).
595 * @param pThis The importer instance.
596 * @param pVolDesc The primary volume descriptor.
597 */
598static int rtFsIsoImportProcessPrimaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660PRIMARYVOLDESC pVolDesc)
599{
600 /*
601 * Validate dual fields first.
602 */
603 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
604 return rtFsIsoImpError(pThis, VERR_VFS_UNSUPPORTED_FORMAT,
605 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
606
607 if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
608 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
609 "Mismatching logical block size: {%#RX16,%#RX16}",
610 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
611 if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
612 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
613 "Mismatching volume space size: {%#RX32,%#RX32}",
614 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
615 if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
616 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
617 "Mismatching volumes in set: {%#RX16,%#RX16}",
618 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
619 if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
620 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
621 "Mismatching volume sequence no.: {%#RX16,%#RX16}",
622 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
623 if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
624 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
625 "Mismatching path table size: {%#RX32,%#RX32}",
626 RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
627
628 /*
629 * Validate field values against our expectations.
630 */
631 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
632 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
633 "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
634
635 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != 1)
636 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MORE_THAN_ONE_VOLUME_IN_SET,
637 "Volumes in set: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet));
638
639 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != 1)
640 return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
641 "Unexpected volume sequence number: %#x", ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo));
642
643 /*
644 * Gather info we need.
645 */
646 pThis->cBlocksInPrimaryVolumeSpace = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize);
647 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)ISO9660_SECTOR_SIZE;
648 pThis->cVolumesInSet = ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet);
649 pThis->idPrimaryVol = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo);
650
651 /*
652 * Validate the root directory record.
653 */
654 int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
655 if (RT_SUCCESS(rc))
656 {
657 /*
658 * Process the directory tree. Start by establishing a root directory.
659 */
660 if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PRIMARY_ISO))
661 rc = rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
662 ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), false /*fUnicode*/);
663 }
664
665 return rc;
666}
667
668
669static int rtFsIsoImportProcessSupplementaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660SUPVOLDESC pSup)
670{
671 RT_NOREF(pThis, pSup);
672 return VINF_SUCCESS;
673}
674
675
676static int rtFsIsoImportProcessElToritoDesc(PRTFSISOMKIMPORTER pThis, PISO9660BOOTRECORDELTORITO pElTorito)
677{
678 RT_NOREF(pThis, pElTorito);
679 return VINF_SUCCESS;
680}
681
682
683/**
684 * Imports an existing ISO.
685 *
686 * Just like other source files, the existing image must remain present and
687 * unmodified till the ISO maker is done with it.
688 *
689 * @returns IRPT status code.
690 * @param hIsoMaker The ISO maker handle.
691 * @param pszIso Path to the existing image to import / clone.
692 * @param fFlags Reserved for the future, MBZ.
693 * @param pErrInfo Where to return additional error information.
694 * Optional.
695 */
696RTDECL(int) RTFsIsoMakerImport(RTFSISOMAKER hIsoMaker, const char *pszIso, uint32_t fFlags, PRTERRINFO pErrInfo)
697{
698 /*
699 * Validate input.
700 */
701 AssertReturn(!(fFlags & ~RTFSISOMK_IMPORT_F_VALID_MASK), VERR_INVALID_FLAGS);
702
703 /*
704 * Open the input file and start working on it.
705 */
706 RTVFSFILE hSrcFile;
707 uint32_t offError;
708 int rc = RTVfsChainOpenFile(pszIso, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hSrcFile, &offError, pErrInfo);
709 if (RT_FAILURE(rc))
710 return rc;
711
712 /*
713 * Allocate and init the importer state.
714 */
715 PRTFSISOMKIMPORTER pThis = (PRTFSISOMKIMPORTER)RTMemAllocZ(sizeof(*pThis));
716 if (pThis)
717 {
718 pThis->hIsoMaker = hIsoMaker;
719 pThis->fFlags = fFlags;
720 pThis->rc = VINF_SUCCESS;
721 pThis->pErrInfo = pErrInfo;
722 pThis->hSrcFile = hSrcFile;
723 pThis->idxSrcFile = UINT32_MAX;
724 //pThis->Block2FileRoot = NULL;
725
726 /*
727 * Check if this looks like a plausible ISO by checking out the first volume descriptor.
728 */
729 rc = RTVfsFileReadAt(hSrcFile, _32K, &pThis->uSectorBuf.PrimVolDesc, sizeof(pThis->uSectorBuf.PrimVolDesc), NULL);
730 if (RT_SUCCESS(rc))
731 {
732 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] == ISO9660VOLDESC_STD_ID_0
733 && pThis->uSectorBuf.VolDescHdr.achStdId[1] == ISO9660VOLDESC_STD_ID_1
734 && pThis->uSectorBuf.VolDescHdr.achStdId[2] == ISO9660VOLDESC_STD_ID_2
735 && pThis->uSectorBuf.VolDescHdr.achStdId[3] == ISO9660VOLDESC_STD_ID_3
736 && pThis->uSectorBuf.VolDescHdr.achStdId[4] == ISO9660VOLDESC_STD_ID_4
737 && ( pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY
738 || pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD) )
739 {
740 /*
741 * Process the volume descriptors using the sector buffer, starting
742 * with the one we've already got sitting there. We postpone processing
743 * the el torito one till after the others, so we can name files and size
744 * referenced in it.
745 */
746 uint32_t cPrimaryVolDescs = 0;
747 uint32_t iElTorito = UINT32_MAX;
748 uint32_t iVolDesc = 0;
749 for (;; iVolDesc++)
750 {
751 switch (pThis->uSectorBuf.VolDescHdr.bDescType)
752 {
753 case ISO9660VOLDESC_TYPE_PRIMARY:
754 cPrimaryVolDescs++;
755 if (cPrimaryVolDescs == 1)
756 rtFsIsoImportProcessPrimaryDesc(pThis, &pThis->uSectorBuf.PrimVolDesc);
757 else
758 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS,
759 "Only a single primary volume descriptor is currently supported");
760 break;
761
762 case ISO9660VOLDESC_TYPE_SUPPLEMENTARY:
763 if (cPrimaryVolDescs > 0)
764 rtFsIsoImportProcessSupplementaryDesc(pThis, &pThis->uSectorBuf.SupVolDesc);
765 else
766 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY,
767 "Primary volume descriptor expected before any supplementary descriptors!");
768 break;
769
770 case ISO9660VOLDESC_TYPE_BOOT_RECORD:
771 if (strcmp(pThis->uSectorBuf.ElToritoDesc.achBootSystemId,
772 ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID) == 0)
773 {
774 if (iElTorito == UINT32_MAX)
775 iElTorito = iVolDesc;
776 else
777 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS,
778 "Only a single El Torito descriptor exepcted!");
779 }
780 break;
781
782 case ISO9660VOLDESC_TYPE_PARTITION:
783 /* ignore for now */
784 break;
785
786 case ISO9660VOLDESC_TYPE_TERMINATOR:
787 AssertFailed();
788 break;
789 }
790
791
792 /*
793 * Read the next volume descriptor and check the signature.
794 */
795 if (iVolDesc >= 32)
796 {
797 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS, "Parses at most 32 volume descriptors");
798 break;
799 }
800
801 rc = RTVfsFileReadAt(hSrcFile, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
802 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
803 if (RT_FAILURE(rc))
804 {
805 rtFsIsoImpError(pThis, rc, "Error reading the volume descriptor #%u at %#RX32: %Rrc",
806 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE, rc);
807 break;
808 }
809
810 if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
811 || pThis->uSectorBuf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
812 || pThis->uSectorBuf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
813 || pThis->uSectorBuf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
814 || pThis->uSectorBuf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
815 {
816 rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR,
817 "Invalid volume descriptor header #%u at %#RX32: %.*Rhxs",
818 iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
819 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
820 break;
821 }
822 /** @todo UDF support. */
823 if (pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
824 break;
825 }
826
827 /*
828 * Process the system area.
829 */
830 if (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX)
831 {
832 rc = RTVfsFileReadAt(hSrcFile, 0, pThis->abBuf, _32K, NULL);
833 if (RT_SUCCESS(rc))
834 {
835 if (!ASMMemIsAllU8(pThis->abBuf, _32K, 0))
836 {
837 /** @todo HFS */
838 rc = RTFsIsoMakerSetSysAreaContent(hIsoMaker, pThis->abBuf, _32K, 0);
839 if (RT_FAILURE(rc))
840 rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetSysAreaContent failed: %Rrc", rc);
841 }
842 }
843 else
844 rtFsIsoImpError(pThis, rc, "Error reading the system area (0..32KB): %Rrc", rc);
845 }
846
847 /*
848 * Do the El Torito descriptor.
849 */
850 if ( iElTorito != UINT32_MAX
851 && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BOOT)
852 && (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX))
853 {
854 rc = RTVfsFileReadAt(hSrcFile, _32K + iElTorito * ISO9660_SECTOR_SIZE,
855 &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
856 if (RT_SUCCESS(rc))
857 rtFsIsoImportProcessElToritoDesc(pThis, &pThis->uSectorBuf.ElToritoDesc);
858 else
859 rtFsIsoImpError(pThis, rc, "Error reading the El Torito volume descriptor at %#RX32: %Rrc",
860 _32K + iElTorito * ISO9660_SECTOR_SIZE, rc);
861 }
862
863 /*
864 * Return the first error status.
865 */
866 rc = pThis->rc;
867 }
868 else
869 rc = RTErrInfoSetF(pErrInfo, VERR_ISOMK_IMPORT_UNKNOWN_FORMAT, "Invalid volume descriptor header: %.*Rhxs",
870 (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
871 }
872
873 /*
874 * Destroy the state.
875 */
876 RTAvlU32Destroy(&pThis->Block2FileRoot, rtFsIsoMakerImportDestroyData2File, NULL);
877 RTMemFree(pThis);
878 }
879 else
880 rc = VERR_NO_MEMORY;
881 RTVfsFileRelease(hSrcFile);
882 return rc;
883
884}
885
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