xref: /linux/fs/cachefiles/volume.c (revision 83ab4b461eb7bdf90984eb56d4954dbe11e926d4)
1fe2140e2SDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
2fe2140e2SDavid Howells /* Volume handling.
3fe2140e2SDavid Howells  *
4fe2140e2SDavid Howells  * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
5fe2140e2SDavid Howells  * Written by David Howells (dhowells@redhat.com)
6fe2140e2SDavid Howells  */
7fe2140e2SDavid Howells 
8fe2140e2SDavid Howells #include <linux/fs.h>
9fe2140e2SDavid Howells #include <linux/slab.h>
10fe2140e2SDavid Howells #include "internal.h"
11fe2140e2SDavid Howells #include <trace/events/fscache.h>
12fe2140e2SDavid Howells 
13fe2140e2SDavid Howells /*
14fe2140e2SDavid Howells  * Allocate and set up a volume representation.  We make sure all the fanout
15fe2140e2SDavid Howells  * directories are created and pinned.
16fe2140e2SDavid Howells  */
cachefiles_acquire_volume(struct fscache_volume * vcookie)17fe2140e2SDavid Howells void cachefiles_acquire_volume(struct fscache_volume *vcookie)
18fe2140e2SDavid Howells {
19fe2140e2SDavid Howells 	struct cachefiles_volume *volume;
20fe2140e2SDavid Howells 	struct cachefiles_cache *cache = vcookie->cache->cache_priv;
21fe2140e2SDavid Howells 	const struct cred *saved_cred;
22fe2140e2SDavid Howells 	struct dentry *vdentry, *fan;
23fe2140e2SDavid Howells 	size_t len;
24fe2140e2SDavid Howells 	char *name;
25*32e15003SDavid Howells 	bool is_new = false;
26*32e15003SDavid Howells 	int ret, n_accesses, i;
27fe2140e2SDavid Howells 
28fe2140e2SDavid Howells 	_enter("");
29fe2140e2SDavid Howells 
30fe2140e2SDavid Howells 	volume = kzalloc(sizeof(struct cachefiles_volume), GFP_KERNEL);
31fe2140e2SDavid Howells 	if (!volume)
32fe2140e2SDavid Howells 		return;
33fe2140e2SDavid Howells 	volume->vcookie = vcookie;
34fe2140e2SDavid Howells 	volume->cache = cache;
35fe2140e2SDavid Howells 	INIT_LIST_HEAD(&volume->cache_link);
36fe2140e2SDavid Howells 
37fe2140e2SDavid Howells 	cachefiles_begin_secure(cache, &saved_cred);
38fe2140e2SDavid Howells 
39fe2140e2SDavid Howells 	len = vcookie->key[0];
40fe2140e2SDavid Howells 	name = kmalloc(len + 3, GFP_NOFS);
41fe2140e2SDavid Howells 	if (!name)
42fe2140e2SDavid Howells 		goto error_vol;
43fe2140e2SDavid Howells 	name[0] = 'I';
44fe2140e2SDavid Howells 	memcpy(name + 1, vcookie->key + 1, len);
45fe2140e2SDavid Howells 	name[len + 1] = 0;
46fe2140e2SDavid Howells 
47*32e15003SDavid Howells retry:
48*32e15003SDavid Howells 	vdentry = cachefiles_get_directory(cache, cache->store, name, &is_new);
49fe2140e2SDavid Howells 	if (IS_ERR(vdentry))
50fe2140e2SDavid Howells 		goto error_name;
51fe2140e2SDavid Howells 	volume->dentry = vdentry;
52fe2140e2SDavid Howells 
53*32e15003SDavid Howells 	if (is_new) {
54*32e15003SDavid Howells 		if (!cachefiles_set_volume_xattr(volume))
55*32e15003SDavid Howells 			goto error_dir;
56*32e15003SDavid Howells 	} else {
57*32e15003SDavid Howells 		ret = cachefiles_check_volume_xattr(volume);
58*32e15003SDavid Howells 		if (ret < 0) {
59*32e15003SDavid Howells 			if (ret != -ESTALE)
60*32e15003SDavid Howells 				goto error_dir;
61*32e15003SDavid Howells 			inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT);
62*32e15003SDavid Howells 			cachefiles_bury_object(cache, NULL, cache->store, vdentry,
63*32e15003SDavid Howells 					       FSCACHE_VOLUME_IS_WEIRD);
64*32e15003SDavid Howells 			cachefiles_put_directory(volume->dentry);
65*32e15003SDavid Howells 			cond_resched();
66*32e15003SDavid Howells 			goto retry;
67*32e15003SDavid Howells 		}
68*32e15003SDavid Howells 	}
69*32e15003SDavid Howells 
70fe2140e2SDavid Howells 	for (i = 0; i < 256; i++) {
71fe2140e2SDavid Howells 		sprintf(name, "@%02x", i);
72fe2140e2SDavid Howells 		fan = cachefiles_get_directory(cache, vdentry, name, NULL);
73fe2140e2SDavid Howells 		if (IS_ERR(fan))
74fe2140e2SDavid Howells 			goto error_fan;
75fe2140e2SDavid Howells 		volume->fanout[i] = fan;
76fe2140e2SDavid Howells 	}
77fe2140e2SDavid Howells 
78fe2140e2SDavid Howells 	cachefiles_end_secure(cache, saved_cred);
79fe2140e2SDavid Howells 
80fe2140e2SDavid Howells 	vcookie->cache_priv = volume;
81fe2140e2SDavid Howells 	n_accesses = atomic_inc_return(&vcookie->n_accesses); /* Stop wakeups on dec-to-0 */
82fe2140e2SDavid Howells 	trace_fscache_access_volume(vcookie->debug_id, 0,
83fe2140e2SDavid Howells 				    refcount_read(&vcookie->ref),
84fe2140e2SDavid Howells 				    n_accesses, fscache_access_cache_pin);
85fe2140e2SDavid Howells 
86fe2140e2SDavid Howells 	spin_lock(&cache->object_list_lock);
87fe2140e2SDavid Howells 	list_add(&volume->cache_link, &volume->cache->volumes);
88fe2140e2SDavid Howells 	spin_unlock(&cache->object_list_lock);
89fe2140e2SDavid Howells 
90fe2140e2SDavid Howells 	kfree(name);
91fe2140e2SDavid Howells 	return;
92fe2140e2SDavid Howells 
93fe2140e2SDavid Howells error_fan:
94fe2140e2SDavid Howells 	for (i = 0; i < 256; i++)
95fe2140e2SDavid Howells 		cachefiles_put_directory(volume->fanout[i]);
96*32e15003SDavid Howells error_dir:
97fe2140e2SDavid Howells 	cachefiles_put_directory(volume->dentry);
98fe2140e2SDavid Howells error_name:
99fe2140e2SDavid Howells 	kfree(name);
100fe2140e2SDavid Howells error_vol:
101fe2140e2SDavid Howells 	kfree(volume);
102fe2140e2SDavid Howells 	cachefiles_end_secure(cache, saved_cred);
103fe2140e2SDavid Howells }
104fe2140e2SDavid Howells 
105fe2140e2SDavid Howells /*
106fe2140e2SDavid Howells  * Release a volume representation.
107fe2140e2SDavid Howells  */
__cachefiles_free_volume(struct cachefiles_volume * volume)108fe2140e2SDavid Howells static void __cachefiles_free_volume(struct cachefiles_volume *volume)
109fe2140e2SDavid Howells {
110fe2140e2SDavid Howells 	int i;
111fe2140e2SDavid Howells 
112fe2140e2SDavid Howells 	_enter("");
113fe2140e2SDavid Howells 
114fe2140e2SDavid Howells 	volume->vcookie->cache_priv = NULL;
115fe2140e2SDavid Howells 
116fe2140e2SDavid Howells 	for (i = 0; i < 256; i++)
117fe2140e2SDavid Howells 		cachefiles_put_directory(volume->fanout[i]);
118fe2140e2SDavid Howells 	cachefiles_put_directory(volume->dentry);
119fe2140e2SDavid Howells 	kfree(volume);
120fe2140e2SDavid Howells }
121fe2140e2SDavid Howells 
cachefiles_free_volume(struct fscache_volume * vcookie)122fe2140e2SDavid Howells void cachefiles_free_volume(struct fscache_volume *vcookie)
123fe2140e2SDavid Howells {
124fe2140e2SDavid Howells 	struct cachefiles_volume *volume = vcookie->cache_priv;
125fe2140e2SDavid Howells 
126fe2140e2SDavid Howells 	if (volume) {
127fe2140e2SDavid Howells 		spin_lock(&volume->cache->object_list_lock);
128fe2140e2SDavid Howells 		list_del_init(&volume->cache_link);
129fe2140e2SDavid Howells 		spin_unlock(&volume->cache->object_list_lock);
130fe2140e2SDavid Howells 		__cachefiles_free_volume(volume);
131fe2140e2SDavid Howells 	}
132fe2140e2SDavid Howells }
133fe2140e2SDavid Howells 
cachefiles_withdraw_volume(struct cachefiles_volume * volume)134fe2140e2SDavid Howells void cachefiles_withdraw_volume(struct cachefiles_volume *volume)
135fe2140e2SDavid Howells {
136*32e15003SDavid Howells 	cachefiles_set_volume_xattr(volume);
137fe2140e2SDavid Howells 	__cachefiles_free_volume(volume);
138fe2140e2SDavid Howells }
139