VirtualBox

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

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

isovfs.cpp: More UDF code (disabled).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 119.2 KB
Line 
1/* $Id: isovfs.cpp 68694 2017-09-07 14:51:09Z 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 Assert(cbBuf >= _2K);
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
2287 NOREF(pThis); NOREF(pbBuf); NOREF(cbBuf); NOREF(pErrInfo);
2288 return VINF_SUCCESS;
2289}
2290
2291
2292static int rtFsIsoVolReadAndHandleUdfAvdp(PRTFSISOVOL pThis, uint64_t offAvdp, uint8_t *pbBuf, size_t cbBuf,
2293 PRTFSISOSEENSEQENCES pSeenSequences, PRTERRINFO pErrInfo)
2294{
2295 /*
2296 * Try read the descriptor and validate its tag.
2297 */
2298 PUDFANCHORVOLUMEDESCPTR pAvdp = (PUDFANCHORVOLUMEDESCPTR)pbBuf;
2299 size_t cbAvdpRead = RT_MIN(pThis->cbSector, cbBuf);
2300 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offAvdp, pAvdp, cbAvdpRead, NULL);
2301 if (RT_SUCCESS(rc))
2302 {
2303 rc = rtFsIsoVolValidateUdfDescTag(&pAvdp->Tag, UDF_TAG_ID_ANCHOR_VOLUME_DESC_PTR, offAvdp / pThis->cbSector, pErrInfo);
2304 if (RT_SUCCESS(rc))
2305 {
2306 Log2(("ISO/UDF: AVDP: MainVolumeDescSeq=%#RX32 LB %#RX32, ReserveVolumeDescSeq=%#RX32 LB %#RX32\n",
2307 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb,
2308 pAvdp->ReserveVolumeDescSeq.off, pAvdp->ReserveVolumeDescSeq.cb));
2309
2310 /*
2311 * Try the main sequence if it looks sane.
2312 */
2313 UDFEXTENTAD const ReserveVolumeDescSeq = pAvdp->ReserveVolumeDescSeq;
2314 if ( pAvdp->MainVolumeDescSeq.off < pThis->cBackingSectors
2315 && (uint64_t)pAvdp->MainVolumeDescSeq.off
2316 + (pAvdp->MainVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
2317 <= pThis->cBackingSectors)
2318 {
2319 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)pAvdp->MainVolumeDescSeq.off * pThis->cbSector,
2320 pAvdp->MainVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
2321 if (RT_SUCCESS(rc))
2322 return rc;
2323 }
2324 else
2325 rc = RTErrInfoSetF(pErrInfo, VERR_NOT_FOUND,
2326 "MainVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
2327 pAvdp->MainVolumeDescSeq.off, pAvdp->MainVolumeDescSeq.cb, pThis->cBackingSectors);
2328 if (ReserveVolumeDescSeq.cb > 0)
2329 {
2330 if ( ReserveVolumeDescSeq.off < pThis->cBackingSectors
2331 && (uint64_t)ReserveVolumeDescSeq.off
2332 + (ReserveVolumeDescSeq.cb + pThis->cbSector - 1) / pThis->cbSector
2333 <= pThis->cBackingSectors)
2334 {
2335 rc = rtFsIsoVolReadAndProcessUdfVds(pThis, (uint64_t)ReserveVolumeDescSeq.off * pThis->cbSector,
2336 ReserveVolumeDescSeq.cb, pSeenSequences, pbBuf, cbBuf, pErrInfo);
2337 if (RT_SUCCESS(rc))
2338 return rc;
2339 }
2340 else if (RT_SUCCESS(rc))
2341 rc = RTErrInfoSetF(pErrInfo, VERR_NOT_FOUND,
2342 "ReserveVolumeDescSeq is out of bounds: sector %#RX32 LB %#RX32 bytes, image is %#RX64 sectors",
2343 ReserveVolumeDescSeq.off, ReserveVolumeDescSeq.cb, pThis->cBackingSectors);
2344 }
2345 }
2346 }
2347 else
2348 rc = RTErrInfoSetF(pErrInfo, rc,
2349 "Error reading sector at offset %#RX64 (anchor volume descriptor pointer): %Rrc", offAvdp, rc);
2350
2351 return rc;
2352}
2353
2354
2355/**
2356 * Goes looking for UDF when we've seens a volume recognition sequence.
2357 *
2358 * @returns IPRT status code.
2359 * @param pThis The volume instance data.
2360 * @param puUdfLevel The UDF level indicated by the VRS.
2361 * @param offUdfBootVolDesc The offset of the BOOT2 descriptor, UINT64_MAX
2362 * if not encountered.
2363 * @param pbBuf Buffer for reading into.
2364 * @param cbBuf The size of the buffer. At least one sector.
2365 * @param pErrInfo Where to return extended error info.
2366 */
2367static int rtFsIsoVolHandleUdfDetection(PRTFSISOVOL pThis, uint8_t *puUdfLevel, uint64_t offUdfBootVolDesc,
2368 uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
2369{
2370 NOREF(offUdfBootVolDesc);
2371
2372 /*
2373 * There are up to three anchor volume descriptor pointers that can give us
2374 * two different descriptor sequences each. Usually, the different AVDP
2375 * structures points to the same two sequences. The idea here is that
2376 * sectors may deteriorate and become unreadable, and we're supposed to try
2377 * out alternative sectors to get the job done. If we really took this
2378 * seriously, we could try read all sequences in parallel and use the
2379 * sectors that are good. However, we'll try keep things reasonably simple
2380 * since we'll most likely be reading from hard disks rather than optical
2381 * media.
2382 *
2383 * We keep track of which sequences we've processed so we don't try to do it
2384 * again when alternative AVDP sectors points to the same sequences.
2385 */
2386 RTFSISOSEENSEQENCES SeenSequences = { 0 };
2387 int rc = rtFsIsoVolReadAndHandleUdfAvdp(pThis, 256 * pThis->cbSector, pbBuf, cbBuf,
2388 &SeenSequences, pErrInfo);
2389 if (RT_FAILURE(rc))
2390 rc = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - 256 * pThis->cbSector,
2391 pbBuf, cbBuf, &SeenSequences, pErrInfo);
2392 if (RT_FAILURE(rc))
2393 rc = rtFsIsoVolReadAndHandleUdfAvdp(pThis, pThis->cbBacking - pThis->cbSector,
2394 pbBuf, cbBuf, &SeenSequences, pErrInfo);
2395 if (RT_SUCCESS(rc))
2396 return rc;
2397 *puUdfLevel = 0;
2398 return VINF_SUCCESS;
2399}
2400
2401
2402
2403#ifdef LOG_ENABLED
2404
2405/** Logging helper. */
2406static size_t rtFsIsoVolGetStrippedLength(const char *pachField, size_t cchField)
2407{
2408 while (cchField > 0 && pachField[cchField - 1] == ' ')
2409 cchField--;
2410 return cchField;
2411}
2412
2413/** Logging helper. */
2414static char *rtFsIsoVolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
2415{
2416 /* Check the format by looking for zero bytes. ISO-9660 doesn't allow zeros.
2417 This doesn't have to be a UTF-16BE string. */
2418 size_t cFirstZeros = 0;
2419 size_t cSecondZeros = 0;
2420 for (size_t off = 0; off + 1 < cchField; off += 2)
2421 {
2422 cFirstZeros += pachField[off] == '\0';
2423 cSecondZeros += pachField[off + 1] == '\0';
2424 }
2425
2426 int rc = VINF_SUCCESS;
2427 char *pszTmp = &pszDst[10];
2428 size_t cchRet = 0;
2429 if (cFirstZeros > cSecondZeros)
2430 {
2431 /* UTF-16BE / UTC-2BE: */
2432 if (cchField & 1)
2433 {
2434 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
2435 cchField--;
2436 else
2437 rc = VERR_INVALID_UTF16_ENCODING;
2438 }
2439 if (RT_SUCCESS(rc))
2440 {
2441 while ( cchField >= 2
2442 && pachField[cchField - 1] == ' '
2443 && pachField[cchField - 2] == '\0')
2444 cchField -= 2;
2445
2446 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
2447 }
2448 if (RT_SUCCESS(rc))
2449 {
2450 pszDst[0] = 'U';
2451 pszDst[1] = 'T';
2452 pszDst[2] = 'F';
2453 pszDst[3] = '-';
2454 pszDst[4] = '1';
2455 pszDst[5] = '6';
2456 pszDst[6] = 'B';
2457 pszDst[7] = 'E';
2458 pszDst[8] = ':';
2459 pszDst[9] = '\'';
2460 pszDst[10 + cchRet] = '\'';
2461 pszDst[10 + cchRet + 1] = '\0';
2462 }
2463 else
2464 RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
2465 }
2466 else if (cSecondZeros > 0)
2467 {
2468 /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
2469 if (cchField & 1)
2470 {
2471 if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
2472 cchField--;
2473 else
2474 rc = VERR_INVALID_UTF16_ENCODING;
2475 }
2476 if (RT_SUCCESS(rc))
2477 {
2478 while ( cchField >= 2
2479 && pachField[cchField - 1] == '\0'
2480 && pachField[cchField - 2] == ' ')
2481 cchField -= 2;
2482
2483 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
2484 }
2485 if (RT_SUCCESS(rc))
2486 {
2487 pszDst[0] = 'U';
2488 pszDst[1] = 'T';
2489 pszDst[2] = 'F';
2490 pszDst[3] = '-';
2491 pszDst[4] = '1';
2492 pszDst[5] = '6';
2493 pszDst[6] = 'L';
2494 pszDst[7] = 'E';
2495 pszDst[8] = ':';
2496 pszDst[9] = '\'';
2497 pszDst[10 + cchRet] = '\'';
2498 pszDst[10 + cchRet + 1] = '\0';
2499 }
2500 else
2501 RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
2502 }
2503 else
2504 {
2505 /* ASSUME UTF-8/ASCII. */
2506 while ( cchField > 0
2507 && pachField[cchField - 1] == ' ')
2508 cchField--;
2509 rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
2510 if (RT_SUCCESS(rc))
2511 RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
2512 else
2513 RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
2514 }
2515 return pszDst;
2516}
2517
2518
2519/**
2520 * Logs the primary or supplementary volume descriptor
2521 *
2522 * @param pVolDesc The descriptor.
2523 */
2524static void rtFsIsoVolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
2525{
2526 if (LogIs2Enabled())
2527 {
2528 char szTmp[384];
2529 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
2530 Log2(("ISO9660: achSystemId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
2531 Log2(("ISO9660: achVolumeId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
2532 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
2533 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
2534 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIsoVolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
2535 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
2536 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
2537 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
2538 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
2539 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
2540 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
2541 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
2542 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
2543 Log2(("ISO9660: achVolumeSetId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
2544 Log2(("ISO9660: achPublisherId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
2545 Log2(("ISO9660: achDataPreparerId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
2546 Log2(("ISO9660: achApplicationId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
2547 Log2(("ISO9660: achCopyrightFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
2548 Log2(("ISO9660: achAbstractFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
2549 Log2(("ISO9660: achBibliographicFileId: %s\n", rtFsIsoVolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
2550 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
2551 pVolDesc->BirthTime.achYear,
2552 pVolDesc->BirthTime.achMonth,
2553 pVolDesc->BirthTime.achDay,
2554 pVolDesc->BirthTime.achHour,
2555 pVolDesc->BirthTime.achMinute,
2556 pVolDesc->BirthTime.achSecond,
2557 pVolDesc->BirthTime.achCentisecond,
2558 pVolDesc->BirthTime.offUtc*4/60));
2559 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
2560 pVolDesc->ModifyTime.achYear,
2561 pVolDesc->ModifyTime.achMonth,
2562 pVolDesc->ModifyTime.achDay,
2563 pVolDesc->ModifyTime.achHour,
2564 pVolDesc->ModifyTime.achMinute,
2565 pVolDesc->ModifyTime.achSecond,
2566 pVolDesc->ModifyTime.achCentisecond,
2567 pVolDesc->ModifyTime.offUtc*4/60));
2568 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
2569 pVolDesc->ExpireTime.achYear,
2570 pVolDesc->ExpireTime.achMonth,
2571 pVolDesc->ExpireTime.achDay,
2572 pVolDesc->ExpireTime.achHour,
2573 pVolDesc->ExpireTime.achMinute,
2574 pVolDesc->ExpireTime.achSecond,
2575 pVolDesc->ExpireTime.achCentisecond,
2576 pVolDesc->ExpireTime.offUtc*4/60));
2577 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
2578 pVolDesc->EffectiveTime.achYear,
2579 pVolDesc->EffectiveTime.achMonth,
2580 pVolDesc->EffectiveTime.achDay,
2581 pVolDesc->EffectiveTime.achHour,
2582 pVolDesc->EffectiveTime.achMinute,
2583 pVolDesc->EffectiveTime.achSecond,
2584 pVolDesc->EffectiveTime.achCentisecond,
2585 pVolDesc->EffectiveTime.offUtc*4/60));
2586 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
2587 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
2588
2589 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
2590 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
2591 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
2592 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
2593 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
2594 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
2595 pVolDesc->RootDir.DirRec.RecTime.bMonth,
2596 pVolDesc->RootDir.DirRec.RecTime.bDay,
2597 pVolDesc->RootDir.DirRec.RecTime.bHour,
2598 pVolDesc->RootDir.DirRec.RecTime.bMinute,
2599 pVolDesc->RootDir.DirRec.RecTime.bSecond,
2600 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
2601 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
2602 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
2603 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
2604 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
2605 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
2606 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
2607 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
2608 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
2609 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
2610 {
2611 Log2(("ISO9660: RootDir System Use:\n%.*Rhxd\n",
2612 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
2613 }
2614 }
2615}
2616
2617#endif /* LOG_ENABLED */
2618
2619/**
2620 * Deal with a root directory from a primary or supplemental descriptor.
2621 *
2622 * @returns IPRT status code.
2623 * @param pThis The ISO 9660 instance being initialized.
2624 * @param pRootDir The root directory record to check out.
2625 * @param pDstRootDir Where to store a copy of the root dir record.
2626 * @param pErrInfo Where to return additional error info. Can be NULL.
2627 */
2628static int rtFsIsoVolHandleRootDir(PRTFSISOVOL pThis, PCISO9660DIRREC pRootDir,
2629 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
2630{
2631 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
2632 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
2633 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
2634
2635 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
2636 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2637 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
2638 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
2639 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2640 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
2641
2642 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
2643 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
2644 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
2645 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
2646 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
2647
2648 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
2649 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
2650 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
2651
2652 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
2653 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
2654 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
2655 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
2656 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2657 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
2658 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
2659
2660 /*
2661 * Seems okay, copy it.
2662 */
2663 *pDstRootDir = *pRootDir;
2664 return VINF_SUCCESS;
2665}
2666
2667
2668/**
2669 * Deal with a primary volume descriptor.
2670 *
2671 * @returns IPRT status code.
2672 * @param pThis The ISO 9660 instance being initialized.
2673 * @param pVolDesc The volume descriptor to handle.
2674 * @param offVolDesc The disk offset of the volume descriptor.
2675 * @param pRootDir Where to return a copy of the root directory record.
2676 * @param poffRootDirRec Where to return the disk offset of the root dir.
2677 * @param pErrInfo Where to return additional error info. Can be NULL.
2678 */
2679static int rtFsIsoVolHandlePrimaryVolDesc(PRTFSISOVOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
2680 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
2681{
2682 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
2683 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2684 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
2685
2686 /*
2687 * We need the block size ...
2688 */
2689 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
2690 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
2691 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
2692 || pThis->cbBlock / pThis->cbSector < 1)
2693 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
2694 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
2695 if (pThis->cbBlock / pThis->cbSector > 128)
2696 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
2697
2698 /*
2699 * ... volume space size ...
2700 */
2701 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
2702 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
2703 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
2704 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
2705 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
2706
2707 /*
2708 * ... number of volumes in the set ...
2709 */
2710 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
2711 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
2712 || pThis->cVolumesInSet == 0)
2713 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
2714 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
2715 if (pThis->cVolumesInSet > 32)
2716 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
2717
2718 /*
2719 * ... primary volume sequence ID ...
2720 */
2721 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
2722 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
2723 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
2724 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
2725 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
2726 || pThis->idPrimaryVol < 1)
2727 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2728 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
2729
2730 /*
2731 * ... and the root directory record.
2732 */
2733 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
2734 return rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
2735}
2736
2737
2738/**
2739 * Deal with a supplementary volume descriptor.
2740 *
2741 * @returns IPRT status code.
2742 * @param pThis The ISO 9660 instance being initialized.
2743 * @param pVolDesc The volume descriptor to handle.
2744 * @param offVolDesc The disk offset of the volume descriptor.
2745 * @param pbUcs2Level Where to return the joliet level, if found. Caller
2746 * initializes this to zero, we'll return 1, 2 or 3 if
2747 * joliet was detected.
2748 * @param pRootDir Where to return the root directory, if found.
2749 * @param poffRootDirRec Where to return the disk offset of the root dir.
2750 * @param pErrInfo Where to return additional error info. Can be NULL.
2751 */
2752static int rtFsIsoVolHandleSupplementaryVolDesc(PRTFSISOVOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
2753 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
2754 PRTERRINFO pErrInfo)
2755{
2756 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
2757 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2758 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
2759
2760 /*
2761 * Is this a joliet volume descriptor? If not, we probably don't need to
2762 * care about it.
2763 */
2764 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
2765 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
2766 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
2767 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
2768 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
2769 return VINF_SUCCESS;
2770
2771 /*
2772 * Skip if joliet is unwanted.
2773 */
2774 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
2775 return VINF_SUCCESS;
2776
2777 /*
2778 * Check that the joliet descriptor matches the primary one.
2779 * Note! These are our assumptions and may be wrong.
2780 */
2781 if (pThis->cbBlock == 0)
2782 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2783 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
2784 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
2785 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2786 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
2787 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
2788 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
2789 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2790 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
2791 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
2792 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
2793 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2794 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
2795 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
2796 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
2797 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2798 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
2799 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
2800
2801 if (*pbUcs2Level != 0)
2802 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
2803
2804 /*
2805 * Switch to the joliet root dir as it has UTF-16 stuff in it.
2806 */
2807 int rc = rtFsIsoVolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
2808 if (RT_SUCCESS(rc))
2809 {
2810 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
2811 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
2812 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
2813 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
2814 }
2815 return rc;
2816}
2817
2818
2819
2820/**
2821 * Worker for RTFsIso9660VolOpen.
2822 *
2823 * @returns IPRT status code.
2824 * @param pThis The ISO VFS instance to initialize.
2825 * @param hVfsSelf The ISO VFS handle (no reference consumed).
2826 * @param hVfsBacking The file backing the alleged FAT file system.
2827 * Reference is consumed (via rtFsIsoVol_Close).
2828 * @param fFlags Flags, RTFSISO9660_F_XXX.
2829 * @param pErrInfo Where to return additional error info. Can be NULL.
2830 */
2831static int rtFsIsoVolTryInit(PRTFSISOVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
2832{
2833 uint32_t const cbSector = 2048;
2834
2835 /*
2836 * First initialize the state so that rtFsIsoVol_Destroy won't trip up.
2837 */
2838 pThis->hVfsSelf = hVfsSelf;
2839 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIsoVol_Destroy releases it. */
2840 pThis->cbBacking = 0;
2841 pThis->cBackingSectors = 0;
2842 pThis->fFlags = fFlags;
2843 pThis->cbSector = cbSector;
2844 pThis->cbBlock = 0;
2845 pThis->cBlocksInPrimaryVolumeSpace = 0;
2846 pThis->cbPrimaryVolumeSpace = 0;
2847 pThis->cVolumesInSet = 0;
2848 pThis->idPrimaryVol = UINT32_MAX;
2849 pThis->fIsUtf16 = false;
2850 pThis->pRootDir = NULL;
2851
2852 /*
2853 * Get stuff that may fail.
2854 */
2855 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
2856 if (RT_SUCCESS(rc))
2857 pThis->cBackingSectors = pThis->cbBacking / pThis->cbSector;
2858 else
2859 return rc;
2860
2861 /*
2862 * Read the volume descriptors starting at logical sector 16.
2863 */
2864 union
2865 {
2866 uint8_t ab[_4K];
2867 uint16_t au16[_4K / 2];
2868 uint32_t au32[_4K / 4];
2869 ISO9660VOLDESCHDR VolDescHdr;
2870 ISO9660BOOTRECORD BootRecord;
2871 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
2872 ISO9660SUPVOLDESC SupVolDesc;
2873 ISO9660VOLPARTDESC VolPartDesc;
2874 } Buf;
2875 RT_ZERO(Buf);
2876
2877 uint64_t offRootDirRec = UINT64_MAX;
2878 ISO9660DIRREC RootDir;
2879 RT_ZERO(RootDir);
2880
2881 uint64_t offJolietRootDirRec = UINT64_MAX;
2882 uint8_t bJolietUcs2Level = 0;
2883 ISO9660DIRREC JolietRootDir;
2884 RT_ZERO(JolietRootDir);
2885
2886 uint8_t uUdfLevel = 0;
2887 uint64_t offUdfBootVolDesc = UINT64_MAX;
2888
2889 uint32_t cPrimaryVolDescs = 0;
2890 uint32_t cSupplementaryVolDescs = 0;
2891 uint32_t cBootRecordVolDescs = 0;
2892 uint32_t offVolDesc = 16 * cbSector;
2893 enum
2894 {
2895 kStateStart = 0,
2896 kStateNoSeq,
2897 kStateCdSeq,
2898 kStateUdfSeq
2899 } enmState = kStateStart;
2900 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
2901 {
2902 if (iVolDesc > 32)
2903 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "More than 32 volume descriptors, doesn't seem right...");
2904
2905 /* Read the next one and check the signature. */
2906 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
2907 if (RT_FAILURE(rc))
2908 return RTErrInfoSetF(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
2909
2910#define MATCH_STD_ID(a_achStdId1, a_szStdId2) \
2911 ( (a_achStdId1)[0] == (a_szStdId2)[0] \
2912 && (a_achStdId1)[1] == (a_szStdId2)[1] \
2913 && (a_achStdId1)[2] == (a_szStdId2)[2] \
2914 && (a_achStdId1)[3] == (a_szStdId2)[3] \
2915 && (a_achStdId1)[4] == (a_szStdId2)[4] )
2916#define MATCH_HDR(a_pStd, a_bType2, a_szStdId2, a_bVer2) \
2917 ( MATCH_STD_ID((a_pStd)->achStdId, a_szStdId2) \
2918 && (a_pStd)->bDescType == (a_bType2) \
2919 && (a_pStd)->bDescVersion == (a_bVer2) )
2920
2921 /*
2922 * ISO 9660 ("CD001").
2923 */
2924 if ( ( enmState == kStateStart
2925 || enmState == kStateCdSeq
2926 || enmState == kStateNoSeq)
2927 && MATCH_STD_ID(Buf.VolDescHdr.achStdId, ISO9660VOLDESC_STD_ID) )
2928 {
2929 enmState = kStateCdSeq;
2930
2931 /* Do type specific handling. */
2932 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
2933 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
2934 {
2935 cPrimaryVolDescs++;
2936 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
2937 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2938 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
2939#ifdef LOG_ENABLED
2940 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
2941#endif
2942 if (cPrimaryVolDescs > 1)
2943 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
2944 rc = rtFsIsoVolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
2945 }
2946 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
2947 {
2948 cSupplementaryVolDescs++;
2949 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
2950 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2951 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
2952#ifdef LOG_ENABLED
2953 rtFsIsoVolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
2954#endif
2955 rc = rtFsIsoVolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
2956 &offJolietRootDirRec, pErrInfo);
2957 }
2958 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
2959 {
2960 cBootRecordVolDescs++;
2961 }
2962 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
2963 {
2964 if (!cPrimaryVolDescs)
2965 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
2966 enmState = kStateNoSeq;
2967 }
2968 else
2969 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2970 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
2971 }
2972 /*
2973 * UDF volume recognition sequence (VRS).
2974 */
2975 else if ( ( enmState == kStateNoSeq
2976 || enmState == kStateStart)
2977 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BEGIN, UDF_EXT_VOL_DESC_VERSION) )
2978 {
2979 if (uUdfLevel == 0)
2980 enmState = kStateUdfSeq;
2981 else
2982 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BEA01 sequence is supported");
2983 }
2984 else if ( enmState == kStateUdfSeq
2985 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_02, UDF_EXT_VOL_DESC_VERSION) )
2986 uUdfLevel = 2;
2987 else if ( enmState == kStateUdfSeq
2988 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_NSR_03, UDF_EXT_VOL_DESC_VERSION) )
2989 uUdfLevel = 3;
2990 else if ( enmState == kStateUdfSeq
2991 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_BOOT, UDF_EXT_VOL_DESC_VERSION) )
2992 {
2993 if (offUdfBootVolDesc == UINT64_MAX)
2994 offUdfBootVolDesc = iVolDesc * cbSector;
2995 else
2996 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Only one BOOT2 descriptor is supported");
2997 }
2998 else if ( enmState == kStateUdfSeq
2999 && MATCH_HDR(&Buf.VolDescHdr, UDF_EXT_VOL_DESC_TYPE, UDF_EXT_VOL_DESC_STD_ID_TERM, UDF_EXT_VOL_DESC_VERSION) )
3000 {
3001 if (uUdfLevel != 0)
3002 enmState = kStateNoSeq;
3003 else
3004 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Found BEA01 & TEA01, but no NSR02 or NSR03 descriptors");
3005 }
3006 /*
3007 * Unknown, probably the end.
3008 */
3009 else if (enmState == kStateNoSeq)
3010 break;
3011 else if (enmState == kStateStart)
3012 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3013 "Not ISO? Unable to recognize volume descriptor signature: %.5Rhxs", Buf.VolDescHdr.achStdId);
3014 else if (enmState == kStateCdSeq)
3015 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3016 "Missing ISO 9660 terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
3017 else if (enmState == kStateUdfSeq)
3018 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3019 "Missing UDF terminator volume descriptor? (Found %.5Rhxs)", Buf.VolDescHdr.achStdId);
3020 else
3021 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3022 "Unknown volume descriptor signature found at sector %u: %.5Rhxs",
3023 16 + iVolDesc, Buf.VolDescHdr.achStdId);
3024 if (RT_FAILURE(rc))
3025 return rc;
3026 }
3027
3028 /*
3029 * If we found a UDF VRS and are interested in UDF, we have more work to do here.
3030 */
3031 if (uUdfLevel > 0 && !(fFlags & RTFSISO9660_F_NO_UDF) && /* Just disable this code for now: */ (fFlags & RT_BIT(24)))
3032 {
3033 Log(("rtFsIsoVolTryInit: uUdfLevel=%d\n", uUdfLevel));
3034 rc = rtFsIsoVolHandleUdfDetection(pThis, &uUdfLevel, offUdfBootVolDesc, Buf.ab, sizeof(Buf), pErrInfo);
3035 if (RT_FAILURE(rc))
3036 return rc;
3037 }
3038
3039 /*
3040 * We may be faced with choosing between joliet and rock ridge (we won't
3041 * have this choice when RTFSISO9660_F_NO_JOLIET is set). The joliet
3042 * option is generally favorable as we don't have to guess wrt to the file
3043 * name encoding. So, we'll pick that for now.
3044 *
3045 * Note! Should we change this preference for joliet, there fun wrt making sure
3046 * there really is rock ridge stuff in the primary volume as well as
3047 * making sure there really is anything of value in the primary volume.
3048 */
3049 if (bJolietUcs2Level != 0)
3050 {
3051 pThis->fIsUtf16 = true;
3052 return rtFsIsoDirShrd_New9660(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
3053 }
3054 return rtFsIsoDirShrd_New9660(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
3055}
3056
3057
3058/**
3059 * Opens an ISO 9660 file system volume.
3060 *
3061 * @returns IPRT status code.
3062 * @param hVfsFileIn The file or device backing the volume.
3063 * @param fFlags RTFSISO9660_F_XXX.
3064 * @param phVfs Where to return the virtual file system handle.
3065 * @param pErrInfo Where to return additional error information.
3066 */
3067RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
3068{
3069 /*
3070 * Quick input validation.
3071 */
3072 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
3073 *phVfs = NIL_RTVFS;
3074 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
3075
3076 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
3077 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
3078
3079 /*
3080 * Create a new FAT VFS instance and try initialize it using the given input file.
3081 */
3082 RTVFS hVfs = NIL_RTVFS;
3083 void *pvThis = NULL;
3084 int rc = RTVfsNew(&g_rtFsIsoVolOps, sizeof(RTFSISOVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
3085 if (RT_SUCCESS(rc))
3086 {
3087 rc = rtFsIsoVolTryInit((PRTFSISOVOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
3088 if (RT_SUCCESS(rc))
3089 *phVfs = hVfs;
3090 else
3091 RTVfsRelease(hVfs);
3092 }
3093 else
3094 RTVfsFileRelease(hVfsFileIn);
3095 return rc;
3096}
3097
3098
3099/**
3100 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
3101 */
3102static DECLCALLBACK(int) rtVfsChainIso9660Vol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
3103 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
3104{
3105 RT_NOREF(pProviderReg, pSpec);
3106
3107 /*
3108 * Basic checks.
3109 */
3110 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
3111 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
3112 if ( pElement->enmType != RTVFSOBJTYPE_VFS
3113 && pElement->enmType != RTVFSOBJTYPE_DIR)
3114 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
3115 if (pElement->cArgs > 1)
3116 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
3117
3118 /*
3119 * Parse the flag if present, save in pElement->uProvider.
3120 */
3121 uint32_t fFlags = 0;
3122 if (pElement->cArgs > 0)
3123 {
3124 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
3125 {
3126 const char *psz = pElement->paArgs[iArg].psz;
3127 if (*psz)
3128 {
3129 if (!strcmp(psz, "nojoliet"))
3130 fFlags |= RTFSISO9660_F_NO_JOLIET;
3131 else if (!strcmp(psz, "norock"))
3132 fFlags |= RTFSISO9660_F_NO_ROCK;
3133 else if (!strcmp(psz, "noudf"))
3134 fFlags |= RTFSISO9660_F_NO_UDF;
3135 else
3136 {
3137 *poffError = pElement->paArgs[iArg].offSpec;
3138 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
3139 }
3140 }
3141 }
3142 }
3143
3144 pElement->uProvider = fFlags;
3145 return VINF_SUCCESS;
3146}
3147
3148
3149/**
3150 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
3151 */
3152static DECLCALLBACK(int) rtVfsChainIso9660Vol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
3153 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
3154 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
3155{
3156 RT_NOREF(pProviderReg, pSpec, poffError);
3157
3158 int rc;
3159 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
3160 if (hVfsFileIn != NIL_RTVFSFILE)
3161 {
3162 RTVFS hVfs;
3163 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
3164 RTVfsFileRelease(hVfsFileIn);
3165 if (RT_SUCCESS(rc))
3166 {
3167 *phVfsObj = RTVfsObjFromVfs(hVfs);
3168 RTVfsRelease(hVfs);
3169 if (*phVfsObj != NIL_RTVFSOBJ)
3170 return VINF_SUCCESS;
3171 rc = VERR_VFS_CHAIN_CAST_FAILED;
3172 }
3173 }
3174 else
3175 rc = VERR_VFS_CHAIN_CAST_FAILED;
3176 return rc;
3177}
3178
3179
3180/**
3181 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
3182 */
3183static DECLCALLBACK(bool) rtVfsChainIso9660Vol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
3184 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
3185 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
3186{
3187 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
3188 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
3189 || !pReuseElement->paArgs[0].uProvider)
3190 return true;
3191 return false;
3192}
3193
3194
3195/** VFS chain element 'file'. */
3196static RTVFSCHAINELEMENTREG g_rtVfsChainIso9660VolReg =
3197{
3198 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
3199 /* fReserved = */ 0,
3200 /* pszName = */ "isofs",
3201 /* ListEntry = */ { NULL, NULL },
3202 /* pszHelp = */ "Open a ISO 9660 file system, requires a file object on the left side.\n"
3203 "The 'noudf' option make it ignore any UDF.\n"
3204 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
3205 "The 'norock' option make it ignore any rock ridge info.\n",
3206 /* pfnValidate = */ rtVfsChainIso9660Vol_Validate,
3207 /* pfnInstantiate = */ rtVfsChainIso9660Vol_Instantiate,
3208 /* pfnCanReuseElement = */ rtVfsChainIso9660Vol_CanReuseElement,
3209 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
3210};
3211
3212RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIso9660VolReg, rtVfsChainIso9660VolReg);
3213
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