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