Skip to contentSkip to conversation

VFS: Mountpoint file descriptor umount support

PATCH In review Mike Waychison 2 participants 21 messages Oct 25, 2004 – Oct 26, 2004 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw

PATCH 15/28 VFS: Mountpoint file descriptor umount support

This patch adds functionality to mountfd so that a user can perform the various types of umount (forced umount, not-busy umount, lazy-umount).

---

 fs/mountfd.c       |   20 ++++++++++++++++++++
 fs/namespace.c     |    2 +-
 include/linux/fs.h |    5 ++++-
 3 files changed, 25 insertions(+), 2 deletions(-)
fs/mountfd.c +20 -0
Index: linux-2.6.9-quilt/fs/mountfd.c
===================================================================
@@ -11,6 +11,8 @@
 
 #define VFSMOUNT(filp) ((struct vfsmount *)((filp)->private_data))
 
+extern int do_umount(struct vfsmount *mnt, int flags);
+
 static struct vfsmount *mfdfs_mnt;
 
 static void mfdfs_read_inode(struct inode *inode);
@@ -72,6 +74,18 @@ static int mfd_release(struct inode *ino
 	return 0;
 }
 
+static long mfd_umount(struct file *mountfilp, int flags)
+{
+	struct vfsmount *mnt;
+	int error;
+	
+	mnt = mntget(VFSMOUNT(mountfilp));
+
+	error = do_umount(mnt, flags);
+
+	return error;
+}
+
 static int mfd_ioctl(struct inode *inode, struct file *filp,
 		     unsigned int cmd, unsigned long arg);
 static struct file_operations mfd_file_ops = {
@@ -243,6 +257,12 @@ static int mfd_ioctl(struct inode *inode
 	switch (cmd) {
 	case MOUNTFD_IOC_GETDIRFD:
 		return mfd_getdirfd(filp);
+	case MOUNTFD_IOC_DETACH:
+		return mfd_umount(filp, MNT_DETACH);
+	case MOUNTFD_IOC_UNMOUNT:
+		return mfd_umount(filp, 0);
+	case MOUNTFD_IOC_FORCEDUNMOUNT:
+		return mfd_umount(filp, MNT_FORCE);
 	}
 	return -ENOTTY;
 }
fs/namespace.c +1 -1
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -601,7 +601,7 @@ static void umount_tree(struct vfsmount 
 	spin_lock(&vfsmount_lock);
 }
 
-static int do_umount(struct vfsmount *mnt, int flags)
+int do_umount(struct vfsmount *mnt, int flags)
 {
 	struct super_block * sb = mnt->mnt_sb;
 	int retval;
include/linux/fs.h +4 -1
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -214,7 +214,10 @@ extern int leases_enable, dir_notify_ena
 #define FIBMAP	   _IO(0x00,1)	/* bmap access */
 #define FIGETBSZ   _IO(0x00,2)	/* get the block size used for bmap */
 
-#define MOUNTFD_IOC_GETDIRFD _IO('p', 0xa0)
+#define MOUNTFD_IOC_GETDIRFD      _IO('p', 0xa0)
+#define MOUNTFD_IOC_UNMOUNT       _IO('p', 0xa1)
+#define MOUNTFD_IOC_DETACH        _IO('p', 0xa2)
+#define MOUNTFD_IOC_FORCEDUNMOUNT _IO('p', 0xa3)
 
 #ifdef __KERNEL__
 
#2 lore raw

PATCH 16/28 VFS: Mountpoint file descriptor attach support

This patch allows an unattached mountfd to be attached to a directory given a direct fd.

NOTE: Probably requires CAP_SYSADMIN.. depends on what we want the security model to be.

---

 fs/mountfd.c       |   35 +++++++++++++++++++++++++++++++++++
 include/linux/fs.h |    1 +
 2 files changed, 36 insertions(+)
fs/mountfd.c +35 -0
Index: linux-2.6.9-quilt/fs/mountfd.c
===================================================================
@@ -5,6 +5,7 @@
 #include <linux/stat.h>
 #include <linux/init.h>
 #include <linux/security.h>
+#include <linux/namei.h>
 
 #define MFDFS_MAGIC 0x4A9F2E43
 #define MFDFS_ROOT_INO 1
@@ -251,6 +252,38 @@ out_filp:
 	return error;
 }
 
+static long mfd_attach(struct file *mountfilp, int dirfd)
+{
+	struct file *dir;
+	struct vfsmount *mnt;
+	struct nameidata nd;
+	int ret;
+
+	mnt = mntget(VFSMOUNT(mountfilp));
+
+	ret = -EBADF;
+	dir = fget(dirfd);
+	if (!dir)
+		goto out;
+
+	ret = -ENOTDIR;
+	if (!S_ISDIR(dir->f_dentry->d_inode->i_mode))
+		goto out2;
+
+	memset(&nd, 0, sizeof(nd));
+	nd.dentry = dget(dir->f_dentry);
+	nd.mnt = mntget(dir->f_vfsmnt);
+
+	ret = do_graft_mount(mnt, &nd);
+
+	path_release(&nd);
+out2:
+	put_filp(dir);
+out:
+	mntput(mnt);
+	return ret;
+}
+
 static int mfd_ioctl(struct inode *inode, struct file *filp,
 		     unsigned int cmd, unsigned long arg)
 {
@@ -263,6 +296,8 @@ static int mfd_ioctl(struct inode *inode
 		return mfd_umount(filp, 0);
 	case MOUNTFD_IOC_FORCEDUNMOUNT:
 		return mfd_umount(filp, MNT_FORCE);
+	case MOUNTFD_IOC_ATTACH:
+		return mfd_attach(filp, arg);
 	}
 	return -ENOTTY;
 }
include/linux/fs.h +1 -0
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -218,6 +218,7 @@ extern int leases_enable, dir_notify_ena
 #define MOUNTFD_IOC_UNMOUNT       _IO('p', 0xa1)
 #define MOUNTFD_IOC_DETACH        _IO('p', 0xa2)
 #define MOUNTFD_IOC_FORCEDUNMOUNT _IO('p', 0xa3)
+#define MOUNTFD_IOC_ATTACH        _IOW('p', 0xa4, int)
 
 #ifdef __KERNEL__
 
#3 lore raw

PATCH 17/28 VFS: Mountpoint file descriptor walking

This patch allows one to walk the mountpoint tree given a mountfd. There are two operations permitted:

MOUNTFD_IOC_GETFIRSTCHILD - Given a mountfd, return the first child mountpoint
as a mountfd.
MOUNTFD_IOC_GETNEXTCHILD - Given a mountfd, return the next mountpoint sibling

                            to it. (*)

(*) XXX: This should be modified to also take a parent mountfd so that you can't walk out of a chroot.

We explicitly don't allow walks to the parent of a mountpoint as that would allow for a user to easily walk out of a chroot.

---

 fs/mountfd.c       |   46 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h |    3 +++
 2 files changed, 49 insertions(+)
include/linux/fs.h +3 -0
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -219,6 +219,9 @@ extern int leases_enable, dir_notify_ena
 #define MOUNTFD_IOC_DETACH        _IO('p', 0xa2)
 #define MOUNTFD_IOC_FORCEDUNMOUNT _IO('p', 0xa3)
 #define MOUNTFD_IOC_ATTACH        _IOW('p', 0xa4, int)
+#define MOUNTFD_IOC_GETFIRSTCHILD _IO('p', 0xa5)
+/* TODO: change this interface to require the parent mfd as well */
+#define MOUNTFD_IOC_GETNEXTCHILD  _IO('p', 0xa6)
 
 #ifdef __KERNEL__
 
fs/mountfd.c +46 -0

58 lines not on this page · raw message

#4 lore raw

PATCH 18/28 VFS: Mountpoint file descriptor read properties

This patch allows userspace to query a mountpoint file descriptor for information using the ioctl interface.

The properties exported in this patch are for _example only_ to demonstrate what the interface might look like. This patch allows mountfds to show the following information:

MOUNTFD_IOC_GETDEV - Get the associated block device (if any)
MOUNTFD_IOC_GETFSTYPE - Get the fstype of the mountpoint.
MOUNTFD_IOC_GETVFSOPTIONS - Get the vfs specific options of the
mountpoint/superblock
MOUNTFD_IOC_GETFSOPTIONS - Get the fstype-specific options (if any)

---

 fs/mountfd.c       |  114 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/fs.h |    6 ++
 2 files changed, 119 insertions(+), 1 deletion(-)
fs/mountfd.c +113 -1
Index: linux-2.6.9-quilt/fs/mountfd.c
===================================================================
@@ -160,6 +160,72 @@ static struct dentry *get_mfd_dentry(str
 	return dget(dentry);
 }
 
+static int mfd_read_fstype(struct vfsmount *mnt, char *buf)
+{
+	return scnprintf(buf, MOUNTFD_READSIZE-1, "%s", mnt->mnt_sb->s_type->name) + 1;
+}
+
+static int mfd_read_dev(struct vfsmount *mnt, char *buf)
+{
+	return scnprintf(buf, MOUNTFD_READSIZE-1, "%s", mnt->mnt_sb->s_id) + 1;
+}
+
+static int mfd_read_vfsoptions(struct vfsmount *mnt, char *buf)
+{
+	static struct vfs_info {
+		int flag;
+		char *str;
+	} vfs_info[] = {
+		{ MS_RDONLY, "ro" },
+		{ MS_DIRSYNC, "dirsync" },
+		{ MS_MANDLOCK, "mand" },
+		{ MS_NOATIME, "noatime" },
+		{ MS_NODIRATIME, "nodiratime" },
+		{ 0, NULL }
+	};
+	struct vfs_info *vfs_infop;
+	static struct mnt_info {
+		int flag;
+		char *str;
+	} mnt_info[] = {
+		{ MNT_NOSUID, "nosuid" },
+		{ MNT_NODEV, "nodev" },
+		{ MNT_NOEXEC, "noexec" },
+		{ 0, NULL }
+	};
+	struct mnt_info *mnt_infop;
+
+	char *p = buf;
+
+	int sb_flags = mnt->mnt_sb->s_flags;
+	int mnt_flags = mnt->mnt_flags;
+	int first = 0;
+
+	/*
+	 * Note: we skip length checks below because we assume we can't overrun
+	 * MOUNTFD_READSIZE.
+	 */
+
+	for (vfs_infop = vfs_info; vfs_infop->flag; vfs_infop++) {
+		if (sb_flags & vfs_infop->flag) {
+			if (first++)
+				*p++ = ',';
+			strcpy(p, vfs_infop->str);
+			p += strlen(vfs_infop->str);
+		}
+	}
+
+	for (mnt_infop = mnt_info; mnt_infop->flag; mnt_infop++) {
+		if (mnt_flags & mnt_infop->flag) {
+			if (first++)
+				*p++ = ',';
+			strcpy(p, mnt_infop->str);
+			p += strlen(mnt_infop->str);
+		}
+	}
+	return p - buf + 1;
+}
+
 static long open_mfd(struct vfsmount *mnt)
 {
 	struct file *file;
@@ -176,6 +242,7 @@ static long open_mfd(struct vfsmount *mn
 	if (fd < 0)
 		goto out_putfilp;
 
+	error = -ENOMEM;
 	file->private_data = mnt;
 	file->f_dentry = get_mfd_dentry(mnt);
 	if (IS_ERR(file->f_dentry)) {
@@ -326,9 +393,53 @@ static long mfd_nextchild(struct file *m
 	return ret;
 }
 
+static int mfd_ioctl_reads(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg)
+{
+	char __user *user_buf = (char __user *)arg;
+	struct vfsmount *mnt;
+	char *buf;
+	int ret;
+
+	mnt = VFSMOUNT(filp);
+
+	buf = (char *)get_zeroed_page(GFP_KERNEL);
+	if (buf)
+		return -ENOMEM;
+	switch (cmd) {
+	/*
+	 * The following calls are expected to return the total number of bytes to write out, including '\0'.
+	 */
+	case MOUNTFD_IOC_GETDEV:
+		ret = mfd_read_dev(mnt, buf);
+		break;
+	case MOUNTFD_IOC_GETFSTYPE:
+		ret = mfd_read_fstype(mnt, buf);
+		break;
+	case MOUNTFD_IOC_GETVFSOPTIONS:
+		ret = mfd_read_vfsoptions(mnt, buf);
+		break;
+	case MOUNTFD_IOC_GETFSOPTIONS:
+		/* TODO: need super_block op that doesn't take a seq_file */
+		ret = -ENOSYS;
+		break;
+	default:
+		ret = -ENOTTY;
+	}
+
+	if (ret >= 0) {
+		if (copy_to_user(user_buf, buf, ret))
+			ret = -EFAULT;
+	}
+
+	free_page((unsigned long)buf);
+	return ret;
+}
+
 static int mfd_ioctl(struct inode *inode, struct file *filp,
 		     unsigned int cmd, unsigned long arg)
 {
+	int ret;
 	switch (cmd) {
 	case MOUNTFD_IOC_GETDIRFD:
 		return mfd_getdirfd(filp);
@@ -345,7 +456,8 @@ static int mfd_ioctl(struct inode *inode
 	case MOUNTFD_IOC_GETNEXTCHILD:
 		return mfd_nextchild(filp);
 	}
-	return -ENOTTY;
+	ret = mfd_ioctl_reads(inode, filp, cmd, arg);
+	return ret;
 }
 
 asmlinkage long sys_mountfd(int dirfd)
include/linux/fs.h +6 -0
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -223,6 +223,12 @@ extern int leases_enable, dir_notify_ena
 /* TODO: change this interface to require the parent mfd as well */
 #define MOUNTFD_IOC_GETNEXTCHILD  _IO('p', 0xa6)
 
+#define MOUNTFD_READSIZE PAGE_SIZE
+#define MOUNTFD_IOC_GETDEV        _IOR('p', 0xa7, char [MOUNTFD_READSIZE])
+#define MOUNTFD_IOC_GETFSTYPE     _IOR('p', 0xa8, char [MOUNTFD_READSIZE])
+#define MOUNTFD_IOC_GETVFSOPTIONS _IOR('p', 0xa9, char [MOUNTFD_READSIZE])
+#define MOUNTFD_IOC_GETFSOPTIONS  _IOR('p', 0xaa, char [MOUNTFD_READSIZE])
+
 #ifdef __KERNEL__
 
 #include <linux/list.h>
#5 lore raw

PATCH 19/28 VFS: Mountpoint file descriptor expiry support

This patch adds support for letting userspcae set the expiry information on a given mountpoint.

---

 fs/mountfd.c       |   10 ++++++++++
 include/linux/fs.h |    1 +
 2 files changed, 11 insertions(+)
include/linux/fs.h +1 -0
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -228,6 +228,7 @@ extern int leases_enable, dir_notify_ena
 #define MOUNTFD_IOC_GETFSTYPE     _IOR('p', 0xa8, char [MOUNTFD_READSIZE])
 #define MOUNTFD_IOC_GETVFSOPTIONS _IOR('p', 0xa9, char [MOUNTFD_READSIZE])
 #define MOUNTFD_IOC_GETFSOPTIONS  _IOR('p', 0xaa, char [MOUNTFD_READSIZE])
+#define MOUNTFD_IOC_SETVFSEXPIRE  _IOW('p', 0xab, int)
 
 #ifdef __KERNEL__
 
fs/mountfd.c +10 -0
Index: linux-2.6.9-quilt/fs/mountfd.c
===================================================================
@@ -393,6 +393,14 @@ static long mfd_nextchild(struct file *m
 	return ret;
 }
 
+static int mfd_vfsexpire(struct file *mountfilp, int arg)
+{
+	struct vfsmount *mnt;
+
+	mnt = VFSMOUNT(mountfilp);
+	return mnt_expire(mnt, arg);
+}
+
 static int mfd_ioctl_reads(struct inode *inode, struct file *filp,
 		     unsigned int cmd, unsigned long arg)
 {
@@ -455,6 +463,8 @@ static int mfd_ioctl(struct inode *inode
 		return mfd_firstchild(filp);
 	case MOUNTFD_IOC_GETNEXTCHILD:
 		return mfd_nextchild(filp);
+	case MOUNTFD_IOC_SETVFSEXPIRE:
+		return mfd_vfsexpire(filp, (int)arg);
 	}
 	ret = mfd_ioctl_reads(inode, filp, cmd, arg);
 	return ret;
#6 lore raw

PATCH 20/28 HOTPLUG: call_usermodehelper callback support

This patch extends the call_usermodehelper api by adding a callback variant. The callback is made right when the system is about to call execve into the new process. This allows for the caller to provide changes to the default environment right before the exec takes place. Note: the context of the callback will be _from within another process_.

---

 include/linux/kmod.h |    2 +
 kernel/kmod.c        |   80 ++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 63 insertions(+), 19 deletions(-)
kernel/kmod.c +61 -19
Index: linux-2.6.9-quilt/kernel/kmod.c
===================================================================
@@ -140,17 +140,22 @@ EXPORT_SYMBOL(hotplug_path);
 
 struct subprocess_info {
 	struct completion *complete;
+	call_usermodehelper_cb_t cb;
+	void *cbdata;
+	int wait;
+	int retval;
+};
+
+struct simple_usermodehelper_info {
 	char *path;
 	char **argv;
 	char **envp;
-	int wait;
-	int retval;
 };
 
 /*
  * This is the task which runs the usermode application
  */
-static int ____call_usermodehelper(void *data)
+static int ____call_usermodehelper_cb(void *data)
 {
 	struct subprocess_info *sub_info = data;
 	int retval;
@@ -168,7 +173,7 @@ static int ____call_usermodehelper(void 
 
 	retval = -EPERM;
 	if (current->fs->root)
-		retval = execve(sub_info->path, sub_info->argv,sub_info->envp);
+		retval = (*sub_info->cb)(sub_info->cbdata);
 
 	/* Exec failed? */
 	sub_info->retval = retval;
@@ -190,7 +195,7 @@ static int wait_for_helper(void *data)
 	do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);
 	allow_signal(SIGCHLD);
 
-	pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD);
+	pid = kernel_thread(____call_usermodehelper_cb, sub_info, SIGCHLD);
 	if (pid < 0) {
 		sub_info->retval = pid;
 	} else {
@@ -211,7 +216,7 @@ static int wait_for_helper(void *data)
 }
 
 /* This is run by khelper thread  */
-static void __call_usermodehelper(void *data)
+static void __call_usermodehelper_cb(void *data)
 {
 	struct subprocess_info *sub_info = data;
 	pid_t pid;
@@ -223,7 +228,7 @@ static void __call_usermodehelper(void *
 		pid = kernel_thread(wait_for_helper, sub_info,
 				    CLONE_FS | CLONE_FILES | SIGCHLD);
 	else
-		pid = kernel_thread(____call_usermodehelper, sub_info,
+		pid = kernel_thread(____call_usermodehelper_cb, sub_info,
 				    CLONE_VFORK | SIGCHLD);
 
 	if (pid < 0) {
@@ -234,12 +239,16 @@ static void __call_usermodehelper(void *
 }
 
 /**
- * call_usermodehelper - start a usermode application
- * @path: pathname for the application
- * @argv: null-terminated argument list
- * @envp: null-terminated environment list
+ * call_usermodehelper_cb - start a usermode application with a callback
+ * @cb: A user provided callback that will eventually execve.
+ * @cbdata: User supplied information that will be passed back to the callback.
  * @wait: wait for the application to finish and return status.
  *
+ * This call will do all the work required for setting up a usermode helper
+ * except perform the actual exec.  It is the caller's responsibility to
+ * provide a callback that will eventually exec.  This allows last minute
+ * changes to current.
+ *
  * Runs a user-space application.  The application is started
  * asynchronously if wait is not set, and runs as a child of keventd.
  * (ie. it runs with full root capabilities).
@@ -247,29 +256,62 @@ static void __call_usermodehelper(void *
  * Must be called from process context.  Returns a negative error code
  * if program was not execed successfully, or 0.
  */
-int call_usermodehelper(char *path, char **argv, char **envp, int wait)
+int call_usermodehelper_cb(call_usermodehelper_cb_t cb, void *cbdata, int wait)
 {
 	DECLARE_COMPLETION(done);
 	struct subprocess_info sub_info = {
 		.complete	= &done,
-		.path		= path,
-		.argv		= argv,
-		.envp		= envp,
+		.cb		= cb,
+		.cbdata		= cbdata,
 		.wait		= wait,
 		.retval		= 0,
 	};
-	DECLARE_WORK(work, __call_usermodehelper, &sub_info);
+	DECLARE_WORK(work, __call_usermodehelper_cb, &sub_info);
 
+	BUG_ON(!cb || !cbdata);
 	if (!khelper_wq)
 		return -EBUSY;
 
-	if (path[0] == '\0')
-		return 0;
-
 	queue_work(khelper_wq, &work);
 	wait_for_completion(&done);
 	return sub_info.retval;
 }
+EXPORT_SYMBOL(call_usermodehelper_cb);
+
+static int call_usermodehelper_simple(void *cbdata)
+{
+	struct simple_usermodehelper_info *info = cbdata;
+	return execve(info->path, info->argv, info->envp);
+}
+
+/**
+ * call_usermodehelper - start a usermode application
+ * @path: pathname for the application
+ * @argv: null-terminated argument list
+ * @envp: null-terminated environment list
+ * @wait: wait for the application to finish and return status.
+ *
+ * Runs a user-space application.  The application is started
+ * asynchronously if wait is not set, and runs as a child of keventd.
+ * (ie. it runs with full root capabilities).
+ *
+ * Must be called from process context.  Returns a negative error code
+ * if program was not execed successfully, or 0.
+ */
+
+int call_usermodehelper(char *path, char **argv, char **envp, int wait)
+{
+	struct simple_usermodehelper_info info = {
+		.path = path,
+		.argv = argv,
+		.envp = envp,
+	};
+	
+	if (path[0] == '\0')
+		return 0;
+
+	return call_usermodehelper_cb(call_usermodehelper_simple, &info, wait);
+}
 EXPORT_SYMBOL(call_usermodehelper);
 
 static __init int usermodehelper_init(void)
include/linux/kmod.h +2 -0
Index: linux-2.6.9-quilt/include/linux/kmod.h
===================================================================
@@ -34,6 +34,8 @@ static inline int request_module(const c
 #endif
 
 #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x)))
+typedef int (*call_usermodehelper_cb_t)(void *cbdata);
+extern int call_usermodehelper_cb(call_usermodehelper_cb_t cb, void *cbdata, int wait);
 extern int call_usermodehelper(char *path, char *argv[], char *envp[], int wait);
 
 #ifdef CONFIG_HOTPLUG
#7 lore raw

PATCH 21/28 HOTPLUG: Hack to allow for call to execve

This patch is a hack while we don't have a proper way for code to call execve. It simply introduces call_usermodehelper_execve(path, argv, envp) that call the execve syscall with an errno set.

We need to figure out a proper way for code to call execve!

---

 include/linux/kmod.h |    1 +
 kernel/kmod.c        |    7 +++++++
 2 files changed, 8 insertions(+)
kernel/kmod.c +7 -0
Index: linux-2.6.9-quilt/kernel/kmod.c
===================================================================
@@ -278,6 +278,13 @@ int call_usermodehelper_cb(call_usermode
 }
 EXPORT_SYMBOL(call_usermodehelper_cb);
 
+/* This is an ugly hack while the __KERNEL_SYSCALLS__ cleanup occurs */
+int call_usermodehelper_execve(char *path, char *argv[], char *envp[])
+{
+	return execve(path, argv, envp);
+}
+EXPORT_SYMBOL(call_usermodehelper_execve);
+
 static int call_usermodehelper_simple(void *cbdata)
 {
 	struct simple_usermodehelper_info *info = cbdata;
include/linux/kmod.h +1 -0
Index: linux-2.6.9-quilt/include/linux/kmod.h
===================================================================
@@ -36,6 +36,7 @@ static inline int request_module(const c
 #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x)))
 typedef int (*call_usermodehelper_cb_t)(void *cbdata);
 extern int call_usermodehelper_cb(call_usermodehelper_cb_t cb, void *cbdata, int wait);
+extern int call_usermodehelper_execve(char *path, char *argv[], char *envp[]);
 extern int call_usermodehelper(char *path, char *argv[], char *envp[], int wait);
 
 #ifdef CONFIG_HOTPLUG
#8 lore raw

PATCH 22/28 VFS: Export put_namespace

This patch exports the __put_namespace symbol so that autofsng can call it as a module (used as part of the call-out mechanism).

---

 namespace.c |    1 +
 1 files changed, 1 insertion(+)
fs/namespace.c +1 -0
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -1733,3 +1733,4 @@ void __put_namespace(struct namespace *n
 	mntput(namespace->root);
 	kfree(namespace);
 }
+EXPORT_SYMBOL(__put_namespace);
#9 lore raw

PATCH 23/28 VFS: Export get_sb_pseudo

Export get_sb_pseudo for module use. Autofsng uses it to create files that are used for feeding browsing information.

---

 libfs.c |    1 +
 1 files changed, 1 insertion(+)
fs/libfs.c +1 -0
Index: linux-2.6.9-quilt/fs/libfs.c
===================================================================
@@ -222,6 +222,7 @@ Enomem:
 	deactivate_super(s);
 	return ERR_PTR(-ENOMEM);
 }
+EXPORT_SYMBOL(get_sb_pseudo);
 
 int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
#10 lore raw

PATCH 24/28 VFS: Fixup for ->follow_link on root of filesystem

When a follow_link dentry call is made, the implementation expects the dentry to be followed as well as a nameidata struct to be filled in. The received nd->mnt is expected to contain the vfsmount of the dentry being followed, so that a subsequent call to vfs_follow_link may properly pivot off that mount and onto another vfsmount as the path of the link is walked, thus keeping reference counts proper.

The changes made in fs/[email protected] break this behaviour if the dentry being follow_link'ed is a root dentry. This is because follow_mount follows down next.mnt and not nd->mnt like it used to. So, if a root dentry has a follow_link op, the nd->mnt it receives is in fact the vfsmount of the mount it is mounted upon (which breaks reference counts).

---

 namei.c |    2 ++
 1 files changed, 2 insertions(+)
fs/namei.c +2 -0
Index: linux-2.6.9-quilt/fs/namei.c
===================================================================
@@ -755,6 +755,7 @@ int fastcall link_path_walk(const char *
 
 		if (inode->i_op->follow_link) {
 			mntget(next.mnt);
+			nd->mnt = next.mnt;
 			err = do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
@@ -809,6 +810,7 @@ last_component:
 		if ((lookup_flags & LOOKUP_FOLLOW)
 		    && inode && inode->i_op && inode->i_op->follow_link) {
 			mntget(next.mnt);
+			nd->mnt = next.mnt;
 			err = do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
#11 lore raw

PATCH 25/28 VFS: statfs(64) shouldn't follow last component symlink

Mount-related userspace tools will require the ability to detect whether what looks like a regular directory is actually a autofs trigger. To handle this, tools can statfs a given directory and check to see if statfs->f_type == AUTOFSNG_SUPER_MAGIC before walking into the directory (and causing the a filesystem to automount).

To make this happen, we cannot allow statfs to follow_link.

NOTE: This may break any userspace that assumes it can statfs across a last-component symlink. I can't think of any real world breakage however, as mount(8) will drop the real path in /etc/mtab and /proc/mounts will always show the true path.

---

 open.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
fs/open.c +2 -2
Index: linux-2.6.9-quilt/fs/open.c
===================================================================
@@ -121,7 +121,7 @@ asmlinkage long sys_statfs(const char __
 	struct nameidata nd;
 	int error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path_walk_link(path, &nd);
 	if (!error) {
 		struct statfs tmp;
 		error = vfs_statfs_native(nd.dentry->d_inode->i_sb, &tmp);
@@ -140,7 +140,7 @@ asmlinkage long sys_statfs64(const char 
 
 	if (sz != sizeof(*buf))
 		return -EINVAL;
-	error = user_path_walk(path, &nd);
+	error = user_path_walk_link(path, &nd);
 	if (!error) {
 		struct statfs64 tmp;
 		error = vfs_statfs64(nd.dentry->d_inode->i_sb, &tmp);
#12 lore raw

PATCH 26/28 VFS: Introduce MNT_NOFOLLOW

Umounting direct mounts requires the ability to umount without following ->follow_link. We do this by adding a new flag called MNT_NOFOLLOW to umount2.

---

 fs/namespace.c     |    3 ++-
 include/linux/fs.h |    1 +
 2 files changed, 3 insertions(+), 1 deletion(-)
include/linux/fs.h +1 -0
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -735,6 +735,7 @@ extern int send_sigurg(struct fown_struc
 
 #define MNT_FORCE	0x00000001	/* Attempt to forcibily umount */
 #define MNT_DETACH	0x00000002	/* Just detach from the tree */
+#define MNT_NOFOLLOW    0x00000004      /* Do not follow symlinks */
 
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
fs/namespace.c +2 -1
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -693,8 +693,9 @@ asmlinkage long sys_umount(char __user *
 {
 	struct nameidata nd;
 	int retval;
+	int walk_flags = flags & MNT_NOFOLLOW ? 0 : LOOKUP_FOLLOW;
 
-	retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
+	retval = __user_walk(name, walk_flags, &nd);
 	if (retval)
 		goto out;
 	retval = -EINVAL;
#13 lore raw

PATCH 27/28 Testing syscall for expiry

This patch adds a temporary syscall (to x86 only) that allows for quick testing to make sure that mnt_expire works properly. Tests can be found in the autofsng userspace package.

---

 arch/i386/kernel/entry.S  |    1 +
 fs/namespace.c            |   32 ++++++++++++++++++++++++++++++++
 include/asm-i386/unistd.h |    3 ++-
 3 files changed, 35 insertions(+), 1 deletion(-)
arch/i386/kernel/entry.S +1 -0
Index: linux-2.6.9-quilt/arch/i386/kernel/entry.S
===================================================================
@@ -903,5 +903,6 @@ ENTRY(sys_call_table)
 	.long sys_ni_syscall
 	.long sys_ni_syscall
 	.long sys_mountfd		/* 300 */
+	.long sys_mnt_expire
 
 syscall_table_size=(.-sys_call_table)
include/asm-i386/unistd.h +2 -1
Index: linux-2.6.9-quilt/include/asm-i386/unistd.h
===================================================================
@@ -290,8 +290,9 @@
 #define __NR_mq_getsetattr	(__NR_mq_open+5)
 #define __NR_sys_kexec_load	283
 #define __NR_mountfd		300
+#define __NR_mnt_expire		301
 
-#define NR_syscalls 301
+#define NR_syscalls 302
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
fs/namespace.c +32 -0
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -1119,6 +1119,38 @@ static void bump_expiry_counter(struct v
 		parent->mnt_expiry_countdown = diff;
 }
 
+/* TESTING PURPOSES ONLY:  THIS IS NOT A REAL SYSCALL!! - IT WILL GO AWAY */
+asmlinkage int sys_mnt_expire(char __user *_path, int ticks)
+{
+	struct nameidata nd;
+	char *path;
+	int err;
+
+	path = getname(_path);
+	err = PTR_ERR(path);
+	if (IS_ERR(path))
+		goto out;
+
+	err = path_lookup(path, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
+	if (err)
+		goto out_name;
+
+	err = -EINVAL;
+	if (nd.mnt->mnt_root != nd.dentry)
+		goto out_nd;
+
+	err = -EBUSY;
+	if (!mnt_expire(nd.mnt, ticks))
+		err = 0;
+
+out_nd:
+	path_release(&nd);
+out_name:
+	putname(path);
+out:
+	return err;
+}
+
 /*
  * process a list of expirable mountpoints with the intent of discarding any
  * mountpoints that aren't in use and haven't been touched since last we came
#14 lore raw

Re: [PATCH 27/28] Testing syscall for expiry

fs/namespace.c static void bump_expiry_counter(struct v · line 1123

Quoted 6 lines · Mike Waychison

This patch adds a temporary syscall (to x86 only) that allows for quick testing to make sure that mnt_expire works properly. Tests can be found in the autofsng userspace package.

fs/namespace.c +2 -0
+/* TESTING PURPOSES ONLY:  THIS IS NOT A REAL SYSCALL!! - IT WILL GO AWAY */
+asmlinkage int sys_mnt_expire(char __user *_path, int ticks)

so don't submit it..

#15 lore raw

PATCH 28/28 AUTOFSNG: New autofs filesystem (resend)

In reply to Mike Waychison

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Enigmail
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfR22dQs4kOxk3/MRAp3oAJ47d7sziVXF160UzQ4HBzLh3upjcACeNMaE JUeekjsmZaQsGt/yZgGh2c8=
=WqbX
-----END PGP SIGNATURE-----

This patch introduces a new fs implementation for the 'autofs' filesystem. If compiled as a module, the module is called 'autofsng'. NOTE: It may prove a better idea to simply change the name of the fs to 'autofsng' as well, so as to not be confused with the two other autofs implementations already in the kernel.

Please see:
ftp://ftp-eng.cobalt.com/pub/whitepapers/autofs/towards_a_modern_autofs.txt for a description of how it works.

---

 Kconfig              |   26 ++
 Makefile             |    1 
 autofsng/Makefile    |    3 
 autofsng/autofs.h    |   93 +++++++
 autofsng/cachetree.c |  206 ++++++++++++++++
 autofsng/direct.c    |   76 ++++++
 autofsng/indirect.c  |  482 ++++++++++++++++++++++++++++++++++++++
 autofsng/init.c      |   53 ++++
 autofsng/mapcache.c  |  638 +++++++++++++++++++++++++++++++++++++++++++++++++++
 autofsng/request.c   |  295 +++++++++++++++++++++++
 autofsng/super.c     |  234 ++++++++++++++++++
 11 files changed, 2107 insertions(+)
fs/autofsng/Makefile new file +3 -0
Index: linux-2.6.9-quilt/fs/autofsng/Makefile
===================================================================
@@ -0,0 +1,3 @@
+obj-$(CONFIG_AUTOFSNG_FS) += autofsng.o
+
+autofsng-objs := super.o init.o direct.o request.o indirect.o mapcache.o cachetree.o
fs/autofsng/init.c new file +53 -0

53 lines not on this page · raw message

fs/autofsng/direct.c new file +76 -0

76 lines not on this page · raw message

fs/autofsng/autofs.h new file +93 -0

93 lines not on this page · raw message

fs/Makefile +1 -0
Index: linux-2.6.9-quilt/fs/Makefile
===================================================================
@@ -84,6 +84,7 @@ obj-$(CONFIG_ROMFS_FS)		+= romfs/
 obj-$(CONFIG_QNX4FS_FS)		+= qnx4/
 obj-$(CONFIG_AUTOFS_FS)		+= autofs/
 obj-$(CONFIG_AUTOFS4_FS)	+= autofs4/
+obj-$(CONFIG_AUTOFSNG_FS)	+= autofsng/
 obj-$(CONFIG_ADFS_FS)		+= adfs/
 obj-$(CONFIG_UDF_FS)		+= udf/
 obj-$(CONFIG_SUN_OPENPROMFS)	+= openpromfs/
fs/autofsng/super.c new file +234 -0

234 lines not on this page · raw message

fs/autofsng/request.c new file +295 -0

295 lines not on this page · raw message

fs/Kconfig +26 -0
Index: linux-2.6.9-quilt/fs/Kconfig
===================================================================
@@ -481,6 +481,32 @@ config AUTOFS4_FS
 	  local network, you probably do not need an automounter, and can say
 	  N here.
 
+config AUTOFSNG_FS
+	tristate "Kernel automounter next-gen support"
+	depends on EXPERIMENTAL
+	help
+	  The automounter is a tool to automatically mount remote file systems
+	  on demand. This implementation is partially kernel-based to reduce
+	  overhead in the already-mounted case;  this is unlike the BSD
+	  automounter (amd), which is a pure user space daemon.
+
+	  Unlike the previous kernel-based automounter solutions, this 'NG'
+	  version does not use a persistent daemon.  Instead, the kernel calls
+	  out to a userspace application much like how the hotplug system works.
+	  This allows the implementation to work well with namespaces and
+	  simplifies the complexity involved.
+
+	  To use the automounter you need the user-space tools from <TODO>; you
+	  also want to answer Y to "NFS file system support".
+
+	  To compile this support as a module, choose M here: the module will be
+	  called autofsng.  You will need to add "alias autofs autofsng" to your
+	  modules configuration file.
+
+	  If you are not a part of a fairly large, distributed network or don't
+	  have a laptop which needs to dynamically reconfiger to the local
+	  network, you probably do not need an automounter, and can say N here.
+
 menu "CD-ROM/DVD Filesystems"
 
 config ISO9660_FS
fs/autofsng/indirect.c new file +482 -0

482 lines not on this page · raw message

fs/autofsng/mapcache.c new file +638 -0

638 lines not on this page · raw message

fs/autofsng/cachetree.c new file +206 -0

206 lines not on this page · raw message

#16 lore raw

Re: [PATCH 25/28] VFS: statfs(64) shouldn't follow last component symlink

In reply to Mike Waychison
Quoted 6 lines · Mike Waychison

Mount-related userspace tools will require the ability to detect whether what looks like a regular directory is actually a autofs trigger. To handle this, tools can statfs a given directory and check to see if statfs->f_type == AUTOFSNG_SUPER_MAGIC before walking into the directory (and causing the a filesystem to automount).

Quoted 6 lines

To make this happen, we cannot allow statfs to follow_link.

NOTE: This may break any userspace that assumes it can statfs across a last-component symlink. I can't think of any real world breakage however, as mount(8) will drop the real path in /etc/mtab and /proc/mounts will always show the true path.

Which means it's vetoed. It's a big change in syscall semantics. And propabably breaks SuS (for statvfs(3) which requires full symlink resolution when it just refers to a path on the filesystem.

#17 lore raw

Re: [PATCH 25/28] VFS: statfs(64) shouldn't follow last component symlink

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 16 lines · Christoph Hellwig
Quoted 6 lines · Mike Waychison

Mount-related userspace tools will require the ability to detect whether what looks like a regular directory is actually a autofs trigger. To handle this, tools can statfs a given directory and check to see if statfs->f_type == AUTOFSNG_SUPER_MAGIC before walking into the directory (and causing the a filesystem to automount).

Quoted 6 lines

To make this happen, we cannot allow statfs to follow_link.

NOTE: This may break any userspace that assumes it can statfs across a last-component symlink. I can't think of any real world breakage however, as mount(8) will drop the real path in /etc/mtab and /proc/mounts will always show the true path.

Quoted 4 lines

Which means it's vetoed. It's a big change in syscall semantics. And propabably breaks SuS (for statvfs(3) which requires full symlink resolution when it just refers to a path on the filesystem.

Ya, I figured that would be the case. What do folks think about a
lstatfs(64)?

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfRoUdQs4kOxk3/MRAgHoAKCApqvkE2hgLAJKXDkLWWJE7BqevgCfQlh9 BxBlFSMUPoo1VyOcntae7Y0=
=rR8G
-----END PGP SIGNATURE-----

#18 lore raw

Re: [PATCH 20/28] HOTPLUG: call_usermodehelper callback support

In reply to Mike Waychison
Quoted 5 lines · Mike Waychison

This patch extends the call_usermodehelper api by adding a callback variant. The callback is made right when the system is about to call execve into the new process. This allows for the caller to provide changes to the default environment right before the exec takes place. Note: the context of the callback will be _from within another process_.

I don't like this at all. First it's the usual fork() + exec() vs spawn() with gazillions of arguments debatte, second this sounds far too complex to do it in kernelspace to me. Why can't you do the enviroment changes from the program beeing executed?

#19 lore raw

Re: [PATCH 20/28] HOTPLUG: call_usermodehelper callback support

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 9 lines · Christoph Hellwig
Quoted 5 lines · Mike Waychison

This patch extends the call_usermodehelper api by adding a callback variant. The callback is made right when the system is about to call execve into the new process. This allows for the caller to provide changes to the default environment right before the exec takes place. Note: the context of the callback will be _from within another process_.

Quoted 5 lines

I don't like this at all. First it's the usual fork() + exec() vs spawn() with gazillions of arguments debatte, second this sounds far too complex to do it in kernelspace to me. Why can't you do the enviroment changes from the program beeing executed?

I want to be able to do two things:

- - To 'call_usermodehelper' a program, but in current's namespace. Namespaces can't be passed around in userspace.

- - To give the execed program an open file. The current interface doesn't allow me to do that.

I figured the _cb way of doing it remove any need for adding ad-hoc api anytime somebody wants to tweak a task before calling execve.

Does this clarify why I added this?

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD4DBQFBfRvNdQs4kOxk3/MRAvInAJYmn4GlPasI0r7VcwSKv03GXoygAJ90FWDM LVQOfOrpbKp7NDSmlFRt+A==
=aQ04
-----END PGP SIGNATURE-----

#20 lore raw
In reply to Mike Waychison
Quoted 44 lines · Mike Waychison

This patch adds functionality to mountfd so that a user can perform the various types of umount (forced umount, not-busy umount, lazy-umount).

---

 fs/mountfd.c       |   20 ++++++++++++++++++++
 fs/namespace.c     |    2 +-
 include/linux/fs.h |    5 ++++-
 3 files changed, 25 insertions(+), 2 deletions(-)

Index: linux-2.6.9-quilt/fs/mountfd.c
===================================================================

fs/mountfd.c +14 -0
@@ -11,6 +11,8 @@
 
 #define VFSMOUNT(filp) ((struct vfsmount *)((filp)->private_data))
 
+extern int do_umount(struct vfsmount *mnt, int flags);
+
 static struct vfsmount *mfdfs_mnt;
 
 static void mfdfs_read_inode(struct inode *inode);
@@ -72,6 +74,18 @@ static int mfd_release(struct inode *ino
 	return 0;
 }
 
+static long mfd_umount(struct file *mountfilp, int flags)
+{
+	struct vfsmount *mnt;
+	int error;
+	
+	mnt = mntget(VFSMOUNT(mountfilp));
+
+	error = do_umount(mnt, flags);
+
+	return error;
+}
+
 static int mfd_ioctl(struct inode *inode, struct file *filp,
 		     unsigned int cmd, unsigned long arg);
 static struct file_operations mfd_file_ops = {

fs/mountfd.c static int mfd_ioctl(struct inode *inode · line 265

Quoted 9 lines
fs/mountfd.c +6 -0
@@ -243,6 +257,12 @@ static int mfd_ioctl(struct inode *inode
 	switch (cmd) {
 	case MOUNTFD_IOC_GETDIRFD:
 		return mfd_getdirfd(filp);
+	case MOUNTFD_IOC_DETACH:
+		return mfd_umount(filp, MNT_DETACH);
+	case MOUNTFD_IOC_UNMOUNT:
+		return mfd_umount(filp, 0);
+	case MOUNTFD_IOC_FORCEDUNMOUNT:
+		return mfd_umount(filp, MNT_FORCE);

Urgg, you don't want to add gazillions of strange ioctls, do you?

#21 lore raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 50 lines · Christoph Hellwig
Quoted 48 lines · Mike Waychison

This patch adds functionality to mountfd so that a user can perform the various types of umount (forced umount, not-busy umount, lazy-umount).

---

fs/mountfd.c       |   20 ++++++++++++++++++++
fs/namespace.c     |    2 +-
include/linux/fs.h |    5 ++++-
3 files changed, 25 insertions(+), 2 deletions(-)

Index: linux-2.6.9-quilt/fs/mountfd.c
===================================================================

fs/mountfd.c +15 -0
@@ -11,6 +11,8 @@
 
 #define VFSMOUNT(filp) ((struct vfsmount *)((filp)->private_data))
 
+extern int do_umount(struct vfsmount *mnt, int flags);
+
 static struct vfsmount *mfdfs_mnt;
 
 static void mfdfs_read_inode(struct inode *inode);
@@ -72,6 +74,18 @@ static int mfd_release(struct inode *ino
 	return 0;
 }
 
+static long mfd_umount(struct file *mountfilp, int flags)
+{
+	struct vfsmount *mnt;
+	int error;
+	
+	mnt = mntget(VFSMOUNT(mountfilp));
+
+	error = do_umount(mnt, flags);
+
+	return error;
+}
+
 static int mfd_ioctl(struct inode *inode, struct file *filp,
 		     unsigned int cmd, unsigned long arg);
 static struct file_operations mfd_file_ops = {
@@ -243,6 +257,12 @@ static int mfd_ioctl(struct inode *inode
 	switch (cmd) {
 	case MOUNTFD_IOC_GETDIRFD:
 		return mfd_getdirfd(filp);
+	case MOUNTFD_IOC_DETACH:
Quoted 9 lines

fs/mountfd.c static int mfd_ioctl(struct inode *inode · line 265

Quoted 5 lines
fs/mountfd.c +5 -0
+		return mfd_umount(filp, MNT_DETACH);
+	case MOUNTFD_IOC_UNMOUNT:
+		return mfd_umount(filp, 0);
+	case MOUNTFD_IOC_FORCEDUNMOUNT:
+		return mfd_umount(filp, MNT_FORCE);

Urgg, you don't want to add gazillions of strange ioctls, do you?

Only a couple million ;)

I have toyed with different interfaces. I have older patches that would allow you to read/write on the fd to perform ops, but the code is a mess of parsing/data checks. I also toyed with a multiplexed syscall, but realized real quick that it was an ioctl with a different name, that only worked on mountfds.

ioctl ended up being the cleanest solution (I came up with) in the end.

Alternatives?

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBflxTdQs4kOxk3/MRAq0NAJ9leUcfelicGndvtqGXqGgMlNoREACfXmXe XsmXEDdefmJi7L8XmuKrQSk=
=xWa5
-----END PGP SIGNATURE-----