summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-08-11 09:23:32 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-11 09:23:32 -0700
commit5af568cbd55f60b5a1d174f621b273e4f585dc35 (patch)
tree7cdc1afeb267519133a29ac595df21278c556c8c /fs
parent062e27ec1b49d12bdb1ecc94d74b5fee5a5775db (diff)
parent66a362a2aa8ffa72670259fa15e2a77a01cc2217 (diff)
downloadlwn-5af568cbd55f60b5a1d174f621b273e4f585dc35.tar.gz
lwn-5af568cbd55f60b5a1d174f621b273e4f585dc35.zip
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: isofs: Fix lseek() to position beyond 4 GB vfs: remove unused MNT_STRICTATIME vfs: show unreachable paths in getcwd and proc vfs: only add " (deleted)" where necessary vfs: add prepend_path() helper vfs: __d_path: dont prepend the name of the root dentry ia64: perfmon: add d_dname method vfs: add helpers to get root and pwd cachefiles: use path_get instead of lone dget fs/sysv/super.c: add support for non-PDP11 v7 filesystems V7: Adjust sanity checks for some volumes Add v7 alias v9fs: fixup for inode_setattr being removed Manual merge to take Al's version of the fs/sysv/super.c file: it merged cleanly, but Al had removed an unnecessary header include, so his side was better.
Diffstat (limited to 'fs')
-rw-r--r--fs/9p/vfs_inode.c15
-rw-r--r--fs/cachefiles/daemon.c32
-rw-r--r--fs/dcache.c188
-rw-r--r--fs/fs_struct.c7
-rw-r--r--fs/isofs/inode.c7
-rw-r--r--fs/namei.c15
-rw-r--r--fs/namespace.c6
-rw-r--r--fs/proc/base.c24
-rw-r--r--fs/sysv/super.c1
9 files changed, 180 insertions, 115 deletions
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index d97c34a24f7a..c7c23eab9440 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -1263,10 +1263,19 @@ static int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
return PTR_ERR(fid);
retval = p9_client_setattr(fid, &p9attr);
- if (retval >= 0)
- retval = inode_setattr(dentry->d_inode, iattr);
+ if (retval < 0)
+ return retval;
- return retval;
+ if ((iattr->ia_valid & ATTR_SIZE) &&
+ iattr->ia_size != i_size_read(dentry->d_inode)) {
+ retval = vmtruncate(dentry->d_inode, iattr->ia_size);
+ if (retval)
+ return retval;
+ }
+
+ setattr_copy(dentry->d_inode, iattr);
+ mark_inode_dirty(dentry->d_inode);
+ return 0;
}
/**
diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c
index 24eb0d37241a..727caedcdd92 100644
--- a/fs/cachefiles/daemon.c
+++ b/fs/cachefiles/daemon.c
@@ -552,8 +552,7 @@ static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *args)
*/
static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args)
{
- struct fs_struct *fs;
- struct dentry *dir;
+ struct path path;
const struct cred *saved_cred;
int ret;
@@ -573,24 +572,21 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args)
}
/* extract the directory dentry from the cwd */
- fs = current->fs;
- read_lock(&fs->lock);
- dir = dget(fs->pwd.dentry);
- read_unlock(&fs->lock);
+ get_fs_pwd(current->fs, &path);
- if (!S_ISDIR(dir->d_inode->i_mode))
+ if (!S_ISDIR(path.dentry->d_inode->i_mode))
goto notdir;
cachefiles_begin_secure(cache, &saved_cred);
- ret = cachefiles_cull(cache, dir, args);
+ ret = cachefiles_cull(cache, path.dentry, args);
cachefiles_end_secure(cache, saved_cred);
- dput(dir);
+ path_put(&path);
_leave(" = %d", ret);
return ret;
notdir:
- dput(dir);
+ path_put(&path);
kerror("cull command requires dirfd to be a directory");
return -ENOTDIR;
@@ -628,8 +624,7 @@ inval:
*/
static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args)
{
- struct fs_struct *fs;
- struct dentry *dir;
+ struct path path;
const struct cred *saved_cred;
int ret;
@@ -649,24 +644,21 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args)
}
/* extract the directory dentry from the cwd */
- fs = current->fs;
- read_lock(&fs->lock);
- dir = dget(fs->pwd.dentry);
- read_unlock(&fs->lock);
+ get_fs_pwd(current->fs, &path);
- if (!S_ISDIR(dir->d_inode->i_mode))
+ if (!S_ISDIR(path.dentry->d_inode->i_mode))
goto notdir;
cachefiles_begin_secure(cache, &saved_cred);
- ret = cachefiles_check_in_use(cache, dir, args);
+ ret = cachefiles_check_in_use(cache, path.dentry, args);
cachefiles_end_secure(cache, saved_cred);
- dput(dir);
+ path_put(&path);
//_leave(" = %d", ret);
return ret;
notdir:
- dput(dir);
+ path_put(&path);
kerror("inuse command requires dirfd to be a directory");
return -ENOTDIR;
diff --git a/fs/dcache.c b/fs/dcache.c
index 9f2c13417969..166d35d56868 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1905,48 +1905,30 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
}
/**
- * __d_path - return the path of a dentry
+ * Prepend path string to a buffer
+ *
* @path: the dentry/vfsmount to report
* @root: root vfsmnt/dentry (may be modified by this function)
- * @buffer: buffer to return value in
- * @buflen: buffer length
+ * @buffer: pointer to the end of the buffer
+ * @buflen: pointer to buffer length
*
- * 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 a pointer into the buffer or an error code if the
- * path was too long.
- *
- * "buflen" should be positive. Caller holds the dcache_lock.
+ * Caller holds the dcache_lock.
*
* If path is not reachable from the supplied root, then the value of
* root is changed (without modifying refcounts).
*/
-char *__d_path(const struct path *path, struct path *root,
- char *buffer, int buflen)
+static int prepend_path(const struct path *path, struct path *root,
+ char **buffer, int *buflen)
{
struct dentry *dentry = path->dentry;
struct vfsmount *vfsmnt = path->mnt;
- char *end = buffer + buflen;
- char *retval;
+ bool slash = false;
+ int error = 0;
spin_lock(&vfsmount_lock);
- prepend(&end, &buflen, "\0", 1);
- if (d_unlinked(dentry) &&
- (prepend(&end, &buflen, " (deleted)", 10) != 0))
- goto Elong;
-
- if (buflen < 1)
- goto Elong;
- /* Get '/' right */
- retval = end-1;
- *retval = '/';
-
- for (;;) {
+ while (dentry != root->dentry || vfsmnt != root->mnt) {
struct dentry * parent;
- if (dentry == root->dentry && vfsmnt == root->mnt)
- break;
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
/* Global root? */
if (vfsmnt->mnt_parent == vfsmnt) {
@@ -1958,28 +1940,88 @@ char *__d_path(const struct path *path, struct path *root,
}
parent = dentry->d_parent;
prefetch(parent);
- if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
- (prepend(&end, &buflen, "/", 1) != 0))
- goto Elong;
- retval = end;
+ error = prepend_name(buffer, buflen, &dentry->d_name);
+ if (!error)
+ error = prepend(buffer, buflen, "/", 1);
+ if (error)
+ break;
+
+ slash = true;
dentry = parent;
}
out:
+ if (!error && !slash)
+ error = prepend(buffer, buflen, "/", 1);
+
spin_unlock(&vfsmount_lock);
- return retval;
+ return error;
global_root:
- retval += 1; /* hit the slash */
- if (prepend_name(&retval, &buflen, &dentry->d_name) != 0)
- goto Elong;
+ /*
+ * Filesystems needing to implement special "root names"
+ * should do so with ->d_dname()
+ */
+ if (IS_ROOT(dentry) &&
+ (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) {
+ WARN(1, "Root dentry has weird name <%.*s>\n",
+ (int) dentry->d_name.len, dentry->d_name.name);
+ }
root->mnt = vfsmnt;
root->dentry = dentry;
goto out;
+}
-Elong:
- retval = ERR_PTR(-ENAMETOOLONG);
- goto out;
+/**
+ * __d_path - return the path of a dentry
+ * @path: the dentry/vfsmount to report
+ * @root: root vfsmnt/dentry (may be modified by this function)
+ * @buffer: buffer to return value in
+ * @buflen: buffer length
+ *
+ * Convert a dentry into an ASCII path name.
+ *
+ * Returns a pointer into the buffer or an error code if the
+ * path was too long.
+ *
+ * "buflen" should be positive. Caller holds the dcache_lock.
+ *
+ * If path is not reachable from the supplied root, then the value of
+ * root is changed (without modifying refcounts).
+ */
+char *__d_path(const struct path *path, struct path *root,
+ char *buf, int buflen)
+{
+ char *res = buf + buflen;
+ int error;
+
+ prepend(&res, &buflen, "\0", 1);
+ error = prepend_path(path, root, &res, &buflen);
+ if (error)
+ return ERR_PTR(error);
+
+ return res;
+}
+
+/*
+ * same as __d_path but appends "(deleted)" for unlinked files.
+ */
+static int path_with_deleted(const struct path *path, struct path *root,
+ char **buf, int *buflen)
+{
+ prepend(buf, buflen, "\0", 1);
+ if (d_unlinked(path->dentry)) {
+ int error = prepend(buf, buflen, " (deleted)", 10);
+ if (error)
+ return error;
+ }
+
+ return prepend_path(path, root, buf, buflen);
+}
+
+static int prepend_unreachable(char **buffer, int *buflen)
+{
+ return prepend(buffer, buflen, "(unreachable)", 13);
}
/**
@@ -2000,9 +2042,10 @@ Elong:
*/
char *d_path(const struct path *path, char *buf, int buflen)
{
- char *res;
+ char *res = buf + buflen;
struct path root;
struct path tmp;
+ int error;
/*
* We have various synthetic filesystems that never get mounted. On
@@ -2014,19 +2057,51 @@ char *d_path(const struct path *path, char *buf, int buflen)
if (path->dentry->d_op && path->dentry->d_op->d_dname)
return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
- read_lock(&current->fs->lock);
- root = current->fs->root;
- path_get(&root);
- read_unlock(&current->fs->lock);
+ get_fs_root(current->fs, &root);
spin_lock(&dcache_lock);
tmp = root;
- res = __d_path(path, &tmp, buf, buflen);
+ error = path_with_deleted(path, &tmp, &res, &buflen);
+ if (error)
+ res = ERR_PTR(error);
spin_unlock(&dcache_lock);
path_put(&root);
return res;
}
EXPORT_SYMBOL(d_path);
+/**
+ * d_path_with_unreachable - return the path of a dentry
+ * @path: path to report
+ * @buf: buffer to return value in
+ * @buflen: buffer length
+ *
+ * The difference from d_path() is that this prepends "(unreachable)"
+ * to paths which are unreachable from the current process' root.
+ */
+char *d_path_with_unreachable(const struct path *path, char *buf, int buflen)
+{
+ char *res = buf + buflen;
+ struct path root;
+ struct path tmp;
+ int error;
+
+ if (path->dentry->d_op && path->dentry->d_op->d_dname)
+ return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
+
+ get_fs_root(current->fs, &root);
+ spin_lock(&dcache_lock);
+ tmp = root;
+ error = path_with_deleted(path, &tmp, &res, &buflen);
+ if (!error && !path_equal(&tmp, &root))
+ error = prepend_unreachable(&res, &buflen);
+ spin_unlock(&dcache_lock);
+ path_put(&root);
+ if (error)
+ res = ERR_PTR(error);
+
+ return res;
+}
+
/*
* Helper function for dentry_operations.d_dname() members
*/
@@ -2129,27 +2204,30 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
if (!page)
return -ENOMEM;
- read_lock(&current->fs->lock);
- pwd = current->fs->pwd;
- path_get(&pwd);
- root = current->fs->root;
- path_get(&root);
- read_unlock(&current->fs->lock);
+ get_fs_root_and_pwd(current->fs, &root, &pwd);
error = -ENOENT;
spin_lock(&dcache_lock);
if (!d_unlinked(pwd.dentry)) {
unsigned long len;
struct path tmp = root;
- char * cwd;
+ char *cwd = page + PAGE_SIZE;
+ int buflen = PAGE_SIZE;
- cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE);
+ prepend(&cwd, &buflen, "\0", 1);
+ error = prepend_path(&pwd, &tmp, &cwd, &buflen);
spin_unlock(&dcache_lock);
- error = PTR_ERR(cwd);
- if (IS_ERR(cwd))
+ if (error)
goto out;
+ /* Unreachable from current root */
+ if (!path_equal(&tmp, &root)) {
+ error = prepend_unreachable(&cwd, &buflen);
+ if (error)
+ goto out;
+ }
+
error = -ERANGE;
len = PAGE_SIZE + page - cwd;
if (len <= size) {
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index eee059052db5..1ee40eb9a2c0 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -106,12 +106,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
fs->in_exec = 0;
rwlock_init(&fs->lock);
fs->umask = old->umask;
- read_lock(&old->lock);
- fs->root = old->root;
- path_get(&old->root);
- fs->pwd = old->pwd;
- path_get(&old->pwd);
- read_unlock(&old->lock);
+ get_fs_root_and_pwd(old, &fs->root, &fs->pwd);
}
return fs;
}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 6b4dcd4f2943..5a44811b5027 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -722,7 +722,12 @@ root_found:
}
s->s_magic = ISOFS_SUPER_MAGIC;
- s->s_maxbytes = 0xffffffff; /* We can handle files up to 4 GB */
+
+ /*
+ * With multi-extent files, file size is only limited by the maximum
+ * size of a file system, which is 8 TB.
+ */
+ s->s_maxbytes = 0x80000000000LL;
/*
* The CDROM is read-only, has no nodes (devices) on it, and since
diff --git a/fs/namei.c b/fs/namei.c
index 13ff4abdbdca..17ea76bf2fbe 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -483,13 +483,8 @@ ok:
static __always_inline void set_root(struct nameidata *nd)
{
- if (!nd->root.mnt) {
- struct fs_struct *fs = current->fs;
- read_lock(&fs->lock);
- nd->root = fs->root;
- path_get(&nd->root);
- read_unlock(&fs->lock);
- }
+ if (!nd->root.mnt)
+ get_fs_root(current->fs, &nd->root);
}
static int link_path_walk(const char *, struct nameidata *);
@@ -1015,11 +1010,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei
nd->path = nd->root;
path_get(&nd->root);
} else if (dfd == AT_FDCWD) {
- struct fs_struct *fs = current->fs;
- read_lock(&fs->lock);
- nd->path = fs->pwd;
- path_get(&fs->pwd);
- read_unlock(&fs->lock);
+ get_fs_pwd(current->fs, &nd->path);
} else {
struct dentry *dentry;
diff --git a/fs/namespace.c b/fs/namespace.c
index 66c4f7e781cb..2e10cb19c5b0 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -788,7 +788,6 @@ static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
{ MNT_NOATIME, ",noatime" },
{ MNT_NODIRATIME, ",nodiratime" },
{ MNT_RELATIME, ",relatime" },
- { MNT_STRICTATIME, ",strictatime" },
{ 0, NULL }
};
const struct proc_fs_info *fs_infop;
@@ -2213,10 +2212,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
goto out1;
}
- read_lock(&current->fs->lock);
- root = current->fs->root;
- path_get(&current->fs->root);
- read_unlock(&current->fs->lock);
+ get_fs_root(current->fs, &root);
down_write(&namespace_sem);
mutex_lock(&old.dentry->d_inode->i_mutex);
error = -EINVAL;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index c806dfb24e08..a1c43e7c8a7b 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -149,18 +149,13 @@ static unsigned int pid_entry_count_dirs(const struct pid_entry *entries,
return count;
}
-static int get_fs_path(struct task_struct *task, struct path *path, bool root)
+static int get_task_root(struct task_struct *task, struct path *root)
{
- struct fs_struct *fs;
int result = -ENOENT;
task_lock(task);
- fs = task->fs;
- if (fs) {
- read_lock(&fs->lock);
- *path = root ? fs->root : fs->pwd;
- path_get(path);
- read_unlock(&fs->lock);
+ if (task->fs) {
+ get_fs_root(task->fs, root);
result = 0;
}
task_unlock(task);
@@ -173,7 +168,12 @@ static int proc_cwd_link(struct inode *inode, struct path *path)
int result = -ENOENT;
if (task) {
- result = get_fs_path(task, path, 0);
+ task_lock(task);
+ if (task->fs) {
+ get_fs_pwd(task->fs, path);
+ result = 0;
+ }
+ task_unlock(task);
put_task_struct(task);
}
return result;
@@ -185,7 +185,7 @@ static int proc_root_link(struct inode *inode, struct path *path)
int result = -ENOENT;
if (task) {
- result = get_fs_path(task, path, 1);
+ result = get_task_root(task, path);
put_task_struct(task);
}
return result;
@@ -597,7 +597,7 @@ static int mounts_open_common(struct inode *inode, struct file *file,
get_mnt_ns(ns);
}
rcu_read_unlock();
- if (ns && get_fs_path(task, &root, 1) == 0)
+ if (ns && get_task_root(task, &root) == 0)
ret = 0;
put_task_struct(task);
}
@@ -1526,7 +1526,7 @@ static int do_proc_readlink(struct path *path, char __user *buffer, int buflen)
if (!tmp)
return -ENOMEM;
- pathname = d_path(path, tmp, PAGE_SIZE);
+ pathname = d_path_with_unreachable(path, tmp, PAGE_SIZE);
len = PTR_ERR(pathname);
if (IS_ERR(pathname))
goto out;
diff --git a/fs/sysv/super.c b/fs/sysv/super.c
index 85359a8df605..a0b0cda6927e 100644
--- a/fs/sysv/super.c
+++ b/fs/sysv/super.c
@@ -24,7 +24,6 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
-#include <linux/parser.h>
#include "sysv.h"
/*