From a1a5b3d93ca45613ec1d920fdb131b69b6553882 Mon Sep 17 00:00:00 2001 From: Peter Staubach Date: Tue, 13 Sep 2005 01:25:12 -0700 Subject: [PATCH] open returns ENFILE but creates file anyway 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 Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/open.c | 98 ++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 42 deletions(-) (limited to 'fs/open.c') diff --git a/fs/open.c b/fs/open.c index 2fac58c51910..f0d90cf0495c 100644 --- 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); /* -- cgit v1.2.3-58-ga151