From 1d13f648d0fade38194db74b4f82ca68c8a26856 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 May 2015 17:42:10 +0200 Subject: util: add generic calls for prefixing a root directory to a path So far a number of utilities implemented their own calls for this, unify them in prefix_root() and prefix_roota(). The former uses heap memory, the latter allocates from the stack via alloca(). Port over most users of a --root= logic. --- src/firstboot/firstboot.c | 18 ++++++++---------- src/shared/conf-files.c | 6 +++--- src/shared/path-util.c | 34 ++++++++++++++++++++++++++++++++++ src/shared/path-util.h | 27 +++++++++++++++++++++++++++ src/sysusers/sysusers.c | 16 +++++++--------- src/test/test-path-util.c | 29 +++++++++++++++++++++++++++++ src/tmpfiles/tmpfiles.c | 2 +- 7 files changed, 109 insertions(+), 23 deletions(-) diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 37326df008..d156d57caf 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -52,8 +52,6 @@ static bool arg_copy_locale = false; static bool arg_copy_timezone = false; static bool arg_copy_root_password = false; -#define prefix_roota(p) (arg_root ? (const char*) strjoina(arg_root, p) : (const char*) p) - static void clear_string(char *x) { if (!x) @@ -87,13 +85,13 @@ static void print_welcome(void) { if (done) return; - os_release = prefix_roota("/etc/os-release"); + os_release = prefix_roota(arg_root, "/etc/os-release"); r = parse_env_file(os_release, NEWLINE, "PRETTY_NAME", &pretty_name, NULL); if (r == -ENOENT) { - os_release = prefix_roota("/usr/lib/os-release"); + os_release = prefix_roota(arg_root, "/usr/lib/os-release"); r = parse_env_file(os_release, NEWLINE, "PRETTY_NAME", &pretty_name, NULL); @@ -251,7 +249,7 @@ static int process_locale(void) { unsigned i = 0; int r; - etc_localeconf = prefix_roota("/etc/locale.conf"); + etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; @@ -325,7 +323,7 @@ static int process_timezone(void) { const char *etc_localtime, *e; int r; - etc_localtime = prefix_roota("/etc/localtime"); + etc_localtime = prefix_roota(arg_root, "/etc/localtime"); if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; @@ -404,7 +402,7 @@ static int process_hostname(void) { const char *etc_hostname; int r; - etc_hostname = prefix_roota("/etc/hostname"); + etc_hostname = prefix_roota(arg_root, "/etc/hostname"); if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; @@ -429,7 +427,7 @@ static int process_machine_id(void) { char id[SD_ID128_STRING_MAX]; int r; - etc_machine_id = prefix_roota("/etc/machine-id"); + etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; @@ -455,7 +453,7 @@ static int prompt_root_password(void) { if (!arg_prompt_root_password) return 0; - etc_shadow = prefix_roota("/etc/shadow"); + etc_shadow = prefix_roota(arg_root, "/etc/shadow"); if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; @@ -544,7 +542,7 @@ static int process_root_password(void) { const char *etc_shadow; int r; - etc_shadow = prefix_roota("/etc/shadow"); + etc_shadow = prefix_roota(arg_root, "/etc/shadow"); if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c index 9ab08355e3..da8745b284 100644 --- a/src/shared/conf-files.c +++ b/src/shared/conf-files.c @@ -36,12 +36,13 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { _cleanup_closedir_ DIR *dir = NULL; - char *dirpath; + const char *dirpath; + int r; assert(path); assert(suffix); - dirpath = strjoina(root ? root : "", path); + dirpath = prefix_roota(root, path); dir = opendir(dirpath); if (!dir) { @@ -53,7 +54,6 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char for (;;) { struct dirent *de; char *p; - int r; errno = 0; de = readdir(dir); diff --git a/src/shared/path-util.c b/src/shared/path-util.c index 635ce33b23..7090989fcb 100644 --- a/src/shared/path-util.c +++ b/src/shared/path-util.c @@ -793,3 +793,37 @@ int fsck_exists(const char *fstype) { return 0; } + +char *prefix_root(const char *root, const char *path) { + char *n, *p; + size_t l; + + /* If root is passed, prefixes path with it. Otherwise returns + * it as is. */ + + assert(path); + + /* First, drop duplicate prefixing slashes from the path */ + while (path[0] == '/' && path[1] == '/') + path++; + + if (isempty(root) || path_equal(root, "/")) + return strdup(path); + + l = strlen(root) + 1 + strlen(path) + 1; + + n = new(char, l); + if (!n) + return NULL; + + p = stpcpy(n, root); + + while (p > n && p[-1] == '/') + p--; + + if (path[0] != '/') + *(p++) = '/'; + + strcpy(p, path); + return n; +} diff --git a/src/shared/path-util.h b/src/shared/path-util.h index 5548ce4a94..4f45cfd2b7 100644 --- a/src/shared/path-util.h +++ b/src/shared/path-util.h @@ -73,3 +73,30 @@ int fsck_exists(const char *fstype); /* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ #define PATH_FOREACH_PREFIX_MORE(prefix, path) \ for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) + +char *prefix_root(const char *root, const char *path); + +/* Similar to prefix_root(), but returns an alloca() buffer, or + * possibly a const pointer into the path parameter */ +#define prefix_roota(root, path) \ + ({ \ + const char* _path = (path), *_root = (root), *_ret; \ + char *_p, *_n; \ + size_t _l; \ + while (_path[0] == '/' && _path[1] == '/') \ + _path ++; \ + if (isempty(_root) || path_equal(_root, "/")) \ + _ret = _path; \ + else { \ + _l = strlen(_root) + 1 + strlen(_path) + 1; \ + _n = alloca(_l); \ + _p = stpcpy(_n, _root); \ + while (_p > _n && _p[-1] == '/') \ + _p--; \ + if (_path[0] != '/') \ + *(_p++) = '/'; \ + strcpy(_p, _path); \ + _ret = _n; \ + } \ + _ret; \ + }) diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index cc4c7ef53f..d7ba482834 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -80,15 +80,13 @@ static uid_t search_uid = UID_INVALID; static UidRange *uid_range = NULL; static unsigned n_uid_range = 0; -#define fix_root(x) (arg_root ? strjoina(arg_root, x) : x) - static int load_user_database(void) { _cleanup_fclose_ FILE *f = NULL; const char *passwd_path; struct passwd *pw; int r; - passwd_path = fix_root("/etc/passwd"); + passwd_path = prefix_roota(arg_root, "/etc/passwd"); f = fopen(passwd_path, "re"); if (!f) return errno == ENOENT ? 0 : -errno; @@ -140,7 +138,7 @@ static int load_group_database(void) { struct group *gr; int r; - group_path = fix_root("/etc/group"); + group_path = prefix_roota(arg_root, "/etc/group"); f = fopen(group_path, "re"); if (!f) return errno == ENOENT ? 0 : -errno; @@ -369,7 +367,7 @@ static int write_files(void) { _cleanup_fclose_ FILE *original = NULL; /* First we update the actual group list file */ - group_path = fix_root("/etc/group"); + group_path = prefix_roota(arg_root, "/etc/group"); r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); if (r < 0) goto finish; @@ -448,7 +446,7 @@ static int write_files(void) { } /* OK, now also update the shadow file for the group list */ - gshadow_path = fix_root("/etc/gshadow"); + gshadow_path = prefix_roota(arg_root, "/etc/gshadow"); r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); if (r < 0) goto finish; @@ -514,7 +512,7 @@ static int write_files(void) { long lstchg; /* First we update the user database itself */ - passwd_path = fix_root("/etc/passwd"); + passwd_path = prefix_roota(arg_root, "/etc/passwd"); r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); if (r < 0) goto finish; @@ -599,7 +597,7 @@ static int write_files(void) { } /* The we update the shadow database */ - shadow_path = fix_root("/etc/shadow"); + shadow_path = prefix_roota(arg_root, "/etc/shadow"); r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); if (r < 0) goto finish; @@ -802,7 +800,7 @@ static int uid_is_ok(uid_t uid, const char *name) { static int root_stat(const char *p, struct stat *st) { const char *fix; - fix = fix_root(p); + fix = prefix_roota(arg_root, p); if (stat(fix, st) < 0) return -errno; diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index e5b9c28bc0..09f0f2f89e 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -294,6 +294,34 @@ static void test_path_startswith(void) { assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/")); } +static void test_prefix_root_one(const char *r, const char *p, const char *expected) { + _cleanup_free_ char *s = NULL; + const char *t; + + assert_se(s = prefix_root(r, p)); + assert_se(streq_ptr(s, expected)); + + t = prefix_roota(r, p); + assert_se(t); + assert_se(streq_ptr(t, expected)); +} + +static void test_prefix_root(void) { + test_prefix_root_one("/", "/foo", "/foo"); + test_prefix_root_one(NULL, "/foo", "/foo"); + test_prefix_root_one("", "/foo", "/foo"); + test_prefix_root_one("///", "/foo", "/foo"); + test_prefix_root_one("/", "////foo", "/foo"); + test_prefix_root_one(NULL, "////foo", "/foo"); + + test_prefix_root_one("/foo", "/bar", "/foo/bar"); + test_prefix_root_one("/foo", "bar", "/foo/bar"); + test_prefix_root_one("foo", "bar", "foo/bar"); + test_prefix_root_one("/foo/", "/bar", "/foo/bar"); + test_prefix_root_one("/foo/", "//bar", "/foo/bar"); + test_prefix_root_one("/foo///", "//bar", "/foo/bar"); +} + int main(int argc, char **argv) { test_path(); test_find_binary(argv[0], true); @@ -304,6 +332,7 @@ int main(int argc, char **argv) { test_make_relative(); test_strv_resolve(); test_path_startswith(); + test_prefix_root(); return 0; } diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 640ad4788d..5a578350a3 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1926,7 +1926,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { if (arg_root) { char *p; - p = strappend(arg_root, i.path); + p = prefix_root(arg_root, i.path); if (!p) return log_oom(); -- cgit v1.2.3-54-g00ecf