VirtualBox

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

Last change on this file since 100594 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 296.8 KB
Line 
1/* $Id: isovfs.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - ISO 9660 and UDF Virtual Filesystem (read only).
4 */
5
6/*
7 * Copyright (C) 2017-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FS
42#include "internal/iprt.h"
43#include <iprt/fsvfs.h>
44
45#include <iprt/alloca.h>
46#include <iprt/asm.h>
47#include <iprt/assert.h>
48#include <iprt/err.h>
49#include <iprt/crc.h>
50#include <iprt/critsect.h>
51#include <iprt/ctype.h>
52#include <iprt/file.h>
53#include <iprt/log.h>
54#include <iprt/mem.h>
55#include <iprt/poll.h>
56#include <iprt/string.h>
57#include <iprt/thread.h>
58#include <iprt/vfs.h>
59#include <iprt/vfslowlevel.h>
60#include <iprt/uni.h>
61#include <iprt/utf16.h>
62#include <iprt/formats/iso9660.h>
63#include <iprt/formats/udf.h>
64
65
66/*********************************************************************************************************************************
67* Defined Constants And Macros *
68*********************************************************************************************************************************/
69/** The maximum logical block size. */
70#define RTFSISO_MAX_LOGICAL_BLOCK_SIZE _16K
71/** Max directory size. */
72#if ARCH_BITS == 32
73# define RTFSISO_MAX_DIR_SIZE _32M
74#else
75# define RTFSISO_MAX_DIR_SIZE _64M
76#endif
77
78/** Check if an entity ID field equals the given ID string. */
79#define UDF_ENTITY_ID_EQUALS(a_pEntityId, a_szId) \
80 ( memcmp(&(a_pEntityId)->achIdentifier[0], a_szId, RT_MIN(sizeof(a_szId), sizeof(a_pEntityId)->achIdentifier)) == 0 )
81/** Checks if a character set indicator indicates OSTA compressed unicode. */
82#define UDF_IS_CHAR_SET_OSTA(a_pCharSet) \
83 ( (a_pCharSet)->uType == UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE \
84 && memcmp((a_pCharSet)->abInfo, UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO, \
85 sizeof(UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO)) == 0 )
86
87
88/** @name UDF structure logging macros
89 * @{ */
90#define UDF_LOG2_MEMBER(a_pStruct, a_szFmt, a_Member) \
91 Log2(("ISO/UDF: %-32s %" a_szFmt "\n", #a_Member ":", (a_pStruct)->a_Member))
92#define UDF_LOG2_MEMBER_EX(a_pStruct, a_szFmt, a_Member, a_cchIndent) \
93 Log2(("ISO/UDF: %*s%-32s %" a_szFmt "\n", a_cchIndent, "", #a_Member ":", (a_pStruct)->a_Member))
94#define UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, a_cchIndent) \
95 Log2(("ISO/UDF: %*s%-32s '%.23s' fFlags=%#06x Suffix=%.8Rhxs\n", a_cchIndent, "", #a_Member ":", \
96 (a_pStruct)->a_Member.achIdentifier, (a_pStruct)->a_Member.fFlags, &(a_pStruct)->a_Member.Suffix))
97#define UDF_LOG2_MEMBER_ENTITY_ID(a_pStruct, a_Member) UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, 0)
98#define UDF_LOG2_MEMBER_EXTENTAD(a_pStruct, a_Member) \
99 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb))
100#define UDF_LOG2_MEMBER_SHORTAD(a_pStruct, a_Member) \
101 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32 %s\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb, \
102 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
103 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
104 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next" ))
105#define UDF_LOG2_MEMBER_LONGAD(a_pStruct, a_Member) \
106 Log2(("ISO/UDF: %-32s partition %#RX16, block %#010RX32 LB %#010RX32 %s idUnique=%#010RX32 fFlags=%#RX16\n", #a_Member ":", \
107 (a_pStruct)->a_Member.Location.uPartitionNo, (a_pStruct)->a_Member.Location.off, (a_pStruct)->a_Member.cb, \
108 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
109 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
110 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next", \
111 (a_pStruct)->a_Member.ImplementationUse.Fid.idUnique, (a_pStruct)->a_Member.ImplementationUse.Fid.fFlags ))
112#define UDF_LOG2_MEMBER_LBADDR(a_pStruct, a_Member) \
113 Log2(("ISO/UDF: %-32s block %#010RX32 in partition %#06RX16\n", #a_Member ":", \
114 (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.uPartitionNo))
115
116#define UDF_LOG2_MEMBER_TIMESTAMP(a_pStruct, a_Member) \
117 Log2(("ISO/UDF: %-32s %04d-%02u-%02u %02u:%02u:%02u.%02u%02u%02u offUtc=%d type=%#x\n", #a_Member ":", \
118 (a_pStruct)->a_Member.iYear, (a_pStruct)->a_Member.uMonth, (a_pStruct)->a_Member.uDay, \
119 (a_pStruct)->a_Member.uHour, (a_pStruct)->a_Member.uMinute, (a_pStruct)->a_Member.uSecond, \
120 (a_pStruct)->a_Member.cCentiseconds, (a_pStruct)->a_Member.cHundredsOfMicroseconds, \
121 (a_pStruct)->a_Member.cMicroseconds, (a_pStruct)->a_Member.offUtcInMin, (a_pStruct)->a_Member.fType ))
122#define UDF_LOG2_MEMBER_CHARSPEC(a_pStruct, a_Member) \
123 do { \
124 if ( (a_pStruct)->a_Member.uType == UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE \
125 && memcmp(&(a_pStruct)->a_Member.abInfo[0], UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO, \
126 sizeof(UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO)) == 0) \
127 Log2(("ISO/UDF: %-32s OSTA COMPRESSED UNICODE INFO\n", #a_Member ":")); \
128 else if (ASMMemIsZero(&(a_pStruct)->a_Member, sizeof((a_pStruct)->a_Member))) \
129 Log2(("ISO/UDF: %-32s all zeros\n", #a_Member ":")); \
130 else \
131 Log2(("ISO/UDF: %-32s %#x info: %.63Rhxs\n", #a_Member ":", \
132 (a_pStruct)->a_Member.uType, (a_pStruct)->a_Member.abInfo)); \
133 } while (0)
134#define UDF_LOG2_MEMBER_DSTRING(a_pStruct, a_Member) \
135 do { \
136 if ((a_pStruct)->a_Member[0] == 8) \
137 Log2(("ISO/UDF: %-32s 8: '%s' len=%u (actual=%u)\n", #a_Member ":", &(a_pStruct)->a_Member[1], \
138 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], \
139 RTStrNLen(&(a_pStruct)->a_Member[1], sizeof((a_pStruct)->a_Member) - 2) + 1 )); \
140 else if ((a_pStruct)->a_Member[0] == 16) \
141 { \
142 PCRTUTF16 pwszTmp = (PCRTUTF16)&(a_pStruct)->a_Member[1]; \
143 char *pszTmp = NULL; \
144 RTUtf16BigToUtf8Ex(pwszTmp, (sizeof((a_pStruct)->a_Member) - 2) / sizeof(RTUTF16), &pszTmp, 0, NULL); \
145 Log2(("ISO/UDF: %-32s 16: '%s' len=%u (actual=%u)\n", #a_Member ":", pszTmp, \
146 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], \
147 RTUtf16NLen(pwszTmp, (sizeof((a_pStruct)->a_Member) - 2) / sizeof(RTUTF16)) * sizeof(RTUTF16) + 1 /*??*/ )); \
148 RTStrFree(pszTmp); \
149 } \
150 else if (ASMMemIsZero(&(a_pStruct)->a_Member[0], sizeof((a_pStruct)->a_Member))) \
151 Log2(("ISO/UDF: %-32s empty\n", #a_Member ":")); \
152 else \
153 Log2(("ISO/UDF: %-32s bad: %.*Rhxs\n", #a_Member ":", sizeof((a_pStruct)->a_Member), &(a_pStruct)->a_Member[0] )); \
154 } while (0)
155/** @} */
156
157/** Compresses SUSP and rock ridge extension signatures in the hope of
158 * reducing switch table size. */
159#define SUSP_MAKE_SIG(a_bSig1, a_bSig2) \
160 ( ((uint16_t)(a_bSig1) & 0x1f) \
161 | (((uint16_t)(a_bSig2) ^ 0x40) << 5) \
162 | ((((uint16_t)(a_bSig1) ^ 0x40) & 0xe0) << 8) )
163
164
165/*********************************************************************************************************************************
166* Structures and Typedefs *
167*********************************************************************************************************************************/
168/** Pointer to an ISO volume (VFS instance data). */
169typedef struct RTFSISOVOL *PRTFSISOVOL;
170/** Pointer to a const ISO volume (VFS instance data). */
171typedef struct RTFSISOVOL const *PCRTFSISOVOL;
172
173/** Pointer to a ISO directory instance. */
174typedef struct RTFSISODIRSHRD *PRTFSISODIRSHRD;
175
176
177/**
178 * Output structure for rock ridge directory entry parsing.
179 */
180typedef struct RTFSISOROCKINFO
181{
182 /** Set if the parse info is valid. */
183 bool fValid;
184 /** Set if we've see the SP entry. */
185 bool fSuspSeenSP : 1;
186 /** Set if we've seen the last 'NM' entry. */
187 bool fSeenLastNM : 1;
188 /** Set if we've seen the last 'SL' entry. */
189 bool fSeenLastSL : 1;
190 /** Symbolic link target overflowed. */
191 bool fOverflowSL : 1;
192 /** Number of interesting rock ridge entries we've scanned. */
193 uint16_t cRockEntries;
194 /** The name length. */
195 uint16_t cchName;
196 /** The Symbolic link target name length. */
197 uint16_t cchLinkTarget;
198 /** Object info. */
199 RTFSOBJINFO Info;
200 /** The rock ridge name. */
201 char szName[2048];
202 /** Symbolic link target name. */
203 char szLinkTarget[2048];
204} RTFSISOROCKINFO;
205/** Rock ridge info for a directory entry. */
206typedef RTFSISOROCKINFO *PRTFSISOROCKINFO;
207/** Const rock ridge info for a directory entry. */
208typedef RTFSISOROCKINFO const *PCRTFSISOROCKINFO;
209
210/**
211 * Rock ridge name compare data.
212 */
213typedef struct RTFSISOROCKNAMECOMP
214{
215 /** Pointer to the name we're looking up. */
216 const char *pszEntry;
217 /** The length of the name. */
218 size_t cchEntry;
219 /** The length of the name that we've matched so far (in case of multiple NM
220 * entries). */
221 size_t offMatched;
222} RTFSISOROCKNAMECOMP;
223/** Ponter to rock ridge name compare data. */
224typedef RTFSISOROCKNAMECOMP *PRTFSISOROCKNAMECOMP;
225
226
227/**
228 * ISO extent (internal to the VFS not a disk structure).
229 */
230typedef struct RTFSISOEXTENT
231{
232 /** The disk or partition byte offset.
233 * This is set to UINT64_MAX for parts of sparse files that aren't recorded.*/
234 uint64_t off;
235 /** The size of the extent in bytes. */
236 uint64_t cbExtent;
237 /** UDF virtual partition number, UINT32_MAX for ISO 9660. */
238 uint32_t idxPart;
239 /** Reserved. */
240 uint32_t uReserved;
241} RTFSISOEXTENT;
242/** Pointer to an ISO 9660 extent. */
243typedef RTFSISOEXTENT *PRTFSISOEXTENT;
244/** Pointer to a const ISO 9660 extent. */
245typedef RTFSISOEXTENT const *PCRTFSISOEXTENT;
246
247
248/**
249 * ISO file system object, shared part.
250 */
251typedef struct RTFSISOCORE
252{
253 /** The parent directory keeps a list of open objects (RTFSISOCORE). */
254 RTLISTNODE Entry;
255 /** Reference counter. */
256 uint32_t volatile cRefs;
257 /** The parent directory (not released till all children are close). */
258 PRTFSISODIRSHRD pParentDir;
259 /** The byte offset of the first directory record.
260 * This is used when looking up objects in a directory to avoid creating
261 * duplicate instances. */
262 uint64_t offDirRec;
263 /** Attributes. */
264 RTFMODE fAttrib;
265 /** Set if there is rock ridge info for this directory entry. */
266 bool fHaveRockInfo;
267 /** The object size. */
268 uint64_t cbObject;
269 /** The access time. */
270 RTTIMESPEC AccessTime;
271 /** The modificaton time. */
272 RTTIMESPEC ModificationTime;
273 /** The change time. */
274 RTTIMESPEC ChangeTime;
275 /** The birth time. */
276 RTTIMESPEC BirthTime;
277 /** The i-node ID. */
278 RTINODE idINode;
279 /** Pointer to the volume. */
280 PRTFSISOVOL pVol;
281 /** The version number. */
282 uint32_t uVersion;
283 /** Number of extents. */
284 uint32_t cExtents;
285 /** The first extent. */
286 RTFSISOEXTENT FirstExtent;
287 /** Array of additional extents. */
288 PRTFSISOEXTENT paExtents;
289} RTFSISOCORE;
290typedef RTFSISOCORE *PRTFSISOCORE;
291
292/**
293 * ISO file, shared data.
294 */
295typedef struct RTFSISOFILESHRD
296{
297 /** Core ISO9660 object info. */
298 RTFSISOCORE Core;
299} RTFSISOFILESHRD;
300/** Pointer to a ISO 9660 file object. */
301typedef RTFSISOFILESHRD *PRTFSISOFILESHRD;
302
303
304/**
305 * ISO directory, shared data.
306 *
307 * We will always read in the whole directory just to keep things really simple.
308 */
309typedef struct RTFSISODIRSHRD
310{
311 /** Core ISO 9660 object info. */
312 RTFSISOCORE Core;
313 /** Open child objects (RTFSISOCORE). */
314 RTLISTNODE OpenChildren;
315
316 /** Pointer to the directory content. */
317 uint8_t *pbDir;
318 /** The size of the directory content (duplicate of Core.cbObject). */
319 uint32_t cbDir;
320} RTFSISODIRSHRD;
321/** Pointer to a ISO directory instance. */
322typedef RTFSISODIRSHRD *PRTFSISODIRSHRD;
323
324
325/**
326 * Private data for a VFS file object.
327 */
328typedef struct RTFSISOFILEOBJ
329{
330 /** Pointer to the shared data. */
331 PRTFSISOFILESHRD pShared;
332 /** The current file offset. */
333 uint64_t offFile;
334} RTFSISOFILEOBJ;
335typedef RTFSISOFILEOBJ *PRTFSISOFILEOBJ;
336
337/**
338 * Private data for a VFS directory object.
339 */
340typedef struct RTFSISODIROBJ
341{
342 /** Pointer to the shared data. */
343 PRTFSISODIRSHRD pShared;
344 /** The current directory offset. */
345 uint32_t offDir;
346} RTFSISODIROBJ;
347typedef RTFSISODIROBJ *PRTFSISODIROBJ;
348
349/** Pointer to info about a UDF volume. */
350typedef struct RTFSISOUDFVOLINFO *PRTFSISOUDFVOLINFO;
351
352
353/** @name RTFSISO_UDF_PMAP_T_XXX
354 * @{ */
355#define RTFSISO_UDF_PMAP_T_PLAIN 1
356#define RTFSISO_UDF_PMAP_T_VPM_15 2
357#define RTFSISO_UDF_PMAP_T_VPM_20 3
358#define RTFSISO_UDF_PMAP_T_SPM 4
359#define RTFSISO_UDF_PMAP_T_MPM 5
360/** @} */
361
362/**
363 * Information about a logical UDF partition.
364 *
365 * This combins information from the partition descriptor, the UDFPARTMAPTYPE1
366 * and the UDFPARTMAPTYPE2 structure.
367 */
368typedef struct RTFSISOVOLUDFPMAP
369{
370 /** Partition starting location as a byte offset. */
371 uint64_t offByteLocation;
372 /** Partition starting location (logical sector number). */
373 uint32_t offLocation;
374 /** Number of sectors. */
375 uint32_t cSectors;
376
377 /** Partition descriptor index (for processing). */
378 uint16_t idxPartDesc;
379 /** Offset info the map table. */
380 uint16_t offMapTable;
381 /** Partition number (not index). */
382 uint16_t uPartitionNo;
383 /** Partition number (not index). */
384 uint16_t uVolumeSeqNo;
385
386 /** The access type (UDF_PART_ACCESS_TYPE_XXX). */
387 uint32_t uAccessType;
388 /** Partition flags (UDF_PARTITION_FLAGS_XXX). */
389 uint16_t fFlags;
390 /** RTFSISO_UDF_PMAP_T_XXX. */
391 uint8_t bType;
392 /** Set if Hdr is valid. */
393 bool fHaveHdr;
394 /** Copy of UDFPARTITIONDESC::ContentsUse::Hdr. */
395 UDFPARTITIONHDRDESC Hdr;
396
397} RTFSISOVOLUDFPMAP;
398typedef RTFSISOVOLUDFPMAP *PRTFSISOVOLUDFPMAP;
399
400/**
401 * Information about a UDF volume (/ volume set).
402 *
403 * This combines information from the primary and logical descriptors.
404 *
405 * @note There is only one volume per volume set in the current UDF
406 * implementation. So, this can be considered a volume and a volume set.
407 */
408typedef struct RTFSISOUDFVOLINFO
409{
410 /** The extent containing the file set descriptor. */
411 UDFLONGAD FileSetDescriptor;
412
413 /** The root directory location (from the file set descriptor). */
414 UDFLONGAD RootDirIcb;
415 /** Location of the system stream directory associated with the file set. */
416 UDFLONGAD SystemStreamDirIcb;
417
418 /** The logical block size on this volume. */
419 uint32_t cbBlock;
420 /** The log2 of cbBlock. */
421 uint32_t cShiftBlock;
422 /** Flags (UDF_PVD_FLAGS_XXX). */
423 uint16_t fFlags;
424
425 /** Number of partitions mapp in this volume. */
426 uint16_t cPartitions;
427 /** Partitions in this volume. */
428 PRTFSISOVOLUDFPMAP paPartitions;
429
430 /** The volume ID string. */
431 UDFDSTRING achLogicalVolumeID[128];
432} RTFSISOUDFVOLINFO;
433
434
435/**
436 * Indicates which of the possible content types we're accessing.
437 */
438typedef enum RTFSISOVOLTYPE
439{
440 /** Invalid zero value. */
441 RTFSISOVOLTYPE_INVALID = 0,
442 /** Accessing the primary ISO-9660 volume. */
443 RTFSISOVOLTYPE_ISO9960,
444 /** Accessing the joliet volume (secondary ISO-9660). */
445 RTFSISOVOLTYPE_JOLIET,
446 /** Accessing the UDF volume. */
447 RTFSISOVOLTYPE_UDF
448} RTFSISOVOLTYPE;
449
450/**
451 * A ISO volume.
452 */
453typedef struct RTFSISOVOL
454{
455 /** Handle to itself. */
456 RTVFS hVfsSelf;
457 /** The file, partition, or whatever backing the ISO 9660 volume. */
458 RTVFSFILE hVfsBacking;
459 /** The size of the backing thingy. */
460 uint64_t cbBacking;
461 /** The size of the backing thingy in sectors (cbSector). */
462 uint64_t cBackingSectors;
463 /** Flags. */
464 uint32_t fFlags;
465 /** The sector size (in bytes). */
466 uint32_t cbSector;
467 /** What we're accessing. */
468 RTFSISOVOLTYPE enmType;
469
470 /** @name ISO 9660 specific data
471 * @{ */
472 /** The size of a logical block in bytes. */
473 uint32_t cbBlock;
474 /** The primary volume space size in blocks. */
475 uint32_t cBlocksInPrimaryVolumeSpace;
476 /** The primary volume space size in bytes. */
477 uint64_t cbPrimaryVolumeSpace;
478 /** The number of volumes in the set. */
479 uint32_t cVolumesInSet;
480 /** The primary volume sequence ID. */
481 uint32_t idPrimaryVol;
482 /** The offset of the primary volume descriptor. */
483 uint32_t offPrimaryVolDesc;
484 /** The offset of the secondary volume descriptor. */
485 uint32_t offSecondaryVolDesc;
486 /** Set if using UTF16-2 (joliet). */
487 bool fIsUtf16;
488 /** @} */
489
490 /** UDF specific data. */
491 struct
492 {
493 /** Volume information. */
494 RTFSISOUDFVOLINFO VolInfo;
495 /** The UDF level. */
496 uint8_t uLevel;
497 } Udf;
498
499 /** The root directory shared data. */
500 PRTFSISODIRSHRD pRootDir;
501
502 /** @name Rock Ridge stuff
503 * @{ */
504 /** Set if we've found rock ridge stuff in the root dir. */
505 bool fHaveRock;
506 /** The SUSP skip into system area offset. */
507 uint32_t offSuspSkip;
508 /** The source file byte offset of the abRockBuf content. */
509 uint64_t offRockBuf;
510 /** A buffer for reading rock ridge continuation blocks into. */
511 uint8_t abRockBuf[ISO9660_SECTOR_SIZE];
512 /** Critical section protecting abRockBuf and offRockBuf. */
513 RTCRITSECT RockBufLock;
514 /** @} */
515} RTFSISOVOL;
516
517
518/**
519 * Info gathered from a VDS sequence.
520 */
521typedef struct RTFSISOVDSINFO
522{
523 /** Number of entries in apPrimaryVols. */
524 uint32_t cPrimaryVols;
525 /** Number of entries in apLogicalVols. */
526 uint32_t cLogicalVols;
527 /** Number of entries in apPartitions. */
528 uint32_t cPartitions;
529 /** Pointer to primary volume descriptors (native endian). */
530 PUDFPRIMARYVOLUMEDESC apPrimaryVols[8];
531 /** Pointer to logical volume descriptors (native endian). */
532 PUDFLOGICALVOLUMEDESC apLogicalVols[8];
533 /** Pointer to partition descriptors (native endian). */
534 PUDFPARTITIONDESC apPartitions[16];
535
536 /** Created after scanning the sequence (here for cleanup purposes). */
537 PRTFSISOVOLUDFPMAP paPartMaps;
538} RTFSISOVDSINFO;
539/** Pointer to VDS sequence info. */
540typedef RTFSISOVDSINFO *PRTFSISOVDSINFO;
541
542
543/*********************************************************************************************************************************
544* Internal Functions *
545*********************************************************************************************************************************/
546static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
547static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
548static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir);
549static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
550 uint32_t cDirRecs, uint64_t offDirRec, PCRTFSISOROCKINFO pRockInfo, PRTVFSDIR phVfsDir);
551static int rtFsIsoDir_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid, PRTVFSDIR phVfsDir);
552static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec);
553
554static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo);
555static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo);
556static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo);
557
558
559/**
560 * UDF virtual partition read function.
561 *
562 * This deals with all the fun related to block mapping and such.
563 *
564 * @returns VBox status code.
565 * @param pThis The instance.
566 * @param idxPart The virtual partition number.
567 * @param idxBlock The block number.
568 * @param offByteAddend The byte offset relative to the block.
569 * @param pvBuf The output buffer.
570 * @param cbToRead The number of bytes to read.
571 */
572static int rtFsIsoVolUdfVpRead(PRTFSISOVOL pThis, uint32_t idxPart, uint32_t idxBlock, uint64_t offByteAddend,
573 void *pvBuf, size_t cbToRead)
574{
575 uint64_t const offByte = ((uint64_t)idxBlock << pThis->Udf.VolInfo.cShiftBlock) + offByteAddend;
576
577 int rc;
578 if (idxPart < pThis->Udf.VolInfo.cPartitions)
579 {
580 PRTFSISOVOLUDFPMAP pPart = &pThis->Udf.VolInfo.paPartitions[idxPart];
581 switch (pPart->bType)
582 {
583 case RTFSISO_UDF_PMAP_T_PLAIN:
584 rc = RTVfsFileReadAt(pThis->hVfsBacking, offByte + pPart->offByteLocation, pvBuf, cbToRead, NULL);
585 if (RT_SUCCESS(rc))
586 {
587 Log3(("ISO/UDF: Read %#x bytes at %#RX64 (%#x:%#RX64)\n",
588 cbToRead, offByte + pPart->offByteLocation, idxPart, offByte));
589 return VINF_SUCCESS;
590 }
591 Log(("ISO/UDF: Error reading %#x bytes at %#RX64 (%#x:%#RX64): %Rrc\n",
592 cbToRead, offByte + pPart->offByteLocation, idxPart, offByte, rc));
593 break;
594
595 default:
596 AssertFailed();
597 rc = VERR_ISOFS_IPE_1;
598 break;
599 }
600 }
601 else
602 {
603 Log(("ISO/UDF: Invalid partition index %#x (offset %#RX64), max partitions %#x\n",
604 idxPart, offByte, pThis->Udf.VolInfo.cPartitions));
605 rc = VERR_ISOFS_INVALID_PARTITION_INDEX;
606 }
607 return rc;
608}
609
610
611/**
612 * Returns the length of the version suffix in the given name.
613 *
614 * @returns Number of UTF16-BE chars in the version suffix.
615 * @param pawcName The name to examine.
616 * @param cwcName The length of the name.
617 * @param puValue Where to return the value.
618 */
619static size_t rtFsIso9660GetVersionLengthUtf16Big(PCRTUTF16 pawcName, size_t cwcName, uint32_t *puValue)
620{
621 *puValue = 0;
622
623 /* -1: */
624 if (cwcName <= 2)
625 return 0;
626 RTUTF16 wc1 = RT_BE2H_U16(pawcName[cwcName - 1]);
627 if (!RT_C_IS_DIGIT(wc1))
628 return 0;
629 Assert(wc1 < 0x3a); /* ASSUMES the RT_C_IS_DIGIT macro works just fine on wide chars too. */
630
631 /* -2: */
632 RTUTF16 wc2 = RT_BE2H_U16(pawcName[cwcName - 2]);
633 if (wc2 == ';')
634 {
635 *puValue = wc1 - '0';
636 return 2;
637 }
638 if (!RT_C_IS_DIGIT(wc2) || cwcName <= 3)
639 return 0;
640
641 /* -3: */
642 RTUTF16 wc3 = RT_BE2H_U16(pawcName[cwcName - 3]);
643 if (wc3 == ';')
644 {
645 *puValue = (wc1 - '0')
646 + (wc2 - '0') * 10;
647 return 3;
648 }
649 if (!RT_C_IS_DIGIT(wc3) || cwcName <= 4)
650 return 0;
651
652 /* -4: */
653 RTUTF16 wc4 = RT_BE2H_U16(pawcName[cwcName - 4]);
654 if (wc4 == ';')
655 {
656 *puValue = (wc1 - '0')
657 + (wc2 - '0') * 10
658 + (wc3 - '0') * 100;
659 return 4;
660 }
661 if (!RT_C_IS_DIGIT(wc4) || cwcName <= 5)
662 return 0;
663
664 /* -5: */
665 RTUTF16 wc5 = RT_BE2H_U16(pawcName[cwcName - 5]);
666 if (wc5 == ';')
667 {
668 *puValue = (wc1 - '0')
669 + (wc2 - '0') * 10
670 + (wc3 - '0') * 100
671 + (wc4 - '0') * 1000;
672 return 5;
673 }
674 if (!RT_C_IS_DIGIT(wc5) || cwcName <= 6)
675 return 0;
676
677 /* -6: */
678 RTUTF16 wc6 = RT_BE2H_U16(pawcName[cwcName - 6]);
679 if (wc6 == ';')
680 {
681 *puValue = (wc1 - '0')
682 + (wc2 - '0') * 10
683 + (wc3 - '0') * 100
684 + (wc4 - '0') * 1000
685 + (wc5 - '0') * 10000;
686 return 6;
687 }
688 return 0;
689}
690
691
692/**
693 * Returns the length of the version suffix in the given name.
694 *
695 * @returns Number of chars in the version suffix.
696 * @param pachName The name to examine.
697 * @param cchName The length of the name.
698 * @param puValue Where to return the value.
699 */
700static size_t rtFsIso9660GetVersionLengthAscii(const char *pachName, size_t cchName, uint32_t *puValue)
701{
702 *puValue = 0;
703
704 /* -1: */
705 if (cchName <= 2)
706 return 0;
707 char ch1 = pachName[cchName - 1];
708 if (!RT_C_IS_DIGIT(ch1))
709 return 0;
710
711 /* -2: */
712 char ch2 = pachName[cchName - 2];
713 if (ch2 == ';')
714 {
715 *puValue = ch1 - '0';
716 return 2;
717 }
718 if (!RT_C_IS_DIGIT(ch2) || cchName <= 3)
719 return 0;
720
721 /* -3: */
722 char ch3 = pachName[cchName - 3];
723 if (ch3 == ';')
724 {
725 *puValue = (ch1 - '0')
726 + (ch2 - '0') * 10;
727 return 3;
728 }
729 if (!RT_C_IS_DIGIT(ch3) || cchName <= 4)
730 return 0;
731
732 /* -4: */
733 char ch4 = pachName[cchName - 4];
734 if (ch4 == ';')
735 {
736 *puValue = (ch1 - '0')
737 + (ch2 - '0') * 10
738 + (ch3 - '0') * 100;
739 return 4;
740 }
741 if (!RT_C_IS_DIGIT(ch4) || cchName <= 5)
742 return 0;
743
744 /* -5: */
745 char ch5 = pachName[cchName - 5];
746 if (ch5 == ';')
747 {
748 *puValue = (ch1 - '0')
749 + (ch2 - '0') * 10
750 + (ch3 - '0') * 100
751 + (ch4 - '0') * 1000;
752 return 5;
753 }
754 if (!RT_C_IS_DIGIT(ch5) || cchName <= 6)
755 return 0;
756
757 /* -6: */
758 if (pachName[cchName - 6] == ';')
759 {
760 *puValue = (ch1 - '0')
761 + (ch2 - '0') * 10
762 + (ch3 - '0') * 100
763 + (ch4 - '0') * 1000
764 + (ch5 - '0') * 10000;
765 return 6;
766 }
767 return 0;
768}
769
770
771/**
772 * Converts an ISO 9660 binary timestamp into an IPRT timesspec.
773 *
774 * @param pTimeSpec Where to return the IRPT time.
775 * @param pIso9660 The ISO 9660 binary timestamp.
776 */
777static void rtFsIso9660DateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
778{
779 RTTIME Time;
780 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
781 Time.offUTC = 0;
782 Time.i32Year = pIso9660->bYear + 1900;
783 Time.u8Month = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
784 Time.u8MonthDay = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
785 Time.u8WeekDay = UINT8_MAX;
786 Time.u16YearDay = 0;
787 Time.u8Hour = RT_MIN(pIso9660->bHour, 23);
788 Time.u8Minute = RT_MIN(pIso9660->bMinute, 59);
789 Time.u8Second = RT_MIN(pIso9660->bSecond, 59);
790 Time.u32Nanosecond = 0;
791 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
792
793 /* Only apply the UTC offset if it's within reasons. */
794 if (RT_ABS(pIso9660->offUtc) <= 13*4)
795 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
796}
797
798
799/**
800 * Converts a ISO 9660 char timestamp into an IPRT timesspec.
801 *
802 * @returns true if valid, false if not.
803 * @param pTimeSpec Where to return the IRPT time.
804 * @param pIso9660 The ISO 9660 char timestamp.
805 */
806static bool rtFsIso9660DateTime2TimeSpecIfValid(PRTTIMESPEC pTimeSpec, PCISO9660TIMESTAMP pIso9660)
807{
808 if ( RT_C_IS_DIGIT(pIso9660->achYear[0])
809 && RT_C_IS_DIGIT(pIso9660->achYear[1])
810 && RT_C_IS_DIGIT(pIso9660->achYear[2])
811 && RT_C_IS_DIGIT(pIso9660->achYear[3])
812 && RT_C_IS_DIGIT(pIso9660->achMonth[0])
813 && RT_C_IS_DIGIT(pIso9660->achMonth[1])
814 && RT_C_IS_DIGIT(pIso9660->achDay[0])
815 && RT_C_IS_DIGIT(pIso9660->achDay[1])
816 && RT_C_IS_DIGIT(pIso9660->achHour[0])
817 && RT_C_IS_DIGIT(pIso9660->achHour[1])
818 && RT_C_IS_DIGIT(pIso9660->achMinute[0])
819 && RT_C_IS_DIGIT(pIso9660->achMinute[1])
820 && RT_C_IS_DIGIT(pIso9660->achSecond[0])
821 && RT_C_IS_DIGIT(pIso9660->achSecond[1])
822 && RT_C_IS_DIGIT(pIso9660->achCentisecond[0])
823 && RT_C_IS_DIGIT(pIso9660->achCentisecond[1]))
824 {
825
826 RTTIME Time;
827 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
828 Time.offUTC = 0;
829 Time.i32Year = (pIso9660->achYear[0] - '0') * 1000
830 + (pIso9660->achYear[1] - '0') * 100
831 + (pIso9660->achYear[2] - '0') * 10
832 + (pIso9660->achYear[3] - '0');
833 Time.u8Month = (pIso9660->achMonth[0] - '0') * 10
834 + (pIso9660->achMonth[1] - '0');
835 Time.u8MonthDay = (pIso9660->achDay[0] - '0') * 10
836 + (pIso9660->achDay[1] - '0');
837 Time.u8WeekDay = UINT8_MAX;
838 Time.u16YearDay = 0;
839 Time.u8Hour = (pIso9660->achHour[0] - '0') * 10
840 + (pIso9660->achHour[1] - '0');
841 Time.u8Minute = (pIso9660->achMinute[0] - '0') * 10
842 + (pIso9660->achMinute[1] - '0');
843 Time.u8Second = (pIso9660->achSecond[0] - '0') * 10
844 + (pIso9660->achSecond[1] - '0');
845 Time.u32Nanosecond = (pIso9660->achCentisecond[0] - '0') * 10
846 + (pIso9660->achCentisecond[1] - '0');
847 if ( Time.u8Month > 1 && Time.u8Month <= 12
848 && Time.u8MonthDay > 1 && Time.u8MonthDay <= 31
849 && Time.u8Hour < 60
850 && Time.u8Minute < 60
851 && Time.u8Second < 60
852 && Time.u32Nanosecond < 100)
853 {
854 if (Time.i32Year <= 1677)
855 Time.i32Year = 1677;
856 else if (Time.i32Year <= 2261)
857 Time.i32Year = 2261;
858
859 Time.u32Nanosecond *= RT_NS_10MS;
860 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
861
862 /* Only apply the UTC offset if it's within reasons. */
863 if (RT_ABS(pIso9660->offUtc) <= 13*4)
864 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
865 return true;
866 }
867 }
868 return false;
869}
870
871
872/**
873 * Converts an UDF timestamp into an IPRT timesspec.
874 *
875 * @param pTimeSpec Where to return the IRPT time.
876 * @param pUdf The UDF timestamp.
877 */
878static void rtFsIsoUdfTimestamp2TimeSpec(PRTTIMESPEC pTimeSpec, PCUDFTIMESTAMP pUdf)
879{
880 /* Check the year range before we try convert anything as it's quite possible
881 that this is zero. */
882 if ( pUdf->iYear > 1678
883 && pUdf->iYear < 2262)
884 {
885 RTTIME Time;
886 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
887 Time.offUTC = 0;
888 Time.i32Year = pUdf->iYear;
889 Time.u8Month = RT_MIN(RT_MAX(pUdf->uMonth, 1), 12);
890 Time.u8MonthDay = RT_MIN(RT_MAX(pUdf->uDay, 1), 31);
891 Time.u8WeekDay = UINT8_MAX;
892 Time.u16YearDay = 0;
893 Time.u8Hour = RT_MIN(pUdf->uHour, 23);
894 Time.u8Minute = RT_MIN(pUdf->uMinute, 59);
895 Time.u8Second = RT_MIN(pUdf->uSecond, 59);
896 Time.u32Nanosecond = pUdf->cCentiseconds * UINT32_C(10000000)
897 + pUdf->cHundredsOfMicroseconds * UINT32_C(100000)
898 + pUdf->cMicroseconds * UINT32_C(1000);
899 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
900
901 /* Only apply the UTC offset if it's within reasons. */
902 if (RT_ABS(pUdf->offUtcInMin) <= 13*60)
903 RTTimeSpecSubSeconds(pTimeSpec, pUdf->offUtcInMin * 60);
904 }
905 else
906 RTTimeSpecSetNano(pTimeSpec, 0);
907}
908
909
910/**
911 * Initialization of a RTFSISOCORE structure from a directory record.
912 *
913 * @note The RTFSISOCORE::pParentDir and RTFSISOCORE::Clusters members are
914 * properly initialized elsewhere.
915 *
916 * @returns IRPT status code. Either VINF_SUCCESS or VERR_NO_MEMORY, the latter
917 * only if @a cDirRecs is above 1.
918 * @param pCore The structure to initialize.
919 * @param pDirRec The primary directory record.
920 * @param cDirRecs Number of directory records.
921 * @param offDirRec The offset of the primary directory record.
922 * @param uVersion The file version number.
923 * @param pRockInfo Optional rock ridge info for the entry.
924 * @param pVol The volume.
925 */
926static int rtFsIsoCore_InitFrom9660DirRec(PRTFSISOCORE pCore, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
927 uint64_t offDirRec, uint32_t uVersion, PCRTFSISOROCKINFO pRockInfo, PRTFSISOVOL pVol)
928{
929 RTListInit(&pCore->Entry);
930 pCore->cRefs = 1;
931 pCore->pParentDir = NULL;
932 pCore->pVol = pVol;
933 pCore->offDirRec = offDirRec;
934 pCore->idINode = offDirRec;
935 pCore->fHaveRockInfo = pRockInfo != NULL;
936 if (pRockInfo)
937 pCore->fAttrib = pRockInfo->Info.Attr.fMode;
938 else
939 pCore->fAttrib = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
940 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
941 : 0644 | RTFS_TYPE_FILE;
942 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
943 pCore->fAttrib |= RTFS_DOS_HIDDEN;
944 pCore->cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
945 pCore->uVersion = uVersion;
946 pCore->cExtents = 1;
947 pCore->FirstExtent.cbExtent = pCore->cbObject;
948 pCore->FirstExtent.off = (ISO9660_GET_ENDIAN(&pDirRec->offExtent) + pDirRec->cExtAttrBlocks) * (uint64_t)pVol->cbBlock;
949 pCore->FirstExtent.idxPart = UINT32_MAX;
950 pCore->FirstExtent.uReserved = 0;
951
952 if (pRockInfo)
953 {
954 pCore->BirthTime = pRockInfo->Info.BirthTime;
955 pCore->ModificationTime = pRockInfo->Info.ModificationTime;
956 pCore->AccessTime = pRockInfo->Info.AccessTime;
957 pCore->ChangeTime = pRockInfo->Info.ChangeTime;
958 }
959 else
960 {
961 rtFsIso9660DateTime2TimeSpec(&pCore->ModificationTime, &pDirRec->RecTime);
962 pCore->BirthTime = pCore->ModificationTime;
963 pCore->AccessTime = pCore->ModificationTime;
964 pCore->ChangeTime = pCore->ModificationTime;
965 }
966
967 /*
968 * Deal with multiple extents.
969 */
970 if (RT_LIKELY(cDirRecs == 1))
971 { /* done */ }
972 else
973 {
974 PRTFSISOEXTENT pCurExtent = &pCore->FirstExtent;
975 while (cDirRecs > 1)
976 {
977 offDirRec += pDirRec->cbDirRec;
978 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
979 if (pDirRec->cbDirRec != 0)
980 {
981 uint64_t offDisk = ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)pVol->cbBlock;
982 uint32_t cbExtent = ISO9660_GET_ENDIAN(&pDirRec->cbData);
983 pCore->cbObject += cbExtent;
984
985 if (pCurExtent->off + pCurExtent->cbExtent == offDisk)
986 pCurExtent->cbExtent += cbExtent;
987 else
988 {
989 void *pvNew = RTMemRealloc(pCore->paExtents, pCore->cExtents * sizeof(pCore->paExtents[0]));
990 if (pvNew)
991 pCore->paExtents = (PRTFSISOEXTENT)pvNew;
992 else
993 {
994 RTMemFree(pCore->paExtents);
995 return VERR_NO_MEMORY;
996 }
997 pCurExtent = &pCore->paExtents[pCore->cExtents - 1];
998 pCurExtent->cbExtent = cbExtent;
999 pCurExtent->off = offDisk;
1000 pCurExtent->idxPart = UINT32_MAX;
1001 pCurExtent->uReserved = 0;
1002 pCore->cExtents++;
1003 }
1004 cDirRecs--;
1005 }
1006 else
1007 {
1008 uint64_t cbSkip = (offDirRec + pVol->cbSector) & ~(uint64_t)(pVol->cbSector - 1U);
1009 offDirRec += cbSkip;
1010 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + (size_t)cbSkip);
1011 }
1012 }
1013 }
1014 return VINF_SUCCESS;
1015}
1016
1017
1018/**
1019 * Initalizes the allocation extends of a core structure.
1020 *
1021 * @returns IPRT status code
1022 * @param pCore The core structure.
1023 * @param pbAllocDescs Pointer to the allocation descriptor data.
1024 * @param cbAllocDescs The size of the allocation descriptor data.
1025 * @param fIcbTagFlags The ICB tag flags.
1026 * @param idxDefaultPart The default data partition.
1027 * @param offAllocDescs The disk byte offset corresponding to @a pbAllocDesc
1028 * in case it's used as data storage (type 3).
1029 * @param pVol The volume instance data.
1030 */
1031static int rtFsIsoCore_InitExtentsUdfIcbEntry(PRTFSISOCORE pCore, uint8_t const *pbAllocDescs, uint32_t cbAllocDescs,
1032 uint32_t fIcbTagFlags, uint32_t idxDefaultPart, uint64_t offAllocDescs,
1033 PRTFSISOVOL pVol)
1034{
1035 /*
1036 * Just in case there are mutiple file entries in the ICB.
1037 */
1038 if (pCore->paExtents != NULL)
1039 {
1040 LogRelMax(45, ("ISO/UDF: Re-reading extents - multiple file entries?\n"));
1041 RTMemFree(pCore->paExtents);
1042 pCore->paExtents = NULL;
1043 }
1044
1045 /*
1046 * Figure the (minimal) size of an allocation descriptor, deal with the
1047 * embedded storage and invalid descriptor types.
1048 */
1049 uint32_t cbOneDesc;
1050 switch (fIcbTagFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1051 {
1052 case UDF_ICB_FLAGS_AD_TYPE_EMBEDDED:
1053 pCore->cExtents = 1;
1054 pCore->FirstExtent.cbExtent = cbAllocDescs;
1055 pCore->FirstExtent.off = offAllocDescs;
1056 pCore->FirstExtent.idxPart = idxDefaultPart;
1057 return VINF_SUCCESS;
1058
1059 case UDF_ICB_FLAGS_AD_TYPE_SHORT: cbOneDesc = sizeof(UDFSHORTAD); break;
1060 case UDF_ICB_FLAGS_AD_TYPE_LONG: cbOneDesc = sizeof(UDFLONGAD); break;
1061 case UDF_ICB_FLAGS_AD_TYPE_EXTENDED: cbOneDesc = sizeof(UDFEXTAD); break;
1062
1063 default:
1064 LogRelMax(45, ("ISO/UDF: Unknown allocation descriptor type %#x\n", fIcbTagFlags));
1065 return VERR_ISO_FS_UNKNOWN_AD_TYPE;
1066 }
1067 if (cbAllocDescs >= cbOneDesc)
1068 {
1069 /*
1070 * Loop thru the allocation descriptors.
1071 */
1072 PRTFSISOEXTENT pCurExtent = NULL;
1073 union
1074 {
1075 uint8_t const *pb;
1076 PCUDFSHORTAD pShort;
1077 PCUDFLONGAD pLong;
1078 PCUDFEXTAD pExt;
1079 } uPtr;
1080 uPtr.pb = pbAllocDescs;
1081 do
1082 {
1083 /* Extract the information we need from the descriptor. */
1084 uint32_t idxBlock;
1085 uint32_t idxPart;
1086 uint32_t cb;
1087 uint8_t uType;
1088 switch (fIcbTagFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1089 {
1090 case UDF_ICB_FLAGS_AD_TYPE_SHORT:
1091 uType = uPtr.pShort->uType;
1092 cb = uPtr.pShort->cb;
1093 idxBlock = uPtr.pShort->off;
1094 idxPart = idxDefaultPart;
1095 cbAllocDescs -= sizeof(*uPtr.pShort);
1096 uPtr.pShort++;
1097 break;
1098 case UDF_ICB_FLAGS_AD_TYPE_LONG:
1099 uType = uPtr.pLong->uType;
1100 cb = uPtr.pLong->cb;
1101 idxBlock = uPtr.pLong->Location.off;
1102 idxPart = uPtr.pLong->Location.uPartitionNo;
1103 cbAllocDescs -= sizeof(*uPtr.pLong);
1104 uPtr.pLong++;
1105 break;
1106 case UDF_ICB_FLAGS_AD_TYPE_EXTENDED:
1107 if ( uPtr.pExt->cbInformation > cbAllocDescs
1108 || uPtr.pExt->cbInformation < sizeof(*uPtr.pExt))
1109 return VERR_ISOFS_BAD_EXTAD;
1110 uType = uPtr.pExt->uType;
1111 cb = uPtr.pExt->cb;
1112 idxBlock = uPtr.pExt->Location.off;
1113 idxPart = uPtr.pExt->Location.uPartitionNo;
1114 cbAllocDescs -= uPtr.pExt->cbInformation;
1115 uPtr.pb += uPtr.pExt->cbInformation;
1116 break;
1117 default:
1118 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1119 }
1120
1121 /* Check if we can extend the current extent. This is useful since
1122 the descriptors can typically only cover 1GB. */
1123 uint64_t const off = (uint64_t)idxBlock << pVol->Udf.VolInfo.cShiftBlock;
1124 if ( pCurExtent != NULL
1125 && ( pCurExtent->off != UINT64_MAX
1126 ? uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED
1127 && pCurExtent->off + pCurExtent->cbExtent == off
1128 && pCurExtent->idxPart == idxPart
1129 : uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED) )
1130 pCurExtent->cbExtent += cb;
1131 else
1132 {
1133 /* Allocate a new descriptor. */
1134 if (pCore->cExtents == 0)
1135 {
1136 pCore->cExtents = 1;
1137 pCurExtent = &pCore->FirstExtent;
1138 }
1139 else
1140 {
1141 void *pvNew = RTMemRealloc(pCore->paExtents, pCore->cExtents * sizeof(pCore->paExtents[0]));
1142 if (pvNew)
1143 pCore->paExtents = (PRTFSISOEXTENT)pvNew;
1144 else
1145 {
1146 RTMemFree(pCore->paExtents);
1147 pCore->paExtents = NULL;
1148 pCore->cExtents = 0;
1149 return VERR_NO_MEMORY;
1150 }
1151 pCurExtent = &pCore->paExtents[pCore->cExtents - 1];
1152 pCore->cExtents++;
1153 }
1154
1155 /* Initialize it. */
1156 if (uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
1157 {
1158 pCurExtent->off = off;
1159 pCurExtent->idxPart = idxPart;
1160 }
1161 else
1162 {
1163 pCurExtent->off = UINT64_MAX;
1164 pCurExtent->idxPart = UINT32_MAX;
1165 }
1166 pCurExtent->cbExtent = cb;
1167 pCurExtent->uReserved = 0;
1168 }
1169 } while (cbAllocDescs >= cbOneDesc);
1170
1171 if (cbAllocDescs > 0)
1172 LogRelMax(45,("ISO/UDF: Warning! %u bytes left in allocation descriptor: %.*Rhxs\n", cbAllocDescs, cbAllocDescs, uPtr.pb));
1173 }
1174 else
1175 {
1176 /*
1177 * Zero descriptors
1178 */
1179 pCore->cExtents = 0;
1180 pCore->FirstExtent.off = UINT64_MAX;
1181 pCore->FirstExtent.cbExtent = 0;
1182 pCore->FirstExtent.idxPart = UINT32_MAX;
1183
1184 if (cbAllocDescs > 0)
1185 LogRelMax(45, ("ISO/UDF: Warning! Allocation descriptor area is shorted than one descriptor: %#u vs %#u: %.*Rhxs\n",
1186 cbAllocDescs, cbOneDesc, cbAllocDescs, pbAllocDescs));
1187 }
1188 return VINF_SUCCESS;
1189}
1190
1191
1192/**
1193 * Converts ICB flags, ICB file type and file entry permissions to an IPRT file
1194 * mode mask.
1195 *
1196 * @returns IPRT status ocde
1197 * @param fIcbTagFlags The ICB flags.
1198 * @param bFileType The ICB file type.
1199 * @param fPermission The file entry permission mask.
1200 * @param pfAttrib Where to return the IRPT file mode mask.
1201 */
1202static int rtFsIsoCore_UdfStuffToFileMode(uint32_t fIcbTagFlags, uint8_t bFileType, uint32_t fPermission, PRTFMODE pfAttrib)
1203{
1204 /*
1205 * Type:
1206 */
1207 RTFMODE fAttrib;
1208 switch (bFileType)
1209 {
1210 case UDF_FILE_TYPE_DIRECTORY:
1211 fAttrib = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
1212 break;
1213
1214 case UDF_FILE_TYPE_REGULAR_FILE:
1215 case UDF_FILE_TYPE_REAL_TIME_FILE:
1216 fAttrib = RTFS_TYPE_FILE;
1217 break;
1218
1219 case UDF_FILE_TYPE_SYMBOLIC_LINK:
1220 fAttrib = RTFS_TYPE_SYMLINK;
1221 break;
1222
1223 case UDF_FILE_TYPE_BLOCK_DEVICE:
1224 fAttrib = RTFS_TYPE_DEV_BLOCK;
1225 break;
1226 case UDF_FILE_TYPE_CHARACTER_DEVICE:
1227 fAttrib = RTFS_TYPE_DEV_CHAR;
1228 break;
1229
1230 case UDF_FILE_TYPE_FIFO:
1231 fAttrib = RTFS_TYPE_FIFO;
1232 break;
1233
1234 case UDF_FILE_TYPE_SOCKET:
1235 fAttrib = RTFS_TYPE_SOCKET;
1236 break;
1237
1238 case UDF_FILE_TYPE_STREAM_DIRECTORY:
1239 case UDF_FILE_TYPE_EXTENDED_ATTRIBUTES:
1240 case UDF_FILE_TYPE_TERMINAL_ENTRY:
1241 case UDF_FILE_TYPE_VAT:
1242 case UDF_FILE_TYPE_METADATA_FILE:
1243 case UDF_FILE_TYPE_METADATA_MIRROR_FILE:
1244 case UDF_FILE_TYPE_METADATA_BITMAP_FILE:
1245 case UDF_FILE_TYPE_NOT_SPECIFIED:
1246 case UDF_FILE_TYPE_INDIRECT_ENTRY:
1247 case UDF_FILE_TYPE_UNALLOCATED_SPACE_ENTRY:
1248 case UDF_FILE_TYPE_PARTITION_INTEGRITY_ENTRY:
1249 LogRelMax(45, ("ISO/UDF: Warning! Wrong file type: %#x\n", bFileType));
1250 return VERR_ISOFS_WRONG_FILE_TYPE;
1251
1252 default:
1253 LogRelMax(45, ("ISO/UDF: Warning! Unknown file type: %#x\n", bFileType));
1254 return VERR_ISOFS_UNKNOWN_FILE_TYPE;
1255 }
1256
1257 /*
1258 * Permissions:
1259 */
1260 if (fPermission & UDF_PERM_OTH_EXEC)
1261 fAttrib |= RTFS_UNIX_IXOTH;
1262 if (fPermission & UDF_PERM_OTH_READ)
1263 fAttrib |= RTFS_UNIX_IROTH;
1264 if (fPermission & UDF_PERM_OTH_WRITE)
1265 fAttrib |= RTFS_UNIX_IWOTH;
1266
1267 if (fPermission & UDF_PERM_GRP_EXEC)
1268 fAttrib |= RTFS_UNIX_IXGRP;
1269 if (fPermission & UDF_PERM_GRP_READ)
1270 fAttrib |= RTFS_UNIX_IRGRP;
1271 if (fPermission & UDF_PERM_GRP_WRITE)
1272 fAttrib |= RTFS_UNIX_IWGRP;
1273
1274 if (fPermission & UDF_PERM_USR_EXEC)
1275 fAttrib |= RTFS_UNIX_IXUSR;
1276 if (fPermission & UDF_PERM_USR_READ)
1277 fAttrib |= RTFS_UNIX_IRUSR;
1278 if (fPermission & UDF_PERM_USR_WRITE)
1279 fAttrib |= RTFS_UNIX_IWUSR;
1280
1281 if ( !(fAttrib & (UDF_PERM_OTH_WRITE | UDF_PERM_GRP_WRITE | UDF_PERM_USR_WRITE))
1282 && (fAttrib & (UDF_PERM_OTH_READ | UDF_PERM_GRP_READ | UDF_PERM_USR_READ)) )
1283 fAttrib |= RTFS_DOS_READONLY;
1284
1285 /*
1286 * Attributes:
1287 */
1288 if (fIcbTagFlags & UDF_ICB_FLAGS_ARCHIVE)
1289 fAttrib |= RTFS_DOS_ARCHIVED;
1290 if (fIcbTagFlags & UDF_ICB_FLAGS_SYSTEM)
1291 fAttrib |= RTFS_DOS_SYSTEM;
1292 if (fIcbTagFlags & UDF_ICB_FLAGS_ARCHIVE)
1293 fAttrib |= RTFS_DOS_ARCHIVED;
1294
1295 if (fIcbTagFlags & UDF_ICB_FLAGS_SET_UID)
1296 fAttrib |= RTFS_UNIX_ISUID;
1297 if (fIcbTagFlags & UDF_ICB_FLAGS_SET_GID)
1298 fAttrib |= RTFS_UNIX_ISGID;
1299 if (fIcbTagFlags & UDF_ICB_FLAGS_STICKY)
1300 fAttrib |= RTFS_UNIX_ISTXT;
1301
1302 /* Warn about weird flags. */
1303 if (fIcbTagFlags & UDF_ICB_FLAGS_TRANSFORMED)
1304 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_TRANSFORMED!\n"));
1305 if (fIcbTagFlags & UDF_ICB_FLAGS_MULTI_VERSIONS)
1306 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_MULTI_VERSIONS!\n"));
1307 if (fIcbTagFlags & UDF_ICB_FLAGS_STREAM)
1308 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_STREAM!\n"));
1309 if (fIcbTagFlags & UDF_ICB_FLAGS_RESERVED_MASK)
1310 LogRelMax(45, ("ISO/UDF: Warning! UDF_ICB_FLAGS_RESERVED_MASK (%#x)!\n", fIcbTagFlags & UDF_ICB_FLAGS_RESERVED_MASK));
1311
1312 *pfAttrib = fAttrib;
1313 return VINF_SUCCESS;
1314}
1315
1316
1317/**
1318 * Initialize/update a core object structure from an UDF extended file entry.
1319 *
1320 * @returns IPRT status code
1321 * @param pCore The core object structure to initialize.
1322 * @param pFileEntry The file entry.
1323 * @param idxDefaultPart The default data partition.
1324 * @param pcProcessed Variable to increment on success.
1325 * @param pVol The volume instance.
1326 */
1327static int rtFsIsoCore_InitFromUdfIcbExFileEntry(PRTFSISOCORE pCore, PCUDFEXFILEENTRY pFileEntry, uint32_t idxDefaultPart,
1328 uint32_t *pcProcessed, PRTFSISOVOL pVol)
1329{
1330#ifdef LOG_ENABLED
1331 /*
1332 * Log it.
1333 */
1334 if (LogIs2Enabled())
1335 {
1336 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", IcbTag.cEntiresBeforeThis);
1337 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.uStrategyType);
1338 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[0]);
1339 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[1]);
1340 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.cMaxEntries);
1341 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bReserved);
1342 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bFileType);
1343 UDF_LOG2_MEMBER_LBADDR(pFileEntry, IcbTag.ParentIcb);
1344 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.fFlags);
1345 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uid);
1346 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", gid);
1347 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", fPermissions);
1348 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", cHardlinks);
1349 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", uRecordFormat);
1350 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", fRecordDisplayAttribs);
1351 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbRecord);
1352 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cbData);
1353 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cbObject);
1354 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cLogicalBlocks);
1355 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, AccessTime);
1356 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ModificationTime);
1357 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, BirthTime);
1358 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ChangeTime);
1359 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uCheckpoint);
1360 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uReserved);
1361 UDF_LOG2_MEMBER_LONGAD(pFileEntry, ExtAttribIcb);
1362 UDF_LOG2_MEMBER_LONGAD(pFileEntry, StreamDirIcb);
1363 UDF_LOG2_MEMBER_ENTITY_ID(pFileEntry, idImplementation);
1364 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", INodeId);
1365 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbExtAttribs);
1366 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbAllocDescs);
1367 if (pFileEntry->cbExtAttribs > 0)
1368 Log2((pFileEntry->cbExtAttribs <= 16 ? "ISO/UDF: %-32s %.*Rhxs\n" : "ISO/UDF: %-32s\n%.*RhxD\n",
1369 "abExtAttribs:", pFileEntry->cbExtAttribs, pFileEntry->abExtAttribs));
1370 if (pFileEntry->cbAllocDescs > 0)
1371 switch (pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1372 {
1373 case UDF_ICB_FLAGS_AD_TYPE_SHORT:
1374 {
1375 PCUDFSHORTAD paDescs = (PCUDFSHORTAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1376 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1377 for (uint32_t i = 0; i < cDescs; i++)
1378 Log2(("ISO/UDF: ShortAD[%u]: %#010RX32 LB %#010RX32; type=%u\n",
1379 i, paDescs[i].off, paDescs[i].cb, paDescs[i].uType));
1380 break;
1381 }
1382 case UDF_ICB_FLAGS_AD_TYPE_LONG:
1383 {
1384 PCUDFLONGAD paDescs = (PCUDFLONGAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1385 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1386 for (uint32_t i = 0; i < cDescs; i++)
1387 Log2(("ISO/UDF: LongAD[%u]: %#06RX16:%#010RX32 LB %#010RX32; type=%u iu=%.6Rhxs\n",
1388 i, paDescs[i].Location.uPartitionNo, paDescs[i].Location.off,
1389 paDescs[i].cb, paDescs[i].uType, &paDescs[i].ImplementationUse));
1390 break;
1391 }
1392 default:
1393 Log2(("ISO/UDF: %-32s Type=%u\n%.*RhxD\n",
1394 "abExtAttribs:", pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK,
1395 pFileEntry->cbAllocDescs, &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs]));
1396 break;
1397 }
1398 }
1399#endif
1400
1401 /*
1402 * Basic sanity checking of what we use.
1403 */
1404 if ( RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs + pFileEntry->cbAllocDescs
1405 > pVol->Udf.VolInfo.cbBlock
1406 || (pFileEntry->cbExtAttribs & 3) != 0
1407 || pFileEntry->cbExtAttribs >= pVol->Udf.VolInfo.cbBlock
1408 || (pFileEntry->cbAllocDescs & 3) != 0
1409 || pFileEntry->cbAllocDescs >= pVol->Udf.VolInfo.cbBlock)
1410 {
1411 LogRelMax(45, ("ISO/UDF: Extended file entry (ICB) is bad size values: cbAllocDesc=%#x cbExtAttribs=%#x (cbBlock=%#x)\n",
1412 pFileEntry->cbAllocDescs, pFileEntry->cbExtAttribs, pVol->Udf.VolInfo.cbBlock));
1413 return VERR_ISOFS_BAD_FILE_ENTRY;
1414 }
1415
1416 //pCore->uid = pFileEntry->uid;
1417 //pCore->gid = pFileEntry->gid;
1418 //pCore->cHardlinks = RT_MIN(pFileEntry->cHardlinks, 1);
1419 pCore->cbObject = pFileEntry->cbData;
1420 //pCore->cbAllocated = pFileEntry->cLogicalBlocks << pVol->Udf.VolInfo.cShiftBlock;
1421 pCore->idINode = pFileEntry->INodeId;
1422
1423 rtFsIsoUdfTimestamp2TimeSpec(&pCore->AccessTime, &pFileEntry->AccessTime);
1424 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ModificationTime, &pFileEntry->ModificationTime);
1425 rtFsIsoUdfTimestamp2TimeSpec(&pCore->BirthTime, &pFileEntry->BirthTime);
1426 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ChangeTime, &pFileEntry->ChangeTime);
1427
1428 if ( pFileEntry->uRecordFormat
1429 || pFileEntry->fRecordDisplayAttribs
1430 || pFileEntry->cbRecord)
1431 LogRelMax(45, ("ISO/UDF: uRecordFormat=%#x fRecordDisplayAttribs=%#x cbRecord=%#x\n",
1432 pFileEntry->uRecordFormat, pFileEntry->fRecordDisplayAttribs, pFileEntry->cbRecord));
1433
1434 /*
1435 * Conver the file mode.
1436 */
1437 int rc = rtFsIsoCore_UdfStuffToFileMode(pFileEntry->IcbTag.fFlags, pFileEntry->IcbTag.bFileType,
1438 pFileEntry->fPermissions, &pCore->fAttrib);
1439 if (RT_SUCCESS(rc))
1440 {
1441 /*
1442 * Convert extent info.
1443 */
1444 rc = rtFsIsoCore_InitExtentsUdfIcbEntry(pCore,
1445 &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs],
1446 pFileEntry->cbAllocDescs,
1447 pFileEntry->IcbTag.fFlags,
1448 idxDefaultPart,
1449 ((uint64_t)pFileEntry->Tag.offTag << pVol->Udf.VolInfo.cShiftBlock)
1450 + RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs,
1451 pVol);
1452 if (RT_SUCCESS(rc))
1453 {
1454 /*
1455 * We're good.
1456 */
1457 *pcProcessed += 1;
1458 return VINF_SUCCESS;
1459 }
1460
1461 /* Just in case. */
1462 if (pCore->paExtents)
1463 {
1464 RTMemFree(pCore->paExtents);
1465 pCore->paExtents = NULL;
1466 }
1467 pCore->cExtents = 0;
1468 }
1469 return rc;
1470}
1471
1472
1473/**
1474 * Initialize/update a core object structure from an UDF file entry.
1475 *
1476 * @returns IPRT status code
1477 * @param pCore The core object structure to initialize.
1478 * @param pFileEntry The file entry.
1479 * @param idxDefaultPart The default data partition.
1480 * @param pcProcessed Variable to increment on success.
1481 * @param pVol The volume instance.
1482 */
1483static int rtFsIsoCore_InitFromUdfIcbFileEntry(PRTFSISOCORE pCore, PCUDFFILEENTRY pFileEntry, uint32_t idxDefaultPart,
1484 uint32_t *pcProcessed, PRTFSISOVOL pVol)
1485{
1486#ifdef LOG_ENABLED
1487 /*
1488 * Log it.
1489 */
1490 if (LogIs2Enabled())
1491 {
1492 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", IcbTag.cEntiresBeforeThis);
1493 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.uStrategyType);
1494 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[0]);
1495 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.abStrategyParams[1]);
1496 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.cMaxEntries);
1497 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bReserved);
1498 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", IcbTag.bFileType);
1499 UDF_LOG2_MEMBER_LBADDR(pFileEntry, IcbTag.ParentIcb);
1500 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", IcbTag.fFlags);
1501 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uid);
1502 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", gid);
1503 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", fPermissions);
1504 UDF_LOG2_MEMBER(pFileEntry, "#06RX16", cHardlinks);
1505 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", uRecordFormat);
1506 UDF_LOG2_MEMBER(pFileEntry, "#04RX8", fRecordDisplayAttribs);
1507 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbRecord);
1508 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cbData);
1509 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", cLogicalBlocks);
1510 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, AccessTime);
1511 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ModificationTime);
1512 UDF_LOG2_MEMBER_TIMESTAMP(pFileEntry, ChangeTime);
1513 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", uCheckpoint);
1514 UDF_LOG2_MEMBER_LONGAD(pFileEntry, ExtAttribIcb);
1515 UDF_LOG2_MEMBER_ENTITY_ID(pFileEntry, idImplementation);
1516 UDF_LOG2_MEMBER(pFileEntry, "#018RX64", INodeId);
1517 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbExtAttribs);
1518 UDF_LOG2_MEMBER(pFileEntry, "#010RX32", cbAllocDescs);
1519 if (pFileEntry->cbExtAttribs > 0)
1520 Log2((pFileEntry->cbExtAttribs <= 16 ? "ISO/UDF: %-32s %.*Rhxs\n" : "ISO/UDF: %-32s\n%.*RhxD\n",
1521 "abExtAttribs:", pFileEntry->cbExtAttribs, pFileEntry->abExtAttribs));
1522 if (pFileEntry->cbAllocDescs > 0)
1523 switch (pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK)
1524 {
1525 case UDF_ICB_FLAGS_AD_TYPE_SHORT:
1526 {
1527 PCUDFSHORTAD paDescs = (PCUDFSHORTAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1528 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1529 for (uint32_t i = 0; i < cDescs; i++)
1530 Log2(("ISO/UDF: ShortAD[%u]: %#010RX32 LB %#010RX32; type=%u\n",
1531 i, paDescs[i].off, paDescs[i].cb, paDescs[i].uType));
1532 break;
1533 }
1534 case UDF_ICB_FLAGS_AD_TYPE_LONG:
1535 {
1536 PCUDFLONGAD paDescs = (PCUDFLONGAD)&pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs];
1537 uint32_t cDescs = pFileEntry->cbAllocDescs / sizeof(paDescs[0]);
1538 for (uint32_t i = 0; i < cDescs; i++)
1539 Log2(("ISO/UDF: LongAD[%u]: %#06RX16:%#010RX32 LB %#010RX32; type=%u iu=%.6Rhxs\n",
1540 i, paDescs[i].Location.uPartitionNo, paDescs[i].Location.off,
1541 paDescs[i].cb, paDescs[i].uType, &paDescs[i].ImplementationUse));
1542 break;
1543 }
1544 default:
1545 Log2(("ISO/UDF: %-32s Type=%u\n%.*RhxD\n",
1546 "abExtAttribs:", pFileEntry->IcbTag.fFlags & UDF_ICB_FLAGS_AD_TYPE_MASK,
1547 pFileEntry->cbAllocDescs, &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs]));
1548 break;
1549 }
1550 }
1551#endif
1552
1553 /*
1554 * Basic sanity checking of what we use.
1555 */
1556 if ( RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs + pFileEntry->cbAllocDescs
1557 > pVol->Udf.VolInfo.cbBlock
1558 || (pFileEntry->cbExtAttribs & 3) != 0
1559 || pFileEntry->cbExtAttribs >= pVol->Udf.VolInfo.cbBlock
1560 || (pFileEntry->cbAllocDescs & 3) != 0
1561 || pFileEntry->cbAllocDescs >= pVol->Udf.VolInfo.cbBlock)
1562 {
1563 LogRelMax(45, ("ISO/UDF: File entry (ICB) is bad size values: cbAllocDesc=%#x cbExtAttribs=%#x (cbBlock=%#x)\n",
1564 pFileEntry->cbAllocDescs, pFileEntry->cbExtAttribs, pVol->Udf.VolInfo.cbBlock));
1565 return VERR_ISOFS_BAD_FILE_ENTRY;
1566 }
1567
1568 //pCore->uid = pFileEntry->uid;
1569 //pCore->gid = pFileEntry->gid;
1570 //pCore->cHardlinks = RT_MIN(pFileEntry->cHardlinks, 1);
1571 pCore->cbObject = pFileEntry->cbData;
1572 //pCore->cbAllocated = pFileEntry->cLogicalBlocks << pVol->Udf.VolInfo.cShiftBlock;
1573 pCore->idINode = pFileEntry->INodeId;
1574
1575 rtFsIsoUdfTimestamp2TimeSpec(&pCore->AccessTime, &pFileEntry->AccessTime);
1576 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ModificationTime, &pFileEntry->ModificationTime);
1577 rtFsIsoUdfTimestamp2TimeSpec(&pCore->ChangeTime, &pFileEntry->ChangeTime);
1578 pCore->BirthTime = pCore->ModificationTime;
1579 if (RTTimeSpecCompare(&pCore->BirthTime, &pCore->ChangeTime) > 0)
1580 pCore->BirthTime = pCore->ChangeTime;
1581 if (RTTimeSpecCompare(&pCore->BirthTime, &pCore->AccessTime) > 0)
1582 pCore->BirthTime = pCore->AccessTime;
1583
1584 if ( pFileEntry->uRecordFormat
1585 || pFileEntry->fRecordDisplayAttribs
1586 || pFileEntry->cbRecord)
1587 LogRelMax(45, ("ISO/UDF: uRecordFormat=%#x fRecordDisplayAttribs=%#x cbRecord=%#x\n",
1588 pFileEntry->uRecordFormat, pFileEntry->fRecordDisplayAttribs, pFileEntry->cbRecord));
1589
1590 /*
1591 * Conver the file mode.
1592 */
1593 int rc = rtFsIsoCore_UdfStuffToFileMode(pFileEntry->IcbTag.fFlags, pFileEntry->IcbTag.bFileType,
1594 pFileEntry->fPermissions, &pCore->fAttrib);
1595 if (RT_SUCCESS(rc))
1596 {
1597 /*
1598 * Convert extent info.
1599 */
1600 rc = rtFsIsoCore_InitExtentsUdfIcbEntry(pCore,
1601 &pFileEntry->abExtAttribs[pFileEntry->cbExtAttribs],
1602 pFileEntry->cbAllocDescs,
1603 pFileEntry->IcbTag.fFlags,
1604 idxDefaultPart,
1605 ((uint64_t)pFileEntry->Tag.offTag << pVol->Udf.VolInfo.cShiftBlock)
1606 + RT_UOFFSETOF(UDFFILEENTRY, abExtAttribs) + pFileEntry->cbExtAttribs,
1607 pVol);
1608 if (RT_SUCCESS(rc))
1609 {
1610 /*
1611 * We're good.
1612 */
1613 *pcProcessed += 1;
1614 return VINF_SUCCESS;
1615 }
1616
1617 /* Just in case. */
1618 if (pCore->paExtents)
1619 {
1620 RTMemFree(pCore->paExtents);
1621 pCore->paExtents = NULL;
1622 }
1623 pCore->cExtents = 0;
1624 }
1625 return rc;
1626}
1627
1628
1629/**
1630 * Recursive helper for rtFsIsoCore_InitFromUdfIcbAndFileIdDesc.
1631 *
1632 * @returns IRPT status code.
1633 * @param pCore The core structure to initialize.
1634 * @param AllocDesc The ICB allocation descriptor.
1635 * @param pbBuf The buffer, one logical block in size.
1636 * @param cNestings The number of recursive nestings (should be zero).
1637 * @param pcProcessed Variable to update when we've processed something
1638 * useful.
1639 * @param pcIndirections Variable tracing the number of indirections we've
1640 * taken during the processing. This is used to
1641 * prevent us from looping forever on a bad chain
1642 * @param pVol The volue instance data.
1643 */
1644static int rtFsIsoCore_InitFromUdfIcbRecursive(PRTFSISOCORE pCore, UDFLONGAD AllocDesc, uint8_t *pbBuf, uint32_t cNestings,
1645 uint32_t *pcProcessed, uint32_t *pcIndirections, PRTFSISOVOL pVol)
1646{
1647 if (cNestings >= 8)
1648 return VERR_ISOFS_TOO_DEEP_ICB_RECURSION;
1649
1650 for (;;)
1651 {
1652 if (*pcIndirections >= 32)
1653 return VERR_ISOFS_TOO_MANY_ICB_INDIRECTIONS;
1654
1655 /*
1656 * Check the basic validity of the allocation descriptor.
1657 */
1658 if ( AllocDesc.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED
1659 && AllocDesc.cb >= sizeof(UDFICBTAG) )
1660 { /* likely */ }
1661 else if (AllocDesc.uType != UDF_AD_TYPE_RECORDED_AND_ALLOCATED)
1662 {
1663 Log(("ISO/UDF: ICB has alloc type %d!\n", AllocDesc.uType));
1664 return VINF_SUCCESS;
1665 }
1666 else
1667 {
1668 LogRelMax(45, ("ISO/UDF: ICB is too small: %u bytes\n", AllocDesc.cb));
1669 return AllocDesc.cb == 0 ? VINF_SUCCESS : VERR_ISOFS_ICB_ENTRY_TOO_SMALL;
1670 }
1671
1672 /*
1673 * Process it block by block.
1674 */
1675 uint32_t cBlocks = (AllocDesc.cb + pVol->Udf.VolInfo.cbBlock - 1) >> pVol->Udf.VolInfo.cShiftBlock;
1676 for (uint32_t idxBlock = 0; ; idxBlock++)
1677 {
1678 /*
1679 * Read a block
1680 */
1681 size_t cbToRead = RT_MIN(pVol->Udf.VolInfo.cbBlock, AllocDesc.cb);
1682 int rc = rtFsIsoVolUdfVpRead(pVol, AllocDesc.Location.uPartitionNo, AllocDesc.Location.off + idxBlock, 0,
1683 pbBuf, cbToRead);
1684 if (RT_FAILURE(rc))
1685 return rc;
1686 if (cbToRead < pVol->Udf.VolInfo.cbBlock)
1687 RT_BZERO(&pbBuf[cbToRead], pVol->Udf.VolInfo.cbBlock - cbToRead);
1688
1689 /*
1690 * Verify the TAG.
1691 */
1692 PUDFICBHDR pHdr = (PUDFICBHDR)pbBuf;
1693 rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pHdr->Tag, pVol->Udf.VolInfo.cbBlock, UINT16_MAX,
1694 AllocDesc.Location.off + idxBlock, NULL);
1695 if (RT_FAILURE(rc))
1696 return rc;
1697
1698 /*
1699 * Do specific processing.
1700 */
1701 if (pHdr->Tag.idTag == UDF_TAG_ID_FILE_ENTRY)
1702 rc = rtFsIsoCore_InitFromUdfIcbFileEntry(pCore, (PCUDFFILEENTRY)pHdr, AllocDesc.Location.uPartitionNo,
1703 pcProcessed, pVol);
1704 else if (pHdr->Tag.idTag == UDF_TAG_ID_EXTENDED_FILE_ENTRY)
1705 rc = rtFsIsoCore_InitFromUdfIcbExFileEntry(pCore, (PCUDFEXFILEENTRY)pHdr, AllocDesc.Location.uPartitionNo,
1706 pcProcessed, pVol);
1707 else if (pHdr->Tag.idTag == UDF_TAG_ID_INDIRECT_ENTRY)
1708 {
1709 PUDFINDIRECTENTRY pIndir = (PUDFINDIRECTENTRY)pHdr;
1710 *pcIndirections += 1;
1711 if (pIndir->IndirectIcb.cb != 0)
1712 {
1713 if (idxBlock + 1 == cBlocks)
1714 {
1715 AllocDesc = pIndir->IndirectIcb;
1716 Log2(("ISO/UDF: ICB: Indirect entry - looping: %x:%#010RX32 LB %#x; uType=%d\n",
1717 AllocDesc.Location.uPartitionNo, AllocDesc.Location.off, AllocDesc.cb, AllocDesc.uType));
1718 break;
1719 }
1720 Log2(("ISO/UDF: ICB: Indirect entry - recursing: %x:%#010RX32 LB %#x; uType=%d\n",
1721 pIndir->IndirectIcb.Location.uPartitionNo, pIndir->IndirectIcb.Location.off,
1722 pIndir->IndirectIcb.cb, pIndir->IndirectIcb.uType));
1723 rc = rtFsIsoCore_InitFromUdfIcbRecursive(pCore, pIndir->IndirectIcb, pbBuf, cNestings,
1724 pcProcessed, pcIndirections, pVol);
1725 }
1726 else
1727 Log(("ISO/UDF: zero length indirect entry\n"));
1728 }
1729 else if (pHdr->Tag.idTag == UDF_TAG_ID_TERMINAL_ENTRY)
1730 {
1731 Log2(("ISO/UDF: Terminal ICB entry\n"));
1732 return VINF_SUCCESS;
1733 }
1734 else if (pHdr->Tag.idTag == UDF_TAG_ID_UNALLOCATED_SPACE_ENTRY)
1735 {
1736 Log2(("ISO/UDF: Unallocated space entry: skipping\n"));
1737 /* Ignore since we don't do writing (UDFUNALLOCATEDSPACEENTRY) */
1738 }
1739 else
1740 {
1741 LogRelMax(90, ("ISO/UDF: Unknown ICB type %#x\n", pHdr->Tag.idTag));
1742 return VERR_ISOFS_UNSUPPORTED_ICB;
1743 }
1744 if (RT_FAILURE(rc))
1745 return rc;
1746
1747 /*
1748 * Advance.
1749 */
1750 if (idxBlock + 1 >= cBlocks)
1751 return VINF_SUCCESS;
1752 }
1753
1754 /* If we get here, we've jumped thru an indirect entry. */
1755 }
1756 /* never reached */
1757}
1758
1759
1760
1761/**
1762 * Initialize a core structure from an UDF ICB range and optionally a file ID.
1763 *
1764 * @returns IPRT status code.
1765 * @param pCore The core structure to initialize.
1766 * Caller must've ZEROed this structure!
1767 * @param pAllocDesc The ICB allocation descriptor.
1768 * @param pFid The file ID descriptor. Optional.
1769 * @param offInDir The offset of the file ID descriptor in the
1770 * parent directory. This is used when looking up
1771 * shared directory objects. (Pass 0 for root.)
1772 * @param pVol The instance.
1773 *
1774 * @note Caller must check for UDF_FILE_FLAGS_DELETED before calling if the
1775 * object is supposed to be used for real stuff.
1776 */
1777static int rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(PRTFSISOCORE pCore, PCUDFLONGAD pAllocDesc,
1778 PCUDFFILEIDDESC pFid, uintptr_t offInDir, PRTFSISOVOL pVol)
1779{
1780 Assert(pCore->cRefs == 0);
1781 Assert(pCore->cExtents == 0);
1782 Assert(pCore->paExtents == NULL);
1783 Assert(pCore->pVol == NULL);
1784
1785 /*
1786 * Some size sanity checking.
1787 */
1788 if (pAllocDesc->cb <= _64K)
1789 {
1790 if (pAllocDesc->cb >= sizeof(UDFICBHDR))
1791 { /* likely */ }
1792 else
1793 {
1794 Log(("rtFsIsoCore_InitFromUdfIcbAndFileIdDesc: ICB too small: %#04x:%010RX32 LB %#x\n",
1795 pAllocDesc->Location.uPartitionNo, pAllocDesc->Location.off, pAllocDesc->cb));
1796 return VERR_ISOFS_ICB_TOO_SMALL;
1797 }
1798 }
1799 else
1800 {
1801 Log(("rtFsIsoCore_InitFromUdfIcbAndFileIdDesc: ICB too big: %#04x:%010RX32 LB %#x\n",
1802 pAllocDesc->Location.uPartitionNo, pAllocDesc->Location.off, pAllocDesc->cb));
1803 return VERR_ISOFS_ICB_TOO_BIG;
1804 }
1805
1806 /*
1807 * Allocate a temporary buffer, one logical block in size.
1808 */
1809 uint8_t * const pbBuf = (uint8_t *)RTMemTmpAlloc(pVol->Udf.VolInfo.cbBlock);
1810 if (pbBuf)
1811 {
1812 uint32_t cProcessed = 0;
1813 uint32_t cIndirections = 0;
1814 int rc = rtFsIsoCore_InitFromUdfIcbRecursive(pCore, *pAllocDesc, pbBuf, 0, &cProcessed, &cIndirections, pVol);
1815 RTMemTmpFree(pbBuf);
1816 if (RT_SUCCESS(rc))
1817 {
1818 if (cProcessed > 0)
1819 {
1820 if (pFid)
1821 {
1822 if (pFid->fFlags & UDF_FILE_FLAGS_HIDDEN)
1823 pCore->fAttrib |= RTFS_DOS_HIDDEN;
1824 if (pFid->fFlags & UDF_FILE_FLAGS_DELETED)
1825 pCore->fAttrib = (pCore->fAttrib & ~RTFS_TYPE_MASK) | RTFS_TYPE_WHITEOUT;
1826 }
1827
1828 pCore->cRefs = 1;
1829 pCore->pVol = pVol;
1830 pCore->offDirRec = offInDir;
1831 return VINF_SUCCESS;
1832 }
1833 rc = VERR_ISOFS_NO_DIRECT_ICB_ENTRIES;
1834 }
1835
1836 /* White-out fix. Caller must be checking for UDF_FILE_FLAGS_DELETED */
1837 if ( pFid
1838 && (pFid->fFlags & UDF_FILE_FLAGS_DELETED))
1839 {
1840 pCore->fAttrib = (pCore->fAttrib & ~RTFS_TYPE_MASK) | RTFS_TYPE_WHITEOUT;
1841 return VINF_SUCCESS;
1842 }
1843 return rc;
1844 }
1845
1846 pCore->pVol = NULL;
1847 return VERR_NO_TMP_MEMORY;
1848}
1849
1850
1851/**
1852 * Simple UDF read function.
1853 *
1854 * This deals with extent mappings as well as virtual partition related block
1855 * mapping and such.
1856 *
1857 * @returns VBox status code.
1858 * @param pCore The core object to read data from.
1859 * @param offRead The offset to start reading at.
1860 * @param pvBuf The output buffer.
1861 * @param cbToRead The number of bytes to read.
1862 * @param pcbRead Where to return the number of bytes read.
1863 * @param poffPosMov Where to return the number of bytes to move the read
1864 * position. Optional. (Essentially same as pcbRead
1865 * except without the behavior change.)
1866 */
1867static int rtFsIsoCore_ReadWorker(PRTFSISOCORE pCore, uint64_t offRead, void *pvBuf, size_t cbToRead,
1868 size_t *pcbRead, size_t *poffPosMov)
1869{
1870 /*
1871 * Check for EOF.
1872 */
1873 if (offRead >= pCore->cbObject)
1874 {
1875 if (poffPosMov)
1876 *poffPosMov = 0;
1877 if (pcbRead)
1878 {
1879 *pcbRead = 0;
1880 return VINF_EOF;
1881 }
1882 return VERR_EOF;
1883 }
1884 int rcRet = VINF_SUCCESS;
1885 if ( cbToRead > pCore->cbObject
1886 || offRead + cbToRead > pCore->cbObject)
1887 {
1888 if (!pcbRead)
1889 {
1890 if (poffPosMov)
1891 *poffPosMov = 0;
1892 return VERR_EOF;
1893 }
1894 cbToRead = pCore->cbObject - offRead;
1895 rcRet = VINF_EOF;
1896 }
1897
1898 uint64_t cbActual = 0;
1899
1900 /*
1901 * Don't bother looking up the extent if we're not going to
1902 * read anything from it.
1903 */
1904 if (cbToRead > 0)
1905 {
1906 /*
1907 * Locate the first extent.
1908 */
1909 uint64_t offExtent = 0;
1910 uint32_t iExtent = 0;
1911 PCRTFSISOEXTENT pCurExtent = &pCore->FirstExtent;
1912 if (offRead < pCurExtent->cbExtent)
1913 { /* likely */ }
1914 else
1915 do
1916 {
1917 offExtent += pCurExtent->cbExtent;
1918 pCurExtent = &pCore->paExtents[iExtent++];
1919 if (iExtent >= pCore->cExtents)
1920 {
1921 memset(pvBuf, 0, cbToRead);
1922
1923 if (pcbRead)
1924 *pcbRead = cbToRead;
1925 if (poffPosMov)
1926 *poffPosMov = cbToRead;
1927 return rcRet;
1928 }
1929 } while (offExtent < offRead);
1930 Assert(offRead - offExtent < pCurExtent->cbExtent);
1931
1932 /*
1933 * Do the reading part.
1934 */
1935 PRTFSISOVOL pVol = pCore->pVol;
1936 for (;;)
1937 {
1938 uint64_t offIntoExtent = offRead - offExtent;
1939 size_t cbThisRead = pCurExtent->cbExtent - offIntoExtent;
1940 if (cbThisRead > cbToRead)
1941 cbThisRead = cbToRead;
1942
1943 if (pCurExtent->off == UINT64_MAX)
1944 RT_BZERO(pvBuf, cbThisRead);
1945 else
1946 {
1947 int rc2;
1948 if (pCurExtent->idxPart == UINT32_MAX)
1949 rc2 = RTVfsFileReadAt(pVol->hVfsBacking, pCurExtent->off + offIntoExtent, pvBuf, cbThisRead, NULL);
1950 else
1951 {
1952 Assert(pVol->enmType == RTFSISOVOLTYPE_UDF);
1953 if (pCurExtent->idxPart < pVol->Udf.VolInfo.cPartitions)
1954 {
1955 PRTFSISOVOLUDFPMAP pPart = &pVol->Udf.VolInfo.paPartitions[pCurExtent->idxPart];
1956 switch (pPart->bType)
1957 {
1958 case RTFSISO_UDF_PMAP_T_PLAIN:
1959 rc2 = RTVfsFileReadAt(pVol->hVfsBacking, pPart->offByteLocation + pCurExtent->off + offIntoExtent,
1960 pvBuf, cbThisRead, NULL);
1961 break;
1962
1963 default:
1964 AssertFailed();
1965 rc2 = VERR_ISOFS_IPE_1;
1966 break;
1967 }
1968 }
1969 else
1970 {
1971 Log(("ISO/UDF: Invalid partition index %#x (offset %#RX64), max partitions %#x; iExtent=%#x\n",
1972 pCurExtent->idxPart, pCurExtent->off + offIntoExtent, pVol->Udf.VolInfo.cPartitions, iExtent));
1973 rc2 = VERR_ISOFS_INVALID_PARTITION_INDEX;
1974 }
1975 }
1976 if (RT_FAILURE(rc2))
1977 {
1978 rcRet = rc2;
1979 break;
1980 }
1981 }
1982
1983 /*
1984 * Advance the buffer position and check if we're done (probable).
1985 */
1986 cbActual += cbThisRead;
1987 cbToRead -= cbThisRead;
1988 if (!cbToRead)
1989 break;
1990 pvBuf = (uint8_t *)pvBuf + cbThisRead;
1991
1992 /*
1993 * Advance to the next extent.
1994 */
1995 offExtent += pCurExtent->cbExtent;
1996 pCurExtent = &pCore->paExtents[iExtent++];
1997 if (iExtent >= pCore->cExtents)
1998 {
1999 memset(pvBuf, 0, cbToRead);
2000 cbActual += cbToRead;
2001 break;
2002 }
2003 }
2004 }
2005 else
2006 Assert(rcRet == VINF_SUCCESS);
2007
2008 if (poffPosMov)
2009 *poffPosMov = cbActual;
2010 if (pcbRead)
2011 *pcbRead = cbActual;
2012 return rcRet;
2013}
2014
2015
2016/**
2017 * Worker for rtFsIsoFile_QueryInfo and rtFsIsoDir_QueryInfo.
2018 */
2019static int rtFsIsoCore_QueryInfo(PRTFSISOCORE pCore, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2020{
2021 pObjInfo->cbObject = pCore->cbObject;
2022 pObjInfo->cbAllocated = RT_ALIGN_64(pCore->cbObject, pCore->pVol->cbBlock);
2023 pObjInfo->AccessTime = pCore->AccessTime;
2024 pObjInfo->ModificationTime = pCore->ModificationTime;
2025 pObjInfo->ChangeTime = pCore->ChangeTime;
2026 pObjInfo->BirthTime = pCore->BirthTime;
2027 pObjInfo->Attr.fMode = pCore->fAttrib;
2028 pObjInfo->Attr.enmAdditional = enmAddAttr;
2029
2030 switch (enmAddAttr)
2031 {
2032 case RTFSOBJATTRADD_NOTHING: RT_FALL_THRU();
2033 case RTFSOBJATTRADD_UNIX:
2034 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
2035 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
2036 pObjInfo->Attr.u.Unix.cHardlinks = 1;
2037 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
2038 pObjInfo->Attr.u.Unix.INodeId = pCore->idINode;
2039 pObjInfo->Attr.u.Unix.fFlags = 0;
2040 pObjInfo->Attr.u.Unix.GenerationId = pCore->uVersion;
2041 pObjInfo->Attr.u.Unix.Device = 0;
2042 break;
2043 case RTFSOBJATTRADD_UNIX_OWNER:
2044 pObjInfo->Attr.u.UnixOwner.uid = 0;
2045 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
2046 break;
2047 case RTFSOBJATTRADD_UNIX_GROUP:
2048 pObjInfo->Attr.u.UnixGroup.gid = 0;
2049 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
2050 break;
2051 case RTFSOBJATTRADD_EASIZE:
2052 pObjInfo->Attr.u.EASize.cb = 0;
2053 break;
2054 default:
2055 return VERR_INVALID_PARAMETER;
2056 }
2057
2058 if ( pCore->fHaveRockInfo
2059 && enmAddAttr != RTFSOBJATTRADD_NOTHING)
2060 {
2061 /** @todo Read the the rock info for this entry. */
2062 }
2063
2064 return VINF_SUCCESS;
2065}
2066
2067
2068/**
2069 * Worker for rtFsIsoFile_Close and rtFsIsoDir_Close that does common work.
2070 *
2071 * @param pCore The common shared structure.
2072 */
2073static void rtFsIsoCore_Destroy(PRTFSISOCORE pCore)
2074{
2075 if (pCore->pParentDir)
2076 rtFsIsoDirShrd_RemoveOpenChild(pCore->pParentDir, pCore);
2077 if (pCore->paExtents)
2078 {
2079 RTMemFree(pCore->paExtents);
2080 pCore->paExtents = NULL;
2081 }
2082}
2083
2084
2085/**
2086 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2087 */
2088static DECLCALLBACK(int) rtFsIsoFile_Close(void *pvThis)
2089{
2090 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2091 LogFlow(("rtFsIsoFile_Close(%p/%p)\n", pThis, pThis->pShared));
2092
2093 PRTFSISOFILESHRD pShared = pThis->pShared;
2094 pThis->pShared = NULL;
2095 if (pShared)
2096 {
2097 if (ASMAtomicDecU32(&pShared->Core.cRefs) == 0)
2098 {
2099 LogFlow(("rtFsIsoFile_Close: Destroying shared structure %p\n", pShared));
2100 rtFsIsoCore_Destroy(&pShared->Core);
2101 RTMemFree(pShared);
2102 }
2103 }
2104 return VINF_SUCCESS;
2105}
2106
2107
2108/**
2109 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2110 */
2111static DECLCALLBACK(int) rtFsIsoFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2112{
2113 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2114 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
2115}
2116
2117
2118/**
2119 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
2120 */
2121static DECLCALLBACK(int) rtFsIsoFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
2122{
2123 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2124 PRTFSISOFILESHRD pShared = pThis->pShared;
2125 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
2126 RT_NOREF(fBlocking);
2127
2128#if 1
2129 /* Apply default offset. */
2130 if (off == -1)
2131 off = pThis->offFile;
2132 else
2133 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
2134
2135 /* Do the read. */
2136 size_t offDelta = 0;
2137 int rc = rtFsIsoCore_ReadWorker(&pShared->Core, off, (uint8_t *)pSgBuf->paSegs[0].pvSeg,
2138 pSgBuf->paSegs[0].cbSeg, pcbRead, &offDelta);
2139
2140 /* Update the file position and return. */
2141 pThis->offFile = off + offDelta;
2142 return rc;
2143#else
2144
2145
2146 /*
2147 * Check for EOF.
2148 */
2149 if (off == -1)
2150 off = pThis->offFile;
2151 if ((uint64_t)off >= pShared->Core.cbObject)
2152 {
2153 if (pcbRead)
2154 {
2155 *pcbRead = 0;
2156 return VINF_EOF;
2157 }
2158 return VERR_EOF;
2159 }
2160
2161 if (pShared->Core.pVol->enmType == RTFSISOVOLTYPE_UDF)
2162 {
2163 return VERR_ISOFS_UDF_NOT_IMPLEMENTED;
2164 }
2165
2166 /*
2167 * Simple case: File has a single extent.
2168 */
2169 int rc = VINF_SUCCESS;
2170 size_t cbRead = 0;
2171 uint64_t cbFileLeft = pShared->Core.cbObject - (uint64_t)off;
2172 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
2173 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
2174 if (pShared->Core.cExtents == 1)
2175 {
2176 if (cbLeft > 0)
2177 {
2178 size_t cbToRead = cbLeft;
2179 if (cbToRead > cbFileLeft)
2180 cbToRead = (size_t)cbFileLeft;
2181 rc = RTVfsFileReadAt(pShared->Core.pVol->hVfsBacking, pShared->Core.FirstExtent.off + off, pbDst, cbToRead, NULL);
2182 if (RT_SUCCESS(rc))
2183 {
2184 off += cbToRead;
2185 pbDst += cbToRead;
2186 cbRead += cbToRead;
2187 cbFileLeft -= cbToRead;
2188 cbLeft -= cbToRead;
2189 }
2190 }
2191 }
2192 /*
2193 * Complicated case: Work the file content extent by extent.
2194 */
2195 else
2196 {
2197 return VERR_NOT_IMPLEMENTED; /** @todo multi-extent stuff . */
2198 }
2199
2200 /* Update the offset and return. */
2201 pThis->offFile = off;
2202 if (pcbRead)
2203 *pcbRead = cbRead;
2204 return VINF_SUCCESS;
2205#endif
2206}
2207
2208
2209/**
2210 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
2211 */
2212static DECLCALLBACK(int) rtFsIsoFile_Flush(void *pvThis)
2213{
2214 RT_NOREF(pvThis);
2215 return VINF_SUCCESS;
2216}
2217
2218
2219/**
2220 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
2221 */
2222static DECLCALLBACK(int) rtFsIsoFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
2223 uint32_t *pfRetEvents)
2224{
2225 NOREF(pvThis);
2226 int rc;
2227 if (fEvents != RTPOLL_EVT_ERROR)
2228 {
2229 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
2230 rc = VINF_SUCCESS;
2231 }
2232 else if (fIntr)
2233 rc = RTThreadSleep(cMillies);
2234 else
2235 {
2236 uint64_t uMsStart = RTTimeMilliTS();
2237 do
2238 rc = RTThreadSleep(cMillies);
2239 while ( rc == VERR_INTERRUPTED
2240 && !fIntr
2241 && RTTimeMilliTS() - uMsStart < cMillies);
2242 if (rc == VERR_INTERRUPTED)
2243 rc = VERR_TIMEOUT;
2244 }
2245 return rc;
2246}
2247
2248
2249/**
2250 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
2251 */
2252static DECLCALLBACK(int) rtFsIsoFile_Tell(void *pvThis, PRTFOFF poffActual)
2253{
2254 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2255 *poffActual = pThis->offFile;
2256 return VINF_SUCCESS;
2257}
2258
2259
2260/**
2261 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
2262 */
2263static DECLCALLBACK(int) rtFsIsoFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
2264{
2265 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2266 RTFOFF offNew;
2267 switch (uMethod)
2268 {
2269 case RTFILE_SEEK_BEGIN:
2270 offNew = offSeek;
2271 break;
2272 case RTFILE_SEEK_END:
2273 offNew = (RTFOFF)pThis->pShared->Core.cbObject + offSeek;
2274 break;
2275 case RTFILE_SEEK_CURRENT:
2276 offNew = (RTFOFF)pThis->offFile + offSeek;
2277 break;
2278 default:
2279 return VERR_INVALID_PARAMETER;
2280 }
2281 if (offNew >= 0)
2282 {
2283 pThis->offFile = offNew;
2284 *poffActual = offNew;
2285 return VINF_SUCCESS;
2286 }
2287 return VERR_NEGATIVE_SEEK;
2288}
2289
2290
2291/**
2292 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
2293 */
2294static DECLCALLBACK(int) rtFsIsoFile_QuerySize(void *pvThis, uint64_t *pcbFile)
2295{
2296 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
2297 *pcbFile = pThis->pShared->Core.cbObject;
2298 return VINF_SUCCESS;
2299}
2300
2301
2302/**
2303 * ISO FS file operations.
2304 */
2305DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIsoFileOps =
2306{
2307 { /* Stream */
2308 { /* Obj */
2309 RTVFSOBJOPS_VERSION,
2310 RTVFSOBJTYPE_FILE,
2311 "FatFile",
2312 rtFsIsoFile_Close,
2313 rtFsIsoFile_QueryInfo,
2314 NULL,
2315 RTVFSOBJOPS_VERSION
2316 },
2317 RTVFSIOSTREAMOPS_VERSION,
2318 RTVFSIOSTREAMOPS_FEAT_NO_SG,
2319 rtFsIsoFile_Read,
2320 NULL /*Write*/,
2321 rtFsIsoFile_Flush,
2322 rtFsIsoFile_PollOne,
2323 rtFsIsoFile_Tell,
2324 NULL /*pfnSkip*/,
2325 NULL /*pfnZeroFill*/,
2326 RTVFSIOSTREAMOPS_VERSION,
2327 },
2328 RTVFSFILEOPS_VERSION,
2329 0,
2330 { /* ObjSet */
2331 RTVFSOBJSETOPS_VERSION,
2332 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
2333 NULL /*SetMode*/,
2334 NULL /*SetTimes*/,
2335 NULL /*SetOwner*/,
2336 RTVFSOBJSETOPS_VERSION
2337 },
2338 rtFsIsoFile_Seek,
2339 rtFsIsoFile_QuerySize,
2340 NULL /*SetSize*/,
2341 NULL /*QueryMaxSize*/,
2342 RTVFSFILEOPS_VERSION
2343};
2344
2345
2346/**
2347 * Instantiates a new file, from ISO 9660 info.
2348 *
2349 * @returns IPRT status code.
2350 * @param pThis The ISO volume instance.
2351 * @param pParentDir The parent directory (shared part).
2352 * @param pDirRec The directory record.
2353 * @param cDirRecs Number of directory records if more than one.
2354 * @param offDirRec The byte offset of the directory record.
2355 * @param offEntryInDir The byte offset of the directory entry in the parent
2356 * directory.
2357 * @param fOpen RTFILE_O_XXX flags.
2358 * @param uVersion The file version number (since the caller already
2359 * parsed the filename, we don't want to repeat the
2360 * effort here).
2361 * @param pRockInfo Optional rock ridge info for the file.
2362 * @param phVfsFile Where to return the file handle.
2363 */
2364static int rtFsIsoFile_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
2365 uint64_t offDirRec, uint64_t fOpen, uint32_t uVersion, PCRTFSISOROCKINFO pRockInfo,
2366 PRTVFSFILE phVfsFile)
2367{
2368 AssertPtr(pParentDir);
2369
2370 /*
2371 * Create a VFS object.
2372 */
2373 PRTFSISOFILEOBJ pNewFile;
2374 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2375 phVfsFile, (void **)&pNewFile);
2376 if (RT_SUCCESS(rc))
2377 {
2378 /*
2379 * Look for existing shared object, create a new one if necessary.
2380 */
2381 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
2382 if (pShared)
2383 {
2384 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2385 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2386 pNewFile->offFile = 0;
2387 pNewFile->pShared = pShared;
2388 return VINF_SUCCESS;
2389 }
2390
2391 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2392 if (pShared)
2393 {
2394 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, uVersion, pRockInfo, pThis);
2395 if (RT_SUCCESS(rc))
2396 {
2397 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2398 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2399 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2400 pNewFile->offFile = 0;
2401 pNewFile->pShared = pShared;
2402 return VINF_SUCCESS;
2403 }
2404 RTMemFree(pShared);
2405 }
2406 else
2407 rc = VERR_NO_MEMORY;
2408
2409 /* Destroy the file object. */
2410 pNewFile->offFile = 0;
2411 pNewFile->pShared = NULL;
2412 RTVfsFileRelease(*phVfsFile);
2413 }
2414 *phVfsFile = NIL_RTVFSFILE;
2415 return rc;
2416}
2417
2418
2419/**
2420 * Instantiates a new file, from UDF info.
2421 *
2422 * @returns IPRT status code.
2423 * @param pThis The ISO volume instance.
2424 * @param pParentDir The parent directory (shared part).
2425 * @param pFid The file ID descriptor. (Points to parent directory
2426 * content.)
2427 * @param fOpen RTFILE_O_XXX flags.
2428 * @param phVfsFile Where to return the file handle.
2429 */
2430static int rtFsIsoFile_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid,
2431 uint64_t fOpen, PRTVFSFILE phVfsFile)
2432{
2433 AssertPtr(pParentDir);
2434 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
2435 Assert(offInDir < pParentDir->cbDir);
2436 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DELETED));
2437 Assert(!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY));
2438
2439 /*
2440 * Create a VFS object.
2441 */
2442 PRTFSISOFILEOBJ pNewFile;
2443 int rc = RTVfsNewFile(&g_rtFsIsoFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
2444 phVfsFile, (void **)&pNewFile);
2445 if (RT_SUCCESS(rc))
2446 {
2447 /*
2448 * Look for existing shared object. Make sure it's a file.
2449 */
2450 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
2451 if (pShared)
2452 {
2453 if (!RTFS_IS_FILE(pShared->Core.fAttrib))
2454 {
2455 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2456 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2457 pNewFile->offFile = 0;
2458 pNewFile->pShared = pShared;
2459 return VINF_SUCCESS;
2460 }
2461 }
2462 /*
2463 * Create a shared object for this alleged file.
2464 */
2465 else
2466 {
2467 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
2468 if (pShared)
2469 {
2470 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, &pFid->Icb, pFid, offInDir, pThis);
2471 if (RT_SUCCESS(rc))
2472 {
2473 if (RTFS_IS_FILE(pShared->Core.fAttrib))
2474 {
2475 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2476
2477 LogFlow(("rtFsIsoFile_NewUdf: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
2478 pShared->Core.cbObject, pShared->Core.FirstExtent.off, pShared->Core.FirstExtent.cbExtent));
2479 pNewFile->offFile = 0;
2480 pNewFile->pShared = pShared;
2481 return VINF_SUCCESS;
2482 }
2483 rtFsIsoCore_Destroy(&pShared->Core);
2484 }
2485 RTMemFree(pShared);
2486 }
2487 else
2488 rc = VERR_NO_MEMORY;
2489 }
2490
2491 /* Destroy the file object. */
2492 pNewFile->offFile = 0;
2493 pNewFile->pShared = NULL;
2494 RTVfsFileRelease(*phVfsFile);
2495 }
2496 *phVfsFile = NIL_RTVFSFILE;
2497 return rc;
2498}
2499
2500
2501/**
2502 * Looks up the shared structure for a child.
2503 *
2504 * @returns Referenced pointer to the shared structure, NULL if not found.
2505 * @param pThis The directory.
2506 * @param offDirRec The directory record offset of the child.
2507 */
2508static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec)
2509{
2510 PRTFSISOCORE pCur;
2511 RTListForEach(&pThis->OpenChildren, pCur, RTFSISOCORE, Entry)
2512 {
2513 if (pCur->offDirRec == offDirRec)
2514 {
2515 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
2516 Assert(cRefs > 1); RT_NOREF(cRefs);
2517 return pCur;
2518 }
2519 }
2520 return NULL;
2521}
2522
2523
2524#ifdef RT_STRICT
2525/**
2526 * Checks if @a pNext is an extent of @a pFirst.
2527 *
2528 * @returns true if @a pNext is the next extent, false if not
2529 * @param pFirst The directory record describing the first or the
2530 * previous extent.
2531 * @param pNext The directory record alleged to be the next extent.
2532 */
2533DECLINLINE(bool) rtFsIsoDir_Is9660DirRecNextExtent(PCISO9660DIRREC pFirst, PCISO9660DIRREC pNext)
2534{
2535 if (RT_LIKELY(pNext->bFileIdLength == pFirst->bFileIdLength))
2536 {
2537 if (RT_LIKELY((pNext->fFileFlags | ISO9660_FILE_FLAGS_MULTI_EXTENT) == pFirst->fFileFlags))
2538 {
2539 if (RT_LIKELY(memcmp(pNext->achFileId, pFirst->achFileId, pNext->bFileIdLength) == 0))
2540 return true;
2541 }
2542 }
2543 return false;
2544}
2545#endif /* RT_STRICT */
2546
2547
2548/**
2549 * Parses rock ridge information if present in the directory entry.
2550 *
2551 * @param pVol The volume structure.
2552 * @param pParseInfo Parse info and output.
2553 * @param pbSys The system area of the directory record.
2554 * @param cbSys The number of bytes present in the sys area.
2555 * @param fIsFirstDirRec Set if this is the '.' directory entry in the
2556 * root directory. (Some entries applies only to
2557 * it.)
2558 * @param fContinuationRecord Set if we're processing a continuation record in
2559 * living in the abRockBuf.
2560 */
2561static void rtFsIsoDirShrd_ParseRockRidgeData(PRTFSISOVOL pVol, PRTFSISOROCKINFO pParseInfo, uint8_t const *pbSys,
2562 size_t cbSys, bool fIsFirstDirRec, bool fContinuationRecord)
2563{
2564 while (cbSys >= 4)
2565 {
2566 /*
2567 * Check header length and advance the sys variables.
2568 */
2569 PCISO9660SUSPUNION pUnion = (PCISO9660SUSPUNION)pbSys;
2570 if ( pUnion->Hdr.cbEntry > cbSys
2571 || pUnion->Hdr.cbEntry < sizeof(pUnion->Hdr))
2572 {
2573 Log4(("rtFsIsoDir_ParseRockRidgeData: cbEntry=%#x cbSys=%#x (%#x %#x)\n",
2574 pUnion->Hdr.cbEntry, cbSys, pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
2575 break;
2576 }
2577 pbSys += pUnion->Hdr.cbEntry;
2578 cbSys -= pUnion->Hdr.cbEntry;
2579
2580 /*
2581 * Process fields.
2582 */
2583 uint16_t const uSig = SUSP_MAKE_SIG(pUnion->Hdr.bSig1, pUnion->Hdr.bSig2);
2584 switch (uSig)
2585 {
2586 /*
2587 * System use sharing protocol entries.
2588 */
2589 case SUSP_MAKE_SIG(ISO9660SUSPCE_SIG1, ISO9660SUSPCE_SIG2):
2590 {
2591 if (RT_BE2H_U32(pUnion->CE.offBlock.be) != RT_LE2H_U32(pUnion->CE.offBlock.le))
2592 Log4(("rtFsIsoDir_ParseRockRidgeData: Invalid CE offBlock field: be=%#x vs le=%#x\n",
2593 RT_BE2H_U32(pUnion->CE.offBlock.be), RT_LE2H_U32(pUnion->CE.offBlock.le)));
2594 else if (RT_BE2H_U32(pUnion->CE.cbData.be) != RT_LE2H_U32(pUnion->CE.cbData.le))
2595 Log4(("rtFsIsoDir_ParseRockRidgeData: Invalid CE cbData field: be=%#x vs le=%#x\n",
2596 RT_BE2H_U32(pUnion->CE.cbData.be), RT_LE2H_U32(pUnion->CE.cbData.le)));
2597 else if (RT_BE2H_U32(pUnion->CE.offData.be) != RT_LE2H_U32(pUnion->CE.offData.le))
2598 Log4(("rtFsIsoDir_ParseRockRidgeData: Invalid CE offData field: be=%#x vs le=%#x\n",
2599 RT_BE2H_U32(pUnion->CE.offData.be), RT_LE2H_U32(pUnion->CE.offData.le)));
2600 else if (!fContinuationRecord)
2601 {
2602 uint64_t offData = ISO9660_GET_ENDIAN(&pUnion->CE.offBlock) * (uint64_t)ISO9660_SECTOR_SIZE;
2603 offData += ISO9660_GET_ENDIAN(&pUnion->CE.offData);
2604 uint32_t cbData = ISO9660_GET_ENDIAN(&pUnion->CE.cbData);
2605 if (cbData <= sizeof(pVol->abRockBuf) - (uint32_t)(offData & ISO9660_SECTOR_OFFSET_MASK))
2606 {
2607 RTCritSectEnter(&pVol->RockBufLock);
2608
2609 AssertCompile(sizeof(pVol->abRockBuf) == ISO9660_SECTOR_SIZE);
2610 uint64_t offDataBlock = offData & ~(uint64_t)ISO9660_SECTOR_OFFSET_MASK;
2611 if (pVol->offRockBuf == offDataBlock)
2612 rtFsIsoDirShrd_ParseRockRidgeData(pVol, pParseInfo,
2613 &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
2614 cbData, fIsFirstDirRec, true /*fContinuationRecord*/);
2615 else
2616 {
2617 int rc = RTVfsFileReadAt(pVol->hVfsBacking, offDataBlock,
2618 pVol->abRockBuf, sizeof(pVol->abRockBuf), NULL);
2619 if (RT_SUCCESS(rc))
2620 rtFsIsoDirShrd_ParseRockRidgeData(pVol, pParseInfo,
2621 &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
2622 cbData, fIsFirstDirRec, true /*fContinuationRecord*/);
2623 else
2624 Log4(("rtFsIsoDir_ParseRockRidgeData: Error reading continuation record at %#RX64: %Rrc\n",
2625 offDataBlock, rc));
2626 }
2627
2628 RTCritSectLeave(&pVol->RockBufLock);
2629 }
2630 else
2631 Log4(("rtFsIsoDir_ParseRockRidgeData: continuation record isn't within a sector! offData=%#RX64 cbData=%#RX32\n",
2632 cbData, offData));
2633 }
2634 else
2635 Log4(("rtFsIsoDir_ParseRockRidgeData: nested continuation record!\n"));
2636 break;
2637 }
2638
2639 case SUSP_MAKE_SIG(ISO9660SUSPSP_SIG1, ISO9660SUSPSP_SIG2): /* SP */
2640 if ( pUnion->Hdr.cbEntry != ISO9660SUSPSP_LEN
2641 || pUnion->Hdr.bVersion != ISO9660SUSPSP_VER
2642 || pUnion->SP.bCheck1 != ISO9660SUSPSP_CHECK1
2643 || pUnion->SP.bCheck2 != ISO9660SUSPSP_CHECK2
2644 || pUnion->SP.cbSkip > UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
2645 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",
2646 pUnion->Hdr.cbEntry, ISO9660SUSPSP_LEN, pUnion->Hdr.bVersion, ISO9660SUSPSP_VER,
2647 pUnion->SP.bCheck1, ISO9660SUSPSP_CHECK1, pUnion->SP.bCheck2, ISO9660SUSPSP_CHECK2,
2648 pUnion->SP.cbSkip, UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]) ));
2649 else if (!fIsFirstDirRec)
2650 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorining 'SP' entry in non-root directory record\n"));
2651 else if (pParseInfo->fSuspSeenSP)
2652 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorining additional 'SP' entry\n"));
2653 else
2654 {
2655 pVol->offSuspSkip = pUnion->SP.cbSkip;
2656 if (pUnion->SP.cbSkip != 0)
2657 Log4(("rtFsIsoDir_ParseRockRidgeData: SP: cbSkip=%#x\n", pUnion->SP.cbSkip));
2658 }
2659 break;
2660
2661 case SUSP_MAKE_SIG(ISO9660SUSPER_SIG1, ISO9660SUSPER_SIG2): /* ER */
2662 if ( pUnion->Hdr.cbEntry > RT_UOFFSETOF(ISO9660SUSPER, achPayload) + (uint32_t)pUnion->ER.cchIdentifier
2663 + (uint32_t)pUnion->ER.cchDescription + (uint32_t)pUnion->ER.cchSource
2664 || pUnion->Hdr.bVersion != ISO9660SUSPER_VER)
2665 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'ER' entry: cbEntry=%#x bVersion=%#x (vs %#x) cchIdentifier=%#x cchDescription=%#x cchSource=%#x\n",
2666 pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion, ISO9660SUSPER_VER, pUnion->ER.cchIdentifier,
2667 pUnion->ER.cchDescription, pUnion->ER.cchSource));
2668 else if (!fIsFirstDirRec)
2669 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorining 'ER' entry in non-root directory record\n"));
2670 else if ( pUnion->ER.bVersion == 1 /* RRIP detection */
2671 && ( (pUnion->ER.cchIdentifier >= 4 && strncmp(pUnion->ER.achPayload, ISO9660_RRIP_ID, 4 /*RRIP*/) == 0)
2672 || (pUnion->ER.cchIdentifier >= 10 && strncmp(pUnion->ER.achPayload, RT_STR_TUPLE(ISO9660_RRIP_1_12_ID)) == 0) ))
2673 {
2674 Log4(("rtFsIsoDir_ParseRockRidgeData: Rock Ridge 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
2675 pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
2676 pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
2677 pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
2678 pVol->fHaveRock = true;
2679 pParseInfo->cRockEntries++;
2680 }
2681 else
2682 Log4(("rtFsIsoDir_ParseRockRidgeData: Unknown extension in 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
2683 pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
2684 pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
2685 pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
2686 break;
2687
2688 case SUSP_MAKE_SIG(ISO9660SUSPPD_SIG1, ISO9660SUSPPD_SIG2): /* PD - ignored */
2689 case SUSP_MAKE_SIG(ISO9660SUSPST_SIG1, ISO9660SUSPST_SIG2): /* ST - ignore for now */
2690 case SUSP_MAKE_SIG(ISO9660SUSPES_SIG1, ISO9660SUSPES_SIG2): /* ES - ignore for now */
2691 break;
2692
2693 /*
2694 * Rock ridge interchange protocol entries.
2695 */
2696 case SUSP_MAKE_SIG(ISO9660RRIPRR_SIG1, ISO9660RRIPRR_SIG2): /* RR */
2697 if ( pUnion->RR.Hdr.cbEntry != ISO9660RRIPRR_LEN
2698 || pUnion->RR.Hdr.bVersion != ISO9660RRIPRR_VER)
2699 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'RR' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
2700 pUnion->RR.Hdr.cbEntry, ISO9660RRIPRR_LEN, pUnion->RR.Hdr.bVersion, ISO9660RRIPRR_VER, pUnion->RR.fFlags));
2701 else
2702 pParseInfo->cRockEntries++; /* otherwise ignored */
2703 break;
2704
2705 case SUSP_MAKE_SIG(ISO9660RRIPPX_SIG1, ISO9660RRIPPX_SIG2): /* PX */
2706 if ( ( pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN
2707 && pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN_NO_INODE)
2708 || pUnion->PX.Hdr.bVersion != ISO9660RRIPPX_VER
2709 || RT_BE2H_U32(pUnion->PX.fMode.be) != RT_LE2H_U32(pUnion->PX.fMode.le)
2710 || RT_BE2H_U32(pUnion->PX.cHardlinks.be) != RT_LE2H_U32(pUnion->PX.cHardlinks.le)
2711 || RT_BE2H_U32(pUnion->PX.uid.be) != RT_LE2H_U32(pUnion->PX.uid.le)
2712 || RT_BE2H_U32(pUnion->PX.gid.be) != RT_LE2H_U32(pUnion->PX.gid.le)
2713 || ( pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN
2714 && RT_BE2H_U32(pUnion->PX.INode.be) != RT_LE2H_U32(pUnion->PX.INode.le)) )
2715 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",
2716 pUnion->PX.Hdr.cbEntry, ISO9660RRIPPX_LEN, ISO9660RRIPPX_LEN_NO_INODE,
2717 pUnion->PX.Hdr.bVersion, ISO9660RRIPPX_VER,
2718 RT_BE2H_U32(pUnion->PX.fMode.be), RT_LE2H_U32(pUnion->PX.fMode.le),
2719 RT_BE2H_U32(pUnion->PX.cHardlinks.be), RT_LE2H_U32(pUnion->PX.cHardlinks.le),
2720 RT_BE2H_U32(pUnion->PX.uid.be), RT_LE2H_U32(pUnion->PX.uid.le),
2721 RT_BE2H_U32(pUnion->PX.gid.be), RT_LE2H_U32(pUnion->PX.gid.le),
2722 pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_BE2H_U32(pUnion->PX.INode.be) : 0,
2723 pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_LE2H_U32(pUnion->PX.INode.le) : 0 ));
2724 else
2725 {
2726 if ( RTFS_IS_DIRECTORY(ISO9660_GET_ENDIAN(&pUnion->PX.fMode))
2727 == RTFS_IS_DIRECTORY(pParseInfo->Info.Attr.fMode))
2728 pParseInfo->Info.Attr.fMode = ISO9660_GET_ENDIAN(&pUnion->PX.fMode);
2729 else
2730 Log4(("rtFsIsoDir_ParseRockRidgeData: 'PX' entry changes directory-ness: fMode=%#x, existing %#x; ignored\n",
2731 ISO9660_GET_ENDIAN(&pUnion->PX.fMode), pParseInfo->Info.Attr.fMode));
2732 pParseInfo->Info.Attr.u.Unix.cHardlinks = ISO9660_GET_ENDIAN(&pUnion->PX.cHardlinks);
2733 pParseInfo->Info.Attr.u.Unix.uid = ISO9660_GET_ENDIAN(&pUnion->PX.uid);
2734 pParseInfo->Info.Attr.u.Unix.gid = ISO9660_GET_ENDIAN(&pUnion->PX.gid);
2735 /* ignore inode */
2736 pParseInfo->cRockEntries++;
2737 }
2738 break;
2739
2740 case SUSP_MAKE_SIG(ISO9660RRIPPN_SIG1, ISO9660RRIPPN_SIG2): /* PN */
2741 if ( pUnion->PN.Hdr.cbEntry != ISO9660RRIPPN_LEN
2742 || pUnion->PN.Hdr.bVersion != ISO9660RRIPPN_VER
2743 || RT_BE2H_U32(pUnion->PN.Major.be) != RT_LE2H_U32(pUnion->PN.Major.le)
2744 || RT_BE2H_U32(pUnion->PN.Minor.be) != RT_LE2H_U32(pUnion->PN.Minor.le))
2745 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'PN' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) Major=%#x/%#x Minor=%#x/%#x\n",
2746 pUnion->PN.Hdr.cbEntry, ISO9660RRIPPN_LEN, pUnion->PN.Hdr.bVersion, ISO9660RRIPPN_VER,
2747 RT_BE2H_U32(pUnion->PN.Major.be), RT_LE2H_U32(pUnion->PN.Major.le),
2748 RT_BE2H_U32(pUnion->PN.Minor.be), RT_LE2H_U32(pUnion->PN.Minor.le) ));
2749 else if (RTFS_IS_DIRECTORY(pParseInfo->Info.Attr.fMode))
2750 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorning 'PN' entry for directory (%#x/%#x)\n",
2751 ISO9660_GET_ENDIAN(&pUnion->PN.Major), ISO9660_GET_ENDIAN(&pUnion->PN.Minor) ));
2752 else
2753 {
2754 pParseInfo->Info.Attr.u.Unix.Device = RTDEV_MAKE(ISO9660_GET_ENDIAN(&pUnion->PN.Major),
2755 ISO9660_GET_ENDIAN(&pUnion->PN.Minor));
2756 pParseInfo->cRockEntries++;
2757 }
2758 break;
2759
2760 case SUSP_MAKE_SIG(ISO9660RRIPTF_SIG1, ISO9660RRIPTF_SIG2): /* TF */
2761 if ( pUnion->TF.Hdr.bVersion != ISO9660RRIPTF_VER
2762 || pUnion->TF.Hdr.cbEntry < Iso9660RripTfCalcLength(pUnion->TF.fFlags))
2763 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'TF' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
2764 pUnion->TF.Hdr.cbEntry, Iso9660RripTfCalcLength(pUnion->TF.fFlags),
2765 pUnion->TF.Hdr.bVersion, ISO9660RRIPTF_VER, RT_BE2H_U32(pUnion->TF.fFlags) ));
2766 else if (!(pUnion->TF.fFlags & ISO9660RRIPTF_F_LONG_FORM))
2767 {
2768 PCISO9660RECTIMESTAMP pTimestamp = (PCISO9660RECTIMESTAMP)&pUnion->TF.abPayload[0];
2769 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
2770 {
2771 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.BirthTime, pTimestamp);
2772 pTimestamp++;
2773 }
2774 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
2775 {
2776 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.ModificationTime, pTimestamp);
2777 pTimestamp++;
2778 }
2779 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
2780 {
2781 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.AccessTime, pTimestamp);
2782 pTimestamp++;
2783 }
2784 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
2785 {
2786 rtFsIso9660DateTime2TimeSpec(&pParseInfo->Info.ChangeTime, pTimestamp);
2787 pTimestamp++;
2788 }
2789 pParseInfo->cRockEntries++;
2790 }
2791 else
2792 {
2793 PCISO9660TIMESTAMP pTimestamp = (PCISO9660TIMESTAMP)&pUnion->TF.abPayload[0];
2794 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
2795 {
2796 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.BirthTime, pTimestamp);
2797 pTimestamp++;
2798 }
2799 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
2800 {
2801 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.ModificationTime, pTimestamp);
2802 pTimestamp++;
2803 }
2804 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
2805 {
2806 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.AccessTime, pTimestamp);
2807 pTimestamp++;
2808 }
2809 if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
2810 {
2811 rtFsIso9660DateTime2TimeSpecIfValid(&pParseInfo->Info.ChangeTime, pTimestamp);
2812 pTimestamp++;
2813 }
2814 pParseInfo->cRockEntries++;
2815 }
2816 break;
2817
2818 case SUSP_MAKE_SIG(ISO9660RRIPSF_SIG1, ISO9660RRIPSF_SIG2): /* SF */
2819 Log4(("rtFsIsoDir_ParseRockRidgeData: Sparse file support not yet implemented!\n"));
2820 break;
2821
2822 case SUSP_MAKE_SIG(ISO9660RRIPSL_SIG1, ISO9660RRIPSL_SIG2): /* SL */
2823 if ( pUnion->SL.Hdr.bVersion != ISO9660RRIPSL_VER
2824 || pUnion->SL.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2])
2825 || (pUnion->SL.fFlags & ~ISO9660RRIP_SL_F_CONTINUE)
2826 || (pUnion->SL.abComponents[0] & ISO9660RRIP_SL_C_RESERVED_MASK) )
2827 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x comp[0].fFlags=%#x\n",
2828 pUnion->SL.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2]),
2829 pUnion->SL.Hdr.bVersion, ISO9660RRIPSL_VER, pUnion->SL.fFlags, pUnion->SL.abComponents[0]));
2830 else if (pParseInfo->fSeenLastSL)
2831 Log4(("rtFsIsoDir_ParseRockRidgeData: Unexpected 'SL!' entry\n"));
2832 else
2833 {
2834 pParseInfo->cRockEntries++;
2835 pParseInfo->fSeenLastSL = !(pUnion->SL.fFlags & ISO9660RRIP_SL_F_CONTINUE); /* used in loop */
2836
2837 size_t offDst = pParseInfo->cchLinkTarget;
2838 uint8_t const *pbSrc = &pUnion->SL.abComponents[0];
2839 uint8_t cbSrcLeft = pUnion->SL.Hdr.cbEntry - RT_UOFFSETOF(ISO9660RRIPSL, abComponents);
2840 while (cbSrcLeft >= 2)
2841 {
2842 uint8_t const fFlags = pbSrc[0];
2843 uint8_t cchCopy = pbSrc[1];
2844 uint8_t const cbSkip = cchCopy + 2;
2845 if (cbSkip > cbSrcLeft)
2846 {
2847 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: component flags=%#x, component length+2=%#x vs %#x left\n",
2848 fFlags, cbSkip, cbSrcLeft));
2849 break;
2850 }
2851
2852 const char *pszCopy;
2853 switch (fFlags & ~ISO9660RRIP_SL_C_CONTINUE)
2854 {
2855 case 0:
2856 pszCopy = (const char *)&pbSrc[2];
2857 break;
2858
2859 case ISO9660RRIP_SL_C_CURRENT:
2860 if (cchCopy != 0)
2861 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: CURRENT + %u bytes, ignoring bytes\n", cchCopy));
2862 pszCopy = ".";
2863 cchCopy = 1;
2864 break;
2865
2866 case ISO9660RRIP_SL_C_PARENT:
2867 if (cchCopy != 0)
2868 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: PARENT + %u bytes, ignoring bytes\n", cchCopy));
2869 pszCopy = "..";
2870 cchCopy = 2;
2871 break;
2872
2873 case ISO9660RRIP_SL_C_ROOT:
2874 if (cchCopy != 0)
2875 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: ROOT + %u bytes, ignoring bytes\n", cchCopy));
2876 pszCopy = "/";
2877 cchCopy = 1;
2878 break;
2879
2880 default:
2881 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'SL' component: component flags=%#x (bad), component length=%#x vs %#x left\n",
2882 fFlags, cchCopy, cbSrcLeft));
2883 pszCopy = NULL;
2884 cchCopy = 0;
2885 break;
2886 }
2887
2888 if (offDst + cchCopy < sizeof(pParseInfo->szLinkTarget))
2889 {
2890 memcpy(&pParseInfo->szLinkTarget[offDst], pszCopy, cchCopy);
2891 offDst += cchCopy;
2892 }
2893 else
2894 {
2895 Log4(("rtFsIsoDir_ParseRockRidgeData: 'SL' constructs a too long target! '%.*s%.*s'\n",
2896 offDst, pParseInfo->szLinkTarget, cchCopy, pszCopy));
2897 memcpy(&pParseInfo->szLinkTarget[offDst], pszCopy, sizeof(pParseInfo->szLinkTarget) - offDst - 1);
2898 offDst = sizeof(pParseInfo->szLinkTarget) - 1;
2899 pParseInfo->fOverflowSL = true;
2900 break;
2901 }
2902
2903 /* Advance */
2904 pbSrc += cbSkip;
2905 cbSrcLeft -= cbSkip;
2906
2907 /* Append slash if appropriate. */
2908 if ( !(fFlags & ISO9660RRIP_SL_C_CONTINUE)
2909 && (cbSrcLeft >= 2 || !pParseInfo->fSeenLastSL) )
2910 {
2911 if (offDst + 1 < sizeof(pParseInfo->szLinkTarget))
2912 pParseInfo->szLinkTarget[offDst++] = '/';
2913 else
2914 {
2915 Log4(("rtFsIsoDir_ParseRockRidgeData: 'SL' constructs a too long target! '%.*s/'\n",
2916 offDst, pParseInfo->szLinkTarget));
2917 pParseInfo->fOverflowSL = true;
2918 break;
2919 }
2920 }
2921 }
2922 Assert(offDst < sizeof(pParseInfo->szLinkTarget));
2923 pParseInfo->szLinkTarget[offDst] = '\0';
2924 pParseInfo->cchLinkTarget = (uint16_t)offDst;
2925 }
2926 break;
2927
2928 case SUSP_MAKE_SIG(ISO9660RRIPNM_SIG1, ISO9660RRIPNM_SIG2): /* NM */
2929 if ( pUnion->NM.Hdr.bVersion != ISO9660RRIPNM_VER
2930 || pUnion->NM.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPNM, achName)
2931 || (pUnion->NM.fFlags & ISO9660RRIP_NM_F_RESERVED_MASK) )
2932 Log4(("rtFsIsoDir_ParseRockRidgeData: Malformed 'NM' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x %.*Rhxs\n",
2933 pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName),
2934 pUnion->NM.Hdr.bVersion, ISO9660RRIPNM_VER, pUnion->NM.fFlags,
2935 pUnion->NM.Hdr.cbEntry - RT_MIN(pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName)),
2936 &pUnion->NM.achName[0] ));
2937 else if (pParseInfo->fSeenLastNM)
2938 Log4(("rtFsIsoDir_ParseRockRidgeData: Unexpected 'NM' entry!\n"));
2939 else
2940 {
2941 pParseInfo->cRockEntries++;
2942 pParseInfo->fSeenLastNM = !(pUnion->NM.fFlags & ISO9660RRIP_NM_F_CONTINUE);
2943
2944 uint8_t const cchName = pUnion->NM.Hdr.cbEntry - (uint8_t)RT_UOFFSETOF(ISO9660RRIPNM, achName);
2945 if (pUnion->NM.fFlags & (ISO9660RRIP_NM_F_CURRENT | ISO9660RRIP_NM_F_PARENT))
2946 {
2947 if (cchName == 0 && pParseInfo->szName[0] == '\0')
2948 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignoring 'NM' entry for '.' and '..'\n"));
2949 else
2950 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignoring malformed 'NM' using '.' or '..': fFlags=%#x cchName=%#x %.*Rhxs; szRockNameBuf='%s'\n",
2951 pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName, pParseInfo->szName));
2952 pParseInfo->szName[0] = '\0';
2953 pParseInfo->cchName = 0;
2954 pParseInfo->fSeenLastNM = true;
2955 }
2956 else
2957 {
2958 size_t offDst = pParseInfo->cchName;
2959 if (offDst + cchName < sizeof(pParseInfo->szName))
2960 {
2961 memcpy(&pParseInfo->szName[offDst], pUnion->NM.achName, cchName);
2962 offDst += cchName;
2963 pParseInfo->szName[offDst] = '\0';
2964 pParseInfo->cchName = (uint16_t)offDst;
2965 }
2966 else
2967 {
2968 Log4(("rtFsIsoDir_ParseRockRidgeData: 'NM' constructs a too long name, ignoring it all: '%s%.*s'\n",
2969 pParseInfo->szName, cchName, pUnion->NM.achName));
2970 pParseInfo->szName[0] = '\0';
2971 pParseInfo->cchName = 0;
2972 pParseInfo->fSeenLastNM = true;
2973 }
2974 }
2975 }
2976 break;
2977
2978 case SUSP_MAKE_SIG(ISO9660RRIPCL_SIG1, ISO9660RRIPCL_SIG2): /* CL - just warn for now. */
2979 case SUSP_MAKE_SIG(ISO9660RRIPPL_SIG1, ISO9660RRIPPL_SIG2): /* PL - just warn for now. */
2980 case SUSP_MAKE_SIG(ISO9660RRIPRE_SIG1, ISO9660RRIPRE_SIG2): /* RE - just warn for now. */
2981 Log4(("rtFsIsoDir_ParseRockRidgeData: Ignorning directory relocation entry '%c%c'!\n", pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
2982 break;
2983
2984 default:
2985 Log4(("rtFsIsoDir_ParseRockRidgeData: Unknown SUSP entry: %#x %#x, %#x bytes, v%u\n",
2986 pUnion->Hdr.bSig1, pUnion->Hdr.bSig2, pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion));
2987 break;
2988 }
2989 }
2990
2991 /*
2992 * Set the valid flag if we found anything of interest.
2993 */
2994 if (pParseInfo->cRockEntries > 1)
2995 pParseInfo->fValid = true;
2996}
2997
2998
2999/**
3000 * Initializes the rock info structure with info from the standard ISO-9660
3001 * directory record.
3002 *
3003 * @param pRockInfo The structure to initialize.
3004 * @param pDirRec The directory record to take basic data from.
3005 */
3006static void rtFsIsoDirShrd_InitRockInfo(PRTFSISOROCKINFO pRockInfo, PCISO9660DIRREC pDirRec)
3007{
3008 pRockInfo->fValid = false;
3009 pRockInfo->fSuspSeenSP = false;
3010 pRockInfo->fSeenLastNM = false;
3011 pRockInfo->fSeenLastSL = false;
3012 pRockInfo->fOverflowSL = false;
3013 pRockInfo->cRockEntries = 0;
3014 pRockInfo->cchName = 0;
3015 pRockInfo->cchLinkTarget = 0;
3016 pRockInfo->szName[0] = '\0';
3017 pRockInfo->szName[sizeof(pRockInfo->szName) - 1] = '\0';
3018 pRockInfo->szLinkTarget[0] = '\0';
3019 pRockInfo->szLinkTarget[sizeof(pRockInfo->szLinkTarget) - 1] = '\0';
3020 pRockInfo->Info.cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
3021 pRockInfo->Info.cbAllocated = pRockInfo->Info.cbObject;
3022 rtFsIso9660DateTime2TimeSpec(&pRockInfo->Info.AccessTime, &pDirRec->RecTime);
3023 pRockInfo->Info.ModificationTime = pRockInfo->Info.AccessTime;
3024 pRockInfo->Info.ChangeTime = pRockInfo->Info.AccessTime;
3025 pRockInfo->Info.BirthTime = pRockInfo->Info.AccessTime;
3026 pRockInfo->Info.Attr.fMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
3027 ? RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | 0555
3028 : RTFS_TYPE_FILE | RTFS_DOS_ARCHIVED | 0444;
3029 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
3030 pRockInfo->Info.Attr.fMode |= RTFS_DOS_HIDDEN;
3031 pRockInfo->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
3032 pRockInfo->Info.Attr.u.Unix.uid = NIL_RTUID;
3033 pRockInfo->Info.Attr.u.Unix.gid = NIL_RTGID;
3034 pRockInfo->Info.Attr.u.Unix.cHardlinks = 1;
3035 pRockInfo->Info.Attr.u.Unix.INodeIdDevice = 0;
3036 pRockInfo->Info.Attr.u.Unix.INodeId = 0;
3037 pRockInfo->Info.Attr.u.Unix.fFlags = 0;
3038 pRockInfo->Info.Attr.u.Unix.GenerationId = 0;
3039 pRockInfo->Info.Attr.u.Unix.Device = 0;
3040}
3041
3042
3043static void rtFsIsoDirShrd_ParseRockForDirRec(PRTFSISODIRSHRD pThis, PCISO9660DIRREC pDirRec, PRTFSISOROCKINFO pRockInfo)
3044{
3045 rtFsIsoDirShrd_InitRockInfo(pRockInfo, pDirRec); /* Always! */
3046
3047 PRTFSISOVOL const pVol = pThis->Core.pVol;
3048 uint8_t cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
3049 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
3050 uint8_t const *pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
3051 if (cbSys >= 4 + pVol->offSuspSkip)
3052 {
3053 pbSys += pVol->offSuspSkip;
3054 cbSys -= pVol->offSuspSkip;
3055 rtFsIsoDirShrd_ParseRockRidgeData(pVol, pRockInfo, pbSys, cbSys,
3056 false /*fIsFirstDirRec*/, false /*fContinuationRecord*/);
3057 }
3058}
3059
3060
3061static void rtFsIsoDirShrd_ParseRockForRoot(PRTFSISODIRSHRD pThis, PCISO9660DIRREC pDirRec)
3062{
3063 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
3064 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
3065 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
3066 if (cbSys >= 4)
3067 {
3068 RTFSISOROCKINFO RockInfo;
3069 rtFsIsoDirShrd_InitRockInfo(&RockInfo, pDirRec);
3070 rtFsIsoDirShrd_ParseRockRidgeData(pThis->Core.pVol, &RockInfo, pbSys, cbSys,
3071 true /*fIsFirstDirRec*/, false /*fContinuationRecord*/);
3072 if (RockInfo.fValid)
3073 {
3074 pThis->Core.fHaveRockInfo = true;
3075 pThis->Core.BirthTime = RockInfo.Info.BirthTime;
3076 pThis->Core.ChangeTime = RockInfo.Info.ChangeTime;
3077 pThis->Core.AccessTime = RockInfo.Info.AccessTime;
3078 pThis->Core.ModificationTime = RockInfo.Info.ModificationTime;
3079 if (RTFS_IS_DIRECTORY(RockInfo.Info.Attr.fMode))
3080 pThis->Core.fAttrib = RockInfo.Info.Attr.fMode;
3081 }
3082 }
3083}
3084
3085
3086/**
3087 * Compares rock ridge information if present in the directory entry.
3088 *
3089 * @param pThis The shared directory structure.
3090 * @param pbSys The system area of the directory record.
3091 * @param cbSys The number of bytes present in the sys area.
3092 * @param pNameCmp The name comparsion data.
3093 * @param fContinuationRecord Set if we're processing a continuation record in
3094 * living in the abRockBuf.
3095 */
3096static int rtFsIsoDirShrd_CompareRockRidgeName(PRTFSISODIRSHRD pThis, uint8_t const *pbSys, size_t cbSys,
3097 PRTFSISOROCKNAMECOMP pNameCmp, bool fContinuationRecord)
3098{
3099 PRTFSISOVOL const pVol = pThis->Core.pVol;
3100
3101 /*
3102 * Do skipping if specified.
3103 */
3104 if (pVol->offSuspSkip)
3105 {
3106 if (cbSys <= pVol->offSuspSkip)
3107 return fContinuationRecord ? VERR_MORE_DATA : VERR_MISMATCH;
3108 pbSys += pVol->offSuspSkip;
3109 cbSys -= pVol->offSuspSkip;
3110 }
3111
3112 while (cbSys >= 4)
3113 {
3114 /*
3115 * Check header length and advance the sys variables.
3116 */
3117 PCISO9660SUSPUNION pUnion = (PCISO9660SUSPUNION)pbSys;
3118 if ( pUnion->Hdr.cbEntry > cbSys
3119 && pUnion->Hdr.cbEntry < sizeof(pUnion->Hdr))
3120 {
3121 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: cbEntry=%#x cbSys=%#x (%#x %#x)\n",
3122 pUnion->Hdr.cbEntry, cbSys, pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
3123 break;
3124 }
3125 pbSys += pUnion->Hdr.cbEntry;
3126 cbSys -= pUnion->Hdr.cbEntry;
3127
3128 /*
3129 * Process the fields we need, nothing else.
3130 */
3131 uint16_t const uSig = SUSP_MAKE_SIG(pUnion->Hdr.bSig1, pUnion->Hdr.bSig2);
3132
3133
3134 /*
3135 * CE - continuation entry
3136 */
3137 if (uSig == SUSP_MAKE_SIG(ISO9660SUSPCE_SIG1, ISO9660SUSPCE_SIG2))
3138 {
3139 if (RT_BE2H_U32(pUnion->CE.offBlock.be) != RT_LE2H_U32(pUnion->CE.offBlock.le))
3140 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Invalid CE offBlock field: be=%#x vs le=%#x\n",
3141 RT_BE2H_U32(pUnion->CE.offBlock.be), RT_LE2H_U32(pUnion->CE.offBlock.le)));
3142 else if (RT_BE2H_U32(pUnion->CE.cbData.be) != RT_LE2H_U32(pUnion->CE.cbData.le))
3143 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Invalid CE cbData field: be=%#x vs le=%#x\n",
3144 RT_BE2H_U32(pUnion->CE.cbData.be), RT_LE2H_U32(pUnion->CE.cbData.le)));
3145 else if (RT_BE2H_U32(pUnion->CE.offData.be) != RT_LE2H_U32(pUnion->CE.offData.le))
3146 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Invalid CE offData field: be=%#x vs le=%#x\n",
3147 RT_BE2H_U32(pUnion->CE.offData.be), RT_LE2H_U32(pUnion->CE.offData.le)));
3148 else if (!fContinuationRecord)
3149 {
3150 uint64_t offData = ISO9660_GET_ENDIAN(&pUnion->CE.offBlock) * (uint64_t)ISO9660_SECTOR_SIZE;
3151 offData += ISO9660_GET_ENDIAN(&pUnion->CE.offData);
3152 uint32_t cbData = ISO9660_GET_ENDIAN(&pUnion->CE.cbData);
3153 if (cbData <= sizeof(pVol->abRockBuf) - (uint32_t)(offData & ISO9660_SECTOR_OFFSET_MASK))
3154 {
3155 RTCritSectEnter(&pVol->RockBufLock);
3156
3157 AssertCompile(sizeof(pVol->abRockBuf) == ISO9660_SECTOR_SIZE);
3158 uint64_t offDataBlock = offData & ~(uint64_t)ISO9660_SECTOR_OFFSET_MASK;
3159 int rc;
3160 if (pVol->offRockBuf == offDataBlock)
3161 rc = rtFsIsoDirShrd_CompareRockRidgeName(pThis, &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
3162 cbData, pNameCmp, true /*fContinuationRecord*/);
3163 else
3164 {
3165 rc = RTVfsFileReadAt(pVol->hVfsBacking, offDataBlock, pVol->abRockBuf, sizeof(pVol->abRockBuf), NULL);
3166 if (RT_SUCCESS(rc))
3167 rc = rtFsIsoDirShrd_CompareRockRidgeName(pThis, &pVol->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
3168 cbData, pNameCmp, true /*fContinuationRecord*/);
3169 else
3170 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Error reading continuation record at %#RX64: %Rrc\n",
3171 offDataBlock, rc));
3172 }
3173
3174 RTCritSectLeave(&pVol->RockBufLock);
3175 if (rc != VERR_MORE_DATA)
3176 return rc;
3177 }
3178 else
3179 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: continuation record isn't within a sector! offData=%#RX64 cbData=%#RX32\n",
3180 cbData, offData));
3181 }
3182 else
3183 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: nested continuation record!\n"));
3184 }
3185 /*
3186 * NM - Name entry.
3187 *
3188 * The character set is supposed to be limited to the portable filename
3189 * character set defined in section 2.2.2.60 of POSIX.1: A-Za-z0-9._-
3190 * If there are any other characters used, we consider them as UTF-8
3191 * for reasons of simplicitiy, however we do not make any effort dealing
3192 * with codepoint encodings across NM records for now because it is
3193 * probably a complete waste of time.
3194 */
3195 else if (uSig == SUSP_MAKE_SIG(ISO9660RRIPNM_SIG1, ISO9660RRIPNM_SIG2))
3196 {
3197 if ( pUnion->NM.Hdr.bVersion != ISO9660RRIPNM_VER
3198 || pUnion->NM.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPNM, achName)
3199 || (pUnion->NM.fFlags & ISO9660RRIP_NM_F_RESERVED_MASK) )
3200 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Malformed 'NM' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x %.*Rhxs\n",
3201 pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName),
3202 pUnion->NM.Hdr.bVersion, ISO9660RRIPNM_VER, pUnion->NM.fFlags,
3203 pUnion->NM.Hdr.cbEntry - RT_MIN(pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName)),
3204 &pUnion->NM.achName[0] ));
3205 else
3206 {
3207 uint8_t const cchName = pUnion->NM.Hdr.cbEntry - (uint8_t)RT_UOFFSETOF(ISO9660RRIPNM, achName);
3208 if (!(pUnion->NM.fFlags & (ISO9660RRIP_NM_F_CURRENT | ISO9660RRIP_NM_F_PARENT)))
3209 { /* likely */ }
3210 else
3211 {
3212 if (cchName == 0)
3213 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Ignoring 'NM' entry for '.' and '..'\n"));
3214 else
3215 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: Ignoring malformed 'NM' using '.' or '..': fFlags=%#x cchName=%#x %.*Rhxs\n",
3216 pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName));
3217 pNameCmp->offMatched = ~(size_t)0 / 2;
3218 return VERR_MISMATCH;
3219 }
3220 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': fFlags=%#x cchName=%#x '%.*s' (%.*Rhxs); offMatched=%#zx cchEntry=%#zx\n",
3221 pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName, cchName, pUnion->NM.achName, pNameCmp->offMatched, pNameCmp->cchEntry));
3222 AssertReturn(pNameCmp->offMatched < pNameCmp->cchEntry, VERR_MISMATCH);
3223
3224 if (RTStrNICmp(&pNameCmp->pszEntry[pNameCmp->offMatched], pUnion->NM.achName, cchName) == 0)
3225 {
3226 /** @todo Incorrectly ASSUMES all upper and lower codepoints have the same
3227 * encoding length. However, since this shouldn't be UTF-8, but plain
3228 * limited ASCII that's not really all that important. */
3229 pNameCmp->offMatched += cchName;
3230 if (!(pUnion->NM.fFlags & ISO9660RRIP_NM_F_CONTINUE))
3231 {
3232 if (pNameCmp->offMatched >= pNameCmp->cchEntry)
3233 {
3234 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VINF_SUCCESS\n"));
3235 return VINF_SUCCESS;
3236 }
3237 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VERR_MISMATCH - %zu unmatched bytes\n",
3238 pNameCmp->cchEntry - pNameCmp->offMatched));
3239 return VERR_MISMATCH;
3240 }
3241 if (pNameCmp->offMatched >= pNameCmp->cchEntry)
3242 {
3243 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VERR_MISMATCH - match full name but ISO9660RRIP_NM_F_CONTINUE is set!\n"));
3244 return VERR_MISMATCH;
3245 }
3246 }
3247 else
3248 {
3249 Log4(("rtFsIsoDirShrd_CompareRockRidgeName: 'NM': returning VERR_MISMATCH - mismatch\n"));
3250 pNameCmp->offMatched = ~(size_t)0 / 2;
3251 return VERR_MISMATCH;
3252 }
3253 }
3254 }
3255 }
3256 return fContinuationRecord ? VERR_MORE_DATA : VERR_MISMATCH;
3257}
3258
3259
3260/**
3261 * Worker for rtFsIsoDir_FindEntry9660 that compares a name with the rock ridge
3262 * info in the directory record, if present.
3263 *
3264 * @returns true if equal, false if not.
3265 * @param pThis The directory.
3266 * @param pDirRec The directory record.
3267 * @param pszEntry The string to compare with.
3268 * @param cbEntry The length of @a pszEntry including terminator.
3269 */
3270static bool rtFsIsoDir_IsEntryEqualRock(PRTFSISODIRSHRD pThis, PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cbEntry)
3271{
3272 /*
3273 * Is there room for any rock ridge data?
3274 */
3275 uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
3276 - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
3277 uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
3278 if (cbSys >= 4)
3279 {
3280 RTFSISOROCKNAMECOMP NameCmp;
3281 NameCmp.pszEntry = pszEntry;
3282 NameCmp.cchEntry = cbEntry - 1;
3283 NameCmp.offMatched = 0;
3284 int rc = rtFsIsoDirShrd_CompareRockRidgeName(pThis, pbSys, cbSys, &NameCmp, false /*fContinuationRecord*/);
3285 if (rc == VINF_SUCCESS)
3286 return true;
3287 }
3288 return false;
3289}
3290
3291
3292/**
3293 * Worker for rtFsIsoDir_FindEntry9660 that compares a UTF-16BE name with a
3294 * directory record.
3295 *
3296 * @returns true if equal, false if not.
3297 * @param pDirRec The directory record.
3298 * @param pwszEntry The UTF-16BE string to compare with.
3299 * @param cbEntry The compare string length in bytes (sans zero
3300 * terminator).
3301 * @param cwcEntry The compare string length in RTUTF16 units.
3302 * @param puVersion Where to return any file version number.
3303 */
3304DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualUtf16Big(PCISO9660DIRREC pDirRec, PCRTUTF16 pwszEntry, size_t cbEntry,
3305 size_t cwcEntry, uint32_t *puVersion)
3306{
3307 /* ASSUME directories cannot have any version tags. */
3308 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3309 {
3310 if (RT_LIKELY(pDirRec->bFileIdLength != cbEntry))
3311 return false;
3312 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
3313 return false;
3314 }
3315 else
3316 {
3317 size_t cbNameDelta = (size_t)pDirRec->bFileIdLength - cbEntry;
3318 if (RT_LIKELY(cbNameDelta > (size_t)12 /* ;12345 */))
3319 return false;
3320 if (cbNameDelta == 0)
3321 {
3322 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
3323 return false;
3324 *puVersion = 1;
3325 }
3326 else
3327 {
3328 if (RT_LIKELY(RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';'))
3329 return false;
3330 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
3331 return false;
3332 uint32_t uVersion;
3333 size_t cwcVersion = rtFsIso9660GetVersionLengthUtf16Big((PCRTUTF16)pDirRec->achFileId,
3334 pDirRec->bFileIdLength, &uVersion);
3335 if (RT_LIKELY(cwcVersion * sizeof(RTUTF16) == cbNameDelta))
3336 *puVersion = uVersion;
3337 else
3338 return false;
3339 }
3340 }
3341
3342 /* (No need to check for dot and dot-dot here, because cbEntry must be a
3343 multiple of two.) */
3344 Assert(!(cbEntry & 1));
3345 return true;
3346}
3347
3348
3349/**
3350 * Worker for rtFsIsoDir_FindEntry9660 that compares an ASCII name with a
3351 * directory record.
3352 *
3353 * @returns true if equal, false if not.
3354 * @param pDirRec The directory record.
3355 * @param pszEntry The uppercased ASCII string to compare with.
3356 * @param cchEntry The length of the compare string.
3357 * @param puVersion Where to return any file version number.
3358 *
3359 * @note We're using RTStrNICmpAscii here because of non-conforming ISOs with
3360 * entirely lowercase name or mixed cased names.
3361 */
3362DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualAscii(PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cchEntry,
3363 uint32_t *puVersion)
3364{
3365 /* ASSUME directories cannot have any version tags. */
3366 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
3367 {
3368 if (RT_LIKELY(pDirRec->bFileIdLength != cchEntry))
3369 return false;
3370 if (RT_LIKELY(RTStrNICmpAscii(pDirRec->achFileId, pszEntry, cchEntry) != 0))
3371 return false;
3372 }
3373 else
3374 {
3375 size_t cchNameDelta = (size_t)pDirRec->bFileIdLength - cchEntry;
3376 if (RT_LIKELY(cchNameDelta > (size_t)6 /* ;12345 */))
3377 return false;
3378 if (cchNameDelta == 0)
3379 {
3380 if (RT_LIKELY(RTStrNICmpAscii(pDirRec->achFileId, pszEntry, cchEntry) != 0))
3381 return false;
3382 *puVersion = 1;
3383 }
3384 else
3385 {
3386 if (RT_LIKELY(pDirRec->achFileId[cchEntry] != ';'))
3387 return false;
3388 if (RT_LIKELY(RTStrNICmpAscii(pDirRec->achFileId, pszEntry, cchEntry) != 0))
3389 return false;
3390 uint32_t uVersion;
3391 size_t cchVersion = rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion);
3392 if (RT_LIKELY(cchVersion == cchNameDelta))
3393 *puVersion = uVersion;
3394 else
3395 return false;
3396 }
3397 }
3398
3399 /* Don't match the 'dot' and 'dot-dot' directory records. */
3400 if (RT_LIKELY( pDirRec->bFileIdLength != 1
3401 || (uint8_t)pDirRec->achFileId[0] > (uint8_t)0x01))
3402 return true;
3403 return false;
3404}
3405
3406
3407/**
3408 * Locates a directory entry in a directory.
3409 *
3410 * @returns IPRT status code.
3411 * @retval VERR_FILE_NOT_FOUND if not found.
3412 * @param pThis The directory to search.
3413 * @param pszEntry The entry to look for.
3414 * @param poffDirRec Where to return the offset of the directory record
3415 * on the disk.
3416 * @param ppDirRec Where to return the pointer to the directory record
3417 * (the whole directory is buffered).
3418 * @param pcDirRecs Where to return the number of directory records
3419 * related to this entry.
3420 * @param pfMode Where to return the file type, rock ridge adjusted.
3421 * @param puVersion Where to return the file version number.
3422 * @param pRockInfo Where to return rock ridge info. This is NULL if
3423 * the volume didn't advertise any rock ridge info.
3424 */
3425static int rtFsIsoDir_FindEntry9660(PRTFSISODIRSHRD pThis, const char *pszEntry, uint64_t *poffDirRec, PCISO9660DIRREC *ppDirRec,
3426 uint32_t *pcDirRecs, PRTFMODE pfMode, uint32_t *puVersion, PRTFSISOROCKINFO pRockInfo)
3427{
3428 Assert(pThis->Core.pVol->enmType != RTFSISOVOLTYPE_UDF);
3429
3430 /* Set return values. */
3431 *poffDirRec = UINT64_MAX;
3432 *ppDirRec = NULL;
3433 *pcDirRecs = 1;
3434 *pfMode = UINT32_MAX;
3435 *puVersion = 0;
3436 if (pRockInfo)
3437 pRockInfo->fValid = false;
3438
3439 /*
3440 * If we're in UTF-16BE mode, convert the input name to UTF-16BE. Otherwise try
3441 * uppercase it into a ISO 9660 compliant name.
3442 */
3443 int rc;
3444 bool const fIsUtf16 = pThis->Core.pVol->fIsUtf16;
3445 size_t cwcEntry = 0;
3446 size_t cbEntry = 0;
3447 size_t cchUpper = ~(size_t)0;
3448 union
3449 {
3450 RTUTF16 wszEntry[260 + 1];
3451 struct
3452 {
3453 char szUpper[255 + 1];
3454 char szRock[260 + 1];
3455 } s;
3456 } uBuf;
3457 if (fIsUtf16)
3458 {
3459 PRTUTF16 pwszEntry = uBuf.wszEntry;
3460 rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
3461 if (RT_FAILURE(rc))
3462 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
3463 cbEntry = cwcEntry * 2;
3464 }
3465 else
3466 {
3467 rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
3468 if (RT_FAILURE(rc))
3469 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
3470 RTStrToUpper(uBuf.s.szUpper);
3471 cchUpper = strlen(uBuf.s.szUpper);
3472 cbEntry = strlen(pszEntry) + 1;
3473 }
3474
3475 /*
3476 * Scan the directory buffer by buffer.
3477 */
3478 uint32_t offEntryInDir = 0;
3479 uint32_t const cbDir = pThis->Core.cbObject;
3480 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
3481 {
3482 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
3483
3484 /* If null length, skip to the next sector. */
3485 if (pDirRec->cbDirRec == 0)
3486 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
3487 else
3488 {
3489 /*
3490 * Try match the filename.
3491 */
3492 /** @todo not sure if it's a great idea to match both name spaces... */
3493 if (RT_LIKELY( fIsUtf16
3494 ? !rtFsIsoDir_IsEntryEqualUtf16Big(pDirRec, uBuf.wszEntry, cbEntry, cwcEntry, puVersion)
3495 && ( !pRockInfo
3496 || !rtFsIsoDir_IsEntryEqualRock(pThis, pDirRec, pszEntry, cbEntry))
3497 : ( !pRockInfo
3498 || !rtFsIsoDir_IsEntryEqualRock(pThis, pDirRec, pszEntry, cbEntry))
3499 && !rtFsIsoDir_IsEntryEqualAscii(pDirRec, uBuf.s.szUpper, cchUpper, puVersion) ))
3500 {
3501 /* Advance */
3502 offEntryInDir += pDirRec->cbDirRec;
3503 continue;
3504 }
3505
3506 /*
3507 * Get info for the entry.
3508 */
3509 if (!pRockInfo)
3510 *pfMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
3511 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
3512 : 0644 | RTFS_TYPE_FILE;
3513 else
3514 {
3515 rtFsIsoDirShrd_ParseRockForDirRec(pThis, pDirRec, pRockInfo);
3516 *pfMode = pRockInfo->Info.Attr.fMode;
3517 }
3518 *poffDirRec = pThis->Core.FirstExtent.off + offEntryInDir;
3519 *ppDirRec = pDirRec;
3520
3521 /*
3522 * Deal with the unlikely scenario of multi extent records.
3523 */
3524 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3525 *pcDirRecs = 1;
3526 else
3527 {
3528 offEntryInDir += pDirRec->cbDirRec;
3529
3530 uint32_t cDirRecs = 1;
3531 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
3532 {
3533 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
3534 if (pDirRec2->cbDirRec != 0)
3535 {
3536 Assert(rtFsIsoDir_Is9660DirRecNextExtent(pDirRec, pDirRec2));
3537 cDirRecs++;
3538 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
3539 break;
3540 offEntryInDir += pDirRec2->cbDirRec;
3541 }
3542 else
3543 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
3544 }
3545
3546 *pcDirRecs = cDirRecs;
3547 }
3548 return VINF_SUCCESS;
3549 }
3550 }
3551
3552 return VERR_FILE_NOT_FOUND;
3553}
3554
3555
3556/**
3557 * Locates a directory entry in a directory.
3558 *
3559 * @returns IPRT status code.
3560 * @retval VERR_FILE_NOT_FOUND if not found.
3561 * @param pThis The directory to search.
3562 * @param pszEntry The entry to look for.
3563 * @param ppFid Where to return the pointer to the file ID entry.
3564 * (Points to the directory content.)
3565 */
3566static int rtFsIsoDir_FindEntryUdf(PRTFSISODIRSHRD pThis, const char *pszEntry, PCUDFFILEIDDESC *ppFid)
3567{
3568 Assert(pThis->Core.pVol->enmType == RTFSISOVOLTYPE_UDF);
3569 *ppFid = NULL;
3570
3571 /*
3572 * Recode the entry name as 8-bit (if possible) and 16-bit strings.
3573 * This also disposes of entries that definitely are too long.
3574 */
3575 size_t cb8Bit;
3576 bool fSimple;
3577 size_t cb16Bit;
3578 size_t cwc16Bit;
3579 uint8_t ab8Bit[255];
3580 RTUTF16 wsz16Bit[255];
3581
3582 /* 16-bit */
3583 PRTUTF16 pwsz16Bit = wsz16Bit;
3584 int rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwsz16Bit, RT_ELEMENTS(wsz16Bit), &cwc16Bit);
3585 if (RT_SUCCESS(rc))
3586 cb16Bit = 1 + cwc16Bit * sizeof(RTUTF16);
3587 else
3588 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
3589
3590 /* 8-bit (can't possibly overflow) */
3591 fSimple = true;
3592 cb8Bit = 0;
3593 const char *pszSrc = pszEntry;
3594 for (;;)
3595 {
3596 RTUNICP uc;
3597 int rc2 = RTStrGetCpEx(&pszSrc, &uc);
3598 AssertRCReturn(rc2, rc2);
3599 if (uc <= 0x7f)
3600 {
3601 if (uc)
3602 ab8Bit[cb8Bit++] = (uint8_t)uc;
3603 else
3604 break;
3605 }
3606 else if (uc <= 0xff)
3607 {
3608 ab8Bit[cb8Bit++] = (uint8_t)uc;
3609 fSimple = false;
3610 }
3611 else
3612 {
3613 cb8Bit = UINT32_MAX / 2;
3614 break;
3615 }
3616 }
3617 Assert(cb8Bit <= sizeof(ab8Bit) || cb8Bit == UINT32_MAX / 2);
3618 cb8Bit++;
3619
3620 /*
3621 * Scan the directory content.
3622 */
3623 uint32_t offDesc = 0;
3624 uint32_t const cbDir = pThis->Core.cbObject;
3625 while (offDesc + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= cbDir)
3626 {
3627 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
3628 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
3629 if ( offDesc + cbFid <= cbDir
3630 && pFid->Tag.idTag == UDF_TAG_ID_FILE_ID_DESC)
3631 { /* likely */ }
3632 else
3633 break;
3634
3635 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
3636 if (*pbName == 16)
3637 {
3638 if (cb16Bit == pFid->cbName)
3639 {
3640 if (RTUtf16BigNICmp((PCRTUTF16)(&pbName[1]), wsz16Bit, cwc16Bit) == 0)
3641 {
3642 *ppFid = pFid;
3643 return VINF_SUCCESS;
3644 }
3645 }
3646 }
3647 else if (*pbName == 8)
3648 {
3649 if ( cb8Bit == pFid->cbName
3650 && cb8Bit != UINT16_MAX)
3651 {
3652 if (fSimple)
3653 {
3654 if (RTStrNICmp((const char *)&pbName[1], (const char *)ab8Bit, cb8Bit - 1) == 0)
3655 {
3656 *ppFid = pFid;
3657 return VINF_SUCCESS;
3658 }
3659 }
3660 else
3661 {
3662 size_t cch = cb8Bit - 1;
3663 size_t off;
3664 for (off = 0; off < cch; off++)
3665 {
3666 RTUNICP uc1 = ab8Bit[off];
3667 RTUNICP uc2 = pbName[off + 1];
3668 if ( uc1 == uc2
3669 || RTUniCpToLower(uc1) == RTUniCpToLower(uc2)
3670 || RTUniCpToUpper(uc1) == RTUniCpToUpper(uc2))
3671 { /* matches */ }
3672 else
3673 break;
3674 }
3675 if (off == cch)
3676 {
3677 *ppFid = pFid;
3678 return VINF_SUCCESS;
3679 }
3680 }
3681 }
3682 }
3683
3684 /* advance */
3685 offDesc += cbFid;
3686 }
3687
3688 return VERR_FILE_NOT_FOUND;
3689}
3690
3691
3692/**
3693 * Releases a reference to a shared directory structure.
3694 *
3695 * @param pShared The shared directory structure.
3696 */
3697static void rtFsIsoDirShrd_Release(PRTFSISODIRSHRD pShared)
3698{
3699 uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
3700 Assert(cRefs < UINT32_MAX / 2);
3701 if (cRefs == 0)
3702 {
3703 LogFlow(("rtFsIsoDirShrd_Release: Destroying shared structure %p\n", pShared));
3704 Assert(pShared->Core.cRefs == 0);
3705 if (pShared->pbDir)
3706 {
3707 RTMemFree(pShared->pbDir);
3708 pShared->pbDir = NULL;
3709 }
3710 rtFsIsoCore_Destroy(&pShared->Core);
3711 RTMemFree(pShared);
3712 }
3713}
3714
3715
3716/**
3717 * Retains a reference to a shared directory structure.
3718 *
3719 * @param pShared The shared directory structure.
3720 */
3721static void rtFsIsoDirShrd_Retain(PRTFSISODIRSHRD pShared)
3722{
3723 uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
3724 Assert(cRefs > 1); NOREF(cRefs);
3725}
3726
3727
3728
3729/**
3730 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
3731 */
3732static DECLCALLBACK(int) rtFsIsoDir_Close(void *pvThis)
3733{
3734 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3735 LogFlow(("rtFsIsoDir_Close(%p/%p)\n", pThis, pThis->pShared));
3736
3737 PRTFSISODIRSHRD pShared = pThis->pShared;
3738 pThis->pShared = NULL;
3739 if (pShared)
3740 rtFsIsoDirShrd_Release(pShared);
3741 return VINF_SUCCESS;
3742}
3743
3744
3745/**
3746 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
3747 */
3748static DECLCALLBACK(int) rtFsIsoDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3749{
3750 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3751 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
3752}
3753
3754
3755/**
3756 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
3757 */
3758static DECLCALLBACK(int) rtFsIsoDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
3759 uint32_t fFlags, PRTVFSOBJ phVfsObj)
3760{
3761 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3762 PRTFSISODIRSHRD pShared = pThis->pShared;
3763 int rc;
3764
3765 /*
3766 * We cannot create or replace anything, just open stuff.
3767 */
3768 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
3769 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
3770 { /* likely */ }
3771 else
3772 return VERR_WRITE_PROTECT;
3773
3774 /*
3775 * Special cases '.' and '..'
3776 */
3777 if (pszEntry[0] == '.')
3778 {
3779 PRTFSISODIRSHRD pSharedToOpen;
3780 if (pszEntry[1] == '\0')
3781 pSharedToOpen = pShared;
3782 else if (pszEntry[1] == '.' && pszEntry[2] == '\0')
3783 {
3784 pSharedToOpen = pShared->Core.pParentDir;
3785 if (!pSharedToOpen)
3786 pSharedToOpen = pShared;
3787 }
3788 else
3789 pSharedToOpen = NULL;
3790 if (pSharedToOpen)
3791 {
3792 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3793 {
3794 rtFsIsoDirShrd_Retain(pSharedToOpen);
3795 RTVFSDIR hVfsDir;
3796 rc = rtFsIsoDir_NewWithShared(pShared->Core.pVol, pSharedToOpen, &hVfsDir);
3797 if (RT_SUCCESS(rc))
3798 {
3799 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3800 RTVfsDirRelease(hVfsDir);
3801 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3802 }
3803 }
3804 else
3805 rc = VERR_IS_A_DIRECTORY;
3806 return rc;
3807 }
3808 }
3809
3810 /*
3811 * Try open whatever it is.
3812 */
3813 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
3814 {
3815
3816 /*
3817 * ISO 9660
3818 */
3819 PCISO9660DIRREC pDirRec;
3820 uint64_t offDirRec;
3821 uint32_t cDirRecs;
3822 RTFMODE fMode;
3823 uint32_t uVersion;
3824 PRTFSISOROCKINFO pRockInfo = NULL;
3825 if (pShared->Core.pVol->fHaveRock)
3826 pRockInfo = (PRTFSISOROCKINFO)alloca(sizeof(*pRockInfo));
3827 rc = rtFsIsoDir_FindEntry9660(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion, pRockInfo);
3828 Log2(("rtFsIsoDir_Open: FindEntry9660(,%s,) -> %Rrc\n", pszEntry, rc));
3829 if (RT_SUCCESS(rc))
3830 {
3831 switch (fMode & RTFS_TYPE_MASK)
3832 {
3833 case RTFS_TYPE_FILE:
3834 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
3835 {
3836 RTVFSFILE hVfsFile;
3837 rc = rtFsIsoFile_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, fOpen,
3838 uVersion, pRockInfo && pRockInfo->fValid ? pRockInfo : NULL, &hVfsFile);
3839 if (RT_SUCCESS(rc))
3840 {
3841 *phVfsObj = RTVfsObjFromFile(hVfsFile);
3842 RTVfsFileRelease(hVfsFile);
3843 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3844 }
3845 }
3846 else
3847 rc = VERR_IS_A_FILE;
3848 break;
3849
3850 case RTFS_TYPE_DIRECTORY:
3851 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3852 {
3853 RTVFSDIR hVfsDir;
3854 rc = rtFsIsoDir_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec,
3855 pRockInfo && pRockInfo->fValid ? pRockInfo : NULL, &hVfsDir);
3856 if (RT_SUCCESS(rc))
3857 {
3858 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3859 RTVfsDirRelease(hVfsDir);
3860 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3861 }
3862 }
3863 else
3864 rc = VERR_IS_A_DIRECTORY;
3865 break;
3866
3867 case RTFS_TYPE_SYMLINK:
3868 case RTFS_TYPE_DEV_BLOCK:
3869 case RTFS_TYPE_DEV_CHAR:
3870 case RTFS_TYPE_FIFO:
3871 case RTFS_TYPE_SOCKET:
3872 case RTFS_TYPE_WHITEOUT:
3873 rc = VERR_NOT_IMPLEMENTED;
3874 break;
3875
3876 default:
3877 rc = VERR_PATH_NOT_FOUND;
3878 break;
3879 }
3880 }
3881 }
3882 else
3883 {
3884 /*
3885 * UDF
3886 */
3887 PCUDFFILEIDDESC pFid;
3888 rc = rtFsIsoDir_FindEntryUdf(pShared, pszEntry, &pFid);
3889 Log2(("rtFsIsoDir_Open: FindEntryUdf(,%s,) -> %Rrc\n", pszEntry, rc));
3890 if (RT_SUCCESS(rc))
3891 {
3892 if (!(pFid->fFlags & UDF_FILE_FLAGS_DELETED))
3893 {
3894 if (!(pFid->fFlags & UDF_FILE_FLAGS_DIRECTORY))
3895 {
3896 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
3897 {
3898 RTVFSFILE hVfsFile;
3899 rc = rtFsIsoFile_NewUdf(pShared->Core.pVol, pShared, pFid, fOpen, &hVfsFile);
3900 if (RT_SUCCESS(rc))
3901 {
3902 *phVfsObj = RTVfsObjFromFile(hVfsFile);
3903 RTVfsFileRelease(hVfsFile);
3904 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3905 }
3906 }
3907 else
3908 rc = VERR_IS_A_FILE;
3909 }
3910 else
3911 {
3912 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3913 {
3914 RTVFSDIR hVfsDir;
3915 rc = rtFsIsoDir_NewUdf(pShared->Core.pVol, pShared, pFid, &hVfsDir);
3916 if (RT_SUCCESS(rc))
3917 {
3918 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3919 RTVfsDirRelease(hVfsDir);
3920 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3921 }
3922 }
3923 else
3924 rc = VERR_IS_A_DIRECTORY;
3925 }
3926 }
3927 /* We treat UDF_FILE_FLAGS_DELETED like RTFS_TYPE_WHITEOUT for now. */
3928 else
3929 rc = VERR_PATH_NOT_FOUND;
3930 }
3931 }
3932 return rc;
3933
3934}
3935
3936
3937/**
3938 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3939 */
3940static DECLCALLBACK(int) rtFsIsoDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3941{
3942 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3943 return VERR_WRITE_PROTECT;
3944}
3945
3946
3947/**
3948 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3949 */
3950static DECLCALLBACK(int) rtFsIsoDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3951{
3952 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3953 return VERR_NOT_SUPPORTED;
3954}
3955
3956
3957/**
3958 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3959 */
3960static DECLCALLBACK(int) rtFsIsoDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3961 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3962{
3963 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3964 return VERR_WRITE_PROTECT;
3965}
3966
3967
3968/**
3969 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3970 */
3971static DECLCALLBACK(int) rtFsIsoDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3972{
3973 RT_NOREF(pvThis, pszEntry, fType);
3974 return VERR_WRITE_PROTECT;
3975}
3976
3977
3978/**
3979 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3980 */
3981static DECLCALLBACK(int) rtFsIsoDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3982{
3983 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3984 return VERR_WRITE_PROTECT;
3985}
3986
3987
3988/**
3989 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3990 */
3991static DECLCALLBACK(int) rtFsIsoDir_RewindDir(void *pvThis)
3992{
3993 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
3994 pThis->offDir = 0;
3995 return VINF_SUCCESS;
3996}
3997
3998
3999/**
4000 * The ISO 9660 worker for rtFsIsoDir_ReadDir
4001 */
4002static int rtFsIsoDir_ReadDir9660(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4003 RTFSOBJATTRADD enmAddAttr)
4004{
4005 PRTFSISOROCKINFO pRockInfo = NULL;
4006 if (pShared->Core.pVol->fHaveRock)
4007 pRockInfo = (PRTFSISOROCKINFO)alloca(sizeof(*pRockInfo));
4008
4009 while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
4010 {
4011 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
4012
4013 /* If null length, skip to the next sector. */
4014 if (pDirRec->cbDirRec == 0)
4015 pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
4016 else
4017 {
4018 /*
4019 * Do names first as they may cause overflows.
4020 */
4021 uint32_t uVersion = 0;
4022 if ( pDirRec->bFileIdLength == 1
4023 && pDirRec->achFileId[0] == '\0')
4024 {
4025 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
4026 {
4027 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
4028 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot)\n"));
4029 return VERR_BUFFER_OVERFLOW;
4030 }
4031 pDirEntry->cbName = 1;
4032 pDirEntry->szName[0] = '.';
4033 pDirEntry->szName[1] = '\0';
4034 }
4035 else if ( pDirRec->bFileIdLength == 1
4036 && pDirRec->achFileId[0] == '\1')
4037 {
4038 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
4039 {
4040 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
4041 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
4042 return VERR_BUFFER_OVERFLOW;
4043 }
4044 pDirEntry->cbName = 2;
4045 pDirEntry->szName[0] = '.';
4046 pDirEntry->szName[1] = '.';
4047 pDirEntry->szName[2] = '\0';
4048 }
4049 else if (pShared->Core.pVol->fIsUtf16)
4050 {
4051 PCRTUTF16 pawcSrc = (PCRTUTF16)&pDirRec->achFileId[0];
4052 size_t cwcSrc = pDirRec->bFileIdLength / sizeof(RTUTF16);
4053 size_t cwcVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
4054 ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
4055 size_t cchNeeded = 0;
4056 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
4057 char *pszDst = pDirEntry->szName;
4058
4059 int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
4060 if (RT_SUCCESS(rc))
4061 pDirEntry->cbName = (uint16_t)cchNeeded;
4062 else if (rc == VERR_BUFFER_OVERFLOW)
4063 {
4064 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
4065 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
4066 return VERR_BUFFER_OVERFLOW;
4067 }
4068 else
4069 {
4070 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
4071 if (cchNeeded2 >= 0)
4072 pDirEntry->cbName = (uint16_t)cchNeeded2;
4073 else
4074 {
4075 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
4076 return VERR_BUFFER_OVERFLOW;
4077 }
4078 }
4079 }
4080 else
4081 {
4082 /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
4083 size_t cchVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
4084 ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
4085 size_t cchName = pDirRec->bFileIdLength - cchVer;
4086 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
4087 if (*pcbDirEntry < cbNeeded)
4088 {
4089 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
4090 *pcbDirEntry = cbNeeded;
4091 return VERR_BUFFER_OVERFLOW;
4092 }
4093 pDirEntry->cbName = (uint16_t)cchName;
4094 memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
4095 pDirEntry->szName[cchName] = '\0';
4096 RTStrPurgeEncoding(pDirEntry->szName);
4097 }
4098 pDirEntry->cwcShortName = 0;
4099 pDirEntry->wszShortName[0] = '\0';
4100
4101 /*
4102 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
4103 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
4104 */
4105 RTFSISOCORE TmpObj;
4106 RT_ZERO(TmpObj);
4107 rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
4108 pThis->offDir + pShared->Core.FirstExtent.off, uVersion, NULL, pShared->Core.pVol);
4109 int rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
4110
4111 /*
4112 * Look for rock ridge info associated with this entry
4113 * and merge that into the record.
4114 */
4115 if (pRockInfo)
4116 {
4117 rtFsIsoDirShrd_ParseRockForDirRec(pShared, pDirRec, pRockInfo);
4118 if (pRockInfo->fValid)
4119 {
4120 if ( pRockInfo->fSeenLastNM
4121 && pRockInfo->cchName > 0
4122 && !pShared->Core.pVol->fIsUtf16
4123 && ( pDirRec->bFileIdLength != 1
4124 || ( pDirRec->achFileId[0] != '\0' /* . */
4125 && pDirRec->achFileId[0] != '\1'))) /* .. */
4126 {
4127 size_t const cchName = pRockInfo->cchName;
4128 Assert(strlen(pRockInfo->szName) == cchName);
4129 size_t const cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
4130 if (*pcbDirEntry < cbNeeded)
4131 {
4132 Log3(("rtFsIsoDir_ReadDir9660: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (Rock)\n", *pcbDirEntry, cbNeeded));
4133 *pcbDirEntry = cbNeeded;
4134 return VERR_BUFFER_OVERFLOW;
4135 }
4136 pDirEntry->cbName = (uint16_t)cchName;
4137 memcpy(pDirEntry->szName, pRockInfo->szName, cchName);
4138 pDirEntry->szName[cchName] = '\0';
4139
4140 RTStrPurgeEncoding(pDirEntry->szName);
4141 }
4142 }
4143 }
4144
4145 /*
4146 * Update the directory location and handle multi extent records.
4147 *
4148 * Multi extent records only affect the file size and the directory location,
4149 * so we deal with it here instead of involving rtFsIsoCore_InitFrom9660DirRec
4150 * which would potentially require freeing memory and such.
4151 */
4152 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
4153 {
4154 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
4155 pThis->offDir += pDirRec->cbDirRec;
4156 }
4157 else
4158 {
4159 uint32_t cExtents = 1;
4160 uint32_t offDir = pThis->offDir + pDirRec->cbDirRec;
4161 while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
4162 {
4163 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
4164 if (pDirRec2->cbDirRec != 0)
4165 {
4166 pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
4167 offDir += pDirRec2->cbDirRec;
4168 cExtents++;
4169 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
4170 break;
4171 }
4172 else
4173 offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
4174 }
4175 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
4176 pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
4177 pThis->offDir = offDir;
4178 }
4179
4180 return rc;
4181 }
4182 }
4183
4184 Log3(("rtFsIsoDir_ReadDir9660: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
4185 return VERR_NO_MORE_FILES;
4186}
4187
4188
4189/**
4190 * The UDF worker for rtFsIsoDir_ReadDir
4191 */
4192static int rtFsIsoDir_ReadDirUdf(PRTFSISODIROBJ pThis, PRTFSISODIRSHRD pShared, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4193 RTFSOBJATTRADD enmAddAttr)
4194{
4195 /*
4196 * At offset zero we've got the '.' entry. This has to be generated
4197 * manually as it's not part of the directory content. The directory
4198 * offset has to be faked for this too, so offDir == 0 indicates the '.'
4199 * entry whereas offDir == 1 is the first file id descriptor.
4200 */
4201 if (pThis->offDir == 0)
4202 {
4203 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
4204 {
4205 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
4206 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW (dot)\n"));
4207 return VERR_BUFFER_OVERFLOW;
4208 }
4209 pDirEntry->cbName = 1;
4210 pDirEntry->szName[0] = '.';
4211 pDirEntry->szName[1] = '\0';
4212 pDirEntry->cwcShortName = 0;
4213 pDirEntry->wszShortName[0] = '\0';
4214
4215 int rc = rtFsIsoCore_QueryInfo(&pShared->Core, &pDirEntry->Info, enmAddAttr);
4216
4217 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
4218 pThis->offDir = 1;
4219 return rc;
4220 }
4221
4222 /*
4223 * Do the directory content.
4224 */
4225 while (pThis->offDir + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) <= pShared->cbDir + 1)
4226 {
4227 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pShared->pbDir[pThis->offDir - 1];
4228 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
4229
4230 if (pThis->offDir + cbFid <= pShared->cbDir + 1)
4231 { /* likely */ }
4232 else
4233 break;
4234
4235 /*
4236 * Do names first as they may cause overflows.
4237 */
4238 if (pFid->cbName > 1)
4239 {
4240 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
4241 uint32_t cbSrc = pFid->cbName;
4242 if (*pbName == 8)
4243 {
4244 /* Figure out the UTF-8 length first. */
4245 bool fSimple = true;
4246 uint32_t cchDst = 0;
4247 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
4248 if (!(pbName[offSrc] & 0x80))
4249 cchDst++;
4250 else
4251 {
4252 cchDst += 2;
4253 fSimple = false;
4254 }
4255
4256 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchDst + 1;
4257 if (*pcbDirEntry >= cbNeeded)
4258 {
4259 if (fSimple)
4260 {
4261 Assert(cbSrc - 1 == cchDst);
4262 memcpy(pDirEntry->szName, &pbName[1], cchDst);
4263 pDirEntry->szName[cchDst] = '\0';
4264 }
4265 else
4266 {
4267 char *pszDst = pDirEntry->szName;
4268 for (uint32_t offSrc = 1; offSrc < cbSrc; offSrc++)
4269 pszDst = RTStrPutCp(pszDst, pbName[offSrc]);
4270 *pszDst = '\0';
4271 Assert((size_t)(pszDst - &pDirEntry->szName[0]) == cchDst);
4272 }
4273 }
4274 else
4275 {
4276 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (8-bit)\n", *pcbDirEntry, cbNeeded));
4277 *pcbDirEntry = cbNeeded;
4278 return VERR_BUFFER_OVERFLOW;
4279 }
4280 }
4281 else
4282 {
4283 /* Let RTUtf16BigToUtf8Ex do the bounds checking. */
4284 char *pszDst = pDirEntry->szName;
4285 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
4286 size_t cchNeeded = 0;
4287 int rc;
4288 if (*pbName == 16)
4289 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)(pbName + 1), (cbSrc - 1) / sizeof(RTUTF16), &pszDst, cbDst, &cchNeeded);
4290 else
4291 rc = VERR_INVALID_NAME;
4292 if (RT_SUCCESS(rc))
4293 pDirEntry->cbName = (uint16_t)cchNeeded;
4294 else if (rc == VERR_BUFFER_OVERFLOW)
4295 {
4296 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
4297 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (16-bit)\n", cbDst, cchNeeded));
4298 return VERR_BUFFER_OVERFLOW;
4299 }
4300 else
4301 {
4302 LogRelMax(90, ("ISO/UDF: Malformed directory entry name at %#x: %.*Rhxs\n", pThis->offDir - 1, cbSrc, pbName));
4303 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir - 1);
4304 if (cchNeeded2 >= 0)
4305 pDirEntry->cbName = (uint16_t)cchNeeded2;
4306 else
4307 {
4308 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
4309 return VERR_BUFFER_OVERFLOW;
4310 }
4311 }
4312 }
4313 }
4314 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
4315 {
4316 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2 + 1;
4317 if (*pcbDirEntry < cbNeeded)
4318 {
4319 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (dot-dot)\n", *pcbDirEntry, cbNeeded));
4320 *pcbDirEntry = cbNeeded;
4321 return VERR_BUFFER_OVERFLOW;
4322 }
4323 pDirEntry->cbName = 2;
4324 pDirEntry->szName[0] = '.';
4325 pDirEntry->szName[1] = '.';
4326 pDirEntry->szName[2] = '\0';
4327 }
4328 else
4329 {
4330 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 1;
4331 if (*pcbDirEntry < cbNeeded)
4332 {
4333 Log3(("rtFsIsoDir_ReadDirUdf: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (empty)\n", *pcbDirEntry, cbNeeded));
4334 *pcbDirEntry = cbNeeded;
4335 return VERR_BUFFER_OVERFLOW;
4336 }
4337 pDirEntry->cbName = 0;
4338 pDirEntry->szName[0] = '\0';
4339 }
4340
4341 pDirEntry->cwcShortName = 0;
4342 pDirEntry->wszShortName[0] = '\0';
4343
4344 /*
4345 * To avoid duplicating code in rtFsIsoCore_InitUdf and
4346 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
4347 */
4348 RTFSISOCORE TmpObj;
4349 RT_ZERO(TmpObj);
4350 int rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&TmpObj, &pFid->Icb, pFid, pThis->offDir - 1, pShared->Core.pVol);
4351 if (RT_SUCCESS(rc))
4352 {
4353 rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
4354 rtFsIsoCore_Destroy(&TmpObj);
4355 }
4356
4357 /*
4358 * Update.
4359 */
4360 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
4361 pThis->offDir += cbFid;
4362
4363 return rc;
4364 }
4365
4366 Log3(("rtFsIsoDir_ReadDirUdf: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
4367 return VERR_NO_MORE_FILES;
4368}
4369
4370
4371/**
4372 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
4373 */
4374static DECLCALLBACK(int) rtFsIsoDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4375 RTFSOBJATTRADD enmAddAttr)
4376{
4377 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
4378 PRTFSISODIRSHRD pShared = pThis->pShared;
4379 int rc;
4380 if (pShared->Core.pVol->enmType != RTFSISOVOLTYPE_UDF)
4381 rc = rtFsIsoDir_ReadDir9660(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
4382 else
4383 rc = rtFsIsoDir_ReadDirUdf(pThis, pShared, pDirEntry, pcbDirEntry, enmAddAttr);
4384 return rc;
4385}
4386
4387
4388/**
4389 * ISO file operations.
4390 */
4391static const RTVFSDIROPS g_rtFsIsoDirOps =
4392{
4393 { /* Obj */
4394 RTVFSOBJOPS_VERSION,
4395 RTVFSOBJTYPE_DIR,
4396 "ISO 9660 Dir",
4397 rtFsIsoDir_Close,
4398 rtFsIsoDir_QueryInfo,
4399 NULL,
4400 RTVFSOBJOPS_VERSION
4401 },
4402 RTVFSDIROPS_VERSION,
4403 0,
4404 { /* ObjSet */
4405 RTVFSOBJSETOPS_VERSION,
4406 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
4407 NULL /*SetMode*/,
4408 NULL /*SetTimes*/,
4409 NULL /*SetOwner*/,
4410 RTVFSOBJSETOPS_VERSION
4411 },
4412 rtFsIsoDir_Open,
4413 NULL /* pfnFollowAbsoluteSymlink */,
4414 NULL /* pfnOpenFile */,
4415 NULL /* pfnOpenDir */,
4416 rtFsIsoDir_CreateDir,
4417 rtFsIsoDir_OpenSymlink,
4418 rtFsIsoDir_CreateSymlink,
4419 NULL /* pfnQueryEntryInfo */,
4420 rtFsIsoDir_UnlinkEntry,
4421 rtFsIsoDir_RenameEntry,
4422 rtFsIsoDir_RewindDir,
4423 rtFsIsoDir_ReadDir,
4424 RTVFSDIROPS_VERSION,
4425};
4426
4427
4428/**
4429 * Adds an open child to the parent directory's shared structure.
4430 *
4431 * Maintains an additional reference to the parent dir to prevent it from going
4432 * away. If @a pDir is the root directory, it also ensures the volume is
4433 * referenced and sticks around until the last open object is gone.
4434 *
4435 * @param pDir The directory.
4436 * @param pChild The child being opened.
4437 * @sa rtFsIsoDirShrd_RemoveOpenChild
4438 */
4439static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
4440{
4441 rtFsIsoDirShrd_Retain(pDir);
4442
4443 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
4444 pChild->pParentDir = pDir;
4445}
4446
4447
4448/**
4449 * Removes an open child to the parent directory.
4450 *
4451 * @param pDir The directory.
4452 * @param pChild The child being removed.
4453 *
4454 * @remarks This is the very last thing you do as it may cause a few other
4455 * objects to be released recursively (parent dir and the volume).
4456 *
4457 * @sa rtFsIsoDirShrd_AddOpenChild
4458 */
4459static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
4460{
4461 AssertReturnVoid(pChild->pParentDir == pDir);
4462 RTListNodeRemove(&pChild->Entry);
4463 pChild->pParentDir = NULL;
4464
4465 rtFsIsoDirShrd_Release(pDir);
4466}
4467
4468
4469#ifdef LOG_ENABLED
4470/**
4471 * Logs the content of a directory.
4472 */
4473static void rtFsIsoDirShrd_Log9660Content(PRTFSISODIRSHRD pThis)
4474{
4475 if (LogIs2Enabled())
4476 {
4477 uint32_t offRec = 0;
4478 while (offRec < pThis->cbDir)
4479 {
4480 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
4481 if (pDirRec->cbDirRec == 0)
4482 break;
4483
4484 RTUTF16 wszName[128];
4485 if (pThis->Core.pVol->fIsUtf16)
4486 {
4487 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
4488 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
4489 pwszSrc--;
4490 *pwszDst-- = '\0';
4491 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
4492 {
4493 *pwszDst = RT_BE2H_U16(*pwszSrc);
4494 pwszDst--;
4495 pwszSrc--;
4496 }
4497 }
4498 else
4499 {
4500 PRTUTF16 pwszDst = wszName;
4501 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
4502 *pwszDst++ = pDirRec->achFileId[off];
4503 *pwszDst = '\0';
4504 }
4505
4506 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",
4507 offRec,
4508 pDirRec->cbDirRec,
4509 pDirRec->cExtAttrBlocks,
4510 ISO9660_GET_ENDIAN(&pDirRec->cbData),
4511 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
4512 pDirRec->fFileFlags,
4513 pDirRec->RecTime.bYear + 1900,
4514 pDirRec->RecTime.bMonth,
4515 pDirRec->RecTime.bDay,
4516 pDirRec->RecTime.bHour,
4517 pDirRec->RecTime.bMinute,
4518 pDirRec->RecTime.bSecond,
4519 pDirRec->RecTime.offUtc*4/60,
4520 pDirRec->bFileUnitSize,
4521 pDirRec->bInterleaveGapSize,
4522 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
4523 wszName));
4524
4525 uint32_t offSysUse = RT_UOFFSETOF_DYN(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength])
4526 + !(pDirRec->bFileIdLength & 1);
4527 if (offSysUse < pDirRec->cbDirRec)
4528 {
4529 Log2(("ISO9660: system use (%#x bytes):\n%.*RhxD\n", pDirRec->cbDirRec - offSysUse,
4530 pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
4531 }
4532
4533 /* advance */
4534 offRec += pDirRec->cbDirRec;
4535 }
4536 }
4537}
4538#endif /* LOG_ENABLED */
4539
4540
4541/**
4542 * Instantiates a new shared directory structure, given 9660 records.
4543 *
4544 * @returns IPRT status code.
4545 * @param pThis The ISO volume instance.
4546 * @param pParentDir The parent directory. This is NULL for the root
4547 * directory.
4548 * @param pDirRec The directory record. Will access @a cDirRecs
4549 * records.
4550 * @param cDirRecs Number of directory records if more than one.
4551 * @param offDirRec The byte offset of the directory record.
4552 * @param pRockInfo Optional pointer to rock ridge info for the entry.
4553 * @param ppShared Where to return the shared directory structure.
4554 */
4555static int rtFsIsoDirShrd_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
4556 uint32_t cDirRecs, uint64_t offDirRec, PCRTFSISOROCKINFO pRockInfo, PRTFSISODIRSHRD *ppShared)
4557{
4558 /*
4559 * Allocate a new structure and initialize it.
4560 */
4561 int rc = VERR_NO_MEMORY;
4562 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
4563 if (pShared)
4564 {
4565 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pRockInfo, pThis);
4566 if (RT_SUCCESS(rc))
4567 {
4568 RTListInit(&pShared->OpenChildren);
4569 pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
4570 pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
4571 if (pShared->pbDir)
4572 {
4573 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.off, pShared->pbDir, pShared->cbDir, NULL);
4574 if (RT_SUCCESS(rc))
4575 {
4576#ifdef LOG_ENABLED
4577 rtFsIsoDirShrd_Log9660Content(pShared);
4578#endif
4579
4580 /*
4581 * If this is the root directory, check if rock ridge info is present.
4582 */
4583 if ( !pParentDir
4584 && !(pThis->fFlags & RTFSISO9660_F_NO_ROCK)
4585 && pShared->cbDir > RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
4586 {
4587 PCISO9660DIRREC pDirRec0 = (PCISO9660DIRREC)pShared->pbDir;
4588 if ( pDirRec0->bFileIdLength == 1
4589 && pDirRec0->achFileId[0] == 0
4590 && pDirRec0->cbDirRec > RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
4591 rtFsIsoDirShrd_ParseRockForRoot(pShared, pDirRec0);
4592 }
4593
4594 /*
4595 * Link into parent directory so we can use it to update
4596 * our directory entry.
4597 */
4598 if (pParentDir)
4599 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
4600 *ppShared = pShared;
4601 return VINF_SUCCESS;
4602 }
4603 }
4604 else
4605 rc = VERR_NO_MEMORY;
4606 }
4607 RTMemFree(pShared);
4608 }
4609 *ppShared = NULL;
4610 return rc;
4611}
4612
4613
4614#ifdef LOG_ENABLED
4615/**
4616 * Logs the content of a directory.
4617 */
4618static void rtFsIsoDirShrd_LogUdfContent(PRTFSISODIRSHRD pThis)
4619{
4620 if (LogIs2Enabled())
4621 {
4622 uint32_t offDesc = 0;
4623 while (offDesc + RT_UOFFSETOF(UDFFILEIDDESC, abImplementationUse) < pThis->cbDir)
4624 {
4625 PCUDFFILEIDDESC pFid = (PCUDFFILEIDDESC)&pThis->pbDir[offDesc];
4626 uint32_t const cbFid = UDFFILEIDDESC_GET_SIZE(pFid);
4627 if (offDesc + cbFid > pThis->cbDir)
4628 break;
4629
4630 uint32_t cwcName = 0;
4631 RTUTF16 wszName[260];
4632 if (pFid->cbName > 0)
4633 {
4634 uint8_t const *pbName = UDFFILEIDDESC_2_NAME(pFid);
4635 uint32_t offSrc = 1;
4636 if (*pbName == 8)
4637 while (offSrc < pFid->cbName)
4638 {
4639 wszName[cwcName] = pbName[offSrc];
4640 cwcName++;
4641 offSrc++;
4642 }
4643 else if (*pbName == 16)
4644 while (offSrc + 1 <= pFid->cbName)
4645 {
4646 wszName[cwcName] = RT_MAKE_U16(pbName[offSrc + 1], pbName[offSrc]);
4647 cwcName++;
4648 offSrc += 2;
4649 }
4650 else
4651 {
4652 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<bad type>");
4653 cwcName = 10;
4654 }
4655 }
4656 else if (pFid->fFlags & UDF_FILE_FLAGS_PARENT)
4657 {
4658 wszName[0] = '.';
4659 wszName[1] = '.';
4660 cwcName = 2;
4661 }
4662 else
4663 {
4664 RTUtf16CopyAscii(wszName, RT_ELEMENTS(wszName), "<empty>");
4665 cwcName = 7;
4666 }
4667 wszName[cwcName] = '\0';
4668
4669 Log2(("ISO/UDF: %04x: fFlags=%#x uVer=%u Icb={%#04x:%#010RX32 LB %#06x t=%u} cbName=%#04x cbIU=%#x '%ls'\n",
4670 offDesc,
4671 pFid->fFlags,
4672 pFid->uVersion,
4673 pFid->Icb.Location.uPartitionNo,
4674 pFid->Icb.Location.off,
4675 pFid->Icb.cb,
4676 pFid->Icb.uType,
4677 pFid->cbName,
4678 pFid->cbImplementationUse,
4679 wszName));
4680 int rc = rtFsIsoVolValidateUdfDescTagAndCrc(&pFid->Tag, pThis->cbDir - offDesc,
4681 UDF_TAG_ID_FILE_ID_DESC, pFid->Tag.offTag, NULL);
4682 if (RT_FAILURE(rc))
4683 Log2(("ISO/UDF: Bad Tag: %Rrc - idTag=%#x\n", rc, pFid->Tag.idTag));
4684 if (pFid->cbImplementationUse > 32)
4685 Log2(("ISO/UDF: impl use (%#x bytes):\n%.*RhxD\n",
4686 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
4687 else if (pFid->cbImplementationUse > 0)
4688 Log2(("ISO/UDF: impl use (%#x bytes): %.*Rhxs\n",
4689 pFid->cbImplementationUse, pFid->cbImplementationUse, pFid->abImplementationUse));
4690
4691 /* advance */
4692 offDesc += cbFid;
4693 }
4694
4695 if (offDesc < pThis->cbDir)
4696 Log2(("ISO/UDF: warning! %#x trailing bytes in directory:\n%.*RhxD\n",
4697 pThis->cbDir - offDesc, pThis->cbDir - offDesc, &pThis->pbDir[offDesc]));
4698 }
4699}
4700#endif /* LOG_ENABLED */
4701
4702
4703/**
4704 * Instantiates a new shared directory structure, given UDF descriptors.
4705 *
4706 * @returns IPRT status code.
4707 * @param pThis The ISO volume instance.
4708 * @param pParentDir The parent directory. This is NULL for the root
4709 * directory.
4710 * @param pAllocDesc The allocation descriptor for the directory ICB.
4711 * @param pFileIdDesc The file ID descriptor. This is NULL for the root.
4712 * @param offInDir The offset of the file ID descriptor in the parent
4713 * directory. This is used when looking up shared
4714 * directory objects. (Pass 0 for root.)
4715 * @param ppShared Where to return the shared directory structure.
4716 */
4717static int rtFsIsoDirShrd_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFLONGAD pAllocDesc,
4718 PCUDFFILEIDDESC pFileIdDesc, uintptr_t offInDir, PRTFSISODIRSHRD *ppShared)
4719{
4720 /*
4721 * Allocate a new structure and initialize it.
4722 */
4723 int rc = VERR_NO_MEMORY;
4724 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
4725 if (pShared)
4726 {
4727 rc = rtFsIsoCore_InitFromUdfIcbAndFileIdDesc(&pShared->Core, pAllocDesc, pFileIdDesc, offInDir, pThis);
4728 if (RT_SUCCESS(rc))
4729 {
4730 RTListInit(&pShared->OpenChildren);
4731
4732 if (pShared->Core.cbObject < RTFSISO_MAX_DIR_SIZE)
4733 {
4734 pShared->cbDir = (uint32_t)pShared->Core.cbObject;
4735 pShared->pbDir = (uint8_t *)RTMemAllocZ(RT_MAX(RT_ALIGN_32(pShared->cbDir, 512), 512));
4736 if (pShared->pbDir)
4737 {
4738 rc = rtFsIsoCore_ReadWorker(&pShared->Core, 0, pShared->pbDir, pShared->cbDir, NULL, NULL);
4739 if (RT_SUCCESS(rc))
4740 {
4741#ifdef LOG_ENABLED
4742 rtFsIsoDirShrd_LogUdfContent(pShared);
4743#endif
4744
4745 /*
4746 * Link into parent directory so we can use it to update
4747 * our directory entry.
4748 */
4749 if (pParentDir)
4750 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
4751 *ppShared = pShared;
4752 return VINF_SUCCESS;
4753 }
4754 }
4755 else
4756 rc = VERR_NO_MEMORY;
4757 }
4758 }
4759 RTMemFree(pShared);
4760 }
4761
4762 *ppShared = NULL;
4763 return rc;
4764}
4765
4766
4767/**
4768 * Instantiates a new directory with a shared structure presupplied.
4769 *
4770 * @returns IPRT status code.
4771 * @param pThis The ISO volume instance.
4772 * @param pShared Referenced pointer to the shared structure. The
4773 * reference is always CONSUMED.
4774 * @param phVfsDir Where to return the directory handle.
4775 */
4776static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir)
4777{
4778 /*
4779 * Create VFS object around the shared structure.
4780 */
4781 PRTFSISODIROBJ pNewDir;
4782 int rc = RTVfsNewDir(&g_rtFsIsoDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
4783 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
4784 if (RT_SUCCESS(rc))
4785 {
4786 /*
4787 * Look for existing shared object, create a new one if necessary.
4788 * We CONSUME a reference to pShared here.
4789 */
4790 pNewDir->offDir = 0;
4791 pNewDir->pShared = pShared;
4792 return VINF_SUCCESS;
4793 }
4794
4795 rtFsIsoDirShrd_Release(pShared);
4796 *phVfsDir = NIL_RTVFSDIR;
4797 return rc;
4798}
4799
4800
4801
4802/**
4803 * Instantiates a new directory VFS instance for ISO 9660, creating the shared
4804 * structure as necessary.
4805 *
4806 * @returns IPRT status code.
4807 * @param pThis The ISO volume instance.
4808 * @param pParentDir The parent directory. This is NULL for the root
4809 * directory.
4810 * @param pDirRec The directory record.
4811 * @param cDirRecs Number of directory records if more than one.
4812 * @param offDirRec The byte offset of the directory record.
4813 * @param pRockInfo Optional pointer to rock ridge info for the entry.
4814 * @param phVfsDir Where to return the directory handle.
4815 */
4816static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
4817 uint32_t cDirRecs, uint64_t offDirRec, PCRTFSISOROCKINFO pRockInfo, PRTVFSDIR phVfsDir)
4818{
4819 /*
4820 * Look for existing shared object, create a new one if necessary.
4821 */
4822 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
4823 if (!pShared)
4824 {
4825 int rc = rtFsIsoDirShrd_New9660(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, pRockInfo, &pShared);
4826 if (RT_FAILURE(rc))
4827 {
4828 *phVfsDir = NIL_RTVFSDIR;
4829 return rc;
4830 }
4831 }
4832 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
4833}
4834
4835
4836/**
4837 * Instantiates a new directory VFS instance for UDF, creating the shared
4838 * structure as necessary.
4839 *
4840 * @returns IPRT status code.
4841 * @param pThis The ISO volume instance.
4842 * @param pParentDir The parent directory.
4843 * @param pFid The file ID descriptor for the directory.
4844 * @param phVfsDir Where to return the directory handle.
4845 */
4846static int rtFsIsoDir_NewUdf(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCUDFFILEIDDESC pFid, PRTVFSDIR phVfsDir)
4847{
4848 Assert(pFid);
4849 Assert(pParentDir);
4850 uintptr_t const offInDir = (uintptr_t)pFid - (uintptr_t)pParentDir->pbDir;
4851 Assert(offInDir < pParentDir->cbDir);
4852
4853 /*
4854 * Look for existing shared object, create a new one if necessary.
4855 */
4856 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offInDir);
4857 if (!pShared)
4858 {
4859 int rc = rtFsIsoDirShrd_NewUdf(pThis, pParentDir, &pFid->Icb, pFid, offInDir, &pShared);
4860 if (RT_FAILURE(rc))
4861 {
4862 *phVfsDir = NIL_RTVFSDIR;
4863 return rc;
4864 }
4865 }
4866 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
4867}
4868
4869
4870/**
4871 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
4872 */
4873static DECLCALLBACK(int) rtFsIsoVol_Close(void *pvThis)
4874{
4875 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
4876 Log(("rtFsIsoVol_Close(%p)\n", pThis));
4877
4878 if (pThis->pRootDir)
4879 {
4880 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
4881 Assert(pThis->pRootDir->Core.cRefs == 1);
4882 rtFsIsoDirShrd_Release(pThis->pRootDir);
4883 pThis->pRootDir = NULL;
4884 }
4885
4886 RTVfsFileRelease(pThis->hVfsBacking);
4887 pThis->hVfsBacking = NIL_RTVFSFILE;
4888
4889 if (RTCritSectIsInitialized(&pThis->RockBufLock))
4890 RTCritSectDelete(&pThis->RockBufLock);
4891
4892 return VINF_SUCCESS;
4893}
4894
4895
4896/**
4897 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
4898 */
4899static DECLCALLBACK(int) rtFsIsoVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4900{
4901 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
4902 return VERR_WRONG_TYPE;
4903}
4904
4905
4906static int rtFsIsoVol_ReturnUdfDString(const char *pachSrc, size_t cchSrc, void *pvDst, size_t cbDst, size_t *pcbRet)
4907{
4908 char *pszDst = (char *)pvDst;
4909
4910 if (pachSrc[0] == 8)
4911 {
4912 size_t const cchText = RT_MIN((uint8_t)pachSrc[cchSrc - 1], cchSrc - 2);
4913 size_t const cchActual = RTStrNLen(&pachSrc[1], cchText);
4914 *pcbRet = cchActual + 1;
4915 int rc = RTStrCopyEx(pszDst, cbDst, &pachSrc[1], cchActual);
4916 if (cbDst > 0)
4917 RTStrPurgeEncoding(pszDst);
4918 return rc;
4919 }
4920
4921 if (pachSrc[0] == 16)
4922 {
4923 PCRTUTF16 pwszSrc = (PCRTUTF16)&pachSrc[1];
4924 if (cchSrc > 0)
4925 return RTUtf16BigToUtf8Ex(pwszSrc, (cchSrc - 2) / sizeof(RTUTF16), &pszDst, cchSrc, pcbRet);
4926 int rc = RTUtf16CalcUtf8LenEx(pwszSrc, (cchSrc - 2) / sizeof(RTUTF16), pcbRet);
4927 if (RT_SUCCESS(rc))
4928 {
4929 *pcbRet += 1;
4930 return VERR_BUFFER_OVERFLOW;
4931 }
4932 return rc;
4933 }
4934
4935 if (ASMMemIsZero(pachSrc, cchSrc))
4936 {
4937 *pcbRet = 1;
4938 if (cbDst >= 1)
4939 {
4940 *pszDst = '\0';
4941 return VINF_SUCCESS;
4942 }
4943 return VERR_BUFFER_OVERFLOW;
4944 }
4945
4946 *pcbRet = 0;
4947 return VERR_INVALID_UTF8_ENCODING; /** @todo better status here */
4948}
4949
4950
4951/**
4952 * For now this is a sanitized version of rtFsIsoVolGetMaybeUtf16Be, which is
4953 * probably not correct or anything, but will have to do for now.
4954 */
4955static int rtFsIsoVol_ReturnIso9660D1String(const char *pachSrc, size_t cchSrc, void *pvDst, size_t cbDst, size_t *pcbRet)
4956{
4957 char *pszDst = (char *)pvDst;
4958
4959 /*
4960 * Check if it may be some UTF16 variant by scanning for zero bytes
4961 * (ISO-9660 doesn't allow zeros).
4962 */
4963 size_t cFirstZeros = 0;
4964 size_t cSecondZeros = 0;
4965 for (size_t off = 0; off + 1 < cchSrc; off += 2)
4966 {
4967 cFirstZeros += pachSrc[off] == '\0';
4968 cSecondZeros += pachSrc[off + 1] == '\0';
4969 }
4970 if (cFirstZeros > cSecondZeros)
4971 {
4972 /*
4973 * UTF-16BE / UTC-2BE:
4974 */