summaryrefslogtreecommitdiff
path: root/fs/cifs/smb2file.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-01 15:27:35 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-01 15:27:35 -0700
commit797b9e5ae93270ec27a1f1ed48cd697d01b2269f (patch)
tree4e44ba1535a243e834bc7268486efcf83ba4e6cd /fs/cifs/smb2file.c
parent9c0ece069b32e8e122aea71aa47181c10eb85ba7 (diff)
parent1d4ab9077681b7cce60ff46e3a42fe2dafa0b83d (diff)
Merge branch 'for-linus' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS updates from Steve French: "This patchset is the final section of the SMB2.1 support merge for cifs.ko. It also includes improvements to the cifs socket handling from Jeff, and also fixes a few cifs bug fixes. It adds SMB2 support for file and inode operations as well as moves some existing cifs code to use ops server struct of protocol specific callbacks. Most of this code is SMB2 specific. When enabled SMB2.1 does pass various functional tests including most of the connectathon test suite, For SMB2.1, Connectathon test 4 and some related tests fail due to not updating mode bits remotely (cifsacl support where mode bits are approximated with the cifs acl is not enable for smb2), and test8 (symlink) support is not completed for SMB2 yet (note that we will likely have a "Unix Extensions" eventually, at least for Samba, so in the long run posix locks won't have to be emulated when mounting Linux to Linux, but for most NAS and for Windows mounts posix lock emulation will still used for SMB2 in a similar fashion as we do for cifs). SMB2.1 dialect is supported. Although additional fixes to enable smb2 (the original smb2.02) dialect and to add various optional features of the smb3 dialect are expected to be added in the future as testing progresses, currently mounting with the "vers=2.1" is supported (in order to mount using SMB2.1 to servers like Samba 4, and Windows 7, Windows 2008R2)." * 'for-linus' of git://git.samba.org/sfrench/cifs-2.6: (82 commits) [CIFS] Fix indentation of fs/cifs/Kconfig entries [CIFS] Fix SMB2 negotiation support to select only one dialect (based on vers=) cifs: obtain file access during backup intent lookup (resend) CIFS: Fix possible freed pointer dereference in CIFS_SessSetup CIFS: Fix possible freed pointer dereference in SMB2_sess_setup CIFS: Make ops->close return void cifs: change DOS/NT/POSIX mapping of ERRnoresource cifs: remove support for deprecated "forcedirectio" and "strictcache" mount options cifs: remove support for CIFS_IOC_CHECKUMOUNT ioctl CIFS: Fix possible memory leaks in SMB2 code CIFS: Fix endian conversion of IndexNumber Trivial endian fixes MARK SMB2 support EXPERIMENTAL Update cifs version number cifs: add FL_CLOSE to fl_flags mask in cifs_read_flock cifs: Mangle string used for unc in /proc/mounts cifs: cleanups for cifs_mkdir_qinfo CIFS: Fix fast lease break after open problem CIFS: Add SMB2.1 lease break support CIFS: Fix cache coherency for read oplock case ...
Diffstat (limited to 'fs/cifs/smb2file.c')
-rw-r--r--fs/cifs/smb2file.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
new file mode 100644
index 000000000000..a93eec30a50d
--- /dev/null
+++ b/fs/cifs/smb2file.c
@@ -0,0 +1,302 @@
+/*
+ * fs/cifs/smb2file.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002, 2011
+ * Author(s): Steve French (sfrench@us.ibm.com),
+ * Pavel Shilovsky ((pshilovsky@samba.org) 2012
+ *
+ * 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/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "fscache.h"
+#include "smb2proto.h"
+
+void
+smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
+{
+ oplock &= 0xFF;
+ if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
+ return;
+ if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
+ cinode->clientCanCacheAll = true;
+ cinode->clientCanCacheRead = true;
+ cFYI(1, "Exclusive Oplock granted on inode %p",
+ &cinode->vfs_inode);
+ } else if (oplock == SMB2_OPLOCK_LEVEL_II) {
+ cinode->clientCanCacheAll = false;
+ cinode->clientCanCacheRead = true;
+ cFYI(1, "Level II Oplock granted on inode %p",
+ &cinode->vfs_inode);
+ } else {
+ cinode->clientCanCacheAll = false;
+ cinode->clientCanCacheRead = false;
+ }
+}
+
+int
+smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
+ int disposition, int desired_access, int create_options,
+ struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc;
+ __le16 *smb2_path;
+ struct smb2_file_all_info *smb2_data = NULL;
+ __u8 smb2_oplock[17];
+
+ smb2_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (smb2_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
+ GFP_KERNEL);
+ if (smb2_data == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ desired_access |= FILE_READ_ATTRIBUTES;
+ *smb2_oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+
+ if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
+ memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
+
+ rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid,
+ &fid->volatile_fid, desired_access, disposition,
+ 0, 0, smb2_oplock, smb2_data);
+ if (rc)
+ goto out;
+
+ if (buf) {
+ /* open response does not have IndexNumber field - get it */
+ rc = SMB2_get_srv_num(xid, tcon, fid->persistent_fid,
+ fid->volatile_fid,
+ &smb2_data->IndexNumber);
+ if (rc) {
+ /* let get_inode_info disable server inode numbers */
+ smb2_data->IndexNumber = 0;
+ rc = 0;
+ }
+ move_smb2_info_to_cifs(buf, smb2_data);
+ }
+
+ *oplock = *smb2_oplock;
+out:
+ kfree(smb2_data);
+ kfree(smb2_path);
+ return rc;
+}
+
+int
+smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
+ const unsigned int xid)
+{
+ int rc = 0, stored_rc;
+ unsigned int max_num, num = 0, max_buf;
+ struct smb2_lock_element *buf, *cur;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ struct cifsLockInfo *li, *tmp;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct list_head tmp_llist;
+
+ INIT_LIST_HEAD(&tmp_llist);
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it for zero before using.
+ */
+ max_buf = tcon->ses->server->maxBuf;
+ if (!max_buf)
+ return -EINVAL;
+
+ max_num = max_buf / sizeof(struct smb2_lock_element);
+ buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ cur = buf;
+
+ down_write(&cinode->lock_sem);
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
+ if (flock->fl_start > li->offset ||
+ (flock->fl_start + length) <
+ (li->offset + li->length))
+ continue;
+ if (current->tgid != li->pid)
+ continue;
+ if (cinode->can_cache_brlcks) {
+ /*
+ * We can cache brlock requests - simply remove a lock
+ * from the file's list.
+ */
+ list_del(&li->llist);
+ cifs_del_lock_waiters(li);
+ kfree(li);
+ continue;
+ }
+ cur->Length = cpu_to_le64(li->length);
+ cur->Offset = cpu_to_le64(li->offset);
+ cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK);
+ /*
+ * We need to save a lock here to let us add it again to the
+ * file's list if the unlock range request fails on the server.
+ */
+ list_move(&li->llist, &tmp_llist);
+ if (++num == max_num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc) {
+ /*
+ * We failed on the unlock range request - add
+ * all locks from the tmp list to the head of
+ * the file's list.
+ */
+ cifs_move_llist(&tmp_llist,
+ &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ /*
+ * The unlock range request succeed - free the
+ * tmp list.
+ */
+ cifs_free_llist(&tmp_llist);
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+ if (num) {
+ stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, current->tgid,
+ num, buf);
+ if (stored_rc) {
+ cifs_move_llist(&tmp_llist, &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ cifs_free_llist(&tmp_llist);
+ }
+ up_write(&cinode->lock_sem);
+
+ kfree(buf);
+ return rc;
+}
+
+static int
+smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid,
+ struct smb2_lock_element *buf, unsigned int max_num)
+{
+ int rc = 0, stored_rc;
+ struct cifsFileInfo *cfile = fdlocks->cfile;
+ struct cifsLockInfo *li;
+ unsigned int num = 0;
+ struct smb2_lock_element *cur = buf;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+
+ list_for_each_entry(li, &fdlocks->locks, llist) {
+ cur->Length = cpu_to_le64(li->length);
+ cur->Offset = cpu_to_le64(li->offset);
+ cur->Flags = cpu_to_le32(li->type |
+ SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+ if (++num == max_num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+ if (num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+
+ return rc;
+}
+
+int
+smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
+{
+ int rc = 0, stored_rc;
+ unsigned int xid;
+ unsigned int max_num, max_buf;
+ struct smb2_lock_element *buf;
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ struct cifs_fid_locks *fdlocks;
+
+ xid = get_xid();
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
+ if (!cinode->can_cache_brlcks) {
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return rc;
+ }
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it for zero before using.
+ */
+ max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
+ if (!max_buf) {
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return -EINVAL;
+ }
+
+ max_num = max_buf / sizeof(struct smb2_lock_element);
+ buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
+ if (!buf) {
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return -ENOMEM;
+ }
+
+ list_for_each_entry(fdlocks, &cinode->llist, llist) {
+ stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+
+ cinode->can_cache_brlcks = false;
+ kfree(buf);
+
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return rc;
+}