diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 150 |
1 files changed, 70 insertions, 80 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index b5f613932912..d68631f18df1 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1739,41 +1739,45 @@ shouldnt_be_hashed: | |||
1739 | * @rootmnt: vfsmnt to which the root dentry belongs | 1739 | * @rootmnt: vfsmnt to which the root dentry belongs |
1740 | * @buffer: buffer to return value in | 1740 | * @buffer: buffer to return value in |
1741 | * @buflen: buffer length | 1741 | * @buflen: buffer length |
1742 | * @fail_deleted: what to return for deleted files | ||
1743 | * | 1742 | * |
1744 | * Convert a dentry into an ASCII path name. If the entry has been deleted, | 1743 | * Convert a dentry into an ASCII path name. If the entry has been deleted |
1745 | * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, | 1744 | * the string " (deleted)" is appended. Note that this is ambiguous. |
1746 | * the the string " (deleted)" is appended. Note that this is ambiguous. | ||
1747 | * | 1745 | * |
1748 | * Returns the buffer or an error code. | 1746 | * Returns the buffer or an error code if the path was too long. |
1747 | * | ||
1748 | * "buflen" should be positive. Caller holds the dcache_lock. | ||
1749 | */ | 1749 | */ |
1750 | static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, | 1750 | static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, |
1751 | struct dentry *root, struct vfsmount *rootmnt, | 1751 | struct dentry *root, struct vfsmount *rootmnt, |
1752 | char *buffer, int buflen, int fail_deleted) | 1752 | char *buffer, int buflen) |
1753 | { | 1753 | { |
1754 | int namelen, is_slash; | 1754 | char * end = buffer+buflen; |
1755 | 1755 | char * retval; | |
1756 | if (buflen < 2) | 1756 | int namelen; |
1757 | return ERR_PTR(-ENAMETOOLONG); | ||
1758 | buffer += --buflen; | ||
1759 | *buffer = '\0'; | ||
1760 | 1757 | ||
1761 | spin_lock(&dcache_lock); | 1758 | *--end = '\0'; |
1759 | buflen--; | ||
1762 | if (!IS_ROOT(dentry) && d_unhashed(dentry)) { | 1760 | if (!IS_ROOT(dentry) && d_unhashed(dentry)) { |
1763 | if (fail_deleted) { | ||
1764 | buffer = ERR_PTR(-ENOENT); | ||
1765 | goto out; | ||
1766 | } | ||
1767 | if (buflen < 10) | ||
1768 | goto Elong; | ||
1769 | buflen -= 10; | 1761 | buflen -= 10; |
1770 | buffer -= 10; | 1762 | end -= 10; |
1771 | memcpy(buffer, " (deleted)", 10); | 1763 | if (buflen < 0) |
1764 | goto Elong; | ||
1765 | memcpy(end, " (deleted)", 10); | ||
1772 | } | 1766 | } |
1773 | while (dentry != root || vfsmnt != rootmnt) { | 1767 | |
1768 | if (buflen < 1) | ||
1769 | goto Elong; | ||
1770 | /* Get '/' right */ | ||
1771 | retval = end-1; | ||
1772 | *retval = '/'; | ||
1773 | |||
1774 | for (;;) { | ||
1774 | struct dentry * parent; | 1775 | struct dentry * parent; |
1775 | 1776 | ||
1777 | if (dentry == root && vfsmnt == rootmnt) | ||
1778 | break; | ||
1776 | if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | 1779 | if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { |
1780 | /* Global root? */ | ||
1777 | spin_lock(&vfsmount_lock); | 1781 | spin_lock(&vfsmount_lock); |
1778 | if (vfsmnt->mnt_parent == vfsmnt) { | 1782 | if (vfsmnt->mnt_parent == vfsmnt) { |
1779 | spin_unlock(&vfsmount_lock); | 1783 | spin_unlock(&vfsmount_lock); |
@@ -1787,60 +1791,33 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, | |||
1787 | parent = dentry->d_parent; | 1791 | parent = dentry->d_parent; |
1788 | prefetch(parent); | 1792 | prefetch(parent); |
1789 | namelen = dentry->d_name.len; | 1793 | namelen = dentry->d_name.len; |
1790 | if (buflen <= namelen) | ||
1791 | goto Elong; | ||
1792 | buflen -= namelen + 1; | 1794 | buflen -= namelen + 1; |
1793 | buffer -= namelen; | 1795 | if (buflen < 0) |
1794 | memcpy(buffer, dentry->d_name.name, namelen); | 1796 | goto Elong; |
1795 | *--buffer = '/'; | 1797 | end -= namelen; |
1798 | memcpy(end, dentry->d_name.name, namelen); | ||
1799 | *--end = '/'; | ||
1800 | retval = end; | ||
1796 | dentry = parent; | 1801 | dentry = parent; |
1797 | } | 1802 | } |
1798 | /* Get '/' right */ | ||
1799 | if (*buffer != '/') | ||
1800 | *--buffer = '/'; | ||
1801 | 1803 | ||
1802 | out: | 1804 | return retval; |
1803 | spin_unlock(&dcache_lock); | ||
1804 | return buffer; | ||
1805 | 1805 | ||
1806 | global_root: | 1806 | global_root: |
1807 | /* | ||
1808 | * We went past the (vfsmount, dentry) we were looking for and have | ||
1809 | * either hit a root dentry, a lazily unmounted dentry, an | ||
1810 | * unconnected dentry, or the file is on a pseudo filesystem. | ||
1811 | */ | ||
1812 | namelen = dentry->d_name.len; | 1807 | namelen = dentry->d_name.len; |
1813 | is_slash = (namelen == 1 && *dentry->d_name.name == '/'); | 1808 | buflen -= namelen; |
1814 | if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) { | 1809 | if (buflen < 0) |
1815 | /* | ||
1816 | * Make sure we won't return a pathname starting with '/'. | ||
1817 | * | ||
1818 | * Historically, we also glue together the root dentry and | ||
1819 | * remaining name for pseudo filesystems like pipefs, which | ||
1820 | * have the MS_NOUSER flag set. This results in pathnames | ||
1821 | * like "pipe:[439336]". | ||
1822 | */ | ||
1823 | if (*buffer == '/') { | ||
1824 | buffer++; | ||
1825 | buflen++; | ||
1826 | } | ||
1827 | if (is_slash) | ||
1828 | goto out; | ||
1829 | } | ||
1830 | if (buflen < namelen) | ||
1831 | goto Elong; | 1810 | goto Elong; |
1832 | buffer -= namelen; | 1811 | retval -= namelen-1; /* hit the slash */ |
1833 | memcpy(buffer, dentry->d_name.name, namelen); | 1812 | memcpy(retval, dentry->d_name.name, namelen); |
1834 | goto out; | 1813 | return retval; |
1835 | |||
1836 | Elong: | 1814 | Elong: |
1837 | buffer = ERR_PTR(-ENAMETOOLONG); | 1815 | return ERR_PTR(-ENAMETOOLONG); |
1838 | goto out; | ||
1839 | } | 1816 | } |
1840 | 1817 | ||
1841 | /* write full pathname into buffer and return start of pathname */ | 1818 | /* write full pathname into buffer and return start of pathname */ |
1842 | char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, | 1819 | char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt, |
1843 | int buflen) | 1820 | char *buf, int buflen) |
1844 | { | 1821 | { |
1845 | char *res; | 1822 | char *res; |
1846 | struct vfsmount *rootmnt; | 1823 | struct vfsmount *rootmnt; |
@@ -1850,7 +1827,9 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, | |||
1850 | rootmnt = mntget(current->fs->rootmnt); | 1827 | rootmnt = mntget(current->fs->rootmnt); |
1851 | root = dget(current->fs->root); | 1828 | root = dget(current->fs->root); |
1852 | read_unlock(¤t->fs->lock); | 1829 | read_unlock(¤t->fs->lock); |
1853 | res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0); | 1830 | spin_lock(&dcache_lock); |
1831 | res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen); | ||
1832 | spin_unlock(&dcache_lock); | ||
1854 | dput(root); | 1833 | dput(root); |
1855 | mntput(rootmnt); | 1834 | mntput(rootmnt); |
1856 | return res; | 1835 | return res; |
@@ -1876,10 +1855,10 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, | |||
1876 | */ | 1855 | */ |
1877 | asmlinkage long sys_getcwd(char __user *buf, unsigned long size) | 1856 | asmlinkage long sys_getcwd(char __user *buf, unsigned long size) |
1878 | { | 1857 | { |
1879 | int error, len; | 1858 | int error; |
1880 | struct vfsmount *pwdmnt, *rootmnt; | 1859 | struct vfsmount *pwdmnt, *rootmnt; |
1881 | struct dentry *pwd, *root; | 1860 | struct dentry *pwd, *root; |
1882 | char *page = (char *) __get_free_page(GFP_USER), *cwd; | 1861 | char *page = (char *) __get_free_page(GFP_USER); |
1883 | 1862 | ||
1884 | if (!page) | 1863 | if (!page) |
1885 | return -ENOMEM; | 1864 | return -ENOMEM; |
@@ -1891,18 +1870,29 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) | |||
1891 | root = dget(current->fs->root); | 1870 | root = dget(current->fs->root); |
1892 | read_unlock(¤t->fs->lock); | 1871 | read_unlock(¤t->fs->lock); |
1893 | 1872 | ||
1894 | cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1); | 1873 | error = -ENOENT; |
1895 | error = PTR_ERR(cwd); | 1874 | /* Has the current directory has been unlinked? */ |
1896 | if (IS_ERR(cwd)) | 1875 | spin_lock(&dcache_lock); |
1897 | goto out; | 1876 | if (pwd->d_parent == pwd || !d_unhashed(pwd)) { |
1877 | unsigned long len; | ||
1878 | char * cwd; | ||
1898 | 1879 | ||
1899 | error = -ERANGE; | 1880 | cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE); |
1900 | len = PAGE_SIZE + page - cwd; | 1881 | spin_unlock(&dcache_lock); |
1901 | if (len <= size) { | 1882 | |
1902 | error = len; | 1883 | error = PTR_ERR(cwd); |
1903 | if (copy_to_user(buf, cwd, len)) | 1884 | if (IS_ERR(cwd)) |
1904 | error = -EFAULT; | 1885 | goto out; |
1905 | } | 1886 | |
1887 | error = -ERANGE; | ||
1888 | len = PAGE_SIZE + page - cwd; | ||
1889 | if (len <= size) { | ||
1890 | error = len; | ||
1891 | if (copy_to_user(buf, cwd, len)) | ||
1892 | error = -EFAULT; | ||
1893 | } | ||
1894 | } else | ||
1895 | spin_unlock(&dcache_lock); | ||
1906 | 1896 | ||
1907 | out: | 1897 | out: |
1908 | dput(pwd); | 1898 | dput(pwd); |