1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "device-nodes.h"
8 #include "errno-util.h"
9 #include "extract-word.h"
10 #include "fstab-util.h"
11 #include "initrd-util.h"
12 #include "libmount-util.h"
14 #include "nulstr-util.h"
15 #include "parse-util.h"
16 #include "path-util.h"
17 #include "proc-cmdline.h"
18 #include "string-util.h"
21 bool fstab_enabled_full(int enabled
) {
22 static int cached
= -1;
23 bool val
= true; /* If nothing specified or the check fails, then defaults to true. */
26 /* If 'enabled' is non-negative, then update the cache with it. */
33 r
= proc_cmdline_get_bool("fstab", PROC_CMDLINE_STRIP_RD_PREFIX
|PROC_CMDLINE_TRUE_WHEN_MISSING
, &val
);
35 log_debug_errno(r
, "Failed to parse fstab= kernel command line option, ignoring: %m");
37 return (cached
= val
);
40 int fstab_has_fstype(const char *fstype
) {
41 _cleanup_(mnt_free_tablep
) struct libmnt_table
*table
= NULL
;
42 _cleanup_(mnt_free_iterp
) struct libmnt_iter
*iter
= NULL
;
50 r
= libmount_parse_fstab(&table
, &iter
);
59 r
= mnt_table_next_fs(table
, iter
, &fs
);
65 if (streq_ptr(mnt_fs_get_fstype(fs
), fstype
))
70 bool fstab_is_extrinsic(const char *mount
, const char *opts
) {
72 /* Don't bother with the OS data itself */
73 if (PATH_IN_SET(mount
,
79 if (PATH_STARTSWITH_SET(mount
,
80 "/run/initramfs", /* This should stay around from before we boot until after we shutdown */
81 "/run/nextroot", /* Similar (though might be updated from the host) */
82 "/proc", /* All of this is API VFS */
83 "/sys", /* … ditto … */
84 "/dev")) /* … ditto … */
87 /* If this is an initrd mount, and we are not in the initrd, then leave
88 * this around forever, too. */
89 if (fstab_test_option(opts
, "x-initrd.mount\0") && !in_initrd())
95 static int fstab_is_same_node(const char *what_fstab
, const char *path
) {
96 _cleanup_free_
char *node
= NULL
;
101 node
= fstab_node_to_udev_node(what_fstab
);
105 if (path_equal(node
, path
))
108 if (is_device_path(path
) && is_device_path(node
))
109 return devnode_same(node
, path
);
114 int fstab_has_mount_point_prefix_strv(char * const *prefixes
) {
115 _cleanup_(mnt_free_tablep
) struct libmnt_table
*table
= NULL
;
116 _cleanup_(mnt_free_iterp
) struct libmnt_iter
*iter
= NULL
;
119 assert(!strv_isempty(prefixes
));
121 /* This function returns true if at least one entry in fstab has a mount point that starts with one
122 * of the passed prefixes. */
124 if (!fstab_enabled())
127 r
= libmount_parse_fstab(&table
, &iter
);
134 struct libmnt_fs
*fs
;
137 r
= mnt_table_next_fs(table
, iter
, &fs
);
143 path
= mnt_fs_get_target(fs
);
147 if (path_startswith_strv(path
, prefixes
))
152 int fstab_is_mount_point_full(const char *where
, const char *path
) {
153 _cleanup_(mnt_free_tablep
) struct libmnt_table
*table
= NULL
;
154 _cleanup_(mnt_free_iterp
) struct libmnt_iter
*iter
= NULL
;
157 assert(where
|| path
);
159 if (!fstab_enabled())
162 r
= libmount_parse_fstab(&table
, &iter
);
169 struct libmnt_fs
*fs
;
171 r
= mnt_table_next_fs(table
, iter
, &fs
);
177 if (where
&& !path_equal(mnt_fs_get_target(fs
), where
))
183 r
= fstab_is_same_node(mnt_fs_get_source(fs
), path
);
184 if (r
> 0 || (r
< 0 && !ERRNO_IS_DEVICE_ABSENT(r
)))
189 int fstab_filter_options(
192 const char **ret_namefound
,
195 char **ret_filtered
) {
197 _cleanup_strv_free_
char **values
= NULL
;
198 _cleanup_free_
char *value
= NULL
, *filtered
= NULL
;
199 const char *namefound
= NULL
;
202 assert(!isempty(names
));
203 assert(!(ret_value
&& ret_values
));
208 /* Finds any options matching 'names', and returns:
209 * - the last matching option name in ret_namefound,
210 * - the last matching value in ret_value,
211 * - any matching values in ret_values,
212 * - the rest of the option string in ret_filtered.
214 * If !ret_value and !ret_values and !ret_filtered, this function is not allowed to fail.
216 * Returns negative on error, true if any matching options were found, false otherwise. */
218 if (ret_value
|| ret_values
|| ret_filtered
) {
219 _cleanup_strv_free_
char **opts_split
= NULL
;
220 _cleanup_free_
char **filtered_strv
= NULL
; /* strings are owned by 'opts_split' */
222 /* For backwards compatibility, we need to pass-through escape characters.
223 * The only ones we "consume" are the ones used as "\," or "\\". */
224 r
= strv_split_full(&opts_split
, opts
, ",", EXTRACT_UNESCAPE_SEPARATORS
|EXTRACT_UNESCAPE_RELAX
);
228 STRV_FOREACH(opt
, opts_split
) {
232 NULSTR_FOREACH(name
, names
) {
233 x
= startswith(*opt
, name
);
237 /* If ret_values, only accept settings followed by assignment. */
238 if (*x
== '=' || (!ret_values
&& *x
== '\0')) {
247 r
= free_and_strdup(&value
, *x
== '=' ? x
+ 1 : NULL
);
249 r
= strv_extend(&values
, x
+ 1);
253 r
= strv_push(&filtered_strv
, *opt
);
259 filtered
= strv_join_full(filtered_strv
, ",", NULL
, /* escape_separator = */ true);
264 for (const char *word
= opts
;;) {
265 const char *end
= word
;
267 /* Look for a *non-escaped* comma separator. Only commas and backslashes can be
268 * escaped, so "\," and "\\" are the only valid escape sequences, and we can do a
269 * very simple test here. */
271 end
+= strcspn(end
, ",\\");
273 if (IN_SET(*end
, ',', '\0'))
275 assert(*end
== '\\');
276 end
++; /* Skip the backslash */
278 end
++; /* Skip the escaped char, but watch out for a trailing comma */
281 NULSTR_FOREACH(name
, names
) {
282 const char *x
= startswith(word
, name
);
286 /* We know that the string is NUL terminated, so *x is valid */
287 if (IN_SET(*x
, '\0', '=', ',')) {
301 *ret_namefound
= namefound
; /* owned by 'names' (passed-in) */
303 *ret_value
= TAKE_PTR(value
);
305 *ret_values
= TAKE_PTR(values
);
307 *ret_filtered
= TAKE_PTR(filtered
);
312 int fstab_find_pri(const char *opts
, int *ret
) {
313 _cleanup_free_
char *v
= NULL
;
318 r
= fstab_filter_options(opts
, "pri\0", NULL
, &v
, NULL
, NULL
);
324 r
= safe_atoi(v
, &pri
);
332 static char *unquote(const char *s
, const char* quotes
) {
336 /* This is rather stupid, simply removes the heading and
337 * trailing quotes if there is one. Doesn't care about
338 * escaping or anything.
340 * DON'T USE THIS FOR NEW CODE ANYMORE! */
346 if (strchr(quotes
, s
[0]) && s
[l
-1] == s
[0])
347 return strndup(s
+1, l
-2);
352 static char *tag_to_udev_node(const char *tagvalue
, const char *by
) {
353 _cleanup_free_
char *t
= NULL
, *u
= NULL
;
356 u
= unquote(tagvalue
, QUOTES
);
360 enc_len
= strlen(u
) * 4 + 1;
361 t
= new(char, enc_len
);
365 if (encode_devnode_name(u
, t
, enc_len
) < 0)
368 return strjoin("/dev/disk/by-", by
, "/", t
);
371 char* fstab_node_to_udev_node(const char *p
) {
376 q
= startswith(p
, "LABEL=");
378 return tag_to_udev_node(q
, "label");
380 q
= startswith(p
, "UUID=");
382 return tag_to_udev_node(q
, "uuid");
384 q
= startswith(p
, "PARTUUID=");
386 return tag_to_udev_node(q
, "partuuid");
388 q
= startswith(p
, "PARTLABEL=");
390 return tag_to_udev_node(q
, "partlabel");
395 const char* fstab_path(void) {
396 return secure_getenv("SYSTEMD_FSTAB") ?: "/etc/fstab";
399 bool fstab_is_bind(const char *options
, const char *fstype
) {
401 if (fstab_test_option(options
, "bind\0" "rbind\0"))
404 if (fstype
&& STR_IN_SET(fstype
, "bind", "rbind"))