aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/dcache.c150
1 files changed, 70 insertions, 80 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index b5f61393291..d68631f18df 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1739,41 +1739,45 @@ shouldnt_be_hashed:
* @rootmnt: vfsmnt to which the root dentry belongs
* @buffer: buffer to return value in
* @buflen: buffer length
- * @fail_deleted: what to return for deleted files
*
- * Convert a dentry into an ASCII path name. If the entry has been deleted,
- * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise,
- * the the string " (deleted)" is appended. Note that this is ambiguous.
+ * Convert a dentry into an ASCII path name. If the entry has been deleted
+ * the string " (deleted)" is appended. Note that this is ambiguous.
*
- * Returns the buffer or an error code.
+ * Returns the buffer or an error code if the path was too long.
+ *
+ * "buflen" should be positive. Caller holds the dcache_lock.
*/
-static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
- struct dentry *root, struct vfsmount *rootmnt,
- char *buffer, int buflen, int fail_deleted)
+static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
+ struct dentry *root, struct vfsmount *rootmnt,
+ char *buffer, int buflen)
{
- int namelen, is_slash;
-
- if (buflen < 2)
- return ERR_PTR(-ENAMETOOLONG);
- buffer += --buflen;
- *buffer = '\0';
+ char * end = buffer+buflen;
+ char * retval;
+ int namelen;
- spin_lock(&dcache_lock);
+ *--end = '\0';
+ buflen--;
if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
- if (fail_deleted) {
- buffer = ERR_PTR(-ENOENT);
- goto out;
- }
- if (buflen < 10)
- goto Elong;
buflen -= 10;
- buffer -= 10;
- memcpy(buffer, " (deleted)", 10);
+ end -= 10;
+ if (buflen < 0)
+ goto Elong;
+ memcpy(end, " (deleted)", 10);
}
- while (dentry != root || vfsmnt != rootmnt) {
+
+ if (buflen < 1)
+ goto Elong;
+ /* Get '/' right */
+ retval = end-1;
+ *retval = '/';
+
+ for (;;) {
struct dentry * parent;
+ if (dentry == root && vfsmnt == rootmnt)
+ break;
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+ /* Global root? */
spin_lock(&vfsmount_lock);
if (vfsmnt->mnt_parent == vfsmnt) {
spin_unlock(&vfsmount_lock);
@@ -1787,60 +1791,33 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
parent = dentry->d_parent;
prefetch(parent);
namelen = dentry->d_name.len;
- if (buflen <= namelen)
- goto Elong;
buflen -= namelen + 1;
- buffer -= namelen;
- memcpy(buffer, dentry->d_name.name, namelen);
- *--buffer = '/';
+ if (buflen < 0)
+ goto Elong;
+ end -= namelen;
+ memcpy(end, dentry->d_name.name, namelen);
+ *--end = '/';
+ retval = end;
dentry = parent;
}
- /* Get '/' right */
- if (*buffer != '/')
- *--buffer = '/';
-out:
- spin_unlock(&dcache_lock);
- return buffer;
+ return retval;
global_root:
- /*
- * We went past the (vfsmount, dentry) we were looking for and have
- * either hit a root dentry, a lazily unmounted dentry, an
- * unconnected dentry, or the file is on a pseudo filesystem.
- */
namelen = dentry->d_name.len;
- is_slash = (namelen == 1 && *dentry->d_name.name == '/');
- if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) {
- /*
- * Make sure we won't return a pathname starting with '/'.
- *
- * Historically, we also glue together the root dentry and
- * remaining name for pseudo filesystems like pipefs, which
- * have the MS_NOUSER flag set. This results in pathnames
- * like "pipe:[439336]".
- */
- if (*buffer == '/') {
- buffer++;
- buflen++;
- }
- if (is_slash)
- goto out;
- }
- if (buflen < namelen)
+ buflen -= namelen;
+ if (buflen < 0)
goto Elong;
- buffer -= namelen;
- memcpy(buffer, dentry->d_name.name, namelen);
- goto out;
-
+ retval -= namelen-1; /* hit the slash */
+ memcpy(retval, dentry->d_name.name, namelen);
+ return retval;
Elong:
- buffer = ERR_PTR(-ENAMETOOLONG);
- goto out;
+ return ERR_PTR(-ENAMETOOLONG);
}
/* write full pathname into buffer and return start of pathname */
-char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
- int buflen)
+char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
+ char *buf, int buflen)
{
char *res;
struct vfsmount *rootmnt;
@@ -1850,7 +1827,9 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
rootmnt = mntget(current->fs->rootmnt);
root = dget(current->fs->root);
read_unlock(&current->fs->lock);
- res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0);
+ spin_lock(&dcache_lock);
+ res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen);
+ spin_unlock(&dcache_lock);
dput(root);
mntput(rootmnt);
return res;
@@ -1876,10 +1855,10 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
*/
asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
{
- int error, len;
+ int error;
struct vfsmount *pwdmnt, *rootmnt;
struct dentry *pwd, *root;
- char *page = (char *) __get_free_page(GFP_USER), *cwd;
+ char *page = (char *) __get_free_page(GFP_USER);
if (!page)
return -ENOMEM;
@@ -1891,18 +1870,29 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
root = dget(current->fs->root);
read_unlock(&current->fs->lock);
- cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1);
- error = PTR_ERR(cwd);
- if (IS_ERR(cwd))
- goto out;
+ error = -ENOENT;
+ /* Has the current directory has been unlinked? */
+ spin_lock(&dcache_lock);
+ if (pwd->d_parent == pwd || !d_unhashed(pwd)) {
+ unsigned long len;
+ char * cwd;
- error = -ERANGE;
- len = PAGE_SIZE + page - cwd;
- if (len <= size) {
- error = len;
- if (copy_to_user(buf, cwd, len))
- error = -EFAULT;
- }
+ cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE);
+ spin_unlock(&dcache_lock);
+
+ error = PTR_ERR(cwd);
+ if (IS_ERR(cwd))
+ goto out;
+
+ error = -ERANGE;
+ len = PAGE_SIZE + page - cwd;
+ if (len <= size) {
+ error = len;
+ if (copy_to_user(buf, cwd, len))
+ error = -EFAULT;
+ }
+ } else
+ spin_unlock(&dcache_lock);
out:
dput(pwd);