18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Red Hat. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/fs.h> 78c2ecf20Sopenharmony_ci#include <linux/string.h> 88c2ecf20Sopenharmony_ci#include <linux/xattr.h> 98c2ecf20Sopenharmony_ci#include <linux/posix_acl_xattr.h> 108c2ecf20Sopenharmony_ci#include <linux/posix_acl.h> 118c2ecf20Sopenharmony_ci#include <linux/sched.h> 128c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "ctree.h" 168c2ecf20Sopenharmony_ci#include "btrfs_inode.h" 178c2ecf20Sopenharmony_ci#include "xattr.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct posix_acl *btrfs_get_acl(struct inode *inode, int type) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci int size; 228c2ecf20Sopenharmony_ci const char *name; 238c2ecf20Sopenharmony_ci char *value = NULL; 248c2ecf20Sopenharmony_ci struct posix_acl *acl; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci switch (type) { 278c2ecf20Sopenharmony_ci case ACL_TYPE_ACCESS: 288c2ecf20Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 298c2ecf20Sopenharmony_ci break; 308c2ecf20Sopenharmony_ci case ACL_TYPE_DEFAULT: 318c2ecf20Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 328c2ecf20Sopenharmony_ci break; 338c2ecf20Sopenharmony_ci default: 348c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci size = btrfs_getxattr(inode, name, NULL, 0); 388c2ecf20Sopenharmony_ci if (size > 0) { 398c2ecf20Sopenharmony_ci value = kzalloc(size, GFP_KERNEL); 408c2ecf20Sopenharmony_ci if (!value) 418c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 428c2ecf20Sopenharmony_ci size = btrfs_getxattr(inode, name, value, size); 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci if (size > 0) 458c2ecf20Sopenharmony_ci acl = posix_acl_from_xattr(&init_user_ns, value, size); 468c2ecf20Sopenharmony_ci else if (size == -ENODATA || size == 0) 478c2ecf20Sopenharmony_ci acl = NULL; 488c2ecf20Sopenharmony_ci else 498c2ecf20Sopenharmony_ci acl = ERR_PTR(size); 508c2ecf20Sopenharmony_ci kfree(value); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return acl; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int __btrfs_set_acl(struct btrfs_trans_handle *trans, 568c2ecf20Sopenharmony_ci struct inode *inode, struct posix_acl *acl, int type) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int ret, size = 0; 598c2ecf20Sopenharmony_ci const char *name; 608c2ecf20Sopenharmony_ci char *value = NULL; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci switch (type) { 638c2ecf20Sopenharmony_ci case ACL_TYPE_ACCESS: 648c2ecf20Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci case ACL_TYPE_DEFAULT: 678c2ecf20Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) 688c2ecf20Sopenharmony_ci return acl ? -EINVAL : 0; 698c2ecf20Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci default: 728c2ecf20Sopenharmony_ci return -EINVAL; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (acl) { 768c2ecf20Sopenharmony_ci unsigned int nofs_flag; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci size = posix_acl_xattr_size(acl->a_count); 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * We're holding a transaction handle, so use a NOFS memory 818c2ecf20Sopenharmony_ci * allocation context to avoid deadlock if reclaim happens. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci nofs_flag = memalloc_nofs_save(); 848c2ecf20Sopenharmony_ci value = kmalloc(size, GFP_KERNEL); 858c2ecf20Sopenharmony_ci memalloc_nofs_restore(nofs_flag); 868c2ecf20Sopenharmony_ci if (!value) { 878c2ecf20Sopenharmony_ci ret = -ENOMEM; 888c2ecf20Sopenharmony_ci goto out; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); 928c2ecf20Sopenharmony_ci if (ret < 0) 938c2ecf20Sopenharmony_ci goto out; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (trans) 978c2ecf20Sopenharmony_ci ret = btrfs_setxattr(trans, inode, name, value, size, 0); 988c2ecf20Sopenharmony_ci else 998c2ecf20Sopenharmony_ci ret = btrfs_setxattr_trans(inode, name, value, size, 0); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciout: 1028c2ecf20Sopenharmony_ci kfree(value); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (!ret) 1058c2ecf20Sopenharmony_ci set_cached_acl(inode, type, acl); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciint btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int ret; 1138c2ecf20Sopenharmony_ci umode_t old_mode = inode->i_mode; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (type == ACL_TYPE_ACCESS && acl) { 1168c2ecf20Sopenharmony_ci ret = posix_acl_update_mode(inode, &inode->i_mode, &acl); 1178c2ecf20Sopenharmony_ci if (ret) 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci ret = __btrfs_set_acl(NULL, inode, acl, type); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci inode->i_mode = old_mode; 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciint btrfs_init_acl(struct btrfs_trans_handle *trans, 1278c2ecf20Sopenharmony_ci struct inode *inode, struct inode *dir) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct posix_acl *default_acl, *acl; 1308c2ecf20Sopenharmony_ci int ret = 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* this happens with subvols */ 1338c2ecf20Sopenharmony_ci if (!dir) 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 1378c2ecf20Sopenharmony_ci if (ret) 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (default_acl) { 1418c2ecf20Sopenharmony_ci ret = __btrfs_set_acl(trans, inode, default_acl, 1428c2ecf20Sopenharmony_ci ACL_TYPE_DEFAULT); 1438c2ecf20Sopenharmony_ci posix_acl_release(default_acl); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (acl) { 1478c2ecf20Sopenharmony_ci if (!ret) 1488c2ecf20Sopenharmony_ci ret = __btrfs_set_acl(trans, inode, acl, 1498c2ecf20Sopenharmony_ci ACL_TYPE_ACCESS); 1508c2ecf20Sopenharmony_ci posix_acl_release(acl); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (!default_acl && !acl) 1548c2ecf20Sopenharmony_ci cache_no_acl(inode); 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci} 157