VirtualBox

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

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

iprt: More vfs bits.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 74.1 KB
Line 
1/* $Id: iso9660vfs.cpp 66762 2017-05-03 22:20:16Z 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/file.h>
39#include <iprt/log.h>
40#include <iprt/mem.h>
41#include <iprt/poll.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include <iprt/vfs.h>
45#include <iprt/vfslowlevel.h>
46#include <iprt/formats/iso9660.h>
47
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53/** Pointer to an ISO 9660 volume (VFS instance data). */
54typedef struct RTFSISO9660VOL *PRTFSISO9660VOL;
55/** Pointer to a const ISO 9660 volume (VFS instance data). */
56typedef struct RTFSISO9660VOL const *PCRTFSISO9660VOL;
57
58/** Pointer to a ISO 9660 directory instance. */
59typedef struct RTFSISO9660DIR *PRTFSISO9660DIR;
60
61
62
63/**
64 * ISO 9660 extent (internal to the VFS not a disk structure).
65 */
66typedef struct RTFSISO9660EXTENT
67{
68 /** The disk offset. */
69 uint64_t offDisk;
70 /** The size of the extent in bytes. */
71 uint64_t cbExtent;
72} RTFSISO9660EXTENT;
73/** Pointer to an ISO 9660 extent. */
74typedef RTFSISO9660EXTENT *PRTFSISO9660EXTENT;
75/** Pointer to a const ISO 9660 extent. */
76typedef RTFSISO9660EXTENT const *PCRTFSISO9660EXTENT;
77
78
79/**
80 * ISO 9660 file system object (common part to files and dirs).
81 */
82typedef struct RTFSISO9660OBJ
83{
84 /** The parent directory keeps a list of open objects (RTFSISO9660OBJ). */
85 RTLISTNODE Entry;
86 /** The parent directory (not released till all children are close). */
87 PRTFSISO9660DIR pParentDir;
88 /** The byte offset of the first directory record. */
89 uint64_t offDirRec;
90 /** Attributes. */
91 RTFMODE fAttrib;
92 /** The object size. */
93 uint64_t cbObject;
94 /** The access time. */
95 RTTIMESPEC AccessTime;
96 /** The modificaton time. */
97 RTTIMESPEC ModificationTime;
98 /** The change time. */
99 RTTIMESPEC ChangeTime;
100 /** The birth time. */
101 RTTIMESPEC BirthTime;
102 /** Pointer to the volume. */
103 PRTFSISO9660VOL pVol;
104 /** Number of extents. */
105 uint32_t cExtents;
106 /** The first extent. */
107 RTFSISO9660EXTENT FirstExtent;
108 /** Array of additional extents. */
109 PRTFSISO9660EXTENT paExtents;
110} RTFSISO9660OBJ;
111typedef RTFSISO9660OBJ *PRTFSISO9660OBJ;
112
113/**
114 * ISO 9660 file.
115 */
116typedef struct RTFSISO9660FILE
117{
118 /** Core ISO9660 object info. */
119 RTFSISO9660OBJ Core;
120 /** The current file offset. */
121 uint64_t offFile;
122} RTFSISO9660FILE;
123/** Pointer to a ISO 9660 file object. */
124typedef RTFSISO9660FILE *PRTFSISO9660FILE;
125
126
127/**
128 * ISO 9660 directory.
129 *
130 * We will always read in the whole directory just to keep things really simple.
131 */
132typedef struct RTFSISO9660DIR
133{
134 /** Core ISO 9660 object info. */
135 RTFSISO9660OBJ Core;
136 /** The VFS handle for this directory (for reference counting). */
137 RTVFSDIR hVfsSelf;
138 /** Open child objects (RTFSISO9660OBJ). */
139 RTLISTNODE OpenChildren;
140
141 /** Pointer to the directory content. */
142 uint8_t *pbDir;
143 /** The size of the directory content (duplicate of Core.cbObject). */
144 uint32_t cbDir;
145} RTFSISO9660DIR;
146/** Pointer to a ISO 9660 directory instance. */
147typedef RTFSISO9660DIR *PRTFSISO9660DIR;
148
149
150
151/**
152 * A ISO 9660 volume.
153 */
154typedef struct RTFSISO9660VOL
155{
156 /** Handle to itself. */
157 RTVFS hVfsSelf;
158 /** The file, partition, or whatever backing the ISO 9660 volume. */
159 RTVFSFILE hVfsBacking;
160 /** The size of the backing thingy. */
161 uint64_t cbBacking;
162 /** Flags. */
163 uint32_t fFlags;
164 /** The sector size (in bytes). */
165 uint32_t cbSector;
166 /** The size of a logical block in bytes. */
167 uint32_t cbBlock;
168 /** The primary volume space size in blocks. */
169 uint32_t cBlocksInPrimaryVolumeSpace;
170 /** The primary volume space size in bytes. */
171 uint64_t cbPrimaryVolumeSpace;
172 /** The number of volumes in the set. */
173 uint32_t cVolumesInSet;
174 /** The primary volume sequence ID. */
175 uint32_t idPrimaryVol;
176 /** Set if using UTF16-2 (joliet). */
177 bool fIsUtf16;
178
179 /** The root directory handle. */
180 RTVFSDIR hVfsRootDir;
181 /** The root directory instance data. */
182 PRTFSISO9660DIR pRootDir;
183} RTFSISO9660VOL;
184
185
186
187/*********************************************************************************************************************************
188* Global Variables *
189*********************************************************************************************************************************/
190
191
192/*********************************************************************************************************************************
193* Internal Functions *
194*********************************************************************************************************************************/
195static void rtFsIso9660Dir_AddOpenChild(PRTFSISO9660DIR pDir, PRTFSISO9660OBJ pChild);
196static void rtFsIso9660Dir_RemoveOpenChild(PRTFSISO9660DIR pDir, PRTFSISO9660OBJ pChild);
197static int rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIR pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
198 uint64_t offDirRec, PRTVFSDIR phVfsDir, PRTFSISO9660DIR *ppDir);
199
200
201
202/**
203 * Converts a ISO 9660 binary timestamp into an IPRT timesspec.
204 *
205 * @param pTimeSpec Where to return the IRPT time.
206 * @param pIso9660 The ISO 9660 binary timestamp.
207 */
208static void rtFsIso9660DateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
209{
210 RTTIME Time;
211 Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
212 Time.offUTC = 0;
213 Time.i32Year = pIso9660->bYear + 1900;
214 Time.u8Month = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
215 Time.u8MonthDay = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
216 Time.u8WeekDay = UINT8_MAX;
217 Time.u16YearDay = 0;
218 Time.u8Hour = RT_MIN(pIso9660->bHour, 23);
219 Time.u8Minute = RT_MIN(pIso9660->bMinute, 59);
220 Time.u8Second = RT_MIN(pIso9660->bSecond, 59);
221 Time.u32Nanosecond = 0;
222 RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
223
224 /* Only apply the UTC offset if it's within reasons. */
225 if (RT_ABS(pIso9660->offUtc) <= 13*4)
226 RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
227}
228
229
230/**
231 * Initialization of a RTFSISO9660OBJ structure from a directory record.
232 *
233 * @note The RTFSISO9660OBJ::pParentDir and RTFSISO9660OBJ::Clusters members are
234 * properly initialized elsewhere.
235 *
236 * @param pObj The structure to initialize.
237 * @param pDirRec The primary directory record.
238 * @param cDirRecs Number of directory records.
239 * @param offDirRec The offset of the primary directory record.
240 * @param pVol The volume.
241 */
242static void rtFsIso9660Obj_InitFromDirRec(PRTFSISO9660OBJ pObj, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
243 uint64_t offDirRec, PRTFSISO9660VOL pVol)
244{
245 Assert(cDirRecs == 1); RT_NOREF(cDirRecs);
246
247 RTListInit(&pObj->Entry);
248 pObj->pParentDir = NULL;
249 pObj->pVol = pVol;
250 pObj->offDirRec = offDirRec;
251 pObj->fAttrib = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
252 ? RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE;
253 if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
254 pObj->fAttrib |= RTFS_DOS_HIDDEN;
255 pObj->cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
256 pObj->cExtents = 1;
257 pObj->FirstExtent.cbExtent = pObj->cbObject;
258 pObj->FirstExtent.offDisk = ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)pVol->cbBlock;
259
260 rtFsIso9660DateTime2TimeSpec(&pObj->ModificationTime, &pDirRec->RecTime);
261 pObj->BirthTime = pObj->ModificationTime;
262 pObj->AccessTime = pObj->ModificationTime;
263 pObj->ChangeTime = pObj->ModificationTime;
264}
265
266
267
268/**
269 * Worker for rtFsIso9660File_Close and rtFsIso9660Dir_Close that does common work.
270 *
271 * @returns IPRT status code.
272 * @param pObj The common object structure.
273 */
274static int rtFsIso9660Obj_Close(PRTFSISO9660OBJ pObj)
275{
276 if (pObj->pParentDir)
277 rtFsIso9660Dir_RemoveOpenChild(pObj->pParentDir, pObj);
278 if (pObj->paExtents)
279 {
280 RTMemFree(pObj->paExtents);
281 pObj->paExtents = NULL;
282 }
283 return VINF_SUCCESS;
284}
285
286
287/**
288 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
289 */
290static DECLCALLBACK(int) rtFsIso9660File_Close(void *pvThis)
291{
292 PRTFSISO9660FILE pThis = (PRTFSISO9660FILE)pvThis;
293 LogFlow(("rtFsIso9660File_Close(%p)\n", pThis));
294 return rtFsIso9660Obj_Close(&pThis->Core);
295}
296
297
298/**
299 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
300 */
301static DECLCALLBACK(int) rtFsIso9660Obj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
302{
303 PRTFSISO9660OBJ pThis = (PRTFSISO9660OBJ)pvThis;
304
305 pObjInfo->cbObject = pThis->cbObject;
306 pObjInfo->cbAllocated = RT_ALIGN_64(pThis->cbObject, pThis->pVol->cbBlock);
307 pObjInfo->AccessTime = pThis->AccessTime;
308 pObjInfo->ModificationTime = pThis->ModificationTime;
309 pObjInfo->ChangeTime = pThis->ChangeTime;
310 pObjInfo->BirthTime = pThis->BirthTime;
311 pObjInfo->Attr.fMode = pThis->fAttrib;
312 pObjInfo->Attr.enmAdditional = enmAddAttr;
313
314 switch (enmAddAttr)
315 {
316 case RTFSOBJATTRADD_NOTHING: /* fall thru */
317 case RTFSOBJATTRADD_UNIX:
318 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
319 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
320 pObjInfo->Attr.u.Unix.cHardlinks = 1;
321 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
322 pObjInfo->Attr.u.Unix.INodeId = 0; /* Could probably use the directory entry offset. */
323 pObjInfo->Attr.u.Unix.fFlags = 0;
324 pObjInfo->Attr.u.Unix.GenerationId = 0;
325 pObjInfo->Attr.u.Unix.Device = 0;
326 break;
327 case RTFSOBJATTRADD_UNIX_OWNER:
328 pObjInfo->Attr.u.UnixOwner.uid = 0;
329 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
330 break;
331 case RTFSOBJATTRADD_UNIX_GROUP:
332 pObjInfo->Attr.u.UnixGroup.gid = 0;
333 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
334 break;
335 case RTFSOBJATTRADD_EASIZE:
336 pObjInfo->Attr.u.EASize.cb = 0;
337 break;
338 default:
339 return VERR_INVALID_PARAMETER;
340 }
341 return VINF_SUCCESS;
342}
343
344
345/**
346 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
347 */
348static DECLCALLBACK(int) rtFsIso9660File_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
349{
350 PRTFSISO9660FILE pThis = (PRTFSISO9660FILE)pvThis;
351 AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
352 RT_NOREF(fBlocking);
353
354 /*
355 * Check for EOF.
356 */
357 if (off == -1)
358 off = pThis->offFile;
359 if ((uint64_t)off >= pThis->Core.cbObject)
360 {
361 if (pcbRead)
362 {
363 *pcbRead = 0;
364 return VINF_EOF;
365 }
366 return VERR_EOF;
367 }
368
369
370 /*
371 * Simple case: File has a single extent.
372 */
373 int rc = VINF_SUCCESS;
374 size_t cbRead = 0;
375 uint64_t cbFileLeft = pThis->Core.cbObject - (uint64_t)off;
376 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
377 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
378 if (pThis->Core.cExtents == 1)
379 {
380 if (cbLeft > 0)
381 {
382 size_t cbToRead = cbLeft;
383 if (cbToRead > cbFileLeft)
384 cbToRead = (size_t)cbFileLeft;
385 rc = RTVfsFileReadAt(pThis->Core.pVol->hVfsBacking, pThis->Core.FirstExtent.offDisk + off, pbDst, cbToRead, NULL);
386 if (RT_SUCCESS(rc))
387 {
388 off += cbToRead;
389 pbDst += cbToRead;
390 cbRead += cbToRead;
391 cbFileLeft -= cbToRead;
392 cbLeft -= cbToRead;
393 }
394 }
395 }
396 /*
397 * Complicated case: Work the file content extent by extent.
398 */
399 else
400 {
401
402 return VERR_NOT_IMPLEMENTED; /** @todo multi-extent stuff . */
403 }
404
405 /* Update the offset and return. */
406 pThis->offFile = off;
407 if (pcbRead)
408 *pcbRead = cbRead;
409 return VINF_SUCCESS;
410}
411
412
413/**
414 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
415 */
416static DECLCALLBACK(int) rtFsIso9660File_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
417{
418 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
419 return VERR_WRITE_PROTECT;
420}
421
422
423/**
424 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
425 */
426static DECLCALLBACK(int) rtFsIso9660File_Flush(void *pvThis)
427{
428 RT_NOREF(pvThis);
429 return VINF_SUCCESS;
430}
431
432
433/**
434 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
435 */
436static DECLCALLBACK(int) rtFsIso9660File_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
437 uint32_t *pfRetEvents)
438{
439 NOREF(pvThis);
440 int rc;
441 if (fEvents != RTPOLL_EVT_ERROR)
442 {
443 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
444 rc = VINF_SUCCESS;
445 }
446 else if (fIntr)
447 rc = RTThreadSleep(cMillies);
448 else
449 {
450 uint64_t uMsStart = RTTimeMilliTS();
451 do
452 rc = RTThreadSleep(cMillies);
453 while ( rc == VERR_INTERRUPTED
454 && !fIntr
455 && RTTimeMilliTS() - uMsStart < cMillies);
456 if (rc == VERR_INTERRUPTED)
457 rc = VERR_TIMEOUT;
458 }
459 return rc;
460}
461
462
463/**
464 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
465 */
466static DECLCALLBACK(int) rtFsIso9660File_Tell(void *pvThis, PRTFOFF poffActual)
467{
468 PRTFSISO9660FILE pThis = (PRTFSISO9660FILE)pvThis;
469 *poffActual = pThis->offFile;
470 return VINF_SUCCESS;
471}
472
473
474/**
475 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
476 */
477static DECLCALLBACK(int) rtFsIso9660Obj_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
478{
479 RT_NOREF(pvThis, fMode, fMask);
480 return VERR_WRITE_PROTECT;
481}
482
483
484/**
485 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
486 */
487static DECLCALLBACK(int) rtFsIso9660Obj_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
488 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
489{
490 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
491 return VERR_WRITE_PROTECT;
492}
493
494
495/**
496 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
497 */
498static DECLCALLBACK(int) rtFsIso9660Obj_SetOwner(void *pvThis, RTUID uid, RTGID gid)
499{
500 RT_NOREF(pvThis, uid, gid);
501 return VERR_WRITE_PROTECT;
502}
503
504
505/**
506 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
507 */
508static DECLCALLBACK(int) rtFsIso9660File_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
509{
510 PRTFSISO9660FILE pThis = (PRTFSISO9660FILE)pvThis;
511 RTFOFF offNew;
512 switch (uMethod)
513 {
514 case RTFILE_SEEK_BEGIN:
515 offNew = offSeek;
516 break;
517 case RTFILE_SEEK_END:
518 offNew = (RTFOFF)pThis->Core.cbObject + offSeek;
519 break;
520 case RTFILE_SEEK_CURRENT:
521 offNew = (RTFOFF)pThis->offFile + offSeek;
522 break;
523 default:
524 return VERR_INVALID_PARAMETER;
525 }
526 if (offNew >= 0)
527 {
528 if (offNew <= _4G)
529 {
530 pThis->offFile = offNew;
531 *poffActual = offNew;
532 return VINF_SUCCESS;
533 }
534 return VERR_OUT_OF_RANGE;
535 }
536 return VERR_NEGATIVE_SEEK;
537}
538
539
540/**
541 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
542 */
543static DECLCALLBACK(int) rtFsIso9660File_QuerySize(void *pvThis, uint64_t *pcbFile)
544{
545 PRTFSISO9660FILE pThis = (PRTFSISO9660FILE)pvThis;
546 *pcbFile = pThis->Core.cbObject;
547 return VINF_SUCCESS;
548}
549
550
551/**
552 * FAT file operations.
553 */
554DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIso9660FileOps =
555{
556 { /* Stream */
557 { /* Obj */
558 RTVFSOBJOPS_VERSION,
559 RTVFSOBJTYPE_FILE,
560 "FatFile",
561 rtFsIso9660File_Close,
562 rtFsIso9660Obj_QueryInfo,
563 RTVFSOBJOPS_VERSION
564 },
565 RTVFSIOSTREAMOPS_VERSION,
566 RTVFSIOSTREAMOPS_FEAT_NO_SG,
567 rtFsIso9660File_Read,
568 rtFsIso9660File_Write,
569 rtFsIso9660File_Flush,
570 rtFsIso9660File_PollOne,
571 rtFsIso9660File_Tell,
572 NULL /*pfnSkip*/,
573 NULL /*pfnZeroFill*/,
574 RTVFSIOSTREAMOPS_VERSION,
575 },
576 RTVFSFILEOPS_VERSION,
577 0,
578 { /* ObjSet */
579 RTVFSOBJSETOPS_VERSION,
580 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
581 rtFsIso9660Obj_SetMode,
582 rtFsIso9660Obj_SetTimes,
583 rtFsIso9660Obj_SetOwner,
584 RTVFSOBJSETOPS_VERSION
585 },
586 rtFsIso9660File_Seek,
587 rtFsIso9660File_QuerySize,
588 RTVFSFILEOPS_VERSION
589};
590
591
592/**
593 * Instantiates a new directory.
594 *
595 * @returns IPRT status code.
596 * @param pThis The FAT volume instance.
597 * @param pParentDir The parent directory.
598 * @param pDirRec The directory record.
599 * @param cDirRecs Number of directory records if more than one.
600 * @param offDirRec The byte offset of the directory record.
601 * @param offEntryInDir The byte offset of the directory entry in the parent
602 * directory.
603 * @param fOpen RTFILE_O_XXX flags.
604 * @param phVfsFile Where to return the file handle.
605 */
606static int rtFsIso9660File_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIR pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
607 uint64_t offDirRec, uint64_t fOpen, PRTVFSFILE phVfsFile)
608{
609 AssertPtr(pParentDir);
610
611 PRTFSISO9660FILE pNewFile;
612 int rc = RTVfsNewFile(&g_rtFsIso9660FileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
613 phVfsFile, (void **)&pNewFile);
614 if (RT_SUCCESS(rc))
615 {
616 /*
617 * Initialize it all so rtFsIso9660File_Close doesn't trip up in anyway.
618 */
619 rtFsIso9660Obj_InitFromDirRec(&pNewFile->Core, pDirRec, cDirRecs, offDirRec, pThis);
620 pNewFile->offFile = 0;
621
622 /*
623 * Link into parent directory so we can use it to update
624 * our directory entry.
625 */
626 rtFsIso9660Dir_AddOpenChild(pParentDir, &pNewFile->Core);
627
628 return VINF_SUCCESS;
629
630 }
631 *phVfsFile = NIL_RTVFSFILE;
632 return rc;
633}
634
635
636/**
637 * RTStrToUtf16Ex returning big-endian UTF-16.
638 */
639static int rtFsIso9660_StrToUtf16BigEndian(const char *pszString, size_t cchString, PRTUTF16 *ppwsz, size_t cwc, size_t *pcwc)
640{
641 int rc = RTStrToUtf16Ex(pszString, cchString, ppwsz, cwc, pcwc);
642#ifndef RT_BIG_ENDIAN
643 if (RT_SUCCESS(rc))
644 {
645 PRTUTF16 pwc = *ppwsz;
646 RTUTF16 wc;
647 while ((wc = *pwc))
648 *pwc++ = RT_H2BE_U16(wc);
649 }
650#endif
651 return rc;
652}
653
654
655
656/**
657 * Locates a directory entry in a directory.
658 *
659 * @returns IPRT status code.
660 * @retval VERR_FILE_NOT_FOUND if not found.
661 * @param pThis The directory to search.
662 * @param pszEntry The entry to look for.
663 * @param poffDirRec Where to return the offset of the directory record
664 * on the disk.
665 * @param ppDirRec Where to return the pointer to the directory record
666 * (the whole directory is buffered).
667 * @param pcDirRecs Where to return the number of directory records
668 * related to this entry.
669 * @param pfMode Where to return the file type, rock ridge adjusted.
670 */
671static int rtFsIso9660Dir_FindEntry(PRTFSISO9660DIR pThis, const char *pszEntry, uint64_t *poffDirRec, PCISO9660DIRREC *ppDirRec,
672 uint32_t *pcDirRecs, PRTFMODE pfMode)
673{
674 /* Set return values. */
675 *poffDirRec = UINT64_MAX;
676 *ppDirRec = NULL;
677 *pcDirRecs = 1;
678 *pfMode = UINT32_MAX;
679
680 /*
681 * If we're in UTF-16BE mode, convert the input name to UTF-16BE. Otherwise try
682 * uppercase it into a ISO 9660 compliant name.
683 */
684 int rc;
685 bool const fIsUtf16 = pThis->Core.pVol->fIsUtf16;
686 size_t cwcEntry = 0;
687 size_t cbEntry = 0;
688 size_t cchUpper = ~(size_t)0;
689 union
690 {
691 RTUTF16 wszEntry[260 + 1];
692 struct
693 {
694 char szUpper[255 + 1];
695 char szRock[260 + 1];
696 } s;
697 } uBuf;
698 if (fIsUtf16)
699 {
700 PRTUTF16 pwszEntry = uBuf.wszEntry;
701 rc = rtFsIso9660_StrToUtf16BigEndian(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
702 if (RT_FAILURE(rc))
703 return rc;
704 cbEntry = cwcEntry * 2;
705 }
706 else
707 {
708 rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
709 if (RT_SUCCESS(rc))
710 {
711 RTStrToUpper(uBuf.s.szUpper);
712 cchUpper = strlen(uBuf.s.szUpper);
713 }
714 }
715
716 /*
717 * Scan the directory buffer by buffer.
718 */
719 uint32_t offEntryInDir = 0;
720 uint32_t const cbDir = pThis->Core.cbObject;
721 while (offEntryInDir + RT_OFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
722 {
723 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
724
725 /* If null length, skip to the next sector. */
726 if (pDirRec->cbDirRec == 0)
727 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
728 else
729 {
730 /* Try match the filename. */
731 /** @todo deal with multi-extend filenames, or however that works... */
732 if (fIsUtf16)
733 {
734 if ( ((size_t)pDirRec->bFileIdLength - cbEntry) > (size_t)12 /* ;12345 */
735 || RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, uBuf.wszEntry, cwcEntry) != 0
736 || ( (size_t)pDirRec->bFileIdLength != cbEntry
737 && RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';') )
738 {
739 /* Advance */
740 offEntryInDir += pDirRec->cbDirRec;
741 continue;
742 }
743 }
744 else
745 {
746 if ( ((size_t)pDirRec->bFileIdLength - cchUpper) > (size_t)6 /* ;12345 */
747 || memcmp(pDirRec->achFileId, uBuf.s.szUpper, cchUpper) != 0
748 || ( (size_t)pDirRec->bFileIdLength != cchUpper
749 && pDirRec->achFileId[cchUpper] != ';') )
750 {
751 /** @todo check rock. */
752 if (1)
753 {
754 /* Advance */
755 offEntryInDir += pDirRec->cbDirRec;
756 continue;
757 }
758 }
759 }
760
761 *poffDirRec = pThis->Core.FirstExtent.offDisk + offEntryInDir;
762 *ppDirRec = pDirRec;
763 *pcDirRecs = 1;
764 *pfMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE;
765 return VINF_SUCCESS;
766
767 }
768 }
769
770 return VERR_FILE_NOT_FOUND;
771}
772
773
774/**
775 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
776 */
777static DECLCALLBACK(int) rtFsIso9660Dir_Close(void *pvThis)
778{
779 PRTFSISO9660DIR pThis = (PRTFSISO9660DIR)pvThis;
780 LogFlow(("rtFsIso9660Dir_Close(%p)\n", pThis));
781 if (pThis->pbDir)
782 {
783 RTMemFree(pThis->pbDir);
784 pThis->pbDir = NULL;
785 }
786 return rtFsIso9660Obj_Close(&pThis->Core);
787}
788
789
790/**
791 * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
792 */
793static DECLCALLBACK(int) rtFsIso9660Dir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
794 PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
795{
796 /*
797 * We may have symbolic links if rock ridge is being used, though currently
798 * we won't have nested mounts.
799 */
800 int rc;
801 if (phVfsMounted)
802 *phVfsMounted = NIL_RTVFS;
803 if (phVfsDir || phVfsSymlink)
804 {
805 if (phVfsSymlink)
806 *phVfsSymlink = NIL_RTVFSSYMLINK;
807 if (phVfsDir)
808 *phVfsDir = NIL_RTVFSDIR;
809
810 PRTFSISO9660DIR pThis = (PRTFSISO9660DIR)pvThis;
811 PCISO9660DIRREC pDirRec;
812 uint64_t offDirRec;
813 uint32_t cDirRecs;
814 RTFMODE fMode;
815 rc = rtFsIso9660Dir_FindEntry(pThis, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode);
816 Log2(("rtFsIso9660Dir_TraversalOpen: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
817 if (RT_SUCCESS(rc))
818 {
819 switch (fMode & RTFS_TYPE_MASK)
820 {
821 case RTFS_TYPE_DIRECTORY:
822 rc = rtFsIso9660Dir_New(pThis->Core.pVol, pThis, pDirRec, cDirRecs, offDirRec, phVfsDir, NULL /*ppDir*/);
823 break;
824
825 case RTFS_TYPE_SYMLINK:
826 rc = VERR_NOT_IMPLEMENTED;
827 break;
828 case RTFS_TYPE_FILE:
829 case RTFS_TYPE_DEV_BLOCK:
830 case RTFS_TYPE_DEV_CHAR:
831 case RTFS_TYPE_FIFO:
832 case RTFS_TYPE_SOCKET:
833 rc = VERR_NOT_A_DIRECTORY;
834 break;
835 default:
836 case RTFS_TYPE_WHITEOUT:
837 rc = VERR_PATH_NOT_FOUND;
838 break;
839 }
840 }
841 else if (rc == VERR_FILE_NOT_FOUND)
842 rc = VERR_PATH_NOT_FOUND;
843 }
844 else
845 rc = VERR_PATH_NOT_FOUND;
846 return rc;
847}
848
849
850/**
851 * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
852 */
853static DECLCALLBACK(int) rtFsIso9660Dir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
854{
855 PRTFSISO9660DIR pThis = (PRTFSISO9660DIR)pvThis;
856
857 /*
858 * We cannot create or replace anything, just open stuff.
859 */
860 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
861 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
862 return VERR_WRITE_PROTECT;
863
864 /*
865 * Try open file.
866 */
867 PCISO9660DIRREC pDirRec;
868 uint64_t offDirRec;
869 uint32_t cDirRecs;
870 RTFMODE fMode;
871 int rc = rtFsIso9660Dir_FindEntry(pThis, pszFilename, &offDirRec, &pDirRec, &cDirRecs, &fMode);
872 Log2(("rtFsIso9660Dir_OpenFile: FindEntry(,%s,) -> %Rrc\n", pszFilename, rc));
873 if (RT_SUCCESS(rc))
874 {
875 switch (fMode & RTFS_TYPE_MASK)
876 {
877 case RTFS_TYPE_FILE:
878 rc = rtFsIso9660File_New(pThis->Core.pVol, pThis, pDirRec, cDirRecs, offDirRec, fOpen, phVfsFile);
879 break;
880
881 case RTFS_TYPE_SYMLINK:
882 case RTFS_TYPE_DEV_BLOCK:
883 case RTFS_TYPE_DEV_CHAR:
884 case RTFS_TYPE_FIFO:
885 case RTFS_TYPE_SOCKET:
886 case RTFS_TYPE_WHITEOUT:
887 rc = VERR_NOT_IMPLEMENTED;
888 break;
889
890 case RTFS_TYPE_DIRECTORY:
891 rc = VERR_NOT_A_FILE;
892 break;
893
894 default:
895 rc = VERR_PATH_NOT_FOUND;
896 break;
897 }
898 }
899 return rc;
900}
901
902
903/**
904 * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
905 */
906static DECLCALLBACK(int) rtFsIso9660Dir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
907{
908 RT_NOREF(pvThis, pszSubDir, fFlags, phVfsDir);
909RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSubDir);
910 return VERR_NOT_IMPLEMENTED;
911}
912
913
914/**
915 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
916 */
917static DECLCALLBACK(int) rtFsIso9660Dir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
918{
919 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
920 return VERR_WRITE_PROTECT;
921}
922
923
924/**
925 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
926 */
927static DECLCALLBACK(int) rtFsIso9660Dir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
928{
929 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
930RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
931 return VERR_NOT_SUPPORTED;
932}
933
934
935/**
936 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
937 */
938static DECLCALLBACK(int) rtFsIso9660Dir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
939 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
940{
941 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
942 return VERR_WRITE_PROTECT;
943}
944
945
946/**
947 * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
948 */
949static DECLCALLBACK(int) rtFsIso9660Dir_QueryEntryInfo(void *pvThis, const char *pszEntry,
950 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
951{
952 /*
953 * Try locate the entry.
954 */
955 PRTFSISO9660DIR pThis = (PRTFSISO9660DIR)pvThis;
956 PCISO9660DIRREC pDirRec;
957 uint64_t offDirRec;
958 uint32_t cDirRecs;
959 RTFMODE fMode;
960 int rc = rtFsIso9660Dir_FindEntry(pThis, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode);
961 Log2(("rtFsIso9660Dir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
962 if (RT_SUCCESS(rc))
963 {
964 /*
965 * To avoid duplicating code in rtFsIso9660Obj_InitFromDirRec and
966 * rtFsIso9660Obj_QueryInfo, we create a dummy RTFSISO9660OBJ on the stack.
967 */
968 RTFSISO9660OBJ TmpObj;
969 RT_ZERO(TmpObj);
970 rtFsIso9660Obj_InitFromDirRec(&TmpObj, pDirRec, cDirRecs, offDirRec, pThis->Core.pVol);
971 rc = rtFsIso9660Obj_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
972 }
973 return rc;
974}
975
976
977/**
978 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
979 */
980static DECLCALLBACK(int) rtFsIso9660Dir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
981{
982 RT_NOREF(pvThis, pszEntry, fType);
983 return VERR_WRITE_PROTECT;
984}
985
986
987/**
988 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
989 */
990static DECLCALLBACK(int) rtFsIso9660Dir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
991{
992 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
993 return VERR_WRITE_PROTECT;
994}
995
996
997/**
998 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
999 */
1000static DECLCALLBACK(int) rtFsIso9660Dir_RewindDir(void *pvThis)
1001{
1002 RT_NOREF(pvThis);
1003RTAssertMsg2("%s\n", __FUNCTION__);
1004 return VERR_NOT_IMPLEMENTED;
1005}
1006
1007
1008/**
1009 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
1010 */
1011static DECLCALLBACK(int) rtFsIso9660Dir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
1012 RTFSOBJATTRADD enmAddAttr)
1013{
1014 RT_NOREF(pvThis, pDirEntry, pcbDirEntry, enmAddAttr);
1015RTAssertMsg2("%s\n", __FUNCTION__);
1016 return VERR_NOT_IMPLEMENTED;
1017}
1018
1019
1020/**
1021 * FAT file operations.
1022 */
1023static const RTVFSDIROPS g_rtFsIso9660DirOps =
1024{
1025 { /* Obj */
1026 RTVFSOBJOPS_VERSION,
1027 RTVFSOBJTYPE_DIR,
1028 "ISO 9660 Dir",
1029 rtFsIso9660Dir_Close,
1030 rtFsIso9660Obj_QueryInfo,
1031 RTVFSOBJOPS_VERSION
1032 },
1033 RTVFSDIROPS_VERSION,
1034 0,
1035 { /* ObjSet */
1036 RTVFSOBJSETOPS_VERSION,
1037 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
1038 rtFsIso9660Obj_SetMode,
1039 rtFsIso9660Obj_SetTimes,
1040 rtFsIso9660Obj_SetOwner,
1041 RTVFSOBJSETOPS_VERSION
1042 },
1043 rtFsIso9660Dir_TraversalOpen,
1044 rtFsIso9660Dir_OpenFile,
1045 rtFsIso9660Dir_OpenDir,
1046 rtFsIso9660Dir_CreateDir,
1047 rtFsIso9660Dir_OpenSymlink,
1048 rtFsIso9660Dir_CreateSymlink,
1049 rtFsIso9660Dir_QueryEntryInfo,
1050 rtFsIso9660Dir_UnlinkEntry,
1051 rtFsIso9660Dir_RenameEntry,
1052 rtFsIso9660Dir_RewindDir,
1053 rtFsIso9660Dir_ReadDir,
1054 RTVFSDIROPS_VERSION,
1055};
1056
1057
1058/**
1059 * Adds an open child to the parent directory.
1060 *
1061 * Maintains an additional reference to the parent dir to prevent it from going
1062 * away. If @a pDir is the root directory, it also ensures the volume is
1063 * referenced and sticks around until the last open object is gone.
1064 *
1065 * @param pDir The directory.
1066 * @param pChild The child being opened.
1067 * @sa rtFsIso9660Dir_RemoveOpenChild
1068 */
1069static void rtFsIso9660Dir_AddOpenChild(PRTFSISO9660DIR pDir, PRTFSISO9660OBJ pChild)
1070{
1071 /* First child that gets opened retains the parent directory. This is
1072 released by the final open child. */
1073 if (RTListIsEmpty(&pDir->OpenChildren))
1074 {
1075 uint32_t cRefs = RTVfsDirRetain(pDir->hVfsSelf);
1076 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1077
1078 /* Root also retains the whole file system. */
1079 if (pDir->Core.pVol->pRootDir == pDir)
1080 {
1081 Assert(pDir->Core.pVol);
1082 Assert(pDir->Core.pVol == pChild->pVol);
1083 cRefs = RTVfsRetain(pDir->Core.pVol->hVfsSelf);
1084 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1085 LogFlow(("rtFsIso9660Dir_AddOpenChild(%p,%p) retains volume (%d)\n", pDir, pChild, cRefs));
1086 }
1087 else
1088 LogFlow(("rtFsIso9660Dir_AddOpenChild(%p,%p) retains parent only (%d)\n", pDir, pChild, cRefs));
1089 }
1090 else
1091 LogFlow(("rtFsIso9660Dir_AddOpenChild(%p,%p) retains nothing\n", pDir, pChild));
1092 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
1093 pChild->pParentDir = pDir;
1094}
1095
1096
1097/**
1098 * Removes an open child to the parent directory.
1099 *
1100 * @param pDir The directory.
1101 * @param pChild The child being removed.
1102 *
1103 * @remarks This is the very last thing you do as it may cause a few other
1104 * objects to be released recursively (parent dir and the volume).
1105 *
1106 * @sa rtFsIso9660Dir_AddOpenChild
1107 */
1108static void rtFsIso9660Dir_RemoveOpenChild(PRTFSISO9660DIR pDir, PRTFSISO9660OBJ pChild)
1109{
1110 AssertReturnVoid(pChild->pParentDir == pDir);
1111 RTListNodeRemove(&pChild->Entry);
1112 pChild->pParentDir = NULL;
1113
1114 /* Final child? If so, release directory. */
1115 if (RTListIsEmpty(&pDir->OpenChildren))
1116 {
1117 bool const fIsRootDir = pDir->Core.pVol->pRootDir == pDir;
1118
1119 uint32_t cRefs = RTVfsDirRelease(pDir->hVfsSelf);
1120 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1121
1122 /* Root directory releases the file system as well. Since the volume
1123 holds a reference to the root directory, it will remain valid after
1124 the above release. */
1125 if (fIsRootDir)
1126 {
1127 Assert(cRefs > 0);
1128 Assert(pDir->Core.pVol);
1129 Assert(pDir->Core.pVol == pChild->pVol);
1130 cRefs = RTVfsRelease(pDir->Core.pVol->hVfsSelf);
1131 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1132 LogFlow(("rtFsIso9660Dir_RemoveOpenChild(%p,%p) releases volume (%d)\n", pDir, pChild, cRefs));
1133 }
1134 else
1135 LogFlow(("rtFsIso9660Dir_RemoveOpenChild(%p,%p) releases parent only (%d)\n", pDir, pChild, cRefs));
1136 }
1137 else
1138 LogFlow(("rtFsIso9660Dir_RemoveOpenChild(%p,%p) releases nothing\n", pDir, pChild));
1139}
1140
1141
1142#ifdef LOG_ENABLED
1143/**
1144 * Logs the content of a directory.
1145 */
1146static void rtFsIso9660Dir_LogContent(PRTFSISO9660DIR pThis)
1147{
1148 if (LogIs2Enabled())
1149 {
1150 uint32_t offRec = 0;
1151 while (offRec < pThis->cbDir)
1152 {
1153 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
1154 if (pDirRec->cbDirRec == 0)
1155 break;
1156
1157 RTUTF16 wszName[128];
1158 if (pThis->Core.pVol->fIsUtf16)
1159 {
1160 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
1161 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
1162 pwszSrc--;
1163 *pwszDst-- = '\0';
1164 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
1165 {
1166 *pwszDst = RT_BE2H_U16(*pwszSrc);
1167 pwszDst--;
1168 pwszSrc--;
1169 }
1170 }
1171 else
1172 {
1173 PRTUTF16 pwszDst = wszName;
1174 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
1175 *pwszDst++ = pDirRec->achFileId[off];
1176 *pwszDst = '\0';
1177 }
1178
1179 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",
1180 offRec,
1181 pDirRec->cbDirRec,
1182 pDirRec->cbExtAttr,
1183 ISO9660_GET_ENDIAN(&pDirRec->cbData),
1184 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
1185 pDirRec->fFileFlags,
1186 pDirRec->RecTime.bYear + 1900,
1187 pDirRec->RecTime.bMonth,
1188 pDirRec->RecTime.bDay,
1189 pDirRec->RecTime.bHour,
1190 pDirRec->RecTime.bMinute,
1191 pDirRec->RecTime.bSecond,
1192 pDirRec->RecTime.offUtc*4/60,
1193 pDirRec->bFileUnitSize,
1194 pDirRec->bInterleaveGapSize,
1195 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
1196 wszName));
1197
1198 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
1199 if (offSysUse < pDirRec->cbDirRec)
1200 {
1201 Log2(("ISO9660: system use:\n%.*Rhxd\n", pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
1202 }
1203
1204 /* advance */
1205 offRec += pDirRec->cbDirRec;
1206 }
1207 }
1208}
1209#endif /* LOG_ENABLED */
1210
1211
1212/**
1213 * Instantiates a new directory.
1214 *
1215 * @returns IPRT status code.
1216 * @param pThis The FAT volume instance.
1217 * @param pParentDir The parent directory. This is NULL for the root
1218 * directory.
1219 * @param pDirRec The directory record.
1220 * @param cDirRecs Number of directory records if more than one.
1221 * @param offDirRec The byte offset of the directory record.
1222 * @param phVfsDir Where to return the directory handle.
1223 * @param ppDir Where to return the FAT directory instance data.
1224 */
1225static int rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIR pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
1226 uint64_t offDirRec, PRTVFSDIR phVfsDir, PRTFSISO9660DIR *ppDir)
1227{
1228 if (ppDir)
1229 *ppDir = NULL;
1230
1231 PRTFSISO9660DIR pNewDir;
1232 int rc = RTVfsNewDir(&g_rtFsIso9660DirOps, sizeof(*pNewDir), pParentDir ? 0 : RTVFSDIR_F_NO_VFS_REF,
1233 pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
1234 if (RT_SUCCESS(rc))
1235 {
1236 /*
1237 * Initialize it all so rtFsIso9660Dir_Close doesn't trip up in anyway.
1238 */
1239 rtFsIso9660Obj_InitFromDirRec(&pNewDir->Core, pDirRec, cDirRecs, offDirRec, pThis);
1240 RTListInit(&pNewDir->OpenChildren);
1241 pNewDir->hVfsSelf = *phVfsDir;
1242 pNewDir->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
1243 pNewDir->pbDir = (uint8_t *)RTMemAllocZ(pNewDir->cbDir + 256);
1244 if (pNewDir->pbDir)
1245 {
1246 rc = RTVfsFileReadAt(pThis->hVfsBacking, pNewDir->Core.FirstExtent.offDisk, pNewDir->pbDir, pNewDir->cbDir, NULL);
1247 if (RT_SUCCESS(rc))
1248 {
1249#ifdef LOG_ENABLED
1250 rtFsIso9660Dir_LogContent(pNewDir);
1251#endif
1252
1253 /*
1254 * Link into parent directory so we can use it to update
1255 * our directory entry.
1256 */
1257 if (pParentDir)
1258 rtFsIso9660Dir_AddOpenChild(pParentDir, &pNewDir->Core);
1259 if (ppDir)
1260 *ppDir = pNewDir;
1261 return VINF_SUCCESS;
1262 }
1263 }
1264 else
1265 rc = VERR_NO_MEMORY;
1266 RTVfsDirRelease(*phVfsDir);
1267 }
1268 *phVfsDir = NIL_RTVFSDIR;
1269 if (ppDir)
1270 *ppDir = NULL;
1271 return rc;
1272}
1273
1274
1275
1276
1277/**
1278 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1279 */
1280static DECLCALLBACK(int) rtFsIso9660Vol_Close(void *pvThis)
1281{
1282 PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
1283 Log(("rtFsIso9660Vol_Close(%p)\n", pThis));
1284
1285 if (pThis->hVfsRootDir != NIL_RTVFSDIR)
1286 {
1287 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
1288 uint32_t cRefs = RTVfsDirRelease(pThis->hVfsRootDir);
1289 Assert(cRefs == 0); NOREF(cRefs);
1290 pThis->hVfsRootDir = NIL_RTVFSDIR;
1291 pThis->pRootDir = NULL;
1292 }
1293
1294 RTVfsFileRelease(pThis->hVfsBacking);
1295 pThis->hVfsBacking = NIL_RTVFSFILE;
1296
1297 return VINF_SUCCESS;
1298}
1299
1300
1301/**
1302 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1303 */
1304static DECLCALLBACK(int) rtFsIso9660Vol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1305{
1306 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
1307 return VERR_WRONG_TYPE;
1308}
1309
1310
1311/**
1312 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
1313 */
1314static DECLCALLBACK(int) rtFsIso9660Vol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
1315{
1316 PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
1317 uint32_t cRefs = RTVfsDirRetain(pThis->hVfsRootDir);
1318 if (cRefs != UINT32_MAX)
1319 {
1320 *phVfsDir = pThis->hVfsRootDir;
1321 LogFlow(("rtFsIso9660Vol_OpenRoot -> %p\n", *phVfsDir));
1322 return VINF_SUCCESS;
1323 }
1324 return VERR_INTERNAL_ERROR_5;
1325}
1326
1327
1328/**
1329 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
1330 */
1331static DECLCALLBACK(int) rtFsIso9660Vol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
1332{
1333 RT_NOREF(pvThis, off, cb, pfUsed);
1334 return VERR_NOT_IMPLEMENTED;
1335}
1336
1337
1338DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIso9660VolOps =
1339{
1340 { /* Obj */
1341 RTVFSOBJOPS_VERSION,
1342 RTVFSOBJTYPE_VFS,
1343 "ISO 9660",
1344 rtFsIso9660Vol_Close,
1345 rtFsIso9660Vol_QueryInfo,
1346 RTVFSOBJOPS_VERSION
1347 },
1348 RTVFSOPS_VERSION,
1349 0 /* fFeatures */,
1350 rtFsIso9660Vol_OpenRoot,
1351 rtFsIso9660Vol_IsRangeInUse,
1352 RTVFSOPS_VERSION
1353};
1354
1355
1356
1357#ifdef LOG_ENABLED
1358
1359/** Logging helper. */
1360static size_t rtFsIso9660VolGetStrippedLength(const char *pachField, size_t cchField)
1361{
1362 while (cchField > 0 && pachField[cchField - 1] == ' ')
1363 cchField--;
1364 return cchField;
1365}
1366
1367
1368/**
1369 * Logs the primary or supplementary volume descriptor
1370 *
1371 * @param pVolDesc The descriptor.
1372 */
1373static void rtFsIso9660VolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
1374{
1375 if (LogIs2Enabled())
1376 {
1377 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
1378 Log2(("ISO9660: achSystemId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId)), pVolDesc->achSystemId));
1379 Log2(("ISO9660: achVolumeId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId)), pVolDesc->achVolumeId));
1380 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
1381 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
1382 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIso9660VolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
1383 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
1384 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
1385 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
1386 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
1387 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
1388 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
1389 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
1390 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
1391 Log2(("ISO9660: achVolumeSetId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId)), pVolDesc->achVolumeSetId));
1392 Log2(("ISO9660: achPublisherId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId)), pVolDesc->achPublisherId));
1393 Log2(("ISO9660: achDataPreparerId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId)), pVolDesc->achDataPreparerId));
1394 Log2(("ISO9660: achApplicationId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId)), pVolDesc->achApplicationId));
1395 Log2(("ISO9660: achCopyrightFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId)), pVolDesc->achCopyrightFileId));
1396 Log2(("ISO9660: achAbstractFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId)), pVolDesc->achAbstractFileId));
1397 Log2(("ISO9660: achBibliographicFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId)), pVolDesc->achBibliographicFileId));
1398 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1399 pVolDesc->BirthTime.achYear,
1400 pVolDesc->BirthTime.achMonth,
1401 pVolDesc->BirthTime.achDay,
1402 pVolDesc->BirthTime.achHour,
1403 pVolDesc->BirthTime.achMinute,
1404 pVolDesc->BirthTime.achSecond,
1405 pVolDesc->BirthTime.achCentisecond,
1406 pVolDesc->BirthTime.offUtc*4/60));
1407 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1408 pVolDesc->ModifyTime.achYear,
1409 pVolDesc->ModifyTime.achMonth,
1410 pVolDesc->ModifyTime.achDay,
1411 pVolDesc->ModifyTime.achHour,
1412 pVolDesc->ModifyTime.achMinute,
1413 pVolDesc->ModifyTime.achSecond,
1414 pVolDesc->ModifyTime.achCentisecond,
1415 pVolDesc->ModifyTime.offUtc*4/60));
1416 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1417 pVolDesc->ExpireTime.achYear,
1418 pVolDesc->ExpireTime.achMonth,
1419 pVolDesc->ExpireTime.achDay,
1420 pVolDesc->ExpireTime.achHour,
1421 pVolDesc->ExpireTime.achMinute,
1422 pVolDesc->ExpireTime.achSecond,
1423 pVolDesc->ExpireTime.achCentisecond,
1424 pVolDesc->ExpireTime.offUtc*4/60));
1425 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1426 pVolDesc->EffectiveTime.achYear,
1427 pVolDesc->EffectiveTime.achMonth,
1428 pVolDesc->EffectiveTime.achDay,
1429 pVolDesc->EffectiveTime.achHour,
1430 pVolDesc->EffectiveTime.achMinute,
1431 pVolDesc->EffectiveTime.achSecond,
1432 pVolDesc->EffectiveTime.achCentisecond,
1433 pVolDesc->EffectiveTime.offUtc*4/60));
1434 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
1435 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
1436
1437 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
1438 Log2(("ISO9660: RootDir.cbExtAttr: %#RX8\n", pVolDesc->RootDir.DirRec.cbExtAttr));
1439 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
1440 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
1441 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
1442 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
1443 pVolDesc->RootDir.DirRec.RecTime.bMonth,
1444 pVolDesc->RootDir.DirRec.RecTime.bDay,
1445 pVolDesc->RootDir.DirRec.RecTime.bHour,
1446 pVolDesc->RootDir.DirRec.RecTime.bMinute,
1447 pVolDesc->RootDir.DirRec.RecTime.bSecond,
1448 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
1449 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
1450 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
1451 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
1452 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
1453 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
1454 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
1455 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
1456 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
1457 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
1458 {
1459 Log2(("ISO9660: RootDir System Use:\n%.*Rhxd\n",
1460 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
1461 }
1462 }
1463}
1464
1465#endif /* LOG_ENABLED */
1466
1467/**
1468 * Deal with a root directory from a primary or supplemental descriptor.
1469 *
1470 * @returns IPRT status code.
1471 * @param pThis The ISO 9660 instance being initialized.
1472 * @param pRootDir The root directory record to check out.
1473 * @param pDstRootDir Where to store a copy of the root dir record.
1474 * @param pErrInfo Where to return additional error info. Can be NULL.
1475 */
1476static int rtFsIso9660VolHandleRootDir(PRTFSISO9660VOL pThis, PCISO9660DIRREC pRootDir,
1477 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
1478{
1479 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
1480 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
1481 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
1482
1483 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
1484 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1485 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
1486 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
1487 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1488 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
1489
1490 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
1491 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
1492 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
1493 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
1494 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
1495
1496 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
1497 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
1498 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
1499
1500 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
1501 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
1502 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
1503 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
1504 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1505 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
1506 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
1507
1508 /*
1509 * Seems okay, copy it.
1510 */
1511 *pDstRootDir = *pRootDir;
1512 return VINF_SUCCESS;
1513}
1514
1515
1516/**
1517 * Deal with a primary volume descriptor.
1518 *
1519 * @returns IPRT status code.
1520 * @param pThis The ISO 9660 instance being initialized.
1521 * @param pVolDesc The volume descriptor to handle.
1522 * @param offVolDesc The disk offset of the volume descriptor.
1523 * @param pRootDir Where to return a copy of the root directory record.
1524 * @param poffRootDirRec Where to return the disk offset of the root dir.
1525 * @param pErrInfo Where to return additional error info. Can be NULL.
1526 */
1527static int rtFsIso9660VolHandlePrimaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
1528 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
1529{
1530 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1531 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1532 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1533
1534 /*
1535 * We need the block size ...
1536 */
1537 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
1538 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
1539 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
1540 || pThis->cbBlock / pThis->cbSector < 1)
1541 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
1542 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
1543 if (pThis->cbBlock / pThis->cbSector > 128)
1544 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
1545
1546 /*
1547 * ... volume space size ...
1548 */
1549 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
1550 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
1551 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
1552 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
1553 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
1554
1555 /*
1556 * ... number of volumes in the set ...
1557 */
1558 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
1559 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
1560 || pThis->cVolumesInSet == 0)
1561 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
1562 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
1563 if (pThis->cVolumesInSet > 32)
1564 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
1565
1566 /*
1567 * ... primary volume sequence ID ...
1568 */
1569 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
1570 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
1571 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
1572 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
1573 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
1574 || pThis->idPrimaryVol < 1)
1575 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1576 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
1577
1578 /*
1579 * ... and the root directory record.
1580 */
1581 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
1582 return rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
1583}
1584
1585
1586/**
1587 * Deal with a supplementary volume descriptor.
1588 *
1589 * @returns IPRT status code.
1590 * @param pThis The ISO 9660 instance being initialized.
1591 * @param pVolDesc The volume descriptor to handle.
1592 * @param offVolDesc The disk offset of the volume descriptor.
1593 * @param pbUcs2Level Where to return the joliet level, if found. Caller
1594 * initializes this to zero, we'll return 1, 2 or 3 if
1595 * joliet was detected.
1596 * @param pRootDir Where to return the root directory, if found.
1597 * @param poffRootDirRec Where to return the disk offset of the root dir.
1598 * @param pErrInfo Where to return additional error info. Can be NULL.
1599 */
1600static int rtFsIso9660VolHandleSupplementaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
1601 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
1602 PRTERRINFO pErrInfo)
1603{
1604 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1605 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1606 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1607
1608 /*
1609 * Is this a joliet volume descriptor? If not, we probably don't need to
1610 * care about it.
1611 */
1612 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
1613 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
1614 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
1615 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
1616 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
1617 return VINF_SUCCESS;
1618
1619 /*
1620 * Skip if joliet is unwanted.
1621 */
1622 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
1623 return VINF_SUCCESS;
1624
1625 /*
1626 * Check that the joliet descriptor matches the primary one.
1627 * Note! These are our assumptions and may be wrong.
1628 */
1629 if (pThis->cbBlock == 0)
1630 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1631 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
1632 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
1633 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1634 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
1635 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
1636 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
1637 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1638 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
1639 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
1640 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
1641 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1642 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
1643 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
1644 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
1645 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1646 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
1647 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
1648
1649 if (*pbUcs2Level != 0)
1650 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
1651
1652 /*
1653 * Switch to the joliet root dir as it has UTF-16 stuff in it.
1654 */
1655 int rc = rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
1656 if (RT_SUCCESS(rc))
1657 {
1658 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
1659 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
1660 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
1661 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
1662 }
1663 return rc;
1664}
1665
1666
1667
1668/**
1669 * Worker for RTFsIso9660VolOpen.
1670 *
1671 * @returns IPRT status code.
1672 * @param pThis The FAT VFS instance to initialize.
1673 * @param hVfsSelf The FAT VFS handle (no reference consumed).
1674 * @param hVfsBacking The file backing the alleged FAT file system.
1675 * Reference is consumed (via rtFsIso9660Vol_Close).
1676 * @param fFlags Flags, MBZ.
1677 * @param pErrInfo Where to return additional error info. Can be NULL.
1678 */
1679static int rtFsIso9660VolTryInit(PRTFSISO9660VOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
1680{
1681 uint32_t const cbSector = 2048;
1682
1683 /*
1684 * First initialize the state so that rtFsIso9660Vol_Destroy won't trip up.
1685 */
1686 pThis->hVfsSelf = hVfsSelf;
1687 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIso9660Vol_Destroy releases it. */
1688 pThis->cbBacking = 0;
1689 pThis->fFlags = fFlags;
1690 pThis->cbSector = cbSector;
1691 pThis->cbBlock = 0;
1692 pThis->cBlocksInPrimaryVolumeSpace = 0;
1693 pThis->cbPrimaryVolumeSpace = 0;
1694 pThis->cVolumesInSet = 0;
1695 pThis->idPrimaryVol = UINT32_MAX;
1696 pThis->fIsUtf16 = false;
1697 pThis->hVfsRootDir = NIL_RTVFSDIR;
1698 pThis->pRootDir = NULL;
1699
1700 /*
1701 * Get stuff that may fail.
1702 */
1703 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
1704 if (RT_FAILURE(rc))
1705 return rc;
1706
1707 /*
1708 * Read the volume descriptors starting at logical sector 16.
1709 */
1710 union
1711 {
1712 uint8_t ab[_4K];
1713 uint16_t au16[_4K / 2];
1714 uint32_t au32[_4K / 4];
1715 ISO9660VOLDESCHDR VolDescHdr;
1716 ISO9660BOOTRECORD BootRecord;
1717 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
1718 ISO9660SUPVOLDESC SupVolDesc;
1719 ISO9660VOLPARTDESC VolPartDesc;
1720 } Buf;
1721 RT_ZERO(Buf);
1722
1723 uint64_t offRootDirRec = UINT64_MAX;
1724 ISO9660DIRREC RootDir;
1725 RT_ZERO(RootDir);
1726
1727 uint64_t offJolietRootDirRec = UINT64_MAX;
1728 uint8_t bJolietUcs2Level = 0;
1729 ISO9660DIRREC JolietRootDir;
1730 RT_ZERO(JolietRootDir);
1731
1732 uint32_t cPrimaryVolDescs = 0;
1733 uint32_t cSupplementaryVolDescs = 0;
1734 uint32_t cBootRecordVolDescs = 0;
1735 uint32_t offVolDesc = 16 * cbSector;
1736 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
1737 {
1738 if (iVolDesc > 32)
1739 return RTErrInfoSet(pErrInfo, rc, "More than 32 volume descriptors, doesn't seem right...");
1740
1741 /* Read the next one and check the signature. */
1742 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
1743 if (RT_FAILURE(rc))
1744 return RTErrInfoSetF(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
1745
1746 if ( Buf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
1747 || Buf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
1748 || Buf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
1749 || Buf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
1750 || Buf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
1751 {
1752 if (!iVolDesc)
1753 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
1754 "No ISO 9660 CD001 signature, instead found: %.5Rhxs", Buf.VolDescHdr.achStdId);
1755 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Missing terminator volume descriptor?");
1756 }
1757
1758 /* Do type specific handling. */
1759 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
1760 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
1761 {
1762 cPrimaryVolDescs++;
1763 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
1764 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1765 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
1766#ifdef LOG_ENABLED
1767 rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
1768#endif
1769 if (cPrimaryVolDescs > 1)
1770 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
1771 rc = rtFsIso9660VolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
1772 }
1773 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
1774 {
1775 cSupplementaryVolDescs++;
1776 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
1777 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1778 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
1779#ifdef LOG_ENABLED
1780 rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
1781#endif
1782 rc = rtFsIso9660VolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
1783 &offJolietRootDirRec, pErrInfo);
1784 }
1785 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
1786 {
1787 cBootRecordVolDescs++;
1788 }
1789 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
1790 {
1791 if (!cPrimaryVolDescs)
1792 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
1793 break;
1794 }
1795 else
1796 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1797 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
1798 if (RT_FAILURE(rc))
1799 return rc;
1800 }
1801
1802 /*
1803 * We may be faced with choosing between joliet and rock ridge (we won't
1804 * have this choice when RTFSISO9660_F_NO_JOLIET is set). The joliet
1805 * option is generally favorable as we don't have to guess wrt to the file
1806 * name encoding. So, we'll pick that for now.
1807 *
1808 * Note! Should we change this preference for joliet, there fun wrt making sure
1809 * there really is rock ridge stuff in the primary volume as well as
1810 * making sure there really is anything of value in the primary volume.
1811 */
1812 if (bJolietUcs2Level != 0)
1813 {
1814 pThis->fIsUtf16 = true;
1815 return rtFsIso9660Dir_New(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->hVfsRootDir, &pThis->pRootDir);
1816 }
1817 return rtFsIso9660Dir_New(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->hVfsRootDir, &pThis->pRootDir);
1818}
1819
1820
1821/**
1822 * Opens an ISO 9660 file system volume.
1823 *
1824 * @returns IPRT status code.
1825 * @param hVfsFileIn The file or device backing the volume.
1826 * @param fFlags RTFSISO9660_F_XXX.
1827 * @param phVfs Where to return the virtual file system handle.
1828 * @param pErrInfo Where to return additional error information.
1829 */
1830RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
1831{
1832 /*
1833 * Quick input validation.
1834 */
1835 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
1836 *phVfs = NIL_RTVFS;
1837 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
1838
1839 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
1840 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1841
1842 /*
1843 * Create a new FAT VFS instance and try initialize it using the given input file.
1844 */
1845 RTVFS hVfs = NIL_RTVFS;
1846 void *pvThis = NULL;
1847 int rc = RTVfsNew(&g_rtFsIso9660VolOps, sizeof(RTFSISO9660VOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
1848 if (RT_SUCCESS(rc))
1849 {
1850 rc = rtFsIso9660VolTryInit((PRTFSISO9660VOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
1851 if (RT_SUCCESS(rc))
1852 *phVfs = hVfs;
1853 else
1854 RTVfsRelease(hVfs);
1855 }
1856 else
1857 RTVfsFileRelease(hVfsFileIn);
1858 return rc;
1859}
1860
1861
1862/**
1863 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
1864 */
1865static DECLCALLBACK(int) rtVfsChainIso9660Vol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
1866 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
1867{
1868 RT_NOREF(pProviderReg, pSpec);
1869
1870 /*
1871 * Basic checks.
1872 */
1873 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
1874 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
1875 if ( pElement->enmType != RTVFSOBJTYPE_VFS
1876 && pElement->enmType != RTVFSOBJTYPE_DIR)
1877 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
1878 if (pElement->cArgs > 1)
1879 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
1880
1881 /*
1882 * Parse the flag if present, save in pElement->uProvider.
1883 */
1884 uint32_t fFlags = 0;
1885 if (pElement->cArgs > 0)
1886 {
1887 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
1888 {
1889 const char *psz = pElement->paArgs[iArg].psz;
1890 if (*psz)
1891 {
1892 if (!strcmp(psz, "nojoliet"))
1893 fFlags |= RTFSISO9660_F_NO_JOLIET;
1894 else if (!strcmp(psz, "norock"))
1895 fFlags |= RTFSISO9660_F_NO_ROCK;
1896 else
1897 {
1898 *poffError = pElement->paArgs[iArg].offSpec;
1899 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
1900 }
1901 }
1902 }
1903 }
1904
1905 pElement->uProvider = fFlags;
1906 return VINF_SUCCESS;
1907}
1908
1909
1910/**
1911 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
1912 */
1913static DECLCALLBACK(int) rtVfsChainIso9660Vol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
1914 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
1915 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1916{
1917 RT_NOREF(pProviderReg, pSpec, poffError);
1918
1919 int rc;
1920 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
1921 if (hVfsFileIn != NIL_RTVFSFILE)
1922 {
1923 RTVFS hVfs;
1924 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
1925 RTVfsFileRelease(hVfsFileIn);
1926 if (RT_SUCCESS(rc))
1927 {
1928 *phVfsObj = RTVfsObjFromVfs(hVfs);
1929 RTVfsRelease(hVfs);
1930 if (*phVfsObj != NIL_RTVFSOBJ)
1931 return VINF_SUCCESS;
1932 rc = VERR_VFS_CHAIN_CAST_FAILED;
1933 }
1934 }
1935 else
1936 rc = VERR_VFS_CHAIN_CAST_FAILED;
1937 return rc;
1938}
1939
1940
1941/**
1942 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
1943 */
1944static DECLCALLBACK(bool) rtVfsChainIso9660Vol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
1945 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
1946 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
1947{
1948 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
1949 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
1950 || !pReuseElement->paArgs[0].uProvider)
1951 return true;
1952 return false;
1953}
1954
1955
1956/** VFS chain element 'file'. */
1957static RTVFSCHAINELEMENTREG g_rtVfsChainIso9660VolReg =
1958{
1959 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1960 /* fReserved = */ 0,
1961 /* pszName = */ "isofs",
1962 /* ListEntry = */ { NULL, NULL },
1963 /* pszHelp = */ "Open a ISO 9660 file system, requires a file object on the left side.\n"
1964 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
1965 "The 'norock' option make it ignore any rock ridge info.\n",
1966 /* pfnValidate = */ rtVfsChainIso9660Vol_Validate,
1967 /* pfnInstantiate = */ rtVfsChainIso9660Vol_Instantiate,
1968 /* pfnCanReuseElement = */ rtVfsChainIso9660Vol_CanReuseElement,
1969 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1970};
1971
1972RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIso9660VolReg, rtVfsChainIso9660VolReg);
1973
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