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