1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <sys/mount.h>
4 
5 #include "alloc-util.h"
6 #include "blockdev-util.h"
7 #include "chase-symlinks.h"
8 #include "devnum-util.h"
9 #include "escape.h"
10 #include "main-func.h"
11 #include "mkdir.h"
12 #include "mount-util.h"
13 #include "mountpoint-util.h"
14 #include "path-util.h"
15 #include "string-util.h"
16 #include "volatile-util.h"
17 
make_volatile(const char * path)18 static int make_volatile(const char *path) {
19         _cleanup_free_ char *old_usr = NULL;
20         int r;
21 
22         assert(path);
23 
24         r = chase_symlinks("/usr", path, CHASE_PREFIX_ROOT, &old_usr, NULL);
25         if (r < 0)
26                 return log_error_errno(r, "/usr not available in old root: %m");
27 
28         r = mkdir_p("/run/systemd/volatile-sysroot", 0700);
29         if (r < 0)
30                 return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m");
31 
32         r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
33         if (r < 0)
34                 goto finish_rmdir;
35 
36         if (mkdir("/run/systemd/volatile-sysroot/usr", 0755) < 0) {
37                 r = log_error_errno(errno, "Failed to create /usr directory: %m");
38                 goto finish_umount;
39         }
40 
41         r = mount_nofollow_verbose(LOG_ERR, old_usr, "/run/systemd/volatile-sysroot/usr", NULL, MS_BIND|MS_REC, NULL);
42         if (r < 0)
43                 goto finish_umount;
44 
45         r = bind_remount_recursive("/run/systemd/volatile-sysroot/usr", MS_RDONLY, MS_RDONLY, NULL);
46         if (r < 0) {
47                 log_error_errno(r, "Failed to remount /usr read-only: %m");
48                 goto finish_umount;
49         }
50 
51         r = umount_recursive(path, 0);
52         if (r < 0) {
53                 log_error_errno(r, "Failed to unmount %s: %m", path);
54                 goto finish_umount;
55         }
56 
57         if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
58                 log_warning_errno(errno, "Failed to remount %s MS_SLAVE|MS_REC, ignoring: %m", path);
59 
60         r = mount_nofollow_verbose(LOG_ERR, "/run/systemd/volatile-sysroot", path, NULL, MS_MOVE, NULL);
61 
62 finish_umount:
63         (void) umount_recursive("/run/systemd/volatile-sysroot", 0);
64 
65 finish_rmdir:
66         (void) rmdir("/run/systemd/volatile-sysroot");
67 
68         return r;
69 }
70 
make_overlay(const char * path)71 static int make_overlay(const char *path) {
72         _cleanup_free_ char *escaped_path = NULL;
73         bool tmpfs_mounted = false;
74         const char *options = NULL;
75         int r;
76 
77         assert(path);
78 
79         r = mkdir_p("/run/systemd/overlay-sysroot", 0700);
80         if (r < 0)
81                 return log_error_errno(r, "Couldn't create overlay sysroot directory: %m");
82 
83         r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
84         if (r < 0)
85                 goto finish;
86 
87         tmpfs_mounted = true;
88 
89         if (mkdir("/run/systemd/overlay-sysroot/upper", 0755) < 0) {
90                 r = log_error_errno(errno, "Failed to create /run/systemd/overlay-sysroot/upper: %m");
91                 goto finish;
92         }
93 
94         if (mkdir("/run/systemd/overlay-sysroot/work", 0755) < 0) {
95                 r = log_error_errno(errno, "Failed to create /run/systemd/overlay-sysroot/work: %m");
96                 goto finish;
97         }
98 
99         escaped_path = shell_escape(path, ",:");
100         if (!escaped_path) {
101                 r = log_oom();
102                 goto finish;
103         }
104 
105         options = strjoina("lowerdir=", escaped_path, ",upperdir=/run/systemd/overlay-sysroot/upper,workdir=/run/systemd/overlay-sysroot/work");
106         r = mount_nofollow_verbose(LOG_ERR, "overlay", path, "overlay", 0, options);
107 
108 finish:
109         if (tmpfs_mounted)
110                 (void) umount_verbose(LOG_ERR, "/run/systemd/overlay-sysroot", UMOUNT_NOFOLLOW);
111 
112         (void) rmdir("/run/systemd/overlay-sysroot");
113         return r;
114 }
115 
run(int argc,char * argv[])116 static int run(int argc, char *argv[]) {
117         VolatileMode m = _VOLATILE_MODE_INVALID;
118         const char *path;
119         dev_t devt;
120         int r;
121 
122         log_setup();
123 
124         if (argc > 3)
125                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
126                                        "Too many arguments. Expected directory and mode.");
127 
128         r = query_volatile_mode(&m);
129         if (r < 0)
130                 return log_error_errno(r, "Failed to determine volatile mode from kernel command line.");
131         if (r == 0 && argc >= 2) {
132                 /* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */
133                 m = volatile_mode_from_string(argv[1]);
134                 if (m < 0)
135                         return log_error_errno(m, "Couldn't parse volatile mode: %s", argv[1]);
136         }
137 
138         if (argc < 3)
139                 path = "/sysroot";
140         else {
141                 path = argv[2];
142 
143                 if (isempty(path))
144                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
145                                                "Directory name cannot be empty.");
146                 if (!path_is_absolute(path))
147                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
148                                                "Directory must be specified as absolute path.");
149                 if (path_equal(path, "/"))
150                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
151                                                "Directory cannot be the root directory.");
152         }
153 
154         if (!IN_SET(m, VOLATILE_YES, VOLATILE_OVERLAY))
155                 return 0;
156 
157         r = path_is_mount_point(path, NULL, AT_SYMLINK_FOLLOW);
158         if (r < 0)
159                 return log_error_errno(r, "Couldn't determine whether %s is a mount point: %m", path);
160         if (r == 0)
161                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a mount point.", path);
162 
163         r = path_is_temporary_fs(path);
164         if (r < 0)
165                 return log_error_errno(r, "Couldn't determine whether %s is a temporary file system: %m", path);
166         if (r > 0) {
167                 log_info("%s already is a temporary file system.", path);
168                 return 0;
169         }
170 
171         /* We are about to replace the root directory with something else. Later code might want to know what we
172          * replaced here, hence let's save that information as a symlink we can later use. (This is particularly
173          * relevant for the overlayfs case where we'll fully obstruct the view onto the underlying device, hence
174          * querying the backing device node from the file system directly is no longer possible. */
175         r = get_block_device_harder(path, &devt);
176         if (r < 0)
177                 return log_error_errno(r, "Failed to determine device major/minor of %s: %m", path);
178         else if (r > 0) { /* backed by block device */
179                 _cleanup_free_ char *dn = NULL;
180 
181                 r = device_path_make_major_minor(S_IFBLK, devt, &dn);
182                 if (r < 0)
183                         return log_error_errno(r, "Failed to format device node path: %m");
184 
185                 if (symlink(dn, "/run/systemd/volatile-root") < 0)
186                         log_warning_errno(errno, "Failed to create symlink /run/systemd/volatile-root: %m");
187         }
188 
189         if (m == VOLATILE_YES)
190                 return make_volatile(path);
191         else {
192                 assert(m == VOLATILE_OVERLAY);
193                 return make_overlay(path);
194         }
195 }
196 
197 DEFINE_MAIN_FUNCTION(run);
198