1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
|
From 4c16207f3eb9c8591511c86409e57caaf94505a7 Mon Sep 17 00:00:00 2001
From: Cheyenne Wills <cwills@sinenomine.net>
Date: Thu, 28 Aug 2025 09:58:11 -0600
Subject: [PATCH 18/18] Linux: Use a stable dentry name in d_revalidate
In order to access a stable dentry name (dentry.d_name) directly
requires taking the d_lock, which can only be held for a short periods
of time and cannot be easily used in d_revalidate
The Linux 4.13 commit:
dentry name snapshots (49d31c2f389ac)
provides a utility function, take_dentry_name_snapshot(), that creates a
safe copy of the dentry name.
Update the d_revalidate function to obtain a stable dentry name instead
of accessing the dentry name directly.
Create wrapper functions get_stable_dentry_name() and
release_stable_dentry_name() for take_dentry_name_snapshot() and
release_dentry_name_snapshot() to help deal with backward compatibility
with Linux versions prior to the 49d31c2f389ac commit.
For Linux versions where take_dentry_name_snapshot is available, use the
take_dentry_sname_snapshot but also return a pointer to the name that
was obtained (name_snapshot.name).
For Linux versions prior that do not have the take_dentry_name_snapshot
the wrapper function simply returns a pointer to the dentry's
d_name.name, so the existing behavior of accessing the d_name remains
the same.
Add configure checks to determine if take_dentry_name_snapshot exists,
and if so check the datatype used within the name_snapshot structure
to see if it is a qstr or not. (Some distributions backported the
49d31c2f389ac commit for older Linux kernels, but the name_snapshot
structure uses a char * instead of a qstr).
Add a compile time check to ensure that Linux provides
take_dentry_name_snapshot() after 4.13 so errors during 'configure'
don't cause us to silently use the unsafe code path by mistake.
Note: This commit doesn't address the issues of using an unstable d_name
for Linux versions that do not provide the take_dentry_name_snapshot().
For these versions of Linux, there are problems within the Linux code
base itself surrounding the use of the dentry's name.
Change-Id: Ic99b1e5d7667f6841feea78ccf94db43ede40356
---
src/afs/LINUX/osi_vnodeops.c | 60 ++++++++++++++++++++++++++++++---
src/cf/linux-kernel-assorted.m4 | 7 +++-
src/cf/linux-kernel-func.m4 | 7 ++++
src/cf/linux-test4.m4 | 14 ++++++++
4 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c
index 12dd4d292..a0c17d19c 100644
--- a/src/afs/LINUX/osi_vnodeops.c
+++ b/src/afs/LINUX/osi_vnodeops.c
@@ -1382,15 +1382,60 @@ check_dentry_race(struct dentry *dp)
}
#endif /* D_SPLICE_ALIAS_RACE */
+/*
+ * Wrapper functions to obtain a stable dentry d_name.
+ * When d_revalidate is called, the dentry's d_dname is unstable.
+ * The function take_dentry_name_snapshot provides a stable name.
+ */
+#if defined(HAVE_LINUX_TAKE_DENTRY_NAME_SNAPSHOT)
+static inline const char *
+get_stable_dentry_name(struct name_snapshot *s, struct dentry *dp)
+{
+ take_dentry_name_snapshot(s, dp);
+# if defined(LINUX_STRUCT_NAME_SNAPSHOT_USES_QSTR)
+ return s->name.name;
+# else
+ return s->name;
+# endif
+}
+static inline void
+release_stable_dentry_name(struct name_snapshot *s)
+{
+ return release_dentry_name_snapshot(s);
+}
+#else
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
+# error "Missing take_dentry_name_snapshot"
+# endif
+/*
+ * For older versions of Linux (prior to the availablity of
+ * take_dentry_name_snapshot), fall back to just using the unstable version of
+ * the dentry d_name. Several of the filesystems within the Linux code base at
+ * this level are also using an unstable dentry d_name.
+ */
+struct name_snapshot {
+ int dummy;
+};
+static inline const char *
+get_stable_dentry_name(struct name_snapshot *s, struct dentry *dp)
+{
+ return dp->d_name.name;
+}
+static inline void
+release_stable_dentry_name(struct name_snapshot *s)
+{
+ return;
+}
+#endif /* HAVE_LINUX_TAKE_DENTRY_NAME_SNAPSHOT */
+
/*
* Validate a dentry. Return 1 if unchanged, 0 if VFS layer should re-evaluate.
*
* @param[in] pvcp vcache for the parent directory containing 'dp'
- * @param[in] name the name of the directory entry for 'dp'
* @param[in] dp the dentry we are checking
*/
static int
-dentry_revalidate_common(struct vcache *pvcp, const char *name, struct dentry *dp)
+dentry_revalidate_common(struct vcache *pvcp, struct dentry *dp)
{
cred_t *credp = NULL;
struct vcache *vcp, *tvc = NULL;
@@ -1399,6 +1444,8 @@ dentry_revalidate_common(struct vcache *pvcp, const char *name, struct dentry *d
int force_drop = 0;
afs_uint32 parent_dv;
int code = 0;
+ struct name_snapshot dname;
+ const char *name;
#ifdef D_SPLICE_ALIAS_RACE
if (check_dentry_race(dp)) {
@@ -1407,6 +1454,8 @@ dentry_revalidate_common(struct vcache *pvcp, const char *name, struct dentry *d
}
#endif
+ name = get_stable_dentry_name(&dname, dp);
+
AFS_GLOCK();
afs_InitFakeStat(&fakestate);
@@ -1560,6 +1609,8 @@ dentry_revalidate_common(struct vcache *pvcp, const char *name, struct dentry *d
if (credp)
crfree(credp);
+ release_stable_dentry_name(&dname);
+
#ifdef ERRORS_FROM_D_REVALIDATE
if (code != 0) {
/*
@@ -1609,7 +1660,7 @@ afs_linux_dentry_revalidate(struct inode *parent_inode, const struct qstr *name,
if ((flags & LOOKUP_RCU) != 0) {
return -ECHILD;
}
- return dentry_revalidate_common(VTOAFS(parent_inode), dp->d_name.name, dp);
+ return dentry_revalidate_common(VTOAFS(parent_inode), dp);
}
#else
# if defined(DOP_REVALIDATE_TAKES_UNSIGNED)
@@ -1640,8 +1691,7 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
# endif
parent = dget_parent(dp);
- code = dentry_revalidate_common(VTOAFS(parent->d_inode),
- dp->d_name.name, dp);
+ code = dentry_revalidate_common(VTOAFS(parent->d_inode), dp);
dput(parent);
return code;
diff --git a/src/cf/linux-kernel-assorted.m4 b/src/cf/linux-kernel-assorted.m4
index 21cec3669..85af7179c 100644
--- a/src/cf/linux-kernel-assorted.m4
+++ b/src/cf/linux-kernel-assorted.m4
@@ -62,8 +62,13 @@ LINUX_KEYRING_SEARCH_TAKES_RECURSE
LINUX_GENERIC_FILLATTR_TAKES_REQUEST_MASK
LINUX_FILE_LOCK_CORE
LINUX_WRITEPAGES_USES_FOLIOS
-])
+dnl If take_dentry_name_snapshot isn't present
+dnl don't bother checking if name_snapshot uses qstr
+AS_IF([test "x$ac_cv_linux_func_take_dentry_name_snapshot" = "xyes"],
+ [LINUX_STRUCT_NAME_SNAPSHOT_USES_QSTR])
+
+])
AC_DEFUN([OPENAFS_LINUX_KERNEL_MORE_ASSORTED_CHECKS],[
if test -f "$LINUX_KERNEL_PATH/include/linux/in_systm.h"; then
diff --git a/src/cf/linux-kernel-func.m4 b/src/cf/linux-kernel-func.m4
index c60804e6b..0550e226a 100644
--- a/src/cf/linux-kernel-func.m4
+++ b/src/cf/linux-kernel-func.m4
@@ -175,6 +175,13 @@ AC_CHECK_LINUX_FUNC([in_compat_syscall],
[#include <linux/compat.h>],
[in_compat_syscall();])
+dnl Linux 4.13 introduced take_dentry_name_snapshot
+AC_CHECK_LINUX_FUNC([take_dentry_name_snapshot],
+ [[#include <linux/dcache.h>
+ static struct name_snapshot dname;
+ static struct dentry *dp;]],
+ [[take_dentry_name_snapshot(&dname, dp);]])
+
dnl lru_cache_add exported in Linux 5.8
dnl replaces lru_cache_add_file
dnl removed in linux 6.1. folio_add_lru is a replacement
diff --git a/src/cf/linux-test4.m4 b/src/cf/linux-test4.m4
index 4155def67..9139f2435 100644
--- a/src/cf/linux-test4.m4
+++ b/src/cf/linux-test4.m4
@@ -855,6 +855,20 @@ AC_DEFUN([LINUX_KEYRING_SEARCH_TAKES_RECURSE], [
[])
])
+dnl Linux 5.2 changed struct name_snapshot.name from a const char* to a
+dnl struct qstr.
+AC_DEFUN([LINUX_STRUCT_NAME_SNAPSHOT_USES_QSTR],[
+ AC_CHECK_LINUX_BUILD([whether struct name_snapshot uses qstr],
+ [ac_cv_linux_name_snapshot_uses_qstr],
+ [[#include <linux/dcache.h>
+ struct qstr *a;
+ struct name_snapshot ns;]],
+ [[if (&ns.name == a) printk("yes\n");]],
+ [[LINUX_STRUCT_NAME_SNAPSHOT_USES_QSTR]],
+ [[define if struct name_snapshot uses qstr]],
+ [[-Werror]])
+])
+
dnl Linux 6.6 added the 'request_mask' parameter to generic_fillattr.
AC_DEFUN([LINUX_GENERIC_FILLATTR_TAKES_REQUEST_MASK], [
AC_CHECK_LINUX_BUILD([whether generic_fillattr has the request_mask parameter],
--
2.51.0
|