mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 00:28:52 +02:00 
			
		
		
		
	 229105e5cf
			
		
	
	
		229105e5cf
		
			
		
	
	
	
	
		
			
			Add a display of the first 8 bytes of the downloaded auxiliary data and of the on-disk stored auxiliary data as these are used in coherency management. In the case of afs, this holds the data version number. Signed-off-by: David Howells <dhowells@redhat.com> Link: https://lore.kernel.org/r/20241216204124.3752367-17-dhowells@redhat.com cc: Jeff Layton <jlayton@kernel.org> cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
		
			
				
	
	
		
			304 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /* CacheFiles extended attribute management
 | |
|  *
 | |
|  * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
 | |
|  * Written by David Howells (dhowells@redhat.com)
 | |
|  */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/file.h>
 | |
| #include <linux/fs.h>
 | |
| #include <linux/fsnotify.h>
 | |
| #include <linux/quotaops.h>
 | |
| #include <linux/xattr.h>
 | |
| #include <linux/slab.h>
 | |
| #include "internal.h"
 | |
| 
 | |
| #define CACHEFILES_COOKIE_TYPE_DATA 1
 | |
| 
 | |
| struct cachefiles_xattr {
 | |
| 	__be64	object_size;	/* Actual size of the object */
 | |
| 	__be64	zero_point;	/* Size after which server has no data not written by us */
 | |
| 	__u8	type;		/* Type of object */
 | |
| 	__u8	content;	/* Content presence (enum cachefiles_content) */
 | |
| 	__u8	data[];		/* netfs coherency data */
 | |
| } __packed;
 | |
| 
 | |
| static const char cachefiles_xattr_cache[] =
 | |
| 	XATTR_USER_PREFIX "CacheFiles.cache";
 | |
| 
 | |
| struct cachefiles_vol_xattr {
 | |
| 	__be32	reserved;	/* Reserved, should be 0 */
 | |
| 	__u8	data[];		/* netfs volume coherency data */
 | |
| } __packed;
 | |
| 
 | |
| /*
 | |
|  * set the state xattr on a cache file
 | |
|  */
 | |
| int cachefiles_set_object_xattr(struct cachefiles_object *object)
 | |
| {
 | |
| 	struct cachefiles_xattr *buf;
 | |
| 	struct dentry *dentry;
 | |
| 	struct file *file = object->file;
 | |
| 	unsigned int len = object->cookie->aux_len;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (!file)
 | |
| 		return -ESTALE;
 | |
| 	dentry = file->f_path.dentry;
 | |
| 
 | |
| 	_enter("%x,#%d", object->debug_id, len);
 | |
| 
 | |
| 	buf = kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL);
 | |
| 	if (!buf)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	buf->object_size	= cpu_to_be64(object->cookie->object_size);
 | |
| 	buf->zero_point		= 0;
 | |
| 	buf->type		= CACHEFILES_COOKIE_TYPE_DATA;
 | |
| 	buf->content		= object->content_info;
 | |
| 	if (test_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags))
 | |
| 		buf->content	= CACHEFILES_CONTENT_DIRTY;
 | |
| 	if (len > 0)
 | |
| 		memcpy(buf->data, fscache_get_aux(object->cookie), len);
 | |
| 
 | |
| 	ret = cachefiles_inject_write_error();
 | |
| 	if (ret == 0) {
 | |
| 		ret = mnt_want_write_file(file);
 | |
| 		if (ret == 0) {
 | |
| 			ret = vfs_setxattr(&nop_mnt_idmap, dentry,
 | |
| 					   cachefiles_xattr_cache, buf,
 | |
| 					   sizeof(struct cachefiles_xattr) + len, 0);
 | |
| 			mnt_drop_write_file(file);
 | |
| 		}
 | |
| 	}
 | |
| 	if (ret < 0) {
 | |
| 		trace_cachefiles_vfs_error(object, file_inode(file), ret,
 | |
| 					   cachefiles_trace_setxattr_error);
 | |
| 		trace_cachefiles_coherency(object, file_inode(file)->i_ino,
 | |
| 					   be64_to_cpup((__be64 *)buf->data),
 | |
| 					   buf->content,
 | |
| 					   cachefiles_coherency_set_fail);
 | |
| 		if (ret != -ENOMEM)
 | |
| 			cachefiles_io_error_obj(
 | |
| 				object,
 | |
| 				"Failed to set xattr with error %d", ret);
 | |
| 	} else {
 | |
| 		trace_cachefiles_coherency(object, file_inode(file)->i_ino,
 | |
| 					   be64_to_cpup((__be64 *)buf->data),
 | |
| 					   buf->content,
 | |
| 					   cachefiles_coherency_set_ok);
 | |
| 	}
 | |
| 
 | |
| 	kfree(buf);
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * check the consistency between the backing cache and the FS-Cache cookie
 | |
|  */
 | |
| int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file)
 | |
| {
 | |
| 	struct cachefiles_xattr *buf;
 | |
| 	struct dentry *dentry = file->f_path.dentry;
 | |
| 	unsigned int len = object->cookie->aux_len, tlen;
 | |
| 	const void *p = fscache_get_aux(object->cookie);
 | |
| 	enum cachefiles_coherency_trace why;
 | |
| 	ssize_t xlen;
 | |
| 	int ret = -ESTALE;
 | |
| 
 | |
| 	tlen = sizeof(struct cachefiles_xattr) + len;
 | |
| 	buf = kmalloc(tlen, GFP_KERNEL);
 | |
| 	if (!buf)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	xlen = cachefiles_inject_read_error();
 | |
| 	if (xlen == 0)
 | |
| 		xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, tlen);
 | |
| 	if (xlen != tlen) {
 | |
| 		if (xlen < 0) {
 | |
| 			ret = xlen;
 | |
| 			trace_cachefiles_vfs_error(object, file_inode(file), xlen,
 | |
| 						   cachefiles_trace_getxattr_error);
 | |
| 		}
 | |
| 		if (xlen == -EIO)
 | |
| 			cachefiles_io_error_obj(
 | |
| 				object,
 | |
| 				"Failed to read aux with error %zd", xlen);
 | |
| 		why = cachefiles_coherency_check_xattr;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	if (buf->type != CACHEFILES_COOKIE_TYPE_DATA) {
 | |
| 		why = cachefiles_coherency_check_type;
 | |
| 	} else if (memcmp(buf->data, p, len) != 0) {
 | |
| 		why = cachefiles_coherency_check_aux;
 | |
| 	} else if (be64_to_cpu(buf->object_size) != object->cookie->object_size) {
 | |
| 		why = cachefiles_coherency_check_objsize;
 | |
| 	} else if (buf->content == CACHEFILES_CONTENT_DIRTY) {
 | |
| 		// TODO: Begin conflict resolution
 | |
| 		pr_warn("Dirty object in cache\n");
 | |
| 		why = cachefiles_coherency_check_dirty;
 | |
| 	} else {
 | |
| 		why = cachefiles_coherency_check_ok;
 | |
| 		ret = 0;
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	trace_cachefiles_coherency(object, file_inode(file)->i_ino,
 | |
| 				   be64_to_cpup((__be64 *)buf->data),
 | |
| 				   buf->content, why);
 | |
| 	kfree(buf);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * remove the object's xattr to mark it stale
 | |
|  */
 | |
| int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
 | |
| 				   struct cachefiles_object *object,
 | |
| 				   struct dentry *dentry)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = cachefiles_inject_remove_error();
 | |
| 	if (ret == 0) {
 | |
| 		ret = mnt_want_write(cache->mnt);
 | |
| 		if (ret == 0) {
 | |
| 			ret = vfs_removexattr(&nop_mnt_idmap, dentry,
 | |
| 					      cachefiles_xattr_cache);
 | |
| 			mnt_drop_write(cache->mnt);
 | |
| 		}
 | |
| 	}
 | |
| 	if (ret < 0) {
 | |
| 		trace_cachefiles_vfs_error(object, d_inode(dentry), ret,
 | |
| 					   cachefiles_trace_remxattr_error);
 | |
| 		if (ret == -ENOENT || ret == -ENODATA)
 | |
| 			ret = 0;
 | |
| 		else if (ret != -ENOMEM)
 | |
| 			cachefiles_io_error(cache,
 | |
| 					    "Can't remove xattr from %lu"
 | |
| 					    " (error %d)",
 | |
| 					    d_backing_inode(dentry)->i_ino, -ret);
 | |
| 	}
 | |
| 
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Stick a marker on the cache object to indicate that it's dirty.
 | |
|  */
 | |
| void cachefiles_prepare_to_write(struct fscache_cookie *cookie)
 | |
| {
 | |
| 	const struct cred *saved_cred;
 | |
| 	struct cachefiles_object *object = cookie->cache_priv;
 | |
| 	struct cachefiles_cache *cache = object->volume->cache;
 | |
| 
 | |
| 	_enter("c=%08x", object->cookie->debug_id);
 | |
| 
 | |
| 	if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
 | |
| 		cachefiles_begin_secure(cache, &saved_cred);
 | |
| 		cachefiles_set_object_xattr(object);
 | |
| 		cachefiles_end_secure(cache, saved_cred);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Set the state xattr on a volume directory.
 | |
|  */
 | |
| bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
 | |
| {
 | |
| 	struct cachefiles_vol_xattr *buf;
 | |
| 	unsigned int len = volume->vcookie->coherency_len;
 | |
| 	const void *p = volume->vcookie->coherency;
 | |
| 	struct dentry *dentry = volume->dentry;
 | |
| 	int ret;
 | |
| 
 | |
| 	_enter("%x,#%d", volume->vcookie->debug_id, len);
 | |
| 
 | |
| 	len += sizeof(*buf);
 | |
| 	buf = kmalloc(len, GFP_KERNEL);
 | |
| 	if (!buf)
 | |
| 		return false;
 | |
| 	buf->reserved = cpu_to_be32(0);
 | |
| 	memcpy(buf->data, p, volume->vcookie->coherency_len);
 | |
| 
 | |
| 	ret = cachefiles_inject_write_error();
 | |
| 	if (ret == 0) {
 | |
| 		ret = mnt_want_write(volume->cache->mnt);
 | |
| 		if (ret == 0) {
 | |
| 			ret = vfs_setxattr(&nop_mnt_idmap, dentry,
 | |
| 					   cachefiles_xattr_cache,
 | |
| 					   buf, len, 0);
 | |
| 			mnt_drop_write(volume->cache->mnt);
 | |
| 		}
 | |
| 	}
 | |
| 	if (ret < 0) {
 | |
| 		trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret,
 | |
| 					   cachefiles_trace_setxattr_error);
 | |
| 		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
 | |
| 					       cachefiles_coherency_vol_set_fail);
 | |
| 		if (ret != -ENOMEM)
 | |
| 			cachefiles_io_error(
 | |
| 				volume->cache, "Failed to set xattr with error %d", ret);
 | |
| 	} else {
 | |
| 		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
 | |
| 					       cachefiles_coherency_vol_set_ok);
 | |
| 	}
 | |
| 
 | |
| 	kfree(buf);
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret == 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Check the consistency between the backing cache and the volume cookie.
 | |
|  */
 | |
| int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
 | |
| {
 | |
| 	struct cachefiles_vol_xattr *buf;
 | |
| 	struct dentry *dentry = volume->dentry;
 | |
| 	unsigned int len = volume->vcookie->coherency_len;
 | |
| 	const void *p = volume->vcookie->coherency;
 | |
| 	enum cachefiles_coherency_trace why;
 | |
| 	ssize_t xlen;
 | |
| 	int ret = -ESTALE;
 | |
| 
 | |
| 	_enter("");
 | |
| 
 | |
| 	len += sizeof(*buf);
 | |
| 	buf = kmalloc(len, GFP_KERNEL);
 | |
| 	if (!buf)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	xlen = cachefiles_inject_read_error();
 | |
| 	if (xlen == 0)
 | |
| 		xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, len);
 | |
| 	if (xlen != len) {
 | |
| 		if (xlen < 0) {
 | |
| 			ret = xlen;
 | |
| 			trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen,
 | |
| 						   cachefiles_trace_getxattr_error);
 | |
| 			if (xlen == -EIO)
 | |
| 				cachefiles_io_error(
 | |
| 					volume->cache,
 | |
| 					"Failed to read xattr with error %zd", xlen);
 | |
| 		}
 | |
| 		why = cachefiles_coherency_vol_check_xattr;
 | |
| 	} else if (buf->reserved != cpu_to_be32(0)) {
 | |
| 		why = cachefiles_coherency_vol_check_resv;
 | |
| 	} else if (memcmp(buf->data, p, len - sizeof(*buf)) != 0) {
 | |
| 		why = cachefiles_coherency_vol_check_cmp;
 | |
| 	} else {
 | |
| 		why = cachefiles_coherency_vol_check_ok;
 | |
| 		ret = 0;
 | |
| 	}
 | |
| 
 | |
| 	trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, why);
 | |
| 	kfree(buf);
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret;
 | |
| }
 |