]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/mount.c
mount: Fix mounting any file systems from the host system
[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>
163851bc 35#include <pakfire/private.h>
bf5f9c24
MT
36#include <pakfire/mount.h>
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
MT
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, },
bf5f9c24 55
9ab7dd21
MT
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, },
60
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, },
65
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, },
70
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, },
bf5f9c24
MT
75
76 // Create a new /dev
9ab7dd21
MT
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, },
bf5f9c24
MT
80
81 // Create a new /run
9ab7dd21 82 { "pakfire_tmpfs", "run", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
f71c82d8 83 "mode=755,size=4m,nr_inodes=1k", },
bf5f9c24 84
bf5f9c24
MT
85 // The end
86 { NULL },
87};
88
f71c82d8
MT
89static const struct pakfire_devnode {
90 const char* path;
91 int major;
92 int minor;
93 mode_t mode;
94} devnodes[] = {
95 { "/dev/null", 1, 3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
96 { "/dev/zero", 1, 5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
97 { "/dev/full", 1, 7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
98 { "/dev/random", 1, 8, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
99 { "/dev/urandom", 1, 9, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
100 { "/dev/kmsg", 1, 11, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
101 { "/dev/tty", 5, 0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
102 { "/dev/console", 5, 1, S_IFCHR|S_IRUSR|S_IWUSR, },
103 { "/dev/ptmx", 5, 2, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
104 { "/dev/rtc0", 252, 0, S_IFCHR|S_IRUSR|S_IWUSR, },
105 { NULL },
106};
dc01afb6 107
f71c82d8
MT
108static const struct pakfire_symlink {
109 const char* target;
110 const char* path;
111} symlinks[] = {
112 { "/proc/self/fd", "/dev/fd", },
113 { "/proc/self/fd/0", "/dev/stdin" },
114 { "/proc/self/fd/1", "/dev/stdout" },
115 { "/proc/self/fd/2", "/dev/stderr" },
116 { "/proc/kcore", "/dev/core" },
117 { NULL },
118};
dc01afb6 119
bf5f9c24
MT
120/*
121 Easy way to iterate through all mountpoints
122*/
0ea82518
MT
123static int pakfire_mount_foreach(struct pakfire* pakfire, int direction,
124 int (*callback)(struct pakfire* pakfire, struct libmnt_fs* fs, const void* data),
f71c82d8 125 const void* data) {
bf5f9c24 126 const char* root = pakfire_get_path(pakfire);
ec64a612 127 int r = 0;
bf5f9c24 128
0ea82518
MT
129 struct libmnt_iter* iterator = NULL;
130 struct libmnt_table* tab = NULL;
131 struct libmnt_fs* fs = NULL;
132
133 // Create an iterator
134 iterator = mnt_new_iter(direction);
135 if (!iterator) {
136 ERROR(pakfire, "Could not setup iterator: %m\n");
137 goto ERROR;
138 }
139
140 // Read /proc/mounts
141 tab = mnt_new_table_from_file("/proc/mounts");
142 if (!tab) {
bf5f9c24 143 ERROR(pakfire, "Could not open /proc/mounts: %m\n");
0ea82518 144 goto ERROR;
bf5f9c24
MT
145 }
146
0ea82518
MT
147 while (mnt_table_next_fs(tab, iterator, &fs) == 0) {
148 const char* target = mnt_fs_get_target(fs);
bf5f9c24
MT
149
150 // Ignore any mointpoints that don't belong to us
0ea82518 151 if (!pakfire_string_startswith(target, root))
bf5f9c24
MT
152 continue;
153
bf5f9c24 154 // Call the callback for each relevant mountpoint
0ea82518 155 r = callback(pakfire, fs, data);
bf5f9c24
MT
156 if (r)
157 break;
158 }
159
0ea82518 160ERROR:
bf5f9c24 161 // Tidy up
0ea82518
MT
162 if (fs)
163 mnt_unref_fs(fs);
164 if (tab)
165 mnt_unref_table(tab);
166 if (iterator)
167 mnt_free_iter(iterator);
bf5f9c24
MT
168
169 return r;
170}
171
172static int __pakfire_is_mountpoint(struct pakfire* pakfire,
0ea82518 173 struct libmnt_fs* fs, const void* data) {
bf5f9c24
MT
174 const char* path = (const char*)data;
175
0ea82518 176 return mnt_fs_streq_target(fs, path);
bf5f9c24
MT
177}
178
179int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path) {
0ea82518 180 return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
f71c82d8 181 __pakfire_is_mountpoint, path);
bf5f9c24
MT
182}
183
163851bc 184static int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target,
bf5f9c24
MT
185 const char* fstype, unsigned long mflags, const void* data) {
186 const char* options = (const char*)data;
bf5f9c24
MT
187
188 // Check for some basic inputs
189 if (!source || !target) {
190 errno = EINVAL;
191 return 1;
192 }
193
7f970e6e 194 DEBUG(pakfire, "Mounting %s from %s (%s - %s)\n", target, source, fstype, options);
bf5f9c24 195
7f970e6e
MT
196 // Perform mount()
197 int r = mount(source, target, fstype, mflags, data);
bf5f9c24 198 if (r) {
7f970e6e 199 ERROR(pakfire, "Could not mount %s: %m\n", target);
bf5f9c24
MT
200 }
201
bf5f9c24
MT
202 return r;
203}
204
205static int pakfire_umount(struct pakfire* pakfire, const char* path, int flags) {
206 int r;
207
208 DEBUG(pakfire, "Umounting %s\n", path);
209
210RETRY:
211 // Perform umount
212 r = umount2(path, flags);
213 if (r) {
214 // Attempt a lazy umount when busy
215 if (errno == EBUSY) {
216 flags |= MNT_DETACH;
217 goto RETRY;
218 }
219
220 ERROR(pakfire, "Could not umount %s: %m\n", path);
221 }
222
223 return r;
224}
225
226static int __pakfire_mount_print(struct pakfire* pakfire,
0ea82518
MT
227 struct libmnt_fs* fs, const void* data) {
228 DEBUG(pakfire,
229 " %s %s %s %s\n",
230 mnt_fs_get_source(fs),
231 mnt_fs_get_target(fs),
232 mnt_fs_get_fstype(fs),
233 mnt_fs_get_fs_options(fs)
bf5f9c24
MT
234 );
235
236 return 0;
237}
238
91247a7b
MT
239int pakfire_mount_list(struct pakfire* pakfire) {
240 DEBUG(pakfire, "Mountpoints:\n");
bf5f9c24 241
0ea82518 242 return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
f71c82d8
MT
243 __pakfire_mount_print, NULL);
244}
245
246static int pakfire_populate_dev(struct pakfire* pakfire) {
247 char path[PATH_MAX];
248
249 // Create device nodes
250 for (const struct pakfire_devnode* devnode = devnodes; devnode->path; devnode++) {
251 DEBUG(pakfire, "Creating device node %s\n", devnode->path);
252
253 int r = pakfire_make_path(pakfire, path, devnode->path);
254 if (r < 0)
255 return 1;
256
257 dev_t dev = makedev(devnode->major, devnode->minor);
258
259 r = mknod(path, devnode->mode, dev);
8f1003a3
MT
260
261 // Continue if mknod was successful
262 if (r == 0)
263 continue;
264
265 // If we could not create the device node because of permission issues,
266 // it might be likely that we are running in a user namespace where creating
267 // device nodes is not permitted. Try bind-mounting them.
268 if (errno == EPERM)
269 goto MOUNT;
270
271 // Otherwise log an error and end
272 ERROR(pakfire, "Could not create %s: %m\n", devnode->path);
273 return r;
274
275MOUNT:
276 // Create an empty file
277 r = pakfire_touch(path, 0444);
f71c82d8 278 if (r) {
8f1003a3 279 ERROR(pakfire, "Could not create %s: %m\n", path);
f71c82d8
MT
280 return r;
281 }
8f1003a3
MT
282
283 // Create a bind-mount over the file
284 r = pakfire_mount(pakfire, path, devnode->path, "bind", MS_BIND, NULL);
285 if (r)
286 return r;
f71c82d8
MT
287 }
288
289 // Create symlinks
290 for (const struct pakfire_symlink* s = symlinks; s->target; s++) {
291 DEBUG(pakfire, "Creating symlink %s -> %s\n", s->path, s->target);
292
293 int r = pakfire_make_path(pakfire, path, s->path);
294 if (r < 0)
295 return 1;
296
297 r = symlink(s->target, path);
298 if (r) {
299 ERROR(pakfire, "Could not create symlink %s: %m\n", s->path);
300 return r;
301 }
302 }
303
304 return 0;
bf5f9c24
MT
305}
306
660120b6
MT
307static int pakfire_mount_interpreter(struct pakfire* pakfire) {
308 char target[PATH_MAX];
309
310 // Fetch the target architecture
311 const char* arch = pakfire_get_arch(pakfire);
312
313 // Can we emulate this architecture?
314 char* interpreter = pakfire_arch_find_interpreter(arch);
315
316 // No interpreter required
317 if (!interpreter)
318 return 0;
319
320 DEBUG(pakfire, "Mounting interpreter %s for %s\n", interpreter, arch);
321
322 // Where to mount this?
323 int r = pakfire_make_path(pakfire, target, interpreter);
324 if (r < 0)
325 return r;
326
327 // Create directory
520ce66c 328 r = pakfire_mkparentdir(target, 0755);
660120b6
MT
329 if (r)
330 return r;
331
332 // Create an empty file
333 FILE* f = fopen(target, "w");
334 if (!f)
335 return 1;
336 fclose(f);
337
338 r = pakfire_mount(pakfire, interpreter, target, NULL, MS_BIND|MS_RDONLY, NULL);
339 if (r)
340 ERROR(pakfire, "Could not mount interpreter %s to %s: %m\n", interpreter, target);
341
342 return r;
343}
344
f71c82d8 345int pakfire_mount_all(struct pakfire* pakfire) {
bf5f9c24 346 char target[PATH_MAX];
bf5f9c24
MT
347 int r;
348
349 // Fetch Pakfire's root directory
350 const char* root = pakfire_get_path(pakfire);
351
bf5f9c24 352 for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
bf5f9c24
MT
353 // Figure out where to mount
354 r = pakfire_path_join(target, root, mp->target);
355 if (r < 0)
356 return r;
357
bf5f9c24 358 // Create target
520ce66c 359 pakfire_mkdir(target, 0755);
bf5f9c24
MT
360
361RETRY:
362 // Perform mount()
f71c82d8 363 r = pakfire_mount(pakfire, mp->source, target, mp->fstype, mp->flags, mp->options);
bf5f9c24
MT
364 if (r) {
365 // If the target directory does not exist, we will create it
366 if (errno == ENOENT) {
520ce66c 367 r = pakfire_mkdir(target, 0755);
bf5f9c24
MT
368 if (r) {
369 ERROR(pakfire, "Could not create %s\n", target);
370 return r;
371 }
372
373 goto RETRY;
374 }
375
bf5f9c24
MT
376 return r;
377 }
378 }
379
f71c82d8
MT
380 // Populate /dev
381 r = pakfire_populate_dev(pakfire);
382 if (r)
383 return r;
384
660120b6
MT
385 // Mount the interpreter (if needed)
386 r = pakfire_mount_interpreter(pakfire);
387 if (r)
388 return r;
389
bf5f9c24
MT
390 return 0;
391}
392
393static int __pakfire_umount(struct pakfire* pakfire,
0ea82518
MT
394 struct libmnt_fs* fs, const void* data) {
395 const char* target = mnt_fs_get_target(fs);
396 if (!target)
397 return 1;
398
399 return pakfire_umount(pakfire, target, 0);
bf5f9c24
MT
400}
401
402/*
403 umounts everything that hasn't been umounted, yet
404*/
f71c82d8 405int pakfire_umount_all(struct pakfire* pakfire) {
0ea82518 406 return pakfire_mount_foreach(pakfire, MNT_ITER_BACKWARD,
f71c82d8 407 __pakfire_umount, NULL);
bf5f9c24 408}
163851bc
MT
409
410PAKFIRE_EXPORT int pakfire_bind(struct pakfire* pakfire, const char* src, const char* dst, int flags) {
411 struct stat st;
412 char mountpoint[PATH_MAX];
413
414 if (!dst)
415 dst = src;
416
417 int r = pakfire_make_path(pakfire, mountpoint, dst);
418 if (r < 0)
419 return 1;
420
421 DEBUG(pakfire, "Bind-mounting %s to %s\n", src, mountpoint);
422
423 r = stat(src, &st);
424 if (r < 0) {
425 ERROR(pakfire, "Could not stat %s: %m\n", src);
426 return 1;
427 }
428
429 // Make sure the mountpoint exists
430 switch (st.st_mode & S_IFMT) {
431 case S_IFDIR:
520ce66c 432 r = pakfire_mkdir(mountpoint, st.st_mode);
163851bc
MT
433 if (r && errno != EEXIST)
434 return r;
435 break;
436
437 case S_IFREG:
438 case S_IFLNK:
439 // Make parent directory
520ce66c 440 r = pakfire_mkparentdir(mountpoint, 0755);
163851bc
MT
441 if (r)
442 return r;
443
444 // Create a file
445 FILE* f = fopen(mountpoint, "w");
446 if (!f)
447 return 1;
448 fclose(f);
449 break;
450
451 default:
452 errno = ENOTSUP;
453 return 1;
454 }
455
456 // Perform mount
457 return pakfire_mount(pakfire, src, mountpoint, NULL, flags|MS_BIND, NULL);
458}