exttblptr RAW superblock
UID/GID table is the array ot uint32_t
- unk1 contains pointer to unk3 followed by some chunk.
+ unk1 contains pointer to fragment table followed by some chunk.
unk2 containts one uint64_t
*/
#define SQUASH_MAGIC 0x73717368
grub_uint32_t dummy1;
grub_uint32_t creation_time;
- grub_uint32_t dummy2;
+ grub_uint32_t block_size;
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_uint64_t dummy4;
grub_uint16_t root_ino_offset;
grub_uint32_t root_ino_chunk;
grub_uint16_t dummy5;
grub_uint64_t unk2offset;
} __attribute__ ((packed));
-
/* Chunk-based */
struct grub_squash_inode
{
grub_uint32_t fragment;
grub_uint32_t offset;
grub_uint32_t size;
+ grub_uint32_t block_size[0];
} __attribute__ ((packed)) file;
+ struct {
+ grub_uint32_t dummy;
+ grub_uint64_t chunk;
+ grub_uint64_t size;
+ grub_uint32_t dummy2[3];
+ grub_uint32_t fragment;
+ grub_uint32_t offset;
+ grub_uint32_t dummy3;
+ grub_uint32_t block_size[0];
+ } __attribute__ ((packed)) long_file;
struct {
grub_uint32_t dummy1;
grub_uint32_t chunk;
} __attribute__ ((packed));
} __attribute__ ((packed));
+struct grub_squash_cache_inode
+{
+ struct grub_squash_inode ino;
+ grub_disk_addr_t ino_chunk;
+ grub_uint16_t ino_offset;
+ grub_uint32_t *block_sizes;
+ grub_disk_addr_t *cumulated_block_sizes;
+};
+
/* Chunk-based. */
struct grub_squash_dirent_header
{
grub_uint16_t ino_offset;
grub_uint16_t dummy;
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];
} __attribute__ ((packed));
+enum
+ {
+ SQUASH_TYPE_DIR = 1,
+ SQUASH_TYPE_REGULAR = 2,
+ SQUASH_TYPE_SYMLINK = 3,
+ SQUASH_TYPE_LONG_REGULAR = 9,
+ };
+
+
struct grub_squash_frag_desc
{
grub_uint64_t offset;
- grub_uint64_t dummy;
+ grub_uint32_t size;
+ grub_uint32_t dummy;
} __attribute__ ((packed));
+enum
+ {
+ SQUASH_CHUNK_FLAGS = 0x8000,
+ SQUASH_CHUNK_UNCOMPRESSED = 0x8000
+ };
+
+enum
+ {
+ SQUASH_BLOCK_FLAGS = 0x1000000,
+ SQUASH_BLOCK_UNCOMPRESSED = 0x1000000
+ };
+
#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;
+ struct grub_squash_cache_inode ino;
grub_uint64_t fragments;
};
{
struct grub_squash_data *data;
struct grub_squash_inode ino;
- grub_uint32_t ino_chunk;
+ grub_disk_addr_t ino_chunk;
grub_uint16_t ino_offset;
};
static grub_err_t
-read_chunk (grub_disk_t disk, void *buf, grub_size_t len,
+read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len,
grub_uint64_t chunk, grub_off_t offset)
{
grub_uint64_t chunk_start;
grub_err_t err;
while (1)
{
- err = grub_disk_read (disk, chunk_start >> GRUB_DISK_SECTOR_BITS,
+ err = grub_disk_read (data->disk,
+ chunk_start >> GRUB_DISK_SECTOR_BITS,
chunk_start & (GRUB_DISK_SECTOR_SIZE - 1),
sizeof (d), &d);
if (err)
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),
+ err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
a & (GRUB_DISK_SECTOR_SIZE - 1),
csize, buf);
if (err)
if (!tmp)
return grub_errno;
/* FIXME: buffer uncompressed data. */
- err = grub_disk_read (disk, (a >> GRUB_DISK_SECTOR_BITS),
+ err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
a & (GRUB_DISK_SECTOR_SIZE - 1),
bsize, tmp);
if (err)
return NULL;
}
- err = grub_disk_read (disk, grub_le_to_cpu32 (sb.unk1offset)
+ err = grub_disk_read (disk,
+ grub_le_to_cpu64 (sb.unk1offset)
>> GRUB_DISK_SECTOR_BITS,
- grub_le_to_cpu32 (sb.unk1offset)
+ grub_le_to_cpu64 (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");
return NULL;
data->sb = sb;
data->disk = disk;
- data->fragments = frag;
+ data->fragments = grub_le_to_cpu64 (frag);
return data;
}
grub_err_t err;
ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
- err = read_chunk (node->data->disk, ret,
+ err = read_chunk (node->data, ret,
grub_le_to_cpu32 (node->ino.symlink.namelen),
grub_le_to_cpu64 (node->data->sb.inodeoffset)
+ node->ino_chunk,
struct grub_squash_dirent_header dh;
grub_err_t err;
- err = read_chunk (dir->data->disk, &dh, sizeof (dh),
+ err = read_chunk (dir->data, &dh, sizeof (dh),
grub_le_to_cpu64 (dir->data->sb.diroffset)
+ grub_le_to_cpu32 (dir->ino.dir.chunk), off);
if (err)
struct grub_squash_dirent di;
struct grub_squash_inode ino;
- err = read_chunk (dir->data->disk, &di, sizeof (di),
+ err = read_chunk (dir->data, &di, sizeof (di),
grub_le_to_cpu64 (dir->data->sb.diroffset)
+ grub_le_to_cpu32 (dir->ino.dir.chunk), off);
if (err)
return 0;
off += sizeof (di);
- err = read_chunk (dir->data->disk, &ino, sizeof (ino),
+ err = read_chunk (dir->data, &ino, sizeof (ino),
grub_le_to_cpu64 (dir->data->sb.inodeoffset)
+ grub_le_to_cpu32 (dh.ino_chunk),
grub_cpu_to_le16 (di.ino_offset));
buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2);
if (!buf)
return 0;
- err = read_chunk (dir->data->disk, buf,
+ err = read_chunk (dir->data, buf,
grub_le_to_cpu16 (di.namelen) + 1,
grub_le_to_cpu64 (dir->data->sb.diroffset)
+ grub_le_to_cpu32 (dir->ino.dir.chunk), off);
grub_memset (root, 0, sizeof (*root));
root->data = data;
- return read_chunk (data->disk, &root->ino, sizeof (root->ino),
+ return read_chunk (data, &root->ino, sizeof (root->ino),
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));
}
file->data = data;
- data->ino = fdiro->ino;
- file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
+ data->ino.ino = fdiro->ino;
+ data->ino.block_sizes = NULL;
+ data->ino.cumulated_block_sizes = NULL;
+ data->ino.ino_chunk = fdiro->ino_chunk;
+ data->ino.ino_offset = fdiro->ino_offset;
+
+ if (fdiro->ino.type
+ == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
+ file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size);
+ else
+ file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
return GRUB_ERR_NONE;
}
+static grub_ssize_t
+direct_read (struct grub_squash_data *data,
+ struct grub_squash_cache_inode *ino,
+ grub_off_t off, char *buf, grub_size_t len)
+{
+ grub_err_t err;
+ grub_off_t cumulated_uncompressed_size = 0;
+ grub_uint64_t a;
+ grub_size_t i;
+ grub_size_t origlen = len;
+
+ if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
+ a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
+ else
+ a = grub_le_to_cpu32 (ino->ino.file.chunk);
+
+ if (!ino->block_sizes)
+ {
+ grub_off_t total_size;
+ grub_size_t total_blocks;
+ grub_size_t block_offset;
+ if (ino->ino.type
+ == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
+ {
+ total_size = grub_le_to_cpu64 (ino->ino.long_file.size);
+ block_offset = ((char *) &ino->ino.long_file.block_size
+ - (char *) &ino->ino);
+ }
+ else
+ {
+ total_size = grub_le_to_cpu32 (ino->ino.file.size);
+ block_offset = ((char *) &ino->ino.file.block_size
+ - (char *) &ino->ino);
+ }
+ total_blocks = grub_divmod64 (total_size
+ + grub_le_to_cpu32 (data->sb.block_size) - 1,
+ grub_le_to_cpu32 (data->sb.block_size),
+ 0);
+ ino->block_sizes = grub_malloc (total_blocks
+ * sizeof (ino->block_sizes[0]));
+ ino->cumulated_block_sizes = grub_malloc (total_blocks
+ * sizeof (ino->cumulated_block_sizes[0]));
+ if (!ino->block_sizes || !ino->cumulated_block_sizes)
+ {
+ grub_free (ino->block_sizes);
+ grub_free (ino->cumulated_block_sizes);
+ ino->block_sizes = 0;
+ ino->cumulated_block_sizes = 0;
+ return -1;
+ }
+ err = read_chunk (data, ino->block_sizes,
+ total_blocks * sizeof (ino->block_sizes[0]),
+ grub_le_to_cpu64 (data->sb.inodeoffset)
+ + ino->ino_chunk,
+ ino->ino_offset + block_offset);
+ if (err)
+ {
+ grub_free (ino->block_sizes);
+ grub_free (ino->cumulated_block_sizes);
+ ino->block_sizes = 0;
+ ino->cumulated_block_sizes = 0;
+ return -1;
+ }
+ ino->cumulated_block_sizes[0] = 0;
+ for (i = 1; i < total_blocks; i++)
+ ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1]
+ + (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS);
+ }
+
+ if (a == 0)
+ a = sizeof (struct grub_squash_super);
+ i = grub_divmod64 (off, grub_le_to_cpu32 (data->sb.block_size), 0);
+ cumulated_uncompressed_size = grub_le_to_cpu32 (data->sb.block_size)
+ * (grub_disk_addr_t) i;
+ while (cumulated_uncompressed_size < off + len)
+ {
+ grub_size_t boff, read;
+ boff = off - cumulated_uncompressed_size;
+ read = grub_le_to_cpu32 (data->sb.block_size) - boff;
+ if (read > len)
+ read = len;
+ if (!(ino->block_sizes[i] & SQUASH_BLOCK_UNCOMPRESSED))
+ err = grub_zlib_disk_read (data->disk,
+ ino->cumulated_block_sizes[i] + a,
+ boff, buf, read);
+ else
+ err = grub_disk_read (data->disk,
+ (ino->cumulated_block_sizes[i] + a + boff)
+ >> GRUB_DISK_SECTOR_BITS,
+ (ino->cumulated_block_sizes[i] + a + boff)
+ & (GRUB_DISK_SECTOR_SIZE - 1),
+ read, buf);
+ if (err)
+ return -1;
+ off += read;
+ len -= read;
+ buf += read;
+ cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size);
+ i++;
+ }
+ return origlen;
+}
+
+
static grub_ssize_t
grub_squash_read_data (struct grub_squash_data *data,
- grub_disk_t disk, const struct grub_squash_inode *ino,
+ struct grub_squash_cache_inode *ino,
grub_off_t off, char *buf, grub_size_t len)
{
grub_err_t err;
grub_uint64_t a, b;
+ grub_uint32_t fragment;
int compressed = 0;
+ struct grub_squash_frag_desc frag;
- if (grub_le_to_cpu16 (ino->file.fragment) == 0xffff)
+ if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
{
- 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);
+ a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
+ fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
}
else
{
- struct grub_squash_frag_desc frag;
- err = read_chunk (disk, &frag, sizeof (frag),
- data->fragments, sizeof (frag)
- * grub_le_to_cpu16 (ino->file.fragment));
- if (err)
- return -1;
- a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (ino->file.chunk);
- compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS);
+ a = grub_le_to_cpu32 (ino->ino.file.chunk);
+ fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
}
- b = grub_le_to_cpu32 (data->ino.file.offset) + off;
-
+ if (fragment == 0xffffffff)
+ return direct_read (data, ino, off, buf, len);
+
+ err = read_chunk (data, &frag, sizeof (frag),
+ data->fragments, sizeof (frag) * fragment);
+ if (err)
+ return -1;
+ a += grub_le_to_cpu64 (frag.offset);
+ compressed = !(frag.size & SQUASH_BLOCK_UNCOMPRESSED);
+ if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
+ b = grub_le_to_cpu64 (ino->ino.long_file.offset) + off;
+ else
+ b = grub_le_to_cpu32 (ino->ino.file.offset) + off;
+
/* FIXME: cache uncompressed chunks. */
if (compressed)
- err = grub_zlib_disk_read (disk, a, b, buf, len);
+ err = grub_zlib_disk_read (data->disk, a, b, buf, len);
else
- err = grub_disk_read (disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
+ err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
(a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
if (err)
return -1;
{
struct grub_squash_data *data = file->data;
- return grub_squash_read_data (data, file->device->disk, &data->ino,
+ return grub_squash_read_data (data, &data->ino,
file->offset, buf, len);
}