VirtualBox

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

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

iso9660vfs.cpp: Implemented support for multi-extent files (really large files).

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