]> git.proxmox.com Git - grub2.git/blob - grub-core/fs/iso9660.c
Add gcc_struct to all packed structures when compiling with mingw.
[grub2.git] / grub-core / fs / iso9660.c
1 /* iso9660.c - iso9660 implementation with extensions:
2 SUSP, Rock Ridge. */
3 /*
4 * GRUB -- GRand Unified Bootloader
5 * Copyright (C) 2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
6 *
7 * GRUB 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 3 of the License, or
10 * (at your option) any later version.
11 *
12 * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <grub/err.h>
22 #include <grub/file.h>
23 #include <grub/mm.h>
24 #include <grub/misc.h>
25 #include <grub/disk.h>
26 #include <grub/dl.h>
27 #include <grub/types.h>
28 #include <grub/fshelp.h>
29 #include <grub/charset.h>
30 #include <grub/datetime.h>
31
32 GRUB_MOD_LICENSE ("GPLv3+");
33
34 #define GRUB_ISO9660_FSTYPE_DIR 0040000
35 #define GRUB_ISO9660_FSTYPE_REG 0100000
36 #define GRUB_ISO9660_FSTYPE_SYMLINK 0120000
37 #define GRUB_ISO9660_FSTYPE_MASK 0170000
38
39 #define GRUB_ISO9660_LOG2_BLKSZ 2
40 #define GRUB_ISO9660_BLKSZ 2048
41
42 #define GRUB_ISO9660_RR_DOT 2
43 #define GRUB_ISO9660_RR_DOTDOT 4
44
45 #define GRUB_ISO9660_VOLDESC_BOOT 0
46 #define GRUB_ISO9660_VOLDESC_PRIMARY 1
47 #define GRUB_ISO9660_VOLDESC_SUPP 2
48 #define GRUB_ISO9660_VOLDESC_PART 3
49 #define GRUB_ISO9660_VOLDESC_END 255
50
51 /* The head of a volume descriptor. */
52 struct grub_iso9660_voldesc
53 {
54 grub_uint8_t type;
55 grub_uint8_t magic[5];
56 grub_uint8_t version;
57 } GRUB_PACKED;
58
59 struct grub_iso9660_date2
60 {
61 grub_uint8_t year;
62 grub_uint8_t month;
63 grub_uint8_t day;
64 grub_uint8_t hour;
65 grub_uint8_t minute;
66 grub_uint8_t second;
67 grub_uint8_t offset;
68 } GRUB_PACKED;
69
70 /* A directory entry. */
71 struct grub_iso9660_dir
72 {
73 grub_uint8_t len;
74 grub_uint8_t ext_sectors;
75 grub_uint32_t first_sector;
76 grub_uint32_t first_sector_be;
77 grub_uint32_t size;
78 grub_uint32_t size_be;
79 struct grub_iso9660_date2 mtime;
80 grub_uint8_t flags;
81 grub_uint8_t unused2[6];
82 #define MAX_NAMELEN 255
83 grub_uint8_t namelen;
84 } GRUB_PACKED;
85
86 struct grub_iso9660_date
87 {
88 grub_uint8_t year[4];
89 grub_uint8_t month[2];
90 grub_uint8_t day[2];
91 grub_uint8_t hour[2];
92 grub_uint8_t minute[2];
93 grub_uint8_t second[2];
94 grub_uint8_t hundredth[2];
95 grub_uint8_t offset;
96 } GRUB_PACKED;
97
98 /* The primary volume descriptor. Only little endian is used. */
99 struct grub_iso9660_primary_voldesc
100 {
101 struct grub_iso9660_voldesc voldesc;
102 grub_uint8_t unused1[33];
103 grub_uint8_t volname[32];
104 grub_uint8_t unused2[16];
105 grub_uint8_t escape[32];
106 grub_uint8_t unused3[12];
107 grub_uint32_t path_table_size;
108 grub_uint8_t unused4[4];
109 grub_uint32_t path_table;
110 grub_uint8_t unused5[12];
111 struct grub_iso9660_dir rootdir;
112 grub_uint8_t unused6[624];
113 struct grub_iso9660_date created;
114 struct grub_iso9660_date modified;
115 } GRUB_PACKED;
116
117 /* A single entry in the path table. */
118 struct grub_iso9660_path
119 {
120 grub_uint8_t len;
121 grub_uint8_t sectors;
122 grub_uint32_t first_sector;
123 grub_uint16_t parentdir;
124 grub_uint8_t name[0];
125 } GRUB_PACKED;
126
127 /* An entry in the System Usage area of the directory entry. */
128 struct grub_iso9660_susp_entry
129 {
130 grub_uint8_t sig[2];
131 grub_uint8_t len;
132 grub_uint8_t version;
133 grub_uint8_t data[0];
134 } GRUB_PACKED;
135
136 /* The CE entry. This is used to describe the next block where data
137 can be found. */
138 struct grub_iso9660_susp_ce
139 {
140 struct grub_iso9660_susp_entry entry;
141 grub_uint32_t blk;
142 grub_uint32_t blk_be;
143 grub_uint32_t off;
144 grub_uint32_t off_be;
145 grub_uint32_t len;
146 grub_uint32_t len_be;
147 } GRUB_PACKED;
148
149 struct grub_iso9660_data
150 {
151 struct grub_iso9660_primary_voldesc voldesc;
152 grub_disk_t disk;
153 int rockridge;
154 int susp_skip;
155 int joliet;
156 struct grub_fshelp_node *node;
157 };
158
159 struct grub_fshelp_node
160 {
161 struct grub_iso9660_data *data;
162 grub_size_t have_dirents, alloc_dirents;
163 int have_symlink;
164 struct grub_iso9660_dir dirents[8];
165 char symlink[0];
166 };
167
168 enum
169 {
170 FLAG_TYPE_PLAIN = 0,
171 FLAG_TYPE_DIR = 2,
172 FLAG_TYPE = 3,
173 FLAG_MORE_EXTENTS = 0x80
174 };
175
176 static grub_dl_t my_mod;
177 \f
178
179 static grub_err_t
180 iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int32_t *nix)
181 {
182 struct grub_datetime datetime;
183
184 if (! i->year[0] && ! i->year[1]
185 && ! i->year[2] && ! i->year[3]
186 && ! i->month[0] && ! i->month[1]
187 && ! i->day[0] && ! i->day[1]
188 && ! i->hour[0] && ! i->hour[1]
189 && ! i->minute[0] && ! i->minute[1]
190 && ! i->second[0] && ! i->second[1]
191 && ! i->hundredth[0] && ! i->hundredth[1])
192 return grub_error (GRUB_ERR_BAD_NUMBER, "empty date");
193 datetime.year = (i->year[0] - '0') * 1000 + (i->year[1] - '0') * 100
194 + (i->year[2] - '0') * 10 + (i->year[3] - '0');
195 datetime.month = (i->month[0] - '0') * 10 + (i->month[1] - '0');
196 datetime.day = (i->day[0] - '0') * 10 + (i->day[1] - '0');
197 datetime.hour = (i->hour[0] - '0') * 10 + (i->hour[1] - '0');
198 datetime.minute = (i->minute[0] - '0') * 10 + (i->minute[1] - '0');
199 datetime.second = (i->second[0] - '0') * 10 + (i->second[1] - '0');
200
201 if (!grub_datetime2unixtime (&datetime, nix))
202 return grub_error (GRUB_ERR_BAD_NUMBER, "incorrect date");
203 *nix -= i->offset * 60 * 15;
204 return GRUB_ERR_NONE;
205 }
206
207 static int
208 iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int32_t *nix)
209 {
210 struct grub_datetime datetime;
211
212 datetime.year = i->year + 1900;
213 datetime.month = i->month;
214 datetime.day = i->day;
215 datetime.hour = i->hour;
216 datetime.minute = i->minute;
217 datetime.second = i->second;
218
219 if (!grub_datetime2unixtime (&datetime, nix))
220 return 0;
221 *nix -= i->offset * 60 * 15;
222 return 1;
223 }
224
225 static grub_err_t
226 read_node (grub_fshelp_node_t node, grub_off_t off, grub_size_t len, char *buf)
227 {
228 grub_size_t i = 0;
229
230 while (len > 0)
231 {
232 grub_size_t toread;
233 grub_err_t err;
234 while (i < node->have_dirents
235 && off >= grub_le_to_cpu32 (node->dirents[i].size))
236 {
237 off -= grub_le_to_cpu32 (node->dirents[i].size);
238 i++;
239 }
240 if (i == node->have_dirents)
241 return grub_error (GRUB_ERR_OUT_OF_RANGE, "read out of range");
242 toread = grub_le_to_cpu32 (node->dirents[i].size);
243 if (toread > len)
244 toread = len;
245 err = grub_disk_read (node->data->disk,
246 ((grub_disk_addr_t) grub_le_to_cpu32 (node->dirents[i].first_sector)) << GRUB_ISO9660_LOG2_BLKSZ,
247 off, toread, buf);
248 if (err)
249 return err;
250 len -= toread;
251 off += toread;
252 buf += toread;
253 }
254 return GRUB_ERR_NONE;
255 }
256
257 /* Iterate over the susp entries, starting with block SUA_BLOCK on the
258 offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for
259 every entry. */
260 static grub_err_t
261 grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off,
262 grub_ssize_t sua_size,
263 grub_err_t (*hook)
264 (struct grub_iso9660_susp_entry *entry, void *hook_arg),
265 void *hook_arg)
266 {
267 char *sua;
268 struct grub_iso9660_susp_entry *entry;
269 grub_err_t err;
270
271 if (sua_size <= 0)
272 return GRUB_ERR_NONE;
273
274 sua = grub_malloc (sua_size);
275 if (!sua)
276 return grub_errno;
277
278 /* Load a part of the System Usage Area. */
279 err = read_node (node, off, sua_size, sua);
280 if (err)
281 return err;
282
283 for (entry = (struct grub_iso9660_susp_entry *) sua; (char *) entry < (char *) sua + sua_size - 1 && entry->len > 0;
284 entry = (struct grub_iso9660_susp_entry *)
285 ((char *) entry + entry->len))
286 {
287 /* The last entry. */
288 if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
289 break;
290
291 /* Additional entries are stored elsewhere. */
292 if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0)
293 {
294 struct grub_iso9660_susp_ce *ce;
295 grub_disk_addr_t ce_block;
296
297 ce = (struct grub_iso9660_susp_ce *) entry;
298 sua_size = grub_le_to_cpu32 (ce->len);
299 off = grub_le_to_cpu32 (ce->off);
300 ce_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ;
301
302 grub_free (sua);
303 sua = grub_malloc (sua_size);
304 if (!sua)
305 return grub_errno;
306
307 /* Load a part of the System Usage Area. */
308 err = grub_disk_read (node->data->disk, ce_block, off,
309 sua_size, sua);
310 if (err)
311 return err;
312
313 entry = (struct grub_iso9660_susp_entry *) sua;
314 }
315
316 if (hook (entry, hook_arg))
317 {
318 grub_free (sua);
319 return 0;
320 }
321 }
322
323 grub_free (sua);
324 return 0;
325 }
326
327 static char *
328 grub_iso9660_convert_string (grub_uint8_t *us, int len)
329 {
330 char *p;
331 int i;
332 grub_uint16_t t[MAX_NAMELEN / 2 + 1];
333
334 p = grub_malloc (len * GRUB_MAX_UTF8_PER_UTF16 + 1);
335 if (! p)
336 return NULL;
337
338 for (i=0; i<len; i++)
339 t[i] = grub_be_to_cpu16 (grub_get_unaligned16 (us + 2 * i));
340
341 *grub_utf16_to_utf8 ((grub_uint8_t *) p, t, len) = '\0';
342
343 return p;
344 }
345
346 static grub_err_t
347 susp_iterate_set_rockridge (struct grub_iso9660_susp_entry *susp_entry,
348 void *_data)
349 {
350 struct grub_iso9660_data *data = _data;
351 /* The "ER" entry is used to detect extensions. The
352 `IEEE_P1285' extension means Rock ridge. */
353 if (grub_strncmp ((char *) susp_entry->sig, "ER", 2) == 0)
354 {
355 data->rockridge = 1;
356 return 1;
357 }
358 return 0;
359 }
360
361 static grub_err_t
362 set_rockridge (struct grub_iso9660_data *data)
363 {
364 int sua_pos;
365 int sua_size;
366 char *sua;
367 struct grub_iso9660_dir rootdir;
368 struct grub_iso9660_susp_entry *entry;
369
370 data->rockridge = 0;
371
372 /* Read the system use area and test it to see if SUSP is
373 supported. */
374 if (grub_disk_read (data->disk,
375 (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
376 << GRUB_ISO9660_LOG2_BLKSZ), 0,
377 sizeof (rootdir), (char *) &rootdir))
378 return grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
379
380 sua_pos = (sizeof (rootdir) + rootdir.namelen
381 + (rootdir.namelen % 2) - 1);
382 sua_size = rootdir.len - sua_pos;
383
384 if (!sua_size)
385 return GRUB_ERR_NONE;
386
387 sua = grub_malloc (sua_size);
388 if (! sua)
389 return grub_errno;
390
391 if (grub_disk_read (data->disk,
392 (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
393 << GRUB_ISO9660_LOG2_BLKSZ), sua_pos,
394 sua_size, sua))
395 {
396 grub_free (sua);
397 return grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
398 }
399
400 entry = (struct grub_iso9660_susp_entry *) sua;
401
402 /* Test if the SUSP protocol is used on this filesystem. */
403 if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0)
404 {
405 struct grub_fshelp_node rootnode;
406
407 rootnode.data = data;
408 rootnode.alloc_dirents = ARRAY_SIZE (rootnode.dirents);
409 rootnode.have_dirents = 1;
410 rootnode.have_symlink = 0;
411 rootnode.dirents[0] = data->voldesc.rootdir;
412
413 /* The 2nd data byte stored how many bytes are skipped every time
414 to get to the SUA (System Usage Area). */
415 data->susp_skip = entry->data[2];
416 entry = (struct grub_iso9660_susp_entry *) ((char *) entry + entry->len);
417
418 /* Iterate over the entries in the SUA area to detect
419 extensions. */
420 if (grub_iso9660_susp_iterate (&rootnode,
421 sua_pos, sua_size, susp_iterate_set_rockridge,
422 data))
423 {
424 grub_free (sua);
425 return grub_errno;
426 }
427 }
428 grub_free (sua);
429 return GRUB_ERR_NONE;
430 }
431
432 static struct grub_iso9660_data *
433 grub_iso9660_mount (grub_disk_t disk)
434 {
435 struct grub_iso9660_data *data = 0;
436 struct grub_iso9660_primary_voldesc voldesc;
437 int block;
438
439 data = grub_zalloc (sizeof (struct grub_iso9660_data));
440 if (! data)
441 return 0;
442
443 data->disk = disk;
444
445 block = 16;
446 do
447 {
448 int copy_voldesc = 0;
449
450 /* Read the superblock. */
451 if (grub_disk_read (disk, block << GRUB_ISO9660_LOG2_BLKSZ, 0,
452 sizeof (struct grub_iso9660_primary_voldesc),
453 (char *) &voldesc))
454 {
455 grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
456 goto fail;
457 }
458
459 if (grub_strncmp ((char *) voldesc.voldesc.magic, "CD001", 5) != 0)
460 {
461 grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
462 goto fail;
463 }
464
465 if (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_PRIMARY)
466 copy_voldesc = 1;
467 else if (!data->rockridge
468 && (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_SUPP)
469 && (voldesc.escape[0] == 0x25) && (voldesc.escape[1] == 0x2f)
470 &&
471 ((voldesc.escape[2] == 0x40) || /* UCS-2 Level 1. */
472 (voldesc.escape[2] == 0x43) || /* UCS-2 Level 2. */
473 (voldesc.escape[2] == 0x45))) /* UCS-2 Level 3. */
474 {
475 copy_voldesc = 1;
476 data->joliet = 1;
477 }
478
479 if (copy_voldesc)
480 {
481 grub_memcpy((char *) &data->voldesc, (char *) &voldesc,
482 sizeof (struct grub_iso9660_primary_voldesc));
483 if (set_rockridge (data))
484 goto fail;
485 }
486
487 block++;
488 } while (voldesc.voldesc.type != GRUB_ISO9660_VOLDESC_END);
489
490 return data;
491
492 fail:
493 grub_free (data);
494 return 0;
495 }
496
497
498 static char *
499 grub_iso9660_read_symlink (grub_fshelp_node_t node)
500 {
501 return node->have_symlink
502 ? grub_strdup (node->symlink
503 + (node->have_dirents) * sizeof (node->dirents[0])
504 - sizeof (node->dirents)) : grub_strdup ("");
505 }
506
507 static grub_off_t
508 get_node_size (grub_fshelp_node_t node)
509 {
510 grub_off_t ret = 0;
511 grub_size_t i;
512
513 for (i = 0; i < node->have_dirents; i++)
514 ret += grub_le_to_cpu32 (node->dirents[i].size);
515 return ret;
516 }
517
518 struct iterate_dir_ctx
519 {
520 char *filename;
521 int filename_alloc;
522 enum grub_fshelp_filetype type;
523 char *symlink;
524 int was_continue;
525 };
526
527 /* Extend the symlink. */
528 static void
529 add_part (struct iterate_dir_ctx *ctx,
530 const char *part,
531 int len2)
532 {
533 int size = ctx->symlink ? grub_strlen (ctx->symlink) : 0;
534
535 ctx->symlink = grub_realloc (ctx->symlink, size + len2 + 1);
536 if (! ctx->symlink)
537 return;
538
539 grub_memcpy (ctx->symlink + size, part, len2);
540 ctx->symlink[size + len2] = 0;
541 }
542
543 static grub_err_t
544 susp_iterate_dir (struct grub_iso9660_susp_entry *entry,
545 void *_ctx)
546 {
547 struct iterate_dir_ctx *ctx = _ctx;
548
549 /* The filename in the rock ridge entry. */
550 if (grub_strncmp ("NM", (char *) entry->sig, 2) == 0)
551 {
552 /* The flags are stored at the data position 0, here the
553 filename type is stored. */
554 /* FIXME: Fix this slightly improper cast. */
555 if (entry->data[0] & GRUB_ISO9660_RR_DOT)
556 ctx->filename = (char *) ".";
557 else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT)
558 ctx->filename = (char *) "..";
559 else if (entry->len >= 5)
560 {
561 grub_size_t off = 0, csize = 1;
562 char *old;
563 csize = entry->len - 5;
564 old = ctx->filename;
565 if (ctx->filename_alloc)
566 {
567 off = grub_strlen (ctx->filename);
568 ctx->filename = grub_realloc (ctx->filename, csize + off + 1);
569 }
570 else
571 {
572 off = 0;
573 ctx->filename = grub_zalloc (csize + 1);
574 }
575 if (!ctx->filename)
576 {
577 ctx->filename = old;
578 return grub_errno;
579 }
580 ctx->filename_alloc = 1;
581 grub_memcpy (ctx->filename + off, (char *) &entry->data[1], csize);
582 ctx->filename[off + csize] = '\0';
583 }
584 }
585 /* The mode information (st_mode). */
586 else if (grub_strncmp ((char *) entry->sig, "PX", 2) == 0)
587 {
588 /* At position 0 of the PX record the st_mode information is
589 stored (little-endian). */
590 grub_uint32_t mode = ((entry->data[0] + (entry->data[1] << 8))
591 & GRUB_ISO9660_FSTYPE_MASK);
592
593 switch (mode)
594 {
595 case GRUB_ISO9660_FSTYPE_DIR:
596 ctx->type = GRUB_FSHELP_DIR;
597 break;
598 case GRUB_ISO9660_FSTYPE_REG:
599 ctx->type = GRUB_FSHELP_REG;
600 break;
601 case GRUB_ISO9660_FSTYPE_SYMLINK:
602 ctx->type = GRUB_FSHELP_SYMLINK;
603 break;
604 default:
605 ctx->type = GRUB_FSHELP_UNKNOWN;
606 }
607 }
608 else if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0)
609 {
610 unsigned int pos = 1;
611
612 /* The symlink is not stored as a POSIX symlink, translate it. */
613 while (pos + sizeof (*entry) < entry->len)
614 {
615 /* The current position is the `Component Flag'. */
616 switch (entry->data[pos] & 30)
617 {
618 case 0:
619 {
620 /* The data on pos + 2 is the actual data, pos + 1
621 is the length. Both are part of the `Component
622 Record'. */
623 if (ctx->symlink && !ctx->was_continue)
624 add_part (ctx, "/", 1);
625 add_part (ctx, (char *) &entry->data[pos + 2],
626 entry->data[pos + 1]);
627 ctx->was_continue = (entry->data[pos] & 1);
628 break;
629 }
630
631 case 2:
632 add_part (ctx, "./", 2);
633 break;
634
635 case 4:
636 add_part (ctx, "../", 3);
637 break;
638
639 case 8:
640 add_part (ctx, "/", 1);
641 break;
642 }
643 /* In pos + 1 the length of the `Component Record' is
644 stored. */
645 pos += entry->data[pos + 1] + 2;
646 }
647
648 /* Check if `grub_realloc' failed. */
649 if (grub_errno)
650 return grub_errno;
651 }
652
653 return 0;
654 }
655
656 static int
657 grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
658 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
659 {
660 struct grub_iso9660_dir dirent;
661 grub_off_t offset = 0;
662 grub_off_t len;
663 struct iterate_dir_ctx ctx;
664
665 len = get_node_size (dir);
666
667 for (; offset < len; offset += dirent.len)
668 {
669 ctx.symlink = 0;
670 ctx.was_continue = 0;
671
672 if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
673 return 0;
674
675 /* The end of the block, skip to the next one. */
676 if (!dirent.len)
677 {
678 offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ;
679 continue;
680 }
681
682 {
683 char name[MAX_NAMELEN + 1];
684 int nameoffset = offset + sizeof (dirent);
685 struct grub_fshelp_node *node;
686 int sua_off = (sizeof (dirent) + dirent.namelen + 1
687 - (dirent.namelen % 2));
688 int sua_size = dirent.len - sua_off;
689
690 sua_off += offset + dir->data->susp_skip;
691
692 ctx.filename = 0;
693 ctx.filename_alloc = 0;
694 ctx.type = GRUB_FSHELP_UNKNOWN;
695
696 if (dir->data->rockridge
697 && grub_iso9660_susp_iterate (dir, sua_off, sua_size,
698 susp_iterate_dir, &ctx))
699 return 0;
700
701 /* Read the name. */
702 if (read_node (dir, nameoffset, dirent.namelen, (char *) name))
703 return 0;
704
705 node = grub_malloc (sizeof (struct grub_fshelp_node));
706 if (!node)
707 return 0;
708
709 node->alloc_dirents = ARRAY_SIZE (node->dirents);
710 node->have_dirents = 1;
711
712 /* Setup a new node. */
713 node->data = dir->data;
714 node->have_symlink = 0;
715
716 /* If the filetype was not stored using rockridge, use
717 whatever is stored in the iso9660 filesystem. */
718 if (ctx.type == GRUB_FSHELP_UNKNOWN)
719 {
720 if ((dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR)
721 ctx.type = GRUB_FSHELP_DIR;
722 else
723 ctx.type = GRUB_FSHELP_REG;
724 }
725
726 /* . and .. */
727 if (!ctx.filename && dirent.namelen == 1 && name[0] == 0)
728 ctx.filename = (char *) ".";
729
730 if (!ctx.filename && dirent.namelen == 1 && name[0] == 1)
731 ctx.filename = (char *) "..";
732
733 /* The filename was not stored in a rock ridge entry. Read it
734 from the iso9660 filesystem. */
735 if (!dir->data->joliet && !ctx.filename)
736 {
737 char *ptr;
738 name[dirent.namelen] = '\0';
739 ctx.filename = grub_strrchr (name, ';');
740 if (ctx.filename)
741 *ctx.filename = '\0';
742 /* ISO9660 names are not case-preserving. */
743 ctx.type |= GRUB_FSHELP_CASE_INSENSITIVE;
744 for (ptr = name; *ptr; ptr++)
745 *ptr = grub_tolower (*ptr);
746 if (ptr != name && *(ptr - 1) == '.')
747 *(ptr - 1) = 0;
748 ctx.filename = name;
749 }
750
751 if (dir->data->joliet && !ctx.filename)
752 {
753 char *oldname, *semicolon;
754
755 oldname = name;
756 ctx.filename = grub_iso9660_convert_string
757 ((grub_uint8_t *) oldname, dirent.namelen >> 1);
758
759 semicolon = grub_strrchr (ctx.filename, ';');
760 if (semicolon)
761 *semicolon = '\0';
762
763 if (ctx.filename_alloc)
764 grub_free (oldname);
765
766 ctx.filename_alloc = 1;
767 }
768
769 node->dirents[0] = dirent;
770 while (dirent.flags & FLAG_MORE_EXTENTS)
771 {
772 offset += dirent.len;
773 if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
774 {
775 if (ctx.filename_alloc)
776 grub_free (ctx.filename);
777 grub_free (node);
778 return 0;
779 }
780 if (node->have_dirents >= node->alloc_dirents)
781 {
782 struct grub_fshelp_node *new_node;
783 node->alloc_dirents *= 2;
784 new_node = grub_realloc (node,
785 sizeof (struct grub_fshelp_node)
786 + ((node->alloc_dirents
787 - ARRAY_SIZE (node->dirents))
788 * sizeof (node->dirents[0])));
789 if (!new_node)
790 {
791 if (ctx.filename_alloc)
792 grub_free (ctx.filename);
793 grub_free (node);
794 return 0;
795 }
796 node = new_node;
797 }
798 node->dirents[node->have_dirents++] = dirent;
799 }
800 if (ctx.symlink)
801 {
802 if ((node->alloc_dirents - node->have_dirents)
803 * sizeof (node->dirents[0]) < grub_strlen (ctx.symlink) + 1)
804 {
805 struct grub_fshelp_node *new_node;
806 new_node = grub_realloc (node,
807 sizeof (struct grub_fshelp_node)
808 + ((node->alloc_dirents
809 - ARRAY_SIZE (node->dirents))
810 * sizeof (node->dirents[0]))
811 + grub_strlen (ctx.symlink) + 1);
812 if (!new_node)
813 {
814 if (ctx.filename_alloc)
815 grub_free (ctx.filename);
816 grub_free (node);
817 return 0;
818 }
819 node = new_node;
820 }
821 node->have_symlink = 1;
822 grub_strcpy (node->symlink
823 + node->have_dirents * sizeof (node->dirents[0])
824 - sizeof (node->dirents), ctx.symlink);
825 grub_free (ctx.symlink);
826 ctx.symlink = 0;
827 ctx.was_continue = 0;
828 }
829 if (hook (ctx.filename, ctx.type, node, hook_data))
830 {
831 if (ctx.filename_alloc)
832 grub_free (ctx.filename);
833 return 1;
834 }
835 if (ctx.filename_alloc)
836 grub_free (ctx.filename);
837 }
838 }
839
840 return 0;
841 }
842
843
844 \f
845 /* Context for grub_iso9660_dir. */
846 struct grub_iso9660_dir_ctx
847 {
848 grub_fs_dir_hook_t hook;
849 void *hook_data;
850 };
851
852 /* Helper for grub_iso9660_dir. */
853 static int
854 grub_iso9660_dir_iter (const char *filename,
855 enum grub_fshelp_filetype filetype,
856 grub_fshelp_node_t node, void *data)
857 {
858 struct grub_iso9660_dir_ctx *ctx = data;
859 struct grub_dirhook_info info;
860
861 grub_memset (&info, 0, sizeof (info));
862 info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
863 info.mtimeset = !!iso9660_to_unixtime2 (&node->dirents[0].mtime, &info.mtime);
864
865 grub_free (node);
866 return ctx->hook (filename, &info, ctx->hook_data);
867 }
868
869 static grub_err_t
870 grub_iso9660_dir (grub_device_t device, const char *path,
871 grub_fs_dir_hook_t hook, void *hook_data)
872 {
873 struct grub_iso9660_dir_ctx ctx = { hook, hook_data };
874 struct grub_iso9660_data *data = 0;
875 struct grub_fshelp_node rootnode;
876 struct grub_fshelp_node *foundnode;
877
878 grub_dl_ref (my_mod);
879
880 data = grub_iso9660_mount (device->disk);
881 if (! data)
882 goto fail;
883
884 rootnode.data = data;
885 rootnode.alloc_dirents = 0;
886 rootnode.have_dirents = 1;
887 rootnode.have_symlink = 0;
888 rootnode.dirents[0] = data->voldesc.rootdir;
889
890 /* Use the fshelp function to traverse the path. */
891 if (grub_fshelp_find_file (path, &rootnode,
892 &foundnode,
893 grub_iso9660_iterate_dir,
894 grub_iso9660_read_symlink,
895 GRUB_FSHELP_DIR))
896 goto fail;
897
898 /* List the files in the directory. */
899 grub_iso9660_iterate_dir (foundnode, grub_iso9660_dir_iter, &ctx);
900
901 if (foundnode != &rootnode)
902 grub_free (foundnode);
903
904 fail:
905 grub_free (data);
906
907 grub_dl_unref (my_mod);
908
909 return grub_errno;
910 }
911
912
913 /* Open a file named NAME and initialize FILE. */
914 static grub_err_t
915 grub_iso9660_open (struct grub_file *file, const char *name)
916 {
917 struct grub_iso9660_data *data;
918 struct grub_fshelp_node rootnode;
919 struct grub_fshelp_node *foundnode;
920
921 grub_dl_ref (my_mod);
922
923 data = grub_iso9660_mount (file->device->disk);
924 if (!data)
925 goto fail;
926
927 rootnode.data = data;
928 rootnode.alloc_dirents = 0;
929 rootnode.have_dirents = 1;
930 rootnode.have_symlink = 0;
931 rootnode.dirents[0] = data->voldesc.rootdir;
932
933 /* Use the fshelp function to traverse the path. */
934 if (grub_fshelp_find_file (name, &rootnode,
935 &foundnode,
936 grub_iso9660_iterate_dir,
937 grub_iso9660_read_symlink,
938 GRUB_FSHELP_REG))
939 goto fail;
940
941 data->node = foundnode;
942 file->data = data;
943 file->size = get_node_size (foundnode);
944 file->offset = 0;
945
946 return 0;
947
948 fail:
949 grub_dl_unref (my_mod);
950
951 grub_free (data);
952
953 return grub_errno;
954 }
955
956
957 static grub_ssize_t
958 grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len)
959 {
960 struct grub_iso9660_data *data =
961 (struct grub_iso9660_data *) file->data;
962
963 /* XXX: The file is stored in as a single extent. */
964 data->disk->read_hook = file->read_hook;
965 data->disk->read_hook_data = file->read_hook_data;
966 read_node (data->node, file->offset, len, buf);
967 data->disk->read_hook = NULL;
968
969 if (grub_errno)
970 return -1;
971
972 return len;
973 }
974
975
976 static grub_err_t
977 grub_iso9660_close (grub_file_t file)
978 {
979 struct grub_iso9660_data *data =
980 (struct grub_iso9660_data *) file->data;
981 grub_free (data->node);
982 grub_free (data);
983
984 grub_dl_unref (my_mod);
985
986 return GRUB_ERR_NONE;
987 }
988
989
990 static grub_err_t
991 grub_iso9660_label (grub_device_t device, char **label)
992 {
993 struct grub_iso9660_data *data;
994 data = grub_iso9660_mount (device->disk);
995
996 if (data)
997 {
998 if (data->joliet)
999 *label = grub_iso9660_convert_string (data->voldesc.volname, 16);
1000 else
1001 *label = grub_strndup ((char *) data->voldesc.volname, 32);
1002 if (*label)
1003 {
1004 char *ptr;
1005 for (ptr = *label; *ptr;ptr++);
1006 ptr--;
1007 while (ptr >= *label && *ptr == ' ')
1008 *ptr-- = 0;
1009 }
1010
1011 grub_free (data);
1012 }
1013 else
1014 *label = 0;
1015
1016 return grub_errno;
1017 }
1018
1019
1020 static grub_err_t
1021 grub_iso9660_uuid (grub_device_t device, char **uuid)
1022 {
1023 struct grub_iso9660_data *data;
1024 grub_disk_t disk = device->disk;
1025
1026 grub_dl_ref (my_mod);
1027
1028 data = grub_iso9660_mount (disk);
1029 if (data)
1030 {
1031 if (! data->voldesc.modified.year[0] && ! data->voldesc.modified.year[1]
1032 && ! data->voldesc.modified.year[2] && ! data->voldesc.modified.year[3]
1033 && ! data->voldesc.modified.month[0] && ! data->voldesc.modified.month[1]
1034 && ! data->voldesc.modified.day[0] && ! data->voldesc.modified.day[1]
1035 && ! data->voldesc.modified.hour[0] && ! data->voldesc.modified.hour[1]
1036 && ! data->voldesc.modified.minute[0] && ! data->voldesc.modified.minute[1]
1037 && ! data->voldesc.modified.second[0] && ! data->voldesc.modified.second[1]
1038 && ! data->voldesc.modified.hundredth[0] && ! data->voldesc.modified.hundredth[1])
1039 {
1040 grub_error (GRUB_ERR_BAD_NUMBER, "no creation date in filesystem to generate UUID");
1041 *uuid = NULL;
1042 }
1043 else
1044 {
1045 *uuid = grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
1046 data->voldesc.modified.year[0],
1047 data->voldesc.modified.year[1],
1048 data->voldesc.modified.year[2],
1049 data->voldesc.modified.year[3],
1050 data->voldesc.modified.month[0],
1051 data->voldesc.modified.month[1],
1052 data->voldesc.modified.day[0],
1053 data->voldesc.modified.day[1],
1054 data->voldesc.modified.hour[0],
1055 data->voldesc.modified.hour[1],
1056 data->voldesc.modified.minute[0],
1057 data->voldesc.modified.minute[1],
1058 data->voldesc.modified.second[0],
1059 data->voldesc.modified.second[1],
1060 data->voldesc.modified.hundredth[0],
1061 data->voldesc.modified.hundredth[1]);
1062 }
1063 }
1064 else
1065 *uuid = NULL;
1066
1067 grub_dl_unref (my_mod);
1068
1069 grub_free (data);
1070
1071 return grub_errno;
1072 }
1073
1074 /* Get writing time of filesystem. */
1075 static grub_err_t
1076 grub_iso9660_mtime (grub_device_t device, grub_int32_t *timebuf)
1077 {
1078 struct grub_iso9660_data *data;
1079 grub_disk_t disk = device->disk;
1080 grub_err_t err;
1081
1082 grub_dl_ref (my_mod);
1083
1084 data = grub_iso9660_mount (disk);
1085 if (!data)
1086 {
1087 grub_dl_unref (my_mod);
1088 return grub_errno;
1089 }
1090 err = iso9660_to_unixtime (&data->voldesc.modified, timebuf);
1091
1092 grub_dl_unref (my_mod);
1093
1094 grub_free (data);
1095
1096 return err;
1097 }
1098
1099
1100 \f
1101
1102 static struct grub_fs grub_iso9660_fs =
1103 {
1104 .name = "iso9660",
1105 .dir = grub_iso9660_dir,
1106 .open = grub_iso9660_open,
1107 .read = grub_iso9660_read,
1108 .close = grub_iso9660_close,
1109 .label = grub_iso9660_label,
1110 .uuid = grub_iso9660_uuid,
1111 .mtime = grub_iso9660_mtime,
1112 #ifdef GRUB_UTIL
1113 .reserved_first_sector = 1,
1114 .blocklist_install = 1,
1115 #endif
1116 .next = 0
1117 };
1118
1119 GRUB_MOD_INIT(iso9660)
1120 {
1121 grub_fs_register (&grub_iso9660_fs);
1122 my_mod = mod;
1123 }
1124
1125 GRUB_MOD_FINI(iso9660)
1126 {
1127 grub_fs_unregister (&grub_iso9660_fs);
1128 }