]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/file.c
take purely descriptor-related stuff from fcntl.c to file.c
[karo-tx-linux.git] / fs / file.c
index 92197dd9fdc8469fc161082a034d460a27b147a1..7f29544755d04324fe5a45d892b798330b96b239 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -6,6 +6,7 @@
  *  Manage the dynamic fd arrays in the process files_struct.
  */
 
+#include <linux/syscalls.h>
 #include <linux/export.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
@@ -794,3 +795,134 @@ struct file *fget_raw_light(unsigned int fd, int *fput_needed)
 
        return file;
 }
+
+void set_close_on_exec(unsigned int fd, int flag)
+{
+       struct files_struct *files = current->files;
+       struct fdtable *fdt;
+       spin_lock(&files->file_lock);
+       fdt = files_fdtable(files);
+       if (flag)
+               __set_close_on_exec(fd, fdt);
+       else
+               __clear_close_on_exec(fd, fdt);
+       spin_unlock(&files->file_lock);
+}
+
+bool get_close_on_exec(unsigned int fd)
+{
+       struct files_struct *files = current->files;
+       struct fdtable *fdt;
+       bool res;
+       rcu_read_lock();
+       fdt = files_fdtable(files);
+       res = close_on_exec(fd, fdt);
+       rcu_read_unlock();
+       return res;
+}
+
+SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags)
+{
+       int err = -EBADF;
+       struct file * file, *tofree;
+       struct files_struct * files = current->files;
+       struct fdtable *fdt;
+
+       if ((flags & ~O_CLOEXEC) != 0)
+               return -EINVAL;
+
+       if (newfd >= rlimit(RLIMIT_NOFILE))
+               return -EMFILE;
+
+       spin_lock(&files->file_lock);
+       err = expand_files(files, newfd);
+       file = fcheck(oldfd);
+       if (unlikely(!file))
+               goto Ebadf;
+       if (unlikely(err < 0)) {
+               if (err == -EMFILE)
+                       goto Ebadf;
+               goto out_unlock;
+       }
+       /*
+        * We need to detect attempts to do dup2() over allocated but still
+        * not finished descriptor.  NB: OpenBSD avoids that at the price of
+        * extra work in their equivalent of fget() - they insert struct
+        * file immediately after grabbing descriptor, mark it larval if
+        * more work (e.g. actual opening) is needed and make sure that
+        * fget() treats larval files as absent.  Potentially interesting,
+        * but while extra work in fget() is trivial, locking implications
+        * and amount of surgery on open()-related paths in VFS are not.
+        * FreeBSD fails with -EBADF in the same situation, NetBSD "solution"
+        * deadlocks in rather amusing ways, AFAICS.  All of that is out of
+        * scope of POSIX or SUS, since neither considers shared descriptor
+        * tables and this condition does not arise without those.
+        */
+       err = -EBUSY;
+       fdt = files_fdtable(files);
+       tofree = fdt->fd[newfd];
+       if (!tofree && fd_is_open(newfd, fdt))
+               goto out_unlock;
+       get_file(file);
+       rcu_assign_pointer(fdt->fd[newfd], file);
+       __set_open_fd(newfd, fdt);
+       if (flags & O_CLOEXEC)
+               __set_close_on_exec(newfd, fdt);
+       else
+               __clear_close_on_exec(newfd, fdt);
+       spin_unlock(&files->file_lock);
+
+       if (tofree)
+               filp_close(tofree, files);
+
+       return newfd;
+
+Ebadf:
+       err = -EBADF;
+out_unlock:
+       spin_unlock(&files->file_lock);
+       return err;
+}
+
+SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
+{
+       if (unlikely(newfd == oldfd)) { /* corner case */
+               struct files_struct *files = current->files;
+               int retval = oldfd;
+
+               rcu_read_lock();
+               if (!fcheck_files(files, oldfd))
+                       retval = -EBADF;
+               rcu_read_unlock();
+               return retval;
+       }
+       return sys_dup3(oldfd, newfd, 0);
+}
+
+SYSCALL_DEFINE1(dup, unsigned int, fildes)
+{
+       int ret = -EBADF;
+       struct file *file = fget_raw(fildes);
+
+       if (file) {
+               ret = get_unused_fd();
+               if (ret >= 0)
+                       fd_install(ret, file);
+               else
+                       fput(file);
+       }
+       return ret;
+}
+
+int f_dupfd(unsigned int from, struct file *file, unsigned flags)
+{
+       int err;
+       if (from >= rlimit(RLIMIT_NOFILE))
+               return -EINVAL;
+       err = alloc_fd(from, flags);
+       if (err >= 0) {
+               get_file(file);
+               fd_install(err, file);
+       }
+       return err;
+}