VirtualBox

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

Last change on this file since 94291 was 94291, 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. 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 94291 2022-03-17 13:29:52Z 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 uint8_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 return VINF_SUCCESS;
5081}
5082
5083
5084/**
5085 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
5086 */
5087static DECLCALLBACK(int) rtFsIsoVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
5088{
5089 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
5090
5091 rtFsIsoDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
5092 return rtFsIsoDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
5093}
5094
5095
5096/**
5097 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
5098 */
5099static DECLCALLBACK(int) rtFsIsoVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
5100{
5101 RT_NOREF(pvThis, off, cb, pfUsed);
5102 return VERR_NOT_IMPLEMENTED;
5103}
5104
5105
5106DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIsoVolOps =
5107{
5108 { /* Obj */
5109 RTVFSOBJOPS_VERSION,
5110 RTVFSOBJTYPE_VFS,
5111 "ISO 9660/UDF",
5112 rtFsIsoVol_Close,
5113 rtFsIsoVol_QueryInfo,
5114 rtFsIsoVol_QueryInfoEx,
5115 RTVFSOBJOPS_VERSION
5116 },
5117 RTVFSOPS_VERSION,
5118 0 /* fFeatures */,
5119 rtFsIsoVol_OpenRoot,
5120 rtFsIsoVol_QueryRangeState,
5121 RTVFSOPS_VERSION
5122};
5123
5124
5125/**
5126 * Checks the descriptor tag and CRC.
5127 *
5128 * @retval IPRT status code.
5129 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
5130 * @retval VERR_MISMATCH
5131 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
5132 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
5133 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
5134 *
5135 * @param pTag The tag to check.
5136 * @param idTag The expected descriptor tag ID, UINT16_MAX matches any
5137 * tag ID.
5138 * @param offTag The sector offset of the tag.
5139 * @param pErrInfo Where to return extended error info.
5140 */
5141static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
5142{
5143 /*
5144 * Checksum the tag first.
5145 */
5146 const uint8_t *pbTag = (const uint8_t *)pTag;
5147 uint8_t const bChecksum = pbTag[0]
5148 + pbTag[1]
5149 + pbTag[2]
5150 + pbTag[3]
5151 + pbTag[5] /* skipping byte 4 as that's the checksum. */
5152 + pbTag[6]
5153 + pbTag[7]
5154 + pbTag[8]
5155 + pbTag[9]
5156 + pbTag[10]
5157 + pbTag[11]
5158 + pbTag[12]
5159 + pbTag[13]
5160 + pbTag[14]
5161 + pbTag[15];
5162 if (pTag->uChecksum == bChecksum)
5163 {
5164 /*
5165 * Do the matching.
5166 */
5167 if ( pTag->uVersion == 3
5168 || pTag->uVersion == 2)
5169 {
5170 if ( pTag->idTag == idTag
5171 || idTag == UINT16_MAX)
5172 {
5173 if (pTag->offTag == offTag)
5174 {
5175 //Log3(("ISO/UDF: Valid descriptor %#06x at %#010RX32; cbDescriptorCrc=%#06RX32 uTagSerialNo=%#x\n",
5176 // pTag->idTag, offTag, pTag->cbDescriptorCrc, pTag->uTagSerialNo));
5177 return VINF_SUCCESS;
5178 }
5179
5180 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Sector mismatch: %#RX32 (%.*Rhxs)\n",
5181 idTag, offTag, pTag->offTag, sizeof(*pTag), pTag));
5182 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TAG_SECTOR_MISMATCH,
5183 "Descriptor tag sector number mismatch: %#x, expected %#x (%.*Rhxs)",
5184 pTag->offTag, offTag, sizeof(*pTag), pTag);
5185 }
5186 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Tag ID mismatch: %#x (%.*Rhxs)\n",
5187 idTag, offTag, pTag->idTag, sizeof(*pTag), pTag));
5188 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_MISMATCH, "Descriptor tag ID mismatch: %#x, expected %#x (%.*Rhxs)",
5189 pTag->idTag, idTag, sizeof(*pTag), pTag);
5190 }
5191 if (ASMMemIsZero(pTag, sizeof(*pTag)))
5192 {
5193 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): All zeros\n", idTag, offTag));
5194 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TAG_IS_ALL_ZEROS, "Descriptor is all zeros");
5195 }
5196
5197 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Unsupported version: %#x (%.*Rhxs)\n",
5198 idTag, offTag, pTag->uVersion, sizeof(*pTag), pTag));
5199 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_TAG_VERSION, "Unsupported descriptor tag version: %#x, expected 2 or 3 (%.*Rhxs)",
5200 pTag->uVersion, sizeof(*pTag), pTag);
5201 }
5202 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): checksum error: %#x, calc %#x (%.*Rhxs)\n",
5203 idTag, offTag, pTag->uChecksum, bChecksum, sizeof(*pTag), pTag));
5204 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_TAG_CHECKSUM,
5205 "Descriptor tag checksum error: %#x, calculated %#x (%.*Rhxs)",
5206 pTag->uChecksum, bChecksum, sizeof(*pTag), pTag);
5207}
5208
5209
5210/**
5211 * Checks the descriptor CRC.
5212 *
5213 * @retval VINF_SUCCESS
5214 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
5215 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
5216 *
5217 * @param pTag The descriptor buffer to checksum.
5218 * @param cbDesc The size of the descriptor buffer.
5219 * @param pErrInfo Where to return extended error info.
5220 */
5221static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo)
5222{
5223 if (pTag->cbDescriptorCrc + sizeof(*pTag) <= cbDesc)
5224 {
5225 uint16_t uCrc = RTCrc16Ccitt(pTag + 1, pTag->cbDescriptorCrc);
5226 if (pTag->uDescriptorCrc == uCrc)
5227 return VINF_SUCCESS;
5228
5229 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Descriptor CRC mismatch: expected %#x, calculated %#x (cbDescriptorCrc=%#x)\n",
5230 pTag->idTag, pTag->offTag, pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc));
5231 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_DESC_CRC_MISMATCH,
5232 "Descriptor CRC mismatch: exepcted %#x, calculated %#x (cbDescriptor=%#x, idTag=%#x, offTag=%#010RX32)",
5233 pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc, pTag->idTag, pTag->offTag);
5234 }
5235
5236 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx\n",
5237 pTag->idTag, pTag->offTag, pTag->cbDescriptorCrc, cbDesc));
5238 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC,
5239 "Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx (idTag=%#x, offTag=%#010RX32)",
5240 pTag->cbDescriptorCrc, cbDesc, pTag->idTag, pTag->offTag);
5241}
5242
5243
5244/**
5245 * Checks the descriptor tag and CRC.
5246 *
5247 * @retval VINF_SUCCESS
5248 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
5249 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
5250 * @retval VERR_MISMATCH
5251 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
5252 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
5253 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
5254 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
5255 *
5256 * @param pTag The descriptor buffer to check the tag of and to
5257 * checksum.
5258 * @param cbDesc The size of the descriptor buffer.
5259 * @param idTag The expected descriptor tag ID, UINT16_MAX
5260 * matches any tag ID.
5261 * @param offTag The sector offset of the tag.
5262 * @param pErrInfo Where to return extended error info.
5263 */
5264static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
5265{
5266 int rc = rtFsIsoVolValidateUdfDescTag(pTag, idTag, offTag, pErrInfo);
5267 if (RT_SUCCESS(rc))
5268 rc = rtFsIsoVolValidateUdfDescCrc(pTag, cbDesc, pErrInfo);
5269 return rc;
5270}
5271
5272
5273
5274
5275static int rtFsIsoVolProcessUdfFileSetDescs(PRTFSISOVOL pThis, uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
5276{
5277
5278 /*
5279 * We assume there is a single file descriptor and don't bother checking what comes next.
5280 */
5281 PUDFFILESETDESC pFsd = (PUDFFILESETDESC)pbBuf;
5282 Assert(cbBuf > sizeof(*pFsd)); NOREF(cbBuf);
5283 RT_ZERO(*pFsd);
5284 size_t cbToRead = RT_MAX(pThis->Udf.VolInfo.FileSetDescriptor.cb, sizeof(*pFsd));
5285 int rc = rtFsIsoVolUdfVpRead(pThis, pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
5286 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, 0, pFsd, cbToRead);
5287 if (RT_SUCCESS(rc))
5288 {
5289 rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFsd->Tag, cbToRead, UDF_TAG_ID_FILE_SET_DESC,
5290 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, pErrInfo);
5291 if (RT_SUCCESS(rc))
5292 {
5293#ifdef LOG_ENABLED
5294 Log(("ISO/UDF: File set descriptor at %#RX32 (%#RX32:%#RX32)\n", pFsd->Tag.offTag,
5295 pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
5296 pThis->Udf.VolInfo.FileSetDescriptor.Location.off));
5297 if (LogIs2Enabled())
5298 {
5299 UDF_LOG2_MEMBER_TIMESTAMP(pFsd, RecordingTimestamp);
5300 UDF_LOG2_MEMBER(pFsd, "#06RX16", uInterchangeLevel);
5301 UDF_LOG2_MEMBER(pFsd, "#06RX16", uMaxInterchangeLevel);
5302 UDF_LOG2_MEMBER(pFsd, "#010RX32", fCharacterSets);
5303 UDF_LOG2_MEMBER(pFsd, "#010RX32", fMaxCharacterSets);
5304 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetNo);
5305 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetDescNo);
5306 UDF_LOG2_MEMBER_CHARSPEC(pFsd, LogicalVolumeIDCharSet);
5307 UDF_LOG2_MEMBER_DSTRING(pFsd, achLogicalVolumeID);
5308 UDF_LOG2_MEMBER_CHARSPEC(pFsd, FileSetCharSet);
5309 UDF_LOG2_MEMBER_DSTRING(pFsd, achFileSetID);
5310 UDF_LOG2_MEMBER_DSTRING(pFsd, achCopyrightFile);
5311 UDF_LOG2_MEMBER_DSTRING(pFsd, achAbstractFile);
5312 UDF_LOG2_MEMBER_LONGAD(pFsd, RootDirIcb);
5313 UDF_LOG2_MEMBER_ENTITY_ID(pFsd, idDomain);
5314 UDF_LOG2_MEMBER_LONGAD(pFsd, NextExtent);
5315 UDF_LOG2_MEMBER_LONGAD(pFsd, SystemStreamDirIcb);
5316 if (!ASMMemIsZero(&pFsd->abReserved[0], sizeof(pFsd->abReserved)))
5317 UDF_LOG2_MEMBER(pFsd, ".32Rhxs", abReserved);
5318 }
5319#endif
5320
5321 /*
5322 * Do some basic sanity checking.
5323 */
5324 if (!UDF_IS_CHAR_SET_OSTA(&pFsd->FileSetCharSet))
5325 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_UNSUPPORTED_CHAR_SET,
5326 "Invalid file set charset %.64Rhxs", &pFsd->FileSetCharSet);
5327 if ( pFsd->RootDirIcb.cb == 0
5328 || pFsd->RootDirIcb.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
5329 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_ZERO_ROOT_DIR,
5330 "Root Dir ICB location is zero or malformed: uType=%#x cb=%#x loc=%#x:%#RX32",
5331 pFsd->RootDirIcb.uType, pFsd->RootDirIcb.cb,
5332 pFsd->RootDirIcb.Location.uPartitionNo, pFsd->RootDirIcb.Location.off);
5333 if ( pFsd->NextExtent.cb != 0
5334 && pFsd->NextExtent.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
5335 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_NEXT_EXTENT,
5336 "NextExtent isn't zero: uType=%#x cb=%#x loc=%#x:%#RX32",
5337 pFsd->NextExtent.uType, pFsd->NextExtent.cb,
5338 pFsd->NextExtent.Location.uPartitionNo, pFsd->NextExtent.Location.off);
5339
5340 /*
5341 * Copy the information we need.
5342 */
5343 pThis->Udf.VolInfo.RootDirIcb = pFsd->RootDirIcb;
5344 if ( pFsd->SystemStreamDirIcb.cb > 0
5345 && pFsd->SystemStreamDirIcb.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
5346 pThis->Udf.VolInfo.SystemStreamDirIcb = pFsd->SystemStreamDirIcb;
5347 else
5348 RT_ZERO(pThis->Udf.VolInfo.SystemStreamDirIcb);
5349 return VINF_SUCCESS;
5350 }
5351 return rc;
5352 }
5353 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading file set descriptor");
5354}
5355
5356
5357/**
5358 * Check validatity and extract information from the descriptors in the VDS seq.
5359 *
5360 * @returns IPRT status code
5361 * @param pThis The instance.
5362 * @param pInfo The VDS sequence info.
5363 * @param pErrInfo Where to return extended error info.
5364 */
5365static int rtFsIsoVolProcessUdfVdsSeqInfo(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, PRTERRINFO pErrInfo)
5366{
5367 /*
5368 * Check the basic descriptor counts.
5369 */
5370 PUDFPRIMARYVOLUMEDESC pPvd;
5371 if (pInfo->cPrimaryVols == 1)
5372 pPvd = pInfo->apPrimaryVols[0];
5373 else
5374 {
5375 if (pInfo->cPrimaryVols == 0)
5376 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PVD, "No primary volume descriptor was found");
5377 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_PVDS,
5378 "More than one primary volume descriptor was found: %u", pInfo->cPrimaryVols);
5379 }
5380
5381 PUDFLOGICALVOLUMEDESC pLvd;
5382 if (pInfo->cLogicalVols == 1)
5383 pLvd = pInfo->apLogicalVols[0];
5384 else
5385 {
5386 if (pInfo->cLogicalVols == 0)
5387 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_LVD, "No logical volume descriptor was found");
5388 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_LVDS,
5389 "More than one logical volume descriptor was found: %u", pInfo->cLogicalVols);
5390 }
5391
5392#if 0
5393 if (pInfo->cPartitions == 0)
5394 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PD, "No partition descriptors was found");
5395#endif
5396
5397 /*
5398 * Check out the partition map in the logical volume descriptor.
5399 * Produce the mapping table while going about that.
5400 */
5401 if (pLvd->cPartitionMaps > 64)
5402 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_MANY_PART_MAPS,
5403 "Too many partition maps: %u (max 64)", pLvd->cPartitionMaps);
5404
5405 PRTFSISOVOLUDFPMAP paPartMaps = NULL;
5406 if (pLvd->cPartitionMaps > 0)
5407 {
5408 pInfo->paPartMaps = paPartMaps = (PRTFSISOVOLUDFPMAP)RTMemAllocZ(sizeof(paPartMaps[0]) * pLvd->cPartitionMaps);
5409 if (!paPartMaps)
5410 return VERR_NO_MEMORY;
5411 }
5412 uint32_t cPartMaps = 0;
5413
5414 if (pLvd->cbMapTable)
5415 {
5416 uint32_t off = 0;
5417 while (off + sizeof(UDFPARTMAPHDR) <= pLvd->cbMapTable)
5418 {
5419 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pLvd->abPartitionMaps[off];
5420
5421 /*
5422 * Bounds checking.
5423 */
5424 if (off + pHdr->cb > pLvd->cbMapTable)
5425 {
5426 if (cPartMaps < pLvd->cbMapTable)
5427 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MALFORMED_PART_MAP_TABLE,
5428 "Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)",
5429 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType);
5430 LogRel(("ISO/UDF: Warning: Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)\n",
5431 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType));
5432 break;
5433 }
5434 if (cPartMaps >= pLvd->cPartitionMaps)
5435 {
5436 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",
5437 cPartMaps - pLvd->cPartitionMaps, off, pHdr->cb, pLvd->cbMapTable, pHdr->bType));
5438 break;
5439 }
5440
5441 /*
5442 * Extract relevant info out of the entry.
5443 */
5444 paPartMaps[cPartMaps].offMapTable = (uint16_t)off;
5445 uint16_t uPartitionNo;
5446 if (pHdr->bType == 1)
5447 {
5448 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
5449 paPartMaps[cPartMaps].uVolumeSeqNo = pType1->uVolumeSeqNo;
5450 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_PLAIN;
5451 uPartitionNo = pType1->uPartitionNo;
5452 }
5453 else if (pHdr->bType == 2)
5454 {
5455 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
5456 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_VPM_PARTITION_TYPE))
5457 {
5458 paPartMaps[cPartMaps].bType = pType2->idPartitionType.Suffix.Udf.uUdfRevision >= 0x200
5459 ? RTFSISO_UDF_PMAP_T_VPM_20 : RTFSISO_UDF_PMAP_T_VPM_15;
5460 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_VPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
5461 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
5462 }
5463 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
5464 {
5465 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_SPM;
5466 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_SPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
5467 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
5468 }
5469 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
5470 {
5471 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_MPM;
5472 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
5473 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
5474 }
5475 else
5476 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_TYPE_ID,
5477 "Unknown partition map ID for #%u @ %#x: %.23s",
5478 cPartMaps, off, pType2->idPartitionType.achIdentifier);
5479#if 0 /* unreachable code */
5480 paPartMaps[cPartMaps].uVolumeSeqNo = pType2->uVolumeSeqNo;
5481 uPartitionNo = pType2->uPartitionNo;
5482#endif
5483 }
5484 else
5485 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_ENTRY_TYPE,
5486 "Unknown partition map entry type #%u @ %#x: %u", cPartMaps, off, pHdr->bType);
5487 paPartMaps[cPartMaps].uPartitionNo = uPartitionNo;
5488
5489 /*
5490 * Lookup the partition number and retrieve the relevant info from the partition descriptor.
5491 */
5492 uint32_t i = pInfo->cPartitions;
5493 while (i-- > 0)
5494 {
5495 PUDFPARTITIONDESC pPd = pInfo->apPartitions[i];
5496 if (paPartMaps[cPartMaps].uPartitionNo == pPd->uPartitionNo)
5497 {
5498 paPartMaps[cPartMaps].idxPartDesc = (uint16_t)i;
5499 paPartMaps[cPartMaps].cSectors = pPd->cSectors;
5500 paPartMaps[cPartMaps].offLocation = pPd->offLocation;
5501 paPartMaps[cPartMaps].offByteLocation = (uint64_t)pPd->offLocation * pThis->cbSector;
5502 paPartMaps[cPartMaps].fFlags = pPd->fFlags;
5503 paPartMaps[cPartMaps].uAccessType = pPd->uAccessType;
5504 if (!UDF_ENTITY_ID_EQUALS(&pPd->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
5505 paPartMaps[cPartMaps].fHaveHdr = false;
5506 else
5507 {
5508 paPartMaps[cPartMaps].fHaveHdr = true;
5509 paPartMaps[cPartMaps].Hdr = pPd->ContentsUse.Hdr;
5510 }
5511 break;
5512 }
5513 }
5514 if (i > pInfo->cPartitions)
5515 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_PARTITION_NOT_FOUND,
5516 "Partition #%u (%#x) specified by mapping entry #%u (@ %#x) was not found! (int-type %u)",
5517 uPartitionNo, uPartitionNo, cPartMaps, off, paPartMaps[cPartMaps].bType);
5518
5519 /*
5520 * Advance.
5521 */
5522 cPartMaps++;
5523 off += pHdr->cb;
5524 }
5525
5526 if (cPartMaps < pLvd->cPartitionMaps)
5527 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INCOMPLETE_PART_MAP_TABLE,
5528 "Only found %u of the %u announced partition mapping table entries",
5529 cPartMaps, pLvd->cPartitionMaps);
5530 }
5531
5532 /* It might be theoretically possible to not use virtual partitions for
5533 accessing data, so just warn if there aren't any. */
5534 if (cPartMaps == 0)
5535 LogRel(("ISO/UDF: Warning: No partition maps!\n"));
5536
5537 /*
5538 * Check out the logical volume descriptor.
5539 */
5540 if ( pLvd->cbLogicalBlock < pThis->cbSector
5541 || pLvd->cbLogicalBlock > RTFSISO_MAX_LOGICAL_BLOCK_SIZE
5542 || (pLvd->cbLogicalBlock % pThis->cbSector) != 0)
5543 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_LOGICAL_BLOCK_SIZE,
5544 "Logical block size of %#x is not supported with a sector size of %#x",
5545 pLvd->cbLogicalBlock, pThis->cbSector);
5546
5547 if (!UDF_ENTITY_ID_EQUALS(&pLvd->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
5548 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DOMAIN_ID,
5549 "Unsupported domain ID in logical volume descriptor: '%.23s'", pLvd->idDomain.achIdentifier);
5550
5551 if ( pLvd->ContentsUse.FileSetDescriptor.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED
5552 || pLvd->ContentsUse.FileSetDescriptor.cb == 0
5553 || pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo >= cPartMaps)
5554 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_FILE_SET_DESC_LOCATION,
5555 "Malformed file set descriptor location (type=%u cb=%#x part=%#x)",
5556 pLvd->ContentsUse.FileSetDescriptor.uType,
5557 pLvd->ContentsUse.FileSetDescriptor.cb,
5558 pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo);
5559
5560 bool fLvdHaveVolId = !ASMMemIsZero(pLvd->achLogicalVolumeID, sizeof(pLvd->achLogicalVolumeID));
5561 if ( fLvdHaveVolId
5562 && !UDF_IS_CHAR_SET_OSTA(&pLvd->DescCharSet))
5563 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DESC_CHAR_SET,
5564 "Logical volume ID is not using OSTA compressed unicode");
5565
5566 /*
5567 * We can ignore much, if not all of the primary volume descriptor.
5568 */
5569
5570 /*
5571 * We're good. So copy over the data.
5572 */
5573 pThis->Udf.VolInfo.FileSetDescriptor = pLvd->ContentsUse.FileSetDescriptor;
5574 pThis->Udf.VolInfo.cbBlock = pLvd->cbLogicalBlock;
5575 pThis->Udf.VolInfo.cShiftBlock = 9;
5576 while (pThis->Udf.VolInfo.cbBlock != RT_BIT_32(pThis->Udf.VolInfo.cShiftBlock))
5577 pThis->Udf.VolInfo.cShiftBlock++;
5578 pThis->Udf.VolInfo.fFlags = pPvd->fFlags;
5579 pThis->Udf.VolInfo.cPartitions = cPartMaps;
5580 pThis->Udf.VolInfo.paPartitions = paPartMaps;
5581 pInfo->paPartMaps = NULL;
5582 if (fLvdHaveVolId)
5583 memcpy(pThis->Udf.VolInfo.achLogicalVolumeID, pLvd->achLogicalVolumeID, sizeof(pThis->Udf.VolInfo.achLogicalVolumeID));
5584 else
5585 RT_ZERO(pThis->Udf.VolInfo.achLogicalVolumeID);
5586
5587 return VINF_SUCCESS;
5588}
5589
5590
5591/**
5592 * Processes a primary volume descriptor in the VDS (UDF).
5593 *
5594 * @returns IPRT status code.
5595 * @param pInfo Where we gather descriptor information.
5596 * @param pDesc The descriptor.
5597 * @param pErrInfo Where to return extended error information.
5598 */
5599//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
5600static int rtFsIsoVolProcessUdfPrimaryVolDesc(PRTFSISOVDSINFO pInfo, PCUDFPRIMARYVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
5601{
5602#ifdef LOG_ENABLED
5603 Log(("ISO/UDF: Primary volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5604 if (LogIs2Enabled())
5605 {
5606 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5607 UDF_LOG2_MEMBER(pDesc, "#010RX32", uPrimaryVolumeDescNo);
5608 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeID);
5609 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
5610 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
5611 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxVolumeSeqNo);
5612 UDF_LOG2_MEMBER(pDesc, "#06RX16", uInterchangeLevel);
5613 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxInterchangeLevel);
5614 UDF_LOG2_MEMBER(pDesc, "#010RX32", fCharacterSets);
5615 UDF_LOG2_MEMBER(pDesc, "#010RX32", fMaxCharacterSets);
5616 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeSetID);
5617 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
5618 UDF_LOG2_MEMBER_CHARSPEC(pDesc, ExplanatoryCharSet);
5619 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeAbstract);
5620 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeCopyrightNotice);
5621 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idApplication);
5622 UDF_LOG2_MEMBER_TIMESTAMP(pDesc, RecordingTimestamp);
5623 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5624 if (!ASMMemIsZero(&pDesc->abImplementationUse, sizeof(pDesc->abImplementationUse)))
5625 Log2(("ISO/UDF: %-32s %.64Rhxs\n", "abReserved[64]:", &pDesc->abImplementationUse[0]));
5626 UDF_LOG2_MEMBER(pDesc, "#010RX32", offPredecessorVolDescSeq);
5627 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
5628 if (!ASMMemIsZero(&pDesc->abReserved, sizeof(pDesc->abReserved)))
5629 Log2(("ISO/UDF: %-32s %.22Rhxs\n", "abReserved[22]:", &pDesc->abReserved[0]));
5630 }
5631#endif
5632
5633 /*
5634 * Check if this is a new revision of an existing primary volume descriptor.
5635 */
5636 PUDFPRIMARYVOLUMEDESC pEndianConvert = NULL;
5637 uint32_t i = pInfo->cPrimaryVols;
5638 while (i--> 0)
5639 {
5640 if ( memcmp(pDesc->achVolumeID, pInfo->apPrimaryVols[i]->achVolumeID, sizeof(pDesc->achVolumeID)) == 0
5641 && memcmp(&pDesc->DescCharSet, &pInfo->apPrimaryVols[i]->DescCharSet, sizeof(pDesc->DescCharSet)) == 0)
5642 {
5643 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPrimaryVols[i]->uVolumeDescSeqNo)
5644 {
5645 Log(("ISO/UDF: Primary descriptor prevails over previous! (%u >= %u)\n",
5646 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5647 pEndianConvert = pInfo->apPrimaryVols[i];
5648 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
5649 }
5650 else
5651 Log(("ISO/UDF: Primary descriptor has lower sequence number than the previous! (%u < %u)\n",
5652 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5653 break;
5654 }
5655 }
5656 if (i >= pInfo->cPrimaryVols)
5657 {
5658 /*
5659 * It wasn't. Append it.
5660 */
5661 i = pInfo->cPrimaryVols;
5662 if (i < RT_ELEMENTS(pInfo->apPrimaryVols))
5663 {
5664 pInfo->apPrimaryVols[i] = pEndianConvert = (PUDFPRIMARYVOLUMEDESC)RTMemDup(pDesc, sizeof(*pDesc));
5665 if (pEndianConvert)
5666 pInfo->cPrimaryVols = i + 1;
5667 else
5668 return VERR_NO_MEMORY;
5669 Log2(("ISO/UDF: ++New primary descriptor.\n"));
5670 }
5671 else
5672 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PVDS, "Encountered too many primary volume descriptors");
5673 }
5674
5675#ifdef RT_BIG_ENDIAN
5676 /*
5677 * Do endian conversion of the descriptor.
5678 */
5679 if (pEndianConvert)
5680 {
5681 AssertFailed();
5682 }
5683#else
5684 RT_NOREF(pEndianConvert);
5685#endif
5686 return VINF_SUCCESS;
5687}
5688
5689
5690/**
5691 * Processes an logical volume descriptor in the VDS (UDF).
5692 *
5693 * @returns IPRT status code.
5694 * @param pInfo Where we gather descriptor information.
5695 * @param pDesc The descriptor.
5696 * @param cbSector The sector size (UDF defines the logical and physical
5697 * sector size to be the same).
5698 * @param pErrInfo Where to return extended error information.
5699 */
5700static int rtFsIsoVolProcessUdfLogicalVolumeDesc(PRTFSISOVDSINFO pInfo, PCUDFLOGICALVOLUMEDESC pDesc,
5701 uint32_t cbSector, PRTERRINFO pErrInfo)
5702{
5703#ifdef LOG_ENABLED
5704 Log(("ISO/UDF: Logical volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5705 if (LogIs2Enabled())
5706 {
5707 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5708 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
5709 UDF_LOG2_MEMBER_DSTRING(pDesc, achLogicalVolumeID);
5710 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbLogicalBlock);
5711 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idDomain);
5712 if (UDF_ENTITY_ID_EQUALS(&pDesc->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
5713 UDF_LOG2_MEMBER_LONGAD(pDesc, ContentsUse.FileSetDescriptor);
5714 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
5715 Log2(("ISO/UDF: %-32s %.16Rhxs\n", "ContentsUse.ab[16]:", &pDesc->ContentsUse.ab[0]));
5716 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbMapTable);
5717 UDF_LOG2_MEMBER(pDesc, "#010RX32", cPartitionMaps);
5718 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5719 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
5720 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
5721 UDF_LOG2_MEMBER_EXTENTAD(pDesc, IntegritySeqExtent);
5722 if (pDesc->cbMapTable)
5723 {
5724 Log2(("ISO/UDF: %-32s\n", "abPartitionMaps"));
5725 uint32_t iMap = 0;
5726 uint32_t off = 0;
5727 while (off + sizeof(UDFPARTMAPHDR) <= pDesc->cbMapTable)
5728 {
5729 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pDesc->abPartitionMaps[off];
5730 Log2(("ISO/UDF: %02u @ %#05x: type %u, length %u\n", iMap, off, pHdr->bType, pHdr->cb));
5731 if (off + pHdr->cb > pDesc->cbMapTable)
5732 {
5733 Log2(("ISO/UDF: BAD! Entry is %d bytes too long!\n", off + pHdr->cb - pDesc->cbMapTable));
5734 break;
5735 }
5736 if (pHdr->bType == 1)
5737 {
5738 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
5739 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uVolumeSeqNo, 5);
5740 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uPartitionNo, 5);
5741 }
5742 else if (pHdr->bType == 2)
5743 {
5744 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
5745 UDF_LOG2_MEMBER_ENTITY_ID_EX(pType2, idPartitionType, 5);
5746 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uVolumeSeqNo, 5);
5747 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uPartitionNo, 5);
5748 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
5749 {
5750 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Spm.cBlocksPerPacket, 5);
5751 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.cSparingTables, 5);
5752 if (pType2->u.Spm.bReserved2)
5753 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.bReserved2, 5);
5754 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.cbSparingTable, 5);
5755 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[0], 5);
5756 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[1], 5);
5757 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[2], 5);
5758 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[3], 5);
5759 }
5760 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
5761 {
5762 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataFile, 5);
5763 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataMirrorFile, 5);
5764 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataBitmapFile, 5);
5765 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.cBlocksAllocationUnit, 5);
5766 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Mpm.cBlocksAlignmentUnit, 5);
5767 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Mpm.fFlags, 5);
5768 if (!ASMMemIsZero(pType2->u.Mpm.abReserved2, sizeof(pType2->u.Mpm.abReserved2)))
5769 UDF_LOG2_MEMBER_EX(&pType2->u, ".5Rhxs", Mpm.abReserved2, 5);
5770 }
5771 }
5772 else
5773 Log2(("ISO/UDF: BAD! Unknown type!\n"));
5774
5775 /* advance */
5776 off += pHdr->cb;
5777 iMap++;
5778 }
5779 }
5780 }
5781#endif
5782
5783 /*
5784 * Check if this is a newer revision of an existing primary volume descriptor.
5785 */
5786 size_t cbDesc = (size_t)pDesc->cbMapTable + RT_UOFFSETOF(UDFLOGICALVOLUMEDESC, abPartitionMaps);
5787 if ( pDesc->cbMapTable >= (UINT32_MAX >> 1)
5788 || cbDesc > cbSector)
5789 {
5790 Log(("ISO/UDF: Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector));
5791 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD,
5792 "Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector);
5793 }
5794
5795 PUDFLOGICALVOLUMEDESC pEndianConvert = NULL;
5796 uint32_t i = pInfo->cLogicalVols;
5797 while (i--> 0)
5798 if ( memcmp(pDesc->achLogicalVolumeID, pInfo->apLogicalVols[i]->achLogicalVolumeID,
5799 sizeof(pDesc->achLogicalVolumeID)) == 0
5800 && memcmp(&pDesc->DescCharSet, &pInfo->apLogicalVols[i]->DescCharSet,
5801 sizeof(pDesc->DescCharSet)) == 0)
5802 {
5803 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apLogicalVols[i]->uVolumeDescSeqNo)
5804 {
5805 Log(("ISO/UDF: Logical descriptor prevails over previous! (%u >= %u)\n",
5806 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
5807 pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
5808 if (!pEndianConvert)
5809 return VERR_NO_MEMORY;
5810 RTMemFree(pInfo->apLogicalVols[i]);
5811 pInfo->apLogicalVols[i] = pEndianConvert;
5812 }
5813 else
5814 Log(("ISO/UDF: Logical descriptor has lower sequence number than the previous! (%u >= %u)\n",
5815 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
5816 break;
5817 }
5818 if (i >= pInfo->cLogicalVols)
5819 {
5820 /*
5821 * It wasn't. Append it.
5822 */
5823 i = pInfo->cLogicalVols;
5824 if (i < RT_ELEMENTS(pInfo->apLogicalVols))
5825 {
5826 pInfo->apLogicalVols[i] = pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
5827 if (pEndianConvert)
5828 pInfo->cLogicalVols = i + 1;
5829 else
5830 return VERR_NO_MEMORY;
5831 Log2(("ISO/UDF: ++New logical volume descriptor.\n"));
5832 }
5833 else
5834 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_LVDS, "Too many logical volume descriptors");
5835 }
5836
5837#ifdef RT_BIG_ENDIAN
5838 /*
5839 * Do endian conversion of the descriptor.
5840 */
5841 if (pEndianConvert)
5842 {
5843 AssertFailed();
5844 }
5845#else
5846 RT_NOREF(pEndianConvert);
5847#endif
5848 return VINF_SUCCESS;
5849}
5850
5851
5852/**
5853 * Processes an partition descriptor in the VDS (UDF).
5854 *
5855 * @returns IPRT status code.
5856 * @param pInfo Where we gather descriptor information.
5857 * @param pDesc The descriptor.
5858 * @param pErrInfo Where to return extended error information.
5859 */
5860static int rtFsIsoVolProcessUdfPartitionDesc(PRTFSISOVDSINFO pInfo, PCUDFPARTITIONDESC pDesc, PRTERRINFO pErrInfo)
5861{
5862#ifdef LOG_ENABLED
5863 Log(("ISO/UDF: Partition descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5864 if (LogIs2Enabled())
5865 {
5866 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5867 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
5868 UDF_LOG2_MEMBER(pDesc, "#06RX16", uPartitionNo);
5869 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, PartitionContents);
5870 if (UDF_ENTITY_ID_EQUALS(&pDesc->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
5871 {
5872 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceTable);
5873 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceBitmap);
5874 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.PartitionIntegrityTable);
5875 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceTable);
5876 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceBitmap);
5877 if (!ASMMemIsZero(&pDesc->ContentsUse.Hdr.abReserved[0], sizeof(pDesc->ContentsUse.Hdr.abReserved)))
5878 Log2(("ISO/UDF: %-32s\n%.88RhxD\n", "Hdr.abReserved[88]:", &pDesc->ContentsUse.Hdr.abReserved[0]));
5879 }
5880 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
5881 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ContentsUse.ab[128]:", &pDesc->ContentsUse.ab[0]));
5882 UDF_LOG2_MEMBER(pDesc, "#010RX32", uAccessType);
5883 UDF_LOG2_MEMBER(pDesc, "#010RX32", offLocation);
5884 UDF_LOG2_MEMBER(pDesc, "#010RX32", cSectors);
5885 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5886 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
5887 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
5888
5889 if (!ASMMemIsZero(&pDesc->abReserved[0], sizeof(pDesc->abReserved)))
5890 Log2(("ISO/UDF: %-32s\n%.156RhxD\n", "ImplementationUse.ab[156]:", &pDesc->abReserved[0]));
5891 }
5892#endif
5893
5894 /*
5895 * Check if this is a newer revision of an existing primary volume descriptor.
5896 */
5897 PUDFPARTITIONDESC pEndianConvert = NULL;
5898 uint32_t i = pInfo->cPartitions;
5899 while (i--> 0)
5900 if (pDesc->uPartitionNo == pInfo->apPartitions[i]->uPartitionNo)
5901 {
5902 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPartitions[i]->uVolumeDescSeqNo)
5903 {
5904 Log(("ISO/UDF: Partition descriptor for part %#u prevails over previous! (%u >= %u)\n",
5905 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5906 pEndianConvert = pInfo->apPartitions[i];
5907 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
5908 }
5909 else
5910 Log(("ISO/UDF: Partition descriptor for part %#u has a lower sequence number than the previous! (%u < %u)\n",
5911 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
5912 break;
5913 }
5914 if (i >= pInfo->cPartitions)
5915 {
5916 /*
5917 * It wasn't. Append it.
5918 */
5919 i = pInfo->cPartitions;
5920 if (i < RT_ELEMENTS(pInfo->apPartitions))
5921 {
5922 pInfo->apPartitions[i] = pEndianConvert = (PUDFPARTITIONDESC)RTMemDup(pDesc, sizeof(*pDesc));
5923 if (pEndianConvert)
5924 pInfo->cPartitions = i + 1;
5925 else
5926 return VERR_NO_MEMORY;
5927 Log2(("ISO/UDF: ++New partition descriptor.\n"));
5928 }
5929 else
5930 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PDS, "Too many physical volume descriptors");
5931 }
5932
5933#ifdef RT_BIG_ENDIAN
5934 /*
5935 * Do endian conversion of the descriptor.
5936 */
5937 if (pEndianConvert)
5938 {
5939 AssertFailed();
5940 }
5941#else
5942 RT_NOREF(pEndianConvert);
5943#endif
5944 return VINF_SUCCESS;
5945}
5946
5947
5948/**
5949 * Processes an implementation use descriptor in the VDS (UDF).
5950 *
5951 * @returns IPRT status code.
5952 * @param pInfo Where we gather descriptor information.
5953 * @param pDesc The descriptor.
5954 * @param pErrInfo Where to return extended error information.
5955 */
5956static int rtFsIsoVolProcessUdfImplUseVolDesc(PRTFSISOVDSINFO pInfo, PCUDFIMPLEMENTATIONUSEVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
5957{
5958#ifdef LOG_ENABLED
5959 Log(("ISO/UDF: Implementation use volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
5960 if (LogIs2Enabled())
5961 {
5962 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
5963 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
5964 if (UDF_ENTITY_ID_EQUALS(&pDesc->idImplementation, UDF_ENTITY_ID_IUVD_IMPLEMENTATION))
5965 {
5966 UDF_LOG2_MEMBER_CHARSPEC(&pDesc->ImplementationUse, Lvi.Charset);
5967 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achVolumeID);
5968 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo1);
5969 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo2);
5970 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo3);
5971 UDF_LOG2_MEMBER_ENTITY_ID(&pDesc->ImplementationUse, Lvi.idImplementation);
5972 if (!ASMMemIsZero(&pDesc->ImplementationUse.Lvi.abUse[0], sizeof(pDesc->ImplementationUse.Lvi.abUse)))
5973 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "Lvi.abUse[128]:", &pDesc->ImplementationUse.Lvi.abUse[0]));
5974 }
5975 else if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
5976 Log2(("ISO/UDF: %-32s\n%.460RhxD\n", "ImplementationUse.ab[460]:", &pDesc->ImplementationUse.ab[0]));
5977 }
5978#endif
5979
5980 RT_NOREF(pInfo, pDesc, pErrInfo);
5981 return VINF_SUCCESS;
5982}
5983
5984
5985
5986typedef struct RTFSISOSEENSEQENCES
5987{
5988 /** Number of sequences we've seen thus far. */
5989 uint32_t cSequences;
5990 /** The per sequence data. */
5991 struct
5992 {
5993 uint64_t off; /**< Byte offset of the sequence. */
5994 uint32_t cb; /**< Size of the sequence. */
5995 } aSequences[8];
5996} RTFSISOSEENSEQENCES;
5997typedef RTFSISOSEENSEQENCES *PRTFSISOSEENSEQENCES;
5998
5999
6000
6001/**
6002 * Process a VDS sequence, recursively dealing with volume descriptor pointers.
6003 *
6004 * This function only gathers information from the sequence, handling the
6005 * prevailing descriptor fun.
6006 *
6007 * @returns IPRT status code.
6008 * @param pThis The instance.
6009 * @param pInfo Where to store info from the VDS sequence.
6010 * @param offSeq The byte offset of the sequence.
6011 * @param cbSeq The length of the sequence.
6012 * @param pbBuf Read buffer.
6013 * @param cbBuf Size of the read buffer. This is at least one
6014 * sector big.
6015 * @param cNestings The VDS nesting depth.
6016 * @param pErrInfo Where to return extended error info.
6017 */
6018static int rtFsIsoVolReadAndProcessUdfVdsSeq(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, uint64_t offSeq, uint32_t cbSeq,
6019 uint8_t *pbBuf, size_t cbBuf, uint32_t cNestings, PRTERRINFO pErrInfo)
6020{
6021 AssertReturn(cbBuf >= pThis->cbSector, VERR_INTERNAL_ERROR);
6022
6023 /*
6024 * Check nesting depth.
6025 */
6026 if (cNestings > 5)
6027 return RTERRINFO_LOG_SET(pErrInfo, VERR_TOO_MUCH_DATA, "The volume descriptor sequence (VDS) is nested too deeply.");
6028
6029
6030 /*
6031 * Do the processing sector by sector to keep things simple.
6032 */
6033 uint32_t offInSeq = 0;
6034 while (offInSeq < cbSeq)
6035 {
6036 int rc;
6037
6038 /*
6039 * Read the next sector. Zero pad if less that a sector.
6040 */
6041 Assert((offInSeq & (pThis->cbSector - 1)) == 0);
6042 rc = RTVfsFileReadAt(pThis->hVfsBacking, offSeq + offInSeq, pbBuf, pThis->cbSector, NULL);
6043 if (RT_FAILURE(rc))
6044 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error reading VDS content at %RX64 (LB %#x): %Rrc",
6045 offSeq + offInSeq, pThis->cbSector, rc);
6046 if (cbSeq - offInSeq < pThis->cbSector)
6047 memset(&pbBuf[cbSeq - offInSeq], 0, pThis->cbSector - (cbSeq - offInSeq));
6048
6049 /*
6050 * Check tag.
6051 */
6052 PCUDFTAG pTag = (PCUDFTAG)pbBuf;
6053 rc = rtFsIsoVolValidateUdfDescTagAndCrc(pTag, pThis->cbSector, UINT16_MAX, (offSeq + offInSeq) / pThis->cbSector, pErrInfo);
6054 if ( RT_SUCCESS(rc)
6055 || ( rc == VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
6056 && ( pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC
6057 || pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_DESC
6058 || pTag->idTag == UDF_TAG_ID_UNALLOCATED_SPACE_DESC
6059 )
6060 )
6061 )
6062 {
6063 switch (pTag->idTag)
6064 {
6065 case UDF_TAG_ID_PRIMARY_VOL_DESC:
6066 rc = rtFsIsoVolProcessUdfPrimaryVolDesc(pInfo, (PCUDFPRIMARYVOLUMEDESC)pTag, pErrInfo);
6067 break;
6068
6069 case UDF_TAG_ID_IMPLEMENTATION_USE_VOLUME_DESC:
6070 rc = rtFsIsoVolProcessUdfImplUseVolDesc(pInfo, (PCUDFIMPLEMENTATIONUSEVOLUMEDESC)pTag, pErrInfo);
6071 break;
6072
6073 case UDF_TAG_ID_PARTITION_DESC:
6074 rc = rtFsIsoVolProcessUdfPartitionDesc(pInfo, (PCUDFPARTITIONDESC)pTag, pErrInfo);
6075 break;
6076
6077 case UDF_TAG_ID_LOGICAL_VOLUME_DESC:
6078 if (rc != VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC)
6079 rc = rtFsIsoVolProcessUdfLogicalVolumeDesc(pInfo, (PCUDFLOGICALVOLUMEDESC)pTag,
6080 pThis->cbSector, pErrInfo);
6081 else
6082 rc = VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD;
6083 break;
6084
6085 case UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC:
6086 Log(("ISO/UDF: Ignoring logical volume integrity descriptor at offset %#RX64.\n", offSeq + offInSeq));
6087 rc = VINF_SUCCESS;
6088 break;
6089
6090 case UDF_TAG_ID_UNALLOCATED_SPACE_DESC:
6091 Log(("ISO/UDF: Ignoring unallocated space descriptor at offset %#RX64.\n", offSeq + offInSeq));
6092 rc = VINF_SUCCESS;
6093 break;
6094
6095 case UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR:
6096 Log(("ISO/UDF: Ignoring AVDP in VDS (at offset %#RX64).\n", offSeq + offInSeq));
6097 rc = VINF_SUCCESS;
6098 break;
6099
6100 case UDF_TAG_ID_VOLUME_DESC_PTR:
6101 {
6102 PCUDFVOLUMEDESCPTR pVdp = (PCUDFVOLUMEDESCPTR)pTag;
6103 Log(("ISO/UDF: Processing volume descriptor pointer at offset %#RX64: %#x LB %#x (seq %#x); cNestings=%d\n",
6104 offSeq + offInSeq, pVdp->NextVolumeDescSeq.off, pVdp->NextVolumeDescSeq.cb,
6105 pVdp->uVolumeDescSeqNo, cNestings));
6106 rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, pInfo, (uint64_t)pVdp->NextVolumeDescSeq.off * pThis->cbSector,
6107 pVdp->NextVolumeDescSeq.cb, pbBuf, cbBuf, cNestings + 1, pErrInfo);
6108 break;
6109 }
6110
6111 case UDF_TAG_ID_TERMINATING_DESC:
6112 Log(("ISO/UDF: Terminating descriptor at offset %#RX64\n", offSeq + offInSeq));
6113 return VINF_SUCCESS;
6114
6115 default:
6116 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNEXPECTED_VDS_DESC,
6117 "Unexpected/unknown VDS descriptor %#x at byte offset %#RX64",
6118 pThis->cbSector, offSeq + offInSeq);
6119 }
6120 if (RT_FAILURE(rc))
6121 return rc;
6122 }
6123 /* The descriptor sequence is usually zero padded to 16 sectors. Just
6124 ignore zero descriptors. */
6125 else if (rc != VERR_ISOFS_TAG_IS_ALL_ZEROS)
6126 return rc;
6127
6128 /*
6129 * Advance.
6130 */
6131 offInSeq += pThis->cbSector;
6132 }
6133
6134 return VINF_SUCCESS;
6135}
6136
6137
6138
6139/**
6140 * Processes a volume descriptor sequence (VDS).
6141 *
6142 * @returns IPRT status code.
6143 * @param pThis The instance.
6144 * @param offSeq The byte offset of the sequence.
6145 * @param cbSeq The length of the sequence.
6146 * @param pSeenSequences Structure where to keep track of VDSes we've already
6147 * processed, to avoid redoing one that we don't
6148 * understand.
6149 * @param pbBuf Read buffer.
6150 * @param cbBuf Size of the read buffer. This is at least one
6151 * sector big.
6152 * @param pErrInfo Where to report extended error information.
6153 */
6154static int rtFsIsoVolReadAndProcessUdfVds(PRTFSISOVOL pThis, uint64_t offSeq, uint32_t cbSeq,
6155 PRTFSISOSEENSEQENCES pSeenSequences, uint8_t *pbBuf, size_t cbBuf,
6156 PRTERRINFO pErrInfo)
6157{
6158 /*
6159 * Skip if already seen.
6160 */
6161 uint32_t i = pSeenSequences->cSequences;
6162 while (i-- > 0)
6163 if ( pSeenSequences->aSequences[i].off == offSeq
6164 && pSeenSequences->aSequences[i].cb == cbSeq)
6165 return VERR_NOT_FOUND;
6166
6167 /* Not seen, so add it. */
6168 Assert(pSeenSequences->cSequences + 1 <= RT_ELEMENTS(pSeenSequences->aSequences));
6169 pSeenSequences->aSequences[pSeenSequences->cSequences].cb = cbSeq;
6170 pSeenSequences->aSequences[pSeenSequences->cSequences].off = offSeq;
6171 pSeenSequences->cSequences++;
6172
6173 LogFlow(("ISO/UDF: Processing anchor volume descriptor sequence at offset %#RX64 LB %#RX32\n", offSeq, cbSeq));
6174
6175 /*
6176 * Gather relevant descriptor info from the VDS then process it and on
6177 * success copy it into the instance.
6178 *
6179 * The processing has to be done in a different function because there may
6180 * be links to sub-sequences that needs to be processed. We do this by
6181 * recursing and check that we don't go to deep.
6182 */
6183 RTFSISOVDSINFO Info;
6184 RT_ZERO(Info);
6185 int rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, &Info, offSeq, cbSeq, pbBuf, cbBuf, 0, pErrInfo);
6186 if (RT_SUCCESS(rc))
6187 {
6188 rc = rtFsIsoVolProcessUdfVdsSeqInfo(pThis, &Info, pErrInfo);
6189 if (RT_SUCCESS(rc))
6190 rc = rtFsIsoVolProcessUdfFileSetDescs(pThis, pbBuf, cbBuf, pErrInfo);
6191 }
6192
6193 /*
6194 * Clean up info.
6195 */
6196 i = Info.cPrimaryVols;
6197 while (i-- > 0)
6198 RTMemFree(Info.apPrimaryVols[i]);
6199
6200 i = Info.cLogicalVols;
6201 while (i-- > 0)
6202 RTMemFree(Info.apLogicalVols[i]);
6203
6204 i = Info.cPartitions;
6205 while (i-- > 0)
6206 RTMemFree(Info.apPartitions[i]);
6207
6208 RTMemFree(Info.paPartMaps);
6209
6210 return rc;
6211}
6212
6213
6214static int rtFsIsoVolReadAndHandleUdfAvdp(PRTFSISOVOL pThis, uint64_t offAvdp, uint8_t *pbBuf, size_t cbBuf,
6215 PRTFSISOSEENSEQENCES pSeenSequences, PRTERRINFO pErrInfo)
6216{
6217 /*
6218 * Try read the descriptor and validate its tag.
6219 */
6220 PUDFANCHORVOLUMEDESCPTR pAvdp = (PUDFANCHORVOLUMEDESCPTR)pbBuf;
6221 size_t cbAvdpRead = RT_MIN(pThis->cbSector, cbBuf);
6222 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offAvdp, pAvdp, cbAvdpRead, NULL);
6223 if (RT_SUCCESS(rc))
6224 {
6225 rc = rtFsIsoVolValidateUdfDescTag(&pAvdp->Tag, UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR, offAvdp / pThis->cbSector, pErrInfo);
6226 if (RT_SUCCESS(rc))
6227 {
6228 Log2(("ISO/UDF: AVDP: MainVolumeDescSeq=%#RX32 LB %#RX32, ReserveVolumeDescSeq=%#RX32 LB %#RX32\n",
6229 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb,
6230 pAvdp->ReserveVolumeDescSeq.off, pAvdp->ReserveVolumeDescSeq.cb));
6231
6232 /*
6233 * Try the main sequence if it looks sane.
6234 */
6235 UDFEXTENTAD const ReserveVolumeDescSeq = pAvdp->ReserveVolumeDescSeq;
6236 if ( pAvdp->MainVolumeDescSeq.off < pThis->cBackingSectors
6237 && (uint64_t)pAvdp->MainVolumeDescSeq.off
6238 + (pAvdp->MainVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
6239 <= pThis->cBackingSectors)
6240 {
6241 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)pAvdp->MainVolumeDescSeq.off * pThis->cbSector,
6242 pAvdp->MainVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
6243 if (RT_SUCCESS(rc))
6244 return rc;
6245 }
6246 else
6247 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
6248 "MainVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
6249 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb, pThis->cBackingSectors);
6250 if (ReserveVolumeDescSeq.cb > 0)
6251 {
6252 if ( ReserveVolumeDescSeq.off < pThis->cBackingSectors
6253 && (uint64_t)ReserveVolumeDescSeq.off
6254 + (ReserveVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
6255 <= pThis->cBackingSectors)
6256 {
6257 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)ReserveVolumeDescSeq.off * pThis->cbSector,
6258 ReserveVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
6259 if (RT_SUCCESS(rc))
6260 return rc;
6261 }
6262 else if (RT_SUCCESS(rc))
6263 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
6264 "ReserveVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
6265 ReserveVolumeDescSeq.off, ReserveVolumeDescSeq.cb, pThis->cBackingSectors);
6266 }
6267 }
6268 }
6269 else
6270 rc = RTERRINFO_LOG_SET_F(pErrInfo, rc,
6271 "Error reading sector at offset %#RX64 (anchor volume descriptor pointer): %Rrc", offAvdp, rc);
6272
6273 return rc;
6274}
6275
6276
6277/**
6278 * Goes looking for UDF when we've seens a volume recognition sequence.
6279 *
6280 * @returns IPRT status code.
6281 * @param pThis The volume instance data.
6282 * @param puUdfLevel The UDF level indicated by the VRS.
6283 * @param offUdfBootVolDesc The offset of the BOOT2 descriptor, UINT64_MAX
6284 * if not encountered.
6285 * @param pbBuf Buffer for reading into.
6286 * @param cbBuf The size of the buffer. At least one sector.
6287 * @param pErrInfo Where to return extended error info.
6288 */
6289static int rtFsIsoVolHandleUdfDetection(PRTFSISOVOL pThis, uint8_t *puUdfLevel, uint64_t offUdfBootVolDesc,
6290 uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
6291{
6292 NOREF(offUdfBootVolDesc);
6293
6294 /*
6295 * There are up to three anchor volume descriptor pointers that can give us
6296 * two different descriptor sequences each. Usually, the different AVDP
6297 * structures points to the same two sequences. The idea here is that
6298 * sectors may deteriorate and become unreadable, and we're supposed to try
6299 * out alternative sectors to get the job done. If we really took this
6300 * seriously, we could try read all sequences in parallel and use the
6301 * sectors that are good. However, we'll try keep things reasonably simple
6302 * since we'll most likely be reading from hard disks rather than optical
6303 * media.
6304 *
6305 * We keep track of which sequences we've processed so we don't try to do it
6306 * again when alternative AVDP sectors points to the same sequences.
6307 */
6308 pThis->Udf.uLevel = *puUdfLevel;
6309 RTFSISOSEENSEQENCES SeenSequences;
6310 RT_ZERO(SeenSequences);
6311 int rc1 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, 256 * pThis->cbSector, pbBuf, cbBuf,
6312 &SeenSequences, pErrInfo);
6313 if (RT_SUCCESS(rc1))
6314 return rc1;
6315
6316 int rc2 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - 256 * pThis->cbSector,
6317 pbBuf, cbBuf, &SeenSequences, pErrInfo);
6318 if (RT_SUCCESS(rc2))
6319 return rc2;
6320
6321 int rc3 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - pThis->cbSector,
6322 pbBuf, cbBuf, &SeenSequences, pErrInfo);
6323 if (RT_SUCCESS(rc3))
6324 return rc3;
6325
6326 /*
6327 * Return failure if the alternatives have been excluded.
6328 *
6329 * Note! The error info won't be correct here.
6330 */
6331 pThis->Udf.uLevel = *puUdfLevel = 0;
6332
6333 if (RTFSISO9660_F_IS_ONLY_TYPE(pThis->fFlags, RTFSISO9660_F_NO_UDF))
6334 return rc1 != VERR_NOT_FOUND ? rc1 : rc2 != VERR_NOT_FOUND ? rc2 : rc3;
6335 return VINF_SUCCESS;
6336}
6337
6338
6339
6340#ifdef LOG_ENABLED
6341
6342/** Logging helper. */
6343static size_t rtFsIsoVolGetStrippedLength(const char *pachField, size_t cchField)
6344{
6345 while (cchField > 0 && pachField[cchField - 1] == ' ')
6346 cchField--;
6347 return cchField;
6348}
6349
6350/** Logging helper. */
6351static char *rtFsIsoVolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
6352{
6353 /* Check the format by looking for zero bytes. ISO-9660 doesn't allow zeros.
6354 This doesn't have to be a UTF-16BE string. */
6355 size_t cFirstZeros = 0;
6356 size_t cSecondZeros = 0;
6357 for (size_t off = 0; off + 1 < cchField; off += 2)
6358 {
6359 cFirstZeros += pachField[off] == '\0';
6360 cSecondZeros += pachField[off + 1] == '\0';
6361 }
6362
6363 int rc = VINF_SUCCESS;
6364 char *pszTmp = &pszDst[10];
6365 size_t cchRet = 0;
6366 if (cFirstZeros > cSecondZeros)
6367 {
6368 /* UTF-16BE / UTC-2BE: */
6369 if (cchField & 1)
6370 {
6371 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
6372 cchField--;
6373 else
6374 rc = VERR_INVALID_UTF16_ENCODING;
6375 }
6376 if (RT_SUCCESS(rc))
6377 {
6378 while ( cchField >= 2
6379 && pachField[cchField - 1] == ' '
6380 && pachField[cchField - 2] == '\0')
6381 cchField -= 2;
6382
6383 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
6384 }
6385 if (RT_SUCCESS(rc))
6386 {
6387 pszDst[0] = 'U';
6388 pszDst[1] = 'T';
6389 pszDst[2] = 'F';
6390 pszDst[3] = '-';
6391 pszDst[4] = '1';
6392 pszDst[5] = '6';
6393 pszDst[6] = 'B';
6394 pszDst[7] = 'E';
6395 pszDst[8] = ':';
6396 pszDst[9] = '\'';
6397 pszDst[10 + cchRet] = '\'';
6398 pszDst[10 + cchRet + 1] = '\0';
6399 }
6400 else
6401 RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
6402 }
6403 else if (cSecondZeros > 0)
6404 {
6405 /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
6406 if (cchField & 1)
6407 {
6408 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
6409 cchField--;
6410 else
6411 rc = VERR_INVALID_UTF16_ENCODING;
6412 }
6413 if (RT_SUCCESS(rc))
6414 {
6415 while ( cchField >= 2
6416 && pachField[cchField - 1] == '\0'
6417 && pachField[cchField - 2] == ' ')
6418 cchField -= 2;
6419
6420 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
6421 }
6422 if (RT_SUCCESS(rc))
6423 {
6424 pszDst[0] = 'U';
6425 pszDst[1] = 'T';
6426 pszDst[2] = 'F';
6427 pszDst[3] = '-';
6428 pszDst[4] = '1';
6429 pszDst[5] = '6';
6430 pszDst[6] = 'L';
6431 pszDst[7] = 'E';
6432 pszDst[8] = ':';
6433 pszDst[9] = '\'';
6434 pszDst[10 + cchRet] = '\'';
6435 pszDst[10 + cchRet + 1] = '\0';
6436 }
6437 else
6438 RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
6439 }
6440 else
6441 {
6442 /* ASSUME UTF-8/ASCII. */
6443 while ( cchField > 0
6444 && pachField[cchField - 1] == ' ')
6445 cchField--;
6446 rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
6447 if (RT_SUCCESS(rc))
6448 RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
6449 else
6450 RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
6451 }
6452 return pszDst;
6453}
6454
6455
6456/**
6457 * Logs the primary or supplementary volume descriptor
6458 *
6459 * @param pVolDesc The descriptor.
6460 */
6461static void rtFsIsoVolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
6462{
6463 if (LogIs2Enabled())
6464 {
6465 char szTmp[384];
6466 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
6467 Log2(("ISO9660: achSystemId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
6468 Log2(("ISO9660: achVolumeId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
6469 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
6470 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
6471 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIsoVolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
6472 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
6473 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
6474 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
6475 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
6476 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
6477 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
6478 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
6479 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
6480 Log2(("ISO9660: achVolumeSetId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
6481 Log2(("ISO9660: achPublisherId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
6482 Log2(("ISO9660: achDataPreparerId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
6483 Log2(("ISO9660: achApplicationId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
6484 Log2(("ISO9660: achCopyrightFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
6485 Log2(("ISO9660: achAbstractFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
6486 Log2(("ISO9660: achBibliographicFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
6487 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6488 pVolDesc->BirthTime.achYear,
6489 pVolDesc->BirthTime.achMonth,
6490 pVolDesc->BirthTime.achDay,
6491 pVolDesc->BirthTime.achHour,
6492 pVolDesc->BirthTime.achMinute,
6493 pVolDesc->BirthTime.achSecond,
6494 pVolDesc->BirthTime.achCentisecond,
6495 pVolDesc->BirthTime.offUtc*4/60));
6496 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6497 pVolDesc->ModifyTime.achYear,
6498 pVolDesc->ModifyTime.achMonth,
6499 pVolDesc->ModifyTime.achDay,
6500 pVolDesc->ModifyTime.achHour,
6501 pVolDesc->ModifyTime.achMinute,
6502 pVolDesc->ModifyTime.achSecond,
6503 pVolDesc->ModifyTime.achCentisecond,
6504 pVolDesc->ModifyTime.offUtc*4/60));
6505 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6506 pVolDesc->ExpireTime.achYear,
6507 pVolDesc->ExpireTime.achMonth,
6508 pVolDesc->ExpireTime.achDay,
6509 pVolDesc->ExpireTime.achHour,
6510 pVolDesc->ExpireTime.achMinute,
6511 pVolDesc->ExpireTime.achSecond,
6512 pVolDesc->ExpireTime.achCentisecond,
6513 pVolDesc->ExpireTime.offUtc*4/60));
6514 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
6515 pVolDesc->EffectiveTime.achYear,
6516 pVolDesc->EffectiveTime.achMonth,
6517 pVolDesc->EffectiveTime.achDay,
6518 pVolDesc->EffectiveTime.achHour,
6519 pVolDesc->EffectiveTime.achMinute,
6520 pVolDesc->EffectiveTime.achSecond,
6521 pVolDesc->EffectiveTime.achCentisecond,
6522 pVolDesc->EffectiveTime.offUtc*4/60));
6523 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
6524 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
6525
6526 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
6527 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
6528 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
6529 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
6530 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
6531 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
6532 pVolDesc->RootDir.DirRec.RecTime.bMonth,
6533 pVolDesc->RootDir.DirRec.RecTime.bDay,
6534 pVolDesc->RootDir.DirRec.RecTime.bHour,
6535 pVolDesc->RootDir.DirRec.RecTime.bMinute,
6536 pVolDesc->RootDir.DirRec.RecTime.bSecond,
6537 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
6538 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
6539 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
6540 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
6541 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
6542 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
6543 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
6544 uint32_t offSysUse = RT_UOFFSETOF_DYN(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
6545 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
6546 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
6547 {
6548 Log2(("ISO9660: RootDir System Use:\n%.*RhxD\n",
6549 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
6550 }
6551 }
6552}
6553
6554#endif /* LOG_ENABLED */
6555
6556/**
6557 * Deal with a root directory from a primary or supplemental descriptor.
6558 *
6559 * @returns IPRT status code.
6560 * @param pThis The ISO 9660 instance being initialized.
6561 * @param pRootDir The root directory record to check out.
6562 * @param pDstRootDir Where to store a copy of the root dir record.
6563 * @param pErrInfo Where to return additional error info. Can be NULL.
6564 */
6565static int rtFsIsoVolHandleRootDir(PRTFSISOVOL pThis, PCISO9660DIRREC pRootDir,
6566 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
6567{
6568 if (pRootDir->cbDirRec < RT_UOFFSETOF(ISO9660DIRREC, achFileId))
6569 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
6570 pRootDir->cbDirRec, RT_UOFFSETOF(ISO9660DIRREC, achFileId));
6571
6572 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
6573 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
6574 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
6575 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
6576 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
6577 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
6578
6579 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
6580 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
6581 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
6582 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
6583 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
6584
6585 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
6586 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
6587 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
6588
6589 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
6590 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
6591 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
6592 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
6593 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6594 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
6595 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
6596
6597 /*
6598 * Seems okay, copy it.
6599 */
6600 *pDstRootDir = *pRootDir;
6601 return VINF_SUCCESS;
6602}
6603
6604
6605/**
6606 * Deal with a primary volume descriptor.
6607 *
6608 * @returns IPRT status code.
6609 * @param pThis The ISO 9660 instance being initialized.
6610 * @param pVolDesc The volume descriptor to handle.
6611 * @param offVolDesc The disk offset of the volume descriptor.
6612 * @param pRootDir Where to return a copy of the root directory record.
6613 * @param poffRootDirRec Where to return the disk offset of the root dir.
6614 * @param pErrInfo Where to return additional error info. Can be NULL.
6615 */
6616static int rtFsIsoVolHandlePrimaryVolDesc(PRTFSISOVOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
6617 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
6618{
6619 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
6620 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6621 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
6622
6623 /*
6624 * Take down the location of the primary volume descriptor so we can get
6625 * the volume lable and other info from it later.
6626 */
6627 pThis->offPrimaryVolDesc = offVolDesc;
6628
6629 /*
6630 * We need the block size ...
6631 */
6632 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
6633 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
6634 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
6635 || pThis->cbBlock / pThis->cbSector < 1)
6636 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
6637 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
6638 if (pThis->cbBlock / pThis->cbSector > 128)
6639 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
6640
6641 /*
6642 * ... volume space size ...
6643 */
6644 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
6645 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
6646 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
6647 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
6648 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
6649
6650 /*
6651 * ... number of volumes in the set ...
6652 */
6653 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
6654 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
6655 || pThis->cVolumesInSet == 0)
6656 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
6657 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
6658 if (pThis->cVolumesInSet > 32)
6659 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
6660
6661 /*
6662 * ... primary volume sequence ID ...
6663 */
6664 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
6665 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
6666 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
6667 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
6668 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
6669 || pThis->idPrimaryVol < 1)
6670 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6671 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
6672
6673 /*
6674 * ... and the root directory record.
6675 */
6676 *poffRootDirRec = offVolDesc + RT_UOFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
6677 return rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
6678}
6679
6680
6681/**
6682 * Deal with a supplementary volume descriptor.
6683 *
6684 * @returns IPRT status code.
6685 * @param pThis The ISO 9660 instance being initialized.
6686 * @param pVolDesc The volume descriptor to handle.
6687 * @param offVolDesc The disk offset of the volume descriptor.
6688 * @param pbUcs2Level Where to return the joliet level, if found. Caller
6689 * initializes this to zero, we'll return 1, 2 or 3 if
6690 * joliet was detected.
6691 * @param pRootDir Where to return the root directory, if found.
6692 * @param poffRootDirRec Where to return the disk offset of the root dir.
6693 * @param pErrInfo Where to return additional error info. Can be NULL.
6694 */
6695static int rtFsIsoVolHandleSupplementaryVolDesc(PRTFSISOVOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
6696 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
6697 PRTERRINFO pErrInfo)
6698{
6699 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
6700 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6701 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
6702
6703 /*
6704 * Is this a joliet volume descriptor? If not, we probably don't need to
6705 * care about it.
6706 */
6707 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
6708 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
6709 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
6710 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
6711 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
6712 return VINF_SUCCESS;
6713
6714 /*
6715 * Skip if joliet is unwanted.
6716 */
6717 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
6718 return VINF_SUCCESS;
6719
6720 /*
6721 * Check that the joliet descriptor matches the primary one.
6722 * Note! These are our assumptions and may be wrong.
6723 */
6724 if (pThis->cbBlock == 0)
6725 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6726 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
6727 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
6728 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6729 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
6730 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
6731#if 0 /* Not necessary. */
6732 /* Used to be !=, changed to > for ubuntu 20.10 and later. Wonder if they exclude a few files
6733 and thus end up with a different total. Obviously, this test is a big bogus, as we don't
6734 really seem to care about the value at all... */
6735 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) > pThis->cBlocksInPrimaryVolumeSpace)
6736 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6737 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
6738 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
6739#endif
6740 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
6741 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6742 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
6743 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
6744 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
6745 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6746 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
6747 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
6748
6749 if (*pbUcs2Level != 0)
6750 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
6751
6752 /*
6753 * Switch to the joliet root dir as it has UTF-16 stuff in it.
6754 */
6755 int rc = rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
6756 if (RT_SUCCESS(rc))
6757 {
6758 *poffRootDirRec = offVolDesc + RT_UOFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
6759 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
6760 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
6761 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
6762
6763 /*
6764 * Take down the location of the secondary volume descriptor so we can get
6765 * the volume lable and other info from it later.
6766 */
6767 pThis->offSecondaryVolDesc = offVolDesc;
6768 }
6769 return rc;
6770}
6771
6772
6773
6774/**
6775 * Worker for RTFsIso9660VolOpen.
6776 *
6777 * @returns IPRT status code.
6778 * @param pThis The ISO VFS instance to initialize.
6779 * @param hVfsSelf The ISO VFS handle (no reference consumed).
6780 * @param hVfsBacking The file backing the alleged ISO file system.
6781 * Reference is consumed (via rtFsIsoVol_Close).
6782 * @param fFlags Flags, RTFSISO9660_F_XXX.
6783 * @param pErrInfo Where to return additional error info. Can be NULL.
6784 */
6785static int rtFsIsoVolTryInit(PRTFSISOVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
6786{
6787 uint32_t const cbSector = 2048;
6788
6789 /*
6790 * First initialize the state so that rtFsIsoVol_Close won't trip up.
6791 */
6792 pThis->hVfsSelf = hVfsSelf;
6793 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIsoVol_Close releases it. */
6794 pThis->cbBacking = 0;
6795 pThis->cBackingSectors = 0;
6796 pThis->fFlags = fFlags;
6797 pThis->cbSector = cbSector;
6798 pThis->cbBlock = 0;
6799 pThis->cBlocksInPrimaryVolumeSpace = 0;
6800 pThis->cbPrimaryVolumeSpace = 0;
6801 pThis->cVolumesInSet = 0;
6802 pThis->idPrimaryVol = UINT32_MAX;
6803 pThis->fIsUtf16 = false;
6804 pThis->pRootDir = NULL;
6805 pThis->fHaveRock = false;
6806 pThis->offSuspSkip = 0;
6807 pThis->offRockBuf = UINT64_MAX;
6808
6809 /*
6810 * Do init stuff that may fail.
6811 */
6812 int rc = RTCritSectInit(&pThis->RockBufLock);
6813 AssertRCReturn(rc, rc);
6814
6815 rc = RTVfsFileQuerySize(hVfsBacking, &pThis->cbBacking);
6816 if (RT_SUCCESS(rc))
6817 pThis->cBackingSectors = pThis->cbBacking / pThis->cbSector;
6818 else
6819 return rc;
6820
6821 /*
6822 * Read the volume descriptors starting at logical sector 16.
6823 */
6824 union
6825 {
6826 uint8_t ab[RTFSISO_MAX_LOGICAL_BLOCK_SIZE];
6827 uint16_t au16[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 2];
6828 uint32_t au32[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 4];
6829 ISO9660VOLDESCHDR VolDescHdr;
6830 ISO9660BOOTRECORD BootRecord;
6831 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
6832 ISO9660SUPVOLDESC SupVolDesc;
6833 ISO9660VOLPARTDESC VolPartDesc;
6834 } Buf;
6835 RT_ZERO(Buf);
6836
6837 uint64_t offRootDirRec = UINT64_MAX;
6838 ISO9660DIRREC RootDir;
6839 RT_ZERO(RootDir);
6840
6841 uint64_t offJolietRootDirRec = UINT64_MAX;
6842 uint8_t bJolietUcs2Level = 0;
6843 ISO9660DIRREC JolietRootDir;
6844 RT_ZERO(JolietRootDir);
6845
6846 uint8_t uUdfLevel = 0;
6847 uint64_t offUdfBootVolDesc = UINT64_MAX;
6848
6849 uint32_t cPrimaryVolDescs = 0;
6850 uint32_t cSupplementaryVolDescs = 0;
6851 uint32_t cBootRecordVolDescs = 0;
6852 uint32_t offVolDesc = 16 * cbSector;
6853 enum
6854 {
6855 kStateStart = 0,
6856 kStateNoSeq,
6857 kStateCdSeq,
6858 kStateUdfSeq
6859 } enmState = kStateStart;
6860 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
6861 {
6862 if (iVolDesc > 32)
6863 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "More than 32 volume descriptors, doesn't seem right...");
6864
6865 /* Read the next one and check the signature. */
6866 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
6867 if (RT_FAILURE(rc))
6868 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
6869
6870#define MATCH_STD_ID(a_achStdId1, a_szStdId2) \
6871 ( (a_achStdId1)[0] == (a_szStdId2)[0] \
6872 && (a_achStdId1)[1] == (a_szStdId2)[1] \
6873 && (a_achStdId1)[2] == (a_szStdId2)[2] \
6874 && (a_achStdId1)[3] == (a_szStdId2)[3] \
6875 && (a_achStdId1)[4] == (a_szStdId2)[4] )
6876#define MATCH_HDR(a_pStd, a_bType2, a_szStdId2, a_bVer2) \
6877 ( MATCH_STD_ID((a_pStd)->achStdId, a_szStdId2) \
6878 && (a_pStd)->bDescType == (a_bType2) \
6879 && (a_pStd)->bDescVersion == (a_bVer2) )
6880
6881 /*
6882 * ISO 9660 ("CD001").
6883 */
6884 if ( ( enmState == kStateStart
6885 || enmState == kStateCdSeq
6886 || enmState == kStateNoSeq)
6887 && MATCH_STD_ID(Buf.VolDescHdr.achStdId, ISO9660VOLDESC_STD_ID) )
6888 {
6889 enmState = kStateCdSeq;
6890
6891 /* Do type specific handling. */
6892 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
6893 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
6894 {
6895 cPrimaryVolDescs++;
6896 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
6897 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6898 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
6899#ifdef LOG_ENABLED
6900 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
6901#endif
6902 if (cPrimaryVolDescs == 1)
6903 rc = rtFsIsoVolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
6904 else if (cPrimaryVolDescs == 2)
6905 Log(("ISO9660: ignoring 2nd primary descriptor\n")); /* so we can read the w2k3 ifs kit */
6906 else
6907 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
6908 }
6909 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
6910 {
6911 cSupplementaryVolDescs++;
6912 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
6913 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6914 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
6915#ifdef LOG_ENABLED
6916 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
6917#endif
6918 rc = rtFsIsoVolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
6919 &offJolietRootDirRec, pErrInfo);
6920 }
6921 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
6922 {
6923 cBootRecordVolDescs++;
6924 }
6925 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
6926 {
6927 if (!cPrimaryVolDescs)
6928 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
6929 enmState = kStateNoSeq;
6930 }
6931 else
6932 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
6933 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
6934 }
6935 /*
6936 * UDF volume recognition sequence (VRS).
6937 */
6938 else if ( ( enmState == kStateNoSeq
6939 || enmState == kStateStart)
6940 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BEGIN, UDF_EXT_VOL_DESC_VERSION) )
6941 {
6942 if (uUdfLevel == 0)
6943 enmState = kStateUdfSeq;
6944 else
6945 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BEA01 sequence is supported");
6946 }
6947 else if ( enmState == kStateUdfSeq
6948 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_02, UDF_EXT_VOL_DESC_VERSION) )
6949 uUdfLevel = 2;
6950 else if ( enmState == kStateUdfSeq
6951 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_03, UDF_EXT_VOL_DESC_VERSION) )
6952 uUdfLevel = 3;
6953 else if ( enmState == kStateUdfSeq
6954 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BOOT, UDF_EXT_VOL_DESC_VERSION) )
6955 {
6956 if (offUdfBootVolDesc == UINT64_MAX)
6957 offUdfBootVolDesc = iVolDesc * cbSector;
6958 else
6959 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BOOT2 descriptor is supported");
6960 }
6961 else if ( enmState == kStateUdfSeq
6962 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_TERM, UDF_EXT_VOL_DESC_VERSION) )
6963 {
6964 if (uUdfLevel != 0)
6965 enmState = kStateNoSeq;
6966 else
6967 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Found BEA01 & TEA01, but no NSR02 or NSR03 descriptors");
6968 }
6969 /*
6970 * Unknown, probably the end.
6971 */
6972 else if (enmState == kStateNoSeq)
6973 break;
6974 else if (enmState == kStateStart)
6975 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
6976 "Not ISO? Unable to recognize volume descriptor signature: %.5Rhxs", Buf.VolDescHdr.achStdId);
6977 else if (enmState == kStateCdSeq)
6978 {
6979#if 1
6980 /* The warp server for ebusiness update ISOs known as ACP2 & MCP2 ends up here,
6981 as they do in deed miss a terminator volume descriptor and we're now at the
6982 root directory already. Just detect this, ignore it and get on with things. */
6983 Log(("rtFsIsoVolTryInit: Ignoring missing ISO 9660 terminator volume descriptor (found %.5Rhxs).\n",
6984 Buf.VolDescHdr.achStdId));
6985 break;
6986#else
6987 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
6988 "Missing ISO 9660 terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
6989#endif
6990 }
6991 else if (enmState == kStateUdfSeq)
6992 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
6993 "Missing UDF terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
6994 else
6995 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
6996 "Unknown volume descriptor signature found at sector %u: %.5Rhxs",
6997 16 + iVolDesc, Buf.VolDescHdr.achStdId);
6998 if (RT_FAILURE(rc))
6999 return rc;
7000 }
7001
7002 /*
7003 * If we found a UDF VRS and are interested in UDF, we have more work to do here.
7004 */
7005 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF))
7006 {
7007 Log(("rtFsIsoVolTryInit: uUdfLevel=%d\n", uUdfLevel));
7008 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
7009 if (RT_FAILURE(rc))
7010 return rc;
7011 }
7012
7013 /*
7014 * Decide which to prefer.
7015 *
7016 * By default we pick UDF over any of the two ISO 9960, there is currently
7017 * no way to override this without using the RTFSISO9660_F_NO_XXX options.
7018 *
7019 * If there isn't UDF, we may be faced with choosing between joliet and
7020 * rock ridge. The joliet option is generally favorable as we don't have
7021 * to guess wrt to the file name encoding. So, we'll pick that for now.
7022 *
7023 * Note! Should we change this preference for joliet, there fun wrt making sure
7024 * there really is rock ridge stuff in the primary volume as well as
7025 * making sure there really is anything of value in the primary volume.
7026 */
7027 if (uUdfLevel > 0)
7028 {
7029 pThis->enmType = RTFSISOVOLTYPE_UDF;
7030 rc = rtFsIsoDirShrd_NewUdf(pThis, NULL /*pParent*/, &pThis->Udf.VolInfo.RootDirIcb,
7031 NULL /*pFileIdDesc*/, 0 /*offInDir*/, &pThis->pRootDir);
7032 /** @todo fall back on failure? */
7033 return rc;
7034 }
7035 if (bJolietUcs2Level != 0)
7036 {
7037 pThis->enmType = RTFSISOVOLTYPE_JOLIET;
7038 pThis->fIsUtf16 = true;
7039 return rtFsIsoDirShrd_New9660(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, NULL, &pThis->pRootDir);
7040 }
7041 pThis->enmType = RTFSISOVOLTYPE_ISO9960;
7042 return rtFsIsoDirShrd_New9660(pThis, NULL, &RootDir, 1, offRootDirRec, NULL, &pThis->pRootDir);
7043}
7044
7045
7046/**
7047 * Opens an ISO 9660 file system volume.
7048 *
7049 * @returns IPRT status code.
7050 * @param hVfsFileIn The file or device backing the volume.
7051 * @param fFlags RTFSISO9660_F_XXX.
7052 * @param phVfs Where to return the virtual file system handle.
7053 * @param pErrInfo Where to return additional error information.
7054 */
7055RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
7056{
7057 /*
7058 * Quick input validation.
7059 */
7060 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
7061 *phVfs = NIL_RTVFS;
7062 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
7063
7064 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
7065 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
7066
7067 /*
7068 * Create a new ISO VFS instance and try initialize it using the given input file.
7069 */
7070 RTVFS hVfs = NIL_RTVFS;
7071 PRTFSISOVOL pThis = NULL;
7072 int rc = RTVfsNew(&g_rtFsIsoVolOps, sizeof(RTFSISOVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
7073 if (RT_SUCCESS(rc))
7074 {
7075 rc = rtFsIsoVolTryInit(pThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
7076 if (RT_SUCCESS(rc))
7077 *phVfs = hVfs;
7078 else
7079 RTVfsRelease(hVfs);
7080 }
7081 else
7082 RTVfsFileRelease(hVfsFileIn);
7083 return rc;
7084}
7085
7086
7087/**
7088 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
7089 */
7090static DECLCALLBACK(int) rtVfsChainIsoFsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
7091 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
7092{
7093 RT_NOREF(pProviderReg, pSpec);
7094
7095 /*
7096 * Basic checks.
7097 */
7098 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
7099 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
7100 if ( pElement->enmType != RTVFSOBJTYPE_VFS
7101 && pElement->enmType != RTVFSOBJTYPE_DIR)
7102 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
7103 if (pElement->cArgs > 1)
7104 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
7105
7106 /*
7107 * Parse the flag if present, save in pElement->uProvider.
7108 */
7109 uint32_t fFlags = 0;
7110 if (pElement->cArgs > 0)
7111 {
7112 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
7113 {
7114 const char *psz = pElement->paArgs[iArg].psz;
7115 if (*psz)
7116 {
7117 if (!strcmp(psz, "nojoliet"))
7118 fFlags |= RTFSISO9660_F_NO_JOLIET;
7119 else if (!strcmp(psz, "norock"))
7120 fFlags |= RTFSISO9660_F_NO_ROCK;
7121 else if (!strcmp(psz, "noudf"))
7122 fFlags |= RTFSISO9660_F_NO_UDF;
7123 else
7124 {
7125 *poffError = pElement->paArgs[iArg].offSpec;
7126 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
7127 }
7128 }
7129 }
7130 }
7131
7132 pElement->uProvider = fFlags;
7133 return VINF_SUCCESS;
7134}
7135
7136
7137/**
7138 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
7139 */
7140static DECLCALLBACK(int) rtVfsChainIsoFsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
7141 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
7142 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
7143{
7144 RT_NOREF(pProviderReg, pSpec, poffError);
7145
7146 int rc;
7147 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
7148 if (hVfsFileIn != NIL_RTVFSFILE)
7149 {
7150 RTVFS hVfs;
7151 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
7152 RTVfsFileRelease(hVfsFileIn);
7153 if (RT_SUCCESS(rc))
7154 {
7155 *phVfsObj = RTVfsObjFromVfs(hVfs);
7156 RTVfsRelease(hVfs);
7157 if (*phVfsObj != NIL_RTVFSOBJ)
7158 return VINF_SUCCESS;
7159 rc = VERR_VFS_CHAIN_CAST_FAILED;
7160 }
7161 }
7162 else
7163 rc = VERR_VFS_CHAIN_CAST_FAILED;
7164 return rc;
7165}
7166
7167
7168/**
7169 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
7170 */
7171static DECLCALLBACK(bool) rtVfsChainIsoFsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
7172 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
7173 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
7174{
7175 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
7176 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
7177 || !pReuseElement->paArgs[0].uProvider)
7178 return true;
7179 return false;
7180}
7181
7182
7183/** VFS chain element 'file'. */
7184static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
7185{
7186 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
7187 /* fReserved = */ 0,
7188 /* pszName = */ "isofs",
7189 /* ListEntry = */ { NULL, NULL },
7190 /* pszHelp = */ "Open a ISO 9660 or UDF file system, requires a file object on the left side.\n"
7191 "The 'noudf' option make it ignore any UDF.\n"
7192 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
7193 "The 'norock' option make it ignore any rock ridge info.\n",
7194 /* pfnValidate = */ rtVfsChainIsoFsVol_Validate,
7195 /* pfnInstantiate = */ rtVfsChainIsoFsVol_Instantiate,
7196 /* pfnCanReuseElement = */ rtVfsChainIsoFsVol_CanReuseElement,
7197 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
7198};
7199
7200RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
7201
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