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 #ifdef FSYS_ISO9660 33 34 #include "shared.h" 35 #include "filesys.h" 36 #include "iso9660.h" 37 38 /* iso9660 super-block data in memory */ 39 struct iso_sb_info { 40 unsigned long vol_sector; 41 42 }; 43 44 /* iso fs inode data in memory */ 45 struct iso_inode_info { 46 unsigned long file_start; 47 }; 48 49 #define ISO_SUPER \ 50 ((struct iso_sb_info *)(FSYS_BUF)) 51 #define INODE \ 52 ((struct iso_inode_info *)(FSYS_BUF+sizeof(struct iso_sb_info))) 53 #define PRIMDESC ((struct iso_primary_descriptor *)(FSYS_BUF + 2048)) 54 #define DIRREC ((struct iso_directory_record *)(FSYS_BUF + 4096)) 55 #define RRCONT_BUF ((unsigned char *)(FSYS_BUF + 6144)) 56 #define NAME_BUF ((unsigned char *)(FSYS_BUF + 8192)) 57 58 59 static inline unsigned long 60 grub_log2 (unsigned long word) 61 { 62 asm volatile ("bsfl %1,%0" 63 : "=r" (word) 64 : "r" (word)); 65 return word; 66 } 67 #define log2 grub_log2 68 69 static int 70 iso9660_devread (int sector, int byte_offset, int byte_len, char *buf) 71 { 72 static int read_count = 0, threshold = 2; 73 unsigned short sector_size_lg2 = log2(buf_geom.sector_size); 74 75 /* 76 * We have to use own devread() function since BIOS return wrong geometry 77 */ 78 if (sector < 0) 79 { 80 errnum = ERR_OUTSIDE_PART; 81 return 0; 82 } 83 if (byte_len <= 0) 84 return 1; 85 86 sector += (byte_offset >> sector_size_lg2); 87 byte_offset &= (buf_geom.sector_size - 1); 88 asm volatile ("shl%L0 %1,%0" 89 : "=r"(sector) 90 : "Ic"((int8_t)(ISO_SECTOR_BITS - sector_size_lg2)), 91 "0"(sector)); 92 93 #if !defined(STAGE1_5) 94 if (disk_read_hook && debug) 95 printf ("<%d, %d, %d>", sector, byte_offset, byte_len); 96 #endif /* !STAGE1_5 */ 97 98 read_count += (byte_len >> 9); 99 if ((read_count >> 11) > threshold) { 100 noisy_printf("."); 101 threshold += 2; /* one dot every 2 MB */ 102 } 103 return rawread(current_drive, part_start + sector, byte_offset, byte_len, buf); 104 } 105 106 int 107 iso9660_mount (void) 108 { 109 unsigned int sector; 110 111 /* 112 * Because there is no defined slice type ID for ISO-9660 filesystem, 113 * this test will pass only either (1) if entire disk is used, or 114 * (2) if current partition is BSD style sub-partition whose ID is 115 * ISO-9660. 116 */ 117 if ((current_partition != 0xFFFFFF) 118 && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660)) 119 return 0; 120 121 /* 122 * Currently, only FIRST session of MultiSession disks are supported !!! 123 */ 124 for (sector = 16 ; sector < 32 ; sector++) 125 { 126 if (!iso9660_devread(sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC)) 127 break; 128 /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */ 129 if (PRIMDESC->type.l == ISO_VD_PRIMARY 130 && !memcmp(PRIMDESC->id, ISO_STANDARD_ID, sizeof(PRIMDESC->id))) 131 { 132 ISO_SUPER->vol_sector = sector; 133 INODE->file_start = 0; 134 fsmax = PRIMDESC->volume_space_size.l; 135 return 1; 136 } 137 } 138 139 return 0; 140 } 141 142 int 143 iso9660_dir (char *dirname) 144 { 145 struct iso_directory_record *idr; 146 RR_ptr_t rr_ptr; 147 struct rock_ridge *ce_ptr; 148 unsigned int pathlen; 149 int size; 150 unsigned int extent; 151 unsigned char file_type; 152 unsigned int rr_len; 153 unsigned char rr_flag; 154 155 idr = &PRIMDESC->root_directory_record; 156 INODE->file_start = 0; 157 158 do 159 { 160 while (*dirname == '/') /* skip leading slashes */ 161 dirname++; 162 /* pathlen = strcspn(dirname, "/\n\t "); */ 163 for (pathlen = 0 ; 164 dirname[pathlen] 165 && !isspace(dirname[pathlen]) && dirname[pathlen] != '/' ; 166 pathlen++) 167 ; 168 169 size = idr->size.l; 170 extent = idr->extent.l; 171 172 while (size > 0) 173 { 174 if (!iso9660_devread(extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC)) 175 { 176 errnum = ERR_FSYS_CORRUPT; 177 return 0; 178 } 179 extent++; 180 181 idr = (struct iso_directory_record *)DIRREC; 182 for (; idr->length.l > 0; 183 idr = (struct iso_directory_record *)((char *)idr + idr->length.l) ) 184 { 185 const char *name = idr->name; 186 unsigned int name_len = idr->name_len.l; 187 188 file_type = (idr->flags.l & 2) ? ISO_DIRECTORY : ISO_REGULAR; 189 if (name_len == 1) 190 { 191 if ((name[0] == 0) || /* self */ 192 (name[0] == 1)) /* parent */ 193 continue; 194 } 195 if (name_len > 2 && CHECK2(name + name_len - 2, ';', '1')) 196 { 197 name_len -= 2; /* truncate trailing file version */ 198 if (name_len > 1 && name[name_len - 1] == '.') 199 name_len--; /* truncate trailing dot */ 200 } 201 202 /* 203 * Parse Rock-Ridge extension 204 */ 205 rr_len = (idr->length.l - idr->name_len.l 206 - sizeof(struct iso_directory_record) 207 + sizeof(idr->name)); 208 rr_ptr.ptr = ((unsigned char *)idr + idr->name_len.l 209 + sizeof(struct iso_directory_record) 210 - sizeof(idr->name)); 211 if (rr_ptr.i & 1) 212 rr_ptr.i++, rr_len--; 213 ce_ptr = NULL; 214 rr_flag = RR_FLAG_NM | RR_FLAG_PX /*| RR_FLAG_SL*/; 215 216 while (rr_len >= 4) 217 { 218 if (rr_ptr.rr->version != 1) 219 { 220 #ifndef STAGE1_5 221 if (debug) 222 printf( 223 "Non-supported version (%d) RockRidge chunk " 224 "`%c%c'\n", rr_ptr.rr->version, 225 rr_ptr.rr->signature & 0xFF, 226 rr_ptr.rr->signature >> 8); 227 #endif 228 } 229 else 230 { 231 switch (rr_ptr.rr->signature) 232 { 233 case RRMAGIC('R', 'R'): 234 if ( rr_ptr.rr->len >= (4+sizeof(struct RR))) 235 rr_flag &= rr_ptr.rr->u.rr.flags.l; 236 break; 237 case RRMAGIC('N', 'M'): 238 name = rr_ptr.rr->u.nm.name; 239 name_len = rr_ptr.rr->len - (4+sizeof(struct NM)); 240 rr_flag &= ~RR_FLAG_NM; 241 break; 242 case RRMAGIC('P', 'X'): 243 if (rr_ptr.rr->len >= (4+sizeof(struct PX))) 244 { 245 file_type = ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) 246 == POSIX_S_IFREG 247 ? ISO_REGULAR 248 : ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) 249 == POSIX_S_IFDIR 250 ? ISO_DIRECTORY : ISO_OTHER)); 251 rr_flag &= ~RR_FLAG_PX; 252 } 253 break; 254 case RRMAGIC('C', 'E'): 255 if (rr_ptr.rr->len >= (4+sizeof(struct CE))) 256 ce_ptr = rr_ptr.rr; 257 break; 258 #if 0 // RockRidge symlinks are not supported yet 259 case RRMAGIC('S', 'L'): 260 { 261 int slen; 262 unsigned char rootflag, prevflag; 263 char *rpnt = NAME_BUF+1024; 264 struct SL_component *slp; 265 266 slen = rr_ptr.rr->len - (4+1); 267 slp = &rr_ptr.rr->u.sl.link; 268 while (slen > 1) 269 { 270 rootflag = 0; 271 switch (slp->flags.l) 272 { 273 case 0: 274 memcpy(rpnt, slp->text, slp->len); 275 rpnt += slp->len; 276 break; 277 case 4: 278 *rpnt++ = '.'; 279 /* fallthru */ 280 case 2: 281 *rpnt++ = '.'; 282 break; 283 case 8: 284 rootflag = 1; 285 *rpnt++ = '/'; 286 break; 287 default: 288 printf("Symlink component flag not implemented (%d)\n", 289 slp->flags.l); 290 slen = 0; 291 break; 292 } 293 slen -= slp->len + 2; 294 prevflag = slp->flags.l; 295 slp = (struct SL_component *) ((char *) slp + slp->len + 2); 296 297 if (slen < 2) 298 { 299 /* 300 * If there is another SL record, and this component 301 * record isn't continued, then add a slash. 302 */ 303 if ((!rootflag) && (rr_ptr.rr->u.sl.flags.l & 1) && !(prevflag & 1)) 304 *rpnt++='/'; 305 break; 306 } 307 308 /* 309 * If this component record isn't continued, then append a '/'. 310 */ 311 if (!rootflag && !(prevflag & 1)) 312 *rpnt++ = '/'; 313 } 314 *rpnt++ = '\0'; 315 grub_putstr(NAME_BUF+1024);// debug print! 316 } 317 rr_flag &= ~RR_FLAG_SL; 318 break; 319 #endif 320 default: 321 break; 322 } 323 } 324 if (!rr_flag) 325 /* 326 * There is no more extension we expects... 327 */ 328 break; 329 330 rr_len -= rr_ptr.rr->len; 331 rr_ptr.ptr += rr_ptr.rr->len; 332 if (rr_len < 4 && ce_ptr != NULL) 333 { 334 /* preserve name before loading new extent. */ 335 if( RRCONT_BUF <= (unsigned char *)name 336 && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE ) 337 { 338 memcpy(NAME_BUF, name, name_len); 339 name = NAME_BUF; 340 } 341 rr_ptr.ptr = RRCONT_BUF + ce_ptr->u.ce.offset.l; 342 rr_len = ce_ptr->u.ce.size.l; 343 if (!iso9660_devread(ce_ptr->u.ce.extent.l, 0, ISO_SECTOR_SIZE, RRCONT_BUF)) 344 { 345 errnum = 0; /* this is not fatal. */ 346 break; 347 } 348 ce_ptr = NULL; 349 } 350 } /* rr_len >= 4 */ 351 352 filemax = MAXINT; 353 if (name_len >= pathlen 354 && !memcmp(name, dirname, pathlen)) 355 { 356 if (dirname[pathlen] == '/' || !print_possibilities) 357 { 358 /* 359 * DIRNAME is directory component of pathname, 360 * or we are to open a file. 361 */ 362 if (pathlen == name_len) 363 { 364 if (dirname[pathlen] == '/') 365 { 366 if (file_type != ISO_DIRECTORY) 367 { 368 errnum = ERR_BAD_FILETYPE; 369 return 0; 370 } 371 goto next_dir_level; 372 } 373 if (file_type != ISO_REGULAR) 374 { 375 errnum = ERR_BAD_FILETYPE; 376 return 0; 377 } 378 INODE->file_start = idr->extent.l; 379 filepos = 0; 380 filemax = idr->size.l; 381 return 1; 382 } 383 } 384 else /* Completion */ 385 { 386 #ifndef STAGE1_5 387 if (print_possibilities > 0) 388 print_possibilities = -print_possibilities; 389 memcpy(NAME_BUF, name, name_len); 390 NAME_BUF[name_len] = '\0'; 391 print_a_completion (NAME_BUF); 392 #endif 393 } 394 } 395 } /* for */ 396 397 size -= ISO_SECTOR_SIZE; 398 } /* size>0 */ 399 400 if (dirname[pathlen] == '/' || print_possibilities >= 0) 401 { 402 errnum = ERR_FILE_NOT_FOUND; 403 return 0; 404 } 405 406 next_dir_level: 407 dirname += pathlen; 408 409 } while (*dirname == '/'); 410 411 return 1; 412 } 413 414 int 415 iso9660_read (char *buf, int len) 416 { 417 int sector, blkoffset, size, ret; 418 419 if (INODE->file_start == 0) 420 return 0; 421 422 ret = 0; 423 blkoffset = filepos & (ISO_SECTOR_SIZE - 1); 424 sector = filepos >> ISO_SECTOR_BITS; 425 while (len > 0) 426 { 427 size = ISO_SECTOR_SIZE - blkoffset; 428 if (size > len) 429 size = len; 430 431 disk_read_func = disk_read_hook; 432 433 if (!iso9660_devread(INODE->file_start + sector, blkoffset, size, buf)) 434 return 0; 435 436 disk_read_func = NULL; 437 438 len -= size; 439 buf += size; 440 ret += size; 441 filepos += size; 442 sector++; 443 blkoffset = 0; 444 } 445 446 return ret; 447 } 448 449 #endif /* FSYS_ISO9660 */ 450