VirtualBox

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

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

IPRT/isomaker: Working on generating rock ridge data.

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

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