| @@ -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 occurs | 986 | * 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_lock | 992 | * The caller should be holding dir->i_lock |
| 990 | */ | 993 | */ |
| 991 | void nfs_force_lookup_revalidate(struct inode *dir) | 994 | void 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 | } |
| 995 | EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate); | 998 | EXPORT_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 | */ |
| | 1009 | static bool nfs_verify_change_attribute(struct inode *dir, unsigned long verf) |
| | 1010 | { |
| | 1011 | return (verf & ~1UL) == nfs_save_change_attribute(dir); |
| | 1012 | } |
| | 1013 | |
| | 1014 | static void nfs_set_verifier_delegated(unsigned long *verf) |
| | 1015 | { |
| | 1016 | *verf |= 1UL; |
| | 1017 | } |
| | 1018 | |
| | 1019 | #if IS_ENABLED(CONFIG_NFS_V4) |
| | 1020 | static void nfs_unset_verifier_delegated(unsigned long *verf) |
| | 1021 | { |
| | 1022 | *verf &= ~1UL; |
| | 1023 | } |
| | 1024 | #endif /* IS_ENABLED(CONFIG_NFS_V4) */ |
| | 1025 | |
| | 1026 | static bool nfs_test_verifier_delegated(unsigned long verf) |
| | 1027 | { |
| | 1028 | return verf & 1; |
| | 1029 | } |
| | 1030 | |
| | 1031 | static bool nfs_verifier_is_delegated(struct dentry *dentry) |
| | 1032 | { |
| | 1033 | return nfs_test_verifier_delegated(dentry->d_time); |
| | 1034 | } |
| | 1035 | |
| | 1036 | static 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); |
| | 1045 | out: |
| | 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 | */ |
| | 1059 | void 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 | } |
| | 1066 | EXPORT_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 | */ |
| | 1079 | void 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 | } |
| | 1093 | EXPORT_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 untrustworthy | 1098 | * 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 */ |