4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 /* #include <debug.h> */
27 /* The iso9660 code from GRUB2 */
28 #define ISO9660_FSTYPE_DIR 0040000
29 #define ISO9660_FSTYPE_REG 0100000
30 #define ISO9660_FSTYPE_SYMLINK 0120000
31 #define ISO9660_FSTYPE_MASK 0170000
33 #define ISO9660_LOG2_BLKSZ 2
34 #define ISO9660_BLKSZ 2048
36 #define ISO9660_RR_DOT 2
37 #define ISO9660_RR_DOTDOT 4
39 #define ISO9660_MAX_DEPTH 8
41 /* The head of a volume descriptor. */
42 struct iso9660_voldesc
{
46 } __attribute__ ((packed
));
48 /* A directory entry. */
52 uint32_t first_sector
;
53 uint32_t first_sector_be
;
60 } __attribute__ ((packed
));
62 /* The primary volume descriptor. Only little endian is used. */
63 struct iso9660_primary_voldesc
{
64 struct iso9660_voldesc voldesc
;
68 uint32_t path_table_size
;
72 struct iso9660_dir rootdir
;
73 } __attribute__ ((packed
));
75 /* A single entry in the path table. */
79 uint32_t first_sector
;
82 } __attribute__ ((packed
));
84 /* An entry in the System Usage area of the directory entry. */
85 struct iso9660_susp_entry
{
90 } __attribute__ ((packed
));
92 /* The CE entry. This is used to describe the next block where data
94 struct iso9660_susp_ce
{
95 struct iso9660_susp_entry entry
;
102 } __attribute__ ((packed
));
104 typedef struct cdrom_boot_dev
{
106 block_dev_desc_t
*phys
;
108 struct iso9660_primary_voldesc voldesc
;
109 struct iso9660_dir rootdir
;
110 uint8_t tmpbuf
[2048];
111 uint32_t sector_in_buf
;
116 static int cdrom_read(cdrom_boot_dev_t
* boot
, uint32_t block
, uint32_t offset
,
117 uint32_t length
, char *dest
)
120 uint32_t transferred
= 0;
122 while (offset
>= 2048) {
128 if (boot
->sector_in_buf
!= block
) {
130 block_read(boot
->phys
->dev
, block
, 1, boot
->tmpbuf
))
132 boot
->sector_in_buf
= block
;
139 memcpy(dest
, &boot
->tmpbuf
[offset
], len
);
148 uint32_t blocks_read
= length
/ 2048;
151 block_read(boot
->phys
->dev
, block
, blocks_read
, dest
))
154 transferred
+= 2048 * blocks_read
;
155 block
+= blocks_read
;
156 dest
+= 2048 * blocks_read
;
157 length
-= 2048 * blocks_read
;
161 if (boot
->sector_in_buf
!= block
) {
163 block_read(boot
->phys
->dev
, block
, 1, boot
->tmpbuf
))
166 boot
->sector_in_buf
= block
;
173 memcpy(dest
, boot
->tmpbuf
, len
);
180 static int iterate_entries(cdrom_boot_dev_t
* dev
, uint32_t block
,
181 uint32_t offset
, uint32_t size
,
182 int (*iter_func
) (struct iso9660_susp_entry
*,
183 void *userdata
), void *userdata
)
185 char *sua
= malloc(size
);
191 /* read the SUA into memory */
192 if (cdrom_read(dev
, block
, offset
, size
, sua
))
197 struct iso9660_susp_entry
*e
= (struct iso9660_susp_entry
*)ptr
;
199 /* In case of CE entry get the new SUA and try again */
200 while (e
->sig
[0] == 'C' && e
->sig
[1] == 'E') {
201 struct iso9660_susp_ce
*ce
=
202 (struct iso9660_susp_ce
*)e
;
212 if (cdrom_read(dev
, block
, offset
, size
, sua
))
215 e
= (struct iso9660_susp_entry
*)ptr
;
218 /* ST entry means STOP NOW */
219 if (e
->sig
[0] == 'S' && e
->sig
[1] == 'T') {
224 /* Call the user function. If it returns non-zero value, break iteration */
225 if (iter_func(e
, userdata
)) {
231 } while (ptr
< sua
+ size
- 1);
240 struct iso9660_susp_entry
*entry
;
244 static int my_iteration(struct iso9660_susp_entry
*e
, struct tmp_data
*d
)
246 if (e
->sig
[0] == d
->sig
[0] && e
->sig
[1] == d
->sig
[1]) {
247 d
->entry
= malloc(e
->len
);
248 memcpy(d
->entry
, e
, e
->len
);
254 static struct iso9660_susp_entry
*get_entry(cdrom_boot_dev_t
* dev
,
255 uint32_t block
, uint32_t offset
,
256 uint32_t size
, uint8_t sig
[2])
259 context_t
*ctx
= dev
->ctx
;
266 iterate_entries(dev
, block
, offset
, size
,
267 (int (*)(struct iso9660_susp_entry
*, void *userdata
))
273 static int fs_cdrom_find_file(cdrom_boot_dev_t
* this, char *filename
,
274 uint32_t * fblock
, uint32_t * fsize
)
276 char *local_filename
;
277 char *elements
[ISO9660_MAX_DEPTH
+ 1];
286 struct iso9660_dir dirent
;
288 if (filename
[0] == '/')
291 local_filename
= malloc(strlen(filename
) + 1);
292 memcpy(local_filename
, filename
, strlen(filename
) + 1);
294 /* Split the filename into separate elements */
295 elements
[depth
++] = local_filename
;
296 for (i
= 0; i
< strlen(filename
) - 1; i
++) {
297 if (local_filename
[i
] == '/') {
298 local_filename
[i
] = 0;
299 elements
[depth
++] = &local_filename
[i
+ 1];
303 block
= this->voldesc
.rootdir
.first_sector_be
;
304 size
= this->voldesc
.rootdir
.size_be
;
306 for (j
= 0; j
< depth
; j
++) {
309 while (!found
&& offset
< size
) {
310 if (cdrom_read(this, block
, offset
, sizeof(dirent
),
315 offset
= (offset
/ 2048 + 1) * 2048;
321 int nameoffset
= offset
+ sizeof(dirent
);
323 (sizeof(dirent
) + dirent
.namelen
+ 1 -
324 (dirent
.namelen
% 2));
325 int sua_size
= dirent
.len
- sua_off
;
327 sua_off
+= offset
+ this->susp_skip
;
333 struct iso9660_susp_entry
*entry
=
334 get_entry(this, block
,
337 (unsigned char *)"NM");
340 data
[0] & ISO9660_RR_DOT
)
347 if (entry
->len
> 5) {
373 name
= malloc(dirent
.namelen
+ 1);
376 (this, block
, nameoffset
,
377 dirent
.namelen
, name
))
380 name
[dirent
.namelen
] = '\0';
381 for (i
= dirent
.namelen
; i
; i
--) {
382 if (name
[i
] == ';') {
388 if (dirent
.namelen
== 1 && name
[0] == 0)
390 else if (dirent
.namelen
== 1
398 if (!strcasecmp(elements
[j
], fname
)) {
399 int type
= dirent
.flags
& 3;
401 if ((j
+ 1 < depth
&& type
== 2)
402 || (j
+ 1 == depth
&& type
!= 2)) {
407 if (fname_alloc
&& fname
)
414 offset
+= dirent
.len
;
418 /* Entry found. Go to the next level */
419 block
= dirent
.first_sector_be
;
420 size
= dirent
.size_be
;
425 free(local_filename
);
430 // cdrom_read(this, block, 0, size, buffer);
440 static int fs_cdrom_destroy(cdrom_boot_dev_t
* this)
446 static int fs_cdrom_load_file(cdrom_boot_dev_t
* this, char *filename
,
452 if (fs_cdrom_find_file(this, filename
, &block
, &size
)) {
453 cdrom_read(this, block
, 0, size
, buffer
);
459 static block_dev_desc_t
*get_dev()
461 block_dev_desc_t
*bdev
= NULL
;
464 void *scan_list
= get_scan_list();
466 if(scan_list
== NULL
)
469 for (hnd
= start_unit_scan(scan_list
, &blocksize
);
470 hnd
!= NULL
; hnd
= next_unit_scan(hnd
, &blocksize
)) {
471 if (hnd
->ush_device
.type
== DEV_TYPE_CDROM
) {
472 bdev
= malloc(sizeof(block_dev_desc_t
));
473 memmove(bdev
, &hnd
->ush_device
,
474 sizeof(block_dev_desc_t
));
485 boot_dev_t
*cdrom_create()
488 block_dev_desc_t
*dev
= get_dev();
493 cdrom_boot_dev_t
*boot
= malloc(sizeof(cdrom_boot_dev_t
));
495 asm volatile ("mr %0,%%r1":"=r" (sp
));
501 struct iso9660_susp_entry
*entry
;
503 char huge_array
[1000];
506 (void)huge_array
; /* Placeholder to eat stack */
509 boot
->dev
.load_file
=
510 (int (*)(void *, char *, void *))fs_cdrom_load_file
;
511 boot
->dev
.destroy
= (void (*)(void *))fs_cdrom_destroy
;
514 if (cdrom_read(boot
, 16, 0, sizeof(boot
->voldesc
),
515 (char *)&boot
->voldesc
)) {
518 // if (!phys->block_read(phys->dev, 16, 1, boot->tmpbuf))
520 // D(printf("[BOOT:CD] Cannot read device!\n"));
523 // boot->sector_in_buf = 16;
524 // memcpy(&boot->voldesc, boot->tmpbuf, sizeof(boot->voldesc));
526 if (strncmp((char *)boot
->voldesc
.voldesc
.magic
, "CD001", 5)) {
530 /* Read the system use area and test it to see if SUSP is
532 if (cdrom_read(boot
, boot
->voldesc
.rootdir
.first_sector_be
, 0,
533 sizeof(boot
->rootdir
), (char *)&boot
->rootdir
)) {
536 // if (!phys->block_read(phys->dev, boot->voldesc.rootdir.first_sector_be, 1, boot->tmpbuf))
538 // D(printf("[BOOT:CD] Not an iso9660 filesystem\n"));
541 // boot->sector_in_buf = boot->voldesc.rootdir.first_sector_be;
542 // memcpy(&boot->rootdir, boot->tmpbuf, sizeof(boot->rootdir));
544 sua_pos
= (sizeof(boot
->rootdir
) + boot
->rootdir
.namelen
545 + (boot
->rootdir
.namelen
% 2) - 1);
546 sua_size
= boot
->rootdir
.len
- sua_pos
;
548 sua
= malloc(sua_size
);
549 // memcpy(sua, &boot->tmpbuf[sua_pos], sua_size);
550 cdrom_read(boot
, boot
->voldesc
.rootdir
.first_sector_be
, sua_pos
,
553 entry
= (struct iso9660_susp_entry
*)sua
;
555 if (entry
->sig
[0] == 'S' && entry
->sig
[1] == 'P') {
557 boot
->susp_skip
= entry
->data
[2];
558 entry
= (struct iso9660_susp_entry
*)(sua
+ entry
->len
);
560 /* Iterate through the SUSP entries and look for the RockRidge marker */
564 boot
->voldesc
.rootdir
.first_sector_be
,
565 sua_pos
, sua_size
, (unsigned char *)"RR");
573 // D(printf("%c%c(%d) ", entry->sig[0], entry->sig[1], entry->len));
575 // if (entry->sig[0] == 'R' && entry->sig[1] == 'R')
581 // if (!strncmp(entry->sig, "ST", 2))
584 // if (!strncmp(entry->sig, "CE", 2))
586 // struct iso9660_susp_ce *ce = (struct iso9660_susp_ce *)entry;
587 // sua_size = ce->len_be;
588 // sua_pos = ce->off_be;
590 // sua = malloc(sua_size);
591 // phys->block_read(phys->dev, ce->blk_be, 1, boot->tmpbuf);
592 // memcpy(sua, &boot->tmpbuf[sua_pos], sua_size);
593 // entry = (struct iso9660_susp_entry *)sua;
596 // entry = (struct iso9660_susp_entry *)((char*)entry + entry->len);
597 // } while ((char *)entry < (char*)sua + sua_size - 1);
600 /* D(printf("[BOOT:CD] The CD supports RockRidge extensions\n")); */
608 boot
->dev
.destroy(boot
);