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