VirtualBox

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

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

isovfs.cpp: More UDF code (disabled). [build fix]

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

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