[PATCH 01/17] VFS: Implement the pioctl() system call

From: David Howells
Date: Tue Jun 16 2009 - 16:41:42 EST


From: Jacob Thebault-Spieker <summatusmentis@xxxxxxxxx>

Implement the pioctl() system call. This is used to support a number of AFS
functions, and could also be used for Coda and other filesystems.

Signed-off-by: Jacob Thebault-Spieker <summatusmentis@xxxxxxxxx>
Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

arch/x86/ia32/ia32entry.S | 1
arch/x86/include/asm/unistd_32.h | 1
arch/x86/include/asm/unistd_64.h | 2 +
arch/x86/kernel/syscall_table_32.S | 1
fs/Kconfig | 6 ++
fs/Makefile | 3 +
fs/afs/Kconfig | 1
fs/afs/Makefile | 3 +
fs/afs/dir.c | 1
fs/afs/file.c | 1
fs/afs/inode.c | 9 +++
fs/afs/internal.h | 5 ++
fs/afs/mntpt.c | 1
fs/afs/pioctl.c | 24 ++++++++
fs/compat_pioctl.c | 100 ++++++++++++++++++++++++++++++++++
fs/pioctl.c | 107 ++++++++++++++++++++++++++++++++++++
include/linux/compat.h | 10 +++
include/linux/fs.h | 2 +
include/linux/pioctl.h | 58 ++++++++++++++++++++
include/linux/syscalls.h | 5 ++
20 files changed, 339 insertions(+), 2 deletions(-)
create mode 100644 fs/afs/pioctl.c
create mode 100644 fs/compat_pioctl.c
create mode 100644 fs/pioctl.c
create mode 100644 include/linux/pioctl.h


diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
index e590261..5caa7bd 100644
--- a/arch/x86/ia32/ia32entry.S
+++ b/arch/x86/ia32/ia32entry.S
@@ -832,4 +832,5 @@ ia32_sys_call_table:
.quad compat_sys_pwritev
.quad compat_sys_rt_tgsigqueueinfo /* 335 */
.quad sys_perf_counter_open
+ .quad compat_sys_pioctl
ia32_syscall_end:
diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h
index 732a307..f4f5c35 100644
--- a/arch/x86/include/asm/unistd_32.h
+++ b/arch/x86/include/asm/unistd_32.h
@@ -342,6 +342,7 @@
#define __NR_pwritev 334
#define __NR_rt_tgsigqueueinfo 335
#define __NR_perf_counter_open 336
+#define __NR_pioctl 337

#ifdef __KERNEL__

diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h
index 900e161..495d0fb 100644
--- a/arch/x86/include/asm/unistd_64.h
+++ b/arch/x86/include/asm/unistd_64.h
@@ -661,6 +661,8 @@ __SYSCALL(__NR_pwritev, sys_pwritev)
__SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo)
#define __NR_perf_counter_open 298
__SYSCALL(__NR_perf_counter_open, sys_perf_counter_open)
+#define __NR_pioctl 299
+__SYSCALL(__NR_pioctl, sys_pioctl)

#ifndef __NO_STUBS
#define __ARCH_WANT_OLD_READDIR
diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S
index d51321d..723f33e 100644
--- a/arch/x86/kernel/syscall_table_32.S
+++ b/arch/x86/kernel/syscall_table_32.S
@@ -336,3 +336,4 @@ ENTRY(sys_call_table)
.long sys_pwritev
.long sys_rt_tgsigqueueinfo /* 335 */
.long sys_perf_counter_open
+ .long sys_pioctl
diff --git a/fs/Kconfig b/fs/Kconfig
index 525da2e..69cff4a 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -76,6 +76,12 @@ config GENERIC_ACL
bool
select FS_POSIX_ACL

+config PIOCTL
+ bool
+ help
+ This option enabled the pioctl() system call, which is used by AFS
+ and Coda
+
menu "Caches"

source "fs/fscache/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index af6d047..d5bf38a 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -51,6 +51,9 @@ obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o

+pioctl-compat-$(CONFIG_COMPAT) := compat_pioctl.o
+obj-$(CONFIG_PIOCTL) += pioctl.o $(pioctl-compat-y)
+
obj-y += quota/

obj-$(CONFIG_PROC_FS) += proc/
diff --git a/fs/afs/Kconfig b/fs/afs/Kconfig
index 5c4e61d..2bd2324 100644
--- a/fs/afs/Kconfig
+++ b/fs/afs/Kconfig
@@ -2,6 +2,7 @@ config AFS_FS
tristate "Andrew File System support (AFS) (EXPERIMENTAL)"
depends on INET && EXPERIMENTAL
select AF_RXRPC
+ select PIOCTL
help
If you say Y here, you will get an experimental Andrew File System
driver. It currently only supports unsecured read-only AFS access.
diff --git a/fs/afs/Makefile b/fs/afs/Makefile
index 4f64b95..0160227 100644
--- a/fs/afs/Makefile
+++ b/fs/afs/Makefile
@@ -27,6 +27,7 @@ kafs-objs := \
vlocation.o \
vnode.o \
volume.o \
- write.o
+ write.o \
+ pioctl.o

obj-$(CONFIG_AFS_FS) := kafs.o
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 9bd7577..e1a785c 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -60,6 +60,7 @@ const struct inode_operations afs_dir_inode_operations = {
.permission = afs_permission,
.getattr = afs_getattr,
.setattr = afs_setattr,
+ .pioctl = afs_pioctl,
};

static const struct dentry_operations afs_fs_dentry_operations = {
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 0149dab..73835b7 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -45,6 +45,7 @@ const struct inode_operations afs_file_inode_operations = {
.getattr = afs_getattr,
.setattr = afs_setattr,
.permission = afs_permission,
+ .pioctl = afs_pioctl,
};

const struct address_space_operations afs_fs_aops = {
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index c048f06..f1de608 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -27,6 +27,13 @@ struct afs_iget_data {
struct afs_volume *volume; /* volume on which resides */
};

+static const struct inode_operations afs_symlink_inode_operations = {
+ .readlink = generic_readlink,
+ .follow_link = page_follow_link_light,
+ .put_link = page_put_link,
+ .pioctl = afs_pioctl,
+};
+
/*
* map the AFS file status to the inode member variables
*/
@@ -54,7 +61,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
break;
case AFS_FTYPE_SYMLINK:
inode->i_mode = S_IFLNK | vnode->status.mode;
- inode->i_op = &page_symlink_inode_operations;
+ inode->i_op = &afs_symlink_inode_operations;
break;
default:
printk("kAFS: AFS vnode with undefined type\n");
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 106be66..0aaa324 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -584,6 +584,11 @@ extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *);
extern void afs_mntpt_kill_timer(void);

/*
+ * pioctl.c
+ */
+extern long afs_pioctl(struct dentry *, int, struct vice_ioctl *);
+
+/*
* proc.c
*/
extern int afs_proc_init(void);
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index c52be53..153bea5 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -37,6 +37,7 @@ const struct inode_operations afs_mntpt_inode_operations = {
.follow_link = afs_mntpt_follow_link,
.readlink = page_readlink,
.getattr = afs_getattr,
+ .pioctl = afs_pioctl,
};

static LIST_HEAD(afs_vfsmounts);
diff --git a/fs/afs/pioctl.c b/fs/afs/pioctl.c
new file mode 100644
index 0000000..e266f27
--- /dev/null
+++ b/fs/afs/pioctl.c
@@ -0,0 +1,24 @@
+/* Path-based I/O control
+ *
+ * Copyright (C) 2008 Jacob Thebault-Spieker <summatusmentis@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/fs.h>
+#include <linux/pioctl.h>
+#include "internal.h"
+
+/*
+ * The AFS path-based I/O control operation
+ */
+long afs_pioctl(struct dentry *dentry, int cmd, struct vice_ioctl *arg)
+{
+ switch (cmd) {
+ default:
+ printk(KERN_DEBUG "AFS: Unsupported pioctl command %x\n", cmd);
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/fs/compat_pioctl.c b/fs/compat_pioctl.c
new file mode 100644
index 0000000..9f2de77
--- /dev/null
+++ b/fs/compat_pioctl.c
@@ -0,0 +1,100 @@
+/* Path-based I/O control, compatibility
+ *
+ * Copyright (C) 2009 David Howells <dhowells@xxxxxxxxxx>
+ * Copyright (C) 2008 Jacob Thebault-Spieker <summatusmentis@xxxxxxxxx>
+ *
+ * Modified by David Howells <dhowells@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/compat.h>
+#include <linux/namei.h>
+#include <linux/pioctl.h>
+#include <linux/slab.h>
+
+/*
+ * Path-based I/O control system call, 32-bit compatibility version
+ */
+long compat_sys_pioctl(const char __user *filename, int cmd,
+ struct compat_ViceIoctl __user *arg, int follow)
+{
+ struct compat_ViceIoctl user_args;
+ struct vice_ioctl kargs;
+ struct path path;
+ long error;
+
+ if (copy_from_user(&user_args, arg, sizeof(user_args)) != 0)
+ return -EFAULT;
+
+ if (user_args.in_size < 0 || user_args.out_size < 0)
+ return -EINVAL;
+
+ if (user_args.in) {
+ if (unlikely(!access_ok(VERIFY_READ,
+ compat_ptr(user_args.in),
+ user_args.in_size)))
+ return -EFAULT;
+
+ kargs.in = kmalloc(user_args.in_size, GFP_KERNEL);
+ if (!kargs.in)
+ return -ENOMEM;
+
+ if (copy_from_user(kargs.in,
+ compat_ptr(user_args.in),
+ user_args.in_size) != 0) {
+ kfree(kargs.in);
+ return -EFAULT;
+ }
+ kargs.in_size = user_args.in_size;
+ } else {
+ kargs.in_size = 0;
+ kargs.in = NULL;
+ }
+
+ if (user_args.out) {
+ if (unlikely(!access_ok(VERIFY_WRITE,
+ compat_ptr(user_args.out),
+ user_args.out_size))) {
+ kfree(kargs.in);
+ return -EFAULT;
+ }
+ kargs.out = kmalloc(user_args.out_size, GFP_KERNEL);
+ if (!kargs.out) {
+ kfree(kargs.in);
+ return -ENOMEM;
+ }
+ } else {
+ kargs.out_size = 0;
+ kargs.out = NULL;
+ }
+
+ error = user_path(filename, &path);
+ if (!error) {
+ if (path.dentry->d_inode)
+ error = vfs_pioctl(path.dentry, cmd, &kargs);
+ path_put(&path);
+ }
+ kfree(kargs.in);
+
+ if (user_args.out) {
+ if (error >= 0) {
+ if (copy_to_user(compat_ptr(user_args.out), kargs.out,
+ kargs.out_size) != 0) {
+ kfree(kargs.out);
+ return -EFAULT;
+ }
+ if (put_user(kargs.out_size, &arg->out_size) != 0)
+ error = -EFAULT;
+ }
+ kfree(kargs.out);
+ }
+
+ return error;
+}
diff --git a/fs/pioctl.c b/fs/pioctl.c
new file mode 100644
index 0000000..c17f220
--- /dev/null
+++ b/fs/pioctl.c
@@ -0,0 +1,107 @@
+/* Path-based I/O control
+ *
+ * Copyright (C) 2009 David Howells <dhowells@xxxxxxxxxx>
+ * Copyright (C) 2008 Jacob Thebault-Spieker <summatusmentis@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it a/or
+ * modify it uer the terms of the GNU General Public License
+ * as published by the Free Software Fouation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/namei.h>
+#include <linux/pioctl.h>
+#include <linux/slab.h>
+
+/*
+ * VFS entry point for path-based I/O control
+ */
+long vfs_pioctl(struct dentry *dentry, int cmd, struct vice_ioctl *arg)
+{
+ if (!dentry->d_inode->i_op || !dentry->d_inode->i_op->pioctl)
+ return -EPERM;
+
+ return dentry->d_inode->i_op->pioctl(dentry, cmd, arg);
+}
+
+/*
+ * Path-based I/O control system call
+ */
+SYSCALL_DEFINE4(pioctl,
+ const char __user *, filename, int, cmd,
+ struct ViceIoctl __user *, arg, int, follow)
+{
+ struct vice_ioctl kargs;
+ struct ViceIoctl user_args;
+ struct path path;
+ long error;
+
+ if (copy_from_user(&user_args, arg, sizeof(user_args)) != 0)
+ return -EFAULT;
+
+ if (user_args.in_size < 0 || user_args.out_size < 0)
+ return -EINVAL;
+
+ if (user_args.in) {
+ if (unlikely(!access_ok(VERIFY_READ, user_args.in,
+ user_args.in_size)))
+ return -EFAULT;
+
+ kargs.in = kmalloc(user_args.in_size, GFP_KERNEL);
+ if (!kargs.in)
+ return -ENOMEM;
+
+ if (copy_from_user(kargs.in, user_args.in,
+ user_args.in_size) != 0) {
+ kfree(kargs.in);
+ return -EFAULT;
+ }
+ kargs.in_size = user_args.in_size;
+ } else {
+ kargs.in = NULL;
+ kargs.in_size = 0;
+ }
+
+ if (user_args.out) {
+ if (unlikely(!access_ok(VERIFY_WRITE, user_args.out,
+ user_args.out_size))) {
+ kfree(kargs.in);
+ return -EFAULT;
+ }
+ kargs.out = kmalloc(user_args.out_size, GFP_KERNEL);
+ if (!kargs.out) {
+ kfree(kargs.in);
+ return -ENOMEM;
+ }
+ kargs.out_size = user_args.out_size;
+ } else {
+ kargs.out_size = 0;
+ kargs.out = NULL;
+ }
+
+ error = user_path(filename, &path);
+ if (!error) {
+ if (path.dentry->d_inode)
+ error = vfs_pioctl(path.dentry, cmd, &kargs);
+ path_put(&path);
+ }
+ kfree(kargs.in);
+
+ if (user_args.out) {
+ if (error >= 0) {
+ if (copy_to_user(user_args.out, kargs.out,
+ kargs.out_size) != 0) {
+ kfree(kargs.out);
+ return -EFAULT;
+ }
+ if (put_user(kargs.out_size, &arg->out_size) != 0)
+ error = -EFAULT;
+ }
+ kfree(kargs.out);
+ }
+
+ return error;
+}
diff --git a/include/linux/compat.h b/include/linux/compat.h
index af931ee..35afe29 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -132,6 +132,13 @@ struct compat_ustat {
char f_fpack[6];
};

+struct compat_ViceIoctl {
+ compat_uptr_t in;
+ compat_uptr_t out;
+ short in_size;
+ short out_size;
+};
+
typedef union compat_sigval {
compat_int_t sival_int;
compat_uptr_t sival_ptr;
@@ -308,6 +315,9 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename,
int flag);
asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename,
int flags, int mode);
+asmlinkage long compat_sys_pioctl(const char __user *filename, int cmd,
+ struct compat_ViceIoctl __user *arg,
+ int follow);

#endif /* CONFIG_COMPAT */
#endif /* _LINUX_COMPAT_H */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 32b0228..1737524 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -388,6 +388,7 @@ struct kstatfs;
struct vm_area_struct;
struct vfsmount;
struct cred;
+struct vice_ioctl;

extern void __init inode_init(void);
extern void __init inode_init_early(void);
@@ -1531,6 +1532,7 @@ struct inode_operations {
loff_t len);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
+ long (*pioctl)(struct dentry *, int, struct vice_ioctl *);
};

struct seq_file;
diff --git a/include/linux/pioctl.h b/include/linux/pioctl.h
new file mode 100644
index 0000000..8e979f4
--- /dev/null
+++ b/include/linux/pioctl.h
@@ -0,0 +1,58 @@
+/* Path-based I/O control command listing
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * Modifications Copyright (C) 2008
+ * Jacob Thebault-Spieker <summatusmentis@xxxxxxxxx>
+ *
+ * pioctl definitions taken from http://grand.central.org/numbers/pioctls.html
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_PIOCTL_H
+#define _LINUX_PIOCTL_H
+
+#ifdef __KERNEL__
+
+/*
+ * pioctl syscall argument block
+ */
+struct ViceIoctl {
+ caddr_t __user in; /* input/argument buffer (or NULL) */
+ caddr_t __user out; /* output/reply buffer (or NULL) */
+ short in_size; /* size of input buffer (or 0) */
+ short out_size; /* size of output buffer (or 0) */
+};
+
+struct vice_ioctl {
+ char *in;
+ char *out;
+ short in_size;
+ short out_size;
+};
+
+/*
+ * Internal pioctl handler
+ */
+extern long vfs_pioctl(struct dentry *, int, struct vice_ioctl *);
+
+#else
+
+/*
+ * Userspace version of pioctl syscall argument block
+ */
+struct ViceIoctl {
+ caddr_t in;
+ caddr_t out;
+ short in_size;
+ short out_size;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_PIOCTL_H */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 418d90f..ab6f49f 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -56,6 +56,7 @@ struct robust_list_head;
struct getcpu_cache;
struct old_linux_dirent;
struct perf_counter_attr;
+struct ViceIoctl;

#include <linux/types.h>
#include <linux/aio_abi.h>
@@ -760,4 +761,8 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[]);
asmlinkage long sys_perf_counter_open(
struct perf_counter_attr __user *attr_uptr,
pid_t pid, int cpu, int group_fd, unsigned long flags);
+
+asmlinkage long sys_pioctl(const char __user *filename, int cmd,
+ struct ViceIoctl __user *args, int nofollow);
+
#endif

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/