162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/ceph/acl.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Guangliang Zhao, <lucienchao@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/xattr.h> 1262306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h> 1362306a36Sopenharmony_ci#include <linux/posix_acl.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "super.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic inline void ceph_set_cached_acl(struct inode *inode, 2062306a36Sopenharmony_ci int type, struct posix_acl *acl) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 2562306a36Sopenharmony_ci if (__ceph_caps_issued_mask_metric(ci, CEPH_CAP_XATTR_SHARED, 0)) 2662306a36Sopenharmony_ci set_cached_acl(inode, type, acl); 2762306a36Sopenharmony_ci else 2862306a36Sopenharmony_ci forget_cached_acl(inode, type); 2962306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct posix_acl *ceph_get_acl(struct inode *inode, int type, bool rcu) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int size; 3562306a36Sopenharmony_ci unsigned int retry_cnt = 0; 3662306a36Sopenharmony_ci const char *name; 3762306a36Sopenharmony_ci char *value = NULL; 3862306a36Sopenharmony_ci struct posix_acl *acl; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (rcu) 4162306a36Sopenharmony_ci return ERR_PTR(-ECHILD); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci switch (type) { 4462306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 4562306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 4862306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci default: 5162306a36Sopenharmony_ci BUG(); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciretry: 5562306a36Sopenharmony_ci size = __ceph_getxattr(inode, name, "", 0); 5662306a36Sopenharmony_ci if (size > 0) { 5762306a36Sopenharmony_ci value = kzalloc(size, GFP_NOFS); 5862306a36Sopenharmony_ci if (!value) 5962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6062306a36Sopenharmony_ci size = __ceph_getxattr(inode, name, value, size); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (size == -ERANGE && retry_cnt < 10) { 6462306a36Sopenharmony_ci retry_cnt++; 6562306a36Sopenharmony_ci kfree(value); 6662306a36Sopenharmony_ci value = NULL; 6762306a36Sopenharmony_ci goto retry; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (size > 0) { 7162306a36Sopenharmony_ci acl = posix_acl_from_xattr(&init_user_ns, value, size); 7262306a36Sopenharmony_ci } else if (size == -ENODATA || size == 0) { 7362306a36Sopenharmony_ci acl = NULL; 7462306a36Sopenharmony_ci } else { 7562306a36Sopenharmony_ci pr_err_ratelimited("get acl %llx.%llx failed, err=%d\n", 7662306a36Sopenharmony_ci ceph_vinop(inode), size); 7762306a36Sopenharmony_ci acl = ERR_PTR(-EIO); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci kfree(value); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!IS_ERR(acl)) 8362306a36Sopenharmony_ci ceph_set_cached_acl(inode, type, acl); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return acl; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciint ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 8962306a36Sopenharmony_ci struct posix_acl *acl, int type) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int ret = 0, size = 0; 9262306a36Sopenharmony_ci const char *name = NULL; 9362306a36Sopenharmony_ci char *value = NULL; 9462306a36Sopenharmony_ci struct iattr newattrs; 9562306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 9662306a36Sopenharmony_ci struct timespec64 old_ctime = inode_get_ctime(inode); 9762306a36Sopenharmony_ci umode_t new_mode = inode->i_mode, old_mode = inode->i_mode; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (ceph_snap(inode) != CEPH_NOSNAP) { 10062306a36Sopenharmony_ci ret = -EROFS; 10162306a36Sopenharmony_ci goto out; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci switch (type) { 10562306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 10662306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 10762306a36Sopenharmony_ci if (acl) { 10862306a36Sopenharmony_ci ret = posix_acl_update_mode(&nop_mnt_idmap, inode, 10962306a36Sopenharmony_ci &new_mode, &acl); 11062306a36Sopenharmony_ci if (ret) 11162306a36Sopenharmony_ci goto out; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 11562306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 11662306a36Sopenharmony_ci ret = acl ? -EINVAL : 0; 11762306a36Sopenharmony_ci goto out; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci default: 12262306a36Sopenharmony_ci ret = -EINVAL; 12362306a36Sopenharmony_ci goto out; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (acl) { 12762306a36Sopenharmony_ci size = posix_acl_xattr_size(acl->a_count); 12862306a36Sopenharmony_ci value = kmalloc(size, GFP_NOFS); 12962306a36Sopenharmony_ci if (!value) { 13062306a36Sopenharmony_ci ret = -ENOMEM; 13162306a36Sopenharmony_ci goto out; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); 13562306a36Sopenharmony_ci if (ret < 0) 13662306a36Sopenharmony_ci goto out_free; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (new_mode != old_mode) { 14062306a36Sopenharmony_ci newattrs.ia_ctime = current_time(inode); 14162306a36Sopenharmony_ci newattrs.ia_mode = new_mode; 14262306a36Sopenharmony_ci newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; 14362306a36Sopenharmony_ci ret = __ceph_setattr(inode, &newattrs, NULL); 14462306a36Sopenharmony_ci if (ret) 14562306a36Sopenharmony_ci goto out_free; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = __ceph_setxattr(inode, name, value, size, 0); 14962306a36Sopenharmony_ci if (ret) { 15062306a36Sopenharmony_ci if (new_mode != old_mode) { 15162306a36Sopenharmony_ci newattrs.ia_ctime = old_ctime; 15262306a36Sopenharmony_ci newattrs.ia_mode = old_mode; 15362306a36Sopenharmony_ci newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; 15462306a36Sopenharmony_ci __ceph_setattr(inode, &newattrs, NULL); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci goto out_free; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ceph_set_cached_acl(inode, type, acl); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciout_free: 16262306a36Sopenharmony_ci kfree(value); 16362306a36Sopenharmony_ciout: 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint ceph_pre_init_acls(struct inode *dir, umode_t *mode, 16862306a36Sopenharmony_ci struct ceph_acl_sec_ctx *as_ctx) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct posix_acl *acl, *default_acl; 17162306a36Sopenharmony_ci size_t val_size1 = 0, val_size2 = 0; 17262306a36Sopenharmony_ci struct ceph_pagelist *pagelist = NULL; 17362306a36Sopenharmony_ci void *tmp_buf = NULL; 17462306a36Sopenharmony_ci int err; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci err = posix_acl_create(dir, mode, &default_acl, &acl); 17762306a36Sopenharmony_ci if (err) 17862306a36Sopenharmony_ci return err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (acl) { 18162306a36Sopenharmony_ci err = posix_acl_equiv_mode(acl, mode); 18262306a36Sopenharmony_ci if (err < 0) 18362306a36Sopenharmony_ci goto out_err; 18462306a36Sopenharmony_ci if (err == 0) { 18562306a36Sopenharmony_ci posix_acl_release(acl); 18662306a36Sopenharmony_ci acl = NULL; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!default_acl && !acl) 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (acl) 19462306a36Sopenharmony_ci val_size1 = posix_acl_xattr_size(acl->a_count); 19562306a36Sopenharmony_ci if (default_acl) 19662306a36Sopenharmony_ci val_size2 = posix_acl_xattr_size(default_acl->a_count); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci err = -ENOMEM; 19962306a36Sopenharmony_ci tmp_buf = kmalloc(max(val_size1, val_size2), GFP_KERNEL); 20062306a36Sopenharmony_ci if (!tmp_buf) 20162306a36Sopenharmony_ci goto out_err; 20262306a36Sopenharmony_ci pagelist = ceph_pagelist_alloc(GFP_KERNEL); 20362306a36Sopenharmony_ci if (!pagelist) 20462306a36Sopenharmony_ci goto out_err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci err = ceph_pagelist_reserve(pagelist, PAGE_SIZE); 20762306a36Sopenharmony_ci if (err) 20862306a36Sopenharmony_ci goto out_err; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ceph_pagelist_encode_32(pagelist, acl && default_acl ? 2 : 1); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (acl) { 21362306a36Sopenharmony_ci size_t len = strlen(XATTR_NAME_POSIX_ACL_ACCESS); 21462306a36Sopenharmony_ci err = ceph_pagelist_reserve(pagelist, len + val_size1 + 8); 21562306a36Sopenharmony_ci if (err) 21662306a36Sopenharmony_ci goto out_err; 21762306a36Sopenharmony_ci ceph_pagelist_encode_string(pagelist, XATTR_NAME_POSIX_ACL_ACCESS, 21862306a36Sopenharmony_ci len); 21962306a36Sopenharmony_ci err = posix_acl_to_xattr(&init_user_ns, acl, 22062306a36Sopenharmony_ci tmp_buf, val_size1); 22162306a36Sopenharmony_ci if (err < 0) 22262306a36Sopenharmony_ci goto out_err; 22362306a36Sopenharmony_ci ceph_pagelist_encode_32(pagelist, val_size1); 22462306a36Sopenharmony_ci ceph_pagelist_append(pagelist, tmp_buf, val_size1); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci if (default_acl) { 22762306a36Sopenharmony_ci size_t len = strlen(XATTR_NAME_POSIX_ACL_DEFAULT); 22862306a36Sopenharmony_ci err = ceph_pagelist_reserve(pagelist, len + val_size2 + 8); 22962306a36Sopenharmony_ci if (err) 23062306a36Sopenharmony_ci goto out_err; 23162306a36Sopenharmony_ci ceph_pagelist_encode_string(pagelist, 23262306a36Sopenharmony_ci XATTR_NAME_POSIX_ACL_DEFAULT, len); 23362306a36Sopenharmony_ci err = posix_acl_to_xattr(&init_user_ns, default_acl, 23462306a36Sopenharmony_ci tmp_buf, val_size2); 23562306a36Sopenharmony_ci if (err < 0) 23662306a36Sopenharmony_ci goto out_err; 23762306a36Sopenharmony_ci ceph_pagelist_encode_32(pagelist, val_size2); 23862306a36Sopenharmony_ci ceph_pagelist_append(pagelist, tmp_buf, val_size2); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci kfree(tmp_buf); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci as_ctx->acl = acl; 24462306a36Sopenharmony_ci as_ctx->default_acl = default_acl; 24562306a36Sopenharmony_ci as_ctx->pagelist = pagelist; 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciout_err: 24962306a36Sopenharmony_ci posix_acl_release(acl); 25062306a36Sopenharmony_ci posix_acl_release(default_acl); 25162306a36Sopenharmony_ci kfree(tmp_buf); 25262306a36Sopenharmony_ci if (pagelist) 25362306a36Sopenharmony_ci ceph_pagelist_release(pagelist); 25462306a36Sopenharmony_ci return err; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_civoid ceph_init_inode_acls(struct inode *inode, struct ceph_acl_sec_ctx *as_ctx) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci if (!inode) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci ceph_set_cached_acl(inode, ACL_TYPE_ACCESS, as_ctx->acl); 26262306a36Sopenharmony_ci ceph_set_cached_acl(inode, ACL_TYPE_DEFAULT, as_ctx->default_acl); 26362306a36Sopenharmony_ci} 264