VirtualBox

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

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

isomaker: Rock updates

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