From 332fa368872a57f945a7387cc1b931e83f197cb0 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 8 Dec 2010 21:22:46 +0100 Subject: [PATCH 01/17] some squash4 code. Works only in very easy cases. --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 5 + grub-core/fs/squash4.c | 384 ++++++++++++++++++++++++++++++++++++ 3 files changed, 390 insertions(+) create mode 100644 grub-core/fs/squash4.c diff --git a/Makefile.util.def b/Makefile.util.def index f3cd1d234..8eec8d0f7 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -65,6 +65,7 @@ library = { common = grub-core/fs/ntfscomp.c; common = grub-core/fs/reiserfs.c; common = grub-core/fs/sfs.c; + common = grub-core/fs/squash4.c; common = grub-core/fs/tar.c; common = grub-core/fs/udf.c; common = grub-core/fs/ufs2.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index c62d7d12f..583218372 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -983,6 +983,11 @@ module = { common = fs/sfs.c; }; +module = { + name = squash4; + common = fs/squash4.c; +}; + module = { name = tar; common = fs/tar.c; diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c new file mode 100644 index 000000000..889f8b94c --- /dev/null +++ b/grub-core/fs/squash4.c @@ -0,0 +1,384 @@ +/* squash4.c - SquashFS */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + object format Pointed by + superblock RAW Fixed offset (0) + data RAW ? Fixed offset (60) + inode table Chunk superblock + dir table Chunk superblock + unk3 Chunk unk1 + unk1 RAW, Chunk superblock + unk2 RAW superblock + UID/GID Chunk exttblptr + exttblptr RAW superblock + + UID/GID table is the array ot uint32_t + unk1 contains pointer to unk3 followed by some chunk. + unk2 containts one uint64_t +*/ + +struct grub_squash_super +{ + grub_uint32_t magic; +#define SQUASH_MAGIC 0x73717368 + grub_uint32_t dummy1; + grub_uint32_t creation_time; + grub_uint32_t dummy2; + grub_uint32_t dummy3[4]; + grub_uint32_t dummy4[2]; + grub_uint64_t total_size; + grub_uint64_t exttbloffset; + grub_uint32_t dummy5[2]; + grub_uint64_t inodeoffset; + grub_uint64_t diroffset; + grub_uint64_t unk1offset; + grub_uint64_t unk2offset; +}; + + +/* Chunk-based */ +struct grub_squash_inode +{ + /* Same values as direlem types. */ + grub_uint16_t type; + grub_uint16_t dummy1[3]; + grub_uint32_t mtime; + grub_uint16_t dummy2[6]; + grub_uint32_t offset; + grub_uint16_t size; + grub_uint16_t dummy3; +} __attribute__ ((packed)); + +/* Chunk-based. */ +struct grub_squash_dirent_header +{ + /* Actually the value is the number of elements - 1. */ + grub_uint16_t nelems; + grub_uint16_t dummy[5]; +}; + +struct grub_squash_dirent +{ + grub_uint16_t ino; + grub_uint16_t dummy; + grub_uint16_t type; +#define SQUASH_TYPE_DIR 1 +#define SQUASH_TYPE_REGULAR 2 + /* Actually the value is the length of name - 1. */ + grub_uint16_t namelen; + char name[0]; +}; + +#define SQUASH_CHUNK_SIZE 0x2000 +#define SQUASH_CHUNK_FLAGS 0x8000 +#define SQUASH_CHUNK_UNCOMPRESSED 0x8000 + +static grub_err_t +read_chunk (grub_disk_t disk, void *buf, grub_size_t len, + grub_uint64_t chunk_start, grub_uint64_t offset) +{ + while (len > 0) + { + grub_uint64_t csize; + grub_uint16_t d; + grub_err_t err; + while (1) + { + err = grub_disk_read (disk, chunk_start >> GRUB_DISK_SECTOR_BITS, + chunk_start & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (d), &d); + if (err) + return err; + if (offset < SQUASH_CHUNK_SIZE) + break; + offset -= SQUASH_CHUNK_SIZE; + chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS); + } + + csize = SQUASH_CHUNK_SIZE - offset; + if (csize > len) + csize = len; + + if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED) + { + grub_disk_addr_t a = chunk_start + 2 + offset; + err = grub_disk_read (disk, (a >> GRUB_DISK_SECTOR_BITS), + a & (GRUB_DISK_SECTOR_SIZE - 1), + csize, buf); + if (err) + return err; + } + else + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "compression not implemented yet"); + } + len -= csize; + offset += csize; + buf = (char *) buf + csize; + } + return GRUB_ERR_NONE; +} + +struct grub_squash_data +{ + grub_disk_t disk; + struct grub_squash_super sb; + struct grub_squash_inode ino; +}; + +struct grub_fshelp_node +{ + struct grub_squash_data *data; + struct grub_squash_inode ino; +}; + +static struct grub_squash_data * +squash_mount (grub_disk_t disk) +{ + struct grub_squash_super sb; + grub_err_t err; + struct grub_squash_data *data; + + err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb); + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_error (GRUB_ERR_BAD_FS, "not a squash4"); + if (err) + return NULL; + if (grub_le_to_cpu32 (sb.magic) != SQUASH_MAGIC) + { + grub_error (GRUB_ERR_BAD_FS, "not squash4"); + return NULL; + } + data = grub_malloc (sizeof (*data)); + if (!data) + return NULL; + data->sb = sb; + data->disk = disk; + + return data; +} + +static int +grub_squash_iterate_dir (grub_fshelp_node_t dir, + int NESTED_FUNC_ATTR + (*hook) (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node)) +{ + grub_uint32_t off = grub_le_to_cpu32 (dir->ino.offset) >> 16; + /* FIXME: determine this. */ + unsigned numheaders = 1; + unsigned i, j; + for (j = 0; j < numheaders; j++) + { + struct grub_squash_dirent_header dh; + grub_err_t err; + + err = read_chunk (dir->data->disk, &dh, sizeof (dh), + grub_le_to_cpu64 (dir->data->sb.diroffset), off); + if (err) + return 0; + off += sizeof (dh); + for (i = 0; i < (unsigned) grub_le_to_cpu16 (dh.nelems) + 1; i++) + { + char *buf; + int r; + struct grub_fshelp_node *node; + enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG; + struct grub_squash_dirent di; + struct grub_squash_inode ino; + + err = read_chunk (dir->data->disk, &di, sizeof (di), + grub_le_to_cpu64 (dir->data->sb.diroffset), off); + if (err) + return 0; + off += sizeof (di); + + err = read_chunk (dir->data->disk, &ino, sizeof (ino), + grub_le_to_cpu64 (dir->data->sb.inodeoffset), + grub_le_to_cpu16 (di.ino)); + if (err) + return 0; + + buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2); + if (!buf) + return 0; + err = read_chunk (dir->data->disk, buf, + grub_le_to_cpu16 (di.namelen) + 1, + grub_le_to_cpu64 (dir->data->sb.diroffset), off); + if (err) + return 0; + + off += grub_le_to_cpu16 (di.namelen) + 1; + buf[grub_le_to_cpu16 (di.namelen) + 1] = 0; + if (grub_le_to_cpu16 (ino.type) == SQUASH_TYPE_DIR) + filetype = GRUB_FSHELP_DIR; + node = grub_malloc (sizeof (*node)); + if (! node) + return 0; + *node = *dir; + node->ino = ino; + r = hook (buf, filetype, node); + + grub_free (buf); + if (r) + return r; + } + } + return 0; +} + +static grub_err_t +make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root) +{ + /* FIXME */ + grub_memset (root, 0, sizeof (*root)); + root->data = data; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_squash_dir (grub_device_t device, const char *path, + int (*hook) (const char *filename, + const struct grub_dirhook_info *info)) +{ + auto int NESTED_FUNC_ATTR iterate (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node); + + int NESTED_FUNC_ATTR iterate (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node __attribute__ ((unused))) + { + struct grub_dirhook_info info; + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + return hook (filename, &info); + } + + struct grub_squash_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + struct grub_fshelp_node root; + grub_err_t err; + + data = squash_mount (device->disk); + if (! data) + return grub_errno; + + err = make_root_node (data, &root); + if (err) + return err; + + grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir, + NULL, GRUB_FSHELP_DIR); + if (!grub_errno) + grub_squash_iterate_dir (fdiro, iterate); + + grub_free (data); + + return grub_errno; +} + +static grub_err_t +grub_squash_open (struct grub_file *file, const char *name) +{ + struct grub_squash_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + struct grub_fshelp_node root; + grub_err_t err; + + data = squash_mount (file->device->disk); + if (! data) + return grub_errno; + + err = make_root_node (data, &root); + if (err) + return err; + + grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir, + NULL, GRUB_FSHELP_REG); + if (grub_errno) + { + grub_free (data); + return grub_errno; + } + file->data = data; + data->ino = fdiro->ino; + file->size = grub_le_to_cpu16 (fdiro->ino.size); + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_squash_read (grub_file_t file, char *buf, grub_size_t len) +{ + grub_err_t err; + grub_uint64_t a; + struct grub_squash_data *data = file->data; + + a = sizeof (struct grub_squash_super) + grub_le_to_cpu32 (data->ino.offset); + + err = grub_disk_read (file->device->disk, a >> GRUB_DISK_SECTOR_BITS, + a & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); + if (err) + return -1; + return len; +} + +static grub_err_t +grub_squash_close (grub_file_t file) +{ + grub_free (file->data); + return GRUB_ERR_NONE; +} + +static struct grub_fs grub_squash_fs = + { + .name = "squash4", + .dir = grub_squash_dir, + .open = grub_squash_open, + .read = grub_squash_read, + .close = grub_squash_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, +#endif + .next = 0 + }; + +GRUB_MOD_INIT(squash4) +{ + grub_fs_register (&grub_squash_fs); +} + +GRUB_MOD_FINI(squash4) +{ + grub_fs_unregister (&grub_squash_fs); +} + From 1deadc83f55187f1ba889d6533341850cb3a5f50 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 8 Dec 2010 23:03:59 +0100 Subject: [PATCH 02/17] extend size to 32-bit and handle file->offset --- grub-core/fs/squash4.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 889f8b94c..f3ac1f474 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -72,8 +72,7 @@ struct grub_squash_inode grub_uint32_t mtime; grub_uint16_t dummy2[6]; grub_uint32_t offset; - grub_uint16_t size; - grub_uint16_t dummy3; + grub_uint32_t size; } __attribute__ ((packed)); /* Chunk-based. */ @@ -330,9 +329,10 @@ grub_squash_open (struct grub_file *file, const char *name) grub_free (data); return grub_errno; } + file->data = data; data->ino = fdiro->ino; - file->size = grub_le_to_cpu16 (fdiro->ino.size); + file->size = grub_le_to_cpu32 (fdiro->ino.size); return GRUB_ERR_NONE; } @@ -343,7 +343,8 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) grub_uint64_t a; struct grub_squash_data *data = file->data; - a = sizeof (struct grub_squash_super) + grub_le_to_cpu32 (data->ino.offset); + a = sizeof (struct grub_squash_super) + grub_le_to_cpu32 (data->ino.offset) + + file->offset; err = grub_disk_read (file->device->disk, a >> GRUB_DISK_SECTOR_BITS, a & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); From 0a040470e53c4620d93249d5aba40097c93740d8 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 00:04:36 +0100 Subject: [PATCH 03/17] determine root inode on squash4fs --- grub-core/fs/squash4.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index f3ac1f474..bd50265a8 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -52,7 +52,8 @@ struct grub_squash_super grub_uint32_t creation_time; grub_uint32_t dummy2; grub_uint32_t dummy3[4]; - grub_uint32_t dummy4[2]; + grub_uint32_t root_ino; + grub_uint32_t dummy4; grub_uint64_t total_size; grub_uint64_t exttbloffset; grub_uint32_t dummy5[2]; @@ -258,10 +259,12 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, static grub_err_t make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root) { - /* FIXME */ grub_memset (root, 0, sizeof (*root)); root->data = data; - return GRUB_ERR_NONE; + + return read_chunk (data->disk, &root->ino, sizeof (root->ino), + grub_le_to_cpu64 (data->sb.inodeoffset), + grub_le_to_cpu32 (data->sb.root_ino)); } static grub_err_t From 7ed6c3e85f51a59d0758de6363d7acd9804c2e21 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 02:48:59 +0100 Subject: [PATCH 04/17] Honor chunk number for directories --- grub-core/fs/squash4.c | 48 ++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index bd50265a8..1622d3736 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -52,7 +52,8 @@ struct grub_squash_super grub_uint32_t creation_time; grub_uint32_t dummy2; grub_uint32_t dummy3[4]; - grub_uint32_t root_ino; + grub_uint16_t root_ino_offset; + grub_uint16_t root_ino_chunk; grub_uint32_t dummy4; grub_uint64_t total_size; grub_uint64_t exttbloffset; @@ -71,11 +72,19 @@ struct grub_squash_inode grub_uint16_t type; grub_uint16_t dummy1[3]; grub_uint32_t mtime; - grub_uint16_t dummy2[6]; + grub_uint16_t dummy2[2]; + grub_uint16_t chunk; + grub_uint16_t dummy3[3]; grub_uint32_t offset; grub_uint32_t size; } __attribute__ ((packed)); +static inline unsigned +decode_chunk_number (grub_uint16_t in) +{ + return (grub_le_to_cpu16 (in) & 0xff) / 2; +} + /* Chunk-based. */ struct grub_squash_dirent_header { @@ -102,8 +111,10 @@ struct grub_squash_dirent static grub_err_t read_chunk (grub_disk_t disk, void *buf, grub_size_t len, - grub_uint64_t chunk_start, grub_uint64_t offset) + grub_uint64_t chunk, unsigned nchunk, grub_off_t offset) { + grub_uint64_t chunk_start; + chunk_start = grub_le_to_cpu64 (chunk); while (len > 0) { grub_uint64_t csize; @@ -116,9 +127,12 @@ read_chunk (grub_disk_t disk, void *buf, grub_size_t len, sizeof (d), &d); if (err) return err; - if (offset < SQUASH_CHUNK_SIZE) + if (!nchunk && offset < SQUASH_CHUNK_SIZE) break; - offset -= SQUASH_CHUNK_SIZE; + if (nchunk) + nchunk--; + else + offset -= SQUASH_CHUNK_SIZE; chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS); } @@ -197,13 +211,15 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, /* FIXME: determine this. */ unsigned numheaders = 1; unsigned i, j; + for (j = 0; j < numheaders; j++) { struct grub_squash_dirent_header dh; grub_err_t err; err = read_chunk (dir->data->disk, &dh, sizeof (dh), - grub_le_to_cpu64 (dir->data->sb.diroffset), off); + dir->data->sb.diroffset, + decode_chunk_number (dir->ino.chunk), off); if (err) return 0; off += sizeof (dh); @@ -217,14 +233,16 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, struct grub_squash_inode ino; err = read_chunk (dir->data->disk, &di, sizeof (di), - grub_le_to_cpu64 (dir->data->sb.diroffset), off); + dir->data->sb.diroffset, + decode_chunk_number (dir->ino.chunk), off); if (err) return 0; off += sizeof (di); err = read_chunk (dir->data->disk, &ino, sizeof (ino), - grub_le_to_cpu64 (dir->data->sb.inodeoffset), - grub_le_to_cpu16 (di.ino)); + dir->data->sb.inodeoffset, + 0, + grub_cpu_to_le16 (di.ino)); if (err) return 0; @@ -233,7 +251,8 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, return 0; err = read_chunk (dir->data->disk, buf, grub_le_to_cpu16 (di.namelen) + 1, - grub_le_to_cpu64 (dir->data->sb.diroffset), off); + dir->data->sb.diroffset, + decode_chunk_number (dir->ino.chunk), off); if (err) return 0; @@ -261,10 +280,11 @@ make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root) { grub_memset (root, 0, sizeof (*root)); root->data = data; - - return read_chunk (data->disk, &root->ino, sizeof (root->ino), - grub_le_to_cpu64 (data->sb.inodeoffset), - grub_le_to_cpu32 (data->sb.root_ino)); + + return read_chunk (data->disk, &root->ino, sizeof (root->ino), + data->sb.inodeoffset, + decode_chunk_number (data->sb.root_ino_chunk), + grub_cpu_to_le16 (data->sb.root_ino_offset)); } static grub_err_t From 948ebd7e91f29efea22bc4987582efb827940294 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 17:06:49 +0100 Subject: [PATCH 05/17] Support fragments and chunks for data --- grub-core/fs/squash4.c | 91 +++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 1622d3736..6fc2bc11b 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -28,16 +28,16 @@ #include /* - object format Pointed by - superblock RAW Fixed offset (0) - data RAW ? Fixed offset (60) - inode table Chunk superblock - dir table Chunk superblock - unk3 Chunk unk1 - unk1 RAW, Chunk superblock - unk2 RAW superblock - UID/GID Chunk exttblptr - exttblptr RAW superblock + object format Pointed by + superblock RAW Fixed offset (0) + data RAW ? Fixed offset (60) + inode table Chunk superblock + dir table Chunk superblock + fragment table Chunk unk1 + unk1 RAW, Chunk superblock + unk2 RAW superblock + UID/GID Chunk exttblptr + exttblptr RAW superblock UID/GID table is the array ot uint32_t unk1 contains pointer to unk3 followed by some chunk. @@ -73,8 +73,8 @@ struct grub_squash_inode grub_uint16_t dummy1[3]; grub_uint32_t mtime; grub_uint16_t dummy2[2]; - grub_uint16_t chunk; - grub_uint16_t dummy3[3]; + grub_uint32_t chunk; + grub_uint32_t fragment; grub_uint32_t offset; grub_uint32_t size; } __attribute__ ((packed)); @@ -105,10 +105,30 @@ struct grub_squash_dirent char name[0]; }; +struct grub_squash_frag_desc +{ + grub_uint64_t offset; + grub_uint64_t dummy; +}; + #define SQUASH_CHUNK_SIZE 0x2000 #define SQUASH_CHUNK_FLAGS 0x8000 #define SQUASH_CHUNK_UNCOMPRESSED 0x8000 +struct grub_squash_data +{ + grub_disk_t disk; + struct grub_squash_super sb; + struct grub_squash_inode ino; + grub_uint64_t fragments; +}; + +struct grub_fshelp_node +{ + struct grub_squash_data *data; + struct grub_squash_inode ino; +}; + static grub_err_t read_chunk (grub_disk_t disk, void *buf, grub_size_t len, grub_uint64_t chunk, unsigned nchunk, grub_off_t offset) @@ -161,25 +181,13 @@ read_chunk (grub_disk_t disk, void *buf, grub_size_t len, return GRUB_ERR_NONE; } -struct grub_squash_data -{ - grub_disk_t disk; - struct grub_squash_super sb; - struct grub_squash_inode ino; -}; - -struct grub_fshelp_node -{ - struct grub_squash_data *data; - struct grub_squash_inode ino; -}; - static struct grub_squash_data * squash_mount (grub_disk_t disk) { struct grub_squash_super sb; grub_err_t err; struct grub_squash_data *data; + grub_uint64_t frag; err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb); if (grub_errno == GRUB_ERR_OUT_OF_RANGE) @@ -191,11 +199,22 @@ squash_mount (grub_disk_t disk) grub_error (GRUB_ERR_BAD_FS, "not squash4"); return NULL; } + + err = grub_disk_read (disk, grub_le_to_cpu32 (sb.unk1offset) + >> GRUB_DISK_SECTOR_BITS, + grub_le_to_cpu32 (sb.unk1offset) + & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag); + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_error (GRUB_ERR_BAD_FS, "not a squash4"); + if (err) + return NULL; + data = grub_malloc (sizeof (*data)); if (!data) return NULL; data->sb = sb; data->disk = disk; + data->fragments = frag; return data; } @@ -356,6 +375,7 @@ grub_squash_open (struct grub_file *file, const char *name) file->data = data; data->ino = fdiro->ino; file->size = grub_le_to_cpu32 (fdiro->ino.size); + return GRUB_ERR_NONE; } @@ -366,8 +386,25 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) grub_uint64_t a; struct grub_squash_data *data = file->data; - a = sizeof (struct grub_squash_super) + grub_le_to_cpu32 (data->ino.offset) - + file->offset; + a = grub_le_to_cpu32 (data->ino.offset) + file->offset; + if (grub_le_to_cpu16 (data->ino.fragment) == 0xffff) + { + if (grub_le_to_cpu32 (data->ino.chunk)) + a += grub_le_to_cpu32 (data->ino.chunk); + else + a += sizeof (struct grub_squash_super); + } + else + { + struct grub_squash_frag_desc frag; + err = read_chunk (file->device->disk, &frag, sizeof (frag), + data->fragments, 0, sizeof (frag) + * grub_le_to_cpu16 (data->ino.fragment)); + if (err) + return -1; + a += grub_le_to_cpu64 (frag.offset); + a += grub_le_to_cpu32 (data->ino.chunk); + } err = grub_disk_read (file->device->disk, a >> GRUB_DISK_SECTOR_BITS, a & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); From 99f0735fb8f961f4281eae3c76366f2eb8afdad2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 18:22:38 +0100 Subject: [PATCH 06/17] Support multi-header directories --- grub-core/fs/squash4.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 6fc2bc11b..5bb128f58 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -227,11 +227,13 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, grub_fshelp_node_t node)) { grub_uint32_t off = grub_le_to_cpu32 (dir->ino.offset) >> 16; - /* FIXME: determine this. */ - unsigned numheaders = 1; - unsigned i, j; + grub_uint32_t endoff; + unsigned i; - for (j = 0; j < numheaders; j++) + /* FIXME: why - 3 ? */ + endoff = (grub_le_to_cpu32 (dir->ino.offset) & 0xffff) + off - 3; + + while (off < endoff) { struct grub_squash_dirent_header dh; grub_err_t err; From 76e39dc8711ce85d43fa06a0a4ee9e0683091d45 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 19:23:35 +0100 Subject: [PATCH 07/17] Compressed metadata support --- grub-core/fs/squash4.c | 74 +++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 5bb128f58..8eca185f3 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -79,23 +79,19 @@ struct grub_squash_inode grub_uint32_t size; } __attribute__ ((packed)); -static inline unsigned -decode_chunk_number (grub_uint16_t in) -{ - return (grub_le_to_cpu16 (in) & 0xff) / 2; -} - /* Chunk-based. */ struct grub_squash_dirent_header { /* Actually the value is the number of elements - 1. */ grub_uint16_t nelems; - grub_uint16_t dummy[5]; + grub_uint16_t dummy1; + grub_uint32_t ino_chunk; + grub_uint16_t dummy2[2]; }; struct grub_squash_dirent { - grub_uint16_t ino; + grub_uint16_t ino_offset; grub_uint16_t dummy; grub_uint16_t type; #define SQUASH_TYPE_DIR 1 @@ -131,7 +127,7 @@ struct grub_fshelp_node static grub_err_t read_chunk (grub_disk_t disk, void *buf, grub_size_t len, - grub_uint64_t chunk, unsigned nchunk, grub_off_t offset) + grub_uint64_t chunk, grub_off_t offset) { grub_uint64_t chunk_start; chunk_start = grub_le_to_cpu64 (chunk); @@ -147,12 +143,9 @@ read_chunk (grub_disk_t disk, void *buf, grub_size_t len, sizeof (d), &d); if (err) return err; - if (!nchunk && offset < SQUASH_CHUNK_SIZE) + if (offset < SQUASH_CHUNK_SIZE) break; - if (nchunk) - nchunk--; - else - offset -= SQUASH_CHUNK_SIZE; + offset -= SQUASH_CHUNK_SIZE; chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS); } @@ -171,8 +164,29 @@ read_chunk (grub_disk_t disk, void *buf, grub_size_t len, } else { - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "compression not implemented yet"); + char *tmp; + grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS; + grub_disk_addr_t a = chunk_start + 2; + tmp = grub_malloc (bsize); + if (!tmp) + return grub_errno; + /* FIXME: buffer uncompressed data. */ + err = grub_disk_read (disk, (a >> GRUB_DISK_SECTOR_BITS), + a & (GRUB_DISK_SECTOR_SIZE - 1), + bsize, tmp); + if (err) + { + grub_free (tmp); + return err; + } + + if (grub_zlib_decompress (tmp, bsize, offset, + buf, csize) < 0) + { + grub_free (tmp); + return grub_errno; + } + grub_free (tmp); } len -= csize; offset += csize; @@ -239,8 +253,8 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, grub_err_t err; err = read_chunk (dir->data->disk, &dh, sizeof (dh), - dir->data->sb.diroffset, - decode_chunk_number (dir->ino.chunk), off); + grub_le_to_cpu64 (dir->data->sb.diroffset) + + grub_le_to_cpu32 (dir->ino.chunk), off); if (err) return 0; off += sizeof (dh); @@ -254,16 +268,16 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, struct grub_squash_inode ino; err = read_chunk (dir->data->disk, &di, sizeof (di), - dir->data->sb.diroffset, - decode_chunk_number (dir->ino.chunk), off); + grub_le_to_cpu64 (dir->data->sb.diroffset) + + grub_le_to_cpu32 (dir->ino.chunk), off); if (err) return 0; off += sizeof (di); err = read_chunk (dir->data->disk, &ino, sizeof (ino), - dir->data->sb.inodeoffset, - 0, - grub_cpu_to_le16 (di.ino)); + grub_le_to_cpu64 (dir->data->sb.inodeoffset) + + grub_le_to_cpu32 (dh.ino_chunk), + grub_cpu_to_le16 (di.ino_offset)); if (err) return 0; @@ -272,14 +286,14 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, return 0; err = read_chunk (dir->data->disk, buf, grub_le_to_cpu16 (di.namelen) + 1, - dir->data->sb.diroffset, - decode_chunk_number (dir->ino.chunk), off); + grub_le_to_cpu64 (dir->data->sb.diroffset) + + grub_le_to_cpu32 (dir->ino.chunk), off); if (err) return 0; off += grub_le_to_cpu16 (di.namelen) + 1; buf[grub_le_to_cpu16 (di.namelen) + 1] = 0; - if (grub_le_to_cpu16 (ino.type) == SQUASH_TYPE_DIR) + if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR) filetype = GRUB_FSHELP_DIR; node = grub_malloc (sizeof (*node)); if (! node) @@ -303,9 +317,9 @@ make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root) root->data = data; return read_chunk (data->disk, &root->ino, sizeof (root->ino), - data->sb.inodeoffset, - decode_chunk_number (data->sb.root_ino_chunk), - grub_cpu_to_le16 (data->sb.root_ino_offset)); + grub_le_to_cpu64 (data->sb.inodeoffset) + + grub_le_to_cpu16 (data->sb.root_ino_chunk), + grub_cpu_to_le16 (data->sb.root_ino_offset)); } static grub_err_t @@ -400,7 +414,7 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) { struct grub_squash_frag_desc frag; err = read_chunk (file->device->disk, &frag, sizeof (frag), - data->fragments, 0, sizeof (frag) + data->fragments, sizeof (frag) * grub_le_to_cpu16 (data->ino.fragment)); if (err) return -1; From e4eaf625553ddf07f6cade56651fda7038542ea2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 19:40:11 +0100 Subject: [PATCH 08/17] Small restructuring of squash_read --- grub-core/fs/squash4.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 8eca185f3..1a121c2cf 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -402,13 +402,12 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) grub_uint64_t a; struct grub_squash_data *data = file->data; - a = grub_le_to_cpu32 (data->ino.offset) + file->offset; if (grub_le_to_cpu16 (data->ino.fragment) == 0xffff) { if (grub_le_to_cpu32 (data->ino.chunk)) - a += grub_le_to_cpu32 (data->ino.chunk); + a = grub_le_to_cpu32 (data->ino.chunk); else - a += sizeof (struct grub_squash_super); + a = sizeof (struct grub_squash_super); } else { @@ -418,10 +417,11 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) * grub_le_to_cpu16 (data->ino.fragment)); if (err) return -1; - a += grub_le_to_cpu64 (frag.offset); - a += grub_le_to_cpu32 (data->ino.chunk); + a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (data->ino.chunk); } + a += grub_le_to_cpu32 (data->ino.offset) + file->offset; + err = grub_disk_read (file->device->disk, a >> GRUB_DISK_SECTOR_BITS, a & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); if (err) From 8eef1f8244a91a1beb18bc7a94fad7347382e65e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 20:41:41 +0100 Subject: [PATCH 09/17] Compressed fragments and compressed data support --- grub-core/fs/squash4.c | 26 +++++++++++++++------ grub-core/io/gzio.c | 53 +++++++++++++++++++++++++++++++++++++++--- include/grub/deflate.h | 4 ++++ 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 1a121c2cf..50e5c3090 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -51,13 +51,18 @@ struct grub_squash_super grub_uint32_t dummy1; grub_uint32_t creation_time; grub_uint32_t dummy2; - grub_uint32_t dummy3[4]; + grub_uint32_t dummy3[2]; + grub_uint8_t flags; +#define SQUASH_FLAG_UNCOMPRESSED_INODES 1 +#define SQUASH_FLAG_UNCOMPRESSED_DATA 2 +#define SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS 8 + grub_uint8_t dummy4[7]; grub_uint16_t root_ino_offset; grub_uint16_t root_ino_chunk; - grub_uint32_t dummy4; + grub_uint32_t dummy5; grub_uint64_t total_size; grub_uint64_t exttbloffset; - grub_uint32_t dummy5[2]; + grub_uint32_t dummy6[2]; grub_uint64_t inodeoffset; grub_uint64_t diroffset; grub_uint64_t unk1offset; @@ -399,8 +404,9 @@ static grub_ssize_t grub_squash_read (grub_file_t file, char *buf, grub_size_t len) { grub_err_t err; - grub_uint64_t a; + grub_uint64_t a, b; struct grub_squash_data *data = file->data; + int compressed = 0; if (grub_le_to_cpu16 (data->ino.fragment) == 0xffff) { @@ -408,6 +414,7 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) a = grub_le_to_cpu32 (data->ino.chunk); else a = sizeof (struct grub_squash_super); + compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_DATA); } else { @@ -418,12 +425,17 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) if (err) return -1; a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (data->ino.chunk); + compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS); } - a += grub_le_to_cpu32 (data->ino.offset) + file->offset; + b = grub_le_to_cpu32 (data->ino.offset) + file->offset; - err = grub_disk_read (file->device->disk, a >> GRUB_DISK_SECTOR_BITS, - a & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); + /* FIXME: cache uncompressed chunks. */ + if (compressed) + err = grub_zlib_disk_read (file->device->disk, a, b, buf, len); + else + err = grub_disk_read (file->device->disk, (a + b) >> GRUB_DISK_SECTOR_BITS, + (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); if (err) return -1; return len; diff --git a/grub-core/io/gzio.c b/grub-core/io/gzio.c index 248a1750e..61227b4fc 100644 --- a/grub-core/io/gzio.c +++ b/grub-core/io/gzio.c @@ -41,6 +41,7 @@ #include #include #include +#include #include /* @@ -62,6 +63,9 @@ struct grub_gzio /* If input is in memory following fields are used instead of file. */ grub_size_t mem_input_size, mem_input_off; grub_uint8_t *mem_input; + grub_disk_addr_t disk_input_off; + grub_disk_addr_t disk_input_start; + grub_disk_t disk_input; /* The offset at which the data starts in the underlying file. */ grub_off_t data_offset; /* The type of current block. */ @@ -383,8 +387,21 @@ get_byte (grub_gzio_t gzio) return 0; } - if (grub_file_tell (gzio->file) == (grub_off_t) gzio->data_offset - || gzio->inbuf_d == INBUFSIZ) + if (gzio->disk_input && (gzio->disk_input_off == gzio->data_offset + || gzio->inbuf_d == INBUFSIZ)) + { + grub_disk_addr_t d = gzio->disk_input_start + gzio->disk_input_off; + gzio->inbuf_d = 0; + grub_disk_read (gzio->disk_input, + d >> GRUB_DISK_SECTOR_BITS, + d & (GRUB_DISK_SECTOR_SIZE - 1), + INBUFSIZ, gzio->inbuf); + gzio->disk_input_off += INBUFSIZ; + } + + if (gzio->file && (grub_file_tell (gzio->file) + == (grub_off_t) gzio->data_offset + || gzio->inbuf_d == INBUFSIZ)) { gzio->inbuf_d = 0; grub_file_read (gzio->file, gzio->inbuf, INBUFSIZ); @@ -402,8 +419,10 @@ gzio_seek (grub_gzio_t gzio, grub_off_t off) grub_error (GRUB_ERR_OUT_OF_RANGE, "attempt to seek outside of the file"); else - gzio->mem_input_off = gzio->data_offset; + gzio->mem_input_off = off; } + else if (gzio->disk_input) + gzio->disk_input_off = off; else grub_file_seek (gzio->file, off); } @@ -1297,6 +1316,34 @@ grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, return ret; } +grub_err_t +grub_zlib_disk_read (grub_disk_t disk, grub_disk_addr_t zlibstart, + grub_off_t off, char *outbuf, grub_size_t outsize) +{ + grub_gzio_t gzio = 0; + grub_ssize_t ret; + + gzio = grub_zalloc (sizeof (*gzio)); + if (! gzio) + return -1; + + gzio->disk_input_off = 0; + gzio->disk_input_start = zlibstart; + gzio->disk_input = disk; + + if (!test_zlib_header (gzio)) + { + grub_free (gzio); + return -1; + } + + ret = grub_gzio_read_real (gzio, off, outbuf, outsize); + grub_free (gzio); + + /* FIXME: Check Adler. */ + return ret < 0 ? grub_errno : GRUB_ERR_NONE; +} + static struct grub_fs grub_gzio_fs = diff --git a/include/grub/deflate.h b/include/grub/deflate.h index 6ec4eaa99..ae4a1f244 100644 --- a/include/grub/deflate.h +++ b/include/grub/deflate.h @@ -23,4 +23,8 @@ grub_ssize_t grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, char *outbuf, grub_size_t outsize); +grub_err_t +grub_zlib_disk_read (grub_disk_t disk, grub_disk_addr_t zlibstart, + grub_off_t off, char *outbuf, grub_size_t outsize); + #endif From 1fc7203971e91efde3645b97fac776872ecf7e5a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 20:48:03 +0100 Subject: [PATCH 10/17] Split grub_squash_read_data --- grub-core/fs/squash4.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 50e5c3090..2fa429803 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -401,17 +401,18 @@ grub_squash_open (struct grub_file *file, const char *name) } static grub_ssize_t -grub_squash_read (grub_file_t file, char *buf, grub_size_t len) +grub_squash_read_data (struct grub_squash_data *data, + grub_disk_t disk, const struct grub_squash_inode *ino, + grub_off_t off, char *buf, grub_size_t len) { grub_err_t err; grub_uint64_t a, b; - struct grub_squash_data *data = file->data; int compressed = 0; - if (grub_le_to_cpu16 (data->ino.fragment) == 0xffff) + if (grub_le_to_cpu16 (ino->fragment) == 0xffff) { - if (grub_le_to_cpu32 (data->ino.chunk)) - a = grub_le_to_cpu32 (data->ino.chunk); + if (grub_le_to_cpu32 (ino->chunk)) + a = grub_le_to_cpu32 (ino->chunk); else a = sizeof (struct grub_squash_super); compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_DATA); @@ -419,28 +420,37 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) else { struct grub_squash_frag_desc frag; - err = read_chunk (file->device->disk, &frag, sizeof (frag), + err = read_chunk (disk, &frag, sizeof (frag), data->fragments, sizeof (frag) - * grub_le_to_cpu16 (data->ino.fragment)); + * grub_le_to_cpu16 (ino->fragment)); if (err) return -1; - a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (data->ino.chunk); + a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (ino->chunk); compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS); } - b = grub_le_to_cpu32 (data->ino.offset) + file->offset; + b = grub_le_to_cpu32 (data->ino.offset) + off; /* FIXME: cache uncompressed chunks. */ if (compressed) - err = grub_zlib_disk_read (file->device->disk, a, b, buf, len); + err = grub_zlib_disk_read (disk, a, b, buf, len); else - err = grub_disk_read (file->device->disk, (a + b) >> GRUB_DISK_SECTOR_BITS, + err = grub_disk_read (disk, (a + b) >> GRUB_DISK_SECTOR_BITS, (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); if (err) return -1; return len; } +static grub_ssize_t +grub_squash_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_squash_data *data = file->data; + + return grub_squash_read_data (data, file->device->disk, &data->ino, + file->offset, buf, len); +} + static grub_err_t grub_squash_close (grub_file_t file) { From 627a62fc70e75578ebcfbef71428afd1700f7e7d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 21:14:42 +0100 Subject: [PATCH 11/17] Make type-dependent portion of inode an union --- grub-core/fs/squash4.c | 49 ++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 2fa429803..409d91e47 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -75,13 +75,26 @@ struct grub_squash_inode { /* Same values as direlem types. */ grub_uint16_t type; - grub_uint16_t dummy1[3]; + grub_uint16_t dummy[3]; grub_uint32_t mtime; - grub_uint16_t dummy2[2]; - grub_uint32_t chunk; - grub_uint32_t fragment; - grub_uint32_t offset; - grub_uint32_t size; + union + { + struct { + grub_uint16_t dummy[2]; + grub_uint32_t chunk; + grub_uint32_t fragment; + grub_uint32_t offset; + grub_uint32_t size; + } file; + struct { + grub_uint16_t dummy1[2]; + grub_uint32_t chunk; + grub_uint16_t dummy2[2]; + grub_uint16_t size; + grub_uint16_t offset; + grub_uint16_t dummy3[2]; + } dir; + }; } __attribute__ ((packed)); /* Chunk-based. */ @@ -245,12 +258,12 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, enum grub_fshelp_filetype filetype, grub_fshelp_node_t node)) { - grub_uint32_t off = grub_le_to_cpu32 (dir->ino.offset) >> 16; + grub_uint32_t off = grub_le_to_cpu16 (dir->ino.dir.offset); grub_uint32_t endoff; unsigned i; /* FIXME: why - 3 ? */ - endoff = (grub_le_to_cpu32 (dir->ino.offset) & 0xffff) + off - 3; + endoff = grub_le_to_cpu32 (dir->ino.dir.size) + off - 3; while (off < endoff) { @@ -259,7 +272,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, err = read_chunk (dir->data->disk, &dh, sizeof (dh), grub_le_to_cpu64 (dir->data->sb.diroffset) - + grub_le_to_cpu32 (dir->ino.chunk), off); + + grub_le_to_cpu32 (dir->ino.dir.chunk), off); if (err) return 0; off += sizeof (dh); @@ -274,7 +287,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, err = read_chunk (dir->data->disk, &di, sizeof (di), grub_le_to_cpu64 (dir->data->sb.diroffset) - + grub_le_to_cpu32 (dir->ino.chunk), off); + + grub_le_to_cpu32 (dir->ino.dir.chunk), off); if (err) return 0; off += sizeof (di); @@ -292,7 +305,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, err = read_chunk (dir->data->disk, buf, grub_le_to_cpu16 (di.namelen) + 1, grub_le_to_cpu64 (dir->data->sb.diroffset) - + grub_le_to_cpu32 (dir->ino.chunk), off); + + grub_le_to_cpu32 (dir->ino.dir.chunk), off); if (err) return 0; @@ -395,7 +408,7 @@ grub_squash_open (struct grub_file *file, const char *name) file->data = data; data->ino = fdiro->ino; - file->size = grub_le_to_cpu32 (fdiro->ino.size); + file->size = grub_le_to_cpu32 (fdiro->ino.file.size); return GRUB_ERR_NONE; } @@ -409,10 +422,10 @@ grub_squash_read_data (struct grub_squash_data *data, grub_uint64_t a, b; int compressed = 0; - if (grub_le_to_cpu16 (ino->fragment) == 0xffff) + if (grub_le_to_cpu16 (ino->file.fragment) == 0xffff) { - if (grub_le_to_cpu32 (ino->chunk)) - a = grub_le_to_cpu32 (ino->chunk); + if (grub_le_to_cpu32 (ino->file.chunk)) + a = grub_le_to_cpu32 (ino->file.chunk); else a = sizeof (struct grub_squash_super); compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_DATA); @@ -422,14 +435,14 @@ grub_squash_read_data (struct grub_squash_data *data, struct grub_squash_frag_desc frag; err = read_chunk (disk, &frag, sizeof (frag), data->fragments, sizeof (frag) - * grub_le_to_cpu16 (ino->fragment)); + * grub_le_to_cpu16 (ino->file.fragment)); if (err) return -1; - a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (ino->chunk); + a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (ino->file.chunk); compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS); } - b = grub_le_to_cpu32 (data->ino.offset) + off; + b = grub_le_to_cpu32 (data->ino.file.offset) + off; /* FIXME: cache uncompressed chunks. */ if (compressed) From e330e4f3830f0c0cab6dfc753c1c9b31c7b909bf Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 21:17:27 +0100 Subject: [PATCH 12/17] support file mtime --- grub-core/fs/squash4.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 409d91e47..9637496d9 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -351,11 +351,13 @@ grub_squash_dir (grub_device_t device, const char *path, int NESTED_FUNC_ATTR iterate (const char *filename, enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node __attribute__ ((unused))) + grub_fshelp_node_t node) { struct grub_dirhook_info info; grub_memset (&info, 0, sizeof (info)); info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtimeset = 1; + info.mtime = grub_le_to_cpu32 (node->ino.mtime); return hook (filename, &info); } From 4a91cd82203200b8c636f4659905449e5ae1395a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 22:29:36 +0100 Subject: [PATCH 13/17] symlink support --- grub-core/fs/squash4.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 9637496d9..95797998a 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -94,6 +94,11 @@ struct grub_squash_inode grub_uint16_t offset; grub_uint16_t dummy3[2]; } dir; + struct { + grub_uint16_t dummy[4]; + grub_uint32_t namelen; + char name[0]; + } symlink; }; } __attribute__ ((packed)); @@ -114,6 +119,7 @@ struct grub_squash_dirent grub_uint16_t type; #define SQUASH_TYPE_DIR 1 #define SQUASH_TYPE_REGULAR 2 +#define SQUASH_TYPE_SYMLINK 3 /* Actually the value is the length of name - 1. */ grub_uint16_t namelen; char name[0]; @@ -141,6 +147,8 @@ struct grub_fshelp_node { struct grub_squash_data *data; struct grub_squash_inode ino; + grub_uint32_t ino_chunk; + grub_uint16_t ino_offset; }; static grub_err_t @@ -251,6 +259,28 @@ squash_mount (grub_disk_t disk) return data; } +static char * +grub_squash_read_symlink (grub_fshelp_node_t node) +{ + char *ret; + grub_err_t err; + ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1); + + err = read_chunk (node->data->disk, ret, + grub_le_to_cpu32 (node->ino.symlink.namelen), + grub_le_to_cpu64 (node->data->sb.inodeoffset) + + node->ino_chunk, + node->ino_offset + (node->ino.symlink.name + - (char *) &node->ino)); + if (err) + { + grub_free (ret); + return NULL; + } + ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0; + return ret; +} + static int grub_squash_iterate_dir (grub_fshelp_node_t dir, int NESTED_FUNC_ATTR @@ -313,11 +343,17 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, buf[grub_le_to_cpu16 (di.namelen) + 1] = 0; if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR) filetype = GRUB_FSHELP_DIR; + if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK) + filetype = GRUB_FSHELP_SYMLINK; + node = grub_malloc (sizeof (*node)); if (! node) return 0; *node = *dir; node->ino = ino; + node->ino_chunk = grub_le_to_cpu32 (dh.ino_chunk); + node->ino_offset = grub_le_to_cpu16 (di.ino_offset); + r = hook (buf, filetype, node); grub_free (buf); @@ -375,7 +411,7 @@ grub_squash_dir (grub_device_t device, const char *path, return err; grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir, - NULL, GRUB_FSHELP_DIR); + grub_squash_read_symlink, GRUB_FSHELP_DIR); if (!grub_errno) grub_squash_iterate_dir (fdiro, iterate); @@ -401,7 +437,7 @@ grub_squash_open (struct grub_file *file, const char *name) return err; grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir, - NULL, GRUB_FSHELP_REG); + grub_squash_read_symlink, GRUB_FSHELP_REG); if (grub_errno) { grub_free (data); From 4aab26313076d3fdb10264cd0b38d9d51741646f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 11 Dec 2010 18:20:45 +0100 Subject: [PATCH 14/17] SquashFS mtime support --- grub-core/fs/squash4.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 95797998a..882f5c959 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -509,6 +509,19 @@ grub_squash_close (grub_file_t file) return GRUB_ERR_NONE; } +static grub_err_t +grub_squash_mtime (grub_device_t dev, grub_int32_t *tm) +{ + struct grub_squash_data *data = 0; + + data = squash_mount (dev->disk); + if (! data) + return grub_errno; + *tm = grub_le_to_cpu32 (data->sb.creation_time); + grub_free (data); + return GRUB_ERR_NONE; +} + static struct grub_fs grub_squash_fs = { .name = "squash4", @@ -516,6 +529,7 @@ static struct grub_fs grub_squash_fs = .open = grub_squash_open, .read = grub_squash_read, .close = grub_squash_close, + .mtime = grub_squash_mtime, #ifdef GRUB_UTIL .reserved_first_sector = 0, #endif From ea17ec49ae1d5d20b93ad7f1c303aa4d1c0682ff Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 11 Dec 2010 18:32:08 +0100 Subject: [PATCH 15/17] Extend fields in dirent header --- grub-core/fs/squash4.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 882f5c959..1c522f893 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -106,11 +106,9 @@ struct grub_squash_inode struct grub_squash_dirent_header { /* Actually the value is the number of elements - 1. */ - grub_uint16_t nelems; - grub_uint16_t dummy1; - grub_uint32_t ino_chunk; - grub_uint16_t dummy2[2]; -}; + grub_uint32_t nelems; + grub_uint64_t ino_chunk; +} __attribute__ ((packed)); struct grub_squash_dirent { From 5e96cb4452735c9e2beb82dbfbb03b90ec6c2d1f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 11 Dec 2010 18:41:42 +0100 Subject: [PATCH 16/17] Extend few other fields --- grub-core/fs/squash4.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 1c522f893..0f94ae0a2 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -51,23 +51,23 @@ struct grub_squash_super grub_uint32_t dummy1; grub_uint32_t creation_time; grub_uint32_t dummy2; - grub_uint32_t dummy3[2]; + grub_uint64_t dummy3; grub_uint8_t flags; #define SQUASH_FLAG_UNCOMPRESSED_INODES 1 #define SQUASH_FLAG_UNCOMPRESSED_DATA 2 #define SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS 8 grub_uint8_t dummy4[7]; grub_uint16_t root_ino_offset; - grub_uint16_t root_ino_chunk; - grub_uint32_t dummy5; + grub_uint32_t root_ino_chunk; + grub_uint16_t dummy5; grub_uint64_t total_size; grub_uint64_t exttbloffset; - grub_uint32_t dummy6[2]; + grub_uint64_t dummy6; grub_uint64_t inodeoffset; grub_uint64_t diroffset; grub_uint64_t unk1offset; grub_uint64_t unk2offset; -}; +} __attribute__ ((packed)); /* Chunk-based */ @@ -80,25 +80,25 @@ struct grub_squash_inode union { struct { - grub_uint16_t dummy[2]; + grub_uint32_t dummy; grub_uint32_t chunk; grub_uint32_t fragment; grub_uint32_t offset; grub_uint32_t size; - } file; + } __attribute__ ((packed)) file; struct { - grub_uint16_t dummy1[2]; + grub_uint32_t dummy1; grub_uint32_t chunk; - grub_uint16_t dummy2[2]; + grub_uint32_t dummy2; grub_uint16_t size; - grub_uint16_t offset; - grub_uint16_t dummy3[2]; - } dir; + grub_uint32_t offset; + grub_uint16_t dummy3; + } __attribute__ ((packed)) dir; struct { - grub_uint16_t dummy[4]; + grub_uint64_t dummy; grub_uint32_t namelen; char name[0]; - } symlink; + } __attribute__ ((packed)) symlink; }; } __attribute__ ((packed)); From 81ecffbfc19293dd6b14050c775926d9b5478eb8 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 11 Dec 2010 18:42:44 +0100 Subject: [PATCH 17/17] Add missing packed attribute --- grub-core/fs/squash4.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 0f94ae0a2..8e8388628 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -99,7 +99,7 @@ struct grub_squash_inode grub_uint32_t namelen; char name[0]; } __attribute__ ((packed)) symlink; - }; + } __attribute__ ((packed)); } __attribute__ ((packed)); /* Chunk-based. */ @@ -121,13 +121,13 @@ struct grub_squash_dirent /* Actually the value is the length of name - 1. */ grub_uint16_t namelen; char name[0]; -}; +} __attribute__ ((packed)); struct grub_squash_frag_desc { grub_uint64_t offset; grub_uint64_t dummy; -}; +} __attribute__ ((packed)); #define SQUASH_CHUNK_SIZE 0x2000 #define SQUASH_CHUNK_FLAGS 0x8000