]> git.proxmox.com Git - mirror_lxc.git/blob - hooks/unmount-namespace.c
string_utils: coding rules
[mirror_lxc.git] / hooks / unmount-namespace.c
1 /*
2 * Copyright © 2015 Wolfgang Bumiller <w.bumiller@proxmox.com>.
3 * Copyright © 2015 Proxmox Server Solutions GmbH
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2, as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * --
19 *
20 * This stop-hook unmounts everything in the container's namespace, and thereby
21 * waits for all calls commands to finish. This is useful when one needs to be
22 * sure that network filesystems are finished unmounting in the namespace
23 * before continuing with other tasks. Without this hook the cleanup of mounts
24 * is done by the kernel in the background after all the references to the
25 * namespaces are gone.
26 */
27
28 #define _GNU_SOURCE /* setns */
29 #include <stdio.h> /* fdopen, getmntent, endmntent */
30 #include <stdlib.h> /* malloc, qsort */
31 #include <unistd.h> /* close */
32 #include <string.h> /* strcmp, strncmp, strdup, strerror */
33 #include <sched.h> /* setns */
34 #include <sys/mount.h> /* umount2 */
35 #include <sys/types.h> /* openat, open */
36 #include <sys/stat.h> /* openat, open */
37 #include <fcntl.h> /* openat, open */
38 #include <errno.h> /* errno */
39
40 #include <../src/config.h>
41
42 #if IS_BIONIC
43 #include <../src/include/lxcmntent.h>
44 #else
45 #include <mntent.h>
46 #endif
47
48 #ifndef O_PATH
49 #define O_PATH 010000000
50 #endif
51
52 /* Define setns() if missing from the C library */
53 #ifndef HAVE_SETNS
54 static inline int setns(int fd, int nstype)
55 {
56 #ifdef __NR_setns
57 return syscall(__NR_setns, fd, nstype);
58 #elif defined(__NR_set_ns)
59 return syscall(__NR_set_ns, fd, nstype);
60 #else
61 errno = ENOSYS;
62 return -1;
63 #endif
64 }
65 #endif
66
67 struct mount {
68 char *src; /* currently not used */
69 char *dst;
70 char *fs; /* currently not used */
71 };
72
73 static void mount_free(struct mount *mnt) {
74 free(mnt->src);
75 free(mnt->dst);
76 free(mnt->fs);
77 }
78
79 static int mount_cmp_dst(const void *a_, const void *b_) {
80 struct mount *a = (struct mount*)a_;
81 struct mount *b = (struct mount*)b_;
82 return strcmp(b->dst, a->dst); /* swapped order */
83 }
84
85 /* Unmounting /dev/pts fails, and so /dev also fails, but /dev is not what
86 * we're interested in. (There might also still be /dev/cgroup mounts).
87 */
88 static int mount_should_error(const struct mount *mnt) {
89 const char *dst = mnt->dst;
90 return !(strncmp(dst, "/dev", 4) == 0 && (dst[4] == 0 || dst[4] == '/'));
91 }
92
93 /* Read mounts from 'self/mounts' relative to a directory filedescriptor.
94 * Before entering the container we open a handle to /proc on the host as we
95 * need to access /proc/self/mounts and the container's /proc doesn't contain
96 * our /self. We then use openat(2) to avoid having to mount a temporary /proc.
97 */
98 static int read_mounts(int procfd, struct mount **mp, size_t *countp) {
99 int fd;
100 struct mntent *ent;
101 FILE *mf;
102 size_t capacity = 32;
103 size_t count = 0;
104 struct mount *mounts = (struct mount*)malloc(capacity * sizeof(*mounts));
105
106 if (!mounts) {
107 errno = ENOMEM;
108 return 0;
109 }
110
111 *mp = NULL;
112 *countp = 0;
113
114 fd = openat(procfd, "self/mounts", O_RDONLY);
115 if (fd < 0) {
116 free(mounts);
117 return 0;
118 }
119
120 mf = fdopen(fd, "r");
121 if (!mf) {
122 int error = errno;
123 close(fd);
124 errno = error;
125 free(mounts);
126 return 0;
127 }
128 while ((ent = getmntent(mf))) {
129 struct mount *new;
130 if (count == capacity) {
131 capacity *= 2;
132 new = (struct mount*)realloc(mounts, capacity * sizeof(*mounts));
133 if (!new)
134 goto out_alloc_entry;
135 mounts = new;
136 }
137 new = &mounts[count++];
138 new->src = strdup(ent->mnt_fsname);
139 new->dst = strdup(ent->mnt_dir);
140 new->fs = strdup(ent->mnt_type);
141 if (!new->src || !new->dst || !new->fs)
142 goto out_alloc_entry;
143 }
144 endmntent(mf);
145
146 *mp = mounts;
147 *countp = count;
148
149 return 1;
150
151 out_alloc_entry:
152 endmntent(mf);
153 while (count--) {
154 free(mounts[count].src);
155 free(mounts[count].dst);
156 free(mounts[count].fs);
157 }
158 free(mounts);
159 errno = ENOMEM;
160 return 0;
161 }
162
163 int main(int argc, char **argv) {
164 int i, procfd, ctmntfd;
165 struct mount *mounts;
166 size_t zi, count = 0;
167 const char *mntns = NULL;
168
169 if (argc < 4 || strcmp(argv[2], "lxc") != 0) {
170 fprintf(stderr, "%s: usage error, expected LXC hook arguments\n", argv[0]);
171 return 2;
172 }
173
174 if (strcmp(argv[3], "stop") != 0)
175 return 0;
176
177 for (i = 4; i != argc; ++i) {
178 if (!strncmp(argv[i], "mnt:", 4)) {
179 mntns = argv[i] + 4;
180 break;
181 }
182 }
183
184 if (!mntns) {
185 fprintf(stderr, "%s: no mount namespace provided\n", argv[0]);
186 return 3;
187 }
188
189 /* Open a handle to /proc on the host as we need to access /proc/self/mounts
190 * and the container's /proc doesn't contain our /self. See read_mounts().
191 */
192 procfd = open("/proc", O_RDONLY | O_DIRECTORY | O_PATH);
193 if (procfd < 0) {
194 fprintf(stderr, "%s: failed to open /proc: %s\n", argv[0], strerror(errno));
195 return 4;
196 }
197
198 /* Open the mount namespace and enter it. */
199 ctmntfd = open(mntns, O_RDONLY);
200 if (ctmntfd < 0) {
201 fprintf(stderr, "%s: failed to open mount namespace: %s\n",
202 argv[0], strerror(errno));
203 close(procfd);
204 return 5;
205 }
206
207 if (setns(ctmntfd, CLONE_NEWNS) != 0) {
208 fprintf(stderr, "%s: failed to attach to namespace: %s\n",
209 argv[0], strerror(errno));
210 close(ctmntfd);
211 close(procfd);
212 return 6;
213 }
214 close(ctmntfd);
215
216 /* Now read [[procfd]]/self/mounts */
217 if (!read_mounts(procfd, &mounts, &count)) {
218 fprintf(stderr, "%s: failed to read mountpoints: %s\n",
219 argv[0], strerror(errno));
220 close(procfd);
221 return 7;
222 }
223 close(procfd);
224
225 /* Just sort to get a sane unmount-order... */
226 qsort(mounts, count, sizeof(*mounts), &mount_cmp_dst);
227
228 for (zi = 0; zi != count; ++zi) {
229 /* fprintf(stderr, "Unmount: %s\n", mounts[zi].dst); */
230 if (umount2(mounts[zi].dst, 0) != 0) {
231 int error = errno;
232 if (mount_should_error(&mounts[zi])) {
233 fprintf(stderr, "%s: failed to unmount %s: %s\n",
234 argv[0], mounts[zi].dst, strerror(error));
235 }
236 }
237 mount_free(&mounts[zi]);
238 }
239 free(mounts);
240
241 return 0;
242 }