]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
3577de7a KS |
2 | |
3 | #include <errno.h> | |
a8fbdf54 TA |
4 | #include <fcntl.h> |
5 | #include <stdbool.h> | |
3577de7a | 6 | #include <stdlib.h> |
07630cea | 7 | #include <sys/stat.h> |
a8fbdf54 | 8 | #include <syslog.h> |
3577de7a KS |
9 | #include <unistd.h> |
10 | ||
b5efdb8a | 11 | #include "alloc-util.h" |
a965a319 | 12 | #include "architecture.h" |
affb60b1 | 13 | #include "base-filesystem.h" |
9a22b098 | 14 | #include "errno-util.h" |
affb60b1 | 15 | #include "fd-util.h" |
3577de7a KS |
16 | #include "log.h" |
17 | #include "macro.h" | |
d8b4d14d | 18 | #include "nulstr-util.h" |
65290fbf | 19 | #include "path-util.h" |
07630cea | 20 | #include "string-util.h" |
affb60b1 | 21 | #include "umask-util.h" |
ee104e11 | 22 | #include "user-util.h" |
3577de7a KS |
23 | |
24 | typedef struct BaseFilesystem { | |
04ba1bb0 | 25 | const char *dir; /* directory or symlink to create */ |
3577de7a | 26 | mode_t mode; |
04ba1bb0 | 27 | const char *target; /* if non-NULL create as symlink to this target */ |
a6f44d61 | 28 | const char *exists; /* conditionalize this entry on existence of this file */ |
6404ecc8 | 29 | bool ignore_failure; |
3577de7a KS |
30 | } BaseFilesystem; |
31 | ||
32 | static const BaseFilesystem table[] = { | |
30d7c9c4 HH |
33 | { "bin", 0, "usr/bin\0", NULL }, |
34 | { "lib", 0, "usr/lib\0", NULL }, | |
93cbc9ca | 35 | { "root", 0750, NULL, NULL, true }, |
30d7c9c4 | 36 | { "sbin", 0, "usr/sbin\0", NULL }, |
03cfe0d5 LP |
37 | { "usr", 0755, NULL, NULL }, |
38 | { "var", 0755, NULL, NULL }, | |
39 | { "etc", 0755, NULL, NULL }, | |
7243cefe LP |
40 | { "proc", 0555, NULL, NULL, true }, |
41 | { "sys", 0555, NULL, NULL, true }, | |
42 | { "dev", 0555, NULL, NULL, true }, | |
4d88d839 | 43 | { "run", 0555, NULL, NULL, true }, |
84c61aea LP |
44 | /* We don't add /tmp/ here for now (even though it's necessary for regular operation), because we |
45 | * want to support both cases where /tmp/ is a mount of its own (in which case we probably should set | |
46 | * the mode to 1555, to indicate that noone should write to it, not even root) and when it's part of | |
47 | * the rootfs (in which case we should set mode 1777), and we simply don't know what's right. */ | |
6f32005f | 48 | |
04ba1bb0 LP |
49 | /* Various architecture ABIs define the path to the dynamic loader via the /lib64/ subdirectory of |
50 | * the root directory. When booting from an otherwise empty root file system (where only /usr/ has | |
51 | * been mounted into) it is thus necessary to create a symlink pointing to the right subdirectory of | |
52 | * /usr/ first — otherwise we couldn't invoke any dynamic binary. Let's detect this case here, and | |
53 | * create the symlink as needed should it be missing. We prefer doing this consistently with Debian's | |
27e69584 | 54 | * multiarch logic, but support Fedora-style and Arch-style multilib too. */ |
6f32005f | 55 | #if defined(__aarch64__) |
dcc87c68 ZJS |
56 | /* aarch64 ELF ABI actually says dynamic loader is in /lib/, but Fedora puts it in /lib64/ anyway and |
57 | * just symlinks /lib/ld-linux-aarch64.so.1 to ../lib64/ld-linux-aarch64.so.1. For this to work | |
58 | * correctly, /lib64/ must be symlinked to /usr/lib64/. */ | |
a965a319 | 59 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" |
27e69584 AV |
60 | "usr/lib64\0" |
61 | "usr/lib\0", "ld-linux-aarch64.so.1" }, | |
dcc87c68 | 62 | # define KNOW_LIB64_DIRS 1 |
6f32005f ZJS |
63 | #elif defined(__alpha__) |
64 | #elif defined(__arc__) || defined(__tilegx__) | |
65 | #elif defined(__arm__) | |
2db409ce ZJS |
66 | /* No /lib64 on arm. The linker is /lib/ld-linux-armhf.so.3. */ |
67 | # define KNOW_LIB64_DIRS 1 | |
6f32005f | 68 | #elif defined(__i386__) || defined(__x86_64__) |
a965a319 | 69 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" |
27e69584 AV |
70 | "usr/lib64\0" |
71 | "usr/lib\0", "ld-linux-x86-64.so.2" }, | |
6f32005f ZJS |
72 | # define KNOW_LIB64_DIRS 1 |
73 | #elif defined(__ia64__) | |
01722893 XW |
74 | #elif defined(__loongarch64) |
75 | # define KNOW_LIB64_DIRS 1 | |
76 | # if defined(__loongarch_double_float) | |
77 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" | |
27e69584 AV |
78 | "usr/lib64\0" |
79 | "usr/lib\0", "ld-linux-loongarch-lp64d.so.1" }, | |
01722893 XW |
80 | # elif defined(__loongarch_single_float) |
81 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" | |
27e69584 AV |
82 | "usr/lib64\0" |
83 | "usr/lib\0", "ld-linux-loongarch-lp64f.so.1" }, | |
01722893 XW |
84 | # elif defined(__loongarch_soft_float) |
85 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" | |
27e69584 AV |
86 | "usr/lib64\0" |
87 | "usr/lib\0", "ld-linux-loongarch-lp64s.so.1" }, | |
01722893 XW |
88 | # else |
89 | # error "Unknown LoongArch ABI" | |
90 | # endif | |
6f32005f | 91 | #elif defined(__m68k__) |
996eaea5 ZJS |
92 | /* No link needed. */ |
93 | # define KNOW_LIB64_DIRS 1 | |
6f32005f ZJS |
94 | #elif defined(_MIPS_SIM) |
95 | # if _MIPS_SIM == _MIPS_SIM_ABI32 | |
96 | # elif _MIPS_SIM == _MIPS_SIM_NABI32 | |
97 | # elif _MIPS_SIM == _MIPS_SIM_ABI64 | |
98 | # else | |
99 | # error "Unknown MIPS ABI" | |
100 | # endif | |
101 | #elif defined(__powerpc__) | |
e98157b9 | 102 | # if defined(__PPC64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
a965a319 | 103 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" |
27e69584 AV |
104 | "usr/lib64\0" |
105 | "usr/lib\0", "ld64.so.2" }, | |
e98157b9 ZJS |
106 | # define KNOW_LIB64_DIRS 1 |
107 | # elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
108 | /* powerpc64-linux-gnu */ | |
109 | # else | |
110 | /* powerpc-linux-gnu */ | |
111 | # endif | |
6f32005f ZJS |
112 | #elif defined(__riscv) |
113 | # if __riscv_xlen == 32 | |
114 | # elif __riscv_xlen == 64 | |
761e9382 | 115 | /* Same situation as for aarch64 */ |
a965a319 | 116 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" |
27e69584 AV |
117 | "usr/lib64\0" |
118 | "usr/lib\0", "ld-linux-riscv64-lp64d.so.1" }, | |
761e9382 | 119 | # define KNOW_LIB64_DIRS 1 |
6f32005f ZJS |
120 | # else |
121 | # error "Unknown RISC-V ABI" | |
122 | # endif | |
123 | #elif defined(__s390__) | |
fe037986 | 124 | /* s390-linux-gnu */ |
6f32005f | 125 | #elif defined(__s390x__) |
a965a319 | 126 | { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" |
27e69584 AV |
127 | "usr/lib64\0" |
128 | "usr/lib\0", "ld-lsb-s390x.so.3" }, | |
fe037986 | 129 | # define KNOW_LIB64_DIRS 1 |
6f32005f | 130 | #elif defined(__sparc__) |
e1ae9755 | 131 | #endif |
6f32005f | 132 | /* gcc doesn't allow pragma to be used within constructs, hence log about this separately below */ |
3577de7a KS |
133 | }; |
134 | ||
6f32005f ZJS |
135 | #ifndef KNOW_LIB64_DIRS |
136 | # pragma message "Please add an entry above specifying whether your architecture uses /lib64/, /lib32/, or no such links." | |
04ba1bb0 LP |
137 | #endif |
138 | ||
8aefedce | 139 | int base_filesystem_create_fd(int fd, const char *root, uid_t uid, gid_t gid) { |
8a383bf2 | 140 | int r; |
3577de7a | 141 | |
8aefedce LP |
142 | assert(fd >= 0); |
143 | assert(root); | |
144 | ||
145 | /* The "root" parameter is decoration only – it's only used as part of log messages */ | |
3577de7a | 146 | |
9b4aba10 | 147 | for (size_t i = 0; i < ELEMENTSOF(table); i++) { |
6f4f8056 HH |
148 | if (faccessat(fd, table[i].dir, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) |
149 | continue; | |
150 | ||
9a22b098 | 151 | if (table[i].target) { /* Create as symlink? */ |
12e2b70f | 152 | const char *target = NULL; |
e1ae9755 KS |
153 | |
154 | /* check if one of the targets exists */ | |
155 | NULSTR_FOREACH(s, table[i].target) { | |
156 | if (faccessat(fd, s, F_OK, AT_SYMLINK_NOFOLLOW) < 0) | |
157 | continue; | |
158 | ||
3fd165e5 KS |
159 | /* check if a specific file exists at the target path */ |
160 | if (table[i].exists) { | |
161 | _cleanup_free_ char *p = NULL; | |
162 | ||
65290fbf | 163 | p = path_join(s, table[i].exists); |
3fd165e5 KS |
164 | if (!p) |
165 | return log_oom(); | |
166 | ||
167 | if (faccessat(fd, p, F_OK, AT_SYMLINK_NOFOLLOW) < 0) | |
168 | continue; | |
169 | } | |
170 | ||
e1ae9755 KS |
171 | target = s; |
172 | break; | |
173 | } | |
174 | ||
175 | if (!target) | |
3577de7a KS |
176 | continue; |
177 | ||
9a22b098 LP |
178 | r = RET_NERRNO(symlinkat(target, fd, table[i].dir)); |
179 | } else { | |
180 | /* Create as directory. */ | |
181 | WITH_UMASK(0000) | |
182 | r = RET_NERRNO(mkdirat(fd, table[i].dir, table[i].mode)); | |
3577de7a | 183 | } |
8258578f | 184 | if (r < 0) { |
9a22b098 LP |
185 | bool ignore = IN_SET(r, -EEXIST, -EROFS) || table[i].ignore_failure; |
186 | log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r, | |
187 | "Failed to create %s/%s: %m", root, table[i].dir); | |
188 | if (ignore) | |
8258578f | 189 | continue; |
d1d59eeb | 190 | |
9a22b098 | 191 | return r; |
6404ecc8 | 192 | } |
03cfe0d5 | 193 | |
264caae2 | 194 | if (uid_is_valid(uid) || gid_is_valid(gid)) |
03cfe0d5 | 195 | if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) |
9a22b098 | 196 | return log_error_errno(errno, "Failed to chown %s/%s: %m", root, table[i].dir); |
3577de7a KS |
197 | } |
198 | ||
199 | return 0; | |
200 | } | |
8aefedce LP |
201 | |
202 | int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { | |
203 | _cleanup_close_ int fd = -EBADF; | |
204 | ||
205 | fd = open(ASSERT_PTR(root), O_DIRECTORY|O_CLOEXEC); | |
206 | if (fd < 0) | |
207 | return log_error_errno(errno, "Failed to open root file system: %m"); | |
208 | ||
209 | return base_filesystem_create_fd(fd, root, uid, gid); | |
210 | } |