VirtualBox

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

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

IPRT/vfs: Implemented RTVFsFileSetSize, RTVfsFileGetMaxSize and RTvfsFileQueryMaxSize.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 237.1 KB
Line 
1/* $Id: isovfs.cpp 69977 2017-12-07 13:02:36Z 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,pfnFlush}
2031 */
2032static DECLCALLBACK(int) rtFsIsoFile_Flush(void *pvThis)
2033{
2034 RT_NOREF(pvThis);
2035 return VINF_SUCCESS;
2036}
2037
2038
2039/**
2040 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
2041 */
2042static DECLCALLBACK(int) rtFsIsoFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
2043 uint32_t *pfRetEvents)
2044{
2045 NOREF(pvThis);
2046 int rc;
2047 if (fEvents != RTPOLL_EVT_ERROR)
2048 {
2049 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
2050 rc = VINF_SUCCESS;
2051 }
2052 else if (fIntr)
2053 rc = RTThreadSleep(cMillies);
2054 else
2055 {
2056 uint64_t uMsStart = RTTimeMilliTS();
2057 do
2058 rc = RTThreadSleep(cMillies);
2059 while ( rc == VERR_INTERRUPTED
2060 && !fIntr
2061 && RTTimeMilliTS() - uMsStart < cMillies);
2062 if (rc == VERR_INTERRUPTED)
2063 rc = VERR_TIMEOUT;
2064 }
2065 return rc;
2066}
2067
2068
2069/**
2070 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
2071 */
2072static DECLCALLBACK(int) rtFsIsoFile_Tell(void *pvThis, PRTFOFF poffActual)
2073{
2074 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2075 *poffActual = pThis->offFile;
2076 return VINF_SUCCESS;
2077}
2078
2079
2080/**
2081 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
2082 */
2083static DECLCALLBACK(int) rtFsIsoFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
2084{
2085 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2086 RTFOFF offNew;
2087 switch (uMethod)
2088 {
2089 case RTFILE_SEEK_BEGIN:
2090 offNew = offSeek;
2091 break;
2092 case RTFILE_SEEK_END:
2093 offNew = (RTFOFF)pThis->pShared->Core.cbObject + offSeek;
2094 break;
2095 case RTFILE_SEEK_CURRENT:
2096 offNew = (RTFOFF)pThis->offFile + offSeek;
2097 break;
2098 default:
2099 return VERR_INVALID_PARAMETER;
2100 }
2101 if (offNew >= 0)
2102 {
2103 if (offNew <= _4G)
2104 {
2105 pThis->offFile = offNew;
2106 *poffActual = offNew;
2107 return VINF_SUCCESS;
2108 }
2109 return VERR_OUT_OF_RANGE;
2110 }
2111 return VERR_NEGATIVE_SEEK;
2112}
2113
2114
2115/**
2116 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
2117 */
2118static DECLCALLBACK(int) rtFsIsoFile_QuerySize(void *pvThis, uint64_t *pcbFile)
2119{
2120 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2121 *pcbFile = pThis->pShared->Core.cbObject;
2122 return VINF_SUCCESS;
2123}
2124
2125
2126/**
2127 * ISO FS file operations.
2128 */
2129DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIsoFileOps =
2130{
2131 { /* Stream */
2132 { /* Obj */
2133 RTVFSOBJOPS_VERSION,
2134 RTVFSOBJTYPE_FILE,
2135 "FatFile",
2136 rtFsIsoFile_Close,
2137 rtFsIsoFile_QueryInfo,
2138 RTVFSOBJOPS_VERSION
2139 },
2140 RTVFSIOSTREAMOPS_VERSION,
2141 RTVFSIOSTREAMOPS_FEAT_NO_SG,
2142 rtFsIsoFile_Read,
2143 NULL /*Write*/,
2144 rtFsIsoFile_Flush,
2145 rtFsIsoFile_PollOne,
2146 rtFsIsoFile_Tell,
2147 NULL /*pfnSkip*/,
2148 NULL /*pfnZeroFill*/,
2149 RTVFSIOSTREAMOPS_VERSION,
2150 },
2151 RTVFSFILEOPS_VERSION,
2152 0,
2153 { /* ObjSet */
2154 RTVFSOBJSETOPS_VERSION,
2155 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
2156 NULL /*SetMode*/,
2157 NULL /*SetTimes*/,
2158 NULL /*SetOwner*/,
2159 RTVFSOBJSETOPS_VERSION
2160 },
2161 rtFsIsoFile_Seek,
2162 rtFsIsoFile_QuerySize,
2163 NULL /*SetSize*/,
2164 NULL /*QueryMaxSize*/,
2165 RTVFSFILEOPS_VERSION
2166};
2167
2168
2169/**
2170 * Instantiates a new file, from ISO 9660 info.
2171 *
2172 * @returns IPRT status code.
2173 * @param pThis The ISO volume instance.
2174 * @param pParentDir The parent directory (shared part).
2175 * @param pDirRec The directory record.
2176 * @param cDirRecs Number of directory records if more than one.
2177 * @param offDirRec The byte offset of the directory record.
2178 * @param offEntryInDir The byte offset of the directory entry in the parent
2179 * directory.
2180 * @param fOpen RTFILE_O_XXX flags.
2181 * @param uVersion The file version number (since the caller already
2182 * parsed the filename, we don't want to repeat the
2183 * effort here).
2184 * @param phVfsFile Where to return the file handle.
2185 */
2186static int rtFsIsoFile_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
2187 uint64_t offDirRec, uint64_t fOpen, uint32_t uVersion, PRTVFSFILE phVfsFile)
2188{
2189 AssertPtr(pParentDir);
2190
2191 /*
2192 * Create a VFS object.
2193 */
2194 PRTFSISOFILEOBJ pNewFile;
2195 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2196 phVfsFile, (void **)&pNewFile);
2197 if (RT_SUCCESS(rc))
2198 {
2199 /*
2200 * Look for existing shared object, create a new one if necessary.
2201 */
2202 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
2203 if (pShared)
2204 {
2205 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2206 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2207 pNewFile->offFile = 0;
2208 pNewFile->pShared = pShared;
2209 return VINF_SUCCESS;
2210 }
2211
2212 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2213 if (pShared)
2214 {
2215 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, uVersion, pThis);
2216 if (RT_SUCCESS(rc))
2217 {
2218 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2219 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2220 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2221 pNewFile->offFile = 0;
2222 pNewFile->pShared = pShared;
2223 return VINF_SUCCESS;
2224 }
2225 RTMemFree(pShared);
2226 }
2227 else
2228 rc = VERR_NO_MEMORY;
2229
2230 /* Destroy the file object. */
2231 pNewFile->offFile = 0;
2232 pNewFile->pShared = NULL;
2233 RTVfsFileRelease(*phVfsFile);
2234 }
2235 *phVfsFile = NIL_RTVFSFILE;
2236 return rc;
2237}
2238
2239
2240/**
2241 * Instantiates a new file, from UDF info.
2242 *
2243 * @returns IPRT status code.
2244 * @param pThis The ISO volume instance.
2245 * @param pParentDir The parent directory (shared part).
2246 * @param pFid The file ID descriptor. (Points to parent directory
2247 * content.)
2248 * @param fOpen RTFILE_O_XXX flags.
2249 * @param phVfsFile Where to return the file handle.
2250 */
2251static int rtFsIsoFile_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid,
2252 uint64_t fOpen, PRTVFSFILE phVfsFile)
2253{
2254 AssertPtr(pParentDir);
2255 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
2256 Assert(offInDir < pParentDir->cbDir);
2257 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DELETED));
2258 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY));
2259
2260 /*
2261 * Create a VFS object.
2262 */
2263 PRTFSISOFILEOBJ pNewFile;
2264 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2265 phVfsFile, (void **)&pNewFile);
2266 if (RT_SUCCESS(rc))
2267 {
2268 /*
2269 * Look for existing shared object. Make sure it's a file.
2270 */
2271 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
2272 if (pShared)
2273 {
2274 if (!RTFS_IS_FILE(pShared->Core.fAttrib))
2275 {
2276 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2277 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2278 pNewFile->offFile = 0;
2279 pNewFile->pShared = pShared;
2280 return VINF_SUCCESS;
2281 }
2282 }
2283 /*
2284 * Create a shared object for this alleged file.
2285 */
2286 else
2287 {
2288 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2289 if (pShared)
2290 {
2291 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, &pFid->Icb, pFid, offInDir, pThis);
2292 if (RT_SUCCESS(rc))
2293 {
2294 if (RTFS_IS_FILE(pShared->Core.fAttrib))
2295 {
2296 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2297
2298 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2299 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2300 pNewFile->offFile = 0;
2301 pNewFile->pShared = pShared;
2302 return VINF_SUCCESS;
2303 }
2304 rtFsIsoCore_Destroy(&pShared->Core);
2305 }
2306 RTMemFree(pShared);
2307 }
2308 else
2309 rc = VERR_NO_MEMORY;
2310 }
2311
2312 /* Destroy the file object. */
2313 pNewFile->offFile = 0;
2314 pNewFile->pShared = NULL;
2315 RTVfsFileRelease(*phVfsFile);
2316 }
2317 *phVfsFile = NIL_RTVFSFILE;
2318 return rc;
2319}
2320
2321
2322/**
2323 * Looks up the shared structure for a child.
2324 *
2325 * @returns Referenced pointer to the shared structure, NULL if not found.
2326 * @param pThis The directory.
2327 * @param offDirRec The directory record offset of the child.
2328 */
2329static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec)
2330{
2331 PRTFSISOCORE pCur;
2332 RTListForEach(&pThis->OpenChildren, pCur, RTFSISOCORE, Entry)
2333 {
2334 if (pCur->offDirRec == offDirRec)
2335 {
2336 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
2337 Assert(cRefs > 1); RT_NOREF(cRefs);
2338 return pCur;
2339 }
2340 }
2341 return NULL;
2342}
2343
2344
2345#ifdef RT_STRICT
2346/**
2347 * Checks if @a pNext is an extent of @a pFirst.
2348 *
2349 * @returns true if @a pNext is the next extent, false if not
2350 * @param pFirst The directory record describing the first or the
2351 * previous extent.
2352 * @param pNext The directory record alleged to be the next extent.
2353 */
2354DECLINLINE(bool) rtFsIsoDir_Is9660DirRecNextExtent(PCISO9660DIRREC pFirst, PCISO9660DIRREC pNext)
2355{
2356 if (RT_LIKELY(pNext->bFileIdLength == pFirst->bFileIdLength))
2357 {
2358 if (RT_LIKELY((pNext->fFileFlags | ISO9660_FILE_FLAGS_MULTI_EXTENT) == pFirst->fFileFlags))
2359 {
2360 if (RT_LIKELY(memcmp(pNext->achFileId, pFirst->achFileId, pNext->bFileIdLength) == 0))
2361 return true;
2362 }
2363 }
2364 return false;
2365}
2366#endif /* RT_STRICT */
2367
2368
2369/**
2370 * Worker for rtFsIsoDir_FindEntry9660 that compares a UTF-16BE name with a
2371 * directory record.
2372 *
2373 * @returns true if equal, false if not.
2374 * @param pDirRec The directory record.
2375 * @param pwszEntry The UTF-16BE string to compare with.
2376 * @param cbEntry The compare string length in bytes (sans zero
2377 * terminator).
2378 * @param cwcEntry The compare string length in RTUTF16 units.
2379 * @param puVersion Where to return any file version number.
2380 */
2381DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualUtf16Big(PCISO9660DIRREC pDirRec, PCRTUTF16 pwszEntry, size_t cbEntry,
2382 size_t cwcEntry, uint32_t *puVersion)
2383{
2384 /* ASSUME directories cannot have any version tags. */
2385 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
2386 {
2387 if (RT_LIKELY(pDirRec->bFileIdLength != cbEntry))
2388 return false;
2389 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
2390 return false;
2391 }
2392 else
2393 {
2394 size_t cbNameDelta = (size_t)pDirRec->bFileIdLength - cbEntry;
2395 if (RT_LIKELY(cbNameDelta > (size_t)12 /* ;12345 */))
2396 return false;
2397 if (cbNameDelta == 0)
2398 {
2399 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
2400 return false;
2401 *puVersion = 1;
2402 }
2403 else
2404 {
2405 if (RT_LIKELY(RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';'))
2406 return false;
2407 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
2408 return false;
2409 uint32_t uVersion;
2410 size_t cwcVersion = rtFsIso9660GetVersionLengthUtf16Big((PCRTUTF16)pDirRec->achFileId,
2411 pDirRec->bFileIdLength, &uVersion);
2412 if (RT_LIKELY(cwcVersion * sizeof(RTUTF16) == cbNameDelta))
2413 *puVersion = uVersion;
2414 else
2415 return false;
2416 }
2417 }
2418
2419 /* (No need to check for dot and dot-dot here, because cbEntry must be a
2420 multiple of two.) */
2421 Assert(!(cbEntry & 1));
2422 return true;
2423}
2424
2425
2426/**
2427 * Worker for rtFsIsoDir_FindEntry9660 that compares an ASCII name with a
2428 * directory record.
2429 *
2430 * @returns true if equal, false if not.
2431 * @param pDirRec The directory record.
2432 * @param pszEntry The uppercased ASCII string to compare with.
2433 * @param cchEntry The length of the compare string.
2434 * @param puVersion Where to return any file version number.
2435 */
2436DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualAscii(PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cchEntry,
2437 uint32_t *puVersion)
2438{
2439 /* ASSUME directories cannot have any version tags. */
2440 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
2441 {
2442 if (RT_LIKELY(pDirRec->bFileIdLength != cchEntry))
2443 return false;
2444 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
2445 return false;
2446 }
2447 else
2448 {
2449 size_t cchNameDelta = (size_t)pDirRec->bFileIdLength - cchEntry;
2450 if (RT_LIKELY(cchNameDelta > (size_t)6 /* ;12345 */))
2451 return false;
2452 if (cchNameDelta == 0)
2453 {
2454 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
2455 return false;
2456 *puVersion = 1;
2457 }
2458 else
2459 {
2460 if (RT_LIKELY(pDirRec->achFileId[cchEntry] != ';'))
2461 return false;
2462 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
2463 return false;
2464 uint32_t uVersion;
2465 size_t cchVersion = rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion);
2466 if (RT_LIKELY(cchVersion == cchNameDelta))
2467 *puVersion = uVersion;
2468 else
2469 return false;
2470 }
2471 }
2472
2473 /* Don't match the 'dot' and 'dot-dot' directory records. */
2474 if (RT_LIKELY( pDirRec->bFileIdLength != 1
2475 || (uint8_t)pDirRec->achFileId[0] > (uint8_t)0x01))
2476 return true;
2477 return false;
2478}
2479
2480
2481/**
2482 * Locates a directory entry in a directory.
2483 *
2484 * @returns IPRT status code.
2485 * @retval VERR_FILE_NOT_FOUND if not found.
2486 * @param pThis The directory to search.
2487 * @param pszEntry The entry to look for.
2488 * @param poffDirRec Where to return the offset of the directory record
2489 * on the disk.
2490 * @param ppDirRec Where to return the pointer to the directory record
2491 * (the whole directory is buffered).
2492 * @param pcDirRecs Where to return the number of directory records
2493 * related to this entry.
2494 * @param pfMode Where to return the file type, rock ridge adjusted.
2495 * @param puVersion Where to return the file version number.
2496 */
2497static int rtFsIsoDir_FindEntry9660(PRTFSISODIRSHRD pThis, const char *pszEntry, uint64_t *poffDirRec,
2498 PCISO9660DIRREC *ppDirRec, uint32_t *pcDirRecs, PRTFMODE pfMode, uint32_t *puVersion)
2499{
2500 Assert(pThis->Core.pVol->enmType != RTFSISOVOLTYPE_UDF);
2501
2502 /* Set return values. */
2503 *poffDirRec = UINT64_MAX;
2504 *ppDirRec = NULL;
2505 *pcDirRecs = 1;
2506 *pfMode = UINT32_MAX;
2507 *puVersion = 0;
2508
2509 /*
2510 * If we're in UTF-16BE mode, convert the input name to UTF-16BE. Otherwise try
2511 * uppercase it into a ISO 9660 compliant name.
2512 */
2513 int rc;
2514 bool const fIsUtf16 = pThis->Core.pVol->fIsUtf16;
2515 size_t cwcEntry = 0;
2516 size_t cbEntry = 0;
2517 size_t cchUpper = ~(size_t)0;
2518 union
2519 {
2520 RTUTF16 wszEntry[260 + 1];
2521 struct
2522 {
2523 char szUpper[255 + 1];
2524 char szRock[260 + 1];
2525 } s;
2526 } uBuf;
2527 if (fIsUtf16)
2528 {
2529 PRTUTF16 pwszEntry = uBuf.wszEntry;
2530 rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
2531 if (RT_FAILURE(rc))
2532 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
2533 cbEntry = cwcEntry * 2;
2534 }
2535 else
2536 {
2537 rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
2538 if (RT_FAILURE(rc))
2539 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
2540 RTStrToUpper(uBuf.s.szUpper);
2541 cchUpper = strlen(uBuf.s.szUpper);
2542 }
2543
2544 /*
2545 * Scan the directory buffer by buffer.
2546 */
2547 uint32_t offEntryInDir = 0;
2548 uint32_t const cbDir = pThis->Core.cbObject;
2549 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
2550 {
2551 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
2552
2553 /* If null length, skip to the next sector. */
2554 if (pDirRec->cbDirRec == 0)
2555 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
2556 else
2557 {
2558 /* Try match the filename. */
2559 if (fIsUtf16)
2560 {
2561 if (RT_LIKELY(!rtFsIsoDir_IsEntryEqualUtf16Big(pDirRec, uBuf.wszEntry, cbEntry, cwcEntry, puVersion)))
2562 {
2563 /* Advance */
2564 offEntryInDir += pDirRec->cbDirRec;
2565 continue;
2566 }
2567 }
2568 else
2569 {
2570 if (RT_LIKELY(!rtFsIsoDir_IsEntryEqualAscii(pDirRec, uBuf.s.szUpper, cchUpper, puVersion)))
2571 {
2572 /** @todo check rock. */
2573 if (1)
2574 {
2575 /* Advance */
2576 offEntryInDir += pDirRec->cbDirRec;
2577 continue;
2578 }
2579 }
2580 }
2581
2582 *poffDirRec = pThis->Core.FirstExtent.off + offEntryInDir;
2583 *ppDirRec = pDirRec;
2584 *pfMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
2585 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
2586 : 0644 | RTFS_TYPE_FILE;
2587
2588 /*
2589 * Deal with the unlikely scenario of multi extent records.
2590 */
2591 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
2592 *pcDirRecs = 1;
2593 else
2594 {
2595 offEntryInDir += pDirRec->cbDirRec;
2596
2597 uint32_t cDirRecs = 1;
2598 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
2599 {
2600 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
2601 if (pDirRec2->cbDirRec != 0)
2602 {
2603 Assert(rtFsIsoDir_Is9660DirRecNextExtent(pDirRec, pDirRec2));
2604 cDirRecs++;
2605 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
2606 break;
2607 offEntryInDir += pDirRec2->cbDirRec;
2608 }
2609 else
2610 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
2611 }
2612
2613 *pcDirRecs = cDirRecs;
2614 }
2615 return VINF_SUCCESS;
2616 }
2617 }
2618
2619 return VERR_FILE_NOT_FOUND;
2620}
2621
2622
2623/**
2624 * Locates a directory entry in a directory.
2625 *
2626 * @returns IPRT status code.
2627 * @retval VERR_FILE_NOT_FOUND if not found.
2628 * @param pThis The directory to search.
2629 * @param pszEntry The entry to look for.
2630 * @param ppFid Where to return the pointer to the file ID entry.
2631 * (Points to the directory content.)
2632 */
2633static int rtFsIsoDir_FindEntryUdf(PRTFSISODIRSHRD pThis, const char *pszEntry, PCUDFFILEIDDESC *ppFid)
2634{
2635 Assert(pThis->Core.pVol->enmType == RTFSISOVOLTYPE_UDF);
2636 *ppFid = NULL;
2637
2638 /*
2639 * Recode the entry name as 8-bit (if possible) and 16-bit strings.
2640 * This also disposes of entries that definitely are too long.
2641 */
2642 size_t cb8Bit;
2643 bool fSimple;
2644 size_t cb16Bit;
2645 size_t cwc16Bit;
2646 uint8_t ab8Bit[255];
2647 RTUTF16 wsz16Bit[255];
2648
2649 /* 16-bit */
2650 PRTUTF16 pwsz16Bit = wsz16Bit;
2651 int rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwsz16Bit, RT_ELEMENTS(wsz16Bit), &cwc16Bit);
2652 if (RT_SUCCESS(rc))
2653 cb16Bit = 1 + cwc16Bit * sizeof(RTUTF16);
2654 else
2655 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
2656
2657 /* 8-bit (can't possibly overflow) */
2658 fSimple = true;
2659 cb8Bit = 0;
2660 const char *pszSrc = pszEntry;
2661 for (;;)
2662 {
2663 RTUNICP uc;
2664 int rc2 = RTStrGetCpEx(&pszSrc, &uc);
2665 AssertRCReturn(rc2, rc2);
2666 if (uc <= 0x7f)
2667 {
2668 if (uc)
2669 ab8Bit[cb8Bit++] = (uint8_t)uc;
2670 else
2671 break;
2672 }
2673 else if (uc <= 0xff)
2674 {
2675 ab8Bit[cb8Bit++] = (uint8_t)uc;
2676 fSimple = false;
2677 }
2678 else
2679 {
2680 cb8Bit = UINT32_MAX / 2;
2681 break;
2682 }
2683 }
2684 Assert(cb8Bit <= sizeof(ab8Bit) || cb8Bit == UINT32_MAX / 2);
2685 cb8Bit++;
2686
2687 /*
2688 * Scan the directory content.
2689 */
2690 uint32_t offDesc = 0;
2691 uint32_t const cbDir = pThis->Core.cbObject;
2692 while (offDesc + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= cbDir)
2693 {
2694 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
2695 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
2696 if ( offDesc + cbFid <= cbDir
2697 && pFid->Tag.idTag == UDF_TAG_ID_FILE_ID_DESC)
2698 { /* likely */ }
2699 else
2700 break;
2701
2702 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
2703 if (*pbName == 16)
2704 {
2705 if (cb16Bit == pFid->cbName)
2706 {
2707 if (RTUtf16BigNICmp((PCRTUTF16)(&pbName[1]), wsz16Bit, cwc16Bit) == 0)
2708 {
2709 *ppFid = pFid;
2710 return VINF_SUCCESS;
2711 }
2712 }
2713 }
2714 else if (*pbName == 8)
2715 {
2716 if ( cb8Bit == pFid->cbName
2717 && cb8Bit != UINT16_MAX)
2718 {
2719 if (fSimple)
2720 {
2721 if (RTStrNICmp((const char *)&pbName[1], (const char *)ab8Bit, cb8Bit - 1) == 0)
2722 {
2723 *ppFid = pFid;
2724 return VINF_SUCCESS;
2725 }
2726 }
2727 else
2728 {
2729 size_t cch = cb8Bit - 1;
2730 size_t off;
2731 for (off = 0; off < cch; off++)
2732 {
2733 RTUNICP uc1 = ab8Bit[off];
2734 RTUNICP uc2 = pbName[off + 1];
2735 if ( uc1 == uc2
2736 || RTUniCpToLower(uc1) == RTUniCpToLower(uc2)
2737 || RTUniCpToUpper(uc1) == RTUniCpToUpper(uc2))
2738 { /* matches */ }
2739 else
2740 break;
2741 }
2742 if (off == cch)
2743 {
2744 *ppFid = pFid;
2745 return VINF_SUCCESS;
2746 }
2747 }
2748 }
2749 }
2750
2751 /* advance */
2752 offDesc += cbFid;
2753 }
2754
2755 return VERR_FILE_NOT_FOUND;
2756}
2757
2758
2759/**
2760 * Releases a reference to a shared directory structure.
2761 *
2762 * @param pShared The shared directory structure.
2763 */
2764static void rtFsIsoDirShrd_Release(PRTFSISODIRSHRD pShared)
2765{
2766 uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
2767 Assert(cRefs < UINT32_MAX / 2);
2768 if (cRefs == 0)
2769 {
2770 LogFlow(("rtFsIsoDirShrd_Release: Destroying shared structure %p\n", pShared));
2771 Assert(pShared->Core.cRefs == 0);
2772 if (pShared->pbDir)
2773 {
2774 RTMemFree(pShared->pbDir);
2775 pShared->pbDir = NULL;
2776 }
2777 rtFsIsoCore_Destroy(&pShared->Core);
2778 RTMemFree(pShared);
2779 }
2780}
2781
2782
2783/**
2784 * Retains a reference to a shared directory structure.
2785 *
2786 * @param pShared The shared directory structure.
2787 */
2788static void rtFsIsoDirShrd_Retain(PRTFSISODIRSHRD pShared)
2789{
2790 uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
2791 Assert(cRefs > 1); NOREF(cRefs);
2792}
2793
2794
2795
2796/**
2797 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2798 */
2799static DECLCALLBACK(int) rtFsIsoDir_Close(void *pvThis)
2800{
2801 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
2802 LogFlow(("rtFsIsoDir_Close(%p/%p)\n", pThis, pThis->pShared));
2803
2804 PRTFSISODIRSHRD pShared = pThis->pShared;
2805 pThis->pShared = NULL;
2806 if (pShared)
2807 rtFsIsoDirShrd_Release(pShared);
2808 return VINF_SUCCESS;
2809}
2810
2811
2812/**
2813 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2814 */
2815static DECLCALLBACK(int) rtFsIsoDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2816{
2817 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
2818 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
2819}
2820
2821
2822/**
2823 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
2824 */
2825static DECLCALLBACK(int) rtFsIsoDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
2826 uint32_t fFlags, PRTVFSOBJ phVfsObj)
2827{
2828 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
2829 PRTFSISODIRSHRD pShared = pThis->pShared;
2830 int rc;
2831
2832 /*
2833 * We cannot create or replace anything, just open stuff.
2834 */
2835 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
2836 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
2837 { /* likely */ }
2838 else
2839 return VERR_WRITE_PROTECT;
2840
2841 /*
2842 * Special cases '.' and '..'
2843 */
2844 if (pszEntry[0] == '.')
2845 {
2846 PRTFSISODIRSHRD pSharedToOpen;
2847 if (pszEntry[1] == '\0')
2848 pSharedToOpen = pShared;
2849 else if (pszEntry[1] == '.' && pszEntry[2] == '\0')
2850 {
2851 pSharedToOpen = pShared->Core.pParentDir;
2852 if (!pSharedToOpen)
2853 pSharedToOpen = pShared;
2854 }
2855 else
2856 pSharedToOpen = NULL;
2857 if (pSharedToOpen)
2858 {
2859 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
2860 {
2861 rtFsIsoDirShrd_Retain(pSharedToOpen);
2862 RTVFSDIR hVfsDir;
2863 rc = rtFsIsoDir_NewWithShared(pShared->Core.pVol, pSharedToOpen, &hVfsDir);
2864 if (RT_SUCCESS(rc))
2865 {
2866 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2867 RTVfsDirRelease(hVfsDir);
2868 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2869 }
2870 }
2871 else
2872 rc = VERR_IS_A_DIRECTORY;
2873 return rc;
2874 }
2875 }
2876
2877 /*
2878 * Try open whatever it is.
2879 */
2880 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
2881 {
2882
2883 /*
2884 * ISO 9660
2885 */
2886 PCISO9660DIRREC pDirRec;
2887 uint64_t offDirRec;
2888 uint32_t cDirRecs;
2889 RTFMODE fMode;
2890 uint32_t uVersion;
2891 rc = rtFsIsoDir_FindEntry9660(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
2892 Log2(("rtFsIsoDir_Open: FindEntry9660(,%s,) -> %Rrc\n", pszEntry, rc));
2893 if (RT_SUCCESS(rc))
2894 {
2895 switch (fMode & RTFS_TYPE_MASK)
2896 {
2897 case RTFS_TYPE_FILE:
2898 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
2899 {
2900 RTVFSFILE hVfsFile;
2901 rc = rtFsIsoFile_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs,
2902 offDirRec, fOpen, uVersion, &hVfsFile);
2903 if (RT_SUCCESS(rc))
2904 {
2905 *phVfsObj = RTVfsObjFromFile(hVfsFile);
2906 RTVfsFileRelease(hVfsFile);
2907 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2908 }
2909 }
2910 else
2911 rc = VERR_IS_A_FILE;
2912 break;
2913
2914 case RTFS_TYPE_DIRECTORY:
2915 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
2916 {
2917 RTVFSDIR hVfsDir;
2918 rc = rtFsIsoDir_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, &hVfsDir);
2919 if (RT_SUCCESS(rc))
2920 {
2921 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2922 RTVfsDirRelease(hVfsDir);
2923 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2924 }
2925 }
2926 else
2927 rc = VERR_IS_A_DIRECTORY;
2928 break;
2929
2930 case RTFS_TYPE_SYMLINK:
2931 case RTFS_TYPE_DEV_BLOCK:
2932 case RTFS_TYPE_DEV_CHAR:
2933 case RTFS_TYPE_FIFO:
2934 case RTFS_TYPE_SOCKET:
2935 case RTFS_TYPE_WHITEOUT:
2936 rc = VERR_NOT_IMPLEMENTED;
2937 break;
2938
2939 default:
2940 rc = VERR_PATH_NOT_FOUND;
2941 break;
2942 }
2943 }
2944 }
2945 else
2946 {
2947 /*
2948 * UDF
2949 */
2950 PCUDFFILEIDDESC pFid;
2951 rc = rtFsIsoDir_FindEntryUdf(pShared, pszEntry, &pFid);
2952 Log2(("rtFsIsoDir_Open: FindEntryUdf(,%s,) -> %Rrc\n", pszEntry, rc));
2953 if (RT_SUCCESS(rc))
2954 {
2955 if (!(pFid->fFlags & UDF_FILE_FLAGS_DELETED))
2956 {
2957 if (!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY))
2958 {
2959 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
2960 {
2961 RTVFSFILE hVfsFile;
2962 rc = rtFsIsoFile_NewUdf(pShared->Core.pVol, pShared, pFid, fOpen, &hVfsFile);
2963 if (RT_SUCCESS(rc))
2964 {
2965 *phVfsObj = RTVfsObjFromFile(hVfsFile);
2966 RTVfsFileRelease(hVfsFile);
2967 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2968 }
2969 }
2970 else
2971 rc = VERR_IS_A_FILE;
2972 }
2973 else
2974 {
2975 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
2976 {
2977 RTVFSDIR hVfsDir;
2978 rc = rtFsIsoDir_NewUdf(pShared->Core.pVol, pShared, pFid, &hVfsDir);
2979 if (RT_SUCCESS(rc))
2980 {
2981 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2982 RTVfsDirRelease(hVfsDir);
2983 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2984 }
2985 }
2986 else
2987 rc = VERR_IS_A_DIRECTORY;
2988 }
2989 }
2990 /* We treat UDF_FILE_FLAGS_DELETED like RTFS_TYPE_WHITEOUT for now. */
2991 else
2992 rc = VERR_PATH_NOT_FOUND;
2993 }
2994 }
2995 return rc;
2996
2997}
2998
2999
3000/**
3001 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3002 */
3003static DECLCALLBACK(int) rtFsIsoDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3004{
3005 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3006 return VERR_WRITE_PROTECT;
3007}
3008
3009
3010/**
3011 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3012 */
3013static DECLCALLBACK(int) rtFsIsoDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3014{
3015 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3016 return VERR_NOT_SUPPORTED;
3017}
3018
3019
3020/**
3021 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3022 */
3023static DECLCALLBACK(int) rtFsIsoDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3024 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3025{
3026 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3027 return VERR_WRITE_PROTECT;
3028}
3029
3030
3031/**
3032 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3033 */
3034static DECLCALLBACK(int) rtFsIsoDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3035{
3036 RT_NOREF(pvThis, pszEntry, fType);
3037 return VERR_WRITE_PROTECT;
3038}
3039
3040
3041/**
3042 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3043 */
3044static DECLCALLBACK(int) rtFsIsoDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3045{
3046 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3047 return VERR_WRITE_PROTECT;
3048}
3049
3050
3051/**
3052 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3053 */
3054static DECLCALLBACK(int) rtFsIsoDir_RewindDir(void *pvThis)
3055{
3056 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3057 pThis->offDir = 0;
3058 return VINF_SUCCESS;
3059}
3060
3061
3062/**
3063 * The ISO 9660 worker for rtFsIsoDir_ReadDir
3064 */
3065static int rtFsIsoDir_ReadDir9660(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3066 RTFSOBJATTRADD enmAddAttr)
3067{
3068 while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
3069 {
3070 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
3071
3072 /* If null length, skip to the next sector. */
3073 if (pDirRec->cbDirRec == 0)
3074 pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
3075 else
3076 {
3077 /*
3078 * Do names first as they may cause overflows.
3079 */
3080 uint32_t uVersion = 0;
3081 if ( pDirRec->bFileIdLength == 1
3082 && pDirRec->achFileId[0] == '\0')
3083 {
3084 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
3085 {
3086 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
3087 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot)\n"));
3088 return VERR_BUFFER_OVERFLOW;
3089 }
3090 pDirEntry->cbName = 1;
3091 pDirEntry->szName[0] = '.';
3092 pDirEntry->szName[1] = '\0';
3093 }
3094 else if ( pDirRec->bFileIdLength == 1
3095 && pDirRec->achFileId[0] == '\1')
3096 {
3097 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
3098 {
3099 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
3100 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
3101 return VERR_BUFFER_OVERFLOW;
3102 }
3103 pDirEntry->cbName = 2;
3104 pDirEntry->szName[0] = '.';
3105 pDirEntry->szName[1] = '.';
3106 pDirEntry->szName[2] = '\0';
3107 }
3108 else if (pShared->Core.pVol->fIsUtf16)
3109 {
3110 PCRTUTF16 pawcSrc = (PCRTUTF16)&pDirRec->achFileId[0];
3111 size_t cwcSrc = pDirRec->bFileIdLength / sizeof(RTUTF16);
3112 size_t cwcVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3113 ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
3114 size_t cchNeeded = 0;
3115 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
3116 char *pszDst = pDirEntry->szName;
3117
3118 int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
3119 if (RT_SUCCESS(rc))
3120 pDirEntry->cbName = (uint16_t)cchNeeded;
3121 else if (rc == VERR_BUFFER_OVERFLOW)
3122 {
3123 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
3124 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
3125 return VERR_BUFFER_OVERFLOW;
3126 }
3127 else
3128 {
3129 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
3130 if (cchNeeded2 >= 0)
3131 pDirEntry->cbName = (uint16_t)cchNeeded2;
3132 else
3133 {
3134 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
3135 return VERR_BUFFER_OVERFLOW;
3136 }
3137 }
3138 }
3139 else
3140 {
3141 /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
3142 size_t cchVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3143 ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
3144 size_t cchName = pDirRec->bFileIdLength - cchVer;
3145 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
3146 if (*pcbDirEntry < cbNeeded)
3147 {
3148 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
3149 *pcbDirEntry = cbNeeded;
3150 return VERR_BUFFER_OVERFLOW;
3151 }
3152 pDirEntry->cbName = (uint16_t)cchName;
3153 memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
3154 pDirEntry->szName[cchName] = '\0';
3155 RTStrPurgeEncoding(pDirEntry->szName);
3156
3157 /** @todo check for rock ridge names here. */
3158 }
3159 pDirEntry->cwcShortName = 0;
3160 pDirEntry->wszShortName[0] = '\0';
3161
3162 /*
3163 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
3164 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3165 */
3166 RTFSISOCORE TmpObj;
3167 RT_ZERO(TmpObj);
3168 rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
3169 pThis->offDir + pShared->Core.FirstExtent.off, uVersion, pShared->Core.pVol);
3170 int rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
3171
3172 /*
3173 * Update the directory location and handle multi extent records.
3174 *
3175 * Multi extent records only affect the file size and the directory location,
3176 * so we deal with it here instead of involving * rtFsIsoCore_InitFrom9660DirRec
3177 * which would potentially require freeing memory and such.
3178 */
3179 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3180 {
3181 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3182 pThis->offDir += pDirRec->cbDirRec;
3183 }
3184 else
3185 {
3186 uint32_t cExtents = 1;
3187 uint32_t offDir = pThis->offDir + pDirRec->cbDirRec;
3188 while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
3189 {
3190 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
3191 if (pDirRec2->cbDirRec != 0)
3192 {
3193 pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
3194 offDir += pDirRec2->cbDirRec;
3195 cExtents++;
3196 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3197 break;
3198 }
3199 else
3200 offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
3201 }
3202 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
3203 pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
3204 pThis->offDir = offDir;
3205 }
3206
3207 return rc;
3208 }
3209 }
3210
3211 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
3212 return VERR_NO_MORE_FILES;
3213}
3214
3215
3216/**
3217 * The UDF worker for rtFsIsoDir_ReadDir
3218 */
3219static int rtFsIsoDir_ReadDirUdf(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3220 RTFSOBJATTRADD enmAddAttr)
3221{
3222 /*
3223 * At offset zero we've got the '.' entry. This has to be generated
3224 * manually as it's not part of the directory content. The directory
3225 * offset has to be faked for this too, so offDir == 0 indicates the '.'
3226 * entry whereas offDir == 1 is the first file id descriptor.
3227 */
3228 if (pThis->offDir == 0)
3229 {
3230 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
3231 {
3232 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
3233 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW (dot)\n"));
3234 return VERR_BUFFER_OVERFLOW;
3235 }
3236 pDirEntry->cbName = 1;
3237 pDirEntry->szName[0] = '.';
3238 pDirEntry->szName[1] = '\0';
3239 pDirEntry->cwcShortName = 0;
3240 pDirEntry->wszShortName[0] = '\0';
3241
3242 int rc = rtFsIsoCore_QueryInfo(&pShared->Core, &pDirEntry->Info, enmAddAttr);
3243
3244 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3245 pThis->offDir = 1;
3246 return rc;
3247 }
3248
3249 /*
3250 * Do the directory content.
3251 */
3252 while (pThis->offDir + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= pShared->cbDir + 1)
3253 {
3254 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pShared->pbDir[pThis->offDir - 1];
3255 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3256
3257 if (pThis->offDir + cbFid <= pShared->cbDir + 1)
3258 { /* likely */ }
3259 else
3260 break;
3261
3262 /*
3263 * Do names first as they may cause overflows.
3264 */
3265 if (pFid->cbName > 1)
3266 {
3267 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3268 uint32_t cbSrc = pFid->cbName;
3269 if (*pbName == 8)
3270 {
3271 /* Figure out the UTF-8 length first. */
3272 bool fSimple = true;
3273 uint32_t cchDst = 0;
3274 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
3275 if (!(pbName[offSrc] & 0x80))
3276 cchDst++;
3277 else
3278 {
3279 cchDst += 2;
3280 fSimple = false;
3281 }
3282
3283 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchDst + 1;
3284 if (*pcbDirEntry >= cbNeeded)
3285 {
3286 if (fSimple)
3287 {
3288 Assert(cbSrc - 1 == cchDst);
3289 memcpy(pDirEntry->szName, &pbName[1], cchDst);
3290 pDirEntry->szName[cchDst] = '\0';
3291 }
3292 else
3293 {
3294 char *pszDst = pDirEntry->szName;
3295 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
3296 pszDst = RTStrPutCp(pszDst, pbName[offSrc]);
3297 *pszDst = '\0';
3298 Assert((size_t)(pszDst - &pDirEntry->szName[0]) == cchDst);
3299 }
3300 }
3301 else
3302 {
3303 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (8-bit)\n", *pcbDirEntry, cbNeeded));
3304 *pcbDirEntry = cbNeeded;
3305 return VERR_BUFFER_OVERFLOW;
3306 }
3307 }
3308 else
3309 {
3310 /* Let RTUtf16BigToUtf8Ex do the bounds checking. */
3311 char *pszDst = pDirEntry->szName;
3312 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
3313 size_t cchNeeded = 0;
3314 int rc;
3315 if (*pbName == 16)
3316 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)(pbName + 1), (cbSrc - 1) / sizeof(RTUTF16), &pszDst, cbDst, &cchNeeded);
3317 else
3318 rc = VERR_INVALID_NAME;
3319 if (RT_SUCCESS(rc))
3320 pDirEntry->cbName = (uint16_t)cchNeeded;
3321 else if (rc == VERR_BUFFER_OVERFLOW)
3322 {
3323 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
3324 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (16-bit)\n", cbDst, cchNeeded));
3325 return VERR_BUFFER_OVERFLOW;
3326 }
3327 else
3328 {
3329 LogRelMax(90, ("ISO/UDF: Malformed directory entry name at %#x: %.*Rhxs\n", pThis->offDir - 1, cbSrc, pbName));
3330 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir - 1);
3331 if (cchNeeded2 >= 0)
3332 pDirEntry->cbName = (uint16_t)cchNeeded2;
3333 else
3334 {
3335 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
3336 return VERR_BUFFER_OVERFLOW;
3337 }
3338 }
3339 }
3340 }
3341 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
3342 {
3343 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2 + 1;
3344 if (*pcbDirEntry < cbNeeded)
3345 {
3346 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (dot-dot)\n", *pcbDirEntry, cbNeeded));
3347 *pcbDirEntry = cbNeeded;
3348 return VERR_BUFFER_OVERFLOW;
3349 }
3350 pDirEntry->cbName = 2;
3351 pDirEntry->szName[0] = '.';
3352 pDirEntry->szName[1] = '.';
3353 pDirEntry->szName[2] = '\0';
3354 }
3355 else
3356 {
3357 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 1;
3358 if (*pcbDirEntry < cbNeeded)
3359 {
3360 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (empty)\n", *pcbDirEntry, cbNeeded));
3361 *pcbDirEntry = cbNeeded;
3362 return VERR_BUFFER_OVERFLOW;
3363 }
3364 pDirEntry->cbName = 0;
3365 pDirEntry->szName[0] = '\0';
3366 }
3367
3368 pDirEntry->cwcShortName = 0;
3369 pDirEntry->wszShortName[0] = '\0';
3370
3371 /*
3372 * To avoid duplicating code in rtFsIsoCore_InitUdf and
3373 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3374 */
3375 RTFSISOCORE TmpObj;
3376 RT_ZERO(TmpObj);
3377 int rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&TmpObj, &pFid->Icb, pFid, pThis->offDir - 1, pShared->Core.pVol);
3378 if (RT_SUCCESS(rc))
3379 {
3380 rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
3381 rtFsIsoCore_Destroy(&TmpObj);
3382 }
3383
3384 /*
3385 * Update.
3386 */
3387 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3388 pThis->offDir += cbFid;
3389
3390 return rc;
3391 }
3392
3393 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
3394 return VERR_NO_MORE_FILES;
3395}
3396
3397
3398/**
3399 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3400 */
3401static DECLCALLBACK(int) rtFsIsoDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3402 RTFSOBJATTRADD enmAddAttr)
3403{
3404 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3405 PRTFSISODIRSHRD pShared = pThis->pShared;
3406 int rc;
3407 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3408 rc = rtFsIsoDir_ReadDir9660(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
3409 else
3410 rc = rtFsIsoDir_ReadDirUdf(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
3411 return rc;
3412}
3413
3414
3415/**
3416 * ISO file operations.
3417 */
3418static const RTVFSDIROPS g_rtFsIsoDirOps =
3419{
3420 { /* Obj */
3421 RTVFSOBJOPS_VERSION,
3422 RTVFSOBJTYPE_DIR,
3423 "ISO 9660 Dir",
3424 rtFsIsoDir_Close,
3425 rtFsIsoDir_QueryInfo,
3426 RTVFSOBJOPS_VERSION
3427 },
3428 RTVFSDIROPS_VERSION,
3429 0,
3430 { /* ObjSet */
3431 RTVFSOBJSETOPS_VERSION,
3432 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
3433 NULL /*SetMode*/,
3434 NULL /*SetTimes*/,
3435 NULL /*SetOwner*/,
3436 RTVFSOBJSETOPS_VERSION
3437 },
3438 rtFsIsoDir_Open,
3439 NULL /* pfnFollowAbsoluteSymlink */,
3440 NULL /* pfnOpenFile */,
3441 NULL /* pfnOpenDir */,
3442 rtFsIsoDir_CreateDir,
3443 rtFsIsoDir_OpenSymlink,
3444 rtFsIsoDir_CreateSymlink,
3445 NULL /* pfnQueryEntryInfo */,
3446 rtFsIsoDir_UnlinkEntry,
3447 rtFsIsoDir_RenameEntry,
3448 rtFsIsoDir_RewindDir,
3449 rtFsIsoDir_ReadDir,
3450 RTVFSDIROPS_VERSION,
3451};
3452
3453
3454/**
3455 * Adds an open child to the parent directory's shared structure.
3456 *
3457 * Maintains an additional reference to the parent dir to prevent it from going
3458 * away. If @a pDir is the root directory, it also ensures the volume is
3459 * referenced and sticks around until the last open object is gone.
3460 *
3461 * @param pDir The directory.
3462 * @param pChild The child being opened.
3463 * @sa rtFsIsoDirShrd_RemoveOpenChild
3464 */
3465static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
3466{
3467 rtFsIsoDirShrd_Retain(pDir);
3468
3469 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
3470 pChild->pParentDir = pDir;
3471}
3472
3473
3474/**
3475 * Removes an open child to the parent directory.
3476 *
3477 * @param pDir The directory.
3478 * @param pChild The child being removed.
3479 *
3480 * @remarks This is the very last thing you do as it may cause a few other
3481 * objects to be released recursively (parent dir and the volume).
3482 *
3483 * @sa rtFsIsoDirShrd_AddOpenChild
3484 */
3485static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
3486{
3487 AssertReturnVoid(pChild->pParentDir == pDir);
3488 RTListNodeRemove(&pChild->Entry);
3489 pChild->pParentDir = NULL;
3490
3491 rtFsIsoDirShrd_Release(pDir);
3492}
3493
3494
3495#ifdef LOG_ENABLED
3496/**
3497 * Logs the content of a directory.
3498 */
3499static void rtFsIsoDirShrd_Log9660Content(PRTFSISODIRSHRD pThis)
3500{
3501 if (LogIs2Enabled())
3502 {
3503 uint32_t offRec = 0;
3504 while (offRec < pThis->cbDir)
3505 {
3506 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
3507 if (pDirRec->cbDirRec == 0)
3508 break;
3509
3510 RTUTF16 wszName[128];
3511 if (pThis->Core.pVol->fIsUtf16)
3512 {
3513 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
3514 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
3515 pwszSrc--;
3516 *pwszDst-- = '\0';
3517 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
3518 {
3519 *pwszDst = RT_BE2H_U16(*pwszSrc);
3520 pwszDst--;
3521 pwszSrc--;
3522 }
3523 }
3524 else
3525 {
3526 PRTUTF16 pwszDst = wszName;
3527 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
3528 *pwszDst++ = pDirRec->achFileId[off];
3529 *pwszDst = '\0';
3530 }
3531
3532 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",
3533 offRec,
3534 pDirRec->cbDirRec,
3535 pDirRec->cExtAttrBlocks,
3536 ISO9660_GET_ENDIAN(&pDirRec->cbData),
3537 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
3538 pDirRec->fFileFlags,
3539 pDirRec->RecTime.bYear + 1900,
3540 pDirRec->RecTime.bMonth,
3541 pDirRec->RecTime.bDay,
3542 pDirRec->RecTime.bHour,
3543 pDirRec->RecTime.bMinute,
3544 pDirRec->RecTime.bSecond,
3545 pDirRec->RecTime.offUtc*4/60,
3546 pDirRec->bFileUnitSize,
3547 pDirRec->bInterleaveGapSize,
3548 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
3549 wszName));
3550
3551 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
3552 if (offSysUse < pDirRec->cbDirRec)
3553 {
3554 Log2(("ISO9660: system use (%#x bytes):\n%.*RhxD\n", pDirRec->cbDirRec - offSysUse,
3555 pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
3556 }
3557
3558 /* advance */
3559 offRec += pDirRec->cbDirRec;
3560 }
3561 }
3562}
3563#endif /* LOG_ENABLED */
3564
3565
3566/**
3567 * Instantiates a new shared directory structure, given 9660 records.
3568 *
3569 * @returns IPRT status code.
3570 * @param pThis The ISO volume instance.
3571 * @param pParentDir The parent directory. This is NULL for the root
3572 * directory.
3573 * @param pDirRec The directory record. Will access @a cDirRecs
3574 * records.
3575 * @param cDirRecs Number of directory records if more than one.
3576 * @param offDirRec The byte offset of the directory record.
3577 * @param ppShared Where to return the shared directory structure.
3578 */
3579static int rtFsIsoDirShrd_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
3580 uint32_t cDirRecs, uint64_t offDirRec, PRTFSISODIRSHRD *ppShared)
3581{
3582 /*
3583 * Allocate a new structure and initialize it.
3584 */
3585 int rc = VERR_NO_MEMORY;
3586 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
3587 if (pShared)
3588 {
3589 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pThis);
3590 if (RT_SUCCESS(rc))
3591 {
3592 RTListInit(&pShared->OpenChildren);
3593 pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
3594 pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
3595 if (pShared->pbDir)
3596 {
3597 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.off, pShared->pbDir, pShared->cbDir, NULL);
3598 if (RT_SUCCESS(rc))
3599 {
3600#ifdef LOG_ENABLED
3601 rtFsIsoDirShrd_Log9660Content(pShared);
3602#endif
3603
3604 /*
3605 * Link into parent directory so we can use it to update
3606 * our directory entry.
3607 */
3608 if (pParentDir)
3609 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
3610 *ppShared = pShared;
3611 return VINF_SUCCESS;
3612 }
3613 }
3614 else
3615 rc = VERR_NO_MEMORY;
3616 }
3617 RTMemFree(pShared);
3618 }
3619 *ppShared = NULL;
3620 return rc;
3621}
3622
3623
3624#ifdef LOG_ENABLED
3625/**
3626 * Logs the content of a directory.
3627 */
3628static void rtFsIsoDirShrd_LogUdfContent(PRTFSISODIRSHRD pThis)
3629{
3630 if (LogIs2Enabled())
3631 {
3632 uint32_t offDesc = 0;
3633 while (offDesc + RT_OFFSETOF(UDFFILEIDDESC, abImplementationUse) < pThis->cbDir)
3634 {
3635 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
3636 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3637 if (offDesc + cbFid > pThis->cbDir)
3638 break;
3639
3640 uint32_t cwcName = 0;
3641 RTUTF16 wszName[260];
3642 if (pFid->cbName > 0)
3643 {
3644 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3645 uint32_t offSrc = 1;
3646 if (*pbName == 8)
3647 while (offSrc < pFid->cbName)
3648 {
3649 wszName[cwcName] = pbName[offSrc];
3650 cwcName++;
3651 offSrc++;
3652 }
3653 else if (*pbName == 16)
3654 while (offSrc + 1 <= pFid->cbName)
3655 {
3656 wszName[cwcName] = RT_MAKE_U16(pbName[offSrc + 1], pbName[offSrc]);
3657 cwcName++;
3658 offSrc += 2;
3659 }
3660 else
3661 {
3662 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<bad type>");
3663 cwcName = 10;
3664 }
3665 }
3666 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
3667 {
3668 wszName[0] = '.';
3669 wszName[1] = '.';
3670 cwcName = 2;
3671 }
3672 else
3673 {
3674 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<empty>");
3675 cwcName = 7;
3676 }
3677 wszName[cwcName] = '\0';
3678
3679 Log2(("ISO/UDF: %04x: fFlags=%#x uVer=%u Icb={%#04x:%#010RX32 LB %#06x t=%u} cbName=%#04x cbIU=%#x '%ls'\n",
3680 offDesc,
3681 pFid->fFlags,
3682 pFid->uVersion,
3683 pFid->Icb.Location.uPartitionNo,
3684 pFid->Icb.Location.off,
3685 pFid->Icb.cb,
3686 pFid->Icb.uType,
3687 pFid->cbName,
3688 pFid->cbImplementationUse,
3689 wszName));
3690 int rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFid->Tag, pThis->cbDir - offDesc,
3691 UDF_TAG_ID_FILE_ID_DESC, pFid->Tag.offTag, NULL);
3692 if (RT_FAILURE(rc))
3693 Log2(("ISO/UDF: Bad Tag: %Rrc - idTag=%#x\n", rc, pFid->Tag.idTag));
3694 if (pFid->cbImplementationUse > 32)
3695 Log2(("ISO/UDF: impl use (%#x bytes):\n%.*RhxD\n",
3696 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
3697 else if (pFid->cbImplementationUse > 0)
3698 Log2(("ISO/UDF: impl use (%#x bytes): %.*Rhxs\n",
3699 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
3700
3701 /* advance */
3702 offDesc += cbFid;
3703 }
3704
3705 if (offDesc < pThis->cbDir)
3706 Log2(("ISO/UDF: warning! %#x trailing bytes in directory:\n%.*RhxD\n",
3707 pThis->cbDir - offDesc, pThis->cbDir - offDesc, &pThis->pbDir[offDesc]));
3708 }
3709}
3710#endif /* LOG_ENABLED */
3711
3712
3713/**
3714 * Instantiates a new shared directory structure, given UDF descriptors.
3715 *
3716 * @returns IPRT status code.
3717 * @param pThis The ISO volume instance.
3718 * @param pParentDir The parent directory. This is NULL for the root
3719 * directory.
3720 * @param pAllocDesc The allocation descriptor for the directory ICB.
3721 * @param pFileIdDesc The file ID descriptor. This is NULL for the root.
3722 * @param offInDir The offset of the file ID descriptor in the parent
3723 * directory. This is used when looking up shared
3724 * directory objects. (Pass 0 for root.)
3725 * @param ppShared Where to return the shared directory structure.
3726 */
3727static int rtFsIsoDirShrd_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFLONGAD pAllocDesc,
3728 PCUDFFILEIDDESC pFileIdDesc, uintptr_t offInDir, PRTFSISODIRSHRD *ppShared)
3729{
3730 /*
3731 * Allocate a new structure and initialize it.
3732 */
3733 int rc = VERR_NO_MEMORY;
3734 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
3735 if (pShared)
3736 {
3737 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, pAllocDesc, pFileIdDesc, offInDir, pThis);
3738 if (RT_SUCCESS(rc))
3739 {
3740 RTListInit(&pShared->OpenChildren);
3741
3742 if (pShared->Core.cbObject < RTFSISO_MAX_DIR_SIZE)
3743 {
3744 pShared->cbDir = (uint32_t)pShared->Core.cbObject;
3745 pShared->pbDir = (uint8_t *)RTMemAllocZ(RT_MAX(RT_ALIGN_32(pShared->cbDir, 512), 512));
3746 if (pShared->pbDir)
3747 {
3748 rc = rtFsIsoCore_ReadWorker(&pShared->Core, 0, pShared->pbDir, pShared->cbDir, NULL, NULL);
3749 if (RT_SUCCESS(rc))
3750 {
3751#ifdef LOG_ENABLED
3752 rtFsIsoDirShrd_LogUdfContent(pShared);
3753#endif
3754
3755 /*
3756 * Link into parent directory so we can use it to update
3757 * our directory entry.
3758 */
3759 if (pParentDir)
3760 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
3761 *ppShared = pShared;
3762 return VINF_SUCCESS;
3763 }
3764 }
3765 else
3766 rc = VERR_NO_MEMORY;
3767 }
3768 }
3769 RTMemFree(pShared);
3770 }
3771
3772 *ppShared = NULL;
3773 return rc;
3774}
3775
3776
3777/**
3778 * Instantiates a new directory with a shared structure presupplied.
3779 *
3780 * @returns IPRT status code.
3781 * @param pThis The ISO volume instance.
3782 * @param pShared Referenced pointer to the shared structure. The
3783 * reference is always CONSUMED.
3784 * @param phVfsDir Where to return the directory handle.
3785 */
3786static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir)
3787{
3788 /*
3789 * Create VFS object around the shared structure.
3790 */
3791 PRTFSISODIROBJ pNewDir;
3792 int rc = RTVfsNewDir(&g_rtFsIsoDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
3793 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
3794 if (RT_SUCCESS(rc))
3795 {
3796 /*
3797 * Look for existing shared object, create a new one if necessary.
3798 * We CONSUME a reference to pShared here.
3799 */
3800 pNewDir->offDir = 0;
3801 pNewDir->pShared = pShared;
3802 return VINF_SUCCESS;
3803 }
3804
3805 rtFsIsoDirShrd_Release(pShared);
3806 *phVfsDir = NIL_RTVFSDIR;
3807 return rc;
3808}
3809
3810
3811
3812/**
3813 * Instantiates a new directory VFS instance for ISO 9660, creating the shared
3814 * structure as necessary.
3815 *
3816 * @returns IPRT status code.
3817 * @param pThis The ISO volume instance.
3818 * @param pParentDir The parent directory. This is NULL for the root
3819 * directory.
3820 * @param pDirRec The directory record.
3821 * @param cDirRecs Number of directory records if more than one.
3822 * @param offDirRec The byte offset of the directory record.
3823 * @param phVfsDir Where to return the directory handle.
3824 */
3825static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
3826 uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir)
3827{
3828 /*
3829 * Look for existing shared object, create a new one if necessary.
3830 */
3831 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
3832 if (!pShared)
3833 {
3834 int rc = rtFsIsoDirShrd_New9660(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, &pShared);
3835 if (RT_FAILURE(rc))
3836 {
3837 *phVfsDir = NIL_RTVFSDIR;
3838 return rc;
3839 }
3840 }
3841 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
3842}
3843
3844
3845/**
3846 * Instantiates a new directory VFS instance for UDF, creating the shared
3847 * structure as necessary.
3848 *
3849 * @returns IPRT status code.
3850 * @param pThis The ISO volume instance.
3851 * @param pParentDir The parent directory.
3852 * @param pFid The file ID descriptor for the directory.
3853 * @param phVfsDir Where to return the directory handle.
3854 */
3855static int rtFsIsoDir_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid, PRTVFSDIR phVfsDir)
3856{
3857 Assert(pFid);
3858 Assert(pParentDir);
3859 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
3860 Assert(offInDir < pParentDir->cbDir);
3861
3862 /*
3863 * Look for existing shared object, create a new one if necessary.
3864 */
3865 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
3866 if (!pShared)
3867 {
3868 int rc = rtFsIsoDirShrd_NewUdf(pThis, pParentDir, &pFid->Icb, pFid, offInDir, &pShared);
3869 if (RT_FAILURE(rc))
3870 {
3871 *phVfsDir = NIL_RTVFSDIR;
3872 return rc;
3873 }
3874 }
3875 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
3876}
3877
3878
3879/**
3880 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
3881 */
3882static DECLCALLBACK(int) rtFsIsoVol_Close(void *pvThis)
3883{
3884 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
3885 Log(("rtFsIsoVol_Close(%p)\n", pThis));
3886
3887 if (pThis->pRootDir)
3888 {
3889 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
3890 Assert(pThis->pRootDir->Core.cRefs == 1);
3891 rtFsIsoDirShrd_Release(pThis->pRootDir);
3892 pThis->pRootDir = NULL;
3893 }
3894
3895 RTVfsFileRelease(pThis->hVfsBacking);
3896 pThis->hVfsBacking = NIL_RTVFSFILE;
3897
3898 return VINF_SUCCESS;
3899}
3900
3901
3902/**
3903 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
3904 */
3905static DECLCALLBACK(int) rtFsIsoVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3906{
3907 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
3908 return VERR_WRONG_TYPE;
3909}
3910
3911
3912/**
3913 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
3914 */
3915static DECLCALLBACK(int) rtFsIsoVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
3916{
3917 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
3918
3919 rtFsIsoDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
3920 return rtFsIsoDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
3921}
3922
3923
3924/**
3925 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
3926 */
3927static DECLCALLBACK(int) rtFsIsoVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
3928{
3929 RT_NOREF(pvThis, off, cb, pfUsed);
3930 return VERR_NOT_IMPLEMENTED;
3931}
3932
3933
3934DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIsoVolOps =
3935{
3936 { /* Obj */
3937 RTVFSOBJOPS_VERSION,
3938 RTVFSOBJTYPE_VFS,
3939 "ISO 9660/UDF",
3940 rtFsIsoVol_Close,
3941 rtFsIsoVol_QueryInfo,
3942 RTVFSOBJOPS_VERSION
3943 },
3944 RTVFSOPS_VERSION,
3945 0 /* fFeatures */,
3946 rtFsIsoVol_OpenRoot,
3947 rtFsIsoVol_QueryRangeState,
3948 RTVFSOPS_VERSION
3949};
3950
3951
3952/**
3953 * Checks the descriptor tag and CRC.
3954 *
3955 * @retval IPRT status code.
3956 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
3957 * @retval VERR_MISMATCH
3958 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
3959 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
3960 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
3961 *
3962 * @param pTag The tag to check.
3963 * @param idTag The expected descriptor tag ID, UINT16_MAX matches any
3964 * tag ID.
3965 * @param offTag The sector offset of the tag.
3966 * @param pErrInfo Where to return extended error info.
3967 */
3968static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
3969{
3970 /*
3971 * Checksum the tag first.
3972 */
3973 const uint8_t *pbTag = (const uint8_t *)pTag;
3974 uint8_t const bChecksum = pbTag[0]
3975 + pbTag[1]
3976 + pbTag[2]
3977 + pbTag[3]
3978 + pbTag[5] /* skipping byte 4 as that's the checksum. */
3979 + pbTag[6]
3980 + pbTag[7]
3981 + pbTag[8]
3982 + pbTag[9]
3983 + pbTag[10]
3984 + pbTag[11]
3985 + pbTag[12]
3986 + pbTag[13]
3987 + pbTag[14]
3988 + pbTag[15];
3989 if (pTag->uChecksum == bChecksum)
3990 {
3991 /*
3992 * Do the matching.
3993 */
3994 if ( pTag->uVersion == 3
3995 || pTag->uVersion == 2)
3996 {
3997 if ( pTag->idTag == idTag
3998 || idTag == UINT16_MAX)
3999 {
4000 if (pTag->offTag == offTag)
4001 {
4002 //Log3(("ISO/UDF: Valid descriptor %#06x at %#010RX32; cbDescriptorCrc=%#06RX32 uTagSerialNo=%#x\n",
4003 // pTag->idTag, offTag, pTag->cbDescriptorCrc, pTag->uTagSerialNo));
4004 return VINF_SUCCESS;
4005 }
4006
4007 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Sector mismatch: %#RX32 (%.*Rhxs)\n",
4008 idTag, offTag, pTag->offTag, sizeof(*pTag), pTag));
4009 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TAG_SECTOR_MISMATCH,
4010 "Descriptor tag sector number mismatch: %#x, expected %#x (%.*Rhxs)",
4011 pTag->offTag, offTag, sizeof(*pTag), pTag);
4012 }
4013 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Tag ID mismatch: %#x (%.*Rhxs)\n",
4014 idTag, offTag, pTag->idTag, sizeof(*pTag), pTag));
4015 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_MISMATCH, "Descriptor tag ID mismatch: %#x, expected %#x (%.*Rhxs)",
4016 pTag->idTag, idTag, sizeof(*pTag), pTag);
4017 }
4018 if (ASMMemIsZero(pTag, sizeof(*pTag)))
4019 {
4020 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): All zeros\n", idTag, offTag));
4021 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TAG_IS_ALL_ZEROS, "Descriptor is all zeros");
4022 }
4023
4024 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Unsupported version: %#x (%.*Rhxs)\n",
4025 idTag, offTag, pTag->uVersion, sizeof(*pTag), pTag));
4026 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_TAG_VERSION, "Unsupported descriptor tag version: %#x, expected 2 or 3 (%.*Rhxs)",
4027 pTag->uVersion, sizeof(*pTag), pTag);
4028 }
4029 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): checksum error: %#x, calc %#x (%.*Rhxs)\n",
4030 idTag, offTag, pTag->uChecksum, bChecksum, sizeof(*pTag), pTag));
4031 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_TAG_CHECKSUM,
4032 "Descriptor tag checksum error: %#x, calculated %#x (%.*Rhxs)",
4033 pTag->uChecksum, bChecksum, sizeof(*pTag), pTag);
4034}
4035
4036
4037/**
4038 * Checks the descriptor CRC.
4039 *
4040 * @retval VINF_SUCCESS
4041 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4042 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
4043 *
4044 * @param pTag The descriptor buffer to checksum.
4045 * @param cbDesc The size of the descriptor buffer.
4046 * @param pErrInfo Where to return extended error info.
4047 */
4048static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo)
4049{
4050 if (pTag->cbDescriptorCrc + sizeof(*pTag) <= cbDesc)
4051 {
4052 uint16_t uCrc = RTCrc16Ccitt(pTag + 1, pTag->cbDescriptorCrc);
4053 if (pTag->uDescriptorCrc == uCrc)
4054 return VINF_SUCCESS;
4055
4056 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Descriptor CRC mismatch: expected %#x, calculated %#x (cbDescriptorCrc=%#x)\n",
4057 pTag->idTag, pTag->offTag, pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc));
4058 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_DESC_CRC_MISMATCH,
4059 "Descriptor CRC mismatch: exepcted %#x, calculated %#x (cbDescriptor=%#x, idTag=%#x, offTag=%#010RX32)",
4060 pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc, pTag->idTag, pTag->offTag);
4061 }
4062
4063 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx\n",
4064 pTag->idTag, pTag->offTag, pTag->cbDescriptorCrc, cbDesc));
4065 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC,
4066 "Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx (idTag=%#x, offTag=%#010RX32)",
4067 pTag->cbDescriptorCrc, cbDesc, pTag->idTag, pTag->offTag);
4068}
4069
4070
4071/**
4072 * Checks the descriptor tag and CRC.
4073 *
4074 * @retval VINF_SUCCESS
4075 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4076 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
4077 * @retval VERR_MISMATCH
4078 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
4079 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
4080 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
4081 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
4082 *
4083 * @param pTag The descriptor buffer to check the tag of and to
4084 * checksum.
4085 * @param cbDesc The size of the descriptor buffer.
4086 * @param idTag The expected descriptor tag ID, UINT16_MAX
4087 * matches any tag ID.
4088 * @param offTag The sector offset of the tag.
4089 * @param pErrInfo Where to return extended error info.
4090 */
4091static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
4092{
4093 int rc = rtFsIsoVolValidateUdfDescTag(pTag, idTag, offTag, pErrInfo);
4094 if (RT_SUCCESS(rc))
4095 rc = rtFsIsoVolValidateUdfDescCrc(pTag, cbDesc, pErrInfo);
4096 return rc;
4097}
4098
4099
4100
4101
4102static int rtFsIsoVolProcessUdfFileSetDescs(PRTFSISOVOL pThis, uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
4103{
4104
4105 /*
4106 * We assume there is a single file descriptor and don't bother checking what comes next.
4107 */
4108 PUDFFILESETDESC pFsd = (PUDFFILESETDESC)pbBuf;
4109 Assert(cbBuf > sizeof(*pFsd)); NOREF(cbBuf);
4110 RT_ZERO(*pFsd);
4111 size_t cbToRead = RT_MAX(pThis->Udf.VolInfo.FileSetDescriptor.cb, sizeof(*pFsd));
4112 int rc = rtFsIsoVolUdfVpRead(pThis, pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
4113 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, 0, pFsd, cbToRead);
4114 if (RT_SUCCESS(rc))
4115 {
4116 rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFsd->Tag, cbToRead, UDF_TAG_ID_FILE_SET_DESC,
4117 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, pErrInfo);
4118 if (RT_SUCCESS(rc))
4119 {
4120#ifdef LOG_ENABLED
4121 Log(("ISO/UDF: File set descriptor at %#RX32 (%#RX32:%#RX32)\n", pFsd->Tag.offTag,
4122 pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
4123 pThis->Udf.VolInfo.FileSetDescriptor.Location.off));
4124 if (LogIs2Enabled())
4125 {
4126 UDF_LOG2_MEMBER_TIMESTAMP(pFsd, RecordingTimestamp);
4127 UDF_LOG2_MEMBER(pFsd, "#06RX16", uInterchangeLevel);
4128 UDF_LOG2_MEMBER(pFsd, "#06RX16", uMaxInterchangeLevel);
4129 UDF_LOG2_MEMBER(pFsd, "#010RX32", fCharacterSets);
4130 UDF_LOG2_MEMBER(pFsd, "#010RX32", fMaxCharacterSets);
4131 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetNo);
4132 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetDescNo);
4133 UDF_LOG2_MEMBER_CHARSPEC(pFsd, LogicalVolumeIDCharSet);
4134 UDF_LOG2_MEMBER_DSTRING(pFsd, achLogicalVolumeID);
4135 UDF_LOG2_MEMBER_CHARSPEC(pFsd, FileSetCharSet);
4136 UDF_LOG2_MEMBER_DSTRING(pFsd, achFileSetID);
4137 UDF_LOG2_MEMBER_DSTRING(pFsd, achCopyrightFile);
4138 UDF_LOG2_MEMBER_DSTRING(pFsd, achAbstractFile);
4139 UDF_LOG2_MEMBER_LONGAD(pFsd, RootDirIcb);
4140 UDF_LOG2_MEMBER_ENTITY_ID(pFsd, idDomain);
4141 UDF_LOG2_MEMBER_LONGAD(pFsd, NextExtent);
4142 UDF_LOG2_MEMBER_LONGAD(pFsd, SystemStreamDirIcb);
4143 if (!ASMMemIsZero(&pFsd->abReserved[0], sizeof(pFsd->abReserved)))
4144 UDF_LOG2_MEMBER(pFsd, ".32Rhxs", abReserved);
4145 }
4146#endif
4147
4148 /*
4149 * Do some basic sanity checking.
4150 */
4151 if (!UDF_IS_CHAR_SET_OSTA(&pFsd->FileSetCharSet))
4152 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_UNSUPPORTED_CHAR_SET,
4153 "Invalid file set charset %.64Rhxs", &pFsd->FileSetCharSet);
4154 if ( pFsd->RootDirIcb.cb == 0
4155 || pFsd->RootDirIcb.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4156 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_ZERO_ROOT_DIR,
4157 "Root Dir ICB location is zero or malformed: uType=%#x cb=%#x loc=%#x:%#RX32",
4158 pFsd->RootDirIcb.uType, pFsd->RootDirIcb.cb,
4159 pFsd->RootDirIcb.Location.uPartitionNo, pFsd->RootDirIcb.Location.off);
4160 if ( pFsd->NextExtent.cb != 0
4161 && pFsd->NextExtent.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4162 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_NEXT_EXTENT,
4163 "NextExtent isn't zero: uType=%#x cb=%#x loc=%#x:%#RX32",
4164 pFsd->NextExtent.uType, pFsd->NextExtent.cb,
4165 pFsd->NextExtent.Location.uPartitionNo, pFsd->NextExtent.Location.off);
4166
4167 /*
4168 * Copy the information we need.
4169 */
4170 pThis->Udf.VolInfo.RootDirIcb = pFsd->RootDirIcb;
4171 if ( pFsd->SystemStreamDirIcb.cb > 0
4172 && pFsd->SystemStreamDirIcb.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4173 pThis->Udf.VolInfo.SystemStreamDirIcb = pFsd->SystemStreamDirIcb;
4174 else
4175 RT_ZERO(pThis->Udf.VolInfo.SystemStreamDirIcb);
4176 return VINF_SUCCESS;
4177 }
4178 return rc;
4179 }
4180 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading file set descriptor");
4181}
4182
4183
4184/**
4185 * Check validatity and extract information from the descriptors in the VDS seq.
4186 *
4187 * @returns IPRT status code
4188 * @param pThis The instance.
4189 * @param pInfo The VDS sequence info.
4190 * @param pErrInfo Where to return extended error info.
4191 */
4192static int rtFsIsoVolProcessUdfVdsSeqInfo(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, PRTERRINFO pErrInfo)
4193{
4194 /*
4195 * Check the basic descriptor counts.
4196 */
4197 PUDFPRIMARYVOLUMEDESC pPvd;
4198 if (pInfo->cPrimaryVols == 1)
4199 pPvd = pInfo->apPrimaryVols[0];
4200 else
4201 {
4202 if (pInfo->cPrimaryVols == 0)
4203 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PVD, "No primary volume descriptor was found");
4204 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_PVDS,
4205 "More than one primary volume descriptor was found: %u", pInfo->cPrimaryVols);
4206 }
4207
4208 PUDFLOGICALVOLUMEDESC pLvd;
4209 if (pInfo->cLogicalVols == 1)
4210 pLvd = pInfo->apLogicalVols[0];
4211 else
4212 {
4213 if (pInfo->cLogicalVols == 0)
4214 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_LVD, "No logical volume descriptor was found");
4215 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_LVDS,
4216 "More than one logical volume descriptor was found: %u", pInfo->cLogicalVols);
4217 }
4218
4219#if 0
4220 if (pInfo->cPartitions == 0)
4221 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PD, "No partition descriptors was found");
4222#endif
4223
4224 /*
4225 * Check out the partition map in the logical volume descriptor.
4226 * Produce the mapping table while going about that.
4227 */
4228 if (pLvd->cPartitionMaps > 64)
4229 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_MANY_PART_MAPS,
4230 "Too many partition maps: %u (max 64)", pLvd->cPartitionMaps);
4231
4232 PRTFSISOVOLUDFPMAP paPartMaps = NULL;
4233 if (pLvd->cPartitionMaps > 0)
4234 {
4235 pInfo->paPartMaps = paPartMaps = (PRTFSISOVOLUDFPMAP)RTMemAllocZ(sizeof(paPartMaps[0]) * pLvd->cPartitionMaps);
4236 if (!paPartMaps)
4237 return VERR_NO_MEMORY;
4238 }
4239 uint32_t cPartMaps = 0;
4240
4241 if (pLvd->cbMapTable)
4242 {
4243 uint32_t off = 0;
4244 while (off + sizeof(UDFPARTMAPHDR) <= pLvd->cbMapTable)
4245 {
4246 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pLvd->abPartitionMaps[off];
4247
4248 /*
4249 * Bounds checking.
4250 */
4251 if (off + pHdr->cb > pLvd->cbMapTable)
4252 {
4253 if (cPartMaps < pLvd->cbMapTable)
4254 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MALFORMED_PART_MAP_TABLE,
4255 "Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)",
4256 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType);
4257 LogRel(("ISO/UDF: Warning: Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)\n",
4258 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType));
4259 break;
4260 }
4261 if (cPartMaps >= pLvd->cPartitionMaps)
4262 {
4263 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",
4264 cPartMaps - pLvd->cPartitionMaps, off, pHdr->cb, pLvd->cbMapTable, pHdr->bType));
4265 break;
4266 }
4267
4268 /*
4269 * Extract relevant info out of the entry.
4270 */
4271 paPartMaps[cPartMaps].offMapTable = (uint16_t)off;
4272 uint16_t uPartitionNo;
4273 if (pHdr->bType == 1)
4274 {
4275 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
4276 paPartMaps[cPartMaps].uVolumeSeqNo = pType1->uVolumeSeqNo;
4277 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_PLAIN;
4278 uPartitionNo = pType1->uPartitionNo;
4279 }
4280 else if (pHdr->bType == 2)
4281 {
4282 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
4283 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_VPM_PARTITION_TYPE))
4284 {
4285 paPartMaps[cPartMaps].bType = pType2->idPartitionType.Suffix.Udf.uUdfRevision >= 0x200
4286 ? RTFSISO_UDF_PMAP_T_VPM_20 : RTFSISO_UDF_PMAP_T_VPM_15;
4287 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_VPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4288 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4289 }
4290 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
4291 {
4292 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_SPM;
4293 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_SPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4294 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4295 }
4296 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
4297 {
4298 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_MPM;
4299 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4300 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4301 }
4302 else
4303 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_TYPE_ID,
4304 "Unknown partition map ID for #%u @ %#x: %.23s",
4305 cPartMaps, off, pType2->idPartitionType.achIdentifier);
4306#if 0 /* unreachable code */
4307 paPartMaps[cPartMaps].uVolumeSeqNo = pType2->uVolumeSeqNo;
4308 uPartitionNo = pType2->uPartitionNo;
4309#endif
4310 }
4311 else
4312 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_ENTRY_TYPE,
4313 "Unknown partition map entry type #%u @ %#x: %u", cPartMaps, off, pHdr->bType);
4314 paPartMaps[cPartMaps].uPartitionNo = uPartitionNo;
4315
4316 /*
4317 * Lookup the partition number and retrieve the relevant info from the partition descriptor.
4318 */
4319 uint32_t i = pInfo->cPartitions;
4320 while (i-- > 0)
4321 {
4322 PUDFPARTITIONDESC pPd = pInfo->apPartitions[i];
4323 if (paPartMaps[cPartMaps].uPartitionNo == pPd->uPartitionNo)
4324 {
4325 paPartMaps[cPartMaps].idxPartDesc = (uint16_t)i;
4326 paPartMaps[cPartMaps].cSectors = pPd->cSectors;
4327 paPartMaps[cPartMaps].offLocation = pPd->offLocation;
4328 paPartMaps[cPartMaps].offByteLocation = (uint64_t)pPd->offLocation * pThis->cbSector;
4329 paPartMaps[cPartMaps].fFlags = pPd->fFlags;
4330 paPartMaps[cPartMaps].uAccessType = pPd->uAccessType;
4331 if (!UDF_ENTITY_ID_EQUALS(&pPd->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
4332 paPartMaps[cPartMaps].fHaveHdr = false;
4333 else
4334 {
4335 paPartMaps[cPartMaps].fHaveHdr = true;
4336 paPartMaps[cPartMaps].Hdr = pPd->ContentsUse.Hdr;
4337 }
4338 break;
4339 }
4340 }
4341 if (i > pInfo->cPartitions)
4342 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_PARTITION_NOT_FOUND,
4343 "Partition #%u (%#x) specified by mapping entry #%u (@ %#x) was not found! (int-type %u)",
4344 uPartitionNo, uPartitionNo, cPartMaps, off, paPartMaps[cPartMaps].bType);
4345
4346 /*
4347 * Advance.
4348 */
4349 cPartMaps++;
4350 off += pHdr->cb;
4351 }
4352
4353 if (cPartMaps < pLvd->cPartitionMaps)
4354 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INCOMPLETE_PART_MAP_TABLE,
4355 "Only found %u of the %u announced partition mapping table entries",
4356 cPartMaps, pLvd->cPartitionMaps);
4357 }
4358
4359 /* It might be theoretically possible to not use virtual partitions for
4360 accessing data, so just warn if there aren't any. */
4361 if (cPartMaps == 0)
4362 LogRel(("ISO/UDF: Warning: No partition maps!\n"));
4363
4364 /*
4365 * Check out the logical volume descriptor.
4366 */
4367 if ( pLvd->cbLogicalBlock < pThis->cbSector
4368 || pLvd->cbLogicalBlock > RTFSISO_MAX_LOGICAL_BLOCK_SIZE
4369 || (pLvd->cbLogicalBlock % pThis->cbSector) != 0)
4370 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_LOGICAL_BLOCK_SIZE,
4371 "Logical block size of %#x is not supported with a sector size of %#x",
4372 pLvd->cbLogicalBlock, pThis->cbSector);
4373
4374 if (!UDF_ENTITY_ID_EQUALS(&pLvd->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
4375 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DOMAIN_ID,
4376 "Unsupported domain ID in logical volume descriptor: '%.23s'", pLvd->idDomain.achIdentifier);
4377
4378 if ( pLvd->ContentsUse.FileSetDescriptor.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED
4379 || pLvd->ContentsUse.FileSetDescriptor.cb == 0
4380 || pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo >= cPartMaps)
4381 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_FILE_SET_DESC_LOCATION,
4382 "Malformed file set descriptor location (type=%u cb=%#x part=%#x)",
4383 pLvd->ContentsUse.FileSetDescriptor.uType,
4384 pLvd->ContentsUse.FileSetDescriptor.cb,
4385 pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo);
4386
4387 bool fLvdHaveVolId = !ASMMemIsZero(pLvd->achLogicalVolumeID, sizeof(pLvd->achLogicalVolumeID));
4388 if ( fLvdHaveVolId
4389 && !UDF_IS_CHAR_SET_OSTA(&pLvd->DescCharSet))
4390 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DESC_CHAR_SET,
4391 "Logical volume ID is not using OSTA compressed unicode");
4392
4393 /*
4394 * We can ignore much, if not all of the primary volume descriptor.
4395 */
4396
4397 /*
4398 * We're good. So copy over the data.
4399 */
4400 pThis->Udf.VolInfo.FileSetDescriptor = pLvd->ContentsUse.FileSetDescriptor;
4401 pThis->Udf.VolInfo.cbBlock = pLvd->cbLogicalBlock;
4402 pThis->Udf.VolInfo.cShiftBlock = 9;
4403 while (pThis->Udf.VolInfo.cbBlock != RT_BIT_32(pThis->Udf.VolInfo.cShiftBlock))
4404 pThis->Udf.VolInfo.cShiftBlock++;
4405 pThis->Udf.VolInfo.fFlags = pPvd->fFlags;
4406 pThis->Udf.VolInfo.cPartitions = cPartMaps;
4407 pThis->Udf.VolInfo.paPartitions = paPartMaps;
4408 pInfo->paPartMaps = NULL;
4409 if (fLvdHaveVolId)
4410 memcpy(pThis->Udf.VolInfo.achLogicalVolumeID, pLvd->achLogicalVolumeID, sizeof(pThis->Udf.VolInfo.achLogicalVolumeID));
4411 else
4412 RT_ZERO(pThis->Udf.VolInfo.achLogicalVolumeID);
4413
4414 return VINF_SUCCESS;
4415}
4416
4417
4418/**
4419 * Processes a primary volume descriptor in the VDS (UDF).
4420 *
4421 * @returns IPRT status code.
4422 * @param pInfo Where we gather descriptor information.
4423 * @param pDesc The descriptor.
4424 * @param pErrInfo Where to return extended error information.
4425 */
4426//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
4427static int rtFsIsoVolProcessUdfPrimaryVolDesc(PRTFSISOVDSINFO pInfo, PCUDFPRIMARYVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
4428{
4429#ifdef LOG_ENABLED
4430 Log(("ISO/UDF: Primary volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4431 if (LogIs2Enabled())
4432 {
4433 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4434 UDF_LOG2_MEMBER(pDesc, "#010RX32", uPrimaryVolumeDescNo);
4435 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeID);
4436 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
4437 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
4438 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxVolumeSeqNo);
4439 UDF_LOG2_MEMBER(pDesc, "#06RX16", uInterchangeLevel);
4440 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxInterchangeLevel);
4441 UDF_LOG2_MEMBER(pDesc, "#010RX32", fCharacterSets);
4442 UDF_LOG2_MEMBER(pDesc, "#010RX32", fMaxCharacterSets);
4443 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeSetID);
4444 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
4445 UDF_LOG2_MEMBER_CHARSPEC(pDesc, ExplanatoryCharSet);
4446 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeAbstract);
4447 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeCopyrightNotice);
4448 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idApplication);
4449 UDF_LOG2_MEMBER_TIMESTAMP(pDesc, RecordingTimestamp);
4450 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4451 if (!ASMMemIsZero(&pDesc->abImplementationUse, sizeof(pDesc->abImplementationUse)))
4452 Log2(("ISO/UDF: %-32s %.64Rhxs\n", "abReserved[64]:", &pDesc->abImplementationUse[0]));
4453 UDF_LOG2_MEMBER(pDesc, "#010RX32", offPredecessorVolDescSeq);
4454 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
4455 if (!ASMMemIsZero(&pDesc->abReserved, sizeof(pDesc->abReserved)))
4456 Log2(("ISO/UDF: %-32s %.22Rhxs\n", "abReserved[22]:", &pDesc->abReserved[0]));
4457 }
4458#endif
4459
4460 /*
4461 * Check if this is a new revision of an existing primary volume descriptor.
4462 */
4463 PUDFPRIMARYVOLUMEDESC pEndianConvert = NULL;
4464 uint32_t i = pInfo->cPrimaryVols;
4465 while (i--> 0)
4466 {
4467 if ( memcmp(pDesc->achVolumeID, pInfo->apPrimaryVols[i]->achVolumeID, sizeof(pDesc->achVolumeID)) == 0
4468 && memcmp(&pDesc->DescCharSet, &pInfo->apPrimaryVols[i]->DescCharSet, sizeof(pDesc->DescCharSet)) == 0)
4469 {
4470 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPrimaryVols[i]->uVolumeDescSeqNo)
4471 {
4472 Log(("ISO/UDF: Primary descriptor prevails over previous! (%u >= %u)\n",
4473 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4474 pEndianConvert = pInfo->apPrimaryVols[i];
4475 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
4476 }
4477 else
4478 Log(("ISO/UDF: Primary descriptor has lower sequence number than the previous! (%u < %u)\n",
4479 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4480 break;
4481 }
4482 }
4483 if (i >= pInfo->cPrimaryVols)
4484 {
4485 /*
4486 * It wasn't. Append it.
4487 */
4488 i = pInfo->cPrimaryVols;
4489 if (i < RT_ELEMENTS(pInfo->apPrimaryVols))
4490 {
4491 pInfo->apPrimaryVols[i] = pEndianConvert = (PUDFPRIMARYVOLUMEDESC)RTMemDup(pDesc, sizeof(*pDesc));
4492 if (pEndianConvert)
4493 pInfo->cPrimaryVols = i + 1;
4494 else
4495 return VERR_NO_MEMORY;
4496 Log2(("ISO/UDF: ++New primary descriptor.\n"));
4497 }
4498 else
4499 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PVDS, "Encountered too many primary volume descriptors");
4500 }
4501
4502#ifdef RT_BIG_ENDIAN
4503 /*
4504 * Do endian conversion of the descriptor.
4505 */
4506 if (pEndianConvert)
4507 {
4508 AssertFailed();
4509 }
4510#else
4511 RT_NOREF(pEndianConvert);
4512#endif
4513 return VINF_SUCCESS;
4514}
4515
4516
4517/**
4518 * Processes an logical volume descriptor in the VDS (UDF).
4519 *
4520 * @returns IPRT status code.
4521 * @param pInfo Where we gather descriptor information.
4522 * @param pDesc The descriptor.
4523 * @param cbSector The sector size (UDF defines the logical and physical
4524 * sector size to be the same).
4525 * @param pErrInfo Where to return extended error information.
4526 */
4527static int rtFsIsoVolProcessUdfLogicalVolumeDesc(PRTFSISOVDSINFO pInfo, PCUDFLOGICALVOLUMEDESC pDesc,
4528 uint32_t cbSector, PRTERRINFO pErrInfo)
4529{
4530#ifdef LOG_ENABLED
4531 Log(("ISO/UDF: Logical volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4532 if (LogIs2Enabled())
4533 {
4534 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4535 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
4536 UDF_LOG2_MEMBER_DSTRING(pDesc, achLogicalVolumeID);
4537 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbLogicalBlock);
4538 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idDomain);
4539 if (UDF_ENTITY_ID_EQUALS(&pDesc->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
4540 UDF_LOG2_MEMBER_LONGAD(pDesc, ContentsUse.FileSetDescriptor);
4541 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
4542 Log2(("ISO/UDF: %-32s %.16Rhxs\n", "ContentsUse.ab[16]:", &pDesc->ContentsUse.ab[0]));
4543 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbMapTable);
4544 UDF_LOG2_MEMBER(pDesc, "#010RX32", cPartitionMaps);
4545 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4546 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4547 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
4548 UDF_LOG2_MEMBER_EXTENTAD(pDesc, IntegritySeqExtent);
4549 if (pDesc->cbMapTable)
4550 {
4551 Log2(("ISO/UDF: %-32s\n", "abPartitionMaps"));
4552 uint32_t iMap = 0;
4553 uint32_t off = 0;
4554 while (off + sizeof(UDFPARTMAPHDR) <= pDesc->cbMapTable)
4555 {
4556 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pDesc->abPartitionMaps[off];
4557 Log2(("ISO/UDF: %02u @ %#05x: type %u, length %u\n", iMap, off, pHdr->bType, pHdr->cb));
4558 if (off + pHdr->cb > pDesc->cbMapTable)
4559 {
4560 Log2(("ISO/UDF: BAD! Entry is %d bytes too long!\n", off + pHdr->cb - pDesc->cbMapTable));
4561 break;
4562 }
4563 if (pHdr->bType == 1)
4564 {
4565 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
4566 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uVolumeSeqNo, 5);
4567 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uPartitionNo, 5);
4568 }
4569 else if (pHdr->bType == 2)
4570 {
4571 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
4572 UDF_LOG2_MEMBER_ENTITY_ID_EX(pType2, idPartitionType, 5);
4573 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uVolumeSeqNo, 5);
4574 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uPartitionNo, 5);
4575 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
4576 {
4577 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Spm.cBlocksPerPacket, 5);
4578 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.cSparingTables, 5);
4579 if (pType2->u.Spm.bReserved2)
4580 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Spm.bReserved2, 5);
4581 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.cbSparingTable, 5);
4582 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[0], 5);
4583 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[1], 5);
4584 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[2], 5);
4585 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Spm.aoffSparingTables[3], 5);
4586 }
4587 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
4588 {
4589 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataFile, 5);
4590 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataMirrorFile, 5);
4591 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.offMetadataBitmapFile, 5);
4592 UDF_LOG2_MEMBER_EX(&pType2->u, "#010RX32", Mpm.cBlocksAllocationUnit, 5);
4593 UDF_LOG2_MEMBER_EX(&pType2->u, "#06RX16", Mpm.cBlocksAlignmentUnit, 5);
4594 UDF_LOG2_MEMBER_EX(&pType2->u, "#04RX8", Mpm.fFlags, 5);
4595 if (!ASMMemIsZero(pType2->u.Mpm.abReserved2, sizeof(pType2->u.Mpm.abReserved2)))
4596 UDF_LOG2_MEMBER_EX(&pType2->u, ".5Rhxs", Mpm.abReserved2, 5);
4597 }
4598 }
4599 else
4600 Log2(("ISO/UDF: BAD! Unknown type!\n"));
4601
4602 /* advance */
4603 off += pHdr->cb;
4604 iMap++;
4605 }
4606 }
4607 }
4608#endif
4609
4610 /*
4611 * Check if this is a newer revision of an existing primary volume descriptor.
4612 */
4613 size_t cbDesc = (size_t)pDesc->cbMapTable + RT_UOFFSETOF(UDFLOGICALVOLUMEDESC, abPartitionMaps);
4614 if ( pDesc->cbMapTable >= (UINT32_MAX >> 1)
4615 || cbDesc > cbSector)
4616 {
4617 Log(("ISO/UDF: Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector));
4618 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD,
4619 "Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector);
4620 }
4621
4622 PUDFLOGICALVOLUMEDESC pEndianConvert = NULL;
4623 uint32_t i = pInfo->cLogicalVols;
4624 while (i--> 0)
4625 if ( memcmp(pDesc->achLogicalVolumeID, pInfo->apLogicalVols[i]->achLogicalVolumeID,
4626 sizeof(pDesc->achLogicalVolumeID)) == 0
4627 && memcmp(&pDesc->DescCharSet, &pInfo->apLogicalVols[i]->DescCharSet,
4628 sizeof(pDesc->DescCharSet)) == 0)
4629 {
4630 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apLogicalVols[i]->uVolumeDescSeqNo)
4631 {
4632 Log(("ISO/UDF: Logical descriptor prevails over previous! (%u >= %u)\n",
4633 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
4634 pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
4635 if (!pEndianConvert)
4636 return VERR_NO_MEMORY;
4637 RTMemFree(pInfo->apLogicalVols[i]);
4638 pInfo->apLogicalVols[i] = pEndianConvert;
4639 }
4640 else
4641 Log(("ISO/UDF: Logical descriptor has lower sequence number than the previous! (%u >= %u)\n",
4642 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
4643 break;
4644 }
4645 if (i >= pInfo->cLogicalVols)
4646 {
4647 /*
4648 * It wasn't. Append it.
4649 */
4650 i = pInfo->cLogicalVols;
4651 if (i < RT_ELEMENTS(pInfo->apLogicalVols))
4652 {
4653 pInfo->apLogicalVols[i] = pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
4654 if (pEndianConvert)
4655 pInfo->cLogicalVols = i + 1;
4656 else
4657 return VERR_NO_MEMORY;
4658 Log2(("ISO/UDF: ++New logical volume descriptor.\n"));
4659 }
4660 else
4661 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_LVDS, "Too many logical volume descriptors");
4662 }
4663
4664#ifdef RT_BIG_ENDIAN
4665 /*
4666 * Do endian conversion of the descriptor.
4667 */
4668 if (pEndianConvert)
4669 {
4670 AssertFailed();
4671 }
4672#else
4673 RT_NOREF(pEndianConvert);
4674#endif
4675 return VINF_SUCCESS;
4676}
4677
4678
4679/**
4680 * Processes an partition descriptor in the VDS (UDF).
4681 *
4682 * @returns IPRT status code.
4683 * @param pInfo Where we gather descriptor information.
4684 * @param pDesc The descriptor.
4685 * @param pErrInfo Where to return extended error information.
4686 */
4687static int rtFsIsoVolProcessUdfPartitionDesc(PRTFSISOVDSINFO pInfo, PCUDFPARTITIONDESC pDesc, PRTERRINFO pErrInfo)
4688{
4689#ifdef LOG_ENABLED
4690 Log(("ISO/UDF: Partition descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4691 if (LogIs2Enabled())
4692 {
4693 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4694 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
4695 UDF_LOG2_MEMBER(pDesc, "#06RX16", uPartitionNo);
4696 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, PartitionContents);
4697 if (UDF_ENTITY_ID_EQUALS(&pDesc->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
4698 {
4699 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceTable);
4700 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceBitmap);
4701 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.PartitionIntegrityTable);
4702 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceTable);
4703 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceBitmap);
4704 if (!ASMMemIsZero(&pDesc->ContentsUse.Hdr.abReserved[0], sizeof(pDesc->ContentsUse.Hdr.abReserved)))
4705 Log2(("ISO/UDF: %-32s\n%.88RhxD\n", "Hdr.abReserved[88]:", &pDesc->ContentsUse.Hdr.abReserved[0]));
4706 }
4707 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
4708 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ContentsUse.ab[128]:", &pDesc->ContentsUse.ab[0]));
4709 UDF_LOG2_MEMBER(pDesc, "#010RX32", uAccessType);
4710 UDF_LOG2_MEMBER(pDesc, "#010RX32", offLocation);
4711 UDF_LOG2_MEMBER(pDesc, "#010RX32", cSectors);
4712 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4713 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4714 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
4715
4716 if (!ASMMemIsZero(&pDesc->abReserved[0], sizeof(pDesc->abReserved)))
4717 Log2(("ISO/UDF: %-32s\n%.156RhxD\n", "ImplementationUse.ab[156]:", &pDesc->abReserved[0]));
4718 }
4719#endif
4720
4721 /*
4722 * Check if this is a newer revision of an existing primary volume descriptor.
4723 */
4724 PUDFPARTITIONDESC pEndianConvert = NULL;
4725 uint32_t i = pInfo->cPartitions;
4726 while (i--> 0)
4727 if (pDesc->uPartitionNo == pInfo->apPartitions[i]->uPartitionNo)
4728 {
4729 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPartitions[i]->uVolumeDescSeqNo)
4730 {
4731 Log(("ISO/UDF: Partition descriptor for part %#u prevails over previous! (%u >= %u)\n",
4732 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4733 pEndianConvert = pInfo->apPartitions[i];
4734 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
4735 }
4736 else
4737 Log(("ISO/UDF: Partition descriptor for part %#u has a lower sequence number than the previous! (%u < %u)\n",
4738 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4739 break;
4740 }
4741 if (i >= pInfo->cPartitions)
4742 {
4743 /*
4744 * It wasn't. Append it.
4745 */
4746 i = pInfo->cPartitions;
4747 if (i < RT_ELEMENTS(pInfo->apPartitions))
4748 {
4749 pInfo->apPartitions[i] = pEndianConvert = (PUDFPARTITIONDESC)RTMemDup(pDesc, sizeof(*pDesc));
4750 if (pEndianConvert)
4751 pInfo->cPartitions = i + 1;
4752 else
4753 return VERR_NO_MEMORY;
4754 Log2(("ISO/UDF: ++New partition descriptor.\n"));
4755 }
4756 else
4757 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PDS, "Too many physical volume descriptors");
4758 }
4759
4760#ifdef RT_BIG_ENDIAN
4761 /*
4762 * Do endian conversion of the descriptor.
4763 */
4764 if (pEndianConvert)
4765 {
4766 AssertFailed();
4767 }
4768#else
4769 RT_NOREF(pEndianConvert);
4770#endif
4771 return VINF_SUCCESS;
4772}
4773
4774
4775/**
4776 * Processes an implementation use descriptor in the VDS (UDF).
4777 *
4778 * @returns IPRT status code.
4779 * @param pInfo Where we gather descriptor information.
4780 * @param pDesc The descriptor.
4781 * @param pErrInfo Where to return extended error information.
4782 */
4783static int rtFsIsoVolProcessUdfImplUseVolDesc(PRTFSISOVDSINFO pInfo, PCUDFIMPLEMENTATIONUSEVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
4784{
4785#ifdef LOG_ENABLED
4786 Log(("ISO/UDF: Implementation use volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4787 if (LogIs2Enabled())
4788 {
4789 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4790 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4791 if (UDF_ENTITY_ID_EQUALS(&pDesc->idImplementation, UDF_ENTITY_ID_IUVD_IMPLEMENTATION))
4792 {
4793 UDF_LOG2_MEMBER_CHARSPEC(&pDesc->ImplementationUse, Lvi.Charset);
4794 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achVolumeID);
4795 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo1);
4796 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo2);
4797 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo3);
4798 UDF_LOG2_MEMBER_ENTITY_ID(&pDesc->ImplementationUse, Lvi.idImplementation);
4799 if (!ASMMemIsZero(&pDesc->ImplementationUse.Lvi.abUse[0], sizeof(pDesc->ImplementationUse.Lvi.abUse)))
4800 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "Lvi.abUse[128]:", &pDesc->ImplementationUse.Lvi.abUse[0]));
4801 }
4802 else if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4803 Log2(("ISO/UDF: %-32s\n%.460RhxD\n", "ImplementationUse.ab[460]:", &pDesc->ImplementationUse.ab[0]));
4804 }
4805#endif
4806
4807 RT_NOREF(pInfo, pDesc, pErrInfo);
4808 return VINF_SUCCESS;
4809}
4810
4811
4812
4813typedef struct RTFSISOSEENSEQENCES
4814{
4815 /** Number of sequences we've seen thus far. */
4816 uint32_t cSequences;
4817 /** The per sequence data. */
4818 struct
4819 {
4820 uint64_t off; /**< Byte offset of the sequence. */
4821 uint32_t cb; /**< Size of the sequence. */
4822 } aSequences[8];
4823} RTFSISOSEENSEQENCES;
4824typedef RTFSISOSEENSEQENCES *PRTFSISOSEENSEQENCES;
4825
4826
4827
4828/**
4829 * Process a VDS sequence, recursively dealing with volume descriptor pointers.
4830 *
4831 * This function only gathers information from the sequence, handling the
4832 * prevailing descriptor fun.
4833 *
4834 * @returns IPRT status code.
4835 * @param pThis The instance.
4836 * @param pInfo Where to store info from the VDS sequence.
4837 * @param offSeq The byte offset of the sequence.
4838 * @param cbSeq The length of the sequence.
4839 * @param pbBuf Read buffer.
4840 * @param cbBuf Size of the read buffer. This is at least one
4841 * sector big.
4842 * @param cNestings The VDS nesting depth.
4843 * @param pErrInfo Where to return extended error info.
4844 */
4845static int rtFsIsoVolReadAndProcessUdfVdsSeq(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, uint64_t offSeq, uint32_t cbSeq,
4846 uint8_t *pbBuf, size_t cbBuf, uint32_t cNestings, PRTERRINFO pErrInfo)
4847{
4848 AssertReturn(cbBuf >= pThis->cbSector, VERR_INTERNAL_ERROR);
4849
4850 /*
4851 * Check nesting depth.
4852 */
4853 if (cNestings > 5)
4854 return RTERRINFO_LOG_SET(pErrInfo, VERR_TOO_MUCH_DATA, "The volume descriptor sequence (VDS) is nested too deeply.");
4855
4856
4857 /*
4858 * Do the processing sector by sector to keep things simple.
4859 */
4860 uint32_t offInSeq = 0;
4861 while (offInSeq < cbSeq)
4862 {
4863 int rc;
4864
4865 /*
4866 * Read the next sector. Zero pad if less that a sector.
4867 */
4868 Assert((offInSeq & (pThis->cbSector - 1)) == 0);
4869 rc = RTVfsFileReadAt(pThis->hVfsBacking, offSeq + offInSeq, pbBuf, pThis->cbSector, NULL);
4870 if (RT_FAILURE(rc))
4871 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error reading VDS content at %RX64 (LB %#x): %Rrc",
4872 offSeq + offInSeq, pThis->cbSector, rc);
4873 if (cbSeq - offInSeq < pThis->cbSector)
4874 memset(&pbBuf[cbSeq - offInSeq], 0, pThis->cbSector - (cbSeq - offInSeq));
4875
4876 /*
4877 * Check tag.
4878 */
4879 PCUDFTAG pTag = (PCUDFTAG)pbBuf;
4880 rc = rtFsIsoVolValidateUdfDescTagAndCrc(pTag, pThis->cbSector, UINT16_MAX, (offSeq + offInSeq) / pThis->cbSector, pErrInfo);
4881 if ( RT_SUCCESS(rc)
4882 || ( rc == VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4883 && ( pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC
4884 || pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_DESC
4885 || pTag->idTag == UDF_TAG_ID_UNALLOCATED_SPACE_DESC
4886 )
4887 )
4888 )
4889 {
4890 switch (pTag->idTag)
4891 {
4892 case UDF_TAG_ID_PRIMARY_VOL_DESC:
4893 rc = rtFsIsoVolProcessUdfPrimaryVolDesc(pInfo, (PCUDFPRIMARYVOLUMEDESC)pTag, pErrInfo);
4894 break;
4895
4896 case UDF_TAG_ID_IMPLEMENTATION_USE_VOLUME_DESC:
4897 rc = rtFsIsoVolProcessUdfImplUseVolDesc(pInfo, (PCUDFIMPLEMENTATIONUSEVOLUMEDESC)pTag, pErrInfo);
4898 break;
4899
4900 case UDF_TAG_ID_PARTITION_DESC:
4901 rc = rtFsIsoVolProcessUdfPartitionDesc(pInfo, (PCUDFPARTITIONDESC)pTag, pErrInfo);
4902 break;
4903
4904 case UDF_TAG_ID_LOGICAL_VOLUME_DESC:
4905 if (rc != VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC)
4906 rc = rtFsIsoVolProcessUdfLogicalVolumeDesc(pInfo, (PCUDFLOGICALVOLUMEDESC)pTag,
4907 pThis->cbSector, pErrInfo);
4908 else
4909 rc = VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD;
4910 break;
4911
4912 case UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC:
4913 Log(("ISO/UDF: Ignoring logical volume integrity descriptor at offset %#RX64.\n", offSeq + offInSeq));
4914 rc = VINF_SUCCESS;
4915 break;
4916
4917 case UDF_TAG_ID_UNALLOCATED_SPACE_DESC:
4918 Log(("ISO/UDF: Ignoring unallocated space descriptor at offset %#RX64.\n", offSeq + offInSeq));
4919 rc = VINF_SUCCESS;
4920 break;
4921
4922 case UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR:
4923 Log(("ISO/UDF: Ignoring AVDP in VDS (at offset %#RX64).\n", offSeq + offInSeq));
4924 rc = VINF_SUCCESS;
4925 break;
4926
4927 case UDF_TAG_ID_VOLUME_DESC_PTR:
4928 {
4929 PCUDFVOLUMEDESCPTR pVdp = (PCUDFVOLUMEDESCPTR)pTag;
4930 Log(("ISO/UDF: Processing volume descriptor pointer at offset %#RX64: %#x LB %#x (seq %#x); cNestings=%d\n",
4931 offSeq + offInSeq, pVdp->NextVolumeDescSeq.off, pVdp->NextVolumeDescSeq.cb,
4932 pVdp->uVolumeDescSeqNo, cNestings));
4933 rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, pInfo, (uint64_t)pVdp->NextVolumeDescSeq.off * pThis->cbSector,
4934 pVdp->NextVolumeDescSeq.cb, pbBuf, cbBuf, cNestings + 1, pErrInfo);
4935 break;
4936 }
4937
4938 case UDF_TAG_ID_TERMINATING_DESC:
4939 Log(("ISO/UDF: Terminating descriptor at offset %#RX64\n", offSeq + offInSeq));
4940 return VINF_SUCCESS;
4941
4942 default:
4943 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNEXPECTED_VDS_DESC,
4944 "Unexpected/unknown VDS descriptor %#x at byte offset %#RX64",
4945 pThis->cbSector, offSeq + offInSeq);
4946 }
4947 if (RT_FAILURE(rc))
4948 return rc;
4949 }
4950 /* The descriptor sequence is usually zero padded to 16 sectors. Just
4951 ignore zero descriptors. */
4952 else if (rc != VERR_ISOFS_TAG_IS_ALL_ZEROS)
4953 return rc;
4954
4955 /*
4956 * Advance.
4957 */
4958 offInSeq += pThis->cbSector;
4959 }
4960
4961 return VINF_SUCCESS;
4962}
4963
4964
4965
4966/**
4967 * Processes a volume descriptor sequence (VDS).
4968 *
4969 * @returns IPRT status code.
4970 * @param pThis The instance.
4971 * @param offSeq The byte offset of the sequence.
4972 * @param cbSeq The length of the sequence.
4973 * @param pSeenSequences Structure where to keep track of VDSes we've already
4974 * processed, to avoid redoing one that we don't
4975 * understand.
4976 * @param pbBuf Read buffer.
4977 * @param cbBuf Size of the read buffer. This is at least one
4978 * sector big.
4979 * @param pErrInfo Where to report extended error information.
4980 */
4981static int rtFsIsoVolReadAndProcessUdfVds(PRTFSISOVOL pThis, uint64_t offSeq, uint32_t cbSeq,
4982 PRTFSISOSEENSEQENCES pSeenSequences, uint8_t *pbBuf, size_t cbBuf,
4983 PRTERRINFO pErrInfo)
4984{
4985 /*
4986 * Skip if already seen.
4987 */
4988 uint32_t i = pSeenSequences->cSequences;
4989 while (i-- > 0)
4990 if ( pSeenSequences->aSequences[i].off == offSeq
4991 && pSeenSequences->aSequences[i].cb == cbSeq)
4992 return VERR_NOT_FOUND;
4993
4994 /* Not seen, so add it. */
4995 Assert(pSeenSequences->cSequences + 1 <= RT_ELEMENTS(pSeenSequences->aSequences));
4996 pSeenSequences->aSequences[pSeenSequences->cSequences].cb = cbSeq;
4997 pSeenSequences->aSequences[pSeenSequences->cSequences].off = offSeq;
4998 pSeenSequences->cSequences++;
4999
5000 LogFlow(("ISO/UDF: Processing anchor volume descriptor sequence at offset %#RX64 LB %#RX32\n", offSeq, cbSeq));
5001
5002 /*
5003 * Gather relevant descriptor info from the VDS then process it and on
5004 * success copy it into the instance.
5005 *
5006 * The processing has to be done in a different function because there may
5007 * be links to sub-sequences that needs to be processed. We do this by
5008 * recursing and check that we don't go to deep.
5009 */
5010 RTFSISOVDSINFO Info;
5011 RT_ZERO(Info);
5012 int rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, &Info, offSeq, cbSeq, pbBuf, cbBuf, 0, pErrInfo);
5013 if (RT_SUCCESS(rc))
5014 {
5015 rc = rtFsIsoVolProcessUdfVdsSeqInfo(pThis, &Info, pErrInfo);
5016 if (RT_SUCCESS(rc))
5017 rc = rtFsIsoVolProcessUdfFileSetDescs(pThis, pbBuf, cbBuf, pErrInfo);
5018 }
5019
5020 /*
5021 * Clean up info.
5022 */
5023 i = Info.cPrimaryVols;
5024 while (i-- > 0)
5025 RTMemFree(Info.apPrimaryVols[i]);
5026
5027 i = Info.cLogicalVols;
5028 while (i-- > 0)
5029 RTMemFree(Info.apLogicalVols[i]);
5030
5031 i = Info.cPartitions;
5032 while (i-- > 0)
5033 RTMemFree(Info.apPartitions[i]);
5034
5035 RTMemFree(Info.paPartMaps);
5036
5037 return rc;
5038}
5039
5040
5041static int rtFsIsoVolReadAndHandleUdfAvdp(PRTFSISOVOL pThis, uint64_t offAvdp, uint8_t *pbBuf, size_t cbBuf,
5042 PRTFSISOSEENSEQENCES pSeenSequences, PRTERRINFO pErrInfo)
5043{
5044 /*
5045 * Try read the descriptor and validate its tag.
5046 */
5047 PUDFANCHORVOLUMEDESCPTR pAvdp = (PUDFANCHORVOLUMEDESCPTR)pbBuf;
5048 size_t cbAvdpRead = RT_MIN(pThis->cbSector, cbBuf);
5049 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offAvdp, pAvdp, cbAvdpRead, NULL);
5050 if (RT_SUCCESS(rc))
5051 {
5052 rc = rtFsIsoVolValidateUdfDescTag(&pAvdp->Tag, UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR, offAvdp / pThis->cbSector, pErrInfo);
5053 if (RT_SUCCESS(rc))
5054 {
5055 Log2(("ISO/UDF: AVDP: MainVolumeDescSeq=%#RX32 LB %#RX32, ReserveVolumeDescSeq=%#RX32 LB %#RX32\n",
5056 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb,
5057 pAvdp->ReserveVolumeDescSeq.off, pAvdp->ReserveVolumeDescSeq.cb));
5058
5059 /*
5060 * Try the main sequence if it looks sane.
5061 */
5062 UDFEXTENTAD const ReserveVolumeDescSeq = pAvdp->ReserveVolumeDescSeq;
5063 if ( pAvdp->MainVolumeDescSeq.off < pThis->cBackingSectors
5064 && (uint64_t)pAvdp->MainVolumeDescSeq.off
5065 + (pAvdp->MainVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
5066 <= pThis->cBackingSectors)
5067 {
5068 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)pAvdp->MainVolumeDescSeq.off * pThis->cbSector,
5069 pAvdp->MainVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
5070 if (RT_SUCCESS(rc))
5071 return rc;
5072 }
5073 else
5074 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
5075 "MainVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
5076 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb, pThis->cBackingSectors);
5077 if (ReserveVolumeDescSeq.cb > 0)
5078 {
5079 if ( ReserveVolumeDescSeq.off < pThis->cBackingSectors
5080 && (uint64_t)ReserveVolumeDescSeq.off
5081 + (ReserveVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
5082 <= pThis->cBackingSectors)
5083 {
5084 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)ReserveVolumeDescSeq.off * pThis->cbSector,
5085 ReserveVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
5086 if (RT_SUCCESS(rc))
5087 return rc;
5088 }
5089 else if (RT_SUCCESS(rc))
5090 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
5091 "ReserveVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
5092 ReserveVolumeDescSeq.off, ReserveVolumeDescSeq.cb, pThis->cBackingSectors);
5093 }
5094 }
5095 }
5096 else
5097 rc = RTERRINFO_LOG_SET_F(pErrInfo, rc,
5098 "Error reading sector at offset %#RX64 (anchor volume descriptor pointer): %Rrc", offAvdp, rc);
5099
5100 return rc;
5101}
5102
5103
5104/**
5105 * Goes looking for UDF when we've seens a volume recognition sequence.
5106 *
5107 * @returns IPRT status code.
5108 * @param pThis The volume instance data.
5109 * @param puUdfLevel The UDF level indicated by the VRS.
5110 * @param offUdfBootVolDesc The offset of the BOOT2 descriptor, UINT64_MAX
5111 * if not encountered.
5112 * @param pbBuf Buffer for reading into.
5113 * @param cbBuf The size of the buffer. At least one sector.
5114 * @param pErrInfo Where to return extended error info.
5115 */
5116static int rtFsIsoVolHandleUdfDetection(PRTFSISOVOL pThis, uint8_t *puUdfLevel, uint64_t offUdfBootVolDesc,
5117 uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
5118{
5119 NOREF(offUdfBootVolDesc);
5120
5121 /*
5122 * There are up to three anchor volume descriptor pointers that can give us
5123 * two different descriptor sequences each. Usually, the different AVDP
5124 * structures points to the same two sequences. The idea here is that
5125 * sectors may deteriorate and become unreadable, and we're supposed to try
5126 * out alternative sectors to get the job done. If we really took this
5127 * seriously, we could try read all sequences in parallel and use the
5128 * sectors that are good. However, we'll try keep things reasonably simple
5129 * since we'll most likely be reading from hard disks rather than optical
5130 * media.
5131 *
5132 * We keep track of which sequences we've processed so we don't try to do it
5133 * again when alternative AVDP sectors points to the same sequences.
5134 */
5135 pThis->Udf.uLevel = *puUdfLevel;
5136 RTFSISOSEENSEQENCES SeenSequences = { 0 };
5137 int rc1 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, 256 * pThis->cbSector, pbBuf, cbBuf,
5138 &SeenSequences, pErrInfo);
5139 if (RT_SUCCESS(rc1))
5140 return rc1;
5141
5142 int rc2 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - 256 * pThis->cbSector,
5143 pbBuf, cbBuf, &SeenSequences, pErrInfo);
5144 if (RT_SUCCESS(rc2))
5145 return rc2;
5146
5147 int rc3 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - pThis->cbSector,
5148 pbBuf, cbBuf, &SeenSequences, pErrInfo);
5149 if (RT_SUCCESS(rc3))
5150 return rc3;
5151
5152 /*
5153 * Return failure if the alternatives have been excluded.
5154 *
5155 * Note! The error info won't be correct here.
5156 */
5157 pThis->Udf.uLevel = *puUdfLevel = 0;
5158
5159 if (RTFSISO9660_F_IS_ONLY_TYPE(pThis->fFlags, RTFSISO9660_F_NO_UDF))
5160 return rc1 != VERR_NOT_FOUND ? rc1 : rc2 != VERR_NOT_FOUND ? rc2 : rc3;
5161 return VINF_SUCCESS;
5162}
5163
5164
5165
5166#ifdef LOG_ENABLED
5167
5168/** Logging helper. */
5169static size_t rtFsIsoVolGetStrippedLength(const char *pachField, size_t cchField)
5170{
5171 while (cchField > 0 && pachField[cchField - 1] == ' ')
5172 cchField--;
5173 return cchField;
5174}
5175
5176/** Logging helper. */
5177static char *rtFsIsoVolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
5178{
5179 /* Check the format by looking for zero bytes. ISO-9660 doesn't allow zeros.
5180 This doesn't have to be a UTF-16BE string. */
5181 size_t cFirstZeros = 0;
5182 size_t cSecondZeros = 0;
5183 for (size_t off = 0; off + 1 < cchField; off += 2)
5184 {
5185 cFirstZeros += pachField[off] == '\0';
5186 cSecondZeros += pachField[off + 1] == '\0';
5187 }
5188
5189 int rc = VINF_SUCCESS;
5190 char *pszTmp = &pszDst[10];
5191 size_t cchRet = 0;
5192 if (cFirstZeros > cSecondZeros)
5193 {
5194 /* UTF-16BE / UTC-2BE: */
5195 if (cchField & 1)
5196 {
5197 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
5198 cchField--;
5199 else
5200 rc = VERR_INVALID_UTF16_ENCODING;
5201 }
5202 if (RT_SUCCESS(rc))
5203 {
5204 while ( cchField >= 2
5205 && pachField[cchField - 1] == ' '
5206 && pachField[cchField - 2] == '\0')
5207 cchField -= 2;
5208
5209 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
5210 }
5211 if (RT_SUCCESS(rc))
5212 {
5213 pszDst[0] = 'U';
5214 pszDst[1] = 'T';
5215 pszDst[2] = 'F';
5216 pszDst[3] = '-';
5217 pszDst[4] = '1';
5218 pszDst[5] = '6';
5219 pszDst[6] = 'B';
5220 pszDst[7] = 'E';
5221 pszDst[8] = ':';
5222 pszDst[9] = '\'';
5223 pszDst[10 + cchRet] = '\'';
5224 pszDst[10 + cchRet + 1] = '\0';
5225 }
5226 else
5227 RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
5228 }
5229 else if (cSecondZeros > 0)
5230 {
5231 /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
5232 if (cchField & 1)
5233 {
5234 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
5235 cchField--;
5236 else
5237 rc = VERR_INVALID_UTF16_ENCODING;
5238 }
5239 if (RT_SUCCESS(rc))
5240 {
5241 while ( cchField >= 2
5242 && pachField[cchField - 1] == '\0'
5243 && pachField[cchField - 2] == ' ')
5244 cchField -= 2;
5245
5246 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
5247 }
5248 if (RT_SUCCESS(rc))
5249 {
5250 pszDst[0] = 'U';
5251 pszDst[1] = 'T';
5252 pszDst[2] = 'F';
5253 pszDst[3] = '-';
5254 pszDst[4] = '1';
5255 pszDst[5] = '6';
5256 pszDst[6] = 'L';
5257 pszDst[7] = 'E';
5258 pszDst[8] = ':';
5259 pszDst[9] = '\'';
5260 pszDst[10 + cchRet] = '\'';
5261 pszDst[10 + cchRet + 1] = '\0';
5262 }
5263 else
5264 RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
5265 }
5266 else
5267 {
5268 /* ASSUME UTF-8/ASCII. */
5269 while ( cchField > 0
5270 && pachField[cchField - 1] == ' ')
5271 cchField--;
5272 rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
5273 if (RT_SUCCESS(rc))
5274 RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
5275 else
5276 RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
5277 }
5278 return pszDst;
5279}
5280
5281
5282/**
5283 * Logs the primary or supplementary volume descriptor
5284 *
5285 * @param pVolDesc The descriptor.
5286 */
5287static void rtFsIsoVolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
5288{
5289 if (LogIs2Enabled())
5290 {
5291 char szTmp[384];
5292 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
5293 Log2(("ISO9660: achSystemId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
5294 Log2(("ISO9660: achVolumeId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
5295 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
5296 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
5297 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIsoVolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
5298 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
5299 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
5300 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
5301 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
5302 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
5303 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
5304 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
5305 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
5306 Log2(("ISO9660: achVolumeSetId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
5307 Log2(("ISO9660: achPublisherId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
5308 Log2(("ISO9660: achDataPreparerId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
5309 Log2(("ISO9660: achApplicationId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
5310 Log2(("ISO9660: achCopyrightFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
5311 Log2(("ISO9660: achAbstractFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
5312 Log2(("ISO9660: achBibliographicFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
5313 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5314 pVolDesc->BirthTime.achYear,
5315 pVolDesc->BirthTime.achMonth,
5316 pVolDesc->BirthTime.achDay,
5317 pVolDesc->BirthTime.achHour,
5318 pVolDesc->BirthTime.achMinute,
5319 pVolDesc->BirthTime.achSecond,
5320 pVolDesc->BirthTime.achCentisecond,
5321 pVolDesc->BirthTime.offUtc*4/60));
5322 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5323 pVolDesc->ModifyTime.achYear,
5324 pVolDesc->ModifyTime.achMonth,
5325 pVolDesc->ModifyTime.achDay,
5326 pVolDesc->ModifyTime.achHour,
5327 pVolDesc->ModifyTime.achMinute,
5328 pVolDesc->ModifyTime.achSecond,
5329 pVolDesc->ModifyTime.achCentisecond,
5330 pVolDesc->ModifyTime.offUtc*4/60));
5331 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5332 pVolDesc->ExpireTime.achYear,
5333 pVolDesc->ExpireTime.achMonth,
5334 pVolDesc->ExpireTime.achDay,
5335 pVolDesc->ExpireTime.achHour,
5336 pVolDesc->ExpireTime.achMinute,
5337 pVolDesc->ExpireTime.achSecond,
5338 pVolDesc->ExpireTime.achCentisecond,
5339 pVolDesc->ExpireTime.offUtc*4/60));
5340 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5341 pVolDesc->EffectiveTime.achYear,
5342 pVolDesc->EffectiveTime.achMonth,
5343 pVolDesc->EffectiveTime.achDay,
5344 pVolDesc->EffectiveTime.achHour,
5345 pVolDesc->EffectiveTime.achMinute,
5346 pVolDesc->EffectiveTime.achSecond,
5347 pVolDesc->EffectiveTime.achCentisecond,
5348 pVolDesc->EffectiveTime.offUtc*4/60));
5349 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
5350 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
5351
5352 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
5353 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
5354 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
5355 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
5356 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
5357 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
5358 pVolDesc->RootDir.DirRec.RecTime.bMonth,
5359 pVolDesc->RootDir.DirRec.RecTime.bDay,
5360 pVolDesc->RootDir.DirRec.RecTime.bHour,
5361 pVolDesc->RootDir.DirRec.RecTime.bMinute,
5362 pVolDesc->RootDir.DirRec.RecTime.bSecond,
5363 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
5364 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
5365 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
5366 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
5367 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
5368 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
5369 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
5370 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
5371 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
5372 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
5373 {
5374 Log2(("ISO9660: RootDir System Use:\n%.*RhxD\n",
5375 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
5376 }
5377 }
5378}
5379
5380#endif /* LOG_ENABLED */
5381
5382/**
5383 * Deal with a root directory from a primary or supplemental descriptor.
5384 *
5385 * @returns IPRT status code.
5386 * @param pThis The ISO 9660 instance being initialized.
5387 * @param pRootDir The root directory record to check out.
5388 * @param pDstRootDir Where to store a copy of the root dir record.
5389 * @param pErrInfo Where to return additional error info. Can be NULL.
5390 */
5391static int rtFsIsoVolHandleRootDir(PRTFSISOVOL pThis, PCISO9660DIRREC pRootDir,
5392 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
5393{
5394 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
5395 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
5396 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
5397
5398 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
5399 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5400 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
5401 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
5402 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5403 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
5404
5405 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
5406 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
5407 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
5408 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
5409 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
5410
5411 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
5412 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
5413 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
5414
5415 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
5416 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
5417 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
5418 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
5419 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5420 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
5421 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
5422
5423 /*
5424 * Seems okay, copy it.
5425 */
5426 *pDstRootDir = *pRootDir;
5427 return VINF_SUCCESS;
5428}
5429
5430
5431/**
5432 * Deal with a primary volume descriptor.
5433 *
5434 * @returns IPRT status code.
5435 * @param pThis The ISO 9660 instance being initialized.
5436 * @param pVolDesc The volume descriptor to handle.
5437 * @param offVolDesc The disk offset of the volume descriptor.
5438 * @param pRootDir Where to return a copy of the root directory record.
5439 * @param poffRootDirRec Where to return the disk offset of the root dir.
5440 * @param pErrInfo Where to return additional error info. Can be NULL.
5441 */
5442static int rtFsIsoVolHandlePrimaryVolDesc(PRTFSISOVOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
5443 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
5444{
5445 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
5446 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5447 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
5448
5449 /*
5450 * We need the block size ...
5451 */
5452 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
5453 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
5454 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
5455 || pThis->cbBlock / pThis->cbSector < 1)
5456 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
5457 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
5458 if (pThis->cbBlock / pThis->cbSector > 128)
5459 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
5460
5461 /*
5462 * ... volume space size ...
5463 */
5464 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
5465 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
5466 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
5467 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
5468 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
5469
5470 /*
5471 * ... number of volumes in the set ...
5472 */
5473 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
5474 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
5475 || pThis->cVolumesInSet == 0)
5476 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
5477 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
5478 if (pThis->cVolumesInSet > 32)
5479 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
5480
5481 /*
5482 * ... primary volume sequence ID ...
5483 */
5484 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
5485 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
5486 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
5487 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
5488 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
5489 || pThis->idPrimaryVol < 1)
5490 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5491 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
5492
5493 /*
5494 * ... and the root directory record.
5495 */
5496 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
5497 return rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
5498}
5499
5500
5501/**
5502 * Deal with a supplementary 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 pbUcs2Level Where to return the joliet level, if found. Caller
5509 * initializes this to zero, we'll return 1, 2 or 3 if
5510 * joliet was detected.
5511 * @param pRootDir Where to return the root directory, if found.
5512 * @param poffRootDirRec Where to return the disk offset of the root dir.
5513 * @param pErrInfo Where to return additional error info. Can be NULL.
5514 */
5515static int rtFsIsoVolHandleSupplementaryVolDesc(PRTFSISOVOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
5516 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
5517 PRTERRINFO pErrInfo)
5518{
5519 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
5520 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5521 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
5522
5523 /*
5524 * Is this a joliet volume descriptor? If not, we probably don't need to
5525 * care about it.
5526 */
5527 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
5528 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
5529 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
5530 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
5531 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
5532 return VINF_SUCCESS;
5533
5534 /*
5535 * Skip if joliet is unwanted.
5536 */
5537 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
5538 return VINF_SUCCESS;
5539
5540 /*
5541 * Check that the joliet descriptor matches the primary one.
5542 * Note! These are our assumptions and may be wrong.
5543 */
5544 if (pThis->cbBlock == 0)
5545 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5546 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
5547 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
5548 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5549 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
5550 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
5551 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
5552 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5553 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
5554 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
5555 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
5556 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5557 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
5558 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
5559 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
5560 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5561 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
5562 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
5563
5564 if (*pbUcs2Level != 0)
5565 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
5566
5567 /*
5568 * Switch to the joliet root dir as it has UTF-16 stuff in it.
5569 */
5570 int rc = rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
5571 if (RT_SUCCESS(rc))
5572 {
5573 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
5574 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
5575 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
5576 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
5577 }
5578 return rc;
5579}
5580
5581
5582
5583/**
5584 * Worker for RTFsIso9660VolOpen.
5585 *
5586 * @returns IPRT status code.
5587 * @param pThis The ISO VFS instance to initialize.
5588 * @param hVfsSelf The ISO VFS handle (no reference consumed).
5589 * @param hVfsBacking The file backing the alleged ISO file system.
5590 * Reference is consumed (via rtFsIsoVol_Close).
5591 * @param fFlags Flags, RTFSISO9660_F_XXX.
5592 * @param pErrInfo Where to return additional error info. Can be NULL.
5593 */
5594static int rtFsIsoVolTryInit(PRTFSISOVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
5595{
5596 uint32_t const cbSector = 2048;
5597
5598 /*
5599 * First initialize the state so that rtFsIsoVol_Destroy won't trip up.
5600 */
5601 pThis->hVfsSelf = hVfsSelf;
5602 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIsoVol_Destroy releases it. */
5603 pThis->cbBacking = 0;
5604 pThis->cBackingSectors = 0;
5605 pThis->fFlags = fFlags;
5606 pThis->cbSector = cbSector;
5607 pThis->cbBlock = 0;
5608 pThis->cBlocksInPrimaryVolumeSpace = 0;
5609 pThis->cbPrimaryVolumeSpace = 0;
5610 pThis->cVolumesInSet = 0;
5611 pThis->idPrimaryVol = UINT32_MAX;
5612 pThis->fIsUtf16 = false;
5613 pThis->pRootDir = NULL;
5614
5615 /*
5616 * Get stuff that may fail.
5617 */
5618 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
5619 if (RT_SUCCESS(rc))
5620 pThis->cBackingSectors = pThis->cbBacking / pThis->cbSector;
5621 else
5622 return rc;
5623
5624 /*
5625 * Read the volume descriptors starting at logical sector 16.
5626 */
5627 union
5628 {
5629 uint8_t ab[RTFSISO_MAX_LOGICAL_BLOCK_SIZE];
5630 uint16_t au16[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 2];
5631 uint32_t au32[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 4];
5632 ISO9660VOLDESCHDR VolDescHdr;
5633 ISO9660BOOTRECORD BootRecord;
5634 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
5635 ISO9660SUPVOLDESC SupVolDesc;
5636 ISO9660VOLPARTDESC VolPartDesc;
5637 } Buf;
5638 RT_ZERO(Buf);
5639
5640 uint64_t offRootDirRec = UINT64_MAX;
5641 ISO9660DIRREC RootDir;
5642 RT_ZERO(RootDir);
5643
5644 uint64_t offJolietRootDirRec = UINT64_MAX;
5645 uint8_t bJolietUcs2Level = 0;
5646 ISO9660DIRREC JolietRootDir;
5647 RT_ZERO(JolietRootDir);
5648
5649 uint8_t uUdfLevel = 0;
5650 uint64_t offUdfBootVolDesc = UINT64_MAX;
5651
5652 uint32_t cPrimaryVolDescs = 0;
5653 uint32_t cSupplementaryVolDescs = 0;
5654 uint32_t cBootRecordVolDescs = 0;
5655 uint32_t offVolDesc = 16 * cbSector;
5656 enum
5657 {
5658 kStateStart = 0,
5659 kStateNoSeq,
5660 kStateCdSeq,
5661 kStateUdfSeq
5662 } enmState = kStateStart;
5663 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
5664 {
5665 if (iVolDesc > 32)
5666 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "More than 32 volume descriptors, doesn't seem right...");
5667
5668 /* Read the next one and check the signature. */
5669 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
5670 if (RT_FAILURE(rc))
5671 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
5672
5673#define MATCH_STD_ID(a_achStdId1, a_szStdId2) \
5674 ( (a_achStdId1)[0] == (a_szStdId2)[0] \
5675 && (a_achStdId1)[1] == (a_szStdId2)[1] \
5676 && (a_achStdId1)[2] == (a_szStdId2)[2] \
5677 && (a_achStdId1)[3] == (a_szStdId2)[3] \
5678 && (a_achStdId1)[4] == (a_szStdId2)[4] )
5679#define MATCH_HDR(a_pStd, a_bType2, a_szStdId2, a_bVer2) \
5680 ( MATCH_STD_ID((a_pStd)->achStdId, a_szStdId2) \
5681 && (a_pStd)->bDescType == (a_bType2) \
5682 && (a_pStd)->bDescVersion == (a_bVer2) )
5683
5684 /*
5685 * ISO 9660 ("CD001").
5686 */
5687 if ( ( enmState == kStateStart
5688 || enmState == kStateCdSeq
5689 || enmState == kStateNoSeq)
5690 && MATCH_STD_ID(Buf.VolDescHdr.achStdId, ISO9660VOLDESC_STD_ID) )
5691 {
5692 enmState = kStateCdSeq;
5693
5694 /* Do type specific handling. */
5695 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
5696 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
5697 {
5698 cPrimaryVolDescs++;
5699 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
5700 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5701 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
5702#ifdef LOG_ENABLED
5703 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
5704#endif
5705 if (cPrimaryVolDescs > 1)
5706 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
5707 rc = rtFsIsoVolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
5708 }
5709 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
5710 {
5711 cSupplementaryVolDescs++;
5712 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
5713 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5714 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
5715#ifdef LOG_ENABLED
5716 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
5717#endif
5718 rc = rtFsIsoVolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
5719 &offJolietRootDirRec, pErrInfo);
5720 }
5721 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
5722 {
5723 cBootRecordVolDescs++;
5724 }
5725 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
5726 {
5727 if (!cPrimaryVolDescs)
5728 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
5729 enmState = kStateNoSeq;
5730 }
5731 else
5732 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5733 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
5734 }
5735 /*
5736 * UDF volume recognition sequence (VRS).
5737 */
5738 else if ( ( enmState == kStateNoSeq
5739 || enmState == kStateStart)
5740 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BEGIN, UDF_EXT_VOL_DESC_VERSION) )
5741 {
5742 if (uUdfLevel == 0)
5743 enmState = kStateUdfSeq;
5744 else
5745 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BEA01 sequence is supported");
5746 }
5747 else if ( enmState == kStateUdfSeq
5748 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_02, UDF_EXT_VOL_DESC_VERSION) )
5749 uUdfLevel = 2;
5750 else if ( enmState == kStateUdfSeq
5751 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_03, UDF_EXT_VOL_DESC_VERSION) )
5752 uUdfLevel = 3;
5753 else if ( enmState == kStateUdfSeq
5754 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BOOT, UDF_EXT_VOL_DESC_VERSION) )
5755 {
5756 if (offUdfBootVolDesc == UINT64_MAX)
5757 offUdfBootVolDesc = iVolDesc * cbSector;
5758 else
5759 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BOOT2 descriptor is supported");
5760 }
5761 else if ( enmState == kStateUdfSeq
5762 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_TERM, UDF_EXT_VOL_DESC_VERSION) )
5763 {
5764 if (uUdfLevel != 0)
5765 enmState = kStateNoSeq;
5766 else
5767 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Found BEA01 & TEA01, but no NSR02 or NSR03 descriptors");
5768 }
5769 /*
5770 * Unknown, probably the end.
5771 */
5772 else if (enmState == kStateNoSeq)
5773 break;
5774 else if (enmState == kStateStart)
5775 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5776 "Not ISO? Unable to recognize volume descriptor signature: %.5Rhxs", Buf.VolDescHdr.achStdId);
5777 else if (enmState == kStateCdSeq)
5778 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5779 "Missing ISO 9660 terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
5780 else if (enmState == kStateUdfSeq)
5781 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5782 "Missing UDF terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
5783 else
5784 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5785 "Unknown volume descriptor signature found at sector %u: %.5Rhxs",
5786 16 + iVolDesc, Buf.VolDescHdr.achStdId);
5787 if (RT_FAILURE(rc))
5788 return rc;
5789 }
5790
5791 /*
5792 * If we found a UDF VRS and are interested in UDF, we have more work to do here.
5793 */
5794 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF) )
5795 {
5796 Log(("rtFsIsoVolTryInit: uUdfLevel=%d\n", uUdfLevel));
5797 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
5798 if (RT_FAILURE(rc))
5799 return rc;
5800 }
5801
5802 /*
5803 * Decide which to prefer.
5804 *
5805 * By default we pick UDF over any of the two ISO 9960, there is currently
5806 * no way to override this without using the RTFSISO9660_F_NO_XXX options.
5807 *
5808 * If there isn't UDF, we may faced with choosing between joliet and rock
5809 * ridge. The joliet option is generally favorable as we don't have to
5810 * guess wrt to the file name encoding. So, we'll pick that for now.
5811 *
5812 * Note! Should we change this preference for joliet, there fun wrt making sure
5813 * there really is rock ridge stuff in the primary volume as well as
5814 * making sure there really is anything of value in the primary volume.
5815 */
5816 if (uUdfLevel > 0)
5817 {
5818 pThis->enmType = RTFSISOVOLTYPE_UDF;
5819 rc = rtFsIsoDirShrd_NewUdf(pThis, NULL /*pParent*/, &pThis->Udf.VolInfo.RootDirIcb,
5820 NULL /*pFileIdDesc*/, 0 /*offInDir*/, &pThis->pRootDir);
5821 /** @todo fall back on failure? */
5822 return rc;
5823 }
5824 if (bJolietUcs2Level != 0)
5825 {
5826 pThis->enmType = RTFSISOVOLTYPE_JOLIET;
5827 pThis->fIsUtf16 = true;
5828 return rtFsIsoDirShrd_New9660(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
5829 }
5830 pThis->enmType = RTFSISOVOLTYPE_ISO9960;
5831 return rtFsIsoDirShrd_New9660(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
5832}
5833
5834
5835/**
5836 * Opens an ISO 9660 file system volume.
5837 *
5838 * @returns IPRT status code.
5839 * @param hVfsFileIn The file or device backing the volume.
5840 * @param fFlags RTFSISO9660_F_XXX.
5841 * @param phVfs Where to return the virtual file system handle.
5842 * @param pErrInfo Where to return additional error information.
5843 */
5844RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
5845{
5846 /*
5847 * Quick input validation.
5848 */
5849 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
5850 *phVfs = NIL_RTVFS;
5851 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
5852
5853 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
5854 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5855
5856 /*
5857 * Create a new ISO VFS instance and try initialize it using the given input file.
5858 */
5859 RTVFS hVfs = NIL_RTVFS;
5860 void *pvThis = NULL;
5861 int rc = RTVfsNew(&g_rtFsIsoVolOps, sizeof(RTFSISOVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
5862 if (RT_SUCCESS(rc))
5863 {
5864 rc = rtFsIsoVolTryInit((PRTFSISOVOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
5865 if (RT_SUCCESS(rc))
5866 *phVfs = hVfs;
5867 else
5868 RTVfsRelease(hVfs);
5869 }
5870 else
5871 RTVfsFileRelease(hVfsFileIn);
5872 return rc;
5873}
5874
5875
5876/**
5877 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
5878 */
5879static DECLCALLBACK(int) rtVfsChainIsoFsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
5880 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
5881{
5882 RT_NOREF(pProviderReg, pSpec);
5883
5884 /*
5885 * Basic checks.
5886 */
5887 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
5888 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
5889 if ( pElement->enmType != RTVFSOBJTYPE_VFS
5890 && pElement->enmType != RTVFSOBJTYPE_DIR)
5891 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
5892 if (pElement->cArgs > 1)
5893 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
5894
5895 /*
5896 * Parse the flag if present, save in pElement->uProvider.
5897 */
5898 uint32_t fFlags = 0;
5899 if (pElement->cArgs > 0)
5900 {
5901 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
5902 {
5903 const char *psz = pElement->paArgs[iArg].psz;
5904 if (*psz)
5905 {
5906 if (!strcmp(psz, "nojoliet"))
5907 fFlags |= RTFSISO9660_F_NO_JOLIET;
5908 else if (!strcmp(psz, "norock"))
5909 fFlags |= RTFSISO9660_F_NO_ROCK;
5910 else if (!strcmp(psz, "noudf"))
5911 fFlags |= RTFSISO9660_F_NO_UDF;
5912 else
5913 {
5914 *poffError = pElement->paArgs[iArg].offSpec;
5915 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
5916 }
5917 }
5918 }
5919 }
5920
5921 pElement->uProvider = fFlags;
5922 return VINF_SUCCESS;
5923}
5924
5925
5926/**
5927 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
5928 */
5929static DECLCALLBACK(int) rtVfsChainIsoFsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
5930 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
5931 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
5932{
5933 RT_NOREF(pProviderReg, pSpec, poffError);
5934
5935 int rc;
5936 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
5937 if (hVfsFileIn != NIL_RTVFSFILE)
5938 {
5939 RTVFS hVfs;
5940 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
5941 RTVfsFileRelease(hVfsFileIn);
5942 if (RT_SUCCESS(rc))
5943 {
5944 *phVfsObj = RTVfsObjFromVfs(hVfs);
5945 RTVfsRelease(hVfs);
5946 if (*phVfsObj != NIL_RTVFSOBJ)
5947 return VINF_SUCCESS;
5948 rc = VERR_VFS_CHAIN_CAST_FAILED;
5949 }
5950 }
5951 else
5952 rc = VERR_VFS_CHAIN_CAST_FAILED;
5953 return rc;
5954}
5955
5956
5957/**
5958 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
5959 */
5960static DECLCALLBACK(bool) rtVfsChainIsoFsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
5961 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
5962 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
5963{
5964 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
5965 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
5966 || !pReuseElement->paArgs[0].uProvider)
5967 return true;
5968 return false;
5969}
5970
5971
5972/** VFS chain element 'file'. */
5973static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
5974{
5975 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
5976 /* fReserved = */ 0,
5977 /* pszName = */ "isofs",
5978 /* ListEntry = */ { NULL, NULL },
5979 /* pszHelp = */ "Open a ISO 9660 or UDF file system, requires a file object on the left side.\n"
5980 "The 'noudf' option make it ignore any UDF.\n"
5981 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
5982 "The 'norock' option make it ignore any rock ridge info.\n",
5983 /* pfnValidate = */ rtVfsChainIsoFsVol_Validate,
5984 /* pfnInstantiate = */ rtVfsChainIsoFsVol_Instantiate,
5985 /* pfnCanReuseElement = */ rtVfsChainIsoFsVol_CanReuseElement,
5986 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
5987};
5988
5989RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
5990
Note: See TracBrowser for help on using the repository browser.

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