On Fri, 11 Feb 2011 12:49:04 -0600 shirishpargaonkar@xxxxxxxxx wrote: > From: Shirish Pargaonkar <shirishpargaonkar@xxxxxxxxx> > > > rb tree search and insertion routines. > > A SID which needs to be mapped, is looked up in one of the rb trees > depending 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. > > Otherwise, an node is inserted in the tree and a mapping id for > that SID is looked-up with the help of Winbind via upcall mechanism. > If, for whatever reasons, winbind calls return error, SID for an > owner is mapped to 'nobody' and SID for a group is mapped to 'nogroup'. > > 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 a mutex lock to prevent multiple upcalls for > a SID. Adding and removing nodes is done within global locks. > shrinker routine does not prune a node if mapping for that node > is either pending or or node was recently created (before timeout > period expires and node could be purged). The timeout period should > be long enough at least for an upcall to return back to cifs. > > 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 (string) for an id. > > > Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@xxxxxxxxx> > --- > fs/cifs/cifsacl.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++----- > fs/cifs/cifsacl.h | 22 +++++ > 2 files changed, 246 insertions(+), 23 deletions(-) > > diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c > index 388b34c..b8055d6 100644 > --- a/fs/cifs/cifsacl.c > +++ b/fs/cifs/cifsacl.c > @@ -54,7 +54,7 @@ static const struct cifs_sid sid_authusers = { > /* group users */ > static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; > > -static const struct cred *root_cred; > +const struct cred *root_cred; > > /* > * Run idmap cache shrinker. > @@ -62,8 +62,39 @@ static const struct cred *root_cred; > static int > cifs_idmap_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) > { > - /* Use a pruning scheme in a subsequent patch instead */ > - cifs_destroy_idmaptrees(); > + struct rb_root *root; > + struct rb_node *node; > + struct rb_node *tmp; > + struct cifs_sid_id *psidid; > + > + root = &uidtree; > + spin_lock(&siduidlock); > + node = rb_first(root); > + while (node) { > + tmp = node; > + node = rb_next(tmp); > + psidid = rb_entry(tmp, struct cifs_sid_id, rbnode); > + if (psidid->state == SID_MAP_PENDING || > + time_after(psidid->time + SID_MAP_EXPIRE, jiffies)) > + continue; > + rb_erase(tmp, root); > + } > + spin_unlock(&siduidlock); > + > + root = &gidtree; > + spin_lock(&sidgidlock); > + node = rb_first(root); > + while (node) { > + tmp = node; > + node = rb_next(tmp); > + psidid = rb_entry(tmp, struct cifs_sid_id, rbnode); > + if (psidid->state == SID_MAP_PENDING || > + time_after(psidid->time + SID_MAP_EXPIRE, jiffies)) > + continue; > + rb_erase(tmp, root); > + } > + spin_unlock(&sidgidlock); > + > return 0; ^^^^^^^^^ I think you need a more meaningful return value here: * 'shrink' is passed a count 'nr_to_scan' and a 'gfpmask'. It should * look through the least-recently-used 'nr_to_scan' entries and * attempt to free them up. It should return the number of objects * which remain in the cache. If it returns -1, it means it cannot do * any scanning at this time (eg. there is a risk of deadlock). > } > > @@ -92,7 +123,6 @@ cifs_idmap_key_destroy(struct key *key) > kfree(key->payload.data); > } > > -static > struct key_type cifs_idmap_key_type = { > .name = "cifs.cifs_idmap", > .instantiate = cifs_idmap_key_instantiate, > @@ -101,6 +131,151 @@ struct key_type cifs_idmap_key_type = { > .match = user_match, > }; > > +static int > +id_rb_search_insert(struct rb_root *root, struct cifs_sid *sidptr, > + struct cifs_sid_id **psidid, char **sidstr, unsigned long *id) > +{ > + int rc = 1; > + struct rb_node *node = root->rb_node; > + struct rb_node *parent = NULL; > + struct rb_node **linkto = &(root->rb_node); > + > + while (node) { > + *psidid = rb_entry(node, struct cifs_sid_id, rbnode); > + parent = node; > + rc = compare_sids(sidptr, &((*psidid)->sid)); > + if (rc > 0) { > + linkto = &(node->rb_left); > + node = node->rb_left; > + } else if (rc < 0) { > + linkto = &(node->rb_right); > + node = node->rb_right; > + } else { /* node found, rc is 0 */ > + if ((*psidid)->state == SID_MAP_COMPLETE) { > + *id = (*psidid)->id; > + break; > + } else > + return 1; > + } > + } > + > + if (rc) { /* node not found, rc is not 0 */ > + *sidstr = kzalloc(SIDLEN, GFP_KERNEL); > + if (!*sidstr) > + return -ENOMEM; > + > + *psidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL); > + if (!*psidid) { > + kfree(*sidstr); > + return -ENOMEM; > + } > + > + memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid)); > + mutex_init(&(*psidid)->id_mutex); > + (*psidid)->state = SID_MAP_PENDING; > + > + rb_link_node(&(*psidid)->rbnode, parent, linkto); > + rb_insert_color(&(*psidid)->rbnode, root); > + } > + > + return rc; > +} > + > +static void > +sid_to_str(struct cifs_sid *sidptr, char *sidstr) > +{ > + int i; > + unsigned long saval; > + char *strptr; > + > + strptr = sidstr; > + > + sprintf(strptr, "%s", "S"); > + strptr = sidstr + strlen(sidstr); > + > + sprintf(strptr, "-%d", sidptr->revision); > + strptr = sidstr + strlen(sidstr); > + > + for (i = 0; i < 6; ++i) { > + if (sidptr->authority[i]) { > + sprintf(strptr, "-%d", sidptr->authority[i]); > + strptr = sidstr + strlen(sidstr); > + } > + } > + > + for (i = 0; i < sidptr->num_subauth; ++i) { > + saval = le32_to_cpu(sidptr->sub_auth[i]); > + sprintf(strptr, "-%ld", saval); > + strptr = sidstr + strlen(sidstr); > + } > +} > + > +static int > +sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, > + struct cifs_fattr *fattr, uint sidtype) > +{ > + int rc; > + unsigned long id; > + char *strptr; > + char *sidstr = NULL; /* alloc'ed in id_rb_search_insert */ > + struct key *idkey; > + const struct cred *saved_cred; > + struct cifs_sid_id *psidid = NULL; /* alloc'ed in id_rb_search_insert */ > + > + if (sidtype == SIDOWNER) { > + id = cifs_sb->mnt_uid; > + spin_lock(&siduidlock); > + rc = id_rb_search_insert(&uidtree, psid, &psidid, &sidstr, &id); > + spin_unlock(&siduidlock); > + } else if (sidtype == SIDGROUP) { > + id = cifs_sb->mnt_gid; > + spin_lock(&sidgidlock); > + rc = id_rb_search_insert(&gidtree, psid, &psidid, &sidstr, &id); > + spin_unlock(&sidgidlock); > + } else > + return -ENOENT; > + > + if (rc == -ENOMEM) > + return rc; > + > + if (rc) { /* id not found */ > + mutex_lock(&psidid->id_mutex); > + if (psidid->state == SID_MAP_COMPLETE) > + id = psidid->id; > + else { > + if (sidtype == SIDOWNER) > + sprintf(sidstr, "%s", "os:"); > + else if (sidtype == SIDGROUP) > + sprintf(sidstr, "%s", "gs:"); > + > + strptr = sidstr + strlen(sidstr); > + sid_to_str(psid, strptr); > + > + saved_cred = override_creds(root_cred); > + idkey = request_key(&cifs_idmap_key_type, sidstr, ""); > + if (IS_ERR(idkey)) > + cFYI(1, "%s: Can't map SID to an id", __func__); > + else { > + id = *(unsigned long *)idkey->payload.value; > + key_put(idkey); > + } > + psidid->time = jiffies;; ^^^^ Nit: one semicolon is plenty > + psidid->id = id; > + psidid->state = SID_MAP_COMPLETE; > + revert_creds(saved_cred); > + kfree(sidstr); > + } > + mutex_unlock(&psidid->id_mutex); > + } > + > + if (sidtype == SIDOWNER) > + fattr->cf_uid = id; > + else > + fattr->cf_gid = id; > + > + return 0; > +} > + > int > init_cifs_idmap(void) > { > @@ -242,16 +417,24 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) > int num_subauth, num_sat, num_saw; > > if ((!ctsid) || (!cwsid)) > - return 0; > + return 1; > > /* compare the revision */ > - if (ctsid->revision != cwsid->revision) > - return 0; > + if (ctsid->revision != cwsid->revision) { > + if (ctsid->revision > cwsid->revision) > + return 1; > + else > + return -1; > + } > > /* compare all of the six auth values */ > for (i = 0; i < 6; ++i) { > - if (ctsid->authority[i] != cwsid->authority[i]) > - return 0; > + if (ctsid->authority[i] != cwsid->authority[i]) { > + if (ctsid->authority[i] > cwsid->authority[i]) > + return 1; > + else > + return -1; > + } > } > > /* compare all of the subauth values if any */ > @@ -260,12 +443,16 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) > num_subauth = num_sat < num_saw ? num_sat : num_saw; > if (num_subauth) { > for (i = 0; i < num_subauth; ++i) { > - if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) > - return 0; > + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { > + if (ctsid->sub_auth[i] > cwsid->sub_auth[i]) > + return 1; > + else > + return -1; > + } > } > } > > - return 1; /* sids compare/match */ > + return 0; /* sids compare/match */ > } > > > @@ -520,22 +707,22 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, > #ifdef CONFIG_CIFS_DEBUG2 > dump_ace(ppace[i], end_of_acl); > #endif > - if (compare_sids(&(ppace[i]->sid), pownersid)) > + if (compare_sids(&(ppace[i]->sid), pownersid) == 0) > access_flags_to_mode(ppace[i]->access_req, > ppace[i]->type, > &fattr->cf_mode, > &user_mask); > - if (compare_sids(&(ppace[i]->sid), pgrpsid)) > + if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) > access_flags_to_mode(ppace[i]->access_req, > ppace[i]->type, > &fattr->cf_mode, > &group_mask); > - if (compare_sids(&(ppace[i]->sid), &sid_everyone)) > + if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) > access_flags_to_mode(ppace[i]->access_req, > ppace[i]->type, > &fattr->cf_mode, > &other_mask); > - if (compare_sids(&(ppace[i]->sid), &sid_authusers)) > + if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0) > access_flags_to_mode(ppace[i]->access_req, > ppace[i]->type, > &fattr->cf_mode, > @@ -613,10 +800,10 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl) > > > /* Convert CIFS ACL to POSIX form */ > -static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, > - struct cifs_fattr *fattr) > +static int parse_sec_desc(struct cifs_sb_info *cifs_sb, > + struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr) > { > - int rc; > + int rc = 0; > struct cifs_sid *owner_sid_ptr, *group_sid_ptr; > struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ > char *end_of_acl = ((char *)pntsd) + acl_len; > @@ -638,12 +825,26 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, > le32_to_cpu(pntsd->sacloffset), dacloffset); > /* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ > rc = parse_sid(owner_sid_ptr, end_of_acl); > - if (rc) > + if (rc) { > + cFYI(1, "%s: Error %d parsing Owner SID", __func__, rc); > + return rc; > + } > + rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); > + if (rc) { > + cFYI(1, "%s: Error %d mapping Owner SID to uid", __func__, rc); > return rc; > + } > > rc = parse_sid(group_sid_ptr, end_of_acl); > - if (rc) > + if (rc) { > + cFYI(1, "%s: Error %d mapping Owner SID to gid", __func__, rc); > return rc; > + } > + rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); > + if (rc) { > + cFYI(1, "%s: Error %d mapping Group SID to gid", __func__, rc); > + return rc; > + } > > if (dacloffset) > parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, > @@ -658,7 +859,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, > memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr, > sizeof(struct cifs_sid)); */ > > - return 0; > + return rc; > } > > > @@ -865,7 +1066,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, > rc = PTR_ERR(pntsd); > cERROR(1, "%s: error %d getting sec desc", __func__, rc); > } else { > - rc = parse_sec_desc(pntsd, acllen, fattr); > + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr); > kfree(pntsd); > if (rc) > cERROR(1, "parse sec desc failed rc = %d", rc); > diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h > index c4ae7d0..a506d30 100644 > --- a/fs/cifs/cifsacl.h > +++ b/fs/cifs/cifsacl.h > @@ -39,6 +39,14 @@ > #define ACCESS_ALLOWED 0 > #define ACCESS_DENIED 1 > > +#define SIDOWNER 1 > +#define SIDGROUP 2 > +#define SIDLEN 150 /* S- 1 revision- 6 authorities- max 5 sub authorities */ > + > +#define SID_MAP_COMPLETE 0 > +#define SID_MAP_PENDING 1 > +#define SID_MAP_EXPIRE 1000 /* enough for an upcall to return? */ > + > struct cifs_ntsd { > __le16 revision; /* revision level */ > __le16 type; > @@ -74,6 +82,20 @@ struct cifs_wksid { > char sidname[SIDNAMELENGTH]; > } __attribute__((packed)); > > +struct cifs_sid_id { > + struct rb_node rbnode; > + struct cifs_sid sid; > + int state; > + unsigned long id; > + unsigned long time; > + struct mutex id_mutex; > +} __attribute__((packed)); > + ^^^^^^^^^^^^^^ A mutex for every entry? Mutexes can be very large -- why not just have a simple flags field and use atomic bitops to set and clear them? Then you can just use wait_on_bit() to handle this. Also, there's no need to pack these structs -- they don't go across the wire. > +#ifdef __KERNEL__ > +extern struct key_type cifs_idmap_key_type; > +extern const struct cred *root_cred; > +#endif /* KERNEL */ > + > extern int match_sid(struct cifs_sid *); > extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *); > -- Jeff Layton <jlayton@xxxxxxxxxx> -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html