diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
commit | d6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch) | |
tree | e2be02f33984c48ec019c654051d27964e42c441 /fs/fcntl.c | |
parent | 609d1e803baf519487233b765eb487f9ec227a18 (diff) |
Merge with 2.3.19.
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r-- | fs/fcntl.c | 126 |
1 files changed, 99 insertions, 27 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index d12e66244..26c9bf4a0 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -12,35 +12,89 @@ extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); -static inline int dupfd(struct file *file, unsigned int arg) +/* + * locate_fd finds a free file descriptor in the open_fds fdset, + * expanding the fd arrays if necessary. The files write lock will be + * held on exit to ensure that the fd can be entered atomically. + */ + +static inline int locate_fd(struct files_struct *files, + struct file *file, int start) { - struct files_struct * files = current->files; + unsigned int newfd; int error; - error = -EMFILE; write_lock(&files->file_lock); - arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg); - if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur) - goto out_putf; - FD_SET(arg, &files->open_fds); - FD_CLR(arg, &files->close_on_exec); - write_unlock(&files->file_lock); - fd_install(arg, file); - error = arg; + +repeat: + error = -EMFILE; + if (start < files->next_fd) + start = files->next_fd; + if (start >= files->max_fdset) { + expand: + error = expand_files(files, start); + if (error < 0) + goto out; + goto repeat; + } + + newfd = find_next_zero_bit(files->open_fds->fds_bits, + files->max_fdset, start); + + error = -EMFILE; + if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur) + goto out; + if (newfd >= files->max_fdset) + goto expand; + + error = expand_files(files, newfd); + if (error < 0) + goto out; + if (error) /* If we might have blocked, try again. */ + goto repeat; + + if (start <= files->next_fd) + files->next_fd = newfd + 1; + + error = newfd; + out: return error; +} + +static inline void allocate_fd(struct files_struct *files, + struct file *file, int fd) +{ + FD_SET(fd, files->open_fds); + FD_CLR(fd, files->close_on_exec); + write_unlock(&files->file_lock); + fd_install(fd, file); +} + +static int dupfd(struct file *file, int start) +{ + struct files_struct * files = current->files; + int ret; + + ret = locate_fd(files, file, start); + if (ret < 0) + goto out_putf; + allocate_fd(files, file, ret); + return ret; out_putf: + write_unlock(&files->file_lock); fput(file); - goto out; + return ret; } -asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) +asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) { int err = -EBADF; struct file * file; + struct files_struct * files = current->files; - read_lock(¤t->files->file_lock); + write_lock(¤t->files->file_lock); if (!(file = fcheck(oldfd))) goto out_unlock; err = newfd; @@ -49,22 +103,41 @@ asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) err = -EBADF; if (newfd >= NR_OPEN) goto out_unlock; /* following POSIX.1 6.2.1 */ - get_file(file); - read_unlock(¤t->files->file_lock); + get_file(file); /* We are now finished with oldfd */ + + err = expand_files(files, newfd); + if (err < 0) { + write_unlock(&files->file_lock); + fput(file); + goto out; + } + + /* To avoid races with open() and dup(), we will mark the fd as + * in-use in the open-file bitmap throughout the entire dup2() + * process. This is quite safe: do_close() uses the fd array + * entry, not the bitmap, to decide what work needs to be + * done. --sct */ + FD_SET(newfd, files->open_fds); + write_unlock(&files->file_lock); + + do_close(newfd, 0); + + write_lock(&files->file_lock); + allocate_fd(files, file, newfd); + err = newfd; - sys_close(newfd); - err = dupfd(file, newfd); out: return err; out_unlock: - read_unlock(¤t->files->file_lock); + write_unlock(¤t->files->file_lock); goto out; } -asmlinkage int sys_dup(unsigned int fildes) +asmlinkage long sys_dup(unsigned int fildes) { int ret = -EBADF; struct file * file = fget(fildes); + if (file) ret = dupfd(file, 0); return ret; @@ -117,13 +190,13 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) } break; case F_GETFD: - err = FD_ISSET(fd, ¤t->files->close_on_exec); + err = FD_ISSET(fd, current->files->close_on_exec); break; case F_SETFD: if (arg&1) - FD_SET(fd, ¤t->files->close_on_exec); + FD_SET(fd, current->files->close_on_exec); else - FD_CLR(fd, ¤t->files->close_on_exec); + FD_CLR(fd, current->files->close_on_exec); break; case F_GETFL: err = filp->f_flags; @@ -151,7 +224,6 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_owner.pid; break; case F_SETOWN: - err = 0; filp->f_owner.pid = arg; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; @@ -162,7 +234,8 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_owner.signum; break; case F_SETSIG: - if (arg <= 0 || arg > _NSIG) { + /* arg == 0 restores default behaviour. */ + if (arg < 0 || arg > _NSIG) { err = -EINVAL; break; } @@ -171,10 +244,9 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) break; default: /* sockets need a few special fcntls. */ + err = -EINVAL; if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, cmd, arg); - else - err = -EINVAL; break; } fput(filp); |