summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/fs.h3
-rw-r--r--kernel/fork.c34
2 files changed, 37 insertions, 0 deletions
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b3a714c094d2..dbe66b285870 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1408,5 +1408,8 @@ static inline ino_t parent_ino(struct dentry *dentry)
return res;
}
+/* kernel/fork.c */
+extern int unshare_files(void);
+
#endif /* __KERNEL__ */
#endif /* _LINUX_FS_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 381b80b510ba..0603f230146b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -642,6 +642,11 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
goto out;
}
+ /*
+ * Note: we may be using current for both targets (See exec.c)
+ * This works because we cache current->files (old) as oldf. Don't
+ * break this.
+ */
tsk->files = NULL;
error = -ENOMEM;
newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
@@ -731,6 +736,35 @@ out_release:
goto out;
}
+/*
+ * Helper to unshare the files of the current task.
+ * We don't want to expose copy_files internals to
+ * the exec layer of the kernel.
+ */
+
+int unshare_files(void)
+{
+ struct files_struct *files = current->files;
+ int rc;
+
+ if(!files)
+ BUG();
+
+ /* This can race but the race causes us to copy when we don't
+ need to and drop the copy */
+ if(atomic_read(&files->count) == 1)
+ {
+ atomic_inc(&files->count);
+ return 0;
+ }
+ rc = copy_files(0, current);
+ if(rc)
+ current->files = files;
+ return rc;
+}
+
+EXPORT_SYMBOL(unshare_files);
+
static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
{
struct sighand_struct *sig;