Branch data 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 : : }
|