diff options
Diffstat (limited to 'fs/cachefiles/interface.c')
| -rw-r--r-- | fs/cachefiles/interface.c | 747 | 
1 files changed, 310 insertions, 437 deletions
| diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index da28ac1fa225..51c968cd00a6 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -1,572 +1,445 @@  // SPDX-License-Identifier: GPL-2.0-or-later  /* FS-Cache interface to CacheFiles   * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.   * Written by David Howells ([email protected])   */  #include <linux/slab.h>  #include <linux/mount.h> +#include <linux/xattr.h> +#include <linux/file.h> +#include <linux/falloc.h> +#include <trace/events/fscache.h>  #include "internal.h" -struct cachefiles_lookup_data { -	struct cachefiles_xattr	*auxdata;	/* auxiliary data */ -	char			*key;		/* key path */ -}; - -static int cachefiles_attr_changed(struct fscache_object *_object); +static atomic_t cachefiles_object_debug_id;  /* - * allocate an object record for a cookie lookup and prepare the lookup data + * Allocate a cache object record.   */ -static struct fscache_object *cachefiles_alloc_object( -	struct fscache_cache *_cache, -	struct fscache_cookie *cookie) +static +struct cachefiles_object *cachefiles_alloc_object(struct fscache_cookie *cookie)  { -	struct cachefiles_lookup_data *lookup_data; +	struct fscache_volume *vcookie = cookie->volume; +	struct cachefiles_volume *volume = vcookie->cache_priv;  	struct cachefiles_object *object; -	struct cachefiles_cache *cache; -	struct cachefiles_xattr *auxdata; -	unsigned keylen, auxlen; -	void *buffer, *p; -	char *key; -	cache = container_of(_cache, struct cachefiles_cache, cache); +	_enter("{%s},%x,", vcookie->key, cookie->debug_id); -	_enter("{%s},%x,", cache->cache.identifier, cookie->debug_id); - -	lookup_data = kmalloc(sizeof(*lookup_data), cachefiles_gfp); -	if (!lookup_data) -		goto nomem_lookup_data; - -	/* create a new object record and a temporary leaf image */ -	object = kmem_cache_alloc(cachefiles_object_jar, cachefiles_gfp); +	object = kmem_cache_zalloc(cachefiles_object_jar, GFP_KERNEL);  	if (!object) -		goto nomem_object; - -	ASSERTCMP(object->backer, ==, NULL); +		return NULL; -	BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); -	atomic_set(&object->usage, 1); +	refcount_set(&object->ref, 1); -	fscache_object_init(&object->fscache, cookie, &cache->cache); +	spin_lock_init(&object->lock); +	INIT_LIST_HEAD(&object->cache_link); +	object->volume = volume; +	object->debug_id = atomic_inc_return(&cachefiles_object_debug_id); +	object->cookie = fscache_get_cookie(cookie, fscache_cookie_get_attach_object); -	object->type = cookie->def->type; - -	/* get hold of the raw key -	 * - stick the length on the front and leave space on the back for the -	 *   encoder -	 */ -	buffer = kmalloc((2 + 512) + 3, cachefiles_gfp); -	if (!buffer) -		goto nomem_buffer; - -	keylen = cookie->key_len; -	if (keylen <= sizeof(cookie->inline_key)) -		p = cookie->inline_key; -	else -		p = cookie->key; -	memcpy(buffer + 2, p, keylen); - -	*(uint16_t *)buffer = keylen; -	((char *)buffer)[keylen + 2] = 0; -	((char *)buffer)[keylen + 3] = 0; -	((char *)buffer)[keylen + 4] = 0; - -	/* turn the raw key into something that can work with as a filename */ -	key = cachefiles_cook_key(buffer, keylen + 2, object->type); -	if (!key) -		goto nomem_key; - -	/* get hold of the auxiliary data and prepend the object type */ -	auxdata = buffer; -	auxlen = cookie->aux_len; -	if (auxlen) { -		if (auxlen <= sizeof(cookie->inline_aux)) -			p = cookie->inline_aux; -		else -			p = cookie->aux; -		memcpy(auxdata->data, p, auxlen); -	} - -	auxdata->len = auxlen + 1; -	auxdata->type = cookie->type; - -	lookup_data->auxdata = auxdata; -	lookup_data->key = key; -	object->lookup_data = lookup_data; - -	_leave(" = %x [%p]", object->fscache.debug_id, lookup_data); -	return &object->fscache; - -nomem_key: -	kfree(buffer); -nomem_buffer: -	BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); -	kmem_cache_free(cachefiles_object_jar, object); -	fscache_object_destroyed(&cache->cache); -nomem_object: -	kfree(lookup_data); -nomem_lookup_data: -	_leave(" = -ENOMEM"); -	return ERR_PTR(-ENOMEM); +	fscache_count_object(vcookie->cache); +	trace_cachefiles_ref(object->debug_id, cookie->debug_id, 1, +			     cachefiles_obj_new); +	return object;  }  /* - * attempt to look up the nominated node in this cache - * - return -ETIMEDOUT to be scheduled again + * Note that an object has been seen.   */ -static int cachefiles_lookup_object(struct fscache_object *_object) +void cachefiles_see_object(struct cachefiles_object *object, +			   enum cachefiles_obj_ref_trace why)  { -	struct cachefiles_lookup_data *lookup_data; -	struct cachefiles_object *parent, *object; -	struct cachefiles_cache *cache; -	const struct cred *saved_cred; -	int ret; - -	_enter("{OBJ%x}", _object->debug_id); - -	cache = container_of(_object->cache, struct cachefiles_cache, cache); -	parent = container_of(_object->parent, -			      struct cachefiles_object, fscache); -	object = container_of(_object, struct cachefiles_object, fscache); -	lookup_data = object->lookup_data; - -	ASSERTCMP(lookup_data, !=, NULL); - -	/* look up the key, creating any missing bits */ -	cachefiles_begin_secure(cache, &saved_cred); -	ret = cachefiles_walk_to_object(parent, object, -					lookup_data->key, -					lookup_data->auxdata); -	cachefiles_end_secure(cache, saved_cred); - -	/* polish off by setting the attributes of non-index files */ -	if (ret == 0 && -	    object->fscache.cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) -		cachefiles_attr_changed(&object->fscache); - -	if (ret < 0 && ret != -ETIMEDOUT) { -		if (ret != -ENOBUFS) -			pr_warn("Lookup failed error %d\n", ret); -		fscache_object_lookup_error(&object->fscache); -	} - -	_leave(" [%d]", ret); -	return ret; +	trace_cachefiles_ref(object->debug_id, object->cookie->debug_id, +			     refcount_read(&object->ref), why);  }  /* - * indication of lookup completion + * Increment the usage count on an object;   */ -static void cachefiles_lookup_complete(struct fscache_object *_object) +struct cachefiles_object *cachefiles_grab_object(struct cachefiles_object *object, +						 enum cachefiles_obj_ref_trace why)  { -	struct cachefiles_object *object; - -	object = container_of(_object, struct cachefiles_object, fscache); - -	_enter("{OBJ%x,%p}", object->fscache.debug_id, object->lookup_data); +	int r; -	if (object->lookup_data) { -		kfree(object->lookup_data->key); -		kfree(object->lookup_data->auxdata); -		kfree(object->lookup_data); -		object->lookup_data = NULL; -	} +	__refcount_inc(&object->ref, &r); +	trace_cachefiles_ref(object->debug_id, object->cookie->debug_id, r, why); +	return object;  }  /* - * increment the usage count on an inode object (may fail if unmounting) + * dispose of a reference to an object   */ -static -struct fscache_object *cachefiles_grab_object(struct fscache_object *_object, -					      enum fscache_obj_ref_trace why) +void cachefiles_put_object(struct cachefiles_object *object, +			   enum cachefiles_obj_ref_trace why)  { -	struct cachefiles_object *object = -		container_of(_object, struct cachefiles_object, fscache); -	int u; +	unsigned int object_debug_id = object->debug_id; +	unsigned int cookie_debug_id = object->cookie->debug_id; +	struct fscache_cache *cache; +	bool done; +	int r; + +	done = __refcount_dec_and_test(&object->ref, &r); +	trace_cachefiles_ref(object_debug_id, cookie_debug_id, r, why); +	if (done) { +		_debug("- kill object OBJ%x", object_debug_id); + +		ASSERTCMP(object->file, ==, NULL); -	_enter("{OBJ%x,%d}", _object->debug_id, atomic_read(&object->usage)); +		kfree(object->d_name); -#ifdef CACHEFILES_DEBUG_SLAB -	ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); -#endif +		cache = object->volume->cache->cache; +		fscache_put_cookie(object->cookie, fscache_cookie_put_object); +		object->cookie = NULL; +		kmem_cache_free(cachefiles_object_jar, object); +		fscache_uncount_object(cache); +	} -	u = atomic_inc_return(&object->usage); -	trace_cachefiles_ref(object, _object->cookie, -			     (enum cachefiles_obj_ref_trace)why, u); -	return &object->fscache; +	_leave("");  }  /* - * update the auxiliary data for an object object on disk + * Adjust the size of a cache file if necessary to match the DIO size.  We keep + * the EOF marker a multiple of DIO blocks so that we don't fall back to doing + * non-DIO for a partial block straddling the EOF, but we also have to be + * careful of someone expanding the file and accidentally accreting the + * padding.   */ -static void cachefiles_update_object(struct fscache_object *_object) +static int cachefiles_adjust_size(struct cachefiles_object *object)  { -	struct cachefiles_object *object; -	struct cachefiles_xattr *auxdata; -	struct cachefiles_cache *cache; -	struct fscache_cookie *cookie; -	const struct cred *saved_cred; -	const void *aux; -	unsigned auxlen; +	struct iattr newattrs; +	struct file *file = object->file; +	uint64_t ni_size; +	loff_t oi_size; +	int ret; -	_enter("{OBJ%x}", _object->debug_id); +	ni_size = object->cookie->object_size; +	ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE); -	object = container_of(_object, struct cachefiles_object, fscache); -	cache = container_of(object->fscache.cache, struct cachefiles_cache, -			     cache); +	_enter("{OBJ%x},[%llu]", +	       object->debug_id, (unsigned long long) ni_size); -	if (!fscache_use_cookie(_object)) { -		_leave(" [relinq]"); -		return; -	} +	if (!file) +		return -ENOBUFS; -	cookie = object->fscache.cookie; -	auxlen = cookie->aux_len; +	oi_size = i_size_read(file_inode(file)); +	if (oi_size == ni_size) +		return 0; -	if (!auxlen) { -		fscache_unuse_cookie(_object); -		_leave(" [no aux]"); -		return; -	} +	inode_lock(file_inode(file)); -	auxdata = kmalloc(2 + auxlen + 3, cachefiles_gfp); -	if (!auxdata) { -		fscache_unuse_cookie(_object); -		_leave(" [nomem]"); -		return; +	/* if there's an extension to a partial page at the end of the backing +	 * file, we need to discard the partial page so that we pick up new +	 * data after it */ +	if (oi_size & ~PAGE_MASK && ni_size > oi_size) { +		_debug("discard tail %llx", oi_size); +		newattrs.ia_valid = ATTR_SIZE; +		newattrs.ia_size = oi_size & PAGE_MASK; +		ret = cachefiles_inject_remove_error(); +		if (ret == 0) +			ret = notify_change(&init_user_ns, file->f_path.dentry, +					    &newattrs, NULL); +		if (ret < 0) +			goto truncate_failed;  	} -	aux = (auxlen <= sizeof(cookie->inline_aux)) ? -		cookie->inline_aux : cookie->aux; +	newattrs.ia_valid = ATTR_SIZE; +	newattrs.ia_size = ni_size; +	ret = cachefiles_inject_write_error(); +	if (ret == 0) +		ret = notify_change(&init_user_ns, file->f_path.dentry, +				    &newattrs, NULL); -	memcpy(auxdata->data, aux, auxlen); -	fscache_unuse_cookie(_object); +truncate_failed: +	inode_unlock(file_inode(file)); -	auxdata->len = auxlen + 1; -	auxdata->type = cookie->type; +	if (ret < 0) +		trace_cachefiles_io_error(NULL, file_inode(file), ret, +					  cachefiles_trace_notify_change_error); +	if (ret == -EIO) { +		cachefiles_io_error_obj(object, "Size set failed"); +		ret = -ENOBUFS; +	} -	cachefiles_begin_secure(cache, &saved_cred); -	cachefiles_update_object_xattr(object, auxdata); -	cachefiles_end_secure(cache, saved_cred); -	kfree(auxdata); -	_leave(""); +	_leave(" = %d", ret); +	return ret;  }  /* - * discard the resources pinned by an object and effect retirement if - * requested + * Attempt to look up the nominated node in this cache   */ -static void cachefiles_drop_object(struct fscache_object *_object) +static bool cachefiles_lookup_cookie(struct fscache_cookie *cookie)  {  	struct cachefiles_object *object; -	struct cachefiles_cache *cache; +	struct cachefiles_cache *cache = cookie->volume->cache->cache_priv;  	const struct cred *saved_cred; -	struct inode *inode; -	blkcnt_t i_blocks = 0; +	bool success; -	ASSERT(_object); +	object = cachefiles_alloc_object(cookie); +	if (!object) +		goto fail; -	object = container_of(_object, struct cachefiles_object, fscache); +	_enter("{OBJ%x}", object->debug_id); -	_enter("{OBJ%x,%d}", -	       object->fscache.debug_id, atomic_read(&object->usage)); +	if (!cachefiles_cook_key(object)) +		goto fail_put; -	cache = container_of(object->fscache.cache, -			     struct cachefiles_cache, cache); +	cookie->cache_priv = object; -#ifdef CACHEFILES_DEBUG_SLAB -	ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); -#endif +	cachefiles_begin_secure(cache, &saved_cred); -	/* We need to tidy the object up if we did in fact manage to open it. -	 * It's possible for us to get here before the object is fully -	 * initialised if the parent goes away or the object gets retired -	 * before we set it up. -	 */ -	if (object->dentry) { -		/* delete retired objects */ -		if (test_bit(FSCACHE_OBJECT_RETIRED, &object->fscache.flags) && -		    _object != cache->cache.fsdef -		    ) { -			_debug("- retire object OBJ%x", object->fscache.debug_id); -			inode = d_backing_inode(object->dentry); -			if (inode) -				i_blocks = inode->i_blocks; - -			cachefiles_begin_secure(cache, &saved_cred); -			cachefiles_delete_object(cache, object); -			cachefiles_end_secure(cache, saved_cred); -		} +	success = cachefiles_look_up_object(object); +	if (!success) +		goto fail_withdraw; -		/* close the filesystem stuff attached to the object */ -		if (object->backer != object->dentry) -			dput(object->backer); -		object->backer = NULL; -	} +	cachefiles_see_object(object, cachefiles_obj_see_lookup_cookie); + +	spin_lock(&cache->object_list_lock); +	list_add(&object->cache_link, &cache->object_list); +	spin_unlock(&cache->object_list_lock); +	cachefiles_adjust_size(object); -	/* note that the object is now inactive */ -	if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) -		cachefiles_mark_object_inactive(cache, object, i_blocks); +	cachefiles_end_secure(cache, saved_cred); +	_leave(" = t"); +	return true; -	dput(object->dentry); -	object->dentry = NULL; +fail_withdraw: +	cachefiles_end_secure(cache, saved_cred); +	cachefiles_see_object(object, cachefiles_obj_see_lookup_failed); +	fscache_caching_failed(cookie); +	_debug("failed c=%08x o=%08x", cookie->debug_id, object->debug_id); +	/* The caller holds an access count on the cookie, so we need them to +	 * drop it before we can withdraw the object. +	 */ +	return false; -	_leave(""); +fail_put: +	cachefiles_put_object(object, cachefiles_obj_put_alloc_fail); +fail: +	return false;  }  /* - * dispose of a reference to an object + * Shorten the backing object to discard any dirty data and free up + * any unused granules.   */ -void cachefiles_put_object(struct fscache_object *_object, -			   enum fscache_obj_ref_trace why) +static bool cachefiles_shorten_object(struct cachefiles_object *object, +				      struct file *file, loff_t new_size)  { -	struct cachefiles_object *object; -	struct fscache_cache *cache; -	int u; - -	ASSERT(_object); - -	object = container_of(_object, struct cachefiles_object, fscache); - -	_enter("{OBJ%x,%d}", -	       object->fscache.debug_id, atomic_read(&object->usage)); - -#ifdef CACHEFILES_DEBUG_SLAB -	ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); -#endif - -	ASSERTIFCMP(object->fscache.parent, -		    object->fscache.parent->n_children, >, 0); - -	u = atomic_dec_return(&object->usage); -	trace_cachefiles_ref(object, _object->cookie, -			     (enum cachefiles_obj_ref_trace)why, u); -	ASSERTCMP(u, !=, -1); -	if (u == 0) { -		_debug("- kill object OBJ%x", object->fscache.debug_id); +	struct cachefiles_cache *cache = object->volume->cache; +	struct inode *inode = file_inode(file); +	loff_t i_size, dio_size; +	int ret; -		ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); -		ASSERTCMP(object->fscache.parent, ==, NULL); -		ASSERTCMP(object->backer, ==, NULL); -		ASSERTCMP(object->dentry, ==, NULL); -		ASSERTCMP(object->fscache.n_ops, ==, 0); -		ASSERTCMP(object->fscache.n_children, ==, 0); +	dio_size = round_up(new_size, CACHEFILES_DIO_BLOCK_SIZE); +	i_size = i_size_read(inode); + +	trace_cachefiles_trunc(object, inode, i_size, dio_size, +			       cachefiles_trunc_shrink); +	ret = cachefiles_inject_remove_error(); +	if (ret == 0) +		ret = vfs_truncate(&file->f_path, dio_size); +	if (ret < 0) { +		trace_cachefiles_io_error(object, file_inode(file), ret, +					  cachefiles_trace_trunc_error); +		cachefiles_io_error_obj(object, "Trunc-to-size failed %d", ret); +		cachefiles_remove_object_xattr(cache, object, file->f_path.dentry); +		return false; +	} -		if (object->lookup_data) { -			kfree(object->lookup_data->key); -			kfree(object->lookup_data->auxdata); -			kfree(object->lookup_data); -			object->lookup_data = NULL; +	if (new_size < dio_size) { +		trace_cachefiles_trunc(object, inode, dio_size, new_size, +				       cachefiles_trunc_dio_adjust); +		ret = cachefiles_inject_write_error(); +		if (ret == 0) +			ret = vfs_fallocate(file, FALLOC_FL_ZERO_RANGE, +					    new_size, dio_size); +		if (ret < 0) { +			trace_cachefiles_io_error(object, file_inode(file), ret, +						  cachefiles_trace_fallocate_error); +			cachefiles_io_error_obj(object, "Trunc-to-dio-size failed %d", ret); +			cachefiles_remove_object_xattr(cache, object, file->f_path.dentry); +			return false;  		} - -		cache = object->fscache.cache; -		fscache_object_destroy(&object->fscache); -		kmem_cache_free(cachefiles_object_jar, object); -		fscache_object_destroyed(cache);  	} -	_leave(""); +	return true;  }  /* - * sync a cache + * Resize the backing object.   */ -static void cachefiles_sync_cache(struct fscache_cache *_cache) +static void cachefiles_resize_cookie(struct netfs_cache_resources *cres, +				     loff_t new_size)  { -	struct cachefiles_cache *cache; +	struct cachefiles_object *object = cachefiles_cres_object(cres); +	struct cachefiles_cache *cache = object->volume->cache; +	struct fscache_cookie *cookie = object->cookie;  	const struct cred *saved_cred; -	int ret; +	struct file *file = cachefiles_cres_file(cres); +	loff_t old_size = cookie->object_size; -	_enter("%s", _cache->tag->name); +	_enter("%llu->%llu", old_size, new_size); -	cache = container_of(_cache, struct cachefiles_cache, cache); - -	/* make sure all pages pinned by operations on behalf of the netfs are -	 * written to disc */ -	cachefiles_begin_secure(cache, &saved_cred); -	down_read(&cache->mnt->mnt_sb->s_umount); -	ret = sync_filesystem(cache->mnt->mnt_sb); -	up_read(&cache->mnt->mnt_sb->s_umount); -	cachefiles_end_secure(cache, saved_cred); +	if (new_size < old_size) { +		cachefiles_begin_secure(cache, &saved_cred); +		cachefiles_shorten_object(object, file, new_size); +		cachefiles_end_secure(cache, saved_cred); +		object->cookie->object_size = new_size; +		return; +	} -	if (ret == -EIO) -		cachefiles_io_error(cache, -				    "Attempt to sync backing fs superblock" -				    " returned error %d", -				    ret); +	/* The file is being expanded.  We don't need to do anything +	 * particularly.  cookie->initial_size doesn't change and so the point +	 * at which we have to download before doesn't change. +	 */ +	cookie->object_size = new_size;  }  /* - * check if the backing cache is updated to FS-Cache - * - called by FS-Cache when evaluates if need to invalidate the cache + * Commit changes to the object as we drop it.   */ -static int cachefiles_check_consistency(struct fscache_operation *op) +static void cachefiles_commit_object(struct cachefiles_object *object, +				     struct cachefiles_cache *cache)  { -	struct cachefiles_object *object; -	struct cachefiles_cache *cache; -	const struct cred *saved_cred; -	int ret; +	bool update = false; -	_enter("{OBJ%x}", op->object->debug_id); +	if (test_and_clear_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags)) +		update = true; +	if (test_and_clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags)) +		update = true; +	if (update) +		cachefiles_set_object_xattr(object); -	object = container_of(op->object, struct cachefiles_object, fscache); -	cache = container_of(object->fscache.cache, -			     struct cachefiles_cache, cache); +	if (test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) +		cachefiles_commit_tmpfile(cache, object); +} -	cachefiles_begin_secure(cache, &saved_cred); -	ret = cachefiles_check_auxdata(object); -	cachefiles_end_secure(cache, saved_cred); +/* + * Finalise and object and close the VFS structs that we have. + */ +static void cachefiles_clean_up_object(struct cachefiles_object *object, +				       struct cachefiles_cache *cache) +{ +	if (test_bit(FSCACHE_COOKIE_RETIRED, &object->cookie->flags)) { +		if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { +			cachefiles_see_object(object, cachefiles_obj_see_clean_delete); +			_debug("- inval object OBJ%x", object->debug_id); +			cachefiles_delete_object(object, FSCACHE_OBJECT_WAS_RETIRED); +		} else { +			cachefiles_see_object(object, cachefiles_obj_see_clean_drop_tmp); +			_debug("- inval object OBJ%x tmpfile", object->debug_id); +		} +	} else { +		cachefiles_see_object(object, cachefiles_obj_see_clean_commit); +		cachefiles_commit_object(object, cache); +	} -	_leave(" = %d", ret); -	return ret; +	cachefiles_unmark_inode_in_use(object, object->file); +	if (object->file) { +		fput(object->file); +		object->file = NULL; +	}  }  /* - * notification the attributes on an object have changed - * - called with reads/writes excluded by FS-Cache + * Withdraw caching for a cookie.   */ -static int cachefiles_attr_changed(struct fscache_object *_object) +static void cachefiles_withdraw_cookie(struct fscache_cookie *cookie)  { -	struct cachefiles_object *object; -	struct cachefiles_cache *cache; +	struct cachefiles_object *object = cookie->cache_priv; +	struct cachefiles_cache *cache = object->volume->cache;  	const struct cred *saved_cred; -	struct iattr newattrs; -	uint64_t ni_size; -	loff_t oi_size; -	int ret; - -	ni_size = _object->store_limit_l; - -	_enter("{OBJ%x},[%llu]", -	       _object->debug_id, (unsigned long long) ni_size); - -	object = container_of(_object, struct cachefiles_object, fscache); -	cache = container_of(object->fscache.cache, -			     struct cachefiles_cache, cache); - -	if (ni_size == object->i_size) -		return 0; - -	if (!object->backer) -		return -ENOBUFS; -	ASSERT(d_is_reg(object->backer)); +	_enter("o=%x", object->debug_id); +	cachefiles_see_object(object, cachefiles_obj_see_withdraw_cookie); -	fscache_set_store_limit(&object->fscache, ni_size); - -	oi_size = i_size_read(d_backing_inode(object->backer)); -	if (oi_size == ni_size) -		return 0; - -	cachefiles_begin_secure(cache, &saved_cred); -	inode_lock(d_inode(object->backer)); - -	/* if there's an extension to a partial page at the end of the backing -	 * file, we need to discard the partial page so that we pick up new -	 * data after it */ -	if (oi_size & ~PAGE_MASK && ni_size > oi_size) { -		_debug("discard tail %llx", oi_size); -		newattrs.ia_valid = ATTR_SIZE; -		newattrs.ia_size = oi_size & PAGE_MASK; -		ret = notify_change(&init_user_ns, object->backer, &newattrs, NULL); -		if (ret < 0) -			goto truncate_failed; +	if (!list_empty(&object->cache_link)) { +		spin_lock(&cache->object_list_lock); +		cachefiles_see_object(object, cachefiles_obj_see_withdrawal); +		list_del_init(&object->cache_link); +		spin_unlock(&cache->object_list_lock);  	} -	newattrs.ia_valid = ATTR_SIZE; -	newattrs.ia_size = ni_size; -	ret = notify_change(&init_user_ns, object->backer, &newattrs, NULL); - -truncate_failed: -	inode_unlock(d_inode(object->backer)); -	cachefiles_end_secure(cache, saved_cred); - -	if (ret == -EIO) { -		fscache_set_store_limit(&object->fscache, 0); -		cachefiles_io_error_obj(object, "Size set failed"); -		ret = -ENOBUFS; +	if (object->file) { +		cachefiles_begin_secure(cache, &saved_cred); +		cachefiles_clean_up_object(object, cache); +		cachefiles_end_secure(cache, saved_cred);  	} -	_leave(" = %d", ret); -	return ret; +	cookie->cache_priv = NULL; +	cachefiles_put_object(object, cachefiles_obj_put_detach);  }  /* - * Invalidate an object + * Invalidate the storage associated with a cookie.   */ -static void cachefiles_invalidate_object(struct fscache_operation *op) +static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie)  { -	struct cachefiles_object *object; -	struct cachefiles_cache *cache; -	const struct cred *saved_cred; -	struct path path; -	uint64_t ni_size; -	int ret; +	struct cachefiles_object *object = cookie->cache_priv; +	struct file *new_file, *old_file; +	bool old_tmpfile; -	object = container_of(op->object, struct cachefiles_object, fscache); -	cache = container_of(object->fscache.cache, -			     struct cachefiles_cache, cache); +	_enter("o=%x,[%llu]", object->debug_id, object->cookie->object_size); -	ni_size = op->object->store_limit_l; +	old_tmpfile = test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); -	_enter("{OBJ%x},[%llu]", -	       op->object->debug_id, (unsigned long long)ni_size); +	if (!object->file) { +		fscache_resume_after_invalidation(cookie); +		_leave(" = t [light]"); +		return true; +	} -	if (object->backer) { -		ASSERT(d_is_reg(object->backer)); +	new_file = cachefiles_create_tmpfile(object); +	if (IS_ERR(new_file)) +		goto failed; -		fscache_set_store_limit(&object->fscache, ni_size); +	/* Substitute the VFS target */ +	_debug("sub"); +	spin_lock(&object->lock); -		path.dentry = object->backer; -		path.mnt = cache->mnt; +	old_file = object->file; +	object->file = new_file; +	object->content_info = CACHEFILES_CONTENT_NO_DATA; +	set_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); +	set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags); -		cachefiles_begin_secure(cache, &saved_cred); -		ret = vfs_truncate(&path, 0); -		if (ret == 0) -			ret = vfs_truncate(&path, ni_size); -		cachefiles_end_secure(cache, saved_cred); +	spin_unlock(&object->lock); +	_debug("subbed"); + +	/* Allow I/O to take place again */ +	fscache_resume_after_invalidation(cookie); + +	if (old_file) { +		if (!old_tmpfile) { +			struct cachefiles_volume *volume = object->volume; +			struct dentry *fan = volume->fanout[(u8)cookie->key_hash]; -		if (ret != 0) { -			fscache_set_store_limit(&object->fscache, 0); -			if (ret == -EIO) -				cachefiles_io_error_obj(object, -							"Invalidate failed"); +			inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); +			cachefiles_bury_object(volume->cache, object, fan, +					       old_file->f_path.dentry, +					       FSCACHE_OBJECT_INVALIDATED);  		} +		fput(old_file);  	} -	fscache_op_complete(op, true); -	_leave(""); -} +	_leave(" = t"); +	return true; -/* - * dissociate a cache from all the pages it was backing - */ -static void cachefiles_dissociate_pages(struct fscache_cache *cache) -{ -	_enter(""); +failed: +	_leave(" = f"); +	return false;  }  const struct fscache_cache_ops cachefiles_cache_ops = {  	.name			= "cachefiles", -	.alloc_object		= cachefiles_alloc_object, -	.lookup_object		= cachefiles_lookup_object, -	.lookup_complete	= cachefiles_lookup_complete, -	.grab_object		= cachefiles_grab_object, -	.update_object		= cachefiles_update_object, -	.invalidate_object	= cachefiles_invalidate_object, -	.drop_object		= cachefiles_drop_object, -	.put_object		= cachefiles_put_object, -	.sync_cache		= cachefiles_sync_cache, -	.attr_changed		= cachefiles_attr_changed, -	.read_or_alloc_page	= cachefiles_read_or_alloc_page, -	.read_or_alloc_pages	= cachefiles_read_or_alloc_pages, -	.allocate_page		= cachefiles_allocate_page, -	.allocate_pages		= cachefiles_allocate_pages, -	.write_page		= cachefiles_write_page, -	.uncache_page		= cachefiles_uncache_page, -	.dissociate_pages	= cachefiles_dissociate_pages, -	.check_consistency	= cachefiles_check_consistency, -	.begin_read_operation	= cachefiles_begin_read_operation, +	.acquire_volume		= cachefiles_acquire_volume, +	.free_volume		= cachefiles_free_volume, +	.lookup_cookie		= cachefiles_lookup_cookie, +	.withdraw_cookie	= cachefiles_withdraw_cookie, +	.invalidate_cookie	= cachefiles_invalidate_cookie, +	.begin_operation	= cachefiles_begin_operation, +	.resize_cookie		= cachefiles_resize_cookie, +	.prepare_to_write	= cachefiles_prepare_to_write,  }; |