History

NFSv4: Fix revalidation of dentries with delegations

If a dentry was not initially looked up while we were holding a delegation, then we do still need to revalidate that it still holds the same name. If there are multiple hard links to the same file, then all the hard links need validation.

Trond Myklebust committed 6 years ago

Commit efeda801 parent cf5b4054 files · +115 −23Browse files

Unified · Split

4 files
  1. fs/nfs/delegation.c +6 −0
  2. fs/nfs/dir.c +102 −3
  3. fs/nfs/inode.c +1 −0
  4. include/linux/nfs_fs.h +6 −20
fs/nfs/delegation.c+6−0
@@ -42,6 +42,8 @@ static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation) · 41 unmodified lines
42 if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {42 if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
43 delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;43 delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
44 atomic_long_dec(&nfs_active_delegations);44 atomic_long_dec(&nfs_active_delegations);
45 if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
46 nfs_clear_verifier_delegated(delegation->inode);
45 }47 }
46}48}
47 49
@@ -276,6 +278,8 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi) · 228 unmodified lines
276 if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))278 if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
277 ret = delegation;279 ret = delegation;
278 spin_unlock(&delegation->lock);280 spin_unlock(&delegation->lock);
281 if (ret)
282 nfs_clear_verifier_delegated(&nfsi->vfs_inode);
279out:283out:
280 return ret;284 return ret;
281}285}
@@ -689,6 +693,8 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) · 407 unmodified lines
689 ret = delegation;693 ret = delegation;
690 }694 }
691 spin_unlock(&delegation->lock);695 spin_unlock(&delegation->lock);
696 if (ret)
697 nfs_clear_verifier_delegated(inode);
692 }698 }
693out:699out:
694 rcu_read_unlock();700 rcu_read_unlock();
fs/nfs/dir.c+102−3
@@ -986,14 +986,113 @@ static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, · 985 unmodified lines
986 * full lookup on all child dentries of 'dir' whenever a change occurs986 * full lookup on all child dentries of 'dir' whenever a change occurs
987 * on the server that might have invalidated our dcache.987 * on the server that might have invalidated our dcache.
988 *988 *
989 * Note that we reserve bit '0' as a tag to let us know when a dentry
990 * was revalidated while holding a delegation on its inode.
991 *
989 * The caller should be holding dir->i_lock992 * The caller should be holding dir->i_lock
990 */993 */
991void nfs_force_lookup_revalidate(struct inode *dir)994void nfs_force_lookup_revalidate(struct inode *dir)
992{995{
993 NFS_I(dir)->cache_change_attribute++;996 NFS_I(dir)->cache_change_attribute += 2;
994}997}
995EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate);998EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate);
996 999
1000/**
1001 * nfs_verify_change_attribute - Detects NFS remote directory changes
1002 * @dir: pointer to parent directory inode
1003 * @verf: previously saved change attribute
1004 *
1005 * Return "false" if the verifiers doesn't match the change attribute.
1006 * This would usually indicate that the directory contents have changed on
1007 * the server, and that any dentries need revalidating.
1008 */
1009static bool nfs_verify_change_attribute(struct inode *dir, unsigned long verf)
1010{
1011 return (verf & ~1UL) == nfs_save_change_attribute(dir);
1012}
1013
1014static void nfs_set_verifier_delegated(unsigned long *verf)
1015{
1016 *verf |= 1UL;
1017}
1018
1019#if IS_ENABLED(CONFIG_NFS_V4)
1020static void nfs_unset_verifier_delegated(unsigned long *verf)
1021{
1022 *verf &= ~1UL;
1023}
1024#endif /* IS_ENABLED(CONFIG_NFS_V4) */
1025
1026static bool nfs_test_verifier_delegated(unsigned long verf)
1027{
1028 return verf & 1;
1029}
1030
1031static bool nfs_verifier_is_delegated(struct dentry *dentry)
1032{
1033 return nfs_test_verifier_delegated(dentry->d_time);
1034}
1035
1036static void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)
1037{
1038 struct inode *inode = d_inode(dentry);
1039
1040 if (!nfs_verifier_is_delegated(dentry) &&
1041 !nfs_verify_change_attribute(d_inode(dentry->d_parent), verf))
1042 goto out;
1043 if (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
1044 nfs_set_verifier_delegated(&verf);
1045out:
1046 dentry->d_time = verf;
1047}
1048
1049/**
1050 * nfs_set_verifier - save a parent directory verifier in the dentry
1051 * @dentry: pointer to dentry
1052 * @verf: verifier to save
1053 *
1054 * Saves the parent directory verifier in @dentry. If the inode has
1055 * a delegation, we also tag the dentry as having been revalidated
1056 * while holding a delegation so that we know we don't have to
1057 * look it up again after a directory change.
1058 */
1059void nfs_set_verifier(struct dentry *dentry, unsigned long verf)
1060{
1061
1062 spin_lock(&dentry->d_lock);
1063 nfs_set_verifier_locked(dentry, verf);
1064 spin_unlock(&dentry->d_lock);
1065}
1066EXPORT_SYMBOL_GPL(nfs_set_verifier);
1067
1068#if IS_ENABLED(CONFIG_NFS_V4)
1069/**
1070 * nfs_clear_verifier_delegated - clear the dir verifier delegation tag
1071 * @inode: pointer to inode
1072 *
1073 * Iterates through the dentries in the inode alias list and clears
1074 * the tag used to indicate that the dentry has been revalidated
1075 * while holding a delegation.
1076 * This function is intended for use when the delegation is being
1077 * returned or revoked.
1078 */
1079void nfs_clear_verifier_delegated(struct inode *inode)
1080{
1081 struct dentry *alias;
1082
1083 if (!inode)
1084 return;
1085 spin_lock(&inode->i_lock);
1086 hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
1087 spin_lock(&alias->d_lock);
1088 nfs_unset_verifier_delegated(&alias->d_time);
1089 spin_unlock(&alias->d_lock);
1090 }
1091 spin_unlock(&inode->i_lock);
1092}
1093EXPORT_SYMBOL_GPL(nfs_clear_verifier_delegated);
1094#endif /* IS_ENABLED(CONFIG_NFS_V4) */
1095
997/*1096/*
998 * A check for whether or not the parent directory has changed.1097 * A check for whether or not the parent directory has changed.
999 * In the case it has, we assume that the dentries are untrustworthy1098 * In the case it has, we assume that the dentries are untrustworthy
@@ -1235,7 +1334,7 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry, · 235 unmodified lines
1235 goto out_bad;1334 goto out_bad;
1236 }1335 }
1237 1336
1238 if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))1337 if (nfs_verifier_is_delegated(dentry))
1239 return nfs_lookup_revalidate_delegated(dir, dentry, inode);1338 return nfs_lookup_revalidate_delegated(dir, dentry, inode);
1240 1339
1241 /* Force a full look up iff the parent directory has changed */1340 /* Force a full look up iff the parent directory has changed */
@@ -1675,7 +1774,7 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry, · 433 unmodified lines
1675 if (inode == NULL)1774 if (inode == NULL)
1676 goto full_reval;1775 goto full_reval;
1677 1776
1678 if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))1777 if (nfs_verifier_is_delegated(dentry))
1679 return nfs_lookup_revalidate_delegated(dir, dentry, inode);1778 return nfs_lookup_revalidate_delegated(dir, dentry, inode);
1680 1779
1681 /* NFS only supports OPEN on regular files */1780 /* NFS only supports OPEN on regular files */
fs/nfs/inode.c+1−0
@@ -2114,6 +2114,7 @@ static void init_once(void *foo) · 2113 unmodified lines
2114 init_rwsem(&nfsi->rmdir_sem);2114 init_rwsem(&nfsi->rmdir_sem);
2115 mutex_init(&nfsi->commit_mutex);2115 mutex_init(&nfsi->commit_mutex);
2116 nfs4_init_once(nfsi);2116 nfs4_init_once(nfsi);
2117 nfsi->cache_change_attribute = 0;
2117}2118}
2118 2119
2119static int __init nfs_init_inodecache(void)2120static int __init nfs_init_inodecache(void)
include/linux/nfs_fs.h+6−20
@@ -337,35 +337,17 @@ static inline int nfs_server_capable(struct inode *inode, int cap) · 336 unmodified lines
337 return NFS_SERVER(inode)->caps & cap;337 return NFS_SERVER(inode)->caps & cap;
338}338}
339 339
340static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
341{
342 dentry->d_time = verf;
343}
344
345/**340/**
346 * nfs_save_change_attribute - Returns the inode attribute change cookie341 * nfs_save_change_attribute - Returns the inode attribute change cookie
347 * @dir - pointer to parent directory inode342 * @dir - pointer to parent directory inode
348 * The "change attribute" is updated every time we finish an operation343 * The "cache change attribute" is updated when we need to revalidate
349 * that will result in a metadata change on the server.344 * our dentry cache after a directory was seen to change on the server.
350 */345 */
351static inline unsigned long nfs_save_change_attribute(struct inode *dir)346static inline unsigned long nfs_save_change_attribute(struct inode *dir)
352{347{
353 return NFS_I(dir)->cache_change_attribute;348 return NFS_I(dir)->cache_change_attribute;
354}349}
355 350
356/**
357 * nfs_verify_change_attribute - Detects NFS remote directory changes
358 * @dir - pointer to parent directory inode
359 * @chattr - previously saved change attribute
360 * Return "false" if the verifiers doesn't match the change attribute.
361 * This would usually indicate that the directory contents have changed on
362 * the server, and that any dentries need revalidating.
363 */
364static inline int nfs_verify_change_attribute(struct inode *dir, unsigned long chattr)
365{
366 return chattr == NFS_I(dir)->cache_change_attribute;
367}
368
369/*351/*
370 * linux/fs/nfs/inode.c352 * linux/fs/nfs/inode.c
371 */353 */
@@ -495,6 +477,10 @@ extern const struct file_operations nfs_dir_operations; · 123 unmodified lines
495extern const struct dentry_operations nfs_dentry_operations;477extern const struct dentry_operations nfs_dentry_operations;
496 478
497extern void nfs_force_lookup_revalidate(struct inode *dir);479extern void nfs_force_lookup_revalidate(struct inode *dir);
480extern void nfs_set_verifier(struct dentry * dentry, unsigned long verf);
481#if IS_ENABLED(CONFIG_NFS_V4)
482extern void nfs_clear_verifier_delegated(struct inode *inode);
483#endif /* IS_ENABLED(CONFIG_NFS_V4) */
498extern struct dentry *nfs_add_or_obtain(struct dentry *dentry,484extern struct dentry *nfs_add_or_obtain(struct dentry *dentry,
499 struct nfs_fh *fh, struct nfs_fattr *fattr,485 struct nfs_fh *fh, struct nfs_fattr *fattr,
500 struct nfs4_label *label);486 struct nfs4_label *label);