1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20da0b7fdSDavid Howells /* AFS dynamic root handling 366c7e1d3SDavid Howells * 466c7e1d3SDavid Howells * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. 566c7e1d3SDavid Howells * Written by David Howells (dhowells@redhat.com) 666c7e1d3SDavid Howells */ 766c7e1d3SDavid Howells 866c7e1d3SDavid Howells #include <linux/fs.h> 966c7e1d3SDavid Howells #include <linux/namei.h> 1066c7e1d3SDavid Howells #include <linux/dns_resolver.h> 1166c7e1d3SDavid Howells #include "internal.h" 1266c7e1d3SDavid Howells 13e49c7b2fSDavid Howells static atomic_t afs_autocell_ino; 14e49c7b2fSDavid Howells 15e49c7b2fSDavid Howells /* 16e49c7b2fSDavid Howells * iget5() comparator for inode created by autocell operations 17e49c7b2fSDavid Howells * 18e49c7b2fSDavid Howells * These pseudo inodes don't match anything. 19e49c7b2fSDavid Howells */ 20e49c7b2fSDavid Howells static int afs_iget5_pseudo_test(struct inode *inode, void *opaque) 21e49c7b2fSDavid Howells { 22e49c7b2fSDavid Howells return 0; 23e49c7b2fSDavid Howells } 24e49c7b2fSDavid Howells 25e49c7b2fSDavid Howells /* 26e49c7b2fSDavid Howells * iget5() inode initialiser 27e49c7b2fSDavid Howells */ 28e49c7b2fSDavid Howells static int afs_iget5_pseudo_set(struct inode *inode, void *opaque) 29e49c7b2fSDavid Howells { 30e49c7b2fSDavid Howells struct afs_super_info *as = AFS_FS_S(inode->i_sb); 31e49c7b2fSDavid Howells struct afs_vnode *vnode = AFS_FS_I(inode); 32e49c7b2fSDavid Howells struct afs_fid *fid = opaque; 33e49c7b2fSDavid Howells 34e49c7b2fSDavid Howells vnode->volume = as->volume; 35e49c7b2fSDavid Howells vnode->fid = *fid; 36e49c7b2fSDavid Howells inode->i_ino = fid->vnode; 37e49c7b2fSDavid Howells inode->i_generation = fid->unique; 38e49c7b2fSDavid Howells return 0; 39e49c7b2fSDavid Howells } 40e49c7b2fSDavid Howells 41e49c7b2fSDavid Howells /* 42e49c7b2fSDavid Howells * Create an inode for a dynamic root directory or an autocell dynamic 43e49c7b2fSDavid Howells * automount dir. 44e49c7b2fSDavid Howells */ 45e49c7b2fSDavid Howells struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root) 46e49c7b2fSDavid Howells { 47e49c7b2fSDavid Howells struct afs_super_info *as = AFS_FS_S(sb); 48e49c7b2fSDavid Howells struct afs_vnode *vnode; 49e49c7b2fSDavid Howells struct inode *inode; 50e49c7b2fSDavid Howells struct afs_fid fid = {}; 51e49c7b2fSDavid Howells 52e49c7b2fSDavid Howells _enter(""); 53e49c7b2fSDavid Howells 54e49c7b2fSDavid Howells if (as->volume) 55e49c7b2fSDavid Howells fid.vid = as->volume->vid; 56e49c7b2fSDavid Howells if (root) { 57e49c7b2fSDavid Howells fid.vnode = 1; 58e49c7b2fSDavid Howells fid.unique = 1; 59e49c7b2fSDavid Howells } else { 60e49c7b2fSDavid Howells fid.vnode = atomic_inc_return(&afs_autocell_ino); 61e49c7b2fSDavid Howells fid.unique = 0; 62e49c7b2fSDavid Howells } 63e49c7b2fSDavid Howells 64e49c7b2fSDavid Howells inode = iget5_locked(sb, fid.vnode, 65e49c7b2fSDavid Howells afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid); 66e49c7b2fSDavid Howells if (!inode) { 67e49c7b2fSDavid Howells _leave(" = -ENOMEM"); 68e49c7b2fSDavid Howells return ERR_PTR(-ENOMEM); 69e49c7b2fSDavid Howells } 70e49c7b2fSDavid Howells 71e49c7b2fSDavid Howells _debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }", 72e49c7b2fSDavid Howells inode, inode->i_ino, fid.vid, fid.vnode, fid.unique); 73e49c7b2fSDavid Howells 74e49c7b2fSDavid Howells vnode = AFS_FS_I(inode); 75e49c7b2fSDavid Howells 76e49c7b2fSDavid Howells /* there shouldn't be an existing inode */ 77e49c7b2fSDavid Howells BUG_ON(!(inode->i_state & I_NEW)); 78e49c7b2fSDavid Howells 79e81fb419SLinus Torvalds netfs_inode_init(&vnode->netfs, NULL); 80e49c7b2fSDavid Howells inode->i_size = 0; 81e49c7b2fSDavid Howells inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; 82e49c7b2fSDavid Howells if (root) { 83e49c7b2fSDavid Howells inode->i_op = &afs_dynroot_inode_operations; 84e49c7b2fSDavid Howells inode->i_fop = &simple_dir_operations; 85e49c7b2fSDavid Howells } else { 86e49c7b2fSDavid Howells inode->i_op = &afs_autocell_inode_operations; 87e49c7b2fSDavid Howells } 88e49c7b2fSDavid Howells set_nlink(inode, 2); 89e49c7b2fSDavid Howells inode->i_uid = GLOBAL_ROOT_UID; 90e49c7b2fSDavid Howells inode->i_gid = GLOBAL_ROOT_GID; 91562ce1f7SJeff Layton simple_inode_init_ts(inode); 92e49c7b2fSDavid Howells inode->i_blocks = 0; 93e49c7b2fSDavid Howells inode->i_generation = 0; 94e49c7b2fSDavid Howells 95e49c7b2fSDavid Howells set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); 96e49c7b2fSDavid Howells if (!root) { 97e49c7b2fSDavid Howells set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); 98e49c7b2fSDavid Howells inode->i_flags |= S_AUTOMOUNT; 99e49c7b2fSDavid Howells } 100e49c7b2fSDavid Howells 101e49c7b2fSDavid Howells inode->i_flags |= S_NOATIME; 102e49c7b2fSDavid Howells unlock_new_inode(inode); 103e49c7b2fSDavid Howells _leave(" = %p", inode); 104e49c7b2fSDavid Howells return inode; 105e49c7b2fSDavid Howells } 106e49c7b2fSDavid Howells 10766c7e1d3SDavid Howells /* 10866c7e1d3SDavid Howells * Probe to see if a cell may exist. This prevents positive dentries from 10966c7e1d3SDavid Howells * being created unnecessarily. 11066c7e1d3SDavid Howells */ 11166c7e1d3SDavid Howells static int afs_probe_cell_name(struct dentry *dentry) 11266c7e1d3SDavid Howells { 11366c7e1d3SDavid Howells struct afs_cell *cell; 114a58946c1SDavid Howells struct afs_net *net = afs_d2net(dentry); 11566c7e1d3SDavid Howells const char *name = dentry->d_name.name; 11666c7e1d3SDavid Howells size_t len = dentry->d_name.len; 11766c7e1d3SDavid Howells int ret; 11866c7e1d3SDavid Howells 11966c7e1d3SDavid Howells /* Names prefixed with a dot are R/W mounts. */ 12066c7e1d3SDavid Howells if (name[0] == '.') { 12166c7e1d3SDavid Howells if (len == 1) 12266c7e1d3SDavid Howells return -EINVAL; 12366c7e1d3SDavid Howells name++; 12466c7e1d3SDavid Howells len--; 12566c7e1d3SDavid Howells } 12666c7e1d3SDavid Howells 127dca54a7bSDavid Howells cell = afs_find_cell(net, name, len, afs_cell_trace_use_probe); 12866c7e1d3SDavid Howells if (!IS_ERR(cell)) { 129dca54a7bSDavid Howells afs_unuse_cell(net, cell, afs_cell_trace_unuse_probe); 13066c7e1d3SDavid Howells return 0; 13166c7e1d3SDavid Howells } 13266c7e1d3SDavid Howells 133a58946c1SDavid Howells ret = dns_query(net->net, "afsdb", name, len, "srv=1", 134a58946c1SDavid Howells NULL, NULL, false); 13566c7e1d3SDavid Howells if (ret == -ENODATA) 13666c7e1d3SDavid Howells ret = -EDESTADDRREQ; 13766c7e1d3SDavid Howells return ret; 13866c7e1d3SDavid Howells } 13966c7e1d3SDavid Howells 14066c7e1d3SDavid Howells /* 14166c7e1d3SDavid Howells * Try to auto mount the mountpoint with pseudo directory, if the autocell 14266c7e1d3SDavid Howells * operation is setted. 14366c7e1d3SDavid Howells */ 14466c7e1d3SDavid Howells struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir) 14566c7e1d3SDavid Howells { 14666c7e1d3SDavid Howells struct afs_vnode *vnode = AFS_FS_I(dir); 14766c7e1d3SDavid Howells struct inode *inode; 14866c7e1d3SDavid Howells int ret = -ENOENT; 14966c7e1d3SDavid Howells 1503b6492dfSDavid Howells _enter("%p{%pd}, {%llx:%llu}", 15166c7e1d3SDavid Howells dentry, dentry, vnode->fid.vid, vnode->fid.vnode); 15266c7e1d3SDavid Howells 15366c7e1d3SDavid Howells if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) 15466c7e1d3SDavid Howells goto out; 15566c7e1d3SDavid Howells 15666c7e1d3SDavid Howells ret = afs_probe_cell_name(dentry); 15766c7e1d3SDavid Howells if (ret < 0) 15866c7e1d3SDavid Howells goto out; 15966c7e1d3SDavid Howells 16066c7e1d3SDavid Howells inode = afs_iget_pseudo_dir(dir->i_sb, false); 16166c7e1d3SDavid Howells if (IS_ERR(inode)) { 16266c7e1d3SDavid Howells ret = PTR_ERR(inode); 16366c7e1d3SDavid Howells goto out; 16466c7e1d3SDavid Howells } 16566c7e1d3SDavid Howells 16666c7e1d3SDavid Howells _leave("= %p", inode); 16766c7e1d3SDavid Howells return inode; 16866c7e1d3SDavid Howells 16966c7e1d3SDavid Howells out: 17066c7e1d3SDavid Howells _leave("= %d", ret); 1711401a0fcSAl Viro return ret == -ENOENT ? NULL : ERR_PTR(ret); 17266c7e1d3SDavid Howells } 17366c7e1d3SDavid Howells 17466c7e1d3SDavid Howells /* 17566c7e1d3SDavid Howells * Look up @cell in a dynroot directory. This is a substitution for the 17666c7e1d3SDavid Howells * local cell name for the net namespace. 17766c7e1d3SDavid Howells */ 17866c7e1d3SDavid Howells static struct dentry *afs_lookup_atcell(struct dentry *dentry) 17966c7e1d3SDavid Howells { 18066c7e1d3SDavid Howells struct afs_cell *cell; 18166c7e1d3SDavid Howells struct afs_net *net = afs_d2net(dentry); 18266c7e1d3SDavid Howells struct dentry *ret; 18366c7e1d3SDavid Howells char *name; 18466c7e1d3SDavid Howells int len; 18566c7e1d3SDavid Howells 18666c7e1d3SDavid Howells if (!net->ws_cell) 18766c7e1d3SDavid Howells return ERR_PTR(-ENOENT); 18866c7e1d3SDavid Howells 18966c7e1d3SDavid Howells ret = ERR_PTR(-ENOMEM); 19066c7e1d3SDavid Howells name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL); 19166c7e1d3SDavid Howells if (!name) 19266c7e1d3SDavid Howells goto out_p; 19366c7e1d3SDavid Howells 19492e3cc91SDavid Howells down_read(&net->cells_lock); 19592e3cc91SDavid Howells cell = net->ws_cell; 19666c7e1d3SDavid Howells if (cell) { 19766c7e1d3SDavid Howells len = cell->name_len; 19866c7e1d3SDavid Howells memcpy(name, cell->name, len + 1); 19966c7e1d3SDavid Howells } 20092e3cc91SDavid Howells up_read(&net->cells_lock); 20166c7e1d3SDavid Howells 20266c7e1d3SDavid Howells ret = ERR_PTR(-ENOENT); 20366c7e1d3SDavid Howells if (!cell) 20466c7e1d3SDavid Howells goto out_n; 20566c7e1d3SDavid Howells 20666c7e1d3SDavid Howells ret = lookup_one_len(name, dentry->d_parent, len); 20766c7e1d3SDavid Howells 20866c7e1d3SDavid Howells /* We don't want to d_add() the @cell dentry here as we don't want to 20966c7e1d3SDavid Howells * the cached dentry to hide changes to the local cell name. 21066c7e1d3SDavid Howells */ 21166c7e1d3SDavid Howells 21266c7e1d3SDavid Howells out_n: 21366c7e1d3SDavid Howells kfree(name); 21466c7e1d3SDavid Howells out_p: 21566c7e1d3SDavid Howells return ret; 21666c7e1d3SDavid Howells } 21766c7e1d3SDavid Howells 21866c7e1d3SDavid Howells /* 21966c7e1d3SDavid Howells * Look up an entry in a dynroot directory. 22066c7e1d3SDavid Howells */ 22166c7e1d3SDavid Howells static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry, 22266c7e1d3SDavid Howells unsigned int flags) 22366c7e1d3SDavid Howells { 22466c7e1d3SDavid Howells _enter("%pd", dentry); 22566c7e1d3SDavid Howells 22666c7e1d3SDavid Howells ASSERTCMP(d_inode(dentry), ==, NULL); 22766c7e1d3SDavid Howells 2281da4bd9fSDavid Howells if (flags & LOOKUP_CREATE) 2291da4bd9fSDavid Howells return ERR_PTR(-EOPNOTSUPP); 2301da4bd9fSDavid Howells 23166c7e1d3SDavid Howells if (dentry->d_name.len >= AFSNAMEMAX) { 23266c7e1d3SDavid Howells _leave(" = -ENAMETOOLONG"); 23366c7e1d3SDavid Howells return ERR_PTR(-ENAMETOOLONG); 23466c7e1d3SDavid Howells } 23566c7e1d3SDavid Howells 23666c7e1d3SDavid Howells if (dentry->d_name.len == 5 && 23766c7e1d3SDavid Howells memcmp(dentry->d_name.name, "@cell", 5) == 0) 23866c7e1d3SDavid Howells return afs_lookup_atcell(dentry); 23966c7e1d3SDavid Howells 2401401a0fcSAl Viro return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry); 24166c7e1d3SDavid Howells } 24266c7e1d3SDavid Howells 24366c7e1d3SDavid Howells const struct inode_operations afs_dynroot_inode_operations = { 24466c7e1d3SDavid Howells .lookup = afs_dynroot_lookup, 24566c7e1d3SDavid Howells }; 24666c7e1d3SDavid Howells 24766c7e1d3SDavid Howells /* 24866c7e1d3SDavid Howells * Dirs in the dynamic root don't need revalidation. 24966c7e1d3SDavid Howells */ 25066c7e1d3SDavid Howells static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags) 25166c7e1d3SDavid Howells { 25266c7e1d3SDavid Howells return 1; 25366c7e1d3SDavid Howells } 25466c7e1d3SDavid Howells 25566c7e1d3SDavid Howells /* 25666c7e1d3SDavid Howells * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't 25766c7e1d3SDavid Howells * sleep) 25866c7e1d3SDavid Howells * - called from dput() when d_count is going to 0. 25966c7e1d3SDavid Howells * - return 1 to request dentry be unhashed, 0 otherwise 26066c7e1d3SDavid Howells */ 26166c7e1d3SDavid Howells static int afs_dynroot_d_delete(const struct dentry *dentry) 26266c7e1d3SDavid Howells { 26366c7e1d3SDavid Howells return d_really_is_positive(dentry); 26466c7e1d3SDavid Howells } 26566c7e1d3SDavid Howells 26666c7e1d3SDavid Howells const struct dentry_operations afs_dynroot_dentry_operations = { 26766c7e1d3SDavid Howells .d_revalidate = afs_dynroot_d_revalidate, 26866c7e1d3SDavid Howells .d_delete = afs_dynroot_d_delete, 26966c7e1d3SDavid Howells .d_release = afs_d_release, 27066c7e1d3SDavid Howells .d_automount = afs_d_automount, 27166c7e1d3SDavid Howells }; 2720da0b7fdSDavid Howells 2730da0b7fdSDavid Howells /* 2740da0b7fdSDavid Howells * Create a manually added cell mount directory. 2750da0b7fdSDavid Howells * - The caller must hold net->proc_cells_lock 2760da0b7fdSDavid Howells */ 2770da0b7fdSDavid Howells int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell) 2780da0b7fdSDavid Howells { 2790da0b7fdSDavid Howells struct super_block *sb = net->dynroot_sb; 2800da0b7fdSDavid Howells struct dentry *root, *subdir; 2810da0b7fdSDavid Howells int ret; 2820da0b7fdSDavid Howells 2830da0b7fdSDavid Howells if (!sb || atomic_read(&sb->s_active) == 0) 2840da0b7fdSDavid Howells return 0; 2850da0b7fdSDavid Howells 2860da0b7fdSDavid Howells /* Let the ->lookup op do the creation */ 2870da0b7fdSDavid Howells root = sb->s_root; 2880da0b7fdSDavid Howells inode_lock(root->d_inode); 2890da0b7fdSDavid Howells subdir = lookup_one_len(cell->name, root, cell->name_len); 2900da0b7fdSDavid Howells if (IS_ERR(subdir)) { 2910da0b7fdSDavid Howells ret = PTR_ERR(subdir); 2920da0b7fdSDavid Howells goto unlock; 2930da0b7fdSDavid Howells } 2940da0b7fdSDavid Howells 2950da0b7fdSDavid Howells /* Note that we're retaining an extra ref on the dentry */ 2960da0b7fdSDavid Howells subdir->d_fsdata = (void *)1UL; 2970da0b7fdSDavid Howells ret = 0; 2980da0b7fdSDavid Howells unlock: 2990da0b7fdSDavid Howells inode_unlock(root->d_inode); 3000da0b7fdSDavid Howells return ret; 3010da0b7fdSDavid Howells } 3020da0b7fdSDavid Howells 3030da0b7fdSDavid Howells /* 3040da0b7fdSDavid Howells * Remove a manually added cell mount directory. 3050da0b7fdSDavid Howells * - The caller must hold net->proc_cells_lock 3060da0b7fdSDavid Howells */ 3070da0b7fdSDavid Howells void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell) 3080da0b7fdSDavid Howells { 3090da0b7fdSDavid Howells struct super_block *sb = net->dynroot_sb; 3100da0b7fdSDavid Howells struct dentry *root, *subdir; 3110da0b7fdSDavid Howells 3120da0b7fdSDavid Howells if (!sb || atomic_read(&sb->s_active) == 0) 3130da0b7fdSDavid Howells return; 3140da0b7fdSDavid Howells 3150da0b7fdSDavid Howells root = sb->s_root; 3160da0b7fdSDavid Howells inode_lock(root->d_inode); 3170da0b7fdSDavid Howells 3180da0b7fdSDavid Howells /* Don't want to trigger a lookup call, which will re-add the cell */ 3190da0b7fdSDavid Howells subdir = try_lookup_one_len(cell->name, root, cell->name_len); 3200da0b7fdSDavid Howells if (IS_ERR_OR_NULL(subdir)) { 3210da0b7fdSDavid Howells _debug("lookup %ld", PTR_ERR(subdir)); 3220da0b7fdSDavid Howells goto no_dentry; 3230da0b7fdSDavid Howells } 3240da0b7fdSDavid Howells 3250da0b7fdSDavid Howells _debug("rmdir %pd %u", subdir, d_count(subdir)); 3260da0b7fdSDavid Howells 3270da0b7fdSDavid Howells if (subdir->d_fsdata) { 3280da0b7fdSDavid Howells _debug("unpin %u", d_count(subdir)); 3290da0b7fdSDavid Howells subdir->d_fsdata = NULL; 3300da0b7fdSDavid Howells dput(subdir); 3310da0b7fdSDavid Howells } 3320da0b7fdSDavid Howells dput(subdir); 3330da0b7fdSDavid Howells no_dentry: 3340da0b7fdSDavid Howells inode_unlock(root->d_inode); 3350da0b7fdSDavid Howells _leave(""); 3360da0b7fdSDavid Howells } 3370da0b7fdSDavid Howells 3380da0b7fdSDavid Howells /* 3390da0b7fdSDavid Howells * Populate a newly created dynamic root with cell names. 3400da0b7fdSDavid Howells */ 3410da0b7fdSDavid Howells int afs_dynroot_populate(struct super_block *sb) 3420da0b7fdSDavid Howells { 3430da0b7fdSDavid Howells struct afs_cell *cell; 3440da0b7fdSDavid Howells struct afs_net *net = afs_sb2net(sb); 3450da0b7fdSDavid Howells int ret; 3460da0b7fdSDavid Howells 3473b05e528SDavid Howells mutex_lock(&net->proc_cells_lock); 3480da0b7fdSDavid Howells 3490da0b7fdSDavid Howells net->dynroot_sb = sb; 3506b3944e4SDavid Howells hlist_for_each_entry(cell, &net->proc_cells, proc_link) { 3510da0b7fdSDavid Howells ret = afs_dynroot_mkdir(net, cell); 3520da0b7fdSDavid Howells if (ret < 0) 3530da0b7fdSDavid Howells goto error; 3540da0b7fdSDavid Howells } 3550da0b7fdSDavid Howells 3560da0b7fdSDavid Howells ret = 0; 3570da0b7fdSDavid Howells out: 3580da0b7fdSDavid Howells mutex_unlock(&net->proc_cells_lock); 3590da0b7fdSDavid Howells return ret; 3600da0b7fdSDavid Howells 3610da0b7fdSDavid Howells error: 3620da0b7fdSDavid Howells net->dynroot_sb = NULL; 3630da0b7fdSDavid Howells goto out; 3640da0b7fdSDavid Howells } 3650da0b7fdSDavid Howells 3660da0b7fdSDavid Howells /* 3670da0b7fdSDavid Howells * When a dynamic root that's in the process of being destroyed, depopulate it 3680da0b7fdSDavid Howells * of pinned directories. 3690da0b7fdSDavid Howells */ 3700da0b7fdSDavid Howells void afs_dynroot_depopulate(struct super_block *sb) 3710da0b7fdSDavid Howells { 3720da0b7fdSDavid Howells struct afs_net *net = afs_sb2net(sb); 373*da549bddSAl Viro struct dentry *root = sb->s_root, *subdir; 3740da0b7fdSDavid Howells 3750da0b7fdSDavid Howells /* Prevent more subdirs from being created */ 3760da0b7fdSDavid Howells mutex_lock(&net->proc_cells_lock); 3770da0b7fdSDavid Howells if (net->dynroot_sb == sb) 3780da0b7fdSDavid Howells net->dynroot_sb = NULL; 3790da0b7fdSDavid Howells mutex_unlock(&net->proc_cells_lock); 3800da0b7fdSDavid Howells 3815e0b17b0SDavid Howells if (root) { 382*da549bddSAl Viro struct hlist_node *n; 3830da0b7fdSDavid Howells inode_lock(root->d_inode); 3840da0b7fdSDavid Howells 3850da0b7fdSDavid Howells /* Remove all the pins for dirs created for manually added cells */ 386*da549bddSAl Viro hlist_for_each_entry_safe(subdir, n, &root->d_children, d_sib) { 3870da0b7fdSDavid Howells if (subdir->d_fsdata) { 3880da0b7fdSDavid Howells subdir->d_fsdata = NULL; 3890da0b7fdSDavid Howells dput(subdir); 3900da0b7fdSDavid Howells } 3910da0b7fdSDavid Howells } 3920da0b7fdSDavid Howells 3930da0b7fdSDavid Howells inode_unlock(root->d_inode); 3940da0b7fdSDavid Howells } 3955e0b17b0SDavid Howells } 396