Re: [PATCH 1/2] cifs: Add data structures and functions for uid/gid to SID mapping (try #2)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Aug 1, 2011 at 3:49 PM,  <shirishpargaonkar@xxxxxxxxx> wrote:
> From: Shirish Pargaonkar <shirishpargaonkar@xxxxxxxxx>
>
>
> Add data structures and functions necessary to map a uid and gid to SID.
> These functions are very similar to the ones used to map a SID to uid and gid.
> This time, instead of storing sid to id mapping sorted on sid value,
> id to sid is stored, sorted on id.
> A cifs upcall sends an id (uid or gid) and expects a SID structure
> in return, if mapping was done successfully.
>
> Changed the return code of failed id to sid mapping to EINVAL.
>
>
> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@xxxxxxxxx>
> ---
>  fs/cifs/cifsacl.c  |  198 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/cifs/cifsglob.h |    4 +
>  2 files changed, 202 insertions(+), 0 deletions(-)
>
> diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
> index 21de1d6..b7e723a 100644
> --- a/fs/cifs/cifsacl.c
> +++ b/fs/cifs/cifsacl.c
> @@ -91,9 +91,76 @@ cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
>        shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
>        spin_unlock(&sidgidlock);
>
> +       root = &siduidtree;
> +       spin_lock(&uidsidlock);
> +       shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
> +       spin_unlock(&uidsidlock);
> +
> +       root = &sidgidtree;
> +       spin_lock(&gidsidlock);
> +       shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
> +       spin_unlock(&gidsidlock);
> +
>        return nr_rem;
>  }
>
> +static void
> +sid_rb_insert(struct rb_root *root, unsigned long cid,
> +               struct cifs_sid_id **psidid, char *typestr)
> +{
> +       char *strptr;
> +       struct rb_node *node = root->rb_node;
> +       struct rb_node *parent = NULL;
> +       struct rb_node **linkto = &(root->rb_node);
> +       struct cifs_sid_id *lsidid;
> +
> +       while (node) {
> +               lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
> +               parent = node;
> +               if (cid > lsidid->id) {
> +                       linkto = &(node->rb_left);
> +                       node = node->rb_left;
> +               }
> +               if (cid < lsidid->id) {
> +                       linkto = &(node->rb_right);
> +                       node = node->rb_right;
> +               }
> +       }
> +
> +       (*psidid)->id = cid;
> +       (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
> +       (*psidid)->refcount = 0;
> +
> +       sprintf((*psidid)->sidstr, "%s", typestr);
> +       strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
> +       sprintf(strptr, "%ld", cid);
> +
> +       clear_bit(SID_ID_PENDING, &(*psidid)->state);
> +       clear_bit(SID_ID_MAPPED, &(*psidid)->state);
> +
> +       rb_link_node(&(*psidid)->rbnode, parent, linkto);
> +       rb_insert_color(&(*psidid)->rbnode, root);
> +}
> +
> +static struct cifs_sid_id *
> +sid_rb_search(struct rb_root *root, unsigned long cid)
> +{
> +       struct rb_node *node = root->rb_node;
> +       struct cifs_sid_id *lsidid;
> +
> +       while (node) {
> +               lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
> +               if (cid > lsidid->id)
> +                       node = node->rb_left;
> +               else if (cid < lsidid->id)
> +                       node = node->rb_right;
> +               else /* node found */
> +                       return lsidid;
> +       }
> +
> +       return NULL;
> +}
> +
>  static struct shrinker cifs_shrinker = {
>        .shrink = cifs_idmap_shrinker,
>        .seeks = DEFAULT_SEEKS,
> @@ -110,6 +177,7 @@ cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen)
>
>        memcpy(payload, data, datalen);
>        key->payload.data = payload;
> +       key->datalen = datalen;
>        return 0;
>  }
>
> @@ -224,6 +292,120 @@ sidid_pending_wait(void *unused)
>  }
>
>  static int
> +id_to_sid(unsigned long cid, uint sidtype, struct cifs_sid *ssid)
> +{
> +       int rc = 0;
> +       struct key *sidkey;
> +       const struct cred *saved_cred;
> +       struct cifs_sid *lsid;
> +       struct cifs_sid_id *psidid, *npsidid;
> +       struct rb_root *cidtree;
> +       spinlock_t *cidlock;
> +
> +       if (sidtype == SIDOWNER) {
> +               cidlock = &siduidlock;
> +               cidtree = &uidtree;
> +       } else if (sidtype == SIDGROUP) {
> +               cidlock = &sidgidlock;
> +               cidtree = &gidtree;
> +       } else
> +               return -EINVAL;
> +
> +       spin_lock(cidlock);
> +       psidid = sid_rb_search(cidtree, cid);
> +
> +       if (!psidid) { /* node does not exist, allocate one & attempt adding */
> +               spin_unlock(cidlock);
> +               npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
> +               if (!npsidid)
> +                       return -ENOMEM;
> +
> +               npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
> +               if (!npsidid->sidstr) {
> +                       kfree(npsidid);
> +                       return -ENOMEM;
> +               }
> +
> +               spin_lock(cidlock);
> +               psidid = sid_rb_search(cidtree, cid);
> +               if (psidid) { /* node happened to get inserted meanwhile */
> +                       ++psidid->refcount;
> +                       spin_unlock(cidlock);
> +                       kfree(npsidid->sidstr);
> +                       kfree(npsidid);
> +               } else {
> +                       psidid = npsidid;
> +                       sid_rb_insert(cidtree, cid, &psidid,
> +                                       sidtype == SIDOWNER ? "oi:" : "gi:");
> +                       ++psidid->refcount;
> +                       spin_unlock(cidlock);
> +               }
> +       } else {
> +               ++psidid->refcount;
> +               spin_unlock(cidlock);
> +       }
> +
> +       /*
> +        * If we are here, it is safe to access psidid and its fields
> +        * since a reference was taken earlier while holding the spinlock.
> +        * A reference on the node is put without holding the spinlock
> +        * and it is OK to do so in this case, shrinker will not erase
> +        * this node until all references are put and we do not access
> +        * any fields of the node after a reference is put .
> +        */
> +       if (test_bit(SID_ID_MAPPED, &psidid->state)) {
> +               memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
> +               psidid->time = jiffies; /* update ts for accessing */
> +               goto id_sid_out;
> +       }
> +
> +       if (time_after(psidid->time + SID_MAP_RETRY, jiffies)) {
> +               rc = -EINVAL;
> +               goto id_sid_out;
> +       }
> +
> +       if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
> +               saved_cred = override_creds(root_cred);
> +               sidkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
> +               if (IS_ERR(sidkey)) {
> +                       rc = -EINVAL;
> +                       cFYI(1, "%s: Can't map and id to a SID", __func__);
> +               } else {
> +                       lsid = (struct cifs_sid *)sidkey->payload.data;
> +                       memcpy(&psidid->sid, lsid,
> +                               sidkey->datalen < sizeof(struct cifs_sid) ?
> +                               sidkey->datalen : sizeof(struct cifs_sid));
> +                       memcpy(ssid, &psidid->sid,
> +                               sidkey->datalen < sizeof(struct cifs_sid) ?
> +                               sidkey->datalen : sizeof(struct cifs_sid));
> +                       set_bit(SID_ID_MAPPED, &psidid->state);
> +                       key_put(sidkey);
> +                       kfree(psidid->sidstr);
> +               }
> +               psidid->time = jiffies; /* update ts for accessing */
> +               revert_creds(saved_cred);
> +               clear_bit(SID_ID_PENDING, &psidid->state);
> +               wake_up_bit(&psidid->state, SID_ID_PENDING);
> +       } else {
> +               rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
> +                               sidid_pending_wait, TASK_INTERRUPTIBLE);
> +               if (rc) {
> +                       cFYI(1, "%s: sidid_pending_wait interrupted %d",
> +                                       __func__, rc);
> +                       --psidid->refcount;
> +                       return rc;
> +               }
> +               if (test_bit(SID_ID_MAPPED, &psidid->state))
> +                       memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
> +               else
> +                       rc = -EINVAL;
> +       }
> +id_sid_out:
> +       --psidid->refcount;
> +       return rc;
> +}
> +
> +static int
>  sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
>                struct cifs_fattr *fattr, uint sidtype)
>  {
> @@ -383,6 +565,10 @@ init_cifs_idmap(void)
>        spin_lock_init(&sidgidlock);
>        gidtree = RB_ROOT;
>
> +       spin_lock_init(&uidsidlock);
> +       siduidtree = RB_ROOT;
> +       spin_lock_init(&gidsidlock);
> +       sidgidtree = RB_ROOT;
>        register_shrinker(&cifs_shrinker);
>
>        cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring));
> @@ -422,6 +608,18 @@ cifs_destroy_idmaptrees(void)
>        while ((node = rb_first(root)))
>                rb_erase(node, root);
>        spin_unlock(&sidgidlock);
> +
> +       root = &siduidtree;
> +       spin_lock(&uidsidlock);
> +       while ((node = rb_first(root)))
> +               rb_erase(node, root);
> +       spin_unlock(&uidsidlock);
> +
> +       root = &sidgidtree;
> +       spin_lock(&gidsidlock);
> +       while ((node = rb_first(root)))
> +               rb_erase(node, root);
> +       spin_unlock(&gidsidlock);
>  }
>
>  /* if the two SIDs (roughly equivalent to a UUID for a user or group) are
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 6255fa8..6b5e4a5 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -940,6 +940,10 @@ GLOBAL_EXTERN struct rb_root uidtree;
>  GLOBAL_EXTERN struct rb_root gidtree;
>  GLOBAL_EXTERN spinlock_t siduidlock;
>  GLOBAL_EXTERN spinlock_t sidgidlock;
> +GLOBAL_EXTERN struct rb_root siduidtree;
> +GLOBAL_EXTERN struct rb_root sidgidtree;
> +GLOBAL_EXTERN spinlock_t uidsidlock;
> +GLOBAL_EXTERN spinlock_t gidsidlock;
>
>  void cifs_oplock_break(struct work_struct *work);
>  void cifs_oplock_break_get(struct cifsFileInfo *cfile);

Should these global externs (and the three or four above)
be in the cifsacl ifdef? Might save a little bit of memory.




-- 
Thanks,

Steve
--
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


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux