diff options
Diffstat (limited to 'ipc/mqueue.c')
-rw-r--r-- | ipc/mqueue.c | 111 |
1 files changed, 82 insertions, 29 deletions
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index a3673a09069a..c82d7b51ef68 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c | |||
@@ -88,7 +88,6 @@ static const struct file_operations mqueue_file_operations; | |||
88 | static struct super_operations mqueue_super_ops; | 88 | static struct super_operations mqueue_super_ops; |
89 | static void remove_notification(struct mqueue_inode_info *info); | 89 | static void remove_notification(struct mqueue_inode_info *info); |
90 | 90 | ||
91 | static spinlock_t mq_lock; | ||
92 | static struct kmem_cache *mqueue_inode_cachep; | 91 | static struct kmem_cache *mqueue_inode_cachep; |
93 | 92 | ||
94 | static struct ctl_table_header * mq_sysctl_table; | 93 | static struct ctl_table_header * mq_sysctl_table; |
@@ -98,27 +97,30 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode) | |||
98 | return container_of(inode, struct mqueue_inode_info, vfs_inode); | 97 | return container_of(inode, struct mqueue_inode_info, vfs_inode); |
99 | } | 98 | } |
100 | 99 | ||
101 | void mq_init_ns(struct ipc_namespace *ns) | 100 | /* |
101 | * This routine should be called with the mq_lock held. | ||
102 | */ | ||
103 | static inline struct ipc_namespace *__get_ns_from_inode(struct inode *inode) | ||
102 | { | 104 | { |
103 | ns->mq_queues_count = 0; | 105 | return get_ipc_ns(inode->i_sb->s_fs_info); |
104 | ns->mq_queues_max = DFLT_QUEUESMAX; | ||
105 | ns->mq_msg_max = DFLT_MSGMAX; | ||
106 | ns->mq_msgsize_max = DFLT_MSGSIZEMAX; | ||
107 | ns->mq_mnt = mntget(init_ipc_ns.mq_mnt); | ||
108 | } | 106 | } |
109 | 107 | ||
110 | void mq_exit_ns(struct ipc_namespace *ns) | 108 | static struct ipc_namespace *get_ns_from_inode(struct inode *inode) |
111 | { | 109 | { |
112 | /* will need to clear out ns->mq_mnt->mnt_sb->s_fs_info here */ | 110 | struct ipc_namespace *ns; |
113 | mntput(ns->mq_mnt); | 111 | |
112 | spin_lock(&mq_lock); | ||
113 | ns = __get_ns_from_inode(inode); | ||
114 | spin_unlock(&mq_lock); | ||
115 | return ns; | ||
114 | } | 116 | } |
115 | 117 | ||
116 | static struct inode *mqueue_get_inode(struct super_block *sb, int mode, | 118 | static struct inode *mqueue_get_inode(struct super_block *sb, |
117 | struct mq_attr *attr) | 119 | struct ipc_namespace *ipc_ns, int mode, |
120 | struct mq_attr *attr) | ||
118 | { | 121 | { |
119 | struct user_struct *u = current_user(); | 122 | struct user_struct *u = current_user(); |
120 | struct inode *inode; | 123 | struct inode *inode; |
121 | struct ipc_namespace *ipc_ns = &init_ipc_ns; | ||
122 | 124 | ||
123 | inode = new_inode(sb); | 125 | inode = new_inode(sb); |
124 | if (inode) { | 126 | if (inode) { |
@@ -193,30 +195,38 @@ out_inode: | |||
193 | static int mqueue_fill_super(struct super_block *sb, void *data, int silent) | 195 | static int mqueue_fill_super(struct super_block *sb, void *data, int silent) |
194 | { | 196 | { |
195 | struct inode *inode; | 197 | struct inode *inode; |
198 | struct ipc_namespace *ns = data; | ||
199 | int error = 0; | ||
196 | 200 | ||
197 | sb->s_blocksize = PAGE_CACHE_SIZE; | 201 | sb->s_blocksize = PAGE_CACHE_SIZE; |
198 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | 202 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
199 | sb->s_magic = MQUEUE_MAGIC; | 203 | sb->s_magic = MQUEUE_MAGIC; |
200 | sb->s_op = &mqueue_super_ops; | 204 | sb->s_op = &mqueue_super_ops; |
201 | 205 | ||
202 | inode = mqueue_get_inode(sb, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL); | 206 | inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO, |
203 | if (!inode) | 207 | NULL); |
204 | return -ENOMEM; | 208 | if (!inode) { |
209 | error = -ENOMEM; | ||
210 | goto out; | ||
211 | } | ||
205 | 212 | ||
206 | sb->s_root = d_alloc_root(inode); | 213 | sb->s_root = d_alloc_root(inode); |
207 | if (!sb->s_root) { | 214 | if (!sb->s_root) { |
208 | iput(inode); | 215 | iput(inode); |
209 | return -ENOMEM; | 216 | error = -ENOMEM; |
210 | } | 217 | } |
211 | 218 | ||
212 | return 0; | 219 | out: |
220 | return error; | ||
213 | } | 221 | } |
214 | 222 | ||
215 | static int mqueue_get_sb(struct file_system_type *fs_type, | 223 | static int mqueue_get_sb(struct file_system_type *fs_type, |
216 | int flags, const char *dev_name, | 224 | int flags, const char *dev_name, |
217 | void *data, struct vfsmount *mnt) | 225 | void *data, struct vfsmount *mnt) |
218 | { | 226 | { |
219 | return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt); | 227 | if (!(flags & MS_KERNMOUNT)) |
228 | data = current->nsproxy->ipc_ns; | ||
229 | return get_sb_ns(fs_type, flags, data, mqueue_fill_super, mnt); | ||
220 | } | 230 | } |
221 | 231 | ||
222 | static void init_once(void *foo) | 232 | static void init_once(void *foo) |
@@ -247,12 +257,13 @@ static void mqueue_delete_inode(struct inode *inode) | |||
247 | struct user_struct *user; | 257 | struct user_struct *user; |
248 | unsigned long mq_bytes; | 258 | unsigned long mq_bytes; |
249 | int i; | 259 | int i; |
250 | struct ipc_namespace *ipc_ns = &init_ipc_ns; | 260 | struct ipc_namespace *ipc_ns; |
251 | 261 | ||
252 | if (S_ISDIR(inode->i_mode)) { | 262 | if (S_ISDIR(inode->i_mode)) { |
253 | clear_inode(inode); | 263 | clear_inode(inode); |
254 | return; | 264 | return; |
255 | } | 265 | } |
266 | ipc_ns = get_ns_from_inode(inode); | ||
256 | info = MQUEUE_I(inode); | 267 | info = MQUEUE_I(inode); |
257 | spin_lock(&info->lock); | 268 | spin_lock(&info->lock); |
258 | for (i = 0; i < info->attr.mq_curmsgs; i++) | 269 | for (i = 0; i < info->attr.mq_curmsgs; i++) |
@@ -268,10 +279,19 @@ static void mqueue_delete_inode(struct inode *inode) | |||
268 | if (user) { | 279 | if (user) { |
269 | spin_lock(&mq_lock); | 280 | spin_lock(&mq_lock); |
270 | user->mq_bytes -= mq_bytes; | 281 | user->mq_bytes -= mq_bytes; |
271 | ipc_ns->mq_queues_count--; | 282 | /* |
283 | * get_ns_from_inode() ensures that the | ||
284 | * (ipc_ns = sb->s_fs_info) is either a valid ipc_ns | ||
285 | * to which we now hold a reference, or it is NULL. | ||
286 | * We can't put it here under mq_lock, though. | ||
287 | */ | ||
288 | if (ipc_ns) | ||
289 | ipc_ns->mq_queues_count--; | ||
272 | spin_unlock(&mq_lock); | 290 | spin_unlock(&mq_lock); |
273 | free_uid(user); | 291 | free_uid(user); |
274 | } | 292 | } |
293 | if (ipc_ns) | ||
294 | put_ipc_ns(ipc_ns); | ||
275 | } | 295 | } |
276 | 296 | ||
277 | static int mqueue_create(struct inode *dir, struct dentry *dentry, | 297 | static int mqueue_create(struct inode *dir, struct dentry *dentry, |
@@ -280,9 +300,14 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry, | |||
280 | struct inode *inode; | 300 | struct inode *inode; |
281 | struct mq_attr *attr = dentry->d_fsdata; | 301 | struct mq_attr *attr = dentry->d_fsdata; |
282 | int error; | 302 | int error; |
283 | struct ipc_namespace *ipc_ns = &init_ipc_ns; | 303 | struct ipc_namespace *ipc_ns; |
284 | 304 | ||
285 | spin_lock(&mq_lock); | 305 | spin_lock(&mq_lock); |
306 | ipc_ns = __get_ns_from_inode(dir); | ||
307 | if (!ipc_ns) { | ||
308 | error = -EACCES; | ||
309 | goto out_unlock; | ||
310 | } | ||
286 | if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max && | 311 | if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max && |
287 | !capable(CAP_SYS_RESOURCE)) { | 312 | !capable(CAP_SYS_RESOURCE)) { |
288 | error = -ENOSPC; | 313 | error = -ENOSPC; |
@@ -291,7 +316,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry, | |||
291 | ipc_ns->mq_queues_count++; | 316 | ipc_ns->mq_queues_count++; |
292 | spin_unlock(&mq_lock); | 317 | spin_unlock(&mq_lock); |
293 | 318 | ||
294 | inode = mqueue_get_inode(dir->i_sb, mode, attr); | 319 | inode = mqueue_get_inode(dir->i_sb, ipc_ns, mode, attr); |
295 | if (!inode) { | 320 | if (!inode) { |
296 | error = -ENOMEM; | 321 | error = -ENOMEM; |
297 | spin_lock(&mq_lock); | 322 | spin_lock(&mq_lock); |
@@ -299,6 +324,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry, | |||
299 | goto out_unlock; | 324 | goto out_unlock; |
300 | } | 325 | } |
301 | 326 | ||
327 | put_ipc_ns(ipc_ns); | ||
302 | dir->i_size += DIRENT_SIZE; | 328 | dir->i_size += DIRENT_SIZE; |
303 | dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; | 329 | dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; |
304 | 330 | ||
@@ -307,6 +333,8 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry, | |||
307 | return 0; | 333 | return 0; |
308 | out_unlock: | 334 | out_unlock: |
309 | spin_unlock(&mq_lock); | 335 | spin_unlock(&mq_lock); |
336 | if (ipc_ns) | ||
337 | put_ipc_ns(ipc_ns); | ||
310 | return error; | 338 | return error; |
311 | } | 339 | } |
312 | 340 | ||
@@ -668,7 +696,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode, | |||
668 | char *name; | 696 | char *name; |
669 | struct mq_attr attr; | 697 | struct mq_attr attr; |
670 | int fd, error; | 698 | int fd, error; |
671 | struct ipc_namespace *ipc_ns = &init_ipc_ns; | 699 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; |
672 | 700 | ||
673 | if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr))) | 701 | if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr))) |
674 | return -EFAULT; | 702 | return -EFAULT; |
@@ -738,7 +766,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) | |||
738 | char *name; | 766 | char *name; |
739 | struct dentry *dentry; | 767 | struct dentry *dentry; |
740 | struct inode *inode = NULL; | 768 | struct inode *inode = NULL; |
741 | struct ipc_namespace *ipc_ns = &init_ipc_ns; | 769 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; |
742 | 770 | ||
743 | name = getname(u_name); | 771 | name = getname(u_name); |
744 | if (IS_ERR(name)) | 772 | if (IS_ERR(name)) |
@@ -1217,6 +1245,32 @@ static struct file_system_type mqueue_fs_type = { | |||
1217 | .kill_sb = kill_litter_super, | 1245 | .kill_sb = kill_litter_super, |
1218 | }; | 1246 | }; |
1219 | 1247 | ||
1248 | int mq_init_ns(struct ipc_namespace *ns) | ||
1249 | { | ||
1250 | ns->mq_queues_count = 0; | ||
1251 | ns->mq_queues_max = DFLT_QUEUESMAX; | ||
1252 | ns->mq_msg_max = DFLT_MSGMAX; | ||
1253 | ns->mq_msgsize_max = DFLT_MSGSIZEMAX; | ||
1254 | |||
1255 | ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns); | ||
1256 | if (IS_ERR(ns->mq_mnt)) { | ||
1257 | int err = PTR_ERR(ns->mq_mnt); | ||
1258 | ns->mq_mnt = NULL; | ||
1259 | return err; | ||
1260 | } | ||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1264 | void mq_clear_sbinfo(struct ipc_namespace *ns) | ||
1265 | { | ||
1266 | ns->mq_mnt->mnt_sb->s_fs_info = NULL; | ||
1267 | } | ||
1268 | |||
1269 | void mq_put_mnt(struct ipc_namespace *ns) | ||
1270 | { | ||
1271 | mntput(ns->mq_mnt); | ||
1272 | } | ||
1273 | |||
1220 | static int msg_max_limit_min = MIN_MSGMAX; | 1274 | static int msg_max_limit_min = MIN_MSGMAX; |
1221 | static int msg_max_limit_max = MAX_MSGMAX; | 1275 | static int msg_max_limit_max = MAX_MSGMAX; |
1222 | 1276 | ||
@@ -1288,15 +1342,14 @@ static int __init init_mqueue_fs(void) | |||
1288 | if (error) | 1342 | if (error) |
1289 | goto out_sysctl; | 1343 | goto out_sysctl; |
1290 | 1344 | ||
1291 | init_ipc_ns.mq_mnt = kern_mount(&mqueue_fs_type); | 1345 | spin_lock_init(&mq_lock); |
1346 | |||
1347 | init_ipc_ns.mq_mnt = kern_mount_data(&mqueue_fs_type, &init_ipc_ns); | ||
1292 | if (IS_ERR(init_ipc_ns.mq_mnt)) { | 1348 | if (IS_ERR(init_ipc_ns.mq_mnt)) { |
1293 | error = PTR_ERR(init_ipc_ns.mq_mnt); | 1349 | error = PTR_ERR(init_ipc_ns.mq_mnt); |
1294 | goto out_filesystem; | 1350 | goto out_filesystem; |
1295 | } | 1351 | } |
1296 | 1352 | ||
1297 | /* internal initialization - not common for vfs */ | ||
1298 | spin_lock_init(&mq_lock); | ||
1299 | |||
1300 | return 0; | 1353 | return 0; |
1301 | 1354 | ||
1302 | out_filesystem: | 1355 | out_filesystem: |