mount: apply SELinux labels before overlayfs mount
[project/fstools.git] / libfstools / mount.c
1 /*
2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/mount.h>
17 #include <sys/wait.h>
18
19 #include <errno.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <string.h>
23
24 #include "libfstools.h"
25
26 /* this is a raw syscall - man 2 pivot_root */
27 extern int pivot_root(const char *new_root, const char *put_old);
28
29 /**
30 * mount_move - move mounted point to the new location
31 *
32 * @oldroot: directory that is current location of mount point
33 * @newroot: new directory for the mount point
34 */
35 int
36 mount_move(const char *oldroot, const char *newroot, const char *dir)
37 {
38 #ifndef MS_MOVE
39 #define MS_MOVE (1 << 13)
40 #endif
41 struct stat s;
42 char olddir[64];
43 char newdir[64];
44 int ret;
45
46 snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir);
47 snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir);
48
49 if (stat(olddir, &s) || !S_ISDIR(s.st_mode))
50 return -1;
51
52 if (stat(newdir, &s) || !S_ISDIR(s.st_mode))
53 return -1;
54
55 ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL);
56
57 /* if (ret)
58 ULOG_ERR("failed %s %s: %m\n", olddir, newdir);*/
59
60 return ret;
61 }
62
63 int
64 pivot(char *new, char *old)
65 {
66 char pivotdir[64];
67 int ret;
68
69 if (mount_move("", new, "/proc"))
70 return -1;
71
72 snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old);
73
74 ret = pivot_root(new, pivotdir);
75
76 if (ret < 0) {
77 ULOG_ERR("pivot_root failed %s %s: %m\n", new, pivotdir);
78 return -1;
79 }
80
81 mount_move(old, "", "/dev");
82 mount_move(old, "", "/tmp");
83 mount_move(old, "", "/sys");
84 mount_move(old, "", "/overlay");
85
86 return 0;
87 }
88
89 void
90 selinux_restorecon(char *overlaydir)
91 {
92 struct stat s;
93 pid_t restorecon_pid;
94 int status;
95
96 /* on non-SELinux system we don't have /sbin/restorecon, return */
97 if (stat("/sbin/restorecon", &s))
98 return;
99
100 restorecon_pid = fork();
101 if (!restorecon_pid)
102 execl("/sbin/restorecon", "restorecon", overlaydir, (char *) NULL);
103 else if (restorecon_pid > 0)
104 waitpid(restorecon_pid, &status, 0);
105 }
106
107 /**
108 * fopivot - switch to overlay using passed dir as upper one
109 *
110 * @rw_root: writable directory that will be used as upper dir
111 * @ro_root: directory where old root will be put
112 */
113 int
114 fopivot(char *rw_root, char *ro_root)
115 {
116 char overlay[64], mount_options[64], upperdir[64], workdir[64], upgrade[64], upgrade_dest[64];
117 struct stat st;
118
119 if (find_filesystem("overlay")) {
120 ULOG_ERR("BUG: no suitable fs found\n");
121 return -1;
122 }
123
124 snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root);
125 snprintf(upperdir, sizeof(upperdir), "%s/upper", rw_root);
126 snprintf(workdir, sizeof(workdir), "%s/work", rw_root);
127 snprintf(upgrade, sizeof(upgrade), "%s/sysupgrade.tgz", rw_root);
128 snprintf(upgrade_dest, sizeof(upgrade_dest), "%s/sysupgrade.tgz", upperdir);
129 snprintf(mount_options, sizeof(mount_options), "lowerdir=/,upperdir=%s,workdir=%s",
130 upperdir, workdir);
131
132 /*
133 * Initialize SELinux security label on newly created overlay
134 * filesystem where /upper doesn't yet exist
135 */
136 if (stat(upperdir, &st))
137 selinux_restorecon(rw_root);
138
139 /*
140 * Overlay FS v23 and later requires both a upper and
141 * a work directory, both on the same filesystem, but
142 * not part of the same subtree.
143 * We can't really deal with these constraints without
144 * creating two new subdirectories in /overlay.
145 */
146 mkdir(upperdir, 0755);
147 mkdir(workdir, 0755);
148
149 if (stat(upgrade, &st) == 0)
150 rename(upgrade, upgrade_dest);
151
152 if (mount(overlay, "/mnt", "overlay", MS_NOATIME, mount_options)) {
153 ULOG_ERR("mount failed: %m, options %s\n", mount_options);
154 return -1;
155 }
156
157 return pivot("/mnt", ro_root);
158 }
159
160 /**
161 * ramoverlay - use RAM to store filesystem changes on top of RO root
162 */
163 int
164 ramoverlay(void)
165 {
166 mkdir("/tmp/root", 0755);
167 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755");
168
169 return fopivot("/tmp/root", "/rom");
170 }