@@ -986,14 +986,113 @@ static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
986
986
* full lookup on all child dentries of 'dir' whenever a change occurs
987
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
992
* The caller should be holding dir->i_lock
990
993
*/
991
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
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
1097
* A check for whether or not the parent directory has changed.
999
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,
1235
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
1338
return nfs_lookup_revalidate_delegated (dir , dentry , inode );
1240
1339
1241
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,
1675
1774
if (inode == NULL )
1676
1775
goto full_reval ;
1677
1776
1678
- if (NFS_PROTO ( dir ) -> have_delegation ( inode , FMODE_READ ))
1777
+ if (nfs_verifier_is_delegated ( dentry ))
1679
1778
return nfs_lookup_revalidate_delegated (dir , dentry , inode );
1680
1779
1681
1780
/* NFS only supports OPEN on regular files */
0 commit comments