summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/idr.c2
-rw-r--r--lib/kobject.c115
-rw-r--r--lib/kobject_uevent.c109
-rw-r--r--lib/kref.c15
4 files changed, 215 insertions, 26 deletions
diff --git a/lib/idr.c b/lib/idr.c
index 2eb1dca..422a9d5 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -623,7 +623,7 @@ void *idr_get_next(struct idr *idp, int *nextidp)
}
return NULL;
}
-
+EXPORT_SYMBOL(idr_get_next);
/**
diff --git a/lib/kobject.c b/lib/kobject.c
index 8115eb1..f07c572 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -850,6 +850,121 @@ struct kset *kset_create_and_add(const char *name,
}
EXPORT_SYMBOL_GPL(kset_create_and_add);
+
+static DEFINE_SPINLOCK(kobj_ns_type_lock);
+static const struct kobj_ns_type_operations *kobj_ns_ops_tbl[KOBJ_NS_TYPES];
+
+int kobj_ns_type_register(const struct kobj_ns_type_operations *ops)
+{
+ enum kobj_ns_type type = ops->type;
+ int error;
+
+ spin_lock(&kobj_ns_type_lock);
+
+ error = -EINVAL;
+ if (type >= KOBJ_NS_TYPES)
+ goto out;
+
+ error = -EINVAL;
+ if (type <= KOBJ_NS_TYPE_NONE)
+ goto out;
+
+ error = -EBUSY;
+ if (kobj_ns_ops_tbl[type])
+ goto out;
+
+ error = 0;
+ kobj_ns_ops_tbl[type] = ops;
+
+out:
+ spin_unlock(&kobj_ns_type_lock);
+ return error;
+}
+
+int kobj_ns_type_registered(enum kobj_ns_type type)
+{
+ int registered = 0;
+
+ spin_lock(&kobj_ns_type_lock);
+ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES))
+ registered = kobj_ns_ops_tbl[type] != NULL;
+ spin_unlock(&kobj_ns_type_lock);
+
+ return registered;
+}
+
+const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent)
+{
+ const struct kobj_ns_type_operations *ops = NULL;
+
+ if (parent && parent->ktype->child_ns_type)
+ ops = parent->ktype->child_ns_type(parent);
+
+ return ops;
+}
+
+const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj)
+{
+ return kobj_child_ns_ops(kobj->parent);
+}
+
+
+const void *kobj_ns_current(enum kobj_ns_type type)
+{
+ const void *ns = NULL;
+
+ spin_lock(&kobj_ns_type_lock);
+ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
+ kobj_ns_ops_tbl[type])
+ ns = kobj_ns_ops_tbl[type]->current_ns();
+ spin_unlock(&kobj_ns_type_lock);
+
+ return ns;
+}
+
+const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk)
+{
+ const void *ns = NULL;
+
+ spin_lock(&kobj_ns_type_lock);
+ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
+ kobj_ns_ops_tbl[type])
+ ns = kobj_ns_ops_tbl[type]->netlink_ns(sk);
+ spin_unlock(&kobj_ns_type_lock);
+
+ return ns;
+}
+
+const void *kobj_ns_initial(enum kobj_ns_type type)
+{
+ const void *ns = NULL;
+
+ spin_lock(&kobj_ns_type_lock);
+ if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
+ kobj_ns_ops_tbl[type])
+ ns = kobj_ns_ops_tbl[type]->initial_ns();
+ spin_unlock(&kobj_ns_type_lock);
+
+ return ns;
+}
+
+/*
+ * kobj_ns_exit - invalidate a namespace tag
+ *
+ * @type: the namespace type (i.e. KOBJ_NS_TYPE_NET)
+ * @ns: the actual namespace being invalidated
+ *
+ * This is called when a tag is no longer valid. For instance,
+ * when a network namespace exits, it uses this helper to
+ * make sure no sb's sysfs_info points to the now-invalidated
+ * netns.
+ */
+void kobj_ns_exit(enum kobj_ns_type type, const void *ns)
+{
+ sysfs_exit_ns(type, ns);
+}
+
+
EXPORT_SYMBOL(kobject_get);
EXPORT_SYMBOL(kobject_put);
EXPORT_SYMBOL(kobject_del);
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 7b48d44..59c1551 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -19,18 +19,24 @@
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/slab.h>
-
+#include <linux/user_namespace.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
+#include <net/net_namespace.h>
u64 uevent_seqnum;
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
static DEFINE_SPINLOCK(sequence_lock);
-#if defined(CONFIG_NET)
-static struct sock *uevent_sock;
+#ifdef CONFIG_NET
+struct uevent_sock {
+ struct list_head list;
+ struct sock *sk;
+};
+static LIST_HEAD(uevent_sock_list);
+static DEFINE_MUTEX(uevent_sock_mutex);
#endif
/* the strings here must match the enum in include/linux/kobject.h */
@@ -77,6 +83,37 @@ out:
return ret;
}
+static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data)
+{
+ struct kobject *kobj = data;
+ const struct kobj_ns_type_operations *ops;
+
+ ops = kobj_ns_ops(kobj);
+ if (ops) {
+ const void *sock_ns, *ns;
+ ns = kobj->ktype->namespace(kobj);
+ sock_ns = ops->netlink_ns(dsk);
+ return sock_ns != ns;
+ }
+
+ return 0;
+}
+
+static int kobj_usermode_filter(struct kobject *kobj)
+{
+ const struct kobj_ns_type_operations *ops;
+
+ ops = kobj_ns_ops(kobj);
+ if (ops) {
+ const void *init_ns, *ns;
+ ns = kobj->ktype->namespace(kobj);
+ init_ns = ops->initial_ns();
+ return ns != init_ns;
+ }
+
+ return 0;
+}
+
/**
* kobject_uevent_env - send an uevent with environmental data
*
@@ -100,6 +137,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
u64 seq;
int i = 0;
int retval = 0;
+#ifdef CONFIG_NET
+ struct uevent_sock *ue_sk;
+#endif
pr_debug("kobject: '%s' (%p): %s\n",
kobject_name(kobj), kobj, __func__);
@@ -211,7 +251,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
#if defined(CONFIG_NET)
/* send netlink message */
- if (uevent_sock) {
+ mutex_lock(&uevent_sock_mutex);
+ list_for_each_entry(ue_sk, &uevent_sock_list, list) {
+ struct sock *uevent_sock = ue_sk->sk;
struct sk_buff *skb;
size_t len;
@@ -233,18 +275,21 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
}
NETLINK_CB(skb).dst_group = 1;
- retval = netlink_broadcast(uevent_sock, skb, 0, 1,
- GFP_KERNEL);
+ retval = netlink_broadcast_filtered(uevent_sock, skb,
+ 0, 1, GFP_KERNEL,
+ kobj_bcast_filter,
+ kobj);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS)
retval = 0;
} else
retval = -ENOMEM;
}
+ mutex_unlock(&uevent_sock_mutex);
#endif
/* call uevent_helper, usually only enabled during early boot */
- if (uevent_helper[0]) {
+ if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
char *argv [3];
argv [0] = uevent_helper;
@@ -320,18 +365,58 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
EXPORT_SYMBOL_GPL(add_uevent_var);
#if defined(CONFIG_NET)
-static int __init kobject_uevent_init(void)
+static int uevent_net_init(struct net *net)
{
- uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
- 1, NULL, NULL, THIS_MODULE);
- if (!uevent_sock) {
+ struct uevent_sock *ue_sk;
+
+ ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
+ if (!ue_sk)
+ return -ENOMEM;
+
+ ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
+ 1, NULL, NULL, THIS_MODULE);
+ if (!ue_sk->sk) {
printk(KERN_ERR
"kobject_uevent: unable to create netlink socket!\n");
return -ENODEV;
}
- netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
+ mutex_lock(&uevent_sock_mutex);
+ list_add_tail(&ue_sk->list, &uevent_sock_list);
+ mutex_unlock(&uevent_sock_mutex);
return 0;
}
+static void uevent_net_exit(struct net *net)
+{
+ struct uevent_sock *ue_sk;
+
+ mutex_lock(&uevent_sock_mutex);
+ list_for_each_entry(ue_sk, &uevent_sock_list, list) {
+ if (sock_net(ue_sk->sk) == net)
+ goto found;
+ }
+ mutex_unlock(&uevent_sock_mutex);
+ return;
+
+found:
+ list_del(&ue_sk->list);
+ mutex_unlock(&uevent_sock_mutex);
+
+ netlink_kernel_release(ue_sk->sk);
+ kfree(ue_sk);
+}
+
+static struct pernet_operations uevent_net_ops = {
+ .init = uevent_net_init,
+ .exit = uevent_net_exit,
+};
+
+static int __init kobject_uevent_init(void)
+{
+ netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
+ return register_pernet_subsys(&uevent_net_ops);
+}
+
+
postcore_initcall(kobject_uevent_init);
#endif
diff --git a/lib/kref.c b/lib/kref.c
index 6d19f69..d3d227a 100644
--- a/lib/kref.c
+++ b/lib/kref.c
@@ -16,23 +16,13 @@
#include <linux/slab.h>
/**
- * kref_set - initialize object and set refcount to requested number.
- * @kref: object in question.
- * @num: initial reference counter
- */
-void kref_set(struct kref *kref, int num)
-{
- atomic_set(&kref->refcount, num);
- smp_mb();
-}
-
-/**
* kref_init - initialize object.
* @kref: object in question.
*/
void kref_init(struct kref *kref)
{
- kref_set(kref, 1);
+ atomic_set(&kref->refcount, 1);
+ smp_mb();
}
/**
@@ -72,7 +62,6 @@ int kref_put(struct kref *kref, void (*release)(struct kref *kref))
return 0;
}
-EXPORT_SYMBOL(kref_set);
EXPORT_SYMBOL(kref_init);
EXPORT_SYMBOL(kref_get);
EXPORT_SYMBOL(kref_put);