1 /* 2 * fs/nfs_common/nfsacl.c 3 * 4 * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de> 5 */ 6 7 /* 8 * The Solaris nfsacl protocol represents some ACLs slightly differently 9 * than POSIX 1003.1e draft 17 does (and we do): 10 * 11 * - Minimal ACLs always have an ACL_MASK entry, so they have 12 * four instead of three entries. 13 * - The ACL_MASK entry in such minimal ACLs always has the same 14 * permissions as the ACL_GROUP_OBJ entry. (In extended ACLs 15 * the ACL_MASK and ACL_GROUP_OBJ entries may differ.) 16 * - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ 17 * entries contain the identifiers of the owner and owning group. 18 * (In POSIX ACLs we always set them to ACL_UNDEFINED_ID). 19 * - ACL entries in the kernel are kept sorted in ascending order 20 * of (e_tag, e_id). Solaris ACLs are unsorted. 21 */ 22 23 #include <linux/module.h> 24 #include <linux/fs.h> 25 #include <linux/gfp.h> 26 #include <linux/sunrpc/xdr.h> 27 #include <linux/nfsacl.h> 28 #include <linux/nfs3.h> 29 #include <linux/sort.h> 30 31 MODULE_LICENSE("GPL"); 32 33 EXPORT_SYMBOL_GPL(nfsacl_encode); 34 EXPORT_SYMBOL_GPL(nfsacl_decode); 35 36 struct nfsacl_encode_desc { 37 struct xdr_array2_desc desc; 38 unsigned int count; 39 struct posix_acl *acl; 40 int typeflag; 41 uid_t uid; 42 gid_t gid; 43 }; 44 45 static int 46 xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem) 47 { 48 struct nfsacl_encode_desc *nfsacl_desc = 49 (struct nfsacl_encode_desc *) desc; 50 __be32 *p = elem; 51 52 struct posix_acl_entry *entry = 53 &nfsacl_desc->acl->a_entries[nfsacl_desc->count++]; 54 55 *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag); 56 switch(entry->e_tag) { 57 case ACL_USER_OBJ: 58 *p++ = htonl(nfsacl_desc->uid); 59 break; 60 case ACL_GROUP_OBJ: 61 *p++ = htonl(nfsacl_desc->gid); 62 break; 63 case ACL_USER: 64 case ACL_GROUP: 65 *p++ = htonl(entry->e_id); 66 break; 67 default: /* Solaris depends on that! */ 68 *p++ = 0; 69 break; 70 } 71 *p++ = htonl(entry->e_perm & S_IRWXO); 72 return 0; 73 } 74 75 unsigned int 76 nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode, 77 struct posix_acl *acl, int encode_entries, int typeflag) 78 { 79 int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0; 80 struct nfsacl_encode_desc nfsacl_desc = { 81 .desc = { 82 .elem_size = 12, 83 .array_len = encode_entries ? entries : 0, 84 .xcode = xdr_nfsace_encode, 85 }, 86 .acl = acl, 87 .typeflag = typeflag, 88 .uid = inode->i_uid, 89 .gid = inode->i_gid, 90 }; 91 int err; 92 struct posix_acl *acl2 = NULL; 93 94 if (entries > NFS_ACL_MAX_ENTRIES || 95 xdr_encode_word(buf, base, entries)) 96 return -EINVAL; 97 if (encode_entries && acl && acl->a_count == 3) { 98 /* Fake up an ACL_MASK entry. */ 99 acl2 = posix_acl_alloc(4, GFP_KERNEL); 100 if (!acl2) 101 return -ENOMEM; 102 /* Insert entries in canonical order: other orders seem 103 to confuse Solaris VxFS. */ 104 acl2->a_entries[0] = acl->a_entries[0]; /* ACL_USER_OBJ */ 105 acl2->a_entries[1] = acl->a_entries[1]; /* ACL_GROUP_OBJ */ 106 acl2->a_entries[2] = acl->a_entries[1]; /* ACL_MASK */ 107 acl2->a_entries[2].e_tag = ACL_MASK; 108 acl2->a_entries[3] = acl->a_entries[2]; /* ACL_OTHER */ 109 nfsacl_desc.acl = acl2; 110 } 111 err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc); 112 if (acl2) 113 posix_acl_release(acl2); 114 if (!err) 115 err = 8 + nfsacl_desc.desc.elem_size * 116 nfsacl_desc.desc.array_len; 117 return err; 118 } 119 120 struct nfsacl_decode_desc { 121 struct xdr_array2_desc desc; 122 unsigned int count; 123 struct posix_acl *acl; 124 }; 125 126 static int 127 xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem) 128 { 129 struct nfsacl_decode_desc *nfsacl_desc = 130 (struct nfsacl_decode_desc *) desc; 131 __be32 *p = elem; 132 struct posix_acl_entry *entry; 133 134 if (!nfsacl_desc->acl) { 135 if (desc->array_len > NFS_ACL_MAX_ENTRIES) 136 return -EINVAL; 137 nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL); 138 if (!nfsacl_desc->acl) 139 return -ENOMEM; 140 nfsacl_desc->count = 0; 141 } 142 143 entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++]; 144 entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT; 145 entry->e_id = ntohl(*p++); 146 entry->e_perm = ntohl(*p++); 147 148 switch(entry->e_tag) { 149 case ACL_USER_OBJ: 150 case ACL_USER: 151 case ACL_GROUP_OBJ: 152 case ACL_GROUP: 153 case ACL_OTHER: 154 if (entry->e_perm & ~S_IRWXO) 155 return -EINVAL; 156 break; 157 case ACL_MASK: 158 /* Solaris sometimes sets additonal bits in the mask */ 159 entry->e_perm &= S_IRWXO; 160 break; 161 default: 162 return -EINVAL; 163 } 164 165 return 0; 166 } 167 168 static int 169 cmp_acl_entry(const void *x, const void *y) 170 { 171 const struct posix_acl_entry *a = x, *b = y; 172 173 if (a->e_tag != b->e_tag) 174 return a->e_tag - b->e_tag; 175 else if (a->e_id > b->e_id) 176 return 1; 177 else if (a->e_id < b->e_id) 178 return -1; 179 else 180 return 0; 181 } 182 183 /* 184 * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL. 185 */ 186 static int 187 posix_acl_from_nfsacl(struct posix_acl *acl) 188 { 189 struct posix_acl_entry *pa, *pe, 190 *group_obj = NULL, *mask = NULL; 191 192 if (!acl) 193 return 0; 194 195 sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry), 196 cmp_acl_entry, NULL); 197 198 /* Clear undefined identifier fields and find the ACL_GROUP_OBJ 199 and ACL_MASK entries. */ 200 FOREACH_ACL_ENTRY(pa, acl, pe) { 201 switch(pa->e_tag) { 202 case ACL_USER_OBJ: 203 pa->e_id = ACL_UNDEFINED_ID; 204 break; 205 case ACL_GROUP_OBJ: 206 pa->e_id = ACL_UNDEFINED_ID; 207 group_obj = pa; 208 break; 209 case ACL_MASK: 210 mask = pa; 211 /* fall through */ 212 case ACL_OTHER: 213 pa->e_id = ACL_UNDEFINED_ID; 214 break; 215 } 216 } 217 if (acl->a_count == 4 && group_obj && mask && 218 mask->e_perm == group_obj->e_perm) { 219 /* remove bogus ACL_MASK entry */ 220 memmove(mask, mask+1, (3 - (mask - acl->a_entries)) * 221 sizeof(struct posix_acl_entry)); 222 acl->a_count = 3; 223 } 224 return 0; 225 } 226 227 unsigned int 228 nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt, 229 struct posix_acl **pacl) 230 { 231 struct nfsacl_decode_desc nfsacl_desc = { 232 .desc = { 233 .elem_size = 12, 234 .xcode = pacl ? xdr_nfsace_decode : NULL, 235 }, 236 }; 237 u32 entries; 238 int err; 239 240 if (xdr_decode_word(buf, base, &entries) || 241 entries > NFS_ACL_MAX_ENTRIES) 242 return -EINVAL; 243 nfsacl_desc.desc.array_maxlen = entries; 244 err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc); 245 if (err) 246 return err; 247 if (pacl) { 248 if (entries != nfsacl_desc.desc.array_len || 249 posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) { 250 posix_acl_release(nfsacl_desc.acl); 251 return -EINVAL; 252 } 253 *pacl = nfsacl_desc.acl; 254 } 255 if (aclcnt) 256 *aclcnt = entries; 257 return 8 + nfsacl_desc.desc.elem_size * 258 nfsacl_desc.desc.array_len; 259 } 260