Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / fs / iso9660.c
blob67a67cf40dae4c00355bc4e93d32d449c45ff4c2
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.
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.
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.
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/>.
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>
32 GRUB_MOD_LICENSE ("GPLv3+");
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
39 #define GRUB_ISO9660_LOG2_BLKSZ 2
40 #define GRUB_ISO9660_BLKSZ 2048
42 #define GRUB_ISO9660_RR_DOT 2
43 #define GRUB_ISO9660_RR_DOTDOT 4
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
51 /* The head of a volume descriptor. */
52 struct grub_iso9660_voldesc
54 grub_uint8_t type;
55 grub_uint8_t magic[5];
56 grub_uint8_t version;
57 } GRUB_PACKED;
59 struct grub_iso9660_date2
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;
70 /* A directory entry. */
71 struct grub_iso9660_dir
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;
86 struct grub_iso9660_date
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;
98 /* The primary volume descriptor. Only little endian is used. */
99 struct grub_iso9660_primary_voldesc
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;
117 /* A single entry in the path table. */
118 struct grub_iso9660_path
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;
127 /* An entry in the System Usage area of the directory entry. */
128 struct grub_iso9660_susp_entry
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;
136 /* The CE entry. This is used to describe the next block where data
137 can be found. */
138 struct grub_iso9660_susp_ce
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;
149 struct grub_iso9660_data
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;
159 struct grub_fshelp_node
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];
168 enum
170 FLAG_TYPE_PLAIN = 0,
171 FLAG_TYPE_DIR = 2,
172 FLAG_TYPE = 3,
173 FLAG_MORE_EXTENTS = 0x80
176 static grub_dl_t my_mod;
179 static grub_err_t
180 iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int32_t *nix)
182 struct grub_datetime datetime;
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');
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;
207 static int
208 iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int32_t *nix)
210 struct grub_datetime datetime;
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;
219 if (!grub_datetime2unixtime (&datetime, nix))
220 return 0;
221 *nix -= i->offset * 60 * 15;
222 return 1;
225 static grub_err_t
226 read_node (grub_fshelp_node_t node, grub_off_t off, grub_size_t len, char *buf)
228 grub_size_t i = 0;
230 while (len > 0)
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))
237 off -= grub_le_to_cpu32 (node->dirents[i].size);
238 i++;
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;
254 return GRUB_ERR_NONE;
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)
267 char *sua;
268 struct grub_iso9660_susp_entry *entry;
269 grub_err_t err;
271 if (sua_size <= 0)
272 return GRUB_ERR_NONE;
274 sua = grub_malloc (sua_size);
275 if (!sua)
276 return grub_errno;
278 /* Load a part of the System Usage Area. */
279 err = read_node (node, off, sua_size, sua);
280 if (err)
281 return err;
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))
287 /* The last entry. */
288 if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
289 break;
291 /* Additional entries are stored elsewhere. */
292 if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0)
294 struct grub_iso9660_susp_ce *ce;
295 grub_disk_addr_t ce_block;
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;
302 grub_free (sua);
303 sua = grub_malloc (sua_size);
304 if (!sua)
305 return grub_errno;
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;
313 entry = (struct grub_iso9660_susp_entry *) sua;
316 if (hook (entry, hook_arg))
318 grub_free (sua);
319 return 0;
323 grub_free (sua);
324 return 0;
327 static char *
328 grub_iso9660_convert_string (grub_uint8_t *us, int len)
330 char *p;
331 int i;
332 grub_uint16_t t[MAX_NAMELEN / 2 + 1];
334 p = grub_malloc (len * GRUB_MAX_UTF8_PER_UTF16 + 1);
335 if (! p)
336 return NULL;
338 for (i=0; i<len; i++)
339 t[i] = grub_be_to_cpu16 (grub_get_unaligned16 (us + 2 * i));
341 *grub_utf16_to_utf8 ((grub_uint8_t *) p, t, len) = '\0';
343 return p;
346 static grub_err_t
347 susp_iterate_set_rockridge (struct grub_iso9660_susp_entry *susp_entry,
348 void *_data)
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)
355 data->rockridge = 1;
356 return 1;
358 return 0;
361 static grub_err_t
362 set_rockridge (struct grub_iso9660_data *data)
364 int sua_pos;
365 int sua_size;
366 char *sua;
367 struct grub_iso9660_dir rootdir;
368 struct grub_iso9660_susp_entry *entry;
370 data->rockridge = 0;
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");
380 sua_pos = (sizeof (rootdir) + rootdir.namelen
381 + (rootdir.namelen % 2) - 1);
382 sua_size = rootdir.len - sua_pos;
384 if (!sua_size)
385 return GRUB_ERR_NONE;
387 sua = grub_malloc (sua_size);
388 if (! sua)
389 return grub_errno;
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))
396 grub_free (sua);
397 return grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
400 entry = (struct grub_iso9660_susp_entry *) sua;
402 /* Test if the SUSP protocol is used on this filesystem. */
403 if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0)
405 struct grub_fshelp_node rootnode;
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;
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);
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))
424 grub_free (sua);
425 return grub_errno;
428 grub_free (sua);
429 return GRUB_ERR_NONE;
432 static struct grub_iso9660_data *
433 grub_iso9660_mount (grub_disk_t disk)
435 struct grub_iso9660_data *data = 0;
436 struct grub_iso9660_primary_voldesc voldesc;
437 int block;
439 data = grub_zalloc (sizeof (struct grub_iso9660_data));
440 if (! data)
441 return 0;
443 data->disk = disk;
445 block = 16;
448 int copy_voldesc = 0;
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))
455 grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
456 goto fail;
459 if (grub_strncmp ((char *) voldesc.voldesc.magic, "CD001", 5) != 0)
461 grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
462 goto fail;
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)
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. */
475 copy_voldesc = 1;
476 data->joliet = 1;
479 if (copy_voldesc)
481 grub_memcpy((char *) &data->voldesc, (char *) &voldesc,
482 sizeof (struct grub_iso9660_primary_voldesc));
483 if (set_rockridge (data))
484 goto fail;
487 block++;
488 } while (voldesc.voldesc.type != GRUB_ISO9660_VOLDESC_END);
490 return data;
492 fail:
493 grub_free (data);
494 return 0;
498 static char *
499 grub_iso9660_read_symlink (grub_fshelp_node_t node)
501 return node->have_symlink
502 ? grub_strdup (node->symlink
503 + (node->have_dirents) * sizeof (node->dirents[0])
504 - sizeof (node->dirents)) : grub_strdup ("");
507 static grub_off_t
508 get_node_size (grub_fshelp_node_t node)
510 grub_off_t ret = 0;
511 grub_size_t i;
513 for (i = 0; i < node->have_dirents; i++)
514 ret += grub_le_to_cpu32 (node->dirents[i].size);
515 return ret;
518 struct iterate_dir_ctx
520 char *filename;
521 int filename_alloc;
522 enum grub_fshelp_filetype type;
523 char *symlink;
524 int was_continue;
527 /* Extend the symlink. */
528 static void
529 add_part (struct iterate_dir_ctx *ctx,
530 const char *part,
531 int len2)
533 int size = ctx->symlink ? grub_strlen (ctx->symlink) : 0;
535 ctx->symlink = grub_realloc (ctx->symlink, size + len2 + 1);
536 if (! ctx->symlink)
537 return;
539 grub_memcpy (ctx->symlink + size, part, len2);
540 ctx->symlink[size + len2] = 0;
543 static grub_err_t
544 susp_iterate_dir (struct grub_iso9660_susp_entry *entry,
545 void *_ctx)
547 struct iterate_dir_ctx *ctx = _ctx;
549 /* The filename in the rock ridge entry. */
550 if (grub_strncmp ("NM", (char *) entry->sig, 2) == 0)
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)
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)
567 off = grub_strlen (ctx->filename);
568 ctx->filename = grub_realloc (ctx->filename, csize + off + 1);
570 else
572 off = 0;
573 ctx->filename = grub_zalloc (csize + 1);
575 if (!ctx->filename)
577 ctx->filename = old;
578 return grub_errno;
580 ctx->filename_alloc = 1;
581 grub_memcpy (ctx->filename + off, (char *) &entry->data[1], csize);
582 ctx->filename[off + csize] = '\0';
585 /* The mode information (st_mode). */
586 else if (grub_strncmp ((char *) entry->sig, "PX", 2) == 0)
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);
593 switch (mode)
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;
608 else if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0)
610 unsigned int pos = 1;
612 /* The symlink is not stored as a POSIX symlink, translate it. */
613 while (pos + sizeof (*entry) < entry->len)
615 /* The current position is the `Component Flag'. */
616 switch (entry->data[pos] & 30)
618 case 0:
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;
631 case 2:
632 add_part (ctx, "./", 2);
633 break;
635 case 4:
636 add_part (ctx, "../", 3);
637 break;
639 case 8:
640 add_part (ctx, "/", 1);
641 break;
643 /* In pos + 1 the length of the `Component Record' is
644 stored. */
645 pos += entry->data[pos + 1] + 2;
648 /* Check if `grub_realloc' failed. */
649 if (grub_errno)
650 return grub_errno;
653 return 0;
656 static int
657 grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
658 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
660 struct grub_iso9660_dir dirent;
661 grub_off_t offset = 0;
662 grub_off_t len;
663 struct iterate_dir_ctx ctx;
665 len = get_node_size (dir);
667 for (; offset < len; offset += dirent.len)
669 ctx.symlink = 0;
670 ctx.was_continue = 0;
672 if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
673 return 0;
675 /* The end of the block, skip to the next one. */
676 if (!dirent.len)
678 offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ;
679 continue;
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;
690 sua_off += offset + dir->data->susp_skip;
692 ctx.filename = 0;
693 ctx.filename_alloc = 0;
694 ctx.type = GRUB_FSHELP_UNKNOWN;
696 if (dir->data->rockridge
697 && grub_iso9660_susp_iterate (dir, sua_off, sua_size,
698 susp_iterate_dir, &ctx))
699 return 0;
701 /* Read the name. */
702 if (read_node (dir, nameoffset, dirent.namelen, (char *) name))
703 return 0;
705 node = grub_malloc (sizeof (struct grub_fshelp_node));
706 if (!node)
707 return 0;
709 node->alloc_dirents = ARRAY_SIZE (node->dirents);
710 node->have_dirents = 1;
712 /* Setup a new node. */
713 node->data = dir->data;
714 node->have_symlink = 0;
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)
720 if ((dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR)
721 ctx.type = GRUB_FSHELP_DIR;
722 else
723 ctx.type = GRUB_FSHELP_REG;
726 /* . and .. */
727 if (!ctx.filename && dirent.namelen == 1 && name[0] == 0)
728 ctx.filename = (char *) ".";
730 if (!ctx.filename && dirent.namelen == 1 && name[0] == 1)
731 ctx.filename = (char *) "..";
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)
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;
751 if (dir->data->joliet && !ctx.filename)
753 char *oldname, *semicolon;
755 oldname = name;
756 ctx.filename = grub_iso9660_convert_string
757 ((grub_uint8_t *) oldname, dirent.namelen >> 1);
759 semicolon = grub_strrchr (ctx.filename, ';');
760 if (semicolon)
761 *semicolon = '\0';
763 if (ctx.filename_alloc)
764 grub_free (oldname);
766 ctx.filename_alloc = 1;
769 node->dirents[0] = dirent;
770 while (dirent.flags & FLAG_MORE_EXTENTS)
772 offset += dirent.len;
773 if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
775 if (ctx.filename_alloc)
776 grub_free (ctx.filename);
777 grub_free (node);
778 return 0;
780 if (node->have_dirents >= node->alloc_dirents)
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)
791 if (ctx.filename_alloc)
792 grub_free (ctx.filename);
793 grub_free (node);
794 return 0;
796 node = new_node;
798 node->dirents[node->have_dirents++] = dirent;
800 if (ctx.symlink)
802 if ((node->alloc_dirents - node->have_dirents)
803 * sizeof (node->dirents[0]) < grub_strlen (ctx.symlink) + 1)
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)
814 if (ctx.filename_alloc)
815 grub_free (ctx.filename);
816 grub_free (node);
817 return 0;
819 node = new_node;
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;
829 if (hook (ctx.filename, ctx.type, node, hook_data))
831 if (ctx.filename_alloc)
832 grub_free (ctx.filename);
833 return 1;
835 if (ctx.filename_alloc)
836 grub_free (ctx.filename);
840 return 0;
845 /* Context for grub_iso9660_dir. */
846 struct grub_iso9660_dir_ctx
848 grub_fs_dir_hook_t hook;
849 void *hook_data;
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)
858 struct grub_iso9660_dir_ctx *ctx = data;
859 struct grub_dirhook_info info;
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);
865 grub_free (node);
866 return ctx->hook (filename, &info, ctx->hook_data);
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)
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;
878 grub_dl_ref (my_mod);
880 data = grub_iso9660_mount (device->disk);
881 if (! data)
882 goto fail;
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;
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;
898 /* List the files in the directory. */
899 grub_iso9660_iterate_dir (foundnode, grub_iso9660_dir_iter, &ctx);
901 if (foundnode != &rootnode)
902 grub_free (foundnode);
904 fail:
905 grub_free (data);
907 grub_dl_unref (my_mod);
909 return grub_errno;
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)
917 struct grub_iso9660_data *data;
918 struct grub_fshelp_node rootnode;
919 struct grub_fshelp_node *foundnode;
921 grub_dl_ref (my_mod);
923 data = grub_iso9660_mount (file->device->disk);
924 if (!data)
925 goto fail;
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;
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;
941 data->node = foundnode;
942 file->data = data;
943 file->size = get_node_size (foundnode);
944 file->offset = 0;
946 return 0;
948 fail:
949 grub_dl_unref (my_mod);
951 grub_free (data);
953 return grub_errno;
957 static grub_ssize_t
958 grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len)
960 struct grub_iso9660_data *data =
961 (struct grub_iso9660_data *) file->data;
962 grub_err_t err;
964 /* XXX: The file is stored in as a single extent. */
965 data->disk->read_hook = file->read_hook;
966 data->disk->read_hook_data = file->read_hook_data;
967 err = read_node (data->node, file->offset, len, buf);
968 data->disk->read_hook = NULL;
970 if (err || grub_errno)
971 return -1;
973 return len;
977 static grub_err_t
978 grub_iso9660_close (grub_file_t file)
980 struct grub_iso9660_data *data =
981 (struct grub_iso9660_data *) file->data;
982 grub_free (data->node);
983 grub_free (data);
985 grub_dl_unref (my_mod);
987 return GRUB_ERR_NONE;
991 static grub_err_t
992 grub_iso9660_label (grub_device_t device, char **label)
994 struct grub_iso9660_data *data;
995 data = grub_iso9660_mount (device->disk);
997 if (data)
999 if (data->joliet)
1000 *label = grub_iso9660_convert_string (data->voldesc.volname, 16);
1001 else
1002 *label = grub_strndup ((char *) data->voldesc.volname, 32);
1003 if (*label)
1005 char *ptr;
1006 for (ptr = *label; *ptr;ptr++);
1007 ptr--;
1008 while (ptr >= *label && *ptr == ' ')
1009 *ptr-- = 0;
1012 grub_free (data);
1014 else
1015 *label = 0;
1017 return grub_errno;
1021 static grub_err_t
1022 grub_iso9660_uuid (grub_device_t device, char **uuid)
1024 struct grub_iso9660_data *data;
1025 grub_disk_t disk = device->disk;
1027 grub_dl_ref (my_mod);
1029 data = grub_iso9660_mount (disk);
1030 if (data)
1032 if (! data->voldesc.modified.year[0] && ! data->voldesc.modified.year[1]
1033 && ! data->voldesc.modified.year[2] && ! data->voldesc.modified.year[3]
1034 && ! data->voldesc.modified.month[0] && ! data->voldesc.modified.month[1]
1035 && ! data->voldesc.modified.day[0] && ! data->voldesc.modified.day[1]
1036 && ! data->voldesc.modified.hour[0] && ! data->voldesc.modified.hour[1]
1037 && ! data->voldesc.modified.minute[0] && ! data->voldesc.modified.minute[1]
1038 && ! data->voldesc.modified.second[0] && ! data->voldesc.modified.second[1]
1039 && ! data->voldesc.modified.hundredth[0] && ! data->voldesc.modified.hundredth[1])
1041 grub_error (GRUB_ERR_BAD_NUMBER, "no creation date in filesystem to generate UUID");
1042 *uuid = NULL;
1044 else
1046 *uuid = grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
1047 data->voldesc.modified.year[0],
1048 data->voldesc.modified.year[1],
1049 data->voldesc.modified.year[2],
1050 data->voldesc.modified.year[3],
1051 data->voldesc.modified.month[0],
1052 data->voldesc.modified.month[1],
1053 data->voldesc.modified.day[0],
1054 data->voldesc.modified.day[1],
1055 data->voldesc.modified.hour[0],
1056 data->voldesc.modified.hour[1],
1057 data->voldesc.modified.minute[0],
1058 data->voldesc.modified.minute[1],
1059 data->voldesc.modified.second[0],
1060 data->voldesc.modified.second[1],
1061 data->voldesc.modified.hundredth[0],
1062 data->voldesc.modified.hundredth[1]);
1065 else
1066 *uuid = NULL;
1068 grub_dl_unref (my_mod);
1070 grub_free (data);
1072 return grub_errno;
1075 /* Get writing time of filesystem. */
1076 static grub_err_t
1077 grub_iso9660_mtime (grub_device_t device, grub_int32_t *timebuf)
1079 struct grub_iso9660_data *data;
1080 grub_disk_t disk = device->disk;
1081 grub_err_t err;
1083 grub_dl_ref (my_mod);
1085 data = grub_iso9660_mount (disk);
1086 if (!data)
1088 grub_dl_unref (my_mod);
1089 return grub_errno;
1091 err = iso9660_to_unixtime (&data->voldesc.modified, timebuf);
1093 grub_dl_unref (my_mod);
1095 grub_free (data);
1097 return err;
1103 static struct grub_fs grub_iso9660_fs =
1105 .name = "iso9660",
1106 .dir = grub_iso9660_dir,
1107 .open = grub_iso9660_open,
1108 .read = grub_iso9660_read,
1109 .close = grub_iso9660_close,
1110 .label = grub_iso9660_label,
1111 .uuid = grub_iso9660_uuid,
1112 .mtime = grub_iso9660_mtime,
1113 #ifdef GRUB_UTIL
1114 .reserved_first_sector = 1,
1115 .blocklist_install = 1,
1116 #endif
1117 .next = 0
1120 GRUB_MOD_INIT(iso9660)
1122 grub_fs_register (&grub_iso9660_fs);
1123 my_mod = mod;
1126 GRUB_MOD_FINI(iso9660)
1128 grub_fs_unregister (&grub_iso9660_fs);