fs/cifs: fix parsing of dfs referrals
[pandora-kernel.git] / fs / cifs / cifssmb.c
index 70aac35..da2f544 100644 (file)
@@ -696,7 +696,7 @@ CIFSSMBTDis(const int xid, struct cifs_tcon *tcon)
        if (rc)
                return rc;
 
-       rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0);
        if (rc)
                cFYI(1, "Tree disconnect failed %d", rc);
 
@@ -792,7 +792,7 @@ CIFSSMBLogoff(const int xid, struct cifs_ses *ses)
        pSMB->hdr.Uid = ses->Suid;
 
        pSMB->AndXCommand = 0xFF;
-       rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);
 session_already_dead:
        mutex_unlock(&ses->session_mutex);
 
@@ -1414,8 +1414,7 @@ cifs_readdata_free(struct cifs_readdata *rdata)
 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);
+       unsigned int rfclen = get_rfc1002_length(server->smallbuf);
        int remaining = rfclen + 4 - server->total_read;
        struct cifs_readdata *rdata = mid->callback_data;
 
@@ -1424,7 +1423,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
                length = cifs_read_from_socket(server, server->bigbuf,
                                min_t(unsigned int, remaining,
-                                       CIFSMaxBufSize + MAX_CIFS_HDR_SIZE));
+                                       CIFSMaxBufSize + max_header_size()));
                if (length < 0)
                        return length;
                server->total_read += length;
@@ -1435,19 +1434,40 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        return 0;
 }
 
+static inline size_t
+read_rsp_size(void)
+{
+       return sizeof(READ_RSP);
+}
+
+static inline unsigned int
+read_data_offset(char *buf)
+{
+       READ_RSP *rsp = (READ_RSP *)buf;
+       return le16_to_cpu(rsp->DataOffset);
+}
+
+static inline unsigned int
+read_data_length(char *buf)
+{
+       READ_RSP *rsp = (READ_RSP *)buf;
+       return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
+              le16_to_cpu(rsp->DataLength);
+}
+
 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;
+       char *buf = server->smallbuf;
+       unsigned int buflen = get_rfc1002_length(buf) + 4;
        u64 eof;
        pgoff_t eof_index;
        struct page *page, *tpage;
 
-       cFYI(1, "%s: mid=%u offset=%llu bytes=%u", __func__,
+       cFYI(1, "%s: mid=%llu offset=%llu bytes=%u", __func__,
                mid->mid, rdata->offset, rdata->bytes);
 
        /*
@@ -1455,10 +1475,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
         * 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;
+       len = min_t(unsigned int, buflen, read_rsp_size()) - header_size() + 1;
 
-       rdata->iov[0].iov_base = server->smallbuf + sizeof(struct smb_hdr) - 1;
+       rdata->iov[0].iov_base = buf + header_size() - 1;
        rdata->iov[0].iov_len = len;
 
        length = cifs_readv_from_socket(server, rdata->iov, 1, len);
@@ -1467,7 +1486,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        server->total_read += length;
 
        /* Was the SMB read successful? */
-       rdata->result = map_smb_to_linux_error(&rsp->hdr, false);
+       rdata->result = map_smb_to_linux_error(buf, false);
        if (rdata->result != 0) {
                cFYI(1, "%s: server returned error %d", __func__,
                        rdata->result);
@@ -1475,14 +1494,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        }
 
        /* Is there enough to get to the rest of the READ_RSP header? */
-       if (server->total_read < sizeof(READ_RSP)) {
+       if (server->total_read < read_rsp_size()) {
                cFYI(1, "%s: server returned short header. got=%u expected=%zu",
-                       __func__, server->total_read, sizeof(READ_RSP));
+                       __func__, server->total_read, read_rsp_size());
                rdata->result = -EIO;
                return cifs_readv_discard(server, mid);
        }
 
-       data_offset = le16_to_cpu(rsp->DataOffset) + 4;
+       data_offset = read_data_offset(buf) + 4;
        if (data_offset < server->total_read) {
                /*
                 * win2k8 sometimes sends an offset of 0 when the read
@@ -1506,7 +1525,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        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_base = buf + server->total_read;
                rdata->iov[0].iov_len = len;
                length = cifs_readv_from_socket(server, rdata->iov, 1, len);
                if (length < 0)
@@ -1515,15 +1534,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        }
 
        /* set up first iov for signature check */
-       rdata->iov[0].iov_base = server->smallbuf;
+       rdata->iov[0].iov_base = buf;
        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 = read_data_length(buf);
+       if (data_offset + data_len > buflen) {
                /* data_len is corrupt -- discard frame */
                rdata->result = -EIO;
                return cifs_readv_discard(server, mid);
@@ -1602,11 +1620,11 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
        rdata->bytes = length;
 
-       cFYI(1, "total_read=%u rfclen=%u remaining=%u", server->total_read,
-               rfclen, remaining);
+       cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
+               buflen, remaining);
 
        /* discard anything left over */
-       if (server->total_read < rfclen)
+       if (server->total_read < buflen)
                return cifs_readv_discard(server, mid);
 
        dequeue_mid(mid, false);
@@ -1647,10 +1665,10 @@ cifs_readv_callback(struct mid_q_entry *mid)
        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);
+       cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
+               mid->mid, mid->mid_state, rdata->result, rdata->bytes);
 
-       switch (mid->midState) {
+       switch (mid->mid_state) {
        case MID_RESPONSE_RECEIVED:
                /* result already set, check signature */
                if (server->sec_mode &
@@ -1671,7 +1689,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
                rdata->result = -EIO;
        }
 
-       queue_work(system_nrt_wq, &rdata->work);
+       queue_work(cifsiod_wq, &rdata->work);
        DeleteMidQEntry(mid);
        cifs_add_credits(server, 1);
 }
@@ -2017,7 +2035,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
        kref_put(&wdata->refcount, cifs_writedata_release);
 }
 
-static void
+void
 cifs_writev_complete(struct work_struct *work)
 {
        struct cifs_writedata *wdata = container_of(work,
@@ -2026,7 +2044,9 @@ cifs_writev_complete(struct work_struct *work)
        int i = 0;
 
        if (wdata->result == 0) {
+               spin_lock(&inode->i_lock);
                cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes);
+               spin_unlock(&inode->i_lock);
                cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink),
                                         wdata->bytes);
        } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN)
@@ -2047,7 +2067,7 @@ cifs_writev_complete(struct work_struct *work)
 }
 
 struct cifs_writedata *
-cifs_writedata_alloc(unsigned int nr_pages)
+cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
 {
        struct cifs_writedata *wdata;
 
@@ -2061,14 +2081,16 @@ cifs_writedata_alloc(unsigned int nr_pages)
        wdata = kzalloc(sizeof(*wdata) +
                        sizeof(struct page *) * (nr_pages - 1), GFP_NOFS);
        if (wdata != NULL) {
-               INIT_WORK(&wdata->work, cifs_writev_complete);
                kref_init(&wdata->refcount);
+               INIT_LIST_HEAD(&wdata->list);
+               init_completion(&wdata->done);
+               INIT_WORK(&wdata->work, complete);
        }
        return wdata;
 }
 
 /*
- * Check the midState and signature on received buffer (if any), and queue the
+ * Check the mid_state and signature on received buffer (if any), and queue the
  * workqueue completion task.
  */
 static void
@@ -2079,7 +2101,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
        unsigned int written;
        WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
 
-       switch (mid->midState) {
+       switch (mid->mid_state) {
        case MID_RESPONSE_RECEIVED:
                wdata->result = cifs_check_receive(mid, tcon->ses->server, 0);
                if (wdata->result != 0)
@@ -2111,7 +2133,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
                break;
        }
 
-       queue_work(system_nrt_wq, &wdata->work);
+       queue_work(cifsiod_wq, &wdata->work);
        DeleteMidQEntry(mid);
        cifs_add_credits(tcon->ses->server, 1);
 }
@@ -2124,7 +2146,6 @@ cifs_async_writev(struct cifs_writedata *wdata)
        WRITE_REQ *smb = NULL;
        int wct;
        struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
-       struct inode *inode = wdata->cfile->dentry->d_inode;
        struct kvec *iov = NULL;
 
        if (tcon->ses->capabilities & CAP_LARGE_FILES) {
@@ -2148,8 +2169,8 @@ cifs_async_writev(struct cifs_writedata *wdata)
                goto async_writev_out;
        }
 
-       smb->hdr.Pid = cpu_to_le16((__u16)wdata->cfile->pid);
-       smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->cfile->pid >> 16));
+       smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
+       smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
 
        smb->AndXCommand = 0xFF;        /* none */
        smb->Fid = wdata->cfile->netfid;
@@ -2167,15 +2188,13 @@ cifs_async_writev(struct cifs_writedata *wdata)
        iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
        iov[0].iov_base = smb;
 
-       /* marshal up the pages into iov array */
-       wdata->bytes = 0;
-       for (i = 0; i < wdata->nr_pages; i++) {
-               iov[i + 1].iov_len = min(inode->i_size -
-                                     page_offset(wdata->pages[i]),
-                                       (loff_t)PAGE_CACHE_SIZE);
-               iov[i + 1].iov_base = kmap(wdata->pages[i]);
-               wdata->bytes += iov[i + 1].iov_len;
-       }
+       /*
+        * This function should marshal up the page array into the kvec
+        * array, reserving [0] for the header. It should kmap the pages
+        * and set the iov_len properly for each one. It may also set
+        * wdata->bytes too.
+        */
+       wdata->marshal_iov(iov, wdata);
 
        cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
 
@@ -2420,8 +2439,7 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
                        (struct smb_hdr *) pSMB, &bytes_returned);
                cifs_small_buf_release(pSMB);
        } else {
-               rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
-                                     timeout);
+               rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, timeout);
                /* SMB buffer freed by function above */
        }
        cifs_stats_inc(&tcon->num_locks);
@@ -2588,7 +2606,7 @@ CIFSSMBClose(const int xid, struct cifs_tcon *tcon, int smb_file_id)
        pSMB->FileID = (__u16) smb_file_id;
        pSMB->LastWriteTime = 0xFFFFFFFF;
        pSMB->ByteCount = 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        cifs_stats_inc(&tcon->num_closes);
        if (rc) {
                if (rc != -EINTR) {
@@ -2617,7 +2635,7 @@ CIFSSMBFlush(const int xid, struct cifs_tcon *tcon, int smb_file_id)
 
        pSMB->FileID = (__u16) smb_file_id;
        pSMB->ByteCount = 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        cifs_stats_inc(&tcon->num_flushes);
        if (rc)
                cERROR(1, "Send error in Flush = %d", rc);
@@ -3874,13 +3892,12 @@ CIFSSMBSetCIFSACL(const int xid, struct cifs_tcon *tcon, __u16 fid,
        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;
@@ -3908,9 +3925,8 @@ setCifsAclRetry:
        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);
@@ -4625,7 +4641,7 @@ CIFSFindClose(const int xid, struct cifs_tcon *tcon,
 
        pSMB->FileID = searchHandle;
        pSMB->ByteCount = 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cERROR(1, "Send error in FindClose = %d", rc);
 
@@ -4828,8 +4844,12 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
                max_len = data_end - temp;
                node->node_name = cifs_strndup_from_utf16(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:
@@ -5646,7 +5666,7 @@ CIFSSMBSetFileSize(const int xid, struct cifs_tcon *tcon, __u64 size,
        pSMB->Reserved4 = 0;
        inc_rfc1001_len(pSMB, byte_count);
        pSMB->ByteCount = cpu_to_le16(byte_count);
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc) {
                cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
        }
@@ -5690,7 +5710,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);
@@ -5715,7 +5736,7 @@ CIFSSMBSetFileInfo(const int xid, struct cifs_tcon *tcon,
        inc_rfc1001_len(pSMB, byte_count);
        pSMB->ByteCount = cpu_to_le16(byte_count);
        memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
 
@@ -5774,7 +5795,7 @@ CIFSSMBSetFileDisposition(const int xid, struct cifs_tcon *tcon,
        inc_rfc1001_len(pSMB, byte_count);
        pSMB->ByteCount = cpu_to_le16(byte_count);
        *data_offset = delete_file ? 1 : 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cFYI(1, "Send error in SetFileDisposition = %d", rc);
 
@@ -5959,7 +5980,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;
 
@@ -5981,8 +6002,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);
@@ -6004,9 +6026,9 @@ 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);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);