diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 92099f61bc64..a7675e0109f0 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -192,6 +192,7 @@ static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char | |||
192 | 192 | ||
193 | static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount) | 193 | static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount) |
194 | { | 194 | { |
195 | const unsigned char *cs; | ||
195 | /* | 196 | /* |
196 | * Be careful about RCU walk racing with rename: | 197 | * Be careful about RCU walk racing with rename: |
197 | * use ACCESS_ONCE to fetch the name pointer. | 198 | * use ACCESS_ONCE to fetch the name pointer. |
@@ -208,7 +209,9 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c | |||
208 | * early because the data cannot match (there can | 209 | * early because the data cannot match (there can |
209 | * be no NUL in the ct/tcount data) | 210 | * be no NUL in the ct/tcount data) |
210 | */ | 211 | */ |
211 | return dentry_string_cmp(ACCESS_ONCE(dentry->d_name.name), ct, tcount); | 212 | cs = ACCESS_ONCE(dentry->d_name.name); |
213 | smp_read_barrier_depends(); | ||
214 | return dentry_string_cmp(cs, ct, tcount); | ||
212 | } | 215 | } |
213 | 216 | ||
214 | static void __d_free(struct rcu_head *head) | 217 | static void __d_free(struct rcu_head *head) |
@@ -1271,6 +1274,13 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) | |||
1271 | if (!dentry) | 1274 | if (!dentry) |
1272 | return NULL; | 1275 | return NULL; |
1273 | 1276 | ||
1277 | /* | ||
1278 | * We guarantee that the inline name is always NUL-terminated. | ||
1279 | * This way the memcpy() done by the name switching in rename | ||
1280 | * will still always have a NUL at the end, even if we might | ||
1281 | * be overwriting an internal NUL character | ||
1282 | */ | ||
1283 | dentry->d_iname[DNAME_INLINE_LEN-1] = 0; | ||
1274 | if (name->len > DNAME_INLINE_LEN-1) { | 1284 | if (name->len > DNAME_INLINE_LEN-1) { |
1275 | dname = kmalloc(name->len + 1, GFP_KERNEL); | 1285 | dname = kmalloc(name->len + 1, GFP_KERNEL); |
1276 | if (!dname) { | 1286 | if (!dname) { |
@@ -1280,13 +1290,16 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) | |||
1280 | } else { | 1290 | } else { |
1281 | dname = dentry->d_iname; | 1291 | dname = dentry->d_iname; |
1282 | } | 1292 | } |
1283 | dentry->d_name.name = dname; | ||
1284 | 1293 | ||
1285 | dentry->d_name.len = name->len; | 1294 | dentry->d_name.len = name->len; |
1286 | dentry->d_name.hash = name->hash; | 1295 | dentry->d_name.hash = name->hash; |
1287 | memcpy(dname, name->name, name->len); | 1296 | memcpy(dname, name->name, name->len); |
1288 | dname[name->len] = 0; | 1297 | dname[name->len] = 0; |
1289 | 1298 | ||
1299 | /* Make sure we always see the terminating NUL character */ | ||
1300 | smp_wmb(); | ||
1301 | dentry->d_name.name = dname; | ||
1302 | |||
1290 | dentry->d_count = 1; | 1303 | dentry->d_count = 1; |
1291 | dentry->d_flags = 0; | 1304 | dentry->d_flags = 0; |
1292 | spin_lock_init(&dentry->d_lock); | 1305 | spin_lock_init(&dentry->d_lock); |