| /* |
| * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) |
| * including Rock Ridge Extensions support |
| * |
| * Copyright (C) 1998, 1999 Kousuke Takai <tak@kmc.kyoto-u.ac.jp> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| * MA 02110-1301, USA. |
| */ |
| /* |
| * References: |
| * linux/fs/isofs/rock.[ch] |
| * mkisofs-1.11.1/diag/isoinfo.c |
| * mkisofs-1.11.1/iso9660.h |
| * (all are written by Eric Youngdale) |
| * |
| * Modifications by: |
| * Leonid Lisovskiy <lly@pisem.net> 2003 |
| */ |
| |
| /* |
| * Modified to make it work with FILO |
| * 2003-10 by SONE Takeshi |
| */ |
| |
| #ifdef FSYS_ISO9660 |
| |
| #include "shared.h" |
| #include "filesys.h" |
| #include "iso9660.h" |
| #include "debug.h" |
| |
| #if defined(__sparc__) || defined(__PPC__) |
| #define ENDIAN b |
| #else |
| #define ENDIAN l |
| #endif |
| |
| struct iso_superblock { |
| unsigned long vol_sector; |
| |
| unsigned long file_start; |
| }; |
| |
| #define ISO_SUPER ((struct iso_superblock *)(FSYS_BUF)) |
| #define PRIMDESC ((struct iso_primary_descriptor *)(FSYS_BUF + 2048)) |
| #define DIRREC ((struct iso_directory_record *)(FSYS_BUF + 4096)) |
| #define RRCONT_BUF ((unsigned char *)(FSYS_BUF + 6144)) |
| #define NAME_BUF ((unsigned char *)(FSYS_BUF + 8192)) |
| |
| static int |
| iso9660_devread (int sector, int byte_offset, int byte_len, char *buf) |
| { |
| /* FILO uses 512-byte "soft" sector, and ISO-9660 uses 2048-byte |
| * CD-ROM sector */ |
| return devread(sector<<2, byte_offset, byte_len, buf); |
| } |
| |
| int |
| iso9660_mount (void) |
| { |
| unsigned int sector; |
| |
| /* |
| * Because there is no defined slice type ID for ISO-9660 filesystem, |
| * this test will pass only either (1) if entire disk is used, or |
| * (2) if current partition is BSD style sub-partition whose ID is |
| * ISO-9660. |
| */ |
| /*if ((current_partition != 0xFFFFFF) |
| && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660)) |
| return 0;*/ |
| |
| /* |
| * Currently, only FIRST session of MultiSession disks are supported !!! |
| */ |
| for (sector = 16 ; sector < 32 ; sector++) |
| { |
| if (!iso9660_devread(sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC)) |
| break; |
| /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */ |
| if (CHECK4(&PRIMDESC->type, ISO_VD_PRIMARY, 'C', 'D', '0') |
| && CHECK2(PRIMDESC->id + 3, '0', '1')) |
| { |
| ISO_SUPER->vol_sector = sector; |
| ISO_SUPER->file_start = 0; |
| fsmax = PRIMDESC->volume_space_size.ENDIAN; |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| iso9660_dir (char *dirname) |
| { |
| struct iso_directory_record *idr; |
| RR_ptr_t rr_ptr; |
| struct rock_ridge *ce_ptr; |
| unsigned int pathlen; |
| int size; |
| unsigned int extent; |
| unsigned int rr_len; |
| unsigned char file_type; |
| unsigned char rr_flag; |
| |
| idr = &PRIMDESC->root_directory_record; |
| ISO_SUPER->file_start = 0; |
| |
| do |
| { |
| while (*dirname == '/') /* skip leading slashes */ |
| dirname++; |
| /* pathlen = strcspn(dirname, "/\n\t "); */ |
| for (pathlen = 0 ; |
| dirname[pathlen] |
| && !isspace(dirname[pathlen]) && dirname[pathlen] != '/' ; |
| pathlen++) |
| ; |
| |
| size = idr->size.ENDIAN; |
| extent = idr->extent.ENDIAN; |
| |
| while (size > 0) |
| { |
| if (!iso9660_devread(extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC)) |
| { |
| errnum = ERR_FSYS_CORRUPT; |
| return 0; |
| } |
| extent++; |
| |
| idr = (struct iso_directory_record *)DIRREC; |
| for (; idr->length.ENDIAN > 0; |
| idr = (struct iso_directory_record *)((char *)idr + idr->length.ENDIAN) ) |
| { |
| const char *name = (char *)idr->name; |
| unsigned int name_len = idr->name_len.ENDIAN; |
| |
| file_type = (idr->flags.ENDIAN & 2) ? ISO_DIRECTORY : ISO_REGULAR; |
| if (name_len == 1) |
| { |
| if ((name[0] == 0) || /* self */ |
| (name[0] == 1)) /* parent */ |
| continue; |
| } |
| if (name_len > 2 && CHECK2(name + name_len - 2, ';', '1')) |
| { |
| name_len -= 2; /* truncate trailing file version */ |
| if (name_len > 1 && name[name_len - 1] == '.') |
| name_len--; /* truncate trailing dot */ |
| } |
| |
| /* |
| * Parse Rock-Ridge extension |
| */ |
| rr_len = (idr->length.ENDIAN - idr->name_len.ENDIAN |
| - (unsigned char)sizeof(struct iso_directory_record) |
| + (unsigned char)sizeof(idr->name)); |
| rr_ptr.ptr = ((char *)idr + idr->name_len.ENDIAN |
| + sizeof(struct iso_directory_record) |
| - sizeof(idr->name)); |
| if (rr_len & 1) |
| rr_ptr.ptr++, rr_len--; |
| ce_ptr = NULL; |
| rr_flag = RR_FLAG_NM | RR_FLAG_PX; |
| |
| while (rr_len >= 4) |
| { |
| if (rr_ptr.rr->version != 1) |
| { |
| #ifndef STAGE1_5 |
| if (debug) |
| printf( |
| "Non-supported version (%d) RockRidge chunk " |
| "`%c%c'\n", rr_ptr.rr->version, |
| rr_ptr.rr->signature & 0xFF, |
| rr_ptr.rr->signature >> 8); |
| #endif |
| } |
| else if (CHECK2(&rr_ptr.rr->signature, 'R', 'R') |
| && rr_ptr.rr->len >= 5) |
| rr_flag &= rr_ptr.rr->u.rr.flags.ENDIAN; |
| else if (CHECK2(&rr_ptr.rr->signature, 'N', 'M')) |
| { |
| name = (char *)rr_ptr.rr->u.nm.name; |
| name_len = rr_ptr.rr->len - 5; |
| rr_flag &= ~RR_FLAG_NM; |
| } |
| else if (CHECK2(&rr_ptr.rr->signature, 'P', 'X') |
| && rr_ptr.rr->len >= 36) |
| { |
| file_type = ((rr_ptr.rr->u.px.mode.ENDIAN & POSIX_S_IFMT) |
| == POSIX_S_IFREG |
| ? ISO_REGULAR |
| : ((rr_ptr.rr->u.px.mode.ENDIAN & POSIX_S_IFMT) |
| == POSIX_S_IFDIR |
| ? ISO_DIRECTORY : ISO_OTHER)); |
| rr_flag &= ~RR_FLAG_PX; |
| } |
| else if (CHECK2(&rr_ptr.rr->signature, 'C', 'E') |
| && rr_ptr.rr->len >= 28) |
| ce_ptr = rr_ptr.rr; |
| if (!rr_flag) |
| /* |
| * There is no more extension we expects... |
| */ |
| break; |
| rr_len -= rr_ptr.rr->len; |
| rr_ptr.ptr += rr_ptr.rr->len; |
| if (rr_len < 4 && ce_ptr != NULL) |
| { |
| /* preserve name before loading new extent. */ |
| if( RRCONT_BUF <= (unsigned char *)name |
| && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE ) |
| { |
| memcpy(NAME_BUF, name, name_len); |
| name = (char *)NAME_BUF; |
| } |
| rr_ptr.ptr = (char *)(RRCONT_BUF + ce_ptr->u.ce.offset.ENDIAN); |
| rr_len = ce_ptr->u.ce.size.ENDIAN; |
| if (!iso9660_devread(ce_ptr->u.ce.extent.ENDIAN, 0, |
| ISO_SECTOR_SIZE, (char *)RRCONT_BUF)) |
| { |
| errnum = 0; /* this is not fatal. */ |
| break; |
| } |
| ce_ptr = NULL; |
| } |
| } /* rr_len >= 4 */ |
| |
| filemax = MAXINT; |
| if (name_len >= pathlen |
| && !strnicmp(name, dirname, pathlen)) |
| { |
| if (dirname[pathlen] == '/' || !print_possibilities) |
| { |
| /* |
| * DIRNAME is directory component of pathname, |
| * or we are to open a file. |
| */ |
| if (pathlen == name_len) |
| { |
| if (dirname[pathlen] == '/') |
| { |
| if (file_type != ISO_DIRECTORY) |
| { |
| errnum = ERR_BAD_FILETYPE; |
| return 0; |
| } |
| goto next_dir_level; |
| } |
| if (file_type != ISO_REGULAR) |
| { |
| errnum = ERR_BAD_FILETYPE; |
| return 0; |
| } |
| ISO_SUPER->file_start = idr->extent.ENDIAN; |
| filepos = 0; |
| filemax = idr->size.ENDIAN; |
| return 1; |
| } |
| } |
| else /* Completion */ |
| { |
| #ifndef STAGE1_5 |
| if (print_possibilities > 0) |
| print_possibilities = -print_possibilities; |
| memcpy(NAME_BUF, name, name_len); |
| NAME_BUF[name_len] = '\0'; |
| print_a_completion (NAME_BUF); |
| #endif |
| } |
| } |
| } /* for */ |
| |
| size -= ISO_SECTOR_SIZE; |
| } /* size>0 */ |
| |
| if (dirname[pathlen] == '/' || print_possibilities >= 0) |
| { |
| errnum = ERR_FILE_NOT_FOUND; |
| return 0; |
| } |
| |
| next_dir_level: |
| dirname += pathlen; |
| |
| } while (*dirname == '/'); |
| |
| return 1; |
| } |
| |
| int |
| iso9660_read (char *buf, int len) |
| { |
| int sector, blkoffset, size, ret; |
| |
| if (ISO_SUPER->file_start == 0) |
| return 0; |
| |
| ret = 0; |
| blkoffset = filepos & (ISO_SECTOR_SIZE - 1); |
| sector = filepos >> ISO_SECTOR_BITS; |
| while (len > 0) |
| { |
| size = ISO_SECTOR_SIZE - blkoffset; |
| if (size > len) |
| size = len; |
| |
| disk_read_func = disk_read_hook; |
| |
| if (!iso9660_devread(ISO_SUPER->file_start + sector, blkoffset, size, buf)) |
| return 0; |
| |
| disk_read_func = NULL; |
| |
| len -= size; |
| buf += size; |
| ret += size; |
| filepos += size; |
| sector++; |
| blkoffset = 0; |
| } |
| |
| return ret; |
| } |
| |
| #endif /* FSYS_ISO9660 */ |