VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use