diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 30 |
1 files changed, 14 insertions, 16 deletions
diff --git a/fs/namei.c b/fs/namei.c index 68921d9b5302..d62fdc875f22 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -232,6 +232,7 @@ int generic_permission(struct inode *inode, int mask, | |||
232 | /* | 232 | /* |
233 | * Searching includes executable on directories, else just read. | 233 | * Searching includes executable on directories, else just read. |
234 | */ | 234 | */ |
235 | mask &= MAY_READ | MAY_WRITE | MAY_EXEC; | ||
235 | if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) | 236 | if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) |
236 | if (capable(CAP_DAC_READ_SEARCH)) | 237 | if (capable(CAP_DAC_READ_SEARCH)) |
237 | return 0; | 238 | return 0; |
@@ -560,6 +561,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata | |||
560 | dget(dentry); | 561 | dget(dentry); |
561 | } | 562 | } |
562 | mntget(path->mnt); | 563 | mntget(path->mnt); |
564 | nd->last_type = LAST_BIND; | ||
563 | cookie = dentry->d_inode->i_op->follow_link(dentry, nd); | 565 | cookie = dentry->d_inode->i_op->follow_link(dentry, nd); |
564 | error = PTR_ERR(cookie); | 566 | error = PTR_ERR(cookie); |
565 | if (!IS_ERR(cookie)) { | 567 | if (!IS_ERR(cookie)) { |
@@ -1602,11 +1604,12 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
1602 | struct file *filp; | 1604 | struct file *filp; |
1603 | struct nameidata nd; | 1605 | struct nameidata nd; |
1604 | int error; | 1606 | int error; |
1605 | struct path path, save; | 1607 | struct path path; |
1606 | struct dentry *dir; | 1608 | struct dentry *dir; |
1607 | int count = 0; | 1609 | int count = 0; |
1608 | int will_truncate; | 1610 | int will_truncate; |
1609 | int flag = open_to_namei_flags(open_flag); | 1611 | int flag = open_to_namei_flags(open_flag); |
1612 | int force_reval = 0; | ||
1610 | 1613 | ||
1611 | /* | 1614 | /* |
1612 | * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only | 1615 | * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only |
@@ -1618,7 +1621,7 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
1618 | open_flag |= O_DSYNC; | 1621 | open_flag |= O_DSYNC; |
1619 | 1622 | ||
1620 | if (!acc_mode) | 1623 | if (!acc_mode) |
1621 | acc_mode = MAY_OPEN | ACC_MODE(flag); | 1624 | acc_mode = MAY_OPEN | ACC_MODE(open_flag); |
1622 | 1625 | ||
1623 | /* O_TRUNC implies we need access checks for write permissions */ | 1626 | /* O_TRUNC implies we need access checks for write permissions */ |
1624 | if (flag & O_TRUNC) | 1627 | if (flag & O_TRUNC) |
@@ -1658,9 +1661,12 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
1658 | /* | 1661 | /* |
1659 | * Create - we need to know the parent. | 1662 | * Create - we need to know the parent. |
1660 | */ | 1663 | */ |
1664 | reval: | ||
1661 | error = path_init(dfd, pathname, LOOKUP_PARENT, &nd); | 1665 | error = path_init(dfd, pathname, LOOKUP_PARENT, &nd); |
1662 | if (error) | 1666 | if (error) |
1663 | return ERR_PTR(error); | 1667 | return ERR_PTR(error); |
1668 | if (force_reval) | ||
1669 | nd.flags |= LOOKUP_REVAL; | ||
1664 | error = path_walk(pathname, &nd); | 1670 | error = path_walk(pathname, &nd); |
1665 | if (error) { | 1671 | if (error) { |
1666 | if (nd.root.mnt) | 1672 | if (nd.root.mnt) |
@@ -1730,8 +1736,7 @@ do_last: | |||
1730 | if (nd.root.mnt) | 1736 | if (nd.root.mnt) |
1731 | path_put(&nd.root); | 1737 | path_put(&nd.root); |
1732 | if (!IS_ERR(filp)) { | 1738 | if (!IS_ERR(filp)) { |
1733 | error = ima_path_check(&filp->f_path, filp->f_mode & | 1739 | error = ima_file_check(filp, acc_mode); |
1734 | (MAY_READ | MAY_WRITE | MAY_EXEC)); | ||
1735 | if (error) { | 1740 | if (error) { |
1736 | fput(filp); | 1741 | fput(filp); |
1737 | filp = ERR_PTR(error); | 1742 | filp = ERR_PTR(error); |
@@ -1791,8 +1796,7 @@ ok: | |||
1791 | } | 1796 | } |
1792 | filp = nameidata_to_filp(&nd); | 1797 | filp = nameidata_to_filp(&nd); |
1793 | if (!IS_ERR(filp)) { | 1798 | if (!IS_ERR(filp)) { |
1794 | error = ima_path_check(&filp->f_path, filp->f_mode & | 1799 | error = ima_file_check(filp, acc_mode); |
1795 | (MAY_READ | MAY_WRITE | MAY_EXEC)); | ||
1796 | if (error) { | 1800 | if (error) { |
1797 | fput(filp); | 1801 | fput(filp); |
1798 | filp = ERR_PTR(error); | 1802 | filp = ERR_PTR(error); |
@@ -1852,17 +1856,7 @@ do_link: | |||
1852 | error = security_inode_follow_link(path.dentry, &nd); | 1856 | error = security_inode_follow_link(path.dentry, &nd); |
1853 | if (error) | 1857 | if (error) |
1854 | goto exit_dput; | 1858 | goto exit_dput; |
1855 | save = nd.path; | ||
1856 | path_get(&save); | ||
1857 | error = __do_follow_link(&path, &nd); | 1859 | error = __do_follow_link(&path, &nd); |
1858 | if (error == -ESTALE) { | ||
1859 | /* nd.path had been dropped */ | ||
1860 | nd.path = save; | ||
1861 | path_get(&nd.path); | ||
1862 | nd.flags |= LOOKUP_REVAL; | ||
1863 | error = __do_follow_link(&path, &nd); | ||
1864 | } | ||
1865 | path_put(&save); | ||
1866 | path_put(&path); | 1860 | path_put(&path); |
1867 | if (error) { | 1861 | if (error) { |
1868 | /* Does someone understand code flow here? Or it is only | 1862 | /* Does someone understand code flow here? Or it is only |
@@ -1872,6 +1866,10 @@ do_link: | |||
1872 | release_open_intent(&nd); | 1866 | release_open_intent(&nd); |
1873 | if (nd.root.mnt) | 1867 | if (nd.root.mnt) |
1874 | path_put(&nd.root); | 1868 | path_put(&nd.root); |
1869 | if (error == -ESTALE && !force_reval) { | ||
1870 | force_reval = 1; | ||
1871 | goto reval; | ||
1872 | } | ||
1875 | return ERR_PTR(error); | 1873 | return ERR_PTR(error); |
1876 | } | 1874 | } |
1877 | nd.flags &= ~LOOKUP_PARENT; | 1875 | nd.flags &= ~LOOKUP_PARENT; |