#include <linux/net_namespace.h>
#endif
+#ifdef HAVE_LINUX_NSFS_H
+#include <linux/nsfs.h>
+#endif
+
#include "pathnames.h"
#include "nls.h"
#include "xalloc.h"
COL_USER,
COL_NETNSID,
COL_NSFS,
+ COL_PNS, /* parent namespace */
+ COL_ONS, /* owner namespace */
};
/* column names */
[COL_UID] = { "UID", 0, SCOLS_FL_RIGHT, N_("UID of the PID"), SCOLS_JSON_NUMBER},
[COL_USER] = { "USER", 0, 0, N_("username of the PID")},
[COL_NETNSID] = { "NETNSID", 0, SCOLS_FL_RIGHT, N_("namespace ID as used by network subsystem")},
- [COL_NSFS] = { "NSFS", 0, SCOLS_FL_WRAP, N_("nsfs mountpoint (usually used network subsystem)")}
+ [COL_NSFS] = { "NSFS", 0, SCOLS_FL_WRAP, N_("nsfs mountpoint (usually used network subsystem)")},
+ [COL_PNS] = { "PNS", 10, SCOLS_FL_RIGHT, N_("parent namespace identifier (inode number)"), SCOLS_JSON_NUMBER },
+ [COL_ONS] = { "ONS", 10, SCOLS_FL_RIGHT, N_("owner namespace identifier (inode number)"), SCOLS_JSON_NUMBER },
};
static int columns[ARRAY_SIZE(infos) * 2];
int type; /* LSNS_* */
int nprocs;
int netnsid;
+ ino_t parentid;
+ ino_t ownerid;
struct lsns_process *proc;
uid_t uid;
ino_t ns_ids[ARRAY_SIZE(ns_names)];
+ ino_t ns_pids[ARRAY_SIZE(ns_names)];
+ ino_t ns_oids[ARRAY_SIZE(ns_names)];
+
struct list_head ns_siblings[ARRAY_SIZE(ns_names)];
struct list_head processes; /* list of processes */
return &infos[ get_column_id(num) ];
}
-static int get_ns_ino(int dir, const char *nsname, ino_t *ino)
+static int get_ns_ino(int dir, const char *nsname, ino_t *ino, ino_t *pino, ino_t *oino)
{
struct stat st;
char path[16];
if (fstatat(dir, path, &st, 0) != 0)
return -errno;
*ino = st.st_ino;
+
+ *pino = 0;
+ *oino = 0;
+
+#ifdef HAVE_LINUX_NSFS_H
+ int fd, pfd, ofd;
+ fd = openat(dir, path, 0);
+ if (fd < 0)
+ return -errno;
+ if (strcmp(nsname, "pid") == 0 || strcmp(nsname, "user") == 0) {
+ if ((pfd = ioctl(fd, NS_GET_PARENT)) < 0) {
+ if (errno == EPERM)
+ goto user;
+ close(fd);
+ return -errno;
+ }
+ if (fstat(pfd, &st) < 0) {
+ close(pfd);
+ close(fd);
+ return -errno;
+ }
+ *pino = st.st_ino;
+ close(pfd);
+ }
+ user:
+ if ((ofd = ioctl(fd, NS_GET_USERNS)) < 0) {
+ if (errno == EPERM)
+ goto out;
+ close(fd);
+ return -errno;
+ }
+ if (fstat(ofd, &st) < 0) {
+ close(ofd);
+ close(fd);
+ return -errno;
+ }
+ *oino = st.st_ino;
+ close(ofd);
+ out:
+ close(fd);
+#endif
return 0;
}
if (!ls->fltr_types[i])
continue;
- rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]);
+ rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i],
+ &p->ns_pids[i], &p->ns_oids[i]);
if (rc && rc != -EACCES && rc != -ENOENT)
goto done;
if (i == LSNS_ID_NET)
return 0;
}
-static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino)
+static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino,
+ ino_t parent_ino, ino_t owner_ino)
{
struct lsns_namespace *ns = xcalloc(1, sizeof(*ns));
ns->type = type;
ns->id = ino;
+ ns->parentid = parent_ino;
+ ns->ownerid = owner_ino;
list_add_tail(&ns->namespaces, &ls->namespaces);
return ns;
if (proc->ns_ids[i] == 0)
continue;
if (!(ns = get_namespace(ls, proc->ns_ids[i]))) {
- ns = add_namespace(ls, i, proc->ns_ids[i]);
+ ns = add_namespace(ls, i, proc->ns_ids[i],
+ proc->ns_pids[i], proc->ns_oids[i]);
if (!ns)
return -ENOMEM;
}
case COL_NSFS:
nsfs_xasputs(&str, ns, ls->tab, ls->no_wrap ? ',' : '\n');
break;
+ case COL_PNS:
+ xasprintf(&str, "%ju", (uintmax_t)ns->parentid);
+ break;
+ case COL_ONS:
+ xasprintf(&str, "%ju", (uintmax_t)ns->ownerid);
+ break;
default:
break;
}
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2021 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="namespace ownership and hierarchy"
+
+. $TS_TOPDIR/functions.sh
+ts_init "$*"
+
+# ts_skip_nonroot
+grep -q '#define HAVE_LINUX_NSFS_H' ${top_builddir}/config.h || ts_skip "no ioctl_ns support"
+
+ts_check_test_command "$TS_CMD_LSNS"
+ts_check_test_command "$TS_CMD_UNSHARE"
+ts_check_prog "stat"
+ts_check_prog "mkfifo"
+ts_check_prog "touch"
+ts_check_prog "uniq"
+
+ts_cd "$TS_OUTDIR"
+
+# The parent process receives namespaces ids via FIFO_DATA from bash
+# run by unshare. The parent can wait till the bash process provides
+# data.
+FIFO_DATA=$TS_OUTDIR/FIFO_DATA-HIERARCHY
+
+# The bash process run by unshare waits with this fifo till the parent
+# process exits. As a result, namespaecs referred by the bash
+# process can be alive till the parent process exits.
+FIFO_WAIT=$TS_OUTDIR/FIFO_WAIT-HIERARCHY
+
+function cleanup {
+ rm -f $FIFO_DATA
+ rm -f $FIFO_WAIT
+}
+
+function init {
+ cleanup
+ mkfifo $FIFO_DATA
+ mkfifo $FIFO_WAIT
+}
+
+init
+(
+ exec 4> $FIFO_WAIT
+ my_userns=$(stat -c %i -L /proc/self/ns/user)
+ my_pidns=$(stat -c %i -L /proc/self/ns/pid)
+ read child_userns < $FIFO_DATA
+ read child_pidns < $FIFO_DATA
+
+ expected="$child_userns $my_userns $my_userns"
+ actual=$("$TS_CMD_LSNS" -t user -n -o NS,PNS,ONS "$child_userns" | uniq)
+ test "$expected" = "$actual"
+ RESULT=$?
+ if [ $RESULT -ne 0 ]; then
+ echo
+ echo userns expected: "$expected"
+ echo userns actual: "$actual"
+ cleanup
+ exit $RESULT
+ fi
+
+ expected="$child_pidns $my_pidns $child_userns"
+ actual=$("$TS_CMD_LSNS" -t pid -n -o NS,PNS,ONS "$child_pidns" | uniq)
+ test "$expected" = "$actual"
+ RESULT=$?
+ if [ $RESULT -ne 0 ]; then
+ echo
+ echo pidns expected: "$expected"
+ echo pidns actual: "$actual"
+ cleanup
+ fi
+ exit $RESULT
+) &
+mainpid=$!
+(
+ exec 4< $FIFO_WAIT
+ $TS_CMD_UNSHARE --user --pid --mount-proc --fork bash -s > $FIFO_DATA <<'EOF'
+ stat -c %i -L /proc/self/ns/user
+ stat -c %i -L /proc/self/ns/pid
+ # Wait till FIFO_WAIT is clsoed.
+ read -u 4
+EOF
+) &
+subpid=$!
+
+wait $subpid
+wait $mainpid
+
+RESULT=$?
+echo $RESULT >> $TS_OUTPUT
+
+cleanup
+ts_finalize