2008-12-17 17:55:31

by Serge E. Hallyn

[permalink] [raw]
Subject: [RFC patch 0/2] posix mqueue namespace (v12)

Posix message queue support in ipc namespaces has had
several submissions by various people. Hopefully this
version, based on code by both Cedric Le Goater and
Nadia Derbey has a shot at making it upstream.

To create a new ipc namespace with posix mq support, you
would

unshare(CLONE_NEWIPC|CLONE_NEWNS);
umount /dev/mqueue
mount -t mqueue mqueue /dev/mqueue

It's perfectly valid to do vfs operations on files
in another ipc_namespace's /dev/mqueue, but any use
of mq_open(3) and friends will act in your own ipc_ns.

thanks,
-serge


2008-12-18 21:36:49

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [PATCH 2/2] ipc namespaces: implement support for posix msqueues

Quoting Sukadev Bhattiprolu ([email protected]):
> | static int mqueue_get_sb(struct file_system_type *fs_type,
> | int flags, const char *dev_name,
> | void *data, struct vfsmount *mnt)
> | {
> | - return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt);
> | + if (flags & MS_KERNMOUNT)
> | + return get_sb_single_ns(fs_type, flags, data,
> | + mqueue_fill_super, mnt);
> | + return get_sb_single_ns(fs_type, flags, current->nsproxy->ipc_ns,
> | + mqueue_fill_super, mnt);
>
> nit. This maybe easier to read with an ns local variable and a single
> call to get_sb_single_ns().

The first time I read this I completely misunderstood what you
meant. Yes, that's nicer. Thanks for taking a look.

New version coming tomorrow (I want to stress this a bit).

thanks,
-serge

2008-12-17 17:56:26

by Serge E. Hallyn

[permalink] [raw]
Subject: [PATCH 2/2] ipc namespaces: implement support for posix msqueues

Implement multiple mounts of the mqueue file system, and
link it to usage of CLONE_NEWIPC.

Each ipc ns has a corresponding mqueuefs superblock. When
a user does clone(CLONE_NEWIPC) or unshare(CLONE_NEWIPC), the
unshare will cause an internal mount of a new mqueuefs sb
linked to the new ipc ns.

When a user does 'mount -t mqueue mqueue /dev/mqueue', he
mounts the mqueuefs superblock.

Posix message queues can be worked with both through the
mq_* system calls (see mq_overview(7)), and through the VFS
through the mqueue mount. Any usage of mq_open() and friends
will work with the acting task's ipc namespace. Any actions
through the VFS will work with the mqueuefs in which the
file was created. So if a user doesn't remount mqueuefs
after unshare(CLONE_NEWIPC), mq_open("/ab") will not be
reflected in "ls /dev/mqueue".

If task a mounts mqueue for ipc_ns:1, then clones task b with
a new ipcns, ipcns:2, and then task a is the last task in
ipc_ns:1 to exit, then (1) ipc_ns:1 will be freed, (2) it's
superblock will live on until task b umounts the corresponding
mqueuefs, and vfs actions will continue to succeed, but (3)
sb->s_fs_info will be NULL for the sb corresponding to the
deceased ipc_ns:1.

Changelog:
Dec 17: removed unused static fn (get_ipcns_from_sb)

Signed-off-by: Cedric Le Goater <[email protected]>
Signed-off-by: Serge E. Hallyn <[email protected]>
---
include/linux/ipc_namespace.h | 16 ++---
ipc/mqueue.c | 140 ++++++++++++++++++++++++++++++++---------
ipc/msgutil.c | 8 +--
ipc/namespace.c | 25 ++++++--
ipc/util.h | 6 +-
5 files changed, 144 insertions(+), 51 deletions(-)

diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index 532598f..74f1ae2 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -25,7 +25,7 @@ struct ipc_ids {
};

struct ipc_namespace {
- struct kref kref;
+ atomic_t count;
struct ipc_ids ids[3];

int sem_ctls[4];
@@ -56,6 +56,7 @@ struct ipc_namespace {
extern struct ipc_namespace init_ipc_ns;
extern atomic_t nr_ipc_ns;

+extern spinlock_t mq_lock;
#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
#define INIT_IPC_NS(ns) .ns = &init_ipc_ns,
#else
@@ -75,18 +76,18 @@ extern int ipcns_notify(unsigned long);
#endif /* CONFIG_SYSVIPC */

#ifdef CONFIG_POSIX_MQUEUE
-extern void mq_init_ns(struct ipc_namespace *ns);
+extern int mq_init_ns(struct ipc_namespace *ns);
/* default values */
#define DFLT_QUEUESMAX 256 /* max number of message queues */
#define DFLT_MSGMAX 10 /* max number of messages in each queue */
#define HARD_MSGMAX (131072/sizeof(void *))
#define DFLT_MSGSIZEMAX 8192 /* max message size */
#else
-#define mq_init_ns(ns)
+#define mq_init_ns(ns) (0)
#endif

#if defined(CONFIG_IPC_NS)
-extern void free_ipc_ns(struct kref *kref);
+extern void free_ipc_ns(struct ipc_namespace *ns);
extern struct ipc_namespace *copy_ipcs(unsigned long flags,
struct ipc_namespace *ns);
extern void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
@@ -96,14 +97,11 @@ extern void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns)
{
if (ns)
- kref_get(&ns->kref);
+ atomic_inc(&ns->count);
return ns;
}

-static inline void put_ipc_ns(struct ipc_namespace *ns)
-{
- kref_put(&ns->kref, free_ipc_ns);
-}
+extern void put_ipc_ns(struct ipc_namespace *ns);
#else
static inline struct ipc_namespace *copy_ipcs(unsigned long flags,
struct ipc_namespace *ns)
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 01d64a0..6b235c1 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -88,7 +88,6 @@ static const struct file_operations mqueue_file_operations;
static struct super_operations mqueue_super_ops;
static void remove_notification(struct mqueue_inode_info *info);

-static spinlock_t mq_lock;
static struct kmem_cache *mqueue_inode_cachep;

static struct ctl_table_header * mq_sysctl_table;
@@ -98,25 +97,30 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
return container_of(inode, struct mqueue_inode_info, vfs_inode);
}

-void mq_init_ns(struct ipc_namespace *ns) {
- ns->mq_queues_count = 0;
- ns->mq_queues_max = DFLT_QUEUESMAX;
- ns->mq_msg_max = DFLT_MSGMAX;
- ns->mq_msgsize_max = DFLT_MSGSIZEMAX;
- ns->mq_mnt = mntget(init_ipc_ns.mq_mnt);
+/*
+ * This routine should be called with the mq_lock held.
+ */
+static inline struct ipc_namespace *__get_ns_from_ino(struct inode *inode)
+{
+ return get_ipc_ns(inode->i_sb->s_fs_info);
}

-void mq_exit_ns(struct ipc_namespace *ns) {
- /* will need to clear out ns->mq_mnt->mnt_sb->s_fs_info here */
- mntput(ns->mq_mnt);
+static inline struct ipc_namespace *get_ns_from_ino(struct inode *inode)
+{
+ struct ipc_namespace *ns;
+
+ spin_lock(&mq_lock);
+ ns = __get_ns_from_ino(inode);
+ spin_unlock(&mq_lock);
+ return ns;
}

-static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
- struct mq_attr *attr)
+static struct inode *mqueue_get_inode(struct super_block *sb,
+ struct ipc_namespace *ipc_ns, int mode,
+ struct mq_attr *attr)
{
struct user_struct *u = current_user();
struct inode *inode;
- struct ipc_namespace *ipc_ns = &init_ipc_ns;

inode = new_inode(sb);
if (inode) {
@@ -192,30 +196,76 @@ out_inode:
static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
+ struct ipc_namespace *ns = data;
+ int error = 0;

sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = MQUEUE_MAGIC;
sb->s_op = &mqueue_super_ops;

- inode = mqueue_get_inode(sb, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL);
- if (!inode)
- return -ENOMEM;
+ inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO,
+ NULL);
+ if (!inode) {
+ error = -ENOMEM;
+ goto out;
+ }

sb->s_root = d_alloc_root(inode);
if (!sb->s_root) {
iput(inode);
- return -ENOMEM;
+ error = -ENOMEM;
}

- return 0;
+out:
+ return error;
+}
+
+static int compare_sb_single_ns(struct super_block *sb, void *data)
+{
+ return sb->s_fs_info == data;
+}
+
+static int set_sb_single_ns(struct super_block *sb, void *data)
+{
+ sb->s_fs_info = data;
+ return set_anon_super(sb, NULL);
+}
+
+static int get_sb_single_ns(struct file_system_type *fs_type,
+ int flags, void *data,
+ int (*fill_super)(struct super_block *, void *, int),
+ struct vfsmount *mnt)
+{
+ struct super_block *s;
+ int error;
+
+ s = sget(fs_type, compare_sb_single_ns, set_sb_single_ns, data);
+ if (IS_ERR(s))
+ return PTR_ERR(s);
+ if (!s->s_root) {
+ s->s_flags = flags;
+ error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
+ if (error) {
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ return error;
+ }
+ s->s_flags |= MS_ACTIVE;
+ }
+ do_remount_sb(s, flags, data, 0);
+ return simple_set_mnt(mnt, s);
}

static int mqueue_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name,
void *data, struct vfsmount *mnt)
{
- return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt);
+ if (flags & MS_KERNMOUNT)
+ return get_sb_single_ns(fs_type, flags, data,
+ mqueue_fill_super, mnt);
+ return get_sb_single_ns(fs_type, flags, current->nsproxy->ipc_ns,
+ mqueue_fill_super, mnt);
}

static void init_once(void *foo)
@@ -246,12 +296,13 @@ static void mqueue_delete_inode(struct inode *inode)
struct user_struct *user;
unsigned long mq_bytes;
int i;
- struct ipc_namespace *ipc_ns = &init_ipc_ns;
+ struct ipc_namespace *ipc_ns;

if (S_ISDIR(inode->i_mode)) {
clear_inode(inode);
return;
}
+ ipc_ns = get_ns_from_ino(inode);
info = MQUEUE_I(inode);
spin_lock(&info->lock);
for (i = 0; i < info->attr.mq_curmsgs; i++)
@@ -267,10 +318,12 @@ static void mqueue_delete_inode(struct inode *inode)
if (user) {
spin_lock(&mq_lock);
user->mq_bytes -= mq_bytes;
- ipc_ns->mq_queues_count--;
+ if (ipc_ns)
+ ipc_ns->mq_queues_count--;
spin_unlock(&mq_lock);
free_uid(user);
}
+ put_ipc_ns(ipc_ns);
}

static int mqueue_create(struct inode *dir, struct dentry *dentry,
@@ -279,9 +332,14 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
struct inode *inode;
struct mq_attr *attr = dentry->d_fsdata;
int error;
- struct ipc_namespace *ipc_ns = &init_ipc_ns;
+ struct ipc_namespace *ipc_ns;

spin_lock(&mq_lock);
+ ipc_ns = __get_ns_from_ino(dir);
+ if (!ipc_ns) {
+ error = -EACCES;
+ goto out_lock;
+ }
if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max &&
!capable(CAP_SYS_RESOURCE)) {
error = -ENOSPC;
@@ -290,7 +348,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
ipc_ns->mq_queues_count++;
spin_unlock(&mq_lock);

- inode = mqueue_get_inode(dir->i_sb, mode, attr);
+ inode = mqueue_get_inode(dir->i_sb, ipc_ns, mode, attr);
if (!inode) {
error = -ENOMEM;
spin_lock(&mq_lock);
@@ -298,6 +356,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
goto out_lock;
}

+ put_ipc_ns(ipc_ns);
dir->i_size += DIRENT_SIZE;
dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;

@@ -306,6 +365,7 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
return 0;
out_lock:
spin_unlock(&mq_lock);
+ put_ipc_ns(ipc_ns);
return error;
}

@@ -673,7 +733,7 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
struct file *filp;
char *name;
int fd, error;
- struct ipc_namespace *ipc_ns = &init_ipc_ns;
+ struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;

error = audit_mq_open(oflag, mode, u_attr);
if (error != 0)
@@ -741,7 +801,7 @@ asmlinkage long sys_mq_unlink(const char __user *u_name)
char *name;
struct dentry *dentry;
struct inode *inode = NULL;
- struct ipc_namespace *ipc_ns = &init_ipc_ns;
+ struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;

name = getname(u_name);
if (IS_ERR(name))
@@ -1212,6 +1272,29 @@ static struct file_system_type mqueue_fs_type = {
.kill_sb = kill_litter_super,
};

+int mq_init_ns(struct ipc_namespace *ns)
+{
+ ns->mq_queues_count = 0;
+ ns->mq_queues_max = DFLT_QUEUESMAX;
+ ns->mq_msg_max = DFLT_MSGMAX;
+ ns->mq_msgsize_max = DFLT_MSGSIZEMAX;
+
+ ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns);
+ if (IS_ERR(ns->mq_mnt))
+ return PTR_ERR(ns->mq_mnt);
+ return 0;
+}
+
+void mq_clear_sbinfo(struct ipc_namespace *ns)
+{
+ ns->mq_mnt->mnt_sb->s_fs_info = NULL;
+}
+
+void mq_put_mnt(struct ipc_namespace *ns)
+{
+ mntput(ns->mq_mnt);
+}
+
static int msg_max_limit_min = MIN_MSGMAX;
static int msg_max_limit_max = MAX_MSGMAX;

@@ -1283,15 +1366,14 @@ static int __init init_mqueue_fs(void)
if (error)
goto out_sysctl;

- init_ipc_ns.mq_mnt = kern_mount(&mqueue_fs_type);
+ spin_lock_init(&mq_lock);
+
+ init_ipc_ns.mq_mnt = kern_mount_data(&mqueue_fs_type, &init_ipc_ns);
if (IS_ERR(init_ipc_ns.mq_mnt)) {
error = PTR_ERR(init_ipc_ns.mq_mnt);
goto out_filesystem;
}

- /* internal initialization - not common for vfs */
- spin_lock_init(&mq_lock);
-
return 0;

out_filesystem:
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index c197cd1..21475b0 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -18,18 +18,16 @@

#include "util.h"

+spinlock_t mq_lock;
+
/*
* The next 2 defines are here bc this is the only file
* compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
* and not CONFIG_IPC_NS.
*/
struct ipc_namespace init_ipc_ns = {
- .kref = {
- .refcount = ATOMIC_INIT(2),
- },
+ .count = ATOMIC_INIT(2),
#ifdef CONFIG_POSIX_MQUEUE
- .mq_mnt = NULL,
- .mq_queues_count = 0,
.mq_queues_max = DFLT_QUEUESMAX,
.mq_msg_max = DFLT_MSGMAX,
.mq_msgsize_max = DFLT_MSGSIZEMAX,
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 4b4dc6d..a4f36ba 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -9,23 +9,31 @@
#include <linux/rcupdate.h>
#include <linux/nsproxy.h>
#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mount.h>

#include "util.h"

static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
{
struct ipc_namespace *ns;
+ int err;

ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
if (ns == NULL)
return ERR_PTR(-ENOMEM);

+ atomic_set(&ns->count, 1);
+ err = mq_init_ns(ns);
+ if (err) {
+ kfree(ns);
+ return ERR_PTR(err);
+ }
atomic_inc(&nr_ipc_ns);

sem_init_ns(ns);
msg_init_ns(ns);
shm_init_ns(ns);
- mq_init_ns(ns);

/*
* msgmni has already been computed for the new ipc ns.
@@ -35,7 +43,6 @@ static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
ipcns_notify(IPCNS_CREATED);
register_ipcns_notifier(ns);

- kref_init(&ns->kref);
return ns;
}

@@ -85,11 +92,18 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
up_write(&ids->rw_mutex);
}

-void free_ipc_ns(struct kref *kref)
+void put_ipc_ns(struct ipc_namespace *ns)
{
- struct ipc_namespace *ns;
+ if (ns && atomic_dec_and_lock(&ns->count, &mq_lock)) {
+ mq_clear_sbinfo(ns);
+ spin_unlock(&mq_lock);
+ mq_put_mnt(ns);
+ free_ipc_ns(ns);
+ }
+}

- ns = container_of(kref, struct ipc_namespace, kref);
+void free_ipc_ns(struct ipc_namespace *ns)
+{
/*
* Unregistering the hotplug notifier at the beginning guarantees
* that the ipc namespace won't be freed while we are inside the
@@ -102,7 +116,6 @@ void free_ipc_ns(struct kref *kref)
sem_exit_ns(ns);
msg_exit_ns(ns);
shm_exit_ns(ns);
- mq_exit_ns(ns);
kfree(ns);
atomic_dec(&nr_ipc_ns);

diff --git a/ipc/util.h b/ipc/util.h
index 52755c1..b4d213f 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -21,9 +21,11 @@ void shm_init (void);
struct ipc_namespace;

#ifdef CONFIG_POSIX_MQUEUE
-void mq_exit_ns(struct ipc_namespace *ns);
+extern void mq_clear_sbinfo(struct ipc_namespace *ns);
+extern void mq_put_mnt(struct ipc_namespace *ns);
#else
-#define mq_exit_ns(ns)
+#define mq_clear_sbinfo(ns)
+#define mq_put_mnt(ns)
#endif

#ifdef CONFIG_SYSVIPC
--
1.5.4.3

2008-12-17 17:55:55

by Serge E. Hallyn

[permalink] [raw]
Subject: [PATCH 1/2] mqueue ns: move mqueue_mnt into struct ipc_namespace

Move mqueue vfsmount plus a few tunables into the
ipc_namespace struct. The CONFIG_IPC_NS boolean
and the ipc_namespace struct will serve both the
posix message queue namespaces and the SYSV ipc
namespaces.

Signed-off-by: Cedric Le Goater <[email protected]>
Signed-off-by: Serge E. Hallyn <[email protected]>
---
include/linux/ipc_namespace.h | 32 ++++++++++--
init/Kconfig | 4 +-
ipc/mqueue.c | 116 ++++++++++++++++++++++-------------------
ipc/msgutil.c | 21 +++++++
ipc/namespace.c | 2 +
ipc/util.c | 9 ---
ipc/util.h | 15 +++++
7 files changed, 131 insertions(+), 68 deletions(-)

diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index ea330f9..532598f 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -44,24 +44,48 @@ struct ipc_namespace {
int shm_tot;

struct notifier_block ipcns_nb;
+
+ struct vfsmount *mq_mnt;
+ unsigned int mq_queues_count;
+ unsigned int mq_queues_max;
+ unsigned int mq_msg_max;
+ unsigned int mq_msgsize_max;
+
};

extern struct ipc_namespace init_ipc_ns;
extern atomic_t nr_ipc_ns;

-#ifdef CONFIG_SYSVIPC
+#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
#define INIT_IPC_NS(ns) .ns = &init_ipc_ns,
+#else
+#define INIT_IPC_NS(ns)
+#endif

+#ifdef CONFIG_SYSVIPC
extern int register_ipcns_notifier(struct ipc_namespace *);
extern int cond_register_ipcns_notifier(struct ipc_namespace *);
extern void unregister_ipcns_notifier(struct ipc_namespace *);
extern int ipcns_notify(unsigned long);
-
#else /* CONFIG_SYSVIPC */
-#define INIT_IPC_NS(ns)
+#define register_ipcns_notifier(ns)
+#define cond_register_ipcns_notifier(ns)
+#define unregister_ipcns_notifier(ns)
+#define ipcns_notify(l)
#endif /* CONFIG_SYSVIPC */

-#if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS)
+#ifdef CONFIG_POSIX_MQUEUE
+extern void mq_init_ns(struct ipc_namespace *ns);
+/* default values */
+#define DFLT_QUEUESMAX 256 /* max number of message queues */
+#define DFLT_MSGMAX 10 /* max number of messages in each queue */
+#define HARD_MSGMAX (131072/sizeof(void *))
+#define DFLT_MSGSIZEMAX 8192 /* max message size */
+#else
+#define mq_init_ns(ns)
+#endif
+
+#if defined(CONFIG_IPC_NS)
extern void free_ipc_ns(struct kref *kref);
extern struct ipc_namespace *copy_ipcs(unsigned long flags,
struct ipc_namespace *ns);
diff --git a/init/Kconfig b/init/Kconfig
index ce75d2d..32c6315 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -489,10 +489,10 @@ config UTS_NS

config IPC_NS
bool "IPC namespace"
- depends on NAMESPACES && SYSVIPC
+ depends on NAMESPACES && (SYSVIPC || POSIX_MQUEUE)
help
In this namespace tasks work with IPC ids which correspond to
- different IPC objects in different namespaces
+ different IPC objects in different namespaces.

config USER_NS
bool "User namespace (EXPERIMENTAL)"
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index d9393f8..01d64a0 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -31,6 +31,7 @@
#include <linux/mutex.h>
#include <linux/nsproxy.h>
#include <linux/pid.h>
+#include <linux/ipc_namespace.h>

#include <net/sock.h>
#include "util.h"
@@ -46,12 +47,6 @@
#define STATE_PENDING 1
#define STATE_READY 2

-/* default values */
-#define DFLT_QUEUESMAX 256 /* max number of message queues */
-#define DFLT_MSGMAX 10 /* max number of messages in each queue */
-#define HARD_MSGMAX (131072/sizeof(void*))
-#define DFLT_MSGSIZEMAX 8192 /* max message size */
-
/*
* Define the ranges various user-specified maximum values can
* be set to.
@@ -95,12 +90,6 @@ static void remove_notification(struct mqueue_inode_info *info);

static spinlock_t mq_lock;
static struct kmem_cache *mqueue_inode_cachep;
-static struct vfsmount *mqueue_mnt;
-
-static unsigned int queues_count;
-static unsigned int queues_max = DFLT_QUEUESMAX;
-static unsigned int msg_max = DFLT_MSGMAX;
-static unsigned int msgsize_max = DFLT_MSGSIZEMAX;

static struct ctl_table_header * mq_sysctl_table;

@@ -109,11 +98,25 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
return container_of(inode, struct mqueue_inode_info, vfs_inode);
}

+void mq_init_ns(struct ipc_namespace *ns) {
+ ns->mq_queues_count = 0;
+ ns->mq_queues_max = DFLT_QUEUESMAX;
+ ns->mq_msg_max = DFLT_MSGMAX;
+ ns->mq_msgsize_max = DFLT_MSGSIZEMAX;
+ ns->mq_mnt = mntget(init_ipc_ns.mq_mnt);
+}
+
+void mq_exit_ns(struct ipc_namespace *ns) {
+ /* will need to clear out ns->mq_mnt->mnt_sb->s_fs_info here */
+ mntput(ns->mq_mnt);
+}
+
static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
struct mq_attr *attr)
{
struct user_struct *u = current_user();
struct inode *inode;
+ struct ipc_namespace *ipc_ns = &init_ipc_ns;

inode = new_inode(sb);
if (inode) {
@@ -142,8 +145,8 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
info->qsize = 0;
info->user = NULL; /* set when all is ok */
memset(&info->attr, 0, sizeof(info->attr));
- info->attr.mq_maxmsg = msg_max;
- info->attr.mq_msgsize = msgsize_max;
+ info->attr.mq_maxmsg = ipc_ns->mq_msg_max;
+ info->attr.mq_msgsize = ipc_ns->mq_msgsize_max;
if (attr) {
info->attr.mq_maxmsg = attr->mq_maxmsg;
info->attr.mq_msgsize = attr->mq_msgsize;
@@ -243,6 +246,7 @@ static void mqueue_delete_inode(struct inode *inode)
struct user_struct *user;
unsigned long mq_bytes;
int i;
+ struct ipc_namespace *ipc_ns = &init_ipc_ns;

if (S_ISDIR(inode->i_mode)) {
clear_inode(inode);
@@ -263,7 +267,7 @@ static void mqueue_delete_inode(struct inode *inode)
if (user) {
spin_lock(&mq_lock);
user->mq_bytes -= mq_bytes;
- queues_count--;
+ ipc_ns->mq_queues_count--;
spin_unlock(&mq_lock);
free_uid(user);
}
@@ -275,20 +279,22 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
struct inode *inode;
struct mq_attr *attr = dentry->d_fsdata;
int error;
+ struct ipc_namespace *ipc_ns = &init_ipc_ns;

spin_lock(&mq_lock);
- if (queues_count >= queues_max && !capable(CAP_SYS_RESOURCE)) {
+ if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max &&
+ !capable(CAP_SYS_RESOURCE)) {
error = -ENOSPC;
goto out_lock;
}
- queues_count++;
+ ipc_ns->mq_queues_count++;
spin_unlock(&mq_lock);

inode = mqueue_get_inode(dir->i_sb, mode, attr);
if (!inode) {
error = -ENOMEM;
spin_lock(&mq_lock);
- queues_count--;
+ ipc_ns->mq_queues_count--;
goto out_lock;
}

@@ -566,7 +572,7 @@ static void remove_notification(struct mqueue_inode_info *info)
info->notify_owner = NULL;
}

-static int mq_attr_ok(struct mq_attr *attr)
+static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr)
{
if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0)
return 0;
@@ -574,8 +580,8 @@ static int mq_attr_ok(struct mq_attr *attr)
if (attr->mq_maxmsg > HARD_MSGMAX)
return 0;
} else {
- if (attr->mq_maxmsg > msg_max ||
- attr->mq_msgsize > msgsize_max)
+ if (attr->mq_maxmsg > ipc_ns->mq_msg_max ||
+ attr->mq_msgsize > ipc_ns->mq_msgsize_max)
return 0;
}
/* check for overflow */
@@ -591,8 +597,9 @@ static int mq_attr_ok(struct mq_attr *attr)
/*
* Invoked when creating a new queue via sys_mq_open
*/
-static struct file *do_create(struct dentry *dir, struct dentry *dentry,
- int oflag, mode_t mode, struct mq_attr __user *u_attr)
+static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir,
+ struct dentry *dentry, int oflag, mode_t mode,
+ struct mq_attr __user *u_attr)
{
const struct cred *cred = current_cred();
struct mq_attr attr;
@@ -604,14 +611,14 @@ static struct file *do_create(struct dentry *dir, struct dentry *dentry,
if (copy_from_user(&attr, u_attr, sizeof(attr)))
goto out;
ret = -EINVAL;
- if (!mq_attr_ok(&attr))
+ if (!mq_attr_ok(ipc_ns, &attr))
goto out;
/* store for use during create */
dentry->d_fsdata = &attr;
}

mode &= ~current->fs->umask;
- ret = mnt_want_write(mqueue_mnt);
+ ret = mnt_want_write(ipc_ns->mq_mnt);
if (ret)
goto out;
ret = vfs_create(dir->d_inode, dentry, mode, NULL);
@@ -619,24 +626,25 @@ static struct file *do_create(struct dentry *dir, struct dentry *dentry,
if (ret)
goto out_drop_write;

- result = dentry_open(dentry, mqueue_mnt, oflag, cred);
+ result = dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred);
/*
* dentry_open() took a persistent mnt_want_write(),
* so we can now drop this one.
*/
- mnt_drop_write(mqueue_mnt);
+ mnt_drop_write(ipc_ns->mq_mnt);
return result;

out_drop_write:
- mnt_drop_write(mqueue_mnt);
+ mnt_drop_write(ipc_ns->mq_mnt);
out:
dput(dentry);
- mntput(mqueue_mnt);
+ mntput(ipc_ns->mq_mnt);
return ERR_PTR(ret);
}

/* Opens existing queue */
-static struct file *do_open(struct dentry *dentry, int oflag)
+static struct file *do_open(struct ipc_namespace *ipc_ns,
+ struct dentry *dentry, int oflag)
{
const struct cred *cred = current_cred();

@@ -645,17 +653,17 @@ static struct file *do_open(struct dentry *dentry, int oflag)

if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) {
dput(dentry);
- mntput(mqueue_mnt);
+ mntput(ipc_ns->mq_mnt);
return ERR_PTR(-EINVAL);
}

if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) {
dput(dentry);
- mntput(mqueue_mnt);
+ mntput(ipc_ns->mq_mnt);
return ERR_PTR(-EACCES);
}

- return dentry_open(dentry, mqueue_mnt, oflag, cred);
+ return dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred);
}

asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
@@ -665,6 +673,7 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
struct file *filp;
char *name;
int fd, error;
+ struct ipc_namespace *ipc_ns = &init_ipc_ns;

error = audit_mq_open(oflag, mode, u_attr);
if (error != 0)
@@ -677,13 +686,13 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
if (fd < 0)
goto out_putname;

- mutex_lock(&mqueue_mnt->mnt_root->d_inode->i_mutex);
- dentry = lookup_one_len(name, mqueue_mnt->mnt_root, strlen(name));
+ mutex_lock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
+ dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name));
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out_err;
}
- mntget(mqueue_mnt);
+ mntget(ipc_ns->mq_mnt);

if (oflag & O_CREAT) {
if (dentry->d_inode) { /* entry already exists */
@@ -691,17 +700,17 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
error = -EEXIST;
if (oflag & O_EXCL)
goto out;
- filp = do_open(dentry, oflag);
+ filp = do_open(ipc_ns, dentry, oflag);
} else {
- filp = do_create(mqueue_mnt->mnt_root, dentry,
- oflag, mode, u_attr);
+ filp = do_create(ipc_ns, ipc_ns->mq_mnt->mnt_root,
+ dentry, oflag, mode, u_attr);
}
} else {
error = -ENOENT;
if (!dentry->d_inode)
goto out;
audit_inode(name, dentry);
- filp = do_open(dentry, oflag);
+ filp = do_open(ipc_ns, dentry, oflag);
}

if (IS_ERR(filp)) {
@@ -714,13 +723,13 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,

out:
dput(dentry);
- mntput(mqueue_mnt);
+ mntput(ipc_ns->mq_mnt);
out_putfd:
put_unused_fd(fd);
out_err:
fd = error;
out_upsem:
- mutex_unlock(&mqueue_mnt->mnt_root->d_inode->i_mutex);
+ mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
out_putname:
putname(name);
return fd;
@@ -732,14 +741,15 @@ asmlinkage long sys_mq_unlink(const char __user *u_name)
char *name;
struct dentry *dentry;
struct inode *inode = NULL;
+ struct ipc_namespace *ipc_ns = &init_ipc_ns;

name = getname(u_name);
if (IS_ERR(name))
return PTR_ERR(name);

- mutex_lock_nested(&mqueue_mnt->mnt_root->d_inode->i_mutex,
+ mutex_lock_nested(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex,
I_MUTEX_PARENT);
- dentry = lookup_one_len(name, mqueue_mnt->mnt_root, strlen(name));
+ dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
goto out_unlock;
@@ -753,16 +763,16 @@ asmlinkage long sys_mq_unlink(const char __user *u_name)
inode = dentry->d_inode;
if (inode)
atomic_inc(&inode->i_count);
- err = mnt_want_write(mqueue_mnt);
+ err = mnt_want_write(ipc_ns->mq_mnt);
if (err)
goto out_err;
err = vfs_unlink(dentry->d_parent->d_inode, dentry);
- mnt_drop_write(mqueue_mnt);
+ mnt_drop_write(ipc_ns->mq_mnt);
out_err:
dput(dentry);

out_unlock:
- mutex_unlock(&mqueue_mnt->mnt_root->d_inode->i_mutex);
+ mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
putname(name);
if (inode)
iput(inode);
@@ -1211,14 +1221,14 @@ static int msg_maxsize_limit_max = MAX_MSGSIZEMAX;
static ctl_table mq_sysctls[] = {
{
.procname = "queues_max",
- .data = &queues_max,
+ .data = &init_ipc_ns.mq_queues_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
.procname = "msg_max",
- .data = &msg_max,
+ .data = &init_ipc_ns.mq_msg_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
@@ -1227,7 +1237,7 @@ static ctl_table mq_sysctls[] = {
},
{
.procname = "msgsize_max",
- .data = &msgsize_max,
+ .data = &init_ipc_ns.mq_msgsize_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
@@ -1273,13 +1283,13 @@ static int __init init_mqueue_fs(void)
if (error)
goto out_sysctl;

- if (IS_ERR(mqueue_mnt = kern_mount(&mqueue_fs_type))) {
- error = PTR_ERR(mqueue_mnt);
+ init_ipc_ns.mq_mnt = kern_mount(&mqueue_fs_type);
+ if (IS_ERR(init_ipc_ns.mq_mnt)) {
+ error = PTR_ERR(init_ipc_ns.mq_mnt);
goto out_filesystem;
}

/* internal initialization - not common for vfs */
- queues_count = 0;
spin_lock_init(&mq_lock);

return 0;
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index c82c215..c197cd1 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -13,10 +13,31 @@
#include <linux/security.h>
#include <linux/slab.h>
#include <linux/ipc.h>
+#include <linux/ipc_namespace.h>
#include <asm/uaccess.h>

#include "util.h"

+/*
+ * The next 2 defines are here bc this is the only file
+ * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
+ * and not CONFIG_IPC_NS.
+ */
+struct ipc_namespace init_ipc_ns = {
+ .kref = {
+ .refcount = ATOMIC_INIT(2),
+ },
+#ifdef CONFIG_POSIX_MQUEUE
+ .mq_mnt = NULL,
+ .mq_queues_count = 0,
+ .mq_queues_max = DFLT_QUEUESMAX,
+ .mq_msg_max = DFLT_MSGMAX,
+ .mq_msgsize_max = DFLT_MSGSIZEMAX,
+#endif
+};
+
+atomic_t nr_ipc_ns = ATOMIC_INIT(1);
+
struct msg_msgseg {
struct msg_msgseg* next;
/* the next part of the message follows immediately */
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 9171d94..4b4dc6d 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -25,6 +25,7 @@ static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
sem_init_ns(ns);
msg_init_ns(ns);
shm_init_ns(ns);
+ mq_init_ns(ns);

/*
* msgmni has already been computed for the new ipc ns.
@@ -101,6 +102,7 @@ void free_ipc_ns(struct kref *kref)
sem_exit_ns(ns);
msg_exit_ns(ns);
shm_exit_ns(ns);
+ mq_exit_ns(ns);
kfree(ns);
atomic_dec(&nr_ipc_ns);

diff --git a/ipc/util.c b/ipc/util.c
index 5a1808c..ac971ce 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -47,15 +47,6 @@ struct ipc_proc_iface {
int (*show)(struct seq_file *, void *);
};

-struct ipc_namespace init_ipc_ns = {
- .kref = {
- .refcount = ATOMIC_INIT(2),
- },
-};
-
-atomic_t nr_ipc_ns = ATOMIC_INIT(1);
-
-
#ifdef CONFIG_MEMORY_HOTPLUG

static void ipc_memory_notifier(struct work_struct *work)
diff --git a/ipc/util.h b/ipc/util.h
index 3646b45..52755c1 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -20,6 +20,13 @@ void shm_init (void);

struct ipc_namespace;

+#ifdef CONFIG_POSIX_MQUEUE
+void mq_exit_ns(struct ipc_namespace *ns);
+#else
+#define mq_exit_ns(ns)
+#endif
+
+#ifdef CONFIG_SYSVIPC
void sem_init_ns(struct ipc_namespace *ns);
void msg_init_ns(struct ipc_namespace *ns);
void shm_init_ns(struct ipc_namespace *ns);
@@ -27,6 +34,14 @@ void shm_init_ns(struct ipc_namespace *ns);
void sem_exit_ns(struct ipc_namespace *ns);
void msg_exit_ns(struct ipc_namespace *ns);
void shm_exit_ns(struct ipc_namespace *ns);
+#else
+#define sem_init_ns(ns)
+#define msg_init_ns(ns)
+#define shm_init_ns(ns)
+#define sem_exit_ns(ns)
+#define msg_exit_ns(ns)
+#define shm_exit_ns(ns)
+#endif

/*
* Structure that holds the parameters needed by the ipc operations
--
1.5.4.3

2008-12-17 17:57:31

by Serge E. Hallyn

[permalink] [raw]
Subject: [LTP PATCH 1/4] posix message queue namespaces: first test

(Subrata, I'm sending these testcases now so people can use them,
but they should not yet be considered for integration into ltp
of course, and when they are, the kernel version test will need
to be switched to at least 2.6.29)

Add the first test for posix message queue namespaces, plus a
test to detect whether they are enabled.

Note that the kernel version check is currently bogus - these are
not yet enabled in 2.6.28. But a fella's gotta test.

Based on older version by Nadia Derbey.
Signed-off-by: Nadia Derbey <[email protected]>

Signed-off-by: Serge Hallyn <[email protected]>
---
testcases/kernel/containers/Makefile | 2 +-
testcases/kernel/containers/README | 4 +
testcases/kernel/containers/container_test.sh | 9 ++
testcases/kernel/containers/mqns/Makefile | 41 +++++++
.../kernel/containers/mqns/check_mqns_enabled.c | 65 ++++++++++
testcases/kernel/containers/mqns/mqns.h | 13 ++
testcases/kernel/containers/mqns/mqns_01.c | 123 ++++++++++++++++++++
testcases/kernel/containers/mqns/runmqnstest.sh | 39 ++++++
8 files changed, 295 insertions(+), 1 deletions(-)
create mode 100644 testcases/kernel/containers/mqns/Makefile
create mode 100644 testcases/kernel/containers/mqns/check_mqns_enabled.c
create mode 100644 testcases/kernel/containers/mqns/mqns.h
create mode 100644 testcases/kernel/containers/mqns/mqns_01.c
create mode 100644 testcases/kernel/containers/mqns/runmqnstest.sh

diff --git a/testcases/kernel/containers/Makefile b/testcases/kernel/containers/Makefile
index d2d143d..b1b6cdc 100644
--- a/testcases/kernel/containers/Makefile
+++ b/testcases/kernel/containers/Makefile
@@ -18,7 +18,7 @@
## ##
################################################################################

-SUBDIRS := libclone utsname sysvipc pidns netns
+SUBDIRS := libclone utsname sysvipc pidns netns mqns

all: check_for_unshare
@set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done
diff --git a/testcases/kernel/containers/README b/testcases/kernel/containers/README
index f1e485b..fd0898f 100644
--- a/testcases/kernel/containers/README
+++ b/testcases/kernel/containers/README
@@ -37,6 +37,10 @@ each functionality README file for detail:

sysvipc/*
Contains all the testcases related to IPC NS tests.
+posixmq/*
+ Contains all the testcases related to POSIX MQ NS tests. These
+ are strictly speaking a part of the ipc namespaces, but can be
+ enabled in the kernel without SYSV IPC support.
utsname/*
Contains all the testcases related to utsname tests.
libclone/*
diff --git a/testcases/kernel/containers/container_test.sh b/testcases/kernel/containers/container_test.sh
index 911a6a4..755e910 100755
--- a/testcases/kernel/containers/container_test.sh
+++ b/testcases/kernel/containers/container_test.sh
@@ -54,6 +54,15 @@ else
echo "Process id namespaces not enabled in kernel. Not running pidns tests."
fi

+check_mqns_enabled
+if [ $? -eq 0 ]; then
+ echo "Running POSIX message queue tests."
+ runmqnstest.sh
+else
+ echo "Posix message queues or ipc namespaces not enabled in kernel."
+ echo "Not running mqns tests."
+fi
+
check_netns_enabled
if [ $? -eq 0 ]; then
echo "Running netns tests."
diff --git a/testcases/kernel/containers/mqns/Makefile b/testcases/kernel/containers/mqns/Makefile
new file mode 100644
index 0000000..ec6ba14
--- /dev/null
+++ b/testcases/kernel/containers/mqns/Makefile
@@ -0,0 +1,41 @@
+################################################################################
+## ##
+## Copyright (c) International Business Machines Corp., 2007 ##
+## ##
+## This program is free software; you can redistribute it and#or modify ##
+## it under the terms of the GNU General Public License as published by ##
+## the Free Software Foundation; either version 2 of the License, or ##
+## (at your option) any later version. ##
+## ##
+## This program is distributed in the hope that it will be useful, but ##
+## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ##
+## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ##
+## for more details. ##
+## ##
+## You should have received a copy of the GNU General Public License ##
+## along with this program; if not, write to the Free Software ##
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ##
+## ##
+################################################################################
+
+CFLAGS += -Wall
+CPPFLAGS += -I../../../../include -I../libclone
+LDLIBS += -L../../../../lib -L../libclone ../libclone/libclone.a -lltp -lrt
+
+SRCS := $(wildcard *.c)
+TARGETS := $(SRCS:%.c=%)
+
+HAS_UNSHARE ?= $(shell ../check_for_unshare && echo y)
+ifneq ($(HAS_UNSHARE),y)
+TARGETS :=
+endif
+
+all: $(TARGETS)
+
+clean:
+ rm -f $(TARGETS) *.o
+
+install:
+ifeq ($(HAS_UNSHARE),y)
+ @set -e; for i in $(TARGETS) runmqnstest.sh check_mqns_enabled; do ln -f $$i ../../../bin/$$i ; chmod +x runmqnstest.sh ; done
+endif
diff --git a/testcases/kernel/containers/mqns/check_mqns_enabled.c b/testcases/kernel/containers/mqns/check_mqns_enabled.c
new file mode 100644
index 0000000..21d56a9
--- /dev/null
+++ b/testcases/kernel/containers/mqns/check_mqns_enabled.c
@@ -0,0 +1,65 @@
+/*
+* Copyright (c) International Business Machines Corp., 2007
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+* the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* Author: Serge Hallyn <[email protected]>
+***************************************************************************/
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "../libclone/libclone.h"
+#include "test.h"
+#include "mqns.h"
+
+int dummy(void *v)
+{
+ return 0;
+}
+
+int main()
+{
+ void *childstack, *stack;
+ int pid;
+ mqd_t mqd;
+
+ if (tst_kvercmp(2,6,27) < 0)
+ return 1;
+
+ mq_unlink("/checkmqnsenabled");
+ mqd = mq_open("/checkmqnsenabled", O_RDWR|O_CREAT|O_EXCL, 0777, NULL);
+ if (mqd == -1) {
+ perror("mq_open");
+ return 3;
+ }
+ mq_close(mqd);
+ mq_unlink("/checkmqnsenabled");
+
+ stack = malloc(getpagesize());
+ if (!stack) {
+ perror("malloc");
+ return 4;
+ }
+ childstack = stack + getpagesize();
+#ifdef __ia64__
+ pid = clone2(dummy, childstack, getpagesize(), CLONE_NEWIPC, NULL, NULL, NULL, NULL);
+#else
+ pid = clone(dummy, childstack, CLONE_NEWIPC, NULL);
+#endif
+
+ if (pid == -1)
+ return 5;
+
+ return 0;
+}
diff --git a/testcases/kernel/containers/mqns/mqns.h b/testcases/kernel/containers/mqns/mqns.h
new file mode 100644
index 0000000..df8fc4b
--- /dev/null
+++ b/testcases/kernel/containers/mqns/mqns.h
@@ -0,0 +1,13 @@
+#ifndef __MQNS_H
+#define __MQNS_H
+
+#include <sys/mount.h>
+#include <mqueue.h>
+#include <test.h>
+#include <libclone.h>
+
+
+#define DEV_MQUEUE "/dev/mqueue"
+#define SLASH_MQ1 "/MQ1"
+
+#endif /* __MQNS_H */
diff --git a/testcases/kernel/containers/mqns/mqns_01.c b/testcases/kernel/containers/mqns/mqns_01.c
new file mode 100644
index 0000000..7cf5482
--- /dev/null
+++ b/testcases/kernel/containers/mqns/mqns_01.c
@@ -0,0 +1,123 @@
+/*
+* Copyright (c) International Business Machines Corp., 2007
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+* the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* Author: Nadia Derbey <[email protected]>
+*
+* Check mqns isolation: father mqns cannot be accessed from newinstance
+*
+* Mount mqueue fs
+* Create a posix mq -->mq1
+* unshare
+* In unshared process:
+* Mount newinstance mqueuefs
+* Check that mq1 is not readable from new ns
+
+***************************************************************************/
+
+#define _GNU_SOURCE 1
+#include <sys/wait.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "mqns.h"
+
+char *TCID = "posixmq_namespace_01";
+int TST_TOTAL=1;
+
+int p1[2];
+int p2[2];
+
+int check_mqueue(void *vtest)
+{
+ char buf[3];
+ mqd_t mqd;
+
+ close(p1[1]);
+ close(p2[0]);
+
+ read(p1[0], buf, 3);
+ mqd = mq_open(SLASH_MQ1, O_RDONLY);
+ if (mqd == -1) {
+ write(p2[1], "notfnd", 7);
+ } else {
+ write(p2[1], "exists", 7);
+ mq_close(mqd);
+ }
+ tst_exit(0);
+
+ /* NOT REACHED */
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int r;
+ mqd_t mqd;
+ char buf[7];
+ int use_clone = T_UNSHARE;
+
+ if (argc == 2 && strcmp(argv[1], "-clone") == 0) {
+ tst_resm(TINFO, "Testing posix mq namespaces through clone(2).\n");
+ use_clone = T_CLONE;
+ } else
+ tst_resm(TINFO, "Testing posix mq namespaces through unshare(2).\n");
+
+ if (pipe(p1) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+ if (pipe(p2) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+
+ mqd = mq_open(SLASH_MQ1, O_RDWR|O_CREAT|O_EXCL, 0777, NULL);
+ if (mqd == -1) {
+ perror("mq_open");
+ tst_resm(TFAIL, "mq_open failed\n");
+ tst_exit(3);
+ }
+
+ tst_resm(TINFO, "Checking namespaces isolation from parent to child\n");
+ /* fire off the test */
+ r = do_clone_unshare_test(use_clone, CLONE_NEWIPC, check_mqueue, NULL);
+ if (r < 0) {
+ tst_resm(TFAIL, "failed clone/unshare\n");
+ mq_close(mqd);
+ mq_unlink(SLASH_MQ1);
+ tst_exit(1);
+ }
+
+ close(p1[0]);
+ close(p2[1]);
+ write(p1[1], "go", 3);
+ read(p2[0], buf, 7);
+ if (!strcmp(buf, "exists")) {
+ tst_resm(TFAIL, "child process found mqueue\n");
+ r = TFAIL;
+ } else if (!strcmp(buf, "notfnd")) {
+ tst_resm(TPASS, "child process didn't find mqueue\n");
+ r = TPASS;
+ } else {
+ tst_resm(TFAIL, "UNKNOWN RESULT\n");
+ r = TFAIL;
+ }
+
+ /* destroy the mqueue */
+ mq_close(mqd);
+ mq_unlink(SLASH_MQ1);
+
+ tst_exit(r);
+
+ /* NOT REACHED */
+ return 0;
+}
diff --git a/testcases/kernel/containers/mqns/runmqnstest.sh b/testcases/kernel/containers/mqns/runmqnstest.sh
new file mode 100644
index 0000000..87c712e
--- /dev/null
+++ b/testcases/kernel/containers/mqns/runmqnstest.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+################################################################################
+## ##
+## Copyright (c) International Business Machines Corp., 2007 ##
+## ##
+## This program is free software; you can redistribute it and#or modify ##
+## it under the terms of the GNU General Public License as published by ##
+## the Free Software Foundation; either version 2 of the License, or ##
+## (at your option) any later version. ##
+## ##
+## This program is distributed in the hope that it will be useful, but ##
+## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ##
+## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ##
+## for more details. ##
+## ##
+## You should have received a copy of the GNU General Public License ##
+## along with this program; if not, write to the Free Software ##
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ##
+## ##
+################################################################################
+
+exit_code=0
+tests_list='mqns_01'
+
+for t in $tests_list
+do
+ $t
+ if [ $? -ne 0 ]; then
+ exit_code="$?"
+ exit $exit_code
+ fi
+ $t -clone
+ if [ $? -ne 0 ]; then
+ exit_code="$?"
+ exit $exit_code
+ fi
+done
+
+exit $exit_code
--
1.6.0.5

2008-12-17 17:58:16

by Serge E. Hallyn

[permalink] [raw]
Subject: [LTP PATCH 2/4] posix mqns: test parent to child mq access

It's kind of redundant with test 01 since there is no hierarchical
relationship between ipc namespaces - they are all completely isolated.
But heck it can't hurt.

Signed-off-by: Nadia Derbey <[email protected]>
Signed-off-by: Serge Hallyn <[email protected]>
---
testcases/kernel/containers/mqns/mqns_02.c | 138 +++++++++++++++++++++++
testcases/kernel/containers/mqns/runmqnstest.sh | 2 +-
2 files changed, 139 insertions(+), 1 deletions(-)
create mode 100644 testcases/kernel/containers/mqns/mqns_02.c
mode change 100644 => 100755 testcases/kernel/containers/mqns/runmqnstest.sh

diff --git a/testcases/kernel/containers/mqns/mqns_02.c b/testcases/kernel/containers/mqns/mqns_02.c
new file mode 100644
index 0000000..2414f43
--- /dev/null
+++ b/testcases/kernel/containers/mqns/mqns_02.c
@@ -0,0 +1,138 @@
+/*
+* Copyright (c) International Business Machines Corp., 2007
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+* the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* Author: Nadia Derbey <[email protected]>
+*
+* Check mqns isolation: child mqns cannot be accessed from father
+*
+* Mount mqueue fs
+* unshare
+* In unshared process:
+* Mount newinstance mqueuefs
+* Create a posix mq -->mq1
+* Check that mq1 is not readable from father
+*
+* Changelog:
+* Dec 16: accomodate new mqns semantics (Serge Hallyn)
+
+***************************************************************************/
+
+#define _GNU_SOURCE 1
+#include <sys/wait.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "mqns.h"
+
+char *TCID = "posixmq_namespace_02";
+int TST_TOTAL=1;
+
+int p1[2];
+int p2[2];
+
+int check_mqueue(void *vtest)
+{
+ char buf[3];
+ mqd_t mqd;
+
+ close(p1[1]);
+ close(p2[0]);
+
+ read(p1[0], buf, 3);
+
+ mqd = mq_open(SLASH_MQ1, O_RDWR|O_CREAT|O_EXCL, 0777, NULL);
+ if (mqd == -1) {
+ write(p2[1], "mqfail", 7);
+ tst_exit(3);
+ }
+
+ write(p2[1], "mqopen", 7);
+
+ read(p1[0], buf, 5);
+
+ /* destroy the mqueue */
+ mq_close(mqd);
+ mq_unlink(SLASH_MQ1);
+
+ write(p2[1], "done", 5);
+
+ tst_exit(0);
+
+ /* NOT REACHED */
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int r;
+ mqd_t mqd;
+ char buf[7];
+ int use_clone = T_UNSHARE;
+
+ if (argc == 2 && strcmp(argv[1], "-clone") == 0) {
+ tst_resm(TINFO, "Testing posix mq namespaces through clone(2).\n");
+ use_clone = T_CLONE;
+ } else
+ tst_resm(TINFO, "Testing posix mq namespaces through unshare(2).\n");
+
+ if (pipe(p1) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+ if (pipe(p2) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+
+ /* fire off the test */
+ r = do_clone_unshare_test(use_clone, CLONE_NEWIPC, check_mqueue, NULL);
+ if (r < 0) {
+ tst_resm(TFAIL, "failed clone/unshare\n");
+ tst_exit(1);
+ }
+
+ tst_resm(TINFO, "Checking namespaces isolation (child to parent)\n");
+
+ close(p1[0]);
+ close(p2[1]);
+ write(p1[1], "go", 3);
+
+ read(p2[0], buf, 7);
+ if (!strcmp(buf, "mqfail")) {
+ tst_resm(TFAIL, "child process could not create mqueue\n");
+ umount(DEV_MQUEUE);
+ tst_exit(TFAIL);
+ } else if (strcmp(buf, "mqopen")) {
+ tst_resm(TFAIL, "child process could not create mqueue\n");
+ umount(DEV_MQUEUE);
+ tst_exit(TFAIL);
+ }
+
+ mqd = mq_open(SLASH_MQ1, O_RDONLY);
+ if (mqd == -1) {
+ r = TPASS;
+ tst_resm(TPASS, "Father process doesn't see mqueue\n");
+ } else {
+ r = TFAIL;
+ tst_resm(TFAIL, "Father process found mqueue\n");
+ mq_close(mqd);
+ }
+
+ write(p1[1], "cont", 5);
+ read(p2[0], buf, 7);
+
+ tst_exit(r);
+
+ /* NOT REACHED */
+ return 0;
+}
diff --git a/testcases/kernel/containers/mqns/runmqnstest.sh b/testcases/kernel/containers/mqns/runmqnstest.sh
old mode 100644
new mode 100755
index 87c712e..a5bbd24
--- a/testcases/kernel/containers/mqns/runmqnstest.sh
+++ b/testcases/kernel/containers/mqns/runmqnstest.sh
@@ -20,7 +20,7 @@
################################################################################

exit_code=0
-tests_list='mqns_01'
+tests_list='mqns_01 mqns_02'

for t in $tests_list
do
--
1.6.0.5

2008-12-17 17:58:39

by Serge E. Hallyn

[permalink] [raw]
Subject: [LTP PATCH 3/4] posix mqns: test vfs and mq interaction

Test that the interaction between mqueuefs and ipc namespaces
is correct (one mqueuefs for each ipc namespace, living at least
as long as the namespace)

Signed-off-by: Serge Hallyn <[email protected]>
---
testcases/kernel/containers/mqns/mqns.h | 1 +
testcases/kernel/containers/mqns/mqns_03.c | 196 +++++++++++++++++++++++
testcases/kernel/containers/mqns/runmqnstest.sh | 2 +-
3 files changed, 198 insertions(+), 1 deletions(-)
create mode 100644 testcases/kernel/containers/mqns/mqns_03.c

diff --git a/testcases/kernel/containers/mqns/mqns.h b/testcases/kernel/containers/mqns/mqns.h
index df8fc4b..4bf1e1f 100644
--- a/testcases/kernel/containers/mqns/mqns.h
+++ b/testcases/kernel/containers/mqns/mqns.h
@@ -9,5 +9,6 @@

#define DEV_MQUEUE "/dev/mqueue"
#define SLASH_MQ1 "/MQ1"
+#define SLASH_MQ2 "/MQ2"

#endif /* __MQNS_H */
diff --git a/testcases/kernel/containers/mqns/mqns_03.c b/testcases/kernel/containers/mqns/mqns_03.c
new file mode 100644
index 0000000..732bf88
--- /dev/null
+++ b/testcases/kernel/containers/mqns/mqns_03.c
@@ -0,0 +1,196 @@
+/*
+* Copyright (c) International Business Machines Corp., 2007
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+* the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* Author: Serge Hallyn <[email protected]>
+*
+* Check ipcns+sb longevity
+*
+* Mount mqueue fs
+* unshare
+* In unshared process:
+* Create "/mq1" with mq_open()
+* Mount mqueuefs
+* Check that /mq1 exists
+* Create /dev/mqueue/mq2 through vfs (create(2))
+* Umount /dev/mqueue
+* Remount /dev/mqueue
+* Check that both /mq1 and /mq2 exist
+
+***************************************************************************/
+
+#define _GNU_SOURCE 1
+#include <sys/wait.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "mqns.h"
+
+char *TCID = "posixmq_namespace_03";
+int TST_TOTAL=1;
+
+int p1[2];
+int p2[2];
+
+#define FNAM1 DEV_MQUEUE2 SLASH_MQ1
+#define FNAM2 DEV_MQUEUE2 SLASH_MQ2
+
+int check_mqueue(void *vtest)
+{
+ char buf[30];
+ mqd_t mqd;
+ int rc;
+ struct stat statbuf;
+
+ close(p1[1]);
+ close(p2[0]);
+
+ read(p1[0], buf, 3); /* go */
+
+ mqd = mq_open(SLASH_MQ1, O_RDWR|O_CREAT|O_EXCL, 0755, NULL);
+ if (mqd == -1) {
+ write(p2[1], "mqfail", 7);
+ tst_exit(3);
+ }
+
+ mq_close(mqd);
+
+ rc = mount("mqueue", DEV_MQUEUE2, "mqueue", 0, NULL);
+ if (rc == -1) {
+ perror("mount");
+ write(p2[1], "mount1", 7);
+ tst_exit(3);
+ }
+
+ rc = stat(FNAM1, &statbuf);
+ if (rc == -1) {
+ write(p2[1], "stat1", 6);
+ tst_exit(3);
+ }
+
+ rc = creat(FNAM2, 0755);
+ if (rc == -1) {
+ write(p2[1], "creat", 6);
+ tst_exit(3);
+ }
+
+ close(rc);
+
+ rc = umount(DEV_MQUEUE2);
+ if (rc == -1) {
+ perror("umount");
+ write(p2[1], "umount", 7);
+ tst_exit(3);
+ }
+
+ rc = mount("mqueue", DEV_MQUEUE2, "mqueue", 0, NULL);
+ if (rc == -1) {
+ write(p2[1], "mount2", 7);
+ tst_exit(3);
+ }
+
+ rc = stat(FNAM1, &statbuf);
+ if (rc == -1) {
+ write(p2[1], "stat2", 7);
+ tst_exit(3);
+ }
+
+ rc = stat(FNAM2, &statbuf);
+ if (rc == -1) {
+ write(p2[1], "stat3", 7);
+ tst_exit(3);
+ }
+
+ write(p2[1], "done", 5);
+
+ tst_exit(0);
+
+ /* NOT REACHED */
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int r;
+ char buf[7];
+ int use_clone = T_UNSHARE;
+
+ if (argc == 2 && strcmp(argv[1], "-clone") == 0) {
+ tst_resm(TINFO, "Testing posix mq namespaces through clone(2).\n");
+ use_clone = T_CLONE;
+ } else
+ tst_resm(TINFO, "Testing posix mq namespaces through unshare(2).\n");
+
+ if (pipe(p1) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+ if (pipe(p2) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+
+ /* fire off the test */
+ r = do_clone_unshare_test(use_clone, CLONE_NEWIPC, check_mqueue, NULL);
+ if (r < 0) {
+ tst_resm(TFAIL, "failed clone/unshare\n");
+ tst_exit(1);
+ }
+
+ tst_resm(TINFO, "Checking correct umount+remount of mqueuefs\n");
+
+ mkdir(DEV_MQUEUE2, 0755);
+
+ close(p1[0]);
+ close(p1[0]);
+ close(p2[1]);
+ write(p1[1], "go", 3);
+
+ read(p2[0], buf, 7);
+ r = TFAIL;
+ if (!strcmp(buf, "mqfail")) {
+ tst_resm(TFAIL, "child process could not create mqueue\n");
+ goto fail;
+ } else if (!strcmp(buf, "mount1")) {
+ tst_resm(TFAIL, "child process could not mount mqueue\n");
+ goto fail;
+ } else if (!strcmp(buf, "stat1x")) {
+ tst_resm(TFAIL, "mq created by child is not in mqueuefs\n");
+ goto fail;
+ } else if (!strcmp(buf, "creat")) {
+ tst_resm(TFAIL, "child couldn't creat mq through mqueuefs\n");
+ goto fail;
+ } else if (!strcmp(buf, "umount")) {
+ tst_resm(TFAIL, "child couldn't umount mqueuefs\n");
+ goto fail;
+ } else if (!strcmp(buf, "mount2")) {
+ tst_resm(TFAIL, "child couldn't remount mqueuefs\n");
+ goto fail;
+ } else if (!strcmp(buf, "stat2")) {
+ tst_resm(TFAIL, "mq_open()d file gone after remount of mqueuefs\n");
+ goto fail;
+ } else if (!strcmp(buf, "stat3")) {
+ tst_resm(TFAIL, "creat(2)'d file gone after remount of mqueuefs\n");
+ goto fail;
+ }
+
+ tst_resm(TPASS, "umount+remount of mqueuefs remounted the right fs\n");
+
+ r = 0;
+fail:
+ umount(DEV_MQUEUE2);
+ rmdir(DEV_MQUEUE2);
+ tst_exit(r);
+
+ /* NOT REACHED */
+ return 0;
+}
diff --git a/testcases/kernel/containers/mqns/runmqnstest.sh b/testcases/kernel/containers/mqns/runmqnstest.sh
index a5bbd24..b518d28 100755
--- a/testcases/kernel/containers/mqns/runmqnstest.sh
+++ b/testcases/kernel/containers/mqns/runmqnstest.sh
@@ -20,7 +20,7 @@
################################################################################

exit_code=0
-tests_list='mqns_01 mqns_02'
+tests_list='mqns_01 mqns_02 mqns_03'

for t in $tests_list
do
--
1.6.0.5

2008-12-17 17:58:54

by Serge E. Hallyn

[permalink] [raw]
Subject: [LTP PATCH 4/4] posix mqns: test that user mount of posixmq survivies the ipcns

If the mqueuefs for a ipc namespace is mounted in a mounts namespace
which outlives the ipcns, then the view of the fs remains valid
until someone umounts it. Run some tests to make sure that all is
sane.

Signed-off-by: Serge Hallyn <[email protected]>
---
testcases/kernel/containers/mqns/mqns.h | 1 +
testcases/kernel/containers/mqns/mqns_04.c | 169 +++++++++++++++++++++++
testcases/kernel/containers/mqns/runmqnstest.sh | 2 +-
3 files changed, 171 insertions(+), 1 deletions(-)
create mode 100644 testcases/kernel/containers/mqns/mqns_04.c

diff --git a/testcases/kernel/containers/mqns/mqns.h b/testcases/kernel/containers/mqns/mqns.h
index 4bf1e1f..33166d2 100644
--- a/testcases/kernel/containers/mqns/mqns.h
+++ b/testcases/kernel/containers/mqns/mqns.h
@@ -8,6 +8,7 @@


#define DEV_MQUEUE "/dev/mqueue"
+#define DEV_MQUEUE2 "/dev/mqueue2"
#define SLASH_MQ1 "/MQ1"
#define SLASH_MQ2 "/MQ2"

diff --git a/testcases/kernel/containers/mqns/mqns_04.c b/testcases/kernel/containers/mqns/mqns_04.c
new file mode 100644
index 0000000..ef76f0a
--- /dev/null
+++ b/testcases/kernel/containers/mqns/mqns_04.c
@@ -0,0 +1,169 @@
+/*
+* Copyright (c) International Business Machines Corp., 2007
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+* the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* Author: Serge Hallyn <[email protected]>
+*
+* Check mqueuefs lifetime
+* . parent creates /dev/mqueue2
+* . child mounts mqueue there
+* . child does mq_open("/ab")
+* . parent checks for /dev/mqueue2
+* . child exits
+* . parent checks for /dev/mqueue2
+* . parent tries 'touch /dev/mqueue2/dd' -> should fail
+* . parent umounts /dev/mqueue2
+
+***************************************************************************/
+
+#define _GNU_SOURCE 1
+#include <sys/wait.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "mqns.h"
+
+char *TCID = "posixmq_namespace_04";
+int TST_TOTAL=1;
+
+int p1[2];
+int p2[2];
+
+#define FNAM1 DEV_MQUEUE2 SLASH_MQ1
+#define FNAM2 DEV_MQUEUE2 SLASH_MQ2
+
+int check_mqueue(void *vtest)
+{
+ char buf[30];
+ mqd_t mqd;
+ int rc;
+
+ close(p1[1]);
+ close(p2[0]);
+
+ read(p1[0], buf, 3); /* go */
+
+ mqd = mq_open(SLASH_MQ1, O_RDWR|O_CREAT|O_EXCL, 0755, NULL);
+ if (mqd == -1) {
+ write(p2[1], "mqfail", 7);
+ tst_exit(3);
+ }
+
+ mq_close(mqd);
+
+ rc = mount("mqueue", DEV_MQUEUE2, "mqueue", 0, NULL);
+ if (rc == -1) {
+ perror("mount");
+ write(p2[1], "mount", 6);
+ tst_exit(3);
+ }
+
+ write(p2[1], "go", 3);
+ read(p1[0], buf, 3);
+
+ tst_exit(0);
+
+ /* NOT REACHED */
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int r = TFAIL;
+ int rc;
+ int status;
+ char buf[7];
+ struct stat statbuf;
+ int use_clone = T_UNSHARE;
+
+ if (argc == 2 && strcmp(argv[1], "-clone") == 0) {
+ tst_resm(TINFO, "Testing posix mq namespaces through clone(2).\n");
+ use_clone = T_CLONE;
+ } else
+ tst_resm(TINFO, "Testing posix mq namespaces through unshare(2).\n");
+
+ if (pipe(p1) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+ if (pipe(p2) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
+
+ mkdir(DEV_MQUEUE2, 0755);
+
+ tst_resm(TINFO, "Checking mqueue filesystem lifetime\n");
+
+ /* fire off the test */
+ rc = do_clone_unshare_test(use_clone, CLONE_NEWIPC, check_mqueue, NULL);
+ if (rc < 0) {
+ tst_resm(TFAIL, "failed clone/unshare\n");
+ goto fail;
+ }
+
+ close(p1[0]);
+ close(p2[1]);
+ write(p1[1], "go", 3);
+
+ read(p2[0], buf, 7);
+ if (!strcmp(buf, "mqfail")) {
+ tst_resm(TFAIL, "child process could not create mqueue\n");
+ goto fail;
+ } else if (!strcmp(buf, "mount")) {
+ tst_resm(TFAIL, "child process could not mount mqueue\n");
+ goto fail;
+ }
+
+ rc = stat(FNAM1, &statbuf);
+ if (rc == -1) {
+ perror("stat");
+ write(p1[1], "go", 3);
+ tst_resm(TFAIL, "parent could not see child's created mq\n");
+ goto fail;
+ }
+ write(p1[1], "go", 3);
+
+ rc = wait(&status);
+ if (rc == -1) {
+ perror("wait");
+ tst_resm(TFAIL, "error while parent waited on child to exit\n");
+ goto fail;
+ }
+ if (!WIFEXITED(status)) {
+ tst_resm(TFAIL, "Child did not exit normally (status %d)\n", status);
+ goto fail;
+ }
+ rc = stat(FNAM1, &statbuf);
+ if (rc == -1) {
+ tst_resm(TFAIL, "parent's view of child's mq died with child\n");
+ goto fail;
+ }
+
+ rc = creat(FNAM2, 0755);
+ if (rc != -1) {
+ tst_resm(TFAIL, "parent was able to create a file in dead child's mqfs\n");
+ goto fail;
+ }
+
+ tst_resm(TPASS, "Child mqueue fs still visible for parent\n");
+
+ r = 0;
+
+fail:
+ umount(DEV_MQUEUE2);
+ rmdir(DEV_MQUEUE2);
+ tst_exit(r);
+
+ /* NOT REACHED */
+ return 0;
+}
diff --git a/testcases/kernel/containers/mqns/runmqnstest.sh b/testcases/kernel/containers/mqns/runmqnstest.sh
index b518d28..9008fd0 100755
--- a/testcases/kernel/containers/mqns/runmqnstest.sh
+++ b/testcases/kernel/containers/mqns/runmqnstest.sh
@@ -20,7 +20,7 @@
################################################################################

exit_code=0
-tests_list='mqns_01 mqns_02 mqns_03'
+tests_list='mqns_01 mqns_02 mqns_03 mqns_04'

for t in $tests_list
do
--
1.6.0.5

2008-12-17 18:36:24

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH 1/2] mqueue ns: move mqueue_mnt into struct ipc_namespace

On Wed, 2008-12-17 at 11:55 -0600, Serge E. Hallyn wrote:
> Move mqueue vfsmount plus a few tunables into the
> ipc_namespace struct. The CONFIG_IPC_NS boolean
> and the ipc_namespace struct will serve both the
> posix message queue namespaces and the SYSV ipc
> namespaces.
>
> Signed-off-by: Cedric Le Goater <[email protected]>
> Signed-off-by: Serge E. Hallyn <[email protected]>
> ---
> include/linux/ipc_namespace.h | 32 ++++++++++--
> init/Kconfig | 4 +-
> ipc/mqueue.c | 116 ++++++++++++++++++++++-------------------
> ipc/msgutil.c | 21 +++++++
> ipc/namespace.c | 2 +
> ipc/util.c | 9 ---
> ipc/util.h | 15 +++++
> 7 files changed, 131 insertions(+), 68 deletions(-)
>
> diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
> index ea330f9..532598f 100644
> --- a/include/linux/ipc_namespace.h
> +++ b/include/linux/ipc_namespace.h
> @@ -44,24 +44,48 @@ struct ipc_namespace {
> int shm_tot;
>
> struct notifier_block ipcns_nb;
> +
> + struct vfsmount *mq_mnt;
> + unsigned int mq_queues_count;
> + unsigned int mq_queues_max;
> + unsigned int mq_msg_max;
> + unsigned int mq_msgsize_max;
> +
> };
>
> extern struct ipc_namespace init_ipc_ns;
> extern atomic_t nr_ipc_ns;
>
> -#ifdef CONFIG_SYSVIPC
> +#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
> #define INIT_IPC_NS(ns) .ns = &init_ipc_ns,
> +#else
> +#define INIT_IPC_NS(ns)
> +#endif
>
> +#ifdef CONFIG_SYSVIPC
> extern int register_ipcns_notifier(struct ipc_namespace *);
> extern int cond_register_ipcns_notifier(struct ipc_namespace *);
> extern void unregister_ipcns_notifier(struct ipc_namespace *);
> extern int ipcns_notify(unsigned long);
> -
> #else /* CONFIG_SYSVIPC */
> -#define INIT_IPC_NS(ns)
> +#define register_ipcns_notifier(ns)
> +#define cond_register_ipcns_notifier(ns)
> +#define unregister_ipcns_notifier(ns)
> +#define ipcns_notify(l)
> #endif /* CONFIG_SYSVIPC */
>
> -#if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS)
> +#ifdef CONFIG_POSIX_MQUEUE
> +extern void mq_init_ns(struct ipc_namespace *ns);
> +/* default values */
> +#define DFLT_QUEUESMAX 256 /* max number of message queues */
> +#define DFLT_MSGMAX 10 /* max number of messages in each queue */
> +#define HARD_MSGMAX (131072/sizeof(void *))
> +#define DFLT_MSGSIZEMAX 8192 /* max message size */
> +#else
> +#define mq_init_ns(ns)
> +#endif

Do all these empty suckers need do{}while(0)'s?

> +#if defined(CONFIG_IPC_NS)
> extern void free_ipc_ns(struct kref *kref);
> extern struct ipc_namespace *copy_ipcs(unsigned long flags,
> struct ipc_namespace *ns);
> diff --git a/init/Kconfig b/init/Kconfig
> index ce75d2d..32c6315 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -489,10 +489,10 @@ config UTS_NS
>
> config IPC_NS
> bool "IPC namespace"
> - depends on NAMESPACES && SYSVIPC
> + depends on NAMESPACES && (SYSVIPC || POSIX_MQUEUE)
> help
> In this namespace tasks work with IPC ids which correspond to
> - different IPC objects in different namespaces
> + different IPC objects in different namespaces.
>
> config USER_NS
> bool "User namespace (EXPERIMENTAL)"
> diff --git a/ipc/mqueue.c b/ipc/mqueue.c
> index d9393f8..01d64a0 100644
> --- a/ipc/mqueue.c
> +++ b/ipc/mqueue.c
> @@ -31,6 +31,7 @@
> #include <linux/mutex.h>
> #include <linux/nsproxy.h>
> #include <linux/pid.h>
> +#include <linux/ipc_namespace.h>
>
> #include <net/sock.h>
> #include "util.h"
> @@ -46,12 +47,6 @@
> #define STATE_PENDING 1
> #define STATE_READY 2
>
> -/* default values */
> -#define DFLT_QUEUESMAX 256 /* max number of message queues */
> -#define DFLT_MSGMAX 10 /* max number of messages in each queue */
> -#define HARD_MSGMAX (131072/sizeof(void*))
> -#define DFLT_MSGSIZEMAX 8192 /* max message size */
> -
> /*
> * Define the ranges various user-specified maximum values can
> * be set to.
> @@ -95,12 +90,6 @@ static void remove_notification(struct mqueue_inode_info *info);
>
> static spinlock_t mq_lock;
> static struct kmem_cache *mqueue_inode_cachep;
> -static struct vfsmount *mqueue_mnt;
> -
> -static unsigned int queues_count;
> -static unsigned int queues_max = DFLT_QUEUESMAX;
> -static unsigned int msg_max = DFLT_MSGMAX;
> -static unsigned int msgsize_max = DFLT_MSGSIZEMAX;
>
> static struct ctl_table_header * mq_sysctl_table;
>
> @@ -109,11 +98,25 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
> return container_of(inode, struct mqueue_inode_info, vfs_inode);
> }
>
> +void mq_init_ns(struct ipc_namespace *ns) {
> + ns->mq_queues_count = 0;
> + ns->mq_queues_max = DFLT_QUEUESMAX;
> + ns->mq_msg_max = DFLT_MSGMAX;
> + ns->mq_msgsize_max = DFLT_MSGSIZEMAX;
> + ns->mq_mnt = mntget(init_ipc_ns.mq_mnt);
> +}

Heh, I read that as a structure definition at first! Could you put that
bracket on a new line.

This part of the patch is nice. The -'s are next to the +'s and it is
easy to audit.

The rest of the patch looks good. Mostly simple substitutions, and it
is pretty obvious what is going on.

-- Dave

2008-12-17 18:52:55

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [PATCH 1/2] mqueue ns: move mqueue_mnt into struct ipc_namespace

Quoting Dave Hansen ([email protected]):
> On Wed, 2008-12-17 at 11:55 -0600, Serge E. Hallyn wrote:

Thanks for taking look.

...
> > -#if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS)
> > +#ifdef CONFIG_POSIX_MQUEUE
> > +extern void mq_init_ns(struct ipc_namespace *ns);
> > +/* default values */
> > +#define DFLT_QUEUESMAX 256 /* max number of message queues */
> > +#define DFLT_MSGMAX 10 /* max number of messages in each queue */
> > +#define HARD_MSGMAX (131072/sizeof(void *))
> > +#define DFLT_MSGSIZEMAX 8192 /* max message size */
> > +#else
> > +#define mq_init_ns(ns)
> > +#endif
>
> Do all these empty suckers need do{}while(0)'s?

Couldn't hurt for future-proofing. Will do, thanks.

> > @@ -109,11 +98,25 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
> > return container_of(inode, struct mqueue_inode_info, vfs_inode);
> > }
> >
> > +void mq_init_ns(struct ipc_namespace *ns) {
> > + ns->mq_queues_count = 0;
> > + ns->mq_queues_max = DFLT_QUEUESMAX;
> > + ns->mq_msg_max = DFLT_MSGMAX;
> > + ns->mq_msgsize_max = DFLT_MSGSIZEMAX;
> > + ns->mq_mnt = mntget(init_ipc_ns.mq_mnt);
> > +}
>
> Heh, I read that as a structure definition at first! Could you put that
> bracket on a new line.

That's actually taken care of in the followup patch - same with
mq_exit_ns. Sorry about that. (Still I'd rather not bother with
the patch flux to change it just in this patch, since whenever
I do that I inevitably mess up something else)

> This part of the patch is nice. The -'s are next to the +'s and it is
> easy to audit.
>
> The rest of the patch looks good. Mostly simple substitutions, and it
> is pretty obvious what is going on.

Thanks. I'm hoping that Pavel and Eric will give their input and
maybe we can put this thing to bed :)

-serge

2008-12-17 18:56:20

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH 1/2] mqueue ns: move mqueue_mnt into struct ipc_namespace

On Wed, 2008-12-17 at 12:52 -0600, Serge E. Hallyn wrote:
> > > +void mq_init_ns(struct ipc_namespace *ns) {
> > > + ns->mq_queues_count = 0;
> > > + ns->mq_queues_max = DFLT_QUEUESMAX;
> > > + ns->mq_msg_max = DFLT_MSGMAX;
> > > + ns->mq_msgsize_max = DFLT_MSGSIZEMAX;
> > > + ns->mq_mnt = mntget(init_ipc_ns.mq_mnt);
> > > +}
> >
> > Heh, I read that as a structure definition at first! Could you put
> that
> > bracket on a new line.
>
> That's actually taken care of in the followup patch - same with
> mq_exit_ns. Sorry about that. (Still I'd rather not bother with
> the patch flux to change it just in this patch, since whenever
> I do that I inevitably mess up something else)

Ahh, ok. I guess it is OK since it is temporary.

-- Dave

2008-12-17 18:55:27

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH 2/2] ipc namespaces: implement support for posix msqueues

On Wed, 2008-12-17 at 11:55 -0600, Serge E. Hallyn wrote:
> -void free_ipc_ns(struct kref *kref)
> +void put_ipc_ns(struct ipc_namespace *ns)
> {
> - struct ipc_namespace *ns;
> + if (ns && atomic_dec_and_lock(&ns->count, &mq_lock)) {
> + mq_clear_sbinfo(ns);
> + spin_unlock(&mq_lock);
> + mq_put_mnt(ns);
> + free_ipc_ns(ns);
> + }
> +}

OK, I see now why you wanted to remove the kref and add an atomic_t.
That's probably worth a sentence in the patch description.

I know that atomic_dec_and_lock() can be a nice logical thing to do, but
is it necessary here? Basically, is there something that happens under
mq_lock that can re-raise the ns->count?

-- Dave

2008-12-17 19:09:22

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [PATCH 2/2] ipc namespaces: implement support for posix msqueues

Quoting Dave Hansen ([email protected]):
> On Wed, 2008-12-17 at 11:55 -0600, Serge E. Hallyn wrote:
> > -void free_ipc_ns(struct kref *kref)
> > +void put_ipc_ns(struct ipc_namespace *ns)
> > {
> > - struct ipc_namespace *ns;
> > + if (ns && atomic_dec_and_lock(&ns->count, &mq_lock)) {
> > + mq_clear_sbinfo(ns);
> > + spin_unlock(&mq_lock);
> > + mq_put_mnt(ns);
> > + free_ipc_ns(ns);
> > + }
> > +}
>
> OK, I see now why you wanted to remove the kref and add an atomic_t.
> That's probably worth a sentence in the patch description.

Good point, will do.

> I know that atomic_dec_and_lock() can be a nice logical thing to do, but
> is it necessary here? Basically, is there something that happens under
> mq_lock that can re-raise the ns->count?

A racing task from another ipc namespace can open a mq file through
the VFS and thereby bump the ipc's refcount. Will comment that here and
in the patch description.

thanks,
-serge

2008-12-17 21:26:41

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: Re: [PATCH 2/2] ipc namespaces: implement support for posix msqueues

Just a couple of nits.

Serge E. Hallyn [[email protected]] wrote:
| Implement multiple mounts of the mqueue file system, and
| link it to usage of CLONE_NEWIPC.
|
| Each ipc ns has a corresponding mqueuefs superblock. When
| a user does clone(CLONE_NEWIPC) or unshare(CLONE_NEWIPC), the
| unshare will cause an internal mount of a new mqueuefs sb
| linked to the new ipc ns.
|
| When a user does 'mount -t mqueue mqueue /dev/mqueue', he
| mounts the mqueuefs superblock.
|
| Posix message queues can be worked with both through the
| mq_* system calls (see mq_overview(7)), and through the VFS
| through the mqueue mount. Any usage of mq_open() and friends
| will work with the acting task's ipc namespace. Any actions
| through the VFS will work with the mqueuefs in which the
| file was created. So if a user doesn't remount mqueuefs
| after unshare(CLONE_NEWIPC), mq_open("/ab") will not be
| reflected in "ls /dev/mqueue".
|
| If task a mounts mqueue for ipc_ns:1, then clones task b with
| a new ipcns, ipcns:2, and then task a is the last task in
| ipc_ns:1 to exit, then (1) ipc_ns:1 will be freed, (2) it's
| superblock will live on until task b umounts the corresponding
| mqueuefs, and vfs actions will continue to succeed, but (3)
| sb->s_fs_info will be NULL for the sb corresponding to the
| deceased ipc_ns:1.
|
| Changelog:
| Dec 17: removed unused static fn (get_ipcns_from_sb)
|
| Signed-off-by: Cedric Le Goater <[email protected]>
| Signed-off-by: Serge E. Hallyn <[email protected]>
| ---
| include/linux/ipc_namespace.h | 16 ++---
| ipc/mqueue.c | 140 ++++++++++++++++++++++++++++++++---------
| ipc/msgutil.c | 8 +--
| ipc/namespace.c | 25 ++++++--
| ipc/util.h | 6 +-
| 5 files changed, 144 insertions(+), 51 deletions(-)
|
| diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
| index 532598f..74f1ae2 100644
| --- a/include/linux/ipc_namespace.h
| +++ b/include/linux/ipc_namespace.h
| @@ -25,7 +25,7 @@ struct ipc_ids {
| };
|
| struct ipc_namespace {
| - struct kref kref;
| + atomic_t count;
| struct ipc_ids ids[3];
|
| int sem_ctls[4];
| @@ -56,6 +56,7 @@ struct ipc_namespace {
| extern struct ipc_namespace init_ipc_ns;
| extern atomic_t nr_ipc_ns;
|
| +extern spinlock_t mq_lock;
| #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
| #define INIT_IPC_NS(ns) .ns = &init_ipc_ns,
| #else
| @@ -75,18 +76,18 @@ extern int ipcns_notify(unsigned long);
| #endif /* CONFIG_SYSVIPC */
|
| #ifdef CONFIG_POSIX_MQUEUE
| -extern void mq_init_ns(struct ipc_namespace *ns);
| +extern int mq_init_ns(struct ipc_namespace *ns);
| /* default values */
| #define DFLT_QUEUESMAX 256 /* max number of message queues */
| #define DFLT_MSGMAX 10 /* max number of messages in each queue */
| #define HARD_MSGMAX (131072/sizeof(void *))
| #define DFLT_MSGSIZEMAX 8192 /* max message size */
| #else
| -#define mq_init_ns(ns)
| +#define mq_init_ns(ns) (0)
| #endif
|
| #if defined(CONFIG_IPC_NS)
| -extern void free_ipc_ns(struct kref *kref);
| +extern void free_ipc_ns(struct ipc_namespace *ns);
| extern struct ipc_namespace *copy_ipcs(unsigned long flags,
| struct ipc_namespace *ns);
| extern void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
| @@ -96,14 +97,11 @@ extern void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
| static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns)
| {
| if (ns)
| - kref_get(&ns->kref);
| + atomic_inc(&ns->count);
| return ns;
| }
|
| -static inline void put_ipc_ns(struct ipc_namespace *ns)
| -{
| - kref_put(&ns->kref, free_ipc_ns);
| -}
| +extern void put_ipc_ns(struct ipc_namespace *ns);
| #else
| static inline struct ipc_namespace *copy_ipcs(unsigned long flags,
| struct ipc_namespace *ns)
| diff --git a/ipc/mqueue.c b/ipc/mqueue.c
| index 01d64a0..6b235c1 100644
| --- a/ipc/mqueue.c
| +++ b/ipc/mqueue.c
| @@ -88,7 +88,6 @@ static const struct file_operations mqueue_file_operations;
| static struct super_operations mqueue_super_ops;
| static void remove_notification(struct mqueue_inode_info *info);
|
| -static spinlock_t mq_lock;
| static struct kmem_cache *mqueue_inode_cachep;
|
| static struct ctl_table_header * mq_sysctl_table;
| @@ -98,25 +97,30 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
| return container_of(inode, struct mqueue_inode_info, vfs_inode);
| }
|
| -void mq_init_ns(struct ipc_namespace *ns) {
| - ns->mq_queues_count = 0;
| - ns->mq_queues_max = DFLT_QUEUESMAX;
| - ns->mq_msg_max = DFLT_MSGMAX;
| - ns->mq_msgsize_max = DFLT_MSGSIZEMAX;
| - ns->mq_mnt = mntget(init_ipc_ns.mq_mnt);
| +/*
| + * This routine should be called with the mq_lock held.
| + */
| +static inline struct ipc_namespace *__get_ns_from_ino(struct inode *inode)

Nit. Lot of places in kernel use 'ino' to represent the numeric inode number.
Maybe better to name get_ns_from_inode().

| +{
| + return get_ipc_ns(inode->i_sb->s_fs_info);
| }
|
| -void mq_exit_ns(struct ipc_namespace *ns) {
| - /* will need to clear out ns->mq_mnt->mnt_sb->s_fs_info here */
| - mntput(ns->mq_mnt);
| +static inline struct ipc_namespace *get_ns_from_ino(struct inode *inode)


| +{
| + struct ipc_namespace *ns;
| +
| + spin_lock(&mq_lock);
| + ns = __get_ns_from_ino(inode);
| + spin_unlock(&mq_lock);
| + return ns;
| }
|
| -static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
| - struct mq_attr *attr)
| +static struct inode *mqueue_get_inode(struct super_block *sb,
| + struct ipc_namespace *ipc_ns, int mode,
| + struct mq_attr *attr)
| {
| struct user_struct *u = current_user();
| struct inode *inode;
| - struct ipc_namespace *ipc_ns = &init_ipc_ns;
|
| inode = new_inode(sb);
| if (inode) {
| @@ -192,30 +196,76 @@ out_inode:
| static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
| {
| struct inode *inode;
| + struct ipc_namespace *ns = data;
| + int error = 0;
|
| sb->s_blocksize = PAGE_CACHE_SIZE;
| sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
| sb->s_magic = MQUEUE_MAGIC;
| sb->s_op = &mqueue_super_ops;
|
| - inode = mqueue_get_inode(sb, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL);
| - if (!inode)
| - return -ENOMEM;
| + inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO,
| + NULL);
| + if (!inode) {
| + error = -ENOMEM;
| + goto out;
| + }
|
| sb->s_root = d_alloc_root(inode);
| if (!sb->s_root) {
| iput(inode);
| - return -ENOMEM;
| + error = -ENOMEM;
| }
|
| - return 0;
| +out:
| + return error;
| +}
| +
| +static int compare_sb_single_ns(struct super_block *sb, void *data)
| +{
| + return sb->s_fs_info == data;
| +}
| +
| +static int set_sb_single_ns(struct super_block *sb, void *data)
| +{
| + sb->s_fs_info = data;
| + return set_anon_super(sb, NULL);
| +}
| +
| +static int get_sb_single_ns(struct file_system_type *fs_type,
| + int flags, void *data,
| + int (*fill_super)(struct super_block *, void *, int),
| + struct vfsmount *mnt)
| +{
| + struct super_block *s;
| + int error;
| +
| + s = sget(fs_type, compare_sb_single_ns, set_sb_single_ns, data);
| + if (IS_ERR(s))
| + return PTR_ERR(s);
| + if (!s->s_root) {
| + s->s_flags = flags;
| + error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
| + if (error) {
| + up_write(&s->s_umount);
| + deactivate_super(s);
| + return error;
| + }
| + s->s_flags |= MS_ACTIVE;
| + }
| + do_remount_sb(s, flags, data, 0);
| + return simple_set_mnt(mnt, s);
| }
|
| static int mqueue_get_sb(struct file_system_type *fs_type,
| int flags, const char *dev_name,
| void *data, struct vfsmount *mnt)
| {
| - return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt);
| + if (flags & MS_KERNMOUNT)
| + return get_sb_single_ns(fs_type, flags, data,
| + mqueue_fill_super, mnt);
| + return get_sb_single_ns(fs_type, flags, current->nsproxy->ipc_ns,
| + mqueue_fill_super, mnt);

nit. This maybe easier to read with an ns local variable and a single
call to get_sb_single_ns().

Suka

2008-12-18 04:45:30

by Subrata Modak1

[permalink] [raw]
Subject: Re: [LTP PATCH 1/4] posix message queue namespaces: first test

> (Subrata, I'm sending these testcases now so people can use them,
> but they should not yet be considered for integration into ltp

Thanks Serge.

> of course, and when they are, the kernel version test will need
> to be switched to at least 2.6.29)

That should be absolutely fine.

Regards--
Subrata

> Add the first test for posix message queue namespaces, plus a
> test to detect whether they are enabled.

> Note that the kernel version check is currently bogus - these are
> not yet enabled in 2.6.28. But a fella's gotta test.

> Based on older version by Nadia Derbey.
> Signed-off-by: Nadia Derbey <[email protected]>

> Signed-off-by: Serge Hallyn <[email protected]>
> ---
> testcases/kernel/containers/Makefile | 2 +-
> testcases/kernel/containers/README | 4 +
> testcases/kernel/containers/container_test.sh | 9 ++
> testcases/kernel/containers/mqns/Makefile | 41 +++++++
> .../kernel/containers/mqns/check_mqns_enabled.c | 65 ++++++++++
> testcases/kernel/containers/mqns/mqns.h | 13 ++
> testcases/kernel/containers/mqns/mqns_01.c | 123
++++++++++++++++++++
> testcases/kernel/containers/mqns/runmqnstest.sh | 39 ++++++
> 8 files changed, 295 insertions(+), 1 deletions(-)
> create mode 100644 testcases/kernel/containers/mqns/Makefile
> create mode 100644 testcases/kernel/containers/mqns/check_mqns_enabled.c
> create mode 100644 testcases/kernel/containers/mqns/mqns.h
> create mode 100644 testcases/kernel/containers/mqns/mqns_01.c
> create mode 100644 testcases/kernel/containers/mqns/runmqnstest.sh
>
> diff --git a/testcases/kernel/containers/Makefile b/testcases/
> kernel/containers/Makefile
> index d2d143d..b1b6cdc 100644
> --- a/testcases/kernel/containers/Makefile
> +++ b/testcases/kernel/containers/Makefile
> @@ -18,7 +18,7 @@
> ##
##
>
################################################################################
>
> -SUBDIRS := libclone utsname sysvipc pidns netns
> +SUBDIRS := libclone utsname sysvipc pidns netns mqns

> all: check_for_unshare
> @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done
> diff --git a/testcases/kernel/containers/README b/testcases/kernel/
> containers/README
> index f1e485b..fd0898f 100644
> --- a/testcases/kernel/containers/README
> +++ b/testcases/kernel/containers/README
> @@ -37,6 +37,10 @@ each functionality README file for detail:

> sysvipc/*
> Contains all the testcases related to IPC NS tests.
> +posixmq/*
> + Contains all the testcases related to POSIX MQ NS tests. These
> + are strictly speaking a part of the ipc namespaces, but can be
> + enabled in the kernel without SYSV IPC support.
> utsname/*
> Contains all the testcases related to utsname tests.
> libclone/*
> diff --git a/testcases/kernel/containers/container_test.sh b/
> testcases/kernel/containers/container_test.sh
> index 911a6a4..755e910 100755
> --- a/testcases/kernel/containers/container_test.sh
> +++ b/testcases/kernel/containers/container_test.sh
> @@ -54,6 +54,15 @@ else
> echo "Process id namespaces not enabled in kernel. Not running pidns
tests."
> fi
>
> +check_mqns_enabled
> +if [ $? -eq 0 ]; then
> + echo "Running POSIX message queue tests."
> + runmqnstest.sh
> +else
> + echo "Posix message queues or ipc namespaces not enabled in kernel."
> + echo "Not running mqns tests."
> +fi
> +
> check_netns_enabled
> if [ $? -eq 0 ]; then
> echo "Running netns tests."
> diff --git a/testcases/kernel/containers/mqns/Makefile b/testcases/
> kernel/containers/mqns/Makefile
> new file mode 100644
> index 0000000..ec6ba14
> --- /dev/null
> +++ b/testcases/kernel/containers/mqns/Makefile
> @@ -0,0 +1,41 @@
>
+################################################################################

> +##
##
> +## Copyright (c) International Business Machines Corp., 2007
##
> +##
##
> +## This program is free software; you can redistribute it and#or
> modify ##
> +## it under the terms of the GNU General Public License as
> published by ##
> +## the Free Software Foundation; either version 2 of the License,
> or ##
> +## (at your option) any later version.
##
> +##
##
> +## This program is distributed in the hope that it will be useful,
> but ##
> +## WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY ##
> +## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> License ##
> +## for more details.
##
> +##
##
> +## You should have received a copy of the GNU General Public
> License ##
> +## along with this program; if not, write to the Free Software
##
> +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> 02111-1307 USA ##
> +##
##
>
+################################################################################

> +
> +CFLAGS += -Wall
> +CPPFLAGS += -I../../../../include -I../libclone
> +LDLIBS += -L../../../../lib -L../libclone ../libclone/libclone.a -lltp
-lrt
> +
> +SRCS := $(wildcard *.c)
> +TARGETS := $(SRCS:%.c=%)
> +
> +HAS_UNSHARE ?= $(shell ../check_for_unshare && echo y)
> +ifneq ($(HAS_UNSHARE),y)
> +TARGETS :=
> +endif
> +
> +all: $(TARGETS)
> +
> +clean:
> + rm -f $(TARGETS) *.o
> +
> +install:
> +ifeq ($(HAS_UNSHARE),y)
> + @set -e; for i in $(TARGETS) runmqnstest.sh check_mqns_enabled; do
> ln -f $$i ../../../bin/$$i ; chmod +x runmqnstest.sh ; done
> +endif
> diff --git a/testcases/kernel/containers/mqns/check_mqns_enabled.c
> b/testcases/kernel/containers/mqns/check_mqns_enabled.c
> new file mode 100644
> index 0000000..21d56a9
> --- /dev/null
> +++ b/testcases/kernel/containers/mqns/check_mqns_enabled.c
> @@ -0,0 +1,65 @@
> +/*
> +* Copyright (c) International Business Machines Corp., 2007
> +* This program is free software; you can redistribute it and/or modify
> +* it under the terms of the GNU General Public License as published by
> +* the Free Software Foundation; either version 2 of the License, or
> +* (at your option) any later version.
> +*
> +* This program is distributed in the hope that it will be useful,
> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
> +* the GNU General Public License for more details.
> +* You should have received a copy of the GNU General Public License
> +* along with this program; if not, write to the Free Software
> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
> +*
> +* Author: Serge Hallyn <[email protected]>
>
+***************************************************************************/

> +#include <sys/mount.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <stdio.h>
> +#include "../libclone/libclone.h"
> +#include "test.h"
> +#include "mqns.h"
> +
> +int dummy(void *v)
> +{
> + return 0;
> +}
> +
> +int main()
> +{
> + void *childstack, *stack;
> + int pid;
> + mqd_t mqd;
> +
> + if (tst_kvercmp(2,6,27) < 0)
> + return 1;
> +
> + mq_unlink("/checkmqnsenabled");
> + mqd = mq_open("/checkmqnsenabled", O_RDWR|O_CREAT|O_EXCL, 0777, NULL);
> + if (mqd == -1) {
> + perror("mq_open");
> + return 3;
> + }
> + mq_close(mqd);
> + mq_unlink("/checkmqnsenabled");
> +
> + stack = malloc(getpagesize());
> + if (!stack) {
> + perror("malloc");
> + return 4;
> + }
> + childstack = stack + getpagesize();
> +#ifdef __ia64__
> + pid = clone2(dummy, childstack, getpagesize(), CLONE_NEWIPC, NULL,
> NULL, NULL, NULL);
> +#else
> + pid = clone(dummy, childstack, CLONE_NEWIPC, NULL);
> +#endif
> +
> + if (pid == -1)
> + return 5;
> +
> + return 0;
> +}
> diff --git a/testcases/kernel/containers/mqns/mqns.h b/testcases/
> kernel/containers/mqns/mqns.h
> new file mode 100644
> index 0000000..df8fc4b
> --- /dev/null
> +++ b/testcases/kernel/containers/mqns/mqns.h
> @@ -0,0 +1,13 @@
> +#ifndef __MQNS_H
> +#define __MQNS_H
> +
> +#include <sys/mount.h>
> +#include <mqueue.h>
> +#include <test.h>
> +#include <libclone.h>
> +
> +
> +#define DEV_MQUEUE "/dev/mqueue"
> +#define SLASH_MQ1 "/MQ1"
> +
> +#endif /* __MQNS_H */
> diff --git a/testcases/kernel/containers/mqns/mqns_01.c b/testcases/
> kernel/containers/mqns/mqns_01.c
> new file mode 100644
> index 0000000..7cf5482
> --- /dev/null
> +++ b/testcases/kernel/containers/mqns/mqns_01.c
> @@ -0,0 +1,123 @@
> +/*
> +* Copyright (c) International Business Machines Corp., 2007
> +* This program is free software; you can redistribute it and/or modify
> +* it under the terms of the GNU General Public License as published by
> +* the Free Software Foundation; either version 2 of the License, or
> +* (at your option) any later version.
> +*
> +* This program is distributed in the hope that it will be useful,
> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
> +* the GNU General Public License for more details.
> +* You should have received a copy of the GNU General Public License
> +* along with this program; if not, write to the Free Software
> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
> +*
> +* Author: Nadia Derbey <[email protected]>
> +*
> +* Check mqns isolation: father mqns cannot be accessed from newinstance
> +*
> +* Mount mqueue fs
> +* Create a posix mq -->mq1
> +* unshare
> +* In unshared process:
> +* Mount newinstance mqueuefs
> +* Check that mq1 is not readable from new ns
> +
>
+***************************************************************************/

> +
> +#define _GNU_SOURCE 1
> +#include <sys/wait.h>
> +#include <assert.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <errno.h>
> +#include "mqns.h"
> +
> +char *TCID = "posixmq_namespace_01";
> +int TST_TOTAL=1;
> +
> +int p1[2];
> +int p2[2];
> +
> +int check_mqueue(void *vtest)
> +{
> + char buf[3];
> + mqd_t mqd;
> +
> + close(p1[1]);
> + close(p2[0]);
> +
> + read(p1[0], buf, 3);
> + mqd = mq_open(SLASH_MQ1, O_RDONLY);
> + if (mqd == -1) {
> + write(p2[1], "notfnd", 7);
> + } else {
> + write(p2[1], "exists", 7);
> + mq_close(mqd);
> + }
> + tst_exit(0);
> +
> + /* NOT REACHED */
> + return 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + int r;
> + mqd_t mqd;
> + char buf[7];
> + int use_clone = T_UNSHARE;
> +
> + if (argc == 2 && strcmp(argv[1], "-clone") == 0) {
> + tst_resm(TINFO, "Testing posix mq namespaces through clone(2).\n");
> + use_clone = T_CLONE;
> + } else
> + tst_resm(TINFO, "Testing posix mq namespaces through unshare(2).\n");
> +
> + if (pipe(p1) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
> + if (pipe(p2) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
> +
> + mqd = mq_open(SLASH_MQ1, O_RDWR|O_CREAT|O_EXCL, 0777, NULL);
> + if (mqd == -1) {
> + perror("mq_open");
> + tst_resm(TFAIL, "mq_open failed\n");
> + tst_exit(3);
> + }
> +
> + tst_resm(TINFO, "Checking namespaces isolation from parent to
child\n");
> + /* fire off the test */
> + r = do_clone_unshare_test(use_clone, CLONE_NEWIPC, check_mqueue, NULL);
> + if (r < 0) {
> + tst_resm(TFAIL, "failed clone/unshare\n");
> + mq_close(mqd);
> + mq_unlink(SLASH_MQ1);
> + tst_exit(1);
> + }
> +
> + close(p1[0]);
> + close(p2[1]);
> + write(p1[1], "go", 3);
> + read(p2[0], buf, 7);
> + if (!strcmp(buf, "exists")) {
> + tst_resm(TFAIL, "child process found mqueue\n");
> + r = TFAIL;
> + } else if (!strcmp(buf, "notfnd")) {
> + tst_resm(TPASS, "child process didn't find mqueue\n");
> + r = TPASS;
> + } else {
> + tst_resm(TFAIL, "UNKNOWN RESULT\n");
> + r = TFAIL;
> + }
> +
> + /* destroy the mqueue */
> + mq_close(mqd);
> + mq_unlink(SLASH_MQ1);
> +
> + tst_exit(r);
> +
> + /* NOT REACHED */
> + return 0;
> +}
> diff --git a/testcases/kernel/containers/mqns/runmqnstest.sh b/
> testcases/kernel/containers/mqns/runmqnstest.sh
> new file mode 100644
> index 0000000..87c712e
> --- /dev/null
> +++ b/testcases/kernel/containers/mqns/runmqnstest.sh
> @@ -0,0 +1,39 @@
> +#!/bin/sh
>
+################################################################################

> +##
##
> +## Copyright (c) International Business Machines Corp., 2007
##
> +##
##
> +## This program is free software; you can redistribute it and#or
> modify ##
> +## it under the terms of the GNU General Public License as
> published by ##
> +## the Free Software Foundation; either version 2 of the License,
> or ##
> +## (at your option) any later version.
##
> +##
##
> +## This program is distributed in the hope that it will be useful,
> but ##
> +## WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY ##
> +## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> License ##
> +## for more details.
##
> +##
##
> +## You should have received a copy of the GNU General Public
> License ##
> +## along with this program; if not, write to the Free Software
##
> +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> 02111-1307 USA ##
> +##
##
>
+################################################################################

> +
> +exit_code=0
> +tests_list='mqns_01'
> +
> +for t in $tests_list
> +do
> + $t
> + if [ $? -ne 0 ]; then
> + exit_code="$?"
> + exit $exit_code
> + fi
> + $t -clone
> + if [ $? -ne 0 ]; then
> + exit_code="$?"
> + exit $exit_code
> + fi
> +done
> +
> +exit $exit_code
> --
> 1.6.0.5