Greg Watson | b9f5c11 | 2004-03-13 03:09:57 +0000 | [diff] [blame] | 1 | /* |
| 2 | * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) |
| 3 | * including Rock Ridge Extensions support |
| 4 | * |
| 5 | * Copyright (C) 1998, 1999 Kousuke Takai <tak@kmc.kyoto-u.ac.jp> |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with this program; if not, write to the Free Software |
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 20 | */ |
| 21 | /* |
| 22 | * References: |
| 23 | * linux/fs/isofs/rock.[ch] |
| 24 | * mkisofs-1.11.1/diag/isoinfo.c |
| 25 | * mkisofs-1.11.1/iso9660.h |
| 26 | * (all are written by Eric Youngdale) |
| 27 | * |
| 28 | * Modifications by: |
| 29 | * Leonid Lisovskiy <lly@pisem.net> 2003 |
| 30 | */ |
| 31 | |
| 32 | /* |
| 33 | * Modified to make it work with FILO |
| 34 | * 2003-10 by SONE Takeshi |
| 35 | */ |
| 36 | |
| 37 | #include <console/console.h> |
| 38 | #include <fs/fs.h> |
| 39 | #include <fs/iso9660.h> |
| 40 | #include <string.h> |
| 41 | |
| 42 | struct iso_superblock { |
| 43 | unsigned long vol_sector; |
| 44 | |
| 45 | unsigned long file_start; |
| 46 | }; |
| 47 | |
| 48 | #define ISO_SUPER ((struct iso_superblock *)(FSYS_BUF)) |
| 49 | #define PRIMDESC ((struct iso_primary_descriptor *)(FSYS_BUF + 2048)) |
| 50 | #define DIRREC ((struct iso_directory_record *)(FSYS_BUF + 4096)) |
| 51 | #define RRCONT_BUF ((unsigned char *)(FSYS_BUF + 6144)) |
| 52 | #define NAME_BUF ((unsigned char *)(FSYS_BUF + 8192)) |
| 53 | |
| 54 | static int |
| 55 | iso9660_devread (int sector, int byte_offset, int byte_len, char *buf) |
| 56 | { |
| 57 | /* FILO uses 512-byte "soft" sector, and ISO-9660 uses 2048-byte |
| 58 | * CD-ROM sector */ |
| 59 | return devread(sector<<2, byte_offset, byte_len, buf); |
| 60 | } |
| 61 | |
| 62 | int |
| 63 | iso9660_mount (void) |
| 64 | { |
| 65 | unsigned int sector; |
| 66 | |
| 67 | /* |
| 68 | * Because there is no defined slice type ID for ISO-9660 filesystem, |
| 69 | * this test will pass only either (1) if entire disk is used, or |
| 70 | * (2) if current partition is BSD style sub-partition whose ID is |
| 71 | * ISO-9660. |
| 72 | */ |
| 73 | /*if ((current_partition != 0xFFFFFF) |
| 74 | && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660)) |
| 75 | return 0;*/ |
| 76 | |
| 77 | /* |
| 78 | * Currently, only FIRST session of MultiSession disks are supported !!! |
| 79 | */ |
| 80 | for (sector = 16 ; sector < 32 ; sector++) |
| 81 | { |
| 82 | if (!iso9660_devread(sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC)) |
| 83 | break; |
| 84 | /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */ |
| 85 | if (isonum_711(PRIMDESC->type) == ISO_VD_PRIMARY && |
| 86 | !__builtin_memcmp(PRIMDESC->id, "CD001", 5)) |
| 87 | { |
| 88 | ISO_SUPER->vol_sector = sector; |
| 89 | ISO_SUPER->file_start = 0; |
| 90 | fsmax = isonum_733(PRIMDESC->volume_space_size); |
| 91 | return 1; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | return 0; |
| 96 | } |
| 97 | |
| 98 | int |
| 99 | iso9660_dir (char *dirname) |
| 100 | { |
| 101 | struct iso_directory_record *idr; |
| 102 | RR_ptr_t rr_ptr; |
| 103 | struct rock_ridge *ce_ptr; |
| 104 | unsigned int pathlen; |
| 105 | int size; |
| 106 | unsigned int extent; |
| 107 | unsigned int rr_len; |
| 108 | unsigned char file_type; |
| 109 | unsigned char rr_flag; |
| 110 | |
| 111 | idr = (struct iso_directory_record *)&PRIMDESC->root_directory_record; |
| 112 | ISO_SUPER->file_start = 0; |
| 113 | |
| 114 | do |
| 115 | { |
| 116 | while (*dirname == '/') /* skip leading slashes */ |
| 117 | dirname++; |
| 118 | /* pathlen = strcspn(dirname, "/\n\t "); */ |
| 119 | for (pathlen = 0 ; |
| 120 | dirname[pathlen] |
| 121 | && !isspace(dirname[pathlen]) && dirname[pathlen] != '/' ; |
| 122 | pathlen++) |
| 123 | ; |
| 124 | |
| 125 | size = isonum_733(idr->size); |
| 126 | extent = isonum_733(idr->extent); |
| 127 | |
| 128 | while (size > 0) |
| 129 | { |
| 130 | if (!iso9660_devread(extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC)) |
| 131 | { |
| 132 | errnum = ERR_FSYS_CORRUPT; |
| 133 | return 0; |
| 134 | } |
| 135 | extent++; |
| 136 | |
| 137 | idr = (struct iso_directory_record *)DIRREC; |
| 138 | for (; isonum_711(idr->length) > 0; |
| 139 | idr = (struct iso_directory_record *)((char *)idr + isonum_711(idr->length)) ) |
| 140 | { |
| 141 | const char *name = idr->name; |
| 142 | unsigned int name_len = isonum_711(idr->name_len); |
| 143 | |
Greg Watson | 9bc5018 | 2004-03-17 22:02:12 +0000 | [diff] [blame] | 144 | file_type = (isonum_711(idr->flags) & 2) ? ISO_DIRECTORY : ISO_REGULAR; |
Greg Watson | b9f5c11 | 2004-03-13 03:09:57 +0000 | [diff] [blame] | 145 | if (name_len == 1) |
| 146 | { |
| 147 | if ((name[0] == 0) || /* self */ |
| 148 | (name[0] == 1)) /* parent */ |
| 149 | continue; |
| 150 | } |
| 151 | if (name_len > 2 && |
| 152 | name[name_len - 2] == ';' && |
| 153 | name[name_len - 1] == '1') |
| 154 | { |
| 155 | name_len -= 2; /* truncate trailing file version */ |
| 156 | if (name_len > 1 && name[name_len - 1] == '.') |
| 157 | name_len--; /* truncate trailing dot */ |
| 158 | } |
| 159 | |
| 160 | /* |
| 161 | * Parse Rock-Ridge extension |
| 162 | */ |
| 163 | rr_len = (isonum_711(idr->length) - isonum_711(idr->name_len) |
Greg Watson | 9bc5018 | 2004-03-17 22:02:12 +0000 | [diff] [blame] | 164 | - sizeof(struct iso_directory_record) |
| 165 | + sizeof(idr->name)); |
Greg Watson | b9f5c11 | 2004-03-13 03:09:57 +0000 | [diff] [blame] | 166 | rr_ptr.ptr = ((unsigned char *)idr + isonum_711(idr->name_len) |
| 167 | + sizeof(struct iso_directory_record) |
| 168 | - sizeof(idr->name)); |
| 169 | if (rr_ptr.i & 1) |
| 170 | rr_ptr.i++, rr_len--; |
| 171 | ce_ptr = 0; |
| 172 | rr_flag = RR_FLAG_NM | RR_FLAG_PX; |
| 173 | |
| 174 | while (rr_len >= 4) |
| 175 | { |
| 176 | if (rr_ptr.rr->version != 1) |
| 177 | { |
| 178 | #ifndef STAGE1_5 |
| 179 | if (debug) |
| 180 | printk_debug( |
| 181 | "Non-supported version (%d) RockRidge chunk " |
| 182 | "`%c%c'\n", rr_ptr.rr->version, |
Greg Watson | 9bc5018 | 2004-03-17 22:02:12 +0000 | [diff] [blame] | 183 | rr_ptr.rr->signature[0], |
| 184 | rr_ptr.rr->signature[1]); |
Greg Watson | b9f5c11 | 2004-03-13 03:09:57 +0000 | [diff] [blame] | 185 | #endif |
Greg Watson | 9bc5018 | 2004-03-17 22:02:12 +0000 | [diff] [blame] | 186 | break; |
Greg Watson | b9f5c11 | 2004-03-13 03:09:57 +0000 | [diff] [blame] | 187 | } |
| 188 | else if (rr_ptr.rr->signature[0] == 'R' |
| 189 | && rr_ptr.rr->signature[1] == 'R' |
| 190 | && rr_ptr.rr->len >= 5) |
| 191 | rr_flag &= isonum_711(rr_ptr.rr->u.RR.flags); |
| 192 | else if (rr_ptr.rr->signature[0] == 'N' |
| 193 | && rr_ptr.rr->signature[1] == 'M') |
| 194 | { |
| 195 | name = rr_ptr.rr->u.NM.name; |
| 196 | name_len = rr_ptr.rr->len - 5; |
| 197 | rr_flag &= ~RR_FLAG_NM; |
| 198 | } |
| 199 | else if (rr_ptr.rr->signature[0] == 'P' |
| 200 | && rr_ptr.rr->signature[1] == 'X' |
| 201 | && rr_ptr.rr->len >= 36) |
| 202 | { |
| 203 | unsigned int mode = isonum_733(rr_ptr.rr->u.PX.mode); |
| 204 | file_type = ((mode & POSIX_S_IFMT) |
| 205 | == POSIX_S_IFREG |
| 206 | ? ISO_REGULAR |
| 207 | : ((mode & POSIX_S_IFMT) |
| 208 | == POSIX_S_IFDIR |
| 209 | ? ISO_DIRECTORY : ISO_OTHER)); |
| 210 | rr_flag &= ~RR_FLAG_PX; |
| 211 | } |
| 212 | else if (rr_ptr.rr->signature[0] == 'C' |
| 213 | && rr_ptr.rr->signature[1] == 'E' |
| 214 | && rr_ptr.rr->len >= 28) |
| 215 | ce_ptr = rr_ptr.rr; |
| 216 | if (!rr_flag) |
| 217 | /* |
| 218 | * There is no more extension we expects... |
| 219 | */ |
| 220 | break; |
| 221 | rr_len -= rr_ptr.rr->len; |
| 222 | rr_ptr.ptr += rr_ptr.rr->len; |
| 223 | if (rr_len < 4 && ce_ptr != 0) |
| 224 | { |
| 225 | /* preserve name before loading new extent. */ |
| 226 | if( RRCONT_BUF <= (unsigned char *)name |
| 227 | && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE ) |
| 228 | { |
| 229 | memcpy(NAME_BUF, name, name_len); |
| 230 | name = NAME_BUF; |
| 231 | } |
| 232 | rr_ptr.ptr = RRCONT_BUF + isonum_733(ce_ptr->u.CE.offset); |
| 233 | rr_len = isonum_733(ce_ptr->u.CE.size); |
| 234 | if (!iso9660_devread(isonum_733(ce_ptr->u.CE.extent), 0, ISO_SECTOR_SIZE, RRCONT_BUF)) |
| 235 | { |
| 236 | errnum = 0; /* this is not fatal. */ |
| 237 | break; |
| 238 | } |
| 239 | ce_ptr = 0; |
| 240 | } |
| 241 | } /* rr_len >= 4 */ |
| 242 | |
| 243 | filemax = MAXINT; |
| 244 | if (name_len >= pathlen |
| 245 | && !__builtin_memcmp(name, dirname, pathlen)) |
| 246 | { |
| 247 | if (dirname[pathlen] == '/' || !print_possibilities) |
| 248 | { |
| 249 | /* |
| 250 | * DIRNAME is directory component of pathname, |
| 251 | * or we are to open a file. |
| 252 | */ |
| 253 | if (pathlen == name_len) |
| 254 | { |
| 255 | if (dirname[pathlen] == '/') |
| 256 | { |
| 257 | if (file_type != ISO_DIRECTORY) |
| 258 | { |
| 259 | errnum = ERR_BAD_FILETYPE; |
| 260 | return 0; |
| 261 | } |
| 262 | goto next_dir_level; |
| 263 | } |
| 264 | if (file_type != ISO_REGULAR) |
| 265 | { |
| 266 | errnum = ERR_BAD_FILETYPE; |
| 267 | return 0; |
| 268 | } |
| 269 | ISO_SUPER->file_start = isonum_733(idr->extent); |
| 270 | filepos = 0; |
| 271 | filemax = isonum_733(idr->size); |
| 272 | return 1; |
| 273 | } |
| 274 | } |
| 275 | else /* Completion */ |
| 276 | { |
| 277 | #ifndef STAGE1_5 |
| 278 | if (print_possibilities > 0) |
| 279 | print_possibilities = -print_possibilities; |
| 280 | memcpy(NAME_BUF, name, name_len); |
| 281 | NAME_BUF[name_len] = '\0'; |
| 282 | print_a_completion (NAME_BUF); |
| 283 | #endif |
| 284 | } |
| 285 | } |
| 286 | } /* for */ |
| 287 | |
| 288 | size -= ISO_SECTOR_SIZE; |
| 289 | } /* size>0 */ |
| 290 | |
| 291 | if (dirname[pathlen] == '/' || print_possibilities >= 0) |
| 292 | { |
| 293 | errnum = ERR_FILE_NOT_FOUND; |
| 294 | return 0; |
| 295 | } |
| 296 | |
| 297 | next_dir_level: |
| 298 | dirname += pathlen; |
| 299 | |
| 300 | } while (*dirname == '/'); |
| 301 | |
| 302 | return 1; |
| 303 | } |
| 304 | |
| 305 | int |
| 306 | iso9660_read (char *buf, int len) |
| 307 | { |
| 308 | int sector, blkoffset, size, ret; |
| 309 | |
| 310 | if (ISO_SUPER->file_start == 0) |
| 311 | return 0; |
| 312 | |
| 313 | ret = 0; |
| 314 | blkoffset = filepos & (ISO_SECTOR_SIZE - 1); |
| 315 | sector = filepos >> ISO_SECTOR_BITS; |
| 316 | while (len > 0) |
| 317 | { |
| 318 | size = ISO_SECTOR_SIZE - blkoffset; |
| 319 | if (size > len) |
| 320 | size = len; |
| 321 | |
| 322 | disk_read_func = disk_read_hook; |
| 323 | |
| 324 | if (!iso9660_devread(ISO_SUPER->file_start + sector, blkoffset, size, buf)) |
| 325 | return 0; |
| 326 | |
| 327 | disk_read_func = 0; |
| 328 | |
| 329 | len -= size; |
| 330 | buf += size; |
| 331 | ret += size; |
| 332 | filepos += size; |
| 333 | sector++; |
| 334 | blkoffset = 0; |
| 335 | } |
| 336 | |
| 337 | return ret; |
| 338 | } |