/* * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) * including Rock Ridge Extensions support * * Copyright (C) 1998, 1999 Kousuke Takai * * 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., 675 Mass Ave, Cambridge, MA 02139, 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 2003 */ #ifdef FSYS_ISO9660 #include "shared.h" #include "filesys.h" #include "iso9660.h" /* iso9660 super-block data in memory */ struct iso_sb_info { unsigned long vol_sector; }; /* iso fs inode data in memory */ struct iso_inode_info { unsigned long file_start; }; #define ISO_SUPER \ ((struct iso_sb_info *)(FSYS_BUF)) #define INODE \ ((struct iso_inode_info *)(FSYS_BUF+sizeof(struct iso_sb_info))) #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 inline unsigned long grub_log2 (unsigned long word) { asm volatile ("bsfl %1,%0" : "=r" (word) : "r" (word)); return word; } #define log2 grub_log2 static int iso9660_devread (int sector, int byte_offset, int byte_len, char *buf) { static int read_count = 0, threshold = 2; unsigned short sector_size_lg2 = log2(buf_geom.sector_size); /* * We have to use own devread() function since BIOS return wrong geometry */ if (sector < 0) { errnum = ERR_OUTSIDE_PART; return 0; } if (byte_len <= 0) return 1; sector += (byte_offset >> sector_size_lg2); byte_offset &= (buf_geom.sector_size - 1); asm volatile ("shl%L0 %1,%0" : "=r"(sector) : "Ic"((int8_t)(ISO_SECTOR_BITS - sector_size_lg2)), "0"(sector)); #if !defined(STAGE1_5) if (disk_read_hook && debug) printf ("<%d, %d, %d>", sector, byte_offset, byte_len); #endif /* !STAGE1_5 */ read_count += (byte_len >> 9); if ((read_count >> 11) > threshold) { noisy_printf("."); threshold += 2; /* one dot every 2 MB */ } return rawread(current_drive, part_start + sector, 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 (PRIMDESC->type.l == ISO_VD_PRIMARY && !memcmp(PRIMDESC->id, ISO_STANDARD_ID, sizeof(PRIMDESC->id))) { ISO_SUPER->vol_sector = sector; INODE->file_start = 0; fsmax = PRIMDESC->volume_space_size.l; 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 char file_type; unsigned int rr_len; unsigned char rr_flag; idr = &PRIMDESC->root_directory_record; INODE->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.l; extent = idr->extent.l; 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.l > 0; idr = (struct iso_directory_record *)((char *)idr + idr->length.l) ) { const char *name = idr->name; unsigned int name_len = idr->name_len.l; file_type = (idr->flags.l & 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.l - idr->name_len.l - sizeof(struct iso_directory_record) + sizeof(idr->name)); rr_ptr.ptr = ((unsigned char *)idr + idr->name_len.l + sizeof(struct iso_directory_record) - sizeof(idr->name)); if (rr_ptr.i & 1) rr_ptr.i++, rr_len--; ce_ptr = NULL; rr_flag = RR_FLAG_NM | RR_FLAG_PX /*| RR_FLAG_SL*/; 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 { switch (rr_ptr.rr->signature) { case RRMAGIC('R', 'R'): if ( rr_ptr.rr->len >= (4+sizeof(struct RR))) rr_flag &= rr_ptr.rr->u.rr.flags.l; break; case RRMAGIC('N', 'M'): name = rr_ptr.rr->u.nm.name; name_len = rr_ptr.rr->len - (4+sizeof(struct NM)); rr_flag &= ~RR_FLAG_NM; break; case RRMAGIC('P', 'X'): if (rr_ptr.rr->len >= (4+sizeof(struct PX))) { file_type = ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) == POSIX_S_IFREG ? ISO_REGULAR : ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) == POSIX_S_IFDIR ? ISO_DIRECTORY : ISO_OTHER)); rr_flag &= ~RR_FLAG_PX; } break; case RRMAGIC('C', 'E'): if (rr_ptr.rr->len >= (4+sizeof(struct CE))) ce_ptr = rr_ptr.rr; break; #if 0 // RockRidge symlinks are not supported yet case RRMAGIC('S', 'L'): { int slen; unsigned char rootflag, prevflag; char *rpnt = NAME_BUF+1024; struct SL_component *slp; slen = rr_ptr.rr->len - (4+1); slp = &rr_ptr.rr->u.sl.link; while (slen > 1) { rootflag = 0; switch (slp->flags.l) { case 0: memcpy(rpnt, slp->text, slp->len); rpnt += slp->len; break; case 4: *rpnt++ = '.'; /* fallthru */ case 2: *rpnt++ = '.'; break; case 8: rootflag = 1; *rpnt++ = '/'; break; default: printf("Symlink component flag not implemented (%d)\n", slp->flags.l); slen = 0; break; } slen -= slp->len + 2; prevflag = slp->flags.l; slp = (struct SL_component *) ((char *) slp + slp->len + 2); if (slen < 2) { /* * If there is another SL record, and this component * record isn't continued, then add a slash. */ if ((!rootflag) && (rr_ptr.rr->u.sl.flags.l & 1) && !(prevflag & 1)) *rpnt++='/'; break; } /* * If this component record isn't continued, then append a '/'. */ if (!rootflag && !(prevflag & 1)) *rpnt++ = '/'; } *rpnt++ = '\0'; grub_putstr(NAME_BUF+1024);// debug print! } rr_flag &= ~RR_FLAG_SL; break; #endif default: break; } } 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 = NAME_BUF; } rr_ptr.ptr = RRCONT_BUF + ce_ptr->u.ce.offset.l; rr_len = ce_ptr->u.ce.size.l; if (!iso9660_devread(ce_ptr->u.ce.extent.l, 0, ISO_SECTOR_SIZE, RRCONT_BUF)) { errnum = 0; /* this is not fatal. */ break; } ce_ptr = NULL; } } /* rr_len >= 4 */ filemax = MAXINT; if (name_len >= pathlen && !memcmp(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; } INODE->file_start = idr->extent.l; filepos = 0; filemax = idr->size.l; 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 (INODE->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(INODE->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 */