Hi,
This patch, against 2.3.99pre3 (or thereabouts) implements an
O_DEFEROPEN option to open(2).
The intent is that programs can avoid stat()/open() races, and
the risk of side-effects to open() (tape rewinding being the
canonical example) by doing open(.., O_DEFEROPEN|etc), fstat(fd)
and then fcntl(fd, F_REALLYOPEN).
I have tried to keep it simple, by leaving all but the target-
specific open call inside the open() path. Until the fcntl(),
the file descriptor should look a lot like a revoke()d fd which
hopefully means that someone else has thought of all of the
potential security risks that this exposes.
An arguable misfeature is that you can't use this facility on
files that you couldn't access anyway. In the spirit of KISS,
I think that this is a good idea. if(open() < 0) stat() isn't
atomic, but that's probably OK.
So:
* Does the idea suck? (It wasn't mine :-)
* Does the implementation suck?
* Is it a sufficiently useful facility to sneak under the
code-freeze?
Cheers,
Matthew.
Index: linux/fs/fcntl.c
===================================================================
RCS file: /cvs/linux/linux/fs/fcntl.c,v
retrieving revision 1.29
diff -u -r1.29 fcntl.c
--- linux/fs/fcntl.c 2000/02/25 06:02:43 1.29
+++ linux/fs/fcntl.c 2000/03/24 22:56:20
@@ -244,6 +244,24 @@
err = 0;
filp->f_owner.signum = arg;
break;
+ case F_REALLYOPEN:
+ if (filp->f_mode & O_DEFEROPEN) {
+ struct inode * inode;
+ struct file_operations * fop;
+
+ /* only get one shot at opening the file */
+ filp->f_mode &= ~O_DEFEROPEN;
+
+ inode = filp->f_dentry->d_inode;
+ fop = inode->i_fop;
+ err = 0;
+ if (fop && fop->open)
+ err = fop->open(inode, filp);
+ if (!err)
+ filp->f_op = fop;
+ } else
+ err = -EINVAL;
+ break;
default:
/* sockets need a few special fcntls. */
err = -EINVAL;
Index: linux/fs/open.c
===================================================================
RCS file: /cvs/linux/linux/fs/open.c,v
retrieving revision 1.64
diff -u -r1.64 open.c
--- linux/fs/open.c 2000/03/24 01:32:37 1.64
+++ linux/fs/open.c 2000/03/24 22:56:21
@@ -13,6 +13,21 @@
#include <asm/uaccess.h>
+e_badf() { return -EBADF; }
+static struct file_operations deferred_fops = {
+llseek: e_badf,
+read: e_badf,
+write: e_badf,
+readdir:e_badf,
+poll: e_badf,
+ioctl: e_badf,
+fsync: e_badf,
+fasync: e_badf,
+lock: e_badf,
+readv: e_badf,
+writev: e_badf,
+};
+
asmlinkage long sys_statfs(const char * path, struct statfs * buf)
{
struct dentry * dentry;
@@ -685,16 +700,17 @@
f->f_dentry = dentry;
f->f_pos = 0;
f->f_reada = 0;
- f->f_op = NULL;
- if (inode->i_op)
- f->f_op = inode->i_fop;
+ f->f_op = inode->i_fop;
if (inode->i_sb)
file_move(f, &inode->i_sb->s_files);
- if (f->f_op && f->f_op->open) {
- error = f->f_op->open(inode,f);
- if (error)
- goto cleanup_all;
- }
+ if (f->f_flags & O_DEFEROPEN)
+ f->f_op = &deferred_fops;
+ else
+ if (f->f_op && f->f_op->open) {
+ error = f->f_op->open(inode,f);
+ if (error)
+ goto cleanup_all;
+ }
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
return f;
Index: linux/include/asm-i386/fcntl.h
===================================================================
RCS file: /cvs/linux/linux/include/asm-i386/fcntl.h,v
retrieving revision 1.5
diff -u -r1.5 fcntl.h
--- linux/include/asm-i386/fcntl.h 1998/10/26 20:03:04 1.5
+++ linux/include/asm-i386/fcntl.h 2000/03/24 22:56:21
@@ -20,6 +20,7 @@
#define O_LARGEFILE 0100000
#define O_DIRECTORY 0200000 /* must be a directory */
#define O_NOFOLLOW 0400000 /* don't follow links */
+#define O_DEFEROPEN 01000000 /* don't call target-specific open routine */
#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get f_flags */
@@ -34,6 +35,8 @@
#define F_GETOWN 9 /* for sockets. */
#define F_SETSIG 10 /* for sockets. */
#define F_GETSIG 11 /* for sockets. */
+
+#define F_REALLYOPEN 12 /* counterpart to O_DEFEROPEN */
/* for F_[GET|SET]FL */
#define FD_CLOEXEC 1 /* actually anything with low bit set goes */