diff --git a/Makefile.util.def b/Makefile.util.def index 27c48e5..d9f7e64 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -119,6 +119,7 @@ library = { common = grub-core/fs/sfs.c; common = grub-core/fs/squash4.c; common = grub-core/fs/tar.c; + common = grub-core/fs/greffs.c; common = grub-core/fs/udf.c; common = grub-core/fs/ufs2.c; common = grub-core/fs/ufs.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index e5e558c..da3a599 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1400,6 +1400,11 @@ module = { }; module = { + name = greffs; + common = fs/greffs.c; +}; + +module = { name = udf; common = fs/udf.c; }; diff --git a/grub-core/fs/greffs.c b/grub-core/fs/greffs.c new file mode 100644 index 0000000..2cc4efb --- /dev/null +++ b/grub-core/fs/greffs.c @@ -0,0 +1,314 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_greffs_data +{ + grub_uint32_t dofs; +}; + +static grub_err_t +get_string (grub_disk_t disk, + const struct grub_greffs_header *head, + grub_uint32_t fn, + char **buf, grub_size_t *size) +{ + grub_uint32_t desc[2]; + grub_size_t read_size; + + if (grub_disk_read (disk, 0, + grub_cpu_to_le32 (head->string_ptrs_offset) + + fn * sizeof (desc[0]), sizeof (desc), &desc)) + return grub_errno; + read_size = desc[1] - desc[0]; + if (*size < read_size + 1) + { + grub_free (*buf); + *size = (read_size + 4) * 2; + *buf = grub_malloc (*size); + if (!*buf) + { + *size = 0; + return grub_errno; + } + } + if (grub_disk_read (disk, 0, desc[0], read_size, *buf)) + return grub_errno; + (*buf)[read_size] = '\0'; + return GRUB_ERR_NONE; +} + +static grub_err_t +find_file (grub_disk_t disk, + struct grub_greffs_header *head, + const char *name_in, grub_uint32_t *f, int exact) +{ + grub_uint32_t num_files, cur_file = 0; + int i; + char *buf = NULL; + grub_size_t buf_size = 0; + grub_err_t err; + + num_files = grub_le_to_cpu32 (head->nfiles); + + for (i = 31; i >= 0; i--) + { + int cmp; + if ((cur_file | (1 << i)) > num_files) + continue; + err = get_string (disk, head, (cur_file | (1 << i)) - 1, &buf, &buf_size); + if (err) + { + grub_free (buf); + return err; + } + cmp = grub_strcmp (buf, name_in); + if (cmp <= 0) + cur_file |= (1 << i); + if (cmp == 0) + { + grub_free (buf); + *f = cur_file - 1; + return GRUB_ERR_NONE; + } + } + + grub_free (buf); + if (!exact) + { + *f = cur_file; + return GRUB_ERR_NONE; + } + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), + name_in); +} + +static char * +canonicalize (const char *name_in, int make_dir) +{ + const char *iptr; + char *out, *optr; + out = grub_malloc (grub_strlen (name_in) + 2); + if (!out) + return NULL; + for (iptr = name_in, optr = out; *iptr; ) + { + while (*iptr == '/') + iptr++; + if (iptr[0] == '.' && (iptr[1] == '/' || iptr[1] == 0)) + { + iptr += 2; + continue; + } + if (iptr[0] == '.' && iptr[1] == '.' && (iptr[2] == '/' || iptr[2] == 0)) + { + iptr += 3; + if (optr == out) + continue; + for (optr -= 2; optr >= out && *optr != '/'; optr--); + optr++; + continue; + } + while (*iptr && *iptr != '/') + *optr++ = *iptr++; + if (*iptr) + *optr++ = *iptr++; + else if (make_dir && optr != out) + *optr++ = '/'; + } + *optr = 0; + return out; +} + +static grub_err_t +grub_greffs_dir (grub_device_t device, const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + grub_err_t err; + grub_uint32_t cur_file, num_files; + char *buf = 0; + grub_size_t buf_size = 0; + char *can; + grub_size_t len; + struct grub_greffs_header head; + + if (grub_disk_read (device->disk, 0, 0, sizeof (head), &head)) + return grub_error (GRUB_ERR_BAD_FS, "not a greffs filesystem"); + + if (grub_memcmp (head.magic, GRUB_GREFFS_MAGIC, sizeof (head.magic)) != 0) + return grub_error (GRUB_ERR_BAD_FS, "not a greffs filesystem"); + + can = canonicalize (path_in, 1); + if (!can) + return grub_errno; + + if (can[0] == '\0') + cur_file = 0; + else + { + err = find_file (device->disk, &head, can, &cur_file, 0); + if (err) + goto fail; + } + + num_files = grub_le_to_cpu32 (head.nfiles); + + len = grub_strlen (can); + + while (cur_file < num_files) + { + char *p, *n; + struct grub_dirhook_info info; + + err = get_string (device->disk, &head, cur_file, &buf, &buf_size); + if (err) + goto fail; + if (grub_memcmp (can, buf, len) != 0) + break; + grub_memset (&info, 0, sizeof (info)); + + n = buf + len; + while (*n == '/') + n++; + + p = grub_strchr (n, '/'); + if (p) + *p = 0; + info.dir = (p != NULL); + if (hook (n, &info, hook_data)) + goto fail; + if (!p) + cur_file++; + else + { + *p = '/' + 1; + p[1] = '\0'; + err = find_file (device->disk, &head, buf, &cur_file, 0); + if (err) + goto fail; + } + } + + fail: + grub_free (buf); + grub_free (can); + return grub_errno; +} + + +static grub_err_t +grub_greffs_open (grub_file_t file, const char *name_in) +{ + struct grub_greffs_header head; + struct grub_greffs_data *data; + struct grub_greffs_inode inode; + grub_err_t err; + grub_uint32_t cur_file; + char *can; + + if (grub_disk_read (file->device->disk, 0, 0, sizeof (head), &head)) + return grub_error (GRUB_ERR_BAD_FS, "not a greffs filesystem"); + + if (grub_memcmp (head.magic, GRUB_GREFFS_MAGIC, sizeof (head.magic)) != 0) + return grub_error (GRUB_ERR_BAD_FS, "not a greffs filesystem"); + + can = canonicalize (name_in, 0); + if (!can) + return grub_errno; + + err = find_file (file->device->disk, &head, can, &cur_file, 1); + grub_free (can); + if (err) + return err; + + data = grub_malloc (sizeof (*data)); + if (!data) + return grub_errno; + if (grub_disk_read (file->device->disk, + 0, grub_le_to_cpu32 (head.inodes_offset) + + sizeof (inode) * cur_file, sizeof (inode), &inode)) + return grub_error (GRUB_ERR_BAD_FS, "not a greffs filesystem"); + + data->dofs = grub_cpu_to_le32 (inode.start); + file->size = grub_cpu_to_le32 (inode.size); + + file->data = data; + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_greffs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_greffs_data *data; + grub_ssize_t ret; + + data = file->data; + + file->device->disk->read_hook = file->read_hook; + file->device->disk->read_hook_data = file->read_hook_data; + ret = (grub_disk_read (file->device->disk, 0, data->dofs + file->offset, + len, buf)) ? -1 : (grub_ssize_t) len; + file->device->disk->read_hook = 0; + + return ret; +} + +static grub_err_t +grub_greffs_close (grub_file_t file) +{ + struct grub_greffs_data *data; + + data = file->data; + grub_free (data); + + return grub_errno; +} + +static struct grub_fs grub_greffs_fs = { + .name = "greffs", + .dir = grub_greffs_dir, + .open = grub_greffs_open, + .read = grub_greffs_read, + .close = grub_greffs_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif +}; + +GRUB_MOD_INIT (greffs) +{ + grub_fs_register (&grub_greffs_fs); +} + +GRUB_MOD_FINI (greffs) +{ + grub_fs_unregister (&grub_greffs_fs); +} diff --git a/include/grub/greffs.h b/include/grub/greffs.h new file mode 100644 index 0000000..6d54bae --- /dev/null +++ b/include/grub/greffs.h @@ -0,0 +1,55 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GREFFS_HEADER +#define GRUB_GREFFS_HEADER 1 + +#include + +/* + Layout: + header + grub_uint32_t[] pointers to names + inodes + names + contents. + Everything is little-endian. + */ + +struct grub_greffs_header +{ + char magic[4]; +#define GRUB_GREFFS_MAGIC "gref" + grub_uint32_t nfiles; + /* must be divisible by 4. */ + grub_uint32_t inodes_offset; + /* must be divisible by 4. */ + grub_uint32_t string_ptrs_offset; +}; + +struct grub_greffs_inode +{ + grub_uint32_t start; + grub_uint32_t size; + grub_uint32_t mtime; + /* Currently always 0. If we ever need symlinks, + it could be added. */ + grub_uint32_t type; +}; + +#endif diff --git a/util/grub-mkstandalone.c b/util/grub-mkstandalone.c index 8e2a2b8..c7dd054 100644 --- a/util/grub-mkstandalone.c +++ b/util/grub-mkstandalone.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -34,7 +35,6 @@ static char *output_image; static char **files; static int nfiles; const struct grub_install_image_target_desc *format; -static FILE *memdisk; enum { @@ -116,91 +116,56 @@ struct argp argp = { NULL, help_filter, NULL }; -/* tar support */ -#define MAGIC "ustar" -struct head +struct file_desc { - char name[100]; - char mode[8]; - char uid[8]; - char gid[8]; - char size[12]; - char mtime[12]; - char chksum[8]; - char typeflag; - char linkname[100]; - char magic[6]; - char version[2]; - char uname[32]; - char gname[32]; - char devmajor[8]; - char devminor[8]; - char prefix[155]; - char pad[12]; -} GRUB_PACKED; - -static void -write_zeros (unsigned rsz) -{ - char buf[512]; - - memset (buf, 0, 512); - fwrite (buf, 1, rsz, memdisk); -} - -static void -write_pad (unsigned sz) -{ - write_zeros ((~sz + 1) & 511); -} - -static void -set_tar_value (char *field, grub_uint32_t val, - unsigned len) -{ - unsigned i; - for (i = 0; i < len - 1; i++) - field[len - 2 - i] = '0' + ((val >> (3 * i)) & 7); -} + char *name; + char *source; + grub_size_t size; + grub_size_t mtime; +}; +static struct file_desc *file_descs; +static size_t n_file_descs, alloc_file_descs; -static void -compute_checksum (struct head *hd) +static inline void +canonicalize (char *name) { - unsigned int chk = 0; - unsigned char *ptr; - memset (hd->chksum, ' ', 8); - for (ptr = (unsigned char *) hd; ptr < (unsigned char *) (hd + 1); ptr++) - chk += *ptr; - set_tar_value (hd->chksum, chk, 8); + char *iptr, *optr; + for (iptr = name, optr = name; *iptr; ) + { + while (*iptr == '/') + iptr++; + if (iptr[0] == '.' && (iptr[1] == '/' || iptr[1] == 0)) + { + iptr += 2; + continue; + } + if (iptr[0] == '.' && iptr[1] == '.' && (iptr[2] == '/' || iptr[2] == 0)) + { + iptr += 3; + if (optr == name) + continue; + for (optr -= 2; optr >= name && *optr != '/'; optr--); + optr++; + continue; + } + while (*iptr && *iptr != '/') + *optr++ = *iptr++; + if (*iptr) + *optr++ = *iptr++; + } + *optr = 0; } static void add_tar_file (const char *from, const char *to) { - char *tcn; - const char *iptr; - char *optr; - struct head hd; grub_util_fd_t in; - ssize_t r; - grub_uint32_t mtime = 0; - grub_uint32_t size; - - COMPILE_TIME_ASSERT (sizeof (hd) == 512); + size_t idx; if (grub_util_is_special_file (from)) return; - mtime = grub_util_get_mtime (from); - - optr = tcn = xmalloc (strlen (to) + 1); - for (iptr = to; *iptr == '/'; iptr++); - for (; *iptr; iptr++) - if (!(iptr[0] == '/' && iptr[1] == '/')) - *optr++ = *iptr; - *optr = '\0'; - if (grub_util_is_directory (from)) { grub_util_fd_dir_t d; @@ -221,69 +186,138 @@ add_tar_file (const char *from, free (fp); } grub_util_fd_closedir (d); - free (tcn); return; } - if (optr - tcn > 99) + idx = n_file_descs++; + if (idx >= alloc_file_descs) { - memset (&hd, 0, sizeof (hd)); - memcpy (hd.name, tcn, 99); - memcpy (hd.mode, "0000600", 7); - memcpy (hd.uid, "0001750", 7); - memcpy (hd.gid, "0001750", 7); - - set_tar_value (hd.size, optr - tcn, 12); - set_tar_value (hd.mtime, mtime, 12); - hd.typeflag = 'L'; - memcpy (hd.magic, "ustar ", 7); - memcpy (hd.uname, "grub", 4); - memcpy (hd.gname, "grub", 4); - - compute_checksum (&hd); - - fwrite (&hd, 1, sizeof (hd), memdisk); - fwrite (tcn, 1, optr - tcn, memdisk); - - write_pad (optr - tcn); + alloc_file_descs = 2 * n_file_descs; + file_descs = xrealloc (file_descs, alloc_file_descs + * sizeof (file_descs[0])); } in = grub_util_fd_open (from, GRUB_UTIL_FD_O_RDONLY); if (!GRUB_UTIL_FD_IS_VALID (in)) grub_util_error (_("cannot open `%s': %s"), from, grub_util_fd_strerror ()); - if (!grub_install_copy_buffer) - grub_install_copy_buffer = xmalloc (GRUB_INSTALL_COPY_BUFFER_SIZE); + file_descs[idx].name = xstrdup (to); + file_descs[idx].source = xstrdup (from); + canonicalize (file_descs[idx].name); + file_descs[idx].mtime = grub_util_get_mtime (from); + file_descs[idx].size = grub_util_get_fd_size (in, from, NULL); + + grub_util_fd_close (in); +} - size = grub_util_get_fd_size (in, from, NULL); +static int +filecmp (const void *p1, const void *p2) +{ + const struct file_desc *a = p1, *b = p2; - memset (&hd, 0, sizeof (hd)); - memcpy (hd.name, tcn, optr - tcn < 99 ? optr - tcn : 99); - memcpy (hd.mode, "0000600", 7); - memcpy (hd.uid, "0001750", 7); - memcpy (hd.gid, "0001750", 7); + /* Don't use strcmp, it's buggy on some systems. */ + return grub_strcmp (a->name, b->name); +} - set_tar_value (hd.size, size, 12); - set_tar_value (hd.mtime, mtime, 12); - hd.typeflag = '0'; - memcpy (hd.magic, "ustar ", 7); - memcpy (hd.uname, "grub", 4); - memcpy (hd.gname, "grub", 4); +static void +write_memdisk (char *memdisk_img) +{ + FILE *memdisk; + struct grub_greffs_header head; + struct grub_greffs_inode inode; + size_t total_strlen = 0, i; + size_t name_pad = 0; + grub_uint32_t file_offset; + + qsort (file_descs, n_file_descs, sizeof (file_descs[0]), filecmp); + + for (i = 0; i < n_file_descs; i++) + total_strlen += grub_strlen (file_descs[i].name); + name_pad = ALIGN_UP (total_strlen, 4) - total_strlen; + total_strlen += name_pad; + + grub_memcpy (head.magic, GRUB_GREFFS_MAGIC, sizeof (head.magic)); + head.nfiles = grub_cpu_to_le32 (n_file_descs); + head.inodes_offset = grub_cpu_to_le32 (sizeof (head) + + sizeof (grub_uint32_t) + * (n_file_descs + 1)); + head.string_ptrs_offset = grub_cpu_to_le32 (sizeof (head)); - compute_checksum (&hd); + memdisk = grub_util_fopen (memdisk_img, "wb"); + if (!memdisk) + grub_util_error (_("Can't create file: %s"), strerror (errno)); - fwrite (&hd, 1, sizeof (hd), memdisk); - - while (1) + fwrite (&head, 1, sizeof (head), memdisk); + + grub_uint32_t curname = sizeof (head) + sizeof (grub_uint32_t) + * (n_file_descs + 1) + sizeof (inode) * n_file_descs; + for (i = 0; i <= n_file_descs; i++) { - r = grub_util_fd_read (in, grub_install_copy_buffer, GRUB_INSTALL_COPY_BUFFER_SIZE); - if (r <= 0) - break; - fwrite (grub_install_copy_buffer, 1, r, memdisk); + grub_uint32_t curname_le = grub_cpu_to_le32 (curname); + fwrite (&curname_le, 1, sizeof (curname_le), memdisk); + if (i != n_file_descs) + curname += grub_strlen (file_descs[i].name); } - grub_util_fd_close (in); - write_pad (size); + file_offset = sizeof (head) + sizeof (grub_uint32_t) + * (n_file_descs + 1) + sizeof (inode) * n_file_descs + total_strlen; + for (i = 0; i < n_file_descs; i++) + { + inode.start = grub_cpu_to_le32 (file_offset); + inode.size = grub_cpu_to_le32 (file_descs[i].size); + inode.mtime = grub_cpu_to_le32 (file_descs[i].mtime); + inode.type = 0; + fwrite (&inode, 1, sizeof (inode), memdisk); + file_offset += file_descs[i].size; + } + + for (i = 0; i < n_file_descs; i++) + fwrite (file_descs[i].name, 1, grub_strlen (file_descs[i].name), memdisk); + + if (!grub_install_copy_buffer) + grub_install_copy_buffer = xmalloc (GRUB_INSTALL_COPY_BUFFER_SIZE); + + grub_memset (grub_install_copy_buffer, 0, 4); + fwrite (grub_install_copy_buffer, 1, name_pad, memdisk); + + for (i = 0; i < n_file_descs; i++) + { + grub_util_fd_t in; + size_t remaining = file_descs[i].size; + in = grub_util_fd_open (file_descs[i].source, GRUB_UTIL_FD_O_RDONLY); + if (!GRUB_UTIL_FD_IS_VALID (in)) + grub_util_error (_("cannot open `%s': %s"), + file_descs[i].source, grub_util_fd_strerror ()); + + while (remaining) + { + size_t toread = remaining; + ssize_t r; + if (remaining > GRUB_INSTALL_COPY_BUFFER_SIZE) + toread = GRUB_INSTALL_COPY_BUFFER_SIZE; + r = grub_util_fd_read (in, grub_install_copy_buffer, toread); + if (r <= 0) + break; + fwrite (grub_install_copy_buffer, 1, r, memdisk); + if (r >= remaining) + remaining = 0; + else + remaining -= r; + } + grub_util_fd_close (in); + + grub_memset (grub_install_copy_buffer, 0, GRUB_INSTALL_COPY_BUFFER_SIZE); + while (remaining) + { + size_t toread = remaining; + if (remaining > GRUB_INSTALL_COPY_BUFFER_SIZE) + toread = GRUB_INSTALL_COPY_BUFFER_SIZE; + fwrite (grub_install_copy_buffer, 1, toread, memdisk); + remaining -= toread; + } + } + + fclose (memdisk); } int @@ -319,8 +353,6 @@ main (int argc, char *argv[]) char *memdisk_img = grub_util_make_temporary_file (); - memdisk = grub_util_fopen (memdisk_img, "wb"); - add_tar_file (memdisk_dir, ""); for (i = 0; i < nfiles; i++) { @@ -341,14 +373,12 @@ main (int argc, char *argv[]) to++; add_tar_file (from, to); } - write_zeros (512); - - fclose (memdisk); + write_memdisk (memdisk_img); grub_util_unlink_recursive (memdisk_dir); grub_install_push_module ("memdisk"); - grub_install_push_module ("tar"); + grub_install_push_module ("greffs"); grub_install_make_image_wrap (grub_install_source_directory, "(memdisk)/boot/grub", output_image,