GNU Linux-libre 5.19-rc6-gnu
[releases.git] / fs / btrfs / acl.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2007 Red Hat.  All rights reserved.
4  */
5
6 #include <linux/fs.h>
7 #include <linux/string.h>
8 #include <linux/xattr.h>
9 #include <linux/posix_acl_xattr.h>
10 #include <linux/posix_acl.h>
11 #include <linux/sched.h>
12 #include <linux/sched/mm.h>
13 #include <linux/slab.h>
14
15 #include "ctree.h"
16 #include "btrfs_inode.h"
17 #include "xattr.h"
18
19 struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu)
20 {
21         int size;
22         const char *name;
23         char *value = NULL;
24         struct posix_acl *acl;
25
26         if (rcu)
27                 return ERR_PTR(-ECHILD);
28
29         switch (type) {
30         case ACL_TYPE_ACCESS:
31                 name = XATTR_NAME_POSIX_ACL_ACCESS;
32                 break;
33         case ACL_TYPE_DEFAULT:
34                 name = XATTR_NAME_POSIX_ACL_DEFAULT;
35                 break;
36         default:
37                 return ERR_PTR(-EINVAL);
38         }
39
40         size = btrfs_getxattr(inode, name, NULL, 0);
41         if (size > 0) {
42                 value = kzalloc(size, GFP_KERNEL);
43                 if (!value)
44                         return ERR_PTR(-ENOMEM);
45                 size = btrfs_getxattr(inode, name, value, size);
46         }
47         if (size > 0)
48                 acl = posix_acl_from_xattr(&init_user_ns, value, size);
49         else if (size == -ENODATA || size == 0)
50                 acl = NULL;
51         else
52                 acl = ERR_PTR(size);
53         kfree(value);
54
55         return acl;
56 }
57
58 int __btrfs_set_acl(struct btrfs_trans_handle *trans, struct inode *inode,
59                     struct posix_acl *acl, int type)
60 {
61         int ret, size = 0;
62         const char *name;
63         char *value = NULL;
64
65         switch (type) {
66         case ACL_TYPE_ACCESS:
67                 name = XATTR_NAME_POSIX_ACL_ACCESS;
68                 break;
69         case ACL_TYPE_DEFAULT:
70                 if (!S_ISDIR(inode->i_mode))
71                         return acl ? -EINVAL : 0;
72                 name = XATTR_NAME_POSIX_ACL_DEFAULT;
73                 break;
74         default:
75                 return -EINVAL;
76         }
77
78         if (acl) {
79                 unsigned int nofs_flag;
80
81                 size = posix_acl_xattr_size(acl->a_count);
82                 /*
83                  * We're holding a transaction handle, so use a NOFS memory
84                  * allocation context to avoid deadlock if reclaim happens.
85                  */
86                 nofs_flag = memalloc_nofs_save();
87                 value = kmalloc(size, GFP_KERNEL);
88                 memalloc_nofs_restore(nofs_flag);
89                 if (!value) {
90                         ret = -ENOMEM;
91                         goto out;
92                 }
93
94                 ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
95                 if (ret < 0)
96                         goto out;
97         }
98
99         if (trans)
100                 ret = btrfs_setxattr(trans, inode, name, value, size, 0);
101         else
102                 ret = btrfs_setxattr_trans(inode, name, value, size, 0);
103
104 out:
105         kfree(value);
106
107         if (!ret)
108                 set_cached_acl(inode, type, acl);
109
110         return ret;
111 }
112
113 int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
114                   struct posix_acl *acl, int type)
115 {
116         int ret;
117         umode_t old_mode = inode->i_mode;
118
119         if (type == ACL_TYPE_ACCESS && acl) {
120                 ret = posix_acl_update_mode(mnt_userns, inode,
121                                             &inode->i_mode, &acl);
122                 if (ret)
123                         return ret;
124         }
125         ret = __btrfs_set_acl(NULL, inode, acl, type);
126         if (ret)
127                 inode->i_mode = old_mode;
128         return ret;
129 }