VirtualBox

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

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

IPRT: VFS opening reworking in progress.

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