diff options
Diffstat (limited to 'fs/open.c')
-rw-r--r-- | fs/open.c | 335 |
1 files changed, 203 insertions, 132 deletions
@@ -17,15 +17,10 @@ #include <linux/tty.h> #include <linux/time.h> #include <linux/mm.h> +#include <linux/file.h> -#include <asm/segment.h> - -extern void locks_remove_locks(struct task_struct *, struct file *); - -asmlinkage int sys_ustat(int dev, struct ustat * ubuf) -{ - return -ENOSYS; -} +#include <asm/uaccess.h> +#include <asm/bitops.h> asmlinkage int sys_statfs(const char * path, struct statfs * buf) { @@ -60,87 +55,115 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) return -EBADF; if (!(inode = file->f_inode)) return -ENOENT; + if (!inode->i_sb) + return -ENODEV; if (!inode->i_sb->s_op->statfs) return -ENOSYS; inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); return 0; } -asmlinkage int sys_truncate(const char * path, unsigned int length) +int do_truncate(struct inode *inode, unsigned long length) { - struct inode * inode; int error; struct iattr newattrs; + down(&inode->i_sem); + newattrs.ia_size = length; + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + error = notify_change(inode, &newattrs); + if (!error) { + /* truncate virtual mappings of this file */ + vmtruncate(inode, length); + if (inode->i_op && inode->i_op->truncate) + inode->i_op->truncate(inode); + } + up(&inode->i_sem); + return error; +} + +asmlinkage int sys_truncate(const char * path, unsigned long length) +{ + struct inode * inode; + int error; + error = namei(path,&inode); if (error) return error; - if (S_ISDIR(inode->i_mode)) { - iput(inode); - return -EACCES; - } - if ((error = permission(inode,MAY_WRITE)) != 0) { - iput(inode); - return error; - } - if (IS_RDONLY(inode)) { - iput(inode); - return -EROFS; - } - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { - iput(inode); - return -EPERM; - } + + error = -EACCES; + if (S_ISDIR(inode->i_mode)) + goto out; + + error = permission(inode,MAY_WRITE); + if (error) + goto out; + + error = -EROFS; + if (IS_RDONLY(inode)) + goto out; + + error = -EPERM; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + goto out; + error = get_write_access(inode); - if (error) { - iput(inode); - return error; + if (error) + goto out; + + error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL, + length < inode->i_size ? length : inode->i_size, + abs(inode->i_size - length)); + if (!error) { + if (inode->i_sb && inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize(inode, -1); + error = do_truncate(inode, length); } - inode->i_size = newattrs.ia_size = length; - if (inode->i_op && inode->i_op->truncate) - inode->i_op->truncate(inode); - newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME; - newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME; - inode->i_dirt = 1; - error = notify_change(inode, &newattrs); put_write_access(inode); +out: iput(inode); return error; } -asmlinkage int sys_ftruncate(unsigned int fd, unsigned int length) +asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) { struct inode * inode; struct file * file; - struct iattr newattrs; + int error; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) return -EBADF; if (!(inode = file->f_inode)) return -ENOENT; - if (S_ISDIR(inode->i_mode) || !(file->f_mode & 2)) + if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) return -EACCES; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EPERM; - inode->i_size = newattrs.ia_size = length; - if (inode->i_op && inode->i_op->truncate) - inode->i_op->truncate(inode); - newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME; - newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME; - inode->i_dirt = 1; - return notify_change(inode, &newattrs); + error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, + length < inode->i_size ? length : inode->i_size, + abs(inode->i_size - length)); + if (!error) + error = do_truncate(inode, length); + return error; } +#ifndef __alpha__ + +/* + * sys_utime() can be implemented in user-level using sys_utimes(). + * Is this for backwards compatibility? If so, why not move it + * into the appropriate arch directory (for those architectures that + * need it). + */ + /* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ asmlinkage int sys_utime(char * filename, struct utimbuf * times) { - struct inode * inode; - long actime,modtime; int error; - unsigned int flags = 0; + struct inode * inode; struct iattr newattrs; error = namei(filename,&inode); @@ -151,42 +174,38 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times) return -EROFS; } /* Don't worry, the checks are done in inode_change_ok() */ + newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = verify_area(VERIFY_READ, times, sizeof(*times)); if (error) { iput(inode); return error; } - actime = get_fs_long((unsigned long *) ×->actime); - modtime = get_fs_long((unsigned long *) ×->modtime); - newattrs.ia_ctime = CURRENT_TIME; - flags = ATTR_ATIME_SET | ATTR_MTIME_SET; + get_user(newattrs.ia_atime, ×->actime); + get_user(newattrs.ia_mtime, ×->modtime); + newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { - if ((error = permission(inode,MAY_WRITE)) != 0) { + if (current->fsuid != inode->i_uid && + (error = permission(inode,MAY_WRITE)) != 0) { iput(inode); return error; } - actime = modtime = newattrs.ia_ctime = CURRENT_TIME; } - newattrs.ia_atime = actime; - newattrs.ia_mtime = modtime; - newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | flags; - inode->i_dirt = 1; error = notify_change(inode, &newattrs); iput(inode); return error; } +#endif + /* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ asmlinkage int sys_utimes(char * filename, struct timeval * utimes) { - struct inode * inode; - long actime,modtime; int error; - unsigned int flags = 0; + struct inode * inode; struct iattr newattrs; error = namei(filename,&inode); @@ -197,6 +216,7 @@ asmlinkage int sys_utimes(char * filename, struct timeval * utimes) return -EROFS; } /* Don't worry, the checks are done in inode_change_ok() */ + newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (utimes) { struct timeval times[2]; error = verify_area(VERIFY_READ, utimes, sizeof(times)); @@ -204,22 +224,16 @@ asmlinkage int sys_utimes(char * filename, struct timeval * utimes) iput(inode); return error; } - memcpy_fromfs(×, utimes, sizeof(times)); - actime = times[0].tv_sec; - modtime = times[1].tv_sec; - newattrs.ia_ctime = CURRENT_TIME; - flags = ATTR_ATIME_SET | ATTR_MTIME_SET; + copy_from_user(×, utimes, sizeof(times)); + newattrs.ia_atime = times[0].tv_sec; + newattrs.ia_mtime = times[1].tv_sec; + newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { if ((error = permission(inode,MAY_WRITE)) != 0) { iput(inode); return error; } - actime = modtime = newattrs.ia_ctime = CURRENT_TIME; } - newattrs.ia_atime = actime; - newattrs.ia_mtime = modtime; - newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | flags; - inode->i_dirt = 1; error = notify_change(inode, &newattrs); iput(inode); return error; @@ -330,7 +344,6 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); - newattrs.ia_ctime = CURRENT_TIME; newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; inode->i_dirt = 1; return notify_change(inode, &newattrs); @@ -356,7 +369,6 @@ asmlinkage int sys_chmod(const char * filename, mode_t mode) if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); - newattrs.ia_ctime = CURRENT_TIME; newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; inode->i_dirt = 1; error = notify_change(inode, &newattrs); @@ -369,6 +381,7 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) struct inode * inode; struct file * file; struct iattr newattrs; + int error; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) return -EBADF; @@ -385,24 +398,35 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) newattrs.ia_mode = inode->i_mode; newattrs.ia_uid = user; newattrs.ia_gid = group; - newattrs.ia_ctime = CURRENT_TIME; newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; /* * If the owner has been changed, remove the setuid bit */ - if (user != inode->i_uid && (inode->i_mode & S_ISUID)) { + if (inode->i_mode & S_ISUID) { newattrs.ia_mode &= ~S_ISUID; newattrs.ia_valid |= ATTR_MODE; } /* * If the group has been changed, remove the setgid bit + * + * Don't remove the setgid bit if no group execute bit. + * This is a file marked for mandatory locking. */ - if (group != inode->i_gid && (inode->i_mode & S_ISGID)) { + if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) { newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } inode->i_dirt = 1; - return notify_change(inode, &newattrs); + if (inode->i_sb && inode->i_sb->dq_op) { + inode->i_sb->dq_op->initialize(inode, -1); + if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) + return -EDQUOT; + error = notify_change(inode, &newattrs); + if (error) + inode->i_sb->dq_op->transfer(inode, &newattrs, 1); + } else + error = notify_change(inode, &newattrs); + return error; } asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) @@ -429,24 +453,34 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) newattrs.ia_mode = inode->i_mode; newattrs.ia_uid = user; newattrs.ia_gid = group; - newattrs.ia_ctime = CURRENT_TIME; newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; /* * If the owner has been changed, remove the setuid bit */ - if (user != inode->i_uid && (inode->i_mode & S_ISUID)) { + if (inode->i_mode & S_ISUID) { newattrs.ia_mode &= ~S_ISUID; newattrs.ia_valid |= ATTR_MODE; } /* * If the group has been changed, remove the setgid bit + * + * Don't remove the setgid bit if no group execute bit. + * This is a file marked for mandatory locking. */ - if (group != inode->i_gid && (inode->i_mode & S_ISGID)) { + if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) { newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } inode->i_dirt = 1; - error = notify_change(inode, &newattrs); + if (inode->i_sb->dq_op) { + inode->i_sb->dq_op->initialize(inode, -1); + if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) + return -EDQUOT; + error = notify_change(inode, &newattrs); + if (error) + inode->i_sb->dq_op->transfer(inode, &newattrs, 1); + } else + error = notify_change(inode, &newattrs); iput(inode); return(error); } @@ -465,38 +499,28 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) * for the internal routines (ie open_namei()/follow_link() etc). 00 is * used by symlinks. */ -int do_open(const char * filename,int flags,int mode) +static int do_open(const char * filename,int flags,int mode, int fd) { struct inode * inode; struct file * f; - int flag,error,fd; - - for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++) - if (!current->files->fd[fd]) - break; - if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur) - return -EMFILE; - FD_CLR(fd,¤t->files->close_on_exec); + int flag,error; + f = get_empty_filp(); if (!f) return -ENFILE; - current->files->fd[fd] = f; f->f_flags = flag = flags; f->f_mode = (flag+1) & O_ACCMODE; if (f->f_mode) flag++; - if (flag & (O_TRUNC | O_CREAT)) + if (flag & O_TRUNC) flag |= 2; error = open_namei(filename,flag,mode,&inode,NULL); - if (!error && (f->f_mode & 2)) { + if (error) + goto cleanup_file; + if (f->f_mode & FMODE_WRITE) { error = get_write_access(inode); if (error) - iput(inode); - } - if (error) { - current->files->fd[fd]=NULL; - f->f_count--; - return error; + goto cleanup_inode; } f->f_inode = inode; @@ -507,36 +531,88 @@ int do_open(const char * filename,int flags,int mode) f->f_op = inode->i_op->default_file_ops; if (f->f_op && f->f_op->open) { error = f->f_op->open(inode,f); - if (error) { - if (f->f_mode & 2) put_write_access(inode); - iput(inode); - f->f_count--; - current->files->fd[fd]=NULL; - return error; - } + if (error) + goto cleanup_all; } f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); - return (fd); + + current->files->fd[fd] = f; + return 0; + +cleanup_all: + if (f->f_mode & FMODE_WRITE) + put_write_access(inode); +cleanup_inode: + iput(inode); +cleanup_file: + f->f_count--; + return error; +} + +/* + * Find an empty file descriptor entry, and mark it busy + */ +int get_unused_fd(void) +{ + int fd; + struct files_struct * files = current->files; + + fd = find_first_zero_bit(&files->open_fds, NR_OPEN); + if (fd < current->rlim[RLIMIT_NOFILE].rlim_cur) { + FD_SET(fd, &files->open_fds); + FD_CLR(fd, &files->close_on_exec); + return fd; + } + return -EMFILE; +} + +inline void put_unused_fd(int fd) +{ + FD_CLR(fd, ¤t->files->open_fds); } asmlinkage int sys_open(const char * filename,int flags,int mode) { char * tmp; - int error; + int fd, error; + fd = get_unused_fd(); + if (fd < 0) + return fd; error = getname(filename, &tmp); - if (error) - return error; - error = do_open(tmp,flags,mode); - putname(tmp); + if (!error) { + error = do_open(tmp,flags,mode, fd); + putname(tmp); + if (!error) + return fd; + } + put_unused_fd(fd); return error; } +#ifndef __alpha__ + +/* + * For backward compatibility? Maybe this should be moved + * into arch/i386 instead? + */ asmlinkage int sys_creat(const char * pathname, int mode) { return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } +#endif + +void __fput(struct file *filp, struct inode *inode) +{ + if (filp->f_op && filp->f_op->release) + filp->f_op->release(inode,filp); + filp->f_inode = NULL; + if (filp->f_mode & FMODE_WRITE) + put_write_access(inode); + iput(inode); +} + int close_fp(struct file *filp) { struct inode *inode; @@ -548,30 +624,25 @@ int close_fp(struct file *filp) inode = filp->f_inode; if (inode) locks_remove_locks(current, filp); - if (filp->f_count > 1) { - filp->f_count--; - return 0; - } - if (filp->f_op && filp->f_op->release) - filp->f_op->release(inode,filp); - filp->f_count--; - filp->f_inode = NULL; - if (filp->f_mode & 2) put_write_access(inode); - iput(inode); + fput(filp, inode); return 0; } asmlinkage int sys_close(unsigned int fd) -{ +{ + int error; struct file * filp; - - if (fd >= NR_OPEN) - return -EBADF; - FD_CLR(fd, ¤t->files->close_on_exec); - if (!(filp = current->files->fd[fd])) - return -EBADF; - current->files->fd[fd] = NULL; - return (close_fp (filp)); + struct files_struct * files; + + files = current->files; + error = -EBADF; + if (fd < NR_OPEN && (filp = files->fd[fd]) != NULL) { + put_unused_fd(fd); + FD_CLR(fd, &files->close_on_exec); + files->fd[fd] = NULL; + error = close_fp(filp); + } + return error; } /* |