VirtualBox

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

Last change on this file since 69059 was 69059, checked in by vboxsync, 8 years ago

UDF: Some more structures.

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

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