[PATCH] open returns ENFILE but creates file anyway
authorPeter Staubach <staubach@redhat.com>
Tue, 13 Sep 2005 08:25:12 +0000 (01:25 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Tue, 13 Sep 2005 15:22:28 +0000 (08:22 -0700)
When open(O_CREAT) is called and the error, ENFILE, is returned, the file
may be created anyway.  This is counter intuitive, against the SUS V3
specification, and may cause applications to misbehave if they are not
coded correctly to handle this semantic.  The SUS V3 specification
explicitly states "No files shall be created or modified if the function
returns -1.".

The error, ENFILE, is used to indicate the system wide open file table is
full and no more file structs can be allocated.

This is due to an ordering problem.  The entry in the directory is created
before the file struct is allocated.  If the allocation for the file struct
fails, then the system call must return an error, but the directory entry
was already created and can not be safely removed.

The solution to this situation is relatively easy.  The file struct should
be allocated before the directory entry is created.  If the allocation
fails, then the error can be returned directly.  If the creation of the
directory entry fails, then the file struct can be easily freed.

Signed-off-by: Peter Staubach <staubach@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/open.c

index 2fac58c51910cf250607a8f7161edc608d8c18cc..f0d90cf0495c490316d427e309c38695ceb4e005 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -738,52 +738,15 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
        return error;
 }
 
-/*
- * Note that while the flag value (low two bits) for sys_open means:
- *     00 - read-only
- *     01 - write-only
- *     10 - read-write
- *     11 - special
- * it is changed into
- *     00 - no permissions needed
- *     01 - read-permission
- *     10 - write-permission
- *     11 - read-write
- * for the internal routines (ie open_namei()/follow_link() etc). 00 is
- * used by symlinks.
- */
-struct file *filp_open(const char * filename, int flags, int mode)
-{
-       int namei_flags, error;
-       struct nameidata nd;
-
-       namei_flags = flags;
-       if ((namei_flags+1) & O_ACCMODE)
-               namei_flags++;
-       if (namei_flags & O_TRUNC)
-               namei_flags |= 2;
-
-       error = open_namei(filename, namei_flags, mode, &nd);
-       if (!error)
-               return dentry_open(nd.dentry, nd.mnt, flags);
-
-       return ERR_PTR(error);
-}
-
-EXPORT_SYMBOL(filp_open);
-
-struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
+                                       int flags, struct file *f)
 {
-       struct file * f;
        struct inode *inode;
        int error;
 
-       error = -ENFILE;
-       f = get_empty_filp();
-       if (!f)
-               goto cleanup_dentry;
        f->f_flags = flags;
-       f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
+       f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK |
+                               FMODE_PREAD | FMODE_PWRITE;
        inode = dentry->d_inode;
        if (f->f_mode & FMODE_WRITE) {
                error = get_write_access(inode);
@@ -828,12 +791,63 @@ cleanup_all:
        f->f_vfsmnt = NULL;
 cleanup_file:
        put_filp(f);
-cleanup_dentry:
        dput(dentry);
        mntput(mnt);
        return ERR_PTR(error);
 }
 
+/*
+ * Note that while the flag value (low two bits) for sys_open means:
+ *     00 - read-only
+ *     01 - write-only
+ *     10 - read-write
+ *     11 - special
+ * it is changed into
+ *     00 - no permissions needed
+ *     01 - read-permission
+ *     10 - write-permission
+ *     11 - read-write
+ * for the internal routines (ie open_namei()/follow_link() etc). 00 is
+ * used by symlinks.
+ */
+struct file *filp_open(const char * filename, int flags, int mode)
+{
+       int namei_flags, error;
+       struct nameidata nd;
+       struct file *f;
+
+       namei_flags = flags;
+       if ((namei_flags+1) & O_ACCMODE)
+               namei_flags++;
+       if (namei_flags & O_TRUNC)
+               namei_flags |= 2;
+
+       error = -ENFILE;
+       f = get_empty_filp();
+       if (f == NULL)
+               return ERR_PTR(error);
+
+       error = open_namei(filename, namei_flags, mode, &nd);
+       if (!error)
+               return __dentry_open(nd.dentry, nd.mnt, flags, f);
+
+       put_filp(f);
+       return ERR_PTR(error);
+}
+EXPORT_SYMBOL(filp_open);
+
+struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+{
+       int error;
+       struct file *f;
+
+       error = -ENFILE;
+       f = get_empty_filp();
+       if (f == NULL)
+               return ERR_PTR(error);
+
+       return __dentry_open(dentry, mnt, flags, f);
+}
 EXPORT_SYMBOL(dentry_open);
 
 /*