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 grub_uint64_t g_ventoy_last_read_pos
= 0;
36 static grub_uint64_t g_ventoy_last_read_offset
= 0;
37 static grub_uint64_t g_ventoy_last_read_dirent_pos
= 0;
38 static grub_uint64_t g_ventoy_last_read_dirent_offset
= 0;
39 static grub_uint64_t g_ventoy_last_file_dirent_pos
= 0;
40 static grub_uint64_t g_ventoy_last_file_dirent_offset
= 0;
42 #define GRUB_ISO9660_FSTYPE_DIR 0040000
43 #define GRUB_ISO9660_FSTYPE_REG 0100000
44 #define GRUB_ISO9660_FSTYPE_SYMLINK 0120000
45 #define GRUB_ISO9660_FSTYPE_MASK 0170000
47 #define GRUB_ISO9660_LOG2_BLKSZ 2
48 #define GRUB_ISO9660_BLKSZ 2048
50 #define GRUB_ISO9660_RR_DOT 2
51 #define GRUB_ISO9660_RR_DOTDOT 4
53 #define GRUB_ISO9660_VOLDESC_BOOT 0
54 #define GRUB_ISO9660_VOLDESC_PRIMARY 1
55 #define GRUB_ISO9660_VOLDESC_SUPP 2
56 #define GRUB_ISO9660_VOLDESC_PART 3
57 #define GRUB_ISO9660_VOLDESC_END 255
59 /* The head of a volume descriptor. */
60 struct grub_iso9660_voldesc
63 grub_uint8_t magic
[5];
67 struct grub_iso9660_date2
78 /* A directory entry. */
79 struct grub_iso9660_dir
82 grub_uint8_t ext_sectors
;
83 grub_uint32_t first_sector
;
84 grub_uint32_t first_sector_be
;
86 grub_uint32_t size_be
;
87 struct grub_iso9660_date2 mtime
;
89 grub_uint8_t unused2
[6];
90 #define MAX_NAMELEN 255
94 struct grub_iso9660_date
97 grub_uint8_t month
[2];
100 grub_uint8_t minute
[2];
101 grub_uint8_t second
[2];
102 grub_uint8_t hundredth
[2];
106 /* The primary volume descriptor. Only little endian is used. */
107 struct grub_iso9660_primary_voldesc
109 struct grub_iso9660_voldesc voldesc
;
110 grub_uint8_t unused1
[33];
111 grub_uint8_t volname
[32];
112 grub_uint8_t unused2
[16];
113 grub_uint8_t escape
[32];
114 grub_uint8_t unused3
[12];
115 grub_uint32_t path_table_size
;
116 grub_uint8_t unused4
[4];
117 grub_uint32_t path_table
;
118 grub_uint8_t unused5
[12];
119 struct grub_iso9660_dir rootdir
;
120 grub_uint8_t unused6
[624];
121 struct grub_iso9660_date created
;
122 struct grub_iso9660_date modified
;
125 /* A single entry in the path table. */
126 struct grub_iso9660_path
129 grub_uint8_t sectors
;
130 grub_uint32_t first_sector
;
131 grub_uint16_t parentdir
;
132 grub_uint8_t name
[0];
135 /* An entry in the System Usage area of the directory entry. */
136 struct grub_iso9660_susp_entry
140 grub_uint8_t version
;
141 grub_uint8_t data
[0];
144 /* The CE entry. This is used to describe the next block where data
146 struct grub_iso9660_susp_ce
148 struct grub_iso9660_susp_entry entry
;
150 grub_uint32_t blk_be
;
152 grub_uint32_t off_be
;
154 grub_uint32_t len_be
;
157 struct grub_iso9660_data
159 struct grub_iso9660_primary_voldesc voldesc
;
164 struct grub_fshelp_node
*node
;
167 struct grub_fshelp_node
169 struct grub_iso9660_data
*data
;
170 grub_size_t have_dirents
, alloc_dirents
;
172 struct grub_iso9660_dir dirents
[8];
181 FLAG_MORE_EXTENTS
= 0x80
184 static grub_dl_t my_mod
;
188 iso9660_to_unixtime (const struct grub_iso9660_date
*i
, grub_int32_t
*nix
)
190 struct grub_datetime datetime
;
192 if (! i
->year
[0] && ! i
->year
[1]
193 && ! i
->year
[2] && ! i
->year
[3]
194 && ! i
->month
[0] && ! i
->month
[1]
195 && ! i
->day
[0] && ! i
->day
[1]
196 && ! i
->hour
[0] && ! i
->hour
[1]
197 && ! i
->minute
[0] && ! i
->minute
[1]
198 && ! i
->second
[0] && ! i
->second
[1]
199 && ! i
->hundredth
[0] && ! i
->hundredth
[1])
200 return grub_error (GRUB_ERR_BAD_NUMBER
, "empty date");
201 datetime
.year
= (i
->year
[0] - '0') * 1000 + (i
->year
[1] - '0') * 100
202 + (i
->year
[2] - '0') * 10 + (i
->year
[3] - '0');
203 datetime
.month
= (i
->month
[0] - '0') * 10 + (i
->month
[1] - '0');
204 datetime
.day
= (i
->day
[0] - '0') * 10 + (i
->day
[1] - '0');
205 datetime
.hour
= (i
->hour
[0] - '0') * 10 + (i
->hour
[1] - '0');
206 datetime
.minute
= (i
->minute
[0] - '0') * 10 + (i
->minute
[1] - '0');
207 datetime
.second
= (i
->second
[0] - '0') * 10 + (i
->second
[1] - '0');
209 if (!grub_datetime2unixtime (&datetime
, nix
))
210 return grub_error (GRUB_ERR_BAD_NUMBER
, "incorrect date");
211 *nix
-= i
->offset
* 60 * 15;
212 return GRUB_ERR_NONE
;
216 iso9660_to_unixtime2 (const struct grub_iso9660_date2
*i
, grub_int32_t
*nix
)
218 struct grub_datetime datetime
;
220 datetime
.year
= i
->year
+ 1900;
221 datetime
.month
= i
->month
;
222 datetime
.day
= i
->day
;
223 datetime
.hour
= i
->hour
;
224 datetime
.minute
= i
->minute
;
225 datetime
.second
= i
->second
;
227 if (!grub_datetime2unixtime (&datetime
, nix
))
229 *nix
-= i
->offset
* 60 * 15;
234 read_node (grub_fshelp_node_t node
, grub_off_t off
, grub_size_t len
, char *buf
)
242 while (i
< node
->have_dirents
243 && off
>= grub_le_to_cpu32 (node
->dirents
[i
].size
))
245 off
-= grub_le_to_cpu32 (node
->dirents
[i
].size
);
248 if (i
== node
->have_dirents
)
249 return grub_error (GRUB_ERR_OUT_OF_RANGE
, "read out of range");
250 toread
= grub_le_to_cpu32 (node
->dirents
[i
].size
);
253 g_ventoy_last_read_pos
= ((grub_disk_addr_t
) grub_le_to_cpu32 (node
->dirents
[i
].first_sector
)) << GRUB_ISO9660_LOG2_BLKSZ
;
254 g_ventoy_last_read_offset
= off
;
255 err
= grub_disk_read (node
->data
->disk
, g_ventoy_last_read_pos
, off
, toread
, buf
);
262 return GRUB_ERR_NONE
;
265 /* Iterate over the susp entries, starting with block SUA_BLOCK on the
266 offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for
269 grub_iso9660_susp_iterate (grub_fshelp_node_t node
, grub_off_t off
,
270 grub_ssize_t sua_size
,
272 (struct grub_iso9660_susp_entry
*entry
, void *hook_arg
),
276 struct grub_iso9660_susp_entry
*entry
;
280 return GRUB_ERR_NONE
;
282 sua
= grub_malloc (sua_size
);
286 /* Load a part of the System Usage Area. */
287 err
= read_node (node
, off
, sua_size
, sua
);
291 for (entry
= (struct grub_iso9660_susp_entry
*) sua
; (char *) entry
< (char *) sua
+ sua_size
- 1 && entry
->len
> 0;
292 entry
= (struct grub_iso9660_susp_entry
*)
293 ((char *) entry
+ entry
->len
))
295 /* The last entry. */
296 if (grub_strncmp ((char *) entry
->sig
, "ST", 2) == 0)
299 /* Additional entries are stored elsewhere. */
300 if (grub_strncmp ((char *) entry
->sig
, "CE", 2) == 0)
302 struct grub_iso9660_susp_ce
*ce
;
303 grub_disk_addr_t ce_block
;
305 ce
= (struct grub_iso9660_susp_ce
*) entry
;
306 sua_size
= grub_le_to_cpu32 (ce
->len
);
307 off
= grub_le_to_cpu32 (ce
->off
);
308 ce_block
= grub_le_to_cpu32 (ce
->blk
) << GRUB_ISO9660_LOG2_BLKSZ
;
311 sua
= grub_malloc (sua_size
);
315 /* Load a part of the System Usage Area. */
316 err
= grub_disk_read (node
->data
->disk
, ce_block
, off
,
321 entry
= (struct grub_iso9660_susp_entry
*) sua
;
324 if (hook (entry
, hook_arg
))
336 grub_iso9660_convert_string (grub_uint8_t
*us
, int len
)
340 grub_uint16_t t
[MAX_NAMELEN
/ 2 + 1];
342 p
= grub_malloc (len
* GRUB_MAX_UTF8_PER_UTF16
+ 1);
346 for (i
=0; i
<len
; i
++)
347 t
[i
] = grub_be_to_cpu16 (grub_get_unaligned16 (us
+ 2 * i
));
349 *grub_utf16_to_utf8 ((grub_uint8_t
*) p
, t
, len
) = '\0';
355 susp_iterate_set_rockridge (struct grub_iso9660_susp_entry
*susp_entry
,
358 struct grub_iso9660_data
*data
= _data
;
359 /* The "ER" entry is used to detect extensions. The
360 `IEEE_P1285' extension means Rock ridge. */
361 if (grub_strncmp ((char *) susp_entry
->sig
, "ER", 2) == 0)
370 set_rockridge (struct grub_iso9660_data
*data
)
375 struct grub_iso9660_dir rootdir
;
376 struct grub_iso9660_susp_entry
*entry
;
380 /* Read the system use area and test it to see if SUSP is
382 if (grub_disk_read (data
->disk
,
383 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
384 << GRUB_ISO9660_LOG2_BLKSZ
), 0,
385 sizeof (rootdir
), (char *) &rootdir
))
386 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
388 sua_pos
= (sizeof (rootdir
) + rootdir
.namelen
389 + (rootdir
.namelen
% 2) - 1);
390 sua_size
= rootdir
.len
- sua_pos
;
393 return GRUB_ERR_NONE
;
395 sua
= grub_malloc (sua_size
);
399 if (grub_disk_read (data
->disk
,
400 (grub_le_to_cpu32 (data
->voldesc
.rootdir
.first_sector
)
401 << GRUB_ISO9660_LOG2_BLKSZ
), sua_pos
,
405 return grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
408 entry
= (struct grub_iso9660_susp_entry
*) sua
;
410 /* Test if the SUSP protocol is used on this filesystem. */
411 if (grub_strncmp ((char *) entry
->sig
, "SP", 2) == 0)
413 struct grub_fshelp_node rootnode
;
415 rootnode
.data
= data
;
416 rootnode
.alloc_dirents
= ARRAY_SIZE (rootnode
.dirents
);
417 rootnode
.have_dirents
= 1;
418 rootnode
.have_symlink
= 0;
419 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
421 /* The 2nd data byte stored how many bytes are skipped every time
422 to get to the SUA (System Usage Area). */
423 data
->susp_skip
= entry
->data
[2];
424 entry
= (struct grub_iso9660_susp_entry
*) ((char *) entry
+ entry
->len
);
426 /* Iterate over the entries in the SUA area to detect
428 if (grub_iso9660_susp_iterate (&rootnode
,
429 sua_pos
, sua_size
, susp_iterate_set_rockridge
,
437 return GRUB_ERR_NONE
;
440 static struct grub_iso9660_data
*
441 grub_iso9660_mount (grub_disk_t disk
)
443 struct grub_iso9660_data
*data
= 0;
444 struct grub_iso9660_primary_voldesc voldesc
;
447 data
= grub_zalloc (sizeof (struct grub_iso9660_data
));
456 int copy_voldesc
= 0;
458 /* Read the superblock. */
459 if (grub_disk_read (disk
, block
<< GRUB_ISO9660_LOG2_BLKSZ
, 0,
460 sizeof (struct grub_iso9660_primary_voldesc
),
463 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
467 if (grub_strncmp ((char *) voldesc
.voldesc
.magic
, "CD001", 5) != 0)
469 grub_error (GRUB_ERR_BAD_FS
, "not a ISO9660 filesystem");
473 if (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_PRIMARY
)
475 else if (!data
->rockridge
476 && (voldesc
.voldesc
.type
== GRUB_ISO9660_VOLDESC_SUPP
)
477 && (voldesc
.escape
[0] == 0x25) && (voldesc
.escape
[1] == 0x2f)
479 ((voldesc
.escape
[2] == 0x40) || /* UCS-2 Level 1. */
480 (voldesc
.escape
[2] == 0x43) || /* UCS-2 Level 2. */
481 (voldesc
.escape
[2] == 0x45))) /* UCS-2 Level 3. */
489 grub_memcpy((char *) &data
->voldesc
, (char *) &voldesc
,
490 sizeof (struct grub_iso9660_primary_voldesc
));
491 if (set_rockridge (data
))
496 } while (voldesc
.voldesc
.type
!= GRUB_ISO9660_VOLDESC_END
);
507 grub_iso9660_read_symlink (grub_fshelp_node_t node
)
509 return node
->have_symlink
510 ? grub_strdup (node
->symlink
511 + (node
->have_dirents
) * sizeof (node
->dirents
[0])
512 - sizeof (node
->dirents
)) : grub_strdup ("");
516 get_node_size (grub_fshelp_node_t node
)
521 for (i
= 0; i
< node
->have_dirents
; i
++)
522 ret
+= grub_le_to_cpu32 (node
->dirents
[i
].size
);
526 struct iterate_dir_ctx
530 enum grub_fshelp_filetype type
;
535 /* Extend the symlink. */
537 add_part (struct iterate_dir_ctx
*ctx
,
541 int size
= ctx
->symlink
? grub_strlen (ctx
->symlink
) : 0;
543 ctx
->symlink
= grub_realloc (ctx
->symlink
, size
+ len2
+ 1);
547 grub_memcpy (ctx
->symlink
+ size
, part
, len2
);
548 ctx
->symlink
[size
+ len2
] = 0;
552 susp_iterate_dir (struct grub_iso9660_susp_entry
*entry
,
555 struct iterate_dir_ctx
*ctx
= _ctx
;
557 /* The filename in the rock ridge entry. */
558 if (grub_strncmp ("NM", (char *) entry
->sig
, 2) == 0)
560 /* The flags are stored at the data position 0, here the
561 filename type is stored. */
562 /* FIXME: Fix this slightly improper cast. */
563 if (entry
->data
[0] & GRUB_ISO9660_RR_DOT
)
564 ctx
->filename
= (char *) ".";
565 else if (entry
->data
[0] & GRUB_ISO9660_RR_DOTDOT
)
566 ctx
->filename
= (char *) "..";
567 else if (entry
->len
>= 5)
569 grub_size_t off
= 0, csize
= 1;
571 csize
= entry
->len
- 5;
573 if (ctx
->filename_alloc
)
575 off
= grub_strlen (ctx
->filename
);
576 ctx
->filename
= grub_realloc (ctx
->filename
, csize
+ off
+ 1);
581 ctx
->filename
= grub_zalloc (csize
+ 1);
588 ctx
->filename_alloc
= 1;
589 grub_memcpy (ctx
->filename
+ off
, (char *) &entry
->data
[1], csize
);
590 ctx
->filename
[off
+ csize
] = '\0';
593 /* The mode information (st_mode). */
594 else if (grub_strncmp ((char *) entry
->sig
, "PX", 2) == 0)
596 /* At position 0 of the PX record the st_mode information is
597 stored (little-endian). */
598 grub_uint32_t mode
= ((entry
->data
[0] + (entry
->data
[1] << 8))
599 & GRUB_ISO9660_FSTYPE_MASK
);
603 case GRUB_ISO9660_FSTYPE_DIR
:
604 ctx
->type
= GRUB_FSHELP_DIR
;
606 case GRUB_ISO9660_FSTYPE_REG
:
607 ctx
->type
= GRUB_FSHELP_REG
;
609 case GRUB_ISO9660_FSTYPE_SYMLINK
:
610 ctx
->type
= GRUB_FSHELP_SYMLINK
;
613 ctx
->type
= GRUB_FSHELP_UNKNOWN
;
616 else if (grub_strncmp ("SL", (char *) entry
->sig
, 2) == 0)
618 unsigned int pos
= 1;
620 /* The symlink is not stored as a POSIX symlink, translate it. */
621 while (pos
+ sizeof (*entry
) < entry
->len
)
623 /* The current position is the `Component Flag'. */
624 switch (entry
->data
[pos
] & 30)
628 /* The data on pos + 2 is the actual data, pos + 1
629 is the length. Both are part of the `Component
631 if (ctx
->symlink
&& !ctx
->was_continue
)
632 add_part (ctx
, "/", 1);
633 add_part (ctx
, (char *) &entry
->data
[pos
+ 2],
634 entry
->data
[pos
+ 1]);
635 ctx
->was_continue
= (entry
->data
[pos
] & 1);
640 add_part (ctx
, "./", 2);
644 add_part (ctx
, "../", 3);
648 add_part (ctx
, "/", 1);
651 /* In pos + 1 the length of the `Component Record' is
653 pos
+= entry
->data
[pos
+ 1] + 2;
656 /* Check if `grub_realloc' failed. */
665 grub_iso9660_iterate_dir (grub_fshelp_node_t dir
,
666 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
668 struct grub_iso9660_dir dirent
;
669 grub_off_t offset
= 0;
671 struct iterate_dir_ctx ctx
;
673 len
= get_node_size (dir
);
675 for (; offset
< len
; offset
+= dirent
.len
)
678 ctx
.was_continue
= 0;
680 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
683 if ((dirent
.flags
& FLAG_TYPE
) != FLAG_TYPE_DIR
) {
684 g_ventoy_last_read_dirent_pos
= g_ventoy_last_read_pos
;
685 g_ventoy_last_read_dirent_offset
= g_ventoy_last_read_offset
;
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 /* The filename was not stored in a rock ridge entry. Read it
747 from the iso9660 filesystem. */
748 if (!dir
->data
->joliet
&& !ctx
.filename
)
751 name
[dirent
.namelen
] = '\0';
752 ctx
.filename
= grub_strrchr (name
, ';');
754 *ctx
.filename
= '\0';
755 /* ISO9660 names are not case-preserving. */
756 ctx
.type
|= GRUB_FSHELP_CASE_INSENSITIVE
;
757 for (ptr
= name
; *ptr
; ptr
++)
758 *ptr
= grub_tolower (*ptr
);
759 if (ptr
!= name
&& *(ptr
- 1) == '.')
764 if (dir
->data
->joliet
&& !ctx
.filename
)
768 ctx
.filename
= grub_iso9660_convert_string
769 ((grub_uint8_t
*) name
, dirent
.namelen
>> 1);
771 semicolon
= grub_strrchr (ctx
.filename
, ';');
775 ctx
.filename_alloc
= 1;
778 node
->dirents
[0] = dirent
;
779 while (dirent
.flags
& FLAG_MORE_EXTENTS
)
781 offset
+= dirent
.len
;
782 if (read_node (dir
, offset
, sizeof (dirent
), (char *) &dirent
))
784 if (ctx
.filename_alloc
)
785 grub_free (ctx
.filename
);
789 if (node
->have_dirents
>= node
->alloc_dirents
)
791 struct grub_fshelp_node
*new_node
;
792 node
->alloc_dirents
*= 2;
793 new_node
= grub_realloc (node
,
794 sizeof (struct grub_fshelp_node
)
795 + ((node
->alloc_dirents
796 - ARRAY_SIZE (node
->dirents
))
797 * sizeof (node
->dirents
[0])));
800 if (ctx
.filename_alloc
)
801 grub_free (ctx
.filename
);
807 node
->dirents
[node
->have_dirents
++] = dirent
;
811 if ((node
->alloc_dirents
- node
->have_dirents
)
812 * sizeof (node
->dirents
[0]) < grub_strlen (ctx
.symlink
) + 1)
814 struct grub_fshelp_node
*new_node
;
815 new_node
= grub_realloc (node
,
816 sizeof (struct grub_fshelp_node
)
817 + ((node
->alloc_dirents
818 - ARRAY_SIZE (node
->dirents
))
819 * sizeof (node
->dirents
[0]))
820 + grub_strlen (ctx
.symlink
) + 1);
823 if (ctx
.filename_alloc
)
824 grub_free (ctx
.filename
);
830 node
->have_symlink
= 1;
831 grub_strcpy (node
->symlink
832 + node
->have_dirents
* sizeof (node
->dirents
[0])
833 - sizeof (node
->dirents
), ctx
.symlink
);
834 grub_free (ctx
.symlink
);
836 ctx
.was_continue
= 0;
838 if (hook (ctx
.filename
, ctx
.type
, node
, hook_data
))
840 g_ventoy_last_file_dirent_pos
= g_ventoy_last_read_dirent_pos
;
841 g_ventoy_last_file_dirent_offset
= g_ventoy_last_read_dirent_offset
;
842 if (ctx
.filename_alloc
)
843 grub_free (ctx
.filename
);
846 if (ctx
.filename_alloc
)
847 grub_free (ctx
.filename
);
856 /* Context for grub_iso9660_dir. */
857 struct grub_iso9660_dir_ctx
859 grub_fs_dir_hook_t hook
;
863 /* Helper for grub_iso9660_dir. */
865 grub_iso9660_dir_iter (const char *filename
,
866 enum grub_fshelp_filetype filetype
,
867 grub_fshelp_node_t node
, void *data
)
869 struct grub_iso9660_dir_ctx
*ctx
= data
;
870 struct grub_dirhook_info info
;
872 grub_memset (&info
, 0, sizeof (info
));
873 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
874 info
.mtimeset
= !!iso9660_to_unixtime2 (&node
->dirents
[0].mtime
, &info
.mtime
);
877 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
881 grub_iso9660_dir (grub_device_t device
, const char *path
,
882 grub_fs_dir_hook_t hook
, void *hook_data
)
884 struct grub_iso9660_dir_ctx ctx
= { hook
, hook_data
};
885 struct grub_iso9660_data
*data
= 0;
886 struct grub_fshelp_node rootnode
;
887 struct grub_fshelp_node
*foundnode
;
889 grub_dl_ref (my_mod
);
891 data
= grub_iso9660_mount (device
->disk
);
895 rootnode
.data
= data
;
896 rootnode
.alloc_dirents
= 0;
897 rootnode
.have_dirents
= 1;
898 rootnode
.have_symlink
= 0;
899 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
901 /* Use the fshelp function to traverse the path. */
902 if (grub_fshelp_find_file (path
, &rootnode
,
904 grub_iso9660_iterate_dir
,
905 grub_iso9660_read_symlink
,
909 /* List the files in the directory. */
910 grub_iso9660_iterate_dir (foundnode
, grub_iso9660_dir_iter
, &ctx
);
912 if (foundnode
!= &rootnode
)
913 grub_free (foundnode
);
918 grub_dl_unref (my_mod
);
924 /* Open a file named NAME and initialize FILE. */
926 grub_iso9660_open (struct grub_file
*file
, const char *name
)
928 struct grub_iso9660_data
*data
;
929 struct grub_fshelp_node rootnode
;
930 struct grub_fshelp_node
*foundnode
;
932 grub_dl_ref (my_mod
);
934 data
= grub_iso9660_mount (file
->device
->disk
);
938 rootnode
.data
= data
;
939 rootnode
.alloc_dirents
= 0;
940 rootnode
.have_dirents
= 1;
941 rootnode
.have_symlink
= 0;
942 rootnode
.dirents
[0] = data
->voldesc
.rootdir
;
944 /* Use the fshelp function to traverse the path. */
945 if (grub_fshelp_find_file (name
, &rootnode
,
947 grub_iso9660_iterate_dir
,
948 grub_iso9660_read_symlink
,
952 data
->node
= foundnode
;
954 file
->size
= get_node_size (foundnode
);
960 grub_dl_unref (my_mod
);
969 grub_iso9660_read (grub_file_t file
, char *buf
, grub_size_t len
)
971 struct grub_iso9660_data
*data
=
972 (struct grub_iso9660_data
*) file
->data
;
975 /* XXX: The file is stored in as a single extent. */
976 data
->disk
->read_hook
= file
->read_hook
;
977 data
->disk
->read_hook_data
= file
->read_hook_data
;
978 err
= read_node (data
->node
, file
->offset
, len
, buf
);
979 data
->disk
->read_hook
= NULL
;
981 if (err
|| grub_errno
)
989 grub_iso9660_close (grub_file_t file
)
991 struct grub_iso9660_data
*data
=
992 (struct grub_iso9660_data
*) file
->data
;
993 grub_free (data
->node
);
996 grub_dl_unref (my_mod
);
998 return GRUB_ERR_NONE
;
1003 grub_iso9660_label (grub_device_t device
, char **label
)
1005 struct grub_iso9660_data
*data
;
1006 data
= grub_iso9660_mount (device
->disk
);
1011 *label
= grub_iso9660_convert_string (data
->voldesc
.volname
, 16);
1013 *label
= grub_strndup ((char *) data
->voldesc
.volname
, 32);
1017 for (ptr
= *label
; *ptr
;ptr
++);
1019 while (ptr
>= *label
&& *ptr
== ' ')
1033 grub_iso9660_uuid (grub_device_t device
, char **uuid
)
1035 struct grub_iso9660_data
*data
;
1036 grub_disk_t disk
= device
->disk
;
1038 grub_dl_ref (my_mod
);
1040 data
= grub_iso9660_mount (disk
);
1043 if (! data
->voldesc
.modified
.year
[0] && ! data
->voldesc
.modified
.year
[1]
1044 && ! data
->voldesc
.modified
.year
[2] && ! data
->voldesc
.modified
.year
[3]
1045 && ! data
->voldesc
.modified
.month
[0] && ! data
->voldesc
.modified
.month
[1]
1046 && ! data
->voldesc
.modified
.day
[0] && ! data
->voldesc
.modified
.day
[1]
1047 && ! data
->voldesc
.modified
.hour
[0] && ! data
->voldesc
.modified
.hour
[1]
1048 && ! data
->voldesc
.modified
.minute
[0] && ! data
->voldesc
.modified
.minute
[1]
1049 && ! data
->voldesc
.modified
.second
[0] && ! data
->voldesc
.modified
.second
[1]
1050 && ! data
->voldesc
.modified
.hundredth
[0] && ! data
->voldesc
.modified
.hundredth
[1])
1052 grub_error (GRUB_ERR_BAD_NUMBER
, "no creation date in filesystem to generate UUID");
1057 *uuid
= grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
1058 data
->voldesc
.modified
.year
[0],
1059 data
->voldesc
.modified
.year
[1],
1060 data
->voldesc
.modified
.year
[2],
1061 data
->voldesc
.modified
.year
[3],
1062 data
->voldesc
.modified
.month
[0],
1063 data
->voldesc
.modified
.month
[1],
1064 data
->voldesc
.modified
.day
[0],
1065 data
->voldesc
.modified
.day
[1],
1066 data
->voldesc
.modified
.hour
[0],
1067 data
->voldesc
.modified
.hour
[1],
1068 data
->voldesc
.modified
.minute
[0],
1069 data
->voldesc
.modified
.minute
[1],
1070 data
->voldesc
.modified
.second
[0],
1071 data
->voldesc
.modified
.second
[1],
1072 data
->voldesc
.modified
.hundredth
[0],
1073 data
->voldesc
.modified
.hundredth
[1]);
1079 grub_dl_unref (my_mod
);
1086 /* Get writing time of filesystem. */
1088 grub_iso9660_mtime (grub_device_t device
, grub_int32_t
*timebuf
)
1090 struct grub_iso9660_data
*data
;
1091 grub_disk_t disk
= device
->disk
;
1094 grub_dl_ref (my_mod
);
1096 data
= grub_iso9660_mount (disk
);
1099 grub_dl_unref (my_mod
);
1102 err
= iso9660_to_unixtime (&data
->voldesc
.modified
, timebuf
);
1104 grub_dl_unref (my_mod
);
1111 grub_uint64_t
grub_iso9660_get_last_read_pos(grub_file_t file
)
1114 return (g_ventoy_last_read_pos
<< GRUB_DISK_SECTOR_BITS
);
1117 grub_uint64_t
grub_iso9660_get_last_file_dirent_pos(grub_file_t file
)
1120 return (g_ventoy_last_file_dirent_pos
<< GRUB_DISK_SECTOR_BITS
) + g_ventoy_last_file_dirent_offset
;
1123 static struct grub_fs grub_iso9660_fs
=
1126 .fs_dir
= grub_iso9660_dir
,
1127 .fs_open
= grub_iso9660_open
,
1128 .fs_read
= grub_iso9660_read
,
1129 .fs_close
= grub_iso9660_close
,
1130 .fs_label
= grub_iso9660_label
,
1131 .fs_uuid
= grub_iso9660_uuid
,
1132 .fs_mtime
= grub_iso9660_mtime
,
1134 .reserved_first_sector
= 1,
1135 .blocklist_install
= 1,
1140 GRUB_MOD_INIT(iso9660
)
1142 grub_fs_register (&grub_iso9660_fs
);
1146 GRUB_MOD_FINI(iso9660
)
1148 grub_fs_unregister (&grub_iso9660_fs
);