7ba2090ca6
fscrypt support to CephFS! The list of things which don't work with encryption should be fairly short, mostly around the edges: fallocate (not supported well in CephFS to begin with), copy_file_range (requires re-encryption), non-default striping patterns. This was a multi-year effort principally by Jeff Layton with assistance from Xiubo Li, Luís Henriques and others, including several dependant changes in the MDS, netfs helper library and fscrypt framework itself. -----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEydHwtzie9C7TfviiSn/eOAIR84sFAmT4pl4THGlkcnlvbW92 QGdtYWlsLmNvbQAKCRBKf944AhHzi5kzB/4sMgzZyUa3T1vA/G2pPvEkyy1qDxsW y+o4dDMWA9twcrBVpNuGd54wbXpmO/LAekHEdorjayH+f0zf10MsnP1ePz9WB3NG jr7RRujb+Gpd2OFYJXGSEbd3faTg8M2kpGCCrVe7SFNoyu8z9NwFItwWMog5aBjX ODGQrq+kA4ARA6xIqwzF5gP0zr+baT9rWhQdm7Xo9itWdosnbyDLJx1dpEfLuqBX te3SmifDzedn3Gw73hdNo/+ybw0kHARoK+RmXCTsoDDQw+JsoO9KxZF5Q8QcDELq 2woPNp0Hl+Dm4MkzGnPxv56Qj8ZDViS59syXC0CfGRmu4nzF1Rw+0qn5 =/WlE -----END PGP SIGNATURE----- Merge tag 'ceph-for-6.6-rc1' of https://github.com/ceph/ceph-client Pull ceph updates from Ilya Dryomov: "Mixed with some fixes and cleanups, this brings in reasonably complete fscrypt support to CephFS! The list of things which don't work with encryption should be fairly short, mostly around the edges: fallocate (not supported well in CephFS to begin with), copy_file_range (requires re-encryption), non-default striping patterns. This was a multi-year effort principally by Jeff Layton with assistance from Xiubo Li, Luís Henriques and others, including several dependant changes in the MDS, netfs helper library and fscrypt framework itself" * tag 'ceph-for-6.6-rc1' of https://github.com/ceph/ceph-client: (53 commits) ceph: make num_fwd and num_retry to __u32 ceph: make members in struct ceph_mds_request_args_ext a union rbd: use list_for_each_entry() helper libceph: do not include crypto/algapi.h ceph: switch ceph_lookup/atomic_open() to use new fscrypt helper ceph: fix updating i_truncate_pagecache_size for fscrypt ceph: wait for OSD requests' callbacks to finish when unmounting ceph: drop messages from MDS when unmounting ceph: update documentation regarding snapshot naming limitations ceph: prevent snapshot creation in encrypted locked directories ceph: add support for encrypted snapshot names ceph: invalidate pages when doing direct/sync writes ceph: plumb in decryption during reads ceph: add encryption support to writepage and writepages ceph: add read/modify/write to ceph_sync_write ceph: align data in pages in ceph_sync_write ceph: don't use special DIO path for encrypted inodes ceph: add truncate size handling support for fscrypt ceph: add object version support for sync read libceph: allow ceph_osdc_new_request to accept a multi-op read ...
264 lines
5.7 KiB
C
264 lines
5.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* linux/fs/ceph/acl.c
|
|
*
|
|
* Copyright (C) 2013 Guangliang Zhao, <lucienchao@gmail.com>
|
|
*/
|
|
|
|
#include <linux/ceph/ceph_debug.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/string.h>
|
|
#include <linux/xattr.h>
|
|
#include <linux/posix_acl_xattr.h>
|
|
#include <linux/posix_acl.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "super.h"
|
|
|
|
static inline void ceph_set_cached_acl(struct inode *inode,
|
|
int type, struct posix_acl *acl)
|
|
{
|
|
struct ceph_inode_info *ci = ceph_inode(inode);
|
|
|
|
spin_lock(&ci->i_ceph_lock);
|
|
if (__ceph_caps_issued_mask_metric(ci, CEPH_CAP_XATTR_SHARED, 0))
|
|
set_cached_acl(inode, type, acl);
|
|
else
|
|
forget_cached_acl(inode, type);
|
|
spin_unlock(&ci->i_ceph_lock);
|
|
}
|
|
|
|
struct posix_acl *ceph_get_acl(struct inode *inode, int type, bool rcu)
|
|
{
|
|
int size;
|
|
unsigned int retry_cnt = 0;
|
|
const char *name;
|
|
char *value = NULL;
|
|
struct posix_acl *acl;
|
|
|
|
if (rcu)
|
|
return ERR_PTR(-ECHILD);
|
|
|
|
switch (type) {
|
|
case ACL_TYPE_ACCESS:
|
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
|
break;
|
|
case ACL_TYPE_DEFAULT:
|
|
name = XATTR_NAME_POSIX_ACL_DEFAULT;
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
retry:
|
|
size = __ceph_getxattr(inode, name, "", 0);
|
|
if (size > 0) {
|
|
value = kzalloc(size, GFP_NOFS);
|
|
if (!value)
|
|
return ERR_PTR(-ENOMEM);
|
|
size = __ceph_getxattr(inode, name, value, size);
|
|
}
|
|
|
|
if (size == -ERANGE && retry_cnt < 10) {
|
|
retry_cnt++;
|
|
kfree(value);
|
|
value = NULL;
|
|
goto retry;
|
|
}
|
|
|
|
if (size > 0) {
|
|
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
|
} else if (size == -ENODATA || size == 0) {
|
|
acl = NULL;
|
|
} else {
|
|
pr_err_ratelimited("get acl %llx.%llx failed, err=%d\n",
|
|
ceph_vinop(inode), size);
|
|
acl = ERR_PTR(-EIO);
|
|
}
|
|
|
|
kfree(value);
|
|
|
|
if (!IS_ERR(acl))
|
|
ceph_set_cached_acl(inode, type, acl);
|
|
|
|
return acl;
|
|
}
|
|
|
|
int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
|
struct posix_acl *acl, int type)
|
|
{
|
|
int ret = 0, size = 0;
|
|
const char *name = NULL;
|
|
char *value = NULL;
|
|
struct iattr newattrs;
|
|
struct inode *inode = d_inode(dentry);
|
|
struct timespec64 old_ctime = inode_get_ctime(inode);
|
|
umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
|
|
|
|
if (ceph_snap(inode) != CEPH_NOSNAP) {
|
|
ret = -EROFS;
|
|
goto out;
|
|
}
|
|
|
|
switch (type) {
|
|
case ACL_TYPE_ACCESS:
|
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
|
if (acl) {
|
|
ret = posix_acl_update_mode(&nop_mnt_idmap, inode,
|
|
&new_mode, &acl);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
break;
|
|
case ACL_TYPE_DEFAULT:
|
|
if (!S_ISDIR(inode->i_mode)) {
|
|
ret = acl ? -EINVAL : 0;
|
|
goto out;
|
|
}
|
|
name = XATTR_NAME_POSIX_ACL_DEFAULT;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (acl) {
|
|
size = posix_acl_xattr_size(acl->a_count);
|
|
value = kmalloc(size, GFP_NOFS);
|
|
if (!value) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
if (new_mode != old_mode) {
|
|
newattrs.ia_ctime = current_time(inode);
|
|
newattrs.ia_mode = new_mode;
|
|
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
|
ret = __ceph_setattr(inode, &newattrs, NULL);
|
|
if (ret)
|
|
goto out_free;
|
|
}
|
|
|
|
ret = __ceph_setxattr(inode, name, value, size, 0);
|
|
if (ret) {
|
|
if (new_mode != old_mode) {
|
|
newattrs.ia_ctime = old_ctime;
|
|
newattrs.ia_mode = old_mode;
|
|
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
|
__ceph_setattr(inode, &newattrs, NULL);
|
|
}
|
|
goto out_free;
|
|
}
|
|
|
|
ceph_set_cached_acl(inode, type, acl);
|
|
|
|
out_free:
|
|
kfree(value);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
|
|
struct ceph_acl_sec_ctx *as_ctx)
|
|
{
|
|
struct posix_acl *acl, *default_acl;
|
|
size_t val_size1 = 0, val_size2 = 0;
|
|
struct ceph_pagelist *pagelist = NULL;
|
|
void *tmp_buf = NULL;
|
|
int err;
|
|
|
|
err = posix_acl_create(dir, mode, &default_acl, &acl);
|
|
if (err)
|
|
return err;
|
|
|
|
if (acl) {
|
|
err = posix_acl_equiv_mode(acl, mode);
|
|
if (err < 0)
|
|
goto out_err;
|
|
if (err == 0) {
|
|
posix_acl_release(acl);
|
|
acl = NULL;
|
|
}
|
|
}
|
|
|
|
if (!default_acl && !acl)
|
|
return 0;
|
|
|
|
if (acl)
|
|
val_size1 = posix_acl_xattr_size(acl->a_count);
|
|
if (default_acl)
|
|
val_size2 = posix_acl_xattr_size(default_acl->a_count);
|
|
|
|
err = -ENOMEM;
|
|
tmp_buf = kmalloc(max(val_size1, val_size2), GFP_KERNEL);
|
|
if (!tmp_buf)
|
|
goto out_err;
|
|
pagelist = ceph_pagelist_alloc(GFP_KERNEL);
|
|
if (!pagelist)
|
|
goto out_err;
|
|
|
|
err = ceph_pagelist_reserve(pagelist, PAGE_SIZE);
|
|
if (err)
|
|
goto out_err;
|
|
|
|
ceph_pagelist_encode_32(pagelist, acl && default_acl ? 2 : 1);
|
|
|
|
if (acl) {
|
|
size_t len = strlen(XATTR_NAME_POSIX_ACL_ACCESS);
|
|
err = ceph_pagelist_reserve(pagelist, len + val_size1 + 8);
|
|
if (err)
|
|
goto out_err;
|
|
ceph_pagelist_encode_string(pagelist, XATTR_NAME_POSIX_ACL_ACCESS,
|
|
len);
|
|
err = posix_acl_to_xattr(&init_user_ns, acl,
|
|
tmp_buf, val_size1);
|
|
if (err < 0)
|
|
goto out_err;
|
|
ceph_pagelist_encode_32(pagelist, val_size1);
|
|
ceph_pagelist_append(pagelist, tmp_buf, val_size1);
|
|
}
|
|
if (default_acl) {
|
|
size_t len = strlen(XATTR_NAME_POSIX_ACL_DEFAULT);
|
|
err = ceph_pagelist_reserve(pagelist, len + val_size2 + 8);
|
|
if (err)
|
|
goto out_err;
|
|
ceph_pagelist_encode_string(pagelist,
|
|
XATTR_NAME_POSIX_ACL_DEFAULT, len);
|
|
err = posix_acl_to_xattr(&init_user_ns, default_acl,
|
|
tmp_buf, val_size2);
|
|
if (err < 0)
|
|
goto out_err;
|
|
ceph_pagelist_encode_32(pagelist, val_size2);
|
|
ceph_pagelist_append(pagelist, tmp_buf, val_size2);
|
|
}
|
|
|
|
kfree(tmp_buf);
|
|
|
|
as_ctx->acl = acl;
|
|
as_ctx->default_acl = default_acl;
|
|
as_ctx->pagelist = pagelist;
|
|
return 0;
|
|
|
|
out_err:
|
|
posix_acl_release(acl);
|
|
posix_acl_release(default_acl);
|
|
kfree(tmp_buf);
|
|
if (pagelist)
|
|
ceph_pagelist_release(pagelist);
|
|
return err;
|
|
}
|
|
|
|
void ceph_init_inode_acls(struct inode *inode, struct ceph_acl_sec_ctx *as_ctx)
|
|
{
|
|
if (!inode)
|
|
return;
|
|
ceph_set_cached_acl(inode, ACL_TYPE_ACCESS, as_ctx->acl);
|
|
ceph_set_cached_acl(inode, ACL_TYPE_DEFAULT, as_ctx->default_acl);
|
|
}
|