All of lore.kernel.org
 help / color / mirror / Atom feed
From: Brian Masney <bmasney@redhat.com>
To: Alexander Larsson <alexl@redhat.com>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	gscrivan@redhat.com
Subject: Re: [PATCH 3/6] composefs: Add descriptor parsing code
Date: Thu, 5 Jan 2023 15:02:20 -0500	[thread overview]
Message-ID: <Y7cszNNvHHUef2qO@x1> (raw)
In-Reply-To: <1c4c49fac5bb6406a8cb55ca71f8060703aa63f6.1669631086.git.alexl@redhat.com>

On Mon, Nov 28, 2022 at 12:16:59PM +0100, Alexander Larsson wrote:
> This adds the code to load and decode the filesystem descriptor file
> format.
> 
> Signed-off-by: Alexander Larsson <alexl@redhat.com>
> Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
> ---
>  fs/composefs/cfs-internals.h |  65 +++
>  fs/composefs/cfs-reader.c    | 958 +++++++++++++++++++++++++++++++++++
>  2 files changed, 1023 insertions(+)
>  create mode 100644 fs/composefs/cfs-internals.h
>  create mode 100644 fs/composefs/cfs-reader.c
> 
> diff --git a/fs/composefs/cfs-internals.h b/fs/composefs/cfs-internals.h
> new file mode 100644
> index 000000000000..f4cb50eec9b8
> --- /dev/null
> +++ b/fs/composefs/cfs-internals.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _CFS_INTERNALS_H
> +#define _CFS_INTERNALS_H
> +
> +#include "cfs.h"
> +
> +#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
> +
> +#define CFS_N_PRELOAD_DIR_CHUNKS 4

From looking through the code it appears that this is actually the
maximum number of chunks. Should this be renamed from PRELOAD to MAX?

> diff --git a/fs/composefs/cfs-reader.c b/fs/composefs/cfs-reader.c
> new file mode 100644
> index 000000000000..ad77ef0bd4d4
> --- /dev/null
> +++ b/fs/composefs/cfs-reader.c
> @@ -0,0 +1,958 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * composefs
> + *
> + * Copyright (C) 2021 Giuseppe Scrivano
> + * Copyright (C) 2022 Alexander Larsson
> + *
> + * This file is released under the GPL.
> + */
> +
> +#include "cfs-internals.h"
> +
> +#include <linux/file.h>
> +#include <linux/fsverity.h>
> +#include <linux/pagemap.h>
> +#include <linux/unaligned/packed_struct.h>
> +
> +struct cfs_buf {
> +	struct page *page;
> +	void *base;
> +};
> +
> +#define CFS_VDATA_BUF_INIT                                                     \
> +	{                                                                      \
> +		NULL, NULL                                                     \
> +	}

Does this really save much in the 4 places it's used below like this:

struct cfs_buf vdata_buf = CFS_VDATA_BUF_INIT;

This seems just as simple:

struct cfs_buf vdata_buf = { NULL, NULL };

> +
> +static void cfs_buf_put(struct cfs_buf *buf)
> +{
> +	if (buf->page) {
> +		if (buf->base)
> +			kunmap(buf->page);
> +		put_page(buf->page);
> +		buf->base = NULL;
> +		buf->page = NULL;
> +	}
> +}
> +
> +static void *cfs_get_buf(struct cfs_context_s *ctx, u64 offset, u32 size,
> +			 struct cfs_buf *buf)
> +{
> +	u64 index = offset >> PAGE_SHIFT;
> +	u32 page_offset = offset & (PAGE_SIZE - 1);
> +	struct page *page = buf->page;
> +	struct inode *inode = ctx->descriptor->f_inode;
> +	struct address_space *const mapping = inode->i_mapping;

Put in reverse Christmas tree order where possible. I won't call out the
other places below.

> +
> +	if (offset > ctx->descriptor_len)
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	if ((offset + size < offset) || (offset + size > ctx->descriptor_len))
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	if (size > PAGE_SIZE)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (PAGE_SIZE - page_offset < size)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (!page || page->index != index) {
> +		cfs_buf_put(buf);
> +
> +		page = read_cache_page(mapping, index, NULL, NULL);
> +		if (IS_ERR(page))
> +			return page;
> +
> +		buf->page = page;
> +		buf->base = kmap(page);
> +	}
> +
> +	return buf->base + page_offset;
> +}
> +
> +static void *cfs_read_data(struct cfs_context_s *ctx, u64 offset, u64 size,
> +			   u8 *dest)
> +{
> +	size_t copied;
> +	loff_t pos = offset;
> +
> +	if (offset > ctx->descriptor_len)
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	if ((offset + size < offset) || (offset + size > ctx->descriptor_len))
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	copied = 0;
> +	while (copied < size) {
> +		ssize_t bytes;
> +
> +		bytes = kernel_read(ctx->descriptor, dest + copied,
> +				    size - copied, &pos);
> +		if (bytes < 0)
> +			return ERR_PTR(bytes);
> +		if (bytes == 0)
> +			return ERR_PTR(-EINVAL);
> +
> +		copied += bytes;
> +	}
> +
> +	if (copied != size)
> +		return ERR_PTR(-EFSCORRUPTED);
> +	return dest;
> +}
> +
> +int cfs_init_ctx(const char *descriptor_path, const u8 *required_digest,
> +		 struct cfs_context_s *ctx_out)
> +{
> +	struct cfs_header_s *header;
> +	struct file *descriptor;
> +	loff_t i_size;
> +	u8 verity_digest[FS_VERITY_MAX_DIGEST_SIZE];
> +	enum hash_algo verity_algo;
> +	struct cfs_context_s ctx;
> +	int res;
> +
> +	descriptor = filp_open(descriptor_path, O_RDONLY, 0);
> +	if (IS_ERR(descriptor))
> +		return PTR_ERR(descriptor);
> +
> +	if (required_digest) {
> +		res = fsverity_get_digest(d_inode(descriptor->f_path.dentry),
> +					  verity_digest, &verity_algo);
> +		if (res < 0) {
> +			pr_err("ERROR: composefs descriptor has no fs-verity digest\n");
> +			goto fail;
> +		}
> +		if (verity_algo != HASH_ALGO_SHA256 ||
> +		    memcmp(required_digest, verity_digest,
> +			   SHA256_DIGEST_SIZE) != 0) {

Move this up a line with memcmp() since line lengths can now be 100
characters. I'm not going to call out the other places in this patch.

> +			pr_err("ERROR: composefs descriptor has wrong fs-verity digest\n");
> +			res = -EINVAL;
> +			goto fail;
> +		}
> +	}
> +
> +	i_size = i_size_read(file_inode(descriptor));
> +	if (i_size <=
> +	    (sizeof(struct cfs_header_s) + sizeof(struct cfs_inode_s))) {
> +		res = -EINVAL;
> +		goto fail;
> +	}
> +
> +	/* Need this temporary ctx for cfs_read_data() */
> +	ctx.descriptor = descriptor;
> +	ctx.descriptor_len = i_size;
> +
> +	header = cfs_read_data(&ctx, 0, sizeof(struct cfs_header_s),
> +			       (u8 *)&ctx.header);
> +	if (IS_ERR(header)) {
> +		res = PTR_ERR(header);
> +		goto fail;
> +	}
> +	header->magic = cfs_u32_from_file(header->magic);
> +	header->data_offset = cfs_u64_from_file(header->data_offset);
> +	header->root_inode = cfs_u64_from_file(header->root_inode);

Should the cpu to little endian conversion occur in cfs_read_data()?

> +
> +	if (header->magic != CFS_MAGIC ||
> +	    header->data_offset > ctx.descriptor_len ||
> +	    sizeof(struct cfs_header_s) + header->root_inode >
> +		    ctx.descriptor_len) {
> +		res = -EINVAL;

Should this be -EFSCORRUPTED?

> +		goto fail;
> +	}
> +
> +	*ctx_out = ctx;
> +	return 0;
> +
> +fail:
> +	fput(descriptor);
> +	return res;
> +}
> +
> +void cfs_ctx_put(struct cfs_context_s *ctx)
> +{
> +	if (ctx->descriptor) {
> +		fput(ctx->descriptor);
> +		ctx->descriptor = NULL;
> +	}
> +}
> +
> +static void *cfs_get_inode_data(struct cfs_context_s *ctx, u64 offset, u64 size,
> +				u8 *dest)
> +{
> +	return cfs_read_data(ctx, offset + sizeof(struct cfs_header_s), size,
> +			     dest);
> +}
> +
> +static void *cfs_get_inode_data_max(struct cfs_context_s *ctx, u64 offset,
> +				    u64 max_size, u64 *read_size, u8 *dest)
> +{
> +	u64 remaining = ctx->descriptor_len - sizeof(struct cfs_header_s);
> +	u64 size;
> +
> +	if (offset > remaining)
> +		return ERR_PTR(-EINVAL);
> +	remaining -= offset;
> +
> +	/* Read at most remaining bytes, and no more than max_size */
> +	size = min(remaining, max_size);
> +	*read_size = size;
> +
> +	return cfs_get_inode_data(ctx, offset, size, dest);
> +}
> +
> +static void *cfs_get_inode_payload_w_len(struct cfs_context_s *ctx,
> +					 u32 payload_length, u64 index,
> +					 u8 *dest, u64 offset, size_t len)
> +{
> +	/* Payload is stored before the inode, check it fits */
> +	if (payload_length > index)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (offset > payload_length)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (offset + len > payload_length)
> +		return ERR_PTR(-EINVAL);
> +
> +	return cfs_get_inode_data(ctx, index - payload_length + offset, len,
> +				  dest);
> +}
> +
> +static void *cfs_get_inode_payload(struct cfs_context_s *ctx,
> +				   struct cfs_inode_s *ino, u64 index, u8 *dest)
> +{
> +	return cfs_get_inode_payload_w_len(ctx, ino->payload_length, index,
> +					   dest, 0, ino->payload_length);
> +}
> +
> +static void *cfs_get_vdata_buf(struct cfs_context_s *ctx, u64 offset, u32 len,
> +			       struct cfs_buf *buf)
> +{
> +	if (offset > ctx->descriptor_len - ctx->header.data_offset)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (len > ctx->descriptor_len - ctx->header.data_offset - offset)
> +		return ERR_PTR(-EINVAL);

It appears that these same checks are already done in cfs_get_buf().

> +
> +	return cfs_get_buf(ctx, ctx->header.data_offset + offset, len, buf);
> +}
> +
> +static u32 cfs_read_u32(u8 **data)
> +{
> +	u32 v = cfs_u32_from_file(__get_unaligned_cpu32(*data));
> +	*data += sizeof(u32);
> +	return v;
> +}
> +
> +static u64 cfs_read_u64(u8 **data)
> +{
> +	u64 v = cfs_u64_from_file(__get_unaligned_cpu64(*data));
> +	*data += sizeof(u64);
> +	return v;
> +}
> +
> +struct cfs_inode_s *cfs_get_ino_index(struct cfs_context_s *ctx, u64 index,
> +				      struct cfs_inode_s *ino)
> +{
> +	u64 offset = index;
> +	/* Buffer that fits the maximal encoded size: */
> +	u8 buffer[sizeof(struct cfs_inode_s)];
> +	u64 read_size;
> +	u64 inode_size;
> +	u8 *data;
> +
> +	data = cfs_get_inode_data_max(ctx, offset, sizeof(buffer), &read_size,
> +				      buffer);
> +	if (IS_ERR(data))
> +		return ERR_CAST(data);
> +
> +	/* Need to fit at least flags to decode */
> +	if (read_size < sizeof(u32))
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	memset(ino, 0, sizeof(struct cfs_inode_s));

sizeof(*ino)

> +	ino->flags = cfs_read_u32(&data);
> +
> +	inode_size = cfs_inode_encoded_size(ino->flags);

Should CFS_INODE_FLAGS_DIGEST_FROM_PAYLOAD also be accounted for in
cfs_inode_encoded_size()?

Also, cfs_inode_encoded_size() is only used here so can be brought into
this file.

> +	/* Shouldn't happen, but lets check */
> +	if (inode_size > sizeof(buffer))
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, PAYLOAD))
> +		ino->payload_length = cfs_read_u32(&data);
> +	else
> +		ino->payload_length = 0;
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, MODE))
> +		ino->st_mode = cfs_read_u32(&data);
> +	else
> +		ino->st_mode = CFS_INODE_DEFAULT_MODE;
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, NLINK)) {
> +		ino->st_nlink = cfs_read_u32(&data);
> +	} else {
> +		if ((ino->st_mode & S_IFMT) == S_IFDIR)
> +			ino->st_nlink = CFS_INODE_DEFAULT_NLINK_DIR;
> +		else
> +			ino->st_nlink = CFS_INODE_DEFAULT_NLINK;
> +	}
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, UIDGID)) {
> +		ino->st_uid = cfs_read_u32(&data);
> +		ino->st_gid = cfs_read_u32(&data);
> +	} else {
> +		ino->st_uid = CFS_INODE_DEFAULT_UIDGID;
> +		ino->st_gid = CFS_INODE_DEFAULT_UIDGID;
> +	}
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, RDEV))
> +		ino->st_rdev = cfs_read_u32(&data);
> +	else
> +		ino->st_rdev = CFS_INODE_DEFAULT_RDEV;
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, TIMES)) {
> +		ino->st_mtim.tv_sec = cfs_read_u64(&data);
> +		ino->st_ctim.tv_sec = cfs_read_u64(&data);
> +	} else {
> +		ino->st_mtim.tv_sec = CFS_INODE_DEFAULT_TIMES;
> +		ino->st_ctim.tv_sec = CFS_INODE_DEFAULT_TIMES;
> +	}
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, TIMES_NSEC)) {
> +		ino->st_mtim.tv_nsec = cfs_read_u32(&data);
> +		ino->st_ctim.tv_nsec = cfs_read_u32(&data);
> +	} else {
> +		ino->st_mtim.tv_nsec = 0;
> +		ino->st_ctim.tv_nsec = 0;
> +	}
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, LOW_SIZE))
> +		ino->st_size = cfs_read_u32(&data);
> +	else
> +		ino->st_size = 0;
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, HIGH_SIZE))
> +		ino->st_size += (u64)cfs_read_u32(&data) << 32;
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, XATTRS)) {
> +		ino->xattrs.off = cfs_read_u64(&data);
> +		ino->xattrs.len = cfs_read_u32(&data);
> +	} else {
> +		ino->xattrs.off = 0;
> +		ino->xattrs.len = 0;
> +	}
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, DIGEST)) {
> +		memcpy(ino->digest, data, SHA256_DIGEST_SIZE);
> +		data += 32;
> +	}
> +
> +	return ino;
> +}
> +
> +struct cfs_inode_s *cfs_get_root_ino(struct cfs_context_s *ctx,
> +				     struct cfs_inode_s *ino_buf, u64 *index)

Second line is misaligned.

> +{
> +	u64 root_ino = ctx->header.root_inode;
> +
> +	*index = root_ino;
> +	return cfs_get_ino_index(ctx, root_ino, ino_buf);
> +}
> +
> +static int cfs_get_digest(struct cfs_context_s *ctx, struct cfs_inode_s *ino,
> +			  const char *payload,
> +			  u8 digest_out[SHA256_DIGEST_SIZE])
> +{
> +	int r;
> +
> +	if (CFS_INODE_FLAG_CHECK(ino->flags, DIGEST)) {
> +		memcpy(digest_out, ino->digest, SHA256_DIGEST_SIZE);
> +		return 1;
> +	}
> +
> +	if (payload && CFS_INODE_FLAG_CHECK(ino->flags, DIGEST_FROM_PAYLOAD)) {
> +		r = cfs_digest_from_payload(payload, ino->payload_length,
> +					    digest_out);
> +		if (r < 0)
> +			return r;
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +static bool cfs_validate_filename(const char *name, size_t name_len)
> +{
> +	if (name_len == 0)
> +		return false;
> +
> +	if (name_len == 1 && name[0] == '.')
> +		return false;
> +
> +	if (name_len == 2 && name[0] == '.' && name[1] == '.')

Can strcmp() be used here?

> +		return false;
> +
> +	if (memchr(name, '/', name_len))
> +		return false;
> +
> +	return true;
> +}
> +
> +static struct cfs_dir_s *cfs_dir_read_chunk_header(struct cfs_context_s *ctx,
> +						   size_t payload_length,
> +						   u64 index, u8 *chunk_buf,
> +						   size_t chunk_buf_size,
> +						   size_t max_n_chunks)
> +{
> +	size_t n_chunks, i;
> +	struct cfs_dir_s *dir;
> +
> +	/* Payload and buffer should be large enough to fit the n_chunks */
> +	if (payload_length < sizeof(struct cfs_dir_s) ||
> +	    chunk_buf_size < sizeof(struct cfs_dir_s))
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	/* Make sure we fit max_n_chunks in buffer before reading it */
> +	if (chunk_buf_size < cfs_dir_size(max_n_chunks))
> +		return ERR_PTR(-EINVAL);
> +
> +	dir = cfs_get_inode_payload_w_len(ctx, payload_length, index, chunk_buf,
> +					  0,
> +					  min(chunk_buf_size, payload_length));
> +	if (IS_ERR(dir))
> +		return ERR_CAST(dir);
> +
> +	n_chunks = cfs_u32_from_file(dir->n_chunks);
> +	dir->n_chunks = n_chunks;
> +
> +	/* Don't support n_chunks == 0, the canonical version of that is payload_length == 0 */
> +	if (n_chunks == 0)
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	if (payload_length != cfs_dir_size(n_chunks))
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	max_n_chunks = min(n_chunks, max_n_chunks);
> +
> +	/* Verify data (up to max_n_chunks) */
> +	for (i = 0; i < max_n_chunks; i++) {
> +		struct cfs_dir_chunk_s *chunk = &dir->chunks[i];
> +
> +		chunk->n_dentries = cfs_u16_from_file(chunk->n_dentries);
> +		chunk->chunk_size = cfs_u16_from_file(chunk->chunk_size);
> +		chunk->chunk_offset = cfs_u64_from_file(chunk->chunk_offset);
> +
> +		if (chunk->chunk_size <
> +		    sizeof(struct cfs_dentry_s) * chunk->n_dentries)
> +			return ERR_PTR(-EFSCORRUPTED);
> +
> +		if (chunk->chunk_size > CFS_MAX_DIR_CHUNK_SIZE)
> +			return ERR_PTR(-EFSCORRUPTED);
> +
> +		if (chunk->n_dentries == 0)
> +			return ERR_PTR(-EFSCORRUPTED);
> +
> +		if (chunk->chunk_size == 0)
> +			return ERR_PTR(-EFSCORRUPTED);
> +
> +		if (chunk->chunk_offset >
> +		    ctx->descriptor_len - ctx->header.data_offset)
> +			return ERR_PTR(-EFSCORRUPTED);
> +	}
> +
> +	return dir;
> +}
> +
> +static char *cfs_dup_payload_path(struct cfs_context_s *ctx,
> +				  struct cfs_inode_s *ino, u64 index)
> +{
> +	const char *v;
> +	u8 *path;
> +
> +	if ((ino->st_mode & S_IFMT) != S_IFREG &&
> +	    (ino->st_mode & S_IFMT) != S_IFLNK) {
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	if (ino->payload_length == 0 || ino->payload_length > PATH_MAX)
> +		return ERR_PTR(-EFSCORRUPTED);
> +
> +	path = kmalloc(ino->payload_length + 1, GFP_KERNEL);
> +	if (!path)
> +		return ERR_PTR(-ENOMEM);
> +
> +	v = cfs_get_inode_payload(ctx, ino, index, path);
> +	if (IS_ERR(v)) {
> +		kfree(path);
> +		return ERR_CAST(v);
> +	}
> +
> +	/* zero terminate */
> +	path[ino->payload_length] = 0;
> +
> +	return (char *)path;
> +}
> +
> +int cfs_init_inode_data(struct cfs_context_s *ctx, struct cfs_inode_s *ino,
> +			u64 index, struct cfs_inode_data_s *inode_data)
> +{
> +	u8 buf[cfs_dir_size(CFS_N_PRELOAD_DIR_CHUNKS)];
> +	struct cfs_dir_s *dir;
> +	int ret = 0;
> +	size_t i;
> +	char *path_payload = NULL;
> +
> +	inode_data->payload_length = ino->payload_length;
> +
> +	if ((ino->st_mode & S_IFMT) != S_IFDIR || ino->payload_length == 0) {
> +		inode_data->n_dir_chunks = 0;
> +	} else {
> +		u32 n_chunks;
> +
> +		dir = cfs_dir_read_chunk_header(ctx, ino->payload_length, index,
> +						buf, sizeof(buf),
> +						CFS_N_PRELOAD_DIR_CHUNKS);
> +		if (IS_ERR(dir))
> +			return PTR_ERR(dir);
> +
> +		n_chunks = dir->n_chunks;
> +		inode_data->n_dir_chunks = n_chunks;
> +
> +		for (i = 0; i < n_chunks && i < CFS_N_PRELOAD_DIR_CHUNKS; i++)
> +			inode_data->preloaded_dir_chunks[i] = dir->chunks[i];
> +	}
> +
> +	if ((ino->st_mode & S_IFMT) == S_IFLNK ||
> +	    ((ino->st_mode & S_IFMT) == S_IFREG && ino->payload_length > 0)) {
> +		path_payload = cfs_dup_payload_path(ctx, ino, index);
> +		if (IS_ERR(path_payload)) {
> +			ret = PTR_ERR(path_payload);
> +			goto fail;
> +		}
> +	}
> +	inode_data->path_payload = path_payload;
> +
> +	ret = cfs_get_digest(ctx, ino, path_payload, inode_data->digest);
> +	if (ret < 0)
> +		goto fail;
> +
> +	inode_data->has_digest = ret != 0;

Can you do 'has_digest = inode_data->digest != NULL;' to get rid of the
need for return 1 in cfs_get_digest().

> +
> +	inode_data->xattrs_offset = ino->xattrs.off;
> +	inode_data->xattrs_len = ino->xattrs.len;
> +
> +	if (inode_data->xattrs_len != 0) {
> +		/* Validate xattr size */
> +		if (inode_data->xattrs_len <
> +			    sizeof(struct cfs_xattr_header_s) ||
> +		    inode_data->xattrs_len > CFS_MAX_XATTRS_SIZE) {
> +			ret = -EFSCORRUPTED;
> +			goto fail;
> +		}
> +	}
> +
> +	return 0;
> +
> +fail:
> +	cfs_inode_data_put(inode_data);
> +	return ret;
> +}
> +
> +void cfs_inode_data_put(struct cfs_inode_data_s *inode_data)
> +{
> +	inode_data->n_dir_chunks = 0;
> +	kfree(inode_data->path_payload);
> +	inode_data->path_payload = NULL;
> +}
> +
> +ssize_t cfs_list_xattrs(struct cfs_context_s *ctx,
> +			struct cfs_inode_data_s *inode_data, char *names,
> +			size_t size)
> +{
> +	u8 *data, *data_end;
> +	size_t n_xattrs = 0, i;
> +	ssize_t copied = 0;
> +	const struct cfs_xattr_header_s *xattrs;
> +	struct cfs_buf vdata_buf = CFS_VDATA_BUF_INIT;
> +
> +	if (inode_data->xattrs_len == 0)
> +		return 0;
> +
> +	/* xattrs_len basic size req was verified in cfs_init_inode_data */
> +
> +	xattrs = cfs_get_vdata_buf(ctx, inode_data->xattrs_offset,
> +				   inode_data->xattrs_len, &vdata_buf);
> +	if (IS_ERR(xattrs))
> +		return PTR_ERR(xattrs);
> +
> +	n_xattrs = cfs_u16_from_file(xattrs->n_attr);
> +
> +	/* Verify that array fits */
> +	if (inode_data->xattrs_len < cfs_xattr_header_size(n_xattrs)) {
> +		copied = -EFSCORRUPTED;
> +		goto exit;
> +	}
> +
> +	data = ((u8 *)xattrs) + cfs_xattr_header_size(n_xattrs);
> +	data_end = ((u8 *)xattrs) + inode_data->xattrs_len;
> +
> +	for (i = 0; i < n_xattrs; i++) {
> +		const struct cfs_xattr_element_s *e = &xattrs->attr[i];
> +		u16 this_key_len = cfs_u16_from_file(e->key_length);
> +		u16 this_value_len = cfs_u16_from_file(e->value_length);
> +		const char *this_key, *this_value;
> +
> +		if (this_key_len > XATTR_NAME_MAX ||
> +		    /* key and data needs to fit in data */
> +		    data_end - data < this_key_len + this_value_len) {
> +			copied = -EFSCORRUPTED;
> +			goto exit;
> +		}
> +
> +		this_key = data;
> +		this_value = data + this_key_len;
> +		data += this_key_len + this_value_len;
> +
> +		if (size) {
> +			if (size - copied < this_key_len + 1) {
> +				copied = -E2BIG;
> +				goto exit;
> +			}
> +
> +			memcpy(names + copied, this_key, this_key_len);
> +			names[copied + this_key_len] = '\0';
> +		}
> +
> +		copied += this_key_len + 1;
> +	}
> +
> +exit:
> +	cfs_buf_put(&vdata_buf);
> +
> +	return copied;
> +}
> +
> +int cfs_get_xattr(struct cfs_context_s *ctx,
> +		  struct cfs_inode_data_s *inode_data, const char *name,
> +		  void *value, size_t size)
> +{
> +	size_t name_len = strlen(name);
> +	size_t n_xattrs = 0, i;
> +	struct cfs_xattr_header_s *xattrs;
> +	u8 *data, *data_end;
> +	struct cfs_buf vdata_buf = CFS_VDATA_BUF_INIT;
> +	int res;
> +
> +	if (inode_data->xattrs_len == 0)
> +		return -ENODATA;
> +
> +	/* xattrs_len basic size req was verified in cfs_init_inode_data */
> +
> +	xattrs = cfs_get_vdata_buf(ctx, inode_data->xattrs_offset,
> +				   inode_data->xattrs_len, &vdata_buf);
> +	if (IS_ERR(xattrs))
> +		return PTR_ERR(xattrs);
> +
> +	n_xattrs = cfs_u16_from_file(xattrs->n_attr);
> +
> +	/* Verify that array fits */
> +	if (inode_data->xattrs_len < cfs_xattr_header_size(n_xattrs)) {
> +		res = -EFSCORRUPTED;
> +		goto exit;
> +	}
> +
> +	data = ((u8 *)xattrs) + cfs_xattr_header_size(n_xattrs);
> +	data_end = ((u8 *)xattrs) + inode_data->xattrs_len;
> +
> +	for (i = 0; i < n_xattrs; i++) {
> +		const struct cfs_xattr_element_s *e = &xattrs->attr[i];
> +		u16 this_key_len = cfs_u16_from_file(e->key_length);
> +		u16 this_value_len = cfs_u16_from_file(e->value_length);
> +		const char *this_key, *this_value;
> +
> +		if (this_key_len > XATTR_NAME_MAX ||
> +		    /* key and data needs to fit in data */
> +		    data_end - data < this_key_len + this_value_len) {
> +			res = -EFSCORRUPTED;
> +			goto exit;
> +		}
> +
> +		this_key = data;
> +		this_value = data + this_key_len;
> +		data += this_key_len + this_value_len;
> +
> +		if (this_key_len != name_len ||
> +		    memcmp(this_key, name, name_len) != 0)
> +			continue;
> +
> +		if (size > 0) {
> +			if (size < this_value_len) {
> +				res = -E2BIG;
> +				goto exit;
> +			}
> +			memcpy(value, this_value, this_value_len);
> +		}
> +
> +		res = this_value_len;
> +		goto exit;
> +	}
> +
> +	res = -ENODATA;
> +
> +exit:
> +	return res;
> +}
> +
> +static struct cfs_dir_s *
> +cfs_dir_read_chunk_header_alloc(struct cfs_context_s *ctx, u64 index,
> +				struct cfs_inode_data_s *inode_data)
> +{
> +	size_t chunk_buf_size = cfs_dir_size(inode_data->n_dir_chunks);
> +	u8 *chunk_buf;
> +	struct cfs_dir_s *dir;
> +
> +	chunk_buf = kmalloc(chunk_buf_size, GFP_KERNEL);
> +	if (!chunk_buf)
> +		return ERR_PTR(-ENOMEM);
> +
> +	dir = cfs_dir_read_chunk_header(ctx, inode_data->payload_length, index,
> +					chunk_buf, chunk_buf_size,
> +					inode_data->n_dir_chunks);
> +	if (IS_ERR(dir)) {
> +		kfree(chunk_buf);
> +		return ERR_CAST(dir);
> +	}
> +
> +	return dir;
> +}
> +
> +static struct cfs_dir_chunk_s *
> +cfs_dir_get_chunk_info(struct cfs_context_s *ctx, u64 index,
> +		       struct cfs_inode_data_s *inode_data, void **chunks_buf)
> +{
> +	struct cfs_dir_s *full_dir;
> +
> +	if (inode_data->n_dir_chunks <= CFS_N_PRELOAD_DIR_CHUNKS) {
> +		*chunks_buf = NULL;
> +		return inode_data->preloaded_dir_chunks;
> +	}
> +
> +	full_dir = cfs_dir_read_chunk_header_alloc(ctx, index, inode_data);
> +	if (IS_ERR(full_dir))
> +		return ERR_CAST(full_dir);
> +
> +	*chunks_buf = full_dir;
> +	return full_dir->chunks;
> +}
> +
> +static inline int memcmp2(const void *a, const size_t a_size, const void *b,
> +			  size_t b_size)
> +{
> +	size_t common_size = min(a_size, b_size);
> +	int res;
> +
> +	res = memcmp(a, b, common_size);
> +	if (res != 0 || a_size == b_size)
> +		return res;
> +
> +	return a_size < b_size ? -1 : 1;

This function appears to be used only in one place below. It doesn't
seem like it matters for the common_size. Can this just be dropped and
use memcmp()?

Brian


  reply	other threads:[~2023-01-05 20:03 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-28 11:13 [PATCH RFC 0/6] Composefs: an opportunistically sharing verified image filesystem Alexander Larsson
2022-11-28 11:13 ` [PATCH 1/6] fsverity: Export fsverity_get_digest Alexander Larsson
2022-11-28 11:16 ` [PATCH 2/6] composefs: Add on-disk layout Alexander Larsson
2023-01-05 15:55   ` Brian Masney
2023-01-10 16:19     ` Alexander Larsson
2022-11-28 11:16 ` [PATCH 3/6] composefs: Add descriptor parsing code Alexander Larsson
2023-01-05 20:02   ` Brian Masney [this message]
2023-01-10 16:33     ` Alexander Larsson
2022-11-28 11:17 ` [PATCH 4/6] composefs: Add filesystem implementation Alexander Larsson
2023-01-06 12:18   ` Brian Masney
2023-01-10 16:41     ` Alexander Larsson
2022-11-28 11:17 ` [PATCH 5/6] composefs: Add documentation Alexander Larsson
2022-11-29 14:08   ` Bagas Sanjaya
2022-11-28 11:17 ` [PATCH 6/6] composefs: Add kconfig and build support Alexander Larsson
2022-11-28 14:16   ` kernel test robot
2022-11-28 17:28   ` kernel test robot
2022-11-29  7:08   ` kernel test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Y7cszNNvHHUef2qO@x1 \
    --to=bmasney@redhat.com \
    --cc=alexl@redhat.com \
    --cc=gscrivan@redhat.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.