18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Hari Bathini, IBM Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "namespaces.h"
88c2ecf20Sopenharmony_ci#include "event.h"
98c2ecf20Sopenharmony_ci#include "get_current_dir_name.h"
108c2ecf20Sopenharmony_ci#include <sys/types.h>
118c2ecf20Sopenharmony_ci#include <sys/stat.h>
128c2ecf20Sopenharmony_ci#include <fcntl.h>
138c2ecf20Sopenharmony_ci#include <limits.h>
148c2ecf20Sopenharmony_ci#include <sched.h>
158c2ecf20Sopenharmony_ci#include <stdlib.h>
168c2ecf20Sopenharmony_ci#include <stdio.h>
178c2ecf20Sopenharmony_ci#include <string.h>
188c2ecf20Sopenharmony_ci#include <unistd.h>
198c2ecf20Sopenharmony_ci#include <asm/bug.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic const char *perf_ns__names[] = {
248c2ecf20Sopenharmony_ci	[NET_NS_INDEX]		= "net",
258c2ecf20Sopenharmony_ci	[UTS_NS_INDEX]		= "uts",
268c2ecf20Sopenharmony_ci	[IPC_NS_INDEX]		= "ipc",
278c2ecf20Sopenharmony_ci	[PID_NS_INDEX]		= "pid",
288c2ecf20Sopenharmony_ci	[USER_NS_INDEX]		= "user",
298c2ecf20Sopenharmony_ci	[MNT_NS_INDEX]		= "mnt",
308c2ecf20Sopenharmony_ci	[CGROUP_NS_INDEX]	= "cgroup",
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciconst char *perf_ns__name(unsigned int id)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	if (id >= ARRAY_SIZE(perf_ns__names))
368c2ecf20Sopenharmony_ci		return "UNKNOWN";
378c2ecf20Sopenharmony_ci	return perf_ns__names[id];
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct namespaces *namespaces__new(struct perf_record_namespaces *event)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct namespaces *namespaces;
438c2ecf20Sopenharmony_ci	u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) *
448c2ecf20Sopenharmony_ci			      sizeof(struct perf_ns_link_info));
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	namespaces = zalloc(sizeof(struct namespaces) + link_info_size);
478c2ecf20Sopenharmony_ci	if (!namespaces)
488c2ecf20Sopenharmony_ci		return NULL;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	namespaces->end_time = -1;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (event)
538c2ecf20Sopenharmony_ci		memcpy(namespaces->link_info, event->link_info, link_info_size);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return namespaces;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_civoid namespaces__free(struct namespaces *namespaces)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	free(namespaces);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciint nsinfo__init(struct nsinfo *nsi)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	char oldns[PATH_MAX];
668c2ecf20Sopenharmony_ci	char spath[PATH_MAX];
678c2ecf20Sopenharmony_ci	char *newns = NULL;
688c2ecf20Sopenharmony_ci	char *statln = NULL;
698c2ecf20Sopenharmony_ci	struct stat old_stat;
708c2ecf20Sopenharmony_ci	struct stat new_stat;
718c2ecf20Sopenharmony_ci	FILE *f = NULL;
728c2ecf20Sopenharmony_ci	size_t linesz = 0;
738c2ecf20Sopenharmony_ci	int rv = -1;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
768c2ecf20Sopenharmony_ci		return rv;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1)
798c2ecf20Sopenharmony_ci		return rv;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (stat(oldns, &old_stat) < 0)
828c2ecf20Sopenharmony_ci		goto out;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (stat(newns, &new_stat) < 0)
858c2ecf20Sopenharmony_ci		goto out;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* Check if the mount namespaces differ, if so then indicate that we
888c2ecf20Sopenharmony_ci	 * want to switch as part of looking up dso/map data.
898c2ecf20Sopenharmony_ci	 */
908c2ecf20Sopenharmony_ci	if (old_stat.st_ino != new_stat.st_ino) {
918c2ecf20Sopenharmony_ci		nsi->need_setns = true;
928c2ecf20Sopenharmony_ci		nsi->mntns_path = newns;
938c2ecf20Sopenharmony_ci		newns = NULL;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* If we're dealing with a process that is in a different PID namespace,
978c2ecf20Sopenharmony_ci	 * attempt to work out the innermost tgid for the process.
988c2ecf20Sopenharmony_ci	 */
998c2ecf20Sopenharmony_ci	if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX)
1008c2ecf20Sopenharmony_ci		goto out;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	f = fopen(spath, "r");
1038c2ecf20Sopenharmony_ci	if (f == NULL)
1048c2ecf20Sopenharmony_ci		goto out;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	while (getline(&statln, &linesz, f) != -1) {
1078c2ecf20Sopenharmony_ci		/* Use tgid if CONFIG_PID_NS is not defined. */
1088c2ecf20Sopenharmony_ci		if (strstr(statln, "Tgid:") != NULL) {
1098c2ecf20Sopenharmony_ci			nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'),
1108c2ecf20Sopenharmony_ci						     NULL, 10);
1118c2ecf20Sopenharmony_ci			nsi->nstgid = nsi->tgid;
1128c2ecf20Sopenharmony_ci		}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (strstr(statln, "NStgid:") != NULL) {
1158c2ecf20Sopenharmony_ci			nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'),
1168c2ecf20Sopenharmony_ci						     NULL, 10);
1178c2ecf20Sopenharmony_ci			break;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	rv = 0;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ciout:
1238c2ecf20Sopenharmony_ci	if (f != NULL)
1248c2ecf20Sopenharmony_ci		(void) fclose(f);
1258c2ecf20Sopenharmony_ci	free(statln);
1268c2ecf20Sopenharmony_ci	free(newns);
1278c2ecf20Sopenharmony_ci	return rv;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistruct nsinfo *nsinfo__new(pid_t pid)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct nsinfo *nsi;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (pid == 0)
1358c2ecf20Sopenharmony_ci		return NULL;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	nsi = calloc(1, sizeof(*nsi));
1388c2ecf20Sopenharmony_ci	if (nsi != NULL) {
1398c2ecf20Sopenharmony_ci		nsi->pid = pid;
1408c2ecf20Sopenharmony_ci		nsi->tgid = pid;
1418c2ecf20Sopenharmony_ci		nsi->nstgid = pid;
1428c2ecf20Sopenharmony_ci		nsi->need_setns = false;
1438c2ecf20Sopenharmony_ci		/* Init may fail if the process exits while we're trying to look
1448c2ecf20Sopenharmony_ci		 * at its proc information.  In that case, save the pid but
1458c2ecf20Sopenharmony_ci		 * don't try to enter the namespace.
1468c2ecf20Sopenharmony_ci		 */
1478c2ecf20Sopenharmony_ci		if (nsinfo__init(nsi) == -1)
1488c2ecf20Sopenharmony_ci			nsi->need_setns = false;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		refcount_set(&nsi->refcnt, 1);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return nsi;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistruct nsinfo *nsinfo__copy(struct nsinfo *nsi)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct nsinfo *nnsi;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (nsi == NULL)
1618c2ecf20Sopenharmony_ci		return NULL;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	nnsi = calloc(1, sizeof(*nnsi));
1648c2ecf20Sopenharmony_ci	if (nnsi != NULL) {
1658c2ecf20Sopenharmony_ci		nnsi->pid = nsi->pid;
1668c2ecf20Sopenharmony_ci		nnsi->tgid = nsi->tgid;
1678c2ecf20Sopenharmony_ci		nnsi->nstgid = nsi->nstgid;
1688c2ecf20Sopenharmony_ci		nnsi->need_setns = nsi->need_setns;
1698c2ecf20Sopenharmony_ci		if (nsi->mntns_path) {
1708c2ecf20Sopenharmony_ci			nnsi->mntns_path = strdup(nsi->mntns_path);
1718c2ecf20Sopenharmony_ci			if (!nnsi->mntns_path) {
1728c2ecf20Sopenharmony_ci				free(nnsi);
1738c2ecf20Sopenharmony_ci				return NULL;
1748c2ecf20Sopenharmony_ci			}
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci		refcount_set(&nnsi->refcnt, 1);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return nnsi;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_civoid nsinfo__delete(struct nsinfo *nsi)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	zfree(&nsi->mntns_path);
1858c2ecf20Sopenharmony_ci	free(nsi);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistruct nsinfo *nsinfo__get(struct nsinfo *nsi)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	if (nsi)
1918c2ecf20Sopenharmony_ci		refcount_inc(&nsi->refcnt);
1928c2ecf20Sopenharmony_ci	return nsi;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_civoid nsinfo__put(struct nsinfo *nsi)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	if (nsi && refcount_dec_and_test(&nsi->refcnt))
1988c2ecf20Sopenharmony_ci		nsinfo__delete(nsi);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_civoid nsinfo__mountns_enter(struct nsinfo *nsi,
2028c2ecf20Sopenharmony_ci				  struct nscookie *nc)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	char curpath[PATH_MAX];
2058c2ecf20Sopenharmony_ci	int oldns = -1;
2068c2ecf20Sopenharmony_ci	int newns = -1;
2078c2ecf20Sopenharmony_ci	char *oldcwd = NULL;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (nc == NULL)
2108c2ecf20Sopenharmony_ci		return;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	nc->oldns = -1;
2138c2ecf20Sopenharmony_ci	nc->newns = -1;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (!nsi || !nsi->need_setns)
2168c2ecf20Sopenharmony_ci		return;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
2198c2ecf20Sopenharmony_ci		return;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	oldcwd = get_current_dir_name();
2228c2ecf20Sopenharmony_ci	if (!oldcwd)
2238c2ecf20Sopenharmony_ci		return;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	oldns = open(curpath, O_RDONLY);
2268c2ecf20Sopenharmony_ci	if (oldns < 0)
2278c2ecf20Sopenharmony_ci		goto errout;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	newns = open(nsi->mntns_path, O_RDONLY);
2308c2ecf20Sopenharmony_ci	if (newns < 0)
2318c2ecf20Sopenharmony_ci		goto errout;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (setns(newns, CLONE_NEWNS) < 0)
2348c2ecf20Sopenharmony_ci		goto errout;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	nc->oldcwd = oldcwd;
2378c2ecf20Sopenharmony_ci	nc->oldns = oldns;
2388c2ecf20Sopenharmony_ci	nc->newns = newns;
2398c2ecf20Sopenharmony_ci	return;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cierrout:
2428c2ecf20Sopenharmony_ci	free(oldcwd);
2438c2ecf20Sopenharmony_ci	if (oldns > -1)
2448c2ecf20Sopenharmony_ci		close(oldns);
2458c2ecf20Sopenharmony_ci	if (newns > -1)
2468c2ecf20Sopenharmony_ci		close(newns);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_civoid nsinfo__mountns_exit(struct nscookie *nc)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)
2528c2ecf20Sopenharmony_ci		return;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	setns(nc->oldns, CLONE_NEWNS);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (nc->oldcwd) {
2578c2ecf20Sopenharmony_ci		WARN_ON_ONCE(chdir(nc->oldcwd));
2588c2ecf20Sopenharmony_ci		zfree(&nc->oldcwd);
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (nc->oldns > -1) {
2628c2ecf20Sopenharmony_ci		close(nc->oldns);
2638c2ecf20Sopenharmony_ci		nc->oldns = -1;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (nc->newns > -1) {
2678c2ecf20Sopenharmony_ci		close(nc->newns);
2688c2ecf20Sopenharmony_ci		nc->newns = -1;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cichar *nsinfo__realpath(const char *path, struct nsinfo *nsi)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	char *rpath;
2758c2ecf20Sopenharmony_ci	struct nscookie nsc;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	nsinfo__mountns_enter(nsi, &nsc);
2788c2ecf20Sopenharmony_ci	rpath = realpath(path, NULL);
2798c2ecf20Sopenharmony_ci	nsinfo__mountns_exit(&nsc);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return rpath;
2828c2ecf20Sopenharmony_ci}
283