All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Marko Petrović" <petrovicmarko2006@gmail.com>
To: linux-um@lists.infradead.org
Cc: richard@nod.at, anton.ivanov@cambridgegreys.com,
	johannes@sipsolutions.net,
	"Marko Petrović" <petrovicmarko2006@gmail.com>
Subject: [PATCH v2 2/2] hostfs: store permissions in extended attributes
Date: Fri, 14 Apr 2023 04:33:57 +0200	[thread overview]
Message-ID: <20230414023357.63722-1-petrovicmarko2006@gmail.com> (raw)
In-Reply-To: <20230413223024.11513-3-petrovicmarko2006@gmail.com>

Fix GID assignment error in uml_chown() and add support for correct
behavior when parent directory has SETGID bit.

Signed-off-by: Marko Petrović <petrovicmarko2006@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	other threads:[~2023-04-14  2:34 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-13 22:30 Document new xattrperm flag Marko Petrović
2023-04-13 22:30 ` [PATCH 1/2] " Marko Petrović
2023-04-14  7:17   ` Johannes Berg
2023-04-13 22:30 ` [PATCH 2/2] hostfs: store permissions in extended attributes Marko Petrović
2023-04-14  2:33   ` Marko Petrović [this message]
2023-04-14  7:40     ` [PATCH v2 " Johannes Berg
2023-04-14 17:19       ` Marko Petrović
2023-04-18  8:26         ` Johannes Berg
2023-04-25 16:10           ` Marko Petrović
2023-04-14 10:54     ` Richard Weinberger
2023-04-14 17:52       ` Marko Petrović
2023-04-14 17:59         ` Richard Weinberger
2023-04-15 16:48 ` [PATCH v3 " Marko Petrović
2023-04-16 17:24   ` Marko Petrović
2023-04-18  8:31     ` Johannes Berg
2023-04-25 16:35       ` Marko Petrović
2023-04-25 17:11         ` Johannes Berg
2023-08-28 19:48   ` Richard Weinberger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230414023357.63722-1-petrovicmarko2006@gmail.com \
    --to=petrovicmarko2006@gmail.com \
    --cc=anton.ivanov@cambridgegreys.com \
    --cc=johannes@sipsolutions.net \
    --cc=linux-um@lists.infradead.org \
    --cc=richard@nod.at \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.