This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / kernel / vserver / space.c
1 /*
2  *  linux/kernel/vserver/space.c
3  *
4  *  Virtual Server: Context Space Support
5  *
6  *  Copyright (C) 2003-2007  Herbert Pƶtzl
7  *
8  *  V0.01  broken out from context.c 0.07
9  *  V0.02  added task locking for namespace
10  *  V0.03  broken out vx_enter_namespace
11  *  V0.04  added *space support and commands
12  *
13  */
14
15 #include <linux/utsname.h>
16 #include <linux/sched.h>
17 #include <linux/vs_context.h>
18 #include <linux/vserver/space.h>
19 #include <linux/vserver/space_cmd.h>
20 #include <linux/dcache.h>
21 #include <linux/mount.h>
22 #include <linux/nsproxy.h>
23 #include <linux/fs.h>
24
25 #include <asm/errno.h>
26 #include <asm/uaccess.h>
27
28
29 atomic_t vs_global_nsproxy      = ATOMIC_INIT(0);
30 atomic_t vs_global_fs           = ATOMIC_INIT(0);
31 atomic_t vs_global_mnt_ns       = ATOMIC_INIT(0);
32 atomic_t vs_global_uts_ns       = ATOMIC_INIT(0);
33 atomic_t vs_global_ipc_ns       = ATOMIC_INIT(0);
34
35
36 /* namespace functions */
37
38 #include <linux/mnt_namespace.h>
39
40 const struct vcmd_space_mask space_mask = {
41         .mask = CLONE_NEWNS |
42                 CLONE_NEWUTS |
43                 CLONE_NEWIPC |
44                 CLONE_FS
45 };
46
47
48 /*
49  *      build a new nsproxy mix
50  *      assumes that both proxies are 'const'
51  *      does not touch nsproxy refcounts
52  *      will hold a reference on the result.
53  */
54
55 struct nsproxy *vs_mix_nsproxy(struct nsproxy *old_nsproxy,
56         struct nsproxy *new_nsproxy, unsigned long mask)
57 {
58         struct mnt_namespace *old_ns;
59         struct uts_namespace *old_uts;
60         struct ipc_namespace *old_ipc;
61         struct nsproxy *nsproxy;
62
63         nsproxy = dup_namespaces(old_nsproxy);
64         if (!nsproxy)
65                 goto out;
66
67         if (mask & CLONE_NEWNS) {
68                 old_ns = nsproxy->mnt_ns;
69                 nsproxy->mnt_ns = new_nsproxy->mnt_ns;
70                 if (nsproxy->mnt_ns)
71                         get_mnt_ns(nsproxy->mnt_ns);
72         } else
73                 old_ns = NULL;
74
75         if (mask & CLONE_NEWUTS) {
76                 old_uts = nsproxy->uts_ns;
77                 nsproxy->uts_ns = new_nsproxy->uts_ns;
78                 if (nsproxy->uts_ns)
79                         get_uts_ns(nsproxy->uts_ns);
80         } else
81                 old_uts = NULL;
82
83         if (mask & CLONE_NEWIPC) {
84                 old_ipc = nsproxy->ipc_ns;
85                 nsproxy->ipc_ns = new_nsproxy->ipc_ns;
86                 if (nsproxy->ipc_ns)
87                         get_ipc_ns(nsproxy->ipc_ns);
88         } else
89                 old_ipc = NULL;
90
91         if (old_ns)
92                 put_mnt_ns(old_ns);
93         if (old_uts)
94                 put_uts_ns(old_uts);
95         if (old_ipc)
96                 put_ipc_ns(old_ipc);
97 out:
98         return nsproxy;
99 }
100
101
102 /*
103  *      merge two nsproxy structs into a new one.
104  *      will hold a reference on the result.
105  */
106
107 static inline
108 struct nsproxy * __vs_merge_nsproxy(struct nsproxy *old,
109         struct nsproxy *proxy, unsigned long mask)
110 {
111         struct nsproxy null_proxy = { .mnt_ns = NULL };
112
113         if (!proxy)
114                 return NULL;
115
116         if (mask)
117                 return vs_mix_nsproxy(old ? old : &null_proxy,
118                         proxy, mask);
119         get_nsproxy(proxy);
120         return proxy;
121 }
122
123 /*
124  *      merge two fs structs into a new one.
125  *      will take a reference on the result.
126  */
127
128 static inline
129 struct fs_struct * __vs_merge_fs(struct fs_struct *old,
130         struct fs_struct *fs, unsigned long mask)
131 {
132         if (!(mask & CLONE_FS)) {
133                 if (old)
134                         atomic_inc(&old->count);
135                 return old;
136         }
137
138         if (!fs)
139                 return NULL;
140
141         return copy_fs_struct(fs);
142 }
143
144
145 int vx_enter_space(struct vx_info *vxi, unsigned long mask)
146 {
147         struct nsproxy *proxy, *proxy_cur, *proxy_new;
148         struct fs_struct *fs, *fs_cur, *fs_new;
149         int ret;
150
151         if (vx_info_flags(vxi, VXF_INFO_PRIVATE, 0))
152                 return -EACCES;
153
154         if (!mask)
155                 mask = vxi->vx_nsmask;
156
157         if ((mask & vxi->vx_nsmask) != mask)
158                 return -EINVAL;
159
160         proxy = vxi->vx_nsproxy;
161         fs = vxi->vx_fs;
162
163         task_lock(current);
164         fs_cur = current->fs;
165         atomic_inc(&fs_cur->count);
166         proxy_cur = current->nsproxy;
167         get_nsproxy(proxy_cur);
168         task_unlock(current);
169
170         fs_new = __vs_merge_fs(fs_cur, fs, mask);
171         if (IS_ERR(fs_new)) {
172                 ret = PTR_ERR(fs_new);
173                 goto out_put;
174         }
175
176         proxy_new = __vs_merge_nsproxy(proxy_cur, proxy, mask);
177         if (IS_ERR(proxy_new)) {
178                 ret = PTR_ERR(proxy_new);
179                 goto out_put_fs;
180         }
181
182         fs_new = xchg(&current->fs, fs_new);
183         proxy_new = xchg(&current->nsproxy, proxy_new);
184         ret = 0;
185
186         if (proxy_new)
187                 put_nsproxy(proxy_new);
188 out_put_fs:
189         if (fs_new)
190                 put_fs_struct(fs_new);
191 out_put:
192         if (proxy_cur)
193                 put_nsproxy(proxy_cur);
194         if (fs_cur)
195                 put_fs_struct(fs_cur);
196         return ret;
197 }
198
199
200 int vx_set_space(struct vx_info *vxi, unsigned long mask)
201 {
202         struct nsproxy *proxy_vxi, *proxy_cur, *proxy_new;
203         struct fs_struct *fs_vxi, *fs_cur, *fs_new;
204         int ret;
205
206         if (!mask)
207                 mask = space_mask.mask;
208
209         if ((mask & space_mask.mask) != mask)
210                 return -EINVAL;
211
212         proxy_vxi = vxi->vx_nsproxy;
213         fs_vxi = vxi->vx_fs;
214
215         task_lock(current);
216         fs_cur = current->fs;
217         atomic_inc(&fs_cur->count);
218         proxy_cur = current->nsproxy;
219         get_nsproxy(proxy_cur);
220         task_unlock(current);
221
222         fs_new = __vs_merge_fs(fs_vxi, fs_cur, mask);
223         if (IS_ERR(fs_new)) {
224                 ret = PTR_ERR(fs_new);
225                 goto out_put;
226         }
227
228         proxy_new = __vs_merge_nsproxy(proxy_vxi, proxy_cur, mask);
229         if (IS_ERR(proxy_new)) {
230                 ret = PTR_ERR(proxy_new);
231                 goto out_put_fs;
232         }
233
234         fs_new = xchg(&vxi->vx_fs, fs_new);
235         proxy_new = xchg(&vxi->vx_nsproxy, proxy_new);
236         vxi->vx_nsmask |= mask;
237         ret = 0;
238
239         if (proxy_new)
240                 put_nsproxy(proxy_new);
241 out_put_fs:
242         if (fs_new)
243                 put_fs_struct(fs_new);
244 out_put:
245         if (proxy_cur)
246                 put_nsproxy(proxy_cur);
247         if (fs_cur)
248                 put_fs_struct(fs_cur);
249         return ret;
250 }
251
252
253 int vc_enter_space(struct vx_info *vxi, void __user *data)
254 {
255         struct vcmd_space_mask vc_data = { .mask = 0 };
256
257         if (data && copy_from_user (&vc_data, data, sizeof(vc_data)))
258                 return -EFAULT;
259
260         return vx_enter_space(vxi, vc_data.mask);
261 }
262
263 int vc_set_space(struct vx_info *vxi, void __user *data)
264 {
265         struct vcmd_space_mask vc_data = { .mask = 0 };
266
267         if (data && copy_from_user (&vc_data, data, sizeof(vc_data)))
268                 return -EFAULT;
269
270         return vx_set_space(vxi, vc_data.mask);
271 }
272
273 int vc_get_space_mask(struct vx_info *vxi, void __user *data)
274 {
275         if (copy_to_user(data, &space_mask, sizeof(space_mask)))
276                 return -EFAULT;
277         return 0;
278 }
279