cdfs-handler: RockRidge extension support (lightly tested)
[AROS.git] / rom / filesys / cdfs / iso9660.c
blobb3b3756c9c0d30c5e86427e6219df57c10382d09
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 */
29 struct ISOVolume {
30 struct isoVolume iv_Volume;
31 struct ISOLock iv_RootLock;
32 TEXT iv_Name[1 + 32 + 1];
35 static ULONG ascdec(STRPTR val, int len)
37 ULONG out = 0;
39 while ((*val >= '0' && *val <= '9') && len > 0) {
40 out *= 10;
41 out += *val - '9';
42 len--;
43 val++;
46 return out;
49 #define AMIGA_EPOC 722484 /* Day number of Amiga Epoc, using Zeller */
51 static void ascdate2stamp(ascDate *asc, struct DateStamp *date)
53 ULONG year = ascdec(asc->Year, 4);
54 ULONG month = ascdec(asc->Month, 2);
55 ULONG day = ascdec(asc->Day, 2);
57 if (month < 3) {
58 month += 12;
59 year--;
61 date->ds_Days = (365*year + year/4 - year/100 + year/400 + day + (153*month+8)/5) - AMIGA_EPOC;
62 date->ds_Minute = ascdec(asc->Hour, 2) * 60 +
63 ascdec(asc->Minute, 2);
64 date->ds_Tick = ascdec(asc->Second, 2) * 50 + ascdec(asc->Hundredth, 2) / 2;
66 /* FIXME: Adjust for timezone */
69 static void bindate2stamp(binDate *bin, struct DateStamp *date)
71 ULONG year = bin->Years + 1900;
72 ULONG month = bin->Month;
73 ULONG day = bin->Day;
75 if (month < 3) {
76 month += 12;
77 year--;
79 date->ds_Days = (365*year + year/4 - year/100 + year/400 + day + (153*month+8)/5) - AMIGA_EPOC;
80 date->ds_Minute = bin->Hour * 60 +
81 bin->Minute;
82 date->ds_Tick = bin->Second * 50;
84 /* FIXME: Adjust for timezone */
87 static inline ULONG isoRead32LM(int32LM *lm)
89 /* What fun! Some ISO creators (ISOCD v1.04, I'm looking at you!)
90 * only filled in one side of this field.
92 #if _BYTE_ORDER == _BIG_ENDIAN
93 return lm->MSB ? lm->MSB : (ULONG)AROS_LE2LONG(lm->LSB);
94 #elif _BYTE_ORDER == _LITTLE_ENDIAN
95 return lm->LSB ? lm->LSB : (ULONG)AROS_BE2LONG(lm->MSB);
96 #else /* Wacky byte order? Impossible in this day and age. */
97 #error PDP11s are no longer supported.
98 #endif
101 static BOOL isoParseDir(struct CDFSVolume *vol, struct ISOLock *il, struct isoDirectory *dir, ULONG iparent)
103 struct CDFS *cdfs = vol->cv_CDFSBase;
104 struct FileInfoBlock *fib = &il->il_Public.cl_FileInfoBlock;
105 struct FileLock *fl = &il->il_Public.cl_FileLock;
106 struct ISOVolume *iv = vol->cv_Private;
107 int i, len;
108 BOOL has_Amiga_Protections = FALSE;
110 D(bug("%s: \"%s\" Extent @0x%08x, Parent 0x%08x, Flags 0x%02x\n", __func__,
111 dir->FileIdentifier,
112 isoRead32LM(&dir->ExtentLocation), iparent, dir->Flags));
114 if (dir->Flags & (ISO_Directory_ASSOC | ISO_Directory_HIDDEN))
115 return FALSE;
117 il->il_Extent = isoRead32LM(&dir->ExtentLocation);
119 if (iv && il->il_Extent == iv->iv_RootLock.il_Extent) {
120 CopyMem(&iv->iv_RootLock, il, sizeof(*il));
121 return TRUE;
124 /* The starting location of the file is its unique 'key' */
125 fl->fl_Key = il->il_Extent;
127 fib->fib_DirEntryType = (dir->Flags & ISO_Directory_ISDIR) ? ST_USERDIR : ST_FILE;
129 len = dir->FileIdentifierLength;
130 if (len > MAXFILENAMELENGTH-2)
131 len = MAXFILENAMELENGTH-2;
132 for (i = 0; i < len; i++) {
133 /* Goddam VMS guys screwing up a perfectly good specification... */
134 if (dir->FileIdentifier[i] == ';') {
135 len = i;
136 break;
138 fib->fib_FileName[i + 1] = dir->FileIdentifier[i];
140 fib->fib_FileName[0] = len;
141 fib->fib_FileName[len+1] = 0;
142 fib->fib_Protection = FIBF_DELETE |
143 FIBF_EXECUTE |
144 FIBF_WRITE |
145 FIBF_GRP_DELETE |
146 FIBF_GRP_EXECUTE |
147 FIBF_GRP_WRITE |
148 FIBF_OTR_DELETE |
149 FIBF_OTR_EXECUTE |
150 FIBF_OTR_WRITE |
151 FIBF_ARCHIVE;
152 fib->fib_EntryType = fib->fib_DirEntryType;
153 fib->fib_Size = isoRead32LM(&dir->DataLength);
154 fib->fib_NumBlocks = (fib->fib_Size + 2047) / 2048;
155 bindate2stamp(&dir->RecordingDate, &fib->fib_Date);
156 fib->fib_Comment[0] = 0;
157 fib->fib_OwnerUID = 0;
158 fib->fib_OwnerGID = 0;
160 il->il_ParentExtent = iparent;
162 /* Parse RockRidge extensions */
163 i = ((UBYTE *)&dir->FileIdentifier[dir->FileIdentifierLength] -
164 (UBYTE *)dir);
165 if (i & 1)
166 i++;
167 while (i < dir->DirectoryLength) {
168 struct rrSystemUse *rr = (APTR)(((UBYTE *)dir) + i);
169 D(bug("%s: RR @%d [%c%c] (%d)\n", __func__, i, (AROS_BE2LONG(rr->Signature) >> 8) & 0xff, AROS_BE2LONG(rr->Signature) & 0xff, rr->Length));
170 if (rr->Length == 0) {
171 D(bug("%s: Corrupted RR section detected\n", __func__));
172 break;
174 switch (AROS_BE2LONG(rr->Signature)) {
175 case RR_SystemUse_AS:
176 if (rr->Version == RR_SystemUse_AS_VERSION) {
177 UBYTE *data = &rr->AS.Data[0];
179 /* Amiga System Use area */
180 if (rr->AS.Flags & RR_AS_PROTECTION) {
181 struct SystemUseASProtection *ap = (APTR)data;
182 fib->fib_Protection = (ap->User << 24) |
183 (ap->Zero << 16) |
184 (ap->MultiUser << 8) |
185 (ap->Protection << 0);
186 data = (UBYTE *)&ap[1];
187 has_Amiga_Protections = TRUE;
189 if (rr->AS.Flags & RR_AS_COMMENT) {
190 struct SystemUseASComment *ac = (APTR)data;
191 int len = ac->Length;
192 if (len > (MAXCOMMENTLENGTH - fib->fib_Comment[0])-1) {
193 len = (MAXCOMMENTLENGTH - fib->fib_Comment[0])-1;
194 CopyMem(&ac->Comment[0], &fib->fib_Comment[fib->fib_Comment[0]+1], len);
195 fib->fib_Comment[0] += len;
199 break;
200 case RR_SystemUse_PX:
201 /* Don't overwrite the Amiga RR protections */
202 if (has_Amiga_Protections)
203 break;
204 if (rr->Version == RR_SystemUse_PX_VERSION) {
205 ULONG prot = 0;
206 ULONG mode = isoRead32LM(&rr->PX.st_mode);
207 prot |= (mode & S_IXOTH) ? FIBF_OTR_EXECUTE : 0;
208 prot |= (mode & S_IWOTH) ? (FIBF_OTR_WRITE | FIBF_OTR_DELETE): 0;
209 prot |= (mode & S_IROTH) ? FIBF_OTR_READ : 0;
210 prot |= (mode & S_IXGRP) ? FIBF_GRP_EXECUTE : 0;
211 prot |= (mode & S_IWGRP) ? (FIBF_GRP_WRITE | FIBF_GRP_DELETE) : 0;
212 prot |= (mode & S_IRGRP) ? FIBF_GRP_READ : 0;
213 prot |= (mode & S_IXUSR) ? 0 : FIBF_EXECUTE;
214 prot |= (mode & S_IWUSR) ? 0 : (FIBF_WRITE | FIBF_DELETE);
215 prot |= (mode & S_IRUSR) ? 0 : FIBF_READ;
217 fib->fib_Protection = prot;
218 fib->fib_OwnerUID = isoRead32LM(&rr->PX.st_uid);
219 fib->fib_OwnerGID = isoRead32LM(&rr->PX.st_gid);
221 break;
222 case RR_SystemUse_NM:
223 if (rr->Version == RR_SystemUse_NM_VERSION) {
224 int len = rr->Length - ((UBYTE *)&rr->NM.Content[0] - (UBYTE *)rr);
225 if (len > (MAXCOMMENTLENGTH - fib->fib_FileName[0])-1) {
226 len = (MAXCOMMENTLENGTH - fib->fib_FileName[0])-1;
227 CopyMem(&rr->NM.Content[0], &fib->fib_FileName[fib->fib_FileName[0]+1], len);
228 fib->fib_FileName[0] += len;
231 break;
232 case RR_SystemUse_CL:
233 if (rr->Version == RR_SystemUse_CL_VERSION) {
234 il->il_Extent = isoRead32LM(&rr->CL.ChildDirectory);
236 break;
237 case RR_SystemUse_PL:
238 if (rr->Version == RR_SystemUse_PL_VERSION) {
239 il->il_ParentExtent = isoRead32LM(&rr->PL.ParentDirectory);
241 break;
242 case RR_SystemUse_RE:
243 if (rr->Version == RR_SystemUse_RE_VERSION) {
244 /* We must ignore this entry */
245 return FALSE;
247 break;
248 case RR_SystemUse_TF:
249 /* TF fields have no use under AROS */
250 break;
251 case RR_SystemUse_SF:
252 D(bug("%s: Sparse files are not (yet) supported\n"));
253 fib->fib_Size = 0;
254 break;
255 default:
256 break;
258 i += rr->Length;
261 D(fib->fib_FileName[fib->fib_FileName[0]+1]=0);
263 D(bug("%s: %c Lock %p, 0x%08x: \"%s\", dir size %d\n", __func__,
264 (fib->fib_DirEntryType == 0) ? 'F' : 'D',
266 il->il_ParentExtent,
267 &il->il_Public.cl_FileInfoBlock.fib_FileName[1],
268 dir->DirectoryLength));
269 D(bug("%s: @0x%08x, %d bytes\n", __func__, il->il_Extent, il->il_Public.cl_FileInfoBlock.fib_Size));
271 return TRUE;
274 static LONG isoReadDir(struct CDFSVolume *vol, struct ISOLock *ilock, ULONG block, ULONG offset, ULONG iparent, ULONG *size)
276 struct BCache *bcache = vol->cv_Device->cd_BCache;
277 UBYTE *buff;
278 LONG err;
280 err = BCache_Read(bcache, block, &buff);
281 if (err == RETURN_OK) {
282 struct isoDirectory *dir = (APTR)&buff[offset];
283 if (!isoParseDir(vol, ilock, dir, iparent))
284 err = ERROR_OBJECT_WRONG_TYPE;
285 else {
286 if (dir->DirectoryLength == 0) {
287 err = ERROR_NO_MORE_ENTRIES;
288 } else {
289 if (size)
290 *size = dir->DirectoryLength;
291 if (ilock->il_ParentExtent == 0 &&
292 offset == 0 &&
293 dir->DirectoryLength) {
294 offset = dir->DirectoryLength;
295 dir = (struct isoDirectory *)&buff[offset];
296 ilock->il_ParentExtent = isoRead32LM(&dir->ExtentLocation);
297 if (size)
298 *size += dir->DirectoryLength;
304 return err;
307 static LONG isoFindCmp(struct CDFSVolume *vol, struct ISOLock *ifile, ULONG extent, BOOL (*cmp)(struct ISOLock *il, APTR data), APTR data)
309 ULONG size;
310 ULONG block, offset;
311 LONG err;
312 struct ISOLock tmp;
314 /* Read the self/parent at the begining of the directory */
315 err = isoReadDir(vol, &tmp, extent, 0, 0, &size);
316 if (err != RETURN_OK)
317 return err;
319 block = 0;
320 offset = size;
322 while ((block * 2048 + offset) < tmp.il_Public.cl_FileInfoBlock.fib_Size) {
323 err = isoReadDir(vol, ifile, extent + block, offset, extent, &size);
325 if (err != ERROR_OBJECT_WRONG_TYPE) {
326 if (err != RETURN_OK)
327 break;
329 if (cmp(ifile, data))
330 return RETURN_OK;
333 offset += size;
334 if ((2048 - offset) < sizeof(struct isoDirectory) || size == 0) {
335 block++;
336 offset = 0;
340 return RETURN_FAIL;
343 static LONG isoFindName(struct CDFSVolume *vol, struct ISOLock *ifile, CONST_STRPTR file, struct ISOLock *iparent)
345 struct CDFS *cdfs = vol->cv_CDFSBase;
346 struct namecmp {
347 CONST_STRPTR file;
348 int len;
349 } data;
351 BOOL cmp(struct ISOLock *fl, APTR data) {
352 struct namecmp *d = data;
353 D(bug("%s: file \"%s\"(%d) vs \"%s\"(%d)\n", __func__,
354 d->file, d->len, &fl->il_Public.cl_FileInfoBlock.fib_FileName[1],
355 fl->il_Public.cl_FileInfoBlock.fib_FileName[0]));
357 return (d->len == fl->il_Public.cl_FileInfoBlock.fib_FileName[0] &&
358 Strnicmp(d->file, &ifile->il_Public.cl_FileInfoBlock.fib_FileName[1], d->len) == 0);
361 data.file = file;
362 data.len = strlen(file);
364 return isoFindCmp(vol, ifile, iparent->il_Extent, cmp, &data);
367 static LONG isoFindExtent(struct CDFSVolume *vol, struct ISOLock *ifile, ULONG extent, ULONG parent)
369 BOOL cmp(struct ISOLock *fl, APTR data) {
370 ULONG extent = (ULONG)(IPTR)data;
371 return extent == fl->il_Extent;
374 return isoFindCmp(vol, ifile, parent, cmp, (APTR)(IPTR)extent);
377 LONG ISO9660_Mount(struct CDFSVolume *vol)
379 struct CDFS *cdfs = vol->cv_CDFSBase;
380 struct BCache *bcache = vol->cv_Device->cd_BCache;
381 struct ISOVolume *iv;
382 LONG err;
383 UQUAD block;
384 BOOL gotdate = FALSE, gotname = FALSE;
386 /* Don't mount filesystems with an invalid block size */
387 if (bcache->bc_BlockSize != 2048)
388 return ERROR_NOT_A_DOS_DISK;
390 iv = AllocVec(sizeof(*iv), MEMF_PUBLIC | MEMF_CLEAR);
391 if (!iv)
392 return ERROR_NO_FREE_STORE;
394 /* Check for an ISO9660 volume */
395 for (block = 16;; block++) {
396 struct isoVolume *iso;
397 err = BCache_Read(bcache, block, (UBYTE **)&iso);
398 if (err != RETURN_OK)
399 break;
401 if (memcmp(iso->Identifier,"CD001",5) != 0) {
402 err = ERROR_NOT_A_DOS_DISK;
403 break;
406 if (iso->Type == ISO_Volume_Primary) {
407 int i;
408 struct ISOLock *il = &iv->iv_RootLock;
409 struct isoDirectory *dir = (APTR)&iso->Primary.RootDirectoryEntry[0];
411 CopyMem(iso, &iv->iv_Volume, sizeof(*iso));
412 for (i = 0; i < 32; i++) {
413 iv->iv_Name[i+1] = iso->Primary.VolumeIdentifier[i];
414 if (iv->iv_Name[i+1] == 0)
415 break;
417 iv->iv_Name[33] = 0;
418 iv->iv_Name[0] = i;
419 gotname = TRUE;
421 /* Convert from ISO date to DOS Datestamp */
422 ascdate2stamp(&iso->Primary.VolumeCreation, &vol->cv_DosVolume.dl_VolumeDate);
423 gotdate = TRUE;
425 isoParseDir(vol, il, dir, 0);
426 isoReadDir(vol, il, il->il_Extent, 0, 0, NULL);
427 il->il_Public.cl_FileLock.fl_Volume = MKB_VOLUME(vol);
429 continue;
432 if (iso->Type == ISO_Volume_Terminator) {
433 vol->cv_Private = iv;
434 struct ISOLock *il = &iv->iv_RootLock;
435 struct FileInfoBlock *fib = &il->il_Public.cl_FileInfoBlock;
437 if (!gotname) {
438 iv->iv_Name[0] = 4;
439 iv->iv_Name[1] = 'I';
440 iv->iv_Name[2] = 'S';
441 iv->iv_Name[3] = 'O';
442 iv->iv_Name[4] = '0';
443 iv->iv_Name[5] = 0;
445 #ifdef AROS_FAST_BSTR
446 vol->cv_DosVolume.dl_Name = &iv->iv_Name[1];
447 #else
448 vol->cv_DosVolume.dl_Name = MKBADDR(iv->iv_Name);
449 #endif
450 if (!gotdate) {
451 struct Library *DOSBase;
452 if ((DOSBase = OpenLibrary("dos.library", 0))) {
453 DateStamp(&vol->cv_DosVolume.dl_VolumeDate);
454 CloseLibrary(DOSBase);
458 /* We use the Volume as our root lock alias */
459 vol->cv_DosVolume.dl_Type = AROS_MAKE_ID('I','S','O',0);
460 vol->cv_DosVolume.dl_Lock = MKB_LOCK(&iv->iv_RootLock.il_Public);
461 vol->cv_Private = iv;
463 fib->fib_DirEntryType = ST_ROOT;
464 CopyMem(AROS_BSTR_ADDR(vol->cv_DosVolume.dl_Name),
465 &fib->fib_FileName[1],
466 AROS_BSTR_strlen(vol->cv_DosVolume.dl_Name));
467 fib->fib_FileName[0] = AROS_BSTR_strlen(vol->cv_DosVolume.dl_Name);
469 return RETURN_OK;
473 FreeVec(iv);
474 vol->cv_Private = NULL;
476 return err;
479 LONG ISO9660_Unmount(struct CDFSVolume *vol)
481 struct CDFS *cdfs = vol->cv_CDFSBase;
483 FreeVec(vol->cv_Private);
484 return ERROR_NO_FREE_STORE;
487 LONG ISO9660_Locate(struct CDFSVolume *vol, struct CDFSLock *idir, CONST_STRPTR file, ULONG mode, struct CDFSLock **ifile)
489 struct CDFS *cdfs = vol->cv_CDFSBase;
490 struct ISOLock *fl, *il, tl;
491 CONST_STRPTR path, rest;
492 LONG err;
494 if (mode != MODE_OLDFILE)
495 return ERROR_DISK_WRITE_PROTECTED;
497 il = (struct ISOLock *)idir;
499 D(bug("%s: 0x%08x: \"%s\"\n", __func__, il->il_ParentExtent, file));
501 fl = AllocVec(sizeof(*fl), MEMF_PUBLIC | MEMF_CLEAR);
502 if (fl) {
503 // Get the actual filename
504 path = strchr(file, ':');
505 if (path) {
506 // Switch to the root directory
507 il = (struct ISOLock *)B_LOCK(vol->cv_DosVolume.dl_Lock);
508 path++;
509 } else {
510 path = file;
513 while (path[0] == 0 || path[0] == '/') {
514 // ""
515 if (path[0] == 0) {
516 CopyMem(il, fl, sizeof(*fl));
517 *ifile = &fl->il_Public;
518 return RETURN_OK;
521 // "/"
522 if (path[0] == '/') {
523 /* Find parent */
524 path++;
526 /* Already at root? */
527 if (il->il_Public.cl_FileInfoBlock.fib_DirEntryType == ST_ROOT)
528 continue;
530 /* Otherwise, move to parent */
531 err = isoReadDir(vol, &tl, il->il_ParentExtent, 0, 0, NULL);
532 if (err == RETURN_OK) {
533 /* .. and find its name in its parent */
534 isoFindExtent(vol, &tl, tl.il_Extent, tl.il_ParentExtent);
535 il = &tl;
536 continue;
538 FreeVec(fl);
539 return err;
543 while (*path && (rest = strchr(path, '/'))) {
544 int len;
545 TEXT fname[MAXFILENAMELENGTH];
547 if ((il->il_Public.cl_FileInfoBlock.fib_DirEntryType != ST_USERDIR) &&
548 (il->il_Public.cl_FileInfoBlock.fib_DirEntryType != ST_ROOT)) {
549 return ERROR_DIR_NOT_FOUND;
552 len = rest - path;
553 if (len >= MAXFILENAMELENGTH) {
554 FreeVec(fl);
555 return ERROR_OBJECT_NOT_FOUND;
558 CopyMem(path, fname, len);
559 fname[len] = 0;
560 D(bug("%s: path=\"%s\" rest=\"%s\" fname=\"%s\"\n",
561 __func__, path, rest, fname));
563 err = isoFindName(vol, fl, fname, il);
564 D(bug("%s:%d isoFindName => %d\n", __func__, __LINE__, err));
565 if (err != RETURN_OK) {
566 FreeVec(fl);
567 return err;
570 CopyMem(fl, &tl, sizeof(*fl));
571 il = &tl;
572 path = rest;
573 while (*path == '/')
574 path++;
577 if (*path == 0) {
578 *ifile = &fl->il_Public;
579 return RETURN_OK;
582 err = isoFindName(vol, fl, path, il);
583 D(bug("%s:%d isoFindName => %d\n", __func__, __LINE__, err));
584 if (err == RETURN_OK) {
585 *ifile = &fl->il_Public;
586 return RETURN_OK;
589 FreeVec(fl);
590 } else {
591 err = ERROR_NO_FREE_STORE;
594 return err;
597 VOID ISO9660_Close(struct CDFSVolume *vol, struct CDFSLock *fl)
599 struct CDFS *cdfs = vol->cv_CDFSBase;
600 FreeVec(fl);
603 LONG ISO9660_ExamineNext(struct CDFSVolume *vol, struct CDFSLock *dl, struct FileInfoBlock *fib)
605 struct CDFS *cdfs = vol->cv_CDFSBase;
606 ULONG offset, block, diskkey;
607 struct ISOLock tl, *il = (struct ISOLock *)dl;
608 LONG err;
609 ULONG size;
611 D(bug("%s: Root %p, This %p, DiskKey %d of %d\n", __func__,
612 &((struct ISOVolume *)vol->cv_Private)->iv_RootLock, dl,
613 fib->fib_DiskKey, dl->cl_FileInfoBlock.fib_Size));
615 /* Skip the first two entries, which simply point to the parent
616 * The funky math is for the single-character 'filename' given
617 * to those directories, and the round-up-to int16 padding
619 if (fib->fib_DiskKey == 0)
620 fib->fib_DiskKey = (((sizeof(struct isoDirectory)+1)+1)&~1)*2;
622 if (fib->fib_DiskKey >= dl->cl_FileInfoBlock.fib_Size)
623 return ERROR_NO_MORE_ENTRIES;
625 diskkey = fib->fib_DiskKey;
626 block = diskkey / 2048;
627 offset = diskkey % 2048;
629 while (diskkey < dl->cl_FileInfoBlock.fib_Size) {
630 err = isoReadDir(vol, &tl, il->il_Extent + block, offset, il->il_Extent, &size);
632 if (err != RETURN_OK && err != ERROR_OBJECT_WRONG_TYPE)
633 break;
635 D(bug("%s: err=%d, size=%d\n", __func__, err, size));
636 offset += size;
637 if ((2048 - offset) < sizeof(struct isoDirectory) || size == 0) {
638 offset = 0;
639 block++;
642 diskkey = block * 2048 + offset;
644 if (err == RETURN_OK) {
645 struct FileInfoBlock *tfib = &tl.il_Public.cl_FileInfoBlock;
646 CopyMem(tfib, fib, sizeof(*fib));
647 break;
650 err = ERROR_NO_MORE_ENTRIES;
653 fib->fib_DiskKey = diskkey;
655 D(bug("%s: Error %d\n", __func__, err));
657 return err;
660 LONG ISO9660_Read(struct CDFSVolume *vol, struct CDFSLock *fl, APTR buff, SIPTR len, SIPTR *actualp)
662 struct CDFS *cdfs = vol->cv_CDFSBase;
663 struct BCache *bcache = vol->cv_Device->cd_BCache;
664 struct ISOLock *il = (struct ISOLock *)fl;
665 UQUAD size = fl->cl_FileInfoBlock.fib_Size;
666 ULONG block;
667 ULONG offset;
668 LONG err = RETURN_OK;
669 SIPTR actual = 0;
670 UBYTE *cache;
672 block = il->il_Offset / 2048;
673 offset = il->il_Offset % 2048;
674 if ((len + il->il_Offset) > size)
675 len = size - il->il_Offset;
677 while (len) {
678 ULONG tocopy = 2048 - offset;
679 if (tocopy > len)
680 tocopy = len;
682 err = BCache_Read(bcache, il->il_Extent + block, &cache);
683 if (err != RETURN_OK)
684 break;
685 CopyMem(cache + offset, buff, tocopy);
687 buff += tocopy;
688 len -= tocopy;
689 actual += tocopy;
690 il->il_Offset += tocopy;
692 offset = 0;
693 block++;
696 *actualp = actual;
697 return err;
700 LONG ISO9660_Seek(struct CDFSVolume *vol, struct CDFSLock *fl, SIPTR pos, LONG mode, SIPTR *oldpos)
702 struct ISOLock *il = (struct ISOLock *)fl;
703 UQUAD size = fl->cl_FileInfoBlock.fib_Size;
705 *oldpos = il->il_Offset;
706 switch (mode) {
707 case OFFSET_BEGINNING:
708 if (pos < 0 || pos > size)
709 return ERROR_SEEK_ERROR;
710 il->il_Offset = pos;
711 break;
712 case OFFSET_CURRENT:
713 if (-pos > il->il_Offset ||
714 (il->il_Offset + pos) > size)
715 return ERROR_SEEK_ERROR;
716 il->il_Offset += pos;
717 break;
718 case OFFSET_END:
719 if (pos < 0 || -pos > size)
720 return ERROR_SEEK_ERROR;
721 il->il_Offset = size + pos;
722 break;
723 default:
724 return ERROR_SEEK_ERROR;
727 return RETURN_OK;
730 const struct CDFSOps ISO9660_Ops = {
731 .op_Type = AROS_MAKE_ID('I','S','O',0),
732 .op_Mount = ISO9660_Mount,
733 .op_Unmount = ISO9660_Unmount,
734 .op_Locate = ISO9660_Locate,
735 .op_Close = ISO9660_Close,
736 .op_ExamineNext = ISO9660_ExamineNext,
737 .op_Seek = ISO9660_Seek,
738 .op_Read = ISO9660_Read,