VirtualBox

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

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

Runtime: scm cleanups

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