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