2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/file.h>
20 #include <grub/types.h>
23 #include <grub/disk.h>
25 #include <grub/fshelp.h>
27 GRUB_MOD_LICENSE ("GPLv3+");
29 struct grub_romfs_superblock
32 #define GRUB_ROMFS_MAGIC "-rom1fs-"
33 grub_uint32_t total_size
;
38 struct grub_romfs_file_header
40 grub_uint32_t next_file
;
47 struct grub_romfs_data
49 grub_disk_addr_t first_file
;
53 struct grub_fshelp_node
55 grub_disk_addr_t addr
;
56 struct grub_romfs_data
*data
;
57 grub_disk_addr_t data_addr
;
58 /* Not filled for root. */
59 struct grub_romfs_file_header file
;
62 #define GRUB_ROMFS_ALIGN 16
63 #define GRUB_ROMFS_TYPE_MASK 7
64 #define GRUB_ROMFS_TYPE_HARDLINK 0
65 #define GRUB_ROMFS_TYPE_DIRECTORY 1
66 #define GRUB_ROMFS_TYPE_REGULAR 2
67 #define GRUB_ROMFS_TYPE_SYMLINK 3
70 do_checksum (void *in
, grub_size_t insize
)
72 grub_uint32_t
*a
= in
;
73 grub_size_t sz
= insize
/ 4;
74 grub_uint32_t
*b
= a
+ sz
;
75 grub_uint32_t csum
= 0;
78 csum
+= grub_be_to_cpu32 (*a
++);
80 return grub_error (GRUB_ERR_BAD_FS
, "invalid checksum");
84 static struct grub_romfs_data
*
85 grub_romfs_mount (grub_device_t dev
)
88 struct grub_romfs_superblock sb
;
93 grub_disk_addr_t sec
= 0;
94 struct grub_romfs_data
*data
;
97 grub_error (GRUB_ERR_BAD_FS
, "not a disk");
100 err
= grub_disk_read (dev
->disk
, 0, 0, sizeof (sb
), &sb
);
101 if (err
== GRUB_ERR_OUT_OF_RANGE
)
102 err
= grub_errno
= GRUB_ERR_BAD_FS
;
105 if (grub_be_to_cpu32 (sb
.sb
.total_size
) < sizeof (sb
))
107 grub_error (GRUB_ERR_BAD_FS
, "too short filesystem");
110 if (grub_memcmp (sb
.sb
.magic
, GRUB_ROMFS_MAGIC
,
111 sizeof (sb
.sb
.magic
)) != 0)
113 grub_error (GRUB_ERR_BAD_FS
, "not romfs");
116 err
= do_checksum (&sb
, sizeof (sb
) < grub_be_to_cpu32 (sb
.sb
.total_size
) ?
117 sizeof (sb
) : grub_be_to_cpu32 (sb
.sb
.total_size
));
120 grub_error (GRUB_ERR_BAD_FS
, "checksum incorrect");
123 for (ptr
= sb
.sb
.label
; (void *) ptr
< (void *) (&sb
+ 1)
124 && ptr
- sb
.d
< (grub_ssize_t
) grub_be_to_cpu32 (sb
.sb
.total_size
); ptr
++)
127 while ((void *) ptr
== &sb
+ 1)
130 err
= grub_disk_read (dev
->disk
, sec
, 0, sizeof (sb
), &sb
);
131 if (err
== GRUB_ERR_OUT_OF_RANGE
)
132 err
= grub_errno
= GRUB_ERR_BAD_FS
;
135 for (ptr
= sb
.d
; (void *) ptr
< (void *) (&sb
+ 1)
136 && (ptr
- sb
.d
+ (sec
<< GRUB_DISK_SECTOR_BITS
)
137 < grub_be_to_cpu32 (sb
.sb
.total_size
));
142 data
= grub_malloc (sizeof (*data
));
145 data
->first_file
= ALIGN_UP (ptr
+ 1 - sb
.d
, GRUB_ROMFS_ALIGN
)
146 + (sec
<< GRUB_DISK_SECTOR_BITS
);
147 data
->disk
= dev
->disk
;
152 grub_romfs_read_symlink (grub_fshelp_node_t node
)
156 ret
= grub_malloc (grub_be_to_cpu32 (node
->file
.size
) + 1);
159 err
= grub_disk_read (node
->data
->disk
,
160 (node
->data_addr
) >> GRUB_DISK_SECTOR_BITS
,
161 (node
->data_addr
) & (GRUB_DISK_SECTOR_SIZE
- 1),
162 grub_be_to_cpu32 (node
->file
.size
), ret
);
168 ret
[grub_be_to_cpu32 (node
->file
.size
)] = 0;
173 grub_romfs_iterate_dir (grub_fshelp_node_t dir
,
175 (*hook
) (const char *filename
,
176 enum grub_fshelp_filetype filetype
,
177 grub_fshelp_node_t node
))
179 grub_disk_addr_t caddr
;
180 struct grub_romfs_file_header hdr
;
184 grub_properly_aligned_t
*name
= NULL
;
186 for (caddr
= dir
->data_addr
; caddr
;
187 caddr
= grub_be_to_cpu32 (hdr
.next_file
) & ~(GRUB_ROMFS_ALIGN
- 1))
189 grub_disk_addr_t naddr
= caddr
+ sizeof (hdr
);
190 grub_uint32_t csum
= 0;
191 enum grub_fshelp_filetype filetype
= GRUB_FSHELP_UNKNOWN
;
192 struct grub_fshelp_node
*node
= NULL
;
195 err
= grub_disk_read (dir
->data
->disk
, caddr
>> GRUB_DISK_SECTOR_BITS
,
196 caddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
203 for (nptr
= 0; ; nptr
++, naddr
+= 16)
207 grub_properly_aligned_t
*on
;
210 name
= grub_realloc (name
, a
* 16);
217 COMPILE_TIME_ASSERT (16 % sizeof (name
[0]) == 0);
218 err
= grub_disk_read (dir
->data
->disk
, naddr
>> GRUB_DISK_SECTOR_BITS
,
219 naddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
220 16, name
+ (16 / sizeof (name
[0])) * nptr
);
223 for (j
= 0; j
< 16; j
++)
224 if (!((char *) name
)[16 * nptr
+ j
])
229 for (i
= 0; i
< sizeof (hdr
) / sizeof (grub_uint32_t
); i
++)
230 csum
+= grub_be_to_cpu32 (((grub_uint32_t
*) &hdr
)[i
]);
231 for (i
= 0; i
< (nptr
+ 1) * 4; i
++)
232 csum
+= grub_be_to_cpu32 (((grub_uint32_t
*) name
)[i
]);
235 grub_error (GRUB_ERR_BAD_FS
, "invalid checksum");
239 node
= grub_malloc (sizeof (*node
));
243 node
->data_addr
= caddr
+ (nptr
+ 1) * 16 + sizeof (hdr
);
244 node
->data
= dir
->data
;
246 switch (grub_be_to_cpu32 (hdr
.next_file
) & GRUB_ROMFS_TYPE_MASK
)
248 case GRUB_ROMFS_TYPE_REGULAR
:
249 filetype
= GRUB_FSHELP_REG
;
251 case GRUB_ROMFS_TYPE_SYMLINK
:
252 filetype
= GRUB_FSHELP_SYMLINK
;
254 case GRUB_ROMFS_TYPE_DIRECTORY
:
255 node
->data_addr
= grub_be_to_cpu32 (hdr
.spec
);
256 filetype
= GRUB_FSHELP_DIR
;
258 case GRUB_ROMFS_TYPE_HARDLINK
:
260 grub_disk_addr_t laddr
;
261 node
->addr
= laddr
= grub_be_to_cpu32 (hdr
.spec
);
262 err
= grub_disk_read (dir
->data
->disk
,
263 laddr
>> GRUB_DISK_SECTOR_BITS
,
264 laddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
265 sizeof (node
->file
), &node
->file
);
268 if ((grub_be_to_cpu32 (node
->file
.next_file
) & GRUB_ROMFS_TYPE_MASK
)
269 == GRUB_ROMFS_TYPE_REGULAR
270 || (grub_be_to_cpu32 (node
->file
.next_file
)
271 & GRUB_ROMFS_TYPE_MASK
) == GRUB_ROMFS_TYPE_SYMLINK
)
273 laddr
+= sizeof (hdr
);
277 err
= grub_disk_read (dir
->data
->disk
,
278 laddr
>> GRUB_DISK_SECTOR_BITS
,
279 laddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
283 for (i
= 0; i
< 16; i
++)
290 node
->data_addr
= laddr
+ 16;
292 if ((grub_be_to_cpu32 (node
->file
.next_file
)
293 & GRUB_ROMFS_TYPE_MASK
) == GRUB_ROMFS_TYPE_REGULAR
)
294 filetype
= GRUB_FSHELP_REG
;
295 if ((grub_be_to_cpu32 (node
->file
.next_file
)
296 & GRUB_ROMFS_TYPE_MASK
) == GRUB_ROMFS_TYPE_SYMLINK
)
297 filetype
= GRUB_FSHELP_SYMLINK
;
298 if ((grub_be_to_cpu32 (node
->file
.next_file
) & GRUB_ROMFS_TYPE_MASK
)
299 == GRUB_ROMFS_TYPE_DIRECTORY
)
301 node
->data_addr
= grub_be_to_cpu32 (node
->file
.spec
);
302 filetype
= GRUB_FSHELP_DIR
;
309 if (hook ((char *) name
, filetype
, node
))
320 grub_romfs_dir (grub_device_t device
, const char *path
,
321 int (*hook
) (const char *filename
,
322 const struct grub_dirhook_info
*info
))
324 struct grub_romfs_data
*data
= 0;
325 struct grub_fshelp_node
*fdiro
= 0, start
;
327 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
328 enum grub_fshelp_filetype filetype
,
329 grub_fshelp_node_t node
);
331 int NESTED_FUNC_ATTR
iterate (const char *filename
,
332 enum grub_fshelp_filetype filetype
,
333 grub_fshelp_node_t node
)
335 struct grub_dirhook_info info
;
336 grub_memset (&info
, 0, sizeof (info
));
338 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
340 return hook (filename
, &info
);
343 data
= grub_romfs_mount (device
);
347 start
.addr
= data
->first_file
;
348 start
.data_addr
= data
->first_file
;
350 grub_fshelp_find_file (path
, &start
, &fdiro
, grub_romfs_iterate_dir
,
351 grub_romfs_read_symlink
, GRUB_FSHELP_DIR
);
355 grub_romfs_iterate_dir (fdiro
, iterate
);
364 grub_romfs_open (struct grub_file
*file
, const char *name
)
366 struct grub_romfs_data
*data
= 0;
367 struct grub_fshelp_node
*fdiro
= 0, start
;
369 data
= grub_romfs_mount (file
->device
);
373 start
.addr
= data
->first_file
;
374 start
.data_addr
= data
->first_file
;
377 grub_fshelp_find_file (name
, &start
, &fdiro
, grub_romfs_iterate_dir
,
378 grub_romfs_read_symlink
, GRUB_FSHELP_REG
);
382 file
->size
= grub_be_to_cpu32 (fdiro
->file
.size
);
384 return GRUB_ERR_NONE
;
393 grub_romfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
395 struct grub_fshelp_node
*data
= file
->data
;
397 /* XXX: The file is stored in as a single extent. */
398 data
->data
->disk
->read_hook
= file
->read_hook
;
399 grub_disk_read (data
->data
->disk
,
400 (data
->data_addr
+ file
->offset
) >> GRUB_DISK_SECTOR_BITS
,
401 (data
->data_addr
+ file
->offset
) & (GRUB_DISK_SECTOR_SIZE
- 1),
403 data
->data
->disk
->read_hook
= NULL
;
412 grub_romfs_close (grub_file_t file
)
414 struct grub_fshelp_node
*data
= file
->data
;
416 grub_free (data
->data
);
419 return GRUB_ERR_NONE
;
423 grub_romfs_label (grub_device_t device
, char **label
)
425 struct grub_romfs_data
*data
;
430 data
= grub_romfs_mount (device
);
433 *label
= grub_malloc (data
->first_file
+ 1
434 - sizeof (struct grub_romfs_superblock
));
440 err
= grub_disk_read (device
->disk
, 0, sizeof (struct grub_romfs_superblock
),
442 - sizeof (struct grub_romfs_superblock
),
451 (*label
)[data
->first_file
- sizeof (struct grub_romfs_superblock
)] = 0;
453 return GRUB_ERR_NONE
;
457 static struct grub_fs grub_romfs_fs
=
460 .dir
= grub_romfs_dir
,
461 .open
= grub_romfs_open
,
462 .read
= grub_romfs_read
,
463 .close
= grub_romfs_close
,
464 .label
= grub_romfs_label
,
466 .reserved_first_sector
= 0,
467 .blocklist_install
= 0,
474 grub_fs_register (&grub_romfs_fs
);
479 grub_fs_unregister (&grub_romfs_fs
);