aboutsummaryrefslogtreecommitdiffstats
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
parentMerge tag 'for-linus-v3.8-rc1' of git://oss.sgi.com/xfs/xfs (diff)
parentCIFS: Fix write after setting a read lock for read oplock files (diff)
downloadlinux-dev-6facac1ab68fbf9cbad31a9d521f3a0d6aa9470e.tar.xz
linux-dev-6facac1ab68fbf9cbad31a9d521f3a0d6aa9470e.zip
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;
- }
+ 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;
}
diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h
index 5c902c7ce524..4f3884835267 100644
--- a/fs/cifs/cifsacl.h
+++ b/fs/cifs/cifsacl.h
@@ -23,11 +23,8 @@
#define _CIFSACL_H
-#define NUM_AUTHS 6 /* number of authority fields */
-#define NUM_SUBAUTHS 5 /* number of sub authority fields */
-#define NUM_WK_SIDS 7 /* number of well known sids */
-#define SIDNAMELENGTH 20 /* long enough for the ones we care about */
-#define DEFSECDESCLEN 192 /* sec desc len contaiting a dacl with three aces */
+#define NUM_AUTHS (6) /* number of authority fields */
+#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */
#define READ_BIT 0x4
#define WRITE_BIT 0x2
@@ -41,12 +38,32 @@
#define SIDOWNER 1
#define SIDGROUP 2
-#define SIDLEN 150 /* S- 1 revision- 6 authorities- max 5 sub authorities */
-#define SID_ID_MAPPED 0
-#define SID_ID_PENDING 1
-#define SID_MAP_EXPIRE (3600 * HZ) /* map entry expires after one hour */
-#define SID_MAP_RETRY (300 * HZ) /* wait 5 minutes for next attempt to map */
+/*
+ * Security Descriptor length containing DACL with 3 ACEs (one each for
+ * owner, group and world).
+ */
+#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \
+ sizeof(struct cifs_acl) + \
+ (sizeof(struct cifs_ace) * 3))
+
+/*
+ * Maximum size of a string representation of a SID:
+ *
+ * The fields are unsigned values in decimal. So:
+ *
+ * u8: max 3 bytes in decimal
+ * u32: max 10 bytes in decimal
+ *
+ * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator
+ *
+ * For authority field, max is when all 6 values are non-zero and it must be
+ * represented in hex. So "-0x" + 12 hex digits.
+ *
+ * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-')
+ */
+#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1)
+#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */
struct cifs_ntsd {
__le16 revision; /* revision level */
@@ -60,10 +77,13 @@ struct cifs_ntsd {
struct cifs_sid {
__u8 revision; /* revision level */
__u8 num_subauth;
- __u8 authority[6];
- __le32 sub_auth[5]; /* sub_auth[num_subauth] */
+ __u8 authority[NUM_AUTHS];
+ __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
} __attribute__((packed));
+/* size of a struct cifs_sid, sans sub_auth array */
+#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS)
+
struct cifs_acl {
__le16 revision; /* revision level */
__le16 size;
@@ -78,26 +98,4 @@ struct cifs_ace {
struct cifs_sid sid; /* ie UUID of user or group who gets these perms */
} __attribute__((packed));
-struct cifs_wksid {
- struct cifs_sid cifssid;
- char sidname[SIDNAMELENGTH];
-} __attribute__((packed));
-
-struct cifs_sid_id {
- unsigned int refcount; /* increment with spinlock, decrement without */
- unsigned long id;
- unsigned long time;
- unsigned long state;
- char *sidstr;
- struct rb_node rbnode;
- struct cifs_sid sid;
-};
-
-#ifdef __KERNEL__
-extern struct key_type cifs_idmap_key_type;
-extern const struct cred *root_cred;
-#endif /* KERNEL */
-
-extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *);
-
#endif /* _CIFSACL_H */
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index e7931cc55d0c..210f0af83fc4 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -64,24 +64,23 @@ unsigned int global_secflags = CIFSSEC_DEF;
unsigned int sign_CIFS_PDUs = 1;
static const struct super_operations cifs_super_ops;
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
-module_param(CIFSMaxBufSize, int, 0);
+module_param(CIFSMaxBufSize, uint, 0);
MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). "
"Default: 16384 Range: 8192 to 130048");
unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL;
-module_param(cifs_min_rcv, int, 0);
+module_param(cifs_min_rcv, uint, 0);
MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: "
"1 to 64");
unsigned int cifs_min_small = 30;
-module_param(cifs_min_small, int, 0);
+module_param(cifs_min_small, uint, 0);
MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 "
"Range: 2 to 256");
unsigned int cifs_max_pending = CIFS_MAX_REQ;
-module_param(cifs_max_pending, int, 0444);
+module_param(cifs_max_pending, uint, 0444);
MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. "
"Default: 32767 Range: 2 to 32767.");
module_param(enable_oplocks, bool, 0644);
-MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks (bool). Default:"
- "y/Y/1");
+MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1");
extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
@@ -230,6 +229,7 @@ cifs_alloc_inode(struct super_block *sb)
cifs_set_oplock_level(cifs_inode, 0);
cifs_inode->delete_pending = false;
cifs_inode->invalid_mapping = false;
+ cifs_inode->leave_pages_clean = false;
cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
cifs_inode->server_eof = 0;
cifs_inode->uniqueid = 0;
@@ -540,8 +540,8 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
char *s, *p;
char sep;
- full_path = build_path_to_root(vol, cifs_sb,
- cifs_sb_master_tcon(cifs_sb));
+ full_path = cifs_build_path_to_root(vol, cifs_sb,
+ cifs_sb_master_tcon(cifs_sb));
if (full_path == NULL)
return ERR_PTR(-ENOMEM);
@@ -1205,7 +1205,6 @@ exit_cifs(void)
unregister_filesystem(&cifs_fs_type);
cifs_dfs_release_automount_timer();
#ifdef CONFIG_CIFS_ACL
- cifs_destroy_idmaptrees();
exit_cifs_idmap();
#endif
#ifdef CONFIG_CIFS_UPCALL
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index f5af2527fc69..aea1eec64911 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -178,6 +178,7 @@ struct smb_rqst {
enum smb_version {
Smb_1 = 1,
+ Smb_20,
Smb_21,
Smb_30,
};
@@ -280,9 +281,6 @@ struct smb_version_operations {
/* set attributes */
int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
const unsigned int);
- /* build a full path to the root of the mount */
- char * (*build_path_to_root)(struct smb_vol *, struct cifs_sb_info *,
- struct cifs_tcon *);
/* check if we can send an echo or nor */
bool (*can_echo)(struct TCP_Server_Info *);
/* send echo request */
@@ -369,6 +367,8 @@ struct smb_version_operations {
void (*set_lease_key)(struct inode *, struct cifs_fid *fid);
/* generate new lease key */
void (*new_lease_key)(struct cifs_fid *fid);
+ int (*calc_signature)(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server);
};
struct smb_version_values {
@@ -396,7 +396,6 @@ struct smb_vol {
char *password;
char *domainname;
char *UNC;
- char *UNCip;
char *iocharset; /* local code page for mapping to and from Unicode */
char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
@@ -444,11 +443,11 @@ struct smb_vol {
unsigned int rsize;
unsigned int wsize;
bool sockopt_tcp_nodelay:1;
- unsigned short int port;
unsigned long actimeo; /* attribute cache timeout (jiffies) */
struct smb_version_operations *ops;
struct smb_version_values *vals;
char *prepath;
+ struct sockaddr_storage dstaddr; /* destination address */
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
struct nls_table *local_nls;
};
@@ -1031,6 +1030,7 @@ struct cifsInodeInfo {
bool clientCanCacheAll; /* read and writebehind oplock */
bool delete_pending; /* DELETE_ON_CLOSE is set */
bool invalid_mapping; /* pagecache is invalid */
+ bool leave_pages_clean; /* protected by i_mutex, not set pages dirty */
unsigned long time; /* jiffies of last update of inode */
u64 server_eof; /* current file size on server -- protected by i_lock */
u64 uniqueid; /* server inode number */
@@ -1067,30 +1067,16 @@ static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)
static inline void
convert_delimiter(char *path, char delim)
{
- int i;
- char old_delim;
-
- if (path == NULL)
- return;
+ char old_delim, *pos;
if (delim == '/')
old_delim = '\\';
else
old_delim = '/';
- for (i = 0; path[i] != '\0'; i++) {
- if (path[i] == old_delim)
- path[i] = delim;
- }
-}
-
-static inline char *
-build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
- struct cifs_tcon *tcon)
-{
- if (!vol->ops->build_path_to_root)
- return NULL;
- return vol->ops->build_path_to_root(vol, cifs_sb, tcon);
+ pos = path;
+ while ((pos = strchr(pos, old_delim)))
+ *pos = delim;
}
#ifdef CONFIG_CIFS_STATS
@@ -1362,7 +1348,7 @@ require use of the stronger protocol */
#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */
#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */
-#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP)
+#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMSSP)
#define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2)
#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)
/*
@@ -1506,6 +1492,6 @@ extern struct smb_version_values smb20_values;
extern struct smb_version_operations smb21_operations;
extern struct smb_version_values smb21_values;
#define SMB30_VERSION_STRING "3.0"
-/*extern struct smb_version_operations smb30_operations; */ /* not needed yet */
+extern struct smb_version_operations smb30_operations;
extern struct smb_version_values smb30_values;
#endif /* _CIFS_GLOB_H */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 5144e9fbeb8c..1988c1baa224 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -58,8 +58,10 @@ do { \
} while (0)
extern int init_cifs_idmap(void);
extern void exit_cifs_idmap(void);
-extern void cifs_destroy_idmaptrees(void);
extern char *build_path_from_dentry(struct dentry *);
+extern char *cifs_build_path_to_root(struct smb_vol *vol,
+ struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon);
extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
extern char *cifs_compose_mount_options(const char *sb_mountdata,
const char *fullpath, const struct dfs_info3_param *ref,
@@ -107,9 +109,7 @@ extern unsigned int smbCalcSize(void *buf);
extern int decode_negTokenInit(unsigned char *security_blob, int length,
struct TCP_Server_Info *server);
extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
-extern int cifs_set_port(struct sockaddr *addr, const unsigned short int port);
-extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len,
- const unsigned short int port);
+extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port);
extern int map_smb_to_linux_error(char *buf, bool logErr);
extern void header_assemble(struct smb_hdr *, char /* command */ ,
const struct cifs_tcon *, int /* length of
@@ -185,7 +185,7 @@ extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
__u64 length, __u8 type,
struct cifsLockInfo **conf_lock,
- bool rw_check);
+ int rw_check);
extern void cifs_add_pending_open(struct cifs_fid *fid,
struct tcon_link *tlink,
struct cifs_pending_open *open);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 5c670b998ffb..7635b5db26a7 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -186,6 +186,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_user, "user=%s" },
{ Opt_user, "username=%s" },
{ Opt_blank_pass, "pass=" },
+ { Opt_blank_pass, "password=" },
{ Opt_pass, "pass=%s" },
{ Opt_pass, "password=%s" },
{ Opt_blank_ip, "ip=" },
@@ -274,6 +275,7 @@ static const match_table_t cifs_cacheflavor_tokens = {
static const match_table_t cifs_smb_version_tokens = {
{ Smb_1, SMB1_VERSION_STRING },
+ { Smb_20, SMB20_VERSION_STRING},
{ Smb_21, SMB21_VERSION_STRING },
{ Smb_30, SMB30_VERSION_STRING },
};
@@ -1074,12 +1076,16 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
vol->vals = &smb1_values;
break;
#ifdef CONFIG_CIFS_SMB2
+ case Smb_20:
+ vol->ops = &smb21_operations; /* currently identical with 2.1 */
+ vol->vals = &smb20_values;
+ break;
case Smb_21:
vol->ops = &smb21_operations;
vol->vals = &smb21_values;
break;
case Smb_30:
- vol->ops = &smb21_operations; /* currently identical with 2.1 */
+ vol->ops = &smb30_operations;
vol->vals = &smb30_values;
break;
#endif
@@ -1090,6 +1096,52 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
return 0;
}
+/*
+ * Parse a devname into substrings and populate the vol->UNC and vol->prepath
+ * fields with the result. Returns 0 on success and an error otherwise.
+ */
+static int
+cifs_parse_devname(const char *devname, struct smb_vol *vol)
+{
+ char *pos;
+ const char *delims = "/\\";
+ size_t len;
+
+ /* make sure we have a valid UNC double delimiter prefix */
+ len = strspn(devname, delims);
+ if (len != 2)
+ return -EINVAL;
+
+ /* find delimiter between host and sharename */
+ pos = strpbrk(devname + 2, delims);
+ if (!pos)
+ return -EINVAL;
+
+ /* skip past delimiter */
+ ++pos;
+
+ /* now go until next delimiter or end of string */
+ len = strcspn(pos, delims);
+
+ /* move "pos" up to delimiter or NULL */
+ pos += len;
+ vol->UNC = kstrndup(devname, pos - devname, GFP_KERNEL);
+ if (!vol->UNC)
+ return -ENOMEM;
+
+ convert_delimiter(vol->UNC, '\\');
+
+ /* If pos is NULL, or is a bogus trailing delimiter then no prepath */
+ if (!*pos++ || !*pos)
+ return 0;
+
+ vol->prepath = kstrdup(pos, GFP_KERNEL);
+ if (!vol->prepath)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int
cifs_parse_mount_options(const char *mountdata, const char *devname,
struct smb_vol *vol)
@@ -1108,11 +1160,17 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
char *string = NULL;
char *tmp_end, *value;
char delim;
+ bool got_ip = false;
+ unsigned short port = 0;
+ struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr;
separator[0] = ',';
separator[1] = 0;
delim = separator[0];
+ /* ensure we always start with zeroed-out smb_vol */
+ memset(vol, 0, sizeof(*vol));
+
/*
* does not have to be perfect mapping since field is
* informational, only used for servers that do not support
@@ -1169,6 +1227,16 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->backupuid_specified = false; /* no backup intent for a user */
vol->backupgid_specified = false; /* no backup intent for a group */
+ /*
+ * For now, we ignore -EINVAL errors under the assumption that the
+ * unc= and prefixpath= options will be usable.
+ */
+ if (cifs_parse_devname(devname, vol) == -ENOMEM) {
+ printk(KERN_ERR "CIFS: Unable to allocate memory to parse "
+ "device string.\n");
+ goto out_nomem;
+ }
+
while ((data = strsep(&options, separator)) != NULL) {
substring_t args[MAX_OPT_ARGS];
unsigned long option;
@@ -1416,12 +1484,12 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->dir_mode = option;
break;
case Opt_port:
- if (get_option_ul(args, &option)) {
- cERROR(1, "%s: Invalid port value",
- __func__);
+ if (get_option_ul(args, &option) ||
+ option > USHRT_MAX) {
+ cERROR(1, "%s: Invalid port value", __func__);
goto cifs_parse_mount_err;
}
- vol->port = option;
+ port = (unsigned short)option;
break;
case Opt_rsize:
if (get_option_ul(args, &option)) {
@@ -1537,53 +1605,48 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->password[j] = '\0';
break;
case Opt_blank_ip:
- vol->UNCip = NULL;
+ /* FIXME: should this be an error instead? */
+ got_ip = false;
break;
case Opt_ip:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
- if (strnlen(string, INET6_ADDRSTRLEN) >
- INET6_ADDRSTRLEN) {
- printk(KERN_WARNING "CIFS: ip address "
- "too long\n");
- goto cifs_parse_mount_err;
- }
- vol->UNCip = kstrdup(string, GFP_KERNEL);
- if (!vol->UNCip) {
- printk(KERN_WARNING "CIFS: no memory "
- "for UNC IP\n");
+ if (!cifs_convert_address(dstaddr, string,
+ strlen(string))) {
+ printk(KERN_ERR "CIFS: bad ip= option (%s).\n",
+ string);
goto cifs_parse_mount_err;
}
+ got_ip = true;
break;
case Opt_unc:
- string = match_strdup(args);
- if (string == NULL)
+ string = vol->UNC;
+ vol->UNC = match_strdup(args);
+ if (vol->UNC == NULL) {
+ kfree(string);
goto out_nomem;
-
- temp_len = strnlen(string, 300);
- if (temp_len == 300) {
- printk(KERN_WARNING "CIFS: UNC name too long\n");
- goto cifs_parse_mount_err;
}
- vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
- if (vol->UNC == NULL) {
- printk(KERN_WARNING "CIFS: no memory for UNC\n");
- goto cifs_parse_mount_err;
- }
- strcpy(vol->UNC, string);
-
- if (strncmp(string, "//", 2) == 0) {
- vol->UNC[0] = '\\';
- vol->UNC[1] = '\\';
- } else if (strncmp(string, "\\\\", 2) != 0) {
- printk(KERN_WARNING "CIFS: UNC Path does not "
- "begin with // or \\\\\n");
+ convert_delimiter(vol->UNC, '\\');
+ if (vol->UNC[0] != '\\' || vol->UNC[1] != '\\') {
+ kfree(string);
+ printk(KERN_ERR "CIFS: UNC Path does not "
+ "begin with // or \\\\\n");
goto cifs_parse_mount_err;
}
+ /* Compare old unc= option to new one */
+ if (!string || strcmp(string, vol->UNC))
+ printk(KERN_WARNING "CIFS: the value of the "
+ "unc= mount option does not match the "
+ "device string. Using the unc= option "
+ "for now. In 3.10, that option will "
+ "be ignored and the contents of the "
+ "device string will be used "
+ "instead. (%s != %s)\n", string,
+ vol->UNC);
break;
case Opt_domain:
string = match_strdup(args);
@@ -1618,31 +1681,26 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
}
break;
case Opt_prefixpath:
- string = match_strdup(args);
- if (string == NULL)
- goto out_nomem;
-
- temp_len = strnlen(string, 1024);
- if (string[0] != '/')
- temp_len++; /* missing leading slash */
- if (temp_len > 1024) {
- printk(KERN_WARNING "CIFS: prefix too long\n");
- goto cifs_parse_mount_err;
- }
+ /* skip over any leading delimiter */
+ if (*args[0].from == '/' || *args[0].from == '\\')
+ args[0].from++;
- vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
+ string = vol->prepath;
+ vol->prepath = match_strdup(args);
if (vol->prepath == NULL) {
- printk(KERN_WARNING "CIFS: no memory "
- "for path prefix\n");
- goto cifs_parse_mount_err;
+ kfree(string);
+ goto out_nomem;
}
-
- if (string[0] != '/') {
- vol->prepath[0] = '/';
- strcpy(vol->prepath+1, string);
- } else
- strcpy(vol->prepath, string);
-
+ /* Compare old prefixpath= option to new one */
+ if (!string || strcmp(string, vol->prepath))
+ printk(KERN_WARNING "CIFS: the value of the "
+ "prefixpath= mount option does not "
+ "match the device string. Using the "
+ "prefixpath= option for now. In 3.10, "
+ "that option will be ignored and the "
+ "contents of the device string will be "
+ "used instead.(%s != %s)\n", string,
+ vol->prepath);
break;
case Opt_iocharset:
string = match_strdup(args);
@@ -1799,9 +1857,30 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
goto cifs_parse_mount_err;
}
#endif
+ if (!vol->UNC) {
+ cERROR(1, "CIFS mount error: No usable UNC path provided in "
+ "device string or in unc= option!");
+ goto cifs_parse_mount_err;
+ }
- if (vol->UNCip == NULL)
- vol->UNCip = &vol->UNC[2];
+ /* make sure UNC has a share name */
+ if (!strchr(vol->UNC + 3, '\\')) {
+ cERROR(1, "Malformed UNC. Unable to find share name.");
+ goto cifs_parse_mount_err;
+ }
+
+ if (!got_ip) {
+ /* No ip= option specified? Try to get it from UNC */
+ if (!cifs_convert_address(dstaddr, &vol->UNC[2],
+ strlen(&vol->UNC[2]))) {
+ printk(KERN_ERR "Unable to determine destination "
+ "address.\n");
+ goto cifs_parse_mount_err;
+ }
+ }
+
+ /* set the port that we got earlier */
+ cifs_set_port(dstaddr, port);
if (uid_specified)
vol->override_uid = override_uid;
@@ -1972,9 +2051,10 @@ match_security(struct TCP_Server_Info *server, struct smb_vol *vol)
return true;
}
-static int match_server(struct TCP_Server_Info *server, struct sockaddr *addr,
- struct smb_vol *vol)
+static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
{
+ struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr;
+
if ((server->vals != vol->vals) || (server->ops != vol->ops))
return 0;
@@ -1995,13 +2075,13 @@ static int match_server(struct TCP_Server_Info *server, struct sockaddr *addr,
}
static struct TCP_Server_Info *
-cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
+cifs_find_tcp_session(struct smb_vol *vol)
{
struct TCP_Server_Info *server;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
- if (!match_server(server, addr, vol))
+ if (!match_server(server, vol))
continue;
++server->srv_count;
@@ -2051,40 +2131,12 @@ static struct TCP_Server_Info *
cifs_get_tcp_session(struct smb_vol *volume_info)
{
struct TCP_Server_Info *tcp_ses = NULL;
- struct sockaddr_storage addr;
- struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
- struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
int rc;
- memset(&addr, 0, sizeof(struct sockaddr_storage));
-
- cFYI(1, "UNC: %s ip: %s", volume_info->UNC, volume_info->UNCip);
-
- if (volume_info->UNCip && volume_info->UNC) {
- rc = cifs_fill_sockaddr((struct sockaddr *)&addr,
- volume_info->UNCip,
- strlen(volume_info->UNCip),
- volume_info->port);
- if (!rc) {
- /* we failed translating address */
- rc = -EINVAL;
- goto out_err;
- }
- } else if (volume_info->UNCip) {
- /* BB using ip addr as tcp_ses name to connect to the
- DFS root below */
- cERROR(1, "Connecting to DFS root not implemented yet");
- rc = -EINVAL;
- goto out_err;
- } else /* which tcp_sess DFS root would we conect to */ {
- cERROR(1, "CIFS mount error: No UNC path (e.g. -o "
- "unc=//192.168.1.100/public) specified");
- rc = -EINVAL;
- goto out_err;
- }
+ cFYI(1, "UNC: %s", volume_info->UNC);
/* see if we already have a matching tcp_ses */
- tcp_ses = cifs_find_tcp_session((struct sockaddr *)&addr, volume_info);
+ tcp_ses = cifs_find_tcp_session(volume_info);
if (tcp_ses)
return tcp_ses;
@@ -2129,27 +2181,18 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
-
+ memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
+ sizeof(tcp_ses->srcaddr));
+ memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
+ sizeof(tcp_ses->dstaddr));
/*
* at this point we are the only ones with the pointer
* to the struct since the kernel thread not created yet
* no need to spinlock this init of tcpStatus or srv_count
*/
tcp_ses->tcpStatus = CifsNew;
- memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
- sizeof(tcp_ses->srcaddr));
++tcp_ses->srv_count;
- if (addr.ss_family == AF_INET6) {
- cFYI(1, "attempting ipv6 connect");
- /* BB should we allow ipv6 on port 139? */
- /* other OS never observed in Wild doing 139 with v6 */
- memcpy(&tcp_ses->dstaddr, sin_server6,
- sizeof(struct sockaddr_in6));
- } else
- memcpy(&tcp_ses->dstaddr, sin_server,
- sizeof(struct sockaddr_in));
-
rc = ip_connect(tcp_ses);
if (rc < 0) {
cERROR(1, "Error connecting to socket. Aborting operation");
@@ -2397,8 +2440,6 @@ cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)),
}
#endif /* CONFIG_KEYS */
-static bool warned_on_ntlm; /* globals init to false automatically */
-
static struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
{
@@ -2475,14 +2516,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
ses->cred_uid = volume_info->cred_uid;
ses->linux_uid = volume_info->linux_uid;
- /* ntlmv2 is much stronger than ntlm security, and has been broadly
- supported for many years, time to update default security mechanism */
- if ((volume_info->secFlg == 0) && warned_on_ntlm == false) {
- warned_on_ntlm = true;
- cERROR(1, "default security mechanism requested. The default "
- "security mechanism will be upgraded from ntlm to "
- "ntlmv2 in kernel release 3.3");
- }
ses->overrideSecFlg = volume_info->secFlg;
mutex_lock(&ses->session_mutex);
@@ -2598,13 +2631,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
}
}
- if (strchr(volume_info->UNC + 3, '\\') == NULL
- && strchr(volume_info->UNC + 3, '/') == NULL) {
- cERROR(1, "Missing share name");
- rc = -ENODEV;
- goto out_fail;
- }
-
/*
* BB Do we need to wrap session_mutex around this TCon call and Unix
* SetFS as we do on SessSetup and reconnect?
@@ -2718,11 +2744,8 @@ cifs_match_super(struct super_block *sb, void *data)
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct tcon_link *tlink;
- struct sockaddr_storage addr;
int rc = 0;
- memset(&addr, 0, sizeof(struct sockaddr_storage));
-
spin_lock(&cifs_tcp_ses_lock);
cifs_sb = CIFS_SB(sb);
tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
@@ -2736,17 +2759,7 @@ cifs_match_super(struct super_block *sb, void *data)
volume_info = mnt_data->vol;
- if (!volume_info->UNCip || !volume_info->UNC)
- goto out;
-
- rc = cifs_fill_sockaddr((struct sockaddr *)&addr,
- volume_info->UNCip,
- strlen(volume_info->UNCip),
- volume_info->port);
- if (!rc)
- goto out;
-
- if (!match_server(tcp_srv, (struct sockaddr *)&addr, volume_info) ||
+ if (!match_server(tcp_srv, volume_info) ||
!match_session(ses, volume_info) ||
!match_tcon(tcon, volume_info->UNC)) {
rc = 0;
@@ -3261,8 +3274,6 @@ cleanup_volume_info_contents(struct smb_vol *volume_info)
{
kfree(volume_info->username);
kzfree(volume_info->password);
- if (volume_info->UNCip != volume_info->UNC + 2)
- kfree(volume_info->UNCip);
kfree(volume_info->UNC);
kfree(volume_info->domainname);
kfree(volume_info->iocharset);
@@ -3280,14 +3291,16 @@ cifs_cleanup_volume_info(struct smb_vol *volume_info)
#ifdef CONFIG_CIFS_DFS_UPCALL
-/* build_path_to_root returns full path to root when
- * we do not have an exiting connection (tcon) */
+/*
+ * cifs_build_path_to_root returns full path to root when we do not have an
+ * exiting connection (tcon)
+ */
static char *
build_unc_path_to_root(const struct smb_vol *vol,
const struct cifs_sb_info *cifs_sb)
{
char *full_path, *pos;
- unsigned int pplen = vol->prepath ? strlen(vol->prepath) : 0;
+ unsigned int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0;
unsigned int unc_len = strnlen(vol->UNC, MAX_TREE_SIZE + 1);
full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL);
@@ -3298,6 +3311,7 @@ build_unc_path_to_root(const struct smb_vol *vol,
pos = full_path + unc_len;
if (pplen) {
+ *pos++ = CIFS_DIR_SEP(cifs_sb);
strncpy(pos, vol->prepath, pplen);
pos += pplen;
}
@@ -3353,7 +3367,6 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
mdata = NULL;
} else {
cleanup_volume_info_contents(volume_info);
- memset(volume_info, '\0', sizeof(*volume_info));
rc = cifs_setup_volume_info(volume_info, mdata,
fake_devname);
}
@@ -3375,7 +3388,6 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
if (cifs_parse_mount_options(mount_data, devname, volume_info))
return -EINVAL;
-
if (volume_info->nullauth) {
cFYI(1, "Anonymous login");
kfree(volume_info->username);
@@ -3412,7 +3424,7 @@ cifs_get_volume_info(char *mount_data, const char *devname)
int rc;
struct smb_vol *volume_info;
- volume_info = kzalloc(sizeof(struct smb_vol), GFP_KERNEL);
+ volume_info = kmalloc(sizeof(struct smb_vol), GFP_KERNEL);
if (!volume_info)
return ERR_PTR(-ENOMEM);
@@ -3537,8 +3549,10 @@ remote_path_check:
rc = -ENOSYS;
goto mount_fail_check;
}
- /* build_path_to_root works only when we have a valid tcon */
- full_path = build_path_to_root(volume_info, cifs_sb, tcon);
+ /*
+ * cifs_build_path_to_root works only when we have a valid tcon
+ */
+ full_path = cifs_build_path_to_root(volume_info, cifs_sb, tcon);
if (full_path == NULL) {
rc = -ENOMEM;
goto mount_fail_check;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index d3671f2acb29..8719bbe0dcc3 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -44,6 +44,38 @@ renew_parental_timestamps(struct dentry *direntry)
} while (!IS_ROOT(direntry));
}
+char *
+cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon)
+{
+ int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0;
+ int dfsplen;
+ char *full_path = NULL;
+
+ /* if no prefix path, simply set path to the root of share to "" */
+ if (pplen == 0) {
+ full_path = kzalloc(1, GFP_KERNEL);
+ return full_path;
+ }
+
+ if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
+ dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
+ else
+ dfsplen = 0;
+
+ full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL);
+ if (full_path == NULL)
+ return full_path;
+
+ if (dfsplen)
+ strncpy(full_path, tcon->treeName, dfsplen);
+ full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb);
+ strncpy(full_path + dfsplen + 1, vol->prepath, pplen);
+ convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
+ full_path[dfsplen + pplen] = 0; /* add trailing null */
+ return full_path;
+}
+
/* Note: caller must free return buffer */
char *
build_path_from_dentry(struct dentry *direntry)
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 70b6f4c3a0c1..0a6677ba212b 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -505,16 +505,36 @@ out:
return rc;
}
+static int cifs_push_posix_locks(struct cifsFileInfo *cfile);
+
/*
* Try to reacquire byte range locks that were released when session
- * to server was lost
+ * to server was lost.
*/
-static int cifs_relock_file(struct cifsFileInfo *cifsFile)
+static int
+cifs_relock_file(struct cifsFileInfo *cfile)
{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
- /* BB list all locks open on this file and relock */
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
+ if (cinode->can_cache_brlcks) {
+ /* can cache locks - no need to push them */
+ up_write(&cinode->lock_sem);
+ return rc;
+ }
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ rc = cifs_push_posix_locks(cfile);
+ else
+ rc = tcon->ses->server->ops->push_mand_locks(cfile);
+
+ up_write(&cinode->lock_sem);
return rc;
}
@@ -739,10 +759,15 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
}
}
+#define CIFS_LOCK_OP 0
+#define CIFS_READ_OP 1
+#define CIFS_WRITE_OP 2
+
+/* @rw_check : 0 - no op, 1 - read, 2 - write */
static bool
cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
__u64 length, __u8 type, struct cifsFileInfo *cfile,
- struct cifsLockInfo **conf_lock, bool rw_check)
+ struct cifsLockInfo **conf_lock, int rw_check)
{
struct cifsLockInfo *li;
struct cifsFileInfo *cur_cfile = fdlocks->cfile;
@@ -752,9 +777,13 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
if (offset + length <= li->offset ||
offset >= li->offset + li->length)
continue;
- if (rw_check && server->ops->compare_fids(cfile, cur_cfile) &&
- current->tgid == li->pid)
- continue;
+ if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid &&
+ server->ops->compare_fids(cfile, cur_cfile)) {
+ /* shared lock prevents write op through the same fid */
+ if (!(li->type & server->vals->shared_lock_type) ||
+ rw_check != CIFS_WRITE_OP)
+ continue;
+ }
if ((type & server->vals->shared_lock_type) &&
((server->ops->compare_fids(cfile, cur_cfile) &&
current->tgid == li->pid) || type == li->type))
@@ -769,7 +798,7 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
bool
cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
__u8 type, struct cifsLockInfo **conf_lock,
- bool rw_check)
+ int rw_check)
{
bool rc = false;
struct cifs_fid_locks *cur;
@@ -805,7 +834,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
down_read(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, offset, length, type,
- &conf_lock, false);
+ &conf_lock, CIFS_LOCK_OP);
if (exist) {
flock->fl_start = conf_lock->offset;
flock->fl_end = conf_lock->offset + conf_lock->length - 1;
@@ -852,7 +881,7 @@ try_again:
down_write(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
- lock->type, &conf_lock, false);
+ lock->type, &conf_lock, CIFS_LOCK_OP);
if (!exist && cinode->can_cache_brlcks) {
list_add_tail(&lock->llist, &cfile->llist->locks);
up_write(&cinode->lock_sem);
@@ -948,7 +977,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
int rc = 0, stored_rc;
struct cifsLockInfo *li, *tmp;
struct cifs_tcon *tcon;
- struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
unsigned int num, max_num, max_buf;
LOCKING_ANDX_RANGE *buf, *cur;
int types[] = {LOCKING_ANDX_LARGE_FILES,
@@ -958,21 +986,12 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
xid = get_xid();
tcon = tlink_tcon(cfile->tlink);
- /* we are going to update can_cache_brlcks here - need a write access */
- down_write(&cinode->lock_sem);
- if (!cinode->can_cache_brlcks) {
- up_write(&cinode->lock_sem);
- free_xid(xid);
- return rc;
- }
-
/*
* Accessing maxBuf is racy with cifs_reconnect - need to store value
* and check it for zero before using.
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf) {
- up_write(&cinode->lock_sem);
free_xid(xid);
return -EINVAL;
}
@@ -981,7 +1000,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
sizeof(LOCKING_ANDX_RANGE);
buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
if (!buf) {
- up_write(&cinode->lock_sem);
free_xid(xid);
return -ENOMEM;
}
@@ -1018,9 +1036,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
}
}
- cinode->can_cache_brlcks = false;
- up_write(&cinode->lock_sem);
-
kfree(buf);
free_xid(xid);
return rc;
@@ -1043,7 +1058,6 @@ struct lock_to_push {
static int
cifs_push_posix_locks(struct cifsFileInfo *cfile)
{
- struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct file_lock *flock, **before;
unsigned int count = 0, i = 0;
@@ -1054,14 +1068,6 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
xid = get_xid();
- /* we are going to update can_cache_brlcks here - need a write access */
- down_write(&cinode->lock_sem);
- if (!cinode->can_cache_brlcks) {
- up_write(&cinode->lock_sem);
- free_xid(xid);
- return rc;
- }
-
lock_flocks();
cifs_for_each_lock(cfile->dentry->d_inode, before) {
if ((*before)->fl_flags & FL_POSIX)
@@ -1127,9 +1133,6 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
}
out:
- cinode->can_cache_brlcks = false;
- up_write(&cinode->lock_sem);
-
free_xid(xid);
return rc;
err_out:
@@ -1144,14 +1147,27 @@ static int
cifs_push_locks(struct cifsFileInfo *cfile)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ int rc = 0;
+
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
+ if (!cinode->can_cache_brlcks) {
+ up_write(&cinode->lock_sem);
+ return rc;
+ }
if (cap_unix(tcon->ses) &&
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
- return cifs_push_posix_locks(cfile);
+ rc = cifs_push_posix_locks(cfile);
+ else
+ rc = tcon->ses->server->ops->push_mand_locks(cfile);
- return tcon->ses->server->ops->push_mand_locks(cfile);
+ cinode->can_cache_brlcks = false;
+ up_write(&cinode->lock_sem);
+ return rc;
}
static void
@@ -1436,16 +1452,18 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
return -ENOMEM;
rc = cifs_lock_add_if(cfile, lock, wait_flag);
- if (rc < 0)
+ if (rc < 0) {
kfree(lock);
- if (rc <= 0)
+ return rc;
+ }
+ if (!rc)
goto out;
rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
type, 1, 0, wait_flag);
if (rc) {
kfree(lock);
- goto out;
+ return rc;
}
cifs_lock_add(cfile, lock);
@@ -2085,7 +2103,15 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
} else {
rc = copied;
pos += copied;
- set_page_dirty(page);
+ /*
+ * When we use strict cache mode and cifs_strict_writev was run
+ * with level II oplock (indicated by leave_pages_clean field of
+ * CIFS_I(inode)), we can leave pages clean - cifs_strict_writev
+ * sent the data to the server itself.
+ */
+ if (!CIFS_I(inode)->leave_pages_clean ||
+ !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO))
+ set_page_dirty(page);
}
if (rc > 0) {
@@ -2436,8 +2462,8 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
}
static ssize_t
-cifs_writev(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+cifs_pagecache_writev(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos, bool cache_ex)
{
struct file *file = iocb->ki_filp;
struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
@@ -2457,10 +2483,14 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
down_read(&cinode->lock_sem);
if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
server->vals->exclusive_lock_type, NULL,
- true)) {
+ CIFS_WRITE_OP)) {
mutex_lock(&inode->i_mutex);
+ if (!cache_ex)
+ cinode->leave_pages_clean = true;
rc = __generic_file_aio_write(iocb, iov, nr_segs,
- &iocb->ki_pos);
+ &iocb->ki_pos);
+ if (!cache_ex)
+ cinode->leave_pages_clean = false;
mutex_unlock(&inode->i_mutex);
}
@@ -2487,42 +2517,62 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
struct cifsFileInfo *cfile = (struct cifsFileInfo *)
iocb->ki_filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
-
-#ifdef CONFIG_CIFS_SMB2
+ ssize_t written, written2;
/*
- * If we have an oplock for read and want to write a data to the file
- * we need to store it in the page cache and then push it to the server
- * to be sure the next read will get a valid data.
+ * We need to store clientCanCacheAll here to prevent race
+ * conditions - this value can be changed during an execution
+ * of generic_file_aio_write. For CIFS it can be changed from
+ * true to false only, but for SMB2 it can be changed both from
+ * true to false and vice versa. So, we can end up with a data
+ * stored in the cache, not marked dirty and not sent to the
+ * server if this value changes its state from false to true
+ * after cifs_write_end.
*/
- if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead) {
- ssize_t written;
- int rc;
-
- written = generic_file_aio_write(iocb, iov, nr_segs, pos);
- rc = filemap_fdatawrite(inode->i_mapping);
- if (rc)
- return (ssize_t)rc;
+ bool cache_ex = cinode->clientCanCacheAll;
+ bool cache_read = cinode->clientCanCacheRead;
+ int rc;
+ loff_t saved_pos;
- return written;
+ if (cache_ex) {
+ if (cap_unix(tcon->ses) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(
+ tcon->fsUnixInfo.Capability)))
+ return generic_file_aio_write(iocb, iov, nr_segs, pos);
+ return cifs_pagecache_writev(iocb, iov, nr_segs, pos, cache_ex);
}
-#endif
/*
- * For non-oplocked files in strict cache mode we need to write the data
- * to the server exactly from the pos to pos+len-1 rather than flush all
- * affected pages because it may cause a error with mandatory locks on
- * these pages but not on the region from pos to ppos+len-1.
+ * For files without exclusive oplock in strict cache mode we need to
+ * write the data to the server exactly from the pos to pos+len-1 rather
+ * than flush all affected pages because it may cause a error with
+ * mandatory locks on these pages but not on the region from pos to
+ * ppos+len-1.
*/
+ written = cifs_user_writev(iocb, iov, nr_segs, pos);
+ if (!cache_read || written <= 0)
+ return written;
- if (!cinode->clientCanCacheAll)
- return cifs_user_writev(iocb, iov, nr_segs, pos);
-
+ saved_pos = iocb->ki_pos;
+ iocb->ki_pos = pos;
+ /* we have a read oplock - need to store a data in the page cache */
if (cap_unix(tcon->ses) &&
- (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
- ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
- return generic_file_aio_write(iocb, iov, nr_segs, pos);
-
- return cifs_writev(iocb, iov, nr_segs, pos);
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(
+ tcon->fsUnixInfo.Capability)))
+ written2 = generic_file_aio_write(iocb, iov, nr_segs, pos);
+ else
+ written2 = cifs_pagecache_writev(iocb, iov, nr_segs, pos,
+ cache_ex);
+ /* errors occured during writing - invalidate the page cache */
+ if (written2 < 0) {
+ rc = cifs_invalidate_mapping(inode);
+ if (rc)
+ written = (ssize_t)rc;
+ else
+ iocb->ki_pos = saved_pos;
+ }
+ return written;
}
static struct cifs_readdata *
@@ -2892,7 +2942,7 @@ cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
down_read(&cinode->lock_sem);
if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
tcon->ses->server->vals->shared_lock_type,
- NULL, true))
+ NULL, CIFS_READ_OP))
rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
up_read(&cinode->lock_sem);
return rc;
@@ -3536,7 +3586,7 @@ void cifs_oplock_break(struct work_struct *work)
if (cinode->clientCanCacheRead == 0) {
rc = filemap_fdatawait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc);
- invalidate_remote_inode(inode);
+ cifs_invalidate_mapping(inode);
}
cFYI(1, "Oplock flush inode %p rc %d", inode, rc);
}
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index afdff79651f1..ed6208ff85a7 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1791,11 +1791,12 @@ int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
stat->ino = CIFS_I(inode)->uniqueid;
/*
- * If on a multiuser mount without unix extensions, and the admin hasn't
- * overridden them, set the ownership to the fsuid/fsgid of the current
- * process.
+ * If on a multiuser mount without unix extensions or cifsacl being
+ * enabled, and the admin hasn't overridden them, set the ownership
+ * to the fsuid/fsgid of the current process.
*/
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) &&
+ !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
!tcon->unix_ext) {
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID))
stat->uid = current_fsuid();
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index d5ce9e26696c..a82bc51fdc82 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -204,7 +204,7 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len)
return rc;
}
-int
+void
cifs_set_port(struct sockaddr *addr, const unsigned short int port)
{
switch (addr->sa_family) {
@@ -214,19 +214,7 @@ cifs_set_port(struct sockaddr *addr, const unsigned short int port)
case AF_INET6:
((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
break;
- default:
- return 0;
}
- return 1;
-}
-
-int
-cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len,
- const unsigned short int port)
-{
- if (!cifs_convert_address(dst, src, len))
- return 0;
- return cifs_set_port(dst, port);
}
/*****************************************************************************
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 1c576e871366..6002fdc920ae 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -66,18 +66,20 @@ static inline void dump_cifs_file_struct(struct file *file, char *label)
#endif /* DEBUG2 */
/*
+ * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
+ *
* Find the dentry that matches "name". If there isn't one, create one. If it's
* a negative dentry or the uniqueid changed, then drop it and recreate it.
*/
-static struct dentry *
-cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
+static void
+cifs_prime_dcache(struct dentry *parent, struct qstr *name,
struct cifs_fattr *fattr)
{
struct dentry *dentry, *alias;
struct inode *inode;
struct super_block *sb = parent->d_inode->i_sb;
- cFYI(1, "For %s", name->name);
+ cFYI(1, "%s: for %s", __func__, name->name);
if (parent->d_op && parent->d_op->d_hash)
parent->d_op->d_hash(parent, parent->d_inode, name);
@@ -87,37 +89,32 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
dentry = d_lookup(parent, name);
if (dentry) {
int err;
+
inode = dentry->d_inode;
/* update inode in place if i_ino didn't change */
if (inode && CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
cifs_fattr_to_inode(inode, fattr);
- return dentry;
+ goto out;
}
err = d_invalidate(dentry);
dput(dentry);
if (err)
- return NULL;
+ return;
}
dentry = d_alloc(parent, name);
- if (dentry == NULL)
- return NULL;
+ if (!dentry)
+ return;
inode = cifs_iget(sb, fattr);
- if (!inode) {
- dput(dentry);
- return NULL;
- }
+ if (!inode)
+ goto out;
alias = d_materialise_unique(dentry, inode);
- if (alias != NULL) {
- dput(dentry);
- if (IS_ERR(alias))
- return NULL;
- dentry = alias;
- }
-
- return dentry;
+ if (alias && !IS_ERR(alias))
+ dput(alias);
+out:
+ dput(dentry);
}
static void
@@ -137,6 +134,16 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~S_IWUGO;
+ /*
+ * We of course don't get ACL info in FIND_FIRST/NEXT results, so
+ * mark it for revalidation so that "ls -l" will look right. It might
+ * be super-slow, but if we don't do this then the ownership of files
+ * may look wrong since the inodes may not have timed out by the time
+ * "ls" does a stat() call on them.
+ */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+ fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
fattr->cf_cifsattrs & ATTR_SYSTEM) {
if (fattr->cf_eof == 0) {
@@ -652,7 +659,6 @@ static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir,
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_dirent de = { NULL, };
struct cifs_fattr fattr;
- struct dentry *dentry;
struct qstr name;
int rc = 0;
ino_t ino;
@@ -723,13 +729,11 @@ static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir,
*/
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
- ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
- dentry = cifs_readdir_lookup(file->f_dentry, &name, &fattr);
+ cifs_prime_dcache(file->f_dentry, &name, &fattr);
+ ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
rc = filldir(dirent, name.name, name.len, file->f_pos, ino,
fattr.cf_dtype);
-
- dput(dentry);
return rc;
}
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index 34cea2798333..a5d234c8d5d9 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -575,37 +575,6 @@ cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data);
}
-static char *
-cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
- struct cifs_tcon *tcon)
-{
- int pplen = vol->prepath ? strlen(vol->prepath) : 0;
- int dfsplen;
- char *full_path = NULL;
-
- /* if no prefix path, simply set path to the root of share to "" */
- if (pplen == 0) {
- full_path = kzalloc(1, GFP_KERNEL);
- return full_path;
- }
-
- if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
- dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
- else
- dfsplen = 0;
-
- full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL);
- if (full_path == NULL)
- return full_path;
-
- if (dfsplen)
- strncpy(full_path, tcon->treeName, dfsplen);
- strncpy(full_path + dfsplen, vol->prepath, pplen);
- convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
- full_path[dfsplen + pplen] = 0; /* add trailing null */
- return full_path;
-}
-
static void
cifs_clear_stats(struct cifs_tcon *tcon)
{
@@ -943,7 +912,6 @@ struct smb_version_operations smb1_operations = {
.set_path_size = CIFSSMBSetEOF,
.set_file_size = CIFSSMBSetFileSize,
.set_file_info = smb_set_file_info,
- .build_path_to_root = cifs_build_path_to_root,
.echo = CIFSSMBEcho,
.mkdir = CIFSSMBMkDir,
.mkdir_setinfo = cifs_mkdir_setinfo,
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index a93eec30a50d..71e6aed4b382 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -260,13 +260,6 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
struct cifs_fid_locks *fdlocks;
xid = get_xid();
- /* we are going to update can_cache_brlcks here - need a write access */
- down_write(&cinode->lock_sem);
- if (!cinode->can_cache_brlcks) {
- up_write(&cinode->lock_sem);
- free_xid(xid);
- return rc;
- }
/*
* Accessing maxBuf is racy with cifs_reconnect - need to store value
@@ -274,7 +267,6 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
*/
max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
if (!max_buf) {
- up_write(&cinode->lock_sem);
free_xid(xid);
return -EINVAL;
}
@@ -282,7 +274,6 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
max_num = max_buf / sizeof(struct smb2_lock_element);
buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
if (!buf) {
- up_write(&cinode->lock_sem);
free_xid(xid);
return -ENOMEM;
}
@@ -293,10 +284,7 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
rc = stored_rc;
}
- cinode->can_cache_brlcks = false;
kfree(buf);
-
- up_write(&cinode->lock_sem);
free_xid(xid);
return rc;
}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 4d9dbe0b7385..d79de7bc4435 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -262,23 +262,6 @@ smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
-static char *
-smb2_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
- struct cifs_tcon *tcon)
-{
- int pplen = vol->prepath ? strlen(vol->prepath) : 0;
- char *full_path = NULL;
-
- /* if no prefix path, simply set path to the root of share to "" */
- if (pplen == 0) {
- full_path = kzalloc(2, GFP_KERNEL);
- return full_path;
- }
-
- cERROR(1, "prefixpath is not supported for SMB2 now");
- return NULL;
-}
-
static bool
smb2_can_echo(struct TCP_Server_Info *server)
{
@@ -613,7 +596,6 @@ struct smb_version_operations smb21_operations = {
.set_path_size = smb2_set_path_size,
.set_file_size = smb2_set_file_size,
.set_file_info = smb2_set_file_info,
- .build_path_to_root = smb2_build_path_to_root,
.mkdir = smb2_mkdir,
.mkdir_setinfo = smb2_mkdir_setinfo,
.rmdir = smb2_rmdir,
@@ -641,6 +623,91 @@ struct smb_version_operations smb21_operations = {
.get_lease_key = smb2_get_lease_key,
.set_lease_key = smb2_set_lease_key,
.new_lease_key = smb2_new_lease_key,
+ .calc_signature = smb2_calc_signature,
+};
+
+
+struct smb_version_operations smb30_operations = {
+ .compare_fids = smb2_compare_fids,
+ .setup_request = smb2_setup_request,
+ .setup_async_request = smb2_setup_async_request,
+ .check_receive = smb2_check_receive,
+ .add_credits = smb2_add_credits,
+ .set_credits = smb2_set_credits,
+ .get_credits_field = smb2_get_credits_field,
+ .get_credits = smb2_get_credits,
+ .get_next_mid = smb2_get_next_mid,
+ .read_data_offset = smb2_read_data_offset,
+ .read_data_length = smb2_read_data_length,
+ .map_error = map_smb2_to_linux_error,
+ .find_mid = smb2_find_mid,
+ .check_message = smb2_check_message,
+ .dump_detail = smb2_dump_detail,
+ .clear_stats = smb2_clear_stats,
+ .print_stats = smb2_print_stats,
+ .is_oplock_break = smb2_is_valid_oplock_break,
+ .need_neg = smb2_need_neg,
+ .negotiate = smb2_negotiate,
+ .negotiate_wsize = smb2_negotiate_wsize,
+ .negotiate_rsize = smb2_negotiate_rsize,
+ .sess_setup = SMB2_sess_setup,
+ .logoff = SMB2_logoff,
+ .tree_connect = SMB2_tcon,
+ .tree_disconnect = SMB2_tdis,
+ .is_path_accessible = smb2_is_path_accessible,
+ .can_echo = smb2_can_echo,
+ .echo = SMB2_echo,
+ .query_path_info = smb2_query_path_info,
+ .get_srv_inum = smb2_get_srv_inum,
+ .query_file_info = smb2_query_file_info,
+ .set_path_size = smb2_set_path_size,
+ .set_file_size = smb2_set_file_size,
+ .set_file_info = smb2_set_file_info,
+ .mkdir = smb2_mkdir,
+ .mkdir_setinfo = smb2_mkdir_setinfo,
+ .rmdir = smb2_rmdir,
+ .unlink = smb2_unlink,
+ .rename = smb2_rename_path,
+ .create_hardlink = smb2_create_hardlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+ .flush = smb2_flush_file,
+ .async_readv = smb2_async_readv,
+ .async_writev = smb2_async_writev,
+ .sync_read = smb2_sync_read,
+ .sync_write = smb2_sync_write,
+ .query_dir_first = smb2_query_dir_first,
+ .query_dir_next = smb2_query_dir_next,
+ .close_dir = smb2_close_dir,
+ .calc_smb_size = smb2_calc_size,
+ .is_status_pending = smb2_is_status_pending,
+ .oplock_response = smb2_oplock_response,
+ .queryfs = smb2_queryfs,
+ .mand_lock = smb2_mand_lock,
+ .mand_unlock_range = smb2_unlock_range,
+ .push_mand_locks = smb2_push_mandatory_locks,
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
+ .calc_signature = smb3_calc_signature,
+};
+
+struct smb_version_values smb20_values = {
+ .version_string = SMB20_VERSION_STRING,
+ .protocol_id = SMB20_PROT_ID,
+ .req_capabilities = 0, /* MBZ */
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_hdr),
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
};
struct smb_version_values smb21_values = {
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index cf33622cdac8..41d9d0725f0f 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -425,7 +425,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
}
cFYI(1, "sec_flags 0x%x", sec_flags);
- if (sec_flags & CIFSSEC_MUST_SIGN) {
+ if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
cFYI(1, "Signing required");
if (!(server->sec_mode & (SMB2_NEGOTIATE_SIGNING_REQUIRED |
SMB2_NEGOTIATE_SIGNING_ENABLED))) {
@@ -612,7 +612,8 @@ ssetup_ntlmssp_authenticate:
/* BB add code to build os and lm fields */
- rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, CIFS_LOG_ERROR);
+ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype,
+ CIFS_LOG_ERROR | CIFS_NEG_OP);
kfree(security_blob);
rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 7d25f8b14f93..2aa3535e38ce 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -47,6 +47,10 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
struct smb_rqst *rqst);
extern struct mid_q_entry *smb2_setup_async_request(
struct TCP_Server_Info *server, struct smb_rqst *rqst);
+extern int smb2_calc_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server);
+extern int smb3_calc_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server);
extern void smb2_echo_request(struct work_struct *work);
extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
extern __u8 smb2_map_lease_to_oplock(__le32 lease_state);
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 2a5fdf26f79f..8dd73e61d762 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -39,7 +39,7 @@
#include "smb2status.h"
#include "smb2glob.h"
-static int
+int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
int i, rc;
@@ -116,6 +116,13 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc;
}
+int
+smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ cFYI(1, "smb3 signatures not supported yet");
+ return -EOPNOTSUPP;
+}
+
/* must be called with server->srv_mutex held */
static int
smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
@@ -132,7 +139,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc;
}
- rc = smb2_calc_signature(rqst, server);
+ rc = server->ops->calc_signature(rqst, server);
return rc;
}
@@ -168,7 +175,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE);
mutex_lock(&server->srv_mutex);
- rc = smb2_calc_signature(rqst, server);
+ rc = server->ops->calc_signature(rqst, server);
mutex_unlock(&server->srv_mutex);
if (rc)