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(sizeof(*v9fs_dentry), 117 GFP_KERNEL); 118 119 if (!v9fs_dentry) 120 return -ENOMEM; 121 122 INIT_HLIST_HEAD(&v9fs_dentry->head); 123 dentry->d_fsdata = (void *)v9fs_dentry; 124 return 0; 125 } 126 127 /** 128 * v9fs_dentry_release - called when dentry is going to be freed 129 * @dentry: dentry that is being released 130 * 131 */ 132 static void v9fs_dentry_release(struct dentry *dentry) 133 { 134 struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); 135 136 __v9fs_dentry_fid_remove(dentry); 137 kfree_rcu(v9fs_dentry, rcu); 138 } 139 140 static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags) 141 { 142 struct p9_fid *fid; 143 struct inode *inode; 144 struct v9fs_inode *v9inode; 145 146 if (flags & LOOKUP_RCU) 147 return -ECHILD; 148 149 inode = d_inode(dentry); 150 if (!inode) 151 return !v9fs_ndentry_is_expired(dentry); 152 153 v9inode = V9FS_I(inode); 154 if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) { 155 int retval; 156 struct v9fs_session_info *v9ses; 157 158 fid = v9fs_fid_lookup(dentry); 159 if (IS_ERR(fid)) { 160 p9_debug( 161 P9_DEBUG_VFS, 162 "v9fs_fid_lookup: dentry = %pd (%p), got error %pe\n", 163 dentry, dentry, fid); 164 return PTR_ERR(fid); 165 } 166 167 v9ses = v9fs_inode2v9ses(inode); 168 if (v9fs_proto_dotl(v9ses)) 169 retval = v9fs_refresh_inode_dotl(fid, inode); 170 else 171 retval = v9fs_refresh_inode(fid, inode); 172 p9_fid_put(fid); 173 174 if (retval == -ENOENT) { 175 p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) invalidated due to ENOENT\n", 176 dentry, dentry); 177 return 0; 178 } 179 if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) { 180 p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) invalidated due to type change\n", 181 dentry, dentry); 182 return 0; 183 } 184 if (retval < 0) { 185 p9_debug(P9_DEBUG_VFS, 186 "refresh inode: dentry = %pd (%p), got error %pe\n", 187 dentry, dentry, ERR_PTR(retval)); 188 return retval; 189 } 190 } 191 p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) is valid\n", dentry, dentry); 192 return 1; 193 } 194 195 static int v9fs_lookup_revalidate(struct inode *dir, const struct qstr *name, 196 struct dentry *dentry, unsigned int flags) 197 { 198 return __v9fs_lookup_revalidate(dentry, flags); 199 } 200 201 static bool v9fs_dentry_unalias_trylock(const struct dentry *dentry) 202 { 203 struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); 204 return down_write_trylock(&v9ses->rename_sem); 205 } 206 207 static void v9fs_dentry_unalias_unlock(const struct dentry *dentry) 208 { 209 struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); 210 up_write(&v9ses->rename_sem); 211 } 212 213 const struct dentry_operations v9fs_cached_dentry_operations = { 214 .d_revalidate = v9fs_lookup_revalidate, 215 .d_weak_revalidate = __v9fs_lookup_revalidate, 216 .d_delete = v9fs_cached_dentry_delete, 217 .d_init = v9fs_dentry_init, 218 .d_release = v9fs_dentry_release, 219 .d_unalias_trylock = v9fs_dentry_unalias_trylock, 220 .d_unalias_unlock = v9fs_dentry_unalias_unlock, 221 }; 222 223 const struct dentry_operations v9fs_dentry_operations = { 224 .d_init = v9fs_dentry_init, 225 .d_release = v9fs_dentry_release, 226 .d_unalias_trylock = v9fs_dentry_unalias_trylock, 227 .d_unalias_unlock = v9fs_dentry_unalias_unlock, 228 }; 229