diff options
author | Dongsheng Yang <yangds.fnst@cn.fujitsu.com> | 2015-08-18 00:38:36 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2015-11-07 05:33:17 -0500 |
commit | ab92a20bce3b4c21927cc1ae514c482f50ad3487 (patch) | |
tree | 5979820c9a40bf54a7542a3df9f75c0482aea70b | |
parent | 54bcfdf19ec7c2f2788fa76426c91abdc4dab973 (diff) |
ubifs: make ubifs_[get|set]xattr atomic
This commit make the ubifs_[get|set]xattr protected by ui_mutex.
Originally, there is a possibility that ubifs_getxattr to get
a wrong value.
P1 P2
---------- ----------
ubifs_getxattr ubifs_setxattr
- kfree()
- memcpy()
- kmemdup()
Then ubifs_getxattr() would get a non-sense data. To solve this
problem, this commit make the xattr of ubifs_inode updated in
atomic.
Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
-rw-r--r-- | fs/ubifs/xattr.c | 12 |
1 files changed, 9 insertions, 3 deletions
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 96f3448b6eb4..99364aeed035 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c | |||
@@ -200,6 +200,7 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, | |||
200 | int err; | 200 | int err; |
201 | struct ubifs_inode *host_ui = ubifs_inode(host); | 201 | struct ubifs_inode *host_ui = ubifs_inode(host); |
202 | struct ubifs_inode *ui = ubifs_inode(inode); | 202 | struct ubifs_inode *ui = ubifs_inode(inode); |
203 | void *buf = NULL; | ||
203 | struct ubifs_budget_req req = { .dirtied_ino = 2, | 204 | struct ubifs_budget_req req = { .dirtied_ino = 2, |
204 | .dirtied_ino_d = ALIGN(size, 8) + ALIGN(host_ui->data_len, 8) }; | 205 | .dirtied_ino_d = ALIGN(size, 8) + ALIGN(host_ui->data_len, 8) }; |
205 | 206 | ||
@@ -208,14 +209,17 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, | |||
208 | if (err) | 209 | if (err) |
209 | return err; | 210 | return err; |
210 | 211 | ||
211 | kfree(ui->data); | 212 | buf = kmemdup(value, size, GFP_NOFS); |
212 | ui->data = kmemdup(value, size, GFP_NOFS); | 213 | if (!buf) { |
213 | if (!ui->data) { | ||
214 | err = -ENOMEM; | 214 | err = -ENOMEM; |
215 | goto out_free; | 215 | goto out_free; |
216 | } | 216 | } |
217 | mutex_lock(&ui->ui_mutex); | ||
218 | kfree(ui->data); | ||
219 | ui->data = buf; | ||
217 | inode->i_size = ui->ui_size = size; | 220 | inode->i_size = ui->ui_size = size; |
218 | ui->data_len = size; | 221 | ui->data_len = size; |
222 | mutex_unlock(&ui->ui_mutex); | ||
219 | 223 | ||
220 | mutex_lock(&host_ui->ui_mutex); | 224 | mutex_lock(&host_ui->ui_mutex); |
221 | host->i_ctime = ubifs_current_time(host); | 225 | host->i_ctime = ubifs_current_time(host); |
@@ -409,6 +413,7 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, | |||
409 | ubifs_assert(inode->i_size == ui->data_len); | 413 | ubifs_assert(inode->i_size == ui->data_len); |
410 | ubifs_assert(ubifs_inode(host)->xattr_size > ui->data_len); | 414 | ubifs_assert(ubifs_inode(host)->xattr_size > ui->data_len); |
411 | 415 | ||
416 | mutex_lock(&ui->ui_mutex); | ||
412 | if (buf) { | 417 | if (buf) { |
413 | /* If @buf is %NULL we are supposed to return the length */ | 418 | /* If @buf is %NULL we are supposed to return the length */ |
414 | if (ui->data_len > size) { | 419 | if (ui->data_len > size) { |
@@ -423,6 +428,7 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, | |||
423 | err = ui->data_len; | 428 | err = ui->data_len; |
424 | 429 | ||
425 | out_iput: | 430 | out_iput: |
431 | mutex_unlock(&ui->ui_mutex); | ||
426 | iput(inode); | 432 | iput(inode); |
427 | out_unlock: | 433 | out_unlock: |
428 | kfree(xent); | 434 | kfree(xent); |