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>
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>
38 static const struct pakfire_mountpoint
{
46 // Mount a new instance of /proc
48 PAKFIRE_MNTNS_INNER
|PAKFIRE_MNTNS_OUTER
,
52 MS_NOSUID
|MS_NOEXEC
|MS_NODEV
,
57 XXX it is kind of problematic to mount /proc twice as a process inside the
58 jail can umount /proc and will then see the host's /proc.
61 // Make /proc/sys read-only (except /proc/sys/net)
83 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
,
87 // Deny write access to /proc/sysrq-trigger (can be used to restart the host)
90 "/proc/sysrq-trigger",
98 "/proc/sysrq-trigger",
101 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
,
105 // Make /proc/irq read-only
119 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
,
123 // Make /proc/bus read-only
137 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
,
141 // Bind-Mount /sys ready-only
155 MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
,
166 "mode=0755,size=4m,nr_inodes=64k",
174 "newinstance,ptmxmode=0666,mode=620",
177 // Create a new /dev/shm
183 MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
,
184 "mode=1777,size=1024m",
193 MS_NOSUID
|MS_NOEXEC
|MS_NODEV
,
203 MS_NOSUID
|MS_NOEXEC
|MS_NODEV
,
204 "mode=755,size=256m,nr_inodes=1k",
213 MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
,
214 "mode=1777,size=4096m",
221 static const struct pakfire_devnode
{
228 { "/dev/null", 1, 3, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, 0 },
229 { "/dev/zero", 1, 5, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, 0 },
230 { "/dev/full", 1, 7, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, 0 },
231 { "/dev/random", 1, 8, S_IFCHR
|S_IRUSR
|S_IRGRP
|S_IROTH
, 0 },
232 { "/dev/urandom", 1, 9, S_IFCHR
|S_IRUSR
|S_IRGRP
|S_IROTH
, 0 },
233 { "/dev/kmsg", 1, 11, S_IFCHR
|S_IRUSR
|S_IRGRP
|S_IROTH
, 0 },
234 { "/dev/tty", 5, 0, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
, 0 },
235 { "/dev/rtc0", 252, 0, S_IFCHR
|S_IRUSR
|S_IWUSR
, 0 },
238 { "/dev/loop-control", 10, 237, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
239 { "/dev/loop0", 7, 0, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
240 { "/dev/loop1", 7, 1, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
241 { "/dev/loop2", 7, 2, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
242 { "/dev/loop3", 7, 3, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
243 { "/dev/loop4", 7, 4, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
244 { "/dev/loop5", 7, 5, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
245 { "/dev/loop6", 7, 6, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
246 { "/dev/loop7", 7, 7, S_IFCHR
|S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
, PAKFIRE_MOUNT_LOOP_DEVICES
},
251 static const struct pakfire_symlink
{
255 { "/dev/pts/ptmx", "/dev/ptmx", },
256 { "/proc/self/fd", "/dev/fd", },
257 { "/proc/self/fd/0", "/dev/stdin" },
258 { "/proc/self/fd/1", "/dev/stdout" },
259 { "/proc/self/fd/2", "/dev/stderr" },
260 { "/proc/kcore", "/dev/core" },
264 int pakfire_mount_change_propagation(struct pakfire_ctx
* ctx
, const char* path
, int propagation
) {
265 CTX_DEBUG(ctx
, "Changing mount propagation on %s\n", path
);
267 int r
= mount(NULL
, path
, NULL
, propagation
|MS_REC
, NULL
);
269 CTX_ERROR(ctx
, "Failed to change mount propagation on %s: %m\n", path
);
274 static int pakfire_mount_is_mountpoint(struct pakfire
* pakfire
, const char* path
) {
275 // XXX THIS STILL NEEDS TO BE IMPLEMENTED
279 int pakfire_mount_make_mounpoint(struct pakfire_ctx
* ctx
, struct pakfire
* pakfire
,
283 // Check if path already is a mountpoint
284 r
= pakfire_mount_is_mountpoint(pakfire
, path
);
286 // Already is a mountpoint
290 // Is not a mountpoint
295 CTX_ERROR(ctx
, "Could not determine whether %s is a mountpoint: %m\n", path
);
299 // Bind-mount to self
300 r
= mount(path
, path
, NULL
, MS_BIND
|MS_REC
, NULL
);
302 CTX_ERROR(ctx
, "Could not make %s a mountpoint: %m\n", path
);
309 static int pakfire_mount(struct pakfire_ctx
* ctx
, struct pakfire
* pakfire
, const char* source
,
310 const char* target
, const char* fstype
, unsigned long mflags
, const void* data
) {
311 const char* options
= (const char*)data
;
313 // Check for some basic inputs
314 if (!source
|| !target
) {
319 CTX_DEBUG(ctx
, "Mounting %s from %s (%s - %s)\n", target
, source
, fstype
, options
);
322 int r
= mount(source
, target
, fstype
, mflags
, data
);
324 CTX_ERROR(ctx
, "Could not mount %s: %m\n", target
);
330 static int __pakfire_mount_list(char* line
, size_t length
, void* data
) {
331 struct pakfire_ctx
* ctx
= data
;
333 // Send the line to the logger
334 CTX_DEBUG(ctx
, " %.*s", (int)length
, line
);
339 int pakfire_mount_list(struct pakfire_ctx
* ctx
) {
340 CTX_DEBUG(ctx
, "Mountpoints:\n");
342 return pakfire_parse_file("/proc/self/mounts", __pakfire_mount_list
, ctx
);
345 int pakfire_populate_dev(struct pakfire_ctx
* ctx
, struct pakfire
* pakfire
, int flags
) {
348 // Create device nodes
349 for (const struct pakfire_devnode
* devnode
= devnodes
; devnode
->path
; devnode
++) {
350 CTX_DEBUG(ctx
, "Creating device node %s\n", devnode
->path
);
352 // Check if flags match
353 if (devnode
->flags
&& !(flags
& devnode
->flags
))
356 int r
= pakfire_path(pakfire
, path
, "%s", devnode
->path
);
360 dev_t dev
= makedev(devnode
->major
, devnode
->minor
);
362 r
= mknod(path
, devnode
->mode
, dev
);
364 // Continue if mknod was successful
368 // If we could not create the device node because of permission issues,
369 // it might be likely that we are running in a user namespace where creating
370 // device nodes is not permitted. Try bind-mounting them.
374 // Otherwise log an error and end
375 CTX_ERROR(ctx
, "Could not create %s: %m\n", devnode
->path
);
379 // Create an empty file
380 r
= pakfire_touch(path
, 0444);
382 CTX_ERROR(ctx
, "Could not create %s: %m\n", path
);
386 // Create a bind-mount over the file
387 r
= pakfire_mount(ctx
, pakfire
, devnode
->path
, path
, "bind", MS_BIND
, NULL
);
393 for (const struct pakfire_symlink
* s
= symlinks
; s
->target
; s
++) {
394 CTX_DEBUG(ctx
, "Creating symlink %s -> %s\n", s
->path
, s
->target
);
396 int r
= pakfire_path(pakfire
, path
, "%s", s
->path
);
400 r
= symlink(s
->target
, path
);
402 CTX_ERROR(ctx
, "Could not create symlink %s: %m\n", s
->path
);
410 int pakfire_mount_interpreter(struct pakfire_ctx
* ctx
, struct pakfire
* pakfire
) {
411 char target
[PATH_MAX
];
413 // Fetch the target architecture
414 const char* arch
= pakfire_get_effective_arch(pakfire
);
416 // Can we emulate this architecture?
417 char* interpreter
= pakfire_arch_find_interpreter(arch
);
419 // No interpreter required
423 CTX_DEBUG(ctx
, "Mounting interpreter %s for %s\n", interpreter
, arch
);
425 // Where to mount this?
426 int r
= pakfire_path(pakfire
, target
, "%s", interpreter
);
431 r
= pakfire_mkparentdir(target
, 0755);
435 // Create an empty file
436 FILE* f
= fopen(target
, "w");
441 r
= pakfire_mount(ctx
, pakfire
, interpreter
, target
, NULL
, MS_BIND
|MS_RDONLY
, NULL
);
443 CTX_ERROR(ctx
, "Could not mount interpreter %s to %s: %m\n", interpreter
, target
);
448 int pakfire_mount_all(struct pakfire_ctx
* ctx
, struct pakfire
* pakfire
, pakfire_mntns_t ns
, int flags
) {
449 char target
[PATH_MAX
];
452 const char* root
= "/";
454 // Fetch Pakfire's root directory
455 if (ns
== PAKFIRE_MNTNS_OUTER
)
456 root
= pakfire_get_path(pakfire
);
458 for (const struct pakfire_mountpoint
* mp
= mountpoints
; mp
->source
; mp
++) {
462 // Figure out where to mount
463 r
= pakfire_path_append(target
, root
, mp
->target
);
467 // Create target if it doesn't exist
468 if (!pakfire_path_exists(target
)) {
469 r
= pakfire_mkdir(target
, 0755);
471 CTX_ERROR(ctx
, "Could not create %s: %m\n", target
);
477 r
= pakfire_mount(ctx
, pakfire
, mp
->source
, target
,
478 mp
->fstype
, mp
->flags
, mp
->options
);
486 int pakfire_make_ramdisk(struct pakfire_ctx
* ctx
, struct pakfire
* pakfire
,
487 char* path
, const char* args
) {
490 // Create a new temporary directory
491 char* p
= pakfire_mkdtemp(path
);
496 r
= pakfire_mount(ctx
, pakfire
, "pakfire_ramdisk", p
, "tmpfs", 0, args
);
498 CTX_ERROR(ctx
, "Could not mount ramdisk at %s (%s): %s\n",
499 p
, args
, strerror(errno
));
503 CTX_DEBUG(ctx
, "Ramdisk mounted at %s (%s)\n", p
, args
);
508 int pakfire_bind(struct pakfire_ctx
* ctx
, struct pakfire
* pakfire
,
509 const char* src
, const char* dst
, int flags
) {
511 char mountpoint
[PATH_MAX
];
516 int r
= pakfire_path(pakfire
, mountpoint
, "%s", dst
);
520 CTX_DEBUG(ctx
, "Bind-mounting %s to %s\n", src
, mountpoint
);
524 CTX_ERROR(ctx
, "Could not stat %s: %m\n", src
);
528 // Make sure the mountpoint exists
529 switch (st
.st_mode
& S_IFMT
) {
531 r
= pakfire_mkdir(mountpoint
, st
.st_mode
);
532 if (r
&& errno
!= EEXIST
)
538 // Make parent directory
539 r
= pakfire_mkparentdir(mountpoint
, 0755);
544 FILE* f
= fopen(mountpoint
, "w");
555 // The Linux kernel seems to be quite funny when trying to bind-mount something
556 // as read-only and requires us to mount the source first, and then remount it
557 // again using MS_RDONLY.
558 if (flags
& MS_RDONLY
) {
559 r
= pakfire_mount(ctx
, pakfire
, src
, mountpoint
, "bind", MS_BIND
|MS_REC
, NULL
);
563 // Add the remount flag
568 return pakfire_mount(ctx
, pakfire
, src
, mountpoint
, "bind", flags
|MS_BIND
|MS_REC
, NULL
);