]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/fstab-util.c
fstab,crypttab: allow escaping of commas
[thirdparty/systemd.git] / src / shared / fstab-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
d15d0333 2
a8fbdf54 3#include <errno.h>
a8fbdf54
TA
4#include <stdio.h>
5#include <stdlib.h>
a8fbdf54 6
b5efdb8a 7#include "alloc-util.h"
6550203e 8#include "device-nodes.h"
6bedfcbb 9#include "fstab-util.h"
a8fbdf54 10#include "macro.h"
4349cd7c 11#include "mount-util.h"
d8b4d14d 12#include "nulstr-util.h"
6bedfcbb 13#include "parse-util.h"
0b6b6787 14#include "path-util.h"
07630cea 15#include "string-util.h"
d15d0333 16#include "strv.h"
d15d0333 17
6c1921e9
FB
18int fstab_has_fstype(const char *fstype) {
19 _cleanup_endmntent_ FILE *f = NULL;
20 struct mntent *m;
21
ed4ad488 22 f = setmntent(fstab_path(), "re");
6c1921e9
FB
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
bc9e5a4c
FB
38bool 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
b9088048 62int fstab_is_mount_point(const char *mount) {
0b6b6787
KS
63 _cleanup_endmntent_ FILE *f = NULL;
64 struct mntent *m;
65
ed4ad488 66 f = setmntent(fstab_path(), "re");
0b6b6787 67 if (!f)
b9088048
FB
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;
0b6b6787 75
0b6b6787
KS
76 if (path_equal(m->mnt_dir, mount))
77 return true;
b9088048 78 }
0b6b6787
KS
79 return false;
80}
81
d15d0333 82int fstab_filter_options(const char *opts, const char *names,
92a08691 83 const char **ret_namefound, char **ret_value, char **ret_filtered) {
0e8d1859 84 const char *name, *namefound = NULL, *x;
d15d0333
ZJS
85 _cleanup_strv_free_ char **stor = NULL;
86 _cleanup_free_ char *v = NULL, **strv = NULL;
0e8d1859 87 int r;
d15d0333
ZJS
88
89 assert(names && *names);
90
91 if (!opts)
92 goto answer;
93
0e8d1859 94 /* If !ret_value and !ret_filtered, this function is not allowed to fail. */
d15d0333 95
92a08691 96 if (!ret_filtered) {
45638a63 97 for (const char *word = opts;;) {
7bb553bb
ZJS
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 }
d15d0333 111
d15d0333 112 NULSTR_FOREACH(name, names) {
45638a63 113 if (end < word + strlen(name))
d15d0333
ZJS
114 continue;
115 if (!strneq(word, name, strlen(name)))
116 continue;
117
0e8d1859 118 /* We know that the string is NUL terminated, so *x is valid */
d15d0333
ZJS
119 x = word + strlen(name);
120 if (IN_SET(*x, '\0', '=', ',')) {
0e8d1859 121 namefound = name;
92a08691 122 if (ret_value) {
0e8d1859
ZJS
123 bool eq = *x == '=';
124 assert(eq || IN_SET(*x, ',', '\0'));
125
126 r = free_and_strndup(&v,
127 eq ? x + 1 : NULL,
45638a63 128 eq ? end - x - 1 : 0);
0e8d1859
ZJS
129 if (r < 0)
130 return r;
d15d0333 131 }
45638a63
ZJS
132
133 break;
d15d0333
ZJS
134 }
135 }
45638a63
ZJS
136
137 if (*end)
138 word = end + 1;
139 else
140 break;
141 }
d15d0333 142 } else {
7bb553bb
ZJS
143 r = strv_split_full(&stor, opts, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
144 if (r < 0)
145 return r;
146
d15d0333
ZJS
147 strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
148 if (!strv)
149 return -ENOMEM;
150
0e8d1859
ZJS
151 char **t = strv;
152 for (char **s = strv; *s; s++) {
d15d0333
ZJS
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:
5238e957 163 /* Keep the last occurrence found */
0e8d1859 164 namefound = name;
92a08691 165 if (ret_value) {
0e8d1859
ZJS
166 assert(IN_SET(*x, '=', '\0'));
167 r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL);
168 if (r < 0)
169 return r;
d15d0333
ZJS
170 }
171 }
172 *t = NULL;
173 }
174
175answer:
92a08691 176 if (ret_namefound)
0e8d1859 177 *ret_namefound = namefound;
92a08691 178 if (ret_filtered) {
d15d0333
ZJS
179 char *f;
180
7bb553bb 181 f = strv_join_full(strv, ",", NULL, true);
d15d0333
ZJS
182 if (!f)
183 return -ENOMEM;
184
92a08691 185 *ret_filtered = f;
d15d0333 186 }
92a08691
LP
187 if (ret_value)
188 *ret_value = TAKE_PTR(v);
d15d0333 189
0e8d1859 190 return !!namefound;
d15d0333
ZJS
191}
192
3519d230
KZ
193int 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
ae2a15bc 217 *values = TAKE_PTR(res);
3519d230
KZ
218
219 return !!*values;
220}
221
d15d0333
ZJS
222int fstab_find_pri(const char *options, int *ret) {
223 _cleanup_free_ char *opt = NULL;
7477451b 224 int r, pri;
d15d0333
ZJS
225
226 assert(ret);
227
228 r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL);
a75f4e2a 229 if (r < 0)
d15d0333 230 return r;
a75f4e2a
ZJS
231 if (r == 0 || !opt)
232 return 0;
d15d0333 233
7477451b 234 r = safe_atoi(opt, &pri);
d15d0333
ZJS
235 if (r < 0)
236 return r;
237
7477451b 238 *ret = pri;
d15d0333
ZJS
239 return 1;
240}
6550203e
LP
241
242static 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 *
13e785f7 250 * DON'T USE THIS FOR NEW CODE ANYMORE! */
6550203e
LP
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
262static 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
605405c6 278 return strjoin("/dev/disk/by-", by, "/", t);
6550203e
LP
279}
280
281char *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}