1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
11 #include "path-util.h"
12 #include "string-util.h"
16 bool image_name_is_valid(const char *s
) {
17 if (!filename_is_valid(s
))
20 if (string_has_cc(s
, NULL
))
23 if (!utf8_is_valid(s
))
26 /* Temporary files for atomically creating new files */
27 if (startswith(s
, ".#"))
33 int path_is_extension_tree(const char *path
, const char *extension
) {
38 /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
39 * always results in -ENOENT, and we can properly distinguish the case where the whole root doesn't exist from
40 * the case where just the os-release file is missing. */
41 if (laccess(path
, F_OK
) < 0)
44 /* We use /usr/lib/extension-release.d/extension-release.NAME as flag file if something is a system extension,
45 * and {/etc|/usr/lib}/os-release as flag file if something is an OS (in case extension == NULL) */
46 r
= open_extension_release(path
, extension
, NULL
, NULL
);
47 if (r
== -ENOENT
) /* We got nothing */
55 int open_extension_release(const char *root
, const char *extension
, char **ret_path
, int *ret_fd
) {
56 _cleanup_free_
char *q
= NULL
;
60 const char *extension_full_path
;
62 if (!image_name_is_valid(extension
))
63 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
64 "The extension name %s is invalid.", extension
);
66 extension_full_path
= strjoina("/usr/lib/extension-release.d/extension-release.", extension
);
67 r
= chase_symlinks(extension_full_path
, root
, CHASE_PREFIX_ROOT
,
73 FOREACH_STRING(p
, "/etc/os-release", "/usr/lib/os-release") {
74 r
= chase_symlinks(p
, root
, CHASE_PREFIX_ROOT
,
87 /* Convert the O_PATH fd into a proper, readable one */
88 real_fd
= fd_reopen(fd
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
97 *ret_path
= TAKE_PTR(q
);
102 int fopen_extension_release(const char *root
, const char *extension
, char **ret_path
, FILE **ret_file
) {
103 _cleanup_free_
char *p
= NULL
;
104 _cleanup_close_
int fd
= -1;
109 return open_extension_release(root
, extension
, ret_path
, NULL
);
111 r
= open_extension_release(root
, extension
, ret_path
? &p
: NULL
, &fd
);
115 f
= take_fdopen(&fd
, "r");
122 *ret_path
= TAKE_PTR(p
);
127 static int parse_release_internal(const char *root
, const char *extension
, va_list ap
) {
128 _cleanup_fclose_
FILE *f
= NULL
;
129 _cleanup_free_
char *p
= NULL
;
132 r
= fopen_extension_release(root
, extension
, &p
, &f
);
136 return parse_env_filev(f
, p
, ap
);
139 int _parse_extension_release(const char *root
, const char *extension
, ...) {
143 va_start(ap
, extension
);
144 r
= parse_release_internal(root
, extension
, ap
);
150 int _parse_os_release(const char *root
, ...) {
155 r
= parse_release_internal(root
, NULL
, ap
);
161 int load_os_release_pairs(const char *root
, char ***ret
) {
162 _cleanup_fclose_
FILE *f
= NULL
;
163 _cleanup_free_
char *p
= NULL
;
166 r
= fopen_os_release(root
, &p
, &f
);
170 return load_env_file_pairs(f
, p
, ret
);
173 int load_os_release_pairs_with_prefix(const char *root
, const char *prefix
, char ***ret
) {
174 _cleanup_strv_free_
char **os_release_pairs
= NULL
, **os_release_pairs_prefixed
= NULL
;
178 r
= load_os_release_pairs(root
, &os_release_pairs
);
182 STRV_FOREACH_PAIR(p
, q
, os_release_pairs
) {
185 /* We strictly return only the four main ID fields and ignore the rest */
186 if (!STR_IN_SET(*p
, "ID", "VERSION_ID", "BUILD_ID", "VARIANT_ID"))
190 line
= strjoin(prefix
, *p
, "=", *q
);
193 r
= strv_consume(&os_release_pairs_prefixed
, line
);
198 *ret
= TAKE_PTR(os_release_pairs_prefixed
);
203 int load_extension_release_pairs(const char *root
, const char *extension
, char ***ret
) {
204 _cleanup_fclose_
FILE *f
= NULL
;
205 _cleanup_free_
char *p
= NULL
;
208 r
= fopen_extension_release(root
, extension
, &p
, &f
);
212 return load_env_file_pairs(f
, p
, ret
);