#include "alloc-util.h"
#include "chase.h"
+#include "errno-util.h"
#include "escape.h"
#include "extract-word.h"
#include "fd-util.h"
if (m->rm_rf_tmpdir && chown(m->source, uid_shift, uid_shift) < 0)
return log_error_errno(errno, "Failed to chown %s: %m", m->source);
- if (stat(m->source, &source_st) < 0)
+ /* UID/GIDs of idmapped mounts are always resolved in the caller's user namespace. In other
+ * words, they're not nested. If we're doing an idmapped mount from a bind mount that's
+ * already idmapped itself, the old idmap is replaced with the new one. This means that the
+ * source uid which we put in the idmap userns has to be the uid of mount source in the
+ * caller's userns *without* any mount idmapping in place. To get that uid, we clone the
+ * mount source tree and clear any existing idmapping and temporarily mount that tree over
+ * the mount source before we stat the mount source to figure out the source uid. */
+ _cleanup_close_ int fd_clone = open_tree_attr_fallback(
+ AT_FDCWD,
+ m->source,
+ OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC,
+ &(struct mount_attr) {
+ .attr_clr = MOUNT_ATTR_IDMAP,
+ });
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(fd_clone))
+ /* We can only clear idmapped mounts with open_tree_attr(), but there might not be one in
+ * the first place, so we keep going if we get a not supported error. */
+ fd_clone = open_tree(AT_FDCWD, m->source, OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC);
+ if (fd_clone < 0)
+ return log_error_errno(errno, "Failed to clone %s: %m", m->source);
+
+ if (fstat(fd_clone, &source_st) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", m->source);
r = chase(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where, NULL);
dest_uid = uid_shift;
}
- r = mount_nofollow_verbose(LOG_ERR, m->source, where, NULL, mount_flags, mount_opts);
- if (r < 0)
- return r;
+ if (move_mount(fd_clone, "", AT_FDCWD, where, MOVE_MOUNT_F_EMPTY_PATH) < 0)
+ return log_error_errno(errno, "Failed to mount %s to %s: %m", m->source, where);
+
+ fd_clone = safe_close(fd_clone);
if (m->read_only) {
r = bind_remount_recursive(where, MS_RDONLY, MS_RDONLY, NULL);
return TAKE_FD(userns_fd);
}
+int open_tree_attr_fallback(int dir_fd, const char *path, unsigned int flags, struct mount_attr *attr) {
+ assert(attr);
+
+ _cleanup_close_ int fd = open_tree_attr(dir_fd, path, flags, attr, sizeof(struct mount_attr));
+ if (fd >= 0)
+ return TAKE_FD(fd);
+ if (!ERRNO_IS_NOT_SUPPORTED(errno))
+ return log_debug_errno(errno, "Failed to open tree and set mount attributes: %m");
+
+ if (attr->attr_clr & MOUNT_ATTR_IDMAP)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Cannot clear idmap from mount without open_tree_attr()");
+
+ fd = open_tree(dir_fd, path, flags);
+ if (fd < 0)
+ return log_debug_errno(errno, "Failed to open tree: %m");
+
+ if (mount_setattr(fd, "", AT_EMPTY_PATH | (flags & AT_RECURSIVE), attr, sizeof(struct mount_attr)) < 0)
+ return log_debug_errno(errno, "Failed to change mount attributes: %m");
+
+ return TAKE_FD(fd);
+}
+
int remount_idmap_fd(
char **paths,
int userns_fd,
CLEANUP_ARRAY(mount_fds, n_mounts_fds, close_many_and_free);
for (size_t i = 0; i < n; i++) {
- int mntfd;
-
- /* Clone the mount point */
- mntfd = mount_fds[n_mounts_fds] = open_tree(-EBADF, paths[i], OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
+ /* Clone the mount point and et the user namespace mapping attribute on the cloned mount point. */
+ mount_fds[n_mounts_fds] = open_tree_attr_fallback(
+ /* dir_fd= */ -EBADF,
+ paths[i],
+ OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC,
+ &(struct mount_attr) {
+ .attr_set = MOUNT_ATTR_IDMAP | extra_mount_attr_set,
+ .userns_fd = userns_fd,
+ });
if (mount_fds[n_mounts_fds] < 0)
- return log_debug_errno(errno, "Failed to open tree of mounted filesystem '%s': %m", paths[i]);
+ return mount_fds[n_mounts_fds];
n_mounts_fds++;
-
- /* Set the user namespace mapping attribute on the cloned mount point */
- if (mount_setattr(mntfd, "", AT_EMPTY_PATH,
- &(struct mount_attr) {
- .attr_set = MOUNT_ATTR_IDMAP | extra_mount_attr_set,
- .userns_fd = userns_fd,
- }, sizeof(struct mount_attr)) < 0)
- return log_debug_errno(errno, "Failed to change bind mount attributes for clone of '%s': %m", paths[i]);
}
for (size_t i = n; i > 0; i--) { /* Unmount the paths right-to-left */
_REMOUNT_IDMAPPING_INVALID = -EINVAL,
} RemountIdmapping;
+int open_tree_attr_fallback(int dir_fd, const char *path, unsigned int flags, struct mount_attr *attr);
+
int make_userns(uid_t uid_shift, uid_t uid_range, uid_t host_owner, uid_t dest_owner, RemountIdmapping idmapping);
int remount_idmap_fd(char **p, int userns_fd, uint64_t extra_mount_attr_set);
int remount_idmap(char **p, uid_t uid_shift, uid_t uid_range, uid_t host_owner, uid_t dest_owner, RemountIdmapping idmapping);