Skip to content

Commit 407669f

Browse files
Al Virogregkh
authored andcommitted
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>
1 parent 9909e61 commit 407669f

6 files changed

Lines changed: 48 additions & 42 deletions

File tree

fs/dcache.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,33 @@ static inline int dname_external(const struct dentry *dentry)
269269
return dentry->d_name.name != dentry->d_iname;
270270
}
271271

272+
void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
273+
{
274+
spin_lock(&dentry->d_lock);
275+
if (unlikely(dname_external(dentry))) {
276+
struct external_name *p = external_name(dentry);
277+
atomic_inc(&p->u.count);
278+
spin_unlock(&dentry->d_lock);
279+
name->name = p->name;
280+
} else {
281+
memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
282+
spin_unlock(&dentry->d_lock);
283+
name->name = name->inline_name;
284+
}
285+
}
286+
EXPORT_SYMBOL(take_dentry_name_snapshot);
287+
288+
void release_dentry_name_snapshot(struct name_snapshot *name)
289+
{
290+
if (unlikely(name->name != name->inline_name)) {
291+
struct external_name *p;
292+
p = container_of(name->name, struct external_name, name[0]);
293+
if (unlikely(atomic_dec_and_test(&p->u.count)))
294+
kfree_rcu(p, u.head);
295+
}
296+
}
297+
EXPORT_SYMBOL(release_dentry_name_snapshot);
298+
272299
static inline void __d_set_inode_and_type(struct dentry *dentry,
273300
struct inode *inode,
274301
unsigned type_flags)

fs/debugfs/inode.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
669669
{
670670
int error;
671671
struct dentry *dentry = NULL, *trap;
672-
const char *old_name;
672+
struct name_snapshot old_name;
673673

674674
trap = lock_rename(new_dir, old_dir);
675675
/* Source or destination directories don't exist? */
@@ -684,19 +684,19 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
684684
if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
685685
goto exit;
686686

687-
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
687+
take_dentry_name_snapshot(&old_name, old_dentry);
688688

689689
error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
690690
dentry);
691691
if (error) {
692-
fsnotify_oldname_free(old_name);
692+
release_dentry_name_snapshot(&old_name);
693693
goto exit;
694694
}
695695
d_move(old_dentry, dentry);
696-
fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name,
696+
fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
697697
d_is_dir(old_dentry),
698698
NULL, old_dentry);
699-
fsnotify_oldname_free(old_name);
699+
release_dentry_name_snapshot(&old_name);
700700
unlock_rename(new_dir, old_dir);
701701
dput(dentry);
702702
return old_dentry;

fs/namei.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4179,11 +4179,11 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
41794179
{
41804180
int error;
41814181
bool is_dir = d_is_dir(old_dentry);
4182-
const unsigned char *old_name;
41834182
struct inode *source = old_dentry->d_inode;
41844183
struct inode *target = new_dentry->d_inode;
41854184
bool new_is_dir = false;
41864185
unsigned max_links = new_dir->i_sb->s_max_links;
4186+
struct name_snapshot old_name;
41874187

41884188
/*
41894189
* Check source == target.
@@ -4237,7 +4237,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
42374237
if (error)
42384238
return error;
42394239

4240-
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
4240+
take_dentry_name_snapshot(&old_name, old_dentry);
42414241
dget(new_dentry);
42424242
if (!is_dir || (flags & RENAME_EXCHANGE))
42434243
lock_two_nondirectories(source, target);
@@ -4298,14 +4298,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
42984298
mutex_unlock(&target->i_mutex);
42994299
dput(new_dentry);
43004300
if (!error) {
4301-
fsnotify_move(old_dir, new_dir, old_name, is_dir,
4301+
fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
43024302
!(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
43034303
if (flags & RENAME_EXCHANGE) {
43044304
fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
43054305
new_is_dir, NULL, new_dentry);
43064306
}
43074307
}
4308-
fsnotify_oldname_free(old_name);
4308+
release_dentry_name_snapshot(&old_name);
43094309

43104310
return error;
43114311
}

fs/notify/fsnotify.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,20 @@ int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
104104
if (unlikely(!fsnotify_inode_watches_children(p_inode)))
105105
__fsnotify_update_child_dentry_flags(p_inode);
106106
else if (p_inode->i_fsnotify_mask & mask) {
107+
struct name_snapshot name;
108+
107109
/* we are notifying a parent so come up with the new mask which
108110
* specifies these are events which came from a child. */
109111
mask |= FS_EVENT_ON_CHILD;
110112

113+
take_dentry_name_snapshot(&name, dentry);
111114
if (path)
112115
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
113-
dentry->d_name.name, 0);
116+
name.name, 0);
114117
else
115118
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);
117121
}
118122

119123
dput(parent);

include/linux/dcache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,5 +615,11 @@ static inline struct inode *d_real_inode(struct dentry *dentry)
615615
return d_backing_inode(d_real(dentry));
616616
}
617617

618+
struct name_snapshot {
619+
const char *name;
620+
char inline_name[DNAME_INLINE_LEN];
621+
};
622+
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
623+
void release_dentry_name_snapshot(struct name_snapshot *);
618624

619625
#endif /* __LINUX_DCACHE_H */

include/linux/fsnotify.h

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -310,35 +310,4 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
310310
}
311311
}
312312

313-
#if defined(CONFIG_FSNOTIFY) /* notify helpers */
314-
315-
/*
316-
* fsnotify_oldname_init - save off the old filename before we change it
317-
*/
318-
static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name)
319-
{
320-
return kstrdup(name, GFP_KERNEL);
321-
}
322-
323-
/*
324-
* fsnotify_oldname_free - free the name we got from fsnotify_oldname_init
325-
*/
326-
static inline void fsnotify_oldname_free(const unsigned char *old_name)
327-
{
328-
kfree(old_name);
329-
}
330-
331-
#else /* CONFIG_FSNOTIFY */
332-
333-
static inline const char *fsnotify_oldname_init(const unsigned char *name)
334-
{
335-
return NULL;
336-
}
337-
338-
static inline void fsnotify_oldname_free(const unsigned char *old_name)
339-
{
340-
}
341-
342-
#endif /* CONFIG_FSNOTIFY */
343-
344313
#endif /* _LINUX_FS_NOTIFY_H */

0 commit comments

Comments
 (0)