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(const struct posix_acl
*acl
, size_t *size
)
114 struct f2fs_acl_header
*f2fs_acl
;
115 struct f2fs_acl_entry
*entry
;
118 f2fs_acl
= f2fs_kmalloc(sizeof(struct f2fs_acl_header
) + acl
->a_count
*
119 sizeof(struct f2fs_acl_entry
), GFP_NOFS
);
121 return ERR_PTR(-ENOMEM
);
123 f2fs_acl
->a_version
= cpu_to_le32(F2FS_ACL_VERSION
);
124 entry
= (struct f2fs_acl_entry
*)(f2fs_acl
+ 1);
126 for (i
= 0; i
< acl
->a_count
; i
++) {
128 entry
->e_tag
= cpu_to_le16(acl
->a_entries
[i
].e_tag
);
129 entry
->e_perm
= cpu_to_le16(acl
->a_entries
[i
].e_perm
);
131 switch (acl
->a_entries
[i
].e_tag
) {
133 entry
->e_id
= cpu_to_le32(
134 from_kuid(&init_user_ns
,
135 acl
->a_entries
[i
].e_uid
));
136 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
137 sizeof(struct f2fs_acl_entry
));
140 entry
->e_id
= cpu_to_le32(
141 from_kgid(&init_user_ns
,
142 acl
->a_entries
[i
].e_gid
));
143 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
144 sizeof(struct f2fs_acl_entry
));
150 entry
= (struct f2fs_acl_entry
*)((char *)entry
+
151 sizeof(struct f2fs_acl_entry_short
));
157 *size
= f2fs_acl_size(acl
->a_count
);
158 return (void *)f2fs_acl
;
162 return ERR_PTR(-EINVAL
);
165 static struct posix_acl
*__f2fs_get_acl(struct inode
*inode
, int type
,
168 int name_index
= F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT
;
170 struct posix_acl
*acl
;
173 if (type
== ACL_TYPE_ACCESS
)
174 name_index
= F2FS_XATTR_INDEX_POSIX_ACL_ACCESS
;
176 retval
= f2fs_getxattr(inode
, name_index
, "", NULL
, 0, dpage
);
178 value
= f2fs_kmalloc(retval
, GFP_F2FS_ZERO
);
180 return ERR_PTR(-ENOMEM
);
181 retval
= f2fs_getxattr(inode
, name_index
, "", value
,
186 acl
= f2fs_acl_from_disk(value
, retval
);
187 else if (retval
== -ENODATA
)
190 acl
= ERR_PTR(retval
);
194 set_cached_acl(inode
, type
, acl
);
199 struct posix_acl
*f2fs_get_acl(struct inode
*inode
, int type
)
201 return __f2fs_get_acl(inode
, type
, NULL
);
204 static int __f2fs_set_acl(struct inode
*inode
, int type
,
205 struct posix_acl
*acl
, struct page
*ipage
)
207 struct f2fs_inode_info
*fi
= F2FS_I(inode
);
214 case ACL_TYPE_ACCESS
:
215 name_index
= F2FS_XATTR_INDEX_POSIX_ACL_ACCESS
;
217 error
= posix_acl_equiv_mode(acl
, &inode
->i_mode
);
220 set_acl_inode(fi
, inode
->i_mode
);
226 case ACL_TYPE_DEFAULT
:
227 name_index
= F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT
;
228 if (!S_ISDIR(inode
->i_mode
))
229 return acl
? -EACCES
: 0;
237 value
= f2fs_acl_to_disk(acl
, &size
);
239 clear_inode_flag(fi
, FI_ACL_MODE
);
240 return (int)PTR_ERR(value
);
244 error
= f2fs_setxattr(inode
, name_index
, "", value
, size
, ipage
, 0);
248 set_cached_acl(inode
, type
, acl
);
250 clear_inode_flag(fi
, FI_ACL_MODE
);
254 int f2fs_set_acl(struct inode
*inode
, struct posix_acl
*acl
, int type
)
256 return __f2fs_set_acl(inode
, type
, acl
, NULL
);
260 * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
261 * are copied from posix_acl.c
263 static struct posix_acl
*f2fs_acl_clone(const struct posix_acl
*acl
,
266 struct posix_acl
*clone
= NULL
;
269 int size
= sizeof(struct posix_acl
) + acl
->a_count
*
270 sizeof(struct posix_acl_entry
);
271 clone
= kmemdup(acl
, size
, flags
);
273 atomic_set(&clone
->a_refcount
, 1);
278 static int f2fs_acl_create_masq(struct posix_acl
*acl
, umode_t
*mode_p
)
280 struct posix_acl_entry
*pa
, *pe
;
281 struct posix_acl_entry
*group_obj
= NULL
, *mask_obj
= NULL
;
282 umode_t mode
= *mode_p
;
285 /* assert(atomic_read(acl->a_refcount) == 1); */
287 FOREACH_ACL_ENTRY(pa
, acl
, pe
) {
290 pa
->e_perm
&= (mode
>> 6) | ~S_IRWXO
;
291 mode
&= (pa
->e_perm
<< 6) | ~S_IRWXU
;
304 pa
->e_perm
&= mode
| ~S_IRWXO
;
305 mode
&= pa
->e_perm
| ~S_IRWXO
;
319 mask_obj
->e_perm
&= (mode
>> 3) | ~S_IRWXO
;
320 mode
&= (mask_obj
->e_perm
<< 3) | ~S_IRWXG
;
324 group_obj
->e_perm
&= (mode
>> 3) | ~S_IRWXO
;
325 mode
&= (group_obj
->e_perm
<< 3) | ~S_IRWXG
;
328 *mode_p
= (*mode_p
& ~S_IRWXUGO
) | mode
;
332 static int f2fs_acl_create(struct inode
*dir
, umode_t
*mode
,
333 struct posix_acl
**default_acl
, struct posix_acl
**acl
,
337 struct posix_acl
*clone
;
343 if (S_ISLNK(*mode
) || !IS_POSIXACL(dir
))
346 p
= __f2fs_get_acl(dir
, ACL_TYPE_DEFAULT
, dpage
);
347 if (!p
|| p
== ERR_PTR(-EOPNOTSUPP
)) {
348 *mode
&= ~current_umask();
354 clone
= f2fs_acl_clone(p
, GFP_NOFS
);
358 ret
= f2fs_acl_create_masq(clone
, mode
);
363 posix_acl_release(clone
);
368 posix_acl_release(p
);
375 posix_acl_release(clone
);
377 posix_acl_release(p
);
381 int f2fs_init_acl(struct inode
*inode
, struct inode
*dir
, struct page
*ipage
,
384 struct posix_acl
*default_acl
= NULL
, *acl
= NULL
;
387 error
= f2fs_acl_create(dir
, &inode
->i_mode
, &default_acl
, &acl
, dpage
);
392 error
= __f2fs_set_acl(inode
, ACL_TYPE_DEFAULT
, default_acl
,
394 posix_acl_release(default_acl
);
398 error
= __f2fs_set_acl(inode
, ACL_TYPE_ACCESS
, acl
,
400 posix_acl_release(acl
);