18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * fs/cifs/smb2file.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2002, 2011 58c2ecf20Sopenharmony_ci * Author(s): Steve French (sfrench@us.ibm.com), 68c2ecf20Sopenharmony_ci * Pavel Shilovsky ((pshilovsky@samba.org) 2012 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 98c2ecf20Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as published 108c2ecf20Sopenharmony_ci * by the Free Software Foundation; either version 2.1 of the License, or 118c2ecf20Sopenharmony_ci * (at your option) any later version. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This library is distributed in the hope that it will be useful, 148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 168c2ecf20Sopenharmony_ci * the GNU Lesser General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public License 198c2ecf20Sopenharmony_ci * along with this library; if not, write to the Free Software 208c2ecf20Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#include <linux/fs.h> 238c2ecf20Sopenharmony_ci#include <linux/stat.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 268c2ecf20Sopenharmony_ci#include <asm/div64.h> 278c2ecf20Sopenharmony_ci#include "cifsfs.h" 288c2ecf20Sopenharmony_ci#include "cifspdu.h" 298c2ecf20Sopenharmony_ci#include "cifsglob.h" 308c2ecf20Sopenharmony_ci#include "cifsproto.h" 318c2ecf20Sopenharmony_ci#include "cifs_debug.h" 328c2ecf20Sopenharmony_ci#include "cifs_fs_sb.h" 338c2ecf20Sopenharmony_ci#include "cifs_unicode.h" 348c2ecf20Sopenharmony_ci#include "fscache.h" 358c2ecf20Sopenharmony_ci#include "smb2proto.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciint 388c2ecf20Sopenharmony_cismb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, 398c2ecf20Sopenharmony_ci __u32 *oplock, FILE_ALL_INFO *buf) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int rc; 428c2ecf20Sopenharmony_ci __le16 *smb2_path; 438c2ecf20Sopenharmony_ci struct smb2_file_all_info *smb2_data = NULL; 448c2ecf20Sopenharmony_ci __u8 smb2_oplock; 458c2ecf20Sopenharmony_ci struct cifs_fid *fid = oparms->fid; 468c2ecf20Sopenharmony_ci struct network_resiliency_req nr_ioctl_req; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); 498c2ecf20Sopenharmony_ci if (smb2_path == NULL) { 508c2ecf20Sopenharmony_ci rc = -ENOMEM; 518c2ecf20Sopenharmony_ci goto out; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, 558c2ecf20Sopenharmony_ci GFP_KERNEL); 568c2ecf20Sopenharmony_ci if (smb2_data == NULL) { 578c2ecf20Sopenharmony_ci rc = -ENOMEM; 588c2ecf20Sopenharmony_ci goto out; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci oparms->desired_access |= FILE_READ_ATTRIBUTES; 628c2ecf20Sopenharmony_ci smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, 658c2ecf20Sopenharmony_ci NULL, NULL); 668c2ecf20Sopenharmony_ci if (rc) 678c2ecf20Sopenharmony_ci goto out; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (oparms->tcon->use_resilient) { 718c2ecf20Sopenharmony_ci /* default timeout is 0, servers pick default (120 seconds) */ 728c2ecf20Sopenharmony_ci nr_ioctl_req.Timeout = 738c2ecf20Sopenharmony_ci cpu_to_le32(oparms->tcon->handle_timeout); 748c2ecf20Sopenharmony_ci nr_ioctl_req.Reserved = 0; 758c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, 768c2ecf20Sopenharmony_ci fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, 778c2ecf20Sopenharmony_ci (char *)&nr_ioctl_req, sizeof(nr_ioctl_req), 788c2ecf20Sopenharmony_ci CIFSMaxBufSize, NULL, NULL /* no return info */); 798c2ecf20Sopenharmony_ci if (rc == -EOPNOTSUPP) { 808c2ecf20Sopenharmony_ci cifs_dbg(VFS, 818c2ecf20Sopenharmony_ci "resiliency not supported by server, disabling\n"); 828c2ecf20Sopenharmony_ci oparms->tcon->use_resilient = false; 838c2ecf20Sopenharmony_ci } else if (rc) 848c2ecf20Sopenharmony_ci cifs_dbg(FYI, "error %d setting resiliency\n", rc); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci rc = 0; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (buf) { 908c2ecf20Sopenharmony_ci /* if open response does not have IndexNumber field - get it */ 918c2ecf20Sopenharmony_ci if (smb2_data->IndexNumber == 0) { 928c2ecf20Sopenharmony_ci rc = SMB2_get_srv_num(xid, oparms->tcon, 938c2ecf20Sopenharmony_ci fid->persistent_fid, 948c2ecf20Sopenharmony_ci fid->volatile_fid, 958c2ecf20Sopenharmony_ci &smb2_data->IndexNumber); 968c2ecf20Sopenharmony_ci if (rc) { 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * let get_inode_info disable server inode 998c2ecf20Sopenharmony_ci * numbers 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci smb2_data->IndexNumber = 0; 1028c2ecf20Sopenharmony_ci rc = 0; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci move_smb2_info_to_cifs(buf, smb2_data); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci *oplock = smb2_oplock; 1098c2ecf20Sopenharmony_ciout: 1108c2ecf20Sopenharmony_ci kfree(smb2_data); 1118c2ecf20Sopenharmony_ci kfree(smb2_path); 1128c2ecf20Sopenharmony_ci return rc; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciint 1168c2ecf20Sopenharmony_cismb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, 1178c2ecf20Sopenharmony_ci const unsigned int xid) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int rc = 0, stored_rc; 1208c2ecf20Sopenharmony_ci unsigned int max_num, num = 0, max_buf; 1218c2ecf20Sopenharmony_ci struct smb2_lock_element *buf, *cur; 1228c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); 1238c2ecf20Sopenharmony_ci struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); 1248c2ecf20Sopenharmony_ci struct cifsLockInfo *li, *tmp; 1258c2ecf20Sopenharmony_ci __u64 length = 1 + flock->fl_end - flock->fl_start; 1268c2ecf20Sopenharmony_ci struct list_head tmp_llist; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tmp_llist); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * Accessing maxBuf is racy with cifs_reconnect - need to store value 1328c2ecf20Sopenharmony_ci * and check it before using. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci max_buf = tcon->ses->server->maxBuf; 1358c2ecf20Sopenharmony_ci if (max_buf < sizeof(struct smb2_lock_element)) 1368c2ecf20Sopenharmony_ci return -EINVAL; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); 1398c2ecf20Sopenharmony_ci max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); 1408c2ecf20Sopenharmony_ci max_num = max_buf / sizeof(struct smb2_lock_element); 1418c2ecf20Sopenharmony_ci buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); 1428c2ecf20Sopenharmony_ci if (!buf) 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci cur = buf; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci cifs_down_write(&cinode->lock_sem); 1488c2ecf20Sopenharmony_ci list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { 1498c2ecf20Sopenharmony_ci if (flock->fl_start > li->offset || 1508c2ecf20Sopenharmony_ci (flock->fl_start + length) < 1518c2ecf20Sopenharmony_ci (li->offset + li->length)) 1528c2ecf20Sopenharmony_ci continue; 1538c2ecf20Sopenharmony_ci if (current->tgid != li->pid) 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * flock and OFD lock are associated with an open 1568c2ecf20Sopenharmony_ci * file description, not the process. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci if (!(flock->fl_flags & (FL_FLOCK | FL_OFDLCK))) 1598c2ecf20Sopenharmony_ci continue; 1608c2ecf20Sopenharmony_ci if (cinode->can_cache_brlcks) { 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * We can cache brlock requests - simply remove a lock 1638c2ecf20Sopenharmony_ci * from the file's list. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci list_del(&li->llist); 1668c2ecf20Sopenharmony_ci cifs_del_lock_waiters(li); 1678c2ecf20Sopenharmony_ci kfree(li); 1688c2ecf20Sopenharmony_ci continue; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci cur->Length = cpu_to_le64(li->length); 1718c2ecf20Sopenharmony_ci cur->Offset = cpu_to_le64(li->offset); 1728c2ecf20Sopenharmony_ci cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * We need to save a lock here to let us add it again to the 1758c2ecf20Sopenharmony_ci * file's list if the unlock range request fails on the server. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci list_move(&li->llist, &tmp_llist); 1788c2ecf20Sopenharmony_ci if (++num == max_num) { 1798c2ecf20Sopenharmony_ci stored_rc = smb2_lockv(xid, tcon, 1808c2ecf20Sopenharmony_ci cfile->fid.persistent_fid, 1818c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 1828c2ecf20Sopenharmony_ci current->tgid, num, buf); 1838c2ecf20Sopenharmony_ci if (stored_rc) { 1848c2ecf20Sopenharmony_ci /* 1858c2ecf20Sopenharmony_ci * We failed on the unlock range request - add 1868c2ecf20Sopenharmony_ci * all locks from the tmp list to the head of 1878c2ecf20Sopenharmony_ci * the file's list. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci cifs_move_llist(&tmp_llist, 1908c2ecf20Sopenharmony_ci &cfile->llist->locks); 1918c2ecf20Sopenharmony_ci rc = stored_rc; 1928c2ecf20Sopenharmony_ci } else 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * The unlock range request succeed - free the 1958c2ecf20Sopenharmony_ci * tmp list. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci cifs_free_llist(&tmp_llist); 1988c2ecf20Sopenharmony_ci cur = buf; 1998c2ecf20Sopenharmony_ci num = 0; 2008c2ecf20Sopenharmony_ci } else 2018c2ecf20Sopenharmony_ci cur++; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci if (num) { 2048c2ecf20Sopenharmony_ci stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, 2058c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, current->tgid, 2068c2ecf20Sopenharmony_ci num, buf); 2078c2ecf20Sopenharmony_ci if (stored_rc) { 2088c2ecf20Sopenharmony_ci cifs_move_llist(&tmp_llist, &cfile->llist->locks); 2098c2ecf20Sopenharmony_ci rc = stored_rc; 2108c2ecf20Sopenharmony_ci } else 2118c2ecf20Sopenharmony_ci cifs_free_llist(&tmp_llist); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci up_write(&cinode->lock_sem); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci kfree(buf); 2168c2ecf20Sopenharmony_ci return rc; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int 2208c2ecf20Sopenharmony_cismb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid, 2218c2ecf20Sopenharmony_ci struct smb2_lock_element *buf, unsigned int max_num) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int rc = 0, stored_rc; 2248c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile = fdlocks->cfile; 2258c2ecf20Sopenharmony_ci struct cifsLockInfo *li; 2268c2ecf20Sopenharmony_ci unsigned int num = 0; 2278c2ecf20Sopenharmony_ci struct smb2_lock_element *cur = buf; 2288c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci list_for_each_entry(li, &fdlocks->locks, llist) { 2318c2ecf20Sopenharmony_ci cur->Length = cpu_to_le64(li->length); 2328c2ecf20Sopenharmony_ci cur->Offset = cpu_to_le64(li->offset); 2338c2ecf20Sopenharmony_ci cur->Flags = cpu_to_le32(li->type | 2348c2ecf20Sopenharmony_ci SMB2_LOCKFLAG_FAIL_IMMEDIATELY); 2358c2ecf20Sopenharmony_ci if (++num == max_num) { 2368c2ecf20Sopenharmony_ci stored_rc = smb2_lockv(xid, tcon, 2378c2ecf20Sopenharmony_ci cfile->fid.persistent_fid, 2388c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 2398c2ecf20Sopenharmony_ci current->tgid, num, buf); 2408c2ecf20Sopenharmony_ci if (stored_rc) 2418c2ecf20Sopenharmony_ci rc = stored_rc; 2428c2ecf20Sopenharmony_ci cur = buf; 2438c2ecf20Sopenharmony_ci num = 0; 2448c2ecf20Sopenharmony_ci } else 2458c2ecf20Sopenharmony_ci cur++; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci if (num) { 2488c2ecf20Sopenharmony_ci stored_rc = smb2_lockv(xid, tcon, 2498c2ecf20Sopenharmony_ci cfile->fid.persistent_fid, 2508c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 2518c2ecf20Sopenharmony_ci current->tgid, num, buf); 2528c2ecf20Sopenharmony_ci if (stored_rc) 2538c2ecf20Sopenharmony_ci rc = stored_rc; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return rc; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ciint 2608c2ecf20Sopenharmony_cismb2_push_mandatory_locks(struct cifsFileInfo *cfile) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int rc = 0, stored_rc; 2638c2ecf20Sopenharmony_ci unsigned int xid; 2648c2ecf20Sopenharmony_ci unsigned int max_num, max_buf; 2658c2ecf20Sopenharmony_ci struct smb2_lock_element *buf; 2668c2ecf20Sopenharmony_ci struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); 2678c2ecf20Sopenharmony_ci struct cifs_fid_locks *fdlocks; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci xid = get_xid(); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * Accessing maxBuf is racy with cifs_reconnect - need to store value 2738c2ecf20Sopenharmony_ci * and check it for zero before using. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; 2768c2ecf20Sopenharmony_ci if (max_buf < sizeof(struct smb2_lock_element)) { 2778c2ecf20Sopenharmony_ci free_xid(xid); 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); 2828c2ecf20Sopenharmony_ci max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); 2838c2ecf20Sopenharmony_ci max_num = max_buf / sizeof(struct smb2_lock_element); 2848c2ecf20Sopenharmony_ci buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); 2858c2ecf20Sopenharmony_ci if (!buf) { 2868c2ecf20Sopenharmony_ci free_xid(xid); 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci list_for_each_entry(fdlocks, &cinode->llist, llist) { 2918c2ecf20Sopenharmony_ci stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num); 2928c2ecf20Sopenharmony_ci if (stored_rc) 2938c2ecf20Sopenharmony_ci rc = stored_rc; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci kfree(buf); 2978c2ecf20Sopenharmony_ci free_xid(xid); 2988c2ecf20Sopenharmony_ci return rc; 2998c2ecf20Sopenharmony_ci} 300