aboutsummaryrefslogtreecommitdiffstats
path: root/fs
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 /fs
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/file.c130
1 files changed, 130 insertions, 0 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);