linux-old/fs/file.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/file.c
   3 *
   4 *  Copyright (C) 1998, Stephen Tweedie and Bill Hawes
   5 *
   6 *  Manage the dynamic fd arrays in the process files_struct.
   7 */
   8
   9#include <linux/fs.h>
  10#include <linux/mm.h>
  11#include <linux/sched.h>
  12#include <linux/malloc.h>
  13#include <linux/vmalloc.h>
  14
  15#include <asm/bitops.h>
  16
  17
  18/*
  19 * Allocate an fd array, using get_free_page() if possible.
  20 * Note: the array isn't cleared at allocation time.
  21 */
  22struct file ** alloc_fd_array(int num)
  23{
  24        struct file **new_fds;
  25        int size = num * sizeof(struct file *);
  26
  27        if (size < PAGE_SIZE)
  28                new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
  29        else if (size == PAGE_SIZE)
  30                new_fds = (struct file **) __get_free_page(GFP_KERNEL);
  31        else
  32                new_fds = (struct file **) vmalloc(size);
  33        return new_fds;
  34}
  35
  36void free_fd_array(struct file **array, int num)
  37{
  38        int size = num * sizeof(struct file *);
  39
  40        if (!array) {
  41                printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
  42                return;
  43        }
  44
  45        if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */
  46                return;
  47        else if (size < PAGE_SIZE)
  48                kfree(array);
  49        else if (size == PAGE_SIZE)
  50                free_page((unsigned long) array);
  51        else
  52                vfree(array);
  53}
  54
  55/*
  56 * Expand the fd array in the files_struct.
  57 */
  58
  59int expand_fd_array(struct files_struct *files, int nr)
  60{
  61        struct file **new_fds;
  62        int error, nfds;
  63
  64        
  65        error = -EMFILE;
  66        if (files->max_fds >= NR_OPEN || nr > NR_OPEN)
  67                goto out;
  68
  69        nfds = files->max_fds;
  70
  71        /* 
  72         * Expand to the max in easy steps, and keep expanding it until
  73         * we have enough for the requested fd array size. 
  74         */
  75
  76        do {
  77#if NR_OPEN_DEFAULT < 256
  78                if (nfds < 256)
  79                        nfds = 256;
  80                else 
  81#endif
  82                if (nfds < (PAGE_SIZE / sizeof(struct file *)))
  83                        nfds = PAGE_SIZE / sizeof(struct file *);
  84                else {
  85                        nfds = nfds * 2;
  86                        if (nfds > NR_OPEN)
  87                                nfds = NR_OPEN;
  88                }
  89        } while (nfds < nr);
  90
  91        error = -ENOMEM;
  92        new_fds = alloc_fd_array(nfds);
  93        if (!new_fds)
  94                goto out;
  95
  96        /* Copy the existing array and install the new pointer */
  97
  98        if (nfds > files->max_fds) {
  99                struct file **old_fds;
 100                int i = files->max_fds;
 101                
 102                old_fds = files->fd;
 103                files->fd = new_fds;
 104                files->max_fds = nfds;
 105                /* Don't copy/clear the array if we are creating a new
 106                   fd array for fork() */
 107                if (i) {
 108                        memcpy(new_fds, old_fds, i * sizeof(struct file *));
 109                        /* clear the remainder of the array */
 110                        memset(&new_fds[i], 0,
 111                               (nfds-i) * sizeof(struct file *)); 
 112                        free_fd_array(old_fds, i);
 113                }
 114        } else {
 115                /* Somebody expanded the array while we slept ... */
 116                free_fd_array(new_fds, nfds);
 117        }
 118        error = 0;
 119out:
 120        return error;
 121}
 122
 123/*
 124 * Allocate an fdset array, using get_free_page() if possible.
 125 * Note: the array isn't cleared at allocation time.
 126 */
 127fd_set * alloc_fdset(int num)
 128{
 129        fd_set *new_fdset;
 130        int size = num / 8;
 131
 132        if (size < PAGE_SIZE)
 133                new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL);
 134        else if (size == PAGE_SIZE)
 135                new_fdset = (fd_set *) __get_free_page(GFP_KERNEL);
 136        else
 137                new_fdset = (fd_set *) vmalloc(size);
 138        return new_fdset;
 139}
 140
 141void free_fdset(fd_set *array, int num)
 142{
 143        int size = num / 8;
 144
 145        if (!array) {
 146                printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
 147                return;
 148        }
 149        
 150        if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
 151                return;
 152        else if (size < PAGE_SIZE)
 153                kfree(array);
 154        else if (size == PAGE_SIZE)
 155                free_page((unsigned long) array);
 156        else
 157                vfree(array);
 158}
 159
 160/*
 161 * Expand the fdset in the files_struct.
 162 */
 163int expand_fdset(struct files_struct *files, int nr)
 164{
 165        fd_set *new_openset = 0, *new_execset = 0;
 166        int error, nfds = 0;
 167
 168        error = -EMFILE;
 169        if (files->max_fdset >= NR_OPEN || nr > NR_OPEN)
 170                goto out;
 171
 172        nfds = files->max_fdset;
 173        /* Expand to the max in easy steps */
 174        do {
 175                if (nfds < (PAGE_SIZE * 8))
 176                        nfds = PAGE_SIZE * 8;
 177                else {
 178                        nfds = nfds * 2;
 179                        if (nfds > NR_OPEN)
 180                                nfds = NR_OPEN;
 181                }
 182        } while (nfds < nr);
 183
 184        error = -ENOMEM;
 185        new_openset = alloc_fdset(nfds);
 186        new_execset = alloc_fdset(nfds);
 187        if (!new_openset || !new_execset)
 188                goto out;
 189
 190        error = 0;
 191        
 192        /* Copy the existing tables and install the new pointers */
 193        if (nfds > files->max_fdset) {
 194                int i = files->max_fdset / (sizeof(unsigned long) * 8);
 195                int count = (nfds - files->max_fdset) / 8;
 196
 197                /* 
 198                 * Don't copy the entire array if the current fdset is
 199                 * not yet initialised.  
 200                 */
 201                if (i) {
 202                        memcpy (new_openset, files->open_fds, files->max_fdset/8);
 203                        memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
 204                        memset (&new_openset->fds_bits[i], 0, count);
 205                        memset (&new_execset->fds_bits[i], 0, count);
 206                }
 207
 208                free_fdset (files->close_on_exec, files->max_fdset);
 209                free_fdset (files->open_fds, files->max_fdset);
 210                files->max_fdset = nfds;
 211                files->open_fds = new_openset;
 212                files->close_on_exec = new_execset;
 213                return 0;
 214        } 
 215        /* Somebody expanded the array while we slept ... */
 216
 217out:
 218        if (new_openset)
 219                free_fdset(new_openset, nfds);
 220        if (new_execset)
 221                free_fdset(new_execset, nfds);
 222        return error;
 223}
 224
 225
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.