VirtualBox

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

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

IPRT: VFS IsRangeInUse cleanup.

  • Renamed to RTVfsQueryRangeState / pfnQueryRangeState because it isn't a predicate returning a bool, but an IPRT status.
  • Changed the pfnIsRangeInUse offset parameter to uint64_t from RTFOFF since we don't want to deal with negative offset and the RTVfsIsRangeInUse API takes an unsigned value.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 238.9 KB
Line 
1/* $Id: isovfs.cpp 69844 2017-11-27 15:44:03Z 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,pfnCreateDir}
3072 */
3073static DECLCALLBACK(int) rtFsIsoDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3074{
3075 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3076 return VERR_WRITE_PROTECT;
3077}
3078
3079
3080/**
3081 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3082 */
3083static DECLCALLBACK(int) rtFsIsoDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3084{
3085 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3086 return VERR_NOT_SUPPORTED;
3087}
3088
3089
3090/**
3091 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3092 */
3093static DECLCALLBACK(int) rtFsIsoDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3094 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3095{
3096 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3097 return VERR_WRITE_PROTECT;
3098}
3099
3100
3101/**
3102 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3103 */
3104static DECLCALLBACK(int) rtFsIsoDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3105{
3106 RT_NOREF(pvThis, pszEntry, fType);
3107 return VERR_WRITE_PROTECT;
3108}
3109
3110
3111/**
3112 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3113 */
3114static DECLCALLBACK(int) rtFsIsoDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3115{
3116 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3117 return VERR_WRITE_PROTECT;
3118}
3119
3120
3121/**
3122 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3123 */
3124static DECLCALLBACK(int) rtFsIsoDir_RewindDir(void *pvThis)
3125{
3126 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3127 pThis->offDir = 0;
3128 return VINF_SUCCESS;
3129}
3130
3131
3132/**
3133 * The ISO 9660 worker for rtFsIsoDir_ReadDir
3134 */
3135static int rtFsIsoDir_ReadDir9660(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3136 RTFSOBJATTRADD enmAddAttr)
3137{
3138 while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
3139 {
3140 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
3141
3142 /* If null length, skip to the next sector. */
3143 if (pDirRec->cbDirRec == 0)
3144 pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
3145 else
3146 {
3147 /*
3148 * Do names first as they may cause overflows.
3149 */
3150 uint32_t uVersion = 0;
3151 if ( pDirRec->bFileIdLength == 1
3152 && pDirRec->achFileId[0] == '\0')
3153 {
3154 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
3155 {
3156 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
3157 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot)\n"));
3158 return VERR_BUFFER_OVERFLOW;
3159 }
3160 pDirEntry->cbName = 1;
3161 pDirEntry->szName[0] = '.';
3162 pDirEntry->szName[1] = '\0';
3163 }
3164 else if ( pDirRec->bFileIdLength == 1
3165 && pDirRec->achFileId[0] == '\1')
3166 {
3167 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
3168 {
3169 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
3170 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
3171 return VERR_BUFFER_OVERFLOW;
3172 }
3173 pDirEntry->cbName = 2;
3174 pDirEntry->szName[0] = '.';
3175 pDirEntry->szName[1] = '.';
3176 pDirEntry->szName[2] = '\0';
3177 }
3178 else if (pShared->Core.pVol->fIsUtf16)
3179 {
3180 PCRTUTF16 pawcSrc = (PCRTUTF16)&pDirRec->achFileId[0];
3181 size_t cwcSrc = pDirRec->bFileIdLength / sizeof(RTUTF16);
3182 size_t cwcVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3183 ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
3184 size_t cchNeeded = 0;
3185 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
3186 char *pszDst = pDirEntry->szName;
3187
3188 int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
3189 if (RT_SUCCESS(rc))
3190 pDirEntry->cbName = (uint16_t)cchNeeded;
3191 else if (rc == VERR_BUFFER_OVERFLOW)
3192 {
3193 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
3194 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
3195 return VERR_BUFFER_OVERFLOW;
3196 }
3197 else
3198 {
3199 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
3200 if (cchNeeded2 >= 0)
3201 pDirEntry->cbName = (uint16_t)cchNeeded2;
3202 else
3203 {
3204 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
3205 return VERR_BUFFER_OVERFLOW;
3206 }
3207 }
3208 }
3209 else
3210 {
3211 /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
3212 size_t cchVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3213 ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
3214 size_t cchName = pDirRec->bFileIdLength - cchVer;
3215 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
3216 if (*pcbDirEntry < cbNeeded)
3217 {
3218 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
3219 *pcbDirEntry = cbNeeded;
3220 return VERR_BUFFER_OVERFLOW;
3221 }
3222 pDirEntry->cbName = (uint16_t)cchName;
3223 memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
3224 pDirEntry->szName[cchName] = '\0';
3225 RTStrPurgeEncoding(pDirEntry->szName);
3226
3227 /** @todo check for rock ridge names here. */
3228 }
3229 pDirEntry->cwcShortName = 0;
3230 pDirEntry->wszShortName[0] = '\0';
3231
3232 /*
3233 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
3234 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3235 */
3236 RTFSISOCORE TmpObj;
3237 RT_ZERO(TmpObj);
3238 rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
3239 pThis->offDir + pShared->Core.FirstExtent.off, uVersion, pShared->Core.pVol);
3240 int rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
3241
3242 /*
3243 * Update the directory location and handle multi extent records.
3244 *
3245 * Multi extent records only affect the file size and the directory location,
3246 * so we deal with it here instead of involving * rtFsIsoCore_InitFrom9660DirRec
3247 * which would potentially require freeing memory and such.
3248 */
3249 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3250 {
3251 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3252 pThis->offDir += pDirRec->cbDirRec;
3253 }
3254 else
3255 {
3256 uint32_t cExtents = 1;
3257 uint32_t offDir = pThis->offDir + pDirRec->cbDirRec;
3258 while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
3259 {
3260 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
3261 if (pDirRec2->cbDirRec != 0)
3262 {
3263 pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
3264 offDir += pDirRec2->cbDirRec;
3265 cExtents++;
3266 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3267 break;
3268 }
3269 else
3270 offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
3271 }
3272 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
3273 pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
3274 pThis->offDir = offDir;
3275 }
3276
3277 return rc;
3278 }
3279 }
3280
3281 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
3282 return VERR_NO_MORE_FILES;
3283}
3284
3285
3286/**
3287 * The UDF worker for rtFsIsoDir_ReadDir
3288 */
3289static int rtFsIsoDir_ReadDirUdf(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3290 RTFSOBJATTRADD enmAddAttr)
3291{
3292 /*
3293 * At offset zero we've got the '.' entry. This has to be generated
3294 * manually as it's not part of the directory content. The directory
3295 * offset has to be faked for this too, so offDir == 0 indicates the '.'
3296 * entry whereas offDir == 1 is the first file id descriptor.
3297 */
3298 if (pThis->offDir == 0)
3299 {
3300 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
3301 {
3302 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
3303 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW (dot)\n"));
3304 return VERR_BUFFER_OVERFLOW;
3305 }
3306 pDirEntry->cbName = 1;
3307 pDirEntry->szName[0] = '.';
3308 pDirEntry->szName[1] = '\0';
3309 pDirEntry->cwcShortName = 0;
3310 pDirEntry->wszShortName[0] = '\0';
3311
3312 int rc = rtFsIsoCore_QueryInfo(&pShared->Core, &pDirEntry->Info, enmAddAttr);
3313
3314 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3315 pThis->offDir = 1;
3316 return rc;
3317 }
3318
3319 /*
3320 * Do the directory content.
3321 */
3322 while (pThis->offDir + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= pShared->cbDir + 1)
3323 {
3324 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pShared->pbDir[pThis->offDir - 1];
3325 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3326
3327 if (pThis->offDir + cbFid <= pShared->cbDir + 1)
3328 { /* likely */ }
3329 else
3330 break;
3331
3332 /*
3333 * Do names first as they may cause overflows.
3334 */
3335 if (pFid->cbName > 1)
3336 {
3337 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3338 uint32_t cbSrc = pFid->cbName;
3339 if (*pbName == 8)
3340 {
3341 /* Figure out the UTF-8 length first. */
3342 bool fSimple = true;
3343 uint32_t cchDst = 0;
3344 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
3345 if (!(pbName[offSrc] & 0x80))
3346 cchDst++;
3347 else
3348 {
3349 cchDst += 2;
3350 fSimple = false;
3351 }
3352
3353 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchDst + 1;
3354 if (*pcbDirEntry >= cbNeeded)
3355 {
3356 if (fSimple)
3357 {
3358 Assert(cbSrc - 1 == cchDst);
3359 memcpy(pDirEntry->szName, &pbName[1], cchDst);
3360 pDirEntry->szName[cchDst] = '\0';
3361 }
3362 else
3363 {
3364 char *pszDst = pDirEntry->szName;
3365 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
3366 pszDst = RTStrPutCp(pszDst, pbName[offSrc]);
3367 *pszDst = '\0';
3368 Assert((size_t)(pszDst - &pDirEntry->szName[0]) == cchDst);
3369 }
3370 }
3371 else
3372 {
3373 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (8-bit)\n", *pcbDirEntry, cbNeeded));
3374 *pcbDirEntry = cbNeeded;
3375 return VERR_BUFFER_OVERFLOW;
3376 }
3377 }
3378 else
3379 {
3380 /* Let RTUtf16BigToUtf8Ex do the bounds checking. */
3381 char *pszDst = pDirEntry->szName;
3382 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
3383 size_t cchNeeded = 0;
3384 int rc;
3385 if (*pbName == 16)
3386 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)(pbName + 1), (cbSrc - 1) / sizeof(RTUTF16), &pszDst, cbDst, &cchNeeded);
3387 else
3388 rc = VERR_INVALID_NAME;
3389 if (RT_SUCCESS(rc))
3390 pDirEntry->cbName = (uint16_t)cchNeeded;
3391 else if (rc == VERR_BUFFER_OVERFLOW)
3392 {
3393 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
3394 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (16-bit)\n", cbDst, cchNeeded));
3395 return VERR_BUFFER_OVERFLOW;
3396 }
3397 else
3398 {
3399 LogRelMax(90, ("ISO/UDF: Malformed directory entry name at %#x: %.*Rhxs\n", pThis->offDir - 1, cbSrc, pbName));
3400 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir - 1);
3401 if (cchNeeded2 >= 0)
3402 pDirEntry->cbName = (uint16_t)cchNeeded2;
3403 else
3404 {
3405 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
3406 return VERR_BUFFER_OVERFLOW;
3407 }
3408 }
3409 }
3410 }
3411 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
3412 {
3413 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2 + 1;
3414 if (*pcbDirEntry < cbNeeded)
3415 {
3416 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (dot-dot)\n", *pcbDirEntry, cbNeeded));
3417 *pcbDirEntry = cbNeeded;
3418 return VERR_BUFFER_OVERFLOW;
3419 }
3420 pDirEntry->cbName = 2;
3421 pDirEntry->szName[0] = '.';
3422 pDirEntry->szName[1] = '.';
3423 pDirEntry->szName[2] = '\0';
3424 }
3425 else
3426 {
3427 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 1;
3428 if (*pcbDirEntry < cbNeeded)
3429 {
3430 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (empty)\n", *pcbDirEntry, cbNeeded));
3431 *pcbDirEntry = cbNeeded;
3432 return VERR_BUFFER_OVERFLOW;
3433 }
3434 pDirEntry->cbName = 0;
3435 pDirEntry->szName[0] = '\0';
3436 }
3437
3438 pDirEntry->cwcShortName = 0;
3439 pDirEntry->wszShortName[0] = '\0';
3440
3441 /*
3442 * To avoid duplicating code in rtFsIsoCore_InitUdf and
3443 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3444 */
3445 RTFSISOCORE TmpObj;
3446 RT_ZERO(TmpObj);
3447 int rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&TmpObj, &pFid->Icb, pFid, pThis->offDir - 1, pShared->Core.pVol);
3448 if (RT_SUCCESS(rc))
3449 {
3450 rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
3451 rtFsIsoCore_Destroy(&TmpObj);
3452 }
3453
3454 /*
3455 * Update.
3456 */
3457 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3458 pThis->offDir += cbFid;
3459
3460 return rc;
3461 }
3462
3463 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
3464 return VERR_NO_MORE_FILES;
3465}
3466
3467
3468/**
3469 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3470 */
3471static DECLCALLBACK(int) rtFsIsoDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3472 RTFSOBJATTRADD enmAddAttr)
3473{
3474 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3475 PRTFSISODIRSHRD pShared = pThis->pShared;
3476 int rc;
3477 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3478 rc = rtFsIsoDir_ReadDir9660(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
3479 else
3480 rc = rtFsIsoDir_ReadDirUdf(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
3481 return rc;
3482}
3483
3484
3485/**
3486 * FAT file operations.
3487 */
3488static const RTVFSDIROPS g_rtFsIsoDirOps =
3489{
3490 { /* Obj */
3491 RTVFSOBJOPS_VERSION,
3492 RTVFSOBJTYPE_DIR,
3493 "ISO 9660 Dir",
3494 rtFsIsoDir_Close,
3495 rtFsIsoDir_QueryInfo,
3496 RTVFSOBJOPS_VERSION
3497 },
3498 RTVFSDIROPS_VERSION,
3499 0,
3500 { /* ObjSet */
3501 RTVFSOBJSETOPS_VERSION,
3502 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
3503 rtFsIsoDir_SetMode,
3504 rtFsIsoDir_SetTimes,
3505 rtFsIsoDir_SetOwner,
3506 RTVFSOBJSETOPS_VERSION
3507 },
3508 rtFsIsoDir_Open,
3509 NULL /* pfnFollowAbsoluteSymlink */,
3510 NULL /* pfnOpenFile */,
3511 NULL /* pfnOpenDir */,
3512 rtFsIsoDir_CreateDir,
3513 rtFsIsoDir_OpenSymlink,
3514 rtFsIsoDir_CreateSymlink,
3515 NULL /* pfnQueryEntryInfo */,
3516 rtFsIsoDir_UnlinkEntry,
3517 rtFsIsoDir_RenameEntry,
3518 rtFsIsoDir_RewindDir,
3519 rtFsIsoDir_ReadDir,
3520 RTVFSDIROPS_VERSION,
3521};
3522
3523
3524/**
3525 * Adds an open child to the parent directory's shared structure.
3526 *
3527 * Maintains an additional reference to the parent dir to prevent it from going
3528 * away. If @a pDir is the root directory, it also ensures the volume is
3529 * referenced and sticks around until the last open object is gone.
3530 *
3531 * @param pDir The directory.
3532 * @param pChild The child being opened.
3533 * @sa rtFsIsoDirShrd_RemoveOpenChild
3534 */
3535static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
3536{
3537 rtFsIsoDirShrd_Retain(pDir);
3538
3539 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
3540 pChild->pParentDir = pDir;
3541}
3542
3543
3544/**
3545 * Removes an open child to the parent directory.
3546 *
3547 * @param pDir The directory.
3548 * @param pChild The child being removed.
3549 *
3550 * @remarks This is the very last thing you do as it may cause a few other
3551 * objects to be released recursively (parent dir and the volume).
3552 *
3553 * @sa rtFsIsoDirShrd_AddOpenChild
3554 */
3555static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
3556{
3557 AssertReturnVoid(pChild->pParentDir == pDir);
3558 RTListNodeRemove(&pChild->Entry);
3559 pChild->pParentDir = NULL;
3560
3561 rtFsIsoDirShrd_Release(pDir);
3562}
3563
3564
3565#ifdef LOG_ENABLED
3566/**
3567 * Logs the content of a directory.
3568 */
3569static void rtFsIsoDirShrd_Log9660Content(PRTFSISODIRSHRD pThis)
3570{
3571 if (LogIs2Enabled())
3572 {
3573 uint32_t offRec = 0;
3574 while (offRec < pThis->cbDir)
3575 {
3576 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
3577 if (pDirRec->cbDirRec == 0)
3578 break;
3579
3580 RTUTF16 wszName[128];
3581 if (pThis->Core.pVol->fIsUtf16)
3582 {
3583 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
3584 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
3585 pwszSrc--;
3586 *pwszDst-- = '\0';
3587 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
3588 {
3589 *pwszDst = RT_BE2H_U16(*pwszSrc);
3590 pwszDst--;
3591 pwszSrc--;
3592 }
3593 }
3594 else
3595 {
3596 PRTUTF16 pwszDst = wszName;
3597 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
3598 *pwszDst++ = pDirRec->achFileId[off];
3599 *pwszDst = '\0';
3600 }
3601
3602 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",
3603 offRec,
3604 pDirRec->cbDirRec,
3605 pDirRec->cExtAttrBlocks,
3606 ISO9660_GET_ENDIAN(&pDirRec->cbData),
3607 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
3608 pDirRec->fFileFlags,
3609 pDirRec->RecTime.bYear + 1900,
3610 pDirRec->RecTime.bMonth,
3611 pDirRec->RecTime.bDay,
3612 pDirRec->RecTime.bHour,
3613 pDirRec->RecTime.bMinute,
3614 pDirRec->RecTime.bSecond,
3615 pDirRec->RecTime.offUtc*4/60,
3616 pDirRec->bFileUnitSize,
3617 pDirRec->bInterleaveGapSize,
3618 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
3619 wszName));
3620
3621 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
3622 if (offSysUse < pDirRec->cbDirRec)
3623 {
3624 Log2(("ISO9660: system use (%#x bytes):\n%.*RhxD\n", pDirRec->cbDirRec - offSysUse,
3625 pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
3626 }
3627
3628 /* advance */
3629 offRec += pDirRec->cbDirRec;
3630 }
3631 }
3632}
3633#endif /* LOG_ENABLED */
3634
3635
3636/**
3637 * Instantiates a new shared directory structure, given 9660 records.
3638 *
3639 * @returns IPRT status code.
3640 * @param pThis The FAT volume instance.
3641 * @param pParentDir The parent directory. This is NULL for the root
3642 * directory.
3643 * @param pDirRec The directory record. Will access @a cDirRecs
3644 * records.
3645 * @param cDirRecs Number of directory records if more than one.
3646 * @param offDirRec The byte offset of the directory record.
3647 * @param ppShared Where to return the shared directory structure.
3648 */
3649static int rtFsIsoDirShrd_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
3650 uint32_t cDirRecs, uint64_t offDirRec, PRTFSISODIRSHRD *ppShared)
3651{
3652 /*
3653 * Allocate a new structure and initialize it.
3654 */
3655 int rc = VERR_NO_MEMORY;
3656 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
3657 if (pShared)
3658 {
3659 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pThis);
3660 if (RT_SUCCESS(rc))
3661 {
3662 RTListInit(&pShared->OpenChildren);
3663 pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
3664 pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
3665 if (pShared->pbDir)
3666 {
3667 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.off, pShared->pbDir, pShared->cbDir, NULL);
3668 if (RT_SUCCESS(rc))
3669 {
3670#ifdef LOG_ENABLED
3671 rtFsIsoDirShrd_Log9660Content(pShared);
3672#endif
3673
3674 /*
3675 * Link into parent directory so we can use it to update
3676 * our directory entry.
3677 */
3678 if (pParentDir)
3679 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
3680 *ppShared = pShared;
3681 return VINF_SUCCESS;
3682 }
3683 }
3684 else
3685 rc = VERR_NO_MEMORY;
3686 }
3687 RTMemFree(pShared);
3688 }
3689 *ppShared = NULL;
3690 return rc;
3691}
3692
3693
3694#ifdef LOG_ENABLED
3695/**
3696 * Logs the content of a directory.
3697 */
3698static void rtFsIsoDirShrd_LogUdfContent(PRTFSISODIRSHRD pThis)
3699{
3700 if (LogIs2Enabled())
3701 {
3702 uint32_t offDesc = 0;
3703 while (offDesc + RT_OFFSETOF(UDFFILEIDDESC, abImplementationUse) < pThis->cbDir)
3704 {
3705 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
3706 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3707 if (offDesc + cbFid > pThis->cbDir)
3708 break;
3709
3710 uint32_t cwcName = 0;
3711 RTUTF16 wszName[260];
3712 if (pFid->cbName > 0)
3713 {
3714 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3715 uint32_t offSrc = 1;
3716 if (*pbName == 8)
3717 while (offSrc < pFid->cbName)
3718 {
3719 wszName[cwcName] = pbName[offSrc];
3720 cwcName++;
3721 offSrc++;
3722 }
3723 else if (*pbName == 16)
3724 while (offSrc + 1 <= pFid->cbName)
3725 {
3726 wszName[cwcName] = RT_MAKE_U16(pbName[offSrc + 1], pbName[offSrc]);
3727 cwcName++;
3728 offSrc += 2;
3729 }
3730 else
3731 {
3732 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<bad type>");
3733 cwcName = 10;
3734 }
3735 }
3736 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
3737 {
3738 wszName[0] = '.';
3739 wszName[1] = '.';
3740 cwcName = 2;
3741 }
3742 else
3743 {
3744 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<empty>");
3745 cwcName = 7;
3746 }
3747 wszName[cwcName] = '\0';
3748
3749 Log2(("ISO/UDF: %04x: fFlags=%#x uVer=%u Icb={%#04x:%#010RX32 LB %#06x t=%u} cbName=%#04x cbIU=%#x '%ls'\n",
3750 offDesc,
3751 pFid->fFlags,
3752 pFid->uVersion,
3753 pFid->Icb.Location.uPartitionNo,
3754 pFid->Icb.Location.off,
3755 pFid->Icb.cb,
3756 pFid->Icb.uType,
3757 pFid->cbName,
3758 pFid->cbImplementationUse,
3759 wszName));
3760 int rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFid->Tag, pThis->cbDir - offDesc,
3761 UDF_TAG_ID_FILE_ID_DESC, pFid->Tag.offTag, NULL);
3762 if (RT_FAILURE(rc))
3763 Log2(("ISO/UDF: Bad Tag: %Rrc - idTag=%#x\n", rc, pFid->Tag.idTag));
3764 if (pFid->cbImplementationUse > 32)
3765 Log2(("ISO/UDF: impl use (%#x bytes):\n%.*RhxD\n",
3766 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
3767 else if (pFid->cbImplementationUse > 0)
3768 Log2(("ISO/UDF: impl use (%#x bytes): %.*Rhxs\n",
3769 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
3770
3771 /* advance */
3772 offDesc += cbFid;
3773 }
3774
3775 if (offDesc < pThis->cbDir)
3776 Log2(("ISO/UDF: warning! %#x trailing bytes in directory:\n%.*RhxD\n",
3777 pThis->cbDir - offDesc, pThis->cbDir - offDesc, &pThis->pbDir[offDesc]));
3778 }
3779}
3780#endif /* LOG_ENABLED */
3781
3782
3783/**
3784 * Instantiates a new shared directory structure, given UDF descriptors.
3785 *
3786 * @returns IPRT status code.
3787 * @param pThis The FAT volume instance.
3788 * @param pParentDir The parent directory. This is NULL for the root
3789 * directory.
3790 * @param pAllocDesc The allocation descriptor for the directory ICB.
3791 * @param pFileIdDesc The file ID descriptor. This is NULL for the root.
3792 * @param offInDir The offset of the file ID descriptor in the parent
3793 * directory. This is used when looking up shared
3794 * directory objects. (Pass 0 for root.)
3795 * @param ppShared Where to return the shared directory structure.
3796 */
3797static int rtFsIsoDirShrd_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFLONGAD pAllocDesc,
3798 PCUDFFILEIDDESC pFileIdDesc, uintptr_t offInDir, PRTFSISODIRSHRD *ppShared)
3799{
3800 /*
3801 * Allocate a new structure and initialize it.
3802 */
3803 int rc = VERR_NO_MEMORY;
3804 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
3805 if (pShared)
3806 {
3807 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, pAllocDesc, pFileIdDesc, offInDir, pThis);
3808 if (RT_SUCCESS(rc))
3809 {
3810 RTListInit(&pShared->OpenChildren);
3811
3812 if (pShared->Core.cbObject < RTFSISO_MAX_DIR_SIZE)
3813 {
3814 pShared->cbDir = (uint32_t)pShared->Core.cbObject;
3815 pShared->pbDir = (uint8_t *)RTMemAllocZ(RT_MAX(RT_ALIGN_32(pShared->cbDir, 512), 512));
3816 if (pShared->pbDir)
3817 {
3818 rc = rtFsIsoCore_ReadWorker(&pShared->Core, 0, pShared->pbDir, pShared->cbDir, NULL, NULL);
3819 if (RT_SUCCESS(rc))
3820 {
3821#ifdef LOG_ENABLED
3822 rtFsIsoDirShrd_LogUdfContent(pShared);
3823#endif
3824
3825 /*
3826 * Link into parent directory so we can use it to update
3827 * our directory entry.
3828 */
3829 if (pParentDir)
3830 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
3831 *ppShared = pShared;
3832 return VINF_SUCCESS;
3833 }
3834 }
3835 else
3836 rc = VERR_NO_MEMORY;
3837 }
3838 }
3839 RTMemFree(pShared);
3840 }
3841
3842 *ppShared = NULL;
3843 return rc;
3844}
3845
3846
3847/**
3848 * Instantiates a new directory with a shared structure presupplied.
3849 *
3850 * @returns IPRT status code.
3851 * @param pThis The FAT volume instance.
3852 * @param pShared Referenced pointer to the shared structure. The
3853 * reference is always CONSUMED.
3854 * @param phVfsDir Where to return the directory handle.
3855 */
3856static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir)
3857{
3858 /*
3859 * Create VFS object around the shared structure.
3860 */
3861 PRTFSISODIROBJ pNewDir;
3862 int rc = RTVfsNewDir(&g_rtFsIsoDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
3863 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
3864 if (RT_SUCCESS(rc))
3865 {
3866 /*
3867 * Look for existing shared object, create a new one if necessary.
3868 * We CONSUME a reference to pShared here.
3869 */
3870 pNewDir->offDir = 0;
3871 pNewDir->pShared = pShared;
3872 return VINF_SUCCESS;
3873 }
3874
3875 rtFsIsoDirShrd_Release(pShared);
3876 *phVfsDir = NIL_RTVFSDIR;
3877 return rc;
3878}
3879
3880
3881
3882/**
3883 * Instantiates a new directory VFS instance for ISO 9660, creating the shared
3884 * structure as necessary.
3885 *
3886 * @returns IPRT status code.
3887 * @param pThis The FAT volume instance.
3888 * @param pParentDir The parent directory. This is NULL for the root
3889 * directory.
3890 * @param pDirRec The directory record.
3891 * @param cDirRecs Number of directory records if more than one.
3892 * @param offDirRec The byte offset of the directory record.
3893 * @param phVfsDir Where to return the directory handle.
3894 */
3895static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
3896 uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir)
3897{
3898 /*
3899 * Look for existing shared object, create a new one if necessary.
3900 */
3901 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
3902 if (!pShared)
3903 {
3904 int rc = rtFsIsoDirShrd_New9660(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, &pShared);
3905 if (RT_FAILURE(rc))
3906 {
3907 *phVfsDir = NIL_RTVFSDIR;
3908 return rc;
3909 }
3910 }
3911 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
3912}
3913
3914
3915/**
3916 * Instantiates a new directory VFS instance for UDF, creating the shared
3917 * structure as necessary.
3918 *
3919 * @returns IPRT status code.
3920 * @param pThis The FAT volume instance.
3921 * @param pParentDir The parent directory.
3922 * @param pFid The file ID descriptor for the directory.
3923 * @param phVfsDir Where to return the directory handle.
3924 */
3925static int rtFsIsoDir_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid, PRTVFSDIR phVfsDir)
3926{
3927 Assert(pFid);
3928 Assert(pParentDir);
3929 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
3930 Assert(offInDir < pParentDir->cbDir);
3931
3932 /*
3933 * Look for existing shared object, create a new one if necessary.
3934 */
3935 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
3936 if (!pShared)
3937 {
3938 int rc = rtFsIsoDirShrd_NewUdf(pThis, pParentDir, &pFid->Icb, pFid, offInDir, &pShared);
3939 if (RT_FAILURE(rc))
3940 {
3941 *phVfsDir = NIL_RTVFSDIR;
3942 return rc;
3943 }
3944 }
3945 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
3946}
3947
3948
3949/**
3950 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
3951 */
3952static DECLCALLBACK(int) rtFsIsoVol_Close(void *pvThis)
3953{
3954 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
3955 Log(("rtFsIsoVol_Close(%p)\n", pThis));
3956
3957 if (pThis->pRootDir)
3958 {
3959 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
3960 Assert(pThis->pRootDir->Core.cRefs == 1);
3961 rtFsIsoDirShrd_Release(pThis->pRootDir);
3962 pThis->pRootDir = NULL;
3963 }
3964
3965 RTVfsFileRelease(pThis->hVfsBacking);
3966 pThis->hVfsBacking = NIL_RTVFSFILE;
3967
3968 return VINF_SUCCESS;
3969}
3970
3971
3972/**
3973 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
3974 */
3975static DECLCALLBACK(int) rtFsIsoVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3976{
3977 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
3978 return VERR_WRONG_TYPE;
3979}
3980
3981
3982/**
3983 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
3984 */
3985static DECLCALLBACK(int) rtFsIsoVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
3986{
3987 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
3988
3989 rtFsIsoDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
3990 return rtFsIsoDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
3991}
3992
3993
3994/**
3995 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
3996 */
3997static DECLCALLBACK(int) rtFsIsoVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
3998{
3999 RT_NOREF(pvThis, off, cb, pfUsed);
4000 return VERR_NOT_IMPLEMENTED;
4001}
4002
4003
4004DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIsoVolOps =
4005{
4006 { /* Obj */
4007 RTVFSOBJOPS_VERSION,
4008 RTVFSOBJTYPE_VFS,
4009 "ISO 9660/UDF",
4010 rtFsIsoVol_Close,
4011 rtFsIsoVol_QueryInfo,
4012 RTVFSOBJOPS_VERSION
4013 },
4014 RTVFSOPS_VERSION,
4015 0 /* fFeatures */,
4016 rtFsIsoVol_OpenRoot,
4017 rtFsIsoVol_QueryRangeState,
4018 RTVFSOPS_VERSION
4019};
4020
4021
4022/**
4023 * Checks the descriptor tag and CRC.
4024 *
4025 * @retval IPRT status code.
4026 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
4027 * @retval VERR_MISMATCH
4028 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
4029 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
4030 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
4031 *
4032 * @param pTag The tag to check.
4033 * @param idTag The expected descriptor tag ID, UINT16_MAX matches any
4034 * tag ID.
4035 * @param offTag The sector offset of the tag.
4036 * @param pErrInfo Where to return extended error info.
4037 */
4038static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
4039{
4040 /*
4041 * Checksum the tag first.
4042 */
4043 const uint8_t *pbTag = (const uint8_t *)pTag;
4044 uint8_t const bChecksum = pbTag[0]
4045 + pbTag[1]
4046 + pbTag[2]
4047 + pbTag[3]
4048 + pbTag[5] /* skipping byte 4 as that's the checksum. */
4049 + pbTag[6]
4050 + pbTag[7]
4051 + pbTag[8]
4052 + pbTag[9]
4053 + pbTag[10]
4054 + pbTag[11]
4055 + pbTag[12]
4056 + pbTag[13]
4057 + pbTag[14]
4058 + pbTag[15];
4059 if (pTag->uChecksum == bChecksum)
4060 {
4061 /*
4062 * Do the matching.
4063 */
4064 if ( pTag->uVersion == 3
4065 || pTag->uVersion == 2)
4066 {
4067 if ( pTag->idTag == idTag
4068 || idTag == UINT16_MAX)
4069 {
4070 if (pTag->offTag == offTag)
4071 {
4072 //Log3(("ISO/UDF: Valid descriptor %#06x at %#010RX32; cbDescriptorCrc=%#06RX32 uTagSerialNo=%#x\n",
4073 // pTag->idTag, offTag, pTag->cbDescriptorCrc, pTag->uTagSerialNo));
4074 return VINF_SUCCESS;
4075 }
4076
4077 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Sector mismatch: %#RX32 (%.*Rhxs)\n",
4078 idTag, offTag, pTag->offTag, sizeof(*pTag), pTag));
4079 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TAG_SECTOR_MISMATCH,
4080 "Descriptor tag sector number mismatch: %#x, expected %#x (%.*Rhxs)",
4081 pTag->offTag, offTag, sizeof(*pTag), pTag);
4082 }
4083 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Tag ID mismatch: %#x (%.*Rhxs)\n",
4084 idTag, offTag, pTag->idTag, sizeof(*pTag), pTag));
4085 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_MISMATCH, "Descriptor tag ID mismatch: %#x, expected %#x (%.*Rhxs)",
4086 pTag->idTag, idTag, sizeof(*pTag), pTag);
4087 }
4088 if (ASMMemIsZero(pTag, sizeof(*pTag)))
4089 {
4090 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): All zeros\n", idTag, offTag));
4091 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TAG_IS_ALL_ZEROS, "Descriptor is all zeros");
4092 }
4093
4094 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Unsupported version: %#x (%.*Rhxs)\n",
4095 idTag, offTag, pTag->uVersion, sizeof(*pTag), pTag));
4096 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_TAG_VERSION, "Unsupported descriptor tag version: %#x, expected 2 or 3 (%.*Rhxs)",
4097 pTag->uVersion, sizeof(*pTag), pTag);
4098 }
4099 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): checksum error: %#x, calc %#x (%.*Rhxs)\n",
4100 idTag, offTag, pTag->uChecksum, bChecksum, sizeof(*pTag), pTag));
4101 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_TAG_CHECKSUM,
4102 "Descriptor tag checksum error: %#x, calculated %#x (%.*Rhxs)",
4103 pTag->uChecksum, bChecksum, sizeof(*pTag), pTag);
4104}
4105
4106
4107/**
4108 * Checks the descriptor CRC.
4109 *
4110 * @retval VINF_SUCCESS
4111 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4112 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
4113 *
4114 * @param pTag The descriptor buffer to checksum.
4115 * @param cbDesc The size of the descriptor buffer.
4116 * @param pErrInfo Where to return extended error info.
4117 */
4118static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo)
4119{
4120 if (pTag->cbDescriptorCrc + sizeof(*pTag) <= cbDesc)
4121 {
4122 uint16_t uCrc = RTCrc16Ccitt(pTag + 1, pTag->cbDescriptorCrc);
4123 if (pTag->uDescriptorCrc == uCrc)
4124 return VINF_SUCCESS;
4125
4126 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Descriptor CRC mismatch: expected %#x, calculated %#x (cbDescriptorCrc=%#x)\n",
4127 pTag->idTag, pTag->offTag, pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc));
4128 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_DESC_CRC_MISMATCH,
4129 "Descriptor CRC mismatch: exepcted %#x, calculated %#x (cbDescriptor=%#x, idTag=%#x, offTag=%#010RX32)",
4130 pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc, pTag->idTag, pTag->offTag);
4131 }
4132
4133 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx\n",
4134 pTag->idTag, pTag->offTag, pTag->cbDescriptorCrc, cbDesc));
4135 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC,
4136 "Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx (idTag=%#x, offTag=%#010RX32)",
4137 pTag->cbDescriptorCrc, cbDesc, pTag->idTag, pTag->offTag);
4138}
4139
4140
4141/**
4142 * Checks the descriptor tag and CRC.
4143 *
4144 * @retval VINF_SUCCESS
4145 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4146 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
4147 * @retval VERR_MISMATCH
4148 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
4149 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
4150 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
4151 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
4152 *
4153 * @param pTag The descriptor buffer to check the tag of and to
4154 * checksum.
4155 * @param cbDesc The size of the descriptor buffer.
4156 * @param idTag The expected descriptor tag ID, UINT16_MAX
4157 * matches any tag ID.
4158 * @param offTag The sector offset of the tag.
4159 * @param pErrInfo Where to return extended error info.
4160 */
4161static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
4162{
4163 int rc = rtFsIsoVolValidateUdfDescTag(pTag, idTag, offTag, pErrInfo);
4164 if (RT_SUCCESS(rc))
4165 rc = rtFsIsoVolValidateUdfDescCrc(pTag, cbDesc, pErrInfo);
4166 return rc;
4167}
4168
4169
4170
4171
4172static int rtFsIsoVolProcessUdfFileSetDescs(PRTFSISOVOL pThis, uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
4173{
4174
4175 /*
4176 * We assume there is a single file descriptor and don't bother checking what comes next.
4177 */
4178 PUDFFILESETDESC pFsd = (PUDFFILESETDESC)pbBuf;
4179 Assert(cbBuf > sizeof(*pFsd)); NOREF(cbBuf);
4180 RT_ZERO(*pFsd);
4181 size_t cbToRead = RT_MAX(pThis->Udf.VolInfo.FileSetDescriptor.cb, sizeof(*pFsd));
4182 int rc = rtFsIsoVolUdfVpRead(pThis, pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
4183 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, 0, pFsd, cbToRead);
4184 if (RT_SUCCESS(rc))
4185 {
4186 rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFsd->Tag, cbToRead, UDF_TAG_ID_FILE_SET_DESC,
4187 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, pErrInfo);
4188 if (RT_SUCCESS(rc))
4189 {
4190#ifdef LOG_ENABLED
4191 Log(("ISO/UDF: File set descriptor at %#RX32 (%#RX32:%#RX32)\n", pFsd->Tag.offTag,
4192 pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
4193 pThis->Udf.VolInfo.FileSetDescriptor.Location.off));
4194 if (LogIs2Enabled())
4195 {
4196 UDF_LOG2_MEMBER_TIMESTAMP(pFsd, RecordingTimestamp);
4197 UDF_LOG2_MEMBER(pFsd, "#06RX16", uInterchangeLevel);
4198 UDF_LOG2_MEMBER(pFsd, "#06RX16", uMaxInterchangeLevel);
4199 UDF_LOG2_MEMBER(pFsd, "#010RX32", fCharacterSets);
4200 UDF_LOG2_MEMBER(pFsd, "#010RX32", fMaxCharacterSets);
4201 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetNo);
4202 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetDescNo);
4203 UDF_LOG2_MEMBER_CHARSPEC(pFsd, LogicalVolumeIDCharSet);
4204 UDF_LOG2_MEMBER_DSTRING(pFsd, achLogicalVolumeID);
4205 UDF_LOG2_MEMBER_CHARSPEC(pFsd, FileSetCharSet);
4206 UDF_LOG2_MEMBER_DSTRING(pFsd, achFileSetID);
4207 UDF_LOG2_MEMBER_DSTRING(pFsd, achCopyrightFile);
4208 UDF_LOG2_MEMBER_DSTRING(pFsd, achAbstractFile);
4209 UDF_LOG2_MEMBER_LONGAD(pFsd, RootDirIcb);
4210 UDF_LOG2_MEMBER_ENTITY_ID(pFsd, idDomain);
4211 UDF_LOG2_MEMBER_LONGAD(pFsd, NextExtent);
4212 UDF_LOG2_MEMBER_LONGAD(pFsd, SystemStreamDirIcb);
4213 if (!ASMMemIsZero(&pFsd->abReserved[0], sizeof(pFsd->abReserved)))
4214 UDF_LOG2_MEMBER(pFsd, ".32Rhxs", abReserved);
4215 }
4216#endif
4217
4218 /*
4219 * Do some basic sanity checking.
4220 */
4221 if (!UDF_IS_CHAR_SET_OSTA(&pFsd->FileSetCharSet))
4222 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_UNSUPPORTED_CHAR_SET,
4223 "Invalid file set charset %.64Rhxs", &pFsd->FileSetCharSet);
4224 if ( pFsd->RootDirIcb.cb == 0
4225 || pFsd->RootDirIcb.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4226 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_ZERO_ROOT_DIR,
4227 "Root Dir ICB location is zero or malformed: uType=%#x cb=%#x loc=%#x:%#RX32",
4228 pFsd->RootDirIcb.uType, pFsd->RootDirIcb.cb,
4229 pFsd->RootDirIcb.Location.uPartitionNo, pFsd->RootDirIcb.Location.off);
4230 if ( pFsd->NextExtent.cb != 0
4231 && pFsd->NextExtent.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4232 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_NEXT_EXTENT,
4233 "NextExtent isn't zero: uType=%#x cb=%#x loc=%#x:%#RX32",
4234 pFsd->NextExtent.uType, pFsd->NextExtent.cb,
4235 pFsd->NextExtent.Location.uPartitionNo, pFsd->NextExtent.Location.off);
4236
4237 /*
4238 * Copy the information we need.
4239 */
4240 pThis->Udf.VolInfo.RootDirIcb = pFsd->RootDirIcb;
4241 if ( pFsd->SystemStreamDirIcb.cb > 0
4242 && pFsd->SystemStreamDirIcb.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4243 pThis->Udf.VolInfo.SystemStreamDirIcb = pFsd->SystemStreamDirIcb;
4244 else
4245 RT_ZERO(pThis->Udf.VolInfo.SystemStreamDirIcb);
4246 return VINF_SUCCESS;
4247 }
4248 return rc;
4249 }
4250 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading file set descriptor");
4251}
4252
4253
4254/**
4255 * Check validatity and extract information from the descriptors in the VDS seq.
4256 *
4257 * @returns IPRT status code
4258 * @param pThis The instance.
4259 * @param pInfo The VDS sequence info.
4260 * @param pErrInfo Where to return extended error info.
4261 */
4262static int rtFsIsoVolProcessUdfVdsSeqInfo(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, PRTERRINFO pErrInfo)
4263{
4264 /*
4265 * Check the basic descriptor counts.
4266 */
4267 PUDFPRIMARYVOLUMEDESC pPvd;
4268 if (pInfo->cPrimaryVols == 1)
4269 pPvd = pInfo->apPrimaryVols[0];
4270 else
4271 {
4272 if (pInfo->cPrimaryVols == 0)
4273 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PVD, "No primary volume descriptor was found");
4274 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_PVDS,
4275 "More than one primary volume descriptor was found: %u", pInfo->cPrimaryVols);
4276 }
4277
4278 PUDFLOGICALVOLUMEDESC pLvd;
4279 if (pInfo->cLogicalVols == 1)
4280 pLvd = pInfo->apLogicalVols[0];
4281 else
4282 {
4283 if (pInfo->cLogicalVols == 0)
4284 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_LVD, "No logical volume descriptor was found");
4285 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_LVDS,
4286 "More than one logical volume descriptor was found: %u", pInfo->cLogicalVols);
4287 }
4288
4289#if 0
4290 if (pInfo->cPartitions == 0)
4291 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PD, "No partition descriptors was found");
4292#endif
4293
4294 /*
4295 * Check out the partition map in the logical volume descriptor.
4296 * Produce the mapping table while going about that.
4297 */
4298 if (pLvd->cPartitionMaps > 64)
4299 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_MANY_PART_MAPS,
4300 "Too many partition maps: %u (max 64)", pLvd->cPartitionMaps);
4301
4302 PRTFSISOVOLUDFPMAP paPartMaps = NULL;
4303 if (pLvd->cPartitionMaps > 0)
4304 {
4305 pInfo->paPartMaps = paPartMaps = (PRTFSISOVOLUDFPMAP)RTMemAllocZ(sizeof(paPartMaps[0]) * pLvd->cPartitionMaps);
4306 if (!paPartMaps)
4307 return VERR_NO_MEMORY;
4308 }
4309 uint32_t cPartMaps = 0;
4310
4311 if (pLvd->cbMapTable)
4312 {
4313 uint32_t off = 0;
4314 while (off + sizeof(UDFPARTMAPHDR) <= pLvd->cbMapTable)
4315 {
4316 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pLvd->abPartitionMaps[off];
4317
4318 /*
4319 * Bounds checking.
4320 */
4321 if (off + pHdr->cb > pLvd->cbMapTable)
4322 {
4323 if (cPartMaps < pLvd->cbMapTable)
4324 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MALFORMED_PART_MAP_TABLE,
4325 "Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)",
4326 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType);
4327 LogRel(("ISO/UDF: Warning: Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)\n",
4328 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType));
4329 break;
4330 }
4331 if (cPartMaps >= pLvd->cPartitionMaps)
4332 {
4333 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",
4334 cPartMaps - pLvd->cPartitionMaps, off, pHdr->cb, pLvd->cbMapTable, pHdr->bType));
4335 break;
4336 }
4337
4338 /*
4339 * Extract relevant info out of the entry.
4340 */
4341 paPartMaps[cPartMaps].offMapTable = (uint16_t)off;
4342 uint16_t uPartitionNo;
4343 if (pHdr->bType == 1)
4344 {
4345 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
4346 paPartMaps[cPartMaps].uVolumeSeqNo = pType1->uVolumeSeqNo;
4347 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_PLAIN;
4348 uPartitionNo = pType1->uPartitionNo;
4349 }
4350 else if (pHdr->bType == 2)
4351 {
4352 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
4353 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_VPM_PARTITION_TYPE))
4354 {
4355 paPartMaps[cPartMaps].bType = pType2->idPartitionType.Suffix.Udf.uUdfRevision >= 0x200
4356 ? RTFSISO_UDF_PMAP_T_VPM_20 : RTFSISO_UDF_PMAP_T_VPM_15;
4357 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_VPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4358 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4359 }
4360 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
4361 {
4362 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_SPM;
4363 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_SPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4364 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4365 }
4366 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
4367 {
4368 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_MPM;
4369 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4370 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4371 }
4372 else
4373 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_TYPE_ID,
4374 "Unknown partition map ID for #%u @ %#x: %.23s",
4375 cPartMaps, off, pType2->idPartitionType.achIdentifier);
4376#if 0 /* unreachable code */
4377 paPartMaps[cPartMaps].uVolumeSeqNo = pType2->uVolumeSeqNo;
4378 uPartitionNo = pType2->uPartitionNo;
4379#endif
4380 }
4381 else
4382 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_ENTRY_TYPE,
4383 "Unknown partition map entry type #%u @ %#x: %u", cPartMaps, off, pHdr->bType);
4384 paPartMaps[cPartMaps].uPartitionNo = uPartitionNo;
4385
4386 /*
4387 * Lookup the partition number and retrieve the relevant info from the partition descriptor.
4388 */
4389 uint32_t i = pInfo->cPartitions;
4390 while (i-- > 0)
4391 {
4392 PUDFPARTITIONDESC pPd = pInfo->apPartitions[i];
4393 if (paPartMaps[cPartMaps].uPartitionNo == pPd->uPartitionNo)
4394 {
4395 paPartMaps[cPartMaps].idxPartDesc = (uint16_t)i;
4396 paPartMaps[cPartMaps].cSectors = pPd->cSectors;
4397 paPartMaps[cPartMaps].offLocation = pPd->offLocation;
4398 paPartMaps[cPartMaps].offByteLocation = (uint64_t)pPd->offLocation * pThis->cbSector;
4399 paPartMaps[cPartMaps].fFlags = pPd->fFlags;
4400 paPartMaps[cPartMaps].uAccessType = pPd->uAccessType;
4401 if (!UDF_ENTITY_ID_EQUALS(&pPd->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
4402 paPartMaps[cPartMaps].fHaveHdr = false;
4403 else
4404 {
4405 paPartMaps[cPartMaps].fHaveHdr = true;
4406 paPartMaps[cPartMaps].Hdr = pPd->ContentsUse.Hdr;
4407 }
4408 break;
4409 }
4410 }
4411 if (i > pInfo->cPartitions)
4412 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_PARTITION_NOT_FOUND,
4413 "Partition #%u (%#x) specified by mapping entry #%u (@ %#x) was not found! (int-type %u)",
4414 uPartitionNo, uPartitionNo, cPartMaps, off, paPartMaps[cPartMaps].bType);
4415
4416 /*
4417 * Advance.
4418 */
4419 cPartMaps++;
4420 off += pHdr->cb;
4421 }
4422
4423 if (cPartMaps < pLvd->cPartitionMaps)
4424 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INCOMPLETE_PART_MAP_TABLE,
4425 "Only found %u of the %u announced partition mapping table entries",
4426 cPartMaps, pLvd->cPartitionMaps);
4427 }
4428
4429 /* It might be theoretically possible to not use virtual partitions for
4430 accessing data, so just warn if there aren't any. */
4431 if (cPartMaps == 0)
4432 LogRel(("ISO/UDF: Warning: No partition maps!\n"));
4433
4434 /*
4435 * Check out the logical volume descriptor.
4436 */
4437 if ( pLvd->cbLogicalBlock < pThis->cbSector
4438 || pLvd->cbLogicalBlock > RTFSISO_MAX_LOGICAL_BLOCK_SIZE
4439 || (pLvd->cbLogicalBlock % pThis->cbSector) != 0)
4440 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_LOGICAL_BLOCK_SIZE,
4441 "Logical block size of %#x is not supported with a sector size of %#x",
4442 pLvd->cbLogicalBlock, pThis->cbSector);
4443
4444 if (!UDF_ENTITY_ID_EQUALS(&pLvd->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
4445 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DOMAIN_ID,
4446 "Unsupported domain ID in logical volume descriptor: '%.23s'", pLvd->idDomain.achIdentifier);
4447
4448 if ( pLvd->ContentsUse.FileSetDescriptor.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED
4449 || pLvd->ContentsUse.FileSetDescriptor.cb == 0
4450 || pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo >= cPartMaps)
4451 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_FILE_SET_DESC_LOCATION,
4452 "Malformed file set descriptor location (type=%u cb=%#x part=%#x)",
4453 pLvd->ContentsUse.FileSetDescriptor.uType,
4454 pLvd->ContentsUse.FileSetDescriptor.cb,
4455 pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo);
4456
4457 bool fLvdHaveVolId = !ASMMemIsZero(pLvd->achLogicalVolumeID, sizeof(pLvd->achLogicalVolumeID));
4458 if ( fLvdHaveVolId
4459 && !UDF_IS_CHAR_SET_OSTA(&pLvd->DescCharSet))
4460 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DESC_CHAR_SET,
4461 "Logical volume ID is not using OSTA compressed unicode");
4462
4463 /*
4464 * We can ignore much, if not all of the primary volume descriptor.
4465 */
4466
4467 /*
4468 * We're good. So copy over the data.
4469 */
4470 pThis->Udf.VolInfo.FileSetDescriptor = pLvd->ContentsUse.FileSetDescriptor;
4471 pThis->Udf.VolInfo.cbBlock = pLvd->cbLogicalBlock;
4472 pThis->Udf.VolInfo.cShiftBlock = 9;
4473 while (pThis->Udf.VolInfo.cbBlock != RT_BIT_32(pThis->Udf.VolInfo.cShiftBlock))
4474 pThis->Udf.VolInfo.cShiftBlock++;
4475 pThis->Udf.VolInfo.fFlags = pPvd->fFlags;
4476 pThis->Udf.VolInfo.cPartitions = cPartMaps;
4477 pThis->Udf.VolInfo.paPartitions = paPartMaps;
4478 pInfo->paPartMaps = NULL;
4479 if (fLvdHaveVolId)
4480 memcpy(pThis->Udf.VolInfo.achLogicalVolumeID, pLvd->achLogicalVolumeID, sizeof(pThis->Udf.VolInfo.achLogicalVolumeID));
4481 else
4482 RT_ZERO(pThis->Udf.VolInfo.achLogicalVolumeID);
4483
4484 return VINF_SUCCESS;
4485}
4486
4487
4488/**
4489 * Processes a primary volume descriptor in the VDS (UDF).
4490 *
4491 * @returns IPRT status code.
4492 * @param pInfo Where we gather descriptor information.
4493 * @param pDesc The descriptor.
4494 * @param pErrInfo Where to return extended error information.
4495 */
4496//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
4497static int rtFsIsoVolProcessUdfPrimaryVolDesc(PRTFSISOVDSINFO pInfo, PCUDFPRIMARYVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
4498{
4499#ifdef LOG_ENABLED
4500 Log(("ISO/UDF: Primary volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4501 if (LogIs2Enabled())
4502 {
4503 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4504 UDF_LOG2_MEMBER(pDesc, "#010RX32", uPrimaryVolumeDescNo);
4505 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeID);
4506 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
4507 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
4508 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxVolumeSeqNo);
4509 UDF_LOG2_MEMBER(pDesc, "#06RX16", uInterchangeLevel);
4510 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxInterchangeLevel);
4511 UDF_LOG2_MEMBER(pDesc, "#010RX32", fCharacterSets);
4512 UDF_LOG2_MEMBER(pDesc, "#010RX32", fMaxCharacterSets);
4513 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeSetID);
4514 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
4515 UDF_LOG2_MEMBER_CHARSPEC(pDesc, ExplanatoryCharSet);
4516 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeAbstract);
4517 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeCopyrightNotice);
4518 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idApplication);
4519 UDF_LOG2_MEMBER_TIMESTAMP(pDesc, RecordingTimestamp);
4520 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4521 if (!ASMMemIsZero(&pDesc->abImplementationUse, sizeof(pDesc->abImplementationUse)))
4522 Log2(("ISO/UDF: %-32s %.64Rhxs\n", "abReserved[64]:", &pDesc->abImplementationUse[0]));
4523 UDF_LOG2_MEMBER(pDesc, "#010RX32", offPredecessorVolDescSeq);
4524 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
4525 if (!ASMMemIsZero(&pDesc->abReserved, sizeof(pDesc->abReserved)))
4526 Log2(("ISO/UDF: %-32s %.22Rhxs\n", "abReserved[22]:", &pDesc->abReserved[0]));
4527 }
4528#endif
4529
4530 /*
4531 * Check if this is a new revision of an existing primary volume descriptor.
4532 */
4533 PUDFPRIMARYVOLUMEDESC pEndianConvert = NULL;
4534 uint32_t i = pInfo->cPrimaryVols;
4535 while (i--> 0)
4536 {
4537 if ( memcmp(pDesc->achVolumeID, pInfo->apPrimaryVols[i]->achVolumeID, sizeof(pDesc->achVolumeID)) == 0
4538 && memcmp(&pDesc->DescCharSet, &pInfo->apPrimaryVols[i]->DescCharSet, sizeof(pDesc->DescCharSet)) == 0)
4539 {
4540 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPrimaryVols[i]->uVolumeDescSeqNo)
4541 {
4542 Log(("ISO/UDF: Primary descriptor prevails over previous! (%u >= %u)\n",
4543 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4544 pEndianConvert = pInfo->apPrimaryVols[i];
4545 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
4546 }
4547 else
4548 Log(("ISO/UDF: Primary descriptor has lower sequence number than the previous! (%u < %u)\n",
4549 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4550 break;
4551 }
4552 }
4553 if (i >= pInfo->cPrimaryVols)
4554 {
4555 /*
4556 * It wasn't. Append it.
4557 */
4558 i = pInfo->cPrimaryVols;
4559 if (i < RT_ELEMENTS(pInfo->apPrimaryVols))
4560 {
4561 pInfo->apPrimaryVols[i] = pEndianConvert = (PUDFPRIMARYVOLUMEDESC)RTMemDup(pDesc, sizeof(*pDesc));
4562 if (pEndianConvert)
4563 pInfo->cPrimaryVols = i + 1;
4564 else
4565 return VERR_NO_MEMORY;
4566 Log2(("ISO/UDF: ++New primary descriptor.\n"));
4567 }
4568 else
4569 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PVDS, "Encountered too many primary volume descriptors");
4570 }
4571
4572#ifdef RT_BIG_ENDIAN
4573 /*
4574 * Do endian conversion of the descriptor.
4575 */
4576 if (pEndianConvert)
4577 {
4578 AssertFailed();
4579 }
4580#else
4581 RT_NOREF(pEndianConvert);
4582#endif
4583 return VINF_SUCCESS;
4584}
4585
4586
4587/**
4588 * Processes an logical volume descriptor in the VDS (UDF).
4589 *
4590 * @returns IPRT status code.
4591 * @param pInfo Where we gather descriptor information.
4592 * @param pDesc The descriptor.
4593 * @param cbSector The sector size (UDF defines the logical and physical
4594 * sector size to be the same).
4595 * @param pErrInfo Where to return extended error information.
4596 */
4597static int rtFsIsoVolProcessUdfLogicalVolumeDesc(PRTFSISOVDSINFO pInfo, PCUDFLOGICALVOLUMEDESC pDesc,
4598 uint32_t cbSector, PRTERRINFO pErrInfo)
4599{
4600#ifdef LOG_ENABLED
4601 Log(("ISO/UDF: Logical volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4602 if (LogIs2Enabled())
4603 {
4604 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4605 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
4606 UDF_LOG2_MEMBER_DSTRING(pDesc, achLogicalVolumeID);
4607 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbLogicalBlock);
4608 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idDomain);
4609 if (UDF_ENTITY_ID_EQUALS(&pDesc->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
4610 UDF_LOG2_MEMBER_LONGAD(pDesc, ContentsUse.FileSetDescriptor);
4611 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
4612 Log2(("ISO/UDF: %-32s %.16Rhxs\n", "ContentsUse.ab[16]:", &pDesc->ContentsUse.ab[0]));
4613 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbMapTable);
4614 UDF_LOG2_MEMBER(pDesc, "#010RX32", cPartitionMaps);
4615 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4616 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4617 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
4618 UDF_LOG2_MEMBER_EXTENTAD(pDesc, IntegritySeqExtent);
4619 if (pDesc->cbMapTable)
4620 {
4621 Log2(("ISO/UDF: %-32s\n", "abPartitionMaps"));
4622 uint32_t iMap = 0;
4623 uint32_t off = 0;
4624 while (off + sizeof(UDFPARTMAPHDR) <= pDesc->cbMapTable)
4625 {
4626 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pDesc->abPartitionMaps[off];
4627 Log2(("ISO/UDF: %02u @ %#05x: type %u, length %u\n", iMap, off, pHdr->bType, pHdr->cb));
4628 if (off + pHdr->cb > pDesc->cbMapTable)
4629 {
4630 Log2(("ISO/UDF: BAD! Entry is %d bytes too long!\n", off + pHdr->cb - pDesc->cbMapTable));
4631 break;
4632 }
4633 if (pHdr->bType == 1)
4634 {
4635 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
4636 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uVolumeSeqNo, 5);
4637 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uPartitionNo, 5);
4638 }
4639 else if (pHdr->bType == 2)
4640 {
4641 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
4642 UDF_LOG2_MEMBER_ENTITY_ID_EX(pType2, idPartitionType, 5);
4643 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uVolumeSeqNo, 5);
4644 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uPartitionNo, 5);
4645 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
4646 {
4647 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Spm.cBlocksPerPacket, 5);
4648 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.cSparingTables, 5);
4649 if (pType2->u.Spm.bReserved2)
4650 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.bReserved2, 5);
4651 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.cbSparingTable, 5);
4652 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[0], 5);
4653 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[1], 5);
4654 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[2], 5);
4655 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[3], 5);
4656 }
4657 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
4658 {
4659 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataFile, 5);
4660 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataMirrorFile, 5);
4661 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataBitmapFile, 5);
4662 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.cBlocksAllocationUnit, 5);
4663 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Mpm.cBlocksAlignmentUnit, 5);
4664 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Mpm.fFlags, 5);
4665 if (!ASMMemIsZero(pType2->u.Mpm.abReserved2, sizeof(pType2->u.Mpm.abReserved2)))
4666 UDF_LOG2_MEMBER_EX(&pType2->u, ".5Rhxs", Mpm.abReserved2, 5);
4667 }
4668 }
4669 else
4670 Log2(("ISO/UDF: BAD! Unknown type!\n"));
4671
4672 /* advance */
4673 off += pHdr->cb;
4674 iMap++;
4675 }
4676 }
4677 }
4678#endif
4679
4680 /*
4681 * Check if this is a newer revision of an existing primary volume descriptor.
4682 */
4683 size_t cbDesc = (size_t)pDesc->cbMapTable + RT_UOFFSETOF(UDFLOGICALVOLUMEDESC, abPartitionMaps);
4684 if ( pDesc->cbMapTable >= (UINT32_MAX >> 1)
4685 || cbDesc > cbSector)
4686 {
4687 Log(("ISO/UDF: Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector));
4688 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD,
4689 "Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector);
4690 }
4691
4692 PUDFLOGICALVOLUMEDESC pEndianConvert = NULL;
4693 uint32_t i = pInfo->cLogicalVols;
4694 while (i--> 0)
4695 if ( memcmp(pDesc->achLogicalVolumeID, pInfo->apLogicalVols[i]->achLogicalVolumeID,
4696 sizeof(pDesc->achLogicalVolumeID)) == 0
4697 && memcmp(&pDesc->DescCharSet, &pInfo->apLogicalVols[i]->DescCharSet,
4698 sizeof(pDesc->DescCharSet)) == 0)
4699 {
4700 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apLogicalVols[i]->uVolumeDescSeqNo)
4701 {
4702 Log(("ISO/UDF: Logical descriptor prevails over previous! (%u >= %u)\n",
4703 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
4704 pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
4705 if (!pEndianConvert)
4706 return VERR_NO_MEMORY;
4707 RTMemFree(pInfo->apLogicalVols[i]);
4708 pInfo->apLogicalVols[i] = pEndianConvert;
4709 }
4710 else
4711 Log(("ISO/UDF: Logical descriptor has lower sequence number than the previous! (%u >= %u)\n",
4712 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
4713 break;
4714 }
4715 if (i >= pInfo->cLogicalVols)
4716 {
4717 /*
4718 * It wasn't. Append it.
4719 */
4720 i = pInfo->cLogicalVols;
4721 if (i < RT_ELEMENTS(pInfo->apLogicalVols))
4722 {
4723 pInfo->apLogicalVols[i] = pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
4724 if (pEndianConvert)
4725 pInfo->cLogicalVols = i + 1;
4726 else
4727 return VERR_NO_MEMORY;
4728 Log2(("ISO/UDF: ++New logical volume descriptor.\n"));
4729 }
4730 else
4731 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_LVDS, "Too many logical volume descriptors");
4732 }
4733
4734#ifdef RT_BIG_ENDIAN
4735 /*
4736 * Do endian conversion of the descriptor.
4737 */
4738 if (pEndianConvert)
4739 {
4740 AssertFailed();
4741 }
4742#else
4743 RT_NOREF(pEndianConvert);
4744#endif
4745 return VINF_SUCCESS;
4746}
4747
4748
4749/**
4750 * Processes an partition descriptor in the VDS (UDF).
4751 *
4752 * @returns IPRT status code.
4753 * @param pInfo Where we gather descriptor information.
4754 * @param pDesc The descriptor.
4755 * @param pErrInfo Where to return extended error information.
4756 */
4757static int rtFsIsoVolProcessUdfPartitionDesc(PRTFSISOVDSINFO pInfo, PCUDFPARTITIONDESC pDesc, PRTERRINFO pErrInfo)
4758{
4759#ifdef LOG_ENABLED
4760 Log(("ISO/UDF: Partition descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4761 if (LogIs2Enabled())
4762 {
4763 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4764 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
4765 UDF_LOG2_MEMBER(pDesc, "#06RX16", uPartitionNo);
4766 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, PartitionContents);
4767 if (UDF_ENTITY_ID_EQUALS(&pDesc->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
4768 {
4769 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceTable);
4770 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceBitmap);
4771 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.PartitionIntegrityTable);
4772 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceTable);
4773 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceBitmap);
4774 if (!ASMMemIsZero(&pDesc->ContentsUse.Hdr.abReserved[0], sizeof(pDesc->ContentsUse.Hdr.abReserved)))
4775 Log2(("ISO/UDF: %-32s\n%.88RhxD\n", "Hdr.abReserved[88]:", &pDesc->ContentsUse.Hdr.abReserved[0]));
4776 }
4777 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
4778 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ContentsUse.ab[128]:", &pDesc->ContentsUse.ab[0]));
4779 UDF_LOG2_MEMBER(pDesc, "#010RX32", uAccessType);
4780 UDF_LOG2_MEMBER(pDesc, "#010RX32", offLocation);
4781 UDF_LOG2_MEMBER(pDesc, "#010RX32", cSectors);
4782 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4783 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4784 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
4785
4786 if (!ASMMemIsZero(&pDesc->abReserved[0], sizeof(pDesc->abReserved)))
4787 Log2(("ISO/UDF: %-32s\n%.156RhxD\n", "ImplementationUse.ab[156]:", &pDesc->abReserved[0]));
4788 }
4789#endif
4790
4791 /*
4792 * Check if this is a newer revision of an existing primary volume descriptor.
4793 */
4794 PUDFPARTITIONDESC pEndianConvert = NULL;
4795 uint32_t i = pInfo->cPartitions;
4796 while (i--> 0)
4797 if (pDesc->uPartitionNo == pInfo->apPartitions[i]->uPartitionNo)
4798 {
4799 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPartitions[i]->uVolumeDescSeqNo)
4800 {
4801 Log(("ISO/UDF: Partition descriptor for part %#u prevails over previous! (%u >= %u)\n",
4802 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4803 pEndianConvert = pInfo->apPartitions[i];
4804 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
4805 }
4806 else
4807 Log(("ISO/UDF: Partition descriptor for part %#u has a lower sequence number than the previous! (%u < %u)\n",
4808 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4809 break;
4810 }
4811 if (i >= pInfo->cPartitions)
4812 {
4813 /*
4814 * It wasn't. Append it.
4815 */
4816 i = pInfo->cPartitions;
4817 if (i < RT_ELEMENTS(pInfo->apPartitions))
4818 {
4819 pInfo->apPartitions[i] = pEndianConvert = (PUDFPARTITIONDESC)RTMemDup(pDesc, sizeof(*pDesc));
4820 if (pEndianConvert)
4821 pInfo->cPartitions = i + 1;
4822 else
4823 return VERR_NO_MEMORY;
4824 Log2(("ISO/UDF: ++New partition descriptor.\n"));
4825 }
4826 else
4827 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PDS, "Too many physical volume descriptors");
4828 }
4829
4830#ifdef RT_BIG_ENDIAN
4831 /*
4832 * Do endian conversion of the descriptor.
4833 */
4834 if (pEndianConvert)
4835 {
4836 AssertFailed();
4837 }
4838#else
4839 RT_NOREF(pEndianConvert);
4840#endif
4841 return VINF_SUCCESS;
4842}
4843
4844
4845/**
4846 * Processes an implementation use descriptor in the VDS (UDF).
4847 *
4848 * @returns IPRT status code.
4849 * @param pInfo Where we gather descriptor information.
4850 * @param pDesc The descriptor.
4851 * @param pErrInfo Where to return extended error information.
4852 */
4853static int rtFsIsoVolProcessUdfImplUseVolDesc(PRTFSISOVDSINFO pInfo, PCUDFIMPLEMENTATIONUSEVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
4854{
4855#ifdef LOG_ENABLED
4856 Log(("ISO/UDF: Implementation use volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4857 if (LogIs2Enabled())
4858 {
4859 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4860 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4861 if (UDF_ENTITY_ID_EQUALS(&pDesc->idImplementation, UDF_ENTITY_ID_IUVD_IMPLEMENTATION))
4862 {
4863 UDF_LOG2_MEMBER_CHARSPEC(&pDesc->ImplementationUse, Lvi.Charset);
4864 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achVolumeID);
4865 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo1);
4866 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo2);
4867 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo3);
4868 UDF_LOG2_MEMBER_ENTITY_ID(&pDesc->ImplementationUse, Lvi.idImplementation);
4869 if (!ASMMemIsZero(&pDesc->ImplementationUse.Lvi.abUse[0], sizeof(pDesc->ImplementationUse.Lvi.abUse)))
4870 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "Lvi.abUse[128]:", &pDesc->ImplementationUse.Lvi.abUse[0]));
4871 }
4872 else if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4873 Log2(("ISO/UDF: %-32s\n%.460RhxD\n", "ImplementationUse.ab[460]:", &pDesc->ImplementationUse.ab[0]));
4874 }
4875#endif
4876
4877 RT_NOREF(pInfo, pDesc, pErrInfo);
4878 return VINF_SUCCESS;
4879}
4880
4881
4882
4883typedef struct RTFSISOSEENSEQENCES
4884{
4885 /** Number of sequences we've seen thus far. */
4886 uint32_t cSequences;
4887 /** The per sequence data. */
4888 struct
4889 {
4890 uint64_t off; /**< Byte offset of the sequence. */
4891 uint32_t cb; /**< Size of the sequence. */
4892 } aSequences[8];
4893} RTFSISOSEENSEQENCES;
4894typedef RTFSISOSEENSEQENCES *PRTFSISOSEENSEQENCES;
4895
4896
4897
4898/**
4899 * Process a VDS sequence, recursively dealing with volume descriptor pointers.
4900 *
4901 * This function only gathers information from the sequence, handling the
4902 * prevailing descriptor fun.
4903 *
4904 * @returns IPRT status code.
4905 * @param pThis The instance.
4906 * @param pInfo Where to store info from the VDS sequence.
4907 * @param offSeq The byte offset of the sequence.
4908 * @param cbSeq The length of the sequence.
4909 * @param pbBuf Read buffer.
4910 * @param cbBuf Size of the read buffer. This is at least one
4911 * sector big.
4912 * @param cNestings The VDS nesting depth.
4913 * @param pErrInfo Where to return extended error info.
4914 */
4915static int rtFsIsoVolReadAndProcessUdfVdsSeq(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, uint64_t offSeq, uint32_t cbSeq,
4916 uint8_t *pbBuf, size_t cbBuf, uint32_t cNestings, PRTERRINFO pErrInfo)
4917{
4918 AssertReturn(cbBuf >= pThis->cbSector, VERR_INTERNAL_ERROR);
4919
4920 /*
4921 * Check nesting depth.
4922 */
4923 if (cNestings > 5)
4924 return RTERRINFO_LOG_SET(pErrInfo, VERR_TOO_MUCH_DATA, "The volume descriptor sequence (VDS) is nested too deeply.");
4925
4926
4927 /*
4928 * Do the processing sector by sector to keep things simple.
4929 */
4930 uint32_t offInSeq = 0;
4931 while (offInSeq < cbSeq)
4932 {
4933 int rc;
4934
4935 /*
4936 * Read the next sector. Zero pad if less that a sector.
4937 */
4938 Assert((offInSeq & (pThis->cbSector - 1)) == 0);
4939 rc = RTVfsFileReadAt(pThis->hVfsBacking, offSeq + offInSeq, pbBuf, pThis->cbSector, NULL);
4940 if (RT_FAILURE(rc))
4941 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error reading VDS content at %RX64 (LB %#x): %Rrc",
4942 offSeq + offInSeq, pThis->cbSector, rc);
4943 if (cbSeq - offInSeq < pThis->cbSector)
4944 memset(&pbBuf[cbSeq - offInSeq], 0, pThis->cbSector - (cbSeq - offInSeq));
4945
4946 /*
4947 * Check tag.
4948 */
4949 PCUDFTAG pTag = (PCUDFTAG)pbBuf;
4950 rc = rtFsIsoVolValidateUdfDescTagAndCrc(pTag, pThis->cbSector, UINT16_MAX, (offSeq + offInSeq) / pThis->cbSector, pErrInfo);
4951 if ( RT_SUCCESS(rc)
4952 || ( rc == VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4953 && ( pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC
4954 || pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_DESC
4955 || pTag->idTag == UDF_TAG_ID_UNALLOCATED_SPACE_DESC
4956 )
4957 )
4958 )
4959 {
4960 switch (pTag->idTag)
4961 {
4962 case UDF_TAG_ID_PRIMARY_VOL_DESC:
4963 rc = rtFsIsoVolProcessUdfPrimaryVolDesc(pInfo, (PCUDFPRIMARYVOLUMEDESC)pTag, pErrInfo);
4964 break;
4965
4966 case UDF_TAG_ID_IMPLEMENTATION_USE_VOLUME_DESC:
4967 rc = rtFsIsoVolProcessUdfImplUseVolDesc(pInfo, (PCUDFIMPLEMENTATIONUSEVOLUMEDESC)pTag, pErrInfo);
4968 break;
4969
4970 case UDF_TAG_ID_PARTITION_DESC:
4971 rc = rtFsIsoVolProcessUdfPartitionDesc(pInfo, (PCUDFPARTITIONDESC)pTag, pErrInfo);
4972 break;
4973
4974 case UDF_TAG_ID_LOGICAL_VOLUME_DESC:
4975 if (rc != VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC)
4976 rc = rtFsIsoVolProcessUdfLogicalVolumeDesc(pInfo, (PCUDFLOGICALVOLUMEDESC)pTag,
4977 pThis->cbSector, pErrInfo);
4978 else
4979 rc = VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD;
4980 break;
4981
4982 case UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC:
4983 Log(("ISO/UDF: Ignoring logical volume integrity descriptor at offset %#RX64.\n", offSeq + offInSeq));
4984 rc = VINF_SUCCESS;
4985 break;
4986
4987 case UDF_TAG_ID_UNALLOCATED_SPACE_DESC:
4988 Log(("ISO/UDF: Ignoring unallocated space descriptor at offset %#RX64.\n", offSeq + offInSeq));
4989 rc = VINF_SUCCESS;
4990 break;
4991
4992 case UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR:
4993 Log(("ISO/UDF: Ignoring AVDP in VDS (at offset %#RX64).\n", offSeq + offInSeq));
4994 rc = VINF_SUCCESS;
4995 break;
4996
4997 case UDF_TAG_ID_VOLUME_DESC_PTR:
4998 {
4999 PCUDFVOLUMEDESCPTR pVdp = (PCUDFVOLUMEDESCPTR)pTag;
5000 Log(("ISO/UDF: Processing volume descriptor pointer at offset %#RX64: %#x LB %#x (seq %#x); cNestings=%d\n",
5001 offSeq + offInSeq, pVdp->NextVolumeDescSeq.off, pVdp->NextVolumeDescSeq.cb,
5002 pVdp->uVolumeDescSeqNo, cNestings));
5003 rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, pInfo, (uint64_t)pVdp->NextVolumeDescSeq.off * pThis->cbSector,
5004 pVdp->NextVolumeDescSeq.cb, pbBuf, cbBuf, cNestings + 1, pErrInfo);
5005 break;
5006 }
5007
5008 case UDF_TAG_ID_TERMINATING_DESC:
5009 Log(("ISO/UDF: Terminating descriptor at offset %#RX64\n", offSeq + offInSeq));
5010 return VINF_SUCCESS;
5011
5012 default:
5013 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNEXPECTED_VDS_DESC,
5014 "Unexpected/unknown VDS descriptor %#x at byte offset %#RX64",
5015 pThis->cbSector, offSeq + offInSeq);
5016 }
5017 if (RT_FAILURE(rc))
5018 return rc;
5019 }
5020 /* The descriptor sequence is usually zero padded to 16 sectors. Just
5021 ignore zero descriptors. */
5022 else if (rc != VERR_ISOFS_TAG_IS_ALL_ZEROS)
5023 return rc;
5024
5025 /*
5026 * Advance.
5027 */
5028 offInSeq += pThis->cbSector;
5029 }
5030
5031 return VINF_SUCCESS;
5032}
5033
5034
5035
5036/**
5037 * Processes a volume descriptor sequence (VDS).
5038 *
5039 * @returns IPRT status code.
5040 * @param pThis The instance.
5041 * @param offSeq The byte offset of the sequence.
5042 * @param cbSeq The length of the sequence.
5043 * @param pSeenSequences Structure where to keep track of VDSes we've already
5044 * processed, to avoid redoing one that we don't
5045 * understand.
5046 * @param pbBuf Read buffer.
5047 * @param cbBuf Size of the read buffer. This is at least one
5048 * sector big.
5049 * @param pErrInfo Where to report extended error information.
5050 */
5051static int rtFsIsoVolReadAndProcessUdfVds(PRTFSISOVOL pThis, uint64_t offSeq, uint32_t cbSeq,
5052 PRTFSISOSEENSEQENCES pSeenSequences, uint8_t *pbBuf, size_t cbBuf,
5053 PRTERRINFO pErrInfo)
5054{
5055 /*
5056 * Skip if already seen.
5057 */
5058 uint32_t i = pSeenSequences->cSequences;
5059 while (i-- > 0)
5060 if ( pSeenSequences->aSequences[i].off == offSeq
5061 && pSeenSequences->aSequences[i].cb == cbSeq)
5062 return VERR_NOT_FOUND;
5063
5064 /* Not seen, so add it. */
5065 Assert(pSeenSequences->cSequences + 1 <= RT_ELEMENTS(pSeenSequences->aSequences));
5066 pSeenSequences->aSequences[pSeenSequences->cSequences].cb = cbSeq;
5067 pSeenSequences->aSequences[pSeenSequences->cSequences].off = offSeq;
5068 pSeenSequences->cSequences++;
5069
5070 LogFlow(("ISO/UDF: Processing anchor volume descriptor sequence at offset %#RX64 LB %#RX32\n", offSeq, cbSeq));
5071
5072 /*
5073 * Gather relevant descriptor info from the VDS then process it and on
5074 * success copy it into the instance.
5075 *
5076 * The processing has to be done in a different function because there may
5077 * be links to sub-sequences that needs to be processed. We do this by
5078 * recursing and check that we don't go to deep.
5079 */
5080 RTFSISOVDSINFO Info;
5081 RT_ZERO(Info);
5082 int rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, &Info, offSeq, cbSeq, pbBuf, cbBuf, 0, pErrInfo);
5083 if (RT_SUCCESS(rc))
5084 {
5085 rc = rtFsIsoVolProcessUdfVdsSeqInfo(pThis, &Info, pErrInfo);
5086 if (RT_SUCCESS(rc))
5087 rc = rtFsIsoVolProcessUdfFileSetDescs(pThis, pbBuf, cbBuf, pErrInfo);
5088 }
5089
5090 /*
5091 * Clean up info.
5092 */
5093 i = Info.cPrimaryVols;
5094 while (i-- > 0)
5095 RTMemFree(Info.apPrimaryVols[i]);
5096
5097 i = Info.cLogicalVols;
5098 while (i-- > 0)
5099 RTMemFree(Info.apLogicalVols[i]);
5100
5101 i = Info.cPartitions;
5102 while (i-- > 0)
5103 RTMemFree(Info.apPartitions[i]);
5104
5105 RTMemFree(Info.paPartMaps);
5106
5107 return rc;
5108}
5109
5110
5111static int rtFsIsoVolReadAndHandleUdfAvdp(PRTFSISOVOL pThis, uint64_t offAvdp, uint8_t *pbBuf, size_t cbBuf,
5112 PRTFSISOSEENSEQENCES pSeenSequences, PRTERRINFO pErrInfo)
5113{
5114 /*
5115 * Try read the descriptor and validate its tag.
5116 */
5117 PUDFANCHORVOLUMEDESCPTR pAvdp = (PUDFANCHORVOLUMEDESCPTR)pbBuf;
5118 size_t cbAvdpRead = RT_MIN(pThis->cbSector, cbBuf);
5119 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offAvdp, pAvdp, cbAvdpRead, NULL);
5120 if (RT_SUCCESS(rc))
5121 {
5122 rc = rtFsIsoVolValidateUdfDescTag(&pAvdp->Tag, UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR, offAvdp / pThis->cbSector, pErrInfo);
5123 if (RT_SUCCESS(rc))
5124 {
5125 Log2(("ISO/UDF: AVDP: MainVolumeDescSeq=%#RX32 LB %#RX32, ReserveVolumeDescSeq=%#RX32 LB %#RX32\n",
5126 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb,
5127 pAvdp->ReserveVolumeDescSeq.off, pAvdp->ReserveVolumeDescSeq.cb));
5128
5129 /*
5130 * Try the main sequence if it looks sane.
5131 */
5132 UDFEXTENTAD const ReserveVolumeDescSeq = pAvdp->ReserveVolumeDescSeq;
5133 if ( pAvdp->MainVolumeDescSeq.off < pThis->cBackingSectors
5134 && (uint64_t)pAvdp->MainVolumeDescSeq.off
5135 + (pAvdp->MainVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
5136 <= pThis->cBackingSectors)
5137 {
5138 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)pAvdp->MainVolumeDescSeq.off * pThis->cbSector,
5139 pAvdp->MainVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
5140 if (RT_SUCCESS(rc))
5141 return rc;
5142 }
5143 else
5144 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
5145 "MainVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
5146 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb, pThis->cBackingSectors);
5147 if (ReserveVolumeDescSeq.cb > 0)
5148 {
5149 if ( ReserveVolumeDescSeq.off < pThis->cBackingSectors
5150 && (uint64_t)ReserveVolumeDescSeq.off
5151 + (ReserveVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
5152 <= pThis->cBackingSectors)
5153 {
5154 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)ReserveVolumeDescSeq.off * pThis->cbSector,
5155 ReserveVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
5156 if (RT_SUCCESS(rc))
5157 return rc;
5158 }
5159 else if (RT_SUCCESS(rc))
5160 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
5161 "ReserveVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
5162 ReserveVolumeDescSeq.off, ReserveVolumeDescSeq.cb, pThis->cBackingSectors);
5163 }
5164 }
5165 }
5166 else
5167 rc = RTERRINFO_LOG_SET_F(pErrInfo, rc,
5168 "Error reading sector at offset %#RX64 (anchor volume descriptor pointer): %Rrc", offAvdp, rc);
5169
5170 return rc;
5171}
5172
5173
5174/**
5175 * Goes looking for UDF when we've seens a volume recognition sequence.
5176 *
5177 * @returns IPRT status code.
5178 * @param pThis The volume instance data.
5179 * @param puUdfLevel The UDF level indicated by the VRS.
5180 * @param offUdfBootVolDesc The offset of the BOOT2 descriptor, UINT64_MAX
5181 * if not encountered.
5182 * @param pbBuf Buffer for reading into.
5183 * @param cbBuf The size of the buffer. At least one sector.
5184 * @param pErrInfo Where to return extended error info.
5185 */
5186static int rtFsIsoVolHandleUdfDetection(PRTFSISOVOL pThis, uint8_t *puUdfLevel, uint64_t offUdfBootVolDesc,
5187 uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
5188{
5189 NOREF(offUdfBootVolDesc);
5190
5191 /*
5192 * There are up to three anchor volume descriptor pointers that can give us
5193 * two different descriptor sequences each. Usually, the different AVDP
5194 * structures points to the same two sequences. The idea here is that
5195 * sectors may deteriorate and become unreadable, and we're supposed to try
5196 * out alternative sectors to get the job done. If we really took this
5197 * seriously, we could try read all sequences in parallel and use the
5198 * sectors that are good. However, we'll try keep things reasonably simple
5199 * since we'll most likely be reading from hard disks rather than optical
5200 * media.
5201 *
5202 * We keep track of which sequences we've processed so we don't try to do it
5203 * again when alternative AVDP sectors points to the same sequences.
5204 */
5205 pThis->Udf.uLevel = *puUdfLevel;
5206 RTFSISOSEENSEQENCES SeenSequences = { 0 };
5207 int rc1 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, 256 * pThis->cbSector, pbBuf, cbBuf,
5208 &SeenSequences, pErrInfo);
5209 if (RT_SUCCESS(rc1))
5210 return rc1;
5211
5212 int rc2 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - 256 * pThis->cbSector,
5213 pbBuf, cbBuf, &SeenSequences, pErrInfo);
5214 if (RT_SUCCESS(rc2))
5215 return rc2;
5216
5217 int rc3 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - pThis->cbSector,
5218 pbBuf, cbBuf, &SeenSequences, pErrInfo);
5219 if (RT_SUCCESS(rc3))
5220 return rc3;
5221
5222 /*
5223 * Return failure if the alternatives have been excluded.
5224 *
5225 * Note! The error info won't be correct here.
5226 */
5227 pThis->Udf.uLevel = *puUdfLevel = 0;
5228
5229 if (RTFSISO9660_F_IS_ONLY_TYPE(pThis->fFlags, RTFSISO9660_F_NO_UDF))
5230 return rc1 != VERR_NOT_FOUND ? rc1 : rc2 != VERR_NOT_FOUND ? rc2 : rc3;
5231 return VINF_SUCCESS;
5232}
5233
5234
5235
5236#ifdef LOG_ENABLED
5237
5238/** Logging helper. */
5239static size_t rtFsIsoVolGetStrippedLength(const char *pachField, size_t cchField)
5240{
5241 while (cchField > 0 && pachField[cchField - 1] == ' ')
5242 cchField--;
5243 return cchField;
5244}
5245
5246/** Logging helper. */
5247static char *rtFsIsoVolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
5248{
5249 /* Check the format by looking for zero bytes. ISO-9660 doesn't allow zeros.
5250 This doesn't have to be a UTF-16BE string. */
5251 size_t cFirstZeros = 0;
5252 size_t cSecondZeros = 0;
5253 for (size_t off = 0; off + 1 < cchField; off += 2)
5254 {
5255 cFirstZeros += pachField[off] == '\0';
5256 cSecondZeros += pachField[off + 1] == '\0';
5257 }
5258
5259 int rc = VINF_SUCCESS;
5260 char *pszTmp = &pszDst[10];
5261 size_t cchRet = 0;
5262 if (cFirstZeros > cSecondZeros)
5263 {
5264 /* UTF-16BE / UTC-2BE: */
5265 if (cchField & 1)
5266 {
5267 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
5268 cchField--;
5269 else
5270 rc = VERR_INVALID_UTF16_ENCODING;
5271 }
5272 if (RT_SUCCESS(rc))
5273 {
5274 while ( cchField >= 2
5275 && pachField[cchField - 1] == ' '
5276 && pachField[cchField - 2] == '\0')
5277 cchField -= 2;
5278
5279 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
5280 }
5281 if (RT_SUCCESS(rc))
5282 {
5283 pszDst[0] = 'U';
5284 pszDst[1] = 'T';
5285 pszDst[2] = 'F';
5286 pszDst[3] = '-';
5287 pszDst[4] = '1';
5288 pszDst[5] = '6';
5289 pszDst[6] = 'B';
5290 pszDst[7] = 'E';
5291 pszDst[8] = ':';
5292 pszDst[9] = '\'';
5293 pszDst[10 + cchRet] = '\'';
5294 pszDst[10 + cchRet + 1] = '\0';
5295 }
5296 else
5297 RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
5298 }
5299 else if (cSecondZeros > 0)
5300 {
5301 /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
5302 if (cchField & 1)
5303 {
5304 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
5305 cchField--;
5306 else
5307 rc = VERR_INVALID_UTF16_ENCODING;
5308 }
5309 if (RT_SUCCESS(rc))
5310 {
5311 while ( cchField >= 2
5312 && pachField[cchField - 1] == '\0'
5313 && pachField[cchField - 2] == ' ')
5314 cchField -= 2;
5315
5316 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
5317 }
5318 if (RT_SUCCESS(rc))
5319 {
5320 pszDst[0] = 'U';
5321 pszDst[1] = 'T';
5322 pszDst[2] = 'F';
5323 pszDst[3] = '-';
5324 pszDst[4] = '1';
5325 pszDst[5] = '6';
5326 pszDst[6] = 'L';
5327 pszDst[7] = 'E';
5328 pszDst[8] = ':';
5329 pszDst[9] = '\'';
5330 pszDst[10 + cchRet] = '\'';
5331 pszDst[10 + cchRet + 1] = '\0';
5332 }
5333 else
5334 RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
5335 }
5336 else
5337 {
5338 /* ASSUME UTF-8/ASCII. */
5339 while ( cchField > 0
5340 && pachField[cchField - 1] == ' ')
5341 cchField--;
5342 rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
5343 if (RT_SUCCESS(rc))
5344 RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
5345 else
5346 RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
5347 }
5348 return pszDst;
5349}
5350
5351
5352/**
5353 * Logs the primary or supplementary volume descriptor
5354 *
5355 * @param pVolDesc The descriptor.
5356 */
5357static void rtFsIsoVolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
5358{
5359 if (LogIs2Enabled())
5360 {
5361 char szTmp[384];
5362 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
5363 Log2(("ISO9660: achSystemId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
5364 Log2(("ISO9660: achVolumeId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
5365 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
5366 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
5367 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIsoVolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
5368 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
5369 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
5370 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
5371 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
5372 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
5373 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
5374 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
5375 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
5376 Log2(("ISO9660: achVolumeSetId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
5377 Log2(("ISO9660: achPublisherId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
5378 Log2(("ISO9660: achDataPreparerId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
5379 Log2(("ISO9660: achApplicationId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
5380 Log2(("ISO9660: achCopyrightFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
5381 Log2(("ISO9660: achAbstractFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
5382 Log2(("ISO9660: achBibliographicFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
5383 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5384 pVolDesc->BirthTime.achYear,
5385 pVolDesc->BirthTime.achMonth,
5386 pVolDesc->BirthTime.achDay,
5387 pVolDesc->BirthTime.achHour,
5388 pVolDesc->BirthTime.achMinute,
5389 pVolDesc->BirthTime.achSecond,
5390 pVolDesc->BirthTime.achCentisecond,
5391 pVolDesc->BirthTime.offUtc*4/60));
5392 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5393 pVolDesc->ModifyTime.achYear,
5394 pVolDesc->ModifyTime.achMonth,
5395 pVolDesc->ModifyTime.achDay,
5396 pVolDesc->ModifyTime.achHour,
5397 pVolDesc->ModifyTime.achMinute,
5398 pVolDesc->ModifyTime.achSecond,
5399 pVolDesc->ModifyTime.achCentisecond,
5400 pVolDesc->ModifyTime.offUtc*4/60));
5401 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5402 pVolDesc->ExpireTime.achYear,
5403 pVolDesc->ExpireTime.achMonth,
5404 pVolDesc->ExpireTime.achDay,
5405 pVolDesc->ExpireTime.achHour,
5406 pVolDesc->ExpireTime.achMinute,
5407 pVolDesc->ExpireTime.achSecond,
5408 pVolDesc->ExpireTime.achCentisecond,
5409 pVolDesc->ExpireTime.offUtc*4/60));
5410 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5411 pVolDesc->EffectiveTime.achYear,
5412 pVolDesc->EffectiveTime.achMonth,
5413 pVolDesc->EffectiveTime.achDay,
5414 pVolDesc->EffectiveTime.achHour,
5415 pVolDesc->EffectiveTime.achMinute,
5416 pVolDesc->EffectiveTime.achSecond,
5417 pVolDesc->EffectiveTime.achCentisecond,
5418 pVolDesc->EffectiveTime.offUtc*4/60));
5419 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
5420 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
5421
5422 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
5423 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
5424 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
5425 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
5426 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
5427 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
5428 pVolDesc->RootDir.DirRec.RecTime.bMonth,
5429 pVolDesc->RootDir.DirRec.RecTime.bDay,
5430 pVolDesc->RootDir.DirRec.RecTime.bHour,
5431 pVolDesc->RootDir.DirRec.RecTime.bMinute,
5432 pVolDesc->RootDir.DirRec.RecTime.bSecond,
5433 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
5434 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
5435 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
5436 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
5437 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
5438 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
5439 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
5440 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
5441 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
5442 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
5443 {
5444 Log2(("ISO9660: RootDir System Use:\n%.*RhxD\n",
5445 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
5446 }
5447 }
5448}
5449
5450#endif /* LOG_ENABLED */
5451
5452/**
5453 * Deal with a root directory from a primary or supplemental descriptor.
5454 *
5455 * @returns IPRT status code.
5456 * @param pThis The ISO 9660 instance being initialized.
5457 * @param pRootDir The root directory record to check out.
5458 * @param pDstRootDir Where to store a copy of the root dir record.
5459 * @param pErrInfo Where to return additional error info. Can be NULL.
5460 */
5461static int rtFsIsoVolHandleRootDir(PRTFSISOVOL pThis, PCISO9660DIRREC pRootDir,
5462 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
5463{
5464 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
5465 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
5466 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
5467
5468 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
5469 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5470 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
5471 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
5472 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5473 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
5474
5475 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
5476 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
5477 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
5478 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
5479 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
5480
5481 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
5482 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
5483 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
5484
5485 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
5486 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
5487 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
5488 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
5489 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5490 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
5491 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
5492
5493 /*
5494 * Seems okay, copy it.
5495 */
5496 *pDstRootDir = *pRootDir;
5497 return VINF_SUCCESS;
5498}
5499
5500
5501/**
5502 * Deal with a primary volume descriptor.
5503 *
5504 * @returns IPRT status code.
5505 * @param pThis The ISO 9660 instance being initialized.
5506 * @param pVolDesc The volume descriptor to handle.
5507 * @param offVolDesc The disk offset of the volume descriptor.
5508 * @param pRootDir Where to return a copy of the root directory record.
5509 * @param poffRootDirRec Where to return the disk offset of the root dir.
5510 * @param pErrInfo Where to return additional error info. Can be NULL.
5511 */
5512static int rtFsIsoVolHandlePrimaryVolDesc(PRTFSISOVOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
5513 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
5514{
5515 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
5516 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5517 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
5518
5519 /*
5520 * We need the block size ...
5521 */
5522 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
5523 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
5524 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
5525 || pThis->cbBlock / pThis->cbSector < 1)
5526 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
5527 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
5528 if (pThis->cbBlock / pThis->cbSector > 128)
5529 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
5530
5531 /*
5532 * ... volume space size ...
5533 */
5534 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
5535 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
5536 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
5537 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
5538 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
5539
5540 /*
5541 * ... number of volumes in the set ...
5542 */
5543 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
5544 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
5545 || pThis->cVolumesInSet == 0)
5546 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
5547 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
5548 if (pThis->cVolumesInSet > 32)
5549 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
5550
5551 /*
5552 * ... primary volume sequence ID ...
5553 */
5554 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
5555 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
5556 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
5557 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
5558 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
5559 || pThis->idPrimaryVol < 1)
5560 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5561 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
5562
5563 /*
5564 * ... and the root directory record.
5565 */
5566 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
5567 return rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
5568}
5569
5570
5571/**
5572 * Deal with a supplementary volume descriptor.
5573 *
5574 * @returns IPRT status code.
5575 * @param pThis The ISO 9660 instance being initialized.
5576 * @param pVolDesc The volume descriptor to handle.
5577 * @param offVolDesc The disk offset of the volume descriptor.
5578 * @param pbUcs2Level Where to return the joliet level, if found. Caller
5579 * initializes this to zero, we'll return 1, 2 or 3 if
5580 * joliet was detected.
5581 * @param pRootDir Where to return the root directory, if found.
5582 * @param poffRootDirRec Where to return the disk offset of the root dir.
5583 * @param pErrInfo Where to return additional error info. Can be NULL.
5584 */
5585static int rtFsIsoVolHandleSupplementaryVolDesc(PRTFSISOVOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
5586 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
5587 PRTERRINFO pErrInfo)
5588{
5589 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
5590 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5591 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
5592
5593 /*
5594 * Is this a joliet volume descriptor? If not, we probably don't need to
5595 * care about it.
5596 */
5597 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
5598 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
5599 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
5600 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
5601 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
5602 return VINF_SUCCESS;
5603
5604 /*
5605 * Skip if joliet is unwanted.
5606 */
5607 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
5608 return VINF_SUCCESS;
5609
5610 /*
5611 * Check that the joliet descriptor matches the primary one.
5612 * Note! These are our assumptions and may be wrong.
5613 */
5614 if (pThis->cbBlock == 0)
5615 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5616 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
5617 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
5618 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5619 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
5620 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
5621 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
5622 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5623 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
5624 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
5625 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
5626 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5627 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
5628 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
5629 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
5630 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5631 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
5632 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
5633
5634 if (*pbUcs2Level != 0)
5635 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
5636
5637 /*
5638 * Switch to the joliet root dir as it has UTF-16 stuff in it.
5639 */
5640 int rc = rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
5641 if (RT_SUCCESS(rc))
5642 {
5643 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
5644 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
5645 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
5646 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
5647 }
5648 return rc;
5649}
5650
5651
5652
5653/**
5654 * Worker for RTFsIso9660VolOpen.
5655 *
5656 * @returns IPRT status code.
5657 * @param pThis The ISO VFS instance to initialize.
5658 * @param hVfsSelf The ISO VFS handle (no reference consumed).
5659 * @param hVfsBacking The file backing the alleged FAT file system.
5660 * Reference is consumed (via rtFsIsoVol_Close).
5661 * @param fFlags Flags, RTFSISO9660_F_XXX.
5662 * @param pErrInfo Where to return additional error info. Can be NULL.
5663 */
5664static int rtFsIsoVolTryInit(PRTFSISOVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
5665{
5666 uint32_t const cbSector = 2048;
5667
5668 /*
5669 * First initialize the state so that rtFsIsoVol_Destroy won't trip up.
5670 */
5671 pThis->hVfsSelf = hVfsSelf;
5672 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIsoVol_Destroy releases it. */
5673 pThis->cbBacking = 0;
5674 pThis->cBackingSectors = 0;
5675 pThis->fFlags = fFlags;
5676 pThis->cbSector = cbSector;
5677 pThis->cbBlock = 0;
5678 pThis->cBlocksInPrimaryVolumeSpace = 0;
5679 pThis->cbPrimaryVolumeSpace = 0;
5680 pThis->cVolumesInSet = 0;
5681 pThis->idPrimaryVol = UINT32_MAX;
5682 pThis->fIsUtf16 = false;
5683 pThis->pRootDir = NULL;
5684
5685 /*
5686 * Get stuff that may fail.
5687 */
5688 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
5689 if (RT_SUCCESS(rc))
5690 pThis->cBackingSectors = pThis->cbBacking / pThis->cbSector;
5691 else
5692 return rc;
5693
5694 /*
5695 * Read the volume descriptors starting at logical sector 16.
5696 */
5697 union
5698 {
5699 uint8_t ab[RTFSISO_MAX_LOGICAL_BLOCK_SIZE];
5700 uint16_t au16[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 2];
5701 uint32_t au32[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 4];
5702 ISO9660VOLDESCHDR VolDescHdr;
5703 ISO9660BOOTRECORD BootRecord;
5704 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
5705 ISO9660SUPVOLDESC SupVolDesc;
5706 ISO9660VOLPARTDESC VolPartDesc;
5707 } Buf;
5708 RT_ZERO(Buf);
5709
5710 uint64_t offRootDirRec = UINT64_MAX;
5711 ISO9660DIRREC RootDir;
5712 RT_ZERO(RootDir);
5713
5714 uint64_t offJolietRootDirRec = UINT64_MAX;
5715 uint8_t bJolietUcs2Level = 0;
5716 ISO9660DIRREC JolietRootDir;
5717 RT_ZERO(JolietRootDir);
5718
5719 uint8_t uUdfLevel = 0;
5720 uint64_t offUdfBootVolDesc = UINT64_MAX;
5721
5722 uint32_t cPrimaryVolDescs = 0;
5723 uint32_t cSupplementaryVolDescs = 0;
5724 uint32_t cBootRecordVolDescs = 0;
5725 uint32_t offVolDesc = 16 * cbSector;
5726 enum
5727 {
5728 kStateStart = 0,
5729 kStateNoSeq,
5730 kStateCdSeq,
5731 kStateUdfSeq
5732 } enmState = kStateStart;
5733 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
5734 {
5735 if (iVolDesc > 32)
5736 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "More than 32 volume descriptors, doesn't seem right...");
5737
5738 /* Read the next one and check the signature. */
5739 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
5740 if (RT_FAILURE(rc))
5741 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
5742
5743#define MATCH_STD_ID(a_achStdId1, a_szStdId2) \
5744 ( (a_achStdId1)[0] == (a_szStdId2)[0] \
5745 && (a_achStdId1)[1] == (a_szStdId2)[1] \
5746 && (a_achStdId1)[2] == (a_szStdId2)[2] \
5747 && (a_achStdId1)[3] == (a_szStdId2)[3] \
5748 && (a_achStdId1)[4] == (a_szStdId2)[4] )
5749#define MATCH_HDR(a_pStd, a_bType2, a_szStdId2, a_bVer2) \
5750 ( MATCH_STD_ID((a_pStd)->achStdId, a_szStdId2) \
5751 && (a_pStd)->bDescType == (a_bType2) \
5752 && (a_pStd)->bDescVersion == (a_bVer2) )
5753
5754 /*
5755 * ISO 9660 ("CD001").
5756 */
5757 if ( ( enmState == kStateStart
5758 || enmState == kStateCdSeq
5759 || enmState == kStateNoSeq)
5760 && MATCH_STD_ID(Buf.VolDescHdr.achStdId, ISO9660VOLDESC_STD_ID) )
5761 {
5762 enmState = kStateCdSeq;
5763
5764 /* Do type specific handling. */
5765 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
5766 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
5767 {
5768 cPrimaryVolDescs++;
5769 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
5770 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5771 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
5772#ifdef LOG_ENABLED
5773 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
5774#endif
5775 if (cPrimaryVolDescs > 1)
5776 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
5777 rc = rtFsIsoVolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
5778 }
5779 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
5780 {
5781 cSupplementaryVolDescs++;
5782 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
5783 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5784 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
5785#ifdef LOG_ENABLED
5786 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
5787#endif
5788 rc = rtFsIsoVolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
5789 &offJolietRootDirRec, pErrInfo);
5790 }
5791 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
5792 {
5793 cBootRecordVolDescs++;
5794 }
5795 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
5796 {
5797 if (!cPrimaryVolDescs)
5798 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
5799 enmState = kStateNoSeq;
5800 }
5801 else
5802 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5803 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
5804 }
5805 /*
5806 * UDF volume recognition sequence (VRS).
5807 */
5808 else if ( ( enmState == kStateNoSeq
5809 || enmState == kStateStart)
5810 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BEGIN, UDF_EXT_VOL_DESC_VERSION) )
5811 {
5812 if (uUdfLevel == 0)
5813 enmState = kStateUdfSeq;
5814 else
5815 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BEA01 sequence is supported");
5816 }
5817 else if ( enmState == kStateUdfSeq
5818 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_02, UDF_EXT_VOL_DESC_VERSION) )
5819 uUdfLevel = 2;
5820 else if ( enmState == kStateUdfSeq
5821 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_03, UDF_EXT_VOL_DESC_VERSION) )
5822 uUdfLevel = 3;
5823 else if ( enmState == kStateUdfSeq
5824 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BOOT, UDF_EXT_VOL_DESC_VERSION) )
5825 {
5826 if (offUdfBootVolDesc == UINT64_MAX)
5827 offUdfBootVolDesc = iVolDesc * cbSector;
5828 else
5829 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BOOT2 descriptor is supported");
5830 }
5831 else if ( enmState == kStateUdfSeq
5832 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_TERM, UDF_EXT_VOL_DESC_VERSION) )
5833 {
5834 if (uUdfLevel != 0)
5835 enmState = kStateNoSeq;
5836 else
5837 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Found BEA01 & TEA01, but no NSR02 or NSR03 descriptors");
5838 }
5839 /*
5840 * Unknown, probably the end.
5841 */
5842 else if (enmState == kStateNoSeq)
5843 break;
5844 else if (enmState == kStateStart)
5845 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5846 "Not ISO? Unable to recognize volume descriptor signature: %.5Rhxs", Buf.VolDescHdr.achStdId);
5847 else if (enmState == kStateCdSeq)
5848 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5849 "Missing ISO 9660 terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
5850 else if (enmState == kStateUdfSeq)
5851 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5852 "Missing UDF terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
5853 else
5854 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5855 "Unknown volume descriptor signature found at sector %u: %.5Rhxs",
5856 16 + iVolDesc, Buf.VolDescHdr.achStdId);
5857 if (RT_FAILURE(rc))
5858 return rc;
5859 }
5860
5861 /*
5862 * If we found a UDF VRS and are interested in UDF, we have more work to do here.
5863 */
5864 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF) )
5865 {
5866 Log(("rtFsIsoVolTryInit: uUdfLevel=%d\n", uUdfLevel));
5867 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
5868 if (RT_FAILURE(rc))
5869 return rc;
5870 }
5871
5872 /*
5873 * Decide which to prefer.
5874 *
5875 * By default we pick UDF over any of the two ISO 9960, there is currently
5876 * no way to override this without using the RTFSISO9660_F_NO_XXX options.
5877 *
5878 * If there isn't UDF, we may faced with choosing between joliet and rock
5879 * ridge. The joliet option is generally favorable as we don't have to
5880 * guess wrt to the file name encoding. So, we'll pick that for now.
5881 *
5882 * Note! Should we change this preference for joliet, there fun wrt making sure
5883 * there really is rock ridge stuff in the primary volume as well as
5884 * making sure there really is anything of value in the primary volume.
5885 */
5886 if (uUdfLevel > 0)
5887 {
5888 pThis->enmType = RTFSISOVOLTYPE_UDF;
5889 rc = rtFsIsoDirShrd_NewUdf(pThis, NULL /*pParent*/, &pThis->Udf.VolInfo.RootDirIcb,
5890 NULL /*pFileIdDesc*/, 0 /*offInDir*/, &pThis->pRootDir);
5891 /** @todo fall back on failure? */
5892 return rc;
5893 }
5894 if (bJolietUcs2Level != 0)
5895 {
5896 pThis->enmType = RTFSISOVOLTYPE_JOLIET;
5897 pThis->fIsUtf16 = true;
5898 return rtFsIsoDirShrd_New9660(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
5899 }
5900 pThis->enmType = RTFSISOVOLTYPE_ISO9960;
5901 return rtFsIsoDirShrd_New9660(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
5902}
5903
5904
5905/**
5906 * Opens an ISO 9660 file system volume.
5907 *
5908 * @returns IPRT status code.
5909 * @param hVfsFileIn The file or device backing the volume.
5910 * @param fFlags RTFSISO9660_F_XXX.
5911 * @param phVfs Where to return the virtual file system handle.
5912 * @param pErrInfo Where to return additional error information.
5913 */
5914RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
5915{
5916 /*
5917 * Quick input validation.
5918 */
5919 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
5920 *phVfs = NIL_RTVFS;
5921 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
5922
5923 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
5924 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5925
5926 /*
5927 * Create a new FAT VFS instance and try initialize it using the given input file.
5928 */
5929 RTVFS hVfs = NIL_RTVFS;
5930 void *pvThis = NULL;
5931 int rc = RTVfsNew(&g_rtFsIsoVolOps, sizeof(RTFSISOVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
5932 if (RT_SUCCESS(rc))
5933 {
5934 rc = rtFsIsoVolTryInit((PRTFSISOVOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
5935 if (RT_SUCCESS(rc))
5936 *phVfs = hVfs;
5937 else
5938 RTVfsRelease(hVfs);
5939 }
5940 else
5941 RTVfsFileRelease(hVfsFileIn);
5942 return rc;
5943}
5944
5945
5946/**
5947 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
5948 */
5949static DECLCALLBACK(int) rtVfsChainIsoFsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
5950 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
5951{
5952 RT_NOREF(pProviderReg, pSpec);
5953
5954 /*
5955 * Basic checks.
5956 */
5957 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
5958 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
5959 if ( pElement->enmType != RTVFSOBJTYPE_VFS
5960 && pElement->enmType != RTVFSOBJTYPE_DIR)
5961 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
5962 if (pElement->cArgs > 1)
5963 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
5964
5965 /*
5966 * Parse the flag if present, save in pElement->uProvider.
5967 */
5968 uint32_t fFlags = 0;
5969 if (pElement->cArgs > 0)
5970 {
5971 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
5972 {
5973 const char *psz = pElement->paArgs[iArg].psz;
5974 if (*psz)
5975 {
5976 if (!strcmp(psz, "nojoliet"))
5977 fFlags |= RTFSISO9660_F_NO_JOLIET;
5978 else if (!strcmp(psz, "norock"))
5979 fFlags |= RTFSISO9660_F_NO_ROCK;
5980 else if (!strcmp(psz, "noudf"))
5981 fFlags |= RTFSISO9660_F_NO_UDF;
5982 else
5983 {
5984 *poffError = pElement->paArgs[iArg].offSpec;
5985 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
5986 }
5987 }
5988 }
5989 }
5990
5991 pElement->uProvider = fFlags;
5992 return VINF_SUCCESS;
5993}
5994
5995
5996/**
5997 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
5998 */
5999static DECLCALLBACK(int) rtVfsChainIsoFsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
6000 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
6001 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
6002{
6003 RT_NOREF(pProviderReg, pSpec, poffError);
6004
6005 int rc;
6006 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
6007 if (hVfsFileIn != NIL_RTVFSFILE)
6008 {
6009 RTVFS hVfs;
6010 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
6011 RTVfsFileRelease(hVfsFileIn);
6012 if (RT_SUCCESS(rc))
6013 {
6014 *phVfsObj = RTVfsObjFromVfs(hVfs);
6015 RTVfsRelease(hVfs);
6016 if (*phVfsObj != NIL_RTVFSOBJ)
6017 return VINF_SUCCESS;
6018 rc = VERR_VFS_CHAIN_CAST_FAILED;
6019 }
6020 }
6021 else
6022 rc = VERR_VFS_CHAIN_CAST_FAILED;
6023 return rc;
6024}
6025
6026
6027/**
6028 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
6029 */
6030static DECLCALLBACK(bool) rtVfsChainIsoFsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
6031 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
6032 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
6033{
6034 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
6035 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
6036 || !pReuseElement->paArgs[0].uProvider)
6037 return true;
6038 return false;
6039}
6040
6041
6042/** VFS chain element 'file'. */
6043static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
6044{
6045 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
6046 /* fReserved = */ 0,
6047 /* pszName = */ "isofs",
6048 /* ListEntry = */ { NULL, NULL },
6049 /* pszHelp = */ "Open a ISO 9660 or UDF file system, requires a file object on the left side.\n"
6050 "The 'noudf' option make it ignore any UDF.\n"
6051 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
6052 "The 'norock' option make it ignore any rock ridge info.\n",
6053 /* pfnValidate = */ rtVfsChainIsoFsVol_Validate,
6054 /* pfnInstantiate = */ rtVfsChainIsoFsVol_Instantiate,
6055 /* pfnCanReuseElement = */ rtVfsChainIsoFsVol_CanReuseElement,
6056 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
6057};
6058
6059RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
6060
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