blob: 977443f1e75f7729aa1c93bcc173997f77cdc3b8 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "utils.h"
ssize_t read_nointr(int fd, void *buf, size_t count)
{
ssize_t ret;
do {
ret = read(fd, buf, count);
} while (ret < 0 && errno == EINTR);
return ret;
}
ssize_t write_nointr(int fd, const void *buf, size_t count)
{
ssize_t ret;
do {
ret = write(fd, buf, count);
} while (ret < 0 && errno == EINTR);
return ret;
}
static int write_file(const char *path, const void *buf, size_t count)
{
int fd;
ssize_t ret;
fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
if (fd < 0)
return -1;
ret = write_nointr(fd, buf, count);
close(fd);
if (ret < 0 || (size_t)ret != count)
return -1;
return 0;
}
static int map_ids(pid_t pid, unsigned long nsid, unsigned long hostid,
unsigned long range)
{
char map[100], procfile[256];
snprintf(procfile, sizeof(procfile), "/proc/%d/uid_map", pid);
snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
if (write_file(procfile, map, strlen(map)))
return -1;
snprintf(procfile, sizeof(procfile), "/proc/%d/gid_map", pid);
snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
if (write_file(procfile, map, strlen(map)))
return -1;
return 0;
}
#define __STACK_SIZE (8 * 1024 * 1024)
pid_t do_clone(int (*fn)(void *), void *arg, int flags)
{
void *stack;
stack = malloc(__STACK_SIZE);
if (!stack)
return -ENOMEM;
#ifdef __ia64__
return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL);
#else
return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL);
#endif
}
int get_userns_fd_cb(void *data)
{
return kill(getpid(), SIGSTOP);
}
int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range)
{
int ret;
pid_t pid;
char path[256];
pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER);
if (pid < 0)
return -errno;
ret = map_ids(pid, nsid, hostid, range);
if (ret < 0)
return ret;
snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
ret = open(path, O_RDONLY | O_CLOEXEC);
kill(pid, SIGKILL);
wait_for_pid(pid);
return ret;
}
int wait_for_pid(pid_t pid)
{
int status, ret;
again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;
return -1;
}
if (!WIFEXITED(status))
return -1;
return WEXITSTATUS(status);
}