...
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <sched.h>
4#include <stdio.h>
5#include <unistd.h>
6#include <stdarg.h>
7#include <stdlib.h>
8
9/*
10 * All of the code here is run inside an aync-signal-safe context, so we need
11 * to be careful to not call any functions that could cause issues. In theory,
12 * since we are a Go program, there are fewer restrictions in practice, it's
13 * better to be safe than sorry.
14 *
15 * The only exception is exit, which we need to call to make sure we don't
16 * return into runc.
17 */
18
19void bail(int pipefd, const char *fmt, ...)
20{
21 va_list args;
22
23 va_start(args, fmt);
24 vdprintf(pipefd, fmt, args);
25 va_end(args);
26
27 exit(1);
28}
29
30int spawn_userns_cat(char *userns_path, char *path, int outfd, int errfd)
31{
32 char buffer[4096] = { 0 };
33
34 pid_t child = fork();
35 if (child != 0)
36 return child;
37 /* in child */
38
39 /* Join the target userns. */
40 int nsfd = open(userns_path, O_RDONLY);
41 if (nsfd < 0)
42 bail(errfd, "open userns path %s failed: %m", userns_path);
43
44 int err = setns(nsfd, CLONE_NEWUSER);
45 if (err < 0)
46 bail(errfd, "setns %s failed: %m", userns_path);
47
48 close(nsfd);
49
50 /* Pipe the requested file contents. */
51 int fd = open(path, O_RDONLY);
52 if (fd < 0)
53 bail(errfd, "open %s in userns %s failed: %m", path, userns_path);
54
55 int nread, ntotal = 0;
56 while ((nread = read(fd, buffer, sizeof(buffer))) != 0) {
57 if (nread < 0)
58 bail(errfd, "read bytes from %s failed (after %d total bytes read): %m", path, ntotal);
59 ntotal += nread;
60
61 int nwritten = 0;
62 while (nwritten < nread) {
63 int n = write(outfd, buffer, nread - nwritten);
64 if (n < 0)
65 bail(errfd, "write %d bytes from %s failed (after %d bytes written): %m",
66 nread - nwritten, path, nwritten);
67 nwritten += n;
68 }
69 if (nread != nwritten)
70 bail(errfd, "mismatch for bytes read and written: %d read != %d written", nread, nwritten);
71 }
72
73 close(fd);
74 close(outfd);
75 close(errfd);
76
77 /* We must exit here, otherwise we would return into a forked runc. */
78 exit(0);
79}
View as plain text