blob: a2d1db2ea9840d5067bf3d762f19ffe5f7b99ffd [file] [log] [blame]
Paul Gortmaker630d9c42011-11-16 23:57:37 -05001#include <linux/export.h>
Al Viro3e93cd62009-03-29 19:00:13 -04002#include <linux/sched.h>
3#include <linux/fs.h>
4#include <linux/path.h>
5#include <linux/slab.h>
Al Viro5ad4e532009-03-29 19:50:06 -04006#include <linux/fs_struct.h>
Al Virof03c6592011-01-14 22:30:21 -05007#include "internal.h"
8
9static inline void path_get_longterm(struct path *path)
10{
11 path_get(path);
12 mnt_make_longterm(path->mnt);
13}
14
15static inline void path_put_longterm(struct path *path)
16{
17 mnt_make_shortterm(path->mnt);
18 path_put(path);
19}
Al Viro3e93cd62009-03-29 19:00:13 -040020
21/*
22 * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
23 * It can block.
24 */
25void set_fs_root(struct fs_struct *fs, struct path *path)
26{
27 struct path old_root;
28
Nick Piggin2a4419b2010-08-18 04:37:33 +100029 spin_lock(&fs->lock);
Nick Pigginc28cc362011-01-07 17:49:53 +110030 write_seqcount_begin(&fs->seq);
Al Viro3e93cd62009-03-29 19:00:13 -040031 old_root = fs->root;
32 fs->root = *path;
Al Virof03c6592011-01-14 22:30:21 -050033 path_get_longterm(path);
Nick Pigginc28cc362011-01-07 17:49:53 +110034 write_seqcount_end(&fs->seq);
Nick Piggin2a4419b2010-08-18 04:37:33 +100035 spin_unlock(&fs->lock);
Al Viro3e93cd62009-03-29 19:00:13 -040036 if (old_root.dentry)
Al Virof03c6592011-01-14 22:30:21 -050037 path_put_longterm(&old_root);
Al Viro3e93cd62009-03-29 19:00:13 -040038}
39
40/*
41 * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
42 * It can block.
43 */
44void set_fs_pwd(struct fs_struct *fs, struct path *path)
45{
46 struct path old_pwd;
47
Nick Piggin2a4419b2010-08-18 04:37:33 +100048 spin_lock(&fs->lock);
Nick Pigginc28cc362011-01-07 17:49:53 +110049 write_seqcount_begin(&fs->seq);
Al Viro3e93cd62009-03-29 19:00:13 -040050 old_pwd = fs->pwd;
51 fs->pwd = *path;
Al Virof03c6592011-01-14 22:30:21 -050052 path_get_longterm(path);
Nick Pigginc28cc362011-01-07 17:49:53 +110053 write_seqcount_end(&fs->seq);
Nick Piggin2a4419b2010-08-18 04:37:33 +100054 spin_unlock(&fs->lock);
Al Viro3e93cd62009-03-29 19:00:13 -040055
56 if (old_pwd.dentry)
Al Virof03c6592011-01-14 22:30:21 -050057 path_put_longterm(&old_pwd);
Al Viro3e93cd62009-03-29 19:00:13 -040058}
59
60void chroot_fs_refs(struct path *old_root, struct path *new_root)
61{
62 struct task_struct *g, *p;
63 struct fs_struct *fs;
64 int count = 0;
65
66 read_lock(&tasklist_lock);
67 do_each_thread(g, p) {
68 task_lock(p);
69 fs = p->fs;
70 if (fs) {
Nick Piggin2a4419b2010-08-18 04:37:33 +100071 spin_lock(&fs->lock);
Nick Pigginc28cc362011-01-07 17:49:53 +110072 write_seqcount_begin(&fs->seq);
Al Viro3e93cd62009-03-29 19:00:13 -040073 if (fs->root.dentry == old_root->dentry
74 && fs->root.mnt == old_root->mnt) {
Al Virof03c6592011-01-14 22:30:21 -050075 path_get_longterm(new_root);
Al Viro3e93cd62009-03-29 19:00:13 -040076 fs->root = *new_root;
77 count++;
78 }
79 if (fs->pwd.dentry == old_root->dentry
80 && fs->pwd.mnt == old_root->mnt) {
Al Virof03c6592011-01-14 22:30:21 -050081 path_get_longterm(new_root);
Al Viro3e93cd62009-03-29 19:00:13 -040082 fs->pwd = *new_root;
83 count++;
84 }
Nick Pigginc28cc362011-01-07 17:49:53 +110085 write_seqcount_end(&fs->seq);
Nick Piggin2a4419b2010-08-18 04:37:33 +100086 spin_unlock(&fs->lock);
Al Viro3e93cd62009-03-29 19:00:13 -040087 }
88 task_unlock(p);
89 } while_each_thread(g, p);
90 read_unlock(&tasklist_lock);
91 while (count--)
Al Virof03c6592011-01-14 22:30:21 -050092 path_put_longterm(old_root);
Al Viro3e93cd62009-03-29 19:00:13 -040093}
94
Al Viro498052b2009-03-30 07:20:30 -040095void free_fs_struct(struct fs_struct *fs)
Al Viro3e93cd62009-03-29 19:00:13 -040096{
Al Virof03c6592011-01-14 22:30:21 -050097 path_put_longterm(&fs->root);
98 path_put_longterm(&fs->pwd);
Al Viro498052b2009-03-30 07:20:30 -040099 kmem_cache_free(fs_cachep, fs);
Al Viro3e93cd62009-03-29 19:00:13 -0400100}
101
102void exit_fs(struct task_struct *tsk)
103{
Al Viro498052b2009-03-30 07:20:30 -0400104 struct fs_struct *fs = tsk->fs;
Al Viro3e93cd62009-03-29 19:00:13 -0400105
106 if (fs) {
Al Viro498052b2009-03-30 07:20:30 -0400107 int kill;
Al Viro3e93cd62009-03-29 19:00:13 -0400108 task_lock(tsk);
Nick Piggin2a4419b2010-08-18 04:37:33 +1000109 spin_lock(&fs->lock);
Nick Pigginc28cc362011-01-07 17:49:53 +1100110 write_seqcount_begin(&fs->seq);
Al Viro3e93cd62009-03-29 19:00:13 -0400111 tsk->fs = NULL;
Al Viro498052b2009-03-30 07:20:30 -0400112 kill = !--fs->users;
Nick Pigginc28cc362011-01-07 17:49:53 +1100113 write_seqcount_end(&fs->seq);
Nick Piggin2a4419b2010-08-18 04:37:33 +1000114 spin_unlock(&fs->lock);
Al Viro3e93cd62009-03-29 19:00:13 -0400115 task_unlock(tsk);
Al Viro498052b2009-03-30 07:20:30 -0400116 if (kill)
117 free_fs_struct(fs);
Al Viro3e93cd62009-03-29 19:00:13 -0400118 }
119}
120
121struct fs_struct *copy_fs_struct(struct fs_struct *old)
122{
123 struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
124 /* We don't need to lock fs - think why ;-) */
125 if (fs) {
Al Viro498052b2009-03-30 07:20:30 -0400126 fs->users = 1;
127 fs->in_exec = 0;
Nick Piggin2a4419b2010-08-18 04:37:33 +1000128 spin_lock_init(&fs->lock);
Nick Pigginc28cc362011-01-07 17:49:53 +1100129 seqcount_init(&fs->seq);
Al Viro3e93cd62009-03-29 19:00:13 -0400130 fs->umask = old->umask;
Nick Pigginb3e19d92011-01-07 17:50:11 +1100131
132 spin_lock(&old->lock);
133 fs->root = old->root;
Al Virof03c6592011-01-14 22:30:21 -0500134 path_get_longterm(&fs->root);
Nick Pigginb3e19d92011-01-07 17:50:11 +1100135 fs->pwd = old->pwd;
Al Virof03c6592011-01-14 22:30:21 -0500136 path_get_longterm(&fs->pwd);
Nick Pigginb3e19d92011-01-07 17:50:11 +1100137 spin_unlock(&old->lock);
Al Viro3e93cd62009-03-29 19:00:13 -0400138 }
139 return fs;
140}
141
142int unshare_fs_struct(void)
143{
Al Viro498052b2009-03-30 07:20:30 -0400144 struct fs_struct *fs = current->fs;
145 struct fs_struct *new_fs = copy_fs_struct(fs);
146 int kill;
147
148 if (!new_fs)
Al Viro3e93cd62009-03-29 19:00:13 -0400149 return -ENOMEM;
Al Viro498052b2009-03-30 07:20:30 -0400150
151 task_lock(current);
Nick Piggin2a4419b2010-08-18 04:37:33 +1000152 spin_lock(&fs->lock);
Al Viro498052b2009-03-30 07:20:30 -0400153 kill = !--fs->users;
154 current->fs = new_fs;
Nick Piggin2a4419b2010-08-18 04:37:33 +1000155 spin_unlock(&fs->lock);
Al Viro498052b2009-03-30 07:20:30 -0400156 task_unlock(current);
157
158 if (kill)
159 free_fs_struct(fs);
160
Al Viro3e93cd62009-03-29 19:00:13 -0400161 return 0;
162}
163EXPORT_SYMBOL_GPL(unshare_fs_struct);
164
Al Viroce3b0f82009-03-29 19:08:22 -0400165int current_umask(void)
166{
167 return current->fs->umask;
168}
169EXPORT_SYMBOL(current_umask);
170
Al Viro3e93cd62009-03-29 19:00:13 -0400171/* to be mentioned only in INIT_TASK */
172struct fs_struct init_fs = {
Al Viro498052b2009-03-30 07:20:30 -0400173 .users = 1,
Nick Piggin2a4419b2010-08-18 04:37:33 +1000174 .lock = __SPIN_LOCK_UNLOCKED(init_fs.lock),
Nick Pigginc28cc362011-01-07 17:49:53 +1100175 .seq = SEQCNT_ZERO,
Al Viro3e93cd62009-03-29 19:00:13 -0400176 .umask = 0022,
177};
178
179void daemonize_fs_struct(void)
180{
Al Viro498052b2009-03-30 07:20:30 -0400181 struct fs_struct *fs = current->fs;
Al Viro3e93cd62009-03-29 19:00:13 -0400182
Al Viro498052b2009-03-30 07:20:30 -0400183 if (fs) {
184 int kill;
185
186 task_lock(current);
187
Nick Piggin2a4419b2010-08-18 04:37:33 +1000188 spin_lock(&init_fs.lock);
Al Viro498052b2009-03-30 07:20:30 -0400189 init_fs.users++;
Nick Piggin2a4419b2010-08-18 04:37:33 +1000190 spin_unlock(&init_fs.lock);
Al Viro498052b2009-03-30 07:20:30 -0400191
Nick Piggin2a4419b2010-08-18 04:37:33 +1000192 spin_lock(&fs->lock);
Al Viro498052b2009-03-30 07:20:30 -0400193 current->fs = &init_fs;
194 kill = !--fs->users;
Nick Piggin2a4419b2010-08-18 04:37:33 +1000195 spin_unlock(&fs->lock);
Al Viro498052b2009-03-30 07:20:30 -0400196
197 task_unlock(current);
198 if (kill)
199 free_fs_struct(fs);
200 }
Al Viro3e93cd62009-03-29 19:00:13 -0400201}