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
= 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
= 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_update_mode(inode
, &inode
->i_mode
, &acl
);
220 set_acl_inode(fi
, inode
->i_mode
);
224 case ACL_TYPE_DEFAULT
:
225 name_index
= F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT
;
226 if (!S_ISDIR(inode
->i_mode
))
227 return acl
? -EACCES
: 0;
235 value
= f2fs_acl_to_disk(acl
, &size
);
237 clear_inode_flag(fi
, FI_ACL_MODE
);
238 return (int)PTR_ERR(value
);
242 error
= f2fs_setxattr(inode
, name_index
, "", value
, size
, ipage
, 0);
246 set_cached_acl(inode
, type
, acl
);
248 clear_inode_flag(fi
, FI_ACL_MODE
);
252 int f2fs_set_acl(struct inode
*inode
, struct posix_acl
*acl
, int type
)
254 return __f2fs_set_acl(inode
, type
, acl
, NULL
);
258 * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
259 * are copied from posix_acl.c
261 static struct posix_acl
*f2fs_acl_clone(const struct posix_acl
*acl
,
264 struct posix_acl
*clone
= NULL
;
267 int size
= sizeof(struct posix_acl
) + acl
->a_count
*
268 sizeof(struct posix_acl_entry
);
269 clone
= kmemdup(acl
, size
, flags
);
271 atomic_set(&clone
->a_refcount
, 1);
276 static int f2fs_acl_create_masq(struct posix_acl
*acl
, umode_t
*mode_p
)
278 struct posix_acl_entry
*pa
, *pe
;
279 struct posix_acl_entry
*group_obj
= NULL
, *mask_obj
= NULL
;
280 umode_t mode
= *mode_p
;
283 /* assert(atomic_read(acl->a_refcount) == 1); */
285 FOREACH_ACL_ENTRY(pa
, acl
, pe
) {
288 pa
->e_perm
&= (mode
>> 6) | ~S_IRWXO
;
289 mode
&= (pa
->e_perm
<< 6) | ~S_IRWXU
;
302 pa
->e_perm
&= mode
| ~S_IRWXO
;
303 mode
&= pa
->e_perm
| ~S_IRWXO
;
317 mask_obj
->e_perm
&= (mode
>> 3) | ~S_IRWXO
;
318 mode
&= (mask_obj
->e_perm
<< 3) | ~S_IRWXG
;
322 group_obj
->e_perm
&= (mode
>> 3) | ~S_IRWXO
;
323 mode
&= (group_obj
->e_perm
<< 3) | ~S_IRWXG
;
326 *mode_p
= (*mode_p
& ~S_IRWXUGO
) | mode
;
330 static int f2fs_acl_create(struct inode
*dir
, umode_t
*mode
,
331 struct posix_acl
**default_acl
, struct posix_acl
**acl
,
337 if (S_ISLNK(*mode
) || !IS_POSIXACL(dir
))
340 p
= __f2fs_get_acl(dir
, ACL_TYPE_DEFAULT
, dpage
);
342 if (p
== ERR_PTR(-EOPNOTSUPP
))
350 *acl
= f2fs_acl_clone(p
, GFP_NOFS
);
354 ret
= f2fs_acl_create_masq(*acl
, mode
);
359 posix_acl_release(*acl
);
363 if (!S_ISDIR(*mode
)) {
364 posix_acl_release(p
);
372 *mode
&= ~current_umask();
379 posix_acl_release(*acl
);
381 posix_acl_release(p
);
385 int f2fs_init_acl(struct inode
*inode
, struct inode
*dir
, struct page
*ipage
,
388 struct posix_acl
*default_acl
= NULL
, *acl
= NULL
;
391 error
= f2fs_acl_create(dir
, &inode
->i_mode
, &default_acl
, &acl
, dpage
);
396 error
= __f2fs_set_acl(inode
, ACL_TYPE_DEFAULT
, default_acl
,
398 posix_acl_release(default_acl
);
402 error
= __f2fs_set_acl(inode
, ACL_TYPE_ACCESS
, acl
,
404 posix_acl_release(acl
);