diff -Naur cifs-1.34-RHEL4/fs/cifs/cifsfs.c cifs-1.34-SLES9-SP2/fs/cifs/cifsfs.c --- cifs-1.34-RHEL4/fs/cifs/cifsfs.c 2005-04-22 07:19:19.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/cifsfs.c 2005-04-22 07:22:49.000000000 -0500 @@ -32,6 +32,7 @@ #include #include #include +#include #include "cifsfs.h" #include "cifspdu.h" #define DECLARE_GLOBALS_HERE @@ -538,9 +539,9 @@ }; struct inode_operations cifs_symlink_inode_ops = { - .readlink = generic_readlink, + .readlink = cifs_readlink, .follow_link = cifs_follow_link, - .put_link = cifs_put_link, +/* .put_link = cifs_put_link,*/ .permission = cifs_permission, /* BB add the following two eventually */ /* revalidate: cifs_revalidate, @@ -563,16 +564,11 @@ .flush = cifs_flush, .mmap = cifs_file_mmap, .sendfile = generic_file_sendfile, -#ifdef CONFIG_CIFS_POSIX - .ioctl = cifs_ioctl, -#endif /* CONFIG_CIFS_POSIX */ - #ifdef CONFIG_CIFS_EXPERIMENTAL .readv = generic_file_readv, .writev = generic_file_writev, .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, - .dir_notify = cifs_dir_notify, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; @@ -586,14 +582,7 @@ .lock = cifs_lock, .fsync = cifs_fsync, .flush = cifs_flush, - .sendfile = generic_file_sendfile, /* BB removeme BB */ -#ifdef CONFIG_CIFS_POSIX - .ioctl = cifs_ioctl, -#endif /* CONFIG_CIFS_POSIX */ - -#ifdef CONFIG_CIFS_EXPERIMENTAL - .dir_notify = cifs_dir_notify, -#endif /* CONFIG_CIFS_EXPERIMENTAL */ + .sendfile = generic_file_sendfile, }; struct file_operations cifs_dir_ops = { diff -Naur cifs-1.34-RHEL4/fs/cifs/cifspdu.h cifs-1.34-SLES9-SP2/fs/cifs/cifspdu.h --- cifs-1.34-RHEL4/fs/cifs/cifspdu.h 2005-04-22 07:18:13.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/cifspdu.h 2005-04-22 07:22:49.000000000 -0500 @@ -83,6 +83,14 @@ * All constants go here ***************************************************************** */ +#ifndef __le16 +#define __le16 __u16 +#define __le32 __u32 +#define __le64 __u64 +#define __be16 __u16 +#define __be32 __u32 +#define __be64 __u64 +#endif /* * Starting value for maximum SMB size negotiation diff -Naur cifs-1.34-RHEL4/fs/cifs/connect.c cifs-1.34-SLES9-SP2/fs/cifs/connect.c --- cifs-1.34-RHEL4/fs/cifs/connect.c 2005-04-22 07:19:19.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/connect.c 2005-04-22 07:22:49.000000000 -0500 @@ -318,7 +318,8 @@ struct smb_hdr *bigbuf = NULL; struct smb_hdr *smallbuf = NULL; struct msghdr smb_msg; - struct kvec iov; + mm_segment_t temp_fs; + struct iovec iov; struct socket *csocket = server->ssocket; struct list_head *tmp; struct cifsSesInfo *ses; @@ -334,6 +335,9 @@ current->flags |= PF_MEMALLOC; server->tsk = current; /* save process info to wake at shutdown */ cFYI(1, ("Demultiplex PID: %d", current->pid)); + temp_fs = get_fs(); /* we must turn off socket api parm checking */ + set_fs(get_ds()); + write_lock(&GlobalSMBSeslock); atomic_inc(&tcpSesAllocCount); length = tcpSesAllocCount.counter; @@ -375,11 +379,13 @@ smb_buffer = smallbuf; iov.iov_base = smb_buffer; iov.iov_len = 4; + smb_msg.msg_iov = &iov; + smb_msg.msg_iovlen = 1; smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; length = - kernel_recvmsg(csocket, &smb_msg, - &iov, 1, 4, 0 /* BB see socket.h flags */); + sock_recvmsg(csocket, &smb_msg, + 4, 0 /* BB see socket.h flags */); if(server->tcpStatus == CifsExiting) { break; @@ -492,8 +498,8 @@ iov.iov_len = pdu_length; for (total_read = 0; total_read < pdu_length; total_read += length) { - length = kernel_recvmsg(csocket, &smb_msg, &iov, 1, - pdu_length - total_read, 0); + length = sock_recvmsg(csocket, &smb_msg, + pdu_length - total_read, 0); if((server->tcpStatus == CifsExiting) || (length == -EINTR)) { /* then will exit */ @@ -1250,7 +1256,7 @@ __be16 orig_port = 0; if(*csocket == NULL) { - rc = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, csocket); + rc = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, csocket); if (rc < 0) { cERROR(1, ("Error %d creating socket",rc)); *csocket = NULL; @@ -1358,7 +1364,7 @@ __be16 orig_port = 0; if(*csocket == NULL) { - rc = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, csocket); + rc = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, csocket); if (rc < 0) { cERROR(1, ("Error %d creating ipv6 socket",rc)); *csocket = NULL; diff -Naur cifs-1.34-RHEL4/fs/cifs/connect.c.orig cifs-1.34-SLES9-SP2/fs/cifs/connect.c.orig --- cifs-1.34-RHEL4/fs/cifs/connect.c.orig 2005-04-22 07:18:13.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/connect.c.orig 2005-04-22 07:20:44.000000000 -0500 @@ -1734,8 +1734,6 @@ sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */ } - sb->s_time_gran = 100; - /* on error free sesinfo and tcon struct if needed */ if (rc) { /* if session setup failed, use count is zero but diff -Naur cifs-1.34-RHEL4/fs/cifs/dir.c cifs-1.34-SLES9-SP2/fs/cifs/dir.c --- cifs-1.34-RHEL4/fs/cifs/dir.c 2005-04-22 07:18:13.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/dir.c 2005-04-22 07:22:49.000000000 -0500 @@ -146,23 +146,23 @@ } if(nd) { - if ((nd->intent.open.flags & O_ACCMODE) == O_RDONLY) + if ((nd->intent.it_flags & O_ACCMODE) == O_RDONLY) desiredAccess = GENERIC_READ; - else if ((nd->intent.open.flags & O_ACCMODE) == O_WRONLY) { + else if ((nd->intent.it_flags & O_ACCMODE) == O_WRONLY) { desiredAccess = GENERIC_WRITE; write_only = TRUE; - } else if ((nd->intent.open.flags & O_ACCMODE) == O_RDWR) { + } else if ((nd->intent.it_flags & O_ACCMODE) == O_RDWR) { /* GENERIC_ALL is too much permission to request */ /* can cause unnecessary access denied on create */ /* desiredAccess = GENERIC_ALL; */ desiredAccess = GENERIC_READ | GENERIC_WRITE; } - if((nd->intent.open.flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + if((nd->intent.it_flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) disposition = FILE_CREATE; - else if((nd->intent.open.flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) + else if((nd->intent.it_flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) disposition = FILE_OVERWRITE_IF; - else if((nd->intent.open.flags & O_CREAT) == O_CREAT) + else if((nd->intent.it_flags & O_CREAT) == O_CREAT) disposition = FILE_OPEN_IF; else { cFYI(1,("Create flag not set in create function")); diff -Naur cifs-1.34-RHEL4/fs/cifs/link.c cifs-1.34-SLES9-SP2/fs/cifs/link.c --- cifs-1.34-RHEL4/fs/cifs/link.c 2005-04-22 07:18:13.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/link.c 2005-04-22 07:22:49.000000000 -0500 @@ -138,6 +138,7 @@ /* BB Add special case check for Samba DFS symlinks */ target_path[PATH_MAX-1] = 0; + rc = vfs_follow_link(nd, target_path); } else { kfree(target_path); target_path = ERR_PTR(rc); @@ -147,7 +148,7 @@ kfree(full_path); out_no_free: FreeXid(xid); - nd_set_link(nd, target_path); + /*nd_set_link(nd, target_path);*/ return 0; } @@ -329,10 +330,3 @@ FreeXid(xid); return rc; } - -void cifs_put_link(struct dentry *direntry, struct nameidata *nd) -{ - char *p = nd_get_link(nd); - if (!IS_ERR(p)) - kfree(p); -} diff -Naur cifs-1.34-RHEL4/fs/cifs/Makefile cifs-1.34-SLES9-SP2/fs/cifs/Makefile --- cifs-1.34-RHEL4/fs/cifs/Makefile 2005-04-22 07:18:13.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/Makefile 2005-04-22 07:22:49.000000000 -0500 @@ -3,4 +3,4 @@ # obj-$(CONFIG_CIFS) += cifs.o -cifs-objs := 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 readdir.o ioctl.o +cifs-objs := cifssmb.o cifs_debug.o file.o inode.o misc.o netmisc.o smbdes.o smbencrypt.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o readdir.o dir.o connect.o link.o transport.o cifsfs.o ioctl.o diff -Naur cifs-1.34-RHEL4/fs/cifs/transport.c cifs-1.34-SLES9-SP2/fs/cifs/transport.c --- cifs-1.34-RHEL4/fs/cifs/transport.c 2005-04-22 07:18:13.000000000 -0500 +++ cifs-1.34-SLES9-SP2/fs/cifs/transport.c 2005-04-22 07:23:26.000000000 -0500 @@ -126,7 +126,8 @@ int rc = 0; int i = 0; struct msghdr smb_msg; - struct kvec iov; + struct iovec iov; + mm_segment_t temp_fs; unsigned len = smb_buf_length + 4; if(ssocket == NULL) @@ -136,6 +137,8 @@ smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); + smb_msg.msg_iov = &iov; + smb_msg.msg_iovlen = 1; smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ @@ -149,8 +152,10 @@ cFYI(1, ("Sending smb of length %d ", smb_buf_length)); dump_smb(smb_buffer, len); + temp_fs = get_fs(); /* we must turn off socket api parm checking */ + set_fs(get_ds()); while (len > 0) { - rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len); + rc = sock_sendmsg(ssocket, &smb_msg, len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; if(i > 60) { @@ -169,6 +174,7 @@ iov.iov_len -= rc; len -= rc; } + set_fs(temp_fs); if (rc < 0) { cERROR(1,("Error %d sending data on socket to server.", rc)); @@ -179,211 +185,6 @@ return rc; } -#ifdef CIFS_EXPERIMENTAL -/* BB finish off this function, adding support for writing set of pages as iovec */ -/* and also adding support for operations that need to parse the response smb */ - -int -smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, - unsigned int smb_buf_length, struct kvec * write_vector - /* page list */, struct sockaddr *sin) -{ - int rc = 0; - int i = 0; - struct msghdr smb_msg; - number_of_pages += 1; /* account for SMB header */ - struct kvec * piov = kmalloc(number_of_pages * sizeof(struct kvec)); - 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; - smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ - - /* 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, number_of_pages, - len); - if ((rc == -ENOSPC) || (rc == -EAGAIN)) { - i++; - if(i > 60) { - cERROR(1, - ("sends on sock %p stuck for 30 seconds", - ssocket)); - rc = -EAGAIN; - break; - } - msleep(500); - continue; - } - if (rc < 0) - break; - 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; - } - - return rc; -} - - -int -CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, - struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) -{ - int rc = 0; - unsigned long timeout = 15 * HZ; - struct mid_q_entry *midQ = NULL; - - if (ses == NULL) { - cERROR(1,("Null smb session")); - return -EIO; - } - if(ses->server == NULL) { - cERROR(1,("Null tcp session")); - return -EIO; - } - if(pbytes_returned == NULL) - return -EIO; - else - *pbytes_returned = 0; - - - - if(ses->server->tcpStatus == CIFS_EXITING) - return -ENOENT; - - /* Ensure that we do not send more than 50 overlapping requests - to the same server. We may make this configurable later or - use ses->maxReq */ - if(long_op == -1) { - /* 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); - wait_event(ses->server->request_q, - atomic_read(&ses->server->inFlight) - < cifs_max_pending); - spin_lock(&GlobalMid_Lock); - } else { - if(ses->server->tcpStatus == CifsExiting) { - spin_unlock(&GlobalMid_Lock); - return -ENOENT; - } - - /* can not count locking commands against total since - they are allowed to block on server */ - - if(long_op < 3) { - /* update # of requests on the wire to server */ - atomic_inc(&ses->server->inFlight); - } - spin_unlock(&GlobalMid_Lock); - break; - } - } - } - /* make sure that we sign in the same order that we send on this socket - and avoid races inside tcp sendmsg code that could cause corruption - of smb data */ - - down(&ses->server->tcpSem); - - if (ses->server->tcpStatus == CifsExiting) { - rc = -ENOENT; - goto cifs_out_label; - } else if (ses->server->tcpStatus == CifsNeedReconnect) { - cFYI(1,("tcp session dead - return to caller to retry")); - rc = -EAGAIN; - goto cifs_out_label; - } else 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)) { - rc = -EAGAIN; - goto cifs_out_label; - } /* else ok - we are setting up session */ - } - midQ = AllocMidQEntry(in_buf, ses); - if (midQ == NULL) { - up(&ses->server->tcpSem); - /* If not lock req, update # of requests on wire to server */ - if(long_op < 3) { - atomic_dec(&ses->server->inFlight); - wake_up(&ses->server->request_q); - } - return -ENOMEM; - } - - 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)); - DeleteMidQEntry(midQ); - /* If not lock req, update # of requests on wire to server */ - if(long_op < 3) { - atomic_dec(&ses->server->inFlight); - wake_up(&ses->server->request_q); - } - return -EIO; - } - - /* BB can we sign efficiently in this path? */ - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); - - midQ->midState = MID_REQUEST_SUBMITTED; -/* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, - piovec, - (struct sockaddr *) &(ses->server->addr.sockAddr));*/ - if(rc < 0) { - DeleteMidQEntry(midQ); - up(&ses->server->tcpSem); - /* If not lock req, update # of requests on wire to server */ - if(long_op < 3) { - atomic_dec(&ses->server->inFlight); - wake_up(&ses->server->request_q); - } - return rc; - } else - up(&ses->server->tcpSem); -cifs_out_label: - if(midQ) - DeleteMidQEntry(midQ); - - if(long_op < 3) { - atomic_dec(&ses->server->inFlight); - wake_up(&ses->server->request_q); - } - - return rc; -} - - -#endif /* CIFS_EXPERIMENTAL */ int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, diff -Naur cifs-1.34-RHEL4/fs/cifs/transport.c.orig cifs-1.34-SLES9-SP2/fs/cifs/transport.c.orig --- cifs-1.34-RHEL4/fs/cifs/transport.c.orig 1969-12-31 18:00:00.000000000 -0600 +++ cifs-1.34-SLES9-SP2/fs/cifs/transport.c.orig 2005-04-22 07:20:44.000000000 -0500 @@ -0,0 +1,635 @@ +/* + * fs/cifs/transport.c + * + * Copyright (C) International Business Machines Corp., 2002,2005 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" + +extern mempool_t *cifs_mid_poolp; +extern kmem_cache_t *cifs_oplock_cachep; + +static struct mid_q_entry * +AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) +{ + struct mid_q_entry *temp; + + if (ses == NULL) { + cERROR(1, ("Null session passed in to AllocMidQEntry")); + return NULL; + } + if (ses->server == NULL) { + cERROR(1, ("Null TCP session in AllocMidQEntry")); + return NULL; + } + + temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS); + if (temp == NULL) + return temp; + else { + memset(temp, 0, sizeof (struct mid_q_entry)); + temp->mid = smb_buffer->Mid; /* always LE */ + temp->pid = current->pid; + temp->command = smb_buffer->Command; + cFYI(1, ("For smb_command %d", temp->command)); + do_gettimeofday(&temp->when_sent); + temp->ses = ses; + temp->tsk = current; + } + + spin_lock(&GlobalMid_Lock); + list_add_tail(&temp->qhead, &ses->server->pending_mid_q); + atomic_inc(&midCount); + temp->midState = MID_REQUEST_ALLOCATED; + spin_unlock(&GlobalMid_Lock); + return temp; +} + +static void +DeleteMidQEntry(struct mid_q_entry *midEntry) +{ + spin_lock(&GlobalMid_Lock); + midEntry->midState = MID_FREE; + list_del(&midEntry->qhead); + atomic_dec(&midCount); + spin_unlock(&GlobalMid_Lock); + if(midEntry->largeBuf) + cifs_buf_release(midEntry->resp_buf); + else + cifs_small_buf_release(midEntry->resp_buf); + mempool_free(midEntry, cifs_mid_poolp); +} + +struct oplock_q_entry * +AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon) +{ + struct oplock_q_entry *temp; + if ((pinode== NULL) || (tcon == NULL)) { + cERROR(1, ("Null parms passed to AllocOplockQEntry")); + return NULL; + } + temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep, + SLAB_KERNEL); + if (temp == NULL) + return temp; + else { + temp->pinode = pinode; + temp->tcon = tcon; + temp->netfid = fid; + spin_lock(&GlobalMid_Lock); + list_add_tail(&temp->qhead, &GlobalOplock_Q); + spin_unlock(&GlobalMid_Lock); + } + return temp; + +} + +void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry) +{ + spin_lock(&GlobalMid_Lock); + /* should we check if list empty first? */ + list_del(&oplockEntry->qhead); + spin_unlock(&GlobalMid_Lock); + kmem_cache_free(cifs_oplock_cachep, oplockEntry); +} + +int +smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, + unsigned int smb_buf_length, struct sockaddr *sin) +{ + 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; + smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ + + /* 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++; + if(i > 60) { + cERROR(1, + ("sends on sock %p stuck for 30 seconds", + ssocket)); + rc = -EAGAIN; + break; + } + msleep(500); + continue; + } + if (rc < 0) + break; + 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; + } + + return rc; +} + +#ifdef CIFS_EXPERIMENTAL +/* BB finish off this function, adding support for writing set of pages as iovec */ +/* and also adding support for operations that need to parse the response smb */ + +int +smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, + unsigned int smb_buf_length, struct kvec * write_vector + /* page list */, struct sockaddr *sin) +{ + int rc = 0; + int i = 0; + struct msghdr smb_msg; + number_of_pages += 1; /* account for SMB header */ + struct kvec * piov = kmalloc(number_of_pages * sizeof(struct kvec)); + 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; + smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ + + /* 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, number_of_pages, + len); + if ((rc == -ENOSPC) || (rc == -EAGAIN)) { + i++; + if(i > 60) { + cERROR(1, + ("sends on sock %p stuck for 30 seconds", + ssocket)); + rc = -EAGAIN; + break; + } + msleep(500); + continue; + } + if (rc < 0) + break; + 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; + } + + return rc; +} + + +int +CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, + struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) +{ + int rc = 0; + unsigned long timeout = 15 * HZ; + struct mid_q_entry *midQ = NULL; + + if (ses == NULL) { + cERROR(1,("Null smb session")); + return -EIO; + } + if(ses->server == NULL) { + cERROR(1,("Null tcp session")); + return -EIO; + } + if(pbytes_returned == NULL) + return -EIO; + else + *pbytes_returned = 0; + + + + if(ses->server->tcpStatus == CIFS_EXITING) + return -ENOENT; + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + if(long_op == -1) { + /* 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); + wait_event(ses->server->request_q, + atomic_read(&ses->server->inFlight) + < cifs_max_pending); + spin_lock(&GlobalMid_Lock); + } else { + if(ses->server->tcpStatus == CifsExiting) { + spin_unlock(&GlobalMid_Lock); + return -ENOENT; + } + + /* can not count locking commands against total since + they are allowed to block on server */ + + if(long_op < 3) { + /* update # of requests on the wire to server */ + atomic_inc(&ses->server->inFlight); + } + spin_unlock(&GlobalMid_Lock); + break; + } + } + } + /* make sure that we sign in the same order that we send on this socket + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ + + down(&ses->server->tcpSem); + + if (ses->server->tcpStatus == CifsExiting) { + rc = -ENOENT; + goto cifs_out_label; + } else if (ses->server->tcpStatus == CifsNeedReconnect) { + cFYI(1,("tcp session dead - return to caller to retry")); + rc = -EAGAIN; + goto cifs_out_label; + } else 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)) { + rc = -EAGAIN; + goto cifs_out_label; + } /* else ok - we are setting up session */ + } + midQ = AllocMidQEntry(in_buf, ses); + if (midQ == NULL) { + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return -ENOMEM; + } + + 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)); + DeleteMidQEntry(midQ); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return -EIO; + } + + /* BB can we sign efficiently in this path? */ + rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); + + midQ->midState = MID_REQUEST_SUBMITTED; +/* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, + piovec, + (struct sockaddr *) &(ses->server->addr.sockAddr));*/ + if(rc < 0) { + DeleteMidQEntry(midQ); + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; + } else + up(&ses->server->tcpSem); +cifs_out_label: + if(midQ) + DeleteMidQEntry(midQ); + + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + + return rc; +} + + +#endif /* CIFS_EXPERIMENTAL */ + +int +SendReceive(const unsigned int xid, struct cifsSesInfo *ses, + struct smb_hdr *in_buf, struct smb_hdr *out_buf, + int *pbytes_returned, const int long_op) +{ + int rc = 0; + unsigned int receive_len; + unsigned long timeout; + struct mid_q_entry *midQ; + + if (ses == NULL) { + cERROR(1,("Null smb session")); + return -EIO; + } + if(ses->server == NULL) { + cERROR(1,("Null tcp session")); + return -EIO; + } + + if(ses->server->tcpStatus == CifsExiting) + return -ENOENT; + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + if(long_op == -1) { + /* 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); + wait_event(ses->server->request_q, + atomic_read(&ses->server->inFlight) + < cifs_max_pending); + spin_lock(&GlobalMid_Lock); + } else { + if(ses->server->tcpStatus == CifsExiting) { + spin_unlock(&GlobalMid_Lock); + return -ENOENT; + } + + /* can not count locking commands against total since + they are allowed to block on server */ + + if(long_op < 3) { + /* update # of requests on the wire to server */ + atomic_inc(&ses->server->inFlight); + } + spin_unlock(&GlobalMid_Lock); + break; + } + } + } + /* make sure that we sign in the same order that we send on this socket + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ + + down(&ses->server->tcpSem); + + if (ses->server->tcpStatus == CifsExiting) { + rc = -ENOENT; + goto out_unlock; + } else if (ses->server->tcpStatus == CifsNeedReconnect) { + cFYI(1,("tcp session dead - return to caller to retry")); + rc = -EAGAIN; + goto out_unlock; + } else 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)) { + rc = -EAGAIN; + goto out_unlock; + } /* else ok - we are setting up session */ + } + midQ = AllocMidQEntry(in_buf, ses); + if (midQ == NULL) { + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return -ENOMEM; + } + + 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)); + DeleteMidQEntry(midQ); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return -EIO; + } + + rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); + + midQ->midState = MID_REQUEST_SUBMITTED; + rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, + (struct sockaddr *) &(ses->server->addr.sockAddr)); + if(rc < 0) { + DeleteMidQEntry(midQ); + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; + } else + up(&ses->server->tcpSem); + if (long_op == -1) + goto cifs_no_response_exit; + else if (long_op == 2) /* writes past end of file can take loong time */ + timeout = 300 * HZ; + else if (long_op == 1) + timeout = 45 * HZ; /* should be greater than + servers oplock break timeout (about 43 seconds) */ + else if (long_op > 2) { + timeout = MAX_SCHEDULE_TIMEOUT; + } else + timeout = 15 * HZ; + /* wait for 15 seconds or until woken up due to response arriving or + due to last connection to this server being unmounted */ + if (signal_pending(current)) { + /* if signal pending do not hold up user for full smb timeout + but we still give response a change to complete */ + timeout = 2 * HZ; + } + + /* No user interrupts in wait - wreaks havoc with performance */ + if(timeout != MAX_SCHEDULE_TIMEOUT) { + timeout += jiffies; + wait_event(ses->server->response_q, + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || + time_after(jiffies, timeout) || + ((ses->server->tcpStatus != CifsGood) && + (ses->server->tcpStatus != CifsNew))); + } else { + wait_event(ses->server->response_q, + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || + ((ses->server->tcpStatus != CifsGood) && + (ses->server->tcpStatus != CifsNew))); + } + + spin_lock(&GlobalMid_Lock); + if (midQ->resp_buf) { + spin_unlock(&GlobalMid_Lock); + receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); + } else { + cERROR(1,("No response buffer")); + if(midQ->midState == MID_REQUEST_SUBMITTED) { + if(ses->server->tcpStatus == CifsExiting) + rc = -EHOSTDOWN; + else { + ses->server->tcpStatus = CifsNeedReconnect; + midQ->midState = MID_RETRY_NEEDED; + } + } + + if (rc != -EHOSTDOWN) { + if(midQ->midState == MID_RETRY_NEEDED) { + rc = -EAGAIN; + cFYI(1,("marking request for retry")); + } else { + rc = -EIO; + } + } + spin_unlock(&GlobalMid_Lock); + DeleteMidQEntry(midQ); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; + } + + 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, + 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); + + /* 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(out_buf)); + } else { + rc = -EIO; + cFYI(1,("Bad MID state? ")); + } + } +cifs_no_response_exit: + DeleteMidQEntry(midQ); + + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + + return rc; + +out_unlock: + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + + return rc; +}