VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/iso9660vfs.cpp@ 68078

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

iso9660vfs: Report half sensible default mode flags on files and dirs, not just zero permissions.

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