From patchwork Sat Apr 15 16:48:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marko_Petrovi=C4=87?= X-Patchwork-Id: 1769301 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=CXz3PZaP; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20221208 header.b=bIBLCARH; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4PzK5p69cvz1yXv for ; Sun, 16 Apr 2023 02:49:02 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=U43z4Weo2y/Np3xt3UoQRMptstRnJDvZIzr1RhyR9Xw=; b=CXz3PZaPXDBFhk 3CujhmdO4WmQ+Aup/OSAhfS0+hI3p6cenrgpaXLRXS+GwkKHhL1c51/1GiJZZRJw7l95n/SFgFvFJ 4FWwxHQBv581S6xGezTQwDTOMtiP3mf8o66XyD5JkwvScMCXPqH3QCsnD/Mod+365VEW00hvHZrN0 SH5k0XoifxojdcifdJ8mEPBAgizUjTON5qCzf3KjA8VlI0oQv5iL8BW/LB9zfbifuIwxCrjrj5R13 HdAlPJz3wMjnXfi9GDzmPuHKZfMT4spa37u8kKOuigCcsqr5ofVRLli4RJ457Lsq7fr2PmX6zyaor 0hquvJWUf/0PDPqgjyHQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1pnj55-00CUKs-0k; Sat, 15 Apr 2023 16:48:55 +0000 Received: from mail-ed1-x536.google.com ([2a00:1450:4864:20::536]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1pnj51-00CUK2-1F for linux-um@lists.infradead.org; Sat, 15 Apr 2023 16:48:54 +0000 Received: by mail-ed1-x536.google.com with SMTP id 4fb4d7f45d1cf-50489c6171dso5865690a12.3 for ; Sat, 15 Apr 2023 09:48:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1681577329; x=1684169329; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=sXmYBkrMyrAk67EERC0t5e06ZgKPFoD3TSkoI9tWTvs=; b=bIBLCARH9lC/g8qZycvgybdxaX4b6MDa3SdgDVgYvcnfFXxbJMd49JCwBitOkwWx8b o4s9jJ9nFsw6LSrRcI/ACftwlR9ot13jQVAj4wIanEYcC3VvC80uCMR2wktnQCq6wHFi 3tqSWYP7SBjNWrVz1YvhtysxZT8QGHg+V3NHqHjUApkCTcfVebmX5iOcE5HUfdK6yK7u doxed/HAZS6Y2WWjYIKEBLPNTmk2XTkf0czA+TVc9QklUnYcoT0Eky9V2vFUIsaDKBRi ihT/7kS2cSiadqbkqag3bCfz9eSIlO5DNBMQgl6GHNkObNU3Lj76kfb8wotw6pB7PUCH PGfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681577329; x=1684169329; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=sXmYBkrMyrAk67EERC0t5e06ZgKPFoD3TSkoI9tWTvs=; b=kFZ247y/lXpvcvQB8Gz1jmP+8vCMBsnwaVc3ecpBNzkNWWPV1rqKpkcgLJMkx0aKS3 A/2vpLwl8fPLZdfxg3vW09XQW5/7h+RUJlx6WxJxMPCgI2x5X4QS7y7TZUnjNyMMEoVg eZxmEOQF98m7w35lwSfcs2MJON0B5qoRm3dg2mDIUMo6sjWu70T/1/bu3nC/shqbXXU1 8rPD6ohWAotfbbYXTgsyG0sd0fl+zmpK639yhcUdGFTRQcfYIseDrMPlZEOPiAuOjZqn PuN2mhKcPTqDFbZQ/Hu/bnYEfzI+dRP4KQrn9rbtRiVnn/aqUO1mGeym8cSjc6p9RtEa ZC2g== X-Gm-Message-State: AAQBX9ccxO1Ka/rTkmD+bHhAVlVbab8/mVL1q8GFSjv7FtT3K9K3Cw5W V/mmnXQnGswzgBnL02u85Kt2MnD1Ccib09Di X-Google-Smtp-Source: AKy350YzCSzrHwcgxfzezEUn6XcgZH9Rfg/FLNZtKJfuYhW4OvinyzCrVoaDMaZv4HdDVWMyiGM7+g== X-Received: by 2002:aa7:cd64:0:b0:506:747f:3bf0 with SMTP id ca4-20020aa7cd64000000b00506747f3bf0mr8816121edb.8.1681577328402; Sat, 15 Apr 2023 09:48:48 -0700 (PDT) Received: from laptop-fedora.. ([79.175.90.109]) by smtp.gmail.com with ESMTPSA id sc2-20020a1709078a0200b0093f322187f0sm3974270ejc.189.2023.04.15.09.48.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Apr 2023 09:48:47 -0700 (PDT) From: =?utf-8?q?Marko_Petrovi=C4=87?= To: linux-um@lists.infradead.org Cc: richard@nod.at, anton.ivanov@cambridgegreys.com, johannes@sipsolutions.net, =?utf-8?q?Marko_Petrovi=C4=87?= Subject: [PATCH v3 2/2] hostfs: store permissions in extended attributes Date: Sat, 15 Apr 2023 18:48:21 +0200 Message-Id: <20230415164821.166606-1-petrovicmarko2006@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230413223024.11513-1-petrovicmarko2006@gmail.com> References: <20230413223024.11513-1-petrovicmarko2006@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230415_094851_429643_33D2F195 X-CRM114-Status: GOOD ( 27.42 ) X-Spam-Score: 0.1 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: 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. The support for per-mountpoint xattrperm/noxattrperm is added. Content analysis details: (0.1 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2a00:1450:4864:20:0:0:0:536 listed in] [list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [petrovicmarko2006[at]gmail.com] 0.2 FREEMAIL_ENVFROM_END_DIGIT Envelope-from freemail username ends in digit [petrovicmarko2006[at]gmail.com] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org 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. 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ć --- 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 #include #include +#include #include "hostfs.h" #include #include @@ -28,6 +29,11 @@ struct hostfs_inode_info { struct mutex open_mutex; }; +struct hostfs_fs_info { + char host_root_path[PATH_MAX+1]; + int use_xattr; +}; + 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" ); #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); + 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); + 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); __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); + 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); + + 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); 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 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 #include #include +#include #include #include #include #include #include +#include +#include #include "hostfs.h" #include +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; + /* 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)); + + 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)); + 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)); + 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--; + } + if (setxattr(pathname, "user.umlmode", mode_string, + sizeof(mode_string), 0)) + 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; }