VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/isovfs.cpp@ 68509

Last change on this file since 68509 was 68509, checked in by vboxsync, 7 years ago

isovfs.cpp: Started on UDF recognition sequence.

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

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