diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2008-05-08 19:42:56 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-05-16 17:22:26 -0400 |
commit | 02afc6267f6d55d47aba9fcafdbd1b7230d2294a (patch) | |
tree | f8cd675baf512fa6f6d561a5bccc0447bec2ff8b /kernel | |
parent | f52111b1546943545e67573c4dde1c7613ca33d3 (diff) |
[PATCH] dup_fd() fixes, part 1
Move the sucker to fs/file.c in preparation to the rest
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/fork.c | 130 |
1 files changed, 0 insertions, 130 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 933e60ebccae..19908b26cf80 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -660,136 +660,6 @@ static int copy_fs(unsigned long clone_flags, struct task_struct *tsk) | |||
660 | return 0; | 660 | return 0; |
661 | } | 661 | } |
662 | 662 | ||
663 | static int count_open_files(struct fdtable *fdt) | ||
664 | { | ||
665 | int size = fdt->max_fds; | ||
666 | int i; | ||
667 | |||
668 | /* Find the last open fd */ | ||
669 | for (i = size/(8*sizeof(long)); i > 0; ) { | ||
670 | if (fdt->open_fds->fds_bits[--i]) | ||
671 | break; | ||
672 | } | ||
673 | i = (i+1) * 8 * sizeof(long); | ||
674 | return i; | ||
675 | } | ||
676 | |||
677 | static struct files_struct *alloc_files(void) | ||
678 | { | ||
679 | struct files_struct *newf; | ||
680 | struct fdtable *fdt; | ||
681 | |||
682 | newf = kmem_cache_alloc(files_cachep, GFP_KERNEL); | ||
683 | if (!newf) | ||
684 | goto out; | ||
685 | |||
686 | atomic_set(&newf->count, 1); | ||
687 | |||
688 | spin_lock_init(&newf->file_lock); | ||
689 | newf->next_fd = 0; | ||
690 | fdt = &newf->fdtab; | ||
691 | fdt->max_fds = NR_OPEN_DEFAULT; | ||
692 | fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init; | ||
693 | fdt->open_fds = (fd_set *)&newf->open_fds_init; | ||
694 | fdt->fd = &newf->fd_array[0]; | ||
695 | INIT_RCU_HEAD(&fdt->rcu); | ||
696 | fdt->next = NULL; | ||
697 | rcu_assign_pointer(newf->fdt, fdt); | ||
698 | out: | ||
699 | return newf; | ||
700 | } | ||
701 | |||
702 | /* | ||
703 | * Allocate a new files structure and copy contents from the | ||
704 | * passed in files structure. | ||
705 | * errorp will be valid only when the returned files_struct is NULL. | ||
706 | */ | ||
707 | static struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) | ||
708 | { | ||
709 | struct files_struct *newf; | ||
710 | struct file **old_fds, **new_fds; | ||
711 | int open_files, size, i; | ||
712 | struct fdtable *old_fdt, *new_fdt; | ||
713 | |||
714 | *errorp = -ENOMEM; | ||
715 | newf = alloc_files(); | ||
716 | if (!newf) | ||
717 | goto out; | ||
718 | |||
719 | spin_lock(&oldf->file_lock); | ||
720 | old_fdt = files_fdtable(oldf); | ||
721 | new_fdt = files_fdtable(newf); | ||
722 | open_files = count_open_files(old_fdt); | ||
723 | |||
724 | /* | ||
725 | * Check whether we need to allocate a larger fd array and fd set. | ||
726 | * Note: we're not a clone task, so the open count won't change. | ||
727 | */ | ||
728 | if (open_files > new_fdt->max_fds) { | ||
729 | new_fdt->max_fds = 0; | ||
730 | spin_unlock(&oldf->file_lock); | ||
731 | spin_lock(&newf->file_lock); | ||
732 | *errorp = expand_files(newf, open_files-1); | ||
733 | spin_unlock(&newf->file_lock); | ||
734 | if (*errorp < 0) | ||
735 | goto out_release; | ||
736 | new_fdt = files_fdtable(newf); | ||
737 | /* | ||
738 | * Reacquire the oldf lock and a pointer to its fd table | ||
739 | * who knows it may have a new bigger fd table. We need | ||
740 | * the latest pointer. | ||
741 | */ | ||
742 | spin_lock(&oldf->file_lock); | ||
743 | old_fdt = files_fdtable(oldf); | ||
744 | } | ||
745 | |||
746 | old_fds = old_fdt->fd; | ||
747 | new_fds = new_fdt->fd; | ||
748 | |||
749 | memcpy(new_fdt->open_fds->fds_bits, | ||
750 | old_fdt->open_fds->fds_bits, open_files/8); | ||
751 | memcpy(new_fdt->close_on_exec->fds_bits, | ||
752 | old_fdt->close_on_exec->fds_bits, open_files/8); | ||
753 | |||
754 | for (i = open_files; i != 0; i--) { | ||
755 | struct file *f = *old_fds++; | ||
756 | if (f) { | ||
757 | get_file(f); | ||
758 | } else { | ||
759 | /* | ||
760 | * The fd may be claimed in the fd bitmap but not yet | ||
761 | * instantiated in the files array if a sibling thread | ||
762 | * is partway through open(). So make sure that this | ||
763 | * fd is available to the new process. | ||
764 | */ | ||
765 | FD_CLR(open_files - i, new_fdt->open_fds); | ||
766 | } | ||
767 | rcu_assign_pointer(*new_fds++, f); | ||
768 | } | ||
769 | spin_unlock(&oldf->file_lock); | ||
770 | |||
771 | /* compute the remainder to be cleared */ | ||
772 | size = (new_fdt->max_fds - open_files) * sizeof(struct file *); | ||
773 | |||
774 | /* This is long word aligned thus could use a optimized version */ | ||
775 | memset(new_fds, 0, size); | ||
776 | |||
777 | if (new_fdt->max_fds > open_files) { | ||
778 | int left = (new_fdt->max_fds-open_files)/8; | ||
779 | int start = open_files / (8 * sizeof(unsigned long)); | ||
780 | |||
781 | memset(&new_fdt->open_fds->fds_bits[start], 0, left); | ||
782 | memset(&new_fdt->close_on_exec->fds_bits[start], 0, left); | ||
783 | } | ||
784 | |||
785 | return newf; | ||
786 | |||
787 | out_release: | ||
788 | kmem_cache_free(files_cachep, newf); | ||
789 | out: | ||
790 | return NULL; | ||
791 | } | ||
792 | |||
793 | static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | 663 | static int copy_files(unsigned long clone_flags, struct task_struct * tsk) |
794 | { | 664 | { |
795 | struct files_struct *oldf, *newf; | 665 | struct files_struct *oldf, *newf; |