summaryrefslogtreecommitdiff
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/AUTHORS2
-rw-r--r--fs/cifs/CHANGES24
-rw-r--r--fs/cifs/Makefile2
-rw-r--r--fs/cifs/README12
-rw-r--r--fs/cifs/cifs_dfs_ref.c48
-rw-r--r--fs/cifs/cifs_fs_sb.h7
-rw-r--r--fs/cifs/cifs_spnego.c6
-rw-r--r--fs/cifs/cifsencrypt.c48
-rw-r--r--fs/cifs/cifsencrypt.h3
-rw-r--r--fs/cifs/cifsfs.c82
-rw-r--r--fs/cifs/cifsfs.h3
-rw-r--r--fs/cifs/cifsglob.h21
-rw-r--r--fs/cifs/cifspdu.h2
-rw-r--r--fs/cifs/cifsproto.h13
-rw-r--r--fs/cifs/cifssmb.c56
-rw-r--r--fs/cifs/connect.c740
-rw-r--r--fs/cifs/dir.c350
-rw-r--r--fs/cifs/fcntl.c118
-rw-r--r--fs/cifs/file.c27
-rw-r--r--fs/cifs/inode.c173
-rw-r--r--fs/cifs/ioctl.c2
-rw-r--r--fs/cifs/md5.c38
-rw-r--r--fs/cifs/md5.h6
-rw-r--r--fs/cifs/misc.c13
-rw-r--r--fs/cifs/readdir.c58
-rw-r--r--fs/cifs/sess.c96
-rw-r--r--fs/cifs/smbdes.c5
-rw-r--r--fs/cifs/smbencrypt.c9
-rw-r--r--fs/cifs/transport.c505
29 files changed, 1392 insertions, 1077 deletions
diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS
index 9c136d7803d9..7f7fa3c302af 100644
--- a/fs/cifs/AUTHORS
+++ b/fs/cifs/AUTHORS
@@ -36,7 +36,9 @@ Miklos Szeredi
Kazeon team for various fixes especially for 2.4 version.
Asser Ferno (Change Notify support)
Shaggy (Dave Kleikamp) for inumerable small fs suggestions and some good cleanup
+Gunter Kukkukk (testing and suggestions for support of old servers)
Igor Mammedov (DFS support)
+Jeff Layton (many, many fixes, as well as great work on the cifs Kerberos code)
Test case and Bug Report contributors
-------------------------------------
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index e078b7aea143..851388fafc73 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,3 +1,27 @@
+Version 1.57
+------------
+Improve support for multiple security contexts to the same server. We
+used to use the same "vcnumber" for all connections which could cause
+the server to treat subsequent connections, especially those that
+are authenticated as guest, as reconnections, invalidating the earlier
+user's smb session. This fix allows cifs to mount multiple times to the
+same server with different userids without risking invalidating earlier
+established security contexts.
+
+Version 1.56
+------------
+Add "forcemandatorylock" mount option to allow user to use mandatory
+rather than posix (advisory) byte range locks, even though server would
+support posix byte range locks. Fix query of root inode when prefixpath
+specified and user does not have access to query information about the
+top of the share. Fix problem in 2.6.28 resolving DFS paths to
+Samba servers (worked to Windows). Fix rmdir so that pending search
+(readdir) requests do not get invalid results which include the now
+removed directory. Fix oops in cifs_dfs_ref.c when prefixpath is not reachable
+when using DFS. Add better file create support to servers which support
+the CIFS POSIX protocol extensions (this adds support for new flags
+on create, and improves semantics for write of locked ranges).
+
Version 1.55
------------
Various fixes to make delete of open files behavior more predictable
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index 6ba43fb346fb..9948c0030e86 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_CIFS) += cifs.o
cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o \
- md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o \
+ md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o \
readdir.o ioctl.o sess.o export.o cifsacl.o
cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
diff --git a/fs/cifs/README b/fs/cifs/README
index a439dc1739b3..da4515e3be20 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -463,9 +463,19 @@ A partial list of the supported mount options follows:
with cifs style mandatory byte range locks (and most
cifs servers do not yet support requesting advisory
byte range locks).
+ forcemandatorylock Even if the server supports posix (advisory) byte range
+ locking, send only mandatory lock requests. For some
+ (presumably rare) applications, originally coded for
+ DOS/Windows, which require Windows style mandatory byte range
+ locking, they may be able to take advantage of this option,
+ forcing the cifs client to only send mandatory locks
+ even if the cifs server would support posix advisory locks.
+ "forcemand" is accepted as a shorter form of this mount
+ option.
nodfs Disable DFS (global name space support) even if the
server claims to support it. This can help work around
- a problem with parsing of DFS paths with Samba 3.0.24 server.
+ a problem with parsing of DFS paths with Samba server
+ versions 3.0.24 and 3.0.25.
remount remount the share (often used to change from ro to rw mounts
or vice versa)
cifsacl Report mode bits (e.g. on stat) based on the Windows ACL for
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index e1c18362ba46..85c0a74d034d 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -122,7 +122,7 @@ static char *compose_mount_options(const char *sb_mountdata,
char **devname)
{
int rc;
- char *mountdata;
+ char *mountdata = NULL;
int md_len;
char *tkn_e;
char *srvIP = NULL;
@@ -136,10 +136,9 @@ static char *compose_mount_options(const char *sb_mountdata,
*devname = cifs_get_share_name(ref->node_name);
rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
if (rc != 0) {
- cERROR(1, ("%s: Failed to resolve server part of %s to IP",
- __func__, *devname));
- mountdata = ERR_PTR(rc);
- goto compose_mount_options_out;
+ cERROR(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
@@ -149,8 +148,8 @@ static char *compose_mount_options(const char *sb_mountdata,
strlen(ref->node_name) + 12;
mountdata = kzalloc(md_len+1, GFP_KERNEL);
if (mountdata == NULL) {
- mountdata = ERR_PTR(-ENOMEM);
- goto compose_mount_options_out;
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
}
/* copy all options except of unc,ip,prefixpath */
@@ -197,18 +196,32 @@ static char *compose_mount_options(const char *sb_mountdata,
/* find & copy prefixpath */
tkn_e = strchr(ref->node_name + 2, '\\');
- if (tkn_e == NULL) /* invalid unc, missing share name*/
- goto compose_mount_options_out;
+ if (tkn_e == NULL) {
+ /* invalid unc, missing share name*/
+ rc = -EINVAL;
+ goto compose_mount_options_err;
+ }
+ /*
+ * this function gives us a path with a double backslash prefix. We
+ * require a single backslash for DFS. Temporarily increment fullpath
+ * to put it in the proper form and decrement before freeing it.
+ */
fullpath = build_path_from_dentry(dentry);
+ if (!fullpath) {
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
+ }
+ ++fullpath;
tkn_e = strchr(tkn_e + 1, '\\');
- if (tkn_e || strlen(fullpath) - (ref->path_consumed)) {
+ if (tkn_e || (strlen(fullpath) - ref->path_consumed)) {
strncat(mountdata, &sep, 1);
strcat(mountdata, "prefixpath=");
if (tkn_e)
strcat(mountdata, tkn_e + 1);
- strcat(mountdata, fullpath + (ref->path_consumed));
+ strcat(mountdata, fullpath + ref->path_consumed);
}
+ --fullpath;
kfree(fullpath);
/*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/
@@ -217,6 +230,11 @@ static char *compose_mount_options(const char *sb_mountdata,
compose_mount_options_out:
kfree(srvIP);
return mountdata;
+
+compose_mount_options_err:
+ kfree(mountdata);
+ mountdata = ERR_PTR(rc);
+ goto compose_mount_options_out;
}
@@ -309,13 +327,19 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
goto out_err;
}
+ /*
+ * The MSDFS spec states that paths in DFS referral requests and
+ * responses must be prefixed by a single '\' character instead of
+ * the double backslashes usually used in the UNC. This function
+ * gives us the latter, so we must adjust the result.
+ */
full_path = build_path_from_dentry(dentry);
if (full_path == NULL) {
rc = -ENOMEM;
goto out_err;
}
- rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls,
+ rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls,
&num_referrals, &referrals,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 877c85409f1f..c4c306f7b06f 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -19,8 +19,8 @@
#define _CIFS_FS_SB_H
#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */
-#define CIFS_MOUNT_SET_UID 2 /* set current->euid in create etc. */
-#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */
+#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */
+#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */
#define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */
#define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */
#define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */
@@ -30,7 +30,8 @@
#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */
#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */
#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */
-#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */
+#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */
+#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */
struct cifs_sb_info {
struct cifsTconInfo *tcon; /* primary mount */
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index 0ab2fb5afef1..3fd3a9df043a 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -121,11 +121,9 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
/* add the server address */
if (server->addr.sockAddr.sin_family == AF_INET)
- sprintf(dp, "ip4=" NIPQUAD_FMT,
- NIPQUAD(server->addr.sockAddr.sin_addr));
+ sprintf(dp, "ip4=%pI4", &server->addr.sockAddr.sin_addr);
else if (server->addr.sockAddr.sin_family == AF_INET6)
- sprintf(dp, "ip6=" NIP6_SEQFMT,
- NIP6(server->addr.sockAddr6.sin6_addr));
+ sprintf(dp, "ip6=%pi6", &server->addr.sockAddr6.sin6_addr);
else
goto out;
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index bd5f13d38450..7c9809523f42 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -37,7 +37,7 @@
extern void mdfour(unsigned char *out, unsigned char *in, int n);
extern void E_md4hash(const unsigned char *passwd, unsigned char *p16);
-extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
+extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8,
unsigned char *p24);
static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
@@ -48,11 +48,11 @@ static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
if ((cifs_pdu == NULL) || (signature == NULL) || (key == NULL))
return -EINVAL;
- MD5Init(&context);
- MD5Update(&context, (char *)&key->data, key->len);
- MD5Update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length);
+ cifs_MD5_init(&context);
+ cifs_MD5_update(&context, (char *)&key->data, key->len);
+ cifs_MD5_update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length);
- MD5Final(signature, &context);
+ cifs_MD5_final(signature, &context);
return 0;
}
@@ -96,8 +96,8 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec,
if ((iov == NULL) || (signature == NULL) || (key == NULL))
return -EINVAL;
- MD5Init(&context);
- MD5Update(&context, (char *)&key->data, key->len);
+ cifs_MD5_init(&context);
+ cifs_MD5_update(&context, (char *)&key->data, key->len);
for (i = 0; i < n_vec; i++) {
if (iov[i].iov_len == 0)
continue;
@@ -110,13 +110,13 @@ 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 */
- MD5Update(&context, iov[0].iov_base+4,
+ cifs_MD5_update(&context, iov[0].iov_base+4,
iov[0].iov_len-4);
} else
- MD5Update(&context, iov[i].iov_base, iov[i].iov_len);
+ cifs_MD5_update(&context, iov[i].iov_base, iov[i].iov_len);
}
- MD5Final(signature, &context);
+ cifs_MD5_final(signature, &context);
return 0;
}
@@ -280,25 +280,22 @@ int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *ses,
}
#ifdef CONFIG_CIFS_WEAK_PW_HASH
-void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key)
+void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,
+ char *lnm_session_key)
{
int i;
char password_with_pad[CIFS_ENCPWD_SIZE];
- if (ses->server == NULL)
- return;
-
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
- if (ses->password)
- strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
-
- if ((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0)
- if (extended_security & CIFSSEC_MAY_PLNTXT) {
- memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE);
- memcpy(lnm_session_key, password_with_pad,
- CIFS_ENCPWD_SIZE);
- return;
- }
+ if (password)
+ strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE);
+
+ if (!encrypt && extended_security & CIFSSEC_MAY_PLNTXT) {
+ memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE);
+ memcpy(lnm_session_key, password_with_pad,
+ CIFS_ENCPWD_SIZE);
+ return;
+ }
/* calculate old style session key */
/* calling toupper is less broken than repeatedly
@@ -314,7 +311,8 @@ void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key)
for (i = 0; i < CIFS_ENCPWD_SIZE; i++)
password_with_pad[i] = toupper(password_with_pad[i]);
- SMBencrypt(password_with_pad, ses->server->cryptKey, lnm_session_key);
+ SMBencrypt(password_with_pad, cryptkey, lnm_session_key);
+
/* clear password before we return/free memory */
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
}
diff --git a/fs/cifs/cifsencrypt.h b/fs/cifs/cifsencrypt.h
index 152fa2dcfc6c..15d2ec006474 100644
--- a/fs/cifs/cifsencrypt.h
+++ b/fs/cifs/cifsencrypt.h
@@ -26,7 +26,8 @@
extern void mdfour(unsigned char *out, unsigned char *in, int n);
/* smbdes.c */
extern void E_P16(unsigned char *p14, unsigned char *p16);
-extern void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24);
+extern void E_P24(unsigned char *p21, const unsigned char *c8,
+ unsigned char *p24);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index d9cf467309e8..13ea53251dcf 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -66,7 +66,9 @@ unsigned int sign_CIFS_PDUs = 1;
extern struct task_struct *oplockThread; /* remove sparse warning */
struct task_struct *oplockThread = NULL;
/* extern struct task_struct * dnotifyThread; remove sparse warning */
+#ifdef CONFIG_CIFS_EXPERIMENTAL
static struct task_struct *dnotifyThread = NULL;
+#endif
static const struct super_operations cifs_super_ops;
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
module_param(CIFSMaxBufSize, int, 0);
@@ -337,39 +339,58 @@ static int
cifs_show_options(struct seq_file *s, struct vfsmount *m)
{
struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *tcon;
+ struct TCP_Server_Info *server;
cifs_sb = CIFS_SB(m->mnt_sb);
if (cifs_sb) {
- if (cifs_sb->tcon) {
-/* BB add prepath to mount options displayed */
+ tcon = cifs_sb->tcon;
+ if (tcon) {
seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName);
- if (cifs_sb->tcon->ses) {
- if (cifs_sb->tcon->ses->userName)
+ if (tcon->ses) {
+ if (tcon->ses->userName)
seq_printf(s, ",username=%s",
- cifs_sb->tcon->ses->userName);
- if (cifs_sb->tcon->ses->domainName)
+ tcon->ses->userName);
+ if (tcon->ses->domainName)
seq_printf(s, ",domain=%s",
- cifs_sb->tcon->ses->domainName);
+ tcon->ses->domainName);
+ server = tcon->ses->server;
+ if (server) {
+ seq_printf(s, ",addr=");
+ switch (server->addr.sockAddr6.
+ sin6_family) {
+ case AF_INET6:
+ seq_printf(s, "%pI6",
+ &server->addr.sockAddr6.sin6_addr);
+ break;
+ case AF_INET:
+ seq_printf(s, "%pI4",
+ &server->addr.sockAddr.sin_addr.s_addr);
+ break;
+ }
+ }
}
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) ||
- !(cifs_sb->tcon->unix_ext))
+ !(tcon->unix_ext))
seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) ||
- !(cifs_sb->tcon->unix_ext))
+ !(tcon->unix_ext))
seq_printf(s, ",gid=%d", cifs_sb->mnt_gid);
- if (!cifs_sb->tcon->unix_ext) {
+ if (!tcon->unix_ext) {
seq_printf(s, ",file_mode=0%o,dir_mode=0%o",
cifs_sb->mnt_file_mode,
cifs_sb->mnt_dir_mode);
}
- if (cifs_sb->tcon->seal)
+ if (tcon->seal)
seq_printf(s, ",seal");
- if (cifs_sb->tcon->nocase)
+ if (tcon->nocase)
seq_printf(s, ",nocase");
- if (cifs_sb->tcon->retry)
+ if (tcon->retry)
seq_printf(s, ",hard");
}
+ if (cifs_sb->prepath)
+ seq_printf(s, ",prepath=%s", cifs_sb->prepath);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
seq_printf(s, ",posixpaths");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
@@ -417,9 +438,8 @@ int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid,
xid = GetXid();
if (pTcon) {
cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -441,9 +461,8 @@ int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid,
xid = GetXid();
if (pTcon) {
cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -464,9 +483,8 @@ int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation)
xid = GetXid();
if (pTcon) {
cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -479,17 +497,16 @@ int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats)
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsTconInfo *pTcon;
- if (cifs_sb) {
+ if (cifs_sb)
pTcon = cifs_sb->tcon;
- } else {
+ else
return -EIO;
- }
+
xid = GetXid();
if (pTcon) {
cFYI(1, ("pqstats %p", qstats));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -730,7 +747,6 @@ const struct file_operations cifs_file_ops = {
#endif /* CONFIG_CIFS_POSIX */
#ifdef CONFIG_CIFS_EXPERIMENTAL
- .dir_notify = cifs_dir_notify,
.setlease = cifs_setlease,
#endif /* CONFIG_CIFS_EXPERIMENTAL */
};
@@ -751,7 +767,6 @@ const struct file_operations cifs_file_direct_ops = {
#endif /* CONFIG_CIFS_POSIX */
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_EXPERIMENTAL
- .dir_notify = cifs_dir_notify,
.setlease = cifs_setlease,
#endif /* CONFIG_CIFS_EXPERIMENTAL */
};
@@ -772,7 +787,6 @@ const struct file_operations cifs_file_nobrl_ops = {
#endif /* CONFIG_CIFS_POSIX */
#ifdef CONFIG_CIFS_EXPERIMENTAL
- .dir_notify = cifs_dir_notify,
.setlease = cifs_setlease,
#endif /* CONFIG_CIFS_EXPERIMENTAL */
};
@@ -792,7 +806,6 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
#endif /* CONFIG_CIFS_POSIX */
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_EXPERIMENTAL
- .dir_notify = cifs_dir_notify,
.setlease = cifs_setlease,
#endif /* CONFIG_CIFS_EXPERIMENTAL */
};
@@ -801,9 +814,6 @@ const struct file_operations cifs_dir_ops = {
.readdir = cifs_readdir,
.release = cifs_closedir,
.read = generic_read_dir,
-#ifdef CONFIG_CIFS_EXPERIMENTAL
- .dir_notify = cifs_dir_notify,
-#endif /* CONFIG_CIFS_EXPERIMENTAL */
.unlocked_ioctl = cifs_ioctl,
.llseek = generic_file_llseek,
};
@@ -1029,6 +1039,7 @@ static int cifs_oplock_thread(void *dummyarg)
return 0;
}
+#ifdef CONFIG_CIFS_EXPERIMENTAL
static int cifs_dnotify_thread(void *dummyarg)
{
struct list_head *tmp;
@@ -1054,6 +1065,7 @@ static int cifs_dnotify_thread(void *dummyarg)
return 0;
}
+#endif
static int __init
init_cifs(void)
@@ -1131,16 +1143,20 @@ init_cifs(void)
goto out_unregister_dfs_key_type;
}
+#ifdef CONFIG_CIFS_EXPERIMENTAL
dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd");
if (IS_ERR(dnotifyThread)) {
rc = PTR_ERR(dnotifyThread);
cERROR(1, ("error %d create dnotify thread", rc));
goto out_stop_oplock_thread;
}
+#endif
return 0;
+#ifdef CONFIG_CIFS_EXPERIMENTAL
out_stop_oplock_thread:
+#endif
kthread_stop(oplockThread);
out_unregister_dfs_key_type:
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -1179,8 +1195,10 @@ exit_cifs(void)
cifs_destroy_inodecache();
cifs_destroy_mids();
cifs_destroy_request_bufs();
- kthread_stop(oplockThread);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
kthread_stop(dnotifyThread);
+#endif
+ kthread_stop(oplockThread);
}
MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 074de0b5064d..2b1d28a9ee28 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -76,7 +76,6 @@ extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
extern const struct file_operations cifs_dir_ops;
extern int cifs_dir_open(struct inode *inode, struct file *file);
extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
-extern int cifs_dir_notify(struct file *, unsigned long arg);
/* Functions related to dir entries */
extern struct dentry_operations cifs_dentry_ops;
@@ -101,5 +100,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* EXPERIMENTAL */
-#define CIFS_VERSION "1.55"
+#define CIFS_VERSION "1.57"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index c57c0565547f..e004f6db5fc8 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -47,7 +47,11 @@
*/
#define CIFS_MAX_REQ 50
-#define SERVER_NAME_LENGTH 15
+#define RFC1001_NAME_LEN 15
+#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1)
+
+/* currently length of NIP6_FMT */
+#define SERVER_NAME_LENGTH 40
#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1)
/* used to define string lengths for reversing unicode strings */
@@ -125,8 +129,7 @@ struct TCP_Server_Info {
struct list_head smb_ses_list;
int srv_count; /* reference counter */
/* 15 character server name + 0x20 16th byte indicating type = srv */
- char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
- char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];
+ char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
char *hostname; /* hostname portion of UNC string */
struct socket *ssocket;
union {
@@ -151,7 +154,7 @@ struct TCP_Server_Info {
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#endif
enum statusEnum tcpStatus; /* what we think the status is */
- struct semaphore tcpSem;
+ struct mutex srv_mutex;
struct task_struct *tsk;
char server_GUID[16];
char secMode;
@@ -161,9 +164,12 @@ struct TCP_Server_Info {
/* multiplexed reads or writes */
unsigned int maxBuf; /* maxBuf specifies the maximum */
/* message size the server can send or receive for non-raw SMBs */
- unsigned int maxRw; /* maxRw specifies the maximum */
+ unsigned int max_rw; /* maxRw specifies the maximum */
/* message size the server can send or receive for */
/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
+ unsigned int max_vcs; /* maximum number of smb sessions, at least
+ those that can be specified uniquely with
+ vcnumbers */
char sessid[4]; /* unique token id for this session */
/* (returned on Negotiate */
int capabilities; /* allow selective disabling of caps by smb sess */
@@ -171,7 +177,7 @@ struct TCP_Server_Info {
__u16 CurrentMid; /* multiplex id - rotating counter */
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
/* 16th byte of RFC1001 workstation name is always null */
- char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
+ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* needed for CIFS PDU signature */
struct mac_key mac_signing_key;
char ntlmv2_hash[16];
@@ -207,6 +213,7 @@ struct cifsSesInfo {
unsigned overrideSecFlg; /* if non-zero override global sec flags */
__u16 ipc_tid; /* special tid for connection to IPC share */
__u16 flags;
+ __u16 vcnum;
char *serverOS; /* name of operating system underlying server */
char *serverNOS; /* name of network operating system of server */
char *serverDomain; /* security realm of server */
@@ -239,6 +246,7 @@ struct cifsTconInfo {
struct cifsSesInfo *ses; /* pointer to session associated with */
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
char *nativeFileSystem;
+ char *password; /* for share-level security */
__u16 tid; /* The 2 byte tree id */
__u16 Flags; /* optional support bits */
enum statusEnum tidStatus;
@@ -422,7 +430,6 @@ struct mid_q_entry {
unsigned long when_sent; /* time when smb send finished */
unsigned long when_received; /* when demux complete (taken off wire) */
#endif
- struct cifsSesInfo *ses; /* smb was sent to this server */
struct task_struct *tsk; /* task waiting for response */
struct smb_hdr *resp_buf; /* response buffer */
int midState; /* wish this were enum but can not pass to wait_event */
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index d2a073edd1b8..b4e2e9f0ee3d 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -1922,7 +1922,7 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {
/* DFS server target type */
#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */
#define DFS_TYPE_ROOT 0x0001
-
+
/* Referral Entry Flags */
#define DFS_NAME_LIST_REF 0x0200
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 6f21ecb85ce5..083dfc57c7a3 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -35,13 +35,14 @@ extern struct smb_hdr *cifs_buf_get(void);
extern void cifs_buf_release(void *);
extern struct smb_hdr *cifs_small_buf_get(void);
extern void cifs_small_buf_release(void *);
-extern int smb_send(struct socket *, struct smb_hdr *,
- unsigned int /* length */ , struct sockaddr *, bool);
+extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
+ unsigned int /* length */);
extern unsigned int _GetXid(void);
extern void _FreeXid(unsigned int);
-#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current->fsuid));
+#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current_fsuid()));
#define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__func__,curr_xid,(int)rc));}
extern char *build_path_from_dentry(struct dentry *);
+extern char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb);
extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
/* extern void renew_parental_timestamps(struct dentry *direntry);*/
extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
@@ -91,6 +92,9 @@ extern u64 cifs_UnixTimeToNT(struct timespec);
extern __le64 cnvrtDosCifsTm(__u16 date, __u16 time);
extern struct timespec cnvrtDosUnixTm(__u16 date, __u16 time);
+extern void posix_fill_in_inode(struct inode *tmp_inode,
+ FILE_UNIX_BASIC_INFO *pData, int isNewInode);
+extern struct inode *cifs_new_inode(struct super_block *sb, __u64 *inum);
extern int cifs_get_inode_info(struct inode **pinode,
const unsigned char *search_path,
FILE_ALL_INFO *pfile_info,
@@ -330,7 +334,8 @@ extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *);
extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *,
const struct nls_table *);
#ifdef CONFIG_CIFS_WEAK_PW_HASH
-extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key);
+extern void calc_lanman_hash(const char *password, const char *cryptkey,
+ bool encrypt, char *lnm_session_key);
#endif /* CIFS_WEAK_PW_HASH */
extern int CIFSSMBCopy(int xid,
struct cifsTconInfo *source_tcon,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 6d51696dc762..939e2f76b959 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -528,14 +528,15 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
(__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+ server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
/* even though we do not use raw we might as well set this
accurately, in case we ever find a need for it */
if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
- server->maxRw = 0xFF00;
+ server->max_rw = 0xFF00;
server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
} else {
- server->maxRw = 0;/* we do not need to use raw anyway */
+ server->max_rw = 0;/* do not need to use raw anyway */
server->capabilities = CAP_MPX_MODE;
}
tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
@@ -638,7 +639,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
/* probably no need to store and check maxvcs */
server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
- server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
+ server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
server->capabilities = le32_to_cpu(pSMBr->Capabilities);
@@ -1382,13 +1383,13 @@ openRetry:
if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
*pOplock |= CIFS_CREATE_ACTION;
if (pfile_info) {
- memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
- 36 /* CreationTime to Attributes */);
- /* the file_info buf is endian converted by caller */
- pfile_info->AllocationSize = pSMBr->AllocationSize;
- pfile_info->EndOfFile = pSMBr->EndOfFile;
- pfile_info->NumberOfLinks = cpu_to_le32(1);
- pfile_info->DeletePending = 0;
+ memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
+ 36 /* CreationTime to Attributes */);
+ /* the file_info buf is endian converted by caller */
+ pfile_info->AllocationSize = pSMBr->AllocationSize;
+ pfile_info->EndOfFile = pSMBr->EndOfFile;
+ pfile_info->NumberOfLinks = cpu_to_le32(1);
+ pfile_info->DeletePending = 0;
}
}
@@ -1414,8 +1415,13 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
if (tcon->ses->capabilities & CAP_LARGE_FILES)
wct = 12;
- else
+ else {
wct = 10; /* old style read */
+ if ((lseek >> 32) > 0) {
+ /* can not handle this big offset for old */
+ return -EIO;
+ }
+ }
*nbytes = 0;
rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
@@ -1431,8 +1437,6 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
if (wct == 12)
pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
- else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
- return -EIO;
pSMB->Remaining = 0;
pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
@@ -1519,8 +1523,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
if (tcon->ses->capabilities & CAP_LARGE_FILES)
wct = 14;
- else
+ else {
wct = 12;
+ if ((offset >> 32) > 0) {
+ /* can not handle big offset for old srv */
+ return -EIO;
+ }
+ }
rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
(void **) &pSMBr);
@@ -1535,8 +1544,6 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
if (wct == 14)
pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
- else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
- return -EIO;
pSMB->Reserved = 0xFFFFFFFF;
pSMB->WriteMode = 0;
@@ -1558,7 +1565,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
pSMB->DataOffset =
cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
if (buf)
- memcpy(pSMB->Data, buf, bytes_sent);
+ memcpy(pSMB->Data, buf, bytes_sent);
else if (ubuf) {
if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
cifs_buf_release(pSMB);
@@ -1621,10 +1628,15 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
- if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ if (tcon->ses->capabilities & CAP_LARGE_FILES) {
wct = 14;
- else
+ } else {
wct = 12;
+ if ((offset >> 32) > 0) {
+ /* can not handle big offset for old srv */
+ return -EIO;
+ }
+ }
rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
if (rc)
return rc;
@@ -1637,8 +1649,6 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
if (wct == 14)
pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
- else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
- return -EIO;
pSMB->Reserved = 0xFFFFFFFF;
pSMB->WriteMode = 0;
pSMB->Remaining = 0;
@@ -1862,10 +1872,6 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
rc = -EIO; /* bad smb */
goto plk_err_exit;
}
- if (pLockData == NULL) {
- rc = -EINVAL;
- goto plk_err_exit;
- }
data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
data_count = le16_to_cpu(pSMBr->t2.DataCount);
if (data_count < sizeof(struct cifs_posix_lock)) {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index c7d341714586..da0f4ffa0613 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -23,7 +23,6 @@
#include <linux/string.h>
#include <linux/list.h>
#include <linux/wait.h>
-#include <linux/ipv6.h>
#include <linux/pagemap.h>
#include <linux/ctype.h>
#include <linux/utsname.h>
@@ -35,6 +34,7 @@
#include <linux/freezer.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
+#include <net/ipv6.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
@@ -89,6 +89,7 @@ struct smb_vol {
bool nullauth:1; /* attempt to authenticate with null user */
bool nocase:1; /* request case insensitive filenames */
bool nobrl:1; /* disable sending byte range locks to srv */
+ bool mand_lock:1; /* send mandatory not posix byte range lock reqs */
bool seal:1; /* request transport encryption on share */
bool nodfs:1; /* Do not request DFS, even if available */
bool local_lease:1; /* check leases only on local system, not remote */
@@ -101,25 +102,17 @@ struct smb_vol {
char *prepath;
};
-static int ipv4_connect(struct sockaddr_in *psin_server,
- struct socket **csocket,
- char *netb_name,
- char *server_netb_name,
- bool noblocksnd,
- bool nosndbuf); /* ipv6 never set sndbuf size */
-static int ipv6_connect(struct sockaddr_in6 *psin_server,
- struct socket **csocket, bool noblocksnd);
-
-
- /*
- * cifs tcp session reconnection
- *
- * mark tcp session as reconnecting so temporarily locked
- * mark all smb sessions as reconnecting for tcp session
- * reconnect tcp session
- * wake up waiters on reconnection? - (not needed currently)
- */
+static int ipv4_connect(struct TCP_Server_Info *server);
+static int ipv6_connect(struct TCP_Server_Info *server);
+/*
+ * cifs tcp session reconnection
+ *
+ * mark tcp session as reconnecting so temporarily locked
+ * mark all smb sessions as reconnecting for tcp session
+ * reconnect tcp session
+ * wake up waiters on reconnection? - (not needed currently)
+ */
static int
cifs_reconnect(struct TCP_Server_Info *server)
{
@@ -156,7 +149,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
}
read_unlock(&cifs_tcp_ses_lock);
/* do not want to be sending data on a socket we are freeing */
- down(&server->tcpSem);
+ mutex_lock(&server->srv_mutex);
if (server->ssocket) {
cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
server->ssocket->flags));
@@ -182,21 +175,15 @@ cifs_reconnect(struct TCP_Server_Info *server)
}
}
spin_unlock(&GlobalMid_Lock);
- up(&server->tcpSem);
+ mutex_unlock(&server->srv_mutex);
while ((server->tcpStatus != CifsExiting) &&
(server->tcpStatus != CifsGood)) {
try_to_freeze();
- if (server->addr.sockAddr6.sin6_family == AF_INET6) {
- rc = ipv6_connect(&server->addr.sockAddr6,
- &server->ssocket, server->noautotune);
- } else {
- rc = ipv4_connect(&server->addr.sockAddr,
- &server->ssocket,
- server->workstation_RFC1001_name,
- server->server_RFC1001_name,
- server->noblocksnd, server->noautotune);
- }
+ if (server->addr.sockAddr6.sin6_family == AF_INET6)
+ rc = ipv6_connect(server);
+ else
+ rc = ipv4_connect(server);
if (rc) {
cFYI(1, ("reconnect error %d", rc));
msleep(3000);
@@ -776,7 +763,7 @@ multi_t2_fnd:
set_current_state(TASK_RUNNING);
}
- return 0;
+ module_put_and_exit(0);
}
/* extract the host portion of the UNC string */
@@ -836,8 +823,8 @@ cifs_parse_mount_options(char *options, const char *devname,
/* null target name indicates to use *SMBSERVR default called name
if we end up sending RFC1001 session initialize */
vol->target_rfc1001_name[0] = 0;
- vol->linux_uid = current->uid; /* current->euid instead? */
- vol->linux_gid = current->gid;
+ vol->linux_uid = current_uid(); /* use current_euid() instead? */
+ vol->linux_gid = current_gid();
vol->dir_mode = S_IRWXUGO;
/* 2767 perms indicate mandatory locking support */
vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
@@ -1260,6 +1247,17 @@ cifs_parse_mount_options(char *options, const char *devname,
if (vol->file_mode ==
(S_IALLUGO & ~(S_ISUID | S_IXGRP)))
vol->file_mode = S_IALLUGO;
+ } else if (strnicmp(data, "forcemandatorylock", 9) == 0) {
+ /* will take the shorter form "forcemand" as well */
+ /* This mount option will force use of mandatory
+ (DOS/Windows style) byte range locks, instead of
+ using posix advisory byte range locks, even if the
+ Unix extensions are available and posix locks would
+ be supported otherwise. If Unix extensions are not
+ negotiated this has no effect since mandatory locks
+ would be used (mandatory locks is all that those
+ those servers support) */
+ vol->mand_lock = 1;
} else if (strnicmp(data, "setuids", 7) == 0) {
vol->setuids = 1;
} else if (strnicmp(data, "nosetuids", 9) == 0) {
@@ -1356,7 +1354,7 @@ cifs_parse_mount_options(char *options, const char *devname,
}
static struct TCP_Server_Info *
-cifs_find_tcp_session(struct sockaddr *addr)
+cifs_find_tcp_session(struct sockaddr_storage *addr)
{
struct list_head *tmp;
struct TCP_Server_Info *server;
@@ -1376,13 +1374,13 @@ cifs_find_tcp_session(struct sockaddr *addr)
if (server->tcpStatus == CifsNew)
continue;
- if (addr->sa_family == AF_INET &&
+ if (addr->ss_family == AF_INET &&
(addr4->sin_addr.s_addr !=
server->addr.sockAddr.sin_addr.s_addr))
continue;
- else if (addr->sa_family == AF_INET6 &&
- memcmp(&server->addr.sockAddr6.sin6_addr,
- &addr6->sin6_addr, sizeof(addr6->sin6_addr)))
+ else if (addr->ss_family == AF_INET6 &&
+ !ipv6_addr_equal(&server->addr.sockAddr6.sin6_addr,
+ &addr6->sin6_addr))
continue;
++server->srv_count;
@@ -1417,6 +1415,143 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
force_sig(SIGKILL, task);
}
+static struct TCP_Server_Info *
+cifs_get_tcp_session(struct smb_vol *volume_info)
+{
+ struct TCP_Server_Info *tcp_ses = NULL;
+ struct sockaddr_storage addr;
+ struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
+ struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
+ int rc;
+
+ memset(&addr, 0, sizeof(struct sockaddr_storage));
+
+ if (volume_info->UNCip && volume_info->UNC) {
+ rc = cifs_inet_pton(AF_INET, volume_info->UNCip,
+ &sin_server->sin_addr.s_addr);
+
+ if (rc <= 0) {
+ /* not ipv4 address, try ipv6 */
+ rc = cifs_inet_pton(AF_INET6, volume_info->UNCip,
+ &sin_server6->sin6_addr.in6_u);
+ if (rc > 0)
+ addr.ss_family = AF_INET6;
+ } else {
+ addr.ss_family = AF_INET;
+ }
+
+ if (rc <= 0) {
+ /* we failed translating address */
+ rc = -EINVAL;
+ goto out_err;
+ }
+
+ cFYI(1, ("UNC: %s ip: %s", volume_info->UNC,
+ volume_info->UNCip));
+ } else if (volume_info->UNCip) {
+ /* BB using ip addr as tcp_ses name to connect to the
+ DFS root below */
+ cERROR(1, ("Connecting to DFS root not implemented yet"));
+ rc = -EINVAL;
+ goto out_err;
+ } else /* which tcp_sess DFS root would we conect to */ {
+ cERROR(1,
+ ("CIFS mount error: No UNC path (e.g. -o "
+ "unc=//192.168.1.100/public) specified"));
+ rc = -EINVAL;
+ goto out_err;
+ }
+
+ /* see if we already have a matching tcp_ses */
+ tcp_ses = cifs_find_tcp_session(&addr);
+ if (tcp_ses)
+ return tcp_ses;
+
+ tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
+ if (!tcp_ses) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+
+ tcp_ses->hostname = extract_hostname(volume_info->UNC);
+ if (IS_ERR(tcp_ses->hostname)) {
+ rc = PTR_ERR(tcp_ses->hostname);
+ goto out_err;
+ }
+
+ tcp_ses->noblocksnd = volume_info->noblocksnd;
+ tcp_ses->noautotune = volume_info->noautotune;
+ atomic_set(&tcp_ses->inFlight, 0);
+ init_waitqueue_head(&tcp_ses->response_q);
+ init_waitqueue_head(&tcp_ses->request_q);
+ INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
+ mutex_init(&tcp_ses->srv_mutex);
+ memcpy(tcp_ses->workstation_RFC1001_name,
+ volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+ memcpy(tcp_ses->server_RFC1001_name,
+ volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+ tcp_ses->sequence_number = 0;
+ INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
+ INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
+
+ /*
+ * at this point we are the only ones with the pointer
+ * to the struct since the kernel thread not created yet
+ * no need to spinlock this init of tcpStatus or srv_count
+ */
+ tcp_ses->tcpStatus = CifsNew;
+ ++tcp_ses->srv_count;
+
+ if (addr.ss_family == AF_INET6) {
+ cFYI(1, ("attempting ipv6 connect"));
+ /* BB should we allow ipv6 on port 139? */
+ /* other OS never observed in Wild doing 139 with v6 */
+ memcpy(&tcp_ses->addr.sockAddr6, sin_server6,
+ sizeof(struct sockaddr_in6));
+ sin_server6->sin6_port = htons(volume_info->port);
+ rc = ipv6_connect(tcp_ses);
+ } else {
+ memcpy(&tcp_ses->addr.sockAddr, sin_server,
+ sizeof(struct sockaddr_in));
+ sin_server->sin_port = htons(volume_info->port);
+ rc = ipv4_connect(tcp_ses);
+ }
+ if (rc < 0) {
+ cERROR(1, ("Error connecting to socket. Aborting operation"));
+ goto out_err;
+ }
+
+ /*
+ * since we're in a cifs function already, we know that
+ * this will succeed. No need for try_module_get().
+ */
+ __module_get(THIS_MODULE);
+ tcp_ses->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread,
+ tcp_ses, "cifsd");
+ if (IS_ERR(tcp_ses->tsk)) {
+ rc = PTR_ERR(tcp_ses->tsk);
+ cERROR(1, ("error %d create cifsd thread", rc));
+ module_put(THIS_MODULE);
+ goto out_err;
+ }
+
+ /* thread spawned, put it on the list */
+ write_lock(&cifs_tcp_ses_lock);
+ list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
+ write_unlock(&cifs_tcp_ses_lock);
+
+ return tcp_ses;
+
+out_err:
+ if (tcp_ses) {
+ kfree(tcp_ses->hostname);
+ if (tcp_ses->ssocket)
+ sock_release(tcp_ses->ssocket);
+ kfree(tcp_ses);
+ }
+ return ERR_PTR(rc);
+}
+
static struct cifsSesInfo *
cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
{
@@ -1593,93 +1728,96 @@ static void rfc1002mangle(char *target, char *source, unsigned int length)
static int
-ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
- char *netbios_name, char *target_name,
- bool noblocksnd, bool noautotune)
+ipv4_connect(struct TCP_Server_Info *server)
{
int rc = 0;
- int connected = 0;
+ bool connected = false;
__be16 orig_port = 0;
+ struct socket *socket = server->ssocket;
- if (*csocket == NULL) {
+ if (socket == NULL) {
rc = sock_create_kern(PF_INET, SOCK_STREAM,
- IPPROTO_TCP, csocket);
+ IPPROTO_TCP, &socket);
if (rc < 0) {
cERROR(1, ("Error %d creating socket", rc));
- *csocket = NULL;
return rc;
- } else {
- /* BB other socket options to set KEEPALIVE, NODELAY? */
- cFYI(1, ("Socket created"));
- (*csocket)->sk->sk_allocation = GFP_NOFS;
- cifs_reclassify_socket4(*csocket);
}
+
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1, ("Socket created"));
+ server->ssocket = socket;
+ socket->sk->sk_allocation = GFP_NOFS;
+ cifs_reclassify_socket4(socket);
}
- psin_server->sin_family = AF_INET;
- if (psin_server->sin_port) { /* user overrode default port */
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
- sizeof(struct sockaddr_in), 0);
+ /* user overrode default port */
+ if (server->addr.sockAddr.sin_port) {
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr,
+ sizeof(struct sockaddr_in), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
if (!connected) {
/* save original port so we can retry user specified port
later if fall back ports fail this time */
- orig_port = psin_server->sin_port;
+ orig_port = server->addr.sockAddr.sin_port;
/* do not retry on the same port we just failed on */
- if (psin_server->sin_port != htons(CIFS_PORT)) {
- psin_server->sin_port = htons(CIFS_PORT);
-
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
- sizeof(struct sockaddr_in), 0);
+ if (server->addr.sockAddr.sin_port != htons(CIFS_PORT)) {
+ server->addr.sockAddr.sin_port = htons(CIFS_PORT);
+ rc = socket->ops->connect(socket,
+ (struct sockaddr *)
+ &server->addr.sockAddr,
+ sizeof(struct sockaddr_in), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
}
if (!connected) {
- psin_server->sin_port = htons(RFC1001_PORT);
- rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
- psin_server,
+ server->addr.sockAddr.sin_port = htons(RFC1001_PORT);
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr,
sizeof(struct sockaddr_in), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
/* give up here - unless we want to retry on different
protocol families some day */
if (!connected) {
if (orig_port)
- psin_server->sin_port = orig_port;
+ server->addr.sockAddr.sin_port = orig_port;
cFYI(1, ("Error %d connecting to server via ipv4", rc));
- sock_release(*csocket);
- *csocket = NULL;
+ sock_release(socket);
+ server->ssocket = NULL;
return rc;
}
- /* Eventually check for other socket options to change from
- the default. sock_setsockopt not used because it expects
- user space buffer */
- cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",
- (*csocket)->sk->sk_sndbuf,
- (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo));
- (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
- if (!noblocksnd)
- (*csocket)->sk->sk_sndtimeo = 3 * HZ;
+
+
+ /*
+ * Eventually check for other socket options to change from
+ * the default. sock_setsockopt not used because it expects
+ * user space buffer
+ */
+ socket->sk->sk_rcvtimeo = 7 * HZ;
+ socket->sk->sk_sndtimeo = 5 * HZ;
/* make the bufsizes depend on wsize/rsize and max requests */
- if (noautotune) {
- if ((*csocket)->sk->sk_sndbuf < (200 * 1024))
- (*csocket)->sk->sk_sndbuf = 200 * 1024;
- if ((*csocket)->sk->sk_rcvbuf < (140 * 1024))
- (*csocket)->sk->sk_rcvbuf = 140 * 1024;
+ if (server->noautotune) {
+ if (socket->sk->sk_sndbuf < (200 * 1024))
+ socket->sk->sk_sndbuf = 200 * 1024;
+ if (socket->sk->sk_rcvbuf < (140 * 1024))
+ socket->sk->sk_rcvbuf = 140 * 1024;
}
+ cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",
+ socket->sk->sk_sndbuf,
+ socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo));
+
/* send RFC1001 sessinit */
- if (psin_server->sin_port == htons(RFC1001_PORT)) {
+ if (server->addr.sockAddr.sin_port == htons(RFC1001_PORT)) {
/* some servers require RFC1001 sessinit before sending
negprot - BB check reconnection in case where second
sessinit is sent but no second negprot */
@@ -1689,31 +1827,40 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
GFP_KERNEL);
if (ses_init_buf) {
ses_init_buf->trailer.session_req.called_len = 32;
- if (target_name && (target_name[0] != 0)) {
- rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
- target_name, 16);
- } else {
- rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
- DEFAULT_CIFS_CALLED_NAME, 16);
- }
+ if (server->server_RFC1001_name &&
+ server->server_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ server->server_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ DEFAULT_CIFS_CALLED_NAME,
+ RFC1001_NAME_LEN_WITH_NULL);
ses_init_buf->trailer.session_req.calling_len = 32;
+
/* calling name ends in null (byte 16) from old smb
convention. */
- if (netbios_name && (netbios_name[0] != 0)) {
- rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
- netbios_name, 16);
- } else {
- rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
- "LINUX_CIFS_CLNT", 16);
- }
+ if (server->workstation_RFC1001_name &&
+ server->workstation_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ server->workstation_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ "LINUX_CIFS_CLNT",
+ RFC1001_NAME_LEN_WITH_NULL);
+
ses_init_buf->trailer.session_req.scope1 = 0;
ses_init_buf->trailer.session_req.scope2 = 0;
smb_buf = (struct smb_hdr *)ses_init_buf;
/* sizeof RFC1002_SESSION_REQUEST with no scope */
smb_buf->smb_buf_length = 0x81000044;
- rc = smb_send(*csocket, smb_buf, 0x44,
- (struct sockaddr *)psin_server, noblocksnd);
+ rc = smb_send(server, smb_buf, 0x44);
kfree(ses_init_buf);
msleep(1); /* RFC1001 layer in at least one server
requires very short break before negprot
@@ -1733,79 +1880,81 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
}
static int
-ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket,
- bool noblocksnd)
+ipv6_connect(struct TCP_Server_Info *server)
{
int rc = 0;
- int connected = 0;
+ bool connected = false;
__be16 orig_port = 0;
+ struct socket *socket = server->ssocket;
- if (*csocket == NULL) {
+ if (socket == NULL) {
rc = sock_create_kern(PF_INET6, SOCK_STREAM,
- IPPROTO_TCP, csocket);
+ IPPROTO_TCP, &socket);
if (rc < 0) {
cERROR(1, ("Error %d creating ipv6 socket", rc));
- *csocket = NULL;
+ socket = NULL;
return rc;
- } else {
- /* BB other socket options to set KEEPALIVE, NODELAY? */
- cFYI(1, ("ipv6 Socket created"));
- (*csocket)->sk->sk_allocation = GFP_NOFS;
- cifs_reclassify_socket6(*csocket);
}
- }
- psin_server->sin6_family = AF_INET6;
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1, ("ipv6 Socket created"));
+ server->ssocket = socket;
+ socket->sk->sk_allocation = GFP_NOFS;
+ cifs_reclassify_socket6(socket);
+ }
- if (psin_server->sin6_port) { /* user overrode default port */
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
+ /* user overrode default port */
+ if (server->addr.sockAddr6.sin6_port) {
+ rc = socket->ops->connect(socket,
+ (struct sockaddr *) &server->addr.sockAddr6,
sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
if (!connected) {
/* save original port so we can retry user specified port
later if fall back ports fail this time */
- orig_port = psin_server->sin6_port;
+ orig_port = server->addr.sockAddr6.sin6_port;
/* do not retry on the same port we just failed on */
- if (psin_server->sin6_port != htons(CIFS_PORT)) {
- psin_server->sin6_port = htons(CIFS_PORT);
-
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
+ if (server->addr.sockAddr6.sin6_port != htons(CIFS_PORT)) {
+ server->addr.sockAddr6.sin6_port = htons(CIFS_PORT);
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr6,
sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
}
if (!connected) {
- psin_server->sin6_port = htons(RFC1001_PORT);
- rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
- psin_server, sizeof(struct sockaddr_in6), 0);
+ server->addr.sockAddr6.sin6_port = htons(RFC1001_PORT);
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr6,
+ sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
/* give up here - unless we want to retry on different
protocol families some day */
if (!connected) {
if (orig_port)
- psin_server->sin6_port = orig_port;
+ server->addr.sockAddr6.sin6_port = orig_port;
cFYI(1, ("Error %d connecting to server via ipv6", rc));
- sock_release(*csocket);
- *csocket = NULL;
+ sock_release(socket);
+ server->ssocket = NULL;
return rc;
}
- /* Eventually check for other socket options to change from
- the default. sock_setsockopt not used because it expects
- user space buffer */
- (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
- if (!noblocksnd)
- (*csocket)->sk->sk_sndtimeo = 3 * HZ;
+ /*
+ * Eventually check for other socket options to change from
+ * the default. sock_setsockopt not used because it expects
+ * user space buffer
+ */
+ socket->sk->sk_rcvtimeo = 7 * HZ;
+ socket->sk->sk_sndtimeo = 5 * HZ;
+ server->ssocket = socket;
return rc;
}
@@ -2011,6 +2160,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
if (pvolume_info->nobrl)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+ if (pvolume_info->mand_lock)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL;
if (pvolume_info->cifs_acl)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
if (pvolume_info->override_uid)
@@ -2029,38 +2180,64 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,
"mount option supported"));
}
+static int
+is_path_accessible(int xid, struct cifsTconInfo *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path)
+{
+ int rc;
+ __u64 inode_num;
+ FILE_ALL_INFO *pfile_info;
+
+ rc = CIFSGetSrvInodeNumber(xid, tcon, full_path, &inode_num,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc != -EOPNOTSUPP)
+ return rc;
+
+ pfile_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (pfile_info == NULL)
+ return -ENOMEM;
+
+ rc = CIFSSMBQPathInfo(xid, tcon, full_path, pfile_info,
+ 0 /* not legacy */, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ kfree(pfile_info);
+ return rc;
+}
+
int
cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
char *mount_data, const char *devname)
{
int rc = 0;
int xid;
- struct socket *csocket = NULL;
- struct sockaddr addr;
- struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
- struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
- struct smb_vol volume_info;
+ struct smb_vol *volume_info;
struct cifsSesInfo *pSesInfo = NULL;
struct cifsTconInfo *tcon = NULL;
struct TCP_Server_Info *srvTcp = NULL;
+ char *full_path;
xid = GetXid();
-/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
+ volume_info = kzalloc(sizeof(struct smb_vol), GFP_KERNEL);
+ if (!volume_info) {
+ rc = -ENOMEM;
+ goto out;
+ }
- memset(&addr, 0, sizeof(struct sockaddr));
- memset(&volume_info, 0, sizeof(struct smb_vol));
- if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
+ if (cifs_parse_mount_options(mount_data, devname, volume_info)) {
rc = -EINVAL;
goto out;
}
- if (volume_info.nullauth) {
+ if (volume_info->nullauth) {
cFYI(1, ("null user"));
- volume_info.username = "";
- } else if (volume_info.username) {
+ volume_info->username = "";
+ } else if (volume_info->username) {
/* BB fixme parse for domain name here */
- cFYI(1, ("Username: %s", volume_info.username));
+ cFYI(1, ("Username: %s", volume_info->username));
} else {
cifserror("No username specified");
/* In userspace mount helper we can get user name from alternate
@@ -2069,139 +2246,29 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
goto out;
}
- if (volume_info.UNCip && volume_info.UNC) {
- rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
- &sin_server->sin_addr.s_addr);
-
- if (rc <= 0) {
- /* not ipv4 address, try ipv6 */
- rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
- &sin_server6->sin6_addr.in6_u);
- if (rc > 0)
- addr.sa_family = AF_INET6;
- } else {
- addr.sa_family = AF_INET;
- }
-
- if (rc <= 0) {
- /* we failed translating address */
- rc = -EINVAL;
- goto out;
- }
-
- cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
- /* success */
- rc = 0;
- } else if (volume_info.UNCip) {
- /* BB using ip addr as server name to connect to the
- DFS root below */
- cERROR(1, ("Connecting to DFS root not implemented yet"));
- rc = -EINVAL;
- goto out;
- } else /* which servers DFS root would we conect to */ {
- cERROR(1,
- ("CIFS mount error: No UNC path (e.g. -o "
- "unc=//192.168.1.100/public) specified"));
- rc = -EINVAL;
- goto out;
- }
/* this is needed for ASCII cp to Unicode converts */
- if (volume_info.iocharset == NULL) {
+ if (volume_info->iocharset == NULL) {
cifs_sb->local_nls = load_nls_default();
/* load_nls_default can not return null */
} else {
- cifs_sb->local_nls = load_nls(volume_info.iocharset);
+ cifs_sb->local_nls = load_nls(volume_info->iocharset);
if (cifs_sb->local_nls == NULL) {
cERROR(1, ("CIFS mount error: iocharset %s not found",
- volume_info.iocharset));
+ volume_info->iocharset));
rc = -ELIBACC;
goto out;
}
}
- srvTcp = cifs_find_tcp_session(&addr);
- if (!srvTcp) { /* create socket */
- if (addr.sa_family == AF_INET6) {
- cFYI(1, ("attempting ipv6 connect"));
- /* BB should we allow ipv6 on port 139? */
- /* other OS never observed in Wild doing 139 with v6 */
- sin_server6->sin6_port = htons(volume_info.port);
- rc = ipv6_connect(sin_server6, &csocket,
- volume_info.noblocksnd);
- } else {
- sin_server->sin_port = htons(volume_info.port);
- rc = ipv4_connect(sin_server, &csocket,
- volume_info.source_rfc1001_name,
- volume_info.target_rfc1001_name,
- volume_info.noblocksnd,
- volume_info.noautotune);
- }
- if (rc < 0) {
- cERROR(1, ("Error connecting to socket. "
- "Aborting operation"));
- if (csocket != NULL)
- sock_release(csocket);
- goto out;
- }
-
- srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
- if (!srvTcp) {
- rc = -ENOMEM;
- sock_release(csocket);
- goto out;
- } else {
- srvTcp->noblocksnd = volume_info.noblocksnd;
- srvTcp->noautotune = volume_info.noautotune;
- if (addr.sa_family == AF_INET6)
- memcpy(&srvTcp->addr.sockAddr6, sin_server6,
- sizeof(struct sockaddr_in6));
- else
- memcpy(&srvTcp->addr.sockAddr, sin_server,
- sizeof(struct sockaddr_in));
- atomic_set(&srvTcp->inFlight, 0);
- /* BB Add code for ipv6 case too */
- srvTcp->ssocket = csocket;
- srvTcp->hostname = extract_hostname(volume_info.UNC);
- if (IS_ERR(srvTcp->hostname)) {
- rc = PTR_ERR(srvTcp->hostname);
- sock_release(csocket);
- goto out;
- }
- init_waitqueue_head(&srvTcp->response_q);
- init_waitqueue_head(&srvTcp->request_q);
- INIT_LIST_HEAD(&srvTcp->pending_mid_q);
- /* at this point we are the only ones with the pointer
- to the struct since the kernel thread not created yet
- so no need to spinlock this init of tcpStatus */
- srvTcp->tcpStatus = CifsNew;
- init_MUTEX(&srvTcp->tcpSem);
- srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
- if (IS_ERR(srvTcp->tsk)) {
- rc = PTR_ERR(srvTcp->tsk);
- cERROR(1, ("error %d create cifsd thread", rc));
- srvTcp->tsk = NULL;
- sock_release(csocket);
- kfree(srvTcp->hostname);
- goto out;
- }
- rc = 0;
- memcpy(srvTcp->workstation_RFC1001_name,
- volume_info.source_rfc1001_name, 16);
- memcpy(srvTcp->server_RFC1001_name,
- volume_info.target_rfc1001_name, 16);
- srvTcp->sequence_number = 0;
- INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
- INIT_LIST_HEAD(&srvTcp->smb_ses_list);
- ++srvTcp->srv_count;
- write_lock(&cifs_tcp_ses_lock);
- list_add(&srvTcp->tcp_ses_list,
- &cifs_tcp_ses_list);
- write_unlock(&cifs_tcp_ses_lock);
- }
+ /* get a reference to a tcp session */
+ srvTcp = cifs_get_tcp_session(volume_info);
+ if (IS_ERR(srvTcp)) {
+ rc = PTR_ERR(srvTcp);
+ goto out;
}
- pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
+ pSesInfo = cifs_find_smb_ses(srvTcp, volume_info->username);
if (pSesInfo) {
cFYI(1, ("Existing smb sess found (status=%d)",
pSesInfo->status));
@@ -2228,31 +2295,38 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* new SMB session uses our srvTcp ref */
pSesInfo->server = srvTcp;
- sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
- NIPQUAD(sin_server->sin_addr.s_addr));
+ if (srvTcp->addr.sockAddr6.sin6_family == AF_INET6)
+ sprintf(pSesInfo->serverName, "%pI6",
+ &srvTcp->addr.sockAddr6.sin6_addr);
+ else
+ sprintf(pSesInfo->serverName, "%pI4",
+ &srvTcp->addr.sockAddr.sin_addr.s_addr);
write_lock(&cifs_tcp_ses_lock);
list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
write_unlock(&cifs_tcp_ses_lock);
- /* volume_info.password freed at unmount */
- if (volume_info.password) {
- pSesInfo->password = volume_info.password;
- /* set to NULL to prevent freeing on exit */
- volume_info.password = NULL;
+ /* volume_info->password freed at unmount */
+ if (volume_info->password) {
+ pSesInfo->password = kstrdup(volume_info->password,
+ GFP_KERNEL);
+ if (!pSesInfo->password) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
}
- if (volume_info.username)
- strncpy(pSesInfo->userName, volume_info.username,
+ if (volume_info->username)
+ strncpy(pSesInfo->userName, volume_info->username,
MAX_USERNAME_SIZE);
- if (volume_info.domainname) {
- int len = strlen(volume_info.domainname);
+ if (volume_info->domainname) {
+ int len = strlen(volume_info->domainname);
pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
if (pSesInfo->domainName)
strcpy(pSesInfo->domainName,
- volume_info.domainname);
+ volume_info->domainname);
}
- pSesInfo->linux_uid = volume_info.linux_uid;
- pSesInfo->overrideSecFlg = volume_info.secFlg;
+ pSesInfo->linux_uid = volume_info->linux_uid;
+ pSesInfo->overrideSecFlg = volume_info->secFlg;
down(&pSesInfo->sesSem);
/* BB FIXME need to pass vol->secFlgs BB */
@@ -2263,14 +2337,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* search for existing tcon to this server share */
if (!rc) {
- setup_cifs_sb(&volume_info, cifs_sb);
+ setup_cifs_sb(volume_info, cifs_sb);
- tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
+ tcon = cifs_find_tcon(pSesInfo, volume_info->UNC);
if (tcon) {
cFYI(1, ("Found match on UNC path"));
/* existing tcon already has a reference */
cifs_put_smb_ses(pSesInfo);
- if (tcon->seal != volume_info.seal)
+ if (tcon->seal != volume_info->seal)
cERROR(1, ("transport encryption setting "
"conflicts with existing tid"));
} else {
@@ -2279,11 +2353,20 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
rc = -ENOMEM;
goto mount_fail_check;
}
+
tcon->ses = pSesInfo;
+ if (volume_info->password) {
+ tcon->password = kstrdup(volume_info->password,
+ GFP_KERNEL);
+ if (!tcon->password) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
+ }
/* check for null share name ie connect to dfs root */
- if ((strchr(volume_info.UNC + 3, '\\') == NULL)
- && (strchr(volume_info.UNC + 3, '/') == NULL)) {
+ if ((strchr(volume_info->UNC + 3, '\\') == NULL)
+ && (strchr(volume_info->UNC + 3, '/') == NULL)) {
/* rc = connect_to_dfs_path(...) */
cFYI(1, ("DFS root not supported"));
rc = -ENODEV;
@@ -2292,10 +2375,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* BB Do we need to wrap sesSem around
* this TCon call and Unix SetFS as
* we do on SessSetup and reconnect? */
- rc = CIFSTCon(xid, pSesInfo, volume_info.UNC,
+ rc = CIFSTCon(xid, pSesInfo, volume_info->UNC,
tcon, cifs_sb->local_nls);
cFYI(1, ("CIFS Tcon rc = %d", rc));
- if (volume_info.nodfs) {
+ if (volume_info->nodfs) {
tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
cFYI(1, ("DFS disabled (%d)",
tcon->Flags));
@@ -2303,7 +2386,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
}
if (rc)
goto mount_fail_check;
- tcon->seal = volume_info.seal;
+ tcon->seal = volume_info->seal;
write_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
write_unlock(&cifs_tcp_ses_lock);
@@ -2313,9 +2396,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
to a share so for resources mounted more than once
to the same server share the last value passed in
for the retry flag is used */
- tcon->retry = volume_info.retry;
- tcon->nocase = volume_info.nocase;
- tcon->local_lease = volume_info.local_lease;
+ tcon->retry = volume_info->retry;
+ tcon->nocase = volume_info->nocase;
+ tcon->local_lease = volume_info->local_lease;
}
if (pSesInfo) {
if (pSesInfo->capabilities & CAP_LARGE_FILES) {
@@ -2352,7 +2435,7 @@ mount_fail_check:
if (tcon->ses->capabilities & CAP_UNIX)
/* reset of caps checks mount to see if unix extensions
disabled for just this mount */
- reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+ reset_cifs_unix_caps(xid, tcon, sb, volume_info);
else
tcon->unix_ext = 0; /* server does not support them */
@@ -2371,18 +2454,39 @@ mount_fail_check:
cifs_sb->rsize = min(cifs_sb->rsize,
(tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
- /* volume_info.password is freed above when existing session found
+ if (!rc && cifs_sb->prepathlen) {
+ /* build_path_to_root works only when we have a valid tcon */
+ full_path = cifs_build_path_to_root(cifs_sb);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
+ rc = is_path_accessible(xid, tcon, cifs_sb, full_path);
+ if (rc) {
+ cERROR(1, ("Path %s in not accessible: %d",
+ full_path, rc));
+ kfree(full_path);
+ goto mount_fail_check;
+ }
+ kfree(full_path);
+ }
+
+ /* 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 */
- if (volume_info.password != NULL) {
- memset(volume_info.password, 0, strlen(volume_info.password));
- kfree(volume_info.password);
+ if (volume_info) {
+ if (volume_info->password != NULL) {
+ memset(volume_info->password, 0,
+ strlen(volume_info->password));
+ kfree(volume_info->password);
+ }
+ kfree(volume_info->UNC);
+ kfree(volume_info->prepath);
+ kfree(volume_info);
}
- kfree(volume_info.UNC);
- kfree(volume_info.prepath);
FreeXid(xid);
return rc;
}
@@ -2533,7 +2637,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
__u16 action = le16_to_cpu(pSMBr->resp.Action);
__u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
if (action & GUEST_LOGIN)
- cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */
+ cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */
ses->Suid = smb_buffer_response->Uid; /* UID left in wire format
(little endian) */
cFYI(1, ("UID = %d ", ses->Suid));
@@ -2679,13 +2783,11 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
len));
}
} else {
- cERROR(1,
- (" Security Blob Length extends beyond "
+ cERROR(1, ("Security Blob Length extends beyond "
"end of SMB"));
}
} else {
- cERROR(1,
- (" Invalid Word count %d: ",
+ cERROR(1, ("Invalid Word count %d: ",
smb_buffer_response->WordCount));
rc = -EIO;
}
@@ -2843,7 +2945,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
__u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
if (action & GUEST_LOGIN)
- cFYI(1, (" Guest login"));
+ cFYI(1, ("Guest login"));
/* Do we want to set anything in SesInfo struct when guest login? */
bcc_ptr = pByteArea(smb_buffer_response);
@@ -2851,8 +2953,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr;
if (SecurityBlob2->MessageType != NtLmChallenge) {
- cFYI(1,
- ("Unexpected NTLMSSP message type received %d",
+ cFYI(1, ("Unexpected NTLMSSP message type received %d",
SecurityBlob2->MessageType));
} else if (ses) {
ses->Suid = smb_buffer_response->Uid; /* UID left in le format */
@@ -3024,8 +3125,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
cERROR(1, ("No session structure passed in."));
}
} else {
- cERROR(1,
- (" Invalid Word count %d:",
+ cERROR(1, ("Invalid Word count %d:",
smb_buffer_response->WordCount));
rc = -EIO;
}
@@ -3264,7 +3364,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
__u16 action = le16_to_cpu(pSMBr->resp.Action);
__u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
if (action & GUEST_LOGIN)
- cFYI(1, (" Guest login")); /* BB Should we set anything
+ cFYI(1, ("Guest login")); /* BB Should we set anything
in SesInfo struct ? */
/* if (SecurityBlob2->MessageType != NtLm??) {
cFYI("Unexpected message type on auth response is %d"));
@@ -3487,12 +3587,14 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
NTLMv2 password here) */
#ifdef CONFIG_CIFS_WEAK_PW_HASH
if ((extended_security & CIFSSEC_MAY_LANMAN) &&
- (ses->server->secType == LANMAN))
- calc_lanman_hash(ses, bcc_ptr);
+ (ses->server->secType == LANMAN))
+ calc_lanman_hash(tcon->password, ses->server->cryptKey,
+ ses->server->secMode &
+ SECMODE_PW_ENCRYPT ? true : false,
+ bcc_ptr);
else
#endif /* CIFS_WEAK_PW_HASH */
- SMBNTencrypt(ses->password,
- ses->server->cryptKey,
+ SMBNTencrypt(tcon->password, ses->server->cryptKey,
bcc_ptr);
bcc_ptr += CIFS_SESS_KEY_SIZE;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index e962e75e6f7b..89fb72832652 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -3,7 +3,7 @@
*
* vfs operations that deal with dentries
*
- * Copyright (C) International Business Machines Corp., 2002,2008
+ * Copyright (C) International Business Machines Corp., 2002,2009
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
@@ -129,6 +129,89 @@ cifs_bp_rename_retry:
return full_path;
}
+static int cifs_posix_open(char *full_path, struct inode **pinode,
+ struct super_block *sb, int mode, int oflags,
+ int *poplock, __u16 *pnetfid, int xid)
+{
+ int rc;
+ __u32 oplock;
+ FILE_UNIX_BASIC_INFO *presp_data;
+ __u32 posix_flags = 0;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+
+ cFYI(1, ("posix open %s", full_path));
+
+ presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
+ if (presp_data == NULL)
+ return -ENOMEM;
+
+/* So far cifs posix extensions can only map the following flags.
+ There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but
+ so far we do not seem to need them, and we can treat them as local only */
+ if ((oflags & (FMODE_READ | FMODE_WRITE)) ==
+ (FMODE_READ | FMODE_WRITE))
+ posix_flags = SMB_O_RDWR;
+ else if (oflags & FMODE_READ)
+ posix_flags = SMB_O_RDONLY;
+ else if (oflags & FMODE_WRITE)
+ posix_flags = SMB_O_WRONLY;
+ if (oflags & O_CREAT)
+ posix_flags |= SMB_O_CREAT;
+ if (oflags & O_EXCL)
+ posix_flags |= SMB_O_EXCL;
+ if (oflags & O_TRUNC)
+ posix_flags |= SMB_O_TRUNC;
+ if (oflags & O_APPEND)
+ posix_flags |= SMB_O_APPEND;
+ if (oflags & O_SYNC)
+ posix_flags |= SMB_O_SYNC;
+ if (oflags & O_DIRECTORY)
+ posix_flags |= SMB_O_DIRECTORY;
+ if (oflags & O_NOFOLLOW)
+ posix_flags |= SMB_O_NOFOLLOW;
+ if (oflags & O_DIRECT)
+ posix_flags |= SMB_O_DIRECT;
+
+
+ rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
+ pnetfid, presp_data, &oplock, full_path,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc)
+ goto posix_open_ret;
+
+ if (presp_data->Type == cpu_to_le32(-1))
+ goto posix_open_ret; /* open ok, caller does qpathinfo */
+
+ /* get new inode and set it up */
+ if (!pinode)
+ goto posix_open_ret; /* caller does not need info */
+
+ *pinode = cifs_new_inode(sb, &presp_data->UniqueId);
+
+ /* We do not need to close the file if new_inode fails since
+ the caller will retry qpathinfo as long as inode is null */
+ if (*pinode == NULL)
+ goto posix_open_ret;
+
+ posix_fill_in_inode(*pinode, presp_data, 1);
+
+posix_open_ret:
+ kfree(presp_data);
+ return rc;
+}
+
+static void setup_cifs_dentry(struct cifsTconInfo *tcon,
+ struct dentry *direntry,
+ struct inode *newinode)
+{
+ if (tcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+ d_instantiate(direntry, newinode);
+}
+
/* Inode operations in similar order to how they appear in Linux file fs.h */
int
@@ -139,14 +222,21 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
int xid;
int create_options = CREATE_NOT_DIR;
int oplock = 0;
+ int oflags;
+ /*
+ * BB below access is probably too much for mknod to request
+ * but we have to do query and setpathinfo so requesting
+ * less could fail (unless we want to request getatr and setatr
+ * permissions (only). At least for POSIX we do not have to
+ * request so much.
+ */
int desiredAccess = GENERIC_READ | GENERIC_WRITE;
__u16 fileHandle;
struct cifs_sb_info *cifs_sb;
- struct cifsTconInfo *pTcon;
+ struct cifsTconInfo *tcon;
char *full_path = NULL;
FILE_ALL_INFO *buf = NULL;
struct inode *newinode = NULL;
- struct cifsFileInfo *pCifsFile = NULL;
struct cifsInodeInfo *pCifsInode;
int disposition = FILE_OVERWRITE_IF;
bool write_only = false;
@@ -154,7 +244,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb);
- pTcon = cifs_sb->tcon;
+ tcon = cifs_sb->tcon;
full_path = build_path_from_dentry(direntry);
if (full_path == NULL) {
@@ -162,12 +252,44 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
return -ENOMEM;
}
- if (nd && (nd->flags & LOOKUP_OPEN)) {
- int oflags = nd->intent.open.flags;
+ mode &= ~current->fs->umask;
+ if (oplockEnabled)
+ oplock = REQ_OPLOCK;
+
+ if (nd && (nd->flags & LOOKUP_OPEN))
+ oflags = nd->intent.open.flags;
+ else
+ oflags = FMODE_READ;
+
+ if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+ rc = cifs_posix_open(full_path, &newinode, inode->i_sb,
+ mode, oflags, &oplock, &fileHandle, xid);
+ /* EIO could indicate that (posix open) operation is not
+ supported, despite what server claimed in capability
+ negotation. EREMOTE indicates DFS junction, which is not
+ handled in posix open */
+
+ if ((rc == 0) && (newinode == NULL))
+ goto cifs_create_get_file_info; /* query inode info */
+ else if (rc == 0) /* success, no need to query */
+ goto cifs_create_set_dentry;
+ else if ((rc != -EIO) && (rc != -EREMOTE) &&
+ (rc != -EOPNOTSUPP)) /* path not found or net err */
+ goto cifs_create_out;
+ /* else fallthrough to retry, using older open call, this is
+ case where server does not support this SMB level, and
+ falsely claims capability (also get here for DFS case
+ which should be rare for path not covered on files) */
+ }
+ if (nd && (nd->flags & LOOKUP_OPEN)) {
+ /* if the file is going to stay open, then we
+ need to set the desired access properly */
desiredAccess = 0;
if (oflags & FMODE_READ)
- desiredAccess |= GENERIC_READ;
+ desiredAccess |= GENERIC_READ; /* is this too little? */
if (oflags & FMODE_WRITE) {
desiredAccess |= GENERIC_WRITE;
if (!(oflags & FMODE_READ))
@@ -186,8 +308,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
/* BB add processing to set equivalent of mode - e.g. via CreateX with
ACLs */
- if (oplockEnabled)
- oplock = REQ_OPLOCK;
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) {
@@ -196,17 +316,15 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
return -ENOMEM;
}
- mode &= ~current->fs->umask;
-
/*
* if we're not using unix extensions, see if we need to set
* ATTR_READONLY on the create call
*/
- if (!pTcon->unix_ext && (mode & S_IWUGO) == 0)
+ if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
create_options |= CREATE_OPTION_READONLY;
if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
- rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
+ rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
desiredAccess, create_options,
&fileHandle, &oplock, buf, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -215,128 +333,119 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
if (rc == -EIO) {
/* old server, retry the open legacy style */
- rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+ rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
desiredAccess, create_options,
&fileHandle, &oplock, buf, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
}
if (rc) {
cFYI(1, ("cifs_create returned 0x%x", rc));
- } else {
- /* If Open reported that we actually created a file
- then we now have to set the mode if possible */
- if ((pTcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
- struct cifs_unix_set_info_args args = {
+ goto cifs_create_out;
+ }
+
+ /* If Open reported that we actually created a file
+ then we now have to set the mode if possible */
+ if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
+ struct cifs_unix_set_info_args args = {
.mode = mode,
.ctime = NO_CHANGE_64,
.atime = NO_CHANGE_64,
.mtime = NO_CHANGE_64,
.device = 0,
- };
+ };
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- args.uid = (__u64) current->fsuid;
- if (inode->i_mode & S_ISGID)
- args.gid = (__u64) inode->i_gid;
- else
- args.gid = (__u64) current->fsgid;
- } else {
- args.uid = NO_CHANGE_64;
- args.gid = NO_CHANGE_64;
- }
- CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = (__u64) current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ args.gid = (__u64) inode->i_gid;
+ else
+ args.gid = (__u64) current_fsgid();
} else {
- /* BB implement mode setting via Windows security
- descriptors e.g. */
- /* CIFSSMBWinSetPerms(xid,pTcon,path,mode,-1,-1,nls);*/
-
- /* Could set r/o dos attribute if mode & 0222 == 0 */
+ args.uid = NO_CHANGE_64;
+ args.gid = NO_CHANGE_64;
}
+ CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else {
+ /* BB implement mode setting via Windows security
+ descriptors e.g. */
+ /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
- /* server might mask mode so we have to query for it */
- if (pTcon->unix_ext)
- rc = cifs_get_inode_info_unix(&newinode, full_path,
- inode->i_sb, xid);
- else {
- rc = cifs_get_inode_info(&newinode, full_path,
- buf, inode->i_sb, xid,
- &fileHandle);
- if (newinode) {
- if (cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_DYNPERM)
- newinode->i_mode = mode;
- if ((oplock & CIFS_CREATE_ACTION) &&
- (cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_SET_UID)) {
- newinode->i_uid = current->fsuid;
- if (inode->i_mode & S_ISGID)
- newinode->i_gid =
- inode->i_gid;
- else
- newinode->i_gid =
- current->fsgid;
- }
+ /* Could set r/o dos attribute if mode & 0222 == 0 */
+ }
+
+cifs_create_get_file_info:
+ /* server might mask mode so we have to query for it */
+ if (tcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+ else {
+ rc = cifs_get_inode_info(&newinode, full_path, buf,
+ inode->i_sb, xid, &fileHandle);
+ if (newinode) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+ newinode->i_mode = mode;
+ if ((oplock & CIFS_CREATE_ACTION) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
+ newinode->i_uid = current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ newinode->i_gid = inode->i_gid;
+ else
+ newinode->i_gid = current_fsgid();
}
}
+ }
- if (rc != 0) {
- cFYI(1,
- ("Create worked but get_inode_info failed rc = %d",
- rc));
- } else {
- if (pTcon->nocase)
- direntry->d_op = &cifs_ci_dentry_ops;
- else
- direntry->d_op = &cifs_dentry_ops;
- d_instantiate(direntry, newinode);
- }
- if ((nd == NULL /* nfsd case - nfs srv does not set nd */) ||
- (!(nd->flags & LOOKUP_OPEN))) {
- /* mknod case - do not leave file open */
- CIFSSMBClose(xid, pTcon, fileHandle);
- } else if (newinode) {
- pCifsFile =
- kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
-
- if (pCifsFile == NULL)
- goto cifs_create_out;
- pCifsFile->netfid = fileHandle;
- pCifsFile->pid = current->tgid;
- pCifsFile->pInode = newinode;
- pCifsFile->invalidHandle = false;
- pCifsFile->closePend = false;
- init_MUTEX(&pCifsFile->fh_sem);
- mutex_init(&pCifsFile->lock_mutex);
- INIT_LIST_HEAD(&pCifsFile->llist);
- atomic_set(&pCifsFile->wrtPending, 0);
-
- /* set the following in open now
+cifs_create_set_dentry:
+ if (rc == 0)
+ setup_cifs_dentry(tcon, direntry, newinode);
+ else
+ cFYI(1, ("Create worked, get_inode_info failed rc = %d", rc));
+
+ /* nfsd case - nfs srv does not set nd */
+ if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) {
+ /* mknod case - do not leave file open */
+ CIFSSMBClose(xid, tcon, fileHandle);
+ } else if (newinode) {
+ struct cifsFileInfo *pCifsFile =
+ kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+
+ if (pCifsFile == NULL)
+ goto cifs_create_out;
+ pCifsFile->netfid = fileHandle;
+ pCifsFile->pid = current->tgid;
+ pCifsFile->pInode = newinode;
+ pCifsFile->invalidHandle = false;
+ pCifsFile->closePend = false;
+ init_MUTEX(&pCifsFile->fh_sem);
+ mutex_init(&pCifsFile->lock_mutex);
+ INIT_LIST_HEAD(&pCifsFile->llist);
+ atomic_set(&pCifsFile->wrtPending, 0);
+
+ /* set the following in open now
pCifsFile->pfile = file; */
- write_lock(&GlobalSMBSeslock);
- list_add(&pCifsFile->tlist, &pTcon->openFileList);
- pCifsInode = CIFS_I(newinode);
- if (pCifsInode) {
- /* if readable file instance put first in list*/
- if (write_only) {
- list_add_tail(&pCifsFile->flist,
- &pCifsInode->openFileList);
- } else {
- list_add(&pCifsFile->flist,
- &pCifsInode->openFileList);
- }
- if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
- pCifsInode->clientCanCacheAll = true;
- pCifsInode->clientCanCacheRead = true;
- cFYI(1, ("Exclusive Oplock inode %p",
- newinode));
- } else if ((oplock & 0xF) == OPLOCK_READ)
- pCifsInode->clientCanCacheRead = true;
+ write_lock(&GlobalSMBSeslock);
+ list_add(&pCifsFile->tlist, &tcon->openFileList);
+ pCifsInode = CIFS_I(newinode);
+ if (pCifsInode) {
+ /* if readable file instance put first in list*/
+ if (write_only) {
+ list_add_tail(&pCifsFile->flist,
+ &pCifsInode->openFileList);
+ } else {
+ list_add(&pCifsFile->flist,
+ &pCifsInode->openFileList);
}
- write_unlock(&GlobalSMBSeslock);
+ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
+ pCifsInode->clientCanCacheAll = true;
+ pCifsInode->clientCanCacheRead = true;
+ cFYI(1, ("Exclusive Oplock inode %p",
+ newinode));
+ } else if ((oplock & 0xF) == OPLOCK_READ)
+ pCifsInode->clientCanCacheRead = true;
}
+ write_unlock(&GlobalSMBSeslock);
}
cifs_create_out:
kfree(buf);
@@ -375,8 +484,8 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
.device = device_number,
};
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- args.uid = (__u64) current->fsuid;
- args.gid = (__u64) current->fsgid;
+ args.uid = (__u64) current_fsuid();
+ args.gid = (__u64) current_fsgid();
} else {
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
@@ -483,7 +592,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
xid = GetXid();
- cFYI(1, (" parent inode = 0x%p name is: %s and dentry = 0x%p",
+ cFYI(1, ("parent inode = 0x%p name is: %s and dentry = 0x%p",
parent_dir_inode, direntry->d_name.name, direntry));
/* check whether path exists */
@@ -515,12 +624,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
}
if (direntry->d_inode != NULL) {
- cFYI(1, (" non-NULL inode in lookup"));
+ cFYI(1, ("non-NULL inode in lookup"));
} else {
- cFYI(1, (" NULL inode in lookup"));
+ cFYI(1, ("NULL inode in lookup"));
}
- cFYI(1,
- (" Full path: %s inode = 0x%p", full_path, direntry->d_inode));
+ cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode));
if (pTcon->unix_ext)
rc = cifs_get_inode_info_unix(&newInode, full_path,
diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c
deleted file mode 100644
index 5a57581eb4b2..000000000000
--- a/fs/cifs/fcntl.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * fs/cifs/fcntl.c
- *
- * vfs operations that deal with the file control API
- *
- * Copyright (C) International Business Machines Corp., 2003,2004
- * Author(s): Steve French (sfrench@us.ibm.com)
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <linux/fs.h>
-#include <linux/stat.h>
-#include <linux/fcntl.h>
-#include "cifsglob.h"
-#include "cifsproto.h"
-#include "cifs_unicode.h"
-#include "cifs_debug.h"
-#include "cifsfs.h"
-
-static __u32 convert_to_cifs_notify_flags(unsigned long fcntl_notify_flags)
-{
- __u32 cifs_ntfy_flags = 0;
-
- /* No way on Linux VFS to ask to monitor xattr
- changes (and no stream support either */
- if (fcntl_notify_flags & DN_ACCESS)
- cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
- if (fcntl_notify_flags & DN_MODIFY) {
- /* What does this mean on directories? */
- cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE |
- FILE_NOTIFY_CHANGE_SIZE;
- }
- if (fcntl_notify_flags & DN_CREATE) {
- cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_CREATION |
- FILE_NOTIFY_CHANGE_LAST_WRITE;
- }
- if (fcntl_notify_flags & DN_DELETE)
- cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
- if (fcntl_notify_flags & DN_RENAME) {
- /* BB review this - checking various server behaviors */
- cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_DIR_NAME |
- FILE_NOTIFY_CHANGE_FILE_NAME;
- }
- if (fcntl_notify_flags & DN_ATTRIB) {
- cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_SECURITY |
- FILE_NOTIFY_CHANGE_ATTRIBUTES;
- }
-/* if (fcntl_notify_flags & DN_MULTISHOT) {
- cifs_ntfy_flags |= ;
- } */ /* BB fixme - not sure how to handle this with CIFS yet */
-
- return cifs_ntfy_flags;
-}
-
-int cifs_dir_notify(struct file *file, unsigned long arg)
-{
- int xid;
- int rc = -EINVAL;
- int oplock = 0;
- struct cifs_sb_info *cifs_sb;
- struct cifsTconInfo *pTcon;
- char *full_path = NULL;
- __u32 filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES;
- __u16 netfid;
-
- if (experimEnabled == 0)
- return 0;
-
- xid = GetXid();
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
- pTcon = cifs_sb->tcon;
-
- full_path = build_path_from_dentry(file->f_path.dentry);
-
- if (full_path == NULL) {
- rc = -ENOMEM;
- } else {
- cFYI(1, ("dir notify on file %s Arg 0x%lx", full_path, arg));
- rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
- GENERIC_READ | SYNCHRONIZE, 0 /* create options */,
- &netfid, &oplock, NULL, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
- /* BB fixme - add this handle to a notify handle list */
- if (rc) {
- cFYI(1, ("Could not open directory for notify"));
- } else {
- filter = convert_to_cifs_notify_flags(arg);
- if (filter != 0) {
- rc = CIFSSMBNotify(xid, pTcon,
- 0 /* no subdirs */, netfid,
- filter, file, arg & DN_MULTISHOT,
- cifs_sb->local_nls);
- } else {
- rc = -EINVAL;
- }
- /* BB add code to close file eventually (at unmount
- it would close automatically but may be a way
- to do it easily when inode freed or when
- notify info is cleared/changed */
- cFYI(1, ("notify rc %d", rc));
- }
- }
-
- FreeXid(xid);
- return rc;
-}
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index f0a81e631ae6..12bb656fbe75 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -644,10 +644,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
__u64 length;
bool wait_flag = false;
struct cifs_sb_info *cifs_sb;
- struct cifsTconInfo *pTcon;
+ struct cifsTconInfo *tcon;
__u16 netfid;
__u8 lockType = LOCKING_ANDX_LARGE_FILES;
- bool posix_locking;
+ bool posix_locking = 0;
length = 1 + pfLock->fl_end - pfLock->fl_start;
rc = -EACCES;
@@ -698,7 +698,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
cFYI(1, ("Unknown type of lock"));
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
- pTcon = cifs_sb->tcon;
+ tcon = cifs_sb->tcon;
if (file->private_data == NULL) {
FreeXid(xid);
@@ -706,9 +706,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
}
netfid = ((struct cifsFileInfo *)file->private_data)->netfid;
- posix_locking = (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability));
-
+ 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 */
@@ -719,7 +720,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
posix_lock_type = CIFS_RDLCK;
else
posix_lock_type = CIFS_WRLCK;
- rc = CIFSSMBPosixLock(xid, pTcon, netfid, 1 /* get */,
+ rc = CIFSSMBPosixLock(xid, tcon, netfid, 1 /* get */,
length, pfLock,
posix_lock_type, wait_flag);
FreeXid(xid);
@@ -727,10 +728,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
}
/* BB we could chain these into one lock request BB */
- rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
+ rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start,
0, 1, lockType, 0 /* wait flag */ );
if (rc == 0) {
- rc = CIFSSMBLock(xid, pTcon, netfid, length,
+ rc = CIFSSMBLock(xid, tcon, netfid, length,
pfLock->fl_start, 1 /* numUnlock */ ,
0 /* numLock */ , lockType,
0 /* wait flag */ );
@@ -767,7 +768,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
if (numUnlock == 1)
posix_lock_type = CIFS_UNLCK;
- rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */,
+ rc = CIFSSMBPosixLock(xid, tcon, netfid, 0 /* set */,
length, pfLock,
posix_lock_type, wait_flag);
} else {
@@ -775,7 +776,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
(struct cifsFileInfo *)file->private_data;
if (numLock) {
- rc = CIFSSMBLock(xid, pTcon, netfid, length,
+ rc = CIFSSMBLock(xid, tcon, netfid, length,
pfLock->fl_start,
0, numLock, lockType, wait_flag);
@@ -796,7 +797,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
if (pfLock->fl_start <= li->offset &&
(pfLock->fl_start + length) >=
(li->offset + li->length)) {
- stored_rc = CIFSSMBLock(xid, pTcon,
+ stored_rc = CIFSSMBLock(xid, tcon,
netfid,
li->length, li->offset,
1, 0, li->type, false);
@@ -2073,7 +2074,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
cFYI(1, ("write_begin from %lld len %d", (long long)pos, len));
- page = __grab_cache_page(mapping, index);
+ page = grab_cache_page_write_begin(mapping, index, flags);
if (!page) {
rc = -ENOMEM;
goto out;
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index ff8c68de4a92..4690a360c855 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1,7 +1,7 @@
/*
* fs/cifs/inode.c
*
- * Copyright (C) International Business Machines Corp., 2002,2007
+ * Copyright (C) International Business Machines Corp., 2002,2008
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
@@ -199,6 +199,49 @@ static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat,
pfnd_dat->Gid = cpu_to_le64(pinode->i_gid);
}
+/**
+ * cifs_new inode - create new inode, initialize, and hash it
+ * @sb - pointer to superblock
+ * @inum - if valid pointer and serverino is enabled, replace i_ino with val
+ *
+ * Create a new inode, initialize it for CIFS and hash it. Returns the new
+ * inode or NULL if one couldn't be allocated.
+ *
+ * If the share isn't mounted with "serverino" or inum is a NULL pointer then
+ * we'll just use the inode number assigned by new_inode(). Note that this can
+ * mean i_ino collisions since the i_ino assigned by new_inode is not
+ * guaranteed to be unique.
+ */
+struct inode *
+cifs_new_inode(struct super_block *sb, __u64 *inum)
+{
+ struct inode *inode;
+
+ inode = new_inode(sb);
+ if (inode == NULL)
+ return NULL;
+
+ /*
+ * BB: Is i_ino == 0 legal? Here, we assume that it is. If it isn't we
+ * stop passing inum as ptr. Are there sanity checks we can use to
+ * ensure that the server is really filling in that field? Also,
+ * if serverino is disabled, perhaps we should be using iunique()?
+ */
+ if (inum && (CIFS_SB(sb)->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
+ inode->i_ino = (unsigned long) *inum;
+
+ /*
+ * must set this here instead of cifs_alloc_inode since VFS will
+ * clobber i_flags
+ */
+ if (sb->s_flags & MS_NOATIME)
+ inode->i_flags |= S_NOATIME | S_NOCMTIME;
+
+ insert_inode_hash(inode);
+
+ return inode;
+}
+
int cifs_get_inode_info_unix(struct inode **pinode,
const unsigned char *full_path, struct super_block *sb, int xid)
{
@@ -233,22 +276,11 @@ int cifs_get_inode_info_unix(struct inode **pinode,
/* get new inode */
if (*pinode == NULL) {
- *pinode = new_inode(sb);
+ *pinode = cifs_new_inode(sb, &find_data.UniqueId);
if (*pinode == NULL) {
rc = -ENOMEM;
goto cgiiu_exit;
}
- /* Is an i_ino of zero legal? */
- /* note ino incremented to unique num in new_inode */
- /* Are there sanity checks we can use to ensure that
- the server is really filling in that field? */
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
- (*pinode)->i_ino = (unsigned long)find_data.UniqueId;
-
- if (sb->s_flags & MS_NOATIME)
- (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
-
- insert_inode_hash(*pinode);
}
inode = *pinode;
@@ -465,11 +497,9 @@ int cifs_get_inode_info(struct inode **pinode,
/* get new inode */
if (*pinode == NULL) {
- *pinode = new_inode(sb);
- if (*pinode == NULL) {
- rc = -ENOMEM;
- goto cgii_exit;
- }
+ __u64 inode_num;
+ __u64 *pinum = &inode_num;
+
/* Is an i_ino of zero legal? Can we use that to check
if the server supports returning inode numbers? Are
there other sanity checks we can use to ensure that
@@ -486,22 +516,26 @@ int cifs_get_inode_info(struct inode **pinode,
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
int rc1 = 0;
- __u64 inode_num;
rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
- full_path, &inode_num,
+ full_path, pinum,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc1) {
cFYI(1, ("GetSrvInodeNum rc %d", rc1));
+ pinum = NULL;
/* BB EOPNOSUPP disable SERVER_INUM? */
- } else /* do we need cast or hash to ino? */
- (*pinode)->i_ino = inode_num;
- } /* else ino incremented to unique num in new_inode*/
- if (sb->s_flags & MS_NOATIME)
- (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
- insert_inode_hash(*pinode);
+ }
+ } else {
+ pinum = NULL;
+ }
+
+ *pinode = cifs_new_inode(sb, pinum);
+ if (*pinode == NULL) {
+ rc = -ENOMEM;
+ goto cgii_exit;
+ }
}
inode = *pinode;
cifsInfo = CIFS_I(inode);
@@ -621,6 +655,47 @@ static const struct inode_operations cifs_ipc_inode_ops = {
.lookup = cifs_lookup,
};
+char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
+{
+ int pplen = cifs_sb->prepathlen;
+ int dfsplen;
+ char *full_path = NULL;
+
+ /* if no prefix path, simply set path to the root of share to "" */
+ if (pplen == 0) {
+ full_path = kmalloc(1, GFP_KERNEL);
+ if (full_path)
+ full_path[0] = 0;
+ return full_path;
+ }
+
+ if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS))
+ dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1);
+ else
+ dfsplen = 0;
+
+ full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL);
+ if (full_path == NULL)
+ return full_path;
+
+ if (dfsplen) {
+ strncpy(full_path, cifs_sb->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, cifs_sb->prepath, pplen);
+ full_path[dfsplen + pplen] = 0; /* add trailing null */
+ return full_path;
+}
+
/* gets root inode */
struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
{
@@ -628,6 +703,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
struct cifs_sb_info *cifs_sb;
struct inode *inode;
long rc;
+ char *full_path;
inode = iget_locked(sb, ino);
if (!inode)
@@ -636,13 +712,17 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
return inode;
cifs_sb = CIFS_SB(inode->i_sb);
- xid = GetXid();
+ full_path = cifs_build_path_to_root(cifs_sb);
+ if (full_path == NULL)
+ return ERR_PTR(-ENOMEM);
+ xid = GetXid();
if (cifs_sb->tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid);
+ rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb,
+ xid);
else
- rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid,
- NULL);
+ rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb,
+ xid, NULL);
if (rc && cifs_sb->tcon->ipc) {
cFYI(1, ("ipc connection - fake read inode"));
inode->i_mode |= S_IFDIR;
@@ -652,6 +732,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
inode->i_uid = cifs_sb->mnt_uid;
inode->i_gid = cifs_sb->mnt_gid;
} else if (rc) {
+ kfree(full_path);
_FreeXid(xid);
iget_failed(inode);
return ERR_PTR(rc);
@@ -659,6 +740,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
unlock_new_inode(inode);
+ kfree(full_path);
/* can not call macro FreeXid here since in a void func
* TODO: This is no longer true
*/
@@ -969,7 +1051,7 @@ out_reval:
return rc;
}
-static void posix_fill_in_inode(struct inode *tmp_inode,
+void posix_fill_in_inode(struct inode *tmp_inode,
FILE_UNIX_BASIC_INFO *pData, int isNewInode)
{
struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
@@ -1066,24 +1148,14 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
else
direntry->d_op = &cifs_dentry_ops;
- newinode = new_inode(inode->i_sb);
+ newinode = cifs_new_inode(inode->i_sb,
+ &pInfo->UniqueId);
if (newinode == NULL) {
kfree(pInfo);
goto mkdir_get_info;
}
- /* Is an i_ino of zero legal? */
- /* Are there sanity checks we can use to ensure that
- the server is really filling in that field? */
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
- newinode->i_ino =
- (unsigned long)pInfo->UniqueId;
- } /* note ino incremented to unique num in new_inode */
- if (inode->i_sb->s_flags & MS_NOATIME)
- newinode->i_flags |= S_NOATIME | S_NOCMTIME;
newinode->i_nlink = 2;
-
- insert_inode_hash(newinode);
d_instantiate(direntry, newinode);
/* we already checked in POSIXCreate whether
@@ -1143,11 +1215,11 @@ mkdir_get_info:
.device = 0,
};
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- args.uid = (__u64)current->fsuid;
+ args.uid = (__u64)current_fsuid();
if (inode->i_mode & S_ISGID)
args.gid = (__u64)inode->i_gid;
else
- args.gid = (__u64)current->fsgid;
+ args.gid = (__u64)current_fsgid();
} else {
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
@@ -1184,13 +1256,13 @@ mkdir_get_info:
if (cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_SET_UID) {
direntry->d_inode->i_uid =
- current->fsuid;
+ current_fsuid();
if (inode->i_mode & S_ISGID)
direntry->d_inode->i_gid =
inode->i_gid;
else
direntry->d_inode->i_gid =
- current->fsgid;
+ current_fsgid();
}
}
}
@@ -1237,6 +1309,11 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
cifsInode = CIFS_I(direntry->d_inode);
cifsInode->time = 0; /* force revalidate to go get info when
needed */
+
+ cifsInode = CIFS_I(inode);
+ cifsInode->time = 0; /* force revalidate to get parent dir info
+ since cached search results now invalid */
+
direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
current_fs_time(inode->i_sb);
@@ -1593,7 +1670,7 @@ do_expand:
i_size_write(inode, offset);
spin_unlock(&inode->i_lock);
out_truncate:
- if (inode->i_op && inode->i_op->truncate)
+ if (inode->i_op->truncate)
inode->i_op->truncate(inode);
return 0;
out_sig:
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 0088a5b52564..f94650683a00 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -65,7 +65,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
switch (command) {
case CIFS_IOC_CHECKUMOUNT:
cFYI(1, ("User unmount attempted"));
- if (cifs_sb->mnt_uid == current->uid)
+ if (cifs_sb->mnt_uid == current_uid())
rc = 0;
else {
rc = -EACCES;
diff --git a/fs/cifs/md5.c b/fs/cifs/md5.c
index 462bbfefd4b6..98b66a54c319 100644
--- a/fs/cifs/md5.c
+++ b/fs/cifs/md5.c
@@ -10,8 +10,8 @@
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
- * MD5Context structure, pass it to MD5Init, call MD5Update as
- * needed on buffers full of bytes, and then call MD5Final, which
+ * MD5Context structure, pass it to cifs_MD5_init, call cifs_MD5_update as
+ * needed on buffers full of bytes, and then call cifs_MD5_final, which
* will fill a supplied 16-byte array with the digest.
*/
@@ -45,7 +45,7 @@ byteReverse(unsigned char *buf, unsigned longs)
* initialization constants.
*/
void
-MD5Init(struct MD5Context *ctx)
+cifs_MD5_init(struct MD5Context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
@@ -61,7 +61,7 @@ MD5Init(struct MD5Context *ctx)
* of bytes.
*/
void
-MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+cifs_MD5_update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
{
register __u32 t;
@@ -110,7 +110,7 @@ MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
-MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+cifs_MD5_final(unsigned char digest[16], struct MD5Context *ctx)
{
unsigned int count;
unsigned char *p;
@@ -165,7 +165,7 @@ MD5Final(unsigned char digest[16], struct MD5Context *ctx)
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * reflect the addition of 16 longwords of new data. cifs_MD5_update blocks
* the data and converts bytes into longwords for this routine.
*/
static void
@@ -267,9 +267,9 @@ hmac_md5_init_rfc2104(unsigned char *key, int key_len,
unsigned char tk[16];
struct MD5Context tctx;
- MD5Init(&tctx);
- MD5Update(&tctx, key, key_len);
- MD5Final(tk, &tctx);
+ cifs_MD5_init(&tctx);
+ cifs_MD5_update(&tctx, key, key_len);
+ cifs_MD5_final(tk, &tctx);
key = tk;
key_len = 16;
@@ -287,8 +287,8 @@ hmac_md5_init_rfc2104(unsigned char *key, int key_len,
ctx->k_opad[i] ^= 0x5c;
}
- MD5Init(&ctx->ctx);
- MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+ cifs_MD5_init(&ctx->ctx);
+ cifs_MD5_update(&ctx->ctx, ctx->k_ipad, 64);
}
#endif
@@ -317,8 +317,8 @@ hmac_md5_init_limK_to_64(const unsigned char *key, int key_len,
ctx->k_opad[i] ^= 0x5c;
}
- MD5Init(&ctx->ctx);
- MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+ cifs_MD5_init(&ctx->ctx);
+ cifs_MD5_update(&ctx->ctx, ctx->k_ipad, 64);
}
/***********************************************************************
@@ -328,7 +328,7 @@ void
hmac_md5_update(const unsigned char *text, int text_len,
struct HMACMD5Context *ctx)
{
- MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */
+ cifs_MD5_update(&ctx->ctx, text, text_len); /* then text of datagram */
}
/***********************************************************************
@@ -339,12 +339,12 @@ hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx)
{
struct MD5Context ctx_o;
- MD5Final(digest, &ctx->ctx);
+ cifs_MD5_final(digest, &ctx->ctx);
- MD5Init(&ctx_o);
- MD5Update(&ctx_o, ctx->k_opad, 64);
- MD5Update(&ctx_o, digest, 16);
- MD5Final(digest, &ctx_o);
+ cifs_MD5_init(&ctx_o);
+ cifs_MD5_update(&ctx_o, ctx->k_opad, 64);
+ cifs_MD5_update(&ctx_o, digest, 16);
+ cifs_MD5_final(digest, &ctx_o);
}
/***********************************************************
diff --git a/fs/cifs/md5.h b/fs/cifs/md5.h
index f7d4f4197bac..6fba8cb402fd 100644
--- a/fs/cifs/md5.h
+++ b/fs/cifs/md5.h
@@ -20,10 +20,10 @@ struct HMACMD5Context {
};
#endif /* _HMAC_MD5_H */
-void MD5Init(struct MD5Context *context);
-void MD5Update(struct MD5Context *context, unsigned char const *buf,
+void cifs_MD5_init(struct MD5Context *context);
+void cifs_MD5_update(struct MD5Context *context, unsigned char const *buf,
unsigned len);
-void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void cifs_MD5_final(unsigned char digest[16], struct MD5Context *context);
/* The following definitions come from lib/hmacmd5.c */
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 9ee3f689c2b0..4c89c572891a 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -97,7 +97,10 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
kfree(buf_to_free->serverOS);
kfree(buf_to_free->serverDomain);
kfree(buf_to_free->serverNOS);
- kfree(buf_to_free->password);
+ if (buf_to_free->password) {
+ memset(buf_to_free->password, 0, strlen(buf_to_free->password));
+ kfree(buf_to_free->password);
+ }
kfree(buf_to_free->domainName);
kfree(buf_to_free);
}
@@ -129,6 +132,10 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
}
atomic_dec(&tconInfoAllocCount);
kfree(buf_to_free->nativeFileSystem);
+ if (buf_to_free->password) {
+ memset(buf_to_free->password, 0, strlen(buf_to_free->password));
+ kfree(buf_to_free->password);
+ }
kfree(buf_to_free);
}
@@ -338,13 +345,13 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
/* BB Add support for establishing new tCon and SMB Session */
/* with userid/password pairs found on the smb session */
/* for other target tcp/ip addresses BB */
- if (current->fsuid != treeCon->ses->linux_uid) {
+ if (current_fsuid() != treeCon->ses->linux_uid) {
cFYI(1, ("Multiuser mode and UID "
"did not match tcon uid"));
read_lock(&cifs_tcp_ses_lock);
list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
- if (ses->linux_uid == current->fsuid) {
+ if (ses->linux_uid == current_fsuid()) {
if (ses->server == treeCon->ses->server) {
cFYI(1, ("found matching uid substitute right smb_uid"));
buffer->Uid = ses->Suid;
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 9f51f9bf0292..c2c01ff4c32c 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -56,35 +56,34 @@ static inline void dump_cifs_file_struct(struct file *file, char *label)
}
#endif /* DEBUG2 */
-/* Returns one if new inode created (which therefore needs to be hashed) */
+/* Returns 1 if new inode created, 2 if both dentry and inode were */
/* Might check in the future if inode number changed so we can rehash inode */
-static int construct_dentry(struct qstr *qstring, struct file *file,
- struct inode **ptmp_inode, struct dentry **pnew_dentry)
+static int
+construct_dentry(struct qstr *qstring, struct file *file,
+ struct inode **ptmp_inode, struct dentry **pnew_dentry,
+ __u64 *inum)
{
- struct dentry *tmp_dentry;
- struct cifs_sb_info *cifs_sb;
- struct cifsTconInfo *pTcon;
+ struct dentry *tmp_dentry = NULL;
+ struct super_block *sb = file->f_path.dentry->d_sb;
int rc = 0;
cFYI(1, ("For %s", qstring->name));
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
- pTcon = cifs_sb->tcon;
qstring->hash = full_name_hash(qstring->name, qstring->len);
tmp_dentry = d_lookup(file->f_path.dentry, qstring);
if (tmp_dentry) {
+ /* BB: overwrite old name? i.e. tmp_dentry->d_name and
+ * tmp_dentry->d_name.len??
+ */
cFYI(0, ("existing dentry with inode 0x%p",
tmp_dentry->d_inode));
*ptmp_inode = tmp_dentry->d_inode;
-/* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/
if (*ptmp_inode == NULL) {
- *ptmp_inode = new_inode(file->f_path.dentry->d_sb);
+ *ptmp_inode = cifs_new_inode(sb, inum);
if (*ptmp_inode == NULL)
return rc;
rc = 1;
}
- if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME)
- (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME;
} else {
tmp_dentry = d_alloc(file->f_path.dentry, qstring);
if (tmp_dentry == NULL) {
@@ -93,15 +92,14 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
return rc;
}
- *ptmp_inode = new_inode(file->f_path.dentry->d_sb);
- if (pTcon->nocase)
+ if (CIFS_SB(sb)->tcon->nocase)
tmp_dentry->d_op = &cifs_ci_dentry_ops;
else
tmp_dentry->d_op = &cifs_dentry_ops;
+
+ *ptmp_inode = cifs_new_inode(sb, inum);
if (*ptmp_inode == NULL)
return rc;
- if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME)
- (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME;
rc = 2;
}
@@ -822,7 +820,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
/* 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, int max_len, ino_t *pinum)
+ struct cifs_sb_info *cifs_sb, int max_len, __u64 *pinum)
{
int rc = 0;
unsigned int len = 0;
@@ -842,9 +840,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
len = strnlen(filename, PATH_MAX);
}
- /* BB fixme - hash low and high 32 bits if not 64 bit arch BB */
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
- *pinum = pFindData->UniqueId;
+ *pinum = pFindData->UniqueId;
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
FILE_DIRECTORY_INFO *pFindData =
(FILE_DIRECTORY_INFO *)current_entry;
@@ -907,7 +903,7 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
struct qstr qstring;
struct cifsFileInfo *pCifsF;
unsigned int obj_type;
- ino_t inum;
+ __u64 inum;
struct cifs_sb_info *cifs_sb;
struct inode *tmp_inode;
struct dentry *tmp_dentry;
@@ -940,20 +936,18 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
if (rc)
return rc;
- rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry);
+ /* only these two infolevels return valid inode numbers */
+ if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX ||
+ pCifsF->srch_inf.info_level == SMB_FIND_FILE_ID_FULL_DIR_INFO)
+ rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry,
+ &inum);
+ else
+ rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry,
+ NULL);
+
if ((tmp_inode == NULL) || (tmp_dentry == NULL))
return -ENOMEM;
- if (rc) {
- /* inode created, we need to hash it with right inode number */
- if (inum != 0) {
- /* BB fixme - hash the 2 32 quantities bits together if
- * necessary BB */
- tmp_inode->i_ino = inum;
- }
- insert_inode_hash(tmp_inode);
- }
-
/* we pass in rc below, indicating whether it is a new inode,
so we can figure out whether to invalidate the inode cached
data if the file has changed */
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 2851d5da0c8c..5c68b4282be9 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -34,15 +34,99 @@
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
+/* Checks if this is the first smb session to be reconnected after
+ the socket has been reestablished (so we know whether to use vc 0).
+ Called while holding the cifs_tcp_ses_lock, so do not block */
+static bool is_first_ses_reconnect(struct cifsSesInfo *ses)
+{
+ struct list_head *tmp;
+ struct cifsSesInfo *tmp_ses;
+
+ list_for_each(tmp, &ses->server->smb_ses_list) {
+ tmp_ses = list_entry(tmp, struct cifsSesInfo,
+ smb_ses_list);
+ if (tmp_ses->need_reconnect == false)
+ return false;
+ }
+ /* could not find a session that was already connected,
+ this must be the first one we are reconnecting */
+ return true;
+}
+
+/*
+ * vc number 0 is treated specially by some servers, and should be the
+ * first one we request. After that we can use vcnumbers up to maxvcs,
+ * one for each smb session (some Windows versions set maxvcs incorrectly
+ * so maxvc=1 can be ignored). If we have too many vcs, we can reuse
+ * any vc but zero (some servers reset the connection on vcnum zero)
+ *
+ */
+static __le16 get_next_vcnum(struct cifsSesInfo *ses)
+{
+ __u16 vcnum = 0;
+ struct list_head *tmp;
+ struct cifsSesInfo *tmp_ses;
+ __u16 max_vcs = ses->server->max_vcs;
+ __u16 i;
+ int free_vc_found = 0;
+
+ /* Quoting the MS-SMB specification: "Windows-based SMB servers set this
+ field to one but do not enforce this limit, which allows an SMB client
+ to establish more virtual circuits than allowed by this value ... but
+ other server implementations can enforce this limit." */
+ if (max_vcs < 2)
+ max_vcs = 0xFFFF;
+
+ write_lock(&cifs_tcp_ses_lock);
+ if ((ses->need_reconnect) && is_first_ses_reconnect(ses))
+ goto get_vc_num_exit; /* vcnum will be zero */
+ for (i = ses->server->srv_count - 1; i < max_vcs; i++) {
+ if (i == 0) /* this is the only connection, use vc 0 */
+ break;
+
+ free_vc_found = 1;
+
+ list_for_each(tmp, &ses->server->smb_ses_list) {
+ tmp_ses = list_entry(tmp, struct cifsSesInfo,
+ smb_ses_list);
+ if (tmp_ses->vcnum == i) {
+ free_vc_found = 0;
+ break; /* found duplicate, try next vcnum */
+ }
+ }
+ if (free_vc_found)
+ break; /* we found a vcnumber that will work - use it */
+ }
+
+ if (i == 0)
+ vcnum = 0; /* for most common case, ie if one smb session, use
+ vc zero. Also for case when no free vcnum, zero
+ is safest to send (some clients only send zero) */
+ else if (free_vc_found == 0)
+ vcnum = 1; /* we can not reuse vc=0 safely, since some servers
+ reset all uids on that, but 1 is ok. */
+ else
+ vcnum = i;
+ ses->vcnum = vcnum;
+get_vc_num_exit:
+ write_unlock(&cifs_tcp_ses_lock);
+
+ return le16_to_cpu(vcnum);
+}
+
static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
{
__u32 capabilities = 0;
/* init fields common to all four types of SessSetup */
- /* note that header is initialized to zero in header_assemble */
+ /* Note that offsets for first seven fields in req struct are same */
+ /* in CIFS Specs so does not matter which of 3 forms of struct */
+ /* 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.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+ pSMB->req.VcNumber = get_next_vcnum(ses);
/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
@@ -71,7 +155,6 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
if (ses->capabilities & CAP_UNIX)
capabilities |= CAP_UNIX;
- /* BB check whether to init vcnum BB */
return capabilities;
}
@@ -228,7 +311,7 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft,
kfree(ses->serverOS);
/* UTF-8 string will not grow more than four times as big as UCS-16 */
- ses->serverOS = kzalloc(4 * len, GFP_KERNEL);
+ ses->serverOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL);
if (ses->serverOS != NULL)
cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp);
data += 2 * (len + 1);
@@ -241,7 +324,7 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft,
return rc;
kfree(ses->serverNOS);
- ses->serverNOS = kzalloc(4 * len, GFP_KERNEL); /* BB this is wrong length FIXME BB */
+ ses->serverNOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL);
if (ses->serverNOS != NULL) {
cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len,
nls_cp);
@@ -417,7 +500,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
/* BB calculate hash with password */
/* and copy into bcc */
- calc_lanman_hash(ses, lnm_session_key);
+ calc_lanman_hash(ses->password, ses->server->cryptKey,
+ ses->server->secMode & SECMODE_PW_ENCRYPT ?
+ true : false, lnm_session_key);
+
ses->flags |= CIFS_SES_LANMAN;
memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE;
diff --git a/fs/cifs/smbdes.c b/fs/cifs/smbdes.c
index 04943c976f98..224a1f478966 100644
--- a/fs/cifs/smbdes.c
+++ b/fs/cifs/smbdes.c
@@ -318,7 +318,8 @@ str_to_key(unsigned char *str, unsigned char *key)
}
static void
-smbhash(unsigned char *out, unsigned char *in, unsigned char *key, int forw)
+smbhash(unsigned char *out, const unsigned char *in, unsigned char *key,
+ int forw)
{
int i;
char *outb; /* outb[64] */
@@ -363,7 +364,7 @@ E_P16(unsigned char *p14, unsigned char *p16)
}
void
-E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24)
+E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24)
{
smbhash(p24, c8, p21, 1);
smbhash(p24 + 8, c8, p21 + 7, 1);
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index ff3232fa1015..93fb09a99c69 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -49,9 +49,10 @@
/*The following definitions come from libsmb/smbencrypt.c */
-void SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
+void SMBencrypt(unsigned char *passwd, const unsigned char *c8,
+ unsigned char *p24);
void E_md4hash(const unsigned char *passwd, unsigned char *p16);
-static void SMBOWFencrypt(unsigned char passwd[16], unsigned char *c8,
+static void SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8,
unsigned char p24[24]);
void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
@@ -61,7 +62,7 @@ void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
encrypted password into p24 */
/* Note that password must be uppercased and null terminated */
void
-SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
+SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24)
{
unsigned char p14[15], p21[21];
@@ -212,7 +213,7 @@ ntv2_owf_gen(const unsigned char owf[16], const char *user_n,
/* Does the des encryption from the NT or LM MD4 hash. */
static void
-SMBOWFencrypt(unsigned char passwd[16], unsigned char *c8,
+SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8,
unsigned char p24[24])
{
unsigned char p21[21];
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index ff8243a8fe3e..0ad3e2d116a6 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -37,15 +37,11 @@ extern mempool_t *cifs_mid_poolp;
extern struct kmem_cache *cifs_oplock_cachep;
static struct mid_q_entry *
-AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
+AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
{
struct mid_q_entry *temp;
- if (ses == NULL) {
- cERROR(1, ("Null session passed in to AllocMidQEntry"));
- return NULL;
- }
- if (ses->server == NULL) {
+ if (server == NULL) {
cERROR(1, ("Null TCP session in AllocMidQEntry"));
return NULL;
}
@@ -62,12 +58,11 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
/* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
/* when mid allocated can be before when sent */
temp->when_alloc = jiffies;
- temp->ses = ses;
temp->tsk = current;
}
spin_lock(&GlobalMid_Lock);
- list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
+ list_add_tail(&temp->qhead, &server->pending_mid_q);
atomic_inc(&midCount);
temp->midState = MID_REQUEST_ALLOCATED;
spin_unlock(&GlobalMid_Lock);
@@ -159,81 +154,8 @@ void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
spin_unlock(&GlobalMid_Lock);
}
-int
-smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
- unsigned int smb_buf_length, struct sockaddr *sin, bool noblocksnd)
-{
- int rc = 0;
- int i = 0;
- struct msghdr smb_msg;
- struct kvec iov;
- unsigned len = smb_buf_length + 4;
-
- if (ssocket == NULL)
- return -ENOTSOCK; /* BB eventually add reconnect code here */
- iov.iov_base = smb_buffer;
- iov.iov_len = len;
-
- smb_msg.msg_name = sin;
- smb_msg.msg_namelen = sizeof(struct sockaddr);
- smb_msg.msg_control = NULL;
- smb_msg.msg_controllen = 0;
- if (noblocksnd)
- smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL;
- else
- smb_msg.msg_flags = MSG_NOSIGNAL;
-
- /* smb header is converted in header_assemble. bcc and rest of SMB word
- area, and byte area if necessary, is converted to littleendian in
- cifssmb.c and RFC1001 len is converted to bigendian in smb_send
- Flags2 is converted in SendReceive */
-
- smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
- cFYI(1, ("Sending smb of length %d", smb_buf_length));
- dump_smb(smb_buffer, len);
-
- while (len > 0) {
- rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
- if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
- i++;
- /* smaller timeout here than send2 since smaller size */
- /* Although it may not be required, this also is smaller
- oplock break time */
- if (i > 12) {
- cERROR(1,
- ("sends on sock %p stuck for 7 seconds",
- ssocket));
- rc = -EAGAIN;
- break;
- }
- msleep(1 << i);
- continue;
- }
- if (rc < 0)
- break;
- else
- i = 0; /* reset i after each successful send */
- iov.iov_base += rc;
- iov.iov_len -= rc;
- len -= rc;
- }
-
- if (rc < 0) {
- cERROR(1, ("Error %d sending data on socket to server", rc));
- } else {
- rc = 0;
- }
-
- /* Don't want to modify the buffer as a
- side effect of this call. */
- smb_buffer->smb_buf_length = smb_buf_length;
-
- return rc;
-}
-
static int
-smb_send2(struct TCP_Server_Info *server, struct kvec *iov, int n_vec,
- struct sockaddr *sin, bool noblocksnd)
+smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
{
int rc = 0;
int i = 0;
@@ -248,11 +170,11 @@ smb_send2(struct TCP_Server_Info *server, struct kvec *iov, int n_vec,
if (ssocket == NULL)
return -ENOTSOCK; /* BB eventually add reconnect code here */
- smb_msg.msg_name = sin;
+ smb_msg.msg_name = (struct sockaddr *) &server->addr.sockAddr;
smb_msg.msg_namelen = sizeof(struct sockaddr);
smb_msg.msg_control = NULL;
smb_msg.msg_controllen = 0;
- if (noblocksnd)
+ if (server->noblocksnd)
smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL;
else
smb_msg.msg_flags = MSG_NOSIGNAL;
@@ -277,7 +199,25 @@ smb_send2(struct TCP_Server_Info *server, struct kvec *iov, int n_vec,
n_vec - first_vec, total_len);
if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
i++;
- if (i >= 14) {
+ /* if blocking send we try 3 times, since each can block
+ for 5 seconds. For nonblocking we have to try more
+ but wait increasing amounts of time allowing time for
+ socket to clear. The overall time we wait in either
+ case to send on the socket is about 15 seconds.
+ Similarly we wait for 15 seconds for
+ a response from the server in SendReceive[2]
+ for the server to send a response back for
+ most types of requests (except SMB Write
+ past end of file which can be slow, and
+ blocking lock operations). NFS waits slightly longer
+ than CIFS, but this can make it take longer for
+ nonresponsive servers to be detected and 15 seconds
+ is more than enough time for modern networks to
+ send a packet. In most cases if we fail to send
+ after the retries we will kill the socket and
+ reconnect which may clear the network problem.
+ */
+ if ((i >= 14) || (!server->noblocksnd && (i > 2))) {
cERROR(1,
("sends on sock %p stuck for 15 seconds",
ssocket));
@@ -344,42 +284,55 @@ smb_send2(struct TCP_Server_Info *server, struct kvec *iov, int n_vec,
return rc;
}
+int
+smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
+ unsigned int smb_buf_length)
+{
+ struct kvec iov;
+
+ iov.iov_base = smb_buffer;
+ iov.iov_len = smb_buf_length + 4;
+
+ return smb_sendv(server, &iov, 1);
+}
+
static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
{
if (long_op == CIFS_ASYNC_OP) {
/* oplock breaks must not be held up */
atomic_inc(&ses->server->inFlight);
- } else {
- spin_lock(&GlobalMid_Lock);
- while (1) {
- if (atomic_read(&ses->server->inFlight) >=
- cifs_max_pending){
- spin_unlock(&GlobalMid_Lock);
+ return 0;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ while (1) {
+ if (atomic_read(&ses->server->inFlight) >=
+ cifs_max_pending){
+ spin_unlock(&GlobalMid_Lock);
#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->num_waiters);
+ atomic_inc(&ses->server->num_waiters);
#endif
- wait_event(ses->server->request_q,
- atomic_read(&ses->server->inFlight)
- < cifs_max_pending);
+ wait_event(ses->server->request_q,
+ atomic_read(&ses->server->inFlight)
+ < cifs_max_pending);
#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->num_waiters);
+ atomic_dec(&ses->server->num_waiters);
#endif
- spin_lock(&GlobalMid_Lock);
- } else {
- if (ses->server->tcpStatus == CifsExiting) {
- spin_unlock(&GlobalMid_Lock);
- return -ENOENT;
- }
-
- /* can not count locking commands against total
- as they are allowed to block on server */
-
- /* update # of requests on the wire to server */
- if (long_op != CIFS_BLOCKING_OP)
- atomic_inc(&ses->server->inFlight);
+ spin_lock(&GlobalMid_Lock);
+ } else {
+ if (ses->server->tcpStatus == CifsExiting) {
spin_unlock(&GlobalMid_Lock);
- break;
+ return -ENOENT;
}
+
+ /* can not count locking commands against total
+ as they are allowed to block on server */
+
+ /* update # of requests on the wire to server */
+ if (long_op != CIFS_BLOCKING_OP)
+ atomic_inc(&ses->server->inFlight);
+ spin_unlock(&GlobalMid_Lock);
+ break;
}
}
return 0;
@@ -390,17 +343,21 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf,
{
if (ses->server->tcpStatus == CifsExiting) {
return -ENOENT;
- } else if (ses->server->tcpStatus == CifsNeedReconnect) {
+ }
+
+ if (ses->server->tcpStatus == CifsNeedReconnect) {
cFYI(1, ("tcp session dead - return to caller to retry"));
return -EAGAIN;
- } else if (ses->status != CifsGood) {
+ }
+
+ if (ses->status != CifsGood) {
/* check if SMB session is bad because we are setting it up */
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE))
return -EAGAIN;
/* else ok - we are setting up session */
}
- *ppmidQ = AllocMidQEntry(in_buf, ses);
+ *ppmidQ = AllocMidQEntry(in_buf, ses->server);
if (*ppmidQ == NULL)
return -ENOMEM;
return 0;
@@ -415,11 +372,8 @@ static int wait_for_response(struct cifsSesInfo *ses,
for (;;) {
curr_timeout = timeout + jiffies;
- wait_event(ses->server->response_q,
- (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
- time_after(jiffies, curr_timeout) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
+ wait_event_timeout(ses->server->response_q,
+ midQ->midState != MID_REQUEST_SUBMITTED, timeout);
if (time_after(jiffies, curr_timeout) &&
(midQ->midState == MID_REQUEST_SUBMITTED) &&
@@ -521,11 +475,11 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(in_buf);
/* Update # of requests on wire to server */
atomic_dec(&ses->server->inFlight);
@@ -533,20 +487,23 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
return rc;
}
rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
+ if (rc) {
+ mutex_unlock(&ses->server->srv_mutex);
+ cifs_small_buf_release(in_buf);
+ goto out;
+ }
midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
atomic_inc(&ses->server->inSend);
#endif
- rc = smb_send2(ses->server, iov, n_vec,
- (struct sockaddr *) &(ses->server->addr.sockAddr),
- ses->server->noblocksnd);
+ rc = smb_sendv(ses->server, iov, n_vec);
#ifdef CONFIG_CIFS_STATS2
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(in_buf);
if (rc < 0)
@@ -581,10 +538,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
- if (midQ->resp_buf) {
- spin_unlock(&GlobalMid_Lock);
- receive_len = midQ->resp_buf->smb_buf_length;
- } else {
+
+ if (midQ->resp_buf == NULL) {
cERROR(1, ("No response to cmd %d mid %d",
midQ->command, midQ->mid));
if (midQ->midState == MID_REQUEST_SUBMITTED) {
@@ -612,53 +567,59 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
return rc;
}
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+
if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
cERROR(1, ("Frame too large received. Length: %d Xid: %d",
receive_len, xid));
rc = -EIO;
- } else { /* rcvd frame is ok */
- if (midQ->resp_buf &&
- (midQ->midState == MID_RESPONSE_RECEIVED)) {
-
- iov[0].iov_base = (char *)midQ->resp_buf;
- if (midQ->largeBuf)
- *pRespBufType = CIFS_LARGE_BUFFER;
- else
- *pRespBufType = CIFS_SMALL_BUFFER;
- iov[0].iov_len = receive_len + 4;
-
- dump_smb(midQ->resp_buf, 80);
- /* convert the length into a more usable form */
- if ((receive_len > 24) &&
- (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
- SECMODE_SIGN_ENABLED))) {
- rc = cifs_verify_signature(midQ->resp_buf,
+ goto out;
+ }
+
+ /* rcvd frame is ok */
+
+ if (midQ->resp_buf &&
+ (midQ->midState == MID_RESPONSE_RECEIVED)) {
+
+ iov[0].iov_base = (char *)midQ->resp_buf;
+ if (midQ->largeBuf)
+ *pRespBufType = CIFS_LARGE_BUFFER;
+ else
+ *pRespBufType = CIFS_SMALL_BUFFER;
+ iov[0].iov_len = receive_len + 4;
+
+ dump_smb(midQ->resp_buf, 80);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(midQ->resp_buf,
&ses->server->mac_signing_key,
midQ->sequence_number+1);
- if (rc) {
- cERROR(1, ("Unexpected SMB signature"));
- /* BB FIXME add code to kill session */
- }
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
}
-
- /* BB special case reconnect tid and uid here? */
- rc = map_smb_to_linux_error(midQ->resp_buf,
- flags & CIFS_LOG_ERROR);
-
- /* convert ByteCount if necessary */
- if (receive_len >= sizeof(struct smb_hdr) - 4
- /* do not count RFC1001 header */ +
- (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
- BCC(midQ->resp_buf) =
- le16_to_cpu(BCC_LE(midQ->resp_buf));
- if ((flags & CIFS_NO_RESP) == 0)
- midQ->resp_buf = NULL; /* mark it so buf will
- not be freed by
- DeleteMidQEntry */
- } else {
- rc = -EIO;
- cFYI(1, ("Bad MID state?"));
}
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(midQ->resp_buf,
+ flags & CIFS_LOG_ERROR);
+
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
+ BCC(midQ->resp_buf) =
+ le16_to_cpu(BCC_LE(midQ->resp_buf));
+ if ((flags & CIFS_NO_RESP) == 0)
+ midQ->resp_buf = NULL; /* mark it so buf will
+ not be freed by
+ DeleteMidQEntry */
+ } else {
+ rc = -EIO;
+ cFYI(1, ("Bad MID state?"));
}
out:
@@ -695,6 +656,12 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
to the same server. We may make this configurable later or
use ses->maxReq */
+ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
+ in_buf->smb_buf_length));
+ return -EIO;
+ }
+
rc = wait_for_free_request(ses, long_op);
if (rc)
return rc;
@@ -703,42 +670,33 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
/* Update # of requests on wire to server */
atomic_dec(&ses->server->inFlight);
wake_up(&ses->server->request_q);
return rc;
}
- if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- cERROR(1, ("Illegal length, greater than maximum frame, %d",
- in_buf->smb_buf_length));
- DeleteMidQEntry(midQ);
- up(&ses->server->tcpSem);
- /* Update # of requests on wire to server */
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- return -EIO;
- }
-
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
+ mutex_unlock(&ses->server->srv_mutex);
+ goto out;
+ }
midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
atomic_inc(&ses->server->inSend);
#endif
- rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
- (struct sockaddr *) &(ses->server->addr.sockAddr),
- ses->server->noblocksnd);
+ rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length);
#ifdef CONFIG_CIFS_STATS2
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
if (rc < 0)
goto out;
@@ -772,10 +730,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
- if (midQ->resp_buf) {
- spin_unlock(&GlobalMid_Lock);
- receive_len = midQ->resp_buf->smb_buf_length;
- } else {
+ if (midQ->resp_buf == NULL) {
cERROR(1, ("No response for cmd %d mid %d",
midQ->command, midQ->mid));
if (midQ->midState == MID_REQUEST_SUBMITTED) {
@@ -803,47 +758,52 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
return rc;
}
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+
if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
cERROR(1, ("Frame too large received. Length: %d Xid: %d",
receive_len, xid));
rc = -EIO;
- } else { /* rcvd frame is ok */
-
- if (midQ->resp_buf && out_buf
- && (midQ->midState == MID_RESPONSE_RECEIVED)) {
- out_buf->smb_buf_length = receive_len;
- memcpy((char *)out_buf + 4,
- (char *)midQ->resp_buf + 4,
- receive_len);
-
- dump_smb(out_buf, 92);
- /* convert the length into a more usable form */
- if ((receive_len > 24) &&
- (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
- SECMODE_SIGN_ENABLED))) {
- rc = cifs_verify_signature(out_buf,
+ goto out;
+ }
+
+ /* rcvd frame is ok */
+
+ if (midQ->resp_buf && out_buf
+ && (midQ->midState == MID_RESPONSE_RECEIVED)) {
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
&ses->server->mac_signing_key,
midQ->sequence_number+1);
- if (rc) {
- cERROR(1, ("Unexpected SMB signature"));
- /* BB FIXME add code to kill session */
- }
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
}
+ }
- *pbytes_returned = out_buf->smb_buf_length;
+ *pbytes_returned = out_buf->smb_buf_length;
- /* BB special case reconnect tid and uid here? */
- rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
- /* convert ByteCount if necessary */
- if (receive_len >= sizeof(struct smb_hdr) - 4
- /* do not count RFC1001 header */ +
- (2 * out_buf->WordCount) + 2 /* bcc */ )
- BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
- } else {
- rc = -EIO;
- cERROR(1, ("Bad MID state?"));
- }
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * out_buf->WordCount) + 2 /* bcc */ )
+ BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+ } else {
+ rc = -EIO;
+ cERROR(1, ("Bad MID state?"));
}
out:
@@ -866,16 +826,14 @@ send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf,
header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0);
in_buf->Mid = mid;
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
return rc;
}
- rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
- (struct sockaddr *) &(ses->server->addr.sockAddr),
- ses->server->noblocksnd);
- up(&ses->server->tcpSem);
+ rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length);
+ mutex_unlock(&ses->server->srv_mutex);
return rc;
}
@@ -933,6 +891,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
to the same server. We may make this configurable later or
use ses->maxReq */
+ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
+ in_buf->smb_buf_length));
+ return -EIO;
+ }
+
rc = wait_for_free_request(ses, CIFS_BLOCKING_OP);
if (rc)
return rc;
@@ -941,36 +905,31 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
return rc;
}
- if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- up(&ses->server->tcpSem);
- cERROR(1, ("Illegal length, greater than maximum frame, %d",
- in_buf->smb_buf_length));
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
DeleteMidQEntry(midQ);
- return -EIO;
+ mutex_unlock(&ses->server->srv_mutex);
+ return rc;
}
- rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
-
midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
atomic_inc(&ses->server->inSend);
#endif
- rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
- (struct sockaddr *) &(ses->server->addr.sockAddr),
- ses->server->noblocksnd);
+ rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length);
#ifdef CONFIG_CIFS_STATS2
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) {
DeleteMidQEntry(midQ);
@@ -1052,44 +1011,48 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
cERROR(1, ("Frame too large received. Length: %d Xid: %d",
receive_len, xid));
rc = -EIO;
- } else { /* rcvd frame is ok */
-
- if (midQ->resp_buf && out_buf
- && (midQ->midState == MID_RESPONSE_RECEIVED)) {
- out_buf->smb_buf_length = receive_len;
- memcpy((char *)out_buf + 4,
- (char *)midQ->resp_buf + 4,
- receive_len);
-
- dump_smb(out_buf, 92);
- /* convert the length into a more usable form */
- if ((receive_len > 24) &&
- (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
- SECMODE_SIGN_ENABLED))) {
- rc = cifs_verify_signature(out_buf,
- &ses->server->mac_signing_key,
- midQ->sequence_number+1);
- if (rc) {
- cERROR(1, ("Unexpected SMB signature"));
- /* BB FIXME add code to kill session */
- }
- }
+ goto out;
+ }
- *pbytes_returned = out_buf->smb_buf_length;
+ /* rcvd frame is ok */
- /* BB special case reconnect tid and uid here? */
- rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+ if ((out_buf == NULL) || (midQ->midState != MID_RESPONSE_RECEIVED)) {
+ rc = -EIO;
+ cERROR(1, ("Bad MID state?"));
+ goto out;
+ }
- /* convert ByteCount if necessary */
- if (receive_len >= sizeof(struct smb_hdr) - 4
- /* do not count RFC1001 header */ +
- (2 * out_buf->WordCount) + 2 /* bcc */ )
- BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
- } else {
- rc = -EIO;
- cERROR(1, ("Bad MID state?"));
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
+ &ses->server->mac_signing_key,
+ midQ->sequence_number+1);
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
}
}
+
+ *pbytes_returned = out_buf->smb_buf_length;
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * out_buf->WordCount) + 2 /* bcc */ )
+ BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+
+out:
DeleteMidQEntry(midQ);
if (rstart && rc == -EACCES)
return -ERESTARTSYS;