11bd9c4e4SDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later 21bd9c4e4SDavid Howells /* CacheFiles path walking and related routines 31bd9c4e4SDavid Howells * 41bd9c4e4SDavid Howells * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. 51bd9c4e4SDavid Howells * Written by David Howells (dhowells@redhat.com) 61bd9c4e4SDavid Howells */ 71bd9c4e4SDavid Howells 81bd9c4e4SDavid Howells #include <linux/fs.h> 932759f7dSDavid Howells #include <linux/namei.h> 101bd9c4e4SDavid Howells #include "internal.h" 111bd9c4e4SDavid Howells 121bd9c4e4SDavid Howells /* 131bd9c4e4SDavid Howells * Mark the backing file as being a cache file if it's not already in use. The 141bd9c4e4SDavid Howells * mark tells the culling request command that it's not allowed to cull the 151bd9c4e4SDavid Howells * file or directory. The caller must hold the inode lock. 161bd9c4e4SDavid Howells */ 171bd9c4e4SDavid Howells static bool __cachefiles_mark_inode_in_use(struct cachefiles_object *object, 181bd9c4e4SDavid Howells struct dentry *dentry) 191bd9c4e4SDavid Howells { 201bd9c4e4SDavid Howells struct inode *inode = d_backing_inode(dentry); 211bd9c4e4SDavid Howells bool can_use = false; 221bd9c4e4SDavid Howells 231bd9c4e4SDavid Howells if (!(inode->i_flags & S_KERNEL_FILE)) { 241bd9c4e4SDavid Howells inode->i_flags |= S_KERNEL_FILE; 251bd9c4e4SDavid Howells trace_cachefiles_mark_active(object, inode); 261bd9c4e4SDavid Howells can_use = true; 271bd9c4e4SDavid Howells } else { 281bd9c4e4SDavid Howells pr_notice("cachefiles: Inode already in use: %pd\n", dentry); 291bd9c4e4SDavid Howells } 301bd9c4e4SDavid Howells 311bd9c4e4SDavid Howells return can_use; 321bd9c4e4SDavid Howells } 331bd9c4e4SDavid Howells 34169379eaSDavid Howells static bool cachefiles_mark_inode_in_use(struct cachefiles_object *object, 35169379eaSDavid Howells struct dentry *dentry) 36169379eaSDavid Howells { 37169379eaSDavid Howells struct inode *inode = d_backing_inode(dentry); 38169379eaSDavid Howells bool can_use; 39169379eaSDavid Howells 40169379eaSDavid Howells inode_lock(inode); 41169379eaSDavid Howells can_use = __cachefiles_mark_inode_in_use(object, dentry); 42169379eaSDavid Howells inode_unlock(inode); 43169379eaSDavid Howells return can_use; 44169379eaSDavid Howells } 45169379eaSDavid Howells 461bd9c4e4SDavid Howells /* 471bd9c4e4SDavid Howells * Unmark a backing inode. The caller must hold the inode lock. 481bd9c4e4SDavid Howells */ 491bd9c4e4SDavid Howells static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object, 501bd9c4e4SDavid Howells struct dentry *dentry) 511bd9c4e4SDavid Howells { 521bd9c4e4SDavid Howells struct inode *inode = d_backing_inode(dentry); 531bd9c4e4SDavid Howells 541bd9c4e4SDavid Howells inode->i_flags &= ~S_KERNEL_FILE; 551bd9c4e4SDavid Howells trace_cachefiles_mark_inactive(object, inode); 561bd9c4e4SDavid Howells } 5732759f7dSDavid Howells 5832759f7dSDavid Howells /* 59169379eaSDavid Howells * Unmark a backing inode and tell cachefilesd that there's something that can 60169379eaSDavid Howells * be culled. 61169379eaSDavid Howells */ 62169379eaSDavid Howells void cachefiles_unmark_inode_in_use(struct cachefiles_object *object, 63169379eaSDavid Howells struct file *file) 64169379eaSDavid Howells { 65169379eaSDavid Howells struct cachefiles_cache *cache = object->volume->cache; 66169379eaSDavid Howells struct inode *inode = file_inode(file); 67169379eaSDavid Howells 68169379eaSDavid Howells if (inode) { 69169379eaSDavid Howells inode_lock(inode); 70169379eaSDavid Howells __cachefiles_unmark_inode_in_use(object, file->f_path.dentry); 71169379eaSDavid Howells inode_unlock(inode); 72169379eaSDavid Howells 73169379eaSDavid Howells if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { 74169379eaSDavid Howells atomic_long_add(inode->i_blocks, &cache->b_released); 75169379eaSDavid Howells if (atomic_inc_return(&cache->f_released)) 76169379eaSDavid Howells cachefiles_state_changed(cache); 77169379eaSDavid Howells } 78169379eaSDavid Howells } 79169379eaSDavid Howells } 80169379eaSDavid Howells 81169379eaSDavid Howells /* 8232759f7dSDavid Howells * get a subdirectory 8332759f7dSDavid Howells */ 8432759f7dSDavid Howells struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, 8532759f7dSDavid Howells struct dentry *dir, 8632759f7dSDavid Howells const char *dirname, 8732759f7dSDavid Howells bool *_is_new) 8832759f7dSDavid Howells { 8932759f7dSDavid Howells struct dentry *subdir; 9032759f7dSDavid Howells struct path path; 9132759f7dSDavid Howells int ret; 9232759f7dSDavid Howells 9332759f7dSDavid Howells _enter(",,%s", dirname); 9432759f7dSDavid Howells 9532759f7dSDavid Howells /* search the current directory for the element name */ 9632759f7dSDavid Howells inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); 9732759f7dSDavid Howells 9832759f7dSDavid Howells retry: 9932759f7dSDavid Howells ret = cachefiles_inject_read_error(); 10032759f7dSDavid Howells if (ret == 0) 10132759f7dSDavid Howells subdir = lookup_one_len(dirname, dir, strlen(dirname)); 10232759f7dSDavid Howells else 10332759f7dSDavid Howells subdir = ERR_PTR(ret); 10432759f7dSDavid Howells if (IS_ERR(subdir)) { 10532759f7dSDavid Howells trace_cachefiles_vfs_error(NULL, d_backing_inode(dir), 10632759f7dSDavid Howells PTR_ERR(subdir), 10732759f7dSDavid Howells cachefiles_trace_lookup_error); 10832759f7dSDavid Howells if (PTR_ERR(subdir) == -ENOMEM) 10932759f7dSDavid Howells goto nomem_d_alloc; 11032759f7dSDavid Howells goto lookup_error; 11132759f7dSDavid Howells } 11232759f7dSDavid Howells 11332759f7dSDavid Howells _debug("subdir -> %pd %s", 11432759f7dSDavid Howells subdir, d_backing_inode(subdir) ? "positive" : "negative"); 11532759f7dSDavid Howells 11632759f7dSDavid Howells /* we need to create the subdir if it doesn't exist yet */ 11732759f7dSDavid Howells if (d_is_negative(subdir)) { 11832759f7dSDavid Howells ret = cachefiles_has_space(cache, 1, 0); 11932759f7dSDavid Howells if (ret < 0) 12032759f7dSDavid Howells goto mkdir_error; 12132759f7dSDavid Howells 12232759f7dSDavid Howells _debug("attempt mkdir"); 12332759f7dSDavid Howells 12432759f7dSDavid Howells path.mnt = cache->mnt; 12532759f7dSDavid Howells path.dentry = dir; 12632759f7dSDavid Howells ret = security_path_mkdir(&path, subdir, 0700); 12732759f7dSDavid Howells if (ret < 0) 12832759f7dSDavid Howells goto mkdir_error; 12932759f7dSDavid Howells ret = cachefiles_inject_write_error(); 13032759f7dSDavid Howells if (ret == 0) 13132759f7dSDavid Howells ret = vfs_mkdir(&init_user_ns, d_inode(dir), subdir, 0700); 13232759f7dSDavid Howells if (ret < 0) { 13332759f7dSDavid Howells trace_cachefiles_vfs_error(NULL, d_inode(dir), ret, 13432759f7dSDavid Howells cachefiles_trace_mkdir_error); 13532759f7dSDavid Howells goto mkdir_error; 13632759f7dSDavid Howells } 13732759f7dSDavid Howells 13832759f7dSDavid Howells if (unlikely(d_unhashed(subdir))) { 13932759f7dSDavid Howells cachefiles_put_directory(subdir); 14032759f7dSDavid Howells goto retry; 14132759f7dSDavid Howells } 14232759f7dSDavid Howells ASSERT(d_backing_inode(subdir)); 14332759f7dSDavid Howells 14432759f7dSDavid Howells _debug("mkdir -> %pd{ino=%lu}", 14532759f7dSDavid Howells subdir, d_backing_inode(subdir)->i_ino); 14632759f7dSDavid Howells if (_is_new) 14732759f7dSDavid Howells *_is_new = true; 14832759f7dSDavid Howells } 14932759f7dSDavid Howells 15032759f7dSDavid Howells /* Tell rmdir() it's not allowed to delete the subdir */ 15132759f7dSDavid Howells inode_lock(d_inode(subdir)); 15232759f7dSDavid Howells inode_unlock(d_inode(dir)); 15332759f7dSDavid Howells 15432759f7dSDavid Howells if (!__cachefiles_mark_inode_in_use(NULL, subdir)) 15532759f7dSDavid Howells goto mark_error; 15632759f7dSDavid Howells 15732759f7dSDavid Howells inode_unlock(d_inode(subdir)); 15832759f7dSDavid Howells 15932759f7dSDavid Howells /* we need to make sure the subdir is a directory */ 16032759f7dSDavid Howells ASSERT(d_backing_inode(subdir)); 16132759f7dSDavid Howells 16232759f7dSDavid Howells if (!d_can_lookup(subdir)) { 16332759f7dSDavid Howells pr_err("%s is not a directory\n", dirname); 16432759f7dSDavid Howells ret = -EIO; 16532759f7dSDavid Howells goto check_error; 16632759f7dSDavid Howells } 16732759f7dSDavid Howells 16832759f7dSDavid Howells ret = -EPERM; 16932759f7dSDavid Howells if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) || 17032759f7dSDavid Howells !d_backing_inode(subdir)->i_op->lookup || 17132759f7dSDavid Howells !d_backing_inode(subdir)->i_op->mkdir || 17232759f7dSDavid Howells !d_backing_inode(subdir)->i_op->rename || 17332759f7dSDavid Howells !d_backing_inode(subdir)->i_op->rmdir || 17432759f7dSDavid Howells !d_backing_inode(subdir)->i_op->unlink) 17532759f7dSDavid Howells goto check_error; 17632759f7dSDavid Howells 17732759f7dSDavid Howells _leave(" = [%lu]", d_backing_inode(subdir)->i_ino); 17832759f7dSDavid Howells return subdir; 17932759f7dSDavid Howells 18032759f7dSDavid Howells check_error: 18132759f7dSDavid Howells cachefiles_put_directory(subdir); 18232759f7dSDavid Howells _leave(" = %d [check]", ret); 18332759f7dSDavid Howells return ERR_PTR(ret); 18432759f7dSDavid Howells 18532759f7dSDavid Howells mark_error: 18632759f7dSDavid Howells inode_unlock(d_inode(subdir)); 18732759f7dSDavid Howells dput(subdir); 18832759f7dSDavid Howells return ERR_PTR(-EBUSY); 18932759f7dSDavid Howells 19032759f7dSDavid Howells mkdir_error: 19132759f7dSDavid Howells inode_unlock(d_inode(dir)); 19232759f7dSDavid Howells dput(subdir); 19332759f7dSDavid Howells pr_err("mkdir %s failed with error %d\n", dirname, ret); 19432759f7dSDavid Howells return ERR_PTR(ret); 19532759f7dSDavid Howells 19632759f7dSDavid Howells lookup_error: 19732759f7dSDavid Howells inode_unlock(d_inode(dir)); 19832759f7dSDavid Howells ret = PTR_ERR(subdir); 19932759f7dSDavid Howells pr_err("Lookup %s failed with error %d\n", dirname, ret); 20032759f7dSDavid Howells return ERR_PTR(ret); 20132759f7dSDavid Howells 20232759f7dSDavid Howells nomem_d_alloc: 20332759f7dSDavid Howells inode_unlock(d_inode(dir)); 20432759f7dSDavid Howells _leave(" = -ENOMEM"); 20532759f7dSDavid Howells return ERR_PTR(-ENOMEM); 20632759f7dSDavid Howells } 20732759f7dSDavid Howells 20832759f7dSDavid Howells /* 20932759f7dSDavid Howells * Put a subdirectory. 21032759f7dSDavid Howells */ 21132759f7dSDavid Howells void cachefiles_put_directory(struct dentry *dir) 21232759f7dSDavid Howells { 21332759f7dSDavid Howells if (dir) { 21432759f7dSDavid Howells inode_lock(dir->d_inode); 21532759f7dSDavid Howells __cachefiles_unmark_inode_in_use(NULL, dir); 21632759f7dSDavid Howells inode_unlock(dir->d_inode); 21732759f7dSDavid Howells dput(dir); 21832759f7dSDavid Howells } 21932759f7dSDavid Howells } 220*07a90e97SDavid Howells 221*07a90e97SDavid Howells /* 222*07a90e97SDavid Howells * Remove a regular file from the cache. 223*07a90e97SDavid Howells */ 224*07a90e97SDavid Howells static int cachefiles_unlink(struct cachefiles_cache *cache, 225*07a90e97SDavid Howells struct cachefiles_object *object, 226*07a90e97SDavid Howells struct dentry *dir, struct dentry *dentry, 227*07a90e97SDavid Howells enum fscache_why_object_killed why) 228*07a90e97SDavid Howells { 229*07a90e97SDavid Howells struct path path = { 230*07a90e97SDavid Howells .mnt = cache->mnt, 231*07a90e97SDavid Howells .dentry = dir, 232*07a90e97SDavid Howells }; 233*07a90e97SDavid Howells int ret; 234*07a90e97SDavid Howells 235*07a90e97SDavid Howells trace_cachefiles_unlink(object, dentry, why); 236*07a90e97SDavid Howells ret = security_path_unlink(&path, dentry); 237*07a90e97SDavid Howells if (ret < 0) { 238*07a90e97SDavid Howells cachefiles_io_error(cache, "Unlink security error"); 239*07a90e97SDavid Howells return ret; 240*07a90e97SDavid Howells } 241*07a90e97SDavid Howells 242*07a90e97SDavid Howells ret = cachefiles_inject_remove_error(); 243*07a90e97SDavid Howells if (ret == 0) { 244*07a90e97SDavid Howells ret = vfs_unlink(&init_user_ns, d_backing_inode(dir), dentry, NULL); 245*07a90e97SDavid Howells if (ret == -EIO) 246*07a90e97SDavid Howells cachefiles_io_error(cache, "Unlink failed"); 247*07a90e97SDavid Howells } 248*07a90e97SDavid Howells if (ret != 0) 249*07a90e97SDavid Howells trace_cachefiles_vfs_error(object, d_backing_inode(dir), ret, 250*07a90e97SDavid Howells cachefiles_trace_unlink_error); 251*07a90e97SDavid Howells return ret; 252*07a90e97SDavid Howells } 253*07a90e97SDavid Howells 254*07a90e97SDavid Howells /* 255*07a90e97SDavid Howells * Delete an object representation from the cache 256*07a90e97SDavid Howells * - File backed objects are unlinked 257*07a90e97SDavid Howells * - Directory backed objects are stuffed into the graveyard for userspace to 258*07a90e97SDavid Howells * delete 259*07a90e97SDavid Howells */ 260*07a90e97SDavid Howells int cachefiles_bury_object(struct cachefiles_cache *cache, 261*07a90e97SDavid Howells struct cachefiles_object *object, 262*07a90e97SDavid Howells struct dentry *dir, 263*07a90e97SDavid Howells struct dentry *rep, 264*07a90e97SDavid Howells enum fscache_why_object_killed why) 265*07a90e97SDavid Howells { 266*07a90e97SDavid Howells struct dentry *grave, *trap; 267*07a90e97SDavid Howells struct path path, path_to_graveyard; 268*07a90e97SDavid Howells char nbuffer[8 + 8 + 1]; 269*07a90e97SDavid Howells int ret; 270*07a90e97SDavid Howells 271*07a90e97SDavid Howells _enter(",'%pd','%pd'", dir, rep); 272*07a90e97SDavid Howells 273*07a90e97SDavid Howells if (rep->d_parent != dir) { 274*07a90e97SDavid Howells inode_unlock(d_inode(dir)); 275*07a90e97SDavid Howells _leave(" = -ESTALE"); 276*07a90e97SDavid Howells return -ESTALE; 277*07a90e97SDavid Howells } 278*07a90e97SDavid Howells 279*07a90e97SDavid Howells /* non-directories can just be unlinked */ 280*07a90e97SDavid Howells if (!d_is_dir(rep)) { 281*07a90e97SDavid Howells dget(rep); /* Stop the dentry being negated if it's only pinned 282*07a90e97SDavid Howells * by a file struct. 283*07a90e97SDavid Howells */ 284*07a90e97SDavid Howells ret = cachefiles_unlink(cache, object, dir, rep, why); 285*07a90e97SDavid Howells dput(rep); 286*07a90e97SDavid Howells 287*07a90e97SDavid Howells inode_unlock(d_inode(dir)); 288*07a90e97SDavid Howells _leave(" = %d", ret); 289*07a90e97SDavid Howells return ret; 290*07a90e97SDavid Howells } 291*07a90e97SDavid Howells 292*07a90e97SDavid Howells /* directories have to be moved to the graveyard */ 293*07a90e97SDavid Howells _debug("move stale object to graveyard"); 294*07a90e97SDavid Howells inode_unlock(d_inode(dir)); 295*07a90e97SDavid Howells 296*07a90e97SDavid Howells try_again: 297*07a90e97SDavid Howells /* first step is to make up a grave dentry in the graveyard */ 298*07a90e97SDavid Howells sprintf(nbuffer, "%08x%08x", 299*07a90e97SDavid Howells (uint32_t) ktime_get_real_seconds(), 300*07a90e97SDavid Howells (uint32_t) atomic_inc_return(&cache->gravecounter)); 301*07a90e97SDavid Howells 302*07a90e97SDavid Howells /* do the multiway lock magic */ 303*07a90e97SDavid Howells trap = lock_rename(cache->graveyard, dir); 304*07a90e97SDavid Howells 305*07a90e97SDavid Howells /* do some checks before getting the grave dentry */ 306*07a90e97SDavid Howells if (rep->d_parent != dir || IS_DEADDIR(d_inode(rep))) { 307*07a90e97SDavid Howells /* the entry was probably culled when we dropped the parent dir 308*07a90e97SDavid Howells * lock */ 309*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 310*07a90e97SDavid Howells _leave(" = 0 [culled?]"); 311*07a90e97SDavid Howells return 0; 312*07a90e97SDavid Howells } 313*07a90e97SDavid Howells 314*07a90e97SDavid Howells if (!d_can_lookup(cache->graveyard)) { 315*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 316*07a90e97SDavid Howells cachefiles_io_error(cache, "Graveyard no longer a directory"); 317*07a90e97SDavid Howells return -EIO; 318*07a90e97SDavid Howells } 319*07a90e97SDavid Howells 320*07a90e97SDavid Howells if (trap == rep) { 321*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 322*07a90e97SDavid Howells cachefiles_io_error(cache, "May not make directory loop"); 323*07a90e97SDavid Howells return -EIO; 324*07a90e97SDavid Howells } 325*07a90e97SDavid Howells 326*07a90e97SDavid Howells if (d_mountpoint(rep)) { 327*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 328*07a90e97SDavid Howells cachefiles_io_error(cache, "Mountpoint in cache"); 329*07a90e97SDavid Howells return -EIO; 330*07a90e97SDavid Howells } 331*07a90e97SDavid Howells 332*07a90e97SDavid Howells grave = lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer)); 333*07a90e97SDavid Howells if (IS_ERR(grave)) { 334*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 335*07a90e97SDavid Howells trace_cachefiles_vfs_error(object, d_inode(cache->graveyard), 336*07a90e97SDavid Howells PTR_ERR(grave), 337*07a90e97SDavid Howells cachefiles_trace_lookup_error); 338*07a90e97SDavid Howells 339*07a90e97SDavid Howells if (PTR_ERR(grave) == -ENOMEM) { 340*07a90e97SDavid Howells _leave(" = -ENOMEM"); 341*07a90e97SDavid Howells return -ENOMEM; 342*07a90e97SDavid Howells } 343*07a90e97SDavid Howells 344*07a90e97SDavid Howells cachefiles_io_error(cache, "Lookup error %ld", PTR_ERR(grave)); 345*07a90e97SDavid Howells return -EIO; 346*07a90e97SDavid Howells } 347*07a90e97SDavid Howells 348*07a90e97SDavid Howells if (d_is_positive(grave)) { 349*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 350*07a90e97SDavid Howells dput(grave); 351*07a90e97SDavid Howells grave = NULL; 352*07a90e97SDavid Howells cond_resched(); 353*07a90e97SDavid Howells goto try_again; 354*07a90e97SDavid Howells } 355*07a90e97SDavid Howells 356*07a90e97SDavid Howells if (d_mountpoint(grave)) { 357*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 358*07a90e97SDavid Howells dput(grave); 359*07a90e97SDavid Howells cachefiles_io_error(cache, "Mountpoint in graveyard"); 360*07a90e97SDavid Howells return -EIO; 361*07a90e97SDavid Howells } 362*07a90e97SDavid Howells 363*07a90e97SDavid Howells /* target should not be an ancestor of source */ 364*07a90e97SDavid Howells if (trap == grave) { 365*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 366*07a90e97SDavid Howells dput(grave); 367*07a90e97SDavid Howells cachefiles_io_error(cache, "May not make directory loop"); 368*07a90e97SDavid Howells return -EIO; 369*07a90e97SDavid Howells } 370*07a90e97SDavid Howells 371*07a90e97SDavid Howells /* attempt the rename */ 372*07a90e97SDavid Howells path.mnt = cache->mnt; 373*07a90e97SDavid Howells path.dentry = dir; 374*07a90e97SDavid Howells path_to_graveyard.mnt = cache->mnt; 375*07a90e97SDavid Howells path_to_graveyard.dentry = cache->graveyard; 376*07a90e97SDavid Howells ret = security_path_rename(&path, rep, &path_to_graveyard, grave, 0); 377*07a90e97SDavid Howells if (ret < 0) { 378*07a90e97SDavid Howells cachefiles_io_error(cache, "Rename security error %d", ret); 379*07a90e97SDavid Howells } else { 380*07a90e97SDavid Howells struct renamedata rd = { 381*07a90e97SDavid Howells .old_mnt_userns = &init_user_ns, 382*07a90e97SDavid Howells .old_dir = d_inode(dir), 383*07a90e97SDavid Howells .old_dentry = rep, 384*07a90e97SDavid Howells .new_mnt_userns = &init_user_ns, 385*07a90e97SDavid Howells .new_dir = d_inode(cache->graveyard), 386*07a90e97SDavid Howells .new_dentry = grave, 387*07a90e97SDavid Howells }; 388*07a90e97SDavid Howells trace_cachefiles_rename(object, rep, grave, why); 389*07a90e97SDavid Howells ret = cachefiles_inject_read_error(); 390*07a90e97SDavid Howells if (ret == 0) 391*07a90e97SDavid Howells ret = vfs_rename(&rd); 392*07a90e97SDavid Howells if (ret != 0) 393*07a90e97SDavid Howells trace_cachefiles_vfs_error(object, d_inode(dir), ret, 394*07a90e97SDavid Howells cachefiles_trace_rename_error); 395*07a90e97SDavid Howells if (ret != 0 && ret != -ENOMEM) 396*07a90e97SDavid Howells cachefiles_io_error(cache, 397*07a90e97SDavid Howells "Rename failed with error %d", ret); 398*07a90e97SDavid Howells } 399*07a90e97SDavid Howells 400*07a90e97SDavid Howells __cachefiles_unmark_inode_in_use(object, rep); 401*07a90e97SDavid Howells unlock_rename(cache->graveyard, dir); 402*07a90e97SDavid Howells dput(grave); 403*07a90e97SDavid Howells _leave(" = 0"); 404*07a90e97SDavid Howells return 0; 405*07a90e97SDavid Howells } 406*07a90e97SDavid Howells 407*07a90e97SDavid Howells /* 408*07a90e97SDavid Howells * Look up an inode to be checked or culled. Return -EBUSY if the inode is 409*07a90e97SDavid Howells * marked in use. 410*07a90e97SDavid Howells */ 411*07a90e97SDavid Howells static struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *cache, 412*07a90e97SDavid Howells struct dentry *dir, 413*07a90e97SDavid Howells char *filename) 414*07a90e97SDavid Howells { 415*07a90e97SDavid Howells struct dentry *victim; 416*07a90e97SDavid Howells int ret = -ENOENT; 417*07a90e97SDavid Howells 418*07a90e97SDavid Howells inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); 419*07a90e97SDavid Howells 420*07a90e97SDavid Howells victim = lookup_one_len(filename, dir, strlen(filename)); 421*07a90e97SDavid Howells if (IS_ERR(victim)) 422*07a90e97SDavid Howells goto lookup_error; 423*07a90e97SDavid Howells if (d_is_negative(victim)) 424*07a90e97SDavid Howells goto lookup_put; 425*07a90e97SDavid Howells if (d_inode(victim)->i_flags & S_KERNEL_FILE) 426*07a90e97SDavid Howells goto lookup_busy; 427*07a90e97SDavid Howells return victim; 428*07a90e97SDavid Howells 429*07a90e97SDavid Howells lookup_busy: 430*07a90e97SDavid Howells ret = -EBUSY; 431*07a90e97SDavid Howells lookup_put: 432*07a90e97SDavid Howells inode_unlock(d_inode(dir)); 433*07a90e97SDavid Howells dput(victim); 434*07a90e97SDavid Howells return ERR_PTR(ret); 435*07a90e97SDavid Howells 436*07a90e97SDavid Howells lookup_error: 437*07a90e97SDavid Howells inode_unlock(d_inode(dir)); 438*07a90e97SDavid Howells ret = PTR_ERR(victim); 439*07a90e97SDavid Howells if (ret == -ENOENT) 440*07a90e97SDavid Howells return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */ 441*07a90e97SDavid Howells 442*07a90e97SDavid Howells if (ret == -EIO) { 443*07a90e97SDavid Howells cachefiles_io_error(cache, "Lookup failed"); 444*07a90e97SDavid Howells } else if (ret != -ENOMEM) { 445*07a90e97SDavid Howells pr_err("Internal error: %d\n", ret); 446*07a90e97SDavid Howells ret = -EIO; 447*07a90e97SDavid Howells } 448*07a90e97SDavid Howells 449*07a90e97SDavid Howells return ERR_PTR(ret); 450*07a90e97SDavid Howells } 451*07a90e97SDavid Howells 452*07a90e97SDavid Howells /* 453*07a90e97SDavid Howells * Cull an object if it's not in use 454*07a90e97SDavid Howells * - called only by cache manager daemon 455*07a90e97SDavid Howells */ 456*07a90e97SDavid Howells int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, 457*07a90e97SDavid Howells char *filename) 458*07a90e97SDavid Howells { 459*07a90e97SDavid Howells struct dentry *victim; 460*07a90e97SDavid Howells struct inode *inode; 461*07a90e97SDavid Howells int ret; 462*07a90e97SDavid Howells 463*07a90e97SDavid Howells _enter(",%pd/,%s", dir, filename); 464*07a90e97SDavid Howells 465*07a90e97SDavid Howells victim = cachefiles_lookup_for_cull(cache, dir, filename); 466*07a90e97SDavid Howells if (IS_ERR(victim)) 467*07a90e97SDavid Howells return PTR_ERR(victim); 468*07a90e97SDavid Howells 469*07a90e97SDavid Howells /* check to see if someone is using this object */ 470*07a90e97SDavid Howells inode = d_inode(victim); 471*07a90e97SDavid Howells inode_lock(inode); 472*07a90e97SDavid Howells if (inode->i_flags & S_KERNEL_FILE) { 473*07a90e97SDavid Howells ret = -EBUSY; 474*07a90e97SDavid Howells } else { 475*07a90e97SDavid Howells /* Stop the cache from picking it back up */ 476*07a90e97SDavid Howells inode->i_flags |= S_KERNEL_FILE; 477*07a90e97SDavid Howells ret = 0; 478*07a90e97SDavid Howells } 479*07a90e97SDavid Howells inode_unlock(inode); 480*07a90e97SDavid Howells if (ret < 0) 481*07a90e97SDavid Howells goto error_unlock; 482*07a90e97SDavid Howells 483*07a90e97SDavid Howells ret = cachefiles_bury_object(cache, NULL, dir, victim, 484*07a90e97SDavid Howells FSCACHE_OBJECT_WAS_CULLED); 485*07a90e97SDavid Howells if (ret < 0) 486*07a90e97SDavid Howells goto error; 487*07a90e97SDavid Howells 488*07a90e97SDavid Howells dput(victim); 489*07a90e97SDavid Howells _leave(" = 0"); 490*07a90e97SDavid Howells return 0; 491*07a90e97SDavid Howells 492*07a90e97SDavid Howells error_unlock: 493*07a90e97SDavid Howells inode_unlock(d_inode(dir)); 494*07a90e97SDavid Howells error: 495*07a90e97SDavid Howells dput(victim); 496*07a90e97SDavid Howells if (ret == -ENOENT) 497*07a90e97SDavid Howells return -ESTALE; /* Probably got retired by the netfs */ 498*07a90e97SDavid Howells 499*07a90e97SDavid Howells if (ret != -ENOMEM) { 500*07a90e97SDavid Howells pr_err("Internal error: %d\n", ret); 501*07a90e97SDavid Howells ret = -EIO; 502*07a90e97SDavid Howells } 503*07a90e97SDavid Howells 504*07a90e97SDavid Howells _leave(" = %d", ret); 505*07a90e97SDavid Howells return ret; 506*07a90e97SDavid Howells } 507*07a90e97SDavid Howells 508*07a90e97SDavid Howells /* 509*07a90e97SDavid Howells * Find out if an object is in use or not 510*07a90e97SDavid Howells * - called only by cache manager daemon 511*07a90e97SDavid Howells * - returns -EBUSY or 0 to indicate whether an object is in use or not 512*07a90e97SDavid Howells */ 513*07a90e97SDavid Howells int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry *dir, 514*07a90e97SDavid Howells char *filename) 515*07a90e97SDavid Howells { 516*07a90e97SDavid Howells struct dentry *victim; 517*07a90e97SDavid Howells int ret = 0; 518*07a90e97SDavid Howells 519*07a90e97SDavid Howells victim = cachefiles_lookup_for_cull(cache, dir, filename); 520*07a90e97SDavid Howells if (IS_ERR(victim)) 521*07a90e97SDavid Howells return PTR_ERR(victim); 522*07a90e97SDavid Howells 523*07a90e97SDavid Howells inode_unlock(d_inode(dir)); 524*07a90e97SDavid Howells dput(victim); 525*07a90e97SDavid Howells return ret; 526*07a90e97SDavid Howells } 527