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 grub_uint64_t g_ventoy_last_read_pos
= 0;
37 static grub_uint64_t g_ventoy_last_read_offset
= 0;
38 static grub_uint64_t g_ventoy_last_read_dirent_pos
= 0;
39 static grub_uint64_t g_ventoy_last_read_dirent_offset
= 0;
40 static grub_uint64_t g_ventoy_last_file_dirent_pos
= 0;
41 static grub_uint64_t g_ventoy_last_file_dirent_offset
= 0;
43 #define GRUB_ISO9660_FSTYPE_DIR 0040000
44 #define GRUB_ISO9660_FSTYPE_REG 0100000
45 #define GRUB_ISO9660_FSTYPE_SYMLINK 0120000
46 #define GRUB_ISO9660_FSTYPE_MASK 0170000
48 #define GRUB_ISO9660_LOG2_BLKSZ 2
49 #define GRUB_ISO9660_BLKSZ 2048
51 #define GRUB_ISO9660_RR_DOT 2
52 #define GRUB_ISO9660_RR_DOTDOT 4
54 #define GRUB_ISO9660_VOLDESC_BOOT 0
55 #define GRUB_ISO9660_VOLDESC_PRIMARY 1
56 #define GRUB_ISO9660_VOLDESC_SUPP 2
57 #define GRUB_ISO9660_VOLDESC_PART 3
58 #define GRUB_ISO9660_VOLDESC_END 255
60 /* The head of a volume descriptor. */
61 struct grub_iso9660_voldesc
64 grub_uint8_t magic
[5];
68 struct grub_iso9660_date2
79 /* A directory entry. */
80 struct grub_iso9660_dir
83 grub_uint8_t ext_sectors
;
84 grub_uint32_t first_sector
;
85 grub_uint32_t first_sector_be
;
87 grub_uint32_t size_be
;
88 struct grub_iso9660_date2 mtime
;
90 grub_uint8_t unused2
[6];
91 #define MAX_NAMELEN 255
95 struct grub_iso9660_date
98 grub_uint8_t month
[2];
100 grub_uint8_t hour
[2];
101 grub_uint8_t minute
[2];
102 grub_uint8_t second
[2];
103 grub_uint8_t hundredth
[2];
107 /* The primary volume descriptor. Only little endian is used. */
108 struct grub_iso9660_primary_voldesc
110 struct grub_iso9660_voldesc voldesc
;
111 grub_uint8_t unused1
[33];
112 grub_uint8_t volname
[32];
113 grub_uint8_t unused2
[16];
114 grub_uint8_t escape
[32];
115 grub_uint8_t unused3
[12];
116 grub_uint32_t path_table_size
;
117 grub_uint8_t unused4
[4];
118 grub_uint32_t path_table
;
119 grub_uint8_t unused5
[12];
120 struct grub_iso9660_dir rootdir
;
121 grub_uint8_t unused6
[624];
122 struct grub_iso9660_date created
;
123 struct grub_iso9660_date modified
;
126 /* A single entry in the path table. */
127 struct grub_iso9660_path
130 grub_uint8_t sectors
;
131 grub_uint32_t first_sector
;
132 grub_uint16_t parentdir
;
133 grub_uint8_t name
[0];
136 /* An entry in the System Usage area of the directory entry. */
137 struct grub_iso9660_susp_entry
141 grub_uint8_t version
;
142 grub_uint8_t data
[0];
145 /* The CE entry. This is used to describe the next block where data
147 struct grub_iso9660_susp_ce
149 struct grub_iso9660_susp_entry entry
;
151 grub_uint32_t blk_be
;
153 grub_uint32_t off_be
;
155 grub_uint32_t len_be
;
158 struct grub_iso9660_data
160 struct grub_iso9660_primary_voldesc voldesc
;
165 struct grub_fshelp_node
*node
;
168 struct grub_fshelp_node
170 struct grub_iso9660_data
*data
;
171 grub_size_t have_dirents
, alloc_dirents
;
173 struct grub_iso9660_dir dirents
[8];
182 FLAG_MORE_EXTENTS
= 0x80
185 static grub_dl_t my_mod
;
189 iso9660_to_unixtime (const struct grub_iso9660_date
*i
, grub_int32_t
*nix
)
191 struct grub_datetime datetime
;
193 if (! i
->year
[0] && ! i
->year
[1]
194 && ! i
->year
[2] && ! i
->year
[3]
195 && ! i
->month
[0] && ! i
->month
[1]
196 && ! i
->day
[0] && ! i
->day
[1]
197 && ! i
->hour
[0] && ! i
->hour
[1]
198 && ! i
->minute
[0] && ! i
->minute
[1]
199 && ! i
->second
[0] && ! i
->second
[1]
200 && ! i
->hundredth
[0] && ! i
->hundredth
[1])
201 return grub_error (GRUB_ERR_BAD_NUMBER
, "empty date");
202 datetime
.year
= (i
->year
[0] - '0') * 1000 + (i
->year
[1] - '0') * 100
203 + (i
->year
[2] - '0') * 10 + (i
->year
[3] - '0');
204 datetime
.month
= (i
->month
[0] - '0') * 10 + (i
->month
[1] - '0');
205 datetime
.day
= (i
->day
[0] - '0') * 10 + (i
->day
[1] - '0');
206 datetime
.hour
= (i
->hour
[0] - '0') * 10 + (i
->hour
[1] - '0');
207 datetime
.minute
= (i
->minute
[0] - '0') * 10 + (i
->minute
[1] - '0');
208 datetime
.second
= (i
->second
[0] - '0') * 10 + (i
->second
[1] - '0');
210 if (!grub_datetime2unixtime (&datetime
, nix
))
211 return grub_error (GRUB_ERR_BAD_NUMBER
, "incorrect date");
212 *nix
-= i
->offset
* 60 * 15;
213 return GRUB_ERR_NONE
;
217 iso9660_to_unixtime2 (const struct grub_iso9660_date2
*i
, grub_int32_t
*nix
)
219 struct grub_datetime datetime
;
221 datetime
.year
= i
->year
+ 1900;
222 datetime
.month
= i
->month
;
223 datetime
.day
= i
->day
;
224 datetime
.hour
= i
->hour
;
225 datetime
.minute
= i
->minute
;
226 datetime
.second
= i
->second
;
228 if (!grub_datetime2unixtime (&datetime
, nix
))
230 *nix
-= i
->offset
* 60 * 15;
235 read_node (grub_fshelp_node_t node
, grub_off_t off
, grub_size_t len
, char *buf
)
243 while (i
< node
->have_dirents
244 && off
>= grub_le_to_cpu32 (node
->dirents
[i
].size
))
246 off
-= grub_le_to_cpu32 (node
->dirents
[i
].size
);
249 if (i
== node
->have_dirents
)
250 return grub_error (GRUB_ERR_OUT_OF_RANGE
, "read out of range");
251 toread
= grub_le_to_cpu32 (node
->dirents
[i
].size
);
254 g_ventoy_last_read_pos
= ((grub_disk_addr_t
) grub_le_to_cpu32 (node
->dirents
[i
].first_sector
)) << GRUB_ISO9660_LOG2_BLKSZ
;
255 g_ventoy_last_read_offset
= off
;
256 err
= grub_disk_read (node
->data
->disk
, g_ventoy_last_read_pos
, off
, toread
, buf
);
263 return GRUB_ERR_NONE
;
266 /* Iterate over the susp entries, starting with block SUA_BLOCK on the
267 offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for
270 grub_iso9660_susp_iterate (grub_fshelp_node_t node
, grub_off_t off
,
271 grub_ssize_t sua_size
,
273 (struct grub_iso9660_susp_entry
*entry
, void *hook_arg
),
277 struct grub_iso9660_susp_entry
*entry
;
281 return GRUB_ERR_NONE
;
283 sua
= grub_malloc (sua_size
);
287 /* Load a part of the System Usage Area. */
288 err
= read_node (node
, off
, sua_size
, sua
);
292 for (entry
= (struct grub_iso9660_susp_entry
*) sua
; (char *) entry
< (char *) sua
+ sua_size
- 1 && entry
->len
> 0;
293 entry
= (struct grub_iso9660_susp_entry
*)
294 ((char *) entry
+ entry
->len
))
296 /* The last entry. */
297 if (grub_strncmp ((char *) entry
->sig
, "ST", 2) == 0)
300 /* Additional entries are stored elsewhere. */
301 if (grub_strncmp ((char *) entry
->sig
, "CE", 2) == 0)
303 struct grub_iso9660_susp_ce
*ce
;
304 grub_disk_addr_t ce_block
;
306 ce
= (struct grub_iso9660_susp_ce
*) entry
;
307 sua_size
= grub_le_to_cpu32 (ce
->len
);
308 off
= grub_le_to_cpu32 (ce
->off
);
309 ce_block
= grub_le_to_cpu32 (ce
->blk
) << GRUB_ISO9660_LOG2_BLKSZ
;
312 sua
= grub_malloc (sua_size
);
316 /* Load a part of the System Usage Area. */
317 err
= grub_disk_read (node
->data
->disk
, ce_block
, off
,
322 entry
= (struct grub_iso9660_susp_entry
*) sua
;
325 if (hook (entry
, hook_arg
))
337 grub_iso9660_convert_string (grub_uint8_t
*us
, int len
)
341 grub_uint16_t t
[MAX_NAMELEN
/ 2 + 1];
343 p
= grub_malloc (len
* GRUB_MAX_UTF8_PER_UTF16
+ 1);
347 for (i
=0; i
<len
; i
++)
348 t
[i
] = grub_be_to_cpu16 (grub_get_unaligned16 (us
+ 2 * i
));
350 *grub_utf16_to_utf8 ((grub_uint8_t
*) p
, t
, len
) = '\0';
356 susp_iterate_set_rockridge (struct grub_iso9660_susp_entry
*susp_entry
,
359 struct grub_iso9660_data
*data
= _data
;
360 /* The "ER" entry is used to detect extensions. The
361 `IEEE_P1285' extension means Rock ridge. */
362 if (grub_strncmp ((char *) susp_entry
->sig
, "ER", 2) == 0)
371 set_rockridge (struct grub_iso9660_data
*data
)
376 struct grub_iso9660_dir rootdir
;
377 struct grub_iso9660_susp_entry
*entry
;
381 /* Read the system use area and test it to see if SUSP is
383 if (grub_disk_read (data
->disk
,
384 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
385 << GRUB_ISO9660_LOG2_BLKSZ
), 0,
386 sizeof (rootdir
), (char *) &rootdir
))
387 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
389 sua_pos
= (sizeof (rootdir
) + rootdir
.namelen
390 + (rootdir
.namelen
% 2) - 1);
391 sua_size
= rootdir
.len
- sua_pos
;
394 return GRUB_ERR_NONE
;
396 sua
= grub_malloc (sua_size
);
400 if (grub_disk_read (data
->disk
,
401 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
402 << GRUB_ISO9660_LOG2_BLKSZ
), sua_pos
,
406 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
409 entry
= (struct grub_iso9660_susp_entry
*) sua
;
411 /* Test if the SUSP protocol is used on this filesystem. */
412 if (grub_strncmp ((char *) entry
->sig
, "SP", 2) == 0)
414 struct grub_fshelp_node rootnode
;
416 rootnode
.data
= data
;
417 rootnode
.alloc_dirents
= ARRAY_SIZE (rootnode
.dirents
);
418 rootnode
.have_dirents
= 1;
419 rootnode
.have_symlink
= 0;
420 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
422 /* The 2nd data byte stored how many bytes are skipped every time
423 to get to the SUA (System Usage Area). */
424 data
->susp_skip
= entry
->data
[2];
425 entry
= (struct grub_iso9660_susp_entry
*) ((char *) entry
+ entry
->len
);
427 /* Iterate over the entries in the SUA area to detect
429 if (grub_iso9660_susp_iterate (&rootnode
,
430 sua_pos
, sua_size
, susp_iterate_set_rockridge
,
438 return GRUB_ERR_NONE
;
441 static struct grub_iso9660_data
*
442 grub_iso9660_mount (grub_disk_t disk
)
444 struct grub_iso9660_data
*data
= 0;
445 struct grub_iso9660_primary_voldesc voldesc
;
448 data
= grub_zalloc (sizeof (struct grub_iso9660_data
));
457 int copy_voldesc
= 0;
459 /* Read the superblock. */
460 if (grub_disk_read (disk
, block
<< GRUB_ISO9660_LOG2_BLKSZ
, 0,
461 sizeof (struct grub_iso9660_primary_voldesc
),
464 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
468 if (grub_strncmp ((char *) voldesc
.voldesc
.magic
, "CD001", 5) != 0)
470 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
474 if (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_PRIMARY
)
476 else if (!data
->rockridge
477 && (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_SUPP
)
478 && (voldesc
.escape
[0] == 0x25) && (voldesc
.escape
[1] == 0x2f)
480 ((voldesc
.escape
[2] == 0x40) || /* UCS-2 Level 1. */
481 (voldesc
.escape
[2] == 0x43) || /* UCS-2 Level 2. */
482 (voldesc
.escape
[2] == 0x45))) /* UCS-2 Level 3. */
484 if (0 == g_ventoy_no_joliet
) {
492 grub_memcpy((char *) &data
->voldesc
, (char *) &voldesc
,
493 sizeof (struct grub_iso9660_primary_voldesc
));
494 if (set_rockridge (data
))
499 } while (voldesc
.voldesc
.type
!= GRUB_ISO9660_VOLDESC_END
);
510 grub_iso9660_read_symlink (grub_fshelp_node_t node
)
512 return node
->have_symlink
513 ? grub_strdup (node
->symlink
514 + (node
->have_dirents
) * sizeof (node
->dirents
[0])
515 - sizeof (node
->dirents
)) : grub_strdup ("");
519 get_node_size (grub_fshelp_node_t node
)
524 for (i
= 0; i
< node
->have_dirents
; i
++)
525 ret
+= grub_le_to_cpu32 (node
->dirents
[i
].size
);
529 struct iterate_dir_ctx
533 enum grub_fshelp_filetype type
;
538 /* Extend the symlink. */
540 add_part (struct iterate_dir_ctx
*ctx
,
544 int size
= ctx
->symlink
? grub_strlen (ctx
->symlink
) : 0;
546 ctx
->symlink
= grub_realloc (ctx
->symlink
, size
+ len2
+ 1);
550 grub_memcpy (ctx
->symlink
+ size
, part
, len2
);
551 ctx
->symlink
[size
+ len2
] = 0;
555 susp_iterate_dir (struct grub_iso9660_susp_entry
*entry
,
558 struct iterate_dir_ctx
*ctx
= _ctx
;
560 /* The filename in the rock ridge entry. */
561 if (grub_strncmp ("NM", (char *) entry
->sig
, 2) == 0)
563 /* The flags are stored at the data position 0, here the
564 filename type is stored. */
565 /* FIXME: Fix this slightly improper cast. */
566 if (entry
->data
[0] & GRUB_ISO9660_RR_DOT
)
567 ctx
->filename
= (char *) ".";
568 else if (entry
->data
[0] & GRUB_ISO9660_RR_DOTDOT
)
569 ctx
->filename
= (char *) "..";
570 else if (entry
->len
>= 5)
572 grub_size_t off
= 0, csize
= 1;
574 csize
= entry
->len
- 5;
576 if (ctx
->filename_alloc
)
578 off
= grub_strlen (ctx
->filename
);
579 ctx
->filename
= grub_realloc (ctx
->filename
, csize
+ off
+ 1);
584 ctx
->filename
= grub_zalloc (csize
+ 1);
591 ctx
->filename_alloc
= 1;
592 grub_memcpy (ctx
->filename
+ off
, (char *) &entry
->data
[1], csize
);
593 ctx
->filename
[off
+ csize
] = '\0';
596 /* The mode information (st_mode). */
597 else if (grub_strncmp ((char *) entry
->sig
, "PX", 2) == 0)
599 /* At position 0 of the PX record the st_mode information is
600 stored (little-endian). */
601 grub_uint32_t mode
= ((entry
->data
[0] + (entry
->data
[1] << 8))
602 & GRUB_ISO9660_FSTYPE_MASK
);
606 case GRUB_ISO9660_FSTYPE_DIR
:
607 ctx
->type
= GRUB_FSHELP_DIR
;
609 case GRUB_ISO9660_FSTYPE_REG
:
610 ctx
->type
= GRUB_FSHELP_REG
;
612 case GRUB_ISO9660_FSTYPE_SYMLINK
:
613 ctx
->type
= GRUB_FSHELP_SYMLINK
;
616 ctx
->type
= GRUB_FSHELP_UNKNOWN
;
619 else if (grub_strncmp ("SL", (char *) entry
->sig
, 2) == 0)
621 unsigned int pos
= 1;
623 /* The symlink is not stored as a POSIX symlink, translate it. */
624 while (pos
+ sizeof (*entry
) < entry
->len
)
626 /* The current position is the `Component Flag'. */
627 switch (entry
->data
[pos
] & 30)
631 /* The data on pos + 2 is the actual data, pos + 1
632 is the length. Both are part of the `Component
634 if (ctx
->symlink
&& !ctx
->was_continue
)
635 add_part (ctx
, "/", 1);
636 add_part (ctx
, (char *) &entry
->data
[pos
+ 2],
637 entry
->data
[pos
+ 1]);
638 ctx
->was_continue
= (entry
->data
[pos
] & 1);
643 add_part (ctx
, "./", 2);
647 add_part (ctx
, "../", 3);
651 add_part (ctx
, "/", 1);
654 /* In pos + 1 the length of the `Component Record' is
656 pos
+= entry
->data
[pos
+ 1] + 2;
659 /* Check if `grub_realloc' failed. */
668 grub_iso9660_iterate_dir (grub_fshelp_node_t dir
,
669 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
671 struct grub_iso9660_dir dirent
;
672 grub_off_t offset
= 0;
674 struct iterate_dir_ctx ctx
;
676 len
= get_node_size (dir
);
678 for (; offset
< len
; offset
+= dirent
.len
)
681 ctx
.was_continue
= 0;
683 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
686 if ((dirent
.flags
& FLAG_TYPE
) != FLAG_TYPE_DIR
) {
687 g_ventoy_last_read_dirent_pos
= g_ventoy_last_read_pos
;
688 g_ventoy_last_read_dirent_offset
= g_ventoy_last_read_offset
;
691 /* The end of the block, skip to the next one. */
694 offset
= (offset
/ GRUB_ISO9660_BLKSZ
+ 1) * GRUB_ISO9660_BLKSZ
;
699 char name
[MAX_NAMELEN
+ 1];
700 int nameoffset
= offset
+ sizeof (dirent
);
701 struct grub_fshelp_node
*node
;
702 int sua_off
= (sizeof (dirent
) + dirent
.namelen
+ 1
703 - (dirent
.namelen
% 2));
704 int sua_size
= dirent
.len
- sua_off
;
706 sua_off
+= offset
+ dir
->data
->susp_skip
;
709 ctx
.filename_alloc
= 0;
710 ctx
.type
= GRUB_FSHELP_UNKNOWN
;
712 if (dir
->data
->rockridge
713 && grub_iso9660_susp_iterate (dir
, sua_off
, sua_size
,
714 susp_iterate_dir
, &ctx
))
718 if (read_node (dir
, nameoffset
, dirent
.namelen
, (char *) name
))
721 node
= grub_malloc (sizeof (struct grub_fshelp_node
));
725 node
->alloc_dirents
= ARRAY_SIZE (node
->dirents
);
726 node
->have_dirents
= 1;
728 /* Setup a new node. */
729 node
->data
= dir
->data
;
730 node
->have_symlink
= 0;
732 /* If the filetype was not stored using rockridge, use
733 whatever is stored in the iso9660 filesystem. */
734 if (ctx
.type
== GRUB_FSHELP_UNKNOWN
)
736 if ((dirent
.flags
& FLAG_TYPE
) == FLAG_TYPE_DIR
)
737 ctx
.type
= GRUB_FSHELP_DIR
;
739 ctx
.type
= GRUB_FSHELP_REG
;
743 if (!ctx
.filename
&& dirent
.namelen
== 1 && name
[0] == 0)
744 ctx
.filename
= (char *) ".";
746 if (!ctx
.filename
&& dirent
.namelen
== 1 && name
[0] == 1)
747 ctx
.filename
= (char *) "..";
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 g_ventoy_last_file_dirent_pos
= g_ventoy_last_read_dirent_pos
;
844 g_ventoy_last_file_dirent_offset
= g_ventoy_last_read_dirent_offset
;
845 if (ctx
.filename_alloc
)
846 grub_free (ctx
.filename
);
849 if (ctx
.filename_alloc
)
850 grub_free (ctx
.filename
);
859 /* Context for grub_iso9660_dir. */
860 struct grub_iso9660_dir_ctx
862 grub_fs_dir_hook_t hook
;
866 /* Helper for grub_iso9660_dir. */
868 grub_iso9660_dir_iter (const char *filename
,
869 enum grub_fshelp_filetype filetype
,
870 grub_fshelp_node_t node
, void *data
)
872 struct grub_iso9660_dir_ctx
*ctx
= data
;
873 struct grub_dirhook_info info
;
875 grub_memset (&info
, 0, sizeof (info
));
876 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
877 info
.mtimeset
= !!iso9660_to_unixtime2 (&node
->dirents
[0].mtime
, &info
.mtime
);
880 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
884 grub_iso9660_dir (grub_device_t device
, const char *path
,
885 grub_fs_dir_hook_t hook
, void *hook_data
)
887 struct grub_iso9660_dir_ctx ctx
= { hook
, hook_data
};
888 struct grub_iso9660_data
*data
= 0;
889 struct grub_fshelp_node rootnode
;
890 struct grub_fshelp_node
*foundnode
;
892 grub_dl_ref (my_mod
);
894 data
= grub_iso9660_mount (device
->disk
);
898 rootnode
.data
= data
;
899 rootnode
.alloc_dirents
= 0;
900 rootnode
.have_dirents
= 1;
901 rootnode
.have_symlink
= 0;
902 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
904 /* Use the fshelp function to traverse the path. */
905 if (grub_fshelp_find_file (path
, &rootnode
,
907 grub_iso9660_iterate_dir
,
908 grub_iso9660_read_symlink
,
912 /* List the files in the directory. */
913 grub_iso9660_iterate_dir (foundnode
, grub_iso9660_dir_iter
, &ctx
);
915 if (foundnode
!= &rootnode
)
916 grub_free (foundnode
);
921 grub_dl_unref (my_mod
);
927 /* Open a file named NAME and initialize FILE. */
929 grub_iso9660_open (struct grub_file
*file
, const char *name
)
931 struct grub_iso9660_data
*data
;
932 struct grub_fshelp_node rootnode
;
933 struct grub_fshelp_node
*foundnode
;
935 grub_dl_ref (my_mod
);
937 data
= grub_iso9660_mount (file
->device
->disk
);
941 rootnode
.data
= data
;
942 rootnode
.alloc_dirents
= 0;
943 rootnode
.have_dirents
= 1;
944 rootnode
.have_symlink
= 0;
945 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
947 /* Use the fshelp function to traverse the path. */
948 if (grub_fshelp_find_file (name
, &rootnode
,
950 grub_iso9660_iterate_dir
,
951 grub_iso9660_read_symlink
,
955 data
->node
= foundnode
;
957 file
->size
= get_node_size (foundnode
);
963 grub_dl_unref (my_mod
);
972 grub_iso9660_read (grub_file_t file
, char *buf
, grub_size_t len
)
974 struct grub_iso9660_data
*data
=
975 (struct grub_iso9660_data
*) file
->data
;
978 /* XXX: The file is stored in as a single extent. */
979 data
->disk
->read_hook
= file
->read_hook
;
980 data
->disk
->read_hook_data
= file
->read_hook_data
;
981 err
= read_node (data
->node
, file
->offset
, len
, buf
);
982 data
->disk
->read_hook
= NULL
;
984 if (err
|| grub_errno
)
992 grub_iso9660_close (grub_file_t file
)
994 struct grub_iso9660_data
*data
=
995 (struct grub_iso9660_data
*) file
->data
;
996 grub_free (data
->node
);
999 grub_dl_unref (my_mod
);
1001 return GRUB_ERR_NONE
;
1006 grub_iso9660_label (grub_device_t device
, char **label
)
1008 struct grub_iso9660_data
*data
;
1009 data
= grub_iso9660_mount (device
->disk
);
1014 *label
= grub_iso9660_convert_string (data
->voldesc
.volname
, 16);
1016 *label
= grub_strndup ((char *) data
->voldesc
.volname
, 32);
1020 for (ptr
= *label
; *ptr
;ptr
++);
1022 while (ptr
>= *label
&& *ptr
== ' ')
1036 grub_iso9660_uuid (grub_device_t device
, char **uuid
)
1038 struct grub_iso9660_data
*data
;
1039 grub_disk_t disk
= device
->disk
;
1041 grub_dl_ref (my_mod
);
1043 data
= grub_iso9660_mount (disk
);
1046 if (! data
->voldesc
.modified
.year
[0] && ! data
->voldesc
.modified
.year
[1]
1047 && ! data
->voldesc
.modified
.year
[2] && ! data
->voldesc
.modified
.year
[3]
1048 && ! data
->voldesc
.modified
.month
[0] && ! data
->voldesc
.modified
.month
[1]
1049 && ! data
->voldesc
.modified
.day
[0] && ! data
->voldesc
.modified
.day
[1]
1050 && ! data
->voldesc
.modified
.hour
[0] && ! data
->voldesc
.modified
.hour
[1]
1051 && ! data
->voldesc
.modified
.minute
[0] && ! data
->voldesc
.modified
.minute
[1]
1052 && ! data
->voldesc
.modified
.second
[0] && ! data
->voldesc
.modified
.second
[1]
1053 && ! data
->voldesc
.modified
.hundredth
[0] && ! data
->voldesc
.modified
.hundredth
[1])
1055 grub_error (GRUB_ERR_BAD_NUMBER
, "no creation date in filesystem to generate UUID");
1060 *uuid
= grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
1061 data
->voldesc
.modified
.year
[0],
1062 data
->voldesc
.modified
.year
[1],
1063 data
->voldesc
.modified
.year
[2],
1064 data
->voldesc
.modified
.year
[3],
1065 data
->voldesc
.modified
.month
[0],
1066 data
->voldesc
.modified
.month
[1],
1067 data
->voldesc
.modified
.day
[0],
1068 data
->voldesc
.modified
.day
[1],
1069 data
->voldesc
.modified
.hour
[0],
1070 data
->voldesc
.modified
.hour
[1],
1071 data
->voldesc
.modified
.minute
[0],
1072 data
->voldesc
.modified
.minute
[1],
1073 data
->voldesc
.modified
.second
[0],
1074 data
->voldesc
.modified
.second
[1],
1075 data
->voldesc
.modified
.hundredth
[0],
1076 data
->voldesc
.modified
.hundredth
[1]);
1082 grub_dl_unref (my_mod
);
1089 /* Get writing time of filesystem. */
1091 grub_iso9660_mtime (grub_device_t device
, grub_int32_t
*timebuf
)
1093 struct grub_iso9660_data
*data
;
1094 grub_disk_t disk
= device
->disk
;
1097 grub_dl_ref (my_mod
);
1099 data
= grub_iso9660_mount (disk
);
1102 grub_dl_unref (my_mod
);
1105 err
= iso9660_to_unixtime (&data
->voldesc
.modified
, timebuf
);
1107 grub_dl_unref (my_mod
);
1114 void grub_iso9660_set_nojoliet(int nojoliet
)
1116 g_ventoy_no_joliet
= nojoliet
;
1119 grub_uint64_t
grub_iso9660_get_last_read_pos(grub_file_t file
)
1122 return (g_ventoy_last_read_pos
<< GRUB_DISK_SECTOR_BITS
);
1125 grub_uint64_t
grub_iso9660_get_last_file_dirent_pos(grub_file_t file
)
1128 return (g_ventoy_last_file_dirent_pos
<< GRUB_DISK_SECTOR_BITS
) + g_ventoy_last_file_dirent_offset
;
1131 static struct grub_fs grub_iso9660_fs
=
1134 .fs_dir
= grub_iso9660_dir
,
1135 .fs_open
= grub_iso9660_open
,
1136 .fs_read
= grub_iso9660_read
,
1137 .fs_close
= grub_iso9660_close
,
1138 .fs_label
= grub_iso9660_label
,
1139 .fs_uuid
= grub_iso9660_uuid
,
1140 .fs_mtime
= grub_iso9660_mtime
,
1142 .reserved_first_sector
= 1,
1143 .blocklist_install
= 1,
1148 GRUB_MOD_INIT(iso9660
)
1150 grub_fs_register (&grub_iso9660_fs
);
1154 GRUB_MOD_FINI(iso9660
)
1156 grub_fs_unregister (&grub_iso9660_fs
);