VirtualBox

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

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

iso9660: build fixes

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

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