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