Commit 02afc6267f6d55d47aba9fcafdbd1b7230d2294a
1 parent
f52111b154
Exists in
master
and in
39 other branches
[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>
Showing 3 changed files with 131 additions and 130 deletions Side-by-side Diff
fs/file.c
... | ... | @@ -261,6 +261,136 @@ |
261 | 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 | 394 | static void __devinit fdtable_defer_list_init(int cpu) |
265 | 395 | { |
266 | 396 | struct fdtable_defer *fddef = &per_cpu(fdtable_defer_list, cpu); |
include/linux/fdtable.h
... | ... | @@ -93,6 +93,7 @@ |
93 | 93 | void put_files_struct(struct files_struct *fs); |
94 | 94 | void reset_files_struct(struct files_struct *); |
95 | 95 | int unshare_files(struct files_struct **); |
96 | +struct files_struct *dup_fd(struct files_struct *, int *); | |
96 | 97 | |
97 | 98 | extern struct kmem_cache *files_cachep; |
98 | 99 |
kernel/fork.c
... | ... | @@ -660,136 +660,6 @@ |
660 | 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 | 663 | static int copy_files(unsigned long clone_flags, struct task_struct * tsk) |
794 | 664 | { |
795 | 665 | struct files_struct *oldf, *newf; |