VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/isovfs.cpp@ 99241

Last change on this file since 99241 was 98103, checked in by vboxsync, 22 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 296.8 KB
Line 
1/* $Id: isovfs.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - ISO 9660 and UDF Virtual Filesystem (read only).
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/fsvfs.h>
44
45#include <iprt/alloca.h>
46#include <iprt/asm.h>
47#include <iprt/assert.h>
48#include <iprt/err.h>
49#include <iprt/crc.h>
50#include <iprt/critsect.h>
51#include <iprt/ctype.h>
52#include <iprt/file.h>
53#include <iprt/log.h>
54#include <iprt/mem.h>
55#include <iprt/poll.h>
56#include <iprt/string.h>
57#include <iprt/thread.h>
58#include <iprt/vfs.h>
59#include <iprt/vfslowlevel.h>
60#include <iprt/uni.h>
61#include <iprt/utf16.h>
62#include <iprt/formats/iso9660.h>
63#include <iprt/formats/udf.h>
64
65
66/*********************************************************************************************************************************
67* Defined Constants And Macros *
68*********************************************************************************************************************************/
69/** The maximum logical block size. */
70#define RTFSISO_MAX_LOGICAL_BLOCK_SIZE _16K
71/** Max directory size. */
72#if ARCH_BITS == 32
73# define RTFSISO_MAX_DIR_SIZE _32M
74#else
75# define RTFSISO_MAX_DIR_SIZE _64M
76#endif
77
78/** Check if an entity ID field equals the given ID string. */
79#define UDF_ENTITY_ID_EQUALS(a_pEntityId, a_szId) \
80 ( memcmp(&(a_pEntityId)->achIdentifier[0], a_szId, RT_MIN(sizeof(a_szId), sizeof(a_pEntityId)->achIdentifier)) == 0 )
81/** Checks if a character set indicator indicates OSTA compressed unicode. */
82#define UDF_IS_CHAR_SET_OSTA(a_pCharSet) \
83 ( (a_pCharSet)->uType == UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE \
84 && memcmp((a_pCharSet)->abInfo, UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO, \
85 sizeof(UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO)) == 0 )
86
87
88/** @name UDF structure logging macros
89 * @{ */
90#define UDF_LOG2_MEMBER(a_pStruct, a_szFmt, a_Member) \
91 Log2(("ISO/UDF: %-32s %" a_szFmt "\n", #a_Member ":", (a_pStruct)->a_Member))
92#define UDF_LOG2_MEMBER_EX(a_pStruct, a_szFmt, a_Member, a_cchIndent) \
93 Log2(("ISO/UDF: %*s%-32s %" a_szFmt "\n", a_cchIndent, "", #a_Member ":", (a_pStruct)->a_Member))
94#define UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, a_cchIndent) \
95 Log2(("ISO/UDF: %*s%-32s '%.23s' fFlags=%#06x Suffix=%.8Rhxs\n", a_cchIndent, "", #a_Member ":", \
96 (a_pStruct)->a_Member.achIdentifier, (a_pStruct)->a_Member.fFlags, &(a_pStruct)->a_Member.Suffix))
97#define UDF_LOG2_MEMBER_ENTITY_ID(a_pStruct, a_Member) UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, 0)
98#define UDF_LOG2_MEMBER_EXTENTAD(a_pStruct, a_Member) \
99 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb))
100#define UDF_LOG2_MEMBER_SHORTAD(a_pStruct, a_Member) \
101 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32 %s\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb, \
102 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
103 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
104 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next" ))
105#define UDF_LOG2_MEMBER_LONGAD(a_pStruct, a_Member) \
106 Log2(("ISO/UDF: %-32s partition %#RX16, block %#010RX32 LB %#010RX32 %s idUnique=%#010RX32 fFlags=%#RX16\n", #a_Member ":", \
107 (a_pStruct)->a_Member.Location.uPartitionNo, (a_pStruct)->a_Member.Location.off, (a_pStruct)->a_Member.cb, \
108 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
109 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
110 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next", \
111 (a_pStruct)->a_Member.ImplementationUse.Fid.idUnique, (a_pStruct)->a_Member.ImplementationUse.Fid.fFlags ))
112#define UDF_LOG2_MEMBER_LBADDR(a_pStruct, a_Member) \
113 Log2(("ISO/UDF: %-32s block %#010RX32 in partition %#06RX16\n", #a_Member ":", \
114 (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.uPartitionNo))
115
116#define UDF_LOG2_MEMBER_TIMESTAMP(a_pStruct, a_Member) \
117 Log2(("ISO/UDF: %-32s %04d-%02u-%02u %02u:%02u:%02u.%02u%02u%02u offUtc=%d type=%#x\n", #a_Member ":", \
118 (a_pStruct)->a_Member.iYear, (a_pStruct)->a_Member.uMonth, (a_pStruct)->a_Member.uDay, \
119 (a_pStruct)->a_Member.uHour, (a_pStruct)->a_Member.uMinute, (a_pStruct)->a_Member.uSecond, \
120 (a_pStruct)->a_Member.cCentiseconds, (a_pStruct)->a_Member.cHundredsOfMicroseconds, \
121 (a_pStruct)->a_Member.cMicroseconds, (a_pStruct)->a_Member.offUtcInMin, (a_pStruct)->a_Member.fType ))
122#define UDF_LOG2_MEMBER_CHARSPEC(a_pStruct, a_Member) \
123 do { \
124 if ( (a_pStruct)->a_Member.uType == UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE \
125 && memcmp(&(a_pStruct)->a_Member.abInfo[0], UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO, \
126 sizeof(UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO)) == 0) \
127 Log2(("ISO/UDF: %-32s OSTA COMPRESSED UNICODE INFO\n", #a_Member ":")); \
128 else if (ASMMemIsZero(&(a_pStruct)->a_Member, sizeof((a_pStruct)->a_Member))) \
129 Log2(("ISO/UDF: %-32s all zeros\n", #a_Member ":")); \
130 else \
131 Log2(("ISO/UDF: %-32s %#x info: %.63Rhxs\n", #a_Member ":", \
132 (a_pStruct)->a_Member.uType, (a_pStruct)->a_Member.abInfo)); \
133 } while (0)
134#define UDF_LOG2_MEMBER_DSTRING(a_pStruct, a_Member) \
135 do { \
136 if ((a_pStruct)->a_Member[0] == 8) \
137 Log2(("ISO/UDF: %-32s 8: '%s' len=%u (actual=%u)\n", #a_Member ":", &(a_pStruct)->a_Member[1], \
138 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], \
139 RTStrNLen(&(a_pStruct)->a_Member[1], sizeof((a_pStruct)->a_Member) - 2) + 1 )); \
140 else if ((a_pStruct)->a_Member[0] == 16) \
141 { \
142 PCRTUTF16 pwszTmp = (PCRTUTF16)&(a_pStruct)->a_Member[1]; \
143 char *pszTmp = NULL; \
144 RTUtf16BigToUtf8Ex(pwszTmp, (sizeof((a_pStruct)->a_Member) - 2) / sizeof(RTUTF16), &pszTmp, 0, NULL); \
145 Log2(("ISO/UDF: %-32s 16: '%s' len=%u (actual=%u)\n", #a_Member ":", pszTmp, \
146 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], \
147 RTUtf16NLen(pwszTmp, (sizeof((a_pStruct)->a_Member) - 2) / sizeof(RTUTF16)) * sizeof(RTUTF16) + 1 /*??*/ )); \
148 RTStrFree(pszTmp); \
149 } \
150 else if (ASMMemIsZero(&(a_pStruct)->a_Member[0], sizeof((a_pStruct)->a_Member))) \
151 Log2(("ISO/UDF: %-32s empty\n", #a_Member ":")); \
152 else \
153 Log2(("ISO/UDF: %-32s bad: %.*Rhxs\n", #a_Member ":", sizeof((a_pStruct)->a_Member), &(a_pStruct)->a_Member[0] )); \
154 } while (0)
155/** @} */
156
157/** Compresses SUSP and rock ridge extension signatures in the hope of
158 * reducing switch table size. */
159#define SUSP_MAKE_SIG(a_bSig1, a_bSig2) \
160 ( ((uint16_t)(a_bSig1) & 0x1f) \
161 | (((uint16_t)(a_bSig2) ^ 0x40) << 5) \
162 | ((((uint16_t)(a_bSig1) ^ 0x40) & 0xe0) << 8) )
163
164
165/*********************************************************************************************************************************
166* Structures and Typedefs *
167*********************************************************************************************************************************/
168/** Pointer to an ISO volume (VFS instance data). */
169typedef struct RTFSISOVOL *PRTFSISOVOL;
170/** Pointer to a const ISO volume (VFS instance data). */
171typedef struct RTFSISOVOL const *PCRTFSISOVOL;
172
173/** Pointer to a ISO directory instance. */
174typedef struct RTFSISODIRSHRD *PRTFSISODIRSHRD;
175
176
177/**
178 * Output structure for rock ridge directory entry parsing.
179 */
180typedef struct RTFSISOROCKINFO
181{
182 /** Set if the parse info is valid. */
183 bool fValid;
184 /** Set if we've see the SP entry. */
185 bool fSuspSeenSP : 1;
186 /** Set if we've seen the last 'NM' entry. */
187 bool fSeenLastNM : 1;
188 /** Set if we've seen the last 'SL' entry. */
189 bool fSeenLastSL : 1;
190 /** Symbolic link target overflowed. */
191 bool fOverflowSL : 1;
192 /** Number of interesting rock ridge entries we've scanned. */
193 uint16_t cRockEntries;
194 /** The name length. */
195 uint16_t cchName;
196 /** The Symbolic link target name length. */
197 uint16_t cchLinkTarget;
198 /** Object info. */
199 RTFSOBJINFO Info;
200 /** The rock ridge name. */
201 char szName[2048];
202 /** Symbolic link target name. */
203 char szLinkTarget[2048];
204} RTFSISOROCKINFO;
205/** Rock ridge info for a directory entry. */
206typedef RTFSISOROCKINFO *PRTFSISOROCKINFO;
207/** Const rock ridge info for a directory entry. */
208typedef RTFSISOROCKINFO const *PCRTFSISOROCKINFO;
209
210/**
211 * Rock ridge name compare data.
212 */
213typedef struct RTFSISOROCKNAMECOMP
214{
215 /** Pointer to the name we're looking up. */
216 const char *pszEntry;
217 /** The length of the name. */
218 size_t cchEntry;
219 /** The length of the name that we've matched so far (in case of multiple NM
220 * entries). */
221 size_t offMatched;
222} RTFSISOROCKNAMECOMP;
223/** Ponter to rock ridge name compare data. */
224typedef RTFSISOROCKNAMECOMP *PRTFSISOROCKNAMECOMP;
225
226
227/**
228 * ISO extent (internal to the VFS not a disk structure).
229 */
230typedef struct RTFSISOEXTENT
231{
232 /** The disk or partition byte offset.
233 * This is set to UINT64_MAX for parts of sparse files that aren't recorded.*/
234 uint64_t off;
235 /** The size of the extent in bytes. */
236 uint64_t cbExtent;
237 /** UDF virtual partition number, UINT32_MAX for ISO 9660. */
238 uint32_t idxPart;
239 /** Reserved. */
240 uint32_t uReserved;
241} RTFSISOEXTENT;
242/** Pointer to an ISO 9660 extent. */
243typedef RTFSISOEXTENT *PRTFSISOEXTENT;
244/** Pointer to a const ISO 9660 extent. */
245typedef RTFSISOEXTENT const *PCRTFSISOEXTENT;
246
247
248/**
249 * ISO file system object, shared part.
250 */
251typedef struct RTFSISOCORE
252{
253 /** The parent directory keeps a list of open objects (RTFSISOCORE). */
254 RTLISTNODE Entry;
255 /** Reference counter. */
256 uint32_t volatile cRefs;
257 /** The parent directory (not released till all children are close). */
258 PRTFSISODIRSHRD pParentDir;
259 /** The byte offset of the first directory record.
260 * This is used when looking up objects in a directory to avoid creating
261 * duplicate instances. */
262 uint64_t offDirRec;
263 /** Attributes. */
264 RTFMODE fAttrib;
265 /** Set if there is rock ridge info for this directory entry. */
266 bool fHaveRockInfo;
267 /** The object size. */
268 uint64_t cbObject;
269 /** The access time. */
270 RTTIMESPEC AccessTime;
271 /** The modificaton time. */
272 RTTIMESPEC ModificationTime;
273 /** The change time. */
274 RTTIMESPEC ChangeTime;
275 /** The birth time. */
276 RTTIMESPEC BirthTime;
277 /** The i-node ID. */
278 RTINODE idINode;
279 /** Pointer to the volume. */
280 PRTFSISOVOL pVol;
281 /** The version number. */
282 uint32_t uVersion;
283 /** Number of extents. */
284 uint32_t cExtents;
285 /** The first extent. */
286 RTFSISOEXTENT FirstExtent;
287 /** Array of additional extents. */
288 PRTFSISOEXTENT paExtents;
289} RTFSISOCORE;
290typedef RTFSISOCORE *PRTFSISOCORE;
291
292/**
293 * ISO file, shared data.
294 */
295typedef struct RTFSISOFILESHRD
296{
297 /** Core ISO9660 object info. */
298 RTFSISOCORE Core;
299} RTFSISOFILESHRD;
300/** Pointer to a ISO 9660 file object. */
301typedef RTFSISOFILESHRD *PRTFSISOFILESHRD;
302
303
304/**
305 * ISO directory, shared data.
306 *
307 * We will always read in the whole directory just to keep things really simple.
308 */
309typedef struct RTFSISODIRSHRD
310{
311 /** Core ISO 9660 object info. */
312 RTFSISOCORE Core;
313 /** Open child objects (RTFSISOCORE). */
314 RTLISTNODE OpenChildren;
315
316 /** Pointer to the directory content. */
317 uint8_t *pbDir;
318 /** The size of the directory content (duplicate of Core.cbObject). */
319 uint32_t cbDir;
320} RTFSISODIRSHRD;
321/** Pointer to a ISO directory instance. */
322typedef RTFSISODIRSHRD *PRTFSISODIRSHRD;
323
324
325/**
326 * Private data for a VFS file object.
327 */
328typedef struct RTFSISOFILEOBJ
329{
330 /** Pointer to the shared data. */
331 PRTFSISOFILESHRD pShared;
332 /** The current file offset. */
333 uint64_t offFile;
334} RTFSISOFILEOBJ;
335typedef RTFSISOFILEOBJ *PRTFSISOFILEOBJ;
336
337/**
338 * Private data for a VFS directory object.
339 */
340typedef struct RTFSISODIROBJ
341{
342 /** Pointer to the shared data. */
343 PRTFSISODIRSHRD pShared;
344 /** The current directory offset. */
345 uint32_t offDir;
346} RTFSISODIROBJ;
347typedef RTFSISODIROBJ *PRTFSISODIROBJ;
348
349/** Pointer to info about a UDF volume. */
350typedef struct RTFSISOUDFVOLINFO *PRTFSISOUDFVOLINFO;
351
352
353/** @name RTFSISO_UDF_PMAP_T_XXX
354 * @{ */
355#define RTFSISO_UDF_PMAP_T_PLAIN 1
356#define RTFSISO_UDF_PMAP_T_VPM_15 2
357#define RTFSISO_UDF_PMAP_T_VPM_20 3
358#define RTFSISO_UDF_PMAP_T_SPM 4
359#define RTFSISO_UDF_PMAP_T_MPM 5
360/** @} */
361
362/**
363 * Information about a logical UDF partition.
364 *
365 * This combins information from the partition descriptor, the UDFPARTMAPTYPE1
366 * and the UDFPARTMAPTYPE2 structure.
367 */
368typedef struct RTFSISOVOLUDFPMAP
369{
370 /** Partition starting location as a byte offset. */
371 uint64_t offByteLocation;
372 /** Partition starting location (logical sector number). */
373 uint32_t offLocation;
374 /** Number of sectors. */
375 uint32_t cSectors;
376
377 /** Partition descriptor index (for processing). */
378 uint16_t idxPartDesc;
379 /** Offset info the map table. */
380 uint16_t offMapTable;
381 /** Partition number (not index). */
382 uint16_t uPartitionNo;
383 /** Partition number (not index). */
384 uint16_t uVolumeSeqNo;
385
386 /** The access type (UDF_PART_ACCESS_TYPE_XXX). */
387 uint32_t uAccessType;
388 /** Partition flags (UDF_PARTITION_FLAGS_XXX). */
389 uint16_t fFlags;
390 /** RTFSISO_UDF_PMAP_T_XXX. */
391 uint8_t bType;
392 /** Set if Hdr is valid. */
393 bool fHaveHdr;
394 /** Copy of UDFPARTITIONDESC::ContentsUse::Hdr. */
395 UDFPARTITIONHDRDESC Hdr;
396
397} RTFSISOVOLUDFPMAP;
398typedef RTFSISOVOLUDFPMAP *PRTFSISOVOLUDFPMAP;
399
400/**
401 * Information about a UDF volume (/ volume set).
402 *
403 * This combines information from the primary and logical descriptors.
404 *
405 * @note There is only one volume per volume set in the current UDF
406 * implementation. So, this can be considered a volume and a volume set.
407 */
408typedef struct RTFSISOUDFVOLINFO
409{
410 /** The extent containing the file set descriptor. */
411 UDFLONGAD FileSetDescriptor;
412
413 /** The root directory location (from the file set descriptor). */
414 UDFLONGAD RootDirIcb;
415 /** Location of the system stream directory associated with the file set. */
416 UDFLONGAD SystemStreamDirIcb;
417
418 /** The logical block size on this volume. */
419 uint32_t cbBlock;
420 /** The log2 of cbBlock. */
421 uint32_t cShiftBlock;
422 /** Flags (UDF_PVD_FLAGS_XXX). */
423 uint16_t fFlags;
424
425 /** Number of partitions mapp in this volume. */
426 uint16_t cPartitions;
427 /** Partitions in this volume. */
428 PRTFSISOVOLUDFPMAP paPartitions;
429
430 /** The volume ID string. */
431 UDFDSTRING achLogicalVolumeID[128];
432} RTFSISOUDFVOLINFO;
433
434
435/**
436 * Indicates which of the possible content types we're accessing.
437 */
438typedef enum RTFSISOVOLTYPE
439{
440 /** Invalid zero value. */
441 RTFSISOVOLTYPE_INVALID = 0,
442 /** Accessing the primary ISO-9660 volume. */
443 RTFSISOVOLTYPE_ISO9960,
444 /** Accessing the joliet volume (secondary ISO-9660). */
445 RTFSISOVOLTYPE_JOLIET,
446 /** Accessing the UDF volume. */
447 RTFSISOVOLTYPE_UDF
448} RTFSISOVOLTYPE;
449
450/**
451 * A ISO volume.
452 */
453typedef struct RTFSISOVOL
454{
455 /** Handle to itself. */
456 RTVFS hVfsSelf;
457 /** The file, partition, or whatever backing the ISO 9660 volume. */
458 RTVFSFILE hVfsBacking;
459 /** The size of the backing thingy. */
460 uint64_t cbBacking;
461 /** The size of the backing thingy in sectors (cbSector). */
462 uint64_t cBackingSectors;
463 /** Flags. */
464 uint32_t fFlags;
465 /** The sector size (in bytes). */
466 uint32_t cbSector;
467 /** What we're accessing. */
468 RTFSISOVOLTYPE enmType;
469
470 /** @name ISO 9660 specific data
471 * @{ */
472 /** The size of a logical block in bytes. */
473 uint32_t cbBlock;
474 /** The primary volume space size in blocks. */
475 uint32_t cBlocksInPrimaryVolumeSpace;
476 /** The primary volume space size in bytes. */
477 uint64_t cbPrimaryVolumeSpace;
478 /** The number of volumes in the set. */
479 uint32_t cVolumesInSet;
480 /** The primary volume sequence ID. */
481 uint32_t idPrimaryVol;
482 /** The offset of the primary volume descriptor. */
483 uint32_t offPrimaryVolDesc;
484 /** The offset of the secondary volume descriptor. */
485 uint32_t offSecondaryVolDesc;
486 /** Set if using UTF16-2 (joliet). */
487 bool fIsUtf16;
488 /** @} */
489
490 /** UDF specific data. */
491 struct
492 {
493 /** Volume information. */
494 RTFSISOUDFVOLINFO VolInfo;
495 /** The UDF level. */
496 uint8_t uLevel;
497 } Udf;
498
499 /** The root directory shared data. */
500 PRTFSISODIRSHRD pRootDir;
501
502 /** @name Rock Ridge stuff
503 * @{ */
504 /** Set if we've found rock ridge stuff in the root dir. */
505 bool fHaveRock;
506 /** The SUSP skip into system area offset. */
507 uint32_t offSuspSkip;
508 /** The source file byte offset of the abRockBuf content. */
509 uint64_t offRockBuf;
510 /** A buffer for reading rock ridge continuation blocks into. */
511 uint8_t abRockBuf[ISO9660_SECTOR_SIZE];
512 /** Critical section protecting abRockBuf and offRockBuf. */
513 RTCRITSECT RockBufLock;
514 /** @} */
515} RTFSISOVOL;
516
517
518/**
519 * Info gathered from a VDS sequence.
520 */
521typedef struct RTFSISOVDSINFO
522{
523 /** Number of entries in apPrimaryVols. */
524 uint32_t cPrimaryVols;
525 /** Number of entries in apLogicalVols. */
526 uint32_t cLogicalVols;
527 /** Number of entries in apPartitions. */
528 uint32_t cPartitions;
529 /** Pointer to primary volume descriptors (native endian). */
530 PUDFPRIMARYVOLUMEDESC apPrimaryVols[8];
531 /** Pointer to logical volume descriptors (native endian). */
532 PUDFLOGICALVOLUMEDESC apLogicalVols[8];
533 /** Pointer to partition descriptors (native endian). */
534 PUDFPARTITIONDESC apPartitions[16];
535
536 /** Created after scanning the sequence (here for cleanup purposes). */
537 PRTFSISOVOLUDFPMAP paPartMaps;
538} RTFSISOVDSINFO;
539/** Pointer to VDS sequence info. */
540typedef RTFSISOVDSINFO *PRTFSISOVDSINFO;
541
542
543/*********************************************************************************************************************************
544* Internal Functions *
545*********************************************************************************************************************************/
546static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
547static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
548static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir);
549static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
550 uint32_t cDirRecs, uint64_t offDirRec, PCRTFSISOROCKINFO pRockInfo, PRTVFSDIR phVfsDir);
551static int rtFsIsoDir_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid, PRTVFSDIR phVfsDir);
552static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec);
553
554static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo);
555static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo);
556static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo);
557
558
559/**
560 * UDF virtual partition read function.
561 *
562 * This deals with all the fun related to block mapping and such.
563 *
564 * @returns VBox status code.
565 * @param pThis The instance.
566 * @param idxPart The virtual partition number.
567 * @param idxBlock The block number.
568 * @param offByteAddend The byte offset relative to the block.
569 * @param pvBuf The output buffer.
570 * @param cbToRead The number of bytes to read.
571 */
572static int rtFsIsoVolUdfVpRead(PRTFSISOVOL pThis, uint32_t idxPart, uint32_t idxBlock, uint64_t offByteAddend,
573 void *pvBuf, size_t cbToRead)
574{
575 uint64_t const offByte = ((uint64_t)idxBlock << pThis->Udf.VolInfo.cShiftBlock) + offByteAddend;
576
577 int rc;
578 if (idxPart < pThis->Udf.VolInfo.cPartitions)
579 {
580 PRTFSISOVOLUDFPMAP pPart = &pThis->Udf.VolInfo.paPartitions[idxPart];
581 switch (pPart->bType)
582 {
583 case RTFSISO_UDF_PMAP_T_PLAIN:
584 rc = RTVfsFileReadAt(pThis->hVfsBacking, offByte + pPart->offByteLocation, pvBuf, cbToRead, NULL);
585 if (RT_SUCCESS(rc))
586 {
587 Log3(("ISO/UDF: Read %#x bytes at %#RX64 (%#x:%#RX64)\n",
588 cbToRead, offByte + pPart->offByteLocation, idxPart, offByte));
589 return VINF_SUCCESS;
590 }
591 Log(("ISO/UDF: Error reading %#x bytes at %#RX64 (%#x:%#RX64): %Rrc\n",
592 cbToRead, offByte + pPart->offByteLocation, idxPart, offByte, rc));
593 break;
594
595 default:
596 AssertFailed();
597 rc = VERR_ISOFS_IPE_1;
598 break;
599 }
600 }
601 else
602 {
603 Log(("ISO/UDF: Invalid partition index %#x (offset %#RX64), max partitions %#x\n",
604 idxPart, offByte, pThis->Udf.VolInfo.cPartitions));
605 rc = VERR_ISOFS_INVALID_PARTITION_INDEX;
606 }
607 return rc;
608}
609
610
611/**
612 * Returns the length of the version suffix in the given name.
613 *
614 * @returns Number of UTF16-BE chars in the version suffix.
615 * @param pawcName The name to examine.
616 * @param cwcName The length of the name.
617 * @param puValue Where to return the value.
618 */
619static size_t rtFsIso9660GetVersionLengthUtf16Big(PCRTUTF16 pawcName, size_t cwcName, uint32_t *puValue)
620{
621 *puValue = 0;
622
623 /* -1: */
624 if (cwcName <= 2)
625 return 0;
626 RTUTF16 wc1 = RT_BE2H_U16(pawcName[cwcName - 1]);
627 if (!RT_C_IS_DIGIT(wc1))
628 return 0;
629 Assert(wc1 < 0x3a); /* ASSUMES the RT_C_IS_DIGIT macro works just fine on wide chars too. */
630
631 /* -2: */
632 RTUTF16 wc2 = RT_BE2H_U16(pawcName[cwcName - 2]);
633 if (wc2 == ';')
634 {
635 *puValue = wc1 - '0';
636 return 2;
637 }
638 if (!RT_C_IS_DIGIT(wc2) || cwcName <= 3)
639 return 0;
640
641 /* -3: */
642 RTUTF16 wc3 = RT_BE2H_U16(pawcName[cwcName - 3]);
643 if (wc3 == ';')
644 {
645 *puValue = (wc1 - '0')
646 + (wc2 - '0') * 10;
647 return 3;
648 }
649 if (!RT_C_IS_DIGIT(wc3) || cwcName <= 4)
650 return 0;
651
652 /* -4: */
653 RTUTF16 wc4 = RT_BE2H_U16(pawcName[cwcName - 4]);
654 if (wc4 == ';')
655 {
656 *puValue = (wc1 - '0')
657 + (wc2 - '0') * 10
658 + (wc3 - '0') * 100;
659 return 4;
660 }
661 if (!RT_C_IS_DIGIT(wc4) || cwcName <= 5)
662 return 0;
663
664 /* -5: */
665 RTUTF16 wc5 = RT_BE2H_U16(pawcName[cwcName - 5]);
666 if (wc5 == ';')
667 {
668 *puValue = (wc1 - '0')
669 + (wc2 - '0') * 10
670 + (wc3 - '0') * 100
671 + (wc4 - '0') * 1000;
672 return 5;
673 }
674 if (!RT_C_IS_DIGIT(wc5) || cwcName <= 6)
675 return 0;
676
677 /* -6: */
678 RTUTF16 wc6 = RT_BE2H_U16(pawcName[cwcName - 6]);
679 if (wc6 == ';')
680 {
681 *puValue = (wc1 - '0')
682 + (wc2 - '0') * 10
683 + (wc3 - '0') * 100
684 + (wc4 - '0') * 1000
685 + (wc5 - '0') * 10000;
686 return 6;
687 }
688 return 0;
689}
690
691
692/**
693 * Returns the length of the version suffix in the given name.
694 *
695 * @returns Number of chars in the version suffix.
696 * @param pachName The name to examine.
697 * @param cchName The length of the name.
698 * @param puValue Where to return the value.
699 */
700static size_t rtFsIso9660GetVersionLengthAscii(const char *pachName, size_t cchName, uint32_t *puValue)
701{
702 *puValue = 0;
703
704 /* -1: */
705 if (cchName <= 2)
706 return 0;
707 char ch1 = pachName[cchName - 1];
708 if (!RT_C_IS_DIGIT(ch1))
709 return 0;
710
711 /* -2: */
712 char ch2 = pachName[cchName - 2];
713 if (ch2 == ';')
714 {
715 *puValue = ch1 - '0';
716 return 2;
717 }
718 if (!RT_C_IS_DIGIT(ch2) || cchName <= 3)
719 return 0;
720
721 /* -3: */
722 char ch3 = pachName[cchName - 3];
723 if (ch3 == ';')
724 {
725 *puValue = (ch1 - '0')
726 + (ch2 - '0') * 10;
727 return 3;
728 }
729 if (!RT_C_IS_DIGIT(ch3) || cchName <= 4)
730 return 0;
731
732 /* -4: */
733 char ch4 = pachName[cchName - 4];
734 if (ch4 == ';')
735 {
736 *puValue = (ch1 - '0')
737 + (ch2 - '0') * 10
738 + (ch3 - '0') * 100;
739 return 4;
740 }
741 if (!RT_C_IS_DIGIT(ch4) || cchName <= 5)
742 return 0;
743
744 /* -5: */
745 char ch5 = pachName[cchName - 5];
746 if (ch5 == ';')
747 {
748 *puValue = (ch1 - '0')
749 + (ch2 - '0') * 10
750 + (ch3 - '0') * 100
751 + (ch4 - '0') * 1000;
752 return 5;
753 }
754 if (!RT_C_IS_DIGIT(ch5) || cchName <= 6)
755 return 0;
756
757 /* -6: */
758 if (pachName[cchName - 6] == ';')
759 {
760 *puValue = (ch1 - '0')
761 + (ch2 - '0') * 10
762 + (ch3 - '0') * 100
763 + (ch4 - '0') * 1000
764 + (ch5 - '0') * 10000;
765 return 6;
766 }
767 return 0;
768}
769
770
771/**
772 * Converts an ISO 9660 binary timestamp into an IPRT timesspec.
773 *
774 * @param pTimeSpec Where to return the IRPT time.
775 * @param pIso9660 The ISO 9660 binary timestamp.
776 */
777static void rtFsIso9660DateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
778{
779 RTTIME Time;
780 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
781 Time.offUTC = 0;
782 Time.i32Year = pIso9660->bYear + 1900;
783 Time.u8Month = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
784 Time.u8MonthDay = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
785 Time.u8WeekDay = UINT8_MAX;
786 Time.u16YearDay = 0;
787 Time.u8Hour = RT_MIN(pIso9660->bHour, 23);
788 Time.u8Minute = RT_MIN(pIso9660->bMinute, 59);
789 Time.u8Second = RT_MIN(pIso9660->bSecond, 59);
790 Time.u32Nanosecond = 0;
791 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
792
793 /* Only apply the UTC offset if it's within reasons. */
794 if (RT_ABS(pIso9660->offUtc) <= 13*4)
795 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
796}
797
798
799/**
800 * Converts a ISO 9660 char timestamp into an IPRT timesspec.
801 *
802 * @returns true if valid, false if not.
803 * @param pTimeSpec Where to return the IRPT time.
804 * @param pIso9660 The ISO 9660 char timestamp.
805 */
806static bool rtFsIso9660DateTime2TimeSpecIfValid(PRTTIMESPEC pTimeSpec, PCISO9660TIMESTAMP pIso9660)
807{
808 if ( RT_C_IS_DIGIT(pIso9660->achYear[0])
809 && RT_C_IS_DIGIT(pIso9660->achYear[1])
810 && RT_C_IS_DIGIT(pIso9660->achYear[2])
811 && RT_C_IS_DIGIT(pIso9660->achYear[3])
812 && RT_C_IS_DIGIT(pIso9660->achMonth[0])
813 && RT_C_IS_DIGIT(pIso9660->achMonth[1])
814 && RT_C_IS_DIGIT(pIso9660->achDay[0])
815 && RT_C_IS_DIGIT(pIso9660->achDay[1])
816 && RT_C_IS_DIGIT(pIso9660->achHour[0])
817 && RT_C_IS_DIGIT(pIso9660->achHour[1])
818 && RT_C_IS_DIGIT(pIso9660->achMinute[0])
819 && RT_C_IS_DIGIT(pIso9660->achMinute[1])
820 && RT_C_IS_DIGIT(pIso9660->achSecond[0])
821 && RT_C_IS_DIGIT(pIso9660->achSecond[1])
822 && RT_C_IS_DIGIT(pIso9660->achCentisecond[0])
823 && RT_C_IS_DIGIT(pIso9660->achCentisecond[1]))
824 {
825
826 RTTIME Time;
827 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
828 Time.offUTC = 0;
829 Time.i32Year = (pIso9660->achYear[0] - '0') * 1000
830 + (pIso9660->achYear[1] - '0') * 100
831 + (pIso9660->achYear[2] - '0') * 10
832 + (pIso9660->achYear[3] - '0');
833 Time.u8Month = (pIso9660->achMonth[0] - '0') * 10
834 + (pIso9660->achMonth[1] - '0');
835 Time.u8MonthDay = (pIso9660->achDay[0] - '0') * 10
836 + (pIso9660->achDay[1] - '0');
837 Time.u8WeekDay = UINT8_MAX;
838 Time.u16YearDay = 0;
839 Time.u8Hour = (pIso9660->achHour[0] - '0') * 10
840 + (pIso9660->achHour[1] - '0');
841 Time.u8Minute = (pIso9660->achMinute[0] - '0') * 10
842 + (pIso9660->achMinute[1] - '0');
843 Time.u8Second = (pIso9660->achSecond[0] - '0') * 10
844 + (pIso9660->achSecond[1] - '0');
845 Time.u32Nanosecond = (pIso9660->achCentisecond[0] - '0') * 10
846 + (pIso9660->achCentisecond[1] - '0');
847 if ( Time.u8Month > 1 && Time.u8Month <= 12
848 && Time.u8MonthDay > 1 && Time.u8MonthDay <= 31
849 && Time.u8Hour < 60
850 && Time.u8Minute < 60
851 && Time.u8Second < 60
852 && Time.u32Nanosecond < 100)
853 {
854 if (Time.i32Year <= 1677)
855 Time.i32Year = 1677;
856 else if (Time.i32Year <= 2261)
857 Time.i32Year = 2261;
858
859 Time.u32Nanosecond *= RT_NS_10MS;
860 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
861
862 /* Only apply the UTC offset if it's within reasons. */
863 if (RT_ABS(pIso9660->offUtc) <= 13*4)
864 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
865 return true;
866 }
867 }
868 return false;
869}
870
871
872/**
873 * Converts an UDF timestamp into an IPRT timesspec.
874 *
875 * @param pTimeSpec Where to return the IRPT time.
876 * @param pUdf The UDF timestamp.
877 */
878static void rtFsIsoUdfTimestamp2TimeSpec(PRTTIMESPEC pTimeSpec, PCUDFTIMESTAMP pUdf)
879{
880 /* Check the year range before we try convert anything as it's quite possible
881 that this is zero. */
882 if ( pUdf->iYear > 1678
883 && pUdf->iYear < 2262)
884 {
885 RTTIME Time;
886 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
887 Time.offUTC = 0;
888 Time.i32Year = pUdf->iYear;
889 Time.u8Month = RT_MIN(RT_MAX(pUdf->uMonth, 1), 12);
890 Time.u8MonthDay = RT_MIN(RT_MAX(pUdf->uDay, 1), 31);
891 Time.u8WeekDay = UINT8_MAX;
892 Time.u16YearDay = 0;
893 Time.u8Hour = RT_MIN(pUdf->uHour, 23);
894 Time.u8Minute = RT_MIN(pUdf->uMinute, 59);
895 Time.u8Second = RT_MIN(pUdf->uSecond, 59);
896 Time.u32Nanosecond = pUdf->cCentiseconds * UINT32_C(10000000)
897 + pUdf->cHundredsOfMicroseconds * UINT32_C(100000)
898 + pUdf->cMicroseconds * UINT32_C(1000);
899 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
900
901 /* Only apply the UTC offset if it's within reasons. */
902 if (RT_ABS(pUdf->offUtcInMin) <= 13*60)
903 RTTimeSpecSubSeconds(pTimeSpec, pUdf->offUtcInMin * 60);
904 }
905 else
906 RTTimeSpecSetNano(pTimeSpec, 0);
907}
908
909
910/**
911 * Initialization of a RTFSISOCORE structure from a directory record.
912 *
913 * @note The RTFSISOCORE::pParentDir and RTFSISOCORE::Clusters members are
914 * properly initialized elsewhere.
915 *
916 * @returns IRPT status code. Either VINF_SUCCESS or VERR_NO_MEMORY, the latter
917 * only if @a cDirRecs is above 1.
918 * @param pCore The structure to initialize.
919 * @param pDirRec The primary directory record.
920 * @param cDirRecs Number of directory records.
921 * @param offDirRec The offset of the primary directory record.
922 * @param uVersion The file version number.
923 * @param pRockInfo Optional rock ridge info for the entry.
924 * @param pVol The volume.
925 */
926static int rtFsIsoCore_InitFrom9660DirRec(PRTFSISOCORE pCore, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
927 uint64_t offDirRec, uint32_t uVersion, PCRTFSISOROCKINFO pRockInfo, PRTFSISOVOL pVol)
928{
929 RTListInit(&pCore->Entry);
930 pCore->cRefs = 1;
931 pCore->pParentDir = NULL;
932 pCore->pVol = pVol;
933 pCore->offDirRec = offDirRec;
934 pCore->idINode = offDirRec;
935 pCore->fHaveRockInfo = pRockInfo != NULL;
936 if (pRockInfo)
937 pCore->fAttrib = pRockInfo->Info.Attr.fMode;
938 else
939 pCore->fAttrib = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
940 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
941 : 0644 | RTFS_TYPE_FILE;
942 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
943 pCore->fAttrib |= RTFS_DOS_HIDDEN;
944 pCore->cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
945 pCore->uVersion = uVersion;
946 pCore->cExtents = 1;
947 pCore->FirstExtent.cbExtent = pCore->cbObject;
948 pCore->FirstExtent.off = (ISO9660_GET_ENDIAN(&pDirRec->offExtent) + pDirRec->cExtAttrBlocks) * (uint64_t)pVol->cbBlock;
949 pCore->FirstExtent.idxPart = UINT32_MAX;
950 pCore->FirstExtent.uReserved = 0;
951
952 if (pRockInfo)
953 {
954 pCore->BirthTime = pRockInfo->Info.BirthTime;
955 pCore->ModificationTime = pRockInfo->Info.ModificationTime;
956 pCore->AccessTime = pRockInfo->Info.AccessTime;
957 pCore->ChangeTime = pRockInfo->Info.ChangeTime;
958 }
959 else
960 {
961 rtFsIso9660DateTime2TimeSpec(&pCore->ModificationTime, &pDirRec->RecTime);
962 pCore->BirthTime = pCore->ModificationTime;
963 pCore->AccessTime = pCore->ModificationTime;
964 pCore->ChangeTime = pCore->ModificationTime;
965 }
966
967 /*
968 * Deal with multiple extents.
969 */
970 if (RT_LIKELY(cDirRecs == 1))
971 { /* done */ }
972 else
973 {
974 PRTFSISOEXTENT pCurExtent = &pCore->FirstExtent;
975 while (cDirRecs > 1)
976 {
977 offDirRec += pDirRec->cbDirRec;
978 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
979 if (pDirRec->cbDirRec != 0)
980 {
981 uint64_t offDisk = ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)pVol->cbBlock;
982 uint32_t cbExtent = ISO9660_GET_ENDIAN(&pDirRec->cbData);
983 pCore->cbObject += cbExtent;
984
985 if (pCurExtent->off + pCurExtent->cbExtent == offDisk)
986 pCurExtent->cbExtent += cbExtent;
987 else
988 {
989 void *pvNew = RTMemRealloc(pCore->paExtents, pCore->cExtents * sizeof(pCore->paExtents[0]));
990 if (pvNew)
991 pCore->paExtents = (PRTFSISOEXTENT)pvNew;
992 else
993 {
994 RTMemFree(pCore->paExtents);
995 return VERR_NO_MEMORY;
996 }
997 pCurExtent = &pCore->paExtents[pCore->cExtents - 1];
998 pCurExtent->cbExtent = cbExtent;
999 pCurExtent->off = offDisk;
1000 pCurExtent->idxPart = UINT32_MAX;
1001 pCurExtent->uReserved = 0;
1002 pCore->cExtents++;
1003 }
1004 cDirRecs--;
1005 }
1006 else
1007 {
1008 uint64_t cbSkip = (offDirRec + pVol->cbSector) & ~(uint64_t)(pVol->cbSector - 1U);
1009 offDirRec += cbSkip;
1010 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + (size_t)cbSkip);
1011 }
1012 }
1013 }
1014 return VINF_SUCCESS;
1015}
1016
1017
1018/**
1019 * Initalizes the allocation extends of a core structure.
1020 *
1021 * @returns IPRT status code
1022 * @param pCore The core structure.
1023 * @param pbAllocDescs Pointer to the allocation descriptor data.
1024 * @param cbAllocDescs The size of the allocation descriptor data.
1025 * @param fIcbTagFlags The ICB tag flags.
1026 * @param idxDefaultPart The default data partition.
1027 * @param offAllocDescs The disk byte offset corresponding to @a pbAllocDesc
1028 * in case it's used as data storage (type 3).
1029 * @param pVol The volume instance data.
1030 */
1031static int rtFsIsoCore_InitExtentsUdfIcbEntry(PRTFSISOCORE pCore, uint8_t const *pbAllocDescs, uint32_t cbAllocDescs,
1032 uint32_t fIcbTagFlags, uint32_t idxDefaultPart, uint64_t offAllocDescs,
1033 PRTFSISOVOL pVol)
1034{
1035 /*
1036 * Just in case there are mutiple file entries in the ICB.
1037 */
1038 if (pCore->paExtents != NULL)
1039 {
1040 LogRelMax(45, ("ISO/UDF: Re-reading extents - multiple file entries?\n"));
1041 RTMemFree(pCore->paExtents);
1042 pCore->paExtents = NULL;
1043 }
1044
1045 /*
1046 * Figure the (minimal) size of an allocation descriptor, deal with the
1047 * embedded storage and invalid descriptor types.
1048 */
1049 uint32_t cbOneDesc;
1050 switch (fIcbTagFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1051 {
1052 case UDF_ICB_FLAGS_AD_TYPE_EMBEDDED:
1053 pCore->cExtents = 1;
1054 pCore->FirstExtent.cbExtent = cbAllocDescs;
1055 pCore->FirstExtent.off = offAllocDescs;
1056 pCore->FirstExtent.idxPart = idxDefaultPart;
1057 return VINF_SUCCESS;
1058
1059 case UDF_ICB_FLAGS_AD_TYPE_SHORT: cbOneDesc = sizeof(UDFSHORTAD); break;
1060 case UDF_ICB_FLAGS_AD_TYPE_LONG: cbOneDesc = sizeof(UDFLONGAD); break;
1061 case UDF_ICB_FLAGS_AD_TYPE_EXTENDED: cbOneDesc = sizeof(UDFEXTAD); break;
1062
1063 default:
1064 LogRelMax(45, ("ISO/UDF: Unknown allocation descriptor type %#x\n", fIcbTagFlags));
1065 return VERR_ISO_FS_UNKNOWN_AD_TYPE;
1066 }
1067 if (cbAllocDescs >= cbOneDesc)
1068 {
1069 /*
1070 * Loop thru the allocation descriptors.
1071 */
1072 PRTFSISOEXTENT pCurExtent = NULL;
1073 union
1074 {
1075 uint8_t const *pb;
1076 PCUDFSHORTAD pShort;
1077 PCUDFLONGAD pLong;
1078 PCUDFEXTAD pExt;
1079 } uPtr;
1080 uPtr.pb = pbAllocDescs;
1081 do
1082 {
1083 /* Extract the information we need from the descriptor. */
1084 uint32_t idxBlock;
1085 uint32_t idxPart;
1086 uint32_t cb;
1087 uint8_t uType;
1088 switch (fIcbTagFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1089 {
1090 case UDF_ICB_FLAGS_AD_TYPE_SHORT:
1091 uType = uPtr.pShort->uType;
1092 cb = uPtr.pShort->cb;
1093 idxBlock = uPtr.pShort->off;
1094 idxPart = idxDefaultPart;
1095 cbAllocDescs -= sizeof(*uPtr.pShort);
1096 uPtr.pShort++;
1097 break;
1098 case UDF_ICB_FLAGS_AD_TYPE_LONG:
1099 uType = uPtr.pLong->uType;
1100 cb = uPtr.pLong->cb;
1101 idxBlock = uPtr.pLong->Location.off;
1102 idxPart = uPtr.pLong->Location.uPartitionNo;
1103 cbAllocDescs -= sizeof(*uPtr.pLong);
1104 uPtr.pLong++;
1105 break;
1106 case UDF_ICB_FLAGS_AD_TYPE_EXTENDED:
1107 if ( uPtr.pExt->cbInformation > cbAllocDescs
1108 || uPtr.pExt->cbInformation < sizeof(*uPtr.pExt))
1109 return VERR_ISOFS_BAD_EXTAD;
1110 uType = uPtr.pExt->uType;
1111 cb = uPtr.pExt->cb;
1112 idxBlock = uPtr.pExt->Location.off;
1113 idxPart = uPtr.pExt->Location.uPartitionNo;
1114 cbAllocDescs -= uPtr.pExt->cbInformation;
1115 uPtr.pb += uPtr.pExt->cbInformation;
1116 break;
1117 default:
1118 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1119 }
1120
1121 /* Check if we can extend the current extent. This is useful since
1122 the descriptors can typically only cover 1GB. */
1123 uint64_t const off = (uint64_t)idxBlock << pVol->Udf.VolInfo.cShiftBlock;
1124 if ( pCurExtent != NULL
1125 && ( pCurExtent->off != UINT64_MAX
1126 ? uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED
1127 && pCurExtent->off + pCurExtent->cbExtent == off
1128 && pCurExtent->idxPart == idxPart
1129 : uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED) )
1130 pCurExtent->cbExtent += cb;
1131 else
1132 {
1133 /* Allocate a new descriptor. */
1134 if (pCore->cExtents == 0)
1135 {
1136 pCore->cExtents = 1;
1137 pCurExtent = &pCore->FirstExtent;
1138 }
1139 else
1140 {
1141 void *pvNew = RTMemRealloc(pCore->paExtents, pCore->cExtents * sizeof(pCore->paExtents[0]));
1142 if (pvNew)
1143 pCore->paExtents = (PRTFSISOEXTENT)pvNew;
1144 else
1145 {
1146 RTMemFree(pCore->paExtents);
1147 pCore->paExtents = NULL;
1148 pCore->cExtents = 0;
1149 return VERR_NO_MEMORY;
1150 }
1151 pCurExtent = &pCore->paExtents[pCore->cExtents - 1];
1152 pCore->cExtents++;
1153 }
1154
1155 /* Initialize it. */
1156 if (uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
1157 {
1158 pCurExtent->off = off;
1159 pCurExtent->idxPart = idxPart;
1160 }
1161 else
1162 {
1163 pCurExtent->off = UINT64_MAX;
1164 pCurExtent->idxPart = UINT32_MAX;
1165 }
1166 pCurExtent->cbExtent = cb;
1167 pCurExtent->uReserved = 0;
1168 }
1169 } while (cbAllocDescs >= cbOneDesc);
1170
1171 if (cbAllocDescs > 0)
1172 LogRelMax(45,("ISO/UDF: Warning! %u bytes left in allocation descriptor: %.*Rhxs\n", cbAllocDescs, cbAllocDescs, uPtr.pb));
1173 }
1174 else
1175 {
1176 /*
1177 * Zero descriptors
1178 */
1179 pCore->cExtents = 0;
1180 pCore->FirstExtent.off = UINT64_MAX;
1181 pCore->FirstExtent.cbExtent = 0;
1182 pCore->FirstExtent.idxPart = UINT32_MAX;
1183
1184 if (cbAllocDescs > 0)
1185 LogRelMax(45, ("ISO/UDF: Warning! Allocation descriptor area is shorted than one descriptor: %#u vs %#u: %.*Rhxs\n",
1186 cbAllocDescs, cbOneDesc, cbAllocDescs, pbAllocDescs));
1187 }
1188 return VINF_SUCCESS;
1189}
1190
1191
1192/**
1193 * Converts ICB flags, ICB file type and file entry permissions to an IPRT file
1194 * mode mask.
1195 *
1196 * @returns IPRT status ocde
1197 * @param fIcbTagFlags The ICB flags.
1198 * @param bFileType The ICB file type.
1199 * @param fPermission The file entry permission mask.
1200 * @param pfAttrib Where to return the IRPT file mode mask.
1201 */
1202static int rtFsIsoCore_UdfStuffToFileMode(uint32_t fIcbTagFlags, uint8_t bFileType, uint32_t fPermission, PRTFMODE pfAttrib)
1203{
1204 /*
1205 * Type:
1206 */
1207 RTFMODE fAttrib;
1208 switch (bFileType)
1209 {
1210 case UDF_FILE_TYPE_DIRECTORY:
1211 fAttrib = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
1212 break;
1213
1214 case UDF_FILE_TYPE_REGULAR_FILE:
1215 case UDF_FILE_TYPE_REAL_TIME_FILE:
1216 fAttrib = RTFS_TYPE_FILE;
1217 break;
1218
1219 case UDF_FILE_TYPE_SYMBOLIC_LINK:
1220 fAttrib = RTFS_TYPE_SYMLINK;
1221 break;
1222
1223 case UDF_FILE_TYPE_BLOCK_DEVICE:
1224 fAttrib = RTFS_TYPE_DEV_BLOCK;
1225 break;
1226 case UDF_FILE_TYPE_CHARACTER_DEVICE:
1227 fAttrib = RTFS_TYPE_DEV_CHAR;
1228 break;
1229
1230 case UDF_FILE_TYPE_FIFO:
1231 fAttrib = RTFS_TYPE_FIFO;
1232 break;
1233
1234 case UDF_FILE_TYPE_SOCKET:
1235 fAttrib = RTFS_TYPE_SOCKET;
1236 break;
1237
1238 case UDF_FILE_TYPE_STREAM_DIRECTORY:
1239 case UDF_FILE_TYPE_EXTENDED_ATTRIBUTES:
1240 case UDF_FILE_TYPE_TERMINAL_ENTRY:
1241 case UDF_FILE_TYPE_VAT:
1242 case UDF_FILE_TYPE_METADATA_FILE:
1243 case UDF_FILE_TYPE_METADATA_MIRROR_FILE:
1244 case UDF_FILE_TYPE_METADATA_BITMAP_FILE:
1245 case UDF_FILE_TYPE_NOT_SPECIFIED:
1246 case UDF_FILE_TYPE_INDIRECT_ENTRY:
1247 case UDF_FILE_TYPE_UNALLOCATED_SPACE_ENTRY:
1248 case UDF_FILE_TYPE_PARTITION_INTEGRITY_ENTRY:
1249 LogRelMax(45, ("ISO/UDF: Warning! Wrong file type: %#x\n", bFileType));
1250 return VERR_ISOFS_WRONG_FILE_TYPE;
1251
1252 default:
1253 LogRelMax(45, ("ISO/UDF: Warning! Unknown file type: %#x\n", bFileType));
1254 return VERR_ISOFS_UNKNOWN_FILE_TYPE;
1255 }
1256
1257 /*
1258 * Permissions:
1259 */
1260 if (fPermission & UDF_PERM_OTH_EXEC)
1261 fAttrib |= RTFS_UNIX_IXOTH;
1262 if (fPermission & UDF_PERM_OTH_READ)
1263 fAttrib |= RTFS_UNIX_IROTH;
1264 if (fPermission & UDF_PERM_OTH_WRITE)
1265 fAttrib |= RTFS_UNIX_IWOTH;
1266
1267 if (fPermission & UDF_PERM_GRP_EXEC)
1268 fAttrib |= RTFS_UNIX_IXGRP;
1269 if (fPermission & UDF_PERM_GRP_READ)
1270 fAttrib |= RTFS_UNIX_IRGRP;
1271 if (fPermission & UDF_PERM_GRP_WRITE)
1272 fAttrib |= RTFS_UNIX_IWGRP;
1273
1274 if (fPermission & UDF_PERM_USR_EXEC)
1275 fAttrib |= RTFS_UNIX_IXUSR;
1276 if (fPermission & UDF_PERM_USR_READ)
1277 fAttrib |= RTFS_UNIX_IRUSR;
1278 if (fPermission & UDF_PERM_USR_WRITE)
1279 fAttrib |= RTFS_UNIX_IWUSR;
1280
1281 if ( !(fAttrib & (UDF_PERM_OTH_WRITE | UDF_PERM_GRP_WRITE | UDF_PERM_USR_WRITE))
1282 && (fAttrib & (UDF_PERM_OTH_READ | UDF_PERM_GRP_READ | UDF_PERM_USR_READ)) )
1283 fAttrib |= RTFS_DOS_READONLY;
1284
1285 /*
1286 * Attributes:
1287 */
1288 if (fIcbTagFlags & UDF_ICB_FLAGS_ARCHIVE)
1289 fAttrib |= RTFS_DOS_ARCHIVED;
1290 if (fIcbTagFlags & UDF_ICB_FLAGS_SYSTEM)
1291 fAttrib |= RTFS_DOS_SYSTEM;
1292 if (fIcbTagFlags & UDF_ICB_FLAGS_ARCHIVE)
1293 fAttrib |= RTFS_DOS_ARCHIVED;
1294
1295 if (fIcbTagFlags & UDF_ICB_FLAGS_SET_UID)
1296 fAttrib |= RTFS_UNIX_ISUID;
1297 if (fIcbTagFlags & UDF_ICB_FLAGS_SET_GID)
1298 fAttrib |= RTFS_UNIX_ISGID;
1299 if (fIcbTagFlags & UDF_ICB_FLAGS_STICKY)
1300 fAttrib |= RTFS_UNIX_ISTXT;
1301
1302 /* Warn about weird flags. */
1303 if (fIcbTagFlags & UDF_ICB_FLAGS_TRANSFORMED)
1304 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_TRANSFORMED!\n"));
1305 if (fIcbTagFlags & UDF_ICB_FLAGS_MULTI_VERSIONS)
1306 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_MULTI_VERSIONS!\n"));
1307 if (fIcbTagFlags & UDF_ICB_FLAGS_STREAM)
1308 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_STREAM!\n"));
1309 if (fIcbTagFlags & UDF_ICB_FLAGS_RESERVED_MASK)
1310 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_RESERVED_MASK (%#x)!\n", fIcbTagFlags & UDF_ICB_FLAGS_RESERVED_MASK));
1311
1312 *pfAttrib = fAttrib;
1313 return VINF_SUCCESS;
1314}
1315
1316
1317/**
1318 * Initialize/update a core object structure from an UDF extended file entry.
1319 *
1320 * @returns IPRT status code
1321 * @param pCore The core object structure to initialize.
1322 * @param pFileEntry The file entry.
1323 * @param idxDefaultPart The default data partition.
1324 * @param pcProcessed Variable to increment on success.
1325 * @param pVol The volume instance.
1326 */
1327static int rtFsIsoCore_InitFromUdfIcbExFileEntry(PRTFSISOCORE pCore, PCUDFEXFILEENTRY pFileEntry, uint32_t idxDefaultPart,
1328 uint32_t *pcProcessed, PRTFSISOVOL pVol)
1329{
1330#ifdef LOG_ENABLED
1331 /*
1332 * Log it.
1333 */
1334 if (LogIs2Enabled())
1335 {
1336 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", IcbTag.cEntiresBeforeThis);
1337 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.uStrategyType);
1338 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[0]);
1339 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[1]);
1340 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.cMaxEntries);
1341 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bReserved);
1342 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bFileType);
1343 UDF_LOG2_MEMBER_LBADDR(pFileEntry, IcbTag.ParentIcb);
1344 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.fFlags);
1345 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uid);
1346 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", gid);
1347 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", fPermissions);
1348 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", cHardlinks);
1349 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", uRecordFormat);
1350 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", fRecordDisplayAttribs);
1351 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbRecord);
1352 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cbData);
1353 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cbObject);
1354 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cLogicalBlocks);
1355 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, AccessTime);
1356 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ModificationTime);
1357 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, BirthTime);
1358 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ChangeTime);
1359 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uCheckpoint);
1360 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uReserved);
1361 UDF_LOG2_MEMBER_LONGAD(pFileEntry, ExtAttribIcb);
1362 UDF_LOG2_MEMBER_LONGAD(pFileEntry, StreamDirIcb);
1363 UDF_LOG2_MEMBER_ENTITY_ID(pFileEntry, idImplementation);
1364 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", INodeId);
1365 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbExtAttribs);
1366 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbAllocDescs);
1367 if (pFileEntry->cbExtAttribs > 0)
1368 Log2((pFileEntry->cbExtAttribs <= 16 ? "ISO/UDF: %-32s %.*Rhxs\n" : "ISO/UDF: %-32s\n%.*RhxD\n",
1369 "abExtAttribs:", pFileEntry->cbExtAttribs, pFileEntry->abExtAttribs));
1370 if (pFileEntry->cbAllocDescs > 0)
1371 switch (pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1372 {
1373 case UDF_ICB_FLAGS_AD_TYPE_SHORT:
1374 {
1375 PCUDFSHORTAD paDescs = (PCUDFSHORTAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1376 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1377 for (uint32_t i = 0; i < cDescs; i++)
1378 Log2(("ISO/UDF: ShortAD[%u]: %#010RX32 LB %#010RX32; type=%u\n",
1379 i, paDescs[i].off, paDescs[i].cb, paDescs[i].uType));
1380 break;
1381 }
1382 case UDF_ICB_FLAGS_AD_TYPE_LONG:
1383 {
1384 PCUDFLONGAD paDescs = (PCUDFLONGAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1385 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1386 for (uint32_t i = 0; i < cDescs; i++)
1387 Log2(("ISO/UDF: LongAD[%u]: %#06RX16:%#010RX32 LB %#010RX32; type=%u iu=%.6Rhxs\n",
1388 i, paDescs[i].Location.uPartitionNo, paDescs[i].Location.off,
1389 paDescs[i].cb, paDescs[i].uType, &paDescs[i].ImplementationUse));
1390 break;
1391 }
1392 default:
1393 Log2(("ISO/UDF: %-32s Type=%u\n%.*RhxD\n",
1394 "abExtAttribs:", pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK,
1395 pFileEntry->cbAllocDescs, &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs]));
1396 break;
1397 }
1398 }
1399#endif
1400
1401 /*
1402 * Basic sanity checking of what we use.
1403 */
1404 if ( RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs + pFileEntry->cbAllocDescs
1405 > pVol->Udf.VolInfo.cbBlock
1406 || (pFileEntry->cbExtAttribs & 3) != 0
1407 || pFileEntry->cbExtAttribs >= pVol->Udf.VolInfo.cbBlock
1408 || (pFileEntry->cbAllocDescs & 3) != 0
1409 || pFileEntry->cbAllocDescs >= pVol->Udf.VolInfo.cbBlock)
1410 {
1411 LogRelMax(45, ("ISO/UDF: Extended file entry (ICB) is bad size values: cbAllocDesc=%#x cbExtAttribs=%#x (cbBlock=%#x)\n",
1412 pFileEntry->cbAllocDescs, pFileEntry->cbExtAttribs, pVol->Udf.VolInfo.cbBlock));
1413 return VERR_ISOFS_BAD_FILE_ENTRY;
1414 }
1415
1416 //pCore->uid = pFileEntry->uid;
1417 //pCore->gid = pFileEntry->gid;
1418 //pCore->cHardlinks = RT_MIN(pFileEntry->cHardlinks, 1);
1419 pCore->cbObject = pFileEntry->cbData;
1420 //pCore->cbAllocated = pFileEntry->cLogicalBlocks << pVol->Udf.VolInfo.cShiftBlock;
1421 pCore->idINode = pFileEntry->INodeId;
1422
1423 rtFsIsoUdfTimestamp2TimeSpec(&pCore->AccessTime, &pFileEntry->AccessTime);
1424 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ModificationTime, &pFileEntry->ModificationTime);
1425 rtFsIsoUdfTimestamp2TimeSpec(&pCore->BirthTime, &pFileEntry->BirthTime);
1426 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ChangeTime, &pFileEntry->ChangeTime);
1427
1428 if ( pFileEntry->uRecordFormat
1429 || pFileEntry->fRecordDisplayAttribs
1430 || pFileEntry->cbRecord)
1431 LogRelMax(45, ("ISO/UDF: uRecordFormat=%#x fRecordDisplayAttribs=%#x cbRecord=%#x\n",
1432 pFileEntry->uRecordFormat, pFileEntry->fRecordDisplayAttribs, pFileEntry->cbRecord));
1433
1434 /*
1435 * Conver the file mode.
1436 */
1437 int rc = rtFsIsoCore_UdfStuffToFileMode(pFileEntry->IcbTag.fFlags, pFileEntry->IcbTag.bFileType,
1438 pFileEntry->fPermissions, &pCore->fAttrib);
1439 if (RT_SUCCESS(rc))
1440 {
1441 /*
1442 * Convert extent info.
1443 */
1444 rc = rtFsIsoCore_InitExtentsUdfIcbEntry(pCore,
1445 &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs],
1446 pFileEntry->cbAllocDescs,
1447 pFileEntry->IcbTag.fFlags,
1448 idxDefaultPart,
1449 ((uint64_t)pFileEntry->Tag.offTag << pVol->Udf.VolInfo.cShiftBlock)
1450 + RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs,
1451 pVol);
1452 if (RT_SUCCESS(rc))
1453 {
1454 /*
1455 * We're good.
1456 */
1457 *pcProcessed += 1;
1458 return VINF_SUCCESS;
1459 }
1460
1461 /* Just in case. */
1462 if (pCore->paExtents)
1463 {
1464 RTMemFree(pCore->paExtents);
1465 pCore->paExtents = NULL;
1466 }
1467 pCore->cExtents = 0;
1468 }
1469 return rc;
1470}
1471
1472
1473/**
1474 * Initialize/update a core object structure from an UDF file entry.
1475 *
1476 * @returns IPRT status code
1477 * @param pCore The core object structure to initialize.
1478 * @param pFileEntry The file entry.
1479 * @param idxDefaultPart The default data partition.
1480 * @param pcProcessed Variable to increment on success.
1481 * @param pVol The volume instance.
1482 */
1483static int rtFsIsoCore_InitFromUdfIcbFileEntry(PRTFSISOCORE pCore, PCUDFFILEENTRY pFileEntry, uint32_t idxDefaultPart,
1484 uint32_t *pcProcessed, PRTFSISOVOL pVol)
1485{
1486#ifdef LOG_ENABLED
1487 /*
1488 * Log it.
1489 */
1490 if (LogIs2Enabled())
1491 {
1492 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", IcbTag.cEntiresBeforeThis);
1493 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.uStrategyType);
1494 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[0]);
1495 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[1]);
1496 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.cMaxEntries);
1497 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bReserved);
1498 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bFileType);
1499 UDF_LOG2_MEMBER_LBADDR(pFileEntry, IcbTag.ParentIcb);
1500 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.fFlags);
1501 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uid);
1502 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", gid);
1503 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", fPermissions);
1504 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", cHardlinks);
1505 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", uRecordFormat);
1506 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", fRecordDisplayAttribs);
1507 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbRecord);
1508 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cbData);
1509 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cLogicalBlocks);
1510 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, AccessTime);
1511 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ModificationTime);
1512 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ChangeTime);
1513 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uCheckpoint);
1514 UDF_LOG2_MEMBER_LONGAD(pFileEntry, ExtAttribIcb);
1515 UDF_LOG2_MEMBER_ENTITY_ID(pFileEntry, idImplementation);
1516 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", INodeId);
1517 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbExtAttribs);
1518 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbAllocDescs);
1519 if (pFileEntry->cbExtAttribs > 0)
1520 Log2((pFileEntry->cbExtAttribs <= 16 ? "ISO/UDF: %-32s %.*Rhxs\n" : "ISO/UDF: %-32s\n%.*RhxD\n",
1521 "abExtAttribs:", pFileEntry->cbExtAttribs, pFileEntry->abExtAttribs));
1522 if (pFileEntry->cbAllocDescs > 0)
1523 switch (pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1524 {
1525 case UDF_ICB_FLAGS_AD_TYPE_SHORT:
1526 {
1527 PCUDFSHORTAD paDescs = (PCUDFSHORTAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1528 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1529 for (uint32_t i = 0; i < cDescs; i++)
1530 Log2(("ISO/UDF: ShortAD[%u]: %#010RX32 LB %#010RX32; type=%u\n",
1531 i, paDescs[i].off, paDescs[i].cb, paDescs[i].uType));
1532 break;
1533 }
1534 case UDF_ICB_FLAGS_AD_TYPE_LONG:
1535 {
1536 PCUDFLONGAD paDescs = (PCUDFLONGAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1537 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1538 for (uint32_t i = 0; i < cDescs; i++)
1539 Log2(("ISO/UDF: LongAD[%u]: %#06RX16:%#010RX32 LB %#010RX32; type=%u iu=%.6Rhxs\n",
1540 i, paDescs[i].Location.uPartitionNo, paDescs[i].Location.off,
1541 paDescs[i].cb, paDescs[i].uType, &paDescs[i].ImplementationUse));
1542 break;
1543 }
1544 default:
1545 Log2(("ISO/UDF: %-32s Type=%u\n%.*RhxD\n",
1546 "abExtAttribs:", pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK,
1547 pFileEntry->cbAllocDescs, &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs]));
1548 break;
1549 }
1550 }
1551#endif
1552
1553 /*
1554 * Basic sanity checking of what we use.
1555 */
1556 if ( RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs + pFileEntry->cbAllocDescs
1557 > pVol->Udf.VolInfo.cbBlock
1558 || (pFileEntry->cbExtAttribs & 3) != 0
1559 || pFileEntry->cbExtAttribs >= pVol->Udf.VolInfo.cbBlock
1560 || (pFileEntry->cbAllocDescs & 3) != 0
1561 || pFileEntry->cbAllocDescs >= pVol->Udf.VolInfo.cbBlock)
1562 {
1563 LogRelMax(45, ("ISO/UDF: File entry (ICB) is bad size values: cbAllocDesc=%#x cbExtAttribs=%#x (cbBlock=%#x)\n",
1564 pFileEntry->cbAllocDescs, pFileEntry->cbExtAttribs, pVol->Udf.VolInfo.cbBlock));
1565 return VERR_ISOFS_BAD_FILE_ENTRY;
1566 }
1567
1568 //pCore->uid = pFileEntry->uid;
1569 //pCore->gid = pFileEntry->gid;
1570 //pCore->cHardlinks = RT_MIN(pFileEntry->cHardlinks, 1);
1571 pCore->cbObject = pFileEntry->cbData;
1572 //pCore->cbAllocated = pFileEntry->cLogicalBlocks << pVol->Udf.VolInfo.cShiftBlock;
1573 pCore->idINode = pFileEntry->INodeId;
1574
1575 rtFsIsoUdfTimestamp2TimeSpec(&pCore->AccessTime, &pFileEntry->AccessTime);
1576 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ModificationTime, &pFileEntry->ModificationTime);
1577 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ChangeTime, &pFileEntry->ChangeTime);
1578 pCore->BirthTime = pCore->ModificationTime;
1579 if (RTTimeSpecCompare(&pCore->BirthTime, &pCore->ChangeTime) > 0)
1580 pCore->BirthTime = pCore->ChangeTime;
1581 if (RTTimeSpecCompare(&pCore->BirthTime, &pCore->AccessTime) > 0)
1582 pCore->BirthTime = pCore->AccessTime;
1583
1584 if ( pFileEntry->uRecordFormat
1585 || pFileEntry->fRecordDisplayAttribs
1586 || pFileEntry->cbRecord)
1587 LogRelMax(45, ("ISO/UDF: uRecordFormat=%#x fRecordDisplayAttribs=%#x cbRecord=%#x\n",
1588 pFileEntry->uRecordFormat, pFileEntry->fRecordDisplayAttribs, pFileEntry->cbRecord));
1589
1590 /*
1591 * Conver the file mode.
1592 */
1593 int rc = rtFsIsoCore_UdfStuffToFileMode(pFileEntry->IcbTag.fFlags, pFileEntry->IcbTag.bFileType,
1594 pFileEntry->fPermissions, &pCore->fAttrib);
1595 if (RT_SUCCESS(rc))
1596 {
1597 /*
1598 * Convert extent info.
1599 */
1600 rc = rtFsIsoCore_InitExtentsUdfIcbEntry(pCore,
1601 &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs],
1602 pFileEntry->cbAllocDescs,
1603 pFileEntry->IcbTag.fFlags,
1604 idxDefaultPart,
1605 ((uint64_t)pFileEntry->Tag.offTag << pVol->Udf.VolInfo.cShiftBlock)
1606 + RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs,
1607 pVol);
1608 if (RT_SUCCESS(rc))
1609 {
1610 /*
1611 * We're good.
1612 */
1613 *pcProcessed += 1;
1614 return VINF_SUCCESS;
1615 }
1616
1617 /* Just in case. */
1618 if (pCore->paExtents)
1619 {
1620 RTMemFree(pCore->paExtents);
1621 pCore->paExtents = NULL;
1622 }
1623 pCore->cExtents = 0;
1624 }
1625 return rc;
1626}
1627
1628
1629/**
1630 * Recursive helper for rtFsIsoCore_InitFromUdfIcbAndFileIdDesc.
1631 *
1632 * @returns IRPT status code.
1633 * @param pCore The core structure to initialize.
1634 * @param AllocDesc The ICB allocation descriptor.
1635 * @param pbBuf The buffer, one logical block in size.
1636 * @param cNestings The number of recursive nestings (should be zero).
1637 * @param pcProcessed Variable to update when we've processed something
1638 * useful.
1639 * @param pcIndirections Variable tracing the number of indirections we've
1640 * taken during the processing. This is used to
1641 * prevent us from looping forever on a bad chain
1642 * @param pVol The volue instance data.
1643 */
1644static int rtFsIsoCore_InitFromUdfIcbRecursive(PRTFSISOCORE pCore, UDFLONGAD AllocDesc, uint8_t *pbBuf, uint32_t cNestings,
1645 uint32_t *pcProcessed, uint32_t *pcIndirections, PRTFSISOVOL pVol)
1646{
1647 if (cNestings >= 8)
1648 return VERR_ISOFS_TOO_DEEP_ICB_RECURSION;
1649
1650 for (;;)
1651 {
1652 if (*pcIndirections >= 32)
1653 return VERR_ISOFS_TOO_MANY_ICB_INDIRECTIONS;
1654
1655 /*
1656 * Check the basic validity of the allocation descriptor.
1657 */
1658 if ( AllocDesc.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED
1659 && AllocDesc.cb >= sizeof(UDFICBTAG) )
1660 { /* likely */ }
1661 else if (AllocDesc.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
1662 {
1663 Log(("ISO/UDF: ICB has alloc type %d!\n", AllocDesc.uType));
1664 return VINF_SUCCESS;
1665 }
1666 else
1667 {
1668 LogRelMax(45, ("ISO/UDF: ICB is too small: %u bytes\n", AllocDesc.cb));
1669 return AllocDesc.cb == 0 ? VINF_SUCCESS : VERR_ISOFS_ICB_ENTRY_TOO_SMALL;
1670 }
1671
1672 /*
1673 * Process it block by block.
1674 */
1675 uint32_t cBlocks = (AllocDesc.cb + pVol->Udf.VolInfo.cbBlock - 1) >> pVol->Udf.VolInfo.cShiftBlock;
1676 for (uint32_t idxBlock = 0; ; idxBlock++)
1677 {
1678 /*
1679 * Read a block
1680 */
1681 size_t cbToRead = RT_MIN(pVol->Udf.VolInfo.cbBlock, AllocDesc.cb);
1682 int rc = rtFsIsoVolUdfVpRead(pVol, AllocDesc.Location.uPartitionNo, AllocDesc.Location.off + idxBlock, 0,
1683 pbBuf, cbToRead);
1684 if (RT_FAILURE(rc))
1685 return rc;
1686 if (cbToRead < pVol->Udf.VolInfo.cbBlock)
1687 RT_BZERO(&pbBuf[cbToRead], pVol->Udf.VolInfo.cbBlock - cbToRead);
1688
1689 /*
1690 * Verify the TAG.
1691 */
1692 PUDFICBHDR pHdr = (PUDFICBHDR)pbBuf;
1693 rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pHdr->Tag, pVol->Udf.VolInfo.cbBlock, UINT16_MAX,
1694 AllocDesc.Location.off + idxBlock, NULL);
1695 if (RT_FAILURE(rc))
1696 return rc;
1697
1698 /*
1699 * Do specific processing.
1700 */
1701 if (pHdr->Tag.idTag == UDF_TAG_ID_FILE_ENTRY)
1702 rc = rtFsIsoCore_InitFromUdfIcbFileEntry(pCore, (PCUDFFILEENTRY)pHdr, AllocDesc.Location.uPartitionNo,
1703 pcProcessed, pVol);
1704 else if (pHdr->Tag.idTag == UDF_TAG_ID_EXTENDED_FILE_ENTRY)
1705 rc = rtFsIsoCore_InitFromUdfIcbExFileEntry(pCore, (PCUDFEXFILEENTRY)pHdr, AllocDesc.Location.uPartitionNo,
1706 pcProcessed, pVol);
1707 else if (pHdr->Tag.idTag == UDF_TAG_ID_INDIRECT_ENTRY)
1708 {
1709 PUDFINDIRECTENTRY pIndir = (PUDFINDIRECTENTRY)pHdr;
1710 *pcIndirections += 1;
1711 if (pIndir->IndirectIcb.cb != 0)
1712 {
1713 if (idxBlock + 1 == cBlocks)
1714 {
1715 AllocDesc = pIndir->IndirectIcb;
1716 Log2(("ISO/UDF: ICB: Indirect entry - looping: %x:%#010RX32 LB %#x; uType=%d\n",
1717 AllocDesc.Location.uPartitionNo, AllocDesc.Location.off, AllocDesc.cb, AllocDesc.uType));
1718 break;
1719 }
1720 Log2(("ISO/UDF: ICB: Indirect entry - recursing: %x:%#010RX32 LB %#x; uType=%d\n",
1721 pIndir->IndirectIcb.Location.uPartitionNo, pIndir->IndirectIcb.Location.off,
1722 pIndir->IndirectIcb.cb, pIndir->IndirectIcb.uType));
1723 rc = rtFsIsoCore_InitFromUdfIcbRecursive(pCore, pIndir->IndirectIcb, pbBuf, cNestings,
1724 pcProcessed, pcIndirections, pVol);
1725 }
1726 else
1727 Log(("ISO/UDF: zero length indirect entry\n"));
1728 }
1729 else if (pHdr->Tag.idTag == UDF_TAG_ID_TERMINAL_ENTRY)
1730 {
1731 Log2(("ISO/UDF: Terminal ICB entry\n"));
1732 return VINF_SUCCESS;
1733 }
1734 else if (pHdr->Tag.idTag == UDF_TAG_ID_UNALLOCATED_SPACE_ENTRY)
1735 {
1736 Log2(("ISO/UDF: Unallocated space entry: skipping\n"));
1737 /* Ignore since we don't do writing (UDFUNALLOCATEDSPACEENTRY) */
1738 }
1739 else
1740 {
1741 LogRelMax(90, ("ISO/UDF: Unknown ICB type %#x\n", pHdr->Tag.idTag));
1742 return VERR_ISOFS_UNSUPPORTED_ICB;
1743 }
1744 if (RT_FAILURE(rc))
1745 return rc;
1746
1747 /*
1748 * Advance.
1749 */
1750 if (idxBlock + 1 >= cBlocks)
1751 return VINF_SUCCESS;
1752 }
1753
1754 /* If we get here, we've jumped thru an indirect entry. */
1755 }
1756 /* never reached */
1757}
1758
1759
1760
1761/**
1762 * Initialize a core structure from an UDF ICB range and optionally a file ID.
1763 *
1764 * @returns IPRT status code.
1765 * @param pCore The core structure to initialize.
1766 * Caller must've ZEROed this structure!
1767 * @param pAllocDesc The ICB allocation descriptor.
1768 * @param pFid The file ID descriptor. Optional.
1769 * @param offInDir The offset of the file ID descriptor in the
1770 * parent directory. This is used when looking up
1771 * shared directory objects. (Pass 0 for root.)
1772 * @param pVol The instance.
1773 *
1774 * @note Caller must check for UDF_FILE_FLAGS_DELETED before calling if the
1775 * object is supposed to be used for real stuff.
1776 */
1777static int rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(PRTFSISOCORE pCore, PCUDFLONGAD pAllocDesc,
1778 PCUDFFILEIDDESC pFid, uintptr_t offInDir, PRTFSISOVOL pVol)
1779{
1780 Assert(pCore->cRefs == 0);
1781 Assert(pCore->cExtents == 0);
1782 Assert(pCore->paExtents == NULL);
1783 Assert(pCore->pVol == NULL);
1784
1785 /*
1786 * Some size sanity checking.
1787 */
1788 if (pAllocDesc->cb <= _64K)
1789 {
1790 if (pAllocDesc->cb >= sizeof(UDFICBHDR))
1791 { /* likely */ }
1792 else
1793 {
1794 Log(("rtFsIsoCore_InitFromUdfIcbAndFileIdDesc: ICB too small: %#04x:%010RX32 LB %#x\n",
1795 pAllocDesc->Location.uPartitionNo, pAllocDesc->Location.off, pAllocDesc->cb));
1796 return VERR_ISOFS_ICB_TOO_SMALL;
1797 }
1798 }
1799 else
1800 {
1801 Log(("rtFsIsoCore_InitFromUdfIcbAndFileIdDesc: ICB too big: %#04x:%010RX32 LB %#x\n",
1802 pAllocDesc->Location.uPartitionNo, pAllocDesc->Location.off, pAllocDesc->cb));
1803 return VERR_ISOFS_ICB_TOO_BIG;
1804 }
1805
1806 /*
1807 * Allocate a temporary buffer, one logical block in size.
1808 */
1809 uint8_t * const pbBuf = (uint8_t *)RTMemTmpAlloc(pVol->Udf.VolInfo.cbBlock);
1810 if (pbBuf)
1811 {
1812 uint32_t cProcessed = 0;
1813 uint32_t cIndirections = 0;
1814 int rc = rtFsIsoCore_InitFromUdfIcbRecursive(pCore, *pAllocDesc, pbBuf, 0, &cProcessed, &cIndirections, pVol);
1815 RTMemTmpFree(pbBuf);
1816 if (RT_SUCCESS(rc))
1817 {
1818 if (cProcessed > 0)
1819 {
1820 if (pFid)
1821 {
1822 if (pFid->fFlags & UDF_FILE_FLAGS_HIDDEN)
1823 pCore->fAttrib |= RTFS_DOS_HIDDEN;
1824 if (pFid->fFlags & UDF_FILE_FLAGS_DELETED)
1825 pCore->fAttrib = (pCore->fAttrib & ~RTFS_TYPE_MASK) | RTFS_TYPE_WHITEOUT;
1826 }
1827
1828 pCore->cRefs = 1;
1829 pCore->pVol = pVol;
1830 pCore->offDirRec = offInDir;
1831 return VINF_SUCCESS;
1832 }
1833 rc = VERR_ISOFS_NO_DIRECT_ICB_ENTRIES;
1834 }
1835
1836 /* White-out fix. Caller must be checking for UDF_FILE_FLAGS_DELETED */
1837 if ( pFid
1838 && (pFid->fFlags & UDF_FILE_FLAGS_DELETED))
1839 {
1840 pCore->fAttrib = (pCore->fAttrib & ~RTFS_TYPE_MASK) | RTFS_TYPE_WHITEOUT;
1841 return VINF_SUCCESS;
1842 }
1843 return rc;
1844 }
1845
1846 pCore->pVol = NULL;
1847 return VERR_NO_TMP_MEMORY;
1848}
1849
1850
1851/**
1852 * Simple UDF read function.
1853 *
1854 * This deals with extent mappings as well as virtual partition related block
1855 * mapping and such.
1856 *
1857 * @returns VBox status code.
1858 * @param pCore The core object to read data from.
1859 * @param offRead The offset to start reading at.
1860 * @param pvBuf The output buffer.
1861 * @param cbToRead The number of bytes to read.
1862 * @param pcbRead Where to return the number of bytes read.
1863 * @param poffPosMov Where to return the number of bytes to move the read
1864 * position. Optional. (Essentially same as pcbRead
1865 * except without the behavior change.)
1866 */
1867static int rtFsIsoCore_ReadWorker(PRTFSISOCORE pCore, uint64_t offRead, void *pvBuf, size_t cbToRead,
1868 size_t *pcbRead, size_t *poffPosMov)
1869{
1870 /*
1871 * Check for EOF.
1872 */
1873 if (offRead >= pCore->cbObject)
1874 {
1875 if (poffPosMov)
1876 *poffPosMov = 0;
1877 if (pcbRead)
1878 {
1879 *pcbRead = 0;
1880 return VINF_EOF;
1881 }
1882 return VERR_EOF;
1883 }
1884 int rcRet = VINF_SUCCESS;
1885 if ( cbToRead > pCore->cbObject
1886 || offRead + cbToRead > pCore->cbObject)
1887 {
1888 if (!pcbRead)
1889 {
1890 if (poffPosMov)
1891 *poffPosMov = 0;
1892 return VERR_EOF;
1893 }
1894 cbToRead = pCore->cbObject - offRead;
1895 rcRet = VINF_EOF;
1896 }
1897
1898 uint64_t cbActual = 0;
1899
1900 /*
1901 * Don't bother looking up the extent if we're not going to
1902 * read anything from it.
1903 */
1904 if (cbToRead > 0)
1905 {
1906 /*
1907 * Locate the first extent.
1908 */
1909 uint64_t offExtent = 0;
1910 uint32_t iExtent = 0;
1911 PCRTFSISOEXTENT pCurExtent = &pCore->FirstExtent;
1912 if (offRead < pCurExtent->cbExtent)
1913 { /* likely */ }
1914 else
1915 do
1916 {
1917 offExtent += pCurExtent->cbExtent;
1918 pCurExtent = &pCore->paExtents[iExtent++];
1919 if (iExtent >= pCore->cExtents)
1920 {
1921 memset(pvBuf, 0, cbToRead);
1922
1923 if (pcbRead)
1924 *pcbRead = cbToRead;
1925 if (poffPosMov)
1926 *poffPosMov = cbToRead;
1927 return rcRet;
1928 }
1929 } while (offExtent < offRead);
1930 Assert(offRead - offExtent < pCurExtent->cbExtent);
1931
1932 /*
1933 * Do the reading part.
1934 */
1935 PRTFSISOVOL pVol = pCore->pVol;
1936 for (;;)
1937 {
1938 uint64_t offIntoExtent = offRead - offExtent;
1939 size_t cbThisRead = pCurExtent->cbExtent - offIntoExtent;
1940 if (cbThisRead > cbToRead)
1941 cbThisRead = cbToRead;
1942
1943 if (pCurExtent->off == UINT64_MAX)
1944 RT_BZERO(pvBuf, cbThisRead);
1945 else
1946 {
1947 int rc2;
1948 if (pCurExtent->idxPart == UINT32_MAX)
1949 rc2 = RTVfsFileReadAt(pVol->hVfsBacking, pCurExtent->off + offIntoExtent, pvBuf, cbThisRead, NULL);
1950 else
1951 {
1952 Assert(pVol->enmType == RTFSISOVOLTYPE_UDF);
1953 if (pCurExtent->idxPart < pVol->Udf.VolInfo.cPartitions)
1954 {
1955 PRTFSISOVOLUDFPMAP pPart = &pVol->Udf.VolInfo.paPartitions[pCurExtent->idxPart];
1956 switch (pPart->bType)
1957 {
1958 case RTFSISO_UDF_PMAP_T_PLAIN:
1959 rc2 = RTVfsFileReadAt(pVol->hVfsBacking, pPart->offByteLocation + pCurExtent->off + offIntoExtent,
1960 pvBuf, cbThisRead, NULL);
1961 break;
1962
1963 default:
1964 AssertFailed();
1965 rc2 = VERR_ISOFS_IPE_1;
1966 break;
1967 }
1968 }
1969 else
1970 {
1971 Log(("ISO/UDF: Invalid partition index %#x (offset %#RX64), max partitions %#x; iExtent=%#x\n",
1972 pCurExtent->idxPart, pCurExtent->off + offIntoExtent, pVol->Udf.VolInfo.cPartitions, iExtent));
1973 rc2 = VERR_ISOFS_INVALID_PARTITION_INDEX;
1974 }
1975 }
1976 if (RT_FAILURE(rc2))
1977 {
1978 rcRet = rc2;
1979 break;
1980 }
1981 }
1982
1983 /*
1984 * Advance the buffer position and check if we're done (probable).
1985 */
1986 cbActual += cbThisRead;
1987 cbToRead -= cbThisRead;
1988 if (!cbToRead)
1989 break;
1990 pvBuf = (uint8_t *)pvBuf + cbThisRead;
1991
1992 /*
1993 * Advance to the next extent.
1994 */
1995 offExtent += pCurExtent->cbExtent;
1996 pCurExtent = &pCore->paExtents[iExtent++];
1997 if (iExtent >= pCore->cExtents)
1998 {
1999 memset(pvBuf, 0, cbToRead);
2000 cbActual += cbToRead;
2001 break;
2002 }
2003 }
2004 }
2005 else
2006 Assert(rcRet == VINF_SUCCESS);
2007
2008 if (poffPosMov)
2009 *poffPosMov = cbActual;
2010 if (pcbRead)
2011 *pcbRead = cbActual;
2012 return rcRet;
2013}
2014
2015
2016/**
2017 * Worker for rtFsIsoFile_QueryInfo and rtFsIsoDir_QueryInfo.
2018 */
2019static int rtFsIsoCore_QueryInfo(PRTFSISOCORE pCore, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2020{
2021 pObjInfo->cbObject = pCore->cbObject;
2022 pObjInfo->cbAllocated = RT_ALIGN_64(pCore->cbObject, pCore->pVol->cbBlock);
2023 pObjInfo->AccessTime = pCore->AccessTime;
2024 pObjInfo->ModificationTime = pCore->ModificationTime;
2025 pObjInfo->ChangeTime = pCore->ChangeTime;
2026 pObjInfo->BirthTime = pCore->BirthTime;
2027 pObjInfo->Attr.fMode = pCore->fAttrib;
2028 pObjInfo->Attr.enmAdditional = enmAddAttr;
2029
2030 switch (enmAddAttr)
2031 {
2032 case RTFSOBJATTRADD_NOTHING: RT_FALL_THRU();
2033 case RTFSOBJATTRADD_UNIX:
2034 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
2035 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
2036 pObjInfo->Attr.u.Unix.cHardlinks = 1;
2037 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
2038 pObjInfo->Attr.u.Unix.INodeId = pCore->idINode;
2039 pObjInfo->Attr.u.Unix.fFlags = 0;
2040 pObjInfo->Attr.u.Unix.GenerationId = pCore->uVersion;
2041 pObjInfo->Attr.u.Unix.Device = 0;
2042 break;
2043 case RTFSOBJATTRADD_UNIX_OWNER:
2044 pObjInfo->Attr.u.UnixOwner.uid = 0;
2045 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
2046 break;
2047 case RTFSOBJATTRADD_UNIX_GROUP:
2048 pObjInfo->Attr.u.UnixGroup.gid = 0;
2049 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
2050 break;
2051 case RTFSOBJATTRADD_EASIZE:
2052 pObjInfo->Attr.u.EASize.cb = 0;
2053 break;
2054 default:
2055 return VERR_INVALID_PARAMETER;
2056 }
2057
2058 if ( pCore->fHaveRockInfo
2059 && enmAddAttr != RTFSOBJATTRADD_NOTHING)
2060 {
2061 /** @todo Read the the rock info for this entry. */
2062 }
2063
2064 return VINF_SUCCESS;
2065}
2066
2067
2068/**
2069 * Worker for rtFsIsoFile_Close and rtFsIsoDir_Close that does common work.
2070 *
2071 * @param pCore The common shared structure.
2072 */
2073static void rtFsIsoCore_Destroy(PRTFSISOCORE pCore)
2074{
2075 if (pCore->pParentDir)
2076 rtFsIsoDirShrd_RemoveOpenChild(pCore->pParentDir, pCore);
2077 if (pCore->paExtents)
2078 {
2079 RTMemFree(pCore->paExtents);
2080 pCore->paExtents = NULL;
2081 }
2082}
2083
2084
2085/**
2086 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2087 */
2088static DECLCALLBACK(int) rtFsIsoFile_Close(void *pvThis)
2089{
2090 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2091 LogFlow(("rtFsIsoFile_Close(%p/%p)\n", pThis, pThis->pShared));
2092
2093 PRTFSISOFILESHRD pShared = pThis->pShared;
2094 pThis->pShared = NULL;
2095 if (pShared)
2096 {
2097 if (ASMAtomicDecU32(&pShared->Core.cRefs) == 0)
2098 {
2099 LogFlow(("rtFsIsoFile_Close: Destroying shared structure %p\n", pShared));
2100 rtFsIsoCore_Destroy(&pShared->Core);
2101 RTMemFree(pShared);
2102 }
2103 }
2104 return VINF_SUCCESS;
2105}
2106
2107
2108/**
2109 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2110 */
2111static DECLCALLBACK(int) rtFsIsoFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2112{
2113 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2114 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
2115}
2116
2117
2118/**
2119 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
2120 */
2121static DECLCALLBACK(int) rtFsIsoFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
2122{
2123 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2124 PRTFSISOFILESHRD pShared = pThis->pShared;
2125 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
2126 RT_NOREF(fBlocking);
2127
2128#if 1
2129 /* Apply default offset. */
2130 if (off == -1)
2131 off = pThis->offFile;
2132 else
2133 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
2134
2135 /* Do the read. */
2136 size_t offDelta = 0;
2137 int rc = rtFsIsoCore_ReadWorker(&pShared->Core, off, (uint8_t *)pSgBuf->paSegs[0].pvSeg,
2138 pSgBuf->paSegs[0].cbSeg, pcbRead, &offDelta);
2139
2140 /* Update the file position and return. */
2141 pThis->offFile = off + offDelta;
2142 return rc;
2143#else
2144
2145
2146 /*
2147 * Check for EOF.
2148 */
2149 if (off == -1)
2150 off = pThis->offFile;
2151 if ((uint64_t)off >= pShared->Core.cbObject)
2152 {
2153 if (pcbRead)
2154 {
2155 *pcbRead = 0;
2156 return VINF_EOF;
2157 }
2158 return VERR_EOF;
2159 }
2160
2161 if (pShared->Core.pVol->enmType == RTFSISOVOLTYPE_UDF)
2162 {
2163 return VERR_ISOFS_UDF_NOT_IMPLEMENTED;
2164 }
2165
2166 /*
2167 * Simple case: File has a single extent.
2168 */
2169 int rc = VINF_SUCCESS;
2170 size_t cbRead = 0;
2171 uint64_t cbFileLeft = pShared->Core.cbObject - (uint64_t)off;
2172 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
2173 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
2174 if (pShared->Core.cExtents == 1)
2175 {
2176 if (cbLeft > 0)
2177 {
2178 size_t cbToRead = cbLeft;
2179 if (cbToRead > cbFileLeft)
2180 cbToRead = (size_t)cbFileLeft;
2181 rc = RTVfsFileReadAt(pShared->Core.pVol->hVfsBacking, pShared->Core.FirstExtent.off + off, pbDst, cbToRead, NULL);
2182 if (RT_SUCCESS(rc))
2183 {
2184 off += cbToRead;
2185 pbDst += cbToRead;
2186 cbRead += cbToRead;
2187 cbFileLeft -= cbToRead;
2188 cbLeft -= cbToRead;
2189 }
2190 }
2191 }
2192 /*
2193 * Complicated case: Work the file content extent by extent.
2194 */
2195 else
2196 {
2197 return VERR_NOT_IMPLEMENTED; /** @todo multi-extent stuff . */
2198 }
2199
2200 /* Update the offset and return. */
2201 pThis->offFile = off;
2202 if (pcbRead)
2203 *pcbRead = cbRead;
2204 return VINF_SUCCESS;
2205#endif
2206}
2207
2208
2209/**
2210 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
2211 */
2212static DECLCALLBACK(int) rtFsIsoFile_Flush(void *pvThis)
2213{
2214 RT_NOREF(pvThis);
2215 return VINF_SUCCESS;
2216}
2217
2218
2219/**
2220 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
2221 */
2222static DECLCALLBACK(int) rtFsIsoFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
2223 uint32_t *pfRetEvents)
2224{
2225 NOREF(pvThis);
2226 int rc;
2227 if (fEvents != RTPOLL_EVT_ERROR)
2228 {
2229 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
2230 rc = VINF_SUCCESS;
2231 }
2232 else if (fIntr)
2233 rc = RTThreadSleep(cMillies);
2234 else
2235 {
2236 uint64_t uMsStart = RTTimeMilliTS();
2237 do
2238 rc = RTThreadSleep(cMillies);
2239 while ( rc == VERR_INTERRUPTED
2240 && !fIntr
2241 && RTTimeMilliTS() - uMsStart < cMillies);
2242 if (rc == VERR_INTERRUPTED)
2243 rc = VERR_TIMEOUT;
2244 }
2245 return rc;
2246}
2247
2248
2249/**
2250 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
2251 */
2252static DECLCALLBACK(int) rtFsIsoFile_Tell(void *pvThis, PRTFOFF poffActual)
2253{
2254 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2255 *poffActual = pThis->offFile;
2256 return VINF_SUCCESS;
2257}
2258
2259
2260/**
2261 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
2262 */
2263static DECLCALLBACK(int) rtFsIsoFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
2264{
2265 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2266 RTFOFF offNew;
2267 switch (uMethod)
2268 {
2269 case RTFILE_SEEK_BEGIN:
2270 offNew = offSeek;
2271 break;
2272 case RTFILE_SEEK_END:
2273 offNew = (RTFOFF)pThis->pShared->Core.cbObject + offSeek;
2274 break;
2275 case RTFILE_SEEK_CURRENT:
2276 offNew = (RTFOFF)pThis->offFile + offSeek;
2277 break;
2278 default:
2279 return VERR_INVALID_PARAMETER;
2280 }
2281 if (offNew >= 0)
2282 {
2283 pThis->offFile = offNew;
2284 *poffActual = offNew;
2285 return VINF_SUCCESS;
2286 }
2287 return VERR_NEGATIVE_SEEK;
2288}
2289
2290
2291/**
2292 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
2293 */
2294static DECLCALLBACK(int) rtFsIsoFile_QuerySize(void *pvThis, uint64_t *pcbFile)
2295{
2296 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2297 *pcbFile = pThis->pShared->Core.cbObject;
2298 return VINF_SUCCESS;
2299}
2300
2301
2302/**
2303 * ISO FS file operations.
2304 */
2305DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIsoFileOps =
2306{
2307 { /* Stream */
2308 { /* Obj */
2309 RTVFSOBJOPS_VERSION,
2310 RTVFSOBJTYPE_FILE,
2311 "FatFile",
2312 rtFsIsoFile_Close,
2313 rtFsIsoFile_QueryInfo,
2314 NULL,
2315 RTVFSOBJOPS_VERSION
2316 },
2317 RTVFSIOSTREAMOPS_VERSION,
2318 RTVFSIOSTREAMOPS_FEAT_NO_SG,
2319 rtFsIsoFile_Read,
2320 NULL /*Write*/,
2321 rtFsIsoFile_Flush,
2322 rtFsIsoFile_PollOne,
2323 rtFsIsoFile_Tell,
2324 NULL /*pfnSkip*/,
2325 NULL /*pfnZeroFill*/,
2326 RTVFSIOSTREAMOPS_VERSION,
2327 },
2328 RTVFSFILEOPS_VERSION,
2329 0,
2330 { /* ObjSet */
2331 RTVFSOBJSETOPS_VERSION,
2332 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
2333 NULL /*SetMode*/,
2334 NULL /*SetTimes*/,
2335 NULL /*SetOwner*/,
2336 RTVFSOBJSETOPS_VERSION
2337 },
2338 rtFsIsoFile_Seek,
2339 rtFsIsoFile_QuerySize,
2340 NULL /*SetSize*/,
2341 NULL /*QueryMaxSize*/,
2342 RTVFSFILEOPS_VERSION
2343};
2344
2345
2346/**
2347 * Instantiates a new file, from ISO 9660 info.
2348 *
2349 * @returns IPRT status code.
2350 * @param pThis The ISO volume instance.
2351 * @param pParentDir The parent directory (shared part).
2352 * @param pDirRec The directory record.
2353 * @param cDirRecs Number of directory records if more than one.
2354 * @param offDirRec The byte offset of the directory record.
2355 * @param offEntryInDir The byte offset of the directory entry in the parent
2356 * directory.
2357 * @param fOpen RTFILE_O_XXX flags.
2358 * @param uVersion The file version number (since the caller already
2359 * parsed the filename, we don't want to repeat the
2360 * effort here).
2361 * @param pRockInfo Optional rock ridge info for the file.
2362 * @param phVfsFile Where to return the file handle.
2363 */
2364static int rtFsIsoFile_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
2365 uint64_t offDirRec, uint64_t fOpen, uint32_t uVersion, PCRTFSISOROCKINFO pRockInfo,
2366 PRTVFSFILE phVfsFile)
2367{
2368 AssertPtr(pParentDir);
2369
2370 /*
2371 * Create a VFS object.
2372 */
2373 PRTFSISOFILEOBJ pNewFile;
2374 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2375 phVfsFile, (void **)&pNewFile);
2376 if (RT_SUCCESS(rc))
2377 {
2378 /*
2379 * Look for existing shared object, create a new one if necessary.
2380 */
2381 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
2382 if (pShared)
2383 {
2384 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2385 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2386 pNewFile->offFile = 0;
2387 pNewFile->pShared = pShared;
2388 return VINF_SUCCESS;
2389 }
2390
2391 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2392 if (pShared)
2393 {
2394 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, uVersion, pRockInfo, pThis);
2395 if (RT_SUCCESS(rc))
2396 {
2397 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2398 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2399 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2400 pNewFile->offFile = 0;
2401 pNewFile->pShared = pShared;
2402 return VINF_SUCCESS;
2403 }
2404 RTMemFree(pShared);
2405 }
2406 else
2407 rc = VERR_NO_MEMORY;
2408
2409 /* Destroy the file object. */
2410 pNewFile->offFile = 0;
2411 pNewFile->pShared = NULL;
2412 RTVfsFileRelease(*phVfsFile);
2413 }
2414 *phVfsFile = NIL_RTVFSFILE;
2415 return rc;
2416}
2417
2418
2419/**
2420 * Instantiates a new file, from UDF info.
2421 *
2422 * @returns IPRT status code.
2423 * @param pThis The ISO volume instance.
2424 * @param pParentDir The parent directory (shared part).
2425 * @param pFid The file ID descriptor. (Points to parent directory
2426 * content.)
2427 * @param fOpen RTFILE_O_XXX flags.
2428 * @param phVfsFile Where to return the file handle.
2429 */
2430static int rtFsIsoFile_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid,
2431 uint64_t fOpen, PRTVFSFILE phVfsFile)
2432{
2433 AssertPtr(pParentDir);
2434 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
2435 Assert(offInDir < pParentDir->cbDir);
2436 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DELETED));
2437 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY));
2438
2439 /*
2440 * Create a VFS object.
2441 */
2442 PRTFSISOFILEOBJ pNewFile;
2443 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2444 phVfsFile, (void **)&pNewFile);
2445 if (RT_SUCCESS(rc))
2446 {
2447 /*
2448 * Look for existing shared object. Make sure it's a file.
2449 */
2450 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
2451 if (pShared)
2452 {
2453 if (!RTFS_IS_FILE(pShared->Core.fAttrib))
2454 {
2455 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2456 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2457 pNewFile->offFile = 0;
2458 pNewFile->pShared = pShared;
2459 return VINF_SUCCESS;
2460 }
2461 }
2462 /*
2463 * Create a shared object for this alleged file.
2464 */
2465 else
2466 {
2467 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2468 if (pShared)
2469 {
2470 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, &pFid->Icb, pFid, offInDir, pThis);
2471 if (RT_SUCCESS(rc))
2472 {
2473 if (RTFS_IS_FILE(pShared->Core.fAttrib))
2474 {
2475 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2476
2477 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2478 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2479 pNewFile->offFile = 0;
2480 pNewFile->pShared = pShared;
2481 return VINF_SUCCESS;
2482 }
2483 rtFsIsoCore_Destroy(&pShared->Core);
2484 }
2485 RTMemFree(pShared);
2486 }
2487 else
2488 rc = VERR_NO_MEMORY;
2489 }
2490
2491 /* Destroy the file object. */
2492 pNewFile->offFile = 0;
2493 pNewFile->pShared = NULL;
2494 RTVfsFileRelease(*phVfsFile);
2495 }
2496 *phVfsFile = NIL_RTVFSFILE;
2497 return rc;
2498}
2499
2500
2501/**
2502 * Looks up the shared structure for a child.
2503 *
2504 * @returns Referenced pointer to the shared structure, NULL if not found.
2505 * @param pThis The directory.
2506 * @param offDirRec The directory record offset of the child.
2507 */
2508static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec)
2509{
2510 PRTFSISOCORE pCur;
2511 RTListForEach(&pThis->OpenChildren, pCur, RTFSISOCORE, Entry)
2512 {
2513 if (pCur->offDirRec == offDirRec)
2514 {
2515 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
2516 Assert(cRefs > 1); RT_NOREF(cRefs);
2517 return pCur;
2518 }
2519 }
2520 return NULL;
2521}
2522
2523
2524#ifdef RT_STRICT
2525/**
2526 * Checks if @a pNext is an extent of @a pFirst.
2527 *
2528 * @returns true if @a pNext is the next extent, false if not
2529 * @param pFirst The directory record describing the first or the
2530 * previous extent.
2531 * @param pNext The directory record alleged to be the next extent.
2532 */
2533DECLINLINE(bool) rtFsIsoDir_Is9660DirRecNextExtent(PCISO9660DIRREC pFirst, PCISO9660DIRREC pNext)
2534{
2535 if (RT_LIKELY(pNext->bFileIdLength == pFirst->bFileIdLength))
2536 {
2537 if (RT_LIKELY((pNext->fFileFlags | ISO9660_FILE_FLAGS_MULTI_EXTENT) == pFirst->fFileFlags))
2538 {
2539 if (RT_LIKELY(memcmp(pNext->achFileId, pFirst->achFileId, pNext->bFileIdLength) == 0))
2540 return true;
2541 }
2542 }
2543 return false;
2544}
2545#endif /* RT_STRICT */
2546
2547
2548/**
2549 * Parses rock ridge information if present in the directory entry.
2550 *
2551 * @param pVol The volume structure.
2552 * @param pParseInfo Parse info and output.
2553 * @param pbSys The system area of the directory record.
2554 * @param cbSys The number of bytes present in the sys area.
2555 * @param fIsFirstDirRec Set if this is the '.' directory entry in the
2556 * root directory. (Some entries applies only to
2557 * it.)
2558 * @param fContinuationRecord Set if we're processing a continuation record in
2559 * living in the abRockBuf.
2560 */
2561static void rtFsIsoDirShrd_ParseRockRidgeData(PRTFSISOVOL pVol, PRTFSISOROCKINFO pParseInfo, uint8_t const *pbSys,
2562 size_t cbSys, bool fIsFirstDirRec, bool fContinuationRecord)
2563{
2564 while (cbSys >= 4)
2565 {
2566 /*
2567 * Check header length and advance the sys variables.
2568 */
2569 PCISO9660SUSPUNION pUnion = (PCISO9660SUSPUNION)pbSys;
2570 if ( pUnion->Hdr.cbEntry > cbSys
2571 || pUnion->Hdr.cbEntry < sizeof(pUnion->Hdr))
2572 {
2573 Log4(("rtFsIsoDir_ParseRockRidgeData: cbEntry=%#x cbSys=%#x (%#x %#x)\n",
2574 pUnion->Hdr.cbEntry, cbSys, pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
2575 break;
2576 }
2577 pbSys += pUnion->Hdr.cbEntry;
2578 cbSys -= pUnion->Hdr.cbEntry;
2579
2580 /*
2581 * Process fields.
2582 */
2583 uint16_t const uSig = SUSP_MAKE_SIG(pUnion->Hdr.bSig1, pUnion->Hdr.bSig2);
2584 switch (uSig)
2585 {
2586 /*
2587 * System use sharing protocol entries.
2588 */
2589 case SUSP_MAKE_SIG(ISO9660SUSPCE_SIG1, ISO9660SUSPCE_SIG2):
2590 {
2591 if (RT_BE2H_U32(pUnion->CE.offBlock.be) != RT_LE2H_U32(pUnion->CE.offBlock.le))
2592 Log4(("rtFsIsoDir_ParseRockRidgeData: Invalid CE offBlock field: be=%#x vs le=%#x\n",
2593 RT_BE2H_U32(pUnion->CE.offBlock.be), RT_LE2H_U32(pUnion->CE.offBlock.le)));
2594 else if (RT_BE2H_U32(pUnion->CE.cbData.be) != RT_LE2H_U32(pUnion->CE.cbData.le))
2595 Log4(("rtFsIsoDir_ParseRockRidgeData: Invalid CE cbData field: be=%#x vs le=%#x\n",
2596 RT_BE2H_U32(pUnion->CE.cbData.be), RT_LE2H_U32(pUnion->CE.cbData.le)));
2597 else if (RT_BE2H_U32(pUnion->CE.offData.be) != RT_LE2H_U32(pUnion->CE.offData.le))
2598 Log4(("rtFsIsoDir_ParseRockRidgeData: Invalid CE offData field: be=%#x vs le=%#x\n",
2599 RT_BE2H_U32(pUnion->CE.offData.be), RT_LE2H_U32(pUnion->CE.offData.le)));
2600 else if (!fContinuationRecord)
2601 {
2602 uint64_t offData = ISO9660_GET_ENDIAN(&pUnion->CE.offBlock) * (uint64_t)ISO9660_SECTOR_SIZE;
2603 offData += ISO9660_GET_ENDIAN(&pUnion->CE.offData);
2604 uint32_t cbData = ISO9660_GET_ENDIAN(&pUnion->CE.cbData);
2605 if (cbData <= sizeof(pVol->abRockBuf) - (uint32_t)(offData & ISO9660_SECTOR_OFFSET_MASK))
2606 {
2607 RTCritSectEnter(&pVol->RockBufLock);
2608
2609 AssertCompile(sizeof(pVol->abRockBuf) == ISO9660_SECTOR_SIZE);
2610 uint64_t offDataBlock = offData & ~(uint64_t)ISO9660_SECTOR_OFFSET_MASK;
2611 if (pVol->offRockBuf == offDataBlock)
2612 rtFsIsoDirShrd_ParseRockRidgeData(pVol, pParseInfo,
2613 &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
2614 cbData, fIsFirstDirRec, true /*fContinuationRecord*/);
2615 else
2616 {
2617 int rc = RTVfsFileReadAt(pVol->hVfsBacking, offDataBlock,
2618 pVol->abRockBuf, sizeof(pVol->abRockBuf), NULL);
2619 if (RT_SUCCESS(rc))
2620 rtFsIsoDirShrd_ParseRockRidgeData(pVol, pParseInfo,
2621 &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
2622 cbData, fIsFirstDirRec, true /*fContinuationRecord*/);
2623 else
2624 Log4(("rtFsIsoDir_ParseRockRidgeData: Error reading continuation record at %#RX64: %Rrc\n",
2625 offDataBlock, rc));
2626 }
2627
2628 RTCritSectLeave(&pVol->RockBufLock);
2629 }
2630 else
2631 Log4(("rtFsIsoDir_ParseRockRidgeData: continuation record isn't within a sector! offData=%#RX64 cbData=%#RX32\n",
2632 cbData, offData));
2633 }
2634 else
2635 Log4(("rtFsIsoDir_ParseRockRidgeData: nested continuation record!\n"));
2636 break;
2637 }
2638
2639 case SUSP_MAKE_SIG(ISO9660SUSPSP_SIG1, ISO9660SUSPSP_SIG2): /* SP */
2640 if ( pUnion->Hdr.cbEntry != ISO9660SUSPSP_LEN
2641 || pUnion->Hdr.bVersion != ISO9660SUSPSP_VER
2642 || pUnion->SP.bCheck1 != ISO9660SUSPSP_CHECK1
2643 || pUnion->SP.bCheck2 != ISO9660SUSPSP_CHECK2
2644 || pUnion->SP.cbSkip > UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
2645 Log4(("rtFsIsoDir_ParseRockRidgeData: 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",
2646 pUnion->Hdr.cbEntry, ISO9660SUSPSP_LEN, pUnion->Hdr.bVersion, ISO9660SUSPSP_VER,
2647 pUnion->SP.bCheck1, ISO9660SUSPSP_CHECK1, pUnion->SP.bCheck2, ISO9660SUSPSP_CHECK2,
2648 pUnion->SP.cbSkip, UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]) ));
2649 else if (!fIsFirstDirRec)
2650 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorining 'SP' entry in non-root directory record\n"));
2651 else if (pParseInfo->fSuspSeenSP)
2652 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorining additional 'SP' entry\n"));
2653 else
2654 {
2655 pVol->offSuspSkip = pUnion->SP.cbSkip;
2656 if (pUnion->SP.cbSkip != 0)
2657 Log4(("rtFsIsoDir_ParseRockRidgeData: SP: cbSkip=%#x\n", pUnion->SP.cbSkip));
2658 }
2659 break;
2660
2661 case SUSP_MAKE_SIG(ISO9660SUSPER_SIG1, ISO9660SUSPER_SIG2): /* ER */
2662 if ( pUnion->Hdr.cbEntry > RT_UOFFSETOF(ISO9660SUSPER, achPayload) + (uint32_t)pUnion->ER.cchIdentifier
2663 + (uint32_t)pUnion->ER.cchDescription + (uint32_t)pUnion->ER.cchSource
2664 || pUnion->Hdr.bVersion != ISO9660SUSPER_VER)
2665 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'ER' entry: cbEntry=%#x bVersion=%#x (vs %#x) cchIdentifier=%#x cchDescription=%#x cchSource=%#x\n",
2666 pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion, ISO9660SUSPER_VER, pUnion->ER.cchIdentifier,
2667 pUnion->ER.cchDescription, pUnion->ER.cchSource));
2668 else if (!fIsFirstDirRec)
2669 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorining 'ER' entry in non-root directory record\n"));
2670 else if ( pUnion->ER.bVersion == 1 /* RRIP detection */
2671 && ( (pUnion->ER.cchIdentifier >= 4 && strncmp(pUnion->ER.achPayload, ISO9660_RRIP_ID, 4 /*RRIP*/) == 0)
2672 || (pUnion->ER.cchIdentifier >= 10 && strncmp(pUnion->ER.achPayload, RT_STR_TUPLE(ISO9660_RRIP_1_12_ID)) == 0) ))
2673 {
2674 Log4(("rtFsIsoDir_ParseRockRidgeData: Rock Ridge 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
2675 pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
2676 pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
2677 pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
2678 pVol->fHaveRock = true;
2679 pParseInfo->cRockEntries++;
2680 }
2681 else
2682 Log4(("rtFsIsoDir_ParseRockRidgeData: Unknown extension in 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
2683 pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
2684 pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
2685 pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
2686 break;
2687
2688 case SUSP_MAKE_SIG(ISO9660SUSPPD_SIG1, ISO9660SUSPPD_SIG2): /* PD - ignored */
2689 case SUSP_MAKE_SIG(ISO9660SUSPST_SIG1, ISO9660SUSPST_SIG2): /* ST - ignore for now */
2690 case SUSP_MAKE_SIG(ISO9660SUSPES_SIG1, ISO9660SUSPES_SIG2): /* ES - ignore for now */
2691 break;
2692
2693 /*
2694 * Rock ridge interchange protocol entries.
2695 */
2696 case SUSP_MAKE_SIG(ISO9660RRIPRR_SIG1, ISO9660RRIPRR_SIG2): /* RR */
2697 if ( pUnion->RR.Hdr.cbEntry != ISO9660RRIPRR_LEN
2698 || pUnion->RR.Hdr.bVersion != ISO9660RRIPRR_VER)
2699 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'RR' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
2700 pUnion->RR.Hdr.cbEntry, ISO9660RRIPRR_LEN, pUnion->RR.Hdr.bVersion, ISO9660RRIPRR_VER, pUnion->RR.fFlags));
2701 else
2702 pParseInfo->cRockEntries++; /* otherwise ignored */
2703 break;
2704
2705 case SUSP_MAKE_SIG(ISO9660RRIPPX_SIG1, ISO9660RRIPPX_SIG2): /* PX */
2706 if ( ( pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN
2707 && pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN_NO_INODE)
2708 || pUnion->PX.Hdr.bVersion != ISO9660RRIPPX_VER
2709 || RT_BE2H_U32(pUnion->PX.fMode.be) != RT_LE2H_U32(pUnion->PX.fMode.le)
2710 || RT_BE2H_U32(pUnion->PX.cHardlinks.be) != RT_LE2H_U32(pUnion->PX.cHardlinks.le)
2711 || RT_BE2H_U32(pUnion->PX.uid.be) != RT_LE2H_U32(pUnion->PX.uid.le)
2712 || RT_BE2H_U32(pUnion->PX.gid.be) != RT_LE2H_U32(pUnion->PX.gid.le)
2713 || ( pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN
2714 && RT_BE2H_U32(pUnion->PX.INode.be) != RT_LE2H_U32(pUnion->PX.INode.le)) )
2715 Log4(("rtFsIsoDir_ParseRockRidgeData: 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",
2716 pUnion->PX.Hdr.cbEntry, ISO9660RRIPPX_LEN, ISO9660RRIPPX_LEN_NO_INODE,
2717 pUnion->PX.Hdr.bVersion, ISO9660RRIPPX_VER,
2718 RT_BE2H_U32(pUnion->PX.fMode.be), RT_LE2H_U32(pUnion->PX.fMode.le),
2719 RT_BE2H_U32(pUnion->PX.cHardlinks.be), RT_LE2H_U32(pUnion->PX.cHardlinks.le),
2720 RT_BE2H_U32(pUnion->PX.uid.be), RT_LE2H_U32(pUnion->PX.uid.le),
2721 RT_BE2H_U32(pUnion->PX.gid.be), RT_LE2H_U32(pUnion->PX.gid.le),
2722 pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_BE2H_U32(pUnion->PX.INode.be) : 0,
2723 pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_LE2H_U32(pUnion->PX.INode.le) : 0 ));
2724 else
2725 {
2726 if ( RTFS_IS_DIRECTORY(ISO9660_GET_ENDIAN(&pUnion->PX.fMode))
2727 == RTFS_IS_DIRECTORY(pParseInfo->Info.Attr.fMode))
2728 pParseInfo->Info.Attr.fMode = ISO9660_GET_ENDIAN(&pUnion->PX.fMode);
2729 else
2730 Log4(("rtFsIsoDir_ParseRockRidgeData: 'PX' entry changes directory-ness: fMode=%#x, existing %#x; ignored\n",
2731 ISO9660_GET_ENDIAN(&pUnion->PX.fMode), pParseInfo->Info.Attr.fMode));
2732 pParseInfo->Info.Attr.u.Unix.cHardlinks = ISO9660_GET_ENDIAN(&pUnion->PX.cHardlinks);
2733 pParseInfo->Info.Attr.u.Unix.uid = ISO9660_GET_ENDIAN(&pUnion->PX.uid);
2734 pParseInfo->Info.Attr.u.Unix.gid = ISO9660_GET_ENDIAN(&pUnion->PX.gid);
2735 /* ignore inode */
2736 pParseInfo->cRockEntries++;
2737 }
2738 break;
2739
2740 case SUSP_MAKE_SIG(ISO9660RRIPPN_SIG1, ISO9660RRIPPN_SIG2): /* PN */
2741 if ( pUnion->PN.Hdr.cbEntry != ISO9660RRIPPN_LEN
2742 || pUnion->PN.Hdr.bVersion != ISO9660RRIPPN_VER
2743 || RT_BE2H_U32(pUnion->PN.Major.be) != RT_LE2H_U32(pUnion->PN.Major.le)
2744 || RT_BE2H_U32(pUnion->PN.Minor.be) != RT_LE2H_U32(pUnion->PN.Minor.le))
2745 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'PN' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) Major=%#x/%#x Minor=%#x/%#x\n",
2746 pUnion->PN.Hdr.cbEntry, ISO9660RRIPPN_LEN, pUnion->PN.Hdr.bVersion, ISO9660RRIPPN_VER,
2747 RT_BE2H_U32(pUnion->PN.Major.be), RT_LE2H_U32(pUnion->PN.Major.le),
2748 RT_BE2H_U32(pUnion->PN.Minor.be), RT_LE2H_U32(pUnion->PN.Minor.le) ));
2749 else if (RTFS_IS_DIRECTORY(pParseInfo->Info.Attr.fMode))
2750 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorning 'PN' entry for directory (%#x/%#x)\n",
2751 ISO9660_GET_ENDIAN(&pUnion->PN.Major), ISO9660_GET_ENDIAN(&pUnion->PN.Minor) ));
2752 else
2753 {
2754 pParseInfo->Info.Attr.u.Unix.Device = RTDEV_MAKE(ISO9660_GET_ENDIAN(&pUnion->PN.Major),
2755 ISO9660_GET_ENDIAN(&pUnion->PN.Minor));
2756 pParseInfo->cRockEntries++;
2757 }
2758 break;
2759
2760 case SUSP_MAKE_SIG(ISO9660RRIPTF_SIG1, ISO9660RRIPTF_SIG2): /* TF */
2761 if ( pUnion->TF.Hdr.bVersion != ISO9660RRIPTF_VER
2762 || pUnion->TF.Hdr.cbEntry < Iso9660RripTfCalcLength(pUnion->TF.fFlags))
2763 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'TF' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
2764 pUnion->TF.Hdr.cbEntry, Iso9660RripTfCalcLength(pUnion->TF.fFlags),
2765 pUnion->TF.Hdr.bVersion, ISO9660RRIPTF_VER, RT_BE2H_U32(pUnion->TF.fFlags) ));
2766 else if (!(pUnion->TF.fFlags & ISO9660RRIPTF_F_LONG_FORM))
2767 {
2768 PCISO9660RECTIMESTAMP pTimestamp = (PCISO9660RECTIMESTAMP)&pUnion->TF.abPayload[0];
2769 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
2770 {
2771 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.BirthTime, pTimestamp);
2772 pTimestamp++;
2773 }
2774 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
2775 {
2776 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.ModificationTime, pTimestamp);
2777 pTimestamp++;
2778 }
2779 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
2780 {
2781 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.AccessTime, pTimestamp);
2782 pTimestamp++;
2783 }
2784 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
2785 {
2786 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.ChangeTime, pTimestamp);
2787 pTimestamp++;
2788 }
2789 pParseInfo->cRockEntries++;
2790 }
2791 else
2792 {
2793 PCISO9660TIMESTAMP pTimestamp = (PCISO9660TIMESTAMP)&pUnion->TF.abPayload[0];
2794 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
2795 {
2796 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.BirthTime, pTimestamp);
2797 pTimestamp++;
2798 }
2799 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
2800 {
2801 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.ModificationTime, pTimestamp);
2802 pTimestamp++;
2803 }
2804 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
2805 {
2806 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.AccessTime, pTimestamp);
2807 pTimestamp++;
2808 }
2809 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
2810 {
2811 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.ChangeTime, pTimestamp);
2812 pTimestamp++;
2813 }
2814 pParseInfo->cRockEntries++;
2815 }
2816 break;
2817
2818 case SUSP_MAKE_SIG(ISO9660RRIPSF_SIG1, ISO9660RRIPSF_SIG2): /* SF */
2819 Log4(("rtFsIsoDir_ParseRockRidgeData: Sparse file support not yet implemented!\n"));
2820 break;
2821
2822 case SUSP_MAKE_SIG(ISO9660RRIPSL_SIG1, ISO9660RRIPSL_SIG2): /* SL */
2823 if ( pUnion->SL.Hdr.bVersion != ISO9660RRIPSL_VER
2824 || pUnion->SL.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2])
2825 || (pUnion->SL.fFlags & ~ISO9660RRIP_SL_F_CONTINUE)
2826 || (pUnion->SL.abComponents[0] & ISO9660RRIP_SL_C_RESERVED_MASK) )
2827 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x comp[0].fFlags=%#x\n",
2828 pUnion->SL.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2]),
2829 pUnion->SL.Hdr.bVersion, ISO9660RRIPSL_VER, pUnion->SL.fFlags, pUnion->SL.abComponents[0]));
2830 else if (pParseInfo->fSeenLastSL)
2831 Log4(("rtFsIsoDir_ParseRockRidgeData: Unexpected 'SL!' entry\n"));
2832 else
2833 {
2834 pParseInfo->cRockEntries++;
2835 pParseInfo->fSeenLastSL = !(pUnion->SL.fFlags & ISO9660RRIP_SL_F_CONTINUE); /* used in loop */
2836
2837 size_t offDst = pParseInfo->cchLinkTarget;
2838 uint8_t const *pbSrc = &pUnion->SL.abComponents[0];
2839 uint8_t cbSrcLeft = pUnion->SL.Hdr.cbEntry - RT_UOFFSETOF(ISO9660RRIPSL, abComponents);
2840 while (cbSrcLeft >= 2)
2841 {
2842 uint8_t const fFlags = pbSrc[0];
2843 uint8_t cchCopy = pbSrc[1];
2844 uint8_t const cbSkip = cchCopy + 2;
2845 if (cbSkip > cbSrcLeft)
2846 {
2847 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: component flags=%#x, component length+2=%#x vs %#x left\n",
2848 fFlags, cbSkip, cbSrcLeft));
2849 break;
2850 }
2851
2852 const char *pszCopy;
2853 switch (fFlags & ~ISO9660RRIP_SL_C_CONTINUE)
2854 {
2855 case 0:
2856 pszCopy = (const char *)&pbSrc[2];
2857 break;
2858
2859 case ISO9660RRIP_SL_C_CURRENT:
2860 if (cchCopy != 0)
2861 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: CURRENT + %u bytes, ignoring bytes\n", cchCopy));
2862 pszCopy = ".";
2863 cchCopy = 1;
2864 break;
2865
2866 case ISO9660RRIP_SL_C_PARENT:
2867 if (cchCopy != 0)
2868 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: PARENT + %u bytes, ignoring bytes\n", cchCopy));
2869 pszCopy = "..";
2870 cchCopy = 2;
2871 break;
2872
2873 case ISO9660RRIP_SL_C_ROOT:
2874 if (cchCopy != 0)
2875 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: ROOT + %u bytes, ignoring bytes\n", cchCopy));
2876 pszCopy = "/";
2877 cchCopy = 1;
2878 break;
2879
2880 default:
2881 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: component flags=%#x (bad), component length=%#x vs %#x left\n",
2882 fFlags, cchCopy, cbSrcLeft));
2883 pszCopy = NULL;
2884 cchCopy = 0;
2885 break;
2886 }
2887
2888 if (offDst + cchCopy < sizeof(pParseInfo->szLinkTarget))
2889 {
2890 memcpy(&pParseInfo->szLinkTarget[offDst], pszCopy, cchCopy);
2891 offDst += cchCopy;
2892 }
2893 else
2894 {
2895 Log4(("rtFsIsoDir_ParseRockRidgeData: 'SL' constructs a too long target! '%.*s%.*s'\n",
2896 offDst, pParseInfo->szLinkTarget, cchCopy, pszCopy));
2897 memcpy(&pParseInfo->szLinkTarget[offDst], pszCopy, sizeof(pParseInfo->szLinkTarget) - offDst - 1);
2898 offDst = sizeof(pParseInfo->szLinkTarget) - 1;
2899 pParseInfo->fOverflowSL = true;
2900 break;
2901 }
2902
2903 /* Advance */
2904 pbSrc += cbSkip;
2905 cbSrcLeft -= cbSkip;
2906
2907 /* Append slash if appropriate. */
2908 if ( !(fFlags & ISO9660RRIP_SL_C_CONTINUE)
2909 && (cbSrcLeft >= 2 || !pParseInfo->fSeenLastSL) )
2910 {
2911 if (offDst + 1 < sizeof(pParseInfo->szLinkTarget))
2912 pParseInfo->szLinkTarget[offDst++] = '/';
2913 else
2914 {
2915 Log4(("rtFsIsoDir_ParseRockRidgeData: 'SL' constructs a too long target! '%.*s/'\n",
2916 offDst, pParseInfo->szLinkTarget));
2917 pParseInfo->fOverflowSL = true;
2918 break;
2919 }
2920 }
2921 }
2922 Assert(offDst < sizeof(pParseInfo->szLinkTarget));
2923 pParseInfo->szLinkTarget[offDst] = '\0';
2924 pParseInfo->cchLinkTarget = (uint16_t)offDst;
2925 }
2926 break;
2927
2928 case SUSP_MAKE_SIG(ISO9660RRIPNM_SIG1, ISO9660RRIPNM_SIG2): /* NM */
2929 if ( pUnion->NM.Hdr.bVersion != ISO9660RRIPNM_VER
2930 || pUnion->NM.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPNM, achName)
2931 || (pUnion->NM.fFlags & ISO9660RRIP_NM_F_RESERVED_MASK) )
2932 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'NM' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x %.*Rhxs\n",
2933 pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName),
2934 pUnion->NM.Hdr.bVersion, ISO9660RRIPNM_VER, pUnion->NM.fFlags,
2935 pUnion->NM.Hdr.cbEntry - RT_MIN(pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName)),
2936 &pUnion->NM.achName[0] ));
2937 else if (pParseInfo->fSeenLastNM)
2938 Log4(("rtFsIsoDir_ParseRockRidgeData: Unexpected 'NM' entry!\n"));
2939 else
2940 {
2941 pParseInfo->cRockEntries++;
2942 pParseInfo->fSeenLastNM = !(pUnion->NM.fFlags & ISO9660RRIP_NM_F_CONTINUE);
2943
2944 uint8_t const cchName = pUnion->NM.Hdr.cbEntry - (uint8_t)RT_UOFFSETOF(ISO9660RRIPNM, achName);
2945 if (pUnion->NM.fFlags & (ISO9660RRIP_NM_F_CURRENT | ISO9660RRIP_NM_F_PARENT))
2946 {
2947 if (cchName == 0 && pParseInfo->szName[0] == '\0')
2948 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignoring 'NM' entry for '.' and '..'\n"));
2949 else
2950 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignoring malformed 'NM' using '.' or '..': fFlags=%#x cchName=%#x %.*Rhxs; szRockNameBuf='%s'\n",
2951 pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName, pParseInfo->szName));
2952 pParseInfo->szName[0] = '\0';
2953 pParseInfo->cchName = 0;
2954 pParseInfo->fSeenLastNM = true;
2955 }
2956 else
2957 {
2958 size_t offDst = pParseInfo->cchName;
2959 if (offDst + cchName < sizeof(pParseInfo->szName))
2960 {
2961 memcpy(&pParseInfo->szName[offDst], pUnion->NM.achName, cchName);
2962 offDst += cchName;
2963 pParseInfo->szName[offDst] = '\0';
2964 pParseInfo->cchName = (uint16_t)offDst;
2965 }
2966 else
2967 {
2968 Log4(("rtFsIsoDir_ParseRockRidgeData: 'NM' constructs a too long name, ignoring it all: '%s%.*s'\n",
2969 pParseInfo->szName, cchName, pUnion->NM.achName));
2970 pParseInfo->szName[0] = '\0';
2971 pParseInfo->cchName = 0;
2972 pParseInfo->fSeenLastNM = true;
2973 }
2974 }
2975 }
2976 break;
2977
2978 case SUSP_MAKE_SIG(ISO9660RRIPCL_SIG1, ISO9660RRIPCL_SIG2): /* CL - just warn for now. */
2979 case SUSP_MAKE_SIG(ISO9660RRIPPL_SIG1, ISO9660RRIPPL_SIG2): /* PL - just warn for now. */
2980 case SUSP_MAKE_SIG(ISO9660RRIPRE_SIG1, ISO9660RRIPRE_SIG2): /* RE - just warn for now. */
2981 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorning directory relocation entry '%c%c'!\n", pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
2982 break;
2983
2984 default:
2985 Log4(("rtFsIsoDir_ParseRockRidgeData: Unknown SUSP entry: %#x %#x, %#x bytes, v%u\n",
2986 pUnion->Hdr.bSig1, pUnion->Hdr.bSig2, pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion));
2987 break;
2988 }
2989 }
2990
2991 /*
2992 * Set the valid flag if we found anything of interest.
2993 */
2994 if (pParseInfo->cRockEntries > 1)
2995 pParseInfo->fValid = true;
2996}
2997
2998
2999/**
3000 * Initializes the rock info structure with info from the standard ISO-9660
3001 * directory record.
3002 *
3003 * @param pRockInfo The structure to initialize.
3004 * @param pDirRec The directory record to take basic data from.
3005 */
3006static void rtFsIsoDirShrd_InitRockInfo(PRTFSISOROCKINFO pRockInfo, PCISO9660DIRREC pDirRec)
3007{
3008 pRockInfo->fValid = false;
3009 pRockInfo->fSuspSeenSP = false;
3010 pRockInfo->fSeenLastNM = false;
3011 pRockInfo->fSeenLastSL = false;
3012 pRockInfo->fOverflowSL = false;
3013 pRockInfo->cRockEntries = 0;
3014 pRockInfo->cchName = 0;
3015 pRockInfo->cchLinkTarget = 0;
3016 pRockInfo->szName[0] = '\0';
3017 pRockInfo->szName[sizeof(pRockInfo->szName) - 1] = '\0';
3018 pRockInfo->szLinkTarget[0] = '\0';
3019 pRockInfo->szLinkTarget[sizeof(pRockInfo->szLinkTarget) - 1] = '\0';
3020 pRockInfo->Info.cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
3021 pRockInfo->Info.cbAllocated = pRockInfo->Info.cbObject;
3022 rtFsIso9660DateTime2TimeSpec(&pRockInfo->Info.AccessTime, &pDirRec->RecTime);
3023 pRockInfo->Info.ModificationTime = pRockInfo->Info.AccessTime;
3024 pRockInfo->Info.ChangeTime = pRockInfo->Info.AccessTime;
3025 pRockInfo->Info.BirthTime = pRockInfo->Info.AccessTime;
3026 pRockInfo->Info.Attr.fMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
3027 ? RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | 0555
3028 : RTFS_TYPE_FILE | RTFS_DOS_ARCHIVED | 0444;
3029 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
3030 pRockInfo->Info.Attr.fMode |= RTFS_DOS_HIDDEN;
3031 pRockInfo->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
3032 pRockInfo->Info.Attr.u.Unix.uid = NIL_RTUID;
3033 pRockInfo->Info.Attr.u.Unix.gid = NIL_RTGID;
3034 pRockInfo->Info.Attr.u.Unix.cHardlinks = 1;
3035 pRockInfo->Info.Attr.u.Unix.INodeIdDevice = 0;
3036 pRockInfo->Info.Attr.u.Unix.INodeId = 0;
3037 pRockInfo->Info.Attr.u.Unix.fFlags = 0;
3038 pRockInfo->Info.Attr.u.Unix.GenerationId = 0;
3039 pRockInfo->Info.Attr.u.Unix.Device = 0;
3040}
3041
3042
3043static void rtFsIsoDirShrd_ParseRockForDirRec(PRTFSISODIRSHRD pThis, PCISO9660DIRREC pDirRec, PRTFSISOROCKINFO pRockInfo)
3044{
3045 rtFsIsoDirShrd_InitRockInfo(pRockInfo, pDirRec); /* Always! */
3046
3047 PRTFSISOVOL const pVol = pThis->Core.pVol;
3048 uint8_t cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
3049 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
3050 uint8_t const *pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
3051 if (cbSys >= 4 + pVol->offSuspSkip)
3052 {
3053 pbSys += pVol->offSuspSkip;
3054 cbSys -= pVol->offSuspSkip;
3055 rtFsIsoDirShrd_ParseRockRidgeData(pVol, pRockInfo, pbSys, cbSys,
3056 false /*fIsFirstDirRec*/, false /*fContinuationRecord*/);
3057 }
3058}
3059
3060
3061static void rtFsIsoDirShrd_ParseRockForRoot(PRTFSISODIRSHRD pThis, PCISO9660DIRREC pDirRec)
3062{
3063 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
3064 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
3065 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
3066 if (cbSys >= 4)
3067 {
3068 RTFSISOROCKINFO RockInfo;
3069 rtFsIsoDirShrd_InitRockInfo(&RockInfo, pDirRec);
3070 rtFsIsoDirShrd_ParseRockRidgeData(pThis->Core.pVol, &RockInfo, pbSys, cbSys,
3071 true /*fIsFirstDirRec*/, false /*fContinuationRecord*/);
3072 if (RockInfo.fValid)
3073 {
3074 pThis->Core.fHaveRockInfo = true;
3075 pThis->Core.BirthTime = RockInfo.Info.BirthTime;
3076 pThis->Core.ChangeTime = RockInfo.Info.ChangeTime;
3077 pThis->Core.AccessTime = RockInfo.Info.AccessTime;
3078 pThis->Core.ModificationTime = RockInfo.Info.ModificationTime;
3079 if (RTFS_IS_DIRECTORY(RockInfo.Info.Attr.fMode))
3080 pThis->Core.fAttrib = RockInfo.Info.Attr.fMode;
3081 }
3082 }
3083}
3084
3085
3086/**
3087 * Compares rock ridge information if present in the directory entry.
3088 *
3089 * @param pThis The shared directory structure.
3090 * @param pbSys The system area of the directory record.
3091 * @param cbSys The number of bytes present in the sys area.
3092 * @param pNameCmp The name comparsion data.
3093 * @param fContinuationRecord Set if we're processing a continuation record in
3094 * living in the abRockBuf.
3095 */
3096static int rtFsIsoDirShrd_CompareRockRidgeName(PRTFSISODIRSHRD pThis, uint8_t const *pbSys, size_t cbSys,
3097 PRTFSISOROCKNAMECOMP pNameCmp, bool fContinuationRecord)
3098{
3099 PRTFSISOVOL const pVol = pThis->Core.pVol;
3100
3101 /*
3102 * Do skipping if specified.
3103 */
3104 if (pVol->offSuspSkip)
3105 {
3106 if (cbSys <= pVol->offSuspSkip)
3107 return fContinuationRecord ? VERR_MORE_DATA : VERR_MISMATCH;
3108 pbSys += pVol->offSuspSkip;
3109 cbSys -= pVol->offSuspSkip;
3110 }
3111
3112 while (cbSys >= 4)
3113 {
3114 /*
3115 * Check header length and advance the sys variables.
3116 */
3117 PCISO9660SUSPUNION pUnion = (PCISO9660SUSPUNION)pbSys;
3118 if ( pUnion->Hdr.cbEntry > cbSys
3119 && pUnion->Hdr.cbEntry < sizeof(pUnion->Hdr))
3120 {
3121 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: cbEntry=%#x cbSys=%#x (%#x %#x)\n",
3122 pUnion->Hdr.cbEntry, cbSys, pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
3123 break;
3124 }
3125 pbSys += pUnion->Hdr.cbEntry;
3126 cbSys -= pUnion->Hdr.cbEntry;
3127
3128 /*
3129 * Process the fields we need, nothing else.
3130 */
3131 uint16_t const uSig = SUSP_MAKE_SIG(pUnion->Hdr.bSig1, pUnion->Hdr.bSig2);
3132
3133
3134 /*
3135 * CE - continuation entry
3136 */
3137 if (uSig == SUSP_MAKE_SIG(ISO9660SUSPCE_SIG1, ISO9660SUSPCE_SIG2))
3138 {
3139 if (RT_BE2H_U32(pUnion->CE.offBlock.be) != RT_LE2H_U32(pUnion->CE.offBlock.le))
3140 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Invalid CE offBlock field: be=%#x vs le=%#x\n",
3141 RT_BE2H_U32(pUnion->CE.offBlock.be), RT_LE2H_U32(pUnion->CE.offBlock.le)));
3142 else if (RT_BE2H_U32(pUnion->CE.cbData.be) != RT_LE2H_U32(pUnion->CE.cbData.le))
3143 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Invalid CE cbData field: be=%#x vs le=%#x\n",
3144 RT_BE2H_U32(pUnion->CE.cbData.be), RT_LE2H_U32(pUnion->CE.cbData.le)));
3145 else if (RT_BE2H_U32(pUnion->CE.offData.be) != RT_LE2H_U32(pUnion->CE.offData.le))
3146 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Invalid CE offData field: be=%#x vs le=%#x\n",
3147 RT_BE2H_U32(pUnion->CE.offData.be), RT_LE2H_U32(pUnion->CE.offData.le)));
3148 else if (!fContinuationRecord)
3149 {
3150 uint64_t offData = ISO9660_GET_ENDIAN(&pUnion->CE.offBlock) * (uint64_t)ISO9660_SECTOR_SIZE;
3151 offData += ISO9660_GET_ENDIAN(&pUnion->CE.offData);
3152 uint32_t cbData = ISO9660_GET_ENDIAN(&pUnion->CE.cbData);
3153 if (cbData <= sizeof(pVol->abRockBuf) - (uint32_t)(offData & ISO9660_SECTOR_OFFSET_MASK))
3154 {
3155 RTCritSectEnter(&pVol->RockBufLock);
3156
3157 AssertCompile(sizeof(pVol->abRockBuf) == ISO9660_SECTOR_SIZE);
3158 uint64_t offDataBlock = offData & ~(uint64_t)ISO9660_SECTOR_OFFSET_MASK;
3159 int rc;
3160 if (pVol->offRockBuf == offDataBlock)
3161 rc = rtFsIsoDirShrd_CompareRockRidgeName(pThis, &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
3162 cbData, pNameCmp, true /*fContinuationRecord*/);
3163 else
3164 {
3165 rc = RTVfsFileReadAt(pVol->hVfsBacking, offDataBlock, pVol->abRockBuf, sizeof(pVol->abRockBuf), NULL);
3166 if (RT_SUCCESS(rc))
3167 rc = rtFsIsoDirShrd_CompareRockRidgeName(pThis, &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
3168 cbData, pNameCmp, true /*fContinuationRecord*/);
3169 else
3170 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Error reading continuation record at %#RX64: %Rrc\n",
3171 offDataBlock, rc));
3172 }
3173
3174 RTCritSectLeave(&pVol->RockBufLock);
3175 if (rc != VERR_MORE_DATA)
3176 return rc;
3177 }
3178 else
3179 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: continuation record isn't within a sector! offData=%#RX64 cbData=%#RX32\n",
3180 cbData, offData));
3181 }
3182 else
3183 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: nested continuation record!\n"));
3184 }
3185 /*
3186 * NM - Name entry.
3187 *
3188 * The character set is supposed to be limited to the portable filename
3189 * character set defined in section 2.2.2.60 of POSIX.1: A-Za-z0-9._-
3190 * If there are any other characters used, we consider them as UTF-8
3191 * for reasons of simplicitiy, however we do not make any effort dealing
3192 * with codepoint encodings across NM records for now because it is
3193 * probably a complete waste of time.
3194 */
3195 else if (uSig == SUSP_MAKE_SIG(ISO9660RRIPNM_SIG1, ISO9660RRIPNM_SIG2))
3196 {
3197 if ( pUnion->NM.Hdr.bVersion != ISO9660RRIPNM_VER
3198 || pUnion->NM.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPNM, achName)
3199 || (pUnion->NM.fFlags & ISO9660RRIP_NM_F_RESERVED_MASK) )
3200 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Malformed 'NM' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x %.*Rhxs\n",
3201 pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName),
3202 pUnion->NM.Hdr.bVersion, ISO9660RRIPNM_VER, pUnion->NM.fFlags,
3203 pUnion->NM.Hdr.cbEntry - RT_MIN(pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName)),
3204 &pUnion->NM.achName[0] ));
3205 else
3206 {
3207 uint8_t const cchName = pUnion->NM.Hdr.cbEntry - (uint8_t)RT_UOFFSETOF(ISO9660RRIPNM, achName);
3208 if (!(pUnion->NM.fFlags & (ISO9660RRIP_NM_F_CURRENT | ISO9660RRIP_NM_F_PARENT)))
3209 { /* likely */ }
3210 else
3211 {
3212 if (cchName == 0)
3213 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Ignoring 'NM' entry for '.' and '..'\n"));
3214 else
3215 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Ignoring malformed 'NM' using '.' or '..': fFlags=%#x cchName=%#x %.*Rhxs\n",
3216 pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName));
3217 pNameCmp->offMatched = ~(size_t)0 / 2;
3218 return VERR_MISMATCH;
3219 }
3220 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': fFlags=%#x cchName=%#x '%.*s' (%.*Rhxs); offMatched=%#zx cchEntry=%#zx\n",
3221 pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName, cchName, pUnion->NM.achName, pNameCmp->offMatched, pNameCmp->cchEntry));
3222 AssertReturn(pNameCmp->offMatched < pNameCmp->cchEntry, VERR_MISMATCH);
3223
3224 if (RTStrNICmp(&pNameCmp->pszEntry[pNameCmp->offMatched], pUnion->NM.achName, cchName) == 0)
3225 {
3226 /** @todo Incorrectly ASSUMES all upper and lower codepoints have the same
3227 * encoding length. However, since this shouldn't be UTF-8, but plain
3228 * limited ASCII that's not really all that important. */
3229 pNameCmp->offMatched += cchName;
3230 if (!(pUnion->NM.fFlags & ISO9660RRIP_NM_F_CONTINUE))
3231 {
3232 if (pNameCmp->offMatched >= pNameCmp->cchEntry)
3233 {
3234 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VINF_SUCCESS\n"));
3235 return VINF_SUCCESS;
3236 }
3237 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VERR_MISMATCH - %zu unmatched bytes\n",
3238 pNameCmp->cchEntry - pNameCmp->offMatched));
3239 return VERR_MISMATCH;
3240 }
3241 if (pNameCmp->offMatched >= pNameCmp->cchEntry)
3242 {
3243 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VERR_MISMATCH - match full name but ISO9660RRIP_NM_F_CONTINUE is set!\n"));
3244 return VERR_MISMATCH;
3245 }
3246 }
3247 else
3248 {
3249 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VERR_MISMATCH - mismatch\n"));
3250 pNameCmp->offMatched = ~(size_t)0 / 2;
3251 return VERR_MISMATCH;
3252 }
3253 }
3254 }
3255 }
3256 return fContinuationRecord ? VERR_MORE_DATA : VERR_MISMATCH;
3257}
3258
3259
3260/**
3261 * Worker for rtFsIsoDir_FindEntry9660 that compares a name with the rock ridge
3262 * info in the directory record, if present.
3263 *
3264 * @returns true if equal, false if not.
3265 * @param pThis The directory.
3266 * @param pDirRec The directory record.
3267 * @param pszEntry The string to compare with.
3268 * @param cbEntry The length of @a pszEntry including terminator.
3269 */
3270static bool rtFsIsoDir_IsEntryEqualRock(PRTFSISODIRSHRD pThis, PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cbEntry)
3271{
3272 /*
3273 * Is there room for any rock ridge data?
3274 */
3275 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
3276 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
3277 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
3278 if (cbSys >= 4)
3279 {
3280 RTFSISOROCKNAMECOMP NameCmp;
3281 NameCmp.pszEntry = pszEntry;
3282 NameCmp.cchEntry = cbEntry - 1;
3283 NameCmp.offMatched = 0;
3284 int rc = rtFsIsoDirShrd_CompareRockRidgeName(pThis, pbSys, cbSys, &NameCmp, false /*fContinuationRecord*/);
3285 if (rc == VINF_SUCCESS)
3286 return true;
3287 }
3288 return false;
3289}
3290
3291
3292/**
3293 * Worker for rtFsIsoDir_FindEntry9660 that compares a UTF-16BE name with a
3294 * directory record.
3295 *
3296 * @returns true if equal, false if not.
3297 * @param pDirRec The directory record.
3298 * @param pwszEntry The UTF-16BE string to compare with.
3299 * @param cbEntry The compare string length in bytes (sans zero
3300 * terminator).
3301 * @param cwcEntry The compare string length in RTUTF16 units.
3302 * @param puVersion Where to return any file version number.
3303 */
3304DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualUtf16Big(PCISO9660DIRREC pDirRec, PCRTUTF16 pwszEntry, size_t cbEntry,
3305 size_t cwcEntry, uint32_t *puVersion)
3306{
3307 /* ASSUME directories cannot have any version tags. */
3308 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3309 {
3310 if (RT_LIKELY(pDirRec->bFileIdLength != cbEntry))
3311 return false;
3312 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
3313 return false;
3314 }
3315 else
3316 {
3317 size_t cbNameDelta = (size_t)pDirRec->bFileIdLength - cbEntry;
3318 if (RT_LIKELY(cbNameDelta > (size_t)12 /* ;12345 */))
3319 return false;
3320 if (cbNameDelta == 0)
3321 {
3322 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
3323 return false;
3324 *puVersion = 1;
3325 }
3326 else
3327 {
3328 if (RT_LIKELY(RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';'))
3329 return false;
3330 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
3331 return false;
3332 uint32_t uVersion;
3333 size_t cwcVersion = rtFsIso9660GetVersionLengthUtf16Big((PCRTUTF16)pDirRec->achFileId,
3334 pDirRec->bFileIdLength, &uVersion);
3335 if (RT_LIKELY(cwcVersion * sizeof(RTUTF16) == cbNameDelta))
3336 *puVersion = uVersion;
3337 else
3338 return false;
3339 }
3340 }
3341
3342 /* (No need to check for dot and dot-dot here, because cbEntry must be a
3343 multiple of two.) */
3344 Assert(!(cbEntry & 1));
3345 return true;
3346}
3347
3348
3349/**
3350 * Worker for rtFsIsoDir_FindEntry9660 that compares an ASCII name with a
3351 * directory record.
3352 *
3353 * @returns true if equal, false if not.
3354 * @param pDirRec The directory record.
3355 * @param pszEntry The uppercased ASCII string to compare with.
3356 * @param cchEntry The length of the compare string.
3357 * @param puVersion Where to return any file version number.
3358 *
3359 * @note We're using RTStrNICmpAscii here because of non-conforming ISOs with
3360 * entirely lowercase name or mixed cased names.
3361 */
3362DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualAscii(PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cchEntry,
3363 uint32_t *puVersion)
3364{
3365 /* ASSUME directories cannot have any version tags. */
3366 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3367 {
3368 if (RT_LIKELY(pDirRec->bFileIdLength != cchEntry))
3369 return false;
3370 if (RT_LIKELY(RTStrNICmpAscii(pDirRec->achFileId, pszEntry, cchEntry) != 0))
3371 return false;
3372 }
3373 else
3374 {
3375 size_t cchNameDelta = (size_t)pDirRec->bFileIdLength - cchEntry;
3376 if (RT_LIKELY(cchNameDelta > (size_t)6 /* ;12345 */))
3377 return false;
3378 if (cchNameDelta == 0)
3379 {
3380 if (RT_LIKELY(RTStrNICmpAscii(pDirRec->achFileId, pszEntry, cchEntry) != 0))
3381 return false;
3382 *puVersion = 1;
3383 }
3384 else
3385 {
3386 if (RT_LIKELY(pDirRec->achFileId[cchEntry] != ';'))
3387 return false;
3388 if (RT_LIKELY(RTStrNICmpAscii(pDirRec->achFileId, pszEntry, cchEntry) != 0))
3389 return false;
3390 uint32_t uVersion;
3391 size_t cchVersion = rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion);
3392 if (RT_LIKELY(cchVersion == cchNameDelta))
3393 *puVersion = uVersion;
3394 else
3395 return false;
3396 }
3397 }
3398
3399 /* Don't match the 'dot' and 'dot-dot' directory records. */
3400 if (RT_LIKELY( pDirRec->bFileIdLength != 1
3401 || (uint8_t)pDirRec->achFileId[0] > (uint8_t)0x01))
3402 return true;
3403 return false;
3404}
3405
3406
3407/**
3408 * Locates a directory entry in a directory.
3409 *
3410 * @returns IPRT status code.
3411 * @retval VERR_FILE_NOT_FOUND if not found.
3412 * @param pThis The directory to search.
3413 * @param pszEntry The entry to look for.
3414 * @param poffDirRec Where to return the offset of the directory record
3415 * on the disk.
3416 * @param ppDirRec Where to return the pointer to the directory record
3417 * (the whole directory is buffered).
3418 * @param pcDirRecs Where to return the number of directory records
3419 * related to this entry.
3420 * @param pfMode Where to return the file type, rock ridge adjusted.
3421 * @param puVersion Where to return the file version number.
3422 * @param pRockInfo Where to return rock ridge info. This is NULL if
3423 * the volume didn't advertise any rock ridge info.
3424 */
3425static int rtFsIsoDir_FindEntry9660(PRTFSISODIRSHRD pThis, const char *pszEntry, uint64_t *poffDirRec, PCISO9660DIRREC *ppDirRec,
3426 uint32_t *pcDirRecs, PRTFMODE pfMode, uint32_t *puVersion, PRTFSISOROCKINFO pRockInfo)
3427{
3428 Assert(pThis->Core.pVol->enmType != RTFSISOVOLTYPE_UDF);
3429
3430 /* Set return values. */
3431 *poffDirRec = UINT64_MAX;
3432 *ppDirRec = NULL;
3433 *pcDirRecs = 1;
3434 *pfMode = UINT32_MAX;
3435 *puVersion = 0;
3436 if (pRockInfo)
3437 pRockInfo->fValid = false;
3438
3439 /*
3440 * If we're in UTF-16BE mode, convert the input name to UTF-16BE. Otherwise try
3441 * uppercase it into a ISO 9660 compliant name.
3442 */
3443 int rc;
3444 bool const fIsUtf16 = pThis->Core.pVol->fIsUtf16;
3445 size_t cwcEntry = 0;
3446 size_t cbEntry = 0;
3447 size_t cchUpper = ~(size_t)0;
3448 union
3449 {
3450 RTUTF16 wszEntry[260 + 1];
3451 struct
3452 {
3453 char szUpper[255 + 1];
3454 char szRock[260 + 1];
3455 } s;
3456 } uBuf;
3457 if (fIsUtf16)
3458 {
3459 PRTUTF16 pwszEntry = uBuf.wszEntry;
3460 rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
3461 if (RT_FAILURE(rc))
3462 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
3463 cbEntry = cwcEntry * 2;
3464 }
3465 else
3466 {
3467 rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
3468 if (RT_FAILURE(rc))
3469 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
3470 RTStrToUpper(uBuf.s.szUpper);
3471 cchUpper = strlen(uBuf.s.szUpper);
3472 cbEntry = strlen(pszEntry) + 1;
3473 }
3474
3475 /*
3476 * Scan the directory buffer by buffer.
3477 */
3478 uint32_t offEntryInDir = 0;
3479 uint32_t const cbDir = pThis->Core.cbObject;
3480 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
3481 {
3482 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
3483
3484 /* If null length, skip to the next sector. */
3485 if (pDirRec->cbDirRec == 0)
3486 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
3487 else
3488 {
3489 /*
3490 * Try match the filename.
3491 */
3492 /** @todo not sure if it's a great idea to match both name spaces... */
3493 if (RT_LIKELY( fIsUtf16
3494 ? !rtFsIsoDir_IsEntryEqualUtf16Big(pDirRec, uBuf.wszEntry, cbEntry, cwcEntry, puVersion)
3495 && ( !pRockInfo
3496 || !rtFsIsoDir_IsEntryEqualRock(pThis, pDirRec, pszEntry, cbEntry))
3497 : ( !pRockInfo
3498 || !rtFsIsoDir_IsEntryEqualRock(pThis, pDirRec, pszEntry, cbEntry))
3499 && !rtFsIsoDir_IsEntryEqualAscii(pDirRec, uBuf.s.szUpper, cchUpper, puVersion) ))
3500 {
3501 /* Advance */
3502 offEntryInDir += pDirRec->cbDirRec;
3503 continue;
3504 }
3505
3506 /*
3507 * Get info for the entry.
3508 */
3509 if (!pRockInfo)
3510 *pfMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
3511 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
3512 : 0644 | RTFS_TYPE_FILE;
3513 else
3514 {
3515 rtFsIsoDirShrd_ParseRockForDirRec(pThis, pDirRec, pRockInfo);
3516 *pfMode = pRockInfo->Info.Attr.fMode;
3517 }
3518 *poffDirRec = pThis->Core.FirstExtent.off + offEntryInDir;
3519 *ppDirRec = pDirRec;
3520
3521 /*
3522 * Deal with the unlikely scenario of multi extent records.
3523 */
3524 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3525 *pcDirRecs = 1;
3526 else
3527 {
3528 offEntryInDir += pDirRec->cbDirRec;
3529
3530 uint32_t cDirRecs = 1;
3531 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
3532 {
3533 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
3534 if (pDirRec2->cbDirRec != 0)
3535 {
3536 Assert(rtFsIsoDir_Is9660DirRecNextExtent(pDirRec, pDirRec2));
3537 cDirRecs++;
3538 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3539 break;
3540 offEntryInDir += pDirRec2->cbDirRec;
3541 }
3542 else
3543 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
3544 }
3545
3546 *pcDirRecs = cDirRecs;
3547 }
3548 return VINF_SUCCESS;
3549 }
3550 }
3551
3552 return VERR_FILE_NOT_FOUND;
3553}
3554
3555
3556/**
3557 * Locates a directory entry in a directory.
3558 *
3559 * @returns IPRT status code.
3560 * @retval VERR_FILE_NOT_FOUND if not found.
3561 * @param pThis The directory to search.
3562 * @param pszEntry The entry to look for.
3563 * @param ppFid Where to return the pointer to the file ID entry.
3564 * (Points to the directory content.)
3565 */
3566static int rtFsIsoDir_FindEntryUdf(PRTFSISODIRSHRD pThis, const char *pszEntry, PCUDFFILEIDDESC *ppFid)
3567{
3568 Assert(pThis->Core.pVol->enmType == RTFSISOVOLTYPE_UDF);
3569 *ppFid = NULL;
3570
3571 /*
3572 * Recode the entry name as 8-bit (if possible) and 16-bit strings.
3573 * This also disposes of entries that definitely are too long.
3574 */
3575 size_t cb8Bit;
3576 bool fSimple;
3577 size_t cb16Bit;
3578 size_t cwc16Bit;
3579 uint8_t ab8Bit[255];
3580 RTUTF16 wsz16Bit[255];
3581
3582 /* 16-bit */
3583 PRTUTF16 pwsz16Bit = wsz16Bit;
3584 int rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwsz16Bit, RT_ELEMENTS(wsz16Bit), &cwc16Bit);
3585 if (RT_SUCCESS(rc))
3586 cb16Bit = 1 + cwc16Bit * sizeof(RTUTF16);
3587 else
3588 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
3589
3590 /* 8-bit (can't possibly overflow) */
3591 fSimple = true;
3592 cb8Bit = 0;
3593 const char *pszSrc = pszEntry;
3594 for (;;)
3595 {
3596 RTUNICP uc;
3597 int rc2 = RTStrGetCpEx(&pszSrc, &uc);
3598 AssertRCReturn(rc2, rc2);
3599 if (uc <= 0x7f)
3600 {
3601 if (uc)
3602 ab8Bit[cb8Bit++] = (uint8_t)uc;
3603 else
3604 break;
3605 }
3606 else if (uc <= 0xff)
3607 {
3608 ab8Bit[cb8Bit++] = (uint8_t)uc;
3609 fSimple = false;
3610 }
3611 else
3612 {
3613 cb8Bit = UINT32_MAX / 2;
3614 break;
3615 }
3616 }
3617 Assert(cb8Bit <= sizeof(ab8Bit) || cb8Bit == UINT32_MAX / 2);
3618 cb8Bit++;
3619
3620 /*
3621 * Scan the directory content.
3622 */
3623 uint32_t offDesc = 0;
3624 uint32_t const cbDir = pThis->Core.cbObject;
3625 while (offDesc + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= cbDir)
3626 {
3627 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
3628 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3629 if ( offDesc + cbFid <= cbDir
3630 && pFid->Tag.idTag == UDF_TAG_ID_FILE_ID_DESC)
3631 { /* likely */ }
3632 else
3633 break;
3634
3635 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3636 if (*pbName == 16)
3637 {
3638 if (cb16Bit == pFid->cbName)
3639 {
3640 if (RTUtf16BigNICmp((PCRTUTF16)(&pbName[1]), wsz16Bit, cwc16Bit) == 0)
3641 {
3642 *ppFid = pFid;
3643 return VINF_SUCCESS;
3644 }
3645 }
3646 }
3647 else if (*pbName == 8)
3648 {
3649 if ( cb8Bit == pFid->cbName
3650 && cb8Bit != UINT16_MAX)
3651 {
3652 if (fSimple)
3653 {
3654 if (RTStrNICmp((const char *)&pbName[1], (const char *)ab8Bit, cb8Bit - 1) == 0)
3655 {
3656 *ppFid = pFid;
3657 return VINF_SUCCESS;
3658 }
3659 }
3660 else
3661 {
3662 size_t cch = cb8Bit - 1;
3663 size_t off;
3664 for (off = 0; off < cch; off++)
3665 {
3666 RTUNICP uc1 = ab8Bit[off];
3667 RTUNICP uc2 = pbName[off + 1];
3668 if ( uc1 == uc2
3669 || RTUniCpToLower(uc1) == RTUniCpToLower(uc2)
3670 || RTUniCpToUpper(uc1) == RTUniCpToUpper(uc2))
3671 { /* matches */ }
3672 else
3673 break;
3674 }
3675 if (off == cch)
3676 {
3677 *ppFid = pFid;
3678 return VINF_SUCCESS;
3679 }
3680 }
3681 }
3682 }
3683
3684 /* advance */
3685 offDesc += cbFid;
3686 }
3687
3688 return VERR_FILE_NOT_FOUND;
3689}
3690
3691
3692/**
3693 * Releases a reference to a shared directory structure.
3694 *
3695 * @param pShared The shared directory structure.
3696 */
3697static void rtFsIsoDirShrd_Release(PRTFSISODIRSHRD pShared)
3698{
3699 uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
3700 Assert(cRefs < UINT32_MAX / 2);
3701 if (cRefs == 0)
3702 {
3703 LogFlow(("rtFsIsoDirShrd_Release: Destroying shared structure %p\n", pShared));
3704 Assert(pShared->Core.cRefs == 0);
3705 if (pShared->pbDir)
3706 {
3707 RTMemFree(pShared->pbDir);
3708 pShared->pbDir = NULL;
3709 }
3710 rtFsIsoCore_Destroy(&pShared->Core);
3711 RTMemFree(pShared);
3712 }
3713}
3714
3715
3716/**
3717 * Retains a reference to a shared directory structure.
3718 *
3719 * @param pShared The shared directory structure.
3720 */
3721static void rtFsIsoDirShrd_Retain(PRTFSISODIRSHRD pShared)
3722{
3723 uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
3724 Assert(cRefs > 1); NOREF(cRefs);
3725}
3726
3727
3728
3729/**
3730 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
3731 */
3732static DECLCALLBACK(int) rtFsIsoDir_Close(void *pvThis)
3733{
3734 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3735 LogFlow(("rtFsIsoDir_Close(%p/%p)\n", pThis, pThis->pShared));
3736
3737 PRTFSISODIRSHRD pShared = pThis->pShared;
3738 pThis->pShared = NULL;
3739 if (pShared)
3740 rtFsIsoDirShrd_Release(pShared);
3741 return VINF_SUCCESS;
3742}
3743
3744
3745/**
3746 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
3747 */
3748static DECLCALLBACK(int) rtFsIsoDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3749{
3750 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3751 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
3752}
3753
3754
3755/**
3756 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
3757 */
3758static DECLCALLBACK(int) rtFsIsoDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
3759 uint32_t fFlags, PRTVFSOBJ phVfsObj)
3760{
3761 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3762 PRTFSISODIRSHRD pShared = pThis->pShared;
3763 int rc;
3764
3765 /*
3766 * We cannot create or replace anything, just open stuff.
3767 */
3768 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
3769 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
3770 { /* likely */ }
3771 else
3772 return VERR_WRITE_PROTECT;
3773
3774 /*
3775 * Special cases '.' and '..'
3776 */
3777 if (pszEntry[0] == '.')
3778 {
3779 PRTFSISODIRSHRD pSharedToOpen;
3780 if (pszEntry[1] == '\0')
3781 pSharedToOpen = pShared;
3782 else if (pszEntry[1] == '.' && pszEntry[2] == '\0')
3783 {
3784 pSharedToOpen = pShared->Core.pParentDir;
3785 if (!pSharedToOpen)
3786 pSharedToOpen = pShared;
3787 }
3788 else
3789 pSharedToOpen = NULL;
3790 if (pSharedToOpen)
3791 {
3792 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3793 {
3794 rtFsIsoDirShrd_Retain(pSharedToOpen);
3795 RTVFSDIR hVfsDir;
3796 rc = rtFsIsoDir_NewWithShared(pShared->Core.pVol, pSharedToOpen, &hVfsDir);
3797 if (RT_SUCCESS(rc))
3798 {
3799 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3800 RTVfsDirRelease(hVfsDir);
3801 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3802 }
3803 }
3804 else
3805 rc = VERR_IS_A_DIRECTORY;
3806 return rc;
3807 }
3808 }
3809
3810 /*
3811 * Try open whatever it is.
3812 */
3813 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3814 {
3815
3816 /*
3817 * ISO 9660
3818 */
3819 PCISO9660DIRREC pDirRec;
3820 uint64_t offDirRec;
3821 uint32_t cDirRecs;
3822 RTFMODE fMode;
3823 uint32_t uVersion;
3824 PRTFSISOROCKINFO pRockInfo = NULL;
3825 if (pShared->Core.pVol->fHaveRock)
3826 pRockInfo = (PRTFSISOROCKINFO)alloca(sizeof(*pRockInfo));
3827 rc = rtFsIsoDir_FindEntry9660(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion, pRockInfo);
3828 Log2(("rtFsIsoDir_Open: FindEntry9660(,%s,) -> %Rrc\n", pszEntry, rc));
3829 if (RT_SUCCESS(rc))
3830 {
3831 switch (fMode & RTFS_TYPE_MASK)
3832 {
3833 case RTFS_TYPE_FILE:
3834 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
3835 {
3836 RTVFSFILE hVfsFile;
3837 rc = rtFsIsoFile_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, fOpen,
3838 uVersion, pRockInfo && pRockInfo->fValid ? pRockInfo : NULL, &hVfsFile);
3839 if (RT_SUCCESS(rc))
3840 {
3841 *phVfsObj = RTVfsObjFromFile(hVfsFile);
3842 RTVfsFileRelease(hVfsFile);
3843 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3844 }
3845 }
3846 else
3847 rc = VERR_IS_A_FILE;
3848 break;
3849
3850 case RTFS_TYPE_DIRECTORY:
3851 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3852 {
3853 RTVFSDIR hVfsDir;
3854 rc = rtFsIsoDir_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec,
3855 pRockInfo && pRockInfo->fValid ? pRockInfo : NULL, &hVfsDir);
3856 if (RT_SUCCESS(rc))
3857 {
3858 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3859 RTVfsDirRelease(hVfsDir);
3860 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3861 }
3862 }
3863 else
3864 rc = VERR_IS_A_DIRECTORY;
3865 break;
3866
3867 case RTFS_TYPE_SYMLINK:
3868 case RTFS_TYPE_DEV_BLOCK:
3869 case RTFS_TYPE_DEV_CHAR:
3870 case RTFS_TYPE_FIFO:
3871 case RTFS_TYPE_SOCKET:
3872 case RTFS_TYPE_WHITEOUT:
3873 rc = VERR_NOT_IMPLEMENTED;
3874 break;
3875
3876 default:
3877 rc = VERR_PATH_NOT_FOUND;
3878 break;
3879 }
3880 }
3881 }
3882 else
3883 {
3884 /*
3885 * UDF
3886 */
3887 PCUDFFILEIDDESC pFid;
3888 rc = rtFsIsoDir_FindEntryUdf(pShared, pszEntry, &pFid);
3889 Log2(("rtFsIsoDir_Open: FindEntryUdf(,%s,) -> %Rrc\n", pszEntry, rc));
3890 if (RT_SUCCESS(rc))
3891 {
3892 if (!(pFid->fFlags & UDF_FILE_FLAGS_DELETED))
3893 {
3894 if (!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY))
3895 {
3896 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
3897 {
3898 RTVFSFILE hVfsFile;
3899 rc = rtFsIsoFile_NewUdf(pShared->Core.pVol, pShared, pFid, fOpen, &hVfsFile);
3900 if (RT_SUCCESS(rc))
3901 {
3902 *phVfsObj = RTVfsObjFromFile(hVfsFile);
3903 RTVfsFileRelease(hVfsFile);
3904 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3905 }
3906 }
3907 else
3908 rc = VERR_IS_A_FILE;
3909 }
3910 else
3911 {
3912 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3913 {
3914 RTVFSDIR hVfsDir;
3915 rc = rtFsIsoDir_NewUdf(pShared->Core.pVol, pShared, pFid, &hVfsDir);
3916 if (RT_SUCCESS(rc))
3917 {
3918 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3919 RTVfsDirRelease(hVfsDir);
3920 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3921 }
3922 }
3923 else
3924 rc = VERR_IS_A_DIRECTORY;
3925 }
3926 }
3927 /* We treat UDF_FILE_FLAGS_DELETED like RTFS_TYPE_WHITEOUT for now. */
3928 else
3929 rc = VERR_PATH_NOT_FOUND;
3930 }
3931 }
3932 return rc;
3933
3934}
3935
3936
3937/**
3938 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3939 */
3940static DECLCALLBACK(int) rtFsIsoDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3941{
3942 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3943 return VERR_WRITE_PROTECT;
3944}
3945
3946
3947/**
3948 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3949 */
3950static DECLCALLBACK(int) rtFsIsoDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3951{
3952 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3953 return VERR_NOT_SUPPORTED;
3954}
3955
3956
3957/**
3958 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3959 */
3960static DECLCALLBACK(int) rtFsIsoDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3961 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3962{
3963 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3964 return VERR_WRITE_PROTECT;
3965}
3966
3967
3968/**
3969 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3970 */
3971static DECLCALLBACK(int) rtFsIsoDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3972{
3973 RT_NOREF(pvThis, pszEntry, fType);
3974 return VERR_WRITE_PROTECT;
3975}
3976
3977
3978/**
3979 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3980 */
3981static DECLCALLBACK(int) rtFsIsoDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3982{
3983 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3984 return VERR_WRITE_PROTECT;
3985}
3986
3987
3988/**
3989 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3990 */
3991static DECLCALLBACK(int) rtFsIsoDir_RewindDir(void *pvThis)
3992{
3993 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3994 pThis->offDir = 0;
3995 return VINF_SUCCESS;
3996}
3997
3998
3999/**
4000 * The ISO 9660 worker for rtFsIsoDir_ReadDir
4001 */
4002static int rtFsIsoDir_ReadDir9660(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4003 RTFSOBJATTRADD enmAddAttr)
4004{
4005 PRTFSISOROCKINFO pRockInfo = NULL;
4006 if (pShared->Core.pVol->fHaveRock)
4007 pRockInfo = (PRTFSISOROCKINFO)alloca(sizeof(*pRockInfo));
4008
4009 while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
4010 {
4011 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
4012
4013 /* If null length, skip to the next sector. */
4014 if (pDirRec->cbDirRec == 0)
4015 pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
4016 else
4017 {
4018 /*
4019 * Do names first as they may cause overflows.
4020 */
4021 uint32_t uVersion = 0;
4022 if ( pDirRec->bFileIdLength == 1
4023 && pDirRec->achFileId[0] == '\0')
4024 {
4025 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
4026 {
4027 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
4028 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot)\n"));
4029 return VERR_BUFFER_OVERFLOW;
4030 }
4031 pDirEntry->cbName = 1;
4032 pDirEntry->szName[0] = '.';
4033 pDirEntry->szName[1] = '\0';
4034 }
4035 else if ( pDirRec->bFileIdLength == 1
4036 && pDirRec->achFileId[0] == '\1')
4037 {
4038 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
4039 {
4040 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
4041 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
4042 return VERR_BUFFER_OVERFLOW;
4043 }
4044 pDirEntry->cbName = 2;
4045 pDirEntry->szName[0] = '.';
4046 pDirEntry->szName[1] = '.';
4047 pDirEntry->szName[2] = '\0';
4048 }
4049 else if (pShared->Core.pVol->fIsUtf16)
4050 {
4051 PCRTUTF16 pawcSrc = (PCRTUTF16)&pDirRec->achFileId[0];
4052 size_t cwcSrc = pDirRec->bFileIdLength / sizeof(RTUTF16);
4053 size_t cwcVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
4054 ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
4055 size_t cchNeeded = 0;
4056 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
4057 char *pszDst = pDirEntry->szName;
4058
4059 int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
4060 if (RT_SUCCESS(rc))
4061 pDirEntry->cbName = (uint16_t)cchNeeded;
4062 else if (rc == VERR_BUFFER_OVERFLOW)
4063 {
4064 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
4065 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
4066 return VERR_BUFFER_OVERFLOW;
4067 }
4068 else
4069 {
4070 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
4071 if (cchNeeded2 >= 0)
4072 pDirEntry->cbName = (uint16_t)cchNeeded2;
4073 else
4074 {
4075 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
4076 return VERR_BUFFER_OVERFLOW;
4077 }
4078 }
4079 }
4080 else
4081 {
4082 /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
4083 size_t cchVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
4084 ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
4085 size_t cchName = pDirRec->bFileIdLength - cchVer;
4086 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
4087 if (*pcbDirEntry < cbNeeded)
4088 {
4089 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
4090 *pcbDirEntry = cbNeeded;
4091 return VERR_BUFFER_OVERFLOW;
4092 }
4093 pDirEntry->cbName = (uint16_t)cchName;
4094 memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
4095 pDirEntry->szName[cchName] = '\0';
4096 RTStrPurgeEncoding(pDirEntry->szName);
4097 }
4098 pDirEntry->cwcShortName = 0;
4099 pDirEntry->wszShortName[0] = '\0';
4100
4101 /*
4102 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
4103 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
4104 */
4105 RTFSISOCORE TmpObj;
4106 RT_ZERO(TmpObj);
4107 rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
4108 pThis->offDir + pShared->Core.FirstExtent.off, uVersion, NULL, pShared->Core.pVol);
4109 int rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
4110
4111 /*
4112 * Look for rock ridge info associated with this entry
4113 * and merge that into the record.
4114 */
4115 if (pRockInfo)
4116 {
4117 rtFsIsoDirShrd_ParseRockForDirRec(pShared, pDirRec, pRockInfo);
4118 if (pRockInfo->fValid)
4119 {
4120 if ( pRockInfo->fSeenLastNM
4121 && pRockInfo->cchName > 0
4122 && !pShared->Core.pVol->fIsUtf16
4123 && ( pDirRec->bFileIdLength != 1
4124 || ( pDirRec->achFileId[0] != '\0' /* . */
4125 && pDirRec->achFileId[0] != '\1'))) /* .. */
4126 {
4127 size_t const cchName = pRockInfo->cchName;
4128 Assert(strlen(pRockInfo->szName) == cchName);
4129 size_t const cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
4130 if (*pcbDirEntry < cbNeeded)
4131 {
4132 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (Rock)\n", *pcbDirEntry, cbNeeded));
4133 *pcbDirEntry = cbNeeded;
4134 return VERR_BUFFER_OVERFLOW;
4135 }
4136 pDirEntry->cbName = (uint16_t)cchName;
4137 memcpy(pDirEntry->szName, pRockInfo->szName, cchName);
4138 pDirEntry->szName[cchName] = '\0';
4139
4140 RTStrPurgeEncoding(pDirEntry->szName);
4141 }
4142 }
4143 }
4144
4145 /*
4146 * Update the directory location and handle multi extent records.
4147 *
4148 * Multi extent records only affect the file size and the directory location,
4149 * so we deal with it here instead of involving rtFsIsoCore_InitFrom9660DirRec
4150 * which would potentially require freeing memory and such.
4151 */
4152 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
4153 {
4154 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
4155 pThis->offDir += pDirRec->cbDirRec;
4156 }
4157 else
4158 {
4159 uint32_t cExtents = 1;
4160 uint32_t offDir = pThis->offDir + pDirRec->cbDirRec;
4161 while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
4162 {
4163 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
4164 if (pDirRec2->cbDirRec != 0)
4165 {
4166 pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
4167 offDir += pDirRec2->cbDirRec;
4168 cExtents++;
4169 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
4170 break;
4171 }
4172 else
4173 offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
4174 }
4175 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
4176 pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
4177 pThis->offDir = offDir;
4178 }
4179
4180 return rc;
4181 }
4182 }
4183
4184 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
4185 return VERR_NO_MORE_FILES;
4186}
4187
4188
4189/**
4190 * The UDF worker for rtFsIsoDir_ReadDir
4191 */
4192static int rtFsIsoDir_ReadDirUdf(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4193 RTFSOBJATTRADD enmAddAttr)
4194{
4195 /*
4196 * At offset zero we've got the '.' entry. This has to be generated
4197 * manually as it's not part of the directory content. The directory
4198 * offset has to be faked for this too, so offDir == 0 indicates the '.'
4199 * entry whereas offDir == 1 is the first file id descriptor.
4200 */
4201 if (pThis->offDir == 0)
4202 {
4203 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
4204 {
4205 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
4206 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW (dot)\n"));
4207 return VERR_BUFFER_OVERFLOW;
4208 }
4209 pDirEntry->cbName = 1;
4210 pDirEntry->szName[0] = '.';
4211 pDirEntry->szName[1] = '\0';
4212 pDirEntry->cwcShortName = 0;
4213 pDirEntry->wszShortName[0] = '\0';
4214
4215 int rc = rtFsIsoCore_QueryInfo(&pShared->Core, &pDirEntry->Info, enmAddAttr);
4216
4217 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
4218 pThis->offDir = 1;
4219 return rc;
4220 }
4221
4222 /*
4223 * Do the directory content.
4224 */
4225 while (pThis->offDir + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= pShared->cbDir + 1)
4226 {
4227 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pShared->pbDir[pThis->offDir - 1];
4228 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
4229
4230 if (pThis->offDir + cbFid <= pShared->cbDir + 1)
4231 { /* likely */ }
4232 else
4233 break;
4234
4235 /*
4236 * Do names first as they may cause overflows.
4237 */
4238 if (pFid->cbName > 1)
4239 {
4240 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
4241 uint32_t cbSrc = pFid->cbName;
4242 if (*pbName == 8)
4243 {
4244 /* Figure out the UTF-8 length first. */
4245 bool fSimple = true;
4246 uint32_t cchDst = 0;
4247 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
4248 if (!(pbName[offSrc] & 0x80))
4249 cchDst++;
4250 else
4251 {
4252 cchDst += 2;
4253 fSimple = false;
4254 }
4255
4256 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchDst + 1;
4257 if (*pcbDirEntry >= cbNeeded)
4258 {
4259 if (fSimple)
4260 {
4261 Assert(cbSrc - 1 == cchDst);
4262 memcpy(pDirEntry->szName, &pbName[1], cchDst);
4263 pDirEntry->szName[cchDst] = '\0';
4264 }
4265 else
4266 {
4267 char *pszDst = pDirEntry->szName;
4268 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
4269 pszDst = RTStrPutCp(pszDst, pbName[offSrc]);
4270 *pszDst = '\0';
4271 Assert((size_t)(pszDst - &pDirEntry->szName[0]) == cchDst);
4272 }
4273 }
4274 else
4275 {
4276 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (8-bit)\n", *pcbDirEntry, cbNeeded));
4277 *pcbDirEntry = cbNeeded;
4278 return VERR_BUFFER_OVERFLOW;
4279 }
4280 }
4281 else
4282 {
4283 /* Let RTUtf16BigToUtf8Ex do the bounds checking. */
4284 char *pszDst = pDirEntry->szName;
4285 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
4286 size_t cchNeeded = 0;
4287 int rc;
4288 if (*pbName == 16)
4289 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)(pbName + 1), (cbSrc - 1) / sizeof(RTUTF16), &pszDst, cbDst, &cchNeeded);
4290 else
4291 rc = VERR_INVALID_NAME;
4292 if (RT_SUCCESS(rc))
4293 pDirEntry->cbName = (uint16_t)cchNeeded;
4294 else if (rc == VERR_BUFFER_OVERFLOW)
4295 {
4296 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
4297 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (16-bit)\n", cbDst, cchNeeded));
4298 return VERR_BUFFER_OVERFLOW;
4299 }
4300 else
4301 {
4302 LogRelMax(90, ("ISO/UDF: Malformed directory entry name at %#x: %.*Rhxs\n", pThis->offDir - 1, cbSrc, pbName));
4303 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir - 1);
4304 if (cchNeeded2 >= 0)
4305 pDirEntry->cbName = (uint16_t)cchNeeded2;
4306 else
4307 {
4308 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
4309 return VERR_BUFFER_OVERFLOW;
4310 }
4311 }
4312 }
4313 }
4314 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
4315 {
4316 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2 + 1;
4317 if (*pcbDirEntry < cbNeeded)
4318 {
4319 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (dot-dot)\n", *pcbDirEntry, cbNeeded));
4320 *pcbDirEntry = cbNeeded;
4321 return VERR_BUFFER_OVERFLOW;
4322 }
4323 pDirEntry->cbName = 2;
4324 pDirEntry->szName[0] = '.';
4325 pDirEntry->szName[1] = '.';
4326 pDirEntry->szName[2] = '\0';
4327 }
4328 else
4329 {
4330 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 1;
4331 if (*pcbDirEntry < cbNeeded)
4332 {
4333 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (empty)\n", *pcbDirEntry, cbNeeded));
4334 *pcbDirEntry = cbNeeded;
4335 return VERR_BUFFER_OVERFLOW;
4336 }
4337 pDirEntry->cbName = 0;
4338 pDirEntry->szName[0] = '\0';
4339 }
4340
4341 pDirEntry->cwcShortName = 0;
4342 pDirEntry->wszShortName[0] = '\0';
4343
4344 /*
4345 * To avoid duplicating code in rtFsIsoCore_InitUdf and
4346 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
4347 */
4348 RTFSISOCORE TmpObj;
4349 RT_ZERO(TmpObj);
4350 int rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&TmpObj, &pFid->Icb, pFid, pThis->offDir - 1, pShared->Core.pVol);
4351 if (RT_SUCCESS(rc))
4352 {
4353 rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
4354 rtFsIsoCore_Destroy(&TmpObj);
4355 }
4356
4357 /*
4358 * Update.
4359 */
4360 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
4361 pThis->offDir += cbFid;
4362
4363 return rc;
4364 }
4365
4366 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
4367 return VERR_NO_MORE_FILES;
4368}
4369
4370
4371/**
4372 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
4373 */
4374static DECLCALLBACK(int) rtFsIsoDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4375 RTFSOBJATTRADD enmAddAttr)
4376{
4377 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
4378 PRTFSISODIRSHRD pShared = pThis->pShared;
4379 int rc;
4380 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
4381 rc = rtFsIsoDir_ReadDir9660(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
4382 else
4383 rc = rtFsIsoDir_ReadDirUdf(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
4384 return rc;
4385}
4386
4387
4388/**
4389 * ISO file operations.
4390 */
4391static const RTVFSDIROPS g_rtFsIsoDirOps =
4392{
4393 { /* Obj */
4394 RTVFSOBJOPS_VERSION,
4395 RTVFSOBJTYPE_DIR,
4396 "ISO 9660 Dir",
4397 rtFsIsoDir_Close,
4398 rtFsIsoDir_QueryInfo,
4399 NULL,
4400 RTVFSOBJOPS_VERSION
4401 },
4402 RTVFSDIROPS_VERSION,
4403 0,
4404 { /* ObjSet */
4405 RTVFSOBJSETOPS_VERSION,
4406 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
4407 NULL /*SetMode*/,
4408 NULL /*SetTimes*/,
4409 NULL /*SetOwner*/,
4410 RTVFSOBJSETOPS_VERSION
4411 },
4412 rtFsIsoDir_Open,
4413 NULL /* pfnFollowAbsoluteSymlink */,
4414 NULL /* pfnOpenFile */,
4415 NULL /* pfnOpenDir */,
4416 rtFsIsoDir_CreateDir,
4417 rtFsIsoDir_OpenSymlink,
4418 rtFsIsoDir_CreateSymlink,
4419 NULL /* pfnQueryEntryInfo */,
4420 rtFsIsoDir_UnlinkEntry,
4421 rtFsIsoDir_RenameEntry,
4422 rtFsIsoDir_RewindDir,
4423 rtFsIsoDir_ReadDir,
4424 RTVFSDIROPS_VERSION,
4425};
4426
4427
4428/**
4429 * Adds an open child to the parent directory's shared structure.
4430 *
4431 * Maintains an additional reference to the parent dir to prevent it from going
4432 * away. If @a pDir is the root directory, it also ensures the volume is
4433 * referenced and sticks around until the last open object is gone.
4434 *
4435 * @param pDir The directory.
4436 * @param pChild The child being opened.
4437 * @sa rtFsIsoDirShrd_RemoveOpenChild
4438 */
4439static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
4440{
4441 rtFsIsoDirShrd_Retain(pDir);
4442
4443 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
4444 pChild->pParentDir = pDir;
4445}
4446
4447
4448/**
4449 * Removes an open child to the parent directory.
4450 *
4451 * @param pDir The directory.
4452 * @param pChild The child being removed.
4453 *
4454 * @remarks This is the very last thing you do as it may cause a few other
4455 * objects to be released recursively (parent dir and the volume).
4456 *
4457 * @sa rtFsIsoDirShrd_AddOpenChild
4458 */
4459static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
4460{
4461 AssertReturnVoid(pChild->pParentDir == pDir);
4462 RTListNodeRemove(&pChild->Entry);
4463 pChild->pParentDir = NULL;
4464
4465 rtFsIsoDirShrd_Release(pDir);
4466}
4467
4468
4469#ifdef LOG_ENABLED
4470/**
4471 * Logs the content of a directory.
4472 */
4473static void rtFsIsoDirShrd_Log9660Content(PRTFSISODIRSHRD pThis)
4474{
4475 if (LogIs2Enabled())
4476 {
4477 uint32_t offRec = 0;
4478 while (offRec < pThis->cbDir)
4479 {
4480 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
4481 if (pDirRec->cbDirRec == 0)
4482 break;
4483
4484 RTUTF16 wszName[128];
4485 if (pThis->Core.pVol->fIsUtf16)
4486 {
4487 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
4488 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
4489 pwszSrc--;
4490 *pwszDst-- = '\0';
4491 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
4492 {
4493 *pwszDst = RT_BE2H_U16(*pwszSrc);
4494 pwszDst--;
4495 pwszSrc--;
4496 }
4497 }
4498 else
4499 {
4500 PRTUTF16 pwszDst = wszName;
4501 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
4502 *pwszDst++ = pDirRec->achFileId[off];
4503 *pwszDst = '\0';
4504 }
4505
4506 Log2(("ISO9660: %04x: rec=%#x ea=%#x cb=%#010RX32 off=%#010RX32 fl=%#04x %04u-%02u-%02u %02u:%02u:%02u%+03d unit=%#x igap=%#x idVol=%#x '%ls'\n",
4507 offRec,
4508 pDirRec->cbDirRec,
4509 pDirRec->cExtAttrBlocks,
4510 ISO9660_GET_ENDIAN(&pDirRec->cbData),
4511 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
4512 pDirRec->fFileFlags,
4513 pDirRec->RecTime.bYear + 1900,
4514 pDirRec->RecTime.bMonth,
4515 pDirRec->RecTime.bDay,
4516 pDirRec->RecTime.bHour,
4517 pDirRec->RecTime.bMinute,
4518 pDirRec->RecTime.bSecond,
4519 pDirRec->RecTime.offUtc*4/60,
4520 pDirRec->bFileUnitSize,
4521 pDirRec->bInterleaveGapSize,
4522 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
4523 wszName));
4524
4525 uint32_t offSysUse = RT_UOFFSETOF_DYN(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength])
4526 + !(pDirRec->bFileIdLength & 1);
4527 if (offSysUse < pDirRec->cbDirRec)
4528 {
4529 Log2(("ISO9660: system use (%#x bytes):\n%.*RhxD\n", pDirRec->cbDirRec - offSysUse,
4530 pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
4531 }
4532
4533 /* advance */
4534 offRec += pDirRec->cbDirRec;
4535 }
4536 }
4537}
4538#endif /* LOG_ENABLED */
4539
4540
4541/**
4542 * Instantiates a new shared directory structure, given 9660 records.
4543 *
4544 * @returns IPRT status code.
4545 * @param pThis The ISO volume instance.
4546 * @param pParentDir The parent directory. This is NULL for the root
4547 * directory.
4548 * @param pDirRec The directory record. Will access @a cDirRecs
4549 * records.
4550 * @param cDirRecs Number of directory records if more than one.
4551 * @param offDirRec The byte offset of the directory record.
4552 * @param pRockInfo Optional pointer to rock ridge info for the entry.
4553 * @param ppShared Where to return the shared directory structure.
4554 */
4555static int rtFsIsoDirShrd_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
4556 uint32_t cDirRecs, uint64_t offDirRec, PCRTFSISOROCKINFO pRockInfo, PRTFSISODIRSHRD *ppShared)
4557{
4558 /*
4559 * Allocate a new structure and initialize it.
4560 */
4561 int rc = VERR_NO_MEMORY;
4562 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
4563 if (pShared)
4564 {
4565 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pRockInfo, pThis);
4566 if (RT_SUCCESS(rc))
4567 {
4568 RTListInit(&pShared->OpenChildren);
4569 pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
4570 pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
4571 if (pShared->pbDir)
4572 {
4573 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.off, pShared->pbDir, pShared->cbDir, NULL);
4574 if (RT_SUCCESS(rc))
4575 {
4576#ifdef LOG_ENABLED
4577 rtFsIsoDirShrd_Log9660Content(pShared);
4578#endif
4579
4580 /*
4581 * If this is the root directory, check if rock ridge info is present.
4582 */
4583 if ( !pParentDir
4584 && !(pThis->fFlags & RTFSISO9660_F_NO_ROCK)
4585 && pShared->cbDir > RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
4586 {
4587 PCISO9660DIRREC pDirRec0 = (PCISO9660DIRREC)pShared->pbDir;
4588 if ( pDirRec0->bFileIdLength == 1
4589 && pDirRec0->achFileId[0] == 0
4590 && pDirRec0->cbDirRec > RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
4591 rtFsIsoDirShrd_ParseRockForRoot(pShared, pDirRec0);
4592 }
4593
4594 /*
4595 * Link into parent directory so we can use it to update
4596 * our directory entry.
4597 */
4598 if (pParentDir)
4599 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
4600 *ppShared = pShared;
4601 return VINF_SUCCESS;
4602 }
4603 }
4604 else
4605 rc = VERR_NO_MEMORY;
4606 }
4607 RTMemFree(pShared);
4608 }
4609 *ppShared = NULL;
4610 return rc;
4611}
4612
4613
4614#ifdef LOG_ENABLED
4615/**
4616 * Logs the content of a directory.
4617 */
4618static void rtFsIsoDirShrd_LogUdfContent(PRTFSISODIRSHRD pThis)
4619{
4620 if (LogIs2Enabled())
4621 {
4622 uint32_t offDesc = 0;
4623 while (offDesc + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) < pThis->cbDir)
4624 {
4625 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
4626 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
4627 if (offDesc + cbFid > pThis->cbDir)
4628 break;
4629
4630 uint32_t cwcName = 0;
4631 RTUTF16 wszName[260];
4632 if (pFid->cbName > 0)
4633 {
4634 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
4635 uint32_t offSrc = 1;
4636 if (*pbName == 8)
4637 while (offSrc < pFid->cbName)
4638 {
4639 wszName[cwcName] = pbName[offSrc];
4640 cwcName++;
4641 offSrc++;
4642 }
4643 else if (*pbName == 16)
4644 while (offSrc + 1 <= pFid->cbName)
4645 {
4646 wszName[cwcName] = RT_MAKE_U16(pbName[offSrc + 1], pbName[offSrc]);
4647 cwcName++;
4648 offSrc += 2;
4649 }
4650 else
4651 {
4652 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<bad type>");
4653 cwcName = 10;
4654 }
4655 }
4656 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
4657 {
4658 wszName[0] = '.';
4659 wszName[1] = '.';
4660 cwcName = 2;
4661 }
4662 else
4663 {
4664 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<empty>");
4665 cwcName = 7;
4666 }
4667 wszName[cwcName] = '\0';
4668
4669 Log2(("ISO/UDF: %04x: fFlags=%#x uVer=%u Icb={%#04x:%#010RX32 LB %#06x t=%u} cbName=%#04x cbIU=%#x '%ls'\n",
4670 offDesc,
4671 pFid->fFlags,
4672 pFid->uVersion,
4673 pFid->Icb.Location.uPartitionNo,
4674 pFid->Icb.Location.off,
4675 pFid->Icb.cb,
4676 pFid->Icb.uType,
4677 pFid->cbName,
4678 pFid->cbImplementationUse,
4679 wszName));
4680 int rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFid->Tag, pThis->cbDir - offDesc,
4681 UDF_TAG_ID_FILE_ID_DESC, pFid->Tag.offTag, NULL);
4682 if (RT_FAILURE(rc))
4683 Log2(("ISO/UDF: Bad Tag: %Rrc - idTag=%#x\n", rc, pFid->Tag.idTag));
4684 if (pFid->cbImplementationUse > 32)
4685 Log2(("ISO/UDF: impl use (%#x bytes):\n%.*RhxD\n",
4686 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
4687 else if (pFid->cbImplementationUse > 0)
4688 Log2(("ISO/UDF: impl use (%#x bytes): %.*Rhxs\n",
4689 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
4690
4691 /* advance */
4692 offDesc += cbFid;
4693 }
4694
4695 if (offDesc < pThis->cbDir)
4696 Log2(("ISO/UDF: warning! %#x trailing bytes in directory:\n%.*RhxD\n",
4697 pThis->cbDir - offDesc, pThis->cbDir - offDesc, &pThis->pbDir[offDesc]));
4698 }
4699}
4700#endif /* LOG_ENABLED */
4701
4702
4703/**
4704 * Instantiates a new shared directory structure, given UDF descriptors.
4705 *
4706 * @returns IPRT status code.
4707 * @param pThis The ISO volume instance.
4708 * @param pParentDir The parent directory. This is NULL for the root
4709 * directory.
4710 * @param pAllocDesc The allocation descriptor for the directory ICB.
4711 * @param pFileIdDesc The file ID descriptor. This is NULL for the root.
4712 * @param offInDir The offset of the file ID descriptor in the parent
4713 * directory. This is used when looking up shared
4714 * directory objects. (Pass 0 for root.)
4715 * @param ppShared Where to return the shared directory structure.
4716 */
4717static int rtFsIsoDirShrd_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFLONGAD pAllocDesc,
4718 PCUDFFILEIDDESC pFileIdDesc, uintptr_t offInDir, PRTFSISODIRSHRD *ppShared)
4719{
4720 /*
4721 * Allocate a new structure and initialize it.
4722 */
4723 int rc = VERR_NO_MEMORY;
4724 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
4725 if (pShared)
4726 {
4727 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, pAllocDesc, pFileIdDesc, offInDir, pThis);
4728 if (RT_SUCCESS(rc))
4729 {
4730 RTListInit(&pShared->OpenChildren);
4731
4732 if (pShared->Core.cbObject < RTFSISO_MAX_DIR_SIZE)
4733 {
4734 pShared->cbDir = (uint32_t)pShared->Core.cbObject;
4735 pShared->pbDir = (uint8_t *)RTMemAllocZ(RT_MAX(RT_ALIGN_32(pShared->cbDir, 512), 512));
4736 if (pShared->pbDir)
4737 {
4738 rc = rtFsIsoCore_ReadWorker(&pShared->Core, 0, pShared->pbDir, pShared->cbDir, NULL, NULL);
4739 if (RT_SUCCESS(rc))
4740 {
4741#ifdef LOG_ENABLED
4742 rtFsIsoDirShrd_LogUdfContent(pShared);
4743#endif
4744
4745 /*
4746 * Link into parent directory so we can use it to update
4747 * our directory entry.
4748 */
4749 if (pParentDir)
4750 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
4751 *ppShared = pShared;
4752 return VINF_SUCCESS;
4753 }
4754 }
4755 else
4756 rc = VERR_NO_MEMORY;
4757 }
4758 }
4759 RTMemFree(pShared);
4760 }
4761
4762 *ppShared = NULL;
4763 return rc;
4764}
4765
4766
4767/**
4768 * Instantiates a new directory with a shared structure presupplied.
4769 *
4770 * @returns IPRT status code.
4771 * @param pThis The ISO volume instance.
4772 * @param pShared Referenced pointer to the shared structure. The
4773 * reference is always CONSUMED.
4774 * @param phVfsDir Where to return the directory handle.
4775 */
4776static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir)
4777{
4778 /*
4779 * Create VFS object around the shared structure.
4780 */
4781 PRTFSISODIROBJ pNewDir;
4782 int rc = RTVfsNewDir(&g_rtFsIsoDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
4783 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
4784 if (RT_SUCCESS(rc))
4785 {
4786 /*
4787 * Look for existing shared object, create a new one if necessary.
4788 * We CONSUME a reference to pShared here.
4789 */
4790 pNewDir->offDir = 0;
4791 pNewDir->pShared = pShared;
4792 return VINF_SUCCESS;
4793 }
4794
4795 rtFsIsoDirShrd_Release(pShared);
4796 *phVfsDir = NIL_RTVFSDIR;
4797 return rc;
4798}
4799
4800
4801
4802/**
4803 * Instantiates a new directory VFS instance for ISO 9660, creating the shared
4804 * structure as necessary.
4805 *
4806 * @returns IPRT status code.
4807 * @param pThis The ISO volume instance.
4808 * @param pParentDir The parent directory. This is NULL for the root
4809 * directory.
4810 * @param pDirRec The directory record.
4811 * @param cDirRecs Number of directory records if more than one.
4812 * @param offDirRec The byte offset of the directory record.
4813 * @param pRockInfo Optional pointer to rock ridge info for the entry.
4814 * @param phVfsDir Where to return the directory handle.
4815 */
4816static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
4817 uint32_t cDirRecs, uint64_t offDirRec, PCRTFSISOROCKINFO pRockInfo, PRTVFSDIR phVfsDir)
4818{
4819 /*
4820 * Look for existing shared object, create a new one if necessary.
4821 */
4822 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
4823 if (!pShared)
4824 {
4825 int rc = rtFsIsoDirShrd_New9660(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, pRockInfo, &pShared);
4826 if (RT_FAILURE(rc))
4827 {
4828 *phVfsDir = NIL_RTVFSDIR;
4829 return rc;
4830 }
4831 }
4832 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
4833}
4834
4835
4836/**
4837 * Instantiates a new directory VFS instance for UDF, creating the shared
4838 * structure as necessary.
4839 *
4840 * @returns IPRT status code.
4841 * @param pThis The ISO volume instance.
4842 * @param pParentDir The parent directory.
4843 * @param pFid The file ID descriptor for the directory.
4844 * @param phVfsDir Where to return the directory handle.
4845 */
4846static int rtFsIsoDir_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid, PRTVFSDIR phVfsDir)
4847{
4848 Assert(pFid);
4849 Assert(pParentDir);
4850 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
4851 Assert(offInDir < pParentDir->cbDir);
4852
4853 /*
4854 * Look for existing shared object, create a new one if necessary.
4855 */
4856 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
4857 if (!pShared)
4858 {
4859 int rc = rtFsIsoDirShrd_NewUdf(pThis, pParentDir, &pFid->Icb, pFid, offInDir, &pShared);
4860 if (RT_FAILURE(rc))
4861 {
4862 *phVfsDir = NIL_RTVFSDIR;
4863 return rc;
4864 }
4865 }
4866 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
4867}
4868
4869
4870/**
4871 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
4872 */
4873static DECLCALLBACK(int) rtFsIsoVol_Close(void *pvThis)
4874{
4875 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
4876 Log(("rtFsIsoVol_Close(%p)\n", pThis));
4877
4878 if (pThis->pRootDir)
4879 {
4880 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
4881 Assert(pThis->pRootDir->Core.cRefs == 1);
4882 rtFsIsoDirShrd_Release(pThis->pRootDir);
4883 pThis->pRootDir = NULL;
4884 }
4885
4886 RTVfsFileRelease(pThis->hVfsBacking);
4887 pThis->hVfsBacking = NIL_RTVFSFILE;
4888
4889 if (RTCritSectIsInitialized(&pThis->RockBufLock))
4890 RTCritSectDelete(&pThis->RockBufLock);
4891
4892 return VINF_SUCCESS;
4893}
4894
4895
4896/**
4897 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
4898 */
4899static DECLCALLBACK(int) rtFsIsoVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4900{
4901 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
4902 return VERR_WRONG_TYPE;
4903}
4904
4905
4906static int rtFsIsoVol_ReturnUdfDString(const char *pachSrc, size_t cchSrc, void *pvDst, size_t cbDst, size_t *pcbRet)
4907{
4908 char *pszDst = (char *)pvDst;
4909
4910 if (pachSrc[0] == 8)
4911 {
4912 size_t const cchText = RT_MIN((uint8_t)pachSrc[cchSrc - 1], cchSrc - 2);
4913 size_t const cchActual = RTStrNLen(&pachSrc[1], cchText);
4914 *pcbRet = cchActual + 1;
4915 int rc = RTStrCopyEx(pszDst, cbDst, &pachSrc[1], cchActual);
4916 if (cbDst > 0)
4917 RTStrPurgeEncoding(pszDst);
4918 return rc;
4919 }
4920
4921 if (pachSrc[0] == 16)
4922 {
4923 PCRTUTF16 pwszSrc = (PCRTUTF16)&pachSrc[1];
4924 if (cchSrc > 0)
4925 return RTUtf16BigToUtf8Ex(pwszSrc, (cchSrc - 2) / sizeof(RTUTF16), &pszDst, cchSrc, pcbRet);
4926 int rc = RTUtf16CalcUtf8LenEx(pwszSrc, (cchSrc - 2) / sizeof(RTUTF16), pcbRet);
4927 if (RT_SUCCESS(rc))
4928 {
4929 *pcbRet += 1;
4930 return VERR_BUFFER_OVERFLOW;
4931 }
4932 return rc;
4933 }
4934
4935 if (ASMMemIsZero(pachSrc, cchSrc))
4936 {
4937 *pcbRet = 1;
4938 if (cbDst >= 1)
4939 {
4940 *pszDst = '\0';
4941 return VINF_SUCCESS;
4942 }
4943 return VERR_BUFFER_OVERFLOW;
4944 }
4945
4946 *pcbRet = 0;
4947 return VERR_INVALID_UTF8_ENCODING; /** @todo better status here */
4948}
4949
4950
4951/**
4952 * For now this is a sanitized version of rtFsIsoVolGetMaybeUtf16Be, which is
4953 * probably not correct or anything, but will have to do for now.
4954 */
4955static int rtFsIsoVol_ReturnIso9660D1String(const char *pachSrc, size_t cchSrc, void *pvDst, size_t cbDst, size_t *pcbRet)
4956{
4957 char *pszDst = (char *)pvDst;
4958
4959 /*
4960 * Check if it may be some UTF16 variant by scanning for zero bytes
4961 * (ISO-9660 doesn't allow zeros).
4962 */
4963 size_t cFirstZeros = 0;
4964 size_t cSecondZeros = 0;
4965 for (size_t off = 0; off + 1 < cchSrc; off += 2)
4966 {
4967 cFirstZeros += pachSrc[off] == '\0';
4968 cSecondZeros += pachSrc[off + 1] == '\0';
4969 }
4970 if (cFirstZeros > cSecondZeros)
4971 {
4972 /*
4973 * UTF-16BE / UTC-2BE:
4974 */
4975 if (cchSrc & 1)
4976 {
4977 AssertReturn(pachSrc[cchSrc - 1] == '\0' || pachSrc[cchSrc - 1] == ' ', VERR_INVALID_UTF16_ENCODING);
4978 cchSrc--;
4979 }
4980 while ( cchSrc >= 2
4981 && pachSrc[cchSrc - 1] == ' '
4982 && pachSrc[cchSrc - 2] == '\0')
4983 cchSrc -= 2;
4984
4985 if (cbDst > 0)
4986 return RTUtf16BigToUtf8Ex((PCRTUTF16)pachSrc, cchSrc / sizeof(RTUTF16), &pszDst, cbDst, pcbRet);
4987 int rc = RTUtf16BigCalcUtf8LenEx((PCRTUTF16)pachSrc, cchSrc / sizeof(RTUTF16), pcbRet);
4988 if (RT_SUCCESS(rc))
4989 {
4990 *pcbRet += 1;
4991 return VERR_BUFFER_OVERFLOW;
4992 }
4993 return rc;
4994 }
4995
4996 if (cSecondZeros > 0)
4997 {
4998 /*
4999 * Little endian UTF-16 / UCS-2.
5000 */
5001 if (cchSrc & 1)
5002 {
5003 AssertReturn(pachSrc[cchSrc - 1] == '\0' || pachSrc[cchSrc - 1] == ' ', VERR_INVALID_UTF16_ENCODING);
5004 cchSrc--;
5005 }
5006 while ( cchSrc >= 2
5007 && pachSrc[cchSrc - 1] == '\0'
5008 && pachSrc[cchSrc - 2] == ' ')
5009 cchSrc -= 2;
5010
5011 if (cbDst)
5012 return RTUtf16LittleToUtf8Ex((PCRTUTF16)pachSrc, cchSrc / sizeof(RTUTF16), &pszDst, cbDst, pcbRet);
5013 int rc = RTUtf16LittleCalcUtf8LenEx((PCRTUTF16)pachSrc, cchSrc / sizeof(RTUTF16), pcbRet);
5014 if (RT_SUCCESS(rc))
5015 {
5016 *pcbRet += 1;
5017 return VERR_BUFFER_OVERFLOW;
5018 }
5019 return rc;
5020 }
5021
5022 /*
5023 * ASSUME UTF-8/ASCII.
5024 */
5025 while ( cchSrc > 0
5026 && pachSrc[cchSrc - 1] == ' ')
5027 cchSrc--;
5028
5029 *pcbRet = cchSrc + 1;
5030 int rc = RTStrCopyEx(pszDst, cbDst, pachSrc, cchSrc);
5031 if (cbDst > 0)
5032 RTStrPurgeEncoding(pszDst);
5033 return rc;
5034}
5035
5036
5037static int rtFsIsoVol_ReturnIso9660DString(const char *pachSrc, size_t cchSrc, void *pvDst, size_t cbDst, size_t *pcbRet)
5038{
5039 /* Lazy bird: */
5040 return rtFsIsoVol_ReturnIso9660D1String(pachSrc, cchSrc, pvDst, cbDst, pcbRet);
5041}
5042
5043
5044/**
5045 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfoEx}
5046 */
5047static DECLCALLBACK(int) rtFsIsoVol_QueryInfoEx(void *pvThis, RTVFSQIEX enmInfo, void *pvInfo, size_t cbInfo, size_t *pcbRet)
5048{
5049 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
5050 LogFlow(("rtFsIsoVol_QueryInfo(%p, %d,, %#zx,)\n", pThis, enmInfo, cbInfo));
5051
5052 union
5053 {
5054 uint8_t ab[RTFSISO_MAX_LOGICAL_BLOCK_SIZE];
5055 ISO9660PRIMARYVOLDESC PriVolDesc;
5056 ISO9660SUPVOLDESC SupVolDesc;
5057 } uBuf;
5058
5059 switch (enmInfo)
5060 {
5061 case RTVFSQIEX_VOL_LABEL:
5062 case RTVFSQIEX_VOL_LABEL_ALT:
5063 {
5064 if (pThis->enmType == RTFSISOVOLTYPE_UDF
5065 && ( enmInfo == RTVFSQIEX_VOL_LABEL
5066 || pThis->offPrimaryVolDesc == 0))
5067 return rtFsIsoVol_ReturnUdfDString(pThis->Udf.VolInfo.achLogicalVolumeID,
5068 sizeof(pThis->Udf.VolInfo.achLogicalVolumeID), pvInfo, cbInfo, pcbRet);
5069
5070 bool const fPrimary = enmInfo == RTVFSQIEX_VOL_LABEL_ALT
5071 || pThis->enmType == RTFSISOVOLTYPE_ISO9960;
5072
5073 int rc = RTVfsFileReadAt(pThis->hVfsBacking,
5074 fPrimary ? pThis->offPrimaryVolDesc : pThis->offSecondaryVolDesc,
5075 uBuf.ab, RT_MAX(RT_MIN(pThis->cbSector, sizeof(uBuf)), sizeof(uBuf.PriVolDesc)), NULL);
5076 AssertRCReturn(rc, rc);
5077
5078 if (fPrimary)
5079 return rtFsIsoVol_ReturnIso9660DString(uBuf.PriVolDesc.achVolumeId, sizeof(uBuf.PriVolDesc.achVolumeId),
5080 pvInfo, cbInfo, pcbRet);
5081 return rtFsIsoVol_ReturnIso9660D1String(uBuf.SupVolDesc.achVolumeId, sizeof(uBuf.SupVolDesc.achVolumeId),
5082 pvInfo, cbInfo, pcbRet);
5083 }
5084
5085 default:
5086 return VERR_NOT_SUPPORTED;
5087
5088 }
5089}
5090
5091
5092/**
5093 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
5094 */
5095static DECLCALLBACK(int) rtFsIsoVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
5096{
5097 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
5098
5099 rtFsIsoDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
5100 return rtFsIsoDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
5101}
5102
5103
5104/**
5105 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
5106 */
5107static DECLCALLBACK(int) rtFsIsoVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
5108{
5109 RT_NOREF(pvThis, off, cb, pfUsed);
5110 return VERR_NOT_IMPLEMENTED;
5111}
5112
5113
5114DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIsoVolOps =
5115{
5116 { /* Obj */
5117 RTVFSOBJOPS_VERSION,
5118 RTVFSOBJTYPE_VFS,
5119 "ISO 9660/UDF",
5120 rtFsIsoVol_Close,
5121 rtFsIsoVol_QueryInfo,
5122 rtFsIsoVol_QueryInfoEx,
5123 RTVFSOBJOPS_VERSION
5124 },
5125 RTVFSOPS_VERSION,
5126 0 /* fFeatures */,
5127 rtFsIsoVol_OpenRoot,
5128 rtFsIsoVol_QueryRangeState,
5129 RTVFSOPS_VERSION
5130};
5131
5132
5133/**
5134 * Checks the descriptor tag and CRC.
5135 *
5136 * @retval IPRT status code.
5137 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
5138 * @retval VERR_MISMATCH
5139 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
5140 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
5141 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
5142 *
5143 * @param pTag The tag to check.
5144 * @param idTag The expected descriptor tag ID, UINT16_MAX matches any
5145 * tag ID.
5146 * @param offTag The sector offset of the tag.
5147 * @param pErrInfo Where to return extended error info.
5148 */
5149static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
5150{
5151 /*
5152 * Checksum the tag first.
5153 */
5154 const uint8_t *pbTag = (const uint8_t *)pTag;
5155 uint8_t const bChecksum = pbTag[0]
5156 + pbTag[1]
5157 + pbTag[2]
5158 + pbTag[3]
5159 + pbTag[5] /* skipping byte 4 as that's the checksum. */
5160 + pbTag[6]
5161 + pbTag[7]
5162 + pbTag[8]
5163 + pbTag[9]
5164 + pbTag[10]
5165 + pbTag[11]
5166 + pbTag[12]
5167 + pbTag[13]
5168 + pbTag[14]
5169 + pbTag[15];
5170 if (pTag->uChecksum == bChecksum)
5171 {
5172 /*
5173 * Do the matching.
5174 */
5175 if ( pTag->uVersion == 3
5176 || pTag->uVersion == 2)
5177 {
5178 if ( pTag->idTag == idTag
5179 || idTag == UINT16_MAX)
5180 {
5181 if (pTag->offTag == offTag)
5182 {
5183 //Log3(("ISO/UDF: Valid descriptor %#06x at %#010RX32; cbDescriptorCrc=%#06RX32 uTagSerialNo=%#x\n",
5184 // pTag->idTag, offTag, pTag->cbDescriptorCrc, pTag->uTagSerialNo));
5185 return VINF_SUCCESS;
5186 }
5187
5188 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Sector mismatch: %#RX32 (%.*Rhxs)\n",
5189 idTag, offTag, pTag->offTag, sizeof(*pTag), pTag));
5190 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TAG_SECTOR_MISMATCH,
5191 "Descriptor tag sector number mismatch: %#x, expected %#x (%.*Rhxs)",
5192 pTag->offTag, offTag, sizeof(*pTag), pTag);
5193 }
5194 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Tag ID mismatch: %#x (%.*Rhxs)\n",
5195 idTag, offTag, pTag->idTag, sizeof(*pTag), pTag));
5196 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_MISMATCH, "Descriptor tag ID mismatch: %#x, expected %#x (%.*Rhxs)",
5197 pTag->idTag, idTag, sizeof(*pTag), pTag);
5198 }
5199 if (ASMMemIsZero(pTag, sizeof(*pTag)))
5200 {
5201 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): All zeros\n", idTag, offTag));
5202 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TAG_IS_ALL_ZEROS, "Descriptor is all zeros");
5203 }
5204
5205 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Unsupported version: %#x (%.*Rhxs)\n",
5206 idTag, offTag, pTag->uVersion, sizeof(*pTag), pTag));
5207 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_TAG_VERSION, "Unsupported descriptor tag version: %#x, expected 2 or 3 (%.*Rhxs)",
5208 pTag->uVersion, sizeof(*pTag), pTag);
5209 }
5210 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): checksum error: %#x, calc %#x (%.*Rhxs)\n",
5211 idTag, offTag, pTag->uChecksum, bChecksum, sizeof(*pTag), pTag));
5212 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_TAG_CHECKSUM,
5213 "Descriptor tag checksum error: %#x, calculated %#x (%.*Rhxs)",
5214 pTag->uChecksum, bChecksum, sizeof(*pTag), pTag);
5215}
5216
5217
5218/**
5219 * Checks the descriptor CRC.
5220 *
5221 * @retval VINF_SUCCESS
5222 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
5223 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
5224 *
5225 * @param pTag The descriptor buffer to checksum.
5226 * @param cbDesc The size of the descriptor buffer.
5227 * @param pErrInfo Where to return extended error info.
5228 */
5229static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo)
5230{
5231 if (pTag->cbDescriptorCrc + sizeof(*pTag) <= cbDesc)
5232 {
5233 uint16_t uCrc = RTCrc16Ccitt(pTag + 1, pTag->cbDescriptorCrc);
5234 if (pTag->uDescriptorCrc == uCrc)
5235 return VINF_SUCCESS;
5236
5237 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Descriptor CRC mismatch: expected %#x, calculated %#x (cbDescriptorCrc=%#x)\n",
5238 pTag->idTag, pTag->offTag, pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc));
5239 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_DESC_CRC_MISMATCH,
5240 "Descriptor CRC mismatch: exepcted %#x, calculated %#x (cbDescriptor=%#x, idTag=%#x, offTag=%#010RX32)",
5241 pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc, pTag->idTag, pTag->offTag);
5242 }
5243
5244 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx\n",
5245 pTag->idTag, pTag->offTag, pTag->cbDescriptorCrc, cbDesc));
5246 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC,
5247 "Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx (idTag=%#x, offTag=%#010RX32)",
5248 pTag->cbDescriptorCrc, cbDesc, pTag->idTag, pTag->offTag);
5249}
5250
5251
5252/**
5253 * Checks the descriptor tag and CRC.
5254 *
5255 * @retval VINF_SUCCESS
5256 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
5257 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
5258 * @retval VERR_MISMATCH
5259 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
5260 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
5261 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
5262 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
5263 *
5264 * @param pTag The descriptor buffer to check the tag of and to
5265 * checksum.
5266 * @param cbDesc The size of the descriptor buffer.
5267 * @param idTag The expected descriptor tag ID, UINT16_MAX
5268 * matches any tag ID.
5269 * @param offTag The sector offset of the tag.
5270 * @param pErrInfo Where to return extended error info.
5271 */
5272static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
5273{
5274 int rc = rtFsIsoVolValidateUdfDescTag(pTag, idTag, offTag, pErrInfo);
5275 if (RT_SUCCESS(rc))
5276 rc = rtFsIsoVolValidateUdfDescCrc(pTag, cbDesc, pErrInfo);
5277 return rc;
5278}
5279
5280
5281
5282
5283static int rtFsIsoVolProcessUdfFileSetDescs(PRTFSISOVOL pThis, uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
5284{
5285
5286 /*
5287 * We assume there is a single file descriptor and don't bother checking what comes next.
5288 */
5289 PUDFFILESETDESC pFsd = (PUDFFILESETDESC)pbBuf;
5290 Assert(cbBuf > sizeof(*pFsd)); NOREF(cbBuf);
5291 RT_ZERO(*pFsd);
5292 size_t cbToRead = RT_MAX(pThis->Udf.VolInfo.FileSetDescriptor.cb, sizeof(*pFsd));
5293 int rc = rtFsIsoVolUdfVpRead(pThis, pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
5294 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, 0, pFsd, cbToRead);
5295 if (RT_SUCCESS(rc))
5296 {
5297 rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFsd->Tag, cbToRead, UDF_TAG_ID_FILE_SET_DESC,
5298 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, pErrInfo);
5299 if (RT_SUCCESS(rc))
5300 {
5301#ifdef LOG_ENABLED
5302 Log(("ISO/UDF: File set descriptor at %#RX32 (%#RX32:%#RX32)\n", pFsd->Tag.offTag,
5303 pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
5304 pThis->Udf.VolInfo.FileSetDescriptor.Location.off));
5305 if (LogIs2Enabled())
5306 {
5307 UDF_LOG2_MEMBER_TIMESTAMP(pFsd, RecordingTimestamp);
5308 UDF_LOG2_MEMBER(pFsd, "#06RX16", uInterchangeLevel);
5309 UDF_LOG2_MEMBER(pFsd, "#06RX16", uMaxInterchangeLevel);
5310 UDF_LOG2_MEMBER(pFsd, "#010RX32", fCharacterSets);
5311 UDF_LOG2_MEMBER(pFsd, "#010RX32", fMaxCharacterSets);
5312 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetNo);
5313 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetDescNo);
5314 UDF_LOG2_MEMBER_CHARSPEC(pFsd, LogicalVolumeIDCharSet);
5315 UDF_LOG2_MEMBER_DSTRING(pFsd, achLogicalVolumeID);
5316 UDF_LOG2_MEMBER_CHARSPEC(pFsd, FileSetCharSet);
5317 UDF_LOG2_MEMBER_DSTRING(pFsd, achFileSetID);
5318 UDF_LOG2_MEMBER_DSTRING(pFsd, achCopyrightFile);
5319 UDF_LOG2_MEMBER_DSTRING(pFsd, achAbstractFile);
5320 UDF_LOG2_MEMBER_LONGAD(pFsd, RootDirIcb);
5321 UDF_LOG2_MEMBER_ENTITY_ID(pFsd, idDomain);
5322 UDF_LOG2_MEMBER_LONGAD(pFsd, NextExtent);
5323 UDF_LOG2_MEMBER_LONGAD(pFsd, SystemStreamDirIcb);
5324 if (!ASMMemIsZero(&pFsd->abReserved[0], sizeof(pFsd->abReserved)))
5325 UDF_LOG2_MEMBER(pFsd, ".32Rhxs", abReserved);
5326 }
5327#endif
5328
5329 /*
5330 * Do some basic sanity checking.
5331 */
5332 if (!UDF_IS_CHAR_SET_OSTA(&pFsd->FileSetCharSet))
5333 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_UNSUPPORTED_CHAR_SET,
5334 "Invalid file set charset %.64Rhxs", &pFsd->FileSetCharSet);
5335 if ( pFsd->RootDirIcb.cb == 0
5336 || pFsd->RootDirIcb.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
5337 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_ZERO_ROOT_DIR,
5338 "Root Dir ICB location is zero or malformed: uType=%#x cb=%#x loc=%#x:%#RX32",
5339 pFsd->RootDirIcb.uType, pFsd->RootDirIcb.cb,
5340 pFsd->RootDirIcb.Location.uPartitionNo, pFsd->RootDirIcb.Location.off);
5341 if ( pFsd->NextExtent.cb != 0
5342 && pFsd->NextExtent.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
5343 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_NEXT_EXTENT,
5344 "NextExtent isn't zero: uType=%#x cb=%#x loc=%#x:%#RX32",
5345 pFsd->NextExtent.uType, pFsd->NextExtent.cb,
5346 pFsd->NextExtent.Location.uPartitionNo, pFsd->NextExtent.Location.off);
5347
5348 /*
5349 * Copy the information we need.
5350 */
5351 pThis->Udf.VolInfo.RootDirIcb = pFsd->RootDirIcb;
5352 if ( pFsd->SystemStreamDirIcb.cb > 0
5353 && pFsd->SystemStreamDirIcb.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
5354 pThis->Udf.VolInfo.SystemStreamDirIcb = pFsd->SystemStreamDirIcb;
5355 else
5356 RT_ZERO(pThis->Udf.VolInfo.SystemStreamDirIcb);
5357 return VINF_SUCCESS;
5358 }
5359 return rc;
5360 }
5361 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading file set descriptor");
5362}
5363
5364
5365/**
5366 * Check validatity and extract information from the descriptors in the VDS seq.
5367 *
5368 * @returns IPRT status code
5369 * @param pThis The instance.
5370 * @param pInfo The VDS sequence info.
5371 * @param pErrInfo Where to return extended error info.
5372 */
5373static int rtFsIsoVolProcessUdfVdsSeqInfo(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, PRTERRINFO pErrInfo)
5374{
5375 /*
5376 * Check the basic descriptor counts.
5377 */
5378 PUDFPRIMARYVOLUMEDESC pPvd;
5379 if (pInfo->cPrimaryVols == 1)
5380 pPvd = pInfo->apPrimaryVols[0];
5381 else
5382 {
5383 if (pInfo->cPrimaryVols == 0)
5384 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PVD, "No primary volume descriptor was found");
5385 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_PVDS,
5386 "More than one primary volume descriptor was found: %u", pInfo->cPrimaryVols);
5387 }
5388
5389 PUDFLOGICALVOLUMEDESC pLvd;
5390 if (pInfo->cLogicalVols == 1)
5391 pLvd = pInfo->apLogicalVols[0];
5392 else
5393 {
5394 if (pInfo->cLogicalVols == 0)
5395 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_LVD, "No logical volume descriptor was found");
5396 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_LVDS,
5397 "More than one logical volume descriptor was found: %u", pInfo->cLogicalVols);
5398 }
5399
5400#if 0
5401 if (pInfo->cPartitions == 0)
5402 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PD, "No partition descriptors was found");
5403#endif
5404
5405 /*
5406 * Check out the partition map in the logical volume descriptor.
5407 * Produce the mapping table while going about that.
5408 */
5409 if (pLvd->cPartitionMaps > 64)
5410 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_MANY_PART_MAPS,
5411 "Too many partition maps: %u (max 64)", pLvd->cPartitionMaps);
5412
5413 PRTFSISOVOLUDFPMAP paPartMaps = NULL;
5414 if (pLvd->cPartitionMaps > 0)
5415 {
5416 pInfo->paPartMaps = paPartMaps = (PRTFSISOVOLUDFPMAP)RTMemAllocZ(sizeof(paPartMaps[0]) * pLvd->cPartitionMaps);
5417 if (!paPartMaps)
5418 return VERR_NO_MEMORY;
5419 }
5420 uint32_t cPartMaps = 0;
5421
5422 if (pLvd->cbMapTable)
5423 {
5424 uint32_t off = 0;
5425 while (off + sizeof(UDFPARTMAPHDR) <= pLvd->cbMapTable)
5426 {
5427 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pLvd->abPartitionMaps[off];
5428
5429 /*
5430 * Bounds checking.
5431 */
5432 if (off + pHdr->cb > pLvd->cbMapTable)
5433 {
5434 if (cPartMaps < pLvd->cbMapTable)
5435 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MALFORMED_PART_MAP_TABLE,
5436 "Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)",
5437 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType);
5438 LogRel(("ISO/UDF: Warning: Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)\n",
5439 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType));
5440 break;
5441 }
5442 if (cPartMaps >= pLvd->cPartitionMaps)
5443 {
5444 LogRel(("ISO/UDF: Warning: LVD::cPartitionMaps is %u but there are more bytes in the table. (off=%#x cb=%#x cbMapTable=%#x bType=%#x)\n",
5445 cPartMaps - pLvd->cPartitionMaps, off, pHdr->cb, pLvd->cbMapTable, pHdr->bType));
5446 break;
5447 }
5448
5449 /*
5450 * Extract relevant info out of the entry.
5451 */
5452 paPartMaps[cPartMaps].offMapTable = (uint16_t)off;
5453 uint16_t uPartitionNo;
5454 if (pHdr->bType == 1)
5455 {
5456 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
5457 paPartMaps[cPartMaps].uVolumeSeqNo = pType1->uVolumeSeqNo;
5458 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_PLAIN;
5459 uPartitionNo = pType1->uPartitionNo;
5460 }
5461 else if (pHdr->bType == 2)
5462 {
5463 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
5464 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_VPM_PARTITION_TYPE))
5465 {
5466 paPartMaps[cPartMaps].bType = pType2->idPartitionType.Suffix.Udf.uUdfRevision >= 0x200
5467 ? RTFSISO_UDF_PMAP_T_VPM_20 : RTFSISO_UDF_PMAP_T_VPM_15;
5468 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_VPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
5469 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
5470 }
5471 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
5472 {
5473 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_SPM;
5474 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_SPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
5475 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
5476 }
5477 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
5478 {
5479 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_MPM;
5480 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
5481 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
5482 }
5483 else
5484 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_TYPE_ID,
5485 "Unknown partition map ID for #%u @ %#x: %.23s",
5486 cPartMaps, off, pType2->idPartitionType.achIdentifier);
5487#if 0 /* unreachable code */
5488 paPartMaps[cPartMaps].uVolumeSeqNo = pType2->uVolumeSeqNo;
5489 uPartitionNo = pType2->uPartitionNo;
5490#endif
5491 }
5492 else
5493 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_ENTRY_TYPE,
5494 "Unknown partition map entry type #%u @ %#x: %u", cPartMaps, off, pHdr->bType);
5495 paPartMaps[cPartMaps].uPartitionNo = uPartitionNo;
5496
5497 /*
5498 * Lookup the partition number and retrieve the relevant info from the partition descriptor.
5499 */
5500 uint32_t i = pInfo->cPartitions;
5501 while (i-- > 0)
5502 {
5503 PUDFPARTITIONDESC pPd = pInfo->apPartitions[i];
5504 if (paPartMaps[cPartMaps].uPartitionNo == pPd->uPartitionNo)
5505 {
5506 paPartMaps[cPartMaps].idxPartDesc = (uint16_t)i;
5507 paPartMaps[cPartMaps].cSectors = pPd->cSectors;
5508 paPartMaps[cPartMaps].offLocation = pPd->offLocation;
5509 paPartMaps[cPartMaps].offByteLocation = (uint64_t)pPd->offLocation * pThis->cbSector;
5510 paPartMaps[cPartMaps].fFlags = pPd->fFlags;
5511 paPartMaps[cPartMaps].uAccessType = pPd->uAccessType;
5512 if (!UDF_ENTITY_ID_EQUALS(&pPd->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
5513 paPartMaps[cPartMaps].fHaveHdr = false;
5514 else
5515 {
5516 paPartMaps[cPartMaps].fHaveHdr = true;
5517 paPartMaps[cPartMaps].Hdr = pPd->ContentsUse.Hdr;
5518 }
5519 break;
5520 }
5521 }
5522 if (i > pInfo->cPartitions)
5523 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_PARTITION_NOT_FOUND,
5524 "Partition #%u (%#x) specified by mapping entry #%u (@ %#x) was not found! (int-type %u)",
5525 uPartitionNo, uPartitionNo, cPartMaps, off, paPartMaps[cPartMaps].bType);
5526
5527 /*
5528 * Advance.
5529 */
5530 cPartMaps++;
5531 off += pHdr->cb;
5532 }
5533
5534 if (cPartMaps < pLvd->cPartitionMaps)
5535 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INCOMPLETE_PART_MAP_TABLE,
5536 "Only found %u of the %u announced partition mapping table entries",
5537 cPartMaps, pLvd->cPartitionMaps);
5538 }
5539
5540 /* It might be theoretically possible to not use virtual partitions for
5541 accessing data, so just warn if there aren't any. */
5542 if (cPartMaps == 0)
5543 LogRel(("ISO/UDF: Warning: No partition maps!\n"));
5544
5545 /*
5546 * Check out the logical volume descriptor.
5547 */
5548 if ( pLvd->cbLogicalBlock < pThis->cbSector
5549 || pLvd->cbLogicalBlock > RTFSISO_MAX_LOGICAL_BLOCK_SIZE
5550 || (pLvd->cbLogicalBlock % pThis->cbSector) != 0)
5551 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_LOGICAL_BLOCK_SIZE,
5552 "Logical block size of %#x is not supported with a sector size of %#x",
5553 pLvd->cbLogicalBlock, pThis->cbSector);
5554
5555 if (!UDF_ENTITY_ID_EQUALS(&pLvd->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
5556 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DOMAIN_ID,
5557 "Unsupported domain ID in logical volume descriptor: '%.23s'", pLvd->idDomain.achIdentifier);
5558
5559 if ( pLvd->ContentsUse.FileSetDescriptor.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED
5560 || pLvd->ContentsUse.FileSetDescriptor.cb == 0
5561 || pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo >= cPartMaps)
5562 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_FILE_SET_DESC_LOCATION,
5563 "Malformed file set descriptor location (type=%u cb=%#x part=%#x)",
5564 pLvd->ContentsUse.FileSetDescriptor.uType,
5565 pLvd->ContentsUse.FileSetDescriptor.cb,
5566 pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo);
5567
5568 bool fLvdHaveVolId = !ASMMemIsZero(pLvd->achLogicalVolumeID, sizeof(pLvd->achLogicalVolumeID));
5569 if ( fLvdHaveVolId
5570 && !UDF_IS_CHAR_SET_OSTA(&pLvd->DescCharSet))
5571 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DESC_CHAR_SET,
5572 "Logical volume ID is not using OSTA compressed unicode");
5573
5574 /*
5575 * We can ignore much, if not all of the primary volume descriptor.
5576 */
5577
5578 /*
5579 * We're good. So copy over the data.
5580 */
5581 pThis->Udf.VolInfo.FileSetDescriptor = pLvd->ContentsUse.FileSetDescriptor;
5582 pThis->Udf.VolInfo.cbBlock = pLvd->cbLogicalBlock;
5583 pThis->Udf.VolInfo.cShiftBlock = 9;
5584 while (pThis->Udf.VolInfo.cbBlock != RT_BIT_32(pThis->Udf.VolInfo.cShiftBlock))
5585 pThis->Udf.VolInfo.cShiftBlock++;
5586 pThis->Udf.VolInfo.fFlags = pPvd->fFlags;
5587 pThis->Udf.VolInfo.cPartitions = cPartMaps;
5588 pThis->Udf.VolInfo.paPartitions = paPartMaps;
5589 pInfo->paPartMaps = NULL;
5590 if (fLvdHaveVolId)
5591 memcpy(pThis->Udf.VolInfo.achLogicalVolumeID, pLvd->achLogicalVolumeID, sizeof(pThis->Udf.VolInfo.achLogicalVolumeID));
5592 else
5593 RT_ZERO(pThis->Udf.VolInfo.achLogicalVolumeID);
5594
5595 return VINF_SUCCESS;
5596}
5597
5598
5599/**
5600 * Processes a primary volume descriptor in the VDS (UDF).
5601 *
5602 * @returns IPRT status code.
5603 * @param pInfo Where we gather descriptor information.
5604 * @param pDesc The descriptor.
5605 * @param pErrInfo Where to return extended error information.
5606 */
5607//cmd: kmk VBoxRT && kmk_redirect -E VBOX_LOG_DEST="nofile stderr" -E VBOX_LOG="rt_fs=~0" -E VBOX_LOG_FLAGS="unbuffered enabled" -- e:\vbox\svn\trunk\out\win.amd64\debug\bin\tools\RTLs.exe :iprtvfs:file(open,d:\Downloads\en_windows_10_enterprise_version_1703_updated_march_2017_x64_dvd_10189290.iso,r):vfs(isofs):/ -la
5608static int rtFsIsoVolProcessUdfPrimaryVolDesc(PRTFSISOVDSINFO pInfo, PCUDFPRIMARYVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
5609{
5610#ifdef LOG_ENABLED
5611 Log(("ISO/UDF: Primary volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5612 if (LogIs2Enabled())
5613 {
5614 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5615 UDF_LOG2_MEMBER(pDesc, "#010RX32", uPrimaryVolumeDescNo);
5616 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeID);
5617 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
5618 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
5619 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxVolumeSeqNo);
5620 UDF_LOG2_MEMBER(pDesc, "#06RX16", uInterchangeLevel);
5621 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxInterchangeLevel);
5622 UDF_LOG2_MEMBER(pDesc, "#010RX32", fCharacterSets);
5623 UDF_LOG2_MEMBER(pDesc, "#010RX32", fMaxCharacterSets);
5624 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeSetID);
5625 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
5626 UDF_LOG2_MEMBER_CHARSPEC(pDesc, ExplanatoryCharSet);
5627 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeAbstract);
5628 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeCopyrightNotice);
5629 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idApplication);
5630 UDF_LOG2_MEMBER_TIMESTAMP(pDesc, RecordingTimestamp);
5631 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5632 if (!ASMMemIsZero(&pDesc->abImplementationUse, sizeof(pDesc->abImplementationUse)))
5633 Log2(("ISO/UDF: %-32s %.64Rhxs\n", "abReserved[64]:", &pDesc->abImplementationUse[0]));
5634 UDF_LOG2_MEMBER(pDesc, "#010RX32", offPredecessorVolDescSeq);
5635 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
5636 if (!ASMMemIsZero(&pDesc->abReserved, sizeof(pDesc->abReserved)))
5637 Log2(("ISO/UDF: %-32s %.22Rhxs\n", "abReserved[22]:", &pDesc->abReserved[0]));
5638 }
5639#endif
5640
5641 /*
5642 * Check if this is a new revision of an existing primary volume descriptor.
5643 */
5644 PUDFPRIMARYVOLUMEDESC pEndianConvert = NULL;
5645 uint32_t i = pInfo->cPrimaryVols;
5646 while (i--> 0)
5647 {
5648 if ( memcmp(pDesc->achVolumeID, pInfo->apPrimaryVols[i]->achVolumeID, sizeof(pDesc->achVolumeID)) == 0
5649 && memcmp(&pDesc->DescCharSet, &pInfo->apPrimaryVols[i]->DescCharSet, sizeof(pDesc->DescCharSet)) == 0)
5650 {
5651 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPrimaryVols[i]->uVolumeDescSeqNo)
5652 {
5653 Log(("ISO/UDF: Primary descriptor prevails over previous! (%u >= %u)\n",
5654 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5655 pEndianConvert = pInfo->apPrimaryVols[i];
5656 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
5657 }
5658 else
5659 Log(("ISO/UDF: Primary descriptor has lower sequence number than the previous! (%u < %u)\n",
5660 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5661 break;
5662 }
5663 }
5664 if (i >= pInfo->cPrimaryVols)
5665 {
5666 /*
5667 * It wasn't. Append it.
5668 */
5669 i = pInfo->cPrimaryVols;
5670 if (i < RT_ELEMENTS(pInfo->apPrimaryVols))
5671 {
5672 pInfo->apPrimaryVols[i] = pEndianConvert = (PUDFPRIMARYVOLUMEDESC)RTMemDup(pDesc, sizeof(*pDesc));
5673 if (pEndianConvert)
5674 pInfo->cPrimaryVols = i + 1;
5675 else
5676 return VERR_NO_MEMORY;
5677 Log2(("ISO/UDF: ++New primary descriptor.\n"));
5678 }
5679 else
5680 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PVDS, "Encountered too many primary volume descriptors");
5681 }
5682
5683#ifdef RT_BIG_ENDIAN
5684 /*
5685 * Do endian conversion of the descriptor.
5686 */
5687 if (pEndianConvert)
5688 {
5689 AssertFailed();
5690 }
5691#else
5692 RT_NOREF(pEndianConvert);
5693#endif
5694 return VINF_SUCCESS;
5695}
5696
5697
5698/**
5699 * Processes an logical volume descriptor in the VDS (UDF).
5700 *
5701 * @returns IPRT status code.
5702 * @param pInfo Where we gather descriptor information.
5703 * @param pDesc The descriptor.
5704 * @param cbSector The sector size (UDF defines the logical and physical
5705 * sector size to be the same).
5706 * @param pErrInfo Where to return extended error information.
5707 */
5708static int rtFsIsoVolProcessUdfLogicalVolumeDesc(PRTFSISOVDSINFO pInfo, PCUDFLOGICALVOLUMEDESC pDesc,
5709 uint32_t cbSector, PRTERRINFO pErrInfo)
5710{
5711#ifdef LOG_ENABLED
5712 Log(("ISO/UDF: Logical volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5713 if (LogIs2Enabled())
5714 {
5715 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5716 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
5717 UDF_LOG2_MEMBER_DSTRING(pDesc, achLogicalVolumeID);
5718 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbLogicalBlock);
5719 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idDomain);
5720 if (UDF_ENTITY_ID_EQUALS(&pDesc->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
5721 UDF_LOG2_MEMBER_LONGAD(pDesc, ContentsUse.FileSetDescriptor);
5722 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
5723 Log2(("ISO/UDF: %-32s %.16Rhxs\n", "ContentsUse.ab[16]:", &pDesc->ContentsUse.ab[0]));
5724 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbMapTable);
5725 UDF_LOG2_MEMBER(pDesc, "#010RX32", cPartitionMaps);
5726 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5727 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
5728 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
5729 UDF_LOG2_MEMBER_EXTENTAD(pDesc, IntegritySeqExtent);
5730 if (pDesc->cbMapTable)
5731 {
5732 Log2(("ISO/UDF: %-32s\n", "abPartitionMaps"));
5733 uint32_t iMap = 0;
5734 uint32_t off = 0;
5735 while (off + sizeof(UDFPARTMAPHDR) <= pDesc->cbMapTable)
5736 {
5737 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pDesc->abPartitionMaps[off];
5738 Log2(("ISO/UDF: %02u @ %#05x: type %u, length %u\n", iMap, off, pHdr->bType, pHdr->cb));
5739 if (off + pHdr->cb > pDesc->cbMapTable)
5740 {
5741 Log2(("ISO/UDF: BAD! Entry is %d bytes too long!\n", off + pHdr->cb - pDesc->cbMapTable));
5742 break;
5743 }
5744 if (pHdr->bType == 1)
5745 {
5746 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
5747 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uVolumeSeqNo, 5);
5748 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uPartitionNo, 5);
5749 }
5750 else if (pHdr->bType == 2)
5751 {
5752 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
5753 UDF_LOG2_MEMBER_ENTITY_ID_EX(pType2, idPartitionType, 5);
5754 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uVolumeSeqNo, 5);
5755 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uPartitionNo, 5);
5756 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
5757 {
5758 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Spm.cBlocksPerPacket, 5);
5759 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.cSparingTables, 5);
5760 if (pType2->u.Spm.bReserved2)
5761 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.bReserved2, 5);
5762 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.cbSparingTable, 5);
5763 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[0], 5);
5764 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[1], 5);
5765 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[2], 5);
5766 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[3], 5);
5767 }
5768 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
5769 {
5770 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataFile, 5);
5771 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataMirrorFile, 5);
5772 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataBitmapFile, 5);
5773 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.cBlocksAllocationUnit, 5);
5774 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Mpm.cBlocksAlignmentUnit, 5);
5775 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Mpm.fFlags, 5);
5776 if (!ASMMemIsZero(pType2->u.Mpm.abReserved2, sizeof(pType2->u.Mpm.abReserved2)))
5777 UDF_LOG2_MEMBER_EX(&pType2->u, ".5Rhxs", Mpm.abReserved2, 5);
5778 }
5779 }
5780 else
5781 Log2(("ISO/UDF: BAD! Unknown type!\n"));
5782
5783 /* advance */
5784 off += pHdr->cb;
5785 iMap++;
5786 }
5787 }
5788 }
5789#endif
5790
5791 /*
5792 * Check if this is a newer revision of an existing primary volume descriptor.
5793 */
5794 size_t cbDesc = (size_t)pDesc->cbMapTable + RT_UOFFSETOF(UDFLOGICALVOLUMEDESC, abPartitionMaps);
5795 if ( pDesc->cbMapTable >= (UINT32_MAX >> 1)
5796 || cbDesc > cbSector)
5797 {
5798 Log(("ISO/UDF: Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector));
5799 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD,
5800 "Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector);
5801 }
5802
5803 PUDFLOGICALVOLUMEDESC pEndianConvert = NULL;
5804 uint32_t i = pInfo->cLogicalVols;
5805 while (i--> 0)
5806 if ( memcmp(pDesc->achLogicalVolumeID, pInfo->apLogicalVols[i]->achLogicalVolumeID,
5807 sizeof(pDesc->achLogicalVolumeID)) == 0
5808 && memcmp(&pDesc->DescCharSet, &pInfo->apLogicalVols[i]->DescCharSet,
5809 sizeof(pDesc->DescCharSet)) == 0)
5810 {
5811 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apLogicalVols[i]->uVolumeDescSeqNo)
5812 {
5813 Log(("ISO/UDF: Logical descriptor prevails over previous! (%u >= %u)\n",
5814 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
5815 pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
5816 if (!pEndianConvert)
5817 return VERR_NO_MEMORY;
5818 RTMemFree(pInfo->apLogicalVols[i]);
5819 pInfo->apLogicalVols[i] = pEndianConvert;
5820 }
5821 else
5822 Log(("ISO/UDF: Logical descriptor has lower sequence number than the previous! (%u >= %u)\n",
5823 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
5824 break;
5825 }
5826 if (i >= pInfo->cLogicalVols)
5827 {
5828 /*
5829 * It wasn't. Append it.
5830 */
5831 i = pInfo->cLogicalVols;
5832 if (i < RT_ELEMENTS(pInfo->apLogicalVols))
5833 {
5834 pInfo->apLogicalVols[i] = pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
5835 if (pEndianConvert)
5836 pInfo->cLogicalVols = i + 1;
5837 else
5838 return VERR_NO_MEMORY;
5839 Log2(("ISO/UDF: ++New logical volume descriptor.\n"));
5840 }
5841 else
5842 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_LVDS, "Too many logical volume descriptors");
5843 }
5844
5845#ifdef RT_BIG_ENDIAN
5846 /*
5847 * Do endian conversion of the descriptor.
5848 */
5849 if (pEndianConvert)
5850 {
5851 AssertFailed();
5852 }
5853#else
5854 RT_NOREF(pEndianConvert);
5855#endif
5856 return VINF_SUCCESS;
5857}
5858
5859
5860/**
5861 * Processes an partition descriptor in the VDS (UDF).
5862 *
5863 * @returns IPRT status code.
5864 * @param pInfo Where we gather descriptor information.
5865 * @param pDesc The descriptor.
5866 * @param pErrInfo Where to return extended error information.
5867 */
5868static int rtFsIsoVolProcessUdfPartitionDesc(PRTFSISOVDSINFO pInfo, PCUDFPARTITIONDESC pDesc, PRTERRINFO pErrInfo)
5869{
5870#ifdef LOG_ENABLED
5871 Log(("ISO/UDF: Partition descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5872 if (LogIs2Enabled())
5873 {
5874 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5875 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
5876 UDF_LOG2_MEMBER(pDesc, "#06RX16", uPartitionNo);
5877 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, PartitionContents);
5878 if (UDF_ENTITY_ID_EQUALS(&pDesc->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
5879 {
5880 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceTable);
5881 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceBitmap);
5882 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.PartitionIntegrityTable);
5883 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceTable);
5884 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceBitmap);
5885 if (!ASMMemIsZero(&pDesc->ContentsUse.Hdr.abReserved[0], sizeof(pDesc->ContentsUse.Hdr.abReserved)))
5886 Log2(("ISO/UDF: %-32s\n%.88RhxD\n", "Hdr.abReserved[88]:", &pDesc->ContentsUse.Hdr.abReserved[0]));
5887 }
5888 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
5889 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ContentsUse.ab[128]:", &pDesc->ContentsUse.ab[0]));
5890 UDF_LOG2_MEMBER(pDesc, "#010RX32", uAccessType);
5891 UDF_LOG2_MEMBER(pDesc, "#010RX32", offLocation);
5892 UDF_LOG2_MEMBER(pDesc, "#010RX32", cSectors);
5893 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5894 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
5895 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
5896
5897 if (!ASMMemIsZero(&pDesc->abReserved[0], sizeof(pDesc->abReserved)))
5898 Log2(("ISO/UDF: %-32s\n%.156RhxD\n", "ImplementationUse.ab[156]:", &pDesc->abReserved[0]));
5899 }
5900#endif
5901
5902 /*
5903 * Check if this is a newer revision of an existing primary volume descriptor.
5904 */
5905 PUDFPARTITIONDESC pEndianConvert = NULL;
5906 uint32_t i = pInfo->cPartitions;
5907 while (i--> 0)
5908 if (pDesc->uPartitionNo == pInfo->apPartitions[i]->uPartitionNo)
5909 {
5910 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPartitions[i]->uVolumeDescSeqNo)
5911 {
5912 Log(("ISO/UDF: Partition descriptor for part %#u prevails over previous! (%u >= %u)\n",
5913 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5914 pEndianConvert = pInfo->apPartitions[i];
5915 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
5916 }
5917 else
5918 Log(("ISO/UDF: Partition descriptor for part %#u has a lower sequence number than the previous! (%u < %u)\n",
5919 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5920 break;
5921 }
5922 if (i >= pInfo->cPartitions)
5923 {
5924 /*
5925 * It wasn't. Append it.
5926 */
5927 i = pInfo->cPartitions;
5928 if (i < RT_ELEMENTS(pInfo->apPartitions))
5929 {
5930 pInfo->apPartitions[i] = pEndianConvert = (PUDFPARTITIONDESC)RTMemDup(pDesc, sizeof(*pDesc));
5931 if (pEndianConvert)
5932 pInfo->cPartitions = i + 1;
5933 else
5934 return VERR_NO_MEMORY;
5935 Log2(("ISO/UDF: ++New partition descriptor.\n"));
5936 }
5937 else
5938 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PDS, "Too many physical volume descriptors");
5939 }
5940
5941#ifdef RT_BIG_ENDIAN
5942 /*
5943 * Do endian conversion of the descriptor.
5944 */
5945 if (pEndianConvert)
5946 {
5947 AssertFailed();
5948 }
5949#else
5950 RT_NOREF(pEndianConvert);
5951#endif
5952 return VINF_SUCCESS;
5953}
5954
5955
5956/**
5957 * Processes an implementation use descriptor in the VDS (UDF).
5958 *
5959 * @returns IPRT status code.
5960 * @param pInfo Where we gather descriptor information.
5961 * @param pDesc The descriptor.
5962 * @param pErrInfo Where to return extended error information.
5963 */
5964static int rtFsIsoVolProcessUdfImplUseVolDesc(PRTFSISOVDSINFO pInfo, PCUDFIMPLEMENTATIONUSEVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
5965{
5966#ifdef LOG_ENABLED
5967 Log(("ISO/UDF: Implementation use volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5968 if (LogIs2Enabled())
5969 {
5970 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5971 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5972 if (UDF_ENTITY_ID_EQUALS(&pDesc->idImplementation, UDF_ENTITY_ID_IUVD_IMPLEMENTATION))
5973 {
5974 UDF_LOG2_MEMBER_CHARSPEC(&pDesc->ImplementationUse, Lvi.Charset);
5975 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achVolumeID);
5976 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo1);
5977 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo2);
5978 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo3);
5979 UDF_LOG2_MEMBER_ENTITY_ID(&pDesc->ImplementationUse, Lvi.idImplementation);
5980 if (!ASMMemIsZero(&pDesc->ImplementationUse.Lvi.abUse[0], sizeof(pDesc->ImplementationUse.Lvi.abUse)))
5981 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "Lvi.abUse[128]:", &pDesc->ImplementationUse.Lvi.abUse[0]));
5982 }
5983 else if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
5984 Log2(("ISO/UDF: %-32s\n%.460RhxD\n", "ImplementationUse.ab[460]:", &pDesc->ImplementationUse.ab[0]));
5985 }
5986#endif
5987
5988 RT_NOREF(pInfo, pDesc, pErrInfo);
5989 return VINF_SUCCESS;
5990}
5991
5992
5993
5994typedef struct RTFSISOSEENSEQENCES
5995{
5996 /** Number of sequences we've seen thus far. */
5997 uint32_t cSequences;
5998 /** The per sequence data. */
5999 struct
6000 {
6001 uint64_t off; /**< Byte offset of the sequence. */
6002 uint32_t cb; /**< Size of the sequence. */
6003 } aSequences[8];
6004} RTFSISOSEENSEQENCES;
6005typedef RTFSISOSEENSEQENCES *PRTFSISOSEENSEQENCES;
6006
6007
6008
6009/**
6010 * Process a VDS sequence, recursively dealing with volume descriptor pointers.
6011 *
6012 * This function only gathers information from the sequence, handling the
6013 * prevailing descriptor fun.
6014 *
6015 * @returns IPRT status code.
6016 * @param pThis The instance.
6017 * @param pInfo Where to store info from the VDS sequence.
6018 * @param offSeq The byte offset of the sequence.
6019 * @param cbSeq The length of the sequence.
6020 * @param pbBuf Read buffer.
6021 * @param cbBuf Size of the read buffer. This is at least one
6022 * sector big.
6023 * @param cNestings The VDS nesting depth.
6024 * @param pErrInfo Where to return extended error info.
6025 */
6026static int rtFsIsoVolReadAndProcessUdfVdsSeq(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, uint64_t offSeq, uint32_t cbSeq,
6027 uint8_t *pbBuf, size_t cbBuf, uint32_t cNestings, PRTERRINFO pErrInfo)
6028{
6029 AssertReturn(cbBuf >= pThis->cbSector, VERR_INTERNAL_ERROR);
6030
6031 /*
6032 * Check nesting depth.
6033 */
6034 if (cNestings > 5)
6035 return RTERRINFO_LOG_SET(pErrInfo, VERR_TOO_MUCH_DATA, "The volume descriptor sequence (VDS) is nested too deeply.");
6036
6037
6038 /*
6039 * Do the processing sector by sector to keep things simple.
6040 */
6041 uint32_t offInSeq = 0;
6042 while (offInSeq < cbSeq)
6043 {
6044 int rc;
6045
6046 /*
6047 * Read the next sector. Zero pad if less that a sector.
6048 */
6049 Assert((offInSeq & (pThis->cbSector - 1)) == 0);
6050 rc = RTVfsFileReadAt(pThis->hVfsBacking, offSeq + offInSeq, pbBuf, pThis->cbSector, NULL);
6051 if (RT_FAILURE(rc))
6052 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error reading VDS content at %RX64 (LB %#x): %Rrc",
6053 offSeq + offInSeq, pThis->cbSector, rc);
6054 if (cbSeq - offInSeq < pThis->cbSector)
6055 memset(&pbBuf[cbSeq - offInSeq], 0, pThis->cbSector - (cbSeq - offInSeq));
6056
6057 /*
6058 * Check tag.
6059 */
6060 PCUDFTAG pTag = (PCUDFTAG)pbBuf;
6061 rc = rtFsIsoVolValidateUdfDescTagAndCrc(pTag, pThis->cbSector, UINT16_MAX, (offSeq + offInSeq) / pThis->cbSector, pErrInfo);
6062 if ( RT_SUCCESS(rc)
6063 || ( rc == VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
6064 && ( pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC
6065 || pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_DESC
6066 || pTag->idTag == UDF_TAG_ID_UNALLOCATED_SPACE_DESC
6067 )
6068 )
6069 )
6070 {
6071 switch (pTag->idTag)
6072 {
6073 case UDF_TAG_ID_PRIMARY_VOL_DESC:
6074 rc = rtFsIsoVolProcessUdfPrimaryVolDesc(pInfo, (PCUDFPRIMARYVOLUMEDESC)pTag, pErrInfo);
6075 break;
6076
6077 case UDF_TAG_ID_IMPLEMENTATION_USE_VOLUME_DESC:
6078 rc = rtFsIsoVolProcessUdfImplUseVolDesc(pInfo, (PCUDFIMPLEMENTATIONUSEVOLUMEDESC)pTag, pErrInfo);
6079 break;
6080
6081 case UDF_TAG_ID_PARTITION_DESC:
6082 rc = rtFsIsoVolProcessUdfPartitionDesc(pInfo, (PCUDFPARTITIONDESC)pTag, pErrInfo);
6083 break;
6084
6085 case UDF_TAG_ID_LOGICAL_VOLUME_DESC:
6086 if (rc != VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC)
6087 rc = rtFsIsoVolProcessUdfLogicalVolumeDesc(pInfo, (PCUDFLOGICALVOLUMEDESC)pTag,
6088 pThis->cbSector, pErrInfo);
6089 else
6090 rc = VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD;
6091 break;
6092
6093 case UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC:
6094 Log(("ISO/UDF: Ignoring logical volume integrity descriptor at offset %#RX64.\n", offSeq + offInSeq));
6095 rc = VINF_SUCCESS;
6096 break;
6097
6098 case UDF_TAG_ID_UNALLOCATED_SPACE_DESC:
6099 Log(("ISO/UDF: Ignoring unallocated space descriptor at offset %#RX64.\n", offSeq + offInSeq));
6100 rc = VINF_SUCCESS;
6101 break;
6102
6103 case UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR:
6104 Log(("ISO/UDF: Ignoring AVDP in VDS (at offset %#RX64).\n", offSeq + offInSeq));
6105 rc = VINF_SUCCESS;
6106 break;
6107
6108 case UDF_TAG_ID_VOLUME_DESC_PTR:
6109 {
6110 PCUDFVOLUMEDESCPTR pVdp = (PCUDFVOLUMEDESCPTR)pTag;
6111 Log(("ISO/UDF: Processing volume descriptor pointer at offset %#RX64: %#x LB %#x (seq %#x); cNestings=%d\n",
6112 offSeq + offInSeq, pVdp->NextVolumeDescSeq.off, pVdp->NextVolumeDescSeq.cb,
6113 pVdp->uVolumeDescSeqNo, cNestings));
6114 rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, pInfo, (uint64_t)pVdp->NextVolumeDescSeq.off * pThis->cbSector,
6115 pVdp->NextVolumeDescSeq.cb, pbBuf, cbBuf, cNestings + 1, pErrInfo);
6116 break;
6117 }
6118
6119 case UDF_TAG_ID_TERMINATING_DESC:
6120 Log(("ISO/UDF: Terminating descriptor at offset %#RX64\n", offSeq + offInSeq));
6121 return VINF_SUCCESS;
6122
6123 default:
6124 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNEXPECTED_VDS_DESC,
6125 "Unexpected/unknown VDS descriptor %#x at byte offset %#RX64",
6126 pThis->cbSector, offSeq + offInSeq);
6127 }
6128 if (RT_FAILURE(rc))
6129 return rc;
6130 }
6131 /* The descriptor sequence is usually zero padded to 16 sectors. Just
6132 ignore zero descriptors. */
6133 else if (rc != VERR_ISOFS_TAG_IS_ALL_ZEROS)
6134 return rc;
6135
6136 /*
6137 * Advance.
6138 */
6139 offInSeq += pThis->cbSector;
6140 }
6141
6142 return VINF_SUCCESS;
6143}
6144
6145
6146
6147/**
6148 * Processes a volume descriptor sequence (VDS).
6149 *
6150 * @returns IPRT status code.
6151 * @param pThis The instance.
6152 * @param offSeq The byte offset of the sequence.
6153 * @param cbSeq The length of the sequence.
6154 * @param pSeenSequences Structure where to keep track of VDSes we've already
6155 * processed, to avoid redoing one that we don't
6156 * understand.
6157 * @param pbBuf Read buffer.
6158 * @param cbBuf Size of the read buffer. This is at least one
6159 * sector big.
6160 * @param pErrInfo Where to report extended error information.
6161 */
6162static int rtFsIsoVolReadAndProcessUdfVds(PRTFSISOVOL pThis, uint64_t offSeq, uint32_t cbSeq,
6163 PRTFSISOSEENSEQENCES pSeenSequences, uint8_t *pbBuf, size_t cbBuf,
6164 PRTERRINFO pErrInfo)
6165{
6166 /*
6167 * Skip if already seen.
6168 */
6169 uint32_t i = pSeenSequences->cSequences;
6170 while (i-- > 0)
6171 if ( pSeenSequences->aSequences[i].off == offSeq
6172 && pSeenSequences->aSequences[i].cb == cbSeq)
6173 return VERR_NOT_FOUND;
6174
6175 /* Not seen, so add it. */
6176 Assert(pSeenSequences->cSequences + 1 <= RT_ELEMENTS(pSeenSequences->aSequences));
6177 pSeenSequences->aSequences[pSeenSequences->cSequences].cb = cbSeq;
6178 pSeenSequences->aSequences[pSeenSequences->cSequences].off = offSeq;
6179 pSeenSequences->cSequences++;
6180
6181 LogFlow(("ISO/UDF: Processing anchor volume descriptor sequence at offset %#RX64 LB %#RX32\n", offSeq, cbSeq));
6182
6183 /*
6184 * Gather relevant descriptor info from the VDS then process it and on
6185 * success copy it into the instance.
6186 *
6187 * The processing has to be done in a different function because there may
6188 * be links to sub-sequences that needs to be processed. We do this by
6189 * recursing and check that we don't go to deep.
6190 */
6191 RTFSISOVDSINFO Info;
6192 RT_ZERO(Info);
6193 int rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, &Info, offSeq, cbSeq, pbBuf, cbBuf, 0, pErrInfo);
6194 if (RT_SUCCESS(rc))
6195 {
6196 rc = rtFsIsoVolProcessUdfVdsSeqInfo(pThis, &Info, pErrInfo);
6197 if (RT_SUCCESS(rc))
6198 rc = rtFsIsoVolProcessUdfFileSetDescs(pThis, pbBuf, cbBuf, pErrInfo);
6199 }
6200
6201 /*
6202 * Clean up info.
6203 */
6204 i = Info.cPrimaryVols;
6205 while (i-- > 0)
6206 RTMemFree(Info.apPrimaryVols[i]);
6207
6208 i = Info.cLogicalVols;
6209 while (i-- > 0)
6210 RTMemFree(Info.apLogicalVols[i]);
6211
6212 i = Info.cPartitions;
6213 while (i-- > 0)
6214 RTMemFree(Info.apPartitions[i]);
6215
6216 RTMemFree(Info.paPartMaps);
6217
6218 return rc;
6219}
6220
6221
6222static int rtFsIsoVolReadAndHandleUdfAvdp(PRTFSISOVOL pThis, uint64_t offAvdp, uint8_t *pbBuf, size_t cbBuf,
6223 PRTFSISOSEENSEQENCES pSeenSequences, PRTERRINFO pErrInfo)
6224{
6225 /*
6226 * Try read the descriptor and validate its tag.
6227 */
6228 PUDFANCHORVOLUMEDESCPTR pAvdp = (PUDFANCHORVOLUMEDESCPTR)pbBuf;
6229 size_t cbAvdpRead = RT_MIN(pThis->cbSector, cbBuf);
6230 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offAvdp, pAvdp, cbAvdpRead, NULL);
6231 if (RT_SUCCESS(rc))
6232 {
6233 rc = rtFsIsoVolValidateUdfDescTag(&pAvdp->Tag, UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR, offAvdp / pThis->cbSector, pErrInfo);
6234 if (RT_SUCCESS(rc))
6235 {
6236 Log2(("ISO/UDF: AVDP: MainVolumeDescSeq=%#RX32 LB %#RX32, ReserveVolumeDescSeq=%#RX32 LB %#RX32\n",
6237 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb,
6238 pAvdp->ReserveVolumeDescSeq.off, pAvdp->ReserveVolumeDescSeq.cb));
6239
6240 /*
6241 * Try the main sequence if it looks sane.
6242 */
6243 UDFEXTENTAD const ReserveVolumeDescSeq = pAvdp->ReserveVolumeDescSeq;
6244 if ( pAvdp->MainVolumeDescSeq.off < pThis->cBackingSectors
6245 && (uint64_t)pAvdp->MainVolumeDescSeq.off
6246 + (pAvdp->MainVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
6247 <= pThis->cBackingSectors)
6248 {
6249 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)pAvdp->MainVolumeDescSeq.off * pThis->cbSector,
6250 pAvdp->MainVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
6251 if (RT_SUCCESS(rc))
6252 return rc;
6253 }
6254 else
6255 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
6256 "MainVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
6257 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb, pThis->cBackingSectors);
6258 if (ReserveVolumeDescSeq.cb > 0)
6259 {
6260 if ( ReserveVolumeDescSeq.off < pThis->cBackingSectors
6261 && (uint64_t)ReserveVolumeDescSeq.off
6262 + (ReserveVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
6263 <= pThis->cBackingSectors)
6264 {
6265 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)ReserveVolumeDescSeq.off * pThis->cbSector,
6266 ReserveVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
6267 if (RT_SUCCESS(rc))
6268 return rc;
6269 }
6270 else if (RT_SUCCESS(rc))
6271 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
6272 "ReserveVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
6273 ReserveVolumeDescSeq.off, ReserveVolumeDescSeq.cb, pThis->cBackingSectors);
6274 }
6275 }
6276 }
6277 else
6278 rc = RTERRINFO_LOG_SET_F(pErrInfo, rc,
6279 "Error reading sector at offset %#RX64 (anchor volume descriptor pointer): %Rrc", offAvdp, rc);
6280
6281 return rc;
6282}
6283
6284
6285/**
6286 * Goes looking for UDF when we've seens a volume recognition sequence.
6287 *
6288 * @returns IPRT status code.
6289 * @param pThis The volume instance data.
6290 * @param puUdfLevel The UDF level indicated by the VRS.
6291 * @param offUdfBootVolDesc The offset of the BOOT2 descriptor, UINT64_MAX
6292 * if not encountered.
6293 * @param pbBuf Buffer for reading into.
6294 * @param cbBuf The size of the buffer. At least one sector.
6295 * @param pErrInfo Where to return extended error info.
6296 */
6297static int rtFsIsoVolHandleUdfDetection(PRTFSISOVOL pThis, uint8_t *puUdfLevel, uint64_t offUdfBootVolDesc,
6298 uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
6299{
6300 NOREF(offUdfBootVolDesc);
6301
6302 /*
6303 * There are up to three anchor volume descriptor pointers that can give us
6304 * two different descriptor sequences each. Usually, the different AVDP
6305 * structures points to the same two sequences. The idea here is that
6306 * sectors may deteriorate and become unreadable, and we're supposed to try
6307 * out alternative sectors to get the job done. If we really took this
6308 * seriously, we could try read all sequences in parallel and use the
6309 * sectors that are good. However, we'll try keep things reasonably simple
6310 * since we'll most likely be reading from hard disks rather than optical
6311 * media.
6312 *
6313 * We keep track of which sequences we've processed so we don't try to do it
6314 * again when alternative AVDP sectors points to the same sequences.
6315 */
6316 pThis->Udf.uLevel = *puUdfLevel;
6317 RTFSISOSEENSEQENCES SeenSequences;
6318 RT_ZERO(SeenSequences);
6319 int rc1 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, 256 * pThis->cbSector, pbBuf, cbBuf,
6320 &SeenSequences, pErrInfo);
6321 if (RT_SUCCESS(rc1))
6322 return rc1;
6323
6324 int rc2 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - 256 * pThis->cbSector,
6325 pbBuf, cbBuf, &SeenSequences, pErrInfo);
6326 if (RT_SUCCESS(rc2))
6327 return rc2;
6328
6329 int rc3 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - pThis->cbSector,
6330 pbBuf, cbBuf, &SeenSequences, pErrInfo);
6331 if (RT_SUCCESS(rc3))
6332 return rc3;
6333
6334 /*
6335 * Return failure if the alternatives have been excluded.
6336 *
6337 * Note! The error info won't be correct here.
6338 */
6339 pThis->Udf.uLevel = *puUdfLevel = 0;
6340
6341 if (RTFSISO9660_F_IS_ONLY_TYPE(pThis->fFlags, RTFSISO9660_F_NO_UDF))
6342 return rc1 != VERR_NOT_FOUND ? rc1 : rc2 != VERR_NOT_FOUND ? rc2 : rc3;
6343 return VINF_SUCCESS;
6344}
6345
6346
6347
6348#ifdef LOG_ENABLED
6349
6350/** Logging helper. */
6351static size_t rtFsIsoVolGetStrippedLength(const char *pachField, size_t cchField)
6352{
6353 while (cchField > 0 && pachField[cchField - 1] == ' ')
6354 cchField--;
6355 return cchField;
6356}
6357
6358/** Logging helper. */
6359static char *rtFsIsoVolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
6360{
6361 /* Check the format by looking for zero bytes. ISO-9660 doesn't allow zeros.
6362 This doesn't have to be a UTF-16BE string. */
6363 size_t cFirstZeros = 0;
6364 size_t cSecondZeros = 0;
6365 for (size_t off = 0; off + 1 < cchField; off += 2)
6366 {
6367 cFirstZeros += pachField[off] == '\0';
6368 cSecondZeros += pachField[off + 1] == '\0';
6369 }
6370
6371 int rc = VINF_SUCCESS;
6372 char *pszTmp = &pszDst[10];
6373 size_t cchRet = 0;
6374 if (cFirstZeros > cSecondZeros)
6375 {
6376 /* UTF-16BE / UTC-2BE: */
6377 if (cchField & 1)
6378 {
6379 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
6380 cchField--;
6381 else
6382 rc = VERR_INVALID_UTF16_ENCODING;
6383 }
6384 if (RT_SUCCESS(rc))
6385 {
6386 while ( cchField >= 2
6387 && pachField[cchField - 1] == ' '
6388 && pachField[cchField - 2] == '\0')
6389 cchField -= 2;
6390
6391 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
6392 }
6393 if (RT_SUCCESS(rc))
6394 {
6395 pszDst[0] = 'U';
6396 pszDst[1] = 'T';
6397 pszDst[2] = 'F';
6398 pszDst[3] = '-';
6399 pszDst[4] = '1';
6400 pszDst[5] = '6';
6401 pszDst[6] = 'B';
6402 pszDst[7] = 'E';
6403 pszDst[8] = ':';
6404 pszDst[9] = '\'';
6405 pszDst[10 + cchRet] = '\'';
6406 pszDst[10 + cchRet + 1] = '\0';
6407 }
6408 else
6409 RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
6410 }
6411 else if (cSecondZeros > 0)
6412 {
6413 /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
6414 if (cchField & 1)
6415 {
6416 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
6417 cchField--;
6418 else
6419 rc = VERR_INVALID_UTF16_ENCODING;
6420 }
6421 if (RT_SUCCESS(rc))
6422 {
6423 while ( cchField >= 2
6424 && pachField[cchField - 1] == '\0'
6425 && pachField[cchField - 2] == ' ')
6426 cchField -= 2;
6427
6428 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
6429 }
6430 if (RT_SUCCESS(rc))
6431 {
6432 pszDst[0] = 'U';
6433 pszDst[1] = 'T';
6434 pszDst[2] = 'F';
6435 pszDst[3] = '-';
6436 pszDst[4] = '1';
6437 pszDst[5] = '6';
6438 pszDst[6] = 'L';
6439 pszDst[7] = 'E';
6440 pszDst[8] = ':';
6441 pszDst[9] = '\'';
6442 pszDst[10 + cchRet] = '\'';
6443 pszDst[10 + cchRet + 1] = '\0';
6444 }
6445 else
6446 RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
6447 }
6448 else
6449 {
6450 /* ASSUME UTF-8/ASCII. */
6451 while ( cchField > 0
6452 && pachField[cchField - 1] == ' ')
6453 cchField--;
6454 rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
6455 if (RT_SUCCESS(rc))
6456 RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
6457 else
6458 RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
6459 }
6460 return pszDst;
6461}
6462
6463
6464/**
6465 * Logs the primary or supplementary volume descriptor
6466 *
6467 * @param pVolDesc The descriptor.
6468 */
6469static void rtFsIsoVolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
6470{
6471 if (LogIs2Enabled())
6472 {
6473 char szTmp[384];
6474 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
6475 Log2(("ISO9660: achSystemId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
6476 Log2(("ISO9660: achVolumeId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
6477 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
6478 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
6479 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIsoVolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
6480 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
6481 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
6482 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
6483 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
6484 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
6485 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
6486 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
6487 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
6488 Log2(("ISO9660: achVolumeSetId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
6489 Log2(("ISO9660: achPublisherId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
6490 Log2(("ISO9660: achDataPreparerId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
6491 Log2(("ISO9660: achApplicationId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
6492 Log2(("ISO9660: achCopyrightFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
6493 Log2(("ISO9660: achAbstractFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
6494 Log2(("ISO9660: achBibliographicFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
6495 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6496 pVolDesc->BirthTime.achYear,
6497 pVolDesc->BirthTime.achMonth,
6498 pVolDesc->BirthTime.achDay,
6499 pVolDesc->BirthTime.achHour,
6500 pVolDesc->BirthTime.achMinute,
6501 pVolDesc->BirthTime.achSecond,
6502 pVolDesc->BirthTime.achCentisecond,
6503 pVolDesc->BirthTime.offUtc*4/60));
6504 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6505 pVolDesc->ModifyTime.achYear,
6506 pVolDesc->ModifyTime.achMonth,
6507 pVolDesc->ModifyTime.achDay,
6508 pVolDesc->ModifyTime.achHour,
6509 pVolDesc->ModifyTime.achMinute,
6510 pVolDesc->ModifyTime.achSecond,
6511 pVolDesc->ModifyTime.achCentisecond,
6512 pVolDesc->ModifyTime.offUtc*4/60));
6513 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6514 pVolDesc->ExpireTime.achYear,
6515 pVolDesc->ExpireTime.achMonth,
6516 pVolDesc->ExpireTime.achDay,
6517 pVolDesc->ExpireTime.achHour,
6518 pVolDesc->ExpireTime.achMinute,
6519 pVolDesc->ExpireTime.achSecond,
6520 pVolDesc->ExpireTime.achCentisecond,
6521 pVolDesc->ExpireTime.offUtc*4/60));
6522 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6523 pVolDesc->EffectiveTime.achYear,
6524 pVolDesc->EffectiveTime.achMonth,
6525 pVolDesc->EffectiveTime.achDay,
6526 pVolDesc->EffectiveTime.achHour,
6527 pVolDesc->EffectiveTime.achMinute,
6528 pVolDesc->EffectiveTime.achSecond,
6529 pVolDesc->EffectiveTime.achCentisecond,
6530 pVolDesc->EffectiveTime.offUtc*4/60));
6531 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
6532 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
6533
6534 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
6535 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
6536 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
6537 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
6538 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
6539 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
6540 pVolDesc->RootDir.DirRec.RecTime.bMonth,
6541 pVolDesc->RootDir.DirRec.RecTime.bDay,
6542 pVolDesc->RootDir.DirRec.RecTime.bHour,
6543 pVolDesc->RootDir.DirRec.RecTime.bMinute,
6544 pVolDesc->RootDir.DirRec.RecTime.bSecond,
6545 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
6546 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
6547 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
6548 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
6549 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
6550 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
6551 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
6552 uint32_t offSysUse = RT_UOFFSETOF_DYN(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
6553 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
6554 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
6555 {
6556 Log2(("ISO9660: RootDir System Use:\n%.*RhxD\n",
6557 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
6558 }
6559 }
6560}
6561
6562#endif /* LOG_ENABLED */
6563
6564/**
6565 * Deal with a root directory from a primary or supplemental descriptor.
6566 *
6567 * @returns IPRT status code.
6568 * @param pThis The ISO 9660 instance being initialized.
6569 * @param pRootDir The root directory record to check out.
6570 * @param pDstRootDir Where to store a copy of the root dir record.
6571 * @param pErrInfo Where to return additional error info. Can be NULL.
6572 */
6573static int rtFsIsoVolHandleRootDir(PRTFSISOVOL pThis, PCISO9660DIRREC pRootDir,
6574 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
6575{
6576 if (pRootDir->cbDirRec < RT_UOFFSETOF(ISO9660DIRREC, achFileId))
6577 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
6578 pRootDir->cbDirRec, RT_UOFFSETOF(ISO9660DIRREC, achFileId));
6579
6580 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
6581 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
6582 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
6583 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
6584 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
6585 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
6586
6587 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
6588 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
6589 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
6590 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
6591 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
6592
6593 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
6594 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
6595 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
6596
6597 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
6598 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
6599 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
6600 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
6601 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6602 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
6603 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
6604
6605 /*
6606 * Seems okay, copy it.
6607 */
6608 *pDstRootDir = *pRootDir;
6609 return VINF_SUCCESS;
6610}
6611
6612
6613/**
6614 * Deal with a primary volume descriptor.
6615 *
6616 * @returns IPRT status code.
6617 * @param pThis The ISO 9660 instance being initialized.
6618 * @param pVolDesc The volume descriptor to handle.
6619 * @param offVolDesc The disk offset of the volume descriptor.
6620 * @param pRootDir Where to return a copy of the root directory record.
6621 * @param poffRootDirRec Where to return the disk offset of the root dir.
6622 * @param pErrInfo Where to return additional error info. Can be NULL.
6623 */
6624static int rtFsIsoVolHandlePrimaryVolDesc(PRTFSISOVOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
6625 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
6626{
6627 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
6628 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6629 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
6630
6631 /*
6632 * Take down the location of the primary volume descriptor so we can get
6633 * the volume lable and other info from it later.
6634 */
6635 pThis->offPrimaryVolDesc = offVolDesc;
6636
6637 /*
6638 * We need the block size ...
6639 */
6640 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
6641 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
6642 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
6643 || pThis->cbBlock / pThis->cbSector < 1)
6644 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
6645 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
6646 if (pThis->cbBlock / pThis->cbSector > 128)
6647 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
6648
6649 /*
6650 * ... volume space size ...
6651 */
6652 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
6653 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
6654 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
6655 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
6656 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
6657
6658 /*
6659 * ... number of volumes in the set ...
6660 */
6661 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
6662 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
6663 || pThis->cVolumesInSet == 0)
6664 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
6665 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
6666 if (pThis->cVolumesInSet > 32)
6667 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
6668
6669 /*
6670 * ... primary volume sequence ID ...
6671 */
6672 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
6673 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
6674 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
6675 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
6676 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
6677 || pThis->idPrimaryVol < 1)
6678 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6679 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
6680
6681 /*
6682 * ... and the root directory record.
6683 */
6684 *poffRootDirRec = offVolDesc + RT_UOFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
6685 return rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
6686}
6687
6688
6689/**
6690 * Deal with a supplementary volume descriptor.
6691 *
6692 * @returns IPRT status code.
6693 * @param pThis The ISO 9660 instance being initialized.
6694 * @param pVolDesc The volume descriptor to handle.
6695 * @param offVolDesc The disk offset of the volume descriptor.
6696 * @param pbUcs2Level Where to return the joliet level, if found. Caller
6697 * initializes this to zero, we'll return 1, 2 or 3 if
6698 * joliet was detected.
6699 * @param pRootDir Where to return the root directory, if found.
6700 * @param poffRootDirRec Where to return the disk offset of the root dir.
6701 * @param pErrInfo Where to return additional error info. Can be NULL.
6702 */
6703static int rtFsIsoVolHandleSupplementaryVolDesc(PRTFSISOVOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
6704 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
6705 PRTERRINFO pErrInfo)
6706{
6707 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
6708 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6709 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
6710
6711 /*
6712 * Is this a joliet volume descriptor? If not, we probably don't need to
6713 * care about it.
6714 */
6715 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
6716 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
6717 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
6718 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
6719 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
6720 return VINF_SUCCESS;
6721
6722 /*
6723 * Skip if joliet is unwanted.
6724 */
6725 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
6726 return VINF_SUCCESS;
6727
6728 /*
6729 * Check that the joliet descriptor matches the primary one.
6730 * Note! These are our assumptions and may be wrong.
6731 */
6732 if (pThis->cbBlock == 0)
6733 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6734 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
6735 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
6736 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6737 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
6738 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
6739#if 0 /* Not necessary. */
6740 /* Used to be !=, changed to > for ubuntu 20.10 and later. Wonder if they exclude a few files
6741 and thus end up with a different total. Obviously, this test is a big bogus, as we don't
6742 really seem to care about the value at all... */
6743 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) > pThis->cBlocksInPrimaryVolumeSpace)
6744 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6745 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
6746 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
6747#endif
6748 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
6749 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6750 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
6751 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
6752 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
6753 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6754 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
6755 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
6756
6757 if (*pbUcs2Level != 0)
6758 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
6759
6760 /*
6761 * Switch to the joliet root dir as it has UTF-16 stuff in it.
6762 */
6763 int rc = rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
6764 if (RT_SUCCESS(rc))
6765 {
6766 *poffRootDirRec = offVolDesc + RT_UOFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
6767 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
6768 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
6769 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
6770
6771 /*
6772 * Take down the location of the secondary volume descriptor so we can get
6773 * the volume lable and other info from it later.
6774 */
6775 pThis->offSecondaryVolDesc = offVolDesc;
6776 }
6777 return rc;
6778}
6779
6780
6781
6782/**
6783 * Worker for RTFsIso9660VolOpen.
6784 *
6785 * @returns IPRT status code.
6786 * @param pThis The ISO VFS instance to initialize.
6787 * @param hVfsSelf The ISO VFS handle (no reference consumed).
6788 * @param hVfsBacking The file backing the alleged ISO file system.
6789 * Reference is consumed (via rtFsIsoVol_Close).
6790 * @param fFlags Flags, RTFSISO9660_F_XXX.
6791 * @param pErrInfo Where to return additional error info. Can be NULL.
6792 */
6793static int rtFsIsoVolTryInit(PRTFSISOVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
6794{
6795 uint32_t const cbSector = 2048;
6796
6797 /*
6798 * First initialize the state so that rtFsIsoVol_Close won't trip up.
6799 */
6800 pThis->hVfsSelf = hVfsSelf;
6801 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIsoVol_Close releases it. */
6802 pThis->cbBacking = 0;
6803 pThis->cBackingSectors = 0;
6804 pThis->fFlags = fFlags;
6805 pThis->cbSector = cbSector;
6806 pThis->cbBlock = 0;
6807 pThis->cBlocksInPrimaryVolumeSpace = 0;
6808 pThis->cbPrimaryVolumeSpace = 0;
6809 pThis->cVolumesInSet = 0;
6810 pThis->idPrimaryVol = UINT32_MAX;
6811 pThis->fIsUtf16 = false;
6812 pThis->pRootDir = NULL;
6813 pThis->fHaveRock = false;
6814 pThis->offSuspSkip = 0;
6815 pThis->offRockBuf = UINT64_MAX;
6816
6817 /*
6818 * Do init stuff that may fail.
6819 */
6820 int rc = RTCritSectInit(&pThis->RockBufLock);
6821 AssertRCReturn(rc, rc);
6822
6823 rc = RTVfsFileQuerySize(hVfsBacking, &pThis->cbBacking);
6824 if (RT_SUCCESS(rc))
6825 pThis->cBackingSectors = pThis->cbBacking / pThis->cbSector;
6826 else
6827 return rc;
6828
6829 /*
6830 * Read the volume descriptors starting at logical sector 16.
6831 */
6832 union
6833 {
6834 uint8_t ab[RTFSISO_MAX_LOGICAL_BLOCK_SIZE];
6835 uint16_t au16[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 2];
6836 uint32_t au32[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 4];
6837 ISO9660VOLDESCHDR VolDescHdr;
6838 ISO9660BOOTRECORD BootRecord;
6839 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
6840 ISO9660SUPVOLDESC SupVolDesc;
6841 ISO9660VOLPARTDESC VolPartDesc;
6842 } Buf;
6843 RT_ZERO(Buf);
6844
6845 uint64_t offRootDirRec = UINT64_MAX;
6846 ISO9660DIRREC RootDir;
6847 RT_ZERO(RootDir);
6848
6849 uint64_t offJolietRootDirRec = UINT64_MAX;
6850 uint8_t bJolietUcs2Level = 0;
6851 ISO9660DIRREC JolietRootDir;
6852 RT_ZERO(JolietRootDir);
6853
6854 uint8_t uUdfLevel = 0;
6855 uint64_t offUdfBootVolDesc = UINT64_MAX;
6856
6857 uint32_t cPrimaryVolDescs = 0;
6858 uint32_t cSupplementaryVolDescs = 0;
6859 uint32_t cBootRecordVolDescs = 0;
6860 uint32_t offVolDesc = 16 * cbSector;
6861 enum
6862 {
6863 kStateStart = 0,
6864 kStateNoSeq,
6865 kStateCdSeq,
6866 kStateUdfSeq
6867 } enmState = kStateStart;
6868 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
6869 {
6870 if (iVolDesc > 32)
6871 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "More than 32 volume descriptors, doesn't seem right...");
6872
6873 /* Read the next one and check the signature. */
6874 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
6875 if (RT_FAILURE(rc))
6876 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
6877
6878#define MATCH_STD_ID(a_achStdId1, a_szStdId2) \
6879 ( (a_achStdId1)[0] == (a_szStdId2)[0] \
6880 && (a_achStdId1)[1] == (a_szStdId2)[1] \
6881 && (a_achStdId1)[2] == (a_szStdId2)[2] \
6882 && (a_achStdId1)[3] == (a_szStdId2)[3] \
6883 && (a_achStdId1)[4] == (a_szStdId2)[4] )
6884#define MATCH_HDR(a_pStd, a_bType2, a_szStdId2, a_bVer2) \
6885 ( MATCH_STD_ID((a_pStd)->achStdId, a_szStdId2) \
6886 && (a_pStd)->bDescType == (a_bType2) \
6887 && (a_pStd)->bDescVersion == (a_bVer2) )
6888
6889 /*
6890 * ISO 9660 ("CD001").
6891 */
6892 if ( ( enmState == kStateStart
6893 || enmState == kStateCdSeq
6894 || enmState == kStateNoSeq)
6895 && MATCH_STD_ID(Buf.VolDescHdr.achStdId, ISO9660VOLDESC_STD_ID) )
6896 {
6897 enmState = kStateCdSeq;
6898
6899 /* Do type specific handling. */
6900 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
6901 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
6902 {
6903 cPrimaryVolDescs++;
6904 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
6905 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6906 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
6907#ifdef LOG_ENABLED
6908 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
6909#endif
6910 if (cPrimaryVolDescs == 1)
6911 rc = rtFsIsoVolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
6912 else if (cPrimaryVolDescs == 2)
6913 Log(("ISO9660: ignoring 2nd primary descriptor\n")); /* so we can read the w2k3 ifs kit */
6914 else
6915 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
6916 }
6917 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
6918 {
6919 cSupplementaryVolDescs++;
6920 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
6921 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6922 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
6923#ifdef LOG_ENABLED
6924 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
6925#endif
6926 rc = rtFsIsoVolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
6927 &offJolietRootDirRec, pErrInfo);
6928 }
6929 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
6930 {
6931 cBootRecordVolDescs++;
6932 }
6933 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
6934 {
6935 if (!cPrimaryVolDescs)
6936 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
6937 enmState = kStateNoSeq;
6938 }
6939 else
6940 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6941 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
6942 }
6943 /*
6944 * UDF volume recognition sequence (VRS).
6945 */
6946 else if ( ( enmState == kStateNoSeq
6947 || enmState == kStateStart)
6948 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BEGIN, UDF_EXT_VOL_DESC_VERSION) )
6949 {
6950 if (uUdfLevel == 0)
6951 enmState = kStateUdfSeq;
6952 else
6953 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BEA01 sequence is supported");
6954 }
6955 else if ( enmState == kStateUdfSeq
6956 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_02, UDF_EXT_VOL_DESC_VERSION) )
6957 uUdfLevel = 2;
6958 else if ( enmState == kStateUdfSeq
6959 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_03, UDF_EXT_VOL_DESC_VERSION) )
6960 uUdfLevel = 3;
6961 else if ( enmState == kStateUdfSeq
6962 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BOOT, UDF_EXT_VOL_DESC_VERSION) )
6963 {
6964 if (offUdfBootVolDesc == UINT64_MAX)
6965 offUdfBootVolDesc = iVolDesc * cbSector;
6966 else
6967 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BOOT2 descriptor is supported");
6968 }
6969 else if ( enmState == kStateUdfSeq
6970 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_TERM, UDF_EXT_VOL_DESC_VERSION) )
6971 {
6972 if (uUdfLevel != 0)
6973 enmState = kStateNoSeq;
6974 else
6975 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Found BEA01 & TEA01, but no NSR02 or NSR03 descriptors");
6976 }
6977 /*
6978 * Unknown, probably the end.
6979 */
6980 else if (enmState == kStateNoSeq)
6981 break;
6982 else if (enmState == kStateStart)
6983 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
6984 "Not ISO? Unable to recognize volume descriptor signature: %.5Rhxs", Buf.VolDescHdr.achStdId);
6985 else if (enmState == kStateCdSeq)
6986 {
6987#if 1
6988 /* The warp server for ebusiness update ISOs known as ACP2 & MCP2 ends up here,
6989 as they do in deed miss a terminator volume descriptor and we're now at the
6990 root directory already. Just detect this, ignore it and get on with things. */
6991 Log(("rtFsIsoVolTryInit: Ignoring missing ISO 9660 terminator volume descriptor (found %.5Rhxs).\n",
6992 Buf.VolDescHdr.achStdId));
6993 break;
6994#else
6995 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
6996 "Missing ISO 9660 terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
6997#endif
6998 }
6999 else if (enmState == kStateUdfSeq)
7000 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
7001 "Missing UDF terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
7002 else
7003 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
7004 "Unknown volume descriptor signature found at sector %u: %.5Rhxs",
7005 16 + iVolDesc, Buf.VolDescHdr.achStdId);
7006 if (RT_FAILURE(rc))
7007 return rc;
7008 }
7009
7010 /*
7011 * If we found a UDF VRS and are interested in UDF, we have more work to do here.
7012 */
7013 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF))
7014 {
7015 Log(("rtFsIsoVolTryInit: uUdfLevel=%d\n", uUdfLevel));
7016 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
7017 if (RT_FAILURE(rc))
7018 return rc;
7019 }
7020
7021 /*
7022 * Decide which to prefer.
7023 *
7024 * By default we pick UDF over any of the two ISO 9960, there is currently
7025 * no way to override this without using the RTFSISO9660_F_NO_XXX options.
7026 *
7027 * If there isn't UDF, we may be faced with choosing between joliet and
7028 * rock ridge. The joliet option is generally favorable as we don't have
7029 * to guess wrt to the file name encoding. So, we'll pick that for now.
7030 *
7031 * Note! Should we change this preference for joliet, there fun wrt making sure
7032 * there really is rock ridge stuff in the primary volume as well as
7033 * making sure there really is anything of value in the primary volume.
7034 */
7035 if (uUdfLevel > 0)
7036 {
7037 pThis->enmType = RTFSISOVOLTYPE_UDF;
7038 rc = rtFsIsoDirShrd_NewUdf(pThis, NULL /*pParent*/, &pThis->Udf.VolInfo.RootDirIcb,
7039 NULL /*pFileIdDesc*/, 0 /*offInDir*/, &pThis->pRootDir);
7040 /** @todo fall back on failure? */
7041 return rc;
7042 }
7043 if (bJolietUcs2Level != 0)
7044 {
7045 pThis->enmType = RTFSISOVOLTYPE_JOLIET;
7046 pThis->fIsUtf16 = true;
7047 return rtFsIsoDirShrd_New9660(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, NULL, &pThis->pRootDir);
7048 }
7049 pThis->enmType = RTFSISOVOLTYPE_ISO9960;
7050 return rtFsIsoDirShrd_New9660(pThis, NULL, &RootDir, 1, offRootDirRec, NULL, &pThis->pRootDir);
7051}
7052
7053
7054/**
7055 * Opens an ISO 9660 file system volume.
7056 *
7057 * @returns IPRT status code.
7058 * @param hVfsFileIn The file or device backing the volume.
7059 * @param fFlags RTFSISO9660_F_XXX.
7060 * @param phVfs Where to return the virtual file system handle.
7061 * @param pErrInfo Where to return additional error information.
7062 */
7063RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
7064{
7065 /*
7066 * Quick input validation.
7067 */
7068 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
7069 *phVfs = NIL_RTVFS;
7070 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
7071
7072 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
7073 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
7074
7075 /*
7076 * Create a new ISO VFS instance and try initialize it using the given input file.
7077 */
7078 RTVFS hVfs = NIL_RTVFS;
7079 PRTFSISOVOL pThis = NULL;
7080 int rc = RTVfsNew(&g_rtFsIsoVolOps, sizeof(RTFSISOVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
7081 if (RT_SUCCESS(rc))
7082 {
7083 rc = rtFsIsoVolTryInit(pThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
7084 if (RT_SUCCESS(rc))
7085 *phVfs = hVfs;
7086 else
7087 RTVfsRelease(hVfs);
7088 }
7089 else
7090 RTVfsFileRelease(hVfsFileIn);
7091 return rc;
7092}
7093
7094
7095/**
7096 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
7097 */
7098static DECLCALLBACK(int) rtVfsChainIsoFsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
7099 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
7100{
7101 RT_NOREF(pProviderReg, pSpec);
7102
7103 /*
7104 * Basic checks.
7105 */
7106 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
7107 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
7108 if ( pElement->enmType != RTVFSOBJTYPE_VFS
7109 && pElement->enmType != RTVFSOBJTYPE_DIR)
7110 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
7111 if (pElement->cArgs > 1)
7112 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
7113
7114 /*
7115 * Parse the flag if present, save in pElement->uProvider.
7116 */
7117 uint32_t fFlags = 0;
7118 if (pElement->cArgs > 0)
7119 {
7120 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
7121 {
7122 const char *psz = pElement->paArgs[iArg].psz;
7123 if (*psz)
7124 {
7125 if (!strcmp(psz, "nojoliet"))
7126 fFlags |= RTFSISO9660_F_NO_JOLIET;
7127 else if (!strcmp(psz, "norock"))
7128 fFlags |= RTFSISO9660_F_NO_ROCK;
7129 else if (!strcmp(psz, "noudf"))
7130 fFlags |= RTFSISO9660_F_NO_UDF;
7131 else
7132 {
7133 *poffError = pElement->paArgs[iArg].offSpec;
7134 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
7135 }
7136 }
7137 }
7138 }
7139
7140 pElement->uProvider = fFlags;
7141 return VINF_SUCCESS;
7142}
7143
7144
7145/**
7146 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
7147 */
7148static DECLCALLBACK(int) rtVfsChainIsoFsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
7149 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
7150 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
7151{
7152 RT_NOREF(pProviderReg, pSpec, poffError);
7153
7154 int rc;
7155 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
7156 if (hVfsFileIn != NIL_RTVFSFILE)
7157 {
7158 RTVFS hVfs;
7159 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
7160 RTVfsFileRelease(hVfsFileIn);
7161 if (RT_SUCCESS(rc))
7162 {
7163 *phVfsObj = RTVfsObjFromVfs(hVfs);
7164 RTVfsRelease(hVfs);
7165 if (*phVfsObj != NIL_RTVFSOBJ)
7166 return VINF_SUCCESS;
7167 rc = VERR_VFS_CHAIN_CAST_FAILED;
7168 }
7169 }
7170 else
7171 rc = VERR_VFS_CHAIN_CAST_FAILED;
7172 return rc;
7173}
7174
7175
7176/**
7177 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
7178 */
7179static DECLCALLBACK(bool) rtVfsChainIsoFsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
7180 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
7181 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
7182{
7183 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
7184 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
7185 || !pReuseElement->paArgs[0].uProvider)
7186 return true;
7187 return false;
7188}
7189
7190
7191/** VFS chain element 'file'. */
7192static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
7193{
7194 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
7195 /* fReserved = */ 0,
7196 /* pszName = */ "isofs",
7197 /* ListEntry = */ { NULL, NULL },
7198 /* pszHelp = */ "Open a ISO 9660 or UDF file system, requires a file object on the left side.\n"
7199 "The 'noudf' option make it ignore any UDF.\n"
7200 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
7201 "The 'norock' option make it ignore any rock ridge info.\n",
7202 /* pfnValidate = */ rtVfsChainIsoFsVol_Validate,
7203 /* pfnInstantiate = */ rtVfsChainIsoFsVol_Instantiate,
7204 /* pfnCanReuseElement = */ rtVfsChainIsoFsVol_CanReuseElement,
7205 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
7206};
7207
7208RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
7209
Note: See TracBrowser for help on using the repository browser.

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