mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-12 09:17:44 +03:00
Merge pull request #10152 from yuwata/udev-use-extract
udev: small cleanups
This commit is contained in:
commit
083d27b654
@ -128,7 +128,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
|
||||
}
|
||||
|
||||
/* Split a string into words. */
|
||||
const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
|
||||
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) {
|
||||
const char *current;
|
||||
|
||||
current = *state;
|
||||
@ -144,20 +144,24 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (quoted && strchr("\'\"", *current)) {
|
||||
if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) {
|
||||
char quotechars[2] = {*current, '\0'};
|
||||
|
||||
*l = strcspn_escaped(current + 1, quotechars);
|
||||
if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
|
||||
(current[*l + 2] && !strchr(separator, current[*l + 2]))) {
|
||||
/* right quote missing or garbage at the end */
|
||||
if (flags & SPLIT_RELAX) {
|
||||
*state = current + *l + 1 + (current[*l + 1] != '\0');
|
||||
return current + 1;
|
||||
}
|
||||
*state = current;
|
||||
return NULL;
|
||||
}
|
||||
*state = current++ + *l + 2;
|
||||
} else if (quoted) {
|
||||
} else if (flags & SPLIT_QUOTES) {
|
||||
*l = strcspn_escaped(current, separator);
|
||||
if (current[*l] && !strchr(separator, current[*l])) {
|
||||
if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) {
|
||||
/* unfinished escape */
|
||||
*state = current;
|
||||
return NULL;
|
||||
|
@ -81,16 +81,21 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_;
|
||||
|
||||
char *first_word(const char *s, const char *word) _pure_;
|
||||
|
||||
const char* split(const char **state, size_t *l, const char *separator, bool quoted);
|
||||
typedef enum SplitFlags {
|
||||
SPLIT_QUOTES = 0x01 << 0,
|
||||
SPLIT_RELAX = 0x01 << 1,
|
||||
} SplitFlags;
|
||||
|
||||
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
|
||||
|
||||
#define FOREACH_WORD(word, length, s, state) \
|
||||
_FOREACH_WORD(word, length, s, WHITESPACE, false, state)
|
||||
_FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
|
||||
|
||||
#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
|
||||
_FOREACH_WORD(word, length, s, separator, false, state)
|
||||
_FOREACH_WORD(word, length, s, separator, 0, state)
|
||||
|
||||
#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
|
||||
for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
|
||||
#define _FOREACH_WORD(word, length, s, separator, flags, state) \
|
||||
for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
|
||||
|
||||
char *strappend(const char *s, const char *suffix);
|
||||
char *strnappend(const char *s, const char *suffix, size_t length);
|
||||
|
@ -245,7 +245,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **strv_split(const char *s, const char *separator) {
|
||||
char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
|
||||
const char *word, *state;
|
||||
size_t l;
|
||||
size_t n, i;
|
||||
@ -253,12 +253,15 @@ char **strv_split(const char *s, const char *separator) {
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!separator)
|
||||
separator = WHITESPACE;
|
||||
|
||||
s += strspn(s, separator);
|
||||
if (isempty(s))
|
||||
return new0(char*, 1);
|
||||
|
||||
n = 0;
|
||||
FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
|
||||
_FOREACH_WORD(word, l, s, separator, flags, state)
|
||||
n++;
|
||||
|
||||
r = new(char*, n+1);
|
||||
@ -266,7 +269,7 @@ char **strv_split(const char *s, const char *separator) {
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
|
||||
_FOREACH_WORD(word, l, s, separator, flags, state) {
|
||||
r[i] = strndup(word, l);
|
||||
if (!r[i]) {
|
||||
strv_free(r);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
char *strv_find(char **l, const char *name) _pure_;
|
||||
@ -66,7 +67,10 @@ static inline bool strv_isempty(char * const *l) {
|
||||
return !l || !*l;
|
||||
}
|
||||
|
||||
char **strv_split(const char *s, const char *separator);
|
||||
char **strv_split_full(const char *s, const char *separator, SplitFlags flags);
|
||||
static inline char **strv_split(const char *s, const char *separator) {
|
||||
return strv_split_full(s, separator, 0);
|
||||
}
|
||||
char **strv_split_newlines(const char *s);
|
||||
|
||||
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
|
||||
|
@ -632,6 +632,18 @@ tests += [
|
||||
libacl],
|
||||
'', 'manual', '-DLOG_REALM=LOG_REALM_UDEV'],
|
||||
|
||||
[['src/test/test-udev-build-argv.c'],
|
||||
[libudev_core,
|
||||
libudev_static,
|
||||
libsystemd_network,
|
||||
libshared],
|
||||
[threads,
|
||||
librt,
|
||||
libblkid,
|
||||
libkmod,
|
||||
libacl],
|
||||
'', '', '-DLOG_REALM=LOG_REALM_UDEV'],
|
||||
|
||||
[['src/test/test-id128.c'],
|
||||
[],
|
||||
[]],
|
||||
|
@ -63,6 +63,13 @@ static const char* const input_table_multiple[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char* const input_table_quoted[] = {
|
||||
"one",
|
||||
" two\t three ",
|
||||
" four five",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char* const input_table_one[] = {
|
||||
"one",
|
||||
NULL,
|
||||
@ -206,23 +213,64 @@ static void test_invalid_unquote(const char *quoted) {
|
||||
}
|
||||
|
||||
static void test_strv_split(void) {
|
||||
char **s;
|
||||
unsigned i = 0;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
const char str[] = "one,two,three";
|
||||
|
||||
l = strv_split(str, ",");
|
||||
assert_se(l);
|
||||
STRV_FOREACH(s, l)
|
||||
assert_se(streq(*s, input_table_multiple[i++]));
|
||||
assert_se(strv_equal(l, (char**) input_table_multiple));
|
||||
|
||||
i = 0;
|
||||
strv_free(l);
|
||||
|
||||
l = strv_split(" one two\t three", WHITESPACE);
|
||||
assert_se(l);
|
||||
STRV_FOREACH(s, l)
|
||||
assert_se(streq(*s, input_table_multiple[i++]));
|
||||
assert_se(strv_equal(l, (char**) input_table_multiple));
|
||||
|
||||
strv_free(l);
|
||||
|
||||
/* Setting NULL for separator is equivalent to WHITESPACE */
|
||||
l = strv_split(" one two\t three", NULL);
|
||||
assert_se(l);
|
||||
assert_se(strv_equal(l, (char**) input_table_multiple));
|
||||
|
||||
strv_free(l);
|
||||
|
||||
l = strv_split_full(" one two\t three", NULL, 0);
|
||||
assert_se(l);
|
||||
assert_se(strv_equal(l, (char**) input_table_multiple));
|
||||
|
||||
strv_free(l);
|
||||
|
||||
l = strv_split_full(" 'one' \" two\t three \" ' four five'", NULL, SPLIT_QUOTES);
|
||||
assert_se(l);
|
||||
assert_se(strv_equal(l, (char**) input_table_quoted));
|
||||
|
||||
strv_free(l);
|
||||
|
||||
/* missing last quote ignores the last element. */
|
||||
l = strv_split_full(" 'one' \" two\t three \" ' four five' ' ignored element ", NULL, SPLIT_QUOTES);
|
||||
assert_se(l);
|
||||
assert_se(strv_equal(l, (char**) input_table_quoted));
|
||||
|
||||
strv_free(l);
|
||||
|
||||
/* missing last quote, but the last element is _not_ ignored with SPLIT_RELAX. */
|
||||
l = strv_split_full(" 'one' \" two\t three \" ' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
|
||||
assert_se(l);
|
||||
assert_se(strv_equal(l, (char**) input_table_quoted));
|
||||
|
||||
strv_free(l);
|
||||
|
||||
/* missing separator between */
|
||||
l = strv_split_full(" 'one' \" two\t three \"' four five'", NULL, SPLIT_QUOTES | SPLIT_RELAX);
|
||||
assert_se(l);
|
||||
assert_se(strv_equal(l, (char**) input_table_quoted));
|
||||
|
||||
strv_free(l);
|
||||
|
||||
l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
|
||||
assert_se(l);
|
||||
assert_se(strv_equal(l, (char**) input_table_quoted));
|
||||
}
|
||||
|
||||
static void test_strv_split_empty(void) {
|
||||
@ -232,11 +280,60 @@ static void test_strv_split_empty(void) {
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split("", NULL);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full("", NULL, 0);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full("", NULL, SPLIT_QUOTES);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full("", WHITESPACE, SPLIT_QUOTES);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full("", WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split(" ", WHITESPACE);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split(" ", NULL);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full(" ", NULL, 0);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full(" ", WHITESPACE, SPLIT_QUOTES);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full(" ", NULL, SPLIT_QUOTES);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
|
||||
strv_free(l);
|
||||
l = strv_split_full(" ", NULL, SPLIT_QUOTES | SPLIT_RELAX);
|
||||
assert_se(l);
|
||||
assert_se(strv_isempty(l));
|
||||
}
|
||||
|
||||
static void test_strv_split_extract(void) {
|
||||
|
78
src/test/test-udev-build-argv.c
Normal file
78
src/test/test-udev-build-argv.c
Normal file
@ -0,0 +1,78 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
#include "udev.h"
|
||||
|
||||
static void test_udev_build_argv_one(const char *c) {
|
||||
_cleanup_strv_free_ char **a = NULL;
|
||||
_cleanup_free_ char *arg = NULL;
|
||||
char *argv[128], **p;
|
||||
int argc;
|
||||
size_t i;
|
||||
|
||||
assert_se(a = strv_split_full(c, NULL, SPLIT_QUOTES | SPLIT_RELAX));
|
||||
|
||||
assert_se(arg = strdup(c));
|
||||
assert_se(udev_build_argv(arg, &argc, argv) >= 0);
|
||||
|
||||
log_info("command: %s", c);
|
||||
|
||||
i = 0;
|
||||
log_info("strv_split:");
|
||||
STRV_FOREACH(p, a)
|
||||
log_info("argv[%zu] = '%s'", i++, *p);
|
||||
|
||||
i = 0;
|
||||
log_info("udev_build_argv:");
|
||||
STRV_FOREACH(p, argv)
|
||||
log_info("argv[%zu] = '%s'", i++, *p);
|
||||
|
||||
assert_se(strv_equal(argv, a));
|
||||
assert_se(argc == (int) strv_length(a));
|
||||
|
||||
}
|
||||
|
||||
static void test_udev_build_argv(void) {
|
||||
test_udev_build_argv_one("one two three");
|
||||
test_udev_build_argv_one("one 'two three ' \" four five \" 'aaa bbb ");
|
||||
test_udev_build_argv_one("/bin/echo -e \\101");
|
||||
test_udev_build_argv_one("/bin/echo -n special-device");
|
||||
test_udev_build_argv_one("/bin/echo -n special-device");
|
||||
test_udev_build_argv_one("/bin/echo test");
|
||||
test_udev_build_argv_one("/bin/echo -n test-%b");
|
||||
test_udev_build_argv_one("/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9");
|
||||
test_udev_build_argv_one("/bin/sh -c 'echo foo3 foo4 foo5 foo6 foo7 foo8 foo9 | sed s/foo9/bar9/'");
|
||||
test_udev_build_argv_one("/bin/echo -n 'foo3 foo4' 'foo5 foo6 foo7 foo8'");
|
||||
test_udev_build_argv_one("/bin/sh -c 'printf %%s \\\"foo1 foo2\\\" | grep \\\"foo1 foo2\\\"'");
|
||||
test_udev_build_argv_one("/bin/sh -c \\\"printf %%s 'foo1 foo2' | grep 'foo1 foo2'\\\"");
|
||||
test_udev_build_argv_one("/bin/sh -c 'printf \\\"%%s %%s\\\" \\\"foo1 foo2\\\" \\\"foo3\\\"| grep \\\"foo1 foo2\\\"'");
|
||||
test_udev_build_argv_one("/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9");
|
||||
test_udev_build_argv_one("/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9");
|
||||
test_udev_build_argv_one("/bin/echo -n foo");
|
||||
test_udev_build_argv_one("/bin/echo -n usb-%b");
|
||||
test_udev_build_argv_one("/bin/echo -n scsi-%b");
|
||||
test_udev_build_argv_one("/bin/echo -n foo-%b");
|
||||
test_udev_build_argv_one("/bin/echo test");
|
||||
test_udev_build_argv_one("/bin/echo symlink test this");
|
||||
test_udev_build_argv_one("/bin/echo symlink test this");
|
||||
test_udev_build_argv_one("/bin/echo link test this");
|
||||
test_udev_build_argv_one("/bin/echo -n node link1 link2");
|
||||
test_udev_build_argv_one("/bin/echo -n node link1 link2 link3 link4");
|
||||
test_udev_build_argv_one("/usr/bin/test -b %N");
|
||||
test_udev_build_argv_one("/bin/echo -e name; (/usr/bin/badprogram)");
|
||||
test_udev_build_argv_one("/bin/echo -e \\xc3\\xbcber");
|
||||
test_udev_build_argv_one("/bin/echo -e \\xef\\xe8garbage");
|
||||
test_udev_build_argv_one("/bin/echo 1 1 0400");
|
||||
test_udev_build_argv_one("/bin/echo 0 0 0400letsdoabuffferoverflow0123456789012345789012345678901234567890");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
test_udev_build_argv();
|
||||
|
||||
return 0;
|
||||
}
|
@ -102,18 +102,18 @@ enum udev_builtin_cmd udev_builtin_lookup(const char *command) {
|
||||
}
|
||||
|
||||
int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) {
|
||||
char arg[UTIL_PATH_SIZE];
|
||||
int argc;
|
||||
char *argv[128];
|
||||
_cleanup_strv_free_ char **argv = NULL;
|
||||
|
||||
if (!builtins[cmd])
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
argv = strv_split_full(command, NULL, SPLIT_QUOTES | SPLIT_RELAX);
|
||||
if (!argv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* we need '0' here to reset the internal state */
|
||||
optind = 0;
|
||||
strscpy(arg, sizeof(arg), command);
|
||||
udev_build_argv(arg, &argc, argv);
|
||||
return builtins[cmd]->cmd(dev, argc, argv, test);
|
||||
return builtins[cmd]->cmd(dev, strv_length(argv), argv, test);
|
||||
}
|
||||
|
||||
int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "netlink-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "string-util.h"
|
||||
@ -737,47 +738,44 @@ int udev_event_spawn(struct udev_event *event,
|
||||
bool accept_failure,
|
||||
const char *cmd,
|
||||
char *result, size_t ressize) {
|
||||
int outpipe[2] = {-1, -1};
|
||||
int errpipe[2] = {-1, -1};
|
||||
_cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1};
|
||||
_cleanup_strv_free_ char **argv = NULL;
|
||||
pid_t pid;
|
||||
int err = 0;
|
||||
int r;
|
||||
|
||||
/* pipes from child to parent */
|
||||
if (result != NULL || log_get_max_level() >= LOG_INFO) {
|
||||
if (pipe2(outpipe, O_NONBLOCK) != 0) {
|
||||
err = log_error_errno(errno, "pipe failed: %m");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (log_get_max_level() >= LOG_INFO) {
|
||||
if (pipe2(errpipe, O_NONBLOCK) != 0) {
|
||||
err = log_error_errno(errno, "pipe failed: %m");
|
||||
goto out;
|
||||
}
|
||||
if (!result || log_get_max_level() >= LOG_INFO)
|
||||
if (pipe2(outpipe, O_NONBLOCK) != 0)
|
||||
return log_error_errno(errno, "Failed to create pipe for command '%s': %m", cmd);
|
||||
|
||||
if (log_get_max_level() >= LOG_INFO)
|
||||
if (pipe2(errpipe, O_NONBLOCK) != 0)
|
||||
return log_error_errno(errno, "Failed to create pipe for command '%s': %m", cmd);
|
||||
|
||||
argv = strv_split_full(cmd, NULL, SPLIT_QUOTES|SPLIT_RELAX);
|
||||
if (!argv)
|
||||
return log_oom();
|
||||
|
||||
/* allow programs in /usr/lib/udev/ to be called without the path */
|
||||
if (!path_is_absolute(argv[0])) {
|
||||
char *program;
|
||||
|
||||
program = path_join(NULL, UDEVLIBEXECDIR, argv[0]);
|
||||
if (!program)
|
||||
return log_oom();
|
||||
|
||||
free_and_replace(argv[0], program);
|
||||
}
|
||||
|
||||
err = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
if (err == 0) {
|
||||
char arg[UTIL_PATH_SIZE];
|
||||
char *argv[128];
|
||||
char program[UTIL_PATH_SIZE];
|
||||
|
||||
r = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to fork() to execute command '%s': %m", cmd);
|
||||
if (r == 0) {
|
||||
/* child closes parent's ends of pipes */
|
||||
outpipe[READ_END] = safe_close(outpipe[READ_END]);
|
||||
errpipe[READ_END] = safe_close(errpipe[READ_END]);
|
||||
|
||||
strscpy(arg, sizeof(arg), cmd);
|
||||
udev_build_argv(arg, NULL, argv);
|
||||
|
||||
/* allow programs in /usr/lib/udev/ to be called without the path */
|
||||
if (argv[0][0] != '/') {
|
||||
strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL);
|
||||
argv[0] = program;
|
||||
}
|
||||
|
||||
log_debug("starting '%s'", cmd);
|
||||
log_debug("Starting '%s'", cmd);
|
||||
|
||||
spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev),
|
||||
outpipe[WRITE_END], errpipe[WRITE_END]);
|
||||
@ -795,18 +793,11 @@ int udev_event_spawn(struct udev_event *event,
|
||||
outpipe[READ_END], errpipe[READ_END],
|
||||
result, ressize);
|
||||
|
||||
err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure);
|
||||
r = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to wait spawned command '%s': %m", cmd);
|
||||
|
||||
out:
|
||||
if (outpipe[READ_END] >= 0)
|
||||
close(outpipe[READ_END]);
|
||||
if (outpipe[WRITE_END] >= 0)
|
||||
close(outpipe[WRITE_END]);
|
||||
if (errpipe[READ_END] >= 0)
|
||||
close(errpipe[READ_END]);
|
||||
if (errpipe[WRITE_END] >= 0)
|
||||
close(errpipe[WRITE_END]);
|
||||
return err;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int rename_netif(struct udev_event *event) {
|
||||
|
@ -20,34 +20,19 @@
|
||||
#include "udev.h"
|
||||
|
||||
static int node_symlink(struct udev_device *dev, const char *node, const char *slink) {
|
||||
struct stat stats;
|
||||
char target[UTIL_PATH_SIZE];
|
||||
char *s;
|
||||
size_t l;
|
||||
_cleanup_free_ char *slink_dirname = NULL, *target = NULL;
|
||||
char slink_tmp[UTIL_PATH_SIZE + 32];
|
||||
int i = 0;
|
||||
int tail = 0;
|
||||
int err = 0;
|
||||
struct stat stats;
|
||||
int r, err = 0;
|
||||
|
||||
slink_dirname = dirname_malloc(slink);
|
||||
if (!slink_dirname)
|
||||
return log_oom();
|
||||
|
||||
/* use relative link */
|
||||
target[0] = '\0';
|
||||
while (node[i] && (node[i] == slink[i])) {
|
||||
if (node[i] == '/')
|
||||
tail = i+1;
|
||||
i++;
|
||||
}
|
||||
s = target;
|
||||
l = sizeof(target);
|
||||
while (slink[i] != '\0') {
|
||||
if (slink[i] == '/')
|
||||
l = strpcpy(&s, l, "../");
|
||||
i++;
|
||||
}
|
||||
l = strscpy(s, l, &node[tail]);
|
||||
if (l == 0) {
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
r = path_make_relative(slink_dirname, node, &target);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
|
||||
|
||||
/* preserve link with correct target, do not replace node of other device */
|
||||
if (lstat(slink, &stats) == 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user