162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* CacheFiles path walking and related routines 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/namei.h> 1062306a36Sopenharmony_ci#include "internal.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * Mark the backing file as being a cache file if it's not already in use. The 1462306a36Sopenharmony_ci * mark tells the culling request command that it's not allowed to cull the 1562306a36Sopenharmony_ci * file or directory. The caller must hold the inode lock. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_cistatic bool __cachefiles_mark_inode_in_use(struct cachefiles_object *object, 1862306a36Sopenharmony_ci struct inode *inode) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci bool can_use = false; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci if (!(inode->i_flags & S_KERNEL_FILE)) { 2362306a36Sopenharmony_ci inode->i_flags |= S_KERNEL_FILE; 2462306a36Sopenharmony_ci trace_cachefiles_mark_active(object, inode); 2562306a36Sopenharmony_ci can_use = true; 2662306a36Sopenharmony_ci } else { 2762306a36Sopenharmony_ci trace_cachefiles_mark_failed(object, inode); 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return can_use; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic bool cachefiles_mark_inode_in_use(struct cachefiles_object *object, 3462306a36Sopenharmony_ci struct inode *inode) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci bool can_use; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci inode_lock(inode); 3962306a36Sopenharmony_ci can_use = __cachefiles_mark_inode_in_use(object, inode); 4062306a36Sopenharmony_ci inode_unlock(inode); 4162306a36Sopenharmony_ci return can_use; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * Unmark a backing inode. The caller must hold the inode lock. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object, 4862306a36Sopenharmony_ci struct inode *inode) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci inode->i_flags &= ~S_KERNEL_FILE; 5162306a36Sopenharmony_ci trace_cachefiles_mark_inactive(object, inode); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void cachefiles_do_unmark_inode_in_use(struct cachefiles_object *object, 5562306a36Sopenharmony_ci struct inode *inode) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci inode_lock(inode); 5862306a36Sopenharmony_ci __cachefiles_unmark_inode_in_use(object, inode); 5962306a36Sopenharmony_ci inode_unlock(inode); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * Unmark a backing inode and tell cachefilesd that there's something that can 6462306a36Sopenharmony_ci * be culled. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_civoid cachefiles_unmark_inode_in_use(struct cachefiles_object *object, 6762306a36Sopenharmony_ci struct file *file) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct cachefiles_cache *cache = object->volume->cache; 7062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci cachefiles_do_unmark_inode_in_use(object, inode); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { 7562306a36Sopenharmony_ci atomic_long_add(inode->i_blocks, &cache->b_released); 7662306a36Sopenharmony_ci if (atomic_inc_return(&cache->f_released)) 7762306a36Sopenharmony_ci cachefiles_state_changed(cache); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * get a subdirectory 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistruct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, 8562306a36Sopenharmony_ci struct dentry *dir, 8662306a36Sopenharmony_ci const char *dirname, 8762306a36Sopenharmony_ci bool *_is_new) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct dentry *subdir; 9062306a36Sopenharmony_ci struct path path; 9162306a36Sopenharmony_ci int ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci _enter(",,%s", dirname); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* search the current directory for the element name */ 9662306a36Sopenharmony_ci inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciretry: 9962306a36Sopenharmony_ci ret = cachefiles_inject_read_error(); 10062306a36Sopenharmony_ci if (ret == 0) 10162306a36Sopenharmony_ci subdir = lookup_one_len(dirname, dir, strlen(dirname)); 10262306a36Sopenharmony_ci else 10362306a36Sopenharmony_ci subdir = ERR_PTR(ret); 10462306a36Sopenharmony_ci trace_cachefiles_lookup(NULL, dir, subdir); 10562306a36Sopenharmony_ci if (IS_ERR(subdir)) { 10662306a36Sopenharmony_ci trace_cachefiles_vfs_error(NULL, d_backing_inode(dir), 10762306a36Sopenharmony_ci PTR_ERR(subdir), 10862306a36Sopenharmony_ci cachefiles_trace_lookup_error); 10962306a36Sopenharmony_ci if (PTR_ERR(subdir) == -ENOMEM) 11062306a36Sopenharmony_ci goto nomem_d_alloc; 11162306a36Sopenharmony_ci goto lookup_error; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci _debug("subdir -> %pd %s", 11562306a36Sopenharmony_ci subdir, d_backing_inode(subdir) ? "positive" : "negative"); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* we need to create the subdir if it doesn't exist yet */ 11862306a36Sopenharmony_ci if (d_is_negative(subdir)) { 11962306a36Sopenharmony_ci ret = cachefiles_has_space(cache, 1, 0, 12062306a36Sopenharmony_ci cachefiles_has_space_for_create); 12162306a36Sopenharmony_ci if (ret < 0) 12262306a36Sopenharmony_ci goto mkdir_error; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci _debug("attempt mkdir"); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci path.mnt = cache->mnt; 12762306a36Sopenharmony_ci path.dentry = dir; 12862306a36Sopenharmony_ci ret = security_path_mkdir(&path, subdir, 0700); 12962306a36Sopenharmony_ci if (ret < 0) 13062306a36Sopenharmony_ci goto mkdir_error; 13162306a36Sopenharmony_ci ret = cachefiles_inject_write_error(); 13262306a36Sopenharmony_ci if (ret == 0) 13362306a36Sopenharmony_ci ret = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700); 13462306a36Sopenharmony_ci if (ret < 0) { 13562306a36Sopenharmony_ci trace_cachefiles_vfs_error(NULL, d_inode(dir), ret, 13662306a36Sopenharmony_ci cachefiles_trace_mkdir_error); 13762306a36Sopenharmony_ci goto mkdir_error; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci trace_cachefiles_mkdir(dir, subdir); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (unlikely(d_unhashed(subdir))) { 14262306a36Sopenharmony_ci cachefiles_put_directory(subdir); 14362306a36Sopenharmony_ci goto retry; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci ASSERT(d_backing_inode(subdir)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci _debug("mkdir -> %pd{ino=%lu}", 14862306a36Sopenharmony_ci subdir, d_backing_inode(subdir)->i_ino); 14962306a36Sopenharmony_ci if (_is_new) 15062306a36Sopenharmony_ci *_is_new = true; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Tell rmdir() it's not allowed to delete the subdir */ 15462306a36Sopenharmony_ci inode_lock(d_inode(subdir)); 15562306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) { 15862306a36Sopenharmony_ci pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n", 15962306a36Sopenharmony_ci subdir, d_inode(subdir)->i_ino); 16062306a36Sopenharmony_ci goto mark_error; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci inode_unlock(d_inode(subdir)); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* we need to make sure the subdir is a directory */ 16662306a36Sopenharmony_ci ASSERT(d_backing_inode(subdir)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!d_can_lookup(subdir)) { 16962306a36Sopenharmony_ci pr_err("%s is not a directory\n", dirname); 17062306a36Sopenharmony_ci ret = -EIO; 17162306a36Sopenharmony_ci goto check_error; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ret = -EPERM; 17562306a36Sopenharmony_ci if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) || 17662306a36Sopenharmony_ci !d_backing_inode(subdir)->i_op->lookup || 17762306a36Sopenharmony_ci !d_backing_inode(subdir)->i_op->mkdir || 17862306a36Sopenharmony_ci !d_backing_inode(subdir)->i_op->rename || 17962306a36Sopenharmony_ci !d_backing_inode(subdir)->i_op->rmdir || 18062306a36Sopenharmony_ci !d_backing_inode(subdir)->i_op->unlink) 18162306a36Sopenharmony_ci goto check_error; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci _leave(" = [%lu]", d_backing_inode(subdir)->i_ino); 18462306a36Sopenharmony_ci return subdir; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cicheck_error: 18762306a36Sopenharmony_ci cachefiles_put_directory(subdir); 18862306a36Sopenharmony_ci _leave(" = %d [check]", ret); 18962306a36Sopenharmony_ci return ERR_PTR(ret); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cimark_error: 19262306a36Sopenharmony_ci inode_unlock(d_inode(subdir)); 19362306a36Sopenharmony_ci dput(subdir); 19462306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cimkdir_error: 19762306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 19862306a36Sopenharmony_ci dput(subdir); 19962306a36Sopenharmony_ci pr_err("mkdir %s failed with error %d\n", dirname, ret); 20062306a36Sopenharmony_ci return ERR_PTR(ret); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cilookup_error: 20362306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 20462306a36Sopenharmony_ci ret = PTR_ERR(subdir); 20562306a36Sopenharmony_ci pr_err("Lookup %s failed with error %d\n", dirname, ret); 20662306a36Sopenharmony_ci return ERR_PTR(ret); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cinomem_d_alloc: 20962306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 21062306a36Sopenharmony_ci _leave(" = -ENOMEM"); 21162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * Put a subdirectory. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_civoid cachefiles_put_directory(struct dentry *dir) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if (dir) { 22062306a36Sopenharmony_ci cachefiles_do_unmark_inode_in_use(NULL, d_inode(dir)); 22162306a36Sopenharmony_ci dput(dir); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * Remove a regular file from the cache. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic int cachefiles_unlink(struct cachefiles_cache *cache, 22962306a36Sopenharmony_ci struct cachefiles_object *object, 23062306a36Sopenharmony_ci struct dentry *dir, struct dentry *dentry, 23162306a36Sopenharmony_ci enum fscache_why_object_killed why) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct path path = { 23462306a36Sopenharmony_ci .mnt = cache->mnt, 23562306a36Sopenharmony_ci .dentry = dir, 23662306a36Sopenharmony_ci }; 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci trace_cachefiles_unlink(object, d_inode(dentry)->i_ino, why); 24062306a36Sopenharmony_ci ret = security_path_unlink(&path, dentry); 24162306a36Sopenharmony_ci if (ret < 0) { 24262306a36Sopenharmony_ci cachefiles_io_error(cache, "Unlink security error"); 24362306a36Sopenharmony_ci return ret; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci ret = cachefiles_inject_remove_error(); 24762306a36Sopenharmony_ci if (ret == 0) { 24862306a36Sopenharmony_ci ret = vfs_unlink(&nop_mnt_idmap, d_backing_inode(dir), dentry, NULL); 24962306a36Sopenharmony_ci if (ret == -EIO) 25062306a36Sopenharmony_ci cachefiles_io_error(cache, "Unlink failed"); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci if (ret != 0) 25362306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_backing_inode(dir), ret, 25462306a36Sopenharmony_ci cachefiles_trace_unlink_error); 25562306a36Sopenharmony_ci return ret; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* 25962306a36Sopenharmony_ci * Delete an object representation from the cache 26062306a36Sopenharmony_ci * - File backed objects are unlinked 26162306a36Sopenharmony_ci * - Directory backed objects are stuffed into the graveyard for userspace to 26262306a36Sopenharmony_ci * delete 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ciint cachefiles_bury_object(struct cachefiles_cache *cache, 26562306a36Sopenharmony_ci struct cachefiles_object *object, 26662306a36Sopenharmony_ci struct dentry *dir, 26762306a36Sopenharmony_ci struct dentry *rep, 26862306a36Sopenharmony_ci enum fscache_why_object_killed why) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct dentry *grave, *trap; 27162306a36Sopenharmony_ci struct path path, path_to_graveyard; 27262306a36Sopenharmony_ci char nbuffer[8 + 8 + 1]; 27362306a36Sopenharmony_ci int ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci _enter(",'%pd','%pd'", dir, rep); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (rep->d_parent != dir) { 27862306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 27962306a36Sopenharmony_ci _leave(" = -ESTALE"); 28062306a36Sopenharmony_ci return -ESTALE; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* non-directories can just be unlinked */ 28462306a36Sopenharmony_ci if (!d_is_dir(rep)) { 28562306a36Sopenharmony_ci dget(rep); /* Stop the dentry being negated if it's only pinned 28662306a36Sopenharmony_ci * by a file struct. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci ret = cachefiles_unlink(cache, object, dir, rep, why); 28962306a36Sopenharmony_ci dput(rep); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 29262306a36Sopenharmony_ci _leave(" = %d", ret); 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* directories have to be moved to the graveyard */ 29762306a36Sopenharmony_ci _debug("move stale object to graveyard"); 29862306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_citry_again: 30162306a36Sopenharmony_ci /* first step is to make up a grave dentry in the graveyard */ 30262306a36Sopenharmony_ci sprintf(nbuffer, "%08x%08x", 30362306a36Sopenharmony_ci (uint32_t) ktime_get_real_seconds(), 30462306a36Sopenharmony_ci (uint32_t) atomic_inc_return(&cache->gravecounter)); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* do the multiway lock magic */ 30762306a36Sopenharmony_ci trap = lock_rename(cache->graveyard, dir); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* do some checks before getting the grave dentry */ 31062306a36Sopenharmony_ci if (rep->d_parent != dir || IS_DEADDIR(d_inode(rep))) { 31162306a36Sopenharmony_ci /* the entry was probably culled when we dropped the parent dir 31262306a36Sopenharmony_ci * lock */ 31362306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 31462306a36Sopenharmony_ci _leave(" = 0 [culled?]"); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!d_can_lookup(cache->graveyard)) { 31962306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 32062306a36Sopenharmony_ci cachefiles_io_error(cache, "Graveyard no longer a directory"); 32162306a36Sopenharmony_ci return -EIO; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (trap == rep) { 32562306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 32662306a36Sopenharmony_ci cachefiles_io_error(cache, "May not make directory loop"); 32762306a36Sopenharmony_ci return -EIO; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (d_mountpoint(rep)) { 33162306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 33262306a36Sopenharmony_ci cachefiles_io_error(cache, "Mountpoint in cache"); 33362306a36Sopenharmony_ci return -EIO; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci grave = lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer)); 33762306a36Sopenharmony_ci if (IS_ERR(grave)) { 33862306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 33962306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_inode(cache->graveyard), 34062306a36Sopenharmony_ci PTR_ERR(grave), 34162306a36Sopenharmony_ci cachefiles_trace_lookup_error); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (PTR_ERR(grave) == -ENOMEM) { 34462306a36Sopenharmony_ci _leave(" = -ENOMEM"); 34562306a36Sopenharmony_ci return -ENOMEM; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci cachefiles_io_error(cache, "Lookup error %ld", PTR_ERR(grave)); 34962306a36Sopenharmony_ci return -EIO; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (d_is_positive(grave)) { 35362306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 35462306a36Sopenharmony_ci dput(grave); 35562306a36Sopenharmony_ci grave = NULL; 35662306a36Sopenharmony_ci cond_resched(); 35762306a36Sopenharmony_ci goto try_again; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (d_mountpoint(grave)) { 36162306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 36262306a36Sopenharmony_ci dput(grave); 36362306a36Sopenharmony_ci cachefiles_io_error(cache, "Mountpoint in graveyard"); 36462306a36Sopenharmony_ci return -EIO; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* target should not be an ancestor of source */ 36862306a36Sopenharmony_ci if (trap == grave) { 36962306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 37062306a36Sopenharmony_ci dput(grave); 37162306a36Sopenharmony_ci cachefiles_io_error(cache, "May not make directory loop"); 37262306a36Sopenharmony_ci return -EIO; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* attempt the rename */ 37662306a36Sopenharmony_ci path.mnt = cache->mnt; 37762306a36Sopenharmony_ci path.dentry = dir; 37862306a36Sopenharmony_ci path_to_graveyard.mnt = cache->mnt; 37962306a36Sopenharmony_ci path_to_graveyard.dentry = cache->graveyard; 38062306a36Sopenharmony_ci ret = security_path_rename(&path, rep, &path_to_graveyard, grave, 0); 38162306a36Sopenharmony_ci if (ret < 0) { 38262306a36Sopenharmony_ci cachefiles_io_error(cache, "Rename security error %d", ret); 38362306a36Sopenharmony_ci } else { 38462306a36Sopenharmony_ci struct renamedata rd = { 38562306a36Sopenharmony_ci .old_mnt_idmap = &nop_mnt_idmap, 38662306a36Sopenharmony_ci .old_dir = d_inode(dir), 38762306a36Sopenharmony_ci .old_dentry = rep, 38862306a36Sopenharmony_ci .new_mnt_idmap = &nop_mnt_idmap, 38962306a36Sopenharmony_ci .new_dir = d_inode(cache->graveyard), 39062306a36Sopenharmony_ci .new_dentry = grave, 39162306a36Sopenharmony_ci }; 39262306a36Sopenharmony_ci trace_cachefiles_rename(object, d_inode(rep)->i_ino, why); 39362306a36Sopenharmony_ci ret = cachefiles_inject_read_error(); 39462306a36Sopenharmony_ci if (ret == 0) 39562306a36Sopenharmony_ci ret = vfs_rename(&rd); 39662306a36Sopenharmony_ci if (ret != 0) 39762306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_inode(dir), ret, 39862306a36Sopenharmony_ci cachefiles_trace_rename_error); 39962306a36Sopenharmony_ci if (ret != 0 && ret != -ENOMEM) 40062306a36Sopenharmony_ci cachefiles_io_error(cache, 40162306a36Sopenharmony_ci "Rename failed with error %d", ret); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci __cachefiles_unmark_inode_in_use(object, d_inode(rep)); 40562306a36Sopenharmony_ci unlock_rename(cache->graveyard, dir); 40662306a36Sopenharmony_ci dput(grave); 40762306a36Sopenharmony_ci _leave(" = 0"); 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* 41262306a36Sopenharmony_ci * Delete a cache file. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ciint cachefiles_delete_object(struct cachefiles_object *object, 41562306a36Sopenharmony_ci enum fscache_why_object_killed why) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct cachefiles_volume *volume = object->volume; 41862306a36Sopenharmony_ci struct dentry *dentry = object->file->f_path.dentry; 41962306a36Sopenharmony_ci struct dentry *fan = volume->fanout[(u8)object->cookie->key_hash]; 42062306a36Sopenharmony_ci int ret; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci _enter(",OBJ%x{%pD}", object->debug_id, object->file); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Stop the dentry being negated if it's only pinned by a file struct. */ 42562306a36Sopenharmony_ci dget(dentry); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci inode_lock_nested(d_backing_inode(fan), I_MUTEX_PARENT); 42862306a36Sopenharmony_ci ret = cachefiles_unlink(volume->cache, object, fan, dentry, why); 42962306a36Sopenharmony_ci inode_unlock(d_backing_inode(fan)); 43062306a36Sopenharmony_ci dput(dentry); 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * Create a temporary file and leave it unattached and un-xattr'd until the 43662306a36Sopenharmony_ci * time comes to discard the object from memory. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_cistruct file *cachefiles_create_tmpfile(struct cachefiles_object *object) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct cachefiles_volume *volume = object->volume; 44162306a36Sopenharmony_ci struct cachefiles_cache *cache = volume->cache; 44262306a36Sopenharmony_ci const struct cred *saved_cred; 44362306a36Sopenharmony_ci struct dentry *fan = volume->fanout[(u8)object->cookie->key_hash]; 44462306a36Sopenharmony_ci struct file *file; 44562306a36Sopenharmony_ci const struct path parentpath = { .mnt = cache->mnt, .dentry = fan }; 44662306a36Sopenharmony_ci uint64_t ni_size; 44762306a36Sopenharmony_ci long ret; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci cachefiles_begin_secure(cache, &saved_cred); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ret = cachefiles_inject_write_error(); 45362306a36Sopenharmony_ci if (ret == 0) { 45462306a36Sopenharmony_ci file = kernel_tmpfile_open(&nop_mnt_idmap, &parentpath, 45562306a36Sopenharmony_ci S_IFREG | 0600, 45662306a36Sopenharmony_ci O_RDWR | O_LARGEFILE | O_DIRECT, 45762306a36Sopenharmony_ci cache->cache_cred); 45862306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(file); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci if (ret) { 46162306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_inode(fan), ret, 46262306a36Sopenharmony_ci cachefiles_trace_tmpfile_error); 46362306a36Sopenharmony_ci if (ret == -EIO) 46462306a36Sopenharmony_ci cachefiles_io_error_obj(object, "Failed to create tmpfile"); 46562306a36Sopenharmony_ci goto err; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci trace_cachefiles_tmpfile(object, file_inode(file)); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* This is a newly created file with no other possible user */ 47162306a36Sopenharmony_ci if (!cachefiles_mark_inode_in_use(object, file_inode(file))) 47262306a36Sopenharmony_ci WARN_ON(1); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = cachefiles_ondemand_init_object(object); 47562306a36Sopenharmony_ci if (ret < 0) 47662306a36Sopenharmony_ci goto err_unuse; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ni_size = object->cookie->object_size; 47962306a36Sopenharmony_ci ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (ni_size > 0) { 48262306a36Sopenharmony_ci trace_cachefiles_trunc(object, file_inode(file), 0, ni_size, 48362306a36Sopenharmony_ci cachefiles_trunc_expand_tmpfile); 48462306a36Sopenharmony_ci ret = cachefiles_inject_write_error(); 48562306a36Sopenharmony_ci if (ret == 0) 48662306a36Sopenharmony_ci ret = vfs_truncate(&file->f_path, ni_size); 48762306a36Sopenharmony_ci if (ret < 0) { 48862306a36Sopenharmony_ci trace_cachefiles_vfs_error( 48962306a36Sopenharmony_ci object, file_inode(file), ret, 49062306a36Sopenharmony_ci cachefiles_trace_trunc_error); 49162306a36Sopenharmony_ci goto err_unuse; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = -EINVAL; 49662306a36Sopenharmony_ci if (unlikely(!file->f_op->read_iter) || 49762306a36Sopenharmony_ci unlikely(!file->f_op->write_iter)) { 49862306a36Sopenharmony_ci fput(file); 49962306a36Sopenharmony_ci pr_notice("Cache does not support read_iter and write_iter\n"); 50062306a36Sopenharmony_ci goto err_unuse; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ciout: 50362306a36Sopenharmony_ci cachefiles_end_secure(cache, saved_cred); 50462306a36Sopenharmony_ci return file; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cierr_unuse: 50762306a36Sopenharmony_ci cachefiles_do_unmark_inode_in_use(object, file_inode(file)); 50862306a36Sopenharmony_ci fput(file); 50962306a36Sopenharmony_cierr: 51062306a36Sopenharmony_ci file = ERR_PTR(ret); 51162306a36Sopenharmony_ci goto out; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/* 51562306a36Sopenharmony_ci * Create a new file. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_cistatic bool cachefiles_create_file(struct cachefiles_object *object) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct file *file; 52062306a36Sopenharmony_ci int ret; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci ret = cachefiles_has_space(object->volume->cache, 1, 0, 52362306a36Sopenharmony_ci cachefiles_has_space_for_create); 52462306a36Sopenharmony_ci if (ret < 0) 52562306a36Sopenharmony_ci return false; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci file = cachefiles_create_tmpfile(object); 52862306a36Sopenharmony_ci if (IS_ERR(file)) 52962306a36Sopenharmony_ci return false; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags); 53262306a36Sopenharmony_ci set_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); 53362306a36Sopenharmony_ci _debug("create -> %pD{ino=%lu}", file, file_inode(file)->i_ino); 53462306a36Sopenharmony_ci object->file = file; 53562306a36Sopenharmony_ci return true; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/* 53962306a36Sopenharmony_ci * Open an existing file, checking its attributes and replacing it if it is 54062306a36Sopenharmony_ci * stale. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_cistatic bool cachefiles_open_file(struct cachefiles_object *object, 54362306a36Sopenharmony_ci struct dentry *dentry) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct cachefiles_cache *cache = object->volume->cache; 54662306a36Sopenharmony_ci struct file *file; 54762306a36Sopenharmony_ci struct path path; 54862306a36Sopenharmony_ci int ret; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci _enter("%pd", dentry); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!cachefiles_mark_inode_in_use(object, d_inode(dentry))) { 55362306a36Sopenharmony_ci pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n", 55462306a36Sopenharmony_ci dentry, d_inode(dentry)->i_ino); 55562306a36Sopenharmony_ci return false; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* We need to open a file interface onto a data file now as we can't do 55962306a36Sopenharmony_ci * it on demand because writeback called from do_exit() sees 56062306a36Sopenharmony_ci * current->fs == NULL - which breaks d_path() called from ext4 open. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci path.mnt = cache->mnt; 56362306a36Sopenharmony_ci path.dentry = dentry; 56462306a36Sopenharmony_ci file = kernel_file_open(&path, O_RDWR | O_LARGEFILE | O_DIRECT, 56562306a36Sopenharmony_ci d_backing_inode(dentry), cache->cache_cred); 56662306a36Sopenharmony_ci if (IS_ERR(file)) { 56762306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_backing_inode(dentry), 56862306a36Sopenharmony_ci PTR_ERR(file), 56962306a36Sopenharmony_ci cachefiles_trace_open_error); 57062306a36Sopenharmony_ci goto error; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (unlikely(!file->f_op->read_iter) || 57462306a36Sopenharmony_ci unlikely(!file->f_op->write_iter)) { 57562306a36Sopenharmony_ci pr_notice("Cache does not support read_iter and write_iter\n"); 57662306a36Sopenharmony_ci goto error_fput; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci _debug("file -> %pd positive", dentry); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = cachefiles_ondemand_init_object(object); 58162306a36Sopenharmony_ci if (ret < 0) 58262306a36Sopenharmony_ci goto error_fput; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci ret = cachefiles_check_auxdata(object, file); 58562306a36Sopenharmony_ci if (ret < 0) 58662306a36Sopenharmony_ci goto check_failed; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci clear_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &object->cookie->flags); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci object->file = file; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Always update the atime on an object we've just looked up (this is 59362306a36Sopenharmony_ci * used to keep track of culling, and atimes are only updated by read, 59462306a36Sopenharmony_ci * write and readdir but not lookup or open). 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci touch_atime(&file->f_path); 59762306a36Sopenharmony_ci dput(dentry); 59862306a36Sopenharmony_ci return true; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cicheck_failed: 60162306a36Sopenharmony_ci fscache_cookie_lookup_negative(object->cookie); 60262306a36Sopenharmony_ci cachefiles_unmark_inode_in_use(object, file); 60362306a36Sopenharmony_ci fput(file); 60462306a36Sopenharmony_ci dput(dentry); 60562306a36Sopenharmony_ci if (ret == -ESTALE) 60662306a36Sopenharmony_ci return cachefiles_create_file(object); 60762306a36Sopenharmony_ci return false; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cierror_fput: 61062306a36Sopenharmony_ci fput(file); 61162306a36Sopenharmony_cierror: 61262306a36Sopenharmony_ci cachefiles_do_unmark_inode_in_use(object, d_inode(dentry)); 61362306a36Sopenharmony_ci dput(dentry); 61462306a36Sopenharmony_ci return false; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* 61862306a36Sopenharmony_ci * walk from the parent object to the child object through the backing 61962306a36Sopenharmony_ci * filesystem, creating directories as we go 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cibool cachefiles_look_up_object(struct cachefiles_object *object) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct cachefiles_volume *volume = object->volume; 62462306a36Sopenharmony_ci struct dentry *dentry, *fan = volume->fanout[(u8)object->cookie->key_hash]; 62562306a36Sopenharmony_ci int ret; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci _enter("OBJ%x,%s,", object->debug_id, object->d_name); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* Look up path "cache/vol/fanout/file". */ 63062306a36Sopenharmony_ci ret = cachefiles_inject_read_error(); 63162306a36Sopenharmony_ci if (ret == 0) 63262306a36Sopenharmony_ci dentry = lookup_positive_unlocked(object->d_name, fan, 63362306a36Sopenharmony_ci object->d_name_len); 63462306a36Sopenharmony_ci else 63562306a36Sopenharmony_ci dentry = ERR_PTR(ret); 63662306a36Sopenharmony_ci trace_cachefiles_lookup(object, fan, dentry); 63762306a36Sopenharmony_ci if (IS_ERR(dentry)) { 63862306a36Sopenharmony_ci if (dentry == ERR_PTR(-ENOENT)) 63962306a36Sopenharmony_ci goto new_file; 64062306a36Sopenharmony_ci if (dentry == ERR_PTR(-EIO)) 64162306a36Sopenharmony_ci cachefiles_io_error_obj(object, "Lookup failed"); 64262306a36Sopenharmony_ci return false; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!d_is_reg(dentry)) { 64662306a36Sopenharmony_ci pr_err("%pd is not a file\n", dentry); 64762306a36Sopenharmony_ci inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); 64862306a36Sopenharmony_ci ret = cachefiles_bury_object(volume->cache, object, fan, dentry, 64962306a36Sopenharmony_ci FSCACHE_OBJECT_IS_WEIRD); 65062306a36Sopenharmony_ci dput(dentry); 65162306a36Sopenharmony_ci if (ret < 0) 65262306a36Sopenharmony_ci return false; 65362306a36Sopenharmony_ci goto new_file; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!cachefiles_open_file(object, dentry)) 65762306a36Sopenharmony_ci return false; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci _leave(" = t [%lu]", file_inode(object->file)->i_ino); 66062306a36Sopenharmony_ci return true; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cinew_file: 66362306a36Sopenharmony_ci fscache_cookie_lookup_negative(object->cookie); 66462306a36Sopenharmony_ci return cachefiles_create_file(object); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * Attempt to link a temporary file into its rightful place in the cache. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_cibool cachefiles_commit_tmpfile(struct cachefiles_cache *cache, 67162306a36Sopenharmony_ci struct cachefiles_object *object) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct cachefiles_volume *volume = object->volume; 67462306a36Sopenharmony_ci struct dentry *dentry, *fan = volume->fanout[(u8)object->cookie->key_hash]; 67562306a36Sopenharmony_ci bool success = false; 67662306a36Sopenharmony_ci int ret; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci _enter(",%pD", object->file); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); 68162306a36Sopenharmony_ci ret = cachefiles_inject_read_error(); 68262306a36Sopenharmony_ci if (ret == 0) 68362306a36Sopenharmony_ci dentry = lookup_one_len(object->d_name, fan, object->d_name_len); 68462306a36Sopenharmony_ci else 68562306a36Sopenharmony_ci dentry = ERR_PTR(ret); 68662306a36Sopenharmony_ci if (IS_ERR(dentry)) { 68762306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry), 68862306a36Sopenharmony_ci cachefiles_trace_lookup_error); 68962306a36Sopenharmony_ci _debug("lookup fail %ld", PTR_ERR(dentry)); 69062306a36Sopenharmony_ci goto out_unlock; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!d_is_negative(dentry)) { 69462306a36Sopenharmony_ci if (d_backing_inode(dentry) == file_inode(object->file)) { 69562306a36Sopenharmony_ci success = true; 69662306a36Sopenharmony_ci goto out_dput; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ret = cachefiles_unlink(volume->cache, object, fan, dentry, 70062306a36Sopenharmony_ci FSCACHE_OBJECT_IS_STALE); 70162306a36Sopenharmony_ci if (ret < 0) 70262306a36Sopenharmony_ci goto out_dput; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci dput(dentry); 70562306a36Sopenharmony_ci ret = cachefiles_inject_read_error(); 70662306a36Sopenharmony_ci if (ret == 0) 70762306a36Sopenharmony_ci dentry = lookup_one_len(object->d_name, fan, object->d_name_len); 70862306a36Sopenharmony_ci else 70962306a36Sopenharmony_ci dentry = ERR_PTR(ret); 71062306a36Sopenharmony_ci if (IS_ERR(dentry)) { 71162306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry), 71262306a36Sopenharmony_ci cachefiles_trace_lookup_error); 71362306a36Sopenharmony_ci _debug("lookup fail %ld", PTR_ERR(dentry)); 71462306a36Sopenharmony_ci goto out_unlock; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ret = cachefiles_inject_read_error(); 71962306a36Sopenharmony_ci if (ret == 0) 72062306a36Sopenharmony_ci ret = vfs_link(object->file->f_path.dentry, &nop_mnt_idmap, 72162306a36Sopenharmony_ci d_inode(fan), dentry, NULL); 72262306a36Sopenharmony_ci if (ret < 0) { 72362306a36Sopenharmony_ci trace_cachefiles_vfs_error(object, d_inode(fan), ret, 72462306a36Sopenharmony_ci cachefiles_trace_link_error); 72562306a36Sopenharmony_ci _debug("link fail %d", ret); 72662306a36Sopenharmony_ci } else { 72762306a36Sopenharmony_ci trace_cachefiles_link(object, file_inode(object->file)); 72862306a36Sopenharmony_ci spin_lock(&object->lock); 72962306a36Sopenharmony_ci /* TODO: Do we want to switch the file pointer to the new dentry? */ 73062306a36Sopenharmony_ci clear_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); 73162306a36Sopenharmony_ci spin_unlock(&object->lock); 73262306a36Sopenharmony_ci success = true; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciout_dput: 73662306a36Sopenharmony_ci dput(dentry); 73762306a36Sopenharmony_ciout_unlock: 73862306a36Sopenharmony_ci inode_unlock(d_inode(fan)); 73962306a36Sopenharmony_ci _leave(" = %u", success); 74062306a36Sopenharmony_ci return success; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci/* 74462306a36Sopenharmony_ci * Look up an inode to be checked or culled. Return -EBUSY if the inode is 74562306a36Sopenharmony_ci * marked in use. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_cistatic struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *cache, 74862306a36Sopenharmony_ci struct dentry *dir, 74962306a36Sopenharmony_ci char *filename) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct dentry *victim; 75262306a36Sopenharmony_ci int ret = -ENOENT; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci victim = lookup_one_len(filename, dir, strlen(filename)); 75762306a36Sopenharmony_ci if (IS_ERR(victim)) 75862306a36Sopenharmony_ci goto lookup_error; 75962306a36Sopenharmony_ci if (d_is_negative(victim)) 76062306a36Sopenharmony_ci goto lookup_put; 76162306a36Sopenharmony_ci if (d_inode(victim)->i_flags & S_KERNEL_FILE) 76262306a36Sopenharmony_ci goto lookup_busy; 76362306a36Sopenharmony_ci return victim; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cilookup_busy: 76662306a36Sopenharmony_ci ret = -EBUSY; 76762306a36Sopenharmony_cilookup_put: 76862306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 76962306a36Sopenharmony_ci dput(victim); 77062306a36Sopenharmony_ci return ERR_PTR(ret); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cilookup_error: 77362306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 77462306a36Sopenharmony_ci ret = PTR_ERR(victim); 77562306a36Sopenharmony_ci if (ret == -ENOENT) 77662306a36Sopenharmony_ci return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */ 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (ret == -EIO) { 77962306a36Sopenharmony_ci cachefiles_io_error(cache, "Lookup failed"); 78062306a36Sopenharmony_ci } else if (ret != -ENOMEM) { 78162306a36Sopenharmony_ci pr_err("Internal error: %d\n", ret); 78262306a36Sopenharmony_ci ret = -EIO; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return ERR_PTR(ret); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/* 78962306a36Sopenharmony_ci * Cull an object if it's not in use 79062306a36Sopenharmony_ci * - called only by cache manager daemon 79162306a36Sopenharmony_ci */ 79262306a36Sopenharmony_ciint cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, 79362306a36Sopenharmony_ci char *filename) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct dentry *victim; 79662306a36Sopenharmony_ci struct inode *inode; 79762306a36Sopenharmony_ci int ret; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci _enter(",%pd/,%s", dir, filename); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci victim = cachefiles_lookup_for_cull(cache, dir, filename); 80262306a36Sopenharmony_ci if (IS_ERR(victim)) 80362306a36Sopenharmony_ci return PTR_ERR(victim); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* check to see if someone is using this object */ 80662306a36Sopenharmony_ci inode = d_inode(victim); 80762306a36Sopenharmony_ci inode_lock(inode); 80862306a36Sopenharmony_ci if (inode->i_flags & S_KERNEL_FILE) { 80962306a36Sopenharmony_ci ret = -EBUSY; 81062306a36Sopenharmony_ci } else { 81162306a36Sopenharmony_ci /* Stop the cache from picking it back up */ 81262306a36Sopenharmony_ci inode->i_flags |= S_KERNEL_FILE; 81362306a36Sopenharmony_ci ret = 0; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci inode_unlock(inode); 81662306a36Sopenharmony_ci if (ret < 0) 81762306a36Sopenharmony_ci goto error_unlock; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci ret = cachefiles_bury_object(cache, NULL, dir, victim, 82062306a36Sopenharmony_ci FSCACHE_OBJECT_WAS_CULLED); 82162306a36Sopenharmony_ci if (ret < 0) 82262306a36Sopenharmony_ci goto error; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci fscache_count_culled(); 82562306a36Sopenharmony_ci dput(victim); 82662306a36Sopenharmony_ci _leave(" = 0"); 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cierror_unlock: 83062306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 83162306a36Sopenharmony_cierror: 83262306a36Sopenharmony_ci dput(victim); 83362306a36Sopenharmony_ci if (ret == -ENOENT) 83462306a36Sopenharmony_ci return -ESTALE; /* Probably got retired by the netfs */ 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (ret != -ENOMEM) { 83762306a36Sopenharmony_ci pr_err("Internal error: %d\n", ret); 83862306a36Sopenharmony_ci ret = -EIO; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci _leave(" = %d", ret); 84262306a36Sopenharmony_ci return ret; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/* 84662306a36Sopenharmony_ci * Find out if an object is in use or not 84762306a36Sopenharmony_ci * - called only by cache manager daemon 84862306a36Sopenharmony_ci * - returns -EBUSY or 0 to indicate whether an object is in use or not 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_ciint cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry *dir, 85162306a36Sopenharmony_ci char *filename) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct dentry *victim; 85462306a36Sopenharmony_ci int ret = 0; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci victim = cachefiles_lookup_for_cull(cache, dir, filename); 85762306a36Sopenharmony_ci if (IS_ERR(victim)) 85862306a36Sopenharmony_ci return PTR_ERR(victim); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 86162306a36Sopenharmony_ci dput(victim); 86262306a36Sopenharmony_ci return ret; 86362306a36Sopenharmony_ci} 864