W.I.P AROS port of SDI examples
[AROS.git] / rom / filesys / cdfs / iso9660.c
blob9fd383d5dc4a97cdc5d15f5ffc178326cd003698
1 /*
2 * Copyright (C) 2013, The AROS Development Team
3 * All right reserved.
4 * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
6 * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1
7 */
9 #include <string.h>
10 #include <endian.h>
12 #include <aros/debug.h>
14 #include <proto/exec.h>
15 #include <proto/dos.h>
16 #include <proto/utility.h>
18 #include "cdfs.h"
19 #include "iso9660.h"
20 #include "bcache.h"
22 struct ISOLock {
23 struct CDFSLock il_Public;
24 ULONG il_Extent;
25 UQUAD il_Offset; /* For read/write offset */
26 ULONG il_ParentExtent; /* Extent of the parent directory */
27 struct isoPathTable *il_PathTable;
30 struct ISOVolume {
31 struct isoVolume iv_Volume;
32 struct ISOLock iv_RootLock;
33 TEXT iv_Name[1 + 32 + 1];
34 struct {
35 ULONG Size; /* In bytes */
36 ULONG Extent;
37 ULONG Entries;
38 struct isoPathTable **Entry;
39 APTR Data;
40 } iv_PathTable;
43 static ULONG ascdec(STRPTR val, int len)
45 ULONG out = 0;
47 while ((*val >= '0' && *val <= '9') && len > 0) {
48 out *= 10;
49 out += *val - '9';
50 len--;
51 val++;
54 return out;
57 #define AMIGA_EPOC 722484 /* Day number of Amiga Epoc, using Zeller */
59 static void ascdate2stamp(ascDate *asc, struct DateStamp *date)
61 ULONG year = ascdec(asc->Year, 4);
62 ULONG month = ascdec(asc->Month, 2);
63 ULONG day = ascdec(asc->Day, 2);
65 if (month < 3) {
66 month += 12;
67 year--;
69 date->ds_Days = (365*year + year/4 - year/100 + year/400 + day + (153*month+8)/5) - AMIGA_EPOC;
70 date->ds_Minute = ascdec(asc->Hour, 2) * 60 +
71 ascdec(asc->Minute, 2);
72 date->ds_Tick = ascdec(asc->Second, 2) * 50 + ascdec(asc->Hundredth, 2) / 2;
74 /* FIXME: Adjust for timezone */
77 static void bindate2stamp(binDate *bin, struct DateStamp *date)
79 ULONG year = bin->Years + 1900;
80 ULONG month = bin->Month;
81 ULONG day = bin->Day;
83 if (month < 3) {
84 month += 12;
85 year--;
87 date->ds_Days = (365*year + year/4 - year/100 + year/400 + day + (153*month+8)/5) - AMIGA_EPOC;
88 date->ds_Minute = bin->Hour * 60 +
89 bin->Minute;
90 date->ds_Tick = bin->Second * 50;
92 /* FIXME: Adjust for timezone */
95 static inline ULONG isoRead32LM(int32LM *lm)
97 #if _BYTE_ORDER == _BIG_ENDIAN
98 return lm->MSB ? lm->MSB : (ULONG)AROS_LE2LONG(lm->LSB);
99 #elif _BYTE_ORDER == _LITTLE_ENDIAN
100 return lm->LSB ? lm->LSB : (ULONG)AROS_BE2LONG(lm->MSB);
101 #else /* Wacky byte order? Impossible in this day and age. */
102 #error PDP11s are no longer supported.
103 #endif
106 static BOOL isoParseDir(struct CDFSVolume *vol, struct ISOLock *il, struct isoDirectory *dir, ULONG iparent)
108 struct CDFS *cdfs = vol->cv_CDFSBase;
109 struct FileInfoBlock *fib = &il->il_Public.cl_FileInfoBlock;
110 struct FileLock *fl = &il->il_Public.cl_FileLock;
111 struct ISOVolume *iv = vol->cv_Private;
112 int i, len;
113 const strD *cp;
114 BOOL has_Amiga_Protections = FALSE;
115 BOOL has_RockRidge = FALSE;
117 if (dir) {
118 il->il_Extent = isoRead32LM(&dir->ExtentLocation);
119 il->il_PathTable = NULL;
120 } else {
121 il->il_Extent = il->il_PathTable->ExtentLocation;
122 iparent = iv->iv_PathTable.Entry[il->il_PathTable->ParentDirectory-1]->ExtentLocation;
125 D(bug("%s: \"%s\" Extent @0x%08x, Parent 0x%08x, Flags 0x%02x\n", __func__,
126 dir ? dir->FileIdentifier : il->il_PathTable->DirectoryIdentifier,
127 il->il_Extent, iparent, dir ? dir->Flags : 0));
129 if (dir && dir->Flags & (ISO_Directory_ASSOC | ISO_Directory_HIDDEN))
130 return FALSE;
132 /* No need to re-parse the root lock */
133 if (iv && il->il_Extent == iv->iv_RootLock.il_Extent) {
134 CopyMem(&iv->iv_RootLock, il, sizeof(*il));
135 return TRUE;
138 /* The starting location of the file is its unique 'key' */
139 fl->fl_Key = il->il_Extent;
141 if (dir) {
142 fib->fib_Size = isoRead32LM(&dir->DataLength);
143 bindate2stamp(&dir->RecordingDate, &fib->fib_Date);
144 fib->fib_DirEntryType = (dir->Flags & ISO_Directory_ISDIR) ? ST_USERDIR : ST_FILE;
145 len = dir->FileIdentifierLength;
146 cp = &dir->FileIdentifier[0];
147 } else {
148 fib->fib_Size = 0;
149 memset(&fib->fib_Date, 0, sizeof(fib->fib_Date));
150 fib->fib_DirEntryType = ST_USERDIR;
151 len = il->il_PathTable->DirectoryIdentifierLength;
152 cp = &il->il_PathTable->DirectoryIdentifier[0];
155 if (len > MAXFILENAMELENGTH-2)
156 len = MAXFILENAMELENGTH-2;
157 for (i = 0; i < len; i++) {
158 /* Goddam VMS guys screwing up a perfectly good specification... */
159 if (cp[i] == ';') {
160 len = i;
161 break;
163 fib->fib_FileName[i + 1] = cp[i];
165 fib->fib_FileName[0] = len;
166 fib->fib_FileName[len+1] = 0;
167 fib->fib_Protection = FIBF_DELETE |
168 FIBF_EXECUTE |
169 FIBF_WRITE |
170 FIBF_GRP_DELETE |
171 FIBF_GRP_EXECUTE |
172 FIBF_GRP_WRITE |
173 FIBF_OTR_DELETE |
174 FIBF_OTR_EXECUTE |
175 FIBF_OTR_WRITE |
176 FIBF_ARCHIVE;
177 fib->fib_EntryType = fib->fib_DirEntryType;
178 fib->fib_NumBlocks = (fib->fib_Size + 2047) / 2048;
179 fib->fib_Comment[0] = 0;
180 fib->fib_OwnerUID = 0;
181 fib->fib_OwnerGID = 0;
183 il->il_ParentExtent = iparent;
185 if (!dir)
186 goto end;
188 /* Parse RockRidge extensions */
189 i = ((UBYTE *)&dir->FileIdentifier[dir->FileIdentifierLength] -
190 (UBYTE *)dir);
191 if (i & 1)
192 i++;
193 while (i < (dir->DirectoryLength - 3)) {
194 struct rrSystemUse *rr = (APTR)(((UBYTE *)dir) + i);
195 D(bug("%s: RR @%d [%c%c] (%d)\n", __func__, i, (AROS_BE2WORD(rr->Signature) >> 8) & 0xff, AROS_BE2WORD(rr->Signature) & 0xff, rr->Length));
196 if (rr->Length == 0) {
197 D(bug("%s: Corrupted RR section detected (%d left)\n", __func__, dir->DirectoryLength - i));
198 break;
200 switch (AROS_BE2WORD(rr->Signature)) {
201 case RR_SystemUse_AS:
202 if (rr->Version == RR_SystemUse_AS_VERSION) {
203 UBYTE *data = &rr->AS.Data[0];
205 /* Amiga System Use area */
206 if (rr->AS.Flags & RR_AS_PROTECTION) {
207 struct SystemUseASProtection *ap = (APTR)data;
208 fib->fib_Protection = (ap->User << 24) |
209 (ap->Zero << 16) |
210 (ap->MultiUser << 8) |
211 (ap->Protection << 0);
212 data = (UBYTE *)&ap[1];
213 has_Amiga_Protections = TRUE;
215 if (rr->AS.Flags & RR_AS_COMMENT) {
216 struct SystemUseASComment *ac = (APTR)data;
217 int len = ac->Length;
218 if (len > (MAXCOMMENTLENGTH - fib->fib_Comment[0])-1) {
219 len = (MAXCOMMENTLENGTH - fib->fib_Comment[0])-1;
220 CopyMem(&ac->Comment[0], &fib->fib_Comment[fib->fib_Comment[0]+1], len);
221 fib->fib_Comment[0] += len;
225 break;
226 case RR_SystemUse_PX:
227 /* This field is mandatory for Rock Ridge */
228 has_RockRidge = TRUE;
230 /* Don't overwrite the Amiga RR protections */
231 if (has_Amiga_Protections)
232 break;
233 if (rr->Version == RR_SystemUse_PX_VERSION) {
234 ULONG prot = 0;
235 ULONG mode = isoRead32LM(&rr->PX.st_mode);
236 prot |= (mode & S_IXOTH) ? FIBF_OTR_EXECUTE : 0;
237 prot |= (mode & S_IWOTH) ? (FIBF_OTR_WRITE | FIBF_OTR_DELETE): 0;
238 prot |= (mode & S_IROTH) ? FIBF_OTR_READ : 0;
239 prot |= (mode & S_IXGRP) ? FIBF_GRP_EXECUTE : 0;
240 prot |= (mode & S_IWGRP) ? (FIBF_GRP_WRITE | FIBF_GRP_DELETE) : 0;
241 prot |= (mode & S_IRGRP) ? FIBF_GRP_READ : 0;
242 prot |= (mode & S_IXUSR) ? 0 : FIBF_EXECUTE;
243 prot |= (mode & S_IWUSR) ? 0 : (FIBF_WRITE | FIBF_DELETE);
244 prot |= (mode & S_IRUSR) ? 0 : FIBF_READ;
246 fib->fib_Protection = prot;
247 fib->fib_OwnerUID = isoRead32LM(&rr->PX.st_uid);
248 fib->fib_OwnerGID = isoRead32LM(&rr->PX.st_gid);
250 break;
251 case RR_SystemUse_NM:
252 if (rr->Version == RR_SystemUse_NM_VERSION) {
253 int len = rr->Length - ((UBYTE *)&rr->NM.Content[0] - (UBYTE *)rr);
254 if (len > (MAXCOMMENTLENGTH - fib->fib_FileName[0])-1) {
255 len = (MAXCOMMENTLENGTH - fib->fib_FileName[0])-1;
256 CopyMem(&rr->NM.Content[0], &fib->fib_FileName[fib->fib_FileName[0]+1], len);
257 fib->fib_FileName[0] += len;
260 break;
261 case RR_SystemUse_CL:
262 if (rr->Version == RR_SystemUse_CL_VERSION) {
263 il->il_Extent = isoRead32LM(&rr->CL.ChildDirectory);
265 break;
266 case RR_SystemUse_PL:
267 if (rr->Version == RR_SystemUse_PL_VERSION) {
268 il->il_ParentExtent = isoRead32LM(&rr->PL.ParentDirectory);
270 break;
271 case RR_SystemUse_RE:
272 if (rr->Version == RR_SystemUse_RE_VERSION) {
273 /* We must ignore this entry */
274 return FALSE;
276 break;
277 case RR_SystemUse_TF:
278 /* TF fields have no use under AROS */
279 break;
280 case RR_SystemUse_SF:
281 D(bug("%s: Sparse files are not (yet) supported\n"));
282 fib->fib_Size = 0;
283 break;
284 default:
285 break;
287 i += rr->Length;
290 /* If RockRidge is detected, the PathTable is
291 * useless to us, due to re-arranged directories.
293 if (has_RockRidge && iv && iv->iv_PathTable.Size) {
294 FreeVec(iv->iv_PathTable.Entry);
295 FreeVec(iv->iv_PathTable.Data);
296 iv->iv_PathTable.Size = 0;
297 iv->iv_PathTable.Entries = 0;
300 end:
302 D(fib->fib_FileName[fib->fib_FileName[0]+1]=0);
304 D(bug("%s: %c Lock %p, 0x%08x: \"%s\", dir size %d\n", __func__,
305 (fib->fib_DirEntryType == 0) ? 'F' : 'D',
307 il->il_ParentExtent,
308 &il->il_Public.cl_FileInfoBlock.fib_FileName[1],
309 dir ? dir->DirectoryLength : il->il_PathTable->DirectoryIdentifierLength));
310 D(bug("%s: @0x%08x, %d bytes\n", __func__, il->il_Extent, il->il_Public.cl_FileInfoBlock.fib_Size));
312 return TRUE;
315 static LONG isoReadDir(struct CDFSVolume *vol, struct ISOLock *ilock, ULONG extent, ULONG offset, ULONG iparent, ULONG *size)
317 struct BCache *bcache = vol->cv_Device->cd_BCache;
318 UBYTE *buff;
319 LONG err;
320 D(struct CDFS *cdfs = vol->cv_CDFSBase;(void)cdfs;);
322 D(bug("%s: Extent @0x%08x, offset 0x%03x, parent 0x%08x\n", __func__, extent, offset, iparent));
324 err = BCache_Read(bcache, extent + (offset / 2048), &buff);
325 if (err == RETURN_OK) {
326 struct isoDirectory *dir = (APTR)&buff[offset];
327 if (!isoParseDir(vol, ilock, dir, iparent))
328 err = ERROR_OBJECT_WRONG_TYPE;
329 else {
330 if (dir->DirectoryLength == 0) {
331 err = ERROR_NO_MORE_ENTRIES;
332 } else {
333 if (size)
334 *size = dir->DirectoryLength;
335 D(bug("%s: pe %d of %d dl %d\n", __func__, ilock->il_ParentExtent, offset, dir->DirectoryLength));
336 if (offset == 0 && dir->DirectoryLength) {
337 offset = dir->DirectoryLength;
338 D(bug("%s: Reading parent dir info at %d\n", __func__, offset));
339 dir = (struct isoDirectory *)&buff[offset];
340 ilock->il_ParentExtent = isoRead32LM(&dir->ExtentLocation);
341 if (size)
342 *size += dir->DirectoryLength;
348 return err;
351 static LONG isoFindCmp(struct CDFSVolume *vol, struct ISOLock *ifile, ULONG extent, BOOL (*cmp)(struct ISOLock *il, APTR data), APTR data)
353 struct ISOVolume *iv = vol->cv_Private;
354 ULONG size;
355 ULONG offset;
356 LONG err;
357 struct ISOLock tmp;
358 int i;
360 /* Scan the path table first, if present */
361 for (i = 0; i < iv->iv_PathTable.Entries; i++) {
362 struct isoPathTable *pt = iv->iv_PathTable.Entry[i];
363 if (pt->ExtentLocation == extent) {
364 int j, parent = i;
365 /* Is the thing we're looking for a subdirectory? */
366 for (j = 0; j < iv->iv_PathTable.Entries; j++) {
367 pt = iv->iv_PathTable.Entry[j];
368 if (j == parent)
369 continue;
370 ifile->il_PathTable = pt;
371 isoParseDir(vol, ifile, NULL, 0);
372 if (cmp(ifile, data))
373 return RETURN_OK;
375 break;
379 /* Read the self/parent at the begining of the directory */
380 err = isoReadDir(vol, &tmp, extent, 0, 0, &size);
381 if (err != RETURN_OK)
382 return err;
384 offset = size;
386 while (offset < tmp.il_Public.cl_FileInfoBlock.fib_Size) {
387 err = isoReadDir(vol, ifile, extent, offset, extent, &size);
389 if (err != ERROR_OBJECT_WRONG_TYPE) {
390 if (err != RETURN_OK)
391 break;
393 if (cmp(ifile, data))
394 return RETURN_OK;
397 offset += size;
399 /* Move to next block */
400 if (err == ERROR_OBJECT_WRONG_TYPE || size == 0 ||
401 (2048 - (offset % 2048)) < sizeof(struct isoDirectory) + 2) {
402 offset = (offset + 2048 - 1) & ~(2048 - 1);
406 return RETURN_FAIL;
409 struct namecmp {
410 struct CDFS *cdfs;
411 CONST_STRPTR file;
412 int len;
415 BOOL cmp(struct ISOLock *fl, APTR data)
417 struct namecmp *d = data;
418 struct CDFS *cdfs = d->cdfs;
420 D(bug("%s: file \"%s\"(%d) vs \"%s\"(%d)\n", __func__,
421 d->file, d->len, &fl->il_Public.cl_FileInfoBlock.fib_FileName[1],
422 fl->il_Public.cl_FileInfoBlock.fib_FileName[0]));
424 return (d->len == fl->il_Public.cl_FileInfoBlock.fib_FileName[0] &&
425 Strnicmp(d->file, &fl->il_Public.cl_FileInfoBlock.fib_FileName[1], d->len) == 0);
428 static LONG isoFindName(struct CDFSVolume *vol, struct ISOLock *ifile, CONST_STRPTR file, struct ISOLock *iparent)
430 struct namecmp data = {
431 .cdfs = vol->cv_CDFSBase,
432 .file = file,
433 .len = strlen(file)
436 return isoFindCmp(vol, ifile, iparent->il_Extent, cmp, &data);
439 static LONG isoFindExtent(struct CDFSVolume *vol, struct ISOLock *ifile, ULONG extent, ULONG parent)
441 BOOL cmp(struct ISOLock *fl, APTR data) {
442 ULONG extent = (ULONG)(IPTR)data;
443 return extent == fl->il_Extent;
446 return isoFindCmp(vol, ifile, parent, cmp, (APTR)(IPTR)extent);
449 static VOID isoReadPathTables(struct CDFSVolume *vol)
451 struct CDFS *cdfs = vol->cv_CDFSBase;
452 struct BCache *bcache = vol->cv_Device->cd_BCache;
453 struct ISOVolume *iv = vol->cv_Private;
454 ULONG sector;
455 UBYTE *data, *dp;
456 LONG size, err;
457 int i;
459 if (iv->iv_PathTable.Size == 0)
460 return;
462 sector = iv->iv_PathTable.Extent;
463 size = iv->iv_PathTable.Size;
465 D(bug("%s: Reading path table @0x%08x, %d bytes\n", __func__, sector * 2048, size));
466 data = AllocVec(size, MEMF_PUBLIC);
467 if (!data)
468 return;
470 /* Read in the path table */
471 dp = data;
472 while (size > 0) {
473 ULONG tocopy = size;
474 UBYTE *cp;
476 if (tocopy > bcache->bc_BlockSize)
477 tocopy = bcache->bc_BlockSize;
479 err = BCache_Read(bcache, sector, &cp);
480 if (err != RETURN_OK) {
481 FreeVec(data);
482 return;
485 CopyMem(cp, dp, tocopy);
486 sector++;
487 dp += tocopy;
488 size -= tocopy;
491 /* Count the number of entries in the PathTable */
492 size = iv->iv_PathTable.Size;
493 iv->iv_PathTable.Entries = 0;
494 dp = data;
495 while (size > 0) {
496 UWORD len = (dp[0] + 8);
497 if (len & 1)
498 len++;
499 dp += len;
500 size -= len;
501 iv->iv_PathTable.Entries++;
504 D(bug("%s: %d entries\n", __func__, iv->iv_PathTable.Entries));
506 if (iv->iv_PathTable.Entries == 0) {
507 FreeVec(data);
508 iv->iv_PathTable.Size = 0;
509 return;
512 iv->iv_PathTable.Entry = AllocVec(sizeof(struct isoPathTable *)*iv->iv_PathTable.Entries, MEMF_PUBLIC);
513 if (iv->iv_PathTable.Entry == NULL) {
514 FreeVec(data);
515 iv->iv_PathTable.Size = 0;
516 return;
519 iv->iv_PathTable.Data = data;
521 /* Compute the pathtable offsets */
522 dp = iv->iv_PathTable.Data;
523 for (i = 0; i < iv->iv_PathTable.Entries; i++) {
524 UWORD len = (dp[0] + 8);
525 if (len & 1)
526 len++;
527 iv->iv_PathTable.Entry[i] = (struct isoPathTable *)dp;
528 dp += len;
531 return;
534 LONG ISO9660_Mount(struct CDFSVolume *vol)
536 struct CDFS *cdfs = vol->cv_CDFSBase;
537 struct BCache *bcache = vol->cv_Device->cd_BCache;
538 struct ISOVolume *iv;
539 LONG err;
540 UQUAD block;
541 BOOL gotdate = FALSE, gotname = FALSE;
543 /* Don't mount filesystems with an invalid block size */
544 if (bcache->bc_BlockSize != 2048)
545 return ERROR_NOT_A_DOS_DISK;
547 iv = AllocVec(sizeof(*iv), MEMF_PUBLIC | MEMF_CLEAR);
548 if (!iv)
549 return ERROR_NO_FREE_STORE;
551 /* Check for an ISO9660 volume */
552 for (block = 16;; block++) {
553 struct isoVolume *iso;
554 err = BCache_Read(bcache, block, (UBYTE **)&iso);
555 if (err != RETURN_OK)
556 break;
558 if (memcmp(iso->Identifier,"CD001",5) != 0) {
559 err = ERROR_NOT_A_DOS_DISK;
560 break;
563 if (iso->Type == ISO_Volume_Primary) {
564 int i;
565 struct ISOLock *il = &iv->iv_RootLock;
566 struct isoDirectory *dir = (APTR)&iso->Primary.RootDirectoryEntry[0];
568 iv->iv_PathTable.Size = isoRead32LM(&iso->Primary.PathTableSize);
569 #if _BYTE_ORDER == _BIG_ENDIAN
570 iv->iv_PathTable.Extent = iso->Primary.TypeMPathTable;
571 #elif _BYTE_ORDER == _LITTLE_ENDIAN
572 iv->iv_PathTable.Extent = iso->Primary.TypeLPathTable;
573 #endif
574 CopyMem(iso, &iv->iv_Volume, sizeof(*iso));
575 for (i = 0; i < 32; i++) {
576 iv->iv_Name[i+1] = iso->Primary.VolumeIdentifier[i];
577 if (iv->iv_Name[i+1] == 0)
578 break;
580 /* Remove trailing spaces from the volume name */
581 while (i > 0 && iv->iv_Name[i] == ' ') i--;
582 iv->iv_Name[0] = i;
583 iv->iv_Name[i+1] = 0;
584 gotname = TRUE;
586 /* Convert from ISO date to DOS Datestamp */
587 ascdate2stamp(&iso->Primary.VolumeCreation, &vol->cv_DosVolume.dl_VolumeDate);
588 gotdate = TRUE;
590 isoParseDir(vol, il, dir, 0);
591 isoReadDir(vol, il, il->il_Extent, 0, 0, NULL);
592 il->il_Public.cl_FileLock.fl_Volume = MKB_VOLUME(vol);
594 continue;
597 if (iso->Type == ISO_Volume_Terminator) {
598 vol->cv_Private = iv;
599 struct ISOLock *il = &iv->iv_RootLock;
600 struct FileInfoBlock *fib = &il->il_Public.cl_FileInfoBlock;
602 if (!gotname) {
603 iv->iv_Name[0] = 4;
604 iv->iv_Name[1] = 'I';
605 iv->iv_Name[2] = 'S';
606 iv->iv_Name[3] = 'O';
607 iv->iv_Name[4] = '0';
608 iv->iv_Name[5] = 0;
610 #ifdef AROS_FAST_BSTR
611 vol->cv_DosVolume.dl_Name = &iv->iv_Name[1];
612 #else
613 vol->cv_DosVolume.dl_Name = MKBADDR(iv->iv_Name);
614 #endif
615 if (!gotdate) {
616 struct Library *DOSBase;
617 if ((DOSBase = OpenLibrary("dos.library", 0))) {
618 DateStamp(&vol->cv_DosVolume.dl_VolumeDate);
619 CloseLibrary(DOSBase);
623 /* We use the Volume as our root lock alias */
624 vol->cv_DosVolume.dl_DiskType = AROS_MAKE_ID('I','S','O',0);
625 vol->cv_DosVolume.dl_Lock = MKB_LOCK(&iv->iv_RootLock.il_Public);
626 vol->cv_Private = iv;
628 fib->fib_DirEntryType = ST_ROOT;
629 CopyMem(AROS_BSTR_ADDR(vol->cv_DosVolume.dl_Name),
630 &fib->fib_FileName[1],
631 AROS_BSTR_strlen(vol->cv_DosVolume.dl_Name));
632 fib->fib_FileName[0] = AROS_BSTR_strlen(vol->cv_DosVolume.dl_Name);
634 /* Cache the path tables */
635 isoReadPathTables(vol);
637 return RETURN_OK;
641 FreeVec(iv);
642 vol->cv_Private = NULL;
644 return err;
647 LONG ISO9660_Unmount(struct CDFSVolume *vol)
649 struct CDFS *cdfs = vol->cv_CDFSBase;
651 FreeVec(vol->cv_Private);
652 return ERROR_NO_FREE_STORE;
655 LONG ISO9660_Locate(struct CDFSVolume *vol, struct CDFSLock *idir, CONST_STRPTR file, ULONG mode, struct CDFSLock **ifile)
657 struct CDFS *cdfs = vol->cv_CDFSBase;
658 struct ISOVolume *iv = vol->cv_Private;
659 struct ISOLock *fl, *il, tl;
660 CONST_STRPTR path, rest;
661 LONG err;
663 if (mode != MODE_OLDFILE)
664 return ERROR_DISK_WRITE_PROTECTED;
666 il = (struct ISOLock *)idir;
668 D(bug("%s: 0x%08x: \"%s\"\n", __func__, il->il_ParentExtent, file));
670 if (idir->cl_FileInfoBlock.fib_DirEntryType != ST_ROOT &&
671 idir->cl_FileInfoBlock.fib_DirEntryType != ST_USERDIR) {
672 D(bug("%s: \"%s\" is not a directory\n", __func__, &idir->cl_FileInfoBlock.fib_FileName[1]));
673 return ERROR_OBJECT_WRONG_TYPE;
676 fl = AllocVec(sizeof(*fl), MEMF_PUBLIC | MEMF_CLEAR);
677 if (fl) {
678 // Get the actual filename
679 path = strchr(file, ':');
680 if (path) {
681 // Switch to the root directory
682 il = (struct ISOLock *)B_LOCK(vol->cv_DosVolume.dl_Lock);
683 path++;
684 } else {
685 path = file;
688 while (path[0] == 0 || path[0] == '/') {
689 // ""
690 if (path[0] == 0) {
691 CopyMem(il, fl, sizeof(*fl));
692 *ifile = &fl->il_Public;
693 return RETURN_OK;
696 // "/"
697 if (path[0] == '/') {
698 /* Find parent */
699 path++;
701 /* Already at root? */
702 if (il->il_Public.cl_FileInfoBlock.fib_DirEntryType == ST_ROOT)
703 continue;
705 ASSERT(il->il_Public.cl_FileInfoBlock.fib_DirEntryType == ST_USERDIR);
707 /* Otherwise, move to parent */
708 if (il->il_PathTable) {
709 /* Non-RockRidge */
710 tl.il_PathTable = iv->iv_PathTable.Entry[il->il_PathTable->ParentDirectory-1];
711 isoParseDir(vol, &tl, NULL, 0);
712 il = &tl;
713 continue;
714 } else {
715 err = isoReadDir(vol, &tl, il->il_ParentExtent, 0, 0, NULL);
716 if (err == RETURN_OK) {
717 /* .. and find its name in its parent */
718 if (tl.il_Public.cl_FileInfoBlock.fib_DirEntryType != ST_ROOT)
719 isoFindExtent(vol, &tl, tl.il_Extent, tl.il_ParentExtent);
720 il = &tl;
721 continue;
724 FreeVec(fl);
725 return err;
729 while (*path && (rest = strchr(path, '/'))) {
730 int len;
731 TEXT fname[MAXFILENAMELENGTH];
733 if ((il->il_Public.cl_FileInfoBlock.fib_DirEntryType != ST_USERDIR) &&
734 (il->il_Public.cl_FileInfoBlock.fib_DirEntryType != ST_ROOT)) {
735 return ERROR_DIR_NOT_FOUND;
738 len = rest - path;
739 if (len >= MAXFILENAMELENGTH) {
740 FreeVec(fl);
741 return ERROR_OBJECT_NOT_FOUND;
744 CopyMem(path, fname, len);
745 fname[len] = 0;
746 D(bug("%s: path=\"%s\" rest=\"%s\" fname=\"%s\"\n",
747 __func__, path, rest, fname));
749 err = isoFindName(vol, fl, fname, il);
750 D(bug("%s:%d isoFindName => %d\n", __func__, __LINE__, err));
751 if (err != RETURN_OK) {
752 FreeVec(fl);
753 return err;
756 CopyMem(fl, &tl, sizeof(*fl));
757 il = &tl;
758 path = rest;
759 while (*path == '/')
760 path++;
763 if (*path == 0) {
764 *ifile = &fl->il_Public;
765 return RETURN_OK;
768 err = isoFindName(vol, fl, path, il);
769 D(bug("%s:%d isoFindName => %d\n", __func__, __LINE__, err));
770 if (err == RETURN_OK) {
771 *ifile = &fl->il_Public;
772 return RETURN_OK;
775 FreeVec(fl);
776 } else {
777 err = ERROR_NO_FREE_STORE;
780 return err;
783 VOID ISO9660_Close(struct CDFSVolume *vol, struct CDFSLock *fl)
785 struct CDFS *cdfs = vol->cv_CDFSBase;
786 FreeVec(fl);
789 LONG ISO9660_ExamineNext(struct CDFSVolume *vol, struct CDFSLock *dl, struct FileInfoBlock *fib)
791 struct CDFS *cdfs = vol->cv_CDFSBase;
792 ULONG offset;
793 struct ISOLock tl = {}, *il = (struct ISOLock *)dl;
794 LONG err;
795 ULONG size;
797 D(bug("%s: Root %p, This %p, DiskKey %d of %d\n", __func__,
798 &((struct ISOVolume *)vol->cv_Private)->iv_RootLock, dl,
799 fib->fib_DiskKey, dl->cl_FileInfoBlock.fib_Size));
801 /* Do we have a valid size for this directory? */
802 if (dl->cl_FileInfoBlock.fib_Size == 0) {
803 struct isoDirectory *dir;
804 err = BCache_Read(vol->cv_Device->cd_BCache, il->il_Extent, (UBYTE **)&dir);
805 if (err != RETURN_OK)
806 return err;
807 dl->cl_FileInfoBlock.fib_Size = isoRead32LM(&dir->DataLength);
810 if (fib->fib_DiskKey == 0) {
811 /* Skip the first two entries */
812 err = isoReadDir(vol, &tl, il->il_Extent, 0, 0, &size);
813 if (err != RETURN_OK)
814 return err;
815 fib->fib_DiskKey = size;
818 if (fib->fib_DiskKey >= dl->cl_FileInfoBlock.fib_Size) {
819 D(bug("%s: No more entries (%d >= %d)\n", __func__, fib->fib_DiskKey, dl->cl_FileInfoBlock.fib_Size));
820 return ERROR_NO_MORE_ENTRIES;
823 offset = fib->fib_DiskKey;
825 while (offset < dl->cl_FileInfoBlock.fib_Size) {
826 err = isoReadDir(vol, &tl, il->il_Extent, offset, il->il_Extent, &size);
828 if (err != RETURN_OK && err != ERROR_OBJECT_WRONG_TYPE)
829 break;
831 D(bug("%s: err=%d, size=%d\n", __func__, err, size));
832 offset += size;
834 /* Move to next block */
835 if (err == ERROR_OBJECT_WRONG_TYPE || size == 0 ||
836 (2048 - (offset % 2048)) < sizeof(struct isoDirectory) + 2) {
837 offset = (offset + 2048 - 1) & ~(2048 - 1);
840 if (err == RETURN_OK) {
841 struct FileInfoBlock *tfib = &tl.il_Public.cl_FileInfoBlock;
842 CopyMem(tfib, fib, sizeof(*fib));
843 break;
846 err = ERROR_NO_MORE_ENTRIES;
849 fib->fib_DiskKey = offset;
851 D(bug("%s: Error %d\n", __func__, err));
853 return err;
856 LONG ISO9660_Read(struct CDFSVolume *vol, struct CDFSLock *fl, APTR buff, SIPTR len, SIPTR *actualp)
858 struct CDFS *cdfs = vol->cv_CDFSBase;
859 struct BCache *bcache = vol->cv_Device->cd_BCache;
860 struct ISOLock *il = (struct ISOLock *)fl;
861 UQUAD size = fl->cl_FileInfoBlock.fib_Size;
862 ULONG block;
863 ULONG offset;
864 LONG err = RETURN_OK;
865 SIPTR actual = 0;
866 UBYTE *cache;
868 block = il->il_Offset / 2048;
869 offset = il->il_Offset % 2048;
870 if ((len + il->il_Offset) > size)
871 len = size - il->il_Offset;
873 while (len) {
874 ULONG tocopy = 2048 - offset;
875 if (tocopy > len)
876 tocopy = len;
878 err = BCache_Read(bcache, il->il_Extent + block, &cache);
879 if (err != RETURN_OK)
880 break;
881 CopyMem(cache + offset, buff, tocopy);
883 buff += tocopy;
884 len -= tocopy;
885 actual += tocopy;
886 il->il_Offset += tocopy;
888 offset = 0;
889 block++;
892 *actualp = actual;
893 return err;
896 LONG ISO9660_Seek(struct CDFSVolume *vol, struct CDFSLock *fl, SIPTR pos, LONG mode, SIPTR *oldpos)
898 struct ISOLock *il = (struct ISOLock *)fl;
899 UQUAD size = fl->cl_FileInfoBlock.fib_Size;
901 *oldpos = il->il_Offset;
902 switch (mode) {
903 case OFFSET_BEGINNING:
904 if (pos < 0 || pos > size)
905 return ERROR_SEEK_ERROR;
906 il->il_Offset = pos;
907 break;
908 case OFFSET_CURRENT:
909 if (-pos > il->il_Offset ||
910 (il->il_Offset + pos) > size)
911 return ERROR_SEEK_ERROR;
912 il->il_Offset += pos;
913 break;
914 case OFFSET_END:
915 if (pos < 0 || -pos > size)
916 return ERROR_SEEK_ERROR;
917 il->il_Offset = size + pos;
918 break;
919 default:
920 return ERROR_SEEK_ERROR;
923 return RETURN_OK;
926 const struct CDFSOps ISO9660_Ops = {
927 .op_Type = AROS_MAKE_ID('I','S','O',0),
928 .op_Mount = ISO9660_Mount,
929 .op_Unmount = ISO9660_Unmount,
930 .op_Locate = ISO9660_Locate,
931 .op_Close = ISO9660_Close,
932 .op_ExamineNext = ISO9660_ExamineNext,
933 .op_Seek = ISO9660_Seek,
934 .op_Read = ISO9660_Read,