aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>2011-04-22 13:09:36 -0400
committerSteve French <sfrench@us.ibm.com>2011-05-19 10:10:51 -0400
commit9409ae58e0759d010b347e7b19ebc90ab5d4b98f (patch)
tree7b64bf5100528daaaf9f29819c78c8fe46b51af4
parent4d79dba0e00749fa40de8ef13a9b85ce57a1603b (diff)
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines. A SID which needs to be mapped, is looked up in one of the rb trees depending on whether SID is either owner or group SID. If found in the tree, a (mapped) id from that node is assigned to uid or gid as appropriate. If unmapped, an upcall is attempted to map the SID to an id. If upcall is successful, node is marked as mapped. If upcall fails, node stays marked as unmapped and a mapping is attempted again only after an arbitrary time period has passed. To map a SID, which can be either a Owner SID or a Group SID, key description starts with the string "os" or "gs" followed by SID converted to a string. Without "os" or "gs", cifs.upcall does not know whether SID needs to be mapped to either an uid or a gid. Nodes in rb tree have fields to prevent multiple upcalls for a SID. Searching, adding, and removing nodes is done within global locks. Whenever a node is either found or inserted in a tree, a reference is taken on that node. Shrinker routine prunes a node if it has expired but does not prune an expired node if its refcount is not zero (i.e. sid/id of that node is_being/will_be accessed). Thus a node, if its SID needs to be mapped by making an upcall, can safely stay and its fields accessed without shrinker pruning it. A reference (refcount) is put on the node without holding the spinlock but a reference is get on the node by holding the spinlock. Every time an existing mapped node is accessed or mapping is attempted, its timestamp is updated to prevent it from getting erased or a to prevent multiple unnecessary repeat mapping retries respectively. For now, cifs.upcall is only used to map a SID to an id (uid or gid) but it would be used to obtain an SID for an id. Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/cifsacl.c325
-rw-r--r--fs/cifs/cifsacl.h24
2 files changed, 325 insertions, 24 deletions
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 061fc3afd841..a4aa0f0af5c3 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -54,7 +54,33 @@ static const struct cifs_sid sid_authusers = {
54/* group users */ 54/* group users */
55static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; 55static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
56 56
57static const struct cred *root_cred; 57const struct cred *root_cred;
58
59static void
60shrink_idmap_tree(struct rb_root *root, int nr_to_scan, int *nr_rem,
61 int *nr_del)
62{
63 struct rb_node *node;
64 struct rb_node *tmp;
65 struct cifs_sid_id *psidid;
66
67 node = rb_first(root);
68 while (node) {
69 tmp = node;
70 node = rb_next(tmp);
71 psidid = rb_entry(tmp, struct cifs_sid_id, rbnode);
72 if (nr_to_scan == 0 || *nr_del == nr_to_scan)
73 ++(*nr_rem);
74 else {
75 if (time_after(jiffies, psidid->time + SID_MAP_EXPIRE)
76 && psidid->refcount == 0) {
77 rb_erase(tmp, root);
78 ++(*nr_del);
79 } else
80 ++(*nr_rem);
81 }
82 }
83}
58 84
59/* 85/*
60 * Run idmap cache shrinker. 86 * Run idmap cache shrinker.
@@ -62,9 +88,21 @@ static const struct cred *root_cred;
62static int 88static int
63cifs_idmap_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) 89cifs_idmap_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask)
64{ 90{
65 /* Use a pruning scheme in a subsequent patch instead */ 91 int nr_del = 0;
66 cifs_destroy_idmaptrees(); 92 int nr_rem = 0;
67 return 0; 93 struct rb_root *root;
94
95 root = &uidtree;
96 spin_lock(&siduidlock);
97 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
98 spin_unlock(&siduidlock);
99
100 root = &gidtree;
101 spin_lock(&sidgidlock);
102 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
103 spin_unlock(&sidgidlock);
104
105 return nr_rem;
68} 106}
69 107
70static struct shrinker cifs_shrinker = { 108static struct shrinker cifs_shrinker = {
@@ -92,7 +130,6 @@ cifs_idmap_key_destroy(struct key *key)
92 kfree(key->payload.data); 130 kfree(key->payload.data);
93} 131}
94 132
95static
96struct key_type cifs_idmap_key_type = { 133struct key_type cifs_idmap_key_type = {
97 .name = "cifs.cifs_idmap", 134 .name = "cifs.cifs_idmap",
98 .instantiate = cifs_idmap_key_instantiate, 135 .instantiate = cifs_idmap_key_instantiate,
@@ -101,6 +138,220 @@ struct key_type cifs_idmap_key_type = {
101 .match = user_match, 138 .match = user_match,
102}; 139};
103 140
141static void
142sid_to_str(struct cifs_sid *sidptr, char *sidstr)
143{
144 int i;
145 unsigned long saval;
146 char *strptr;
147
148 strptr = sidstr;
149
150 sprintf(strptr, "%s", "S");
151 strptr = sidstr + strlen(sidstr);
152
153 sprintf(strptr, "-%d", sidptr->revision);
154 strptr = sidstr + strlen(sidstr);
155
156 for (i = 0; i < 6; ++i) {
157 if (sidptr->authority[i]) {
158 sprintf(strptr, "-%d", sidptr->authority[i]);
159 strptr = sidstr + strlen(sidstr);
160 }
161 }
162
163 for (i = 0; i < sidptr->num_subauth; ++i) {
164 saval = le32_to_cpu(sidptr->sub_auth[i]);
165 sprintf(strptr, "-%ld", saval);
166 strptr = sidstr + strlen(sidstr);
167 }
168}
169
170static void
171id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr,
172 struct cifs_sid_id **psidid, char *typestr)
173{
174 int rc;
175 char *strptr;
176 struct rb_node *node = root->rb_node;
177 struct rb_node *parent = NULL;
178 struct rb_node **linkto = &(root->rb_node);
179 struct cifs_sid_id *lsidid;
180
181 while (node) {
182 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
183 parent = node;
184 rc = compare_sids(sidptr, &((lsidid)->sid));
185 if (rc > 0) {
186 linkto = &(node->rb_left);
187 node = node->rb_left;
188 } else if (rc < 0) {
189 linkto = &(node->rb_right);
190 node = node->rb_right;
191 }
192 }
193
194 memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid));
195 (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
196 (*psidid)->refcount = 0;
197
198 sprintf((*psidid)->sidstr, "%s", typestr);
199 strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
200 sid_to_str(&(*psidid)->sid, strptr);
201
202 clear_bit(SID_ID_PENDING, &(*psidid)->state);
203 clear_bit(SID_ID_MAPPED, &(*psidid)->state);
204
205 rb_link_node(&(*psidid)->rbnode, parent, linkto);
206 rb_insert_color(&(*psidid)->rbnode, root);
207}
208
209static struct cifs_sid_id *
210id_rb_search(struct rb_root *root, struct cifs_sid *sidptr)
211{
212 int rc;
213 struct rb_node *node = root->rb_node;
214 struct rb_node *parent = NULL;
215 struct rb_node **linkto = &(root->rb_node);
216 struct cifs_sid_id *lsidid;
217
218 while (node) {
219 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
220 parent = node;
221 rc = compare_sids(sidptr, &((lsidid)->sid));
222 if (rc > 0) {
223 linkto = &(node->rb_left);
224 node = node->rb_left;
225 } else if (rc < 0) {
226 linkto = &(node->rb_right);
227 node = node->rb_right;
228 } else /* node found */
229 return lsidid;
230 }
231
232 return NULL;
233}
234
235static int
236sidid_pending_wait(void *unused)
237{
238 schedule();
239 return signal_pending(current) ? -ERESTARTSYS : 0;
240}
241
242static int
243sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
244 struct cifs_fattr *fattr, uint sidtype)
245{
246 int rc;
247 unsigned long cid;
248 struct key *idkey;
249 const struct cred *saved_cred;
250 struct cifs_sid_id *psidid, *npsidid;
251 struct rb_root *cidtree;
252 spinlock_t *cidlock;
253
254 if (sidtype == SIDOWNER) {
255 cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */
256 cidlock = &siduidlock;
257 cidtree = &uidtree;
258 } else if (sidtype == SIDGROUP) {
259 cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */
260 cidlock = &sidgidlock;
261 cidtree = &gidtree;
262 } else
263 return -ENOENT;
264
265 spin_lock(cidlock);
266 psidid = id_rb_search(cidtree, psid);
267
268 if (!psidid) { /* node does not exist, allocate one & attempt adding */
269 spin_unlock(cidlock);
270 npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
271 if (!npsidid)
272 return -ENOMEM;
273
274 npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
275 if (!npsidid->sidstr) {
276 kfree(npsidid);
277 return -ENOMEM;
278 }
279
280 spin_lock(cidlock);
281 psidid = id_rb_search(cidtree, psid);
282 if (psidid) { /* node happened to get inserted meanwhile */
283 ++psidid->refcount;
284 spin_unlock(cidlock);
285 kfree(npsidid->sidstr);
286 kfree(npsidid);
287 } else {
288 psidid = npsidid;
289 id_rb_insert(cidtree, psid, &psidid,
290 sidtype == SIDOWNER ? "os:" : "gs:");
291 ++psidid->refcount;
292 spin_unlock(cidlock);
293 }
294 } else {
295 ++psidid->refcount;
296 spin_unlock(cidlock);
297 }
298
299 /*
300 * If we are here, it is safe to access psidid and its fields
301 * since a reference was taken earlier while holding the spinlock.
302 * A reference on the node is put without holding the spinlock
303 * and it is OK to do so in this case, shrinker will not erase
304 * this node until all references are put and we do not access
305 * any fields of the node after a reference is put .
306 */
307 if (test_bit(SID_ID_MAPPED, &psidid->state)) {
308 cid = psidid->id;
309 psidid->time = jiffies; /* update ts for accessing */
310 goto sid_to_id_out;
311 }
312
313 if (time_after(psidid->time + SID_MAP_RETRY, jiffies))
314 goto sid_to_id_out;
315
316 if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
317 saved_cred = override_creds(root_cred);
318 idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
319 if (IS_ERR(idkey))
320 cFYI(1, "%s: Can't map SID to an id", __func__);
321 else {
322 cid = *(unsigned long *)idkey->payload.value;
323 psidid->id = cid;
324 set_bit(SID_ID_MAPPED, &psidid->state);
325 key_put(idkey);
326 kfree(psidid->sidstr);
327 }
328 revert_creds(saved_cred);
329 psidid->time = jiffies; /* update ts for accessing */
330 clear_bit(SID_ID_PENDING, &psidid->state);
331 wake_up_bit(&psidid->state, SID_ID_PENDING);
332 } else {
333 rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
334 sidid_pending_wait, TASK_INTERRUPTIBLE);
335 if (rc) {
336 cFYI(1, "%s: sidid_pending_wait interrupted %d",
337 __func__, rc);
338 --psidid->refcount; /* decremented without spinlock */
339 return rc;
340 }
341 if (test_bit(SID_ID_MAPPED, &psidid->state))
342 cid = psidid->id;
343 }
344
345sid_to_id_out:
346 --psidid->refcount; /* decremented without spinlock */
347 if (sidtype == SIDOWNER)
348 fattr->cf_uid = cid;
349 else
350 fattr->cf_gid = cid;
351
352 return 0;
353}
354
104int 355int
105init_cifs_idmap(void) 356init_cifs_idmap(void)
106{ 357{
@@ -242,16 +493,24 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
242 int num_subauth, num_sat, num_saw; 493 int num_subauth, num_sat, num_saw;
243 494
244 if ((!ctsid) || (!cwsid)) 495 if ((!ctsid) || (!cwsid))
245 return 0; 496 return 1;
246 497
247 /* compare the revision */ 498 /* compare the revision */
248 if (ctsid->revision != cwsid->revision) 499 if (ctsid->revision != cwsid->revision) {
249 return 0; 500 if (ctsid->revision > cwsid->revision)
501 return 1;
502 else
503 return -1;
504 }
250 505
251 /* compare all of the six auth values */ 506 /* compare all of the six auth values */
252 for (i = 0; i < 6; ++i) { 507 for (i = 0; i < 6; ++i) {
253 if (ctsid->authority[i] != cwsid->authority[i]) 508 if (ctsid->authority[i] != cwsid->authority[i]) {
254 return 0; 509 if (ctsid->authority[i] > cwsid->authority[i])
510 return 1;
511 else
512 return -1;
513 }
255 } 514 }
256 515
257 /* compare all of the subauth values if any */ 516 /* compare all of the subauth values if any */
@@ -260,12 +519,16 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
260 num_subauth = num_sat < num_saw ? num_sat : num_saw; 519 num_subauth = num_sat < num_saw ? num_sat : num_saw;
261 if (num_subauth) { 520 if (num_subauth) {
262 for (i = 0; i < num_subauth; ++i) { 521 for (i = 0; i < num_subauth; ++i) {
263 if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) 522 if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
264 return 0; 523 if (ctsid->sub_auth[i] > cwsid->sub_auth[i])
524 return 1;
525 else
526 return -1;
527 }
265 } 528 }
266 } 529 }
267 530
268 return 1; /* sids compare/match */ 531 return 0; /* sids compare/match */
269} 532}
270 533
271 534
@@ -520,22 +783,22 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
520#ifdef CONFIG_CIFS_DEBUG2 783#ifdef CONFIG_CIFS_DEBUG2
521 dump_ace(ppace[i], end_of_acl); 784 dump_ace(ppace[i], end_of_acl);
522#endif 785#endif
523 if (compare_sids(&(ppace[i]->sid), pownersid)) 786 if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
524 access_flags_to_mode(ppace[i]->access_req, 787 access_flags_to_mode(ppace[i]->access_req,
525 ppace[i]->type, 788 ppace[i]->type,
526 &fattr->cf_mode, 789 &fattr->cf_mode,
527 &user_mask); 790 &user_mask);
528 if (compare_sids(&(ppace[i]->sid), pgrpsid)) 791 if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
529 access_flags_to_mode(ppace[i]->access_req, 792 access_flags_to_mode(ppace[i]->access_req,
530 ppace[i]->type, 793 ppace[i]->type,
531 &fattr->cf_mode, 794 &fattr->cf_mode,
532 &group_mask); 795 &group_mask);
533 if (compare_sids(&(ppace[i]->sid), &sid_everyone)) 796 if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
534 access_flags_to_mode(ppace[i]->access_req, 797 access_flags_to_mode(ppace[i]->access_req,
535 ppace[i]->type, 798 ppace[i]->type,
536 &fattr->cf_mode, 799 &fattr->cf_mode,
537 &other_mask); 800 &other_mask);
538 if (compare_sids(&(ppace[i]->sid), &sid_authusers)) 801 if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
539 access_flags_to_mode(ppace[i]->access_req, 802 access_flags_to_mode(ppace[i]->access_req,
540 ppace[i]->type, 803 ppace[i]->type,
541 &fattr->cf_mode, 804 &fattr->cf_mode,
@@ -613,10 +876,10 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
613 876
614 877
615/* Convert CIFS ACL to POSIX form */ 878/* Convert CIFS ACL to POSIX form */
616static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, 879static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
617 struct cifs_fattr *fattr) 880 struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr)
618{ 881{
619 int rc; 882 int rc = 0;
620 struct cifs_sid *owner_sid_ptr, *group_sid_ptr; 883 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
621 struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ 884 struct cifs_acl *dacl_ptr; /* no need for SACL ptr */
622 char *end_of_acl = ((char *)pntsd) + acl_len; 885 char *end_of_acl = ((char *)pntsd) + acl_len;
@@ -638,12 +901,26 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
638 le32_to_cpu(pntsd->sacloffset), dacloffset); 901 le32_to_cpu(pntsd->sacloffset), dacloffset);
639/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ 902/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
640 rc = parse_sid(owner_sid_ptr, end_of_acl); 903 rc = parse_sid(owner_sid_ptr, end_of_acl);
641 if (rc) 904 if (rc) {
905 cFYI(1, "%s: Error %d parsing Owner SID", __func__, rc);
642 return rc; 906 return rc;
907 }
908 rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
909 if (rc) {
910 cFYI(1, "%s: Error %d mapping Owner SID to uid", __func__, rc);
911 return rc;
912 }
643 913
644 rc = parse_sid(group_sid_ptr, end_of_acl); 914 rc = parse_sid(group_sid_ptr, end_of_acl);
645 if (rc) 915 if (rc) {
916 cFYI(1, "%s: Error %d mapping Owner SID to gid", __func__, rc);
646 return rc; 917 return rc;
918 }
919 rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
920 if (rc) {
921 cFYI(1, "%s: Error %d mapping Group SID to gid", __func__, rc);
922 return rc;
923 }
647 924
648 if (dacloffset) 925 if (dacloffset)
649 parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, 926 parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
@@ -658,7 +935,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
658 memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr, 935 memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
659 sizeof(struct cifs_sid)); */ 936 sizeof(struct cifs_sid)); */
660 937
661 return 0; 938 return rc;
662} 939}
663 940
664 941
@@ -865,7 +1142,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
865 rc = PTR_ERR(pntsd); 1142 rc = PTR_ERR(pntsd);
866 cERROR(1, "%s: error %d getting sec desc", __func__, rc); 1143 cERROR(1, "%s: error %d getting sec desc", __func__, rc);
867 } else { 1144 } else {
868 rc = parse_sec_desc(pntsd, acllen, fattr); 1145 rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr);
869 kfree(pntsd); 1146 kfree(pntsd);
870 if (rc) 1147 if (rc)
871 cERROR(1, "parse sec desc failed rc = %d", rc); 1148 cERROR(1, "parse sec desc failed rc = %d", rc);
diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h
index c4ae7d036563..757cf5aec3ee 100644
--- a/fs/cifs/cifsacl.h
+++ b/fs/cifs/cifsacl.h
@@ -39,6 +39,15 @@
39#define ACCESS_ALLOWED 0 39#define ACCESS_ALLOWED 0
40#define ACCESS_DENIED 1 40#define ACCESS_DENIED 1
41 41
42#define SIDOWNER 1
43#define SIDGROUP 2
44#define SIDLEN 150 /* S- 1 revision- 6 authorities- max 5 sub authorities */
45
46#define SID_ID_MAPPED 0
47#define SID_ID_PENDING 1
48#define SID_MAP_EXPIRE (3600 * HZ) /* map entry expires after one hour */
49#define SID_MAP_RETRY (300 * HZ) /* wait 5 minutes for next attempt to map */
50
42struct cifs_ntsd { 51struct cifs_ntsd {
43 __le16 revision; /* revision level */ 52 __le16 revision; /* revision level */
44 __le16 type; 53 __le16 type;
@@ -74,6 +83,21 @@ struct cifs_wksid {
74 char sidname[SIDNAMELENGTH]; 83 char sidname[SIDNAMELENGTH];
75} __attribute__((packed)); 84} __attribute__((packed));
76 85
86struct cifs_sid_id {
87 unsigned int refcount; /* increment with spinlock, decrement without */
88 unsigned long id;
89 unsigned long time;
90 unsigned long state;
91 char *sidstr;
92 struct rb_node rbnode;
93 struct cifs_sid sid;
94};
95
96#ifdef __KERNEL__
97extern struct key_type cifs_idmap_key_type;
98extern const struct cred *root_cred;
99#endif /* KERNEL */
100
77extern int match_sid(struct cifs_sid *); 101extern int match_sid(struct cifs_sid *);
78extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *); 102extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *);
79 103