VirtualBox

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

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

IPRT: Enable UDF code. Fixed allocation bug.

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