diff options
-rw-r--r-- | man/machinectl.xml | 26 | ||||
-rw-r--r-- | man/systemd-nspawn.xml | 8 | ||||
-rw-r--r-- | src/basic/fs-util.c | 16 | ||||
-rw-r--r-- | src/basic/fs-util.h | 2 | ||||
-rw-r--r-- | src/machine/machine-dbus.c | 84 | ||||
-rw-r--r-- | src/nspawn/meson.build | 23 | ||||
-rw-r--r-- | src/nspawn/nspawn-def.h | 33 | ||||
-rw-r--r-- | src/nspawn/nspawn-patch-uid.c | 121 | ||||
-rw-r--r-- | src/nspawn/nspawn-patch-uid.h | 2 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 7 | ||||
-rw-r--r-- | src/test/test-fs-util.c | 27 |
11 files changed, 248 insertions, 101 deletions
diff --git a/man/machinectl.xml b/man/machinectl.xml index cf46fe8024..8c71880cf2 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -208,16 +208,16 @@ <varlistentry> <term><option>--mkdir</option></term> - <listitem><para>When used with <command>bind</command>, creates - the destination directory before applying the bind - mount.</para></listitem> + <listitem><para>When used with <command>bind</command>, creates the destination file or directory before + applying the bind mount. Note that even though the name of this option suggests that it is suitable only for + directories, this option also creates the destination file node to mount over if the the object to mount is not + a directory, but a regular file, device node, socket or FIFO.</para></listitem> </varlistentry> <varlistentry> <term><option>--read-only</option></term> - <listitem><para>When used with <command>bind</command>, applies - a read-only bind mount.</para> + <listitem><para>When used with <command>bind</command>, creates a read-only bind mount.</para> <para>When used with <command>clone</command>, <command>import-raw</command> or <command>import-tar</command> a read-only container or VM image is created.</para></listitem> @@ -528,14 +528,16 @@ <varlistentry> <term><command>bind</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term> - <listitem><para>Bind mounts a directory from the host into the specified container. The first directory - argument is the source directory on the host, the second directory argument is the destination directory in the - container. When the latter is omitted, the destination path in the container is the same as the source path on - the host. When combined with the <option>--read-only</option> switch, a ready-only bind mount is created. When - combined with the <option>--mkdir</option> switch, the destination path is first created before the mount is - applied. Note that this option is currently only supported for + <listitem><para>Bind mounts a file or directory from the host into the specified container. The first path + argument is the source file or directory on the host, the second path argument is the destination file or + directory in the container. When the latter is omitted, the destination path in the container is the same as + the source path on the host. When combined with the <option>--read-only</option> switch, a ready-only bind + mount is created. When combined with the <option>--mkdir</option> switch, the destination path is first created + before the mount is applied. Note that this option is currently only supported for <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> containers, - and only if user namespacing (<option>--private-users</option>) is not used.</para></listitem> + and only if user namespacing (<option>--private-users</option>) is not used. This command supports bind + mounting directories, regular files, device nodes, <constant>AF_UNIX</constant> socket nodes, as well as + FIFOs.</para></listitem> </varlistentry> <varlistentry> diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 98ce1529de..1ef6567e48 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -806,7 +806,13 @@ <option>norbind</option> are allowed, controlling whether to create a recursive or a regular bind mount. Defaults to "rbind". Backslash escapes are interpreted, so <literal>\:</literal> may be used to embed colons in either path. This option may be specified multiple times for creating multiple independent bind - mount points. The <option>--bind-ro=</option> option creates read-only bind mounts.</para></listitem> + mount points. The <option>--bind-ro=</option> option creates read-only bind mounts.</para> + + <para>Note that when this option is used in combination with <option>--private-users</option>, the resulting + mount points will be owned by the <constant>nobody</constant> user. That's because the mount and its files and + directories continue to be owned by the relevant host users and groups, which do not exist in the container, + and thus show up under the wildcard UID 65534 (nobody). If such bind mounts are created, it is recommended to + make them read-only, using <option>--bind-ro=</option>.</para></listitem> </varlistentry> <varlistentry> diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 946f779a32..ff8b4e8747 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -104,7 +104,6 @@ int rmdir_parents(const char *path, const char *stop) { return 0; } - int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { struct stat buf; int ret; @@ -809,3 +808,18 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return exists; } + +int access_fd(int fd, int mode) { + char p[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + int r; + + /* Like access() but operates on an already open fd */ + + xsprintf(p, "/proc/self/fd/%i", fd); + + r = access(p, mode); + if (r < 0) + r = -errno; + + return r; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index d3342d5cda..9849522f5a 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -98,3 +98,5 @@ static inline void unlink_and_free(char *p) { free(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); + +int access_fd(int fd, int mode); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 3cb90be67d..28d05c088a 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -836,11 +836,13 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu bool mount_slave_created = false, mount_slave_mounted = false, mount_tmp_created = false, mount_tmp_mounted = false, mount_outside_created = false, mount_outside_mounted = false; + _cleanup_free_ char *chased_src = NULL; + int read_only, make_file_or_directory; const char *dest, *src; Machine *m = userdata; - int read_only, make_directory; - pid_t child; + struct stat st; siginfo_t si; + pid_t child; uid_t uid; int r; @@ -850,7 +852,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu if (m->class != MACHINE_CONTAINER) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines."); - r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_directory); + r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory); if (r < 0) return r; @@ -890,6 +892,15 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu if (laccess(p, F_OK) < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points."); + r = chase_symlinks(src, NULL, 0, &chased_src); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to resolve source path: %m"); + + if (lstat(chased_src, &st) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to stat() source path: %m"); + if (S_ISLNK(st.st_mode)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safeā¦ */ + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Source directory can't be a symbolic link"); + /* Our goal is to install a new bind mount into the container, possibly read-only. This is irritatingly complex unfortunately, currently. @@ -916,18 +927,21 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu goto finish; } - /* Second, we mount the source directory to a directory inside - of our MS_SLAVE playground. */ + /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */ mount_tmp = strjoina(mount_slave, "/mount"); - if (mkdir(mount_tmp, 0700) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp); + if (S_ISDIR(st.st_mode)) + r = mkdir(mount_tmp, 0700) < 0 ? -errno : 0; + else + r = touch(mount_tmp); + if (r < 0) { + sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp); goto finish; } mount_tmp_created = true; - if (mount(src, mount_tmp, NULL, MS_BIND, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to mount %s: %m", src); + if (mount(chased_src, mount_tmp, NULL, MS_BIND, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to mount %s: %m", chased_src); goto finish; } @@ -940,13 +954,18 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu goto finish; } - /* Fourth, we move the new bind mount into the propagation - * directory. This way it will appear there read-only + /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only * right-away. */ mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX"); - if (!mkdtemp(mount_outside)) { - r = sd_bus_error_set_errnof(error, errno, "Cannot create propagation directory %s: %m", mount_outside); + if (S_ISDIR(st.st_mode)) + r = mkdtemp(mount_outside) ? 0 : -errno; + else { + r = mkostemp_safe(mount_outside); + safe_close(r); + } + if (r < 0) { + sd_bus_error_set_errnof(error, errno, "Cannot create propagation file or directory %s: %m", mount_outside); goto finish; } @@ -960,7 +979,10 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu mount_outside_mounted = true; mount_tmp_mounted = false; - (void) rmdir(mount_tmp); + if (S_ISDIR(st.st_mode)) + (void) rmdir(mount_tmp); + else + (void) unlink(mount_tmp); mount_tmp_created = false; (void) umount(mount_slave); @@ -999,8 +1021,14 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu goto child_fail; } - if (make_directory) - (void) mkdir_p(dest, 0755); + if (make_file_or_directory) { + if (S_ISDIR(st.st_mode)) + (void) mkdir_p(dest, 0755); + else { + (void) mkdir_parents(dest, 0755); + safe_close(open(dest, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)); + } + } /* Fifth, move the mount to the right place inside */ mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside)); @@ -1042,19 +1070,27 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu finish: if (mount_outside_mounted) - umount(mount_outside); - if (mount_outside_created) - rmdir(mount_outside); + (void) umount(mount_outside); + if (mount_outside_created) { + if (S_ISDIR(st.st_mode)) + (void) rmdir(mount_outside); + else + (void) unlink(mount_outside); + } if (mount_tmp_mounted) - umount(mount_tmp); - if (mount_tmp_created) - rmdir(mount_tmp); + (void) umount(mount_tmp); + if (mount_tmp_created) { + if (S_ISDIR(st.st_mode)) + (void) rmdir(mount_tmp); + else + (void) unlink(mount_tmp); + } if (mount_slave_mounted) - umount(mount_slave); + (void) umount(mount_slave); if (mount_slave_created) - rmdir(mount_slave); + (void) rmdir(mount_slave); return r; } diff --git a/src/nspawn/meson.build b/src/nspawn/meson.build index b6ac6006ab..7f54ebc6db 100644 --- a/src/nspawn/meson.build +++ b/src/nspawn/meson.build @@ -1,25 +1,26 @@ systemd_nspawn_sources = files(''' - nspawn.c - nspawn-settings.c - nspawn-settings.h + nspawn-cgroup.c + nspawn-cgroup.h + nspawn-def.h + nspawn-expose-ports.c + nspawn-expose-ports.h nspawn-mount.c nspawn-mount.h nspawn-network.c nspawn-network.h - nspawn-expose-ports.c - nspawn-expose-ports.h - nspawn-cgroup.c - nspawn-cgroup.h - nspawn-seccomp.c - nspawn-seccomp.h + nspawn-patch-uid.c + nspawn-patch-uid.h nspawn-register.c nspawn-register.h + nspawn-seccomp.c + nspawn-seccomp.h + nspawn-settings.c + nspawn-settings.h nspawn-setuid.c nspawn-setuid.h nspawn-stub-pid1.c nspawn-stub-pid1.h - nspawn-patch-uid.c - nspawn-patch-uid.h + nspawn.c '''.split()) nspawn_gperf_c = custom_target( diff --git a/src/nspawn/nspawn-def.h b/src/nspawn/nspawn-def.h new file mode 100644 index 0000000000..fc3c94064c --- /dev/null +++ b/src/nspawn/nspawn-def.h @@ -0,0 +1,33 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/types.h> + +/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit + * UID range here. We leave a bit of room at the lower end and a lot of room at the upper end, so that other subsystems + * may have their own allocation ranges too. */ +#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000)) +#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000)) + +/* While we are chmod()ing a directory tree, we set the top-level UID base to this "busy" base, so that we can always + * recognize trees we are were chmod()ing recursively and got interrupted in */ +#define UID_BUSY_BASE ((uid_t) UINT32_C(0xFFFE0000)) +#define UID_BUSY_MASK ((uid_t) UINT32_C(0xFFFF0000)) diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c index 063fdb1053..7857b453d6 100644 --- a/src/nspawn/nspawn-patch-uid.c +++ b/src/nspawn/nspawn-patch-uid.c @@ -23,13 +23,16 @@ #include <sys/acl.h> #endif #include <sys/stat.h> +#include <sys/statvfs.h> #include <sys/vfs.h> #include <unistd.h> #include "acl-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "fs-util.h" #include "missing.h" +#include "nspawn-def.h" #include "nspawn-patch-uid.h" #include "stat-util.h" #include "stdio-util.h" @@ -289,42 +292,44 @@ static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift * user namespaces, however their inodes may relate to host resources or only * valid in the global user namespace, therefore no patching should be applied. */ -static int is_fs_fully_userns_compatible(int fd) { - struct statfs sfs; - - assert(fd >= 0); - - if (fstatfs(fd, &sfs) < 0) - return -errno; - - return F_TYPE_EQUAL(sfs.f_type, BINFMTFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, CGROUP_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, CGROUP2_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, DEBUGFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, DEVPTS_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, EFIVARFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, HUGETLBFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, MQUEUE_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, PROC_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, PSTOREFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SELINUX_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SMACK_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SECURITYFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, BPF_FS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, TRACEFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SYSFS_MAGIC); +static int is_fs_fully_userns_compatible(const struct statfs *sfs) { + + assert(sfs); + + return F_TYPE_EQUAL(sfs->f_type, BINFMTFS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, CGROUP_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, CGROUP2_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, DEBUGFS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, DEVPTS_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, EFIVARFS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, HUGETLBFS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, MQUEUE_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, PROC_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, PSTOREFS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, SELINUX_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, SMACK_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, SECURITYFS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, BPF_FS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, TRACEFS_MAGIC) || + F_TYPE_EQUAL(sfs->f_type, SYSFS_MAGIC); } static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) { + _cleanup_closedir_ DIR *d = NULL; bool changed = false; + struct statfs sfs; int r; assert(fd >= 0); - /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we - * probably shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's - * stop the recursion when we hit procfs, sysfs or some other special file systems. */ - r = is_fs_fully_userns_compatible(fd); + if (fstatfs(fd, &sfs) < 0) + return -errno; + + /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we probably + * shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's stop the recursion + * when we hit procfs, sysfs or some other special file systems. */ + + r = is_fs_fully_userns_compatible(&sfs); if (r < 0) goto finish; if (r > 0) { @@ -332,26 +337,12 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift goto finish; } - r = patch_fd(fd, NULL, st, shift); - if (r == -EROFS) { - _cleanup_free_ char *name = NULL; - - if (!is_toplevel) { - /* When we hit a ready-only subtree we simply skip it, but log about it. */ - (void) fd_get_path(fd, &name); - log_debug("Skippping read-only file or directory %s.", strna(name)); - r = 0; - } - - goto finish; - } - if (r < 0) - goto finish; - if (r > 0) - changed = true; + /* Also, if we hit a read-only file system, then don't bother, skip the whole subtree */ + if ((sfs.f_flags & ST_RDONLY) || + access_fd(fd, W_OK) == -EROFS) + goto read_only; if (S_ISDIR(st->st_mode)) { - _cleanup_closedir_ DIR *d = NULL; struct dirent *de; if (!donate_fd) { @@ -411,7 +402,27 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift } } + /* After we descended, also patch the directory itself. It's key to do this in this order so that the top-level + * directory is patched as very last object in the tree, so that we can use it as quick indicator whether the + * tree is properly chown()ed already. */ + r = patch_fd(d ? dirfd(d) : fd, NULL, st, shift); + if (r == -EROFS) + goto read_only; + if (r > 0) + changed = true; + r = changed; + goto finish; + +read_only: + if (!is_toplevel) { + _cleanup_free_ char *name = NULL; + + /* When we hit a ready-only subtree we simply skip it, but log about it. */ + (void) fd_get_path(fd, &name); + log_debug("Skippping read-only file or directory %s.", strna(name)); + r = changed; + } finish: if (donate_fd) @@ -437,6 +448,11 @@ static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t rang goto finish; } + if (shift == UID_BUSY_BASE) { + r = -EINVAL; + goto finish; + } + if (range != 0x10000) { /* We only support containers with 16bit UID ranges for the patching logic */ r = -EOPNOTSUPP; @@ -459,6 +475,19 @@ static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t rang if (((uint32_t) (st.st_uid ^ shift) >> 16) == 0) return 0; + /* Before we start recursively chowning, mark the top-level dir as "busy" by chowning it to the "busy" + * range. Should we be interrupted in the middle of our work, we'll see it owned by this user and will start + * chown()ing it again, unconditionally, as the busy UID is not a valid UID we'd everpick for ourselves. */ + + if ((st.st_uid & UID_BUSY_MASK) != UID_BUSY_BASE) { + if (fchown(fd, + UID_BUSY_BASE | (st.st_uid & ~UID_BUSY_MASK), + (gid_t) UID_BUSY_BASE | (st.st_gid & ~(gid_t) UID_BUSY_MASK)) < 0) { + r = -errno; + goto finish; + } + } + return recurse_fd(fd, donate_fd, &st, shift, true); finish: diff --git a/src/nspawn/nspawn-patch-uid.h b/src/nspawn/nspawn-patch-uid.h index 55d0990016..ff6a7b7647 100644 --- a/src/nspawn/nspawn-patch-uid.h +++ b/src/nspawn/nspawn-patch-uid.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index de72c37452..e946bf80b6 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -77,6 +77,7 @@ #include "mount-util.h" #include "netlink-util.h" #include "nspawn-cgroup.h" +#include "nspawn-def.h" #include "nspawn-expose-ports.h" #include "nspawn-mount.h" #include "nspawn-network.h" @@ -106,12 +107,6 @@ #include "user-util.h" #include "util.h" -/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit - * UID range here. We leave a bit of room at the lower end and a lot of room at the upper end, so that other subsystems - * may have their own allocation ranges too. */ -#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000)) -#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000)) - /* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path * nspawn_notify_socket_path is relative to the container * the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */ diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index a5871a25b0..e8fe2b3ff1 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -315,6 +315,32 @@ static void test_dot_or_dot_dot(void) { assert_se(!dot_or_dot_dot("..foo")); } +static void test_access_fd(void) { + _cleanup_(rmdir_and_freep) char *p = NULL; + _cleanup_close_ int fd = -1; + + assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0); + + fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC); + assert_se(fd >= 0); + + assert_se(access_fd(fd, R_OK) >= 0); + assert_se(access_fd(fd, F_OK) >= 0); + assert_se(access_fd(fd, W_OK) >= 0); + + assert_se(fchmod(fd, 0000) >= 0); + + assert_se(access_fd(fd, F_OK) >= 0); + + if (geteuid() == 0) { + assert_se(access_fd(fd, R_OK) >= 0); + assert_se(access_fd(fd, W_OK) >= 0); + } else { + assert_se(access_fd(fd, R_OK) == -EACCES); + assert_se(access_fd(fd, W_OK) == -EACCES); + } +} + int main(int argc, char *argv[]) { test_unlink_noerrno(); test_get_files_in_directory(); @@ -322,6 +348,7 @@ int main(int argc, char *argv[]) { test_var_tmp(); test_chase_symlinks(); test_dot_or_dot_dot(); + test_access_fd(); return 0; } |