@@ -1690,4 +1690,5 @@ const struct file_operations kernfs_dir_fops = {
.iterate_shared = kernfs_fop_readdir,
.release = kernfs_dir_fop_release,
.llseek = generic_file_llseek,
+ .unlocked_ioctl = kernfs_ioctl,
};
@@ -15,6 +15,10 @@
#include <linux/pagemap.h>
#include <linux/sched/mm.h>
#include <linux/fsnotify.h>
+#include <linux/proc_ns.h>
+#include <linux/magic.h>
+#include <linux/socket.h>
+#include <net/net_namespace.h>
#include "kernfs-internal.h"
@@ -949,6 +953,24 @@ void kernfs_notify(struct kernfs_node *kn)
}
EXPORT_SYMBOL_GPL(kernfs_notify);
+long kernfs_ioctl(struct file *file, unsigned int ioctl,
+ unsigned long arg)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ const void *ns = kernfs_info(dentry->d_sb)->ns;
+ struct net *net;
+
+ switch (ioctl) {
+ case KERNFS_GET_NS:
+ if (dentry->d_sb->s_magic != SYSFS_MAGIC || !ns)
+ return -ENOTTY;
+ net = (struct net *)ns;
+ return open_related_ns(&net->ns, maybe_get_net_ns);
+ default:
+ return -ENOTTY;
+ }
+}
+
const struct file_operations kernfs_file_fops = {
.read = kernfs_fop_read,
.write = kernfs_fop_write,
@@ -958,6 +980,7 @@ const struct file_operations kernfs_file_fops = {
.release = kernfs_fop_release,
.poll = kernfs_fop_poll,
.fsync = noop_fsync,
+ .unlocked_ioctl = kernfs_ioctl,
};
/**
@@ -118,6 +118,7 @@ struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root,
extern const struct file_operations kernfs_file_fops;
void kernfs_drain_open_files(struct kernfs_node *kn);
+long kernfs_ioctl(struct file *file, unsigned int ioctl, unsigned long arg);
/*
* symlink.c
@@ -16,6 +16,7 @@
#include <linux/rbtree.h>
#include <linux/atomic.h>
#include <linux/wait.h>
+#include <linux/ioctl.h>
#include <linux/rh_kabi.h>
@@ -558,4 +559,8 @@ kernfs_mount(struct file_system_type *fs_type, int flags,
magic, new_sb_created, NULL);
}
+#define KERNIO 0xb8
+
+#define KERNFS_GET_NS _IO(KERNIO, 0x1)
+
#endif /* __LINUX_KERNFS_H */
@@ -386,4 +386,5 @@ extern int __sys_socketpair(int family, int type, int protocol,
extern int __sys_shutdown(int fd, int how);
extern struct ns_common *get_net_ns(struct ns_common *ns);
+extern struct ns_common *maybe_get_net_ns(struct ns_common *ns);
#endif /* _LINUX_SOCKET_H */
@@ -1011,6 +1011,18 @@ struct ns_common *get_net_ns(struct ns_common *ns)
}
EXPORT_SYMBOL_GPL(get_net_ns);
+struct ns_common *maybe_get_net_ns(struct ns_common *ns)
+{
+ struct net *net;
+
+ net = maybe_get_net(container_of(ns, struct net, ns));
+ if (!net)
+ return ERR_PTR(-ENXIO);
+
+ return &net->ns;
+}
+EXPORT_SYMBOL_GPL(maybe_get_net_ns);
+
static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
struct socket *sock;
Sysfs mounts save current netns when mounting and show different set of network devices in /sys/class/net based on it. But there is currently no simple way to find out to that netns the sysfs mount is tagged to. Lets add an ioctl so that we can get open file descriptor to this namespace on nsfs. Next we would be able to readlink this fd to get network namespace id. Sysfs holds only "passive" reference to the net namespace, so the only guaranty we have is that struct net is not freed yet. So lets add a helper maybe_get_net_ns to get net only if it's fully alive. https://jira.sw.ru/browse/PSBM-105161 Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com> --- fs/kernfs/dir.c | 1 + fs/kernfs/file.c | 23 +++++++++++++++++++++++ fs/kernfs/kernfs-internal.h | 1 + include/linux/kernfs.h | 5 +++++ include/linux/socket.h | 1 + net/socket.c | 12 ++++++++++++ 6 files changed, 43 insertions(+)