Add chroot functionality for systemcalls that can operate on a file using relative directory file descriptor.
Signed-off-by: M. Mohan Kumar <mo...@in.ibm.com> --- hw/9pfs/virtio-9p-local.c | 156 ++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 139 insertions(+), 17 deletions(-) diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index 1670e33..0157eb8 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -22,6 +22,7 @@ #include <sys/socket.h> #include <sys/un.h> #include <attr/xattr.h> +#include <libgen.h> /* Helper routine to fill V9fsFileObjectRequest structure */ static int fill_fileobjectrequest(V9fsFileObjectRequest *request, @@ -70,14 +71,41 @@ static int passthrough_request(FsContext *fs_ctx, const char *old_path, return retval; } +/* + * Returns file descriptor of dirname(path) + * This fd can be used by *at functions + */ +static int get_dirfd(FsContext *fs_ctx, const char *path) +{ + V9fsFileObjectRequest request; + int fd; + char *dpath = qemu_strdup(path); + + fd = fill_fileobjectrequest(&request, dirname(dpath), NULL); + if (fd < 0) { + qemu_free(dpath); + errno = -fd; + return -1; + } + request.data.type = T_OPEN; + fd = v9fs_request(fs_ctx, &request); + qemu_free(dpath); + if (fd < 0) { + errno = -fd; + fd = -1; + } + return fd; +} + static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf) { int err; - err = lstat(rpath(fs_ctx, path), stbuf); - if (err) { - return err; - } + if (fs_ctx->fs_sm == SM_MAPPED) { + err = lstat(rpath(fs_ctx, path), stbuf); + if (err) { + return err; + } /* Actual credentials are part of extended attrs */ uid_t tmp_uid; gid_t tmp_gid; @@ -99,6 +127,27 @@ static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf) sizeof(dev_t)) > 0) { stbuf->st_rdev = tmp_dev; } + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int pfd, serrno = 0; + char *tmp_path; + + pfd = get_dirfd(fs_ctx, path); + if (pfd < 0) { + return -1; + } + tmp_path = qemu_strdup(path); + err = fstatat(pfd, basename(tmp_path), stbuf, AT_SYMLINK_NOFOLLOW); + if (err < 0) { + serrno = errno; + } + close(pfd); + qemu_free(tmp_path); + errno = serrno; + } else { + err = lstat(rpath(fs_ctx, path), stbuf); + if (err) { + return err; + } } return err; } @@ -163,9 +212,23 @@ static ssize_t local_readlink(FsContext *fs_ctx, const char *path, } while (tsize == -1 && errno == EINTR); close(fd); return tsize; - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if (fs_ctx->fs_sm == SM_NONE) { tsize = readlink(rpath(fs_ctx, path), buf, bufsz); + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int pfd, serrno = 0; + char *tmp_path; + pfd = get_dirfd(fs_ctx, path); + if (pfd < 0) { + return -1; + } + tmp_path = qemu_strdup(path); + tsize = readlinkat(pfd, basename(tmp_path), buf, bufsz); + if (tsize < 0) { + serrno = errno; + } + close(pfd); + qemu_free(tmp_path); + errno = serrno; } return tsize; } @@ -257,8 +320,23 @@ static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp) { if (fs_ctx->fs_sm == SM_MAPPED) { return local_set_xattr(rpath(fs_ctx, path), credp); - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int pfd, err, serrno = 0; + char *tmp_path; + pfd = get_dirfd(fs_ctx, path); + if (pfd < 0) { + return -1; + } + tmp_path = qemu_strdup(path); + err = fchmodat(pfd, basename(tmp_path), credp->fc_mode, 0); + if (err == -1) { + serrno = errno; + } + qemu_free(tmp_path); + close(pfd); + errno = serrno; + return err; + } else if (fs_ctx->fs_sm == SM_NONE) { return chmod(rpath(fs_ctx, path), credp->fc_mode); } return -1; @@ -506,7 +584,21 @@ static int local_link(FsContext *fs_ctx, const char *oldpath, static int local_truncate(FsContext *ctx, const char *path, off_t size) { - return truncate(rpath(ctx, path), size); + if (ctx->fs_sm == SM_PASSTHROUGH) { + int fd, retval, serrno; + fd = passthrough_request(ctx, NULL, path, O_RDWR, NULL, T_OPEN); + if (fd < 0) { + errno = -fd; + return -1; + } + retval = ftruncate(fd, size); + serrno = errno; + close(fd); + errno = serrno; + return retval; + } else { + return truncate(rpath(ctx, path), size); + } } static int local_rename(FsContext *ctx, const char *oldpath, @@ -536,22 +628,52 @@ static int local_rename(FsContext *ctx, const char *oldpath, static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp) { - if ((credp->fc_uid == -1 && credp->fc_gid == -1) || - (fs_ctx->fs_sm == SM_PASSTHROUGH)) { + if (fs_ctx->fs_sm != SM_PASSTHROUGH && + (credp->fc_uid == -1 && credp->fc_gid == -1)) { + return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); + } else if (fs_ctx->fs_sm == SM_NONE) { return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); } else if (fs_ctx->fs_sm == SM_MAPPED) { return local_set_xattr(rpath(fs_ctx, path), credp); - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { - return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); + } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int pfd, err, serrno = 0; + char *old_path; + pfd = get_dirfd(fs_ctx, path); + if (pfd < 0) { + return -1; + } + old_path = qemu_strdup(path); + err = fchownat(pfd, basename(old_path), credp->fc_uid, credp->fc_gid, + AT_SYMLINK_NOFOLLOW); + if (err == -1) { + serrno = errno; + } + qemu_free(old_path); + close(pfd); + errno = serrno; + return err; } return -1; } -static int local_utimensat(FsContext *s, const char *path, - const struct timespec *buf) +static int local_utimensat(FsContext *fs_ctx, const char *path, + const struct timespec *buf) { - return qemu_utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW); + if (fs_ctx->fs_sm == SM_PASSTHROUGH) { + int fd, retval; + fd = passthrough_request(fs_ctx, NULL, path, + O_RDONLY | O_NONBLOCK | O_NOFOLLOW, NULL, T_OPEN); + if (fd < 0) { + errno = -fd; + return -1; + } + retval = futimens(fd, buf); + close(fd); + return retval; + } else { + return utimensat(AT_FDCWD, rpath(fs_ctx, path), buf, + AT_SYMLINK_NOFOLLOW); + } } static int local_remove(FsContext *ctx, const char *path) -- 1.7.3.4