diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/file.c | 130 |
1 files changed, 130 insertions, 0 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); |