]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/fstab-util.c
shutdown: log on container exit
[thirdparty/systemd.git] / src / shared / fstab-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include "alloc-util.h"
8 #include "device-nodes.h"
9 #include "fstab-util.h"
10 #include "macro.h"
11 #include "mount-util.h"
12 #include "nulstr-util.h"
13 #include "parse-util.h"
14 #include "path-util.h"
15 #include "string-util.h"
16 #include "strv.h"
17
18 int fstab_has_fstype(const char *fstype) {
19 _cleanup_endmntent_ FILE *f = NULL;
20 struct mntent *m;
21
22 f = setmntent(fstab_path(), "re");
23 if (!f)
24 return errno == ENOENT ? false : -errno;
25
26 for (;;) {
27 errno = 0;
28 m = getmntent(f);
29 if (!m)
30 return errno != 0 ? -errno : false;
31
32 if (streq(m->mnt_type, fstype))
33 return true;
34 }
35 return false;
36 }
37
38 bool fstab_is_extrinsic(const char *mount, const char *opts) {
39
40 /* Don't bother with the OS data itself */
41 if (PATH_IN_SET(mount,
42 "/",
43 "/usr",
44 "/etc"))
45 return true;
46
47 if (PATH_STARTSWITH_SET(mount,
48 "/run/initramfs", /* This should stay around from before we boot until after we shutdown */
49 "/proc", /* All of this is API VFS */
50 "/sys", /* … dito … */
51 "/dev")) /* … dito … */
52 return true;
53
54 /* If this is an initrd mount, and we are not in the initrd, then leave
55 * this around forever, too. */
56 if (opts && fstab_test_option(opts, "x-initrd.mount\0") && !in_initrd())
57 return true;
58
59 return false;
60 }
61
62 int fstab_is_mount_point(const char *mount) {
63 _cleanup_endmntent_ FILE *f = NULL;
64 struct mntent *m;
65
66 f = setmntent(fstab_path(), "re");
67 if (!f)
68 return errno == ENOENT ? false : -errno;
69
70 for (;;) {
71 errno = 0;
72 m = getmntent(f);
73 if (!m)
74 return errno != 0 ? -errno : false;
75
76 if (path_equal(m->mnt_dir, mount))
77 return true;
78 }
79 return false;
80 }
81
82 int fstab_filter_options(const char *opts, const char *names,
83 const char **ret_namefound, char **ret_value, char **ret_filtered) {
84 const char *name, *namefound = NULL, *x;
85 _cleanup_strv_free_ char **stor = NULL;
86 _cleanup_free_ char *v = NULL, **strv = NULL;
87 int r;
88
89 assert(names && *names);
90
91 if (!opts)
92 goto answer;
93
94 /* If !ret_value and !ret_filtered, this function is not allowed to fail. */
95
96 if (!ret_filtered) {
97 for (const char *word = opts;;) {
98 const char *end = word;
99
100 /* Look for an *non-escaped* comma separator. Only commas can be escaped, so "\," is
101 * the only valid escape sequence, so we can do a very simple test here. */
102 for (;;) {
103 size_t n = strcspn(end, ",");
104
105 end += n;
106 if (n > 0 && end[-1] == '\\')
107 end++;
108 else
109 break;
110 }
111
112 NULSTR_FOREACH(name, names) {
113 if (end < word + strlen(name))
114 continue;
115 if (!strneq(word, name, strlen(name)))
116 continue;
117
118 /* We know that the string is NUL terminated, so *x is valid */
119 x = word + strlen(name);
120 if (IN_SET(*x, '\0', '=', ',')) {
121 namefound = name;
122 if (ret_value) {
123 bool eq = *x == '=';
124 assert(eq || IN_SET(*x, ',', '\0'));
125
126 r = free_and_strndup(&v,
127 eq ? x + 1 : NULL,
128 eq ? end - x - 1 : 0);
129 if (r < 0)
130 return r;
131 }
132
133 break;
134 }
135 }
136
137 if (*end)
138 word = end + 1;
139 else
140 break;
141 }
142 } else {
143 r = strv_split_full(&stor, opts, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
144 if (r < 0)
145 return r;
146
147 strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
148 if (!strv)
149 return -ENOMEM;
150
151 char **t = strv;
152 for (char **s = strv; *s; s++) {
153 NULSTR_FOREACH(name, names) {
154 x = startswith(*s, name);
155 if (x && IN_SET(*x, '\0', '='))
156 goto found;
157 }
158
159 *t = *s;
160 t++;
161 continue;
162 found:
163 /* Keep the last occurrence found */
164 namefound = name;
165 if (ret_value) {
166 assert(IN_SET(*x, '=', '\0'));
167 r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL);
168 if (r < 0)
169 return r;
170 }
171 }
172 *t = NULL;
173 }
174
175 answer:
176 if (ret_namefound)
177 *ret_namefound = namefound;
178 if (ret_filtered) {
179 char *f;
180
181 f = strv_join_full(strv, ",", NULL, true);
182 if (!f)
183 return -ENOMEM;
184
185 *ret_filtered = f;
186 }
187 if (ret_value)
188 *ret_value = TAKE_PTR(v);
189
190 return !!namefound;
191 }
192
193 int fstab_extract_values(const char *opts, const char *name, char ***values) {
194 _cleanup_strv_free_ char **optsv = NULL, **res = NULL;
195 char **s;
196
197 assert(opts);
198 assert(name);
199 assert(values);
200
201 optsv = strv_split(opts, ",");
202 if (!optsv)
203 return -ENOMEM;
204
205 STRV_FOREACH(s, optsv) {
206 char *arg;
207 int r;
208
209 arg = startswith(*s, name);
210 if (!arg || *arg != '=')
211 continue;
212 r = strv_extend(&res, arg + 1);
213 if (r < 0)
214 return r;
215 }
216
217 *values = TAKE_PTR(res);
218
219 return !!*values;
220 }
221
222 int fstab_find_pri(const char *options, int *ret) {
223 _cleanup_free_ char *opt = NULL;
224 int r, pri;
225
226 assert(ret);
227
228 r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL);
229 if (r < 0)
230 return r;
231 if (r == 0 || !opt)
232 return 0;
233
234 r = safe_atoi(opt, &pri);
235 if (r < 0)
236 return r;
237
238 *ret = pri;
239 return 1;
240 }
241
242 static char *unquote(const char *s, const char* quotes) {
243 size_t l;
244 assert(s);
245
246 /* This is rather stupid, simply removes the heading and
247 * trailing quotes if there is one. Doesn't care about
248 * escaping or anything.
249 *
250 * DON'T USE THIS FOR NEW CODE ANYMORE! */
251
252 l = strlen(s);
253 if (l < 2)
254 return strdup(s);
255
256 if (strchr(quotes, s[0]) && s[l-1] == s[0])
257 return strndup(s+1, l-2);
258
259 return strdup(s);
260 }
261
262 static char *tag_to_udev_node(const char *tagvalue, const char *by) {
263 _cleanup_free_ char *t = NULL, *u = NULL;
264 size_t enc_len;
265
266 u = unquote(tagvalue, QUOTES);
267 if (!u)
268 return NULL;
269
270 enc_len = strlen(u) * 4 + 1;
271 t = new(char, enc_len);
272 if (!t)
273 return NULL;
274
275 if (encode_devnode_name(u, t, enc_len) < 0)
276 return NULL;
277
278 return strjoin("/dev/disk/by-", by, "/", t);
279 }
280
281 char *fstab_node_to_udev_node(const char *p) {
282 assert(p);
283
284 if (startswith(p, "LABEL="))
285 return tag_to_udev_node(p+6, "label");
286
287 if (startswith(p, "UUID="))
288 return tag_to_udev_node(p+5, "uuid");
289
290 if (startswith(p, "PARTUUID="))
291 return tag_to_udev_node(p+9, "partuuid");
292
293 if (startswith(p, "PARTLABEL="))
294 return tag_to_udev_node(p+10, "partlabel");
295
296 return strdup(p);
297 }