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

Reply via email to