VirtualBox

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

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

iso9660vfs,fatvfs: reference count fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.8 KB
Line 
1/* $Id: iso9660vfs.cpp 66736 2017-05-02 00:01:39Z 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);
920RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSubDir);
921 return VERR_NOT_IMPLEMENTED;
922}
923
924
925/**
926 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
927 */
928static DECLCALLBACK(int) rtFsIso9660Dir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
929{
930 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
931RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
932 return VERR_NOT_SUPPORTED;
933}
934
935
936/**
937 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
938 */
939static DECLCALLBACK(int) rtFsIso9660Dir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
940 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
941{
942 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
943RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
944 return VERR_NOT_SUPPORTED;
945}
946
947
948/**
949 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
950 */
951static DECLCALLBACK(int) rtFsIso9660Dir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
952{
953 RT_NOREF(pvThis, pszEntry, fType);
954RTAssertMsg2("%s: %s\n", __FUNCTION__, pszEntry);
955 return VERR_NOT_IMPLEMENTED;
956}
957
958
959/**
960 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
961 */
962static DECLCALLBACK(int) rtFsIso9660Dir_RewindDir(void *pvThis)
963{
964 RT_NOREF(pvThis);
965RTAssertMsg2("%s\n", __FUNCTION__);
966 return VERR_NOT_IMPLEMENTED;
967}
968
969
970/**
971 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
972 */
973static DECLCALLBACK(int) rtFsIso9660Dir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
974 RTFSOBJATTRADD enmAddAttr)
975{
976 RT_NOREF(pvThis, pDirEntry, pcbDirEntry, enmAddAttr);
977RTAssertMsg2("%s\n", __FUNCTION__);
978 return VERR_NOT_IMPLEMENTED;
979}
980
981
982/**
983 * FAT file operations.
984 */
985static const RTVFSDIROPS g_rtFsIso9660DirOps =
986{
987 { /* Obj */
988 RTVFSOBJOPS_VERSION,
989 RTVFSOBJTYPE_DIR,
990 "ISO 9660 Dir",
991 rtFsIso9660Dir_Close,
992 rtFsIso9660Obj_QueryInfo,
993 RTVFSOBJOPS_VERSION
994 },
995 RTVFSDIROPS_VERSION,
996 0,
997 { /* ObjSet */
998 RTVFSOBJSETOPS_VERSION,
999 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
1000 rtFsIso9660Obj_SetMode,
1001 rtFsIso9660Obj_SetTimes,
1002 rtFsIso9660Obj_SetOwner,
1003 RTVFSOBJSETOPS_VERSION
1004 },
1005 rtFsIso9660Dir_TraversalOpen,
1006 rtFsIso9660Dir_OpenFile,
1007 rtFsIso9660Dir_OpenDir,
1008 rtFsIso9660Dir_CreateDir,
1009 rtFsIso9660Dir_OpenSymlink,
1010 rtFsIso9660Dir_CreateSymlink,
1011 rtFsIso9660Dir_UnlinkEntry,
1012 rtFsIso9660Dir_RewindDir,
1013 rtFsIso9660Dir_ReadDir,
1014 RTVFSDIROPS_VERSION,
1015};
1016
1017
1018/**
1019 * Adds an open child to the parent directory.
1020 *
1021 * Maintains an additional reference to the parent dir to prevent it from going
1022 * away. If @a pDir is the root directory, it also ensures the volume is
1023 * referenced and sticks around until the last open object is gone.
1024 *
1025 * @param pDir The directory.
1026 * @param pChild The child being opened.
1027 * @sa rtFsIso9660Dir_RemoveOpenChild
1028 */
1029static void rtFsIso9660Dir_AddOpenChild(PRTFSISO9660DIR pDir, PRTFSISO9660OBJ pChild)
1030{
1031 /* First child that gets opened retains the parent directory. This is
1032 released by the final open child. */
1033 if (RTListIsEmpty(&pDir->OpenChildren))
1034 {
1035 uint32_t cRefs = RTVfsDirRetain(pDir->hVfsSelf);
1036 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1037
1038 /* Root also retains the whole file system. */
1039 if (pDir->Core.pVol->pRootDir == pDir)
1040 {
1041 Assert(pDir->Core.pVol);
1042 Assert(pDir->Core.pVol == pChild->pVol);
1043 cRefs = RTVfsRetain(pDir->Core.pVol->hVfsSelf);
1044 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1045 LogFlow(("rtFsIso9660Dir_AddOpenChild(%p,%p) retains volume (%d)\n", pDir, pChild, cRefs));
1046 }
1047 else
1048 LogFlow(("rtFsIso9660Dir_AddOpenChild(%p,%p) retains parent only (%d)\n", pDir, pChild, cRefs));
1049 }
1050 else
1051 LogFlow(("rtFsIso9660Dir_AddOpenChild(%p,%p) retains nothing\n", pDir, pChild));
1052 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
1053 pChild->pParentDir = pDir;
1054}
1055
1056
1057/**
1058 * Removes an open child to the parent directory.
1059 *
1060 * @param pDir The directory.
1061 * @param pChild The child being removed.
1062 *
1063 * @remarks This is the very last thing you do as it may cause a few other
1064 * objects to be released recursively (parent dir and the volume).
1065 *
1066 * @sa rtFsIso9660Dir_AddOpenChild
1067 */
1068static void rtFsIso9660Dir_RemoveOpenChild(PRTFSISO9660DIR pDir, PRTFSISO9660OBJ pChild)
1069{
1070 AssertReturnVoid(pChild->pParentDir == pDir);
1071 RTListNodeRemove(&pChild->Entry);
1072 pChild->pParentDir = NULL;
1073
1074 /* Final child? If so, release directory. */
1075 if (RTListIsEmpty(&pDir->OpenChildren))
1076 {
1077 bool const fIsRootDir = pDir->Core.pVol->pRootDir == pDir;
1078
1079 uint32_t cRefs = RTVfsDirRelease(pDir->hVfsSelf);
1080 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1081
1082 /* Root directory releases the file system as well. Since the volume
1083 holds a reference to the root directory, it will remain valid after
1084 the above release. */
1085 if (fIsRootDir)
1086 {
1087 Assert(cRefs > 0);
1088 Assert(pDir->Core.pVol);
1089 Assert(pDir->Core.pVol == pChild->pVol);
1090 cRefs = RTVfsRelease(pDir->Core.pVol->hVfsSelf);
1091 Assert(cRefs != UINT32_MAX); NOREF(cRefs);
1092 LogFlow(("rtFsIso9660Dir_RemoveOpenChild(%p,%p) releases volume (%d)\n", pDir, pChild, cRefs));
1093 }
1094 else
1095 LogFlow(("rtFsIso9660Dir_RemoveOpenChild(%p,%p) releases parent only (%d)\n", pDir, pChild, cRefs));
1096 }
1097 else
1098 LogFlow(("rtFsIso9660Dir_RemoveOpenChild(%p,%p) releases nothing\n", pDir, pChild));
1099}
1100
1101
1102#ifdef LOG_ENABLED
1103/**
1104 * Logs the content of a directory.
1105 */
1106static void rtFsIso9660Dir_LogContent(PRTFSISO9660DIR pThis)
1107{
1108 if (LogIs2Enabled())
1109 {
1110 uint32_t offRec = 0;
1111 while (offRec < pThis->cbDir)
1112 {
1113 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
1114 if (pDirRec->cbDirRec == 0)
1115 break;
1116
1117 RTUTF16 wszName[128];
1118 if (pThis->Core.pVol->fIsUtf16)
1119 {
1120 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
1121 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
1122 pwszSrc--;
1123 *pwszDst-- = '\0';
1124 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
1125 {
1126 *pwszDst = RT_BE2H_U16(*pwszSrc);
1127 pwszDst--;
1128 pwszSrc--;
1129 }
1130 }
1131 else
1132 {
1133 PRTUTF16 pwszDst = wszName;
1134 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
1135 *pwszDst++ = pDirRec->achFileId[off];
1136 *pwszDst = '\0';
1137 }
1138
1139 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",
1140 offRec,
1141 pDirRec->cbDirRec,
1142 pDirRec->cbExtAttr,
1143 ISO9660_GET_ENDIAN(&pDirRec->cbData),
1144 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
1145 pDirRec->fFileFlags,
1146 pDirRec->RecTime.bYear + 1900,
1147 pDirRec->RecTime.bMonth,
1148 pDirRec->RecTime.bDay,
1149 pDirRec->RecTime.bHour,
1150 pDirRec->RecTime.bMinute,
1151 pDirRec->RecTime.bSecond,
1152 pDirRec->RecTime.offUtc*4/60,
1153 pDirRec->bFileUnitSize,
1154 pDirRec->bInterleaveGapSize,
1155 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
1156 wszName));
1157
1158 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
1159 if (offSysUse < pDirRec->cbDirRec)
1160 {
1161 Log2(("ISO9660: system use:\n%.*Rhxd\n", pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
1162 }
1163
1164 /* advance */
1165 offRec += pDirRec->cbDirRec;
1166 }
1167 }
1168}
1169#endif /* LOG_ENABLED */
1170
1171
1172/**
1173 * Instantiates a new directory.
1174 *
1175 * @returns IPRT status code.
1176 * @param pThis The FAT volume instance.
1177 * @param pParentDir The parent directory. This is NULL for the root
1178 * directory.
1179 * @param pDirRec The directory record.
1180 * @param cDirRecs Number of directory records if more than one.
1181 * @param offDirRec The byte offset of the directory record.
1182 * @param phVfsDir Where to return the directory handle.
1183 * @param ppDir Where to return the FAT directory instance data.
1184 */
1185static int rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIR pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
1186 uint64_t offDirRec, PRTVFSDIR phVfsDir, PRTFSISO9660DIR *ppDir)
1187{
1188 if (ppDir)
1189 *ppDir = NULL;
1190
1191 PRTFSISO9660DIR pNewDir;
1192 int rc = RTVfsNewDir(&g_rtFsIso9660DirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
1193 phVfsDir, (void **)&pNewDir);
1194 if (RT_SUCCESS(rc))
1195 {
1196 /*
1197 * Initialize it all so rtFsIso9660Dir_Close doesn't trip up in anyway.
1198 */
1199 rtFsIso9660Obj_InitFromDirRec(&pNewDir->Core, pDirRec, cDirRecs, offDirRec, pThis);
1200 RTListInit(&pNewDir->OpenChildren);
1201 pNewDir->hVfsSelf = *phVfsDir;
1202 pNewDir->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
1203 pNewDir->pbDir = (uint8_t *)RTMemAllocZ(pNewDir->cbDir + 256);
1204 if (pNewDir->pbDir)
1205 {
1206 rc = RTVfsFileReadAt(pThis->hVfsBacking, pNewDir->Core.FirstExtent.offDisk, pNewDir->pbDir, pNewDir->cbDir, NULL);
1207 if (RT_SUCCESS(rc))
1208 {
1209#ifdef LOG_ENABLED
1210 rtFsIso9660Dir_LogContent(pNewDir);
1211#endif
1212
1213 /*
1214 * Link into parent directory so we can use it to update
1215 * our directory entry.
1216 */
1217 if (pParentDir)
1218 rtFsIso9660Dir_AddOpenChild(pParentDir, &pNewDir->Core);
1219 if (ppDir)
1220 *ppDir = pNewDir;
1221 return VINF_SUCCESS;
1222 }
1223 }
1224 else
1225 rc = VERR_NO_MEMORY;
1226 RTVfsDirRelease(*phVfsDir);
1227 }
1228 *phVfsDir = NIL_RTVFSDIR;
1229 if (ppDir)
1230 *ppDir = NULL;
1231 return rc;
1232}
1233
1234
1235
1236
1237/**
1238 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1239 */
1240static DECLCALLBACK(int) rtFsIso9660Vol_Close(void *pvThis)
1241{
1242 PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
1243 LogFlow(("rtFsIso9660Vol_Close(%p)\n", pThis));
1244
1245 if (pThis->hVfsRootDir != NIL_RTVFSDIR)
1246 {
1247 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
1248 uint32_t cRefs = RTVfsDirRelease(pThis->hVfsRootDir);
1249 Assert(cRefs == 0); NOREF(cRefs);
1250 pThis->hVfsRootDir = NIL_RTVFSDIR;
1251 pThis->pRootDir = NULL;
1252 }
1253
1254 RTVfsFileRelease(pThis->hVfsBacking);
1255 pThis->hVfsBacking = NIL_RTVFSFILE;
1256
1257 return VINF_SUCCESS;
1258}
1259
1260
1261/**
1262 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1263 */
1264static DECLCALLBACK(int) rtFsIso9660Vol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1265{
1266 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
1267 return VERR_WRONG_TYPE;
1268}
1269
1270
1271/**
1272 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
1273 */
1274static DECLCALLBACK(int) rtFsIso9660Vol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
1275{
1276 PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
1277 uint32_t cRefs = RTVfsDirRetain(pThis->hVfsRootDir);
1278 if (cRefs != UINT32_MAX)
1279 {
1280 *phVfsDir = pThis->hVfsRootDir;
1281 LogFlow(("rtFsIso9660Vol_OpenRoot -> %p\n", *phVfsDir));
1282 return VINF_SUCCESS;
1283 }
1284 return VERR_INTERNAL_ERROR_5;
1285}
1286
1287
1288/**
1289 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
1290 */
1291static DECLCALLBACK(int) rtFsIso9660Vol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
1292{
1293 RT_NOREF(pvThis, off, cb, pfUsed);
1294 return VERR_NOT_IMPLEMENTED;
1295}
1296
1297
1298DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIso9660VolOps =
1299{
1300 { /* Obj */
1301 RTVFSOBJOPS_VERSION,
1302 RTVFSOBJTYPE_VFS,
1303 "ISO 9660",
1304 rtFsIso9660Vol_Close,
1305 rtFsIso9660Vol_QueryInfo,
1306 RTVFSOBJOPS_VERSION
1307 },
1308 RTVFSOPS_VERSION,
1309 0 /* fFeatures */,
1310 rtFsIso9660Vol_OpenRoot,
1311 rtFsIso9660Vol_IsRangeInUse,
1312 RTVFSOPS_VERSION
1313};
1314
1315
1316
1317#ifdef LOG_ENABLED
1318
1319/** Logging helper. */
1320static size_t rtFsIso9660VolGetStrippedLength(const char *pachField, size_t cchField)
1321{
1322 while (cchField > 0 && pachField[cchField - 1] == ' ')
1323 cchField--;
1324 return cchField;
1325}
1326
1327
1328/**
1329 * Logs the primary or supplementary volume descriptor
1330 *
1331 * @param pVolDesc The descriptor.
1332 */
1333static void rtFsIso9660VolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
1334{
1335 if (LogIs2Enabled())
1336 {
1337 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
1338 Log2(("ISO9660: achSystemId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId)), pVolDesc->achSystemId));
1339 Log2(("ISO9660: achVolumeId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId)), pVolDesc->achVolumeId));
1340 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
1341 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
1342 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIso9660VolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
1343 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
1344 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
1345 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
1346 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
1347 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
1348 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
1349 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
1350 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
1351 Log2(("ISO9660: achVolumeSetId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId)), pVolDesc->achVolumeSetId));
1352 Log2(("ISO9660: achPublisherId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId)), pVolDesc->achPublisherId));
1353 Log2(("ISO9660: achDataPreparerId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId)), pVolDesc->achDataPreparerId));
1354 Log2(("ISO9660: achApplicationId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId)), pVolDesc->achApplicationId));
1355 Log2(("ISO9660: achCopyrightFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId)), pVolDesc->achCopyrightFileId));
1356 Log2(("ISO9660: achAbstractFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId)), pVolDesc->achAbstractFileId));
1357 Log2(("ISO9660: achBibliographicFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId)), pVolDesc->achBibliographicFileId));
1358 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1359 pVolDesc->BirthTime.achYear,
1360 pVolDesc->BirthTime.achMonth,
1361 pVolDesc->BirthTime.achDay,
1362 pVolDesc->BirthTime.achHour,
1363 pVolDesc->BirthTime.achMinute,
1364 pVolDesc->BirthTime.achSecond,
1365 pVolDesc->BirthTime.achCentisecond,
1366 pVolDesc->BirthTime.offUtc*4/60));
1367 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1368 pVolDesc->ModifyTime.achYear,
1369 pVolDesc->ModifyTime.achMonth,
1370 pVolDesc->ModifyTime.achDay,
1371 pVolDesc->ModifyTime.achHour,
1372 pVolDesc->ModifyTime.achMinute,
1373 pVolDesc->ModifyTime.achSecond,
1374 pVolDesc->ModifyTime.achCentisecond,
1375 pVolDesc->ModifyTime.offUtc*4/60));
1376 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1377 pVolDesc->ExpireTime.achYear,
1378 pVolDesc->ExpireTime.achMonth,
1379 pVolDesc->ExpireTime.achDay,
1380 pVolDesc->ExpireTime.achHour,
1381 pVolDesc->ExpireTime.achMinute,
1382 pVolDesc->ExpireTime.achSecond,
1383 pVolDesc->ExpireTime.achCentisecond,
1384 pVolDesc->ExpireTime.offUtc*4/60));
1385 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1386 pVolDesc->EffectiveTime.achYear,
1387 pVolDesc->EffectiveTime.achMonth,
1388 pVolDesc->EffectiveTime.achDay,
1389 pVolDesc->EffectiveTime.achHour,
1390 pVolDesc->EffectiveTime.achMinute,
1391 pVolDesc->EffectiveTime.achSecond,
1392 pVolDesc->EffectiveTime.achCentisecond,
1393 pVolDesc->EffectiveTime.offUtc*4/60));
1394 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
1395 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
1396
1397 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
1398 Log2(("ISO9660: RootDir.cbExtAttr: %#RX8\n", pVolDesc->RootDir.DirRec.cbExtAttr));
1399 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
1400 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
1401 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
1402 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
1403 pVolDesc->RootDir.DirRec.RecTime.bMonth,
1404 pVolDesc->RootDir.DirRec.RecTime.bDay,
1405 pVolDesc->RootDir.DirRec.RecTime.bHour,
1406 pVolDesc->RootDir.DirRec.RecTime.bMinute,
1407 pVolDesc->RootDir.DirRec.RecTime.bSecond,
1408 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
1409 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
1410 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
1411 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
1412 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
1413 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
1414 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
1415 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
1416 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
1417 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
1418 {
1419 Log2(("ISO9660: RootDir System Use:\n%.*Rhxd\n",
1420 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
1421 }
1422 }
1423}
1424
1425#endif /* LOG_ENABLED */
1426
1427/**
1428 * Deal with a root directory from a primary or supplemental descriptor.
1429 *
1430 * @returns IPRT status code.
1431 * @param pThis The ISO 9660 instance being initialized.
1432 * @param pRootDir The root directory record to check out.
1433 * @param pDstRootDir Where to store a copy of the root dir record.
1434 * @param pErrInfo Where to return additional error info. Can be NULL.
1435 */
1436static int rtFsIso9660VolHandleRootDir(PRTFSISO9660VOL pThis, PCISO9660DIRREC pRootDir,
1437 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
1438{
1439 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
1440 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
1441 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
1442
1443 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
1444 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1445 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
1446 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
1447 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1448 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
1449
1450 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
1451 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
1452 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
1453 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
1454 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
1455
1456 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
1457 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
1458 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
1459
1460 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
1461 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
1462 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
1463 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
1464 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1465 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
1466 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
1467
1468 /*
1469 * Seems okay, copy it.
1470 */
1471 *pDstRootDir = *pRootDir;
1472 return VINF_SUCCESS;
1473}
1474
1475
1476/**
1477 * Deal with a primary volume descriptor.
1478 *
1479 * @returns IPRT status code.
1480 * @param pThis The ISO 9660 instance being initialized.
1481 * @param pVolDesc The volume descriptor to handle.
1482 * @param offVolDesc The disk offset of the volume descriptor.
1483 * @param pRootDir Where to return a copy of the root directory record.
1484 * @param poffRootDirRec Where to return the disk offset of the root dir.
1485 * @param pErrInfo Where to return additional error info. Can be NULL.
1486 */
1487static int rtFsIso9660VolHandlePrimaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
1488 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
1489{
1490 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1491 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1492 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1493
1494 /*
1495 * We need the block size ...
1496 */
1497 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
1498 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
1499 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
1500 || pThis->cbBlock / pThis->cbSector < 1)
1501 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
1502 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
1503 if (pThis->cbBlock / pThis->cbSector > 128)
1504 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
1505
1506 /*
1507 * ... volume space size ...
1508 */
1509 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
1510 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
1511 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
1512 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
1513 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
1514
1515 /*
1516 * ... number of volumes in the set ...
1517 */
1518 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
1519 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
1520 || pThis->cVolumesInSet == 0)
1521 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
1522 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
1523 if (pThis->cVolumesInSet > 32)
1524 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
1525
1526 /*
1527 * ... primary volume sequence ID ...
1528 */
1529 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
1530 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
1531 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
1532 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
1533 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
1534 || pThis->idPrimaryVol < 1)
1535 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1536 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
1537
1538 /*
1539 * ... and the root directory record.
1540 */
1541 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
1542 return rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
1543}
1544
1545
1546/**
1547 * Deal with a supplementary volume descriptor.
1548 *
1549 * @returns IPRT status code.
1550 * @param pThis The ISO 9660 instance being initialized.
1551 * @param pVolDesc The volume descriptor to handle.
1552 * @param offVolDesc The disk offset of the volume descriptor.
1553 * @param pbUcs2Level Where to return the joliet level, if found. Caller
1554 * initializes this to zero, we'll return 1, 2 or 3 if
1555 * joliet was detected.
1556 * @param pRootDir Where to return the root directory, if found.
1557 * @param poffRootDirRec Where to return the disk offset of the root dir.
1558 * @param pErrInfo Where to return additional error info. Can be NULL.
1559 */
1560static int rtFsIso9660VolHandleSupplementaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
1561 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
1562 PRTERRINFO pErrInfo)
1563{
1564 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1565 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1566 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1567
1568 /*
1569 * Is this a joliet volume descriptor? If not, we probably don't need to
1570 * care about it.
1571 */
1572 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
1573 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
1574 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
1575 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
1576 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
1577 return VINF_SUCCESS;
1578
1579 /*
1580 * Skip if joliet is unwanted.
1581 */
1582 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
1583 return VINF_SUCCESS;
1584
1585 /*
1586 * Check that the joliet descriptor matches the primary one.
1587 * Note! These are our assumptions and may be wrong.
1588 */
1589 if (pThis->cbBlock == 0)
1590 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1591 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
1592 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
1593 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1594 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
1595 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
1596 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
1597 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1598 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
1599 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
1600 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
1601 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1602 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
1603 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
1604 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
1605 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1606 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
1607 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
1608
1609 if (*pbUcs2Level != 0)
1610 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
1611
1612 /*
1613 * Switch to the joliet root dir as it has UTF-16 stuff in it.
1614 */
1615 int rc = rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
1616 if (RT_SUCCESS(rc))
1617 {
1618 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
1619 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
1620 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
1621 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
1622 }
1623 return rc;
1624}
1625
1626
1627
1628/**
1629 * Worker for RTFsIso9660VolOpen.
1630 *
1631 * @returns IPRT status code.
1632 * @param pThis The FAT VFS instance to initialize.
1633 * @param hVfsSelf The FAT VFS handle (no reference consumed).
1634 * @param hVfsBacking The file backing the alleged FAT file system.
1635 * Reference is consumed (via rtFsIso9660Vol_Close).
1636 * @param fFlags Flags, MBZ.
1637 * @param pErrInfo Where to return additional error info. Can be NULL.
1638 */
1639static int rtFsIso9660VolTryInit(PRTFSISO9660VOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
1640{
1641 uint32_t const cbSector = 2048;
1642
1643 /*
1644 * First initialize the state so that rtFsIso9660Vol_Destroy won't trip up.
1645 */
1646 pThis->hVfsSelf = hVfsSelf;
1647 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIso9660Vol_Destroy releases it. */
1648 pThis->cbBacking = 0;
1649 pThis->fFlags = fFlags;
1650 pThis->cbSector = cbSector;
1651 pThis->cbBlock = 0;
1652 pThis->cBlocksInPrimaryVolumeSpace = 0;
1653 pThis->cbPrimaryVolumeSpace = 0;
1654 pThis->cVolumesInSet = 0;
1655 pThis->idPrimaryVol = UINT32_MAX;
1656 pThis->fIsUtf16 = false;
1657 pThis->hVfsRootDir = NIL_RTVFSDIR;
1658 pThis->pRootDir = NULL;
1659
1660 /*
1661 * Get stuff that may fail.
1662 */
1663 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
1664 if (RT_FAILURE(rc))
1665 return rc;
1666
1667 /*
1668 * Read the volume descriptors starting at logical sector 16.
1669 */
1670 union
1671 {
1672 uint8_t ab[_4K];
1673 uint16_t au16[_4K / 2];
1674 uint32_t au32[_4K / 4];
1675 ISO9660VOLDESCHDR VolDescHdr;
1676 ISO9660BOOTRECORD BootRecord;
1677 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
1678 ISO9660SUPVOLDESC SupVolDesc;
1679 ISO9660VOLPARTDESC VolPartDesc;
1680 } Buf;
1681 RT_ZERO(Buf);
1682
1683 uint64_t offRootDirRec = UINT64_MAX;
1684 ISO9660DIRREC RootDir;
1685 RT_ZERO(RootDir);
1686
1687 uint64_t offJolietRootDirRec = UINT64_MAX;
1688 uint8_t bJolietUcs2Level = 0;
1689 ISO9660DIRREC JolietRootDir;
1690 RT_ZERO(JolietRootDir);
1691
1692 uint32_t cPrimaryVolDescs = 0;
1693 uint32_t cSupplementaryVolDescs = 0;
1694 uint32_t cBootRecordVolDescs = 0;
1695 uint32_t offVolDesc = 16 * cbSector;
1696 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
1697 {
1698 if (iVolDesc > 32)
1699 return RTErrInfoSet(pErrInfo, rc, "More than 32 volume descriptors, doesn't seem right...");
1700
1701 /* Read the next one and check the signature. */
1702 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
1703 if (RT_FAILURE(rc))
1704 return RTErrInfoSetF(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
1705
1706 if ( Buf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
1707 || Buf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
1708 || Buf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
1709 || Buf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
1710 || Buf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
1711 {
1712 if (!iVolDesc)
1713 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
1714 "No ISO 9660 CD001 signature, instead found: %.5Rhxs", Buf.VolDescHdr.achStdId);
1715 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Missing terminator volume descriptor?");
1716 }
1717
1718 /* Do type specific handling. */
1719 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
1720 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
1721 {
1722 cPrimaryVolDescs++;
1723 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
1724 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1725 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
1726#ifdef LOG_ENABLED
1727 rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
1728#endif
1729 if (cPrimaryVolDescs > 1)
1730 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
1731 rc = rtFsIso9660VolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
1732 }
1733 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
1734 {
1735 cSupplementaryVolDescs++;
1736 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
1737 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1738 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
1739#ifdef LOG_ENABLED
1740 rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
1741#endif
1742 rc = rtFsIso9660VolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
1743 &offJolietRootDirRec, pErrInfo);
1744 }
1745 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
1746 {
1747 cBootRecordVolDescs++;
1748 }
1749 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
1750 {
1751 if (!cPrimaryVolDescs)
1752 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
1753 break;
1754 }
1755 else
1756 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1757 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
1758 if (RT_FAILURE(rc))
1759 return rc;
1760 }
1761
1762 /*
1763 * We may be faced with choosing between joliet and rock ridge (we won't
1764 * have this choice when RTFSISO9660_F_NO_JOLIET is set). The joliet
1765 * option is generally favorable as we don't have to guess wrt to the file
1766 * name encoding. So, we'll pick that for now.
1767 *
1768 * Note! Should we change this preference for joliet, there fun wrt making sure
1769 * there really is rock ridge stuff in the primary volume as well as
1770 * making sure there really is anything of value in the primary volume.
1771 */
1772 if (bJolietUcs2Level != 0)
1773 {
1774 pThis->fIsUtf16 = true;
1775 return rtFsIso9660Dir_New(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->hVfsRootDir, &pThis->pRootDir);
1776 }
1777 return rtFsIso9660Dir_New(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->hVfsRootDir, &pThis->pRootDir);
1778}
1779
1780
1781/**
1782 * Opens an ISO 9660 file system volume.
1783 *
1784 * @returns IPRT status code.
1785 * @param hVfsFileIn The file or device backing the volume.
1786 * @param fFlags RTFSISO9660_F_XXX.
1787 * @param phVfs Where to return the virtual file system handle.
1788 * @param pErrInfo Where to return additional error information.
1789 */
1790RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
1791{
1792 /*
1793 * Quick input validation.
1794 */
1795 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
1796 *phVfs = NIL_RTVFS;
1797 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
1798
1799 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
1800 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1801
1802 /*
1803 * Create a new FAT VFS instance and try initialize it using the given input file.
1804 */
1805 RTVFS hVfs = NIL_RTVFS;
1806 void *pvThis = NULL;
1807 int rc = RTVfsNew(&g_rtFsIso9660VolOps, sizeof(RTFSISO9660VOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
1808 if (RT_SUCCESS(rc))
1809 {
1810 rc = rtFsIso9660VolTryInit((PRTFSISO9660VOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
1811 if (RT_SUCCESS(rc))
1812 *phVfs = hVfs;
1813 else
1814 RTVfsRelease(hVfs);
1815 }
1816 else
1817 RTVfsFileRelease(hVfsFileIn);
1818 return rc;
1819}
1820
1821
1822/**
1823 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
1824 */
1825static DECLCALLBACK(int) rtVfsChainIso9660Vol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
1826 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
1827{
1828 RT_NOREF(pProviderReg, pSpec);
1829
1830 /*
1831 * Basic checks.
1832 */
1833 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
1834 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
1835 if ( pElement->enmType != RTVFSOBJTYPE_VFS
1836 && pElement->enmType != RTVFSOBJTYPE_DIR)
1837 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
1838 if (pElement->cArgs > 1)
1839 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
1840
1841 /*
1842 * Parse the flag if present, save in pElement->uProvider.
1843 */
1844 uint32_t fFlags = 0;
1845 if (pElement->cArgs > 0)
1846 {
1847 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
1848 {
1849 const char *psz = pElement->paArgs[iArg].psz;
1850 if (*psz)
1851 {
1852 if (!strcmp(psz, "nojoliet"))
1853 fFlags |= RTFSISO9660_F_NO_JOLIET;
1854 else if (!strcmp(psz, "norock"))
1855 fFlags |= RTFSISO9660_F_NO_ROCK;
1856 else
1857 {
1858 *poffError = pElement->paArgs[iArg].offSpec;
1859 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
1860 }
1861 }
1862 }
1863 }
1864
1865 pElement->uProvider = fFlags;
1866 return VINF_SUCCESS;
1867}
1868
1869
1870/**
1871 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
1872 */
1873static DECLCALLBACK(int) rtVfsChainIso9660Vol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
1874 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
1875 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1876{
1877 RT_NOREF(pProviderReg, pSpec, poffError);
1878
1879 int rc;
1880 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
1881 if (hVfsFileIn != NIL_RTVFSFILE)
1882 {
1883 RTVFS hVfs;
1884 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
1885 RTVfsFileRelease(hVfsFileIn);
1886 if (RT_SUCCESS(rc))
1887 {
1888 *phVfsObj = RTVfsObjFromVfs(hVfs);
1889 RTVfsRelease(hVfs);
1890 if (*phVfsObj != NIL_RTVFSOBJ)
1891 return VINF_SUCCESS;
1892 rc = VERR_VFS_CHAIN_CAST_FAILED;
1893 }
1894 }
1895 else
1896 rc = VERR_VFS_CHAIN_CAST_FAILED;
1897 return rc;
1898}
1899
1900
1901/**
1902 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
1903 */
1904static DECLCALLBACK(bool) rtVfsChainIso9660Vol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
1905 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
1906 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
1907{
1908 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
1909 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
1910 || !pReuseElement->paArgs[0].uProvider)
1911 return true;
1912 return false;
1913}
1914
1915
1916/** VFS chain element 'file'. */
1917static RTVFSCHAINELEMENTREG g_rtVfsChainIso9660VolReg =
1918{
1919 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1920 /* fReserved = */ 0,
1921 /* pszName = */ "isofs",
1922 /* ListEntry = */ { NULL, NULL },
1923 /* pszHelp = */ "Open a ISO 9660 file system, requires a file object on the left side.\n"
1924 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
1925 "The 'norock' option make it ignore any rock ridge info.\n",
1926 /* pfnValidate = */ rtVfsChainIso9660Vol_Validate,
1927 /* pfnInstantiate = */ rtVfsChainIso9660Vol_Instantiate,
1928 /* pfnCanReuseElement = */ rtVfsChainIso9660Vol_CanReuseElement,
1929 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1930};
1931
1932RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIso9660VolReg, rtVfsChainIso9660VolReg);
1933
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