From 792af7b05b8a78def080ec757a4d4420b9fd0cc2 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:02 -0400 Subject: CIFS: Separate protocol-specific code from transport routines that lets us use this functions for SMB2. Signed-off-by: Pavel Shilovsky --- fs/cifs/cifs_debug.h | 2 +- fs/cifs/cifsglob.h | 6 ++ fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 21 +++---- fs/cifs/misc.c | 5 +- fs/cifs/transport.c | 171 +++++++++++++++++++++++++++++---------------------- 6 files changed, 119 insertions(+), 88 deletions(-) diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 8942b28..0a234c1 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -32,7 +32,7 @@ void cifs_dump_mids(struct TCP_Server_Info *); #define DBG2 0 #endif extern int traceSMB; /* flag which enables the function below */ -void dump_smb(struct smb_hdr *, int); +void dump_smb(void *, int); #define CIFS_INFO 0x01 #define CIFS_RC 0x02 #define CIFS_TIMER 0x04 diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 339ebe3..c3c7d7c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -230,6 +230,12 @@ struct cifs_mnt_data { int flags; }; +static inline unsigned int +get_rfc1002_length(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)); +} + struct TCP_Server_Info { struct list_head tcp_ses_list; struct list_head smb_ses_list; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 503e73d..5f2f266 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -77,7 +77,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, struct smb_hdr * /* out */ , int * /* bytes returned */ , const int long_op); extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - struct smb_hdr *in_buf, int flags); + char *in_buf, int flags); extern int cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, bool log_error); extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 70aac35c..a7ed01c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -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); @@ -2420,8 +2420,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 +2587,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 +2616,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); @@ -4625,7 +4624,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); @@ -5646,7 +5645,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); } @@ -5715,7 +5714,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 +5773,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); @@ -6006,7 +6005,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, cifs_fill_unix_set_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); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index c273c12..e88601f 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -604,16 +604,15 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) } void -dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) +dump_smb(void *buf, int smb_buf_length) { int i, j; char debug_line[17]; - unsigned char *buffer; + unsigned char *buffer = buf; if (traceSMB == 0) return; - buffer = (unsigned char *) smb_buf; for (i = 0, j = 0; i < smb_buf_length; i++, j++) { if (i % 8 == 0) { /* have reached the beginning of line */ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 310918b..3bb447d 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -126,11 +126,11 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) int rc = 0; int i = 0; struct msghdr smb_msg; - struct smb_hdr *smb_buffer = iov[0].iov_base; + __be32 *buf_len = (__be32 *)(iov[0].iov_base); unsigned int len = iov[0].iov_len; unsigned int total_len; int first_vec = 0; - unsigned int smb_buf_length = be32_to_cpu(smb_buffer->smb_buf_length); + unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); struct socket *ssocket = server->ssocket; if (ssocket == NULL) @@ -150,7 +150,7 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) total_len += iov[i].iov_len; cFYI(1, "Sending smb: total_len %d", total_len); - dump_smb(smb_buffer, len); + dump_smb(iov[0].iov_base, len); i = 0; while (total_len) { @@ -158,24 +158,24 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) n_vec - first_vec, total_len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; - /* 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 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); @@ -235,9 +235,8 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) else rc = 0; - /* Don't want to modify the buffer as a - side effect of this call. */ - smb_buffer->smb_buf_length = cpu_to_be32(smb_buf_length); + /* Don't want to modify the buffer as a side effect of this call. */ + *buf_len = cpu_to_be32(smb_buf_length); return rc; } @@ -349,6 +348,33 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) return 0; } +static int +cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov, + unsigned int nvec, struct mid_q_entry **ret_mid) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base; + struct mid_q_entry *mid; + + /* enable signing if server requires it */ + if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + mid = AllocMidQEntry(hdr, server); + if (mid == NULL) + return -ENOMEM; + + /* put it on the pending_mid_q */ + spin_lock(&GlobalMid_Lock); + list_add_tail(&mid->qhead, &server->pending_mid_q); + spin_unlock(&GlobalMid_Lock); + + rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number); + if (rc) + delete_mid(mid); + *ret_mid = mid; + return rc; +} /* * Send a SMB request and set the callback function in the mid to handle @@ -361,34 +387,18 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, { int rc; struct mid_q_entry *mid; - struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base; rc = wait_for_free_request(server, ignore_pend ? CIFS_ASYNC_OP : 0); if (rc) return rc; - /* enable signing if server requires it */ - if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - mutex_lock(&server->srv_mutex); - mid = AllocMidQEntry(hdr, server); - if (mid == NULL) { + rc = cifs_setup_async_request(server, iov, nvec, &mid); + if (rc) { mutex_unlock(&server->srv_mutex); cifs_add_credits(server, 1); wake_up(&server->request_q); - return -ENOMEM; - } - - /* put it on the pending_mid_q */ - spin_lock(&GlobalMid_Lock); - list_add_tail(&mid->qhead, &server->pending_mid_q); - spin_unlock(&GlobalMid_Lock); - - rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number); - if (rc) { - mutex_unlock(&server->srv_mutex); - goto out_err; + return rc; } mid->receive = receive; @@ -424,14 +434,14 @@ out_err: */ int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - struct smb_hdr *in_buf, int flags) + char *in_buf, int flags) { int rc; struct kvec iov[1]; int resp_buf_type; - iov[0].iov_base = (char *)in_buf; - iov[0].iov_len = be32_to_cpu(in_buf->smb_buf_length) + 4; + iov[0].iov_base = in_buf; + iov[0].iov_len = get_rfc1002_length(in_buf) + 4; flags |= CIFS_NO_RESP; rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags); cFYI(DBG2, "SendRcvNoRsp flags %d rc %d", flags, rc); @@ -514,7 +524,7 @@ int cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, bool log_error) { - unsigned int len = be32_to_cpu(mid->resp_buf->smb_buf_length) + 4; + unsigned int len = get_rfc1002_length(mid->resp_buf) + 4; dump_smb(mid->resp_buf, min_t(u32, 92, len)); @@ -534,6 +544,24 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, return map_smb_to_linux_error(mid->resp_buf, log_error); } +static int +cifs_setup_request(struct cifs_ses *ses, struct kvec *iov, + unsigned int nvec, struct mid_q_entry **ret_mid) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base; + struct mid_q_entry *mid; + + rc = allocate_mid(ses, hdr, &mid); + if (rc) + return rc; + rc = cifs_sign_smb2(iov, nvec, ses->server, &mid->sequence_number); + if (rc) + delete_mid(mid); + *ret_mid = mid; + return rc; +} + int SendReceive2(const unsigned int xid, struct cifs_ses *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, @@ -542,53 +570,51 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, int rc = 0; int long_op; struct mid_q_entry *midQ; - struct smb_hdr *in_buf = iov[0].iov_base; + char *buf = iov[0].iov_base; long_op = flags & CIFS_TIMEOUT_MASK; *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); cERROR(1, "Null session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) { - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); 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 */ + /* + * 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. + */ rc = wait_for_free_request(ses->server, long_op); if (rc) { - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); return rc; } - /* 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 */ + /* + * 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. + */ mutex_lock(&ses->server->srv_mutex); - rc = allocate_mid(ses, in_buf, &midQ); + rc = cifs_setup_request(ses, iov, n_vec, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); /* Update # of requests on wire to server */ cifs_add_credits(ses->server, 1); 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; cifs_in_send_inc(ses->server); @@ -599,30 +625,30 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); goto out; } if (long_op == CIFS_ASYNC_OP) { - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); goto out; } rc = wait_for_response(ses->server, midQ); if (rc != 0) { - send_nt_cancel(ses->server, in_buf, midQ); + send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->midState == MID_REQUEST_SUBMITTED) { midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); cifs_add_credits(ses->server, 1); return rc; } spin_unlock(&GlobalMid_Lock); } - cifs_small_buf_release(in_buf); + cifs_small_buf_release(buf); rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { @@ -636,8 +662,9 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, goto out; } - iov[0].iov_base = (char *)midQ->resp_buf; - iov[0].iov_len = be32_to_cpu(midQ->resp_buf->smb_buf_length) + 4; + buf = (char *)midQ->resp_buf; + iov[0].iov_base = buf; + iov[0].iov_len = get_rfc1002_length(buf) + 4; if (midQ->largeBuf) *pRespBufType = CIFS_LARGE_BUFFER; else -- cgit v1.1 From d4e4854fd1c85ac8ba4d6de39703e07704754b85 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:02 -0400 Subject: CIFS: Separate protocol-specific code from demultiplex code Signed-off-by: Pavel Shilovsky --- fs/cifs/cifs_debug.c | 5 ++-- fs/cifs/cifs_debug.h | 2 +- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifsproto.h | 5 ++-- fs/cifs/connect.c | 78 ++++++++++++++++++++++++++++------------------------ fs/cifs/misc.c | 7 +++-- fs/cifs/transport.c | 4 +-- 7 files changed, 56 insertions(+), 47 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 573b899..bcd0db7 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -58,15 +58,16 @@ cifs_dump_mem(char *label, void *data, int length) } #ifdef CONFIG_CIFS_DEBUG2 -void cifs_dump_detail(struct smb_hdr *smb) +void cifs_dump_detail(void *buf) { + struct smb_hdr *smb = (struct smb_hdr *)buf; + cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d", smb->Command, smb->Status.CifsError, smb->Flags, smb->Flags2, smb->Mid, smb->Pid); cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb)); } - void cifs_dump_mids(struct TCP_Server_Info *server) { struct list_head *tmp; diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 0a234c1..566e0ae 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -26,7 +26,7 @@ void cifs_dump_mem(char *label, void *data, int length); #ifdef CONFIG_CIFS_DEBUG2 #define DBG2 2 -void cifs_dump_detail(struct smb_hdr *); +void cifs_dump_detail(void *); void cifs_dump_mids(struct TCP_Server_Info *); #else #define DBG2 0 diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c3c7d7c..34a897e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -730,7 +730,7 @@ struct mid_q_entry { mid_receive_t *receive; /* call receive callback */ mid_callback_t *callback; /* call completion callback */ void *callback_data; /* general purpose pointer for callback */ - struct smb_hdr *resp_buf; /* pointer to received SMB header */ + void *resp_buf; /* pointer to received SMB header */ int midState; /* wish this were enum but can not pass to wait_event */ __u8 command; /* smb command code */ bool largeBuf:1; /* if valid response, is pointer to large buf */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 5f2f266..70124ae 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -91,9 +91,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid, extern void cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add); extern void cifs_set_credits(struct TCP_Server_Info *server, const int val); -extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); -extern bool is_valid_oplock_break(struct smb_hdr *smb, - struct TCP_Server_Info *); +extern int checkSMB(char *buf, unsigned int length); +extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); extern bool backup_cred(struct cifs_sb_info *); extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5560e1d..cb6e757 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -183,8 +183,9 @@ cifs_reconnect(struct TCP_Server_Info *server) -EINVAL = invalid transact2 */ -static int check2ndT2(struct smb_hdr *pSMB) +static int check2ndT2(char *buf) { + struct smb_hdr *pSMB = (struct smb_hdr *)buf; struct smb_t2_rsp *pSMBt; int remaining; __u16 total_data_size, data_in_this_rsp; @@ -224,10 +225,10 @@ static int check2ndT2(struct smb_hdr *pSMB) return remaining; } -static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) +static int coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) { - struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)psecond; - struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; + struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; + struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; char *data_area_of_tgt; char *data_area_of_src; int remaining; @@ -280,23 +281,23 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); /* fix up the BCC */ - byte_count = get_bcc(pTargetSMB); + byte_count = get_bcc(target_hdr); byte_count += total_in_src; /* is the result too big for the field? */ if (byte_count > USHRT_MAX) { cFYI(1, "coalesced BCC too large (%u)", byte_count); return -EPROTO; } - put_bcc(byte_count, pTargetSMB); + put_bcc(byte_count, target_hdr); - byte_count = be32_to_cpu(pTargetSMB->smb_buf_length); + byte_count = be32_to_cpu(target_hdr->smb_buf_length); byte_count += total_in_src; /* don't allow buffer to overflow */ if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); return -ENOBUFS; } - pTargetSMB->smb_buf_length = cpu_to_be32(byte_count); + target_hdr->smb_buf_length = cpu_to_be32(byte_count); /* copy second buffer into end of first buffer */ memcpy(data_area_of_tgt, data_area_of_src, total_in_src); @@ -337,6 +338,18 @@ requeue_echo: queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL); } +static inline size_t +header_size(void) +{ + return sizeof(struct smb_hdr); +} + +static inline size_t +max_header_size(void) +{ + return MAX_CIFS_HDR_SIZE; +} + static bool allocate_buffers(struct TCP_Server_Info *server) { @@ -350,7 +363,7 @@ allocate_buffers(struct TCP_Server_Info *server) } } else if (server->large_buf) { /* we are reusing a dirty large buf, clear its start */ - memset(server->bigbuf, 0, sizeof(struct smb_hdr)); + memset(server->bigbuf, 0, header_size()); } if (!server->smallbuf) { @@ -364,7 +377,7 @@ allocate_buffers(struct TCP_Server_Info *server) /* beginning of smb buffer is cleared in our buf_get */ } else { /* if existing small buf clear beginning */ - memset(server->smallbuf, 0, sizeof(struct smb_hdr)); + memset(server->smallbuf, 0, header_size()); } return true; @@ -566,8 +579,9 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type) } static struct mid_q_entry * -find_mid(struct TCP_Server_Info *server, struct smb_hdr *buf) +find_mid(struct TCP_Server_Info *server, char *buffer) { + struct smb_hdr *buf = (struct smb_hdr *)buffer; struct mid_q_entry *mid; spin_lock(&GlobalMid_Lock); @@ -600,7 +614,7 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed) static void handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, - struct smb_hdr *buf, int malformed) + char *buf, int malformed) { if (malformed == 0 && check2ndT2(buf) > 0) { mid->multiRsp = true; @@ -731,11 +745,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) { int length; char *buf = server->smallbuf; - struct smb_hdr *smb_buffer = (struct smb_hdr *)buf; - unsigned int pdu_length = be32_to_cpu(smb_buffer->smb_buf_length); + unsigned int pdu_length = get_rfc1002_length(buf); /* make sure this will fit in a large buffer */ - if (pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + if (pdu_length > CIFSMaxBufSize + max_header_size() - 4) { cERROR(1, "SMB response too long (%u bytes)", pdu_length); cifs_reconnect(server); @@ -746,20 +759,18 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) /* switch to large buffer if too big for a small one */ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { server->large_buf = true; - memcpy(server->bigbuf, server->smallbuf, server->total_read); + memcpy(server->bigbuf, buf, server->total_read); buf = server->bigbuf; - smb_buffer = (struct smb_hdr *)buf; } /* now read the rest */ - length = cifs_read_from_socket(server, - buf + sizeof(struct smb_hdr) - 1, - pdu_length - sizeof(struct smb_hdr) + 1 + 4); + length = cifs_read_from_socket(server, buf + header_size() - 1, + pdu_length - header_size() + 1 + 4); if (length < 0) return length; server->total_read += length; - dump_smb(smb_buffer, server->total_read); + dump_smb(buf, server->total_read); /* * We know that we received enough to get to the MID as we @@ -770,7 +781,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) * 48 bytes is enough to display the header and a little bit * into the payload for debugging purposes. */ - length = checkSMB(smb_buffer, smb_buffer->Mid, server->total_read); + length = checkSMB(buf, server->total_read); if (length != 0) cifs_dump_mem("Bad SMB: ", buf, min_t(unsigned int, server->total_read, 48)); @@ -778,7 +789,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) if (!mid) return length; - handle_mid(mid, server, smb_buffer, length); + handle_mid(mid, server, buf, length); return 0; } @@ -789,7 +800,6 @@ cifs_demultiplex_thread(void *p) struct TCP_Server_Info *server = p; unsigned int pdu_length; char *buf = NULL; - struct smb_hdr *smb_buffer = NULL; struct task_struct *task_to_wake = NULL; struct mid_q_entry *mid_entry; @@ -810,7 +820,6 @@ cifs_demultiplex_thread(void *p) continue; server->large_buf = false; - smb_buffer = (struct smb_hdr *)server->smallbuf; buf = server->smallbuf; pdu_length = 4; /* enough to get RFC1001 header */ @@ -823,14 +832,14 @@ cifs_demultiplex_thread(void *p) * The right amount was read from socket - 4 bytes, * so we can now interpret the length field. */ - pdu_length = be32_to_cpu(smb_buffer->smb_buf_length); + pdu_length = get_rfc1002_length(buf); cFYI(1, "RFC1002 header 0x%x", pdu_length); if (!is_smb_response(server, buf[0])) continue; /* make sure we have enough to get to the MID */ - if (pdu_length < sizeof(struct smb_hdr) - 1 - 4) { + if (pdu_length < header_size() - 1 - 4) { cERROR(1, "SMB response too short (%u bytes)", pdu_length); cifs_reconnect(server); @@ -840,12 +849,12 @@ cifs_demultiplex_thread(void *p) /* read down to the MID */ length = cifs_read_from_socket(server, buf + 4, - sizeof(struct smb_hdr) - 1 - 4); + header_size() - 1 - 4); if (length < 0) continue; server->total_read += length; - mid_entry = find_mid(server, smb_buffer); + mid_entry = find_mid(server, buf); if (!mid_entry || !mid_entry->receive) length = standard_receive3(server, mid_entry); @@ -855,22 +864,19 @@ cifs_demultiplex_thread(void *p) if (length < 0) continue; - if (server->large_buf) { + if (server->large_buf) buf = server->bigbuf; - smb_buffer = (struct smb_hdr *)buf; - } server->lstrp = jiffies; if (mid_entry != NULL) { if (!mid_entry->multiRsp || mid_entry->multiEnd) mid_entry->callback(mid_entry); - } else if (!is_valid_oplock_break(smb_buffer, server)) { + } else if (!is_valid_oplock_break(buf, server)) { cERROR(1, "No task to wake, unknown frame received! " "NumMids %d", atomic_read(&midCount)); - cifs_dump_mem("Received Data is: ", buf, - sizeof(struct smb_hdr)); + cifs_dump_mem("Received Data is: ", buf, header_size()); #ifdef CONFIG_CIFS_DEBUG2 - cifs_dump_detail(smb_buffer); + cifs_dump_detail(buf); cifs_dump_mids(server); #endif /* CIFS_DEBUG2 */ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index e88601f..dc61dff2 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -420,8 +420,10 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid) } int -checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read) +checkSMB(char *buf, unsigned int total_read) { + struct smb_hdr *smb = (struct smb_hdr *)buf; + __u16 mid = smb->Mid; __u32 rfclen = be32_to_cpu(smb->smb_buf_length); __u32 clc_len; /* calculated length */ cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", @@ -502,8 +504,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read) } bool -is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) +is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) { + struct smb_hdr *buf = (struct smb_hdr *)buffer; struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; struct list_head *tmp, *tmp1, *tmp2; struct cifs_ses *ses; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 3bb447d..66b4ede 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -778,7 +778,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, goto out; } - *pbytes_returned = be32_to_cpu(midQ->resp_buf->smb_buf_length); + *pbytes_returned = get_rfc1002_length(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); rc = cifs_check_receive(midQ, ses->server, 0); out: @@ -945,7 +945,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, goto out; } - *pbytes_returned = be32_to_cpu(midQ->resp_buf->smb_buf_length); + *pbytes_returned = get_rfc1002_length(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); rc = cifs_check_receive(midQ, ses->server, 0); out: -- cgit v1.1 From 5ffef7bf1dd582e93b15f8cc735328a556a1d2c4 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:03 -0400 Subject: CIFS: Separate protocol-specific code from cifs_readv_receive code Signed-off-by: Pavel Shilovsky --- fs/cifs/cifsglob.h | 12 +++++++++++ fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 58 +++++++++++++++++++++++++++++++++++------------------ fs/cifs/connect.c | 12 ----------- fs/cifs/netmisc.c | 3 ++- 5 files changed, 53 insertions(+), 34 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 34a897e..a403398 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -341,6 +341,18 @@ has_credits(struct TCP_Server_Info *server, int *credits) return num > 0; } +static inline size_t +header_size(void) +{ + return sizeof(struct smb_hdr); +} + +static inline size_t +max_header_size(void) +{ + return MAX_CIFS_HDR_SIZE; +} + /* * Macros to allow the TCP_Server_Info->net field and related code to drop out * when CONFIG_NET_NS isn't set. diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 70124ae..05f0a2e 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -106,7 +106,7 @@ extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); extern int cifs_set_port(struct sockaddr *addr, const unsigned short int port); extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len, const unsigned short int port); -extern int map_smb_to_linux_error(struct smb_hdr *smb, bool logErr); +extern int map_smb_to_linux_error(char *buf, bool logErr); extern void header_assemble(struct smb_hdr *, char /* command */ , const struct cifs_tcon *, int /* length of fixed section (word count) in two byte units */); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a7ed01c..c45d445 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -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,14 +1434,35 @@ 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; @@ -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); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index cb6e757..007d4df 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -338,18 +338,6 @@ requeue_echo: queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL); } -static inline size_t -header_size(void) -{ - return sizeof(struct smb_hdr); -} - -static inline size_t -max_header_size(void) -{ - return MAX_CIFS_HDR_SIZE; -} - static bool allocate_buffers(struct TCP_Server_Info *server) { diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 73e47e8..dd23a32 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -836,8 +836,9 @@ ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) } int -map_smb_to_linux_error(struct smb_hdr *smb, bool logErr) +map_smb_to_linux_error(char *buf, bool logErr) { + struct smb_hdr *smb = (struct smb_hdr *)buf; unsigned int i; int rc = -EIO; /* if transport error smb error may not be set */ __u8 smberrclass; -- cgit v1.1 From 243d04b6e6de7fd08578fffd28b890c0200a2ca5 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:03 -0400 Subject: CIFS: Expand CurrentMid field While in CIFS/SMB we have 16 bit mid, in SMB2 it is 64 bit. Convert the existing field to 64 bit and mask off higher bits for CIFS/SMB. Signed-off-by: Pavel Shilovsky --- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifsproto.h | 2 +- fs/cifs/misc.c | 84 +++++++++++++++++++++++++++++------------------------ 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a403398..b213458 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -282,7 +282,7 @@ struct TCP_Server_Info { vcnumbers */ int capabilities; /* allow selective disabling of caps by smb sess */ int timeAdj; /* Adjust for difference in server time zone in sec */ - __u16 CurrentMid; /* multiplex id - rotating counter */ + __u64 CurrentMid; /* multiplex id - rotating counter */ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 05f0a2e..95ee5a6 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -115,7 +115,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct, void **request_buf); extern int CIFS_SessSetup(unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp); -extern __u16 GetNextMid(struct TCP_Server_Info *server); +extern __u64 GetNextMid(struct TCP_Server_Info *server); extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); extern u64 cifs_UnixTimeToNT(struct timespec); extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index dc61dff2..d2ccce8 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -213,54 +213,61 @@ cifs_small_buf_release(void *buf_to_free) } /* - Find a free multiplex id (SMB mid). Otherwise there could be - mid collisions which might cause problems, demultiplexing the - wrong response to this request. Multiplex ids could collide if - one of a series requests takes much longer than the others, or - if a very large number of long lived requests (byte range - locks or FindNotify requests) are pending. No more than - 64K-1 requests can be outstanding at one time. If no - mids are available, return zero. A future optimization - could make the combination of mids and uid the key we use - to demultiplex on (rather than mid alone). - In addition to the above check, the cifs demultiplex - code already used the command code as a secondary - check of the frame and if signing is negotiated the - response would be discarded if the mid were the same - but the signature was wrong. Since the mid is not put in the - pending queue until later (when it is about to be dispatched) - we do have to limit the number of outstanding requests - to somewhat less than 64K-1 although it is hard to imagine - so many threads being in the vfs at one time. -*/ -__u16 GetNextMid(struct TCP_Server_Info *server) + * Find a free multiplex id (SMB mid). Otherwise there could be + * mid collisions which might cause problems, demultiplexing the + * wrong response to this request. Multiplex ids could collide if + * one of a series requests takes much longer than the others, or + * if a very large number of long lived requests (byte range + * locks or FindNotify requests) are pending. No more than + * 64K-1 requests can be outstanding at one time. If no + * mids are available, return zero. A future optimization + * could make the combination of mids and uid the key we use + * to demultiplex on (rather than mid alone). + * In addition to the above check, the cifs demultiplex + * code already used the command code as a secondary + * check of the frame and if signing is negotiated the + * response would be discarded if the mid were the same + * but the signature was wrong. Since the mid is not put in the + * pending queue until later (when it is about to be dispatched) + * we do have to limit the number of outstanding requests + * to somewhat less than 64K-1 although it is hard to imagine + * so many threads being in the vfs at one time. + */ +__u64 GetNextMid(struct TCP_Server_Info *server) { - __u16 mid = 0; - __u16 last_mid; + __u64 mid = 0; + __u16 last_mid, cur_mid; bool collision; spin_lock(&GlobalMid_Lock); - last_mid = server->CurrentMid; /* we do not want to loop forever */ - server->CurrentMid++; - /* This nested loop looks more expensive than it is. - In practice the list of pending requests is short, - fewer than 50, and the mids are likely to be unique - on the first pass through the loop unless some request - takes longer than the 64 thousand requests before it - (and it would also have to have been a request that - did not time out) */ - while (server->CurrentMid != last_mid) { + + /* mid is 16 bit only for CIFS/SMB */ + cur_mid = (__u16)((server->CurrentMid) & 0xffff); + /* we do not want to loop forever */ + last_mid = cur_mid; + cur_mid++; + + /* + * This nested loop looks more expensive than it is. + * In practice the list of pending requests is short, + * fewer than 50, and the mids are likely to be unique + * on the first pass through the loop unless some request + * takes longer than the 64 thousand requests before it + * (and it would also have to have been a request that + * did not time out). + */ + while (cur_mid != last_mid) { struct mid_q_entry *mid_entry; unsigned int num_mids; collision = false; - if (server->CurrentMid == 0) - server->CurrentMid++; + if (cur_mid == 0) + cur_mid++; num_mids = 0; list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { ++num_mids; - if (mid_entry->mid == server->CurrentMid && + if (mid_entry->mid == cur_mid && mid_entry->midState == MID_REQUEST_SUBMITTED) { /* This mid is in use, try a different one */ collision = true; @@ -282,10 +289,11 @@ __u16 GetNextMid(struct TCP_Server_Info *server) server->tcpStatus = CifsNeedReconnect; if (!collision) { - mid = server->CurrentMid; + mid = (__u64)cur_mid; + server->CurrentMid = mid; break; } - server->CurrentMid++; + cur_mid++; } spin_unlock(&GlobalMid_Lock); return mid; -- cgit v1.1 From 7c9421e1a9ce8d17816f480c3a5b4f2609442cd5 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:03 -0400 Subject: CIFS: Change mid_q_entry structure fields to be protocol-unspecific and big enough to keep both CIFS and SMB2 values. Signed-off-by: Pavel Shilovsky --- fs/cifs/cifs_debug.c | 20 ++++++++++---------- fs/cifs/cifsglob.h | 10 +++++----- fs/cifs/cifssmb.c | 12 ++++++------ fs/cifs/connect.c | 22 +++++++++++----------- fs/cifs/misc.c | 2 +- fs/cifs/transport.c | 52 ++++++++++++++++++++++++++-------------------------- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index bcd0db7..81be263 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -80,15 +80,15 @@ void cifs_dump_mids(struct TCP_Server_Info *server) spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cERROR(1, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %d", - mid_entry->midState, - (int)mid_entry->command, + cERROR(1, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu", + mid_entry->mid_state, + le16_to_cpu(mid_entry->command), mid_entry->pid, mid_entry->callback_data, mid_entry->mid); #ifdef CONFIG_CIFS_STATS2 cERROR(1, "IsLarge: %d buf: %p time rcv: %ld now: %ld", - mid_entry->largeBuf, + mid_entry->large_buf, mid_entry->resp_buf, mid_entry->when_received, jiffies); @@ -218,12 +218,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) mid_entry = list_entry(tmp3, struct mid_q_entry, qhead); seq_printf(m, "\tState: %d com: %d pid:" - " %d cbdata: %p mid %d\n", - mid_entry->midState, - (int)mid_entry->command, - mid_entry->pid, - mid_entry->callback_data, - mid_entry->mid); + " %d cbdata: %p mid %llu\n", + mid_entry->mid_state, + le16_to_cpu(mid_entry->command), + mid_entry->pid, + mid_entry->callback_data, + mid_entry->mid); } spin_unlock(&GlobalMid_Lock); } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index b213458..d5ccd46 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -731,8 +731,8 @@ typedef void (mid_callback_t)(struct mid_q_entry *mid); /* one of these for every pending CIFS request to the server */ struct mid_q_entry { struct list_head qhead; /* mids waiting on reply from this server */ - __u16 mid; /* multiplex id */ - __u16 pid; /* process id */ + __u64 mid; /* multiplex id */ + __u32 pid; /* process id */ __u32 sequence_number; /* for CIFS signing */ unsigned long when_alloc; /* when mid was created */ #ifdef CONFIG_CIFS_STATS2 @@ -743,9 +743,9 @@ struct mid_q_entry { mid_callback_t *callback; /* call completion callback */ void *callback_data; /* general purpose pointer for callback */ void *resp_buf; /* pointer to received SMB header */ - int midState; /* wish this were enum but can not pass to wait_event */ - __u8 command; /* smb command code */ - bool largeBuf:1; /* if valid response, is pointer to large buf */ + int mid_state; /* wish this were enum but can not pass to wait_event */ + __le16 command; /* smb command code */ + bool large_buf:1; /* if valid response, is pointer to large buf */ bool multiRsp:1; /* multiple trans2 responses for one request */ bool multiEnd:1; /* both received */ }; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index c45d445..f0b1c59 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1467,7 +1467,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) 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); /* @@ -1665,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 & @@ -2086,7 +2086,7 @@ cifs_writedata_alloc(unsigned int nr_pages) } /* - * 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 @@ -2097,7 +2097,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) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 007d4df..f3c9329 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -143,8 +143,8 @@ cifs_reconnect(struct TCP_Server_Info *server) spin_lock(&GlobalMid_Lock); list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - if (mid_entry->midState == MID_REQUEST_SUBMITTED) - mid_entry->midState = MID_RETRY_NEEDED; + if (mid_entry->mid_state == MID_REQUEST_SUBMITTED) + mid_entry->mid_state = MID_RETRY_NEEDED; list_move(&mid_entry->qhead, &retry_list); } spin_unlock(&GlobalMid_Lock); @@ -575,8 +575,8 @@ find_mid(struct TCP_Server_Info *server, char *buffer) spin_lock(&GlobalMid_Lock); list_for_each_entry(mid, &server->pending_mid_q, qhead) { if (mid->mid == buf->Mid && - mid->midState == MID_REQUEST_SUBMITTED && - mid->command == buf->Command) { + mid->mid_state == MID_REQUEST_SUBMITTED && + le16_to_cpu(mid->command) == buf->Command) { spin_unlock(&GlobalMid_Lock); return mid; } @@ -593,9 +593,9 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed) #endif spin_lock(&GlobalMid_Lock); if (!malformed) - mid->midState = MID_RESPONSE_RECEIVED; + mid->mid_state = MID_RESPONSE_RECEIVED; else - mid->midState = MID_RESPONSE_MALFORMED; + mid->mid_state = MID_RESPONSE_MALFORMED; list_del_init(&mid->qhead); spin_unlock(&GlobalMid_Lock); } @@ -622,13 +622,13 @@ handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, } else { /* Have first buffer */ mid->resp_buf = buf; - mid->largeBuf = true; + mid->large_buf = true; server->bigbuf = NULL; } return; } mid->resp_buf = buf; - mid->largeBuf = server->large_buf; + mid->large_buf = server->large_buf; /* Was previous buf put in mpx struct for multi-rsp? */ if (!mid->multiRsp) { /* smb buffer will be freed by user thread */ @@ -684,8 +684,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) spin_lock(&GlobalMid_Lock); list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cFYI(1, "Clearing mid 0x%x", mid_entry->mid); - mid_entry->midState = MID_SHUTDOWN; + cFYI(1, "Clearing mid 0x%llx", mid_entry->mid); + mid_entry->mid_state = MID_SHUTDOWN; list_move(&mid_entry->qhead, &dispose_list); } spin_unlock(&GlobalMid_Lock); @@ -693,7 +693,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) /* now walk dispose list and issue callbacks */ list_for_each_safe(tmp, tmp2, &dispose_list) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cFYI(1, "Callback mid 0x%x", mid_entry->mid); + cFYI(1, "Callback mid 0x%llx", mid_entry->mid); list_del_init(&mid_entry->qhead); mid_entry->callback(mid_entry); } diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index d2ccce8..425e4f2 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -268,7 +268,7 @@ __u64 GetNextMid(struct TCP_Server_Info *server) list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { ++num_mids; if (mid_entry->mid == cur_mid && - mid_entry->midState == MID_REQUEST_SUBMITTED) { + mid_entry->mid_state == MID_REQUEST_SUBMITTED) { /* This mid is in use, try a different one */ collision = true; break; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 66b4ede..0961336 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -60,8 +60,8 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) 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); + temp->command = cpu_to_le16(smb_buffer->Command); + cFYI(1, "For smb_command %d", smb_buffer->Command); /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ /* when mid allocated can be before when sent */ temp->when_alloc = jiffies; @@ -75,7 +75,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) } atomic_inc(&midCount); - temp->midState = MID_REQUEST_ALLOCATED; + temp->mid_state = MID_REQUEST_ALLOCATED; return temp; } @@ -85,9 +85,9 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) #ifdef CONFIG_CIFS_STATS2 unsigned long now; #endif - midEntry->midState = MID_FREE; + midEntry->mid_state = MID_FREE; atomic_dec(&midCount); - if (midEntry->largeBuf) + if (midEntry->large_buf) cifs_buf_release(midEntry->resp_buf); else cifs_small_buf_release(midEntry->resp_buf); @@ -97,8 +97,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) something is wrong, unless it is quite a slow link or server */ if ((now - midEntry->when_alloc) > HZ) { if ((cifsFYI & CIFS_TIMER) && - (midEntry->command != SMB_COM_LOCKING_ANDX)) { - printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d", + (midEntry->command != cpu_to_le16(SMB_COM_LOCKING_ANDX))) { + printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %llu", midEntry->command, midEntry->mid); printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n", now - midEntry->when_alloc, @@ -341,7 +341,7 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) int error; error = wait_event_freezekillable(server->response_q, - midQ->midState != MID_REQUEST_SUBMITTED); + midQ->mid_state != MID_REQUEST_SUBMITTED); if (error < 0) return -ERESTARTSYS; @@ -404,7 +404,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, mid->receive = receive; mid->callback = callback; mid->callback_data = cbdata; - mid->midState = MID_REQUEST_SUBMITTED; + mid->mid_state = MID_REQUEST_SUBMITTED; cifs_in_send_inc(server); rc = smb_sendv(server, iov, nvec); @@ -454,11 +454,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) { int rc = 0; - cFYI(1, "%s: cmd=%d mid=%d state=%d", __func__, mid->command, - mid->mid, mid->midState); + cFYI(1, "%s: cmd=%d mid=%llu state=%d", __func__, + le16_to_cpu(mid->command), mid->mid, mid->mid_state); spin_lock(&GlobalMid_Lock); - switch (mid->midState) { + switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: spin_unlock(&GlobalMid_Lock); return rc; @@ -473,8 +473,8 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) break; default: list_del_init(&mid->qhead); - cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__, - mid->mid, mid->midState); + cERROR(1, "%s: invalid mid state mid=%llu state=%d", __func__, + mid->mid, mid->mid_state); rc = -EIO; } spin_unlock(&GlobalMid_Lock); @@ -616,7 +616,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, return rc; } - midQ->midState = MID_REQUEST_SUBMITTED; + midQ->mid_state = MID_REQUEST_SUBMITTED; cifs_in_send_inc(ses->server); rc = smb_sendv(ses->server, iov, n_vec); cifs_in_send_dec(ses->server); @@ -638,7 +638,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, if (rc != 0) { send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ); spin_lock(&GlobalMid_Lock); - if (midQ->midState == MID_REQUEST_SUBMITTED) { + if (midQ->mid_state == MID_REQUEST_SUBMITTED) { midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); cifs_small_buf_release(buf); @@ -656,7 +656,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, return rc; } - if (!midQ->resp_buf || midQ->midState != MID_RESPONSE_RECEIVED) { + if (!midQ->resp_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cFYI(1, "Bad MID state?"); goto out; @@ -665,7 +665,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, buf = (char *)midQ->resp_buf; iov[0].iov_base = buf; iov[0].iov_len = get_rfc1002_length(buf) + 4; - if (midQ->largeBuf) + if (midQ->large_buf) *pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; @@ -737,7 +737,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, goto out; } - midQ->midState = MID_REQUEST_SUBMITTED; + midQ->mid_state = MID_REQUEST_SUBMITTED; cifs_in_send_inc(ses->server); rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); @@ -755,7 +755,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, if (rc != 0) { send_nt_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); - if (midQ->midState == MID_REQUEST_SUBMITTED) { + if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); @@ -772,7 +772,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, } if (!midQ->resp_buf || !out_buf || - midQ->midState != MID_RESPONSE_RECEIVED) { + midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cERROR(1, "Bad MID state?"); goto out; @@ -871,7 +871,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, return rc; } - midQ->midState = MID_REQUEST_SUBMITTED; + midQ->mid_state = MID_REQUEST_SUBMITTED; cifs_in_send_inc(ses->server); rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); cifs_in_send_dec(ses->server); @@ -885,13 +885,13 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(ses->server->response_q, - (!(midQ->midState == MID_REQUEST_SUBMITTED)) || + (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ if ((rc == -ERESTARTSYS) && - (midQ->midState == MID_REQUEST_SUBMITTED) && + (midQ->mid_state == MID_REQUEST_SUBMITTED) && ((ses->server->tcpStatus == CifsGood) || (ses->server->tcpStatus == CifsNew))) { @@ -921,7 +921,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, if (rc) { send_nt_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); - if (midQ->midState == MID_REQUEST_SUBMITTED) { + if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); @@ -939,7 +939,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, return rc; /* rcvd frame is ok */ - if (out_buf == NULL || midQ->midState != MID_RESPONSE_RECEIVED) { + if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cERROR(1, "Bad MID state?"); goto out; -- cgit v1.1 From da472fc847e9d8c9da69b09ce0ab975b24f9b894 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:53 -0400 Subject: cifs: add new cifsiod_wq workqueue ...and convert existing cifs users of system_nrt_wq to use that instead. Also, make it freezable, and set WQ_MEM_RECLAIM since we use it to deal with write reply handling. Signed-off-by: Jeff Layton Acked-by: Shirish Pargaonkar --- fs/cifs/cifsfs.c | 13 ++++++++++++- fs/cifs/cifsglob.h | 1 + fs/cifs/cifssmb.c | 4 ++-- fs/cifs/connect.c | 8 ++++---- fs/cifs/misc.c | 2 +- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index eee522c..d342128 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -85,6 +85,8 @@ extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_mid_poolp; +struct workqueue_struct *cifsiod_wq; + static int cifs_read_super(struct super_block *sb) { @@ -1111,9 +1113,15 @@ init_cifs(void) cFYI(1, "cifs_max_pending set to max of %u", CIFS_MAX_REQ); } + cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!cifsiod_wq) { + rc = -ENOMEM; + goto out_clean_proc; + } + rc = cifs_fscache_register(); if (rc) - goto out_clean_proc; + goto out_destroy_wq; rc = cifs_init_inodecache(); if (rc) @@ -1161,6 +1169,8 @@ out_destroy_inodecache: cifs_destroy_inodecache(); out_unreg_fscache: cifs_fscache_unregister(); +out_destroy_wq: + destroy_workqueue(cifsiod_wq); out_clean_proc: cifs_proc_clean(); return rc; @@ -1183,6 +1193,7 @@ exit_cifs(void) cifs_destroy_mids(); cifs_destroy_inodecache(); cifs_fscache_unregister(); + destroy_workqueue(cifsiod_wq); cifs_proc_clean(); } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d5ccd46..79eba5a1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1070,5 +1070,6 @@ GLOBAL_EXTERN spinlock_t gidsidlock; void cifs_oplock_break(struct work_struct *work); extern const struct slow_work_ops cifs_oplock_break_ops; +extern struct workqueue_struct *cifsiod_wq; #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f0b1c59..76d8981 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1689,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); } @@ -2129,7 +2129,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); } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f3c9329..855d8c0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -335,7 +335,7 @@ cifs_echo_request(struct work_struct *work) server->hostname); requeue_echo: - queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL); + queue_delayed_work(cifsiod_wq, &server->echo, SMB_ECHO_INTERVAL); } static bool @@ -1971,7 +1971,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) cifs_fscache_get_client_cookie(tcp_ses); /* queue echo request delayed work */ - queue_delayed_work(system_nrt_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL); + queue_delayed_work(cifsiod_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL); return tcp_ses; @@ -3537,7 +3537,7 @@ remote_path_check: tlink_rb_insert(&cifs_sb->tlink_tree, tlink); spin_unlock(&cifs_sb->tlink_tree_lock); - queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, TLINK_IDLE_EXPIRE); mount_fail_check: @@ -4091,6 +4091,6 @@ cifs_prune_tlinks(struct work_struct *work) } spin_unlock(&cifs_sb->tlink_tree_lock); - queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, TLINK_IDLE_EXPIRE); } diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 425e4f2..c29d1aa 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -595,7 +595,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) cifs_set_oplock_level(pCifsInode, pSMB->OplockLevel ? OPLOCK_READ : 0); - queue_work(system_nrt_wq, + queue_work(cifsiod_wq, &netfile->oplock_break); netfile->oplock_break_cancelled = false; -- cgit v1.1 From fe5f5d2e908957392aebbb852a6fb22885860d4b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:55 -0400 Subject: cifs: add pid field to cifs_writedata We'll need this to handle rwpidforward option correctly when we use async writes in the aio_write op. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky --- fs/cifs/cifsproto.h | 1 + fs/cifs/cifssmb.c | 4 ++-- fs/cifs/file.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 95ee5a6..9e68340 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -487,6 +487,7 @@ struct cifs_writedata { struct work_struct work; struct cifsFileInfo *cfile; __u64 offset; + pid_t pid; unsigned int bytes; int result; unsigned int nr_pages; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 76d8981..6192214 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2166,8 +2166,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; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 159fcc5..5fedf6c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1802,6 +1802,7 @@ retry: rc = -EBADF; break; } + wdata->pid = wdata->cfile->pid; rc = cifs_async_writev(wdata); } while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN); -- cgit v1.1 From c2e8764009a0245fd24fcd2a63ffbf64236af016 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:55 -0400 Subject: cifs: allow caller to specify completion op when allocating writedata We'll need a different set of write completion ops when not writing out of the pagecache. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky --- fs/cifs/cifsproto.h | 4 +++- fs/cifs/cifssmb.c | 6 +++--- fs/cifs/file.c | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 9e68340..cbf09cd 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -495,7 +495,9 @@ struct cifs_writedata { }; int cifs_async_writev(struct cifs_writedata *wdata); -struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages); +void cifs_writev_complete(struct work_struct *work); +struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, + work_func_t complete); void cifs_writedata_release(struct kref *refcount); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 6192214..21ff4bf 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2035,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, @@ -2065,7 +2065,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; @@ -2079,7 +2079,7 @@ 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); + INIT_WORK(&wdata->work, complete); kref_init(&wdata->refcount); } return wdata; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 5fedf6c..f624c4d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1684,7 +1684,8 @@ retry: tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1, end - index) + 1; - wdata = cifs_writedata_alloc((unsigned int)tofind); + wdata = cifs_writedata_alloc((unsigned int)tofind, + cifs_writev_complete); if (!wdata) { rc = -ENOMEM; break; -- cgit v1.1 From e94f7ba124bfbd3898f620c46891ebcfb9cf20d0 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: fix allocation in cifs_write_allocate_pages The gfp flags are currently set to __GPF_HIGHMEM, which doesn't allow for any reclaim. Make this more resilient by or'ing that with GFP_KERNEL. Also, get rid of the goto and unify the exit codepath. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky --- fs/cifs/file.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index f624c4d..70bd546 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2045,7 +2045,7 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) unsigned long i; for (i = 0; i < num_pages; i++) { - pages[i] = alloc_page(__GFP_HIGHMEM); + pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); if (!pages[i]) { /* * save number of pages we have already allocated and @@ -2053,15 +2053,14 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) */ num_pages = i; rc = -ENOMEM; - goto error; + break; } } - return rc; - -error: - for (i = 0; i < num_pages; i++) - put_page(pages[i]); + if (rc) { + for (i = 0; i < num_pages; i++) + put_page(pages[i]); + } return rc; } -- cgit v1.1 From 35ebb4155f2f9d5580679d368aea81a5933db481 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: make cifsFileInfo_get return the cifsFileInfo pointer Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky --- fs/cifs/cifsglob.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 79eba5a1..d7e39b0 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -601,9 +601,11 @@ struct cifs_io_parms { * Take a reference on the file private data. Must be called with * cifs_file_list_lock held. */ -static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) +static inline +struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file) { ++cifs_file->count; + return cifs_file; } void cifsFileInfo_put(struct cifsFileInfo *cifs_file); -- cgit v1.1 From a7103b99e4cc00b9d40f6bad77389f9e2341820a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: fix up get_numpages Use DIV_ROUND_UP. Also, PAGE_SIZE is more appropriate here since these aren't pagecache pages. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky --- fs/cifs/file.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 70bd546..5633202 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2071,9 +2071,7 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) size_t clen; clen = min_t(const size_t, len, wsize); - num_pages = clen / PAGE_CACHE_SIZE; - if (clen % PAGE_CACHE_SIZE) - num_pages++; + num_pages = DIV_ROUND_UP(clen, PAGE_SIZE); if (cur_len) *cur_len = clen; -- cgit v1.1 From e9492871fb0546f1b73f309d9e8c0f030bfdfdca Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: abstract out function to marshal up the iovec array for async writes We'll need to do something a bit different depending on the caller. Abstract the code that marshals the page array into an iovec. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky --- fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 17 +++++++---------- fs/cifs/file.c | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index cbf09cd..57ce6f8 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -490,6 +490,8 @@ struct cifs_writedata { pid_t pid; unsigned int bytes; int result; + void (*marshal_iov) (struct kvec *iov, + struct cifs_writedata *wdata); unsigned int nr_pages; struct page *pages[1]; }; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 21ff4bf..5ec0b90 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2142,7 +2142,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) { @@ -2185,15 +2184,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); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 5633202..58ac0f0 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1648,6 +1648,27 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) return rc; } +/* + * Marshal up the iov array, reserving the first one for the header. Also, + * set wdata->bytes. + */ +static void +cifs_writepages_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata) +{ + int i; + struct inode *inode = wdata->cfile->dentry->d_inode; + loff_t size = i_size_read(inode); + + /* marshal up the pages into iov array */ + wdata->bytes = 0; + for (i = 0; i < wdata->nr_pages; i++) { + iov[i + 1].iov_len = min(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; + } +} + static int cifs_writepages(struct address_space *mapping, struct writeback_control *wbc) { @@ -1792,6 +1813,7 @@ retry: wdata->sync_mode = wbc->sync_mode; wdata->nr_pages = nr_pages; wdata->offset = page_offset(wdata->pages[0]); + wdata->marshal_iov = cifs_writepages_marshal_iov; do { if (wdata->cfile != NULL) -- cgit v1.1 From 597b027f694481ffeebcffe634c24b807198d46c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: call cifs_update_eof with i_lock held cifs_update_eof has the potential to be racy if multiple threads are trying to modify it at the same time. Protect modifications of the server_eof value with the inode->i_lock. Signed-off-by: Jeff Layton --- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifssmb.c | 2 ++ fs/cifs/file.c | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d7e39b0..4ff6313 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -626,7 +626,7 @@ struct cifsInodeInfo { bool delete_pending; /* DELETE_ON_CLOSE is set */ bool invalid_mapping; /* pagecache is invalid */ unsigned long time; /* jiffies of last update of inode */ - u64 server_eof; /* current file size on server */ + u64 server_eof; /* current file size on server -- protected by i_lock */ u64 uniqueid; /* server inode number */ u64 createtime; /* creation time on server */ #ifdef CONFIG_CIFS_FSCACHE diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 5ec0b90..b63bf5f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2044,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) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 58ac0f0..6883b08 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1399,7 +1399,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) return rc; } -/* update the file size (if needed) after a write */ +/* + * update the file size (if needed) after a write. Should be called with + * the inode->i_lock held + */ void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, unsigned int bytes_written) @@ -1471,7 +1474,9 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid, return rc; } } else { + spin_lock(&dentry->d_inode->i_lock); cifs_update_eof(cifsi, *poffset, bytes_written); + spin_unlock(&dentry->d_inode->i_lock); *poffset += bytes_written; } } @@ -2197,7 +2202,9 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, if (written) { len -= written; total_written += written; + spin_lock(&inode->i_lock); cifs_update_eof(CIFS_I(inode), *poffset, written); + spin_unlock(&inode->i_lock); *poffset += written; } else if (rc < 0) { if (!total_written) -- cgit v1.1 From da82f7e755d2808ba726c9b23267d5bb23980e94 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: convert cifs_iovec_write to use async writes Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky --- fs/cifs/cifsproto.h | 2 + fs/cifs/cifssmb.c | 4 +- fs/cifs/file.c | 223 ++++++++++++++++++++++++++++++++-------------------- 3 files changed, 143 insertions(+), 86 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 57ce6f8..96192c1 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -483,6 +483,8 @@ int cifs_async_readv(struct cifs_readdata *rdata); /* asynchronous write support */ struct cifs_writedata { struct kref refcount; + struct list_head list; + struct completion done; enum writeback_sync_modes sync_mode; struct work_struct work; struct cifsFileInfo *cfile; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index b63bf5f..8fecc99 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2081,8 +2081,10 @@ cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) wdata = kzalloc(sizeof(*wdata) + sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); if (wdata != NULL) { - INIT_WORK(&wdata->work, complete); kref_init(&wdata->refcount); + INIT_LIST_HEAD(&wdata->list); + init_completion(&wdata->done); + INIT_WORK(&wdata->work, complete); } return wdata; } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 6883b08..daaaca8 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2106,24 +2106,79 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) return num_pages; } +static void +cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata) +{ + int i; + size_t bytes = wdata->bytes; + + /* marshal up the pages into iov array */ + for (i = 0; i < wdata->nr_pages; i++) { + iov[i + 1].iov_len = min(bytes, PAGE_SIZE); + iov[i + 1].iov_base = kmap(wdata->pages[i]); + bytes -= iov[i + 1].iov_len; + } +} + +static void +cifs_uncached_writev_complete(struct work_struct *work) +{ + int i; + struct cifs_writedata *wdata = container_of(work, + struct cifs_writedata, work); + struct inode *inode = wdata->cfile->dentry->d_inode; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + spin_lock(&inode->i_lock); + cifs_update_eof(cifsi, wdata->offset, wdata->bytes); + if (cifsi->server_eof > inode->i_size) + i_size_write(inode, cifsi->server_eof); + spin_unlock(&inode->i_lock); + + complete(&wdata->done); + + if (wdata->result != -EAGAIN) { + for (i = 0; i < wdata->nr_pages; i++) + put_page(wdata->pages[i]); + } + + kref_put(&wdata->refcount, cifs_writedata_release); +} + +/* attempt to send write to server, retry on any -EAGAIN errors */ +static int +cifs_uncached_retry_writev(struct cifs_writedata *wdata) +{ + int rc; + + do { + if (wdata->cfile->invalidHandle) { + rc = cifs_reopen_file(wdata->cfile, false); + if (rc != 0) + continue; + } + rc = cifs_async_writev(wdata); + } while (rc == -EAGAIN); + + return rc; +} + static ssize_t cifs_iovec_write(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *poffset) { - unsigned int written; - unsigned long num_pages, npages, i; + unsigned long nr_pages, i; size_t copied, len, cur_len; ssize_t total_written = 0; - struct kvec *to_send; - struct page **pages; + loff_t offset = *poffset; struct iov_iter it; - struct inode *inode; struct cifsFileInfo *open_file; - struct cifs_tcon *pTcon; + struct cifs_tcon *tcon; struct cifs_sb_info *cifs_sb; - struct cifs_io_parms io_parms; - int xid, rc; - __u32 pid; + struct cifs_writedata *wdata, *tmp; + struct list_head wdata_list; + int rc; + pid_t pid; len = iov_length(iov, nr_segs); if (!len) @@ -2133,105 +2188,103 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, if (rc) return rc; + INIT_LIST_HEAD(&wdata_list); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - num_pages = get_numpages(cifs_sb->wsize, len, &cur_len); - - pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL); - if (!pages) - return -ENOMEM; - - to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL); - if (!to_send) { - kfree(pages); - return -ENOMEM; - } - - rc = cifs_write_allocate_pages(pages, num_pages); - if (rc) { - kfree(pages); - kfree(to_send); - return rc; - } - - xid = GetXid(); open_file = file->private_data; + tcon = tlink_tcon(open_file->tlink); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) pid = open_file->pid; else pid = current->tgid; - pTcon = tlink_tcon(open_file->tlink); - inode = file->f_path.dentry->d_inode; - iov_iter_init(&it, iov, nr_segs, len, 0); - npages = num_pages; - do { - size_t save_len = cur_len; - for (i = 0; i < npages; i++) { - copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE); - copied = iov_iter_copy_from_user(pages[i], &it, 0, - copied); + size_t save_len; + + nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len); + wdata = cifs_writedata_alloc(nr_pages, + cifs_uncached_writev_complete); + if (!wdata) { + rc = -ENOMEM; + break; + } + + rc = cifs_write_allocate_pages(wdata->pages, nr_pages); + if (rc) { + kfree(wdata); + break; + } + + save_len = cur_len; + for (i = 0; i < nr_pages; i++) { + copied = min_t(const size_t, cur_len, PAGE_SIZE); + copied = iov_iter_copy_from_user(wdata->pages[i], &it, + 0, copied); cur_len -= copied; iov_iter_advance(&it, copied); - to_send[i+1].iov_base = kmap(pages[i]); - to_send[i+1].iov_len = copied; } - cur_len = save_len - cur_len; - do { - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, false); - if (rc != 0) - break; - } - io_parms.netfid = open_file->netfid; - io_parms.pid = pid; - io_parms.tcon = pTcon; - io_parms.offset = *poffset; - io_parms.length = cur_len; - rc = CIFSSMBWrite2(xid, &io_parms, &written, to_send, - npages, 0); - } while (rc == -EAGAIN); - - for (i = 0; i < npages; i++) - kunmap(pages[i]); - - if (written) { - len -= written; - total_written += written; - spin_lock(&inode->i_lock); - cifs_update_eof(CIFS_I(inode), *poffset, written); - spin_unlock(&inode->i_lock); - *poffset += written; - } else if (rc < 0) { - if (!total_written) - total_written = rc; + wdata->sync_mode = WB_SYNC_ALL; + wdata->nr_pages = nr_pages; + wdata->offset = (__u64)offset; + wdata->cfile = cifsFileInfo_get(open_file); + wdata->pid = pid; + wdata->bytes = cur_len; + wdata->marshal_iov = cifs_uncached_marshal_iov; + rc = cifs_uncached_retry_writev(wdata); + if (rc) { + kref_put(&wdata->refcount, cifs_writedata_release); break; } - /* get length and number of kvecs of the next write */ - npages = get_numpages(cifs_sb->wsize, len, &cur_len); + list_add_tail(&wdata->list, &wdata_list); + offset += cur_len; + len -= cur_len; } while (len > 0); - if (total_written > 0) { - spin_lock(&inode->i_lock); - if (*poffset > inode->i_size) - i_size_write(inode, *poffset); - spin_unlock(&inode->i_lock); + /* + * If at least one write was successfully sent, then discard any rc + * value from the later writes. If the other write succeeds, then + * we'll end up returning whatever was written. If it fails, then + * we'll get a new rc value from that. + */ + if (!list_empty(&wdata_list)) + rc = 0; + + /* + * Wait for and collect replies for any successful sends in order of + * increasing offset. Once an error is hit or we get a fatal signal + * while waiting, then return without waiting for any more replies. + */ +restart_loop: + list_for_each_entry_safe(wdata, tmp, &wdata_list, list) { + if (!rc) { + /* FIXME: freezable too? */ + rc = wait_for_completion_killable(&wdata->done); + if (rc) + rc = -EINTR; + else if (wdata->result) + rc = wdata->result; + else + total_written += wdata->bytes; + + /* resend call if it's a retryable error */ + if (rc == -EAGAIN) { + rc = cifs_uncached_retry_writev(wdata); + goto restart_loop; + } + } + list_del_init(&wdata->list); + kref_put(&wdata->refcount, cifs_writedata_release); } - cifs_stats_bytes_written(pTcon, total_written); - mark_inode_dirty_sync(inode); + if (total_written > 0) + *poffset += total_written; - for (i = 0; i < num_pages; i++) - put_page(pages[i]); - kfree(to_send); - kfree(pages); - FreeXid(xid); - return total_written; + cifs_stats_bytes_written(tcon, total_written); + return total_written ? total_written : (ssize_t)rc; } ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, -- cgit v1.1 From 27ac5755aee0c99b9148ccceccb800270b9e5628 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: remove /proc/fs/cifs/OplockEnabled We've had a deprecation warning on this file for 2 releases now. Remove it as promised for 3.4. Signed-off-by: Jeff Layton --- fs/cifs/cifs_debug.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 81be263..2704646 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -418,7 +418,6 @@ static const struct file_operations cifs_stats_proc_fops = { static struct proc_dir_entry *proc_fs_cifs; static const struct file_operations cifsFYI_proc_fops; -static const struct file_operations cifs_oplock_proc_fops; static const struct file_operations cifs_lookup_cache_proc_fops; static const struct file_operations traceSMB_proc_fops; static const struct file_operations cifs_multiuser_mount_proc_fops; @@ -439,7 +438,6 @@ cifs_proc_init(void) #endif /* STATS */ proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops); proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops); - proc_create("OplockEnabled", 0, proc_fs_cifs, &cifs_oplock_proc_fops); proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs, &cifs_linux_ext_proc_fops); proc_create("MultiuserMount", 0, proc_fs_cifs, @@ -463,7 +461,6 @@ cifs_proc_clean(void) remove_proc_entry("Stats", proc_fs_cifs); #endif remove_proc_entry("MultiuserMount", proc_fs_cifs); - remove_proc_entry("OplockEnabled", proc_fs_cifs); remove_proc_entry("SecurityFlags", proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); @@ -509,46 +506,6 @@ static const struct file_operations cifsFYI_proc_fops = { .write = cifsFYI_proc_write, }; -static int cifs_oplock_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", enable_oplocks); - return 0; -} - -static int cifs_oplock_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_oplock_proc_show, NULL); -} - -static ssize_t cifs_oplock_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - char c; - int rc; - - printk(KERN_WARNING "CIFS: The /proc/fs/cifs/OplockEnabled interface " - "will be removed in kernel version 3.4. Please migrate to " - "using the 'enable_oplocks' module parameter in cifs.ko.\n"); - rc = get_user(c, buffer); - if (rc) - return rc; - if (c == '0' || c == 'n' || c == 'N') - enable_oplocks = false; - else if (c == '1' || c == 'y' || c == 'Y') - enable_oplocks = true; - - return count; -} - -static const struct file_operations cifs_oplock_proc_fops = { - .owner = THIS_MODULE, - .open = cifs_oplock_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = cifs_oplock_proc_write, -}; - static int cifs_linux_ext_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", linuxExtEnabled); -- cgit v1.1 From 8830d7e07a5e38bc47650a7554b7c1cfd49902bf Mon Sep 17 00:00:00 2001 From: Sachin Prabhu Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: use standard token parser for mount options Use the standard token parser instead of the long if condition to parse cifs mount options. This was first proposed by Scott Lovenberg http://lists.samba.org/archive/linux-cifs-client/2010-May/006079.html Mount options have been grouped together in terms of their input types. Aliases for username, password, domain and credentials have been added. The password parser has been modified to make it easier to read. Since the patch was first proposed, the following bugs have been fixed 1) Allow blank 'pass' option to be passed by the cifs mount helper when using sec=none. 2) Do not explicitly set vol->nullauth to 0. This causes a problem when using sec=none while also using a username. Signed-off-by: Sachin Prabhu Reviewed-by: Jeff Layton --- fs/cifs/connect.c | 1338 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 814 insertions(+), 524 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 855d8c0..3d72218 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -40,6 +40,8 @@ #include #include #include +#include + #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" @@ -63,6 +65,191 @@ extern mempool_t *cifs_req_poolp; #define TLINK_ERROR_EXPIRE (1 * HZ) #define TLINK_IDLE_EXPIRE (600 * HZ) +enum { + + /* Mount options that take no arguments */ + Opt_user_xattr, Opt_nouser_xattr, + Opt_forceuid, Opt_noforceuid, + Opt_noblocksend, Opt_noautotune, + Opt_hard, Opt_soft, Opt_perm, Opt_noperm, + Opt_mapchars, Opt_nomapchars, Opt_sfu, + Opt_nosfu, Opt_nodfs, Opt_posixpaths, + Opt_noposixpaths, Opt_nounix, + Opt_nocase, + Opt_brl, Opt_nobrl, + Opt_forcemandatorylock, Opt_setuids, + Opt_nosetuids, Opt_dynperm, Opt_nodynperm, + Opt_nohard, Opt_nosoft, + Opt_nointr, Opt_intr, + Opt_nostrictsync, Opt_strictsync, + Opt_serverino, Opt_noserverino, + Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl, + Opt_acl, Opt_noacl, Opt_locallease, + Opt_sign, Opt_seal, Opt_direct, + Opt_strictcache, Opt_noac, + Opt_fsc, Opt_mfsymlinks, + Opt_multiuser, + + /* Mount options which take numeric value */ + Opt_backupuid, Opt_backupgid, Opt_uid, + Opt_cruid, Opt_gid, Opt_file_mode, + Opt_dirmode, Opt_port, + Opt_rsize, Opt_wsize, Opt_actimeo, + + /* Mount options which take string value */ + Opt_user, Opt_pass, Opt_ip, + Opt_unc, Opt_domain, + Opt_srcaddr, Opt_prefixpath, + Opt_iocharset, Opt_sockopt, + Opt_netbiosname, Opt_servern, + Opt_ver, Opt_sec, + + /* Mount options to be ignored */ + Opt_ignore, + + /* Options which could be blank */ + Opt_blank_pass, + + Opt_err +}; + +static const match_table_t cifs_mount_option_tokens = { + + { Opt_user_xattr, "user_xattr" }, + { Opt_nouser_xattr, "nouser_xattr" }, + { Opt_forceuid, "forceuid" }, + { Opt_noforceuid, "noforceuid" }, + { Opt_noblocksend, "noblocksend" }, + { Opt_noautotune, "noautotune" }, + { Opt_hard, "hard" }, + { Opt_soft, "soft" }, + { Opt_perm, "perm" }, + { Opt_noperm, "noperm" }, + { Opt_mapchars, "mapchars" }, + { Opt_nomapchars, "nomapchars" }, + { Opt_sfu, "sfu" }, + { Opt_nosfu, "nosfu" }, + { Opt_nodfs, "nodfs" }, + { Opt_posixpaths, "posixpaths" }, + { Opt_noposixpaths, "noposixpaths" }, + { Opt_nounix, "nounix" }, + { Opt_nounix, "nolinux" }, + { Opt_nocase, "nocase" }, + { Opt_nocase, "ignorecase" }, + { Opt_brl, "brl" }, + { Opt_nobrl, "nobrl" }, + { Opt_nobrl, "nolock" }, + { Opt_forcemandatorylock, "forcemandatorylock" }, + { Opt_setuids, "setuids" }, + { Opt_nosetuids, "nosetuids" }, + { Opt_dynperm, "dynperm" }, + { Opt_nodynperm, "nodynperm" }, + { Opt_nohard, "nohard" }, + { Opt_nosoft, "nosoft" }, + { Opt_nointr, "nointr" }, + { Opt_intr, "intr" }, + { Opt_nostrictsync, "nostrictsync" }, + { Opt_strictsync, "strictsync" }, + { Opt_serverino, "serverino" }, + { Opt_noserverino, "noserverino" }, + { Opt_rwpidforward, "rwpidforward" }, + { Opt_cifsacl, "cifsacl" }, + { Opt_nocifsacl, "nocifsacl" }, + { Opt_acl, "acl" }, + { Opt_noacl, "noacl" }, + { Opt_locallease, "locallease" }, + { Opt_sign, "sign" }, + { Opt_seal, "seal" }, + { Opt_direct, "direct" }, + { Opt_direct, "forceddirectio" }, + { Opt_strictcache, "strictcache" }, + { Opt_noac, "noac" }, + { Opt_fsc, "fsc" }, + { Opt_mfsymlinks, "mfsymlinks" }, + { Opt_multiuser, "multiuser" }, + + { Opt_backupuid, "backupuid=%s" }, + { Opt_backupgid, "backupgid=%s" }, + { Opt_uid, "uid=%s" }, + { Opt_cruid, "cruid=%s" }, + { Opt_gid, "gid=%s" }, + { Opt_file_mode, "file_mode=%s" }, + { Opt_dirmode, "dirmode=%s" }, + { Opt_dirmode, "dir_mode=%s" }, + { Opt_port, "port=%s" }, + { Opt_rsize, "rsize=%s" }, + { Opt_wsize, "wsize=%s" }, + { Opt_actimeo, "actimeo=%s" }, + + { Opt_user, "user=%s" }, + { Opt_user, "username=%s" }, + { Opt_blank_pass, "pass=" }, + { Opt_pass, "pass=%s" }, + { Opt_pass, "password=%s" }, + { Opt_ip, "ip=%s" }, + { Opt_ip, "addr=%s" }, + { Opt_unc, "unc=%s" }, + { Opt_unc, "target=%s" }, + { Opt_unc, "path=%s" }, + { Opt_domain, "dom=%s" }, + { Opt_domain, "domain=%s" }, + { Opt_domain, "workgroup=%s" }, + { Opt_srcaddr, "srcaddr=%s" }, + { Opt_prefixpath, "prefixpath=%s" }, + { Opt_iocharset, "iocharset=%s" }, + { Opt_sockopt, "sockopt=%s" }, + { Opt_netbiosname, "netbiosname=%s" }, + { Opt_servern, "servern=%s" }, + { Opt_ver, "ver=%s" }, + { Opt_ver, "vers=%s" }, + { Opt_ver, "version=%s" }, + { Opt_sec, "sec=%s" }, + + { Opt_ignore, "cred" }, + { Opt_ignore, "credentials" }, + { Opt_ignore, "guest" }, + { Opt_ignore, "rw" }, + { Opt_ignore, "ro" }, + { Opt_ignore, "suid" }, + { Opt_ignore, "nosuid" }, + { Opt_ignore, "exec" }, + { Opt_ignore, "noexec" }, + { Opt_ignore, "nodev" }, + { Opt_ignore, "noauto" }, + { Opt_ignore, "dev" }, + { Opt_ignore, "mand" }, + { Opt_ignore, "nomand" }, + { Opt_ignore, "_netdev" }, + + { Opt_err, NULL } +}; + +enum { + Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p, + Opt_sec_ntlmsspi, Opt_sec_ntlmssp, + Opt_ntlm, Opt_sec_ntlmi, Opt_sec_ntlmv2i, + Opt_sec_nontlm, Opt_sec_lanman, + Opt_sec_none, + + Opt_sec_err +}; + +static const match_table_t cifs_secflavor_tokens = { + { Opt_sec_krb5, "krb5" }, + { Opt_sec_krb5i, "krb5i" }, + { Opt_sec_krb5p, "krb5p" }, + { Opt_sec_ntlmsspi, "ntlmsspi" }, + { Opt_sec_ntlmssp, "ntlmssp" }, + { Opt_ntlm, "ntlm" }, + { Opt_sec_ntlmi, "ntlmi" }, + { Opt_sec_ntlmv2i, "ntlmv2i" }, + { Opt_sec_nontlm, "nontlm" }, + { Opt_sec_lanman, "lanman" }, + { Opt_sec_none, "none" }, + + { Opt_sec_err, NULL } +}; + static int ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); @@ -920,13 +1107,79 @@ extract_hostname(const char *unc) return dst; } +static int get_option_ul(substring_t args[], unsigned long *option) +{ + int rc; + char *string; + + string = match_strdup(args); + if (string == NULL) + return -ENOMEM; + rc = kstrtoul(string, 10, option); + kfree(string); + + return rc; +} + + +static int cifs_parse_security_flavors(char *value, + struct smb_vol *vol) +{ + + substring_t args[MAX_OPT_ARGS]; + + switch (match_token(value, cifs_secflavor_tokens, args)) { + case Opt_sec_krb5: + vol->secFlg |= CIFSSEC_MAY_KRB5; + break; + case Opt_sec_krb5i: + vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN; + break; + case Opt_sec_krb5p: + /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */ + cERROR(1, "Krb5 cifs privacy not supported"); + break; + case Opt_sec_ntlmssp: + vol->secFlg |= CIFSSEC_MAY_NTLMSSP; + break; + case Opt_sec_ntlmsspi: + vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN; + break; + case Opt_ntlm: + /* ntlm is default so can be turned off too */ + vol->secFlg |= CIFSSEC_MAY_NTLM; + break; + case Opt_sec_ntlmi: + vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN; + break; + case Opt_sec_nontlm: + vol->secFlg |= CIFSSEC_MAY_NTLMV2; + break; + case Opt_sec_ntlmv2i: + vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN; + break; +#ifdef CONFIG_CIFS_WEAK_PW_HASH + case Opt_sec_lanman: + vol->secFlg |= CIFSSEC_MAY_LANMAN; + break; +#endif + case Opt_sec_none: + vol->nullauth = 1; + break; + default: + cERROR(1, "bad security option: %s", value); + return 1; + } + + return 0; +} + static int cifs_parse_mount_options(const char *mountdata, const char *devname, struct smb_vol *vol) { - char *value, *data, *end; + char *data, *end; char *mountdata_copy = NULL, *options; - int err; unsigned int temp_len, i, j; char separator[2]; short int override_uid = -1; @@ -934,9 +1187,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, bool uid_specified = false; bool gid_specified = false; char *nodename = utsname()->nodename; + char *string = NULL; + char *tmp_end, *value; + char delim; separator[0] = ','; separator[1] = 0; + delim = separator[0]; /* * does not have to be perfect mapping since field is @@ -975,6 +1232,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, options = mountdata_copy; end = options + strlen(options); + if (strncmp(options, "sep=", 4) == 0) { if (options[4] != 0) { separator[0] = options[4]; @@ -987,609 +1245,638 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, vol->backupgid_specified = false; /* no backup intent for a group */ while ((data = strsep(&options, separator)) != NULL) { + substring_t args[MAX_OPT_ARGS]; + unsigned long option; + int token; + if (!*data) continue; - if ((value = strchr(data, '=')) != NULL) - *value++ = '\0'; - /* Have to parse this before we parse for "user" */ - if (strnicmp(data, "user_xattr", 10) == 0) { + token = match_token(data, cifs_mount_option_tokens, args); + + switch (token) { + + /* Ingnore the following */ + case Opt_ignore: + break; + + /* Boolean values */ + case Opt_user_xattr: vol->no_xattr = 0; - } else if (strnicmp(data, "nouser_xattr", 12) == 0) { + break; + case Opt_nouser_xattr: vol->no_xattr = 1; - } else if (strnicmp(data, "user", 4) == 0) { - if (!value) { - printk(KERN_WARNING - "CIFS: invalid or missing username\n"); - goto cifs_parse_mount_err; - } else if (!*value) { - /* null user, ie anonymous, authentication */ - vol->nullauth = 1; - } - if (strnlen(value, MAX_USERNAME_SIZE) < - MAX_USERNAME_SIZE) { - vol->username = kstrdup(value, GFP_KERNEL); - if (!vol->username) { - printk(KERN_WARNING "CIFS: no memory " - "for username\n"); - goto cifs_parse_mount_err; - } - } else { - printk(KERN_WARNING "CIFS: username too long\n"); - goto cifs_parse_mount_err; - } - } else if (strnicmp(data, "pass", 4) == 0) { - if (!value) { - vol->password = NULL; - continue; - } else if (value[0] == 0) { - /* check if string begins with double comma - since that would mean the password really - does start with a comma, and would not - indicate an empty string */ - if (value[1] != separator[0]) { - vol->password = NULL; - continue; - } - } - temp_len = strlen(value); - /* removed password length check, NTLM passwords - can be arbitrarily long */ - - /* if comma in password, the string will be - prematurely null terminated. Commas in password are - specified across the cifs mount interface by a double - comma ie ,, and a comma used as in other cases ie ',' - as a parameter delimiter/separator is single and due - to the strsep above is temporarily zeroed. */ - - /* NB: password legally can have multiple commas and - the only illegal character in a password is null */ - - if ((value[temp_len] == 0) && - (value + temp_len < end) && - (value[temp_len+1] == separator[0])) { - /* reinsert comma */ - value[temp_len] = separator[0]; - temp_len += 2; /* move after second comma */ - while (value[temp_len] != 0) { - if (value[temp_len] == separator[0]) { - if (value[temp_len+1] == - separator[0]) { - /* skip second comma */ - temp_len++; - } else { - /* single comma indicating start - of next parm */ - break; - } - } - temp_len++; - } - if (value[temp_len] == 0) { - options = NULL; - } else { - value[temp_len] = 0; - /* point option to start of next parm */ - options = value + temp_len + 1; - } - /* go from value to value + temp_len condensing - double commas to singles. Note that this ends up - allocating a few bytes too many, which is ok */ - vol->password = kzalloc(temp_len, GFP_KERNEL); - if (vol->password == NULL) { - printk(KERN_WARNING "CIFS: no memory " - "for password\n"); - goto cifs_parse_mount_err; - } - for (i = 0, j = 0; i < temp_len; i++, j++) { - vol->password[j] = value[i]; - if (value[i] == separator[0] - && value[i+1] == separator[0]) { - /* skip second comma */ - i++; - } - } - vol->password[j] = 0; - } else { - vol->password = kzalloc(temp_len+1, GFP_KERNEL); - if (vol->password == NULL) { - printk(KERN_WARNING "CIFS: no memory " - "for password\n"); - goto cifs_parse_mount_err; - } - strcpy(vol->password, value); - } - } else if (!strnicmp(data, "ip", 2) || - !strnicmp(data, "addr", 4)) { - if (!value || !*value) { - vol->UNCip = NULL; - } else if (strnlen(value, INET6_ADDRSTRLEN) < - INET6_ADDRSTRLEN) { - vol->UNCip = kstrdup(value, GFP_KERNEL); - if (!vol->UNCip) { - printk(KERN_WARNING "CIFS: no memory " - "for UNC IP\n"); - goto cifs_parse_mount_err; - } - } else { - printk(KERN_WARNING "CIFS: ip address " - "too long\n"); - goto cifs_parse_mount_err; - } - } else if (strnicmp(data, "sec", 3) == 0) { - if (!value || !*value) { - cERROR(1, "no security value specified"); - continue; - } else if (strnicmp(value, "krb5i", 5) == 0) { - vol->secFlg |= CIFSSEC_MAY_KRB5 | - CIFSSEC_MUST_SIGN; - } else if (strnicmp(value, "krb5p", 5) == 0) { - /* vol->secFlg |= CIFSSEC_MUST_SEAL | - CIFSSEC_MAY_KRB5; */ - cERROR(1, "Krb5 cifs privacy not supported"); - goto cifs_parse_mount_err; - } else if (strnicmp(value, "krb5", 4) == 0) { - vol->secFlg |= CIFSSEC_MAY_KRB5; - } else if (strnicmp(value, "ntlmsspi", 8) == 0) { - vol->secFlg |= CIFSSEC_MAY_NTLMSSP | - CIFSSEC_MUST_SIGN; - } else if (strnicmp(value, "ntlmssp", 7) == 0) { - vol->secFlg |= CIFSSEC_MAY_NTLMSSP; - } else if (strnicmp(value, "ntlmv2i", 7) == 0) { - vol->secFlg |= CIFSSEC_MAY_NTLMV2 | - CIFSSEC_MUST_SIGN; - } else if (strnicmp(value, "ntlmv2", 6) == 0) { - vol->secFlg |= CIFSSEC_MAY_NTLMV2; - } else if (strnicmp(value, "ntlmi", 5) == 0) { - vol->secFlg |= CIFSSEC_MAY_NTLM | - CIFSSEC_MUST_SIGN; - } else if (strnicmp(value, "ntlm", 4) == 0) { - /* ntlm is default so can be turned off too */ - vol->secFlg |= CIFSSEC_MAY_NTLM; - } else if (strnicmp(value, "nontlm", 6) == 0) { - /* BB is there a better way to do this? */ - vol->secFlg |= CIFSSEC_MAY_NTLMV2; -#ifdef CONFIG_CIFS_WEAK_PW_HASH - } else if (strnicmp(value, "lanman", 6) == 0) { - vol->secFlg |= CIFSSEC_MAY_LANMAN; -#endif - } else if (strnicmp(value, "none", 4) == 0) { - vol->nullauth = 1; - } else { - cERROR(1, "bad security option: %s", value); - goto cifs_parse_mount_err; - } - } else if (strnicmp(data, "vers", 3) == 0) { - if (!value || !*value) { - cERROR(1, "no protocol version specified" - " after vers= mount option"); - } else if ((strnicmp(value, "cifs", 4) == 0) || - (strnicmp(value, "1", 1) == 0)) { - /* this is the default */ - continue; - } - } else if ((strnicmp(data, "unc", 3) == 0) - || (strnicmp(data, "target", 6) == 0) - || (strnicmp(data, "path", 4) == 0)) { - if (!value || !*value) { - printk(KERN_WARNING "CIFS: invalid path to " - "network resource\n"); - goto cifs_parse_mount_err; - } - if ((temp_len = strnlen(value, 300)) < 300) { - vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); - if (vol->UNC == NULL) - goto cifs_parse_mount_err; - strcpy(vol->UNC, value); - if (strncmp(vol->UNC, "//", 2) == 0) { - vol->UNC[0] = '\\'; - vol->UNC[1] = '\\'; - } else if (strncmp(vol->UNC, "\\\\", 2) != 0) { - printk(KERN_WARNING - "CIFS: UNC Path does not begin " - "with // or \\\\ \n"); - goto cifs_parse_mount_err; - } - } else { - printk(KERN_WARNING "CIFS: UNC name too long\n"); - goto cifs_parse_mount_err; - } - } else if ((strnicmp(data, "domain", 3) == 0) - || (strnicmp(data, "workgroup", 5) == 0)) { - if (!value || !*value) { - printk(KERN_WARNING "CIFS: invalid domain name\n"); - goto cifs_parse_mount_err; - } - /* BB are there cases in which a comma can be valid in - a domain name and need special handling? */ - if (strnlen(value, 256) < 256) { - vol->domainname = kstrdup(value, GFP_KERNEL); - if (!vol->domainname) { - printk(KERN_WARNING "CIFS: no memory " - "for domainname\n"); - goto cifs_parse_mount_err; - } - cFYI(1, "Domain name set"); - } else { - printk(KERN_WARNING "CIFS: domain name too " - "long\n"); - goto cifs_parse_mount_err; - } - } else if (strnicmp(data, "srcaddr", 7) == 0) { - vol->srcaddr.ss_family = AF_UNSPEC; - - if (!value || !*value) { - printk(KERN_WARNING "CIFS: srcaddr value" - " not specified.\n"); - goto cifs_parse_mount_err; - } - i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, - value, strlen(value)); - if (i == 0) { - printk(KERN_WARNING "CIFS: Could not parse" - " srcaddr: %s\n", - value); - goto cifs_parse_mount_err; - } - } else if (strnicmp(data, "prefixpath", 10) == 0) { - if (!value || !*value) { - printk(KERN_WARNING - "CIFS: invalid path prefix\n"); - goto cifs_parse_mount_err; - } - if ((temp_len = strnlen(value, 1024)) < 1024) { - if (value[0] != '/') - temp_len++; /* missing leading slash */ - vol->prepath = kmalloc(temp_len+1, GFP_KERNEL); - if (vol->prepath == NULL) - goto cifs_parse_mount_err; - if (value[0] != '/') { - vol->prepath[0] = '/'; - strcpy(vol->prepath+1, value); - } else - strcpy(vol->prepath, value); - cFYI(1, "prefix path %s", vol->prepath); - } else { - printk(KERN_WARNING "CIFS: prefix too long\n"); - goto cifs_parse_mount_err; - } - } else if (strnicmp(data, "iocharset", 9) == 0) { - if (!value || !*value) { - printk(KERN_WARNING "CIFS: invalid iocharset " - "specified\n"); - goto cifs_parse_mount_err; - } - if (strnlen(value, 65) < 65) { - if (strnicmp(value, "default", 7)) { - vol->iocharset = kstrdup(value, - GFP_KERNEL); - - if (!vol->iocharset) { - printk(KERN_WARNING "CIFS: no " - "memory for" - "charset\n"); - goto cifs_parse_mount_err; - } - } - /* if iocharset not set then load_nls_default - is used by caller */ - cFYI(1, "iocharset set to %s", value); - } else { - printk(KERN_WARNING "CIFS: iocharset name " - "too long.\n"); - goto cifs_parse_mount_err; - } - } else if (!strnicmp(data, "uid", 3) && value && *value) { - vol->linux_uid = simple_strtoul(value, &value, 0); - uid_specified = true; - } else if (!strnicmp(data, "cruid", 5) && value && *value) { - vol->cred_uid = simple_strtoul(value, &value, 0); - } else if (!strnicmp(data, "forceuid", 8)) { + break; + case Opt_forceuid: override_uid = 1; - } else if (!strnicmp(data, "noforceuid", 10)) { + break; + case Opt_noforceuid: override_uid = 0; - } else if (!strnicmp(data, "gid", 3) && value && *value) { - vol->linux_gid = simple_strtoul(value, &value, 0); - gid_specified = true; - } else if (!strnicmp(data, "forcegid", 8)) { - override_gid = 1; - } else if (!strnicmp(data, "noforcegid", 10)) { - override_gid = 0; - } else if (strnicmp(data, "file_mode", 4) == 0) { - if (value && *value) { - vol->file_mode = - simple_strtoul(value, &value, 0); - } - } else if (strnicmp(data, "dir_mode", 4) == 0) { - if (value && *value) { - vol->dir_mode = - simple_strtoul(value, &value, 0); - } - } else if (strnicmp(data, "dirmode", 4) == 0) { - if (value && *value) { - vol->dir_mode = - simple_strtoul(value, &value, 0); - } - } else if (strnicmp(data, "port", 4) == 0) { - if (value && *value) { - vol->port = - simple_strtoul(value, &value, 0); - } - } else if (strnicmp(data, "rsize", 5) == 0) { - if (value && *value) { - vol->rsize = - simple_strtoul(value, &value, 0); - } - } else if (strnicmp(data, "wsize", 5) == 0) { - if (value && *value) { - vol->wsize = - simple_strtoul(value, &value, 0); - } - } else if (strnicmp(data, "sockopt", 5) == 0) { - if (!value || !*value) { - cERROR(1, "no socket option specified"); - continue; - } else if (strnicmp(value, "TCP_NODELAY", 11) == 0) { - vol->sockopt_tcp_nodelay = 1; - } - } else if (strnicmp(data, "netbiosname", 4) == 0) { - if (!value || !*value || (*value == ' ')) { - cFYI(1, "invalid (empty) netbiosname"); - } else { - memset(vol->source_rfc1001_name, 0x20, - RFC1001_NAME_LEN); - /* - * FIXME: are there cases in which a comma can - * be valid in workstation netbios name (and - * need special handling)? - */ - for (i = 0; i < RFC1001_NAME_LEN; i++) { - /* don't ucase netbiosname for user */ - if (value[i] == 0) - break; - vol->source_rfc1001_name[i] = value[i]; - } - /* The string has 16th byte zero still from - set at top of the function */ - if (i == RFC1001_NAME_LEN && value[i] != 0) - printk(KERN_WARNING "CIFS: netbiosname" - " longer than 15 truncated.\n"); - } - } else if (strnicmp(data, "servern", 7) == 0) { - /* servernetbiosname specified override *SMBSERVER */ - if (!value || !*value || (*value == ' ')) { - cFYI(1, "empty server netbiosname specified"); - } else { - /* last byte, type, is 0x20 for servr type */ - memset(vol->target_rfc1001_name, 0x20, - RFC1001_NAME_LEN_WITH_NULL); - - for (i = 0; i < 15; i++) { - /* BB are there cases in which a comma can be - valid in this workstation netbios name - (and need special handling)? */ - - /* user or mount helper must uppercase - the netbiosname */ - if (value[i] == 0) - break; - else - vol->target_rfc1001_name[i] = - value[i]; - } - /* The string has 16th byte zero still from - set at top of the function */ - if (i == RFC1001_NAME_LEN && value[i] != 0) - printk(KERN_WARNING "CIFS: server net" - "biosname longer than 15 truncated.\n"); - } - } else if (strnicmp(data, "actimeo", 7) == 0) { - if (value && *value) { - vol->actimeo = HZ * simple_strtoul(value, - &value, 0); - if (vol->actimeo > CIFS_MAX_ACTIMEO) { - cERROR(1, "CIFS: attribute cache" - "timeout too large"); - goto cifs_parse_mount_err; - } - } - } else if (strnicmp(data, "credentials", 4) == 0) { - /* ignore */ - } else if (strnicmp(data, "version", 3) == 0) { - /* ignore */ - } else if (strnicmp(data, "guest", 5) == 0) { - /* ignore */ - } else if (strnicmp(data, "rw", 2) == 0 && strlen(data) == 2) { - /* ignore */ - } else if (strnicmp(data, "ro", 2) == 0) { - /* ignore */ - } else if (strnicmp(data, "noblocksend", 11) == 0) { + break; + case Opt_noblocksend: vol->noblocksnd = 1; - } else if (strnicmp(data, "noautotune", 10) == 0) { + break; + case Opt_noautotune: vol->noautotune = 1; - } else if ((strnicmp(data, "suid", 4) == 0) || - (strnicmp(data, "nosuid", 6) == 0) || - (strnicmp(data, "exec", 4) == 0) || - (strnicmp(data, "noexec", 6) == 0) || - (strnicmp(data, "nodev", 5) == 0) || - (strnicmp(data, "noauto", 6) == 0) || - (strnicmp(data, "dev", 3) == 0)) { - /* The mount tool or mount.cifs helper (if present) - uses these opts to set flags, and the flags are read - by the kernel vfs layer before we get here (ie - before read super) so there is no point trying to - parse these options again and set anything and it - is ok to just ignore them */ - continue; - } else if (strnicmp(data, "hard", 4) == 0) { + break; + case Opt_hard: vol->retry = 1; - } else if (strnicmp(data, "soft", 4) == 0) { + break; + case Opt_soft: vol->retry = 0; - } else if (strnicmp(data, "perm", 4) == 0) { + break; + case Opt_perm: vol->noperm = 0; - } else if (strnicmp(data, "noperm", 6) == 0) { + break; + case Opt_noperm: vol->noperm = 1; - } else if (strnicmp(data, "mapchars", 8) == 0) { + break; + case Opt_mapchars: vol->remap = 1; - } else if (strnicmp(data, "nomapchars", 10) == 0) { + break; + case Opt_nomapchars: vol->remap = 0; - } else if (strnicmp(data, "sfu", 3) == 0) { + break; + case Opt_sfu: vol->sfu_emul = 1; - } else if (strnicmp(data, "nosfu", 5) == 0) { + break; + case Opt_nosfu: vol->sfu_emul = 0; - } else if (strnicmp(data, "nodfs", 5) == 0) { + break; + case Opt_nodfs: vol->nodfs = 1; - } else if (strnicmp(data, "posixpaths", 10) == 0) { + break; + case Opt_posixpaths: vol->posix_paths = 1; - } else if (strnicmp(data, "noposixpaths", 12) == 0) { + break; + case Opt_noposixpaths: vol->posix_paths = 0; - } else if (strnicmp(data, "nounix", 6) == 0) { - vol->no_linux_ext = 1; - } else if (strnicmp(data, "nolinux", 7) == 0) { + break; + case Opt_nounix: vol->no_linux_ext = 1; - } else if ((strnicmp(data, "nocase", 6) == 0) || - (strnicmp(data, "ignorecase", 10) == 0)) { + break; + case Opt_nocase: vol->nocase = 1; - } else if (strnicmp(data, "mand", 4) == 0) { - /* ignore */ - } else if (strnicmp(data, "nomand", 6) == 0) { - /* ignore */ - } else if (strnicmp(data, "_netdev", 7) == 0) { - /* ignore */ - } else if (strnicmp(data, "brl", 3) == 0) { + break; + case Opt_brl: vol->nobrl = 0; - } else if ((strnicmp(data, "nobrl", 5) == 0) || - (strnicmp(data, "nolock", 6) == 0)) { + break; + case Opt_nobrl: vol->nobrl = 1; /* turn off mandatory locking in mode - if remote locking is turned off since the - local vfs will do advisory */ + * if remote locking is turned off since the + * local vfs will do advisory */ 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) */ + break; + case Opt_forcemandatorylock: vol->mand_lock = 1; - } else if (strnicmp(data, "setuids", 7) == 0) { + break; + case Opt_setuids: vol->setuids = 1; - } else if (strnicmp(data, "nosetuids", 9) == 0) { + break; + case Opt_nosetuids: vol->setuids = 0; - } else if (strnicmp(data, "dynperm", 7) == 0) { + break; + case Opt_dynperm: vol->dynperm = true; - } else if (strnicmp(data, "nodynperm", 9) == 0) { + break; + case Opt_nodynperm: vol->dynperm = false; - } else if (strnicmp(data, "nohard", 6) == 0) { + break; + case Opt_nohard: vol->retry = 0; - } else if (strnicmp(data, "nosoft", 6) == 0) { + break; + case Opt_nosoft: vol->retry = 1; - } else if (strnicmp(data, "nointr", 6) == 0) { + break; + case Opt_nointr: vol->intr = 0; - } else if (strnicmp(data, "intr", 4) == 0) { + break; + case Opt_intr: vol->intr = 1; - } else if (strnicmp(data, "nostrictsync", 12) == 0) { + break; + case Opt_nostrictsync: vol->nostrictsync = 1; - } else if (strnicmp(data, "strictsync", 10) == 0) { + break; + case Opt_strictsync: vol->nostrictsync = 0; - } else if (strnicmp(data, "serverino", 7) == 0) { + break; + case Opt_serverino: vol->server_ino = 1; - } else if (strnicmp(data, "noserverino", 9) == 0) { + break; + case Opt_noserverino: vol->server_ino = 0; - } else if (strnicmp(data, "rwpidforward", 12) == 0) { + break; + case Opt_rwpidforward: vol->rwpidforward = 1; - } else if (strnicmp(data, "cifsacl", 7) == 0) { + break; + case Opt_cifsacl: vol->cifs_acl = 1; - } else if (strnicmp(data, "nocifsacl", 9) == 0) { + break; + case Opt_nocifsacl: vol->cifs_acl = 0; - } else if (strnicmp(data, "acl", 3) == 0) { + break; + case Opt_acl: vol->no_psx_acl = 0; - } else if (strnicmp(data, "noacl", 5) == 0) { + break; + case Opt_noacl: vol->no_psx_acl = 1; - } else if (strnicmp(data, "locallease", 6) == 0) { + break; + case Opt_locallease: vol->local_lease = 1; - } else if (strnicmp(data, "sign", 4) == 0) { + break; + case Opt_sign: vol->secFlg |= CIFSSEC_MUST_SIGN; - } else if (strnicmp(data, "seal", 4) == 0) { + break; + case Opt_seal: /* we do not do the following in secFlags because seal - is a per tree connection (mount) not a per socket - or per-smb connection option in the protocol */ - /* vol->secFlg |= CIFSSEC_MUST_SEAL; */ + * is a per tree connection (mount) not a per socket + * or per-smb connection option in the protocol + * vol->secFlg |= CIFSSEC_MUST_SEAL; + */ vol->seal = 1; - } else if (strnicmp(data, "direct", 6) == 0) { - vol->direct_io = 1; - } else if (strnicmp(data, "forcedirectio", 13) == 0) { + break; + case Opt_direct: vol->direct_io = 1; - } else if (strnicmp(data, "strictcache", 11) == 0) { + break; + case Opt_strictcache: vol->strict_io = 1; - } else if (strnicmp(data, "noac", 4) == 0) { + break; + case Opt_noac: printk(KERN_WARNING "CIFS: Mount option noac not " "supported. Instead set " "/proc/fs/cifs/LookupCacheEnabled to 0\n"); - } else if (strnicmp(data, "fsc", 3) == 0) { + break; + case Opt_fsc: #ifndef CONFIG_CIFS_FSCACHE cERROR(1, "FS-Cache support needs CONFIG_CIFS_FSCACHE " "kernel config option set"); goto cifs_parse_mount_err; #endif vol->fsc = true; - } else if (strnicmp(data, "mfsymlinks", 10) == 0) { + break; + case Opt_mfsymlinks: vol->mfsymlinks = true; - } else if (strnicmp(data, "multiuser", 8) == 0) { + break; + case Opt_multiuser: vol->multiuser = true; - } else if (!strnicmp(data, "backupuid", 9) && value && *value) { - err = kstrtouint(value, 0, &vol->backupuid); - if (err < 0) { + break; + + /* Numeric Values */ + case Opt_backupuid: + if (get_option_ul(args, &option)) { cERROR(1, "%s: Invalid backupuid value", __func__); goto cifs_parse_mount_err; } + vol->backupuid = option; vol->backupuid_specified = true; - } else if (!strnicmp(data, "backupgid", 9) && value && *value) { - err = kstrtouint(value, 0, &vol->backupgid); - if (err < 0) { + break; + case Opt_backupgid: + if (get_option_ul(args, &option)) { cERROR(1, "%s: Invalid backupgid value", __func__); goto cifs_parse_mount_err; } + vol->backupgid = option; vol->backupgid_specified = true; - } else - printk(KERN_WARNING "CIFS: Unknown mount option %s\n", - data); - } - if (vol->UNC == NULL) { - if (devname == NULL) { - printk(KERN_WARNING "CIFS: Missing UNC name for mount " - "target\n"); - goto cifs_parse_mount_err; - } - if ((temp_len = strnlen(devname, 300)) < 300) { - vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); - if (vol->UNC == NULL) + break; + case Opt_uid: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid uid value", + __func__); + goto cifs_parse_mount_err; + } + vol->linux_uid = option; + uid_specified = true; + break; + case Opt_cruid: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid cruid value", + __func__); + goto cifs_parse_mount_err; + } + vol->cred_uid = option; + break; + case Opt_gid: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid gid value", + __func__); + goto cifs_parse_mount_err; + } + vol->linux_gid = option; + gid_specified = true; + break; + case Opt_file_mode: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid file_mode value", + __func__); + goto cifs_parse_mount_err; + } + vol->file_mode = option; + break; + case Opt_dirmode: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid dir_mode value", + __func__); + goto cifs_parse_mount_err; + } + vol->dir_mode = option; + break; + case Opt_port: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid port value", + __func__); + goto cifs_parse_mount_err; + } + vol->port = option; + break; + case Opt_rsize: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid rsize value", + __func__); + goto cifs_parse_mount_err; + } + vol->rsize = option; + break; + case Opt_wsize: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid wsize value", + __func__); + goto cifs_parse_mount_err; + } + vol->wsize = option; + break; + case Opt_actimeo: + if (get_option_ul(args, &option)) { + cERROR(1, "%s: Invalid actimeo value", + __func__); + goto cifs_parse_mount_err; + } + vol->actimeo = HZ * option; + if (vol->actimeo > CIFS_MAX_ACTIMEO) { + cERROR(1, "CIFS: attribute cache" + "timeout too large"); + goto cifs_parse_mount_err; + } + break; + + /* String Arguments */ + + case Opt_user: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + /* null user, ie. anonymous authentication */ + vol->nullauth = 1; + } else if (strnlen(string, MAX_USERNAME_SIZE) > + MAX_USERNAME_SIZE) { + printk(KERN_WARNING "CIFS: username too long\n"); + goto cifs_parse_mount_err; + } + vol->username = kstrdup(string, GFP_KERNEL); + if (!vol->username) { + printk(KERN_WARNING "CIFS: no memory " + "for username\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_blank_pass: + vol->password = NULL; + break; + case Opt_pass: + /* passwords have to be handled differently + * to allow the character used for deliminator + * to be passed within them + */ + + /* Obtain the value string */ + value = strchr(data, '='); + if (value != NULL) + *value++ = '\0'; + + /* Set tmp_end to end of the string */ + tmp_end = (char *) value + strlen(value); + + /* Check if following character is the deliminator + * If yes, we have encountered a double deliminator + * reset the NULL character to the deliminator + */ + if (tmp_end < end && tmp_end[1] == delim) + tmp_end[0] = delim; + + /* Keep iterating until we get to a single deliminator + * OR the end + */ + while ((tmp_end = strchr(tmp_end, delim)) != NULL && + (tmp_end[1] == delim)) { + tmp_end = (char *) &tmp_end[2]; + } + + /* Reset var options to point to next element */ + if (tmp_end) { + tmp_end[0] = '\0'; + options = (char *) &tmp_end[1]; + } else + /* Reached the end of the mount option string */ + options = end; + + /* Now build new password string */ + temp_len = strlen(value); + vol->password = kzalloc(temp_len+1, GFP_KERNEL); + if (vol->password == NULL) { + printk(KERN_WARNING "CIFS: no memory " + "for password\n"); + goto cifs_parse_mount_err; + } + + for (i = 0, j = 0; i < temp_len; i++, j++) { + vol->password[j] = value[i]; + if ((value[i] == delim) && + value[i+1] == delim) + /* skip the second deliminator */ + i++; + } + vol->password[j] = '\0'; + break; + case Opt_ip: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + vol->UNCip = NULL; + } else if (strnlen(string, INET6_ADDRSTRLEN) > + INET6_ADDRSTRLEN) { + printk(KERN_WARNING "CIFS: ip address " + "too long\n"); + goto cifs_parse_mount_err; + } + vol->UNCip = kstrdup(string, GFP_KERNEL); + if (!vol->UNCip) { + printk(KERN_WARNING "CIFS: no memory " + "for UNC IP\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_unc: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: invalid path to " + "network resource\n"); + goto cifs_parse_mount_err; + } + + temp_len = strnlen(string, 300); + if (temp_len == 300) { + printk(KERN_WARNING "CIFS: UNC name too long\n"); goto cifs_parse_mount_err; - strcpy(vol->UNC, devname); - if (strncmp(vol->UNC, "//", 2) == 0) { + } + + if (strncmp(string, "//", 2) == 0) { vol->UNC[0] = '\\'; vol->UNC[1] = '\\'; - } else if (strncmp(vol->UNC, "\\\\", 2) != 0) { + } else if (strncmp(string, "\\\\", 2) != 0) { printk(KERN_WARNING "CIFS: UNC Path does not " - "begin with // or \\\\ \n"); + "begin with // or \\\\\n"); goto cifs_parse_mount_err; } - value = strpbrk(vol->UNC+2, "/\\"); - if (value) - *value = '\\'; - } else { - printk(KERN_WARNING "CIFS: UNC name too long\n"); + + vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); + if (vol->UNC == NULL) { + printk(KERN_WARNING "CIFS: no memory " + "for UNC\n"); + goto cifs_parse_mount_err; + } + strcpy(vol->UNC, string); + break; + case Opt_domain: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: invalid domain" + " name\n"); + goto cifs_parse_mount_err; + } else if (strnlen(string, 256) == 256) { + printk(KERN_WARNING "CIFS: domain name too" + " long\n"); + goto cifs_parse_mount_err; + } + + vol->domainname = kstrdup(string, GFP_KERNEL); + if (!vol->domainname) { + printk(KERN_WARNING "CIFS: no memory " + "for domainname\n"); + goto cifs_parse_mount_err; + } + cFYI(1, "Domain name set"); + break; + case Opt_srcaddr: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: srcaddr value not" + " specified\n"); + goto cifs_parse_mount_err; + } else if (!cifs_convert_address( + (struct sockaddr *)&vol->srcaddr, + string, strlen(string))) { + printk(KERN_WARNING "CIFS: Could not parse" + " srcaddr: %s\n", string); + goto cifs_parse_mount_err; + } + break; + case Opt_prefixpath: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: Invalid path" + " prefix\n"); + goto cifs_parse_mount_err; + } + temp_len = strnlen(string, 1024); + if (string[0] != '/') + temp_len++; /* missing leading slash */ + if (temp_len > 1024) { + printk(KERN_WARNING "CIFS: prefix too long\n"); + goto cifs_parse_mount_err; + } + + vol->prepath = kmalloc(temp_len+1, GFP_KERNEL); + if (vol->prepath == NULL) { + printk(KERN_WARNING "CIFS: no memory " + "for path prefix\n"); + goto cifs_parse_mount_err; + } + + if (string[0] != '/') { + vol->prepath[0] = '/'; + strcpy(vol->prepath+1, string); + } else + strcpy(vol->prepath, string); + + break; + case Opt_iocharset: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: Invalid iocharset" + " specified\n"); + goto cifs_parse_mount_err; + } else if (strnlen(string, 1024) >= 65) { + printk(KERN_WARNING "CIFS: iocharset name " + "too long.\n"); + goto cifs_parse_mount_err; + } + + if (strnicmp(string, "default", 7) != 0) { + vol->iocharset = kstrdup(string, + GFP_KERNEL); + if (!vol->iocharset) { + printk(KERN_WARNING "CIFS: no memory" + "for charset\n"); + goto cifs_parse_mount_err; + } + } + /* if iocharset not set then load_nls_default + * is used by caller + */ + cFYI(1, "iocharset set to %s", string); + break; + case Opt_sockopt: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: No socket option" + " specified\n"); + goto cifs_parse_mount_err; + } + if (strnicmp(string, "TCP_NODELAY", 11) == 0) + vol->sockopt_tcp_nodelay = 1; + break; + case Opt_netbiosname: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: Invalid (empty)" + " netbiosname\n"); + break; + } + + memset(vol->source_rfc1001_name, 0x20, + RFC1001_NAME_LEN); + /* + * FIXME: are there cases in which a comma can + * be valid in workstation netbios name (and + * need special handling)? + */ + for (i = 0; i < RFC1001_NAME_LEN; i++) { + /* don't ucase netbiosname for user */ + if (string[i] == 0) + break; + vol->source_rfc1001_name[i] = string[i]; + } + /* The string has 16th byte zero still from + * set at top of the function + */ + if (i == RFC1001_NAME_LEN && string[i] != 0) + printk(KERN_WARNING "CIFS: netbiosname" + " longer than 15 truncated.\n"); + + break; + case Opt_servern: + /* servernetbiosname specified override *SMBSERVER */ + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: Empty server" + " netbiosname specified\n"); + break; + } + /* last byte, type, is 0x20 for servr type */ + memset(vol->target_rfc1001_name, 0x20, + RFC1001_NAME_LEN_WITH_NULL); + + /* BB are there cases in which a comma can be + valid in this workstation netbios name + (and need special handling)? */ + + /* user or mount helper must uppercase the + netbios name */ + for (i = 0; i < 15; i++) { + if (string[i] == 0) + break; + vol->target_rfc1001_name[i] = string[i]; + } + /* The string has 16th byte zero still from + set at top of the function */ + if (i == RFC1001_NAME_LEN && string[i] != 0) + printk(KERN_WARNING "CIFS: server net" + "biosname longer than 15 truncated.\n"); + break; + case Opt_ver: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + cERROR(1, "no protocol version specified" + " after vers= mount option"); + goto cifs_parse_mount_err; + } + + if (strnicmp(string, "cifs", 4) == 0 || + strnicmp(string, "1", 1) == 0) { + /* This is the default */ + break; + } + /* For all other value, error */ + printk(KERN_WARNING "CIFS: Invalid version" + " specified\n"); goto cifs_parse_mount_err; + case Opt_sec: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (!*string) { + printk(KERN_WARNING "CIFS: no security flavor" + " specified\n"); + break; + } + + if (cifs_parse_security_flavors(string, vol) != 0) + goto cifs_parse_mount_err; + break; + default: + printk(KERN_WARNING "CIFS: Unknown mount option %s\n", + data); + break; } + /* Free up any allocated string */ + kfree(string); + string = NULL; } #ifndef CONFIG_KEYS @@ -1619,7 +1906,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, kfree(mountdata_copy); return 0; +out_nomem: + printk(KERN_WARNING "Could not allocate temporary buffer\n"); cifs_parse_mount_err: + kfree(string); kfree(mountdata_copy); return 1; } -- cgit v1.1 From d81625587f4fac44a4367c468ba2fe2d981460f8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:56 -0400 Subject: cifs: handle "sloppy" option appropriately cifs.ko has historically been tolerant of options that it does not recognize. This is not normal behavior for a filesystem however. Usually, it should only do this if you mount with '-s', and autofs generally passes -s to the mount command to allow this behavior. This patch makes cifs handle the option "sloppy" appropriately. If it's present in the options string, then the client will tolerate options that it doesn't recognize. If it's not present then the client will error out in the presence of options that it does not recognize and throw an error message explaining why. There is also a companion patch being proposed for mount.cifs to make it append "sloppy" to the mount options when passed the '-s' flag. This also should (obviously) be applied on top of Sachin's conversion to the standard option parser. Signed-off-by: Jeff Layton Acked-By: Sachin Prabhu --- fs/cifs/connect.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 3d72218..9808154 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -88,7 +88,7 @@ enum { Opt_sign, Opt_seal, Opt_direct, Opt_strictcache, Opt_noac, Opt_fsc, Opt_mfsymlinks, - Opt_multiuser, + Opt_multiuser, Opt_sloppy, /* Mount options which take numeric value */ Opt_backupuid, Opt_backupgid, Opt_uid, @@ -167,6 +167,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_fsc, "fsc" }, { Opt_mfsymlinks, "mfsymlinks" }, { Opt_multiuser, "multiuser" }, + { Opt_sloppy, "sloppy" }, { Opt_backupuid, "backupuid=%s" }, { Opt_backupgid, "backupgid=%s" }, @@ -1186,6 +1187,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, short int override_gid = -1; bool uid_specified = false; bool gid_specified = false; + bool sloppy = false; + char *invalid = NULL; char *nodename = utsname()->nodename; char *string = NULL; char *tmp_end, *value; @@ -1423,6 +1426,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, case Opt_multiuser: vol->multiuser = true; break; + case Opt_sloppy: + sloppy = true; + break; /* Numeric Values */ case Opt_backupuid: @@ -1870,8 +1876,12 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, goto cifs_parse_mount_err; break; default: - printk(KERN_WARNING "CIFS: Unknown mount option %s\n", - data); + /* + * An option we don't recognize. Save it off for later + * if we haven't already found one + */ + if (!invalid) + invalid = data; break; } /* Free up any allocated string */ @@ -1879,6 +1889,11 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, string = NULL; } + if (!sloppy && invalid) { + printk(KERN_ERR "CIFS: Unknown mount option \"%s\"\n", invalid); + goto cifs_parse_mount_err; + } + #ifndef CONFIG_KEYS /* Muliuser mounts require CONFIG_KEYS support */ if (vol->multiuser) { -- cgit v1.1 From c7ad42b52d866b238ebb311d32ac80338f5c39d4 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 23 Mar 2012 16:30:56 -0500 Subject: [CIFS] Fix trivial sparse warning with asyn i/o patch Signed-off-by: Steve French Reviewed-by: Jeff Layton --- fs/cifs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index daaaca8..460d87b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2114,7 +2114,7 @@ cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata) /* marshal up the pages into iov array */ for (i = 0; i < wdata->nr_pages; i++) { - iov[i + 1].iov_len = min(bytes, PAGE_SIZE); + iov[i + 1].iov_len = min_t(size_t, bytes, PAGE_SIZE); iov[i + 1].iov_base = kmap(wdata->pages[i]); bytes -= iov[i + 1].iov_len; } -- cgit v1.1 From 5cfdddcfc415b0a67be25a0dec28ea9b1a537397 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 27 Mar 2012 20:51:15 +0400 Subject: CIFS: Add missed forcemand mount option The 'forcemand' form of 'forcemandatorylock' mount option was missed when the code moved to use the standard token parser. Return it back. Also fix a comment style in the parser. Signed-off-by: Pavel Shilovsky Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9808154..302a15c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -140,6 +140,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_nobrl, "nobrl" }, { Opt_nobrl, "nolock" }, { Opt_forcemandatorylock, "forcemandatorylock" }, + { Opt_forcemandatorylock, "forcemand" }, { Opt_setuids, "setuids" }, { Opt_nosetuids, "nosetuids" }, { Opt_dynperm, "dynperm" }, @@ -1326,9 +1327,11 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, break; case Opt_nobrl: vol->nobrl = 1; - /* turn off mandatory locking in mode + /* + * turn off mandatory locking in mode * if remote locking is turned off since the - * local vfs will do advisory */ + * local vfs will do advisory + */ if (vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP))) vol->file_mode = S_IALLUGO; -- cgit v1.1 From 867646f246de2dd5c91c480637a4901732ea3151 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 27 Mar 2012 14:55:13 -0500 Subject: [CIFS] Update CIFS version number to 1.77 Signed-off-by: Steve French --- fs/cifs/cifsfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index fe5ecf1..d1389bb 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -125,5 +125,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "1.76" +#define CIFS_VERSION "1.77" #endif /* _CIFSFS_H */ -- cgit v1.1