VirtualBox

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

最後變更 在這個檔案從67612是 67612,由 vboxsync 提交於 8 年 前

rtFsIso9660VolGetMaybeUtf16Be: Fix for fields with odd length.

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

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