4 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
12 * Convert from filesystem to in-memory representation.
14 static struct posix_acl
*
15 ext3_acl_from_disk(const void *value
, size_t size
)
17 const char *end
= (char *)value
+ size
;
19 struct posix_acl
*acl
;
23 if (size
< sizeof(ext3_acl_header
))
24 return ERR_PTR(-EINVAL
);
25 if (((ext3_acl_header
*)value
)->a_version
!=
26 cpu_to_le32(EXT3_ACL_VERSION
))
27 return ERR_PTR(-EINVAL
);
28 value
= (char *)value
+ sizeof(ext3_acl_header
);
29 count
= ext3_acl_count(size
);
31 return ERR_PTR(-EINVAL
);
34 acl
= posix_acl_alloc(count
, GFP_NOFS
);
36 return ERR_PTR(-ENOMEM
);
37 for (n
=0; n
< count
; n
++) {
38 ext3_acl_entry
*entry
=
39 (ext3_acl_entry
*)value
;
40 if ((char *)value
+ sizeof(ext3_acl_entry_short
) > end
)
42 acl
->a_entries
[n
].e_tag
= le16_to_cpu(entry
->e_tag
);
43 acl
->a_entries
[n
].e_perm
= le16_to_cpu(entry
->e_perm
);
44 switch(acl
->a_entries
[n
].e_tag
) {
49 value
= (char *)value
+
50 sizeof(ext3_acl_entry_short
);
51 acl
->a_entries
[n
].e_id
= ACL_UNDEFINED_ID
;
56 value
= (char *)value
+ sizeof(ext3_acl_entry
);
57 if ((char *)value
> end
)
59 acl
->a_entries
[n
].e_id
=
60 le32_to_cpu(entry
->e_id
);
72 posix_acl_release(acl
);
73 return ERR_PTR(-EINVAL
);
77 * Convert from in-memory to filesystem representation.
80 ext3_acl_to_disk(const struct posix_acl
*acl
, size_t *size
)
82 ext3_acl_header
*ext_acl
;
86 *size
= ext3_acl_size(acl
->a_count
);
87 ext_acl
= kmalloc(sizeof(ext3_acl_header
) + acl
->a_count
*
88 sizeof(ext3_acl_entry
), GFP_NOFS
);
90 return ERR_PTR(-ENOMEM
);
91 ext_acl
->a_version
= cpu_to_le32(EXT3_ACL_VERSION
);
92 e
= (char *)ext_acl
+ sizeof(ext3_acl_header
);
93 for (n
=0; n
< acl
->a_count
; n
++) {
94 ext3_acl_entry
*entry
= (ext3_acl_entry
*)e
;
95 entry
->e_tag
= cpu_to_le16(acl
->a_entries
[n
].e_tag
);
96 entry
->e_perm
= cpu_to_le16(acl
->a_entries
[n
].e_perm
);
97 switch(acl
->a_entries
[n
].e_tag
) {
101 cpu_to_le32(acl
->a_entries
[n
].e_id
);
102 e
+= sizeof(ext3_acl_entry
);
109 e
+= sizeof(ext3_acl_entry_short
);
116 return (char *)ext_acl
;
120 return ERR_PTR(-EINVAL
);
124 * Inode operation get_posix_acl().
126 * inode->i_mutex: don't care
129 ext3_get_acl(struct inode
*inode
, int type
)
133 struct posix_acl
*acl
;
136 if (!test_opt(inode
->i_sb
, POSIX_ACL
))
139 acl
= get_cached_acl(inode
, type
);
140 if (acl
!= ACL_NOT_CACHED
)
144 case ACL_TYPE_ACCESS
:
145 name_index
= EXT3_XATTR_INDEX_POSIX_ACL_ACCESS
;
147 case ACL_TYPE_DEFAULT
:
148 name_index
= EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT
;
154 retval
= ext3_xattr_get(inode
, name_index
, "", NULL
, 0);
156 value
= kmalloc(retval
, GFP_NOFS
);
158 return ERR_PTR(-ENOMEM
);
159 retval
= ext3_xattr_get(inode
, name_index
, "", value
, retval
);
162 acl
= ext3_acl_from_disk(value
, retval
);
163 else if (retval
== -ENODATA
|| retval
== -ENOSYS
)
166 acl
= ERR_PTR(retval
);
170 set_cached_acl(inode
, type
, acl
);
176 * Set the access or default ACL of an inode.
178 * inode->i_mutex: down unless called from ext3_new_inode
181 ext3_set_acl(handle_t
*handle
, struct inode
*inode
, int type
,
182 struct posix_acl
*acl
)
189 if (S_ISLNK(inode
->i_mode
))
193 case ACL_TYPE_ACCESS
:
194 name_index
= EXT3_XATTR_INDEX_POSIX_ACL_ACCESS
;
196 error
= posix_acl_equiv_mode(acl
, &inode
->i_mode
);
200 inode
->i_ctime
= CURRENT_TIME_SEC
;
201 ext3_mark_inode_dirty(handle
, inode
);
208 case ACL_TYPE_DEFAULT
:
209 name_index
= EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT
;
210 if (!S_ISDIR(inode
->i_mode
))
211 return acl
? -EACCES
: 0;
218 value
= ext3_acl_to_disk(acl
, &size
);
220 return (int)PTR_ERR(value
);
223 error
= ext3_xattr_set_handle(handle
, inode
, name_index
, "",
229 set_cached_acl(inode
, type
, acl
);
235 * Initialize the ACLs of a new inode. Called from ext3_new_inode.
238 * inode->i_mutex: up (access to inode is still exclusive)
241 ext3_init_acl(handle_t
*handle
, struct inode
*inode
, struct inode
*dir
)
243 struct posix_acl
*acl
= NULL
;
246 if (!S_ISLNK(inode
->i_mode
)) {
247 if (test_opt(dir
->i_sb
, POSIX_ACL
)) {
248 acl
= ext3_get_acl(dir
, ACL_TYPE_DEFAULT
);
253 inode
->i_mode
&= ~current_umask();
255 if (test_opt(inode
->i_sb
, POSIX_ACL
) && acl
) {
256 if (S_ISDIR(inode
->i_mode
)) {
257 error
= ext3_set_acl(handle
, inode
,
258 ACL_TYPE_DEFAULT
, acl
);
262 error
= posix_acl_create(&acl
, GFP_NOFS
, &inode
->i_mode
);
267 /* This is an extended ACL */
268 error
= ext3_set_acl(handle
, inode
, ACL_TYPE_ACCESS
, acl
);
272 posix_acl_release(acl
);
277 * Does chmod for an inode that may have an Access Control List. The
278 * inode->i_mode field must be updated to the desired value by the caller
279 * before calling this function.
280 * Returns 0 on success, or a negative error number.
282 * We change the ACL rather than storing some ACL entries in the file
283 * mode permission bits (which would be more efficient), because that
284 * would break once additional permissions (like ACL_APPEND, ACL_DELETE
285 * for directories) are added. There are no more bits available in the
288 * inode->i_mutex: down
291 ext3_acl_chmod(struct inode
*inode
)
293 struct posix_acl
*acl
;
298 if (S_ISLNK(inode
->i_mode
))
300 if (!test_opt(inode
->i_sb
, POSIX_ACL
))
302 acl
= ext3_get_acl(inode
, ACL_TYPE_ACCESS
);
303 if (IS_ERR(acl
) || !acl
)
305 error
= posix_acl_chmod(&acl
, GFP_KERNEL
, inode
->i_mode
);
309 handle
= ext3_journal_start(inode
,
310 EXT3_DATA_TRANS_BLOCKS(inode
->i_sb
));
311 if (IS_ERR(handle
)) {
312 error
= PTR_ERR(handle
);
313 ext3_std_error(inode
->i_sb
, error
);
316 error
= ext3_set_acl(handle
, inode
, ACL_TYPE_ACCESS
, acl
);
317 ext3_journal_stop(handle
);
318 if (error
== -ENOSPC
&&
319 ext3_should_retry_alloc(inode
->i_sb
, &retries
))
322 posix_acl_release(acl
);
327 * Extended attribute handlers
330 ext3_xattr_list_acl_access(struct dentry
*dentry
, char *list
, size_t list_len
,
331 const char *name
, size_t name_len
, int type
)
333 const size_t size
= sizeof(POSIX_ACL_XATTR_ACCESS
);
335 if (!test_opt(dentry
->d_sb
, POSIX_ACL
))
337 if (list
&& size
<= list_len
)
338 memcpy(list
, POSIX_ACL_XATTR_ACCESS
, size
);
343 ext3_xattr_list_acl_default(struct dentry
*dentry
, char *list
, size_t list_len
,
344 const char *name
, size_t name_len
, int type
)
346 const size_t size
= sizeof(POSIX_ACL_XATTR_DEFAULT
);
348 if (!test_opt(dentry
->d_sb
, POSIX_ACL
))
350 if (list
&& size
<= list_len
)
351 memcpy(list
, POSIX_ACL_XATTR_DEFAULT
, size
);
356 ext3_xattr_get_acl(struct dentry
*dentry
, const char *name
, void *buffer
,
357 size_t size
, int type
)
359 struct posix_acl
*acl
;
362 if (strcmp(name
, "") != 0)
364 if (!test_opt(dentry
->d_sb
, POSIX_ACL
))
367 acl
= ext3_get_acl(dentry
->d_inode
, type
);
372 error
= posix_acl_to_xattr(acl
, buffer
, size
);
373 posix_acl_release(acl
);
379 ext3_xattr_set_acl(struct dentry
*dentry
, const char *name
, const void *value
,
380 size_t size
, int flags
, int type
)
382 struct inode
*inode
= dentry
->d_inode
;
384 struct posix_acl
*acl
;
385 int error
, retries
= 0;
387 if (strcmp(name
, "") != 0)
389 if (!test_opt(inode
->i_sb
, POSIX_ACL
))
391 if (!inode_owner_or_capable(inode
))
395 acl
= posix_acl_from_xattr(value
, size
);
399 error
= posix_acl_valid(acl
);
401 goto release_and_out
;
407 handle
= ext3_journal_start(inode
, EXT3_DATA_TRANS_BLOCKS(inode
->i_sb
));
409 return PTR_ERR(handle
);
410 error
= ext3_set_acl(handle
, inode
, type
, acl
);
411 ext3_journal_stop(handle
);
412 if (error
== -ENOSPC
&& ext3_should_retry_alloc(inode
->i_sb
, &retries
))
416 posix_acl_release(acl
);
420 const struct xattr_handler ext3_xattr_acl_access_handler
= {
421 .prefix
= POSIX_ACL_XATTR_ACCESS
,
422 .flags
= ACL_TYPE_ACCESS
,
423 .list
= ext3_xattr_list_acl_access
,
424 .get
= ext3_xattr_get_acl
,
425 .set
= ext3_xattr_set_acl
,
428 const struct xattr_handler ext3_xattr_acl_default_handler
= {
429 .prefix
= POSIX_ACL_XATTR_DEFAULT
,
430 .flags
= ACL_TYPE_DEFAULT
,
431 .list
= ext3_xattr_list_acl_default
,
432 .get
= ext3_xattr_get_acl
,
433 .set
= ext3_xattr_set_acl
,