162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Volume handling.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include "internal.h"
1162306a36Sopenharmony_ci#include <trace/events/fscache.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * Allocate and set up a volume representation.  We make sure all the fanout
1562306a36Sopenharmony_ci * directories are created and pinned.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_civoid cachefiles_acquire_volume(struct fscache_volume *vcookie)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct cachefiles_volume *volume;
2062306a36Sopenharmony_ci	struct cachefiles_cache *cache = vcookie->cache->cache_priv;
2162306a36Sopenharmony_ci	const struct cred *saved_cred;
2262306a36Sopenharmony_ci	struct dentry *vdentry, *fan;
2362306a36Sopenharmony_ci	size_t len;
2462306a36Sopenharmony_ci	char *name;
2562306a36Sopenharmony_ci	bool is_new = false;
2662306a36Sopenharmony_ci	int ret, n_accesses, i;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	_enter("");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	volume = kzalloc(sizeof(struct cachefiles_volume), GFP_KERNEL);
3162306a36Sopenharmony_ci	if (!volume)
3262306a36Sopenharmony_ci		return;
3362306a36Sopenharmony_ci	volume->vcookie = vcookie;
3462306a36Sopenharmony_ci	volume->cache = cache;
3562306a36Sopenharmony_ci	INIT_LIST_HEAD(&volume->cache_link);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	cachefiles_begin_secure(cache, &saved_cred);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	len = vcookie->key[0];
4062306a36Sopenharmony_ci	name = kmalloc(len + 3, GFP_NOFS);
4162306a36Sopenharmony_ci	if (!name)
4262306a36Sopenharmony_ci		goto error_vol;
4362306a36Sopenharmony_ci	name[0] = 'I';
4462306a36Sopenharmony_ci	memcpy(name + 1, vcookie->key + 1, len);
4562306a36Sopenharmony_ci	name[len + 1] = 0;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciretry:
4862306a36Sopenharmony_ci	vdentry = cachefiles_get_directory(cache, cache->store, name, &is_new);
4962306a36Sopenharmony_ci	if (IS_ERR(vdentry))
5062306a36Sopenharmony_ci		goto error_name;
5162306a36Sopenharmony_ci	volume->dentry = vdentry;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (is_new) {
5462306a36Sopenharmony_ci		if (!cachefiles_set_volume_xattr(volume))
5562306a36Sopenharmony_ci			goto error_dir;
5662306a36Sopenharmony_ci	} else {
5762306a36Sopenharmony_ci		ret = cachefiles_check_volume_xattr(volume);
5862306a36Sopenharmony_ci		if (ret < 0) {
5962306a36Sopenharmony_ci			if (ret != -ESTALE)
6062306a36Sopenharmony_ci				goto error_dir;
6162306a36Sopenharmony_ci			inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT);
6262306a36Sopenharmony_ci			cachefiles_bury_object(cache, NULL, cache->store, vdentry,
6362306a36Sopenharmony_ci					       FSCACHE_VOLUME_IS_WEIRD);
6462306a36Sopenharmony_ci			cachefiles_put_directory(volume->dentry);
6562306a36Sopenharmony_ci			cond_resched();
6662306a36Sopenharmony_ci			goto retry;
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
7162306a36Sopenharmony_ci		sprintf(name, "@%02x", i);
7262306a36Sopenharmony_ci		fan = cachefiles_get_directory(cache, vdentry, name, NULL);
7362306a36Sopenharmony_ci		if (IS_ERR(fan))
7462306a36Sopenharmony_ci			goto error_fan;
7562306a36Sopenharmony_ci		volume->fanout[i] = fan;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	cachefiles_end_secure(cache, saved_cred);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	vcookie->cache_priv = volume;
8162306a36Sopenharmony_ci	n_accesses = atomic_inc_return(&vcookie->n_accesses); /* Stop wakeups on dec-to-0 */
8262306a36Sopenharmony_ci	trace_fscache_access_volume(vcookie->debug_id, 0,
8362306a36Sopenharmony_ci				    refcount_read(&vcookie->ref),
8462306a36Sopenharmony_ci				    n_accesses, fscache_access_cache_pin);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	spin_lock(&cache->object_list_lock);
8762306a36Sopenharmony_ci	list_add(&volume->cache_link, &volume->cache->volumes);
8862306a36Sopenharmony_ci	spin_unlock(&cache->object_list_lock);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	kfree(name);
9162306a36Sopenharmony_ci	return;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cierror_fan:
9462306a36Sopenharmony_ci	for (i = 0; i < 256; i++)
9562306a36Sopenharmony_ci		cachefiles_put_directory(volume->fanout[i]);
9662306a36Sopenharmony_cierror_dir:
9762306a36Sopenharmony_ci	cachefiles_put_directory(volume->dentry);
9862306a36Sopenharmony_cierror_name:
9962306a36Sopenharmony_ci	kfree(name);
10062306a36Sopenharmony_cierror_vol:
10162306a36Sopenharmony_ci	kfree(volume);
10262306a36Sopenharmony_ci	cachefiles_end_secure(cache, saved_cred);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/*
10662306a36Sopenharmony_ci * Release a volume representation.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistatic void __cachefiles_free_volume(struct cachefiles_volume *volume)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	int i;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	_enter("");
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	volume->vcookie->cache_priv = NULL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	for (i = 0; i < 256; i++)
11762306a36Sopenharmony_ci		cachefiles_put_directory(volume->fanout[i]);
11862306a36Sopenharmony_ci	cachefiles_put_directory(volume->dentry);
11962306a36Sopenharmony_ci	kfree(volume);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid cachefiles_free_volume(struct fscache_volume *vcookie)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct cachefiles_volume *volume = vcookie->cache_priv;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (volume) {
12762306a36Sopenharmony_ci		spin_lock(&volume->cache->object_list_lock);
12862306a36Sopenharmony_ci		list_del_init(&volume->cache_link);
12962306a36Sopenharmony_ci		spin_unlock(&volume->cache->object_list_lock);
13062306a36Sopenharmony_ci		__cachefiles_free_volume(volume);
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_civoid cachefiles_withdraw_volume(struct cachefiles_volume *volume)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	fscache_withdraw_volume(volume->vcookie);
13762306a36Sopenharmony_ci	cachefiles_set_volume_xattr(volume);
13862306a36Sopenharmony_ci	__cachefiles_free_volume(volume);
13962306a36Sopenharmony_ci}
140