VirtualBox

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

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

IPRT: Added RTStrToUtf16BigEx and RTStrToUtf16Big for turning UTF-8 into UTF-16BE (big endian).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.3 KB
Line 
1/* $Id: iso9660vfs.cpp 67334 2017-06-12 09:48:11Z 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) + pDirRec->cExtAttrBlocks) * (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 * Looks up the shared structure for a child.
691 *
692 * @returns Referenced pointer to the shared structure, NULL if not found.
693 * @param pThis The directory.
694 * @param offDirRec The directory record offset of the child.
695 */
696static PRTFSISO9660CORE rtFsIso9660Dir_LookupShared(PRTFSISO9660DIRSHRD pThis, uint64_t offDirRec)
697{
698 PRTFSISO9660CORE pCur;
699 RTListForEach(&pThis->OpenChildren, pCur, RTFSISO9660CORE, Entry)
700 {
701 if (pCur->offDirRec == offDirRec)
702 {
703 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
704 Assert(cRefs > 1); RT_NOREF(cRefs);
705 return pCur;
706 }
707 }
708 return NULL;
709}
710
711
712/**
713 * Locates a directory entry in a directory.
714 *
715 * @returns IPRT status code.
716 * @retval VERR_FILE_NOT_FOUND if not found.
717 * @param pThis The directory to search.
718 * @param pszEntry The entry to look for.
719 * @param poffDirRec Where to return the offset of the directory record
720 * on the disk.
721 * @param ppDirRec Where to return the pointer to the directory record
722 * (the whole directory is buffered).
723 * @param pcDirRecs Where to return the number of directory records
724 * related to this entry.
725 * @param pfMode Where to return the file type, rock ridge adjusted.
726 */
727static int rtFsIso9660Dir_FindEntry(PRTFSISO9660DIRSHRD pThis, const char *pszEntry,
728 uint64_t *poffDirRec, PCISO9660DIRREC *ppDirRec, uint32_t *pcDirRecs, PRTFMODE pfMode)
729{
730 /* Set return values. */
731 *poffDirRec = UINT64_MAX;
732 *ppDirRec = NULL;
733 *pcDirRecs = 1;
734 *pfMode = UINT32_MAX;
735
736 /*
737 * If we're in UTF-16BE mode, convert the input name to UTF-16BE. Otherwise try
738 * uppercase it into a ISO 9660 compliant name.
739 */
740 int rc;
741 bool const fIsUtf16 = pThis->Core.pVol->fIsUtf16;
742 size_t cwcEntry = 0;
743 size_t cbEntry = 0;
744 size_t cchUpper = ~(size_t)0;
745 union
746 {
747 RTUTF16 wszEntry[260 + 1];
748 struct
749 {
750 char szUpper[255 + 1];
751 char szRock[260 + 1];
752 } s;
753 } uBuf;
754 if (fIsUtf16)
755 {
756 PRTUTF16 pwszEntry = uBuf.wszEntry;
757 rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
758 if (RT_FAILURE(rc))
759 return rc;
760 cbEntry = cwcEntry * 2;
761 }
762 else
763 {
764 rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
765 if (RT_SUCCESS(rc))
766 {
767 RTStrToUpper(uBuf.s.szUpper);
768 cchUpper = strlen(uBuf.s.szUpper);
769 }
770 }
771
772 /*
773 * Scan the directory buffer by buffer.
774 */
775 uint32_t offEntryInDir = 0;
776 uint32_t const cbDir = pThis->Core.cbObject;
777 while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
778 {
779 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
780
781 /* If null length, skip to the next sector. */
782 if (pDirRec->cbDirRec == 0)
783 offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
784 else
785 {
786 /* Try match the filename. */
787 /** @todo deal with multi-extend filenames, or however that works... */
788 if (fIsUtf16)
789 {
790 if ( ((size_t)pDirRec->bFileIdLength - cbEntry) > (size_t)12 /* ;12345 */
791 || RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, uBuf.wszEntry, cwcEntry) != 0
792 || ( (size_t)pDirRec->bFileIdLength != cbEntry
793 && RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';') )
794 {
795 /* Advance */
796 offEntryInDir += pDirRec->cbDirRec;
797 continue;
798 }
799 }
800 else
801 {
802 if ( ((size_t)pDirRec->bFileIdLength - cchUpper) > (size_t)6 /* ;12345 */
803 || memcmp(pDirRec->achFileId, uBuf.s.szUpper, cchUpper) != 0
804 || ( (size_t)pDirRec->bFileIdLength != cchUpper
805 && pDirRec->achFileId[cchUpper] != ';') )
806 {
807 /** @todo check rock. */
808 if (1)
809 {
810 /* Advance */
811 offEntryInDir += pDirRec->cbDirRec;
812 continue;
813 }
814 }
815 }
816
817 *poffDirRec = pThis->Core.FirstExtent.offDisk + offEntryInDir;
818 *ppDirRec = pDirRec;
819 *pcDirRecs = 1;
820 *pfMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE;
821 return VINF_SUCCESS;
822
823 }
824 }
825
826 return VERR_FILE_NOT_FOUND;
827}
828
829
830/**
831 * Releases a reference to a shared directory structure.
832 *
833 * @param pShared The shared directory structure.
834 */
835static void rtFsIso9660DirShrd_Release(PRTFSISO9660DIRSHRD pShared)
836{
837 uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
838 Assert(cRefs < UINT32_MAX / 2);
839 if (cRefs == 0)
840 {
841 LogFlow(("rtFsIso9660DirShrd_Release: Destroying shared structure %p\n", pShared));
842 Assert(pShared->Core.cRefs == 0);
843 if (pShared->pbDir)
844 {
845 RTMemFree(pShared->pbDir);
846 pShared->pbDir = NULL;
847 }
848 rtFsIso9660Core_Destroy(&pShared->Core);
849 RTMemFree(pShared);
850 }
851}
852
853
854/**
855 * Retains a reference to a shared directory structure.
856 *
857 * @param pShared The shared directory structure.
858 */
859static void rtFsIso9660DirShrd_Retain(PRTFSISO9660DIRSHRD pShared)
860{
861 uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
862 Assert(cRefs > 1); NOREF(cRefs);
863}
864
865
866
867/**
868 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
869 */
870static DECLCALLBACK(int) rtFsIso9660Dir_Close(void *pvThis)
871{
872 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
873 LogFlow(("rtFsIso9660Dir_Close(%p/%p)\n", pThis, pThis->pShared));
874
875 PRTFSISO9660DIRSHRD pShared = pThis->pShared;
876 pThis->pShared = NULL;
877 if (pShared)
878 rtFsIso9660DirShrd_Release(pShared);
879 return VINF_SUCCESS;
880}
881
882
883/**
884 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
885 */
886static DECLCALLBACK(int) rtFsIso9660Dir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
887{
888 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
889 return rtFsIso9660Core_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
890}
891
892
893/**
894 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
895 */
896static DECLCALLBACK(int) rtFsIso9660Dir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
897{
898 RT_NOREF(pvThis, fMode, fMask);
899 return VERR_WRITE_PROTECT;
900}
901
902
903/**
904 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
905 */
906static DECLCALLBACK(int) rtFsIso9660Dir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
907 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
908{
909 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
910 return VERR_WRITE_PROTECT;
911}
912
913
914/**
915 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
916 */
917static DECLCALLBACK(int) rtFsIso9660Dir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
918{
919 RT_NOREF(pvThis, uid, gid);
920 return VERR_WRITE_PROTECT;
921}
922
923
924/**
925 * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
926 */
927static DECLCALLBACK(int) rtFsIso9660Dir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
928 PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
929{
930 /*
931 * We may have symbolic links if rock ridge is being used, though currently
932 * we won't have nested mounts.
933 */
934 int rc;
935 if (phVfsMounted)
936 *phVfsMounted = NIL_RTVFS;
937 if (phVfsDir || phVfsSymlink)
938 {
939 if (phVfsSymlink)
940 *phVfsSymlink = NIL_RTVFSSYMLINK;
941 if (phVfsDir)
942 *phVfsDir = NIL_RTVFSDIR;
943
944 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
945 PRTFSISO9660DIRSHRD pShared = pThis->pShared;
946 PCISO9660DIRREC pDirRec;
947 uint64_t offDirRec;
948 uint32_t cDirRecs;
949 RTFMODE fMode;
950 rc = rtFsIso9660Dir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode);
951 Log2(("rtFsIso9660Dir_TraversalOpen: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
952 if (RT_SUCCESS(rc))
953 {
954 switch (fMode & RTFS_TYPE_MASK)
955 {
956 case RTFS_TYPE_DIRECTORY:
957 if (phVfsDir)
958 rc = rtFsIso9660Dir_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
959 else
960 rc = VERR_NOT_SYMLINK;
961 break;
962
963 case RTFS_TYPE_SYMLINK:
964 rc = VERR_NOT_IMPLEMENTED;
965 break;
966 case RTFS_TYPE_FILE:
967 case RTFS_TYPE_DEV_BLOCK:
968 case RTFS_TYPE_DEV_CHAR:
969 case RTFS_TYPE_FIFO:
970 case RTFS_TYPE_SOCKET:
971 rc = VERR_NOT_A_DIRECTORY;
972 break;
973 default:
974 case RTFS_TYPE_WHITEOUT:
975 rc = VERR_PATH_NOT_FOUND;
976 break;
977 }
978 }
979 else if (rc == VERR_FILE_NOT_FOUND)
980 rc = VERR_PATH_NOT_FOUND;
981 }
982 else
983 rc = VERR_PATH_NOT_FOUND;
984 return rc;
985}
986
987
988/**
989 * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
990 */
991static DECLCALLBACK(int) rtFsIso9660Dir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
992{
993 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
994 PRTFSISO9660DIRSHRD pShared = pThis->pShared;
995
996 /*
997 * We cannot create or replace anything, just open stuff.
998 */
999 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
1000 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
1001 return VERR_WRITE_PROTECT;
1002
1003 /*
1004 * Try open file.
1005 */
1006 PCISO9660DIRREC pDirRec;
1007 uint64_t offDirRec;
1008 uint32_t cDirRecs;
1009 RTFMODE fMode;
1010 int rc = rtFsIso9660Dir_FindEntry(pShared, pszFilename, &offDirRec, &pDirRec, &cDirRecs, &fMode);
1011 Log2(("rtFsIso9660Dir_OpenFile: FindEntry(,%s,) -> %Rrc\n", pszFilename, rc));
1012 if (RT_SUCCESS(rc))
1013 {
1014 switch (fMode & RTFS_TYPE_MASK)
1015 {
1016 case RTFS_TYPE_FILE:
1017 rc = rtFsIso9660File_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, fOpen, phVfsFile);
1018 break;
1019
1020 case RTFS_TYPE_SYMLINK:
1021 case RTFS_TYPE_DEV_BLOCK:
1022 case RTFS_TYPE_DEV_CHAR:
1023 case RTFS_TYPE_FIFO:
1024 case RTFS_TYPE_SOCKET:
1025 case RTFS_TYPE_WHITEOUT:
1026 rc = VERR_NOT_IMPLEMENTED;
1027 break;
1028
1029 case RTFS_TYPE_DIRECTORY:
1030 rc = VERR_NOT_A_FILE;
1031 break;
1032
1033 default:
1034 rc = VERR_PATH_NOT_FOUND;
1035 break;
1036 }
1037 }
1038 return rc;
1039}
1040
1041
1042/**
1043 * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
1044 */
1045static DECLCALLBACK(int) rtFsIso9660Dir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
1046{
1047 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
1048 PRTFSISO9660DIRSHRD pShared = pThis->pShared;
1049 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
1050
1051 /*
1052 * Try open file.
1053 */
1054 PCISO9660DIRREC pDirRec;
1055 uint64_t offDirRec;
1056 uint32_t cDirRecs;
1057 RTFMODE fMode;
1058 int rc = rtFsIso9660Dir_FindEntry(pShared, pszSubDir, &offDirRec, &pDirRec, &cDirRecs, &fMode);
1059 Log2(("rtFsIso9660Dir_OpenDir: FindEntry(,%s,) -> %Rrc\n", pszSubDir, rc));
1060 if (RT_SUCCESS(rc))
1061 {
1062 switch (fMode & RTFS_TYPE_MASK)
1063 {
1064 case RTFS_TYPE_DIRECTORY:
1065 rc = rtFsIso9660Dir_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
1066 break;
1067
1068 case RTFS_TYPE_FILE:
1069 case RTFS_TYPE_SYMLINK:
1070 case RTFS_TYPE_DEV_BLOCK:
1071 case RTFS_TYPE_DEV_CHAR:
1072 case RTFS_TYPE_FIFO:
1073 case RTFS_TYPE_SOCKET:
1074 case RTFS_TYPE_WHITEOUT:
1075 rc = VERR_NOT_A_DIRECTORY;
1076 break;
1077
1078 default:
1079 rc = VERR_PATH_NOT_FOUND;
1080 break;
1081 }
1082 }
1083 return rc;
1084}
1085
1086
1087/**
1088 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
1089 */
1090static DECLCALLBACK(int) rtFsIso9660Dir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
1091{
1092 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
1093 return VERR_WRITE_PROTECT;
1094}
1095
1096
1097/**
1098 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
1099 */
1100static DECLCALLBACK(int) rtFsIso9660Dir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
1101{
1102 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
1103RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
1104 return VERR_NOT_SUPPORTED;
1105}
1106
1107
1108/**
1109 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
1110 */
1111static DECLCALLBACK(int) rtFsIso9660Dir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
1112 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
1113{
1114 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
1115 return VERR_WRITE_PROTECT;
1116}
1117
1118
1119/**
1120 * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
1121 */
1122static DECLCALLBACK(int) rtFsIso9660Dir_QueryEntryInfo(void *pvThis, const char *pszEntry,
1123 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1124{
1125 /*
1126 * Try locate the entry.
1127 */
1128 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
1129 PRTFSISO9660DIRSHRD pShared = pThis->pShared;
1130 PCISO9660DIRREC pDirRec;
1131 uint64_t offDirRec;
1132 uint32_t cDirRecs;
1133 RTFMODE fMode;
1134 int rc = rtFsIso9660Dir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode);
1135 Log2(("rtFsIso9660Dir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
1136 if (RT_SUCCESS(rc))
1137 {
1138 /*
1139 * To avoid duplicating code in rtFsIso9660Core_InitFromDirRec and
1140 * rtFsIso9660Core_QueryInfo, we create a dummy RTFSISO9660CORE on the stack.
1141 */
1142 RTFSISO9660CORE TmpObj;
1143 RT_ZERO(TmpObj);
1144 rtFsIso9660Core_InitFromDirRec(&TmpObj, pDirRec, cDirRecs, offDirRec, pShared->Core.pVol);
1145 rc = rtFsIso9660Core_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
1146 }
1147 return rc;
1148}
1149
1150
1151/**
1152 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
1153 */
1154static DECLCALLBACK(int) rtFsIso9660Dir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
1155{
1156 RT_NOREF(pvThis, pszEntry, fType);
1157 return VERR_WRITE_PROTECT;
1158}
1159
1160
1161/**
1162 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
1163 */
1164static DECLCALLBACK(int) rtFsIso9660Dir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
1165{
1166 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
1167 return VERR_WRITE_PROTECT;
1168}
1169
1170
1171/**
1172 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
1173 */
1174static DECLCALLBACK(int) rtFsIso9660Dir_RewindDir(void *pvThis)
1175{
1176 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
1177 pThis->offDir = 0;
1178 return VINF_SUCCESS;
1179}
1180
1181
1182/**
1183 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
1184 */
1185static DECLCALLBACK(int) rtFsIso9660Dir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
1186 RTFSOBJATTRADD enmAddAttr)
1187{
1188 PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
1189 PRTFSISO9660DIRSHRD pShared = pThis->pShared;
1190
1191 while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
1192 {
1193 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
1194
1195 /* If null length, skip to the next sector. */
1196 if (pDirRec->cbDirRec == 0)
1197 pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
1198 else
1199 {
1200 /*
1201 * Do names first as they may cause overflows.
1202 */
1203 if ( pDirRec->bFileIdLength == 1
1204 && pDirRec->achFileId[0] == '\0')
1205 {
1206 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
1207 {
1208 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
1209 return VERR_BUFFER_OVERFLOW;
1210 }
1211 pDirEntry->cbName = 1;
1212 pDirEntry->szName[0] = '.';
1213 pDirEntry->szName[1] = '\0';
1214 }
1215 else if ( pDirRec->bFileIdLength == 1
1216 && pDirRec->achFileId[0] == '\1')
1217 {
1218 if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
1219 {
1220 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
1221 return VERR_BUFFER_OVERFLOW;
1222 }
1223 pDirEntry->cbName = 2;
1224 pDirEntry->szName[0] = '.';
1225 pDirEntry->szName[1] = '.';
1226 pDirEntry->szName[2] = '\0';
1227 }
1228 else if (pShared->Core.pVol->fIsUtf16)
1229 {
1230 /* UTF-16BE -> UTF-8 conversion is a bit tedious since we don't have the necessary IPRT APIs yet. */
1231 PCRTUTF16 pwcSrc = (PCRTUTF16)&pDirRec->achFileId[0];
1232 PCRTUTF16 pwcSrcEnd = &pwcSrc[pDirRec->bFileIdLength / sizeof(RTUTF16)];
1233 size_t cbDst = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
1234 size_t offDst = 0;
1235 while ((uintptr_t)pwcSrc < (uintptr_t)pwcSrcEnd)
1236 {
1237 RTUNICP uc;
1238 int rc = RTUtf16BigGetCpEx(&pwcSrc, &uc);
1239 if (RT_FAILURE(rc))
1240 uc = '?';
1241 else if (!uc)
1242 break;
1243 size_t cchCp = RTStrCpSize(uc);
1244 if (offDst + cchCp < cbDst)
1245 {
1246 RTStrPutCp(&pDirEntry->szName[offDst], uc);
1247 offDst += cchCp;
1248 }
1249 else
1250 {
1251 /* Buffer overflow. Figure out how much we really need. */
1252 offDst += cchCp;
1253 while ((uintptr_t)pwcSrc < (uintptr_t)pwcSrcEnd)
1254 {
1255 RTUtf16BigGetCpEx(&pwcSrc, &uc);
1256 offDst += RTStrCpSize(uc);
1257 }
1258 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + offDst + 1;
1259 return VERR_BUFFER_OVERFLOW;
1260 }
1261 }
1262 pDirEntry->szName[offDst] = '\0';
1263 pDirEntry->cbName = (uint16_t)offDst;
1264 }
1265 else
1266 {
1267 /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
1268 size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + pDirRec->bFileIdLength + 1;
1269 if (*pcbDirEntry < cbNeeded)
1270 {
1271 *pcbDirEntry = cbNeeded;
1272 return VERR_BUFFER_OVERFLOW;
1273 }
1274 pDirEntry->cbName = pDirRec->bFileIdLength;
1275 memcpy(pDirEntry->szName, pDirRec->achFileId, pDirRec->bFileIdLength);
1276 RTStrPurgeEncoding(pDirEntry->szName);
1277
1278 /** @todo check for rock ridge names here. */
1279 }
1280 pDirEntry->cwcShortName = 0;
1281 pDirEntry->wszShortName[0] = '\0';
1282
1283 /*
1284 * To avoid duplicating code in rtFsIso9660Core_InitFromDirRec and
1285 * rtFsIso9660Core_QueryInfo, we create a dummy RTFSISO9660CORE on the stack.
1286 */
1287 RTFSISO9660CORE TmpObj;
1288 RT_ZERO(TmpObj);
1289 rtFsIso9660Core_InitFromDirRec(&TmpObj, pDirRec, 1/*cDirRecs*/, /** @todo multi-extent stuff */
1290 pThis->offDir + pShared->Core.FirstExtent.offDisk, pShared->Core.pVol);
1291 int rc = rtFsIso9660Core_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
1292
1293 /*
1294 * Update the location and return.
1295 */
1296 pThis->offDir += pDirRec->cbDirRec;
1297 return rc;
1298 }
1299 }
1300
1301 return VERR_NO_MORE_FILES;
1302}
1303
1304
1305/**
1306 * FAT file operations.
1307 */
1308static const RTVFSDIROPS g_rtFsIso9660DirOps =
1309{
1310 { /* Obj */
1311 RTVFSOBJOPS_VERSION,
1312 RTVFSOBJTYPE_DIR,
1313 "ISO 9660 Dir",
1314 rtFsIso9660Dir_Close,
1315 rtFsIso9660Dir_QueryInfo,
1316 RTVFSOBJOPS_VERSION
1317 },
1318 RTVFSDIROPS_VERSION,
1319 0,
1320 { /* ObjSet */
1321 RTVFSOBJSETOPS_VERSION,
1322 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
1323 rtFsIso9660Dir_SetMode,
1324 rtFsIso9660Dir_SetTimes,
1325 rtFsIso9660Dir_SetOwner,
1326 RTVFSOBJSETOPS_VERSION
1327 },
1328 rtFsIso9660Dir_TraversalOpen,
1329 rtFsIso9660Dir_OpenFile,
1330 rtFsIso9660Dir_OpenDir,
1331 rtFsIso9660Dir_CreateDir,
1332 rtFsIso9660Dir_OpenSymlink,
1333 rtFsIso9660Dir_CreateSymlink,
1334 rtFsIso9660Dir_QueryEntryInfo,
1335 rtFsIso9660Dir_UnlinkEntry,
1336 rtFsIso9660Dir_RenameEntry,
1337 rtFsIso9660Dir_RewindDir,
1338 rtFsIso9660Dir_ReadDir,
1339 RTVFSDIROPS_VERSION,
1340};
1341
1342
1343/**
1344 * Adds an open child to the parent directory's shared structure.
1345 *
1346 * Maintains an additional reference to the parent dir to prevent it from going
1347 * away. If @a pDir is the root directory, it also ensures the volume is
1348 * referenced and sticks around until the last open object is gone.
1349 *
1350 * @param pDir The directory.
1351 * @param pChild The child being opened.
1352 * @sa rtFsIso9660DirShrd_RemoveOpenChild
1353 */
1354static void rtFsIso9660DirShrd_AddOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild)
1355{
1356 rtFsIso9660DirShrd_Retain(pDir);
1357
1358 RTListAppend(&pDir->OpenChildren, &pChild->Entry);
1359 pChild->pParentDir = pDir;
1360}
1361
1362
1363/**
1364 * Removes an open child to the parent directory.
1365 *
1366 * @param pDir The directory.
1367 * @param pChild The child being removed.
1368 *
1369 * @remarks This is the very last thing you do as it may cause a few other
1370 * objects to be released recursively (parent dir and the volume).
1371 *
1372 * @sa rtFsIso9660DirShrd_AddOpenChild
1373 */
1374static void rtFsIso9660DirShrd_RemoveOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild)
1375{
1376 AssertReturnVoid(pChild->pParentDir == pDir);
1377 RTListNodeRemove(&pChild->Entry);
1378 pChild->pParentDir = NULL;
1379
1380 rtFsIso9660DirShrd_Release(pDir);
1381}
1382
1383
1384#ifdef LOG_ENABLED
1385/**
1386 * Logs the content of a directory.
1387 */
1388static void rtFsIso9660DirShrd_LogContent(PRTFSISO9660DIRSHRD pThis)
1389{
1390 if (LogIs2Enabled())
1391 {
1392 uint32_t offRec = 0;
1393 while (offRec < pThis->cbDir)
1394 {
1395 PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
1396 if (pDirRec->cbDirRec == 0)
1397 break;
1398
1399 RTUTF16 wszName[128];
1400 if (pThis->Core.pVol->fIsUtf16)
1401 {
1402 PRTUTF16 pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
1403 PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
1404 pwszSrc--;
1405 *pwszDst-- = '\0';
1406 while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
1407 {
1408 *pwszDst = RT_BE2H_U16(*pwszSrc);
1409 pwszDst--;
1410 pwszSrc--;
1411 }
1412 }
1413 else
1414 {
1415 PRTUTF16 pwszDst = wszName;
1416 for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
1417 *pwszDst++ = pDirRec->achFileId[off];
1418 *pwszDst = '\0';
1419 }
1420
1421 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",
1422 offRec,
1423 pDirRec->cbDirRec,
1424 pDirRec->cExtAttrBlocks,
1425 ISO9660_GET_ENDIAN(&pDirRec->cbData),
1426 ISO9660_GET_ENDIAN(&pDirRec->offExtent),
1427 pDirRec->fFileFlags,
1428 pDirRec->RecTime.bYear + 1900,
1429 pDirRec->RecTime.bMonth,
1430 pDirRec->RecTime.bDay,
1431 pDirRec->RecTime.bHour,
1432 pDirRec->RecTime.bMinute,
1433 pDirRec->RecTime.bSecond,
1434 pDirRec->RecTime.offUtc*4/60,
1435 pDirRec->bFileUnitSize,
1436 pDirRec->bInterleaveGapSize,
1437 ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
1438 wszName));
1439
1440 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
1441 if (offSysUse < pDirRec->cbDirRec)
1442 {
1443 Log2(("ISO9660: system use:\n%.*Rhxd\n", pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
1444 }
1445
1446 /* advance */
1447 offRec += pDirRec->cbDirRec;
1448 }
1449 }
1450}
1451#endif /* LOG_ENABLED */
1452
1453
1454/**
1455 * Instantiates a new shared directory structure.
1456 *
1457 * @returns IPRT status code.
1458 * @param pThis The FAT volume instance.
1459 * @param pParentDir The parent directory. This is NULL for the root
1460 * directory.
1461 * @param pDirRec The directory record.
1462 * @param cDirRecs Number of directory records if more than one.
1463 * @param offDirRec The byte offset of the directory record.
1464 * @param ppShared Where to return the shared directory structure.
1465 */
1466static int rtFsIso9660DirShrd_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
1467 uint32_t cDirRecs, uint64_t offDirRec, PRTFSISO9660DIRSHRD *ppShared)
1468{
1469 /*
1470 * Allocate a new structure and initialize it.
1471 */
1472 int rc = VERR_NO_MEMORY;
1473 PRTFSISO9660DIRSHRD pShared = (PRTFSISO9660DIRSHRD)RTMemAllocZ(sizeof(*pShared));
1474 if (pShared)
1475 {
1476 rtFsIso9660Core_InitFromDirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, pThis);
1477 RTListInit(&pShared->OpenChildren);
1478 pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
1479 pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
1480 if (pShared->pbDir)
1481 {
1482 rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.offDisk, pShared->pbDir, pShared->cbDir, NULL);
1483 if (RT_SUCCESS(rc))
1484 {
1485#ifdef LOG_ENABLED
1486 rtFsIso9660DirShrd_LogContent(pShared);
1487#endif
1488
1489 /*
1490 * Link into parent directory so we can use it to update
1491 * our directory entry.
1492 */
1493 if (pParentDir)
1494 rtFsIso9660DirShrd_AddOpenChild(pParentDir, &pShared->Core);
1495 *ppShared = pShared;
1496 return VINF_SUCCESS;
1497 }
1498 }
1499 }
1500 *ppShared = NULL;
1501 return rc;
1502}
1503
1504
1505/**
1506 * Instantiates a new directory with a shared structure presupplied.
1507 *
1508 * @returns IPRT status code.
1509 * @param pThis The FAT volume instance.
1510 * @param pShared Referenced pointer to the shared structure. The
1511 * reference is always CONSUMED.
1512 * @param phVfsDir Where to return the directory handle.
1513 */
1514static int rtFsIso9660Dir_NewWithShared(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pShared, PRTVFSDIR phVfsDir)
1515{
1516 /*
1517 * Create VFS object around the shared structure.
1518 */
1519 PRTFSISO9660DIROBJ pNewDir;
1520 int rc = RTVfsNewDir(&g_rtFsIso9660DirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
1521 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
1522 if (RT_SUCCESS(rc))
1523 {
1524 /*
1525 * Look for existing shared object, create a new one if necessary.
1526 * We CONSUME a reference to pShared here.
1527 */
1528 pNewDir->offDir = 0;
1529 pNewDir->pShared = pShared;
1530 return VINF_SUCCESS;
1531 }
1532
1533 rtFsIso9660DirShrd_Release(pShared);
1534 *phVfsDir = NIL_RTVFSDIR;
1535 return rc;
1536}
1537
1538
1539
1540/**
1541 * Instantiates a new directory VFS instance, creating the shared structure as
1542 * necessary.
1543 *
1544 * @returns IPRT status code.
1545 * @param pThis The FAT volume instance.
1546 * @param pParentDir The parent directory. This is NULL for the root
1547 * directory.
1548 * @param pDirRec The directory record.
1549 * @param cDirRecs Number of directory records if more than one.
1550 * @param offDirRec The byte offset of the directory record.
1551 * @param phVfsDir Where to return the directory handle.
1552 */
1553static int rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
1554 uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir)
1555{
1556 /*
1557 * Look for existing shared object, create a new one if necessary.
1558 */
1559 int rc = VINF_SUCCESS;
1560 PRTFSISO9660DIRSHRD pShared = (PRTFSISO9660DIRSHRD)rtFsIso9660Dir_LookupShared(pParentDir, offDirRec);
1561 if (!pShared)
1562 {
1563 rc = rtFsIso9660DirShrd_New(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, &pShared);
1564 if (RT_FAILURE(rc))
1565 {
1566 *phVfsDir = NIL_RTVFSDIR;
1567 return rc;
1568 }
1569 }
1570 return rtFsIso9660Dir_NewWithShared(pThis, pShared, phVfsDir);
1571}
1572
1573
1574
1575/**
1576 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1577 */
1578static DECLCALLBACK(int) rtFsIso9660Vol_Close(void *pvThis)
1579{
1580 PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
1581 Log(("rtFsIso9660Vol_Close(%p)\n", pThis));
1582
1583 if (pThis->pRootDir)
1584 {
1585 Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
1586 Assert(pThis->pRootDir->Core.cRefs == 1);
1587 rtFsIso9660DirShrd_Release(pThis->pRootDir);
1588 pThis->pRootDir = NULL;
1589 }
1590
1591 RTVfsFileRelease(pThis->hVfsBacking);
1592 pThis->hVfsBacking = NIL_RTVFSFILE;
1593
1594 return VINF_SUCCESS;
1595}
1596
1597
1598/**
1599 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1600 */
1601static DECLCALLBACK(int) rtFsIso9660Vol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1602{
1603 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
1604 return VERR_WRONG_TYPE;
1605}
1606
1607
1608/**
1609 * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
1610 */
1611static DECLCALLBACK(int) rtFsIso9660Vol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
1612{
1613 PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
1614
1615 rtFsIso9660DirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
1616 return rtFsIso9660Dir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
1617}
1618
1619
1620/**
1621 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
1622 */
1623static DECLCALLBACK(int) rtFsIso9660Vol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
1624{
1625 RT_NOREF(pvThis, off, cb, pfUsed);
1626 return VERR_NOT_IMPLEMENTED;
1627}
1628
1629
1630DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIso9660VolOps =
1631{
1632 { /* Obj */
1633 RTVFSOBJOPS_VERSION,
1634 RTVFSOBJTYPE_VFS,
1635 "ISO 9660",
1636 rtFsIso9660Vol_Close,
1637 rtFsIso9660Vol_QueryInfo,
1638 RTVFSOBJOPS_VERSION
1639 },
1640 RTVFSOPS_VERSION,
1641 0 /* fFeatures */,
1642 rtFsIso9660Vol_OpenRoot,
1643 rtFsIso9660Vol_IsRangeInUse,
1644 RTVFSOPS_VERSION
1645};
1646
1647
1648
1649#ifdef LOG_ENABLED
1650
1651/** Logging helper. */
1652static size_t rtFsIso9660VolGetStrippedLength(const char *pachField, size_t cchField)
1653{
1654 while (cchField > 0 && pachField[cchField - 1] == ' ')
1655 cchField--;
1656 return cchField;
1657}
1658
1659
1660/**
1661 * Logs the primary or supplementary volume descriptor
1662 *
1663 * @param pVolDesc The descriptor.
1664 */
1665static void rtFsIso9660VolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
1666{
1667 if (LogIs2Enabled())
1668 {
1669 Log2(("ISO9660: fVolumeFlags: %#RX8\n", pVolDesc->fVolumeFlags));
1670 Log2(("ISO9660: achSystemId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId)), pVolDesc->achSystemId));
1671 Log2(("ISO9660: achVolumeId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId)), pVolDesc->achVolumeId));
1672 Log2(("ISO9660: Unused73: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
1673 Log2(("ISO9660: VolumeSpaceSize: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
1674 Log2(("ISO9660: abEscapeSequences: '%.*s'\n", rtFsIso9660VolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
1675 Log2(("ISO9660: cVolumesInSet: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
1676 Log2(("ISO9660: VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
1677 Log2(("ISO9660: cbLogicalBlock: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
1678 Log2(("ISO9660: cbPathTable: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
1679 Log2(("ISO9660: offTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
1680 Log2(("ISO9660: offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
1681 Log2(("ISO9660: offTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
1682 Log2(("ISO9660: offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
1683 Log2(("ISO9660: achVolumeSetId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId)), pVolDesc->achVolumeSetId));
1684 Log2(("ISO9660: achPublisherId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId)), pVolDesc->achPublisherId));
1685 Log2(("ISO9660: achDataPreparerId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId)), pVolDesc->achDataPreparerId));
1686 Log2(("ISO9660: achApplicationId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId)), pVolDesc->achApplicationId));
1687 Log2(("ISO9660: achCopyrightFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId)), pVolDesc->achCopyrightFileId));
1688 Log2(("ISO9660: achAbstractFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId)), pVolDesc->achAbstractFileId));
1689 Log2(("ISO9660: achBibliographicFileId: '%.*s'\n", rtFsIso9660VolGetStrippedLength(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId)), pVolDesc->achBibliographicFileId));
1690 Log2(("ISO9660: BirthTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1691 pVolDesc->BirthTime.achYear,
1692 pVolDesc->BirthTime.achMonth,
1693 pVolDesc->BirthTime.achDay,
1694 pVolDesc->BirthTime.achHour,
1695 pVolDesc->BirthTime.achMinute,
1696 pVolDesc->BirthTime.achSecond,
1697 pVolDesc->BirthTime.achCentisecond,
1698 pVolDesc->BirthTime.offUtc*4/60));
1699 Log2(("ISO9660: ModifyTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1700 pVolDesc->ModifyTime.achYear,
1701 pVolDesc->ModifyTime.achMonth,
1702 pVolDesc->ModifyTime.achDay,
1703 pVolDesc->ModifyTime.achHour,
1704 pVolDesc->ModifyTime.achMinute,
1705 pVolDesc->ModifyTime.achSecond,
1706 pVolDesc->ModifyTime.achCentisecond,
1707 pVolDesc->ModifyTime.offUtc*4/60));
1708 Log2(("ISO9660: ExpireTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1709 pVolDesc->ExpireTime.achYear,
1710 pVolDesc->ExpireTime.achMonth,
1711 pVolDesc->ExpireTime.achDay,
1712 pVolDesc->ExpireTime.achHour,
1713 pVolDesc->ExpireTime.achMinute,
1714 pVolDesc->ExpireTime.achSecond,
1715 pVolDesc->ExpireTime.achCentisecond,
1716 pVolDesc->ExpireTime.offUtc*4/60));
1717 Log2(("ISO9660: EffectiveTime: %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
1718 pVolDesc->EffectiveTime.achYear,
1719 pVolDesc->EffectiveTime.achMonth,
1720 pVolDesc->EffectiveTime.achDay,
1721 pVolDesc->EffectiveTime.achHour,
1722 pVolDesc->EffectiveTime.achMinute,
1723 pVolDesc->EffectiveTime.achSecond,
1724 pVolDesc->EffectiveTime.achCentisecond,
1725 pVolDesc->EffectiveTime.offUtc*4/60));
1726 Log2(("ISO9660: bFileStructureVersion: %#RX8\n", pVolDesc->bFileStructureVersion));
1727 Log2(("ISO9660: bReserved883: %#RX8\n", pVolDesc->bReserved883));
1728
1729 Log2(("ISO9660: RootDir.cbDirRec: %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
1730 Log2(("ISO9660: RootDir.cExtAttrBlocks: %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
1731 Log2(("ISO9660: RootDir.offExtent: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
1732 Log2(("ISO9660: RootDir.cbData: {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
1733 Log2(("ISO9660: RootDir.RecTime: %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
1734 pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
1735 pVolDesc->RootDir.DirRec.RecTime.bMonth,
1736 pVolDesc->RootDir.DirRec.RecTime.bDay,
1737 pVolDesc->RootDir.DirRec.RecTime.bHour,
1738 pVolDesc->RootDir.DirRec.RecTime.bMinute,
1739 pVolDesc->RootDir.DirRec.RecTime.bSecond,
1740 pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
1741 Log2(("ISO9660: RootDir.RecTime.fFileFlags: %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
1742 Log2(("ISO9660: RootDir.RecTime.bFileUnitSize: %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
1743 Log2(("ISO9660: RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
1744 Log2(("ISO9660: RootDir.RecTime.VolumeSeqNo: {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
1745 Log2(("ISO9660: RootDir.RecTime.bFileIdLength: %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
1746 Log2(("ISO9660: RootDir.RecTime.achFileId: '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
1747 uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
1748 + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
1749 if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
1750 {
1751 Log2(("ISO9660: RootDir System Use:\n%.*Rhxd\n",
1752 pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
1753 }
1754 }
1755}
1756
1757#endif /* LOG_ENABLED */
1758
1759/**
1760 * Deal with a root directory from a primary or supplemental descriptor.
1761 *
1762 * @returns IPRT status code.
1763 * @param pThis The ISO 9660 instance being initialized.
1764 * @param pRootDir The root directory record to check out.
1765 * @param pDstRootDir Where to store a copy of the root dir record.
1766 * @param pErrInfo Where to return additional error info. Can be NULL.
1767 */
1768static int rtFsIso9660VolHandleRootDir(PRTFSISO9660VOL pThis, PCISO9660DIRREC pRootDir,
1769 PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
1770{
1771 if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
1772 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
1773 pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
1774
1775 if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
1776 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1777 "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
1778 if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
1779 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1780 "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
1781
1782 if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
1783 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
1784 RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
1785 if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
1786 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
1787
1788 if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
1789 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
1790 RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
1791
1792 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
1793 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
1794 RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
1795 if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
1796 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1797 "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
1798 RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
1799
1800 /*
1801 * Seems okay, copy it.
1802 */
1803 *pDstRootDir = *pRootDir;
1804 return VINF_SUCCESS;
1805}
1806
1807
1808/**
1809 * Deal with a primary volume descriptor.
1810 *
1811 * @returns IPRT status code.
1812 * @param pThis The ISO 9660 instance being initialized.
1813 * @param pVolDesc The volume descriptor to handle.
1814 * @param offVolDesc The disk offset of the volume descriptor.
1815 * @param pRootDir Where to return a copy of the root directory record.
1816 * @param poffRootDirRec Where to return the disk offset of the root dir.
1817 * @param pErrInfo Where to return additional error info. Can be NULL.
1818 */
1819static int rtFsIso9660VolHandlePrimaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
1820 PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
1821{
1822 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1823 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1824 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1825
1826 /*
1827 * We need the block size ...
1828 */
1829 pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
1830 if ( pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
1831 || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
1832 || pThis->cbBlock / pThis->cbSector < 1)
1833 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
1834 RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
1835 if (pThis->cbBlock / pThis->cbSector > 128)
1836 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
1837
1838 /*
1839 * ... volume space size ...
1840 */
1841 pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
1842 if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
1843 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
1844 RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
1845 pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
1846
1847 /*
1848 * ... number of volumes in the set ...
1849 */
1850 pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
1851 if ( pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
1852 || pThis->cVolumesInSet == 0)
1853 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
1854 RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
1855 if (pThis->cVolumesInSet > 32)
1856 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
1857
1858 /*
1859 * ... primary volume sequence ID ...
1860 */
1861 pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
1862 if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
1863 return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
1864 RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
1865 if ( pThis->idPrimaryVol > pThis->cVolumesInSet
1866 || pThis->idPrimaryVol < 1)
1867 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1868 "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
1869
1870 /*
1871 * ... and the root directory record.
1872 */
1873 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
1874 return rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
1875}
1876
1877
1878/**
1879 * Deal with a supplementary volume descriptor.
1880 *
1881 * @returns IPRT status code.
1882 * @param pThis The ISO 9660 instance being initialized.
1883 * @param pVolDesc The volume descriptor to handle.
1884 * @param offVolDesc The disk offset of the volume descriptor.
1885 * @param pbUcs2Level Where to return the joliet level, if found. Caller
1886 * initializes this to zero, we'll return 1, 2 or 3 if
1887 * joliet was detected.
1888 * @param pRootDir Where to return the root directory, if found.
1889 * @param poffRootDirRec Where to return the disk offset of the root dir.
1890 * @param pErrInfo Where to return additional error info. Can be NULL.
1891 */
1892static int rtFsIso9660VolHandleSupplementaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
1893 uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
1894 PRTERRINFO pErrInfo)
1895{
1896 if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
1897 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1898 "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
1899
1900 /*
1901 * Is this a joliet volume descriptor? If not, we probably don't need to
1902 * care about it.
1903 */
1904 if ( pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
1905 || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
1906 || ( pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
1907 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
1908 && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
1909 return VINF_SUCCESS;
1910
1911 /*
1912 * Skip if joliet is unwanted.
1913 */
1914 if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
1915 return VINF_SUCCESS;
1916
1917 /*
1918 * Check that the joliet descriptor matches the primary one.
1919 * Note! These are our assumptions and may be wrong.
1920 */
1921 if (pThis->cbBlock == 0)
1922 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1923 "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
1924 if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
1925 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1926 "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
1927 ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
1928 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
1929 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1930 "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
1931 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
1932 if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
1933 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1934 "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
1935 ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
1936 if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
1937 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
1938 "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
1939 ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
1940
1941 if (*pbUcs2Level != 0)
1942 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
1943
1944 /*
1945 * Switch to the joliet root dir as it has UTF-16 stuff in it.
1946 */
1947 int rc = rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
1948 if (RT_SUCCESS(rc))
1949 {
1950 *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
1951 *pbUcs2Level = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
1952 : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
1953 Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
1954 }
1955 return rc;
1956}
1957
1958
1959
1960/**
1961 * Worker for RTFsIso9660VolOpen.
1962 *
1963 * @returns IPRT status code.
1964 * @param pThis The FAT VFS instance to initialize.
1965 * @param hVfsSelf The FAT VFS handle (no reference consumed).
1966 * @param hVfsBacking The file backing the alleged FAT file system.
1967 * Reference is consumed (via rtFsIso9660Vol_Close).
1968 * @param fFlags Flags, MBZ.
1969 * @param pErrInfo Where to return additional error info. Can be NULL.
1970 */
1971static int rtFsIso9660VolTryInit(PRTFSISO9660VOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
1972{
1973 uint32_t const cbSector = 2048;
1974
1975 /*
1976 * First initialize the state so that rtFsIso9660Vol_Destroy won't trip up.
1977 */
1978 pThis->hVfsSelf = hVfsSelf;
1979 pThis->hVfsBacking = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIso9660Vol_Destroy releases it. */
1980 pThis->cbBacking = 0;
1981 pThis->fFlags = fFlags;
1982 pThis->cbSector = cbSector;
1983 pThis->cbBlock = 0;
1984 pThis->cBlocksInPrimaryVolumeSpace = 0;
1985 pThis->cbPrimaryVolumeSpace = 0;
1986 pThis->cVolumesInSet = 0;
1987 pThis->idPrimaryVol = UINT32_MAX;
1988 pThis->fIsUtf16 = false;
1989 pThis->pRootDir = NULL;
1990
1991 /*
1992 * Get stuff that may fail.
1993 */
1994 int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
1995 if (RT_FAILURE(rc))
1996 return rc;
1997
1998 /*
1999 * Read the volume descriptors starting at logical sector 16.
2000 */
2001 union
2002 {
2003 uint8_t ab[_4K];
2004 uint16_t au16[_4K / 2];
2005 uint32_t au32[_4K / 4];
2006 ISO9660VOLDESCHDR VolDescHdr;
2007 ISO9660BOOTRECORD BootRecord;
2008 ISO9660PRIMARYVOLDESC PrimaryVolDesc;
2009 ISO9660SUPVOLDESC SupVolDesc;
2010 ISO9660VOLPARTDESC VolPartDesc;
2011 } Buf;
2012 RT_ZERO(Buf);
2013
2014 uint64_t offRootDirRec = UINT64_MAX;
2015 ISO9660DIRREC RootDir;
2016 RT_ZERO(RootDir);
2017
2018 uint64_t offJolietRootDirRec = UINT64_MAX;
2019 uint8_t bJolietUcs2Level = 0;
2020 ISO9660DIRREC JolietRootDir;
2021 RT_ZERO(JolietRootDir);
2022
2023 uint32_t cPrimaryVolDescs = 0;
2024 uint32_t cSupplementaryVolDescs = 0;
2025 uint32_t cBootRecordVolDescs = 0;
2026 uint32_t offVolDesc = 16 * cbSector;
2027 for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
2028 {
2029 if (iVolDesc > 32)
2030 return RTErrInfoSet(pErrInfo, rc, "More than 32 volume descriptors, doesn't seem right...");
2031
2032 /* Read the next one and check the signature. */
2033 rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
2034 if (RT_FAILURE(rc))
2035 return RTErrInfoSetF(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
2036
2037 if ( Buf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
2038 || Buf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
2039 || Buf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
2040 || Buf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
2041 || Buf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
2042 {
2043 if (!iVolDesc)
2044 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
2045 "No ISO 9660 CD001 signature, instead found: %.5Rhxs", Buf.VolDescHdr.achStdId);
2046 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Missing terminator volume descriptor?");
2047 }
2048
2049 /* Do type specific handling. */
2050 Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
2051 if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
2052 {
2053 cPrimaryVolDescs++;
2054 if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
2055 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2056 "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
2057#ifdef LOG_ENABLED
2058 rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
2059#endif
2060 if (cPrimaryVolDescs > 1)
2061 return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
2062 rc = rtFsIso9660VolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
2063 }
2064 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
2065 {
2066 cSupplementaryVolDescs++;
2067 if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
2068 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2069 "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
2070#ifdef LOG_ENABLED
2071 rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
2072#endif
2073 rc = rtFsIso9660VolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
2074 &offJolietRootDirRec, pErrInfo);
2075 }
2076 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
2077 {
2078 cBootRecordVolDescs++;
2079 }
2080 else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
2081 {
2082 if (!cPrimaryVolDescs)
2083 return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
2084 break;
2085 }
2086 else
2087 return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2088 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
2089 if (RT_FAILURE(rc))
2090 return rc;
2091 }
2092
2093 /*
2094 * We may be faced with choosing between joliet and rock ridge (we won't
2095 * have this choice when RTFSISO9660_F_NO_JOLIET is set). The joliet
2096 * option is generally favorable as we don't have to guess wrt to the file
2097 * name encoding. So, we'll pick that for now.
2098 *
2099 * Note! Should we change this preference for joliet, there fun wrt making sure
2100 * there really is rock ridge stuff in the primary volume as well as
2101 * making sure there really is anything of value in the primary volume.
2102 */
2103 if (bJolietUcs2Level != 0)
2104 {
2105 pThis->fIsUtf16 = true;
2106 return rtFsIso9660DirShrd_New(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
2107 }
2108 return rtFsIso9660DirShrd_New(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
2109}
2110
2111
2112/**
2113 * Opens an ISO 9660 file system volume.
2114 *
2115 * @returns IPRT status code.
2116 * @param hVfsFileIn The file or device backing the volume.
2117 * @param fFlags RTFSISO9660_F_XXX.
2118 * @param phVfs Where to return the virtual file system handle.
2119 * @param pErrInfo Where to return additional error information.
2120 */
2121RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
2122{
2123 /*
2124 * Quick input validation.
2125 */
2126 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
2127 *phVfs = NIL_RTVFS;
2128 AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
2129
2130 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
2131 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2132
2133 /*
2134 * Create a new FAT VFS instance and try initialize it using the given input file.
2135 */
2136 RTVFS hVfs = NIL_RTVFS;
2137 void *pvThis = NULL;
2138 int rc = RTVfsNew(&g_rtFsIso9660VolOps, sizeof(RTFSISO9660VOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
2139 if (RT_SUCCESS(rc))
2140 {
2141 rc = rtFsIso9660VolTryInit((PRTFSISO9660VOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
2142 if (RT_SUCCESS(rc))
2143 *phVfs = hVfs;
2144 else
2145 RTVfsRelease(hVfs);
2146 }
2147 else
2148 RTVfsFileRelease(hVfsFileIn);
2149 return rc;
2150}
2151
2152
2153/**
2154 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
2155 */
2156static DECLCALLBACK(int) rtVfsChainIso9660Vol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
2157 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
2158{
2159 RT_NOREF(pProviderReg, pSpec);
2160
2161 /*
2162 * Basic checks.
2163 */
2164 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
2165 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
2166 if ( pElement->enmType != RTVFSOBJTYPE_VFS
2167 && pElement->enmType != RTVFSOBJTYPE_DIR)
2168 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
2169 if (pElement->cArgs > 1)
2170 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
2171
2172 /*
2173 * Parse the flag if present, save in pElement->uProvider.
2174 */
2175 uint32_t fFlags = 0;
2176 if (pElement->cArgs > 0)
2177 {
2178 for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
2179 {
2180 const char *psz = pElement->paArgs[iArg].psz;
2181 if (*psz)
2182 {
2183 if (!strcmp(psz, "nojoliet"))
2184 fFlags |= RTFSISO9660_F_NO_JOLIET;
2185 else if (!strcmp(psz, "norock"))
2186 fFlags |= RTFSISO9660_F_NO_ROCK;
2187 else
2188 {
2189 *poffError = pElement->paArgs[iArg].offSpec;
2190 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
2191 }
2192 }
2193 }
2194 }
2195
2196 pElement->uProvider = fFlags;
2197 return VINF_SUCCESS;
2198}
2199
2200
2201/**
2202 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2203 */
2204static DECLCALLBACK(int) rtVfsChainIso9660Vol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2205 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2206 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2207{
2208 RT_NOREF(pProviderReg, pSpec, poffError);
2209
2210 int rc;
2211 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2212 if (hVfsFileIn != NIL_RTVFSFILE)
2213 {
2214 RTVFS hVfs;
2215 rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
2216 RTVfsFileRelease(hVfsFileIn);
2217 if (RT_SUCCESS(rc))
2218 {
2219 *phVfsObj = RTVfsObjFromVfs(hVfs);
2220 RTVfsRelease(hVfs);
2221 if (*phVfsObj != NIL_RTVFSOBJ)
2222 return VINF_SUCCESS;
2223 rc = VERR_VFS_CHAIN_CAST_FAILED;
2224 }
2225 }
2226 else
2227 rc = VERR_VFS_CHAIN_CAST_FAILED;
2228 return rc;
2229}
2230
2231
2232/**
2233 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2234 */
2235static DECLCALLBACK(bool) rtVfsChainIso9660Vol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2236 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2237 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2238{
2239 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2240 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2241 || !pReuseElement->paArgs[0].uProvider)
2242 return true;
2243 return false;
2244}
2245
2246
2247/** VFS chain element 'file'. */
2248static RTVFSCHAINELEMENTREG g_rtVfsChainIso9660VolReg =
2249{
2250 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2251 /* fReserved = */ 0,
2252 /* pszName = */ "isofs",
2253 /* ListEntry = */ { NULL, NULL },
2254 /* pszHelp = */ "Open a ISO 9660 file system, requires a file object on the left side.\n"
2255 "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
2256 "The 'norock' option make it ignore any rock ridge info.\n",
2257 /* pfnValidate = */ rtVfsChainIso9660Vol_Validate,
2258 /* pfnInstantiate = */ rtVfsChainIso9660Vol_Instantiate,
2259 /* pfnCanReuseElement = */ rtVfsChainIso9660Vol_CanReuseElement,
2260 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2261};
2262
2263RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIso9660VolReg, rtVfsChainIso9660VolReg);
2264
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