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>
31 #include <grub/ventoy.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 static int g_ventoy_no_joliet
= 0;
36 static int g_ventoy_cur_joliet
= 0;
37 static grub_uint64_t g_ventoy_last_read_pos
= 0;
38 static grub_uint64_t g_ventoy_last_read_offset
= 0;
39 static grub_uint64_t g_ventoy_last_read_dirent_pos
= 0;
40 static grub_uint64_t g_ventoy_last_read_dirent_offset
= 0;
41 static grub_uint64_t g_ventoy_last_file_dirent_pos
= 0;
42 static grub_uint64_t g_ventoy_last_file_dirent_offset
= 0;
44 #define GRUB_ISO9660_FSTYPE_DIR 0040000
45 #define GRUB_ISO9660_FSTYPE_REG 0100000
46 #define GRUB_ISO9660_FSTYPE_SYMLINK 0120000
47 #define GRUB_ISO9660_FSTYPE_MASK 0170000
49 #define GRUB_ISO9660_LOG2_BLKSZ 2
50 #define GRUB_ISO9660_BLKSZ 2048
52 #define GRUB_ISO9660_RR_DOT 2
53 #define GRUB_ISO9660_RR_DOTDOT 4
55 #define GRUB_ISO9660_VOLDESC_BOOT 0
56 #define GRUB_ISO9660_VOLDESC_PRIMARY 1
57 #define GRUB_ISO9660_VOLDESC_SUPP 2
58 #define GRUB_ISO9660_VOLDESC_PART 3
59 #define GRUB_ISO9660_VOLDESC_END 255
61 /* The head of a volume descriptor. */
62 struct grub_iso9660_voldesc
65 grub_uint8_t magic
[5];
69 struct grub_iso9660_date2
80 /* A directory entry. */
81 struct grub_iso9660_dir
84 grub_uint8_t ext_sectors
;
85 grub_uint32_t first_sector
;
86 grub_uint32_t first_sector_be
;
88 grub_uint32_t size_be
;
89 struct grub_iso9660_date2 mtime
;
91 grub_uint8_t unused2
[6];
92 #define MAX_NAMELEN 255
96 struct grub_iso9660_date
99 grub_uint8_t month
[2];
101 grub_uint8_t hour
[2];
102 grub_uint8_t minute
[2];
103 grub_uint8_t second
[2];
104 grub_uint8_t hundredth
[2];
108 /* The primary volume descriptor. Only little endian is used. */
109 struct grub_iso9660_primary_voldesc
111 struct grub_iso9660_voldesc voldesc
;
112 grub_uint8_t unused1
[33];
113 grub_uint8_t volname
[32];
114 grub_uint8_t unused2
[16];
115 grub_uint8_t escape
[32];
116 grub_uint8_t unused3
[12];
117 grub_uint32_t path_table_size
;
118 grub_uint8_t unused4
[4];
119 grub_uint32_t path_table
;
120 grub_uint8_t unused5
[12];
121 struct grub_iso9660_dir rootdir
;
122 grub_uint8_t unused6
[624];
123 struct grub_iso9660_date created
;
124 struct grub_iso9660_date modified
;
127 /* A single entry in the path table. */
128 struct grub_iso9660_path
131 grub_uint8_t sectors
;
132 grub_uint32_t first_sector
;
133 grub_uint16_t parentdir
;
134 grub_uint8_t name
[0];
137 /* An entry in the System Usage area of the directory entry. */
138 struct grub_iso9660_susp_entry
142 grub_uint8_t version
;
143 grub_uint8_t data
[0];
146 /* The CE entry. This is used to describe the next block where data
148 struct grub_iso9660_susp_ce
150 struct grub_iso9660_susp_entry entry
;
152 grub_uint32_t blk_be
;
154 grub_uint32_t off_be
;
156 grub_uint32_t len_be
;
159 struct grub_iso9660_data
161 struct grub_iso9660_primary_voldesc voldesc
;
166 struct grub_fshelp_node
*node
;
169 struct grub_fshelp_node
171 struct grub_iso9660_data
*data
;
172 grub_size_t have_dirents
, alloc_dirents
;
174 struct grub_iso9660_dir dirents
[8];
183 FLAG_MORE_EXTENTS
= 0x80
186 static grub_dl_t my_mod
;
190 iso9660_to_unixtime (const struct grub_iso9660_date
*i
, grub_int32_t
*nix
)
192 struct grub_datetime datetime
;
194 if (! i
->year
[0] && ! i
->year
[1]
195 && ! i
->year
[2] && ! i
->year
[3]
196 && ! i
->month
[0] && ! i
->month
[1]
197 && ! i
->day
[0] && ! i
->day
[1]
198 && ! i
->hour
[0] && ! i
->hour
[1]
199 && ! i
->minute
[0] && ! i
->minute
[1]
200 && ! i
->second
[0] && ! i
->second
[1]
201 && ! i
->hundredth
[0] && ! i
->hundredth
[1])
202 return grub_error (GRUB_ERR_BAD_NUMBER
, "empty date");
203 datetime
.year
= (i
->year
[0] - '0') * 1000 + (i
->year
[1] - '0') * 100
204 + (i
->year
[2] - '0') * 10 + (i
->year
[3] - '0');
205 datetime
.month
= (i
->month
[0] - '0') * 10 + (i
->month
[1] - '0');
206 datetime
.day
= (i
->day
[0] - '0') * 10 + (i
->day
[1] - '0');
207 datetime
.hour
= (i
->hour
[0] - '0') * 10 + (i
->hour
[1] - '0');
208 datetime
.minute
= (i
->minute
[0] - '0') * 10 + (i
->minute
[1] - '0');
209 datetime
.second
= (i
->second
[0] - '0') * 10 + (i
->second
[1] - '0');
211 if (!grub_datetime2unixtime (&datetime
, nix
))
212 return grub_error (GRUB_ERR_BAD_NUMBER
, "incorrect date");
213 *nix
-= i
->offset
* 60 * 15;
214 return GRUB_ERR_NONE
;
218 iso9660_to_unixtime2 (const struct grub_iso9660_date2
*i
, grub_int32_t
*nix
)
220 struct grub_datetime datetime
;
222 datetime
.year
= i
->year
+ 1900;
223 datetime
.month
= i
->month
;
224 datetime
.day
= i
->day
;
225 datetime
.hour
= i
->hour
;
226 datetime
.minute
= i
->minute
;
227 datetime
.second
= i
->second
;
229 if (!grub_datetime2unixtime (&datetime
, nix
))
231 *nix
-= i
->offset
* 60 * 15;
236 read_node (grub_fshelp_node_t node
, grub_off_t off
, grub_size_t len
, char *buf
)
244 while (i
< node
->have_dirents
245 && off
>= grub_le_to_cpu32 (node
->dirents
[i
].size
))
247 off
-= grub_le_to_cpu32 (node
->dirents
[i
].size
);
250 if (i
== node
->have_dirents
)
251 return grub_error (GRUB_ERR_OUT_OF_RANGE
, "read out of range");
252 toread
= grub_le_to_cpu32 (node
->dirents
[i
].size
);
255 g_ventoy_last_read_pos
= ((grub_disk_addr_t
) grub_le_to_cpu32 (node
->dirents
[i
].first_sector
)) << GRUB_ISO9660_LOG2_BLKSZ
;
256 g_ventoy_last_read_offset
= off
;
257 err
= grub_disk_read (node
->data
->disk
, g_ventoy_last_read_pos
, off
, toread
, buf
);
264 return GRUB_ERR_NONE
;
267 /* Iterate over the susp entries, starting with block SUA_BLOCK on the
268 offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for
271 grub_iso9660_susp_iterate (grub_fshelp_node_t node
, grub_off_t off
,
272 grub_ssize_t sua_size
,
274 (struct grub_iso9660_susp_entry
*entry
, void *hook_arg
),
278 struct grub_iso9660_susp_entry
*entry
;
282 return GRUB_ERR_NONE
;
284 sua
= grub_malloc (sua_size
);
288 /* Load a part of the System Usage Area. */
289 err
= read_node (node
, off
, sua_size
, sua
);
293 for (entry
= (struct grub_iso9660_susp_entry
*) sua
; (char *) entry
< (char *) sua
+ sua_size
- 1 && entry
->len
> 0;
294 entry
= (struct grub_iso9660_susp_entry
*)
295 ((char *) entry
+ entry
->len
))
297 /* The last entry. */
298 if (grub_strncmp ((char *) entry
->sig
, "ST", 2) == 0)
301 /* Additional entries are stored elsewhere. */
302 if (grub_strncmp ((char *) entry
->sig
, "CE", 2) == 0)
304 struct grub_iso9660_susp_ce
*ce
;
305 grub_disk_addr_t ce_block
;
307 ce
= (struct grub_iso9660_susp_ce
*) entry
;
308 sua_size
= grub_le_to_cpu32 (ce
->len
);
309 off
= grub_le_to_cpu32 (ce
->off
);
310 ce_block
= grub_le_to_cpu32 (ce
->blk
) << GRUB_ISO9660_LOG2_BLKSZ
;
313 sua
= grub_malloc (sua_size
);
317 /* Load a part of the System Usage Area. */
318 err
= grub_disk_read (node
->data
->disk
, ce_block
, off
,
323 entry
= (struct grub_iso9660_susp_entry
*) sua
;
326 if (hook (entry
, hook_arg
))
338 grub_iso9660_convert_string (grub_uint8_t
*us
, int len
)
342 grub_uint16_t t
[MAX_NAMELEN
/ 2 + 1];
344 p
= grub_malloc (len
* GRUB_MAX_UTF8_PER_UTF16
+ 1);
348 for (i
=0; i
<len
; i
++)
349 t
[i
] = grub_be_to_cpu16 (grub_get_unaligned16 (us
+ 2 * i
));
351 *grub_utf16_to_utf8 ((grub_uint8_t
*) p
, t
, len
) = '\0';
357 susp_iterate_set_rockridge (struct grub_iso9660_susp_entry
*susp_entry
,
360 struct grub_iso9660_data
*data
= _data
;
361 /* The "ER" entry is used to detect extensions. The
362 `IEEE_P1285' extension means Rock ridge. */
363 if (grub_strncmp ((char *) susp_entry
->sig
, "ER", 2) == 0)
372 set_rockridge (struct grub_iso9660_data
*data
)
377 struct grub_iso9660_dir rootdir
;
378 struct grub_iso9660_susp_entry
*entry
;
382 /* Read the system use area and test it to see if SUSP is
384 if (grub_disk_read (data
->disk
,
385 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
386 << GRUB_ISO9660_LOG2_BLKSZ
), 0,
387 sizeof (rootdir
), (char *) &rootdir
))
388 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
390 sua_pos
= (sizeof (rootdir
) + rootdir
.namelen
391 + (rootdir
.namelen
% 2) - 1);
392 sua_size
= rootdir
.len
- sua_pos
;
395 return GRUB_ERR_NONE
;
397 sua
= grub_malloc (sua_size
);
401 if (grub_disk_read (data
->disk
,
402 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
403 << GRUB_ISO9660_LOG2_BLKSZ
), sua_pos
,
407 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
410 entry
= (struct grub_iso9660_susp_entry
*) sua
;
412 /* Test if the SUSP protocol is used on this filesystem. */
413 if (grub_strncmp ((char *) entry
->sig
, "SP", 2) == 0)
415 struct grub_fshelp_node rootnode
;
417 rootnode
.data
= data
;
418 rootnode
.alloc_dirents
= ARRAY_SIZE (rootnode
.dirents
);
419 rootnode
.have_dirents
= 1;
420 rootnode
.have_symlink
= 0;
421 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
423 /* The 2nd data byte stored how many bytes are skipped every time
424 to get to the SUA (System Usage Area). */
425 data
->susp_skip
= entry
->data
[2];
426 entry
= (struct grub_iso9660_susp_entry
*) ((char *) entry
+ entry
->len
);
428 /* Iterate over the entries in the SUA area to detect
430 if (grub_iso9660_susp_iterate (&rootnode
,
431 sua_pos
, sua_size
, susp_iterate_set_rockridge
,
439 return GRUB_ERR_NONE
;
442 static struct grub_iso9660_data
*
443 grub_iso9660_mount (grub_disk_t disk
)
445 struct grub_iso9660_data
*data
= 0;
446 struct grub_iso9660_primary_voldesc voldesc
;
449 data
= grub_zalloc (sizeof (struct grub_iso9660_data
));
455 g_ventoy_cur_joliet
= 0;
459 int copy_voldesc
= 0;
461 /* Read the superblock. */
462 if (grub_disk_read (disk
, block
<< GRUB_ISO9660_LOG2_BLKSZ
, 0,
463 sizeof (struct grub_iso9660_primary_voldesc
),
466 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
470 if (grub_strncmp ((char *) voldesc
.voldesc
.magic
, "CD001", 5) != 0)
472 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
476 if (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_PRIMARY
)
478 else if (!data
->rockridge
479 && (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_SUPP
)
480 && (voldesc
.escape
[0] == 0x25) && (voldesc
.escape
[1] == 0x2f)
482 ((voldesc
.escape
[2] == 0x40) || /* UCS-2 Level 1. */
483 (voldesc
.escape
[2] == 0x43) || /* UCS-2 Level 2. */
484 (voldesc
.escape
[2] == 0x45))) /* UCS-2 Level 3. */
486 if (0 == g_ventoy_no_joliet
) {
489 g_ventoy_cur_joliet
= 1;
495 grub_memcpy((char *) &data
->voldesc
, (char *) &voldesc
,
496 sizeof (struct grub_iso9660_primary_voldesc
));
497 if (set_rockridge (data
))
502 } while (voldesc
.voldesc
.type
!= GRUB_ISO9660_VOLDESC_END
);
513 grub_iso9660_read_symlink (grub_fshelp_node_t node
)
515 return node
->have_symlink
516 ? grub_strdup (node
->symlink
517 + (node
->have_dirents
) * sizeof (node
->dirents
[0])
518 - sizeof (node
->dirents
)) : grub_strdup ("");
522 get_node_size (grub_fshelp_node_t node
)
527 for (i
= 0; i
< node
->have_dirents
; i
++)
528 ret
+= grub_le_to_cpu32 (node
->dirents
[i
].size
);
532 struct iterate_dir_ctx
536 enum grub_fshelp_filetype type
;
541 /* Extend the symlink. */
543 add_part (struct iterate_dir_ctx
*ctx
,
547 int size
= ctx
->symlink
? grub_strlen (ctx
->symlink
) : 0;
549 ctx
->symlink
= grub_realloc (ctx
->symlink
, size
+ len2
+ 1);
553 grub_memcpy (ctx
->symlink
+ size
, part
, len2
);
554 ctx
->symlink
[size
+ len2
] = 0;
558 susp_iterate_dir (struct grub_iso9660_susp_entry
*entry
,
561 struct iterate_dir_ctx
*ctx
= _ctx
;
563 /* The filename in the rock ridge entry. */
564 if (grub_strncmp ("NM", (char *) entry
->sig
, 2) == 0)
566 /* The flags are stored at the data position 0, here the
567 filename type is stored. */
568 /* FIXME: Fix this slightly improper cast. */
569 if (entry
->data
[0] & GRUB_ISO9660_RR_DOT
)
570 ctx
->filename
= (char *) ".";
571 else if (entry
->data
[0] & GRUB_ISO9660_RR_DOTDOT
)
572 ctx
->filename
= (char *) "..";
573 else if (entry
->len
>= 5)
575 grub_size_t off
= 0, csize
= 1;
577 csize
= entry
->len
- 5;
579 if (ctx
->filename_alloc
)
581 off
= grub_strlen (ctx
->filename
);
582 ctx
->filename
= grub_realloc (ctx
->filename
, csize
+ off
+ 1);
587 ctx
->filename
= grub_zalloc (csize
+ 1);
594 ctx
->filename_alloc
= 1;
595 grub_memcpy (ctx
->filename
+ off
, (char *) &entry
->data
[1], csize
);
596 ctx
->filename
[off
+ csize
] = '\0';
599 /* The mode information (st_mode). */
600 else if (grub_strncmp ((char *) entry
->sig
, "PX", 2) == 0)
602 /* At position 0 of the PX record the st_mode information is
603 stored (little-endian). */
604 grub_uint32_t mode
= ((entry
->data
[0] + (entry
->data
[1] << 8))
605 & GRUB_ISO9660_FSTYPE_MASK
);
609 case GRUB_ISO9660_FSTYPE_DIR
:
610 ctx
->type
= GRUB_FSHELP_DIR
;
612 case GRUB_ISO9660_FSTYPE_REG
:
613 ctx
->type
= GRUB_FSHELP_REG
;
615 case GRUB_ISO9660_FSTYPE_SYMLINK
:
616 ctx
->type
= GRUB_FSHELP_SYMLINK
;
619 ctx
->type
= GRUB_FSHELP_UNKNOWN
;
622 else if (grub_strncmp ("SL", (char *) entry
->sig
, 2) == 0)
624 unsigned int pos
= 1;
626 /* The symlink is not stored as a POSIX symlink, translate it. */
627 while (pos
+ sizeof (*entry
) < entry
->len
)
629 /* The current position is the `Component Flag'. */
630 switch (entry
->data
[pos
] & 30)
634 /* The data on pos + 2 is the actual data, pos + 1
635 is the length. Both are part of the `Component
637 if (ctx
->symlink
&& !ctx
->was_continue
)
638 add_part (ctx
, "/", 1);
639 add_part (ctx
, (char *) &entry
->data
[pos
+ 2],
640 entry
->data
[pos
+ 1]);
641 ctx
->was_continue
= (entry
->data
[pos
] & 1);
646 add_part (ctx
, "./", 2);
650 add_part (ctx
, "../", 3);
654 add_part (ctx
, "/", 1);
657 /* In pos + 1 the length of the `Component Record' is
659 pos
+= entry
->data
[pos
+ 1] + 2;
662 /* Check if `grub_realloc' failed. */
671 grub_iso9660_iterate_dir (grub_fshelp_node_t dir
,
672 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
674 struct grub_iso9660_dir dirent
;
675 grub_off_t offset
= 0;
677 struct iterate_dir_ctx ctx
;
679 len
= get_node_size (dir
);
681 for (; offset
< len
; offset
+= dirent
.len
)
684 ctx
.was_continue
= 0;
686 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
689 if ((dirent
.flags
& FLAG_TYPE
) != FLAG_TYPE_DIR
) {
690 g_ventoy_last_read_dirent_pos
= g_ventoy_last_read_pos
;
691 g_ventoy_last_read_dirent_offset
= g_ventoy_last_read_offset
;
694 /* The end of the block, skip to the next one. */
697 offset
= (offset
/ GRUB_ISO9660_BLKSZ
+ 1) * GRUB_ISO9660_BLKSZ
;
702 char name
[MAX_NAMELEN
+ 1];
703 int nameoffset
= offset
+ sizeof (dirent
);
704 struct grub_fshelp_node
*node
;
705 int sua_off
= (sizeof (dirent
) + dirent
.namelen
+ 1
706 - (dirent
.namelen
% 2));
707 int sua_size
= dirent
.len
- sua_off
;
709 sua_off
+= offset
+ dir
->data
->susp_skip
;
712 ctx
.filename_alloc
= 0;
713 ctx
.type
= GRUB_FSHELP_UNKNOWN
;
715 if (dir
->data
->rockridge
716 && grub_iso9660_susp_iterate (dir
, sua_off
, sua_size
,
717 susp_iterate_dir
, &ctx
))
721 if (read_node (dir
, nameoffset
, dirent
.namelen
, (char *) name
))
724 node
= grub_malloc (sizeof (struct grub_fshelp_node
));
728 node
->alloc_dirents
= ARRAY_SIZE (node
->dirents
);
729 node
->have_dirents
= 1;
731 /* Setup a new node. */
732 node
->data
= dir
->data
;
733 node
->have_symlink
= 0;
735 /* If the filetype was not stored using rockridge, use
736 whatever is stored in the iso9660 filesystem. */
737 if (ctx
.type
== GRUB_FSHELP_UNKNOWN
)
739 if ((dirent
.flags
& FLAG_TYPE
) == FLAG_TYPE_DIR
)
740 ctx
.type
= GRUB_FSHELP_DIR
;
741 else if ((dirent
.flags
& FLAG_TYPE
) == 3)
742 ctx
.type
= GRUB_FSHELP_DIR
;
744 ctx
.type
= GRUB_FSHELP_REG
;
748 if (!ctx
.filename
&& dirent
.namelen
== 1 && name
[0] == 0)
749 ctx
.filename
= (char *) ".";
751 if (!ctx
.filename
&& dirent
.namelen
== 1 && name
[0] == 1)
752 ctx
.filename
= (char *) "..";
754 /* The filename was not stored in a rock ridge entry. Read it
755 from the iso9660 filesystem. */
756 if (!dir
->data
->joliet
&& !ctx
.filename
)
759 name
[dirent
.namelen
] = '\0';
760 ctx
.filename
= grub_strrchr (name
, ';');
762 *ctx
.filename
= '\0';
763 /* ISO9660 names are not case-preserving. */
764 ctx
.type
|= GRUB_FSHELP_CASE_INSENSITIVE
;
765 for (ptr
= name
; *ptr
; ptr
++)
766 *ptr
= grub_tolower (*ptr
);
767 if (ptr
!= name
&& *(ptr
- 1) == '.')
772 if (dir
->data
->joliet
&& !ctx
.filename
)
776 ctx
.filename
= grub_iso9660_convert_string
777 ((grub_uint8_t
*) name
, dirent
.namelen
>> 1);
779 semicolon
= grub_strrchr (ctx
.filename
, ';');
783 ctx
.filename_alloc
= 1;
786 node
->dirents
[0] = dirent
;
787 while (dirent
.flags
& FLAG_MORE_EXTENTS
)
789 offset
+= dirent
.len
;
790 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
792 if (ctx
.filename_alloc
)
793 grub_free (ctx
.filename
);
797 if (node
->have_dirents
>= node
->alloc_dirents
)
799 struct grub_fshelp_node
*new_node
;
800 node
->alloc_dirents
*= 2;
801 new_node
= grub_realloc (node
,
802 sizeof (struct grub_fshelp_node
)
803 + ((node
->alloc_dirents
804 - ARRAY_SIZE (node
->dirents
))
805 * sizeof (node
->dirents
[0])));
808 if (ctx
.filename_alloc
)
809 grub_free (ctx
.filename
);
815 node
->dirents
[node
->have_dirents
++] = dirent
;
819 if ((node
->alloc_dirents
- node
->have_dirents
)
820 * sizeof (node
->dirents
[0]) < grub_strlen (ctx
.symlink
) + 1)
822 struct grub_fshelp_node
*new_node
;
823 new_node
= grub_realloc (node
,
824 sizeof (struct grub_fshelp_node
)
825 + ((node
->alloc_dirents
826 - ARRAY_SIZE (node
->dirents
))
827 * sizeof (node
->dirents
[0]))
828 + grub_strlen (ctx
.symlink
) + 1);
831 if (ctx
.filename_alloc
)
832 grub_free (ctx
.filename
);
838 node
->have_symlink
= 1;
839 grub_strcpy (node
->symlink
840 + node
->have_dirents
* sizeof (node
->dirents
[0])
841 - sizeof (node
->dirents
), ctx
.symlink
);
842 grub_free (ctx
.symlink
);
844 ctx
.was_continue
= 0;
846 if (hook (ctx
.filename
, ctx
.type
, node
, hook_data
))
848 g_ventoy_last_file_dirent_pos
= g_ventoy_last_read_dirent_pos
;
849 g_ventoy_last_file_dirent_offset
= g_ventoy_last_read_dirent_offset
;
850 if (ctx
.filename_alloc
)
851 grub_free (ctx
.filename
);
854 if (ctx
.filename_alloc
)
855 grub_free (ctx
.filename
);
864 /* Context for grub_iso9660_dir. */
865 struct grub_iso9660_dir_ctx
867 grub_fs_dir_hook_t hook
;
871 /* Helper for grub_iso9660_dir. */
873 grub_iso9660_dir_iter (const char *filename
,
874 enum grub_fshelp_filetype filetype
,
875 grub_fshelp_node_t node
, void *data
)
877 struct grub_iso9660_dir_ctx
*ctx
= data
;
878 struct grub_dirhook_info info
;
880 grub_memset (&info
, 0, sizeof (info
));
881 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
882 info
.mtimeset
= !!iso9660_to_unixtime2 (&node
->dirents
[0].mtime
, &info
.mtime
);
885 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
889 grub_iso9660_dir (grub_device_t device
, const char *path
,
890 grub_fs_dir_hook_t hook
, void *hook_data
)
892 struct grub_iso9660_dir_ctx ctx
= { hook
, hook_data
};
893 struct grub_iso9660_data
*data
= 0;
894 struct grub_fshelp_node rootnode
;
895 struct grub_fshelp_node
*foundnode
;
897 grub_dl_ref (my_mod
);
899 data
= grub_iso9660_mount (device
->disk
);
903 rootnode
.data
= data
;
904 rootnode
.alloc_dirents
= 0;
905 rootnode
.have_dirents
= 1;
906 rootnode
.have_symlink
= 0;
907 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
909 /* Use the fshelp function to traverse the path. */
910 if (grub_fshelp_find_file (path
, &rootnode
,
912 grub_iso9660_iterate_dir
,
913 grub_iso9660_read_symlink
,
917 /* List the files in the directory. */
918 grub_iso9660_iterate_dir (foundnode
, grub_iso9660_dir_iter
, &ctx
);
920 if (foundnode
!= &rootnode
)
921 grub_free (foundnode
);
926 grub_dl_unref (my_mod
);
932 /* Open a file named NAME and initialize FILE. */
934 grub_iso9660_open (struct grub_file
*file
, const char *name
)
936 struct grub_iso9660_data
*data
;
937 struct grub_fshelp_node rootnode
;
938 struct grub_fshelp_node
*foundnode
;
940 grub_dl_ref (my_mod
);
942 data
= grub_iso9660_mount (file
->device
->disk
);
946 rootnode
.data
= data
;
947 rootnode
.alloc_dirents
= 0;
948 rootnode
.have_dirents
= 1;
949 rootnode
.have_symlink
= 0;
950 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
952 /* Use the fshelp function to traverse the path. */
953 if (grub_fshelp_find_file (name
, &rootnode
,
955 grub_iso9660_iterate_dir
,
956 grub_iso9660_read_symlink
,
960 data
->node
= foundnode
;
962 file
->size
= get_node_size (foundnode
);
968 grub_dl_unref (my_mod
);
977 grub_iso9660_read (grub_file_t file
, char *buf
, grub_size_t len
)
979 struct grub_iso9660_data
*data
=
980 (struct grub_iso9660_data
*) file
->data
;
983 /* XXX: The file is stored in as a single extent. */
984 data
->disk
->read_hook
= file
->read_hook
;
985 data
->disk
->read_hook_data
= file
->read_hook_data
;
986 err
= read_node (data
->node
, file
->offset
, len
, buf
);
987 data
->disk
->read_hook
= NULL
;
989 if (err
|| grub_errno
)
997 grub_iso9660_close (grub_file_t file
)
999 struct grub_iso9660_data
*data
=
1000 (struct grub_iso9660_data
*) file
->data
;
1001 grub_free (data
->node
);
1004 grub_dl_unref (my_mod
);
1006 return GRUB_ERR_NONE
;
1011 grub_iso9660_label (grub_device_t device
, char **label
)
1013 struct grub_iso9660_data
*data
;
1014 data
= grub_iso9660_mount (device
->disk
);
1019 *label
= grub_iso9660_convert_string (data
->voldesc
.volname
, 16);
1021 *label
= grub_strndup ((char *) data
->voldesc
.volname
, 32);
1025 for (ptr
= *label
; *ptr
;ptr
++);
1027 while (ptr
>= *label
&& *ptr
== ' ')
1041 grub_iso9660_uuid (grub_device_t device
, char **uuid
)
1043 struct grub_iso9660_data
*data
;
1044 grub_disk_t disk
= device
->disk
;
1046 grub_dl_ref (my_mod
);
1048 data
= grub_iso9660_mount (disk
);
1051 if (! data
->voldesc
.modified
.year
[0] && ! data
->voldesc
.modified
.year
[1]
1052 && ! data
->voldesc
.modified
.year
[2] && ! data
->voldesc
.modified
.year
[3]
1053 && ! data
->voldesc
.modified
.month
[0] && ! data
->voldesc
.modified
.month
[1]
1054 && ! data
->voldesc
.modified
.day
[0] && ! data
->voldesc
.modified
.day
[1]
1055 && ! data
->voldesc
.modified
.hour
[0] && ! data
->voldesc
.modified
.hour
[1]
1056 && ! data
->voldesc
.modified
.minute
[0] && ! data
->voldesc
.modified
.minute
[1]
1057 && ! data
->voldesc
.modified
.second
[0] && ! data
->voldesc
.modified
.second
[1]
1058 && ! data
->voldesc
.modified
.hundredth
[0] && ! data
->voldesc
.modified
.hundredth
[1])
1060 grub_error (GRUB_ERR_BAD_NUMBER
, "no creation date in filesystem to generate UUID");
1065 *uuid
= grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
1066 data
->voldesc
.modified
.year
[0],
1067 data
->voldesc
.modified
.year
[1],
1068 data
->voldesc
.modified
.year
[2],
1069 data
->voldesc
.modified
.year
[3],
1070 data
->voldesc
.modified
.month
[0],
1071 data
->voldesc
.modified
.month
[1],
1072 data
->voldesc
.modified
.day
[0],
1073 data
->voldesc
.modified
.day
[1],
1074 data
->voldesc
.modified
.hour
[0],
1075 data
->voldesc
.modified
.hour
[1],
1076 data
->voldesc
.modified
.minute
[0],
1077 data
->voldesc
.modified
.minute
[1],
1078 data
->voldesc
.modified
.second
[0],
1079 data
->voldesc
.modified
.second
[1],
1080 data
->voldesc
.modified
.hundredth
[0],
1081 data
->voldesc
.modified
.hundredth
[1]);
1087 grub_dl_unref (my_mod
);
1094 /* Get writing time of filesystem. */
1096 grub_iso9660_mtime (grub_device_t device
, grub_int32_t
*timebuf
)
1098 struct grub_iso9660_data
*data
;
1099 grub_disk_t disk
= device
->disk
;
1102 grub_dl_ref (my_mod
);
1104 data
= grub_iso9660_mount (disk
);
1107 grub_dl_unref (my_mod
);
1110 err
= iso9660_to_unixtime (&data
->voldesc
.modified
, timebuf
);
1112 grub_dl_unref (my_mod
);
1119 void grub_iso9660_set_nojoliet(int nojoliet
)
1121 g_ventoy_no_joliet
= nojoliet
;
1124 int grub_iso9660_is_joliet(void)
1126 return g_ventoy_cur_joliet
;
1129 grub_uint64_t
grub_iso9660_get_last_read_pos(grub_file_t file
)
1132 return (g_ventoy_last_read_pos
<< GRUB_DISK_SECTOR_BITS
);
1135 grub_uint64_t
grub_iso9660_get_last_file_dirent_pos(grub_file_t file
)
1138 return (g_ventoy_last_file_dirent_pos
<< GRUB_DISK_SECTOR_BITS
) + g_ventoy_last_file_dirent_offset
;
1141 static struct grub_fs grub_iso9660_fs
=
1144 .fs_dir
= grub_iso9660_dir
,
1145 .fs_open
= grub_iso9660_open
,
1146 .fs_read
= grub_iso9660_read
,
1147 .fs_close
= grub_iso9660_close
,
1148 .fs_label
= grub_iso9660_label
,
1149 .fs_uuid
= grub_iso9660_uuid
,
1150 .fs_mtime
= grub_iso9660_mtime
,
1152 .reserved_first_sector
= 1,
1153 .blocklist_install
= 1,
1158 GRUB_MOD_INIT(iso9660
)
1160 grub_fs_register (&grub_iso9660_fs
);
1164 GRUB_MOD_FINI(iso9660
)
1166 grub_fs_unregister (&grub_iso9660_fs
);