UBIFS: xattr bugfixes
Xattr code has not been tested for a while and there were serveral bugs. One of them is using wrong inode in 'ubifs_jnl_change_xattr()'. The other is a deadlock in 'ubifs_setxattr()': the i_mutex is locked in 'cap_inode_need_killpriv()' path, so deadlock happens when 'ubifs_setxattr()' tries to lock it again. Thanks to Zoltan Sogor for finding these bugs. Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
This commit is contained in:
@@ -1374,7 +1374,7 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
|
|||||||
const struct inode *host)
|
const struct inode *host)
|
||||||
{
|
{
|
||||||
int err, len1, len2, aligned_len, aligned_len1, lnum, offs;
|
int err, len1, len2, aligned_len, aligned_len1, lnum, offs;
|
||||||
struct ubifs_inode *host_ui = ubifs_inode(inode);
|
struct ubifs_inode *host_ui = ubifs_inode(host);
|
||||||
struct ubifs_ino_node *ino;
|
struct ubifs_ino_node *ino;
|
||||||
union ubifs_key key;
|
union ubifs_key key;
|
||||||
int sync = IS_DIRSYNC(host);
|
int sync = IS_DIRSYNC(host);
|
||||||
|
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Limit the number of extended attributes per inode so that the total size
|
* Limit the number of extended attributes per inode so that the total size
|
||||||
* (xattr_size) is guaranteeded to fit in an 'unsigned int'.
|
* (@xattr_size) is guaranteeded to fit in an 'unsigned int'.
|
||||||
*/
|
*/
|
||||||
#define MAX_XATTRS_PER_INODE 65535
|
#define MAX_XATTRS_PER_INODE 65535
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
|
|||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
/*
|
/*
|
||||||
* Linux limits the maximum size of the extended attribute names list
|
* Linux limits the maximum size of the extended attribute names list
|
||||||
* to %XATTR_LIST_MAX. This means we should not allow creating more*
|
* to %XATTR_LIST_MAX. This means we should not allow creating more
|
||||||
* extended attributes if the name list becomes larger. This limitation
|
* extended attributes if the name list becomes larger. This limitation
|
||||||
* is artificial for UBIFS, though.
|
* is artificial for UBIFS, though.
|
||||||
*/
|
*/
|
||||||
@@ -128,7 +128,6 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
|
|||||||
goto out_budg;
|
goto out_budg;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&host_ui->ui_mutex);
|
|
||||||
/* Re-define all operations to be "nothing" */
|
/* Re-define all operations to be "nothing" */
|
||||||
inode->i_mapping->a_ops = &none_address_operations;
|
inode->i_mapping->a_ops = &none_address_operations;
|
||||||
inode->i_op = &none_inode_operations;
|
inode->i_op = &none_inode_operations;
|
||||||
@@ -141,23 +140,19 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
|
|||||||
ui->data = kmalloc(size, GFP_NOFS);
|
ui->data = kmalloc(size, GFP_NOFS);
|
||||||
if (!ui->data) {
|
if (!ui->data) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out_unlock;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(ui->data, value, size);
|
memcpy(ui->data, value, size);
|
||||||
|
inode->i_size = ui->ui_size = size;
|
||||||
|
ui->data_len = size;
|
||||||
|
|
||||||
|
mutex_lock(&host_ui->ui_mutex);
|
||||||
host->i_ctime = ubifs_current_time(host);
|
host->i_ctime = ubifs_current_time(host);
|
||||||
host_ui->xattr_cnt += 1;
|
host_ui->xattr_cnt += 1;
|
||||||
host_ui->xattr_size += CALC_DENT_SIZE(nm->len);
|
host_ui->xattr_size += CALC_DENT_SIZE(nm->len);
|
||||||
host_ui->xattr_size += CALC_XATTR_BYTES(size);
|
host_ui->xattr_size += CALC_XATTR_BYTES(size);
|
||||||
host_ui->xattr_names += nm->len;
|
host_ui->xattr_names += nm->len;
|
||||||
|
|
||||||
/*
|
|
||||||
* We do not use i_size_write() because nobody can race with us as we
|
|
||||||
* are holding host @host->i_mutex - every xattr operation for this
|
|
||||||
* inode is serialized by it.
|
|
||||||
*/
|
|
||||||
inode->i_size = ui->ui_size = size;
|
|
||||||
ui->data_len = size;
|
|
||||||
err = ubifs_jnl_update(c, host, nm, inode, 0, 1);
|
err = ubifs_jnl_update(c, host, nm, inode, 0, 1);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_cancel;
|
goto out_cancel;
|
||||||
@@ -172,8 +167,8 @@ out_cancel:
|
|||||||
host_ui->xattr_cnt -= 1;
|
host_ui->xattr_cnt -= 1;
|
||||||
host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
|
host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
|
||||||
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
|
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
|
||||||
out_unlock:
|
|
||||||
mutex_unlock(&host_ui->ui_mutex);
|
mutex_unlock(&host_ui->ui_mutex);
|
||||||
|
out_free:
|
||||||
make_bad_inode(inode);
|
make_bad_inode(inode);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
out_budg:
|
out_budg:
|
||||||
@@ -207,22 +202,21 @@ static int change_xattr(struct ubifs_info *c, struct inode *host,
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
mutex_lock(&host_ui->ui_mutex);
|
|
||||||
host->i_ctime = ubifs_current_time(host);
|
|
||||||
host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len);
|
|
||||||
host_ui->xattr_size += CALC_XATTR_BYTES(size);
|
|
||||||
|
|
||||||
kfree(ui->data);
|
kfree(ui->data);
|
||||||
ui->data = kmalloc(size, GFP_NOFS);
|
ui->data = kmalloc(size, GFP_NOFS);
|
||||||
if (!ui->data) {
|
if (!ui->data) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out_unlock;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(ui->data, value, size);
|
memcpy(ui->data, value, size);
|
||||||
inode->i_size = ui->ui_size = size;
|
inode->i_size = ui->ui_size = size;
|
||||||
ui->data_len = size;
|
ui->data_len = size;
|
||||||
|
|
||||||
|
mutex_lock(&host_ui->ui_mutex);
|
||||||
|
host->i_ctime = ubifs_current_time(host);
|
||||||
|
host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len);
|
||||||
|
host_ui->xattr_size += CALC_XATTR_BYTES(size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It is important to write the host inode after the xattr inode
|
* It is important to write the host inode after the xattr inode
|
||||||
* because if the host inode gets synchronized (via 'fsync()'), then
|
* because if the host inode gets synchronized (via 'fsync()'), then
|
||||||
@@ -240,9 +234,9 @@ static int change_xattr(struct ubifs_info *c, struct inode *host,
|
|||||||
out_cancel:
|
out_cancel:
|
||||||
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
|
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
|
||||||
host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len);
|
host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len);
|
||||||
make_bad_inode(inode);
|
|
||||||
out_unlock:
|
|
||||||
mutex_unlock(&host_ui->ui_mutex);
|
mutex_unlock(&host_ui->ui_mutex);
|
||||||
|
make_bad_inode(inode);
|
||||||
|
out_free:
|
||||||
ubifs_release_budget(c, &req);
|
ubifs_release_budget(c, &req);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -312,6 +306,7 @@ int ubifs_setxattr(struct dentry *dentry, const char *name,
|
|||||||
|
|
||||||
dbg_gen("xattr '%s', host ino %lu ('%.*s'), size %zd", name,
|
dbg_gen("xattr '%s', host ino %lu ('%.*s'), size %zd", name,
|
||||||
host->i_ino, dentry->d_name.len, dentry->d_name.name, size);
|
host->i_ino, dentry->d_name.len, dentry->d_name.name, size);
|
||||||
|
ubifs_assert(mutex_is_locked(&host->i_mutex));
|
||||||
|
|
||||||
if (size > UBIFS_MAX_INO_DATA)
|
if (size > UBIFS_MAX_INO_DATA)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
@@ -384,7 +379,6 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf,
|
|||||||
if (!xent)
|
if (!xent)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
mutex_lock(&host->i_mutex);
|
|
||||||
xent_key_init(c, &key, host->i_ino, &nm);
|
xent_key_init(c, &key, host->i_ino, &nm);
|
||||||
err = ubifs_tnc_lookup_nm(c, &key, xent, &nm);
|
err = ubifs_tnc_lookup_nm(c, &key, xent, &nm);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -419,7 +413,6 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf,
|
|||||||
out_iput:
|
out_iput:
|
||||||
iput(inode);
|
iput(inode);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&host->i_mutex);
|
|
||||||
kfree(xent);
|
kfree(xent);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -449,8 +442,6 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
|||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
lowest_xent_key(c, &key, host->i_ino);
|
lowest_xent_key(c, &key, host->i_ino);
|
||||||
|
|
||||||
mutex_lock(&host->i_mutex);
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int type;
|
int type;
|
||||||
|
|
||||||
@@ -479,7 +470,6 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
|||||||
pxent = xent;
|
pxent = xent;
|
||||||
key_read(c, &xent->key, &key);
|
key_read(c, &xent->key, &key);
|
||||||
}
|
}
|
||||||
mutex_unlock(&host->i_mutex);
|
|
||||||
|
|
||||||
kfree(pxent);
|
kfree(pxent);
|
||||||
if (err != -ENOENT) {
|
if (err != -ENOENT) {
|
||||||
|
Reference in New Issue
Block a user