1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
8 #include "device-nodes.h"
9 #include "fstab-util.h"
10 #include "initrd-util.h"
12 #include "mount-util.h"
13 #include "nulstr-util.h"
14 #include "parse-util.h"
15 #include "path-util.h"
16 #include "proc-cmdline.h"
17 #include "string-util.h"
20 bool fstab_enabled_full(int enabled
) {
21 static int cached
= -1;
22 bool val
= true; /* If nothing specified or the check fails, then defaults to true. */
25 /* If 'enabled' is non-negative, then update the cache with it. */
32 r
= proc_cmdline_get_bool("fstab", PROC_CMDLINE_STRIP_RD_PREFIX
|PROC_CMDLINE_TRUE_WHEN_MISSING
, &val
);
34 log_debug_errno(r
, "Failed to parse fstab= kernel command line option, ignoring: %m");
36 return (cached
= val
);
39 int fstab_has_fstype(const char *fstype
) {
40 _cleanup_endmntent_
FILE *f
= NULL
;
48 f
= setmntent(fstab_path(), "re");
50 return errno
== ENOENT
? false : -errno
;
56 return errno
!= 0 ? -errno
: false;
58 if (streq(m
->mnt_type
, fstype
))
64 bool fstab_is_extrinsic(const char *mount
, const char *opts
) {
66 /* Don't bother with the OS data itself */
67 if (PATH_IN_SET(mount
,
73 if (PATH_STARTSWITH_SET(mount
,
74 "/run/initramfs", /* This should stay around from before we boot until after we shutdown */
75 "/run/nextroot", /* Similar (though might be updated from the host) */
76 "/proc", /* All of this is API VFS */
77 "/sys", /* … dito … */
78 "/dev")) /* … dito … */
81 /* If this is an initrd mount, and we are not in the initrd, then leave
82 * this around forever, too. */
83 if (fstab_test_option(opts
, "x-initrd.mount\0") && !in_initrd())
89 static int fstab_is_same_node(const char *what_fstab
, const char *path
) {
90 _cleanup_free_
char *node
= NULL
;
95 node
= fstab_node_to_udev_node(what_fstab
);
99 if (path_equal(node
, path
))
102 if (is_device_path(path
) && is_device_path(node
))
103 return devnode_same(node
, path
);
108 int fstab_is_mount_point_full(const char *where
, const char *path
) {
109 _cleanup_endmntent_
FILE *f
= NULL
;
112 assert(where
|| path
);
114 if (!fstab_enabled())
117 f
= setmntent(fstab_path(), "re");
119 return errno
== ENOENT
? false : -errno
;
127 return errno
!= 0 ? -errno
: false;
129 if (where
&& !path_equal(where
, me
->mnt_dir
))
135 r
= fstab_is_same_node(me
->mnt_fsname
, path
);
136 if (r
> 0 || (r
< 0 && !ERRNO_IS_DEVICE_ABSENT(r
)))
143 int fstab_filter_options(
146 const char **ret_namefound
,
149 char **ret_filtered
) {
151 const char *namefound
= NULL
, *x
;
152 _cleanup_strv_free_
char **stor
= NULL
, **values
= NULL
;
153 _cleanup_free_
char *value
= NULL
, **filtered
= NULL
;
156 assert(names
&& *names
);
157 assert(!(ret_value
&& ret_values
));
162 /* Finds any options matching 'names', and returns:
163 * - the last matching option name in ret_namefound,
164 * - the last matching value in ret_value,
165 * - any matching values in ret_values,
166 * - the rest of the option string in ret_filtered.
168 * If !ret_value and !ret_values and !ret_filtered, this function is not allowed to fail.
170 * Returns negative on error, true if any matching options were found, false otherwise. */
172 if (ret_filtered
|| ret_value
|| ret_values
) {
173 /* For backwards compatibility, we need to pass-through escape characters.
174 * The only ones we "consume" are the ones used as "\," or "\\". */
175 r
= strv_split_full(&stor
, opts
, ",", EXTRACT_UNESCAPE_SEPARATORS
| EXTRACT_UNESCAPE_RELAX
);
179 filtered
= memdup(stor
, sizeof(char*) * (strv_length(stor
) + 1));
184 for (char **s
= t
; *s
; s
++) {
185 NULSTR_FOREACH(name
, names
) {
186 x
= startswith(*s
, name
);
189 /* Match name, but when ret_values, only when followed by assignment. */
190 if (*x
== '=' || (!ret_values
&& *x
== '\0')) {
191 /* Keep the last occurrence found */
201 if (ret_value
|| ret_values
) {
202 assert(IN_SET(*x
, '=', '\0'));
205 r
= free_and_strdup(&value
, *x
== '=' ? x
+ 1 : NULL
);
209 r
= strv_extend(&values
, x
+ 1);
217 for (const char *word
= opts
;;) {
218 const char *end
= word
;
220 /* Look for a *non-escaped* comma separator. Only commas and backslashes can be
221 * escaped, so "\," and "\\" are the only valid escape sequences, and we can do a
222 * very simple test here. */
224 end
+= strcspn(end
, ",\\");
226 if (IN_SET(*end
, ',', '\0'))
228 assert(*end
== '\\');
229 end
++; /* Skip the backslash */
231 end
++; /* Skip the escaped char, but watch out for a trailing comma */
234 NULSTR_FOREACH(name
, names
) {
235 if (end
< word
+ strlen(name
))
237 if (!strneq(word
, name
, strlen(name
)))
240 /* We know that the string is NUL terminated, so *x is valid */
241 x
= word
+ strlen(name
);
242 if (IN_SET(*x
, '\0', '=', ',')) {
256 *ret_namefound
= namefound
;
260 f
= strv_join_full(filtered
, ",", NULL
, true);
267 *ret_value
= TAKE_PTR(value
);
269 *ret_values
= TAKE_PTR(values
);
274 int fstab_find_pri(const char *options
, int *ret
) {
275 _cleanup_free_
char *opt
= NULL
;
280 r
= fstab_filter_options(options
, "pri\0", NULL
, &opt
, NULL
, NULL
);
286 r
= safe_atoi(opt
, &pri
);
294 static char *unquote(const char *s
, const char* quotes
) {
298 /* This is rather stupid, simply removes the heading and
299 * trailing quotes if there is one. Doesn't care about
300 * escaping or anything.
302 * DON'T USE THIS FOR NEW CODE ANYMORE! */
308 if (strchr(quotes
, s
[0]) && s
[l
-1] == s
[0])
309 return strndup(s
+1, l
-2);
314 static char *tag_to_udev_node(const char *tagvalue
, const char *by
) {
315 _cleanup_free_
char *t
= NULL
, *u
= NULL
;
318 u
= unquote(tagvalue
, QUOTES
);
322 enc_len
= strlen(u
) * 4 + 1;
323 t
= new(char, enc_len
);
327 if (encode_devnode_name(u
, t
, enc_len
) < 0)
330 return strjoin("/dev/disk/by-", by
, "/", t
);
333 char *fstab_node_to_udev_node(const char *p
) {
338 q
= startswith(p
, "LABEL=");
340 return tag_to_udev_node(q
, "label");
342 q
= startswith(p
, "UUID=");
344 return tag_to_udev_node(q
, "uuid");
346 q
= startswith(p
, "PARTUUID=");
348 return tag_to_udev_node(q
, "partuuid");
350 q
= startswith(p
, "PARTLABEL=");
352 return tag_to_udev_node(q
, "partlabel");
357 bool fstab_is_bind(const char *options
, const char *fstype
) {
359 if (fstab_test_option(options
, "bind\0" "rbind\0"))
362 if (fstype
&& STR_IN_SET(fstype
, "bind", "rbind"))