diff options
author | Jeff Mahoney <jeffm@suse.com> | 2007-04-23 17:41:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-04-24 11:23:09 -0400 |
commit | 9b7f375505f5611efb562065b57814b28a81abc3 (patch) | |
tree | 3f51e49ca5b70081bf15f4ce9d7ddbd289917932 /fs/reiserfs/xattr.c | |
parent | 1a641fceb6bb6b0930db1aadbda1aaf5711d65d6 (diff) |
reiserfs: fix xattr root locking/refcount bug
The listxattr() and getxattr() operations are only protected by a read
lock. As a result, if either of these operations run in parallel, a race
condition exists where the xattr_root will end up being cached twice, which
results in the leaking of a reference and a BUG() on umount.
This patch refactors get_xa_root(), __get_xa_root(), and create_xa_root(),
into one get_xa_root() function that takes the appropriate locking around
the entire critical section.
Reported, diagnosed and tested by Andrea Righi <a.righi@cineca.it>
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
Cc: Andrea Righi <a.righi@cineca.it>
Cc: "Vladimir V. Saveliev" <vs@namesys.com>
Cc: Edward Shishkin <edward@namesys.com>
Cc: Alex Zarochentsev <zam@namesys.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/reiserfs/xattr.c')
-rw-r--r-- | fs/reiserfs/xattr.c | 92 |
1 files changed, 24 insertions, 68 deletions
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index f01389fd162e..c8178b7b9212 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c | |||
@@ -54,82 +54,48 @@ | |||
54 | static struct reiserfs_xattr_handler *find_xattr_handler_prefix(const char | 54 | static struct reiserfs_xattr_handler *find_xattr_handler_prefix(const char |
55 | *prefix); | 55 | *prefix); |
56 | 56 | ||
57 | static struct dentry *create_xa_root(struct super_block *sb) | 57 | /* Returns the dentry referring to the root of the extended attribute |
58 | * directory tree. If it has already been retrieved, it is used. If it | ||
59 | * hasn't been created and the flags indicate creation is allowed, we | ||
60 | * attempt to create it. On error, we return a pointer-encoded error. | ||
61 | */ | ||
62 | static struct dentry *get_xa_root(struct super_block *sb, int flags) | ||
58 | { | 63 | { |
59 | struct dentry *privroot = dget(REISERFS_SB(sb)->priv_root); | 64 | struct dentry *privroot = dget(REISERFS_SB(sb)->priv_root); |
60 | struct dentry *xaroot; | 65 | struct dentry *xaroot; |
61 | 66 | ||
62 | /* This needs to be created at mount-time */ | 67 | /* This needs to be created at mount-time */ |
63 | if (!privroot) | 68 | if (!privroot) |
64 | return ERR_PTR(-EOPNOTSUPP); | 69 | return ERR_PTR(-ENODATA); |
65 | 70 | ||
66 | xaroot = lookup_one_len(XAROOT_NAME, privroot, strlen(XAROOT_NAME)); | 71 | mutex_lock(&privroot->d_inode->i_mutex); |
67 | if (IS_ERR(xaroot)) { | 72 | if (REISERFS_SB(sb)->xattr_root) { |
73 | xaroot = dget(REISERFS_SB(sb)->xattr_root); | ||
68 | goto out; | 74 | goto out; |
69 | } else if (!xaroot->d_inode) { | ||
70 | int err; | ||
71 | mutex_lock(&privroot->d_inode->i_mutex); | ||
72 | err = | ||
73 | privroot->d_inode->i_op->mkdir(privroot->d_inode, xaroot, | ||
74 | 0700); | ||
75 | mutex_unlock(&privroot->d_inode->i_mutex); | ||
76 | |||
77 | if (err) { | ||
78 | dput(xaroot); | ||
79 | dput(privroot); | ||
80 | return ERR_PTR(err); | ||
81 | } | ||
82 | REISERFS_SB(sb)->xattr_root = dget(xaroot); | ||
83 | } | 75 | } |
84 | 76 | ||
85 | out: | ||
86 | dput(privroot); | ||
87 | return xaroot; | ||
88 | } | ||
89 | |||
90 | /* This will return a dentry, or error, refering to the xa root directory. | ||
91 | * If the xa root doesn't exist yet, the dentry will be returned without | ||
92 | * an associated inode. This dentry can be used with ->mkdir to create | ||
93 | * the xa directory. */ | ||
94 | static struct dentry *__get_xa_root(struct super_block *s) | ||
95 | { | ||
96 | struct dentry *privroot = dget(REISERFS_SB(s)->priv_root); | ||
97 | struct dentry *xaroot = NULL; | ||
98 | |||
99 | if (IS_ERR(privroot) || !privroot) | ||
100 | return privroot; | ||
101 | |||
102 | xaroot = lookup_one_len(XAROOT_NAME, privroot, strlen(XAROOT_NAME)); | 77 | xaroot = lookup_one_len(XAROOT_NAME, privroot, strlen(XAROOT_NAME)); |
103 | if (IS_ERR(xaroot)) { | 78 | if (IS_ERR(xaroot)) { |
104 | goto out; | 79 | goto out; |
105 | } else if (!xaroot->d_inode) { | 80 | } else if (!xaroot->d_inode) { |
106 | dput(xaroot); | 81 | int err = -ENODATA; |
107 | xaroot = NULL; | 82 | if (flags == 0 || flags & XATTR_CREATE) |
108 | goto out; | 83 | err = privroot->d_inode->i_op->mkdir(privroot->d_inode, |
84 | xaroot, 0700); | ||
85 | if (err) { | ||
86 | dput(xaroot); | ||
87 | xaroot = ERR_PTR(err); | ||
88 | goto out; | ||
89 | } | ||
109 | } | 90 | } |
110 | 91 | REISERFS_SB(sb)->xattr_root = dget(xaroot); | |
111 | REISERFS_SB(s)->xattr_root = dget(xaroot); | ||
112 | 92 | ||
113 | out: | 93 | out: |
94 | mutex_unlock(&privroot->d_inode->i_mutex); | ||
114 | dput(privroot); | 95 | dput(privroot); |
115 | return xaroot; | 96 | return xaroot; |
116 | } | 97 | } |
117 | 98 | ||
118 | /* Returns the dentry (or NULL) referring to the root of the extended | ||
119 | * attribute directory tree. If it has already been retrieved, it is used. | ||
120 | * Otherwise, we attempt to retrieve it from disk. It may also return | ||
121 | * a pointer-encoded error. | ||
122 | */ | ||
123 | static inline struct dentry *get_xa_root(struct super_block *s) | ||
124 | { | ||
125 | struct dentry *dentry = dget(REISERFS_SB(s)->xattr_root); | ||
126 | |||
127 | if (!dentry) | ||
128 | dentry = __get_xa_root(s); | ||
129 | |||
130 | return dentry; | ||
131 | } | ||
132 | |||
133 | /* Opens the directory corresponding to the inode's extended attribute store. | 99 | /* Opens the directory corresponding to the inode's extended attribute store. |
134 | * If flags allow, the tree to the directory may be created. If creation is | 100 | * If flags allow, the tree to the directory may be created. If creation is |
135 | * prohibited, -ENODATA is returned. */ | 101 | * prohibited, -ENODATA is returned. */ |
@@ -138,21 +104,11 @@ static struct dentry *open_xa_dir(const struct inode *inode, int flags) | |||
138 | struct dentry *xaroot, *xadir; | 104 | struct dentry *xaroot, *xadir; |
139 | char namebuf[17]; | 105 | char namebuf[17]; |
140 | 106 | ||
141 | xaroot = get_xa_root(inode->i_sb); | 107 | xaroot = get_xa_root(inode->i_sb, flags); |
142 | if (IS_ERR(xaroot)) { | 108 | if (IS_ERR(xaroot)) |
143 | return xaroot; | 109 | return xaroot; |
144 | } else if (!xaroot) { | ||
145 | if (flags == 0 || flags & XATTR_CREATE) { | ||
146 | xaroot = create_xa_root(inode->i_sb); | ||
147 | if (IS_ERR(xaroot)) | ||
148 | return xaroot; | ||
149 | } | ||
150 | if (!xaroot) | ||
151 | return ERR_PTR(-ENODATA); | ||
152 | } | ||
153 | 110 | ||
154 | /* ok, we have xaroot open */ | 111 | /* ok, we have xaroot open */ |
155 | |||
156 | snprintf(namebuf, sizeof(namebuf), "%X.%X", | 112 | snprintf(namebuf, sizeof(namebuf), "%X.%X", |
157 | le32_to_cpu(INODE_PKEY(inode)->k_objectid), | 113 | le32_to_cpu(INODE_PKEY(inode)->k_objectid), |
158 | inode->i_generation); | 114 | inode->i_generation); |
@@ -821,7 +777,7 @@ int reiserfs_delete_xattrs(struct inode *inode) | |||
821 | 777 | ||
822 | /* Leftovers besides . and .. -- that's not good. */ | 778 | /* Leftovers besides . and .. -- that's not good. */ |
823 | if (dir->d_inode->i_nlink <= 2) { | 779 | if (dir->d_inode->i_nlink <= 2) { |
824 | root = get_xa_root(inode->i_sb); | 780 | root = get_xa_root(inode->i_sb, XATTR_REPLACE); |
825 | reiserfs_write_lock_xattrs(inode->i_sb); | 781 | reiserfs_write_lock_xattrs(inode->i_sb); |
826 | err = vfs_rmdir(root->d_inode, dir); | 782 | err = vfs_rmdir(root->d_inode, dir); |
827 | reiserfs_write_unlock_xattrs(inode->i_sb); | 783 | reiserfs_write_unlock_xattrs(inode->i_sb); |