VirtualBox

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

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

iso9660vfs.cpp: Can find and read files from iso.

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