Commit ad25f11ed216d5ce3b5566b2f187b59fa3061b40

Authored by Al Viro
Committed by Greg Kroah-Hartman
1 parent d933777b1b

dentry name snapshots

commit 49d31c2f389acfe83417083e1208422b4091cd9e upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 6 changed files with 48 additions and 42 deletions Side-by-side Diff

... ... @@ -277,6 +277,33 @@
277 277 return dentry->d_name.name != dentry->d_iname;
278 278 }
279 279  
  280 +void 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 +}
  294 +EXPORT_SYMBOL(take_dentry_name_snapshot);
  295 +
  296 +void 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 +}
  305 +EXPORT_SYMBOL(release_dentry_name_snapshot);
  306 +
280 307 static inline void __d_set_inode_and_type(struct dentry *dentry,
281 308 struct inode *inode,
282 309 unsigned type_flags)
... ... @@ -730,7 +730,7 @@
730 730 {
731 731 int error;
732 732 struct dentry *dentry = NULL, *trap;
733   - const char *old_name;
  733 + struct name_snapshot old_name;
734 734  
735 735 trap = lock_rename(new_dir, old_dir);
736 736 /* Source or destination directories don't exist? */
737 737  
738 738  
739 739  
... ... @@ -745,19 +745,19 @@
745 745 if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
746 746 goto exit;
747 747  
748   - old_name = fsnotify_oldname_init(old_dentry->d_name.name);
  748 + take_dentry_name_snapshot(&old_name, old_dentry);
749 749  
750 750 error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
751 751 dentry, 0);
752 752 if (error) {
753   - fsnotify_oldname_free(old_name);
  753 + release_dentry_name_snapshot(&old_name);
754 754 goto exit;
755 755 }
756 756 d_move(old_dentry, dentry);
757   - fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name,
  757 + fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
758 758 d_is_dir(old_dentry),
759 759 NULL, old_dentry);
760   - fsnotify_oldname_free(old_name);
  760 + release_dentry_name_snapshot(&old_name);
761 761 unlock_rename(new_dir, old_dir);
762 762 dput(dentry);
763 763 return old_dentry;
... ... @@ -4336,11 +4336,11 @@
4336 4336 {
4337 4337 int error;
4338 4338 bool is_dir = d_is_dir(old_dentry);
4339   - const unsigned char *old_name;
4340 4339 struct inode *source = old_dentry->d_inode;
4341 4340 struct inode *target = new_dentry->d_inode;
4342 4341 bool new_is_dir = false;
4343 4342 unsigned max_links = new_dir->i_sb->s_max_links;
  4343 + struct name_snapshot old_name;
4344 4344  
4345 4345 /*
4346 4346 * Check source == target.
... ... @@ -4391,7 +4391,7 @@
4391 4391 if (error)
4392 4392 return error;
4393 4393  
4394   - old_name = fsnotify_oldname_init(old_dentry->d_name.name);
  4394 + take_dentry_name_snapshot(&old_name, old_dentry);
4395 4395 dget(new_dentry);
4396 4396 if (!is_dir || (flags & RENAME_EXCHANGE))
4397 4397 lock_two_nondirectories(source, target);
4398 4398  
... ... @@ -4446,14 +4446,14 @@
4446 4446 inode_unlock(target);
4447 4447 dput(new_dentry);
4448 4448 if (!error) {
4449   - fsnotify_move(old_dir, new_dir, old_name, is_dir,
  4449 + fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
4450 4450 !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
4451 4451 if (flags & RENAME_EXCHANGE) {
4452 4452 fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
4453 4453 new_is_dir, NULL, new_dentry);
4454 4454 }
4455 4455 }
4456   - fsnotify_oldname_free(old_name);
  4456 + release_dentry_name_snapshot(&old_name);
4457 4457  
4458 4458 return error;
4459 4459 }
fs/notify/fsnotify.c
... ... @@ -104,16 +104,20 @@
104 104 if (unlikely(!fsnotify_inode_watches_children(p_inode)))
105 105 __fsnotify_update_child_dentry_flags(p_inode);
106 106 else if (p_inode->i_fsnotify_mask & mask) {
  107 + struct name_snapshot name;
  108 +
107 109 /* we are notifying a parent so come up with the new mask which
108 110 * specifies these are events which came from a child. */
109 111 mask |= FS_EVENT_ON_CHILD;
110 112  
  113 + take_dentry_name_snapshot(&name, dentry);
111 114 if (path)
112 115 ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
113   - dentry->d_name.name, 0);
  116 + name.name, 0);
114 117 else
115 118 ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
116   - dentry->d_name.name, 0);
  119 + name.name, 0);
  120 + release_dentry_name_snapshot(&name);
117 121 }
118 122  
119 123 dput(parent);
include/linux/dcache.h
... ... @@ -590,6 +590,12 @@
590 590 return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0));
591 591 }
592 592  
  593 +struct name_snapshot {
  594 + const char *name;
  595 + char inline_name[DNAME_INLINE_LEN];
  596 +};
  597 +void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
  598 +void release_dentry_name_snapshot(struct name_snapshot *);
593 599  
594 600 #endif /* __LINUX_DCACHE_H */
include/linux/fsnotify.h
... ... @@ -293,36 +293,5 @@
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   - */
301   -static 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   - */
309   -static inline void fsnotify_oldname_free(const unsigned char *old_name)
310   -{
311   - kfree(old_name);
312   -}
313   -
314   -#else /* CONFIG_FSNOTIFY */
315   -
316   -static inline const char *fsnotify_oldname_init(const unsigned char *name)
317   -{
318   - return NULL;
319   -}
320   -
321   -static inline void fsnotify_oldname_free(const unsigned char *old_name)
322   -{
323   -}
324   -
325   -#endif /* CONFIG_FSNOTIFY */
326   -
327 296 #endif /* _LINUX_FS_NOTIFY_H */