81176ce399b4cd8e2d347c0008c13dec92407f55
[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
18 #include <errno.h>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <string.h>
22
23 #include "libfstools.h"
24
25 /* this is a raw syscall - man 2 pivot_root */
26 extern int pivot_root(const char *new_root, const char *put_old);
27
28 int
29 mount_move(char *oldroot, char *newroot, char *dir)
30 {
31 #ifndef MS_MOVE
32 #define MS_MOVE (1 << 13)
33 #endif
34         struct stat s;
35         char olddir[64];
36         char newdir[64];
37         int ret;
38
39         snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir);
40         snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir);
41
42         if (stat(olddir, &s) || !S_ISDIR(s.st_mode))
43                 return -1;
44
45         if (stat(newdir, &s) || !S_ISDIR(s.st_mode))
46                 return -1;
47
48         ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL);
49
50 /*      if (ret)
51                 ULOG_ERR("failed %s %s: %s\n", olddir, newdir, strerror(errno));*/
52
53         return ret;
54 }
55
56 int
57 pivot(char *new, char *old)
58 {
59         char pivotdir[64];
60         int ret;
61
62         if (mount_move("", new, "/proc"))
63                 return -1;
64
65         snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old);
66
67         ret = pivot_root(new, pivotdir);
68
69         if (ret < 0) {
70                 ULOG_ERR("pivot_root failed %s %s: %s\n", new, pivotdir, strerror(errno));
71                 return -1;
72         }
73
74         mount_move(old, "", "/dev");
75         mount_move(old, "", "/tmp");
76         mount_move(old, "", "/sys");
77         mount_move(old, "", "/overlay");
78
79         return 0;
80 }
81
82 int
83 fopivot(char *rw_root, char *ro_root)
84 {
85         char overlay[64], lowerdir[64];
86
87         if (find_filesystem("overlay")) {
88                 ULOG_ERR("BUG: no suitable fs found\n");
89                 return -1;
90         }
91
92         snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root);
93
94         /*
95          * First, try to mount without a workdir, for overlayfs v22 and before.
96          * If it fails, it means that we are probably using a v23 and
97          * later versions that require a workdir
98          */
99         snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root);
100         if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) {
101                 char upperdir[64], workdir[64], upgrade[64], upgrade_dest[64];
102                 struct stat st;
103
104                 snprintf(upperdir, sizeof(upperdir), "%s/upper", rw_root);
105                 snprintf(workdir, sizeof(workdir), "%s/work", rw_root);
106                 snprintf(upgrade, sizeof(upgrade), "%s/sysupgrade.tgz", rw_root);
107                 snprintf(upgrade_dest, sizeof(upgrade_dest), "%s/sysupgrade.tgz", upperdir);
108                 snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s,workdir=%s",
109                          upperdir, workdir);
110
111                 /*
112                  * Overlay FS v23 and later requires both a upper and
113                  * a work directory, both on the same filesystem, but
114                  * not part of the same subtree.
115                  * We can't really deal with these constraints without
116                  * creating two new subdirectories in /overlay.
117                  */
118                 mkdir(upperdir, 0755);
119                 mkdir(workdir, 0755);
120
121                 if (stat(upgrade, &st) == 0)
122                     rename(upgrade, upgrade_dest);
123
124                 /* Mainlined overlayfs has been renamed to "overlay", try that first */
125                 if (mount(overlay, "/mnt", "overlay", MS_NOATIME, lowerdir)) {
126                         if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) {
127                                 ULOG_ERR("mount failed: %s, options %s\n",
128                                          strerror(errno), lowerdir);
129                                 return -1;
130                         }
131                 }
132         }
133
134         return pivot("/mnt", ro_root);
135 }
136
137 int
138 ramoverlay(void)
139 {
140         mkdir("/tmp/root", 0755);
141         mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755");
142
143         return fopivot("/tmp/root", "/rom");
144 }