GNU Linux-libre 4.14.332-gnu1
[releases.git] / fs / hfsplus / posix_acl.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * linux/fs/hfsplus/posix_acl.c
4  *
5  * Vyacheslav Dubeyko <slava@dubeyko.com>
6  *
7  * Handler for Posix Access Control Lists (ACLs) support.
8  */
9
10 #include "hfsplus_fs.h"
11 #include "xattr.h"
12 #include "acl.h"
13
14 struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type)
15 {
16         struct posix_acl *acl;
17         char *xattr_name;
18         char *value = NULL;
19         ssize_t size;
20
21         hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);
22
23         switch (type) {
24         case ACL_TYPE_ACCESS:
25                 xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
26                 break;
27         case ACL_TYPE_DEFAULT:
28                 xattr_name = XATTR_NAME_POSIX_ACL_DEFAULT;
29                 break;
30         default:
31                 return ERR_PTR(-EINVAL);
32         }
33
34         size = __hfsplus_getxattr(inode, xattr_name, NULL, 0);
35
36         if (size > 0) {
37                 value = (char *)hfsplus_alloc_attr_entry();
38                 if (unlikely(!value))
39                         return ERR_PTR(-ENOMEM);
40                 size = __hfsplus_getxattr(inode, xattr_name, value, size);
41         }
42
43         if (size > 0)
44                 acl = posix_acl_from_xattr(&init_user_ns, value, size);
45         else if (size == -ENODATA)
46                 acl = NULL;
47         else
48                 acl = ERR_PTR(size);
49
50         hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
51
52         return acl;
53 }
54
55 static int __hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
56                                    int type)
57 {
58         int err;
59         char *xattr_name;
60         size_t size = 0;
61         char *value = NULL;
62
63         hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);
64
65         switch (type) {
66         case ACL_TYPE_ACCESS:
67                 xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
68                 break;
69
70         case ACL_TYPE_DEFAULT:
71                 xattr_name = XATTR_NAME_POSIX_ACL_DEFAULT;
72                 if (!S_ISDIR(inode->i_mode))
73                         return acl ? -EACCES : 0;
74                 break;
75
76         default:
77                 return -EINVAL;
78         }
79
80         if (acl) {
81                 size = posix_acl_xattr_size(acl->a_count);
82                 if (unlikely(size > HFSPLUS_MAX_INLINE_DATA_SIZE))
83                         return -ENOMEM;
84                 value = (char *)hfsplus_alloc_attr_entry();
85                 if (unlikely(!value))
86                         return -ENOMEM;
87                 err = posix_acl_to_xattr(&init_user_ns, acl, value, size);
88                 if (unlikely(err < 0))
89                         goto end_set_acl;
90         }
91
92         err = __hfsplus_setxattr(inode, xattr_name, value, size, 0);
93
94 end_set_acl:
95         hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
96
97         if (!err)
98                 set_cached_acl(inode, type, acl);
99
100         return err;
101 }
102
103 int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl, int type)
104 {
105         int err;
106
107         if (type == ACL_TYPE_ACCESS && acl) {
108                 err = posix_acl_update_mode(inode, &inode->i_mode, &acl);
109                 if (err)
110                         return err;
111         }
112         return __hfsplus_set_posix_acl(inode, acl, type);
113 }
114
115 int hfsplus_init_posix_acl(struct inode *inode, struct inode *dir)
116 {
117         int err = 0;
118         struct posix_acl *default_acl, *acl;
119
120         hfs_dbg(ACL_MOD,
121                 "[%s]: ino %lu, dir->ino %lu\n",
122                 __func__, inode->i_ino, dir->i_ino);
123
124         if (S_ISLNK(inode->i_mode))
125                 return 0;
126
127         err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
128         if (err)
129                 return err;
130
131         if (default_acl) {
132                 err = __hfsplus_set_posix_acl(inode, default_acl,
133                                               ACL_TYPE_DEFAULT);
134                 posix_acl_release(default_acl);
135         }
136
137         if (acl) {
138                 if (!err)
139                         err = __hfsplus_set_posix_acl(inode, acl,
140                                                       ACL_TYPE_ACCESS);
141                 posix_acl_release(acl);
142         }
143         return err;
144 }