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
,
174 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
176 grub_disk_addr_t caddr
;
177 struct grub_romfs_file_header hdr
;
181 grub_properly_aligned_t
*name
= NULL
;
183 for (caddr
= dir
->data_addr
; caddr
;
184 caddr
= grub_be_to_cpu32 (hdr
.next_file
) & ~(GRUB_ROMFS_ALIGN
- 1))
186 grub_disk_addr_t naddr
= caddr
+ sizeof (hdr
);
187 grub_uint32_t csum
= 0;
188 enum grub_fshelp_filetype filetype
= GRUB_FSHELP_UNKNOWN
;
189 struct grub_fshelp_node
*node
= NULL
;
192 err
= grub_disk_read (dir
->data
->disk
, caddr
>> GRUB_DISK_SECTOR_BITS
,
193 caddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
200 for (nptr
= 0; ; nptr
++, naddr
+= 16)
204 grub_properly_aligned_t
*on
;
207 name
= grub_realloc (name
, a
* 16);
214 COMPILE_TIME_ASSERT (16 % sizeof (name
[0]) == 0);
215 err
= grub_disk_read (dir
->data
->disk
, naddr
>> GRUB_DISK_SECTOR_BITS
,
216 naddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
217 16, name
+ (16 / sizeof (name
[0])) * nptr
);
220 for (j
= 0; j
< 16; j
++)
221 if (!((char *) name
)[16 * nptr
+ j
])
226 for (i
= 0; i
< sizeof (hdr
) / sizeof (grub_uint32_t
); i
++)
227 csum
+= grub_be_to_cpu32 (((grub_uint32_t
*) &hdr
)[i
]);
228 for (i
= 0; i
< (nptr
+ 1) * 4; i
++)
229 csum
+= grub_be_to_cpu32 (((grub_uint32_t
*) name
)[i
]);
232 grub_error (GRUB_ERR_BAD_FS
, "invalid checksum");
236 node
= grub_malloc (sizeof (*node
));
240 node
->data_addr
= caddr
+ (nptr
+ 1) * 16 + sizeof (hdr
);
241 node
->data
= dir
->data
;
243 switch (grub_be_to_cpu32 (hdr
.next_file
) & GRUB_ROMFS_TYPE_MASK
)
245 case GRUB_ROMFS_TYPE_REGULAR
:
246 filetype
= GRUB_FSHELP_REG
;
248 case GRUB_ROMFS_TYPE_SYMLINK
:
249 filetype
= GRUB_FSHELP_SYMLINK
;
251 case GRUB_ROMFS_TYPE_DIRECTORY
:
252 node
->data_addr
= grub_be_to_cpu32 (hdr
.spec
);
253 filetype
= GRUB_FSHELP_DIR
;
255 case GRUB_ROMFS_TYPE_HARDLINK
:
257 grub_disk_addr_t laddr
;
258 node
->addr
= laddr
= grub_be_to_cpu32 (hdr
.spec
);
259 err
= grub_disk_read (dir
->data
->disk
,
260 laddr
>> GRUB_DISK_SECTOR_BITS
,
261 laddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
262 sizeof (node
->file
), &node
->file
);
265 if ((grub_be_to_cpu32 (node
->file
.next_file
) & GRUB_ROMFS_TYPE_MASK
)
266 == GRUB_ROMFS_TYPE_REGULAR
267 || (grub_be_to_cpu32 (node
->file
.next_file
)
268 & GRUB_ROMFS_TYPE_MASK
) == GRUB_ROMFS_TYPE_SYMLINK
)
270 laddr
+= sizeof (hdr
);
274 err
= grub_disk_read (dir
->data
->disk
,
275 laddr
>> GRUB_DISK_SECTOR_BITS
,
276 laddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
280 for (i
= 0; i
< 16; i
++)
287 node
->data_addr
= laddr
+ 16;
289 if ((grub_be_to_cpu32 (node
->file
.next_file
)
290 & GRUB_ROMFS_TYPE_MASK
) == GRUB_ROMFS_TYPE_REGULAR
)
291 filetype
= GRUB_FSHELP_REG
;
292 if ((grub_be_to_cpu32 (node
->file
.next_file
)
293 & GRUB_ROMFS_TYPE_MASK
) == GRUB_ROMFS_TYPE_SYMLINK
)
294 filetype
= GRUB_FSHELP_SYMLINK
;
295 if ((grub_be_to_cpu32 (node
->file
.next_file
) & GRUB_ROMFS_TYPE_MASK
)
296 == GRUB_ROMFS_TYPE_DIRECTORY
)
298 node
->data_addr
= grub_be_to_cpu32 (node
->file
.spec
);
299 filetype
= GRUB_FSHELP_DIR
;
306 if (hook ((char *) name
, filetype
, node
, hook_data
))
316 /* Context for grub_romfs_dir. */
317 struct grub_romfs_dir_ctx
319 grub_fs_dir_hook_t hook
;
323 /* Helper for grub_romfs_dir. */
325 grub_romfs_dir_iter (const char *filename
, enum grub_fshelp_filetype filetype
,
326 grub_fshelp_node_t node
, void *data
)
328 struct grub_romfs_dir_ctx
*ctx
= data
;
329 struct grub_dirhook_info info
;
331 grub_memset (&info
, 0, sizeof (info
));
333 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
335 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
339 grub_romfs_dir (grub_device_t device
, const char *path
,
340 grub_fs_dir_hook_t hook
, void *hook_data
)
342 struct grub_romfs_dir_ctx ctx
= { hook
, hook_data
};
343 struct grub_romfs_data
*data
= 0;
344 struct grub_fshelp_node
*fdiro
= 0, start
;
346 data
= grub_romfs_mount (device
);
350 start
.addr
= data
->first_file
;
351 start
.data_addr
= data
->first_file
;
353 grub_fshelp_find_file (path
, &start
, &fdiro
, grub_romfs_iterate_dir
,
354 grub_romfs_read_symlink
, GRUB_FSHELP_DIR
);
358 grub_romfs_iterate_dir (fdiro
, grub_romfs_dir_iter
, &ctx
);
367 grub_romfs_open (struct grub_file
*file
, const char *name
)
369 struct grub_romfs_data
*data
= 0;
370 struct grub_fshelp_node
*fdiro
= 0, start
;
372 data
= grub_romfs_mount (file
->device
);
376 start
.addr
= data
->first_file
;
377 start
.data_addr
= data
->first_file
;
380 grub_fshelp_find_file (name
, &start
, &fdiro
, grub_romfs_iterate_dir
,
381 grub_romfs_read_symlink
, GRUB_FSHELP_REG
);
385 file
->size
= grub_be_to_cpu32 (fdiro
->file
.size
);
387 return GRUB_ERR_NONE
;
396 grub_romfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
398 struct grub_fshelp_node
*data
= file
->data
;
400 /* XXX: The file is stored in as a single extent. */
401 data
->data
->disk
->read_hook
= file
->read_hook
;
402 data
->data
->disk
->read_hook_data
= file
->read_hook_data
;
403 grub_disk_read (data
->data
->disk
,
404 (data
->data_addr
+ file
->offset
) >> GRUB_DISK_SECTOR_BITS
,
405 (data
->data_addr
+ file
->offset
) & (GRUB_DISK_SECTOR_SIZE
- 1),
407 data
->data
->disk
->read_hook
= NULL
;
416 grub_romfs_close (grub_file_t file
)
418 struct grub_fshelp_node
*data
= file
->data
;
420 grub_free (data
->data
);
423 return GRUB_ERR_NONE
;
427 grub_romfs_label (grub_device_t device
, char **label
)
429 struct grub_romfs_data
*data
;
434 data
= grub_romfs_mount (device
);
437 *label
= grub_malloc (data
->first_file
+ 1
438 - sizeof (struct grub_romfs_superblock
));
444 err
= grub_disk_read (device
->disk
, 0, sizeof (struct grub_romfs_superblock
),
446 - sizeof (struct grub_romfs_superblock
),
455 (*label
)[data
->first_file
- sizeof (struct grub_romfs_superblock
)] = 0;
457 return GRUB_ERR_NONE
;
461 static struct grub_fs grub_romfs_fs
=
464 .dir
= grub_romfs_dir
,
465 .open
= grub_romfs_open
,
466 .read
= grub_romfs_read
,
467 .close
= grub_romfs_close
,
468 .label
= grub_romfs_label
,
470 .reserved_first_sector
= 0,
471 .blocklist_install
= 0,
478 grub_fs_register (&grub_romfs_fs
);
483 grub_fs_unregister (&grub_romfs_fs
);