summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-12 09:24:13 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-12 09:24:13 -0800
commit6facac1ab68fbf9cbad31a9d521f3a0d6aa9470e (patch)
tree1b00b4d9aab6c5567036157044da98857a84237a /fs
parent3f1c64f410e4394ecefadd7a597a7c20368a65fc (diff)
parentc299dd0e2d3dd61d0048a9d9b021aa01f023ed0c (diff)
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS fixes from Steve French: "This includes a set of misc. cifs fixes (most importantly some byte range lock related write fixes from Pavel, and some ACL and idmap related fixes from Jeff) but also includes the SMB2.02 dialect enablement, and a key fix for SMB3 mounts. Default authentication upgraded to ntlmv2 for cifs (it was already ntlmv2 for smb2)" * 'for-next' of git://git.samba.org/sfrench/cifs-2.6: (43 commits) CIFS: Fix write after setting a read lock for read oplock files cifs: parse the device name into UNC and prepath cifs: fix up handling of prefixpath= option cifs: clean up handling of unc= option cifs: fix SID binary to string conversion fix "disabling echoes and oplocks" on SMB2 mounts Do not send SMB2 signatures for SMB3 frames cifs: deal with id_to_sid embedded sid reply corner case cifs: fix hardcoded default security descriptor length cifs: extra sanity checking for cifs.idmap keys cifs: avoid extra allocation for small cifs.idmap keys cifs: simplify id_to_sid and sid_to_id mapping code CIFS: Fix possible data coherency problem after oplock break to None CIFS: Do not permit write to a range mandatory locked with a read lock cifs: rename cifs_readdir_lookup to cifs_prime_dcache and make it void return cifs: Add CONFIG_CIFS_DEBUG and rename use of CIFS_DEBUG cifs: Make CIFS_DEBUG possible to undefine SMB3 mounts fail with access denied to some servers cifs: Remove unused cEVENT macro cifs: always zero out smb_vol before parsing options ...
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/Kconfig10
-rw-r--r--fs/cifs/cifs_debug.h72
-rw-r--r--fs/cifs/cifsacl.c746
-rw-r--r--fs/cifs/cifsacl.h66
-rw-r--r--fs/cifs/cifsfs.c17
-rw-r--r--fs/cifs/cifsglob.h36
-rw-r--r--fs/cifs/cifsproto.h10
-rw-r--r--fs/cifs/connect.c310
-rw-r--r--fs/cifs/dir.c32
-rw-r--r--fs/cifs/file.c200
-rw-r--r--fs/cifs/inode.c7
-rw-r--r--fs/cifs/netmisc.c14
-rw-r--r--fs/cifs/readdir.c52
-rw-r--r--fs/cifs/smb1ops.c32
-rw-r--r--fs/cifs/smb2file.c12
-rw-r--r--fs/cifs/smb2ops.c103
-rw-r--r--fs/cifs/smb2pdu.c5
-rw-r--r--fs/cifs/smb2proto.h4
-rw-r--r--fs/cifs/smb2transport.c13
19 files changed, 787 insertions, 954 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 2075ddfffa73..21ff76c22a17 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -122,9 +122,17 @@ config CIFS_ACL
Allows fetching CIFS/NTFS ACL from the server. The DACL blob
is handed over to the application/caller.
+config CIFS_DEBUG
+ bool "Enable CIFS debugging routines"
+ default y
+ depends on CIFS
+ help
+ Enabling this option adds helpful debugging messages to
+ the cifs code which increases the size of the cifs module.
+ If unsure, say Y.
config CIFS_DEBUG2
bool "Enable additional CIFS debugging routines"
- depends on CIFS
+ depends on CIFS_DEBUG
help
Enabling this option adds a few more debugging routines
to the cifs code which slightly increases the size of
diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h
index c0c68bb492d7..86e92ef2abc1 100644
--- a/fs/cifs/cifs_debug.h
+++ b/fs/cifs/cifs_debug.h
@@ -18,7 +18,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#define CIFS_DEBUG /* BB temporary */
#ifndef _H_CIFS_DEBUG
#define _H_CIFS_DEBUG
@@ -37,49 +36,43 @@ void dump_smb(void *, int);
#define CIFS_RC 0x02
#define CIFS_TIMER 0x04
+extern int cifsFYI;
+extern int cifsERROR;
+
/*
* debug ON
* --------
*/
-#ifdef CIFS_DEBUG
+#ifdef CONFIG_CIFS_DEBUG
/* information message: e.g., configuration, major event */
-extern int cifsFYI;
-#define cifsfyi(fmt, arg...) \
+#define cifsfyi(fmt, ...) \
do { \
if (cifsFYI & CIFS_INFO) \
- printk(KERN_DEBUG "%s: " fmt "\n", __FILE__, ##arg); \
+ printk(KERN_DEBUG "%s: " fmt "\n", \
+ __FILE__, ##__VA_ARGS__); \
} while (0)
-#define cFYI(set, fmt, arg...) \
-do { \
- if (set) \
- cifsfyi(fmt, ##arg); \
+#define cFYI(set, fmt, ...) \
+do { \
+ if (set) \
+ cifsfyi(fmt, ##__VA_ARGS__); \
} while (0)
-#define cifswarn(fmt, arg...) \
- printk(KERN_WARNING fmt "\n", ##arg)
+#define cifswarn(fmt, ...) \
+ printk(KERN_WARNING fmt "\n", ##__VA_ARGS__)
-/* debug event message: */
-extern int cifsERROR;
-
-#define cEVENT(fmt, arg...) \
+/* error event message: e.g., i/o error */
+#define cifserror(fmt, ...) \
do { \
if (cifsERROR) \
- printk(KERN_EVENT "%s: " fmt "\n", __FILE__, ##arg); \
-} while (0)
-
-/* error event message: e.g., i/o error */
-#define cifserror(fmt, arg...) \
-do { \
- if (cifsERROR) \
- printk(KERN_ERR "CIFS VFS: " fmt "\n", ##arg); \
+ printk(KERN_ERR "CIFS VFS: " fmt "\n", ##__VA_ARGS__); \
} while (0)
-#define cERROR(set, fmt, arg...) \
-do { \
- if (set) \
- cifserror(fmt, ##arg); \
+#define cERROR(set, fmt, ...) \
+do { \
+ if (set) \
+ cifserror(fmt, ##__VA_ARGS__); \
} while (0)
/*
@@ -87,10 +80,27 @@ do { \
* ---------
*/
#else /* _CIFS_DEBUG */
-#define cERROR(set, fmt, arg...)
-#define cEVENT(fmt, arg...)
-#define cFYI(set, fmt, arg...)
-#define cifserror(fmt, arg...)
+#define cifsfyi(fmt, ...) \
+do { \
+ if (0) \
+ printk(KERN_DEBUG "%s: " fmt "\n", \
+ __FILE__, ##__VA_ARGS__); \
+} while (0)
+#define cFYI(set, fmt, ...) \
+do { \
+ if (0 && set) \
+ cifsfyi(fmt, ##__VA_ARGS__); \
+} while (0)
+#define cifserror(fmt, ...) \
+do { \
+ if (0) \
+ printk(KERN_ERR "CIFS VFS: " fmt "\n", ##__VA_ARGS__); \
+} while (0)
+#define cERROR(set, fmt, ...) \
+do { \
+ if (0 && set) \
+ cifserror(fmt, ##__VA_ARGS__); \
+} while (0)
#endif /* _CIFS_DEBUG */
#endif /* _H_CIFS_DEBUG */
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 0fb15bbbe43c..75c1ee699143 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -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,221 +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;
}
-}
-static void
-cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
-{
- memcpy(dst, src, sizeof(*dst));
- dst->num_subauth = min_t(u8, src->num_subauth, NUM_SUBAUTHS);
+ 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;
- }
- }
-
- cifs_copy_sid(&(*psidid)->sid, sidptr);
- (*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)) {
- cifs_copy_sid(ssid, &psidid->sid);
- psidid->time = jiffies; /* update ts for accessing */
- 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 (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 if (sidkey->datalen < sizeof(struct cifs_sid)) {
- rc = -EIO;
- cFYI(1, "%s: Downcall contained malformed key "
- "(datalen=%hu)", __func__, sidkey->datalen);
- } else {
- lsid = (struct cifs_sid *)sidkey->payload.data;
- cifs_copy_sid(&psidid->sid, lsid);
- cifs_copy_sid(ssid, &psidid->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))
- cifs_copy_sid(ssid, &psidid->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
@@ -417,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;
}
@@ -568,17 +370,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;
@@ -595,89 +386,9 @@ 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)
@@ -811,7 +522,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];
@@ -987,8 +698,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);
@@ -1002,8 +713,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;
}
@@ -1307,42 +1018,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;