diff options
| -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; |
