VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use