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