|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* Volume handling. | 
|  | * | 
|  | * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. | 
|  | * Written by David Howells (dhowells@redhat.com) | 
|  | */ | 
|  |  | 
|  | #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) | 
|  | { | 
|  | cachefiles_set_volume_xattr(volume); | 
|  | __cachefiles_free_volume(volume); | 
|  | } |