VirtualBox

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

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

UDF: Implemented directory listing. Works for root, other directories needs traversal and opendir impl.

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