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