📄 cifssmb.c
字号:
/* * fs/cifs/cifssmb.c * * Copyright (C) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * * Contains the routines for constructing the SMB PDUs themselves * * 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 */ /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ /* These are mostly routines that operate on a pathname, or on a tree id */ /* (mounted volume), but there are eight handle based routines which must be */ /* treated slightly differently for reconnection purposes since we never */ /* want to reuse a stale file handle and only the caller knows the file info */#include <linux/fs.h>#include <linux/kernel.h>#include <linux/vfs.h>#include <linux/version.h>#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)#include <linux/posix_acl_xattr.h>#endif#include <asm/uaccess.h>#include "cifspdu.h"#include "cifsglob.h"#include "cifsproto.h"#include "cifs_unicode.h"#include "cifs_debug.h"#include "cifsacl.h"#ifdef CONFIG_CIFS_POSIXstatic struct { int index; char *name;} protocols[] = {#ifdef CONFIG_CIFS_WEAK_PW_HASH {LANMAN_PROT, "\2LM1.2X002"}, {LANMAN2_PROT, "\2LANMAN2.1"},#endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {POSIX_PROT, "\2POSIX 2"}, {BAD_PROT, "\2"}};#elsestatic struct { int index; char *name;} protocols[] = {#ifdef CONFIG_CIFS_WEAK_PW_HASH {LANMAN_PROT, "\2LM1.2X002"}, {LANMAN2_PROT, "\2LANMAN2.1"},#endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {BAD_PROT, "\2"}};#endif/* define the number of elements in the cifs dialect array */#ifdef CONFIG_CIFS_POSIX#ifdef CONFIG_CIFS_WEAK_PW_HASH#define CIFS_NUM_PROT 4#else#define CIFS_NUM_PROT 2#endif /* CIFS_WEAK_PW_HASH */#else /* not posix */#ifdef CONFIG_CIFS_WEAK_PW_HASH#define CIFS_NUM_PROT 3#else#define CIFS_NUM_PROT 1#endif /* CONFIG_CIFS_WEAK_PW_HASH */#endif /* CIFS_POSIX *//* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */static void mark_open_files_invalid(struct cifsTconInfo *pTcon){ struct cifsFileInfo *open_file = NULL; struct list_head *tmp; struct list_head *tmp1;/* list all files open on tree connection and mark them invalid */ write_lock(&GlobalSMBSeslock); list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { open_file = list_entry(tmp, struct cifsFileInfo, tlist); if (open_file) open_file->invalidHandle = TRUE; } write_unlock(&GlobalSMBSeslock); /* BB Add call to invalidate_inodes(sb) for all superblocks mounted to this tcon */}/* If the return code is zero, this function must fill in request_buf pointer */static intsmall_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, void **request_buf /* returned */){ int rc = 0; /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for tcp and smb session status done differently for those three - in the calling routine */ if (tcon) { if (tcon->tidStatus == CifsExiting) { /* only tree disconnect, open, and write, (and ulogoff which does not have tcon) are allowed as we start force umount */ if ((smb_command != SMB_COM_WRITE_ANDX) && (smb_command != SMB_COM_OPEN_ANDX) && (smb_command != SMB_COM_TREE_DISCONNECT)) { cFYI(1, ("can not send cmd %d while umounting", smb_command)); return -ENODEV; } } if ((tcon->ses) && (tcon->ses->status != CifsExiting) && (tcon->ses->server)) { struct nls_table *nls_codepage; /* Give Demultiplex thread up to 10 seconds to reconnect, should be greater than cifs socket timeout which is 7 seconds */ while(tcon->ses->server->tcpStatus == CifsNeedReconnect) {#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) int timeout = 10 * HZ; while((tcon->ses->server->tcpStatus != CifsGood) && (timeout > 0)) { timeout = interruptible_sleep_on_timeout( &tcon->ses->server->response_q, timeout); }#else wait_event_interruptible_timeout(tcon->ses->server->response_q, (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ);#endif if (tcon->ses->server->tcpStatus == CifsNeedReconnect) { /* on "soft" mounts we wait once */ if ((tcon->retry == FALSE) || (tcon->ses->status == CifsExiting)) { cFYI(1, ("gave up waiting on " "reconnect in smb_init")); return -EHOSTDOWN; } /* else "hard" mount - keep retrying until process is killed or server comes back on-line */ } else /* TCP session is reestablished now */ break; } nls_codepage = load_nls_default(); /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); if (tcon->ses->status == CifsNeedReconnect) rc = cifs_setup_session(0, tcon->ses, nls_codepage); if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); /* tell server which Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) reset_cifs_unix_caps(0 /* no xid */, tcon, NULL /* we do not know sb */, NULL /* no vol info */); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ if (rc == 0) atomic_inc(&tconInfoReconnectCount); cFYI(1, ("reconnect tcon rc = %d", rc)); /* Removed call to reopen open files here. It is safer (and faster) to reopen files one at a time as needed in read and write */ /* Check if handle based operation so we know whether we can continue or not without returning to caller to reset file handle */ switch (smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: case SMB_COM_FIND_CLOSE2: case SMB_COM_LOCKING_ANDX: { unload_nls(nls_codepage); return -EAGAIN; } } } else { up(&tcon->ses->sesSem); } unload_nls(nls_codepage); } else { return -EIO; } } if (rc) return rc; *request_buf = cifs_small_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM; } header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct); if (tcon != NULL) cifs_stats_inc(&tcon->num_smbs_sent); return rc;}intsmall_smb_init_no_tc(const int smb_command, const int wct, struct cifsSesInfo *ses, void **request_buf){ int rc; struct smb_hdr *buffer; rc = small_smb_init(smb_command, wct, NULL, request_buf); if (rc) return rc; buffer = (struct smb_hdr *)*request_buf; buffer->Mid = GetNextMid(ses->server); if (ses->capabilities & CAP_UNICODE) buffer->Flags2 |= SMBFLG2_UNICODE; if (ses->capabilities & CAP_STATUS32) buffer->Flags2 |= SMBFLG2_ERR_STATUS; /* uid, tid can stay at zero as set in header assemble */ /* BB add support for turning on the signing when this function is used after 1st of session setup requests */ return rc;}/* If the return code is zero, this function must fill in request_buf pointer */static intsmb_init(int smb_command, int wct, struct cifsTconInfo *tcon, void **request_buf /* returned */ , void **response_buf /* returned */ ){ int rc = 0; /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for tcp and smb session status done differently for those three - in the calling routine */ if (tcon) { if (tcon->tidStatus == CifsExiting) { /* only tree disconnect, open, and write, (and ulogoff which does not have tcon) are allowed as we start force umount */ if ((smb_command != SMB_COM_WRITE_ANDX) && (smb_command != SMB_COM_OPEN_ANDX) && (smb_command != SMB_COM_TREE_DISCONNECT)) { cFYI(1, ("can not send cmd %d while umounting", smb_command)); return -ENODEV; } } if ((tcon->ses) && (tcon->ses->status != CifsExiting) && (tcon->ses->server)) { struct nls_table *nls_codepage; /* Give Demultiplex thread up to 10 seconds to reconnect, should be greater than cifs socket timeout which is 7 seconds */ while(tcon->ses->server->tcpStatus == CifsNeedReconnect) {#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) int timeout = 10 * HZ; while((tcon->ses->server->tcpStatus != CifsGood) && (timeout > 0)) { timeout = interruptible_sleep_on_timeout (&tcon->ses->server->response_q, timeout); }#else wait_event_interruptible_timeout(tcon->ses->server->response_q, (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ);#endif if (tcon->ses->server->tcpStatus == CifsNeedReconnect) { /* on "soft" mounts we wait once */ if ((tcon->retry == FALSE) || (tcon->ses->status == CifsExiting)) { cFYI(1, ("gave up waiting on " "reconnect in smb_init")); return -EHOSTDOWN; } /* else "hard" mount - keep retrying until process is killed or server comes on-line */ } else /* TCP session is reestablished now */ break; } nls_codepage = load_nls_default(); /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); if (tcon->ses->status == CifsNeedReconnect) rc = cifs_setup_session(0, tcon->ses, nls_codepage); if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); /* tell server which Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) reset_cifs_unix_caps(0 /* no xid */, tcon, NULL /* do not know sb */, NULL /* no vol info */); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ if (rc == 0) atomic_inc(&tconInfoReconnectCount); cFYI(1, ("reconnect tcon rc = %d", rc)); /* Removed call to reopen open files here. It is safer (and faster) to reopen files one at a time as needed in read and write */ /* Check if handle based operation so we know whether we can continue or not without returning to caller to reset file handle */ switch (smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: case SMB_COM_FIND_CLOSE2: case SMB_COM_LOCKING_ANDX: { unload_nls(nls_codepage); return -EAGAIN; } } } else { up(&tcon->ses->sesSem); } unload_nls(nls_codepage); } else { return -EIO; } } if (rc) return rc; *request_buf = cifs_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM; } /* Although the original thought was we needed the response buf for */ /* potential retries of smb operations it turns out we can determine */ /* from the mid flags when the request buffer can be resent without */ /* having to use a second distinct buffer for the response */ if (response_buf) *response_buf = *request_buf; header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct /*wct */ ); if (tcon != NULL) cifs_stats_inc(&tcon->num_smbs_sent); return rc;}static int validate_t2(struct smb_t2_rsp *pSMB){ int rc = -EINVAL; int total_size; char *pBCC; /* check for plausible wct, bcc and t2 data and parm sizes */ /* check for parm and data offset going beyond end of smb */ if (pSMB->hdr.WordCount >= 10) { if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) && (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) { /* check that bcc is at least as big as parms + data */ /* check that bcc is less than negotiated smb buffer */ total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount); if (total_size < 512) { total_size += le16_to_cpu(pSMB->t2_rsp.DataCount); /* BCC le converted in SendReceive */ pBCC = (pSMB->hdr.WordCount * 2) + sizeof(struct smb_hdr) + (char *)pSMB; if ((total_size <= (*(u16 *)pBCC)) && (total_size < CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) { return 0; } } } } cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, sizeof(struct smb_t2_rsp) + 16); return rc;}intCIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses){ NEGOTIATE_REQ *pSMB; NEGOTIATE_RSP *pSMBr; int rc = 0; int bytes_returned; int i; struct TCP_Server_Info *server;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -