diff options
Diffstat (limited to 'fs/cachefiles/xattr.c')
| -rw-r--r-- | fs/cachefiles/xattr.c | 421 | 
1 files changed, 178 insertions, 243 deletions
| diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c index 9e82de668595..83f41bd0c3a9 100644 --- a/fs/cachefiles/xattr.c +++ b/fs/cachefiles/xattr.c @@ -1,7 +1,7 @@  // SPDX-License-Identifier: GPL-2.0-or-later  /* CacheFiles extended attribute management   * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.   * Written by David Howells ([email protected])   */ @@ -15,310 +15,245 @@  #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";  /* - * check the type label on an object - * - done using xattrs + * set the state xattr on a cache file   */ -int cachefiles_check_object_type(struct cachefiles_object *object) +int cachefiles_set_object_xattr(struct cachefiles_object *object)  { -	struct dentry *dentry = object->dentry; -	char type[3], xtype[3]; +	struct cachefiles_xattr *buf; +	struct dentry *dentry; +	struct file *file = object->file; +	unsigned int len = object->cookie->aux_len;  	int ret; -	ASSERT(dentry); -	ASSERT(d_backing_inode(dentry)); - -	if (!object->fscache.cookie) -		strcpy(type, "C3"); -	else -		snprintf(type, 3, "%02x", object->fscache.cookie->def->type); - -	_enter("%x{%s}", object->fscache.debug_id, type); +	if (!file) +		return -ESTALE; +	dentry = file->f_path.dentry; -	/* attempt to install a type label directly */ -	ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, type, -			   2, XATTR_CREATE); -	if (ret == 0) { -		_debug("SET"); /* we succeeded */ -		goto error; -	} +	_enter("%x,#%d", object->debug_id, len); -	if (ret != -EEXIST) { -		pr_err("Can't set xattr on %pd [%lu] (err %d)\n", -		       dentry, d_backing_inode(dentry)->i_ino, -		       -ret); -		goto error; -	} +	buf = kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; -	/* read the current type label */ -	ret = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, xtype, -			   3); +	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 = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, +				   buf, sizeof(struct cachefiles_xattr) + len, 0);  	if (ret < 0) { -		if (ret == -ERANGE) -			goto bad_type_length; - -		pr_err("Can't read xattr on %pd [%lu] (err %d)\n", -		       dentry, d_backing_inode(dentry)->i_ino, -		       -ret); -		goto error; +		trace_cachefiles_vfs_error(object, file_inode(file), ret, +					   cachefiles_trace_setxattr_error); +		trace_cachefiles_coherency(object, file_inode(file)->i_ino, +					   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, +					   buf->content, +					   cachefiles_coherency_set_ok);  	} -	/* check the type is what we're expecting */ -	if (ret != 2) -		goto bad_type_length; - -	if (xtype[0] != type[0] || xtype[1] != type[1]) -		goto bad_type; - -	ret = 0; - -error: +	kfree(buf);  	_leave(" = %d", ret);  	return ret; - -bad_type_length: -	pr_err("Cache object %lu type xattr length incorrect\n", -	       d_backing_inode(dentry)->i_ino); -	ret = -EIO; -	goto error; - -bad_type: -	xtype[2] = 0; -	pr_err("Cache object %pd [%lu] type %s not %s\n", -	       dentry, d_backing_inode(dentry)->i_ino, -	       xtype, type); -	ret = -EIO; -	goto error;  }  /* - * set the state xattr on a cache file + * check the consistency between the backing cache and the FS-Cache cookie   */ -int cachefiles_set_object_xattr(struct cachefiles_object *object, -				struct cachefiles_xattr *auxdata) +int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file)  { -	struct dentry *dentry = object->dentry; -	int ret; - -	ASSERT(dentry); - -	_enter("%p,#%d", object, auxdata->len); +	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; -	/* attempt to install the cache metadata directly */ -	_debug("SET #%u", auxdata->len); +	tlen = sizeof(struct cachefiles_xattr) + len; +	buf = kmalloc(tlen, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; -	clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags); -	ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, -			   &auxdata->type, auxdata->len, XATTR_CREATE); -	if (ret < 0 && ret != -ENOMEM) -		cachefiles_io_error_obj( -			object, -			"Failed to set xattr with error %d", ret); +	xlen = cachefiles_inject_read_error(); +	if (xlen == 0) +		xlen = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, buf, tlen); +	if (xlen != tlen) { +		if (xlen < 0) +			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; +	} else 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; +	} -	_leave(" = %d", ret); +	trace_cachefiles_coherency(object, file_inode(file)->i_ino, +				   buf->content, why); +	kfree(buf);  	return ret;  }  /* - * update the state xattr on a cache file + * remove the object's xattr to mark it stale   */ -int cachefiles_update_object_xattr(struct cachefiles_object *object, -				   struct cachefiles_xattr *auxdata) +int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, +				   struct cachefiles_object *object, +				   struct dentry *dentry)  { -	struct dentry *dentry = object->dentry;  	int ret; -	if (!dentry) -		return -ESTALE; - -	_enter("%x,#%d", object->fscache.debug_id, auxdata->len); - -	/* attempt to install the cache metadata directly */ -	_debug("SET #%u", auxdata->len); - -	clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags); -	ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, -			   &auxdata->type, auxdata->len, XATTR_REPLACE); -	if (ret < 0 && ret != -ENOMEM) -		cachefiles_io_error_obj( -			object, -			"Failed to update xattr with error %d", ret); +	ret = cachefiles_inject_remove_error(); +	if (ret == 0) +		ret = vfs_removexattr(&init_user_ns, dentry, cachefiles_xattr_cache); +	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;  }  /* - * check the consistency between the backing cache and the FS-Cache cookie + * Stick a marker on the cache object to indicate that it's dirty.   */ -int cachefiles_check_auxdata(struct cachefiles_object *object) +void cachefiles_prepare_to_write(struct fscache_cookie *cookie)  { -	struct cachefiles_xattr *auxbuf; -	enum fscache_checkaux validity; -	struct dentry *dentry = object->dentry; -	ssize_t xlen; -	int ret; - -	ASSERT(dentry); -	ASSERT(d_backing_inode(dentry)); -	ASSERT(object->fscache.cookie->def->check_aux); - -	auxbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, GFP_KERNEL); -	if (!auxbuf) -		return -ENOMEM; +	const struct cred *saved_cred; +	struct cachefiles_object *object = cookie->cache_priv; +	struct cachefiles_cache *cache = object->volume->cache; -	xlen = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, -			    &auxbuf->type, 512 + 1); -	ret = -ESTALE; -	if (xlen < 1 || -	    auxbuf->type != object->fscache.cookie->def->type) -		goto error; +	_enter("c=%08x", object->cookie->debug_id); -	xlen--; -	validity = fscache_check_aux(&object->fscache, &auxbuf->data, xlen, -				     i_size_read(d_backing_inode(dentry))); -	if (validity != FSCACHE_CHECKAUX_OKAY) -		goto error; - -	ret = 0; -error: -	kfree(auxbuf); -	return ret; +	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); +	}  }  /* - * check the state xattr on a cache file - * - return -ESTALE if the object should be deleted + * Set the state xattr on a volume directory.   */ -int cachefiles_check_object_xattr(struct cachefiles_object *object, -				  struct cachefiles_xattr *auxdata) +bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)  { -	struct cachefiles_xattr *auxbuf; -	struct dentry *dentry = object->dentry; +	unsigned int len = volume->vcookie->coherency_len; +	const void *p = volume->vcookie->coherency; +	struct dentry *dentry = volume->dentry;  	int ret; -	_enter("%p,#%d", object, auxdata->len); - -	ASSERT(dentry); -	ASSERT(d_backing_inode(dentry)); - -	auxbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, cachefiles_gfp); -	if (!auxbuf) { -		_leave(" = -ENOMEM"); -		return -ENOMEM; -	} +	_enter("%x,#%d", volume->vcookie->debug_id, len); -	/* read the current type label */ -	ret = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, -			   &auxbuf->type, 512 + 1); +	ret = cachefiles_inject_write_error(); +	if (ret == 0) +		ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, +				   p, len, 0);  	if (ret < 0) { -		if (ret == -ENODATA) -			goto stale; /* no attribute - power went off -				     * mid-cull? */ - -		if (ret == -ERANGE) -			goto bad_type_length; - -		cachefiles_io_error_obj(object, -					"Can't read xattr on %lu (err %d)", -					d_backing_inode(dentry)->i_ino, -ret); -		goto error; +		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);  	} -	/* check the on-disk object */ -	if (ret < 1) -		goto bad_type_length; - -	if (auxbuf->type != auxdata->type) -		goto stale; - -	auxbuf->len = ret; - -	/* consult the netfs */ -	if (object->fscache.cookie->def->check_aux) { -		enum fscache_checkaux result; -		unsigned int dlen; - -		dlen = auxbuf->len - 1; - -		_debug("checkaux %s #%u", -		       object->fscache.cookie->def->name, dlen); - -		result = fscache_check_aux(&object->fscache, -					   &auxbuf->data, dlen, -					   i_size_read(d_backing_inode(dentry))); - -		switch (result) { -			/* entry okay as is */ -		case FSCACHE_CHECKAUX_OKAY: -			goto okay; - -			/* entry requires update */ -		case FSCACHE_CHECKAUX_NEEDS_UPDATE: -			break; - -			/* entry requires deletion */ -		case FSCACHE_CHECKAUX_OBSOLETE: -			goto stale; - -		default: -			BUG(); -		} - -		/* update the current label */ -		ret = vfs_setxattr(&init_user_ns, dentry, -				   cachefiles_xattr_cache, &auxdata->type, -				   auxdata->len, XATTR_REPLACE); -		if (ret < 0) { -			cachefiles_io_error_obj(object, -						"Can't update xattr on %lu" -						" (error %d)", -						d_backing_inode(dentry)->i_ino, -ret); -			goto error; -		} -	} - -okay: -	ret = 0; - -error: -	kfree(auxbuf);  	_leave(" = %d", ret); -	return ret; - -bad_type_length: -	pr_err("Cache object %lu xattr length incorrect\n", -	       d_backing_inode(dentry)->i_ino); -	ret = -EIO; -	goto error; - -stale: -	ret = -ESTALE; -	goto error; +	return ret == 0;  }  /* - * remove the object's xattr to mark it stale + * Check the consistency between the backing cache and the volume cookie.   */ -int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, -				   struct dentry *dentry) +int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)  { -	int ret; +	struct cachefiles_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; -	ret = vfs_removexattr(&init_user_ns, dentry, cachefiles_xattr_cache); -	if (ret < 0) { -		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); +	_enter(""); + +	buf = kmalloc(len, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	xlen = cachefiles_inject_read_error(); +	if (xlen == 0) +		xlen = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, buf, len); +	if (xlen != len) { +		if (xlen < 0) { +			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 (memcmp(buf->data, p, len) != 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;  } |