diff options
| author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-08-12 09:30:12 -0400 |
|---|---|---|
| committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-08-14 05:46:20 -0400 |
| commit | c78c7e35a4709b55d3126624662c8f6d7e3d1a5e (patch) | |
| tree | f36f56651f50f51b7e79451d0986e3656da0269d | |
| parent | 720b499c806200d06f4f22c668d46db784117089 (diff) | |
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>
| -rw-r--r-- | fs/ubifs/journal.c | 2 | ||||
| -rw-r--r-- | fs/ubifs/xattr.c | 44 |
2 files changed, 18 insertions, 28 deletions
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index acdae00aaa54..22993f867d19 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c | |||
| @@ -1374,7 +1374,7 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode, | |||
| 1374 | const struct inode *host) | 1374 | const struct inode *host) |
| 1375 | { | 1375 | { |
| 1376 | int err, len1, len2, aligned_len, aligned_len1, lnum, offs; | 1376 | int err, len1, len2, aligned_len, aligned_len1, lnum, offs; |
| 1377 | struct ubifs_inode *host_ui = ubifs_inode(inode); | 1377 | struct ubifs_inode *host_ui = ubifs_inode(host); |
| 1378 | struct ubifs_ino_node *ino; | 1378 | struct ubifs_ino_node *ino; |
| 1379 | union ubifs_key key; | 1379 | union ubifs_key key; |
| 1380 | int sync = IS_DIRSYNC(host); | 1380 | int sync = IS_DIRSYNC(host); |
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 6f493dea561e..649bec78b645 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c | |||
| @@ -61,7 +61,7 @@ | |||
| 61 | 61 | ||
| 62 | /* | 62 | /* |
| 63 | * Limit the number of extended attributes per inode so that the total size | 63 | * Limit the number of extended attributes per inode so that the total size |
| 64 | * (xattr_size) is guaranteeded to fit in an 'unsigned int'. | 64 | * (@xattr_size) is guaranteeded to fit in an 'unsigned int'. |
| 65 | */ | 65 | */ |
| 66 | #define MAX_XATTRS_PER_INODE 65535 | 66 | #define MAX_XATTRS_PER_INODE 65535 |
| 67 | 67 | ||
| @@ -110,7 +110,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host, | |||
| 110 | return -ENOSPC; | 110 | return -ENOSPC; |
| 111 | /* | 111 | /* |
| 112 | * Linux limits the maximum size of the extended attribute names list | 112 | * Linux limits the maximum size of the extended attribute names list |
| 113 | * to %XATTR_LIST_MAX. This means we should not allow creating more* | 113 | * to %XATTR_LIST_MAX. This means we should not allow creating more |
| 114 | * extended attributes if the name list becomes larger. This limitation | 114 | * extended attributes if the name list becomes larger. This limitation |
| 115 | * is artificial for UBIFS, though. | 115 | * is artificial for UBIFS, though. |
| 116 | */ | 116 | */ |
| @@ -128,7 +128,6 @@ static int create_xattr(struct ubifs_info *c, struct inode *host, | |||
| 128 | goto out_budg; | 128 | goto out_budg; |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | mutex_lock(&host_ui->ui_mutex); | ||
| 132 | /* Re-define all operations to be "nothing" */ | 131 | /* Re-define all operations to be "nothing" */ |
| 133 | inode->i_mapping->a_ops = &none_address_operations; | 132 | inode->i_mapping->a_ops = &none_address_operations; |
| 134 | inode->i_op = &none_inode_operations; | 133 | inode->i_op = &none_inode_operations; |
| @@ -141,23 +140,19 @@ static int create_xattr(struct ubifs_info *c, struct inode *host, | |||
| 141 | ui->data = kmalloc(size, GFP_NOFS); | 140 | ui->data = kmalloc(size, GFP_NOFS); |
| 142 | if (!ui->data) { | 141 | if (!ui->data) { |
| 143 | err = -ENOMEM; | 142 | err = -ENOMEM; |
| 144 | goto out_unlock; | 143 | goto out_free; |
| 145 | } | 144 | } |
| 146 | |||
| 147 | memcpy(ui->data, value, size); | 145 | memcpy(ui->data, value, size); |
| 146 | inode->i_size = ui->ui_size = size; | ||
| 147 | ui->data_len = size; | ||
| 148 | |||
| 149 | mutex_lock(&host_ui->ui_mutex); | ||
| 148 | host->i_ctime = ubifs_current_time(host); | 150 | host->i_ctime = ubifs_current_time(host); |
| 149 | host_ui->xattr_cnt += 1; | 151 | host_ui->xattr_cnt += 1; |
| 150 | host_ui->xattr_size += CALC_DENT_SIZE(nm->len); | 152 | host_ui->xattr_size += CALC_DENT_SIZE(nm->len); |
| 151 | host_ui->xattr_size += CALC_XATTR_BYTES(size); | 153 | host_ui->xattr_size += CALC_XATTR_BYTES(size); |
| 152 | host_ui->xattr_names += nm->len; | 154 | host_ui->xattr_names += nm->len; |
| 153 | 155 | ||
| 154 | /* | ||
| 155 | * We do not use i_size_write() because nobody can race with us as we | ||
| 156 | * are holding host @host->i_mutex - every xattr operation for this | ||
| 157 | * inode is serialized by it. | ||
| 158 | */ | ||
| 159 | inode->i_size = ui->ui_size = size; | ||
| 160 | ui->data_len = size; | ||
| 161 | err = ubifs_jnl_update(c, host, nm, inode, 0, 1); | 156 | err = ubifs_jnl_update(c, host, nm, inode, 0, 1); |
| 162 | if (err) | 157 | if (err) |
| 163 | goto out_cancel; | 158 | goto out_cancel; |
| @@ -172,8 +167,8 @@ out_cancel: | |||
| 172 | host_ui->xattr_cnt -= 1; | 167 | host_ui->xattr_cnt -= 1; |
| 173 | host_ui->xattr_size -= CALC_DENT_SIZE(nm->len); | 168 | host_ui->xattr_size -= CALC_DENT_SIZE(nm->len); |
| 174 | host_ui->xattr_size -= CALC_XATTR_BYTES(size); | 169 | host_ui->xattr_size -= CALC_XATTR_BYTES(size); |
| 175 | out_unlock: | ||
| 176 | mutex_unlock(&host_ui->ui_mutex); | 170 | mutex_unlock(&host_ui->ui_mutex); |
| 171 | out_free: | ||
| 177 | make_bad_inode(inode); | 172 | make_bad_inode(inode); |
| 178 | iput(inode); | 173 | iput(inode); |
| 179 | out_budg: | 174 | out_budg: |
| @@ -207,22 +202,21 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, | |||
| 207 | if (err) | 202 | if (err) |
| 208 | return err; | 203 | return err; |
| 209 | 204 | ||
| 210 | mutex_lock(&host_ui->ui_mutex); | ||
| 211 | host->i_ctime = ubifs_current_time(host); | ||
| 212 | host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len); | ||
| 213 | host_ui->xattr_size += CALC_XATTR_BYTES(size); | ||
| 214 | |||
| 215 | kfree(ui->data); | 205 | kfree(ui->data); |
| 216 | ui->data = kmalloc(size, GFP_NOFS); | 206 | ui->data = kmalloc(size, GFP_NOFS); |
| 217 | if (!ui->data) { | 207 | if (!ui->data) { |
| 218 | err = -ENOMEM; | 208 | err = -ENOMEM; |
| 219 | goto out_unlock; | 209 | goto out_free; |
| 220 | } | 210 | } |
| 221 | |||
| 222 | memcpy(ui->data, value, size); | 211 | memcpy(ui->data, value, size); |
| 223 | inode->i_size = ui->ui_size = size; | 212 | inode->i_size = ui->ui_size = size; |
| 224 | ui->data_len = size; | 213 | ui->data_len = size; |
| 225 | 214 | ||
| 215 | mutex_lock(&host_ui->ui_mutex); | ||
| 216 | host->i_ctime = ubifs_current_time(host); | ||
| 217 | host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len); | ||
| 218 | host_ui->xattr_size += CALC_XATTR_BYTES(size); | ||
| 219 | |||
| 226 | /* | 220 | /* |
| 227 | * It is important to write the host inode after the xattr inode | 221 | * It is important to write the host inode after the xattr inode |
| 228 | * because if the host inode gets synchronized (via 'fsync()'), then | 222 | * 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, | |||
| 240 | out_cancel: | 234 | out_cancel: |
| 241 | host_ui->xattr_size -= CALC_XATTR_BYTES(size); | 235 | host_ui->xattr_size -= CALC_XATTR_BYTES(size); |
| 242 | host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len); | 236 | host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len); |
| 243 | make_bad_inode(inode); | ||
| 244 | out_unlock: | ||
| 245 | mutex_unlock(&host_ui->ui_mutex); | 237 | mutex_unlock(&host_ui->ui_mutex); |
| 238 | make_bad_inode(inode); | ||
| 239 | out_free: | ||
| 246 | ubifs_release_budget(c, &req); | 240 | ubifs_release_budget(c, &req); |
| 247 | return err; | 241 | return err; |
| 248 | } | 242 | } |
| @@ -312,6 +306,7 @@ int ubifs_setxattr(struct dentry *dentry, const char *name, | |||
| 312 | 306 | ||
| 313 | dbg_gen("xattr '%s', host ino %lu ('%.*s'), size %zd", name, | 307 | dbg_gen("xattr '%s', host ino %lu ('%.*s'), size %zd", name, |
| 314 | host->i_ino, dentry->d_name.len, dentry->d_name.name, size); | 308 | host->i_ino, dentry->d_name.len, dentry->d_name.name, size); |
| 309 | ubifs_assert(mutex_is_locked(&host->i_mutex)); | ||
| 315 | 310 | ||
| 316 | if (size > UBIFS_MAX_INO_DATA) | 311 | if (size > UBIFS_MAX_INO_DATA) |
| 317 | return -ERANGE; | 312 | return -ERANGE; |
| @@ -384,7 +379,6 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, | |||
| 384 | if (!xent) | 379 | if (!xent) |
| 385 | return -ENOMEM; | 380 | return -ENOMEM; |
| 386 | 381 | ||
| 387 | mutex_lock(&host->i_mutex); | ||
| 388 | xent_key_init(c, &key, host->i_ino, &nm); | 382 | xent_key_init(c, &key, host->i_ino, &nm); |
| 389 | err = ubifs_tnc_lookup_nm(c, &key, xent, &nm); | 383 | err = ubifs_tnc_lookup_nm(c, &key, xent, &nm); |
| 390 | if (err) { | 384 | if (err) { |
| @@ -419,7 +413,6 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, | |||
| 419 | out_iput: | 413 | out_iput: |
| 420 | iput(inode); | 414 | iput(inode); |
| 421 | out_unlock: | 415 | out_unlock: |
| 422 | mutex_unlock(&host->i_mutex); | ||
| 423 | kfree(xent); | 416 | kfree(xent); |
| 424 | return err; | 417 | return err; |
| 425 | } | 418 | } |
| @@ -449,8 +442,6 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size) | |||
| 449 | return -ERANGE; | 442 | return -ERANGE; |
| 450 | 443 | ||
| 451 | lowest_xent_key(c, &key, host->i_ino); | 444 | lowest_xent_key(c, &key, host->i_ino); |
| 452 | |||
| 453 | mutex_lock(&host->i_mutex); | ||
| 454 | while (1) { | 445 | while (1) { |
| 455 | int type; | 446 | int type; |
| 456 | 447 | ||
| @@ -479,7 +470,6 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size) | |||
| 479 | pxent = xent; | 470 | pxent = xent; |
| 480 | key_read(c, &xent->key, &key); | 471 | key_read(c, &xent->key, &key); |
| 481 | } | 472 | } |
| 482 | mutex_unlock(&host->i_mutex); | ||
| 483 | 473 | ||
| 484 | kfree(pxent); | 474 | kfree(pxent); |
| 485 | if (err != -ENOENT) { | 475 | if (err != -ENOENT) { |
