VirtualBox

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

Last change on this file since 75410 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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

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