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