VirtualBox

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

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

IPRT: More ISO maker code.

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