summaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/README14
-rw-r--r--fs/cifs/cifs_debug.c11
-rw-r--r--fs/cifs/cifs_dfs_ref.c5
-rw-r--r--fs/cifs/cifs_fs_sb.h4
-rw-r--r--fs/cifs/cifsacl.c373
-rw-r--r--fs/cifs/cifsencrypt.c247
-rw-r--r--fs/cifs/cifsfs.c50
-rw-r--r--fs/cifs/cifsfs.h10
-rw-r--r--fs/cifs/cifsglob.h115
-rw-r--r--fs/cifs/cifspdu.h48
-rw-r--r--fs/cifs/cifsproto.h56
-rw-r--r--fs/cifs/cifssmb.c461
-rw-r--r--fs/cifs/connect.c972
-rw-r--r--fs/cifs/dir.c33
-rw-r--r--fs/cifs/dns_resolve.c4
-rw-r--r--fs/cifs/export.c4
-rw-r--r--fs/cifs/file.c1159
-rw-r--r--fs/cifs/inode.c74
-rw-r--r--fs/cifs/link.c27
-rw-r--r--fs/cifs/misc.c77
-rw-r--r--fs/cifs/readdir.c427
-rw-r--r--fs/cifs/sess.c6
-rw-r--r--fs/cifs/smbencrypt.c192
-rw-r--r--fs/cifs/transport.c72
-rw-r--r--fs/cifs/xattr.c42
25 files changed, 2815 insertions, 1668 deletions
diff --git a/fs/cifs/README b/fs/cifs/README
index c5c2c5e5f0f..895da1dc155 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -745,4 +745,18 @@ installed and something like the following lines should be added to the
create cifs.spnego * * /usr/local/sbin/cifs.upcall %k
create dns_resolver * * /usr/local/sbin/cifs.upcall %k
+CIFS kernel module parameters
+=============================
+These module parameters can be specified or modified either during the time of
+module loading or during the runtime by using the interface
+ /proc/module/cifs/parameters/<param>
+
+i.e. echo "value" > /sys/module/cifs/parameters/<param>
+
+1. echo_retries - The number of echo attempts before giving up and
+ reconnecting to the server. The default is 5. The value 0
+ means never reconnect.
+
+2. enable_oplocks - Enable or disable oplocks. Oplocks are enabled by default.
+ [Y/y/1]. To disable use any of [N/n/0].
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 2fe3cf13b2e..84e8c072470 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -176,7 +176,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
#ifdef CONFIG_CIFS_STATS2
seq_printf(m, " In Send: %d In MaxReq Wait: %d",
- atomic_read(&server->inSend),
+ atomic_read(&server->in_send),
atomic_read(&server->num_waiters));
#endif
@@ -511,7 +511,7 @@ static const struct file_operations cifsFYI_proc_fops = {
static int cifs_oplock_proc_show(struct seq_file *m, void *v)
{
- seq_printf(m, "%d\n", oplockEnabled);
+ seq_printf(m, "%d\n", enable_oplocks);
return 0;
}
@@ -526,13 +526,16 @@ static ssize_t cifs_oplock_proc_write(struct file *file,
char c;
int rc;
+ printk(KERN_WARNING "CIFS: The /proc/fs/cifs/OplockEnabled interface "
+ "will be removed in kernel version 3.4. Please migrate to "
+ "using the 'enable_oplocks' module parameter in cifs.ko.\n");
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '0' || c == 'n' || c == 'N')
- oplockEnabled = 0;
+ enable_oplocks = false;
else if (c == '1' || c == 'y' || c == 'Y')
- oplockEnabled = 1;
+ enable_oplocks = true;
return count;
}
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 8d8f28c94c0..6873bb634a9 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -141,10 +141,11 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
if (rc < 0) {
- cERROR(1, "%s: Failed to resolve server part of %s to IP: %d",
- __func__, *devname, rc);
+ cFYI(1, "%s: Failed to resolve server part of %s to IP: %d",
+ __func__, *devname, rc);
goto compose_mount_options_err;
}
+
/* md_len = strlen(...) + 12 for 'sep+prefixpath='
* assuming that we have 'unc=' and 'ip=' in
* the original sb_mountdata
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 7260e11e21f..500d6585927 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -43,6 +43,8 @@
#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */
#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */
#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */
+#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
+#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
struct cifs_sb_info {
struct rb_root tlink_tree;
@@ -55,6 +57,8 @@ struct cifs_sb_info {
atomic_t active;
uid_t mnt_uid;
gid_t mnt_gid;
+ uid_t mnt_backupuid;
+ gid_t mnt_backupgid;
mode_t mnt_file_mode;
mode_t mnt_dir_mode;
unsigned int mnt_cifs_flags;
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 21de1d6d584..72ddf23ef6f 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -91,9 +91,76 @@ cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
spin_unlock(&sidgidlock);
+ root = &siduidtree;
+ spin_lock(&uidsidlock);
+ shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
+ spin_unlock(&uidsidlock);
+
+ root = &sidgidtree;
+ spin_lock(&gidsidlock);
+ shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
+ spin_unlock(&gidsidlock);
+
return nr_rem;
}
+static void
+sid_rb_insert(struct rb_root *root, unsigned long cid,
+ struct cifs_sid_id **psidid, char *typestr)
+{
+ char *strptr;
+ struct rb_node *node = root->rb_node;
+ struct rb_node *parent = NULL;
+ struct rb_node **linkto = &(root->rb_node);
+ struct cifs_sid_id *lsidid;
+
+ while (node) {
+ lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
+ parent = node;
+ if (cid > lsidid->id) {
+ linkto = &(node->rb_left);
+ node = node->rb_left;
+ }
+ if (cid < lsidid->id) {
+ linkto = &(node->rb_right);
+ node = node->rb_right;
+ }
+ }
+
+ (*psidid)->id = cid;
+ (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
+ (*psidid)->refcount = 0;
+
+ sprintf((*psidid)->sidstr, "%s", typestr);
+ strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
+ sprintf(strptr, "%ld", cid);
+
+ clear_bit(SID_ID_PENDING, &(*psidid)->state);
+ clear_bit(SID_ID_MAPPED, &(*psidid)->state);
+
+ rb_link_node(&(*psidid)->rbnode, parent, linkto);
+ rb_insert_color(&(*psidid)->rbnode, root);
+}
+
+static struct cifs_sid_id *
+sid_rb_search(struct rb_root *root, unsigned long cid)
+{
+ struct rb_node *node = root->rb_node;
+ struct cifs_sid_id *lsidid;
+
+ while (node) {
+ lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
+ if (cid > lsidid->id)
+ node = node->rb_left;
+ else if (cid < lsidid->id)
+ node = node->rb_right;
+ else /* node found */
+ return lsidid;
+ }
+
+ return NULL;
+}
+
static struct shrinker cifs_shrinker = {
.shrink = cifs_idmap_shrinker,
.seeks = DEFAULT_SEEKS,
@@ -110,6 +177,7 @@ cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen)
memcpy(payload, data, datalen);
key->payload.data = payload;
+ key->datalen = datalen;
return 0;
}
@@ -224,6 +292,120 @@ sidid_pending_wait(void *unused)
}
static int
+id_to_sid(unsigned long cid, uint sidtype, struct cifs_sid *ssid)
+{
+ int rc = 0;
+ struct key *sidkey;
+ const struct cred *saved_cred;
+ struct cifs_sid *lsid;
+ struct cifs_sid_id *psidid, *npsidid;
+ struct rb_root *cidtree;
+ spinlock_t *cidlock;
+
+ if (sidtype == SIDOWNER) {
+ cidlock = &siduidlock;
+ cidtree = &uidtree;
+ } else if (sidtype == SIDGROUP) {
+ cidlock = &sidgidlock;
+ cidtree = &gidtree;
+ } else
+ return -EINVAL;
+
+ spin_lock(cidlock);
+ psidid = sid_rb_search(cidtree, cid);
+
+ if (!psidid) { /* node does not exist, allocate one & attempt adding */
+ spin_unlock(cidlock);
+ npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
+ if (!npsidid)
+ return -ENOMEM;
+
+ npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
+ if (!npsidid->sidstr) {
+ kfree(npsidid);
+ return -ENOMEM;
+ }
+
+ spin_lock(cidlock);
+ psidid = sid_rb_search(cidtree, cid);
+ if (psidid) { /* node happened to get inserted meanwhile */
+ ++psidid->refcount;
+ spin_unlock(cidlock);
+ kfree(npsidid->sidstr);
+ kfree(npsidid);
+ } else {
+ psidid = npsidid;
+ sid_rb_insert(cidtree, cid, &psidid,
+ sidtype == SIDOWNER ? "oi:" : "gi:");
+ ++psidid->refcount;
+ spin_unlock(cidlock);
+ }
+ } else {
+ ++psidid->refcount;
+ spin_unlock(cidlock);
+ }
+
+ /*
+ * If we are here, it is safe to access psidid and its fields
+ * since a reference was taken earlier while holding the spinlock.
+ * A reference on the node is put without holding the spinlock
+ * and it is OK to do so in this case, shrinker will not erase
+ * this node until all references are put and we do not access
+ * any fields of the node after a reference is put .
+ */
+ if (test_bit(SID_ID_MAPPED, &psidid->state)) {
+ memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
+ psidid->time = jiffies; /* update ts for accessing */
+ goto id_sid_out;
+ }
+
+ if (time_after(psidid->time + SID_MAP_RETRY, jiffies)) {
+ rc = -EINVAL;
+ goto id_sid_out;
+ }
+
+ if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
+ saved_cred = override_creds(root_cred);
+ sidkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
+ if (IS_ERR(sidkey)) {
+ rc = -EINVAL;
+ cFYI(1, "%s: Can't map and id to a SID", __func__);
+ } else {
+ lsid = (struct cifs_sid *)sidkey->payload.data;
+ memcpy(&psidid->sid, lsid,
+ sidkey->datalen < sizeof(struct cifs_sid) ?
+ sidkey->datalen : sizeof(struct cifs_sid));
+ memcpy(ssid, &psidid->sid,
+ sidkey->datalen < sizeof(struct cifs_sid) ?
+ sidkey->datalen : sizeof(struct cifs_sid));
+ set_bit(SID_ID_MAPPED, &psidid->state);
+ key_put(sidkey);
+ kfree(psidid->sidstr);
+ }
+ psidid->time = jiffies; /* update ts for accessing */
+ revert_creds(saved_cred);
+ clear_bit(SID_ID_PENDING, &psidid->state);
+ wake_up_bit(&psidid->state, SID_ID_PENDING);
+ } else {
+ rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
+ sidid_pending_wait, TASK_INTERRUPTIBLE);
+ if (rc) {
+ cFYI(1, "%s: sidid_pending_wait interrupted %d",
+ __func__, rc);
+ --psidid->refcount;
+ return rc;
+ }
+ if (test_bit(SID_ID_MAPPED, &psidid->state))
+ memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
+ else
+ rc = -EINVAL;
+ }
+id_sid_out:
+ --psidid->refcount;
+ return rc;
+}
+
+static int
sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
struct cifs_fattr *fattr, uint sidtype)
{
@@ -383,6 +565,10 @@ init_cifs_idmap(void)
spin_lock_init(&sidgidlock);
gidtree = RB_ROOT;
+ spin_lock_init(&uidsidlock);
+ siduidtree = RB_ROOT;
+ spin_lock_init(&gidsidlock);
+ sidgidtree = RB_ROOT;
register_shrinker(&cifs_shrinker);
cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring));
@@ -422,6 +608,18 @@ cifs_destroy_idmaptrees(void)
while ((node = rb_first(root)))
rb_erase(node, root);
spin_unlock(&sidgidlock);
+
+ root = &siduidtree;
+ spin_lock(&uidsidlock);
+ while ((node = rb_first(root)))
+ rb_erase(node, root);
+ spin_unlock(&uidsidlock);
+
+ root = &sidgidtree;
+ spin_lock(&gidsidlock);
+ while ((node = rb_first(root)))
+ rb_erase(node, root);
+ spin_unlock(&gidsidlock);
}
/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
@@ -706,7 +904,7 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
acl_size = sizeof(struct cifs_acl);
num_aces = le32_to_cpu(pdacl->num_aces);
- if (num_aces > 0) {
+ if (num_aces > 0) {
umode_t user_mask = S_IRWXU;
umode_t group_mask = S_IRWXG;
umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO;
@@ -868,52 +1066,82 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
else
cFYI(1, "no ACL"); /* BB grant all or default perms? */
-/* cifscred->uid = owner_sid_ptr->rid;
- cifscred->gid = group_sid_ptr->rid;
- memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr,
- sizeof(struct cifs_sid));
- memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
- sizeof(struct cifs_sid)); */
-
return rc;
}
-
/* Convert permission bits from mode to equivalent CIFS ACL */
static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
- struct inode *inode, __u64 nmode)
+ __u32 secdesclen, __u64 nmode, uid_t uid, gid_t gid, int *aclflag)
{
int rc = 0;
__u32 dacloffset;
__u32 ndacloffset;
__u32 sidsoffset;
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+ struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
- if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL))
- return -EIO;
-
- owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ if (nmode != NO_CHANGE_64) { /* chmod */
+ owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
- group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
-
- dacloffset = le32_to_cpu(pntsd->dacloffset);
- dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
-
- ndacloffset = sizeof(struct cifs_ntsd);
- ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
- ndacl_ptr->revision = dacl_ptr->revision;
- ndacl_ptr->size = 0;
- ndacl_ptr->num_aces = 0;
-
- rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode);
-
- sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
-
- /* copy security descriptor control portion and owner and group sid */
- copy_sec_desc(pntsd, pnntsd, sidsoffset);
+ dacloffset = le32_to_cpu(pntsd->dacloffset);
+ dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
+ ndacloffset = sizeof(struct cifs_ntsd);
+ ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
+ ndacl_ptr->revision = dacl_ptr->revision;
+ ndacl_ptr->size = 0;
+ ndacl_ptr->num_aces = 0;
+
+ rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr,
+ nmode);
+ sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
+ /* copy sec desc control portion & owner and group sids */
+ copy_sec_desc(pntsd, pnntsd, sidsoffset);
+ *aclflag = CIFS_ACL_DACL;
+ } else {
+ memcpy(pnntsd, pntsd, secdesclen);
+ if (uid != NO_CHANGE_32) { /* chown */
+ owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
+ le32_to_cpu(pnntsd->osidoffset));
+ nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid),
+ GFP_KERNEL);
+ if (!nowner_sid_ptr)
+ return -ENOMEM;
+ rc = id_to_sid(uid, SIDOWNER, nowner_sid_ptr);
+ if (rc) {
+ cFYI(1, "%s: Mapping error %d for owner id %d",
+ __func__, rc, uid);
+ kfree(nowner_sid_ptr);
+ return rc;
+ }
+ memcpy(owner_sid_ptr, nowner_sid_ptr,
+ sizeof(struct cifs_sid));
+ kfree(nowner_sid_ptr);
+ *aclflag = CIFS_ACL_OWNER;
+ }
+ if (gid != NO_CHANGE_32) { /* chgrp */
+ group_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
+ le32_to_cpu(pnntsd->gsidoffset));
+ ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid),
+ GFP_KERNEL);
+ if (!ngroup_sid_ptr)
+ return -ENOMEM;
+ rc = id_to_sid(gid, SIDGROUP, ngroup_sid_ptr);
+ if (rc) {
+ cFYI(1, "%s: Mapping error %d for group id %d",
+ __func__, rc, gid);
+ kfree(ngroup_sid_ptr);
+ return rc;
+ }
+ memcpy(group_sid_ptr, ngroup_sid_ptr,
+ sizeof(struct cifs_sid));
+ kfree(ngroup_sid_ptr);
+ *aclflag = CIFS_ACL_GROUP;
+ }
+ }
return rc;
}
@@ -945,7 +1173,7 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
{
struct cifs_ntsd *pntsd = NULL;
int oplock = 0;
- int xid, rc;
+ int xid, rc, create_options = 0;
__u16 fid;
struct cifs_tcon *tcon;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
@@ -956,9 +1184,12 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
tcon = tlink_tcon(tlink);
xid = GetXid();
- rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, 0,
- &fid, &oplock, NULL, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL,
+ create_options, &fid, &oplock, NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (!rc) {
rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
CIFSSMBClose(xid, tcon, fid);
@@ -991,31 +1222,15 @@ struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
return pntsd;
}
-static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid,
- struct cifs_ntsd *pnntsd, u32 acllen)
-{
- int xid, rc;
- struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
-
- if (IS_ERR(tlink))
- return PTR_ERR(tlink);
-
- xid = GetXid();
- rc = CIFSSMBSetCIFSACL(xid, tlink_tcon(tlink), fid, pnntsd, acllen);
- FreeXid(xid);
- cifs_put_tlink(tlink);
-
- cFYI(DBG2, "SetCIFSACL rc = %d", rc);
- return rc;
-}
-
-static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
- struct cifs_ntsd *pnntsd, u32 acllen)
+ /* Set an ACL on the server */
+int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
+ struct inode *inode, const char *path, int aclflag)
{
int oplock = 0;
- int xid, rc;
+ int xid, rc, access_flags, create_options = 0;
__u16 fid;
struct cifs_tcon *tcon;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
@@ -1024,15 +1239,23 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
tcon = tlink_tcon(tlink);
xid = GetXid();
- rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, 0,
- &fid, &oplock, NULL, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP)
+ access_flags = WRITE_OWNER;
+ else
+ access_flags = WRITE_DAC;
+
+ rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, access_flags,
+ create_options, &fid, &oplock, NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) {
cERROR(1, "Unable to open file to set ACL");
goto out;
}
- rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen);
+ rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen, aclflag);
cFYI(DBG2, "SetCIFSACL rc = %d", rc);
CIFSSMBClose(xid, tcon, fid);
@@ -1042,25 +1265,6 @@ out:
return rc;
}
-/* Set an ACL on the server */
-int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
- struct inode *inode, const char *path)
-{
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- struct cifsFileInfo *open_file;
- int rc;
-
- cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode);
-
- open_file = find_readable_file(CIFS_I(inode), true);
- if (!open_file)
- return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
-
- rc = set_cifs_acl_by_fid(cifs_sb, open_file->netfid, pnntsd, acllen);
- cifsFileInfo_put(open_file);
- return rc;
-}
-
/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
int
cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
@@ -1092,9 +1296,12 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
}
/* Convert mode bits to an ACL so we can update the ACL on the server */
-int mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode)
+int
+id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
+ uid_t uid, gid_t gid)
{
int rc = 0;
+ int aclflag = CIFS_ACL_DACL; /* default flag to set */
__u32 secdesclen = 0;
struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
@@ -1124,13 +1331,15 @@ int mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode)
return -ENOMEM;
}
- rc = build_sec_desc(pntsd, pnntsd, inode, nmode);
+ rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
+ &aclflag);
cFYI(DBG2, "build_sec_desc rc: %d", rc);
if (!rc) {
/* Set the security descriptor */
- rc = set_cifs_acl(pnntsd, secdesclen, inode, path);
+ rc = set_cifs_acl(pnntsd, secdesclen, inode,
+ path, aclflag);
cFYI(DBG2, "set_cifs_acl rc: %d", rc);
}
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 5a0ee7f2af0..5d9b9acc5fc 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -37,12 +37,13 @@
* the sequence number before this function is called. Also, this function
* should be called with the server->srv_mutex held.
*/
-static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
- struct TCP_Server_Info *server, char *signature)
+static int cifs_calc_signature(const struct kvec *iov, int n_vec,
+ struct TCP_Server_Info *server, char *signature)
{
+ int i;
int rc;
- if (cifs_pdu == NULL || signature == NULL || server == NULL)
+ if (iov == NULL || signature == NULL || server == NULL)
return -EINVAL;
if (!server->secmech.sdescmd5) {
@@ -52,73 +53,17 @@ static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
if (rc) {
- cERROR(1, "%s: Oould not init md5\n", __func__);
+ cERROR(1, "%s: Could not init md5\n", __func__);
return rc;
}
- crypto_shash_update(&server->secmech.sdescmd5->shash,
+ rc = crypto_shash_update(&server->secmech.sdescmd5->shash,
server->session_key.response, server->session_key.len);
-
- crypto_shash_update(&server->secmech.sdescmd5->shash,
- cifs_pdu->Protocol, be32_to_cpu(cifs_pdu->smb_buf_length));
-
- rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
-
- return 0;
-}
-
-/* must be called with server->srv_mutex held */
-int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
- __u32 *pexpected_response_sequence_number)
-{
- int rc = 0;
- char smb_signature[20];
-
- if ((cifs_pdu == NULL) || (server == NULL))
- return -EINVAL;
-
- if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
- return rc;
-
- cifs_pdu->Signature.Sequence.SequenceNumber =
- cpu_to_le32(server->sequence_number);
- cifs_pdu->Signature.Sequence.Reserved = 0;
-
- *pexpected_response_sequence_number = server->sequence_number++;
- server->sequence_number++;
-
- rc = cifs_calculate_signature(cifs_pdu, server, smb_signature);
- if (rc)
- memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
- else
- memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
-
- return rc;
-}
-
-static int cifs_calc_signature2(const struct kvec *iov, int n_vec,
- struct TCP_Server_Info *server, char *signature)
-{
- int i;
- int rc;
-
- if (iov == NULL || signature == NULL || server == NULL)
- return -EINVAL;
-
- if (!server->secmech.sdescmd5) {
- cERROR(1, "%s: Can't generate signature\n", __func__);
- return -1;
- }
-
- rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
if (rc) {
- cERROR(1, "%s: Oould not init md5\n", __func__);
+ cERROR(1, "%s: Could not update with response\n", __func__);
return rc;
}
- crypto_shash_update(&server->secmech.sdescmd5->shash,
- server->session_key.response, server->session_key.len);
-
for (i = 0; i < n_vec; i++) {
if (iov[i].iov_len == 0)
continue;
@@ -131,14 +76,24 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec,
if (i == 0) {
if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
break; /* nothing to sign or corrupt header */
+ rc =
crypto_shash_update(&server->secmech.sdescmd5->shash,
iov[i].iov_base + 4, iov[i].iov_len - 4);
- } else
+ } else {
+ rc =
crypto_shash_update(&server->secmech.sdescmd5->shash,
iov[i].iov_base, iov[i].iov_len);
+ }
+ if (rc) {
+ cERROR(1, "%s: Could not update with payload\n",
+ __func__);
+ return rc;
+ }
}
rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
+ if (rc)
+ cERROR(1, "%s: Could not generate md5 hash\n", __func__);
return rc;
}
@@ -149,14 +104,20 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
{
int rc = 0;
char smb_signature[20];
- struct smb_hdr *cifs_pdu = iov[0].iov_base;
+ struct smb_hdr *cifs_pdu = (struct smb_hdr *)iov[0].iov_base;
if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL;
- if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
+ if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
+ server->tcpStatus == CifsNeedNegotiate)
return rc;
+ if (!server->session_estab) {
+ memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
+ return rc;
+ }
+
cifs_pdu->Signature.Sequence.SequenceNumber =
cpu_to_le32(server->sequence_number);
cifs_pdu->Signature.Sequence.Reserved = 0;
@@ -164,7 +125,7 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
*pexpected_response_sequence_number = server->sequence_number++;
server->sequence_number++;
- rc = cifs_calc_signature2(iov, n_vec, server, smb_signature);
+ rc = cifs_calc_signature(iov, n_vec, server, smb_signature);
if (rc)
memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
else
@@ -173,13 +134,27 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
return rc;
}
-int cifs_verify_signature(struct smb_hdr *cifs_pdu,
+/* must be called with server->srv_mutex held */
+int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number)
+{
+ struct kvec iov;
+
+ iov.iov_base = cifs_pdu;
+ iov.iov_len = be32_to_cpu(cifs_pdu->smb_buf_length) + 4;
+
+ return cifs_sign_smb2(&iov, 1, server,
+ pexpected_response_sequence_number);
+}
+
+int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
struct TCP_Server_Info *server,
__u32 expected_sequence_number)
{
unsigned int rc;
char server_response_sig[8];
char what_we_think_sig_should_be[20];
+ struct smb_hdr *cifs_pdu = (struct smb_hdr *)iov[0].iov_base;
if (cifs_pdu == NULL || server == NULL)
return -EINVAL;
@@ -211,8 +186,8 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,
cifs_pdu->Signature.Sequence.Reserved = 0;
mutex_lock(&server->srv_mutex);
- rc = cifs_calculate_signature(cifs_pdu, server,
- what_we_think_sig_should_be);
+ rc = cifs_calc_signature(iov, nr_iov, server,
+ what_we_think_sig_should_be);
mutex_unlock(&server->srv_mutex);
if (rc)
@@ -229,7 +204,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,
}
/* first calculate 24 bytes ntlm response and then 16 byte session key */
-int setup_ntlm_response(struct cifs_ses *ses)
+int setup_ntlm_response(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
int rc = 0;
unsigned int temp_len = CIFS_SESS_KEY_SIZE + CIFS_AUTH_RESP_SIZE;
@@ -246,14 +221,14 @@ int setup_ntlm_response(struct cifs_ses *ses)
ses->auth_key.len = temp_len;
rc = SMBNTencrypt(ses->password, ses->server->cryptkey,
- ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+ ses->auth_key.response + CIFS_SESS_KEY_SIZE, nls_cp);
if (rc) {
cFYI(1, "%s Can't generate NTLM response, error: %d",
__func__, rc);
return rc;
}
- rc = E_md4hash(ses->password, temp_key);
+ rc = E_md4hash(ses->password, temp_key, nls_cp);
if (rc) {
cFYI(1, "%s Can't generate NT hash, error: %d", __func__, rc);
return rc;
@@ -315,9 +290,7 @@ static int
build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
unsigned int dlen;
- unsigned int wlen;
- unsigned int size = 6 * sizeof(struct ntlmssp2_name);
- __le64 curtime;
+ unsigned int size = 2 * sizeof(struct ntlmssp2_name);
char *defdmname = "WORKGROUP";
unsigned char *blobptr;
struct ntlmssp2_name *attrptr;
@@ -329,15 +302,14 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
}
dlen = strlen(ses->domainName);
- wlen = strlen(ses->server->hostname);
- /* The length of this blob is a size which is
- * six times the size of a structure which holds name/size +
- * two times the unicode length of a domain name +
- * two times the unicode length of a server name +
- * size of a timestamp (which is 8 bytes).
+ /*
+ * The length of this blob is two times the size of a
+ * structure (av pair) which holds name/size
+ * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) +
+ * unicode length of a netbios domain name
*/
- ses->auth_key.len = size + 2 * (2 * dlen) + 2 * (2 * wlen) + 8;
+ ses->auth_key.len = size + 2 * dlen;
ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL);
if (!ses->auth_key.response) {
ses->auth_key.len = 0;
@@ -348,44 +320,15 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
blobptr = ses->auth_key.response;
attrptr = (struct ntlmssp2_name *) blobptr;
+ /*
+ * As defined in MS-NTLM 3.3.2, just this av pair field
+ * is sufficient as part of the temp
+ */
attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME);
attrptr->length = cpu_to_le16(2 * dlen);
blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp);
- blobptr += 2 * dlen;
- attrptr = (struct ntlmssp2_name *) blobptr;
-
- attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_COMPUTER_NAME);
- attrptr->length = cpu_to_le16(2 * wlen);
- blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
- cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp);
-
- blobptr += 2 * wlen;
- attrptr = (struct ntlmssp2_name *) blobptr;
-
- attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_DOMAIN_NAME);
- attrptr->length = cpu_to_le16(2 * dlen);
- blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
- cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp);
-
- blobptr += 2 * dlen;
- attrptr = (struct ntlmssp2_name *) blobptr;
-
- attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_COMPUTER_NAME);
- attrptr->length = cpu_to_le16(2 * wlen);
- blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
- cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp);
-
- blobptr += 2 * wlen;
- attrptr = (struct ntlmssp2_name *) blobptr;
-
- attrptr->type = cpu_to_le16(NTLMSSP_AV_TIMESTAMP);
- attrptr->length = cpu_to_le16(sizeof(__le64));
- blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
- curtime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- memcpy(blobptr, &curtime, sizeof(__le64));
-
return 0;
}
@@ -461,10 +404,14 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
}
/* calculate md4 hash of password */
- E_md4hash(ses->password, nt_hash);
+ E_md4hash(ses->password, nt_hash, nls_cp);
- crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash,
+ rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash,
CIFS_NTHASH_SIZE);
+ if (rc) {
+ cERROR(1, "%s: Could not set NT Hash as a key", __func__);
+ return rc;
+ }
rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) {
@@ -478,13 +425,18 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
if (user == NULL) {
cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n");
rc = -ENOMEM;
- goto calc_exit_2;
+ return rc;
}
len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp);
UniStrupr(user);
- crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
(char *)user, 2 * len);
+ kfree(user);
+ if (rc) {
+ cERROR(1, "%s: Could not update with user\n", __func__);
+ return rc;
+ }
/* convert ses->domainName to unicode and uppercase */
if (ses->domainName) {
@@ -494,13 +446,19 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
if (domain == NULL) {
cERROR(1, "calc_ntlmv2_hash: domain mem alloc failure");
rc = -ENOMEM;
- goto calc_exit_1;
+ return rc;
}
len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len,
nls_cp);
+ rc =
crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
(char *)domain, 2 * len);
kfree(domain);
+ if (rc) {
+ cERROR(1, "%s: Could not update with domain\n",
+ __func__);
+ return rc;
+ }
} else if (ses->serverName) {
len = strlen(ses->serverName);
@@ -508,21 +466,26 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
if (server == NULL) {
cERROR(1, "calc_ntlmv2_hash: server mem alloc failure");
rc = -ENOMEM;
- goto calc_exit_1;
+ return rc;
}
len = cifs_strtoUCS((__le16 *)server, ses->serverName, len,
nls_cp);
+ rc =
crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
(char *)server, 2 * len);
kfree(server);
+ if (rc) {
+ cERROR(1, "%s: Could not update with server\n",
+ __func__);
+ return rc;
+ }
}
rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
ntlmv2_hash);
+ if (rc)
+ cERROR(1, "%s: Could not generate md5 hash\n", __func__);
-calc_exit_1:
- kfree(user);
-calc_exit_2:
return rc;
}
@@ -537,8 +500,12 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
return -1;
}
- crypto_shash_setkey(ses->server->secmech.hmacmd5,
+ rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+ if (rc) {
+ cERROR(1, "%s: Could not set NTLMV2 Hash as a key", __func__);
+ return rc;
+ }
rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) {
@@ -552,11 +519,17 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
else
memcpy(ses->auth_key.response + offset,
ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
- crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + offset, ses->auth_key.len - offset);
+ if (rc) {
+ cERROR(1, "%s: Could not update with response\n", __func__);
+ return rc;
+ }
rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+ if (rc)
+ cERROR(1, "%s: Could not generate md5 hash\n", __func__);
return rc;
}
@@ -626,8 +599,12 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
}
/* now calculate the session key for NTLMv2 */
- crypto_shash_setkey(ses->server->secmech.hmacmd5,
+ rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+ if (rc) {
+ cERROR(1, "%s: Could not set NTLMV2 Hash as a key", __func__);
+ goto setup_ntlmv2_rsp_ret;
+ }
rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) {
@@ -635,12 +612,18 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
goto setup_ntlmv2_rsp_ret;
}
- crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + CIFS_SESS_KEY_SIZE,
CIFS_HMAC_MD5_HASH_SIZE);
+ if (rc) {
+ cERROR(1, "%s: Could not update with response\n", __func__);
+ goto setup_ntlmv2_rsp_ret;
+ }
rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response);
+ if (rc)
+ cERROR(1, "%s: Could not generate md5 hash\n", __func__);
setup_ntlmv2_rsp_ret:
kfree(tiblob);
@@ -668,8 +651,12 @@ calc_seckey(struct cifs_ses *ses)
desc.tfm = tfm_arc4;
- crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response,
+ rc = crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response,
CIFS_SESS_KEY_SIZE);
+ if (rc) {
+ cERROR(1, "%s: Could not set response as a key", __func__);
+ return rc;
+ }
sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE);
sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
@@ -688,7 +675,7 @@ calc_seckey(struct cifs_ses *ses)
crypto_free_blkcipher(tfm_arc4);
- return 0;
+ return rc;
}
void
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 86551747096..8f1fe324162 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -53,7 +53,7 @@
int cifsFYI = 0;
int cifsERROR = 1;
int traceSMB = 0;
-unsigned int oplockEnabled = 1;
+bool enable_oplocks = true;
unsigned int linuxExtEnabled = 1;
unsigned int lookupCacheEnabled = 1;
unsigned int multiuser_mount = 0;
@@ -74,7 +74,7 @@ module_param(cifs_min_small, int, 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, 0);
+module_param(cifs_max_pending, int, 0444);
MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. "
"Default: 50 Range: 2 to 256");
unsigned short echo_retries = 5;
@@ -82,28 +82,14 @@ module_param(echo_retries, ushort, 0644);
MODULE_PARM_DESC(echo_retries, "Number of echo attempts before giving up and "
"reconnecting server. Default: 5. 0 means "
"never reconnect.");
+module_param(enable_oplocks, bool, 0644);
+MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks (bool). Default:"
+ "y/Y/1");
+
extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp;
-void
-cifs_sb_active(struct super_block *sb)
-{
- struct cifs_sb_info *server = CIFS_SB(sb);
-
- if (atomic_inc_return(&server->active) == 1)
- atomic_inc(&sb->s_active);
-}
-
-void
-cifs_sb_deactive(struct super_block *sb)
-{
- struct cifs_sb_info *server = CIFS_SB(sb);
-
- if (atomic_dec_and_test(&server->active))
- deactivate_super(sb);
-}
-
static int
cifs_read_super(struct super_block *sb)
{
@@ -150,12 +136,12 @@ cifs_read_super(struct super_block *sb)
else
sb->s_d_op = &cifs_dentry_ops;
-#ifdef CIFS_NFSD_EXPORT
+#ifdef CONFIG_CIFS_NFSD_EXPORT
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
cFYI(1, "export ops supported");
sb->s_export_op = &cifs_export_ops;
}
-#endif /* CIFS_NFSD_EXPORT */
+#endif /* CONFIG_CIFS_NFSD_EXPORT */
return 0;
@@ -450,6 +436,12 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
seq_printf(s, ",mfsymlinks");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE)
seq_printf(s, ",fsc");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)
+ seq_printf(s, ",nostrictsync");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
+ seq_printf(s, ",noperm");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
+ seq_printf(s, ",strictcache");
seq_printf(s, ",rsize=%d", cifs_sb->rsize);
seq_printf(s, ",wsize=%d", cifs_sb->wsize);
@@ -548,7 +540,6 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
char *full_path = NULL;
char *s, *p;
char sep;
- int xid;
full_path = cifs_build_path_to_root(vol, cifs_sb,
cifs_sb_master_tcon(cifs_sb));
@@ -557,7 +548,6 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
cFYI(1, "Get root dentry for %s", full_path);
- xid = GetXid();
sep = CIFS_DIR_SEP(cifs_sb);
dentry = dget(sb->s_root);
p = s = full_path;
@@ -566,6 +556,12 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
struct inode *dir = dentry->d_inode;
struct dentry *child;
+ if (!dir) {
+ dput(dentry);
+ dentry = ERR_PTR(-ENOENT);
+ break;
+ }
+
/* skip separators */
while (*s == sep)
s++;
@@ -582,7 +578,6 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
dput(dentry);
dentry = child;
} while (!IS_ERR(dentry));
- _FreeXid(xid);
kfree(full_path);
return dentry;
}
@@ -735,7 +730,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
if (rc < 0)
return (loff_t)rc;
}
- return generic_file_llseek_unlocked(file, offset, origin);
+ return generic_file_llseek(file, offset, origin);
}
static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
@@ -954,7 +949,8 @@ cifs_init_once(void *inode)
struct cifsInodeInfo *cifsi = inode;
inode_init_once(&cifsi->vfs_inode);
- INIT_LIST_HEAD(&cifsi->lockList);
+ INIT_LIST_HEAD(&cifsi->llist);
+ mutex_init(&cifsi->lock_mutex);
}
static int
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index fbd050c8d52..30ff56005d8 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -41,10 +41,6 @@ extern struct file_system_type cifs_fs_type;
extern const struct address_space_operations cifs_addr_ops;
extern const struct address_space_operations cifs_addr_ops_smallbuf;
-/* Functions related to super block operations */
-extern void cifs_sb_active(struct super_block *sb);
-extern void cifs_sb_deactive(struct super_block *sb);
-
/* Functions related to inodes */
extern const struct inode_operations cifs_dir_inode_ops;
extern struct inode *cifs_root_iget(struct super_block *);
@@ -125,9 +121,9 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
-#ifdef CIFS_NFSD_EXPORT
+#ifdef CONFIG_CIFS_NFSD_EXPORT
extern const struct export_operations cifs_export_ops;
-#endif /* CIFS_NFSD_EXPORT */
+#endif /* CONFIG_CIFS_NFSD_EXPORT */
-#define CIFS_VERSION "1.74"
+#define CIFS_VERSION "1.76"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 6255fa812c7..8238aa13e01 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -167,6 +167,8 @@ struct smb_vol {
uid_t cred_uid;
uid_t linux_uid;
gid_t linux_gid;
+ uid_t backupuid;
+ gid_t backupgid;
mode_t file_mode;
mode_t dir_mode;
unsigned secFlg;
@@ -179,6 +181,8 @@ struct smb_vol {
bool noperm:1;
bool no_psx_acl:1; /* set if posix acl support should be disabled */
bool cifs_acl:1;
+ bool backupuid_specified; /* mount option backupuid is specified */
+ bool backupgid_specified; /* mount option backupgid is specified */
bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
bool server_ino:1; /* use inode numbers from server ie UniqueId */
bool direct_io:1;
@@ -219,7 +223,8 @@ struct smb_vol {
CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \
CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \
CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \
- CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO)
+ CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \
+ CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID)
#define CIFS_MS_MASK (MS_RDONLY | MS_MANDLOCK | MS_NOEXEC | MS_NOSUID | \
MS_NODEV | MS_SYNCHRONOUS)
@@ -286,12 +291,18 @@ struct TCP_Server_Info {
bool sec_kerberosu2u; /* supports U2U Kerberos */
bool sec_kerberos; /* supports plain Kerberos */
bool sec_mskerberos; /* supports legacy MS Kerberos */
+ bool large_buf; /* is current buffer large? */
struct delayed_work echo; /* echo ping workqueue job */
+ struct kvec *iov; /* reusable kvec array for receives */
+ unsigned int nr_iov; /* number of kvecs in array */
+ char *smallbuf; /* pointer to current "small" buffer */
+ char *bigbuf; /* pointer to current "big" buffer */
+ unsigned int total_read; /* total amount of data read in this pass */
#ifdef CONFIG_CIFS_FSCACHE
struct fscache_cookie *fscache; /* client index cache cookie */
#endif
#ifdef CONFIG_CIFS_STATS2
- atomic_t inSend; /* requests trying to send */
+ atomic_t in_send; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#endif
};
@@ -485,9 +496,13 @@ extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
*/
struct cifsLockInfo {
struct list_head llist; /* pointer to next cifsLockInfo */
+ struct list_head blist; /* pointer to locks blocked on this */
+ wait_queue_head_t block_q;
__u64 offset;
__u64 length;
+ __u32 pid;
__u8 type;
+ __u16 netfid;
};
/*
@@ -501,7 +516,7 @@ struct cifs_search_info {
char *ntwrk_buf_start;
char *srch_entries_start;
char *last_entry;
- char *presume_name;
+ const char *presume_name;
unsigned int resume_name_len;
bool endOfSearch:1;
bool emptyDir:1;
@@ -520,8 +535,6 @@ struct cifsFileInfo {
struct dentry *dentry;
unsigned int f_flags;
struct tcon_link *tlink;
- struct mutex lock_mutex;
- struct list_head llist; /* list of byte range locks we have. */
bool invalidHandle:1; /* file closed via session abend */
bool oplock_break_cancelled:1;
int count; /* refcount protected by cifs_file_list_lock */
@@ -554,7 +567,9 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
*/
struct cifsInodeInfo {
- struct list_head lockList;
+ struct list_head llist; /* brlocks for this inode */
+ bool can_cache_brlcks;
+ struct mutex lock_mutex; /* protect two fields above */
/* BB add in lists for dirty pages i.e. write caching info for oplock */
struct list_head openFileList;
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
@@ -643,8 +658,24 @@ static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon,
struct mid_q_entry;
/*
- * This is the prototype for the mid callback function. When creating one,
- * take special care to avoid deadlocks. Things to bear in mind:
+ * This is the prototype for the mid receive function. This function is for
+ * receiving the rest of the SMB frame, starting with the WordCount (which is
+ * just after the MID in struct smb_hdr). Note:
+ *
+ * - This will be called by cifsd, with no locks held.
+ * - The mid will still be on the pending_mid_q.
+ * - mid->resp_buf will point to the current buffer.
+ *
+ * Returns zero on a successful receive, or an error. The receive state in
+ * the TCP_Server_Info will also be updated.
+ */
+typedef int (mid_receive_t)(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid);
+
+/*
+ * This is the prototype for the mid callback function. This is called once the
+ * mid has been received off of the socket. When creating one, take special
+ * care to avoid deadlocks. Things to bear in mind:
*
* - it will be called by cifsd, with no locks held
* - the mid will be removed from any lists
@@ -662,9 +693,10 @@ struct mid_q_entry {
unsigned long when_sent; /* time when smb send finished */
unsigned long when_received; /* when demux complete (taken off wire) */
#endif
+ mid_receive_t *receive; /* call receive callback */
mid_callback_t *callback; /* call completion callback */
void *callback_data; /* general purpose pointer for callback */
- struct smb_hdr *resp_buf; /* response buffer */
+ struct smb_hdr *resp_buf; /* pointer to received SMB header */
int midState; /* wish this were enum but can not pass to wait_event */
__u8 command; /* smb command code */
bool largeBuf:1; /* if valid response, is pointer to large buf */
@@ -672,12 +704,54 @@ struct mid_q_entry {
bool multiEnd:1; /* both received */
};
-struct oplock_q_entry {
- struct list_head qhead;
- struct inode *pinode;
- struct cifs_tcon *tcon;
- __u16 netfid;
-};
+/* Make code in transport.c a little cleaner by moving
+ update of optional stats into function below */
+#ifdef CONFIG_CIFS_STATS2
+
+static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
+{
+ atomic_inc(&server->in_send);
+}
+
+static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
+{
+ atomic_dec(&server->in_send);
+}
+
+static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
+{
+ atomic_inc(&server->num_waiters);
+}
+
+static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
+{
+ atomic_dec(&server->num_waiters);
+}
+
+static inline void cifs_save_when_sent(struct mid_q_entry *mid)
+{
+ mid->when_sent = jiffies;
+}
+#else
+static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
+{
+}
+static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_save_when_sent(struct mid_q_entry *mid)
+{
+}
+#endif
/* for pending dnotify requests */
struct dir_notify_req {
@@ -922,7 +996,8 @@ GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions
to be established on existing mount if we
have the uid/password or Kerberos credential
or equivalent for current user */
-GLOBAL_EXTERN unsigned int oplockEnabled;
+/* enable or disable oplocks */
+GLOBAL_EXTERN bool enable_oplocks;
GLOBAL_EXTERN unsigned int lookupCacheEnabled;
GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent
with more secure ntlmssp2 challenge/resp */
@@ -936,14 +1011,18 @@ GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/
/* reconnect after this many failed echo attempts */
GLOBAL_EXTERN unsigned short echo_retries;
+#ifdef CONFIG_CIFS_ACL
GLOBAL_EXTERN struct rb_root uidtree;
GLOBAL_EXTERN struct rb_root gidtree;
GLOBAL_EXTERN spinlock_t siduidlock;
GLOBAL_EXTERN spinlock_t sidgidlock;
+GLOBAL_EXTERN struct rb_root siduidtree;
+GLOBAL_EXTERN struct rb_root sidgidtree;
+GLOBAL_EXTERN spinlock_t uidsidlock;
+GLOBAL_EXTERN spinlock_t gidsidlock;
+#endif /* CONFIG_CIFS_ACL */
void cifs_oplock_break(struct work_struct *work);
-void cifs_oplock_break_get(struct cifsFileInfo *cfile);
-void cifs_oplock_break_put(struct cifsFileInfo *cfile);
extern const struct slow_work_ops cifs_oplock_break_ops;
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index de3aa285de0..3fb03e2c8e8 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -1089,9 +1089,7 @@ typedef struct smb_com_read_rsp {
__le16 DataLengthHigh;
__u64 Reserved2;
__u16 ByteCount;
- __u8 Pad; /* BB check for whether padded to DWORD
- boundary and optimum performance here */
- char Data[1];
+ /* read response data immediately follows */
} __attribute__((packed)) READ_RSP;
typedef struct locking_andx_range {
@@ -1913,6 +1911,10 @@ typedef struct whoami_rsp_data { /* Query level 0x202 */
/* SETFSInfo Levels */
#define SMB_SET_CIFS_UNIX_INFO 0x200
+/* level 0x203 is defined above in list of QFS info levels */
+/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */
+
+/* Level 0x200 request structure follows */
typedef struct smb_com_transaction2_setfsi_req {
struct smb_hdr hdr; /* wct = 15 */
__le16 TotalParameterCount;
@@ -1940,13 +1942,39 @@ typedef struct smb_com_transaction2_setfsi_req {
__le64 ClientUnixCap; /* Data end */
} __attribute__((packed)) TRANSACTION2_SETFSI_REQ;
+/* level 0x203 request structure follows */
+typedef struct smb_com_transaction2_setfs_enc_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount; /* 4 */
+ __le16 ParameterOffset;
+ __le16 DataCount; /* 12 */
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 Reserved4; /* Parameters start. */
+ __le16 InformationLevel;/* Parameters end. */
+ /* NTLMSSP Blob, Data start. */
+} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ;
+
+/* response for setfsinfo levels 0x200 and 0x203 */
typedef struct smb_com_transaction2_setfsi_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
} __attribute__((packed)) TRANSACTION2_SETFSI_RSP;
-
typedef struct smb_com_transaction2_get_dfs_refer_req {
struct smb_hdr hdr; /* wct = 15 */
__le16 TotalParameterCount;
@@ -2098,13 +2126,13 @@ typedef struct {
#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and
QFS PROXY call */
#ifdef CONFIG_CIFS_POSIX
-/* Can not set pathnames cap yet until we send new posix create SMB since
- otherwise server can treat such handles opened with older ntcreatex
- (by a new client which knows how to send posix path ops)
- as non-posix handles (can affect write behavior with byte range locks.
- We can add back in POSIX_PATH_OPS cap when Posix Create/Mkdir finished */
+/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send
+ LockingX instead of posix locking call on unix sess (and we do not expect
+ LockingX to use different (ie Windows) semantics than posix locking on
+ the same session (if WINE needs to do this later, we can add this cap
+ back in later */
/* #define CIFS_UNIX_CAP_MASK 0x000000fb */
-#define CIFS_UNIX_CAP_MASK 0x000000db
+#define CIFS_UNIX_CAP_MASK 0x000003db
#else
#define CIFS_UNIX_CAP_MASK 0x00000013
#endif /* CONFIG_CIFS_POSIX */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 8df28e925e5..6f4e243e0f6 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -69,8 +69,9 @@ extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
struct TCP_Server_Info *server);
extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
extern int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
- unsigned int nvec, mid_callback_t *callback,
- void *cbdata, bool ignore_pend);
+ unsigned int nvec, mid_receive_t *receive,
+ mid_callback_t *callback, void *cbdata,
+ bool ignore_pend);
extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
struct smb_hdr * /* input */ ,
struct smb_hdr * /* out */ ,
@@ -90,6 +91,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
extern bool is_valid_oplock_break(struct smb_hdr *smb,
struct TCP_Server_Info *);
+extern bool backup_cred(struct cifs_sb_info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written);
@@ -145,12 +147,19 @@ extern int cifs_get_inode_info_unix(struct inode **pinode,
extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr, struct inode *inode,
const char *path, const __u16 *pfid);
-extern int mode_to_cifs_acl(struct inode *inode, const char *path, __u64);
+extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64,
+ uid_t, gid_t);
extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *,
const char *, u32 *);
extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *,
- const char *);
+ const char *, int);
+extern void dequeue_mid(struct mid_q_entry *mid, bool malformed);
+extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
+ unsigned int to_read);
+extern int cifs_readv_from_socket(struct TCP_Server_Info *server,
+ struct kvec *iov_orig, unsigned int nr_segs,
+ unsigned int to_read);
extern void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
struct cifs_sb_info *cifs_sb);
extern int cifs_match_super(struct super_block *, void *);
@@ -359,14 +368,17 @@ extern int CIFSGetSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
const struct nls_table *nls_codepage,
int remap_special_chars);
+extern int cifs_lockv(const int xid, struct cifs_tcon *tcon, const __u16 netfid,
+ const __u8 lock_type, const __u32 num_unlock,
+ const __u32 num_lock, LOCKING_ANDX_RANGE *buf);
extern int CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
- const __u16 netfid, const __u64 len,
+ const __u16 netfid, const __u32 netpid, const __u64 len,
const __u64 offset, const __u32 numUnlock,
const __u32 numLock, const __u8 lockType,
const bool waitFlag, const __u8 oplock_level);
extern int CIFSSMBPosixLock(const int xid, struct cifs_tcon *tcon,
- const __u16 smb_file_id, const int get_flag,
- const __u64 len, struct file_lock *,
+ const __u16 smb_file_id, const __u32 netpid,
+ const int get_flag, const __u64 len, struct file_lock *,
const __u16 lock_type, const bool waitFlag);
extern int CIFSSMBTDis(const int xid, struct cifs_tcon *tcon);
extern int CIFSSMBEcho(struct TCP_Server_Info *server);
@@ -380,11 +392,12 @@ extern void tconInfoFree(struct cifs_tcon *);
extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
__u32 *);
-extern int cifs_verify_signature(struct smb_hdr *,
+extern int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
struct TCP_Server_Info *server,
__u32 expected_sequence_number);
-extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *);
-extern int setup_ntlm_response(struct cifs_ses *);
+extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *,
+ const struct nls_table *);
+extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *);
extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
@@ -419,7 +432,7 @@ extern int CIFSSMBSetEA(const int xid, struct cifs_tcon *tcon,
extern int CIFSSMBGetCIFSACL(const int xid, struct cifs_tcon *tcon,
__u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
extern int CIFSSMBSetCIFSACL(const int, struct cifs_tcon *, __u16,
- struct cifs_ntsd *, __u32);
+ struct cifs_ntsd *, __u32, int);
extern int CIFSSMBGetPosixACL(const int xid, struct cifs_tcon *tcon,
const unsigned char *searchName,
char *acl_inf, const int buflen, const int acl_type,
@@ -436,10 +449,29 @@ extern int CIFSCheckMFSymlink(struct cifs_fattr *fattr,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, int xid);
extern int mdfour(unsigned char *, unsigned char *, int);
-extern int E_md4hash(const unsigned char *passwd, unsigned char *p16);
+extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
+ const struct nls_table *codepage);
extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
unsigned char *p24);
+/* asynchronous read support */
+struct cifs_readdata {
+ struct cifsFileInfo *cfile;
+ struct address_space *mapping;
+ __u64 offset;
+ unsigned int bytes;
+ pid_t pid;
+ int result;
+ struct list_head pages;
+ struct work_struct work;
+ unsigned int nr_iov;
+ struct kvec iov[1];
+};
+
+struct cifs_readdata *cifs_readdata_alloc(unsigned int nr_pages);
+void cifs_readdata_free(struct cifs_readdata *rdata);
+int cifs_async_readv(struct cifs_readdata *rdata);
+
/* asynchronous write support */
struct cifs_writedata {
struct kref refcount;
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 1a9fe7f816d..6600aa2d2ef 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -33,6 +33,8 @@
#include <linux/slab.h>
#include <linux/posix_acl_xattr.h>
#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/task_io_accounting_ops.h>
#include <asm/uaccess.h>
#include "cifspdu.h"
#include "cifsglob.h"
@@ -40,6 +42,7 @@
#include "cifsproto.h"
#include "cifs_unicode.h"
#include "cifs_debug.h"
+#include "fscache.h"
#ifdef CONFIG_CIFS_POSIX
static struct {
@@ -83,6 +86,9 @@ static struct {
#endif /* CONFIG_CIFS_WEAK_PW_HASH */
#endif /* CIFS_POSIX */
+/* Forward declarations */
+static void cifs_readv_complete(struct work_struct *work);
+
/* Mark as invalid, all open files on tree connections since they
were closed when session to server was lost */
static void mark_open_files_invalid(struct cifs_tcon *pTcon)
@@ -107,7 +113,7 @@ static void mark_open_files_invalid(struct cifs_tcon *pTcon)
static int
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
{
- int rc = 0;
+ int rc;
struct cifs_ses *ses;
struct TCP_Server_Info *server;
struct nls_table *nls_codepage;
@@ -453,8 +459,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
}
server->sec_mode = (__u8)le16_to_cpu(rsp->SecurityMode);
server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
- server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
- (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+ server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
/* even though we do not use raw we might as well set this
accurately, in case we ever find a need for it */
@@ -561,8 +566,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
little endian */
server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
/* probably no need to store and check maxvcs */
- server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
- (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+ server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);
server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
server->capabilities = le32_to_cpu(pSMBr->Capabilities);
@@ -739,7 +743,8 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
iov.iov_base = smb;
iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
- rc = cifs_call_async(server, &iov, 1, cifs_echo_callback, server, true);
+ rc = cifs_call_async(server, &iov, 1, NULL, cifs_echo_callback,
+ server, true);
if (rc)
cFYI(1, "Echo request failed: %d", rc);
@@ -1376,6 +1381,359 @@ openRetry:
return rc;
}
+struct cifs_readdata *
+cifs_readdata_alloc(unsigned int nr_pages)
+{
+ struct cifs_readdata *rdata;
+
+ /* readdata + 1 kvec for each page */
+ rdata = kzalloc(sizeof(*rdata) +
+ sizeof(struct kvec) * nr_pages, GFP_KERNEL);
+ if (rdata != NULL) {
+ INIT_WORK(&rdata->work, cifs_readv_complete);
+ INIT_LIST_HEAD(&rdata->pages);
+ }
+ return rdata;
+}
+
+void
+cifs_readdata_free(struct cifs_readdata *rdata)
+{
+ cifsFileInfo_put(rdata->cfile);
+ kfree(rdata);
+}
+
+/*
+ * Discard any remaining data in the current SMB. To do this, we borrow the
+ * current bigbuf.
+ */
+static int
+cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ READ_RSP *rsp = (READ_RSP *)server->smallbuf;
+ unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length);
+ int remaining = rfclen + 4 - server->total_read;
+ struct cifs_readdata *rdata = mid->callback_data;
+
+ while (remaining > 0) {
+ int length;
+
+ length = cifs_read_from_socket(server, server->bigbuf,
+ min_t(unsigned int, remaining,
+ CIFSMaxBufSize + MAX_CIFS_HDR_SIZE));
+ if (length < 0)
+ return length;
+ server->total_read += length;
+ remaining -= length;
+ }
+
+ dequeue_mid(mid, rdata->result);
+ return 0;
+}
+
+static int
+cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ int length, len;
+ unsigned int data_offset, remaining, data_len;
+ struct cifs_readdata *rdata = mid->callback_data;
+ READ_RSP *rsp = (READ_RSP *)server->smallbuf;
+ unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length) + 4;
+ u64 eof;
+ pgoff_t eof_index;
+ struct page *page, *tpage;
+
+ cFYI(1, "%s: mid=%u offset=%llu bytes=%u", __func__,
+ mid->mid, rdata->offset, rdata->bytes);
+
+ /*
+ * read the rest of READ_RSP header (sans Data array), or whatever we
+ * can if there's not enough data. At this point, we've read down to
+ * the Mid.
+ */
+ len = min_t(unsigned int, rfclen, sizeof(*rsp)) -
+ sizeof(struct smb_hdr) + 1;
+
+ rdata->iov[0].iov_base = server->smallbuf + sizeof(struct smb_hdr) - 1;
+ rdata->iov[0].iov_len = len;
+
+ length = cifs_readv_from_socket(server, rdata->iov, 1, len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+
+ /* Was the SMB read successful? */
+ rdata->result = map_smb_to_linux_error(&rsp->hdr, false);
+ if (rdata->result != 0) {
+ cFYI(1, "%s: server returned error %d", __func__,
+ rdata->result);
+ return cifs_readv_discard(server, mid);
+ }
+
+ /* Is there enough to get to the rest of the READ_RSP header? */
+ if (server->total_read < sizeof(READ_RSP)) {
+ cFYI(1, "%s: server returned short header. got=%u expected=%zu",
+ __func__, server->total_read, sizeof(READ_RSP));
+ rdata->result = -EIO;
+ return cifs_readv_discard(server, mid);
+ }
+
+ data_offset = le16_to_cpu(rsp->DataOffset) + 4;
+ if (data_offset < server->total_read) {
+ /*
+ * win2k8 sometimes sends an offset of 0 when the read
+ * is beyond the EOF. Treat it as if the data starts just after
+ * the header.
+ */
+ cFYI(1, "%s: data offset (%u) inside read response header",
+ __func__, data_offset);
+ data_offset = server->total_read;
+ } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
+ /* data_offset is beyond the end of smallbuf */
+ cFYI(1, "%s: data offset (%u) beyond end of smallbuf",
+ __func__, data_offset);
+ rdata->result = -EIO;
+ return cifs_readv_discard(server, mid);
+ }
+
+ cFYI(1, "%s: total_read=%u data_offset=%u", __func__,
+ server->total_read, data_offset);
+
+ len = data_offset - server->total_read;
+ if (len > 0) {
+ /* read any junk before data into the rest of smallbuf */
+ rdata->iov[0].iov_base = server->smallbuf + server->total_read;
+ rdata->iov[0].iov_len = len;
+ length = cifs_readv_from_socket(server, rdata->iov, 1, len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+ }
+
+ /* set up first iov for signature check */
+ rdata->iov[0].iov_base = server->smallbuf;
+ rdata->iov[0].iov_len = server->total_read;
+ cFYI(1, "0: iov_base=%p iov_len=%zu",
+ rdata->iov[0].iov_base, rdata->iov[0].iov_len);
+
+ /* how much data is in the response? */
+ data_len = le16_to_cpu(rsp->DataLengthHigh) << 16;
+ data_len += le16_to_cpu(rsp->DataLength);
+ if (data_offset + data_len > rfclen) {
+ /* data_len is corrupt -- discard frame */
+ rdata->result = -EIO;
+ return cifs_readv_discard(server, mid);
+ }
+
+ /* marshal up the page array */
+ len = 0;
+ remaining = data_len;
+ rdata->nr_iov = 1;
+
+ /* determine the eof that the server (probably) has */
+ eof = CIFS_I(rdata->mapping->host)->server_eof;
+ eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
+ cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index);
+
+ list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+ if (remaining >= PAGE_CACHE_SIZE) {
+ /* enough data to fill the page */
+ rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+ rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE;
+ cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+ rdata->nr_iov, page->index,
+ rdata->iov[rdata->nr_iov].iov_base,
+ rdata->iov[rdata->nr_iov].iov_len);
+ ++rdata->nr_iov;
+ len += PAGE_CACHE_SIZE;
+ remaining -= PAGE_CACHE_SIZE;
+ } else if (remaining > 0) {
+ /* enough for partial page, fill and zero the rest */
+ rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+ rdata->iov[rdata->nr_iov].iov_len = remaining;
+ cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+ rdata->nr_iov, page->index,
+ rdata->iov[rdata->nr_iov].iov_base,
+ rdata->iov[rdata->nr_iov].iov_len);
+ memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
+ '\0', PAGE_CACHE_SIZE - remaining);
+ ++rdata->nr_iov;
+ len += remaining;
+ remaining = 0;
+ } else if (page->index > eof_index) {
+ /*
+ * The VFS will not try to do readahead past the
+ * i_size, but it's possible that we have outstanding
+ * writes with gaps in the middle and the i_size hasn't
+ * caught up yet. Populate those with zeroed out pages
+ * to prevent the VFS from repeatedly attempting to
+ * fill them until the writes are flushed.
+ */
+ zero_user(page, 0, PAGE_CACHE_SIZE);
+ list_del(&page->lru);
+ lru_cache_add_file(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+ page_cache_release(page);
+ } else {
+ /* no need to hold page hostage */
+ list_del(&page->lru);
+ lru_cache_add_file(page);
+ unlock_page(page);
+ page_cache_release(page);
+ }
+ }
+
+ /* issue the read if we have any iovecs left to fill */
+ if (rdata->nr_iov > 1) {
+ length = cifs_readv_from_socket(server, &rdata->iov[1],
+ rdata->nr_iov - 1, len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+ } else {
+ length = 0;
+ }
+
+ rdata->bytes = length;
+
+ cFYI(1, "total_read=%u rfclen=%u remaining=%u", server->total_read,
+ rfclen, remaining);
+
+ /* discard anything left over */
+ if (server->total_read < rfclen)
+ return cifs_readv_discard(server, mid);
+
+ dequeue_mid(mid, false);
+ return length;
+}
+
+static void
+cifs_readv_complete(struct work_struct *work)
+{
+ struct cifs_readdata *rdata = container_of(work,
+ struct cifs_readdata, work);
+ struct page *page, *tpage;
+
+ list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+ list_del(&page->lru);
+ lru_cache_add_file(page);
+
+ if (rdata->result == 0) {
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ }
+
+ unlock_page(page);
+
+ if (rdata->result == 0)
+ cifs_readpage_to_fscache(rdata->mapping->host, page);
+
+ page_cache_release(page);
+ }
+ cifs_readdata_free(rdata);
+}
+
+static void
+cifs_readv_callback(struct mid_q_entry *mid)
+{
+ struct cifs_readdata *rdata = mid->callback_data;
+ struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ cFYI(1, "%s: mid=%u state=%d result=%d bytes=%u", __func__,
+ mid->mid, mid->midState, rdata->result, rdata->bytes);
+
+ switch (mid->midState) {
+ case MID_RESPONSE_RECEIVED:
+ /* result already set, check signature */
+ if (server->sec_mode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+ if (cifs_verify_signature(rdata->iov, rdata->nr_iov,
+ server, mid->sequence_number + 1))
+ cERROR(1, "Unexpected SMB signature");
+ }
+ /* FIXME: should this be counted toward the initiating task? */
+ task_io_account_read(rdata->bytes);
+ cifs_stats_bytes_read(tcon, rdata->bytes);
+ break;
+ case MID_REQUEST_SUBMITTED:
+ case MID_RETRY_NEEDED:
+ rdata->result = -EAGAIN;
+ break;
+ default:
+ rdata->result = -EIO;
+ }
+
+ queue_work(system_nrt_wq, &rdata->work);
+ DeleteMidQEntry(mid);
+ atomic_dec(&server->inFlight);
+ wake_up(&server->request_q);
+}
+
+/* cifs_async_readv - send an async write, and set up mid to handle result */
+int
+cifs_async_readv(struct cifs_readdata *rdata)
+{
+ int rc;
+ READ_REQ *smb = NULL;
+ int wct;
+ struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+
+ cFYI(1, "%s: offset=%llu bytes=%u", __func__,
+ rdata->offset, rdata->bytes);
+
+ if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ wct = 12;
+ else {
+ wct = 10; /* old style read */
+ if ((rdata->offset >> 32) > 0) {
+ /* can not handle this big offset for old */
+ return -EIO;
+ }
+ }
+
+ rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb);
+ if (rc)
+ return rc;
+
+ smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid);
+ smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16));
+
+ smb->AndXCommand = 0xFF; /* none */
+ smb->Fid = rdata->cfile->netfid;
+ smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF);
+ if (wct == 12)
+ smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32);
+ smb->Remaining = 0;
+ smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF);
+ smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16);
+ if (wct == 12)
+ smb->ByteCount = 0;
+ else {
+ /* old style read */
+ struct smb_com_readx_req *smbr =
+ (struct smb_com_readx_req *)smb;
+ smbr->ByteCount = 0;
+ }
+
+ /* 4 for RFC1001 length + 1 for BCC */
+ rdata->iov[0].iov_base = smb;
+ rdata->iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
+
+ rc = cifs_call_async(tcon->ses->server, rdata->iov, 1,
+ cifs_readv_receive, cifs_readv_callback,
+ rdata, false);
+
+ if (rc == 0)
+ cifs_stats_inc(&tcon->num_reads);
+
+ cifs_small_buf_release(smb);
+ return rc;
+}
+
int
CIFSSMBRead(const int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes,
char **buf, int *pbuf_type)
@@ -1836,7 +2194,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
kref_get(&wdata->refcount);
rc = cifs_call_async(tcon->ses->server, iov, wdata->nr_pages + 1,
- cifs_writev_callback, wdata, false);
+ NULL, cifs_writev_callback, wdata, false);
if (rc == 0)
cifs_stats_inc(&tcon->num_writes);
@@ -1962,10 +2320,50 @@ CIFSSMBWrite2(const int xid, struct cifs_io_parms *io_parms,
return rc;
}
+int cifs_lockv(const int xid, struct cifs_tcon *tcon, const __u16 netfid,
+ const __u8 lock_type, const __u32 num_unlock,
+ const __u32 num_lock, LOCKING_ANDX_RANGE *buf)
+{
+ int rc = 0;
+ LOCK_REQ *pSMB = NULL;
+ struct kvec iov[2];
+ int resp_buf_type;
+ __u16 count;
+
+ cFYI(1, "cifs_lockv num lock %d num unlock %d", num_lock, num_unlock);
+
+ rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ pSMB->Timeout = 0;
+ pSMB->NumberOfLocks = cpu_to_le16(num_lock);
+ pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock);
+ pSMB->LockType = lock_type;
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid; /* netfid stays le */
+
+ count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+ inc_rfc1001_len(pSMB, count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 -
+ (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+
+ cifs_stats_inc(&tcon->num_locks);
+ rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP);
+ if (rc)
+ cFYI(1, "Send error in cifs_lockv = %d", rc);
+
+ return rc;
+}
int
CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
- const __u16 smb_file_id, const __u64 len,
+ const __u16 smb_file_id, const __u32 netpid, const __u64 len,
const __u64 offset, const __u32 numUnlock,
const __u32 numLock, const __u8 lockType,
const bool waitFlag, const __u8 oplock_level)
@@ -2001,7 +2399,7 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
pSMB->Fid = smb_file_id; /* netfid stays le */
if ((numLock != 0) || (numUnlock != 0)) {
- pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
+ pSMB->Locks[0].Pid = cpu_to_le16(netpid);
/* BB where to store pid high? */
pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
@@ -2035,9 +2433,9 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
int
CIFSSMBPosixLock(const int xid, struct cifs_tcon *tcon,
- const __u16 smb_file_id, const int get_flag, const __u64 len,
- struct file_lock *pLockData, const __u16 lock_type,
- const bool waitFlag)
+ const __u16 smb_file_id, const __u32 netpid, const int get_flag,
+ const __u64 len, struct file_lock *pLockData,
+ const __u16 lock_type, const bool waitFlag)
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
@@ -2095,7 +2493,7 @@ CIFSSMBPosixLock(const int xid, struct cifs_tcon *tcon,
} else
pSMB->Timeout = 0;
- parm_data->pid = cpu_to_le32(current->tgid);
+ parm_data->pid = cpu_to_le32(netpid);
parm_data->start = cpu_to_le64(pLockData->fl_start);
parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
@@ -2812,8 +3210,7 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifs_tcon *tcon,
pSMB->TotalDataCount = 0;
pSMB->MaxParameterCount = cpu_to_le32(2);
/* BB find exact data count max from sess structure BB */
- pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
- MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
pSMB->MaxSetupCount = 4;
pSMB->Reserved = 0;
pSMB->ParameterOffset = 0;
@@ -3306,8 +3703,7 @@ smb_init_nttransact(const __u16 sub_command, const int setup_count,
pSMB->Reserved = 0;
pSMB->TotalParameterCount = cpu_to_le32(parm_len);
pSMB->TotalDataCount = 0;
- pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
- MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->DataCount = pSMB->TotalDataCount;
temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
@@ -3467,7 +3863,7 @@ qsec_out:
int
CIFSSMBSetCIFSACL(const int xid, struct cifs_tcon *tcon, __u16 fid,
- struct cifs_ntsd *pntsd, __u32 acllen)
+ struct cifs_ntsd *pntsd, __u32 acllen, int aclflag)
{
__u16 byte_count, param_count, data_count, param_offset, data_offset;
int rc = 0;
@@ -3504,7 +3900,7 @@ setCifsAclRetry:
pSMB->Fid = fid; /* file handle always le */
pSMB->Reserved2 = 0;
- pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
+ pSMB->AclFlags = cpu_to_le32(aclflag);
if (pntsd && acllen) {
memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
@@ -3977,8 +4373,7 @@ findFirstRetry:
params = 12 + name_len /* includes null */ ;
pSMB->TotalDataCount = 0; /* no EAs */
pSMB->MaxParameterCount = cpu_to_le16(10);
- pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
- MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -4052,8 +4447,7 @@ findFirstRetry:
psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
psrch_inf->entries_in_buffer;
lnoff = le16_to_cpu(parms->LastNameOffset);
- if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
- lnoff) {
+ if (CIFSMaxBufSize < lnoff) {
cERROR(1, "ignoring corrupt resume name");
psrch_inf->last_entry = NULL;
return rc;
@@ -4079,7 +4473,8 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
T2_FNEXT_RSP_PARMS *parms;
char *response_data;
int rc = 0;
- int bytes_returned, name_len;
+ int bytes_returned;
+ unsigned int name_len;
__u16 params, byte_count;
cFYI(1, "In FindNext");
@@ -4096,9 +4491,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
byte_count = 0;
pSMB->TotalDataCount = 0; /* no EAs */
pSMB->MaxParameterCount = cpu_to_le16(8);
- pSMB->MaxDataCount =
- cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
- 0xFFFFFF00);
+ pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -4180,8 +4573,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
psrch_inf->index_of_last_entry +=
psrch_inf->entries_in_buffer;
lnoff = le16_to_cpu(parms->LastNameOffset);
- if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
- lnoff) {
+ if (CIFSMaxBufSize < lnoff) {
cERROR(1, "ignoring corrupt resume name");
psrch_inf->last_entry = NULL;
return rc;
@@ -5720,6 +6112,7 @@ CIFSSMBQAllEAs(const int xid, struct cifs_tcon *tcon,
char *temp_ptr;
char *end_of_smb;
__u16 params, byte_count, data_offset;
+ unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
cFYI(1, "In Query All EAs path %s", searchName);
QAllEAsRetry:
@@ -5837,7 +6230,8 @@ QAllEAsRetry:
}
if (ea_name) {
- if (strncmp(ea_name, temp_ptr, name_len) == 0) {
+ if (ea_name_len == name_len &&
+ memcmp(ea_name, temp_ptr, name_len) == 0) {
temp_ptr += name_len + 1;
rc = value_len;
if (buf_size == 0)
@@ -6032,12 +6426,7 @@ int CIFSSMBNotify(const int xid, struct cifs_tcon *tcon,
pSMB->TotalParameterCount = 0 ;
pSMB->TotalDataCount = 0;
pSMB->MaxParameterCount = cpu_to_le32(2);
- /* BB find exact data count max from sess structure BB */
- pSMB->MaxDataCount = 0; /* same in little endian or be */
-/* BB VERIFY verify which is correct for above BB */
- pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
- MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
-
+ pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
pSMB->MaxSetupCount = 4;
pSMB->Reserved = 0;
pSMB->ParameterOffset = 0;
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index e66297bad41..d6a972df033 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -37,6 +37,7 @@
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <linux/inet.h>
+#include <linux/module.h>
#include <net/ipv6.h>
#include "cifspdu.h"
#include "cifsglob.h"
@@ -181,7 +182,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
-EINVAL = invalid transact2
*/
-static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize)
+static int check2ndT2(struct smb_hdr *pSMB)
{
struct smb_t2_rsp *pSMBt;
int remaining;
@@ -214,9 +215,9 @@ static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize)
cFYI(1, "missing %d bytes from transact2, check next response",
remaining);
- if (total_data_size > maxBufSize) {
+ if (total_data_size > CIFSMaxBufSize) {
cERROR(1, "TotalDataSize %d is over maximum buffer %d",
- total_data_size, maxBufSize);
+ total_data_size, CIFSMaxBufSize);
return -EINVAL;
}
return remaining;
@@ -319,344 +320,298 @@ requeue_echo:
queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL);
}
-static int
-cifs_demultiplex_thread(void *p)
+static bool
+allocate_buffers(struct TCP_Server_Info *server)
{
- int length;
- struct TCP_Server_Info *server = p;
- unsigned int pdu_length, total_read;
- struct smb_hdr *smb_buffer = NULL;
- struct smb_hdr *bigbuf = NULL;
- struct smb_hdr *smallbuf = NULL;
- struct msghdr smb_msg;
- struct kvec iov;
- struct socket *csocket = server->ssocket;
- struct list_head *tmp, *tmp2;
- struct task_struct *task_to_wake = NULL;
- struct mid_q_entry *mid_entry;
- char temp;
- bool isLargeBuf = false;
- bool isMultiRsp;
- int reconnect;
+ if (!server->bigbuf) {
+ server->bigbuf = (char *)cifs_buf_get();
+ if (!server->bigbuf) {
+ cERROR(1, "No memory for large SMB response");
+ msleep(3000);
+ /* retry will check if exiting */
+ return false;
+ }
+ } else if (server->large_buf) {
+ /* we are reusing a dirty large buf, clear its start */
+ memset(server->bigbuf, 0, sizeof(struct smb_hdr));
+ }
- current->flags |= PF_MEMALLOC;
- cFYI(1, "Demultiplex PID: %d", task_pid_nr(current));
+ if (!server->smallbuf) {
+ server->smallbuf = (char *)cifs_small_buf_get();
+ if (!server->smallbuf) {
+ cERROR(1, "No memory for SMB response");
+ msleep(1000);
+ /* retry will check if exiting */
+ return false;
+ }
+ /* beginning of smb buffer is cleared in our buf_get */
+ } else {
+ /* if existing small buf clear beginning */
+ memset(server->smallbuf, 0, sizeof(struct smb_hdr));
+ }
- length = atomic_inc_return(&tcpSesAllocCount);
- if (length > 1)
- mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
- GFP_KERNEL);
+ return true;
+}
- set_freezable();
- while (server->tcpStatus != CifsExiting) {
- if (try_to_freeze())
- continue;
- if (bigbuf == NULL) {
- bigbuf = cifs_buf_get();
- if (!bigbuf) {
- cERROR(1, "No memory for large SMB response");
- msleep(3000);
- /* retry will check if exiting */
- continue;
- }
- } else if (isLargeBuf) {
- /* we are reusing a dirty large buf, clear its start */
- memset(bigbuf, 0, sizeof(struct smb_hdr));
+static bool
+server_unresponsive(struct TCP_Server_Info *server)
+{
+ if (echo_retries > 0 && server->tcpStatus == CifsGood &&
+ time_after(jiffies, server->lstrp +
+ (echo_retries * SMB_ECHO_INTERVAL))) {
+ cERROR(1, "Server %s has not responded in %d seconds. "
+ "Reconnecting...", server->hostname,
+ (echo_retries * SMB_ECHO_INTERVAL / HZ));
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * kvec_array_init - clone a kvec array, and advance into it
+ * @new: pointer to memory for cloned array
+ * @iov: pointer to original array
+ * @nr_segs: number of members in original array
+ * @bytes: number of bytes to advance into the cloned array
+ *
+ * This function will copy the array provided in iov to a section of memory
+ * and advance the specified number of bytes into the new array. It returns
+ * the number of segments in the new array. "new" must be at least as big as
+ * the original iov array.
+ */
+static unsigned int
+kvec_array_init(struct kvec *new, struct kvec *iov, unsigned int nr_segs,
+ size_t bytes)
+{
+ size_t base = 0;
+
+ while (bytes || !iov->iov_len) {
+ int copy = min(bytes, iov->iov_len);
+
+ bytes -= copy;
+ base += copy;
+ if (iov->iov_len == base) {
+ iov++;
+ nr_segs--;
+ base = 0;
}
+ }
+ memcpy(new, iov, sizeof(*iov) * nr_segs);
+ new->iov_base += base;
+ new->iov_len -= base;
+ return nr_segs;
+}
- if (smallbuf == NULL) {
- smallbuf = cifs_small_buf_get();
- if (!smallbuf) {
- cERROR(1, "No memory for SMB response");
- msleep(1000);
- /* retry will check if exiting */
- continue;
- }
- /* beginning of smb buffer is cleared in our buf_get */
- } else /* if existing small buf clear beginning */
- memset(smallbuf, 0, sizeof(struct smb_hdr));
-
- isLargeBuf = false;
- isMultiRsp = false;
- smb_buffer = smallbuf;
- iov.iov_base = smb_buffer;
- iov.iov_len = 4;
- smb_msg.msg_control = NULL;
- smb_msg.msg_controllen = 0;
- pdu_length = 4; /* enough to get RFC1001 header */
+static struct kvec *
+get_server_iovec(struct TCP_Server_Info *server, unsigned int nr_segs)
+{
+ struct kvec *new_iov;
-incomplete_rcv:
- if (echo_retries > 0 && server->tcpStatus == CifsGood &&
- time_after(jiffies, server->lstrp +
- (echo_retries * SMB_ECHO_INTERVAL))) {
- cERROR(1, "Server %s has not responded in %d seconds. "
- "Reconnecting...", server->hostname,
- (echo_retries * SMB_ECHO_INTERVAL / HZ));
- cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
+ if (server->iov && nr_segs <= server->nr_iov)
+ return server->iov;
+
+ /* not big enough -- allocate a new one and release the old */
+ new_iov = kmalloc(sizeof(*new_iov) * nr_segs, GFP_NOFS);
+ if (new_iov) {
+ kfree(server->iov);
+ server->iov = new_iov;
+ server->nr_iov = nr_segs;
+ }
+ return new_iov;
+}
+
+int
+cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig,
+ unsigned int nr_segs, unsigned int to_read)
+{
+ int length = 0;
+ int total_read;
+ unsigned int segs;
+ struct msghdr smb_msg;
+ struct kvec *iov;
+
+ iov = get_server_iovec(server, nr_segs);
+ if (!iov)
+ return -ENOMEM;
+
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
+
+ for (total_read = 0; to_read; total_read += length, to_read -= length) {
+ if (server_unresponsive(server)) {
+ total_read = -EAGAIN;
+ break;
}
- length =
- kernel_recvmsg(csocket, &smb_msg,
- &iov, 1, pdu_length, 0 /* BB other flags? */);
+ segs = kvec_array_init(iov, iov_orig, nr_segs, total_read);
+
+ length = kernel_recvmsg(server->ssocket, &smb_msg,
+ iov, segs, to_read, 0);
if (server->tcpStatus == CifsExiting) {
+ total_read = -ESHUTDOWN;
break;
} else if (server->tcpStatus == CifsNeedReconnect) {
- cFYI(1, "Reconnect after server stopped responding");
cifs_reconnect(server);
- cFYI(1, "call to reconnect done");
- csocket = server->ssocket;
- continue;
+ total_read = -EAGAIN;
+ break;
} else if (length == -ERESTARTSYS ||
length == -EAGAIN ||
length == -EINTR) {
- msleep(1); /* minimum sleep to prevent looping
- allowing socket to clear and app threads to set
- tcpStatus CifsNeedReconnect if server hung */
- if (pdu_length < 4) {
- iov.iov_base = (4 - pdu_length) +
- (char *)smb_buffer;
- iov.iov_len = pdu_length;
- smb_msg.msg_control = NULL;
- smb_msg.msg_controllen = 0;
- goto incomplete_rcv;
- } else
- continue;
- } else if (length <= 0) {
- cFYI(1, "Reconnect after unexpected peek error %d",
- length);
- cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
- } else if (length < pdu_length) {
- cFYI(1, "requested %d bytes but only got %d bytes",
- pdu_length, length);
- pdu_length -= length;
- msleep(1);
- goto incomplete_rcv;
- }
-
- /* The right amount was read from socket - 4 bytes */
- /* so we can now interpret the length field */
-
- /* the first byte big endian of the length field,
- is actually not part of the length but the type
- with the most common, zero, as regular data */
- temp = *((char *) smb_buffer);
-
- /* Note that FC 1001 length is big endian on the wire,
- but we convert it here so it is always manipulated
- as host byte order */
- pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
-
- cFYI(1, "rfc1002 length 0x%x", pdu_length+4);
-
- if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
- continue;
- } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
- cFYI(1, "Good RFC 1002 session rsp");
- continue;
- } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
- /* we get this from Windows 98 instead of
- an error on SMB negprot response */
- cFYI(1, "Negative RFC1002 Session Response Error 0x%x)",
- pdu_length);
- /* give server a second to clean up */
- msleep(1000);
- /* always try 445 first on reconnect since we get NACK
- * on some if we ever connected to port 139 (the NACK
- * is since we do not begin with RFC1001 session
- * initialize frame)
+ /*
+ * Minimum sleep to prevent looping, allowing socket
+ * to clear and app threads to set tcpStatus
+ * CifsNeedReconnect if server hung.
*/
- cifs_set_port((struct sockaddr *)
- &server->dstaddr, CIFS_PORT);
- cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
- } else if (temp != (char) 0) {
- cERROR(1, "Unknown RFC 1002 frame");
- cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
- length);
- cifs_reconnect(server);
- csocket = server->ssocket;
+ usleep_range(1000, 2000);
+ length = 0;
continue;
- }
-
- /* else we have an SMB response */
- if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
- (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
- cERROR(1, "Invalid size SMB length %d pdu_length %d",
- length, pdu_length+4);
+ } else if (length <= 0) {
+ cFYI(1, "Received no data or error: expecting %d "
+ "got %d", to_read, length);
cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
+ total_read = -EAGAIN;
+ break;
}
+ }
+ return total_read;
+}
- /* else length ok */
- reconnect = 0;
-
- if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
- isLargeBuf = true;
- memcpy(bigbuf, smallbuf, 4);
- smb_buffer = bigbuf;
- }
- length = 0;
- iov.iov_base = 4 + (char *)smb_buffer;
- iov.iov_len = pdu_length;
- for (total_read = 0; total_read < pdu_length;
- total_read += length) {
- length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
- pdu_length - total_read, 0);
- if (server->tcpStatus == CifsExiting) {
- /* then will exit */
- reconnect = 2;
- break;
- } else if (server->tcpStatus == CifsNeedReconnect) {
- cifs_reconnect(server);
- csocket = server->ssocket;
- /* Reconnect wakes up rspns q */
- /* Now we will reread sock */
- reconnect = 1;
- break;
- } else if (length == -ERESTARTSYS ||
- length == -EAGAIN ||
- length == -EINTR) {
- msleep(1); /* minimum sleep to prevent looping,
- allowing socket to clear and app
- threads to set tcpStatus
- CifsNeedReconnect if server hung*/
- length = 0;
- continue;
- } else if (length <= 0) {
- cERROR(1, "Received no data, expecting %d",
- pdu_length - total_read);
- cifs_reconnect(server);
- csocket = server->ssocket;
- reconnect = 1;
- break;
- }
- }
- if (reconnect == 2)
- break;
- else if (reconnect == 1)
- continue;
+int
+cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
+ unsigned int to_read)
+{
+ struct kvec iov;
- total_read += 4; /* account for rfc1002 hdr */
+ iov.iov_base = buf;
+ iov.iov_len = to_read;
- dump_smb(smb_buffer, total_read);
+ return cifs_readv_from_socket(server, &iov, 1, to_read);
+}
+static bool
+is_smb_response(struct TCP_Server_Info *server, unsigned char type)
+{
+ /*
+ * The first byte big endian of the length field,
+ * is actually not part of the length but the type
+ * with the most common, zero, as regular data.
+ */
+ switch (type) {
+ case RFC1002_SESSION_MESSAGE:
+ /* Regular SMB response */
+ return true;
+ case RFC1002_SESSION_KEEP_ALIVE:
+ cFYI(1, "RFC 1002 session keep alive");
+ break;
+ case RFC1002_POSITIVE_SESSION_RESPONSE:
+ cFYI(1, "RFC 1002 positive session response");
+ break;
+ case RFC1002_NEGATIVE_SESSION_RESPONSE:
/*
- * We know that we received enough to get to the MID as we
- * checked the pdu_length earlier. Now check to see
- * if the rest of the header is OK. We borrow the length
- * var for the rest of the loop to avoid a new stack var.
- *
- * 48 bytes is enough to display the header and a little bit
- * into the payload for debugging purposes.
+ * We get this from Windows 98 instead of an error on
+ * SMB negprot response.
*/
- length = checkSMB(smb_buffer, smb_buffer->Mid, total_read);
- if (length != 0)
- cifs_dump_mem("Bad SMB: ", smb_buffer,
- min_t(unsigned int, total_read, 48));
+ cFYI(1, "RFC 1002 negative session response");
+ /* give server a second to clean up */
+ msleep(1000);
+ /*
+ * Always try 445 first on reconnect since we get NACK
+ * on some if we ever connected to port 139 (the NACK
+ * is since we do not begin with RFC1001 session
+ * initialize frame).
+ */
+ cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ break;
+ default:
+ cERROR(1, "RFC 1002 unknown response type 0x%x", type);
+ cifs_reconnect(server);
+ }
- mid_entry = NULL;
- server->lstrp = jiffies;
+ return false;
+}
- spin_lock(&GlobalMid_Lock);
- list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
- mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+static struct mid_q_entry *
+find_mid(struct TCP_Server_Info *server, struct smb_hdr *buf)
+{
+ struct mid_q_entry *mid;
- if (mid_entry->mid != smb_buffer->Mid ||
- mid_entry->midState != MID_REQUEST_SUBMITTED ||
- mid_entry->command != smb_buffer->Command) {
- mid_entry = NULL;
- continue;
- }
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_entry(mid, &server->pending_mid_q, qhead) {
+ if (mid->mid == buf->Mid &&
+ mid->midState == MID_REQUEST_SUBMITTED &&
+ mid->command == buf->Command) {
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ return NULL;
+}
- if (length == 0 &&
- check2ndT2(smb_buffer, server->maxBuf) > 0) {
- /* We have a multipart transact2 resp */
- isMultiRsp = true;
- if (mid_entry->resp_buf) {
- /* merge response - fix up 1st*/
- length = coalesce_t2(smb_buffer,
- mid_entry->resp_buf);
- if (length > 0) {
- length = 0;
- mid_entry->multiRsp = true;
- break;
- } else {
- /* all parts received or
- * packet is malformed
- */
- mid_entry->multiEnd = true;
- goto multi_t2_fnd;
- }
- } else {
- if (!isLargeBuf) {
- /*
- * FIXME: switch to already
- * allocated largebuf?
- */
- cERROR(1, "1st trans2 resp "
- "needs bigbuf");
- } else {
- /* Have first buffer */
- mid_entry->resp_buf =
- smb_buffer;
- mid_entry->largeBuf = true;
- bigbuf = NULL;
- }
- }
- break;
- }
- mid_entry->resp_buf = smb_buffer;
- mid_entry->largeBuf = isLargeBuf;
-multi_t2_fnd:
- if (length == 0)
- mid_entry->midState = MID_RESPONSE_RECEIVED;
- else
- mid_entry->midState = MID_RESPONSE_MALFORMED;
+void
+dequeue_mid(struct mid_q_entry *mid, bool malformed)
+{
#ifdef CONFIG_CIFS_STATS2
- mid_entry->when_received = jiffies;
+ mid->when_received = jiffies;
#endif
- list_del_init(&mid_entry->qhead);
- break;
- }
- spin_unlock(&GlobalMid_Lock);
-
- if (mid_entry != NULL) {
- mid_entry->callback(mid_entry);
- /* Was previous buf put in mpx struct for multi-rsp? */
- if (!isMultiRsp) {
- /* smb buffer will be freed by user thread */
- if (isLargeBuf)
- bigbuf = NULL;
- else
- smallbuf = NULL;
- }
- } else if (length != 0) {
- /* response sanity checks failed */
- continue;
- } else if (!is_valid_oplock_break(smb_buffer, server) &&
- !isMultiRsp) {
- cERROR(1, "No task to wake, unknown frame received! "
- "NumMids %d", atomic_read(&midCount));
- cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
- sizeof(struct smb_hdr));
-#ifdef CONFIG_CIFS_DEBUG2
- cifs_dump_detail(smb_buffer);
- cifs_dump_mids(server);
-#endif /* CIFS_DEBUG2 */
+ spin_lock(&GlobalMid_Lock);
+ if (!malformed)
+ mid->midState = MID_RESPONSE_RECEIVED;
+ else
+ mid->midState = MID_RESPONSE_MALFORMED;
+ list_del_init(&mid->qhead);
+ spin_unlock(&GlobalMid_Lock);
+}
+static void
+handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ struct smb_hdr *buf, int malformed)
+{
+ if (malformed == 0 && check2ndT2(buf) > 0) {
+ mid->multiRsp = true;
+ if (mid->resp_buf) {
+ /* merge response - fix up 1st*/
+ malformed = coalesce_t2(buf, mid->resp_buf);
+ if (malformed > 0)
+ return;
+
+ /* All parts received or packet is malformed. */
+ mid->multiEnd = true;
+ return dequeue_mid(mid, malformed);
}
- } /* end while !EXITING */
+ if (!server->large_buf) {
+ /*FIXME: switch to already allocated largebuf?*/
+ cERROR(1, "1st trans2 resp needs bigbuf");
+ } else {
+ /* Have first buffer */
+ mid->resp_buf = buf;
+ mid->largeBuf = true;
+ server->bigbuf = NULL;
+ }
+ return;
+ }
+ mid->resp_buf = buf;
+ mid->largeBuf = server->large_buf;
+ /* Was previous buf put in mpx struct for multi-rsp? */
+ if (!mid->multiRsp) {
+ /* smb buffer will be freed by user thread */
+ if (server->large_buf)
+ server->bigbuf = NULL;
+ else
+ server->smallbuf = NULL;
+ }
+ dequeue_mid(mid, malformed);
+}
+
+static void clean_demultiplex_info(struct TCP_Server_Info *server)
+{
+ int length;
/* take it off the list, if it's not already */
spin_lock(&cifs_tcp_ses_lock);
@@ -668,35 +623,39 @@ multi_t2_fnd:
spin_unlock(&GlobalMid_Lock);
wake_up_all(&server->response_q);
- /* check if we have blocked requests that need to free */
- /* Note that cifs_max_pending is normally 50, but
- can be set at module install time to as little as two */
+ /*
+ * Check if we have blocked requests that need to free. Note that
+ * cifs_max_pending is normally 50, but can be set at module install
+ * time to as little as two.
+ */
spin_lock(&GlobalMid_Lock);
if (atomic_read(&server->inFlight) >= cifs_max_pending)
atomic_set(&server->inFlight, cifs_max_pending - 1);
- /* We do not want to set the max_pending too low or we
- could end up with the counter going negative */
+ /*
+ * We do not want to set the max_pending too low or we could end up
+ * with the counter going negative.
+ */
spin_unlock(&GlobalMid_Lock);
- /* Although there should not be any requests blocked on
- this queue it can not hurt to be paranoid and try to wake up requests
- that may haven been blocked when more than 50 at time were on the wire
- to the same server - they now will see the session is in exit state
- and get out of SendReceive. */
+ /*
+ * Although there should not be any requests blocked on this queue it
+ * can not hurt to be paranoid and try to wake up requests that may
+ * haven been blocked when more than 50 at time were on the wire to the
+ * same server - they now will see the session is in exit state and get
+ * out of SendReceive.
+ */
wake_up_all(&server->request_q);
/* give those requests time to exit */
msleep(125);
if (server->ssocket) {
- sock_release(csocket);
+ sock_release(server->ssocket);
server->ssocket = NULL;
}
- /* buffer usually freed in free_mid - need to free it here on exit */
- cifs_buf_release(bigbuf);
- if (smallbuf) /* no sense logging a debug message if NULL */
- cifs_small_buf_release(smallbuf);
if (!list_empty(&server->pending_mid_q)) {
struct list_head dispose_list;
+ struct mid_q_entry *mid_entry;
+ struct list_head *tmp, *tmp2;
INIT_LIST_HEAD(&dispose_list);
spin_lock(&GlobalMid_Lock);
@@ -720,26 +679,189 @@ multi_t2_fnd:
}
if (!list_empty(&server->pending_mid_q)) {
- /* mpx threads have not exited yet give them
- at least the smb send timeout time for long ops */
- /* due to delays on oplock break requests, we need
- to wait at least 45 seconds before giving up
- on a request getting a response and going ahead
- and killing cifsd */
+ /*
+ * mpx threads have not exited yet give them at least the smb
+ * send timeout time for long ops.
+ *
+ * Due to delays on oplock break requests, we need to wait at
+ * least 45 seconds before giving up on a request getting a
+ * response and going ahead and killing cifsd.
+ */
cFYI(1, "Wait for exit from demultiplex thread");
msleep(46000);
- /* if threads still have not exited they are probably never
- coming home not much else we can do but free the memory */
+ /*
+ * If threads still have not exited they are probably never
+ * coming home not much else we can do but free the memory.
+ */
}
kfree(server->hostname);
- task_to_wake = xchg(&server->tsk, NULL);
+ kfree(server->iov);
kfree(server);
length = atomic_dec_return(&tcpSesAllocCount);
- if (length > 0)
+ if (length > 0)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
GFP_KERNEL);
+}
+
+static int
+standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ int length;
+ char *buf = server->smallbuf;
+ struct smb_hdr *smb_buffer = (struct smb_hdr *)buf;
+ unsigned int pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
+
+ /* make sure this will fit in a large buffer */
+ if (pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cERROR(1, "SMB response too long (%u bytes)",
+ pdu_length);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -EAGAIN;
+ }
+
+ /* switch to large buffer if too big for a small one */
+ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
+ server->large_buf = true;
+ memcpy(server->bigbuf, server->smallbuf, server->total_read);
+ buf = server->bigbuf;
+ smb_buffer = (struct smb_hdr *)buf;
+ }
+
+ /* now read the rest */
+ length = cifs_read_from_socket(server,
+ buf + sizeof(struct smb_hdr) - 1,
+ pdu_length - sizeof(struct smb_hdr) + 1 + 4);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+
+ dump_smb(smb_buffer, server->total_read);
+
+ /*
+ * We know that we received enough to get to the MID as we
+ * checked the pdu_length earlier. Now check to see
+ * if the rest of the header is OK. We borrow the length
+ * var for the rest of the loop to avoid a new stack var.
+ *
+ * 48 bytes is enough to display the header and a little bit
+ * into the payload for debugging purposes.
+ */
+ length = checkSMB(smb_buffer, smb_buffer->Mid, server->total_read);
+ if (length != 0)
+ cifs_dump_mem("Bad SMB: ", buf,
+ min_t(unsigned int, server->total_read, 48));
+
+ if (mid)
+ handle_mid(mid, server, smb_buffer, length);
+
+ return length;
+}
+
+static int
+cifs_demultiplex_thread(void *p)
+{
+ int length;
+ struct TCP_Server_Info *server = p;
+ unsigned int pdu_length;
+ char *buf = NULL;
+ struct smb_hdr *smb_buffer = NULL;
+ struct task_struct *task_to_wake = NULL;
+ struct mid_q_entry *mid_entry;
+
+ current->flags |= PF_MEMALLOC;
+ cFYI(1, "Demultiplex PID: %d", task_pid_nr(current));
+
+ length = atomic_inc_return(&tcpSesAllocCount);
+ if (length > 1)
+ mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
+ GFP_KERNEL);
+
+ set_freezable();
+ while (server->tcpStatus != CifsExiting) {
+ if (try_to_freeze())
+ continue;
+
+ if (!allocate_buffers(server))
+ continue;
+
+ server->large_buf = false;
+ smb_buffer = (struct smb_hdr *)server->smallbuf;
+ buf = server->smallbuf;
+ pdu_length = 4; /* enough to get RFC1001 header */
+
+ length = cifs_read_from_socket(server, buf, pdu_length);
+ if (length < 0)
+ continue;
+ server->total_read = length;
+
+ /*
+ * The right amount was read from socket - 4 bytes,
+ * so we can now interpret the length field.
+ */
+ pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
+
+ cFYI(1, "RFC1002 header 0x%x", pdu_length);
+ if (!is_smb_response(server, buf[0]))
+ continue;
+
+ /* make sure we have enough to get to the MID */
+ if (pdu_length < sizeof(struct smb_hdr) - 1 - 4) {
+ cERROR(1, "SMB response too short (%u bytes)",
+ pdu_length);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ continue;
+ }
+
+ /* read down to the MID */
+ length = cifs_read_from_socket(server, buf + 4,
+ sizeof(struct smb_hdr) - 1 - 4);
+ if (length < 0)
+ continue;
+ server->total_read += length;
+
+ mid_entry = find_mid(server, smb_buffer);
+
+ if (!mid_entry || !mid_entry->receive)
+ length = standard_receive3(server, mid_entry);
+ else
+ length = mid_entry->receive(server, mid_entry);
+
+ if (length < 0)
+ continue;
+
+ if (server->large_buf) {
+ buf = server->bigbuf;
+ smb_buffer = (struct smb_hdr *)buf;
+ }
+
+ server->lstrp = jiffies;
+ if (mid_entry != NULL) {
+ if (!mid_entry->multiRsp || mid_entry->multiEnd)
+ mid_entry->callback(mid_entry);
+ } else if (!is_valid_oplock_break(smb_buffer, server)) {
+ cERROR(1, "No task to wake, unknown frame received! "
+ "NumMids %d", atomic_read(&midCount));
+ cifs_dump_mem("Received Data is: ", buf,
+ sizeof(struct smb_hdr));
+#ifdef CONFIG_CIFS_DEBUG2
+ cifs_dump_detail(smb_buffer);
+ cifs_dump_mids(server);
+#endif /* CIFS_DEBUG2 */
+
+ }
+ } /* end while !EXITING */
+
+ /* buffer usually freed in free_mid - need to free it here on exit */
+ cifs_buf_release(server->bigbuf);
+ if (server->smallbuf) /* no sense logging a debug message if NULL */
+ cifs_small_buf_release(server->smallbuf);
+
+ task_to_wake = xchg(&server->tsk, NULL);
+ clean_demultiplex_info(server);
/* if server->tsk was NULL then wait for a signal before exiting */
if (!task_to_wake) {
@@ -788,6 +910,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
{
char *value, *data, *end;
char *mountdata_copy = NULL, *options;
+ int err;
unsigned int temp_len, i, j;
char separator[2];
short int override_uid = -1;
@@ -844,6 +967,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
cFYI(1, "Null separator not allowed");
}
}
+ vol->backupuid_specified = false; /* no backup intent for a user */
+ vol->backupgid_specified = false; /* no backup intent for a group */
while ((data = strsep(&options, separator)) != NULL) {
if (!*data)
@@ -1259,7 +1384,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
/* ignore */
} else if (strnicmp(data, "guest", 5) == 0) {
/* ignore */
- } else if (strnicmp(data, "rw", 2) == 0) {
+ } else if (strnicmp(data, "rw", 2) == 0 && strlen(data) == 2) {
/* ignore */
} else if (strnicmp(data, "ro", 2) == 0) {
/* ignore */
@@ -1362,7 +1487,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->server_ino = 1;
} else if (strnicmp(data, "noserverino", 9) == 0) {
vol->server_ino = 0;
- } else if (strnicmp(data, "rwpidforward", 4) == 0) {
+ } else if (strnicmp(data, "rwpidforward", 12) == 0) {
vol->rwpidforward = 1;
} else if (strnicmp(data, "cifsacl", 7) == 0) {
vol->cifs_acl = 1;
@@ -1403,6 +1528,22 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->mfsymlinks = true;
} else if (strnicmp(data, "multiuser", 8) == 0) {
vol->multiuser = true;
+ } else if (!strnicmp(data, "backupuid", 9) && value && *value) {
+ err = kstrtouint(value, 0, &vol->backupuid);
+ if (err < 0) {
+ cERROR(1, "%s: Invalid backupuid value",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->backupuid_specified = true;
+ } else if (!strnicmp(data, "backupgid", 9) && value && *value) {
+ err = kstrtouint(value, 0, &vol->backupgid);
+ if (err < 0) {
+ cERROR(1, "%s: Invalid backupgid value",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->backupgid_specified = true;
} else
printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
data);
@@ -1979,7 +2120,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
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.1");
+ "ntlmv2 in kernel release 3.2");
}
ses->overrideSecFlg = volume_info->secFlg;
@@ -2170,16 +2311,16 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
(new->mnt_cifs_flags & CIFS_MOUNT_MASK))
return 0;
- if (old->rsize != new->rsize)
- return 0;
-
/*
- * We want to share sb only if we don't specify wsize or specified wsize
- * is greater or equal than existing one.
+ * We want to share sb only if we don't specify an r/wsize or
+ * specified r/wsize is greater than or equal to existing one.
*/
if (new->wsize && new->wsize < old->wsize)
return 0;
+ if (new->rsize && new->rsize < old->rsize)
+ return 0;
+
if (old->mnt_uid != new->mnt_uid || old->mnt_gid != new->mnt_gid)
return 0;
@@ -2617,14 +2758,6 @@ void reset_cifs_unix_caps(int xid, struct cifs_tcon *tcon,
CIFS_MOUNT_POSIX_PATHS;
}
- if (cifs_sb && (cifs_sb->rsize > 127 * 1024)) {
- if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) {
- cifs_sb->rsize = 127 * 1024;
- cFYI(DBG2, "larger reads not supported by srv");
- }
- }
-
-
cFYI(1, "Negotiate caps 0x%x", (int)cap);
#ifdef CONFIG_CIFS_DEBUG2
if (cap & CIFS_UNIX_FCNTL_CAP)
@@ -2669,31 +2802,19 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
spin_lock_init(&cifs_sb->tlink_tree_lock);
cifs_sb->tlink_tree = RB_ROOT;
- if (pvolume_info->rsize > CIFSMaxBufSize) {
- cERROR(1, "rsize %d too large, using MaxBufSize",
- pvolume_info->rsize);
- cifs_sb->rsize = CIFSMaxBufSize;
- } else if ((pvolume_info->rsize) &&
- (pvolume_info->rsize <= CIFSMaxBufSize))
- cifs_sb->rsize = pvolume_info->rsize;
- else /* default */
- cifs_sb->rsize = CIFSMaxBufSize;
-
- if (cifs_sb->rsize < 2048) {
- cifs_sb->rsize = 2048;
- /* Windows ME may prefer this */
- cFYI(1, "readsize set to minimum: 2048");
- }
-
/*
- * Temporarily set wsize for matching superblock. If we end up using
- * new sb then cifs_negotiate_wsize will later negotiate it downward
- * if needed.
+ * Temporarily set r/wsize for matching superblock. If we end up using
+ * new sb then client will later negotiate it downward if needed.
*/
+ cifs_sb->rsize = pvolume_info->rsize;
cifs_sb->wsize = pvolume_info->wsize;
cifs_sb->mnt_uid = pvolume_info->linux_uid;
cifs_sb->mnt_gid = pvolume_info->linux_gid;
+ if (pvolume_info->backupuid_specified)
+ cifs_sb->mnt_backupuid = pvolume_info->backupuid;
+ if (pvolume_info->backupgid_specified)
+ cifs_sb->mnt_backupgid = pvolume_info->backupgid;
cifs_sb->mnt_file_mode = pvolume_info->file_mode;
cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
cFYI(1, "file mode: 0x%x dir mode: 0x%x",
@@ -2724,6 +2845,10 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD;
if (pvolume_info->cifs_acl)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
+ if (pvolume_info->backupuid_specified)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID;
+ if (pvolume_info->backupgid_specified)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID;
if (pvolume_info->override_uid)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
if (pvolume_info->override_gid)
@@ -2756,29 +2881,41 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
}
/*
- * When the server supports very large writes via POSIX extensions, we can
- * allow up to 2^24-1, minus the size of a WRITE_AND_X header, not including
- * the RFC1001 length.
+ * When the server supports very large reads and writes via POSIX extensions,
+ * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not
+ * including the RFC1001 length.
*
* Note that this might make for "interesting" allocation problems during
* writeback however as we have to allocate an array of pointers for the
* pages. A 16M write means ~32kb page array with PAGE_CACHE_SIZE == 4096.
+ *
+ * For reads, there is a similar problem as we need to allocate an array
+ * of kvecs to handle the receive, though that should only need to be done
+ * once.
*/
#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4)
+#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4)
/*
- * When the server doesn't allow large posix writes, only allow a wsize of
- * 128k minus the size of the WRITE_AND_X header. That allows for a write up
- * to the maximum size described by RFC1002.
+ * When the server doesn't allow large posix writes, only allow a rsize/wsize
+ * of 2^17-1 minus the size of the call header. That allows for a read or
+ * write up to the maximum size described by RFC1002.
*/
-#define CIFS_MAX_RFC1002_WSIZE (128 * 1024 - sizeof(WRITE_REQ) + 4)
+#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
+#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
/*
* The default wsize is 1M. find_get_pages seems to return a maximum of 256
* pages in a single call. With PAGE_CACHE_SIZE == 4k, this means we can fill
* a single wsize request with a single call.
*/
-#define CIFS_DEFAULT_WSIZE (1024 * 1024)
+#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
+
+/*
+ * Windows only supports a max of 60k reads. Default to that when posix
+ * extensions aren't in force.
+ */
+#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024)
static unsigned int
cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
@@ -2786,7 +2923,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
struct TCP_Server_Info *server = tcon->ses->server;
unsigned int wsize = pvolume_info->wsize ? pvolume_info->wsize :
- CIFS_DEFAULT_WSIZE;
+ CIFS_DEFAULT_IOSIZE;
/* can server support 24-bit write sizes? (via UNIX extensions) */
if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
@@ -2809,6 +2946,50 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
return wsize;
}
+static unsigned int
+cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
+{
+ __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int rsize, defsize;
+
+ /*
+ * Set default value...
+ *
+ * HACK alert! Ancient servers have very small buffers. Even though
+ * MS-CIFS indicates that servers are only limited by the client's
+ * bufsize for reads, testing against win98se shows that it throws
+ * INVALID_PARAMETER errors if you try to request too large a read.
+ *
+ * If the server advertises a MaxBufferSize of less than one page,
+ * assume that it also can't satisfy reads larger than that either.
+ *
+ * FIXME: Is there a better heuristic for this?
+ */
+ if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP))
+ defsize = CIFS_DEFAULT_IOSIZE;
+ else if (server->capabilities & CAP_LARGE_READ_X)
+ defsize = CIFS_DEFAULT_NON_POSIX_RSIZE;
+ else if (server->maxBuf >= PAGE_CACHE_SIZE)
+ defsize = CIFSMaxBufSize;
+ else
+ defsize = server->maxBuf - sizeof(READ_RSP);
+
+ rsize = pvolume_info->rsize ? pvolume_info->rsize : defsize;
+
+ /*
+ * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
+ * the client's MaxBufferSize.
+ */
+ if (!(server->capabilities & CAP_LARGE_READ_X))
+ rsize = min_t(unsigned int, CIFSMaxBufSize, rsize);
+
+ /* hard limit of CIFS_MAX_RSIZE */
+ rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE);
+
+ return rsize;
+}
+
static int
is_path_accessible(int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
@@ -2838,8 +3019,9 @@ 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->UNCip);
kfree(volume_info->domainname);
kfree(volume_info->iocharset);
kfree(volume_info->prepath);
@@ -3001,6 +3183,22 @@ cifs_get_volume_info(char *mount_data, const char *devname)
return volume_info;
}
+/* make sure ra_pages is a multiple of rsize */
+static inline unsigned int
+cifs_ra_pages(struct cifs_sb_info *cifs_sb)
+{
+ unsigned int reads;
+ unsigned int rsize_pages = cifs_sb->rsize / PAGE_CACHE_SIZE;
+
+ if (rsize_pages >= default_backing_dev_info.ra_pages)
+ return default_backing_dev_info.ra_pages;
+ else if (rsize_pages == 0)
+ return rsize_pages;
+
+ reads = default_backing_dev_info.ra_pages / rsize_pages;
+ return reads * rsize_pages;
+}
+
int
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
{
@@ -3019,8 +3217,6 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
if (rc)
return rc;
- cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages;
-
#ifdef CONFIG_CIFS_DFS_UPCALL
try_mount_again:
/* cleanup activities if we're chasing a referral */
@@ -3085,15 +3281,11 @@ try_mount_again:
CIFSSMBQFSAttributeInfo(xid, tcon);
}
- if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
- cifs_sb->rsize = 1024 * 127;
- cFYI(DBG2, "no very large read support, rsize now 127K");
- }
- if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
- cifs_sb->rsize = min(cifs_sb->rsize,
- (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
-
cifs_sb->wsize = cifs_negotiate_wsize(tcon, volume_info);
+ cifs_sb->rsize = cifs_negotiate_rsize(tcon, volume_info);
+
+ /* tune readahead according to rsize */
+ cifs_sb->bdi.ra_pages = cifs_ra_pages(cifs_sb);
remote_path_check:
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -3193,15 +3385,9 @@ mount_fail_check:
else
cifs_put_tcp_session(srvTcp);
bdi_destroy(&cifs_sb->bdi);
- goto out;
}
- /* volume_info->password is freed above when existing session found
- (in which case it is not needed anymore) but when new sesion is created
- the password ptr is put in the new session structure (in which case the
- password will be freed at unmount time) */
out:
- /* zero out password before freeing */
FreeXid(xid);
return rc;
}
@@ -3267,7 +3453,7 @@ CIFSTCon(unsigned int xid, struct cifs_ses *ses,
else
#endif /* CIFS_WEAK_PW_HASH */
rc = SMBNTencrypt(tcon->password, ses->server->cryptkey,
- bcc_ptr);
+ bcc_ptr, nls_codepage);
bcc_ptr += CIFS_AUTH_RESP_SIZE;
if (ses->capabilities & CAP_UNICODE) {
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 14d602f178c..d7eeb9d3ed6 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -57,11 +57,6 @@ build_path_from_dentry(struct dentry *direntry)
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
unsigned seq;
- if (direntry == NULL)
- return NULL; /* not much we can do if dentry is freed and
- we need to reopen the file after it was closed implicitly
- when the server crashed */
-
dirsep = CIFS_DIR_SEP(cifs_sb);
if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
@@ -110,8 +105,8 @@ cifs_bp_rename_retry:
}
rcu_read_unlock();
if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
- cERROR(1, "did not end path lookup where expected namelen is %d",
- namelen);
+ cFYI(1, "did not end path lookup where expected. namelen=%d "
+ "dfsplen=%d", namelen, dfsplen);
/* presumably this is only possible if racing with a rename
of one of the parent directories (we can not lock the dentries
above us to prevent this, but retrying should be harmless) */
@@ -176,7 +171,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
}
tcon = tlink_tcon(tlink);
- if (oplockEnabled)
+ if (enable_oplocks)
oplock = REQ_OPLOCK;
if (nd)
@@ -249,6 +244,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
create_options |= CREATE_OPTION_READONLY;
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
if (tcon->ses->capabilities & CAP_NT_SMBS)
rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
desiredAccess, create_options,
@@ -362,6 +360,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
{
int rc = -EPERM;
int xid;
+ int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
@@ -436,9 +435,11 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
return rc;
}
- /* FIXME: would WRITE_OWNER | WRITE_DAC be better? */
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE,
- GENERIC_WRITE, CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
+ GENERIC_WRITE, create_options,
&fileHandle, &oplock, buf, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc)
@@ -641,14 +642,22 @@ lookup_out:
static int
cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
{
- if (nd->flags & LOOKUP_RCU)
+ if (nd && (nd->flags & LOOKUP_RCU))
return -ECHILD;
if (direntry->d_inode) {
if (cifs_revalidate_dentry(direntry))
return 0;
- else
+ else {
+ /*
+ * Forcibly invalidate automounting directory inodes
+ * (remote DFS directories) so to have them
+ * instantiated again for automount
+ */
+ if (IS_AUTOMOUNT(direntry->d_inode))
+ return 0;
return 1;
+ }
}
/*
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index 548f06230a6..1d2d91d9bf6 100644
--- a/fs/cifs/dns_resolve.c
+++ b/fs/cifs/dns_resolve.c
@@ -79,8 +79,8 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
/* Perform the upcall */
rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL);
if (rc < 0)
- cERROR(1, "%s: unable to resolve: %*.*s",
- __func__, len, len, hostname);
+ cFYI(1, "%s: unable to resolve: %*.*s",
+ __func__, len, len, hostname);
else
cFYI(1, "%s: resolved: %*.*s to %s",
__func__, len, len, hostname, *ip_addr);
diff --git a/fs/cifs/export.c b/fs/cifs/export.c
index 55d87ac5200..9c7ecdccf2f 100644
--- a/fs/cifs/export.c
+++ b/fs/cifs/export.c
@@ -45,7 +45,7 @@
#include "cifs_debug.h"
#include "cifsfs.h"
-#ifdef CIFS_NFSD_EXPORT
+#ifdef CONFIG_CIFS_NFSD_EXPORT
static struct dentry *cifs_get_parent(struct dentry *dentry)
{
/* BB need to add code here eventually to enable export via NFSD */
@@ -63,5 +63,5 @@ const struct export_operations cifs_export_ops = {
.encode_fs = */
};
-#endif /* CIFS_NFSD_EXPORT */
+#endif /* CONFIG_CIFS_NFSD_EXPORT */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 378acdafa35..cf0b1539b32 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -32,6 +32,7 @@
#include <linux/delay.h>
#include <linux/mount.h>
#include <linux/slab.h>
+#include <linux/swap.h>
#include <asm/div64.h>
#include "cifsfs.h"
#include "cifspdu.h"
@@ -174,6 +175,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
int rc;
int desiredAccess;
int disposition;
+ int create_options = CREATE_NOT_DIR;
FILE_ALL_INFO *buf;
desiredAccess = cifs_convert_flags(f_flags);
@@ -210,9 +212,12 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
if (!buf)
return -ENOMEM;
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
if (tcon->ses->capabilities & CAP_NT_SMBS)
rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
- desiredAccess, CREATE_NOT_DIR, pnetfid, poplock, buf,
+ desiredAccess, create_options, pnetfid, poplock, buf,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
& CIFS_MOUNT_MAP_SPECIAL_CHR);
else
@@ -258,8 +263,6 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file,
pCifsFile->invalidHandle = false;
pCifsFile->tlink = cifs_get_tlink(tlink);
mutex_init(&pCifsFile->fh_mutex);
- mutex_init(&pCifsFile->lock_mutex);
- INIT_LIST_HEAD(&pCifsFile->llist);
INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
spin_lock(&cifs_file_list_lock);
@@ -272,11 +275,14 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file,
spin_unlock(&cifs_file_list_lock);
cifs_set_oplock_level(pCifsInode, oplock);
+ pCifsInode->can_cache_brlcks = pCifsInode->clientCanCacheAll;
file->private_data = pCifsFile;
return pCifsFile;
}
+static void cifs_del_lock_waiters(struct cifsLockInfo *lock);
+
/*
* Release a reference on the file private data. This may involve closing
* the filehandle out on the server. Must be called without holding
@@ -314,6 +320,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
}
spin_unlock(&cifs_file_list_lock);
+ cancel_work_sync(&cifs_file->oplock_break);
+
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
int xid, rc;
@@ -325,12 +333,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
/* Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
*/
- mutex_lock(&cifs_file->lock_mutex);
- list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) {
+ mutex_lock(&cifsi->lock_mutex);
+ list_for_each_entry_safe(li, tmp, &cifsi->llist, llist) {
+ if (li->netfid != cifs_file->netfid)
+ continue;
list_del(&li->llist);
+ cifs_del_lock_waiters(li);
kfree(li);
}
- mutex_unlock(&cifs_file->lock_mutex);
+ mutex_unlock(&cifsi->lock_mutex);
cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry);
@@ -369,7 +380,7 @@ int cifs_open(struct inode *inode, struct file *file)
cFYI(1, "inode = 0x%p file flags are 0x%x for %s",
inode, file->f_flags, full_path);
- if (oplockEnabled)
+ if (enable_oplocks)
oplock = REQ_OPLOCK;
else
oplock = 0;
@@ -463,6 +474,7 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
char *full_path = NULL;
int desiredAccess;
int disposition = FILE_OPEN;
+ int create_options = CREATE_NOT_DIR;
__u16 netfid;
xid = GetXid();
@@ -493,7 +505,7 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
cFYI(1, "inode = 0x%p file flags 0x%x for %s",
inode, pCifsFile->f_flags, full_path);
- if (oplockEnabled)
+ if (enable_oplocks)
oplock = REQ_OPLOCK;
else
oplock = 0;
@@ -522,6 +534,9 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
desiredAccess = cifs_convert_flags(pCifsFile->f_flags);
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
/* Can not refresh inode by passing in file_info buf to be returned
by SMBOpen and then calling get_inode_info with returned buf
since file might have write behind data that needs to be flushed
@@ -529,7 +544,7 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
that inode was not dirty locally we could do this */
rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess,
- CREATE_NOT_DIR, &netfid, &oplock, NULL,
+ create_options, &netfid, &oplock, NULL,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) {
@@ -629,219 +644,687 @@ int cifs_closedir(struct inode *inode, struct file *file)
return rc;
}
-static int store_file_lock(struct cifsFileInfo *fid, __u64 len,
- __u64 offset, __u8 lockType)
+static struct cifsLockInfo *
+cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 netfid)
{
- struct cifsLockInfo *li =
+ struct cifsLockInfo *lock =
kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
- if (li == NULL)
- return -ENOMEM;
- li->offset = offset;
- li->length = len;
- li->type = lockType;
- mutex_lock(&fid->lock_mutex);
- list_add(&li->llist, &fid->llist);
- mutex_unlock(&fid->lock_mutex);
- return 0;
+ if (!lock)
+ return lock;
+ lock->offset = offset;
+ lock->length = length;
+ lock->type = type;
+ lock->netfid = netfid;
+ lock->pid = current->tgid;
+ INIT_LIST_HEAD(&lock->blist);
+ init_waitqueue_head(&lock->block_q);
+ return lock;
}
-int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
+static void
+cifs_del_lock_waiters(struct cifsLockInfo *lock)
{
- int rc, xid;
- __u32 numLock = 0;
- __u32 numUnlock = 0;
- __u64 length;
- bool wait_flag = false;
- struct cifs_sb_info *cifs_sb;
+ struct cifsLockInfo *li, *tmp;
+ list_for_each_entry_safe(li, tmp, &lock->blist, blist) {
+ list_del_init(&li->blist);
+ wake_up(&li->block_q);
+ }
+}
+
+static bool
+__cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset,
+ __u64 length, __u8 type, __u16 netfid,
+ struct cifsLockInfo **conf_lock)
+{
+ struct cifsLockInfo *li, *tmp;
+
+ list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
+ if (offset + length <= li->offset ||
+ offset >= li->offset + li->length)
+ continue;
+ else if ((type & LOCKING_ANDX_SHARED_LOCK) &&
+ ((netfid == li->netfid && current->tgid == li->pid) ||
+ type == li->type))
+ continue;
+ else {
+ *conf_lock = li;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+cifs_find_lock_conflict(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock,
+ struct cifsLockInfo **conf_lock)
+{
+ return __cifs_find_lock_conflict(cinode, lock->offset, lock->length,
+ lock->type, lock->netfid, conf_lock);
+}
+
+static int
+cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length,
+ __u8 type, __u16 netfid, struct file_lock *flock)
+{
+ int rc = 0;
+ struct cifsLockInfo *conf_lock;
+ bool exist;
+
+ mutex_lock(&cinode->lock_mutex);
+
+ exist = __cifs_find_lock_conflict(cinode, offset, length, type, netfid,
+ &conf_lock);
+ if (exist) {
+ flock->fl_start = conf_lock->offset;
+ flock->fl_end = conf_lock->offset + conf_lock->length - 1;
+ flock->fl_pid = conf_lock->pid;
+ if (conf_lock->type & LOCKING_ANDX_SHARED_LOCK)
+ flock->fl_type = F_RDLCK;
+ else
+ flock->fl_type = F_WRLCK;
+ } else if (!cinode->can_cache_brlcks)
+ rc = 1;
+ else
+ flock->fl_type = F_UNLCK;
+
+ mutex_unlock(&cinode->lock_mutex);
+ return rc;
+}
+
+static void
+cifs_lock_add(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock)
+{
+ mutex_lock(&cinode->lock_mutex);
+ list_add_tail(&lock->llist, &cinode->llist);
+ mutex_unlock(&cinode->lock_mutex);
+}
+
+static int
+cifs_lock_add_if(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock,
+ bool wait)
+{
+ struct cifsLockInfo *conf_lock;
+ bool exist;
+ int rc = 0;
+
+try_again:
+ exist = false;
+ mutex_lock(&cinode->lock_mutex);
+
+ exist = cifs_find_lock_conflict(cinode, lock, &conf_lock);
+ if (!exist && cinode->can_cache_brlcks) {
+ list_add_tail(&lock->llist, &cinode->llist);
+ mutex_unlock(&cinode->lock_mutex);
+ return rc;
+ }
+
+ if (!exist)
+ rc = 1;
+ else if (!wait)
+ rc = -EACCES;
+ else {
+ list_add_tail(&lock->blist, &conf_lock->blist);
+ mutex_unlock(&cinode->lock_mutex);
+ rc = wait_event_interruptible(lock->block_q,
+ (lock->blist.prev == &lock->blist) &&
+ (lock->blist.next == &lock->blist));
+ if (!rc)
+ goto try_again;
+ mutex_lock(&cinode->lock_mutex);
+ list_del_init(&lock->blist);
+ }
+
+ mutex_unlock(&cinode->lock_mutex);
+ return rc;
+}
+
+static int
+cifs_posix_lock_test(struct file *file, struct file_lock *flock)
+{
+ int rc = 0;
+ struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode);
+ unsigned char saved_type = flock->fl_type;
+
+ if ((flock->fl_flags & FL_POSIX) == 0)
+ return 1;
+
+ mutex_lock(&cinode->lock_mutex);
+ posix_test_lock(file, flock);
+
+ if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) {
+ flock->fl_type = saved_type;
+ rc = 1;
+ }
+
+ mutex_unlock(&cinode->lock_mutex);
+ return rc;
+}
+
+static int
+cifs_posix_lock_set(struct file *file, struct file_lock *flock)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode);
+ int rc = 1;
+
+ if ((flock->fl_flags & FL_POSIX) == 0)
+ return rc;
+
+ mutex_lock(&cinode->lock_mutex);
+ if (!cinode->can_cache_brlcks) {
+ mutex_unlock(&cinode->lock_mutex);
+ return rc;
+ }
+ rc = posix_lock_file_wait(file, flock);
+ mutex_unlock(&cinode->lock_mutex);
+ return rc;
+}
+
+static int
+cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
+{
+ int xid, rc = 0, stored_rc;
+ struct cifsLockInfo *li, *tmp;
struct cifs_tcon *tcon;
- __u16 netfid;
- __u8 lockType = LOCKING_ANDX_LARGE_FILES;
- bool posix_locking = 0;
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ unsigned int num, max_num;
+ LOCKING_ANDX_RANGE *buf, *cur;
+ int types[] = {LOCKING_ANDX_LARGE_FILES,
+ LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES};
+ int i;
+
+ xid = GetXid();
+ tcon = tlink_tcon(cfile->tlink);
+
+ mutex_lock(&cinode->lock_mutex);
+ if (!cinode->can_cache_brlcks) {
+ mutex_unlock(&cinode->lock_mutex);
+ FreeXid(xid);
+ return rc;
+ }
+
+ max_num = (tcon->ses->server->maxBuf - sizeof(struct smb_hdr)) /
+ sizeof(LOCKING_ANDX_RANGE);
+ buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
+ if (!buf) {
+ mutex_unlock(&cinode->lock_mutex);
+ FreeXid(xid);
+ return rc;
+ }
+
+ for (i = 0; i < 2; i++) {
+ cur = buf;
+ num = 0;
+ list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
+ if (li->type != types[i])
+ continue;
+ cur->Pid = cpu_to_le16(li->pid);
+ cur->LengthLow = cpu_to_le32((u32)li->length);
+ cur->LengthHigh = cpu_to_le32((u32)(li->length>>32));
+ cur->OffsetLow = cpu_to_le32((u32)li->offset);
+ cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32));
+ if (++num == max_num) {
+ stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ li->type, 0, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+
+ if (num) {
+ stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ types[i], 0, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+ }
+
+ cinode->can_cache_brlcks = false;
+ mutex_unlock(&cinode->lock_mutex);
+
+ kfree(buf);
+ FreeXid(xid);
+ return rc;
+}
+
+/* copied from fs/locks.c with a name change */
+#define cifs_for_each_lock(inode, lockp) \
+ for (lockp = &inode->i_flock; *lockp != NULL; \
+ lockp = &(*lockp)->fl_next)
+
+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;
+ struct cifsLockInfo *lck, *tmp;
+ int rc = 0, xid, type;
+ __u64 length;
+ struct list_head locks_to_send;
- length = 1 + pfLock->fl_end - pfLock->fl_start;
- rc = -EACCES;
xid = GetXid();
- cFYI(1, "Lock parm: 0x%x flockflags: "
- "0x%x flocktype: 0x%x start: %lld end: %lld",
- cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start,
- pfLock->fl_end);
+ mutex_lock(&cinode->lock_mutex);
+ if (!cinode->can_cache_brlcks) {
+ mutex_unlock(&cinode->lock_mutex);
+ FreeXid(xid);
+ return rc;
+ }
+
+ INIT_LIST_HEAD(&locks_to_send);
+
+ lock_flocks();
+ cifs_for_each_lock(cfile->dentry->d_inode, before) {
+ flock = *before;
+ length = 1 + flock->fl_end - flock->fl_start;
+ if (flock->fl_type == F_RDLCK || flock->fl_type == F_SHLCK)
+ type = CIFS_RDLCK;
+ else
+ type = CIFS_WRLCK;
+
+ lck = cifs_lock_init(flock->fl_start, length, type,
+ cfile->netfid);
+ if (!lck) {
+ rc = -ENOMEM;
+ goto send_locks;
+ }
+ lck->pid = flock->fl_pid;
+
+ list_add_tail(&lck->llist, &locks_to_send);
+ }
+
+send_locks:
+ unlock_flocks();
+
+ list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) {
+ struct file_lock tmp_lock;
+ int stored_rc;
+
+ tmp_lock.fl_start = lck->offset;
+ stored_rc = CIFSSMBPosixLock(xid, tcon, lck->netfid, lck->pid,
+ 0, lck->length, &tmp_lock,
+ lck->type, 0);
+ if (stored_rc)
+ rc = stored_rc;
+ list_del(&lck->llist);
+ kfree(lck);
+ }
+
+ cinode->can_cache_brlcks = false;
+ mutex_unlock(&cinode->lock_mutex);
+
+ FreeXid(xid);
+ return rc;
+}
- if (pfLock->fl_flags & FL_POSIX)
+static int
+cifs_push_locks(struct cifsFileInfo *cfile)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+
+ if ((tcon->ses->capabilities & CAP_UNIX) &&
+ (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);
+
+ return cifs_push_mandatory_locks(cfile);
+}
+
+static void
+cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock,
+ bool *wait_flag)
+{
+ if (flock->fl_flags & FL_POSIX)
cFYI(1, "Posix");
- if (pfLock->fl_flags & FL_FLOCK)
+ if (flock->fl_flags & FL_FLOCK)
cFYI(1, "Flock");
- if (pfLock->fl_flags & FL_SLEEP) {
+ if (flock->fl_flags & FL_SLEEP) {
cFYI(1, "Blocking lock");
- wait_flag = true;
+ *wait_flag = true;
}
- if (pfLock->fl_flags & FL_ACCESS)
+ if (flock->fl_flags & FL_ACCESS)
cFYI(1, "Process suspended by mandatory locking - "
- "not implemented yet");
- if (pfLock->fl_flags & FL_LEASE)
+ "not implemented yet");
+ if (flock->fl_flags & FL_LEASE)
cFYI(1, "Lease on file - not implemented yet");
- if (pfLock->fl_flags &
+ if (flock->fl_flags &
(~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE)))
- cFYI(1, "Unknown lock flags 0x%x", pfLock->fl_flags);
+ cFYI(1, "Unknown lock flags 0x%x", flock->fl_flags);
- if (pfLock->fl_type == F_WRLCK) {
+ *type = LOCKING_ANDX_LARGE_FILES;
+ if (flock->fl_type == F_WRLCK) {
cFYI(1, "F_WRLCK ");
- numLock = 1;
- } else if (pfLock->fl_type == F_UNLCK) {
+ *lock = 1;
+ } else if (flock->fl_type == F_UNLCK) {
cFYI(1, "F_UNLCK");
- numUnlock = 1;
- /* Check if unlock includes more than
- one lock range */
- } else if (pfLock->fl_type == F_RDLCK) {
+ *unlock = 1;
+ /* Check if unlock includes more than one lock range */
+ } else if (flock->fl_type == F_RDLCK) {
cFYI(1, "F_RDLCK");
- lockType |= LOCKING_ANDX_SHARED_LOCK;
- numLock = 1;
- } else if (pfLock->fl_type == F_EXLCK) {
+ *type |= LOCKING_ANDX_SHARED_LOCK;
+ *lock = 1;
+ } else if (flock->fl_type == F_EXLCK) {
cFYI(1, "F_EXLCK");
- numLock = 1;
- } else if (pfLock->fl_type == F_SHLCK) {
+ *lock = 1;
+ } else if (flock->fl_type == F_SHLCK) {
cFYI(1, "F_SHLCK");
- lockType |= LOCKING_ANDX_SHARED_LOCK;
- numLock = 1;
+ *type |= LOCKING_ANDX_SHARED_LOCK;
+ *lock = 1;
} else
cFYI(1, "Unknown type of lock");
+}
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
- tcon = tlink_tcon(((struct cifsFileInfo *)file->private_data)->tlink);
- netfid = ((struct cifsFileInfo *)file->private_data)->netfid;
+static int
+cifs_getlk(struct file *file, struct file_lock *flock, __u8 type,
+ bool wait_flag, bool posix_lck, int xid)
+{
+ int rc = 0;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ __u16 netfid = cfile->netfid;
- if ((tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
- ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
- posix_locking = 1;
- /* BB add code here to normalize offset and length to
- account for negative length which we can not accept over the
- wire */
- if (IS_GETLK(cmd)) {
- if (posix_locking) {
- int posix_lock_type;
- if (lockType & LOCKING_ANDX_SHARED_LOCK)
- posix_lock_type = CIFS_RDLCK;
- else
- posix_lock_type = CIFS_WRLCK;
- rc = CIFSSMBPosixLock(xid, tcon, netfid, 1 /* get */,
- length, pfLock, posix_lock_type,
- wait_flag);
- FreeXid(xid);
+ if (posix_lck) {
+ int posix_lock_type;
+
+ rc = cifs_posix_lock_test(file, flock);
+ if (!rc)
return rc;
- }
- /* BB we could chain these into one lock request BB */
- rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start,
- 0, 1, lockType, 0 /* wait flag */, 0);
- if (rc == 0) {
- rc = CIFSSMBLock(xid, tcon, netfid, length,
- pfLock->fl_start, 1 /* numUnlock */ ,
- 0 /* numLock */ , lockType,
- 0 /* wait flag */, 0);
- pfLock->fl_type = F_UNLCK;
- if (rc != 0)
- cERROR(1, "Error unlocking previously locked "
- "range %d during test of lock", rc);
- rc = 0;
+ if (type & LOCKING_ANDX_SHARED_LOCK)
+ posix_lock_type = CIFS_RDLCK;
+ else
+ posix_lock_type = CIFS_WRLCK;
+ rc = CIFSSMBPosixLock(xid, tcon, netfid, current->tgid,
+ 1 /* get */, length, flock,
+ posix_lock_type, wait_flag);
+ return rc;
+ }
- } else {
- /* if rc == ERR_SHARING_VIOLATION ? */
- rc = 0;
+ rc = cifs_lock_test(cinode, flock->fl_start, length, type, netfid,
+ flock);
+ if (!rc)
+ return rc;
+
+ /* BB we could chain these into one lock request BB */
+ rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
+ flock->fl_start, 0, 1, type, 0, 0);
+ if (rc == 0) {
+ rc = CIFSSMBLock(xid, tcon, netfid, current->tgid,
+ length, flock->fl_start, 1, 0,
+ type, 0, 0);
+ flock->fl_type = F_UNLCK;
+ if (rc != 0)
+ cERROR(1, "Error unlocking previously locked "
+ "range %d during test of lock", rc);
+ return 0;
+ }
+
+ if (type & LOCKING_ANDX_SHARED_LOCK) {
+ flock->fl_type = F_WRLCK;
+ return 0;
+ }
+
+ rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
+ flock->fl_start, 0, 1,
+ type | LOCKING_ANDX_SHARED_LOCK, 0, 0);
+ if (rc == 0) {
+ rc = CIFSSMBLock(xid, tcon, netfid, current->tgid,
+ length, flock->fl_start, 1, 0,
+ type | LOCKING_ANDX_SHARED_LOCK,
+ 0, 0);
+ flock->fl_type = F_RDLCK;
+ if (rc != 0)
+ cERROR(1, "Error unlocking previously locked "
+ "range %d during test of lock", rc);
+ } else
+ flock->fl_type = F_WRLCK;
+
+ return 0;
+}
+
+static void
+cifs_move_llist(struct list_head *source, struct list_head *dest)
+{
+ struct list_head *li, *tmp;
+ list_for_each_safe(li, tmp, source)
+ list_move(li, dest);
+}
+
+static void
+cifs_free_llist(struct list_head *llist)
+{
+ struct cifsLockInfo *li, *tmp;
+ list_for_each_entry_safe(li, tmp, llist, llist) {
+ cifs_del_lock_waiters(li);
+ list_del(&li->llist);
+ kfree(li);
+ }
+}
+
+static int
+cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
+{
+ int rc = 0, stored_rc;
+ int types[] = {LOCKING_ANDX_LARGE_FILES,
+ LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES};
+ unsigned int i;
+ unsigned int max_num, num;
+ LOCKING_ANDX_RANGE *buf, *cur;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ struct cifsLockInfo *li, *tmp;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct list_head tmp_llist;
+
+ INIT_LIST_HEAD(&tmp_llist);
- if (lockType & LOCKING_ANDX_SHARED_LOCK) {
- pfLock->fl_type = F_WRLCK;
+ max_num = (tcon->ses->server->maxBuf - sizeof(struct smb_hdr)) /
+ sizeof(LOCKING_ANDX_RANGE);
+ buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&cinode->lock_mutex);
+ for (i = 0; i < 2; i++) {
+ cur = buf;
+ num = 0;
+ list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
+ if (flock->fl_start > li->offset ||
+ (flock->fl_start + length) <
+ (li->offset + li->length))
+ continue;
+ if (current->tgid != li->pid)
+ continue;
+ if (cfile->netfid != li->netfid)
+ continue;
+ if (types[i] != li->type)
+ continue;
+ if (!cinode->can_cache_brlcks) {
+ cur->Pid = cpu_to_le16(li->pid);
+ cur->LengthLow = cpu_to_le32((u32)li->length);
+ cur->LengthHigh =
+ cpu_to_le32((u32)(li->length>>32));
+ cur->OffsetLow = cpu_to_le32((u32)li->offset);
+ cur->OffsetHigh =
+ cpu_to_le32((u32)(li->offset>>32));
+ /*
+ * We need to save a lock here to let us add
+ * it again to the inode list if the unlock
+ * range request fails on the server.
+ */
+ list_move(&li->llist, &tmp_llist);
+ if (++num == max_num) {
+ stored_rc = cifs_lockv(xid, tcon,
+ cfile->netfid,
+ li->type, num,
+ 0, buf);
+ if (stored_rc) {
+ /*
+ * We failed on the unlock range
+ * request - add all locks from
+ * the tmp list to the head of
+ * the inode list.
+ */
+ cifs_move_llist(&tmp_llist,
+ &cinode->llist);
+ rc = stored_rc;
+ } else
+ /*
+ * The unlock range request
+ * succeed - free the tmp list.
+ */
+ cifs_free_llist(&tmp_llist);
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
} else {
- rc = CIFSSMBLock(xid, tcon, netfid, length,
- pfLock->fl_start, 0, 1,
- lockType | LOCKING_ANDX_SHARED_LOCK,
- 0 /* wait flag */, 0);
- if (rc == 0) {
- rc = CIFSSMBLock(xid, tcon, netfid,
- length, pfLock->fl_start, 1, 0,
- lockType |
- LOCKING_ANDX_SHARED_LOCK,
- 0 /* wait flag */, 0);
- pfLock->fl_type = F_RDLCK;
- if (rc != 0)
- cERROR(1, "Error unlocking "
- "previously locked range %d "
- "during test of lock", rc);
- rc = 0;
- } else {
- pfLock->fl_type = F_WRLCK;
- rc = 0;
- }
+ /*
+ * We can cache brlock requests - simply remove
+ * a lock from the inode list.
+ */
+ list_del(&li->llist);
+ cifs_del_lock_waiters(li);
+ kfree(li);
}
}
-
- FreeXid(xid);
- return rc;
+ if (num) {
+ stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ types[i], num, 0, buf);
+ if (stored_rc) {
+ cifs_move_llist(&tmp_llist, &cinode->llist);
+ rc = stored_rc;
+ } else
+ cifs_free_llist(&tmp_llist);
+ }
}
- if (!numLock && !numUnlock) {
- /* if no lock or unlock then nothing
- to do since we do not know what it is */
- FreeXid(xid);
- return -EOPNOTSUPP;
- }
+ mutex_unlock(&cinode->lock_mutex);
+ kfree(buf);
+ return rc;
+}
- if (posix_locking) {
+static int
+cifs_setlk(struct file *file, struct file_lock *flock, __u8 type,
+ bool wait_flag, bool posix_lck, int lock, int unlock, int xid)
+{
+ int rc = 0;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode);
+ __u16 netfid = cfile->netfid;
+
+ if (posix_lck) {
int posix_lock_type;
- if (lockType & LOCKING_ANDX_SHARED_LOCK)
+
+ rc = cifs_posix_lock_set(file, flock);
+ if (!rc || rc < 0)
+ return rc;
+
+ if (type & LOCKING_ANDX_SHARED_LOCK)
posix_lock_type = CIFS_RDLCK;
else
posix_lock_type = CIFS_WRLCK;
- if (numUnlock == 1)
+ if (unlock == 1)
posix_lock_type = CIFS_UNLCK;
- rc = CIFSSMBPosixLock(xid, tcon, netfid, 0 /* set */,
- length, pfLock, posix_lock_type,
- wait_flag);
- } else {
- struct cifsFileInfo *fid = file->private_data;
+ rc = CIFSSMBPosixLock(xid, tcon, netfid, current->tgid,
+ 0 /* set */, length, flock,
+ posix_lock_type, wait_flag);
+ goto out;
+ }
- if (numLock) {
- rc = CIFSSMBLock(xid, tcon, netfid, length,
- pfLock->fl_start, 0, numLock, lockType,
- wait_flag, 0);
+ if (lock) {
+ struct cifsLockInfo *lock;
- if (rc == 0) {
- /* For Windows locks we must store them. */
- rc = store_file_lock(fid, length,
- pfLock->fl_start, lockType);
- }
- } else if (numUnlock) {
- /* For each stored lock that this unlock overlaps
- completely, unlock it. */
- int stored_rc = 0;
- struct cifsLockInfo *li, *tmp;
+ lock = cifs_lock_init(flock->fl_start, length, type, netfid);
+ if (!lock)
+ return -ENOMEM;
- rc = 0;
- mutex_lock(&fid->lock_mutex);
- list_for_each_entry_safe(li, tmp, &fid->llist, llist) {
- if (pfLock->fl_start <= li->offset &&
- (pfLock->fl_start + length) >=
- (li->offset + li->length)) {
- stored_rc = CIFSSMBLock(xid, tcon,
- netfid, li->length,
- li->offset, 1, 0,
- li->type, false, 0);
- if (stored_rc)
- rc = stored_rc;
- else {
- list_del(&li->llist);
- kfree(li);
- }
- }
- }
- mutex_unlock(&fid->lock_mutex);
+ rc = cifs_lock_add_if(cinode, lock, wait_flag);
+ if (rc < 0)
+ kfree(lock);
+ if (rc <= 0)
+ goto out;
+
+ rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
+ flock->fl_start, 0, 1, type, wait_flag, 0);
+ if (rc) {
+ kfree(lock);
+ goto out;
}
+
+ cifs_lock_add(cinode, lock);
+ } else if (unlock)
+ rc = cifs_unlock_range(cfile, flock, xid);
+
+out:
+ if (flock->fl_flags & FL_POSIX)
+ posix_lock_file_wait(file, flock);
+ return rc;
+}
+
+int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
+{
+ int rc, xid;
+ int lock = 0, unlock = 0;
+ bool wait_flag = false;
+ bool posix_lck = false;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ struct cifsInodeInfo *cinode;
+ struct cifsFileInfo *cfile;
+ __u16 netfid;
+ __u8 type;
+
+ rc = -EACCES;
+ xid = GetXid();
+
+ cFYI(1, "Lock parm: 0x%x flockflags: 0x%x flocktype: 0x%x start: %lld "
+ "end: %lld", cmd, flock->fl_flags, flock->fl_type,
+ flock->fl_start, flock->fl_end);
+
+ cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag);
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ cfile = (struct cifsFileInfo *)file->private_data;
+ tcon = tlink_tcon(cfile->tlink);
+ netfid = cfile->netfid;
+ cinode = CIFS_I(file->f_path.dentry->d_inode);
+
+ if ((tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ posix_lck = true;
+ /*
+ * BB add code here to normalize offset and length to account for
+ * negative length which we can not accept over the wire.
+ */
+ if (IS_GETLK(cmd)) {
+ rc = cifs_getlk(file, flock, type, wait_flag, posix_lck, xid);
+ FreeXid(xid);
+ return rc;
}
- if (pfLock->fl_flags & FL_POSIX)
- posix_lock_file_wait(file, pfLock);
+ if (!lock && !unlock) {
+ /*
+ * if no lock or unlock then nothing to do since we do not
+ * know what it is
+ */
+ FreeXid(xid);
+ return -EOPNOTSUPP;
+ }
+
+ rc = cifs_setlk(file, flock, type, wait_flag, posix_lck, lock, unlock,
+ xid);
FreeXid(xid);
return rc;
}
@@ -1712,6 +2195,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
struct smb_com_read_rsp *pSMBr;
struct cifs_io_parms io_parms;
char *read_data;
+ unsigned int rsize;
__u32 pid;
if (!nr_segs)
@@ -1724,6 +2208,9 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ /* FIXME: set up handlers for larger reads and/or convert to async */
+ rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize);
+
open_file = file->private_data;
pTcon = tlink_tcon(open_file->tlink);
@@ -1736,7 +2223,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
cFYI(1, "attempting read on write only file instance");
for (total_read = 0; total_read < len; total_read += bytes_read) {
- cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);
+ cur_len = min_t(const size_t, len - total_read, rsize);
rc = -EAGAIN;
read_data = NULL;
@@ -1828,6 +2315,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
unsigned int bytes_read = 0;
unsigned int total_read;
unsigned int current_read_size;
+ unsigned int rsize;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *pTcon;
int xid;
@@ -1840,6 +2328,9 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ /* FIXME: set up handlers for larger reads and/or convert to async */
+ rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize);
+
if (file->private_data == NULL) {
rc = -EBADF;
FreeXid(xid);
@@ -1859,14 +2350,14 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
for (total_read = 0, current_offset = read_data;
read_size > total_read;
total_read += bytes_read, current_offset += bytes_read) {
- current_read_size = min_t(const int, read_size - total_read,
- cifs_sb->rsize);
+ current_read_size = min_t(uint, read_size - total_read, rsize);
+
/* For windows me and 9x we do not want to request more
than it negotiated since it will refuse the read then */
if ((pTcon->ses) &&
!(pTcon->ses->capabilities & CAP_LARGE_FILES)) {
- current_read_size = min_t(const int, current_read_size,
- pTcon->ses->server->maxBuf - 128);
+ current_read_size = min_t(uint, current_read_size,
+ CIFSMaxBufSize);
}
rc = -EAGAIN;
while (rc == -EAGAIN) {
@@ -1955,82 +2446,24 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
return rc;
}
-
-static void cifs_copy_cache_pages(struct address_space *mapping,
- struct list_head *pages, int bytes_read, char *data)
-{
- struct page *page;
- char *target;
-
- while (bytes_read > 0) {
- if (list_empty(pages))
- break;
-
- page = list_entry(pages->prev, struct page, lru);
- list_del(&page->lru);
-
- if (add_to_page_cache_lru(page, mapping, page->index,
- GFP_KERNEL)) {
- page_cache_release(page);
- cFYI(1, "Add page cache failed");
- data += PAGE_CACHE_SIZE;
- bytes_read -= PAGE_CACHE_SIZE;
- continue;
- }
- page_cache_release(page);
-
- target = kmap_atomic(page, KM_USER0);
-
- if (PAGE_CACHE_SIZE > bytes_read) {
- memcpy(target, data, bytes_read);
- /* zero the tail end of this partial page */
- memset(target + bytes_read, 0,
- PAGE_CACHE_SIZE - bytes_read);
- bytes_read = 0;
- } else {
- memcpy(target, data, PAGE_CACHE_SIZE);
- bytes_read -= PAGE_CACHE_SIZE;
- }
- kunmap_atomic(target, KM_USER0);
-
- flush_dcache_page(page);
- SetPageUptodate(page);
- unlock_page(page);
- data += PAGE_CACHE_SIZE;
-
- /* add page to FS-Cache */
- cifs_readpage_to_fscache(mapping->host, page);
- }
- return;
-}
-
static int cifs_readpages(struct file *file, struct address_space *mapping,
struct list_head *page_list, unsigned num_pages)
{
- int rc = -EACCES;
- int xid;
- loff_t offset;
- struct page *page;
- struct cifs_sb_info *cifs_sb;
- struct cifs_tcon *pTcon;
- unsigned int bytes_read = 0;
- unsigned int read_size, i;
- char *smb_read_data = NULL;
- struct smb_com_read_rsp *pSMBr;
- struct cifsFileInfo *open_file;
- struct cifs_io_parms io_parms;
- int buf_type = CIFS_NO_BUFFER;
- __u32 pid;
+ int rc;
+ struct list_head tmplist;
+ struct cifsFileInfo *open_file = file->private_data;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ unsigned int rsize = cifs_sb->rsize;
+ pid_t pid;
- xid = GetXid();
- if (file->private_data == NULL) {
- rc = -EBADF;
- FreeXid(xid);
- return rc;
- }
- open_file = file->private_data;
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
- pTcon = tlink_tcon(open_file->tlink);
+ /*
+ * Give up immediately if rsize is too small to read an entire page.
+ * The VFS will fall back to readpage. We should never reach this
+ * point however since we set ra_pages to 0 when the rsize is smaller
+ * than a cache page.
+ */
+ if (unlikely(rsize < PAGE_CACHE_SIZE))
+ return 0;
/*
* Reads as many pages as possible from fscache. Returns -ENOBUFS
@@ -2039,125 +2472,127 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list,
&num_pages);
if (rc == 0)
- goto read_complete;
+ return rc;
- cFYI(DBG2, "rpages: num pages %d", num_pages);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
pid = open_file->pid;
else
pid = current->tgid;
- for (i = 0; i < num_pages; ) {
- unsigned contig_pages;
- struct page *tmp_page;
- unsigned long expected_index;
+ rc = 0;
+ INIT_LIST_HEAD(&tmplist);
- if (list_empty(page_list))
- break;
+ cFYI(1, "%s: file=%p mapping=%p num_pages=%u", __func__, file,
+ mapping, num_pages);
+
+ /*
+ * Start with the page at end of list and move it to private
+ * list. Do the same with any following pages until we hit
+ * the rsize limit, hit an index discontinuity, or run out of
+ * pages. Issue the async read and then start the loop again
+ * until the list is empty.
+ *
+ * Note that list order is important. The page_list is in
+ * the order of declining indexes. When we put the pages in
+ * the rdata->pages, then we want them in increasing order.
+ */
+ while (!list_empty(page_list)) {
+ unsigned int bytes = PAGE_CACHE_SIZE;
+ unsigned int expected_index;
+ unsigned int nr_pages = 1;
+ loff_t offset;
+ struct page *page, *tpage;
+ struct cifs_readdata *rdata;
page = list_entry(page_list->prev, struct page, lru);
+
+ /*
+ * Lock the page and put it in the cache. Since no one else
+ * should have access to this page, we're safe to simply set
+ * PG_locked without checking it first.
+ */
+ __set_page_locked(page);
+ rc = add_to_page_cache_locked(page, mapping,
+ page->index, GFP_KERNEL);
+
+ /* give up if we can't stick it in the cache */
+ if (rc) {
+ __clear_page_locked(page);
+ break;
+ }
+
+ /* move first page to the tmplist */
offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+ list_move_tail(&page->lru, &tmplist);
- /* count adjacent pages that we will read into */
- contig_pages = 0;
- expected_index =
- list_entry(page_list->prev, struct page, lru)->index;
- list_for_each_entry_reverse(tmp_page, page_list, lru) {
- if (tmp_page->index == expected_index) {
- contig_pages++;
- expected_index++;
- } else
+ /* now try and add more pages onto the request */
+ expected_index = page->index + 1;
+ list_for_each_entry_safe_reverse(page, tpage, page_list, lru) {
+ /* discontinuity ? */
+ if (page->index != expected_index)
+ break;
+
+ /* would this page push the read over the rsize? */
+ if (bytes + PAGE_CACHE_SIZE > rsize)
break;
+
+ __set_page_locked(page);
+ if (add_to_page_cache_locked(page, mapping,
+ page->index, GFP_KERNEL)) {
+ __clear_page_locked(page);
+ break;
+ }
+ list_move_tail(&page->lru, &tmplist);
+ bytes += PAGE_CACHE_SIZE;
+ expected_index++;
+ nr_pages++;
}
- if (contig_pages + i > num_pages)
- contig_pages = num_pages - i;
-
- /* for reads over a certain size could initiate async
- read ahead */
-
- read_size = contig_pages * PAGE_CACHE_SIZE;
- /* Read size needs to be in multiples of one page */
- read_size = min_t(const unsigned int, read_size,
- cifs_sb->rsize & PAGE_CACHE_MASK);
- cFYI(DBG2, "rpages: read size 0x%x contiguous pages %d",
- read_size, contig_pages);
- rc = -EAGAIN;
- while (rc == -EAGAIN) {
+
+ rdata = cifs_readdata_alloc(nr_pages);
+ if (!rdata) {
+ /* best to give up if we're out of mem */
+ list_for_each_entry_safe(page, tpage, &tmplist, lru) {
+ list_del(&page->lru);
+ lru_cache_add_file(page);
+ unlock_page(page);
+ page_cache_release(page);
+ }
+ rc = -ENOMEM;
+ break;
+ }
+
+ spin_lock(&cifs_file_list_lock);
+ cifsFileInfo_get(open_file);
+ spin_unlock(&cifs_file_list_lock);
+ rdata->cfile = open_file;
+ rdata->mapping = mapping;
+ rdata->offset = offset;
+ rdata->bytes = bytes;
+ rdata->pid = pid;
+ list_splice_init(&tmplist, &rdata->pages);
+
+ do {
if (open_file->invalidHandle) {
rc = cifs_reopen_file(open_file, true);
if (rc != 0)
- break;
+ continue;
}
- io_parms.netfid = open_file->netfid;
- io_parms.pid = pid;
- io_parms.tcon = pTcon;
- io_parms.offset = offset;
- io_parms.length = read_size;
- rc = CIFSSMBRead(xid, &io_parms, &bytes_read,
- &smb_read_data, &buf_type);
- /* BB more RC checks ? */
- if (rc == -EAGAIN) {
- if (smb_read_data) {
- if (buf_type == CIFS_SMALL_BUFFER)
- cifs_small_buf_release(smb_read_data);
- else if (buf_type == CIFS_LARGE_BUFFER)
- cifs_buf_release(smb_read_data);
- smb_read_data = NULL;
- }
- }
- }
- if ((rc < 0) || (smb_read_data == NULL)) {
- cFYI(1, "Read error in readpages: %d", rc);
- break;
- } else if (bytes_read > 0) {
- task_io_account_read(bytes_read);
- pSMBr = (struct smb_com_read_rsp *)smb_read_data;
- cifs_copy_cache_pages(mapping, page_list, bytes_read,
- smb_read_data + 4 /* RFC1001 hdr */ +
- le16_to_cpu(pSMBr->DataOffset));
-
- i += bytes_read >> PAGE_CACHE_SHIFT;
- cifs_stats_bytes_read(pTcon, bytes_read);
- if ((bytes_read & PAGE_CACHE_MASK) != bytes_read) {
- i++; /* account for partial page */
-
- /* server copy of file can have smaller size
- than client */
- /* BB do we need to verify this common case ?
- this case is ok - if we are at server EOF
- we will hit it on next read */
+ rc = cifs_async_readv(rdata);
+ } while (rc == -EAGAIN);
- /* break; */
+ if (rc != 0) {
+ list_for_each_entry_safe(page, tpage, &rdata->pages,
+ lru) {
+ list_del(&page->lru);
+ lru_cache_add_file(page);
+ unlock_page(page);
+ page_cache_release(page);
}
- } else {
- cFYI(1, "No bytes read (%d) at offset %lld . "
- "Cleaning remaining pages from readahead list",
- bytes_read, offset);
- /* BB turn off caching and do new lookup on
- file size at server? */
+ cifs_readdata_free(rdata);
break;
}
- if (smb_read_data) {
- if (buf_type == CIFS_SMALL_BUFFER)
- cifs_small_buf_release(smb_read_data);
- else if (buf_type == CIFS_LARGE_BUFFER)
- cifs_buf_release(smb_read_data);
- smb_read_data = NULL;
- }
- bytes_read = 0;
}
-/* need to free smb_read_data buf before exit */
- if (smb_read_data) {
- if (buf_type == CIFS_SMALL_BUFFER)
- cifs_small_buf_release(smb_read_data);
- else if (buf_type == CIFS_LARGE_BUFFER)
- cifs_buf_release(smb_read_data);
- smb_read_data = NULL;
- }
-
-read_complete:
- FreeXid(xid);
return rc;
}
@@ -2406,6 +2841,10 @@ void cifs_oplock_break(struct work_struct *work)
cFYI(1, "Oplock flush inode %p rc %d", inode, rc);
}
+ rc = cifs_push_locks(cfile);
+ if (rc)
+ cERROR(1, "Push locks rc = %d", rc);
+
/*
* releasing stale oplock after recent reconnect of smb session using
* a now incorrect file handle is not a data integrity issue but do
@@ -2413,36 +2852,12 @@ void cifs_oplock_break(struct work_struct *work)
* disconnected since oplock already released by the server
*/
if (!cfile->oplock_break_cancelled) {
- rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0,
- 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false,
+ rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid,
+ current->tgid, 0, 0, 0, 0,
+ LOCKING_ANDX_OPLOCK_RELEASE, false,
cinode->clientCanCacheRead ? 1 : 0);
cFYI(1, "Oplock release rc = %d", rc);
}
-
- /*
- * We might have kicked in before is_valid_oplock_break()
- * finished grabbing reference for us. Make sure it's done by
- * waiting for cifs_file_list_lock.
- */
- spin_lock(&cifs_file_list_lock);
- spin_unlock(&cifs_file_list_lock);
-
- cifs_oplock_break_put(cfile);
-}
-
-/* must be called while holding cifs_file_list_lock */
-void cifs_oplock_break_get(struct cifsFileInfo *cfile)
-{
- cifs_sb_active(cfile->dentry->d_sb);
- cifsFileInfo_get(cfile);
-}
-
-void cifs_oplock_break_put(struct cifsFileInfo *cfile)
-{
- struct super_block *sb = cfile->dentry->d_sb;
-
- cifsFileInfo_put(cfile);
- cifs_sb_deactive(sb);
}
const struct address_space_operations cifs_addr_ops = {
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 9b018c8334f..e851d5b8931 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -132,7 +132,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
inode->i_mtime = fattr->cf_mtime;
inode->i_ctime = fattr->cf_ctime;
inode->i_rdev = fattr->cf_rdev;
- inode->i_nlink = fattr->cf_nlink;
+ set_nlink(inode, fattr->cf_nlink);
inode->i_uid = fattr->cf_uid;
inode->i_gid = fattr->cf_gid;
@@ -562,7 +562,16 @@ int cifs_get_file_info(struct file *filp)
xid = GetXid();
rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data);
- if (rc == -EOPNOTSUPP || rc == -EINVAL) {
+ switch (rc) {
+ case 0:
+ cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false);
+ break;
+ case -EREMOTE:
+ cifs_create_dfs_fattr(&fattr, inode->i_sb);
+ rc = 0;
+ break;
+ case -EOPNOTSUPP:
+ case -EINVAL:
/*
* FIXME: legacy server -- fall back to path-based call?
* for now, just skip revalidating and mark inode for
@@ -570,18 +579,14 @@ int cifs_get_file_info(struct file *filp)
*/
rc = 0;
CIFS_I(inode)->time = 0;
+ default:
goto cgfi_exit;
- } else if (rc == -EREMOTE) {
- cifs_create_dfs_fattr(&fattr, inode->i_sb);
- rc = 0;
- } else if (rc)
- goto cgfi_exit;
+ }
/*
* don't bother with SFU junk here -- just mark inode as needing
* revalidation.
*/
- cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false);
fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
cifs_fattr_to_inode(inode, &fattr);
@@ -764,20 +769,10 @@ char *cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
if (full_path == NULL)
return full_path;
- if (dfsplen) {
+ if (dfsplen)
strncpy(full_path, tcon->treeName, dfsplen);
- /* switch slash direction in prepath depending on whether
- * windows or posix style path names
- */
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
- int i;
- for (i = 0; i < dfsplen; i++) {
- if (full_path[i] == '\\')
- full_path[i] = '/';
- }
- }
- }
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;
}
@@ -910,7 +905,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
if (rc && tcon->ipc) {
cFYI(1, "ipc connection - fake read inode");
inode->i_mode |= S_IFDIR;
- inode->i_nlink = 2;
+ set_nlink(inode, 2);
inode->i_op = &cifs_ipc_inode_ops;
inode->i_fop = &simple_dir_operations;
inode->i_uid = cifs_sb->mnt_uid;
@@ -1372,7 +1367,7 @@ mkdir_get_info:
/* setting nlink not necessary except in cases where we
* failed to get it from the server or was set bogus */
if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2))
- direntry->d_inode->i_nlink = 2;
+ set_nlink(direntry->d_inode, 2);
mode &= ~current_umask();
/* must turn on setgid bit if parent dir has it */
@@ -2106,6 +2101,8 @@ static int
cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
{
int xid;
+ uid_t uid = NO_CHANGE_32;
+ gid_t gid = NO_CHANGE_32;
struct inode *inode = direntry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
@@ -2156,13 +2153,25 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
goto cifs_setattr_exit;
}
- /*
- * Without unix extensions we can't send ownership changes to the
- * server, so silently ignore them. This is consistent with how
- * local DOS/Windows filesystems behave (VFAT, NTFS, etc). With
- * CIFSACL support + proper Windows to Unix idmapping, we may be
- * able to support this in the future.
- */
+ if (attrs->ia_valid & ATTR_UID)
+ uid = attrs->ia_uid;
+
+ if (attrs->ia_valid & ATTR_GID)
+ gid = attrs->ia_gid;
+
+#ifdef CONFIG_CIFS_ACL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+ if (uid != NO_CHANGE_32 || gid != NO_CHANGE_32) {
+ rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64,
+ uid, gid);
+ if (rc) {
+ cFYI(1, "%s: Setting id failed with error: %d",
+ __func__, rc);
+ goto cifs_setattr_exit;
+ }
+ }
+ } else
+#endif /* CONFIG_CIFS_ACL */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID))
attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
@@ -2171,15 +2180,12 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~ATTR_MODE;
if (attrs->ia_valid & ATTR_MODE) {
- cFYI(1, "Mode changed to 0%o", attrs->ia_mode);
mode = attrs->ia_mode;
- }
-
- if (attrs->ia_valid & ATTR_MODE) {
rc = 0;
#ifdef CONFIG_CIFS_ACL
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
- rc = mode_to_cifs_acl(inode, full_path, mode);
+ rc = id_mode_to_cifs_acl(inode, full_path, mode,
+ NO_CHANGE_32, NO_CHANGE_32);
if (rc) {
cFYI(1, "%s: Setting ACL failed with error: %d",
__func__, rc);
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 556b1a0b54d..6b0e0643439 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -74,8 +74,14 @@ symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
cERROR(1, "%s: Could not init md5 shash\n", __func__);
goto symlink_hash_err;
}
- crypto_shash_update(&sdescmd5->shash, link_str, link_len);
+ rc = crypto_shash_update(&sdescmd5->shash, link_str, link_len);
+ if (rc) {
+ cERROR(1, "%s: Could not update iwth link_str\n", __func__);
+ goto symlink_hash_err;
+ }
rc = crypto_shash_final(&sdescmd5->shash, md5_hash);
+ if (rc)
+ cERROR(1, "%s: Could not generate md5 hash\n", __func__);
symlink_hash_err:
crypto_free_shash(md5);
@@ -177,14 +183,20 @@ CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)
static int
CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon,
const char *fromName, const char *toName,
- const struct nls_table *nls_codepage, int remap)
+ struct cifs_sb_info *cifs_sb)
{
int rc;
int oplock = 0;
+ int remap;
+ int create_options = CREATE_NOT_DIR;
__u16 netfid = 0;
u8 *buf;
unsigned int bytes_written = 0;
struct cifs_io_parms io_parms;
+ struct nls_table *nls_codepage;
+
+ nls_codepage = cifs_sb->local_nls;
+ remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf)
@@ -196,8 +208,11 @@ CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon,
return rc;
}
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE,
- CREATE_NOT_DIR, &netfid, &oplock, NULL,
+ create_options, &netfid, &oplock, NULL,
nls_codepage, remap);
if (rc != 0) {
kfree(buf);
@@ -418,7 +433,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
if (old_file->d_inode) {
cifsInode = CIFS_I(old_file->d_inode);
if (rc == 0) {
- old_file->d_inode->i_nlink++;
+ inc_nlink(old_file->d_inode);
/* BB should we make this contingent on superblock flag NOATIME? */
/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
/* parent dir timestamps will update from srv
@@ -553,9 +568,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
/* BB what if DFS and this volume is on different share? BB */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ cifs_sb);
else if (pTcon->unix_ext)
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
cifs_sb->local_nls);
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 03a1f491d39..703ef5c6fdb 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -420,19 +420,22 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)
}
int
-checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)
+checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read)
{
- __u32 len = be32_to_cpu(smb->smb_buf_length);
+ __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
__u32 clc_len; /* calculated length */
- cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len);
+ cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x",
+ total_read, rfclen);
- if (length < 2 + sizeof(struct smb_hdr)) {
- if ((length >= sizeof(struct smb_hdr) - 1)
+ /* is this frame too small to even get to a BCC? */
+ if (total_read < 2 + sizeof(struct smb_hdr)) {
+ if ((total_read >= sizeof(struct smb_hdr) - 1)
&& (smb->Status.CifsError != 0)) {
+ /* it's an error return */
smb->WordCount = 0;
/* some error cases do not return wct and bcc */
return 0;
- } else if ((length == sizeof(struct smb_hdr) + 1) &&
+ } else if ((total_read == sizeof(struct smb_hdr) + 1) &&
(smb->WordCount == 0)) {
char *tmp = (char *)smb;
/* Need to work around a bug in two servers here */
@@ -452,39 +455,35 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)
} else {
cERROR(1, "Length less than smb header size");
}
- return 1;
- }
- if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- cERROR(1, "smb length greater than MaxBufSize, mid=%d",
- smb->Mid);
- return 1;
+ return -EIO;
}
+ /* otherwise, there is enough to get to the BCC */
if (check_smb_hdr(smb, mid))
- return 1;
+ return -EIO;
clc_len = smbCalcSize(smb);
- if (4 + len != length) {
+ if (4 + rfclen != total_read) {
cERROR(1, "Length read does not match RFC1001 length %d",
- len);
- return 1;
+ rfclen);
+ return -EIO;
}
- if (4 + len != clc_len) {
+ if (4 + rfclen != clc_len) {
/* check if bcc wrapped around for large read responses */
- if ((len > 64 * 1024) && (len > clc_len)) {
+ if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {
/* check if lengths match mod 64K */
- if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF))
+ if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF))
return 0; /* bcc wrapped */
}
cFYI(1, "Calculated size %u vs length %u mismatch for mid=%u",
- clc_len, 4 + len, smb->Mid);
+ clc_len, 4 + rfclen, smb->Mid);
- if (4 + len < clc_len) {
+ if (4 + rfclen < clc_len) {
cERROR(1, "RFC1001 size %u smaller than SMB for mid=%u",
- len, smb->Mid);
- return 1;
- } else if (len > clc_len + 512) {
+ rfclen, smb->Mid);
+ return -EIO;
+ } else if (rfclen > clc_len + 512) {
/*
* Some servers (Windows XP in particular) send more
* data than the lengths in the SMB packet would
@@ -495,8 +494,8 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)
* data to 512 bytes.
*/
cERROR(1, "RFC1001 size %u more than 512 bytes larger "
- "than SMB for mid=%u", len, smb->Mid);
- return 1;
+ "than SMB for mid=%u", rfclen, smb->Mid);
+ return -EIO;
}
}
return 0;
@@ -585,15 +584,8 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
cifs_set_oplock_level(pCifsInode,
pSMB->OplockLevel ? OPLOCK_READ : 0);
- /*
- * cifs_oplock_break_put() can't be called
- * from here. Get reference after queueing
- * succeeded. cifs_oplock_break() will
- * synchronize using cifs_file_list_lock.
- */
- if (queue_work(system_nrt_wq,
- &netfile->oplock_break))
- cifs_oplock_break_get(netfile);
+ queue_work(system_nrt_wq,
+ &netfile->oplock_break);
netfile->oplock_break_cancelled = false;
spin_unlock(&cifs_file_list_lock);
@@ -683,3 +675,18 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
cinode->clientCanCacheRead = false;
}
}
+
+bool
+backup_cred(struct cifs_sb_info *cifs_sb)
+{
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) {
+ if (cifs_sb->mnt_backupuid == current_fsuid())
+ return true;
+ }
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) {
+ if (in_group_p(cifs_sb->mnt_backupgid))
+ return true;
+ }
+
+ return false;
+}
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 965a3af186a..5de03ec2014 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -4,6 +4,7 @@
* Directory search handling
*
* Copyright (C) International Business Machines Corp., 2004, 2008
+ * Copyright (C) Red Hat, Inc., 2011
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
@@ -290,10 +291,10 @@ error_exit:
}
/* return length of unicode string in bytes */
-static int cifs_unicode_bytelen(char *str)
+static int cifs_unicode_bytelen(const char *str)
{
int len;
- __le16 *ustr = (__le16 *)str;
+ const __le16 *ustr = (const __le16 *)str;
for (len = 0; len <= PATH_MAX; len++) {
if (ustr[len] == 0)
@@ -334,78 +335,128 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
}
+struct cifs_dirent {
+ const char *name;
+ size_t namelen;
+ u32 resume_key;
+ u64 ino;
+};
+
+static void cifs_fill_dirent_unix(struct cifs_dirent *de,
+ const FILE_UNIX_INFO *info, bool is_unicode)
+{
+ de->name = &info->FileName[0];
+ if (is_unicode)
+ de->namelen = cifs_unicode_bytelen(de->name);
+ else
+ de->namelen = strnlen(de->name, PATH_MAX);
+ de->resume_key = info->ResumeKey;
+ de->ino = le64_to_cpu(info->basic.UniqueId);
+}
+
+static void cifs_fill_dirent_dir(struct cifs_dirent *de,
+ const FILE_DIRECTORY_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_full(struct cifs_dirent *de,
+ const FILE_FULL_DIRECTORY_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_search(struct cifs_dirent *de,
+ const SEARCH_ID_FULL_DIR_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+ de->ino = le64_to_cpu(info->UniqueId);
+}
+
+static void cifs_fill_dirent_both(struct cifs_dirent *de,
+ const FILE_BOTH_DIRECTORY_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_std(struct cifs_dirent *de,
+ const FIND_FILE_STANDARD_INFO *info)
+{
+ de->name = &info->FileName[0];
+ /* one byte length, no endianess conversion */
+ de->namelen = info->FileNameLength;
+ de->resume_key = info->ResumeKey;
+}
+
+static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
+ u16 level, bool is_unicode)
+{
+ memset(de, 0, sizeof(*de));
+
+ switch (level) {
+ case SMB_FIND_FILE_UNIX:
+ cifs_fill_dirent_unix(de, info, is_unicode);
+ break;
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ cifs_fill_dirent_dir(de, info);
+ break;
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ cifs_fill_dirent_full(de, info);
+ break;
+ case SMB_FIND_FILE_ID_FULL_DIR_INFO:
+ cifs_fill_dirent_search(de, info);
+ break;
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ cifs_fill_dirent_both(de, info);
+ break;
+ case SMB_FIND_FILE_INFO_STANDARD:
+ cifs_fill_dirent_std(de, info);
+ break;
+ default:
+ cFYI(1, "Unknown findfirst level %d", level);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
#define UNICODE_DOT cpu_to_le16(0x2e)
/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
-static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
+static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
{
int rc = 0;
- char *filename = NULL;
- int len = 0;
-
- if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
- FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- if (cfile->srch_inf.unicode) {
- len = cifs_unicode_bytelen(filename);
- } else {
- /* BB should we make this strnlen of PATH_MAX? */
- len = strnlen(filename, 5);
- }
- } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
- FILE_DIRECTORY_INFO *pFindData =
- (FILE_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- } else if (cfile->srch_inf.info_level ==
- SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
- FILE_FULL_DIRECTORY_INFO *pFindData =
- (FILE_FULL_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- } else if (cfile->srch_inf.info_level ==
- SMB_FIND_FILE_ID_FULL_DIR_INFO) {
- SEARCH_ID_FULL_DIR_INFO *pFindData =
- (SEARCH_ID_FULL_DIR_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- } else if (cfile->srch_inf.info_level ==
- SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
- FILE_BOTH_DIRECTORY_INFO *pFindData =
- (FILE_BOTH_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
- FIND_FILE_STANDARD_INFO *pFindData =
- (FIND_FILE_STANDARD_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = pFindData->FileNameLength;
- } else {
- cFYI(1, "Unknown findfirst level %d",
- cfile->srch_inf.info_level);
- }
- if (filename) {
- if (cfile->srch_inf.unicode) {
- __le16 *ufilename = (__le16 *)filename;
- if (len == 2) {
- /* check for . */
- if (ufilename[0] == UNICODE_DOT)
- rc = 1;
- } else if (len == 4) {
- /* check for .. */
- if ((ufilename[0] == UNICODE_DOT)
- && (ufilename[1] == UNICODE_DOT))
- rc = 2;
- }
- } else /* ASCII */ {
- if (len == 1) {
- if (filename[0] == '.')
- rc = 1;
- } else if (len == 2) {
- if ((filename[0] == '.') && (filename[1] == '.'))
- rc = 2;
- }
+ if (!de->name)
+ return 0;
+
+ if (is_unicode) {
+ __le16 *ufilename = (__le16 *)de->name;
+ if (de->namelen == 2) {
+ /* check for . */
+ if (ufilename[0] == UNICODE_DOT)
+ rc = 1;
+ } else if (de->namelen == 4) {
+ /* check for .. */
+ if (ufilename[0] == UNICODE_DOT &&
+ ufilename[1] == UNICODE_DOT)
+ rc = 2;
+ }
+ } else /* ASCII */ {
+ if (de->namelen == 1) {
+ if (de->name[0] == '.')
+ rc = 1;
+ } else if (de->namelen == 2) {
+ if (de->name[0] == '.' && de->name[1] == '.')
+ rc = 2;
}
}
@@ -427,66 +478,18 @@ static int is_dir_changed(struct file *file)
}
static int cifs_save_resume_key(const char *current_entry,
- struct cifsFileInfo *cifsFile)
+ struct cifsFileInfo *file_info)
{
- int rc = 0;
- unsigned int len = 0;
- __u16 level;
- char *filename;
-
- if ((cifsFile == NULL) || (current_entry == NULL))
- return -EINVAL;
-
- level = cifsFile->srch_inf.info_level;
-
- if (level == SMB_FIND_FILE_UNIX) {
- FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
+ struct cifs_dirent de;
+ int rc;
- filename = &pFindData->FileName[0];
- if (cifsFile->srch_inf.unicode) {
- len = cifs_unicode_bytelen(filename);
- } else {
- /* BB should we make this strnlen of PATH_MAX? */
- len = strnlen(filename, PATH_MAX);
- }
- cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
- } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
- FILE_DIRECTORY_INFO *pFindData =
- (FILE_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- cifsFile->srch_inf.resume_key = pFindData->FileIndex;
- } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
- FILE_FULL_DIRECTORY_INFO *pFindData =
- (FILE_FULL_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- cifsFile->srch_inf.resume_key = pFindData->FileIndex;
- } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
- SEARCH_ID_FULL_DIR_INFO *pFindData =
- (SEARCH_ID_FULL_DIR_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- cifsFile->srch_inf.resume_key = pFindData->FileIndex;
- } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
- FILE_BOTH_DIRECTORY_INFO *pFindData =
- (FILE_BOTH_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- cifsFile->srch_inf.resume_key = pFindData->FileIndex;
- } else if (level == SMB_FIND_FILE_INFO_STANDARD) {
- FIND_FILE_STANDARD_INFO *pFindData =
- (FIND_FILE_STANDARD_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- /* one byte length, no name conversion */
- len = (unsigned int)pFindData->FileNameLength;
- cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
- } else {
- cFYI(1, "Unknown findfirst level %d", level);
- return -EINVAL;
+ rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level,
+ file_info->srch_inf.unicode);
+ if (!rc) {
+ file_info->srch_inf.presume_name = de.name;
+ file_info->srch_inf.resume_name_len = de.namelen;
+ file_info->srch_inf.resume_key = de.resume_key;
}
- cifsFile->srch_inf.resume_name_len = len;
- cifsFile->srch_inf.presume_name = filename;
return rc;
}
@@ -605,136 +608,70 @@ static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon,
return rc;
}
-/* inode num, inode type and filename returned */
-static int cifs_get_name_from_search_buf(struct qstr *pqst,
- char *current_entry, __u16 level, unsigned int unicode,
- struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum)
+static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir,
+ void *dirent, char *scratch_buf, unsigned int max_len)
{
+ struct cifsFileInfo *file_info = file->private_data;
+ struct super_block *sb = file->f_path.dentry->d_sb;
+ 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;
- unsigned int len = 0;
- char *filename;
- struct nls_table *nlt = cifs_sb->local_nls;
-
- *pinum = 0;
-
- if (level == SMB_FIND_FILE_UNIX) {
- FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
-
- filename = &pFindData->FileName[0];
- if (unicode) {
- len = cifs_unicode_bytelen(filename);
- } else {
- /* BB should we make this strnlen of PATH_MAX? */
- len = strnlen(filename, PATH_MAX);
- }
+ ino_t ino;
- *pinum = le64_to_cpu(pFindData->basic.UniqueId);
- } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
- FILE_DIRECTORY_INFO *pFindData =
- (FILE_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
- FILE_FULL_DIRECTORY_INFO *pFindData =
- (FILE_FULL_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
- SEARCH_ID_FULL_DIR_INFO *pFindData =
- (SEARCH_ID_FULL_DIR_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- *pinum = le64_to_cpu(pFindData->UniqueId);
- } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
- FILE_BOTH_DIRECTORY_INFO *pFindData =
- (FILE_BOTH_DIRECTORY_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
- } else if (level == SMB_FIND_FILE_INFO_STANDARD) {
- FIND_FILE_STANDARD_INFO *pFindData =
- (FIND_FILE_STANDARD_INFO *)current_entry;
- filename = &pFindData->FileName[0];
- /* one byte length, no name conversion */
- len = (unsigned int)pFindData->FileNameLength;
- } else {
- cFYI(1, "Unknown findfirst level %d", level);
- return -EINVAL;
- }
+ rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
+ file_info->srch_inf.unicode);
+ if (rc)
+ return rc;
- if (len > max_len) {
- cERROR(1, "bad search response length %d past smb end", len);
+ if (de.namelen > max_len) {
+ cERROR(1, "bad search response length %zd past smb end",
+ de.namelen);
return -EINVAL;
}
- if (unicode) {
- pqst->len = cifs_from_ucs2((char *) pqst->name,
- (__le16 *) filename,
- UNICODE_NAME_MAX,
- min(len, max_len), nlt,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- pqst->len -= nls_nullsize(nlt);
- } else {
- pqst->name = filename;
- pqst->len = len;
- }
- return rc;
-}
-
-static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
- void *direntry, char *scratch_buf, unsigned int max_len)
-{
- int rc = 0;
- struct qstr qstring;
- struct cifsFileInfo *pCifsF;
- u64 inum;
- ino_t ino;
- struct super_block *sb;
- struct cifs_sb_info *cifs_sb;
- struct dentry *tmp_dentry;
- struct cifs_fattr fattr;
-
- /* get filename and len into qstring */
- /* get dentry */
- /* decide whether to create and populate ionde */
- if ((direntry == NULL) || (file == NULL))
- return -EINVAL;
-
- pCifsF = file->private_data;
-
- if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
- return -ENOENT;
-
- rc = cifs_entry_is_dot(pfindEntry, pCifsF);
/* skip . and .. since we added them first */
- if (rc != 0)
+ if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
return 0;
- sb = file->f_path.dentry->d_sb;
- cifs_sb = CIFS_SB(sb);
-
- qstring.name = scratch_buf;
- rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
- pCifsF->srch_inf.info_level,
- pCifsF->srch_inf.unicode, cifs_sb,
- max_len, &inum /* returned */);
+ if (file_info->srch_inf.unicode) {
+ struct nls_table *nlt = cifs_sb->local_nls;
- if (rc)
- return rc;
+ name.name = scratch_buf;
+ name.len =
+ cifs_from_ucs2((char *)name.name, (__le16 *)de.name,
+ UNICODE_NAME_MAX,
+ min(de.namelen, (size_t)max_len), nlt,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ name.len -= nls_nullsize(nlt);
+ } else {
+ name.name = de.name;
+ name.len = de.namelen;
+ }
- if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
+ switch (file_info->srch_inf.info_level) {
+ case SMB_FIND_FILE_UNIX:
cifs_unix_basic_to_fattr(&fattr,
- &((FILE_UNIX_INFO *) pfindEntry)->basic,
- cifs_sb);
- else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
- cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *)
- pfindEntry, cifs_sb);
- else
- cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)
- pfindEntry, cifs_sb);
+ &((FILE_UNIX_INFO *)find_entry)->basic,
+ cifs_sb);
+ break;
+ case SMB_FIND_FILE_INFO_STANDARD:
+ cifs_std_info_to_fattr(&fattr,
+ (FIND_FILE_STANDARD_INFO *)find_entry,
+ cifs_sb);
+ break;
+ default:
+ cifs_dir_info_to_fattr(&fattr,
+ (FILE_DIRECTORY_INFO *)find_entry,
+ cifs_sb);
+ break;
+ }
- if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
- fattr.cf_uniqueid = inum;
+ if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
+ fattr.cf_uniqueid = de.ino;
} else {
fattr.cf_uniqueid = iunique(sb, ROOT_I);
cifs_autodisable_serverino(cifs_sb);
@@ -750,12 +687,12 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
- tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
+ dentry = cifs_readdir_lookup(file->f_dentry, &name, &fattr);
- rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
- ino, fattr.cf_dtype);
+ rc = filldir(dirent, name.name, name.len, file->f_pos, ino,
+ fattr.cf_dtype);
- dput(tmp_dentry);
+ dput(dentry);
return rc;
}
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index d3e619692ee..4ec3ee9d72c 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -124,7 +124,9 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
/* that we use in next few lines */
/* Note that header is initialized to zero in header_assemble */
pSMB->req.AndXCommand = 0xFF;
- pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32,
+ CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
+ USHRT_MAX));
pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
pSMB->req.VcNumber = get_next_vcnum(ses);
@@ -681,7 +683,7 @@ ssetup_ntlmssp_authenticate:
cpu_to_le16(CIFS_AUTH_RESP_SIZE);
/* calculate ntlm response and session key */
- rc = setup_ntlm_response(ses);
+ rc = setup_ntlm_response(ses, nls_cp);
if (rc) {
cERROR(1, "Error %d during NTLM authentication", rc);
goto ssetup_exit;
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index 1c5b770c314..7cacba12b8f 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -157,8 +157,14 @@ mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len)
cERROR(1, "%s: Could not init md4 shash\n", __func__);
goto mdfour_err;
}
- crypto_shash_update(&sdescmd4->shash, link_str, link_len);
+ rc = crypto_shash_update(&sdescmd4->shash, link_str, link_len);
+ if (rc) {
+ cERROR(1, "%s: Could not update with link_str\n", __func__);
+ goto mdfour_err;
+ }
rc = crypto_shash_final(&sdescmd4->shash, md4_hash);
+ if (rc)
+ cERROR(1, "%s: Could not genereate md4 hash\n", __func__);
mdfour_err:
crypto_free_shash(md4);
@@ -193,160 +199,36 @@ SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24)
return rc;
}
-/* Routines for Windows NT MD4 Hash functions. */
-static int
-_my_wcslen(__u16 *str)
-{
- int len = 0;
- while (*str++ != 0)
- len++;
- return len;
-}
-
-/*
- * Convert a string into an NT UNICODE string.
- * Note that regardless of processor type
- * this must be in intel (little-endian)
- * format.
- */
-
-static int
-_my_mbstowcs(__u16 *dst, const unsigned char *src, int len)
-{ /* BB not a very good conversion routine - change/fix */
- int i;
- __u16 val;
-
- for (i = 0; i < len; i++) {
- val = *src;
- SSVAL(dst, 0, val);
- dst++;
- src++;
- if (val == 0)
- break;
- }
- return i;
-}
-
/*
* Creates the MD4 Hash of the users password in NT UNICODE.
*/
int
-E_md4hash(const unsigned char *passwd, unsigned char *p16)
+E_md4hash(const unsigned char *passwd, unsigned char *p16,
+ const struct nls_table *codepage)
{
int rc;
int len;
__u16 wpwd[129];
/* Password cannot be longer than 128 characters */
- if (passwd) {
- len = strlen((char *) passwd);
- if (len > 128)
- len = 128;
-
- /* Password must be converted to NT unicode */
- _my_mbstowcs(wpwd, passwd, len);
- } else
+ if (passwd) /* Password must be converted to NT unicode */
+ len = cifs_strtoUCS(wpwd, passwd, 128, codepage);
+ else {
len = 0;
+ *wpwd = 0; /* Ensure string is null terminated */
+ }
- wpwd[len] = 0; /* Ensure string is null terminated */
- /* Calculate length in bytes */
- len = _my_wcslen(wpwd) * sizeof(__u16);
-
- rc = mdfour(p16, (unsigned char *) wpwd, len);
- memset(wpwd, 0, 129 * 2);
+ rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__u16));
+ memset(wpwd, 0, 129 * sizeof(__u16));
return rc;
}
-#if 0 /* currently unused */
-/* Does both the NT and LM owfs of a user's password */
-static void
-nt_lm_owf_gen(char *pwd, unsigned char nt_p16[16], unsigned char p16[16])
-{
- char passwd[514];
-
- memset(passwd, '\0', 514);
- if (strlen(pwd) < 513)
- strcpy(passwd, pwd);
- else
- memcpy(passwd, pwd, 512);
- /* Calculate the MD4 hash (NT compatible) of the password */
- memset(nt_p16, '\0', 16);
- E_md4hash(passwd, nt_p16);
-
- /* Mangle the passwords into Lanman format */
- passwd[14] = '\0';
-/* strupper(passwd); */
-
- /* Calculate the SMB (lanman) hash functions of the password */
-
- memset(p16, '\0', 16);
- E_P16((unsigned char *) passwd, (unsigned char *) p16);
-
- /* clear out local copy of user's password (just being paranoid). */
- memset(passwd, '\0', sizeof(passwd));
-}
-#endif
-
-/* Does the NTLMv2 owfs of a user's password */
-#if 0 /* function not needed yet - but will be soon */
-static void
-ntv2_owf_gen(const unsigned char owf[16], const char *user_n,
- const char *domain_n, unsigned char kr_buf[16],
- const struct nls_table *nls_codepage)
-{
- wchar_t *user_u;
- wchar_t *dom_u;
- int user_l, domain_l;
- struct HMACMD5Context ctx;
-
- /* might as well do one alloc to hold both (user_u and dom_u) */
- user_u = kmalloc(2048 * sizeof(wchar_t), GFP_KERNEL);
- if (user_u == NULL)
- return;
- dom_u = user_u + 1024;
-
- /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2,
- STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER);
- push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2,
- STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */
-
- /* BB user and domain may need to be uppercased */
- user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage);
- domain_l = cifs_strtoUCS(dom_u, domain_n, 511, nls_codepage);
-
- user_l++; /* trailing null */
- domain_l++;
-
- hmac_md5_init_limK_to_64(owf, 16, &ctx);
- hmac_md5_update((const unsigned char *) user_u, user_l * 2, &ctx);
- hmac_md5_update((const unsigned char *) dom_u, domain_l * 2, &ctx);
- hmac_md5_final(kr_buf, &ctx);
-
- kfree(user_u);
-}
-#endif
-
-/* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */
-#if 0 /* currently unused */
-static void
-NTLMSSPOWFencrypt(unsigned char passwd[8],
- unsigned char *ntlmchalresp, unsigned char p24[24])
-{
- unsigned char p21[21];
-
- memset(p21, '\0', 21);
- memcpy(p21, passwd, 8);
- memset(p21 + 8, 0xbd, 8);
-
- E_P24(p21, ntlmchalresp, p24);
-}
-#endif
-
/* Does the NT MD4 hash then des encryption. */
int
-SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
+SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24,
+ const struct nls_table *codepage)
{
int rc;
unsigned char p16[16], p21[21];
@@ -354,7 +236,7 @@ SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
memset(p16, '\0', 16);
memset(p21, '\0', 21);
- rc = E_md4hash(passwd, p16);
+ rc = E_md4hash(passwd, p16, codepage);
if (rc) {
cFYI(1, "%s Can't generate NT hash, error: %d", __func__, rc);
return rc;
@@ -363,39 +245,3 @@ SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
rc = E_P24(p21, c8, p24);
return rc;
}
-
-
-/* Does the md5 encryption from the NT hash for NTLMv2. */
-/* These routines will be needed later */
-#if 0
-static void
-SMBOWFencrypt_ntv2(const unsigned char kr[16],
- const struct data_blob *srv_chal,
- const struct data_blob *cli_chal, unsigned char resp_buf[16])
-{
- struct HMACMD5Context ctx;
-
- hmac_md5_init_limK_to_64(kr, 16, &ctx);
- hmac_md5_update(srv_chal->data, srv_chal->length, &ctx);
- hmac_md5_update(cli_chal->data, cli_chal->length, &ctx);
- hmac_md5_final(resp_buf, &ctx);
-}
-
-static void
-SMBsesskeygen_ntv2(const unsigned char kr[16],
- const unsigned char *nt_resp, __u8 sess_key[16])
-{
- struct HMACMD5Context ctx;
-
- hmac_md5_init_limK_to_64(kr, 16, &ctx);
- hmac_md5_update(nt_resp, 16, &ctx);
- hmac_md5_final((unsigned char *) sess_key, &ctx);
-}
-
-static void
-SMBsesskeygen_ntv1(const unsigned char kr[16],
- const unsigned char *nt_resp, __u8 sess_key[16])
-{
- mdfour((unsigned char *) sess_key, (unsigned char *) kr, 16);
-}
-#endif
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 147aa22c3c3..0cc9584f588 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -26,6 +26,7 @@
#include <linux/wait.h>
#include <linux/net.h>
#include <linux/delay.h>
+#include <linux/freezer.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <linux/mempool.h>
@@ -266,15 +267,11 @@ static int wait_for_free_request(struct TCP_Server_Info *server,
while (1) {
if (atomic_read(&server->inFlight) >= cifs_max_pending) {
spin_unlock(&GlobalMid_Lock);
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&server->num_waiters);
-#endif
+ cifs_num_waiters_inc(server);
wait_event(server->request_q,
atomic_read(&server->inFlight)
< cifs_max_pending);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&server->num_waiters);
-#endif
+ cifs_num_waiters_dec(server);
spin_lock(&GlobalMid_Lock);
} else {
if (server->tcpStatus == CifsExiting) {
@@ -328,7 +325,7 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
{
int error;
- error = wait_event_killable(server->response_q,
+ error = wait_event_freezekillable(server->response_q,
midQ->midState != MID_REQUEST_SUBMITTED);
if (error < 0)
return -ERESTARTSYS;
@@ -343,8 +340,8 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
*/
int
cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
- unsigned int nvec, mid_callback_t *callback, void *cbdata,
- bool ignore_pend)
+ unsigned int nvec, mid_receive_t *receive,
+ mid_callback_t *callback, void *cbdata, bool ignore_pend)
{
int rc;
struct mid_q_entry *mid;
@@ -362,6 +359,8 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
mid = AllocMidQEntry(hdr, server);
if (mid == NULL) {
mutex_unlock(&server->srv_mutex);
+ atomic_dec(&server->inFlight);
+ wake_up(&server->request_q);
return -ENOMEM;
}
@@ -376,18 +375,17 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
goto out_err;
}
+ mid->receive = receive;
mid->callback = callback;
mid->callback_data = cbdata;
mid->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&server->inSend);
-#endif
+
+ cifs_in_send_inc(server);
rc = smb_sendv(server, iov, nvec);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&server->inSend);
- mid->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(server);
+ cifs_save_when_sent(mid);
mutex_unlock(&server->srv_mutex);
+
if (rc)
goto out_err;
@@ -500,13 +498,18 @@ int
cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
bool log_error)
{
- dump_smb(mid->resp_buf,
- min_t(u32, 92, be32_to_cpu(mid->resp_buf->smb_buf_length)));
+ unsigned int len = be32_to_cpu(mid->resp_buf->smb_buf_length) + 4;
+
+ dump_smb(mid->resp_buf, min_t(u32, 92, len));
/* convert the length into a more usable form */
if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+ struct kvec iov;
+
+ iov.iov_base = mid->resp_buf;
+ iov.iov_len = len;
/* FIXME: add code to kill session */
- if (cifs_verify_signature(mid->resp_buf, server,
+ if (cifs_verify_signature(&iov, 1, server,
mid->sequence_number + 1) != 0)
cERROR(1, "Unexpected SMB signature");
}
@@ -573,14 +576,10 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
}
midQ->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->inSend);
-#endif
+ cifs_in_send_inc(ses->server);
rc = smb_sendv(ses->server, iov, n_vec);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->inSend);
- midQ->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
mutex_unlock(&ses->server->srv_mutex);
@@ -701,14 +700,11 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
}
midQ->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->inSend);
-#endif
+
+ cifs_in_send_inc(ses->server);
rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->inSend);
- midQ->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
mutex_unlock(&ses->server->srv_mutex);
if (rc < 0)
@@ -841,14 +837,10 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
}
midQ->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->inSend);
-#endif
+ cifs_in_send_inc(ses->server);
rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->inSend);
- midQ->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) {
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index 2a22fb2989e..45f07c46f3e 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -22,6 +22,7 @@
#include <linux/fs.h>
#include <linux/posix_acl_xattr.h>
#include <linux/slab.h>
+#include <linux/xattr.h>
#include "cifsfs.h"
#include "cifspdu.h"
#include "cifsglob.h"
@@ -31,16 +32,8 @@
#define MAX_EA_VALUE_SIZE 65535
#define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
#define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
-#define CIFS_XATTR_USER_PREFIX "user."
-#define CIFS_XATTR_SYSTEM_PREFIX "system."
-#define CIFS_XATTR_OS2_PREFIX "os2."
-#define CIFS_XATTR_SECURITY_PREFIX "security."
-#define CIFS_XATTR_TRUSTED_PREFIX "trusted."
-#define XATTR_TRUSTED_PREFIX_LEN 8
-#define XATTR_SECURITY_PREFIX_LEN 9
-/* BB need to add server (Samba e.g) support for security and trusted prefix */
-
+/* BB need to add server (Samba e.g) support for security and trusted prefix */
int cifs_removexattr(struct dentry *direntry, const char *ea_name)
{
@@ -76,8 +69,8 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
}
if (ea_name == NULL) {
cFYI(1, "Null xattr names not supported");
- } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5)
- && (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4))) {
+ } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+ && (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) {
cFYI(1,
"illegal xattr request %s (only user namespace supported)",
ea_name);
@@ -88,7 +81,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto remove_ea_exit;
- ea_name += 5; /* skip past user. prefix */
+ ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL,
(__u16)0, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -149,21 +142,23 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
if (ea_name == NULL) {
cFYI(1, "Null xattr names not supported");
- } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+ } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+ == 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto set_ea_exit;
if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0)
cFYI(1, "attempt to set cifs inode metadata");
- ea_name += 5; /* skip past user. prefix */
+ ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
(__u16)value_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
- } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+ } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)
+ == 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto set_ea_exit;
- ea_name += 4; /* skip past os2. prefix */
+ ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
(__u16)value_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -178,7 +173,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
#ifdef CONFIG_CIFS_ACL
memcpy(pacl, ea_value, value_size);
rc = set_cifs_acl(pacl, value_size,
- direntry->d_inode, full_path);
+ direntry->d_inode, full_path, CIFS_ACL_DACL);
if (rc == 0) /* force revalidate of the inode */
CIFS_I(direntry->d_inode)->time = 0;
kfree(pacl);
@@ -269,7 +264,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
/* return alt name if available as pseudo attr */
if (ea_name == NULL) {
cFYI(1, "Null xattr names not supported");
- } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+ } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+ == 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto get_ea_exit;
@@ -277,15 +273,15 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
cFYI(1, "attempt to query cifs inode metadata");
/* revalidate/getattr then populate from inode */
} /* BB add else when above is implemented */
- ea_name += 5; /* skip past user. prefix */
+ ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
buf_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
- } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+ } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto get_ea_exit;
- ea_name += 4; /* skip past os2. prefix */
+ ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
buf_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -339,10 +335,10 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
cFYI(1, "Query CIFS ACL not supported yet");
#endif /* CONFIG_CIFS_ACL */
} else if (strncmp(ea_name,
- CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
+ XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
cFYI(1, "Trusted xattr namespace not supported yet");
} else if (strncmp(ea_name,
- CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
+ XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
cFYI(1, "Security xattr namespace not supported yet");
} else
cFYI(1,