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 | |
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>
-rw-r--r-- | fs/file.c | 130 | ||||
-rw-r--r-- | include/linux/fdtable.h | 1 | ||||
-rw-r--r-- | kernel/fork.c | 130 |
3 files changed, 131 insertions, 130 deletions
@@ -261,6 +261,136 @@ int expand_files(struct files_struct *files, int nr) | |||
261 | return expand_fdtable(files, nr); | 261 | return expand_fdtable(files, nr); |
262 | } | 262 | } |
263 | 263 | ||
264 | static int count_open_files(struct fdtable *fdt) | ||
265 | { | ||
266 | int size = fdt->max_fds; | ||
267 | int i; | ||
268 | |||
269 | /* Find the last open fd */ | ||
270 | for (i = size/(8*sizeof(long)); i > 0; ) { | ||
271 | if (fdt->open_fds->fds_bits[--i]) | ||
272 | break; | ||
273 | } | ||
274 | i = (i+1) * 8 * sizeof(long); | ||
275 | return i; | ||
276 | } | ||
277 | |||
278 | static struct files_struct *alloc_files(void) | ||
279 | { | ||
280 | struct files_struct *newf; | ||
281 | struct fdtable *fdt; | ||
282 | |||
283 | newf = kmem_cache_alloc(files_cachep, GFP_KERNEL); | ||
284 | if (!newf) | ||
285 | goto out; | ||
286 | |||
287 | atomic_set(&newf->count, 1); | ||
288 | |||
289 | spin_lock_init(&newf->file_lock); | ||
290 | newf->next_fd = 0; | ||
291 | fdt = &newf->fdtab; | ||
292 | fdt->max_fds = NR_OPEN_DEFAULT; | ||
293 | fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init; | ||
294 | fdt->open_fds = (fd_set *)&newf->open_fds_init; | ||
295 | fdt->fd = &newf->fd_array[0]; | ||
296 | INIT_RCU_HEAD(&fdt->rcu); | ||
297 | fdt->next = NULL; | ||
298 | rcu_assign_pointer(newf->fdt, fdt); | ||
299 | out: | ||
300 | return newf; | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * Allocate a new files structure and copy contents from the | ||
305 | * passed in files structure. | ||
306 | * errorp will be valid only when the returned files_struct is NULL. | ||
307 | */ | ||
308 | struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) | ||
309 | { | ||
310 | struct files_struct *newf; | ||
311 | struct file **old_fds, **new_fds; | ||
312 | int open_files, size, i; | ||
313 | struct fdtable *old_fdt, *new_fdt; | ||
314 | |||
315 | *errorp = -ENOMEM; | ||
316 | newf = alloc_files(); | ||
317 | if (!newf) | ||
318 | goto out; | ||
319 | |||
320 | spin_lock(&oldf->file_lock); | ||
321 | old_fdt = files_fdtable(oldf); | ||
322 | new_fdt = files_fdtable(newf); | ||
323 | open_files = count_open_files(old_fdt); | ||
324 | |||
325 | /* | ||
326 | * Check whether we need to allocate a larger fd array and fd set. | ||
327 | * Note: we're not a clone task, so the open count won't change. | ||
328 | */ | ||
329 | if (open_files > new_fdt->max_fds) { | ||
330 | new_fdt->max_fds = 0; | ||
331 | spin_unlock(&oldf->file_lock); | ||
332 | spin_lock(&newf->file_lock); | ||
333 | *errorp = expand_files(newf, open_files-1); | ||
334 | spin_unlock(&newf->file_lock); | ||
335 | if (*errorp < 0) | ||
336 | goto out_release; | ||
337 | new_fdt = files_fdtable(newf); | ||
338 | /* | ||
339 | * Reacquire the oldf lock and a pointer to its fd table | ||
340 | * who knows it may have a new bigger fd table. We need | ||
341 | * the latest pointer. | ||
342 | */ | ||
343 | spin_lock(&oldf->file_lock); | ||
344 | old_fdt = files_fdtable(oldf); | ||
345 | } | ||
346 | |||
347 | old_fds = old_fdt->fd; | ||
348 | new_fds = new_fdt->fd; | ||
349 | |||
350 | memcpy(new_fdt->open_fds->fds_bits, | ||
351 | old_fdt->open_fds->fds_bits, open_files/8); | ||
352 | memcpy(new_fdt->close_on_exec->fds_bits, | ||
353 | old_fdt->close_on_exec->fds_bits, open_files/8); | ||
354 | |||
355 | for (i = open_files; i != 0; i--) { | ||
356 | struct file *f = *old_fds++; | ||
357 | if (f) { | ||
358 | get_file(f); | ||
359 | } else { | ||
360 | /* | ||
361 | * The fd may be claimed in the fd bitmap but not yet | ||
362 | * instantiated in the files array if a sibling thread | ||
363 | * is partway through open(). So make sure that this | ||
364 | * fd is available to the new process. | ||
365 | */ | ||
366 | FD_CLR(open_files - i, new_fdt->open_fds); | ||
367 | } | ||
368 | rcu_assign_pointer(*new_fds++, f); | ||
369 | } | ||
370 | spin_unlock(&oldf->file_lock); | ||
371 | |||
372 | /* compute the remainder to be cleared */ | ||
373 | size = (new_fdt->max_fds - open_files) * sizeof(struct file *); | ||
374 | |||
375 | /* This is long word aligned thus could use a optimized version */ | ||
376 | memset(new_fds, 0, size); | ||
377 | |||
378 | if (new_fdt->max_fds > open_files) { | ||
379 | int left = (new_fdt->max_fds-open_files)/8; | ||
380 | int start = open_files / (8 * sizeof(unsigned long)); | ||
381 | |||
382 | memset(&new_fdt->open_fds->fds_bits[start], 0, left); | ||
383 | memset(&new_fdt->close_on_exec->fds_bits[start], 0, left); | ||
384 | } | ||
385 | |||
386 | return newf; | ||
387 | |||
388 | out_release: | ||
389 | kmem_cache_free(files_cachep, newf); | ||
390 | out: | ||
391 | return NULL; | ||
392 | } | ||
393 | |||
264 | static void __devinit fdtable_defer_list_init(int cpu) | 394 | static void __devinit fdtable_defer_list_init(int cpu) |
265 | { | 395 | { |
266 | struct fdtable_defer *fddef = &per_cpu(fdtable_defer_list, cpu); | 396 | struct fdtable_defer *fddef = &per_cpu(fdtable_defer_list, cpu); |
diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h index a118f3c0b240..4aab6f12cfab 100644 --- a/include/linux/fdtable.h +++ b/include/linux/fdtable.h | |||
@@ -93,6 +93,7 @@ struct files_struct *get_files_struct(struct task_struct *); | |||
93 | void put_files_struct(struct files_struct *fs); | 93 | void put_files_struct(struct files_struct *fs); |
94 | void reset_files_struct(struct files_struct *); | 94 | void reset_files_struct(struct files_struct *); |
95 | int unshare_files(struct files_struct **); | 95 | int unshare_files(struct files_struct **); |
96 | struct files_struct *dup_fd(struct files_struct *, int *); | ||
96 | 97 | ||
97 | extern struct kmem_cache *files_cachep; | 98 | extern struct kmem_cache *files_cachep; |
98 | 99 | ||
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; |