From 36358c21423d58dde90aedde2b8517192c4092f4 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 2 Jun 2005 20:01:16 -0500 Subject: [CIFS] fs/cifs/netmisc.c: fix sparse warning Signed-off-by: Steve French Signed-off-by: Alexey Dobriyan Signed-off-by: Domen Puncer --- fs/cifs/netmisc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index a92af41d44119a..873b812c0f408e 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -133,7 +133,6 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = { int cifs_inet_pton(int address_family, char *cp,void *dst) { - struct in_addr address; int value; int digit; int i; @@ -190,8 +189,7 @@ cifs_inet_pton(int address_family, char *cp,void *dst) if (value > addr_class_max[end - bytes]) return 0; - address.s_addr = *((__be32 *) bytes) | htonl(value); - *((__be32 *)dst) = address.s_addr; + *((__be32 *)dst) = *((__be32 *) bytes) | htonl(value); return 1; /* success */ } -- cgit 1.2.3-korg From d6e04ae64c6b06ef76a5d4fb49106b393b7fa50a Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 13 Jun 2005 13:24:43 -0500 Subject: [CIFS] CIFS writepage improvements - eliminate double copy Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsproto.h | 6 +- fs/cifs/cifssmb.c | 57 +++++++++----- fs/cifs/file.c | 21 ++++- fs/cifs/transport.c | 223 ++++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 231 insertions(+), 76 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ea239dea571e1f..b43ac9230eabf6 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -47,6 +47,10 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , int * /* bytes returned */ , const int long_op); +extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, + struct smb_hdr * /* input */ , int hdr_len, + const char * /* SMB data to send */ , int data_len, + int * /* bytes returned */ , const int long_op); extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); extern int is_valid_oplock_break(struct smb_hdr *smb); @@ -222,7 +226,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, - const char __user *buf,const int long_op); + const char *buf,const int long_op); extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 * inode_number, const struct nls_table *nls_codepage, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3c628bf667a5f0..b4f7b9859e3bed 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -951,56 +951,69 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, } #ifdef CONFIG_CIFS_EXPERIMENTAL -int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, +int +CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, - const __u64 offset, unsigned int *nbytes, const char __user *buf, + const __u64 offset, unsigned int *nbytes, const char *buf, const int long_op) { int rc = -EACCES; WRITE_REQ *pSMB = NULL; - WRITE_RSP *pSMBr = NULL; - /*int bytes_returned;*/ - unsigned bytes_sent; + int bytes_returned; + int smb_hdr_len; + __u32 bytes_sent; __u16 byte_count; + cERROR(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); - if (rc) return rc; - - pSMBr = (WRITE_RSP *)pSMB; /* BB removeme BB */ - /* tcon and ses pointer are checked in smb_init */ if (tcon->ses->server == NULL) return -ECONNABORTED; - pSMB->AndXCommand = 0xFF; /* none */ + pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); pSMB->OffsetHigh = cpu_to_le32(offset >> 32); pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; - bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF; + + /* Can increase buffer size if buffer is big enough in some cases - ie + can send more if LARGE_WRITE_X capability returned by the server and if + our buffer is big enough or if we convert to iovecs on socket writes + and eliminate the copy to the CIFS buffer */ + if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) { + bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); + } else { + bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) + & ~0xFF; + } + if (bytes_sent > count) bytes_sent = count; - pSMB->DataLengthHigh = 0; pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); - byte_count = bytes_sent + 1 /* pad */ ; - pSMB->DataLengthLow = cpu_to_le16(bytes_sent); - pSMB->DataLengthHigh = 0; - pSMB->hdr.smb_buf_length += byte_count; + byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */ + pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); + pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); + smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ + pSMB->hdr.smb_buf_length += bytes_sent+1; pSMB->ByteCount = cpu_to_le16(byte_count); -/* rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, buf, buflen, &bytes_returned, long_op); */ /* BB fixme BB */ + rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, smb_hdr_len, + buf, bytes_sent, &bytes_returned, long_op); if (rc) { - cFYI(1, ("Send error in write2 (large write) = %d", rc)); + cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; - } else - *nbytes = le16_to_cpu(pSMBr->Count); + } else { + WRITE_RSP * pSMBr = (WRITE_RSP *)pSMB; + *nbytes = le16_to_cpu(pSMBr->CountHigh); + *nbytes = (*nbytes) << 16; + *nbytes += le16_to_cpu(pSMBr->Count); + } cifs_small_buf_release(pSMB); @@ -1009,6 +1022,8 @@ int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, return rc; } + + #endif /* CIFS_EXPERIMENTAL */ int diff --git a/fs/cifs/file.c b/fs/cifs/file.c index dde2d251fc3d6b..ca74c1151be932 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -791,9 +791,8 @@ static ssize_t cifs_write(struct file *file, const char *write_data, pTcon = cifs_sb->tcon; - /* cFYI(1, - (" write %d bytes to offset %lld of %s", write_size, - *poffset, file->f_dentry->d_name.name)); */ + cFYI(1,(" write %d bytes to offset %lld of %s", write_size, + *poffset, file->f_dentry->d_name.name)); /* BB removeme BB */ if (file->private_data == NULL) return -EBADF; @@ -846,7 +845,21 @@ static ssize_t cifs_write(struct file *file, const char *write_data, if (rc != 0) break; } - +#ifdef CIFS_EXPERIMENTAL + /* BB FIXME We can not sign across two buffers yet */ + cERROR(1,("checking signing")); /* BB removeme BB */ + if(pTcon->ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED) == 0) + rc = CIFSSMBWrite2(xid, pTcon, + open_file->netfid, + min_t(const int, cifs_sb->wsize, + write_size - total_written), + *poffset, &bytes_written, + write_data + total_written, + long_op); + } else + /* BB FIXME fixup indentation of line below */ +#endif rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 0046c219833d6c..04f4af07fdd40a 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -49,7 +49,8 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) return NULL; } - temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS); + temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp, + SLAB_KERNEL | SLAB_NOFS); if (temp == NULL) return temp; else { @@ -179,27 +180,24 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, return rc; } -#ifdef CIFS_EXPERIMENTAL -/* BB finish off this function, adding support for writing set of pages as iovec */ -/* and also adding support for operations that need to parse the response smb */ - -int -smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, - unsigned int smb_buf_length, struct kvec * write_vector - /* page list */, struct sockaddr *sin) +#ifdef CONFIG_CIFS_EXPERIMENTAL +static int +smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, + unsigned int smb_hdr_length, const char * data, unsigned int datalen, + struct sockaddr *sin) { int rc = 0; int i = 0; struct msghdr smb_msg; - number_of_pages += 1; /* account for SMB header */ - struct kvec * piov = kmalloc(number_of_pages * sizeof(struct kvec)); - unsigned len = smb_buf_length + 4; - + struct kvec iov[2]; + unsigned len = smb_hdr_length + 4; + if(ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ - iov.iov_base = smb_buffer; - iov.iov_len = len; - + iov[0].iov_base = smb_buffer; + iov[0].iov_len = len; + iov[1].iov_base = data; + iov[2].iov_len = datalen; smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); smb_msg.msg_control = NULL; @@ -212,12 +210,11 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, Flags2 is converted in SendReceive */ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); - cFYI(1, ("Sending smb of length %d ", smb_buf_length)); + cFYI(1, ("Sending smb of length %d ", len + datalen)); dump_smb(smb_buffer, len); - while (len > 0) { - rc = kernel_sendmsg(ssocket, &smb_msg, &iov, number_of_pages, - len); + while (len + datalen > 0) { + rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; if(i > 60) { @@ -232,9 +229,22 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, } if (rc < 0) break; - iov.iov_base += rc; - iov.iov_len -= rc; - len -= rc; + if(iov[0].iov_len > 0) { + if(rc >= len) { + iov[0].iov_len = 0; + rc -= len; + } else { /* some of hdr was not sent */ + len -= rc; + iov[0].iov_len -= rc; + iov[0].iov_base += rc; + continue; + } + } + if((iov[0].iov_len == 0) && (rc > 0)){ + iov[1].iov_base += rc; + iov[1].iov_len -= rc; + datalen -= rc; + } } if (rc < 0) { @@ -246,14 +256,15 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, return rc; } - int -CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, - struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) +SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, + struct smb_hdr *in_buf, int hdrlen, const char * data, + int datalen, int *pbytes_returned, const int long_op) { int rc = 0; - unsigned long timeout = 15 * HZ; - struct mid_q_entry *midQ = NULL; + unsigned int receive_len; + unsigned long timeout; + struct mid_q_entry *midQ; if (ses == NULL) { cERROR(1,("Null smb session")); @@ -263,14 +274,8 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, cERROR(1,("Null tcp session")); return -EIO; } - if(pbytes_returned == NULL) - return -EIO; - else - *pbytes_returned = 0; - - - if(ses->server->tcpStatus == CIFS_EXITING) + if(ses->server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests @@ -282,7 +287,8 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, } else { spin_lock(&GlobalMid_Lock); while(1) { - if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ + if(atomic_read(&ses->server->inFlight) >= + cifs_max_pending){ spin_unlock(&GlobalMid_Lock); wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) @@ -314,17 +320,17 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, if (ses->server->tcpStatus == CifsExiting) { rc = -ENOENT; - goto cifs_out_label; + goto out_unlock2; } else if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1,("tcp session dead - return to caller to retry")); rc = -EAGAIN; - goto cifs_out_label; + goto out_unlock2; } else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) { rc = -EAGAIN; - goto cifs_out_label; + goto out_unlock2; } /* else ok - we are setting up session */ } midQ = AllocMidQEntry(in_buf, ses); @@ -352,13 +358,12 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, return -EIO; } - /* BB can we sign efficiently in this path? */ - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); +/* BB FIXME */ +/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */ midQ->midState = MID_REQUEST_SUBMITTED; -/* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, - piovec, - (struct sockaddr *) &(ses->server->addr.sockAddr));*/ + rc = smb_send2(ses->server->ssocket, in_buf, hdrlen, data, datalen, + (struct sockaddr *) &(ses->server->addr.sockAddr)); if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); @@ -370,19 +375,137 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, return rc; } else up(&ses->server->tcpSem); -cifs_out_label: - if(midQ) - DeleteMidQEntry(midQ); - + if (long_op == -1) + goto cifs_no_response_exit2; + else if (long_op == 2) /* writes past end of file can take loong time */ + timeout = 300 * HZ; + else if (long_op == 1) + timeout = 45 * HZ; /* should be greater than + servers oplock break timeout (about 43 seconds) */ + else if (long_op > 2) { + timeout = MAX_SCHEDULE_TIMEOUT; + } else + timeout = 15 * HZ; + /* wait for 15 seconds or until woken up due to response arriving or + due to last connection to this server being unmounted */ + if (signal_pending(current)) { + /* if signal pending do not hold up user for full smb timeout + but we still give response a change to complete */ + timeout = 2 * HZ; + } + + /* No user interrupts in wait - wreaks havoc with performance */ + if(timeout != MAX_SCHEDULE_TIMEOUT) { + timeout += jiffies; + wait_event(ses->server->response_q, + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || + time_after(jiffies, timeout) || + ((ses->server->tcpStatus != CifsGood) && + (ses->server->tcpStatus != CifsNew))); + } else { + wait_event(ses->server->response_q, + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || + ((ses->server->tcpStatus != CifsGood) && + (ses->server->tcpStatus != CifsNew))); + } + + spin_lock(&GlobalMid_Lock); + if (midQ->resp_buf) { + spin_unlock(&GlobalMid_Lock); + receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); + } else { + cERROR(1,("No response buffer")); + if(midQ->midState == MID_REQUEST_SUBMITTED) { + if(ses->server->tcpStatus == CifsExiting) + rc = -EHOSTDOWN; + else { + ses->server->tcpStatus = CifsNeedReconnect; + midQ->midState = MID_RETRY_NEEDED; + } + } + + if (rc != -EHOSTDOWN) { + if(midQ->midState == MID_RETRY_NEEDED) { + rc = -EAGAIN; + cFYI(1,("marking request for retry")); + } else { + rc = -EIO; + } + } + spin_unlock(&GlobalMid_Lock); + DeleteMidQEntry(midQ); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; + } + + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { + cERROR(1, ("Frame too large received. Length: %d Xid: %d", + receive_len, xid)); + rc = -EIO; + } else { /* rcvd frame is ok */ + + if (midQ->resp_buf && + (midQ->midState == MID_RESPONSE_RECEIVED)) { + in_buf->smb_buf_length = receive_len; + /* BB verify that length would not overrun small buf */ + memcpy((char *)in_buf + 4, + (char *)midQ->resp_buf + 4, + receive_len); + + dump_smb(in_buf, 80); + /* convert the length into a more usable form */ + if((receive_len > 24) && + (ses->server->secMode & (SECMODE_SIGN_REQUIRED | + SECMODE_SIGN_ENABLED))) { + rc = cifs_verify_signature(in_buf, + ses->server->mac_signing_key, + midQ->sequence_number+1); + if(rc) { + cERROR(1,("Unexpected SMB signature")); + /* BB FIXME add code to kill session */ + } + } + + *pbytes_returned = in_buf->smb_buf_length; + + /* BB special case reconnect tid and uid here? */ + rc = map_smb_to_linux_error(in_buf); + + /* convert ByteCount if necessary */ + if (receive_len >= + sizeof (struct smb_hdr) - + 4 /* do not count RFC1001 header */ + + (2 * in_buf->WordCount) + 2 /* bcc */ ) + BCC(in_buf) = le16_to_cpu(BCC(in_buf)); + } else { + rc = -EIO; + cFYI(1,("Bad MID state? ")); + } + } +cifs_no_response_exit2: + DeleteMidQEntry(midQ); + if(long_op < 3) { - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; -} +out_unlock2: + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; +} #endif /* CIFS_EXPERIMENTAL */ int -- cgit 1.2.3-korg From dfb7533b5f157ac7135da23883e80d895227d965 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 22 Jun 2005 17:13:47 -0700 Subject: [CIFS] Add stats for findfirst, findnext, findclose Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsglob.h | 3 +++ fs/cifs/cifssmb.c | 13 ++++++++++++- fs/cifs/readdir.c | 16 ++++++++++------ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 81babab265e1a3..4ed9c13fff5504 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -219,6 +219,9 @@ struct cifsTconInfo { atomic_t num_rmdirs; atomic_t num_renames; atomic_t num_t2renames; + atomic_t num_ffirst; + atomic_t num_fnext; + atomic_t num_fclose; __u64 bytes_read; __u64 bytes_written; spinlock_t stat_lock; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3c628bf667a5f0..b31158a2643dad 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2396,7 +2396,9 @@ findUniqueRetry: if (rc) { cFYI(1, ("Send error in FindFileDirInfo = %d", rc)); } else { /* decode response */ - +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_ffirst); +#endif /* BB fill in */ } @@ -2509,6 +2511,9 @@ findFirstRetry: if (rc == -EAGAIN) goto findFirstRetry; } else { /* decode response */ +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_ffirst); +#endif /* BB remember to free buffer if error BB */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if(rc == 0) { @@ -2622,6 +2627,9 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, } else cFYI(1, ("FindNext returned = %d", rc)); } else { /* decode response */ +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_fnext); +#endif rc = validate_t2((struct smb_t2_rsp *)pSMBr); if(rc == 0) { @@ -2691,6 +2699,9 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle if (rc) { cERROR(1, ("Send error in FindClose = %d", rc)); } +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_fclose); +#endif cifs_small_buf_release(pSMB); /* Since session is dead, search handle closed on server already */ diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 22557716f9afb4..487221eeddb7c1 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -190,8 +190,9 @@ static void fill_in_inode(struct inode *tmp_inode, tmp_inode->i_data.a_ops = &cifs_addr_ops; if(isNewInode) - return; /* No sense invalidating pages for new inode since we - have not started caching readahead file data yet */ + return; /* No sense invalidating pages for new inode + since have not started caching readahead file + data yet */ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && (local_size == tmp_inode->i_size)) { @@ -536,7 +537,8 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, while((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && (rc == 0) && (cifsFile->srch_inf.endOfSearch == FALSE)){ cFYI(1,("calling findnext2")); - rc = CIFSFindNext(xid,pTcon,cifsFile->netfid, &cifsFile->srch_inf); + rc = CIFSFindNext(xid,pTcon,cifsFile->netfid, + &cifsFile->srch_inf); if(rc) return -ENOENT; } @@ -555,7 +557,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); current_entry = cifsFile->srch_inf.srch_entries_start; for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) { - /* go entry to next entry figuring out which we need to start with */ + /* go entry by entry figuring out which is first */ /* if( . or ..) skip */ rc = cifs_entry_is_dot(current_entry,cifsFile); @@ -721,7 +723,8 @@ static int cifs_filldir(char *pfindEntry, struct file *file, (FILE_DIRECTORY_INFO *)pfindEntry,&obj_type, rc); } - rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,tmp_inode->i_ino,obj_type); + rc = filldir(direntry,qstring.name,qstring.len,file->f_pos, + tmp_inode->i_ino,obj_type); if(rc) { cFYI(1,("filldir rc = %d",rc)); } @@ -906,7 +909,8 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifs_save_resume_key(current_entry,cifsFile); break; } else - current_entry = nxt_dir_entry(current_entry,end_of_smb); + current_entry = nxt_dir_entry(current_entry, + end_of_smb); } kfree(tmp_buf); break; -- cgit 1.2.3-korg From ac67055ef2378ea95c34b593ddf9d0a0737a240a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 22 Jun 2005 17:26:35 -0700 Subject: [CIFS] POSIX extensions, SetFSInfo added Signed-off-by: Steve French@sfrench@us.ibm.com Signed-off-by: Jeremy Allison (jra@samba.org) --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsglob.h | 7 +++++ fs/cifs/cifspdu.h | 46 +++++++++++++++++++++++++++++++ fs/cifs/cifsproto.h | 7 +++-- fs/cifs/cifssmb.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++-- fs/cifs/connect.c | 20 ++++++++++++++ fs/cifs/dir.c | 10 +++---- fs/cifs/fcntl.c | 2 +- fs/cifs/file.c | 4 +-- fs/cifs/inode.c | 14 +++++----- fs/cifs/link.c | 15 +++++----- fs/cifs/readdir.c | 4 +-- fs/cifs/xattr.c | 8 +++--- 13 files changed, 182 insertions(+), 33 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index ec00d61d53080b..5dc5fe6b486d30 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -24,6 +24,7 @@ #define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */ #define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */ #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ +#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible. */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4ed9c13fff5504..d3773e57acf99e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -309,6 +309,13 @@ CIFS_SB(struct super_block *sb) return sb->s_fs_info; } +static inline const char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) + return '/'; + else + return '\\'; +} /* one of these for every pending CIFS request to the server */ struct mid_q_entry { diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index aede6a81316794..84d37f8e986e47 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -59,6 +59,7 @@ #define TRANS2_FIND_FIRST 0x01 #define TRANS2_FIND_NEXT 0x02 #define TRANS2_QUERY_FS_INFORMATION 0x03 +#define TRANS2_SET_FS_INFORMATION 0x04 #define TRANS2_QUERY_PATH_INFORMATION 0x05 #define TRANS2_SET_PATH_INFORMATION 0x06 #define TRANS2_QUERY_FILE_INFORMATION 0x07 @@ -1411,6 +1412,43 @@ typedef struct smb_com_transaction_qfsi_rsp { __u8 Pad; /* may be three bytes *//* followed by data area */ } TRANSACTION2_QFSI_RSP; + +/* SETFSInfo Levels */ +#define SMB_SET_CIFS_UNIX_INFO 0x200 +typedef struct smb_com_transaction2_setfsi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 FileNum; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + __le16 ClientUnixMajor; /* Data start. */ + __le16 ClientUnixMinor; + __le64 ClientUnixCap; /* Data end */ +} TRANSACTION2_SETFSI_REQ; + +typedef struct smb_com_transaction2_setfsi_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} TRANSACTION2_SETFSI_RSP; + + typedef struct smb_com_transaction2_get_dfs_refer_req { struct smb_hdr hdr; /* wct = 15 */ __le16 TotalParameterCount; @@ -1551,12 +1589,20 @@ typedef struct { __le16 MinorVersionNumber; __le64 Capability; } FILE_SYSTEM_UNIX_INFO; /* Unix extensions info, level 0x200 */ + +/* Version numbers for CIFS UNIX major and minor. */ +#define CIFS_UNIX_MAJOR_VERSION 1 +#define CIFS_UNIX_MINOR_VERSION 0 + /* Linux/Unix extensions capability flags */ #define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ #define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ #define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ #define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ +#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Use POSIX pathnames on the wire. */ + #define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ + typedef struct { /* For undefined recommended transfer size return -1 in that field */ __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ea239dea571e1f..db2adf0b206c29 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -40,7 +40,7 @@ extern unsigned int _GetXid(void); extern void _FreeXid(unsigned int); #define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__FUNCTION__, xid,current->fsuid)); #define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__FUNCTION__,curr_xid,(int)rc));} -extern char *build_path_from_dentry(struct dentry *); +extern char *build_path_from_dentry(struct dentry *, const struct cifs_sb_info *cifs_sb); extern char *build_wildcard_path_from_dentry(struct dentry *direntry); extern void renew_parental_timestamps(struct dentry *direntry); extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, @@ -89,7 +89,7 @@ extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, const char *searchName, const struct nls_table *nls_codepage, - __u16 *searchHandle, struct cifs_search_info * psrch_inf, int map); + __u16 *searchHandle, struct cifs_search_info * psrch_inf, int map, const char dirsep); extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, __u16 searchHandle, struct cifs_search_info * psrch_inf); @@ -125,6 +125,9 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, int remap); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); +extern int CIFSSMBSETFSUnixInfo(const int xid, struct cifsTconInfo *tcon, + __u64 cap); + extern int CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index b31158a2643dad..81c9d3f393f562 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2416,7 +2416,7 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, const char *searchName, const struct nls_table *nls_codepage, __u16 * pnetfid, - struct cifs_search_info * psrch_inf, int remap) + struct cifs_search_info * psrch_inf, int remap, const char dirsep) { /* level 257 SMB_ */ TRANSACTION2_FFIRST_REQ *pSMB = NULL; @@ -2443,7 +2443,7 @@ findFirstRetry: it got remapped to 0xF03A as if it were part of the directory name instead of a wildcard */ name_len *= 2; - pSMB->FileName[name_len] = '\\'; + pSMB->FileName[name_len] = dirsep; pSMB->FileName[name_len+1] = 0; pSMB->FileName[name_len+2] = '*'; pSMB->FileName[name_len+3] = 0; @@ -2457,7 +2457,7 @@ findFirstRetry: if(name_len > buffersize-header) free buffer exit; BB */ strncpy(pSMB->FileName, searchName, name_len); - pSMB->FileName[name_len] = '\\'; + pSMB->FileName[name_len] = dirsep; pSMB->FileName[name_len+1] = '*'; pSMB->FileName[name_len+2] = 0; name_len += 3; @@ -3265,6 +3265,77 @@ QFSUnixRetry: return rc; } +int +CIFSSMBSETFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap) +{ +/* level 0x200 SMB_SET_CIFS_UNIX_INFO */ + TRANSACTION2_SETFSI_REQ *pSMB = NULL; + TRANSACTION2_SETFSI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cFYI(1, ("In SETFSUnixInfo")); +SETFSUnixRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 4; /* 2 bytes zero followed by info level. */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) - 4; + offset = param_offset + params; + + pSMB->MaxParameterCount = cpu_to_le16(4); + pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */ + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION); + byte_count = 1 /* pad */ + params + 12; + + pSMB->DataCount = cpu_to_le16(12); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + + /* Params. */ + pSMB->FileNum = 0; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO); + + /* Data. */ + pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION); + pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION); + pSMB->ClientUnixCap = cpu_to_le64(cap); + + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cERROR(1, ("Send error in SETFSUnixInfo = %d", rc)); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc) { + rc = -EIO; /* bad smb */ + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SETFSUnixRetry; + + return rc; +} + + int CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon, diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e568cc47a7f930..bef5d6f30975aa 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -74,6 +74,7 @@ struct smb_vol { unsigned server_ino:1; /* use inode numbers from server ie UniqueId */ unsigned direct_io:1; unsigned remap:1; /* set to remap seven reserved chars in filenames */ + unsigned posix_paths:1; /* unset to not ask for posix pathnames. */ unsigned int rsize; unsigned int wsize; unsigned int sockopt; @@ -745,6 +746,9 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */ vol->rw = TRUE; + /* default is always to request posix paths. */ + vol->posix_paths = 1; + if (!options) return 1; @@ -1023,6 +1027,10 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->remap = 1; } else if (strnicmp(data, "nomapchars", 10) == 0) { vol->remap = 0; + } else if (strnicmp(data, "posixpaths", 10) == 0) { + vol->posix_paths = 1; + } else if (strnicmp(data, "noposixpaths", 12) == 0) { + vol->posix_paths = 0; } else if (strnicmp(data, "setuids", 7) == 0) { vol->setuids = 1; } else if (strnicmp(data, "nosetuids", 9) == 0) { @@ -1679,6 +1687,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; if(volume_info.no_xattr) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; + if(volume_info.direct_io) { cERROR(1,("mounting share using direct i/o")); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; @@ -1781,6 +1790,17 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cFYI(1,("server negotiated posix acl support")); sb->s_flags |= MS_POSIXACL; } + + /* Try and negotiate POSIX pathnames if we can. */ + if (volume_info.posix_paths && (CIFS_UNIX_POSIX_PATHNAMES_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + if (!CIFSSMBSETFSUnixInfo(xid, tcon, CIFS_UNIX_POSIX_PATHNAMES_CAP, 0)) { + cFYI(1,("negotiated posix pathnames support")); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; + } else { + cFYI(1,("posix pathnames support requested but not supported")); + } + } } } } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 3f3538d4a1fad1..9360d8fb9ef78f 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -43,7 +43,7 @@ renew_parental_timestamps(struct dentry *direntry) /* Note: caller must free return buffer */ char * -build_path_from_dentry(struct dentry *direntry) +build_path_from_dentry(struct dentry *direntry, const struct cifs_sb_info *cifs_sb) { struct dentry *temp; int namelen = 0; @@ -74,7 +74,7 @@ cifs_bp_rename_retry: if (namelen < 0) { break; } else { - full_path[namelen] = '\\'; + full_path[namelen] = CIFS_DIR_SEP(cifs_sb); strncpy(full_path + namelen + 1, temp->d_name.name, temp->d_name.len); cFYI(0, (" name: %s ", full_path + namelen)); @@ -138,7 +138,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, pTcon = cifs_sb->tcon; down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&direntry->d_sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -299,7 +299,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev pTcon = cifs_sb->tcon; down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&direntry->d_sb->s_vfs_rename_sem); if(full_path == NULL) rc = -ENOMEM; @@ -360,7 +360,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct name /* can not grab the rename sem here since it would deadlock in the cases (beginning of sys_rename itself) in which we already have the sb rename sem */ - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); if(full_path == NULL) { FreeXid(xid); return ERR_PTR(-ENOMEM); diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index 7d2a9202c39a32..d47ce7f49dc313 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -83,7 +83,7 @@ int cifs_dir_notify(struct file * file, unsigned long arg) pTcon = cifs_sb->tcon; down(&file->f_dentry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(file->f_dentry); + full_path = build_path_from_dentry(file->f_dentry, cifs_sb); up(&file->f_dentry->d_sb->s_vfs_rename_sem); if(full_path == NULL) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 30ab70ce554716..8dd11fecaaca34 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -196,7 +196,7 @@ int cifs_open(struct inode *inode, struct file *file) } down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(file->f_dentry); + full_path = build_path_from_dentry(file->f_dentry, cifs_sb); up(&inode->i_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); @@ -359,7 +359,7 @@ static int cifs_reopen_file(struct inode *inode, struct file *file, those that already have the rename sem can end up causing writepage to get called and if the server was down that means we end up here, and we can never tell if the caller already has the rename_sem */ - full_path = build_path_from_dentry(file->f_dentry); + full_path = build_path_from_dentry(file->f_dentry, cifs_sb); if (full_path == NULL) { up(&pCifsFile->fh_sem); FreeXid(xid); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 8d336a90025584..95354da606d65a 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -412,7 +412,7 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) /* Unlink can be called from rename so we can not grab the sem here since we deadlock otherwise */ /* down(&direntry->d_sb->s_vfs_rename_sem);*/ - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); /* up(&direntry->d_sb->s_vfs_rename_sem);*/ if (full_path == NULL) { FreeXid(xid); @@ -556,7 +556,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) pTcon = cifs_sb->tcon; down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&inode->i_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); @@ -627,7 +627,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) pTcon = cifs_sb->tcon; down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&inode->i_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); @@ -680,8 +680,8 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, /* we already have the rename sem so we do not need to grab it again here to protect the path integrity */ - fromName = build_path_from_dentry(source_direntry); - toName = build_path_from_dentry(target_direntry); + fromName = build_path_from_dentry(source_direntry, cifs_sb_source); + toName = build_path_from_dentry(target_direntry, cifs_sb_target); if ((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; goto cifs_rename_exit; @@ -797,7 +797,7 @@ int cifs_revalidate(struct dentry *direntry) /* can not safely grab the rename sem here if rename calls revalidate since that would deadlock */ - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -946,7 +946,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) pTcon = cifs_sb->tcon; down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&direntry->d_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index bde0fabfece0ae..214aa816f66919 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -49,8 +49,8 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, BB note DFS case in future though (when we may have to check) */ down(&inode->i_sb->s_vfs_rename_sem); - fromName = build_path_from_dentry(old_file); - toName = build_path_from_dentry(direntry); + fromName = build_path_from_dentry(old_file, cifs_sb_target); + toName = build_path_from_dentry(direntry, cifs_sb_target); up(&inode->i_sb->s_vfs_rename_sem); if((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; @@ -105,16 +105,17 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) xid = GetXid(); + cifs_sb = CIFS_SB(inode->i_sb); + pTcon = cifs_sb->tcon; + down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&direntry->d_sb->s_vfs_rename_sem); if (!full_path) goto out_no_free; cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode)); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; target_path = kmalloc(PATH_MAX, GFP_KERNEL); if (!target_path) { target_path = ERR_PTR(-ENOMEM); @@ -167,7 +168,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) pTcon = cifs_sb->tcon; down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&inode->i_sb->s_vfs_rename_sem); if(full_path == NULL) { @@ -233,7 +234,7 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) /* BB would it be safe against deadlock to grab this sem even though rename itself grabs the sem and calls lookup? */ /* down(&inode->i_sb->s_vfs_rename_sem);*/ - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); /* up(&inode->i_sb->s_vfs_rename_sem);*/ if(full_path == NULL) { diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 487221eeddb7c1..42310281871c2b 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -354,7 +354,7 @@ static int initiate_cifs_search(const int xid, struct file *file) return -EINVAL; down(&file->f_dentry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(file->f_dentry); + full_path = build_path_from_dentry(file->f_dentry, cifs_sb); up(&file->f_dentry->d_sb->s_vfs_rename_sem); if(full_path == NULL) { @@ -375,7 +375,7 @@ ffirst_retry: rc = CIFSFindFirst(xid, pTcon,full_path,cifs_sb->local_nls, &cifsFile->netfid, &cifsFile->srch_inf, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); if(rc == 0) cifsFile->invalidHandle = FALSE; if((rc == -EOPNOTSUPP) && diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index c1e02eff1d25a2..f4fc8ddebba75d 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -63,7 +63,7 @@ int cifs_removexattr(struct dentry * direntry, const char * ea_name) pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -118,7 +118,7 @@ int cifs_setxattr(struct dentry * direntry, const char * ea_name, pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -227,7 +227,7 @@ ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -328,7 +328,7 @@ ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size) pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(direntry, cifs_sb); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); -- cgit 1.2.3-korg From 45abc6ee2b916a235d6824a41225177bd6e5e24f Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 23 Jun 2005 13:42:03 -0500 Subject: [CIFS] Fix typo in POSIX SetFSInfo call Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 2 +- fs/cifs/connect.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index c7b220206ce026..66eaa6b4037316 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -129,7 +129,7 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, int remap); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); -extern int CIFSSMBSETFSUnixInfo(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap); extern int CIFSSMBQFSAttributeInfo(const int xid, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 7d14f24148120e..f3dfae7788fd02 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3281,7 +3281,7 @@ QFSUnixRetry: } int -CIFSSMBSETFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap) +CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap) { /* level 0x200 SMB_SET_CIFS_UNIX_INFO */ TRANSACTION2_SETFSI_REQ *pSMB = NULL; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index bef5d6f30975aa..f6d2a7974fc13f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1794,7 +1794,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* Try and negotiate POSIX pathnames if we can. */ if (volume_info.posix_paths && (CIFS_UNIX_POSIX_PATHNAMES_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { - if (!CIFSSMBSETFSUnixInfo(xid, tcon, CIFS_UNIX_POSIX_PATHNAMES_CAP, 0)) { + if (!CIFSSMBSetFSUnixInfo(xid, tcon, CIFS_UNIX_POSIX_PATHNAMES_CAP)) { cFYI(1,("negotiated posix pathnames support")); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; } else { -- cgit 1.2.3-korg From 0c0ff09329dafb165c0d9ac08965ddc0615020b1 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 23 Jun 2005 19:31:17 -0500 Subject: [CIFS] Performance improvement, finish up adding CIFSSMBWrite2 Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifs_debug.c | 10 ++++++++-- fs/cifs/cifssmb.c | 2 +- fs/cifs/file.c | 7 +++---- fs/cifs/transport.c | 6 ++++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 4061e43471c1a1..83817132807696 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -283,6 +283,12 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset, atomic_read(&tcon->num_t2renames)); buf += item_length; length += item_length; + item_length = sprintf(buf,"\nFindFirst: %d FNext %d FClose %d", + atomic_read(&tcon->num_ffirst), + atomic_read(&tcon->num_fnext), + atomic_read(&tcon->num_fclose)); + buf += item_length; + length += item_length; } read_unlock(&GlobalSMBSeslock); @@ -360,7 +366,7 @@ cifs_proc_init(void) if (pde) pde->write_proc = oplockEnabled_write; - pde = create_proc_read_entry("ReenableOldCifsReaddirCode", 0, proc_fs_cifs, + pde = create_proc_read_entry("Experimental", 0, proc_fs_cifs, quotaEnabled_read, NULL); if (pde) pde->write_proc = quotaEnabled_write; @@ -419,7 +425,7 @@ cifs_proc_clean(void) remove_proc_entry("ExtendedSecurity",proc_fs_cifs); remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs); - remove_proc_entry("ReenableOldCifsReaddirCode",proc_fs_cifs); + remove_proc_entry("Experimental",proc_fs_cifs); remove_proc_entry("LookupCacheEnabled",proc_fs_cifs); remove_proc_entry("cifs", proc_root_fs); } diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f3dfae7788fd02..56d79fd9067915 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -964,7 +964,7 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, __u32 bytes_sent; __u16 byte_count; - cERROR(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ + cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); if (rc) return rc; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index f55c0c7aeeb0b7..ddb25a0a63d5bc 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -845,11 +845,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data, if (rc != 0) break; } -#ifdef CIFS_EXPERIMENTAL +#ifdef CONFIG_CIFS_EXPERIMENTAL /* BB FIXME We can not sign across two buffers yet */ - cERROR(1,("checking signing")); /* BB removeme BB */ - if(pTcon->ses->server->secMode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED) == 0) + if((experimEnabled) && ((pTcon->ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) { rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 04f4af07fdd40a..496a2738bbe3d5 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -197,7 +197,7 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, iov[0].iov_base = smb_buffer; iov[0].iov_len = len; iov[1].iov_base = data; - iov[2].iov_len = datalen; + iov[1].iov_len = datalen; smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); smb_msg.msg_control = NULL; @@ -210,7 +210,8 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, Flags2 is converted in SendReceive */ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); - cFYI(1, ("Sending smb of length %d ", len + datalen)); + cFYI(1, ("Sending smb: hdrlen %d datalen %d", + smb_hdr_length,datalen)); dump_smb(smb_buffer, len); while (len + datalen > 0) { @@ -233,6 +234,7 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, if(rc >= len) { iov[0].iov_len = 0; rc -= len; + len = 0; } else { /* some of hdr was not sent */ len -= rc; iov[0].iov_len -= rc; -- cgit 1.2.3-korg From d7245c2ccf14cde2023273c1ec246732d96e2c27 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 14 Jul 2005 18:25:12 -0500 Subject: [CIFS] Add compat with SFU (part 1) This should help the case of creating fifos and other special files to servers which do not support the Unix extensions. Signed-off-by: Steve French (sfrench@us.ibm.com) Thanks to Martin Koeppe for his suggestions and good analysis --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/connect.c | 7 +++++++ fs/cifs/dir.c | 12 +++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 5dc5fe6b486d30..6b93587c490e53 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -25,6 +25,7 @@ #define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */ #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ #define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible. */ +#define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f6d2a7974fc13f..36f78596c81a8a 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -75,6 +75,7 @@ struct smb_vol { unsigned direct_io:1; unsigned remap:1; /* set to remap seven reserved chars in filenames */ unsigned posix_paths:1; /* unset to not ask for posix pathnames. */ + unsigned sfu_emul:1; unsigned int rsize; unsigned int wsize; unsigned int sockopt; @@ -1027,6 +1028,10 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->remap = 1; } else if (strnicmp(data, "nomapchars", 10) == 0) { vol->remap = 0; + } else if (strnicmp(data, "sfu", 3) == 0) { + vol->sfu_emul = 1; + } else if (strnicmp(data, "nosfu", 5) == 0) { + vol->sfu_emul = 0; } else if (strnicmp(data, "posixpaths", 10) == 0) { vol->posix_paths = 1; } else if (strnicmp(data, "noposixpaths", 12) == 0) { @@ -1687,6 +1692,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; if(volume_info.no_xattr) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; + if(volume_info.sfu_emul) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; if(volume_info.direct_io) { cERROR(1,("mounting share using direct i/o")); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 9360d8fb9ef78f..0d5e27fec92b00 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -209,7 +209,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, CIFS_MOUNT_MAP_SPECIAL_CHR); } else { - /* BB implement via Windows security descriptors */ + /* BB implement mode setting via Windows security descriptors */ /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ /* could set r/o dos attribute if mode & 0222 == 0 */ } @@ -326,6 +326,16 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev if(rc == 0) d_instantiate(direntry, newinode); } + } else { + if((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (special_file(mode))) { + + cFYI(1,("sfu compat create special file")); + /* Attributes = cpu_to_le32(ATTR_SYSTEM); + rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, ...); */ + + /* add code here to set EAs */ + } } kfree(full_path); -- cgit 1.2.3-korg From f4cfd69cf349dd27e00d5cf804b57aee04e059c2 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 14 Jul 2005 18:29:02 -0500 Subject: [CIFS] Fix path name conversion for long filenames when mapchars mount option was specified at mount time. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 6 ++++++ fs/cifs/misc.c | 1 + 2 files changed, 7 insertions(+) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index dab4774ee7bbb6..3196d4c4eed36f 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,9 @@ +Version 1.35 +------------ +Add writepage performance improvements. Fix path name conversions +for long filenames on mounts which were done with "mapchars" mount option +specified. + Version 1.34 ------------ Fix error mapping of the TOO_MANY_LINKS (hardlinks) case. diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 072b4ee8c53e1a..20ae4153f79167 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -611,6 +611,7 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen, src_char = source[i]; switch (src_char) { case 0: + target[j] = 0; goto ctoUCS_out; case ':': target[j] = cpu_to_le16(UNI_COLON); -- cgit 1.2.3-korg From eda3c029899cbf435d76fea43b7e1404439ccec9 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 21 Jul 2005 15:20:28 -0700 Subject: [CIFS] Add compat with SFU (part 2) Creating FIFOs to non-Unix servers (with cifs mounts for which sfu option was specified) now works. Signed-off-by: Steve French (sfrench@us.ibm.com) Thanks to Martin Koeppe for his assistance --- fs/cifs/cifspdu.h | 10 +++++++++- fs/cifs/cifssmb.c | 10 ++++++++-- fs/cifs/dir.c | 34 ++++++++++++++++++++++++++++++---- fs/cifs/inode.c | 10 ++++++++++ fs/cifs/readdir.c | 7 +++++++ 5 files changed, 64 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 84d37f8e986e47..3cef57b7a34f38 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -268,10 +268,18 @@ /* CreateOptions */ #define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ #define CREATE_WRITE_THROUGH 0x00000002 -#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ +#define CREATE_SEQUENTIAL 0x00000004 +#define CREATE_SYNC_ALERT 0x00000010 +#define CREATE_ASYNC_ALERT 0x00000020 +#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ +#define CREATE_NO_EA_KNOWLEDGE 0x00000200 +#define CREATE_EIGHT_DOT_THREE 0x00000400 #define CREATE_RANDOM_ACCESS 0x00000800 #define CREATE_DELETE_ON_CLOSE 0x00001000 +#define CREATE_OPEN_BY_ID 0x00002000 #define OPEN_REPARSE_POINT 0x00200000 +#define CREATE_OPTIONS_MASK 0x007FFFFF +#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ /* ImpersonationLevel flags */ #define SECURITY_ANONYMOUS 0 diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 56d79fd9067915..fbe651858c886c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -738,7 +738,13 @@ openRetry: } pSMB->DesiredAccess = cpu_to_le32(access_flags); pSMB->AllocationSize = 0; - pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); + /* set file as system file if special file such + as fifo and server expecting SFU style and + no Unix extensions */ + if(create_options & CREATE_OPTION_SPECIAL) + pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); + else + pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); /* XP does not handle ATTR_POSIX_SEMANTICS */ /* but it helps speed up case sensitive checks for other servers such as Samba */ @@ -752,7 +758,7 @@ openRetry: being created */ pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); pSMB->CreateDisposition = cpu_to_le32(openDisposition); - pSMB->CreateOptions = cpu_to_le32(create_options); + pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); /* BB Expirement with various impersonation levels and verify */ pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); pSMB->SecurityFlags = diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 0d5e27fec92b00..c0f20fc0929037 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -327,13 +327,39 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev d_instantiate(direntry, newinode); } } else { - if((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && - (special_file(mode))) { + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { + int oplock = 0; + u16 fileHandle; + FILE_ALL_INFO * buf; cFYI(1,("sfu compat create special file")); - /* Attributes = cpu_to_le32(ATTR_SYSTEM); - rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, ...); */ + buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL); + if(buf == NULL) { + kfree(full_path); + FreeXid(xid); + return -ENOMEM; + } + + rc = CIFSSMBOpen(xid, pTcon, full_path, + FILE_CREATE, /* fail if exists */ + GENERIC_WRITE /* BB would + WRITE_OWNER | WRITE_DAC be better? */, + /* Create a file and set the + file attribute to SYSTEM */ + CREATE_NOT_DIR | CREATE_OPTION_SPECIAL, + &fileHandle, &oplock, buf, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + + if(!rc) { + /* BB Do not bother to decode buf since no + local inode yet to put timestamps in */ + CIFSSMBClose(xid, pTcon, fileHandle); + d_drop(direntry); + } + kfree(buf); /* add code here to set EAs */ } } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 95354da606d65a..628aa1a9fe6439 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -320,6 +320,16 @@ int cifs_get_inode_info(struct inode **pinode, on dirs */ inode->i_mode = cifs_sb->mnt_dir_mode; inode->i_mode |= S_IFDIR; + } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (cifsInfo->cifsAttrs & ATTR_SYSTEM) && + /* No need to le64 convert size of zero */ + (pfindData->EndOfFile == 0)) { + inode->i_mode = cifs_sb->mnt_file_mode; + inode->i_mode |= S_IFIFO; +/* BB Finish for SFU style symlinks and devies */ +/* } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (cifsInfo->cifsAttrs & ATTR_SYSTEM) && ) */ + } else { inode->i_mode |= S_IFREG; /* treat the dos attribute of read-only as read-only diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 42310281871c2b..dec3c9dd04d7e1 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -148,6 +148,13 @@ static void fill_in_inode(struct inode *tmp_inode, tmp_inode->i_mode = cifs_sb->mnt_dir_mode; } tmp_inode->i_mode |= S_IFDIR; + } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (attr & ATTR_SYSTEM) && (end_of_file == 0)) { + *pobject_type = DT_FIFO; + tmp_inode->i_mode |= S_IFIFO; +/* BB Finish for SFU style symlinks and devies */ +/* } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (attr & ATTR_SYSTEM) && ) { */ /* we no longer mark these because we could not follow them */ /* } else if (attr & ATTR_REPARSE) { *pobject_type = DT_LNK; -- cgit 1.2.3-korg From ef6724e32142c2d9ca252d423cacc435c142734e Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 2 Aug 2005 21:31:05 -0700 Subject: [CIFS] Fix missing entries in search results when very long file names and more than 50 (or so) of such long search entries in the directory. FindNext could send corrupt last byte of resume name when resume key was a few hundred bytes long file name or longer. Fixes Samba Bug # 2932 Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifssmb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index fbe651858c886c..e555cb5cf49376 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2628,6 +2628,9 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, if(name_len < PATH_MAX) { memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); byte_count += name_len; + /* 14 byte parm len above enough for 2 byte null terminator */ + pSMB->ResumeFileName[name_len] = 0; + pSMB->ResumeFileName[name_len+1] = 0; } else { rc = -EINVAL; goto FNext2_err_exit; -- cgit 1.2.3-korg From 4a6d87f1db06c9670251d6c72a89319e7d1cbaee Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 13 Aug 2005 08:15:54 -0700 Subject: [CIFS] Add missing check for path name allocation failure. Remove four redundant null pointer checks before cifs_buf_release. Found by coverity analyzer. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifssmb.c | 24 +++++++++++------------- fs/cifs/dir.c | 6 +++--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index e555cb5cf49376..459320222cf7d5 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -415,15 +415,16 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) if(server->secMode & SECMODE_SIGN_REQUIRED) cERROR(1, ("Server requires /proc/fs/cifs/PacketSigningEnabled")); - server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + server->secMode &= ~(SECMODE_SIGN_ENABLED | + SECMODE_SIGN_REQUIRED); } else if(sign_CIFS_PDUs == 1) { if((server->secMode & SECMODE_SIGN_REQUIRED) == 0) - server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + server->secMode &= ~(SECMODE_SIGN_ENABLED | + SECMODE_SIGN_REQUIRED); } } - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); return rc; } @@ -537,9 +538,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) rc = -ESHUTDOWN; } } - if (pSMB) - cifs_small_buf_release(pSMB); - up(&ses->sesSem); + up(&ses->sesSem) + cifs_small_buf_release(pSMB); /* if session dead then we do not need to do ulogoff, since server closed smb session, no sense reporting @@ -1796,8 +1796,7 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, } } qreparse_out: - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -2520,12 +2519,11 @@ findFirstRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ + if (rc) {/* BB add logic to retry regular search if Unix search + rejected unexpectedly by server */ /* BB Add code to handle unsupported level rc */ cFYI(1, ("Error in FindFirst = %d", rc)); - - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); /* BB eventually could optimize out free and realloc of buf */ /* for this case */ diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index c0f20fc0929037..c619d45060ce0d 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -226,7 +226,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, } if (rc != 0) { - cFYI(1,("Create worked but get_inode_info failed with rc = %d", + cFYI(1, + ("Create worked but get_inode_info failed rc = %d", rc)); } else { direntry->d_op = &cifs_dentry_ops; @@ -303,8 +304,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev up(&direntry->d_sb->s_vfs_rename_sem); if(full_path == NULL) rc = -ENOMEM; - - if (full_path && (pTcon->ses->capabilities & CAP_UNIX)) { + else if (pTcon->ses->capabilities & CAP_UNIX) { if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,(__u64)current->euid,(__u64)current->egid, -- cgit 1.2.3-korg From a59c658607b63ec7b6c2536597a075ee307b1b4c Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 17 Aug 2005 12:12:19 -0700 Subject: [CIFS] Missing ; from previous fix. Pointed out by Shaggy. Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 459320222cf7d5..1b073546f5d901 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -538,7 +538,7 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) rc = -ESHUTDOWN; } } - up(&ses->sesSem) + up(&ses->sesSem); cifs_small_buf_release(pSMB); /* if session dead then we do not need to do ulogoff, -- cgit 1.2.3-korg From 1982c344f1bf08118f7c224958b30c64e162009e Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 17 Aug 2005 12:38:22 -0700 Subject: [CIFS] Ensure that cifs multiplex ids do not collide. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsglob.h | 1 + fs/cifs/cifspdu.h | 19 ++++++------ fs/cifs/cifsproto.h | 6 ++-- fs/cifs/cifssmb.c | 23 +++++++++------ fs/cifs/connect.c | 9 ++++++ fs/cifs/misc.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 113 insertions(+), 29 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d3773e57acf99e..e8287f76484f13 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -147,6 +147,7 @@ struct TCP_Server_Info { /* (returned on Negotiate */ int capabilities; /* allow selective disabling of caps by smb sess */ __u16 timeZone; + __u16 CurrentMid; /* multiplex id - rotating counter */ char cryptKey[CIFS_CRYPTO_KEY_SIZE]; char workstation_RFC1001_name[16]; /* 16th byte is always zero */ __u32 sequence_number; /* needed for CIFS PDU signature */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 3cef57b7a34f38..49cc66825309ea 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1961,18 +1961,17 @@ struct data_blob { perhaps add a CreateDevice - to create Pipes and other special .inodes Also note POSIX open flags 2) Close - to return the last write time to do cache across close more safely - 3) PosixQFSInfo - to return statfs info - 4) FindFirst return unique inode number - what about resume key, two forms short (matches readdir) and full (enough info to cache inodes) - 5) Mkdir - set mode + 3) FindFirst return unique inode number - what about resume key, two + forms short (matches readdir) and full (enough info to cache inodes) + 4) Mkdir - set mode And under consideration: - 6) FindClose2 (return nanosecond timestamp ??) - 7) Use nanosecond timestamps throughout all time fields if + 5) FindClose2 (return nanosecond timestamp ??) + 6) Use nanosecond timestamps throughout all time fields if corresponding attribute flag is set - 8) sendfile - handle based copy - 9) Direct i/o - 10) "POSIX ACL" support - 11) Misc fcntls? + 7) sendfile - handle based copy + 8) Direct i/o + 9) Misc fcntls? what about fixing 64 bit alignment @@ -2028,7 +2027,7 @@ struct data_blob { */ -/* xsymlink is a symlink format that can be used +/* xsymlink is a symlink format (used by MacOS) that can be used to save symlink info in a regular file when mounted to operating systems that do not support the cifs Unix extensions or EAs (for xattr diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 66eaa6b4037316..b9b13e3fe79d1c 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -61,9 +61,9 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length, extern int cifs_inet_pton(int, char * source, void *dst); extern int map_smb_to_linux_error(struct smb_hdr *smb); extern void header_assemble(struct smb_hdr *, char /* command */ , - const struct cifsTconInfo *, int /* specifies length - of fixed section (word count) in two byte units */ - ); + const struct cifsTconInfo *, int /* length of + fixed section (word count) in two byte units */); +extern __u16 GetNextMid(struct TCP_Server_Info *server); extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16, struct cifsTconInfo *); extern void DeleteOplockQEntry(struct oplock_q_entry *); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 1b073546f5d901..930be0927de221 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -330,7 +330,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; - + pSMB->hdr.Mid = GetNextMid(server); pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; if (extended_security) pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; @@ -415,15 +415,14 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) if(server->secMode & SECMODE_SIGN_REQUIRED) cERROR(1, ("Server requires /proc/fs/cifs/PacketSigningEnabled")); - server->secMode &= ~(SECMODE_SIGN_ENABLED | - SECMODE_SIGN_REQUIRED); + server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } else if(sign_CIFS_PDUs == 1) { if((server->secMode & SECMODE_SIGN_REQUIRED) == 0) - server->secMode &= ~(SECMODE_SIGN_ENABLED | - SECMODE_SIGN_REQUIRED); + server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } } + cifs_buf_release(pSMB); return rc; } @@ -519,6 +518,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */ if(ses->server) { + pSMB->hdr.Mid = GetNextMid(ses->server); + if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; @@ -2519,11 +2520,12 @@ findFirstRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) {/* BB add logic to retry regular search if Unix search - rejected unexpectedly by server */ + if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ /* BB Add code to handle unsupported level rc */ cFYI(1, ("Error in FindFirst = %d", rc)); - cifs_buf_release(pSMB); + + if (pSMB) + cifs_buf_release(pSMB); /* BB eventually could optimize out free and realloc of buf */ /* for this case */ @@ -2857,7 +2859,10 @@ getDFSRetry: (void **) &pSMBr); if (rc) return rc; - + + /* server pointer checked in called function, + but should never be null here anyway */ + pSMB->hdr.Mid = GetNextMid(ses->server); pSMB->hdr.Tid = ses->ipc_tid; pSMB->hdr.Uid = ses->Suid; if (ses->capabilities & CAP_STATUS32) { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 36f78596c81a8a..9e8256003f7315 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1857,6 +1857,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 13 /* wct */ ); + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req_no_secext.AndXCommand = 0xFF; pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq); @@ -2132,6 +2133,8 @@ CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.AndXCommand = 0xFF; pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); @@ -2398,6 +2401,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); @@ -2740,6 +2745,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.AndXCommand = 0xFF; @@ -3111,6 +3118,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, NULL /*no tid */ , 4 /*wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); smb_buffer->Uid = ses->Suid; pSMB = (TCONX_REQ *) smb_buffer; pSMBr = (TCONX_RSP *) smb_buffer_response; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 20ae4153f79167..beeff82841696c 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -34,8 +34,6 @@ extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern struct task_struct * oplockThread; -static __u16 GlobalMid; /* multiplex id - rotating counter */ - /* The xid serves as a useful identifier for each incoming vfs request, in a similar way to the mid which is useful to track each sent smb, and CurrentXid can also provide a running counter (although it @@ -51,6 +49,8 @@ _GetXid(void) GlobalTotalActiveXid++; if (GlobalTotalActiveXid > GlobalMaxActiveXid) GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */ + if(GlobalTotalActiveXid > 65000) + cFYI(1,("warning: more than 65000 requests active")); xid = GlobalCurrentXid++; spin_unlock(&GlobalMid_Lock); return xid; @@ -218,6 +218,76 @@ cifs_small_buf_release(void *buf_to_free) return; } +/* + 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) +{ + __u16 mid = 0; + __u16 last_mid; + int collision; + + if(server == NULL) + return mid; + + 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) { + struct list_head *tmp; + struct mid_q_entry *mid_entry; + + collision = 0; + if(server->CurrentMid == 0) + server->CurrentMid++; + + list_for_each(tmp, &server->pending_mid_q) { + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + + if ((mid_entry->mid == server->CurrentMid) && + (mid_entry->midState == MID_REQUEST_SUBMITTED)) { + /* This mid is in use, try a different one */ + collision = 1; + break; + } + } + if(collision == 0) { + mid = server->CurrentMid; + break; + } + server->CurrentMid++; + } + spin_unlock(&GlobalMid_Lock); + return mid; +} + +/* NB: MID can not be set if treeCon not passed in, in that + case it is responsbility of caller to set the mid */ void header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , const struct cifsTconInfo *treeCon, int word_count @@ -233,7 +303,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , (2 * word_count) + sizeof (struct smb_hdr) - 4 /* RFC 1001 length field does not count */ + 2 /* for bcc field itself */ ; - /* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */ + /* Note that this is the only network field that has to be converted + to big endian and it is done just before we send it */ buffer->Protocol[0] = 0xFF; buffer->Protocol[1] = 'S'; @@ -245,8 +316,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , buffer->Pid = cpu_to_le16((__u16)current->tgid); buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); spin_lock(&GlobalMid_Lock); - GlobalMid++; - buffer->Mid = GlobalMid; spin_unlock(&GlobalMid_Lock); if (treeCon) { buffer->Tid = treeCon->tid; @@ -256,8 +325,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , if (treeCon->ses->capabilities & CAP_STATUS32) { buffer->Flags2 |= SMBFLG2_ERR_STATUS; } - - buffer->Uid = treeCon->ses->Suid; /* always in LE format */ + /* Uid is not converted */ + buffer->Uid = treeCon->ses->Suid; + buffer->Mid = GetNextMid(treeCon->ses->server); if(multiuser_mount != 0) { /* For the multiuser case, there are few obvious technically */ /* possible mechanisms to match the local linux user (uid) */ -- cgit 1.2.3-korg From f191401f5906f4d942fac87ebeb4671faf1ba7d6 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 18 Aug 2005 09:37:34 -0700 Subject: [CIFS] rmmod cifs can oops if done soon after the last cifs unmount Signed-off-by: Shaggy (shaggy@austin.ibm.com) Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsfs.c | 2 +- fs/cifs/connect.c | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8cc23e7d0d5d34..7fda0f7d9c004d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -834,8 +834,8 @@ static int cifs_oplock_thread(void * dummyarg) spin_unlock(&GlobalMid_Lock); } } while(!signal_pending(current)); - complete_and_exit (&cifs_oplock_exited, 0); oplockThread = NULL; + complete_and_exit (&cifs_oplock_exited, 0); } static int __init diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9e8256003f7315..ef0432c4448226 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "cifspdu.h" @@ -44,6 +45,8 @@ #define CIFS_PORT 445 #define RFC1001_PORT 139 +static DECLARE_COMPLETION(cifsd_complete); + extern void SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, @@ -339,6 +342,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) atomic_inc(&tcpSesAllocCount); length = tcpSesAllocCount.counter; write_unlock(&GlobalSMBSeslock); + complete(&cifsd_complete); if(length > 1) { mempool_resize(cifs_req_poolp, length + cifs_min_rcv, @@ -676,7 +680,7 @@ multi_t2_fnd: msleep(125); } - if (list_empty(&server->pending_mid_q)) { + if (!list_empty(&server->pending_mid_q)) { /* mpx threads have not exited yet give them at least the smb send timeout time for long ops */ /* due to delays on oplock break requests, we need @@ -713,7 +717,7 @@ multi_t2_fnd: GFP_KERNEL); } - msleep(250); + complete_and_exit(&cifsd_complete, 0); return 0; } @@ -1617,8 +1621,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, kfree(volume_info.password); FreeXid(xid); return rc; - } else - rc = 0; + } + wait_for_completion(&cifsd_complete); + rc = 0; memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16); srvTcp->sequence_number = 0; } @@ -1759,8 +1764,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, spin_lock(&GlobalMid_Lock); srvTcp->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); - if(srvTcp->tsk) + if(srvTcp->tsk) { send_sig(SIGKILL,srvTcp->tsk,1); + wait_for_completion(&cifsd_complete); + } } /* If find_unc succeeded then rc == 0 so we can not end */ if (tcon) /* up accidently freeing someone elses tcon struct */ @@ -1773,8 +1780,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, temp_rc = CIFSSMBLogoff(xid, pSesInfo); /* if the socketUseCount is now zero */ if((temp_rc == -ESHUTDOWN) && - (pSesInfo->server->tsk)) + (pSesInfo->server->tsk)) { send_sig(SIGKILL,pSesInfo->server->tsk,1); + wait_for_completion(&cifsd_complete); + } } else cFYI(1, ("No session or bad tcon")); sesInfoFree(pSesInfo); @@ -3241,8 +3250,10 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) return 0; } else if (rc == -ESHUTDOWN) { cFYI(1,("Waking up socket by sending it signal")); - if(cifsd_task) + if(cifsd_task) { send_sig(SIGKILL,cifsd_task,1); + wait_for_completion(&cifsd_complete); + } rc = 0; } /* else - we have an smb session left on this socket do not kill cifsd */ -- cgit 1.2.3-korg From 8d0d50948b276b46b75b1b5855d3f9fab1e0fd92 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 18 Aug 2005 09:41:43 -0700 Subject: [CIFS] Change Notify support part 1 - add dnotify thread for processing notify responses. Signed-off-by: Asser Ferno Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 7fda0f7d9c004d..d77abe236a670b 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -59,6 +59,8 @@ unsigned int ntlmv2_support = 0; unsigned int sign_CIFS_PDUs = 1; extern struct task_struct * oplockThread; /* remove sparse warning */ struct task_struct * oplockThread = NULL; +extern struct task_struct * dnotifyThread; /* remove sparse warning */ +struct task_struct * dnotifyThread = NULL; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; module_param(CIFSMaxBufSize, int, 0); MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048"); @@ -73,6 +75,7 @@ module_param(cifs_max_pending, int, 0); MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256"); static DECLARE_COMPLETION(cifs_oplock_exited); +static DECLARE_COMPLETION(cifs_dnotify_exited); extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; @@ -838,6 +841,19 @@ static int cifs_oplock_thread(void * dummyarg) complete_and_exit (&cifs_oplock_exited, 0); } +static int cifs_dnotify_thread(void * dummyarg) +{ + daemonize("cifsdnotifyd"); + allow_signal(SIGTERM); + + dnotifyThread = current; + do { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(39*HZ); + } while(!signal_pending(current)); + complete_and_exit (&cifs_dnotify_exited, 0); +} + static int __init init_cifs(void) { @@ -884,10 +900,16 @@ init_cifs(void) if (!rc) { rc = (int)kernel_thread(cifs_oplock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_VM); - if(rc > 0) - return 0; - else + if(rc > 0) { + rc = (int)kernel_thread(cifs_dnotify_thread, NULL, + CLONE_FS | CLONE_FILES | CLONE_VM); + if(rc > 0) + return 0; + else + cERROR(1,("error %d create dnotify thread", rc)); + } else { cERROR(1,("error %d create oplock thread",rc)); + } } cifs_destroy_request_bufs(); } @@ -916,6 +938,10 @@ exit_cifs(void) send_sig(SIGTERM, oplockThread, 1); wait_for_completion(&cifs_oplock_exited); } + if(dnotifyThread) { + send_sig(SIGTERM, dnotifyThread, 1); + wait_for_completion(&cifs_dnotify_exited); + } } MODULE_AUTHOR("Steve French "); -- cgit 1.2.3-korg From c46fa8acdc533e8084359ea11c79d56eb98313fb Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 18 Aug 2005 20:49:57 -0700 Subject: [CIFS] Add mount option for disabling sending byte range lock requests over the wire (to help the case when applications break with cifs mandatory lock behavior. Add part one of mount option for requesting case insensitive path name matching. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 8 ++++++++ fs/cifs/README | 7 +++++++ fs/cifs/cifs_fs_sb.h | 2 ++ fs/cifs/cifsfs.h | 2 +- fs/cifs/connect.c | 14 +++++++++++++- fs/cifs/inode.c | 4 ++++ fs/cifs/readdir.c | 10 ++++------ 7 files changed, 39 insertions(+), 8 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 3196d4c4eed36f..b0429ea524fbd1 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,11 @@ +Version 1.36 +------------ +Add mount option for disabling the default behavior of sending byte range lock +requests to the server (necessary for certain applications which break with +mandatory lock behavior such as Evolution), and also mount option for +requesting case insensitive matching for path based requests (requesting +case sensitive is the default). + Version 1.35 ------------ Add writepage performance improvements. Fix path name conversions diff --git a/fs/cifs/README b/fs/cifs/README index 34b0cf7111f384..3b610d08dc1e32 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -407,6 +407,13 @@ A partial list of the supported mount options follows: This has no effect if the server does not support Unicode on the wire. nomapchars Do not translate any of these seven characters (default). + nocase Request case insensitive path name matching (case + sensitive is the default if the server suports it). + nobrl Do not send byte range lock requests to the server. + This is necessary for certain applications that break + with cifs style mandatory byte range locks (and most + cifs servers do not yet support requesting advisory + byte range locks). remount remount the share (often used to change from ro to rw mounts or vice versa) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 6b93587c490e53..5fb695f1e50bbf 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -26,6 +26,8 @@ #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ #define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible. */ #define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */ +#define CIFS_MOUNT_CASE_INSENS 0x100 /* Request case insenstive searches */ +#define CIFS_MOUNT_NO_BRL 0x200 /* No sending byte range locks to srv */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 78af5850c55812..cf45ca359dba5e 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -96,5 +96,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); extern int cifs_ioctl (struct inode * inode, struct file * filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.35" +#define CIFS_VERSION "1.36" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ef0432c4448226..6b21f6aa9f609e 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -79,6 +79,8 @@ struct smb_vol { unsigned remap:1; /* set to remap seven reserved chars in filenames */ unsigned posix_paths:1; /* unset to not ask for posix pathnames. */ unsigned sfu_emul:1; + unsigned nocase; /* request case insensitive filenames */ + unsigned nobrl; /* disable sending byte range locks to srv */ unsigned int rsize; unsigned int wsize; unsigned int sockopt; @@ -1040,6 +1042,12 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->posix_paths = 1; } else if (strnicmp(data, "noposixpaths", 12) == 0) { vol->posix_paths = 0; + } else if (strnicmp(data, "nocase", 6) == 0) { + vol->nocase = 1; + } else if (strnicmp(data, "brl", 3) == 0) { + vol->nobrl = 0; + } else if (strnicmp(data, "nobrl", 5) == 0) { + vol->nobrl = 1; } else if (strnicmp(data, "setuids", 7) == 0) { vol->setuids = 1; } else if (strnicmp(data, "nosetuids", 9) == 0) { @@ -1699,9 +1707,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; if(volume_info.sfu_emul) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; + if(volume_info.nocase) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CASE_INSENS; + if(volume_info.nobrl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; if(volume_info.direct_io) { - cERROR(1,("mounting share using direct i/o")); + cFYI(1,("mounting share using direct i/o")); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 628aa1a9fe6439..ed3e9207d92ec5 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -166,6 +166,8 @@ int cifs_get_inode_info_unix(struct inode **pinode, inode->i_fop = &cifs_file_direct_ops; else inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop->lock = NULL; inode->i_data.a_ops = &cifs_addr_ops; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode")); @@ -369,6 +371,8 @@ int cifs_get_inode_info(struct inode **pinode, inode->i_fop = &cifs_file_direct_ops; else inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop->lock = NULL; inode->i_data.a_ops = &cifs_addr_ops; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode ")); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index dec3c9dd04d7e1..ef5eb804ce8211 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -194,6 +194,8 @@ static void fill_in_inode(struct inode *tmp_inode, tmp_inode->i_fop = &cifs_file_direct_ops; else tmp_inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + tmp_inode->i_fop->lock = NULL; tmp_inode->i_data.a_ops = &cifs_addr_ops; if(isNewInode) @@ -298,6 +300,8 @@ static void unix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_fop = &cifs_file_direct_ops; else tmp_inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + tmp_inode->i_fop->lock = NULL; tmp_inode->i_data.a_ops = &cifs_addr_ops; if(isNewInode) @@ -557,7 +561,6 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, char * end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + smbCalcSize((struct smb_hdr *) cifsFile->srch_inf.ntwrk_buf_start); -/* dump_cifs_file_struct(file,"found entry in fce "); */ first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry - cifsFile->srch_inf.entries_in_buffer; pos_in_buf = index_to_find - first_entry_in_buffer; @@ -595,7 +598,6 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, *num_to_ret = 0; } else *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; -/* dump_cifs_file_struct(file, "end fce ");*/ return rc; } @@ -815,14 +817,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) FreeXid(xid); return -EIO; } -/* dump_cifs_file_struct(file, "Begin rdir "); */ cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; if(pTcon == NULL) return -EINVAL; -/* cFYI(1,("readdir2 pos: %lld",file->f_pos)); */ switch ((int) file->f_pos) { case 0: @@ -876,7 +876,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifsFile->search_resume_name = NULL; */ /* BB account for . and .. in f_pos as special case */ - /* dump_cifs_file_struct(file, "rdir after default ");*/ rc = find_cifs_entry(xid,pTcon, file, ¤t_entry,&num_to_fill); @@ -924,7 +923,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) } /* end switch */ rddir2_exit: - /* dump_cifs_file_struct(file, "end rdir "); */ FreeXid(xid); return rc; } -- cgit 1.2.3-korg From d3485d37c0b3292aec0618b6663c57542df5da99 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Aug 2005 11:04:29 -0700 Subject: [CIFS] Finish cifs mount option which requests case insensitive path name matching. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifs_fs_sb.h | 3 +-- fs/cifs/cifsglob.h | 5 +++-- fs/cifs/connect.c | 9 +++++++-- fs/cifs/misc.c | 2 ++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 5fb695f1e50bbf..f799f6f0e72969 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -26,8 +26,7 @@ #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ #define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible. */ #define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */ -#define CIFS_MOUNT_CASE_INSENS 0x100 /* Request case insenstive searches */ -#define CIFS_MOUNT_NO_BRL 0x200 /* No sending byte range locks to srv */ +#define CIFS_MOUNT_NO_BRL 0x100 /* No sending byte range locks to srv */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index e8287f76484f13..e7ba48c61a7af3 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -110,8 +110,8 @@ enum protocolEnum { */ struct TCP_Server_Info { - char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* 15 chars + X'20'in 16th */ - char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; /* Unicode version of server_Name */ + char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* 15 chars + X'20' 16th */ + char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; struct socket *ssocket; union { struct sockaddr_in sockAddr; @@ -231,6 +231,7 @@ struct cifsTconInfo { FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if file system name truncated */ FILE_SYSTEM_UNIX_INFO fsUnixInfo; unsigned retry:1; + unsigned nocase:1; /* BB add field for back pointer to sb struct? */ }; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 6b21f6aa9f609e..ac2c8bdc8e55a4 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1048,6 +1048,11 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->nobrl = 0; } else if (strnicmp(data, "nobrl", 5) == 0) { vol->nobrl = 1; + /* turn off mandatory locking in mode + 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, "setuids", 7) == 0) { vol->setuids = 1; } else if (strnicmp(data, "nosetuids", 9) == 0) { @@ -1707,8 +1712,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; if(volume_info.sfu_emul) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; - if(volume_info.nocase) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CASE_INSENS; if(volume_info.nobrl) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; @@ -1727,6 +1730,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, to the same server share the last value passed in for the retry flag is used */ tcon->retry = volume_info.retry; + tcon->nocase = volume_info.nocase; } else { tcon = tconInfoAlloc(); if (tcon == NULL) @@ -1755,6 +1759,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (!rc) { atomic_inc(&pSesInfo->inUse); tcon->retry = volume_info.retry; + tcon->nocase = volume_info.nocase; } } } diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index beeff82841696c..40d50b77bfe753 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -375,6 +375,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , } if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) buffer->Flags2 |= SMBFLG2_DFS; + if (treeCon->nocase) + buffer->Flags |= SMBFLG_CASELESS; if((treeCon->ses) && (treeCon->ses->server)) if(treeCon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) -- cgit 1.2.3-korg From a5a2b489bae8f66559a531df99a26eb16b42299e Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 20 Aug 2005 21:42:53 -0700 Subject: [CIFS] Make CIFS statistics more accurate and add some stats that were missing. Most importantly SMB reads were undercounted. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifs_debug.c | 30 ++++++++++++------ fs/cifs/cifsglob.h | 20 +++++++++++- fs/cifs/cifssmb.c | 89 ++++++++++++++++++++++++++++++---------------------- fs/cifs/file.c | 5 --- fs/cifs/transport.c | 2 +- 5 files changed, 91 insertions(+), 55 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 83817132807696..f4c6544468abec 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -254,36 +254,46 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset, buf += sprintf(buf, "\tDISCONNECTED "); length += 14; } - item_length = sprintf(buf,"\nSMBs: %d Oplock Breaks: %d", + item_length = sprintf(buf, "\nSMBs: %d Oplock Breaks: %d", atomic_read(&tcon->num_smbs_sent), atomic_read(&tcon->num_oplock_brks)); buf += item_length; length += item_length; - item_length = sprintf(buf,"\nReads: %d Bytes %lld", + item_length = sprintf(buf, "\nReads: %d Bytes %lld", atomic_read(&tcon->num_reads), (long long)(tcon->bytes_read)); buf += item_length; length += item_length; - item_length = sprintf(buf,"\nWrites: %d Bytes: %lld", + item_length = sprintf(buf, "\nWrites: %d Bytes: %lld", atomic_read(&tcon->num_writes), (long long)(tcon->bytes_written)); + buf += item_length; + length += item_length; + item_length = sprintf(buf, + "\nLocks: %d HardLinks: %d Symlinks: %d", + atomic_read(&tcon->num_locks), + atomic_read(&tcon->num_hardlinks), + atomic_read(&tcon->num_symlinks)); + buf += item_length; + length += item_length; + + item_length = sprintf(buf, "\nOpens: %d Closes: %d Deletes: %d", + atomic_read(&tcon->num_opens), + atomic_read(&tcon->num_closes), + atomic_read(&tcon->num_deletes)); buf += item_length; length += item_length; - item_length = sprintf(buf, - "\nOpens: %d Deletes: %d\nMkdirs: %d Rmdirs: %d", - atomic_read(&tcon->num_opens), - atomic_read(&tcon->num_deletes), + item_length = sprintf(buf, "\nMkdirs: %d Rmdirs: %d", atomic_read(&tcon->num_mkdirs), atomic_read(&tcon->num_rmdirs)); buf += item_length; length += item_length; - item_length = sprintf(buf, - "\nRenames: %d T2 Renames %d", + item_length = sprintf(buf, "\nRenames: %d T2 Renames %d", atomic_read(&tcon->num_renames), atomic_read(&tcon->num_t2renames)); buf += item_length; length += item_length; - item_length = sprintf(buf,"\nFindFirst: %d FNext %d FClose %d", + item_length = sprintf(buf, "\nFindFirst: %d FNext %d FClose %d", atomic_read(&tcon->num_ffirst), atomic_read(&tcon->num_fnext), atomic_read(&tcon->num_fclose)); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index e7ba48c61a7af3..6a8c7d1bee8cef 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -215,6 +215,7 @@ struct cifsTconInfo { atomic_t num_reads; atomic_t num_oplock_brks; atomic_t num_opens; + atomic_t num_closes; atomic_t num_deletes; atomic_t num_mkdirs; atomic_t num_rmdirs; @@ -223,10 +224,27 @@ struct cifsTconInfo { atomic_t num_ffirst; atomic_t num_fnext; atomic_t num_fclose; + atomic_t num_hardlinks; + atomic_t num_symlinks; + atomic_t num_locks; +#ifdef CONFIG_CIFS_STATS2 + unsigned long long time_writes; + unsigned long long time_reads; + unsigned long long time_opens; + unsigned long long time_deletes; + unsigned long long time_closes; + unsigned long long time_mkdirs; + unsigned long long time_rmdirs; + unsigned long long time_renames; + unsigned long long time_t2renames; + unsigned long long time_ffirst; + unsigned long long time_fnext; + unsigned long long time_fclose; +#endif /* CONFIG_CIFS_STATS2 */ __u64 bytes_read; __u64 bytes_written; spinlock_t stat_lock; -#endif +#endif /* CONFIG_CIFS_STATS */ FILE_SYSTEM_DEVICE_INFO fsDevInfo; FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if file system name truncated */ FILE_SYSTEM_UNIX_INFO fsUnixInfo; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 930be0927de221..1292db50fe6564 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -584,14 +584,12 @@ DelFileRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_deletes); +#endif if (rc) { cFYI(1, ("Error in RMFile = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_deletes); - } -#endif cifs_buf_release(pSMB); if (rc == -EAGAIN) @@ -633,14 +631,12 @@ RmDirRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_rmdirs); +#endif if (rc) { cFYI(1, ("Error in RMDir = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_rmdirs); - } -#endif cifs_buf_release(pSMB); if (rc == -EAGAIN) @@ -681,14 +677,13 @@ MkDirRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_mkdirs); +#endif if (rc) { cFYI(1, ("Error in Mkdir = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_mkdirs); - } -#endif + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto MkDirRetry; @@ -772,6 +767,9 @@ openRetry: /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 1); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_opens); +#endif if (rc) { cFYI(1, ("Error in Open = %d", rc)); } else { @@ -789,11 +787,8 @@ openRetry: pfile_info->EndOfFile = pSMBr->EndOfFile; pfile_info->NumberOfLinks = cpu_to_le32(1); } - -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_opens); -#endif } + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto openRetry; @@ -838,6 +833,9 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_reads); +#endif if (rc) { cERROR(1, ("Send error in read = %d", rc)); } else { @@ -940,6 +938,9 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, long_op); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_writes); +#endif if (rc) { cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; @@ -1012,6 +1013,9 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, smb_hdr_len, buf, bytes_sent, &bytes_returned, long_op); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_writes); +#endif if (rc) { cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; @@ -1087,7 +1091,9 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, timeout); - +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_locks); +#endif if (rc) { cFYI(1, ("Send error in Lock = %d", rc)); } @@ -1121,6 +1127,9 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_closes); +#endif if (rc) { if(rc!=-EINTR) { /* EINTR is expected when user ctl-c to kill app */ @@ -1193,16 +1202,13 @@ renameRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_renames); +#endif if (rc) { cFYI(1, ("Send error in rename = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_renames); - } -#endif - cifs_buf_release(pSMB); if (rc == -EAGAIN) @@ -1277,14 +1283,13 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&pTcon->num_t2renames); +#endif if (rc) { cFYI(1,("Send error in Rename (by file handle) = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&pTcon->num_t2renames); - } -#endif + cifs_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls @@ -1438,6 +1443,9 @@ createSymLinkRetry: pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_symlinks); +#endif if (rc) { cFYI(1, ("Send error in SetPathInfo (create symlink) = %d", @@ -1527,6 +1535,9 @@ createHardLinkRetry: pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_hardlinks); +#endif if (rc) { cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc)); } @@ -1597,6 +1608,9 @@ winCreateHardLinkRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_hardlinks); +#endif if (rc) { cFYI(1, ("Send error in hard link (NT rename) = %d", rc)); } @@ -2519,6 +2533,9 @@ findFirstRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_ffirst); +#endif if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ /* BB Add code to handle unsupported level rc */ @@ -2532,9 +2549,6 @@ findFirstRetry: if (rc == -EAGAIN) goto findFirstRetry; } else { /* decode response */ -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_ffirst); -#endif /* BB remember to free buffer if error BB */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if(rc == 0) { @@ -2643,7 +2657,9 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); - +#ifdef CONFIG_CIFS_STATS + atomic_inc(&tcon->num_fnext); +#endif if (rc) { if (rc == -EBADF) { psrch_inf->endOfSearch = TRUE; @@ -2651,9 +2667,6 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, } else cFYI(1, ("FindNext returned = %d", rc)); } else { /* decode response */ -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_fnext); -#endif rc = validate_t2((struct smb_t2_rsp *)pSMBr); if(rc == 0) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ddb25a0a63d5bc..b054df2dee1eac 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -746,7 +746,6 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, #ifdef CONFIG_CIFS_STATS if (total_written > 0) { - atomic_inc(&pTcon->num_writes); spin_lock(&pTcon->stat_lock); pTcon->bytes_written += total_written; spin_unlock(&pTcon->stat_lock); @@ -881,7 +880,6 @@ static ssize_t cifs_write(struct file *file, const char *write_data, #ifdef CONFIG_CIFS_STATS if (total_written > 0) { - atomic_inc(&pTcon->num_writes); spin_lock(&pTcon->stat_lock); pTcon->bytes_written += total_written; spin_unlock(&pTcon->stat_lock); @@ -1248,7 +1246,6 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, } } else { #ifdef CONFIG_CIFS_STATS - atomic_inc(&pTcon->num_reads); spin_lock(&pTcon->stat_lock); pTcon->bytes_read += total_read; spin_unlock(&pTcon->stat_lock); @@ -1316,7 +1313,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, } } else { #ifdef CONFIG_CIFS_STATS - atomic_inc(&pTcon->num_reads); spin_lock(&pTcon->stat_lock); pTcon->bytes_read += total_read; spin_unlock(&pTcon->stat_lock); @@ -1493,7 +1489,6 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, i += bytes_read >> PAGE_CACHE_SHIFT; #ifdef CONFIG_CIFS_STATS - atomic_inc(&pTcon->num_reads); spin_lock(&pTcon->stat_lock); pTcon->bytes_read += bytes_read; spin_unlock(&pTcon->stat_lock); diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 496a2738bbe3d5..96f89eb6604025 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -735,7 +735,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, BCC(out_buf) = le16_to_cpu(BCC(out_buf)); } else { rc = -EIO; - cFYI(1,("Bad MID state? ")); + cERROR(1,("Bad MID state? ")); } } cifs_no_response_exit: -- cgit 1.2.3-korg From b92327fe6b25d60004b79df9e3c19091c03118ba Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 22 Aug 2005 20:09:43 -0700 Subject: [CIFS] Finish up of case-insensitive dentry handling for cifs. This will eventually (or should eventually) be common code for jfs, smbfs, etc. but in the meantime is small enough and necessary when mounting case insensitive to Windows (nocase). Signed-off-by: Shaggy (shaggy@austin.ibm.com) Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsfs.h | 1 + fs/cifs/dir.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- fs/cifs/inode.c | 5 ++++- fs/cifs/link.c | 5 ++++- fs/cifs/readdir.c | 5 ++++- 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index d5fb3441555f0f..bb3404a99e5f9b 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -81,6 +81,7 @@ extern int cifs_dir_notify(struct file *, unsigned long arg); /* Functions related to dir entries */ extern struct dentry_operations cifs_dentry_ops; +extern struct dentry_operations cifs_ci_dentry_ops; /* Functions related to symlinks */ extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index c619d45060ce0d..5311c50734b0a2 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -230,7 +230,10 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ("Create worked but get_inode_info failed rc = %d", rc)); } else { - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); } if((nd->flags & LOOKUP_OPEN) == FALSE) { @@ -322,7 +325,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev if(!rc) { rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,xid); - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; if(rc == 0) d_instantiate(direntry, newinode); } @@ -418,7 +424,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct name parent_dir_inode->i_sb,xid); if ((rc == 0) && (newInode != NULL)) { - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_add(direntry, newInode); /* since paths are not looked up by component - the parent directories are presumed to be good here */ @@ -477,3 +486,42 @@ struct dentry_operations cifs_dentry_ops = { /* d_delete: cifs_d_delete, *//* not needed except for debugging */ /* no need for d_hash, d_compare, d_release, d_iput ... yet. BB confirm this BB */ }; + +static int cifs_ci_hash(struct dentry *dentry, struct qstr *q) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; + unsigned long hash; + int i; + + hash = init_name_hash(); + for (i = 0; i < q->len; i++) + hash = partial_name_hash(nls_tolower(codepage, q->name[i]), + hash); + q->hash = end_name_hash(hash); + + return 0; +} + +static int cifs_ci_compare(struct dentry *dentry, struct qstr *a, + struct qstr *b) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; + + if ((a->len == b->len) && + (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) { + /* + * To preserve case, don't let an existing negative dentry's + * case take precedence. If a is not a negative dentry, this + * should have no side effects + */ + memcpy((unsigned char *)a->name, b->name, a->len); + return 0; + } + return 1; +} + +struct dentry_operations cifs_ci_dentry_ops = { + .d_revalidate = cifs_d_revalidate, + .d_hash = cifs_ci_hash, + .d_compare = cifs_ci_compare, +}; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ed3e9207d92ec5..2d50b3507d13d2 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -591,7 +591,10 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) rc = cifs_get_inode_info(&newinode, full_path, NULL, inode->i_sb,xid); - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); if (direntry->d_inode) direntry->d_inode->i_nlink = 2; diff --git a/fs/cifs/link.c b/fs/cifs/link.c index da420e8c32985c..b8ec6646456a74 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -199,7 +199,10 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) ("Create symlink worked but get_inode_info failed with rc = %d ", rc)); } else { - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); } } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index ef5eb804ce8211..f769292e2a93db 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -91,7 +91,10 @@ static int construct_dentry(struct qstr *qstring, struct file *file, } *ptmp_inode = new_inode(file->f_dentry->d_sb); - tmp_dentry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + tmp_dentry->d_op = &cifs_ci_dentry_ops; + else + tmp_dentry->d_op = &cifs_dentry_ops; if(*ptmp_inode == NULL) return rc; rc = 1; -- cgit 1.2.3-korg From a10faeb2a3e266385cc334fe9af76e08e5e4330f Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 22 Aug 2005 21:38:31 -0700 Subject: [CIFS] Support for mounting to older, pre-CIFS servers added. This allows specifying an RFC1001 target "called" name (netbios name of the server, which can now be pecified as mount option "servernetbiosname" but will eventually be passed in automatically on retry of host down error messages caused when server refuses to handle default server name and can not handle port 445). This is an important step, but additional testing and fixup is needed to add remaining function needed for these. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 7 ++++++- fs/cifs/cifsglob.h | 6 ++++-- fs/cifs/connect.c | 57 +++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index b0429ea524fbd1..2137002aecc4aa 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,5 +1,6 @@ Version 1.36 ------------ +Add support for moounting to older pre-CIFS servers such as Windows9x and ME. Add mount option for disabling the default behavior of sending byte range lock requests to the server (necessary for certain applications which break with mandatory lock behavior such as Evolution), and also mount option for @@ -10,7 +11,11 @@ Version 1.35 ------------ Add writepage performance improvements. Fix path name conversions for long filenames on mounts which were done with "mapchars" mount option -specified. +specified. Ensure multiplex ids do not collide. Fix case in which +rmmod can oops if done soon after last unmount. Fix truncated +search (readdir) output when resume filename was a long filename. +Fix filename conversion when mapchars mount option was specified and +filename was a long filename. Version 1.34 ------------ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6a8c7d1bee8cef..f143975627e0fb 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -110,7 +110,8 @@ enum protocolEnum { */ struct TCP_Server_Info { - char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* 15 chars + X'20' 16th */ + /* 15 character server name + 0x20 16th byte indicating type = srv */ + char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; struct socket *ssocket; union { @@ -149,7 +150,8 @@ struct TCP_Server_Info { __u16 timeZone; __u16 CurrentMid; /* multiplex id - rotating counter */ char cryptKey[CIFS_CRYPTO_KEY_SIZE]; - char workstation_RFC1001_name[16]; /* 16th byte is always zero */ + /* 16th byte of RFC1001 workstation name is always null */ + char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ char mac_signing_key[CIFS_SESSION_KEY_SIZE + 16]; }; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ac2c8bdc8e55a4..9d61844e89b6d8 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -63,6 +63,7 @@ struct smb_vol { char *in6_addr; /* ipv6 address as human readable form of in6_addr */ char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[16]; /* netbios name of client */ + char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */ uid_t linux_uid; gid_t linux_gid; mode_t file_mode; @@ -89,7 +90,8 @@ struct smb_vol { static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netb_name); + char * netb_name, + char * server_netb_name); static int ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket); @@ -182,7 +184,8 @@ cifs_reconnect(struct TCP_Server_Info *server) } else { rc = ipv4_connect(&server->addr.sockAddr, &server->ssocket, - server->workstation_RFC1001_name); + server->workstation_RFC1001_name, + server->server_RFC1001_name); } if(rc) { msleep(3000); @@ -743,7 +746,9 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) toupper(system_utsname.nodename[i]); } vol->source_rfc1001_name[15] = 0; - + /* null target name indicates to use *SMBSERVR default called name + if we end up sending RFC1001 session initialize */ + vol->target_rfc1001_name[0] = 0; vol->linux_uid = current->uid; /* current->euid instead? */ vol->linux_gid = current->gid; vol->dir_mode = S_IRWXUGO; @@ -996,7 +1001,31 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) /* The string has 16th byte zero still from set at top of the function */ if((i==15) && (value[i] != 0)) - printk(KERN_WARNING "CIFS: netbiosname longer than 15 and was truncated.\n"); + 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,16); + + 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 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==15) && (value[i] != 0)) + printk(KERN_WARNING "CIFS: server netbiosname longer than 15 truncated.\n"); } } else if (strnicmp(data, "credentials", 4) == 0) { /* ignore */ @@ -1042,7 +1071,8 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->posix_paths = 1; } else if (strnicmp(data, "noposixpaths", 12) == 0) { vol->posix_paths = 0; - } else if (strnicmp(data, "nocase", 6) == 0) { + } else if ((strnicmp(data, "nocase", 6) == 0) || + (strnicmp(data, "ignorecase", 10) == 0)) { vol->nocase = 1; } else if (strnicmp(data, "brl", 3) == 0) { vol->nobrl = 0; @@ -1272,7 +1302,7 @@ static void rfc1002mangle(char * target,char * source, unsigned int length) static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netbios_name) + char * netbios_name, char * target_name) { int rc = 0; int connected = 0; @@ -1350,8 +1380,14 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, ses_init_buf = kcalloc(1, sizeof(struct rfc1002_session_packet), GFP_KERNEL); if(ses_init_buf) { ses_init_buf->trailer.session_req.called_len = 32; - rfc1002mangle(ses_init_buf->trailer.session_req.called_name, - DEFAULT_CIFS_CALLED_NAME,16); + if(target_name && (target_name[0] != 0)) { + rfc1002mangle(ses_init_buf->trailer.session_req.called_name, + target_name, 16); + } else { + rfc1002mangle(ses_init_buf->trailer.session_req.called_name, + DEFAULT_CIFS_CALLED_NAME,16); + } + ses_init_buf->trailer.session_req.calling_len = 32; /* calling name ends in null (byte 16) from old smb convention. */ @@ -1584,7 +1620,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, sin_server.sin_port = htons(volume_info.port); else sin_server.sin_port = 0; - rc = ipv4_connect(&sin_server,&csocket,volume_info.source_rfc1001_name); + rc = ipv4_connect(&sin_server,&csocket, + volume_info.source_rfc1001_name, + volume_info.target_rfc1001_name); if (rc < 0) { cERROR(1, ("Error connecting to IPv4 socket. Aborting operation")); @@ -1638,6 +1676,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, wait_for_completion(&cifsd_complete); rc = 0; memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16); + memcpy(srvTcp->server_RFC1001_name, volume_info.target_rfc1001_name,16); srvTcp->sequence_number = 0; } } -- cgit 1.2.3-korg From 6b8edfe0f918e7585acb3bd63f62ff56e32dd3d2 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 23 Aug 2005 20:26:03 -0700 Subject: [CIFS] Support for mounting to older servers part 2. Add support for legacy getattr (lookup). Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 2 ++ fs/cifs/cifspdu.h | 17 +++++++++++++++ fs/cifs/cifsproto.h | 4 ++++ fs/cifs/cifssmb.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/connect.c | 5 +++-- fs/cifs/inode.c | 12 ++++++++++- 6 files changed, 96 insertions(+), 3 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 2137002aecc4aa..299ed312cea560 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,6 +1,8 @@ Version 1.36 ------------ Add support for moounting to older pre-CIFS servers such as Windows9x and ME. +For these older servers, add option for passing netbios name of server in +on mount (servernetbiosname). Add mount option for disabling the default behavior of sending byte range lock requests to the server (necessary for certain applications which break with mandatory lock behavior such as Evolution), and also mount option for diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 49cc66825309ea..42c16cf32284f8 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -36,6 +36,7 @@ #define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ #define SMB_COM_DELETE 0x06 /* trivial response */ #define SMB_COM_RENAME 0x07 /* trivial response */ +#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ #define SMB_COM_SETATTR 0x09 /* trivial response */ #define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ #define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ @@ -885,6 +886,22 @@ typedef struct smb_com_create_directory_rsp { __u16 ByteCount; /* bct = 0 */ } CREATE_DIRECTORY_RSP; +typedef struct smb_com_query_information_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* 1 + namelen + 1 */ + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char FileName[1]; +} QUERY_INFORMATION_REQ; + +typedef struct smb_com_query_information_rsp { + struct smb_hdr hdr; /* wct = 10 */ + __le16 attr; + __le32 last_write_time; + __le32 size; + __u16 reserved[5]; + __le16 ByteCount; /* bcc = 0 */ +} QUERY_INFORMATION_RSP; + typedef struct smb_com_setattr_req { struct smb_hdr hdr; /* wct = 8 */ __le16 attr; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index b9b13e3fe79d1c..0cc0612eacb468 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -105,6 +105,10 @@ extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO * findData, const struct nls_table *nls_codepage, int remap); +extern int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, + FILE_ALL_INFO * findData, + const struct nls_table *nls_codepage, int remap); extern int CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 1292db50fe6564..811ab3dffafa4e 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2200,6 +2200,65 @@ GetExtAttrOut: #endif /* CONFIG_POSIX */ +/* Legacy Query Path Information call for lookup to old servers such + as Win9x/WinME */ +int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, + FILE_ALL_INFO * pFinfo, + const struct nls_table *nls_codepage, int remap) +{ + QUERY_INFORMATION_REQ * pSMB; + QUERY_INFORMATION_RSP * pSMBr; + int rc = 0; + int bytes_returned; + int name_len; + + cFYI(1, ("In SMBQPath path %s", searchName)); +QInfRetry: + rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = strnlen(searchName, PATH_MAX); + name_len++; /* trailing null */ + strncpy(pSMB->FileName, searchName, name_len); + } + pSMB->BufferFormat = 0x04; + name_len++; /* account for buffer type byte */ + pSMB->hdr.smb_buf_length += (__u16) name_len; + pSMB->ByteCount = cpu_to_le16(name_len); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Send error in QueryInfo = %d", rc)); + } else if (pFinfo) { /* decode response */ + memset(pFinfo, 0, sizeof(FILE_ALL_INFO)); + pFinfo->AllocationSize = (__le64) pSMBr->size; + pFinfo->EndOfFile = (__le64) pSMBr->size; + pFinfo->Attributes = (__le32) pSMBr->attr; + } else + rc = -EIO; /* bad buffer passed in */ + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QInfRetry; + + return rc; +} + + + + int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9d61844e89b6d8..c75bae1242dcce 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1730,8 +1730,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, else cifs_sb->wsize = CIFSMaxBufSize; /* default */ if(cifs_sb->rsize < PAGE_CACHE_SIZE) { - cifs_sb->rsize = PAGE_CACHE_SIZE; - cERROR(1,("Attempt to set readsize for mount to less than one page (4096)")); + cifs_sb->rsize = PAGE_CACHE_SIZE; + /* Windows ME does this */ + cFYI(1,("Attempt to set readsize for mount to less than one page (4096)")); } cifs_sb->mnt_uid = volume_info.linux_uid; cifs_sb->mnt_gid = volume_info.linux_gid; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 2d50b3507d13d2..34f0168c4041dd 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -215,8 +215,18 @@ int cifs_get_inode_info(struct inode **pinode, pfindData = (FILE_ALL_INFO *)buf; /* could do find first instead but this returns more info */ rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + /* BB optimize code so we do not make the above call + when server claims no NT SMB support and the above call + failed at least once - set flag in tcon or mount */ + if((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { + rc = SMBQueryInformation(xid, pTcon, search_path, + pfindData, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + } + } /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ if (rc) { -- cgit 1.2.3-korg From a45443475835ab4d1c753159812aca21b5c333a3 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 13:59:35 -0700 Subject: CIFS: Reduce CONFIG_CIFS_STATS ifdefs Make cifs_stats code conditional in the header files to avoid ifdefs in the main code. Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 28 +++++++++++++++++ fs/cifs/cifssmb.c | 88 +++++++++++++++--------------------------------------- fs/cifs/file.c | 34 ++++----------------- fs/cifs/misc.c | 4 +-- 4 files changed, 58 insertions(+), 96 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index f143975627e0fb..9a3c85bdd77eab 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -339,6 +339,34 @@ static inline const char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) return '\\'; } +#ifdef CONFIG_CIFS_STATS +#define cifs_stats_inc atomic_inc + +static inline void cifs_stats_bytes_written(struct cifsTconInfo *tcon, + unsigned int bytes) +{ + if (bytes) { + spin_lock(&tcon->stat_lock); + tcon->bytes_written += bytes; + spin_unlock(&tcon->stat_lock); + } +} + +static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon, + unsigned int bytes) +{ + spin_lock(&tcon->stat_lock); + tcon->bytes_read += bytes; + spin_unlock(&tcon->stat_lock); +} +#else + +#define cifs_stats_inc(field) do {} while(0) +#define cifs_stats_bytes_written(tcon, bytes) do {} while(0) +#define cifs_stats_bytes_read(tcon, bytes) do {} while(0) + +#endif + /* 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 */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 811ab3dffafa4e..698cdcebca04b9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -166,11 +166,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,wct); -#ifdef CONFIG_CIFS_STATS - if(tcon != NULL) { - atomic_inc(&tcon->num_smbs_sent); - } -#endif /* CONFIG_CIFS_STATS */ + if(tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + return rc; } @@ -269,11 +267,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct /*wct */ ); -#ifdef CONFIG_CIFS_STATS - if(tcon != NULL) { - atomic_inc(&tcon->num_smbs_sent); - } -#endif /* CONFIG_CIFS_STATS */ + if(tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + return rc; } @@ -584,9 +580,7 @@ DelFileRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_deletes); -#endif + cifs_stats_inc(&tcon->num_deletes); if (rc) { cFYI(1, ("Error in RMFile = %d", rc)); } @@ -631,9 +625,7 @@ RmDirRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_rmdirs); -#endif + cifs_stats_inc(&tcon->num_rmdirs); if (rc) { cFYI(1, ("Error in RMDir = %d", rc)); } @@ -677,9 +669,7 @@ MkDirRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_mkdirs); -#endif + cifs_stats_inc(&tcon->num_mkdirs); if (rc) { cFYI(1, ("Error in Mkdir = %d", rc)); } @@ -767,9 +757,7 @@ openRetry: /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 1); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_opens); -#endif + cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, ("Error in Open = %d", rc)); } else { @@ -833,9 +821,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_reads); -#endif + cifs_stats_inc(&tcon->num_reads); if (rc) { cERROR(1, ("Send error in read = %d", rc)); } else { @@ -938,9 +924,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, long_op); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_writes); -#endif + cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; @@ -1013,9 +997,7 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, smb_hdr_len, buf, bytes_sent, &bytes_returned, long_op); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_writes); -#endif + cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; @@ -1091,9 +1073,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, timeout); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_locks); -#endif + cifs_stats_inc(&tcon->num_locks); if (rc) { cFYI(1, ("Send error in Lock = %d", rc)); } @@ -1127,9 +1107,7 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_closes); -#endif + cifs_stats_inc(&tcon->num_closes); if (rc) { if(rc!=-EINTR) { /* EINTR is expected when user ctl-c to kill app */ @@ -1202,9 +1180,7 @@ renameRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_renames); -#endif + cifs_stats_inc(&tcon->num_renames); if (rc) { cFYI(1, ("Send error in rename = %d", rc)); } @@ -1283,9 +1259,7 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&pTcon->num_t2renames); -#endif + cifs_stats_inc(&pTcon->num_t2renames); if (rc) { cFYI(1,("Send error in Rename (by file handle) = %d", rc)); } @@ -1443,9 +1417,7 @@ createSymLinkRetry: pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_symlinks); -#endif + cifs_stats_inc(&tcon->num_symlinks); if (rc) { cFYI(1, ("Send error in SetPathInfo (create symlink) = %d", @@ -1535,9 +1507,7 @@ createHardLinkRetry: pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_hardlinks); -#endif + cifs_stats_inc(&tcon->num_hardlinks); if (rc) { cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc)); } @@ -1608,9 +1578,7 @@ winCreateHardLinkRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_hardlinks); -#endif + cifs_stats_inc(&tcon->num_hardlinks); if (rc) { cFYI(1, ("Send error in hard link (NT rename) = %d", rc)); } @@ -2490,9 +2458,7 @@ findUniqueRetry: if (rc) { cFYI(1, ("Send error in FindFileDirInfo = %d", rc)); } else { /* decode response */ -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_ffirst); -#endif + cifs_stats_inc(&tcon->num_ffirst); /* BB fill in */ } @@ -2592,9 +2558,7 @@ findFirstRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_ffirst); -#endif + cifs_stats_inc(&tcon->num_ffirst); if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ /* BB Add code to handle unsupported level rc */ @@ -2716,9 +2680,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_fnext); -#endif + cifs_stats_inc(&tcon->num_fnext); if (rc) { if (rc == -EBADF) { psrch_inf->endOfSearch = TRUE; @@ -2795,9 +2757,7 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle if (rc) { cERROR(1, ("Send error in FindClose = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_fclose); -#endif + cifs_stats_inc(&tcon->num_fclose); cifs_small_buf_release(pSMB); /* Since session is dead, search handle closed on server already */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b054df2dee1eac..5857d12611e601 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -744,13 +744,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, 15 seconds is plenty */ } -#ifdef CONFIG_CIFS_STATS - if (total_written > 0) { - spin_lock(&pTcon->stat_lock); - pTcon->bytes_written += total_written; - spin_unlock(&pTcon->stat_lock); - } -#endif + cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if (file->f_dentry) { @@ -878,13 +872,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, 15 seconds is plenty */ } -#ifdef CONFIG_CIFS_STATS - if (total_written > 0) { - spin_lock(&pTcon->stat_lock); - pTcon->bytes_written += total_written; - spin_unlock(&pTcon->stat_lock); - } -#endif + cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if (file->f_dentry) { @@ -1245,11 +1233,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, return rc; } } else { -#ifdef CONFIG_CIFS_STATS - spin_lock(&pTcon->stat_lock); - pTcon->bytes_read += total_read; - spin_unlock(&pTcon->stat_lock); -#endif + cifs_stats_bytes_read(pTcon, bytes_read); *poffset += bytes_read; } } @@ -1312,11 +1296,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return rc; } } else { -#ifdef CONFIG_CIFS_STATS - spin_lock(&pTcon->stat_lock); - pTcon->bytes_read += total_read; - spin_unlock(&pTcon->stat_lock); -#endif + cifs_stats_bytes_read(pTcon, total_read); *poffset += bytes_read; } } @@ -1488,11 +1468,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, le16_to_cpu(pSMBr->DataOffset), &lru_pvec); i += bytes_read >> PAGE_CACHE_SHIFT; -#ifdef CONFIG_CIFS_STATS - spin_lock(&pTcon->stat_lock); - pTcon->bytes_read += bytes_read; - spin_unlock(&pTcon->stat_lock); -#endif + cifs_stats_bytes_read(pTcon, bytes_read); if ((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) { i++; /* account for partial page */ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 40d50b77bfe753..fafbdbfa63a138 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -520,9 +520,7 @@ is_valid_oplock_break(struct smb_hdr *buf) list_for_each(tmp, &GlobalTreeConnectionList) { tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); if (tcon->tid == buf->Tid) { -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_oplock_brks); -#endif + cifs_stats_inc(&tcon->num_oplock_brks); list_for_each(tmp1,&tcon->openFileList){ netfile = list_entry(tmp1,struct cifsFileInfo, tlist); -- cgit 1.2.3-korg From c0c3e8edaabcb6cf6786226813cf087ad21f0743 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 14:01:13 -0700 Subject: CIFS: Fix typos in fs/cifs/CHANGES Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/CHANGES | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 299ed312cea560..340b4ffb3493bc 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,6 +1,6 @@ Version 1.36 ------------ -Add support for moounting to older pre-CIFS servers such as Windows9x and ME. +Add support for mounting to older pre-CIFS servers such as Windows9x and ME. For these older servers, add option for passing netbios name of server in on mount (servernetbiosname). Add mount option for disabling the default behavior of sending byte range lock @@ -26,7 +26,7 @@ Do not oops if root user kills cifs oplock kernel thread or kills the cifsd thread (NB: killing the cifs kernel threads is not recommended, unmount and rmmod cifs will kill them when they are no longer needed). Fix readdir to ASCII servers (ie older servers -which do not support Unicode) and also require asterik. +which do not support Unicode) and also require asterisk. Fix out of memory case in which data could be written one page off in the page cache. @@ -116,7 +116,7 @@ improperly zeroed buffer in CIFS Unix extensions set times call. Version 1.25 ------------ -Fix internationlization problem in cifs readdir with filenames that map to +Fix internationalization problem in cifs readdir with filenames that map to longer UTF8 strings than the string on the wire was in Unicode. Add workaround for readdir to netapp servers. Fix search rewind (seek into readdir to return non-consecutive entries). Do not do readdir when server negotiates @@ -291,7 +291,7 @@ Fix caching problem when files opened by multiple clients in which page cache could contain stale data, and write through did not occur often enough while file was still open when read ahead (read oplock) not allowed. Treat "sep=" when first mount option -as an overrride of comma as the default separator between mount +as an override of comma as the default separator between mount options. Version 1.01 @@ -301,7 +301,7 @@ Allow passwords longer than 16 bytes. Allow null password string. Version 1.00 ------------ Gracefully clean up failed mounts when attempting to mount to servers such as -Windows 98 that terminate tcp sessions during prototocol negotiation. Handle +Windows 98 that terminate tcp sessions during protocol negotiation. Handle embedded commas in mount parsing of passwords. Version 0.99 @@ -310,7 +310,7 @@ Invalidate local inode cached pages on oplock break and when last file instance is closed so that the client does not continue using stale local copy rather than later modified server copy of file. Do not reconnect when server drops the tcp session prematurely before negotiate -protocol response. Fix oops in roepen_file when dentry freed. Allow +protocol response. Fix oops in reopen_file when dentry freed. Allow the support for CIFS Unix Extensions to be disabled via proc interface. Version 0.98 @@ -652,7 +652,7 @@ versions of 2.4 kernel (now builds and works again on kernels at least as early Version 0.41 ------------ Various minor fixes for Connectathon Posix "basic" file i/o test suite. Directory caching fixed so hardlinked -files now return the correct rumber of links on fstat as they are repeatedly linked and unlinked. +files now return the correct number of links on fstat as they are repeatedly linked and unlinked. Version 0.40 ------------ @@ -719,7 +719,7 @@ session) and cleaned them up and made them more consistent with other cifs functions. 7) Server support for Unix extensions is now fully detected and FindFirst is implemented both ways -(with or without Unix exentions) but FindNext and QueryPathInfo with the Unix extensions are not completed, +(with or without Unix extensions) but FindNext and QueryPathInfo with the Unix extensions are not completed, nor is the symlink support using the Unix extensions 8) Started adding the readlink and follow_link code -- cgit 1.2.3-korg From e2a98a7543696306346ba8302a8df6cedf20fdfc Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 14:43:14 -0700 Subject: [CIFS] Update thanks/contributor list with Miklos Szeredi (Signed-off-by: Miklos Szeredi also should have been listed on the last cifs patch fixing some lookup intent handling in cifs) Signed-off-by: Steve French --- fs/cifs/AUTHORS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS index 72fdc10dfdd7c9..8848e4dfa026d1 100644 --- a/fs/cifs/AUTHORS +++ b/fs/cifs/AUTHORS @@ -32,6 +32,10 @@ Domen Puncer Jesper Juhl (in particular for lots of whitespace/formatting cleanup) Vince Negri and Dave Stahl (for finding an important caching bug) Adrian Bunk (kcalloc cleanups) +Miklos Szeredi +Kazeon team for various fixes especially for 2.4 version. +Asser Ferno (Change Notify support) +Shaggy (Dave Kleikamp) for inumerable small fs suggestions and some good cleanup Test case and Bug Report contributors ------------------------------------- -- cgit 1.2.3-korg From ff5dbd9ead0d82466cab8bdbdcbc9666707d328a Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 17:10:36 -0700 Subject: [CIFS] Change notify support part 2 Signed-off-by: Asser Ferno Signed-off-by: Steve French and lightly modified --- fs/cifs/cifsglob.h | 17 +++++++++++++++++ fs/cifs/cifspdu.h | 1 + fs/cifs/cifssmb.c | 16 ++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9a3c85bdd77eab..92fba7609e67bc 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -391,6 +391,20 @@ struct oplock_q_entry { __u16 netfid; }; +/* for pending dnotify requests */ +struct dir_notify_req { + struct list_head lhead; + __le16 Pid; + __le16 PidHigh; + __u16 Mid; + __u16 Tid; + __u16 Uid; + __u16 netfid; + __u32 filter; /* CompletionFilter (for multishot) */ + int multishot; + struct dentry * dentry; +}; + #define MID_FREE 0 #define MID_REQUEST_ALLOCATED 1 #define MID_REQUEST_SUBMITTED 2 @@ -459,6 +473,9 @@ GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ GLOBAL_EXTERN struct list_head GlobalOplock_Q; +GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; /* Outstanding dir notify requests */ +GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q; /* Dir notify response queue */ + /* * Global transaction id (XID) information */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 42c16cf32284f8..026c88f486a23f 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -53,6 +53,7 @@ #define SMB_COM_NT_TRANSACT 0xA0 #define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 #define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 /* no response */ #define SMB_COM_NT_RENAME 0xA5 /* trivial response */ /* Transact2 subcommand codes */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 698cdcebca04b9..36d45b1dffc254 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -4015,6 +4015,22 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, (struct smb_hdr *) pSMBr, &bytes_returned, -1); if (rc) { cFYI(1, ("Error in Notify = %d", rc)); + } else { + /* Add file to outstanding requests */ + dnotify_req = (struct dir_notify_req *) kmalloc( + sizeof(struct dir_notify_req), GFP_KERNEL); + dnotify_req->Pid = pSMB->hdr.Pid; + dnotify_req->PidHigh = pSMB->hdr.PidHigh; + dnotify_req->Mid = pSMB->hdr.Mid; + dnotify_req->Tid = pSMB->hdr.Tid; + dnotify_req->Uid = pSMB->hdr.Uid; + dnotify_req->netfid = netfid; + dnotify_req->dentry = dentry; + dnotify_req->filter = filter; + dnotify_req->multishot = multishot; + spin_lock(&GlobalMid_Lock); + list_add_tail(&dnotify_req->lhead, &GlobalDnotifyReqList); + spin_unlock(&GlobalMid_Lock); } cifs_buf_release(pSMB); return rc; -- cgit 1.2.3-korg From abb15b8ae4eb7cdff0061e7ac5eca1f8dd8a84af Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 18:51:02 -0700 Subject: [CIFS] Missing line from previous patch Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifssmb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 36d45b1dffc254..be2a3b9a718c38 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3983,6 +3983,7 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, int rc = 0; struct smb_com_transaction_change_notify_req * pSMB = NULL; struct smb_com_transaction_change_notify_rsp * pSMBr = NULL; + struct dir_notify_req *dnotify_req; int bytes_returned; cFYI(1, ("In CIFSSMBNotify for file handle %d",(int)netfid)); -- cgit 1.2.3-korg From 167a251ad6678ad26aa3cf27bab677b274374ab6 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 20:03:11 -0700 Subject: [CIFS] Change notify support part 3 Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifsproto.h | 3 ++- fs/cifs/cifssmb.c | 7 ++++--- fs/cifs/fcntl.c | 8 +++++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 92fba7609e67bc..236de07cbda641 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -402,7 +402,7 @@ struct dir_notify_req { __u16 netfid; __u32 filter; /* CompletionFilter (for multishot) */ int multishot; - struct dentry * dentry; + struct file * dentry; }; #define MID_FREE 0 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 0cc0612eacb468..28b1ebbd380156 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -275,7 +275,8 @@ extern int CIFSSMBCopy(int xid, int remap_special_chars); extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, const int notify_subdirs,const __u16 netfid, - __u32 filter, const struct nls_table *nls_codepage); + __u32 filter, struct file * file, int multishot, + const struct nls_table *nls_codepage); extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char * EAData, size_t bufsize, const struct nls_table *nls_codepage, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index be2a3b9a718c38..67a6240ff2ba8d 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3977,8 +3977,9 @@ setPermsRetry: } int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, - const int notify_subdirs, const __u16 netfid, - __u32 filter, const struct nls_table *nls_codepage) + const int notify_subdirs, const __u16 netfid, + __u32 filter, struct file * pfile, int multishot, + const struct nls_table *nls_codepage) { int rc = 0; struct smb_com_transaction_change_notify_req * pSMB = NULL; @@ -4026,7 +4027,7 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, dnotify_req->Tid = pSMB->hdr.Tid; dnotify_req->Uid = pSMB->hdr.Uid; dnotify_req->netfid = netfid; - dnotify_req->dentry = dentry; + dnotify_req->pfile = pfile; dnotify_req->filter = filter; dnotify_req->multishot = multishot; spin_lock(&GlobalMid_Lock); diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index d47ce7f49dc313..2e5137b7352a48 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -100,8 +100,10 @@ int cifs_dir_notify(struct file * file, unsigned long arg) } else { filter = convert_to_cifs_notify_flags(arg); if(filter != 0) { - rc = CIFSSMBNotify(xid, pTcon, 0 /* no subdirs */, netfid, - filter, cifs_sb->local_nls); + rc = CIFSSMBNotify(xid, pTcon, + 0 /* no subdirs */, netfid, + filter, file, arg & DN_MULTISHOT, + cifs_sb->local_nls); } else { rc = -EINVAL; } @@ -109,7 +111,7 @@ int cifs_dir_notify(struct file * file, unsigned long arg) it would close automatically but may be a way to do it easily when inode freed or when notify info is cleared/changed */ - cERROR(1,("notify rc %d",rc)); + cFYI(1,("notify rc %d",rc)); } } -- cgit 1.2.3-korg From e22cb8bcb8bce94bf5cca90c98933a28816c6a75 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 20:25:21 -0700 Subject: [CIFS] Use file instead of dentry in cifs dir_notify struct Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 236de07cbda641..a9c791edede595 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -402,7 +402,7 @@ struct dir_notify_req { __u16 netfid; __u32 filter; /* CompletionFilter (for multishot) */ int multishot; - struct file * dentry; + struct file * pfile; }; #define MID_FREE 0 -- cgit 1.2.3-korg From a9d02ad49013c8fc527f06ca66417103cdbb08b6 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 24 Aug 2005 23:06:05 -0700 Subject: [CIFS] Support for legacy servers part 3 - Add support for Open and most of Read support. Signed-off-by: Steve French --- fs/cifs/cifspdu.h | 73 ++++++++++++++++++ fs/cifs/cifsproto.h | 8 ++ fs/cifs/cifssmb.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/dir.c | 7 ++ fs/cifs/file.c | 29 ++++++- 5 files changed, 330 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 026c88f486a23f..cf466595b0d400 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -40,6 +40,7 @@ #define SMB_COM_SETATTR 0x09 /* trivial response */ #define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ #define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ #define SMB_COM_READ_ANDX 0x2E #define SMB_COM_WRITE_ANDX 0x2F #define SMB_COM_TRANSACTION2 0x32 @@ -625,6 +626,7 @@ typedef struct smb_com_findclose_req { } FINDCLOSE_REQ; /* OpenFlags */ +#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ #define REQ_OPLOCK 0x00000002 #define REQ_BATCHOPLOCK 0x00000004 #define REQ_OPENDIRONLY 0x00000008 @@ -680,6 +682,62 @@ typedef struct smb_com_open_rsp { __u16 ByteCount; /* bct = 0 */ } OPEN_RSP; +/* format of legacy open request */ +typedef struct smb_com_openx_req { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OpenFlags; + __le16 Mode; + __le16 Sattr; /* search attributes */ + __le16 FileAttributes; /* dos attrs */ + __le32 CreateTime; /* os2 format */ + __le16 OpenFunction; + __le32 EndOfFile; + __le32 Timeout; + __le32 Reserved; + __u16 ByteCount; /* file name follows */ + char fileName[1]; +} OPENX_REQ; + +typedef struct smb_com_openx_rsp { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le16 FileAttributes; + __le32 LastWriteTime; /* os2 format */ + __le32 EndOfFile; + __le16 Access; + __le16 FileType; + __le16 IPCState; + __le16 Action; + __u32 FileId; + __u16 Reserved; + __u16 ByteCount; +} OPENX_RSP; + +/* Legacy write request for older servers */ +typedef struct smb_com_writex_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; /* Timeout */ + __le16 WriteMode; /* 1 = write through */ + __le16 Remaining; + __le16 Reserved2; + __le16 DataLengthLow; + __le16 DataOffset; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ + char Data[0]; +} WRITEX_REQ; + typedef struct smb_com_write_req { struct smb_hdr hdr; /* wct = 14 */ __u8 AndXCommand; @@ -711,6 +769,21 @@ typedef struct smb_com_write_rsp { __u16 ByteCount; } WRITE_RSP; +/* legacy read request for older servers */ +typedef struct smb_com_readx_req { + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 Reserved; + __le16 Remaining; + __le16 ByteCount; +} READX_REQ; + typedef struct smb_com_read_req { struct smb_hdr hdr; /* wct = 12 */ __u8 AndXCommand; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 28b1ebbd380156..c411f2e001aabd 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -218,9 +218,17 @@ extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, const int access_flags, const int omode, __u16 * netfid, int *pOplock, FILE_ALL_INFO *, const struct nls_table *nls_codepage, int remap); +extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, + const char *fileName, const int disposition, + const int access_flags, const int omode, + __u16 * netfid, int *pOplock, FILE_ALL_INFO *, + const struct nls_table *nls_codepage, int remap); extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, const int smb_file_id); +extern int SMBLegacyRead(const int xid, struct cifsTconInfo *tcon, + const int netfid, unsigned int count, + const __u64 lseek, unsigned int *nbytes, char **buf); extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, unsigned int count, const __u64 lseek, unsigned int *nbytes, char **buf); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 67a6240ff2ba8d..c8ae3ef422baa4 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -680,6 +680,146 @@ MkDirRetry: return rc; } +static __u16 convert_disposition(int disposition) +{ + __u16 ofun = 0; + + switch (disposition) { + case FILE_SUPERSEDE: + ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; + break; + case FILE_OPEN: + ofun = SMBOPEN_OAPPEND; + break; + case FILE_CREATE: + ofun = SMBOPEN_OCREATE; + break; + case FILE_OPEN_IF: + ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND; + break; + case FILE_OVERWRITE: + ofun = SMBOPEN_OTRUNC; + break; + case FILE_OVERWRITE_IF: + ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; + break; + default: + cFYI(1,("unknown disposition %d",disposition)); + ofun = SMBOPEN_OAPPEND; /* regular open */ + } + return ofun; +} + +int +SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, + const char *fileName, const int openDisposition, + const int access_flags, const int create_options, __u16 * netfid, + int *pOplock, FILE_ALL_INFO * pfile_info, + const struct nls_table *nls_codepage, int remap) +{ + int rc = -EACCES; + OPENX_REQ *pSMB = NULL; + OPENX_RSP *pSMBr = NULL; + int bytes_returned; + int name_len; + __u16 count; + +OldOpenRetry: + rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->AndXCommand = 0xFF; /* none */ + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + count = 1; /* account for one byte pad to word boundary */ + name_len = + cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), + fileName, PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB improve check for buffer overruns BB */ + count = 0; /* no pad */ + name_len = strnlen(fileName, PATH_MAX); + name_len++; /* trailing null */ + strncpy(pSMB->fileName, fileName, name_len); + } + if (*pOplock & REQ_OPLOCK) + pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK); + else if (*pOplock & REQ_BATCHOPLOCK) { + pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK); + } + pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO); + /* BB fixme add conversion for access_flags to bits 0 - 2 of mode */ + /* 0 = read + 1 = write + 2 = rw + 3 = execute + */ + pSMB->Mode = cpu_to_le16(2); + pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ + /* set file as system file if special file such + as fifo and server expecting SFU style and + no Unix extensions */ + + if(create_options & CREATE_OPTION_SPECIAL) + pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); + else + pSMB->FileAttributes = cpu_to_le16(ATTR_NORMAL); + + /* if ((omode & S_IWUGO) == 0) + pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/ + /* Above line causes problems due to vfs splitting create into two + pieces - need to set mode after file created not while it is + being created */ + + /* BB FIXME BB */ +/* pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */ + /* BB FIXME END BB */ + pSMB->OpenFunction = convert_disposition(openDisposition); + count += name_len; + pSMB->hdr.smb_buf_length += count; + + pSMB->ByteCount = cpu_to_le16(count); + /* long_op set to 1 to allow for oplock break timeouts */ + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 1); + cifs_stats_inc(&tcon->num_opens); + if (rc) { + cFYI(1, ("Error in Open = %d", rc)); + } else { + /* BB verify if wct == 15 */ + +/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field BB */ + + *netfid = pSMBr->Fid; /* cifs fid stays in le */ + /* Let caller know file was created so we can set the mode. */ + /* Do we care about the CreateAction in any other cases? */ + /* BB FIXME BB */ +/* if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) + *pOplock |= CIFS_CREATE_ACTION; */ + /* BB FIXME END */ + + if(pfile_info) { + pfile_info->CreationTime = 0; /* BB convert CreateTime*/ + pfile_info->LastAccessTime = 0; /* BB fixme */ + pfile_info->LastWriteTime = 0; /* BB fixme */ + pfile_info->ChangeTime = 0; /* BB fixme */ + pfile_info->Attributes = pSMBr->FileAttributes; + /* the file_info buf is endian converted by caller */ + pfile_info->AllocationSize = pSMBr->EndOfFile; + pfile_info->EndOfFile = pSMBr->EndOfFile; + pfile_info->NumberOfLinks = cpu_to_le32(1); + } + } + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto OldOpenRetry; + return rc; +} + int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int openDisposition, @@ -783,6 +923,81 @@ openRetry: return rc; } +int +SMBLegacyRead(const int xid, struct cifsTconInfo *tcon, + const int netfid, unsigned int count, + const __u64 lseek, unsigned int *nbytes, char **buf) +{ + int rc = -EACCES; + READX_REQ *pSMB = NULL; + READ_RSP *pSMBr = NULL; + char *pReadData = NULL; + int bytes_returned; + + cFYI(1,("Legacy read %d bytes fid %d",count,netfid)); + + /* field is shorter in legacy read, only 16 bits */ + if(count > 2048) + count = 2048; /* BB FIXME make this configurable */ + + if(lseek > 0xFFFFFFFF) + return -EIO; /* can not read that far into file on old server */ + + *nbytes = 0; + rc = smb_init(SMB_COM_READ_ANDX, 10, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); + pSMB->Remaining = 0; + pSMB->MaxCount = cpu_to_le16(count); + pSMB->Reserved = 0; /* Must Be Zero */ + pSMB->ByteCount = 0; /* no need to do le conversion since it is 0 */ + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_reads); + if (rc) { + cERROR(1, ("Send error in legacy read = %d", rc)); + } else { + int data_length = le16_to_cpu(pSMBr->DataLengthHigh); + data_length = data_length << 16; + data_length += le16_to_cpu(pSMBr->DataLength); + *nbytes = data_length; + + /*check that DataLength would not go beyond end of SMB */ + if ((data_length > CIFSMaxBufSize) || (data_length > count)) { + cFYI(1,("bad length %d for count %d",data_length,count)); + rc = -EIO; + *nbytes = 0; + } else { + pReadData = (char *) (&pSMBr->hdr.Protocol) + + le16_to_cpu(pSMBr->DataOffset); +/* if(rc = copy_to_user(buf, pReadData, data_length)) { + cERROR(1,("Faulting on read rc = %d",rc)); + rc = -EFAULT; + }*/ /* can not use copy_to_user when using page cache*/ + if(*buf) + memcpy(*buf,pReadData,data_length); + } + } + if(*buf) + cifs_buf_release(pSMB); + else + *buf = (char *)pSMB; + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + return rc; +} + /* If no buffer passed in, then caller wants to do the copy as in the case of readpages so the SMB buffer must be freed by the caller */ diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 5311c50734b0a2..248ddebd67f4f5 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -184,6 +184,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, desiredAccess, CREATE_NOT_DIR, &fileHandle, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + if(rc == -EIO) { + /* old server, retry the open legacy style */ + rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, + desiredAccess, CREATE_NOT_DIR, + &fileHandle, &oplock, buf, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + } if (rc) { cFYI(1, ("cifs_create returned 0x%x ", rc)); } else { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 5857d12611e601..8ae962e7c93f48 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -256,6 +256,13 @@ int cifs_open(struct inode *inode, struct file *file) CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + if (rc == -EIO) { + /* Old server, try legacy style OpenX */ + rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, + desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags + & CIFS_MOUNT_MAP_SPECIAL_CHR); + } if (rc) { cFYI(1, ("cifs_open returned 0x%x ", rc)); goto out; @@ -1210,7 +1217,12 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, open_file->netfid, current_read_size, *poffset, &bytes_read, &smb_read_data); - + if(rc == -EINVAL) { + rc = SMBLegacyRead(xid, pTcon, + open_file->netfid, + current_read_size, *poffset, + &bytes_read, &smb_read_data); + } pSMBr = (struct smb_com_read_rsp *)smb_read_data; if (copy_to_user(current_offset, smb_read_data + 4 /* RFC1001 hdr */ @@ -1287,6 +1299,12 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, open_file->netfid, current_read_size, *poffset, &bytes_read, ¤t_offset); + if(rc == -EINVAL) { + rc = SMBLegacyRead(xid, pTcon, + open_file->netfid, + current_read_size, *poffset, + &bytes_read, ¤t_offset); + } } if (rc || (bytes_read == 0)) { if (total_read) { @@ -1443,7 +1461,14 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, open_file->netfid, read_size, offset, &bytes_read, &smb_read_data); - /* BB need to check return code here */ + if (rc == -EINVAL) { + rc = SMBLegacyRead(xid, pTcon, + open_file->netfid, + read_size, offset, + &bytes_read, &smb_read_data); + } + + /* BB more RC checks ? */ if (rc== -EAGAIN) { if (smb_read_data) { cifs_buf_release(smb_read_data); -- cgit 1.2.3-korg From 7f57356b70dda014ef269135942426e4a852023e Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 30 Aug 2005 11:32:14 -0700 Subject: [CIFS] Remove cifs_sb argument from *build_path_from_dentry This argument was added in a recent patch, but is unnecessary, since the superblock is easily obtained from the dentry. Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 +- fs/cifs/dir.c | 11 ++++++----- fs/cifs/fcntl.c | 2 +- fs/cifs/file.c | 4 ++-- fs/cifs/inode.c | 14 +++++++------- fs/cifs/link.c | 15 +++++++-------- fs/cifs/readdir.c | 2 +- fs/cifs/xattr.c | 8 ++++---- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index c411f2e001aabd..656b78ddf6743a 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -40,7 +40,7 @@ extern unsigned int _GetXid(void); extern void _FreeXid(unsigned int); #define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__FUNCTION__, xid,current->fsuid)); #define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__FUNCTION__,curr_xid,(int)rc));} -extern char *build_path_from_dentry(struct dentry *, const struct cifs_sb_info *cifs_sb); +extern char *build_path_from_dentry(struct dentry *); extern char *build_wildcard_path_from_dentry(struct dentry *direntry); extern void renew_parental_timestamps(struct dentry *direntry); extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 248ddebd67f4f5..cf90c9ad2c870f 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -43,11 +43,12 @@ renew_parental_timestamps(struct dentry *direntry) /* Note: caller must free return buffer */ char * -build_path_from_dentry(struct dentry *direntry, const struct cifs_sb_info *cifs_sb) +build_path_from_dentry(struct dentry *direntry) { struct dentry *temp; int namelen = 0; char *full_path; + char dirsep = CIFS_DIR_SEP(CIFS_SB(direntry->d_sb)); if(direntry == NULL) return NULL; /* not much we can do if dentry is freed and @@ -74,7 +75,7 @@ cifs_bp_rename_retry: if (namelen < 0) { break; } else { - full_path[namelen] = CIFS_DIR_SEP(cifs_sb); + full_path[namelen] = dirsep; strncpy(full_path + namelen + 1, temp->d_name.name, temp->d_name.len); cFYI(0, (" name: %s ", full_path + namelen)); @@ -138,7 +139,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, pTcon = cifs_sb->tcon; down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&direntry->d_sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -310,7 +311,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev pTcon = cifs_sb->tcon; down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&direntry->d_sb->s_vfs_rename_sem); if(full_path == NULL) rc = -ENOMEM; @@ -409,7 +410,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct name /* can not grab the rename sem here since it would deadlock in the cases (beginning of sys_rename itself) in which we already have the sb rename sem */ - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); if(full_path == NULL) { FreeXid(xid); return ERR_PTR(-ENOMEM); diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index 2e5137b7352a48..d527e2c76073e6 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -83,7 +83,7 @@ int cifs_dir_notify(struct file * file, unsigned long arg) pTcon = cifs_sb->tcon; down(&file->f_dentry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(file->f_dentry, cifs_sb); + full_path = build_path_from_dentry(file->f_dentry); up(&file->f_dentry->d_sb->s_vfs_rename_sem); if(full_path == NULL) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 8ae962e7c93f48..026b5c5ccc897f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -196,7 +196,7 @@ int cifs_open(struct inode *inode, struct file *file) } down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(file->f_dentry, cifs_sb); + full_path = build_path_from_dentry(file->f_dentry); up(&inode->i_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); @@ -366,7 +366,7 @@ static int cifs_reopen_file(struct inode *inode, struct file *file, those that already have the rename sem can end up causing writepage to get called and if the server was down that means we end up here, and we can never tell if the caller already has the rename_sem */ - full_path = build_path_from_dentry(file->f_dentry, cifs_sb); + full_path = build_path_from_dentry(file->f_dentry); if (full_path == NULL) { up(&pCifsFile->fh_sem); FreeXid(xid); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 34f0168c4041dd..0485c6d6ecd525 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -436,7 +436,7 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) /* Unlink can be called from rename so we can not grab the sem here since we deadlock otherwise */ /* down(&direntry->d_sb->s_vfs_rename_sem);*/ - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); /* up(&direntry->d_sb->s_vfs_rename_sem);*/ if (full_path == NULL) { FreeXid(xid); @@ -580,7 +580,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) pTcon = cifs_sb->tcon; down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&inode->i_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); @@ -654,7 +654,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) pTcon = cifs_sb->tcon; down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&inode->i_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); @@ -707,8 +707,8 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, /* we already have the rename sem so we do not need to grab it again here to protect the path integrity */ - fromName = build_path_from_dentry(source_direntry, cifs_sb_source); - toName = build_path_from_dentry(target_direntry, cifs_sb_target); + fromName = build_path_from_dentry(source_direntry); + toName = build_path_from_dentry(target_direntry); if ((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; goto cifs_rename_exit; @@ -824,7 +824,7 @@ int cifs_revalidate(struct dentry *direntry) /* can not safely grab the rename sem here if rename calls revalidate since that would deadlock */ - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; @@ -973,7 +973,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) pTcon = cifs_sb->tcon; down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&direntry->d_sb->s_vfs_rename_sem); if (full_path == NULL) { FreeXid(xid); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index b8ec6646456a74..b43e071fe11064 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -49,8 +49,8 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, BB note DFS case in future though (when we may have to check) */ down(&inode->i_sb->s_vfs_rename_sem); - fromName = build_path_from_dentry(old_file, cifs_sb_target); - toName = build_path_from_dentry(direntry, cifs_sb_target); + fromName = build_path_from_dentry(old_file); + toName = build_path_from_dentry(direntry); up(&inode->i_sb->s_vfs_rename_sem); if((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; @@ -105,17 +105,16 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; - down(&direntry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&direntry->d_sb->s_vfs_rename_sem); if (!full_path) goto out_no_free; cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode)); + cifs_sb = CIFS_SB(inode->i_sb); + pTcon = cifs_sb->tcon; target_path = kmalloc(PATH_MAX, GFP_KERNEL); if (!target_path) { target_path = ERR_PTR(-ENOMEM); @@ -168,7 +167,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) pTcon = cifs_sb->tcon; down(&inode->i_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&inode->i_sb->s_vfs_rename_sem); if(full_path == NULL) { @@ -237,7 +236,7 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) /* BB would it be safe against deadlock to grab this sem even though rename itself grabs the sem and calls lookup? */ /* down(&inode->i_sb->s_vfs_rename_sem);*/ - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); /* up(&inode->i_sb->s_vfs_rename_sem);*/ if(full_path == NULL) { diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index f769292e2a93db..9780f4ee7f1285 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -368,7 +368,7 @@ static int initiate_cifs_search(const int xid, struct file *file) return -EINVAL; down(&file->f_dentry->d_sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(file->f_dentry, cifs_sb); + full_path = build_path_from_dentry(file->f_dentry); up(&file->f_dentry->d_sb->s_vfs_rename_sem); if(full_path == NULL) { diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index f4fc8ddebba75d..c1e02eff1d25a2 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -63,7 +63,7 @@ int cifs_removexattr(struct dentry * direntry, const char * ea_name) pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -118,7 +118,7 @@ int cifs_setxattr(struct dentry * direntry, const char * ea_name, pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -227,7 +227,7 @@ ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); @@ -328,7 +328,7 @@ ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size) pTcon = cifs_sb->tcon; down(&sb->s_vfs_rename_sem); - full_path = build_path_from_dentry(direntry, cifs_sb); + full_path = build_path_from_dentry(direntry); up(&sb->s_vfs_rename_sem); if(full_path == NULL) { FreeXid(xid); -- cgit 1.2.3-korg From 16abbecdad3367c76c12537450eba0d86943fe2c Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 30 Aug 2005 13:10:14 -0700 Subject: [CIFS] Add support for suspend cifsd had been preventing software suspend from completing. Signed-off-by: pavel@suse.de Signed-off-by: Steve French lightly modified --- fs/cifs/CHANGES | 3 ++- fs/cifs/cifsfs.c | 4 ++++ fs/cifs/connect.c | 2 ++ 3 files changed, 8 insertions(+), 1 deletions(-) --- fs/cifs/CHANGES | 3 ++- fs/cifs/cifsfs.c | 4 ++++ fs/cifs/connect.c | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 340b4ffb3493bc..6c73f020ef400b 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -2,7 +2,8 @@ Version 1.36 ------------ Add support for mounting to older pre-CIFS servers such as Windows9x and ME. For these older servers, add option for passing netbios name of server in -on mount (servernetbiosname). +on mount (servernetbiosname). Add suspend support for power management, to +avoid cifsd thread preventing software suspend from working. Add mount option for disabling the default behavior of sending byte range lock requests to the server (necessary for certain applications which break with mandatory lock behavior such as Evolution), and also mount option for diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d77abe236a670b..43fb2aafa528ce 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -784,6 +784,8 @@ static int cifs_oplock_thread(void * dummyarg) oplockThread = current; do { + if(try_to_freeze()) + continue; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1*HZ); @@ -848,6 +850,8 @@ static int cifs_dnotify_thread(void * dummyarg) dnotifyThread = current; do { + if(try_to_freeze()) + continue; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(39*HZ); } while(!signal_pending(current)); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c75bae1242dcce..134195cc40731c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -355,6 +355,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } while (server->tcpStatus != CifsExiting) { + if(try_to_freeze()) + continue; if (bigbuf == NULL) { bigbuf = cifs_buf_get(); if(bigbuf == NULL) { -- cgit 1.2.3-korg From cb8be64084e6294fcb9e558188fe104050b94f0b Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 30 Aug 2005 15:25:52 -0700 Subject: [CIFS] Add nolock synonym (ala nfs) for nobrl to disable sending byte range locks remotely. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/connect.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 134195cc40731c..f784b70abfeb5b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1078,7 +1078,8 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->nocase = 1; } else if (strnicmp(data, "brl", 3) == 0) { vol->nobrl = 0; - } else if (strnicmp(data, "nobrl", 5) == 0) { + } else if ((strnicmp(data, "nobrl", 5) == 0) || + (strnicmp(data, "nolock", 6)) { vol->nobrl = 1; /* turn off mandatory locking in mode if remote locking is turned off since the -- cgit 1.2.3-korg From 1c9551878c4629ca78dfe12ed23b9dc8d97770cc Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 30 Aug 2005 20:58:07 -0700 Subject: [CIFS] Add support for legacy servers part 4 Fix WriteX support for old servers which do not support large files. Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 27 +++++++++++++++++++---- fs/cifs/connect.c | 2 +- fs/cifs/file.c | 66 ++++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index c8ae3ef422baa4..74733851cfad5f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1082,12 +1082,20 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, int rc = -EACCES; WRITE_REQ *pSMB = NULL; WRITE_RSP *pSMBr = NULL; - int bytes_returned; + int bytes_returned, wct; __u32 bytes_sent; __u16 byte_count; /* cFYI(1,("write at %lld %d bytes",offset,count));*/ - rc = smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB, + if(tcon->ses == NULL) + return -ECONNABORTED; + + if(tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 14; + else + wct = 12; + + rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; @@ -1098,7 +1106,11 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + if(wct == 14) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + else if((offset >> 32) > 0) /* can not handle this big offset for old */ + return -EIO; + pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; @@ -1135,7 +1147,14 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); pSMB->hdr.smb_buf_length += bytes_sent+1; - pSMB->ByteCount = cpu_to_le16(byte_count); + + if(wct == 14) + pSMB->ByteCount = cpu_to_le16(byte_count); + else { /* old style write has byte count 4 bytes earlier */ + struct smb_com_writex_req * pSMBW = + (struct smb_com_writex_req *)pSMB; + pSMBW->ByteCount = cpu_to_le16(byte_count); + } rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, long_op); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f784b70abfeb5b..196976049c00e1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1079,7 +1079,7 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) } else if (strnicmp(data, "brl", 3) == 0) { vol->nobrl = 0; } else if ((strnicmp(data, "nobrl", 5) == 0) || - (strnicmp(data, "nolock", 6)) { + (strnicmp(data, "nolock", 6) == 0)) { vol->nobrl = 1; /* turn off mandatory locking in mode if remote locking is turned off since the diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ef455dda0473c2..b6c303f6373f05 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1183,11 +1183,16 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, char *smb_read_data; char __user *current_offset; struct smb_com_read_rsp *pSMBr; + int use_old_read = FALSE; xid = GetXid(); cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; + if(pTcon->ses) + if((pTcon->ses->capabilities & CAP_LARGE_FILES) == 0) + use_old_read = TRUE; + if (file->private_data == NULL) { FreeXid(xid); return -EBADF; @@ -1212,16 +1217,21 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, if (rc != 0) break; } - - rc = CIFSSMBRead(xid, pTcon, - open_file->netfid, - current_read_size, *poffset, - &bytes_read, &smb_read_data); - if(rc == -EINVAL) { + if(use_old_read) rc = SMBLegacyRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, &smb_read_data); + else { + rc = CIFSSMBRead(xid, pTcon, + open_file->netfid, + current_read_size, *poffset, + &bytes_read, &smb_read_data); + if(rc == -EINVAL) { + use_old_read = TRUE; + rc = -EAGAIN; + continue; + } } pSMBr = (struct smb_com_read_rsp *)smb_read_data; if (copy_to_user(current_offset, @@ -1266,6 +1276,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, int xid; char *current_offset; struct cifsFileInfo *open_file; + int use_old_read = FALSE; xid = GetXid(); cifs_sb = CIFS_SB(file->f_dentry->d_sb); @@ -1276,6 +1287,9 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; + if(pTcon->ses) + if((pTcon->ses->capabilities & CAP_LARGE_FILES) == 0) + use_old_read = TRUE; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, ("attempting read on write only file instance")); @@ -1294,16 +1308,23 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, if (rc != 0) break; } - - rc = CIFSSMBRead(xid, pTcon, - open_file->netfid, - current_read_size, *poffset, - &bytes_read, ¤t_offset); - if(rc == -EINVAL) { + if(use_old_read) rc = SMBLegacyRead(xid, pTcon, + open_file->netfid, + current_read_size, *poffset, + &bytes_read, ¤t_offset); + else { + rc = CIFSSMBRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, ¤t_offset); + /* check if server disavows support for + 64 bit offsets */ + if(rc == -EINVAL) { + rc = -EAGAIN; + use_old_read = TRUE; + continue; + } } } if (rc || (bytes_read == 0)) { @@ -1402,6 +1423,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, struct smb_com_read_rsp *pSMBr; struct pagevec lru_pvec; struct cifsFileInfo *open_file; + int use_old_read = FALSE; xid = GetXid(); if (file->private_data == NULL) { @@ -1411,7 +1433,9 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, open_file = (struct cifsFileInfo *)file->private_data; cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; - + if(pTcon->ses) + if((pTcon->ses->capabilities & CAP_LARGE_FILES) == 0) + use_old_read = TRUE; pagevec_init(&lru_pvec, 0); for (i = 0; i < num_pages; ) { @@ -1457,15 +1481,21 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, break; } - rc = CIFSSMBRead(xid, pTcon, - open_file->netfid, - read_size, offset, - &bytes_read, &smb_read_data); - if (rc == -EINVAL) { + if(use_old_read) rc = SMBLegacyRead(xid, pTcon, open_file->netfid, read_size, offset, &bytes_read, &smb_read_data); + else { + rc = CIFSSMBRead(xid, pTcon, + open_file->netfid, + read_size, offset, + &bytes_read, &smb_read_data); + if(rc == -EINVAL) { + use_old_read = TRUE; + rc = -EAGAIN; + continue; + } } /* BB more RC checks ? */ -- cgit 1.2.3-korg From bfa0d75a1eee59f0577e3c1697ff570b77581a35 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 31 Aug 2005 21:50:37 -0700 Subject: [CIFS] Add support for legacy servers part 5 Handle small negotiated read sizes (under 4K) and finish up read and write support. Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 3 -- fs/cifs/cifssmb.c | 99 +++++++++++------------------------------------------ fs/cifs/file.c | 58 +++---------------------------- fs/cifs/inode.c | 7 ++++ fs/cifs/readdir.c | 9 ++++- 5 files changed, 39 insertions(+), 137 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 656b78ddf6743a..6943f7c6de0846 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -226,9 +226,6 @@ extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, const int smb_file_id); -extern int SMBLegacyRead(const int xid, struct cifsTconInfo *tcon, - const int netfid, unsigned int count, - const __u64 lseek, unsigned int *nbytes, char **buf); extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, unsigned int count, const __u64 lseek, unsigned int *nbytes, char **buf); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 74733851cfad5f..b8830118f09a0f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -923,81 +923,6 @@ openRetry: return rc; } -int -SMBLegacyRead(const int xid, struct cifsTconInfo *tcon, - const int netfid, unsigned int count, - const __u64 lseek, unsigned int *nbytes, char **buf) -{ - int rc = -EACCES; - READX_REQ *pSMB = NULL; - READ_RSP *pSMBr = NULL; - char *pReadData = NULL; - int bytes_returned; - - cFYI(1,("Legacy read %d bytes fid %d",count,netfid)); - - /* field is shorter in legacy read, only 16 bits */ - if(count > 2048) - count = 2048; /* BB FIXME make this configurable */ - - if(lseek > 0xFFFFFFFF) - return -EIO; /* can not read that far into file on old server */ - - *nbytes = 0; - rc = smb_init(SMB_COM_READ_ANDX, 10, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) - return -ECONNABORTED; - - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; - pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); - pSMB->Remaining = 0; - pSMB->MaxCount = cpu_to_le16(count); - pSMB->Reserved = 0; /* Must Be Zero */ - pSMB->ByteCount = 0; /* no need to do le conversion since it is 0 */ - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->num_reads); - if (rc) { - cERROR(1, ("Send error in legacy read = %d", rc)); - } else { - int data_length = le16_to_cpu(pSMBr->DataLengthHigh); - data_length = data_length << 16; - data_length += le16_to_cpu(pSMBr->DataLength); - *nbytes = data_length; - - /*check that DataLength would not go beyond end of SMB */ - if ((data_length > CIFSMaxBufSize) || (data_length > count)) { - cFYI(1,("bad length %d for count %d",data_length,count)); - rc = -EIO; - *nbytes = 0; - } else { - pReadData = (char *) (&pSMBr->hdr.Protocol) + - le16_to_cpu(pSMBr->DataOffset); -/* if(rc = copy_to_user(buf, pReadData, data_length)) { - cERROR(1,("Faulting on read rc = %d",rc)); - rc = -EFAULT; - }*/ /* can not use copy_to_user when using page cache*/ - if(*buf) - memcpy(*buf,pReadData,data_length); - } - } - if(*buf) - cifs_buf_release(pSMB); - else - *buf = (char *)pSMB; - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - return rc; -} - /* If no buffer passed in, then caller wants to do the copy as in the case of readpages so the SMB buffer must be freed by the caller */ @@ -1012,11 +937,16 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, READ_RSP *pSMBr = NULL; char *pReadData = NULL; int bytes_returned; + int wct; cFYI(1,("Reading %d bytes on fid %d",count,netfid)); + if(tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 12; + else + wct = 10; /* old style read */ *nbytes = 0; - rc = smb_init(SMB_COM_READ_ANDX, 12, tcon, (void **) &pSMB, + rc = smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; @@ -1028,12 +958,23 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); - pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); + if(wct == 12) + pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); + else if((lseek >> 32) > 0) /* can not handle this big offset for old */ + return -EIO; + pSMB->Remaining = 0; pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); pSMB->MaxCountHigh = cpu_to_le32(count >> 16); - pSMB->ByteCount = 0; /* no need to do le conversion since it is 0 */ - + if(wct == 12) + pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ + else { + /* old style read */ + struct smb_com_readx_req * pSMBW = + (struct smb_com_readx_req *)pSMB; + pSMBW->ByteCount = 0; + } + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_reads); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b6c303f6373f05..5ecda554f9130f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1183,16 +1183,11 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, char *smb_read_data; char __user *current_offset; struct smb_com_read_rsp *pSMBr; - int use_old_read = FALSE; xid = GetXid(); cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; - if(pTcon->ses) - if((pTcon->ses->capabilities & CAP_LARGE_FILES) == 0) - use_old_read = TRUE; - if (file->private_data == NULL) { FreeXid(xid); return -EBADF; @@ -1217,22 +1212,10 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, if (rc != 0) break; } - if(use_old_read) - rc = SMBLegacyRead(xid, pTcon, - open_file->netfid, - current_read_size, *poffset, - &bytes_read, &smb_read_data); - else { - rc = CIFSSMBRead(xid, pTcon, + rc = CIFSSMBRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, &smb_read_data); - if(rc == -EINVAL) { - use_old_read = TRUE; - rc = -EAGAIN; - continue; - } - } pSMBr = (struct smb_com_read_rsp *)smb_read_data; if (copy_to_user(current_offset, smb_read_data + 4 /* RFC1001 hdr */ @@ -1276,7 +1259,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, int xid; char *current_offset; struct cifsFileInfo *open_file; - int use_old_read = FALSE; xid = GetXid(); cifs_sb = CIFS_SB(file->f_dentry->d_sb); @@ -1287,9 +1269,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; - if(pTcon->ses) - if((pTcon->ses->capabilities & CAP_LARGE_FILES) == 0) - use_old_read = TRUE; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, ("attempting read on write only file instance")); @@ -1308,24 +1287,10 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, if (rc != 0) break; } - if(use_old_read) - rc = SMBLegacyRead(xid, pTcon, - open_file->netfid, - current_read_size, *poffset, - &bytes_read, ¤t_offset); - else { - rc = CIFSSMBRead(xid, pTcon, + rc = CIFSSMBRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, ¤t_offset); - /* check if server disavows support for - 64 bit offsets */ - if(rc == -EINVAL) { - rc = -EAGAIN; - use_old_read = TRUE; - continue; - } - } } if (rc || (bytes_read == 0)) { if (total_read) { @@ -1423,7 +1388,6 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, struct smb_com_read_rsp *pSMBr; struct pagevec lru_pvec; struct cifsFileInfo *open_file; - int use_old_read = FALSE; xid = GetXid(); if (file->private_data == NULL) { @@ -1433,9 +1397,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, open_file = (struct cifsFileInfo *)file->private_data; cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; - if(pTcon->ses) - if((pTcon->ses->capabilities & CAP_LARGE_FILES) == 0) - use_old_read = TRUE; + pagevec_init(&lru_pvec, 0); for (i = 0; i < num_pages; ) { @@ -1481,22 +1443,10 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, break; } - if(use_old_read) - rc = SMBLegacyRead(xid, pTcon, - open_file->netfid, - read_size, offset, - &bytes_read, &smb_read_data); - else { - rc = CIFSSMBRead(xid, pTcon, + rc = CIFSSMBRead(xid, pTcon, open_file->netfid, read_size, offset, &bytes_read, &smb_read_data); - if(rc == -EINVAL) { - use_old_read = TRUE; - rc = -EAGAIN; - continue; - } - } /* BB more RC checks ? */ if (rc== -EAGAIN) { diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 0485c6d6ecd525..0fbe02ebc03354 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -169,6 +169,10 @@ int cifs_get_inode_info_unix(struct inode **pinode, if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) inode->i_fop->lock = NULL; inode->i_data.a_ops = &cifs_addr_ops; + /* check if server can support readpages */ + if(pTcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE) + inode->i_data.a_ops->readpages = NULL; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode")); inode->i_op = &cifs_dir_inode_ops; @@ -384,6 +388,9 @@ int cifs_get_inode_info(struct inode **pinode, if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) inode->i_fop->lock = NULL; inode->i_data.a_ops = &cifs_addr_ops; + if(pTcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE) + inode->i_data.a_ops->readpages = NULL; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode ")); inode->i_op = &cifs_dir_inode_ops; diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 9780f4ee7f1285..a1e8dc901de46d 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -200,7 +200,10 @@ static void fill_in_inode(struct inode *tmp_inode, if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) tmp_inode->i_fop->lock = NULL; tmp_inode->i_data.a_ops = &cifs_addr_ops; - + if((cifs_sb->tcon) && (cifs_sb->tcon->ses) && + (cifs_sb->tcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE)) + tmp_inode->i_data.a_ops->readpages = NULL; if(isNewInode) return; /* No sense invalidating pages for new inode since have not started caching readahead file @@ -306,6 +309,10 @@ static void unix_fill_in_inode(struct inode *tmp_inode, if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) tmp_inode->i_fop->lock = NULL; tmp_inode->i_data.a_ops = &cifs_addr_ops; + if((cifs_sb->tcon) && (cifs_sb->tcon->ses) && + (cifs_sb->tcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE)) + tmp_inode->i_data.a_ops->readpages = NULL; if(isNewInode) return; /* No sense invalidating pages for new inode since we -- cgit 1.2.3-korg From 9a899e76683639486846ce17dbaa0c2ec1ae5ab5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 6 Sep 2005 15:55:49 -0700 Subject: [CIFS] Update cifs TODO list with additional new features that have been requested. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/TODO | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/fs/cifs/TODO b/fs/cifs/TODO index 8cc881694e2911..0593f644731934 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -1,4 +1,4 @@ -version 1.34 April 29, 2005 +version 1.36 September 6, 2005 A Partial List of Missing Features ================================== @@ -7,14 +7,14 @@ Contributions are welcome. There are plenty of opportunities for visible, important contributions to this module. Here is a partial list of the known problems and missing features: -a) Support for SecurityDescriptors for chmod/chgrp/chown so -these can be supported for Windows servers +a) Support for SecurityDescriptors(Windows/CIFS ACLs) for chmod/chgrp/chown +so that these operations can be supported to Windows servers -b) Better pam/winbind integration (e.g. to handle uid mapping -better) +b) Mapping POSIX ACLs (and eventually NFSv4 ACLs) to CIFS +SecurityDescriptors -c) multi-user mounts - multiplexed sessionsetups over single vc -(ie tcp session) - more testing needed +c) Better pam/winbind integration (e.g. to handle uid mapping +better) d) Kerberos/SPNEGO session setup support - (started) @@ -29,12 +29,17 @@ f) Directory entry caching relies on a 1 second timer, rather than using FindNotify or equivalent. - (started) g) A few byte range testcases fail due to POSIX vs. Windows/CIFS -style byte range lock differences +style byte range lock differences. Save byte range locks so +reconnect can replay them. -h) quota support +h) Support unlock all (unlock 0,MAX_OFFSET) +by unlocking all known byte range locks that we locked on the file. + +i) quota support (needs minor kernel change since quota calls +to make it to network filesystems or deviceless filesystems) j) finish writepages support (multi-page write behind for improved -performance) and syncpage +performance) and syncpage. Started by Shaggy. k) hook lower into the sockets api (as NFS/SunRPC does) to avoid the extra copy in/out of the socket buffers in some cases. @@ -57,20 +62,18 @@ p) Add support for storing symlink and fifo info to Windows servers in the Extended Attribute format their SFU clients would recognize. q) Finish fcntl D_NOTIFY support so kde and gnome file list windows -will autorefresh (started) +will autorefresh (partially complete by Asser). Needs minor kernel +vfs change to support removing D_NOTIFY on a file. r) Add GUI tool to configure /proc/fs/cifs settings and for display of the CIFS statistics (started) -q) implement support for security and trusted categories of xattrs +s) implement support for security and trusted categories of xattrs (requires minor protocol extension) to enable better support for SELINUX -r) Implement O_DIRECT flag on open (already supported on mount) - -s) Allow remapping of last remaining character (\) to +0xF000 which -(this character is valid for POSIX but not for Windows) +t) Implement O_DIRECT flag on open (already supported on mount) -t) Create UID mapping facility so server UIDs can be mapped on a per +u) Create UID mapping facility so server UIDs can be mapped on a per mount or a per server basis to client UIDs or nobody if no mapping exists. This is helpful when Unix extensions are negotiated to allow better permission checking when UIDs differ on the server @@ -78,6 +81,17 @@ and client. Add new protocol request to the CIFS protocol standard for asking the server for the corresponding name of a particular uid. +v) Add support for CIFS Unix and also the newer POSIX extensions to the +server side for Samba 4. + +w) Finish up the dos time conversion routines needed to return old server +time to the client (default time, of now or time 0 is used now for these +very old servers) + +x) Add support for OS/2 (LANMAN 1.2 and LANMAN2.1 based SMB servers) + +y) Finish testing of Windows 9x/Windows ME server support (started). + KNOWN BUGS (updated April 29, 2005) ==================================== See http://bugzilla.samba.org - search on product "CifsVFS" for -- cgit 1.2.3-korg From ab2f218f4fa2c36ecd39ac1406eec1e63cd430bd Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 15 Sep 2005 20:44:50 -0700 Subject: [CIFS] Fix compiler warnings Fix some compiler warnings noticed on x64 by me and ppc64 by Shaggy Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/asn1.c | 3 ++- fs/cifs/cifsglob.h | 2 +- fs/cifs/file.c | 4 ++-- fs/cifs/transport.c | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c index e02010dd73ec58..98539e2afe81e9 100644 --- a/fs/cifs/asn1.c +++ b/fs/cifs/asn1.c @@ -191,7 +191,8 @@ asn1_header_decode(struct asn1_ctx *ctx, unsigned char **eoc, unsigned int *cls, unsigned int *con, unsigned int *tag) { - unsigned int def, len; + unsigned int def = 0; + unsigned int len = 0; if (!asn1_id_decode(ctx, cls, con, tag)) return 0; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a9c791edede595..cd421c76805e78 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -331,7 +331,7 @@ CIFS_SB(struct super_block *sb) return sb->s_fs_info; } -static inline const char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) +static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) return '/'; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 5ecda554f9130f..4173f23a71d96b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -791,8 +791,8 @@ static ssize_t cifs_write(struct file *file, const char *write_data, pTcon = cifs_sb->tcon; - cFYI(1,(" write %d bytes to offset %lld of %s", write_size, - *poffset, file->f_dentry->d_name.name)); /* BB removeme BB */ + cFYI(1,("write %zd bytes to offset %lld of %s", write_size, + *poffset, file->f_dentry->d_name.name)); if (file->private_data == NULL) return -EBADF; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 96f89eb6604025..d5e0c4018f92e3 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -485,7 +485,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, BCC(in_buf) = le16_to_cpu(BCC(in_buf)); } else { rc = -EIO; - cFYI(1,("Bad MID state? ")); + cFYI(1,("Bad MID state?")); } } cifs_no_response_exit2: -- cgit 1.2.3-korg From eafe87012159a40a1e7151cc576e99a22aea2f0b Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 15 Sep 2005 21:47:30 -0700 Subject: [CIFS] Fix readdir caching when unlink removes file in current search buffer, and this is followed by a rewind search to just before the deleted entry. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 5 +++++ fs/cifs/cifsfs.h | 2 +- fs/cifs/readdir.c | 37 ++++++++++++++++++++++++++++++++----- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 6c73f020ef400b..8b55e56cf1fec1 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,8 @@ +Version 1.37 +------------ +Fix readdir caching when unlink removes file in current search buffer, +and this is followed by a rewind search to just before the deleted entry. + Version 1.36 ------------ Add support for mounting to older pre-CIFS servers such as Windows9x and ME. diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index bb3404a99e5f9b..0f6d352ab813f7 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -97,5 +97,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); extern int cifs_ioctl (struct inode * inode, struct file * filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.36" +#define CIFS_VERSION "1.37" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index a1e8dc901de46d..a86bd1c076021b 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -396,7 +396,8 @@ ffirst_retry: rc = CIFSFindFirst(xid, pTcon,full_path,cifs_sb->local_nls, &cifsFile->netfid, &cifsFile->srch_inf, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); if(rc == 0) cifsFile->invalidHandle = FALSE; if((rc == -EOPNOTSUPP) && @@ -513,6 +514,30 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) return rc; } +/* Check if directory that we are searching has changed so we can decide + whether we can use the cached search results from the previous search */ +static int is_dir_changed(struct file * file) +{ + struct inode * inode; + struct cifsInodeInfo *cifsInfo; + + if(file->f_dentry == NULL) + return 0; + + inode = file->f_dentry->d_inode; + + if(inode == NULL) + return 0; + + cifsInfo = CIFS_I(inode); + + if(cifsInfo->time == 0) + return 1; /* directory was changed, perhaps due to unlink */ + else + return 0; + +} + /* find the corresponding entry in the search */ /* Note that the SMB server returns search entries for . and .. which complicates logic here if we choose to parse for them and we do not @@ -529,7 +554,8 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, struct cifsFileInfo * cifsFile = file->private_data; /* check if index in the buffer */ - if((cifsFile == NULL) || (ppCurrentEntry == NULL) || (num_to_ret == NULL)) + if((cifsFile == NULL) || (ppCurrentEntry == NULL) || + (num_to_ret == NULL)) return -ENOENT; *ppCurrentEntry = NULL; @@ -537,7 +563,9 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, cifsFile->srch_inf.index_of_last_entry - cifsFile->srch_inf.entries_in_buffer; /* dump_cifs_file_struct(file, "In fce ");*/ - if(index_to_find < first_entry_in_buffer) { + if(((index_to_find < cifsFile->srch_inf.index_of_last_entry) && + is_dir_changed(file)) || + (index_to_find < first_entry_in_buffer)) { /* close and restart search */ cFYI(1,("search backing up - close and restart search")); cifsFile->invalidHandle = TRUE; @@ -604,7 +632,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, } if(pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) { - cFYI(1,("can not return entries when pos_in_buf beyond last entry")); + cFYI(1,("can not return entries pos_in_buf beyond last entry")); *num_to_ret = 0; } else *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; @@ -833,7 +861,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) if(pTcon == NULL) return -EINVAL; - switch ((int) file->f_pos) { case 0: /*if (filldir(direntry, ".", 1, file->f_pos, -- cgit 1.2.3-korg From f9f5c81769f88bccd177423a30a7d30461754c39 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 15 Sep 2005 23:06:38 -0700 Subject: [CIFS] Add support for legacy servers part six. Fix read syntax so we do not request more than negotiated buffer size even if buffer size is small (smaller than one page) Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/file.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 4173f23a71d96b..3766db2bb7f28b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1278,6 +1278,13 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, total_read += bytes_read, current_offset += bytes_read) { current_read_size = min_t(const int, read_size - total_read, cifs_sb->rsize); + /* For windows me and 9x we do not want to request more + than it negotiated since it will refuse the read then */ + if((pTcon->ses) && + !(pTcon->ses->capabilities & CAP_LARGE_FILES)) { + current_read_size = min_t(const int, current_read_size, + pTcon->ses->server->maxBuf - 128); + } rc = -EAGAIN; while (rc == -EAGAIN) { if ((open_file->invalidHandle) && -- cgit 1.2.3-korg From 3e87d80391c84eefceb4bda94a6363661dba4f71 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 18 Sep 2005 20:49:21 -0700 Subject: [CIFS] Add support for legacy servers part seven. Fix open for write, begin implementation of Win9x style set file size via open then write of zero bytes. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifssmb.c | 10 ++++++---- fs/cifs/inode.c | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index b8830118f09a0f..575b2281518d59 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -766,7 +766,7 @@ OldOpenRetry: if(create_options & CREATE_OPTION_SPECIAL) pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); else - pSMB->FileAttributes = cpu_to_le16(ATTR_NORMAL); + pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); /* BB FIXME */ /* if ((omode & S_IWUGO) == 0) pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/ @@ -777,6 +777,8 @@ OldOpenRetry: /* BB FIXME BB */ /* pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */ /* BB FIXME END BB */ + + pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); pSMB->OpenFunction = convert_disposition(openDisposition); count += name_len; pSMB->hdr.smb_buf_length += count; @@ -3689,7 +3691,7 @@ SetEOFRetry: PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); @@ -3697,7 +3699,7 @@ SetEOFRetry: params = 6 + name_len; data_count = sizeof (struct file_end_of_file_info); pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */ + pSMB->MaxDataCount = cpu_to_le16(4100); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; @@ -4079,7 +4081,7 @@ setPermsRetry: PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 0fbe02ebc03354..6e82e1ae03b492 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1030,6 +1030,14 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) /* now that we found one valid file handle no sense continuing to loop trying others, so break here */ + /* if(rc == -EINVAL) { + int bytes_written; + rc = CIFSSMBWrite(xid, pTcon, + nfid, 0, + attrs->ia_size, + &bytes_written, + NULL, NULL, long_op); + } */ break; } } @@ -1048,6 +1056,13 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cFYI(1, (" SetEOF by path (setattrs) rc = %d", rc)); + /* if(rc == -EINVAL) + old_style_set_eof_via_write(xid, pTcon, + full_path, + attrs->ia_size, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR);*/ } /* Server is ok setting allocation size implicitly - no need -- cgit 1.2.3-korg From e30dcf3a1905b4d2154f95db5fdfdf69691b4f0e Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 20 Sep 2005 20:49:16 -0700 Subject: [CIFS] Add support for legacy servers part eight. Write fixes for Windows ME, and do not set ctime unless explicitly requested with atime and/or mtime (it gets thrown away by most servers anyway as there is no way to set this via posix). Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 2 ++ fs/cifs/cifsfs.c | 2 +- fs/cifs/cifssmb.c | 15 ++++++----- fs/cifs/inode.c | 78 ++++++++++++++++++++++++++++++++++++++----------------- 4 files changed, 66 insertions(+), 31 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 8b55e56cf1fec1..47ae68b51847c3 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -2,6 +2,8 @@ Version 1.37 ------------ Fix readdir caching when unlink removes file in current search buffer, and this is followed by a rewind search to just before the deleted entry. +Do not attempt to set ctime unless atime and/or mtime change requested +(most servers throw it away anyway). Version 1.36 ------------ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 43fb2aafa528ce..f738c8b19e3b19 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -256,7 +256,7 @@ cifs_alloc_inode(struct super_block *sb) cifs_inode->clientCanCacheAll = FALSE; cifs_inode->vfs_inode.i_blksize = CIFS_MAX_MSGSIZE; cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ - + cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; INIT_LIST_HEAD(&cifs_inode->openFileList); return &cifs_inode->vfs_inode; } diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 575b2281518d59..f72a61df3c68cb 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1072,7 +1072,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, if (bytes_sent > count) bytes_sent = count; pSMB->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); + cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); if(buf) memcpy(pSMB->Data,buf,bytes_sent); else if(ubuf) { @@ -1080,20 +1080,23 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, cifs_buf_release(pSMB); return -EFAULT; } - } else { + } else if (count != 0) { /* No buffer */ cifs_buf_release(pSMB); return -EINVAL; + } /* else setting file size with write of zero bytes */ + if(wct == 14) + byte_count = bytes_sent + 1; /* pad */ + else /* wct == 12 */ { + byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */ } - - byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */ pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); - pSMB->hdr.smb_buf_length += bytes_sent+1; + pSMB->hdr.smb_buf_length += byte_count; if(wct == 14) pSMB->ByteCount = cpu_to_le16(byte_count); - else { /* old style write has byte count 4 bytes earlier */ + else { /* old style write has byte count 4 bytes earlier so 4 bytes pad */ struct smb_com_writex_req * pSMBW = (struct smb_com_writex_req *)pSMB; pSMBW->ByteCount = cpu_to_le16(byte_count); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 6e82e1ae03b492..ca3af4eafcb2f1 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1030,14 +1030,15 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) /* now that we found one valid file handle no sense continuing to loop trying others, so break here */ - /* if(rc == -EINVAL) { + if(rc == -EINVAL) { int bytes_written; rc = CIFSSMBWrite(xid, pTcon, nfid, 0, attrs->ia_size, - &bytes_written, - NULL, NULL, long_op); - } */ + &bytes_written, NULL, + NULL, 1 /* 45 sec */); + cFYI(1,("wrt seteof rc %d",rc)); + } break; } } @@ -1055,14 +1056,30 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - cFYI(1, (" SetEOF by path (setattrs) rc = %d", rc)); - /* if(rc == -EINVAL) - old_style_set_eof_via_write(xid, pTcon, - full_path, - attrs->ia_size, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR);*/ + cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc)); + if(rc == -EINVAL) { + __u16 netfid; + int oplock = FALSE; + + rc = SMBLegacyOpen(xid, pTcon, full_path, + FILE_OPEN, + SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, + CREATE_NOT_DIR, &netfid, &oplock, + NULL, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + if (rc==0) { + int bytes_written; + rc = CIFSSMBWrite(xid, pTcon, + netfid, 0, + attrs->ia_size, + &bytes_written, NULL, + NULL, 1 /* 45 sec */); + cFYI(1,("wrt seteof rc %d",rc)); + CIFSSMBClose(xid, pTcon, netfid); + } + + } } /* Server is ok setting allocation size implicitly - no need @@ -1075,24 +1092,22 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) rc = vmtruncate(direntry->d_inode, attrs->ia_size); cifs_truncate_page(direntry->d_inode->i_mapping, direntry->d_inode->i_size); - } + } else + goto cifs_setattr_exit; } if (attrs->ia_valid & ATTR_UID) { - cFYI(1, (" CIFS - UID changed to %d", attrs->ia_uid)); + cFYI(1, ("UID changed to %d", attrs->ia_uid)); uid = attrs->ia_uid; - /* entry->uid = cpu_to_le16(attr->ia_uid); */ } if (attrs->ia_valid & ATTR_GID) { - cFYI(1, (" CIFS - GID changed to %d", attrs->ia_gid)); + cFYI(1, ("GID changed to %d", attrs->ia_gid)); gid = attrs->ia_gid; - /* entry->gid = cpu_to_le16(attr->ia_gid); */ } time_buf.Attributes = 0; if (attrs->ia_valid & ATTR_MODE) { - cFYI(1, (" CIFS - Mode changed to 0x%x", attrs->ia_mode)); + cFYI(1, ("Mode changed to 0x%x", attrs->ia_mode)); mode = attrs->ia_mode; - /* entry->mode = cpu_to_le16(attr->ia_mode); */ } if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX) @@ -1132,18 +1147,24 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); } else time_buf.LastWriteTime = 0; - - if (attrs->ia_valid & ATTR_CTIME) { + /* Do not set ctime explicitly unless other time + stamps are changed explicitly (i.e. by utime() + since we would then have a mix of client and + server times */ + + if (set_time && (attrs->ia_valid & ATTR_CTIME)) { set_time = TRUE; - cFYI(1, (" CIFS - CTIME changed ")); /* BB probably no need */ + /* Although Samba throws this field away + it may be useful to Windows - but we do + not want to set ctime unless some other + timestamp is changing */ + cFYI(1, ("CIFS - CTIME changed ")); time_buf.ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); } else time_buf.ChangeTime = 0; if (set_time || time_buf.Attributes) { - /* BB what if setting one attribute fails (such as size) but - time setting works? */ time_buf.CreationTime = 0; /* do not change */ /* In the future we should experiment - try setting timestamps via Handle (SetFileInfo) instead of by path */ @@ -1182,12 +1203,21 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) &time_buf, cifs_sb->local_nls); */ } } + /* Even if error on time set, no sense failing the call if + the server would set the time to a reasonable value anyway, + and this check ensures that we are not being called from + sys_utimes in which case we ought to fail the call back to + the user when the server rejects the call */ + if((rc) && (attrs->ia_valid && + (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) + rc = 0; } /* do not need local check to inode_check_ok since the server does that */ if (!rc) rc = inode_setattr(direntry->d_inode, attrs); +cifs_setattr_exit: kfree(full_path); FreeXid(xid); return rc; -- cgit 1.2.3-korg From 2096243885ee34b78cb57ce835e07c8536a67d2a Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 21 Sep 2005 22:05:57 -0700 Subject: [CIFS] Add support for legacy servers part nine. statfs (df and du) is now functional, and the length check is fixed so readdir does not throw a warning message when windows me messes up the response to FindFirst of an empty dir (with only . and ..). Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 3 +- fs/cifs/cifsfs.c | 4 +++ fs/cifs/cifspdu.h | 8 +++++ fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++---- fs/cifs/misc.c | 3 +- fs/cifs/netmisc.c | 2 +- 7 files changed, 107 insertions(+), 10 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 47ae68b51847c3..661b45906d0980 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -3,7 +3,8 @@ Version 1.37 Fix readdir caching when unlink removes file in current search buffer, and this is followed by a rewind search to just before the deleted entry. Do not attempt to set ctime unless atime and/or mtime change requested -(most servers throw it away anyway). +(most servers throw it away anyway). Fix length check of received smbs +to be more accurate. Version 1.36 ------------ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f738c8b19e3b19..1f97d39100ee51 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -205,6 +205,10 @@ cifs_statfs(struct super_block *sb, struct kstatfs *buf) #endif /* CIFS_EXPERIMENTAL */ rc = CIFSSMBQFSInfo(xid, pTcon, buf); + /* Old Windows servers do not support level 103, retry with level + one if old server failed the previous call */ + if(rc) + rc = SMBOldQFSInfo(xid, pTcon, buf); /* int f_type; __fsid_t f_fsid; diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index cf466595b0d400..3fa37790bea271 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1683,6 +1683,14 @@ typedef struct { __le32 BytesPerSector; } FILE_SYSTEM_INFO; /* size info, level 0x103 */ +typedef struct { + __le32 fsid; + __le32 SectorsPerAllocationUnit; + __le32 TotalAllocationUnits; + __le32 FreeAllocationUnits; + __le16 BytesPerSector; +} FILE_SYSTEM_ALLOC_INFO; + typedef struct { __le16 MajorVersionNumber; __le16 MinorVersionNumber; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 6943f7c6de0846..0bace385e97a88 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -133,6 +133,8 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, int remap); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); +extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, + struct kstatfs *FSData); extern int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f72a61df3c68cb..daf717e6b6eb24 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3215,6 +3215,92 @@ GetDFSRefExit: return rc; } +/* Query File System Info such as free space to old servers such as Win 9x */ +int +SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData) +{ +/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_ALLOC_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cFYI(1, ("OldQFSInfo")); +oldQFSInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION); + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Send error in QFSInfo = %d", rc)); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < 18)) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + cFYI(1,("qfsinf resp BCC: %d Offset %d", + pSMBr->ByteCount, data_offset)); + + response_data = + (FILE_SYSTEM_ALLOC_INFO *) + (((char *) &pSMBr->hdr.Protocol) + data_offset); + FSData->f_bsize = + le16_to_cpu(response_data->BytesPerSector) * + le32_to_cpu(response_data-> + SectorsPerAllocationUnit); + FSData->f_blocks = + le32_to_cpu(response_data->TotalAllocationUnits); + FSData->f_bfree = FSData->f_bavail = + le32_to_cpu(response_data->FreeAllocationUnits); + cFYI(1, + ("Blocks: %lld Free: %lld Block size %ld", + (unsigned long long)FSData->f_blocks, + (unsigned long long)FSData->f_bfree, + FSData->f_bsize)); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto oldQFSInfoRetry; + + return rc; +} + int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData) { @@ -3236,7 +3322,7 @@ QFSInfoRetry: params = 2; /* level */ pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; @@ -3259,17 +3345,14 @@ QFSInfoRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { - cERROR(1, ("Send error in QFSInfo = %d", rc)); + cFYI(1, ("Send error in QFSInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < 24)) /* BB alsO CHEck enough total bytes returned */ + if (rc || (pSMBr->ByteCount < 24)) rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - cFYI(1, - ("Decoding qfsinfo response. BCC: %d Offset %d", - pSMBr->ByteCount, data_offset)); response_data = (FILE_SYSTEM_INFO diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index fafbdbfa63a138..26b35b55f31b99 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -450,13 +450,12 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length) if ((4 + len != smbCalcSize(smb)) || (4 + len != (unsigned int)length)) { - return 0; - } else { cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb))); cERROR(1, ("bad smb size detected. The Mid=%d", smb->Mid)); return 1; } + return 0; } int is_valid_oplock_break(struct smb_hdr *buf) diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 873b812c0f408e..32efa32774d22a 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -868,7 +868,7 @@ unsigned int smbCalcSize(struct smb_hdr *ptr) { return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) + - BCC(ptr)); + 2 /* size of the bcc field itself */ + BCC(ptr)); } /* The following are taken from fs/ntfs/util.c */ -- cgit 1.2.3-korg From 70ca734a14366b634224a1e4586d43b36b65ab67 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 22 Sep 2005 16:32:06 -0700 Subject: [CIFS] Various minor bigendian fixes and sparse level 2 warning message fixes Most important of these fixes mapchars on bigendian and a few statfs fields Signed-off-by: Shaggy (shaggy@austin.ibm.com) Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/CHANGES | 3 ++- fs/cifs/cifspdu.h | 2 +- fs/cifs/cifsproto.h | 1 + fs/cifs/cifssmb.c | 24 ++++++++++++++---------- fs/cifs/connect.c | 38 +++++++++++++++++++++++++------------- fs/cifs/misc.c | 12 +++++++----- fs/cifs/netmisc.c | 9 ++++++++- fs/cifs/transport.c | 4 ++-- 8 files changed, 60 insertions(+), 33 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 661b45906d0980..535177238020e6 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -4,7 +4,8 @@ Fix readdir caching when unlink removes file in current search buffer, and this is followed by a rewind search to just before the deleted entry. Do not attempt to set ctime unless atime and/or mtime change requested (most servers throw it away anyway). Fix length check of received smbs -to be more accurate. +to be more accurate. Fix big endian problem with mapchars mount option, +and with a field returned by statfs. Version 1.36 ------------ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 3fa37790bea271..193f06eb43f9e2 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -697,7 +697,7 @@ typedef struct smb_com_openx_req { __le32 EndOfFile; __le32 Timeout; __le32 Reserved; - __u16 ByteCount; /* file name follows */ + __le16 ByteCount; /* file name follows */ char fileName[1]; } OPENX_REQ; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 0bace385e97a88..dc5a6a6ff2f925 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -56,6 +56,7 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); extern int is_valid_oplock_break(struct smb_hdr *smb); extern int is_size_safe_to_change(struct cifsInodeInfo *); extern unsigned int smbCalcSize(struct smb_hdr *ptr); +extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, enum securityEnum *secType); extern int cifs_inet_pton(int, char * source, void *dst); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index daf717e6b6eb24..52caac063a7750 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -779,7 +779,7 @@ OldOpenRetry: /* BB FIXME END BB */ pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); - pSMB->OpenFunction = convert_disposition(openDisposition); + pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition)); count += name_len; pSMB->hdr.smb_buf_length += count; @@ -808,10 +808,12 @@ OldOpenRetry: pfile_info->LastAccessTime = 0; /* BB fixme */ pfile_info->LastWriteTime = 0; /* BB fixme */ pfile_info->ChangeTime = 0; /* BB fixme */ - pfile_info->Attributes = pSMBr->FileAttributes; + pfile_info->Attributes = + cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); /* the file_info buf is endian converted by caller */ - pfile_info->AllocationSize = pSMBr->EndOfFile; - pfile_info->EndOfFile = pSMBr->EndOfFile; + pfile_info->AllocationSize = + cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile)); + pfile_info->EndOfFile = pfile_info->AllocationSize; pfile_info->NumberOfLinks = cpu_to_le32(1); } } @@ -2390,9 +2392,11 @@ QInfRetry: cFYI(1, ("Send error in QueryInfo = %d", rc)); } else if (pFinfo) { /* decode response */ memset(pFinfo, 0, sizeof(FILE_ALL_INFO)); - pFinfo->AllocationSize = (__le64) pSMBr->size; - pFinfo->EndOfFile = (__le64) pSMBr->size; - pFinfo->Attributes = (__le32) pSMBr->attr; + pFinfo->AllocationSize = + cpu_to_le64(le32_to_cpu(pSMBr->size)); + pFinfo->EndOfFile = pFinfo->AllocationSize; + pFinfo->Attributes = + cpu_to_le32(le16_to_cpu(pSMBr->attr)); } else rc = -EIO; /* bad buffer passed in */ @@ -3722,16 +3726,16 @@ QFSPosixRetry: le64_to_cpu(response_data->TotalBlocks); FSData->f_bfree = le64_to_cpu(response_data->BlocksAvail); - if(response_data->UserBlocksAvail == -1) { + if(response_data->UserBlocksAvail == cpu_to_le64(-1)) { FSData->f_bavail = FSData->f_bfree; } else { FSData->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); } - if(response_data->TotalFileNodes != -1) + if(response_data->TotalFileNodes != cpu_to_le64(-1)) FSData->f_files = le64_to_cpu(response_data->TotalFileNodes); - if(response_data->FreeFileNodes != -1) + if(response_data->FreeFileNodes != cpu_to_le64(-1)) FSData->f_ffree = le64_to_cpu(response_data->FreeFileNodes); } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 196976049c00e1..e27e5ad8b59198 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -303,12 +303,12 @@ static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB) byte_count += total_in_buf2; BCC_LE(pTargetSMB) = cpu_to_le16(byte_count); - byte_count = be32_to_cpu(pTargetSMB->smb_buf_length); + byte_count = pTargetSMB->smb_buf_length; byte_count += total_in_buf2; /* BB also add check that we are not beyond maximum buffer size */ - pTargetSMB->smb_buf_length = cpu_to_be32(byte_count); + pTargetSMB->smb_buf_length = byte_count; if(remaining == total_in_buf2) { cFYI(1,("found the last secondary response")); @@ -333,7 +333,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) struct cifsSesInfo *ses; struct task_struct *task_to_wake = NULL; struct mid_q_entry *mid_entry; - char *temp; + char temp; int isLargeBuf = FALSE; int isMultiRsp; int reconnect; @@ -435,22 +435,32 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) continue; } - /* the right amount was read from socket - 4 bytes */ + /* The right amount was read from socket - 4 bytes */ + /* so we can now interpret the length field */ + /* the first byte big endian of the length field, + is actually not part of the length but the type + with the most common, zero, as regular data */ + temp = *((char *) smb_buffer); + + /* Note that FC 1001 length is big endian on the wire, + but we convert it here so it is always manipulated + as host byte order */ pdu_length = ntohl(smb_buffer->smb_buf_length); - cFYI(1,("rfc1002 length(big endian)0x%x)", pdu_length+4)); + smb_buffer->smb_buf_length = pdu_length; + + cFYI(1,("rfc1002 length 0x%x)", pdu_length+4)); - temp = (char *) smb_buffer; - if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) { + if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) { continue; - } else if (temp[0] == (char)RFC1002_POSITIVE_SESSION_RESPONSE) { + } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) { cFYI(1,("Good RFC 1002 session rsp")); continue; - } else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { + } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { /* we get this from Windows 98 instead of an error on SMB negprot response */ cFYI(1,("Negative RFC1002 Session Response Error 0x%x)", - temp[4])); + pdu_length)); if(server->tcpStatus == CifsNew) { /* if nack on negprot (rather than ret of smb negprot error) reconnecting @@ -472,9 +482,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) wake_up(&server->response_q); continue; } - } else if (temp[0] != (char) 0) { + } else if (temp != (char) 0) { cERROR(1,("Unknown RFC 1002 frame")); - cifs_dump_mem(" Received Data: ", temp, length); + cifs_dump_mem(" Received Data: ", (char *)smb_buffer, + length); cifs_reconnect(server); csocket = server->ssocket; continue; @@ -609,7 +620,8 @@ multi_t2_fnd: } else if ((is_valid_oplock_break(smb_buffer) == FALSE) && (isMultiRsp == FALSE)) { cERROR(1, ("No task to wake, unknown frame rcvd!")); - cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr)); + cifs_dump_mem("Received Data is: ",(char *)smb_buffer, + sizeof(struct smb_hdr)); } } /* end while !EXITING */ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 26b35b55f31b99..8a0edd695f84ba 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -419,7 +419,7 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid) int checkSMB(struct smb_hdr *smb, __u16 mid, int length) { - __u32 len = be32_to_cpu(smb->smb_buf_length); + __u32 len = smb->smb_buf_length; cFYI(0, ("Entering checkSMB with Length: %x, smb_buf_length: %x ", length, len)); @@ -448,9 +448,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length) if (checkSMBhdr(smb, mid)) return 1; - if ((4 + len != smbCalcSize(smb)) + if ((4 + len != smbCalcSize_LE(smb)) || (4 + len != (unsigned int)length)) { - cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb))); + cERROR(1, ("smbCalcSize %x ", smbCalcSize_LE(smb))); cERROR(1, ("bad smb size detected. The Mid=%d", smb->Mid)); return 1; @@ -672,6 +672,7 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen, int i,j,charlen; int len_remaining = maxlen; char src_char; + __u16 temp; if(!mapChars) return cifs_strtoUCS((wchar_t *) target, source, PATH_MAX, cp); @@ -708,13 +709,14 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen, break;*/ default: charlen = cp->char2uni(source+i, - len_remaining, target+j); + len_remaining, &temp); /* if no match, use question mark, which at least in some cases servers as wild card */ if(charlen < 1) { target[j] = cpu_to_le16(0x003f); charlen = 1; - } + } else + target[j] = cpu_to_le16(temp); len_remaining -= charlen; /* character may take more than one byte in the the source string, but will take exactly two diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 32efa32774d22a..29e6efc5597c37 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -868,7 +868,14 @@ unsigned int smbCalcSize(struct smb_hdr *ptr) { return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) + - 2 /* size of the bcc field itself */ + BCC(ptr)); + 2 /* size of the bcc field */ + BCC(ptr)); +} + +unsigned int +smbCalcSize_LE(struct smb_hdr *ptr) +{ + return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) + + 2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr))); } /* The following are taken from fs/ntfs/util.c */ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index d5e0c4018f92e3..9e8e85a8d186b6 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -414,7 +414,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); - receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); + receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1,("No response buffer")); if(midQ->midState == MID_REQUEST_SUBMITTED) { @@ -665,7 +665,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); - receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); + receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1,("No response buffer")); if(midQ->midState == MID_REQUEST_SUBMITTED) { -- cgit 1.2.3-korg From 3e84469d0101456caceffc6b22218a49017fcd3f Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 3 Oct 2005 13:37:24 -0700 Subject: [CIFS] Add writepages support to shrink memory usage on writes, eliminate the double copy, and improve cifs write performance and help the server by upping the typical write size from 4K to 16K (or even larger if wsize set explicitly) for servers which support this. Part 1 of 2 Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 5 ++- fs/cifs/cifssmb.c | 41 ++++++++++------------- fs/cifs/connect.c | 4 +++ fs/cifs/file.c | 16 ++++++--- fs/cifs/transport.c | 95 +++++++++++++++++++++++++++++++---------------------- 5 files changed, 89 insertions(+), 72 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index dc5a6a6ff2f925..fb3e76043c5094 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -48,8 +48,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, struct smb_hdr * /* out */ , int * /* bytes returned */ , const int long_op); extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, - struct smb_hdr * /* input */ , int hdr_len, - const char * /* SMB data to send */ , int data_len, + struct kvec *, int /* nvec */, int * /* bytes returned */ , const int long_op); extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); @@ -241,7 +240,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, - const char *buf,const int long_op); + struct kvec *iov, const int nvec, const int long_op); extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 * inode_number, const struct nls_table *nls_codepage, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 52caac063a7750..365949c1464691 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -125,6 +125,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon , nls_codepage); up(&tcon->ses->sesSem); + /* BB FIXME add code to check if wsize needs + update due to negotiated smb buffer size + shrinking */ if(rc == 0) atomic_inc(&tconInfoReconnectCount); @@ -220,6 +223,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); + /* BB FIXME add code to check if wsize needs + update due to negotiated smb buffer size + shrinking */ if(rc == 0) atomic_inc(&tconInfoReconnectCount); @@ -1128,15 +1134,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, - const __u64 offset, unsigned int *nbytes, const char *buf, - const int long_op) + const __u64 offset, unsigned int *nbytes, struct kvec *iov, + int n_vec, const int long_op) { int rc = -EACCES; WRITE_REQ *pSMB = NULL; int bytes_returned; int smb_hdr_len; - __u32 bytes_sent; - __u16 byte_count; cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); @@ -1154,31 +1158,20 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, pSMB->WriteMode = 0; pSMB->Remaining = 0; - /* Can increase buffer size if buffer is big enough in some cases - ie - can send more if LARGE_WRITE_X capability returned by the server and if - our buffer is big enough or if we convert to iovecs on socket writes - and eliminate the copy to the CIFS buffer */ - if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) { - bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); - } else { - bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) - & ~0xFF; - } - - if (bytes_sent > count) - bytes_sent = count; pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); - byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */ - pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); - pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); + pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); + pSMB->DataLengthHigh = cpu_to_le16(count >> 16); smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ - pSMB->hdr.smb_buf_length += bytes_sent+1; - pSMB->ByteCount = cpu_to_le16(byte_count); + pSMB->hdr.smb_buf_length += count+1; + pSMB->ByteCount = cpu_to_le16(count + 1); + + iov[0].iov_base = pSMB; + iov[0].iov_len = smb_hdr_len + 4; - rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, smb_hdr_len, - buf, bytes_sent, &bytes_returned, long_op); + rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned, + long_op); cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error in write = %d", rc)); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e27e5ad8b59198..f05d9e2016d5e6 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1891,6 +1891,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } } } + if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) + cifs_sb->wsize = min(cifs_sb->wsize, + (tcon->ses->server->maxBuf - + MAX_CIFS_HDR_SIZE)); } /* volume_info.password is freed above when existing session found diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 3766db2bb7f28b..9411083525470e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -849,13 +849,19 @@ static ssize_t cifs_write(struct file *file, const char *write_data, /* BB FIXME We can not sign across two buffers yet */ if((experimEnabled) && ((pTcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) { + struct kvec iov[2]; + unsigned int len; + + len = min(cifs_sb->wsize, + write_size - total_written); + /* iov[0] is reserved for smb header */ + iov[1].iov_base = (char *)write_data + + total_written; + iov[1].iov_len = len; rc = CIFSSMBWrite2(xid, pTcon, - open_file->netfid, - min_t(const int, cifs_sb->wsize, - write_size - total_written), + open_file->netfid, len, *poffset, &bytes_written, - write_data + total_written, - long_op); + iov, 1, long_op); } else /* BB FIXME fixup indentation of line below */ #endif diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 9e8e85a8d186b6..38b3b2463ae4dc 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -147,16 +147,19 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, Flags2 is converted in SendReceive */ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); - cFYI(1, ("Sending smb of length %d ", smb_buf_length)); + cFYI(1, ("Sending smb of length %d", smb_buf_length)); dump_smb(smb_buffer, len); while (len > 0) { rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; - if(i > 60) { + /* smaller timeout here than send2 since smaller size */ + /* Although it may not be required, this also is smaller + oplock break time */ + if(i > 30) { cERROR(1, - ("sends on sock %p stuck for 30 seconds", + ("sends on sock %p stuck for 15 seconds", ssocket)); rc = -EAGAIN; break; @@ -172,7 +175,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, } if (rc < 0) { - cERROR(1,("Error %d sending data on socket to server.", rc)); + cERROR(1,("Error %d sending data on socket to server", rc)); } else { rc = 0; } @@ -182,22 +185,20 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, #ifdef CONFIG_CIFS_EXPERIMENTAL static int -smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, - unsigned int smb_hdr_length, const char * data, unsigned int datalen, - struct sockaddr *sin) +smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, + struct sockaddr *sin) { int rc = 0; int i = 0; struct msghdr smb_msg; - struct kvec iov[2]; - unsigned len = smb_hdr_length + 4; + struct smb_hdr *smb_buffer = iov[0].iov_base; + unsigned int len = iov[0].iov_len; + unsigned int total_len; + int first_vec = 0; if(ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ - iov[0].iov_base = smb_buffer; - iov[0].iov_len = len; - iov[1].iov_base = data; - iov[1].iov_len = datalen; + smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); smb_msg.msg_control = NULL; @@ -209,18 +210,23 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, cifssmb.c and RFC1001 len is converted to bigendian in smb_send Flags2 is converted in SendReceive */ + + total_len = 0; + for (i = 0; i < n_vec; i++) + total_len += iov[i].iov_len; + smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); - cFYI(1, ("Sending smb: hdrlen %d datalen %d", - smb_hdr_length,datalen)); + cFYI(1, ("Sending smb: total_len %d", total_len)); dump_smb(smb_buffer, len); - while (len + datalen > 0) { - rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len); + while (total_len) { + rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec], + n_vec - first_vec, total_len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; - if(i > 60) { + if(i > 40) { cERROR(1, - ("sends on sock %p stuck for 30 seconds", + ("sends on sock %p stuck for 20 seconds", ssocket)); rc = -EAGAIN; break; @@ -230,43 +236,52 @@ smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, } if (rc < 0) break; - if(iov[0].iov_len > 0) { - if(rc >= len) { - iov[0].iov_len = 0; - rc -= len; - len = 0; - } else { /* some of hdr was not sent */ - len -= rc; - iov[0].iov_len -= rc; - iov[0].iov_base += rc; - continue; - } + + if (rc >= total_len) { + WARN_ON(rc > total_len); + break; + } + if(rc == 0) { + /* should never happen, letting socket clear before + retrying is our only obvious option here */ + cERROR(1,("tcp sent no data"); + msleep(500); + continue; } - if((iov[0].iov_len == 0) && (rc > 0)){ - iov[1].iov_base += rc; - iov[1].iov_len -= rc; - datalen -= rc; + total_len -= rc; + for (i = first_vec; i < n_vec; i++) { + if (iov[i].iov_len) { + if (rc > iov[i].iov_len) { + rc -= iov[i].iov_len; + iov[i].iov_len = 0; + } else { + iov[i].iov_base += rc; + iov[i].iov_len -= rc; + first_vec = i; + break; + } + } } } if (rc < 0) { - cERROR(1,("Error %d sending data on socket to server.", rc)); - } else { + cERROR(1,("Error %d sending data on socket to server", rc)); + } else rc = 0; - } return rc; } int SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, - struct smb_hdr *in_buf, int hdrlen, const char * data, - int datalen, int *pbytes_returned, const int long_op) + struct kvec *iov, int n_vec, int *pbytes_returned, + const int long_op) { int rc = 0; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; + struct smb_hdr *in_buf = iov[0].iov_base; if (ses == NULL) { cERROR(1,("Null smb session")); @@ -364,7 +379,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, /* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */ midQ->midState = MID_REQUEST_SUBMITTED; - rc = smb_send2(ses->server->ssocket, in_buf, hdrlen, data, datalen, + rc = smb_send2(ses->server->ssocket, iov, n_vec, (struct sockaddr *) &(ses->server->addr.sockAddr)); if(rc < 0) { DeleteMidQEntry(midQ); -- cgit 1.2.3-korg From 8cc64c6ecfef020d40829f3e1152aab006c13899 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 3 Oct 2005 13:49:43 -0700 Subject: [CIFS] Allow SMBWrite2 to work to older servers Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifssmb.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 365949c1464691..41996a240149dd 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1139,11 +1139,15 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, { int rc = -EACCES; WRITE_REQ *pSMB = NULL; - int bytes_returned; + int bytes_returned, wct; int smb_hdr_len; cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ - rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); + if(tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 14; + else + wct = 12; + rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); if (rc) return rc; /* tcon and ses pointer are checked in smb_init */ @@ -1153,7 +1157,10 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + if(wct == 14) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + else if((offset >> 32) > 0) /* can not handle this big offset for old */ + return -EIO; pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; @@ -1164,9 +1171,17 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(count >> 16); smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ - pSMB->hdr.smb_buf_length += count+1; - pSMB->ByteCount = cpu_to_le16(count + 1); - + if(wct == 14) + pSMB->hdr.smb_buf_length += count+1; + else /* wct == 12 */ + pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */ + if(wct == 14) + pSMB->ByteCount = cpu_to_le16(count + 1); + else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ { + struct smb_com_writex_req * pSMBW = + (struct smb_com_writex_req *)pSMB; + pSMBW->ByteCount = cpu_to_le16(count + 5); + } iov[0].iov_base = pSMB; iov[0].iov_len = smb_hdr_len + 4; @@ -1174,7 +1189,7 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, long_op); cifs_stats_inc(&tcon->num_writes); if (rc) { - cFYI(1, ("Send error in write = %d", rc)); + cFYI(1, ("Send error Write2 = %d", rc)); *nbytes = 0; } else { WRITE_RSP * pSMBr = (WRITE_RSP *)pSMB; -- cgit 1.2.3-korg From 04c08816d693f010ce14b8f408c6228600053af0 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 3 Oct 2005 19:33:15 -0700 Subject: [CIFS] Missing parenthesis from error message in previous fix Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 38b3b2463ae4dc..64c712629f2754 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -244,7 +244,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, if(rc == 0) { /* should never happen, letting socket clear before retrying is our only obvious option here */ - cERROR(1,("tcp sent no data"); + cERROR(1,("tcp sent no data")); msleep(500); continue; } -- cgit 1.2.3-korg From 6148a742b2bd76abfe0c1fc50dd747cb9f28cd6b Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 5 Oct 2005 12:23:19 -0700 Subject: CIFS: Create routine find_writable_file to reduce redundant code Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 1 + fs/cifs/file.c | 112 ++++++++++++++++------------------------------------ fs/cifs/inode.c | 56 +++++++------------------- 3 files changed, 50 insertions(+), 119 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index fb3e76043c5094..d301149b1bb0f8 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -54,6 +54,7 @@ extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); extern int is_valid_oplock_break(struct smb_hdr *smb); extern int is_size_safe_to_change(struct cifsInodeInfo *); +extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 9411083525470e..94875455d7fa5e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -904,6 +904,25 @@ static ssize_t cifs_write(struct file *file, const char *write_data, return total_written; } +static struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) +{ + struct cifsFileInfo *open_file; + + read_lock(&GlobalSMBSeslock); + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (open_file->closePend) + continue; + if (open_file->pfile && + ((open_file->pfile->f_flags & O_RDWR) || + (open_file->pfile->f_flags & O_WRONLY))) { + read_unlock(&GlobalSMBSeslock); + return open_file; + } + } + read_unlock(&GlobalSMBSeslock); + return NULL; +} + static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) { struct address_space *mapping = page->mapping; @@ -914,10 +933,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct inode *inode; - struct cifsInodeInfo *cifsInode; - struct cifsFileInfo *open_file = NULL; - struct list_head *tmp; - struct list_head *tmp1; + struct cifsFileInfo *open_file; if (!mapping || !mapping->host) return -EFAULT; @@ -945,49 +961,19 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) if (mapping->host->i_size - offset < (loff_t)to) to = (unsigned)(mapping->host->i_size - offset); - cifsInode = CIFS_I(mapping->host); - read_lock(&GlobalSMBSeslock); - /* BB we should start at the end */ - list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) { - open_file = list_entry(tmp, struct cifsFileInfo, flist); - if (open_file->closePend) - continue; - /* We check if file is open for writing first */ - if ((open_file->pfile) && - ((open_file->pfile->f_flags & O_RDWR) || - (open_file->pfile->f_flags & O_WRONLY))) { - read_unlock(&GlobalSMBSeslock); - bytes_written = cifs_write(open_file->pfile, - write_data, to-from, - &offset); - read_lock(&GlobalSMBSeslock); + open_file = find_writable_file(CIFS_I(mapping->host)); + if (open_file) { + bytes_written = cifs_write(open_file->pfile, write_data, + to-from, &offset); /* Does mm or vfs already set times? */ - inode->i_atime = - inode->i_mtime = current_fs_time(inode->i_sb); - if ((bytes_written > 0) && (offset)) { - rc = 0; - } else if (bytes_written < 0) { - if (rc == -EBADF) { - /* have seen a case in which kernel seemed to - have closed/freed a file even with writes - active so we might as well see if there are - other file structs to try for the same - inode before giving up */ - continue; - } else - rc = bytes_written; - } - break; /* now that we found a valid file handle and - tried to write to it we are done, no sense - continuing to loop looking for another */ - } - if (tmp->next == NULL) { - cFYI(1, ("File instance %p removed", tmp)); - break; + inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); + if ((bytes_written > 0) && (offset)) { + rc = 0; + } else if (bytes_written < 0) { + if (rc != -EBADF) + rc = bytes_written; } - } - read_unlock(&GlobalSMBSeslock); - if (open_file == NULL) { + } else { cFYI(1, ("No writeable filehandles for inode")); rc = -EIO; } @@ -1604,40 +1590,12 @@ static int cifs_readpage(struct file *file, struct page *page) page caching in the current Linux kernel design */ int is_size_safe_to_change(struct cifsInodeInfo *cifsInode) { - struct list_head *tmp; - struct list_head *tmp1; - struct cifsFileInfo *open_file = NULL; - int rc = TRUE; - - if (cifsInode == NULL) - return rc; - - read_lock(&GlobalSMBSeslock); - list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) { - open_file = list_entry(tmp, struct cifsFileInfo, flist); - if (open_file == NULL) - break; - if (open_file->closePend) - continue; - /* We check if file is open for writing, - BB we could supplement this with a check to see if file size - changes have been flushed to server - ie inode metadata dirty */ - if ((open_file->pfile) && - ((open_file->pfile->f_flags & O_RDWR) || - (open_file->pfile->f_flags & O_WRONLY))) { - rc = FALSE; - break; - } - if (tmp->next == NULL) { - cFYI(1, ("File instance %p removed", tmp)); - break; - } - } - read_unlock(&GlobalSMBSeslock); - return rc; + if (cifsInode && find_writable_file(cifsInode)) + return 0; + else + return 1; } - static int cifs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ca3af4eafcb2f1..49efdefcff7c65 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -995,7 +995,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) filemap_fdatawait(direntry->d_inode->i_mapping); if (attrs->ia_valid & ATTR_SIZE) { - read_lock(&GlobalSMBSeslock); /* To avoid spurious oplock breaks from server, in the case of inodes that we already have open, avoid doing path based setting of file size if we can do it by handle. @@ -1003,49 +1002,22 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) when the local oplock break takes longer to flush writebehind data than the SMB timeout for the SetPathInfo request would allow */ - list_for_each(tmp, &cifsInode->openFileList) { - open_file = list_entry(tmp, struct cifsFileInfo, - flist); - /* We check if file is open for writing first */ - if ((open_file->pfile) && - ((open_file->pfile->f_flags & O_RDWR) || - (open_file->pfile->f_flags & O_WRONLY))) { - if (open_file->invalidHandle == FALSE) { - /* we found a valid, writeable network - file handle to use to try to set the - file size */ - __u16 nfid = open_file->netfid; - __u32 npid = open_file->pid; - read_unlock(&GlobalSMBSeslock); - found = TRUE; - rc = CIFSSMBSetFileSize(xid, pTcon, - attrs->ia_size, nfid, npid, - FALSE); - cFYI(1, ("SetFileSize by handle " - "(setattrs) rc = %d", rc)); - /* Do not need reopen and retry on - EAGAIN since we will retry by - pathname below */ - - /* now that we found one valid file - handle no sense continuing to loop - trying others, so break here */ - if(rc == -EINVAL) { - int bytes_written; - rc = CIFSSMBWrite(xid, pTcon, - nfid, 0, - attrs->ia_size, - &bytes_written, NULL, - NULL, 1 /* 45 sec */); - cFYI(1,("wrt seteof rc %d",rc)); - } - break; - } + open_file = find_writable_file(cifsInode); + if (open_file) { + __u16 nfid = open_file->netfid; + __u32 npid = open_file->pid; + rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, + nfid, npid, FALSE); + cFYI(1,("SetFSize for attrs rc = %d", rc)); + if(rc == -EINVAL) { + int bytes_written; + rc = CIFSSMBWrite(xid, pTcon, + nfid, 0, attrs->ia_size, + &bytes_written, NULL, NULL, + 1 /* 45 seconds */); + cFYI(1,("Wrt seteof rc %d", rc)); } } - if (found == FALSE) - read_unlock(&GlobalSMBSeslock); - if (rc != 0) { /* Set file size by pathname rather than by handle either because no valid, writeable file handle for -- cgit 1.2.3-korg From 37c0eb4677f733a773df6287b0f73f00274402e3 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 5 Oct 2005 14:50:29 -0700 Subject: CIFS: implement cifs_writepages to perform multi-page I/O Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/file.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++-- fs/cifs/transport.c | 10 +-- 2 files changed, 190 insertions(+), 11 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 94875455d7fa5e..0473b221f64307 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -21,11 +21,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include +#include #include #include #include +#include #include #include "cifsfs.h" #include "cifspdu.h" @@ -916,6 +919,16 @@ static struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { read_unlock(&GlobalSMBSeslock); + if(open_file->invalidHandle) { + rc = cifs_reopen_file(cifs_inode->vfs_inode, + open_file->pfile, FALSE); + /* if it fails, try another handle - might be */ + /* dangerous to hold up writepages with retry */ + if(rc) { + read_lock(&GlobalSMBSeslock); + continue; + } + } return open_file; } } @@ -982,20 +995,181 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) return rc; } -#if 0 +#ifdef CONFIG_CIFS_EXPERIMENTAL static int cifs_writepages(struct address_space *mapping, - struct writeback_control *wbc) + struct writeback_control *wbc) { - int rc = -EFAULT; + struct backing_dev_info *bdi = mapping->backing_dev_info; + unsigned int bytes_to_write; + unsigned int bytes_written; + struct cifs_sb_info *cifs_sb; + int done = 0; + pgoff_t end = -1; + pgoff_t index; + int is_range = 0; + struct kvec iov[32]; + int n_iov = 0; + pgoff_t next; + int nr_pages; + __u64 offset = 0; + struct cifsFileInfo *open_file = NULL; + struct page *page; + struct pagevec pvec; + int rc = 0; + int scanned = 0; int xid; + cifs_sb = CIFS_SB(mapping->host->i_sb); + + /* + * If wsize is smaller that the page cache size, default to writing + * one page at a time via cifs_writepage + */ + if (cifs_sb->wsize < PAGE_CACHE_SIZE) + return generic_writepages(mapping, wbc); + + /* + * BB: Is this meaningful for a non-block-device file system? + * If it is, we should test it again after we do I/O + */ + if (wbc->nonblocking && bdi_write_congested(bdi)) { + wbc->encountered_congestion = 1; + return 0; + } + xid = GetXid(); - /* Find contiguous pages then iterate through repeating - call 16K write then Setpageuptodate or if LARGE_WRITE_X - support then send larger writes via kevec so as to eliminate - a memcpy */ + pagevec_init(&pvec, 0); + if (wbc->sync_mode == WB_SYNC_NONE) + index = mapping->writeback_index; /* Start from prev offset */ + else { + index = 0; + scanned = 1; + } + if (wbc->start || wbc->end) { + index = wbc->start >> PAGE_CACHE_SHIFT; + end = wbc->end >> PAGE_CACHE_SHIFT; + is_range = 1; + scanned = 1; + } +retry: + while (!done && (index <= end) && + (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, + PAGECACHE_TAG_DIRTY, + min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) { + int first; + unsigned int i; + + if (!open_file) { + open_file = find_writable_file(CIFS_I(mapping->host)); + if (!open_file) { + pagevec_release(&pvec); + cERROR(1, ("No writable handles for inode")); + return -EIO; + } + } + + first = -1; + next = 0; + n_iov = 0; + bytes_to_write = 0; + + for (i = 0; i < nr_pages; i++) { + page = pvec.pages[i]; + /* + * At this point we hold neither mapping->tree_lock nor + * lock on the page itself: the page may be truncated or + * invalidated (changing page->mapping to NULL), or even + * swizzled back from swapper_space to tmpfs file + * mapping + */ + + if (first < 0) + lock_page(page); + else if (TestSetPageLocked(page)) + break; + + if (unlikely(page->mapping != mapping)) { + unlock_page(page); + break; + } + + if (unlikely(is_range) && (page->index > end)) { + done = 1; + unlock_page(page); + break; + } + + if (next && (page->index != next)) { + /* Not next consecutive page */ + unlock_page(page); + break; + } + + if (wbc->sync_mode != WB_SYNC_NONE) + wait_on_page_writeback(page); + + if (PageWriteback(page) || + !test_clear_page_dirty(page)) { + unlock_page(page); + break; + } + /* + * BB can we get rid of this? pages are held by pvec + */ + page_cache_get(page); + + /* reserve iov[0] for the smb header */ + n_iov++; + iov[n_iov].iov_base = kmap(page); + iov[n_iov].iov_len = PAGE_CACHE_SIZE; + bytes_to_write += PAGE_CACHE_SIZE; + + if (first < 0) { + first = i; + offset = page_offset(page); + } + next = page->index + 1; + if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize) + break; + } + if (n_iov) { + rc = CIFSSMBWrite2(xid, cifs_sb->tcon, + open_file->netfid, bytes_to_write, + offset, &bytes_written, iov, n_iov, + 1); + if (rc || bytes_written < bytes_to_write) { + cERROR(1,("CIFSSMBWrite2 returned %d, written = %x", + rc, bytes_written)); + set_bit(AS_EIO, &mapping->flags); + SetPageError(page); + } + for (i = 0; i < n_iov; i++) { + page = pvec.pages[first + i]; + kunmap(page); + unlock_page(page); + page_cache_release(page); + } + if ((wbc->nr_to_write -= n_iov) <= 0) + done = 1; + index = next; + } + pagevec_release(&pvec); + } + if (!scanned && !done) { + /* + * We hit the last page and there is more work to be done: wrap + * back to the start of the file + */ + scanned = 1; + index = 0; + goto retry; + } + if (!is_range) + mapping->writeback_index = index; + FreeXid(xid); + return rc; } #endif @@ -1635,6 +1809,9 @@ struct address_space_operations cifs_addr_ops = { .readpage = cifs_readpage, .readpages = cifs_readpages, .writepage = cifs_writepage, +#ifdef CONFIG_CIFS_EXPERIMENTAL + .writepages = cifs_writepages, +#endif .prepare_write = cifs_prepare_write, .commit_write = cifs_commit_write, .set_page_dirty = __set_page_dirty_nobuffers, diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 64c712629f2754..e104c1ad2da3d2 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -395,7 +395,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, if (long_op == -1) goto cifs_no_response_exit2; else if (long_op == 2) /* writes past end of file can take loong time */ - timeout = 300 * HZ; + timeout = 180 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ @@ -431,7 +431,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { - cERROR(1,("No response buffer")); + cERROR(1,("No response to cmd %d mid %d", + midQ->command, midQ->mid)); if(midQ->midState == MID_REQUEST_SUBMITTED) { if(ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; @@ -646,7 +647,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, if (long_op == -1) goto cifs_no_response_exit; else if (long_op == 2) /* writes past end of file can take loong time */ - timeout = 300 * HZ; + timeout = 180 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ @@ -682,7 +683,8 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { - cERROR(1,("No response buffer")); + cERROR(1,("No response for cmd %d mid %d", + midQ->command, midQ->mid)); if(midQ->midState == MID_REQUEST_SUBMITTED) { if(ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; -- cgit 1.2.3-korg From 4a77118cd5018fec11bf86f6f8d659352ad9a92b Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 5 Oct 2005 15:14:33 -0700 Subject: CIFS: Allow wsize to exceed CIFSMaxBufSize This allows cifs_writepages to send data in larger chunks from the page cache, without requiring larger memory allocations in other cases. Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/connect.c | 2 +- fs/cifs/file.c | 8 ++++++++ fs/cifs/transport.c | 16 +--------------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f05d9e2016d5e6..31eb9a3bf62712 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1740,7 +1740,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->rsize = volume_info.rsize; else cifs_sb->rsize = srvTcp->maxBuf - MAX_CIFS_HDR_SIZE; /* default */ - if((volume_info.wsize) && (volume_info.wsize <= CIFSMaxBufSize)) + if(volume_info.wsize) cifs_sb->wsize = volume_info.wsize; else cifs_sb->wsize = CIFSMaxBufSize; /* default */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0473b221f64307..0f66ae5b694ba7 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -925,6 +925,7 @@ static struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) /* if it fails, try another handle - might be */ /* dangerous to hold up writepages with retry */ if(rc) { + cFYI(1,("failed on reopen file in wp")); read_lock(&GlobalSMBSeslock); continue; } @@ -1028,6 +1029,13 @@ static int cifs_writepages(struct address_space *mapping, if (cifs_sb->wsize < PAGE_CACHE_SIZE) return generic_writepages(mapping, wbc); + /* BB FIXME we do not have code to sign across multiple buffers yet, + so go to older writepage style write which we can sign if needed */ + if((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server)) + if(cifs_sb->tcon->ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + return generic_writepages(mapping, wbc); + /* * BB: Is this meaningful for a non-block-device file system? * If it is, we should test it again after we do I/O diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index e104c1ad2da3d2..c86b7077b92fff 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -361,22 +361,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, return -ENOMEM; } - if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - up(&ses->server->tcpSem); - cERROR(1, - ("Illegal length, greater than maximum frame, %d ", - in_buf->smb_buf_length)); - DeleteMidQEntry(midQ); - /* If not lock req, update # of requests on wire to server */ - if(long_op < 3) { - atomic_dec(&ses->server->inFlight); - wake_up(&ses->server->request_q); - } - return -EIO; - } - /* BB FIXME */ -/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */ +/* rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); */ midQ->midState = MID_REQUEST_SUBMITTED; rc = smb_send2(ses->server->ssocket, iov, n_vec, -- cgit 1.2.3-korg From dd99cd803d460576cf84f012786ff39814b73f7f Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 5 Oct 2005 19:32:49 -0700 Subject: [CIFS] cleanup sparse and compile errors in previous fix Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/file.c | 5 +++-- fs/cifs/inode.c | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0f66ae5b694ba7..39b23f4fa6c323 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -907,9 +907,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data, return total_written; } -static struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) +struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; + int rc; read_lock(&GlobalSMBSeslock); list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { @@ -920,7 +921,7 @@ static struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) (open_file->pfile->f_flags & O_WRONLY))) { read_unlock(&GlobalSMBSeslock); if(open_file->invalidHandle) { - rc = cifs_reopen_file(cifs_inode->vfs_inode, + rc = cifs_reopen_file(&cifs_inode->vfs_inode, open_file->pfile, FALSE); /* if it fails, try another handle - might be */ /* dangerous to hold up writepages with retry */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 49efdefcff7c65..ff4d1cc7c2484b 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -962,7 +962,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) struct cifsTconInfo *pTcon; char *full_path = NULL; int rc = -EACCES; - int found = FALSE; struct cifsFileInfo *open_file = NULL; FILE_BASIC_INFO time_buf; int set_time = FALSE; @@ -970,7 +969,6 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) __u64 uid = 0xFFFFFFFFFFFFFFFFULL; __u64 gid = 0xFFFFFFFFFFFFFFFFULL; struct cifsInodeInfo *cifsInode; - struct list_head *tmp; xid = GetXid(); -- cgit 1.2.3-korg From 131afd0b748e382c3a00355d3fa245801f929298 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 7 Oct 2005 09:51:05 -0700 Subject: [CIFS] /proc/fs/cifs debug code cleanup and new stats2 These changes to debug code and new stats are helpful in debugging potential tcp performance/configuration problems under cifs. Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 16 ++++++++++++---- fs/cifs/cifsglob.h | 6 +++++- fs/cifs/transport.c | 24 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index f4c6544468abec..785239618d89c0 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -81,6 +81,8 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, buf += length; length = sprintf(buf,"CIFS Version %s\n",CIFS_VERSION); buf += length; + length = sprintf(buf,"Active VFS Requests: %d\n", GlobalTotalActiveXid); + buf += length; length = sprintf(buf, "Servers:"); buf += length; @@ -97,7 +99,7 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, } else { length = sprintf(buf, - "\n%d) Name: %s Domain: %s Mounts: %d ServerOS: %s \n\tServerNOS: %s\tCapabilities: 0x%x\n\tSMB session status: %d\t", + "\n%d) Name: %s Domain: %s Mounts: %d OS: %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB session status: %d\t", i, ses->serverName, ses->serverDomain, atomic_read(&ses->inUse), ses->serverOS, ses->serverNOS, @@ -105,12 +107,18 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, buf += length; } if(ses->server) { - buf += sprintf(buf, "TCP status: %d\n\tLocal Users To Server: %d SecMode: 0x%x Req Active: %d", + buf += sprintf(buf, "TCP status: %d\n\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d", ses->server->tcpStatus, atomic_read(&ses->server->socketUseCount), ses->server->secMode, atomic_read(&ses->server->inFlight)); - + +#ifdef CONFIG_CIFS_STATS2 + buf += sprintf(buf, "\tIn Send: %d In MaxReq Wait: %d", + atomic_read(&ses->server->inSend), + atomic_read(&ses->server->num_waiters)); +#endif + length = sprintf(buf, "\nMIDs:\n"); buf += length; @@ -149,7 +157,7 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); length = sprintf(buf, - "\n%d) %s Uses: %d Type: %s Characteristics: 0x%x Attributes: 0x%x\nPathComponentMax: %d Status: %d", + "\n%d) %s Uses: %d Type: %s DevInfo: 0x%x Attributes: 0x%x\nPathComponentMax: %d Status: %d", i, tcon->treeName, atomic_read(&tcon->useCount), tcon->nativeFileSystem, diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index cd421c76805e78..729717281b4086 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -123,13 +123,17 @@ struct TCP_Server_Info { struct list_head pending_mid_q; void *Server_NlsInfo; /* BB - placeholder for future NLS info */ unsigned short server_codepage; /* codepage for the server */ - unsigned long ip_address; /* IP addr for the server if known */ + unsigned long ip_address; /* IP addr for the server if known */ enum protocolEnum protocolType; char versionMajor; char versionMinor; unsigned svlocal:1; /* local server or remote */ atomic_t socketUseCount; /* number of open cifs sessions on socket */ atomic_t inFlight; /* number of requests on the wire to server */ +#ifdef CONFIG_CIFS_STATS2 + atomic_t inSend; /* requests trying to send */ + atomic_t num_waiters; /* blocked waiting to get in sendrecv */ +#endif enum statusEnum tcpStatus; /* what we think the status is */ struct semaphore tcpSem; struct task_struct *tsk; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index c86b7077b92fff..893a6fef98530e 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -307,9 +307,15 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ spin_unlock(&GlobalMid_Lock); +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&ses->server->num_waiters); +#endif wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < cifs_max_pending); +#ifdef CONFIG_CIFS_STATS2 + atomic_dec(&ses->server->num_waiters); +#endif spin_lock(&GlobalMid_Lock); } else { if(ses->server->tcpStatus == CifsExiting) { @@ -365,8 +371,14 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, /* rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); */ midQ->midState = MID_REQUEST_SUBMITTED; +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&ses->server->inSend); +#endif rc = smb_send2(ses->server->ssocket, iov, n_vec, (struct sockaddr *) &(ses->server->addr.sockAddr)); +#ifdef CONFIG_CIFS_STATS2 + atomic_dec(&ses->server->inSend); +#endif if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); @@ -546,9 +558,15 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ spin_unlock(&GlobalMid_Lock); +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&ses->server->num_waiters); +#endif wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < cifs_max_pending); +#ifdef CONFIG_CIFS_STATS2 + atomic_dec(&ses->server->num_waiters); +#endif spin_lock(&GlobalMid_Lock); } else { if(ses->server->tcpStatus == CifsExiting) { @@ -617,8 +635,14 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&ses->server->inSend); +#endif rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr)); +#ifdef CONFIG_CIFS_STATS2 + atomic_dec(&ses->server->inSend); +#endif if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); -- cgit 1.2.3-korg From 68058e757573d4e81550e74c5a03a29a29069ce7 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 10:34:22 -0700 Subject: [CIFS] Reduce CIFS tcp congestion timeout (it was too long) and backoff ever longer amounts (up to 15 seconds). This improves performance especially when using large wsize. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifs_debug.c | 4 ++-- fs/cifs/cifsfs.c | 16 +++++++++++++--- fs/cifs/transport.c | 13 +++++++------ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 785239618d89c0..b7fb064f654894 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -114,7 +114,7 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, atomic_read(&ses->server->inFlight)); #ifdef CONFIG_CIFS_STATS2 - buf += sprintf(buf, "\tIn Send: %d In MaxReq Wait: %d", + buf += sprintf(buf, " In Send: %d In MaxReq Wait: %d", atomic_read(&ses->server->inSend), atomic_read(&ses->server->num_waiters)); #endif @@ -267,7 +267,7 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset, atomic_read(&tcon->num_oplock_brks)); buf += item_length; length += item_length; - item_length = sprintf(buf, "\nReads: %d Bytes %lld", + item_length = sprintf(buf, "\nReads: %d Bytes %lld", atomic_read(&tcon->num_reads), (long long)(tcon->bytes_read)); buf += item_length; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 1f97d39100ee51..e3177a031edc17 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -405,6 +405,16 @@ static struct quotactl_ops cifs_quotactl_ops = { }; #endif +static void cifs_umount_begin(struct super_block * sblock) +{ + cERROR(1,("kill all tasks now - umount begin not implemented yet")); + +/* BB FIXME - finish BB */ + + return; +} + + static int cifs_remount(struct super_block *sb, int *flags, char *data) { *flags |= MS_NODIRATIME; @@ -422,7 +432,7 @@ struct super_operations cifs_super_ops = { unless later we add lazy close of inodes or unless the kernel forgets to call us with the same number of releases (closes) as opens */ .show_options = cifs_show_options, -/* .umount_begin = cifs_umount_begin, *//* consider adding in the future */ +/* .umount_begin = cifs_umount_begin, */ /* BB finish in the future */ .remount_fs = cifs_remount, }; @@ -790,9 +800,7 @@ static int cifs_oplock_thread(void * dummyarg) do { if(try_to_freeze()) continue; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1*HZ); spin_lock(&GlobalMid_Lock); if(list_empty(&GlobalOplock_Q)) { spin_unlock(&GlobalMid_Lock); @@ -841,6 +849,8 @@ static int cifs_oplock_thread(void * dummyarg) } } else spin_unlock(&GlobalMid_Lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); /* yield in case q were corrupt */ } } while(!signal_pending(current)); oplockThread = NULL; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 893a6fef98530e..d9b11690746db6 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -157,14 +157,14 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, /* smaller timeout here than send2 since smaller size */ /* Although it may not be required, this also is smaller oplock break time */ - if(i > 30) { + if(i > 12) { cERROR(1, - ("sends on sock %p stuck for 15 seconds", + ("sends on sock %p stuck for 7 seconds", ssocket)); rc = -EAGAIN; break; } - msleep(500); + msleep(1 << i); continue; } if (rc < 0) @@ -224,14 +224,14 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, n_vec - first_vec, total_len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; - if(i > 40) { + if(i >= 14) { cERROR(1, - ("sends on sock %p stuck for 20 seconds", + ("sends on sock %p stuck for 15 seconds", ssocket)); rc = -EAGAIN; break; } - msleep(500); + msleep(1 << i); continue; } if (rc < 0) @@ -249,6 +249,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, continue; } total_len -= rc; + /* the line below resets i */ for (i = first_vec; i < n_vec; i++) { if (iov[i].iov_len) { if (rc > iov[i].iov_len) { -- cgit 1.2.3-korg From 0ae0efada36219024e4e3008f16c993d5d091280 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 10:57:19 -0700 Subject: [CIFS] Fix rsize calculation so that large readx flag is checked. Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifs_debug.c | 2 +- fs/cifs/connect.c | 29 ++++++++++++++++++++++++----- fs/cifs/file.c | 5 +++-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index b7fb064f654894..6f7810992db3d6 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -267,7 +267,7 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset, atomic_read(&tcon->num_oplock_brks)); buf += item_length; length += item_length; - item_length = sprintf(buf, "\nReads: %d Bytes %lld", + item_length = sprintf(buf, "\nReads: %d Bytes: %lld", atomic_read(&tcon->num_reads), (long long)(tcon->bytes_read)); buf += item_length; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 31eb9a3bf62712..d1c6acee620e52 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include "cifspdu.h" @@ -188,6 +189,7 @@ cifs_reconnect(struct TCP_Server_Info *server) server->server_RFC1001_name); } if(rc) { + cERROR(1,("reconnect error %d",rc)); msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); @@ -469,6 +471,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } else { /* give server a second to clean up before reconnect attempt */ + cERROR(1,("sleep before reconnect")); msleep(1000); /* always try 445 first on reconnect since we get NACK on some if we ever @@ -556,6 +559,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) dump_smb(smb_buffer, length); if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) { cERROR(1, ("Bad SMB Received ")); + cifs_dump_mem("smb: ", smb_buffer, 48); continue; } @@ -1383,7 +1387,9 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, the default. sock_setsockopt not used because it expects user space buffer */ (*csocket)->sk->sk_rcvtimeo = 7 * HZ; - + cERROR(1,("sndbuf %d rcvbuf %d reset to 200K each",(*csocket)->sk->sk_sndbuf, (*csocket)->sk->sk_rcvbuf)); + (*csocket)->sk->sk_sndbuf = 300 * 1024; + (*csocket)->sk->sk_rcvbuf = 200 * 1024; /* send RFC1001 sessinit */ if(psin_server->sin_port == htons(RFC1001_PORT)) { @@ -1736,11 +1742,20 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* search for existing tcon to this server share */ if (!rc) { - if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize)) + if(volume_info.rsize > CIFSMaxBufSize) { + cERROR(1,("rsize %d too large, using MaxBufSize", + volume_info.rsize)); + cifs_sb->rsize = CIFSMaxBufSize; + } else if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize)) cifs_sb->rsize = volume_info.rsize; - else - cifs_sb->rsize = srvTcp->maxBuf - MAX_CIFS_HDR_SIZE; /* default */ - if(volume_info.wsize) + else /* default */ + cifs_sb->rsize = CIFSMaxBufSize; + + if(volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { + cERROR(1,("wsize %d too large using 4096 instead", + volume_info.wsize)); + cifs_sb->wsize = 4096; + } else if(volume_info.wsize) cifs_sb->wsize = volume_info.wsize; else cifs_sb->wsize = CIFSMaxBufSize; /* default */ @@ -1895,6 +1910,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->wsize = min(cifs_sb->wsize, (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); + if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) + cifs_sb->rsize = min(cifs_sb->rsize, + (tcon->ses->server->maxBuf - + MAX_CIFS_HDR_SIZE)); } /* volume_info.password is freed above when existing session found diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 39b23f4fa6c323..11806c879c4715 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -855,7 +855,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, struct kvec iov[2]; unsigned int len; - len = min(cifs_sb->wsize, + len = min((size_t)cifs_sb->wsize, write_size - total_written); /* iov[0] is reserved for smb header */ iov[1].iov_base = (char *)write_data + @@ -920,7 +920,8 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { read_unlock(&GlobalSMBSeslock); - if(open_file->invalidHandle) { + if((open_file->invalidHandle) && + (!open_file->closePend)) { rc = cifs_reopen_file(&cifs_inode->vfs_inode, open_file->pfile, FALSE); /* if it fails, try another handle - might be */ -- cgit 1.2.3-korg From 190fdeb84499a2dc046adae2eebfdda49e315e96 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 11:48:26 -0700 Subject: [CIFS] Fix byte range locking to Windows when Windows server returns illegal RFC1001 length (which had caused the lock to block forever until killed). --- fs/cifs/CHANGES | 12 ++++++++++++ fs/cifs/README | 4 ++-- fs/cifs/TODO | 6 +++--- fs/cifs/misc.c | 22 +++++++++++++++------- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 535177238020e6..1c249a2abae384 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,15 @@ +Version 1.38 +------------ +Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket) +to be smaller at first (but increasing) so large write performance performance +over GigE is better. Do not hang thread on illegal byte range lock response +from Windows (Windows can send an RFC1001 size which does not match smb size) by +allowing an SMBs TCP length to be up to a few bytes longer than it should be. +wsize and rsize can now be larger than negotiated buffer size if server +supports large readx/writex, even when directio mount flag not specified. +Write size will in many cases now be 16K instead of 4K which greatly helps +file copy performance on lightly loaded networks. + Version 1.37 ------------ Fix readdir caching when unlink removes file in current search buffer, diff --git a/fs/cifs/README b/fs/cifs/README index 3b610d08dc1e32..5d9a953888d9e9 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -294,8 +294,8 @@ A partial list of the supported mount options follows: during the local client kernel build will be used. If server does not support Unicode, this parameter is unused. - rsize default read size - wsize default write size + rsize default read size (usually 16K) + wsize default write size (usually 16K, 32K is often better over GigE) rw mount the network share read-write (note that the server may still consider the share read-only) ro mount network share read-only diff --git a/fs/cifs/TODO b/fs/cifs/TODO index 0593f644731934..c909298d11ed60 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -1,4 +1,4 @@ -version 1.36 September 6, 2005 +version 1.37 October 9, 2005 A Partial List of Missing Features ================================== @@ -38,8 +38,8 @@ by unlocking all known byte range locks that we locked on the file. i) quota support (needs minor kernel change since quota calls to make it to network filesystems or deviceless filesystems) -j) finish writepages support (multi-page write behind for improved -performance) and syncpage. Started by Shaggy. +j) investigate sync behavior (including syncpage) and check +for proper behavior of intr/nointr k) hook lower into the sockets api (as NFS/SunRPC does) to avoid the extra copy in/out of the socket buffers in some cases. diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 8a0edd695f84ba..eba1de917f2a87 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -420,6 +420,7 @@ int checkSMB(struct smb_hdr *smb, __u16 mid, int length) { __u32 len = smb->smb_buf_length; + __u32 clc_len; /* calculated length */ cFYI(0, ("Entering checkSMB with Length: %x, smb_buf_length: %x ", length, len)); @@ -440,20 +441,27 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length) cERROR(1, ("smb_buf_length greater than MaxBufSize")); cERROR(1, - ("bad smb detected. Illegal length. The mid=%d", + ("bad smb detected. Illegal length. mid=%d", smb->Mid)); return 1; } if (checkSMBhdr(smb, mid)) return 1; - - if ((4 + len != smbCalcSize_LE(smb)) + clc_len = smbCalcSize_LE(smb); + if ((4 + len != clc_len) || (4 + len != (unsigned int)length)) { - cERROR(1, ("smbCalcSize %x ", smbCalcSize_LE(smb))); - cERROR(1, - ("bad smb size detected. The Mid=%d", smb->Mid)); - return 1; + cERROR(1, ("Calculated size 0x%x vs actual length 0x%x", + clc_len, 4 + len)); + cERROR(1, ("bad smb size detected for Mid=%d", smb->Mid)); + /* Windows XP can return a few bytes too much, presumably + an illegal pad, at the end of byte range lock responses + so we allow for up to eight byte pad, as long as actual + received length is as long or longer than calculated length */ + if((4+len > clc_len) && (len <= clc_len + 3)) + return 0; + else + return 1; } return 0; } -- cgit 1.2.3-korg From 02c37a6df558fb8768c4c8ae792e86e9abf2d733 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 11:49:37 -0700 Subject: [CIFS] Update cifs version to 1.38 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 0f6d352ab813f7..4cdb29fdc8c2bf 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -97,5 +97,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); extern int cifs_ioctl (struct inode * inode, struct file * filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.37" +#define CIFS_VERSION "1.38" #endif /* _CIFSFS_H */ -- cgit 1.2.3-korg From 5e1253b50111220f06ee13bc4e555d89ff39176b Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 14:06:37 -0700 Subject: [CIFS] Correct cifs tcp retry when some data sent before getting EAGAIN. Continue implementation of cifs umount begin to allow force unmounts of cifs mounts. Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 19 +++++++++++++++++-- fs/cifs/transport.c | 3 +++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index e3177a031edc17..fd5eae37f2a84d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -407,9 +407,24 @@ static struct quotactl_ops cifs_quotactl_ops = { static void cifs_umount_begin(struct super_block * sblock) { - cERROR(1,("kill all tasks now - umount begin not implemented yet")); + struct cifs_sb_info *cifs_sb; -/* BB FIXME - finish BB */ + cifs_sb = CIFS_SB(sb); + if(cifs_sb == NULL) + return -EIO; + if(cifs_sb->tcon == NULL) + return -EIO; + down(&tcon->tconSem); + if (atomic_read(&tcon->useCount) == 1) + tcon->tidStatus = CifsExiting; + up(&tcon->tconSem); + + if((cifs->sb->tcon->ses) && (cifs_sb->tcon->ses->server)) + { + cERROR(1,("wake up tasks now - umount begin not complete")); + wake_up_all(&server->request_q); + } +/* BB FIXME - finish add checks for tidStatus BB */ return; } diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index d9b11690746db6..d8865fbd876ab7 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -169,6 +169,8 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, } if (rc < 0) break; + else + i = 0; /* reset i after each successful send */ iov.iov_base += rc; iov.iov_len -= rc; len -= rc; @@ -263,6 +265,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, } } } + i = 0; /* in case we get ENOSPC on the next send */ } if (rc < 0) { -- cgit 1.2.3-korg From b387eaeb666f6a5e24990a1f4d6a0447ae14315d Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 14:21:15 -0700 Subject: [CIFS] Do not shrink tcp sndbuf/rcvbuf from their defaults Signed-off-by: Steve French --- fs/cifs/connect.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d1c6acee620e52..177289771abeaa 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -189,7 +189,7 @@ cifs_reconnect(struct TCP_Server_Info *server) server->server_RFC1001_name); } if(rc) { - cERROR(1,("reconnect error %d",rc)); + cFYI(1,("reconnect error %d",rc)); msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); @@ -471,7 +471,6 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } else { /* give server a second to clean up before reconnect attempt */ - cERROR(1,("sleep before reconnect")); msleep(1000); /* always try 445 first on reconnect since we get NACK on some if we ever @@ -558,8 +557,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) dump_smb(smb_buffer, length); if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) { - cERROR(1, ("Bad SMB Received ")); - cifs_dump_mem("smb: ", smb_buffer, 48); + cifs_dump_mem("Bad SMB: ", smb_buffer, 48); continue; } @@ -1386,12 +1384,16 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, /* Eventually check for other socket options to change from the default. sock_setsockopt not used because it expects user space buffer */ + cFYI(1,("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",(*csocket)->sk->sk_sndbuf, + (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo)); (*csocket)->sk->sk_rcvtimeo = 7 * HZ; - cERROR(1,("sndbuf %d rcvbuf %d reset to 200K each",(*csocket)->sk->sk_sndbuf, (*csocket)->sk->sk_rcvbuf)); - (*csocket)->sk->sk_sndbuf = 300 * 1024; - (*csocket)->sk->sk_rcvbuf = 200 * 1024; - /* send RFC1001 sessinit */ + /* make the bufsizes depend on wsize/rsize and max requests */ + if((*csocket)->sk->sk_sndbuf < (200 * 1024)) + (*csocket)->sk->sk_sndbuf = 200 * 1024; + if((*csocket)->sk->sk_rcvbuf < (140 * 1024)) + (*csocket)->sk->sk_rcvbuf = 140 * 1024; + /* send RFC1001 sessinit */ if(psin_server->sin_port == htons(RFC1001_PORT)) { /* some servers require RFC1001 sessinit before sending negprot - BB check reconnection in case where second -- cgit 1.2.3-korg From 9e2e85f82fa280e937ee42152e7cbaff78be01a2 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 14:28:38 -0700 Subject: [CIFS] Fix minor build problem with previous changeset Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index fd5eae37f2a84d..99e087d3554fb1 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -408,21 +408,24 @@ static struct quotactl_ops cifs_quotactl_ops = { static void cifs_umount_begin(struct super_block * sblock) { struct cifs_sb_info *cifs_sb; + struct cifsTconInfo * tcon; cifs_sb = CIFS_SB(sb); if(cifs_sb == NULL) - return -EIO; - if(cifs_sb->tcon == NULL) - return -EIO; + return; + + tcon = cifs_sb->tcon; + if(tcon == NULL) + return; down(&tcon->tconSem); if (atomic_read(&tcon->useCount) == 1) tcon->tidStatus = CifsExiting; up(&tcon->tconSem); - if((cifs->sb->tcon->ses) && (cifs_sb->tcon->ses->server)) + if(tcon->ses && tcon->ses->server) { cERROR(1,("wake up tasks now - umount begin not complete")); - wake_up_all(&server->request_q); + wake_up_all(&tcon->ses->server->request_q); } /* BB FIXME - finish add checks for tidStatus BB */ -- cgit 1.2.3-korg From 34210f33025a3256b9ac3f0a182f02f1879140cb Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 14:31:13 -0700 Subject: [CIFS] Still missing a line from previous fix Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 99e087d3554fb1..3bc9db522600b5 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -410,7 +410,7 @@ static void cifs_umount_begin(struct super_block * sblock) struct cifs_sb_info *cifs_sb; struct cifsTconInfo * tcon; - cifs_sb = CIFS_SB(sb); + cifs_sb = CIFS_SB(sblock); if(cifs_sb == NULL) return; -- cgit 1.2.3-korg From 4ca9c190d902caa7efb899a4c7fc8c6e6d926e95 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 10 Oct 2005 19:52:13 -0700 Subject: [CIFS] Fix oops in experimental notify code (when CONFIG_CIFS_EXPERIMENTAL was turned on). Signed-off-by: Steve French --- fs/cifs/CHANGES | 3 ++- fs/cifs/README | 2 ++ fs/cifs/cifsfs.c | 4 ++++ fs/cifs/fcntl.c | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 1c249a2abae384..b2a938378bef66 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -8,7 +8,8 @@ allowing an SMBs TCP length to be up to a few bytes longer than it should be. wsize and rsize can now be larger than negotiated buffer size if server supports large readx/writex, even when directio mount flag not specified. Write size will in many cases now be 16K instead of 4K which greatly helps -file copy performance on lightly loaded networks. +file copy performance on lightly loaded networks. Fix oops in dnotify +when experimental config flag enabled. Version 1.37 ------------ diff --git a/fs/cifs/README b/fs/cifs/README index 5d9a953888d9e9..e7a3ce62d71b35 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -296,6 +296,8 @@ A partial list of the supported mount options follows: unused. rsize default read size (usually 16K) wsize default write size (usually 16K, 32K is often better over GigE) + maximum wsize currently allowed by CIFS is 57344 (14 4096 byte + pages) rw mount the network share read-write (note that the server may still consider the share read-only) ro mount network share read-only diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 3bc9db522600b5..d2d16a9c1f05d3 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -901,6 +901,10 @@ init_cifs(void) INIT_LIST_HEAD(&GlobalSMBSessionList); INIT_LIST_HEAD(&GlobalTreeConnectionList); INIT_LIST_HEAD(&GlobalOplock_Q); +#ifdef CONFIG_CIFS_EXPERIMENTAL + INIT_LIST_HEAD(&GlobalDnotifyReqList); + INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); +#endif /* * Initialize Global counters */ diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index d527e2c76073e6..a7a47bb36bf308 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -78,6 +78,10 @@ int cifs_dir_notify(struct file * file, unsigned long arg) __u32 filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES; __u16 netfid; + + if(experimEnabled == 0) + return 0; + xid = GetXid(); cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; -- cgit 1.2.3-korg From 1047abc159b4eb4ba4a7342a0969e16e9d4b4c69 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 11 Oct 2005 19:58:06 -0700 Subject: [CIFS] CIFS Stats improvements New cifs_writepages routine was not updated bytes written in cifs stats. Also added ability to clear /proc/fs/cifs/Stats by writing (0 or 1) to it. Signed-off-by: Steve French --- fs/cifs/CHANGES | 2 +- fs/cifs/README | 11 +++++++++-- fs/cifs/cifs_debug.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- fs/cifs/cifs_debug.h | 5 ++++- fs/cifs/cifsglob.h | 6 +++++- fs/cifs/connect.c | 3 +++ fs/cifs/file.c | 3 +++ fs/cifs/netmisc.c | 2 +- fs/cifs/transport.c | 25 ++++++++++++++++++++++++- 9 files changed, 98 insertions(+), 8 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index b2a938378bef66..f554a70c9cf3a5 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -9,7 +9,7 @@ wsize and rsize can now be larger than negotiated buffer size if server supports large readx/writex, even when directio mount flag not specified. Write size will in many cases now be 16K instead of 4K which greatly helps file copy performance on lightly loaded networks. Fix oops in dnotify -when experimental config flag enabled. +when experimental config flag enabled. Make cifsFYI more granular. Version 1.37 ------------ diff --git a/fs/cifs/README b/fs/cifs/README index e7a3ce62d71b35..bb90941826adb4 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -482,9 +482,16 @@ These experimental features and tracing can be enabled by changing flags in kernel, e.g. insmod cifs). To enable a feature set it to 1 e.g. to enable tracing to the kernel message log type: - echo 1 > /proc/fs/cifs/cifsFYI + echo 7 > /proc/fs/cifs/cifsFYI -and for more extensive tracing including the start of smb requests and responses +cifsFYI functions as a bit mask. Setting it to 1 enables additional kernel +logging of various informational messages. 2 enables logging of non-zero +SMB return codes while 4 enables logging of requests that take longer +than one second to complete (except for byte range lock requests). +Setting it to 4 requires defining CONFIG_CIFS_STATS2 manually in the +source code (typically by setting it in the beginning of cifsglob.h), +and setting it to seven enables all three. Finally, tracing +the start of smb requests and responses can be enabled via: echo 1 > /proc/fs/cifs/traceSMB diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 6f7810992db3d6..f4054d695f81f7 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -203,6 +203,49 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, } #ifdef CONFIG_CIFS_STATS + +static int +cifs_stats_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char c; + int rc; + struct list_head *tmp; + struct cifsTconInfo *tcon; + + rc = get_user(c, buffer); + if (rc) + return rc; + + if (c == '1' || c == 'y' || c == 'Y') { + read_lock(&GlobalSMBSeslock); + list_for_each(tmp, &GlobalTreeConnectionList) { + tcon = list_entry(tmp, struct cifsTconInfo, + cifsConnectionList); + atomic_set(&tcon->num_smbs_sent, 0); + atomic_set(&tcon->num_writes, 0); + atomic_set(&tcon->num_reads, 0); + atomic_set(&tcon->num_oplock_brks, 0); + atomic_set(&tcon->num_opens, 0); + atomic_set(&tcon->num_closes, 0); + atomic_set(&tcon->num_deletes, 0); + atomic_set(&tcon->num_mkdirs, 0); + atomic_set(&tcon->num_rmdirs, 0); + atomic_set(&tcon->num_renames, 0); + atomic_set(&tcon->num_t2renames, 0); + atomic_set(&tcon->num_ffirst, 0); + atomic_set(&tcon->num_fnext, 0); + atomic_set(&tcon->num_fclose, 0); + atomic_set(&tcon->num_hardlinks, 0); + atomic_set(&tcon->num_symlinks, 0); + atomic_set(&tcon->num_locks, 0); + } + read_unlock(&GlobalSMBSeslock); + } + + return count; +} + static int cifs_stats_read(char *buf, char **beginBuffer, off_t offset, int count, int *eof, void *data) @@ -365,8 +408,10 @@ cifs_proc_init(void) cifs_debug_data_read, NULL); #ifdef CONFIG_CIFS_STATS - create_proc_read_entry("Stats", 0, proc_fs_cifs, + pde = create_proc_read_entry("Stats", 0, proc_fs_cifs, cifs_stats_read, NULL); + if (pde) + pde->write_proc = cifs_stats_write; #endif pde = create_proc_read_entry("cifsFYI", 0, proc_fs_cifs, cifsFYI_read, NULL); @@ -483,6 +528,8 @@ cifsFYI_write(struct file *file, const char __user *buffer, cifsFYI = 0; else if (c == '1' || c == 'y' || c == 'Y') cifsFYI = 1; + else if((c > '1') && (c <= '9')) + cifsFYI = (int) (c - '0'); /* see cifs_debug.h for meanings */ return count; } diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index bf24d2828f6857..4304d9dcfb6c65 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -26,6 +26,9 @@ void cifs_dump_mem(char *label, void *data, int length); extern int traceSMB; /* flag which enables the function below */ void dump_smb(struct smb_hdr *, int); +#define CIFS_INFO 0x01 +#define CIFS_RC 0x02 +#define CIFS_TIMER 0x04 /* * debug ON @@ -36,7 +39,7 @@ void dump_smb(struct smb_hdr *, int); /* information message: e.g., configuration, major event */ extern int cifsFYI; -#define cifsfyi(format,arg...) if (cifsFYI) printk(KERN_DEBUG " " __FILE__ ": " format "\n" "" , ## arg) +#define cifsfyi(format,arg...) if (cifsFYI & CIFS_INFO) printk(KERN_DEBUG " " __FILE__ ": " format "\n" "" , ## arg) #define cFYI(button,prspec) if (button) cifsfyi prspec diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 729717281b4086..839a55667c3ca1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -377,7 +377,11 @@ struct mid_q_entry { __u16 mid; /* multiplex id */ __u16 pid; /* process id */ __u32 sequence_number; /* for CIFS signing */ - struct timeval when_sent; /* time when smb sent */ + unsigned long when_alloc; /* when mid was created */ +#ifdef CONFIG_CIFS_STATS2 + unsigned long when_sent; /* time when smb send finished */ + unsigned long when_received; /* when demux complete (taken off wire) */ +#endif struct cifsSesInfo *ses; /* smb was sent to this server */ struct task_struct *tsk; /* task waiting for response */ struct smb_hdr *resp_buf; /* response buffer */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 177289771abeaa..a8f0cbada0f0ac 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -605,6 +605,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) multi_t2_fnd: task_to_wake = mid_entry->tsk; mid_entry->midState = MID_RESPONSE_RECEIVED; +#ifdef CONFIG_CIFS_STATS2 + mid_entry->when_received = jiffies; +#endif break; } } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 11806c879c4715..585a62aebd590c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1153,6 +1153,9 @@ retry: rc, bytes_written)); set_bit(AS_EIO, &mapping->flags); SetPageError(page); + } else { + cifs_stats_bytes_written(cifs_sb->tcon, + bytes_written); } for (i = 0; i < n_iov; i++) { page = pvec.pages[first + i]; diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 29e6efc5597c37..f7814689844b2f 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -813,7 +813,7 @@ map_smb_to_linux_error(struct smb_hdr *smb) if (smb->Flags2 & SMBFLG2_ERR_STATUS) { /* translate the newer STATUS codes to old style errors and then to POSIX errors */ __u32 err = le32_to_cpu(smb->Status.CifsError); - if(cifsFYI) + if(cifsFYI & CIFS_RC) cifs_print_status(err); ntstatus_to_dos(err, &smberrclass, &smberrcode); } else { diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index d8865fbd876ab7..981ea0d8b9cdaa 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -59,7 +59,9 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) temp->pid = current->pid; temp->command = smb_buffer->Command; cFYI(1, ("For smb_command %d", temp->command)); - do_gettimeofday(&temp->when_sent); + /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ + /* when mid allocated can be before when sent */ + temp->when_alloc = jiffies; temp->ses = ses; temp->tsk = current; } @@ -75,6 +77,9 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) static void DeleteMidQEntry(struct mid_q_entry *midEntry) { +#ifdef CONFIG_CIFS_STATS2 + unsigned long now; +#endif spin_lock(&GlobalMid_Lock); midEntry->midState = MID_FREE; list_del(&midEntry->qhead); @@ -84,6 +89,22 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) cifs_buf_release(midEntry->resp_buf); else cifs_small_buf_release(midEntry->resp_buf); +#ifdef CONFIG_CIFS_STATS2 + now = jiffies; + /* commands taking longer than one second are indications that + 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, midEntry->mid); + printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n", + now - midEntry->when_alloc, + now - midEntry->when_sent, + now - midEntry->when_received); + } + } +#endif mempool_free(midEntry, cifs_mid_poolp); } @@ -382,6 +403,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, (struct sockaddr *) &(ses->server->addr.sockAddr)); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); + midQ->when_sent = jiffies; #endif if(rc < 0) { DeleteMidQEntry(midQ); @@ -646,6 +668,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, (struct sockaddr *) &(ses->server->addr.sockAddr)); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); + midQ->when_sent = jiffies; #endif if(rc < 0) { DeleteMidQEntry(midQ); -- cgit 1.2.3-korg From 47c786e79b46ef478a1123cb57c711ecb481cbfa Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 11 Oct 2005 20:03:18 -0700 Subject: [CIFS] Add null malloc response check in notify experimental code Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifs_debug.c | 2 +- fs/cifs/cifssmb.c | 32 +++++++++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index f4054d695f81f7..22a444a3fe4c2f 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -217,7 +217,7 @@ cifs_stats_write(struct file *file, const char __user *buffer, if (rc) return rc; - if (c == '1' || c == 'y' || c == 'Y') { + if (c == '1' || c == 'y' || c == 'Y' || c == '0') { read_lock(&GlobalSMBSeslock); list_for_each(tmp, &GlobalTreeConnectionList) { tcon = list_entry(tmp, struct cifsTconInfo, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 41996a240149dd..9312bfc5668202 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -4294,20 +4294,26 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, cFYI(1, ("Error in Notify = %d", rc)); } else { /* Add file to outstanding requests */ + /* BB change to kmem cache alloc */ dnotify_req = (struct dir_notify_req *) kmalloc( - sizeof(struct dir_notify_req), GFP_KERNEL); - dnotify_req->Pid = pSMB->hdr.Pid; - dnotify_req->PidHigh = pSMB->hdr.PidHigh; - dnotify_req->Mid = pSMB->hdr.Mid; - dnotify_req->Tid = pSMB->hdr.Tid; - dnotify_req->Uid = pSMB->hdr.Uid; - dnotify_req->netfid = netfid; - dnotify_req->pfile = pfile; - dnotify_req->filter = filter; - dnotify_req->multishot = multishot; - spin_lock(&GlobalMid_Lock); - list_add_tail(&dnotify_req->lhead, &GlobalDnotifyReqList); - spin_unlock(&GlobalMid_Lock); + sizeof(struct dir_notify_req), + GFP_KERNEL); + if(dnotify_req) { + dnotify_req->Pid = pSMB->hdr.Pid; + dnotify_req->PidHigh = pSMB->hdr.PidHigh; + dnotify_req->Mid = pSMB->hdr.Mid; + dnotify_req->Tid = pSMB->hdr.Tid; + dnotify_req->Uid = pSMB->hdr.Uid; + dnotify_req->netfid = netfid; + dnotify_req->pfile = pfile; + dnotify_req->filter = filter; + dnotify_req->multishot = multishot; + spin_lock(&GlobalMid_Lock); + list_add_tail(&dnotify_req->lhead, + &GlobalDnotifyReqList); + spin_unlock(&GlobalMid_Lock); + } else + rc = -ENOMEM; } cifs_buf_release(pSMB); return rc; -- cgit 1.2.3-korg From 84d2f07e8e5e2424eec0f5acfef6792c924a0549 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 12 Oct 2005 15:32:05 -0700 Subject: CIFS: cifs_writepages should not write beyond end of file Signed-off-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/file.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 585a62aebd590c..23af20d5af7c41 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1011,6 +1011,7 @@ static int cifs_writepages(struct address_space *mapping, pgoff_t index; int is_range = 0; struct kvec iov[32]; + int len; int n_iov = 0; pgoff_t next; int nr_pages; @@ -1124,16 +1125,26 @@ retry: unlock_page(page); break; } + + if (page_offset(page) >= mapping->host->i_size) { + done = 1; + unlock_page(page); + break; + } + /* * BB can we get rid of this? pages are held by pvec */ page_cache_get(page); + len = min(mapping->host->i_size - page_offset(page), + (loff_t)PAGE_CACHE_SIZE); + /* reserve iov[0] for the smb header */ n_iov++; iov[n_iov].iov_base = kmap(page); - iov[n_iov].iov_len = PAGE_CACHE_SIZE; - bytes_to_write += PAGE_CACHE_SIZE; + iov[n_iov].iov_len = len; + bytes_to_write += len; if (first < 0) { first = i; -- cgit 1.2.3-korg From 23e7dd7d95f6fdc167a6d6ddea79ced0af33bbff Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 20 Oct 2005 13:44:56 -0700 Subject: [CIFS] Defer close of file handle slightly if there are pending writes that need to get in ahead of it that depend on that file handle. Fixes occassional bad file handle errors on write with heavy use multiple process cases. Signed-off-by: Steve French --- fs/cifs/CHANGES | 6 ++++ fs/cifs/cifsfs.h | 2 +- fs/cifs/cifsglob.h | 1 + fs/cifs/file.c | 88 +++++++++++++++++++++++++++++++++++++++--------------- fs/cifs/inode.c | 1 + 5 files changed, 73 insertions(+), 25 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index f554a70c9cf3a5..5bab24f59053f0 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,9 @@ +Version 1.39 +------------ +Defer close of a file handle slightly if pending writes depend on that file handle +(this reduces the EBADF bad file handle errors that can be logged under heavy +stress on writes). + Version 1.38 ------------ Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 4cdb29fdc8c2bf..1223fa81dbd269 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -97,5 +97,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); extern int cifs_ioctl (struct inode * inode, struct file * filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.38" +#define CIFS_VERSION "1.39" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 839a55667c3ca1..1ba08f8c5bc4aa 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -299,6 +299,7 @@ struct cifsFileInfo { struct inode * pInode; /* needed for oplock break */ unsigned closePend:1; /* file is marked to close */ unsigned invalidHandle:1; /* file closed via session abend */ + atomic_t wrtPending; /* handle in use - defer close */ struct semaphore fh_sem; /* prevents reopen race after dead ses*/ char * search_resume_name; /* BB removeme BB */ unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 23af20d5af7c41..da4f5e10b3cc0f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "cifsfs.h" #include "cifspdu.h" @@ -50,6 +51,11 @@ static inline struct cifsFileInfo *cifs_init_private( private_data->pInode = inode; private_data->invalidHandle = FALSE; private_data->closePend = FALSE; + /* we have to track num writers to the inode, since writepages + does not tell us which handle the write is for so there can + be a close (overlapping with write) of the filehandle that + cifs_writepages chose to use */ + atomic_set(&private_data->wrtPending,0); return private_data; } @@ -473,6 +479,20 @@ int cifs_close(struct inode *inode, struct file *file) /* no sense reconnecting to close a file that is already closed */ if (pTcon->tidStatus != CifsNeedReconnect) { + int timeout = 2; + while((atomic_read(&pSMBFile->wrtPending) != 0) + && (timeout < 1000) ) { + /* Give write a better chance to get to + server ahead of the close. We do not + want to add a wait_q here as it would + increase the memory utilization as + the struct would be in each open file, + but this should give enough time to + clear the socket */ + cERROR(1,("close with pending writes")); + msleep(timeout); + timeout *= 4; + } write_unlock(&file->f_owner.lock); rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); @@ -919,9 +939,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { + atomic_inc(&open_file->wrtPending); read_unlock(&GlobalSMBSeslock); if((open_file->invalidHandle) && - (!open_file->closePend)) { + (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) { rc = cifs_reopen_file(&cifs_inode->vfs_inode, open_file->pfile, FALSE); /* if it fails, try another handle - might be */ @@ -929,6 +950,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) if(rc) { cFYI(1,("failed on reopen file in wp")); read_lock(&GlobalSMBSeslock); + /* can not use this handle, no write + pending on this one after all */ + atomic_dec + (&open_file->wrtPending); continue; } } @@ -981,6 +1006,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) if (open_file) { bytes_written = cifs_write(open_file->pfile, write_data, to-from, &offset); + atomic_dec(&open_file->wrtPending); /* Does mm or vfs already set times? */ inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); if ((bytes_written > 0) && (offset)) { @@ -1016,7 +1042,7 @@ static int cifs_writepages(struct address_space *mapping, pgoff_t next; int nr_pages; __u64 offset = 0; - struct cifsFileInfo *open_file = NULL; + struct cifsFileInfo *open_file; struct page *page; struct pagevec pvec; int rc = 0; @@ -1071,15 +1097,6 @@ retry: int first; unsigned int i; - if (!open_file) { - open_file = find_writable_file(CIFS_I(mapping->host)); - if (!open_file) { - pagevec_release(&pvec); - cERROR(1, ("No writable handles for inode")); - return -EIO; - } - } - first = -1; next = 0; n_iov = 0; @@ -1155,18 +1172,32 @@ retry: break; } if (n_iov) { - rc = CIFSSMBWrite2(xid, cifs_sb->tcon, - open_file->netfid, bytes_to_write, - offset, &bytes_written, iov, n_iov, - 1); - if (rc || bytes_written < bytes_to_write) { - cERROR(1,("CIFSSMBWrite2 returned %d, written = %x", - rc, bytes_written)); - set_bit(AS_EIO, &mapping->flags); - SetPageError(page); + /* Search for a writable handle every time we call + * CIFSSMBWrite2. We can't rely on the last handle + * we used to still be valid + */ + open_file = find_writable_file(CIFS_I(mapping->host)); + if (!open_file) { + cERROR(1, ("No writable handles for inode")); + rc = -EBADF; } else { - cifs_stats_bytes_written(cifs_sb->tcon, - bytes_written); + rc = CIFSSMBWrite2(xid, cifs_sb->tcon, + open_file->netfid, + bytes_to_write, offset, + &bytes_written, iov, n_iov, + 1); + atomic_dec(&open_file->wrtPending); + if (rc || bytes_written < bytes_to_write) { + cERROR(1,("Write2 ret %d, written = %d", + rc, bytes_written)); + /* BB what if continued retry is + requested via mount flags? */ + set_bit(AS_EIO, &mapping->flags); + SetPageError(page); + } else { + cifs_stats_bytes_written(cifs_sb->tcon, + bytes_written); + } } for (i = 0; i < n_iov; i++) { page = pvec.pages[first + i]; @@ -1788,9 +1819,18 @@ static int cifs_readpage(struct file *file, struct page *page) page caching in the current Linux kernel design */ int is_size_safe_to_change(struct cifsInodeInfo *cifsInode) { - if (cifsInode && find_writable_file(cifsInode)) + struct cifsFileInfo *open_file = NULL; + + if (cifsInode) + open_file = find_writable_file(cifsInode); + + if(open_file) { + /* there is not actually a write pending so let + this handle go free and allow it to + be closable if needed */ + atomic_dec(&open_file->wrtPending); return 0; - else + } else return 1; } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ff4d1cc7c2484b..912d401600f6e2 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1006,6 +1006,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) __u32 npid = open_file->pid; rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, npid, FALSE); + atomic_dec(&open_file->wrtPending); cFYI(1,("SetFSize for attrs rc = %d", rc)); if(rc == -EINVAL) { int bytes_written; -- cgit 1.2.3-korg