diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-10 12:19:19 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-10 19:54:09 -0700 |
commit | 94753db5ed9ad97582ef453127d9626a7a2be602 (patch) | |
tree | ebf9b6cce17c3f4348a598988cb398a9807b3db7 | |
parent | 8c01a529b861ba97c7d78368e6a5d4d42e946f75 (diff) |
vfs: do the careful dentry name access for all dentry_cmp cases
Commit 12f8ad4b0533 ("vfs: clean up __d_lookup_rcu() and dentry_cmp()
interfaces") did the careful ACCESS_ONCE() of the dentry name only for
the word-at-a-time case, even though the issue is generic.
Admittedly I don't really see gcc ever reloading the value in the middle
of the loop, so the ACCESS_ONCE() protects us from a fairly theoretical
issue. But better safe than sorry.
Also, this consolidates the common parts of the word-at-a-time and
bytewise logic, which includes checking the length. We'll be changing
that later.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/dcache.c | 54 |
1 files changed, 26 insertions, 28 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index c4d2ff8b4912..5c09ad7b4a15 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -153,30 +153,9 @@ int proc_nr_dentry(ctl_table *table, int write, void __user *buffer, * In contrast, 'ct' and 'tcount' can be from a pathname, and do * need the careful unaligned handling. */ -static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount) +static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char *ct, unsigned tcount) { unsigned long a,b,mask; - const unsigned char *cs; - - if (unlikely(dentry->d_name.len != tcount)) - return 1; - /* - * Be careful about RCU walk racing with rename: - * use ACCESS_ONCE to fetch the name pointer. - * - * NOTE! Even if a rename will mean that the length - * was not loaded atomically, we don't care. The - * RCU walk will check the sequence count eventually, - * and catch it. And we won't overrun the buffer, - * because we're reading the name pointer atomically, - * and a dentry name is guaranteed to be properly - * terminated with a NUL byte. - * - * End result: even if 'len' is wrong, we'll exit - * early because the data cannot match (there can - * be no NUL in the ct/tcount data) - */ - cs = ACCESS_ONCE(dentry->d_name.name); for (;;) { a = *(unsigned long *)cs; @@ -197,13 +176,8 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c #else -static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount) +static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char *ct, unsigned tcount) { - const unsigned char *cs = dentry->d_name.name; - - if (dentry->d_name.len != tcount) - return 1; - do { if (*cs != *ct) return 1; @@ -216,6 +190,30 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c #endif +static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount) +{ + if (dentry->d_name.len != tcount) + return 1; + + /* + * Be careful about RCU walk racing with rename: + * use ACCESS_ONCE to fetch the name pointer. + * + * NOTE! Even if a rename will mean that the length + * was not loaded atomically, we don't care. The + * RCU walk will check the sequence count eventually, + * and catch it. And we won't overrun the buffer, + * because we're reading the name pointer atomically, + * and a dentry name is guaranteed to be properly + * terminated with a NUL byte. + * + * End result: even if 'len' is wrong, we'll exit + * early because the data cannot match (there can + * be no NUL in the ct/tcount data) + */ + return dentry_string_cmp(ACCESS_ONCE(dentry->d_name.name), ct, tcount); +} + static void __d_free(struct rcu_head *head) { struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); |