4 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
5 * http://www.samsung.com/
7 * Portions of this code from linux/fs/ext2/acl.c
9 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 #include <linux/f2fs_fs.h>
20 static inline size_t f2fs_acl_size(int count
)
23 return sizeof(struct f2fs_acl_header
) +
24 count
* sizeof(struct f2fs_acl_entry_short
);
26 return sizeof(struct f2fs_acl_header
) +
27 4 * sizeof(struct f2fs_acl_entry_short
) +
28 (count
- 4) * sizeof(struct f2fs_acl_entry
);
32 static inline int f2fs_acl_count(size_t size
)
35 size
-= sizeof(struct f2fs_acl_header
);
36 s
= size
- 4 * sizeof(struct f2fs_acl_entry_short
);
38 if (size
% sizeof(struct f2fs_acl_entry_short
))
40 return size
/ sizeof(struct f2fs_acl_entry_short
);
42 if (s
% sizeof(struct f2fs_acl_entry
))
44 return s
/ sizeof(struct f2fs_acl_entry
) + 4;
48 static struct posix_acl
*f2fs_acl_from_disk(const char *value
, size_t size
)
51 struct posix_acl
*acl
;
52 struct f2fs_acl_header
*hdr
= (struct f2fs_acl_header
*)value
;
53 struct f2fs_acl_entry
*entry
= (struct f2fs_acl_entry
*)(hdr
+ 1);
54 const char *end
= value
+ size
;
56 if (hdr
->a_version
!= cpu_to_le32(F2FS_ACL_VERSION
))
57 return ERR_PTR(-EINVAL
);
59 count
= f2fs_acl_count(size
);
61 return ERR_PTR(-EINVAL
);
65 acl
= posix_acl_alloc(count
, GFP_NOFS
);
67 return ERR_PTR(-ENOMEM
);
69 for (i
= 0; i
< count
; i
++) {
71 if ((char *)entry
> end
)
74 acl
->a_entries
[i
].e_tag
= le16_to_cpu(entry
->e_tag
);
75 acl
->a_entries
[i
].e_perm
= le16_to_cpu(entry
->e_perm
);
77 switch (acl
->a_entries
[i
].e_tag
) {
82 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
83 sizeof(struct f2fs_acl_entry_short
));
87 acl
->a_entries
[i
].e_uid
=
88 make_kuid(&init_user_ns
,
89 le32_to_cpu(entry
->e_id
));
90 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
91 sizeof(struct f2fs_acl_entry
));
94 acl
->a_entries
[i
].e_gid
=
95 make_kgid(&init_user_ns
,
96 le32_to_cpu(entry
->e_id
));
97 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
98 sizeof(struct f2fs_acl_entry
));
104 if ((char *)entry
!= end
)
108 posix_acl_release(acl
);
109 return ERR_PTR(-EINVAL
);
112 static void *f2fs_acl_to_disk(struct f2fs_sb_info
*sbi
,
113 const struct posix_acl
*acl
, size_t *size
)
115 struct f2fs_acl_header
*f2fs_acl
;
116 struct f2fs_acl_entry
*entry
;
119 f2fs_acl
= f2fs_kmalloc(sbi
, sizeof(struct f2fs_acl_header
) +
120 acl
->a_count
* sizeof(struct f2fs_acl_entry
),
123 return ERR_PTR(-ENOMEM
);
125 f2fs_acl
->a_version
= cpu_to_le32(F2FS_ACL_VERSION
);
126 entry
= (struct f2fs_acl_entry
*)(f2fs_acl
+ 1);
128 for (i
= 0; i
< acl
->a_count
; i
++) {
130 entry
->e_tag
= cpu_to_le16(acl
->a_entries
[i
].e_tag
);
131 entry
->e_perm
= cpu_to_le16(acl
->a_entries
[i
].e_perm
);
133 switch (acl
->a_entries
[i
].e_tag
) {
135 entry
->e_id
= cpu_to_le32(
136 from_kuid(&init_user_ns
,
137 acl
->a_entries
[i
].e_uid
));
138 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
139 sizeof(struct f2fs_acl_entry
));
142 entry
->e_id
= cpu_to_le32(
143 from_kgid(&init_user_ns
,
144 acl
->a_entries
[i
].e_gid
));
145 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
146 sizeof(struct f2fs_acl_entry
));
152 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
153 sizeof(struct f2fs_acl_entry_short
));
159 *size
= f2fs_acl_size(acl
->a_count
);
160 return (void *)f2fs_acl
;
164 return ERR_PTR(-EINVAL
);
167 static struct posix_acl
*__f2fs_get_acl(struct inode
*inode
, int type
,
170 int name_index
= F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT
;
172 struct posix_acl
*acl
;
175 if (type
== ACL_TYPE_ACCESS
)
176 name_index
= F2FS_XATTR_INDEX_POSIX_ACL_ACCESS
;
178 retval
= f2fs_getxattr(inode
, name_index
, "", NULL
, 0, dpage
);
180 value
= f2fs_kmalloc(F2FS_I_SB(inode
), retval
, GFP_F2FS_ZERO
);
182 return ERR_PTR(-ENOMEM
);
183 retval
= f2fs_getxattr(inode
, name_index
, "", value
,
188 acl
= f2fs_acl_from_disk(value
, retval
);
189 else if (retval
== -ENODATA
)
192 acl
= ERR_PTR(retval
);
198 struct posix_acl
*f2fs_get_acl(struct inode
*inode
, int type
)
200 return __f2fs_get_acl(inode
, type
, NULL
);
203 static int __f2fs_set_acl(struct inode
*inode
, int type
,
204 struct posix_acl
*acl
, struct page
*ipage
)
212 case ACL_TYPE_ACCESS
:
213 name_index
= F2FS_XATTR_INDEX_POSIX_ACL_ACCESS
;
215 error
= posix_acl_update_mode(inode
, &inode
->i_mode
, &acl
);
218 set_acl_inode(inode
, inode
->i_mode
);
222 case ACL_TYPE_DEFAULT
:
223 name_index
= F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT
;
224 if (!S_ISDIR(inode
->i_mode
))
225 return acl
? -EACCES
: 0;
233 value
= f2fs_acl_to_disk(F2FS_I_SB(inode
), acl
, &size
);
235 clear_inode_flag(inode
, FI_ACL_MODE
);
236 return (int)PTR_ERR(value
);
240 error
= f2fs_setxattr(inode
, name_index
, "", value
, size
, ipage
, 0);
244 set_cached_acl(inode
, type
, acl
);
246 clear_inode_flag(inode
, FI_ACL_MODE
);
250 int f2fs_set_acl(struct inode
*inode
, struct posix_acl
*acl
, int type
)
252 return __f2fs_set_acl(inode
, type
, acl
, NULL
);
256 * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
257 * are copied from posix_acl.c
259 static struct posix_acl
*f2fs_acl_clone(const struct posix_acl
*acl
,
262 struct posix_acl
*clone
= NULL
;
265 int size
= sizeof(struct posix_acl
) + acl
->a_count
*
266 sizeof(struct posix_acl_entry
);
267 clone
= kmemdup(acl
, size
, flags
);
269 atomic_set(&clone
->a_refcount
, 1);
274 static int f2fs_acl_create_masq(struct posix_acl
*acl
, umode_t
*mode_p
)
276 struct posix_acl_entry
*pa
, *pe
;
277 struct posix_acl_entry
*group_obj
= NULL
, *mask_obj
= NULL
;
278 umode_t mode
= *mode_p
;
281 /* assert(atomic_read(acl->a_refcount) == 1); */
283 FOREACH_ACL_ENTRY(pa
, acl
, pe
) {
286 pa
->e_perm
&= (mode
>> 6) | ~S_IRWXO
;
287 mode
&= (pa
->e_perm
<< 6) | ~S_IRWXU
;
300 pa
->e_perm
&= mode
| ~S_IRWXO
;
301 mode
&= pa
->e_perm
| ~S_IRWXO
;
315 mask_obj
->e_perm
&= (mode
>> 3) | ~S_IRWXO
;
316 mode
&= (mask_obj
->e_perm
<< 3) | ~S_IRWXG
;
320 group_obj
->e_perm
&= (mode
>> 3) | ~S_IRWXO
;
321 mode
&= (group_obj
->e_perm
<< 3) | ~S_IRWXG
;
324 *mode_p
= (*mode_p
& ~S_IRWXUGO
) | mode
;
328 static int f2fs_acl_create(struct inode
*dir
, umode_t
*mode
,
329 struct posix_acl
**default_acl
, struct posix_acl
**acl
,
333 struct posix_acl
*clone
;
339 if (S_ISLNK(*mode
) || !IS_POSIXACL(dir
))
342 p
= __f2fs_get_acl(dir
, ACL_TYPE_DEFAULT
, dpage
);
343 if (!p
|| p
== ERR_PTR(-EOPNOTSUPP
)) {
344 *mode
&= ~current_umask();
350 clone
= f2fs_acl_clone(p
, GFP_NOFS
);
354 ret
= f2fs_acl_create_masq(clone
, mode
);
359 posix_acl_release(clone
);
364 posix_acl_release(p
);
371 posix_acl_release(clone
);
373 posix_acl_release(p
);
377 int f2fs_init_acl(struct inode
*inode
, struct inode
*dir
, struct page
*ipage
,
380 struct posix_acl
*default_acl
= NULL
, *acl
= NULL
;
383 error
= f2fs_acl_create(dir
, &inode
->i_mode
, &default_acl
, &acl
, dpage
);
387 f2fs_mark_inode_dirty_sync(inode
);
390 error
= __f2fs_set_acl(inode
, ACL_TYPE_DEFAULT
, default_acl
,
392 posix_acl_release(default_acl
);
396 error
= __f2fs_set_acl(inode
, ACL_TYPE_ACCESS
, acl
,
398 posix_acl_release(acl
);