]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/mount.c
dist: Drop our own source mirror
[people/ms/pakfire.git] / src / libpakfire / mount.c
CommitLineData
bf5f9c24
MT
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>
bf5f9c24
MT
23#include <stddef.h>
24#include <sys/mount.h>
25#include <sys/stat.h>
f71c82d8 26#include <sys/sysmacros.h>
bf5f9c24
MT
27#include <sys/types.h>
28
29// libmount
30#include <libmount/libmount.h>
31
660120b6 32#include <pakfire/arch.h>
bf5f9c24
MT
33#include <pakfire/logging.h>
34#include <pakfire/pakfire.h>
35#include <pakfire/mount.h>
d973a13d 36#include <pakfire/string.h>
bf5f9c24
MT
37#include <pakfire/util.h>
38
39static const struct pakfire_mountpoint {
40 const char* source;
41 const char* target;
42 const char* fstype;
43 int flags;
44 const char* options;
bf5f9c24 45} mountpoints[] = {
9ab7dd21
MT
46 // Mount a new instance of /proc
47 { "pakfire_proc", "proc", "proc",
48 MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, },
bf5f9c24 49
9ab7dd21 50 // Make /proc/sys read-only (except /proc/sys/net)
cc752ada
MT
51 { "/proc/sys", "proc/sys", "bind", MS_BIND|MS_REC, NULL, },
52 { "/proc/sys/net", "proc/sys/net", "bind", MS_BIND|MS_REC, NULL, },
9ab7dd21
MT
53 { "/proc/sys", "proc/sys", "bind",
54 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
bf5f9c24 55
9ab7dd21 56 // Deny write access to /proc/sysrq-trigger (can be used to restart the host)
cc752ada 57 { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind", MS_BIND|MS_REC, NULL, },
9ab7dd21
MT
58 { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind",
59 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
60
61 // Make /proc/irq read-only
cc752ada 62 { "/proc/irq", "proc/irq", "bind", MS_BIND|MS_REC, NULL, },
9ab7dd21
MT
63 { "/proc/irq", "proc/irq", "bind",
64 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
65
66 // Make /proc/bus read-only
cc752ada 67 { "/proc/bus", "proc/bus", "bind", MS_BIND|MS_REC, NULL, },
9ab7dd21
MT
68 { "/proc/bus", "proc/bus", "bind",
69 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
70
71 // Bind-Mount /sys ready-only
cc752ada 72 { "/sys", "sys", "bind", MS_BIND|MS_REC, NULL, },
9ab7dd21
MT
73 { "/sys", "sys", "bind",
74 MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
bf5f9c24
MT
75
76 // Create a new /dev
cc752ada 77 { "pakfire_dev", "dev", "tmpfs", MS_NOSUID|MS_NOEXEC,
9ab7dd21 78 "mode=0755,size=4m,nr_inodes=64k", },
0675c514
MT
79 { "pakfire_dev_pts", "dev/pts", "devpts", MS_NOSUID|MS_NOEXEC,
80 "newinstance,ptmxmode=0666,mode=620", },
cc752ada 81
99affb62
MT
82 // Create a new /dev/shm
83 { "pakfire_dev_shm", "dev/shm", "tmpfs",
84 MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=1024m", },
85
cc752ada
MT
86 // Mount /dev/mqueue
87 { "mqueue", "dev/mqueue", "mqueue",
88 MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL },
bf5f9c24
MT
89
90 // Create a new /run
b1a6d98c
MT
91 { "pakfire_run", "run", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
92 "mode=755,size=256m,nr_inodes=1k", },
93
94 // Create a new /tmp
95 { "pakfire_tmp", "tmp", "tmpfs",
84e5948e 96 MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=4096m", },
bf5f9c24 97
bf5f9c24
MT
98 // The end
99 { NULL },
100};
101
f71c82d8
MT
102static const struct pakfire_devnode {
103 const char* path;
104 int major;
105 int minor;
106 mode_t mode;
282b732a 107 int flags;
f71c82d8 108} devnodes[] = {
282b732a
MT
109 { "/dev/null", 1, 3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
110 { "/dev/zero", 1, 5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
111 { "/dev/full", 1, 7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
112 { "/dev/random", 1, 8, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
113 { "/dev/urandom", 1, 9, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
114 { "/dev/kmsg", 1, 11, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
115 { "/dev/tty", 5, 0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
116 { "/dev/console", 5, 1, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
117 { "/dev/rtc0", 252, 0, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
118
119 // Loop Devices
120 { "/dev/loop-control", 10, 237, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
121 { "/dev/loop0", 7, 0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
122 { "/dev/loop1", 7, 1, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
123 { "/dev/loop2", 7, 2, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
124 { "/dev/loop3", 7, 3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
125 { "/dev/loop4", 7, 4, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
126 { "/dev/loop5", 7, 5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
127 { "/dev/loop6", 7, 6, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
128 { "/dev/loop7", 7, 7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
129
f71c82d8
MT
130 { NULL },
131};
dc01afb6 132
f71c82d8
MT
133static const struct pakfire_symlink {
134 const char* target;
135 const char* path;
136} symlinks[] = {
0675c514 137 { "/dev/pts/ptmx", "/dev/ptmx", },
f71c82d8
MT
138 { "/proc/self/fd", "/dev/fd", },
139 { "/proc/self/fd/0", "/dev/stdin" },
140 { "/proc/self/fd/1", "/dev/stdout" },
141 { "/proc/self/fd/2", "/dev/stderr" },
142 { "/proc/kcore", "/dev/core" },
143 { NULL },
144};
dc01afb6 145
9f558f7d
MT
146int pakfire_mount_change_propagation(struct pakfire* pakfire, int propagation, const char* path) {
147 DEBUG(pakfire, "Changing mount propagation on %s\n", path);
148
149 int r = mount(NULL, path, NULL, propagation|MS_REC, NULL);
150 if (r)
151 ERROR(pakfire, "Failed to change mount propagation on %s: %m\n", path);
152
153 return r;
154}
155
14df7388
MT
156static int pakfire_mount_is_mountpoint(struct pakfire* pakfire, const char* path) {
157 // XXX THIS STILL NEEDS TO BE IMPLEMENTED
158 return 1;
159}
160
161int pakfire_mount_make_mounpoint(struct pakfire* pakfire, const char* path) {
162 int r;
163
164 // Check if path already is a mountpoint
165 r = pakfire_mount_is_mountpoint(pakfire, path);
166 switch (r) {
167 // Already is a mountpoint
168 case 0:
169 return 0;
170
171 // Is not a mountpoint
172 case 1:
173 break;
174
175 default:
176 ERROR(pakfire, "Could not determine whether %s is a mountpoint: %m\n", path);
177 return r;
178 }
179
180 // Bind-mount to self
181 r = mount(path, path, NULL, MS_BIND|MS_REC, NULL);
182 if (r) {
183 ERROR(pakfire, "Could not make %s a mountpoint: %m\n", path);
184 return r;
185 }
186
187 return 0;
188}
189
bf5f9c24
MT
190/*
191 Easy way to iterate through all mountpoints
192*/
0ea82518
MT
193static int pakfire_mount_foreach(struct pakfire* pakfire, int direction,
194 int (*callback)(struct pakfire* pakfire, struct libmnt_fs* fs, const void* data),
f71c82d8 195 const void* data) {
bf5f9c24 196 const char* root = pakfire_get_path(pakfire);
ec64a612 197 int r = 0;
bf5f9c24 198
0ea82518
MT
199 struct libmnt_iter* iterator = NULL;
200 struct libmnt_table* tab = NULL;
201 struct libmnt_fs* fs = NULL;
202
203 // Create an iterator
204 iterator = mnt_new_iter(direction);
205 if (!iterator) {
206 ERROR(pakfire, "Could not setup iterator: %m\n");
207 goto ERROR;
208 }
209
210 // Read /proc/mounts
211 tab = mnt_new_table_from_file("/proc/mounts");
212 if (!tab) {
bf5f9c24 213 ERROR(pakfire, "Could not open /proc/mounts: %m\n");
0ea82518 214 goto ERROR;
bf5f9c24
MT
215 }
216
0ea82518
MT
217 while (mnt_table_next_fs(tab, iterator, &fs) == 0) {
218 const char* target = mnt_fs_get_target(fs);
bf5f9c24
MT
219
220 // Ignore any mointpoints that don't belong to us
0ea82518 221 if (!pakfire_string_startswith(target, root))
bf5f9c24
MT
222 continue;
223
bf5f9c24 224 // Call the callback for each relevant mountpoint
0ea82518 225 r = callback(pakfire, fs, data);
bf5f9c24
MT
226 if (r)
227 break;
228 }
229
0ea82518 230ERROR:
bf5f9c24 231 // Tidy up
0ea82518
MT
232 if (fs)
233 mnt_unref_fs(fs);
234 if (tab)
235 mnt_unref_table(tab);
236 if (iterator)
237 mnt_free_iter(iterator);
bf5f9c24
MT
238
239 return r;
240}
241
242static int __pakfire_is_mountpoint(struct pakfire* pakfire,
0ea82518 243 struct libmnt_fs* fs, const void* data) {
bf5f9c24
MT
244 const char* path = (const char*)data;
245
0ea82518 246 return mnt_fs_streq_target(fs, path);
bf5f9c24
MT
247}
248
249int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path) {
0ea82518 250 return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
f71c82d8 251 __pakfire_is_mountpoint, path);
bf5f9c24
MT
252}
253
163851bc 254static int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target,
bf5f9c24
MT
255 const char* fstype, unsigned long mflags, const void* data) {
256 const char* options = (const char*)data;
bf5f9c24
MT
257
258 // Check for some basic inputs
259 if (!source || !target) {
260 errno = EINVAL;
261 return 1;
262 }
263
7f970e6e 264 DEBUG(pakfire, "Mounting %s from %s (%s - %s)\n", target, source, fstype, options);
bf5f9c24 265
7f970e6e
MT
266 // Perform mount()
267 int r = mount(source, target, fstype, mflags, data);
bf5f9c24 268 if (r) {
7f970e6e 269 ERROR(pakfire, "Could not mount %s: %m\n", target);
bf5f9c24
MT
270 }
271
bf5f9c24
MT
272 return r;
273}
274
bf5f9c24 275static int __pakfire_mount_print(struct pakfire* pakfire,
0ea82518
MT
276 struct libmnt_fs* fs, const void* data) {
277 DEBUG(pakfire,
278 " %s %s %s %s\n",
279 mnt_fs_get_source(fs),
280 mnt_fs_get_target(fs),
281 mnt_fs_get_fstype(fs),
282 mnt_fs_get_fs_options(fs)
bf5f9c24
MT
283 );
284
285 return 0;
286}
287
91247a7b
MT
288int pakfire_mount_list(struct pakfire* pakfire) {
289 DEBUG(pakfire, "Mountpoints:\n");
bf5f9c24 290
0ea82518 291 return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
f71c82d8
MT
292 __pakfire_mount_print, NULL);
293}
294
282b732a 295static int pakfire_populate_dev(struct pakfire* pakfire, int flags) {
f71c82d8
MT
296 char path[PATH_MAX];
297
298 // Create device nodes
299 for (const struct pakfire_devnode* devnode = devnodes; devnode->path; devnode++) {
300 DEBUG(pakfire, "Creating device node %s\n", devnode->path);
301
282b732a
MT
302 // Check if flags match
303 if (devnode->flags && !(flags & devnode->flags))
304 continue;
305
77e26129
MT
306 int r = pakfire_path(pakfire, path, "%s", devnode->path);
307 if (r)
308 return r;
f71c82d8
MT
309
310 dev_t dev = makedev(devnode->major, devnode->minor);
311
312 r = mknod(path, devnode->mode, dev);
8f1003a3
MT
313
314 // Continue if mknod was successful
315 if (r == 0)
316 continue;
317
318 // If we could not create the device node because of permission issues,
319 // it might be likely that we are running in a user namespace where creating
320 // device nodes is not permitted. Try bind-mounting them.
321 if (errno == EPERM)
322 goto MOUNT;
323
324 // Otherwise log an error and end
325 ERROR(pakfire, "Could not create %s: %m\n", devnode->path);
326 return r;
327
328MOUNT:
329 // Create an empty file
330 r = pakfire_touch(path, 0444);
f71c82d8 331 if (r) {
8f1003a3 332 ERROR(pakfire, "Could not create %s: %m\n", path);
f71c82d8
MT
333 return r;
334 }
8f1003a3
MT
335
336 // Create a bind-mount over the file
64e3b4ff 337 r = pakfire_mount(pakfire, devnode->path, path, "bind", MS_BIND, NULL);
8f1003a3
MT
338 if (r)
339 return r;
f71c82d8
MT
340 }
341
342 // Create symlinks
343 for (const struct pakfire_symlink* s = symlinks; s->target; s++) {
344 DEBUG(pakfire, "Creating symlink %s -> %s\n", s->path, s->target);
345
77e26129
MT
346 int r = pakfire_path(pakfire, path, "%s", s->path);
347 if (r)
348 return r;
f71c82d8
MT
349
350 r = symlink(s->target, path);
351 if (r) {
352 ERROR(pakfire, "Could not create symlink %s: %m\n", s->path);
353 return r;
354 }
355 }
356
357 return 0;
bf5f9c24
MT
358}
359
660120b6
MT
360static int pakfire_mount_interpreter(struct pakfire* pakfire) {
361 char target[PATH_MAX];
362
363 // Fetch the target architecture
652f2a99 364 const char* arch = pakfire_get_effective_arch(pakfire);
660120b6
MT
365
366 // Can we emulate this architecture?
367 char* interpreter = pakfire_arch_find_interpreter(arch);
368
369 // No interpreter required
370 if (!interpreter)
371 return 0;
372
373 DEBUG(pakfire, "Mounting interpreter %s for %s\n", interpreter, arch);
374
375 // Where to mount this?
77e26129
MT
376 int r = pakfire_path(pakfire, target, "%s", interpreter);
377 if (r)
660120b6
MT
378 return r;
379
380 // Create directory
520ce66c 381 r = pakfire_mkparentdir(target, 0755);
660120b6
MT
382 if (r)
383 return r;
384
385 // Create an empty file
386 FILE* f = fopen(target, "w");
387 if (!f)
388 return 1;
389 fclose(f);
390
391 r = pakfire_mount(pakfire, interpreter, target, NULL, MS_BIND|MS_RDONLY, NULL);
392 if (r)
393 ERROR(pakfire, "Could not mount interpreter %s to %s: %m\n", interpreter, target);
394
395 return r;
396}
397
282b732a 398int pakfire_mount_all(struct pakfire* pakfire, int flags) {
bf5f9c24 399 char target[PATH_MAX];
bf5f9c24
MT
400 int r;
401
402 // Fetch Pakfire's root directory
403 const char* root = pakfire_get_path(pakfire);
404
bf5f9c24 405 for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
bf5f9c24
MT
406 // Figure out where to mount
407 r = pakfire_path_join(target, root, mp->target);
56796f84 408 if (r)
bf5f9c24
MT
409 return r;
410
a685a503
MT
411 // Create target if it doesn't exist
412 if (!pakfire_path_exists(target)) {
413 r = pakfire_mkdir(target, 0755);
414 if (r) {
415 ERROR(pakfire, "Could not create %s: %m\n", target);
416 return r;
417 }
418 }
bf5f9c24 419
bf5f9c24 420 // Perform mount()
f71c82d8 421 r = pakfire_mount(pakfire, mp->source, target, mp->fstype, mp->flags, mp->options);
a685a503 422 if (r)
bf5f9c24 423 return r;
bf5f9c24
MT
424 }
425
f71c82d8 426 // Populate /dev
282b732a 427 r = pakfire_populate_dev(pakfire, flags);
f71c82d8
MT
428 if (r)
429 return r;
430
660120b6
MT
431 // Mount the interpreter (if needed)
432 r = pakfire_mount_interpreter(pakfire);
433 if (r)
434 return r;
435
bf5f9c24
MT
436 return 0;
437}
438
06d741c6
MT
439int pakfire_make_ramdisk(struct pakfire* pakfire, char* path, const char* args) {
440 int r;
441
442 // Create a new temporary directory
443 char* p = pakfire_mkdtemp(path);
444 if (!p)
445 return -errno;
446
447 // Mount the ramdisk
448 r = pakfire_mount(pakfire, "pakfire_ramdisk", p, "tmpfs", 0, args);
449 if (r) {
450 ERROR_ERRNO(pakfire, r, "Could not mount ramdisk at %s (%s): %m\n", p, args);
451 return r;
452 }
453
454 DEBUG(pakfire, "Ramdisk mounted at %s (%s)\n", p, args);
455
456 return 0;
457}
458
061223f7 459int pakfire_bind(struct pakfire* pakfire, const char* src, const char* dst, int flags) {
163851bc
MT
460 struct stat st;
461 char mountpoint[PATH_MAX];
462
463 if (!dst)
464 dst = src;
465
77e26129
MT
466 int r = pakfire_path(pakfire, mountpoint, "%s", dst);
467 if (r)
468 return r;
163851bc
MT
469
470 DEBUG(pakfire, "Bind-mounting %s to %s\n", src, mountpoint);
471
472 r = stat(src, &st);
473 if (r < 0) {
474 ERROR(pakfire, "Could not stat %s: %m\n", src);
475 return 1;
476 }
477
478 // Make sure the mountpoint exists
479 switch (st.st_mode & S_IFMT) {
480 case S_IFDIR:
520ce66c 481 r = pakfire_mkdir(mountpoint, st.st_mode);
163851bc
MT
482 if (r && errno != EEXIST)
483 return r;
484 break;
485
486 case S_IFREG:
487 case S_IFLNK:
488 // Make parent directory
520ce66c 489 r = pakfire_mkparentdir(mountpoint, 0755);
163851bc
MT
490 if (r)
491 return r;
492
493 // Create a file
494 FILE* f = fopen(mountpoint, "w");
495 if (!f)
496 return 1;
497 fclose(f);
498 break;
499
500 default:
501 errno = ENOTSUP;
502 return 1;
503 }
504
976fbbc8
MT
505 // The Linux kernel seems to be quite funny when trying to bind-mount something
506 // as read-only and requires us to mount the source first, and then remount it
507 // again using MS_RDONLY.
508 if (flags & MS_RDONLY) {
509 r = pakfire_mount(pakfire, src, mountpoint, NULL, MS_BIND|MS_REC, NULL);
510 if (r)
511 return r;
512
513 // Add the remount flag
514 flags |= MS_REMOUNT;
515 }
516
163851bc 517 // Perform mount
976fbbc8 518 return pakfire_mount(pakfire, src, mountpoint, NULL, flags|MS_BIND|MS_REC, NULL);
163851bc 519}