18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * fs/cifs/misc.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2002,2008 58c2ecf20Sopenharmony_ci * Author(s): Steve French (sfrench@us.ibm.com) 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 88c2ecf20Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as published 98c2ecf20Sopenharmony_ci * by the Free Software Foundation; either version 2.1 of the License, or 108c2ecf20Sopenharmony_ci * (at your option) any later version. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This library is distributed in the hope that it will be useful, 138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 158c2ecf20Sopenharmony_ci * the GNU Lesser General Public License for more details. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public License 188c2ecf20Sopenharmony_ci * along with this library; if not, write to the Free Software 198c2ecf20Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/ctype.h> 248c2ecf20Sopenharmony_ci#include <linux/mempool.h> 258c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 268c2ecf20Sopenharmony_ci#include "cifspdu.h" 278c2ecf20Sopenharmony_ci#include "cifsglob.h" 288c2ecf20Sopenharmony_ci#include "cifsproto.h" 298c2ecf20Sopenharmony_ci#include "cifs_debug.h" 308c2ecf20Sopenharmony_ci#include "smberr.h" 318c2ecf20Sopenharmony_ci#include "nterr.h" 328c2ecf20Sopenharmony_ci#include "cifs_unicode.h" 338c2ecf20Sopenharmony_ci#include "smb2pdu.h" 348c2ecf20Sopenharmony_ci#include "cifsfs.h" 358c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL 368c2ecf20Sopenharmony_ci#include "dns_resolve.h" 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciextern mempool_t *cifs_sm_req_poolp; 408c2ecf20Sopenharmony_ciextern mempool_t *cifs_req_poolp; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* The xid serves as a useful identifier for each incoming vfs request, 438c2ecf20Sopenharmony_ci in a similar way to the mid which is useful to track each sent smb, 448c2ecf20Sopenharmony_ci and CurrentXid can also provide a running counter (although it 458c2ecf20Sopenharmony_ci will eventually wrap past zero) of the total vfs operations handled 468c2ecf20Sopenharmony_ci since the cifs fs was mounted */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciunsigned int 498c2ecf20Sopenharmony_ci_get_xid(void) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci unsigned int xid; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 548c2ecf20Sopenharmony_ci GlobalTotalActiveXid++; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* keep high water mark for number of simultaneous ops in filesystem */ 578c2ecf20Sopenharmony_ci if (GlobalTotalActiveXid > GlobalMaxActiveXid) 588c2ecf20Sopenharmony_ci GlobalMaxActiveXid = GlobalTotalActiveXid; 598c2ecf20Sopenharmony_ci if (GlobalTotalActiveXid > 65000) 608c2ecf20Sopenharmony_ci cifs_dbg(FYI, "warning: more than 65000 requests active\n"); 618c2ecf20Sopenharmony_ci xid = GlobalCurrentXid++; 628c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 638c2ecf20Sopenharmony_ci return xid; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_civoid 678c2ecf20Sopenharmony_ci_free_xid(unsigned int xid) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 708c2ecf20Sopenharmony_ci /* if (GlobalTotalActiveXid == 0) 718c2ecf20Sopenharmony_ci BUG(); */ 728c2ecf20Sopenharmony_ci GlobalTotalActiveXid--; 738c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct cifs_ses * 778c2ecf20Sopenharmony_cisesInfoAlloc(void) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct cifs_ses *ret_buf; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL); 828c2ecf20Sopenharmony_ci if (ret_buf) { 838c2ecf20Sopenharmony_ci atomic_inc(&sesInfoAllocCount); 848c2ecf20Sopenharmony_ci ret_buf->status = CifsNew; 858c2ecf20Sopenharmony_ci ++ret_buf->ses_count; 868c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ret_buf->smb_ses_list); 878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ret_buf->tcon_list); 888c2ecf20Sopenharmony_ci mutex_init(&ret_buf->session_mutex); 898c2ecf20Sopenharmony_ci spin_lock_init(&ret_buf->iface_lock); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci return ret_buf; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_civoid 958c2ecf20Sopenharmony_cisesInfoFree(struct cifs_ses *buf_to_free) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci if (buf_to_free == NULL) { 988c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n"); 998c2ecf20Sopenharmony_ci return; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci atomic_dec(&sesInfoAllocCount); 1038c2ecf20Sopenharmony_ci kfree(buf_to_free->serverOS); 1048c2ecf20Sopenharmony_ci kfree(buf_to_free->serverDomain); 1058c2ecf20Sopenharmony_ci kfree(buf_to_free->serverNOS); 1068c2ecf20Sopenharmony_ci kfree_sensitive(buf_to_free->password); 1078c2ecf20Sopenharmony_ci kfree(buf_to_free->user_name); 1088c2ecf20Sopenharmony_ci kfree(buf_to_free->domainName); 1098c2ecf20Sopenharmony_ci kfree_sensitive(buf_to_free->auth_key.response); 1108c2ecf20Sopenharmony_ci kfree(buf_to_free->iface_list); 1118c2ecf20Sopenharmony_ci kfree_sensitive(buf_to_free); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistruct cifs_tcon * 1158c2ecf20Sopenharmony_citconInfoAlloc(void) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct cifs_tcon *ret_buf; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); 1208c2ecf20Sopenharmony_ci if (!ret_buf) 1218c2ecf20Sopenharmony_ci return NULL; 1228c2ecf20Sopenharmony_ci ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL); 1238c2ecf20Sopenharmony_ci if (!ret_buf->crfid.fid) { 1248c2ecf20Sopenharmony_ci kfree(ret_buf); 1258c2ecf20Sopenharmony_ci return NULL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci atomic_inc(&tconInfoAllocCount); 1298c2ecf20Sopenharmony_ci ret_buf->tidStatus = CifsNew; 1308c2ecf20Sopenharmony_ci ++ret_buf->tc_count; 1318c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ret_buf->openFileList); 1328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ret_buf->tcon_list); 1338c2ecf20Sopenharmony_ci spin_lock_init(&ret_buf->open_file_lock); 1348c2ecf20Sopenharmony_ci mutex_init(&ret_buf->crfid.fid_mutex); 1358c2ecf20Sopenharmony_ci spin_lock_init(&ret_buf->stat_lock); 1368c2ecf20Sopenharmony_ci atomic_set(&ret_buf->num_local_opens, 0); 1378c2ecf20Sopenharmony_ci atomic_set(&ret_buf->num_remote_opens, 0); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return ret_buf; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid 1438c2ecf20Sopenharmony_citconInfoFree(struct cifs_tcon *buf_to_free) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci if (buf_to_free == NULL) { 1468c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n"); 1478c2ecf20Sopenharmony_ci return; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci atomic_dec(&tconInfoAllocCount); 1508c2ecf20Sopenharmony_ci kfree(buf_to_free->nativeFileSystem); 1518c2ecf20Sopenharmony_ci kfree_sensitive(buf_to_free->password); 1528c2ecf20Sopenharmony_ci kfree(buf_to_free->crfid.fid); 1538c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL 1548c2ecf20Sopenharmony_ci kfree(buf_to_free->dfs_path); 1558c2ecf20Sopenharmony_ci#endif 1568c2ecf20Sopenharmony_ci kfree(buf_to_free); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistruct smb_hdr * 1608c2ecf20Sopenharmony_cicifs_buf_get(void) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct smb_hdr *ret_buf = NULL; 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * SMB2 header is bigger than CIFS one - no problems to clean some 1658c2ecf20Sopenharmony_ci * more bytes for CIFS. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci size_t buf_size = sizeof(struct smb2_sync_hdr); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * We could use negotiated size instead of max_msgsize - 1718c2ecf20Sopenharmony_ci * but it may be more efficient to always alloc same size 1728c2ecf20Sopenharmony_ci * albeit slightly larger than necessary and maxbuffersize 1738c2ecf20Sopenharmony_ci * defaults to this and can not be bigger. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* clear the first few header bytes */ 1788c2ecf20Sopenharmony_ci /* for most paths, more is cleared in header_assemble */ 1798c2ecf20Sopenharmony_ci memset(ret_buf, 0, buf_size + 3); 1808c2ecf20Sopenharmony_ci atomic_inc(&bufAllocCount); 1818c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2 1828c2ecf20Sopenharmony_ci atomic_inc(&totBufAllocCount); 1838c2ecf20Sopenharmony_ci#endif /* CONFIG_CIFS_STATS2 */ 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return ret_buf; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_civoid 1898c2ecf20Sopenharmony_cicifs_buf_release(void *buf_to_free) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci if (buf_to_free == NULL) { 1928c2ecf20Sopenharmony_ci /* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/ 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci mempool_free(buf_to_free, cifs_req_poolp); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci atomic_dec(&bufAllocCount); 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistruct smb_hdr * 2028c2ecf20Sopenharmony_cicifs_small_buf_get(void) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct smb_hdr *ret_buf = NULL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* We could use negotiated size instead of max_msgsize - 2078c2ecf20Sopenharmony_ci but it may be more efficient to always alloc same size 2088c2ecf20Sopenharmony_ci albeit slightly larger than necessary and maxbuffersize 2098c2ecf20Sopenharmony_ci defaults to this and can not be bigger */ 2108c2ecf20Sopenharmony_ci ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS); 2118c2ecf20Sopenharmony_ci /* No need to clear memory here, cleared in header assemble */ 2128c2ecf20Sopenharmony_ci /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ 2138c2ecf20Sopenharmony_ci atomic_inc(&smBufAllocCount); 2148c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2 2158c2ecf20Sopenharmony_ci atomic_inc(&totSmBufAllocCount); 2168c2ecf20Sopenharmony_ci#endif /* CONFIG_CIFS_STATS2 */ 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return ret_buf; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_civoid 2228c2ecf20Sopenharmony_cicifs_small_buf_release(void *buf_to_free) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (buf_to_free == NULL) { 2268c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n"); 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci mempool_free(buf_to_free, cifs_sm_req_poolp); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci atomic_dec(&smBufAllocCount); 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_civoid 2368c2ecf20Sopenharmony_cifree_rsp_buf(int resp_buftype, void *rsp) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci if (resp_buftype == CIFS_SMALL_BUFFER) 2398c2ecf20Sopenharmony_ci cifs_small_buf_release(rsp); 2408c2ecf20Sopenharmony_ci else if (resp_buftype == CIFS_LARGE_BUFFER) 2418c2ecf20Sopenharmony_ci cifs_buf_release(rsp); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* NB: MID can not be set if treeCon not passed in, in that 2458c2ecf20Sopenharmony_ci case it is responsbility of caller to set the mid */ 2468c2ecf20Sopenharmony_civoid 2478c2ecf20Sopenharmony_ciheader_assemble(struct smb_hdr *buffer, char smb_command /* command */ , 2488c2ecf20Sopenharmony_ci const struct cifs_tcon *treeCon, int word_count 2498c2ecf20Sopenharmony_ci /* length of fixed section (word count) in two byte units */) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci char *temp = (char *) buffer; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci buffer->smb_buf_length = cpu_to_be32( 2568c2ecf20Sopenharmony_ci (2 * word_count) + sizeof(struct smb_hdr) - 2578c2ecf20Sopenharmony_ci 4 /* RFC 1001 length field does not count */ + 2588c2ecf20Sopenharmony_ci 2 /* for bcc field itself */) ; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci buffer->Protocol[0] = 0xFF; 2618c2ecf20Sopenharmony_ci buffer->Protocol[1] = 'S'; 2628c2ecf20Sopenharmony_ci buffer->Protocol[2] = 'M'; 2638c2ecf20Sopenharmony_ci buffer->Protocol[3] = 'B'; 2648c2ecf20Sopenharmony_ci buffer->Command = smb_command; 2658c2ecf20Sopenharmony_ci buffer->Flags = 0x00; /* case sensitive */ 2668c2ecf20Sopenharmony_ci buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; 2678c2ecf20Sopenharmony_ci buffer->Pid = cpu_to_le16((__u16)current->tgid); 2688c2ecf20Sopenharmony_ci buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); 2698c2ecf20Sopenharmony_ci if (treeCon) { 2708c2ecf20Sopenharmony_ci buffer->Tid = treeCon->tid; 2718c2ecf20Sopenharmony_ci if (treeCon->ses) { 2728c2ecf20Sopenharmony_ci if (treeCon->ses->capabilities & CAP_UNICODE) 2738c2ecf20Sopenharmony_ci buffer->Flags2 |= SMBFLG2_UNICODE; 2748c2ecf20Sopenharmony_ci if (treeCon->ses->capabilities & CAP_STATUS32) 2758c2ecf20Sopenharmony_ci buffer->Flags2 |= SMBFLG2_ERR_STATUS; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* Uid is not converted */ 2788c2ecf20Sopenharmony_ci buffer->Uid = treeCon->ses->Suid; 2798c2ecf20Sopenharmony_ci buffer->Mid = get_next_mid(treeCon->ses->server); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) 2828c2ecf20Sopenharmony_ci buffer->Flags2 |= SMBFLG2_DFS; 2838c2ecf20Sopenharmony_ci if (treeCon->nocase) 2848c2ecf20Sopenharmony_ci buffer->Flags |= SMBFLG_CASELESS; 2858c2ecf20Sopenharmony_ci if ((treeCon->ses) && (treeCon->ses->server)) 2868c2ecf20Sopenharmony_ci if (treeCon->ses->server->sign) 2878c2ecf20Sopenharmony_ci buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* endian conversion of flags is now done just before sending */ 2918c2ecf20Sopenharmony_ci buffer->WordCount = (char) word_count; 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int 2968c2ecf20Sopenharmony_cicheck_smb_hdr(struct smb_hdr *smb) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci /* does it have the right SMB "signature" ? */ 2998c2ecf20Sopenharmony_ci if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { 3008c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", 3018c2ecf20Sopenharmony_ci *(unsigned int *)smb->Protocol); 3028c2ecf20Sopenharmony_ci return 1; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* if it's a response then accept */ 3068c2ecf20Sopenharmony_ci if (smb->Flags & SMBFLG_RESPONSE) 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* only one valid case where server sends us request */ 3108c2ecf20Sopenharmony_ci if (smb->Command == SMB_COM_LOCKING_ANDX) 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", 3148c2ecf20Sopenharmony_ci get_mid(smb)); 3158c2ecf20Sopenharmony_ci return 1; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ciint 3198c2ecf20Sopenharmony_cicheckSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct smb_hdr *smb = (struct smb_hdr *)buf; 3228c2ecf20Sopenharmony_ci __u32 rfclen = be32_to_cpu(smb->smb_buf_length); 3238c2ecf20Sopenharmony_ci __u32 clc_len; /* calculated length */ 3248c2ecf20Sopenharmony_ci cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", 3258c2ecf20Sopenharmony_ci total_read, rfclen); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* is this frame too small to even get to a BCC? */ 3288c2ecf20Sopenharmony_ci if (total_read < 2 + sizeof(struct smb_hdr)) { 3298c2ecf20Sopenharmony_ci if ((total_read >= sizeof(struct smb_hdr) - 1) 3308c2ecf20Sopenharmony_ci && (smb->Status.CifsError != 0)) { 3318c2ecf20Sopenharmony_ci /* it's an error return */ 3328c2ecf20Sopenharmony_ci smb->WordCount = 0; 3338c2ecf20Sopenharmony_ci /* some error cases do not return wct and bcc */ 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci } else if ((total_read == sizeof(struct smb_hdr) + 1) && 3368c2ecf20Sopenharmony_ci (smb->WordCount == 0)) { 3378c2ecf20Sopenharmony_ci char *tmp = (char *)smb; 3388c2ecf20Sopenharmony_ci /* Need to work around a bug in two servers here */ 3398c2ecf20Sopenharmony_ci /* First, check if the part of bcc they sent was zero */ 3408c2ecf20Sopenharmony_ci if (tmp[sizeof(struct smb_hdr)] == 0) { 3418c2ecf20Sopenharmony_ci /* some servers return only half of bcc 3428c2ecf20Sopenharmony_ci * on simple responses (wct, bcc both zero) 3438c2ecf20Sopenharmony_ci * in particular have seen this on 3448c2ecf20Sopenharmony_ci * ulogoffX and FindClose. This leaves 3458c2ecf20Sopenharmony_ci * one byte of bcc potentially unitialized 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci /* zero rest of bcc */ 3488c2ecf20Sopenharmony_ci tmp[sizeof(struct smb_hdr)+1] = 0; 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n"); 3528c2ecf20Sopenharmony_ci } else { 3538c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Length less than smb header size\n"); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci return -EIO; 3568c2ecf20Sopenharmony_ci } else if (total_read < sizeof(*smb) + 2 * smb->WordCount) { 3578c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n", 3588c2ecf20Sopenharmony_ci __func__, smb->WordCount); 3598c2ecf20Sopenharmony_ci return -EIO; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* otherwise, there is enough to get to the BCC */ 3638c2ecf20Sopenharmony_ci if (check_smb_hdr(smb)) 3648c2ecf20Sopenharmony_ci return -EIO; 3658c2ecf20Sopenharmony_ci clc_len = smbCalcSize(smb, server); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (4 + rfclen != total_read) { 3688c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n", 3698c2ecf20Sopenharmony_ci rfclen); 3708c2ecf20Sopenharmony_ci return -EIO; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (4 + rfclen != clc_len) { 3748c2ecf20Sopenharmony_ci __u16 mid = get_mid(smb); 3758c2ecf20Sopenharmony_ci /* check if bcc wrapped around for large read responses */ 3768c2ecf20Sopenharmony_ci if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { 3778c2ecf20Sopenharmony_ci /* check if lengths match mod 64K */ 3788c2ecf20Sopenharmony_ci if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) 3798c2ecf20Sopenharmony_ci return 0; /* bcc wrapped */ 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", 3828c2ecf20Sopenharmony_ci clc_len, 4 + rfclen, mid); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (4 + rfclen < clc_len) { 3858c2ecf20Sopenharmony_ci cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", 3868c2ecf20Sopenharmony_ci rfclen, mid); 3878c2ecf20Sopenharmony_ci return -EIO; 3888c2ecf20Sopenharmony_ci } else if (rfclen > clc_len + 512) { 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * Some servers (Windows XP in particular) send more 3918c2ecf20Sopenharmony_ci * data than the lengths in the SMB packet would 3928c2ecf20Sopenharmony_ci * indicate on certain calls (byte range locks and 3938c2ecf20Sopenharmony_ci * trans2 find first calls in particular). While the 3948c2ecf20Sopenharmony_ci * client can handle such a frame by ignoring the 3958c2ecf20Sopenharmony_ci * trailing data, we choose limit the amount of extra 3968c2ecf20Sopenharmony_ci * data to 512 bytes. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", 3998c2ecf20Sopenharmony_ci rfclen, mid); 4008c2ecf20Sopenharmony_ci return -EIO; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cibool 4078c2ecf20Sopenharmony_ciis_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct smb_hdr *buf = (struct smb_hdr *)buffer; 4108c2ecf20Sopenharmony_ci struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; 4118c2ecf20Sopenharmony_ci struct list_head *tmp, *tmp1, *tmp2; 4128c2ecf20Sopenharmony_ci struct cifs_ses *ses; 4138c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 4148c2ecf20Sopenharmony_ci struct cifsInodeInfo *pCifsInode; 4158c2ecf20Sopenharmony_ci struct cifsFileInfo *netfile; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Checking for oplock break or dnotify response\n"); 4188c2ecf20Sopenharmony_ci if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && 4198c2ecf20Sopenharmony_ci (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { 4208c2ecf20Sopenharmony_ci struct smb_com_transaction_change_notify_rsp *pSMBr = 4218c2ecf20Sopenharmony_ci (struct smb_com_transaction_change_notify_rsp *)buf; 4228c2ecf20Sopenharmony_ci struct file_notify_information *pnotify; 4238c2ecf20Sopenharmony_ci __u32 data_offset = 0; 4248c2ecf20Sopenharmony_ci size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (get_bcc(buf) > sizeof(struct file_notify_information)) { 4278c2ecf20Sopenharmony_ci data_offset = le32_to_cpu(pSMBr->DataOffset); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (data_offset > 4308c2ecf20Sopenharmony_ci len - sizeof(struct file_notify_information)) { 4318c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Invalid data_offset %u\n", 4328c2ecf20Sopenharmony_ci data_offset); 4338c2ecf20Sopenharmony_ci return true; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci pnotify = (struct file_notify_information *) 4368c2ecf20Sopenharmony_ci ((char *)&pSMBr->hdr.Protocol + data_offset); 4378c2ecf20Sopenharmony_ci cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n", 4388c2ecf20Sopenharmony_ci pnotify->FileName, pnotify->Action); 4398c2ecf20Sopenharmony_ci /* cifs_dump_mem("Rcvd notify Data: ",buf, 4408c2ecf20Sopenharmony_ci sizeof(struct smb_hdr)+60); */ 4418c2ecf20Sopenharmony_ci return true; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci if (pSMBr->hdr.Status.CifsError) { 4448c2ecf20Sopenharmony_ci cifs_dbg(FYI, "notify err 0x%x\n", 4458c2ecf20Sopenharmony_ci pSMBr->hdr.Status.CifsError); 4468c2ecf20Sopenharmony_ci return true; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci return false; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) 4518c2ecf20Sopenharmony_ci return false; 4528c2ecf20Sopenharmony_ci if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { 4538c2ecf20Sopenharmony_ci /* no sense logging error on invalid handle on oplock 4548c2ecf20Sopenharmony_ci break - harmless race between close request and oplock 4558c2ecf20Sopenharmony_ci break response is expected from time to time writing out 4568c2ecf20Sopenharmony_ci large dirty files cached on the client */ 4578c2ecf20Sopenharmony_ci if ((NT_STATUS_INVALID_HANDLE) == 4588c2ecf20Sopenharmony_ci le32_to_cpu(pSMB->hdr.Status.CifsError)) { 4598c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Invalid handle on oplock break\n"); 4608c2ecf20Sopenharmony_ci return true; 4618c2ecf20Sopenharmony_ci } else if (ERRbadfid == 4628c2ecf20Sopenharmony_ci le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { 4638c2ecf20Sopenharmony_ci return true; 4648c2ecf20Sopenharmony_ci } else { 4658c2ecf20Sopenharmony_ci return false; /* on valid oplock brk we get "request" */ 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci if (pSMB->hdr.WordCount != 8) 4698c2ecf20Sopenharmony_ci return false; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n", 4728c2ecf20Sopenharmony_ci pSMB->LockType, pSMB->OplockLevel); 4738c2ecf20Sopenharmony_ci if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) 4748c2ecf20Sopenharmony_ci return false; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* look up tcon based on tid & uid */ 4778c2ecf20Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 4788c2ecf20Sopenharmony_ci list_for_each(tmp, &srv->smb_ses_list) { 4798c2ecf20Sopenharmony_ci ses = list_entry(tmp, struct cifs_ses, smb_ses_list); 4808c2ecf20Sopenharmony_ci list_for_each(tmp1, &ses->tcon_list) { 4818c2ecf20Sopenharmony_ci tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); 4828c2ecf20Sopenharmony_ci if (tcon->tid != buf->Tid) 4838c2ecf20Sopenharmony_ci continue; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); 4868c2ecf20Sopenharmony_ci spin_lock(&tcon->open_file_lock); 4878c2ecf20Sopenharmony_ci list_for_each(tmp2, &tcon->openFileList) { 4888c2ecf20Sopenharmony_ci netfile = list_entry(tmp2, struct cifsFileInfo, 4898c2ecf20Sopenharmony_ci tlist); 4908c2ecf20Sopenharmony_ci if (pSMB->Fid != netfile->fid.netfid) 4918c2ecf20Sopenharmony_ci continue; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci cifs_dbg(FYI, "file id match, oplock break\n"); 4948c2ecf20Sopenharmony_ci pCifsInode = CIFS_I(d_inode(netfile->dentry)); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, 4978c2ecf20Sopenharmony_ci &pCifsInode->flags); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci netfile->oplock_epoch = 0; 5008c2ecf20Sopenharmony_ci netfile->oplock_level = pSMB->OplockLevel; 5018c2ecf20Sopenharmony_ci netfile->oplock_break_cancelled = false; 5028c2ecf20Sopenharmony_ci cifs_queue_oplock_break(netfile); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci spin_unlock(&tcon->open_file_lock); 5058c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 5068c2ecf20Sopenharmony_ci return true; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci spin_unlock(&tcon->open_file_lock); 5098c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 5108c2ecf20Sopenharmony_ci cifs_dbg(FYI, "No matching file for oplock break\n"); 5118c2ecf20Sopenharmony_ci return true; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 5158c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); 5168c2ecf20Sopenharmony_ci return true; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_civoid 5208c2ecf20Sopenharmony_cidump_smb(void *buf, int smb_buf_length) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci if (traceSMB == 0) 5238c2ecf20Sopenharmony_ci return; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 8, 2, buf, 5268c2ecf20Sopenharmony_ci smb_buf_length, true); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_civoid 5308c2ecf20Sopenharmony_cicifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { 5338c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = NULL; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (cifs_sb->master_tlink) 5368c2ecf20Sopenharmony_ci tcon = cifs_sb_master_tcon(cifs_sb); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; 5398c2ecf20Sopenharmony_ci cifs_sb->mnt_cifs_serverino_autodisabled = true; 5408c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n", 5418c2ecf20Sopenharmony_ci tcon ? tcon->treeName : "new server"); 5428c2ecf20Sopenharmony_ci cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n"); 5438c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n"); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_civoid cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci oplock &= 0xF; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (oplock == OPLOCK_EXCLUSIVE) { 5538c2ecf20Sopenharmony_ci cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG; 5548c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", 5558c2ecf20Sopenharmony_ci &cinode->vfs_inode); 5568c2ecf20Sopenharmony_ci } else if (oplock == OPLOCK_READ) { 5578c2ecf20Sopenharmony_ci cinode->oplock = CIFS_CACHE_READ_FLG; 5588c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", 5598c2ecf20Sopenharmony_ci &cinode->vfs_inode); 5608c2ecf20Sopenharmony_ci } else 5618c2ecf20Sopenharmony_ci cinode->oplock = 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* 5658c2ecf20Sopenharmony_ci * We wait for oplock breaks to be processed before we attempt to perform 5668c2ecf20Sopenharmony_ci * writes. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ciint cifs_get_writer(struct cifsInodeInfo *cinode) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci int rc; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistart: 5738c2ecf20Sopenharmony_ci rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, 5748c2ecf20Sopenharmony_ci TASK_KILLABLE); 5758c2ecf20Sopenharmony_ci if (rc) 5768c2ecf20Sopenharmony_ci return rc; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci spin_lock(&cinode->writers_lock); 5798c2ecf20Sopenharmony_ci if (!cinode->writers) 5808c2ecf20Sopenharmony_ci set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); 5818c2ecf20Sopenharmony_ci cinode->writers++; 5828c2ecf20Sopenharmony_ci /* Check to see if we have started servicing an oplock break */ 5838c2ecf20Sopenharmony_ci if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { 5848c2ecf20Sopenharmony_ci cinode->writers--; 5858c2ecf20Sopenharmony_ci if (cinode->writers == 0) { 5868c2ecf20Sopenharmony_ci clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); 5878c2ecf20Sopenharmony_ci wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci spin_unlock(&cinode->writers_lock); 5908c2ecf20Sopenharmony_ci goto start; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci spin_unlock(&cinode->writers_lock); 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_civoid cifs_put_writer(struct cifsInodeInfo *cinode) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci spin_lock(&cinode->writers_lock); 5998c2ecf20Sopenharmony_ci cinode->writers--; 6008c2ecf20Sopenharmony_ci if (cinode->writers == 0) { 6018c2ecf20Sopenharmony_ci clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); 6028c2ecf20Sopenharmony_ci wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci spin_unlock(&cinode->writers_lock); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/** 6088c2ecf20Sopenharmony_ci * cifs_queue_oplock_break - queue the oplock break handler for cfile 6098c2ecf20Sopenharmony_ci * 6108c2ecf20Sopenharmony_ci * This function is called from the demultiplex thread when it 6118c2ecf20Sopenharmony_ci * receives an oplock break for @cfile. 6128c2ecf20Sopenharmony_ci * 6138c2ecf20Sopenharmony_ci * Assumes the tcon->open_file_lock is held. 6148c2ecf20Sopenharmony_ci * Assumes cfile->file_info_lock is NOT held. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_civoid cifs_queue_oplock_break(struct cifsFileInfo *cfile) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci /* 6198c2ecf20Sopenharmony_ci * Bump the handle refcount now while we hold the 6208c2ecf20Sopenharmony_ci * open_file_lock to enforce the validity of it for the oplock 6218c2ecf20Sopenharmony_ci * break handler. The matching put is done at the end of the 6228c2ecf20Sopenharmony_ci * handler. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci cifsFileInfo_get(cfile); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci queue_work(cifsoplockd_wq, &cfile->oplock_break); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_civoid cifs_done_oplock_break(struct cifsInodeInfo *cinode) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); 6328c2ecf20Sopenharmony_ci wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cibool 6368c2ecf20Sopenharmony_cibackup_cred(struct cifs_sb_info *cifs_sb) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { 6398c2ecf20Sopenharmony_ci if (uid_eq(cifs_sb->mnt_backupuid, current_fsuid())) 6408c2ecf20Sopenharmony_ci return true; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { 6438c2ecf20Sopenharmony_ci if (in_group_p(cifs_sb->mnt_backupgid)) 6448c2ecf20Sopenharmony_ci return true; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return false; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_civoid 6518c2ecf20Sopenharmony_cicifs_del_pending_open(struct cifs_pending_open *open) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci spin_lock(&tlink_tcon(open->tlink)->open_file_lock); 6548c2ecf20Sopenharmony_ci list_del(&open->olist); 6558c2ecf20Sopenharmony_ci spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_civoid 6598c2ecf20Sopenharmony_cicifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, 6608c2ecf20Sopenharmony_ci struct cifs_pending_open *open) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); 6638c2ecf20Sopenharmony_ci open->oplock = CIFS_OPLOCK_NO_CHANGE; 6648c2ecf20Sopenharmony_ci open->tlink = tlink; 6658c2ecf20Sopenharmony_ci fid->pending_open = open; 6668c2ecf20Sopenharmony_ci list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_civoid 6708c2ecf20Sopenharmony_cicifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, 6718c2ecf20Sopenharmony_ci struct cifs_pending_open *open) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci spin_lock(&tlink_tcon(tlink)->open_file_lock); 6748c2ecf20Sopenharmony_ci cifs_add_pending_open_locked(fid, tlink, open); 6758c2ecf20Sopenharmony_ci spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/* parses DFS refferal V3 structure 6798c2ecf20Sopenharmony_ci * caller is responsible for freeing target_nodes 6808c2ecf20Sopenharmony_ci * returns: 6818c2ecf20Sopenharmony_ci * - on success - 0 6828c2ecf20Sopenharmony_ci * - on failure - errno 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_ciint 6858c2ecf20Sopenharmony_ciparse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, 6868c2ecf20Sopenharmony_ci unsigned int *num_of_nodes, 6878c2ecf20Sopenharmony_ci struct dfs_info3_param **target_nodes, 6888c2ecf20Sopenharmony_ci const struct nls_table *nls_codepage, int remap, 6898c2ecf20Sopenharmony_ci const char *searchName, bool is_unicode) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci int i, rc = 0; 6928c2ecf20Sopenharmony_ci char *data_end; 6938c2ecf20Sopenharmony_ci struct dfs_referral_level_3 *ref; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (*num_of_nodes < 1) { 6988c2ecf20Sopenharmony_ci cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n", 6998c2ecf20Sopenharmony_ci *num_of_nodes); 7008c2ecf20Sopenharmony_ci rc = -EINVAL; 7018c2ecf20Sopenharmony_ci goto parse_DFS_referrals_exit; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci ref = (struct dfs_referral_level_3 *) &(rsp->referrals); 7058c2ecf20Sopenharmony_ci if (ref->VersionNumber != cpu_to_le16(3)) { 7068c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n", 7078c2ecf20Sopenharmony_ci le16_to_cpu(ref->VersionNumber)); 7088c2ecf20Sopenharmony_ci rc = -EINVAL; 7098c2ecf20Sopenharmony_ci goto parse_DFS_referrals_exit; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* get the upper boundary of the resp buffer */ 7138c2ecf20Sopenharmony_ci data_end = (char *)rsp + rsp_size; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", 7168c2ecf20Sopenharmony_ci *num_of_nodes, le32_to_cpu(rsp->DFSFlags)); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param), 7198c2ecf20Sopenharmony_ci GFP_KERNEL); 7208c2ecf20Sopenharmony_ci if (*target_nodes == NULL) { 7218c2ecf20Sopenharmony_ci rc = -ENOMEM; 7228c2ecf20Sopenharmony_ci goto parse_DFS_referrals_exit; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* collect necessary data from referrals */ 7268c2ecf20Sopenharmony_ci for (i = 0; i < *num_of_nodes; i++) { 7278c2ecf20Sopenharmony_ci char *temp; 7288c2ecf20Sopenharmony_ci int max_len; 7298c2ecf20Sopenharmony_ci struct dfs_info3_param *node = (*target_nodes)+i; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci node->flags = le32_to_cpu(rsp->DFSFlags); 7328c2ecf20Sopenharmony_ci if (is_unicode) { 7338c2ecf20Sopenharmony_ci __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, 7348c2ecf20Sopenharmony_ci GFP_KERNEL); 7358c2ecf20Sopenharmony_ci if (tmp == NULL) { 7368c2ecf20Sopenharmony_ci rc = -ENOMEM; 7378c2ecf20Sopenharmony_ci goto parse_DFS_referrals_exit; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci cifsConvertToUTF16((__le16 *) tmp, searchName, 7408c2ecf20Sopenharmony_ci PATH_MAX, nls_codepage, remap); 7418c2ecf20Sopenharmony_ci node->path_consumed = cifs_utf16_bytes(tmp, 7428c2ecf20Sopenharmony_ci le16_to_cpu(rsp->PathConsumed), 7438c2ecf20Sopenharmony_ci nls_codepage); 7448c2ecf20Sopenharmony_ci kfree(tmp); 7458c2ecf20Sopenharmony_ci } else 7468c2ecf20Sopenharmony_ci node->path_consumed = le16_to_cpu(rsp->PathConsumed); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci node->server_type = le16_to_cpu(ref->ServerType); 7498c2ecf20Sopenharmony_ci node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* copy DfsPath */ 7528c2ecf20Sopenharmony_ci temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); 7538c2ecf20Sopenharmony_ci max_len = data_end - temp; 7548c2ecf20Sopenharmony_ci node->path_name = cifs_strndup_from_utf16(temp, max_len, 7558c2ecf20Sopenharmony_ci is_unicode, nls_codepage); 7568c2ecf20Sopenharmony_ci if (!node->path_name) { 7578c2ecf20Sopenharmony_ci rc = -ENOMEM; 7588c2ecf20Sopenharmony_ci goto parse_DFS_referrals_exit; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* copy link target UNC */ 7628c2ecf20Sopenharmony_ci temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); 7638c2ecf20Sopenharmony_ci max_len = data_end - temp; 7648c2ecf20Sopenharmony_ci node->node_name = cifs_strndup_from_utf16(temp, max_len, 7658c2ecf20Sopenharmony_ci is_unicode, nls_codepage); 7668c2ecf20Sopenharmony_ci if (!node->node_name) { 7678c2ecf20Sopenharmony_ci rc = -ENOMEM; 7688c2ecf20Sopenharmony_ci goto parse_DFS_referrals_exit; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci node->ttl = le32_to_cpu(ref->TimeToLive); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci ref++; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ciparse_DFS_referrals_exit: 7778c2ecf20Sopenharmony_ci if (rc) { 7788c2ecf20Sopenharmony_ci free_dfs_info_array(*target_nodes, *num_of_nodes); 7798c2ecf20Sopenharmony_ci *target_nodes = NULL; 7808c2ecf20Sopenharmony_ci *num_of_nodes = 0; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci return rc; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistruct cifs_aio_ctx * 7868c2ecf20Sopenharmony_cicifs_aio_ctx_alloc(void) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct cifs_aio_ctx *ctx; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* 7918c2ecf20Sopenharmony_ci * Must use kzalloc to initialize ctx->bv to NULL and ctx->direct_io 7928c2ecf20Sopenharmony_ci * to false so that we know when we have to unreference pages within 7938c2ecf20Sopenharmony_ci * cifs_aio_ctx_release() 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL); 7968c2ecf20Sopenharmony_ci if (!ctx) 7978c2ecf20Sopenharmony_ci return NULL; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ctx->list); 8008c2ecf20Sopenharmony_ci mutex_init(&ctx->aio_mutex); 8018c2ecf20Sopenharmony_ci init_completion(&ctx->done); 8028c2ecf20Sopenharmony_ci kref_init(&ctx->refcount); 8038c2ecf20Sopenharmony_ci return ctx; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_civoid 8078c2ecf20Sopenharmony_cicifs_aio_ctx_release(struct kref *refcount) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct cifs_aio_ctx *ctx = container_of(refcount, 8108c2ecf20Sopenharmony_ci struct cifs_aio_ctx, refcount); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci cifsFileInfo_put(ctx->cfile); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* 8158c2ecf20Sopenharmony_ci * ctx->bv is only set if setup_aio_ctx_iter() was call successfuly 8168c2ecf20Sopenharmony_ci * which means that iov_iter_get_pages() was a success and thus that 8178c2ecf20Sopenharmony_ci * we have taken reference on pages. 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_ci if (ctx->bv) { 8208c2ecf20Sopenharmony_ci unsigned i; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci for (i = 0; i < ctx->npages; i++) { 8238c2ecf20Sopenharmony_ci if (ctx->should_dirty) 8248c2ecf20Sopenharmony_ci set_page_dirty(ctx->bv[i].bv_page); 8258c2ecf20Sopenharmony_ci put_page(ctx->bv[i].bv_page); 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci kvfree(ctx->bv); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci kfree(ctx); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024) 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ciint 8368c2ecf20Sopenharmony_cisetup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci ssize_t rc; 8398c2ecf20Sopenharmony_ci unsigned int cur_npages; 8408c2ecf20Sopenharmony_ci unsigned int npages = 0; 8418c2ecf20Sopenharmony_ci unsigned int i; 8428c2ecf20Sopenharmony_ci size_t len; 8438c2ecf20Sopenharmony_ci size_t count = iov_iter_count(iter); 8448c2ecf20Sopenharmony_ci unsigned int saved_len; 8458c2ecf20Sopenharmony_ci size_t start; 8468c2ecf20Sopenharmony_ci unsigned int max_pages = iov_iter_npages(iter, INT_MAX); 8478c2ecf20Sopenharmony_ci struct page **pages = NULL; 8488c2ecf20Sopenharmony_ci struct bio_vec *bv = NULL; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (iov_iter_is_kvec(iter)) { 8518c2ecf20Sopenharmony_ci memcpy(&ctx->iter, iter, sizeof(*iter)); 8528c2ecf20Sopenharmony_ci ctx->len = count; 8538c2ecf20Sopenharmony_ci iov_iter_advance(iter, count); 8548c2ecf20Sopenharmony_ci return 0; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (array_size(max_pages, sizeof(*bv)) <= CIFS_AIO_KMALLOC_LIMIT) 8588c2ecf20Sopenharmony_ci bv = kmalloc_array(max_pages, sizeof(*bv), GFP_KERNEL); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (!bv) { 8618c2ecf20Sopenharmony_ci bv = vmalloc(array_size(max_pages, sizeof(*bv))); 8628c2ecf20Sopenharmony_ci if (!bv) 8638c2ecf20Sopenharmony_ci return -ENOMEM; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (array_size(max_pages, sizeof(*pages)) <= CIFS_AIO_KMALLOC_LIMIT) 8678c2ecf20Sopenharmony_ci pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (!pages) { 8708c2ecf20Sopenharmony_ci pages = vmalloc(array_size(max_pages, sizeof(*pages))); 8718c2ecf20Sopenharmony_ci if (!pages) { 8728c2ecf20Sopenharmony_ci kvfree(bv); 8738c2ecf20Sopenharmony_ci return -ENOMEM; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci saved_len = count; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci while (count && npages < max_pages) { 8808c2ecf20Sopenharmony_ci rc = iov_iter_get_pages(iter, pages, count, max_pages, &start); 8818c2ecf20Sopenharmony_ci if (rc < 0) { 8828c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc); 8838c2ecf20Sopenharmony_ci break; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (rc > count) { 8878c2ecf20Sopenharmony_ci cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc, 8888c2ecf20Sopenharmony_ci count); 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci iov_iter_advance(iter, rc); 8938c2ecf20Sopenharmony_ci count -= rc; 8948c2ecf20Sopenharmony_ci rc += start; 8958c2ecf20Sopenharmony_ci cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (npages + cur_npages > max_pages) { 8988c2ecf20Sopenharmony_ci cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n", 8998c2ecf20Sopenharmony_ci npages + cur_npages, max_pages); 9008c2ecf20Sopenharmony_ci break; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci for (i = 0; i < cur_npages; i++) { 9048c2ecf20Sopenharmony_ci len = rc > PAGE_SIZE ? PAGE_SIZE : rc; 9058c2ecf20Sopenharmony_ci bv[npages + i].bv_page = pages[i]; 9068c2ecf20Sopenharmony_ci bv[npages + i].bv_offset = start; 9078c2ecf20Sopenharmony_ci bv[npages + i].bv_len = len - start; 9088c2ecf20Sopenharmony_ci rc -= len; 9098c2ecf20Sopenharmony_ci start = 0; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci npages += cur_npages; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci kvfree(pages); 9168c2ecf20Sopenharmony_ci ctx->bv = bv; 9178c2ecf20Sopenharmony_ci ctx->len = saved_len - count; 9188c2ecf20Sopenharmony_ci ctx->npages = npages; 9198c2ecf20Sopenharmony_ci iov_iter_bvec(&ctx->iter, rw, ctx->bv, npages, ctx->len); 9208c2ecf20Sopenharmony_ci return 0; 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci/** 9248c2ecf20Sopenharmony_ci * cifs_alloc_hash - allocate hash and hash context together 9258c2ecf20Sopenharmony_ci * 9268c2ecf20Sopenharmony_ci * The caller has to make sure @sdesc is initialized to either NULL or 9278c2ecf20Sopenharmony_ci * a valid context. Both can be freed via cifs_free_hash(). 9288c2ecf20Sopenharmony_ci */ 9298c2ecf20Sopenharmony_ciint 9308c2ecf20Sopenharmony_cicifs_alloc_hash(const char *name, 9318c2ecf20Sopenharmony_ci struct crypto_shash **shash, struct sdesc **sdesc) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci int rc = 0; 9348c2ecf20Sopenharmony_ci size_t size; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (*sdesc != NULL) 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci *shash = crypto_alloc_shash(name, 0, 0); 9408c2ecf20Sopenharmony_ci if (IS_ERR(*shash)) { 9418c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Could not allocate crypto %s\n", name); 9428c2ecf20Sopenharmony_ci rc = PTR_ERR(*shash); 9438c2ecf20Sopenharmony_ci *shash = NULL; 9448c2ecf20Sopenharmony_ci *sdesc = NULL; 9458c2ecf20Sopenharmony_ci return rc; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci size = sizeof(struct shash_desc) + crypto_shash_descsize(*shash); 9498c2ecf20Sopenharmony_ci *sdesc = kmalloc(size, GFP_KERNEL); 9508c2ecf20Sopenharmony_ci if (*sdesc == NULL) { 9518c2ecf20Sopenharmony_ci cifs_dbg(VFS, "no memory left to allocate crypto %s\n", name); 9528c2ecf20Sopenharmony_ci crypto_free_shash(*shash); 9538c2ecf20Sopenharmony_ci *shash = NULL; 9548c2ecf20Sopenharmony_ci return -ENOMEM; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci (*sdesc)->shash.tfm = *shash; 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci/** 9628c2ecf20Sopenharmony_ci * cifs_free_hash - free hash and hash context together 9638c2ecf20Sopenharmony_ci * 9648c2ecf20Sopenharmony_ci * Freeing a NULL hash or context is safe. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_civoid 9678c2ecf20Sopenharmony_cicifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci kfree(*sdesc); 9708c2ecf20Sopenharmony_ci *sdesc = NULL; 9718c2ecf20Sopenharmony_ci if (*shash) 9728c2ecf20Sopenharmony_ci crypto_free_shash(*shash); 9738c2ecf20Sopenharmony_ci *shash = NULL; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci/** 9778c2ecf20Sopenharmony_ci * rqst_page_get_length - obtain the length and offset for a page in smb_rqst 9788c2ecf20Sopenharmony_ci * Input: rqst - a smb_rqst, page - a page index for rqst 9798c2ecf20Sopenharmony_ci * Output: *len - the length for this page, *offset - the offset for this page 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_civoid rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, 9828c2ecf20Sopenharmony_ci unsigned int *len, unsigned int *offset) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci *len = rqst->rq_pagesz; 9858c2ecf20Sopenharmony_ci *offset = (page == 0) ? rqst->rq_offset : 0; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (rqst->rq_npages == 1 || page == rqst->rq_npages-1) 9888c2ecf20Sopenharmony_ci *len = rqst->rq_tailsz; 9898c2ecf20Sopenharmony_ci else if (page == 0) 9908c2ecf20Sopenharmony_ci *len = rqst->rq_pagesz - rqst->rq_offset; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_civoid extract_unc_hostname(const char *unc, const char **h, size_t *len) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci const char *end; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* skip initial slashes */ 9988c2ecf20Sopenharmony_ci while (*unc && (*unc == '\\' || *unc == '/')) 9998c2ecf20Sopenharmony_ci unc++; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci end = unc; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci while (*end && !(*end == '\\' || *end == '/')) 10048c2ecf20Sopenharmony_ci end++; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci *h = unc; 10078c2ecf20Sopenharmony_ci *len = end - unc; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci/** 10118c2ecf20Sopenharmony_ci * copy_path_name - copy src path to dst, possibly truncating 10128c2ecf20Sopenharmony_ci * 10138c2ecf20Sopenharmony_ci * returns number of bytes written (including trailing nul) 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_ciint copy_path_name(char *dst, const char *src) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci int name_len; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* 10208c2ecf20Sopenharmony_ci * PATH_MAX includes nul, so if strlen(src) >= PATH_MAX it 10218c2ecf20Sopenharmony_ci * will truncate and strlen(dst) will be PATH_MAX-1 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci name_len = strscpy(dst, src, PATH_MAX); 10248c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(name_len < 0)) 10258c2ecf20Sopenharmony_ci name_len = PATH_MAX-1; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* we count the trailing nul */ 10288c2ecf20Sopenharmony_ci name_len++; 10298c2ecf20Sopenharmony_ci return name_len; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistruct super_cb_data { 10338c2ecf20Sopenharmony_ci void *data; 10348c2ecf20Sopenharmony_ci struct super_block *sb; 10358c2ecf20Sopenharmony_ci}; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_cistatic void tcp_super_cb(struct super_block *sb, void *arg) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci struct super_cb_data *sd = arg; 10408c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = sd->data; 10418c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb; 10428c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (sd->sb) 10458c2ecf20Sopenharmony_ci return; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci cifs_sb = CIFS_SB(sb); 10488c2ecf20Sopenharmony_ci tcon = cifs_sb_master_tcon(cifs_sb); 10498c2ecf20Sopenharmony_ci if (tcon->ses->server == server) 10508c2ecf20Sopenharmony_ci sd->sb = sb; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *), 10548c2ecf20Sopenharmony_ci void *data) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci struct super_cb_data sd = { 10578c2ecf20Sopenharmony_ci .data = data, 10588c2ecf20Sopenharmony_ci .sb = NULL, 10598c2ecf20Sopenharmony_ci }; 10608c2ecf20Sopenharmony_ci struct file_system_type **fs_type = (struct file_system_type *[]) { 10618c2ecf20Sopenharmony_ci &cifs_fs_type, &smb3_fs_type, NULL, 10628c2ecf20Sopenharmony_ci }; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci for (; *fs_type; fs_type++) { 10658c2ecf20Sopenharmony_ci iterate_supers_type(*fs_type, f, &sd); 10668c2ecf20Sopenharmony_ci if (sd.sb) { 10678c2ecf20Sopenharmony_ci /* 10688c2ecf20Sopenharmony_ci * Grab an active reference in order to prevent automounts (DFS links) 10698c2ecf20Sopenharmony_ci * of expiring and then freeing up our cifs superblock pointer while 10708c2ecf20Sopenharmony_ci * we're doing failover. 10718c2ecf20Sopenharmony_ci */ 10728c2ecf20Sopenharmony_ci cifs_sb_active(sd.sb); 10738c2ecf20Sopenharmony_ci return sd.sb; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistatic void __cifs_put_super(struct super_block *sb) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(sb)) 10828c2ecf20Sopenharmony_ci cifs_sb_deactive(sb); 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistruct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci return __cifs_get_super(tcp_super_cb, server); 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_civoid cifs_put_tcp_super(struct super_block *sb) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci __cifs_put_super(sb); 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL 10968c2ecf20Sopenharmony_ciint match_target_ip(struct TCP_Server_Info *server, 10978c2ecf20Sopenharmony_ci const char *share, size_t share_len, 10988c2ecf20Sopenharmony_ci bool *result) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci int rc; 11018c2ecf20Sopenharmony_ci char *target, *tip = NULL; 11028c2ecf20Sopenharmony_ci struct sockaddr tipaddr; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci *result = false; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci target = kzalloc(share_len + 3, GFP_KERNEL); 11078c2ecf20Sopenharmony_ci if (!target) { 11088c2ecf20Sopenharmony_ci rc = -ENOMEM; 11098c2ecf20Sopenharmony_ci goto out; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci rc = dns_resolve_server_name_to_ip(target, &tip); 11178c2ecf20Sopenharmony_ci if (rc < 0) 11188c2ecf20Sopenharmony_ci goto out; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: target ip: %s\n", __func__, tip); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (!cifs_convert_address(&tipaddr, tip, strlen(tip))) { 11238c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%s: failed to convert target ip address\n", 11248c2ecf20Sopenharmony_ci __func__); 11258c2ecf20Sopenharmony_ci rc = -EINVAL; 11268c2ecf20Sopenharmony_ci goto out; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci *result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, 11308c2ecf20Sopenharmony_ci &tipaddr); 11318c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result); 11328c2ecf20Sopenharmony_ci rc = 0; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ciout: 11358c2ecf20Sopenharmony_ci kfree(target); 11368c2ecf20Sopenharmony_ci kfree(tip); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci return rc; 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic void tcon_super_cb(struct super_block *sb, void *arg) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct super_cb_data *sd = arg; 11448c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = sd->data; 11458c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (sd->sb) 11488c2ecf20Sopenharmony_ci return; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci cifs_sb = CIFS_SB(sb); 11518c2ecf20Sopenharmony_ci if (tcon->dfs_path && cifs_sb->origin_fullpath && 11528c2ecf20Sopenharmony_ci !strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath)) 11538c2ecf20Sopenharmony_ci sd->sb = sb; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci return __cifs_get_super(tcon_super_cb, tcon); 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cistatic inline void cifs_put_tcon_super(struct super_block *sb) 11628c2ecf20Sopenharmony_ci{ 11638c2ecf20Sopenharmony_ci __cifs_put_super(sb); 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci#else 11668c2ecf20Sopenharmony_cistatic inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic inline void cifs_put_tcon_super(struct super_block *sb) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci#endif 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ciint update_super_prepath(struct cifs_tcon *tcon, char *prefix) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct super_block *sb; 11798c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb; 11808c2ecf20Sopenharmony_ci int rc = 0; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci sb = cifs_get_tcon_super(tcon); 11838c2ecf20Sopenharmony_ci if (IS_ERR(sb)) 11848c2ecf20Sopenharmony_ci return PTR_ERR(sb); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci cifs_sb = CIFS_SB(sb); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci kfree(cifs_sb->prepath); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (prefix && *prefix) { 11918c2ecf20Sopenharmony_ci cifs_sb->prepath = kstrndup(prefix, strlen(prefix), GFP_ATOMIC); 11928c2ecf20Sopenharmony_ci if (!cifs_sb->prepath) { 11938c2ecf20Sopenharmony_ci rc = -ENOMEM; 11948c2ecf20Sopenharmony_ci goto out; 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); 11988c2ecf20Sopenharmony_ci } else 11998c2ecf20Sopenharmony_ci cifs_sb->prepath = NULL; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ciout: 12048c2ecf20Sopenharmony_ci cifs_put_tcon_super(sb); 12058c2ecf20Sopenharmony_ci return rc; 12068c2ecf20Sopenharmony_ci} 1207