mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-23 23:50:51 +07:00
5fa3ea047a
Signed-off-by: AuxXxilium <info@auxxxilium.tech>
357 lines
8.0 KiB
C
357 lines
8.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2014-2019 Junjiro R. Okajima
|
|
*
|
|
* This program, aufs is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* handling xattr functions
|
|
*/
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/posix_acl_xattr.h>
|
|
#include <linux/xattr.h>
|
|
#include "aufs.h"
|
|
|
|
static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags)
|
|
{
|
|
if (!ignore_flags)
|
|
goto out;
|
|
switch (err) {
|
|
case -ENOMEM:
|
|
case -EDQUOT:
|
|
goto out;
|
|
}
|
|
|
|
if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) {
|
|
err = 0;
|
|
goto out;
|
|
}
|
|
|
|
#define cmp(brattr, prefix) do { \
|
|
if (!strncmp(name, XATTR_##prefix##_PREFIX, \
|
|
XATTR_##prefix##_PREFIX_LEN)) { \
|
|
if (ignore_flags & AuBrAttr_ICEX_##brattr) \
|
|
err = 0; \
|
|
goto out; \
|
|
} \
|
|
} while (0)
|
|
|
|
cmp(SEC, SECURITY);
|
|
cmp(SYS, SYSTEM);
|
|
cmp(TR, TRUSTED);
|
|
cmp(USR, USER);
|
|
#undef cmp
|
|
|
|
if (ignore_flags & AuBrAttr_ICEX_OTH)
|
|
err = 0;
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1;
|
|
|
|
static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src,
|
|
char *name, char **buf, unsigned int ignore_flags,
|
|
unsigned int verbose)
|
|
{
|
|
int err;
|
|
ssize_t ssz;
|
|
struct inode *h_idst;
|
|
|
|
ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS);
|
|
err = ssz;
|
|
if (unlikely(err <= 0)) {
|
|
if (err == -ENODATA
|
|
|| (err == -EOPNOTSUPP
|
|
&& ((ignore_flags & au_xattr_out_of_list)
|
|
|| (au_test_nfs_noacl(d_inode(h_src))
|
|
&& (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS)
|
|
|| !strcmp(name,
|
|
XATTR_NAME_POSIX_ACL_DEFAULT))))
|
|
))
|
|
err = 0;
|
|
if (err && (verbose || au_debug_test()))
|
|
pr_err("%s, err %d\n", name, err);
|
|
goto out;
|
|
}
|
|
|
|
/* unlock it temporary */
|
|
h_idst = d_inode(h_dst);
|
|
inode_unlock(h_idst);
|
|
err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0);
|
|
inode_lock_nested(h_idst, AuLsc_I_CHILD2);
|
|
if (unlikely(err)) {
|
|
if (verbose || au_debug_test())
|
|
pr_err("%s, err %d\n", name, err);
|
|
err = au_xattr_ignore(err, name, ignore_flags);
|
|
}
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags,
|
|
unsigned int verbose)
|
|
{
|
|
int err, unlocked, acl_access, acl_default;
|
|
ssize_t ssz;
|
|
struct inode *h_isrc, *h_idst;
|
|
char *value, *p, *o, *e;
|
|
|
|
/* try stopping to update the source inode while we are referencing */
|
|
/* there should not be the parent-child relationship between them */
|
|
h_isrc = d_inode(h_src);
|
|
h_idst = d_inode(h_dst);
|
|
inode_unlock(h_idst);
|
|
inode_lock_shared_nested(h_isrc, AuLsc_I_CHILD);
|
|
inode_lock_nested(h_idst, AuLsc_I_CHILD2);
|
|
unlocked = 0;
|
|
|
|
/* some filesystems don't list POSIX ACL, for example tmpfs */
|
|
ssz = vfs_listxattr(h_src, NULL, 0);
|
|
err = ssz;
|
|
if (unlikely(err < 0)) {
|
|
AuTraceErr(err);
|
|
if (err == -ENODATA
|
|
|| err == -EOPNOTSUPP)
|
|
err = 0; /* ignore */
|
|
goto out;
|
|
}
|
|
|
|
err = 0;
|
|
p = NULL;
|
|
o = NULL;
|
|
if (ssz) {
|
|
err = -ENOMEM;
|
|
p = kmalloc(ssz, GFP_NOFS);
|
|
o = p;
|
|
if (unlikely(!p))
|
|
goto out;
|
|
err = vfs_listxattr(h_src, p, ssz);
|
|
}
|
|
inode_unlock_shared(h_isrc);
|
|
unlocked = 1;
|
|
AuDbg("err %d, ssz %zd\n", err, ssz);
|
|
if (unlikely(err < 0))
|
|
goto out_free;
|
|
|
|
err = 0;
|
|
e = p + ssz;
|
|
value = NULL;
|
|
acl_access = 0;
|
|
acl_default = 0;
|
|
while (!err && p < e) {
|
|
acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS,
|
|
sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1);
|
|
acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT,
|
|
sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)
|
|
- 1);
|
|
err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags,
|
|
verbose);
|
|
p += strlen(p) + 1;
|
|
}
|
|
AuTraceErr(err);
|
|
ignore_flags |= au_xattr_out_of_list;
|
|
if (!err && !acl_access) {
|
|
err = au_do_cpup_xattr(h_dst, h_src,
|
|
XATTR_NAME_POSIX_ACL_ACCESS, &value,
|
|
ignore_flags, verbose);
|
|
AuTraceErr(err);
|
|
}
|
|
if (!err && !acl_default) {
|
|
err = au_do_cpup_xattr(h_dst, h_src,
|
|
XATTR_NAME_POSIX_ACL_DEFAULT, &value,
|
|
ignore_flags, verbose);
|
|
AuTraceErr(err);
|
|
}
|
|
|
|
au_kfree_try_rcu(value);
|
|
|
|
out_free:
|
|
au_kfree_try_rcu(o);
|
|
out:
|
|
if (!unlocked)
|
|
inode_unlock_shared(h_isrc);
|
|
AuTraceErr(err);
|
|
return err;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static int au_smack_reentering(struct super_block *sb)
|
|
{
|
|
#if IS_ENABLED(CONFIG_SECURITY_SMACK) || IS_ENABLED(CONFIG_SECURITY_SELINUX)
|
|
/*
|
|
* as a part of lookup, smack_d_instantiate() is called, and it calls
|
|
* i_op->getxattr(). ouch.
|
|
*/
|
|
return si_pid_test(sb);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
enum {
|
|
AU_XATTR_LIST,
|
|
AU_XATTR_GET
|
|
};
|
|
|
|
struct au_lgxattr {
|
|
int type;
|
|
union {
|
|
struct {
|
|
char *list;
|
|
size_t size;
|
|
} list;
|
|
struct {
|
|
const char *name;
|
|
void *value;
|
|
size_t size;
|
|
} get;
|
|
} u;
|
|
};
|
|
|
|
static ssize_t au_lgxattr(struct dentry *dentry, struct inode *inode,
|
|
struct au_lgxattr *arg)
|
|
{
|
|
ssize_t err;
|
|
int reenter;
|
|
struct path h_path;
|
|
struct super_block *sb;
|
|
|
|
sb = dentry->d_sb;
|
|
reenter = au_smack_reentering(sb);
|
|
if (!reenter) {
|
|
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
|
|
if (unlikely(err))
|
|
goto out;
|
|
}
|
|
err = au_h_path_getattr(dentry, inode, /*force*/1, &h_path, reenter);
|
|
if (unlikely(err))
|
|
goto out_si;
|
|
if (unlikely(!h_path.dentry))
|
|
/* illegally overlapped or something */
|
|
goto out_di; /* pretending success */
|
|
|
|
/* always topmost entry only */
|
|
switch (arg->type) {
|
|
case AU_XATTR_LIST:
|
|
err = vfs_listxattr(h_path.dentry,
|
|
arg->u.list.list, arg->u.list.size);
|
|
break;
|
|
case AU_XATTR_GET:
|
|
AuDebugOn(d_is_negative(h_path.dentry));
|
|
err = vfs_getxattr(h_path.dentry,
|
|
arg->u.get.name, arg->u.get.value,
|
|
arg->u.get.size);
|
|
break;
|
|
}
|
|
|
|
out_di:
|
|
if (!reenter)
|
|
di_read_unlock(dentry, AuLock_IR);
|
|
out_si:
|
|
if (!reenter)
|
|
si_read_unlock(sb);
|
|
out:
|
|
AuTraceErr(err);
|
|
return err;
|
|
}
|
|
|
|
ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size)
|
|
{
|
|
struct au_lgxattr arg = {
|
|
.type = AU_XATTR_LIST,
|
|
.u.list = {
|
|
.list = list,
|
|
.size = size
|
|
},
|
|
};
|
|
|
|
return au_lgxattr(dentry, /*inode*/NULL, &arg);
|
|
}
|
|
|
|
static ssize_t au_getxattr(struct dentry *dentry, struct inode *inode,
|
|
const char *name, void *value, size_t size)
|
|
{
|
|
struct au_lgxattr arg = {
|
|
.type = AU_XATTR_GET,
|
|
.u.get = {
|
|
.name = name,
|
|
.value = value,
|
|
.size = size
|
|
},
|
|
};
|
|
|
|
return au_lgxattr(dentry, inode, &arg);
|
|
}
|
|
|
|
static int au_setxattr(struct dentry *dentry, struct inode *inode,
|
|
const char *name, const void *value, size_t size,
|
|
int flags)
|
|
{
|
|
struct au_sxattr arg = {
|
|
.type = AU_XATTR_SET,
|
|
.u.set = {
|
|
.name = name,
|
|
.value = value,
|
|
.size = size,
|
|
.flags = flags
|
|
},
|
|
};
|
|
|
|
return au_sxattr(dentry, inode, &arg);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static int au_xattr_get(const struct xattr_handler *handler,
|
|
struct dentry *dentry, struct inode *inode,
|
|
const char *name, void *buffer, size_t size)
|
|
{
|
|
return au_getxattr(dentry, inode, name, buffer, size);
|
|
}
|
|
|
|
static int au_xattr_set(const struct xattr_handler *handler,
|
|
struct dentry *dentry, struct inode *inode,
|
|
const char *name, const void *value, size_t size,
|
|
int flags)
|
|
{
|
|
return au_setxattr(dentry, inode, name, value, size, flags);
|
|
}
|
|
|
|
static const struct xattr_handler au_xattr_handler = {
|
|
.name = "",
|
|
.prefix = "",
|
|
.get = au_xattr_get,
|
|
.set = au_xattr_set
|
|
};
|
|
|
|
static const struct xattr_handler *au_xattr_handlers[] = {
|
|
#ifdef CONFIG_FS_POSIX_ACL
|
|
&posix_acl_access_xattr_handler,
|
|
&posix_acl_default_xattr_handler,
|
|
#endif
|
|
&au_xattr_handler, /* must be last */
|
|
NULL
|
|
};
|
|
|
|
void au_xattr_init(struct super_block *sb)
|
|
{
|
|
sb->s_xattr = au_xattr_handlers;
|
|
}
|