]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/mount.c
libpakfire: Drop using libmount
[people/ms/pakfire.git] / src / libpakfire / mount.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2022 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <errno.h>
22 #include <linux/limits.h>
23 #include <stddef.h>
24 #include <sys/mount.h>
25 #include <sys/stat.h>
26 #include <sys/sysmacros.h>
27 #include <sys/types.h>
28
29 #include <pakfire/arch.h>
30 #include <pakfire/logging.h>
31 #include <pakfire/pakfire.h>
32 #include <pakfire/parse.h>
33 #include <pakfire/path.h>
34 #include <pakfire/mount.h>
35 #include <pakfire/string.h>
36 #include <pakfire/util.h>
37
38 static const struct pakfire_mountpoint {
39 const char* source;
40 const char* target;
41 const char* fstype;
42 int flags;
43 const char* options;
44 } mountpoints[] = {
45 // Mount a new instance of /proc
46 { "pakfire_proc", "proc", "proc",
47 MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, },
48
49 // Make /proc/sys read-only (except /proc/sys/net)
50 { "/proc/sys", "proc/sys", "bind", MS_BIND|MS_REC, NULL, },
51 { "/proc/sys/net", "proc/sys/net", "bind", MS_BIND|MS_REC, NULL, },
52 { "/proc/sys", "proc/sys", "bind",
53 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
54
55 // Deny write access to /proc/sysrq-trigger (can be used to restart the host)
56 { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind", MS_BIND|MS_REC, NULL, },
57 { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind",
58 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
59
60 // Make /proc/irq read-only
61 { "/proc/irq", "proc/irq", "bind", MS_BIND|MS_REC, NULL, },
62 { "/proc/irq", "proc/irq", "bind",
63 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
64
65 // Make /proc/bus read-only
66 { "/proc/bus", "proc/bus", "bind", MS_BIND|MS_REC, NULL, },
67 { "/proc/bus", "proc/bus", "bind",
68 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
69
70 // Bind-Mount /sys ready-only
71 { "/sys", "sys", "bind", MS_BIND|MS_REC, NULL, },
72 { "/sys", "sys", "bind",
73 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
74
75 // Create a new /dev
76 { "pakfire_dev", "dev", "tmpfs", MS_NOSUID|MS_NOEXEC,
77 "mode=0755,size=4m,nr_inodes=64k", },
78 { "pakfire_dev_pts", "dev/pts", "devpts", MS_NOSUID|MS_NOEXEC,
79 "newinstance,ptmxmode=0666,mode=620", },
80
81 // Create a new /dev/shm
82 { "pakfire_dev_shm", "dev/shm", "tmpfs",
83 MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=1024m", },
84
85 // Mount /dev/mqueue
86 { "mqueue", "dev/mqueue", "mqueue",
87 MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL },
88
89 // Create a new /run
90 { "pakfire_run", "run", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
91 "mode=755,size=256m,nr_inodes=1k", },
92
93 // Create a new /tmp
94 { "pakfire_tmp", "tmp", "tmpfs",
95 MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=4096m", },
96
97 // The end
98 { NULL },
99 };
100
101 static const struct pakfire_devnode {
102 const char* path;
103 int major;
104 int minor;
105 mode_t mode;
106 int flags;
107 } devnodes[] = {
108 { "/dev/null", 1, 3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
109 { "/dev/zero", 1, 5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
110 { "/dev/full", 1, 7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
111 { "/dev/random", 1, 8, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
112 { "/dev/urandom", 1, 9, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
113 { "/dev/kmsg", 1, 11, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
114 { "/dev/tty", 5, 0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
115 { "/dev/console", 5, 1, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
116 { "/dev/rtc0", 252, 0, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
117
118 // Loop Devices
119 { "/dev/loop-control", 10, 237, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
120 { "/dev/loop0", 7, 0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
121 { "/dev/loop1", 7, 1, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
122 { "/dev/loop2", 7, 2, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
123 { "/dev/loop3", 7, 3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
124 { "/dev/loop4", 7, 4, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
125 { "/dev/loop5", 7, 5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
126 { "/dev/loop6", 7, 6, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
127 { "/dev/loop7", 7, 7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
128
129 { NULL },
130 };
131
132 static const struct pakfire_symlink {
133 const char* target;
134 const char* path;
135 } symlinks[] = {
136 { "/dev/pts/ptmx", "/dev/ptmx", },
137 { "/proc/self/fd", "/dev/fd", },
138 { "/proc/self/fd/0", "/dev/stdin" },
139 { "/proc/self/fd/1", "/dev/stdout" },
140 { "/proc/self/fd/2", "/dev/stderr" },
141 { "/proc/kcore", "/dev/core" },
142 { NULL },
143 };
144
145 int pakfire_mount_change_propagation(struct pakfire_ctx* ctx, const char* path, int propagation) {
146 CTX_DEBUG(ctx, "Changing mount propagation on %s\n", path);
147
148 int r = mount(NULL, path, NULL, propagation|MS_REC, NULL);
149 if (r)
150 CTX_ERROR(ctx, "Failed to change mount propagation on %s: %m\n", path);
151
152 return r;
153 }
154
155 static int pakfire_mount_is_mountpoint(struct pakfire* pakfire, const char* path) {
156 // XXX THIS STILL NEEDS TO BE IMPLEMENTED
157 return 1;
158 }
159
160 int pakfire_mount_make_mounpoint(struct pakfire* pakfire, const char* path) {
161 int r;
162
163 // Check if path already is a mountpoint
164 r = pakfire_mount_is_mountpoint(pakfire, path);
165 switch (r) {
166 // Already is a mountpoint
167 case 0:
168 return 0;
169
170 // Is not a mountpoint
171 case 1:
172 break;
173
174 default:
175 ERROR(pakfire, "Could not determine whether %s is a mountpoint: %m\n", path);
176 return r;
177 }
178
179 // Bind-mount to self
180 r = mount(path, path, NULL, MS_BIND|MS_REC, NULL);
181 if (r) {
182 ERROR(pakfire, "Could not make %s a mountpoint: %m\n", path);
183 return r;
184 }
185
186 return 0;
187 }
188
189 static int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target,
190 const char* fstype, unsigned long mflags, const void* data) {
191 const char* options = (const char*)data;
192
193 // Check for some basic inputs
194 if (!source || !target) {
195 errno = EINVAL;
196 return 1;
197 }
198
199 DEBUG(pakfire, "Mounting %s from %s (%s - %s)\n", target, source, fstype, options);
200
201 // Perform mount()
202 int r = mount(source, target, fstype, mflags, data);
203 if (r) {
204 ERROR(pakfire, "Could not mount %s: %m\n", target);
205 }
206
207 return r;
208 }
209
210 static int __pakfire_mount_list(char* line, size_t length, void* data) {
211 struct pakfire_ctx* ctx = data;
212
213 // Send the line to the logger
214 CTX_DEBUG(ctx, " %.*s", (int)length, line);
215
216 return 0;
217 }
218
219 int pakfire_mount_list(struct pakfire_ctx* ctx) {
220 CTX_DEBUG(ctx, "Mountpoints:\n");
221
222 return pakfire_parse_file("/proc/self/mounts", __pakfire_mount_list, ctx);
223 }
224
225 static int pakfire_populate_dev(struct pakfire* pakfire, int flags) {
226 char path[PATH_MAX];
227
228 // Create device nodes
229 for (const struct pakfire_devnode* devnode = devnodes; devnode->path; devnode++) {
230 DEBUG(pakfire, "Creating device node %s\n", devnode->path);
231
232 // Check if flags match
233 if (devnode->flags && !(flags & devnode->flags))
234 continue;
235
236 int r = pakfire_path(pakfire, path, "%s", devnode->path);
237 if (r)
238 return r;
239
240 dev_t dev = makedev(devnode->major, devnode->minor);
241
242 r = mknod(path, devnode->mode, dev);
243
244 // Continue if mknod was successful
245 if (r == 0)
246 continue;
247
248 // If we could not create the device node because of permission issues,
249 // it might be likely that we are running in a user namespace where creating
250 // device nodes is not permitted. Try bind-mounting them.
251 if (errno == EPERM)
252 goto MOUNT;
253
254 // Otherwise log an error and end
255 ERROR(pakfire, "Could not create %s: %m\n", devnode->path);
256 return r;
257
258 MOUNT:
259 // Create an empty file
260 r = pakfire_touch(path, 0444);
261 if (r) {
262 ERROR(pakfire, "Could not create %s: %m\n", path);
263 return r;
264 }
265
266 // Create a bind-mount over the file
267 r = pakfire_mount(pakfire, devnode->path, path, "bind", MS_BIND, NULL);
268 if (r)
269 return r;
270 }
271
272 // Create symlinks
273 for (const struct pakfire_symlink* s = symlinks; s->target; s++) {
274 DEBUG(pakfire, "Creating symlink %s -> %s\n", s->path, s->target);
275
276 int r = pakfire_path(pakfire, path, "%s", s->path);
277 if (r)
278 return r;
279
280 r = symlink(s->target, path);
281 if (r) {
282 ERROR(pakfire, "Could not create symlink %s: %m\n", s->path);
283 return r;
284 }
285 }
286
287 return 0;
288 }
289
290 static int pakfire_mount_interpreter(struct pakfire* pakfire) {
291 char target[PATH_MAX];
292
293 // Fetch the target architecture
294 const char* arch = pakfire_get_effective_arch(pakfire);
295
296 // Can we emulate this architecture?
297 char* interpreter = pakfire_arch_find_interpreter(arch);
298
299 // No interpreter required
300 if (!interpreter)
301 return 0;
302
303 DEBUG(pakfire, "Mounting interpreter %s for %s\n", interpreter, arch);
304
305 // Where to mount this?
306 int r = pakfire_path(pakfire, target, "%s", interpreter);
307 if (r)
308 return r;
309
310 // Create directory
311 r = pakfire_mkparentdir(target, 0755);
312 if (r)
313 return r;
314
315 // Create an empty file
316 FILE* f = fopen(target, "w");
317 if (!f)
318 return 1;
319 fclose(f);
320
321 r = pakfire_mount(pakfire, interpreter, target, NULL, MS_BIND|MS_RDONLY, NULL);
322 if (r)
323 ERROR(pakfire, "Could not mount interpreter %s to %s: %m\n", interpreter, target);
324
325 return r;
326 }
327
328 int pakfire_mount_all(struct pakfire* pakfire, int flags) {
329 char target[PATH_MAX];
330 int r;
331
332 // Fetch Pakfire's root directory
333 const char* root = pakfire_get_path(pakfire);
334
335 for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
336 // Figure out where to mount
337 r = pakfire_path_append(target, root, mp->target);
338 if (r)
339 return r;
340
341 // Create target if it doesn't exist
342 if (!pakfire_path_exists(target)) {
343 r = pakfire_mkdir(target, 0755);
344 if (r) {
345 ERROR(pakfire, "Could not create %s: %m\n", target);
346 return r;
347 }
348 }
349
350 // Perform mount()
351 r = pakfire_mount(pakfire, mp->source, target, mp->fstype, mp->flags, mp->options);
352 if (r)
353 return r;
354 }
355
356 // Populate /dev
357 r = pakfire_populate_dev(pakfire, flags);
358 if (r)
359 return r;
360
361 // Mount the interpreter (if needed)
362 r = pakfire_mount_interpreter(pakfire);
363 if (r)
364 return r;
365
366 return 0;
367 }
368
369 int pakfire_make_ramdisk(struct pakfire* pakfire, char* path, const char* args) {
370 int r;
371
372 // Create a new temporary directory
373 char* p = pakfire_mkdtemp(path);
374 if (!p)
375 return -errno;
376
377 // Mount the ramdisk
378 r = pakfire_mount(pakfire, "pakfire_ramdisk", p, "tmpfs", 0, args);
379 if (r) {
380 ERROR_ERRNO(pakfire, r, "Could not mount ramdisk at %s (%s): %m\n", p, args);
381 return r;
382 }
383
384 DEBUG(pakfire, "Ramdisk mounted at %s (%s)\n", p, args);
385
386 return 0;
387 }
388
389 int pakfire_bind(struct pakfire* pakfire, const char* src, const char* dst, int flags) {
390 struct stat st;
391 char mountpoint[PATH_MAX];
392
393 if (!dst)
394 dst = src;
395
396 int r = pakfire_path(pakfire, mountpoint, "%s", dst);
397 if (r)
398 return r;
399
400 DEBUG(pakfire, "Bind-mounting %s to %s\n", src, mountpoint);
401
402 r = stat(src, &st);
403 if (r < 0) {
404 ERROR(pakfire, "Could not stat %s: %m\n", src);
405 return 1;
406 }
407
408 // Make sure the mountpoint exists
409 switch (st.st_mode & S_IFMT) {
410 case S_IFDIR:
411 r = pakfire_mkdir(mountpoint, st.st_mode);
412 if (r && errno != EEXIST)
413 return r;
414 break;
415
416 case S_IFREG:
417 case S_IFLNK:
418 // Make parent directory
419 r = pakfire_mkparentdir(mountpoint, 0755);
420 if (r)
421 return r;
422
423 // Create a file
424 FILE* f = fopen(mountpoint, "w");
425 if (!f)
426 return 1;
427 fclose(f);
428 break;
429
430 default:
431 errno = ENOTSUP;
432 return 1;
433 }
434
435 // The Linux kernel seems to be quite funny when trying to bind-mount something
436 // as read-only and requires us to mount the source first, and then remount it
437 // again using MS_RDONLY.
438 if (flags & MS_RDONLY) {
439 r = pakfire_mount(pakfire, src, mountpoint, "bind", MS_BIND|MS_REC, NULL);
440 if (r)
441 return r;
442
443 // Add the remount flag
444 flags |= MS_REMOUNT;
445 }
446
447 // Perform mount
448 return pakfire_mount(pakfire, src, mountpoint, "bind", flags|MS_BIND|MS_REC, NULL);
449 }