[PATCH v3 2/2] hostfs: store permissions in extended attributes
Richard Weinberger
richard at nod.at
Mon Aug 28 12:48:53 PDT 2023
----- Ursprüngliche Mail -----
> Von: "Marko Petrović" <petrovicmarko2006 at gmail.com>
> An: "linux-um" <linux-um at lists.infradead.org>
> CC: "richard" <richard at nod.at>, "anton ivanov" <anton.ivanov at cambridgegreys.com>, "Johannes Berg"
> <johannes at sipsolutions.net>, "Marko Petrović" <petrovicmarko2006 at gmail.com>
> Gesendet: Samstag, 15. April 2023 18:48:21
> Betreff: [PATCH v3 2/2] hostfs: store permissions in extended attributes
> This patch adds support for xattrperm hostfs kernel command line option
> which enables the use of extended attributes for storing permissions by
> default on all mounted hostfs filesystems.
Sorry for the delay. Just unearthed this patch from patchwork.
> The support for per-mountpoint xattrperm/noxattrperm is added.
>
> 'struct super_block -> s_fs_info' now points to 'struct hostfs_fs_info'.
> All code that relied on s_fs_info pointing to a string is changed to use
> the same string stored inside 'struct hostfs_fs_info'. This allows easy
> extensions of the super_block data in the future for storing mount
> options.
>
> Function hostfs_parse_options() is added for parsing the string of
> mount options and storing them in 'struct hostfs_fs_info'.
>
> When xattrperm is enabled, filesystem stores permissions in a
> human-readable string in attributes user.umlmode (for mode) and
> user.umlcred (for uid and gid).
>
> Signed-off-by: Marko Petrović <petrovicmarko2006 at gmail.com>
> ---
> fs/hostfs/hostfs.h | 13 ++-
> fs/hostfs/hostfs_kern.c | 129 +++++++++++++++++----
> fs/hostfs/hostfs_user.c | 242 ++++++++++++++++++++++++++++++++++++----
> 3 files changed, 340 insertions(+), 44 deletions(-)
>
> diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h
> index 69cb796f6270..03f773b7c423 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;
> @@ -67,7 +68,8 @@ struct hostfs_stat {
> unsigned int min;
> };
>
> -extern int stat_file(const char *path, struct hostfs_stat *p, int fd);
> +extern int stat_file(const char *path, struct hostfs_stat *p, int fd,
> + int mnt_use_xattr);
> extern int access_file(char *path, int r, int w, int x);
> extern int open_file(char *path, int r, int w, int append);
> extern void *open_dir(char *path, int *err_out);
> @@ -83,11 +85,14 @@ 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 set_attr(const char *file, struct hostfs_iattr *attrs, int fd);
> +extern int file_create(char *name, int mode, uid_t uid, gid_t gid,
> + int mnt_use_xattr);
> +extern int set_attr(const char *file, struct hostfs_iattr *attrs, int fd,
> + int mnt_use_xattr);
> 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,
> + int mnt_use_xattr);
> 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..a2afe70abf14 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>
> @@ -28,6 +29,11 @@ struct hostfs_inode_info {
> struct mutex open_mutex;
> };
>
> +struct hostfs_fs_info {
> + char host_root_path[PATH_MAX+1];
Why +1? AFAIK PATH_MAX includes space for the NUL byte.
> + int use_xattr;
This is a very generic name.
Feel also free to use bool instead of int.
> +};
> +
> static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
> {
> return list_entry(inode, struct hostfs_inode_info, vfs_inode);
> @@ -64,6 +70,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,18 +87,22 @@ __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"
Please mention that they are stored on xattrs on the host side.
> );
> #endif
>
> static char *__dentry_name(struct dentry *dentry, char *name)
> {
> char *p = dentry_path_raw(dentry, name, PATH_MAX);
> + struct hostfs_fs_info *sb_data;
> char *root;
> size_t len;
>
> - root = dentry->d_sb->s_fs_info;
> + sb_data = dentry->d_sb->s_fs_info;
> + root = sb_data->host_root_path;
> len = strlen(root);
> if (IS_ERR(p)) {
> __putname(name);
> @@ -203,8 +215,10 @@ static int hostfs_statfs(struct dentry *dentry, struct
> kstatfs *sf)
> long long f_bavail;
> long long f_files;
> long long f_ffree;
> + struct hostfs_fs_info *sb_data;
>
> - err = do_statfs(dentry->d_sb->s_fs_info,
> + sb_data = dentry->d_sb->s_fs_info;
> + err = do_statfs(sb_data->host_root_path,
> &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files,
> &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid),
> &sf->f_namelen);
> @@ -250,15 +264,21 @@ static void hostfs_free_inode(struct inode *inode)
>
> static int hostfs_show_options(struct seq_file *seq, struct dentry *root)
> {
> - const char *root_path = root->d_sb->s_fs_info;
> + struct hostfs_fs_info *sb_data = root->d_sb->s_fs_info;
> size_t offset = strlen(root_ino) + 1;
>
> - if (strlen(root_path) > offset)
> - seq_show_option(seq, root_path + offset, NULL);
> + if (strlen(sb_data->host_root_path) > offset)
> + seq_show_option(seq, sb_data->host_root_path + offset, NULL);
>
> if (append)
> seq_puts(seq, ",append");
>
> + if (sb_data->use_xattr == 0)
> + seq_puts(seq, ",noxattrperm");
> +
> + if (sb_data->use_xattr == 1)
> + seq_puts(seq, ",xattrperm");
> +
> return 0;
> }
>
> @@ -516,7 +536,8 @@ static int read_name(struct inode *ino, char *name)
> {
> dev_t rdev;
> struct hostfs_stat st;
> - int err = stat_file(name, &st, -1);
> + struct hostfs_fs_info *sb_data = ino->i_sb->s_fs_info;
> + int err = stat_file(name, &st, -1, sb_data->use_xattr);
> if (err)
> return err;
>
> @@ -564,9 +585,13 @@ static int hostfs_create(struct mnt_idmap *idmap, struct
> inode *dir,
> struct dentry *dentry, umode_t mode, bool excl)
> {
> struct inode *inode;
> + struct hostfs_fs_info *sb_data;
> char *name;
> int error, fd;
> + unsigned int currentuid;
> + unsigned int currentgid;
>
> + sb_data = dentry->d_sb->s_fs_info;
> inode = hostfs_iget(dir->i_sb);
> if (IS_ERR(inode)) {
> error = PTR_ERR(inode);
> @@ -578,7 +603,10 @@ 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);
I think we should better use filesystem instead of the effective id.
These days they're in most cases the same, but applications could still use setfsuid() and friends.
> + fd = file_create(name, mode & 0777, currentuid, currentgid,
> + sb_data->use_xattr);
> if (fd < 0)
> error = fd;
> else
> @@ -675,12 +703,18 @@ static int hostfs_symlink(struct mnt_idmap *idmap, struct
> inode *ino,
> static int hostfs_mkdir(struct mnt_idmap *idmap, struct inode *ino,
> struct dentry *dentry, umode_t mode)
> {
> + struct hostfs_fs_info *sb_data;
> char *file;
> int err;
> + unsigned int currentuid;
> + unsigned int currentgid;
>
> + sb_data = dentry->d_sb->s_fs_info;
> 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);
Same.
Also make this a helper function, please.
> + err = do_mkdir(file, mode, currentuid, currentgid, sb_data->use_xattr);
> __putname(file);
> return err;
> }
> @@ -796,10 +830,12 @@ static int hostfs_setattr(struct mnt_idmap *idmap,
> {
> struct inode *inode = d_inode(dentry);
> struct hostfs_iattr attrs;
> + struct hostfs_fs_info *sb_data;
> char *name;
> int err;
>
> int fd = HOSTFS_I(inode)->fd;
> + sb_data = dentry->d_sb->s_fs_info;
>
> err = setattr_prepare(&nop_mnt_idmap, dentry, attr);
> if (err)
> @@ -849,7 +885,7 @@ static int hostfs_setattr(struct mnt_idmap *idmap,
> name = dentry_name(dentry);
> if (name == NULL)
> return -ENOMEM;
> - err = set_attr(name, &attrs, fd);
> + err = set_attr(name, &attrs, fd, sb_data->use_xattr);
Instead of passing use_xattr to very function just pass sb_data itself
as first parameter. Maybe it is useful later for passing other filesystem
context stuff.
> __putname(name);
> if (err)
> return err;
> @@ -915,10 +951,58 @@ static const struct inode_operations hostfs_link_iops = {
> .get_link = hostfs_get_link,
> };
>
> +static int hostfs_parse_options(struct hostfs_fs_info *sb_data, const char *d)
> +{
> + int err = 0, unknown = 0;
> + char *ptr, *options, *string;
> +
> + snprintf(sb_data->host_root_path, PATH_MAX, "%s/", root_ino);
Why is host_root_path a static array? Just use kasprintf() or such.
> + sb_data->use_xattr = -1;
> + if (d == NULL)
> + return 0;
> +
> + string = kmalloc(strlen(d) + 1, GFP_KERNEL);
> + if (string == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + options = string;
> + strcpy(options, d);
Why not strdup()?
> +
> + while (options) {
> + ptr = strchr(options, ',');
> + if (ptr != NULL)
> + *ptr++ = '\0';
> + if (*options != '\0') {
> + if (!strcmp(options, "noxattrperm")) {
> + sb_data->use_xattr = 0;
> + goto _break;
> + }
> + if (!strcmp(options, "xattrperm")) {
> + sb_data->use_xattr = 1;
> + goto _break;
> + }
> + if (!unknown) {
> + unknown = 1;
> + strcat(sb_data->host_root_path, options);
> + goto _break;
> + }
> + pr_warn("hostfs: unsupported mount option\n");
> + err = -EINVAL;
> + goto out;
> + }
> +_break:
> + options = ptr;
> + }
> +
> +out:
> + kfree(string);
> + return err;
> +}
> static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
> {
> struct inode *root_inode;
> - char *host_root_path, *req_root = d;
> + struct hostfs_fs_info *sb_data;
> int err;
>
> sb->s_blocksize = 1024;
> @@ -931,26 +1015,26 @@ static int hostfs_fill_sb_common(struct super_block *sb,
> void *d, int silent)
> if (err)
> goto out;
>
> - /* NULL is printed as '(null)' by printf(): avoid that. */
> - if (req_root == NULL)
> - req_root = "";
> -
> err = -ENOMEM;
> - sb->s_fs_info = host_root_path =
> - kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root);
> - if (host_root_path == NULL)
> + sb_data = kmalloc(sizeof(struct hostfs_fs_info), GFP_KERNEL);
> + if (sb_data == NULL)
> + goto out;
> +
> + err = hostfs_parse_options(sb_data, d);
> + if (err != 0)
> goto out;
> + sb->s_fs_info = sb_data;
>
> root_inode = new_inode(sb);
> if (!root_inode)
> goto out;
>
> - err = read_name(root_inode, host_root_path);
> + err = read_name(root_inode, sb_data->host_root_path);
Also for such functions, just pass sb_data.
> if (err)
> goto out_put;
>
> if (S_ISLNK(root_inode->i_mode)) {
> - char *name = follow_link(host_root_path);
> + char *name = follow_link(sb_data->host_root_path);
> if (IS_ERR(name)) {
> err = PTR_ERR(name);
> goto out_put;
> @@ -1001,6 +1085,9 @@ static int __init init_hostfs(void)
> hostfs_inode_cache = KMEM_CACHE(hostfs_inode_info, 0);
> if (!hostfs_inode_cache)
> return -ENOMEM;
> + #ifdef MODULE
> + use_xattr = 0;
> + #endif
Why is this needed?
> return register_filesystem(&hostfs_type);
> }
>
> diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c
> index 5ecc4706172b..f5dd524cd153 100644
> --- a/fs/hostfs/hostfs_user.c
> +++ b/fs/hostfs/hostfs_user.c
> @@ -10,14 +10,19 @@
> #include <errno.h>
> #include <fcntl.h>
> #include <string.h>
> +#include <stdlib.h>
> #include <sys/stat.h>
> #include <sys/time.h>
> #include <sys/types.h>
> #include <sys/vfs.h>
> #include <sys/syscall.h>
> +#include <sys/xattr.h>
> +#include <um_malloc.h>
> #include "hostfs.h"
> #include <utime.h>
>
> +int use_xattr;
> +
> static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p)
> {
> p->ino = buf->st_ino;
> @@ -38,7 +43,184 @@ static void stat64_to_hostfs(const struct stat64 *buf,
> struct hostfs_stat *p)
> p->min = os_minor(buf->st_rdev);
> }
>
> -int stat_file(const char *path, struct hostfs_stat *p, int fd)
> +static int uml_chown(const char *pathname, uid_t owner, gid_t group,
> + int mnt_use_xattr)
> +{
> + int xattr = (mnt_use_xattr == -1) ? use_xattr : mnt_use_xattr;
At this level you should not distinguish between a flag set at mount time or globally.
Use one variable and set it at mount time.
e.g. the global module flag is the default and a mount option can override it.
> + /* 10 digits uid, 10 digits gid, null byte and ',' */
> + char umlcred[22];
> + /* max_gid=4294967295 - 10 digits + null byte */
> + char gid[11];
> + int i = 0;
> +
> + if (xattr) {
> + memset(umlcred, 0, sizeof(umlcred));
> + getxattr(pathname, "user.umlcred", umlcred, sizeof(umlcred));
You need to check the return code. What if the xattr is not there?
> + while (umlcred[i] != ',' && umlcred[i] != '\0')
> + i++;
What if the xattr contains garbage and no NULL terminator?
...then you loop here forever.
> + umlcred[i] = '\0';
> +
> + if (group == -1)
> + strncpy(gid, ¨cred[i+1], sizeof(gid));
> + else
> + snprintf(gid, sizeof(gid), "%u", group);
> + if (owner != -1)
> + snprintf(umlcred, sizeof(gid), "%u", owner);
> +
> + strcat(umlcred, ",");
> + strncat(umlcred, gid, sizeof(gid-1));
I'm really not a fan of such old school C string hackery.
Can't you read using ssscanf() and produce the new string with snprintf() or such?
> + if (setxattr(pathname, "user.umlcred", umlcred,
> + strlen(umlcred)+1, 0))
> + return -errno;
> +
> + return 0;
> + }
> + if (chown(pathname, owner, group))
> + return -errno;
> + return 0;
> +}
> +
> +static int uml_fchown(int fd, uid_t owner, gid_t group, int mnt_use_xattr)
> +{
> + int xattr = (mnt_use_xattr == -1) ? use_xattr : mnt_use_xattr;
> + /* 10 digits uid, 10 digits gid, null byte and ',' */
> + char umlcred[22];
> + /* max_gid=4294967295 - 10 digits + null byte */
> + char gid[11];
> + int i = 0;
> +
> + if (xattr) {
> + memset(umlcred, 0, sizeof(umlcred));
> + fgetxattr(fd, "user.umlcred", umlcred, sizeof(umlcred));
> +
> + while (umlcred[i] != ',' && umlcred[i] != '\0')
> + i++;
> + umlcred[i] = '\0';
> +
> + if (group == -1)
> + strncpy(gid, ¨cred[i+1], sizeof(gid));
> + else
> + snprintf(gid, sizeof(gid), "%u", group);
> + if (owner != -1)
> + snprintf(umlcred, sizeof(gid), "%u", owner);
> +
> + strcat(umlcred, ",");
> + strncat(umlcred, gid, sizeof(gid-1));
Please add a helper function for common code.
> + if (fsetxattr(fd, "user.umlcred", umlcred, strlen(umlcred)+1, 0))
> + return -errno;
> +
> + return 0;
> + }
> + if (fchown(fd, owner, group))
> + return -errno;
> + return 0;
> +}
> +
> +static int uml_chmod(const char *pathname, unsigned int mode, int
> mnt_use_xattr)
> +{
> + int xattr = (mnt_use_xattr == -1) ? use_xattr : mnt_use_xattr;
> + int i;
> + char mode_string[7]; /* mode is 16-bit -> max 6 digits in octal */
> +
> + if (xattr) {
> + strcpy(mode_string, "000000");
> + i = sizeof(mode_string) - 1;
> + while (mode > 0) {
> + mode_string[i] = (mode % 8) + '0';
> + mode = mode / 8;
> + i--;
> + }
kstrtoint()?
> + if (setxattr(pathname, "user.umlmode", mode_string,
> + sizeof(mode_string), 0))
So we have user.umlcred and user.umlmode xattrs on the host side.
Why not just one xattr?
> + return -errno;
> + return 0;
> + }
> + if (chmod(pathname, mode))
> + return -errno;
> + return 0;
> +}
> +
> +static int uml_fchmod(int fd, unsigned int mode, int mnt_use_xattr)
> +{
> + int xattr = (mnt_use_xattr == -1) ? use_xattr : mnt_use_xattr;
> + int i;
> + char mode_string[7]; /* mode is 16-bit -> max 6 digits in octal */
> +
> + if (xattr) {
> + strcpy(mode_string, "000000");
> + i = sizeof(mode_string) - 1;
> + while (mode > 0) {
> + mode_string[i] = (mode % 8) + '0';
> + mode = mode / 8;
> + i--;
> + }
> + if (fsetxattr(fd, "user.umlmode", mode_string,
> + sizeof(mode_string), 0))
> + return -errno;
> + return 0;
> + }
> + if (fchmod(fd, mode))
> + return -errno;
> + return 0;
> +}
> +
> +static void read_permissions(const char *path, struct stat64 *p,
> + int mnt_use_xattr)
> +{
> + unsigned int mode = 0, i;
> + char mode_string[7], umlcred[22];
> + int xattr = (mnt_use_xattr == -1) ? use_xattr : mnt_use_xattr;
> +
> + if (!xattr)
> + return;
> +
> + if (getxattr(path, "user.umlmode", mode_string, sizeof(mode_string)) != -1) {
> + i = 0;
> + while (mode_string[i] != '\0') {
> + mode *= 8;
> + mode += mode_string[i] - '0';
> + i++;
> + }
> + p->st_mode = mode;
> + }
> +
> + if (getxattr(path, "user.umlcred", umlcred, sizeof(umlcred)) != -1) {
> + i = 0;
> + while (umlcred[i] != ',')
> + i++;
> + umlcred[i] = '\0';
> +
> + if (strlen(umlcred) > 0)
> + p->st_uid = atoi(umlcred);
> + if (strlen(¨cred[i+1]) > 0)
> + p->st_gid = atoi(¨cred[i+1]);
> + }
> +}
> +
> +static long is_set_gid(const char *path, int mnt_use_xattr)
> +{
> + int i = strlen(path) + 1;
> + char *parent = uml_kmalloc(i, UM_GFP_KERNEL);
> + struct stat64 buf = { 0 };
> +
> + if (parent == NULL)
> + return -1;
> + strcpy(parent, path);
> + i = i - 3;
> + while (parent[i] != '/')
> + i--;
> + parent[i] = '\0';
> +
> + stat64(parent, &buf);
> + read_permissions(parent, &buf, mnt_use_xattr);
> + kfree(parent);
> + if (buf.st_mode & S_ISGID)
> + return buf.st_gid;
> + return -1;
> +}
> +
> +int stat_file(const char *path, struct hostfs_stat *p, int fd, int
> mnt_use_xattr)
> {
> struct stat64 buf;
>
> @@ -48,6 +230,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, mnt_use_xattr);
> stat64_to_hostfs(&buf, p);
> return 0;
> }
> @@ -181,44 +364,60 @@ void close_dir(void *stream)
> closedir(stream);
> }
>
> -int file_create(char *name, int mode)
> +int file_create(char *name, int mode, uid_t uid, gid_t gid, int mnt_use_xattr)
> {
> int fd;
> + long ret;
>
> fd = open64(name, O_CREAT | O_RDWR, mode);
> if (fd < 0)
> return -errno;
> +
> + ret = is_set_gid(name, mnt_use_xattr);
> + if (ret != -1)
> + gid = ret;
> + uml_chown(name, uid, gid, mnt_use_xattr);
> return fd;
> }
>
> -int set_attr(const char *file, struct hostfs_iattr *attrs, int fd)
> +int set_attr(const char *file, struct hostfs_iattr *attrs, int fd,
> + int mnt_use_xattr)
> {
> struct hostfs_stat st;
> struct timeval times[2];
> - int err, ma;
> + int err, ma, ret;
>
> if (attrs->ia_valid & HOSTFS_ATTR_MODE) {
> if (fd >= 0) {
> - if (fchmod(fd, attrs->ia_mode) != 0)
> - return -errno;
> - } else if (chmod(file, attrs->ia_mode) != 0) {
> - return -errno;
> + ret = uml_fchmod(fd, attrs->ia_mode, mnt_use_xattr);
> + if (ret != 0)
> + return ret;
> + } else {
> + ret = uml_chmod(file, attrs->ia_mode, mnt_use_xattr);
> + if (ret != 0)
> + return ret;
> }
> }
> if (attrs->ia_valid & HOSTFS_ATTR_UID) {
> if (fd >= 0) {
> - if (fchown(fd, attrs->ia_uid, -1))
> - return -errno;
> - } else if (chown(file, attrs->ia_uid, -1)) {
> - return -errno;
> + ret = uml_fchown(fd, attrs->ia_uid, -1, mnt_use_xattr);
> + if (ret != 0)
> + return ret;
> + } else {
> + ret = uml_chown(file, attrs->ia_uid, -1, mnt_use_xattr);
> + if (ret != 0)
> + return ret;
> }
> }
> if (attrs->ia_valid & HOSTFS_ATTR_GID) {
> if (fd >= 0) {
> - if (fchown(fd, -1, attrs->ia_gid))
> - return -errno;
> - } else if (chown(file, -1, attrs->ia_gid)) {
> - return -errno;
> + ret = uml_fchown(fd, -1, attrs->ia_gid, mnt_use_xattr);
> + if (ret != 0)
> + return ret;
> + } else {
> + ret = uml_chown(file, -1, attrs->ia_gid, mnt_use_xattr);
> + if (ret != 0)
> + return ret;
> }
> }
> if (attrs->ia_valid & HOSTFS_ATTR_SIZE) {
> @@ -237,7 +436,7 @@ int set_attr(const char *file, struct hostfs_iattr *attrs,
> int fd)
> */
> ma = (HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET);
> if (attrs->ia_valid & ma) {
> - err = stat_file(file, &st, fd);
> + err = stat_file(file, &st, fd, mnt_use_xattr);
> if (err != 0)
> return err;
>
> @@ -265,7 +464,7 @@ int set_attr(const char *file, struct hostfs_iattr *attrs,
> int fd)
>
> /* Note: ctime is not handled */
> if (attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)) {
> - err = stat_file(file, &st, fd);
> + err = stat_file(file, &st, fd, mnt_use_xattr);
> attrs->ia_atime = st.atime;
> attrs->ia_mtime = st.mtime;
> if (err != 0)
> @@ -294,13 +493,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, uid_t uid, gid_t gid, int
> mnt_use_xattr)
> {
> int err;
> + long ret;
>
> err = mkdir(file, mode);
> if (err)
> return -errno;
> + ret = is_set_gid(file, mnt_use_xattr);
> + if (ret != -1)
> + gid = ret;
> + uml_chown(file, uid, gid, mnt_use_xattr);
> return 0;
> }
>
> --
> 2.39.2
More information about the linux-um
mailing list