📄 connect.c
字号:
/* * fs/cifs/connect.c * * Copyright (C) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/fs.h>#include <linux/net.h>#include <linux/string.h>#include <linux/list.h>#include <linux/wait.h>#include <linux/ipv6.h>#include <linux/pagemap.h>#include <linux/ctype.h>#include <linux/utsname.h>#include <linux/delay.h>#include <linux/completion.h>#include <linux/version.h>#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)#include <linux/mempool.h>#include <linux/pagevec.h>#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)#include <linux/freezer.h>#endif /* 2.6.19 */#endif#include <linux/kthread.h>#include <asm/uaccess.h>#include <asm/processor.h>#include "cifspdu.h"#include "cifsglob.h"#include "cifsproto.h"#include "cifs_unicode.h"#include "cifs_debug.h"#include "cifs_fs_sb.h"#include "ntlmssp.h"#include "nterr.h"#include "rfc1002pdu.h"#include "cn_cifs.h"#define CIFS_PORT 445#define RFC1001_PORT 139#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 7)#define sock_create_kern sock_create#endifstatic DECLARE_COMPLETION(cifsd_complete);extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)extern mempool_t *cifs_req_poolp;#else /* old 2.4 kernel */#ifndef PAGEVEC_SIZE#define PAGEVEC_SIZE 14#endif#endifstruct smb_vol { char *username; char *password; char *domainname; char *UNC; char *UNCip; 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; mode_t dir_mode; unsigned secFlg; unsigned rw:1; unsigned retry:1; unsigned intr:1; unsigned setuids:1; unsigned override_uid:1; unsigned override_gid:1; unsigned noperm:1; unsigned no_psx_acl:1; /* set if posix acl support should be disabled */ unsigned cifs_acl:1; unsigned no_xattr:1; /* set if xattr (EA) support should be disabled*/ 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 no_linux_ext:1; unsigned sfu_emul:1; unsigned nullauth:1; /* attempt to authenticate with null user */ unsigned nocase; /* request case insensitive filenames */ unsigned nobrl; /* disable sending byte range locks to srv */ unsigned int rsize; unsigned int wsize; unsigned int sockopt; unsigned short int port; char *prepath;};static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, char *netb_name, char *server_netb_name);static int ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket); /* * cifs tcp session reconnection * * mark tcp session as reconnecting so temporarily locked * mark all smb sessions as reconnecting for tcp session * reconnect tcp session * wake up waiters on reconnection? - (not needed currently) */static intcifs_reconnect(struct TCP_Server_Info *server){ int rc = 0; struct list_head *tmp; struct cifsSesInfo *ses; struct cifsTconInfo *tcon; struct mid_q_entry *mid_entry; spin_lock(&GlobalMid_Lock); if (kthread_should_stop()) { /* the demux thread will exit normally next time through the loop */ spin_unlock(&GlobalMid_Lock); return rc; } else server->tcpStatus = CifsNeedReconnect; spin_unlock(&GlobalMid_Lock); server->maxBuf = 0; cFYI(1, ("Reconnecting tcp session")); /* before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they are not used until reconnected */ read_lock(&GlobalSMBSeslock); list_for_each(tmp, &GlobalSMBSessionList) { ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); if (ses->server) { if (ses->server == server) { ses->status = CifsNeedReconnect; ses->ipc_tid = 0; } } /* else tcp and smb sessions need reconnection */ } list_for_each(tmp, &GlobalTreeConnectionList) { tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); if ((tcon) && (tcon->ses) && (tcon->ses->server == server)) tcon->tidStatus = CifsNeedReconnect; } read_unlock(&GlobalSMBSeslock); /* do not want to be sending data on a socket we are freeing */ down(&server->tcpSem); if (server->ssocket) { cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state, server->ssocket->flags)); server->ssocket->ops->shutdown(server->ssocket, SEND_SHUTDOWN); cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx", server->ssocket->state, server->ssocket->flags)); sock_release(server->ssocket); server->ssocket = NULL; } spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if (mid_entry) { if (mid_entry->midState == MID_REQUEST_SUBMITTED) { /* Mark other intransit requests as needing retry so we do not immediately mark the session bad again (ie after we reconnect below) as they timeout too */ mid_entry->midState = MID_RETRY_NEEDED; } } } spin_unlock(&GlobalMid_Lock); up(&server->tcpSem); while ((!kthread_should_stop()) && (server->tcpStatus != CifsGood)) {#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 12) try_to_freeze();#endif if (server->protocolType == IPV6) { rc = ipv6_connect(&server->addr.sockAddr6, &server->ssocket); } else { rc = ipv4_connect(&server->addr.sockAddr, &server->ssocket, server->workstation_RFC1001_name, server->server_RFC1001_name); } if (rc) { cFYI(1, ("reconnect error %d", rc)); msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); spin_lock(&GlobalMid_Lock); if (!kthread_should_stop()) server->tcpStatus = CifsGood; server->sequence_number = 0; spin_unlock(&GlobalMid_Lock); /* atomic_set(&server->inFlight,0);*/ wake_up(&server->response_q); } } return rc;}/* return codes: 0 not a transact2, or all data present >0 transact2 with that much data missing -EINVAL = invalid transact2 */static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize){ struct smb_t2_rsp *pSMBt; int total_data_size; int data_in_this_rsp; int remaining; if (pSMB->Command != SMB_COM_TRANSACTION2) return 0; /* check for plausible wct, bcc and t2 data and parm sizes */ /* check for parm and data offset going beyond end of smb */ if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ cFYI(1, ("invalid transact2 word count")); return -EINVAL; } pSMBt = (struct smb_t2_rsp *)pSMB; total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount); remaining = total_data_size - data_in_this_rsp; if (remaining == 0) return 0; else if (remaining < 0) { cFYI(1, ("total data %d smaller than data in frame %d", total_data_size, data_in_this_rsp)); return -EINVAL; } else { cFYI(1, ("missing %d bytes from transact2, check next response", remaining)); if (total_data_size > maxBufSize) { cERROR(1, ("TotalDataSize %d is over maximum buffer %d", total_data_size, maxBufSize)); return -EINVAL; } return remaining; }}static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB){ struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; int total_data_size; int total_in_buf; int remaining; int total_in_buf2; char *data_area_of_target; char *data_area_of_buf2; __u16 byte_count; total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) { cFYI(1, ("total data size of primary and secondary t2 differ")); } total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount); remaining = total_data_size - total_in_buf; if (remaining < 0) return -EINVAL; if (remaining == 0) /* nothing to do, ignore */ return 0; total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount); if (remaining < total_in_buf2) { cFYI(1, ("transact2 2nd response contains too much data")); } /* find end of first SMB data area */ data_area_of_target = (char *)&pSMBt->hdr.Protocol + le16_to_cpu(pSMBt->t2_rsp.DataOffset); /* validate target area */ data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol + le16_to_cpu(pSMB2->t2_rsp.DataOffset); data_area_of_target += total_in_buf; /* copy second buffer into end of first buffer */ memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); total_in_buf += total_in_buf2; pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf); byte_count = le16_to_cpu(BCC_LE(pTargetSMB)); byte_count += total_in_buf2; BCC_LE(pTargetSMB) = cpu_to_le16(byte_count); 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 = byte_count; if (remaining == total_in_buf2) { cFYI(1, ("found the last secondary response")); return 0; /* we are done */ } else /* more responses to go */ return 1;}static intcifs_demultiplex_thread(struct TCP_Server_Info *server){ int length; unsigned int pdu_length, total_read; struct smb_hdr *smb_buffer = NULL; struct smb_hdr *bigbuf = NULL; struct smb_hdr *smallbuf = NULL; struct msghdr smb_msg; struct kvec iov; struct socket *csocket = server->ssocket; struct list_head *tmp; struct cifsSesInfo *ses; struct task_struct *task_to_wake = NULL; struct mid_q_entry *mid_entry; char temp; int isLargeBuf = FALSE; int isMultiRsp; int reconnect;#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) mm_segment_t temp_fs;#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) daemonize(); sprintf(current->comm,"cifsd");#endif current->flags |= PF_MEMALLOC; server->tsk = current; /* save process info to wake at shutdown */ cFYI(1, ("Demultiplex PID: %d", current->pid)); write_lock(&GlobalSMBSeslock); atomic_inc(&tcpSesAllocCount); length = tcpSesAllocCount.counter; write_unlock(&GlobalSMBSeslock); complete(&cifsd_complete);#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10) if (length > 1) mempool_resize(cifs_req_poolp, length + cifs_min_rcv, GFP_KERNEL);#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) temp_fs = get_fs(); /* we must turn off socket api parm checking */ set_fs(get_ds());#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22) set_freezable();#endif while (!kthread_should_stop()) {#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 12) if (try_to_freeze()) continue;#endif if (bigbuf == NULL) { bigbuf = cifs_buf_get(); if (!bigbuf) { cERROR(1, ("No memory for large SMB response")); msleep(3000); /* retry will check if exiting */ continue; } } else if (isLargeBuf) { /* we are reusing a dirty large buf, clear its start */ memset(bigbuf, 0, sizeof(struct smb_hdr)); } if (smallbuf == NULL) { smallbuf = cifs_small_buf_get(); if (!smallbuf) { cERROR(1, ("No memory for SMB response")); msleep(1000); /* retry will check if exiting */ continue; } /* beginning of smb buffer is cleared in our buf_get */ } else /* if existing small buf clear beginning */ memset(smallbuf, 0, sizeof(struct smb_hdr)); isLargeBuf = FALSE; isMultiRsp = FALSE; smb_buffer = smallbuf; iov.iov_base = smb_buffer; iov.iov_len = 4; smb_msg.msg_control = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -