VirtualBox

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

Last change on this file since 67559 was 67559, checked in by vboxsync, 7 years ago

iso9660vfs.cpp: Better name version handling.

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

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