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 */

Reply via email to