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