Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <errno.h>
4 : #include <fcntl.h>
5 : #include <stdbool.h>
6 : #include <stdlib.h>
7 : #include <sys/stat.h>
8 : #include <syslog.h>
9 : #include <unistd.h>
10 :
11 : #include "alloc-util.h"
12 : #include "base-filesystem.h"
13 : #include "fd-util.h"
14 : #include "log.h"
15 : #include "macro.h"
16 : #include "nulstr-util.h"
17 : #include "path-util.h"
18 : #include "string-util.h"
19 : #include "umask-util.h"
20 : #include "user-util.h"
21 :
22 : typedef struct BaseFilesystem {
23 : const char *dir;
24 : mode_t mode;
25 : const char *target;
26 : const char *exists;
27 : bool ignore_failure;
28 : } BaseFilesystem;
29 :
30 : static const BaseFilesystem table[] = {
31 : { "bin", 0, "usr/bin\0", NULL },
32 : { "lib", 0, "usr/lib\0", NULL },
33 : { "root", 0755, NULL, NULL, true },
34 : { "sbin", 0, "usr/sbin\0", NULL },
35 : { "usr", 0755, NULL, NULL },
36 : { "var", 0755, NULL, NULL },
37 : { "etc", 0755, NULL, NULL },
38 : { "proc", 0755, NULL, NULL, true },
39 : { "sys", 0755, NULL, NULL, true },
40 : { "dev", 0755, NULL, NULL, true },
41 : #if defined(__i386__) || defined(__x86_64__)
42 : { "lib64", 0, "usr/lib/x86_64-linux-gnu\0"
43 : "usr/lib64\0", "ld-linux-x86-64.so.2" },
44 : #endif
45 : };
46 :
47 0 : int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
48 0 : _cleanup_close_ int fd = -1;
49 : size_t i;
50 : int r;
51 :
52 0 : fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
53 0 : if (fd < 0)
54 0 : return log_error_errno(errno, "Failed to open root file system: %m");
55 :
56 0 : for (i = 0; i < ELEMENTSOF(table); i ++) {
57 0 : if (faccessat(fd, table[i].dir, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
58 0 : continue;
59 :
60 0 : if (table[i].target) {
61 0 : const char *target = NULL, *s;
62 :
63 : /* check if one of the targets exists */
64 0 : NULSTR_FOREACH(s, table[i].target) {
65 0 : if (faccessat(fd, s, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
66 0 : continue;
67 :
68 : /* check if a specific file exists at the target path */
69 0 : if (table[i].exists) {
70 0 : _cleanup_free_ char *p = NULL;
71 :
72 0 : p = path_join(s, table[i].exists);
73 0 : if (!p)
74 0 : return log_oom();
75 :
76 0 : if (faccessat(fd, p, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
77 0 : continue;
78 : }
79 :
80 0 : target = s;
81 0 : break;
82 : }
83 :
84 0 : if (!target)
85 0 : continue;
86 :
87 0 : if (symlinkat(target, fd, table[i].dir) < 0) {
88 0 : log_full_errno(IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno,
89 : "Failed to create symlink at %s/%s: %m", root, table[i].dir);
90 :
91 0 : if (IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure)
92 0 : continue;
93 :
94 0 : return -errno;
95 : }
96 :
97 0 : if (uid_is_valid(uid) || gid_is_valid(gid)) {
98 0 : if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
99 0 : return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
100 : }
101 :
102 0 : continue;
103 : }
104 :
105 0 : RUN_WITH_UMASK(0000)
106 0 : r = mkdirat(fd, table[i].dir, table[i].mode);
107 0 : if (r < 0) {
108 0 : log_full_errno(IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno,
109 : "Failed to create directory at %s/%s: %m", root, table[i].dir);
110 :
111 0 : if (IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure)
112 0 : continue;
113 :
114 0 : return -errno;
115 : }
116 :
117 0 : if (uid != UID_INVALID || gid != UID_INVALID) {
118 0 : if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
119 0 : return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir);
120 : }
121 : }
122 :
123 0 : return 0;
124 : }
|