2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
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
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.
14 #include <sys/types.h>
16 #include <sys/mount.h>
24 #include "libfstools.h"
26 /* this is a raw syscall - man 2 pivot_root */
27 extern int pivot_root(const char *new_root
, const char *put_old
);
30 * mount_move - move mounted point to the new location
32 * @oldroot: directory that is current location of mount point
33 * @newroot: new directory for the mount point
36 mount_move(const char *oldroot
, const char *newroot
, const char *dir
)
39 #define MS_MOVE (1 << 13)
46 snprintf(olddir
, sizeof(olddir
), "%s%s", oldroot
, dir
);
47 snprintf(newdir
, sizeof(newdir
), "%s%s", newroot
, dir
);
49 if (stat(olddir
, &s
) || !S_ISDIR(s
.st_mode
))
52 if (stat(newdir
, &s
) || !S_ISDIR(s
.st_mode
))
55 ret
= mount(olddir
, newdir
, NULL
, MS_NOATIME
| MS_MOVE
, NULL
);
58 ULOG_ERR("failed %s %s: %m\n", olddir, newdir);*/
64 pivot(char *new, char *old
)
69 if (mount_move("", new, "/proc"))
72 snprintf(pivotdir
, sizeof(pivotdir
), "%s%s", new, old
);
74 ret
= pivot_root(new, pivotdir
);
77 ULOG_ERR("pivot_root failed %s %s: %m\n", new, pivotdir
);
81 mount_move(old
, "", "/dev");
82 mount_move(old
, "", "/tmp");
83 mount_move(old
, "", "/sys");
84 mount_move(old
, "", "/overlay");
90 selinux_restorecon(char *overlaydir
)
96 /* on non-SELinux system we don't have /sbin/restorecon, return */
97 if (stat("/sbin/restorecon", &s
))
100 restorecon_pid
= fork();
102 exit(execl("/sbin/restorecon", "restorecon", overlaydir
, (char *) NULL
));
103 else if (restorecon_pid
> 0)
104 waitpid(restorecon_pid
, &status
, 0);
108 * fopivot - switch to overlay using passed dir as upper one
110 * @rw_root: writable directory that will be used as upper dir
111 * @ro_root: directory where old root will be put
114 fopivot(char *rw_root
, char *ro_root
)
116 char overlay
[64], mount_options
[64], upperdir
[64], workdir
[64], upgrade
[64], upgrade_dest
[64];
119 if (find_filesystem("overlay")) {
120 ULOG_ERR("BUG: no suitable fs found\n");
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",
133 * Initialize SELinux security label on newly created overlay
134 * filesystem where /upper doesn't yet exist
136 if (stat(upperdir
, &st
))
137 selinux_restorecon(rw_root
);
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.
146 if (mkdir(upperdir
, 0755) == -1 && errno
!= EEXIST
)
149 if (mkdir(workdir
, 0755) == -1 && errno
!= EEXIST
)
152 if (stat(upgrade
, &st
) == 0)
153 rename(upgrade
, upgrade_dest
);
155 if (mount(overlay
, "/mnt", "overlay", MS_NOATIME
, mount_options
)) {
156 ULOG_ERR("mount failed: %m, options %s\n", mount_options
);
160 return pivot("/mnt", ro_root
);
164 * ramoverlay - use RAM to store filesystem changes on top of RO root
169 mkdir("/tmp/root", 0755);
170 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME
, "mode=0755");
172 return fopivot("/tmp/root", "/rom");