apparmor/kernel-patches/for-mainline/mount-consistent-d_path.diff

59 lines
1.9 KiB
Diff

Make d_path() consistent across mount operations
Right now, the path that __d_path() computes can become slightly
inconsistent when it races with mount operations: it grabs the
vfsmount_lock when traversing mount points, but immediately drops the
lock again, only to re-grab it when it reaches the next mount point.
The result is that the filename computed is not always consisent, and
the file may never have had that name. (This is extremely unlikely, but
still possible.)
This can easily be fixed by grabbing the vfsmount_lock when the first
mount point is reached, and holding onto it until the d_cache lookup is
completed. As a bonus, this makes __d_path() slightly more efficient
when traversing a number of mount points.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Index: b/fs/dcache.c
===================================================================
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1754,7 +1754,7 @@ static char *__d_path(struct dentry *den
struct dentry *root, struct vfsmount *rootmnt,
char *buffer, int buflen, int fail_deleted)
{
- int namelen, is_slash;
+ int namelen, is_slash, vfsmount_locked = 0;
if (buflen < 2)
return ERR_PTR(-ENAMETOOLONG);
@@ -1777,14 +1777,14 @@ static char *__d_path(struct dentry *den
struct dentry * parent;
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
- spin_lock(&vfsmount_lock);
- if (vfsmnt->mnt_parent == vfsmnt) {
- spin_unlock(&vfsmount_lock);
- goto global_root;
+ if (!vfsmount_locked) {
+ spin_lock(&vfsmount_lock);
+ vfsmount_locked = 1;
}
+ if (vfsmnt->mnt_parent == vfsmnt)
+ goto global_root;
dentry = vfsmnt->mnt_mountpoint;
vfsmnt = vfsmnt->mnt_parent;
- spin_unlock(&vfsmount_lock);
continue;
}
parent = dentry->d_parent;
@@ -1803,6 +1803,8 @@ static char *__d_path(struct dentry *den
*--buffer = '/';
out:
+ if (vfsmount_locked)
+ spin_unlock(&vfsmount_lock);
spin_unlock(&dcache_lock);
return buffer;