]>
git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/mount.c
70a323a0d17f7cc02d3adff1636e159d58a1ee49
1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2022 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
22 #include <linux/limits.h>
24 #include <sys/mount.h>
26 #include <sys/sysmacros.h>
27 #include <sys/types.h>
30 #include <libmount/libmount.h>
32 #include <pakfire/arch.h>
33 #include <pakfire/logging.h>
34 #include <pakfire/pakfire.h>
35 #include <pakfire/private.h>
36 #include <pakfire/mount.h>
37 #include <pakfire/util.h>
39 static const struct pakfire_mountpoint
{
46 // Mount a new instance of /proc
47 { "pakfire_proc", "proc", "proc",
48 MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
, },
50 // Make /proc/sys read-only (except /proc/sys/net)
51 { "/proc/sys", "proc/sys", "bind", MS_BIND
|MS_REC
, NULL
, },
52 { "/proc/sys/net", "proc/sys/net", "bind", MS_BIND
|MS_REC
, NULL
, },
53 { "/proc/sys", "proc/sys", "bind",
54 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
, },
56 // Deny write access to /proc/sysrq-trigger (can be used to restart the host)
57 { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind", MS_BIND
|MS_REC
, NULL
, },
58 { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind",
59 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
, },
61 // Make /proc/irq read-only
62 { "/proc/irq", "proc/irq", "bind", MS_BIND
|MS_REC
, NULL
, },
63 { "/proc/irq", "proc/irq", "bind",
64 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
, },
66 // Make /proc/bus read-only
67 { "/proc/bus", "proc/bus", "bind", MS_BIND
|MS_REC
, NULL
, },
68 { "/proc/bus", "proc/bus", "bind",
69 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
, },
71 // Bind-Mount /sys ready-only
72 { "/sys", "sys", "bind", MS_BIND
|MS_REC
, NULL
, },
73 { "/sys", "sys", "bind",
74 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
, },
77 { "pakfire_dev", "dev", "tmpfs", MS_NOSUID
|MS_NOEXEC
,
78 "mode=0755,size=4m,nr_inodes=64k", },
79 { "/dev/pts", "dev/pts", "bind", MS_BIND
, NULL
, },
82 { "mqueue", "dev/mqueue", "mqueue",
83 MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
},
86 { "pakfire_tmpfs", "run", "tmpfs", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
,
87 "mode=755,size=4m,nr_inodes=1k", },
93 static const struct pakfire_devnode
{
99 { "/dev/null", 1, 3, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, },
100 { "/dev/zero", 1, 5, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, },
101 { "/dev/full", 1, 7, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, },
102 { "/dev/random", 1, 8, S_IFCHR
|S_IRUSR
|S_IRGRP
|S_IROTH
, },
103 { "/dev/urandom", 1, 9, S_IFCHR
|S_IRUSR
|S_IRGRP
|S_IROTH
, },
104 { "/dev/kmsg", 1, 11, S_IFCHR
|S_IRUSR
|S_IRGRP
|S_IROTH
, },
105 { "/dev/tty", 5, 0, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, },
106 { "/dev/console", 5, 1, S_IFCHR
|S_IRUSR
|S_IWUSR
, },
107 { "/dev/ptmx", 5, 2, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, },
108 { "/dev/rtc0", 252, 0, S_IFCHR
|S_IRUSR
|S_IWUSR
, },
112 static const struct pakfire_symlink
{
116 { "/proc/self/fd", "/dev/fd", },
117 { "/proc/self/fd/0", "/dev/stdin" },
118 { "/proc/self/fd/1", "/dev/stdout" },
119 { "/proc/self/fd/2", "/dev/stderr" },
120 { "/proc/kcore", "/dev/core" },
125 Easy way to iterate through all mountpoints
127 static int pakfire_mount_foreach(struct pakfire
* pakfire
, int direction
,
128 int (*callback
)(struct pakfire
* pakfire
, struct libmnt_fs
* fs
, const void* data
),
130 const char* root
= pakfire_get_path(pakfire
);
133 struct libmnt_iter
* iterator
= NULL
;
134 struct libmnt_table
* tab
= NULL
;
135 struct libmnt_fs
* fs
= NULL
;
137 // Create an iterator
138 iterator
= mnt_new_iter(direction
);
140 ERROR(pakfire
, "Could not setup iterator: %m\n");
145 tab
= mnt_new_table_from_file("/proc/mounts");
147 ERROR(pakfire
, "Could not open /proc/mounts: %m\n");
151 while (mnt_table_next_fs(tab
, iterator
, &fs
) == 0) {
152 const char* target
= mnt_fs_get_target(fs
);
154 // Ignore any mointpoints that don't belong to us
155 if (!pakfire_string_startswith(target
, root
))
158 // Call the callback for each relevant mountpoint
159 r
= callback(pakfire
, fs
, data
);
169 mnt_unref_table(tab
);
171 mnt_free_iter(iterator
);
176 static int __pakfire_is_mountpoint(struct pakfire
* pakfire
,
177 struct libmnt_fs
* fs
, const void* data
) {
178 const char* path
= (const char*)data
;
180 return mnt_fs_streq_target(fs
, path
);
183 int pakfire_is_mountpoint(struct pakfire
* pakfire
, const char* path
) {
184 return pakfire_mount_foreach(pakfire
, MNT_ITER_FORWARD
,
185 __pakfire_is_mountpoint
, path
);
188 static int pakfire_mount(struct pakfire
* pakfire
, const char* source
, const char* target
,
189 const char* fstype
, unsigned long mflags
, const void* data
) {
190 const char* options
= (const char*)data
;
192 // Check for some basic inputs
193 if (!source
|| !target
) {
198 DEBUG(pakfire
, "Mounting %s from %s (%s - %s)\n", target
, source
, fstype
, options
);
201 int r
= mount(source
, target
, fstype
, mflags
, data
);
203 ERROR(pakfire
, "Could not mount %s: %m\n", target
);
209 static int pakfire_umount(struct pakfire
* pakfire
, const char* path
, int flags
) {
212 DEBUG(pakfire
, "Umounting %s\n", path
);
216 r
= umount2(path
, flags
);
218 // Attempt a lazy umount when busy
219 if (errno
== EBUSY
) {
224 ERROR(pakfire
, "Could not umount %s: %m\n", path
);
230 static int __pakfire_mount_print(struct pakfire
* pakfire
,
231 struct libmnt_fs
* fs
, const void* data
) {
234 mnt_fs_get_source(fs
),
235 mnt_fs_get_target(fs
),
236 mnt_fs_get_fstype(fs
),
237 mnt_fs_get_fs_options(fs
)
243 int pakfire_mount_list(struct pakfire
* pakfire
) {
244 DEBUG(pakfire
, "Mountpoints:\n");
246 return pakfire_mount_foreach(pakfire
, MNT_ITER_FORWARD
,
247 __pakfire_mount_print
, NULL
);
250 static int pakfire_populate_dev(struct pakfire
* pakfire
) {
253 // Create device nodes
254 for (const struct pakfire_devnode
* devnode
= devnodes
; devnode
->path
; devnode
++) {
255 DEBUG(pakfire
, "Creating device node %s\n", devnode
->path
);
257 int r
= pakfire_make_path(pakfire
, path
, devnode
->path
);
261 dev_t dev
= makedev(devnode
->major
, devnode
->minor
);
263 r
= mknod(path
, devnode
->mode
, dev
);
265 // Continue if mknod was successful
269 // If we could not create the device node because of permission issues,
270 // it might be likely that we are running in a user namespace where creating
271 // device nodes is not permitted. Try bind-mounting them.
275 // Otherwise log an error and end
276 ERROR(pakfire
, "Could not create %s: %m\n", devnode
->path
);
280 // Create an empty file
281 r
= pakfire_touch(path
, 0444);
283 ERROR(pakfire
, "Could not create %s: %m\n", path
);
287 // Create a bind-mount over the file
288 r
= pakfire_mount(pakfire
, path
, devnode
->path
, "bind", MS_BIND
, NULL
);
294 for (const struct pakfire_symlink
* s
= symlinks
; s
->target
; s
++) {
295 DEBUG(pakfire
, "Creating symlink %s -> %s\n", s
->path
, s
->target
);
297 int r
= pakfire_make_path(pakfire
, path
, s
->path
);
301 r
= symlink(s
->target
, path
);
303 ERROR(pakfire
, "Could not create symlink %s: %m\n", s
->path
);
311 static int pakfire_mount_interpreter(struct pakfire
* pakfire
) {
312 char target
[PATH_MAX
];
314 // Fetch the target architecture
315 const char* arch
= pakfire_get_arch(pakfire
);
317 // Can we emulate this architecture?
318 char* interpreter
= pakfire_arch_find_interpreter(arch
);
320 // No interpreter required
324 DEBUG(pakfire
, "Mounting interpreter %s for %s\n", interpreter
, arch
);
326 // Where to mount this?
327 int r
= pakfire_make_path(pakfire
, target
, interpreter
);
332 r
= pakfire_mkparentdir(target
, 0755);
336 // Create an empty file
337 FILE* f
= fopen(target
, "w");
342 r
= pakfire_mount(pakfire
, interpreter
, target
, NULL
, MS_BIND
|MS_RDONLY
, NULL
);
344 ERROR(pakfire
, "Could not mount interpreter %s to %s: %m\n", interpreter
, target
);
349 int pakfire_mount_all(struct pakfire
* pakfire
) {
350 char target
[PATH_MAX
];
353 // Fetch Pakfire's root directory
354 const char* root
= pakfire_get_path(pakfire
);
356 for (const struct pakfire_mountpoint
* mp
= mountpoints
; mp
->source
; mp
++) {
357 // Figure out where to mount
358 r
= pakfire_path_join(target
, root
, mp
->target
);
363 pakfire_mkdir(target
, 0755);
367 r
= pakfire_mount(pakfire
, mp
->source
, target
, mp
->fstype
, mp
->flags
, mp
->options
);
369 // If the target directory does not exist, we will create it
370 if (errno
== ENOENT
) {
371 r
= pakfire_mkdir(target
, 0755);
373 ERROR(pakfire
, "Could not create %s\n", target
);
385 r
= pakfire_populate_dev(pakfire
);
389 // Mount the interpreter (if needed)
390 r
= pakfire_mount_interpreter(pakfire
);
397 static int __pakfire_umount(struct pakfire
* pakfire
,
398 struct libmnt_fs
* fs
, const void* data
) {
399 const char* target
= mnt_fs_get_target(fs
);
403 return pakfire_umount(pakfire
, target
, 0);
407 umounts everything that hasn't been umounted, yet
409 int pakfire_umount_all(struct pakfire
* pakfire
) {
410 return pakfire_mount_foreach(pakfire
, MNT_ITER_BACKWARD
,
411 __pakfire_umount
, NULL
);
414 PAKFIRE_EXPORT
int pakfire_bind(struct pakfire
* pakfire
, const char* src
, const char* dst
, int flags
) {
416 char mountpoint
[PATH_MAX
];
421 int r
= pakfire_make_path(pakfire
, mountpoint
, dst
);
425 DEBUG(pakfire
, "Bind-mounting %s to %s\n", src
, mountpoint
);
429 ERROR(pakfire
, "Could not stat %s: %m\n", src
);
433 // Make sure the mountpoint exists
434 switch (st
.st_mode
& S_IFMT
) {
436 r
= pakfire_mkdir(mountpoint
, st
.st_mode
);
437 if (r
&& errno
!= EEXIST
)
443 // Make parent directory
444 r
= pakfire_mkparentdir(mountpoint
, 0755);
449 FILE* f
= fopen(mountpoint
, "w");
461 return pakfire_mount(pakfire
, src
, mountpoint
, NULL
, flags
|MS_BIND
, NULL
);