1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * This file contians vfs dentry ops for the 9P2000 protocol. 4 * 5 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> 6 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/errno.h> 11 #include <linux/fs.h> 12 #include <linux/file.h> 13 #include <linux/pagemap.h> 14 #include <linux/stat.h> 15 #include <linux/string.h> 16 #include <linux/namei.h> 17 #include <linux/sched.h> 18 #include <linux/slab.h> 19 #include <net/9p/9p.h> 20 #include <net/9p/client.h> 21 22 #include "v9fs.h" 23 #include "v9fs_vfs.h" 24 #include "fid.h" 25 26 /** 27 * v9fs_ndentry_is_expired - Check if negative dentry lookup has expired 28 * 29 * This should be called to know if a negative dentry should be removed from 30 * cache. 31 * 32 * @dentry: dentry in question 33 * 34 */ 35 static bool v9fs_ndentry_is_expired(struct dentry const *dentry) 36 { 37 struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); 38 struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); 39 40 if (v9ses->ndentry_timeout_ms == NDENTRY_TIMEOUT_NEVER) 41 return false; 42 43 return time_before_eq64(v9fs_dentry->expire_time, get_jiffies_64()); 44 } 45 46 /** 47 * v9fs_ndentry_refresh_timeout - Refresh negative dentry lookup cache timeout 48 * 49 * This should be called when a look up yields a negative entry. 50 * 51 * @dentry: dentry in question 52 * 53 */ 54 void v9fs_ndentry_refresh_timeout(struct dentry *dentry) 55 { 56 struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); 57 struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); 58 59 if (v9ses->ndentry_timeout_ms == NDENTRY_TIMEOUT_NEVER) 60 return; 61 62 v9fs_dentry->expire_time = get_jiffies_64() + 63 msecs_to_jiffies(v9ses->ndentry_timeout_ms); 64 } 65 66 /** 67 * v9fs_cached_dentry_delete - called when dentry refcount equals 0 68 * @dentry: dentry in question 69 * 70 */ 71 static int v9fs_cached_dentry_delete(const struct dentry *dentry) 72 { 73 p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n", 74 dentry, dentry); 75 76 if (!d_really_is_negative(dentry)) 77 return 0; 78 79 return v9fs_ndentry_is_expired(dentry); 80 } 81 82 static void __v9fs_dentry_fid_remove(struct dentry *dentry) 83 { 84 struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); 85 struct hlist_node *p, *n; 86 struct hlist_head head; 87 88 p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n", 89 dentry, dentry); 90 91 spin_lock(&dentry->d_lock); 92 hlist_move_list(&v9fs_dentry->head, &head); 93 spin_unlock(&dentry->d_lock); 94 95 hlist_for_each_safe(p, n, &head) 96 p9_fid_put(hlist_entry(p, struct p9_fid, dlist)); 97 } 98 99 /** 100 * v9fs_dentry_fid_remove - Release all dentry's fids 101 * @dentry: dentry in question 102 * 103 */ 104 void v9fs_dentry_fid_remove(struct dentry *dentry) 105 { 106 __v9fs_dentry_fid_remove(dentry); 107 } 108 109 /** 110 * v9fs_dentry_init - Initialize v9fs dentry data 111 * @dentry: dentry in question 112 * 113 */ 114 static int v9fs_dentry_init(struct dentry *dentry) 115 { 116 struct v9fs_dentry *v9fs_dentry = kzalloc_obj(*v9fs_dentry); 117 118 if (!v9fs_dentry) 119 return -ENOMEM; 120 121 INIT_HLIST_HEAD(&v9fs_dentry->head); 122 dentry->d_fsdata = (void *)v9fs_dentry; 123 return 0; 124 } 125 126 /** 127 * v9fs_dentry_release - called when dentry is going to be freed 128 * @dentry: dentry that is being released 129 * 130 */ 131 static void v9fs_dentry_release(struct dentry *dentry) 132 { 133 struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); 134 135 __v9fs_dentry_fid_remove(dentry); 136 kfree_rcu(v9fs_dentry, rcu); 137 } 138 139 static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags) 140 { 141 struct p9_fid *fid; 142 struct inode *inode; 143 struct v9fs_inode *v9inode; 144 145 if (flags & LOOKUP_RCU) 146 return -ECHILD; 147 148 inode = d_inode(dentry); 149 if (!inode) 150 return !v9fs_ndentry_is_expired(dentry); 151 152 v9inode = V9FS_I(inode); 153 if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) { 154 int retval; 155 struct v9fs_session_info *v9ses; 156 157 fid = v9fs_fid_lookup(dentry); 158 if (IS_ERR(fid)) { 159 p9_debug( 160 P9_DEBUG_VFS, 161 "v9fs_fid_lookup: dentry = %pd (%p), got error %pe\n", 162 dentry, dentry, fid); 163 return PTR_ERR(fid); 164 } 165 166 v9ses = v9fs_inode2v9ses(inode); 167 if (v9fs_proto_dotl(v9ses)) 168 retval = v9fs_refresh_inode_dotl(fid, inode); 169 else 170 retval = v9fs_refresh_inode(fid, inode); 171 p9_fid_put(fid); 172 173 if (retval == -ENOENT) { 174 p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) invalidated due to ENOENT\n", 175 dentry, dentry); 176 return 0; 177 } 178 if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) { 179 p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) invalidated due to type change\n", 180 dentry, dentry); 181 return 0; 182 } 183 if (retval < 0) { 184 p9_debug(P9_DEBUG_VFS, 185 "refresh inode: dentry = %pd (%p), got error %pe\n", 186 dentry, dentry, ERR_PTR(retval)); 187 return retval; 188 } 189 } 190 p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) is valid\n", dentry, dentry); 191 return 1; 192 } 193 194 static int v9fs_lookup_revalidate(struct inode *dir, const struct qstr *name, 195 struct dentry *dentry, unsigned int flags) 196 { 197 return __v9fs_lookup_revalidate(dentry, flags); 198 } 199 200 static bool v9fs_dentry_unalias_trylock(const struct dentry *dentry) 201 { 202 struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); 203 return down_write_trylock(&v9ses->rename_sem); 204 } 205 206 static void v9fs_dentry_unalias_unlock(const struct dentry *dentry) 207 { 208 struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); 209 up_write(&v9ses->rename_sem); 210 } 211 212 const struct dentry_operations v9fs_cached_dentry_operations = { 213 .d_revalidate = v9fs_lookup_revalidate, 214 .d_weak_revalidate = __v9fs_lookup_revalidate, 215 .d_delete = v9fs_cached_dentry_delete, 216 .d_init = v9fs_dentry_init, 217 .d_release = v9fs_dentry_release, 218 .d_unalias_trylock = v9fs_dentry_unalias_trylock, 219 .d_unalias_unlock = v9fs_dentry_unalias_unlock, 220 }; 221 222 const struct dentry_operations v9fs_dentry_operations = { 223 .d_init = v9fs_dentry_init, 224 .d_release = v9fs_dentry_release, 225 .d_unalias_trylock = v9fs_dentry_unalias_trylock, 226 .d_unalias_unlock = v9fs_dentry_unalias_unlock, 227 }; 228