1 /* iso9660.c - iso9660 implementation with extensions:
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/>.
22 #include <grub/file.h>
24 #include <grub/misc.h>
25 #include <grub/disk.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
55 grub_uint8_t magic
[5];
59 struct grub_iso9660_date2
70 /* A directory entry. */
71 struct grub_iso9660_dir
74 grub_uint8_t ext_sectors
;
75 grub_uint32_t first_sector
;
76 grub_uint32_t first_sector_be
;
78 grub_uint32_t size_be
;
79 struct grub_iso9660_date2 mtime
;
81 grub_uint8_t unused2
[6];
82 #define MAX_NAMELEN 255
86 struct grub_iso9660_date
89 grub_uint8_t month
[2];
92 grub_uint8_t minute
[2];
93 grub_uint8_t second
[2];
94 grub_uint8_t hundredth
[2];
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
;
117 /* A single entry in the path table. */
118 struct grub_iso9660_path
121 grub_uint8_t sectors
;
122 grub_uint32_t first_sector
;
123 grub_uint16_t parentdir
;
124 grub_uint8_t name
[0];
127 /* An entry in the System Usage area of the directory entry. */
128 struct grub_iso9660_susp_entry
132 grub_uint8_t version
;
133 grub_uint8_t data
[0];
136 /* The CE entry. This is used to describe the next block where data
138 struct grub_iso9660_susp_ce
140 struct grub_iso9660_susp_entry entry
;
142 grub_uint32_t blk_be
;
144 grub_uint32_t off_be
;
146 grub_uint32_t len_be
;
149 struct grub_iso9660_data
151 struct grub_iso9660_primary_voldesc voldesc
;
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
;
164 struct grub_iso9660_dir dirents
[8];
173 FLAG_MORE_EXTENTS
= 0x80
176 static grub_dl_t my_mod
;
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
;
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
))
221 *nix
-= i
->offset
* 60 * 15;
226 read_node (grub_fshelp_node_t node
, grub_off_t off
, grub_size_t len
, char *buf
)
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
);
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
);
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
,
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
261 grub_iso9660_susp_iterate (grub_fshelp_node_t node
, grub_off_t off
,
262 grub_ssize_t sua_size
,
264 (struct grub_iso9660_susp_entry
*entry
, void *hook_arg
),
268 struct grub_iso9660_susp_entry
*entry
;
272 return GRUB_ERR_NONE
;
274 sua
= grub_malloc (sua_size
);
278 /* Load a part of the System Usage Area. */
279 err
= read_node (node
, off
, sua_size
, sua
);
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)
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
;
303 sua
= grub_malloc (sua_size
);
307 /* Load a part of the System Usage Area. */
308 err
= grub_disk_read (node
->data
->disk
, ce_block
, off
,
313 entry
= (struct grub_iso9660_susp_entry
*) sua
;
316 if (hook (entry
, hook_arg
))
328 grub_iso9660_convert_string (grub_uint8_t
*us
, int len
)
332 grub_uint16_t t
[MAX_NAMELEN
/ 2 + 1];
334 p
= grub_malloc (len
* GRUB_MAX_UTF8_PER_UTF16
+ 1);
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';
347 susp_iterate_set_rockridge (struct grub_iso9660_susp_entry
*susp_entry
,
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)
362 set_rockridge (struct grub_iso9660_data
*data
)
367 struct grub_iso9660_dir rootdir
;
368 struct grub_iso9660_susp_entry
*entry
;
372 /* Read the system use area and test it to see if SUSP is
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
;
385 return GRUB_ERR_NONE
;
387 sua
= grub_malloc (sua_size
);
391 if (grub_disk_read (data
->disk
,
392 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
393 << GRUB_ISO9660_LOG2_BLKSZ
), sua_pos
,
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
420 if (grub_iso9660_susp_iterate (&rootnode
,
421 sua_pos
, sua_size
, susp_iterate_set_rockridge
,
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
;
439 data
= grub_zalloc (sizeof (struct grub_iso9660_data
));
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
),
455 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
459 if (grub_strncmp ((char *) voldesc
.voldesc
.magic
, "CD001", 5) != 0)
461 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
465 if (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_PRIMARY
)
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. */
481 grub_memcpy((char *) &data
->voldesc
, (char *) &voldesc
,
482 sizeof (struct grub_iso9660_primary_voldesc
));
483 if (set_rockridge (data
))
488 } while (voldesc
.voldesc
.type
!= GRUB_ISO9660_VOLDESC_END
);
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 ("");
508 get_node_size (grub_fshelp_node_t node
)
513 for (i
= 0; i
< node
->have_dirents
; i
++)
514 ret
+= grub_le_to_cpu32 (node
->dirents
[i
].size
);
518 struct iterate_dir_ctx
522 enum grub_fshelp_filetype type
;
527 /* Extend the symlink. */
529 add_part (struct iterate_dir_ctx
*ctx
,
533 int size
= ctx
->symlink
? grub_strlen (ctx
->symlink
) : 0;
535 ctx
->symlink
= grub_realloc (ctx
->symlink
, size
+ len2
+ 1);
539 grub_memcpy (ctx
->symlink
+ size
, part
, len2
);
540 ctx
->symlink
[size
+ len2
] = 0;
544 susp_iterate_dir (struct grub_iso9660_susp_entry
*entry
,
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;
563 csize
= entry
->len
- 5;
565 if (ctx
->filename_alloc
)
567 off
= grub_strlen (ctx
->filename
);
568 ctx
->filename
= grub_realloc (ctx
->filename
, csize
+ off
+ 1);
573 ctx
->filename
= grub_zalloc (csize
+ 1);
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
);
595 case GRUB_ISO9660_FSTYPE_DIR
:
596 ctx
->type
= GRUB_FSHELP_DIR
;
598 case GRUB_ISO9660_FSTYPE_REG
:
599 ctx
->type
= GRUB_FSHELP_REG
;
601 case GRUB_ISO9660_FSTYPE_SYMLINK
:
602 ctx
->type
= GRUB_FSHELP_SYMLINK
;
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)
620 /* The data on pos + 2 is the actual data, pos + 1
621 is the length. Both are part of the `Component
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);
632 add_part (ctx
, "./", 2);
636 add_part (ctx
, "../", 3);
640 add_part (ctx
, "/", 1);
643 /* In pos + 1 the length of the `Component Record' is
645 pos
+= entry
->data
[pos
+ 1] + 2;
648 /* Check if `grub_realloc' failed. */
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;
663 struct iterate_dir_ctx ctx
;
665 len
= get_node_size (dir
);
667 for (; offset
< len
; offset
+= dirent
.len
)
670 ctx
.was_continue
= 0;
672 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
675 /* The end of the block, skip to the next one. */
678 offset
= (offset
/ GRUB_ISO9660_BLKSZ
+ 1) * GRUB_ISO9660_BLKSZ
;
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
;
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
))
702 if (read_node (dir
, nameoffset
, dirent
.namelen
, (char *) name
))
705 node
= grub_malloc (sizeof (struct grub_fshelp_node
));
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
;
723 ctx
.type
= GRUB_FSHELP_REG
;
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
)
738 name
[dirent
.namelen
] = '\0';
739 ctx
.filename
= grub_strrchr (name
, ';');
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) == '.')
751 if (dir
->data
->joliet
&& !ctx
.filename
)
753 char *oldname
, *semicolon
;
756 ctx
.filename
= grub_iso9660_convert_string
757 ((grub_uint8_t
*) oldname
, dirent
.namelen
>> 1);
759 semicolon
= grub_strrchr (ctx
.filename
, ';');
763 if (ctx
.filename_alloc
)
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
);
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])));
791 if (ctx
.filename_alloc
)
792 grub_free (ctx
.filename
);
798 node
->dirents
[node
->have_dirents
++] = dirent
;
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);
814 if (ctx
.filename_alloc
)
815 grub_free (ctx
.filename
);
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
);
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
);
835 if (ctx
.filename_alloc
)
836 grub_free (ctx
.filename
);
845 /* Context for grub_iso9660_dir. */
846 struct grub_iso9660_dir_ctx
848 grub_fs_dir_hook_t hook
;
852 /* Helper for grub_iso9660_dir. */
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
);
866 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
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
);
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
,
893 grub_iso9660_iterate_dir
,
894 grub_iso9660_read_symlink
,
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
);
907 grub_dl_unref (my_mod
);
913 /* Open a file named NAME and initialize FILE. */
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
);
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
,
936 grub_iso9660_iterate_dir
,
937 grub_iso9660_read_symlink
,
941 data
->node
= foundnode
;
943 file
->size
= get_node_size (foundnode
);
949 grub_dl_unref (my_mod
);
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
;
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
)
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
);
985 grub_dl_unref (my_mod
);
987 return GRUB_ERR_NONE
;
992 grub_iso9660_label (grub_device_t device
, char **label
)
994 struct grub_iso9660_data
*data
;
995 data
= grub_iso9660_mount (device
->disk
);
1000 *label
= grub_iso9660_convert_string (data
->voldesc
.volname
, 16);
1002 *label
= grub_strndup ((char *) data
->voldesc
.volname
, 32);
1006 for (ptr
= *label
; *ptr
;ptr
++);
1008 while (ptr
>= *label
&& *ptr
== ' ')
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
);
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");
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]);
1068 grub_dl_unref (my_mod
);
1075 /* Get writing time of filesystem. */
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
;
1083 grub_dl_ref (my_mod
);
1085 data
= grub_iso9660_mount (disk
);
1088 grub_dl_unref (my_mod
);
1091 err
= iso9660_to_unixtime (&data
->voldesc
.modified
, timebuf
);
1093 grub_dl_unref (my_mod
);
1103 static struct grub_fs grub_iso9660_fs
=
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
,
1114 .reserved_first_sector
= 1,
1115 .blocklist_install
= 1,
1120 GRUB_MOD_INIT(iso9660
)
1122 grub_fs_register (&grub_iso9660_fs
);
1126 GRUB_MOD_FINI(iso9660
)
1128 grub_fs_unregister (&grub_iso9660_fs
);