Skip to content

Commit

Permalink
Merge pull request #1325 from giuseppe/copy-symlinks
Browse files Browse the repository at this point in the history
linux: new mount option "copy-symlink"
  • Loading branch information
giuseppe committed Oct 18, 2023
2 parents e8f42ce + 2cc7390 commit dd8801d
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 95 deletions.
8 changes: 8 additions & 0 deletions crun.1
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,14 @@ If the \fB\fCtmpcopyup\fR option is specified for a tmpfs, then the path that
is shadowed by the tmpfs mount is recursively copied up to the tmpfs
itself.

.SH copy-symlink mount options
.PP
If the \fB\fCcopy-symlink\fR option is specified, if the source of a bind
mount is a symlink, the symlink is recreated at the specified
destination instead of attempting a mount that would resolve the
symlink itself. If the destination already exists and it is not a
symlink with the expected content, crun will return an error.

.SH r$FLAG mount options
.PP
If a \fB\fCr$FLAG\fR mount option is specified then the flag \fB\fC$FLAG\fR is set
Expand Down
8 changes: 8 additions & 0 deletions crun.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,14 @@ If the `tmpcopyup` option is specified for a tmpfs, then the path that
is shadowed by the tmpfs mount is recursively copied up to the tmpfs
itself.

## copy-symlink mount options

If the `copy-symlink` option is specified, if the source of a bind
mount is a symlink, the symlink is recreated at the specified
destination instead of attempting a mount that would resolve the
symlink itself. If the destination already exists and it is not a
symlink with the expected content, crun will return an error.

## r$FLAG mount options

If a `r$FLAG` mount option is specified then the flag `$FLAG` is set
Expand Down
84 changes: 76 additions & 8 deletions src/libcrun/linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,53 @@ append_mode_if_missing (char *data, const char *mode)
return new_data;
}

static int
safe_create_symlink (int rootfsfd, const char *rootfs, size_t rootfs_len, const char *target, const char *destination, libcrun_error_t *err)
{
cleanup_close int parent_dir_fd = -1;
cleanup_free char *buffer = NULL;
char *part;
int ret;

if (is_empty_string (destination))
return crun_make_error (err, 0, "empty destination for symlink `%s`", target);

buffer = xstrdup (destination);
part = dirname (buffer);

parent_dir_fd = crun_safe_create_and_open_ref_at (true, rootfsfd, rootfs, rootfs_len,
part, 0755, err);
if (UNLIKELY (parent_dir_fd < 0))
return crun_make_error (err, errno, "symlink creation");

/* It is safe to reuse the buffer since it was created with xstrdup (destination). */
strcpy (buffer, destination);
part = basename (buffer);

ret = symlinkat (target, parent_dir_fd, part);
if (UNLIKELY (ret < 0))
{
/* If it exists, check if it has the same content, if so just ignore the error. */
if (errno == EEXIST)
{
cleanup_free char *link = NULL;
ssize_t len;

len = safe_readlinkat (parent_dir_fd, part, &link, 0, err);
if (UNLIKELY (len < 0))
return len;

if ((((size_t) len) == strlen (target)) && strncmp (link, target, len) == 0)
return 0;

return crun_make_error (err, 0, "symlink `%s` already exists with a different content", destination);
}
return crun_make_error (err, errno, "symlink creation `%s`", target);
}

return 0;
}

static const char *
get_force_cgroup_v1_annotation (libcrun_container_t *container)
{
Expand Down Expand Up @@ -1990,7 +2037,7 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, con
char *source;
unsigned long flags = 0;
unsigned long extra_flags = 0;
int is_dir = 1;
mode_t src_mode = S_IFDIR;
cleanup_close int copy_from_fd = -1;
cleanup_close int targetfd = -1;
bool mounted = false;
Expand Down Expand Up @@ -2026,20 +2073,39 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, con
{
proc_fd_path_t proc_buf;
const char *path = def->mounts[i]->source;
if (mount_fds->fds[i] >= 0)

/* If copy-symlink is provided, ignore the pre-opened file descriptor since its source was resolved. */
if (mount_fds->fds[i] >= 0 && ! (extra_flags & OPTION_COPY_SYMLINK))
{
get_proc_self_fd_path (proc_buf, mount_fds->fds[i]);
path = proc_buf;
}

is_dir = crun_dir_p (path, false, err);
if (UNLIKELY (is_dir < 0))
return is_dir;
ret = get_file_type (&src_mode, (extra_flags & OPTION_COPY_SYMLINK) ? true : false, path);
if (UNLIKELY (ret < 0))
return crun_make_error (err, errno, "cannot stat `%s`", path);

data = append_mode_if_missing (data, "mode=1755");
}

if (is_sysfs_or_proc)
if (S_ISLNK (src_mode))
{
cleanup_free char *target = NULL;
ssize_t len;

/* If we got here, it means the OPTION_COPY_SYMLINK was provided, so we need to copy the origin
symlink instead of performing the mount operation. */
len = safe_readlinkat (AT_FDCWD, def->mounts[i]->source, &target, 0, err);
if (UNLIKELY (len < 0))
return len;

ret = safe_create_symlink (rootfsfd, rootfs, rootfs_len, target, def->mounts[i]->destination, err);
if (UNLIKELY (ret < 0))
return ret;

mounted = true;
}
else if (is_sysfs_or_proc)
{
/* Enforce sysfs and proc to be mounted on a regular directory. */
ret = openat (rootfsfd, target, O_NOFOLLOW | O_DIRECTORY);
Expand Down Expand Up @@ -2068,6 +2134,8 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, con
}
else
{
bool is_dir = S_ISDIR (src_mode);

/* Make sure any other directory/file is created and take a O_PATH reference to it. */
ret = crun_safe_create_and_open_ref_at (is_dir, rootfsfd, rootfs, rootfs_len, target,
is_dir ? 01755 : 0755, err);
Expand Down Expand Up @@ -2096,7 +2164,7 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, con
source = def->mounts[i]->source ? def->mounts[i]->source : type;

/* Check if there is already a mount for the requested file system. */
if (mount_fds && mount_fds->fds[i] >= 0)
if (! mounted && mount_fds && mount_fds->fds[i] >= 0)
{
cleanup_close int mfd = get_and_reset (&(mount_fds->fds[i]));

Expand Down Expand Up @@ -2147,7 +2215,7 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, con

destfd = safe_openat (rootfsfd, rootfs, rootfs_len, target, O_DIRECTORY, 0, err);
if (UNLIKELY (destfd < 0))
return crun_make_error (err, errno, "open target to write for tmpcopyup");
return crun_error_wrap (err, "open target to write for tmpcopyup");

/* take ownership for the fd. */
tmpfd = get_and_reset (&copy_from_fd);
Expand Down
Loading

1 comment on commit dd8801d

@packit-as-a-service
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podman-next COPR build failed. @containers/packit-build please check.

Please sign in to comment.