VirtualBox

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

Last change on this file since 84207 was 84155, checked in by vboxsync, 5 years ago

IPRT/isovfs: Warning.

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

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