VirtualBox

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

Last change on this file since 100908 was 100908, checked in by vboxsync, 15 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

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

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