]>
git.proxmox.com Git - mirror_lxc.git/blob - hooks/unmount-namespace.c
2 * Copyright © 2015 Wolfgang Bumiller <w.bumiller@proxmox.com>.
3 * Copyright © 2015 Proxmox Server Solutions GmbH
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.
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.
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.
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.
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 */
40 #include <../src/config.h>
43 #include "lxcmntent.h"
49 #define O_PATH 010000000
52 /* Define setns() if missing from the C library */
54 static inline int setns(int fd
, int nstype
)
57 return syscall(__NR_setns
, fd
, nstype
);
58 #elif defined(__NR_set_ns)
59 return syscall(__NR_set_ns
, fd
, nstype
);
68 char *src
; /* currently not used */
70 char *fs
; /* currently not used */
73 static void mount_free(struct mount
*mnt
) {
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 */
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).
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] == '/'));
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.
98 static int read_mounts(int procfd
, struct mount
**mp
, size_t *countp
) {
102 size_t capacity
= 32;
104 struct mount
*mounts
= (struct mount
*)malloc(capacity
* sizeof(*mounts
));
114 fd
= openat(procfd
, "self/mounts", O_RDONLY
);
120 mf
= fdopen(fd
, "r");
128 while ((ent
= getmntent(mf
))) {
130 if (count
== capacity
) {
132 new = (struct mount
*)realloc(mounts
, capacity
* sizeof(*mounts
));
134 goto out_alloc_entry
;
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
;
154 free(mounts
[count
].src
);
155 free(mounts
[count
].dst
);
156 free(mounts
[count
].fs
);
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
;
169 if (argc
< 4 || strcmp(argv
[2], "lxc") != 0) {
170 fprintf(stderr
, "%s: usage error, expected LXC hook arguments\n", argv
[0]);
174 if (strcmp(argv
[3], "stop") != 0)
177 for (i
= 4; i
!= argc
; ++i
) {
178 if (!strncmp(argv
[i
], "mnt:", 4)) {
185 fprintf(stderr
, "%s: no mount namespace provided\n", argv
[0]);
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().
192 procfd
= open("/proc", O_RDONLY
| O_DIRECTORY
| O_PATH
);
194 fprintf(stderr
, "%s: failed to open /proc: %s\n", argv
[0], strerror(errno
));
198 /* Open the mount namespace and enter it. */
199 ctmntfd
= open(mntns
, O_RDONLY
);
201 fprintf(stderr
, "%s: failed to open mount namespace: %s\n",
202 argv
[0], strerror(errno
));
207 if (setns(ctmntfd
, CLONE_NEWNS
) != 0) {
208 fprintf(stderr
, "%s: failed to attach to namespace: %s\n",
209 argv
[0], strerror(errno
));
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
));
225 /* Just sort to get a sane unmount-order... */
226 qsort(mounts
, count
, sizeof(*mounts
), &mount_cmp_dst
);
228 for (zi
= 0; zi
!= count
; ++zi
) {
229 /* fprintf(stderr, "Unmount: %s\n", mounts[zi].dst); */
230 if (umount2(mounts
[zi
].dst
, 0) != 0) {
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
));
237 mount_free(&mounts
[zi
]);