[RHEL8,COMMIT] kernfs/sysfs: add ioctl to get fd network namespace tag

Submitted by Konstantin Khorenko on July 24, 2020, 4:39 p.m.

Details

Message ID 202007241639.06OGdEvC001590@finist-co8.sw.ru
State New
Series "kernfs/sysfs: add ioctl to get fd network namespace tag"
Headers show

Commit Message

Konstantin Khorenko July 24, 2020, 4:39 p.m.
The commit is pushed to "branch-rh8-4.18.0-193.6.3.vz8.4.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-193.6.3.vz8.4.4
------>
commit d53f348850ab12a9c6b42b1c23be147a08ac9357
Author: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Date:   Fri Jul 24 19:38:23 2020 +0300

    kernfs/sysfs: add ioctl to get fd network namespace tag
    
    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(+)

Patch hide | download patch | download mbox

diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 66d5ae4ab79e..86e3e1779c74 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1715,4 +1715,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,
 };
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 5a5c2eb884f0..e67c23302164 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -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"
 
@@ -960,6 +964,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,
@@ -969,6 +991,7 @@  const struct file_operations kernfs_file_fops = {
 	.release	= kernfs_fop_release,
 	.poll		= kernfs_fop_poll,
 	.fsync		= noop_fsync,
+	.unlocked_ioctl = kernfs_ioctl,
 };
 
 /**
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index b300e93177ca..64ed3d55e958 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -122,6 +122,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
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 9e125bcf2164..f2bfd9d4d42f 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -17,6 +17,7 @@ 
 #include <linux/atomic.h>
 #include <linux/uidgid.h>
 #include <linux/wait.h>
+#include <linux/ioctl.h>
 
 #include <linux/rh_kabi.h>
 
@@ -585,4 +586,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 */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index dc27d3cbf1c9..0f1a2a011641 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -397,4 +397,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 */
diff --git a/net/socket.c b/net/socket.c
index 22b8357d8e75..f2e1676dd4b9 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1079,6 +1079,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;