From 774f79b570ce1976a2c98d7e097df637e484f2e7 Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Fri, 2 Mar 2018 16:13:07 +0100 Subject: [PATCH 1/5] tmpfiles: make hardlink_vulnerable() argument constant --- src/tmpfiles/tmpfiles.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 98368c36f1..42d85a3928 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -777,7 +777,7 @@ static bool dangerous_hardlinks(void) { return cached; } -static bool hardlink_vulnerable(struct stat *st) { +static bool hardlink_vulnerable(const struct stat *st) { assert(st); return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks(); From 56114d45d01fe3e4938e31549ad7c0bd19b9cf42 Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Fri, 2 Mar 2018 16:25:56 +0100 Subject: [PATCH 2/5] tmpfiles: fstat() works with fd opened with O_PATH since Linux 3.6 --- src/tmpfiles/tmpfiles.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 42d85a3928..dba3152671 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -814,7 +814,7 @@ static int path_set_perms(Item *i, const char *path) { return r; } - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) + if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to fstat() file %s: %m", path); if (hardlink_vulnerable(&st)) { @@ -1003,7 +1003,7 @@ static int path_set_acls(Item *item, const char *path) { if (fd < 0) return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) + if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to fstat() file %s: %m", path); if (hardlink_vulnerable(&st)) { From 936f6bdb803c432578e2cdcc5f93f3bfff93aff0 Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Fri, 2 Mar 2018 17:19:32 +0100 Subject: [PATCH 3/5] tmpfiles: don't resolve pathnames when traversing recursively through directory trees Otherwise we can be fooled if one path component is replaced underneath us. The patch achieves that by always operating at file descriptor level (by using *at() helpers) and by making sure we do not any path resolution when traversing direcotry trees. However this is not always possible, for instance when listing the content of a directory or some operations don't provide the *at() helpers or others (such as fchmodat()) don't have the AT_EMPTY_PATH flag. In such cases we operate on /proc/self/fd/%i pseudo-symlink instead, which works the same for all kinds of objects and requires no checking of type beforehand. Also O_PATH flag is used when opening file objects in order to prevent undesired behaviors: device nodes from reacting, automounts from triggering, etc... Fixes: #7986 Fixes: CVE-2018-6954 --- src/tmpfiles/tmpfiles.c | 411 +++++++++++++++++++++++++--------------- 1 file changed, 263 insertions(+), 148 deletions(-) diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index dba3152671..f1890f3261 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -783,22 +783,84 @@ static bool hardlink_vulnerable(const struct stat *st) { return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks(); } +static int fd_set_perms(Item *i, int fd, const struct stat *st) { + _cleanup_free_ char *path = NULL; + int r; + + assert(i); + assert(fd); + + r = fd_get_path(fd, &path); + if (r < 0) + return r; + + if (!i->mode_set && !i->uid_set && !i->gid_set) + goto shortcut; + + if (hardlink_vulnerable(st)) { + log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); + return -EPERM; + } + + if (i->mode_set) { + if (S_ISLNK(st->st_mode)) + log_debug("Skipping mode fix for symlink %s.", path); + else { + mode_t m = i->mode; + + if (i->mask_perms) { + if (!(st->st_mode & 0111)) + m &= ~0111; + if (!(st->st_mode & 0222)) + m &= ~0222; + if (!(st->st_mode & 0444)) + m &= ~0444; + if (!S_ISDIR(st->st_mode)) + m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ + } + + if (m == (st->st_mode & 07777)) + log_debug("\"%s\" has correct mode %o already.", path, st->st_mode); + else { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + log_debug("Changing \"%s\" to mode %o.", path, m); + + /* fchmodat() still doesn't have AT_EMPTY_PATH flag. */ + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + + if (chmod(procfs_path, m) < 0) + return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, procfs_path); + } + } + } + + if ((i->uid_set && i->uid != st->st_uid) || + (i->gid_set && i->gid != st->st_gid)) { + log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT, + path, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID); + + if (fchownat(fd, + "", + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID, + AT_EMPTY_PATH) < 0) + return log_error_errno(errno, "fchownat() of %s failed: %m", path); + } + +shortcut: + return label_fix(path, false, false); +} + static int path_set_perms(Item *i, const char *path) { - char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int fd = -1; struct stat st; assert(i); assert(path); - if (!i->mode_set && !i->uid_set && !i->gid_set) - goto shortcut; - - /* We open the file with O_PATH here, to make the operation - * somewhat atomic. Also there's unfortunately no fchmodat() - * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via - * O_PATH. */ - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); if (fd < 0) { int level = LOG_ERR, r = -errno; @@ -817,58 +879,7 @@ static int path_set_perms(Item *i, const char *path) { if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to fstat() file %s: %m", path); - if (hardlink_vulnerable(&st)) { - log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); - return -EPERM; - } - - xsprintf(fn, "/proc/self/fd/%i", fd); - - if (i->mode_set) { - if (S_ISLNK(st.st_mode)) - log_debug("Skipping mode fix for symlink %s.", path); - else { - mode_t m = i->mode; - - if (i->mask_perms) { - if (!(st.st_mode & 0111)) - m &= ~0111; - if (!(st.st_mode & 0222)) - m &= ~0222; - if (!(st.st_mode & 0444)) - m &= ~0444; - if (!S_ISDIR(st.st_mode)) - m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ - } - - if (m == (st.st_mode & 07777)) - log_debug("\"%s\" has correct mode %o already.", path, st.st_mode); - else { - log_debug("Changing \"%s\" to mode %o.", path, m); - - if (chmod(fn, m) < 0) - return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, fn); - } - } - } - - if ((i->uid_set && i->uid != st.st_uid) || - (i->gid_set && i->gid != st.st_gid)) { - log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT, - path, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID); - - if (chown(fn, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID) < 0) - return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn); - } - - fd = safe_close(fd); - -shortcut: - return label_fix(path, false, false); + return fd_set_perms(i, fd, &st); } static int parse_xattrs_from_arg(Item *i) { @@ -909,21 +920,43 @@ static int parse_xattrs_from_arg(Item *i) { return 0; } -static int path_set_xattrs(Item *i, const char *path) { +static int fd_set_xattrs(Item *i, int fd, const struct stat *st) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_free_ char *path = NULL; char **name, **value; + int r; assert(i); - assert(path); + assert(fd); + + r = fd_get_path(fd, &path); + if (r < 0) + return r; + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); STRV_FOREACH_PAIR(name, value, i->xattrs) { log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); - if (lsetxattr(path, *name, *value, strlen(*value), 0) < 0) + if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0) return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); } return 0; } +static int path_set_xattrs(Item *i, const char *path) { + _cleanup_close_ int fd = -1; + + assert(i); + assert(path); + + fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd < 0) + return log_error_errno(errno, "Cannot open '%s': %m", path); + + return fd_set_xattrs(i, fd, NULL); +} + static int parse_acls_from_arg(Item *item) { #if HAVE_ACL int r; @@ -989,10 +1022,53 @@ static int path_set_acl(const char *path, const char *pretty, acl_type_t type, a } #endif -static int path_set_acls(Item *item, const char *path) { +static int fd_set_acls(Item *item, int fd, const struct stat *st) { int r = 0; #if HAVE_ACL - char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_free_ char *path = NULL; + + assert(item); + assert(fd); + assert(st); + + r = fd_get_path(fd, &path); + if (r < 0) + return r; + + if (hardlink_vulnerable(st)) { + log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); + return -EPERM; + } + + if (S_ISLNK(st->st_mode)) { + log_debug("Skipping ACL fix for symlink %s.", path); + return 0; + } + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + + if (item->acl_access) + r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->force); + + if (r == 0 && item->acl_default) + r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); + + if (r > 0) + return -r; /* already warned */ + if (r == -EOPNOTSUPP) { + log_debug_errno(r, "ACLs not supported by file system at %s", path); + return 0; + } + if (r < 0) + return log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); +#endif + return r; +} + +static int path_set_acls(Item *item, const char *path) { + int r = 0; +#ifdef HAVE_ACL _cleanup_close_ int fd = -1; struct stat st; @@ -1006,34 +1082,10 @@ static int path_set_acls(Item *item, const char *path) { if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to fstat() file %s: %m", path); - if (hardlink_vulnerable(&st)) { - log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); - return -EPERM; - } - - if (S_ISLNK(st.st_mode)) { - log_debug("Skipping ACL fix for symlink %s.", path); - return 0; - } - - xsprintf(fn, "/proc/self/fd/%i", fd); - - if (item->acl_access) - r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force); - - if (r == 0 && item->acl_default) - r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); - - if (r > 0) - return -r; /* already warned */ - else if (r == -EOPNOTSUPP) { - log_debug_errno(r, "ACLs not supported by file system at %s", path); - return 0; - } else if (r < 0) - log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); -#endif - return r; -} + r = fd_set_acls(item, fd, &st); + #endif + return r; + } #define ATTRIBUTES_ALL \ (FS_NOATIME_FL | \ @@ -1134,30 +1186,24 @@ static int parse_attribute_from_arg(Item *item) { return 0; } -static int path_set_attribute(Item *item, const char *path) { - _cleanup_close_ int fd = -1; - struct stat st; +static int fd_set_attribute(Item *item, int fd, const struct stat *st) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int procfs_fd = -1; + _cleanup_free_ char *path = NULL; unsigned f; int r; if (!item->attribute_set || item->attribute_mask == 0) return 0; - fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW); - if (fd < 0) { - if (errno == ELOOP) - return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path); - - return log_error_errno(errno, "Cannot open '%s': %m", path); - } - - if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Cannot stat '%s': %m", path); + r = fd_get_path(fd, &path); + if (r < 0) + return r; /* Issuing the file attribute ioctls on device nodes is not * safe, as that will be delivered to the drivers, not the * file system containing the device node. */ - if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { + if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) { log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path); return -EINVAL; } @@ -1165,10 +1211,16 @@ static int path_set_attribute(Item *item, const char *path) { f = item->attribute_value & item->attribute_mask; /* Mask away directory-specific flags */ - if (!S_ISDIR(st.st_mode)) + if (!S_ISDIR(st->st_mode)) f &= ~FS_DIRSYNC_FL; - r = chattr_fd(fd, f, item->attribute_mask); + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + + procfs_fd = open(procfs_path, O_RDONLY|O_CLOEXEC|O_NOATIME); + if (procfs_fd < 0) + return -errno; + + r = chattr_fd(procfs_fd, f, item->attribute_mask); if (r < 0) log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING, r, @@ -1178,6 +1230,23 @@ static int path_set_attribute(Item *item, const char *path) { return 0; } +static int path_set_attribute(Item *item, const char *path) { + _cleanup_close_ int fd = -1; + struct stat st; + + if (!item->attribute_set || item->attribute_mask == 0) + return 0; + + fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd < 0) + return log_error_errno(errno, "Cannot open '%s': %m", path); + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Cannot stat '%s': %m", path); + + return fd_set_attribute(item, fd, &st); +} + static int write_one_file(Item *i, const char *path) { _cleanup_close_ int fd = -1; int flags, r = 0; @@ -1242,48 +1311,58 @@ done: } typedef int (*action_t)(Item *, const char *); +typedef int (*fdaction_t)(Item *, int fd, const struct stat *st); -static int item_do_children(Item *i, const char *path, action_t action) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - int r = 0; +static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) { + int r = 0, q; assert(i); - assert(path); + assert(fd >= 0); + assert(st); /* This returns the first error we run into, but nevertheless * tries to go on */ + r = action(i, fd, st); - d = opendir_nomod(path); - if (!d) - return IN_SET(errno, ENOENT, ENOTDIR, ELOOP) ? 0 : -errno; + if (S_ISDIR(st->st_mode)) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; - FOREACH_DIRENT_ALL(de, d, r = -errno) { - _cleanup_free_ char *p = NULL; - int q; + /* The passed 'fd' was opened with O_PATH. We need to convert + * it into a 'regular' fd before reading the directory content. */ + xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (dot_or_dot_dot(de->d_name)) - continue; + d = opendir(procfs_path); + if (!d) { + r = r ?: -errno; + goto finish; + } - p = strjoin(path, "/", de->d_name); - if (!p) - return -ENOMEM; + FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) { + struct stat de_st; + int de_fd; - q = action(i, p); - if (q < 0 && q != -ENOENT && r == 0) - r = q; + if (dot_or_dot_dot(de->d_name)) + continue; + + de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0) + /* pass ownership of dirent fd over */ + q = item_do(i, de_fd, &de_st, action); + else + q = -errno; - if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { - q = item_do_children(i, p, action); if (q < 0 && r == 0) r = q; } } - +finish: + safe_close(fd); return r; } -static int glob_item(Item *i, action_t action, bool recursive) { +static int glob_item(Item *i, action_t action) { _cleanup_globfree_ glob_t g = { .gl_opendir = (void *(*)(const char *)) opendir_nomod, }; @@ -1298,12 +1377,48 @@ static int glob_item(Item *i, action_t action, bool recursive) { k = action(i, *fn); if (k < 0 && r == 0) r = k; + } - if (recursive) { - k = item_do_children(i, *fn, action); - if (k < 0 && r == 0) - r = k; + return r; +} + +static int glob_item_recursively(Item *i, fdaction_t action) { + _cleanup_globfree_ glob_t g = { + .gl_opendir = (void *(*)(const char *)) opendir_nomod, + }; + int r = 0, k; + char **fn; + + k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g); + if (k < 0 && k != -ENOENT) + return log_error_errno(k, "glob(%s) failed: %m", i->path); + + STRV_FOREACH(fn, g.gl_pathv) { + _cleanup_close_ int fd = -1; + struct stat st; + + /* Make sure we won't trigger/follow file object (such as + * device nodes, automounts, ...) pointed out by 'fn' with + * O_PATH. Note, when O_PATH is used, flags other than + * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */ + + fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd < 0) { + r = r ?: -errno; + continue; } + + if (fstat(fd, &st) < 0) { + r = r ?: -errno; + continue; + } + + k = item_do(i, fd, &st, action); + if (k < 0 && r == 0) + r = k; + + /* we passed fd ownership to the previous call */ + fd = -1; } return r; @@ -1392,7 +1507,7 @@ static int create_item(Item *i) { break; case WRITE_FILE: - r = glob_item(i, write_one_file, false); + r = glob_item(i, write_one_file); if (r < 0) return r; @@ -1651,49 +1766,49 @@ static int create_item(Item *i) { case ADJUST_MODE: case RELABEL_PATH: - r = glob_item(i, path_set_perms, false); + r = glob_item(i, path_set_perms); if (r < 0) return r; break; case RECURSIVE_RELABEL_PATH: - r = glob_item(i, path_set_perms, true); + r = glob_item_recursively(i, fd_set_perms); if (r < 0) return r; break; case SET_XATTR: - r = glob_item(i, path_set_xattrs, false); + r = glob_item(i, path_set_xattrs); if (r < 0) return r; break; case RECURSIVE_SET_XATTR: - r = glob_item(i, path_set_xattrs, true); + r = glob_item_recursively(i, fd_set_xattrs); if (r < 0) return r; break; case SET_ACL: - r = glob_item(i, path_set_acls, false); + r = glob_item(i, path_set_acls); if (r < 0) return r; break; case RECURSIVE_SET_ACL: - r = glob_item(i, path_set_acls, true); + r = glob_item_recursively(i, fd_set_acls); if (r < 0) return r; break; case SET_ATTRIBUTE: - r = glob_item(i, path_set_attribute, false); + r = glob_item(i, path_set_attribute); if (r < 0) return r; break; case RECURSIVE_SET_ATTRIBUTE: - r = glob_item(i, path_set_attribute, true); + r = glob_item_recursively(i, fd_set_attribute); if (r < 0) return r; break; @@ -1743,7 +1858,7 @@ static int remove_item(Item *i) { case REMOVE_PATH: case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - return glob_item(i, remove_item_instance, false); + return glob_item(i, remove_item_instance); default: return 0; @@ -1817,7 +1932,7 @@ static int clean_item(Item *i) { return 0; case EMPTY_DIRECTORY: case IGNORE_DIRECTORY_PATH: - return glob_item(i, clean_item_instance, false); + return glob_item(i, clean_item_instance); default: return 0; } From caced73270d2e85ff7608b39a977a9c6d92a77df Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Mon, 5 Mar 2018 19:02:25 +0100 Subject: [PATCH 4/5] test: fix setup_suse() to make it work with an already populated root --- test/test-functions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-functions b/test/test-functions index 2ece55414a..17e83ccf3f 100644 --- a/test/test-functions +++ b/test/test-functions @@ -1383,8 +1383,8 @@ inst_libdir_file() { } setup_suse() { - ln -s ../usr/bin/systemctl $initdir/bin/systemctl - ln -s ../usr/lib/systemd $initdir/lib/systemd + ln -fs ../usr/bin/systemctl $initdir/bin/ + ln -fs ../usr/lib/systemd $initdir/lib/ inst_simple "/usr/lib/systemd/system/haveged.service" } From e04fc13f5f4fb6db46ef035949c56128b52243e0 Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Mon, 5 Mar 2018 12:32:18 +0100 Subject: [PATCH 5/5] test: add tests for systemd-tmpfiles --- test/TEST-22-TMPFILES/Makefile | 4 +++ test/TEST-22-TMPFILES/run-tmpfiles-tests.sh | 13 +++++++ test/TEST-22-TMPFILES/test-01.sh | 13 +++++++ test/TEST-22-TMPFILES/test.sh | 38 +++++++++++++++++++++ test/TEST-22-TMPFILES/testsuite.service | 8 +++++ 5 files changed, 76 insertions(+) create mode 100644 test/TEST-22-TMPFILES/Makefile create mode 100755 test/TEST-22-TMPFILES/run-tmpfiles-tests.sh create mode 100755 test/TEST-22-TMPFILES/test-01.sh create mode 100755 test/TEST-22-TMPFILES/test.sh create mode 100644 test/TEST-22-TMPFILES/testsuite.service diff --git a/test/TEST-22-TMPFILES/Makefile b/test/TEST-22-TMPFILES/Makefile new file mode 100644 index 0000000000..34d7cc6cdf --- /dev/null +++ b/test/TEST-22-TMPFILES/Makefile @@ -0,0 +1,4 @@ +BUILD_DIR=$(shell ../../tools/find-build-dir.sh) + +all setup clean run: + @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ diff --git a/test/TEST-22-TMPFILES/run-tmpfiles-tests.sh b/test/TEST-22-TMPFILES/run-tmpfiles-tests.sh new file mode 100755 index 0000000000..3ad652f4ed --- /dev/null +++ b/test/TEST-22-TMPFILES/run-tmpfiles-tests.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -x +set -e + +>/failed + +for t in test-*.sh; do + echo "Running $t"; ./$t +done + +touch /testok +rm /failed diff --git a/test/TEST-22-TMPFILES/test-01.sh b/test/TEST-22-TMPFILES/test-01.sh new file mode 100755 index 0000000000..d233e37fb2 --- /dev/null +++ b/test/TEST-22-TMPFILES/test-01.sh @@ -0,0 +1,13 @@ +#! /bin/bash +# +# With "e" don't attempt to set permissions when file doesn't exist, see +# https://github.com/systemd/systemd/pull/6682. +# + +set -e + +rm -fr /tmp/test + +echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create - + +! test -e /tmp/test diff --git a/test/TEST-22-TMPFILES/test.sh b/test/TEST-22-TMPFILES/test.sh new file mode 100755 index 0000000000..ca78933380 --- /dev/null +++ b/test/TEST-22-TMPFILES/test.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -e +TEST_DESCRIPTION="Tmpfiles related tests" +TEST_NO_QEMU=1 + +. $TEST_BASE_DIR/test-functions + +test_setup() { + # create the basic filesystem layout + setup_basic_environment >/dev/null + inst_binary mv + inst_binary stat + inst_binary seq + inst_binary xargs + + # mask some services that we do not want to run in these tests + ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket + ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service + + # setup the testsuite service + cp testsuite.service $initdir/etc/systemd/system/ + setup_testsuite + + mkdir -p $initdir/testsuite + cp run-tmpfiles-tests.sh $initdir/testsuite/ + cp test-*.sh $initdir/testsuite/ + + # create dedicated rootfs for nspawn (located in $TESTDIR/nspawn-root) + setup_nspawn_root +} + +do_test "$@" diff --git a/test/TEST-22-TMPFILES/testsuite.service b/test/TEST-22-TMPFILES/testsuite.service new file mode 100644 index 0000000000..3a44b41989 --- /dev/null +++ b/test/TEST-22-TMPFILES/testsuite.service @@ -0,0 +1,8 @@ +[Unit] +Description=Testsuite service +After=multi-user.target + +[Service] +WorkingDirectory=/testsuite +ExecStart=/testsuite/run-tmpfiles-tests.sh +Type=oneshot