Merge pull request #8822 from fbuihuu/rfc-tmpfiles-safe-upstream
Make tmpfiles safe
This commit is contained in:
+19
-7
@@ -116,8 +116,25 @@ int btrfs_is_subvol(const char *path) {
|
||||
return btrfs_is_subvol_fd(fd);
|
||||
}
|
||||
|
||||
int btrfs_subvol_make(const char *path) {
|
||||
int btrfs_subvol_make_fd(int fd, const char *subvolume) {
|
||||
struct btrfs_ioctl_vol_args args = {};
|
||||
int r;
|
||||
|
||||
assert(subvolume);
|
||||
|
||||
r = validate_subvolume_name(subvolume);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strncpy(args.name, subvolume, sizeof(args.name)-1);
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_subvol_make(const char *path) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
const char *subvolume;
|
||||
int r;
|
||||
@@ -132,12 +149,7 @@ int btrfs_subvol_make(const char *path) {
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
strncpy(args.name, subvolume, sizeof(args.name)-1);
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
return btrfs_subvol_make_fd(fd, subvolume);
|
||||
}
|
||||
|
||||
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
|
||||
|
||||
@@ -65,6 +65,7 @@ int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
|
||||
int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
|
||||
|
||||
int btrfs_subvol_make(const char *path);
|
||||
int btrfs_subvol_make_fd(int fd, const char *subvolume);
|
||||
|
||||
int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
|
||||
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
|
||||
|
||||
+5
-3
@@ -1319,8 +1319,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
|
||||
r = tmp_dir(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (isempty(p))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
extra = strempty(extra);
|
||||
|
||||
@@ -1328,7 +1327,10 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
|
||||
if (isempty(p))
|
||||
x = stpcpy(stpcpy(t, ".#"), extra);
|
||||
else
|
||||
x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
|
||||
|
||||
u = random_u64();
|
||||
for (i = 0; i < 16; i++) {
|
||||
|
||||
+27
-2
@@ -435,6 +435,31 @@ int mkfifo_atomic(const char *path, mode_t mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (path_is_absolute(path))
|
||||
return mkfifo_atomic(path, mode);
|
||||
|
||||
/* We're only interested in the (random) filename. */
|
||||
r = tempfn_random_child("", NULL, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mkfifoat(dirfd, t, mode) < 0)
|
||||
return -errno;
|
||||
|
||||
if (renameat(dirfd, t, dirfd, path) < 0) {
|
||||
unlink_noerrno(t);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_files_in_directory(const char *path, char ***list) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
@@ -670,7 +695,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) {
|
||||
/* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
|
||||
* and doesn't care about any of the other special features we provide either. */
|
||||
r = open(path, O_PATH|O_CLOEXEC);
|
||||
r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
@@ -825,7 +850,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
|
||||
return -EREMOTE;
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
|
||||
char *joined;
|
||||
|
||||
_cleanup_free_ char *destination = NULL;
|
||||
|
||||
@@ -42,6 +42,7 @@ int symlink_idempotent(const char *from, const char *to);
|
||||
int symlink_atomic(const char *from, const char *to);
|
||||
int mknod_atomic(const char *path, mode_t mode, dev_t dev);
|
||||
int mkfifo_atomic(const char *path, mode_t mode);
|
||||
int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode);
|
||||
|
||||
int get_files_in_directory(const char *path, char ***list);
|
||||
|
||||
@@ -72,6 +73,7 @@ enum {
|
||||
CHASE_OPEN = 1 << 4, /* If set, return an O_PATH object to the final component */
|
||||
CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */
|
||||
CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */
|
||||
CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */
|
||||
};
|
||||
|
||||
/* How many iterations to execute before returning -ELOOP */
|
||||
|
||||
@@ -12,6 +12,7 @@ typedef enum LabelFixFlags {
|
||||
int label_fix(const char *path, LabelFixFlags flags);
|
||||
|
||||
int mkdir_label(const char *path, mode_t mode);
|
||||
int mkdirat_label(int dirfd, const char *path, mode_t mode);
|
||||
int symlink_label(const char *old_path, const char *new_path);
|
||||
|
||||
int btrfs_subvol_make_label(const char *path);
|
||||
|
||||
@@ -28,6 +28,23 @@ int mkdir_label(const char *path, mode_t mode) {
|
||||
return mac_smack_fix(path, 0);
|
||||
}
|
||||
|
||||
int mkdirat_label(int dirfd, const char *path, mode_t mode) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = mac_selinux_create_file_prepare_at(dirfd, path, S_IFDIR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mkdirat_errno_wrapper(dirfd, path, mode);
|
||||
mac_selinux_create_file_clear();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mac_smack_fix_at(dirfd, path, 0);
|
||||
}
|
||||
|
||||
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
|
||||
return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_label);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,12 @@ int mkdir_errno_wrapper(const char *pathname, mode_t mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) {
|
||||
if (mkdirat(dirfd, pathname, mode) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
|
||||
return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_errno_wrapper);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ typedef enum MkdirFlags {
|
||||
} MkdirFlags;
|
||||
|
||||
int mkdir_errno_wrapper(const char *pathname, mode_t mode);
|
||||
int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode);
|
||||
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
|
||||
int mkdir_parents(const char *path, mode_t mode);
|
||||
int mkdir_p(const char *path, mode_t mode);
|
||||
|
||||
@@ -110,10 +110,7 @@ int path_make_absolute_cwd(const char *p, char **ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (endswith(cwd, "/"))
|
||||
c = strjoin(cwd, p);
|
||||
else
|
||||
c = strjoin(cwd, "/", p);
|
||||
c = path_join(NULL, cwd, p);
|
||||
}
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
+67
-26
@@ -316,47 +316,88 @@ char* mac_selinux_free(char *label) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
|
||||
#if HAVE_SELINUX
|
||||
static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
|
||||
_cleanup_freecon_ char *filecon = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
int r;
|
||||
|
||||
assert(abspath);
|
||||
assert(path_is_absolute(abspath));
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
|
||||
if (r < 0) {
|
||||
/* No context specified by the policy? Proceed without setting it. */
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
|
||||
} else {
|
||||
if (setfscreatecon_raw(filecon) >= 0)
|
||||
return 0; /* Success! */
|
||||
|
||||
log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
|
||||
}
|
||||
|
||||
if (security_getenforce() > 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
|
||||
int r = 0;
|
||||
|
||||
#if HAVE_SELINUX
|
||||
_cleanup_freecon_ char *filecon = NULL;
|
||||
int r;
|
||||
_cleanup_free_ char *abspath = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!label_hnd)
|
||||
return 0;
|
||||
|
||||
if (path_is_absolute(path))
|
||||
r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
|
||||
else {
|
||||
_cleanup_free_ char *newpath = NULL;
|
||||
if (!path_is_absolute(path)) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
r = path_make_absolute_cwd(path, &newpath);
|
||||
if (dirfd == AT_FDCWD)
|
||||
r = safe_getcwd(&p);
|
||||
else
|
||||
r = fd_get_path(dirfd, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
|
||||
abspath = path_join(NULL, p, path);
|
||||
if (!abspath)
|
||||
return -ENOMEM;
|
||||
|
||||
path = abspath;
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
/* No context specified by the policy? Proceed without setting it. */
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
|
||||
} else {
|
||||
if (setfscreatecon_raw(filecon) >= 0)
|
||||
return 0; /* Success! */
|
||||
|
||||
log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path);
|
||||
}
|
||||
|
||||
if (security_getenforce() > 0)
|
||||
return -errno;
|
||||
|
||||
r = selinux_create_file_prepare_abspath(path, mode);
|
||||
#endif
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
|
||||
int r = 0;
|
||||
|
||||
#if HAVE_SELINUX
|
||||
_cleanup_free_ char *abspath = NULL;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!label_hnd)
|
||||
return 0;
|
||||
|
||||
r = path_make_absolute_cwd(path, &abspath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = selinux_create_file_prepare_abspath(abspath, mode);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
void mac_selinux_create_file_clear(void) {
|
||||
|
||||
@@ -23,6 +23,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
|
||||
char* mac_selinux_free(char *label);
|
||||
|
||||
int mac_selinux_create_file_prepare(const char *path, mode_t mode);
|
||||
int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode);
|
||||
void mac_selinux_create_file_clear(void);
|
||||
|
||||
int mac_selinux_create_socket_prepare(const char *label);
|
||||
|
||||
+61
-30
@@ -122,43 +122,20 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int mac_smack_fix(const char *path, LabelFixFlags flags) {
|
||||
static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_ int fd = -1;
|
||||
const char *label;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
/* The caller should have done the sanity checks. */
|
||||
assert(abspath);
|
||||
assert(path_is_absolute(abspath));
|
||||
|
||||
if (!mac_smack_use())
|
||||
/* Path must be in /dev. */
|
||||
if (!path_startswith(abspath, "/dev"))
|
||||
return 0;
|
||||
|
||||
/* Path must be in /dev. Note that this check is pretty sloppy, as we might be called with non-normalized paths
|
||||
* and hence not detect all cases of /dev. */
|
||||
|
||||
if (path_is_absolute(path)) {
|
||||
if (!path_startswith(path, "/dev"))
|
||||
return 0;
|
||||
} else {
|
||||
_cleanup_free_ char *cwd = NULL;
|
||||
|
||||
r = safe_getcwd(&cwd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_startswith(cwd, "/dev"))
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
|
||||
if (fd < 0) {
|
||||
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
@@ -196,12 +173,62 @@ int mac_smack_fix(const char *path, LabelFixFlags flags) {
|
||||
streq(old_label, label))
|
||||
return 0;
|
||||
|
||||
return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", path);
|
||||
return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", abspath);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
fd = openat(dirfd, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
|
||||
if (fd < 0) {
|
||||
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = fd_get_path(fd, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return smack_fix_fd(fd, p, flags);
|
||||
}
|
||||
|
||||
int mac_smack_fix(const char *path, LabelFixFlags flags) {
|
||||
_cleanup_free_ char *abspath = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
r = path_make_absolute_cwd(path, &abspath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = open(abspath, O_NOFOLLOW|O_CLOEXEC|O_PATH);
|
||||
if (fd < 0) {
|
||||
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return smack_fix_fd(fd, abspath, flags);
|
||||
}
|
||||
|
||||
int mac_smack_copy(const char *dest, const char *src) {
|
||||
int r = 0;
|
||||
_cleanup_free_ char *label = NULL;
|
||||
@@ -249,6 +276,10 @@ int mac_smack_fix(const char *path, LabelFixFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_copy(const char *dest, const char *src) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ typedef enum SmackAttr {
|
||||
bool mac_smack_use(void);
|
||||
|
||||
int mac_smack_fix(const char *path, LabelFixFlags flags);
|
||||
int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags);
|
||||
|
||||
const char* smack_attr_to_string(SmackAttr i) _const_;
|
||||
SmackAttr smack_attr_from_string(const char *s) _pure_;
|
||||
|
||||
@@ -45,6 +45,17 @@ int is_dir(const char* path, bool follow) {
|
||||
return !!S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
int is_dir_fd(int fd) {
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
r = fstat(fd, &st);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return !!S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
int is_device_node(const char *path) {
|
||||
struct stat info;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
int is_symlink(const char *path);
|
||||
int is_dir(const char *path, bool follow);
|
||||
int is_dir_fd(int fd);
|
||||
int is_device_node(const char *path);
|
||||
|
||||
int dir_is_empty(const char *path);
|
||||
|
||||
@@ -22,6 +22,7 @@ static void test_chase_symlinks(void) {
|
||||
_cleanup_free_ char *result = NULL;
|
||||
char temp[] = "/tmp/test-chase.XXXXXX";
|
||||
const char *top, *p, *pslash, *q, *qslash;
|
||||
struct stat st;
|
||||
int r, pfd;
|
||||
|
||||
assert_se(mkdtemp(temp));
|
||||
@@ -266,6 +267,30 @@ static void test_chase_symlinks(void) {
|
||||
assert_se(sd_id128_equal(a, b));
|
||||
}
|
||||
|
||||
/* Test CHASE_NOFOLLOW */
|
||||
|
||||
p = strjoina(temp, "/target");
|
||||
q = strjoina(temp, "/symlink");
|
||||
assert_se(symlink(p, q) >= 0);
|
||||
pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
|
||||
assert_se(pfd > 0);
|
||||
assert_se(path_equal(result, q));
|
||||
assert_se(fstat(pfd, &st) >= 0);
|
||||
assert_se(S_ISLNK(st.st_mode));
|
||||
result = mfree(result);
|
||||
|
||||
/* s1 -> s2 -> nonexistent */
|
||||
q = strjoina(temp, "/s1");
|
||||
assert_se(symlink("s2", q) >= 0);
|
||||
p = strjoina(temp, "/s2");
|
||||
assert_se(symlink("nonexistent", p) >= 0);
|
||||
pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
|
||||
assert_se(pfd > 0);
|
||||
assert_se(path_equal(result, q));
|
||||
assert_se(fstat(pfd, &st) >= 0);
|
||||
assert_se(S_ISLNK(st.st_mode));
|
||||
result = mfree(result);
|
||||
|
||||
/* Test CHASE_ONE */
|
||||
|
||||
p = strjoina(temp, "/start");
|
||||
|
||||
+618
-302
File diff suppressed because it is too large
Load Diff
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
# Basic tests for types creating directories
|
||||
#
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
rm -fr /tmp/{d,D,e}
|
||||
mkdir /tmp/{d,D,e}
|
||||
|
||||
#
|
||||
# 'd'
|
||||
#
|
||||
mkdir /tmp/d/2
|
||||
chmod 777 /tmp/d/2
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
d /tmp/d/1 0755 daemon daemon - -
|
||||
d /tmp/d/2 0755 daemon daemon - -
|
||||
EOF
|
||||
|
||||
test -d /tmp/d/1
|
||||
test $(stat -c %U:%G:%a /tmp/d/1) = "daemon:daemon:755"
|
||||
|
||||
test -d /tmp/d/2
|
||||
test $(stat -c %U:%G:%a /tmp/d/2) = "daemon:daemon:755"
|
||||
|
||||
#
|
||||
# 'D'
|
||||
#
|
||||
mkdir /tmp/D/2
|
||||
chmod 777 /tmp/D/2
|
||||
touch /tmp/D/2/foo
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
D /tmp/D/1 0755 daemon daemon - -
|
||||
D /tmp/D/2 0755 daemon daemon - -
|
||||
EOF
|
||||
|
||||
test -d /tmp/D/1
|
||||
test $(stat -c %U:%G:%a /tmp/D/1) = "daemon:daemon:755"
|
||||
|
||||
test -d /tmp/D/2
|
||||
test $(stat -c %U:%G:%a /tmp/D/2) = "daemon:daemon:755"
|
||||
|
||||
systemd-tmpfiles --remove - <<EOF
|
||||
D /tmp/D/2 0755 daemon daemon - -
|
||||
EOF
|
||||
|
||||
# the content of '2' should be removed
|
||||
test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
|
||||
|
||||
#
|
||||
# 'e'
|
||||
#
|
||||
mkdir -p /tmp/e/2/{d1,d2}
|
||||
chmod 777 /tmp/e/2
|
||||
chmod 777 /tmp/e/2/d*
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
e /tmp/e/1 0755 daemon daemon - -
|
||||
e /tmp/e/2/* 0755 daemon daemon - -
|
||||
EOF
|
||||
|
||||
! test -d /tmp/e/1
|
||||
|
||||
test -d /tmp/e/2
|
||||
test $(stat -c %U:%G:%a /tmp/e/2) = "root:root:777"
|
||||
|
||||
test -d /tmp/e/2/d1
|
||||
test $(stat -c %U:%G:%a /tmp/e/2/d1) = "daemon:daemon:755"
|
||||
test -d /tmp/e/2/d2
|
||||
test $(stat -c %U:%G:%a /tmp/e/2/d2) = "daemon:daemon:755"
|
||||
|
||||
# 'e' operates on directories only
|
||||
mkdir -p /tmp/e/3/{d1,d2}
|
||||
chmod 777 /tmp/e/3
|
||||
chmod 777 /tmp/e/3/d*
|
||||
touch /tmp/e/3/f1
|
||||
chmod 644 /tmp/e/3/f1
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
e /tmp/e/3/* 0755 daemon daemon - -
|
||||
EOF
|
||||
|
||||
# the directories should have been processed although systemd-tmpfiles failed
|
||||
# previously due to the presence of a file.
|
||||
test -d /tmp/e/3/d1
|
||||
test $(stat -c %U:%G:%a /tmp/e/3/d1) = "daemon:daemon:755"
|
||||
test -d /tmp/e/3/d2
|
||||
test $(stat -c %U:%G:%a /tmp/e/3/d2) = "daemon:daemon:755"
|
||||
|
||||
test -f /tmp/e/3/f1
|
||||
test $(stat -c %U:%G:%a /tmp/e/3/f1) = "root:root:644"
|
||||
Executable
+236
@@ -0,0 +1,236 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
# Basic tests for types creating/writing files
|
||||
#
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
rm -fr /tmp/{f,F,w}
|
||||
mkdir /tmp/{f,F,w}
|
||||
touch /tmp/file-owned-by-root
|
||||
|
||||
#
|
||||
# 'f'
|
||||
#
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/1 0644 - - - -
|
||||
f /tmp/f/2 0644 - - - This string should be written
|
||||
EOF
|
||||
|
||||
### '1' should exist and be empty
|
||||
test -f /tmp/f/1; ! test -s /tmp/f/1
|
||||
test $(stat -c %U:%G:%a /tmp/f/1) = "root:root:644"
|
||||
|
||||
test $(stat -c %U:%G:%a /tmp/f/2) = "root:root:644"
|
||||
test "$(< /tmp/f/2)" = "This string should be written"
|
||||
|
||||
### The perms are supposed to be updated even if the file already exists.
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/1 0666 nobody nogroup - This string should not be written
|
||||
EOF
|
||||
|
||||
# file should be empty
|
||||
! test -s /tmp/f/1
|
||||
test $(stat -c %U:%G:%a /tmp/f/1) = "nobody:nogroup:666"
|
||||
|
||||
### But we shouldn't try to set perms on an existing file which is not a
|
||||
### regular one.
|
||||
mkfifo /tmp/f/fifo
|
||||
chmod 644 /tmp/f/fifo
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/fifo 0666 nobody nogroup - This string should not be written
|
||||
EOF
|
||||
|
||||
test -p /tmp/f/fifo
|
||||
test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
|
||||
|
||||
### 'f' should not follow symlinks.
|
||||
ln -s missing /tmp/f/dangling
|
||||
ln -s /tmp/file-owned-by-root /tmp/f/symlink
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/dangling 0644 nobody nogroup - -
|
||||
f /tmp/f/symlink 0644 nobody nogroup - -
|
||||
EOF
|
||||
! test -e /tmp/f/missing
|
||||
test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
|
||||
|
||||
### Handle read-only filesystem gracefully: we shouldn't fail if the target
|
||||
### already exists and have the correct perms.
|
||||
mkdir /tmp/f/rw-fs
|
||||
mkdir /tmp/f/ro-fs
|
||||
|
||||
touch /tmp/f/rw-fs/foo
|
||||
chmod 644 /tmp/f/rw-fs/foo
|
||||
|
||||
mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written
|
||||
EOF
|
||||
test -f /tmp/f/ro-fs/foo; ! test -s /tmp/f/ro-fs/foo
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/ro-fs/foo 0666 - - - -
|
||||
EOF
|
||||
test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/ro-fs/bar 0644 - - - -
|
||||
EOF
|
||||
! test -e /tmp/f/ro-fs/bar
|
||||
|
||||
### 'f' shouldn't follow unsafe paths.
|
||||
mkdir /tmp/f/nobody
|
||||
ln -s /root /tmp/f/nobody/unsafe-symlink
|
||||
chown -R --no-dereference nobody:nogroup /tmp/f/nobody
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/f/nobody/unsafe-symlink/exploit 0644 nobody nogroup - -
|
||||
EOF
|
||||
! test -e /tmp/f/nobody/unsafe-symlink/exploit
|
||||
|
||||
#
|
||||
# 'F'
|
||||
#
|
||||
echo "This should be truncated" >/tmp/F/truncated
|
||||
echo "This should be truncated" >/tmp/F/truncated-with-content
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/created 0644 - - - -
|
||||
F /tmp/F/created-with-content 0644 - - - new content
|
||||
F /tmp/F/truncated 0666 nobody nogroup - -
|
||||
F /tmp/F/truncated-with-content 0666 nobody nogroup - new content
|
||||
EOF
|
||||
|
||||
test -f /tmp/F/created; ! test -s /tmp/F/created
|
||||
test -f /tmp/F/created-with-content
|
||||
test "$(< /tmp/F/created-with-content)" = "new content"
|
||||
test -f /tmp/F/truncated; ! test -s /tmp/F/truncated
|
||||
test $(stat -c %U:%G:%a /tmp/F/truncated) = "nobody:nogroup:666"
|
||||
test -s /tmp/F/truncated-with-content
|
||||
test $(stat -c %U:%G:%a /tmp/F/truncated-with-content) = "nobody:nogroup:666"
|
||||
|
||||
### We shouldn't try to truncate anything but regular files since the behavior is
|
||||
### unspecified in the other cases.
|
||||
mkfifo /tmp/F/fifo
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/fifo 0644 - - - -
|
||||
EOF
|
||||
|
||||
test -p /tmp/F/fifo
|
||||
|
||||
### 'F' should not follow symlinks.
|
||||
ln -s missing /tmp/F/dangling
|
||||
ln -s /tmp/file-owned-by-root /tmp/F/symlink
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/F/dangling 0644 nobody nogroup - -
|
||||
f /tmp/F/symlink 0644 nobody nogroup - -
|
||||
EOF
|
||||
! test -e /tmp/F/missing
|
||||
test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
|
||||
|
||||
### Handle read-only filesystem gracefully: we shouldn't fail if the target
|
||||
### already exists and is empty.
|
||||
mkdir /tmp/F/rw-fs
|
||||
mkdir /tmp/F/ro-fs
|
||||
|
||||
touch /tmp/F/rw-fs/foo
|
||||
chmod 644 /tmp/F/rw-fs/foo
|
||||
|
||||
mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/ro-fs/foo 0644 - - - -
|
||||
EOF
|
||||
test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
|
||||
|
||||
echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/ro-fs/foo 0644 - - - -
|
||||
EOF
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written
|
||||
EOF
|
||||
test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
|
||||
|
||||
# Trying to change the perms should fail.
|
||||
>/tmp/F/rw-fs/foo
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/ro-fs/foo 0666 - - - -
|
||||
EOF
|
||||
test $(stat -c %U:%G:%a /tmp/F/ro-fs/foo) = "root:root:644"
|
||||
|
||||
### Try to create a new file.
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/ro-fs/bar 0644 - - - -
|
||||
EOF
|
||||
! test -e /tmp/F/ro-fs/bar
|
||||
|
||||
### 'F' shouldn't follow unsafe paths.
|
||||
mkdir /tmp/F/nobody
|
||||
ln -s /root /tmp/F/nobody/unsafe-symlink
|
||||
chown -R --no-dereference nobody:nogroup /tmp/F/nobody
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
F /tmp/F/nobody/unsafe-symlink/exploit 0644 nobody nogroup - -
|
||||
EOF
|
||||
! test -e /tmp/F/nobody/unsafe-symlink/exploit
|
||||
|
||||
#
|
||||
# 'w'
|
||||
#
|
||||
touch /tmp/w/overwritten
|
||||
|
||||
### nop if the target does not exist.
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
w /tmp/w/unexistent 0644 - - - new content
|
||||
EOF
|
||||
! test -e /tmp/w/unexistent
|
||||
|
||||
### no argument given -> fails.
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
w /tmp/w/unexistent 0644 - - - -
|
||||
EOF
|
||||
|
||||
### write into an empty file.
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
w /tmp/w/overwritten 0644 - - - old content
|
||||
EOF
|
||||
test -f /tmp/w/overwritten
|
||||
test "$(< /tmp/w/overwritten)" = "old content"
|
||||
|
||||
### new content is overwritten
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
w /tmp/w/overwritten 0644 - - - new content
|
||||
EOF
|
||||
test -f /tmp/w/overwritten
|
||||
test "$(< /tmp/w/overwritten)" = "new content"
|
||||
|
||||
### writing into an 'exotic' file sould be allowed.
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
w /dev/null - - - - new content
|
||||
EOF
|
||||
|
||||
### 'w' follows symlinks
|
||||
ln -s ./overwritten /tmp/w/symlink
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
w /tmp/w/symlink - - - - $(readlink -e /tmp/w/symlink)
|
||||
EOF
|
||||
readlink -e /tmp/w/symlink
|
||||
test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
|
||||
|
||||
### 'w' shouldn't follow unsafe paths.
|
||||
mkdir /tmp/w/nobody
|
||||
ln -s /root /tmp/w/nobody/unsafe-symlink
|
||||
chown -R --no-dereference nobody:nogroup /tmp/w/nobody
|
||||
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
f /tmp/w/nobody/unsafe-symlink/exploit 0644 nobody nogroup - -
|
||||
EOF
|
||||
! test -e /tmp/w/nobody/unsafe-symlink/exploit
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
# Basic tests for types creating fifos
|
||||
#
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
rm -fr /tmp/p
|
||||
mkdir /tmp/p
|
||||
touch /tmp/p/f1
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
p /tmp/p/fifo1 0666 - - - -
|
||||
EOF
|
||||
|
||||
test -p /tmp/p/fifo1
|
||||
test $(stat -c %U:%G:%a /tmp/p/fifo1) = "root:root:666"
|
||||
|
||||
# it should refuse to overwrite an existing file
|
||||
! systemd-tmpfiles --create - <<EOF
|
||||
p /tmp/p/f1 0666 - - - -
|
||||
EOF
|
||||
|
||||
test -f /tmp/p/f1
|
||||
|
||||
# unless '+' prefix is used
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
p+ /tmp/p/f1 0666 - - - -
|
||||
EOF
|
||||
|
||||
test -p /tmp/p/f1
|
||||
test $(stat -c %U:%G:%a /tmp/p/f1) = "root:root:666"
|
||||
|
||||
#
|
||||
# Must be fixed
|
||||
#
|
||||
# mkdir /tmp/p/nobody
|
||||
# #ln -s /root /tmp/F/nobody/unsafe-symlink
|
||||
# chown -R --no-dereference nobody:nogroup /tmp/p/nobody
|
||||
#
|
||||
# systemd-tmpfiles --create - <<EOF
|
||||
# p /tmp/p/nobody/fifo2 0666 nobody nogroup - -
|
||||
# EOF
|
||||
@@ -14,6 +14,8 @@ test_setup() {
|
||||
inst_binary stat
|
||||
inst_binary seq
|
||||
inst_binary xargs
|
||||
inst_binary mkfifo
|
||||
inst_binary readlink
|
||||
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user