VirtualBox

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

Last change on this file since 107601 was 107432, checked in by vboxsync, 2 months ago

Runtime/common/fs/isovfs.cpp: Fixed a warning found by Parfait. ​jiraref:VBP-1424

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

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