| File: | build-scan/../src/basic/path-util.c |
| Warning: | line 925, column 9 Potential leak of memory pointed to by 'path' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <errno(*__errno_location ()).h> | |||
| 4 | #include <limits.h> | |||
| 5 | #include <stdio.h> | |||
| 6 | #include <stdlib.h> | |||
| 7 | #include <string.h> | |||
| 8 | #include <sys/stat.h> | |||
| 9 | #include <unistd.h> | |||
| 10 | ||||
| 11 | /* When we include libgen.h because we need dirname() we immediately | |||
| 12 | * undefine basename() since libgen.h defines it as a macro to the | |||
| 13 | * POSIX version which is really broken. We prefer GNU basename(). */ | |||
| 14 | #include <libgen.h> | |||
| 15 | #undef basename | |||
| 16 | ||||
| 17 | #include "alloc-util.h" | |||
| 18 | #include "extract-word.h" | |||
| 19 | #include "fs-util.h" | |||
| 20 | #include "glob-util.h" | |||
| 21 | #include "log.h" | |||
| 22 | #include "macro.h" | |||
| 23 | #include "missing.h" | |||
| 24 | #include "parse-util.h" | |||
| 25 | #include "path-util.h" | |||
| 26 | #include "stat-util.h" | |||
| 27 | #include "string-util.h" | |||
| 28 | #include "strv.h" | |||
| 29 | #include "time-util.h" | |||
| 30 | #include "utf8.h" | |||
| 31 | ||||
| 32 | bool_Bool path_is_absolute(const char *p) { | |||
| 33 | return p[0] == '/'; | |||
| 34 | } | |||
| 35 | ||||
| 36 | bool_Bool is_path(const char *p) { | |||
| 37 | return !!strchr(p, '/'); | |||
| 38 | } | |||
| 39 | ||||
| 40 | int path_split_and_make_absolute(const char *p, char ***ret) { | |||
| 41 | char **l; | |||
| 42 | int r; | |||
| 43 | ||||
| 44 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/path-util.c", 44, __PRETTY_FUNCTION__ ); } while (0); | |||
| 45 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/path-util.c", 45, __PRETTY_FUNCTION__ ); } while (0); | |||
| 46 | ||||
| 47 | l = strv_split(p, ":"); | |||
| 48 | if (!l) | |||
| 49 | return -ENOMEM12; | |||
| 50 | ||||
| 51 | r = path_strv_make_absolute_cwd(l); | |||
| 52 | if (r < 0) { | |||
| 53 | strv_free(l); | |||
| 54 | return r; | |||
| 55 | } | |||
| 56 | ||||
| 57 | *ret = l; | |||
| 58 | return r; | |||
| 59 | } | |||
| 60 | ||||
| 61 | char *path_make_absolute(const char *p, const char *prefix) { | |||
| 62 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/path-util.c", 62, __PRETTY_FUNCTION__ ); } while (0); | |||
| 63 | ||||
| 64 | /* Makes every item in the list an absolute path by prepending | |||
| 65 | * the prefix, if specified and necessary */ | |||
| 66 | ||||
| 67 | if (path_is_absolute(p) || isempty(prefix)) | |||
| 68 | return strdup(p); | |||
| 69 | ||||
| 70 | if (endswith(prefix, "/")) | |||
| 71 | return strjoin(prefix, p)strjoin_real((prefix), p, ((void*)0)); | |||
| 72 | else | |||
| 73 | return strjoin(prefix, "/", p)strjoin_real((prefix), "/", p, ((void*)0)); | |||
| 74 | } | |||
| 75 | ||||
| 76 | int safe_getcwd(char **ret) { | |||
| 77 | char *cwd; | |||
| 78 | ||||
| 79 | cwd = get_current_dir_name(); | |||
| 80 | if (!cwd) | |||
| 81 | return negative_errno(); | |||
| 82 | ||||
| 83 | /* Let's make sure the directory is really absolute, to protect us from the logic behind | |||
| 84 | * CVE-2018-1000001 */ | |||
| 85 | if (cwd[0] != '/') { | |||
| 86 | free(cwd); | |||
| 87 | return -ENOMEDIUM123; | |||
| 88 | } | |||
| 89 | ||||
| 90 | *ret = cwd; | |||
| 91 | return 0; | |||
| 92 | } | |||
| 93 | ||||
| 94 | int path_make_absolute_cwd(const char *p, char **ret) { | |||
| 95 | char *c; | |||
| 96 | int r; | |||
| 97 | ||||
| 98 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/path-util.c", 98, __PRETTY_FUNCTION__ ); } while (0); | |||
| 99 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/path-util.c", 99, __PRETTY_FUNCTION__ ); } while (0); | |||
| 100 | ||||
| 101 | /* Similar to path_make_absolute(), but prefixes with the | |||
| 102 | * current working directory. */ | |||
| 103 | ||||
| 104 | if (path_is_absolute(p)) | |||
| 105 | c = strdup(p); | |||
| 106 | else { | |||
| 107 | _cleanup_free___attribute__((cleanup(freep))) char *cwd = NULL((void*)0); | |||
| 108 | ||||
| 109 | r = safe_getcwd(&cwd); | |||
| 110 | if (r < 0) | |||
| 111 | return r; | |||
| 112 | ||||
| 113 | if (endswith(cwd, "/")) | |||
| 114 | c = strjoin(cwd, p)strjoin_real((cwd), p, ((void*)0)); | |||
| 115 | else | |||
| 116 | c = strjoin(cwd, "/", p)strjoin_real((cwd), "/", p, ((void*)0)); | |||
| 117 | } | |||
| 118 | if (!c) | |||
| 119 | return -ENOMEM12; | |||
| 120 | ||||
| 121 | *ret = c; | |||
| 122 | return 0; | |||
| 123 | } | |||
| 124 | ||||
| 125 | int path_make_relative(const char *from_dir, const char *to_path, char **_r) { | |||
| 126 | char *f, *t, *r, *p; | |||
| 127 | unsigned n_parents = 0; | |||
| 128 | ||||
| 129 | assert(from_dir)do { if ((__builtin_expect(!!(!(from_dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("from_dir"), "../src/basic/path-util.c", 129, __PRETTY_FUNCTION__); } while (0); | |||
| 130 | assert(to_path)do { if ((__builtin_expect(!!(!(to_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("to_path"), "../src/basic/path-util.c", 130 , __PRETTY_FUNCTION__); } while (0); | |||
| 131 | assert(_r)do { if ((__builtin_expect(!!(!(_r)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_r"), "../src/basic/path-util.c", 131, __PRETTY_FUNCTION__ ); } while (0); | |||
| 132 | ||||
| 133 | /* Strips the common part, and adds ".." elements as necessary. */ | |||
| 134 | ||||
| 135 | if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) | |||
| 136 | return -EINVAL22; | |||
| 137 | ||||
| 138 | f = strdupa(from_dir)(__extension__ ({ const char *__old = (from_dir); size_t __len = strlen (__old) + 1; char *__new = (char *) __builtin_alloca (__len); (char *) memcpy (__new, __old, __len); })); | |||
| 139 | t = strdupa(to_path)(__extension__ ({ const char *__old = (to_path); size_t __len = strlen (__old) + 1; char *__new = (char *) __builtin_alloca (__len); (char *) memcpy (__new, __old, __len); })); | |||
| 140 | ||||
| 141 | path_simplify(f, true1); | |||
| 142 | path_simplify(t, true1); | |||
| 143 | ||||
| 144 | /* Skip the common part. */ | |||
| 145 | for (;;) { | |||
| 146 | size_t a, b; | |||
| 147 | ||||
| 148 | f += *f == '/'; | |||
| 149 | t += *t == '/'; | |||
| 150 | ||||
| 151 | if (!*f) { | |||
| 152 | if (!*t) | |||
| 153 | /* from_dir equals to_path. */ | |||
| 154 | r = strdup("."); | |||
| 155 | else | |||
| 156 | /* from_dir is a parent directory of to_path. */ | |||
| 157 | r = strdup(t); | |||
| 158 | if (!r) | |||
| 159 | return -ENOMEM12; | |||
| 160 | ||||
| 161 | *_r = r; | |||
| 162 | return 0; | |||
| 163 | } | |||
| 164 | ||||
| 165 | if (!*t) | |||
| 166 | break; | |||
| 167 | ||||
| 168 | a = strcspn(f, "/"); | |||
| 169 | b = strcspn(t, "/"); | |||
| 170 | ||||
| 171 | if (a != b || memcmp(f, t, a) != 0) | |||
| 172 | break; | |||
| 173 | ||||
| 174 | f += a; | |||
| 175 | t += b; | |||
| 176 | } | |||
| 177 | ||||
| 178 | /* If we're here, then "from_dir" has one or more elements that need to | |||
| 179 | * be replaced with "..". */ | |||
| 180 | ||||
| 181 | /* Count the number of necessary ".." elements. */ | |||
| 182 | for (; *f;) { | |||
| 183 | size_t w; | |||
| 184 | ||||
| 185 | w = strcspn(f, "/"); | |||
| 186 | ||||
| 187 | /* If this includes ".." we can't do a simple series of "..", refuse */ | |||
| 188 | if (w == 2 && f[0] == '.' && f[1] == '.') | |||
| 189 | return -EINVAL22; | |||
| 190 | ||||
| 191 | /* Count number of elements */ | |||
| 192 | n_parents++; | |||
| 193 | ||||
| 194 | f += w; | |||
| 195 | f += *f == '/'; | |||
| 196 | } | |||
| 197 | ||||
| 198 | r = new(char, n_parents * 3 + strlen(t) + 1)((char*) malloc_multiply(sizeof(char), (n_parents * 3 + strlen (t) + 1))); | |||
| 199 | if (!r) | |||
| 200 | return -ENOMEM12; | |||
| 201 | ||||
| 202 | for (p = r; n_parents > 0; n_parents--) | |||
| 203 | p = mempcpy(p, "../", 3); | |||
| 204 | ||||
| 205 | if (*t) | |||
| 206 | strcpy(p, t); | |||
| 207 | else | |||
| 208 | /* Remove trailing slash */ | |||
| 209 | *(--p) = 0; | |||
| 210 | ||||
| 211 | *_r = r; | |||
| 212 | return 0; | |||
| 213 | } | |||
| 214 | ||||
| 215 | int path_strv_make_absolute_cwd(char **l) { | |||
| 216 | char **s; | |||
| 217 | int r; | |||
| 218 | ||||
| 219 | /* Goes through every item in the string list and makes it | |||
| 220 | * absolute. This works in place and won't rollback any | |||
| 221 | * changes on failure. */ | |||
| 222 | ||||
| 223 | STRV_FOREACH(s, l)for ((s) = (l); (s) && *(s); (s)++) { | |||
| 224 | char *t; | |||
| 225 | ||||
| 226 | r = path_make_absolute_cwd(*s, &t); | |||
| 227 | if (r < 0) | |||
| 228 | return r; | |||
| 229 | ||||
| 230 | path_simplify(t, false0); | |||
| 231 | free_and_replace(*s, t)({ free(*s); (*s) = (t); (t) = ((void*)0); 0; }); | |||
| 232 | } | |||
| 233 | ||||
| 234 | return 0; | |||
| 235 | } | |||
| 236 | ||||
| 237 | char **path_strv_resolve(char **l, const char *root) { | |||
| 238 | char **s; | |||
| 239 | unsigned k = 0; | |||
| 240 | bool_Bool enomem = false0; | |||
| 241 | int r; | |||
| 242 | ||||
| 243 | if (strv_isempty(l)) | |||
| 244 | return l; | |||
| 245 | ||||
| 246 | /* Goes through every item in the string list and canonicalize | |||
| 247 | * the path. This works in place and won't rollback any | |||
| 248 | * changes on failure. */ | |||
| 249 | ||||
| 250 | STRV_FOREACH(s, l)for ((s) = (l); (s) && *(s); (s)++) { | |||
| 251 | _cleanup_free___attribute__((cleanup(freep))) char *orig = NULL((void*)0); | |||
| 252 | char *t, *u; | |||
| 253 | ||||
| 254 | if (!path_is_absolute(*s)) { | |||
| 255 | free(*s); | |||
| 256 | continue; | |||
| 257 | } | |||
| 258 | ||||
| 259 | if (root) { | |||
| 260 | orig = *s; | |||
| 261 | t = prefix_root(root, orig); | |||
| 262 | if (!t) { | |||
| 263 | enomem = true1; | |||
| 264 | continue; | |||
| 265 | } | |||
| 266 | } else | |||
| 267 | t = *s; | |||
| 268 | ||||
| 269 | r = chase_symlinks(t, root, 0, &u); | |||
| 270 | if (r == -ENOENT2) { | |||
| 271 | if (root) { | |||
| 272 | u = TAKE_PTR(orig)({ typeof(orig) _ptr_ = (orig); (orig) = ((void*)0); _ptr_; } ); | |||
| 273 | free(t); | |||
| 274 | } else | |||
| 275 | u = t; | |||
| 276 | } else if (r < 0) { | |||
| 277 | free(t); | |||
| 278 | ||||
| 279 | if (r == -ENOMEM12) | |||
| 280 | enomem = true1; | |||
| 281 | ||||
| 282 | continue; | |||
| 283 | } else if (root) { | |||
| 284 | char *x; | |||
| 285 | ||||
| 286 | free(t); | |||
| 287 | x = path_startswith(u, root); | |||
| 288 | if (x) { | |||
| 289 | /* restore the slash if it was lost */ | |||
| 290 | if (!startswith(x, "/")) | |||
| 291 | *(--x) = '/'; | |||
| 292 | ||||
| 293 | t = strdup(x); | |||
| 294 | free(u); | |||
| 295 | if (!t) { | |||
| 296 | enomem = true1; | |||
| 297 | continue; | |||
| 298 | } | |||
| 299 | u = t; | |||
| 300 | } else { | |||
| 301 | /* canonicalized path goes outside of | |||
| 302 | * prefix, keep the original path instead */ | |||
| 303 | free_and_replace(u, orig)({ free(u); (u) = (orig); (orig) = ((void*)0); 0; }); | |||
| 304 | } | |||
| 305 | } else | |||
| 306 | free(t); | |||
| 307 | ||||
| 308 | l[k++] = u; | |||
| 309 | } | |||
| 310 | ||||
| 311 | l[k] = NULL((void*)0); | |||
| 312 | ||||
| 313 | if (enomem) | |||
| 314 | return NULL((void*)0); | |||
| 315 | ||||
| 316 | return l; | |||
| 317 | } | |||
| 318 | ||||
| 319 | char **path_strv_resolve_uniq(char **l, const char *root) { | |||
| 320 | ||||
| 321 | if (strv_isempty(l)) | |||
| 322 | return l; | |||
| 323 | ||||
| 324 | if (!path_strv_resolve(l, root)) | |||
| 325 | return NULL((void*)0); | |||
| 326 | ||||
| 327 | return strv_uniq(l); | |||
| 328 | } | |||
| 329 | ||||
| 330 | char *path_simplify(char *path, bool_Bool kill_dots) { | |||
| 331 | char *f, *t; | |||
| 332 | bool_Bool slash = false0, ignore_slash = false0, absolute; | |||
| 333 | ||||
| 334 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 334 , __PRETTY_FUNCTION__); } while (0); | |||
| 335 | ||||
| 336 | /* Removes redundant inner and trailing slashes. Also removes unnecessary dots | |||
| 337 | * if kill_dots is true. Modifies the passed string in-place. | |||
| 338 | * | |||
| 339 | * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) | |||
| 340 | * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) | |||
| 341 | * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false) | |||
| 342 | * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) | |||
| 343 | */ | |||
| 344 | ||||
| 345 | absolute = path_is_absolute(path); | |||
| 346 | ||||
| 347 | f = path; | |||
| 348 | if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, '/'})/sizeof(int)]; switch(f[1]) { case 0: case '/': _found = 1; break; default: break; } _found; })) { | |||
| 349 | ignore_slash = true1; | |||
| 350 | f++; | |||
| 351 | } | |||
| 352 | ||||
| 353 | for (t = path; *f; f++) { | |||
| 354 | ||||
| 355 | if (*f == '/') { | |||
| 356 | slash = true1; | |||
| 357 | continue; | |||
| 358 | } | |||
| 359 | ||||
| 360 | if (slash) { | |||
| 361 | if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, '/'})/sizeof(int)]; switch(f[1]) { case 0: case '/': _found = 1; break; default: break; } _found; })) | |||
| 362 | continue; | |||
| 363 | ||||
| 364 | slash = false0; | |||
| 365 | if (ignore_slash) | |||
| 366 | ignore_slash = false0; | |||
| 367 | else | |||
| 368 | *(t++) = '/'; | |||
| 369 | } | |||
| 370 | ||||
| 371 | *(t++) = *f; | |||
| 372 | } | |||
| 373 | ||||
| 374 | /* Special rule, if we are talking of the root directory, a trailing slash is good */ | |||
| 375 | if (absolute && t == path) | |||
| 376 | *(t++) = '/'; | |||
| 377 | ||||
| 378 | *t = 0; | |||
| 379 | return path; | |||
| 380 | } | |||
| 381 | ||||
| 382 | char* path_startswith(const char *path, const char *prefix) { | |||
| 383 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 383 , __PRETTY_FUNCTION__); } while (0); | |||
| 384 | assert(prefix)do { if ((__builtin_expect(!!(!(prefix)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("prefix"), "../src/basic/path-util.c", 384 , __PRETTY_FUNCTION__); } while (0); | |||
| 385 | ||||
| 386 | /* Returns a pointer to the start of the first component after the parts matched by | |||
| 387 | * the prefix, iff | |||
| 388 | * - both paths are absolute or both paths are relative, | |||
| 389 | * and | |||
| 390 | * - each component in prefix in turn matches a component in path at the same position. | |||
| 391 | * An empty string will be returned when the prefix and path are equivalent. | |||
| 392 | * | |||
| 393 | * Returns NULL otherwise. | |||
| 394 | */ | |||
| 395 | ||||
| 396 | if ((path[0] == '/') != (prefix[0] == '/')) | |||
| 397 | return NULL((void*)0); | |||
| 398 | ||||
| 399 | for (;;) { | |||
| 400 | size_t a, b; | |||
| 401 | ||||
| 402 | path += strspn(path, "/"); | |||
| 403 | prefix += strspn(prefix, "/"); | |||
| 404 | ||||
| 405 | if (*prefix == 0) | |||
| 406 | return (char*) path; | |||
| 407 | ||||
| 408 | if (*path == 0) | |||
| 409 | return NULL((void*)0); | |||
| 410 | ||||
| 411 | a = strcspn(path, "/"); | |||
| 412 | b = strcspn(prefix, "/"); | |||
| 413 | ||||
| 414 | if (a != b) | |||
| 415 | return NULL((void*)0); | |||
| 416 | ||||
| 417 | if (memcmp(path, prefix, a) != 0) | |||
| 418 | return NULL((void*)0); | |||
| 419 | ||||
| 420 | path += a; | |||
| 421 | prefix += b; | |||
| 422 | } | |||
| 423 | } | |||
| 424 | ||||
| 425 | int path_compare(const char *a, const char *b) { | |||
| 426 | int d; | |||
| 427 | ||||
| 428 | assert(a)do { if ((__builtin_expect(!!(!(a)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("a"), "../src/basic/path-util.c", 428, __PRETTY_FUNCTION__ ); } while (0); | |||
| 429 | assert(b)do { if ((__builtin_expect(!!(!(b)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("b"), "../src/basic/path-util.c", 429, __PRETTY_FUNCTION__ ); } while (0); | |||
| 430 | ||||
| 431 | /* A relative path and an abolute path must not compare as equal. | |||
| 432 | * Which one is sorted before the other does not really matter. | |||
| 433 | * Here a relative path is ordered before an absolute path. */ | |||
| 434 | d = (a[0] == '/') - (b[0] == '/'); | |||
| 435 | if (d != 0) | |||
| 436 | return d; | |||
| 437 | ||||
| 438 | for (;;) { | |||
| 439 | size_t j, k; | |||
| 440 | ||||
| 441 | a += strspn(a, "/"); | |||
| 442 | b += strspn(b, "/"); | |||
| 443 | ||||
| 444 | if (*a == 0 && *b == 0) | |||
| 445 | return 0; | |||
| 446 | ||||
| 447 | /* Order prefixes first: "/foo" before "/foo/bar" */ | |||
| 448 | if (*a == 0) | |||
| 449 | return -1; | |||
| 450 | if (*b == 0) | |||
| 451 | return 1; | |||
| 452 | ||||
| 453 | j = strcspn(a, "/"); | |||
| 454 | k = strcspn(b, "/"); | |||
| 455 | ||||
| 456 | /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ | |||
| 457 | d = memcmp(a, b, MIN(j, k)__extension__ ({ const typeof((j)) __unique_prefix_A14 = ((j) ); const typeof((k)) __unique_prefix_B15 = ((k)); __unique_prefix_A14 < __unique_prefix_B15 ? __unique_prefix_A14 : __unique_prefix_B15 ; })); | |||
| 458 | if (d != 0) | |||
| 459 | return (d > 0) - (d < 0); /* sign of d */ | |||
| 460 | ||||
| 461 | /* Sort "/foo/a" before "/foo/aaa" */ | |||
| 462 | d = (j > k) - (j < k); /* sign of (j - k) */ | |||
| 463 | if (d != 0) | |||
| 464 | return d; | |||
| 465 | ||||
| 466 | a += j; | |||
| 467 | b += k; | |||
| 468 | } | |||
| 469 | } | |||
| 470 | ||||
| 471 | bool_Bool path_equal(const char *a, const char *b) { | |||
| 472 | return path_compare(a, b) == 0; | |||
| 473 | } | |||
| 474 | ||||
| 475 | bool_Bool path_equal_or_files_same(const char *a, const char *b, int flags) { | |||
| 476 | return path_equal(a, b) || files_same(a, b, flags) > 0; | |||
| 477 | } | |||
| 478 | ||||
| 479 | char* path_join(const char *root, const char *path, const char *rest) { | |||
| 480 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 480 , __PRETTY_FUNCTION__); } while (0); | |||
| 481 | ||||
| 482 | if (!isempty(root)) | |||
| 483 | return strjoin(root, endswith(root, "/") ? "" : "/",strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)) | |||
| 484 | path[0] == '/' ? path+1 : path,strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)) | |||
| 485 | rest ? (endswith(path, "/") ? "" : "/") : NULL,strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)) | |||
| 486 | rest && rest[0] == '/' ? rest+1 : rest)strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)); | |||
| 487 | else | |||
| 488 | return strjoin(path,strjoin_real((path), rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest, ( (void*)0)) | |||
| 489 | rest ? (endswith(path, "/") ? "" : "/") : NULL,strjoin_real((path), rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest, ( (void*)0)) | |||
| 490 | rest && rest[0] == '/' ? rest+1 : rest)strjoin_real((path), rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest, ( (void*)0)); | |||
| 491 | } | |||
| 492 | ||||
| 493 | int find_binary(const char *name, char **ret) { | |||
| 494 | int last_error, r; | |||
| 495 | const char *p; | |||
| 496 | ||||
| 497 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/basic/path-util.c", 497 , __PRETTY_FUNCTION__); } while (0); | |||
| 498 | ||||
| 499 | if (is_path(name)) { | |||
| 500 | if (access(name, X_OK1) < 0) | |||
| 501 | return -errno(*__errno_location ()); | |||
| 502 | ||||
| 503 | if (ret) { | |||
| 504 | r = path_make_absolute_cwd(name, ret); | |||
| 505 | if (r < 0) | |||
| 506 | return r; | |||
| 507 | } | |||
| 508 | ||||
| 509 | return 0; | |||
| 510 | } | |||
| 511 | ||||
| 512 | /** | |||
| 513 | * Plain getenv, not secure_getenv, because we want | |||
| 514 | * to actually allow the user to pick the binary. | |||
| 515 | */ | |||
| 516 | p = getenv("PATH"); | |||
| 517 | if (!p) | |||
| 518 | p = DEFAULT_PATH"/usr/local/" "sbin:" "/usr/local/" "bin" ":" "/usr/" "sbin:" "/usr/" "bin"; | |||
| 519 | ||||
| 520 | last_error = -ENOENT2; | |||
| 521 | ||||
| 522 | for (;;) { | |||
| 523 | _cleanup_free___attribute__((cleanup(freep))) char *j = NULL((void*)0), *element = NULL((void*)0); | |||
| 524 | ||||
| 525 | r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); | |||
| 526 | if (r < 0) | |||
| 527 | return r; | |||
| 528 | if (r == 0) | |||
| 529 | break; | |||
| 530 | ||||
| 531 | if (!path_is_absolute(element)) | |||
| 532 | continue; | |||
| 533 | ||||
| 534 | j = strjoin(element, "/", name)strjoin_real((element), "/", name, ((void*)0)); | |||
| 535 | if (!j) | |||
| 536 | return -ENOMEM12; | |||
| 537 | ||||
| 538 | if (access(j, X_OK1) >= 0) { | |||
| 539 | /* Found it! */ | |||
| 540 | ||||
| 541 | if (ret) { | |||
| 542 | *ret = path_simplify(j, false0); | |||
| 543 | j = NULL((void*)0); | |||
| 544 | } | |||
| 545 | ||||
| 546 | return 0; | |||
| 547 | } | |||
| 548 | ||||
| 549 | last_error = -errno(*__errno_location ()); | |||
| 550 | } | |||
| 551 | ||||
| 552 | return last_error; | |||
| 553 | } | |||
| 554 | ||||
| 555 | bool_Bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool_Bool update) { | |||
| 556 | bool_Bool changed = false0; | |||
| 557 | const char* const* i; | |||
| 558 | ||||
| 559 | assert(timestamp)do { if ((__builtin_expect(!!(!(timestamp)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("timestamp"), "../src/basic/path-util.c" , 559, __PRETTY_FUNCTION__); } while (0); | |||
| 560 | ||||
| 561 | if (!paths) | |||
| 562 | return false0; | |||
| 563 | ||||
| 564 | STRV_FOREACH(i, paths)for ((i) = (paths); (i) && *(i); (i)++) { | |||
| 565 | struct stat stats; | |||
| 566 | usec_t u; | |||
| 567 | ||||
| 568 | if (stat(*i, &stats) < 0) | |||
| 569 | continue; | |||
| 570 | ||||
| 571 | u = timespec_load(&stats.st_mtim); | |||
| 572 | ||||
| 573 | /* first check */ | |||
| 574 | if (*timestamp >= u) | |||
| 575 | continue; | |||
| 576 | ||||
| 577 | log_debug("timestamp of '%s' changed", *i)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 577, __func__, "timestamp of '%s' changed" , *i) : -abs(_e); }); | |||
| 578 | ||||
| 579 | /* update timestamp */ | |||
| 580 | if (update) { | |||
| 581 | *timestamp = u; | |||
| 582 | changed = true1; | |||
| 583 | } else | |||
| 584 | return true1; | |||
| 585 | } | |||
| 586 | ||||
| 587 | return changed; | |||
| 588 | } | |||
| 589 | ||||
| 590 | static int binary_is_good(const char *binary) { | |||
| 591 | _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0), *d = NULL((void*)0); | |||
| 592 | int r; | |||
| 593 | ||||
| 594 | r = find_binary(binary, &p); | |||
| 595 | if (r == -ENOENT2) | |||
| 596 | return 0; | |||
| 597 | if (r < 0) | |||
| 598 | return r; | |||
| 599 | ||||
| 600 | /* An fsck that is linked to /bin/true is a non-existent | |||
| 601 | * fsck */ | |||
| 602 | ||||
| 603 | r = readlink_malloc(p, &d); | |||
| 604 | if (r == -EINVAL22) /* not a symlink */ | |||
| 605 | return 1; | |||
| 606 | if (r < 0) | |||
| 607 | return r; | |||
| 608 | ||||
| 609 | return !PATH_IN_SET(d, "true"({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }) | |||
| 610 | "/bin/true",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }) | |||
| 611 | "/usr/bin/true",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }) | |||
| 612 | "/dev/null")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }); | |||
| 613 | } | |||
| 614 | ||||
| 615 | int fsck_exists(const char *fstype) { | |||
| 616 | const char *checker; | |||
| 617 | ||||
| 618 | assert(fstype)do { if ((__builtin_expect(!!(!(fstype)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fstype"), "../src/basic/path-util.c", 618 , __PRETTY_FUNCTION__); } while (0); | |||
| 619 | ||||
| 620 | if (streq(fstype, "auto")(strcmp((fstype),("auto")) == 0)) | |||
| 621 | return -EINVAL22; | |||
| 622 | ||||
| 623 | checker = strjoina("fsck.", fstype)({ const char *_appendees_[] = { "fsck.", fstype }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 624 | return binary_is_good(checker); | |||
| 625 | } | |||
| 626 | ||||
| 627 | int mkfs_exists(const char *fstype) { | |||
| 628 | const char *mkfs; | |||
| 629 | ||||
| 630 | assert(fstype)do { if ((__builtin_expect(!!(!(fstype)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fstype"), "../src/basic/path-util.c", 630 , __PRETTY_FUNCTION__); } while (0); | |||
| 631 | ||||
| 632 | if (streq(fstype, "auto")(strcmp((fstype),("auto")) == 0)) | |||
| 633 | return -EINVAL22; | |||
| 634 | ||||
| 635 | mkfs = strjoina("mkfs.", fstype)({ const char *_appendees_[] = { "mkfs.", fstype }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
| 636 | return binary_is_good(mkfs); | |||
| 637 | } | |||
| 638 | ||||
| 639 | char *prefix_root(const char *root, const char *path) { | |||
| 640 | char *n, *p; | |||
| 641 | size_t l; | |||
| 642 | ||||
| 643 | /* If root is passed, prefixes path with it. Otherwise returns | |||
| 644 | * it as is. */ | |||
| 645 | ||||
| 646 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 646 , __PRETTY_FUNCTION__); } while (0); | |||
| 647 | ||||
| 648 | /* First, drop duplicate prefixing slashes from the path */ | |||
| 649 | while (path[0] == '/' && path[1] == '/') | |||
| 650 | path++; | |||
| 651 | ||||
| 652 | if (empty_or_root(root)) | |||
| 653 | return strdup(path); | |||
| 654 | ||||
| 655 | l = strlen(root) + 1 + strlen(path) + 1; | |||
| 656 | ||||
| 657 | n = new(char, l)((char*) malloc_multiply(sizeof(char), (l))); | |||
| 658 | if (!n) | |||
| 659 | return NULL((void*)0); | |||
| 660 | ||||
| 661 | p = stpcpy(n, root); | |||
| 662 | ||||
| 663 | while (p > n && p[-1] == '/') | |||
| 664 | p--; | |||
| 665 | ||||
| 666 | if (path[0] != '/') | |||
| 667 | *(p++) = '/'; | |||
| 668 | ||||
| 669 | strcpy(p, path); | |||
| 670 | return n; | |||
| 671 | } | |||
| 672 | ||||
| 673 | int parse_path_argument_and_warn(const char *path, bool_Bool suppress_root, char **arg) { | |||
| 674 | char *p; | |||
| 675 | int r; | |||
| 676 | ||||
| 677 | /* | |||
| 678 | * This function is intended to be used in command line | |||
| 679 | * parsers, to handle paths that are passed in. It makes the | |||
| 680 | * path absolute, and reduces it to NULL if omitted or | |||
| 681 | * root (the latter optionally). | |||
| 682 | * | |||
| 683 | * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON | |||
| 684 | * SUCCESS! Hence, do not pass in uninitialized pointers. | |||
| 685 | */ | |||
| 686 | ||||
| 687 | if (isempty(path)) { | |||
| 688 | *arg = mfree(*arg); | |||
| 689 | return 0; | |||
| 690 | } | |||
| 691 | ||||
| 692 | r = path_make_absolute_cwd(path, &p); | |||
| 693 | if (r < 0) | |||
| 694 | return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 694, __func__, "Failed to parse path \"%s\" and make it absolute: %m" , path) : -abs(_e); }); | |||
| 695 | ||||
| 696 | path_simplify(p, false0); | |||
| 697 | if (suppress_root && empty_or_root(p)) | |||
| 698 | p = mfree(p); | |||
| 699 | ||||
| 700 | free_and_replace(*arg, p)({ free(*arg); (*arg) = (p); (p) = ((void*)0); 0; }); | |||
| 701 | ||||
| 702 | return 0; | |||
| 703 | } | |||
| 704 | ||||
| 705 | char* dirname_malloc(const char *path) { | |||
| 706 | char *d, *dir, *dir2; | |||
| 707 | ||||
| 708 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 708 , __PRETTY_FUNCTION__); } while (0); | |||
| 709 | ||||
| 710 | d = strdup(path); | |||
| 711 | if (!d) | |||
| 712 | return NULL((void*)0); | |||
| 713 | ||||
| 714 | dir = dirname(d); | |||
| 715 | assert(dir)do { if ((__builtin_expect(!!(!(dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dir"), "../src/basic/path-util.c", 715, __PRETTY_FUNCTION__); } while (0); | |||
| 716 | ||||
| 717 | if (dir == d) | |||
| 718 | return d; | |||
| 719 | ||||
| 720 | dir2 = strdup(dir); | |||
| 721 | free(d); | |||
| 722 | ||||
| 723 | return dir2; | |||
| 724 | } | |||
| 725 | ||||
| 726 | const char *last_path_component(const char *path) { | |||
| 727 | ||||
| 728 | /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory. | |||
| 729 | * | |||
| 730 | * a/b/c → c | |||
| 731 | * a/b/c/ → c/ | |||
| 732 | * x → x | |||
| 733 | * x/ → x/ | |||
| 734 | * /y → y | |||
| 735 | * /y/ → y/ | |||
| 736 | * / → / | |||
| 737 | * // → / | |||
| 738 | * /foo/a → a | |||
| 739 | * /foo/a/ → a/ | |||
| 740 | * | |||
| 741 | * Also, the empty string is mapped to itself. | |||
| 742 | * | |||
| 743 | * This is different than basename(), which returns "" when a trailing slash is present. | |||
| 744 | */ | |||
| 745 | ||||
| 746 | unsigned l, k; | |||
| 747 | ||||
| 748 | l = k = strlen(path); | |||
| 749 | if (l == 0) /* special case — an empty string */ | |||
| 750 | return path; | |||
| 751 | ||||
| 752 | while (k > 0 && path[k-1] == '/') | |||
| 753 | k--; | |||
| 754 | ||||
| 755 | if (k == 0) /* the root directory */ | |||
| 756 | return path + l - 1; | |||
| 757 | ||||
| 758 | while (k > 0 && path[k-1] != '/') | |||
| 759 | k--; | |||
| 760 | ||||
| 761 | return path + k; | |||
| 762 | } | |||
| 763 | ||||
| 764 | bool_Bool filename_is_valid(const char *p) { | |||
| 765 | const char *e; | |||
| 766 | ||||
| 767 | if (isempty(p)) | |||
| 768 | return false0; | |||
| 769 | ||||
| 770 | if (dot_or_dot_dot(p)) | |||
| 771 | return false0; | |||
| 772 | ||||
| 773 | e = strchrnul(p, '/'); | |||
| 774 | if (*e != 0) | |||
| 775 | return false0; | |||
| 776 | ||||
| 777 | if (e - p > FILENAME_MAX4096) | |||
| 778 | return false0; | |||
| 779 | ||||
| 780 | return true1; | |||
| 781 | } | |||
| 782 | ||||
| 783 | bool_Bool path_is_normalized(const char *p) { | |||
| 784 | ||||
| 785 | if (isempty(p)) | |||
| 786 | return false0; | |||
| 787 | ||||
| 788 | if (dot_or_dot_dot(p)) | |||
| 789 | return false0; | |||
| 790 | ||||
| 791 | if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) | |||
| 792 | return false0; | |||
| 793 | ||||
| 794 | if (strlen(p)+1 > PATH_MAX4096) | |||
| 795 | return false0; | |||
| 796 | ||||
| 797 | if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) | |||
| 798 | return false0; | |||
| 799 | ||||
| 800 | if (strstr(p, "//")) | |||
| 801 | return false0; | |||
| 802 | ||||
| 803 | return true1; | |||
| 804 | } | |||
| 805 | ||||
| 806 | char *file_in_same_dir(const char *path, const char *filename) { | |||
| 807 | char *e, *ret; | |||
| 808 | size_t k; | |||
| 809 | ||||
| 810 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 810 , __PRETTY_FUNCTION__); } while (0); | |||
| 811 | assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("filename"), "../src/basic/path-util.c", 811, __PRETTY_FUNCTION__); } while (0); | |||
| 812 | ||||
| 813 | /* This removes the last component of path and appends | |||
| 814 | * filename, unless the latter is absolute anyway or the | |||
| 815 | * former isn't */ | |||
| 816 | ||||
| 817 | if (path_is_absolute(filename)) | |||
| 818 | return strdup(filename); | |||
| 819 | ||||
| 820 | e = strrchr(path, '/'); | |||
| 821 | if (!e) | |||
| 822 | return strdup(filename); | |||
| 823 | ||||
| 824 | k = strlen(filename); | |||
| 825 | ret = new(char, (e + 1 - path) + k + 1)((char*) malloc_multiply(sizeof(char), ((e + 1 - path) + k + 1 ))); | |||
| 826 | if (!ret) | |||
| 827 | return NULL((void*)0); | |||
| 828 | ||||
| 829 | memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); | |||
| 830 | return ret; | |||
| 831 | } | |||
| 832 | ||||
| 833 | bool_Bool hidden_or_backup_file(const char *filename) { | |||
| 834 | const char *p; | |||
| 835 | ||||
| 836 | assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("filename"), "../src/basic/path-util.c", 836, __PRETTY_FUNCTION__); } while (0); | |||
| 837 | ||||
| 838 | if (filename[0] == '.' || | |||
| 839 | streq(filename, "lost+found")(strcmp((filename),("lost+found")) == 0) || | |||
| 840 | streq(filename, "aquota.user")(strcmp((filename),("aquota.user")) == 0) || | |||
| 841 | streq(filename, "aquota.group")(strcmp((filename),("aquota.group")) == 0) || | |||
| 842 | endswith(filename, "~")) | |||
| 843 | return true1; | |||
| 844 | ||||
| 845 | p = strrchr(filename, '.'); | |||
| 846 | if (!p) | |||
| 847 | return false0; | |||
| 848 | ||||
| 849 | /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up | |||
| 850 | * with always new suffixes and that everybody else should just adjust to that, then it really should be on | |||
| 851 | * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt | |||
| 852 | * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional | |||
| 853 | * string. Specifically: there's now: | |||
| 854 | * | |||
| 855 | * The generic suffixes "~" and ".bak" for backup files | |||
| 856 | * The generic prefix "." for hidden files | |||
| 857 | * | |||
| 858 | * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" | |||
| 859 | * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. | |||
| 860 | */ | |||
| 861 | ||||
| 862 | return STR_IN_SET(p + 1,(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 863 | "rpmnew",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 864 | "rpmsave",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 865 | "rpmorig",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 866 | "dpkg-old",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 867 | "dpkg-new",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 868 | "dpkg-tmp",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 869 | "dpkg-dist",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 870 | "dpkg-bak",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 871 | "dpkg-backup",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 872 | "dpkg-remove",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 873 | "ucf-new",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 874 | "ucf-old",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 875 | "ucf-dist",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 876 | "swp",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 877 | "bak",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 878 | "old",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
| 879 | "new")(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))); | |||
| 880 | } | |||
| 881 | ||||
| 882 | bool_Bool is_device_path(const char *path) { | |||
| 883 | ||||
| 884 | /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ | |||
| 885 | ||||
| 886 | return PATH_STARTSWITH_SET(path, "/dev/", "/sys/")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/dev/", "/sys/", ((void*)0) }))); (s) && *(s) ; (s)++) if (path_startswith(path, *s)) { _found = 1; break; } _found; }); | |||
| 887 | } | |||
| 888 | ||||
| 889 | bool_Bool valid_device_node_path(const char *path) { | |||
| 890 | ||||
| 891 | /* Some superficial checks whether the specified path is a valid device node path, all without looking at the | |||
| 892 | * actual device node. */ | |||
| 893 | ||||
| 894 | if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/dev/", "/run/systemd/inaccessible/", ((void*)0) }))) ; (s) && *(s); (s)++) if (path_startswith(path, *s)) { _found = 1; break; } _found; })) | |||
| 895 | return false0; | |||
| 896 | ||||
| 897 | if (endswith(path, "/")) /* can't be a device node if it ends in a slash */ | |||
| 898 | return false0; | |||
| 899 | ||||
| 900 | return path_is_normalized(path); | |||
| 901 | } | |||
| 902 | ||||
| 903 | bool_Bool valid_device_allow_pattern(const char *path) { | |||
| 904 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 904 , __PRETTY_FUNCTION__); } while (0); | |||
| 905 | ||||
| 906 | /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= | |||
| 907 | * accept it */ | |||
| 908 | ||||
| 909 | if (startswith(path, "block-") || | |||
| 910 | startswith(path, "char-")) | |||
| 911 | return true1; | |||
| 912 | ||||
| 913 | return valid_device_node_path(path); | |||
| 914 | } | |||
| 915 | ||||
| 916 | int systemd_installation_has_version(const char *root, unsigned minimal_version) { | |||
| 917 | const char *pattern; | |||
| 918 | int r; | |||
| 919 | ||||
| 920 | /* Try to guess if systemd installation is later than the specified version. This | |||
| 921 | * is hacky and likely to yield false negatives, particularly if the installation | |||
| 922 | * is non-standard. False positives should be relatively rare. | |||
| 923 | */ | |||
| 924 | ||||
| 925 | NULSTR_FOREACH(pattern,for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| ||||
| ||||
| 926 | /* /lib works for systems without usr-merge, and for systems with a sanefor ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| 927 | * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessaryfor ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| 928 | * for Gentoo which does a merge without making /lib a symlink.for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| 929 | */for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| 930 | "lib/systemd/libsystemd-shared-*.so\0"for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| 931 | "lib64/systemd/libsystemd-shared-*.so\0"for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| 932 | "usr/lib/systemd/libsystemd-shared-*.so\0"for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
| 933 | "usr/lib64/systemd/libsystemd-shared-*.so\0")for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) { | |||
| 934 | ||||
| 935 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **names = NULL((void*)0); | |||
| 936 | _cleanup_free___attribute__((cleanup(freep))) char *path = NULL((void*)0); | |||
| 937 | char *c, **name; | |||
| 938 | ||||
| 939 | path = prefix_root(root, pattern); | |||
| 940 | if (!path) | |||
| 941 | return -ENOMEM12; | |||
| 942 | ||||
| 943 | r = glob_extend(&names, path); | |||
| 944 | if (r == -ENOENT2) | |||
| 945 | continue; | |||
| 946 | if (r < 0) | |||
| 947 | return r; | |||
| 948 | ||||
| 949 | assert_se(c = endswith(path, "*.so"))do { if ((__builtin_expect(!!(!(c = endswith(path, "*.so"))), 0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("c = endswith(path, \"*.so\")" ), "../src/basic/path-util.c", 949, __PRETTY_FUNCTION__); } while (0); | |||
| 950 | *c = '\0'; /* truncate the glob part */ | |||
| 951 | ||||
| 952 | STRV_FOREACH(name, names)for ((name) = (names); (name) && *(name); (name)++) { | |||
| 953 | /* This is most likely to run only once, hence let's not optimize anything. */ | |||
| 954 | char *t, *t2; | |||
| 955 | unsigned version; | |||
| 956 | ||||
| 957 | t = startswith(*name, path); | |||
| 958 | if (!t) | |||
| 959 | continue; | |||
| 960 | ||||
| 961 | t2 = endswith(t, ".so"); | |||
| 962 | if (!t2) | |||
| 963 | continue; | |||
| 964 | ||||
| 965 | t2[0] = '\0'; /* truncate the suffix */ | |||
| 966 | ||||
| 967 | r = safe_atou(t, &version); | |||
| 968 | if (r < 0) { | |||
| 969 | log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 969, __func__, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m" , *name) : -abs(_e); }); | |||
| 970 | continue; | |||
| 971 | } | |||
| 972 | ||||
| 973 | log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 975, __func__, "Found libsystemd shared at \"%s.so\", version %u (%s)." , *name, version, version >= minimal_version ? "OK" : "too old" ) : -abs(_e); }) | |||
| 974 | *name, version,({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 975, __func__, "Found libsystemd shared at \"%s.so\", version %u (%s)." , *name, version, version >= minimal_version ? "OK" : "too old" ) : -abs(_e); }) | |||
| 975 | version >= minimal_version ? "OK" : "too old")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 975, __func__, "Found libsystemd shared at \"%s.so\", version %u (%s)." , *name, version, version >= minimal_version ? "OK" : "too old" ) : -abs(_e); }); | |||
| 976 | if (version >= minimal_version) | |||
| 977 | return true1; | |||
| 978 | } | |||
| 979 | } | |||
| 980 | ||||
| 981 | return false0; | |||
| 982 | } | |||
| 983 | ||||
| 984 | bool_Bool dot_or_dot_dot(const char *path) { | |||
| 985 | if (!path) | |||
| 986 | return false0; | |||
| 987 | if (path[0] != '.') | |||
| 988 | return false0; | |||
| 989 | if (path[1] == 0) | |||
| 990 | return true1; | |||
| 991 | if (path[1] != '.') | |||
| 992 | return false0; | |||
| 993 | ||||
| 994 | return path[2] == 0; | |||
| 995 | } | |||
| 996 | ||||
| 997 | bool_Bool empty_or_root(const char *root) { | |||
| 998 | ||||
| 999 | /* For operations relative to some root directory, returns true if the specified root directory is redundant, | |||
| 1000 | * i.e. either / or NULL or the empty string or any equivalent. */ | |||
| 1001 | ||||
| 1002 | if (!root) | |||
| 1003 | return true1; | |||
| 1004 | ||||
| 1005 | return root[strspn(root, "/")] == 0; | |||
| 1006 | } | |||
| 1007 | ||||
| 1008 | int path_simplify_and_warn( | |||
| 1009 | char *path, | |||
| 1010 | unsigned flag, | |||
| 1011 | const char *unit, | |||
| 1012 | const char *filename, | |||
| 1013 | unsigned line, | |||
| 1014 | const char *lvalue) { | |||
| 1015 | ||||
| 1016 | bool_Bool absolute, fatal = flag & PATH_CHECK_FATAL; | |||
| 1017 | ||||
| 1018 | assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE))do { if ((__builtin_expect(!!(!(!(((flag) & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) == (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE )))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)" ), "../src/basic/path-util.c", 1018, __PRETTY_FUNCTION__); } while (0); | |||
| 1019 | ||||
| 1020 | if (!utf8_is_valid(path)) { | |||
| 1021 | log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path)({ int _level = (3); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_invalid_utf8_internal (unit, _level, filename, line, "../src/basic/path-util.c", 1021 , __func__, path) : -22; }); | |||
| 1022 | return -EINVAL22; | |||
| 1023 | } | |||
| 1024 | ||||
| 1025 | if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { | |||
| 1026 | absolute = path_is_absolute(path); | |||
| 1027 | ||||
| 1028 | if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) { | |||
| 1029 | log_syntax(unit, LOG_ERR, filename, line, 0,({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1031, __func__ , "%s= path is not absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
| 1030 | "%s= path is not absolute%s: %s",({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1031, __func__ , "%s= path is not absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
| 1031 | lvalue, fatal ? "" : ", ignoring", path)({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1031, __func__ , "%s= path is not absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }); | |||
| 1032 | return -EINVAL22; | |||
| 1033 | } | |||
| 1034 | ||||
| 1035 | if (absolute && (flag & PATH_CHECK_RELATIVE)) { | |||
| 1036 | log_syntax(unit, LOG_ERR, filename, line, 0,({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1038, __func__ , "%s= path is absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
| 1037 | "%s= path is absolute%s: %s",({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1038, __func__ , "%s= path is absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
| 1038 | lvalue, fatal ? "" : ", ignoring", path)({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1038, __func__ , "%s= path is absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }); | |||
| 1039 | return -EINVAL22; | |||
| 1040 | } | |||
| 1041 | } | |||
| 1042 | ||||
| 1043 | path_simplify(path, true1); | |||
| 1044 | ||||
| 1045 | if (!path_is_normalized(path)) { | |||
| 1046 | log_syntax(unit, LOG_ERR, filename, line, 0,({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1048, __func__ , "%s= path is not normalized%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
| 1047 | "%s= path is not normalized%s: %s",({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1048, __func__ , "%s= path is not normalized%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
| 1048 | lvalue, fatal ? "" : ", ignoring", path)({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1048, __func__ , "%s= path is not normalized%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }); | |||
| 1049 | return -EINVAL22; | |||
| 1050 | } | |||
| 1051 | ||||
| 1052 | return 0; | |||
| 1053 | } |