diff mbox series

[2/2] hostfs: store permissions in extended attributes

Message ID 20230413223024.11513-3-petrovicmarko2006@gmail.com
State Superseded
Headers show
Series [1/2] Document new xattrperm flag | expand

Commit Message

Marko Petrović April 13, 2023, 10:30 p.m. UTC
Signed-off-by: Marko Petrović <petrovicmarko2006@gmail.com>
---
 fs/hostfs/hostfs.h      |  5 ++-
 fs/hostfs/hostfs_kern.c | 23 ++++++++--
 fs/hostfs/hostfs_user.c | 96 +++++++++++++++++++++++++++++++++++++----
 3 files changed, 110 insertions(+), 14 deletions(-)
diff mbox series

Patch

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..fdbd34b9add6 100644
--- a/fs/hostfs/hostfs_user.c
+++ b/fs/hostfs/hostfs_user.c
@@ -15,6 +15,7 @@ 
 #include <sys/types.h>
 #include <sys/vfs.h>
 #include <sys/syscall.h>
+#include <sys/xattr.h>
 #include "hostfs.h"
 #include <utime.h>
 
@@ -38,6 +39,82 @@  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", &owner,
+							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", &owner,
+						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 hostfs_stat *p)
+{
+	unsigned int mode, uid, gid;
+
+	if (!use_xattr)
+		return;
+	if (getxattr(path, "user.umlmode", &mode, sizeof(unsigned int)) != -1)
+		p->mode = mode;
+	if (getxattr(path, "user.umluid", &uid, sizeof(unsigned int)) != -1)
+		p->uid = uid;
+	if (getxattr(path, "user.umlgid", &gid, sizeof(unsigned int)) != -1)
+		p->gid = gid;
+}
+
 int stat_file(const char *path, struct hostfs_stat *p, int fd)
 {
 	struct stat64 buf;
@@ -49,6 +126,7 @@  int stat_file(const char *path, struct hostfs_stat *p, int fd)
 		return -errno;
 	}
 	stat64_to_hostfs(&buf, p);
+	read_permissions(path, p);
 	return 0;
 }
 
@@ -181,13 +259,14 @@  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;
 
 	fd = open64(name, O_CREAT | O_RDWR, mode);
 	if (fd < 0)
 		return -errno;
+	uml_chown(name, uid, gid);
 	return fd;
 }
 
@@ -199,25 +278,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 +373,14 @@  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;
 
 	err = mkdir(file, mode);
 	if (err)
 		return -errno;
+	uml_chown(file, uid, gid);
 	return 0;
 }