VirtualBox

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

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

IPRT/rtFsIsoImportProcessIso9660AddAndNameFile: Fixed stupid bug where hardlinked files would kill part of the block lookup tree used to detect hardlinks.

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