aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2008-05-08 19:42:56 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2008-05-16 17:22:26 -0400
commit02afc6267f6d55d47aba9fcafdbd1b7230d2294a (patch)
treef8cd675baf512fa6f6d561a5bccc0447bec2ff8b
parentf52111b1546943545e67573c4dde1c7613ca33d3 (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.c130
-rw-r--r--include/linux/fdtable.h1
-rw-r--r--kernel/fork.c130
3 files changed, 131 insertions, 130 deletions
diff --git a/fs/file.c b/fs/file.c
index 754cd05b06af..7dbadaaf00f0 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -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
264static 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
278static 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);
299out:
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 */
308struct 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
388out_release:
389 kmem_cache_free(files_cachep, newf);
390out:
391 return NULL;
392}
393
264static void __devinit fdtable_defer_list_init(int cpu) 394static 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 *);
93void put_files_struct(struct files_struct *fs); 93void put_files_struct(struct files_struct *fs);
94void reset_files_struct(struct files_struct *); 94void reset_files_struct(struct files_struct *);
95int unshare_files(struct files_struct **); 95int unshare_files(struct files_struct **);
96struct files_struct *dup_fd(struct files_struct *, int *);
96 97
97extern struct kmem_cache *files_cachep; 98extern 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
663static 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
677static 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);
698out:
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 */
707static 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
787out_release:
788 kmem_cache_free(files_cachep, newf);
789out:
790 return NULL;
791}
792
793static int copy_files(unsigned long clone_flags, struct task_struct * tsk) 663static 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;