VirtualBox

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

Last change on this file since 100908 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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

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