[PATCH v2 2/2] hostfs: store permissions in extended attributes
Marko Petrović
petrovicmarko2006 at gmail.com
Thu Apr 13 19:33:57 PDT 2023
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 at 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
More information about the linux-um
mailing list