Blame view

fs/f2fs/acl.c 8.84 KB
0a8165d7c   Jaegeuk Kim   f2fs: adjust kern...
1
  /*
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   * fs/f2fs/acl.c
   *
   * Copyright (c) 2012 Samsung Electronics Co., Ltd.
   *             http://www.samsung.com/
   *
   * Portions of this code from linux/fs/ext2/acl.c
   *
   * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  #include <linux/f2fs_fs.h>
  #include "f2fs.h"
  #include "xattr.h"
  #include "acl.h"
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  static inline size_t f2fs_acl_size(int count)
  {
  	if (count <= 4) {
  		return sizeof(struct f2fs_acl_header) +
  			count * sizeof(struct f2fs_acl_entry_short);
  	} else {
  		return sizeof(struct f2fs_acl_header) +
  			4 * sizeof(struct f2fs_acl_entry_short) +
  			(count - 4) * sizeof(struct f2fs_acl_entry);
  	}
  }
  
  static inline int f2fs_acl_count(size_t size)
  {
  	ssize_t s;
  	size -= sizeof(struct f2fs_acl_header);
  	s = size - 4 * sizeof(struct f2fs_acl_entry_short);
  	if (s < 0) {
  		if (size % sizeof(struct f2fs_acl_entry_short))
  			return -1;
  		return size / sizeof(struct f2fs_acl_entry_short);
  	} else {
  		if (s % sizeof(struct f2fs_acl_entry))
  			return -1;
  		return s / sizeof(struct f2fs_acl_entry) + 4;
  	}
  }
  
  static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
  {
  	int i, count;
  	struct posix_acl *acl;
  	struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
  	struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
  	const char *end = value + size;
  
  	if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION))
  		return ERR_PTR(-EINVAL);
  
  	count = f2fs_acl_count(size);
  	if (count < 0)
  		return ERR_PTR(-EINVAL);
  	if (count == 0)
  		return NULL;
dd802406e   Jaegeuk Kim   f2fs: avoid doubl...
63
  	acl = posix_acl_alloc(count, GFP_NOFS);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  	if (!acl)
  		return ERR_PTR(-ENOMEM);
  
  	for (i = 0; i < count; i++) {
  
  		if ((char *)entry > end)
  			goto fail;
  
  		acl->a_entries[i].e_tag  = le16_to_cpu(entry->e_tag);
  		acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
  
  		switch (acl->a_entries[i].e_tag) {
  		case ACL_USER_OBJ:
  		case ACL_GROUP_OBJ:
  		case ACL_MASK:
  		case ACL_OTHER:
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  			entry = (struct f2fs_acl_entry *)((char *)entry +
  					sizeof(struct f2fs_acl_entry_short));
  			break;
  
  		case ACL_USER:
  			acl->a_entries[i].e_uid =
  				make_kuid(&init_user_ns,
  						le32_to_cpu(entry->e_id));
  			entry = (struct f2fs_acl_entry *)((char *)entry +
  					sizeof(struct f2fs_acl_entry));
  			break;
  		case ACL_GROUP:
  			acl->a_entries[i].e_gid =
  				make_kgid(&init_user_ns,
  						le32_to_cpu(entry->e_id));
  			entry = (struct f2fs_acl_entry *)((char *)entry +
  					sizeof(struct f2fs_acl_entry));
  			break;
  		default:
  			goto fail;
  		}
  	}
  	if ((char *)entry != end)
  		goto fail;
  	return acl;
  fail:
  	posix_acl_release(acl);
  	return ERR_PTR(-EINVAL);
  }
1ecc0c5c5   Chao Yu   f2fs: support con...
109
110
  static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
  				const struct posix_acl *acl, size_t *size)
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
111
112
113
114
  {
  	struct f2fs_acl_header *f2fs_acl;
  	struct f2fs_acl_entry *entry;
  	int i;
1ecc0c5c5   Chao Yu   f2fs: support con...
115
116
117
  	f2fs_acl = f2fs_kmalloc(sbi, sizeof(struct f2fs_acl_header) +
  			acl->a_count * sizeof(struct f2fs_acl_entry),
  			GFP_NOFS);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
  	if (!f2fs_acl)
  		return ERR_PTR(-ENOMEM);
  
  	f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION);
  	entry = (struct f2fs_acl_entry *)(f2fs_acl + 1);
  
  	for (i = 0; i < acl->a_count; i++) {
  
  		entry->e_tag  = cpu_to_le16(acl->a_entries[i].e_tag);
  		entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm);
  
  		switch (acl->a_entries[i].e_tag) {
  		case ACL_USER:
  			entry->e_id = cpu_to_le32(
  					from_kuid(&init_user_ns,
  						acl->a_entries[i].e_uid));
  			entry = (struct f2fs_acl_entry *)((char *)entry +
  					sizeof(struct f2fs_acl_entry));
  			break;
  		case ACL_GROUP:
  			entry->e_id = cpu_to_le32(
  					from_kgid(&init_user_ns,
  						acl->a_entries[i].e_gid));
  			entry = (struct f2fs_acl_entry *)((char *)entry +
  					sizeof(struct f2fs_acl_entry));
  			break;
  		case ACL_USER_OBJ:
  		case ACL_GROUP_OBJ:
  		case ACL_MASK:
  		case ACL_OTHER:
  			entry = (struct f2fs_acl_entry *)((char *)entry +
  					sizeof(struct f2fs_acl_entry_short));
  			break;
  		default:
  			goto fail;
  		}
  	}
  	*size = f2fs_acl_size(acl->a_count);
  	return (void *)f2fs_acl;
  
  fail:
  	kfree(f2fs_acl);
  	return ERR_PTR(-EINVAL);
  }
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
162
163
  static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
  						struct page *dpage)
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
164
  {
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
165
166
167
168
  	int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
  	void *value = NULL;
  	struct posix_acl *acl;
  	int retval;
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
169
170
  	if (type == ACL_TYPE_ACCESS)
  		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
171
  	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
172
  	if (retval > 0) {
1ecc0c5c5   Chao Yu   f2fs: support con...
173
  		value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
174
175
  		if (!value)
  			return ERR_PTR(-ENOMEM);
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
176
177
  		retval = f2fs_getxattr(inode, name_index, "", value,
  							retval, dpage);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
178
  	}
c1b75eabe   Jaegeuk Kim   f2fs: avoid null ...
179
  	if (retval > 0)
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
180
  		acl = f2fs_acl_from_disk(value, retval);
c1b75eabe   Jaegeuk Kim   f2fs: avoid null ...
181
182
183
184
  	else if (retval == -ENODATA)
  		acl = NULL;
  	else
  		acl = ERR_PTR(retval);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
185
  	kfree(value);
c1b75eabe   Jaegeuk Kim   f2fs: avoid null ...
186

af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
187
188
  	return acl;
  }
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
189
190
191
192
  struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
  {
  	return __f2fs_get_acl(inode, type, NULL);
  }
a6dda0e63   Christoph Hellwig   f2fs: use generic...
193
  static int __f2fs_set_acl(struct inode *inode, int type,
2ed2d5b33   Jaegeuk Kim   f2fs: fix a deadl...
194
  			struct posix_acl *acl, struct page *ipage)
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
195
  {
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
196
197
198
199
  	int name_index;
  	void *value = NULL;
  	size_t size = 0;
  	int error;
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
200
201
202
203
  	switch (type) {
  	case ACL_TYPE_ACCESS:
  		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
  		if (acl) {
073931017   Jan Kara   posix_acl: Clear ...
204
205
  			error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
  			if (error)
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
206
  				return error;
91942321e   Jaegeuk Kim   f2fs: use inode p...
207
  			set_acl_inode(inode, inode->i_mode);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  		}
  		break;
  
  	case ACL_TYPE_DEFAULT:
  		name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
  		if (!S_ISDIR(inode->i_mode))
  			return acl ? -EACCES : 0;
  		break;
  
  	default:
  		return -EINVAL;
  	}
  
  	if (acl) {
1ecc0c5c5   Chao Yu   f2fs: support con...
222
  		value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
223
  		if (IS_ERR(value)) {
91942321e   Jaegeuk Kim   f2fs: use inode p...
224
  			clear_inode_flag(inode, FI_ACL_MODE);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
225
226
227
  			return (int)PTR_ERR(value);
  		}
  	}
c02745ef6   Jaegeuk Kim   f2fs: pass flags ...
228
  	error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
229
230
231
232
  
  	kfree(value);
  	if (!error)
  		set_cached_acl(inode, type, acl);
91942321e   Jaegeuk Kim   f2fs: use inode p...
233
  	clear_inode_flag(inode, FI_ACL_MODE);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
234
235
  	return error;
  }
a6dda0e63   Christoph Hellwig   f2fs: use generic...
236
  int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
237
  {
a6dda0e63   Christoph Hellwig   f2fs: use generic...
238
  	return __f2fs_set_acl(inode, type, acl, NULL);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
239
  }
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  /*
   * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
   * are copied from posix_acl.c
   */
  static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
  							gfp_t flags)
  {
  	struct posix_acl *clone = NULL;
  
  	if (acl) {
  		int size = sizeof(struct posix_acl) + acl->a_count *
  				sizeof(struct posix_acl_entry);
  		clone = kmemdup(acl, size, flags);
  		if (clone)
  			atomic_set(&clone->a_refcount, 1);
  	}
  	return clone;
  }
  
  static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
  {
  	struct posix_acl_entry *pa, *pe;
  	struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
  	umode_t mode = *mode_p;
  	int not_equiv = 0;
  
  	/* assert(atomic_read(acl->a_refcount) == 1); */
  
  	FOREACH_ACL_ENTRY(pa, acl, pe) {
  		switch(pa->e_tag) {
  		case ACL_USER_OBJ:
  			pa->e_perm &= (mode >> 6) | ~S_IRWXO;
  			mode &= (pa->e_perm << 6) | ~S_IRWXU;
  			break;
  
  		case ACL_USER:
  		case ACL_GROUP:
  			not_equiv = 1;
  			break;
  
  		case ACL_GROUP_OBJ:
  			group_obj = pa;
  			break;
  
  		case ACL_OTHER:
  			pa->e_perm &= mode | ~S_IRWXO;
  			mode &= pa->e_perm | ~S_IRWXO;
  			break;
  
  		case ACL_MASK:
  			mask_obj = pa;
  			not_equiv = 1;
  			break;
  
  		default:
  			return -EIO;
  		}
  	}
  
  	if (mask_obj) {
  		mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
  		mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
  	} else {
  		if (!group_obj)
  			return -EIO;
  		group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
  		mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
  	}
  
  	*mode_p = (*mode_p & ~S_IRWXUGO) | mode;
          return not_equiv;
  }
  
  static int f2fs_acl_create(struct inode *dir, umode_t *mode,
  		struct posix_acl **default_acl, struct posix_acl **acl,
  		struct page *dpage)
  {
  	struct posix_acl *p;
272e083f7   Chao Yu   f2fs: make posix_...
318
  	struct posix_acl *clone;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
319
  	int ret;
272e083f7   Chao Yu   f2fs: make posix_...
320
321
  	*acl = NULL;
  	*default_acl = NULL;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
322
  	if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
272e083f7   Chao Yu   f2fs: make posix_...
323
  		return 0;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
324
325
  
  	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
272e083f7   Chao Yu   f2fs: make posix_...
326
327
328
  	if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
  		*mode &= ~current_umask();
  		return 0;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
329
  	}
272e083f7   Chao Yu   f2fs: make posix_...
330
331
  	if (IS_ERR(p))
  		return PTR_ERR(p);
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
332

272e083f7   Chao Yu   f2fs: make posix_...
333
334
  	clone = f2fs_acl_clone(p, GFP_NOFS);
  	if (!clone)
83dfe53c1   Chao Yu   f2fs: fix referen...
335
  		goto no_mem;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
336

272e083f7   Chao Yu   f2fs: make posix_...
337
  	ret = f2fs_acl_create_masq(clone, mode);
83dfe53c1   Chao Yu   f2fs: fix referen...
338
339
  	if (ret < 0)
  		goto no_mem_clone;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
340

272e083f7   Chao Yu   f2fs: make posix_...
341
342
343
344
  	if (ret == 0)
  		posix_acl_release(clone);
  	else
  		*acl = clone;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
345

272e083f7   Chao Yu   f2fs: make posix_...
346
  	if (!S_ISDIR(*mode))
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
347
  		posix_acl_release(p);
272e083f7   Chao Yu   f2fs: make posix_...
348
  	else
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
349
  		*default_acl = p;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
350

bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
351
  	return 0;
83dfe53c1   Chao Yu   f2fs: fix referen...
352
353
  
  no_mem_clone:
272e083f7   Chao Yu   f2fs: make posix_...
354
  	posix_acl_release(clone);
83dfe53c1   Chao Yu   f2fs: fix referen...
355
356
357
  no_mem:
  	posix_acl_release(p);
  	return -ENOMEM;
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
358
359
360
361
  }
  
  int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
  							struct page *dpage)
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
362
  {
bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
363
  	struct posix_acl *default_acl = NULL, *acl = NULL;
a6dda0e63   Christoph Hellwig   f2fs: use generic...
364
  	int error = 0;
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
365

bce8d1120   Jaegeuk Kim   f2fs: avoid deadl...
366
  	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
367
368
  	if (error)
  		return error;
b8b60e1a6   Jaegeuk Kim   f2fs: clean up ac...
369

b56ab837a   Jaegeuk Kim   f2fs: avoid mark_...
370
  	f2fs_mark_inode_dirty_sync(inode);
205b98221   Jaegeuk Kim   f2fs: call mark_i...
371

a6dda0e63   Christoph Hellwig   f2fs: use generic...
372
373
374
375
376
377
  	if (default_acl) {
  		error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
  				       ipage);
  		posix_acl_release(default_acl);
  	}
  	if (acl) {
3b6709b77   Kinglong Mee   f2fs: fix a bug o...
378
  		if (!error)
a6dda0e63   Christoph Hellwig   f2fs: use generic...
379
380
381
  			error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl,
  					       ipage);
  		posix_acl_release(acl);
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
382
  	}
af48b85b8   Jaegeuk Kim   f2fs: add xattr a...
383
384
  	return error;
  }