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 extern int g_fs_name_nocase
;
52 /* The head of a volume descriptor. */
54 struct grub_iso9660_voldesc
57 grub_uint8_t magic
[5];
61 struct grub_iso9660_date2
72 /* A directory entry. */
73 struct grub_iso9660_dir
76 grub_uint8_t ext_sectors
;
77 grub_uint32_t first_sector
;
78 grub_uint32_t first_sector_be
;
80 grub_uint32_t size_be
;
81 struct grub_iso9660_date2 mtime
;
83 grub_uint8_t unused2
[6];
84 #define MAX_NAMELEN 255
88 struct grub_iso9660_date
91 grub_uint8_t month
[2];
94 grub_uint8_t minute
[2];
95 grub_uint8_t second
[2];
96 grub_uint8_t hundredth
[2];
100 /* The primary volume descriptor. Only little endian is used. */
101 struct grub_iso9660_primary_voldesc
103 struct grub_iso9660_voldesc voldesc
;
104 grub_uint8_t unused1
[33];
105 grub_uint8_t volname
[32];
106 grub_uint8_t unused2
[16];
107 grub_uint8_t escape
[32];
108 grub_uint8_t unused3
[12];
109 grub_uint32_t path_table_size
;
110 grub_uint8_t unused4
[4];
111 grub_uint32_t path_table
;
112 grub_uint8_t unused5
[12];
113 struct grub_iso9660_dir rootdir
;
114 grub_uint8_t unused6
[624];
115 struct grub_iso9660_date created
;
116 struct grub_iso9660_date modified
;
119 /* A single entry in the path table. */
120 struct grub_iso9660_path
123 grub_uint8_t sectors
;
124 grub_uint32_t first_sector
;
125 grub_uint16_t parentdir
;
126 grub_uint8_t name
[0];
129 /* An entry in the System Usage area of the directory entry. */
130 struct grub_iso9660_susp_entry
134 /*! MSVC compilers cannot handle a zero sized array in the middle
135 of a struct, and grub_iso9660_susp_entry is reused within
136 grub_iso9660_susp_ce. Therefore, instead of defining:
137 grub_uint8_t version;
139 we leverage the fact that these attributes are the same size
140 and use an union. The only gotcha is that the actual
141 payload of u.data[] starts at 1, not 0. */
143 grub_uint8_t version
;
144 grub_uint8_t data
[1];
148 /* The CE entry. This is used to describe the next block where data
150 struct grub_iso9660_susp_ce
152 struct grub_iso9660_susp_entry entry
;
154 grub_uint32_t blk_be
;
156 grub_uint32_t off_be
;
158 grub_uint32_t len_be
;
162 struct grub_iso9660_data
164 struct grub_iso9660_primary_voldesc voldesc
;
169 struct grub_fshelp_node
*node
;
172 struct grub_fshelp_node
174 struct grub_iso9660_data
*data
;
175 grub_size_t have_dirents
, alloc_dirents
;
177 struct grub_iso9660_dir dirents
[8];
186 FLAG_MORE_EXTENTS
= 0x80
189 static grub_dl_t my_mod
;
193 iso9660_to_unixtime (const struct grub_iso9660_date
*i
, grub_int32_t
*nix
)
195 struct grub_datetime datetime
;
197 if (! i
->year
[0] && ! i
->year
[1]
198 && ! i
->year
[2] && ! i
->year
[3]
199 && ! i
->month
[0] && ! i
->month
[1]
200 && ! i
->day
[0] && ! i
->day
[1]
201 && ! i
->hour
[0] && ! i
->hour
[1]
202 && ! i
->minute
[0] && ! i
->minute
[1]
203 && ! i
->second
[0] && ! i
->second
[1]
204 && ! i
->hundredth
[0] && ! i
->hundredth
[1])
205 return grub_error (GRUB_ERR_BAD_NUMBER
, "empty date");
206 datetime
.year
= (i
->year
[0] - '0') * 1000 + (i
->year
[1] - '0') * 100
207 + (i
->year
[2] - '0') * 10 + (i
->year
[3] - '0');
208 datetime
.month
= (i
->month
[0] - '0') * 10 + (i
->month
[1] - '0');
209 datetime
.day
= (i
->day
[0] - '0') * 10 + (i
->day
[1] - '0');
210 datetime
.hour
= (i
->hour
[0] - '0') * 10 + (i
->hour
[1] - '0');
211 datetime
.minute
= (i
->minute
[0] - '0') * 10 + (i
->minute
[1] - '0');
212 datetime
.second
= (i
->second
[0] - '0') * 10 + (i
->second
[1] - '0');
214 if (!grub_datetime2unixtime (&datetime
, nix
))
215 return grub_error (GRUB_ERR_BAD_NUMBER
, "incorrect date");
216 *nix
-= i
->offset
* 60 * 15;
217 return GRUB_ERR_NONE
;
221 iso9660_to_unixtime2 (const struct grub_iso9660_date2
*i
, grub_int32_t
*nix
)
223 struct grub_datetime datetime
;
225 datetime
.year
= i
->year
+ 1900;
226 datetime
.month
= i
->month
;
227 datetime
.day
= i
->day
;
228 datetime
.hour
= i
->hour
;
229 datetime
.minute
= i
->minute
;
230 datetime
.second
= i
->second
;
232 if (!grub_datetime2unixtime (&datetime
, nix
))
234 *nix
-= i
->offset
* 60 * 15;
239 read_node (grub_fshelp_node_t node
, grub_off_t off
, grub_size_t len
, char *buf
)
247 while (i
< node
->have_dirents
248 && off
>= grub_le_to_cpu32 (node
->dirents
[i
].size
))
250 off
-= grub_le_to_cpu32 (node
->dirents
[i
].size
);
253 if (i
== node
->have_dirents
)
254 return grub_error (GRUB_ERR_OUT_OF_RANGE
, "read out of range");
255 toread
= grub_le_to_cpu32 (node
->dirents
[i
].size
);
258 err
= grub_disk_read (node
->data
->disk
,
259 ((grub_disk_addr_t
) grub_le_to_cpu32 (node
->dirents
[i
].first_sector
)) << GRUB_ISO9660_LOG2_BLKSZ
,
267 return GRUB_ERR_NONE
;
270 /* Iterate over the susp entries, starting with block SUA_BLOCK on the
271 offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for
274 grub_iso9660_susp_iterate (grub_fshelp_node_t node
, grub_off_t off
,
275 grub_ssize_t sua_size
,
277 (struct grub_iso9660_susp_entry
*entry
, void *hook_arg
),
281 struct grub_iso9660_susp_entry
*entry
;
285 return GRUB_ERR_NONE
;
287 sua
= grub_malloc (sua_size
);
291 /* Load a part of the System Usage Area. */
292 err
= read_node (node
, off
, sua_size
, sua
);
296 for (entry
= (struct grub_iso9660_susp_entry
*) sua
; (char *) entry
< (char *) sua
+ sua_size
- 1 && entry
->len
> 0;
297 entry
= (struct grub_iso9660_susp_entry
*)
298 ((char *) entry
+ entry
->len
))
300 /* The last entry. */
301 if (grub_strncmp ((char *) entry
->sig
, "ST", 2) == 0)
304 /* Additional entries are stored elsewhere. */
305 if (grub_strncmp ((char *) entry
->sig
, "CE", 2) == 0)
307 struct grub_iso9660_susp_ce
*ce
;
308 grub_disk_addr_t ce_block
;
310 ce
= (struct grub_iso9660_susp_ce
*) entry
;
311 sua_size
= grub_le_to_cpu32 (ce
->len
);
312 off
= grub_le_to_cpu32 (ce
->off
);
313 ce_block
= grub_le_to_cpu32 (ce
->blk
) << GRUB_ISO9660_LOG2_BLKSZ
;
316 sua
= grub_malloc (sua_size
);
320 /* Load a part of the System Usage Area. */
321 err
= grub_disk_read (node
->data
->disk
, ce_block
, off
,
326 entry
= (struct grub_iso9660_susp_entry
*) sua
;
329 if (hook (entry
, hook_arg
))
341 grub_iso9660_convert_string (grub_uint8_t
*us
, int len
)
345 grub_uint16_t t
[MAX_NAMELEN
/ 2 + 1];
347 p
= grub_malloc (len
* GRUB_MAX_UTF8_PER_UTF16
+ 1);
351 for (i
=0; i
<len
; i
++)
352 t
[i
] = grub_be_to_cpu16 (grub_get_unaligned16 (us
+ 2 * i
));
354 *grub_utf16_to_utf8 ((grub_uint8_t
*) p
, t
, len
) = '\0';
360 susp_iterate_set_rockridge (struct grub_iso9660_susp_entry
*susp_entry
,
363 struct grub_iso9660_data
*data
= _data
;
364 /* The "ER" entry is used to detect extensions. The
365 `IEEE_P1285' extension means Rock ridge. */
366 if (grub_strncmp ((char *) susp_entry
->sig
, "ER", 2) == 0)
375 set_rockridge (struct grub_iso9660_data
*data
)
380 struct grub_iso9660_dir rootdir
;
381 struct grub_iso9660_susp_entry
*entry
;
385 /* Read the system use area and test it to see if SUSP is
387 if (grub_disk_read (data
->disk
,
388 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
389 << GRUB_ISO9660_LOG2_BLKSZ
), 0,
390 sizeof (rootdir
), (char *) &rootdir
))
391 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
393 sua_pos
= (sizeof (rootdir
) + rootdir
.namelen
394 + (rootdir
.namelen
% 2) - 1);
395 sua_size
= rootdir
.len
- sua_pos
;
398 return GRUB_ERR_NONE
;
400 sua
= grub_malloc (sua_size
);
404 if (grub_disk_read (data
->disk
,
405 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
406 << GRUB_ISO9660_LOG2_BLKSZ
), sua_pos
,
410 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
413 entry
= (struct grub_iso9660_susp_entry
*) sua
;
415 /* Test if the SUSP protocol is used on this filesystem. */
416 if (grub_strncmp ((char *) entry
->sig
, "SP", 2) == 0)
418 struct grub_fshelp_node rootnode
;
420 rootnode
.data
= data
;
421 rootnode
.alloc_dirents
= ARRAY_SIZE (rootnode
.dirents
);
422 rootnode
.have_dirents
= 1;
423 rootnode
.have_symlink
= 0;
424 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
426 /* The 2nd data byte stored how many bytes are skipped every time
427 to get to the SUA (System Usage Area). */
428 data
->susp_skip
= entry
->u
.data
[1 + 2];
429 entry
= (struct grub_iso9660_susp_entry
*) ((char *) entry
+ entry
->len
);
431 /* Iterate over the entries in the SUA area to detect
433 if (grub_iso9660_susp_iterate (&rootnode
,
434 sua_pos
, sua_size
, susp_iterate_set_rockridge
,
442 return GRUB_ERR_NONE
;
445 static struct grub_iso9660_data
*
446 grub_iso9660_mount (grub_disk_t disk
)
448 struct grub_iso9660_data
*data
= 0;
449 struct grub_iso9660_primary_voldesc voldesc
;
452 data
= grub_zalloc (sizeof (struct grub_iso9660_data
));
461 int copy_voldesc
= 0;
463 /* Read the superblock. */
464 if (grub_disk_read (disk
, block
<< GRUB_ISO9660_LOG2_BLKSZ
, 0,
465 sizeof (struct grub_iso9660_primary_voldesc
),
468 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
472 if (grub_strncmp ((char *) voldesc
.voldesc
.magic
, "CD001", 5) != 0)
474 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
478 if (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_PRIMARY
)
480 else if (!data
->rockridge
481 && (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_SUPP
)
482 && (voldesc
.escape
[0] == 0x25) && (voldesc
.escape
[1] == 0x2f)
484 ((voldesc
.escape
[2] == 0x40) || /* UCS-2 Level 1. */
485 (voldesc
.escape
[2] == 0x43) || /* UCS-2 Level 2. */
486 (voldesc
.escape
[2] == 0x45))) /* UCS-2 Level 3. */
494 grub_memcpy((char *) &data
->voldesc
, (char *) &voldesc
,
495 sizeof (struct grub_iso9660_primary_voldesc
));
496 if (set_rockridge (data
))
501 } while (voldesc
.voldesc
.type
!= GRUB_ISO9660_VOLDESC_END
);
512 grub_iso9660_read_symlink (grub_fshelp_node_t node
)
514 return node
->have_symlink
515 ? grub_strdup (node
->symlink
516 + (node
->have_dirents
) * sizeof (node
->dirents
[0])
517 - sizeof (node
->dirents
)) : grub_strdup ("");
521 get_node_size (grub_fshelp_node_t node
)
526 for (i
= 0; i
< node
->have_dirents
; i
++)
527 ret
+= grub_le_to_cpu32 (node
->dirents
[i
].size
);
531 struct iterate_dir_ctx
535 enum grub_fshelp_filetype type
;
540 /* Extend the symlink. */
542 add_part (struct iterate_dir_ctx
*ctx
,
546 int size
= ctx
->symlink
? grub_strlen (ctx
->symlink
) : 0;
548 ctx
->symlink
= grub_realloc (ctx
->symlink
, size
+ len2
+ 1);
552 grub_memcpy (ctx
->symlink
+ size
, part
, len2
);
553 ctx
->symlink
[size
+ len2
] = 0;
557 susp_iterate_dir (struct grub_iso9660_susp_entry
*entry
,
560 struct iterate_dir_ctx
*ctx
= _ctx
;
562 /* The filename in the rock ridge entry. */
563 if (grub_strncmp ("NM", (char *) entry
->sig
, 2) == 0)
565 /* The flags are stored at the data position 0, here the
566 filename type is stored. */
567 /* FIXME: Fix this slightly improper cast. */
568 if (entry
->u
.data
[1 + 0] & GRUB_ISO9660_RR_DOT
)
569 ctx
->filename
= (char *) ".";
570 else if (entry
->u
.data
[1 + 0] & GRUB_ISO9660_RR_DOTDOT
)
571 ctx
->filename
= (char *) "..";
572 else if (entry
->len
>= 5)
574 grub_size_t off
= 0, csize
= 1;
576 csize
= entry
->len
- 5;
578 if (ctx
->filename_alloc
)
580 off
= grub_strlen (ctx
->filename
);
581 ctx
->filename
= grub_realloc (ctx
->filename
, csize
+ off
+ 1);
586 ctx
->filename
= grub_zalloc (csize
+ 1);
593 ctx
->filename_alloc
= 1;
594 grub_memcpy (ctx
->filename
+ off
, (char *) &entry
->u
.data
[1 + 1], csize
);
595 ctx
->filename
[off
+ csize
] = '\0';
598 /* The mode information (st_mode). */
599 else if (grub_strncmp ((char *) entry
->sig
, "PX", 2) == 0)
601 /* At position 0 of the PX record the st_mode information is
602 stored (little-endian). */
603 grub_uint32_t mode
= ((entry
->u
.data
[1 + 0] + (entry
->u
.data
[1 + 1] << 8))
604 & GRUB_ISO9660_FSTYPE_MASK
);
608 case GRUB_ISO9660_FSTYPE_DIR
:
609 ctx
->type
= GRUB_FSHELP_DIR
;
611 case GRUB_ISO9660_FSTYPE_REG
:
612 ctx
->type
= GRUB_FSHELP_REG
;
614 case GRUB_ISO9660_FSTYPE_SYMLINK
:
615 ctx
->type
= GRUB_FSHELP_SYMLINK
;
618 ctx
->type
= GRUB_FSHELP_UNKNOWN
;
621 else if (grub_strncmp ("SL", (char *) entry
->sig
, 2) == 0)
623 unsigned int pos
= 1;
625 /* The symlink is not stored as a POSIX symlink, translate it. */
626 while (pos
+ sizeof (*entry
) < entry
->len
)
628 /* The current position is the `Component Flag'. */
629 switch (entry
->u
.data
[1 + pos
] & 30)
633 /* The data on pos + 2 is the actual data, pos + 1
634 is the length. Both are part of the `Component
636 if (ctx
->symlink
&& !ctx
->was_continue
)
637 add_part (ctx
, "/", 1);
638 add_part (ctx
, (char *) &entry
->u
.data
[1 + pos
+ 2],
639 entry
->u
.data
[1 + pos
+ 1]);
640 ctx
->was_continue
= (entry
->u
.data
[1 + pos
] & 1);
645 add_part (ctx
, "./", 2);
649 add_part (ctx
, "../", 3);
653 add_part (ctx
, "/", 1);
656 /* In pos + 1 the length of the `Component Record' is
658 pos
+= entry
->u
.data
[1 + pos
+ 1] + 2;
661 /* Check if `grub_realloc' failed. */
670 grub_iso9660_iterate_dir (grub_fshelp_node_t dir
,
671 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
673 struct grub_iso9660_dir dirent
;
674 grub_off_t offset
= 0;
676 struct iterate_dir_ctx ctx
;
678 len
= get_node_size (dir
);
680 for (; offset
< len
; offset
+= dirent
.len
)
683 ctx
.was_continue
= 0;
685 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
688 /* The end of the block, skip to the next one. */
691 offset
= (offset
/ GRUB_ISO9660_BLKSZ
+ 1) * GRUB_ISO9660_BLKSZ
;
696 char name
[MAX_NAMELEN
+ 1];
697 int nameoffset
= offset
+ sizeof (dirent
);
698 struct grub_fshelp_node
*node
;
699 int sua_off
= (sizeof (dirent
) + dirent
.namelen
+ 1
700 - (dirent
.namelen
% 2));
701 int sua_size
= dirent
.len
- sua_off
;
703 sua_off
+= offset
+ dir
->data
->susp_skip
;
706 ctx
.filename_alloc
= 0;
707 ctx
.type
= GRUB_FSHELP_UNKNOWN
;
709 if (dir
->data
->rockridge
710 && grub_iso9660_susp_iterate (dir
, sua_off
, sua_size
,
711 susp_iterate_dir
, &ctx
))
715 if (read_node (dir
, nameoffset
, dirent
.namelen
, (char *) name
))
718 node
= grub_malloc (sizeof (struct grub_fshelp_node
));
722 node
->alloc_dirents
= ARRAY_SIZE (node
->dirents
);
723 node
->have_dirents
= 1;
725 /* Setup a new node. */
726 node
->data
= dir
->data
;
727 node
->have_symlink
= 0;
729 /* If the filetype was not stored using rockridge, use
730 whatever is stored in the iso9660 filesystem. */
731 if (ctx
.type
== GRUB_FSHELP_UNKNOWN
)
733 if ((dirent
.flags
& FLAG_TYPE
) == FLAG_TYPE_DIR
)
734 ctx
.type
= GRUB_FSHELP_DIR
;
736 ctx
.type
= GRUB_FSHELP_REG
;
740 if (!ctx
.filename
&& dirent
.namelen
== 1 && name
[0] == 0)
741 ctx
.filename
= (char *) ".";
743 if (!ctx
.filename
&& dirent
.namelen
== 1 && name
[0] == 1)
744 ctx
.filename
= (char *) "..";
746 if (g_fs_name_nocase
)
747 ctx
.type
|= GRUB_FSHELP_CASE_INSENSITIVE
;
749 /* The filename was not stored in a rock ridge entry. Read it
750 from the iso9660 filesystem. */
751 if (!dir
->data
->joliet
&& !ctx
.filename
)
754 name
[dirent
.namelen
] = '\0';
755 ctx
.filename
= grub_strrchr (name
, ';');
757 *ctx
.filename
= '\0';
758 /* ISO9660 names are not case-preserving. */
759 ctx
.type
|= GRUB_FSHELP_CASE_INSENSITIVE
;
760 for (ptr
= name
; *ptr
; ptr
++)
761 *ptr
= grub_tolower (*ptr
);
762 if (ptr
!= name
&& *(ptr
- 1) == '.')
767 if (dir
->data
->joliet
&& !ctx
.filename
)
771 ctx
.filename
= grub_iso9660_convert_string
772 ((grub_uint8_t
*) name
, dirent
.namelen
>> 1);
774 semicolon
= grub_strrchr (ctx
.filename
, ';');
778 ctx
.filename_alloc
= 1;
781 node
->dirents
[0] = dirent
;
782 while (dirent
.flags
& FLAG_MORE_EXTENTS
)
784 offset
+= dirent
.len
;
785 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
787 if (ctx
.filename_alloc
)
788 grub_free (ctx
.filename
);
792 if (node
->have_dirents
>= node
->alloc_dirents
)
794 struct grub_fshelp_node
*new_node
;
795 node
->alloc_dirents
*= 2;
796 new_node
= grub_realloc (node
,
797 sizeof (struct grub_fshelp_node
)
798 + ((node
->alloc_dirents
799 - ARRAY_SIZE (node
->dirents
))
800 * sizeof (node
->dirents
[0])));
803 if (ctx
.filename_alloc
)
804 grub_free (ctx
.filename
);
810 node
->dirents
[node
->have_dirents
++] = dirent
;
814 if ((node
->alloc_dirents
- node
->have_dirents
)
815 * sizeof (node
->dirents
[0]) < grub_strlen (ctx
.symlink
) + 1)
817 struct grub_fshelp_node
*new_node
;
818 new_node
= grub_realloc (node
,
819 sizeof (struct grub_fshelp_node
)
820 + ((node
->alloc_dirents
821 - ARRAY_SIZE (node
->dirents
))
822 * sizeof (node
->dirents
[0]))
823 + grub_strlen (ctx
.symlink
) + 1);
826 if (ctx
.filename_alloc
)
827 grub_free (ctx
.filename
);
833 node
->have_symlink
= 1;
834 grub_strcpy (node
->symlink
835 + node
->have_dirents
* sizeof (node
->dirents
[0])
836 - sizeof (node
->dirents
), ctx
.symlink
);
837 grub_free (ctx
.symlink
);
839 ctx
.was_continue
= 0;
841 if (hook (ctx
.filename
, ctx
.type
, node
, hook_data
))
843 if (ctx
.filename_alloc
)
844 grub_free (ctx
.filename
);
847 if (ctx
.filename_alloc
)
848 grub_free (ctx
.filename
);
857 /* Context for grub_iso9660_dir. */
858 struct grub_iso9660_dir_ctx
860 grub_fs_dir_hook_t hook
;
864 /* Helper for grub_iso9660_dir. */
866 grub_iso9660_dir_iter (const char *filename
,
867 enum grub_fshelp_filetype filetype
,
868 grub_fshelp_node_t node
, void *data
)
870 struct grub_iso9660_dir_ctx
*ctx
= data
;
871 struct grub_dirhook_info info
;
873 grub_memset (&info
, 0, sizeof (info
));
874 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
875 info
.mtimeset
= !!iso9660_to_unixtime2 (&node
->dirents
[0].mtime
, &info
.mtime
);
878 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
882 grub_iso9660_dir (grub_device_t device
, const char *path
,
883 grub_fs_dir_hook_t hook
, void *hook_data
)
885 struct grub_iso9660_dir_ctx ctx
= { hook
, hook_data
};
886 struct grub_iso9660_data
*data
= 0;
887 struct grub_fshelp_node rootnode
;
888 struct grub_fshelp_node
*foundnode
;
890 grub_dl_ref (my_mod
);
892 data
= grub_iso9660_mount (device
->disk
);
896 rootnode
.data
= data
;
897 rootnode
.alloc_dirents
= 0;
898 rootnode
.have_dirents
= 1;
899 rootnode
.have_symlink
= 0;
900 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
902 /* Use the fshelp function to traverse the path. */
903 if (grub_fshelp_find_file (path
, &rootnode
,
905 grub_iso9660_iterate_dir
,
906 grub_iso9660_read_symlink
,
910 /* List the files in the directory. */
911 grub_iso9660_iterate_dir (foundnode
, grub_iso9660_dir_iter
, &ctx
);
913 if (foundnode
!= &rootnode
)
914 grub_free (foundnode
);
919 grub_dl_unref (my_mod
);
925 /* Open a file named NAME and initialize FILE. */
927 grub_iso9660_open (struct grub_file
*file
, const char *name
)
929 struct grub_iso9660_data
*data
;
930 struct grub_fshelp_node rootnode
;
931 struct grub_fshelp_node
*foundnode
;
933 grub_dl_ref (my_mod
);
935 data
= grub_iso9660_mount (file
->device
->disk
);
939 rootnode
.data
= data
;
940 rootnode
.alloc_dirents
= 0;
941 rootnode
.have_dirents
= 1;
942 rootnode
.have_symlink
= 0;
943 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
945 /* Use the fshelp function to traverse the path. */
946 if (grub_fshelp_find_file (name
, &rootnode
,
948 grub_iso9660_iterate_dir
,
949 grub_iso9660_read_symlink
,
953 data
->node
= foundnode
;
955 file
->size
= get_node_size (foundnode
);
961 grub_dl_unref (my_mod
);
970 grub_iso9660_read (grub_file_t file
, char *buf
, grub_size_t len
)
972 struct grub_iso9660_data
*data
=
973 (struct grub_iso9660_data
*) file
->data
;
976 /* XXX: The file is stored in as a single extent. */
977 data
->disk
->read_hook
= file
->read_hook
;
978 data
->disk
->read_hook_data
= file
->read_hook_data
;
979 err
= read_node (data
->node
, file
->offset
, len
, buf
);
980 data
->disk
->read_hook
= NULL
;
982 if (err
|| grub_errno
)
990 grub_iso9660_close (grub_file_t file
)
992 struct grub_iso9660_data
*data
=
993 (struct grub_iso9660_data
*) file
->data
;
994 grub_free (data
->node
);
997 grub_dl_unref (my_mod
);
999 return GRUB_ERR_NONE
;
1004 grub_iso9660_label (grub_device_t device
, char **label
)
1006 struct grub_iso9660_data
*data
;
1007 data
= grub_iso9660_mount (device
->disk
);
1012 *label
= grub_iso9660_convert_string (data
->voldesc
.volname
, 16);
1014 *label
= grub_strndup ((char *) data
->voldesc
.volname
, 32);
1018 for (ptr
= *label
; *ptr
;ptr
++);
1020 while (ptr
>= *label
&& *ptr
== ' ')
1034 grub_iso9660_uuid (grub_device_t device
, char **uuid
)
1036 struct grub_iso9660_data
*data
;
1037 grub_disk_t disk
= device
->disk
;
1039 grub_dl_ref (my_mod
);
1041 data
= grub_iso9660_mount (disk
);
1044 if (! data
->voldesc
.modified
.year
[0] && ! data
->voldesc
.modified
.year
[1]
1045 && ! data
->voldesc
.modified
.year
[2] && ! data
->voldesc
.modified
.year
[3]
1046 && ! data
->voldesc
.modified
.month
[0] && ! data
->voldesc
.modified
.month
[1]
1047 && ! data
->voldesc
.modified
.day
[0] && ! data
->voldesc
.modified
.day
[1]
1048 && ! data
->voldesc
.modified
.hour
[0] && ! data
->voldesc
.modified
.hour
[1]
1049 && ! data
->voldesc
.modified
.minute
[0] && ! data
->voldesc
.modified
.minute
[1]
1050 && ! data
->voldesc
.modified
.second
[0] && ! data
->voldesc
.modified
.second
[1]
1051 && ! data
->voldesc
.modified
.hundredth
[0] && ! data
->voldesc
.modified
.hundredth
[1])
1053 grub_error (GRUB_ERR_BAD_NUMBER
, "no creation date in filesystem to generate UUID");
1058 *uuid
= grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
1059 data
->voldesc
.modified
.year
[0],
1060 data
->voldesc
.modified
.year
[1],
1061 data
->voldesc
.modified
.year
[2],
1062 data
->voldesc
.modified
.year
[3],
1063 data
->voldesc
.modified
.month
[0],
1064 data
->voldesc
.modified
.month
[1],
1065 data
->voldesc
.modified
.day
[0],
1066 data
->voldesc
.modified
.day
[1],
1067 data
->voldesc
.modified
.hour
[0],
1068 data
->voldesc
.modified
.hour
[1],
1069 data
->voldesc
.modified
.minute
[0],
1070 data
->voldesc
.modified
.minute
[1],
1071 data
->voldesc
.modified
.second
[0],
1072 data
->voldesc
.modified
.second
[1],
1073 data
->voldesc
.modified
.hundredth
[0],
1074 data
->voldesc
.modified
.hundredth
[1]);
1080 grub_dl_unref (my_mod
);
1087 /* Get writing time of filesystem. */
1089 grub_iso9660_mtime (grub_device_t device
, grub_int32_t
*timebuf
)
1091 struct grub_iso9660_data
*data
;
1092 grub_disk_t disk
= device
->disk
;
1095 grub_dl_ref (my_mod
);
1097 data
= grub_iso9660_mount (disk
);
1100 grub_dl_unref (my_mod
);
1103 err
= iso9660_to_unixtime (&data
->voldesc
.modified
, timebuf
);
1105 grub_dl_unref (my_mod
);
1115 static struct grub_fs grub_iso9660_fs
=
1118 .fs_dir
= grub_iso9660_dir
,
1119 .fs_open
= grub_iso9660_open
,
1120 .fs_read
= grub_iso9660_read
,
1121 .fs_close
= grub_iso9660_close
,
1122 .fs_label
= grub_iso9660_label
,
1123 .fs_uuid
= grub_iso9660_uuid
,
1124 .fs_mtime
= grub_iso9660_mtime
,
1126 .reserved_first_sector
= 1,
1127 .blocklist_install
= 1,
1132 GRUB_MOD_INIT(iso9660
)
1134 grub_fs_register (&grub_iso9660_fs
);
1138 GRUB_MOD_FINI(iso9660
)
1140 grub_fs_unregister (&grub_iso9660_fs
);