VirtualBox

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

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