VirtualBox

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

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

iprt: udf updates

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

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