VirtualBox

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

最後變更 在這個檔案從68949是 68921,由 vboxsync 提交於 7 年 前

iprt: udf reading updates

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 142.0 KB
 
1/* $Id: isovfs.cpp 68921 2017-09-29 08:44:14Z vboxsync $ */
2/** @file
3 * IPRT - ISO 9660 and UDF Virtual Filesystem (read only).
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal/iprt.h"
33#include <iprt/fsvfs.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/err.h>
38#include <iprt/crc.h>
39#include <iprt/ctype.h>
40#include <iprt/file.h>
41#include <iprt/log.h>
42#include <iprt/mem.h>
43#include <iprt/poll.h>
44#include <iprt/string.h>
45#include <iprt/thread.h>
46#include <iprt/vfs.h>
47#include <iprt/vfslowlevel.h>
48#include <iprt/formats/iso9660.h>
49#include <iprt/formats/udf.h>
50
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56/** Pointer to an ISO volume (VFS instance data). */
57typedef struct RTFSISOVOL *PRTFSISOVOL;
58/** Pointer to a const ISO volume (VFS instance data). */
59typedef struct RTFSISOVOL const *PCRTFSISOVOL;
60
61/** Pointer to a ISO directory instance. */
62typedef struct RTFSISODIRSHRD *PRTFSISODIRSHRD;
63
64
65
66/**
67 * ISO extent (internal to the VFS not a disk structure).
68 */
69typedef struct RTFSISOEXTENT
70{
71 /** The disk offset. */
72 uint64_t offDisk;
73 /** The size of the extent in bytes. */
74 uint64_t cbExtent;
75} RTFSISOEXTENT;
76/** Pointer to an ISO 9660 extent. */
77typedef RTFSISOEXTENT *PRTFSISOEXTENT;
78/** Pointer to a const ISO 9660 extent. */
79typedef RTFSISOEXTENT const *PCRTFSISOEXTENT;
80
81
82/**
83 * ISO file system object, shared part.
84 */
85typedef struct RTFSISOCORE
86{
87 /** The parent directory keeps a list of open objects (RTFSISOCORE). */
88 RTLISTNODE Entry;
89 /** Reference counter. */
90 uint32_t volatile cRefs;
91 /** The parent directory (not released till all children are close). */
92 PRTFSISODIRSHRD pParentDir;
93 /** The byte offset of the first directory record. */
94 uint64_t offDirRec;
95 /** Attributes. */
96 RTFMODE fAttrib;
97 /** The object size. */
98 uint64_t cbObject;
99 /** The access time. */
100 RTTIMESPEC AccessTime;
101 /** The modificaton time. */
102 RTTIMESPEC ModificationTime;
103 /** The change time. */
104 RTTIMESPEC ChangeTime;
105 /** The birth time. */
106 RTTIMESPEC BirthTime;
107 /** Pointer to the volume. */
108 PRTFSISOVOL pVol;
109 /** The version number. */
110 uint32_t uVersion;
111 /** Number of extents. */
112 uint32_t cExtents;
113 /** The first extent. */
114 RTFSISOEXTENT FirstExtent;
115 /** Array of additional extents. */
116 PRTFSISOEXTENT paExtents;
117} RTFSISOCORE;
118typedef RTFSISOCORE *PRTFSISOCORE;
119
120/**
121 * ISO file, shared data.
122 */
123typedef struct RTFSISOFILESHRD
124{
125 /** Core ISO9660 object info. */
126 RTFSISOCORE Core;
127} RTFSISOFILESHRD;
128/** Pointer to a ISO 9660 file object. */
129typedef RTFSISOFILESHRD *PRTFSISOFILESHRD;
130
131
132/**
133 * ISO directory, shared data.
134 *
135 * We will always read in the whole directory just to keep things really simple.
136 */
137typedef struct RTFSISODIRSHRD
138{
139 /** Core ISO 9660 object info. */
140 RTFSISOCORE Core;
141 /** Open child objects (RTFSISOCORE). */
142 RTLISTNODE OpenChildren;
143
144 /** Pointer to the directory content. */
145 uint8_t *pbDir;
146 /** The size of the directory content (duplicate of Core.cbObject). */
147 uint32_t cbDir;
148} RTFSISODIRSHRD;
149/** Pointer to a ISO directory instance. */
150typedef RTFSISODIRSHRD *PRTFSISODIRSHRD;
151
152
153/**
154 * Private data for a VFS file object.
155 */
156typedef struct RTFSISOFILEOBJ
157{
158 /** Pointer to the shared data. */
159 PRTFSISOFILESHRD pShared;
160 /** The current file offset. */
161 uint64_t offFile;
162} RTFSISOFILEOBJ;
163typedef RTFSISOFILEOBJ *PRTFSISOFILEOBJ;
164
165/**
166 * Private data for a VFS directory object.
167 */
168typedef struct RTFSISODIROBJ
169{
170 /** Pointer to the shared data. */
171 PRTFSISODIRSHRD pShared;
172 /** The current directory offset. */
173 uint32_t offDir;
174} RTFSISODIROBJ;
175typedef RTFSISODIROBJ *PRTFSISODIROBJ;
176
177/**
178 * Information about a logical partition.
179 */
180typedef struct RTFSISOVOLUDFPART
181{
182 /** Partition number (not index). */
183 uint16_t uPartitionNo;
184 uint16_t fFlags;
185 /** Number of sectors. */
186 uint32_t cSectors;
187#if 0
188
189
190 /** 0x000: The descriptor tag (UDF_TAG_ID_PARTITION_DESC). */
191 UDFTAG Tag;
192 /** 0x010: Volume descriptor sequence number. */
193 uint32_t uVolumeDescSeqNo;
194 /** 0x014: The partition flags (UDF_PARTITION_FLAGS_XXX). */
195 uint16_t fFlags;
196 /** 0x016: The partition number. */
197 uint16_t uPartitionNo;
198 /** 0x018: Partition contents (UDF_ENTITY_ID_PD_PARTITION_CONTENTS_XXX). */
199 UDFENTITYID PartitionContents;
200 /** 0x038: partition contents use (depends on the PartitionContents field). */
201 union
202 {
203 /** Generic view. */
204 uint8_t ab[128];
205 /** UDF partition header descriptor (UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF). */
206 UDFPARTITIONHDRDESC Hdr;
207 } ContentsUse;
208 /** 0x0b8: Access type (UDF_PART_ACCESS_TYPE_XXX). */
209 uint32_t uAccessType;
210 /** 0x0bc: Partition starting location (logical sector number). */
211 uint32_t offLocation;
212 /** 0x0c0: Partition length in sectors. */
213 uint32_t cSectors;
214 /** 0x0c4: Implementation identifier ("*Developer ID"). */
215 UDFENTITYID idImplementation;
216 /** 0x0e4: Implemenation use bytes. */
217 union
218 {
219 /** Generic view. */
220 uint8_t ab[128];
221 } ImplementationUse;
222 /** 0x164: Reserved. */
223 uint8_t abReserved[156];
224#endif
225
226} RTFSISOVOLUDFPART;
227
228
229/**
230 * A ISO volume.
231 */
232typedef struct RTFSISOVOL
233{
234 /** Handle to itself. */
235 RTVFS hVfsSelf;
236 /** The file, partition, or whatever backing the ISO 9660 volume. */
237 RTVFSFILE hVfsBacking;
238 /** The size of the backing thingy. */
239 uint64_t cbBacking;
240 /** The size of the backing thingy in sectors (cbSector). */
241 uint64_t cBackingSectors;
242 /** Flags. */
243 uint32_t fFlags;
244 /** The sector size (in bytes). */
245 uint32_t cbSector;
246
247 /** @name ISO 9660 specific data
248 * @{ */
249 /** The size of a logical block in bytes. */
250 uint32_t cbBlock;
251 /** The primary volume space size in blocks. */
252 uint32_t cBlocksInPrimaryVolumeSpace;
253 /** The primary volume space size in bytes. */
254 uint64_t cbPrimaryVolumeSpace;
255 /** The number of volumes in the set. */
256 uint32_t cVolumesInSet;
257 /** The primary volume sequence ID. */
258 uint32_t idPrimaryVol;
259 /** Set if using UTF16-2 (joliet). */
260 bool fIsUtf16;
261 /** @} */
262
263 /** UDF specific data. */
264 struct
265 {
266 /** Offset of the Anchor volume descriptor sequence. */
267 uint64_t offAvdp;
268 /** Length of the anchor volume descriptor sequence. */
269 uint32_t cbAvdp;
270 /** The UDF level. */
271 uint8_t uLevel;
272 /** Number of volume sets. */
273 uint8_t cVolumeSets;
274 /** Set if we already seen the primary volume descriptor. */
275 bool fSeenPrimaryDesc : 1;
276#if 0
277 /** Volume sets. */
278 struct
279 {
280 /** Number of partitions in the set. */
281 uint16_t cPartitions;
282
283
284 } aVolumeSets[1];
285
286
287 /** Partitions. */
288 struct
289 {
290
291 } aPartitions[]
292#endif
293 } udf;
294
295 /** The root directory shared data. */
296 PRTFSISODIRSHRD pRootDir;
297} RTFSISOVOL;
298
299
300
301/*********************************************************************************************************************************
302* Global Variables *
303*********************************************************************************************************************************/
304
305/*********************************************************************************************************************************
306* Internal Functions *
307*********************************************************************************************************************************/
308static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
309static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild);
310static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
311 uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir);
312static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec);
313
314
315/**
316 * Returns the length of the version suffix in the given name.
317 *
318 * @returns Number of UTF16-BE chars in the version suffix.
319 * @param pawcName The name to examine.
320 * @param cwcName The length of the name.
321 * @param puValue Where to return the value.
322 */
323static size_t rtFsIso9660GetVersionLengthUtf16Big(PCRTUTF16 pawcName, size_t cwcName, uint32_t *puValue)
324{
325 *puValue = 0;
326
327 /* -1: */
328 if (cwcName <= 2)
329 return 0;
330 RTUTF16 wc1 = RT_BE2H_U16(pawcName[cwcName - 1]);
331 if (!RT_C_IS_DIGIT(wc1))
332 return 0;
333 Assert(wc1 < 0x3a); /* ASSUMES the RT_C_IS_DIGIT macro works just fine on wide chars too. */
334
335 /* -2: */
336 RTUTF16 wc2 = RT_BE2H_U16(pawcName[cwcName - 2]);
337 if (wc2 == ';')
338 {
339 *puValue = wc1 - '0';
340 return 2;
341 }
342 if (!RT_C_IS_DIGIT(wc2) || cwcName <= 3)
343 return 0;
344
345 /* -3: */
346 RTUTF16 wc3 = RT_BE2H_U16(pawcName[cwcName - 3]);
347 if (wc3 == ';')
348 {
349 *puValue = (wc1 - '0')
350 + (wc2 - '0') * 10;
351 return 3;
352 }
353 if (!RT_C_IS_DIGIT(wc3) || cwcName <= 4)
354 return 0;
355
356 /* -4: */
357 RTUTF16 wc4 = RT_BE2H_U16(pawcName[cwcName - 4]);
358 if (wc4 == ';')
359 {
360 *puValue = (wc1 - '0')
361 + (wc2 - '0') * 10
362 + (wc3 - '0') * 100;
363 return 4;
364 }
365 if (!RT_C_IS_DIGIT(wc4) || cwcName <= 5)
366 return 0;
367
368 /* -5: */
369 RTUTF16 wc5 = RT_BE2H_U16(pawcName[cwcName - 5]);
370 if (wc5 == ';')
371 {
372 *puValue = (wc1 - '0')
373 + (wc2 - '0') * 10
374 + (wc3 - '0') * 100
375 + (wc4 - '0') * 1000;
376 return 5;
377 }
378 if (!RT_C_IS_DIGIT(wc5) || cwcName <= 6)
379 return 0;
380
381 /* -6: */
382 RTUTF16 wc6 = RT_BE2H_U16(pawcName[cwcName - 6]);
383 if (wc6 == ';')
384 {
385 *puValue = (wc1 - '0')
386 + (wc2 - '0') * 10
387 + (wc3 - '0') * 100
388 + (wc4 - '0') * 1000
389 + (wc5 - '0') * 10000;
390 return 6;
391 }
392 return 0;
393}
394
395
396/**
397 * Returns the length of the version suffix in the given name.
398 *
399 * @returns Number of chars in the version suffix.
400 * @param pachName The name to examine.
401 * @param cchName The length of the name.
402 * @param puValue Where to return the value.
403 */
404static size_t rtFsIso9660GetVersionLengthAscii(const char *pachName, size_t cchName, uint32_t *puValue)
405{
406 *puValue = 0;
407
408 /* -1: */
409 if (cchName <= 2)
410 return 0;
411 char ch1 = pachName[cchName - 1];
412 if (!RT_C_IS_DIGIT(ch1))
413 return 0;
414
415 /* -2: */
416 char ch2 = pachName[cchName - 2];
417 if (ch2 == ';')
418 {
419 *puValue = ch1 - '0';
420 return 2;
421 }
422 if (!RT_C_IS_DIGIT(ch2) || cchName <= 3)
423 return 0;
424
425 /* -3: */
426 char ch3 = pachName[cchName - 3];
427 if (ch3 == ';')
428 {
429 *puValue = (ch1 - '0')
430 + (ch2 - '0') * 10;
431 return 3;
432 }
433 if (!RT_C_IS_DIGIT(ch3) || cchName <= 4)
434 return 0;
435
436 /* -4: */
437 char ch4 = pachName[cchName - 4];
438 if (ch4 == ';')
439 {
440 *puValue = (ch1 - '0')
441 + (ch2 - '0') * 10
442 + (ch3 - '0') * 100;
443 return 4;
444 }
445 if (!RT_C_IS_DIGIT(ch4) || cchName <= 5)
446 return 0;
447
448 /* -5: */
449 char ch5 = pachName[cchName - 5];
450 if (ch5 == ';')
451 {
452 *puValue = (ch1 - '0')
453 + (ch2 - '0') * 10
454 + (ch3 - '0') * 100
455 + (ch4 - '0') * 1000;
456 return 5;
457 }
458 if (!RT_C_IS_DIGIT(ch5) || cchName <= 6)
459 return 0;
460
461 /* -6: */
462 if (pachName[cchName - 6] == ';')
463 {
464 *puValue = (ch1 - '0')
465 + (ch2 - '0') * 10
466 + (ch3 - '0') * 100
467 + (ch4 - '0') * 1000
468 + (ch5 - '0') * 10000;
469 return 6;
470 }
471 return 0;
472}
473
474
475/**
476 * Converts a ISO 9660 binary timestamp into an IPRT timesspec.
477 *
478 * @param pTimeSpec Where to return the IRPT time.
479 * @param pIso9660 The ISO 9660 binary timestamp.
480 */
481static void rtFsIso9660DateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
482{
483 RTTIME Time;
484 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
485 Time.offUTC = 0;
486 Time.i32Year = pIso9660->bYear + 1900;
487 Time.u8Month = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
488 Time.u8MonthDay = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
489 Time.u8WeekDay = UINT8_MAX;
490 Time.u16YearDay = 0;
491 Time.u8Hour = RT_MIN(pIso9660->bHour, 23);
492 Time.u8Minute = RT_MIN(pIso9660->bMinute, 59);
493 Time.u8Second = RT_MIN(pIso9660->bSecond, 59);
494 Time.u32Nanosecond = 0;
495 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
496
497 /* Only apply the UTC offset if it's within reasons. */
498 if (RT_ABS(pIso9660->offUtc) <= 13*4)
499 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
500}
501
502
503/**
504 * Initialization of a RTFSISOCORE structure from a directory record.
505 *
506 * @note The RTFSISOCORE::pParentDir and RTFSISOCORE::Clusters members are
507 * properly initialized elsewhere.
508 *
509 * @returns IRPT status code. Either VINF_SUCCESS or VERR_NO_MEMORY, the latter
510 * only if @a cDirRecs is above 1.
511 * @param pCore The structure to initialize.
512 * @param pDirRec The primary directory record.
513 * @param cDirRecs Number of directory records.
514 * @param offDirRec The offset of the primary directory record.
515 * @param uVersion The file version number.
516 * @param pVol The volume.
517 */
518static int rtFsIsoCore_InitFrom9660DirRec(PRTFSISOCORE pCore, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
519 uint64_t offDirRec, uint32_t uVersion, PRTFSISOVOL pVol)
520{
521 RTListInit(&pCore->Entry);
522 pCore->cRefs = 1;
523 pCore->pParentDir = NULL;
524 pCore->pVol = pVol;
525 pCore->offDirRec = offDirRec;
526 pCore->fAttrib = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
527 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
528 : 0644 | RTFS_TYPE_FILE;
529 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
530 pCore->fAttrib |= RTFS_DOS_HIDDEN;
531 pCore->cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
532 pCore->uVersion = uVersion;
533 pCore->cExtents = 1;
534 pCore->FirstExtent.cbExtent = pCore->cbObject;
535 pCore->FirstExtent.offDisk = (ISO9660_GET_ENDIAN(&pDirRec->offExtent) + pDirRec->cExtAttrBlocks) * (uint64_t)pVol->cbBlock;
536
537 rtFsIso9660DateTime2TimeSpec(&pCore->ModificationTime, &pDirRec->RecTime);
538 pCore->BirthTime = pCore->ModificationTime;
539 pCore->AccessTime = pCore->ModificationTime;
540 pCore->ChangeTime = pCore->ModificationTime;
541
542 /*
543 * Deal with multiple extents.
544 */
545 if (RT_LIKELY(cDirRecs == 1))
546 { /* done */ }
547 else
548 {
549 PRTFSISOEXTENT pCurExtent = &pCore->FirstExtent;
550 while (cDirRecs > 1)
551 {
552 offDirRec += pDirRec->cbDirRec;
553 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
554 if (pDirRec->cbDirRec != 0)
555 {
556 uint64_t offDisk = ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)pVol->cbBlock;
557 uint32_t cbExtent = ISO9660_GET_ENDIAN(&pDirRec->cbData);
558 pCore->cbObject += cbExtent;
559
560 if (pCurExtent->offDisk + pCurExtent->cbExtent == offDisk)
561 pCurExtent->cbExtent += cbExtent;
562 else
563 {
564 void *pvNew = RTMemRealloc(pCore->paExtents, pCore->cExtents * sizeof(pCore->paExtents[0]));
565 if (pvNew)
566 pCore->paExtents = (PRTFSISOEXTENT)pvNew;
567 else
568 {
569 RTMemFree(pCore->paExtents);
570 return VERR_NO_MEMORY;
571 }
572 pCurExtent = &pCore->paExtents[pCore->cExtents - 1];
573 pCurExtent->cbExtent = cbExtent;
574 pCurExtent->offDisk = offDisk;
575 pCore->cExtents++;
576 }
577 cDirRecs--;
578 }
579 else
580 {
581 size_t cbSkip = (offDirRec + pVol->cbSector) & ~(pVol->cbSector - 1U);
582 offDirRec += cbSkip;
583 pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + cbSkip);
584 }
585 }
586 }
587 return VINF_SUCCESS;
588}
589
590
591/**
592 * Worker for rtFsIsoFile_QueryInfo and rtFsIsoDir_QueryInfo.
593 */
594static int rtFsIsoCore_QueryInfo(PRTFSISOCORE pCore, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
595{
596 pObjInfo->cbObject = pCore->cbObject;
597 pObjInfo->cbAllocated = RT_ALIGN_64(pCore->cbObject, pCore->pVol->cbBlock);
598 pObjInfo->AccessTime = pCore->AccessTime;
599 pObjInfo->ModificationTime = pCore->ModificationTime;
600 pObjInfo->ChangeTime = pCore->ChangeTime;
601 pObjInfo->BirthTime = pCore->BirthTime;
602 pObjInfo->Attr.fMode = pCore->fAttrib;
603 pObjInfo->Attr.enmAdditional = enmAddAttr;
604
605 switch (enmAddAttr)
606 {
607 case RTFSOBJATTRADD_NOTHING: /* fall thru */
608 case RTFSOBJATTRADD_UNIX:
609 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
610 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
611 pObjInfo->Attr.u.Unix.cHardlinks = 1;
612 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
613 pObjInfo->Attr.u.Unix.INodeId = 0; /* Could probably use the directory entry offset. */
614 pObjInfo->Attr.u.Unix.fFlags = 0;
615 pObjInfo->Attr.u.Unix.GenerationId = pCore->uVersion;
616 pObjInfo->Attr.u.Unix.Device = 0;
617 break;
618 case RTFSOBJATTRADD_UNIX_OWNER:
619 pObjInfo->Attr.u.UnixOwner.uid = 0;
620 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
621 break;
622 case RTFSOBJATTRADD_UNIX_GROUP:
623 pObjInfo->Attr.u.UnixGroup.gid = 0;
624 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
625 break;
626 case RTFSOBJATTRADD_EASIZE:
627 pObjInfo->Attr.u.EASize.cb = 0;
628 break;
629 default:
630 return VERR_INVALID_PARAMETER;
631 }
632 return VINF_SUCCESS;
633}
634
635
636/**
637 * Worker for rtFsIsoFile_Close and rtFsIsoDir_Close that does common work.
638 *
639 * @param pCore The common shared structure.
640 */
641static void rtFsIsoCore_Destroy(PRTFSISOCORE pCore)
642{
643 if (pCore->pParentDir)
644 rtFsIsoDirShrd_RemoveOpenChild(pCore->pParentDir, pCore);
645 if (pCore->paExtents)
646 {
647 RTMemFree(pCore->paExtents);
648 pCore->paExtents = NULL;
649 }
650}
651
652
653/**
654 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
655 */
656static DECLCALLBACK(int) rtFsIsoFile_Close(void *pvThis)
657{
658 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
659 LogFlow(("rtFsIsoFile_Close(%p/%p)\n", pThis, pThis->pShared));
660
661 PRTFSISOFILESHRD pShared = pThis->pShared;
662 pThis->pShared = NULL;
663 if (pShared)
664 {
665 if (ASMAtomicDecU32(&pShared->Core.cRefs) == 0)
666 {
667 LogFlow(("rtFsIsoFile_Close: Destroying shared structure %p\n", pShared));
668 rtFsIsoCore_Destroy(&pShared->Core);
669 RTMemFree(pShared);
670 }
671 }
672 return VINF_SUCCESS;
673}
674
675
676/**
677 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
678 */
679static DECLCALLBACK(int) rtFsIsoFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
680{
681 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
682 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
683}
684
685
686/**
687 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
688 */
689static DECLCALLBACK(int) rtFsIsoFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
690{
691 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
692 PRTFSISOFILESHRD pShared = pThis->pShared;
693 AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
694 RT_NOREF(fBlocking);
695
696 /*
697 * Check for EOF.
698 */
699 if (off == -1)
700 off = pThis->offFile;
701 if ((uint64_t)off >= pShared->Core.cbObject)
702 {
703 if (pcbRead)
704 {
705 *pcbRead = 0;
706 return VINF_EOF;
707 }
708 return VERR_EOF;
709 }
710
711
712 /*
713 * Simple case: File has a single extent.
714 */
715 int rc = VINF_SUCCESS;
716 size_t cbRead = 0;
717 uint64_t cbFileLeft = pShared->Core.cbObject - (uint64_t)off;
718 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
719 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
720 if (pShared->Core.cExtents == 1)
721 {
722 if (cbLeft > 0)
723 {
724 size_t cbToRead = cbLeft;
725 if (cbToRead > cbFileLeft)
726 cbToRead = (size_t)cbFileLeft;
727 rc = RTVfsFileReadAt(pShared->Core.pVol->hVfsBacking, pShared->Core.FirstExtent.offDisk + off, pbDst, cbToRead, NULL);
728 if (RT_SUCCESS(rc))
729 {
730 off += cbToRead;
731 pbDst += cbToRead;
732 cbRead += cbToRead;
733 cbFileLeft -= cbToRead;
734 cbLeft -= cbToRead;
735 }
736 }
737 }
738 /*
739 * Complicated case: Work the file content extent by extent.
740 */
741 else
742 {
743 return VERR_NOT_IMPLEMENTED; /** @todo multi-extent stuff . */
744 }
745
746 /* Update the offset and return. */
747 pThis->offFile = off;
748 if (pcbRead)
749 *pcbRead = cbRead;
750 return VINF_SUCCESS;
751}
752
753
754/**
755 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
756 */
757static DECLCALLBACK(int) rtFsIsoFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
758{
759 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
760 return VERR_WRITE_PROTECT;
761}
762
763
764/**
765 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
766 */
767static DECLCALLBACK(int) rtFsIsoFile_Flush(void *pvThis)
768{
769 RT_NOREF(pvThis);
770 return VINF_SUCCESS;
771}
772
773
774/**
775 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
776 */
777static DECLCALLBACK(int) rtFsIsoFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
778 uint32_t *pfRetEvents)
779{
780 NOREF(pvThis);
781 int rc;
782 if (fEvents != RTPOLL_EVT_ERROR)
783 {
784 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
785 rc = VINF_SUCCESS;
786 }
787 else if (fIntr)
788 rc = RTThreadSleep(cMillies);
789 else
790 {
791 uint64_t uMsStart = RTTimeMilliTS();
792 do
793 rc = RTThreadSleep(cMillies);
794 while ( rc == VERR_INTERRUPTED
795 && !fIntr
796 && RTTimeMilliTS() - uMsStart < cMillies);
797 if (rc == VERR_INTERRUPTED)
798 rc = VERR_TIMEOUT;
799 }
800 return rc;
801}
802
803
804/**
805 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
806 */
807static DECLCALLBACK(int) rtFsIsoFile_Tell(void *pvThis, PRTFOFF poffActual)
808{
809 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
810 *poffActual = pThis->offFile;
811 return VINF_SUCCESS;
812}
813
814
815/**
816 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
817 */
818static DECLCALLBACK(int) rtFsIsoFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
819{
820 RT_NOREF(pvThis, fMode, fMask);
821 return VERR_WRITE_PROTECT;
822}
823
824
825/**
826 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
827 */
828static DECLCALLBACK(int) rtFsIsoFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
829 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
830{
831 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
832 return VERR_WRITE_PROTECT;
833}
834
835
836/**
837 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
838 */
839static DECLCALLBACK(int) rtFsIsoFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
840{
841 RT_NOREF(pvThis, uid, gid);
842 return VERR_WRITE_PROTECT;
843}
844
845
846/**
847 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
848 */
849static DECLCALLBACK(int) rtFsIsoFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
850{
851 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
852 RTFOFF offNew;
853 switch (uMethod)
854 {
855 case RTFILE_SEEK_BEGIN:
856 offNew = offSeek;
857 break;
858 case RTFILE_SEEK_END:
859 offNew = (RTFOFF)pThis->pShared->Core.cbObject + offSeek;
860 break;
861 case RTFILE_SEEK_CURRENT:
862 offNew = (RTFOFF)pThis->offFile + offSeek;
863 break;
864 default:
865 return VERR_INVALID_PARAMETER;
866 }
867 if (offNew >= 0)
868 {
869 if (offNew <= _4G)
870 {
871 pThis->offFile = offNew;
872 *poffActual = offNew;
873 return VINF_SUCCESS;
874 }
875 return VERR_OUT_OF_RANGE;
876 }
877 return VERR_NEGATIVE_SEEK;
878}
879
880
881/**
882 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
883 */
884static DECLCALLBACK(int) rtFsIsoFile_QuerySize(void *pvThis, uint64_t *pcbFile)
885{
886 PRTFSISOFILEOBJ pThis = (PRTFSISOFILEOBJ)pvThis;
887 *pcbFile = pThis->pShared->Core.cbObject;
888 return VINF_SUCCESS;
889}
890
891
892/**
893 * ISO FS file operations.
894 */
895DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIos9660FileOps =
896{
897 { /* Stream */
898 { /* Obj */
899 RTVFSOBJOPS_VERSION,
900 RTVFSOBJTYPE_FILE,
901 "FatFile",
902 rtFsIsoFile_Close,
903 rtFsIsoFile_QueryInfo,
904 RTVFSOBJOPS_VERSION
905 },
906 RTVFSIOSTREAMOPS_VERSION,
907 RTVFSIOSTREAMOPS_FEAT_NO_SG,
908 rtFsIsoFile_Read,
909 rtFsIsoFile_Write,
910 rtFsIsoFile_Flush,
911 rtFsIsoFile_PollOne,
912 rtFsIsoFile_Tell,
913 NULL /*pfnSkip*/,
914 NULL /*pfnZeroFill*/,
915 RTVFSIOSTREAMOPS_VERSION,
916 },
917 RTVFSFILEOPS_VERSION,
918 0,
919 { /* ObjSet */
920 RTVFSOBJSETOPS_VERSION,
921 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
922 rtFsIsoFile_SetMode,
923 rtFsIsoFile_SetTimes,
924 rtFsIsoFile_SetOwner,
925 RTVFSOBJSETOPS_VERSION
926 },
927 rtFsIsoFile_Seek,
928 rtFsIsoFile_QuerySize,
929 RTVFSFILEOPS_VERSION
930};
931
932
933/**
934 * Instantiates a new directory, from 9660.
935 *
936 * @returns IPRT status code.
937 * @param pThis The FAT volume instance.
938 * @param pParentDir The parent directory (shared part).
939 * @param pDirRec The directory record.
940 * @param cDirRecs Number of directory records if more than one.
941 * @param offDirRec The byte offset of the directory record.
942 * @param offEntryInDir The byte offset of the directory entry in the parent
943 * directory.
944 * @param fOpen RTFILE_O_XXX flags.
945 * @param uVersion The file version number (since the caller already
946 * parsed the filename, we don't want to repeat the
947 * effort here).
948 * @param phVfsFile Where to return the file handle.
949 */
950static int rtFsIsoFile_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
951 uint64_t offDirRec, uint64_t fOpen, uint32_t uVersion, PRTVFSFILE phVfsFile)
952{
953 AssertPtr(pParentDir);
954
955 /*
956 * Create a VFS object.
957 */
958 PRTFSISOFILEOBJ pNewFile;
959 int rc = RTVfsNewFile(&g_rtFsIos9660FileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
960 phVfsFile, (void **)&pNewFile);
961 if (RT_SUCCESS(rc))
962 {
963 /*
964 * Look for existing shared object, create a new one if necessary.
965 */
966 PRTFSISOFILESHRD pShared = (PRTFSISOFILESHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
967 if (!pShared)
968 {
969 pShared = (PRTFSISOFILESHRD)RTMemAllocZ(sizeof(*pShared));
970 if (pShared)
971 {
972 /*
973 * Initialize it all so rtFsIsoFile_Close doesn't trip up in anyway.
974 */
975 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, uVersion, pThis);
976 if (RT_SUCCESS(rc))
977 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
978 else
979 {
980 RTMemFree(pShared);
981 pShared = NULL;
982 }
983 }
984 }
985 if (pShared)
986 {
987 LogFlow(("rtFsIsoFile_New9660: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
988 pShared->Core.cbObject, pShared->Core.FirstExtent.offDisk, pShared->Core.FirstExtent.cbExtent));
989 pNewFile->offFile = 0;
990 pNewFile->pShared = pShared;
991 return VINF_SUCCESS;
992 }
993
994 rc = VERR_NO_MEMORY;
995 }
996 *phVfsFile = NIL_RTVFSFILE;
997 return rc;
998}
999
1000
1001/**
1002 * Looks up the shared structure for a child.
1003 *
1004 * @returns Referenced pointer to the shared structure, NULL if not found.
1005 * @param pThis The directory.
1006 * @param offDirRec The directory record offset of the child.
1007 */
1008static PRTFSISOCORE rtFsIsoDir_LookupShared(PRTFSISODIRSHRD pThis, uint64_t offDirRec)
1009{
1010 PRTFSISOCORE pCur;
1011 RTListForEach(&pThis->OpenChildren, pCur, RTFSISOCORE, Entry)
1012 {
1013 if (pCur->offDirRec == offDirRec)
1014 {
1015 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
1016 Assert(cRefs > 1); RT_NOREF(cRefs);
1017 return pCur;
1018 }
1019 }
1020 return NULL;
1021}
1022
1023
1024#ifdef RT_STRICT
1025/**
1026 * Checks if @a pNext is an extent of @a pFirst.
1027 *
1028 * @returns true if @a pNext is the next extent, false if not
1029 * @param pFirst The directory record describing the first or the
1030 * previous extent.
1031 * @param pNext The directory record alleged to be the next extent.
1032 */
1033DECLINLINE(bool) rtFsIsoDir_Is9660DirRecNextExtent(PCISO9660DIRREC pFirst, PCISO9660DIRREC pNext)
1034{
1035 if (RT_LIKELY(pNext->bFileIdLength == pFirst->bFileIdLength))
1036 {
1037 if (RT_LIKELY((pNext->fFileFlags | ISO9660_FILE_FLAGS_MULTI_EXTENT) == pFirst->fFileFlags))
1038 {
1039 if (RT_LIKELY(memcmp(pNext->achFileId, pFirst->achFileId, pNext->bFileIdLength) == 0))
1040 return true;
1041 }
1042 }
1043 return false;
1044}
1045#endif /* RT_STRICT */
1046
1047
1048/**
1049 * Worker for rtFsIsoDir_FindEntry that compares a UTF-16BE name with a
1050 * directory record.
1051 *
1052 * @returns true if equal, false if not.
1053 * @param pDirRec The directory record.
1054 * @param pwszEntry The UTF-16BE string to compare with.
1055 * @param cbEntry The compare string length in bytes (sans zero
1056 * terminator).
1057 * @param cwcEntry The compare string length in RTUTF16 units.
1058 * @param puVersion Where to return any file version number.
1059 */
1060DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualUtf16Big(PCISO9660DIRREC pDirRec, PCRTUTF16 pwszEntry, size_t cbEntry,
1061 size_t cwcEntry, uint32_t *puVersion)
1062{
1063 /* ASSUME directories cannot have any version tags. */
1064 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
1065 {
1066 if (RT_LIKELY(pDirRec->bFileIdLength != cbEntry))
1067 return false;
1068 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
1069 return false;
1070 }
1071 else
1072 {
1073 size_t cbNameDelta = (size_t)pDirRec->bFileIdLength - cbEntry;
1074 if (RT_LIKELY(cbNameDelta > (size_t)12 /* ;12345 */))
1075 return false;
1076 if (cbNameDelta == 0)
1077 {
1078 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
1079 return false;
1080 *puVersion = 1;
1081 }
1082 else
1083 {
1084 if (RT_LIKELY(RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';'))
1085 return false;
1086 if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
1087 return false;
1088 uint32_t uVersion;
1089 size_t cwcVersion = rtFsIso9660GetVersionLengthUtf16Big((PCRTUTF16)pDirRec->achFileId,
1090 pDirRec->bFileIdLength, &uVersion);
1091 if (RT_LIKELY(cwcVersion * sizeof(RTUTF16) == cbNameDelta))
1092 *puVersion = uVersion;
1093 else
1094 return false;
1095 }
1096 }
1097
1098 /* (No need to check for dot and dot-dot here, because cbEntry must be a
1099 multiple of two.) */
1100 Assert(!(cbEntry & 1));
1101 return true;
1102}
1103
1104
1105/**
1106 * Worker for rtFsIsoDir_FindEntry that compares an ASCII name with a
1107 * directory record.
1108 *
1109 * @returns true if equal, false if not.
1110 * @param pDirRec The directory record.
1111 * @param pszEntry The uppercased ASCII string to compare with.
1112 * @param cchEntry The length of the compare string.
1113 * @param puVersion Where to return any file version number.
1114 */
1115DECL_FORCE_INLINE(bool) rtFsIsoDir_IsEntryEqualAscii(PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cchEntry,
1116 uint32_t *puVersion)
1117{
1118 /* ASSUME directories cannot have any version tags. */
1119 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
1120 {
1121 if (RT_LIKELY(pDirRec->bFileIdLength != cchEntry))
1122 return false;
1123 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
1124 return false;
1125 }
1126 else
1127 {
1128 size_t cchNameDelta = (size_t)pDirRec->bFileIdLength - cchEntry;
1129 if (RT_LIKELY(cchNameDelta > (size_t)6 /* ;12345 */))
1130 return false;
1131 if (cchNameDelta == 0)
1132 {
1133 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
1134 return false;
1135 *puVersion = 1;
1136 }
1137 else
1138 {
1139 if (RT_LIKELY(pDirRec->achFileId[cchEntry] != ';'))
1140 return false;
1141 if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
1142 return false;
1143 uint32_t uVersion;
1144 size_t cchVersion = rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion);
1145 if (RT_LIKELY(cchVersion == cchNameDelta))
1146 *puVersion = uVersion;
1147 else
1148 return false;
1149 }
1150 }
1151
1152 /* Don't match the 'dot' and 'dot-dot' directory records. */
1153 if (RT_LIKELY( pDirRec->bFileIdLength != 1
1154 || (uint8_t)pDirRec->achFileId[0] > (uint8_t)0x01))
1155 return true;
1156 return false;
1157}
1158
1159
1160/**
1161 * Locates a directory entry in a directory.
1162 *
1163 * @returns IPRT status code.
1164 * @retval VERR_FILE_NOT_FOUND if not found.
1165 * @param pThis The directory to search.
1166 * @param pszEntry The entry to look for.
1167 * @param poffDirRec Where to return the offset of the directory record
1168 * on the disk.
1169 * @param ppDirRec Where to return the pointer to the directory record
1170 * (the whole directory is buffered).
1171 * @param pcDirRecs Where to return the number of directory records
1172 * related to this entry.
1173 * @param pfMode Where to return the file type, rock ridge adjusted.
1174 * @param puVersion Where to return the file version number.
1175 */
1176static int rtFsIsoDir_FindEntry(PRTFSISODIRSHRD pThis, const char *pszEntry, uint64_t *poffDirRec,
1177 PCISO9660DIRREC *ppDirRec, uint32_t *pcDirRecs, PRTFMODE pfMode, uint32_t *puVersion)
1178{
1179 /* Set return values. */
1180 *poffDirRec = UINT64_MAX;
1181 *ppDirRec = NULL;
1182 *pcDirRecs = 1;
1183 *pfMode = UINT32_MAX;
1184 *puVersion = 0;
1185
1186 /*
1187 * If we're in UTF-16BE mode, convert the input name to UTF-16BE. Otherwise try
1188 * uppercase it into a ISO 9660 compliant name.
1189 */
1190 int rc;
1191 bool const fIsUtf16 = pThis->Core.pVol->fIsUtf16;
1192 size_t cwcEntry = 0;
1193 size_t cbEntry = 0;
1194 size_t cchUpper = ~(size_t)0;
1195 union
1196 {
1197 RTUTF16 wszEntry[260 + 1];
1198 struct
1199 {
1200 char szUpper[255 + 1];
1201 char szRock[260 + 1];
1202 } s;
1203 } uBuf;
1204 if (fIsUtf16)
1205 {
1206 PRTUTF16 pwszEntry = uBuf.wszEntry;
1207 rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
1208 if (RT_FAILURE(rc))
1209 return rc;
1210 cbEntry = cwcEntry * 2;
1211 }
1212 else
1213 {
1214 rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
1215 if (RT_SUCCESS(rc))
1216 {
1217 RTStrToUpper(uBuf.s.szUpper);
1218 cchUpper = strlen(uBuf.s.szUpper);
1219 }
1220 }
1221
1222 /*
1223 * Scan the directory buffer by buffer.
1224 */
1225 uint32_t offEntryInDir = 0;
1226 uint32_t const cbDir = pThis->Core.cbObject;
1227 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
1228 {
1229 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
1230
1231 /* If null length, skip to the next sector. */
1232 if (pDirRec->cbDirRec == 0)
1233 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
1234 else
1235 {
1236 /* Try match the filename. */
1237 if (fIsUtf16)
1238 {
1239 if (RT_LIKELY(!rtFsIsoDir_IsEntryEqualUtf16Big(pDirRec, uBuf.wszEntry, cbEntry, cwcEntry, puVersion)))
1240 {
1241 /* Advance */
1242 offEntryInDir += pDirRec->cbDirRec;
1243 continue;
1244 }
1245 }
1246 else
1247 {
1248 if (RT_LIKELY(!rtFsIsoDir_IsEntryEqualAscii(pDirRec, uBuf.s.szUpper, cchUpper, puVersion)))
1249 {
1250 /** @todo check rock. */
1251 if (1)
1252 {
1253 /* Advance */
1254 offEntryInDir += pDirRec->cbDirRec;
1255 continue;
1256 }
1257 }
1258 }
1259
1260 *poffDirRec = pThis->Core.FirstExtent.offDisk + offEntryInDir;
1261 *ppDirRec = pDirRec;
1262 *pfMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
1263 ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
1264 : 0644 | RTFS_TYPE_FILE;
1265
1266 /*
1267 * Deal with the unlikely scenario of multi extent records.
1268 */
1269 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
1270 *pcDirRecs = 1;
1271 else
1272 {
1273 offEntryInDir += pDirRec->cbDirRec;
1274
1275 uint32_t cDirRecs = 1;
1276 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
1277 {
1278 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
1279 if (pDirRec2->cbDirRec != 0)
1280 {
1281 Assert(rtFsIsoDir_Is9660DirRecNextExtent(pDirRec, pDirRec2));
1282 cDirRecs++;
1283 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
1284 break;
1285 offEntryInDir += pDirRec2->cbDirRec;
1286 }
1287 else
1288 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
1289 }
1290
1291 *pcDirRecs = cDirRecs;
1292 }
1293 return VINF_SUCCESS;
1294 }
1295 }
1296
1297 return VERR_FILE_NOT_FOUND;
1298}
1299
1300
1301/**
1302 * Releases a reference to a shared directory structure.
1303 *
1304 * @param pShared The shared directory structure.
1305 */
1306static void rtFsIsoDirShrd_Release(PRTFSISODIRSHRD pShared)
1307{
1308 uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
1309 Assert(cRefs < UINT32_MAX / 2);
1310 if (cRefs == 0)
1311 {
1312 LogFlow(("rtFsIsoDirShrd_Release: Destroying shared structure %p\n", pShared));
1313 Assert(pShared->Core.cRefs == 0);
1314 if (pShared->pbDir)
1315 {
1316 RTMemFree(pShared->pbDir);
1317 pShared->pbDir = NULL;
1318 }
1319 rtFsIsoCore_Destroy(&pShared->Core);
1320 RTMemFree(pShared);
1321 }
1322}
1323
1324
1325/**
1326 * Retains a reference to a shared directory structure.
1327 *
1328 * @param pShared The shared directory structure.
1329 */
1330static void rtFsIsoDirShrd_Retain(PRTFSISODIRSHRD pShared)
1331{
1332 uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
1333 Assert(cRefs > 1); NOREF(cRefs);
1334}
1335
1336
1337
1338/**
1339 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1340 */
1341static DECLCALLBACK(int) rtFsIsoDir_Close(void *pvThis)
1342{
1343 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1344 LogFlow(("rtFsIsoDir_Close(%p/%p)\n", pThis, pThis->pShared));
1345
1346 PRTFSISODIRSHRD pShared = pThis->pShared;
1347 pThis->pShared = NULL;
1348 if (pShared)
1349 rtFsIsoDirShrd_Release(pShared);
1350 return VINF_SUCCESS;
1351}
1352
1353
1354/**
1355 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1356 */
1357static DECLCALLBACK(int) rtFsIsoDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1358{
1359 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1360 return rtFsIsoCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
1361}
1362
1363
1364/**
1365 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1366 */
1367static DECLCALLBACK(int) rtFsIsoDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1368{
1369 RT_NOREF(pvThis, fMode, fMask);
1370 return VERR_WRITE_PROTECT;
1371}
1372
1373
1374/**
1375 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1376 */
1377static DECLCALLBACK(int) rtFsIsoDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1378 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1379{
1380 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1381 return VERR_WRITE_PROTECT;
1382}
1383
1384
1385/**
1386 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1387 */
1388static DECLCALLBACK(int) rtFsIsoDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1389{
1390 RT_NOREF(pvThis, uid, gid);
1391 return VERR_WRITE_PROTECT;
1392}
1393
1394
1395/**
1396 * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
1397 */
1398static DECLCALLBACK(int) rtFsIsoDir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
1399 PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
1400{
1401 /*
1402 * We may have symbolic links if rock ridge is being used, though currently
1403 * we won't have nested mounts.
1404 */
1405 int rc;
1406 if (phVfsMounted)
1407 *phVfsMounted = NIL_RTVFS;
1408 if (phVfsDir || phVfsSymlink)
1409 {
1410 if (phVfsSymlink)
1411 *phVfsSymlink = NIL_RTVFSSYMLINK;
1412 if (phVfsDir)
1413 *phVfsDir = NIL_RTVFSDIR;
1414
1415 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1416 PRTFSISODIRSHRD pShared = pThis->pShared;
1417 PCISO9660DIRREC pDirRec;
1418 uint64_t offDirRec;
1419 uint32_t cDirRecs;
1420 RTFMODE fMode;
1421 uint32_t uVersion;
1422 rc = rtFsIsoDir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
1423 Log2(("rtFsIsoDir_TraversalOpen: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
1424 if (RT_SUCCESS(rc))
1425 {
1426 switch (fMode & RTFS_TYPE_MASK)
1427 {
1428 case RTFS_TYPE_DIRECTORY:
1429 if (phVfsDir)
1430 rc = rtFsIsoDir_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
1431 else
1432 rc = VERR_NOT_SYMLINK;
1433 break;
1434
1435 case RTFS_TYPE_SYMLINK:
1436 rc = VERR_NOT_IMPLEMENTED;
1437 break;
1438 case RTFS_TYPE_FILE:
1439 case RTFS_TYPE_DEV_BLOCK:
1440 case RTFS_TYPE_DEV_CHAR:
1441 case RTFS_TYPE_FIFO:
1442 case RTFS_TYPE_SOCKET:
1443 rc = VERR_NOT_A_DIRECTORY;
1444 break;
1445 default:
1446 case RTFS_TYPE_WHITEOUT:
1447 rc = VERR_PATH_NOT_FOUND;
1448 break;
1449 }
1450 }
1451 else if (rc == VERR_FILE_NOT_FOUND)
1452 rc = VERR_PATH_NOT_FOUND;
1453 }
1454 else
1455 rc = VERR_PATH_NOT_FOUND;
1456 return rc;
1457}
1458
1459
1460/**
1461 * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
1462 */
1463static DECLCALLBACK(int) rtFsIsoDir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
1464{
1465 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1466 PRTFSISODIRSHRD pShared = pThis->pShared;
1467
1468 /*
1469 * We cannot create or replace anything, just open stuff.
1470 */
1471 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
1472 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
1473 return VERR_WRITE_PROTECT;
1474
1475 /*
1476 * Try open file.
1477 */
1478 PCISO9660DIRREC pDirRec;
1479 uint64_t offDirRec;
1480 uint32_t cDirRecs;
1481 RTFMODE fMode;
1482 uint32_t uVersion;
1483 int rc = rtFsIsoDir_FindEntry(pShared, pszFilename, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
1484 Log2(("rtFsIsoDir_OpenFile: FindEntry(,%s,) -> %Rrc\n", pszFilename, rc));
1485 if (RT_SUCCESS(rc))
1486 {
1487 switch (fMode & RTFS_TYPE_MASK)
1488 {
1489 case RTFS_TYPE_FILE:
1490 rc = rtFsIsoFile_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, fOpen, uVersion, phVfsFile);
1491 break;
1492
1493 case RTFS_TYPE_SYMLINK:
1494 case RTFS_TYPE_DEV_BLOCK:
1495 case RTFS_TYPE_DEV_CHAR:
1496 case RTFS_TYPE_FIFO:
1497 case RTFS_TYPE_SOCKET:
1498 case RTFS_TYPE_WHITEOUT:
1499 rc = VERR_NOT_IMPLEMENTED;
1500 break;
1501
1502 case RTFS_TYPE_DIRECTORY:
1503 rc = VERR_NOT_A_FILE;
1504 break;
1505
1506 default:
1507 rc = VERR_PATH_NOT_FOUND;
1508 break;
1509 }
1510 }
1511 return rc;
1512}
1513
1514
1515/**
1516 * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
1517 */
1518static DECLCALLBACK(int) rtFsIsoDir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
1519{
1520 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1521 PRTFSISODIRSHRD pShared = pThis->pShared;
1522 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
1523
1524 /*
1525 * Try open file.
1526 */
1527 PCISO9660DIRREC pDirRec;
1528 uint64_t offDirRec;
1529 uint32_t cDirRecs;
1530 RTFMODE fMode;
1531 uint32_t uVersion;
1532 int rc = rtFsIsoDir_FindEntry(pShared, pszSubDir, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
1533 Log2(("rtFsIsoDir_OpenDir: FindEntry(,%s,) -> %Rrc\n", pszSubDir, rc));
1534 if (RT_SUCCESS(rc))
1535 {
1536 switch (fMode & RTFS_TYPE_MASK)
1537 {
1538 case RTFS_TYPE_DIRECTORY:
1539 rc = rtFsIsoDir_New9660(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
1540 break;
1541
1542 case RTFS_TYPE_FILE:
1543 case RTFS_TYPE_SYMLINK:
1544 case RTFS_TYPE_DEV_BLOCK:
1545 case RTFS_TYPE_DEV_CHAR:
1546 case RTFS_TYPE_FIFO:
1547 case RTFS_TYPE_SOCKET:
1548 case RTFS_TYPE_WHITEOUT:
1549 rc = VERR_NOT_A_DIRECTORY;
1550 break;
1551
1552 default:
1553 rc = VERR_PATH_NOT_FOUND;
1554 break;
1555 }
1556 }
1557 return rc;
1558}
1559
1560
1561/**
1562 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
1563 */
1564static DECLCALLBACK(int) rtFsIsoDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
1565{
1566 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
1567 return VERR_WRITE_PROTECT;
1568}
1569
1570
1571/**
1572 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
1573 */
1574static DECLCALLBACK(int) rtFsIsoDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
1575{
1576 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
1577RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
1578 return VERR_NOT_SUPPORTED;
1579}
1580
1581
1582/**
1583 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
1584 */
1585static DECLCALLBACK(int) rtFsIsoDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
1586 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
1587{
1588 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
1589 return VERR_WRITE_PROTECT;
1590}
1591
1592
1593/**
1594 * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
1595 */
1596static DECLCALLBACK(int) rtFsIsoDir_QueryEntryInfo(void *pvThis, const char *pszEntry,
1597 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1598{
1599 /*
1600 * Try locate the entry.
1601 */
1602 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1603 PRTFSISODIRSHRD pShared = pThis->pShared;
1604 PCISO9660DIRREC pDirRec;
1605 uint64_t offDirRec;
1606 uint32_t cDirRecs;
1607 RTFMODE fMode;
1608 uint32_t uVersion;
1609 int rc = rtFsIsoDir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
1610 Log2(("rtFsIsoDir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
1611 if (RT_SUCCESS(rc))
1612 {
1613 /*
1614 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
1615 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
1616 */
1617 RTFSISOCORE TmpObj;
1618 RT_ZERO(TmpObj);
1619 rc = rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, cDirRecs, offDirRec, uVersion, pShared->Core.pVol);
1620 if (RT_SUCCESS(rc))
1621 {
1622 rc = rtFsIsoCore_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
1623 RTMemFree(TmpObj.paExtents);
1624 }
1625 }
1626 return rc;
1627}
1628
1629
1630/**
1631 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
1632 */
1633static DECLCALLBACK(int) rtFsIsoDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
1634{
1635 RT_NOREF(pvThis, pszEntry, fType);
1636 return VERR_WRITE_PROTECT;
1637}
1638
1639
1640/**
1641 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
1642 */
1643static DECLCALLBACK(int) rtFsIsoDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
1644{
1645 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
1646 return VERR_WRITE_PROTECT;
1647}
1648
1649
1650/**
1651 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
1652 */
1653static DECLCALLBACK(int) rtFsIsoDir_RewindDir(void *pvThis)
1654{
1655 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1656 pThis->offDir = 0;
1657 return VINF_SUCCESS;
1658}
1659
1660
1661/**
1662 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
1663 */
1664static DECLCALLBACK(int) rtFsIsoDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
1665 RTFSOBJATTRADD enmAddAttr)
1666{
1667 PRTFSISODIROBJ pThis = (PRTFSISODIROBJ)pvThis;
1668 PRTFSISODIRSHRD pShared = pThis->pShared;
1669
1670 while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
1671 {
1672 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
1673
1674 /* If null length, skip to the next sector. */
1675 if (pDirRec->cbDirRec == 0)
1676 pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
1677 else
1678 {
1679 /*
1680 * Do names first as they may cause overflows.
1681 */
1682 uint32_t uVersion = 0;
1683 if ( pDirRec->bFileIdLength == 1
1684 && pDirRec->achFileId[0] == '\0')
1685 {
1686 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
1687 {
1688 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
1689 Log3(("rtFsIsoDir_ReadDir: VERR_BUFFER_OVERFLOW (dot)\n"));
1690 return VERR_BUFFER_OVERFLOW;
1691 }
1692 pDirEntry->cbName = 1;
1693 pDirEntry->szName[0] = '.';
1694 pDirEntry->szName[1] = '\0';
1695 }
1696 else if ( pDirRec->bFileIdLength == 1
1697 && pDirRec->achFileId[0] == '\1')
1698 {
1699 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
1700 {
1701 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
1702 Log3(("rtFsIsoDir_ReadDir: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
1703 return VERR_BUFFER_OVERFLOW;
1704 }
1705 pDirEntry->cbName = 2;
1706 pDirEntry->szName[0] = '.';
1707 pDirEntry->szName[1] = '.';
1708 pDirEntry->szName[2] = '\0';
1709 }
1710 else if (pShared->Core.pVol->fIsUtf16)
1711 {
1712 PCRTUTF16 pawcSrc = (PCRTUTF16)&pDirRec->achFileId[0];
1713 size_t cwcSrc = pDirRec->bFileIdLength / sizeof(RTUTF16);
1714 size_t cwcVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
1715 ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
1716 size_t cchNeeded = 0;
1717 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
1718 char *pszDst = pDirEntry->szName;
1719
1720 int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
1721 if (RT_SUCCESS(rc))
1722 pDirEntry->cbName = (uint16_t)cchNeeded;
1723 else if (rc == VERR_BUFFER_OVERFLOW)
1724 {
1725 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
1726 Log3(("rtFsIsoDir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
1727 return VERR_BUFFER_OVERFLOW;
1728 }
1729 else
1730 {
1731 ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
1732 if (cchNeeded2 >= 0)
1733 pDirEntry->cbName = (uint16_t)cchNeeded2;
1734 else
1735 {
1736 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
1737 return VERR_BUFFER_OVERFLOW;
1738 }
1739 }
1740 }
1741 else
1742 {
1743 /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
1744 size_t cchVer = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
1745 ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
1746 size_t cchName = pDirRec->bFileIdLength - cchVer;
1747 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
1748 if (*pcbDirEntry < cbNeeded)
1749 {
1750 Log3(("rtFsIsoDir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
1751 *pcbDirEntry = cbNeeded;
1752 return VERR_BUFFER_OVERFLOW;
1753 }
1754 pDirEntry->cbName = (uint16_t)cchName;
1755 memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
1756 pDirEntry->szName[cchName] = '\0';
1757 RTStrPurgeEncoding(pDirEntry->szName);
1758
1759 /** @todo check for rock ridge names here. */
1760 }
1761 pDirEntry->cwcShortName = 0;
1762 pDirEntry->wszShortName[0] = '\0';
1763
1764 /*
1765 * To avoid duplicating code in rtFsIsoCore_InitFrom9660DirRec and
1766 * rtFsIsoCore_QueryInfo, we create a dummy RTFSISOCORE on the stack.
1767 */
1768 RTFSISOCORE TmpObj;
1769 RT_ZERO(TmpObj);
1770 rtFsIsoCore_InitFrom9660DirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
1771 pThis->offDir + pShared->Core.FirstExtent.offDisk, uVersion, pShared->Core.pVol);
1772 int rc = rtFsIsoCore_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
1773
1774 /*
1775 * Update the directory location and handle multi extent records.
1776 *
1777 * Multi extent records only affect the file size and the directory location,
1778 * so we deal with it here instead of involving * rtFsIsoCore_InitFrom9660DirRec
1779 * which would potentially require freeing memory and such.
1780 */
1781 if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
1782 {
1783 Log3(("rtFsIsoDir_ReadDir: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
1784 pThis->offDir += pDirRec->cbDirRec;
1785 }
1786 else
1787 {
1788 uint32_t cExtents = 1;
1789 uint32_t offDir = pThis->offDir + pDirRec->cbDirRec;
1790 while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
1791 {
1792 PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
1793 if (pDirRec2->cbDirRec != 0)
1794 {
1795 pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
1796 offDir += pDirRec2->cbDirRec;
1797 cExtents++;
1798 if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
1799 break;
1800 }
1801 else
1802 offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
1803 }
1804 Log3(("rtFsIsoDir_ReadDir: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
1805 pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
1806 pThis->offDir = offDir;
1807 }
1808
1809 return rc;
1810 }
1811 }
1812
1813 Log3(("rtFsIsoDir_ReadDir: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
1814 return VERR_NO_MORE_FILES;
1815}
1816
1817
1818/**
1819 * FAT file operations.
1820 */
1821static const RTVFSDIROPS g_rtFsIsoDirOps =
1822{
1823 { /* Obj */
1824 RTVFSOBJOPS_VERSION,
1825 RTVFSOBJTYPE_DIR,
1826 "ISO 9660 Dir",
1827 rtFsIsoDir_Close,
1828 rtFsIsoDir_QueryInfo,
1829 RTVFSOBJOPS_VERSION
1830 },
1831 RTVFSDIROPS_VERSION,
1832 0,
1833 { /* ObjSet */
1834 RTVFSOBJSETOPS_VERSION,
1835 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
1836 rtFsIsoDir_SetMode,
1837 rtFsIsoDir_SetTimes,
1838 rtFsIsoDir_SetOwner,
1839 RTVFSOBJSETOPS_VERSION
1840 },
1841 rtFsIsoDir_TraversalOpen,
1842 rtFsIsoDir_OpenFile,
1843 rtFsIsoDir_OpenDir,
1844 rtFsIsoDir_CreateDir,
1845 rtFsIsoDir_OpenSymlink,
1846 rtFsIsoDir_CreateSymlink,
1847 rtFsIsoDir_QueryEntryInfo,
1848 rtFsIsoDir_UnlinkEntry,
1849 rtFsIsoDir_RenameEntry,
1850 rtFsIsoDir_RewindDir,
1851 rtFsIsoDir_ReadDir,
1852 RTVFSDIROPS_VERSION,
1853};
1854
1855
1856/**
1857 * Adds an open child to the parent directory's shared structure.
1858 *
1859 * Maintains an additional reference to the parent dir to prevent it from going
1860 * away. If @a pDir is the root directory, it also ensures the volume is
1861 * referenced and sticks around until the last open object is gone.
1862 *
1863 * @param pDir The directory.
1864 * @param pChild The child being opened.
1865 * @sa rtFsIsoDirShrd_RemoveOpenChild
1866 */
1867static void rtFsIsoDirShrd_AddOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
1868{
1869 rtFsIsoDirShrd_Retain(pDir);
1870
1871 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
1872 pChild->pParentDir = pDir;
1873}
1874
1875
1876/**
1877 * Removes an open child to the parent directory.
1878 *
1879 * @param pDir The directory.
1880 * @param pChild The child being removed.
1881 *
1882 * @remarks This is the very last thing you do as it may cause a few other
1883 * objects to be released recursively (parent dir and the volume).
1884 *
1885 * @sa rtFsIsoDirShrd_AddOpenChild
1886 */
1887static void rtFsIsoDirShrd_RemoveOpenChild(PRTFSISODIRSHRD pDir, PRTFSISOCORE pChild)
1888{
1889 AssertReturnVoid(pChild->pParentDir == pDir);
1890 RTListNodeRemove(&pChild->Entry);
1891 pChild->pParentDir = NULL;
1892
1893 rtFsIsoDirShrd_Release(pDir);
1894}
1895
1896
1897#ifdef LOG_ENABLED
1898/**
1899 * Logs the content of a directory.
1900 */
1901static void rtFsIsoDirShrd_Log9660Content(PRTFSISODIRSHRD pThis)
1902{
1903 if (LogIs2Enabled())
1904 {
1905 uint32_t offRec = 0;
1906 while (offRec < pThis->cbDir)
1907 {
1908 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
1909 if (pDirRec->cbDirRec == 0)
1910 break;
1911
1912 RTUTF16 wszName[128];
1913 if (pThis->Core.pVol->fIsUtf16)
1914 {
1915 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
1916 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
1917 pwszSrc--;
1918 *pwszDst-- = '\0';
1919 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
1920 {
1921 *pwszDst = RT_BE2H_U16(*pwszSrc);
1922 pwszDst--;
1923 pwszSrc--;
1924 }
1925 }
1926 else
1927 {
1928 PRTUTF16 pwszDst = wszName;
1929 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
1930 *pwszDst++ = pDirRec->achFileId[off];
1931 *pwszDst = '\0';
1932 }
1933
1934 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",
1935 offRec,
1936 pDirRec->cbDirRec,
1937 pDirRec->cExtAttrBlocks,
1938 ISO9660_GET_ENDIAN(&pDirRec->cbData),
1939 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
1940 pDirRec->fFileFlags,
1941 pDirRec->RecTime.bYear + 1900,
1942 pDirRec->RecTime.bMonth,
1943 pDirRec->RecTime.bDay,
1944 pDirRec->RecTime.bHour,
1945 pDirRec->RecTime.bMinute,
1946 pDirRec->RecTime.bSecond,
1947 pDirRec->RecTime.offUtc*4/60,
1948 pDirRec->bFileUnitSize,
1949 pDirRec->bInterleaveGapSize,
1950 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
1951 wszName));
1952
1953 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
1954 if (offSysUse < pDirRec->cbDirRec)
1955 {
1956 Log2(("ISO9660: system use (%#x bytes):\n%.*Rhxd\n", pDirRec->cbDirRec - offSysUse,
1957 pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
1958 }
1959
1960 /* advance */
1961 offRec += pDirRec->cbDirRec;
1962 }
1963 }
1964}
1965#endif /* LOG_ENABLED */
1966
1967
1968/**
1969 * Instantiates a new shared directory structure, given 9660 records.
1970 *
1971 * @returns IPRT status code.
1972 * @param pThis The FAT volume instance.
1973 * @param pParentDir The parent directory. This is NULL for the root
1974 * directory.
1975 * @param pDirRec The directory record. Will access @a cDirRecs
1976 * records.
1977 * @param cDirRecs Number of directory records if more than one.
1978 * @param offDirRec The byte offset of the directory record.
1979 * @param ppShared Where to return the shared directory structure.
1980 */
1981static int rtFsIsoDirShrd_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
1982 uint32_t cDirRecs, uint64_t offDirRec, PRTFSISODIRSHRD *ppShared)
1983{
1984 /*
1985 * Allocate a new structure and initialize it.
1986 */
1987 int rc = VERR_NO_MEMORY;
1988 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)RTMemAllocZ(sizeof(*pShared));
1989 if (pShared)
1990 {
1991 rc = rtFsIsoCore_InitFrom9660DirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pThis);
1992 if (RT_SUCCESS(rc))
1993 {
1994 RTListInit(&pShared->OpenChildren);
1995 pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
1996 pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
1997 if (pShared->pbDir)
1998 {
1999 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.offDisk, pShared->pbDir, pShared->cbDir, NULL);
2000 if (RT_SUCCESS(rc))
2001 {
2002#ifdef LOG_ENABLED
2003 rtFsIsoDirShrd_Log9660Content(pShared);
2004#endif
2005
2006 /*
2007 * Link into parent directory so we can use it to update
2008 * our directory entry.
2009 */
2010 if (pParentDir)
2011 rtFsIsoDirShrd_AddOpenChild(pParentDir, &pShared->Core);
2012 *ppShared = pShared;
2013 return VINF_SUCCESS;
2014 }
2015 }
2016 }
2017 RTMemFree(pShared);
2018 }
2019 *ppShared = NULL;
2020 return rc;
2021}
2022
2023
2024/**
2025 * Instantiates a new directory with a shared structure presupplied.
2026 *
2027 * @returns IPRT status code.
2028 * @param pThis The FAT volume instance.
2029 * @param pShared Referenced pointer to the shared structure. The
2030 * reference is always CONSUMED.
2031 * @param phVfsDir Where to return the directory handle.
2032 */
2033static int rtFsIsoDir_NewWithShared(PRTFSISOVOL pThis, PRTFSISODIRSHRD pShared, PRTVFSDIR phVfsDir)
2034{
2035 /*
2036 * Create VFS object around the shared structure.
2037 */
2038 PRTFSISODIROBJ pNewDir;
2039 int rc = RTVfsNewDir(&g_rtFsIsoDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
2040 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
2041 if (RT_SUCCESS(rc))
2042 {
2043 /*
2044 * Look for existing shared object, create a new one if necessary.
2045 * We CONSUME a reference to pShared here.
2046 */
2047 pNewDir->offDir = 0;
2048 pNewDir->pShared = pShared;
2049 return VINF_SUCCESS;
2050 }
2051
2052 rtFsIsoDirShrd_Release(pShared);
2053 *phVfsDir = NIL_RTVFSDIR;
2054 return rc;
2055}
2056
2057
2058
2059/**
2060 * Instantiates a new directory VFS instance for ISO 9660, creating the shared
2061 * structure as necessary.
2062 *
2063 * @returns IPRT status code.
2064 * @param pThis The FAT volume instance.
2065 * @param pParentDir The parent directory. This is NULL for the root
2066 * directory.
2067 * @param pDirRec The directory record.
2068 * @param cDirRecs Number of directory records if more than one.
2069 * @param offDirRec The byte offset of the directory record.
2070 * @param phVfsDir Where to return the directory handle.
2071 */
2072static int rtFsIsoDir_New9660(PRTFSISOVOL pThis, PRTFSISODIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
2073 uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir)
2074{
2075 /*
2076 * Look for existing shared object, create a new one if necessary.
2077 */
2078 PRTFSISODIRSHRD pShared = (PRTFSISODIRSHRD)rtFsIsoDir_LookupShared(pParentDir, offDirRec);
2079 if (!pShared)
2080 {
2081 int rc = rtFsIsoDirShrd_New9660(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, &pShared);
2082 if (RT_FAILURE(rc))
2083 {
2084 *phVfsDir = NIL_RTVFSDIR;
2085 return rc;
2086 }
2087 }
2088 return rtFsIsoDir_NewWithShared(pThis, pShared, phVfsDir);
2089}
2090
2091
2092
2093/**
2094 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
2095 */
2096static DECLCALLBACK(int) rtFsIsoVol_Close(void *pvThis)
2097{
2098 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
2099 Log(("rtFsIsoVol_Close(%p)\n", pThis));
2100
2101 if (pThis->pRootDir)
2102 {
2103 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
2104 Assert(pThis->pRootDir->Core.cRefs == 1);
2105 rtFsIsoDirShrd_Release(pThis->pRootDir);
2106 pThis->pRootDir = NULL;
2107 }
2108
2109 RTVfsFileRelease(pThis->hVfsBacking);
2110 pThis->hVfsBacking = NIL_RTVFSFILE;
2111
2112 return VINF_SUCCESS;
2113}
2114
2115
2116/**
2117 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
2118 */
2119static DECLCALLBACK(int) rtFsIsoVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2120{
2121 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
2122 return VERR_WRONG_TYPE;
2123}
2124
2125
2126/**
2127 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
2128 */
2129static DECLCALLBACK(int) rtFsIsoVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
2130{
2131 PRTFSISOVOL pThis = (PRTFSISOVOL)pvThis;
2132
2133 rtFsIsoDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
2134 return rtFsIsoDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
2135}
2136
2137
2138/**
2139 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
2140 */
2141static DECLCALLBACK(int) rtFsIsoVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
2142{
2143 RT_NOREF(pvThis, off, cb, pfUsed);
2144 return VERR_NOT_IMPLEMENTED;
2145}
2146
2147
2148DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIsoVolOps =
2149{
2150 { /* Obj */
2151 RTVFSOBJOPS_VERSION,
2152 RTVFSOBJTYPE_VFS,
2153 "ISO 9660/UDF",
2154 rtFsIsoVol_Close,
2155 rtFsIsoVol_QueryInfo,
2156 RTVFSOBJOPS_VERSION
2157 },
2158 RTVFSOPS_VERSION,
2159 0 /* fFeatures */,
2160 rtFsIsoVol_OpenRoot,
2161 rtFsIsoVol_IsRangeInUse,
2162 RTVFSOPS_VERSION
2163};
2164
2165
2166/**
2167 * Checks the descriptor tag and CRC.
2168 *
2169 * @retval IPRT status code.
2170 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
2171 * @retval VERR_MISMATCH
2172 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
2173 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
2174 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
2175 *
2176 * @param pTag The tag to check.
2177 * @param idTag The expected descriptor tag ID, UINT16_MAX matches any
2178 * tag ID.
2179 * @param offTag The sector offset of the tag.
2180 * @param pErrInfo Where to return extended error info.
2181 */
2182static int rtFsIsoVolValidateUdfDescTag(PCUDFTAG pTag, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
2183{
2184 /*
2185 * Checksum the tag first.
2186 */
2187 const uint8_t *pbTag = (const uint8_t *)pTag;
2188 uint8_t const bChecksum = pbTag[0]
2189 + pbTag[1]
2190 + pbTag[2]
2191 + pbTag[3]
2192 + pbTag[5] /* skipping byte 4 as that's the checksum. */
2193 + pbTag[6]
2194 + pbTag[7]
2195 + pbTag[8]
2196 + pbTag[9]
2197 + pbTag[10]
2198 + pbTag[11]
2199 + pbTag[12]
2200 + pbTag[13]
2201 + pbTag[14]
2202 + pbTag[15];
2203 if (pTag->uChecksum == bChecksum)
2204 {
2205 /*
2206 * Do the matching.
2207 */
2208 if ( pTag->uVersion == 3
2209 || pTag->uVersion == 2)
2210 {
2211 if ( pTag->idTag == idTag
2212 || idTag == UINT16_MAX)
2213 {
2214 if (pTag->offTag == offTag)
2215 {
2216 Log2(("ISO/UDF: Valid descriptor %#06x at %#010RX32; cbDescriptorCrc=%#06RX32 uTagSerialNo=%#x\n",
2217 pTag->idTag, offTag, pTag->cbDescriptorCrc, pTag->uTagSerialNo));
2218 return VINF_SUCCESS;
2219 }
2220
2221 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Sector mismatch: %#RX32 (%.*Rhxs)\n",
2222 idTag, offTag, pTag->offTag, sizeof(*pTag), pTag));
2223 return RTErrInfoSetF(pErrInfo, VERR_ISOFS_TAG_SECTOR_MISMATCH,
2224 "Descriptor tag sector number mismatch: %#x, expected %#x (%.*Rhxs)",
2225 pTag->offTag, offTag, sizeof(*pTag), pTag);
2226 }
2227 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Tag ID mismatch: %#x (%.*Rhxs)\n",
2228 idTag, offTag, pTag->idTag, sizeof(*pTag), pTag));
2229 return RTErrInfoSetF(pErrInfo, VERR_MISMATCH, "Descriptor tag ID mismatch: %#x, expected %#x (%.*Rhxs)",
2230 pTag->idTag, idTag, sizeof(*pTag), pTag);
2231 }
2232 if (ASMMemIsZero(pTag, sizeof(*pTag)))
2233 {
2234 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): All zeros\n", idTag, offTag));
2235 return RTErrInfoSet(pErrInfo, VERR_ISOFS_TAG_IS_ALL_ZEROS, "Descriptor is all zeros");
2236 }
2237
2238 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): Unsupported version: %#x (%.*Rhxs)\n",
2239 idTag, offTag, pTag->uVersion, sizeof(*pTag), pTag));
2240 return RTErrInfoSetF(pErrInfo, VERR_ISOFS_UNSUPPORTED_TAG_VERSION, "Unsupported descriptor tag version: %#x, expected 2 or 3 (%.*Rhxs)",
2241 pTag->uVersion, sizeof(*pTag), pTag);
2242 }
2243 Log(("rtFsIsoVolValidateUdfDescTag(,%#x,%#010RX32,): checksum error: %#x, calc %#x (%.*Rhxs)\n",
2244 idTag, offTag, pTag->uChecksum, bChecksum, sizeof(*pTag), pTag));
2245 return RTErrInfoSetF(pErrInfo, VERR_ISOFS_BAD_TAG_CHECKSUM,
2246 "Descriptor tag checksum error: %#x, calculated %#x (%.*Rhxs)",
2247 pTag->uChecksum, bChecksum, sizeof(*pTag), pTag);
2248}
2249
2250
2251/**
2252 * Checks the descriptor CRC.
2253 *
2254 * @retval VINF_SUCCESS
2255 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
2256 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
2257 *
2258 * @param pTag The descriptor buffer to checksum.
2259 * @param cbDesc The size of the descriptor buffer.
2260 * @param pErrInfo Where to return extended error info.
2261 */
2262static int rtFsIsoVolValidateUdfDescCrc(PCUDFTAG pTag, size_t cbDesc, PRTERRINFO pErrInfo)
2263{
2264 if (pTag->cbDescriptorCrc + sizeof(*pTag) <= cbDesc)
2265 {
2266 uint16_t uCrc = RTCrc16Ccitt(pTag + 1, pTag->cbDescriptorCrc);
2267 if (pTag->uDescriptorCrc == uCrc)
2268 return VINF_SUCCESS;
2269
2270 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Descriptor CRC mismatch: expected %#x, calculated %#x (cbDescriptorCrc=%#x)\n",
2271 pTag->idTag, pTag->offTag, pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc));
2272 return RTErrInfoSetF(pErrInfo, VERR_ISOFS_DESC_CRC_MISMATCH,
2273 "Descriptor CRC mismatch: exepcted %#x, calculated %#x (cbDescriptor=%#x, idTag=%#x, offTag=%#010RX32)",
2274 pTag->uDescriptorCrc, uCrc, pTag->cbDescriptorCrc, pTag->idTag, pTag->offTag);
2275 }
2276
2277 Log(("rtFsIsoVolValidateUdfDescCrc(,%#x,%#010RX32,): Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx\n",
2278 pTag->idTag, pTag->offTag, pTag->cbDescriptorCrc, cbDesc));
2279 return RTErrInfoSetF(pErrInfo, VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC,
2280 "Insufficient data to CRC: cbDescriptorCrc=%#x cbDesc=%#zx (idTag=%#x, offTag=%#010RX32)",
2281 pTag->cbDescriptorCrc, cbDesc, pTag->idTag, pTag->offTag);
2282}
2283
2284
2285/**
2286 * Checks the descriptor tag and CRC.
2287 *
2288 * @retval VINF_SUCCESS
2289 * @retval VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
2290 * @retval VERR_ISOFS_TAG_IS_ALL_ZEROS
2291 * @retval VERR_MISMATCH
2292 * @retval VERR_ISOFS_UNSUPPORTED_TAG_VERSION
2293 * @retval VERR_ISOFS_TAG_SECTOR_MISMATCH
2294 * @retval VERR_ISOFS_BAD_TAG_CHECKSUM
2295 * @retval VERR_ISOFS_DESC_CRC_MISMATCH
2296 *
2297 * @param pTag The descriptor buffer to check the tag of and to
2298 * checksum.
2299 * @param cbDesc The size of the descriptor buffer.
2300 * @param idTag The expected descriptor tag ID, UINT16_MAX
2301 * matches any tag ID.
2302 * @param offTag The sector offset of the tag.
2303 * @param pErrInfo Where to return extended error info.
2304 */
2305static int rtFsIsoVolValidateUdfDescTagAndCrc(PCUDFTAG pTag, size_t cbDesc, uint16_t idTag, uint32_t offTag, PRTERRINFO pErrInfo)
2306{
2307 int rc = rtFsIsoVolValidateUdfDescTag(pTag, idTag, offTag, pErrInfo);
2308 if (RT_SUCCESS(rc))
2309 rc = rtFsIsoVolValidateUdfDescCrc(pTag, cbDesc, pErrInfo);
2310 return rc;
2311}
2312
2313
2314#define UDF_LOG2_MEMBER(a_pStruct, a_szFmt, a_Member) \
2315 Log2(("ISO/UDF: %-32s %" a_szFmt "\n", #a_Member ":", (a_pStruct)->a_Member))
2316#define UDF_LOG2_MEMBER_EX(a_pStruct, a_szFmt, a_Member, a_cchIndent) \
2317 Log2(("ISO/UDF: %*s%-32s %" a_szFmt "\n", a_cchIndent, "", #a_Member ":", (a_pStruct)->a_Member))
2318#define UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, a_cchIndent) \
2319 Log2(("ISO/UDF: %*s%-32s '%.23s' fFlags=%#06x Suffix=%.8Rhxs\n", a_cchIndent, "", #a_Member ":", \
2320 (a_pStruct)->a_Member.achIdentifier, (a_pStruct)->a_Member.fFlags, &(a_pStruct)->a_Member.Suffix))
2321#define UDF_LOG2_MEMBER_ENTITY_ID(a_pStruct, a_Member) UDF_LOG2_MEMBER_ENTITY_ID_EX(a_pStruct, a_Member, 0)
2322#define UDF_LOG2_MEMBER_EXTENTAD(a_pStruct, a_Member) \
2323 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb))
2324#define UDF_LOG2_MEMBER_SHORTAD(a_pStruct, a_Member) \
2325 Log2(("ISO/UDF: %-32s sector %#010RX32 LB %#010RX32 %s\n", #a_Member ":", (a_pStruct)->a_Member.off, (a_pStruct)->a_Member.cb, \
2326 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
2327 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
2328 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next" ))
2329#define UDF_LOG2_MEMBER_LONGAD(a_pStruct, a_Member) \
2330 Log2(("ISO/UDF: %-32s partition %#RX16, sector %#010RX32 LB %#010RX32 %s idUnique=%#010RX32 fFlags=%#RX16\n", #a_Member ":", \
2331 (a_pStruct)->a_Member.Location.uPartitionNo, (a_pStruct)->a_Member.Location.off, (a_pStruct)->a_Member.cb, \
2332 (a_pStruct)->a_Member.uType == UDF_AD_TYPE_RECORDED_AND_ALLOCATED ? "alloced+recorded" \
2333 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_ONLY_ALLOCATED ? "alloced" \
2334 : (a_pStruct)->a_Member.uType == UDF_AD_TYPE_FREE ? "free" : "next", \
2335 (a_pStruct)->a_Member.ImplementationUse.Fid.idUnique, (a_pStruct)->a_Member.ImplementationUse.Fid.fFlags ))
2336
2337#define UDF_LOG2_MEMBER_TIMESTAMP(a_pStruct, a_Member) \
2338 Log2(("ISO/UDF: %-32s %04d-%02u-%02u %02u:%02u:%02u.%02u%02u%02u uTypeAndZone=%#x\n", #a_Member ":", \
2339 (a_pStruct)->a_Member.iYear, (a_pStruct)->a_Member.uMonth, (a_pStruct)->a_Member.uDay, \
2340 (a_pStruct)->a_Member.uHour, (a_pStruct)->a_Member.uMinute, (a_pStruct)->a_Member.uSecond, \
2341 (a_pStruct)->a_Member.cCentiseconds, (a_pStruct)->a_Member.cHundredsOfMicroseconds, \
2342 (a_pStruct)->a_Member.cMicroseconds, (a_pStruct)->a_Member.uTypeAndZone))
2343#define UDF_LOG2_MEMBER_CHARSPEC(a_pStruct, a_Member) \
2344 do { \
2345 if ( (a_pStruct)->a_Member.uType == UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE \
2346 && memcmp(&(a_pStruct)->a_Member.abInfo[0], UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO, \
2347 sizeof(UDF_CHAR_SET_OSTA_COMPRESSED_UNICODE_INFO)) == 0) \
2348 Log2(("ISO/UDF: %-32s OSTA COMPRESSED UNICODE INFO\n", #a_Member ":")); \
2349 else if (ASMMemIsZero(&(a_pStruct)->a_Member, sizeof((a_pStruct)->a_Member))) \
2350 Log2(("ISO/UDF: %-32s all zeros\n", #a_Member ":")); \
2351 else \
2352 Log2(("ISO/UDF: %-32s %#x info: %.63Rhxs\n", #a_Member ":", \
2353 (a_pStruct)->a_Member.uType, (a_pStruct)->a_Member.abInfo)); \
2354 } while (0)
2355#define UDF_LOG2_MEMBER_DSTRING(a_pStruct, a_Member) \
2356 do { \
2357 if ((a_pStruct)->a_Member[0] == 8) \
2358 Log2(("ISO/UDF: %-32s 8: '%s' len=%u (actual=%u)\n", #a_Member ":", &(a_pStruct)->a_Member[1], \
2359 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], strlen(&(a_pStruct)->a_Member[1]) + 1 )); \
2360 else if ((a_pStruct)->a_Member[0] == 16) \
2361 { \
2362 PCRTUTF16 pwszTmp = (PCRTUTF16)&(a_pStruct)->a_Member[1]; \
2363 char *pszTmp = NULL; \
2364 RTUtf16BigToUtf8Ex(pwszTmp, (sizeof((a_pStruct)->a_Member) - 2) / sizeof(RTUTF16), &pszTmp, 0, NULL); \
2365 Log2(("ISO/UDF: %-32s 16: '%s' len=%u (actual=%u)\n", #a_Member ":", pszTmp, \
2366 (a_pStruct)->a_Member[sizeof((a_pStruct)->a_Member) - 1], RTUtf16Len(pwszTmp) * sizeof(RTUTF16) + 1 /*??*/ )); \
2367 } \
2368 else if (ASMMemIsZero(&(a_pStruct)->a_Member[0], sizeof((a_pStruct)->a_Member))) \
2369 Log2(("ISO/UDF: %-32s empty\n", #a_Member ":")); \
2370 else \
2371 Log2(("ISO/UDF: %-32s bad: %.*Rhxs\n", #a_Member ":", sizeof((a_pStruct)->a_Member), &(a_pStruct)->a_Member[0] )); \
2372 } while (0)
2373
2374
2375
2376/**
2377 * Processes a primary volume descriptor in the VDS (UDF).
2378 *
2379 * @returns IPRT status code.
2380 * @param pThis The instance.
2381 * @param pDesc The descriptor.
2382 * @param pErrInfo Where to return extended error information.
2383 */
2384//cmd: kmk VBoxRT && kmk_redirect -E VBOX_LOG_DEST="nofile stderr" -E VBOX_LOG="rt_fs=~0" -E VBOX_LOG_FLAGS="unbuffered enabled" -- e:\vbox\svn\trunk\out\win.amd64\debug\bin\tools\RTLs.exe :iprtvfs:file(open,d:\Downloads\en_windows_10_enterprise_version_1703_updated_march_2017_x64_dvd_10189290.iso,r):vfs(isofs):/ -la
2385static int rtFsIsoVolProcessUdfPrimaryVolDesc(PRTFSISOVOL pThis, PCUDFPRIMARYVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
2386{
2387#ifdef LOG_ENABLED
2388 Log(("ISO/UDF: Primary volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
2389 if (LogIs2Enabled())
2390 {
2391 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
2392 UDF_LOG2_MEMBER(pDesc, "#010RX32", uPrimaryVolumeDescNo);
2393 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeID);
2394 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
2395 UDF_LOG2_MEMBER(pDesc, "#06RX16", uVolumeSeqNo);
2396 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxVolumeSeqNo);
2397 UDF_LOG2_MEMBER(pDesc, "#06RX16", uInterchangeLevel);
2398 UDF_LOG2_MEMBER(pDesc, "#06RX16", uMaxInterchangeLevel);
2399 UDF_LOG2_MEMBER(pDesc, "#010RX32", fCharacterSets);
2400 UDF_LOG2_MEMBER(pDesc, "#010RX32", fMaxCharacterSets);
2401 UDF_LOG2_MEMBER_DSTRING(pDesc, achVolumeSetID);
2402 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescCharSet);
2403 UDF_LOG2_MEMBER_CHARSPEC(pDesc, ExplanatoryCharSet);
2404 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeAbstract);
2405 UDF_LOG2_MEMBER_EXTENTAD(pDesc, VolumeCopyrightNotice);
2406 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idApplication);
2407 UDF_LOG2_MEMBER_TIMESTAMP(pDesc, RecordingTimestamp);
2408 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
2409 if (!ASMMemIsZero(&pDesc->abImplementationUse, sizeof(pDesc->abImplementationUse)))
2410 Log2(("ISO/UDF: %-32s %.64Rhxs\n", "abReserved[64]:", &pDesc->abImplementationUse[0]));
2411 UDF_LOG2_MEMBER(pDesc, "#010RX32", offPredecessorVolDescSeq);
2412 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
2413 if (!ASMMemIsZero(&pDesc->abReserved, sizeof(pDesc->abReserved)))
2414 Log2(("ISO/UDF: %-32s %.22Rhxs\n", "abReserved[22]:", &pDesc->abReserved[0]));
2415 }
2416#endif
2417
2418 RT_NOREF(pThis, pDesc, pErrInfo);
2419 return VINF_SUCCESS;
2420}
2421
2422
2423/**
2424 * Processes an logical volume descriptor in the VDS (UDF).
2425 *
2426 * @returns IPRT status code.
2427 * @param pThis The instance.
2428 * @param pDesc The descriptor.
2429 * @param pErrInfo Where to return extended error information.
2430 */
2431static int rtFsIsoVolProcessUdfLogicalVolumeDesc(PRTFSISOVOL pThis, PCUDFLOGICALVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
2432{
2433#ifdef LOG_ENABLED
2434 Log(("ISO/UDF: Logical volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
2435 if (LogIs2Enabled())
2436 {
2437 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
2438 UDF_LOG2_MEMBER_CHARSPEC(pDesc, DescriptorCharSet);
2439 UDF_LOG2_MEMBER_DSTRING(pDesc, achLogicalVolumeID);
2440 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbLogicalBlock);
2441 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idDomain);
2442 if (memcmp(&pDesc->idDomain.achIdentifier[0], RT_STR_TUPLE(UDF_ENTITY_ID_LVD_DOMAIN) + 1) == 0)
2443 UDF_LOG2_MEMBER_LONGAD(pDesc, ContentsUse.FileSetDescriptor);
2444 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
2445 Log2(("ISO/UDF: %-32s %.16Rhxs\n", "ContentsUse.ab[16]:", &pDesc->ContentsUse.ab[0]));
2446 UDF_LOG2_MEMBER(pDesc, "#010RX32", cbMapTable);
2447 UDF_LOG2_MEMBER(pDesc, "#010RX32", cPartitionMaps);
2448 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
2449 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
2450 Log2(("ISO/UDF: %-32s\n%.128Rhxd\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
2451 UDF_LOG2_MEMBER_EXTENTAD(pDesc, IntegritySeqExtent);
2452 if (pDesc->cbMapTable)
2453 {
2454 Log2(("ISO/UDF: %-32s\n", "abPartitionMaps"));
2455 uint32_t iMap = 0;
2456 uint32_t off = 0;
2457 while (off + sizeof(UDFPARTMAPHDR) <= pDesc->cbMapTable)
2458 {
2459 PCUDFPARTMAPHDR pHdr = (PCUDFPARTMAPHDR)&pDesc->abPartitionMaps[off];
2460 Log2(("ISO/UDF: %02u @ %#05x: type %u, length %u\n", iMap, off, pHdr->bType, pHdr->cb));
2461 if (off + pHdr->cb > pDesc->cbMapTable)
2462 {
2463 Log2(("ISO/UDF: BAD! Entry is %d bytes too long!\n", off + pHdr->cb - pDesc->cbMapTable));
2464 break;
2465 }
2466 if (pHdr->bType == 1)
2467 {
2468 PCUDFPARTMAPTYPE1 pType1 = (PCUDFPARTMAPTYPE1)pHdr;
2469 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uVolumeSeqNo, 5);
2470 UDF_LOG2_MEMBER_EX(pType1, "#06RX16", uPartitionNo, 5);
2471 }
2472 else if (pHdr->bType == 2)
2473 {
2474 PCUDFPARTMAPTYPE2 pType2 = (PCUDFPARTMAPTYPE2)pHdr;
2475 UDF_LOG2_MEMBER_ENTITY_ID_EX(pType2, idPartitionType, 5);
2476 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uVolumeSeqNo, 5);
2477 UDF_LOG2_MEMBER_EX(pType2, "#06RX16", uPartitionNo, 5);
2478 }
2479 else
2480 Log2(("ISO/UDF: BAD! Unknown type!\n"));
2481
2482 /* advance */
2483 off += pHdr->cb;
2484 iMap++;
2485 }
2486 }
2487 }
2488#endif
2489
2490 RT_NOREF(pThis, pDesc, pErrInfo);
2491 return VINF_SUCCESS;
2492}
2493
2494
2495/**
2496 * Processes an partition descriptor in the VDS (UDF).
2497 *
2498 * @returns IPRT status code.
2499 * @param pThis The instance.
2500 * @param pDesc The descriptor.
2501 * @param pErrInfo Where to return extended error information.
2502 */
2503static int rtFsIsoVolProcessUdfPartitionDesc(PRTFSISOVOL pThis, PCUDFPARTITIONDESC pDesc, PRTERRINFO pErrInfo)
2504{
2505#ifdef LOG_ENABLED
2506 Log(("ISO/UDF: Partition descriptor at sector %#RX32\n", pDesc->Tag.offTag));
2507 if (LogIs2Enabled())
2508 {
2509 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
2510 UDF_LOG2_MEMBER(pDesc, "#06RX16", fFlags);
2511 UDF_LOG2_MEMBER(pDesc, "#06RX16", uPartitionNo);
2512 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, PartitionContents);
2513 if (memcmp(&pDesc->PartitionContents.achIdentifier[0], RT_STR_TUPLE(UDF_ENTITY_ID_PD_PARTITION_CONTENTS_UDF) + 1) == 0)
2514 {
2515 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceTable);
2516 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.UnallocatedSpaceBitmap);
2517 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.PartitionIntegrityTable);
2518 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceTable);
2519 UDF_LOG2_MEMBER_SHORTAD(&pDesc->ContentsUse, Hdr.FreedSpaceBitmap);
2520 if (!ASMMemIsZero(&pDesc->ContentsUse.Hdr.abReserved[0], sizeof(pDesc->ContentsUse.Hdr.abReserved)))
2521 Log2(("ISO/UDF: %-32s\n%.88Rhxd\n", "Hdr.abReserved[88]:", &pDesc->ContentsUse.Hdr.abReserved[0]));
2522 }
2523 else if (!ASMMemIsZero(&pDesc->ContentsUse.ab[0], sizeof(pDesc->ContentsUse.ab)))
2524 Log2(("ISO/UDF: %-32s\n%.128Rhxd\n", "ContentsUse.ab[128]:", &pDesc->ContentsUse.ab[0]));
2525 UDF_LOG2_MEMBER(pDesc, "#010RX32", uAccessType);
2526 UDF_LOG2_MEMBER(pDesc, "#010RX32", offLocation);
2527 UDF_LOG2_MEMBER(pDesc, "#010RX32", cSectors);
2528 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
2529 if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
2530 Log2(("ISO/UDF: %-32s\n%.128Rhxd\n", "ImplementationUse.ab[128]:", &pDesc->ImplementationUse.ab[0]));
2531
2532 if (!ASMMemIsZero(&pDesc->abReserved[0], sizeof(pDesc->abReserved)))
2533 Log2(("ISO/UDF: %-32s\n%.156Rhxd\n", "ImplementationUse.ab[156]:", &pDesc->abReserved[0]));
2534 }
2535#endif
2536
2537 RT_NOREF(pThis, pDesc, pErrInfo);
2538 return VINF_SUCCESS;
2539}
2540
2541
2542/**
2543 * Processes an implementation use descriptor in the VDS (UDF).
2544 *
2545 * @returns IPRT status code.
2546 * @param pThis The instance.
2547 * @param pDesc The descriptor.
2548 * @param pErrInfo Where to return extended error information.
2549 */
2550static int rtFsIsoVolProcessUdfImplUseVolDesc(PRTFSISOVOL pThis, PCUDFIMPLEMENTATIONUSEVOLUMEDESC pDesc, PRTERRINFO pErrInfo)
2551{
2552#ifdef LOG_ENABLED
2553 Log(("ISO/UDF: Implementation use volume descriptor at sector %#RX32\n", pDesc->Tag.offTag));
2554 if (LogIs2Enabled())
2555 {
2556 UDF_LOG2_MEMBER(pDesc, "#010RX32", uVolumeDescSeqNo);
2557 UDF_LOG2_MEMBER_ENTITY_ID(pDesc, idImplementation);
2558 if (memcmp(pDesc->idImplementation.achIdentifier, RT_STR_TUPLE(UDF_ENTITY_ID_IUVD_IMPLEMENTATION) + 1) == 0)
2559 {
2560 UDF_LOG2_MEMBER_CHARSPEC(&pDesc->ImplementationUse, Lvi.Charset);
2561 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achVolumeID);
2562 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo1);
2563 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo2);
2564 UDF_LOG2_MEMBER_DSTRING(&pDesc->ImplementationUse, Lvi.achInfo3);
2565 UDF_LOG2_MEMBER_ENTITY_ID(&pDesc->ImplementationUse, Lvi.idImplementation);
2566 if (!ASMMemIsZero(&pDesc->ImplementationUse.Lvi.abUse[0], sizeof(pDesc->ImplementationUse.Lvi.abUse)))
2567 Log2(("ISO/UDF: %-32s\n%.128Rhxd\n", "Lvi.abUse[128]:", &pDesc->ImplementationUse.Lvi.abUse[0]));
2568 }
2569 else if (!ASMMemIsZero(&pDesc->ImplementationUse.ab[0], sizeof(pDesc->ImplementationUse.ab)))
2570 Log2(("ISO/UDF: %-32s\n%.460Rhxd\n", "ImplementationUse.ab[460]:", &pDesc->ImplementationUse.ab[0]));
2571 }
2572#endif
2573
2574 RT_NOREF(pThis, pDesc, pErrInfo);
2575 return VINF_SUCCESS;
2576}
2577
2578
2579
2580typedef struct RTFSISOSEENSEQENCES
2581{
2582 /** Number of sequences we've seen thus far. */
2583 uint32_t cSequences;
2584 /** The per sequence data. */
2585 struct
2586 {
2587 uint64_t off; /**< Byte offset of the sequence. */
2588 uint32_t cb; /**< Size of the sequence. */
2589 } aSequences[8];
2590} RTFSISOSEENSEQENCES;
2591typedef RTFSISOSEENSEQENCES *PRTFSISOSEENSEQENCES;
2592
2593
2594/**
2595 * Process a VDS sequence, recursively dealing with volume descriptor pointers.
2596 *
2597 * @returns IPRT status code.
2598 * @param pThis The instance.
2599 * @param offSeq The byte offset of the sequence.
2600 * @param cbSeq The length of the sequence.
2601 * @param pbBuf Read buffer.
2602 * @param cbBuf Size of the read buffer. This is at least one
2603 * sector big.
2604 * @param cNestings The VDS nesting depth.
2605 * @param pErrInfo Where to return extended error info.
2606 */
2607static int rtFsIsoVolReadAndProcessUdfVdsSeq(PRTFSISOVOL pThis, uint64_t offSeq, uint32_t cbSeq, uint8_t *pbBuf, size_t cbBuf,
2608 uint32_t cNestings, PRTERRINFO pErrInfo)
2609{
2610 AssertReturn(cbBuf >= pThis->cbSector, VERR_INTERNAL_ERROR);
2611
2612 /*
2613 * Check nesting depth.
2614 */
2615 if (cNestings > 5)
2616 return RTErrInfoSet(pErrInfo, VERR_TOO_MUCH_DATA, "The volume descriptor sequence (VDS) is nested too deeply.");
2617
2618
2619 /*
2620 * Do the processing sector by sector to keep things simple.
2621 */
2622 uint32_t offInSeq = 0;
2623 while (offInSeq < cbSeq)
2624 {
2625 int rc;
2626
2627 /*
2628 * Read the next sector. Zero pad if less that a sector.
2629 */
2630 Assert((offInSeq & (pThis->cbSector - 1)) == 0);
2631 rc = RTVfsFileReadAt(pThis->hVfsBacking, offSeq + offInSeq, pbBuf, pThis->cbSector, NULL);
2632 if (RT_FAILURE(rc))
2633 return RTErrInfoSetF(pErrInfo, rc, "Error reading VDS content at %RX64 (LB %#x): %Rrc",
2634 offSeq + offInSeq, pThis->cbSector, rc);
2635 if (cbSeq - offInSeq < pThis->cbSector)
2636 memset(&pbBuf[cbSeq - offInSeq], 0, pThis->cbSector - (cbSeq - offInSeq));
2637
2638 /*
2639 * Check tag.
2640 */
2641 PCUDFTAG pTag = (PCUDFTAG)pbBuf;
2642 rc = rtFsIsoVolValidateUdfDescTagAndCrc(pTag, pThis->cbSector, UINT16_MAX, (offSeq + offInSeq) / pThis->cbSector, pErrInfo);
2643 if ( RT_SUCCESS(rc)
2644 || ( rc == VERR_ISOFS_INSUFFICIENT_DATA_FOR_DESC_CRC
2645 && ( pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC
2646 || pTag->idTag == UDF_TAG_ID_LOGICAL_VOLUME_DESC
2647 || pTag->idTag == UDF_TAG_ID_UNALLOCATED_SPACE_DESC
2648 )
2649 )
2650 )
2651 {
2652 switch (pTag->idTag)
2653 {
2654 case UDF_TAG_ID_PRIMARY_VOL_DESC:
2655 rc = rtFsIsoVolProcessUdfPrimaryVolDesc(pThis, (PCUDFPRIMARYVOLUMEDESC)pTag, pErrInfo);
2656 break;
2657
2658 case UDF_TAG_ID_IMPLEMENATION_USE_VOLUME_DESC:
2659 rc = rtFsIsoVolProcessUdfImplUseVolDesc(pThis, (PCUDFIMPLEMENTATIONUSEVOLUMEDESC)pTag, pErrInfo);
2660 break;
2661
2662 case UDF_TAG_ID_PARTITION_DESC:
2663 rc = rtFsIsoVolProcessUdfPartitionDesc(pThis, (PCUDFPARTITIONDESC)pTag, pErrInfo);
2664 break;
2665
2666 case UDF_TAG_ID_LOGICAL_VOLUME_DESC:
2667 rc = rtFsIsoVolProcessUdfLogicalVolumeDesc(pThis, (PCUDFLOGICALVOLUMEDESC)pTag, pErrInfo);
2668 break;
2669
2670 case UDF_TAG_ID_LOGICAL_VOLUME_INTEGRITY_DESC:
2671 Log(("ISO/UDF: Ignoring logical volume integrity descriptor at offset %#RX64.\n", offSeq + offInSeq));
2672 rc = VINF_SUCCESS;
2673 break;
2674
2675 case UDF_TAG_ID_UNALLOCATED_SPACE_DESC:
2676 Log(("ISO/UDF: Ignoring unallocated space descriptor at offset %#RX64.\n", offSeq + offInSeq));
2677 rc = VINF_SUCCESS;
2678 break;
2679
2680 case UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR:
2681 Log(("ISO/UDF: Ignoring AVDP in VDS (at offset %#RX64).\n", offSeq + offInSeq));
2682 rc = VINF_SUCCESS;
2683 break;
2684
2685 case UDF_TAG_ID_VOLUME_DESC_PTR:
2686 {
2687 PCUDFVOLUMEDESCPTR pVdp = (PCUDFVOLUMEDESCPTR)pTag;
2688 Log(("ISO/UDF: Processing volume descriptor pointer at offset %#RX64: %#x LB %#x (seq %#x); cNestings=%d\n",
2689 offSeq + offInSeq, pVdp->NextVolumeDescSeq.off, pVdp->NextVolumeDescSeq.cb,
2690 pVdp->uVolumeDescSeqNo, cNestings));
2691 rc = rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, (uint64_t)pVdp->NextVolumeDescSeq.off * pThis->cbSector,
2692 pVdp->NextVolumeDescSeq.cb, pbBuf, cbBuf, cNestings + 1, pErrInfo);
2693 break;
2694 }
2695
2696 case UDF_TAG_ID_TERMINATING_DESC:
2697 Log(("ISO/UDF: Terminating descriptor at offset %#RX64\n", offSeq + offInSeq));
2698 return VINF_SUCCESS;
2699
2700 default:
2701 return RTErrInfoSetF(pErrInfo, VERR_ISOFS_UNEXPECTED_VDS_DESC,
2702 "Unexpected/unknown VDS descriptor %#x at byte offset %#RX64",
2703 pThis->cbSector, offSeq + offInSeq);
2704 }
2705 }
2706 /* The descriptor sequence is usually zero padded to 16 sectors. Just
2707 ignore zero descriptors. */
2708 else if (rc != VERR_ISOFS_TAG_IS_ALL_ZEROS)
2709 return rc;
2710
2711 /*
2712 * Advance.
2713 */
2714 offInSeq += pThis->cbSector;
2715 }
2716
2717 return VINF_SUCCESS;
2718}
2719
2720
2721
2722/**
2723 * Processes a volume descriptor sequence (VDS).
2724 *
2725 * @returns IPRT status code.
2726 * @param pThis The instance.
2727 * @param offSeq The byte offset of the sequence.
2728 * @param cbSeq The length of the sequence.
2729 * @param pSeenSequences Structure where to keep track of VDSes we've already
2730 * processed, to avoid redoing one that we don't
2731 * understand.
2732 * @param pbBuf Read buffer.
2733 * @param cbBuf Size of the read buffer. This is at least one
2734 * sector big.
2735 * @param pErrInfo Where to report extended error information.
2736 */
2737static int rtFsIsoVolReadAndProcessUdfVds(PRTFSISOVOL pThis, uint64_t offSeq, uint32_t cbSeq,
2738 PRTFSISOSEENSEQENCES pSeenSequences, uint8_t *pbBuf, size_t cbBuf,
2739 PRTERRINFO pErrInfo)
2740{
2741 /*
2742 * Skip if already seen.
2743 */
2744 uint32_t i = pSeenSequences->cSequences;
2745 while (i-- > 0)
2746 if ( pSeenSequences->aSequences[i].off == offSeq
2747 && pSeenSequences->aSequences[i].cb == cbSeq)
2748 return VERR_NOT_FOUND;
2749
2750 /* Not seen, so add it. */
2751 Assert(pSeenSequences->cSequences + 1 <= RT_ELEMENTS(pSeenSequences->aSequences));
2752 pSeenSequences->aSequences[pSeenSequences->cSequences].cb = cbSeq;
2753 pSeenSequences->aSequences[pSeenSequences->cSequences].off = offSeq;
2754 pSeenSequences->cSequences++;
2755
2756 LogFlow(("ISO/UDF: Processing anchor volume descriptor sequence at offset %#RX64 LB %#RX32\n", offSeq, cbSeq));
2757
2758 /*
2759 * Reset the state before we start processing the descriptors.
2760 *
2761 * The processing has to be done in a different function because there may
2762 * be links to sub-sequences that needs to be processed. We do this by
2763 * recursing and check that we don't go to deep.
2764 */
2765
2766 /** @todo state reset */
2767
2768 return rtFsIsoVolReadAndProcessUdfVdsSeq(pThis, offSeq, cbSeq, pbBuf, cbBuf, 0, pErrInfo);
2769}
2770
2771
2772static int rtFsIsoVolReadAndHandleUdfAvdp(PRTFSISOVOL pThis, uint64_t offAvdp, uint8_t *pbBuf, size_t cbBuf,
2773 PRTFSISOSEENSEQENCES pSeenSequences, PRTERRINFO pErrInfo)
2774{
2775 /*
2776 * Try read the descriptor and validate its tag.
2777 */
2778 PUDFANCHORVOLUMEDESCPTR pAvdp = (PUDFANCHORVOLUMEDESCPTR)pbBuf;
2779 size_t cbAvdpRead = RT_MIN(pThis->cbSector, cbBuf);
2780 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offAvdp, pAvdp, cbAvdpRead, NULL);
2781 if (RT_SUCCESS(rc))
2782 {
2783 rc = rtFsIsoVolValidateUdfDescTag(&pAvdp->Tag, UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR, offAvdp / pThis->cbSector, pErrInfo);
2784 if (RT_SUCCESS(rc))
2785 {
2786 Log2(("ISO/UDF: AVDP: MainVolumeDescSeq=%#RX32 LB %#RX32, ReserveVolumeDescSeq=%#RX32 LB %#RX32\n",
2787 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb,
2788 pAvdp->ReserveVolumeDescSeq.off, pAvdp->ReserveVolumeDescSeq.cb));
2789
2790 /*
2791 * Try the main sequence if it looks sane.
2792 */
2793 UDFEXTENTAD const ReserveVolumeDescSeq = pAvdp->ReserveVolumeDescSeq;
2794 if ( pAvdp->MainVolumeDescSeq.off < pThis->cBackingSectors
2795 && (uint64_t)pAvdp->MainVolumeDescSeq.off
2796 + (pAvdp->MainVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
2797 <= pThis->cBackingSectors)
2798 {
2799 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)pAvdp->MainVolumeDescSeq.off * pThis->cbSector,
2800 pAvdp->MainVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
2801 if (RT_SUCCESS(rc))
2802 return rc;
2803 }
2804 else
2805 rc = RTErrInfoSetF(pErrInfo, VERR_NOT_FOUND,
2806 "MainVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
2807 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb, pThis->cBackingSectors);
2808 if (ReserveVolumeDescSeq.cb > 0)
2809 {
2810 if ( ReserveVolumeDescSeq.off < pThis->cBackingSectors
2811 && (uint64_t)ReserveVolumeDescSeq.off
2812 + (ReserveVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
2813 <= pThis->cBackingSectors)
2814 {
2815 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)ReserveVolumeDescSeq.off * pThis->cbSector,
2816 ReserveVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
2817 if (RT_SUCCESS(rc))
2818 return rc;
2819 }
2820 else if (RT_SUCCESS(rc))
2821 rc = RTErrInfoSetF(pErrInfo, VERR_NOT_FOUND,
2822 "ReserveVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
2823 ReserveVolumeDescSeq.off, ReserveVolumeDescSeq.cb, pThis->cBackingSectors);
2824 }
2825 }
2826 }
2827 else
2828 rc = RTErrInfoSetF(pErrInfo, rc,
2829 "Error reading sector at offset %#RX64 (anchor volume descriptor pointer): %Rrc", offAvdp, rc);
2830
2831 return rc;
2832}
2833
2834
2835/**
2836 * Goes looking for UDF when we've seens a volume recognition sequence.
2837 *
2838 * @returns IPRT status code.
2839 * @param pThis The volume instance data.
2840 * @param puUdfLevel The UDF level indicated by the VRS.
2841 * @param offUdfBootVolDesc The offset of the BOOT2 descriptor, UINT64_MAX
2842 * if not encountered.
2843 * @param pbBuf Buffer for reading into.
2844 * @param cbBuf The size of the buffer. At least one sector.
2845 * @param pErrInfo Where to return extended error info.
2846 */
2847static int rtFsIsoVolHandleUdfDetection(PRTFSISOVOL pThis, uint8_t *puUdfLevel, uint64_t offUdfBootVolDesc,
2848 uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
2849{
2850 NOREF(offUdfBootVolDesc);
2851
2852 /*
2853 * There are up to three anchor volume descriptor pointers that can give us
2854 * two different descriptor sequences each. Usually, the different AVDP
2855 * structures points to the same two sequences. The idea here is that
2856 * sectors may deteriorate and become unreadable, and we're supposed to try
2857 * out alternative sectors to get the job done. If we really took this
2858 * seriously, we could try read all sequences in parallel and use the
2859 * sectors that are good. However, we'll try keep things reasonably simple
2860 * since we'll most likely be reading from hard disks rather than optical
2861 * media.
2862 *
2863 * We keep track of which sequences we've processed so we don't try to do it
2864 * again when alternative AVDP sectors points to the same sequences.
2865 */
2866 RTFSISOSEENSEQENCES SeenSequences = { 0 };
2867 int rc = rtFsIsoVolReadAndHandleUdfAvdp(pThis, 256 * pThis->cbSector, pbBuf, cbBuf,
2868 &SeenSequences, pErrInfo);
2869 if (RT_FAILURE(rc))
2870 rc = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - 256 * pThis->cbSector,
2871 pbBuf, cbBuf, &SeenSequences, pErrInfo);
2872 if (RT_FAILURE(rc))
2873 rc = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - pThis->cbSector,
2874 pbBuf, cbBuf, &SeenSequences, pErrInfo);
2875 if (RT_SUCCESS(rc))
2876 return rc;
2877 *puUdfLevel = 0;
2878 return VINF_SUCCESS;
2879}
2880
2881
2882
2883#ifdef LOG_ENABLED
2884
2885/** Logging helper. */
2886static size_t rtFsIsoVolGetStrippedLength(const char *pachField, size_t cchField)
2887{
2888 while (cchField > 0 && pachField[cchField - 1] == ' ')
2889 cchField--;
2890 return cchField;
2891}
2892
2893/** Logging helper. */
2894static char *rtFsIsoVolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
2895{
2896 /* Check the format by looking for zero bytes. ISO-9660 doesn't allow zeros.
2897 This doesn't have to be a UTF-16BE string. */
2898 size_t cFirstZeros = 0;
2899 size_t cSecondZeros = 0;
2900 for (size_t off = 0; off + 1 < cchField; off += 2)
2901 {
2902 cFirstZeros += pachField[off] == '\0';
2903 cSecondZeros += pachField[off + 1] == '\0';
2904 }
2905
2906 int rc = VINF_SUCCESS;
2907 char *pszTmp = &pszDst[10];
2908 size_t cchRet = 0;
2909 if (cFirstZeros > cSecondZeros)
2910 {
2911 /* UTF-16BE / UTC-2BE: */
2912 if (cchField & 1)
2913 {
2914 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
2915 cchField--;
2916 else
2917 rc = VERR_INVALID_UTF16_ENCODING;
2918 }
2919 if (RT_SUCCESS(rc))
2920 {
2921 while ( cchField >= 2
2922 && pachField[cchField - 1] == ' '
2923 && pachField[cchField - 2] == '\0')
2924 cchField -= 2;
2925
2926 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
2927 }
2928 if (RT_SUCCESS(rc))
2929 {
2930 pszDst[0] = 'U';
2931 pszDst[1] = 'T';
2932 pszDst[2] = 'F';
2933 pszDst[3] = '-';
2934 pszDst[4] = '1';
2935 pszDst[5] = '6';
2936 pszDst[6] = 'B';
2937 pszDst[7] = 'E';
2938 pszDst[8] = ':';
2939 pszDst[9] = '\'';
2940 pszDst[10 + cchRet] = '\'';
2941 pszDst[10 + cchRet + 1] = '\0';
2942 }
2943 else
2944 RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
2945 }
2946 else if (cSecondZeros > 0)
2947 {
2948 /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
2949 if (cchField & 1)
2950 {
2951 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
2952 cchField--;
2953 else
2954 rc = VERR_INVALID_UTF16_ENCODING;
2955 }
2956 if (RT_SUCCESS(rc))
2957 {
2958 while ( cchField >= 2
2959 && pachField[cchField - 1] == '\0'
2960 && pachField[cchField - 2] == ' ')
2961 cchField -= 2;
2962
2963 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
2964 }
2965 if (RT_SUCCESS(rc))
2966 {
2967 pszDst[0] = 'U';
2968 pszDst[1] = 'T';
2969 pszDst[2] = 'F';
2970 pszDst[3] = '-';
2971 pszDst[4] = '1';
2972 pszDst[5] = '6';
2973 pszDst[6] = 'L';
2974 pszDst[7] = 'E';
2975 pszDst[8] = ':';
2976 pszDst[9] = '\'';
2977 pszDst[10 + cchRet] = '\'';
2978 pszDst[10 + cchRet + 1] = '\0';
2979 }
2980 else
2981 RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
2982 }
2983 else
2984 {
2985 /* ASSUME UTF-8/ASCII. */
2986 while ( cchField > 0
2987 && pachField[cchField - 1] == ' ')
2988 cchField--;
2989 rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
2990 if (RT_SUCCESS(rc))
2991 RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
2992 else
2993 RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
2994 }
2995 return pszDst;
2996}
2997
2998
2999/**
3000 * Logs the primary or supplementary volume descriptor
3001 *
3002 * @param pVolDesc The descriptor.
3003 */
3004static void rtFsIsoVolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
3005{
3006 if (LogIs2Enabled())
3007 {
3008 char szTmp[384];
3009 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
3010 Log2(("ISO9660: achSystemId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
3011 Log2(("ISO9660: achVolumeId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
3012 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
3013 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
3014 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIsoVolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
3015 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
3016 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
3017 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
3018 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
3019 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
3020 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
3021 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
3022 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
3023 Log2(("ISO9660: achVolumeSetId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
3024 Log2(("ISO9660: achPublisherId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
3025 Log2(("ISO9660: achDataPreparerId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
3026 Log2(("ISO9660: achApplicationId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
3027 Log2(("ISO9660: achCopyrightFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
3028 Log2(("ISO9660: achAbstractFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
3029 Log2(("ISO9660: achBibliographicFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
3030 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
3031 pVolDesc->BirthTime.achYear,
3032 pVolDesc->BirthTime.achMonth,
3033 pVolDesc->BirthTime.achDay,
3034 pVolDesc->BirthTime.achHour,
3035 pVolDesc->BirthTime.achMinute,
3036 pVolDesc->BirthTime.achSecond,
3037 pVolDesc->BirthTime.achCentisecond,
3038 pVolDesc->BirthTime.offUtc*4/60));
3039 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
3040 pVolDesc->ModifyTime.achYear,
3041 pVolDesc->ModifyTime.achMonth,
3042 pVolDesc->ModifyTime.achDay,
3043 pVolDesc->ModifyTime.achHour,
3044 pVolDesc->ModifyTime.achMinute,
3045 pVolDesc->ModifyTime.achSecond,
3046 pVolDesc->ModifyTime.achCentisecond,
3047 pVolDesc->ModifyTime.offUtc*4/60));
3048 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
3049 pVolDesc->ExpireTime.achYear,
3050 pVolDesc->ExpireTime.achMonth,
3051 pVolDesc->ExpireTime.achDay,
3052 pVolDesc->ExpireTime.achHour,
3053 pVolDesc->ExpireTime.achMinute,
3054 pVolDesc->ExpireTime.achSecond,
3055 pVolDesc->ExpireTime.achCentisecond,
3056 pVolDesc->ExpireTime.offUtc*4/60));
3057 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
3058 pVolDesc->EffectiveTime.achYear,
3059 pVolDesc->EffectiveTime.achMonth,
3060 pVolDesc->EffectiveTime.achDay,
3061 pVolDesc->EffectiveTime.achHour,
3062 pVolDesc->EffectiveTime.achMinute,
3063 pVolDesc->EffectiveTime.achSecond,
3064 pVolDesc->EffectiveTime.achCentisecond,
3065 pVolDesc->EffectiveTime.offUtc*4/60));
3066 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
3067 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
3068
3069 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
3070 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
3071 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
3072 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
3073 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
3074 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
3075 pVolDesc->RootDir.DirRec.RecTime.bMonth,
3076 pVolDesc->RootDir.DirRec.RecTime.bDay,
3077 pVolDesc->RootDir.DirRec.RecTime.bHour,
3078 pVolDesc->RootDir.DirRec.RecTime.bMinute,
3079 pVolDesc->RootDir.DirRec.RecTime.bSecond,
3080 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
3081 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
3082 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
3083 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
3084 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
3085 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
3086 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
3087 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
3088 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
3089 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
3090 {
3091 Log2(("ISO9660: RootDir System Use:\n%.*Rhxd\n",
3092 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
3093 }
3094 }
3095}
3096
3097#endif /* LOG_ENABLED */
3098
3099/**
3100 * Deal with a root directory from a primary or supplemental descriptor.
3101 *
3102 * @returns IPRT status code.
3103 * @param pThis The ISO 9660 instance being initialized.
3104 * @param pRootDir The root directory record to check out.
3105 * @param pDstRootDir Where to store a copy of the root dir record.
3106 * @param pErrInfo Where to return additional error info. Can be NULL.
3107 */
3108static int rtFsIsoVolHandleRootDir(PRTFSISOVOL pThis, PCISO9660DIRREC pRootDir,
3109 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
3110{
3111 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
3112 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
3113 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
3114
3115 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
3116 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3117 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
3118 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
3119 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3120 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
3121
3122 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
3123 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
3124 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
3125 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
3126 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
3127
3128 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
3129 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
3130 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
3131
3132 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
3133 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
3134 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
3135 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
3136 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3137 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
3138 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
3139
3140 /*
3141 * Seems okay, copy it.
3142 */
3143 *pDstRootDir = *pRootDir;
3144 return VINF_SUCCESS;
3145}
3146
3147
3148/**
3149 * Deal with a primary volume descriptor.
3150 *
3151 * @returns IPRT status code.
3152 * @param pThis The ISO 9660 instance being initialized.
3153 * @param pVolDesc The volume descriptor to handle.
3154 * @param offVolDesc The disk offset of the volume descriptor.
3155 * @param pRootDir Where to return a copy of the root directory record.
3156 * @param poffRootDirRec Where to return the disk offset of the root dir.
3157 * @param pErrInfo Where to return additional error info. Can be NULL.
3158 */
3159static int rtFsIsoVolHandlePrimaryVolDesc(PRTFSISOVOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
3160 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
3161{
3162 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
3163 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3164 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
3165
3166 /*
3167 * We need the block size ...
3168 */
3169 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
3170 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
3171 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
3172 || pThis->cbBlock / pThis->cbSector < 1)
3173 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
3174 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
3175 if (pThis->cbBlock / pThis->cbSector > 128)
3176 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
3177
3178 /*
3179 * ... volume space size ...
3180 */
3181 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
3182 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
3183 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
3184 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
3185 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
3186
3187 /*
3188 * ... number of volumes in the set ...
3189 */
3190 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
3191 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
3192 || pThis->cVolumesInSet == 0)
3193 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
3194 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
3195 if (pThis->cVolumesInSet > 32)
3196 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
3197
3198 /*
3199 * ... primary volume sequence ID ...
3200 */
3201 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
3202 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
3203 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
3204 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
3205 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
3206 || pThis->idPrimaryVol < 1)
3207 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3208 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
3209
3210 /*
3211 * ... and the root directory record.
3212 */
3213 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
3214 return rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
3215}
3216
3217
3218/**
3219 * Deal with a supplementary volume descriptor.
3220 *
3221 * @returns IPRT status code.
3222 * @param pThis The ISO 9660 instance being initialized.
3223 * @param pVolDesc The volume descriptor to handle.
3224 * @param offVolDesc The disk offset of the volume descriptor.
3225 * @param pbUcs2Level Where to return the joliet level, if found. Caller
3226 * initializes this to zero, we'll return 1, 2 or 3 if
3227 * joliet was detected.
3228 * @param pRootDir Where to return the root directory, if found.
3229 * @param poffRootDirRec Where to return the disk offset of the root dir.
3230 * @param pErrInfo Where to return additional error info. Can be NULL.
3231 */
3232static int rtFsIsoVolHandleSupplementaryVolDesc(PRTFSISOVOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
3233 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
3234 PRTERRINFO pErrInfo)
3235{
3236 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
3237 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3238 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
3239
3240 /*
3241 * Is this a joliet volume descriptor? If not, we probably don't need to
3242 * care about it.
3243 */
3244 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
3245 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
3246 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
3247 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
3248 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
3249 return VINF_SUCCESS;
3250
3251 /*
3252 * Skip if joliet is unwanted.
3253 */
3254 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
3255 return VINF_SUCCESS;
3256
3257 /*
3258 * Check that the joliet descriptor matches the primary one.
3259 * Note! These are our assumptions and may be wrong.
3260 */
3261 if (pThis->cbBlock == 0)
3262 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3263 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
3264 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
3265 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3266 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
3267 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
3268 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
3269 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3270 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
3271 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
3272 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
3273 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3274 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
3275 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
3276 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
3277 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3278 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
3279 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
3280
3281 if (*pbUcs2Level != 0)
3282 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
3283
3284 /*
3285 * Switch to the joliet root dir as it has UTF-16 stuff in it.
3286 */
3287 int rc = rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
3288 if (RT_SUCCESS(rc))
3289 {
3290 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
3291 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
3292 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
3293 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
3294 }
3295 return rc;
3296}
3297
3298
3299
3300/**
3301 * Worker for RTFsIso9660VolOpen.
3302 *
3303 * @returns IPRT status code.
3304 * @param pThis The ISO VFS instance to initialize.
3305 * @param hVfsSelf The ISO VFS handle (no reference consumed).
3306 * @param hVfsBacking The file backing the alleged FAT file system.
3307 * Reference is consumed (via rtFsIsoVol_Close).
3308 * @param fFlags Flags, RTFSISO9660_F_XXX.
3309 * @param pErrInfo Where to return additional error info. Can be NULL.
3310 */
3311static int rtFsIsoVolTryInit(PRTFSISOVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
3312{
3313 uint32_t const cbSector = 2048;
3314
3315 /*
3316 * First initialize the state so that rtFsIsoVol_Destroy won't trip up.
3317 */
3318 pThis->hVfsSelf = hVfsSelf;
3319 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIsoVol_Destroy releases it. */
3320 pThis->cbBacking = 0;
3321 pThis->cBackingSectors = 0;
3322 pThis->fFlags = fFlags;
3323 pThis->cbSector = cbSector;
3324 pThis->cbBlock = 0;
3325 pThis->cBlocksInPrimaryVolumeSpace = 0;
3326 pThis->cbPrimaryVolumeSpace = 0;
3327 pThis->cVolumesInSet = 0;
3328 pThis->idPrimaryVol = UINT32_MAX;
3329 pThis->fIsUtf16 = false;
3330 pThis->pRootDir = NULL;
3331
3332 /*
3333 * Get stuff that may fail.
3334 */
3335 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
3336 if (RT_SUCCESS(rc))
3337 pThis->cBackingSectors = pThis->cbBacking / pThis->cbSector;
3338 else
3339 return rc;
3340
3341 /*
3342 * Read the volume descriptors starting at logical sector 16.
3343 */
3344 union
3345 {
3346 uint8_t ab[_4K];
3347 uint16_t au16[_4K / 2];
3348 uint32_t au32[_4K / 4];
3349 ISO9660VOLDESCHDR VolDescHdr;
3350 ISO9660BOOTRECORD BootRecord;
3351 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
3352 ISO9660SUPVOLDESC SupVolDesc;
3353 ISO9660VOLPARTDESC VolPartDesc;
3354 } Buf;
3355 RT_ZERO(Buf);
3356
3357 uint64_t offRootDirRec = UINT64_MAX;
3358 ISO9660DIRREC RootDir;
3359 RT_ZERO(RootDir);
3360
3361 uint64_t offJolietRootDirRec = UINT64_MAX;
3362 uint8_t bJolietUcs2Level = 0;
3363 ISO9660DIRREC JolietRootDir;
3364 RT_ZERO(JolietRootDir);
3365
3366 uint8_t uUdfLevel = 0;
3367 uint64_t offUdfBootVolDesc = UINT64_MAX;
3368
3369 uint32_t cPrimaryVolDescs = 0;
3370 uint32_t cSupplementaryVolDescs = 0;
3371 uint32_t cBootRecordVolDescs = 0;
3372 uint32_t offVolDesc = 16 * cbSector;
3373 enum
3374 {
3375 kStateStart = 0,
3376 kStateNoSeq,
3377 kStateCdSeq,
3378 kStateUdfSeq
3379 } enmState = kStateStart;
3380 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
3381 {
3382 if (iVolDesc > 32)
3383 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "More than 32 volume descriptors, doesn't seem right...");
3384
3385 /* Read the next one and check the signature. */
3386 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
3387 if (RT_FAILURE(rc))
3388 return RTErrInfoSetF(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
3389
3390#define MATCH_STD_ID(a_achStdId1, a_szStdId2) \
3391 ( (a_achStdId1)[0] == (a_szStdId2)[0] \
3392 && (a_achStdId1)[1] == (a_szStdId2)[1] \
3393 && (a_achStdId1)[2] == (a_szStdId2)[2] \
3394 && (a_achStdId1)[3] == (a_szStdId2)[3] \
3395 && (a_achStdId1)[4] == (a_szStdId2)[4] )
3396#define MATCH_HDR(a_pStd, a_bType2, a_szStdId2, a_bVer2) \
3397 ( MATCH_STD_ID((a_pStd)->achStdId, a_szStdId2) \
3398 && (a_pStd)->bDescType == (a_bType2) \
3399 && (a_pStd)->bDescVersion == (a_bVer2) )
3400
3401 /*
3402 * ISO 9660 ("CD001").
3403 */
3404 if ( ( enmState == kStateStart
3405 || enmState == kStateCdSeq
3406 || enmState == kStateNoSeq)
3407 && MATCH_STD_ID(Buf.VolDescHdr.achStdId, ISO9660VOLDESC_STD_ID) )
3408 {
3409 enmState = kStateCdSeq;
3410
3411 /* Do type specific handling. */
3412 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
3413 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
3414 {
3415 cPrimaryVolDescs++;
3416 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
3417 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3418 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
3419#ifdef LOG_ENABLED
3420 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
3421#endif
3422 if (cPrimaryVolDescs > 1)
3423 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
3424 rc = rtFsIsoVolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
3425 }
3426 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
3427 {
3428 cSupplementaryVolDescs++;
3429 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
3430 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3431 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
3432#ifdef LOG_ENABLED
3433 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
3434#endif
3435 rc = rtFsIsoVolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
3436 &offJolietRootDirRec, pErrInfo);
3437 }
3438 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
3439 {
3440 cBootRecordVolDescs++;
3441 }
3442 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
3443 {
3444 if (!cPrimaryVolDescs)
3445 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
3446 enmState = kStateNoSeq;
3447 }
3448 else
3449 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
3450 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
3451 }
3452 /*
3453 * UDF volume recognition sequence (VRS).
3454 */
3455 else if ( ( enmState == kStateNoSeq
3456 || enmState == kStateStart)
3457 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BEGIN, UDF_EXT_VOL_DESC_VERSION) )
3458 {
3459 if (uUdfLevel == 0)
3460 enmState = kStateUdfSeq;
3461 else
3462 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BEA01 sequence is supported");
3463 }
3464 else if ( enmState == kStateUdfSeq
3465 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_02, UDF_EXT_VOL_DESC_VERSION) )
3466 uUdfLevel = 2;
3467 else if ( enmState == kStateUdfSeq
3468 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_03, UDF_EXT_VOL_DESC_VERSION) )
3469 uUdfLevel = 3;
3470 else if ( enmState == kStateUdfSeq
3471 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BOOT, UDF_EXT_VOL_DESC_VERSION) )
3472 {
3473 if (offUdfBootVolDesc == UINT64_MAX)
3474 offUdfBootVolDesc = iVolDesc * cbSector;
3475 else
3476 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BOOT2 descriptor is supported");
3477 }
3478 else if ( enmState == kStateUdfSeq
3479 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_TERM, UDF_EXT_VOL_DESC_VERSION) )
3480 {
3481 if (uUdfLevel != 0)
3482 enmState = kStateNoSeq;
3483 else
3484 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Found BEA01 & TEA01, but no NSR02 or NSR03 descriptors");
3485 }
3486 /*
3487 * Unknown, probably the end.
3488 */
3489 else if (enmState == kStateNoSeq)
3490 break;
3491 else if (enmState == kStateStart)
3492 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3493 "Not ISO? Unable to recognize volume descriptor signature: %.5Rhxs", Buf.VolDescHdr.achStdId);
3494 else if (enmState == kStateCdSeq)
3495 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3496 "Missing ISO 9660 terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
3497 else if (enmState == kStateUdfSeq)
3498 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3499 "Missing UDF terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
3500 else
3501 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3502 "Unknown volume descriptor signature found at sector %u: %.5Rhxs",
3503 16 + iVolDesc, Buf.VolDescHdr.achStdId);
3504 if (RT_FAILURE(rc))
3505 return rc;
3506 }
3507
3508 /*
3509 * If we found a UDF VRS and are interested in UDF, we have more work to do here.
3510 */
3511 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF) && /* Just disable this code for now: */ (fFlags & RT_BIT(24)))
3512 {
3513 Log(("rtFsIsoVolTryInit: uUdfLevel=%d\n", uUdfLevel));
3514 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
3515 if (RT_FAILURE(rc))
3516 return rc;
3517 }
3518#if 0
3519 return VERR_NOT_IMPLEMENTED;
3520#else
3521
3522 /*
3523 * We may be faced with choosing between joliet and rock ridge (we won't
3524 * have this choice when RTFSISO9660_F_NO_JOLIET is set). The joliet
3525 * option is generally favorable as we don't have to guess wrt to the file
3526 * name encoding. So, we'll pick that for now.
3527 *
3528 * Note! Should we change this preference for joliet, there fun wrt making sure
3529 * there really is rock ridge stuff in the primary volume as well as
3530 * making sure there really is anything of value in the primary volume.
3531 */
3532 if (bJolietUcs2Level != 0)
3533 {
3534 pThis->fIsUtf16 = true;
3535 return rtFsIsoDirShrd_New9660(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
3536 }
3537 return rtFsIsoDirShrd_New9660(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
3538#endif
3539}
3540
3541
3542/**
3543 * Opens an ISO 9660 file system volume.
3544 *
3545 * @returns IPRT status code.
3546 * @param hVfsFileIn The file or device backing the volume.
3547 * @param fFlags RTFSISO9660_F_XXX.
3548 * @param phVfs Where to return the virtual file system handle.
3549 * @param pErrInfo Where to return additional error information.
3550 */
3551RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
3552{
3553 /*
3554 * Quick input validation.
3555 */
3556 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
3557 *phVfs = NIL_RTVFS;
3558 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
3559
3560 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
3561 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
3562
3563 /*
3564 * Create a new FAT VFS instance and try initialize it using the given input file.
3565 */
3566 RTVFS hVfs = NIL_RTVFS;
3567 void *pvThis = NULL;
3568 int rc = RTVfsNew(&g_rtFsIsoVolOps, sizeof(RTFSISOVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
3569 if (RT_SUCCESS(rc))
3570 {
3571 rc = rtFsIsoVolTryInit((PRTFSISOVOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
3572 if (RT_SUCCESS(rc))
3573 *phVfs = hVfs;
3574 else
3575 RTVfsRelease(hVfs);
3576 }
3577 else
3578 RTVfsFileRelease(hVfsFileIn);
3579 return rc;
3580}
3581
3582
3583/**
3584 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
3585 */
3586static DECLCALLBACK(int) rtVfsChainIso9660Vol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
3587 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
3588{
3589 RT_NOREF(pProviderReg, pSpec);
3590
3591 /*
3592 * Basic checks.
3593 */
3594 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
3595 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
3596 if ( pElement->enmType != RTVFSOBJTYPE_VFS
3597 && pElement->enmType != RTVFSOBJTYPE_DIR)
3598 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
3599 if (pElement->cArgs > 1)
3600 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
3601
3602 /*
3603 * Parse the flag if present, save in pElement->uProvider.
3604 */
3605 uint32_t fFlags = 0;
3606 if (pElement->cArgs > 0)
3607 {
3608 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
3609 {
3610 const char *psz = pElement->paArgs[iArg].psz;
3611 if (*psz)
3612 {
3613 if (!strcmp(psz, "nojoliet"))
3614 fFlags |= RTFSISO9660_F_NO_JOLIET;
3615 else if (!strcmp(psz, "norock"))
3616 fFlags |= RTFSISO9660_F_NO_ROCK;
3617 else if (!strcmp(psz, "noudf"))
3618 fFlags |= RTFSISO9660_F_NO_UDF;
3619 else
3620 {
3621 *poffError = pElement->paArgs[iArg].offSpec;
3622 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
3623 }
3624 }
3625 }
3626 }
3627
3628 pElement->uProvider = fFlags;
3629 return VINF_SUCCESS;
3630}
3631
3632
3633/**
3634 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
3635 */
3636static DECLCALLBACK(int) rtVfsChainIso9660Vol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
3637 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
3638 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
3639{
3640 RT_NOREF(pProviderReg, pSpec, poffError);
3641
3642 int rc;
3643 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
3644 if (hVfsFileIn != NIL_RTVFSFILE)
3645 {
3646 RTVFS hVfs;
3647 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
3648 RTVfsFileRelease(hVfsFileIn);
3649 if (RT_SUCCESS(rc))
3650 {
3651 *phVfsObj = RTVfsObjFromVfs(hVfs);
3652 RTVfsRelease(hVfs);
3653 if (*phVfsObj != NIL_RTVFSOBJ)
3654 return VINF_SUCCESS;
3655 rc = VERR_VFS_CHAIN_CAST_FAILED;
3656 }
3657 }
3658 else
3659 rc = VERR_VFS_CHAIN_CAST_FAILED;
3660 return rc;
3661}
3662
3663
3664/**
3665 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
3666 */
3667static DECLCALLBACK(bool) rtVfsChainIso9660Vol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
3668 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
3669 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
3670{
3671 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
3672 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
3673 || !pReuseElement->paArgs[0].uProvider)
3674 return true;
3675 return false;
3676}
3677
3678
3679/** VFS chain element 'file'. */
3680static RTVFSCHAINELEMENTREG g_rtVfsChainIso9660VolReg =
3681{
3682 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
3683 /* fReserved = */ 0,
3684 /* pszName = */ "isofs",
3685 /* ListEntry = */ { NULL, NULL },
3686 /* pszHelp = */ "Open a ISO 9660 file system, requires a file object on the left side.\n"
3687 "The 'noudf' option make it ignore any UDF.\n"
3688 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
3689 "The 'norock' option make it ignore any rock ridge info.\n",
3690 /* pfnValidate = */ rtVfsChainIso9660Vol_Validate,
3691 /* pfnInstantiate = */ rtVfsChainIso9660Vol_Instantiate,
3692 /* pfnCanReuseElement = */ rtVfsChainIso9660Vol_CanReuseElement,
3693 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
3694};
3695
3696RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIso9660VolReg, rtVfsChainIso9660VolReg);
3697
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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