VirtualBox

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

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

IPRT/vfs: Made pfnSetMode, pfnSetTimes, pfnSetOwner and pfnWrite optional and fallback to VERR_WRITE_PROTECT.

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

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