Remove pkg2sqfs

Rewritten and moved to a seperate project.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
This commit is contained in:
David Oberhollenzer 2019-06-09 02:18:27 +02:00
parent e08c819606
commit f426e4ef91
12 changed files with 1 additions and 1869 deletions

View File

@ -10,4 +10,3 @@ EXTRA_DIST = autogen.sh LICENSE README.md
include lib/Makemodule.am
include main/Makemodule.am
include sqfs/Makemodule.am

View File

@ -1,198 +0,0 @@
/* SPDX-License-Identifier: ISC */
#ifndef SQUASHFS_H
#define SQUASHFS_H
#include "comp/compressor.h"
#include "pkg/pkgformat.h"
#include <stdint.h>
#define SQFS_MAGIC 0x73717368
#define SQFS_VERSION_MAJOR 4
#define SQFS_VERSION_MINOR 0
#define SQFS_META_BLOCK_SIZE 8192
#define SQFS_DEFAULT_BLOCK_SIZE 131072
#define SQFS_DEVBLK_SIZE 4096
#define SQFS_MAX_DIR_ENT 256
typedef struct {
uint32_t magic;
uint32_t inode_count;
uint32_t modification_time;
uint32_t block_size;
uint32_t fragment_entry_count;
uint16_t compression_id;
uint16_t block_log;
uint16_t flags;
uint16_t id_count;
uint16_t version_major;
uint16_t version_minor;
uint64_t root_inode_ref;
uint64_t bytes_used;
uint64_t id_table_start;
uint64_t xattr_id_table_start;
uint64_t inode_table_start;
uint64_t directory_table_start;
uint64_t fragment_table_start;
uint64_t export_table_start;
} __attribute__((packed)) sqfs_super_t;
typedef struct {
uint64_t start_offset;
uint32_t size;
uint32_t pad0;
} sqfs_fragment_t;
typedef struct {
uint16_t type;
uint16_t mode;
uint16_t uid_idx;
uint16_t gid_idx;
uint32_t mod_time;
uint32_t inode_number;
} sqfs_inode_t;
typedef struct {
uint32_t nlink;
uint32_t devno;
} sqfs_inode_dev_t;
typedef struct {
uint32_t nlink;
uint32_t devno;
uint32_t xattr_idx;
} sqfs_inode_dev_ext_t;
typedef struct {
uint32_t nlink;
uint32_t target_size;
uint8_t target[];
} sqfs_inode_slink_t;
typedef struct {
uint32_t blocks_start;
uint32_t fragment_index;
uint32_t fragment_offset;
uint32_t file_size;
uint32_t block_sizes[];
} sqfs_inode_file_t;
typedef struct {
uint64_t blocks_start;
uint64_t file_size;
uint64_t sparse;
uint32_t nlink;
uint32_t fragment_idx;
uint32_t fragment_offset;
uint32_t xattr_idx;
uint32_t block_sizes[];
} sqfs_inode_file_ext_t;
typedef struct {
uint32_t start_block;
uint32_t nlink;
uint16_t size;
uint16_t offset;
uint32_t parent_inode;
} sqfs_inode_dir_t;
typedef struct {
uint32_t nlink;
uint32_t size;
uint32_t start_block;
uint32_t parent_inode;
uint16_t inodex_count;
uint16_t offset;
uint32_t xattr_idx;
} sqfs_inode_dir_ext_t;
typedef struct {
uint32_t count;
uint32_t start_block;
uint32_t inode_number;
} sqfs_dir_header_t;
typedef struct {
uint16_t offset;
uint16_t inode_number;
uint16_t type;
uint16_t size;
uint8_t name[];
} sqfs_dir_entry_t;
typedef enum {
SQFS_COMP_GZIP = 1,
SQFS_COMP_LZMA = 2,
SQFS_COMP_LZO = 3,
SQFS_COMP_XZ = 4,
SQFS_COMP_LZ4 = 5,
SQFS_COMP_ZSTD = 6,
SQFS_COMP_MIN = 1,
SQFS_COMP_MAX = 6,
} E_SQFS_COMPRESSOR;
typedef enum {
SQFS_FLAG_UNCOMPRESSED_INODES = 0x0001,
SQFS_FLAG_UNCOMPRESSED_DATA = 0x0002,
SQFS_FLAG_UNCOMPRESSED_FRAGMENTS = 0x0008,
SQFS_FLAG_NO_FRAGMENTS = 0x0010,
SQFS_FLAG_ALWAYS_FRAGMENTS = 0x0020,
SQFS_FLAG_DUPLICATES = 0x0040,
SQFS_FLAG_EXPORTABLE = 0x0080,
SQFS_FLAG_UNCOMPRESSED_XATTRS = 0x0100,
SQFS_FLAG_NO_XATTRS = 0x0200,
SQFS_FLAG_COMPRESSOR_OPTIONS = 0x0400,
SQFS_FLAG_UNCOMPRESSED_IDS = 0x0800,
} E_SQFS_SUPER_FLAGS;
typedef enum {
SQFS_INODE_DIR = 1,
SQFS_INODE_FILE = 2,
SQFS_INODE_SLINK = 3,
SQFS_INODE_BDEV = 4,
SQFS_INODE_CDEV = 5,
SQFS_INODE_FIFO = 6,
SQFS_INODE_SOCKET = 7,
SQFS_INODE_EXT_DIR = 8,
SQFS_INODE_EXT_FILE = 9,
SQFS_INODE_EXT_SLINK = 10,
SQFS_INODE_EXT_BDEV = 11,
SQFS_INODE_EXT_CDEV = 12,
SQFS_INODE_EXT_FIFO = 13,
SQFS_INODE_EXT_SOCKET = 14,
} E_SQFS_INODE_TYPE;
typedef struct {
uint8_t data[SQFS_META_BLOCK_SIZE + 2];
uint8_t scratch[SQFS_META_BLOCK_SIZE];
size_t offset;
size_t written;
int outfd;
compressor_stream_t *strm;
} meta_writer_t;
int sqfs_super_init(sqfs_super_t *s, int64_t timestamp,
uint32_t blocksize, E_SQFS_COMPRESSOR comp);
int sqfs_super_write(const sqfs_super_t *super, int outfd);
compressor_stream_t *sqfs_get_compressor(sqfs_super_t *s);
meta_writer_t *meta_writer_create(int fd, compressor_stream_t *strm);
void meta_writer_destroy(meta_writer_t *m);
int meta_writer_flush(meta_writer_t *m);
int meta_writer_append(meta_writer_t *m, const void *data, size_t size);
int sqfs_write_ids(int outfd, sqfs_super_t *super,
uint32_t *id_tbl, size_t count,
compressor_stream_t *strm);
int sqfs_write_fragment_table(int outfd, sqfs_super_t *super,
sqfs_fragment_t *fragments, size_t count,
compressor_stream_t *strm);
#endif /* SQUASHFS_H */

View File

@ -33,7 +33,4 @@ libpkg_a_SOURCES += include/pkg/pkgio.h include/pkg/pkgwriter.h
libpkg_a_SOURCES += lib/pkg/pkgreader.c lib/pkg/pkgwriter.c
libpkg_a_SOURCES += lib/pkg/pkg_unpack.c lib/pkg/pkgio_rd_image_entry.c
libsqfs_a_SOURCES = include/sqfs/squashfs.h lib/sqfs/super.c
libsqfs_a_SOURCES += lib/sqfs/meta_writer.c lib/sqfs/table.c
noinst_LIBRARIES += libutil.a libfilelist.a libcomp.a libpkg.a libsqfs.a
noinst_LIBRARIES += libutil.a libfilelist.a libcomp.a libpkg.a

View File

@ -1,92 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "sqfs/squashfs.h"
#include "util/util.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
meta_writer_t *meta_writer_create(int fd, compressor_stream_t *strm)
{
meta_writer_t *m = calloc(1, sizeof(*m));
if (m == NULL) {
perror("creating meta data writer");
return NULL;
}
m->strm = strm;
m->outfd = fd;
return m;
}
void meta_writer_destroy(meta_writer_t *m)
{
free(m);
}
int meta_writer_flush(meta_writer_t *m)
{
ssize_t ret, count;
if (m->offset == 0)
return 0;
ret = m->strm->do_block(m->strm, m->data + 2, m->scratch,
m->offset, sizeof(m->scratch));
if (ret < 0)
return -1;
if (ret > 0 && (size_t)ret < m->offset) {
memcpy(m->data + 2, m->scratch, ret);
((uint16_t *)m->data)[0] = htole16(ret);
count = ret + 2;
} else {
((uint16_t *)m->data)[0] = htole16(m->offset | 0x8000);
count = m->offset + 2;
}
ret = write_retry(m->outfd, m->data, count);
if (ret < 0) {
perror("writing meta data");
return -1;
}
if (ret < count) {
fputs("meta data was truncated\n", stderr);
return -1;
}
memset(m->data, 0, sizeof(m->data));
m->offset = 0;
m->written += count;
return 0;
}
int meta_writer_append(meta_writer_t *m, const void *data, size_t size)
{
size_t diff;
while (size != 0) {
diff = sizeof(m->data) - 2 - m->offset;
if (diff == 0) {
if (meta_writer_flush(m))
return -1;
diff = sizeof(m->data) - 2;
}
if (diff > size)
diff = size;
memcpy(m->data + 2 + m->offset, data, diff);
m->offset += diff;
size -= diff;
data = (const char *)data + diff;
}
if (m->offset == (sizeof(m->data) - 2))
return meta_writer_flush(m);
return 0;
}

View File

@ -1,129 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include <endian.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include "sqfs/squashfs.h"
#include "util/util.h"
int sqfs_super_init(sqfs_super_t *s, int64_t timestamp,
uint32_t blocksize, E_SQFS_COMPRESSOR comp)
{
memset(s, 0, sizeof(*s));
if (blocksize & (blocksize - 1)) {
fputs("Block size must be a power of 2\n", stderr);
return -1;
}
if (blocksize < 4096 || blocksize >= (1 << 24)) {
fputs("Block size must be between 4k and 1M\n", stderr);
return -1;
}
if (timestamp < 0 || timestamp > 0x00000000FFFFFFFF) {
fputs("Timestamp too large for squashfs\n", stderr);
return -1;
}
s->magic = SQFS_MAGIC;
s->modification_time = timestamp;
s->block_size = blocksize;
s->compression_id = comp;
s->flags = SQFS_FLAG_NO_FRAGMENTS | SQFS_FLAG_NO_XATTRS;
s->version_major = SQFS_VERSION_MAJOR;
s->version_minor = SQFS_VERSION_MINOR;
s->bytes_used = sizeof(*s);
s->id_table_start = 0xFFFFFFFFFFFFFFFFUL;
s->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL;
s->inode_table_start = 0xFFFFFFFFFFFFFFFFUL;
s->directory_table_start = 0xFFFFFFFFFFFFFFFFUL;
s->fragment_table_start = 0xFFFFFFFFFFFFFFFFUL;
s->export_table_start = 0xFFFFFFFFFFFFFFFFUL;
while (blocksize != 0) {
s->block_log += 1;
blocksize >>= 1;
}
s->block_log -= 1;
return 0;
}
int sqfs_super_write(const sqfs_super_t *super, int outfd)
{
sqfs_super_t copy;
ssize_t ret;
off_t off;
copy.magic = htole32(super->magic);
copy.inode_count = htole32(super->inode_count);
copy.modification_time = htole32(super->modification_time);
copy.block_size = htole32(super->block_size);
copy.fragment_entry_count = htole32(super->fragment_entry_count);
copy.compression_id = htole16(super->compression_id);
copy.block_log = htole16(super->block_log);
copy.flags = htole16(super->flags);
copy.id_count = htole16(super->id_count);
copy.version_major = htole16(super->version_major);
copy.version_minor = htole16(super->version_minor);
copy.root_inode_ref = htole64(super->root_inode_ref);
copy.bytes_used = htole64(super->bytes_used);
copy.id_table_start = htole64(super->id_table_start);
copy.xattr_id_table_start = htole64(super->xattr_id_table_start);
copy.inode_table_start = htole64(super->inode_table_start);
copy.directory_table_start = htole64(super->directory_table_start);
copy.fragment_table_start = htole64(super->fragment_table_start);
copy.export_table_start = htole64(super->export_table_start);
off = lseek(outfd, 0, SEEK_SET);
if (off == (off_t)-1) {
perror("squashfs writing super block: seek on output file");
return -1;
}
ret = write_retry(outfd, &copy, sizeof(copy));
if (ret < 0) {
perror("squashfs writing super block");
return -1;
}
if (ret == 0) {
fputs("squashfs writing super block: truncated write\n",
stderr);
return -1;
}
return 0;
}
compressor_stream_t *sqfs_get_compressor(sqfs_super_t *s)
{
PKG_COMPRESSION id;
compressor_t *cmp;
size_t xz_dictsz;
void *options;
switch (s->compression_id) {
case SQFS_COMP_GZIP:
id = PKG_COMPRESSION_ZLIB;
options = NULL;
break;
case SQFS_COMP_XZ:
id = PKG_COMPRESSION_LZMA;
xz_dictsz = s->block_size;
options = &xz_dictsz;
break;
default:
fputs("unsupported compressor\n", stderr);
return NULL;
}
cmp = compressor_by_id(id);
if (cmp == NULL)
return NULL;
return cmp->compression_stream(cmp, options);
}

View File

@ -1,92 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "sqfs/squashfs.h"
#include "util/util.h"
#include <endian.h>
#include <stdio.h>
static int sqfs_write_table(int outfd, sqfs_super_t *super, const void *data,
size_t entsize, size_t count, uint64_t *startblock,
compressor_stream_t *strm)
{
size_t ent_per_blocks = SQFS_META_BLOCK_SIZE / entsize;
uint64_t blocks[count / ent_per_blocks + 1];
size_t i, blkidx = 0, tblsize;
meta_writer_t *m;
ssize_t ret;
/* Write actual data. Whenever we cross a block boundary, remember
the block start offset */
m = meta_writer_create(outfd, strm);
if (m == NULL)
return -1;
for (i = 0; i < count; ++i) {
if (blkidx == 0 || m->written > blocks[blkidx - 1])
blocks[blkidx++] = m->written;
if (meta_writer_append(m, data, entsize))
goto fail;
data = (const char *)data + entsize;
}
if (meta_writer_flush(m))
goto fail;
for (i = 0; i < blkidx; ++i)
blocks[i] = htole64(blocks[i] + super->bytes_used);
super->bytes_used += m->written;
meta_writer_destroy(m);
/* write new index table */
*startblock = super->bytes_used;
tblsize = sizeof(blocks[0]) * blkidx;
ret = write_retry(outfd, blocks, tblsize);
if (ret < 0) {
perror("writing index table");
return -1;
}
if ((size_t)ret < tblsize) {
fputs("index table truncated\n", stderr);
return -1;
}
super->bytes_used += tblsize;
return 0;
fail:
meta_writer_destroy(m);
return -1;
}
int sqfs_write_fragment_table(int outfd, sqfs_super_t *super,
sqfs_fragment_t *fragments, size_t count,
compressor_stream_t *strm)
{
super->fragment_entry_count = count;
return sqfs_write_table(outfd, super, fragments, sizeof(fragments[0]),
count, &super->fragment_table_start, strm);
}
int sqfs_write_ids(int outfd, sqfs_super_t *super,
uint32_t *id_tbl, size_t count,
compressor_stream_t *strm)
{
size_t i;
int ret;
for (i = 0; i < count; ++i)
id_tbl[i] = htole32(id_tbl[i]);
ret = sqfs_write_table(outfd, super, id_tbl, sizeof(id_tbl[0]),
count, &super->id_table_start, strm);
for (i = 0; i < count; ++i)
id_tbl[i] = htole32(id_tbl[i]);
return ret;
}

View File

@ -1,16 +0,0 @@
pkg2sqfs_SOURCES = sqfs/pkg2sqfs.c
pkg2sqfs_SOURCES += sqfs/vfs.c sqfs/pkg2sqfs.h sqfs/block.c
pkg2sqfs_SOURCES += sqfs/meta.c
pkg2sqfs_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/sqfs
pkg2sqfs_CFLAGS = $(AM_CFLAGS)
pkg2sqfs_LDADD = libsqfs.a libpkg.a libutil.a libfilelist.a libcomp.a
if WITH_LZMA
pkg2sqfs_LDADD += $(XZ_LIBS)
endif
if WITH_ZLIB
pkg2sqfs_LDADD += $(ZLIB_LIBS)
endif
bin_PROGRAMS += pkg2sqfs

View File

@ -1,225 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "pkg2sqfs.h"
static int write_block(file_info_t *file, sqfs_info_t *info,
compressor_stream_t *strm)
{
size_t idx, bs;
ssize_t ret;
void *ptr;
idx = info->file_block_count++;
bs = info->super.block_size;
ret = strm->do_block(strm, info->block, info->scratch, bs, bs);
if (ret < 0)
return -1;
if (ret > 0) {
ptr = info->scratch;
bs = ret;
file->blocksizes[idx] = bs;
} else {
ptr = info->block;
file->blocksizes[idx] = bs | (1 << 24);
}
ret = write_retry(info->outfd, ptr, bs);
if (ret < 0) {
perror("writing to output file");
return -1;
}
if ((size_t)ret < bs) {
fputs("write to output file truncated\n", stderr);
return -1;
}
info->super.bytes_used += bs;
return 0;
}
static int flush_fragments(sqfs_info_t *info, compressor_stream_t *strm)
{
size_t newsz, size;
file_info_t *fi;
uint64_t offset;
void *new, *ptr;
ssize_t ret;
if (info->num_fragments == info->max_fragments) {
newsz = info->max_fragments ? info->max_fragments * 2 : 16;
new = realloc(info->fragments,
sizeof(info->fragments[0]) * newsz);
if (new == NULL) {
perror("appending to fragment table");
return -1;
}
info->max_fragments = newsz;
info->fragments = new;
}
offset = info->super.bytes_used;
size = info->frag_offset;
for (fi = info->frag_list; fi != NULL; fi = fi->frag_next)
fi->fragment = info->num_fragments;
ret = strm->do_block(strm, info->fragment, info->scratch,
size, info->super.block_size);
if (ret < 0)
return -1;
info->fragments[info->num_fragments].start_offset = htole64(offset);
info->fragments[info->num_fragments].pad0 = 0;
if (ret > 0 && (size_t)ret < size) {
size = ret;
info->fragments[info->num_fragments].size = htole32(size);
ptr = info->scratch;
} else {
info->fragments[info->num_fragments].size =
htole32(size | (1 << 24));
ptr = info->fragment;
}
info->num_fragments += 1;
ret = write_retry(info->outfd, ptr, size);
if (ret < 0) {
perror("writing to output file");
return -1;
}
if ((size_t)ret < size) {
fputs("write to output file truncated\n", stderr);
return -1;
}
memset(info->fragment, 0, info->super.block_size);
info->super.bytes_used += size;
info->frag_offset = 0;
info->frag_list = NULL;
info->super.flags &= ~SQFS_FLAG_NO_FRAGMENTS;
info->super.flags |= SQFS_FLAG_ALWAYS_FRAGMENTS;
return 0;
}
static int add_fragment(file_info_t *fi, sqfs_info_t *info, size_t size,
compressor_stream_t *strm)
{
if (info->frag_offset + size > info->super.block_size) {
if (flush_fragments(info, strm))
return -1;
}
fi->fragment_offset = info->frag_offset;
fi->frag_next = info->frag_list;
info->frag_list = fi;
memcpy((char *)info->fragment + info->frag_offset, info->block, size);
info->frag_offset += size;
return 0;
}
static int process_file(node_t *node, sqfs_info_t *info,
compressor_stream_t *strm)
{
uint64_t count = node->data.file->size;
int ret;
size_t diff;
node->data.file->startblock = info->super.bytes_used;
info->file_block_count = 0;
while (count != 0) {
diff = count > (uint64_t)info->super.block_size ?
info->super.block_size : count;
ret = pkg_reader_read_payload(info->rd, info->block, diff);
if (ret < 0)
return -1;
if ((size_t)ret < diff)
goto fail_trunc;
if (diff < info->super.block_size) {
if (add_fragment(node->data.file, info, diff, strm))
return -1;
} else {
if (write_block(node->data.file, info, strm))
return -1;
}
count -= diff;
}
return 0;
fail_trunc:
fprintf(stderr, "%s: truncated data block\n",
pkg_reader_get_filename(info->rd));
return -1;
}
int pkg_data_to_sqfs(sqfs_info_t *info, compressor_stream_t *strm)
{
file_data_t frec;
record_t *hdr;
node_t *node;
int ret;
if (pkg_reader_rewind(info->rd))
return -1;
memset(info->fragment, 0, info->super.block_size);
for (;;) {
ret = pkg_reader_get_next_record(info->rd);
if (ret == 0)
break;
if (ret < 0)
return -1;
hdr = pkg_reader_current_record_header(info->rd);
if (hdr->magic != PKG_MAGIC_DATA)
continue;
for (;;) {
ret = pkg_reader_read_payload(info->rd, &frec,
sizeof(frec));
if (ret == 0)
break;
if (ret < 0)
return -1;
if ((size_t)ret < sizeof(frec))
goto fail_trunc;
frec.id = le32toh(frec.id);
node = vfs_node_from_file_id(&info->fs, frec.id);
if (node == NULL)
goto fail_meta;
if (process_file(node, info, strm))
return -1;
}
}
if (info->frag_offset != 0) {
if (flush_fragments(info, strm))
return -1;
}
return 0;
fail_meta:
fprintf(stderr, "%s: missing meta information for file %u\n",
pkg_reader_get_filename(info->rd), (unsigned int)frec.id);
return -1;
fail_trunc:
fprintf(stderr, "%s: truncated file data record\n",
pkg_reader_get_filename(info->rd));
return -1;
}

View File

@ -1,297 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "pkg2sqfs.h"
static size_t hard_link_count(node_t *node)
{
size_t count;
node_t *n;
if (S_ISDIR(node->mode)) {
count = 2;
for (n = node->data.dir->children; n != NULL; n = n->next)
++count;
return count;
}
return 1;
}
static int write_dir(meta_writer_t *dm, dir_info_t *dir)
{
sqfs_dir_header_t hdr;
sqfs_dir_entry_t ent;
size_t i, count;
node_t *c, *d;
c = dir->children;
dir->size = 0;
dir->dref = (dm->written << 16) | dm->offset;
while (c != NULL) {
count = 0;
for (d = c; d != NULL; d = d->next) {
if ((d->inode_ref >> 16) != (c->inode_ref >> 16))
break;
if ((d->inode_num - c->inode_num) > 0xFFFF)
break;
count += 1;
}
if (count > SQFS_MAX_DIR_ENT)
count = SQFS_MAX_DIR_ENT;
hdr.count = htole32(count - 1);
hdr.start_block = htole32(c->inode_ref >> 16);
hdr.inode_number = htole32(c->inode_num);
dir->size += sizeof(hdr);
if (meta_writer_append(dm, &hdr, sizeof(hdr)))
return -1;
d = c;
for (i = 0; i < count; ++i) {
ent.offset = htole16(c->inode_ref & 0x0000FFFF);
ent.inode_number = htole16(c->inode_num - d->inode_num);
ent.type = htole16(c->type);
ent.size = htole16(strlen(c->name) - 1);
dir->size += sizeof(ent) + strlen(c->name);
if (meta_writer_append(dm, &ent, sizeof(ent)))
return -1;
if (meta_writer_append(dm, c->name, strlen(c->name)))
return -1;
c = c->next;
}
}
return 0;
}
static int write_inode(sqfs_info_t *info, meta_writer_t *im, meta_writer_t *dm,
node_t *node)
{
file_info_t *fi = NULL;
sqfs_inode_t base;
uint32_t bs;
uint64_t i;
node->inode_ref = (im->written << 16) | im->offset;
base.mode = htole16(node->mode);
base.uid_idx = htole16(node->uid_idx);
base.gid_idx = htole16(node->gid_idx);
base.mod_time = htole32(info->super.modification_time);
base.inode_number = htole32(node->inode_num);
switch (node->mode & S_IFMT) {
case S_IFLNK: node->type = SQFS_INODE_SLINK; break;
case S_IFBLK: node->type = SQFS_INODE_BDEV; break;
case S_IFCHR: node->type = SQFS_INODE_CDEV; break;
case S_IFDIR:
if (write_dir(dm, node->data.dir))
return -1;
node->type = SQFS_INODE_DIR;
i = node->data.dir->dref;
if ((i >> 16) > 0xFFFFFFFFUL || node->data.dir->size > 0xFFFF)
node->type = SQFS_INODE_EXT_DIR;
break;
case S_IFREG:
fi = node->data.file;
node->type = SQFS_INODE_FILE;
if (fi->startblock > 0xFFFFFFFFUL || fi->size > 0xFFFFFFFFUL ||
hard_link_count(node) > 1) {
node->type = SQFS_INODE_EXT_FILE;
}
break;
default:
assert(0);
}
base.type = htole16(node->type);
if (meta_writer_append(im, &base, sizeof(base)))
return -1;
switch (node->type) {
case SQFS_INODE_SLINK: {
sqfs_inode_slink_t slink = {
.nlink = htole32(hard_link_count(node)),
.target_size = htole32(strlen(node->data.symlink)),
};
if (meta_writer_append(im, &slink, sizeof(slink)))
return -1;
if (meta_writer_append(im, node->data.symlink,
le32toh(slink.target_size))) {
return -1;
}
break;
}
case SQFS_INODE_BDEV:
case SQFS_INODE_CDEV: {
sqfs_inode_dev_t dev = {
.nlink = htole32(hard_link_count(node)),
.devno = htole32(node->data.device),
};
return meta_writer_append(im, &dev, sizeof(dev));
}
case SQFS_INODE_EXT_FILE: {
sqfs_inode_file_ext_t ext = {
.blocks_start = htole64(fi->startblock),
.file_size = htole64(fi->size),
.sparse = htole64(0xFFFFFFFFFFFFFFFFUL),
.nlink = htole32(hard_link_count(node)),
.fragment_idx = htole32(fi->fragment),
.fragment_offset = htole32(fi->fragment_offset),
.xattr_idx = htole32(0xFFFFFFFF),
};
if (meta_writer_append(im, &ext, sizeof(ext)))
return -1;
goto out_file_blocks;
}
case SQFS_INODE_FILE: {
sqfs_inode_file_t reg = {
.blocks_start = htole32(fi->startblock),
.fragment_index = htole32(fi->fragment),
.fragment_offset = htole32(fi->fragment_offset),
.file_size = htole32(fi->size),
};
if (meta_writer_append(im, &reg, sizeof(reg)))
return -1;
goto out_file_blocks;
}
case SQFS_INODE_DIR: {
sqfs_inode_dir_t dir = {
.start_block = htole32(node->data.dir->dref >> 16),
.nlink = htole32(hard_link_count(node)),
.size = htole16(node->data.dir->size),
.offset = htole16(node->data.dir->dref & 0xFFFF),
.parent_inode = node->parent ?
htole32(node->parent->inode_num) : htole32(1),
};
return meta_writer_append(im, &dir, sizeof(dir));
}
case SQFS_INODE_EXT_DIR: {
sqfs_inode_dir_ext_t ext = {
.nlink = htole32(hard_link_count(node)),
.size = htole32(node->data.dir->size),
.start_block = htole32(node->data.dir->dref >> 16),
.parent_inode = node->parent ?
htole32(node->parent->inode_num) : htole32(1),
.inodex_count = htole32(0),
.offset = htole16(node->data.dir->dref & 0xFFFF),
.xattr_idx = htole32(0xFFFFFFFF),
};
return meta_writer_append(im, &ext, sizeof(ext));
}
default:
assert(0);
}
return 0;
out_file_blocks:
for (i = 0; i < fi->size / info->super.block_size; ++i) {
bs = htole32(fi->blocksizes[i]);
if (meta_writer_append(im, &bs, sizeof(bs)))
return -1;
}
return 0;
}
int sqfs_write_inodes(sqfs_info_t *info, compressor_stream_t *strm)
{
meta_writer_t *im, *dm;
size_t i, diff;
ssize_t ret;
FILE *tmp;
int tmpfd;
tmp = tmpfile();
if (tmp == NULL) {
perror("tmpfile");
return -1;
}
tmpfd = fileno(tmp);
im = meta_writer_create(info->outfd, strm);
if (im == NULL)
goto fail_tmp;
dm = meta_writer_create(tmpfd, strm);
if (dm == NULL)
goto fail_im;
for (i = 2; i < info->fs.num_inodes; ++i) {
if (write_inode(info, im, dm, info->fs.node_tbl[i]))
goto fail;
}
if (meta_writer_flush(im))
goto fail;
if (meta_writer_flush(dm))
goto fail;
info->super.root_inode_ref = info->fs.root->inode_ref;
info->super.inode_table_start = info->super.bytes_used;
info->super.bytes_used += im->written;
info->super.directory_table_start = info->super.bytes_used;
info->super.bytes_used += dm->written;
if (lseek(tmpfd, 0, SEEK_SET) == (off_t)-1) {
perror("rewind on directory temp file");
goto fail;
}
for (;;) {
ret = read_retry(tmpfd, dm->data, sizeof(dm->data));
if (ret < 0) {
perror("read from temp file");
goto fail;
}
if (ret == 0)
break;
diff = ret;
ret = write_retry(info->outfd, dm->data, diff);
if (ret < 0) {
perror("write to image file");
goto fail;
}
if ((size_t)ret < diff) {
fputs("copying meta data to image file: "
"truncated write\n", stderr);
goto fail;
}
}
meta_writer_destroy(dm);
meta_writer_destroy(im);
fclose(tmp);
return 0;
fail:
meta_writer_destroy(dm);
fail_im:
meta_writer_destroy(im);
fail_tmp:
fclose(tmp);
return -1;
}

View File

@ -1,316 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include "pkg2sqfs.h"
static struct option long_opts[] = {
{ "timestamp", required_argument, NULL, 't' },
{ "compressor", required_argument, NULL, 'c' },
{ "block-size", required_argument, NULL, 'b' },
{ "dev-block-size", required_argument, NULL, 'B' },
{ "default-uid", required_argument, NULL, 'u' },
{ "default-gid", required_argument, NULL, 'g' },
{ "default-mode", required_argument, NULL, 'm' },
{ "force", no_argument, NULL, 'f' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
};
static const char *short_opts = "t:c:b:B:u:g:m:fhV";
extern char *__progname;
static const char *version_string =
"%s (Pygos %s) %s\n"
"Copyright (c) 2019 David Oberhollenzer\n"
"License ISC <https://opensource.org/licenses/ISC>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by David Oberhollenzer.\n";
static const char *help_string =
"Usage: %s [OPTIONS] <package-file> <squashfs-file>\n"
"\n"
"Convert a package file into a squashfs image.\n"
"\n"
"Possible options:\n"
"\n"
" --compressor, -c <name> Select the compressor to use.\n"
" directories (defaults to 'xz').\n"
" --timestamp, -t <value> Set timestamp for all inodes and image file.\n"
" Defaults to 0 (Jan 1st, 1970).\n"
" --block-size, -b <size> Block size to use for Squashfs image.\n"
" Defaults to %u.\n"
" --dev-block-size, -B <size> Device block size to padd the image to.\n"
" Defaults to %u.\n"
" --default-uid, -u <valie> Default user ID for implicitly created\n"
" directories (defaults to 0).\n"
" --default-gid, -g <value> Default group ID for implicitly created\n"
" directories (defaults to 0).\n"
" --default-mode, -m <value> Default permissions for implicitly created\n"
" directories (defaults to 0755).\n"
" --force, -f Overwrite the output file if it exists.\n"
" --help, -h Print help text and exit.\n"
" --version, -V Print version information and exit.\n"
"\n";
static const char *compressors[] = {
[SQFS_COMP_GZIP] = "gzip",
[SQFS_COMP_LZMA] = "lzma",
[SQFS_COMP_LZO] = "lzo",
[SQFS_COMP_XZ] = "xz",
[SQFS_COMP_LZ4] = "lz4",
[SQFS_COMP_ZSTD] = "zstd",
};
static long read_number(const char *name, const char *str, long min, long max)
{
long base = 10, result = 0;
int x;
if (str[0] == '0') {
if (str[1] == 'x' || str[1] == 'X') {
base = 16;
str += 2;
} else {
base = 8;
}
}
if (!isxdigit(*str))
goto fail_num;
while (isxdigit(*str)) {
x = *(str++);
if (isupper(x)) {
x = x - 'A' + 10;
} else if (islower(x)) {
x = x - 'a' + 10;
} else {
x -= '0';
}
if (x >= base)
goto fail_num;
if (result > (LONG_MAX - x) / base)
goto fail_ov;
result = result * base + x;
}
if (result < min)
goto fail_uf;
if (result > max)
goto fail_ov;
return result;
fail_num:
fprintf(stderr, "%s: expected numeric value > 0\n", name);
goto fail;
fail_uf:
fprintf(stderr, "%s: number to small\n", name);
goto fail;
fail_ov:
fprintf(stderr, "%s: number to large\n", name);
goto fail;
fail:
exit(EXIT_FAILURE);
}
static int sqfs_padd_file(sqfs_info_t *info)
{
size_t padd_sz = info->super.bytes_used % info->dev_blk_size;
uint8_t *buffer;
ssize_t ret;
off_t off;
if (padd_sz == 0)
return 0;
off = lseek(info->outfd, 0, SEEK_END);
if (off == (off_t)-1) {
perror("seek on output file");
return -1;
}
padd_sz = info->dev_blk_size - padd_sz;
buffer = alloca(padd_sz);
memset(buffer, 0, padd_sz);
ret = write_retry(info->outfd, buffer, padd_sz);
if (ret < 0) {
perror("Error padding squashfs image to page size");
return -1;
}
if (ret == 0) {
fputs("Truncated write trying to padd squashfs image\n",
stderr);
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
uint32_t blocksize = SQFS_DEFAULT_BLOCK_SIZE, timestamp = 0;
int i, outmode = O_WRONLY | O_CREAT | O_EXCL;
E_SQFS_COMPRESSOR compressor = SQFS_COMP_XZ;
const char *infile, *outfile;
int status = EXIT_FAILURE;
compressor_stream_t *cmp;
sqfs_info_t info;
memset(&info, 0, sizeof(info));
info.dev_blk_size = SQFS_DEVBLK_SIZE;
info.fs.default_uid = 0;
info.fs.default_gid = 0;
info.fs.default_mode = 0755;
for (;;) {
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
if (i == -1)
break;
switch (i) {
case 't':
timestamp = read_number("Timestamp", optarg,
0, 0xFFFFFFFF);
break;
case 'c':
for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) {
if (strcmp(compressors[i], optarg) == 0) {
compressor = i;
break;
}
}
break;
case 'b':
blocksize = read_number("Block size", optarg,
1024, 0xFFFFFFFF);
break;
case 'B':
info.dev_blk_size = read_number("Device block size",
optarg, 4096,
0xFFFFFFFF);
break;
case 'u':
info.fs.default_uid = read_number("User ID", optarg,
0, 0xFFFFFFFF);
break;
case 'g':
info.fs.default_gid = read_number("Group ID", optarg,
0, 0xFFFFFFFF);
break;
case 'm':
info.fs.default_mode = read_number("Permissions",
optarg, 0, 0777);
break;
case 'f':
outmode = O_WRONLY | O_CREAT | O_TRUNC;
break;
case 'h':
printf(help_string, __progname,
SQFS_DEFAULT_BLOCK_SIZE, SQFS_DEVBLK_SIZE);
fputs("Available compressors:\n", stdout);
for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i)
printf("\t%s\n", compressors[i]);
exit(EXIT_SUCCESS);
case 'V':
printf(version_string, __progname,
PACKAGE_NAME, PACKAGE_VERSION);
exit(EXIT_SUCCESS);
default:
goto fail_arg;
}
}
if ((optind + 1) >= argc) {
fputs("Missing arguments: input and output files.\n", stderr);
goto fail_arg;
}
infile = argv[optind++];
outfile = argv[optind++];
info.rd = pkg_reader_open(infile);
if (info.rd == NULL)
return EXIT_FAILURE;
info.outfd = open(outfile, outmode, 0644);
if (info.outfd < 0) {
perror(outfile);
goto out_pkg_close;
}
if (sqfs_super_init(&info.super, timestamp, blocksize, compressor))
goto out_close;
cmp = sqfs_get_compressor(&info.super);
if (cmp == NULL)
goto out_close;
info.block = malloc(info.super.block_size * 3);
if (info.block == NULL) {
perror("malloc");
goto out_cmp;
}
info.scratch = (char *)info.block + info.super.block_size;
info.fragment = (char *)info.block + 2 * info.super.block_size;
if (create_vfs_tree(&info))
goto out_buffer;
if (sqfs_super_write(&info.super, info.outfd))
goto out_tree;
if (pkg_data_to_sqfs(&info, cmp))
goto out_fragments;
free(info.block);
info.block = NULL;
if (sqfs_write_inodes(&info, cmp))
goto out_fragments;
if (sqfs_write_fragment_table(info.outfd, &info.super,
info.fragments, info.num_fragments,
cmp))
goto out_fragments;
if (sqfs_write_ids(info.outfd, &info.super,
info.fs.id_tbl, info.fs.num_ids, cmp))
goto out_fragments;
if (sqfs_super_write(&info.super, info.outfd))
goto out_fragments;
if (sqfs_padd_file(&info))
goto out_fragments;
status = EXIT_SUCCESS;
out_fragments:
free(info.fragments);
out_tree:
destroy_vfs_tree(&info.fs);
out_buffer:
free(info.block);
out_cmp:
cmp->destroy(cmp);
out_close:
close(info.outfd);
out_pkg_close:
pkg_reader_close(info.rd);
return status;
fail_arg:
fprintf(stderr, "Try `%s --help' for more information.\n", __progname);
return EXIT_FAILURE;
}

View File

@ -1,114 +0,0 @@
/* SPDX-License-Identifier: ISC */
#ifndef PKG2SQFS_H
#define PKG2SQFS_H
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdint.h>
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include "util/util.h"
#include "filelist/image_entry.h"
#include "comp/compressor.h"
#include "pkg/pkgreader.h"
#include "sqfs/squashfs.h"
#include "pkg/pkgio.h"
#include "config.h"
typedef struct file_info_t {
struct file_info_t *frag_next;
uint64_t size;
uint64_t startblock;
uint32_t id;
uint32_t fragment;
uint32_t fragment_offset;
uint32_t blocksizes[];
} file_info_t;
typedef struct {
struct node_t *children;
uint64_t dref;
size_t size;
} dir_info_t;
typedef struct node_t {
struct node_t *parent;
struct node_t *next;
const char *name;
union {
dir_info_t *dir;
const char *symlink;
dev_t device;
file_info_t *file;
} data;
uint64_t inode_ref;
uint32_t inode_num;
uint16_t type;
uint16_t mode;
uint16_t uid_idx;
uint16_t gid_idx;
uint8_t extra[];
} node_t;
typedef struct {
uint32_t default_uid;
uint32_t default_gid;
uint32_t default_mode;
node_t **node_tbl;
size_t num_inodes;
uint32_t *id_tbl;
size_t num_ids;
size_t max_ids;
node_t *root;
} vfs_t;
typedef struct {
int outfd;
sqfs_super_t super;
vfs_t fs;
pkg_reader_t *rd;
void *block;
void *scratch;
void *fragment;
sqfs_fragment_t *fragments;
size_t num_fragments;
size_t max_fragments;
int file_block_count;
file_info_t *frag_list;
size_t frag_offset;
size_t dev_blk_size;
} sqfs_info_t;
int pkg_data_to_sqfs(sqfs_info_t *info, compressor_stream_t *strm);
int create_vfs_tree(sqfs_info_t *info);
void destroy_vfs_tree(vfs_t *fs);
node_t *vfs_node_from_file_id(vfs_t *fs, uint32_t id);
int sqfs_write_inodes(sqfs_info_t *info, compressor_stream_t *strm);
#endif /* PKG2SQFS_H */

View File

@ -1,385 +0,0 @@
/* SPDX-License-Identifier: ISC */
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "pkg2sqfs.h"
static int id_to_index(vfs_t *fs, uint32_t id, uint16_t *out)
{
size_t i, new_sz;
void *new;
for (i = 0; i < fs->num_ids; ++i) {
if (fs->id_tbl[i] == id) {
*out = i;
return 0;
}
}
if (fs->num_ids > 0xFFFF) {
fputs("too many unique UIDs/GIDs (more than 64k)!\n", stderr);
return -1;
}
if (fs->num_ids == fs->max_ids) {
new_sz = fs->max_ids + 10;
new = realloc(fs->id_tbl, new_sz * sizeof(fs->id_tbl[0]));
if (new == NULL) {
perror("expanding ID table");
return -1;
}
fs->max_ids = new_sz;
fs->id_tbl = new;
}
*out = fs->num_ids;
fs->id_tbl[fs->num_ids++] = id;
return 0;
}
static node_t *node_create(vfs_t *fs, node_t *parent, size_t extra,
const char *name, size_t name_len,
uint16_t mode, uint32_t uid, uint32_t gid)
{
node_t *n = calloc(1, sizeof(*n) + extra + name_len + 1);
if (n == NULL) {
perror("allocating vfs node");
return NULL;
}
if (parent != NULL) {
n->parent = parent;
n->next = parent->data.dir->children;
parent->data.dir->children = n;
}
if (id_to_index(fs, uid, &n->uid_idx))
return NULL;
if (id_to_index(fs, gid, &n->gid_idx))
return NULL;
n->mode = mode;
n->name = (char *)n->extra;
memcpy(n->extra, name, name_len);
return n;
}
static node_t *node_from_ent(vfs_t *fs, sqfs_super_t *s, image_entry_t *ent)
{
node_t *n, *parent = fs->root;
const char *path = ent->name;
size_t len, extra;
char *end;
next:
end = strchrnul(path, '/');
len = end - path;
for (n = parent->data.dir->children; n != NULL; n = n->next) {
if (!strncmp(n->name, path, len) && strlen(n->name) == len) {
if (path[len] != '/')
goto fail_exists;
if (!S_ISDIR(n->mode))
goto fail_not_dir;
parent = n;
path += len + 1;
goto next;
}
}
if (path[len] == '/') {
if (S_ISDIR(ent->mode)) {
n = node_create(fs, parent, sizeof(dir_info_t),
path, len, S_IFDIR | fs->default_mode,
fs->default_uid, fs->default_gid);
if (n == NULL)
return NULL;
n->data.dir =
(dir_info_t *)(n->name + strlen(n->name) + 1);
parent = n;
path += len + 1;
goto next;
}
goto fail_no_ent;
}
switch (ent->mode & S_IFMT) {
case S_IFREG:
extra = sizeof(file_info_t);
extra += (ent->data.file.size / s->block_size) *
sizeof(uint32_t);
break;
case S_IFLNK:
extra = strlen(ent->data.symlink.target) + 1;
break;
case S_IFDIR:
extra = sizeof(dir_info_t);
break;
default:
extra = 0;
break;
}
n = node_create(fs, parent, extra, path, len,
ent->mode, ent->uid, ent->gid);
if (n == NULL)
return NULL;
switch (ent->mode & S_IFMT) {
case S_IFREG:
n->data.file = (file_info_t *)(n->name + strlen(n->name) + 1);
n->data.file->size = ent->data.file.size;
n->data.file->id = ent->data.file.id;
n->data.file->startblock = 0xFFFFFFFFFFFFFFFFUL;
n->data.file->fragment = 0xFFFFFFFFL;
break;
case S_IFLNK:
n->data.symlink = n->name + strlen(n->name) + 1;
strcpy((char *)n->data.symlink, ent->data.symlink.target);
break;
case S_IFBLK:
case S_IFCHR:
n->data.device = ent->data.device.devno;
break;
case S_IFDIR:
n->data.dir = (dir_info_t *)(n->name + strlen(n->name) + 1);
break;
default:
break;
}
return n;
fail_no_ent:
fprintf(stderr, "cannot create /%s: '%.*s' does not exist\n",
ent->name, (int)len, path);
return NULL;
fail_exists:
fprintf(stderr, "cannot create /%s: already exists\n", ent->name);
return NULL;
fail_not_dir:
fprintf(stderr, "cannot create /%s: %.*s is not a directory\n",
ent->name, (int)len, path);
return NULL;
}
static void node_recursive_delete(node_t *n)
{
node_t *child;
if (n != NULL && S_ISDIR(n->mode)) {
while (n->data.dir->children != NULL) {
child = n->data.dir->children;
n->data.dir->children = child->next;
node_recursive_delete(child);
}
}
free(n);
}
typedef int(*node_cb)(vfs_t *fs, node_t *n, void *user);
static int foreach_node(vfs_t *fs, node_t *root, void *user, node_cb cb)
{
bool has_subdirs = false;
node_t *it;
for (it = root->data.dir->children; it != NULL; it = it->next) {
if (S_ISDIR(it->mode)) {
has_subdirs = true;
break;
}
}
if (has_subdirs) {
for (it = root->data.dir->children; it != NULL; it = it->next) {
if (!S_ISDIR(it->mode))
continue;
if (foreach_node(fs, it, user, cb) != 0)
return -1;
}
}
for (it = root->data.dir->children; it != NULL; it = it->next) {
if (cb(fs, it, user))
return -1;
}
if (root->parent == NULL) {
if (cb(fs, root, user))
return -1;
}
return 0;
}
static int alloc_inum(vfs_t *fs, node_t *n, void *user)
{
uint32_t *counter = user;
(void)fs;
if (*counter == 0xFFFFFFFF) {
fputs("too many inodes (more than 2^32 - 2)!\n", stderr);
return -1;
}
n->inode_num = (*counter)++;
return 0;
}
static int link_node_to_tbl(vfs_t *fs, node_t *n, void *user)
{
(void)user;
fs->node_tbl[n->inode_num] = n;
return 0;
}
static node_t *sort_list(node_t *head)
{
node_t *it, *prev, *lhs, *rhs;
size_t i, count = 0;
for (it = head; it != NULL; it = it->next)
++count;
if (count < 2)
return head;
prev = NULL;
it = head;
for (i = 0; i < count / 2; ++i) {
prev = it;
it = it->next;
}
prev->next = NULL;
lhs = sort_list(head);
rhs = sort_list(it);
head = NULL;
prev = NULL;
while (lhs != NULL && rhs != NULL) {
if (strcmp(lhs->name, rhs->name) <= 0) {
it = lhs;
lhs = lhs->next;
} else {
it = rhs;
rhs = rhs->next;
}
it->next = NULL;
if (prev != NULL) {
prev->next = it;
prev = it;
} else {
prev = head = it;
}
}
it = (lhs != NULL ? lhs : rhs);
if (prev != NULL) {
prev->next = it;
} else {
head = it;
}
return head;
}
static void sort_directory(node_t *n)
{
n->data.dir->children = sort_list(n->data.dir->children);
for (n = n->data.dir->children; n != NULL; n = n->next) {
if (S_ISDIR(n->mode))
sort_directory(n);
}
}
int create_vfs_tree(sqfs_info_t *info)
{
image_entry_t *ent, *list;
uint32_t counter = 2;
vfs_t *fs = &info->fs;
node_t *n;
if (image_entry_list_from_package(info->rd, &list))
return -1;
fs->root = node_create(fs, NULL, sizeof(dir_info_t),
"", 0, S_IFDIR | 0755, 0, 0);
if (fs->root == NULL)
goto fail;
fs->root->data.dir = (dir_info_t *)(fs->root->name + strlen(fs->root->name) + 1);
for (ent = list; ent != NULL; ent = ent->next) {
n = node_from_ent(fs, &info->super, ent);
if (n == NULL)
goto fail;
}
sort_directory(fs->root);
if (foreach_node(fs, fs->root, &counter, alloc_inum))
goto fail;
fs->num_inodes = counter;
fs->node_tbl = calloc(fs->num_inodes, sizeof(fs->node_tbl[0]));
if (fs->node_tbl == NULL)
goto fail_oom;
if (foreach_node(fs, fs->root, NULL, link_node_to_tbl))
goto fail;
info->super.inode_count = fs->num_inodes - 2;
info->super.id_count = fs->num_ids;
image_entry_free_list(list);
return 0;
fail_oom:
fputs("out of memory\n", stderr);
fail:
image_entry_free_list(list);
free(fs->node_tbl);
free(fs->id_tbl);
node_recursive_delete(fs->root);
return -1;
}
void destroy_vfs_tree(vfs_t *fs)
{
free(fs->node_tbl);
free(fs->id_tbl);
node_recursive_delete(fs->root);
}
node_t *vfs_node_from_file_id(vfs_t *fs, uint32_t id)
{
size_t i;
for (i = 0; i < fs->num_inodes; ++i) {
if (fs->node_tbl[i] == NULL)
continue;
if (!S_ISREG(fs->node_tbl[i]->mode))
continue;
if (fs->node_tbl[i]->data.file->id == id)
return fs->node_tbl[i];
}
return NULL;
}