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