aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2017-07-07 14:51:19 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2017-07-07 20:09:10 -0400
commit49d31c2f389acfe83417083e1208422b4091cd9e (patch)
treeb4ab96fd8300dd73a398a7d587f8839fa052c03b
parenta8e2b6367794e6cee9eecba6d5ff425f338e0754 (diff)
dentry name snapshots
take_dentry_name_snapshot() takes a safe snapshot of dentry name; if the name is a short one, it gets copied into caller-supplied structure, otherwise an extra reference to external name is grabbed (those are never modified). In either case the pointer to stable string is stored into the same structure. dentry must be held by the caller of take_dentry_name_snapshot(), but may be freely dropped afterwards - the snapshot will stay until destroyed by release_dentry_name_snapshot(). Intended use: struct name_snapshot s; take_dentry_name_snapshot(&s, dentry); ... access s.name ... release_dentry_name_snapshot(&s); Replaces fsnotify_oldname_...(), gets used in fsnotify to obtain the name to pass down with event. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/dcache.c27
-rw-r--r--fs/debugfs/inode.c10
-rw-r--r--fs/namei.c8
-rw-r--r--fs/notify/fsnotify.c8
-rw-r--r--include/linux/dcache.h6
-rw-r--r--include/linux/fsnotify.h31
6 files changed, 48 insertions, 42 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index b85da8897ffa..831f3a9a8f05 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -277,6 +277,33 @@ static inline int dname_external(const struct dentry *dentry)
277 return dentry->d_name.name != dentry->d_iname; 277 return dentry->d_name.name != dentry->d_iname;
278} 278}
279 279
280void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
281{
282 spin_lock(&dentry->d_lock);
283 if (unlikely(dname_external(dentry))) {
284 struct external_name *p = external_name(dentry);
285 atomic_inc(&p->u.count);
286 spin_unlock(&dentry->d_lock);
287 name->name = p->name;
288 } else {
289 memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
290 spin_unlock(&dentry->d_lock);
291 name->name = name->inline_name;
292 }
293}
294EXPORT_SYMBOL(take_dentry_name_snapshot);
295
296void release_dentry_name_snapshot(struct name_snapshot *name)
297{
298 if (unlikely(name->name != name->inline_name)) {
299 struct external_name *p;
300 p = container_of(name->name, struct external_name, name[0]);
301 if (unlikely(atomic_dec_and_test(&p->u.count)))
302 kfree_rcu(p, u.head);
303 }
304}
305EXPORT_SYMBOL(release_dentry_name_snapshot);
306
280static inline void __d_set_inode_and_type(struct dentry *dentry, 307static inline void __d_set_inode_and_type(struct dentry *dentry,
281 struct inode *inode, 308 struct inode *inode,
282 unsigned type_flags) 309 unsigned type_flags)
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index e892ae7d89f8..acd3be2cc691 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -766,7 +766,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
766{ 766{
767 int error; 767 int error;
768 struct dentry *dentry = NULL, *trap; 768 struct dentry *dentry = NULL, *trap;
769 const char *old_name; 769 struct name_snapshot old_name;
770 770
771 trap = lock_rename(new_dir, old_dir); 771 trap = lock_rename(new_dir, old_dir);
772 /* Source or destination directories don't exist? */ 772 /* Source or destination directories don't exist? */
@@ -781,19 +781,19 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
781 if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry)) 781 if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
782 goto exit; 782 goto exit;
783 783
784 old_name = fsnotify_oldname_init(old_dentry->d_name.name); 784 take_dentry_name_snapshot(&old_name, old_dentry);
785 785
786 error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir), 786 error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
787 dentry, 0); 787 dentry, 0);
788 if (error) { 788 if (error) {
789 fsnotify_oldname_free(old_name); 789 release_dentry_name_snapshot(&old_name);
790 goto exit; 790 goto exit;
791 } 791 }
792 d_move(old_dentry, dentry); 792 d_move(old_dentry, dentry);
793 fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name, 793 fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
794 d_is_dir(old_dentry), 794 d_is_dir(old_dentry),
795 NULL, old_dentry); 795 NULL, old_dentry);
796 fsnotify_oldname_free(old_name); 796 release_dentry_name_snapshot(&old_name);
797 unlock_rename(new_dir, old_dir); 797 unlock_rename(new_dir, old_dir);
798 dput(dentry); 798 dput(dentry);
799 return old_dentry; 799 return old_dentry;
diff --git a/fs/namei.c b/fs/namei.c
index efe53a5d0737..c5588e837b15 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4362,11 +4362,11 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
4362{ 4362{
4363 int error; 4363 int error;
4364 bool is_dir = d_is_dir(old_dentry); 4364 bool is_dir = d_is_dir(old_dentry);
4365 const unsigned char *old_name;
4366 struct inode *source = old_dentry->d_inode; 4365 struct inode *source = old_dentry->d_inode;
4367 struct inode *target = new_dentry->d_inode; 4366 struct inode *target = new_dentry->d_inode;
4368 bool new_is_dir = false; 4367 bool new_is_dir = false;
4369 unsigned max_links = new_dir->i_sb->s_max_links; 4368 unsigned max_links = new_dir->i_sb->s_max_links;
4369 struct name_snapshot old_name;
4370 4370
4371 if (source == target) 4371 if (source == target)
4372 return 0; 4372 return 0;
@@ -4413,7 +4413,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
4413 if (error) 4413 if (error)
4414 return error; 4414 return error;
4415 4415
4416 old_name = fsnotify_oldname_init(old_dentry->d_name.name); 4416 take_dentry_name_snapshot(&old_name, old_dentry);
4417 dget(new_dentry); 4417 dget(new_dentry);
4418 if (!is_dir || (flags & RENAME_EXCHANGE)) 4418 if (!is_dir || (flags & RENAME_EXCHANGE))
4419 lock_two_nondirectories(source, target); 4419 lock_two_nondirectories(source, target);
@@ -4468,14 +4468,14 @@ out:
4468 inode_unlock(target); 4468 inode_unlock(target);
4469 dput(new_dentry); 4469 dput(new_dentry);
4470 if (!error) { 4470 if (!error) {
4471 fsnotify_move(old_dir, new_dir, old_name, is_dir, 4471 fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
4472 !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry); 4472 !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
4473 if (flags & RENAME_EXCHANGE) { 4473 if (flags & RENAME_EXCHANGE) {
4474 fsnotify_move(new_dir, old_dir, old_dentry->d_name.name, 4474 fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
4475 new_is_dir, NULL, new_dentry); 4475 new_is_dir, NULL, new_dentry);
4476 } 4476 }
4477 } 4477 }
4478 fsnotify_oldname_free(old_name); 4478 release_dentry_name_snapshot(&old_name);
4479 4479
4480 return error; 4480 return error;
4481} 4481}
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 01a9f0f007d4..0c4583b61717 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -161,16 +161,20 @@ int __fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask
161 if (unlikely(!fsnotify_inode_watches_children(p_inode))) 161 if (unlikely(!fsnotify_inode_watches_children(p_inode)))
162 __fsnotify_update_child_dentry_flags(p_inode); 162 __fsnotify_update_child_dentry_flags(p_inode);
163 else if (p_inode->i_fsnotify_mask & mask) { 163 else if (p_inode->i_fsnotify_mask & mask) {
164 struct name_snapshot name;
165
164 /* we are notifying a parent so come up with the new mask which 166 /* we are notifying a parent so come up with the new mask which
165 * specifies these are events which came from a child. */ 167 * specifies these are events which came from a child. */
166 mask |= FS_EVENT_ON_CHILD; 168 mask |= FS_EVENT_ON_CHILD;
167 169
170 take_dentry_name_snapshot(&name, dentry);
168 if (path) 171 if (path)
169 ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, 172 ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
170 dentry->d_name.name, 0); 173 name.name, 0);
171 else 174 else
172 ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, 175 ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
173 dentry->d_name.name, 0); 176 name.name, 0);
177 release_dentry_name_snapshot(&name);
174 } 178 }
175 179
176 dput(parent); 180 dput(parent);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d2e38dc6172c..025727bf6797 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -591,5 +591,11 @@ static inline struct inode *d_real_inode(const struct dentry *dentry)
591 return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0)); 591 return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0));
592} 592}
593 593
594struct name_snapshot {
595 const char *name;
596 char inline_name[DNAME_INLINE_LEN];
597};
598void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
599void release_dentry_name_snapshot(struct name_snapshot *);
594 600
595#endif /* __LINUX_DCACHE_H */ 601#endif /* __LINUX_DCACHE_H */
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index b43d3f5bd9ea..b78aa7ac77ce 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -293,35 +293,4 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
293 } 293 }
294} 294}
295 295
296#if defined(CONFIG_FSNOTIFY) /* notify helpers */
297
298/*
299 * fsnotify_oldname_init - save off the old filename before we change it
300 */
301static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name)
302{
303 return kstrdup(name, GFP_KERNEL);
304}
305
306/*
307 * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init
308 */
309static inline void fsnotify_oldname_free(const unsigned char *old_name)
310{
311 kfree(old_name);
312}
313
314#else /* CONFIG_FSNOTIFY */
315
316static inline const char *fsnotify_oldname_init(const unsigned char *name)
317{
318 return NULL;
319}
320
321static inline void fsnotify_oldname_free(const unsigned char *old_name)
322{
323}
324
325#endif /* CONFIG_FSNOTIFY */
326
327#endif /* _LINUX_FS_NOTIFY_H */ 296#endif /* _LINUX_FS_NOTIFY_H */