VirtualBox

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

Last change on this file since 95205 was 94293, checked in by vboxsync, 3 years ago

IPRT,Storage: Adding RTVfsQueryLabel and internally a generic pfnQueryInfoEx method to the RTVFSOBJOPS function table. Untested implementation of the latter for iso/udf. [build fix] bugref:9781

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

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