[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1296328099-23263-4-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
Date: Sun, 30 Jan 2011 00:38:06 +0530
From: "Aneesh Kumar K.V" <aneesh.kumar@...ux.vnet.ibm.com>
To: hch@...radead.org, viro@...iv.linux.org.uk, adilger@....com,
corbet@....net, neilb@...e.de, npiggin@...nel.dk,
hooanon05@...oo.co.jp, bfields@...ldses.org, miklos@...redi.hu
Cc: linux-fsdevel@...r.kernel.org, sfrench@...ibm.com,
philippe.deniel@....FR, linux-kernel@...r.kernel.org,
"Aneesh Kumar K.V" <aneesh.kumar@...ux.vnet.ibm.com>
Subject: [PATCH -V26 03/16] vfs: Add open by file handle support
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@...ux.vnet.ibm.com>
---
fs/compat.c | 11 ++
fs/exportfs/expfs.c | 2 +
fs/namei.c | 230 ++++++++++++++++++++++++++++++++++++++++++---
fs/open.c | 32 ++++++-
include/linux/fs.h | 10 ++-
include/linux/namei.h | 1 +
include/linux/syscalls.h | 3 +
7 files changed, 268 insertions(+), 21 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c
index f6fd0a0..e8436f3 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -2308,3 +2308,14 @@ asmlinkage long compat_sys_timerfd_gettime(int ufd,
}
#endif /* CONFIG_TIMERFD */
+
+/*
+ * Exactly like fs/open.c:sys_open_by_handle_at(), except that it
+ * doesn't set the O_LARGEFILE flag.
+ */
+asmlinkage long
+compat_sys_open_by_handle_at(int mountdirfd,
+ struct file_handle __user *handle, int flags)
+{
+ return do_handle_open(mountdirfd, handle, flags);
+}
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index cfe5573..b05acb7 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -374,6 +374,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
/*
* Try to get any dentry for the given file handle from the filesystem.
*/
+ if (!nop || !nop->fh_to_dentry)
+ return ERR_PTR(-ESTALE);
result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
if (!result)
result = ERR_PTR(-ESTALE);
diff --git a/fs/namei.c b/fs/namei.c
index 7d77f24..6b0536f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -32,6 +32,7 @@
#include <linux/fcntl.h>
#include <linux/device_cgroup.h>
#include <linux/fs_struct.h>
+#include <linux/exportfs.h>
#include <asm/uaccess.h>
#include "internal.h"
@@ -1697,6 +1698,29 @@ out_fail:
return retval;
}
+struct vfsmount *get_vfsmount_from_fd(int fd)
+{
+ int fput_needed;
+ struct path path;
+ struct file *filep;
+
+ if (fd == AT_FDCWD) {
+ struct fs_struct *fs = current->fs;
+ spin_lock(&fs->lock);
+ path = fs->pwd;
+ mntget(path.mnt);
+ spin_unlock(&fs->lock);
+ } else {
+ filep = fget_light(fd, &fput_needed);
+ if (!filep)
+ return ERR_PTR(-EBADF);
+ path = filep->f_path;
+ mntget(path.mnt);
+ fput_light(filep, fput_needed);
+ }
+ return path.mnt;
+}
+
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
static int do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
@@ -2218,26 +2242,30 @@ static int open_will_truncate(int flag, struct inode *inode)
return (flag & O_TRUNC);
}
-static struct file *finish_open(struct nameidata *nd,
+static struct file *finish_open(struct file *filp, struct path *path,
int open_flag, int acc_mode)
{
- struct file *filp;
- int will_truncate;
int error;
+ int will_truncate;
- will_truncate = open_will_truncate(open_flag, nd->path.dentry->d_inode);
+ will_truncate = open_will_truncate(open_flag, path->dentry->d_inode);
if (will_truncate) {
- error = mnt_want_write(nd->path.mnt);
+ error = mnt_want_write(path->mnt);
if (error)
goto exit;
}
- error = may_open(&nd->path, acc_mode, open_flag);
+ error = may_open(path, acc_mode, open_flag);
if (error) {
if (will_truncate)
- mnt_drop_write(nd->path.mnt);
+ mnt_drop_write(path->mnt);
goto exit;
}
- filp = nameidata_to_filp(nd);
+ /* Has the filesystem initialised the file for us? */
+ if (filp->f_path.dentry == NULL) {
+ path_get(path);
+ filp = __dentry_open(path->dentry, path->mnt, filp,
+ NULL, current_cred());
+ }
if (!IS_ERR(filp)) {
error = ima_file_check(filp, acc_mode);
if (error) {
@@ -2260,14 +2288,18 @@ static struct file *finish_open(struct nameidata *nd,
* on its behalf.
*/
if (will_truncate)
- mnt_drop_write(nd->path.mnt);
- path_put(&nd->path);
+ mnt_drop_write(path->mnt);
+ path_put(path);
return filp;
exit:
- if (!IS_ERR(nd->intent.open.file))
- release_open_intent(nd);
- path_put(&nd->path);
+ if (!IS_ERR(filp)) {
+ if (filp->f_path.dentry == NULL)
+ put_filp(filp);
+ else
+ fput(filp);
+ }
+ path_put(path);
return ERR_PTR(error);
}
@@ -2381,7 +2413,8 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
if (S_ISDIR(nd->inode->i_mode))
goto exit;
ok:
- filp = finish_open(nd, open_flag, acc_mode);
+ filp = finish_open(nd->intent.open.file, &nd->path,
+ open_flag, acc_mode);
return filp;
exit_mutex_unlock:
@@ -2476,9 +2509,9 @@ struct file *do_filp_open(int dfd, const char *pathname,
goto out_path;
}
audit_inode(pathname, nd.path.dentry);
- filp = finish_open(&nd, open_flag, acc_mode);
+ filp = finish_open(nd.intent.open.file, &nd.path,
+ open_flag, acc_mode);
return filp;
-
creat:
/* OK, have to create the file. Find the parent. */
error = path_init_rcu(dfd, pathname,
@@ -2583,6 +2616,171 @@ struct file *filp_open(const char *filename, int flags, int mode)
}
EXPORT_SYMBOL(filp_open);
+#ifdef CONFIG_EXPORTFS
+static int vfs_dentry_acceptable(void *context, struct dentry *dentry)
+{
+ return 1;
+}
+
+static int do_handle_to_path(int mountdirfd, struct file_handle *handle,
+ struct path *path)
+{
+ int retval = 0;
+ int handle_dwords;
+
+ path->mnt = get_vfsmount_from_fd(mountdirfd);
+ if (IS_ERR(path->mnt)) {
+ retval = PTR_ERR(path->mnt);
+ goto out_err;
+ }
+ /* change the handle size to multiple of sizeof(u32) */
+ handle_dwords = handle->handle_bytes >> 2;
+ path->dentry = exportfs_decode_fh(path->mnt,
+ (struct fid *)handle->f_handle,
+ handle_dwords, handle->handle_type,
+ vfs_dentry_acceptable, NULL);
+ if (IS_ERR(path->dentry)) {
+ retval = PTR_ERR(path->dentry);
+ goto out_mnt;
+ }
+ return 0;
+out_mnt:
+ mntput(path->mnt);
+out_err:
+ return retval;
+}
+
+int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
+ struct path *path)
+{
+ int retval = 0;
+ struct file_handle f_handle;
+ struct file_handle *handle = NULL;
+
+ /*
+ * With handle we don't look at the execute bit on the
+ * the directory. Ideally we would like CAP_DAC_SEARCH.
+ * But we don't have that
+ */
+ if (!capable(CAP_DAC_READ_SEARCH)) {
+ retval = -EPERM;
+ goto out_err;
+ }
+ if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
+ retval = -EFAULT;
+ goto out_err;
+ }
+ if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
+ (f_handle.handle_bytes == 0)) {
+ retval = -EINVAL;
+ goto out_err;
+ }
+ handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
+ GFP_KERNEL);
+ if (!handle) {
+ retval = -ENOMEM;
+ goto out_err;
+ }
+ /* copy the full handle */
+ if (copy_from_user(handle, ufh,
+ sizeof(struct file_handle) +
+ f_handle.handle_bytes)) {
+ retval = -EFAULT;
+ goto out_handle;
+ }
+
+ retval = do_handle_to_path(mountdirfd, handle, path);
+
+out_handle:
+ kfree(handle);
+out_err:
+ return retval;
+}
+#else
+int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
+ struct path *path)
+{
+ return -ENOSYS;
+}
+#endif
+
+long do_handle_open(int mountdirfd,
+ struct file_handle __user *ufh, int open_flag)
+{
+ long retval = 0;
+ int fd, acc_mode;
+ struct path path;
+ struct file *filp;
+
+ /* can't use O_CREATE with open_by_handle */
+ if (open_flag & O_CREAT) {
+ retval = -EINVAL;
+ goto out_err;
+ }
+ retval = handle_to_path(mountdirfd, ufh, &path);
+ if (retval)
+ goto out_err;
+
+ if ((open_flag & O_DIRECTORY) &&
+ !S_ISDIR(path.dentry->d_inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto out_path;
+ }
+
+ /* Must never be set by userspace */
+ open_flag &= ~FMODE_NONOTIFY;
+
+ /*
+ * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only
+ * check for O_DSYNC if the need any syncing at all we enforce it's
+ * always set instead of having to deal with possibly weird behaviour
+ * for malicious applications setting only __O_SYNC.
+ */
+ if (open_flag & __O_SYNC)
+ open_flag |= O_DSYNC;
+
+ acc_mode = MAY_OPEN | ACC_MODE(open_flag);
+
+ /* O_TRUNC implies we need access checks for write permissions */
+ if (open_flag & O_TRUNC)
+ acc_mode |= MAY_WRITE;
+ /*
+ * Allow the LSM permission hook to distinguish append
+ * access from general write access.
+ */
+ if (open_flag & O_APPEND)
+ acc_mode |= MAY_APPEND;
+
+ fd = get_unused_fd_flags(open_flag);
+ if (fd < 0) {
+ retval = fd;
+ goto out_path;
+ }
+ filp = get_empty_filp();
+ if (!filp) {
+ retval = -ENFILE;
+ goto out_free_fd;
+ }
+ filp->f_flags = open_flag;
+ filp = finish_open(filp, &path, open_flag, acc_mode);
+ if (IS_ERR(filp)) {
+ put_unused_fd(fd);
+ retval = PTR_ERR(filp);
+ } else {
+ retval = fd;
+ fsnotify_open(filp);
+ fd_install(fd, filp);
+ }
+ return retval;
+
+out_free_fd:
+ put_unused_fd(fd);
+out_path:
+ path_put(&path);
+out_err:
+ return retval;
+}
+
/**
* lookup_create - lookup a dentry, creating it if it doesn't exist
* @nd: nameidata info
diff --git a/fs/open.c b/fs/open.c
index d12723a..1ec2623 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -657,10 +657,10 @@ static inline int __get_file_write_access(struct inode *inode,
return error;
}
-static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
- struct file *f,
- int (*open)(struct inode *, struct file *),
- const struct cred *cred)
+struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *f,
+ int (*open)(struct inode *, struct file *),
+ const struct cred *cred)
{
struct inode *inode;
int error;
@@ -1175,3 +1175,27 @@ err_out:
return ret;
}
#endif
+
+/**
+ * sys_open_by_handle_at: Open the file handle
+ * @mountdirfd: directory file descriptor
+ * @handle: file handle to be opened
+ * @flag: open flags.
+ *
+ * @mountdirfd indicate the directory file descriptor
+ * of the mount point. file handle is decoded relative
+ * to the vfsmount pointed by the @mountdirfd. @flags
+ * value is same as the open(2) flags.
+ */
+SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
+ struct file_handle __user *, handle,
+ int, flags)
+{
+ long ret;
+
+ if (force_o_largefile())
+ flags |= O_LARGEFILE;
+
+ ret = do_handle_open(mountdirfd, handle, flags);
+ return ret;
+}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 9fbb0e9..1aed36f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1996,6 +1996,10 @@ extern int do_fallocate(struct file *file, int mode, loff_t offset,
extern long do_sys_open(int dfd, const char __user *filename, int flags,
int mode);
extern struct file *filp_open(const char *, int, int);
+struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *f,
+ int (*open)(struct inode *, struct file *),
+ const struct cred *cred);
extern struct file * dentry_open(struct dentry *, struct vfsmount *, int,
const struct cred *);
extern int filp_close(struct file *, fl_owner_t id);
@@ -2213,11 +2217,15 @@ extern void free_write_pipe(struct file *);
extern struct file *do_filp_open(int dfd, const char *pathname,
int open_flag, int mode, int acc_mode);
+extern int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
+ struct path *path);
+extern long do_handle_open(int mountdirfd,
+ struct file_handle __user *ufh, int open_flag);
extern int may_open(struct path *, int, int);
extern int kernel_read(struct file *, loff_t, char *, unsigned long);
extern struct file * open_exec(const char *);
-
+
/* fs/dcache.c -- generic fs support functions */
extern int is_subdir(struct dentry *, struct dentry *);
extern int path_is_under(struct path *, struct path *);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index f276d4f..3ec030a 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -70,6 +70,7 @@ extern int user_path_at(int, const char __user *, unsigned, struct path *);
#define user_path_dir(name, path) \
user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path)
+extern struct vfsmount *get_vfsmount_from_fd(int);
extern int kern_path(const char *, unsigned, struct path *);
extern int path_lookup(const char *, unsigned, struct nameidata *);
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index e1ef441..a4734c5 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -834,4 +834,7 @@ asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg);
asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
struct file_handle __user *handle,
int __user *mnt_id, int flag);
+asmlinkage long sys_open_by_handle_at(int mountdirfd,
+ struct file_handle __user *handle,
+ int flags);
#endif
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists