LibXenon
Bare-metal Xbox 360 homebrew library
Loading...
Searching...
No Matches
iso9660.c
Go to the documentation of this file.
1/* modified for libxenon */
2
3/* KallistiOS 1.2.0
4
5 fs_iso9660.c
6 Copyright (C)2000,2001,2003 Dan Potter
7 Copyright (C)2001 Andrew Kieschnick
8 Copyright (C)2002 Bero
9
10*/
11
12/*
13
14This module implements an ISO9660 file system for reading from a CDR or CD
15in the DC's GD-Rom drive.
16
17Rock Ridge support has now been implemented, thanks to Andrew Kieschnick
18who donated the code. Thanks to Bero for the Joliet support here.
19
20This FS is considerably simplified from what you'd find in a bigger kernel
21like Linux or BSD, since we have the pleasure of working with only a single
22device capable of ISO9660 at once =). So there are a number of things in here
23that are global variables that might otherwise not be.
24
25Some thanks are in order here to Marcus Comstedt for providing an ISO9660
26implementation that was easy enough to understand without downloading the
27full spec =). Thanks also in order to the creators of the BSD and Linux
28ISO9660 systems, as these were used as references as well.
29
30*/
31
32#include <iso9660/iso9660.h>
33#include <xetypes.h>
34#include <ppc/atomic.h>
35#include <diskio/disc_io.h>
36#include <fcntl.h>
37#include <sys/dirent.h>
38#include <debug.h>
39
40#include <stdio.h>
41#include <ctype.h>
42#include <string.h>
43#include <malloc.h>
44
45#define MAX_FN_LEN 256
46#define DBG_NOTICE "[iso9660] Notice: "
47#define DBG_ERROR "[iso9660] Error: "
48
49#define dbglog(sev,prm...) printf(sev prm);
50
51#define O_DIR 0x100000
52#define MAX_ISO_FILES 8
53
54static DISC_INTERFACE * iso9660_dev = NULL;
55static bool is_mass = false;
56static bool is_mounted = false;
57
58int iso_reset();
59static int init_percd();
60static int percd_done;
61
62// hack for ISOhybrid support in the XeLL
63static bool read_iso9660_sectors(DISC_INTERFACE * dev, sec_t sector, sec_t numSectors, void* buffer) {
64 if (!is_mass)
65 {
66 // optical discs have a sector size of 2048 bytes
67 return dev->readSectors(sector, numSectors, buffer);
68 }
69 else
70 {
71 // mass storage devices have a sector size of 512 bytes
72 // multiply everything by 4 to get 2048 bytes out of it
73 sec_t trueSector = sector * 4;
74 sec_t trueNumSectors = numSectors * 4;
75 //dbglog(DBG_NOTICE, "sector %d -> %d (0x%x)\n", sector, trueSector, trueSector * 512);
76 //dbglog(DBG_NOTICE, "length %d -> %d (0x%x)\n", numSectors, trueNumSectors, trueNumSectors * 512);
77 return dev->readSectors(trueSector, trueNumSectors, buffer);
78 }
79}
80
81/********************************************************************************/
82/* Low-level Joliet utils */
83
84/* Joliet UCS is big endian */
85static void utf2ucs(char * ucs, const char * utf) {
86 int c;
87
88 do {
89 c = *utf++;
90 if (c <= 0x7f) {
91 } else if (c < 0xc0) {
92 c = (c & 0x1f) << 6;
93 c |= (*utf++) & 0x3f;
94 } else {
95 c = (c & 0x0f) << 12;
96 c |= ((*utf++) & 0x3f) << 6;
97 c |= (*utf++) & 0x3f;
98 }
99 *ucs++ = c >> 8;
100 *ucs++ = c & 0xff;
101 } while (c);
102}
103
104static void ucs2utfn(char * utf, const char * ucs, size_t len) {
105 int c;
106
107 len = len / 2;
108 while (len) {
109 len--;
110 c = (*ucs++) << 8;
111 c |= *ucs++;
112 if (c == ';') break;
113 if (c <= 0x7f) {
114 *utf++ = c;
115 } else if (c <= 0x7ff) {
116 *utf++ = 0xc0 | (c >> 6);
117 *utf++ = 0x80 | (c & 0x3f);
118 } else {
119 *utf++ = 0xe0 | (c >> 12);
120 *utf++ = 0x80 | ((c >> 6) & 0x3f);
121 *utf++ = 0x80 | (c & 0x3f);
122 }
123 }
124 *utf = 0;
125}
126
127static int ucscompare(const char * isofn, const char * normalfn, int isosize) {
128 int i, c0, c1 = 0;
129
130 /* Compare ISO name */
131 for (i=0; i<isosize; i+=2) {
132 c0 = ((int)isofn[i] << 8) | ((int)isofn[i+1]);
133 c1 = ((int)normalfn[i] << 8) | ((int)normalfn[i+1]);
134
135 if (c0 == ';') break;
136
137 /* Otherwise, compare the chars normally */
138 if (tolower(c0) != tolower(c1))
139 return -1;
140 }
141
142 c1 = ((int)normalfn[i] << 8) | (normalfn[i+1]);
143
144 /* Catch ISO name shorter than normal name */
145 if (c1 != '/' && c1 != '\0')
146 return -1;
147 else
148 return 0;
149}
150
151static int isjoliet(char * p) {
152 if (p[0] == '%' && p[1] == '/') {
153 switch (p[2]) {
154 case '@': return 1;
155 case 'C': return 2;
156 case 'E': return 3;
157 }
158 }
159 return 0;
160}
161
162static int joliet;
163
164/********************************************************************************/
165/* Low-level ISO utils */
166
167/* ISO Directory entry */
168typedef struct {
169 u8 length; /* 711 */
171 u8 extent[8]; /* 733 */
172 u8 size[8]; /* 733 */
173 u8 date[7]; /* 7x711 */
176 u8 interleave; /* 711 */
177 u8 vol_sequence[4]; /* 723 */
178 u8 name_len; /* 711 */
179 char name[1];
181
182typedef struct filestruct_s
183{
184 int fd;
186
187/* Util function to reverse the byte order of a u32 */
188static u32 ntohl_32(const void *data) {
189 const u8 *d = (const u8*)data;
190 return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | (d[3] << 0);
191}
192
193/* This seems kinda silly, but it's important since it allows us
194 to do unaligned accesses on a buffer */
195static u32 htohl_32(const void *data) {
196 const u8 *d = (const u8*)data;
197 return (d[0] << 0) | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
198}
199
200/* Read red-book section 7.1.1 number (8 bit) */
201/* static u8 iso_711(const u8 *from) { return (*from & 0xff); } */
202
203/* Read red-book section 7.3.3 number (32 bit LE / 32 bit BE) */
204static u32 iso_733(const u8 *from) { return htohl_32(from); }
205
206
207/********************************************************************************/
208/* Low-level block cacheing routines. This implements a simple queue-based
209 LRU/MRU cacheing system. Whenever a block is requested, it will be placed
210 on the MRU end of the queue. As more blocks are loaded than can fit in
211 the cache, blocks are deleted from the LRU end. */
212
213/* Holds the data for one cache block, and a pointer to the next one.
214 As sectors are read from the disc, they are added to the front of
215 this cache. As the cache fills up, sectors are removed from the end
216 of it. */
217typedef struct {
218 s32 sector; /* CD sector */
219 u8 data[2048]; /* Sector data */
221
222/* List of cache blocks (ordered least recently used to most recently) */
223#define NUM_CACHE_BLOCKS 16
224static cache_block_t *icache[NUM_CACHE_BLOCKS]; /* inode cache */
225static cache_block_t *dcache[NUM_CACHE_BLOCKS]; /* data cache */
226
227/* Cache modification mutex */
228static unsigned int * cache_mutex;
229
230/* Clears all cache blocks */
231static void bclear_cache(cache_block_t **cache) {
232 int i;
233
234 lock(cache_mutex);
235 for (i=0; i<NUM_CACHE_BLOCKS; i++)
236 cache[i]->sector = -1;
237 unlock(cache_mutex);
238}
239
240/* Graduate a block from its current position to the MRU end of the cache */
241static void bgrad_cache(cache_block_t **cache, int block) {
242 int i;
243 cache_block_t *tmp;
244
245 /* Don't try it with the end block */
246 if (block < 0 || block >= (NUM_CACHE_BLOCKS-1)) return;
247
248 /* Make a copy and scoot everything down */
249 tmp = cache[block];
250 for (i=block; i<(NUM_CACHE_BLOCKS - 1); i++)
251 cache[i] = cache[i+1];
252 cache[NUM_CACHE_BLOCKS-1] = tmp;
253}
254
255/* Pulls the requested sector into a cache block and returns the cache
256 block index. Note that the sector in question may already be in the
257 cache, in which case it just returns the containing block. */
258static void iso_break_all();
259static int bread_cache(cache_block_t **cache, u32 sector) {
260 int i, j, rv;
261
262 rv = -1;
263 lock(cache_mutex);
264
265 /* Look for a pre-existing cache block */
266 for (i=NUM_CACHE_BLOCKS-1; i>=0; i--) {
267 if (cache[i]->sector == sector) {
268 bgrad_cache(cache, i);
269 rv = NUM_CACHE_BLOCKS - 1;
270 goto bread_exit;
271 }
272 }
273
274 /* If not, look for an open cache slot; if we find one, use it */
275 for (i=0; i<NUM_CACHE_BLOCKS; i++) {
276 if (cache[i]->sector == -1) break;
277 }
278
279 /* If we didn't find one, kick an LRU block out of cache */
280 if (i >= NUM_CACHE_BLOCKS) { i = 0; }
281
282 /* Load the requested block */
283 j = read_iso9660_sectors(iso9660_dev, sector, 1, cache[i]->data);
284 if (j < 0) {
285
287 unlock(cache_mutex);
288 iso_reset();
289 } else
290 dbglog(DBG_ERROR, "fs_iso9660: can't read_sectors for %d: %d\n", (unsigned int)sector, j);
291
292 rv = -1;
293 goto bread_exit;
294 }
295 cache[i]->sector = sector;
296
297 /* Move it to the most-recently-used position */
298 bgrad_cache(cache, i);
299 rv = NUM_CACHE_BLOCKS - 1;
300
301 /* Return the new cache block index */
302bread_exit:
303 unlock(cache_mutex);
304 return rv;
305}
306
307/* read data block */
308static int bdread(u32 sector) {
309 return bread_cache(dcache, sector);
310}
311
312/* read inode block */
313static int biread(u32 sector) {
314 return bread_cache(icache, sector);
315}
316
317/* Clear both caches */
318static void bclear() {
319 bclear_cache(dcache);
320 bclear_cache(icache);
321}
322
323/********************************************************************************/
324/* Higher-level ISO9660 primitives */
325
326/* Root FS session location (in sectors) */
327static u32 session_base = 0;
328
329/* Root directory extent and size in bytes */
330static u32 root_extent = 0, root_size = 0;
331
332/* Root dirent */
333static iso_dirent_t root_dirent;
334
335
336/* Per-disc initialization; this is done every time it's discovered that
337 a new CD has been inserted. */
338static int init_percd() {
339 int i, blk;
340
341 //dbglog(DBG_NOTICE, "fs_iso9660: disc change detected\n");
342
343 /* Start off with no cached blocks and no open files*/
344 iso_reset();
345
346 /* Check for joliet extensions */
347 joliet = 0;
348 for (i=1; i<=3; i++) {
349 blk = biread(session_base + i + 16);
350 if (blk < 0) return blk;
351 if (memcmp((char *)icache[blk]->data, "\02CD001", 6) == 0) {
352 joliet = isjoliet((char *)icache[blk]->data+88);
353 dbglog(DBG_NOTICE, " (joliet level %d extensions detected)\n", joliet);
354 if (joliet) break;
355 }
356 }
357
358 /* If that failed, go after standard/RockRidge ISO */
359 if (!joliet) {
360 /* Grab and check the volume descriptor */
361 blk = biread(session_base + 16);
362 if (blk < 0) return i;
363 if (memcmp((char*)icache[blk]->data, "\01CD001", 6)) {
364 dbglog(DBG_ERROR, "fs_iso9660: disc is not iso9660\r\n");
365 return -1;
366 }
367 }
368
369 /* Locate the root directory */
370 memcpy(&root_dirent, icache[blk]->data+156, sizeof(iso_dirent_t));
371 root_extent = iso_733(root_dirent.extent);
372 root_size = iso_733(root_dirent.size);
373
374 return 0;
375}
376
377/* Compare an ISO9660 filename against a normal filename. This takes into
378 account the version code on the end and is not case sensitive. Also
379 takes into account the trailing period that some CD burning software
380 adds. */
381static int fncompare(const char *isofn, int isosize, const char *normalfn) {
382 //printf("Comparing %s against %s\n", isofn, normalfn);
383 int i;
384
385 /* Compare ISO name */
386 for (i=0; i<isosize; i++) {
387 /* Weed out version codes */
388 if (isofn[i] == ';') break;
389
390 /* Deal with crap '.' at end of filenames */
391 if (isofn[i] == '.' &&
392 (i == (isosize-1) || isofn[i+1] == ';'))
393 break;
394
395 /* Otherwise, compare the chars normally */
396 if (tolower(isofn[i]) != tolower(normalfn[i]))
397 return -1;
398 }
399
400 /* Catch ISO name shorter than normal name */
401 if (normalfn[i] != '/' && normalfn[i] != '\0')
402 return -1;
403 else
404 return 0;
405}
406
407/* Locate an ISO9660 object in the given directory; this can be a directory or
408 a file, it works fine for either one. Pass in:
409
410 fn: object filename (relative to the passed directory)
411 dir: 0 if looking for a file, 1 if looking for a dir
412 dir_extent: directory extent to start with
413 dir_size: directory size (in bytes)
414
415 It will return a pointer to a transient dirent buffer (i.e., don't
416 expect this buffer to stay around much longer than the call itself).
417 */
418static iso_dirent_t *find_object(const char *fn, int dir, u32 dir_extent, u32 dir_size) {
419 int i, c;
420 iso_dirent_t *de;
421
422 /* RockRidge */
423 int len;
424 char *pnt;
425 char rrname[MAX_FN_LEN];
426 int rrnamelen;
427 int size_left;
428
429 /* We need this to be signed for our while loop to end properly */
430 size_left = (int)dir_size;
431
432 /* Joliet */
433 char * ucsname = rrname;
434
435 /* If this is a Joliet CD, then UCSify the name */
436 if (joliet)
437 utf2ucs(ucsname, fn);
438
439 while (size_left > 0) {
440 c = biread(dir_extent);
441 if (c < 0) return NULL;
442
443 for (i=0; i<2048 && i<size_left; ) {
444 /* Locate the current dirent */
445 de = (iso_dirent_t *)(icache[c]->data + i);
446 if (!de->length) break;
447
448 /* Try the Joliet filename if the CD is a Joliet disc */
449 if (joliet) {
450 if (!ucscompare(de->name, ucsname, de->name_len)) {
451 if (!((dir << 1) ^ de->flags))
452 return de;
453 }
454 } else {
455 /* Assume no Rock Ridge name */
456 rrnamelen = 0;
457
458 /* Check for Rock Ridge NM extension */
459 len = de->length - sizeof(iso_dirent_t)
460 + sizeof(de->name) - de->name_len;
461 pnt = (char *)de + sizeof(iso_dirent_t)
462 - sizeof(de->name) + de->name_len;
463 if ((de->name_len & 1) == 0) {
464 pnt++; len--;
465 }
466 while ((len >= 4) && ((pnt[3] == 1) || (pnt[3] == 2))) {
467 if (strncmp(pnt, "NM", 2) == 0) {
468 rrnamelen = pnt[2] - 5;
469 strncpy(rrname, pnt+5, rrnamelen);
470 rrname[rrnamelen] = 0;
471 }
472 len -= pnt[2];
473 pnt += pnt[2];
474 }
475
476 /* Check the filename against the requested one */
477 if (rrnamelen > 0) {
478 char *p = strchr(fn, '/');
479 int fnlen;
480
481 if (p)
482 fnlen = p - fn;
483 else
484 fnlen = strlen(fn);
485
486 if (!strncasecmp(rrname, fn, fnlen)) {
487 if (!((dir << 1) ^ de->flags))
488 return de;
489 }
490 } else {
491 if (!fncompare(de->name, de->name_len, fn)) {
492 if (!((dir << 1) ^ de->flags))
493 return de;
494 }
495 }
496 }
497
498 i += de->length;
499 }
500
501 dir_extent++;
502 size_left -= 2048;
503 }
504
505 return NULL;
506}
507
508/* Locate an ISO9660 object anywhere on the disc, starting at the root,
509 and expecting a fully qualified path name. This is analogous to find_object
510 but it searches with the path in mind.
511
512 fn: object filename (relative to the passed directory)
513 dir: 0 if looking for a file, 1 if looking for a dir
514 dir_extent: directory extent to start with
515 dir_size: directory size (in bytes)
516
517 It will return a pointer to a transient dirent buffer (i.e., don't
518 expect this buffer to stay around much longer than the call itself).
519 */
520static iso_dirent_t *find_object_path(const char *fn, int dir, iso_dirent_t *start) {
521 char *cur;
522
523 /* If the object is in a sub-tree, traverse the trees looking
524 for the right directory */
525 while ((cur = strchr(fn, '/'))) {
526 if (cur != fn) {
527 /* Note: trailing path parts don't matter since find_object
528 only compares based on the FN length on the disc. */
529 start = find_object(fn, 1, iso_733(start->extent), iso_733(start->size));
530 if (start == NULL)
531 return NULL;
532 }
533 fn = cur + 1;
534 }
535
536 /* Locate the file in the resulting directory */
537 if (*fn) {
538 start = find_object(fn, dir, iso_733(start->extent), iso_733(start->size));
539 return start;
540 }
541 else {
542 if (!dir)
543 return NULL;
544 else
545 return start;
546 }
547}
548
549/********************************************************************************/
550/* File primitives */
551
552/* File handles.. I could probably do this with a linked list, but I'm just
553 too lazy right now. =) */
554static struct {
555 u32 first_extent; /* First sector */
556 int dir; /* >0 if a directory */
557 u32 ptr; /* Current read position in bytes */
558 u32 size; /* Length of file in bytes */
559 struct dirent dirent; /* A static dirent to pass back to clients */
560 int broken; /* >0 if the CD has been swapped out since open */
561} fh[MAX_ISO_FILES];
562
563/* Mutex for file handles */
564static unsigned int * fh_mutex;
565
566/* Break all of our open file descriptor. This is necessary when the disc
567 is changed so that we don't accidentally try to keep on doing stuff
568 with the old info. As files are closed and re-opened, the broken flag
569 will be cleared. */
570static void iso_break_all() {
571 int i;
572
573 lock(fh_mutex);
574 for (i=0; i<MAX_ISO_FILES; i++)
575 fh[i].broken = 1;
576 unlock(fh_mutex);
577}
578
579/* Open a file or directory */
580static int iso_open(const char *fn, int mode) {
581 int fd;
582 iso_dirent_t *de;
583
584 if (memcmp(fn, "dvd:/", 6))
585 fn += 5;
586 else if (memcmp(fn, "dvd", 3) && memcmp(fn + 4, ":/", 2))
587 fn += 6;
588
589 /* Make sure they don't want to open things as writeable */
590 if (mode != O_RDONLY && mode != O_DIR)
591 return 0;
592
593 /* Do this only when we need to (this is still imperfect) */
594 if (!percd_done && init_percd() < 0)
595 return 0;
596 percd_done = 1;
597
598 /* Find the file we want */
599 de = find_object_path(fn, (mode & O_DIR)?1:0, &root_dirent);
600 if (!de)
601 return 0;
602
603 /* Find a free file handle */
604 lock(fh_mutex);
605 for (fd=0; fd<MAX_ISO_FILES; fd++){
606 if (fh[fd].first_extent == 0) {
607 fh[fd].first_extent = -1;
608 break;
609 }
610 }
611 unlock(fh_mutex);
612 if (fd >= MAX_ISO_FILES)
613 return 0;
614
615 /* Fill in the file handle and return the fd */
616 fh[fd].first_extent = iso_733(de->extent);
617 fh[fd].dir = (mode & O_DIR)?1:0;
618 fh[fd].ptr = 0;
619 fh[fd].size = iso_733(de->size);
620 fh[fd].broken = 0;
621
622 return fd;
623}
624
625/* Close a file or directory */
626static void iso_close(int fd) {
627 /* Check that the fd is valid */
628 if (fd < MAX_ISO_FILES) {
629 /* No need to lock the mutex: this is an atomic op */
630 fh[fd].first_extent = 0;
631 }
632}
633
634/* Read from a file */
635static ssize_t iso_read(int fd, void *buf, size_t bytes) {
636 int rv, toread, thissect, c;
637 u8 * outbuf;
638
639 /* Check that the fd is valid */
640 if (fd >= MAX_ISO_FILES || fh[fd].first_extent == 0 || fh[fd].broken)
641 return -1;
642
643 rv = 0;
644 outbuf = (u8 *)buf;
645
646 /* Read zero or more sectors into the buffer from the current pos */
647 while (bytes > 0) {
648 /* Figure out how much we still need to read */
649 toread = (bytes > (fh[fd].size - fh[fd].ptr)) ?
650 fh[fd].size - fh[fd].ptr : bytes;
651 if (toread == 0) break;
652
653 /* How much more can we read in the current sector? */
654 thissect = 2048 - (fh[fd].ptr % 2048);
655
656 /* If we're on a sector boundary and we have more than one
657 full sector to read, then short-circuit the cache here
658 and use the multi-sector reads from the CD unit (this
659 should theoretically be a lot faster) */
660 /* XXX This code isn't actually faster, but I'm leaving it
661 here commented out in case we could find a better use
662 for it later than speed (i.e., preventing thread context
663 switches). */
664 if (thissect == 2048 && toread >= 2048) {
665 // Round it off to an even sector count
666 thissect = toread / 2048;
667 if (thissect>32) thissect=32; // 64K max block size
668 toread = thissect * 2048;
669
670 /*printf("cdrom: short-circuit read for %d sectors\n",
671 thissect);*/
672
673 // Do the read
674 if (read_iso9660_sectors(iso9660_dev, fh[fd].first_extent + fh[fd].ptr/2048, thissect, outbuf) < 0)
675 return -2; // Something went wrong...
676 } else {
677 toread = (toread > thissect) ? thissect : toread;
678
679 /* Do the read */
680 c = bdread(fh[fd].first_extent + fh[fd].ptr/2048);
681 if (c < 0)
682 return -3;
683 memcpy(outbuf, dcache[c]->data + (fh[fd].ptr%2048), toread);
684 }
685
686 /* Adjust pointers */
687 outbuf += toread;
688 fh[fd].ptr += toread;
689 bytes -= toread;
690 rv += toread;
691 }
692
693 return rv;
694}
695
696/* Seek elsewhere in a file */
697static off_t iso_seek(int fd, off_t offset, int whence) {
698 /* Check that the fd is valid */
699 if (fd>=MAX_ISO_FILES || fh[fd].first_extent==0 || fh[fd].broken)
700 return -1;
701
702 /* Update current position according to arguments */
703 switch (whence) {
704 case SEEK_SET:
705 fh[fd].ptr = offset;
706 break;
707 case SEEK_CUR:
708 fh[fd].ptr += offset;
709 break;
710 case SEEK_END:
711 fh[fd].ptr = fh[fd].size + offset;
712 break;
713 default:
714 return -1;
715 }
716
717 /* Check bounds */
718 if (fh[fd].ptr < 0) fh[fd].ptr = 0;
719 if (fh[fd].ptr > fh[fd].size) fh[fd].ptr = fh[fd].size;
720
721 return fh[fd].ptr;
722}
723
724/* Tell where in the file we are */
725static off_t iso_tell(int fd) {
726 if (fd>=MAX_ISO_FILES || fh[fd].first_extent==0 || fh[fd].broken)
727 return -1;
728
729 return fh[fd].ptr;
730}
731
732/* Tell how big the file is */
733static size_t iso_total(int fd) {
734 if (fd>=MAX_ISO_FILES || fh[fd].first_extent==0 || fh[fd].broken)
735 return -1;
736
737 return fh[fd].size;
738}
739
740/* Helper function for readdir: post-processes an ISO filename to make
741 it a bit prettier. */
742static void fn_postprocess(char *fnin) {
743 char * fn = fnin;
744
745 while (*fn && *fn != ';') {
746 *fn = tolower(*fn);
747 fn++;
748 }
749 *fn = 0;
750
751 /* Strip trailing dots */
752 if (fn > fnin && fn[-1] == '.') {
753 fn[-1] = 0;
754 }
755}
756
757/* Read a directory entry */
758static struct dirent *iso_readdir(int fd) {
759 int c;
760 iso_dirent_t *de;
761
762 /* RockRidge */
763 int len;
764 char *pnt;
765
766 if (fd>=MAX_ISO_FILES || fh[fd].first_extent==0 || !fh[fd].dir || fh[fd].broken)
767 return NULL;
768
769 /* Scan forwards until we find the next valid entry, an
770 end-of-entry mark, or run out of dir size. */
771 c = -1; de = NULL;
772 while(fh[fd].ptr < fh[fd].size) {
773 /* Get the current dirent block */
774 c = biread(fh[fd].first_extent + fh[fd].ptr/2048);
775 if (c < 0) return NULL;
776
777 de = (iso_dirent_t *)(icache[c]->data + (fh[fd].ptr%2048));
778 if (de->length) break;
779
780 /* Skip to the next sector */
781 fh[fd].ptr += 2048 - (fh[fd].ptr%2048);
782 }
783 if (fh[fd].ptr >= fh[fd].size) return NULL;
784
785 /* If we're at the first, skip the two blank entries */
786 if (!de->name[0] && de->name_len == 1) {
787 fh[fd].ptr += de->length;
788 de = (iso_dirent_t *)(icache[c]->data + (fh[fd].ptr%2048));
789 fh[fd].ptr += de->length;
790 de = (iso_dirent_t *)(icache[c]->data + (fh[fd].ptr%2048));
791 if (!de->length) return NULL;
792 }
793
794 if (joliet) {
795 ucs2utfn(fh[fd].dirent.d_name, de->name, de->name_len);
796 } else {
797 /* Fill out the VFS dirent */
798 strncpy(fh[fd].dirent.d_name, de->name, de->name_len);
799 fh[fd].dirent.d_name[de->name_len] = 0;
800 fn_postprocess(fh[fd].dirent.d_name);
801
802 /* Check for Rock Ridge NM extension */
803 len = de->length - sizeof(iso_dirent_t) + sizeof(de->name) - de->name_len;
804 pnt = (char*)de + sizeof(iso_dirent_t) - sizeof(de->name) + de->name_len;
805 if ((de->name_len & 1) == 0) {
806 pnt++; len--;
807 }
808 while ((len >= 4) && ((pnt[3] == 1) || (pnt[3] == 2))) {
809 if (strncmp(pnt, "NM", 2) == 0) {
810 strncpy(fh[fd].dirent.d_name, pnt+5, pnt[2] - 5);
811 fh[fd].dirent.d_name[pnt[2] - 5] = 0;
812 }
813 len -= pnt[2];
814 pnt += pnt[2];
815 }
816 }
817
818 //fh[fd].dirent.d_namlen = de->name_len;
819
820 if (de->flags & 2){
821 //fh[fd].dirent.d_reclen = -1;
822 fh[fd].dirent.d_type = DT_DIR;
823 }else{
824 //fh[fd].dirent.d_reclen = iso_733(de->size);
825 fh[fd].dirent.d_type = DT_REG;
826 }
827
828 fh[fd].ptr += de->length;
829
830 return &fh[fd].dirent;
831}
832
834 iso_break_all();
835 bclear();
836 percd_done = 0;
837 return 0;
838}
839
840#if 0
841/* This handler will be called during every vblank. We have to
842 be careful about modifying variables that are in use in the
843 foreground, so instead we'll just set a "dead" flag and next
844 time someone calls in it'll get reset. */
845static int iso_last_status;
846static int iso_vblank_hnd;
847static void iso_vblank(u32 evt) {
848 int status, disc_type;
849
850 /* Get the status. This may fail if a CD operation is in
851 progress in the foreground. */
852 if (cdrom_get_status(&status, &disc_type) < 0)
853 return;
854
855 if (iso_last_status != status) {
856 if (status == CD_STATUS_OPEN || status == CD_STATUS_NO_DISC)
857 percd_done = 0;
858 iso_last_status = status;
859 }
860}
861#endif
862
863/* There's only one ioctl at the moment (re-initialize caches) but you should
864 always clear data and size. */
865static int iso_ioctl(int hnd, void *data, size_t size) {
866 iso_reset();
867
868 return 0;
869}
870
871/* Initialize the file system */
873 int i;
874
875 /* Reset fd's */
876 memset(fh, 0, sizeof(fh));
877
878 /* Mark the first as active so we can have an error FD of zero */
879 fh[0].first_extent = -1;
880
881 /* Init thread mutexes */
882 cache_mutex = malloc(sizeof(u32));
883 fh_mutex = malloc(sizeof(u32));
884 *cache_mutex = 0;
885 *fh_mutex = 0;
886
887 /* Allocate cache block space */
888 for (i=0; i<NUM_CACHE_BLOCKS; i++) {
889 icache[i] = malloc(sizeof(cache_block_t));
890 icache[i]->sector = -1;
891 dcache[i] = malloc(sizeof(cache_block_t));
892 dcache[i]->sector = -1;
893 }
894
895 percd_done = 0;
896
897#if 0
898 iso_last_status = -1;
899#endif
900
901 return 0;
902}
903
904/* De-init the file system */
906 int i;
907
908 /* Dealloc cache block space */
909 for (i=0; i<NUM_CACHE_BLOCKS; i++) {
910 free(icache[i]);
911 free(dcache[i]);
912 }
913
914 /* Free muteces */
915 if (cache_mutex != NULL)
916 free(cache_mutex);
917 if (fh_mutex != NULL)
918 free(fh_mutex);
919 cache_mutex = fh_mutex = NULL;
920
921 return 0;
922}
923
924static int _ISO9660_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
925{
926 FILE_STRUCT* file = (FILE_STRUCT*)fileStruct;
927 file->fd = iso_open(path, flags);
928 return (int)file;
929}
930
931static int _ISO9660_close_r(struct _reent *r, int fd)
932{
933 fd = ((FILE_STRUCT*)fd)->fd;
934 iso_close(fd);
935 return 0;
936}
937
938static ssize_t _ISO9660_read_r(struct _reent *r, int fd, char *ptr, size_t len)
939{
940 fd = ((FILE_STRUCT*)fd)->fd;
941 return iso_read(fd, ptr, len);
942}
943
944static off_t _ISO9660_seek_r(struct _reent *r, int fd, off_t pos, int dir)
945{
946 fd = ((FILE_STRUCT*)fd)->fd;
947 return iso_seek(fd, pos, dir);
948}
949
950static int _ISO9660_fstat_r(struct _reent *r, int fd, struct stat *st)
951{
952 fd = ((FILE_STRUCT*)fd)->fd;
953 memset(st, 0, sizeof(stat));
954 st->st_size = iso_total(fd);
955 st->st_mode = S_IFREG;
956 st->st_blksize = 65536;
957 return 0;
958}
959
960static int _ISO9660_stat_r(struct _reent *r, const char *path, struct stat *st)
961{
962 int fd = iso_open(path, O_RDONLY);
963 memset(st, 0, sizeof(stat));
964 st->st_size = iso_total(fd);
965 st->st_mode = S_IFREG;
966 st->st_blksize = 65536;
967 iso_close(fd);
968 return 0;
969}
970
971static int _ISO9660_chdir_r(struct _reent *r, const char *path)
972{
973 /*DIR_ENTRY entry;
974 MOUNT_DESCR *mdescr;
975
976 mdescr = _ISO9660_getMountDescrFromPath(path, NULL);
977 if (mdescr == NULL)
978 {
979 r->_errno = ENODEV;
980 return -1;
981 }
982
983 if (!entry_from_path(mdescr, &entry, path))
984 {
985 r->_errno = ENOENT;
986 return -1;
987 }
988 else if (!is_dir(&entry))
989 {
990 r->_errno = ENOTDIR;
991 return -1;
992 }
993
994 mdescr->iso_currententry = entry.path_entry;
995 if (entry.children)
996 free(entry.children);
997*/
998 return 0; //Dunno what to do about this one yet...
999}
1000
1001static DIR_ITER* _ISO9660_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path)
1002{
1003 /*DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
1004 MOUNT_DESCR *mdescr;
1005
1006 mdescr = _ISO9660_getMountDescrFromPath(path, NULL);
1007 if (mdescr == NULL)
1008 {
1009 r->_errno = ENODEV;
1010 return NULL;
1011 }
1012
1013 if (!entry_from_path(mdescr, &state->entry, path))
1014 {
1015 r->_errno = ENOENT;
1016 return NULL;
1017 }
1018 else if (!is_dir(&state->entry))
1019 {
1020 r->_errno = ENOTDIR;
1021 return NULL;
1022 }
1023
1024 state->index = 0;
1025 state->inUse = true;
1026 return dirState;*/
1027 return dirState; //Dunno what to do about this one yet...
1028}
1029
1030static int _ISO9660_dirreset_r(struct _reent *r, DIR_ITER *dirState)
1031{
1032 /*DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
1033
1034 if (!state->inUse)
1035 {
1036 r->_errno = EBADF;
1037 return -1;
1038 }
1039
1040 state->index = 0;*/
1041 return 0; //Dunno what to do about this one yet...
1042}
1043
1044static int _ISO9660_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st)
1045{
1046 /*DIR_ENTRY *entry;
1047 DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
1048
1049 if (!state->inUse)
1050 {
1051 r->_errno = EBADF;
1052 return -1;
1053 }
1054
1055 if (state->index >= state->entry.fileCount)
1056 {
1057 r->_errno = ENOENT;
1058 return -1;
1059 }
1060
1061 entry = &state->entry.children[state->index++];
1062 strncpy(filename, entry->name, ISO_MAXPATHLEN - 1);
1063 stat_entry(entry, st);*/
1064 return 0; //Dunno what to do about this one yet...
1065}
1066
1067static int _ISO9660_dirclose_r(struct _reent *r, DIR_ITER *dirState)
1068{
1069 /*DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
1070
1071 if (!state->inUse)
1072 {
1073 r->_errno = EBADF;
1074 return -1;
1075 }
1076
1077 state->inUse = false;
1078 if (state->entry.children)
1079 free(state->entry.children);*/
1080 return 0; //Dunno what to do about this one yet...
1081}
1082
1083static int _ISO9660_statvfs_r(struct _reent *r, const char *path, struct statvfs *buf)
1084{
1085 // FAT clusters = POSIX blocks
1086 buf->f_bsize = 0x800; // File system block size.
1087 buf->f_frsize = 0x800; // Fundamental file system block size.
1088
1089 //buf->f_blocks = totalsectors; // Total number of blocks on file system in units of f_frsize.
1090 buf->f_bfree = 0; // Total number of free blocks.
1091 buf->f_bavail = 0; // Number of free blocks available to non-privileged process.
1092
1093 // Treat requests for info on inodes as clusters
1094 //buf->f_files = totalentries; // Total number of file serial numbers.
1095 buf->f_ffree = 0; // Total number of free file serial numbers.
1096 buf->f_favail = 0; // Number of file serial numbers available to non-privileged process.
1097
1098 // File system ID. 32bit ioType value
1099 buf->f_fsid = 0; //??!!?
1100
1101 // Bit mask of f_flag values.
1102 buf->f_flag = ST_NOSUID // No support for ST_ISUID and ST_ISGID file mode bits
1103 | ST_RDONLY; // Read only file system
1104 // Maximum filename length.
1105 buf->f_namemax = 208;
1106 return 0;
1107}
1108
1109static const devoptab_t dotab_iso9660 =
1110{
1111 NULL,
1112 sizeof(FILE_STRUCT),//Dunno?!
1113 _ISO9660_open_r,
1114 _ISO9660_close_r,
1115 NULL,
1116 _ISO9660_read_r,
1117 _ISO9660_seek_r,
1118 _ISO9660_fstat_r,
1119 _ISO9660_stat_r,
1120 NULL,
1121 NULL,
1122 _ISO9660_chdir_r,
1123 NULL,
1124 NULL,
1125 sizeof(FILE_STRUCT),//Dunno?!
1126 _ISO9660_diropen_r,
1127 _ISO9660_dirreset_r,
1128 _ISO9660_dirnext_r,
1129 _ISO9660_dirclose_r,
1130 _ISO9660_statvfs_r,
1131 NULL, // device ftruncate_r
1132 NULL, // device fsync_r
1133 NULL // device data
1134};
1135
1136static int verify_iso9660_disc(const DISC_INTERFACE *di)
1137{
1138 char sector_data[2048];
1139 int is_joliet, i, blk = 0;
1140
1141 /* Check for joliet extensions */
1142 is_joliet = 0;
1143 for (i=1; i<=3; i++) {
1144 blk = read_iso9660_sectors(di, 16, 1, sector_data);
1145 if (blk < 0) return blk;
1146 if (memcmp(sector_data, "\02CD001", 6) == 0) {
1147 is_joliet = isjoliet(sector_data+88);
1148 if (is_joliet) break;
1149 }
1150 }
1151
1152 /* If that failed, go after standard/RockRidge ISO */
1153 if (!is_joliet) {
1154 /* Grab and check the volume descriptor */
1155 blk = read_iso9660_sectors(di, 16, 1, sector_data);
1156 if (blk < 0) return i;
1157 if (memcmp(sector_data, "\01CD001", 6) != 0) {
1158 return -1;
1159 }
1160 }
1161
1162 return 0;
1163}
1164
1165bool ISO9660_Mount(const char* name, const DISC_INTERFACE *disc_interface)
1166{
1167 char *nameCopy;
1168 devoptab_t *devops = NULL;
1169 char devname[10];
1170
1171 // We already have an ISO9660 drive mounted
1172 if (is_mounted)
1173 return false;
1174
1175 if (!name || strlen(name) > 8 || !disc_interface)
1176 return false;
1177
1178 if (!disc_interface->startup())
1179 return false;
1180
1181 if (!disc_interface->isInserted())
1182 return false;
1183
1184 // Check if it's a mass storage device or not
1185 is_mass = disc_interface->ioType == FEATURE_XENON_USB || disc_interface->ioType == FEATURE_XENON_ATA;
1186
1187 // Verify that it's actually an ISO9660 formatted disc and not another format or not inserted
1188 if (verify_iso9660_disc(disc_interface) != 0)
1189 return false;
1190
1191 sprintf(devname, "%s:", name);
1192 if (FindDevice(devname) >= 0)
1193 return false;
1194
1195 devops = malloc(sizeof(dotab_iso9660) + strlen(name) + 1);
1196 if (!devops)
1197 return false;
1198
1199 // Use the space allocated at the end of the devoptab struct for storing the name
1200 nameCopy = (char*) (devops + 1);
1201
1202 iso9660_dev = disc_interface;
1204
1205 // Add an entry for this device to the devoptab table
1206 memcpy(devops, &dotab_iso9660, sizeof(dotab_iso9660));
1207 strcpy(nameCopy, name);
1208 devops->name = nameCopy;
1209 if (AddDevice(devops) < 0)
1210 {
1211 free(devops);
1212 return false;
1213 }
1214
1215 is_mounted = true;
1216
1217 return true;
1218}
1219
1220static bool check_dev_name(const char* name, char *devname, size_t devname_size)
1221{
1222 size_t len;
1223
1224 if (!name)
1225 return false;
1226
1227 len = strlen(name);
1228 if (len == 0 || len > devname_size-2)
1229 return false;
1230
1231 // append ':' if missing
1232 strcpy(devname, name);
1233 if (devname[len-1] != ':')
1234 strcat(devname, ":");
1235
1236 return true;
1237}
1238
1239bool ISO9660_Unmount(const char* name)
1240{
1241 char devname[11];
1242 if (! check_dev_name(name, devname, sizeof(devname)))
1243 return false;
1245 if (RemoveDevice(name) == -1)
1246 return false;
1247 is_mounted = false;
1248 return true;
1249}
1250
1251const char *ISO9660_GetVolumeLabel(const char *name)
1252{
1253 char devname[11];
1254 if (!check_dev_name(name, devname, sizeof(devname)))
1255 return NULL;
1256 return name;
1257}
void lock(unsigned int *lock)
void unlock(unsigned int *lock)
#define NULL
Definition: def.h:47
#define DT_DIR
Definition: dirent.h:13
#define DT_REG
Definition: dirent.h:15
#define FEATURE_XENON_ATA
Definition: disc_io.h:41
#define DISKIO_ERROR_NO_MEDIA
Definition: disc_io.h:37
#define FEATURE_XENON_USB
Definition: disc_io.h:43
uint32_t sec_t
Definition: disc_io.h:45
u32 status
Definition: ehci_defs.h:15
#define DBG_ERROR
Definition: iso9660.c:47
#define NUM_CACHE_BLOCKS
Definition: iso9660.c:223
int broken
Definition: iso9660.c:560
struct filestruct_s FILE_STRUCT
#define dbglog(sev, prm...)
Definition: iso9660.c:49
int iso_reset()
Definition: iso9660.c:833
u32 first_extent
Definition: iso9660.c:555
int fs_iso9660_shutdown()
Definition: iso9660.c:905
bool ISO9660_Mount(const char *name, const DISC_INTERFACE *disc_interface)
Definition: iso9660.c:1165
const char * ISO9660_GetVolumeLabel(const char *name)
Definition: iso9660.c:1251
int dir
Definition: iso9660.c:556
#define DBG_NOTICE
Definition: iso9660.c:46
u32 ptr
Definition: iso9660.c:557
bool ISO9660_Unmount(const char *name)
Definition: iso9660.c:1239
#define MAX_ISO_FILES
Definition: iso9660.c:52
u32 size
Definition: iso9660.c:558
#define O_DIR
Definition: iso9660.c:51
#define MAX_FN_LEN
Definition: iso9660.c:45
int fs_iso9660_init()
Definition: iso9660.c:872
int stat(const char *file, struct stat *st)
Definition: newlib.c:643
int RemoveDevice(const char *name)
Definition: newlib.c:467
int AddDevice(const devoptab_t *device)
Definition: newlib.c:482
int FindDevice(const char *name)
Definition: newlib.c:443
#define blk(i)
Definition: sha1.c:40
#define ST_RDONLY
Definition: statvfs.h:5
#define ST_NOSUID
Definition: statvfs.h:6
FN_MEDIUM_ISINSERTED isInserted
Definition: disc_io.h:59
FN_MEDIUM_READSECTORS readSectors
Definition: disc_io.h:60
unsigned long ioType
Definition: disc_io.h:56
FN_MEDIUM_STARTUP startup
Definition: disc_io.h:58
const char * name
Definition: iosupport.h:34
Definition: dirent.h:24
char d_name[NAME_MAX+1]
Definition: dirent.h:27
u8 size[8]
Definition: iso9660.c:172
u8 interleave
Definition: iso9660.c:176
char name[1]
Definition: iso9660.c:179
u8 extent[8]
Definition: iso9660.c:171
u8 ext_attr_length
Definition: iso9660.c:170
u8 file_unit_size
Definition: iso9660.c:175
unsigned long f_namemax
Definition: statvfs.h:27
unsigned long f_fsid
Definition: statvfs.h:25
unsigned long f_bsize
Definition: statvfs.h:17
fsblkcnt_t f_bavail
Definition: statvfs.h:21
fsfilcnt_t f_ffree
Definition: statvfs.h:23
fsfilcnt_t f_favail
Definition: statvfs.h:24
fsblkcnt_t f_bfree
Definition: statvfs.h:20
unsigned long f_flag
Definition: statvfs.h:26
unsigned long f_frsize
Definition: statvfs.h:18
u8 c
Definition: xenos_edid.h:7
union @15 data
u8 j
Definition: xenos_edid.h:10
uint8_t u8
8bit unsigned integer
Definition: xetypes.h:12
int32_t s32
32bit signed integer
Definition: xetypes.h:19
uint32_t u32
32bit unsigned integer
Definition: xetypes.h:14