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(nfsacl_encode); 33 EXPORT_SYMBOL(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 u32 *p = (u32 *) elem; 50 51 if (nfsacl_desc->count < nfsacl_desc->acl->a_count) { 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 } else { 73 const struct posix_acl_entry *pa, *pe; 74 int group_obj_perm = ACL_READ|ACL_WRITE|ACL_EXECUTE; 75 76 FOREACH_ACL_ENTRY(pa, nfsacl_desc->acl, pe) { 77 if (pa->e_tag == ACL_GROUP_OBJ) { 78 group_obj_perm = pa->e_perm & S_IRWXO; 79 break; 80 } 81 } 82 /* fake up ACL_MASK entry */ 83 *p++ = htonl(ACL_MASK | nfsacl_desc->typeflag); 84 *p++ = htonl(0); 85 *p++ = htonl(group_obj_perm); 86 } 87 88 return 0; 89 } 90 91 unsigned int 92 nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode, 93 struct posix_acl *acl, int encode_entries, int typeflag) 94 { 95 int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0; 96 struct nfsacl_encode_desc nfsacl_desc = { 97 .desc = { 98 .elem_size = 12, 99 .array_len = encode_entries ? entries : 0, 100 .xcode = xdr_nfsace_encode, 101 }, 102 .acl = acl, 103 .typeflag = typeflag, 104 .uid = inode->i_uid, 105 .gid = inode->i_gid, 106 }; 107 int err; 108 109 if (entries > NFS_ACL_MAX_ENTRIES || 110 xdr_encode_word(buf, base, entries)) 111 return -EINVAL; 112 err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc); 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 u32 *p = (u32 *) 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