Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / fs / richacl_xattr.c
1 /*
2  * Copyright (C) 2006, 2010  Novell, Inc.
3  * Written by Andreas Gruenbacher <agruen@suse.de>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2, or (at your option) any
8  * later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  */
15
16 #include <linux/kernel.h>
17 #include <linux/fs.h>
18 #include <linux/slab.h>
19 #include <linux/module.h>
20 #include <linux/richacl_xattr.h>
21
22 MODULE_LICENSE("GPL");
23
24 /**
25  * richacl_from_xattr  -  convert a richacl xattr into the in-memory representation
26  */
27 struct richacl *
28 richacl_from_xattr(const void *value, size_t size)
29 {
30         const struct richacl_xattr *xattr_acl = value;
31         const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1);
32         struct richacl *acl;
33         struct richace *ace;
34         int count;
35
36         if (size < sizeof(struct richacl_xattr) ||
37             xattr_acl->a_version != ACL4_XATTR_VERSION ||
38             (xattr_acl->a_flags & ~ACL4_VALID_FLAGS))
39                 return ERR_PTR(-EINVAL);
40
41         count = le16_to_cpu(xattr_acl->a_count);
42         if (count > ACL4_XATTR_MAX_COUNT)
43                 return ERR_PTR(-EINVAL);
44
45         acl = richacl_alloc(count);
46         if (!acl)
47                 return ERR_PTR(-ENOMEM);
48
49         acl->a_flags = xattr_acl->a_flags;
50         acl->a_owner_mask = le32_to_cpu(xattr_acl->a_owner_mask);
51         if (acl->a_owner_mask & ~ACE4_VALID_MASK)
52                 goto fail_einval;
53         acl->a_group_mask = le32_to_cpu(xattr_acl->a_group_mask);
54         if (acl->a_group_mask & ~ACE4_VALID_MASK)
55                 goto fail_einval;
56         acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask);
57         if (acl->a_other_mask & ~ACE4_VALID_MASK)
58                 goto fail_einval;
59
60         richacl_for_each_entry(ace, acl) {
61                 const char *who = (void *)(xattr_ace + 1), *end;
62                 ssize_t used = (void *)who - value;
63
64                 if (used > size)
65                         goto fail_einval;
66                 end = memchr(who, 0, size - used);
67                 if (!end)
68                         goto fail_einval;
69
70                 ace->e_type = le16_to_cpu(xattr_ace->e_type);
71                 ace->e_flags = le16_to_cpu(xattr_ace->e_flags);
72                 ace->e_mask = le32_to_cpu(xattr_ace->e_mask);
73                 ace->u.e_id = le32_to_cpu(xattr_ace->e_id);
74
75                 if (ace->e_flags & ~ACE4_VALID_FLAGS)
76                         goto fail_einval;
77                 if (ace->e_type > ACE4_ACCESS_DENIED_ACE_TYPE ||
78                     (ace->e_mask & ~ACE4_VALID_MASK))
79                         goto fail_einval;
80
81                 if (who == end) {
82                         if (ace->u.e_id == -1)
83                                 goto fail_einval;  /* uid/gid needed */
84                 } else if (richace_set_who(ace, who))
85                         goto fail_einval;
86
87                 xattr_ace = (void *)who + ALIGN(end - who + 1, 4);
88         }
89
90         return acl;
91
92 fail_einval:
93         richacl_put(acl);
94         return ERR_PTR(-EINVAL);
95 }
96 EXPORT_SYMBOL_GPL(richacl_from_xattr);
97
98 /**
99  * richacl_xattr_size  -  compute the size of the xattr representation of @acl
100  */
101 size_t
102 richacl_xattr_size(const struct richacl *acl)
103 {
104         size_t size = sizeof(struct richacl_xattr);
105         const struct richace *ace;
106
107         richacl_for_each_entry(ace, acl) {
108                 size += sizeof(struct richace_xattr) +
109                         (richace_is_unix_id(ace) ? 4 :
110                          ALIGN(strlen(ace->u.e_who) + 1, 4));
111         }
112         return size;
113 }
114 EXPORT_SYMBOL_GPL(richacl_xattr_size);
115
116 /**
117  * richacl_to_xattr  -  convert @acl into its xattr representation
118  * @acl:        the richacl to convert
119  * @buffer:     buffer of size richacl_xattr_size(@acl) for the result
120  */
121 void
122 richacl_to_xattr(const struct richacl *acl, void *buffer)
123 {
124         struct richacl_xattr *xattr_acl = buffer;
125         struct richace_xattr *xattr_ace;
126         const struct richace *ace;
127
128         xattr_acl->a_version = ACL4_XATTR_VERSION;
129         xattr_acl->a_flags = acl->a_flags;
130         xattr_acl->a_count = cpu_to_le16(acl->a_count);
131
132         xattr_acl->a_owner_mask = cpu_to_le32(acl->a_owner_mask);
133         xattr_acl->a_group_mask = cpu_to_le32(acl->a_group_mask);
134         xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask);
135
136         xattr_ace = (void *)(xattr_acl + 1);
137         richacl_for_each_entry(ace, acl) {
138                 xattr_ace->e_type = cpu_to_le16(ace->e_type);
139                 xattr_ace->e_flags = cpu_to_le16(ace->e_flags &
140                         ACE4_VALID_FLAGS);
141                 xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
142                 if (richace_is_unix_id(ace)) {
143                         xattr_ace->e_id = cpu_to_le32(ace->u.e_id);
144                         memset(xattr_ace->e_who, 0, 4);
145                         xattr_ace = (void *)xattr_ace->e_who + 4;
146                 } else {
147                         int sz = ALIGN(strlen(ace->u.e_who) + 1, 4);
148
149                         xattr_ace->e_id = cpu_to_le32(-1);
150                         memset(xattr_ace->e_who + sz - 4, 0, 4);
151                         strcpy(xattr_ace->e_who, ace->u.e_who);
152                         xattr_ace = (void *)xattr_ace->e_who + sz;
153                 }
154         }
155 }
156 EXPORT_SYMBOL_GPL(richacl_to_xattr);