diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2009-12-16 03:54:00 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-12-16 12:16:47 -0500 |
commit | 7715b521222b6ebb6e927fa261ed91ed687fe454 (patch) | |
tree | 33c6f8179d405974ed6763df331f731044c16072 | |
parent | 85a17f552dfe77efb44b971615e4f221a5f28f37 (diff) |
O_TRUNC open shouldn't fail after file truncation
* take truncate logics into a helper (handle_truncate())
* rip it out of may_open()
* call it from the only caller of may_open() that might pass
O_TRUNC
* and do that after we'd finished with opening.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/namei.c | 109 |
1 files changed, 56 insertions, 53 deletions
diff --git a/fs/namei.c b/fs/namei.c index 1fc038b117be..0f0fcccab19f 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1444,69 +1444,52 @@ int may_open(struct path *path, int acc_mode, int flag) | |||
1444 | if (error) | 1444 | if (error) |
1445 | return error; | 1445 | return error; |
1446 | 1446 | ||
1447 | error = ima_path_check(path, acc_mode ? | ||
1448 | acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) : | ||
1449 | ACC_MODE(flag) & (MAY_READ | MAY_WRITE), | ||
1450 | IMA_COUNT_UPDATE); | ||
1451 | |||
1452 | if (error) | ||
1453 | return error; | ||
1454 | /* | 1447 | /* |
1455 | * An append-only file must be opened in append mode for writing. | 1448 | * An append-only file must be opened in append mode for writing. |
1456 | */ | 1449 | */ |
1457 | if (IS_APPEND(inode)) { | 1450 | if (IS_APPEND(inode)) { |
1458 | error = -EPERM; | ||
1459 | if ((flag & FMODE_WRITE) && !(flag & O_APPEND)) | 1451 | if ((flag & FMODE_WRITE) && !(flag & O_APPEND)) |
1460 | goto err_out; | 1452 | return -EPERM; |
1461 | if (flag & O_TRUNC) | 1453 | if (flag & O_TRUNC) |
1462 | goto err_out; | 1454 | return -EPERM; |
1463 | } | 1455 | } |
1464 | 1456 | ||
1465 | /* O_NOATIME can only be set by the owner or superuser */ | 1457 | /* O_NOATIME can only be set by the owner or superuser */ |
1466 | if (flag & O_NOATIME) | 1458 | if (flag & O_NOATIME && !is_owner_or_cap(inode)) |
1467 | if (!is_owner_or_cap(inode)) { | 1459 | return -EPERM; |
1468 | error = -EPERM; | ||
1469 | goto err_out; | ||
1470 | } | ||
1471 | 1460 | ||
1472 | /* | 1461 | /* |
1473 | * Ensure there are no outstanding leases on the file. | 1462 | * Ensure there are no outstanding leases on the file. |
1474 | */ | 1463 | */ |
1475 | error = break_lease(inode, flag); | 1464 | error = break_lease(inode, flag); |
1476 | if (error) | 1465 | if (error) |
1477 | goto err_out; | 1466 | return error; |
1478 | |||
1479 | if (flag & O_TRUNC) { | ||
1480 | error = get_write_access(inode); | ||
1481 | if (error) | ||
1482 | goto err_out; | ||
1483 | |||
1484 | /* | ||
1485 | * Refuse to truncate files with mandatory locks held on them. | ||
1486 | */ | ||
1487 | error = locks_verify_locked(inode); | ||
1488 | if (!error) | ||
1489 | error = security_path_truncate(path, 0, | ||
1490 | ATTR_MTIME|ATTR_CTIME|ATTR_OPEN); | ||
1491 | if (!error) { | ||
1492 | vfs_dq_init(inode); | ||
1493 | 1467 | ||
1494 | error = do_truncate(dentry, 0, | 1468 | return ima_path_check(path, acc_mode ? |
1495 | ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, | 1469 | acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) : |
1496 | NULL); | 1470 | ACC_MODE(flag) & (MAY_READ | MAY_WRITE), |
1497 | } | 1471 | IMA_COUNT_UPDATE); |
1498 | put_write_access(inode); | 1472 | } |
1499 | if (error) | ||
1500 | goto err_out; | ||
1501 | } else | ||
1502 | if (flag & FMODE_WRITE) | ||
1503 | vfs_dq_init(inode); | ||
1504 | 1473 | ||
1505 | return 0; | 1474 | static int handle_truncate(struct path *path) |
1506 | err_out: | 1475 | { |
1507 | ima_counts_put(path, acc_mode ? | 1476 | struct inode *inode = path->dentry->d_inode; |
1508 | acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) : | 1477 | int error = get_write_access(inode); |
1509 | ACC_MODE(flag) & (MAY_READ | MAY_WRITE)); | 1478 | if (error) |
1479 | return error; | ||
1480 | /* | ||
1481 | * Refuse to truncate files with mandatory locks held on them. | ||
1482 | */ | ||
1483 | error = locks_verify_locked(inode); | ||
1484 | if (!error) | ||
1485 | error = security_path_truncate(path, 0, | ||
1486 | ATTR_MTIME|ATTR_CTIME|ATTR_OPEN); | ||
1487 | if (!error) { | ||
1488 | error = do_truncate(path->dentry, 0, | ||
1489 | ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, | ||
1490 | NULL); | ||
1491 | } | ||
1492 | put_write_access(inode); | ||
1510 | return error; | 1493 | return error; |
1511 | } | 1494 | } |
1512 | 1495 | ||
@@ -1561,7 +1544,7 @@ static inline int open_to_namei_flags(int flag) | |||
1561 | return flag; | 1544 | return flag; |
1562 | } | 1545 | } |
1563 | 1546 | ||
1564 | static int open_will_write_to_fs(int flag, struct inode *inode) | 1547 | static int open_will_truncate(int flag, struct inode *inode) |
1565 | { | 1548 | { |
1566 | /* | 1549 | /* |
1567 | * We'll never write to the fs underlying | 1550 | * We'll never write to the fs underlying |
@@ -1586,7 +1569,7 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
1586 | struct path path, save; | 1569 | struct path path, save; |
1587 | struct dentry *dir; | 1570 | struct dentry *dir; |
1588 | int count = 0; | 1571 | int count = 0; |
1589 | int will_write; | 1572 | int will_truncate; |
1590 | int flag = open_to_namei_flags(open_flag); | 1573 | int flag = open_to_namei_flags(open_flag); |
1591 | 1574 | ||
1592 | /* | 1575 | /* |
@@ -1752,28 +1735,48 @@ ok: | |||
1752 | * be avoided. Taking this mnt write here | 1735 | * be avoided. Taking this mnt write here |
1753 | * ensures that (2) can not occur. | 1736 | * ensures that (2) can not occur. |
1754 | */ | 1737 | */ |
1755 | will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode); | 1738 | will_truncate = open_will_truncate(flag, nd.path.dentry->d_inode); |
1756 | if (will_write) { | 1739 | if (will_truncate) { |
1757 | error = mnt_want_write(nd.path.mnt); | 1740 | error = mnt_want_write(nd.path.mnt); |
1758 | if (error) | 1741 | if (error) |
1759 | goto exit; | 1742 | goto exit; |
1760 | } | 1743 | } |
1761 | error = may_open(&nd.path, acc_mode, flag); | 1744 | error = may_open(&nd.path, acc_mode, flag); |
1762 | if (error) { | 1745 | if (error) { |
1763 | if (will_write) | 1746 | if (will_truncate) |
1764 | mnt_drop_write(nd.path.mnt); | 1747 | mnt_drop_write(nd.path.mnt); |
1765 | goto exit; | 1748 | goto exit; |
1766 | } | 1749 | } |
1767 | filp = nameidata_to_filp(&nd, open_flag); | 1750 | filp = nameidata_to_filp(&nd, open_flag); |
1768 | if (IS_ERR(filp)) | 1751 | if (IS_ERR(filp)) { |
1769 | ima_counts_put(&nd.path, | 1752 | ima_counts_put(&nd.path, |
1770 | acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC)); | 1753 | acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC)); |
1754 | if (will_truncate) | ||
1755 | mnt_drop_write(nd.path.mnt); | ||
1756 | if (nd.root.mnt) | ||
1757 | path_put(&nd.root); | ||
1758 | return filp; | ||
1759 | } | ||
1760 | |||
1761 | if (acc_mode & MAY_WRITE) | ||
1762 | vfs_dq_init(nd.path.dentry->d_inode); | ||
1763 | |||
1764 | if (will_truncate) { | ||
1765 | error = handle_truncate(&nd.path); | ||
1766 | if (error) { | ||
1767 | mnt_drop_write(nd.path.mnt); | ||
1768 | fput(filp); | ||
1769 | if (nd.root.mnt) | ||
1770 | path_put(&nd.root); | ||
1771 | return ERR_PTR(error); | ||
1772 | } | ||
1773 | } | ||
1771 | /* | 1774 | /* |
1772 | * It is now safe to drop the mnt write | 1775 | * It is now safe to drop the mnt write |
1773 | * because the filp has had a write taken | 1776 | * because the filp has had a write taken |
1774 | * on its behalf. | 1777 | * on its behalf. |
1775 | */ | 1778 | */ |
1776 | if (will_write) | 1779 | if (will_truncate) |
1777 | mnt_drop_write(nd.path.mnt); | 1780 | mnt_drop_write(nd.path.mnt); |
1778 | if (nd.root.mnt) | 1781 | if (nd.root.mnt) |
1779 | path_put(&nd.root); | 1782 | path_put(&nd.root); |