Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[~shefty/rdma-dev.git] / fs / cifs / cifsacl.c
index 3151a264988ec1a98cc03d45e3d30f95fccf17bd..5cbd00e740671dc13a0f4a07621513eb469762ac 100644 (file)
@@ -42,135 +42,27 @@ static const struct cifs_sid sid_authusers = {
 /* group users */
 static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
 
-const struct cred *root_cred;
-
-static void
-shrink_idmap_tree(struct rb_root *root, int nr_to_scan, int *nr_rem,
-                       int *nr_del)
-{
-       struct rb_node *node;
-       struct rb_node *tmp;
-       struct cifs_sid_id *psidid;
-
-       node = rb_first(root);
-       while (node) {
-               tmp = node;
-               node = rb_next(tmp);
-               psidid = rb_entry(tmp, struct cifs_sid_id, rbnode);
-               if (nr_to_scan == 0 || *nr_del == nr_to_scan)
-                       ++(*nr_rem);
-               else {
-                       if (time_after(jiffies, psidid->time + SID_MAP_EXPIRE)
-                                               && psidid->refcount == 0) {
-                               rb_erase(tmp, root);
-                               ++(*nr_del);
-                       } else
-                               ++(*nr_rem);
-               }
-       }
-}
-
-/*
- * Run idmap cache shrinker.
- */
-static int
-cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
-{
-       int nr_to_scan = sc->nr_to_scan;
-       int nr_del = 0;
-       int nr_rem = 0;
-       struct rb_root *root;
-
-       root = &uidtree;
-       spin_lock(&siduidlock);
-       shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
-       spin_unlock(&siduidlock);
-
-       root = &gidtree;
-       spin_lock(&sidgidlock);
-       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,
-};
+static const struct cred *root_cred;
 
 static int
 cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
 {
        char *payload;
 
+       /*
+        * If the payload is less than or equal to the size of a pointer, then
+        * an allocation here is wasteful. Just copy the data directly to the
+        * payload.value union member instead.
+        *
+        * With this however, you must check the datalen before trying to
+        * dereference payload.data!
+        */
+       if (prep->datalen <= sizeof(key->payload)) {
+               key->payload.value = 0;
+               memcpy(&key->payload.value, prep->data, prep->datalen);
+               key->datalen = prep->datalen;
+               return 0;
+       }
        payload = kmalloc(prep->datalen, GFP_KERNEL);
        if (!payload)
                return -ENOMEM;
@@ -184,10 +76,11 @@ cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
 static inline void
 cifs_idmap_key_destroy(struct key *key)
 {
-       kfree(key->payload.data);
+       if (key->datalen > sizeof(key->payload))
+               kfree(key->payload.data);
 }
 
-struct key_type cifs_idmap_key_type = {
+static struct key_type cifs_idmap_key_type = {
        .name        = "cifs.idmap",
        .instantiate = cifs_idmap_key_instantiate,
        .destroy     = cifs_idmap_key_destroy,
@@ -195,214 +88,174 @@ struct key_type cifs_idmap_key_type = {
        .match       = user_match,
 };
 
-static void
-sid_to_str(struct cifs_sid *sidptr, char *sidstr)
+static char *
+sid_to_key_str(struct cifs_sid *sidptr, unsigned int type)
 {
-       int i;
-       unsigned long saval;
-       char *strptr;
+       int i, len;
+       unsigned int saval;
+       char *sidstr, *strptr;
+       unsigned long long id_auth_val;
+
+       /* 3 bytes for prefix */
+       sidstr = kmalloc(3 + SID_STRING_BASE_SIZE +
+                        (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth),
+                        GFP_KERNEL);
+       if (!sidstr)
+               return sidstr;
 
        strptr = sidstr;
+       len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g',
+                       sidptr->revision);
+       strptr += len;
+
+       /* The authority field is a single 48-bit number */
+       id_auth_val = (unsigned long long)sidptr->authority[5];
+       id_auth_val |= (unsigned long long)sidptr->authority[4] << 8;
+       id_auth_val |= (unsigned long long)sidptr->authority[3] << 16;
+       id_auth_val |= (unsigned long long)sidptr->authority[2] << 24;
+       id_auth_val |= (unsigned long long)sidptr->authority[1] << 32;
+       id_auth_val |= (unsigned long long)sidptr->authority[0] << 48;
 
-       sprintf(strptr, "%s", "S");
-       strptr = sidstr + strlen(sidstr);
-
-       sprintf(strptr, "-%d", sidptr->revision);
-       strptr = sidstr + strlen(sidstr);
+       /*
+        * MS-DTYP states that if the authority is >= 2^32, then it should be
+        * expressed as a hex value.
+        */
+       if (id_auth_val <= UINT_MAX)
+               len = sprintf(strptr, "-%llu", id_auth_val);
+       else
+               len = sprintf(strptr, "-0x%llx", id_auth_val);
 
-       for (i = 0; i < 6; ++i) {
-               if (sidptr->authority[i]) {
-                       sprintf(strptr, "-%d", sidptr->authority[i]);
-                       strptr = sidstr + strlen(sidstr);
-               }
-       }
+       strptr += len;
 
        for (i = 0; i < sidptr->num_subauth; ++i) {
                saval = le32_to_cpu(sidptr->sub_auth[i]);
-               sprintf(strptr, "-%ld", saval);
-               strptr = sidstr + strlen(sidstr);
+               len = sprintf(strptr, "-%u", saval);
+               strptr += len;
        }
+
+       return sidstr;
 }
 
-static void
-id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr,
-               struct cifs_sid_id **psidid, char *typestr)
+/*
+ * if the two SIDs (roughly equivalent to a UUID for a user or group) are
+ * the same returns zero, if they do not match returns non-zero.
+ */
+static int
+compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
 {
-       int rc;
-       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;
-               rc = compare_sids(sidptr, &((lsidid)->sid));
-               if (rc > 0) {
-                       linkto = &(node->rb_left);
-                       node = node->rb_left;
-               } else if (rc < 0) {
-                       linkto = &(node->rb_right);
-                       node = node->rb_right;
-               }
-       }
-
-       memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid));
-       (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
-       (*psidid)->refcount = 0;
+       int i;
+       int num_subauth, num_sat, num_saw;
 
-       sprintf((*psidid)->sidstr, "%s", typestr);
-       strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
-       sid_to_str(&(*psidid)->sid, strptr);
+       if ((!ctsid) || (!cwsid))
+               return 1;
 
-       clear_bit(SID_ID_PENDING, &(*psidid)->state);
-       clear_bit(SID_ID_MAPPED, &(*psidid)->state);
+       /* compare the revision */
+       if (ctsid->revision != cwsid->revision) {
+               if (ctsid->revision > cwsid->revision)
+                       return 1;
+               else
+                       return -1;
+       }
 
-       rb_link_node(&(*psidid)->rbnode, parent, linkto);
-       rb_insert_color(&(*psidid)->rbnode, root);
-}
+       /* compare all of the six auth values */
+       for (i = 0; i < NUM_AUTHS; ++i) {
+               if (ctsid->authority[i] != cwsid->authority[i]) {
+                       if (ctsid->authority[i] > cwsid->authority[i])
+                               return 1;
+                       else
+                               return -1;
+               }
+       }
 
-static struct cifs_sid_id *
-id_rb_search(struct rb_root *root, struct cifs_sid *sidptr)
-{
-       int rc;
-       struct rb_node *node = root->rb_node;
-       struct cifs_sid_id *lsidid;
-
-       while (node) {
-               lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
-               rc = compare_sids(sidptr, &((lsidid)->sid));
-               if (rc > 0) {
-                       node = node->rb_left;
-               } else if (rc < 0) {
-                       node = node->rb_right;
-               } else /* node found */
-                       return lsidid;
+       /* compare all of the subauth values if any */
+       num_sat = ctsid->num_subauth;
+       num_saw = cwsid->num_subauth;
+       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]) {
+                               if (le32_to_cpu(ctsid->sub_auth[i]) >
+                                       le32_to_cpu(cwsid->sub_auth[i]))
+                                       return 1;
+                               else
+                                       return -1;
+                       }
+               }
        }
 
-       return NULL;
+       return 0; /* sids compare/match */
 }
 
-static int
-sidid_pending_wait(void *unused)
+static void
+cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
 {
-       schedule();
-       return signal_pending(current) ? -ERESTARTSYS : 0;
+       int i;
+
+       dst->revision = src->revision;
+       dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES);
+       for (i = 0; i < NUM_AUTHS; ++i)
+               dst->authority[i] = src->authority[i];
+       for (i = 0; i < dst->num_subauth; ++i)
+               dst->sub_auth[i] = src->sub_auth[i];
 }
 
 static int
-id_to_sid(unsigned long cid, uint sidtype, struct cifs_sid *ssid)
+id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid)
 {
-       int rc = 0;
+       int rc;
        struct key *sidkey;
+       struct cifs_sid *ksid;
+       unsigned int ksid_size;
+       char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */
        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;
-               }
+       rc = snprintf(desc, sizeof(desc), "%ci:%u",
+                       sidtype == SIDOWNER ? 'o' : 'g', cid);
+       if (rc >= sizeof(desc))
+               return -EINVAL;
 
-               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);
+       rc = 0;
+       saved_cred = override_creds(root_cred);
+       sidkey = request_key(&cifs_idmap_key_type, desc, "");
+       if (IS_ERR(sidkey)) {
+               rc = -EINVAL;
+               cFYI(1, "%s: Can't map %cid %u to a SID", __func__,
+                       sidtype == SIDOWNER ? 'u' : 'g', cid);
+               goto out_revert_creds;
+       } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
+               rc = -EIO;
+               cFYI(1, "%s: Downcall contained malformed key "
+                       "(datalen=%hu)", __func__, sidkey->datalen);
+               goto invalidate_key;
        }
 
        /*
-        * 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 .
+        * A sid is usually too large to be embedded in payload.value, but if
+        * there are no subauthorities and the host has 8-byte pointers, then
+        * it could be.
         */
-       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;
+       ksid = sidkey->datalen <= sizeof(sidkey->payload) ?
+               (struct cifs_sid *)&sidkey->payload.value :
+               (struct cifs_sid *)sidkey->payload.data;
+
+       ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32));
+       if (ksid_size > sidkey->datalen) {
+               rc = -EIO;
+               cFYI(1, "%s: Downcall contained malformed key (datalen=%hu, "
+                       "ksid_size=%u)", __func__, sidkey->datalen, ksid_size);
+               goto invalidate_key;
        }
 
-       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;
+       cifs_copy_sid(ssid, ksid);
+out_key_put:
+       key_put(sidkey);
+out_revert_creds:
+       revert_creds(saved_cred);
        return rc;
+
+invalidate_key:
+       key_invalidate(sidkey);
+       goto out_key_put;
 }
 
 static int
@@ -410,111 +263,67 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
                struct cifs_fattr *fattr, uint sidtype)
 {
        int rc;
-       unsigned long cid;
-       struct key *idkey;
+       struct key *sidkey;
+       char *sidstr;
        const struct cred *saved_cred;
-       struct cifs_sid_id *psidid, *npsidid;
-       struct rb_root *cidtree;
-       spinlock_t *cidlock;
-
-       if (sidtype == SIDOWNER) {
-               cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */
-               cidlock = &siduidlock;
-               cidtree = &uidtree;
-       } else if (sidtype == SIDGROUP) {
-               cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */
-               cidlock = &sidgidlock;
-               cidtree = &gidtree;
-       } else
-               return -ENOENT;
-
-       spin_lock(cidlock);
-       psidid = id_rb_search(cidtree, psid);
-
-       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 = id_rb_search(cidtree, psid);
-               if (psidid) { /* node happened to get inserted meanwhile */
-                       ++psidid->refcount;
-                       spin_unlock(cidlock);
-                       kfree(npsidid->sidstr);
-                       kfree(npsidid);
-               } else {
-                       psidid = npsidid;
-                       id_rb_insert(cidtree, psid, &psidid,
-                                       sidtype == SIDOWNER ? "os:" : "gs:");
-                       ++psidid->refcount;
-                       spin_unlock(cidlock);
-               }
-       } else {
-               ++psidid->refcount;
-               spin_unlock(cidlock);
-       }
+       uid_t fuid = cifs_sb->mnt_uid;
+       gid_t fgid = cifs_sb->mnt_gid;
 
        /*
-        * 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 we have too many subauthorities, then something is really wrong.
+        * Just return an error.
         */
-       if (test_bit(SID_ID_MAPPED, &psidid->state)) {
-               cid = psidid->id;
-               psidid->time = jiffies; /* update ts for accessing */
-               goto sid_to_id_out;
+       if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
+               cFYI(1, "%s: %u subauthorities is too many!", __func__,
+                       psid->num_subauth);
+               return -EIO;
        }
 
-       if (time_after(psidid->time + SID_MAP_RETRY, jiffies))
-               goto sid_to_id_out;
-
-       if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
-               saved_cred = override_creds(root_cred);
-               idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
-               if (IS_ERR(idkey))
-                       cFYI(1, "%s: Can't map SID to an id", __func__);
-               else {
-                       cid = *(unsigned long *)idkey->payload.value;
-                       psidid->id = cid;
-                       set_bit(SID_ID_MAPPED, &psidid->state);
-                       key_put(idkey);
-                       kfree(psidid->sidstr);
-               }
-               revert_creds(saved_cred);
-               psidid->time = jiffies; /* update ts for accessing */
-               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; /* decremented without spinlock */
-                       return rc;
-               }
-               if (test_bit(SID_ID_MAPPED, &psidid->state))
-                       cid = psidid->id;
+       sidstr = sid_to_key_str(psid, sidtype);
+       if (!sidstr)
+               return -ENOMEM;
+
+       saved_cred = override_creds(root_cred);
+       sidkey = request_key(&cifs_idmap_key_type, sidstr, "");
+       if (IS_ERR(sidkey)) {
+               rc = -EINVAL;
+               cFYI(1, "%s: Can't map SID %s to a %cid", __func__, sidstr,
+                       sidtype == SIDOWNER ? 'u' : 'g');
+               goto out_revert_creds;
+       }
+
+       /*
+        * FIXME: Here we assume that uid_t and gid_t are same size. It's
+        * probably a safe assumption but might be better to check based on
+        * sidtype.
+        */
+       if (sidkey->datalen != sizeof(uid_t)) {
+               rc = -EIO;
+               cFYI(1, "%s: Downcall contained malformed key "
+                       "(datalen=%hu)", __func__, sidkey->datalen);
+               key_invalidate(sidkey);
+               goto out_key_put;
        }
 
-sid_to_id_out:
-       --psidid->refcount; /* decremented without spinlock */
        if (sidtype == SIDOWNER)
-               fattr->cf_uid = cid;
+               memcpy(&fuid, &sidkey->payload.value, sizeof(uid_t));
        else
-               fattr->cf_gid = cid;
+               memcpy(&fgid, &sidkey->payload.value, sizeof(gid_t));
+
+out_key_put:
+       key_put(sidkey);
+out_revert_creds:
+       revert_creds(saved_cred);
+       kfree(sidstr);
 
+       /*
+        * Note that we return 0 here unconditionally. If the mapping
+        * fails then we just fall back to using the mnt_uid/mnt_gid.
+        */
+       if (sidtype == SIDOWNER)
+               fattr->cf_uid = fuid;
+       else
+               fattr->cf_gid = fgid;
        return 0;
 }
 
@@ -557,17 +366,6 @@ init_cifs_idmap(void)
        cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
        root_cred = cred;
 
-       spin_lock_init(&siduidlock);
-       uidtree = RB_ROOT;
-       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", key_serial(keyring));
        return 0;
 
@@ -584,95 +382,13 @@ exit_cifs_idmap(void)
        key_revoke(root_cred->thread_keyring);
        unregister_key_type(&cifs_idmap_key_type);
        put_cred(root_cred);
-       unregister_shrinker(&cifs_shrinker);
        cFYI(1, "Unregistered %s key type", cifs_idmap_key_type.name);
 }
 
-void
-cifs_destroy_idmaptrees(void)
-{
-       struct rb_root *root;
-       struct rb_node *node;
-
-       root = &uidtree;
-       spin_lock(&siduidlock);
-       while ((node = rb_first(root)))
-               rb_erase(node, root);
-       spin_unlock(&siduidlock);
-
-       root = &gidtree;
-       spin_lock(&sidgidlock);
-       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
-   the same returns 1, if they do not match returns 0 */
-int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
-{
-       int i;
-       int num_subauth, num_sat, num_saw;
-
-       if ((!ctsid) || (!cwsid))
-               return 1;
-
-       /* compare the revision */
-       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]) {
-                       if (ctsid->authority[i] > cwsid->authority[i])
-                               return 1;
-                       else
-                               return -1;
-               }
-       }
-
-       /* compare all of the subauth values if any */
-       num_sat = ctsid->num_subauth;
-       num_saw = cwsid->num_subauth;
-       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]) {
-                               if (le32_to_cpu(ctsid->sub_auth[i]) >
-                                       le32_to_cpu(cwsid->sub_auth[i]))
-                                       return 1;
-                               else
-                                       return -1;
-                       }
-               }
-       }
-
-       return 0; /* sids compare/match */
-}
-
-
 /* copy ntsd, owner sid, and group sid from a security descriptor to another */
 static void copy_sec_desc(const struct cifs_ntsd *pntsd,
                                struct cifs_ntsd *pnntsd, __u32 sidsoffset)
 {
-       int i;
-
        struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
        struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
 
@@ -688,26 +404,14 @@ static void copy_sec_desc(const struct cifs_ntsd *pntsd,
        owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
                                le32_to_cpu(pntsd->osidoffset));
        nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
-
-       nowner_sid_ptr->revision = owner_sid_ptr->revision;
-       nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
-       for (i = 0; i < 6; i++)
-               nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
-       for (i = 0; i < 5; i++)
-               nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
+       cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);
 
        /* copy group sid */
        group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
                                le32_to_cpu(pntsd->gsidoffset));
        ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
                                        sizeof(struct cifs_sid));
-
-       ngroup_sid_ptr->revision = group_sid_ptr->revision;
-       ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
-       for (i = 0; i < 6; i++)
-               ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
-       for (i = 0; i < 5; i++)
-               ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
+       cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);
 
        return;
 }
@@ -814,7 +518,7 @@ static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
 
        pntace->sid.revision = psid->revision;
        pntace->sid.num_subauth = psid->num_subauth;
-       for (i = 0; i < 6; i++)
+       for (i = 0; i < NUM_AUTHS; i++)
                pntace->sid.authority[i] = psid->authority[i];
        for (i = 0; i < psid->num_subauth; i++)
                pntace->sid.sub_auth[i] = psid->sub_auth[i];
@@ -990,8 +694,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
                return -EINVAL;
        }
 
-       if (psid->num_subauth) {
 #ifdef CONFIG_CIFS_DEBUG2
+       if (psid->num_subauth) {
                int i;
                cFYI(1, "SID revision %d num_auth %d",
                        psid->revision, psid->num_subauth);
@@ -1005,8 +709,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
                        num auths and therefore go off the end */
                cFYI(1, "RID 0x%x",
                        le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
-#endif
        }
+#endif
 
        return 0;
 }
@@ -1116,8 +820,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
                                kfree(nowner_sid_ptr);
                                return rc;
                        }
-                       memcpy(owner_sid_ptr, nowner_sid_ptr,
-                                       sizeof(struct cifs_sid));
+                       cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr);
                        kfree(nowner_sid_ptr);
                        *aclflag = CIFS_ACL_OWNER;
                }
@@ -1135,8 +838,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
                                kfree(ngroup_sid_ptr);
                                return rc;
                        }
-                       memcpy(group_sid_ptr, ngroup_sid_ptr,
-                                       sizeof(struct cifs_sid));
+                       cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr);
                        kfree(ngroup_sid_ptr);
                        *aclflag = CIFS_ACL_GROUP;
                }
@@ -1218,7 +920,7 @@ struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
        if (!open_file)
                return get_cifs_acl_by_path(cifs_sb, path, pacllen);
 
-       pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
+       pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->fid.netfid, pacllen);
        cifsFileInfo_put(open_file);
        return pntsd;
 }
@@ -1312,42 +1014,39 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
 
        /* Get the security descriptor */
        pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
-
-       /* Add three ACEs for owner, group, everyone getting rid of
-          other ACEs as chmod disables ACEs and set the security descriptor */
-
        if (IS_ERR(pntsd)) {
                rc = PTR_ERR(pntsd);
                cERROR(1, "%s: error %d getting sec desc", __func__, rc);
-       } else {
-               /* allocate memory for the smb header,
-                  set security descriptor request security descriptor
-                  parameters, and secuirty descriptor itself */
-
-               secdesclen = secdesclen < DEFSECDESCLEN ?
-                                       DEFSECDESCLEN : secdesclen;
-               pnntsd = kmalloc(secdesclen, GFP_KERNEL);
-               if (!pnntsd) {
-                       cERROR(1, "Unable to allocate security descriptor");
-                       kfree(pntsd);
-                       return -ENOMEM;
-               }
+               goto out;
+       }
 
-               rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
-                                       &aclflag);
+       /*
+        * Add three ACEs for owner, group, everyone getting rid of other ACEs
+        * as chmod disables ACEs and set the security descriptor. Allocate
+        * memory for the smb header, set security descriptor request security
+        * descriptor parameters, and secuirty descriptor itself
+        */
+       secdesclen = max_t(u32, secdesclen, DEFAULT_SEC_DESC_LEN);
+       pnntsd = kmalloc(secdesclen, GFP_KERNEL);
+       if (!pnntsd) {
+               cERROR(1, "Unable to allocate security descriptor");
+               kfree(pntsd);
+               return -ENOMEM;
+       }
 
-               cFYI(DBG2, "build_sec_desc rc: %d", rc);
+       rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
+                               &aclflag);
 
-               if (!rc) {
-                       /* Set the security descriptor */
-                       rc = set_cifs_acl(pnntsd, secdesclen, inode,
-                                               path, aclflag);
-                       cFYI(DBG2, "set_cifs_acl rc: %d", rc);
-               }
+       cFYI(DBG2, "build_sec_desc rc: %d", rc);
 
-               kfree(pnntsd);
-               kfree(pntsd);
+       if (!rc) {
+               /* Set the security descriptor */
+               rc = set_cifs_acl(pnntsd, secdesclen, inode, path, aclflag);
+               cFYI(DBG2, "set_cifs_acl rc: %d", rc);
        }
 
+       kfree(pnntsd);
+       kfree(pntsd);
+out:
        return rc;
 }