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>
31
#define GRUB_ISO9660_FSTYPE_DIR 0040000
32
#define GRUB_ISO9660_FSTYPE_REG 0100000
33
#define GRUB_ISO9660_FSTYPE_SYMLINK 0120000
34
#define GRUB_ISO9660_FSTYPE_MASK 0170000
36
#define GRUB_ISO9660_LOG2_BLKSZ 2
37
#define GRUB_ISO9660_BLKSZ 2048
39
#define GRUB_ISO9660_RR_DOT 2
40
#define GRUB_ISO9660_RR_DOTDOT 4
42
#define GRUB_ISO9660_VOLDESC_BOOT 0
43
#define GRUB_ISO9660_VOLDESC_PRIMARY 1
44
#define GRUB_ISO9660_VOLDESC_SUPP 2
45
#define GRUB_ISO9660_VOLDESC_PART 3
46
#define GRUB_ISO9660_VOLDESC_END 255
48
/* The head of a volume descriptor. */
49
struct grub_iso9660_voldesc
52
grub_uint8_t magic[5];
54
} __attribute__ ((packed));
56
/* A directory entry. */
57
struct grub_iso9660_dir
60
grub_uint8_t ext_sectors;
61
grub_uint32_t first_sector;
62
grub_uint32_t first_sector_be;
64
grub_uint32_t size_be;
65
grub_uint8_t unused1[7];
67
grub_uint8_t unused2[6];
69
} __attribute__ ((packed));
71
struct grub_iso9660_date
74
grub_uint8_t month[2];
77
grub_uint8_t minute[2];
78
grub_uint8_t second[2];
79
grub_uint8_t hundredth[2];
81
} __attribute__ ((packed));
83
/* The primary volume descriptor. Only little endian is used. */
84
struct grub_iso9660_primary_voldesc
86
struct grub_iso9660_voldesc voldesc;
87
grub_uint8_t unused1[33];
88
grub_uint8_t volname[32];
89
grub_uint8_t unused2[16];
90
grub_uint8_t escape[32];
91
grub_uint8_t unused3[12];
92
grub_uint32_t path_table_size;
93
grub_uint8_t unused4[4];
94
grub_uint32_t path_table;
95
grub_uint8_t unused5[12];
96
struct grub_iso9660_dir rootdir;
97
grub_uint8_t unused6[624];
98
struct grub_iso9660_date created;
99
struct grub_iso9660_date modified;
100
} __attribute__ ((packed));
102
/* A single entry in the path table. */
103
struct grub_iso9660_path
106
grub_uint8_t sectors;
107
grub_uint32_t first_sector;
108
grub_uint16_t parentdir;
109
grub_uint8_t name[0];
110
} __attribute__ ((packed));
112
/* An entry in the System Usage area of the directory entry. */
113
struct grub_iso9660_susp_entry
117
grub_uint8_t version;
118
grub_uint8_t data[0];
119
} __attribute__ ((packed));
121
/* The CE entry. This is used to describe the next block where data
123
struct grub_iso9660_susp_ce
125
struct grub_iso9660_susp_entry entry;
127
grub_uint32_t blk_be;
129
grub_uint32_t off_be;
131
grub_uint32_t len_be;
132
} __attribute__ ((packed));
134
struct grub_iso9660_data
136
struct grub_iso9660_primary_voldesc voldesc;
138
unsigned int first_sector;
144
struct grub_fshelp_node
146
struct grub_iso9660_data *data;
149
unsigned int dir_blk;
150
unsigned int dir_off;
153
static grub_dl_t my_mod;
156
/* Iterate over the susp entries, starting with block SUA_BLOCK on the
157
offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for
160
grub_iso9660_susp_iterate (struct grub_iso9660_data *data,
161
int sua_block, int sua_pos, int sua_size,
163
(struct grub_iso9660_susp_entry *entry))
166
struct grub_iso9660_susp_entry *entry;
168
auto grub_err_t load_sua (void);
170
/* Load a part of the System Usage Area. */
171
grub_err_t load_sua (void)
173
sua = grub_malloc (sua_size);
177
if (grub_disk_read (data->disk, sua_block, sua_pos,
181
entry = (struct grub_iso9660_susp_entry *) sua;
188
for (; (char *) entry < (char *) sua + sua_size - 1;
189
entry = (struct grub_iso9660_susp_entry *)
190
((char *) entry + entry->len))
192
/* The last entry. */
193
if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
196
/* Additional entries are stored elsewhere. */
197
if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0)
199
struct grub_iso9660_susp_ce *ce;
201
ce = (struct grub_iso9660_susp_ce *) entry;
202
sua_size = grub_le_to_cpu32 (ce->len);
203
sua_pos = grub_le_to_cpu32 (ce->off);
204
sua_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ;
223
grub_iso9660_convert_string (grub_uint16_t *us, int len)
228
p = grub_malloc (len * 4 + 1);
232
for (i=0; i<len; i++)
233
us[i] = grub_be_to_cpu16 (us[i]);
235
*grub_utf16_to_utf8 ((grub_uint8_t *) p, us, len) = '\0';
240
static struct grub_iso9660_data *
241
grub_iso9660_mount (grub_disk_t disk)
243
struct grub_iso9660_data *data = 0;
244
struct grub_iso9660_dir rootdir;
248
struct grub_iso9660_susp_entry *entry;
249
struct grub_iso9660_primary_voldesc voldesc;
252
auto grub_err_t susp_iterate (struct grub_iso9660_susp_entry *);
254
grub_err_t susp_iterate (struct grub_iso9660_susp_entry *susp_entry)
256
/* The "ER" entry is used to detect extensions. The
257
`IEEE_P1285' extension means Rock ridge. */
258
if (grub_strncmp ((char *) susp_entry->sig, "ER", 2) == 0)
266
data = grub_zalloc (sizeof (struct grub_iso9660_data));
275
int copy_voldesc = 0;
277
/* Read the superblock. */
278
if (grub_disk_read (disk, block << GRUB_ISO9660_LOG2_BLKSZ, 0,
279
sizeof (struct grub_iso9660_primary_voldesc),
282
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
286
if (grub_strncmp ((char *) voldesc.voldesc.magic, "CD001", 5) != 0)
288
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
292
if (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_PRIMARY)
294
else if ((voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_SUPP) &&
295
(voldesc.escape[0] == 0x25) && (voldesc.escape[1] == 0x2f) &&
296
((voldesc.escape[2] == 0x40) || /* UCS-2 Level 1. */
297
(voldesc.escape[2] == 0x43) || /* UCS-2 Level 2. */
298
(voldesc.escape[2] == 0x45))) /* UCS-2 Level 3. */
305
grub_memcpy((char *) &data->voldesc, (char *) &voldesc,
306
sizeof (struct grub_iso9660_primary_voldesc));
309
} while (voldesc.voldesc.type != GRUB_ISO9660_VOLDESC_END);
311
/* Read the system use area and test it to see if SUSP is
313
if (grub_disk_read (disk, (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
314
<< GRUB_ISO9660_LOG2_BLKSZ), 0,
315
sizeof (rootdir), (char *) &rootdir))
317
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
321
sua_pos = (sizeof (rootdir) + rootdir.namelen
322
+ (rootdir.namelen % 2) - 1);
323
sua_size = rootdir.len - sua_pos;
325
sua = grub_malloc (sua_size);
329
if (grub_disk_read (disk, (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
330
<< GRUB_ISO9660_LOG2_BLKSZ), sua_pos,
333
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
337
entry = (struct grub_iso9660_susp_entry *) sua;
339
/* Test if the SUSP protocol is used on this filesystem. */
340
if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0)
342
/* The 2nd data byte stored how many bytes are skipped every time
343
to get to the SUA (System Usage Area). */
344
data->susp_skip = entry->data[2];
345
entry = (struct grub_iso9660_susp_entry *) ((char *) entry + entry->len);
347
/* Iterate over the entries in the SUA area to detect
349
if (grub_iso9660_susp_iterate (data,
350
(grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
351
<< GRUB_ISO9660_LOG2_BLKSZ),
352
sua_pos, sua_size, susp_iterate))
365
grub_iso9660_read_symlink (grub_fshelp_node_t node)
367
struct grub_iso9660_dir dirent;
373
auto void add_part (const char *part, int len);
374
auto grub_err_t susp_iterate_sl (struct grub_iso9660_susp_entry *);
376
/* Extend the symlink. */
377
void add_part (const char *part, int len)
379
int size = grub_strlen (symlink);
381
symlink = grub_realloc (symlink, size + len + 1);
385
grub_strncat (symlink, part, len);
388
/* Read in a symlink. */
389
grub_err_t susp_iterate_sl (struct grub_iso9660_susp_entry *entry)
391
if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0)
393
unsigned int pos = 1;
395
/* The symlink is not stored as a POSIX symlink, translate it. */
396
while (pos < grub_le_to_cpu32 (entry->len))
404
/* The current position is the `Component Flag'. */
405
switch (entry->data[pos] & 30)
409
/* The data on pos + 2 is the actual data, pos + 1
410
is the length. Both are part of the `Component
412
add_part ((char *) &entry->data[pos + 2],
413
entry->data[pos + 1]);
414
if ((entry->data[pos] & 1))
432
/* In pos + 1 the length of the `Component Record' is
434
pos += entry->data[pos + 1] + 2;
437
/* Check if `grub_realloc' failed. */
445
if (grub_disk_read (node->data->disk, node->dir_blk, node->dir_off,
446
sizeof (dirent), (char *) &dirent))
449
sua_off = (sizeof (dirent) + dirent.namelen + 1 - (dirent.namelen % 2)
450
+ node->data->susp_skip);
451
sua_size = dirent.len - sua_off;
453
symlink = grub_malloc (1);
459
if (grub_iso9660_susp_iterate (node->data, node->dir_blk,
460
node->dir_off + sua_off,
461
sua_size, susp_iterate_sl))
472
grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
474
(*hook) (const char *filename,
475
enum grub_fshelp_filetype filetype,
476
grub_fshelp_node_t node))
478
struct grub_iso9660_dir dirent;
479
unsigned int offset = 0;
481
int filename_alloc = 0;
482
enum grub_fshelp_filetype type;
484
auto grub_err_t susp_iterate_dir (struct grub_iso9660_susp_entry *);
486
grub_err_t susp_iterate_dir (struct grub_iso9660_susp_entry *entry)
488
/* The filename in the rock ridge entry. */
489
if (grub_strncmp ("NM", (char *) entry->sig, 2) == 0)
491
/* The flags are stored at the data position 0, here the
492
filename type is stored. */
493
if (entry->data[0] & GRUB_ISO9660_RR_DOT)
495
else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT)
502
size += grub_strlen (filename);
503
grub_realloc (filename,
504
grub_strlen (filename)
509
size = entry->len - 5;
510
filename = grub_zalloc (size + 1);
513
grub_strncpy (filename, (char *) &entry->data[1], size);
514
filename[size] = '\0';
517
/* The mode information (st_mode). */
518
else if (grub_strncmp ((char *) entry->sig, "PX", 2) == 0)
520
/* At position 0 of the PX record the st_mode information is
521
stored (little-endian). */
522
grub_uint32_t mode = ((entry->data[0] + (entry->data[1] << 8))
523
& GRUB_ISO9660_FSTYPE_MASK);
527
case GRUB_ISO9660_FSTYPE_DIR:
528
type = GRUB_FSHELP_DIR;
530
case GRUB_ISO9660_FSTYPE_REG:
531
type = GRUB_FSHELP_REG;
533
case GRUB_ISO9660_FSTYPE_SYMLINK:
534
type = GRUB_FSHELP_SYMLINK;
537
type = GRUB_FSHELP_UNKNOWN;
544
while (offset < dir->size)
546
if (grub_disk_read (dir->data->disk,
547
(dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
548
+ offset / GRUB_DISK_SECTOR_SIZE,
549
offset % GRUB_DISK_SECTOR_SIZE,
550
sizeof (dirent), (char *) &dirent))
553
/* The end of the block, skip to the next one. */
556
offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ;
561
char name[dirent.namelen + 1];
562
int nameoffset = offset + sizeof (dirent);
563
struct grub_fshelp_node *node;
564
int sua_off = (sizeof (dirent) + dirent.namelen + 1
565
- (dirent.namelen % 2));
566
int sua_size = dirent.len - sua_off;
568
sua_off += offset + dir->data->susp_skip;
572
type = GRUB_FSHELP_UNKNOWN;
574
if (dir->data->rockridge
575
&& grub_iso9660_susp_iterate (dir->data,
576
(dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
578
/ GRUB_DISK_SECTOR_SIZE),
579
sua_off % GRUB_DISK_SECTOR_SIZE,
580
sua_size, susp_iterate_dir))
584
if (grub_disk_read (dir->data->disk,
585
(dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
586
+ nameoffset / GRUB_DISK_SECTOR_SIZE,
587
nameoffset % GRUB_DISK_SECTOR_SIZE,
588
dirent.namelen, (char *) name))
591
node = grub_malloc (sizeof (struct grub_fshelp_node));
595
/* Setup a new node. */
596
node->data = dir->data;
597
node->size = grub_le_to_cpu32 (dirent.size);
598
node->blk = grub_le_to_cpu32 (dirent.first_sector);
599
node->dir_blk = ((dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
600
+ offset / GRUB_DISK_SECTOR_SIZE);
601
node->dir_off = offset % GRUB_DISK_SECTOR_SIZE;
603
/* If the filetype was not stored using rockridge, use
604
whatever is stored in the iso9660 filesystem. */
605
if (type == GRUB_FSHELP_UNKNOWN)
607
if ((dirent.flags & 3) == 2)
608
type = GRUB_FSHELP_DIR;
610
type = GRUB_FSHELP_REG;
613
/* The filename was not stored in a rock ridge entry. Read it
614
from the iso9660 filesystem. */
617
name[dirent.namelen] = '\0';
618
filename = grub_strrchr (name, ';');
622
if (dirent.namelen == 1 && name[0] == 0)
624
else if (dirent.namelen == 1 && name[0] == 1)
630
if (dir->data->joliet)
632
char *oldname, *semicolon;
635
filename = grub_iso9660_convert_string
636
((grub_uint16_t *) oldname, dirent.namelen >> 1);
638
semicolon = grub_strrchr (filename, ';');
648
if (hook (filename, type, node))
651
grub_free (filename);
655
grub_free (filename);
658
offset += dirent.len;
667
grub_iso9660_dir (grub_device_t device, const char *path,
668
int (*hook) (const char *filename,
669
const struct grub_dirhook_info *info))
671
struct grub_iso9660_data *data = 0;
672
struct grub_fshelp_node rootnode;
673
struct grub_fshelp_node *foundnode;
675
auto int NESTED_FUNC_ATTR iterate (const char *filename,
676
enum grub_fshelp_filetype filetype,
677
grub_fshelp_node_t node);
679
int NESTED_FUNC_ATTR iterate (const char *filename,
680
enum grub_fshelp_filetype filetype,
681
grub_fshelp_node_t node)
683
struct grub_dirhook_info info;
684
grub_memset (&info, 0, sizeof (info));
685
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
687
return hook (filename, &info);
690
grub_dl_ref (my_mod);
692
data = grub_iso9660_mount (device->disk);
696
rootnode.data = data;
697
rootnode.blk = grub_le_to_cpu32 (data->voldesc.rootdir.first_sector);
698
rootnode.size = grub_le_to_cpu32 (data->voldesc.rootdir.size);
700
/* Use the fshelp function to traverse the path. */
701
if (grub_fshelp_find_file (path, &rootnode,
703
grub_iso9660_iterate_dir,
704
grub_iso9660_read_symlink,
708
/* List the files in the directory. */
709
grub_iso9660_iterate_dir (foundnode, iterate);
711
if (foundnode != &rootnode)
712
grub_free (foundnode);
717
grub_dl_unref (my_mod);
723
/* Open a file named NAME and initialize FILE. */
725
grub_iso9660_open (struct grub_file *file, const char *name)
727
struct grub_iso9660_data *data;
728
struct grub_fshelp_node rootnode;
729
struct grub_fshelp_node *foundnode;
731
grub_dl_ref (my_mod);
733
data = grub_iso9660_mount (file->device->disk);
737
rootnode.data = data;
738
rootnode.blk = grub_le_to_cpu32 (data->voldesc.rootdir.first_sector);
739
rootnode.size = grub_le_to_cpu32 (data->voldesc.rootdir.size);
741
/* Use the fshelp function to traverse the path. */
742
if (grub_fshelp_find_file (name, &rootnode,
744
grub_iso9660_iterate_dir,
745
grub_iso9660_read_symlink,
749
data->first_sector = foundnode->blk;
752
file->size = foundnode->size;
758
grub_dl_unref (my_mod);
767
grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len)
769
struct grub_iso9660_data *data =
770
(struct grub_iso9660_data *) file->data;
772
/* XXX: The file is stored in as a single extent. */
773
data->disk->read_hook = file->read_hook;
774
grub_disk_read (data->disk,
775
data->first_sector << GRUB_ISO9660_LOG2_BLKSZ,
778
data->disk->read_hook = NULL;
788
grub_iso9660_close (grub_file_t file)
790
grub_free (file->data);
792
grub_dl_unref (my_mod);
794
return GRUB_ERR_NONE;
799
grub_iso9660_label (grub_device_t device, char **label)
801
struct grub_iso9660_data *data;
802
data = grub_iso9660_mount (device->disk);
807
*label = grub_iso9660_convert_string
808
((grub_uint16_t *) &data->voldesc.volname, 16);
810
*label = grub_strndup ((char *) data->voldesc.volname, 32);
821
grub_iso9660_uuid (grub_device_t device, char **uuid)
823
struct grub_iso9660_data *data;
824
grub_disk_t disk = device->disk;
826
grub_dl_ref (my_mod);
828
data = grub_iso9660_mount (disk);
831
if (! data->voldesc.modified.year[0] && ! data->voldesc.modified.year[1]
832
&& ! data->voldesc.modified.year[2] && ! data->voldesc.modified.year[3]
833
&& ! data->voldesc.modified.month[0] && ! data->voldesc.modified.month[1]
834
&& ! data->voldesc.modified.day[0] && ! data->voldesc.modified.day[1]
835
&& ! data->voldesc.modified.hour[0] && ! data->voldesc.modified.hour[1]
836
&& ! data->voldesc.modified.minute[0] && ! data->voldesc.modified.minute[1]
837
&& ! data->voldesc.modified.second[0] && ! data->voldesc.modified.second[1]
838
&& ! data->voldesc.modified.hundredth[0] && ! data->voldesc.modified.hundredth[1])
840
grub_error (GRUB_ERR_BAD_NUMBER, "no creation date in filesystem to generate UUID");
845
*uuid = grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
846
data->voldesc.modified.year[0],
847
data->voldesc.modified.year[1],
848
data->voldesc.modified.year[2],
849
data->voldesc.modified.year[3],
850
data->voldesc.modified.month[0],
851
data->voldesc.modified.month[1],
852
data->voldesc.modified.day[0],
853
data->voldesc.modified.day[1],
854
data->voldesc.modified.hour[0],
855
data->voldesc.modified.hour[1],
856
data->voldesc.modified.minute[0],
857
data->voldesc.modified.minute[1],
858
data->voldesc.modified.second[0],
859
data->voldesc.modified.second[1],
860
data->voldesc.modified.hundredth[0],
861
data->voldesc.modified.hundredth[1]);
867
grub_dl_unref (my_mod);
876
static struct grub_fs grub_iso9660_fs =
879
.dir = grub_iso9660_dir,
880
.open = grub_iso9660_open,
881
.read = grub_iso9660_read,
882
.close = grub_iso9660_close,
883
.label = grub_iso9660_label,
884
.uuid = grub_iso9660_uuid,
888
GRUB_MOD_INIT(iso9660)
890
grub_fs_register (&grub_iso9660_fs);
894
GRUB_MOD_FINI(iso9660)
896
grub_fs_unregister (&grub_iso9660_fs);