Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / fs / cifs / cifssmb.c
index a80f7bd..c6b8cbc 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/slab.h>
 #include <linux/posix_acl_xattr.h>
 #include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/task_io_accounting_ops.h>
 #include <asm/uaccess.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
@@ -40,6 +42,7 @@
 #include "cifsproto.h"
 #include "cifs_unicode.h"
 #include "cifs_debug.h"
+#include "fscache.h"
 
 #ifdef CONFIG_CIFS_POSIX
 static struct {
@@ -83,6 +86,35 @@ static struct {
 #endif /* CONFIG_CIFS_WEAK_PW_HASH */
 #endif /* CIFS_POSIX */
 
+/* Forward declarations */
+static void cifs_readv_complete(struct work_struct *work);
+
+#ifdef CONFIG_HIGHMEM
+/*
+ * On arches that have high memory, kmap address space is limited. By
+ * serializing the kmap operations on those arches, we ensure that we don't
+ * end up with a bunch of threads in writeback with partially mapped page
+ * arrays, stuck waiting for kmap to come back. That situation prevents
+ * progress and can deadlock.
+ */
+static DEFINE_MUTEX(cifs_kmap_mutex);
+
+static inline void
+cifs_kmap_lock(void)
+{
+       mutex_lock(&cifs_kmap_mutex);
+}
+
+static inline void
+cifs_kmap_unlock(void)
+{
+       mutex_unlock(&cifs_kmap_mutex);
+}
+#else /* !CONFIG_HIGHMEM */
+#define cifs_kmap_lock() do { ; } while(0)
+#define cifs_kmap_unlock() do { ; } while(0)
+#endif /* CONFIG_HIGHMEM */
+
 /* Mark as invalid, all open files on tree connections since they
    were closed when session to server was lost */
 static void mark_open_files_invalid(struct cifs_tcon *pTcon)
@@ -452,9 +484,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
                        goto neg_err_exit;
                }
                server->sec_mode = (__u8)le16_to_cpu(rsp->SecurityMode);
-               server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
-               server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
-                               (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+               server->maxReq = min_t(unsigned int,
+                                      le16_to_cpu(rsp->MaxMpxCount),
+                                      cifs_max_pending);
+               server->oplocks = server->maxReq > 1 ? enable_oplocks : false;
+               server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
                server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
                /* even though we do not use raw we might as well set this
                accurately, in case we ever find a need for it */
@@ -559,10 +593,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
 
        /* one byte, so no need to convert this or EncryptionKeyLen from
           little endian */
-       server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
+       server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount),
+                              cifs_max_pending);
+       server->oplocks = server->maxReq > 1 ? enable_oplocks : false;
        /* probably no need to store and check maxvcs */
-       server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
-                       (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+       server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);
        server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
        cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
        server->capabilities = le32_to_cpu(pSMBr->Capabilities);
@@ -729,6 +764,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
        if (rc)
                return rc;
 
+       if (server->capabilities & CAP_UNICODE)
+               smb->hdr.Flags2 |= SMBFLG2_UNICODE;
+
        /* set up echo request */
        smb->hdr.Tid = 0xffff;
        smb->hdr.WordCount = 1;
@@ -739,7 +777,8 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
        iov.iov_base = smb;
        iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
 
-       rc = cifs_call_async(server, &iov, 1, cifs_echo_callback, server, true);
+       rc = cifs_call_async(server, &iov, 1, NULL, cifs_echo_callback,
+                            server, true);
        if (rc)
                cFYI(1, "Echo request failed: %d", rc);
 
@@ -1376,6 +1415,361 @@ openRetry:
        return rc;
 }
 
+struct cifs_readdata *
+cifs_readdata_alloc(unsigned int nr_pages)
+{
+       struct cifs_readdata *rdata;
+
+       /* readdata + 1 kvec for each page */
+       rdata = kzalloc(sizeof(*rdata) +
+                       sizeof(struct kvec) * nr_pages, GFP_KERNEL);
+       if (rdata != NULL) {
+               INIT_WORK(&rdata->work, cifs_readv_complete);
+               INIT_LIST_HEAD(&rdata->pages);
+       }
+       return rdata;
+}
+
+void
+cifs_readdata_free(struct cifs_readdata *rdata)
+{
+       cifsFileInfo_put(rdata->cfile);
+       kfree(rdata);
+}
+
+/*
+ * Discard any remaining data in the current SMB. To do this, we borrow the
+ * current bigbuf.
+ */
+static int
+cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+       READ_RSP *rsp = (READ_RSP *)server->smallbuf;
+       unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length);
+       int remaining = rfclen + 4 - server->total_read;
+       struct cifs_readdata *rdata = mid->callback_data;
+
+       while (remaining > 0) {
+               int length;
+
+               length = cifs_read_from_socket(server, server->bigbuf,
+                               min_t(unsigned int, remaining,
+                                       CIFSMaxBufSize + MAX_CIFS_HDR_SIZE));
+               if (length < 0)
+                       return length;
+               server->total_read += length;
+               remaining -= length;
+       }
+
+       dequeue_mid(mid, rdata->result);
+       return 0;
+}
+
+static int
+cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+       int length, len;
+       unsigned int data_offset, remaining, data_len;
+       struct cifs_readdata *rdata = mid->callback_data;
+       READ_RSP *rsp = (READ_RSP *)server->smallbuf;
+       unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length) + 4;
+       u64 eof;
+       pgoff_t eof_index;
+       struct page *page, *tpage;
+
+       cFYI(1, "%s: mid=%u offset=%llu bytes=%u", __func__,
+               mid->mid, rdata->offset, rdata->bytes);
+
+       /*
+        * read the rest of READ_RSP header (sans Data array), or whatever we
+        * can if there's not enough data. At this point, we've read down to
+        * the Mid.
+        */
+       len = min_t(unsigned int, rfclen, sizeof(*rsp)) -
+                       sizeof(struct smb_hdr) + 1;
+
+       rdata->iov[0].iov_base = server->smallbuf + sizeof(struct smb_hdr) - 1;
+       rdata->iov[0].iov_len = len;
+
+       length = cifs_readv_from_socket(server, rdata->iov, 1, len);
+       if (length < 0)
+               return length;
+       server->total_read += length;
+
+       /* Was the SMB read successful? */
+       rdata->result = map_smb_to_linux_error(&rsp->hdr, false);
+       if (rdata->result != 0) {
+               cFYI(1, "%s: server returned error %d", __func__,
+                       rdata->result);
+               return cifs_readv_discard(server, mid);
+       }
+
+       /* Is there enough to get to the rest of the READ_RSP header? */
+       if (server->total_read < sizeof(READ_RSP)) {
+               cFYI(1, "%s: server returned short header. got=%u expected=%zu",
+                       __func__, server->total_read, sizeof(READ_RSP));
+               rdata->result = -EIO;
+               return cifs_readv_discard(server, mid);
+       }
+
+       data_offset = le16_to_cpu(rsp->DataOffset) + 4;
+       if (data_offset < server->total_read) {
+               /*
+                * win2k8 sometimes sends an offset of 0 when the read
+                * is beyond the EOF. Treat it as if the data starts just after
+                * the header.
+                */
+               cFYI(1, "%s: data offset (%u) inside read response header",
+                       __func__, data_offset);
+               data_offset = server->total_read;
+       } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
+               /* data_offset is beyond the end of smallbuf */
+               cFYI(1, "%s: data offset (%u) beyond end of smallbuf",
+                       __func__, data_offset);
+               rdata->result = -EIO;
+               return cifs_readv_discard(server, mid);
+       }
+
+       cFYI(1, "%s: total_read=%u data_offset=%u", __func__,
+               server->total_read, data_offset);
+
+       len = data_offset - server->total_read;
+       if (len > 0) {
+               /* read any junk before data into the rest of smallbuf */
+               rdata->iov[0].iov_base = server->smallbuf + server->total_read;
+               rdata->iov[0].iov_len = len;
+               length = cifs_readv_from_socket(server, rdata->iov, 1, len);
+               if (length < 0)
+                       return length;
+               server->total_read += length;
+       }
+
+       /* set up first iov for signature check */
+       rdata->iov[0].iov_base = server->smallbuf;
+       rdata->iov[0].iov_len = server->total_read;
+       cFYI(1, "0: iov_base=%p iov_len=%zu",
+               rdata->iov[0].iov_base, rdata->iov[0].iov_len);
+
+       /* how much data is in the response? */
+       data_len = le16_to_cpu(rsp->DataLengthHigh) << 16;
+       data_len += le16_to_cpu(rsp->DataLength);
+       if (data_offset + data_len > rfclen) {
+               /* data_len is corrupt -- discard frame */
+               rdata->result = -EIO;
+               return cifs_readv_discard(server, mid);
+       }
+
+       /* marshal up the page array */
+       len = 0;
+       remaining = data_len;
+       rdata->nr_iov = 1;
+
+       /* determine the eof that the server (probably) has */
+       eof = CIFS_I(rdata->mapping->host)->server_eof;
+       eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
+       cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index);
+
+       cifs_kmap_lock();
+       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+               if (remaining >= PAGE_CACHE_SIZE) {
+                       /* enough data to fill the page */
+                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+                       rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE;
+                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+                               rdata->nr_iov, page->index,
+                               rdata->iov[rdata->nr_iov].iov_base,
+                               rdata->iov[rdata->nr_iov].iov_len);
+                       ++rdata->nr_iov;
+                       len += PAGE_CACHE_SIZE;
+                       remaining -= PAGE_CACHE_SIZE;
+               } else if (remaining > 0) {
+                       /* enough for partial page, fill and zero the rest */
+                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+                       rdata->iov[rdata->nr_iov].iov_len = remaining;
+                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+                               rdata->nr_iov, page->index,
+                               rdata->iov[rdata->nr_iov].iov_base,
+                               rdata->iov[rdata->nr_iov].iov_len);
+                       memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
+                               '\0', PAGE_CACHE_SIZE - remaining);
+                       ++rdata->nr_iov;
+                       len += remaining;
+                       remaining = 0;
+               } else if (page->index > eof_index) {
+                       /*
+                        * The VFS will not try to do readahead past the
+                        * i_size, but it's possible that we have outstanding
+                        * writes with gaps in the middle and the i_size hasn't
+                        * caught up yet. Populate those with zeroed out pages
+                        * to prevent the VFS from repeatedly attempting to
+                        * fill them until the writes are flushed.
+                        */
+                       zero_user(page, 0, PAGE_CACHE_SIZE);
+                       list_del(&page->lru);
+                       lru_cache_add_file(page);
+                       flush_dcache_page(page);
+                       SetPageUptodate(page);
+                       unlock_page(page);
+                       page_cache_release(page);
+               } else {
+                       /* no need to hold page hostage */
+                       list_del(&page->lru);
+                       lru_cache_add_file(page);
+                       unlock_page(page);
+                       page_cache_release(page);
+               }
+       }
+       cifs_kmap_unlock();
+
+       /* issue the read if we have any iovecs left to fill */
+       if (rdata->nr_iov > 1) {
+               length = cifs_readv_from_socket(server, &rdata->iov[1],
+                                               rdata->nr_iov - 1, len);
+               if (length < 0)
+                       return length;
+               server->total_read += length;
+       } else {
+               length = 0;
+       }
+
+       rdata->bytes = length;
+
+       cFYI(1, "total_read=%u rfclen=%u remaining=%u", server->total_read,
+               rfclen, remaining);
+
+       /* discard anything left over */
+       if (server->total_read < rfclen)
+               return cifs_readv_discard(server, mid);
+
+       dequeue_mid(mid, false);
+       return length;
+}
+
+static void
+cifs_readv_complete(struct work_struct *work)
+{
+       struct cifs_readdata *rdata = container_of(work,
+                                               struct cifs_readdata, work);
+       struct page *page, *tpage;
+
+       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+               list_del(&page->lru);
+               lru_cache_add_file(page);
+
+               if (rdata->result == 0) {
+                       kunmap(page);
+                       flush_dcache_page(page);
+                       SetPageUptodate(page);
+               }
+
+               unlock_page(page);
+
+               if (rdata->result == 0)
+                       cifs_readpage_to_fscache(rdata->mapping->host, page);
+
+               page_cache_release(page);
+       }
+       cifs_readdata_free(rdata);
+}
+
+static void
+cifs_readv_callback(struct mid_q_entry *mid)
+{
+       struct cifs_readdata *rdata = mid->callback_data;
+       struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+       struct TCP_Server_Info *server = tcon->ses->server;
+
+       cFYI(1, "%s: mid=%u state=%d result=%d bytes=%u", __func__,
+               mid->mid, mid->midState, rdata->result, rdata->bytes);
+
+       switch (mid->midState) {
+       case MID_RESPONSE_RECEIVED:
+               /* result already set, check signature */
+               if (server->sec_mode &
+                   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+                       if (cifs_verify_signature(rdata->iov, rdata->nr_iov,
+                                         server, mid->sequence_number + 1))
+                               cERROR(1, "Unexpected SMB signature");
+               }
+               /* FIXME: should this be counted toward the initiating task? */
+               task_io_account_read(rdata->bytes);
+               cifs_stats_bytes_read(tcon, rdata->bytes);
+               break;
+       case MID_REQUEST_SUBMITTED:
+       case MID_RETRY_NEEDED:
+               rdata->result = -EAGAIN;
+               break;
+       default:
+               rdata->result = -EIO;
+       }
+
+       queue_work(system_nrt_wq, &rdata->work);
+       DeleteMidQEntry(mid);
+       atomic_dec(&server->inFlight);
+       wake_up(&server->request_q);
+}
+
+/* cifs_async_readv - send an async write, and set up mid to handle result */
+int
+cifs_async_readv(struct cifs_readdata *rdata)
+{
+       int rc;
+       READ_REQ *smb = NULL;
+       int wct;
+       struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+
+       cFYI(1, "%s: offset=%llu bytes=%u", __func__,
+               rdata->offset, rdata->bytes);
+
+       if (tcon->ses->capabilities & CAP_LARGE_FILES)
+               wct = 12;
+       else {
+               wct = 10; /* old style read */
+               if ((rdata->offset >> 32) > 0)  {
+                       /* can not handle this big offset for old */
+                       return -EIO;
+               }
+       }
+
+       rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb);
+       if (rc)
+               return rc;
+
+       smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid);
+       smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16));
+
+       smb->AndXCommand = 0xFF;        /* none */
+       smb->Fid = rdata->cfile->netfid;
+       smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF);
+       if (wct == 12)
+               smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32);
+       smb->Remaining = 0;
+       smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF);
+       smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16);
+       if (wct == 12)
+               smb->ByteCount = 0;
+       else {
+               /* old style read */
+               struct smb_com_readx_req *smbr =
+                       (struct smb_com_readx_req *)smb;
+               smbr->ByteCount = 0;
+       }
+
+       /* 4 for RFC1001 length + 1 for BCC */
+       rdata->iov[0].iov_base = smb;
+       rdata->iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
+
+       rc = cifs_call_async(tcon->ses->server, rdata->iov, 1,
+                            cifs_readv_receive, cifs_readv_callback,
+                            rdata, false);
+
+       if (rc == 0)
+               cifs_stats_inc(&tcon->num_reads);
+
+       cifs_small_buf_release(smb);
+       return rc;
+}
+
 int
 CIFSSMBRead(const int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes,
            char **buf, int *pbuf_type)
@@ -1808,6 +2202,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
        iov[0].iov_base = smb;
 
        /* marshal up the pages into iov array */
+       cifs_kmap_lock();
        wdata->bytes = 0;
        for (i = 0; i < wdata->nr_pages; i++) {
                iov[i + 1].iov_len = min(inode->i_size -
@@ -1816,6 +2211,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
                iov[i + 1].iov_base = kmap(wdata->pages[i]);
                wdata->bytes += iov[i + 1].iov_len;
        }
+       cifs_kmap_unlock();
 
        cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
 
@@ -1836,7 +2232,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
 
        kref_get(&wdata->refcount);
        rc = cifs_call_async(tcon->ses->server, iov, wdata->nr_pages + 1,
-                            cifs_writev_callback, wdata, false);
+                            NULL, cifs_writev_callback, wdata, false);
 
        if (rc == 0)
                cifs_stats_inc(&tcon->num_writes);
@@ -1962,10 +2358,50 @@ CIFSSMBWrite2(const int xid, struct cifs_io_parms *io_parms,
        return rc;
 }
 
+int cifs_lockv(const int xid, struct cifs_tcon *tcon, const __u16 netfid,
+              const __u8 lock_type, const __u32 num_unlock,
+              const __u32 num_lock, LOCKING_ANDX_RANGE *buf)
+{
+       int rc = 0;
+       LOCK_REQ *pSMB = NULL;
+       struct kvec iov[2];
+       int resp_buf_type;
+       __u16 count;
+
+       cFYI(1, "cifs_lockv num lock %d num unlock %d", num_lock, num_unlock);
+
+       rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
+       if (rc)
+               return rc;
+
+       pSMB->Timeout = 0;
+       pSMB->NumberOfLocks = cpu_to_le16(num_lock);
+       pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock);
+       pSMB->LockType = lock_type;
+       pSMB->AndXCommand = 0xFF; /* none */
+       pSMB->Fid = netfid; /* netfid stays le */
+
+       count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+       inc_rfc1001_len(pSMB, count);
+       pSMB->ByteCount = cpu_to_le16(count);
+
+       iov[0].iov_base = (char *)pSMB;
+       iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 -
+                        (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+       iov[1].iov_base = (char *)buf;
+       iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+
+       cifs_stats_inc(&tcon->num_locks);
+       rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP);
+       if (rc)
+               cFYI(1, "Send error in cifs_lockv = %d", rc);
+
+       return rc;
+}
 
 int
 CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
-           const __u16 smb_file_id, const __u64 len,
+           const __u16 smb_file_id, const __u32 netpid, const __u64 len,
            const __u64 offset, const __u32 numUnlock,
            const __u32 numLock, const __u8 lockType,
            const bool waitFlag, const __u8 oplock_level)
@@ -2001,7 +2437,7 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
        pSMB->Fid = smb_file_id; /* netfid stays le */
 
        if ((numLock != 0) || (numUnlock != 0)) {
-               pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
+               pSMB->Locks[0].Pid = cpu_to_le16(netpid);
                /* BB where to store pid high? */
                pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
                pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
@@ -2035,9 +2471,9 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
 
 int
 CIFSSMBPosixLock(const int xid, struct cifs_tcon *tcon,
-               const __u16 smb_file_id, const int get_flag, const __u64 len,
-               struct file_lock *pLockData, const __u16 lock_type,
-               const bool waitFlag)
+               const __u16 smb_file_id, const __u32 netpid, const int get_flag,
+               const __u64 len, struct file_lock *pLockData,
+               const __u16 lock_type, const bool waitFlag)
 {
        struct smb_com_transaction2_sfi_req *pSMB  = NULL;
        struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
@@ -2095,7 +2531,7 @@ CIFSSMBPosixLock(const int xid, struct cifs_tcon *tcon,
        } else
                pSMB->Timeout = 0;
 
-       parm_data->pid = cpu_to_le32(current->tgid);
+       parm_data->pid = cpu_to_le32(netpid);
        parm_data->start = cpu_to_le64(pLockData->fl_start);
        parm_data->length = cpu_to_le64(len);  /* normalize negative numbers */
 
@@ -2812,8 +3248,7 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifs_tcon *tcon,
        pSMB->TotalDataCount = 0;
        pSMB->MaxParameterCount = cpu_to_le32(2);
        /* BB find exact data count max from sess structure BB */
-       pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
-                                         MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+       pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
        pSMB->MaxSetupCount = 4;
        pSMB->Reserved = 0;
        pSMB->ParameterOffset = 0;
@@ -3005,11 +3440,13 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
                return 0;
        }
        cifs_acl->version = cpu_to_le16(1);
-       if (acl_type == ACL_TYPE_ACCESS)
+       if (acl_type == ACL_TYPE_ACCESS) {
                cifs_acl->access_entry_count = cpu_to_le16(count);
-       else if (acl_type == ACL_TYPE_DEFAULT)
+               cifs_acl->default_entry_count = __constant_cpu_to_le16(0xFFFF);
+       } else if (acl_type == ACL_TYPE_DEFAULT) {
                cifs_acl->default_entry_count = cpu_to_le16(count);
-       else {
+               cifs_acl->access_entry_count = __constant_cpu_to_le16(0xFFFF);
+       } else {
                cFYI(1, "unknown ACL type %d", acl_type);
                return 0;
        }
@@ -3306,8 +3743,7 @@ smb_init_nttransact(const __u16 sub_command, const int setup_count,
        pSMB->Reserved = 0;
        pSMB->TotalParameterCount = cpu_to_le32(parm_len);
        pSMB->TotalDataCount  = 0;
-       pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
-                                         MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+       pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
        pSMB->ParameterCount = pSMB->TotalParameterCount;
        pSMB->DataCount  = pSMB->TotalDataCount;
        temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
@@ -3467,19 +3903,18 @@ qsec_out:
 
 int
 CIFSSMBSetCIFSACL(const int xid, struct cifs_tcon *tcon, __u16 fid,
-                       struct cifs_ntsd *pntsd, __u32 acllen)
+                       struct cifs_ntsd *pntsd, __u32 acllen, int aclflag)
 {
        __u16 byte_count, param_count, data_count, param_offset, data_offset;
        int rc = 0;
        int bytes_returned = 0;
        SET_SEC_DESC_REQ *pSMB = NULL;
-       NTRANSACT_RSP *pSMBr = NULL;
+       void *pSMBr;
 
 setCifsAclRetry:
-       rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
-                       (void **) &pSMBr);
+       rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr);
        if (rc)
-                       return (rc);
+               return rc;
 
        pSMB->MaxSetupCount = 0;
        pSMB->Reserved = 0;
@@ -3504,12 +3939,11 @@ setCifsAclRetry:
 
        pSMB->Fid = fid; /* file handle always le */
        pSMB->Reserved2 = 0;
-       pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
+       pSMB->AclFlags = cpu_to_le32(aclflag);
 
        if (pntsd && acllen) {
-               memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
-                       (char *) pntsd,
-                       acllen);
+               memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) +
+                               data_offset, pntsd, acllen);
                inc_rfc1001_len(pSMB, byte_count + data_count);
        } else
                inc_rfc1001_len(pSMB, byte_count);
@@ -3926,7 +4360,7 @@ int
 CIFSFindFirst(const int xid, struct cifs_tcon *tcon,
              const char *searchName,
              const struct nls_table *nls_codepage,
-             __u16 *pnetfid,
+             __u16 *pnetfid, __u16 search_flags,
              struct cifs_search_info *psrch_inf, int remap, const char dirsep)
 {
 /* level 257 SMB_ */
@@ -3977,8 +4411,7 @@ findFirstRetry:
        params = 12 + name_len /* includes null */ ;
        pSMB->TotalDataCount = 0;       /* no EAs */
        pSMB->MaxParameterCount = cpu_to_le16(10);
-       pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
-                                         MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+       pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
        pSMB->MaxSetupCount = 0;
        pSMB->Reserved = 0;
        pSMB->Flags = 0;
@@ -3999,8 +4432,7 @@ findFirstRetry:
            cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
                        ATTR_DIRECTORY);
        pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
-       pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
-               CIFS_SEARCH_RETURN_RESUME);
+       pSMB->SearchFlags = cpu_to_le16(search_flags);
        pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
 
        /* BB what should we set StorageType to? Does it matter? BB */
@@ -4052,8 +4484,7 @@ findFirstRetry:
                        psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
                                psrch_inf->entries_in_buffer;
                        lnoff = le16_to_cpu(parms->LastNameOffset);
-                       if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
-                             lnoff) {
+                       if (CIFSMaxBufSize < lnoff) {
                                cERROR(1, "ignoring corrupt resume name");
                                psrch_inf->last_entry = NULL;
                                return rc;
@@ -4071,8 +4502,8 @@ findFirstRetry:
        return rc;
 }
 
-int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
-                __u16 searchHandle, struct cifs_search_info *psrch_inf)
+int CIFSFindNext(const int xid, struct cifs_tcon *tcon, __u16 searchHandle,
+                __u16 search_flags, struct cifs_search_info *psrch_inf)
 {
        TRANSACTION2_FNEXT_REQ *pSMB = NULL;
        TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
@@ -4097,9 +4528,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
        byte_count = 0;
        pSMB->TotalDataCount = 0;       /* no EAs */
        pSMB->MaxParameterCount = cpu_to_le16(8);
-       pSMB->MaxDataCount =
-               cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
-                               0xFFFFFF00);
+       pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
        pSMB->MaxSetupCount = 0;
        pSMB->Reserved = 0;
        pSMB->Flags = 0;
@@ -4117,8 +4546,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
                cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
        pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
        pSMB->ResumeKey = psrch_inf->resume_key;
-       pSMB->SearchFlags =
-             cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
+       pSMB->SearchFlags = cpu_to_le16(search_flags);
 
        name_len = psrch_inf->resume_name_len;
        params += name_len;
@@ -4181,8 +4609,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
                        psrch_inf->index_of_last_entry +=
                                psrch_inf->entries_in_buffer;
                        lnoff = le16_to_cpu(parms->LastNameOffset);
-                       if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
-                             lnoff) {
+                       if (CIFSMaxBufSize < lnoff) {
                                cERROR(1, "ignoring corrupt resume name");
                                psrch_inf->last_entry = NULL;
                                return rc;
@@ -4430,8 +4857,12 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
                max_len = data_end - temp;
                node->node_name = cifs_strndup_from_ucs(temp, max_len,
                                                      is_unicode, nls_codepage);
-               if (!node->node_name)
+               if (!node->node_name) {
                        rc = -ENOMEM;
+                       goto parse_DFS_referrals_exit;
+               }
+
+               ref++;
        }
 
 parse_DFS_referrals_exit:
@@ -5291,7 +5722,8 @@ CIFSSMBSetFileInfo(const int xid, struct cifs_tcon *tcon,
        param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
        offset = param_offset + params;
 
-       data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+       data_offset = (char *)pSMB +
+                       offsetof(struct smb_hdr, Protocol) + offset;
 
        count = sizeof(FILE_BASIC_INFO);
        pSMB->MaxParameterCount = cpu_to_le16(2);
@@ -5560,7 +5992,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon,
                       u16 fid, u32 pid_of_opener)
 {
        struct smb_com_transaction2_sfi_req *pSMB  = NULL;
-       FILE_UNIX_BASIC_INFO *data_offset;
+       char *data_offset;
        int rc = 0;
        u16 params, param_offset, offset, byte_count, count;
 
@@ -5582,8 +6014,9 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon,
        param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
        offset = param_offset + params;
 
-       data_offset = (FILE_UNIX_BASIC_INFO *)
-                               ((char *)(&pSMB->hdr.Protocol) + offset);
+       data_offset = (char *)pSMB +
+                       offsetof(struct smb_hdr, Protocol) + offset;
+
        count = sizeof(FILE_UNIX_BASIC_INFO);
 
        pSMB->MaxParameterCount = cpu_to_le16(2);
@@ -5605,7 +6038,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon,
        inc_rfc1001_len(pSMB, byte_count);
        pSMB->ByteCount = cpu_to_le16(byte_count);
 
-       cifs_fill_unix_set_info(data_offset, args);
+       cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args);
 
        rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
        if (rc)
@@ -5840,7 +6273,7 @@ QAllEAsRetry:
 
                if (ea_name) {
                        if (ea_name_len == name_len &&
-                           strncmp(ea_name, temp_ptr, name_len) == 0) {
+                           memcmp(ea_name, temp_ptr, name_len) == 0) {
                                temp_ptr += name_len + 1;
                                rc = value_len;
                                if (buf_size == 0)
@@ -6035,12 +6468,7 @@ int CIFSSMBNotify(const int xid, struct cifs_tcon *tcon,
        pSMB->TotalParameterCount = 0 ;
        pSMB->TotalDataCount = 0;
        pSMB->MaxParameterCount = cpu_to_le32(2);
-       /* BB find exact data count max from sess structure BB */
-       pSMB->MaxDataCount = 0; /* same in little endian or be */
-/* BB VERIFY verify which is correct for above BB */
-       pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
-                                            MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
-
+       pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
        pSMB->MaxSetupCount = 4;
        pSMB->Reserved = 0;
        pSMB->ParameterOffset = 0;