|
@@ -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;
|
|
|
}
|
|
|
|
|
@@ -223,6 +291,120 @@ sidid_pending_wait(void *unused)
|
|
|
return signal_pending(current) ? -ERESTARTSYS : 0;
|
|
|
}
|
|
|
|
|
|
+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
|