VirtualBox

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

Last change on this file since 69828 was 69828, checked in by vboxsync, 7 years ago

IPRT/VFS: Reimplemented RTVfsDirOpen and RTVfsDirOpenDir to use pfnOpen, making pfnOpenDir optional. Fixed a couple of problems related to '.' and '..' handling in pfnQueryEntryInfo and pfnOpen implementations.

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