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_has_mount_point_prefix_strv(char **prefixes
) {
109 _cleanup_endmntent_
FILE *f
= NULL
;
113 /* This function returns true if at least one entry in fstab has a mount point that starts with one
114 * of the passed prefixes. */
116 if (!fstab_enabled())
119 f
= setmntent(fstab_path(), "re");
121 return errno
== ENOENT
? false : -errno
;
129 return errno
!= 0 ? -errno
: false;
131 if (path_startswith_strv(me
->mnt_dir
, prefixes
))
136 int fstab_is_mount_point_full(const char *where
, const char *path
) {
137 _cleanup_endmntent_
FILE *f
= NULL
;
140 assert(where
|| path
);
142 if (!fstab_enabled())
145 f
= setmntent(fstab_path(), "re");
147 return errno
== ENOENT
? false : -errno
;
155 return errno
!= 0 ? -errno
: false;
157 if (where
&& !path_equal(where
, me
->mnt_dir
))
163 r
= fstab_is_same_node(me
->mnt_fsname
, path
);
164 if (r
> 0 || (r
< 0 && !ERRNO_IS_DEVICE_ABSENT(r
)))
169 int fstab_filter_options(
172 const char **ret_namefound
,
175 char **ret_filtered
) {
177 _cleanup_strv_free_
char **values
= NULL
;
178 _cleanup_free_
char *value
= NULL
, *filtered
= NULL
;
179 const char *namefound
= NULL
;
182 assert(!isempty(names
));
183 assert(!(ret_value
&& ret_values
));
188 /* Finds any options matching 'names', and returns:
189 * - the last matching option name in ret_namefound,
190 * - the last matching value in ret_value,
191 * - any matching values in ret_values,
192 * - the rest of the option string in ret_filtered.
194 * If !ret_value and !ret_values and !ret_filtered, this function is not allowed to fail.
196 * Returns negative on error, true if any matching options were found, false otherwise. */
198 if (ret_value
|| ret_values
|| ret_filtered
) {
199 _cleanup_strv_free_
char **opts_split
= NULL
;
200 _cleanup_free_
char **filtered_strv
= NULL
; /* strings are owned by 'opts_split' */
202 /* For backwards compatibility, we need to pass-through escape characters.
203 * The only ones we "consume" are the ones used as "\," or "\\". */
204 r
= strv_split_full(&opts_split
, opts
, ",", EXTRACT_UNESCAPE_SEPARATORS
|EXTRACT_UNESCAPE_RELAX
);
208 STRV_FOREACH(opt
, opts_split
) {
212 NULSTR_FOREACH(name
, names
) {
213 x
= startswith(*opt
, name
);
217 /* If ret_values, only accept settings followed by assignment. */
218 if (*x
== '=' || (!ret_values
&& *x
== '\0')) {
227 r
= free_and_strdup(&value
, *x
== '=' ? x
+ 1 : NULL
);
229 r
= strv_extend(&values
, x
+ 1);
233 r
= strv_push(&filtered_strv
, *opt
);
238 filtered
= strv_join_full(filtered_strv
, ",", NULL
, /* escape_separator = */ true);
242 for (const char *word
= opts
;;) {
243 const char *end
= word
;
245 /* Look for a *non-escaped* comma separator. Only commas and backslashes can be
246 * escaped, so "\," and "\\" are the only valid escape sequences, and we can do a
247 * very simple test here. */
249 end
+= strcspn(end
, ",\\");
251 if (IN_SET(*end
, ',', '\0'))
253 assert(*end
== '\\');
254 end
++; /* Skip the backslash */
256 end
++; /* Skip the escaped char, but watch out for a trailing comma */
259 NULSTR_FOREACH(name
, names
) {
260 const char *x
= startswith(word
, name
);
264 /* We know that the string is NUL terminated, so *x is valid */
265 if (IN_SET(*x
, '\0', '=', ',')) {
279 *ret_namefound
= namefound
; /* owned by 'names' (passed-in) */
281 *ret_value
= TAKE_PTR(value
);
283 *ret_values
= TAKE_PTR(values
);
285 *ret_filtered
= TAKE_PTR(filtered
);
290 int fstab_find_pri(const char *opts
, int *ret
) {
291 _cleanup_free_
char *v
= NULL
;
296 r
= fstab_filter_options(opts
, "pri\0", NULL
, &v
, NULL
, NULL
);
302 r
= safe_atoi(v
, &pri
);
310 static char *unquote(const char *s
, const char* quotes
) {
314 /* This is rather stupid, simply removes the heading and
315 * trailing quotes if there is one. Doesn't care about
316 * escaping or anything.
318 * DON'T USE THIS FOR NEW CODE ANYMORE! */
324 if (strchr(quotes
, s
[0]) && s
[l
-1] == s
[0])
325 return strndup(s
+1, l
-2);
330 static char *tag_to_udev_node(const char *tagvalue
, const char *by
) {
331 _cleanup_free_
char *t
= NULL
, *u
= NULL
;
334 u
= unquote(tagvalue
, QUOTES
);
338 enc_len
= strlen(u
) * 4 + 1;
339 t
= new(char, enc_len
);
343 if (encode_devnode_name(u
, t
, enc_len
) < 0)
346 return strjoin("/dev/disk/by-", by
, "/", t
);
349 char *fstab_node_to_udev_node(const char *p
) {
354 q
= startswith(p
, "LABEL=");
356 return tag_to_udev_node(q
, "label");
358 q
= startswith(p
, "UUID=");
360 return tag_to_udev_node(q
, "uuid");
362 q
= startswith(p
, "PARTUUID=");
364 return tag_to_udev_node(q
, "partuuid");
366 q
= startswith(p
, "PARTLABEL=");
368 return tag_to_udev_node(q
, "partlabel");
373 bool fstab_is_bind(const char *options
, const char *fstype
) {
375 if (fstab_test_option(options
, "bind\0" "rbind\0"))
378 if (fstype
&& STR_IN_SET(fstype
, "bind", "rbind"))