// 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 #include #include "internal.h" #include /* * 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); }