VirtualBox

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

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

IPRT: UDF read support is mostly done.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 242.7 KB
Line 
1/* $Id: isovfs.cpp 69048 2017-10-11 16:34:05Z vboxsync $ */
2/** @file
3 * IPRT - ISO 9660 and UDF Virtual Filesystem (read only).
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsvfs.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/err.h>
38#include <iprt/crc.h>
39#include <iprt/ctype.h>
40#include <iprt/file.h>
41#include <iprt/log.h>
42#include <iprt/mem.h>
43#include <iprt/poll.h>
44#include <iprt/string.h>
45#include <iprt/thread.h>
46#include <iprt/vfs.h>
47#include <iprt/vfslowlevel.h>
48#include <iprt/uni.h>
49#include <iprt/formats/iso9660.h>
50#include <iprt/formats/udf.h>
51
52/** @todo move to err.h: */
53
54
55
56/*********************************************************************************************************************************
57* Defined Constants And Macros *
58*********************************************************************************************************************************/
59/** The maximum logical block size. */
60#define RTFSISO_MAX_LOGICAL_BLOCK_SIZE _16K
61/** Max directory size. */
62#if ARCH_BITS == 32
63# define RTFSISO_MAX_DIR_SIZE _32M
64#else
65# define RTFSISO_MAX_DIR_SIZE _64M
66#endif
67
68/** Check if an entity ID field equals the given ID string. */
69#define UDF_ENTITY_ID_EQUALS(a_pEntityId, a_szId) \
70 ( memcmp(&(a_pEntityId)->achIdentifier[0], a_szId, RT_MIN(sizeof(a_szId), sizeof(a_pEntityId)->achIdentifier)) == 0 )
71/** Checks if a character set indicator indicates OSTA compressed unicode. */
72#define UDF_IS_CHAR_SET_OSTA(a_pCharSet) \
73 ( (a_pCharSet)->uType == UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE \
74 && memcmp((a_pCharSet)->abInfo, UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO, \
75 sizeof(UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO)) == 0 )
76
77
78/** @name UDF structure logging macros
79 * @{ */
80#define UDF_LOG2_MEMBER(a_pStruct, a_szFmt, a_Member) \
81 Log2(("ISO/UDF: %-32s %" a_szFmt "\n", #a_Member ":", (a_pStruct)->a_Member))
82#define UDF_LOG2_MEMBER_EX(a_pStruct, a_szFmt, a_Member, a_cchIndent) \
83 Log2(("ISO/UDF: %*s%-32s %" a_szFmt "\n", a_cchIndent, "", #a_Member ":", (a_pStruct)->a_Member))
84#define UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, a_cchIndent) \
85 Log2(("ISO/UDF: %*s%-32s '%.23s' fFlags=%#06x Suffix=%.8Rhxs\n", a_cchIndent, "", #a_Member ":", \
86 (a_pStruct)->a_Member.achIdentifier, (a_pStruct)->a_Member.fFlags, &(a_pStruct)->a_Member.Suffix))
87#define UDF_LOG2_MEMBER_ENTITY_ID(a_pStruct, a_Member) UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, 0)
88#define UDF_LOG2_MEMBER_EXTENTAD(a_pStruct, a_Member) \
89 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb))
90#define UDF_LOG2_MEMBER_SHORTAD(a_pStruct, a_Member) \
91 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32 %s\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb, \
92 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
93 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
94 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next" ))
95#define UDF_LOG2_MEMBER_LONGAD(a_pStruct, a_Member) \
96 Log2(("ISO/UDF: %-32s partition %#RX16, block %#010RX32 LB %#010RX32 %s idUnique=%#010RX32 fFlags=%#RX16\n", #a_Member ":", \
97 (a_pStruct)->a_Member.Location.uPartitionNo, (a_pStruct)->a_Member.Location.off, (a_pStruct)->a_Member.cb, \
98 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
99 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
100 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next", \
101 (a_pStruct)->a_Member.ImplementationUse.Fid.idUnique, (a_pStruct)->a_Member.ImplementationUse.Fid.fFlags ))
102#define UDF_LOG2_MEMBER_LBADDR(a_pStruct, a_Member) \
103 Log2(("ISO/UDF: %-32s block %#010RX32 in partition %#06RX16\n", #a_Member ":", \
104 (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.uPartitionNo))
105
106#define UDF_LOG2_MEMBER_TIMESTAMP(a_pStruct, a_Member) \
107 Log2(("ISO/UDF: %-32s %04d-%02u-%02u %02u:%02u:%02u.%02u%02u%02u offUtc=%d type=%#x\n", #a_Member ":", \
108 (a_pStruct)->a_Member.iYear, (a_pStruct)->a_Member.uMonth, (a_pStruct)->a_Member.uDay, \
109 (a_pStruct)->a_Member.uHour, (a_pStruct)->a_Member.uMinute, (a_pStruct)->a_Member.uSecond, \
110 (a_pStruct)->a_Member.cCentiseconds, (a_pStruct)->a_Member.cHundredsOfMicroseconds, \
111 (a_pStruct)->a_Member.cMicroseconds, (a_pStruct)->a_Member.offUtcInMin, (a_pStruct)->a_Member.fType ))
112#define UDF_LOG2_MEMBER_CHARSPEC(a_pStruct, a_Member) \
113 do { \
114 if ( (a_pStruct)->a_Member.uType == UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE \
115 && memcmp(&(a_pStruct)->a_Member.abInfo[0], UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO, \
116 sizeof(UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO)) == 0) \
117 Log2(("ISO/UDF: %-32s OSTA COMPRESSED UNICODE INFO\n", #a_Member ":")); \
118 else if (ASMMemIsZero(&(a_pStruct)->a_Member, sizeof((a_pStruct)->a_Member))) \
119 Log2(("ISO/UDF: %-32s all zeros\n", #a_Member ":")); \
120 else \
121 Log2(("ISO/UDF: %-32s %#x info: %.63Rhxs\n", #a_Member ":", \
122 (a_pStruct)->a_Member.uType, (a_pStruct)->a_Member.abInfo)); \
123 } while (0)
124#define UDF_LOG2_MEMBER_DSTRING(a_pStruct, a_Member) \
125 do { \
126 if ((a_pStruct)->a_Member[0] == 8) \
127 Log2(("ISO/UDF: %-32s 8: '%s' len=%u (actual=%u)\n", #a_Member ":", &(a_pStruct)->a_Member[1], \
128 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], \
129 RTStrNLen(&(a_pStruct)->a_Member[1], sizeof((a_pStruct)->a_Member) - 2) + 1 )); \
130 else if ((a_pStruct)->a_Member[0] == 16) \
131 { \
132 PCRTUTF16 pwszTmp = (PCRTUTF16)&(a_pStruct)->a_Member[1]; \
133 char *pszTmp = NULL; \
134 RTUtf16BigToUtf8Ex(pwszTmp, (sizeof((a_pStruct)->a_Member) - 2) / sizeof(RTUTF16), &pszTmp, 0, NULL); \
135 Log2(("ISO/UDF: %-32s 16: '%s' len=%u (actual=%u)\n", #a_Member ":", pszTmp, \
136 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], \
137 RTUtf16NLen(pwszTmp, (sizeof((a_pStruct)->a_Member) - 2) / sizeof(RTUTF16)) * sizeof(RTUTF16) + 1 /*??*/ )); \
138 } \
139 else if (ASMMemIsZero(&(a_pStruct)->a_Member[0], sizeof((a_pStruct)->a_Member))) \
140 Log2(("ISO/UDF: %-32s empty\n", #a_Member ":")); \
141 else \
142 Log2(("ISO/UDF: %-32s bad: %.*Rhxs\n", #a_Member ":", sizeof((a_pStruct)->a_Member), &(a_pStruct)->a_Member[0] )); \
143 } while (0)
144/** @} */
145
146
147
148/*********************************************************************************************************************************
149* Structures and Typedefs *
150*********************************************************************************************************************************/
151/** Pointer to an ISO volume (VFS instance data). */
152typedef struct RTFSISOVOL *PRTFSISOVOL;
153/** Pointer to a const ISO volume (VFS instance data). */
154typedef struct RTFSISOVOL const *PCRTFSISOVOL;
155
156/** Pointer to a ISO directory instance. */
157typedef struct RTFSISODIRSHRD *PRTFSISODIRSHRD;
158
159
160
161/**
162 * ISO extent (internal to the VFS not a disk structure).
163 */
164typedef struct RTFSISOEXTENT
165{
166 /** The disk or partition byte offset.
167 * This is set to UINT64_MAX for parts of sparse files that aren't recorded.*/
168 uint64_t off;
169 /** The size of the extent in bytes. */
170 uint64_t cbExtent;
171 /** UDF virtual partition number, UINT32_MAX for ISO 9660. */
172 uint32_t idxPart;
173 /** Reserved. */
174 uint32_t uReserved;
175} RTFSISOEXTENT;
176/** Pointer to an ISO 9660 extent. */
177typedef RTFSISOEXTENT *PRTFSISOEXTENT;
178/** Pointer to a const ISO 9660 extent. */
179typedef RTFSISOEXTENT const *PCRTFSISOEXTENT;
180
181
182/**
183 * ISO file system object, shared part.
184 */
185typedef struct RTFSISOCORE
186{
187 /** The parent directory keeps a list of open objects (RTFSISOCORE). */
188 RTLISTNODE Entry;
189 /** Reference counter. */
190 uint32_t volatile cRefs;
191 /** The parent directory (not released till all children are close). */
192 PRTFSISODIRSHRD pParentDir;
193 /** The byte offset of the first directory record.
194 * This is used when looking up objects in a directory to avoid creating
195 * duplicate instances. */
196 uint64_t offDirRec;
197 /** Attributes. */
198 RTFMODE fAttrib;
199 /** The object size. */
200 uint64_t cbObject;
201 /** The access time. */
202 RTTIMESPEC AccessTime;
203 /** The modificaton time. */
204 RTTIMESPEC ModificationTime;
205 /** The change time. */
206 RTTIMESPEC ChangeTime;
207 /** The birth time. */
208 RTTIMESPEC BirthTime;
209 /** The i-node ID. */
210 RTINODE idINode;
211 /** Pointer to the volume. */
212 PRTFSISOVOL pVol;
213 /** The version number. */
214 uint32_t uVersion;
215 /** Number of extents. */
216 uint32_t cExtents;
217 /** The first extent. */
218 RTFSISOEXTENT FirstExtent;
219 /** Array of additional extents. */
220 PRTFSISOEXTENT paExtents;
221} RTFSISOCORE;
222typedef RTFSISOCORE *PRTFSISOCORE;
223
224/**
225 * ISO file, shared data.
226 */
227typedef struct RTFSISOFILESHRD
228{
229 /** Core ISO9660 object info. */
230 RTFSISOCORE Core;
231} RTFSISOFILESHRD;
232/** Pointer to a ISO 9660 file object. */
233typedef RTFSISOFILESHRD *PRTFSISOFILESHRD;
234
235
236/**
237 * ISO directory, shared data.
238 *
239 * We will always read in the whole directory just to keep things really simple.
240 */
241typedef struct RTFSISODIRSHRD
242{
243 /** Core ISO 9660 object info. */
244 RTFSISOCORE Core;
245 /** Open child objects (RTFSISOCORE). */
246 RTLISTNODE OpenChildren;
247
248 /** Pointer to the directory content. */
249 uint8_t *pbDir;
250 /** The size of the directory content (duplicate of Core.cbObject). */
251 uint32_t cbDir;
252} RTFSISODIRSHRD;
253/** Pointer to a ISO directory instance. */
254typedef RTFSISODIRSHRD *PRTFSISODIRSHRD;
255
256
257/**
258 * Private data for a VFS file object.
259 */
260typedef struct RTFSISOFILEOBJ
261{
262 /** Pointer to the shared data. */
263 PRTFSISOFILESHRD pShared;
264 /** The current file offset. */
265 uint64_t offFile;
266} RTFSISOFILEOBJ;
267typedef RTFSISOFILEOBJ *PRTFSISOFILEOBJ;
268
269/**
270 * Private data for a VFS directory object.
271 */
272typedef struct RTFSISODIROBJ
273{
274 /** Pointer to the shared data. */
275 PRTFSISODIRSHRD pShared;
276 /** The current directory offset. */
277 uint32_t offDir;
278} RTFSISODIROBJ;
279typedef RTFSISODIROBJ *PRTFSISODIROBJ;
280
281/** Pointer to info about a UDF volume. */
282typedef struct RTFSISOUDFVOLINFO *PRTFSISOUDFVOLINFO;
283
284
285/** @name RTFSISO_UDF_PMAP_T_XXX
286 * @{ */
287#define RTFSISO_UDF_PMAP_T_PLAIN 1
288#define RTFSISO_UDF_PMAP_T_VPM_15 2
289#define RTFSISO_UDF_PMAP_T_VPM_20 3
290#define RTFSISO_UDF_PMAP_T_SPM 4
291#define RTFSISO_UDF_PMAP_T_MPM 5
292/** @} */
293
294/**
295 * Information about a logical UDF partition.
296 *
297 * This combins information from the partition descriptor, the UDFPARTMAPTYPE1
298 * and the UDFPARTMAPTYPE2 structure.
299 */
300typedef struct RTFSISOVOLUDFPMAP
301{
302 /** Partition starting location as a byte offset. */
303 uint64_t offByteLocation;
304 /** Partition starting location (logical sector number). */
305 uint32_t offLocation;
306 /** Number of sectors. */
307 uint32_t cSectors;
308
309 /** Partition descriptor index (for processing). */
310 uint16_t idxPartDesc;
311 /** Offset info the map table. */
312 uint16_t offMapTable;
313 /** Partition number (not index). */
314 uint16_t uPartitionNo;
315 /** Partition number (not index). */
316 uint16_t uVolumeSeqNo;
317
318 /** The access type (UDF_PART_ACCESS_TYPE_XXX). */
319 uint32_t uAccessType;
320 /** Partition flags (UDF_PARTITION_FLAGS_XXX). */
321 uint16_t fFlags;
322 /** RTFSISO_UDF_PMAP_T_XXX. */
323 uint8_t bType;
324 /** Set if Hdr is valid. */
325 bool fHaveHdr;
326 /** Copy of UDFPARTITIONDESC::ContentsUse::Hdr. */
327 UDFPARTITIONHDRDESC Hdr;
328
329} RTFSISOVOLUDFPMAP;
330typedef RTFSISOVOLUDFPMAP *PRTFSISOVOLUDFPMAP;
331
332/**
333 * Information about a UDF volume (/ volume set).
334 *
335 * This combines information from the primary and logical descriptors.
336 *
337 * @note There is only one volume per volume set in the current UDF
338 * implementation. So, this can be considered a volume and a volume set.
339 */
340typedef struct RTFSISOUDFVOLINFO
341{
342 /** The extent containing the file set descriptor. */
343 UDFLONGAD FileSetDescriptor;
344
345 /** The root directory location (from the file set descriptor). */
346 UDFLONGAD RootDirIcb;
347 /** Location of the system stream directory associated with the file set. */
348 UDFLONGAD SystemStreamDirIcb;
349
350 /** The logical block size on this volume. */
351 uint32_t cbBlock;
352 /** The log2 of cbBlock. */
353 uint32_t cShiftBlock;
354 /** Flags (UDF_PVD_FLAGS_XXX). */
355 uint16_t fFlags;
356
357 /** Number of partitions mapp in this volume. */
358 uint16_t cPartitions;
359 /** Partitions in this volume. */
360 PRTFSISOVOLUDFPMAP paPartitions;
361
362 /** The volume ID string. */
363 UDFDSTRING achLogicalVolumeID[128];
364} RTFSISOUDFVOLINFO;
365
366
367/**
368 * Indicates which of the possible content types we're accessing.
369 */
370typedef enum RTFSISOVOLTYPE
371{
372 /** Accessing the primary ISO-9660 volume. */
373 RTFSISOVOLTYPE_ISO9960 = 0,
374 /** Accessing the joliet volume (secondary ISO-9660). */
375 RTFSISOVOLTYPE_JOLIET,
376 /** Accessing the UDF volume. */
377 RTFSISOVOLTYPE_UDF
378} RTFSISOVOLTYPE;
379
380/**
381 * A ISO volume.
382 */
383typedef struct RTFSISOVOL
384{
385 /** Handle to itself. */
386 RTVFS hVfsSelf;
387 /** The file, partition, or whatever backing the ISO 9660 volume. */
388 RTVFSFILE hVfsBacking;
389 /** The size of the backing thingy. */
390 uint64_t cbBacking;
391 /** The size of the backing thingy in sectors (cbSector). */
392 uint64_t cBackingSectors;
393 /** Flags. */
394 uint32_t fFlags;
395 /** The sector size (in bytes). */
396 uint32_t cbSector;
397 /** What we're accessing. */
398 RTFSISOVOLTYPE enmType;
399
400 /** @name ISO 9660 specific data
401 * @{ */
402 /** The size of a logical block in bytes. */
403 uint32_t cbBlock;
404 /** The primary volume space size in blocks. */
405 uint32_t cBlocksInPrimaryVolumeSpace;
406 /** The primary volume space size in bytes. */
407 uint64_t cbPrimaryVolumeSpace;
408 /** The number of volumes in the set. */
409 uint32_t cVolumesInSet;
410 /** The primary volume sequence ID. */
411 uint32_t idPrimaryVol;
412 /** Set if using UTF16-2 (joliet). */
413 bool fIsUtf16;
414 /** @} */
415
416 /** UDF specific data. */
417 struct
418 {
419 /** Volume information. */
420 RTFSISOUDFVOLINFO VolInfo;
421 /** The UDF level. */
422 uint8_t uLevel;
423 } Udf;
424
425 /** The root directory shared data. */
426 PRTFSISODIRSHRD pRootDir;
427} RTFSISOVOL;
428
429
430/**
431 * Info gathered from a VDS sequence.
432 */
433typedef struct RTFSISOVDSINFO
434{
435 /** Number of entries in apPrimaryVols. */
436 uint32_t cPrimaryVols;
437 /** Number of entries in apLogicalVols. */
438 uint32_t cLogicalVols;
439 /** Number of entries in apPartitions. */
440 uint32_t cPartitions;
441 /** Pointer to primary volume descriptors (native endian). */
442 PUDFPRIMARYVOLUMEDESC apPrimaryVols[8];
443 /** Pointer to logical volume descriptors (native endian). */
444 PUDFLOGICALVOLUMEDESC apLogicalVols[8];
445 /** Pointer to partition descriptors (native endian). */
446 PUDFPARTITIONDESC apPartitions[16];
447
448 /** Created after scanning the sequence (here for cleanup purposes). */
449 PRTFSISOVOLUDFPMAP paPartMaps;
450} RTFSISOVDSINFO;
451/** Pointer to VDS sequence info. */
452typedef RTFSISOVDSINFO *PRTFSISOVDSINFO;
453
454
455
456/*********************************************************************************************************************************
457* Global Variables *
458*********************************************************************************************************************************/
459
460/*********************************************************************************************************************************
461* Internal Functions *
462*********************************************************************************************************************************/
463static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
464static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
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 size_t cbSkip = (offDirRec + pVol->cbSector) & ~(pVol->cbSector - 1U);
837 offDirRec += cbSkip;
838 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + 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_OFFSETOF(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_OFFSETOF(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_OFFSETOF(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_OFFSETOF(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,pfnWrite}
2032 */
2033static DECLCALLBACK(int) rtFsIsoFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
2034{
2035 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
2036 return VERR_WRITE_PROTECT;
2037}
2038
2039
2040/**
2041 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
2042 */
2043static DECLCALLBACK(int) rtFsIsoFile_Flush(void *pvThis)
2044{
2045 RT_NOREF(pvThis);
2046 return VINF_SUCCESS;
2047}
2048
2049
2050/**
2051 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
2052 */
2053static DECLCALLBACK(int) rtFsIsoFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
2054 uint32_t *pfRetEvents)
2055{
2056 NOREF(pvThis);
2057 int rc;
2058 if (fEvents != RTPOLL_EVT_ERROR)
2059 {
2060 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
2061 rc = VINF_SUCCESS;
2062 }
2063 else if (fIntr)
2064 rc = RTThreadSleep(cMillies);
2065 else
2066 {
2067 uint64_t uMsStart = RTTimeMilliTS();
2068 do
2069 rc = RTThreadSleep(cMillies);
2070 while ( rc == VERR_INTERRUPTED
2071 && !fIntr
2072 && RTTimeMilliTS() - uMsStart < cMillies);
2073 if (rc == VERR_INTERRUPTED)
2074 rc = VERR_TIMEOUT;
2075 }
2076 return rc;
2077}
2078
2079
2080/**
2081 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
2082 */
2083static DECLCALLBACK(int) rtFsIsoFile_Tell(void *pvThis, PRTFOFF poffActual)
2084{
2085 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2086 *poffActual = pThis->offFile;
2087 return VINF_SUCCESS;
2088}
2089
2090
2091/**
2092 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2093 */
2094static DECLCALLBACK(int) rtFsIsoFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2095{
2096 RT_NOREF(pvThis, fMode, fMask);
2097 return VERR_WRITE_PROTECT;
2098}
2099
2100
2101/**
2102 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2103 */
2104static DECLCALLBACK(int) rtFsIsoFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2105 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2106{
2107 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2108 return VERR_WRITE_PROTECT;
2109}
2110
2111
2112/**
2113 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2114 */
2115static DECLCALLBACK(int) rtFsIsoFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2116{
2117 RT_NOREF(pvThis, uid, gid);
2118 return VERR_WRITE_PROTECT;
2119}
2120
2121
2122/**
2123 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
2124 */
2125static DECLCALLBACK(int) rtFsIsoFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
2126{
2127 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2128 RTFOFF offNew;
2129 switch (uMethod)
2130 {
2131 case RTFILE_SEEK_BEGIN:
2132 offNew = offSeek;
2133 break;
2134 case RTFILE_SEEK_END:
2135 offNew = (RTFOFF)pThis->pShared->Core.cbObject + offSeek;
2136 break;
2137 case RTFILE_SEEK_CURRENT:
2138 offNew = (RTFOFF)pThis->offFile + offSeek;
2139 break;
2140 default:
2141 return VERR_INVALID_PARAMETER;
2142 }
2143 if (offNew >= 0)
2144 {
2145 if (offNew <= _4G)
2146 {
2147 pThis->offFile = offNew;
2148 *poffActual = offNew;
2149 return VINF_SUCCESS;
2150 }
2151 return VERR_OUT_OF_RANGE;
2152 }
2153 return VERR_NEGATIVE_SEEK;
2154}
2155
2156
2157/**
2158 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
2159 */
2160static DECLCALLBACK(int) rtFsIsoFile_QuerySize(void *pvThis, uint64_t *pcbFile)
2161{
2162 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2163 *pcbFile = pThis->pShared->Core.cbObject;
2164 return VINF_SUCCESS;
2165}
2166
2167
2168/**
2169 * ISO FS file operations.
2170 */
2171DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIsoFileOps =
2172{
2173 { /* Stream */
2174 { /* Obj */
2175 RTVFSOBJOPS_VERSION,
2176 RTVFSOBJTYPE_FILE,
2177 "FatFile",
2178 rtFsIsoFile_Close,
2179 rtFsIsoFile_QueryInfo,
2180 RTVFSOBJOPS_VERSION
2181 },
2182 RTVFSIOSTREAMOPS_VERSION,
2183 RTVFSIOSTREAMOPS_FEAT_NO_SG,
2184 rtFsIsoFile_Read,
2185 rtFsIsoFile_Write,
2186 rtFsIsoFile_Flush,
2187 rtFsIsoFile_PollOne,
2188 rtFsIsoFile_Tell,
2189 NULL /*pfnSkip*/,
2190 NULL /*pfnZeroFill*/,
2191 RTVFSIOSTREAMOPS_VERSION,
2192 },
2193 RTVFSFILEOPS_VERSION,
2194 0,
2195 { /* ObjSet */
2196 RTVFSOBJSETOPS_VERSION,
2197 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
2198 rtFsIsoFile_SetMode,
2199 rtFsIsoFile_SetTimes,
2200 rtFsIsoFile_SetOwner,
2201 RTVFSOBJSETOPS_VERSION
2202 },
2203 rtFsIsoFile_Seek,
2204 rtFsIsoFile_QuerySize,
2205 RTVFSFILEOPS_VERSION
2206};
2207
2208
2209/**
2210 * Instantiates a new file, from ISO 9660 info.
2211 *
2212 * @returns IPRT status code.
2213 * @param pThis The FAT volume instance.
2214 * @param pParentDir The parent directory (shared part).
2215 * @param pDirRec The directory record.
2216 * @param cDirRecs Number of directory records if more than one.
2217 * @param offDirRec The byte offset of the directory record.
2218 * @param offEntryInDir The byte offset of the directory entry in the parent
2219 * directory.
2220 * @param fOpen RTFILE_O_XXX flags.
2221 * @param uVersion The file version number (since the caller already
2222 * parsed the filename, we don't want to repeat the
2223 * effort here).
2224 * @param phVfsFile Where to return the file handle.
2225 */
2226static int rtFsIsoFile_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
2227 uint64_t offDirRec, uint64_t fOpen, uint32_t uVersion, PRTVFSFILE phVfsFile)
2228{
2229 AssertPtr(pParentDir);
2230
2231 /*
2232 * Create a VFS object.
2233 */
2234 PRTFSISOFILEOBJ pNewFile;
2235 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2236 phVfsFile, (void **)&pNewFile);
2237 if (RT_SUCCESS(rc))
2238 {
2239 /*
2240 * Look for existing shared object, create a new one if necessary.
2241 */
2242 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
2243 if (pShared)
2244 {
2245 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2246 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2247 pNewFile->offFile = 0;
2248 pNewFile->pShared = pShared;
2249 return VINF_SUCCESS;
2250 }
2251
2252 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2253 if (pShared)
2254 {
2255 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, uVersion, pThis);
2256 if (RT_SUCCESS(rc))
2257 {
2258 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2259 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2260 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2261 pNewFile->offFile = 0;
2262 pNewFile->pShared = pShared;
2263 return VINF_SUCCESS;
2264 }
2265 RTMemFree(pShared);
2266 }
2267 else
2268 rc = VERR_NO_MEMORY;
2269
2270 /* Destroy the file object. */
2271 pNewFile->offFile = 0;
2272 pNewFile->pShared = NULL;
2273 RTVfsFileRelease(*phVfsFile);
2274 }
2275 *phVfsFile = NIL_RTVFSFILE;
2276 return rc;
2277}
2278
2279
2280/**
2281 * Instantiates a new file, from UDF info.
2282 *
2283 * @returns IPRT status code.
2284 * @param pThis The FAT volume instance.
2285 * @param pParentDir The parent directory (shared part).
2286 * @param pFid The file ID descriptor. (Points to parent directory
2287 * content.)
2288 * @param fOpen RTFILE_O_XXX flags.
2289 * @param phVfsFile Where to return the file handle.
2290 */
2291static int rtFsIsoFile_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid,
2292 uint64_t fOpen, PRTVFSFILE phVfsFile)
2293{
2294 AssertPtr(pParentDir);
2295 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
2296 Assert(offInDir < pParentDir->cbDir);
2297 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DELETED));
2298 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY));
2299
2300 /*
2301 * Create a VFS object.
2302 */
2303 PRTFSISOFILEOBJ pNewFile;
2304 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2305 phVfsFile, (void **)&pNewFile);
2306 if (RT_SUCCESS(rc))
2307 {
2308 /*
2309 * Look for existing shared object. Make sure it's a file.
2310 */
2311 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
2312 if (pShared)
2313 {
2314 if (!RTFS_IS_FILE(pShared->Core.fAttrib))
2315 {
2316 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2317 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2318 pNewFile->offFile = 0;
2319 pNewFile->pShared = pShared;
2320 return VINF_SUCCESS;
2321 }
2322 }
2323 /*
2324 * Create a shared object for this alleged file.
2325 */
2326 else
2327 {
2328 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2329 if (pShared)
2330 {
2331 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, &pFid->Icb, pFid, offInDir, pThis);
2332 if (RT_SUCCESS(rc))
2333 {
2334 if (RTFS_IS_FILE(pShared->Core.fAttrib))
2335 {
2336 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2337
2338 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2339 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2340 pNewFile->offFile = 0;
2341 pNewFile->pShared = pShared;
2342 return VINF_SUCCESS;
2343 }
2344 rtFsIsoCore_Destroy(&pShared->Core);
2345 }
2346 RTMemFree(pShared);
2347 }
2348 else
2349 rc = VERR_NO_MEMORY;
2350 }
2351
2352 /* Destroy the file object. */
2353 pNewFile->offFile = 0;
2354 pNewFile->pShared = NULL;
2355 RTVfsFileRelease(*phVfsFile);
2356 }
2357 *phVfsFile = NIL_RTVFSFILE;
2358 return rc;
2359}
2360
2361
2362/**
2363 * Looks up the shared structure for a child.
2364 *
2365 * @returns Referenced pointer to the shared structure, NULL if not found.
2366 * @param pThis The directory.
2367 * @param offDirRec The directory record offset of the child.
2368 */
2369static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec)
2370{
2371 PRTFSISOCORE pCur;
2372 RTListForEach(&pThis->OpenChildren, pCur, RTFSISOCORE, Entry)
2373 {
2374 if (pCur->offDirRec == offDirRec)
2375 {
2376 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
2377 Assert(cRefs > 1); RT_NOREF(cRefs);
2378 return pCur;
2379 }
2380 }
2381 return NULL;
2382}
2383
2384
2385#ifdef RT_STRICT
2386/**
2387 * Checks if @a pNext is an extent of @a pFirst.
2388 *
2389 * @returns true if @a pNext is the next extent, false if not
2390 * @param pFirst The directory record describing the first or the
2391 * previous extent.
2392 * @param pNext The directory record alleged to be the next extent.
2393 */
2394DECLINLINE(bool) rtFsIsoDir_Is9660DirRecNextExtent(PCISO9660DIRREC pFirst, PCISO9660DIRREC pNext)
2395{
2396 if (RT_LIKELY(pNext->bFileIdLength == pFirst->bFileIdLength))
2397 {
2398 if (RT_LIKELY((pNext->fFileFlags | ISO9660_FILE_FLAGS_MULTI_EXTENT) == pFirst->fFileFlags))
2399 {
2400 if (RT_LIKELY(memcmp(pNext->achFileId, pFirst->achFileId, pNext->bFileIdLength) == 0))
2401 return true;
2402 }
2403 }
2404 return false;
2405}
2406#endif /* RT_STRICT */
2407
2408
2409/**
2410 * Worker for rtFsIsoDir_FindEntry9660 that compares a UTF-16BE name with a
2411 * directory record.
2412 *
2413 * @returns true if equal, false if not.
2414 * @param pDirRec The directory record.
2415 * @param pwszEntry The UTF-16BE string to compare with.
2416 * @param cbEntry The compare string length in bytes (sans zero
2417 * terminator).
2418 * @param cwcEntry The compare string length in RTUTF16 units.
2419 * @param puVersion Where to return any file version number.
2420 */
2421DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualUtf16Big(PCISO9660DIRREC pDirRec, PCRTUTF16 pwszEntry, size_t cbEntry,
2422 size_t cwcEntry, uint32_t *puVersion)
2423{
2424 /* ASSUME directories cannot have any version tags. */
2425 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
2426 {
2427 if (RT_LIKELY(pDirRec->bFileIdLength != cbEntry))
2428 return false;
2429 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
2430 return false;
2431 }
2432 else
2433 {
2434 size_t cbNameDelta = (size_t)pDirRec->bFileIdLength - cbEntry;
2435 if (RT_LIKELY(cbNameDelta > (size_t)12 /* ;12345 */))
2436 return false;
2437 if (cbNameDelta == 0)
2438 {
2439 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
2440 return false;
2441 *puVersion = 1;
2442 }
2443 else
2444 {
2445 if (RT_LIKELY(RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';'))
2446 return false;
2447 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
2448 return false;
2449 uint32_t uVersion;
2450 size_t cwcVersion = rtFsIso9660GetVersionLengthUtf16Big((PCRTUTF16)pDirRec->achFileId,
2451 pDirRec->bFileIdLength, &uVersion);
2452 if (RT_LIKELY(cwcVersion * sizeof(RTUTF16) == cbNameDelta))
2453 *puVersion = uVersion;
2454 else
2455 return false;
2456 }
2457 }
2458
2459 /* (No need to check for dot and dot-dot here, because cbEntry must be a
2460 multiple of two.) */
2461 Assert(!(cbEntry & 1));
2462 return true;
2463}
2464
2465
2466/**
2467 * Worker for rtFsIsoDir_FindEntry9660 that compares an ASCII name with a
2468 * directory record.
2469 *
2470 * @returns true if equal, false if not.
2471 * @param pDirRec The directory record.
2472 * @param pszEntry The uppercased ASCII string to compare with.
2473 * @param cchEntry The length of the compare string.
2474 * @param puVersion Where to return any file version number.
2475 */
2476DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualAscii(PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cchEntry,
2477 uint32_t *puVersion)
2478{
2479 /* ASSUME directories cannot have any version tags. */
2480 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
2481 {
2482 if (RT_LIKELY(pDirRec->bFileIdLength != cchEntry))
2483 return false;
2484 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
2485 return false;
2486 }
2487 else
2488 {
2489 size_t cchNameDelta = (size_t)pDirRec->bFileIdLength - cchEntry;
2490 if (RT_LIKELY(cchNameDelta > (size_t)6 /* ;12345 */))
2491 return false;
2492 if (cchNameDelta == 0)
2493 {
2494 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
2495 return false;
2496 *puVersion = 1;
2497 }
2498 else
2499 {
2500 if (RT_LIKELY(pDirRec->achFileId[cchEntry] != ';'))
2501 return false;
2502 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
2503 return false;
2504 uint32_t uVersion;
2505 size_t cchVersion = rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion);
2506 if (RT_LIKELY(cchVersion == cchNameDelta))
2507 *puVersion = uVersion;
2508 else
2509 return false;
2510 }
2511 }
2512
2513 /* Don't match the 'dot' and 'dot-dot' directory records. */
2514 if (RT_LIKELY( pDirRec->bFileIdLength != 1
2515 || (uint8_t)pDirRec->achFileId[0] > (uint8_t)0x01))
2516 return true;
2517 return false;
2518}
2519
2520
2521/**
2522 * Locates a directory entry in a directory.
2523 *
2524 * @returns IPRT status code.
2525 * @retval VERR_FILE_NOT_FOUND if not found.
2526 * @param pThis The directory to search.
2527 * @param pszEntry The entry to look for.
2528 * @param poffDirRec Where to return the offset of the directory record
2529 * on the disk.
2530 * @param ppDirRec Where to return the pointer to the directory record
2531 * (the whole directory is buffered).
2532 * @param pcDirRecs Where to return the number of directory records
2533 * related to this entry.
2534 * @param pfMode Where to return the file type, rock ridge adjusted.
2535 * @param puVersion Where to return the file version number.
2536 */
2537static int rtFsIsoDir_FindEntry9660(PRTFSISODIRSHRD pThis, const char *pszEntry, uint64_t *poffDirRec,
2538 PCISO9660DIRREC *ppDirRec, uint32_t *pcDirRecs, PRTFMODE pfMode, uint32_t *puVersion)
2539{
2540 Assert(pThis->Core.pVol->enmType != RTFSISOVOLTYPE_UDF);
2541
2542 /* Set return values. */
2543 *poffDirRec = UINT64_MAX;
2544 *ppDirRec = NULL;
2545 *pcDirRecs = 1;
2546 *pfMode = UINT32_MAX;
2547 *puVersion = 0;
2548
2549 /*
2550 * If we're in UTF-16BE mode, convert the input name to UTF-16BE. Otherwise try
2551 * uppercase it into a ISO 9660 compliant name.
2552 */
2553 int rc;
2554 bool const fIsUtf16 = pThis->Core.pVol->fIsUtf16;
2555 size_t cwcEntry = 0;
2556 size_t cbEntry = 0;
2557 size_t cchUpper = ~(size_t)0;
2558 union
2559 {
2560 RTUTF16 wszEntry[260 + 1];
2561 struct
2562 {
2563 char szUpper[255 + 1];
2564 char szRock[260 + 1];
2565 } s;
2566 } uBuf;
2567 if (fIsUtf16)
2568 {
2569 PRTUTF16 pwszEntry = uBuf.wszEntry;
2570 rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
2571 if (RT_FAILURE(rc))
2572 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
2573 cbEntry = cwcEntry * 2;
2574 }
2575 else
2576 {
2577 rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
2578 if (RT_FAILURE(rc))
2579 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
2580 RTStrToUpper(uBuf.s.szUpper);
2581 cchUpper = strlen(uBuf.s.szUpper);
2582 }
2583
2584 /*
2585 * Scan the directory buffer by buffer.
2586 */
2587 uint32_t offEntryInDir = 0;
2588 uint32_t const cbDir = pThis->Core.cbObject;
2589 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
2590 {
2591 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
2592
2593 /* If null length, skip to the next sector. */
2594 if (pDirRec->cbDirRec == 0)
2595 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
2596 else
2597 {
2598 /* Try match the filename. */
2599 if (fIsUtf16)
2600 {
2601 if (RT_LIKELY(!rtFsIsoDir_IsEntryEqualUtf16Big(pDirRec, uBuf.wszEntry, cbEntry, cwcEntry, puVersion)))
2602 {
2603 /* Advance */
2604 offEntryInDir += pDirRec->cbDirRec;
2605 continue;
2606 }
2607 }
2608 else
2609 {
2610 if (RT_LIKELY(!rtFsIsoDir_IsEntryEqualAscii(pDirRec, uBuf.s.szUpper, cchUpper, puVersion)))
2611 {
2612 /** @todo check rock. */
2613 if (1)
2614 {
2615 /* Advance */
2616 offEntryInDir += pDirRec->cbDirRec;
2617 continue;
2618 }
2619 }
2620 }
2621
2622 *poffDirRec = pThis->Core.FirstExtent.off + offEntryInDir;
2623 *ppDirRec = pDirRec;
2624 *pfMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
2625 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
2626 : 0644 | RTFS_TYPE_FILE;
2627
2628 /*
2629 * Deal with the unlikely scenario of multi extent records.
2630 */
2631 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
2632 *pcDirRecs = 1;
2633 else
2634 {
2635 offEntryInDir += pDirRec->cbDirRec;
2636
2637 uint32_t cDirRecs = 1;
2638 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
2639 {
2640 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
2641 if (pDirRec2->cbDirRec != 0)
2642 {
2643 Assert(rtFsIsoDir_Is9660DirRecNextExtent(pDirRec, pDirRec2));
2644 cDirRecs++;
2645 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
2646 break;
2647 offEntryInDir += pDirRec2->cbDirRec;
2648 }
2649 else
2650 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
2651 }
2652
2653 *pcDirRecs = cDirRecs;
2654 }
2655 return VINF_SUCCESS;
2656 }
2657 }
2658
2659 return VERR_FILE_NOT_FOUND;
2660}
2661
2662
2663/**
2664 * Locates a directory entry in a directory.
2665 *
2666 * @returns IPRT status code.
2667 * @retval VERR_FILE_NOT_FOUND if not found.
2668 * @param pThis The directory to search.
2669 * @param pszEntry The entry to look for.
2670 * @param ppFid Where to return the pointer to the file ID entry.
2671 * (Points to the directory content.)
2672 */
2673static int rtFsIsoDir_FindEntryUdf(PRTFSISODIRSHRD pThis, const char *pszEntry, PCUDFFILEIDDESC *ppFid)
2674{
2675 Assert(pThis->Core.pVol->enmType == RTFSISOVOLTYPE_UDF);
2676 *ppFid = NULL;
2677
2678 /*
2679 * Recode the entry name as 8-bit (if possible) and 16-bit strings.
2680 * This also disposes of entries that definitely are too long.
2681 */
2682 size_t cb8Bit;
2683 bool fSimple;
2684 size_t cb16Bit;
2685 size_t cwc16Bit;
2686 uint8_t ab8Bit[255];
2687 RTUTF16 wsz16Bit[255];
2688
2689 /* 16-bit */
2690 PRTUTF16 pwsz16Bit = wsz16Bit;
2691 int rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwsz16Bit, RT_ELEMENTS(wsz16Bit), &cwc16Bit);
2692 if (RT_SUCCESS(rc))
2693 cb16Bit = 1 + cwc16Bit * sizeof(RTUTF16);
2694 else
2695 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
2696
2697 /* 8-bit (can't possibly overflow) */
2698 fSimple = true;
2699 cb8Bit = 0;
2700 const char *pszSrc = pszEntry;
2701 for (;;)
2702 {
2703 RTUNICP uc;
2704 int rc = RTStrGetCpEx(&pszSrc, &uc);
2705 AssertRCReturn(rc, rc);
2706 if (uc <= 0x7f)
2707 {
2708 if (uc)
2709 ab8Bit[cb8Bit++] = (uint8_t)uc;
2710 else
2711 break;
2712 }
2713 else if (uc <= 0xff)
2714 {
2715 ab8Bit[cb8Bit++] = (uint8_t)uc;
2716 fSimple = false;
2717 }
2718 else
2719 {
2720 cb8Bit = UINT32_MAX / 2;
2721 break;
2722 }
2723 }
2724 Assert(cb8Bit <= sizeof(ab8Bit) || cb8Bit == UINT32_MAX / 2);
2725 cb8Bit++;
2726
2727 /*
2728 * Scan the directory content.
2729 */
2730 uint32_t offDesc = 0;
2731 uint32_t const cbDir = pThis->Core.cbObject;
2732 while (offDesc + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= cbDir)
2733 {
2734 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
2735 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
2736 if ( offDesc + cbFid <= cbDir
2737 && pFid->Tag.idTag == UDF_TAG_ID_FILE_ID_DESC)
2738 { /* likely */ }
2739 else
2740 break;
2741
2742 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
2743 if (*pbName == 16)
2744 {
2745 if (cb16Bit == pFid->cbName)
2746 {
2747 if (RTUtf16BigNICmp((PCRTUTF16)(&pbName[1]), wsz16Bit, cwc16Bit) == 0)
2748 {
2749 *ppFid = pFid;
2750 return VINF_SUCCESS;
2751 }
2752 }
2753 }
2754 else if (*pbName == 8)
2755 {
2756 if ( cb8Bit == pFid->cbName
2757 && cb8Bit != UINT16_MAX)
2758 {
2759 if (fSimple)
2760 {
2761 if (RTStrNICmp((const char *)&pbName[1], (const char *)ab8Bit, cb8Bit - 1) == 0)
2762 {
2763 *ppFid = pFid;
2764 return VINF_SUCCESS;
2765 }
2766 }
2767 else
2768 {
2769 size_t cch = cb8Bit - 1;
2770 size_t off;
2771 for (off = 0; off < cch; off++)
2772 {
2773 RTUNICP uc1 = ab8Bit[off];
2774 RTUNICP uc2 = pbName[off + 1];
2775 if ( uc1 == uc2
2776 || RTUniCpToLower(uc1) == RTUniCpToLower(uc2)
2777 || RTUniCpToUpper(uc1) == RTUniCpToUpper(uc2))
2778 { /* matches */ }
2779 else
2780 break;
2781 }
2782 if (off == cch)
2783 {
2784 *ppFid = pFid;
2785 return VINF_SUCCESS;
2786 }
2787 }
2788 }
2789 }
2790
2791 /* advance */
2792 offDesc += cbFid;
2793 }
2794
2795 return VERR_FILE_NOT_FOUND;
2796}
2797
2798
2799/**
2800 * Releases a reference to a shared directory structure.
2801 *
2802 * @param pShared The shared directory structure.
2803 */
2804static void rtFsIsoDirShrd_Release(PRTFSISODIRSHRD pShared)
2805{
2806 uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
2807 Assert(cRefs < UINT32_MAX / 2);
2808 if (cRefs == 0)
2809 {
2810 LogFlow(("rtFsIsoDirShrd_Release: Destroying shared structure %p\n", pShared));
2811 Assert(pShared->Core.cRefs == 0);
2812 if (pShared->pbDir)
2813 {
2814 RTMemFree(pShared->pbDir);
2815 pShared->pbDir = NULL;
2816 }
2817 rtFsIsoCore_Destroy(&pShared->Core);
2818 RTMemFree(pShared);
2819 }
2820}
2821
2822
2823/**
2824 * Retains a reference to a shared directory structure.
2825 *
2826 * @param pShared The shared directory structure.
2827 */
2828static void rtFsIsoDirShrd_Retain(PRTFSISODIRSHRD pShared)
2829{
2830 uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
2831 Assert(cRefs > 1); NOREF(cRefs);
2832}
2833
2834
2835
2836/**
2837 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2838 */
2839static DECLCALLBACK(int) rtFsIsoDir_Close(void *pvThis)
2840{
2841 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
2842 LogFlow(("rtFsIsoDir_Close(%p/%p)\n", pThis, pThis->pShared));
2843
2844 PRTFSISODIRSHRD pShared = pThis->pShared;
2845 pThis->pShared = NULL;
2846 if (pShared)
2847 rtFsIsoDirShrd_Release(pShared);
2848 return VINF_SUCCESS;
2849}
2850
2851
2852/**
2853 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2854 */
2855static DECLCALLBACK(int) rtFsIsoDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2856{
2857 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
2858 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
2859}
2860
2861
2862/**
2863 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2864 */
2865static DECLCALLBACK(int) rtFsIsoDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2866{
2867 RT_NOREF(pvThis, fMode, fMask);
2868 return VERR_WRITE_PROTECT;
2869}
2870
2871
2872/**
2873 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2874 */
2875static DECLCALLBACK(int) rtFsIsoDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2876 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2877{
2878 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2879 return VERR_WRITE_PROTECT;
2880}
2881
2882
2883/**
2884 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2885 */
2886static DECLCALLBACK(int) rtFsIsoDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2887{
2888 RT_NOREF(pvThis, uid, gid);
2889 return VERR_WRITE_PROTECT;
2890}
2891
2892
2893/**
2894 * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
2895 */
2896static DECLCALLBACK(int) rtFsIsoDir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
2897 PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
2898{
2899 /*
2900 * We may have symbolic links if rock ridge is being used, though currently
2901 * we won't have nested mounts.
2902 */
2903 int rc;
2904 if (phVfsMounted)
2905 *phVfsMounted = NIL_RTVFS;
2906 if (phVfsDir || phVfsSymlink)
2907 {
2908 if (phVfsSymlink)
2909 *phVfsSymlink = NIL_RTVFSSYMLINK;
2910 if (phVfsDir)
2911 *phVfsDir = NIL_RTVFSDIR;
2912
2913 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
2914 PRTFSISODIRSHRD pShared = pThis->pShared;
2915 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
2916 {
2917 /*
2918 * ISO 9660
2919 */
2920 PCISO9660DIRREC pDirRec;
2921 uint64_t offDirRec;
2922 uint32_t cDirRecs;
2923 RTFMODE fMode;
2924 uint32_t uVersion;
2925 rc = rtFsIsoDir_FindEntry9660(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
2926 Log2(("rtFsIsoDir_TraversalOpen: FindEntry9660(,%s,) -> %Rrc\n", pszEntry, rc));
2927 if (RT_SUCCESS(rc))
2928 {
2929 switch (fMode & RTFS_TYPE_MASK)
2930 {
2931 case RTFS_TYPE_DIRECTORY:
2932 if (phVfsDir)
2933 rc = rtFsIsoDir_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
2934 else
2935 rc = VERR_NOT_SYMLINK;
2936 break;
2937
2938 case RTFS_TYPE_SYMLINK:
2939 rc = VERR_NOT_IMPLEMENTED;
2940 break;
2941 case RTFS_TYPE_FILE:
2942 case RTFS_TYPE_DEV_BLOCK:
2943 case RTFS_TYPE_DEV_CHAR:
2944 case RTFS_TYPE_FIFO:
2945 case RTFS_TYPE_SOCKET:
2946 rc = VERR_NOT_A_DIRECTORY;
2947 break;
2948 default:
2949 case RTFS_TYPE_WHITEOUT:
2950 rc = VERR_PATH_NOT_FOUND;
2951 break;
2952 }
2953 }
2954 else if (rc == VERR_FILE_NOT_FOUND)
2955 rc = VERR_PATH_NOT_FOUND;
2956 }
2957 else
2958 {
2959 /*
2960 * UDF
2961 */
2962 PCUDFFILEIDDESC pFid;
2963 rc = rtFsIsoDir_FindEntryUdf(pShared, pszEntry, &pFid);
2964 Log2(("rtFsIsoDir_TraversalOpen: FindEntryUdf(,%s,) -> %Rrc\n", pszEntry, rc));
2965 if (RT_SUCCESS(rc))
2966 {
2967 if (!(pFid->fFlags & UDF_FILE_FLAGS_DELETED))
2968 {
2969 if (pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY)
2970 {
2971 if (phVfsDir)
2972 rc = rtFsIsoDir_NewUdf(pShared->Core.pVol, pShared, pFid, phVfsDir);
2973 else
2974 rc = VERR_NOT_SYMLINK;
2975 }
2976 else if (phVfsSymlink)
2977 {
2978 /** @todo symlink support */
2979 rc = VERR_NOT_A_DIRECTORY;
2980 }
2981 else
2982 rc = VERR_NOT_A_DIRECTORY;
2983 }
2984 /* We treat UDF_FILE_FLAGS_DELETED like RTFS_TYPE_WHITEOUT for now. */
2985 else
2986 rc = VERR_PATH_NOT_FOUND;
2987 }
2988 }
2989 }
2990 else
2991 rc = VERR_PATH_NOT_FOUND;
2992 return rc;
2993}
2994
2995
2996/**
2997 * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
2998 */
2999static DECLCALLBACK(int) rtFsIsoDir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
3000{
3001 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3002 PRTFSISODIRSHRD pShared = pThis->pShared;
3003
3004 /*
3005 * We cannot create or replace anything, just open stuff.
3006 */
3007 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
3008 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
3009 return VERR_WRITE_PROTECT;
3010
3011 /*
3012 * Try open file.
3013 */
3014 int rc;
3015 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3016 {
3017 /*
3018 * ISO 9660
3019 */
3020 PCISO9660DIRREC pDirRec;
3021 uint64_t offDirRec;
3022 uint32_t cDirRecs;
3023 RTFMODE fMode;
3024 uint32_t uVersion;
3025 rc = rtFsIsoDir_FindEntry9660(pShared, pszFilename, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
3026 Log2(("rtFsIsoDir_OpenFile: FindEntry9660(,%s,) -> %Rrc\n", pszFilename, rc));
3027 if (RT_SUCCESS(rc))
3028 {
3029 switch (fMode & RTFS_TYPE_MASK)
3030 {
3031 case RTFS_TYPE_FILE:
3032 rc = rtFsIsoFile_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, fOpen, uVersion, phVfsFile);
3033 break;
3034
3035 case RTFS_TYPE_SYMLINK:
3036 case RTFS_TYPE_DEV_BLOCK:
3037 case RTFS_TYPE_DEV_CHAR:
3038 case RTFS_TYPE_FIFO:
3039 case RTFS_TYPE_SOCKET:
3040 case RTFS_TYPE_WHITEOUT:
3041 rc = VERR_NOT_IMPLEMENTED;
3042 break;
3043
3044 case RTFS_TYPE_DIRECTORY:
3045 rc = VERR_NOT_A_FILE;
3046 break;
3047
3048 default:
3049 rc = VERR_PATH_NOT_FOUND;
3050 break;
3051 }
3052 }
3053 }
3054 else
3055 {
3056 /*
3057 * UDF
3058 */
3059 PCUDFFILEIDDESC pFid;
3060 rc = rtFsIsoDir_FindEntryUdf(pShared, pszFilename, &pFid);
3061 Log2(("rtFsIsoDir_OpenFile: FindEntryUdf(,%s,) -> %Rrc\n", pszFilename, rc));
3062 if (RT_SUCCESS(rc))
3063 {
3064 if (!(pFid->fFlags & UDF_FILE_FLAGS_DELETED))
3065 {
3066 if (!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY))
3067 rc = rtFsIsoFile_NewUdf(pShared->Core.pVol, pShared, pFid, fOpen, phVfsFile);
3068 else
3069 rc = VERR_NOT_A_FILE;
3070 }
3071 /* We treat UDF_FILE_FLAGS_DELETED like RTFS_TYPE_WHITEOUT for now. */
3072 else
3073 rc = VERR_PATH_NOT_FOUND;
3074 }
3075 }
3076 return rc;
3077}
3078
3079
3080/**
3081 * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
3082 */
3083static DECLCALLBACK(int) rtFsIsoDir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
3084{
3085 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3086 PRTFSISODIRSHRD pShared = pThis->pShared;
3087 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
3088
3089 /*
3090 * Try open file.
3091 */
3092 int rc;
3093 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3094 {
3095 /*
3096 * ISO 9660
3097 */
3098 PCISO9660DIRREC pDirRec;
3099 uint64_t offDirRec;
3100 uint32_t cDirRecs;
3101 RTFMODE fMode;
3102 uint32_t uVersion;
3103 rc = rtFsIsoDir_FindEntry9660(pShared, pszSubDir, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
3104 Log2(("rtFsIsoDir_OpenDir: FindEntry9660(,%s,) -> %Rrc\n", pszSubDir, rc));
3105 if (RT_SUCCESS(rc))
3106 {
3107 switch (fMode & RTFS_TYPE_MASK)
3108 {
3109 case RTFS_TYPE_DIRECTORY:
3110 rc = rtFsIsoDir_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
3111 break;
3112
3113 case RTFS_TYPE_FILE:
3114 case RTFS_TYPE_SYMLINK:
3115 case RTFS_TYPE_DEV_BLOCK:
3116 case RTFS_TYPE_DEV_CHAR:
3117 case RTFS_TYPE_FIFO:
3118 case RTFS_TYPE_SOCKET:
3119 case RTFS_TYPE_WHITEOUT:
3120 rc = VERR_NOT_A_DIRECTORY;
3121 break;
3122
3123 default:
3124 rc = VERR_PATH_NOT_FOUND;
3125 break;
3126 }
3127 }
3128 }
3129 else
3130 {
3131 /*
3132 * UDF
3133 */
3134 PCUDFFILEIDDESC pFid;
3135 rc = rtFsIsoDir_FindEntryUdf(pShared, pszSubDir, &pFid);
3136 Log2(("rtFsIsoDir_OpenDir: FindEntryUdf(,%s,) -> %Rrc\n", pszSubDir, rc));
3137 if (RT_SUCCESS(rc))
3138 {
3139 if (!(pFid->fFlags & UDF_FILE_FLAGS_DELETED))
3140 {
3141 if (pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY)
3142 rc = rtFsIsoDir_NewUdf(pShared->Core.pVol, pShared, pFid, phVfsDir);
3143 else
3144 rc = VERR_NOT_A_DIRECTORY;
3145 }
3146 /* We treat UDF_FILE_FLAGS_DELETED like RTFS_TYPE_WHITEOUT for now. */
3147 else
3148 rc = VERR_PATH_NOT_FOUND;
3149 }
3150 }
3151 return rc;
3152}
3153
3154
3155/**
3156 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3157 */
3158static DECLCALLBACK(int) rtFsIsoDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3159{
3160 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3161 return VERR_WRITE_PROTECT;
3162}
3163
3164
3165/**
3166 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3167 */
3168static DECLCALLBACK(int) rtFsIsoDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3169{
3170 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3171 return VERR_NOT_SUPPORTED;
3172}
3173
3174
3175/**
3176 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3177 */
3178static DECLCALLBACK(int) rtFsIsoDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3179 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3180{
3181 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3182 return VERR_WRITE_PROTECT;
3183}
3184
3185
3186/**
3187 * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
3188 */
3189static DECLCALLBACK(int) rtFsIsoDir_QueryEntryInfo(void *pvThis, const char *pszEntry,
3190 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3191{
3192 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3193 PRTFSISODIRSHRD pShared = pThis->pShared;
3194 RTFSISOCORE TmpObj;
3195 int rc;
3196 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3197 {
3198 /*
3199 * Try locate the entry.
3200 */
3201 PCISO9660DIRREC pDirRec;
3202 uint64_t offDirRec;
3203 uint32_t cDirRecs;
3204 RTFMODE fMode;
3205 uint32_t uVersion;
3206 rc = rtFsIsoDir_FindEntry9660(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
3207 Log2(("rtFsIsoDir_QueryEntryInfo: FindEntry9660(,%s,) -> %Rrc\n", pszEntry, rc));
3208 if (RT_SUCCESS(rc))
3209 {
3210 /*
3211 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
3212 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3213 */
3214 RT_ZERO(TmpObj);
3215 rc = rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, cDirRecs, offDirRec, uVersion, pShared->Core.pVol);
3216 if (RT_SUCCESS(rc))
3217 {
3218 rc = rtFsIsoCore_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
3219 rtFsIsoCore_Destroy(&TmpObj);
3220 }
3221 }
3222 }
3223 else
3224 {
3225 /*
3226 * Try locate the entry.
3227 */
3228 PCUDFFILEIDDESC pFid;
3229 rc = rtFsIsoDir_FindEntryUdf(pShared, pszEntry, &pFid);
3230 Log2(("rtFsIsoDir_QueryEntryInfo: FindEntryUdf(,%s,) -> %Rrc\n", pszEntry, rc));
3231 if (RT_SUCCESS(rc))
3232 {
3233 /*
3234 * To avoid duplicating code in rtFsIsoCore_InitFromUdfIcbAndFileIdDesc and
3235 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3236 */
3237 RT_ZERO(TmpObj);
3238 uintptr_t offInDir = (uintptr_t)pFid - (uintptr_t)pShared->pbDir;
3239 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&TmpObj, &pFid->Icb, pFid, offInDir, pShared->Core.pVol);
3240 if (RT_SUCCESS(rc))
3241 {
3242 rc = rtFsIsoCore_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
3243 rtFsIsoCore_Destroy(&TmpObj);
3244 }
3245 }
3246 }
3247 return rc;
3248}
3249
3250
3251/**
3252 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3253 */
3254static DECLCALLBACK(int) rtFsIsoDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3255{
3256 RT_NOREF(pvThis, pszEntry, fType);
3257 return VERR_WRITE_PROTECT;
3258}
3259
3260
3261/**
3262 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3263 */
3264static DECLCALLBACK(int) rtFsIsoDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3265{
3266 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3267 return VERR_WRITE_PROTECT;
3268}
3269
3270
3271/**
3272 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3273 */
3274static DECLCALLBACK(int) rtFsIsoDir_RewindDir(void *pvThis)
3275{
3276 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3277 pThis->offDir = 0;
3278 return VINF_SUCCESS;
3279}
3280
3281
3282/**
3283 * The ISO 9660 worker for rtFsIsoDir_ReadDir
3284 */
3285static int rtFsIsoDir_ReadDir9660(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3286 RTFSOBJATTRADD enmAddAttr)
3287{
3288 while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
3289 {
3290 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
3291
3292 /* If null length, skip to the next sector. */
3293 if (pDirRec->cbDirRec == 0)
3294 pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
3295 else
3296 {
3297 /*
3298 * Do names first as they may cause overflows.
3299 */
3300 uint32_t uVersion = 0;
3301 if ( pDirRec->bFileIdLength == 1
3302 && pDirRec->achFileId[0] == '\0')
3303 {
3304 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
3305 {
3306 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
3307 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot)\n"));
3308 return VERR_BUFFER_OVERFLOW;
3309 }
3310 pDirEntry->cbName = 1;
3311 pDirEntry->szName[0] = '.';
3312 pDirEntry->szName[1] = '\0';
3313 }
3314 else if ( pDirRec->bFileIdLength == 1
3315 && pDirRec->achFileId[0] == '\1')
3316 {
3317 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
3318 {
3319 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
3320 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
3321 return VERR_BUFFER_OVERFLOW;
3322 }
3323 pDirEntry->cbName = 2;
3324 pDirEntry->szName[0] = '.';
3325 pDirEntry->szName[1] = '.';
3326 pDirEntry->szName[2] = '\0';
3327 }
3328 else if (pShared->Core.pVol->fIsUtf16)
3329 {
3330 PCRTUTF16 pawcSrc = (PCRTUTF16)&pDirRec->achFileId[0];
3331 size_t cwcSrc = pDirRec->bFileIdLength / sizeof(RTUTF16);
3332 size_t cwcVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3333 ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
3334 size_t cchNeeded = 0;
3335 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
3336 char *pszDst = pDirEntry->szName;
3337
3338 int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
3339 if (RT_SUCCESS(rc))
3340 pDirEntry->cbName = (uint16_t)cchNeeded;
3341 else if (rc == VERR_BUFFER_OVERFLOW)
3342 {
3343 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
3344 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
3345 return VERR_BUFFER_OVERFLOW;
3346 }
3347 else
3348 {
3349 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
3350 if (cchNeeded2 >= 0)
3351 pDirEntry->cbName = (uint16_t)cchNeeded2;
3352 else
3353 {
3354 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
3355 return VERR_BUFFER_OVERFLOW;
3356 }
3357 }
3358 }
3359 else
3360 {
3361 /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
3362 size_t cchVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3363 ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
3364 size_t cchName = pDirRec->bFileIdLength - cchVer;
3365 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
3366 if (*pcbDirEntry < cbNeeded)
3367 {
3368 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
3369 *pcbDirEntry = cbNeeded;
3370 return VERR_BUFFER_OVERFLOW;
3371 }
3372 pDirEntry->cbName = (uint16_t)cchName;
3373 memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
3374 pDirEntry->szName[cchName] = '\0';
3375 RTStrPurgeEncoding(pDirEntry->szName);
3376
3377 /** @todo check for rock ridge names here. */
3378 }
3379 pDirEntry->cwcShortName = 0;
3380 pDirEntry->wszShortName[0] = '\0';
3381
3382 /*
3383 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
3384 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3385 */
3386 RTFSISOCORE TmpObj;
3387 RT_ZERO(TmpObj);
3388 rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
3389 pThis->offDir + pShared->Core.FirstExtent.off, uVersion, pShared->Core.pVol);
3390 int rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
3391
3392 /*
3393 * Update the directory location and handle multi extent records.
3394 *
3395 * Multi extent records only affect the file size and the directory location,
3396 * so we deal with it here instead of involving * rtFsIsoCore_InitFrom9660DirRec
3397 * which would potentially require freeing memory and such.
3398 */
3399 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3400 {
3401 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3402 pThis->offDir += pDirRec->cbDirRec;
3403 }
3404 else
3405 {
3406 uint32_t cExtents = 1;
3407 uint32_t offDir = pThis->offDir + pDirRec->cbDirRec;
3408 while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
3409 {
3410 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
3411 if (pDirRec2->cbDirRec != 0)
3412 {
3413 pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
3414 offDir += pDirRec2->cbDirRec;
3415 cExtents++;
3416 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3417 break;
3418 }
3419 else
3420 offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
3421 }
3422 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
3423 pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
3424 pThis->offDir = offDir;
3425 }
3426
3427 return rc;
3428 }
3429 }
3430
3431 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
3432 return VERR_NO_MORE_FILES;
3433}
3434
3435
3436/**
3437 * The UDF worker for rtFsIsoDir_ReadDir
3438 */
3439static int rtFsIsoDir_ReadDirUdf(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3440 RTFSOBJATTRADD enmAddAttr)
3441{
3442 /*
3443 * At offset zero we've got the '.' entry. This has to be generated
3444 * manually as it's not part of the directory content. The directory
3445 * offset has to be faked for this too, so offDir == 0 indicates the '.'
3446 * entry whereas offDir == 1 is the first file id descriptor.
3447 */
3448 if (pThis->offDir == 0)
3449 {
3450 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
3451 {
3452 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
3453 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW (dot)\n"));
3454 return VERR_BUFFER_OVERFLOW;
3455 }
3456 pDirEntry->cbName = 1;
3457 pDirEntry->szName[0] = '.';
3458 pDirEntry->szName[1] = '\0';
3459 pDirEntry->cwcShortName = 0;
3460 pDirEntry->wszShortName[0] = '\0';
3461
3462 int rc = rtFsIsoCore_QueryInfo(&pShared->Core, &pDirEntry->Info, enmAddAttr);
3463
3464 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3465 pThis->offDir = 1;
3466 return rc;
3467 }
3468
3469 /*
3470 * Do the directory content.
3471 */
3472 while (pThis->offDir + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= pShared->cbDir + 1)
3473 {
3474 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pShared->pbDir[pThis->offDir - 1];
3475 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3476
3477 if (pThis->offDir + cbFid <= pShared->cbDir + 1)
3478 { /* likely */ }
3479 else
3480 break;
3481
3482 /*
3483 * Do names first as they may cause overflows.
3484 */
3485 if (pFid->cbName > 1)
3486 {
3487 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3488 uint32_t cbSrc = pFid->cbName;
3489 if (*pbName == 8)
3490 {
3491 /* Figure out the UTF-8 length first. */
3492 bool fSimple = true;
3493 uint32_t cchDst = 0;
3494 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
3495 if (!(pbName[offSrc] & 0x80))
3496 cchDst++;
3497 else
3498 {
3499 cchDst += 2;
3500 fSimple = false;
3501 }
3502
3503 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchDst + 1;
3504 if (*pcbDirEntry >= cbNeeded)
3505 {
3506 if (fSimple)
3507 {
3508 Assert(cbSrc - 1 == cchDst);
3509 memcpy(pDirEntry->szName, &pbName[1], cchDst);
3510 pDirEntry->szName[cchDst] = '\0';
3511 }
3512 else
3513 {
3514 char *pszDst = pDirEntry->szName;
3515 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
3516 pszDst = RTStrPutCp(pszDst, pbName[offSrc]);
3517 *pszDst = '\0';
3518 Assert((size_t)(pszDst - &pDirEntry->szName[0]) == cchDst);
3519 }
3520 }
3521 else
3522 {
3523 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (8-bit)\n", *pcbDirEntry, cbNeeded));
3524 *pcbDirEntry = cbNeeded;
3525 return VERR_BUFFER_OVERFLOW;
3526 }
3527 }
3528 else
3529 {
3530 /* Let RTUtf16BigToUtf8Ex do the bounds checking. */
3531 char *pszDst = pDirEntry->szName;
3532 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
3533 size_t cchNeeded = 0;
3534 int rc;
3535 if (*pbName == 16)
3536 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)(pbName + 1), (cbSrc - 1) / sizeof(RTUTF16), &pszDst, cbDst, &cchNeeded);
3537 else
3538 rc = VERR_INVALID_NAME;
3539 if (RT_SUCCESS(rc))
3540 pDirEntry->cbName = (uint16_t)cchNeeded;
3541 else if (rc == VERR_BUFFER_OVERFLOW)
3542 {
3543 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
3544 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (16-bit)\n", cbDst, cchNeeded));
3545 return VERR_BUFFER_OVERFLOW;
3546 }
3547 else
3548 {
3549 LogRelMax(90, ("ISO/UDF: Malformed directory entry name at %#x: %.*Rhxs\n", pThis->offDir - 1, cbSrc, pbName));
3550 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir - 1);
3551 if (cchNeeded2 >= 0)
3552 pDirEntry->cbName = (uint16_t)cchNeeded2;
3553 else
3554 {
3555 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
3556 return VERR_BUFFER_OVERFLOW;
3557 }
3558 }
3559 }
3560 }
3561 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
3562 {
3563 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2 + 1;
3564 if (*pcbDirEntry < cbNeeded)
3565 {
3566 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (dot-dot)\n", *pcbDirEntry, cbNeeded));
3567 *pcbDirEntry = cbNeeded;
3568 return VERR_BUFFER_OVERFLOW;
3569 }
3570 pDirEntry->cbName = 2;
3571 pDirEntry->szName[0] = '.';
3572 pDirEntry->szName[1] = '.';
3573 pDirEntry->szName[2] = '\0';
3574 }
3575 else
3576 {
3577 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 1;
3578 if (*pcbDirEntry < cbNeeded)
3579 {
3580 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (empty)\n", *pcbDirEntry, cbNeeded));
3581 *pcbDirEntry = cbNeeded;
3582 return VERR_BUFFER_OVERFLOW;
3583 }
3584 pDirEntry->cbName = 0;
3585 pDirEntry->szName[0] = '\0';
3586 }
3587
3588 pDirEntry->cwcShortName = 0;
3589 pDirEntry->wszShortName[0] = '\0';
3590
3591 /*
3592 * To avoid duplicating code in rtFsIsoCore_InitUdf and
3593 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
3594 */
3595 RTFSISOCORE TmpObj;
3596 RT_ZERO(TmpObj);
3597 int rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&TmpObj, &pFid->Icb, pFid, pThis->offDir - 1, pShared->Core.pVol);
3598 if (RT_SUCCESS(rc))
3599 {
3600 rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
3601 rtFsIsoCore_Destroy(&TmpObj);
3602 }
3603
3604 /*
3605 * Update.
3606 */
3607 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
3608 pThis->offDir += cbFid;
3609
3610 return rc;
3611 }
3612
3613 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
3614 return VERR_NO_MORE_FILES;
3615}
3616
3617
3618/**
3619 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3620 */
3621static DECLCALLBACK(int) rtFsIsoDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3622 RTFSOBJATTRADD enmAddAttr)
3623{
3624 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3625 PRTFSISODIRSHRD pShared = pThis->pShared;
3626 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3627 return rtFsIsoDir_ReadDir9660(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
3628 return rtFsIsoDir_ReadDirUdf(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
3629}
3630
3631
3632/**
3633 * FAT file operations.
3634 */
3635static const RTVFSDIROPS g_rtFsIsoDirOps =
3636{
3637 { /* Obj */
3638 RTVFSOBJOPS_VERSION,
3639 RTVFSOBJTYPE_DIR,
3640 "ISO 9660 Dir",
3641 rtFsIsoDir_Close,
3642 rtFsIsoDir_QueryInfo,
3643 RTVFSOBJOPS_VERSION
3644 },
3645 RTVFSDIROPS_VERSION,
3646 0,
3647 { /* ObjSet */
3648 RTVFSOBJSETOPS_VERSION,
3649 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
3650 rtFsIsoDir_SetMode,
3651 rtFsIsoDir_SetTimes,
3652 rtFsIsoDir_SetOwner,
3653 RTVFSOBJSETOPS_VERSION
3654 },
3655 rtFsIsoDir_TraversalOpen,
3656 rtFsIsoDir_OpenFile,
3657 rtFsIsoDir_OpenDir,
3658 rtFsIsoDir_CreateDir,
3659 rtFsIsoDir_OpenSymlink,
3660 rtFsIsoDir_CreateSymlink,
3661 rtFsIsoDir_QueryEntryInfo,
3662 rtFsIsoDir_UnlinkEntry,
3663 rtFsIsoDir_RenameEntry,
3664 rtFsIsoDir_RewindDir,
3665 rtFsIsoDir_ReadDir,
3666 RTVFSDIROPS_VERSION,
3667};
3668
3669
3670/**
3671 * Adds an open child to the parent directory's shared structure.
3672 *
3673 * Maintains an additional reference to the parent dir to prevent it from going
3674 * away. If @a pDir is the root directory, it also ensures the volume is
3675 * referenced and sticks around until the last open object is gone.
3676 *
3677 * @param pDir The directory.
3678 * @param pChild The child being opened.
3679 * @sa rtFsIsoDirShrd_RemoveOpenChild
3680 */
3681static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
3682{
3683 rtFsIsoDirShrd_Retain(pDir);
3684
3685 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
3686 pChild->pParentDir = pDir;
3687}
3688
3689
3690/**
3691 * Removes an open child to the parent directory.
3692 *
3693 * @param pDir The directory.
3694 * @param pChild The child being removed.
3695 *
3696 * @remarks This is the very last thing you do as it may cause a few other
3697 * objects to be released recursively (parent dir and the volume).
3698 *
3699 * @sa rtFsIsoDirShrd_AddOpenChild
3700 */
3701static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
3702{
3703 AssertReturnVoid(pChild->pParentDir == pDir);
3704 RTListNodeRemove(&pChild->Entry);
3705 pChild->pParentDir = NULL;
3706
3707 rtFsIsoDirShrd_Release(pDir);
3708}
3709
3710
3711#ifdef LOG_ENABLED
3712/**
3713 * Logs the content of a directory.
3714 */
3715static void rtFsIsoDirShrd_Log9660Content(PRTFSISODIRSHRD pThis)
3716{
3717 if (LogIs2Enabled())
3718 {
3719 uint32_t offRec = 0;
3720 while (offRec < pThis->cbDir)
3721 {
3722 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
3723 if (pDirRec->cbDirRec == 0)
3724 break;
3725
3726 RTUTF16 wszName[128];
3727 if (pThis->Core.pVol->fIsUtf16)
3728 {
3729 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
3730 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
3731 pwszSrc--;
3732 *pwszDst-- = '\0';
3733 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
3734 {
3735 *pwszDst = RT_BE2H_U16(*pwszSrc);
3736 pwszDst--;
3737 pwszSrc--;
3738 }
3739 }
3740 else
3741 {
3742 PRTUTF16 pwszDst = wszName;
3743 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
3744 *pwszDst++ = pDirRec->achFileId[off];
3745 *pwszDst = '\0';
3746 }
3747
3748 Log2(("ISO9660: %04x: rec=%#x ea=%#x cb=%#010RX32 off=%#010RX32 fl=%#04x %04u-%02u-%02u %02u:%02u:%02u%+03d unit=%#x igap=%#x idVol=%#x '%ls'\n",
3749 offRec,
3750 pDirRec->cbDirRec,
3751 pDirRec->cExtAttrBlocks,
3752 ISO9660_GET_ENDIAN(&pDirRec->cbData),
3753 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
3754 pDirRec->fFileFlags,
3755 pDirRec->RecTime.bYear + 1900,
3756 pDirRec->RecTime.bMonth,
3757 pDirRec->RecTime.bDay,
3758 pDirRec->RecTime.bHour,
3759 pDirRec->RecTime.bMinute,
3760 pDirRec->RecTime.bSecond,
3761 pDirRec->RecTime.offUtc*4/60,
3762 pDirRec->bFileUnitSize,
3763 pDirRec->bInterleaveGapSize,
3764 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
3765 wszName));
3766
3767 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
3768 if (offSysUse < pDirRec->cbDirRec)
3769 {
3770 Log2(("ISO9660: system use (%#x bytes):\n%.*RhxD\n", pDirRec->cbDirRec - offSysUse,
3771 pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
3772 }
3773
3774 /* advance */
3775 offRec += pDirRec->cbDirRec;
3776 }
3777 }
3778}
3779#endif /* LOG_ENABLED */
3780
3781
3782/**
3783 * Instantiates a new shared directory structure, given 9660 records.
3784 *
3785 * @returns IPRT status code.
3786 * @param pThis The FAT volume instance.
3787 * @param pParentDir The parent directory. This is NULL for the root
3788 * directory.
3789 * @param pDirRec The directory record. Will access @a cDirRecs
3790 * records.
3791 * @param cDirRecs Number of directory records if more than one.
3792 * @param offDirRec The byte offset of the directory record.
3793 * @param ppShared Where to return the shared directory structure.
3794 */
3795static int rtFsIsoDirShrd_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
3796 uint32_t cDirRecs, uint64_t offDirRec, PRTFSISODIRSHRD *ppShared)
3797{
3798 /*
3799 * Allocate a new structure and initialize it.
3800 */
3801 int rc = VERR_NO_MEMORY;
3802 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
3803 if (pShared)
3804 {
3805 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pThis);
3806 if (RT_SUCCESS(rc))
3807 {
3808 RTListInit(&pShared->OpenChildren);
3809 pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
3810 pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
3811 if (pShared->pbDir)
3812 {
3813 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.off, pShared->pbDir, pShared->cbDir, NULL);
3814 if (RT_SUCCESS(rc))
3815 {
3816#ifdef LOG_ENABLED
3817 rtFsIsoDirShrd_Log9660Content(pShared);
3818#endif
3819
3820 /*
3821 * Link into parent directory so we can use it to update
3822 * our directory entry.
3823 */
3824 if (pParentDir)
3825 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
3826 *ppShared = pShared;
3827 return VINF_SUCCESS;
3828 }
3829 }
3830 else
3831 rc = VERR_NO_MEMORY;
3832 }
3833 RTMemFree(pShared);
3834 }
3835 *ppShared = NULL;
3836 return rc;
3837}
3838
3839
3840#ifdef LOG_ENABLED
3841/**
3842 * Logs the content of a directory.
3843 */
3844static void rtFsIsoDirShrd_LogUdfContent(PRTFSISODIRSHRD pThis)
3845{
3846 if (LogIs2Enabled())
3847 {
3848 uint32_t offDesc = 0;
3849 while (offDesc + RT_OFFSETOF(UDFFILEIDDESC, abImplementationUse) < pThis->cbDir)
3850 {
3851 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
3852 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3853 if (offDesc + cbFid > pThis->cbDir)
3854 break;
3855
3856 uint32_t cwcName = 0;
3857 RTUTF16 wszName[260];
3858 if (pFid->cbName > 0)
3859 {
3860 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3861 uint32_t offSrc = 1;
3862 if (*pbName == 8)
3863 while (offSrc < pFid->cbName)
3864 {
3865 wszName[cwcName] = pbName[offSrc];
3866 cwcName++;
3867 offSrc++;
3868 }
3869 else if (*pbName == 16)
3870 while (offSrc + 1 <= pFid->cbName)
3871 {
3872 wszName[cwcName] = RT_MAKE_U16(pbName[offSrc + 1], pbName[offSrc]);
3873 cwcName++;
3874 offSrc += 2;
3875 }
3876 else
3877 {
3878 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<bad type>");
3879 cwcName = 10;
3880 }
3881 }
3882 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
3883 {
3884 wszName[0] = '.';
3885 wszName[1] = '.';
3886 cwcName = 2;
3887 }
3888 else
3889 {
3890 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<empty>");
3891 cwcName = 7;
3892 }
3893 wszName[cwcName] = '\0';
3894
3895 Log2(("ISO/UDF: %04x: fFlags=%#x uVer=%u Icb={%#04x:%#010RX32 LB %#06x t=%u} cbName=%#04x cbIU=%#x '%ls'\n",
3896 offDesc,
3897 pFid->fFlags,
3898 pFid->uVersion,
3899 pFid->Icb.Location.uPartitionNo,
3900 pFid->Icb.Location.off,
3901 pFid->Icb.cb,
3902 pFid->Icb.uType,
3903 pFid->cbName,
3904 pFid->cbImplementationUse,
3905 wszName));
3906 int rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFid->Tag, pThis->cbDir - offDesc,
3907 UDF_TAG_ID_FILE_ID_DESC, pFid->Tag.offTag, NULL);
3908 if (RT_FAILURE(rc))
3909 Log2(("ISO/UDF: Bad Tag: %Rrc - idTag=%#x\n", rc, pFid->Tag.idTag));
3910 if (pFid->cbImplementationUse > 32)
3911 Log2(("ISO/UDF: impl use (%#x bytes):\n%.*RhxD\n", pFid->cbImplementationUse, pFid->abImplementationUse));
3912 else if (pFid->cbImplementationUse > 0)
3913 Log2(("ISO/UDF: impl use (%#x bytes): %.*Rhxs\n", pFid->cbImplementationUse, pFid->abImplementationUse));
3914
3915 /* advance */
3916 offDesc += cbFid;
3917 }
3918
3919 if (offDesc < pThis->cbDir)
3920 Log2(("ISO/UDF: warning! %#x trailing bytes in directory:\n%.*RhxD\n",
3921 pThis->cbDir - offDesc, pThis->cbDir - offDesc, &pThis->pbDir[offDesc]));
3922 }
3923}
3924#endif /* LOG_ENABLED */
3925
3926
3927/**
3928 * Instantiates a new shared directory structure, given UDF descriptors.
3929 *
3930 * @returns IPRT status code.
3931 * @param pThis The FAT volume instance.
3932 * @param pParentDir The parent directory. This is NULL for the root
3933 * directory.
3934 * @param pAllocDesc The allocation descriptor for the directory ICB.
3935 * @param pFileIdDesc The file ID descriptor. This is NULL for the root.
3936 * @param offInDir The offset of the file ID descriptor in the parent
3937 * directory. This is used when looking up shared
3938 * directory objects. (Pass 0 for root.)
3939 * @param ppShared Where to return the shared directory structure.
3940 */
3941static int rtFsIsoDirShrd_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFLONGAD pAllocDesc,
3942 PCUDFFILEIDDESC pFileIdDesc, uintptr_t offInDir, PRTFSISODIRSHRD *ppShared)
3943{
3944 /*
3945 * Allocate a new structure and initialize it.
3946 */
3947 int rc = VERR_NO_MEMORY;
3948 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
3949 if (pShared)
3950 {
3951 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, pAllocDesc, pFileIdDesc, offInDir, pThis);
3952 if (RT_SUCCESS(rc))
3953 {
3954 RTListInit(&pShared->OpenChildren);
3955
3956 if (pShared->Core.cbObject < RTFSISO_MAX_DIR_SIZE)
3957 {
3958 pShared->cbDir = (uint32_t)pShared->Core.cbObject;
3959 pShared->pbDir = (uint8_t *)RTMemAllocZ(RT_MIN(RT_ALIGN_32(pShared->cbDir, 512), 512));
3960 if (pShared->pbDir)
3961 {
3962 rc = rtFsIsoCore_ReadWorker(&pShared->Core, 0, pShared->pbDir, pShared->cbDir, NULL, NULL);
3963 if (RT_SUCCESS(rc))
3964 {
3965#ifdef LOG_ENABLED
3966 rtFsIsoDirShrd_LogUdfContent(pShared);
3967#endif
3968
3969 /*
3970 * Link into parent directory so we can use it to update
3971 * our directory entry.
3972 */
3973 if (pParentDir)
3974 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
3975 *ppShared = pShared;
3976 return VINF_SUCCESS;
3977 }
3978 }
3979 else
3980 rc = VERR_NO_MEMORY;
3981 }
3982 }
3983 RTMemFree(pShared);
3984 }
3985
3986 *ppShared = NULL;
3987 return rc;
3988}
3989
3990
3991/**
3992 * Instantiates a new directory with a shared structure presupplied.
3993 *
3994 * @returns IPRT status code.
3995 * @param pThis The FAT volume instance.
3996 * @param pShared Referenced pointer to the shared structure. The
3997 * reference is always CONSUMED.
3998 * @param phVfsDir Where to return the directory handle.
3999 */
4000static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir)
4001{
4002 /*
4003 * Create VFS object around the shared structure.
4004 */
4005 PRTFSISODIROBJ pNewDir;
4006 int rc = RTVfsNewDir(&g_rtFsIsoDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
4007 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
4008 if (RT_SUCCESS(rc))
4009 {
4010 /*
4011 * Look for existing shared object, create a new one if necessary.
4012 * We CONSUME a reference to pShared here.
4013 */
4014 pNewDir->offDir = 0;
4015 pNewDir->pShared = pShared;
4016 return VINF_SUCCESS;
4017 }
4018
4019 rtFsIsoDirShrd_Release(pShared);
4020 *phVfsDir = NIL_RTVFSDIR;
4021 return rc;
4022}
4023
4024
4025
4026/**
4027 * Instantiates a new directory VFS instance for ISO 9660, creating the shared
4028 * structure as necessary.
4029 *
4030 * @returns IPRT status code.
4031 * @param pThis The FAT volume instance.
4032 * @param pParentDir The parent directory. This is NULL for the root
4033 * directory.
4034 * @param pDirRec The directory record.
4035 * @param cDirRecs Number of directory records if more than one.
4036 * @param offDirRec The byte offset of the directory record.
4037 * @param phVfsDir Where to return the directory handle.
4038 */
4039static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
4040 uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir)
4041{
4042 /*
4043 * Look for existing shared object, create a new one if necessary.
4044 */
4045 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
4046 if (!pShared)
4047 {
4048 int rc = rtFsIsoDirShrd_New9660(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, &pShared);
4049 if (RT_FAILURE(rc))
4050 {
4051 *phVfsDir = NIL_RTVFSDIR;
4052 return rc;
4053 }
4054 }
4055 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
4056}
4057
4058
4059/**
4060 * Instantiates a new directory VFS instance for UDF, creating the shared
4061 * structure as necessary.
4062 *
4063 * @returns IPRT status code.
4064 * @param pThis The FAT volume instance.
4065 * @param pParentDir The parent directory.
4066 * @param pFid The file ID descriptor for the directory.
4067 * @param phVfsDir Where to return the directory handle.
4068 */
4069static int rtFsIsoDir_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid, PRTVFSDIR phVfsDir)
4070{
4071 Assert(pFid);
4072 Assert(pParentDir);
4073 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
4074 Assert(offInDir < pParentDir->cbDir);
4075
4076 /*
4077 * Look for existing shared object, create a new one if necessary.
4078 */
4079 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
4080 if (!pShared)
4081 {
4082 int rc = rtFsIsoDirShrd_NewUdf(pThis, pParentDir, &pFid->Icb, pFid, offInDir, &pShared);
4083 if (RT_FAILURE(rc))
4084 {
4085 *phVfsDir = NIL_RTVFSDIR;
4086 return rc;
4087 }
4088 }
4089 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
4090}
4091
4092
4093/**
4094 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
4095 */
4096static DECLCALLBACK(int) rtFsIsoVol_Close(void *pvThis)
4097{
4098 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
4099 Log(("rtFsIsoVol_Close(%p)\n", pThis));
4100
4101 if (pThis->pRootDir)
4102 {
4103 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
4104 Assert(pThis->pRootDir->Core.cRefs == 1);
4105 rtFsIsoDirShrd_Release(pThis->pRootDir);
4106 pThis->pRootDir = NULL;
4107 }
4108
4109 RTVfsFileRelease(pThis->hVfsBacking);
4110 pThis->hVfsBacking = NIL_RTVFSFILE;
4111
4112 return VINF_SUCCESS;
4113}
4114
4115
4116/**
4117 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
4118 */
4119static DECLCALLBACK(int) rtFsIsoVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4120{
4121 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
4122 return VERR_WRONG_TYPE;
4123}
4124
4125
4126/**
4127 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
4128 */
4129static DECLCALLBACK(int) rtFsIsoVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
4130{
4131 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
4132
4133 rtFsIsoDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
4134 return rtFsIsoDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
4135}
4136
4137
4138/**
4139 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
4140 */
4141static DECLCALLBACK(int) rtFsIsoVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
4142{
4143 RT_NOREF(pvThis, off, cb, pfUsed);
4144 return VERR_NOT_IMPLEMENTED;
4145}
4146
4147
4148DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIsoVolOps =
4149{
4150 { /* Obj */
4151 RTVFSOBJOPS_VERSION,
4152 RTVFSOBJTYPE_VFS,
4153 "ISO 9660/UDF",
4154 rtFsIsoVol_Close,
4155 rtFsIsoVol_QueryInfo,
4156 RTVFSOBJOPS_VERSION
4157 },
4158 RTVFSOPS_VERSION,
4159 0 /* fFeatures */,
4160 rtFsIsoVol_OpenRoot,
4161 rtFsIsoVol_IsRangeInUse,
4162 RTVFSOPS_VERSION
4163};
4164
4165
4166/**
4167 * Checks the descriptor tag and CRC.
4168 *
4169 * @retval IPRT status code.
4170 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
4171 * @retval VERR_MISMATCH
4172 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
4173 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
4174 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
4175 *
4176 * @param pTag The tag to check.
4177 * @param idTag The expected descriptor tag ID, UINT16_MAX matches any
4178 * tag ID.
4179 * @param offTag The sector offset of the tag.
4180 * @param pErrInfo Where to return extended error info.
4181 */
4182static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
4183{
4184 /*
4185 * Checksum the tag first.
4186 */
4187 const uint8_t *pbTag = (const uint8_t *)pTag;
4188 uint8_t const bChecksum = pbTag[0]
4189 + pbTag[1]
4190 + pbTag[2]
4191 + pbTag[3]
4192 + pbTag[5] /* skipping byte 4 as that's the checksum. */
4193 + pbTag[6]
4194 + pbTag[7]
4195 + pbTag[8]
4196 + pbTag[9]
4197 + pbTag[10]
4198 + pbTag[11]
4199 + pbTag[12]
4200 + pbTag[13]
4201 + pbTag[14]
4202 + pbTag[15];
4203 if (pTag->uChecksum == bChecksum)
4204 {
4205 /*
4206 * Do the matching.
4207 */
4208 if ( pTag->uVersion == 3
4209 || pTag->uVersion == 2)
4210 {
4211 if ( pTag->idTag == idTag
4212 || idTag == UINT16_MAX)
4213 {
4214 if (pTag->offTag == offTag)
4215 {
4216 //Log3(("ISO/UDF: Valid descriptor %#06x at %#010RX32; cbDescriptorCrc=%#06RX32 uTagSerialNo=%#x\n",
4217 // pTag->idTag, offTag, pTag->cbDescriptorCrc, pTag->uTagSerialNo));
4218 return VINF_SUCCESS;
4219 }
4220
4221 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Sector mismatch: %#RX32 (%.*Rhxs)\n",
4222 idTag, offTag, pTag->offTag, sizeof(*pTag), pTag));
4223 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TAG_SECTOR_MISMATCH,
4224 "Descriptor tag sector number mismatch: %#x, expected %#x (%.*Rhxs)",
4225 pTag->offTag, offTag, sizeof(*pTag), pTag);
4226 }
4227 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Tag ID mismatch: %#x (%.*Rhxs)\n",
4228 idTag, offTag, pTag->idTag, sizeof(*pTag), pTag));
4229 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_MISMATCH, "Descriptor tag ID mismatch: %#x, expected %#x (%.*Rhxs)",
4230 pTag->idTag, idTag, sizeof(*pTag), pTag);
4231 }
4232 if (ASMMemIsZero(pTag, sizeof(*pTag)))
4233 {
4234 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): All zeros\n", idTag, offTag));
4235 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TAG_IS_ALL_ZEROS, "Descriptor is all zeros");
4236 }
4237
4238 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Unsupported version: %#x (%.*Rhxs)\n",
4239 idTag, offTag, pTag->uVersion, sizeof(*pTag), pTag));
4240 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_TAG_VERSION, "Unsupported descriptor tag version: %#x, expected 2 or 3 (%.*Rhxs)",
4241 pTag->uVersion, sizeof(*pTag), pTag);
4242 }
4243 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): checksum error: %#x, calc %#x (%.*Rhxs)\n",
4244 idTag, offTag, pTag->uChecksum, bChecksum, sizeof(*pTag), pTag));
4245 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_TAG_CHECKSUM,
4246 "Descriptor tag checksum error: %#x, calculated %#x (%.*Rhxs)",
4247 pTag->uChecksum, bChecksum, sizeof(*pTag), pTag);
4248}
4249
4250
4251/**
4252 * Checks the descriptor CRC.
4253 *
4254 * @retval VINF_SUCCESS
4255 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4256 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
4257 *
4258 * @param pTag The descriptor buffer to checksum.
4259 * @param cbDesc The size of the descriptor buffer.
4260 * @param pErrInfo Where to return extended error info.
4261 */
4262static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo)
4263{
4264 if (pTag->cbDescriptorCrc + sizeof(*pTag) <= cbDesc)
4265 {
4266 uint16_t uCrc = RTCrc16Ccitt(pTag + 1, pTag->cbDescriptorCrc);
4267 if (pTag->uDescriptorCrc == uCrc)
4268 return VINF_SUCCESS;
4269
4270 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Descriptor CRC mismatch: expected %#x, calculated %#x (cbDescriptorCrc=%#x)\n",
4271 pTag->idTag, pTag->offTag, pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc));
4272 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_DESC_CRC_MISMATCH,
4273 "Descriptor CRC mismatch: exepcted %#x, calculated %#x (cbDescriptor=%#x, idTag=%#x, offTag=%#010RX32)",
4274 pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc, pTag->idTag, pTag->offTag);
4275 }
4276
4277 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx\n",
4278 pTag->idTag, pTag->offTag, pTag->cbDescriptorCrc, cbDesc));
4279 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC,
4280 "Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx (idTag=%#x, offTag=%#010RX32)",
4281 pTag->cbDescriptorCrc, cbDesc, pTag->idTag, pTag->offTag);
4282}
4283
4284
4285/**
4286 * Checks the descriptor tag and CRC.
4287 *
4288 * @retval VINF_SUCCESS
4289 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
4290 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
4291 * @retval VERR_MISMATCH
4292 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
4293 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
4294 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
4295 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
4296 *
4297 * @param pTag The descriptor buffer to check the tag of and to
4298 * checksum.
4299 * @param cbDesc The size of the descriptor buffer.
4300 * @param idTag The expected descriptor tag ID, UINT16_MAX
4301 * matches any tag ID.
4302 * @param offTag The sector offset of the tag.
4303 * @param pErrInfo Where to return extended error info.
4304 */
4305static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
4306{
4307 int rc = rtFsIsoVolValidateUdfDescTag(pTag, idTag, offTag, pErrInfo);
4308 if (RT_SUCCESS(rc))
4309 rc = rtFsIsoVolValidateUdfDescCrc(pTag, cbDesc, pErrInfo);
4310 return rc;
4311}
4312
4313
4314
4315
4316static int rtFsIsoVolProcessUdfFileSetDescs(PRTFSISOVOL pThis, uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
4317{
4318
4319 /*
4320 * We assume there is a single file descriptor and don't bother checking what comes next.
4321 */
4322 PUDFFILESETDESC pFsd = (PUDFFILESETDESC)pbBuf;
4323 Assert(cbBuf > sizeof(*pFsd)); NOREF(cbBuf);
4324 RT_ZERO(*pFsd);
4325 size_t cbToRead = RT_MAX(pThis->Udf.VolInfo.FileSetDescriptor.cb, sizeof(*pFsd));
4326 int rc = rtFsIsoVolUdfVpRead(pThis, pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
4327 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, 0, pFsd, cbToRead);
4328 if (RT_SUCCESS(rc))
4329 {
4330 rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFsd->Tag, cbToRead, UDF_TAG_ID_FILE_SET_DESC,
4331 pThis->Udf.VolInfo.FileSetDescriptor.Location.off, pErrInfo);
4332 if (RT_SUCCESS(rc))
4333 {
4334#ifdef LOG_ENABLED
4335 Log(("ISO/UDF: File set descriptor at %#RX32 (%#RX32:%#RX32)\n", pFsd->Tag.offTag,
4336 pThis->Udf.VolInfo.FileSetDescriptor.Location.uPartitionNo,
4337 pThis->Udf.VolInfo.FileSetDescriptor.Location.off));
4338 if (LogIs2Enabled())
4339 {
4340 UDF_LOG2_MEMBER_TIMESTAMP(pFsd, RecordingTimestamp);
4341 UDF_LOG2_MEMBER(pFsd, "#06RX16", uInterchangeLevel);
4342 UDF_LOG2_MEMBER(pFsd, "#06RX16", uMaxInterchangeLevel);
4343 UDF_LOG2_MEMBER(pFsd, "#010RX32", fCharacterSets);
4344 UDF_LOG2_MEMBER(pFsd, "#010RX32", fMaxCharacterSets);
4345 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetNo);
4346 UDF_LOG2_MEMBER(pFsd, "#010RX32", uFileSetDescNo);
4347 UDF_LOG2_MEMBER_CHARSPEC(pFsd, LogicalVolumeIDCharSet);
4348 UDF_LOG2_MEMBER_DSTRING(pFsd, achLogicalVolumeID);
4349 UDF_LOG2_MEMBER_CHARSPEC(pFsd, FileSetCharSet);
4350 UDF_LOG2_MEMBER_DSTRING(pFsd, achFileSetID);
4351 UDF_LOG2_MEMBER_DSTRING(pFsd, achCopyrightFile);
4352 UDF_LOG2_MEMBER_DSTRING(pFsd, achAbstractFile);
4353 UDF_LOG2_MEMBER_LONGAD(pFsd, RootDirIcb);
4354 UDF_LOG2_MEMBER_ENTITY_ID(pFsd, idDomain);
4355 UDF_LOG2_MEMBER_LONGAD(pFsd, NextExtent);
4356 UDF_LOG2_MEMBER_LONGAD(pFsd, SystemStreamDirIcb);
4357 if (!ASMMemIsZero(&pFsd->abReserved[0], sizeof(pFsd->abReserved)))
4358 UDF_LOG2_MEMBER(pFsd, "%.32Rhxs", abReserved);
4359 }
4360#endif
4361
4362 /*
4363 * Do some basic sanity checking.
4364 */
4365 if (!UDF_IS_CHAR_SET_OSTA(&pFsd->FileSetCharSet))
4366 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_UNSUPPORTED_CHAR_SET,
4367 "Invalid file set charset %.64Rhxs", &pFsd->FileSetCharSet);
4368 if ( pFsd->RootDirIcb.cb == 0
4369 || pFsd->RootDirIcb.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4370 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_ZERO_ROOT_DIR,
4371 "Root Dir ICB location is zero or malformed: uType=%#x cb=%#x loc=%#x:%#RX32",
4372 pFsd->RootDirIcb.uType, pFsd->RootDirIcb.cb,
4373 pFsd->RootDirIcb.Location.uPartitionNo, pFsd->RootDirIcb.Location.off);
4374 if ( pFsd->NextExtent.cb != 0
4375 && pFsd->NextExtent.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4376 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_FSD_NEXT_EXTENT,
4377 "NextExtent isn't zero: uType=%#x cb=%#x loc=%#x:%#RX32",
4378 pFsd->NextExtent.uType, pFsd->NextExtent.cb,
4379 pFsd->NextExtent.Location.uPartitionNo, pFsd->NextExtent.Location.off);
4380
4381 /*
4382 * Copy the information we need.
4383 */
4384 pThis->Udf.VolInfo.RootDirIcb = pFsd->RootDirIcb;
4385 if ( pFsd->SystemStreamDirIcb.cb > 0
4386 && pFsd->SystemStreamDirIcb.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
4387 pThis->Udf.VolInfo.SystemStreamDirIcb = pFsd->SystemStreamDirIcb;
4388 else
4389 RT_ZERO(pThis->Udf.VolInfo.SystemStreamDirIcb);
4390 return VINF_SUCCESS;
4391 }
4392 return rc;
4393 }
4394 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading file set descriptor");
4395}
4396
4397
4398/**
4399 * Check validatity and extract information from the descriptors in the VDS seq.
4400 *
4401 * @returns IPRT status code
4402 * @param pThis The instance.
4403 * @param pInfo The VDS sequence info.
4404 * @param pErrInfo Where to return extended error info.
4405 */
4406static int rtFsIsoVolProcessUdfVdsSeqInfo(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, PRTERRINFO pErrInfo)
4407{
4408 /*
4409 * Check the basic descriptor counts.
4410 */
4411 PUDFPRIMARYVOLUMEDESC pPvd;
4412 if (pInfo->cPrimaryVols == 1)
4413 pPvd = pInfo->apPrimaryVols[0];
4414 else
4415 {
4416 if (pInfo->cPrimaryVols == 0)
4417 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PVD, "No primary volume descriptor was found");
4418 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_PVDS,
4419 "More than one primary volume descriptor was found: %u", pInfo->cPrimaryVols);
4420 }
4421
4422 PUDFLOGICALVOLUMEDESC pLvd;
4423 if (pInfo->cLogicalVols == 1)
4424 pLvd = pInfo->apLogicalVols[0];
4425 else
4426 {
4427 if (pInfo->cLogicalVols == 0)
4428 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_LVD, "No logical volume descriptor was found");
4429 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MULTIPLE_LVDS,
4430 "More than one logical volume descriptor was found: %u", pInfo->cLogicalVols);
4431 }
4432
4433#if 0
4434 if (pInfo->cPartitions == 0)
4435 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_NO_PD, "No partition descriptors was found");
4436#endif
4437
4438 /*
4439 * Check out the partition map in the logical volume descriptor.
4440 * Produce the mapping table while going about that.
4441 */
4442 if (pLvd->cPartitionMaps > 64)
4443 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_MANY_PART_MAPS,
4444 "Too many partition maps: %u (max 64)", pLvd->cPartitionMaps);
4445
4446 PRTFSISOVOLUDFPMAP paPartMaps = NULL;
4447 if (pLvd->cPartitionMaps > 0)
4448 {
4449 pInfo->paPartMaps = paPartMaps = (PRTFSISOVOLUDFPMAP)RTMemAllocZ(sizeof(paPartMaps[0]) * pLvd->cPartitionMaps);
4450 if (!paPartMaps)
4451 return VERR_NO_MEMORY;
4452 }
4453 uint32_t cPartMaps = 0;
4454
4455 if (pLvd->cbMapTable)
4456 {
4457 uint32_t off = 0;
4458 while (off + sizeof(UDFPARTMAPHDR) <= pLvd->cbMapTable)
4459 {
4460 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pLvd->abPartitionMaps[off];
4461
4462 /*
4463 * Bounds checking.
4464 */
4465 if (off + pHdr->cb > pLvd->cbMapTable)
4466 {
4467 if (cPartMaps < pLvd->cbMapTable)
4468 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MALFORMED_PART_MAP_TABLE,
4469 "Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)",
4470 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType);
4471 LogRel(("ISO/UDF: Warning: Incomplete partition map entry at offset %#x: cb=%#x -> offEnd=%#x cbMapTable=%#x (type=%#x)\n",
4472 off, pHdr->cb, off + pHdr->cb, pLvd->cbMapTable, pHdr->bType));
4473 break;
4474 }
4475 if (cPartMaps >= pLvd->cPartitionMaps)
4476 {
4477 LogRel(("ISO/UDF: Warning: LVD::cPartitionMaps is %u but there are more bytes in the table. (off=%#x cb=%#x cbMapTable=%#x bType=%#x)\n",
4478 cPartMaps - pLvd->cPartitionMaps, off, pHdr->cb, pLvd->cbMapTable, pHdr->bType));
4479 break;
4480 }
4481
4482 /*
4483 * Extract relevant info out of the entry.
4484 */
4485 paPartMaps[cPartMaps].offMapTable = (uint16_t)off;
4486 uint16_t uPartitionNo;
4487 if (pHdr->bType == 1)
4488 {
4489 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
4490 paPartMaps[cPartMaps].uVolumeSeqNo = pType1->uVolumeSeqNo;
4491 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_PLAIN;
4492 uPartitionNo = pType1->uPartitionNo;
4493 }
4494 else if (pHdr->bType == 2)
4495 {
4496 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
4497 if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_VPM_PARTITION_TYPE))
4498 {
4499 paPartMaps[cPartMaps].bType = pType2->idPartitionType.Suffix.Udf.uUdfRevision >= 0x200
4500 ? RTFSISO_UDF_PMAP_T_VPM_20 : RTFSISO_UDF_PMAP_T_VPM_15;
4501 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_VPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4502 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4503 }
4504 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_SPM_PARTITION_TYPE))
4505 {
4506 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_SPM;
4507 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_SPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4508 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4509 }
4510 else if (UDF_ENTITY_ID_EQUALS(&pType2->idPartitionType, UDF_ENTITY_ID_MPM_PARTITION_TYPE))
4511 {
4512 paPartMaps[cPartMaps].bType = RTFSISO_UDF_PMAP_T_MPM;
4513 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_MPM_NOT_SUPPORTED, "Partition type '%.23s' (%#x) not supported",
4514 pType2->idPartitionType.achIdentifier, pType2->idPartitionType.Suffix.Udf.uUdfRevision);
4515 }
4516 else
4517 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_TYPE_ID,
4518 "Unknown partition map ID for #%u @ %#x: %.23s",
4519 cPartMaps, off, pType2->idPartitionType.achIdentifier);
4520#if 0 /* unreachable code */
4521 paPartMaps[cPartMaps].uVolumeSeqNo = pType2->uVolumeSeqNo;
4522 uPartitionNo = pType2->uPartitionNo;
4523#endif
4524 }
4525 else
4526 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNKNOWN_PART_MAP_ENTRY_TYPE,
4527 "Unknown partition map entry type #%u @ %#x: %u", cPartMaps, off, pHdr->bType);
4528 paPartMaps[cPartMaps].uPartitionNo = uPartitionNo;
4529
4530 /*
4531 * Lookup the partition number and retrieve the relevant info from the partition descriptor.
4532 */
4533 uint32_t i = pInfo->cPartitions;
4534 while (i-- > 0)
4535 {
4536 PUDFPARTITIONDESC pPd = pInfo->apPartitions[i];
4537 if (paPartMaps[cPartMaps].uPartitionNo == pPd->uPartitionNo)
4538 {
4539 paPartMaps[cPartMaps].idxPartDesc = (uint16_t)i;
4540 paPartMaps[cPartMaps].cSectors = pPd->cSectors;
4541 paPartMaps[cPartMaps].offLocation = pPd->offLocation;
4542 paPartMaps[cPartMaps].offByteLocation = (uint64_t)pPd->offLocation * pThis->cbSector;
4543 paPartMaps[cPartMaps].fFlags = pPd->fFlags;
4544 paPartMaps[cPartMaps].uAccessType = pPd->uAccessType;
4545 if (!UDF_ENTITY_ID_EQUALS(&pPd->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
4546 paPartMaps[cPartMaps].fHaveHdr = false;
4547 else
4548 {
4549 paPartMaps[cPartMaps].fHaveHdr = true;
4550 paPartMaps[cPartMaps].Hdr = pPd->ContentsUse.Hdr;
4551 }
4552 break;
4553 }
4554 }
4555 if (i > pInfo->cPartitions)
4556 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_PARTITION_NOT_FOUND,
4557 "Partition #%u (%#x) specified by mapping entry #%u (@ %#x) was not found! (int-type %u)",
4558 uPartitionNo, uPartitionNo, cPartMaps, off, paPartMaps[cPartMaps].bType);
4559
4560 /*
4561 * Advance.
4562 */
4563 cPartMaps++;
4564 off += pHdr->cb;
4565 }
4566
4567 if (cPartMaps < pLvd->cPartitionMaps)
4568 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_INCOMPLETE_PART_MAP_TABLE,
4569 "Only found %u of the %u announced partition mapping table entries",
4570 cPartMaps, pLvd->cPartitionMaps);
4571 }
4572
4573 /* It might be theoretically possible to not use virtual partitions for
4574 accessing data, so just warn if there aren't any. */
4575 if (cPartMaps == 0)
4576 LogRel(("ISO/UDF: Warning: No partition maps!\n"));
4577
4578 /*
4579 * Check out the logical volume descriptor.
4580 */
4581 if ( pLvd->cbLogicalBlock < pThis->cbSector
4582 || pLvd->cbLogicalBlock > RTFSISO_MAX_LOGICAL_BLOCK_SIZE
4583 || (pLvd->cbLogicalBlock % pThis->cbSector) != 0)
4584 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNSUPPORTED_LOGICAL_BLOCK_SIZE,
4585 "Logical block size of %#x is not supported with a sector size of %#x",
4586 pLvd->cbLogicalBlock, pThis->cbSector);
4587
4588 if (!UDF_ENTITY_ID_EQUALS(&pLvd->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
4589 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DOMAIN_ID,
4590 "Unsupported domain ID in logical volume descriptor: '%.23s'", pLvd->idDomain.achIdentifier);
4591
4592 if ( pLvd->ContentsUse.FileSetDescriptor.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED
4593 || pLvd->ContentsUse.FileSetDescriptor.cb == 0
4594 || pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo >= cPartMaps)
4595 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_FILE_SET_DESC_LOCATION,
4596 "Malformed file set descriptor location (type=%u cb=%#x part=%#x)",
4597 pLvd->ContentsUse.FileSetDescriptor.uType,
4598 pLvd->ContentsUse.FileSetDescriptor.cb,
4599 pLvd->ContentsUse.FileSetDescriptor.Location.uPartitionNo);
4600
4601 bool fLvdHaveVolId = !ASMMemIsZero(pLvd->achLogicalVolumeID, sizeof(pLvd->achLogicalVolumeID));
4602 if ( fLvdHaveVolId
4603 && !UDF_IS_CHAR_SET_OSTA(&pLvd->DescCharSet))
4604 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_BAD_LVD_DESC_CHAR_SET,
4605 "Logical volume ID is not using OSTA compressed unicode");
4606
4607 /*
4608 * We can ignore much, if not all of the primary volume descriptor.
4609 */
4610
4611 /*
4612 * We're good. So copy over the data.
4613 */
4614 pThis->Udf.VolInfo.FileSetDescriptor = pLvd->ContentsUse.FileSetDescriptor;
4615 pThis->Udf.VolInfo.cbBlock = pLvd->cbLogicalBlock;
4616 pThis->Udf.VolInfo.cShiftBlock = 9;
4617 while (pThis->Udf.VolInfo.cbBlock != RT_BIT_32(pThis->Udf.VolInfo.cShiftBlock))
4618 pThis->Udf.VolInfo.cShiftBlock++;
4619 pThis->Udf.VolInfo.fFlags = pPvd->fFlags;
4620 pThis->Udf.VolInfo.cPartitions = cPartMaps;
4621 pThis->Udf.VolInfo.paPartitions = paPartMaps;
4622 pInfo->paPartMaps = NULL;
4623 if (fLvdHaveVolId)
4624 memcpy(pThis->Udf.VolInfo.achLogicalVolumeID, pLvd->achLogicalVolumeID, sizeof(pThis->Udf.VolInfo.achLogicalVolumeID));
4625 else
4626 RT_ZERO(pThis->Udf.VolInfo.achLogicalVolumeID);
4627
4628 return VINF_SUCCESS;
4629}
4630
4631
4632/**
4633 * Processes a primary volume descriptor in the VDS (UDF).
4634 *
4635 * @returns IPRT status code.
4636 * @param pInfo Where we gather descriptor information.
4637 * @param pDesc The descriptor.
4638 * @param pErrInfo Where to return extended error information.
4639 */
4640//cmd: kmk VBoxRT && kmk_redirect -E VBOX_LOG_DEST="nofile stderr" -E VBOX_LOG="rt_fs=~0" -E VBOX_LOG_FLAGS="unbuffered enabled" -- e:\vbox\svn\trunk\out\win.amd64\debug\bin\tools\RTLs.exe :iprtvfs:file(open,d:\Downloads\en_windows_10_enterprise_version_1703_updated_march_2017_x64_dvd_10189290.iso,r):vfs(isofs):/ -la
4641static int rtFsIsoVolProcessUdfPrimaryVolDesc(PRTFSISOVDSINFO pInfo, PCUDFPRIMARYVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
4642{
4643#ifdef LOG_ENABLED
4644 Log(("ISO/UDF: Primary volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4645 if (LogIs2Enabled())
4646 {
4647 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4648 UDF_LOG2_MEMBER(pDesc, "#010RX32", uPrimaryVolumeDescNo);
4649 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeID);
4650 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
4651 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
4652 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxVolumeSeqNo);
4653 UDF_LOG2_MEMBER(pDesc, "#06RX16", uInterchangeLevel);
4654 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxInterchangeLevel);
4655 UDF_LOG2_MEMBER(pDesc, "#010RX32", fCharacterSets);
4656 UDF_LOG2_MEMBER(pDesc, "#010RX32", fMaxCharacterSets);
4657 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeSetID);
4658 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
4659 UDF_LOG2_MEMBER_CHARSPEC(pDesc, ExplanatoryCharSet);
4660 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeAbstract);
4661 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeCopyrightNotice);
4662 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idApplication);
4663 UDF_LOG2_MEMBER_TIMESTAMP(pDesc, RecordingTimestamp);
4664 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4665 if (!ASMMemIsZero(&pDesc->abImplementationUse, sizeof(pDesc->abImplementationUse)))
4666 Log2(("ISO/UDF: %-32s %.64Rhxs\n", "abReserved[64]:", &pDesc->abImplementationUse[0]));
4667 UDF_LOG2_MEMBER(pDesc, "#010RX32", offPredecessorVolDescSeq);
4668 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
4669 if (!ASMMemIsZero(&pDesc->abReserved, sizeof(pDesc->abReserved)))
4670 Log2(("ISO/UDF: %-32s %.22Rhxs\n", "abReserved[22]:", &pDesc->abReserved[0]));
4671 }
4672#endif
4673
4674 /*
4675 * Check if this is a new revision of an existing primary volume descriptor.
4676 */
4677 PUDFPRIMARYVOLUMEDESC pEndianConvert = NULL;
4678 uint32_t i = pInfo->cPrimaryVols;
4679 while (i--> 0)
4680 {
4681 if ( memcmp(pDesc->achVolumeID, pInfo->apPrimaryVols[i]->achVolumeID, sizeof(pDesc->achVolumeID)) == 0
4682 && memcmp(&pDesc->DescCharSet, &pInfo->apPrimaryVols[i]->DescCharSet, sizeof(pDesc->DescCharSet)) == 0)
4683 {
4684 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPrimaryVols[i]->uVolumeDescSeqNo)
4685 {
4686 Log(("ISO/UDF: Primary descriptor prevails over previous! (%u >= %u)\n",
4687 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4688 pEndianConvert = pInfo->apPrimaryVols[i];
4689 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
4690 }
4691 else
4692 Log(("ISO/UDF: Primary descriptor has lower sequence number than the previous! (%u < %u)\n",
4693 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4694 break;
4695 }
4696 }
4697 if (i >= pInfo->cPrimaryVols)
4698 {
4699 /*
4700 * It wasn't. Append it.
4701 */
4702 i = pInfo->cPrimaryVols;
4703 if (i < RT_ELEMENTS(pInfo->apPrimaryVols))
4704 {
4705 pInfo->apPrimaryVols[i] = pEndianConvert = (PUDFPRIMARYVOLUMEDESC)RTMemDup(pDesc, sizeof(*pDesc));
4706 if (pEndianConvert)
4707 pInfo->cPrimaryVols = i + 1;
4708 else
4709 return VERR_NO_MEMORY;
4710 Log2(("ISO/UDF: ++New primary descriptor.\n"));
4711 }
4712 else
4713 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PVDS, "Encountered too many primary volume descriptors");
4714 }
4715
4716#ifdef RT_BIG_ENDIAN
4717 /*
4718 * Do endian conversion of the descriptor.
4719 */
4720 if (pEndianConvert)
4721 {
4722 AssertFailed();
4723 }
4724#else
4725 RT_NOREF(pEndianConvert);
4726#endif
4727 return VINF_SUCCESS;
4728}
4729
4730
4731/**
4732 * Processes an logical volume descriptor in the VDS (UDF).
4733 *
4734 * @returns IPRT status code.
4735 * @param pInfo Where we gather descriptor information.
4736 * @param pDesc The descriptor.
4737 * @param cbSector The sector size (UDF defines the logical and physical
4738 * sector size to be the same).
4739 * @param pErrInfo Where to return extended error information.
4740 */
4741static int rtFsIsoVolProcessUdfLogicalVolumeDesc(PRTFSISOVDSINFO pInfo, PCUDFLOGICALVOLUMEDESC pDesc,
4742 uint32_t cbSector, PRTERRINFO pErrInfo)
4743{
4744#ifdef LOG_ENABLED
4745 Log(("ISO/UDF: Logical volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4746 if (LogIs2Enabled())
4747 {
4748 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4749 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
4750 UDF_LOG2_MEMBER_DSTRING(pDesc, achLogicalVolumeID);
4751 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbLogicalBlock);
4752 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idDomain);
4753 if (UDF_ENTITY_ID_EQUALS(&pDesc->idDomain, UDF_ENTITY_ID_LVD_DOMAIN))
4754 UDF_LOG2_MEMBER_LONGAD(pDesc, ContentsUse.FileSetDescriptor);
4755 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
4756 Log2(("ISO/UDF: %-32s %.16Rhxs\n", "ContentsUse.ab[16]:", &pDesc->ContentsUse.ab[0]));
4757 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbMapTable);
4758 UDF_LOG2_MEMBER(pDesc, "#010RX32", cPartitionMaps);
4759 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4760 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4761 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
4762 UDF_LOG2_MEMBER_EXTENTAD(pDesc, IntegritySeqExtent);
4763 if (pDesc->cbMapTable)
4764 {
4765 Log2(("ISO/UDF: %-32s\n", "abPartitionMaps"));
4766 uint32_t iMap = 0;
4767 uint32_t off = 0;
4768 while (off + sizeof(UDFPARTMAPHDR) <= pDesc->cbMapTable)
4769 {
4770 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pDesc->abPartitionMaps[off];
4771 Log2(("ISO/UDF: %02u @ %#05x: type %u, length %u\n", iMap, off, pHdr->bType, pHdr->cb));
4772 if (off + pHdr->cb > pDesc->cbMapTable)
4773 {
4774 Log2(("ISO/UDF: BAD! Entry is %d bytes too long!\n", off + pHdr->cb - pDesc->cbMapTable));
4775 break;
4776 }
4777 if (pHdr->bType == 1)
4778 {
4779 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
4780 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uVolumeSeqNo, 5);
4781 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uPartitionNo, 5);
4782 }
4783 else if (pHdr->bType == 2)
4784 {
4785 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
4786 UDF_LOG2_MEMBER_ENTITY_ID_EX(pType2, idPartitionType, 5);
4787 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uVolumeSeqNo, 5);
4788 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uPartitionNo, 5);
4789 }
4790 else
4791 Log2(("ISO/UDF: BAD! Unknown type!\n"));
4792
4793 /* advance */
4794 off += pHdr->cb;
4795 iMap++;
4796 }
4797 }
4798 }
4799#endif
4800
4801 /*
4802 * Check if this is a newer revision of an existing primary volume descriptor.
4803 */
4804 size_t cbDesc = (size_t)pDesc->cbMapTable + RT_UOFFSETOF(UDFLOGICALVOLUMEDESC, abPartitionMaps);
4805 if ( pDesc->cbMapTable >= (UINT32_MAX >> 1)
4806 || cbDesc > cbSector)
4807 {
4808 Log(("ISO/UDF: Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector));
4809 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD,
4810 "Logical volume descriptor is too big: %#zx (cbSector=%#x)\n", cbDesc, cbSector);
4811 }
4812
4813 PUDFLOGICALVOLUMEDESC pEndianConvert = NULL;
4814 uint32_t i = pInfo->cLogicalVols;
4815 while (i--> 0)
4816 if ( memcmp(pDesc->achLogicalVolumeID, pInfo->apLogicalVols[i]->achLogicalVolumeID,
4817 sizeof(pDesc->achLogicalVolumeID)) == 0
4818 && memcmp(&pDesc->DescCharSet, &pInfo->apLogicalVols[i]->DescCharSet,
4819 sizeof(pDesc->DescCharSet)) == 0)
4820 {
4821 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apLogicalVols[i]->uVolumeDescSeqNo)
4822 {
4823 Log(("ISO/UDF: Logical descriptor prevails over previous! (%u >= %u)\n",
4824 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
4825 pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
4826 if (!pEndianConvert)
4827 return VERR_NO_MEMORY;
4828 RTMemFree(pInfo->apLogicalVols[i]);
4829 pInfo->apLogicalVols[i] = pEndianConvert;
4830 }
4831 else
4832 Log(("ISO/UDF: Logical descriptor has lower sequence number than the previous! (%u >= %u)\n",
4833 RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apLogicalVols[i]->uVolumeDescSeqNo));
4834 break;
4835 }
4836 if (i >= pInfo->cLogicalVols)
4837 {
4838 /*
4839 * It wasn't. Append it.
4840 */
4841 i = pInfo->cLogicalVols;
4842 if (i < RT_ELEMENTS(pInfo->apLogicalVols))
4843 {
4844 pInfo->apLogicalVols[i] = pEndianConvert = (PUDFLOGICALVOLUMEDESC)RTMemDup(pDesc, cbDesc);
4845 if (pEndianConvert)
4846 pInfo->cLogicalVols = i + 1;
4847 else
4848 return VERR_NO_MEMORY;
4849 Log2(("ISO/UDF: ++New logical volume descriptor.\n"));
4850 }
4851 else
4852 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_LVDS, "Too many logical volume descriptors");
4853 }
4854
4855#ifdef RT_BIG_ENDIAN
4856 /*
4857 * Do endian conversion of the descriptor.
4858 */
4859 if (pEndianConvert)
4860 {
4861 AssertFailed();
4862 }
4863#else
4864 RT_NOREF(pEndianConvert);
4865#endif
4866 return VINF_SUCCESS;
4867}
4868
4869
4870/**
4871 * Processes an partition descriptor in the VDS (UDF).
4872 *
4873 * @returns IPRT status code.
4874 * @param pInfo Where we gather descriptor information.
4875 * @param pDesc The descriptor.
4876 * @param pErrInfo Where to return extended error information.
4877 */
4878static int rtFsIsoVolProcessUdfPartitionDesc(PRTFSISOVDSINFO pInfo, PCUDFPARTITIONDESC pDesc, PRTERRINFO pErrInfo)
4879{
4880#ifdef LOG_ENABLED
4881 Log(("ISO/UDF: Partition descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4882 if (LogIs2Enabled())
4883 {
4884 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4885 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
4886 UDF_LOG2_MEMBER(pDesc, "#06RX16", uPartitionNo);
4887 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, PartitionContents);
4888 if (UDF_ENTITY_ID_EQUALS(&pDesc->PartitionContents, UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF))
4889 {
4890 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceTable);
4891 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceBitmap);
4892 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.PartitionIntegrityTable);
4893 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceTable);
4894 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceBitmap);
4895 if (!ASMMemIsZero(&pDesc->ContentsUse.Hdr.abReserved[0], sizeof(pDesc->ContentsUse.Hdr.abReserved)))
4896 Log2(("ISO/UDF: %-32s\n%.88RhxD\n", "Hdr.abReserved[88]:", &pDesc->ContentsUse.Hdr.abReserved[0]));
4897 }
4898 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
4899 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ContentsUse.ab[128]:", &pDesc->ContentsUse.ab[0]));
4900 UDF_LOG2_MEMBER(pDesc, "#010RX32", uAccessType);
4901 UDF_LOG2_MEMBER(pDesc, "#010RX32", offLocation);
4902 UDF_LOG2_MEMBER(pDesc, "#010RX32", cSectors);
4903 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4904 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4905 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
4906
4907 if (!ASMMemIsZero(&pDesc->abReserved[0], sizeof(pDesc->abReserved)))
4908 Log2(("ISO/UDF: %-32s\n%.156RhxD\n", "ImplementationUse.ab[156]:", &pDesc->abReserved[0]));
4909 }
4910#endif
4911
4912 /*
4913 * Check if this is a newer revision of an existing primary volume descriptor.
4914 */
4915 PUDFPARTITIONDESC pEndianConvert = NULL;
4916 uint32_t i = pInfo->cPartitions;
4917 while (i--> 0)
4918 if (pDesc->uPartitionNo == pInfo->apPartitions[i]->uPartitionNo)
4919 {
4920 if (RT_LE2H_U32(pDesc->uVolumeDescSeqNo) >= pInfo->apPartitions[i]->uVolumeDescSeqNo)
4921 {
4922 Log(("ISO/UDF: Partition descriptor for part %#u prevails over previous! (%u >= %u)\n",
4923 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4924 pEndianConvert = pInfo->apPartitions[i];
4925 memcpy(pEndianConvert, pDesc, sizeof(*pDesc));
4926 }
4927 else
4928 Log(("ISO/UDF: Partition descriptor for part %#u has a lower sequence number than the previous! (%u < %u)\n",
4929 pDesc->uPartitionNo, RT_LE2H_U32(pDesc->uVolumeDescSeqNo), pInfo->apPartitions[i]->uVolumeDescSeqNo));
4930 break;
4931 }
4932 if (i >= pInfo->cPartitions)
4933 {
4934 /*
4935 * It wasn't. Append it.
4936 */
4937 i = pInfo->cPartitions;
4938 if (i < RT_ELEMENTS(pInfo->apPartitions))
4939 {
4940 pInfo->apPartitions[i] = pEndianConvert = (PUDFPARTITIONDESC)RTMemDup(pDesc, sizeof(*pDesc));
4941 if (pEndianConvert)
4942 pInfo->cPartitions = i + 1;
4943 else
4944 return VERR_NO_MEMORY;
4945 Log2(("ISO/UDF: ++New partition descriptor.\n"));
4946 }
4947 else
4948 return RTERRINFO_LOG_SET(pErrInfo, VERR_ISOFS_TOO_MANY_PDS, "Too many physical volume descriptors");
4949 }
4950
4951#ifdef RT_BIG_ENDIAN
4952 /*
4953 * Do endian conversion of the descriptor.
4954 */
4955 if (pEndianConvert)
4956 {
4957 AssertFailed();
4958 }
4959#else
4960 RT_NOREF(pEndianConvert);
4961#endif
4962 return VINF_SUCCESS;
4963}
4964
4965
4966/**
4967 * Processes an implementation use descriptor in the VDS (UDF).
4968 *
4969 * @returns IPRT status code.
4970 * @param pInfo Where we gather descriptor information.
4971 * @param pDesc The descriptor.
4972 * @param pErrInfo Where to return extended error information.
4973 */
4974static int rtFsIsoVolProcessUdfImplUseVolDesc(PRTFSISOVDSINFO pInfo, PCUDFIMPLEMENTATIONUSEVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
4975{
4976#ifdef LOG_ENABLED
4977 Log(("ISO/UDF: Implementation use volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
4978 if (LogIs2Enabled())
4979 {
4980 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
4981 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
4982 if (UDF_ENTITY_ID_EQUALS(&pDesc->idImplementation, UDF_ENTITY_ID_IUVD_IMPLEMENTATION))
4983 {
4984 UDF_LOG2_MEMBER_CHARSPEC(&pDesc->ImplementationUse, Lvi.Charset);
4985 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achVolumeID);
4986 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo1);
4987 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo2);
4988 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo3);
4989 UDF_LOG2_MEMBER_ENTITY_ID(&pDesc->ImplementationUse, Lvi.idImplementation);
4990 if (!ASMMemIsZero(&pDesc->ImplementationUse.Lvi.abUse[0], sizeof(pDesc->ImplementationUse.Lvi.abUse)))
4991 Log2(("ISO/UDF: %-32s\n%.128RhxD\n", "Lvi.abUse[128]:", &pDesc->ImplementationUse.Lvi.abUse[0]));
4992 }
4993 else if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
4994 Log2(("ISO/UDF: %-32s\n%.460RhxD\n", "ImplementationUse.ab[460]:", &pDesc->ImplementationUse.ab[0]));
4995 }
4996#endif
4997
4998 RT_NOREF(pInfo, pDesc, pErrInfo);
4999 return VINF_SUCCESS;
5000}
5001
5002
5003
5004typedef struct RTFSISOSEENSEQENCES
5005{
5006 /** Number of sequences we've seen thus far. */
5007 uint32_t cSequences;
5008 /** The per sequence data. */
5009 struct
5010 {
5011 uint64_t off; /**< Byte offset of the sequence. */
5012 uint32_t cb; /**< Size of the sequence. */
5013 } aSequences[8];
5014} RTFSISOSEENSEQENCES;
5015typedef RTFSISOSEENSEQENCES *PRTFSISOSEENSEQENCES;
5016
5017
5018
5019/**
5020 * Process a VDS sequence, recursively dealing with volume descriptor pointers.
5021 *
5022 * This function only gathers information from the sequence, handling the
5023 * prevailing descriptor fun.
5024 *
5025 * @returns IPRT status code.
5026 * @param pThis The instance.
5027 * @param pInfo Where to store info from the VDS sequence.
5028 * @param offSeq The byte offset of the sequence.
5029 * @param cbSeq The length of the sequence.
5030 * @param pbBuf Read buffer.
5031 * @param cbBuf Size of the read buffer. This is at least one
5032 * sector big.
5033 * @param cNestings The VDS nesting depth.
5034 * @param pErrInfo Where to return extended error info.
5035 */
5036static int rtFsIsoVolReadAndProcessUdfVdsSeq(PRTFSISOVOL pThis, PRTFSISOVDSINFO pInfo, uint64_t offSeq, uint32_t cbSeq,
5037 uint8_t *pbBuf, size_t cbBuf, uint32_t cNestings, PRTERRINFO pErrInfo)
5038{
5039 AssertReturn(cbBuf >= pThis->cbSector, VERR_INTERNAL_ERROR);
5040
5041 /*
5042 * Check nesting depth.
5043 */
5044 if (cNestings > 5)
5045 return RTERRINFO_LOG_SET(pErrInfo, VERR_TOO_MUCH_DATA, "The volume descriptor sequence (VDS) is nested too deeply.");
5046
5047
5048 /*
5049 * Do the processing sector by sector to keep things simple.
5050 */
5051 uint32_t offInSeq = 0;
5052 while (offInSeq < cbSeq)
5053 {
5054 int rc;
5055
5056 /*
5057 * Read the next sector. Zero pad if less that a sector.
5058 */
5059 Assert((offInSeq & (pThis->cbSector - 1)) == 0);
5060 rc = RTVfsFileReadAt(pThis->hVfsBacking, offSeq + offInSeq, pbBuf, pThis->cbSector, NULL);
5061 if (RT_FAILURE(rc))
5062 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error reading VDS content at %RX64 (LB %#x): %Rrc",
5063 offSeq + offInSeq, pThis->cbSector, rc);
5064 if (cbSeq - offInSeq < pThis->cbSector)
5065 memset(&pbBuf[cbSeq - offInSeq], 0, pThis->cbSector - (cbSeq - offInSeq));
5066
5067 /*
5068 * Check tag.
5069 */
5070 PCUDFTAG pTag = (PCUDFTAG)pbBuf;
5071 rc = rtFsIsoVolValidateUdfDescTagAndCrc(pTag, pThis->cbSector, UINT16_MAX, (offSeq + offInSeq) / pThis->cbSector, pErrInfo);
5072 if ( RT_SUCCESS(rc)
5073 || ( rc == VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
5074 && ( pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC
5075 || pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_DESC
5076 || pTag->idTag == UDF_TAG_ID_UNALLOCATED_SPACE_DESC
5077 )
5078 )
5079 )
5080 {
5081 switch (pTag->idTag)
5082 {
5083 case UDF_TAG_ID_PRIMARY_VOL_DESC:
5084 rc = rtFsIsoVolProcessUdfPrimaryVolDesc(pInfo, (PCUDFPRIMARYVOLUMEDESC)pTag, pErrInfo);
5085 break;
5086
5087 case UDF_TAG_ID_IMPLEMENTATION_USE_VOLUME_DESC:
5088 rc = rtFsIsoVolProcessUdfImplUseVolDesc(pInfo, (PCUDFIMPLEMENTATIONUSEVOLUMEDESC)pTag, pErrInfo);
5089 break;
5090
5091 case UDF_TAG_ID_PARTITION_DESC:
5092 rc = rtFsIsoVolProcessUdfPartitionDesc(pInfo, (PCUDFPARTITIONDESC)pTag, pErrInfo);
5093 break;
5094
5095 case UDF_TAG_ID_LOGICAL_VOLUME_DESC:
5096 if (rc != VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC)
5097 rc = rtFsIsoVolProcessUdfLogicalVolumeDesc(pInfo, (PCUDFLOGICALVOLUMEDESC)pTag,
5098 pThis->cbSector, pErrInfo);
5099 else
5100 rc = VERR_ISOFS_TOO_BIT_PARTMAP_IN_LVD;
5101 break;
5102
5103 case UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC:
5104 Log(("ISO/UDF: Ignoring logical volume integrity descriptor at offset %#RX64.\n", offSeq + offInSeq));
5105 rc = VINF_SUCCESS;
5106 break;
5107
5108 case UDF_TAG_ID_UNALLOCATED_SPACE_DESC:
5109 Log(("ISO/UDF: Ignoring unallocated space descriptor at offset %#RX64.\n", offSeq + offInSeq));
5110 rc = VINF_SUCCESS;
5111 break;
5112
5113 case UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR:
5114 Log(("ISO/UDF: Ignoring AVDP in VDS (at offset %#RX64).\n", offSeq + offInSeq));
5115 rc = VINF_SUCCESS;
5116 break;
5117
5118 case UDF_TAG_ID_VOLUME_DESC_PTR:
5119 {
5120 PCUDFVOLUMEDESCPTR pVdp = (PCUDFVOLUMEDESCPTR)pTag;
5121 Log(("ISO/UDF: Processing volume descriptor pointer at offset %#RX64: %#x LB %#x (seq %#x); cNestings=%d\n",
5122 offSeq + offInSeq, pVdp->NextVolumeDescSeq.off, pVdp->NextVolumeDescSeq.cb,
5123 pVdp->uVolumeDescSeqNo, cNestings));
5124 rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, pInfo, (uint64_t)pVdp->NextVolumeDescSeq.off * pThis->cbSector,
5125 pVdp->NextVolumeDescSeq.cb, pbBuf, cbBuf, cNestings + 1, pErrInfo);
5126 break;
5127 }
5128
5129 case UDF_TAG_ID_TERMINATING_DESC:
5130 Log(("ISO/UDF: Terminating descriptor at offset %#RX64\n", offSeq + offInSeq));
5131 return VINF_SUCCESS;
5132
5133 default:
5134 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_ISOFS_UNEXPECTED_VDS_DESC,
5135 "Unexpected/unknown VDS descriptor %#x at byte offset %#RX64",
5136 pThis->cbSector, offSeq + offInSeq);
5137 }
5138 if (RT_FAILURE(rc))
5139 return rc;
5140 }
5141 /* The descriptor sequence is usually zero padded to 16 sectors. Just
5142 ignore zero descriptors. */
5143 else if (rc != VERR_ISOFS_TAG_IS_ALL_ZEROS)
5144 return rc;
5145
5146 /*
5147 * Advance.
5148 */
5149 offInSeq += pThis->cbSector;
5150 }
5151
5152 return VINF_SUCCESS;
5153}
5154
5155
5156
5157/**
5158 * Processes a volume descriptor sequence (VDS).
5159 *
5160 * @returns IPRT status code.
5161 * @param pThis The instance.
5162 * @param offSeq The byte offset of the sequence.
5163 * @param cbSeq The length of the sequence.
5164 * @param pSeenSequences Structure where to keep track of VDSes we've already
5165 * processed, to avoid redoing one that we don't
5166 * understand.
5167 * @param pbBuf Read buffer.
5168 * @param cbBuf Size of the read buffer. This is at least one
5169 * sector big.
5170 * @param pErrInfo Where to report extended error information.
5171 */
5172static int rtFsIsoVolReadAndProcessUdfVds(PRTFSISOVOL pThis, uint64_t offSeq, uint32_t cbSeq,
5173 PRTFSISOSEENSEQENCES pSeenSequences, uint8_t *pbBuf, size_t cbBuf,
5174 PRTERRINFO pErrInfo)
5175{
5176 /*
5177 * Skip if already seen.
5178 */
5179 uint32_t i = pSeenSequences->cSequences;
5180 while (i-- > 0)
5181 if ( pSeenSequences->aSequences[i].off == offSeq
5182 && pSeenSequences->aSequences[i].cb == cbSeq)
5183 return VERR_NOT_FOUND;
5184
5185 /* Not seen, so add it. */
5186 Assert(pSeenSequences->cSequences + 1 <= RT_ELEMENTS(pSeenSequences->aSequences));
5187 pSeenSequences->aSequences[pSeenSequences->cSequences].cb = cbSeq;
5188 pSeenSequences->aSequences[pSeenSequences->cSequences].off = offSeq;
5189 pSeenSequences->cSequences++;
5190
5191 LogFlow(("ISO/UDF: Processing anchor volume descriptor sequence at offset %#RX64 LB %#RX32\n", offSeq, cbSeq));
5192
5193 /*
5194 * Gather relevant descriptor info from the VDS then process it and on
5195 * success copy it into the instance.
5196 *
5197 * The processing has to be done in a different function because there may
5198 * be links to sub-sequences that needs to be processed. We do this by
5199 * recursing and check that we don't go to deep.
5200 */
5201 RTFSISOVDSINFO Info;
5202 RT_ZERO(Info);
5203 int rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, &Info, offSeq, cbSeq, pbBuf, cbBuf, 0, pErrInfo);
5204 if (RT_SUCCESS(rc))
5205 {
5206 rc = rtFsIsoVolProcessUdfVdsSeqInfo(pThis, &Info, pErrInfo);
5207 if (RT_SUCCESS(rc))
5208 rc = rtFsIsoVolProcessUdfFileSetDescs(pThis, pbBuf, cbBuf, pErrInfo);
5209 }
5210
5211 /*
5212 * Clean up info.
5213 */
5214 i = Info.cPrimaryVols;
5215 while (i-- > 0)
5216 RTMemFree(Info.apPrimaryVols[i]);
5217
5218 i = Info.cLogicalVols;
5219 while (i-- > 0)
5220 RTMemFree(Info.apLogicalVols[i]);
5221
5222 i = Info.cPartitions;
5223 while (i-- > 0)
5224 RTMemFree(Info.apPartitions[i]);
5225
5226 RTMemFree(Info.paPartMaps);
5227
5228 return rc;
5229}
5230
5231
5232static int rtFsIsoVolReadAndHandleUdfAvdp(PRTFSISOVOL pThis, uint64_t offAvdp, uint8_t *pbBuf, size_t cbBuf,
5233 PRTFSISOSEENSEQENCES pSeenSequences, PRTERRINFO pErrInfo)
5234{
5235 /*
5236 * Try read the descriptor and validate its tag.
5237 */
5238 PUDFANCHORVOLUMEDESCPTR pAvdp = (PUDFANCHORVOLUMEDESCPTR)pbBuf;
5239 size_t cbAvdpRead = RT_MIN(pThis->cbSector, cbBuf);
5240 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offAvdp, pAvdp, cbAvdpRead, NULL);
5241 if (RT_SUCCESS(rc))
5242 {
5243 rc = rtFsIsoVolValidateUdfDescTag(&pAvdp->Tag, UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR, offAvdp / pThis->cbSector, pErrInfo);
5244 if (RT_SUCCESS(rc))
5245 {
5246 Log2(("ISO/UDF: AVDP: MainVolumeDescSeq=%#RX32 LB %#RX32, ReserveVolumeDescSeq=%#RX32 LB %#RX32\n",
5247 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb,
5248 pAvdp->ReserveVolumeDescSeq.off, pAvdp->ReserveVolumeDescSeq.cb));
5249
5250 /*
5251 * Try the main sequence if it looks sane.
5252 */
5253 UDFEXTENTAD const ReserveVolumeDescSeq = pAvdp->ReserveVolumeDescSeq;
5254 if ( pAvdp->MainVolumeDescSeq.off < pThis->cBackingSectors
5255 && (uint64_t)pAvdp->MainVolumeDescSeq.off
5256 + (pAvdp->MainVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
5257 <= pThis->cBackingSectors)
5258 {
5259 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)pAvdp->MainVolumeDescSeq.off * pThis->cbSector,
5260 pAvdp->MainVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
5261 if (RT_SUCCESS(rc))
5262 return rc;
5263 }
5264 else
5265 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
5266 "MainVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
5267 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb, pThis->cBackingSectors);
5268 if (ReserveVolumeDescSeq.cb > 0)
5269 {
5270 if ( ReserveVolumeDescSeq.off < pThis->cBackingSectors
5271 && (uint64_t)ReserveVolumeDescSeq.off
5272 + (ReserveVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
5273 <= pThis->cBackingSectors)
5274 {
5275 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)ReserveVolumeDescSeq.off * pThis->cbSector,
5276 ReserveVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
5277 if (RT_SUCCESS(rc))
5278 return rc;
5279 }
5280 else if (RT_SUCCESS(rc))
5281 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_FOUND,
5282 "ReserveVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
5283 ReserveVolumeDescSeq.off, ReserveVolumeDescSeq.cb, pThis->cBackingSectors);
5284 }
5285 }
5286 }
5287 else
5288 rc = RTERRINFO_LOG_SET_F(pErrInfo, rc,
5289 "Error reading sector at offset %#RX64 (anchor volume descriptor pointer): %Rrc", offAvdp, rc);
5290
5291 return rc;
5292}
5293
5294
5295/**
5296 * Goes looking for UDF when we've seens a volume recognition sequence.
5297 *
5298 * @returns IPRT status code.
5299 * @param pThis The volume instance data.
5300 * @param puUdfLevel The UDF level indicated by the VRS.
5301 * @param offUdfBootVolDesc The offset of the BOOT2 descriptor, UINT64_MAX
5302 * if not encountered.
5303 * @param pbBuf Buffer for reading into.
5304 * @param cbBuf The size of the buffer. At least one sector.
5305 * @param pErrInfo Where to return extended error info.
5306 */
5307static int rtFsIsoVolHandleUdfDetection(PRTFSISOVOL pThis, uint8_t *puUdfLevel, uint64_t offUdfBootVolDesc,
5308 uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
5309{
5310 NOREF(offUdfBootVolDesc);
5311
5312 /*
5313 * There are up to three anchor volume descriptor pointers that can give us
5314 * two different descriptor sequences each. Usually, the different AVDP
5315 * structures points to the same two sequences. The idea here is that
5316 * sectors may deteriorate and become unreadable, and we're supposed to try
5317 * out alternative sectors to get the job done. If we really took this
5318 * seriously, we could try read all sequences in parallel and use the
5319 * sectors that are good. However, we'll try keep things reasonably simple
5320 * since we'll most likely be reading from hard disks rather than optical
5321 * media.
5322 *
5323 * We keep track of which sequences we've processed so we don't try to do it
5324 * again when alternative AVDP sectors points to the same sequences.
5325 */
5326 pThis->Udf.uLevel = *puUdfLevel;
5327 RTFSISOSEENSEQENCES SeenSequences = { 0 };
5328 int rc1 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, 256 * pThis->cbSector, pbBuf, cbBuf,
5329 &SeenSequences, pErrInfo);
5330 if (RT_SUCCESS(rc1))
5331 return rc1;
5332
5333 int rc2 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - 256 * pThis->cbSector,
5334 pbBuf, cbBuf, &SeenSequences, pErrInfo);
5335 if (RT_SUCCESS(rc2))
5336 return rc2;
5337
5338 int rc3 = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - pThis->cbSector,
5339 pbBuf, cbBuf, &SeenSequences, pErrInfo);
5340 if (RT_SUCCESS(rc3))
5341 return rc3;
5342
5343 /*
5344 * Return failure if the alternatives have been excluded.
5345 *
5346 * Note! The error info won't be correct here.
5347 */
5348 pThis->Udf.uLevel = *puUdfLevel = 0;
5349
5350 if (RTFSISO9660_F_IS_ONLY_TYPE(pThis->fFlags, RTFSISO9660_F_NO_UDF))
5351 return rc1 != VERR_NOT_FOUND ? rc1 : rc2 != VERR_NOT_FOUND ? rc2 : rc3;
5352 return VINF_SUCCESS;
5353}
5354
5355
5356
5357#ifdef LOG_ENABLED
5358
5359/** Logging helper. */
5360static size_t rtFsIsoVolGetStrippedLength(const char *pachField, size_t cchField)
5361{
5362 while (cchField > 0 && pachField[cchField - 1] == ' ')
5363 cchField--;
5364 return cchField;
5365}
5366
5367/** Logging helper. */
5368static char *rtFsIsoVolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
5369{
5370 /* Check the format by looking for zero bytes. ISO-9660 doesn't allow zeros.
5371 This doesn't have to be a UTF-16BE string. */
5372 size_t cFirstZeros = 0;
5373 size_t cSecondZeros = 0;
5374 for (size_t off = 0; off + 1 < cchField; off += 2)
5375 {
5376 cFirstZeros += pachField[off] == '\0';
5377 cSecondZeros += pachField[off + 1] == '\0';
5378 }
5379
5380 int rc = VINF_SUCCESS;
5381 char *pszTmp = &pszDst[10];
5382 size_t cchRet = 0;
5383 if (cFirstZeros > cSecondZeros)
5384 {
5385 /* UTF-16BE / UTC-2BE: */
5386 if (cchField & 1)
5387 {
5388 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
5389 cchField--;
5390 else
5391 rc = VERR_INVALID_UTF16_ENCODING;
5392 }
5393 if (RT_SUCCESS(rc))
5394 {
5395 while ( cchField >= 2
5396 && pachField[cchField - 1] == ' '
5397 && pachField[cchField - 2] == '\0')
5398 cchField -= 2;
5399
5400 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
5401 }
5402 if (RT_SUCCESS(rc))
5403 {
5404 pszDst[0] = 'U';
5405 pszDst[1] = 'T';
5406 pszDst[2] = 'F';
5407 pszDst[3] = '-';
5408 pszDst[4] = '1';
5409 pszDst[5] = '6';
5410 pszDst[6] = 'B';
5411 pszDst[7] = 'E';
5412 pszDst[8] = ':';
5413 pszDst[9] = '\'';
5414 pszDst[10 + cchRet] = '\'';
5415 pszDst[10 + cchRet + 1] = '\0';
5416 }
5417 else
5418 RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
5419 }
5420 else if (cSecondZeros > 0)
5421 {
5422 /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
5423 if (cchField & 1)
5424 {
5425 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
5426 cchField--;
5427 else
5428 rc = VERR_INVALID_UTF16_ENCODING;
5429 }
5430 if (RT_SUCCESS(rc))
5431 {
5432 while ( cchField >= 2
5433 && pachField[cchField - 1] == '\0'
5434 && pachField[cchField - 2] == ' ')
5435 cchField -= 2;
5436
5437 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
5438 }
5439 if (RT_SUCCESS(rc))
5440 {
5441 pszDst[0] = 'U';
5442 pszDst[1] = 'T';
5443 pszDst[2] = 'F';
5444 pszDst[3] = '-';
5445 pszDst[4] = '1';
5446 pszDst[5] = '6';
5447 pszDst[6] = 'L';
5448 pszDst[7] = 'E';
5449 pszDst[8] = ':';
5450 pszDst[9] = '\'';
5451 pszDst[10 + cchRet] = '\'';
5452 pszDst[10 + cchRet + 1] = '\0';
5453 }
5454 else
5455 RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
5456 }
5457 else
5458 {
5459 /* ASSUME UTF-8/ASCII. */
5460 while ( cchField > 0
5461 && pachField[cchField - 1] == ' ')
5462 cchField--;
5463 rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
5464 if (RT_SUCCESS(rc))
5465 RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
5466 else
5467 RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
5468 }
5469 return pszDst;
5470}
5471
5472
5473/**
5474 * Logs the primary or supplementary volume descriptor
5475 *
5476 * @param pVolDesc The descriptor.
5477 */
5478static void rtFsIsoVolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
5479{
5480 if (LogIs2Enabled())
5481 {
5482 char szTmp[384];
5483 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
5484 Log2(("ISO9660: achSystemId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
5485 Log2(("ISO9660: achVolumeId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
5486 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
5487 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
5488 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIsoVolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
5489 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
5490 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
5491 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
5492 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
5493 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
5494 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
5495 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
5496 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
5497 Log2(("ISO9660: achVolumeSetId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
5498 Log2(("ISO9660: achPublisherId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
5499 Log2(("ISO9660: achDataPreparerId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
5500 Log2(("ISO9660: achApplicationId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
5501 Log2(("ISO9660: achCopyrightFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
5502 Log2(("ISO9660: achAbstractFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
5503 Log2(("ISO9660: achBibliographicFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
5504 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5505 pVolDesc->BirthTime.achYear,
5506 pVolDesc->BirthTime.achMonth,
5507 pVolDesc->BirthTime.achDay,
5508 pVolDesc->BirthTime.achHour,
5509 pVolDesc->BirthTime.achMinute,
5510 pVolDesc->BirthTime.achSecond,
5511 pVolDesc->BirthTime.achCentisecond,
5512 pVolDesc->BirthTime.offUtc*4/60));
5513 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5514 pVolDesc->ModifyTime.achYear,
5515 pVolDesc->ModifyTime.achMonth,
5516 pVolDesc->ModifyTime.achDay,
5517 pVolDesc->ModifyTime.achHour,
5518 pVolDesc->ModifyTime.achMinute,
5519 pVolDesc->ModifyTime.achSecond,
5520 pVolDesc->ModifyTime.achCentisecond,
5521 pVolDesc->ModifyTime.offUtc*4/60));
5522 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5523 pVolDesc->ExpireTime.achYear,
5524 pVolDesc->ExpireTime.achMonth,
5525 pVolDesc->ExpireTime.achDay,
5526 pVolDesc->ExpireTime.achHour,
5527 pVolDesc->ExpireTime.achMinute,
5528 pVolDesc->ExpireTime.achSecond,
5529 pVolDesc->ExpireTime.achCentisecond,
5530 pVolDesc->ExpireTime.offUtc*4/60));
5531 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
5532 pVolDesc->EffectiveTime.achYear,
5533 pVolDesc->EffectiveTime.achMonth,
5534 pVolDesc->EffectiveTime.achDay,
5535 pVolDesc->EffectiveTime.achHour,
5536 pVolDesc->EffectiveTime.achMinute,
5537 pVolDesc->EffectiveTime.achSecond,
5538 pVolDesc->EffectiveTime.achCentisecond,
5539 pVolDesc->EffectiveTime.offUtc*4/60));
5540 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
5541 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
5542
5543 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
5544 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
5545 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
5546 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
5547 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
5548 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
5549 pVolDesc->RootDir.DirRec.RecTime.bMonth,
5550 pVolDesc->RootDir.DirRec.RecTime.bDay,
5551 pVolDesc->RootDir.DirRec.RecTime.bHour,
5552 pVolDesc->RootDir.DirRec.RecTime.bMinute,
5553 pVolDesc->RootDir.DirRec.RecTime.bSecond,
5554 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
5555 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
5556 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
5557 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
5558 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
5559 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
5560 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
5561 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
5562 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
5563 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
5564 {
5565 Log2(("ISO9660: RootDir System Use:\n%.*RhxD\n",
5566 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
5567 }
5568 }
5569}
5570
5571#endif /* LOG_ENABLED */
5572
5573/**
5574 * Deal with a root directory from a primary or supplemental descriptor.
5575 *
5576 * @returns IPRT status code.
5577 * @param pThis The ISO 9660 instance being initialized.
5578 * @param pRootDir The root directory record to check out.
5579 * @param pDstRootDir Where to store a copy of the root dir record.
5580 * @param pErrInfo Where to return additional error info. Can be NULL.
5581 */
5582static int rtFsIsoVolHandleRootDir(PRTFSISOVOL pThis, PCISO9660DIRREC pRootDir,
5583 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
5584{
5585 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
5586 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
5587 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
5588
5589 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
5590 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5591 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
5592 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
5593 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5594 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
5595
5596 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
5597 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
5598 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
5599 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
5600 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
5601
5602 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
5603 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
5604 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
5605
5606 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
5607 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
5608 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
5609 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
5610 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5611 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
5612 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
5613
5614 /*
5615 * Seems okay, copy it.
5616 */
5617 *pDstRootDir = *pRootDir;
5618 return VINF_SUCCESS;
5619}
5620
5621
5622/**
5623 * Deal with a primary volume descriptor.
5624 *
5625 * @returns IPRT status code.
5626 * @param pThis The ISO 9660 instance being initialized.
5627 * @param pVolDesc The volume descriptor to handle.
5628 * @param offVolDesc The disk offset of the volume descriptor.
5629 * @param pRootDir Where to return a copy of the root directory record.
5630 * @param poffRootDirRec Where to return the disk offset of the root dir.
5631 * @param pErrInfo Where to return additional error info. Can be NULL.
5632 */
5633static int rtFsIsoVolHandlePrimaryVolDesc(PRTFSISOVOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
5634 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
5635{
5636 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
5637 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5638 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
5639
5640 /*
5641 * We need the block size ...
5642 */
5643 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
5644 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
5645 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
5646 || pThis->cbBlock / pThis->cbSector < 1)
5647 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
5648 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
5649 if (pThis->cbBlock / pThis->cbSector > 128)
5650 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
5651
5652 /*
5653 * ... volume space size ...
5654 */
5655 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
5656 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
5657 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
5658 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
5659 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
5660
5661 /*
5662 * ... number of volumes in the set ...
5663 */
5664 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
5665 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
5666 || pThis->cVolumesInSet == 0)
5667 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
5668 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
5669 if (pThis->cVolumesInSet > 32)
5670 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
5671
5672 /*
5673 * ... primary volume sequence ID ...
5674 */
5675 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
5676 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
5677 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
5678 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
5679 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
5680 || pThis->idPrimaryVol < 1)
5681 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5682 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
5683
5684 /*
5685 * ... and the root directory record.
5686 */
5687 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
5688 return rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
5689}
5690
5691
5692/**
5693 * Deal with a supplementary volume descriptor.
5694 *
5695 * @returns IPRT status code.
5696 * @param pThis The ISO 9660 instance being initialized.
5697 * @param pVolDesc The volume descriptor to handle.
5698 * @param offVolDesc The disk offset of the volume descriptor.
5699 * @param pbUcs2Level Where to return the joliet level, if found. Caller
5700 * initializes this to zero, we'll return 1, 2 or 3 if
5701 * joliet was detected.
5702 * @param pRootDir Where to return the root directory, if found.
5703 * @param poffRootDirRec Where to return the disk offset of the root dir.
5704 * @param pErrInfo Where to return additional error info. Can be NULL.
5705 */
5706static int rtFsIsoVolHandleSupplementaryVolDesc(PRTFSISOVOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
5707 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
5708 PRTERRINFO pErrInfo)
5709{
5710 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
5711 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5712 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
5713
5714 /*
5715 * Is this a joliet volume descriptor? If not, we probably don't need to
5716 * care about it.
5717 */
5718 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
5719 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
5720 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
5721 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
5722 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
5723 return VINF_SUCCESS;
5724
5725 /*
5726 * Skip if joliet is unwanted.
5727 */
5728 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
5729 return VINF_SUCCESS;
5730
5731 /*
5732 * Check that the joliet descriptor matches the primary one.
5733 * Note! These are our assumptions and may be wrong.
5734 */
5735 if (pThis->cbBlock == 0)
5736 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5737 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
5738 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
5739 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5740 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
5741 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
5742 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
5743 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5744 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
5745 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
5746 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
5747 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5748 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
5749 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
5750 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
5751 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5752 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
5753 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
5754
5755 if (*pbUcs2Level != 0)
5756 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
5757
5758 /*
5759 * Switch to the joliet root dir as it has UTF-16 stuff in it.
5760 */
5761 int rc = rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
5762 if (RT_SUCCESS(rc))
5763 {
5764 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
5765 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
5766 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
5767 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
5768 }
5769 return rc;
5770}
5771
5772
5773
5774/**
5775 * Worker for RTFsIso9660VolOpen.
5776 *
5777 * @returns IPRT status code.
5778 * @param pThis The ISO VFS instance to initialize.
5779 * @param hVfsSelf The ISO VFS handle (no reference consumed).
5780 * @param hVfsBacking The file backing the alleged FAT file system.
5781 * Reference is consumed (via rtFsIsoVol_Close).
5782 * @param fFlags Flags, RTFSISO9660_F_XXX.
5783 * @param pErrInfo Where to return additional error info. Can be NULL.
5784 */
5785static int rtFsIsoVolTryInit(PRTFSISOVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
5786{
5787 uint32_t const cbSector = 2048;
5788
5789 /*
5790 * First initialize the state so that rtFsIsoVol_Destroy won't trip up.
5791 */
5792 pThis->hVfsSelf = hVfsSelf;
5793 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIsoVol_Destroy releases it. */
5794 pThis->cbBacking = 0;
5795 pThis->cBackingSectors = 0;
5796 pThis->fFlags = fFlags;
5797 pThis->cbSector = cbSector;
5798 pThis->cbBlock = 0;
5799 pThis->cBlocksInPrimaryVolumeSpace = 0;
5800 pThis->cbPrimaryVolumeSpace = 0;
5801 pThis->cVolumesInSet = 0;
5802 pThis->idPrimaryVol = UINT32_MAX;
5803 pThis->fIsUtf16 = false;
5804 pThis->pRootDir = NULL;
5805
5806 /*
5807 * Get stuff that may fail.
5808 */
5809 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
5810 if (RT_SUCCESS(rc))
5811 pThis->cBackingSectors = pThis->cbBacking / pThis->cbSector;
5812 else
5813 return rc;
5814
5815 /*
5816 * Read the volume descriptors starting at logical sector 16.
5817 */
5818 union
5819 {
5820 uint8_t ab[RTFSISO_MAX_LOGICAL_BLOCK_SIZE];
5821 uint16_t au16[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 2];
5822 uint32_t au32[RTFSISO_MAX_LOGICAL_BLOCK_SIZE / 4];
5823 ISO9660VOLDESCHDR VolDescHdr;
5824 ISO9660BOOTRECORD BootRecord;
5825 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
5826 ISO9660SUPVOLDESC SupVolDesc;
5827 ISO9660VOLPARTDESC VolPartDesc;
5828 } Buf;
5829 RT_ZERO(Buf);
5830
5831 uint64_t offRootDirRec = UINT64_MAX;
5832 ISO9660DIRREC RootDir;
5833 RT_ZERO(RootDir);
5834
5835 uint64_t offJolietRootDirRec = UINT64_MAX;
5836 uint8_t bJolietUcs2Level = 0;
5837 ISO9660DIRREC JolietRootDir;
5838 RT_ZERO(JolietRootDir);
5839
5840 uint8_t uUdfLevel = 0;
5841 uint64_t offUdfBootVolDesc = UINT64_MAX;
5842
5843 uint32_t cPrimaryVolDescs = 0;
5844 uint32_t cSupplementaryVolDescs = 0;
5845 uint32_t cBootRecordVolDescs = 0;
5846 uint32_t offVolDesc = 16 * cbSector;
5847 enum
5848 {
5849 kStateStart = 0,
5850 kStateNoSeq,
5851 kStateCdSeq,
5852 kStateUdfSeq
5853 } enmState = kStateStart;
5854 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
5855 {
5856 if (iVolDesc > 32)
5857 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "More than 32 volume descriptors, doesn't seem right...");
5858
5859 /* Read the next one and check the signature. */
5860 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
5861 if (RT_FAILURE(rc))
5862 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
5863
5864#define MATCH_STD_ID(a_achStdId1, a_szStdId2) \
5865 ( (a_achStdId1)[0] == (a_szStdId2)[0] \
5866 && (a_achStdId1)[1] == (a_szStdId2)[1] \
5867 && (a_achStdId1)[2] == (a_szStdId2)[2] \
5868 && (a_achStdId1)[3] == (a_szStdId2)[3] \
5869 && (a_achStdId1)[4] == (a_szStdId2)[4] )
5870#define MATCH_HDR(a_pStd, a_bType2, a_szStdId2, a_bVer2) \
5871 ( MATCH_STD_ID((a_pStd)->achStdId, a_szStdId2) \
5872 && (a_pStd)->bDescType == (a_bType2) \
5873 && (a_pStd)->bDescVersion == (a_bVer2) )
5874
5875 /*
5876 * ISO 9660 ("CD001").
5877 */
5878 if ( ( enmState == kStateStart
5879 || enmState == kStateCdSeq
5880 || enmState == kStateNoSeq)
5881 && MATCH_STD_ID(Buf.VolDescHdr.achStdId, ISO9660VOLDESC_STD_ID) )
5882 {
5883 enmState = kStateCdSeq;
5884
5885 /* Do type specific handling. */
5886 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
5887 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
5888 {
5889 cPrimaryVolDescs++;
5890 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
5891 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5892 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
5893#ifdef LOG_ENABLED
5894 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
5895#endif
5896 if (cPrimaryVolDescs > 1)
5897 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
5898 rc = rtFsIsoVolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
5899 }
5900 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
5901 {
5902 cSupplementaryVolDescs++;
5903 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
5904 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5905 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
5906#ifdef LOG_ENABLED
5907 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
5908#endif
5909 rc = rtFsIsoVolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
5910 &offJolietRootDirRec, pErrInfo);
5911 }
5912 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
5913 {
5914 cBootRecordVolDescs++;
5915 }
5916 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
5917 {
5918 if (!cPrimaryVolDescs)
5919 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
5920 enmState = kStateNoSeq;
5921 }
5922 else
5923 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5924 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
5925 }
5926 /*
5927 * UDF volume recognition sequence (VRS).
5928 */
5929 else if ( ( enmState == kStateNoSeq
5930 || enmState == kStateStart)
5931 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BEGIN, UDF_EXT_VOL_DESC_VERSION) )
5932 {
5933 if (uUdfLevel == 0)
5934 enmState = kStateUdfSeq;
5935 else
5936 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BEA01 sequence is supported");
5937 }
5938 else if ( enmState == kStateUdfSeq
5939 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_02, UDF_EXT_VOL_DESC_VERSION) )
5940 uUdfLevel = 2;
5941 else if ( enmState == kStateUdfSeq
5942 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_03, UDF_EXT_VOL_DESC_VERSION) )
5943 uUdfLevel = 3;
5944 else if ( enmState == kStateUdfSeq
5945 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BOOT, UDF_EXT_VOL_DESC_VERSION) )
5946 {
5947 if (offUdfBootVolDesc == UINT64_MAX)
5948 offUdfBootVolDesc = iVolDesc * cbSector;
5949 else
5950 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BOOT2 descriptor is supported");
5951 }
5952 else if ( enmState == kStateUdfSeq
5953 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_TERM, UDF_EXT_VOL_DESC_VERSION) )
5954 {
5955 if (uUdfLevel != 0)
5956 enmState = kStateNoSeq;
5957 else
5958 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Found BEA01 & TEA01, but no NSR02 or NSR03 descriptors");
5959 }
5960 /*
5961 * Unknown, probably the end.
5962 */
5963 else if (enmState == kStateNoSeq)
5964 break;
5965 else if (enmState == kStateStart)
5966 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5967 "Not ISO? Unable to recognize volume descriptor signature: %.5Rhxs", Buf.VolDescHdr.achStdId);
5968 else if (enmState == kStateCdSeq)
5969 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5970 "Missing ISO 9660 terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
5971 else if (enmState == kStateUdfSeq)
5972 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5973 "Missing UDF terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
5974 else
5975 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5976 "Unknown volume descriptor signature found at sector %u: %.5Rhxs",
5977 16 + iVolDesc, Buf.VolDescHdr.achStdId);
5978 if (RT_FAILURE(rc))
5979 return rc;
5980 }
5981
5982 /*
5983 * If we found a UDF VRS and are interested in UDF, we have more work to do here.
5984 */
5985#if defined(DEBUG_bird) && 0
5986 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF) )
5987 {
5988 Log(("rtFsIsoVolTryInit: uUdfLevel=%d\n", uUdfLevel));
5989 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
5990 if (RT_FAILURE(rc))
5991 return rc;
5992 }
5993
5994 /*
5995 * Decide which to prefer.
5996 *
5997 * By default we pick UDF over any of the two ISO 9960, there is currently
5998 * no way to override this without using the RTFSISO9660_F_NO_XXX options.
5999 *
6000 * If there isn't UDF, we may faced with choosing between joliet and rock
6001 * ridge. The joliet option is generally favorable as we don't have to
6002 * guess wrt to the file name encoding. So, we'll pick that for now.
6003 *
6004 * Note! Should we change this preference for joliet, there fun wrt making sure
6005 * there really is rock ridge stuff in the primary volume as well as
6006 * making sure there really is anything of value in the primary volume.
6007 */
6008 if (uUdfLevel > 0)
6009 {
6010 pThis->enmType = RTFSISOVOLTYPE_UDF;
6011 rc = rtFsIsoDirShrd_NewUdf(pThis, NULL /*pParent*/, &pThis->Udf.VolInfo.RootDirIcb,
6012 NULL /*pFileIdDesc*/, 0 /*offInDir*/, &pThis->pRootDir);
6013 /** @todo fall back on failure? */
6014 return rc;
6015 }
6016#else
6017 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF) && /* Just disable this code for now: */ (fFlags & RT_BIT(24)))
6018 {
6019 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
6020 rc = rtFsIsoDirShrd_NewUdf(pThis, NULL /*pParent*/, &pThis->Udf.VolInfo.RootDirIcb,
6021 NULL /*pFileIdDesc*/, 0 /*offInDir*/, &pThis->pRootDir);
6022 }
6023
6024 /*
6025 * We may be faced with choosing between joliet and rock ridge (we won't
6026 * have this choice when RTFSISO9660_F_NO_JOLIET is set). The joliet
6027 * option is generally favorable as we don't have to guess wrt to the file
6028 * name encoding. So, we'll pick that for now.
6029 *
6030 * Note! Should we change this preference for joliet, there fun wrt making sure
6031 * there really is rock ridge stuff in the primary volume as well as
6032 * making sure there really is anything of value in the primary volume.
6033 */
6034#endif
6035 if (bJolietUcs2Level != 0)
6036 {
6037 pThis->enmType = RTFSISOVOLTYPE_JOLIET;
6038 pThis->fIsUtf16 = true;
6039 return rtFsIsoDirShrd_New9660(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
6040 }
6041 pThis->enmType = RTFSISOVOLTYPE_ISO9960;
6042 return rtFsIsoDirShrd_New9660(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
6043}
6044
6045
6046/**
6047 * Opens an ISO 9660 file system volume.
6048 *
6049 * @returns IPRT status code.
6050 * @param hVfsFileIn The file or device backing the volume.
6051 * @param fFlags RTFSISO9660_F_XXX.
6052 * @param phVfs Where to return the virtual file system handle.
6053 * @param pErrInfo Where to return additional error information.
6054 */
6055RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
6056{
6057 /*
6058 * Quick input validation.
6059 */
6060 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
6061 *phVfs = NIL_RTVFS;
6062 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
6063
6064 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
6065 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
6066
6067 /*
6068 * Create a new FAT VFS instance and try initialize it using the given input file.
6069 */
6070 RTVFS hVfs = NIL_RTVFS;
6071 void *pvThis = NULL;
6072 int rc = RTVfsNew(&g_rtFsIsoVolOps, sizeof(RTFSISOVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
6073 if (RT_SUCCESS(rc))
6074 {
6075 rc = rtFsIsoVolTryInit((PRTFSISOVOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
6076 if (RT_SUCCESS(rc))
6077 *phVfs = hVfs;
6078 else
6079 RTVfsRelease(hVfs);
6080 }
6081 else
6082 RTVfsFileRelease(hVfsFileIn);
6083 return rc;
6084}
6085
6086
6087/**
6088 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
6089 */
6090static DECLCALLBACK(int) rtVfsChainIsoFsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
6091 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
6092{
6093 RT_NOREF(pProviderReg, pSpec);
6094
6095 /*
6096 * Basic checks.
6097 */
6098 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
6099 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
6100 if ( pElement->enmType != RTVFSOBJTYPE_VFS
6101 && pElement->enmType != RTVFSOBJTYPE_DIR)
6102 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
6103 if (pElement->cArgs > 1)
6104 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
6105
6106 /*
6107 * Parse the flag if present, save in pElement->uProvider.
6108 */
6109 uint32_t fFlags = 0;
6110 if (pElement->cArgs > 0)
6111 {
6112 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
6113 {
6114 const char *psz = pElement->paArgs[iArg].psz;
6115 if (*psz)
6116 {
6117 if (!strcmp(psz, "nojoliet"))
6118 fFlags |= RTFSISO9660_F_NO_JOLIET;
6119 else if (!strcmp(psz, "norock"))
6120 fFlags |= RTFSISO9660_F_NO_ROCK;
6121 else if (!strcmp(psz, "noudf"))
6122 fFlags |= RTFSISO9660_F_NO_UDF;
6123 else
6124 {
6125 *poffError = pElement->paArgs[iArg].offSpec;
6126 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
6127 }
6128 }
6129 }
6130 }
6131
6132 pElement->uProvider = fFlags;
6133 return VINF_SUCCESS;
6134}
6135
6136
6137/**
6138 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
6139 */
6140static DECLCALLBACK(int) rtVfsChainIsoFsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
6141 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
6142 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
6143{
6144 RT_NOREF(pProviderReg, pSpec, poffError);
6145
6146 int rc;
6147 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
6148 if (hVfsFileIn != NIL_RTVFSFILE)
6149 {
6150 RTVFS hVfs;
6151 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
6152 RTVfsFileRelease(hVfsFileIn);
6153 if (RT_SUCCESS(rc))
6154 {
6155 *phVfsObj = RTVfsObjFromVfs(hVfs);
6156 RTVfsRelease(hVfs);
6157 if (*phVfsObj != NIL_RTVFSOBJ)
6158 return VINF_SUCCESS;
6159 rc = VERR_VFS_CHAIN_CAST_FAILED;
6160 }
6161 }
6162 else
6163 rc = VERR_VFS_CHAIN_CAST_FAILED;
6164 return rc;
6165}
6166
6167
6168/**
6169 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
6170 */
6171static DECLCALLBACK(bool) rtVfsChainIsoFsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
6172 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
6173 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
6174{
6175 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
6176 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
6177 || !pReuseElement->paArgs[0].uProvider)
6178 return true;
6179 return false;
6180}
6181
6182
6183/** VFS chain element 'file'. */
6184static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
6185{
6186 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
6187 /* fReserved = */ 0,
6188 /* pszName = */ "isofs",
6189 /* ListEntry = */ { NULL, NULL },
6190 /* pszHelp = */ "Open a ISO 9660 or UDF file system, requires a file object on the left side.\n"
6191 "The 'noudf' option make it ignore any UDF.\n"
6192 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
6193 "The 'norock' option make it ignore any rock ridge info.\n",
6194 /* pfnValidate = */ rtVfsChainIsoFsVol_Validate,
6195 /* pfnInstantiate = */ rtVfsChainIsoFsVol_Instantiate,
6196 /* pfnCanReuseElement = */ rtVfsChainIsoFsVol_CanReuseElement,
6197 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
6198};
6199
6200RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
6201
Note: See TracBrowser for help on using the repository browser.

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