Merge branch 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux-2.6-block.git] / fs / ceph / acl.c
CommitLineData
7221fe4c
GZ
1/*
2 * linux/fs/ceph/acl.c
3 *
4 * Copyright (C) 2013 Guangliang Zhao, <lucienchao@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License v2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 021110-1307, USA.
19 */
20
21#include <linux/ceph/ceph_debug.h>
22#include <linux/fs.h>
23#include <linux/string.h>
24#include <linux/xattr.h>
25#include <linux/posix_acl_xattr.h>
26#include <linux/posix_acl.h>
27#include <linux/sched.h>
28#include <linux/slab.h>
29
30#include "super.h"
31
32static inline void ceph_set_cached_acl(struct inode *inode,
33 int type, struct posix_acl *acl)
34{
35 struct ceph_inode_info *ci = ceph_inode(inode);
36
37 spin_lock(&ci->i_ceph_lock);
38 if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0))
39 set_cached_acl(inode, type, acl);
b8a7a3a6
AG
40 else
41 forget_cached_acl(inode, type);
7221fe4c
GZ
42 spin_unlock(&ci->i_ceph_lock);
43}
44
7221fe4c
GZ
45struct posix_acl *ceph_get_acl(struct inode *inode, int type)
46{
47 int size;
f017754d 48 unsigned int retry_cnt = 0;
7221fe4c
GZ
49 const char *name;
50 char *value = NULL;
51 struct posix_acl *acl;
52
7221fe4c
GZ
53 switch (type) {
54 case ACL_TYPE_ACCESS:
97d79299 55 name = XATTR_NAME_POSIX_ACL_ACCESS;
7221fe4c
GZ
56 break;
57 case ACL_TYPE_DEFAULT:
97d79299 58 name = XATTR_NAME_POSIX_ACL_DEFAULT;
7221fe4c
GZ
59 break;
60 default:
61 BUG();
62 }
63
f017754d 64retry:
7221fe4c
GZ
65 size = __ceph_getxattr(inode, name, "", 0);
66 if (size > 0) {
67 value = kzalloc(size, GFP_NOFS);
68 if (!value)
69 return ERR_PTR(-ENOMEM);
70 size = __ceph_getxattr(inode, name, value, size);
71 }
72
f017754d
CX
73 if (size == -ERANGE && retry_cnt < 10) {
74 retry_cnt++;
75 kfree(value);
76 value = NULL;
77 goto retry;
78 }
79
80 if (size > 0) {
7221fe4c 81 acl = posix_acl_from_xattr(&init_user_ns, value, size);
f017754d 82 } else if (size == -ENODATA || size == 0) {
7221fe4c 83 acl = NULL;
f017754d
CX
84 } else {
85 pr_err_ratelimited("get acl %llx.%llx failed, err=%d\n",
86 ceph_vinop(inode), size);
7221fe4c 87 acl = ERR_PTR(-EIO);
f017754d 88 }
7221fe4c
GZ
89
90 kfree(value);
91
92 if (!IS_ERR(acl))
93 ceph_set_cached_acl(inode, type, acl);
94
95 return acl;
96}
97
72466d0b 98int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
7221fe4c
GZ
99{
100 int ret = 0, size = 0;
101 const char *name = NULL;
102 char *value = NULL;
103 struct iattr newattrs;
93d35c75 104 struct timespec64 old_ctime = inode->i_ctime;
7221fe4c
GZ
105 umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
106
5da20799
CX
107 if (ceph_snap(inode) != CEPH_NOSNAP) {
108 ret = -EROFS;
109 goto out;
110 }
111
7221fe4c
GZ
112 switch (type) {
113 case ACL_TYPE_ACCESS:
97d79299 114 name = XATTR_NAME_POSIX_ACL_ACCESS;
7221fe4c 115 if (acl) {
07393101
JK
116 ret = posix_acl_update_mode(inode, &new_mode, &acl);
117 if (ret)
7221fe4c 118 goto out;
7221fe4c
GZ
119 }
120 break;
121 case ACL_TYPE_DEFAULT:
122 if (!S_ISDIR(inode->i_mode)) {
123 ret = acl ? -EINVAL : 0;
124 goto out;
125 }
97d79299 126 name = XATTR_NAME_POSIX_ACL_DEFAULT;
7221fe4c
GZ
127 break;
128 default:
129 ret = -EINVAL;
130 goto out;
131 }
132
133 if (acl) {
134 size = posix_acl_xattr_size(acl->a_count);
135 value = kmalloc(size, GFP_NOFS);
136 if (!value) {
137 ret = -ENOMEM;
138 goto out;
139 }
140
141 ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
142 if (ret < 0)
143 goto out_free;
144 }
145
146 if (new_mode != old_mode) {
4ca2fea6 147 newattrs.ia_ctime = current_time(inode);
7221fe4c 148 newattrs.ia_mode = new_mode;
93d35c75 149 newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
a26fecca 150 ret = __ceph_setattr(inode, &newattrs);
7221fe4c 151 if (ret)
a26fecca 152 goto out_free;
7221fe4c
GZ
153 }
154
a26fecca 155 ret = __ceph_setxattr(inode, name, value, size, 0);
7221fe4c
GZ
156 if (ret) {
157 if (new_mode != old_mode) {
93d35c75 158 newattrs.ia_ctime = old_ctime;
7221fe4c 159 newattrs.ia_mode = old_mode;
93d35c75 160 newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
a26fecca 161 __ceph_setattr(inode, &newattrs);
7221fe4c 162 }
a26fecca 163 goto out_free;
7221fe4c
GZ
164 }
165
166 ceph_set_cached_acl(inode, type, acl);
167
168out_free:
169 kfree(value);
170out:
171 return ret;
172}
173
b1ee94aa
YZ
174int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
175 struct ceph_acls_info *info)
7221fe4c 176{
b1ee94aa
YZ
177 struct posix_acl *acl, *default_acl;
178 size_t val_size1 = 0, val_size2 = 0;
179 struct ceph_pagelist *pagelist = NULL;
180 void *tmp_buf = NULL;
181 int err;
182
183 err = posix_acl_create(dir, mode, &default_acl, &acl);
184 if (err)
185 return err;
186
187 if (acl) {
61ad36d4
CX
188 err = posix_acl_equiv_mode(acl, mode);
189 if (err < 0)
b1ee94aa 190 goto out_err;
61ad36d4 191 if (err == 0) {
b1ee94aa
YZ
192 posix_acl_release(acl);
193 acl = NULL;
f5f18647 194 }
f5f18647 195 }
7221fe4c 196
b1ee94aa
YZ
197 if (!default_acl && !acl)
198 return 0;
199
200 if (acl)
201 val_size1 = posix_acl_xattr_size(acl->a_count);
202 if (default_acl)
203 val_size2 = posix_acl_xattr_size(default_acl->a_count);
204
205 err = -ENOMEM;
687265e5 206 tmp_buf = kmalloc(max(val_size1, val_size2), GFP_KERNEL);
b1ee94aa
YZ
207 if (!tmp_buf)
208 goto out_err;
33165d47 209 pagelist = ceph_pagelist_alloc(GFP_KERNEL);
b1ee94aa
YZ
210 if (!pagelist)
211 goto out_err;
b1ee94aa
YZ
212
213 err = ceph_pagelist_reserve(pagelist, PAGE_SIZE);
214 if (err)
215 goto out_err;
216
217 ceph_pagelist_encode_32(pagelist, acl && default_acl ? 2 : 1);
218
75858236 219 if (acl) {
97d79299 220 size_t len = strlen(XATTR_NAME_POSIX_ACL_ACCESS);
b1ee94aa
YZ
221 err = ceph_pagelist_reserve(pagelist, len + val_size1 + 8);
222 if (err)
223 goto out_err;
97d79299 224 ceph_pagelist_encode_string(pagelist, XATTR_NAME_POSIX_ACL_ACCESS,
b1ee94aa
YZ
225 len);
226 err = posix_acl_to_xattr(&init_user_ns, acl,
227 tmp_buf, val_size1);
228 if (err < 0)
229 goto out_err;
230 ceph_pagelist_encode_32(pagelist, val_size1);
231 ceph_pagelist_append(pagelist, tmp_buf, val_size1);
75858236 232 }
b1ee94aa 233 if (default_acl) {
97d79299 234 size_t len = strlen(XATTR_NAME_POSIX_ACL_DEFAULT);
b1ee94aa
YZ
235 err = ceph_pagelist_reserve(pagelist, len + val_size2 + 8);
236 if (err)
237 goto out_err;
238 err = ceph_pagelist_encode_string(pagelist,
97d79299 239 XATTR_NAME_POSIX_ACL_DEFAULT, len);
b1ee94aa
YZ
240 err = posix_acl_to_xattr(&init_user_ns, default_acl,
241 tmp_buf, val_size2);
242 if (err < 0)
243 goto out_err;
244 ceph_pagelist_encode_32(pagelist, val_size2);
245 ceph_pagelist_append(pagelist, tmp_buf, val_size2);
246 }
247
248 kfree(tmp_buf);
249
250 info->acl = acl;
251 info->default_acl = default_acl;
252 info->pagelist = pagelist;
253 return 0;
254
255out_err:
256 posix_acl_release(acl);
257 posix_acl_release(default_acl);
258 kfree(tmp_buf);
259 if (pagelist)
260 ceph_pagelist_release(pagelist);
261 return err;
262}
263
264void ceph_init_inode_acls(struct inode* inode, struct ceph_acls_info *info)
265{
266 if (!inode)
267 return;
268 ceph_set_cached_acl(inode, ACL_TYPE_ACCESS, info->acl);
269 ceph_set_cached_acl(inode, ACL_TYPE_DEFAULT, info->default_acl);
270}
271
272void ceph_release_acls_info(struct ceph_acls_info *info)
273{
274 posix_acl_release(info->acl);
275 posix_acl_release(info->default_acl);
276 if (info->pagelist)
277 ceph_pagelist_release(info->pagelist);
7221fe4c 278}