diff options
| author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-06-10 22:10:46 +0200 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-06-10 22:10:58 +0200 | 
| commit | 69de75de97c17ef7370b26944ee286e4b3dc6266 (patch) | |
| tree | 3f35ecefdb56aaae6fc806a099dba50777f01a3d /lib | |
| parent | 41fd1d4b9cd42eb28bf889719b8ce1a81af444b9 (diff) | |
cleanup: move entire fstree serialization to libsqfs.a
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makemodule.am | 2 | ||||
| -rw-r--r-- | lib/sqfs/serialize_fstree.c | 100 | ||||
| -rw-r--r-- | lib/sqfs/write_dir.c | 127 | ||||
| -rw-r--r-- | lib/sqfs/write_inode.c | 292 | 
4 files changed, 521 insertions, 0 deletions
diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 536cad0..bf4ed61 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -17,6 +17,8 @@ libsquashfs_a_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c  libsquashfs_a_SOURCES += include/meta_reader.h lib/sqfs/id_table_write.c  libsquashfs_a_SOURCES += lib/sqfs/id_table_read.c lib/sqfs/read_inode.c  libsquashfs_a_SOURCES += lib/sqfs/readdir.c lib/sqfs/frag_reader.c +libsquashfs_a_SOURCES += lib/sqfs/write_dir.c lib/sqfs/write_inode.c +libsquashfs_a_SOURCES += lib/sqfs/serialize_fstree.c  libsquashfs_a_SOURCES += include/frag_reader.h  libutil_a_SOURCES = lib/util/canonicalize_name.c lib/util/write_retry.c diff --git a/lib/sqfs/serialize_fstree.c b/lib/sqfs/serialize_fstree.c new file mode 100644 index 0000000..95b9144 --- /dev/null +++ b/lib/sqfs/serialize_fstree.c @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "meta_writer.h" +#include "table.h" +#include "util.h" + +#include <unistd.h> +#include <stdio.h> + +int sqfs_serialize_fstree(int outfd, sqfs_super_t *super, fstree_t *fs, +			  compressor_t *cmp, id_table_t *idtbl) +{ +	meta_writer_t *im, *dm; +	uint8_t buffer[1024]; +	uint32_t offset; +	uint64_t block; +	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(outfd, cmp); +	if (im == NULL) +		goto fail_tmp; + +	dm = meta_writer_create(tmpfd, cmp); +	if (dm == NULL) +		goto fail_im; + +	for (i = 2; i < fs->inode_tbl_size; ++i) { +		if (meta_writer_write_inode(fs, idtbl, im, dm, +					    fs->inode_table[i])) { +			goto fail; +		} +	} + +	if (meta_writer_flush(im)) +		goto fail; + +	if (meta_writer_flush(dm)) +		goto fail; + +	super->root_inode_ref = fs->root->inode_ref; + +	meta_writer_get_position(im, &block, &offset); +	super->inode_table_start = super->bytes_used; +	super->bytes_used += block; + +	super->directory_table_start = super->bytes_used; +	meta_writer_get_position(dm, &block, &offset); +	super->bytes_used += block; + +	if (lseek(tmpfd, 0, SEEK_SET) == (off_t)-1) { +		perror("rewind on directory temp file"); +		goto fail; +	} + +	for (;;) { +		ret = read_retry(tmpfd, buffer, sizeof(buffer)); + +		if (ret < 0) { +			perror("read from temp file"); +			goto fail; +		} +		if (ret == 0) +			break; + +		diff = ret; +		ret = write_retry(outfd, buffer, 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; +} diff --git a/lib/sqfs/write_dir.c b/lib/sqfs/write_dir.c new file mode 100644 index 0000000..b334864 --- /dev/null +++ b/lib/sqfs/write_dir.c @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "meta_writer.h" +#include "util.h" + +#include <assert.h> +#include <endian.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +static int get_type(mode_t mode) +{ +	switch (mode & S_IFMT) { +	case S_IFSOCK: return SQFS_INODE_SOCKET; +	case S_IFIFO:  return SQFS_INODE_FIFO; +	case S_IFLNK:  return SQFS_INODE_SLINK; +	case S_IFBLK:  return SQFS_INODE_BDEV; +	case S_IFCHR:  return SQFS_INODE_CDEV; +	case S_IFDIR:  return SQFS_INODE_DIR; +	case S_IFREG:  return SQFS_INODE_FILE; +	default: +		assert(0); +	} +} + +static int dir_index_grow(dir_index_t **index) +{ +	size_t size = sizeof(dir_index_t) + sizeof(idx_ref_t) * 10; +	void *new; + +	if (*index == NULL) { +		new = calloc(1, size); +	} else { +		if ((*index)->num_nodes < (*index)->max_nodes) +			return 0; + +		size += sizeof(idx_ref_t) * (*index)->num_nodes; +		new = realloc(*index, size); +	} + +	if (new == NULL) { +		perror("creating directory index"); +		return -1; +	} + +	*index = new; +	(*index)->max_nodes += 10; +	return 0; +} + +int meta_writer_write_dir(meta_writer_t *dm, dir_info_t *dir, +			  dir_index_t **index) +{ +	size_t i, size, count; +	sqfs_dir_header_t hdr; +	sqfs_dir_entry_t ent; +	tree_node_t *c, *d; +	uint32_t offset; +	uint64_t block; + +	c = dir->children; +	dir->size = 0; + +	meta_writer_get_position(dm, &dir->start_block, &dir->block_offset); + +	while (c != NULL) { +		meta_writer_get_position(dm, &block, &offset); + +		count = 0; +		size = (offset + sizeof(hdr)) % SQFS_META_BLOCK_SIZE; + +		for (d = c; d != NULL; d = d->next) { +			if ((d->inode_ref >> 16) != (c->inode_ref >> 16)) +				break; + +			/* XXX: difference is actually signed */ +			if ((d->inode_num - c->inode_num) > 0x7FFF) +				break; + +			size += sizeof(ent) + strlen(c->name); + +			if (count > 0 && size > SQFS_META_BLOCK_SIZE) +				break; + +			count += 1; +		} + +		if (count > SQFS_MAX_DIR_ENT) +			count = SQFS_MAX_DIR_ENT; + +		if (dir_index_grow(index)) +			return -1; + +		meta_writer_get_position(dm, &block, &offset); + +		i = (*index)->num_nodes++; +		(*index)->idx_nodes[i].node = c; +		(*index)->idx_nodes[i].block = block; +		(*index)->idx_nodes[i].offset = offset; + +		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(get_type(c->mode)); +			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; +} diff --git a/lib/sqfs/write_inode.c b/lib/sqfs/write_inode.c new file mode 100644 index 0000000..961013c --- /dev/null +++ b/lib/sqfs/write_inode.c @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "meta_writer.h" +#include "util.h" + +#include <assert.h> +#include <endian.h> +#include <stdlib.h> +#include <string.h> + +static int get_type(tree_node_t *node) +{ +	int type; + +	switch (node->mode & S_IFMT) { +	case S_IFSOCK: type = SQFS_INODE_SOCKET; break; +	case S_IFIFO:  type = SQFS_INODE_FIFO; break; +	case S_IFLNK:  type = SQFS_INODE_SLINK; break; +	case S_IFBLK:  type = SQFS_INODE_BDEV; break; +	case S_IFCHR:  type = SQFS_INODE_CDEV; break; +	case S_IFDIR:  type = SQFS_INODE_DIR; break; +	case S_IFREG:  type = SQFS_INODE_FILE; break; +	default: +		assert(0); +	} + +	if (node->xattr != NULL) +		type = SQFS_INODE_EXT_TYPE(type); + +	return type; +} + +static size_t hard_link_count(tree_node_t *n) +{ +	size_t count; + +	if (S_ISDIR(n->mode)) { +		count = 2; + +		for (n = n->data.dir->children; n != NULL; n = n->next) +			++count; + +		return count; +	} + +	return 1; +} + +int meta_writer_write_inode(fstree_t *fs, id_table_t *idtbl, meta_writer_t *im, +			    meta_writer_t *dm, tree_node_t *node) +{ +	dir_index_t *diridx = NULL; +	uint16_t uid_idx, gid_idx; +	file_info_t *fi = NULL; +	dir_info_t *di = NULL; +	uint32_t bs, offset; +	sqfs_inode_t base; +	uint64_t i, block; +	int type; + +	if (id_table_id_to_index(idtbl, node->uid, &uid_idx)) +		return -1; + +	if (id_table_id_to_index(idtbl, node->gid, &gid_idx)) +		return -1; + +	meta_writer_get_position(im, &block, &offset); +	node->inode_ref = (block << 16) | offset; + +	type = get_type(node); + +	if (S_ISDIR(node->mode)) { +		di = node->data.dir; + +		if (meta_writer_write_dir(dm, di, &diridx)) +			return -1; + +		if ((di->start_block) > 0xFFFFFFFFUL || di->size > 0xFFFF || +		    node->xattr != NULL) { +			type = SQFS_INODE_EXT_DIR; +		} else { +			type = SQFS_INODE_DIR; +			free(diridx); +			diridx = NULL; +		} +	} else if (S_ISREG(node->mode)) { +		fi = node->data.file; + +		if (fi->startblock > 0xFFFFFFFFUL || fi->size > 0xFFFFFFFFUL || +		    hard_link_count(node) > 1) { +			type = SQFS_INODE_EXT_FILE; +		} +	} + +	base.type = htole16(type); +	base.mode = htole16(node->mode); +	base.uid_idx = htole16(uid_idx); +	base.gid_idx = htole16(gid_idx); +	base.mod_time = htole32(fs->default_mtime); +	base.inode_number = htole32(node->inode_num); + +	if (meta_writer_append(im, &base, sizeof(base))) { +		free(diridx); +		return -1; +	} + +	switch (type) { +	case SQFS_INODE_FIFO: +	case SQFS_INODE_SOCKET: { +		sqfs_inode_ipc_t ipc = { +			.nlink = hard_link_count(node), +		}; + +		return meta_writer_append(im, &ipc, sizeof(ipc)); +	} +	case SQFS_INODE_EXT_FIFO: +	case SQFS_INODE_EXT_SOCKET: { +		sqfs_inode_ipc_ext_t ipc = { +			.nlink = hard_link_count(node), +			.xattr_idx = htole32(0xFFFFFFFF), +		}; + +		if (node->xattr != NULL) +			ipc.xattr_idx = htole32(node->xattr->index); + +		return meta_writer_append(im, &ipc, sizeof(ipc)); +	} +	case SQFS_INODE_SLINK: { +		sqfs_inode_slink_t slink = { +			.nlink = htole32(hard_link_count(node)), +			.target_size = htole32(strlen(node->data.slink_target)), +		}; + +		if (meta_writer_append(im, &slink, sizeof(slink))) +			return -1; +		if (meta_writer_append(im, node->data.slink_target, +				       le32toh(slink.target_size))) { +			return -1; +		} +		break; +	} +	case SQFS_INODE_EXT_SLINK: { +		sqfs_inode_slink_t slink = { +			.nlink = htole32(hard_link_count(node)), +			.target_size = htole32(strlen(node->data.slink_target)), +		}; +		uint32_t xattr = htole32(0xFFFFFFFF); + +		if (node->xattr != NULL) +			xattr = htole32(node->xattr->index); + +		if (meta_writer_append(im, &slink, sizeof(slink))) +			return -1; +		if (meta_writer_append(im, node->data.slink_target, +				       le32toh(slink.target_size))) { +			return -1; +		} +		if (meta_writer_append(im, &xattr, sizeof(xattr))) +			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.devno), +		}; + +		return meta_writer_append(im, &dev, sizeof(dev)); +	} +	case SQFS_INODE_EXT_BDEV: +	case SQFS_INODE_EXT_CDEV: { +		sqfs_inode_dev_ext_t dev = { +			.nlink = htole32(hard_link_count(node)), +			.devno = htole32(node->data.devno), +			.xattr_idx = htole32(0xFFFFFFFF), +		}; + +		if (node->xattr != NULL) +			dev.xattr_idx = htole32(node->xattr->index); + +		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 (node->xattr != NULL) +			ext.xattr_idx = htole32(node->xattr->index); + +		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, ®, sizeof(reg))) +			return -1; +		goto out_file_blocks; +	} +	case SQFS_INODE_DIR: { +		sqfs_inode_dir_t dir = { +			.start_block = htole32(node->data.dir->start_block), +			.nlink = htole32(hard_link_count(node)), +			.size = htole16(node->data.dir->size), +			.offset = htole16(node->data.dir->block_offset), +			.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_dir_index_t idx; +		size_t i; +		sqfs_inode_dir_ext_t ext = { +			.nlink = htole32(hard_link_count(node)), +			.size = htole32(node->data.dir->size), +			.start_block = htole32(node->data.dir->start_block), +			.parent_inode = node->parent ? +				htole32(node->parent->inode_num) : htole32(1), +			.inodex_count = htole32(0), +			.offset = htole16(node->data.dir->block_offset), +			.xattr_idx = htole32(0xFFFFFFFF), +		}; + +		if (node->xattr != NULL) +			ext.xattr_idx = htole32(node->xattr->index); + +		if (meta_writer_append(im, &ext, sizeof(ext))) { +			free(diridx); +			return -1; +		} + +		/* HACK: truncated index for empty directories */ +		if (node->data.dir->size == 0) +			break; + +		ext.inodex_count = htole32(diridx->num_nodes - 1); + +		for (i = 0; i < diridx->num_nodes; ++i) { +			idx.start_block = htole32(diridx->idx_nodes[i].block); + +			idx.index = diridx->idx_nodes[i].offset; +			idx.index -= node->data.dir->block_offset; +			idx.index = htole32(idx.index); + +			idx.size = strlen(diridx->idx_nodes[i].node->name) - 1; +			idx.size = htole32(idx.size); + +			if (meta_writer_append(im, &idx, sizeof(idx))) { +				free(diridx); +				return -1; +			} + +			if (meta_writer_append(im, +					       diridx->idx_nodes[i].node->name, +					       le32toh(idx.size) + 1)) { +				free(diridx); +				return -1; +			} +		} + +		free(diridx); +		break; +	} +	default: +		assert(0); +	} +	return 0; +out_file_blocks: +	for (i = 0; i < fi->size / fs->block_size; ++i) { +		bs = htole32(fi->blocksizes[i]); + +		if (meta_writer_append(im, &bs, sizeof(bs))) +			return -1; +	} +	return 0; +}  | 
