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