common/rc: add _scratch_{u}mount_idmapped() helpers
[xfstests-dev.git] / src / idmapped-mounts / utils.c
1 // SPDX-License-Identifier: GPL-2.0
2 #ifndef _GNU_SOURCE
3 #define _GNU_SOURCE
4 #endif
5 #include <fcntl.h>
6 #include <linux/limits.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sched.h>
10 #include <sys/mount.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14
15 #include "utils.h"
16
17 ssize_t read_nointr(int fd, void *buf, size_t count)
18 {
19         ssize_t ret;
20
21         do {
22                 ret = read(fd, buf, count);
23         } while (ret < 0 && errno == EINTR);
24
25         return ret;
26 }
27
28 ssize_t write_nointr(int fd, const void *buf, size_t count)
29 {
30         ssize_t ret;
31
32         do {
33                 ret = write(fd, buf, count);
34         } while (ret < 0 && errno == EINTR);
35
36         return ret;
37 }
38
39 static int write_file(const char *path, const void *buf, size_t count)
40 {
41         int fd;
42         ssize_t ret;
43
44         fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
45         if (fd < 0)
46                 return -1;
47
48         ret = write_nointr(fd, buf, count);
49         close(fd);
50         if (ret < 0 || (size_t)ret != count)
51                 return -1;
52
53         return 0;
54 }
55
56 static int map_ids(pid_t pid, unsigned long nsid, unsigned long hostid,
57                    unsigned long range)
58 {
59         char map[100], procfile[256];
60
61         snprintf(procfile, sizeof(procfile), "/proc/%d/uid_map", pid);
62         snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
63         if (write_file(procfile, map, strlen(map)))
64                 return -1;
65
66
67         snprintf(procfile, sizeof(procfile), "/proc/%d/gid_map", pid);
68         snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
69         if (write_file(procfile, map, strlen(map)))
70                 return -1;
71
72         return 0;
73 }
74
75 #define __STACK_SIZE (8 * 1024 * 1024)
76 pid_t do_clone(int (*fn)(void *), void *arg, int flags)
77 {
78         void *stack;
79
80         stack = malloc(__STACK_SIZE);
81         if (!stack)
82                 return -ENOMEM;
83
84 #ifdef __ia64__
85         return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL);
86 #else
87         return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL);
88 #endif
89 }
90
91 int get_userns_fd_cb(void *data)
92 {
93         return kill(getpid(), SIGSTOP);
94 }
95
96 int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range)
97 {
98         int ret;
99         pid_t pid;
100         char path[256];
101
102         pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER);
103         if (pid < 0)
104                 return -errno;
105
106         ret = map_ids(pid, nsid, hostid, range);
107         if (ret < 0)
108                 return ret;
109
110         snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
111         ret = open(path, O_RDONLY | O_CLOEXEC);
112         kill(pid, SIGKILL);
113         wait_for_pid(pid);
114         return ret;
115 }
116
117 int wait_for_pid(pid_t pid)
118 {
119         int status, ret;
120
121 again:
122         ret = waitpid(pid, &status, 0);
123         if (ret == -1) {
124                 if (errno == EINTR)
125                         goto again;
126
127                 return -1;
128         }
129
130         if (!WIFEXITED(status))
131                 return -1;
132
133         return WEXITSTATUS(status);
134 }