#include <unistd.h>
+#ifdef HAVE_LINUX_NSFS_H
+# include <linux/nsfs.h>
+# if defined(NS_GET_NSTYPE)
+# define USE_NS_GET_API 1
+# include <sys/ioctl.h>
+# endif
+#endif
+#include <linux/sched.h>
+
#include "xalloc.h"
#include "nls.h"
#include "buffer.h"
.handle_fdinfo = file_handle_fdinfo,
.free_content = file_free_content,
};
+
+/*
+ * Regular files on NSFS
+ */
+
+struct nsfs_file {
+ struct file file;
+ int clone_type;
+};
+
+static const char *get_ns_type_name(int clone_type)
+{
+ switch (clone_type) {
+#ifdef USE_NS_GET_API
+ case CLONE_NEWNS:
+ return "mnt";
+ case CLONE_NEWCGROUP:
+ return "cgroup";
+ case CLONE_NEWUTS:
+ return "uts";
+ case CLONE_NEWIPC:
+ return "ipc";
+ case CLONE_NEWUSER:
+ return "user";
+ case CLONE_NEWPID:
+ return "pid";
+ case CLONE_NEWNET:
+ return "net";
+#ifdef CLONE_NEWTIME
+ case CLONE_NEWTIME:
+ return "time";
+#endif /* CLONE_NEWTIME */
+#endif /* USE_NS_GET_API */
+ default:
+ return "unknown";
+ }
+}
+
+static void init_nsfs_file_content(struct file *file)
+{
+ struct nsfs_file *nsfs_file = (struct nsfs_file *)file;
+ nsfs_file->clone_type = -1;
+
+#ifdef USE_NS_GET_API
+ char *proc_fname = NULL;
+ int ns_fd;
+ int ns_type;
+
+ if (is_association (file, NS_CGROUP))
+ nsfs_file->clone_type = CLONE_NEWCGROUP;
+ else if (is_association (file, NS_IPC))
+ nsfs_file->clone_type = CLONE_NEWIPC;
+ else if (is_association (file, NS_MNT))
+ nsfs_file->clone_type = CLONE_NEWNS;
+ else if (is_association (file, NS_NET))
+ nsfs_file->clone_type = CLONE_NEWNET;
+ else if (is_association (file, NS_PID)
+ || is_association (file, NS_PID4C))
+ nsfs_file->clone_type = CLONE_NEWPID;
+#ifdef CLONE_NEWTIME
+ else if (is_association (file, NS_TIME)
+ || is_association (file, NS_TIME4C))
+ nsfs_file->clone_type = CLONE_NEWTIME;
+#endif
+ else if (is_association (file, NS_USER))
+ nsfs_file->clone_type = CLONE_NEWUSER;
+ else if (is_association (file, NS_UTS))
+ nsfs_file->clone_type = CLONE_NEWUTS;
+
+ if (nsfs_file->clone_type != -1)
+ return;
+
+ if (!is_opened_file(file))
+ return;
+
+ if (!file->name)
+ return;
+
+ xasprintf(&proc_fname, "/proc/%d/fd/%d",
+ file->proc->pid, file->association);
+ ns_fd = open(proc_fname, O_RDONLY);
+ free(proc_fname);
+ if (ns_fd < 0)
+ return;
+
+ ns_type = ioctl(ns_fd, NS_GET_NSTYPE);
+ close(ns_fd);
+ if (ns_type < 0)
+ return;
+
+ nsfs_file->clone_type = ns_type;
+#endif /* USE_NS_GET_API */
+}
+
+
+static bool nsfs_file_fill_column(struct proc *proc __attribute__((__unused__)),
+ struct file *file,
+ struct libscols_line *ln,
+ int column_id,
+ size_t column_index)
+{
+ struct nsfs_file *nsfs_file = (struct nsfs_file *)file;
+ char *name = NULL;
+
+ if (nsfs_file->clone_type == -1)
+ return false;
+
+ switch (column_id) {
+ case COL_NS_NAME:
+ xasprintf(&name, "%s:[%llu]",
+ get_ns_type_name(nsfs_file->clone_type),
+ (unsigned long long)file->stat.st_ino);
+ break;
+ case COL_NS_TYPE:
+ if (scols_line_set_data(ln, column_index,
+ get_ns_type_name(nsfs_file->clone_type)))
+ err(EXIT_FAILURE, _("failed to add output data"));
+ return true;
+ default:
+ return false;
+ }
+
+ if (name && scols_line_refer_data(ln, column_index, name))
+ err(EXIT_FAILURE, _("failed to add output data"));
+
+ return true;
+}
+
+const struct file_class nsfs_file_class = {
+ .super = &file_class,
+ .size = sizeof(struct nsfs_file),
+ .initialize_class = NULL,
+ .finalize_class = NULL,
+ .initialize_content = init_nsfs_file_content,
+ .free_content = NULL,
+ .fill_column = nsfs_file_fill_column,
+ .handle_fdinfo = NULL,
+};
NLINK <``number``>::
Link count.
+NS.NAME <``string``>::
+Name (_NS.TYPE_:[_INODE_]) of the namespace specified with the file.
+
+NS.TYPE <``string``>::
+Type of the namespace specified with the file.
+The type is `mnt`, `cgroup`, `uts`, `ipc`, `user`, `pid`, `net`,
+`time`, or `unknown`.
+
OWNER <``string``>::
Owner of the file.
N_("name of the file (cooked)") },
[COL_NLINK] = { "NLINK", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
N_("link count") },
+ [COL_NS_NAME] = { "NS.NAME", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
+ N_("name of the namespace (NS.TYPE:[INODE])") },
+ [COL_NS_TYPE] = { "NS.TYPE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
+ N_("type of the namespace") },
[COL_OWNER] = { "OWNER", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("owner of the file") },
[COL_PARTITION]={ "PARTITION",0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
static const struct file_class *stat2class(struct stat *sb)
{
+ const char *fs;
+ dev_t dev;
+
assert(sb);
switch (sb->st_mode & S_IFMT) {
case S_IFIFO:
return &fifo_class;
case S_IFLNK:
- case S_IFREG:
case S_IFDIR:
+ return &file_class;
+ case S_IFREG:
+ dev = sb->st_dev;
+ if (major(dev) != 0)
+ return &file_class;
+
+ fs = get_nodev_filesystem(minor(dev));
+ if (fs && strcmp(fs, "nsfs") == 0)
+ return &nsfs_file_class;
+
return &file_class;
default:
break;
COL_MODE,
COL_NAME,
COL_NLINK,
+ COL_NS_NAME,
+ COL_NS_TYPE,
COL_PARTITION,
COL_PID,
COL_PIDFD_COMM,
struct ipc_class *(*get_ipc_class)(struct file *file);
};
-extern const struct file_class file_class, cdev_class, bdev_class, sock_class, unkn_class, fifo_class;
+extern const struct file_class file_class, cdev_class, bdev_class, sock_class, unkn_class, fifo_class,
+ nsfs_file_class;
/*
* IPC
--- /dev/null
+ net
+net:[4026531840] == net:[4026531840]: 0
+net:[4026531840] == net:[4026531840]: 0
+net:[4026531840] == net:[4026531840]: 0
#include <getopt.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
+#include <linux/sockios.h> /* SIOCGSKNS */
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
+#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/select.h>
return NULL;
}
+static void *make_netns(const struct factory *factory _U_, struct fdesc fdescs[],
+ int argc _U_, char ** argv _U_)
+{
+ int sd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sd < 0)
+ err(EXIT_FAILURE, "failed in socket()");
+
+ int ns = ioctl(sd, SIOCGSKNS);
+ if (ns < 0)
+ err(EXIT_FAILURE, "failed in ioctl(SIOCGSKNS)");
+ close(sd);
+
+ if (ns != fdescs[0].fd) {
+ if (dup2(ns, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(ns);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", ns, fdescs[0].fd);
+ }
+ close(ns);
+ }
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_fdesc,
+ .data = NULL
+ };
+
+ return NULL;
+}
+
#define PARAM_END { .name = NULL, }
static const struct factory factories[] = {
{
},
PARAM_END
}
- }
+ },
+ {
+ .name = "netns",
+ .desc = "open a file specifying a netns",
+ .priv = true,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_netns,
+ .params = (struct parameter []) {
+ PARAM_END
+ }
+ },
};
static int count_parameters(const struct factory *factory)
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2022 Masatake YAMATO <yamato@redhat.com>
+#
+# This file is part of util-linux.
+#
+# This file 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 file 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.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="netns associated with a fd"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+ts_skip_nonroot
+
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+
+ts_cd "$TS_OUTDIR"
+
+PID=
+FD=3
+NAME_FD=
+NAME_NS=
+INO_FD=
+INO_NS=
+{
+ coproc MKFDS { "$TS_HELPER_MKFDS" netns "$FD"; }
+ if read -r -u "${MKFDS[0]}" PID; then
+ "${TS_CMD_LSFD}" -n -oNS.TYPE -p "${PID}" -Q "(FD == $FD)"
+ NAME_FD=$("${TS_CMD_LSFD}" -n -oNS.NAME -p "${PID}" -Q "(FD == $FD)")
+ NAME_NS=$("${TS_CMD_LSFD}" -n -oNS.NAME -p "${PID}" -Q '(ASSOC == "net")')
+ INO_FD=$( "${TS_CMD_LSFD}" -n -oINODE -p "${PID}" -Q "(FD == $FD)")
+ INO_NS=$( "${TS_CMD_LSFD}" -n -oINODE -p "${PID}" -Q '(ASSOC == "net")')
+
+ [[ "$NAME_FD" = "$NAME_NS" ]]
+ echo "$NAME_FD" == "$NAME_NS": $?
+
+ [[ "$NAME_FD" = "net:[$INO_FD]" ]]
+ echo "$NAME_FD" == "net:[$INO_FD]": $?
+
+ [[ "$NAME_FD" = "net:[$INO_NS]" ]]
+ echo "$NAME_FD" == "net:[$INO_NS]": $?
+
+ kill -CONT ${PID}
+ fi
+ wait "${MKFDS_PID}"
+} > "$TS_OUTPUT" 2>&1
+
+ts_finalize