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