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