diff options
| author | Ingo Molnar <mingo@elte.hu> | 2010-08-12 15:38:56 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2010-08-12 15:39:04 -0400 |
| commit | f46a6804135795f77d096ab0128f27531c7d051c (patch) | |
| tree | 7cd33f69e3661327739ae4c96e5a8389e7fc912e /fs/dcache.c | |
| parent | b3e84ffa21f916e3354a12a7f19169c9febe96d0 (diff) | |
| parent | ad41a1e0cab07c5125456e8d38e5b1ab148d04aa (diff) | |
Merge branch 'linus' into perf/urgent
Merge reason: Fix upstream breakage introduced by:
de5d9bf: Move list types from <linux/list.h> to <linux/types.h>.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'fs/dcache.c')
| -rw-r--r-- | fs/dcache.c | 227 |
1 files changed, 162 insertions, 65 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 86d4db15473e..166d35d56868 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
| @@ -536,7 +536,7 @@ restart: | |||
| 536 | */ | 536 | */ |
| 537 | static void prune_dcache(int count) | 537 | static void prune_dcache(int count) |
| 538 | { | 538 | { |
| 539 | struct super_block *sb, *n; | 539 | struct super_block *sb, *p = NULL; |
| 540 | int w_count; | 540 | int w_count; |
| 541 | int unused = dentry_stat.nr_unused; | 541 | int unused = dentry_stat.nr_unused; |
| 542 | int prune_ratio; | 542 | int prune_ratio; |
| @@ -550,7 +550,7 @@ static void prune_dcache(int count) | |||
| 550 | else | 550 | else |
| 551 | prune_ratio = unused / count; | 551 | prune_ratio = unused / count; |
| 552 | spin_lock(&sb_lock); | 552 | spin_lock(&sb_lock); |
| 553 | list_for_each_entry_safe(sb, n, &super_blocks, s_list) { | 553 | list_for_each_entry(sb, &super_blocks, s_list) { |
| 554 | if (list_empty(&sb->s_instances)) | 554 | if (list_empty(&sb->s_instances)) |
| 555 | continue; | 555 | continue; |
| 556 | if (sb->s_nr_dentry_unused == 0) | 556 | if (sb->s_nr_dentry_unused == 0) |
| @@ -590,14 +590,16 @@ static void prune_dcache(int count) | |||
| 590 | up_read(&sb->s_umount); | 590 | up_read(&sb->s_umount); |
| 591 | } | 591 | } |
| 592 | spin_lock(&sb_lock); | 592 | spin_lock(&sb_lock); |
| 593 | /* lock was dropped, must reset next */ | 593 | if (p) |
| 594 | list_safe_reset_next(sb, n, s_list); | 594 | __put_super(p); |
| 595 | count -= pruned; | 595 | count -= pruned; |
| 596 | __put_super(sb); | 596 | p = sb; |
| 597 | /* more work left to do? */ | 597 | /* more work left to do? */ |
| 598 | if (count <= 0) | 598 | if (count <= 0) |
| 599 | break; | 599 | break; |
| 600 | } | 600 | } |
| 601 | if (p) | ||
| 602 | __put_super(p); | ||
| 601 | spin_unlock(&sb_lock); | 603 | spin_unlock(&sb_lock); |
| 602 | spin_unlock(&dcache_lock); | 604 | spin_unlock(&dcache_lock); |
| 603 | } | 605 | } |
| @@ -1903,48 +1905,30 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) | |||
| 1903 | } | 1905 | } |
| 1904 | 1906 | ||
| 1905 | /** | 1907 | /** |
| 1906 | * __d_path - return the path of a dentry | 1908 | * Prepend path string to a buffer |
| 1909 | * | ||
| 1907 | * @path: the dentry/vfsmount to report | 1910 | * @path: the dentry/vfsmount to report |
| 1908 | * @root: root vfsmnt/dentry (may be modified by this function) | 1911 | * @root: root vfsmnt/dentry (may be modified by this function) |
| 1909 | * @buffer: buffer to return value in | 1912 | * @buffer: pointer to the end of the buffer |
| 1910 | * @buflen: buffer length | 1913 | * @buflen: pointer to buffer length |
| 1911 | * | ||
| 1912 | * Convert a dentry into an ASCII path name. If the entry has been deleted | ||
| 1913 | * the string " (deleted)" is appended. Note that this is ambiguous. | ||
| 1914 | * | 1914 | * |
| 1915 | * Returns a pointer into the buffer or an error code if the | 1915 | * Caller holds the dcache_lock. |
| 1916 | * path was too long. | ||
| 1917 | * | ||
| 1918 | * "buflen" should be positive. Caller holds the dcache_lock. | ||
| 1919 | * | 1916 | * |
| 1920 | * If path is not reachable from the supplied root, then the value of | 1917 | * If path is not reachable from the supplied root, then the value of |
| 1921 | * root is changed (without modifying refcounts). | 1918 | * root is changed (without modifying refcounts). |
| 1922 | */ | 1919 | */ |
| 1923 | char *__d_path(const struct path *path, struct path *root, | 1920 | static int prepend_path(const struct path *path, struct path *root, |
| 1924 | char *buffer, int buflen) | 1921 | char **buffer, int *buflen) |
| 1925 | { | 1922 | { |
| 1926 | struct dentry *dentry = path->dentry; | 1923 | struct dentry *dentry = path->dentry; |
| 1927 | struct vfsmount *vfsmnt = path->mnt; | 1924 | struct vfsmount *vfsmnt = path->mnt; |
| 1928 | char *end = buffer + buflen; | 1925 | bool slash = false; |
| 1929 | char *retval; | 1926 | int error = 0; |
| 1930 | 1927 | ||
| 1931 | spin_lock(&vfsmount_lock); | 1928 | spin_lock(&vfsmount_lock); |
| 1932 | prepend(&end, &buflen, "\0", 1); | 1929 | while (dentry != root->dentry || vfsmnt != root->mnt) { |
| 1933 | if (d_unlinked(dentry) && | ||
| 1934 | (prepend(&end, &buflen, " (deleted)", 10) != 0)) | ||
| 1935 | goto Elong; | ||
| 1936 | |||
| 1937 | if (buflen < 1) | ||
| 1938 | goto Elong; | ||
| 1939 | /* Get '/' right */ | ||
| 1940 | retval = end-1; | ||
| 1941 | *retval = '/'; | ||
| 1942 | |||
| 1943 | for (;;) { | ||
| 1944 | struct dentry * parent; | 1930 | struct dentry * parent; |
| 1945 | 1931 | ||
| 1946 | if (dentry == root->dentry && vfsmnt == root->mnt) | ||
| 1947 | break; | ||
| 1948 | if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | 1932 | if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { |
| 1949 | /* Global root? */ | 1933 | /* Global root? */ |
| 1950 | if (vfsmnt->mnt_parent == vfsmnt) { | 1934 | if (vfsmnt->mnt_parent == vfsmnt) { |
| @@ -1956,28 +1940,88 @@ char *__d_path(const struct path *path, struct path *root, | |||
| 1956 | } | 1940 | } |
| 1957 | parent = dentry->d_parent; | 1941 | parent = dentry->d_parent; |
| 1958 | prefetch(parent); | 1942 | prefetch(parent); |
| 1959 | if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || | 1943 | error = prepend_name(buffer, buflen, &dentry->d_name); |
| 1960 | (prepend(&end, &buflen, "/", 1) != 0)) | 1944 | if (!error) |
| 1961 | goto Elong; | 1945 | error = prepend(buffer, buflen, "/", 1); |
| 1962 | retval = end; | 1946 | if (error) |
| 1947 | break; | ||
| 1948 | |||
| 1949 | slash = true; | ||
| 1963 | dentry = parent; | 1950 | dentry = parent; |
| 1964 | } | 1951 | } |
| 1965 | 1952 | ||
| 1966 | out: | 1953 | out: |
| 1954 | if (!error && !slash) | ||
| 1955 | error = prepend(buffer, buflen, "/", 1); | ||
| 1956 | |||
| 1967 | spin_unlock(&vfsmount_lock); | 1957 | spin_unlock(&vfsmount_lock); |
| 1968 | return retval; | 1958 | return error; |
| 1969 | 1959 | ||
| 1970 | global_root: | 1960 | global_root: |
| 1971 | retval += 1; /* hit the slash */ | 1961 | /* |
| 1972 | if (prepend_name(&retval, &buflen, &dentry->d_name) != 0) | 1962 | * Filesystems needing to implement special "root names" |
| 1973 | goto Elong; | 1963 | * should do so with ->d_dname() |
| 1964 | */ | ||
| 1965 | if (IS_ROOT(dentry) && | ||
| 1966 | (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) { | ||
| 1967 | WARN(1, "Root dentry has weird name <%.*s>\n", | ||
| 1968 | (int) dentry->d_name.len, dentry->d_name.name); | ||
| 1969 | } | ||
| 1974 | root->mnt = vfsmnt; | 1970 | root->mnt = vfsmnt; |
| 1975 | root->dentry = dentry; | 1971 | root->dentry = dentry; |
| 1976 | goto out; | 1972 | goto out; |
| 1973 | } | ||
| 1977 | 1974 | ||
| 1978 | Elong: | 1975 | /** |
| 1979 | retval = ERR_PTR(-ENAMETOOLONG); | 1976 | * __d_path - return the path of a dentry |
| 1980 | goto out; | 1977 | * @path: the dentry/vfsmount to report |
| 1978 | * @root: root vfsmnt/dentry (may be modified by this function) | ||
| 1979 | * @buffer: buffer to return value in | ||
| 1980 | * @buflen: buffer length | ||
| 1981 | * | ||
| 1982 | * Convert a dentry into an ASCII path name. | ||
| 1983 | * | ||
| 1984 | * Returns a pointer into the buffer or an error code if the | ||
| 1985 | * path was too long. | ||
| 1986 | * | ||
| 1987 | * "buflen" should be positive. Caller holds the dcache_lock. | ||
| 1988 | * | ||
| 1989 | * If path is not reachable from the supplied root, then the value of | ||
| 1990 | * root is changed (without modifying refcounts). | ||
| 1991 | */ | ||
| 1992 | char *__d_path(const struct path *path, struct path *root, | ||
| 1993 | char *buf, int buflen) | ||
| 1994 | { | ||
| 1995 | char *res = buf + buflen; | ||
| 1996 | int error; | ||
| 1997 | |||
| 1998 | prepend(&res, &buflen, "\0", 1); | ||
| 1999 | error = prepend_path(path, root, &res, &buflen); | ||
| 2000 | if (error) | ||
| 2001 | return ERR_PTR(error); | ||
| 2002 | |||
| 2003 | return res; | ||
| 2004 | } | ||
| 2005 | |||
| 2006 | /* | ||
| 2007 | * same as __d_path but appends "(deleted)" for unlinked files. | ||
| 2008 | */ | ||
| 2009 | static int path_with_deleted(const struct path *path, struct path *root, | ||
| 2010 | char **buf, int *buflen) | ||
| 2011 | { | ||
| 2012 | prepend(buf, buflen, "\0", 1); | ||
| 2013 | if (d_unlinked(path->dentry)) { | ||
| 2014 | int error = prepend(buf, buflen, " (deleted)", 10); | ||
| 2015 | if (error) | ||
| 2016 | return error; | ||
| 2017 | } | ||
| 2018 | |||
| 2019 | return prepend_path(path, root, buf, buflen); | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | static int prepend_unreachable(char **buffer, int *buflen) | ||
| 2023 | { | ||
| 2024 | return prepend(buffer, buflen, "(unreachable)", 13); | ||
| 1981 | } | 2025 | } |
| 1982 | 2026 | ||
| 1983 | /** | 2027 | /** |
| @@ -1998,9 +2042,10 @@ Elong: | |||
| 1998 | */ | 2042 | */ |
| 1999 | char *d_path(const struct path *path, char *buf, int buflen) | 2043 | char *d_path(const struct path *path, char *buf, int buflen) |
| 2000 | { | 2044 | { |
| 2001 | char *res; | 2045 | char *res = buf + buflen; |
| 2002 | struct path root; | 2046 | struct path root; |
| 2003 | struct path tmp; | 2047 | struct path tmp; |
| 2048 | int error; | ||
| 2004 | 2049 | ||
| 2005 | /* | 2050 | /* |
| 2006 | * We have various synthetic filesystems that never get mounted. On | 2051 | * We have various synthetic filesystems that never get mounted. On |
| @@ -2012,19 +2057,51 @@ char *d_path(const struct path *path, char *buf, int buflen) | |||
| 2012 | if (path->dentry->d_op && path->dentry->d_op->d_dname) | 2057 | if (path->dentry->d_op && path->dentry->d_op->d_dname) |
| 2013 | return path->dentry->d_op->d_dname(path->dentry, buf, buflen); | 2058 | return path->dentry->d_op->d_dname(path->dentry, buf, buflen); |
| 2014 | 2059 | ||
| 2015 | read_lock(¤t->fs->lock); | 2060 | get_fs_root(current->fs, &root); |
| 2016 | root = current->fs->root; | ||
| 2017 | path_get(&root); | ||
| 2018 | read_unlock(¤t->fs->lock); | ||
| 2019 | spin_lock(&dcache_lock); | 2061 | spin_lock(&dcache_lock); |
| 2020 | tmp = root; | 2062 | tmp = root; |
| 2021 | res = __d_path(path, &tmp, buf, buflen); | 2063 | error = path_with_deleted(path, &tmp, &res, &buflen); |
| 2064 | if (error) | ||
| 2065 | res = ERR_PTR(error); | ||
| 2022 | spin_unlock(&dcache_lock); | 2066 | spin_unlock(&dcache_lock); |
| 2023 | path_put(&root); | 2067 | path_put(&root); |
| 2024 | return res; | 2068 | return res; |
| 2025 | } | 2069 | } |
| 2026 | EXPORT_SYMBOL(d_path); | 2070 | EXPORT_SYMBOL(d_path); |
| 2027 | 2071 | ||
| 2072 | /** | ||
| 2073 | * d_path_with_unreachable - return the path of a dentry | ||
| 2074 | * @path: path to report | ||
| 2075 | * @buf: buffer to return value in | ||
| 2076 | * @buflen: buffer length | ||
| 2077 | * | ||
| 2078 | * The difference from d_path() is that this prepends "(unreachable)" | ||
| 2079 | * to paths which are unreachable from the current process' root. | ||
| 2080 | */ | ||
| 2081 | char *d_path_with_unreachable(const struct path *path, char *buf, int buflen) | ||
| 2082 | { | ||
| 2083 | char *res = buf + buflen; | ||
| 2084 | struct path root; | ||
| 2085 | struct path tmp; | ||
| 2086 | int error; | ||
| 2087 | |||
| 2088 | if (path->dentry->d_op && path->dentry->d_op->d_dname) | ||
| 2089 | return path->dentry->d_op->d_dname(path->dentry, buf, buflen); | ||
| 2090 | |||
| 2091 | get_fs_root(current->fs, &root); | ||
| 2092 | spin_lock(&dcache_lock); | ||
| 2093 | tmp = root; | ||
| 2094 | error = path_with_deleted(path, &tmp, &res, &buflen); | ||
| 2095 | if (!error && !path_equal(&tmp, &root)) | ||
| 2096 | error = prepend_unreachable(&res, &buflen); | ||
| 2097 | spin_unlock(&dcache_lock); | ||
| 2098 | path_put(&root); | ||
| 2099 | if (error) | ||
| 2100 | res = ERR_PTR(error); | ||
| 2101 | |||
| 2102 | return res; | ||
| 2103 | } | ||
| 2104 | |||
| 2028 | /* | 2105 | /* |
| 2029 | * Helper function for dentry_operations.d_dname() members | 2106 | * Helper function for dentry_operations.d_dname() members |
| 2030 | */ | 2107 | */ |
| @@ -2049,16 +2126,12 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen, | |||
| 2049 | /* | 2126 | /* |
| 2050 | * Write full pathname from the root of the filesystem into the buffer. | 2127 | * Write full pathname from the root of the filesystem into the buffer. |
| 2051 | */ | 2128 | */ |
| 2052 | char *dentry_path(struct dentry *dentry, char *buf, int buflen) | 2129 | char *__dentry_path(struct dentry *dentry, char *buf, int buflen) |
| 2053 | { | 2130 | { |
| 2054 | char *end = buf + buflen; | 2131 | char *end = buf + buflen; |
| 2055 | char *retval; | 2132 | char *retval; |
| 2056 | 2133 | ||
| 2057 | spin_lock(&dcache_lock); | ||
| 2058 | prepend(&end, &buflen, "\0", 1); | 2134 | prepend(&end, &buflen, "\0", 1); |
| 2059 | if (d_unlinked(dentry) && | ||
| 2060 | (prepend(&end, &buflen, "//deleted", 9) != 0)) | ||
| 2061 | goto Elong; | ||
| 2062 | if (buflen < 1) | 2135 | if (buflen < 1) |
| 2063 | goto Elong; | 2136 | goto Elong; |
| 2064 | /* Get '/' right */ | 2137 | /* Get '/' right */ |
| @@ -2076,7 +2149,28 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen) | |||
| 2076 | retval = end; | 2149 | retval = end; |
| 2077 | dentry = parent; | 2150 | dentry = parent; |
| 2078 | } | 2151 | } |
| 2152 | return retval; | ||
| 2153 | Elong: | ||
| 2154 | return ERR_PTR(-ENAMETOOLONG); | ||
| 2155 | } | ||
| 2156 | EXPORT_SYMBOL(__dentry_path); | ||
| 2157 | |||
| 2158 | char *dentry_path(struct dentry *dentry, char *buf, int buflen) | ||
| 2159 | { | ||
| 2160 | char *p = NULL; | ||
| 2161 | char *retval; | ||
| 2162 | |||
| 2163 | spin_lock(&dcache_lock); | ||
| 2164 | if (d_unlinked(dentry)) { | ||
| 2165 | p = buf + buflen; | ||
| 2166 | if (prepend(&p, &buflen, "//deleted", 10) != 0) | ||
| 2167 | goto Elong; | ||
| 2168 | buflen++; | ||
| 2169 | } | ||
| 2170 | retval = __dentry_path(dentry, buf, buflen); | ||
| 2079 | spin_unlock(&dcache_lock); | 2171 | spin_unlock(&dcache_lock); |
| 2172 | if (!IS_ERR(retval) && p) | ||
| 2173 | *p = '/'; /* restore '/' overriden with '\0' */ | ||
| 2080 | return retval; | 2174 | return retval; |
| 2081 | Elong: | 2175 | Elong: |
| 2082 | spin_unlock(&dcache_lock); | 2176 | spin_unlock(&dcache_lock); |
| @@ -2110,27 +2204,30 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) | |||
| 2110 | if (!page) | 2204 | if (!page) |
| 2111 | return -ENOMEM; | 2205 | return -ENOMEM; |
| 2112 | 2206 | ||
| 2113 | read_lock(¤t->fs->lock); | 2207 | get_fs_root_and_pwd(current->fs, &root, &pwd); |
| 2114 | pwd = current->fs->pwd; | ||
| 2115 | path_get(&pwd); | ||
| 2116 | root = current->fs->root; | ||
| 2117 | path_get(&root); | ||
| 2118 | read_unlock(¤t->fs->lock); | ||
| 2119 | 2208 | ||
| 2120 | error = -ENOENT; | 2209 | error = -ENOENT; |
| 2121 | spin_lock(&dcache_lock); | 2210 | spin_lock(&dcache_lock); |
| 2122 | if (!d_unlinked(pwd.dentry)) { | 2211 | if (!d_unlinked(pwd.dentry)) { |
| 2123 | unsigned long len; | 2212 | unsigned long len; |
| 2124 | struct path tmp = root; | 2213 | struct path tmp = root; |
| 2125 | char * cwd; | 2214 | char *cwd = page + PAGE_SIZE; |
| 2215 | int buflen = PAGE_SIZE; | ||
| 2126 | 2216 | ||
| 2127 | cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE); | 2217 | prepend(&cwd, &buflen, "\0", 1); |
| 2218 | error = prepend_path(&pwd, &tmp, &cwd, &buflen); | ||
| 2128 | spin_unlock(&dcache_lock); | 2219 | spin_unlock(&dcache_lock); |
| 2129 | 2220 | ||
| 2130 | error = PTR_ERR(cwd); | 2221 | if (error) |
| 2131 | if (IS_ERR(cwd)) | ||
| 2132 | goto out; | 2222 | goto out; |
| 2133 | 2223 | ||
| 2224 | /* Unreachable from current root */ | ||
| 2225 | if (!path_equal(&tmp, &root)) { | ||
| 2226 | error = prepend_unreachable(&cwd, &buflen); | ||
| 2227 | if (error) | ||
| 2228 | goto out; | ||
| 2229 | } | ||
| 2230 | |||
| 2134 | error = -ERANGE; | 2231 | error = -ERANGE; |
| 2135 | len = PAGE_SIZE + page - cwd; | 2232 | len = PAGE_SIZE + page - cwd; |
| 2136 | if (len <= size) { | 2233 | if (len <= size) { |
