VirtualBox

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

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