diff options
Diffstat (limited to 'fs/cachefiles/volume.c')
| -rw-r--r-- | fs/cachefiles/volume.c | 139 | 
1 files changed, 139 insertions, 0 deletions
| diff --git a/fs/cachefiles/volume.c b/fs/cachefiles/volume.c new file mode 100644 index 000000000000..89df0ba8ba5e --- /dev/null +++ b/fs/cachefiles/volume.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Volume handling. + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells ([email protected]) + */ + +#include <linux/fs.h> +#include <linux/slab.h> +#include "internal.h" +#include <trace/events/fscache.h> + +/* + * Allocate and set up a volume representation.  We make sure all the fanout + * directories are created and pinned. + */ +void cachefiles_acquire_volume(struct fscache_volume *vcookie) +{ +	struct cachefiles_volume *volume; +	struct cachefiles_cache *cache = vcookie->cache->cache_priv; +	const struct cred *saved_cred; +	struct dentry *vdentry, *fan; +	size_t len; +	char *name; +	bool is_new = false; +	int ret, n_accesses, i; + +	_enter(""); + +	volume = kzalloc(sizeof(struct cachefiles_volume), GFP_KERNEL); +	if (!volume) +		return; +	volume->vcookie = vcookie; +	volume->cache = cache; +	INIT_LIST_HEAD(&volume->cache_link); + +	cachefiles_begin_secure(cache, &saved_cred); + +	len = vcookie->key[0]; +	name = kmalloc(len + 3, GFP_NOFS); +	if (!name) +		goto error_vol; +	name[0] = 'I'; +	memcpy(name + 1, vcookie->key + 1, len); +	name[len + 1] = 0; + +retry: +	vdentry = cachefiles_get_directory(cache, cache->store, name, &is_new); +	if (IS_ERR(vdentry)) +		goto error_name; +	volume->dentry = vdentry; + +	if (is_new) { +		if (!cachefiles_set_volume_xattr(volume)) +			goto error_dir; +	} else { +		ret = cachefiles_check_volume_xattr(volume); +		if (ret < 0) { +			if (ret != -ESTALE) +				goto error_dir; +			inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT); +			cachefiles_bury_object(cache, NULL, cache->store, vdentry, +					       FSCACHE_VOLUME_IS_WEIRD); +			cachefiles_put_directory(volume->dentry); +			cond_resched(); +			goto retry; +		} +	} +	 +	for (i = 0; i < 256; i++) { +		sprintf(name, "@%02x", i); +		fan = cachefiles_get_directory(cache, vdentry, name, NULL); +		if (IS_ERR(fan)) +			goto error_fan; +		volume->fanout[i] = fan; +	} + +	cachefiles_end_secure(cache, saved_cred); + +	vcookie->cache_priv = volume; +	n_accesses = atomic_inc_return(&vcookie->n_accesses); /* Stop wakeups on dec-to-0 */ +	trace_fscache_access_volume(vcookie->debug_id, 0, +				    refcount_read(&vcookie->ref), +				    n_accesses, fscache_access_cache_pin); + +	spin_lock(&cache->object_list_lock); +	list_add(&volume->cache_link, &volume->cache->volumes); +	spin_unlock(&cache->object_list_lock); + +	kfree(name); +	return; + +error_fan: +	for (i = 0; i < 256; i++) +		cachefiles_put_directory(volume->fanout[i]); +error_dir: +	cachefiles_put_directory(volume->dentry); +error_name: +	kfree(name); +error_vol: +	kfree(volume); +	cachefiles_end_secure(cache, saved_cred); +} + +/* + * Release a volume representation. + */ +static void __cachefiles_free_volume(struct cachefiles_volume *volume) +{ +	int i; + +	_enter(""); + +	volume->vcookie->cache_priv = NULL; + +	for (i = 0; i < 256; i++) +		cachefiles_put_directory(volume->fanout[i]); +	cachefiles_put_directory(volume->dentry); +	kfree(volume); +} + +void cachefiles_free_volume(struct fscache_volume *vcookie) +{ +	struct cachefiles_volume *volume = vcookie->cache_priv; + +	if (volume) { +		spin_lock(&volume->cache->object_list_lock); +		list_del_init(&volume->cache_link); +		spin_unlock(&volume->cache->object_list_lock); +		__cachefiles_free_volume(volume); +	} +} + +void cachefiles_withdraw_volume(struct cachefiles_volume *volume) +{ +	fscache_withdraw_volume(volume->vcookie); +	cachefiles_set_volume_xattr(volume); +	__cachefiles_free_volume(volume); +} |