0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/init.h>
0009 #include <linux/sched.h>
0010 #include <linux/slab.h>
0011 #include <linux/fs.h>
0012 #include "ext2.h"
0013 #include "xattr.h"
0014 #include "acl.h"
0015
0016
0017
0018
0019 static struct posix_acl *
0020 ext2_acl_from_disk(const void *value, size_t size)
0021 {
0022 const char *end = (char *)value + size;
0023 int n, count;
0024 struct posix_acl *acl;
0025
0026 if (!value)
0027 return NULL;
0028 if (size < sizeof(ext2_acl_header))
0029 return ERR_PTR(-EINVAL);
0030 if (((ext2_acl_header *)value)->a_version !=
0031 cpu_to_le32(EXT2_ACL_VERSION))
0032 return ERR_PTR(-EINVAL);
0033 value = (char *)value + sizeof(ext2_acl_header);
0034 count = ext2_acl_count(size);
0035 if (count < 0)
0036 return ERR_PTR(-EINVAL);
0037 if (count == 0)
0038 return NULL;
0039 acl = posix_acl_alloc(count, GFP_KERNEL);
0040 if (!acl)
0041 return ERR_PTR(-ENOMEM);
0042 for (n=0; n < count; n++) {
0043 ext2_acl_entry *entry =
0044 (ext2_acl_entry *)value;
0045 if ((char *)value + sizeof(ext2_acl_entry_short) > end)
0046 goto fail;
0047 acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
0048 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
0049 switch(acl->a_entries[n].e_tag) {
0050 case ACL_USER_OBJ:
0051 case ACL_GROUP_OBJ:
0052 case ACL_MASK:
0053 case ACL_OTHER:
0054 value = (char *)value +
0055 sizeof(ext2_acl_entry_short);
0056 break;
0057
0058 case ACL_USER:
0059 value = (char *)value + sizeof(ext2_acl_entry);
0060 if ((char *)value > end)
0061 goto fail;
0062 acl->a_entries[n].e_uid =
0063 make_kuid(&init_user_ns,
0064 le32_to_cpu(entry->e_id));
0065 break;
0066 case ACL_GROUP:
0067 value = (char *)value + sizeof(ext2_acl_entry);
0068 if ((char *)value > end)
0069 goto fail;
0070 acl->a_entries[n].e_gid =
0071 make_kgid(&init_user_ns,
0072 le32_to_cpu(entry->e_id));
0073 break;
0074
0075 default:
0076 goto fail;
0077 }
0078 }
0079 if (value != end)
0080 goto fail;
0081 return acl;
0082
0083 fail:
0084 posix_acl_release(acl);
0085 return ERR_PTR(-EINVAL);
0086 }
0087
0088
0089
0090
0091 static void *
0092 ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
0093 {
0094 ext2_acl_header *ext_acl;
0095 char *e;
0096 size_t n;
0097
0098 *size = ext2_acl_size(acl->a_count);
0099 ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count *
0100 sizeof(ext2_acl_entry), GFP_KERNEL);
0101 if (!ext_acl)
0102 return ERR_PTR(-ENOMEM);
0103 ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
0104 e = (char *)ext_acl + sizeof(ext2_acl_header);
0105 for (n=0; n < acl->a_count; n++) {
0106 const struct posix_acl_entry *acl_e = &acl->a_entries[n];
0107 ext2_acl_entry *entry = (ext2_acl_entry *)e;
0108 entry->e_tag = cpu_to_le16(acl_e->e_tag);
0109 entry->e_perm = cpu_to_le16(acl_e->e_perm);
0110 switch(acl_e->e_tag) {
0111 case ACL_USER:
0112 entry->e_id = cpu_to_le32(
0113 from_kuid(&init_user_ns, acl_e->e_uid));
0114 e += sizeof(ext2_acl_entry);
0115 break;
0116 case ACL_GROUP:
0117 entry->e_id = cpu_to_le32(
0118 from_kgid(&init_user_ns, acl_e->e_gid));
0119 e += sizeof(ext2_acl_entry);
0120 break;
0121
0122 case ACL_USER_OBJ:
0123 case ACL_GROUP_OBJ:
0124 case ACL_MASK:
0125 case ACL_OTHER:
0126 e += sizeof(ext2_acl_entry_short);
0127 break;
0128
0129 default:
0130 goto fail;
0131 }
0132 }
0133 return (char *)ext_acl;
0134
0135 fail:
0136 kfree(ext_acl);
0137 return ERR_PTR(-EINVAL);
0138 }
0139
0140
0141
0142
0143 struct posix_acl *
0144 ext2_get_acl(struct inode *inode, int type, bool rcu)
0145 {
0146 int name_index;
0147 char *value = NULL;
0148 struct posix_acl *acl;
0149 int retval;
0150
0151 if (rcu)
0152 return ERR_PTR(-ECHILD);
0153
0154 switch (type) {
0155 case ACL_TYPE_ACCESS:
0156 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
0157 break;
0158 case ACL_TYPE_DEFAULT:
0159 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
0160 break;
0161 default:
0162 BUG();
0163 }
0164 retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
0165 if (retval > 0) {
0166 value = kmalloc(retval, GFP_KERNEL);
0167 if (!value)
0168 return ERR_PTR(-ENOMEM);
0169 retval = ext2_xattr_get(inode, name_index, "", value, retval);
0170 }
0171 if (retval > 0)
0172 acl = ext2_acl_from_disk(value, retval);
0173 else if (retval == -ENODATA || retval == -ENOSYS)
0174 acl = NULL;
0175 else
0176 acl = ERR_PTR(retval);
0177 kfree(value);
0178
0179 return acl;
0180 }
0181
0182 static int
0183 __ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
0184 {
0185 int name_index;
0186 void *value = NULL;
0187 size_t size = 0;
0188 int error;
0189
0190 switch(type) {
0191 case ACL_TYPE_ACCESS:
0192 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
0193 break;
0194
0195 case ACL_TYPE_DEFAULT:
0196 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
0197 if (!S_ISDIR(inode->i_mode))
0198 return acl ? -EACCES : 0;
0199 break;
0200
0201 default:
0202 return -EINVAL;
0203 }
0204 if (acl) {
0205 value = ext2_acl_to_disk(acl, &size);
0206 if (IS_ERR(value))
0207 return (int)PTR_ERR(value);
0208 }
0209
0210 error = ext2_xattr_set(inode, name_index, "", value, size, 0);
0211
0212 kfree(value);
0213 if (!error)
0214 set_cached_acl(inode, type, acl);
0215 return error;
0216 }
0217
0218
0219
0220
0221 int
0222 ext2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
0223 struct posix_acl *acl, int type)
0224 {
0225 int error;
0226 int update_mode = 0;
0227 umode_t mode = inode->i_mode;
0228
0229 if (type == ACL_TYPE_ACCESS && acl) {
0230 error = posix_acl_update_mode(&init_user_ns, inode, &mode,
0231 &acl);
0232 if (error)
0233 return error;
0234 update_mode = 1;
0235 }
0236 error = __ext2_set_acl(inode, acl, type);
0237 if (!error && update_mode) {
0238 inode->i_mode = mode;
0239 inode->i_ctime = current_time(inode);
0240 mark_inode_dirty(inode);
0241 }
0242 return error;
0243 }
0244
0245
0246
0247
0248
0249
0250
0251 int
0252 ext2_init_acl(struct inode *inode, struct inode *dir)
0253 {
0254 struct posix_acl *default_acl, *acl;
0255 int error;
0256
0257 error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
0258 if (error)
0259 return error;
0260
0261 if (default_acl) {
0262 error = __ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
0263 posix_acl_release(default_acl);
0264 } else {
0265 inode->i_default_acl = NULL;
0266 }
0267 if (acl) {
0268 if (!error)
0269 error = __ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);
0270 posix_acl_release(acl);
0271 } else {
0272 inode->i_acl = NULL;
0273 }
0274 return error;
0275 }