Fix GID assignment error in uml_chown() and add support for correct behavior when parent directory has SETGID bit.
Signed-off-by: Marko Petrović <petrovicmarko2...@gmail.com> --- fs/hostfs/hostfs.h | 5 +- fs/hostfs/hostfs_kern.c | 23 +++++-- fs/hostfs/hostfs_user.c | 142 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 156 insertions(+), 14 deletions(-) diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h index 69cb796f6270..9756303fc089 100644 --- a/fs/hostfs/hostfs.h +++ b/fs/hostfs/hostfs.h @@ -37,6 +37,7 @@ * is on, and remove the appropriate bits from attr->ia_mode (attr is a * "struct iattr *"). -BlaisorBlade */ +extern int use_xattr; struct hostfs_timespec { long long tv_sec; long long tv_nsec; @@ -83,11 +84,11 @@ extern int write_file(int fd, unsigned long long *offset, const char *buf, int len); extern int lseek_file(int fd, long long offset, int whence); extern int fsync_file(int fd, int datasync); -extern int file_create(char *name, int mode); +extern int file_create(char *name, int mode, uid_t uid, gid_t gid); extern int set_attr(const char *file, struct hostfs_iattr *attrs, int fd); extern int make_symlink(const char *from, const char *to); extern int unlink_file(const char *file); -extern int do_mkdir(const char *file, int mode); +extern int do_mkdir(const char *file, int mode, uid_t uid, gid_t gid); extern int hostfs_do_rmdir(const char *file); extern int do_mknod(const char *file, int mode, unsigned int major, unsigned int minor); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 28b4f15c19eb..920d211d4e19 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -17,6 +17,7 @@ #include <linux/writeback.h> #include <linux/mount.h> #include <linux/namei.h> +#include <linux/uidgid.h> #include "hostfs.h" #include <init.h> #include <kern.h> @@ -40,6 +41,7 @@ static struct kmem_cache *hostfs_inode_cache; /* Changed in hostfs_args before the kernel starts running */ static char *root_ino = ""; static int append = 0; +int use_xattr; static const struct inode_operations hostfs_iops; static const struct inode_operations hostfs_dir_iops; @@ -50,6 +52,7 @@ static int __init hostfs_args(char *options, int *add) { char *ptr; + use_xattr = 0; ptr = strchr(options, ','); if (ptr != NULL) *ptr++ = '\0'; @@ -64,6 +67,8 @@ static int __init hostfs_args(char *options, int *add) if (*options != '\0') { if (!strcmp(options, "append")) append = 1; + else if (!strcmp(options, "xattrperm")) + use_xattr = 1; else printf("hostfs_args - unsupported option - %s\n", options); } @@ -79,8 +84,10 @@ __uml_setup("hostfs=", hostfs_args, " tree on the host. If this isn't specified, then a user inside UML can\n" " mount anything on the host that's accessible to the user that's running\n" " it.\n" -" The only flag currently supported is 'append', which specifies that all\n" -" files opened by hostfs will be opened in append mode.\n\n" +" The only flags currently supported are 'append', which specifies that\n" +" all files opened by hostfs will be opened in append mode and 'xattrperm'\n" +" which specifies that permissions of files will be stored in extended\n" +" attributes.\n\n" ); #endif @@ -566,6 +573,8 @@ static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode; char *name; int error, fd; + unsigned int currentuid; + unsigned int currentgid; inode = hostfs_iget(dir->i_sb); if (IS_ERR(inode)) { @@ -578,7 +587,9 @@ static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir, if (name == NULL) goto out_put; - fd = file_create(name, mode & 0777); + currentuid = from_kuid(current->cred->user_ns, current->cred->euid); + currentgid = from_kgid(current->cred->user_ns, current->cred->egid); + fd = file_create(name, mode & 0777, currentuid, currentgid); if (fd < 0) error = fd; else @@ -677,10 +688,14 @@ static int hostfs_mkdir(struct mnt_idmap *idmap, struct inode *ino, { char *file; int err; + unsigned int currentuid; + unsigned int currentgid; if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; - err = do_mkdir(file, mode); + currentuid = from_kuid(current->cred->user_ns, current->cred->euid); + currentgid = from_kgid(current->cred->user_ns, current->cred->egid); + err = do_mkdir(file, mode, currentuid, currentgid); __putname(file); return err; } diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index 5ecc4706172b..f2cc667ab0dd 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -15,6 +15,8 @@ #include <sys/types.h> #include <sys/vfs.h> #include <sys/syscall.h> +#include <sys/xattr.h> +#include <sys/mman.h> #include "hostfs.h" #include <utime.h> @@ -38,6 +40,118 @@ static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p) p->min = os_minor(buf->st_rdev); } +static int uml_chown(const char *pathname, unsigned int owner, unsigned int group) +{ + int status; + + if (use_xattr) { + if (owner != -1) { + status = setxattr(pathname, "user.umluid", &owner, + sizeof(unsigned int), 0); + if (status < 0) + return status; + } + if (group != -1) { + status = setxattr(pathname, "user.umlgid", &group, + sizeof(unsigned int), 0); + if (status < 0) + return status; + } + return 0; + } else { + return chown(pathname, owner, group); + } +} + +static int uml_fchown(int fd, unsigned int owner, unsigned int group) +{ + int status; + + if (use_xattr) { + if (owner != -1) { + status = fsetxattr(fd, "user.umluid", &owner, + sizeof(unsigned int), 0); + if (status < 0) + return status; + } + if (group != -1) { + status = fsetxattr(fd, "user.umlgid", &group, + sizeof(unsigned int), 0); + if (status < 0) + return status; + } + return 0; + } else { + return fchown(fd, owner, group); + } +} + +static int uml_chmod(const char *pathname, unsigned int mode) +{ + if (use_xattr) + return setxattr(pathname, "user.umlmode", &mode, + sizeof(unsigned int), 0); + return chmod(pathname, mode); +} + +static int uml_fchmod(int fd, unsigned int mode) +{ + if (use_xattr) + return fsetxattr(fd, "user.umlmode", &mode, + sizeof(unsigned int), 0); + return fchmod(fd, mode); +} + +static void read_permissions(const char *path, struct stat64 *p) +{ + unsigned int mode, uid, gid; + + if (!use_xattr) + return; + if (getxattr(path, "user.umlmode", &mode, sizeof(unsigned int)) != -1) + p->st_mode = mode; + if (getxattr(path, "user.umluid", &uid, sizeof(unsigned int)) != -1) + p->st_uid = uid; + if (getxattr(path, "user.umlgid", &gid, sizeof(unsigned int)) != -1) + p->st_gid = gid; +} + +// Remove double slash from the path +static void fix_path(char *path) +{ + int i = 0; + int skip = 0; + + while (path[i] != '\0') { + if (path[i] == '/' && path[i+1] == '/') + skip = 1; + path[i] = path[i+skip]; + i++; + } +} + +// path is in the form "rootfs//var/abc" +static long is_set_gid(const char *path) +{ + int i = strlen(path) + 1; + char *parent = mmap(NULL, i, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + struct stat64 buf = { 0 }; + + strcpy(parent, path); + i = i - 3; + while (parent[i] != '/') + i--; + parent[i] = '\0'; + fix_path(parent); + + stat64(parent, &buf); + read_permissions(parent, &buf); + munmap(parent, strlen(path) + 1); + if (buf.st_mode & S_ISGID) + return buf.st_gid; + return -1; +} + int stat_file(const char *path, struct hostfs_stat *p, int fd) { struct stat64 buf; @@ -48,6 +162,7 @@ int stat_file(const char *path, struct hostfs_stat *p, int fd) } else if (lstat64(path, &buf) < 0) { return -errno; } + read_permissions(path, &buf); stat64_to_hostfs(&buf, p); return 0; } @@ -181,13 +296,19 @@ void close_dir(void *stream) closedir(stream); } -int file_create(char *name, int mode) +int file_create(char *name, int mode, unsigned int uid, unsigned int gid) { int fd; + long ret; fd = open64(name, O_CREAT | O_RDWR, mode); if (fd < 0) return -errno; + + ret = is_set_gid(name); + if (ret != -1) + gid = ret; + uml_chown(name, uid, gid); return fd; } @@ -199,25 +320,25 @@ int set_attr(const char *file, struct hostfs_iattr *attrs, int fd) if (attrs->ia_valid & HOSTFS_ATTR_MODE) { if (fd >= 0) { - if (fchmod(fd, attrs->ia_mode) != 0) + if (uml_fchmod(fd, attrs->ia_mode) != 0) return -errno; - } else if (chmod(file, attrs->ia_mode) != 0) { + } else if (uml_chmod(file, attrs->ia_mode) != 0) { return -errno; } } if (attrs->ia_valid & HOSTFS_ATTR_UID) { if (fd >= 0) { - if (fchown(fd, attrs->ia_uid, -1)) + if (uml_fchown(fd, attrs->ia_uid, -1)) return -errno; - } else if (chown(file, attrs->ia_uid, -1)) { + } else if (uml_chown(file, attrs->ia_uid, -1)) { return -errno; } } if (attrs->ia_valid & HOSTFS_ATTR_GID) { if (fd >= 0) { - if (fchown(fd, -1, attrs->ia_gid)) + if (uml_fchown(fd, -1, attrs->ia_gid)) return -errno; - } else if (chown(file, -1, attrs->ia_gid)) { + } else if (uml_chown(file, -1, attrs->ia_gid)) { return -errno; } } @@ -294,13 +415,18 @@ int unlink_file(const char *file) return 0; } -int do_mkdir(const char *file, int mode) +int do_mkdir(const char *file, int mode, unsigned int uid, unsigned int gid) { int err; + long ret; err = mkdir(file, mode); if (err) return -errno; + ret = is_set_gid(file); + if (ret != -1) + gid = ret; + uml_chown(file, uid, gid); return 0; } -- 2.39.2 _______________________________________________ linux-um mailing list linux-um@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-um