~droyo/guix-channel

ref: 3bfc628072a3ea8dda59cde303edd7869e30c3d9 guix-channel/aqwari/ns-helper.c -rw-r--r-- 3.9 KiB
3bfc6280David Arroyo Add module and syntax for declaring mount namespaces 15 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define _GNU_SOURCE
#include <sched.h>        /* for unshare(2) */
#include <sys/mount.h>    /* for mount(2) */
#include <sys/syscall.h>  /* for pivot_root(2) */
#include <sys/stat.h>     /* for mkdirat(2) */
#include <linux/limits.h> /* for PATH_MAX */

#define NELEMS(x) ((sizeof x) / (sizeof *x))


void die(const int rc, const char *fmterr, ...) {
	va_list ap;
	va_start(ap, fmterr);
	vfprintf(stderr, fmterr, ap);
	va_end(ap);
	exit(rc);
}

void fail(const int rc, const char *fmterr, ...) {
	int n;
	va_list ap;
	char prefix[500];
	memset(prefix, 0, sizeof prefix);
	va_start(ap, fmterr);
	n = vsnprintf(prefix, (sizeof prefix) - 1, fmterr, ap);
	va_end(ap);
	perror(prefix);
	exit(rc);
}

int pivot_root(const char *new_root, const char *put_old) {
	return syscall(SYS_pivot_root, new_root, put_old);
}

void dumpfstab() {
	printf("root = %s\n", root);
	for (int i = 0; i < NELEMS(fstab); i++) {
		struct fstab *fs = &fstab[i];
		printf("%s\t%s\t\%s\t%s\n",
			fs->spec, fs->file, fs->type, fs->opts);
	}
}

int main(int argc, char **argv) {
	const char *usage = "Usage: exec prog arg ...\n";

	if (argc <= 1) die(2, usage);
	if (!strcmp(argv[1] , "-h")) die(0, usage);
	if (!strcmp(argv[1], "--help")) die(0, usage);
	if (!strcmp(argv[1], "-D")) {
		dumpfstab();
		exit(0);
	}

	if (unshare(CLONE_NEWNS)) fail(1, "unshare(CLONE_NEWNS)");

	if (mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL)) {
		fail(1, "force mounts private: mount(/, MS_PRIVATE)");
	}


	for (int i = 0; i < NELEMS(fstab); i++) {
		struct fstab *fs = &fstab[i];
		int flags = 0;
		if (!strcmp(fs->opts, "defaults,bind")) {
			flags = MS_BIND | MS_REC;
		}
		if (mount(fs->spec, fs->file, fs->type, flags, fs->opts)) {
			fail(1, "mount(%s, %s, %s, %x, %s)",
				fs->spec, fs->file, fs->type, flags, fs->opts);
		}

		/* tmpfs mounts used to support a writable overlay must be
		   pre-populated with an "upper" and "work" dir */
		int tmpfs;
		if (fs->upper) {
			if ((tmpfs = open(fs->file, O_PATH)) < 0)
				fail(1, "open(%s, O_PATH)", fs->file);
			if (mkdirat(tmpfs, "upper", 0777))
				fail(1, "mkdirat(%s, \"upper\")", fs->file);
			if (mkdirat(tmpfs, "work", 0777))
				fail(1, "mkdirat(%s, \"work\")", fs->file);
			if (close(tmpfs))
				fail(1, "close(%s)", fs->file);
		}
	}

	/* I don't like that this is necessary. However, most, if not all
	   binaries in the GNU store are dynamically linked against other
	   libraries in the GNU store.

	   Note that MS_REC is left out intentionally to avoid creating
	   a loop. */
	if (mount("/gnu/store", gnustore, NULL, MS_BIND, "defaults,none"))
		fail(1, "bind /gnu/store: mount(/gnu/store, %s)", gnustore);

	/* This satisfies pivot_root(2)'s requirement that new_root must be
	   the path to a mount point. In practice, there is likely to be an
	   entry for the new root in fstab and this is not necessary. However,
	   it does not hurt, so we do it unconditionally. */
	if (mount(root, root, NULL, MS_BIND | MS_REC, NULL)) {
		fail(1, "ensure root is mountpoint: mount(%s, MS_BIND)", root);
	}

	/* This sequence is taken from the pivot_root(2) man page, and inspired
	   by the `lxc_pivot_root` function from LXC. */
	int oldroot, newroot;
	if ((oldroot = open("/", O_PATH)) < 0)
		fail(1, "get fd for oldroot: open(/)");

	if ((newroot = open(root, O_PATH)) < 0)
		fail(1, "get fd for newroot: open(%s)", root);

	if (fchdir(newroot))
		fail(1, "change to new root: fchdir(%d)", newroot);

	if (pivot_root(".", "."))
		fail(1, "pivot_root(\".\", \".\")");

	if (fchdir(oldroot))
		fail(1, "escape to oldroot: fchdir(%d)", oldroot);

	if (umount2(".", MNT_DETACH))
		fail(1, "detach oldroot: umount2(\".\", MNT_DETACH)");

	if (fchdir(newroot))
		fail(1, "re-enter newroot: fchdir(%d)", newroot);

	if (close(oldroot)) fail(1, "close oldroot");
	if (close(newroot)) fail(1, "close newroot");

	execv(argv[1], &argv[1]);
	fail(1, "execv(%s, ...)", argv[1]);
}