summaryrefslogtreecommitdiff
path: root/fs/cifs/file.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-12 17:24:13 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-12 17:24:13 (GMT)
commit6facac1ab68fbf9cbad31a9d521f3a0d6aa9470e (patch)
tree1b00b4d9aab6c5567036157044da98857a84237a /fs/cifs/file.c
parent3f1c64f410e4394ecefadd7a597a7c20368a65fc (diff)
parentc299dd0e2d3dd61d0048a9d9b021aa01f023ed0c (diff)
downloadlinux-6facac1ab68fbf9cbad31a9d521f3a0d6aa9470e.tar.xz
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS fixes from Steve French: "This includes a set of misc. cifs fixes (most importantly some byte range lock related write fixes from Pavel, and some ACL and idmap related fixes from Jeff) but also includes the SMB2.02 dialect enablement, and a key fix for SMB3 mounts. Default authentication upgraded to ntlmv2 for cifs (it was already ntlmv2 for smb2)" * 'for-next' of git://git.samba.org/sfrench/cifs-2.6: (43 commits) CIFS: Fix write after setting a read lock for read oplock files cifs: parse the device name into UNC and prepath cifs: fix up handling of prefixpath= option cifs: clean up handling of unc= option cifs: fix SID binary to string conversion fix "disabling echoes and oplocks" on SMB2 mounts Do not send SMB2 signatures for SMB3 frames cifs: deal with id_to_sid embedded sid reply corner case cifs: fix hardcoded default security descriptor length cifs: extra sanity checking for cifs.idmap keys cifs: avoid extra allocation for small cifs.idmap keys cifs: simplify id_to_sid and sid_to_id mapping code CIFS: Fix possible data coherency problem after oplock break to None CIFS: Do not permit write to a range mandatory locked with a read lock cifs: rename cifs_readdir_lookup to cifs_prime_dcache and make it void return cifs: Add CONFIG_CIFS_DEBUG and rename use of CIFS_DEBUG cifs: Make CIFS_DEBUG possible to undefine SMB3 mounts fail with access denied to some servers cifs: Remove unused cEVENT macro cifs: always zero out smb_vol before parsing options ...
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r--fs/cifs/file.c200
1 files changed, 125 insertions, 75 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 70b6f4c..0a6677b 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -505,16 +505,36 @@ out:
return rc;
}
+static int cifs_push_posix_locks(struct cifsFileInfo *cfile);
+
/*
* Try to reacquire byte range locks that were released when session
- * to server was lost
+ * to server was lost.
*/
-static int cifs_relock_file(struct cifsFileInfo *cifsFile)
+static int
+cifs_relock_file(struct cifsFileInfo *cfile)
{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
- /* BB list all locks open on this file and relock */
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
+ if (cinode->can_cache_brlcks) {
+ /* can cache locks - no need to push them */
+ up_write(&cinode->lock_sem);
+ return rc;
+ }
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ rc = cifs_push_posix_locks(cfile);
+ else
+ rc = tcon->ses->server->ops->push_mand_locks(cfile);
+
+ up_write(&cinode->lock_sem);
return rc;
}
@@ -739,10 +759,15 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
}
}
+#define CIFS_LOCK_OP 0
+#define CIFS_READ_OP 1
+#define CIFS_WRITE_OP 2
+
+/* @rw_check : 0 - no op, 1 - read, 2 - write */
static bool
cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
__u64 length, __u8 type, struct cifsFileInfo *cfile,
- struct cifsLockInfo **conf_lock, bool rw_check)
+ struct cifsLockInfo **conf_lock, int rw_check)
{
struct cifsLockInfo *li;
struct cifsFileInfo *cur_cfile = fdlocks->cfile;
@@ -752,9 +777,13 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
if (offset + length <= li->offset ||
offset >= li->offset + li->length)
continue;
- if (rw_check && server->ops->compare_fids(cfile, cur_cfile) &&
- current->tgid == li->pid)
- continue;
+ if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid &&
+ server->ops->compare_fids(cfile, cur_cfile)) {
+ /* shared lock prevents write op through the same fid */
+ if (!(li->type & server->vals->shared_lock_type) ||
+ rw_check != CIFS_WRITE_OP)
+ continue;
+ }
if ((type & server->vals->shared_lock_type) &&
((server->ops->compare_fids(cfile, cur_cfile) &&
current->tgid == li->pid) || type == li->type))
@@ -769,7 +798,7 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
bool
cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
__u8 type, struct cifsLockInfo **conf_lock,
- bool rw_check)
+ int rw_check)
{
bool rc = false;
struct cifs_fid_locks *cur;
@@ -805,7 +834,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
down_read(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, offset, length, type,
- &conf_lock, false);
+ &conf_lock, CIFS_LOCK_OP);
if (exist) {
flock->fl_start = conf_lock->offset;
flock->fl_end = conf_lock->offset + conf_lock->length - 1;
@@ -852,7 +881,7 @@ try_again:
down_write(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
- lock->type, &conf_lock, false);
+ lock->type, &conf_lock, CIFS_LOCK_OP);
if (!exist && cinode->can_cache_brlcks) {
list_add_tail(&lock->llist, &cfile->llist->locks);
up_write(&cinode->lock_sem);
@@ -948,7 +977,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
int rc = 0, stored_rc;
struct cifsLockInfo *li, *tmp;
struct cifs_tcon *tcon;
- struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
unsigned int num, max_num, max_buf;
LOCKING_ANDX_RANGE *buf, *cur;
int types[] = {LOCKING_ANDX_LARGE_FILES,
@@ -958,21 +986,12 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
xid = get_xid();
tcon = tlink_tcon(cfile->tlink);
- /* 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 = tcon->ses->server->maxBuf;
if (!max_buf) {
- up_write(&cinode->lock_sem);
free_xid(xid);
return -EINVAL;
}
@@ -981,7 +1000,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
sizeof(LOCKING_ANDX_RANGE);
buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
if (!buf) {
- up_write(&cinode->lock_sem);
free_xid(xid);
return -ENOMEM;
}
@@ -1018,9 +1036,6 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
}
}
- cinode->can_cache_brlcks = false;
- up_write(&cinode->lock_sem);
-
kfree(buf);
free_xid(xid);
return rc;
@@ -1043,7 +1058,6 @@ struct lock_to_push {
static int
cifs_push_posix_locks(struct cifsFileInfo *cfile)
{
- struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct file_lock *flock, **before;
unsigned int count = 0, i = 0;
@@ -1054,14 +1068,6 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
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;
- }
-
lock_flocks();
cifs_for_each_lock(cfile->dentry->d_inode, before) {
if ((*before)->fl_flags & FL_POSIX)
@@ -1127,9 +1133,6 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
}
out:
- cinode->can_cache_brlcks = false;
- up_write(&cinode->lock_sem);
-
free_xid(xid);
return rc;
err_out:
@@ -1144,14 +1147,27 @@ static int
cifs_push_locks(struct cifsFileInfo *cfile)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ int rc = 0;
+
+ /* 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);
+ return rc;
+ }
if (cap_unix(tcon->ses) &&
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
- return cifs_push_posix_locks(cfile);
+ rc = cifs_push_posix_locks(cfile);
+ else
+ rc = tcon->ses->server->ops->push_mand_locks(cfile);
- return tcon->ses->server->ops->push_mand_locks(cfile);
+ cinode->can_cache_brlcks = false;
+ up_write(&cinode->lock_sem);
+ return rc;
}
static void
@@ -1436,16 +1452,18 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
return -ENOMEM;
rc = cifs_lock_add_if(cfile, lock, wait_flag);
- if (rc < 0)
+ if (rc < 0) {
kfree(lock);
- if (rc <= 0)
+ return rc;
+ }
+ if (!rc)
goto out;
rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
type, 1, 0, wait_flag);
if (rc) {
kfree(lock);
- goto out;
+ return rc;
}
cifs_lock_add(cfile, lock);
@@ -2085,7 +2103,15 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
} else {
rc = copied;
pos += copied;
- set_page_dirty(page);
+ /*
+ * When we use strict cache mode and cifs_strict_writev was run
+ * with level II oplock (indicated by leave_pages_clean field of
+ * CIFS_I(inode)), we can leave pages clean - cifs_strict_writev
+ * sent the data to the server itself.
+ */
+ if (!CIFS_I(inode)->leave_pages_clean ||
+ !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO))
+ set_page_dirty(page);
}
if (rc > 0) {
@@ -2436,8 +2462,8 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
}
static ssize_t
-cifs_writev(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+cifs_pagecache_writev(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos, bool cache_ex)
{
struct file *file = iocb->ki_filp;
struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
@@ -2457,10 +2483,14 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
down_read(&cinode->lock_sem);
if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
server->vals->exclusive_lock_type, NULL,
- true)) {
+ CIFS_WRITE_OP)) {
mutex_lock(&inode->i_mutex);
+ if (!cache_ex)
+ cinode->leave_pages_clean = true;
rc = __generic_file_aio_write(iocb, iov, nr_segs,
- &iocb->ki_pos);
+ &iocb->ki_pos);
+ if (!cache_ex)
+ cinode->leave_pages_clean = false;
mutex_unlock(&inode->i_mutex);
}
@@ -2487,42 +2517,62 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
struct cifsFileInfo *cfile = (struct cifsFileInfo *)
iocb->ki_filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
-
-#ifdef CONFIG_CIFS_SMB2
+ ssize_t written, written2;
/*
- * If we have an oplock for read and want to write a data to the file
- * we need to store it in the page cache and then push it to the server
- * to be sure the next read will get a valid data.
+ * We need to store clientCanCacheAll here to prevent race
+ * conditions - this value can be changed during an execution
+ * of generic_file_aio_write. For CIFS it can be changed from
+ * true to false only, but for SMB2 it can be changed both from
+ * true to false and vice versa. So, we can end up with a data
+ * stored in the cache, not marked dirty and not sent to the
+ * server if this value changes its state from false to true
+ * after cifs_write_end.
*/
- if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead) {
- ssize_t written;
- int rc;
-
- written = generic_file_aio_write(iocb, iov, nr_segs, pos);
- rc = filemap_fdatawrite(inode->i_mapping);
- if (rc)
- return (ssize_t)rc;
+ bool cache_ex = cinode->clientCanCacheAll;
+ bool cache_read = cinode->clientCanCacheRead;
+ int rc;
+ loff_t saved_pos;
- return written;
+ if (cache_ex) {
+ if (cap_unix(tcon->ses) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(
+ tcon->fsUnixInfo.Capability)))
+ return generic_file_aio_write(iocb, iov, nr_segs, pos);
+ return cifs_pagecache_writev(iocb, iov, nr_segs, pos, cache_ex);
}
-#endif
/*
- * For non-oplocked files in strict cache mode we need to write the data
- * to the server exactly from the pos to pos+len-1 rather than flush all
- * affected pages because it may cause a error with mandatory locks on
- * these pages but not on the region from pos to ppos+len-1.
+ * For files without exclusive oplock in strict cache mode we need to
+ * write the data to the server exactly from the pos to pos+len-1 rather
+ * than flush all affected pages because it may cause a error with
+ * mandatory locks on these pages but not on the region from pos to
+ * ppos+len-1.
*/
+ written = cifs_user_writev(iocb, iov, nr_segs, pos);
+ if (!cache_read || written <= 0)
+ return written;
- if (!cinode->clientCanCacheAll)
- return cifs_user_writev(iocb, iov, nr_segs, pos);
-
+ saved_pos = iocb->ki_pos;
+ iocb->ki_pos = pos;
+ /* we have a read oplock - need to store a data in the page cache */
if (cap_unix(tcon->ses) &&
- (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
- ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
- return generic_file_aio_write(iocb, iov, nr_segs, pos);
-
- return cifs_writev(iocb, iov, nr_segs, pos);
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(
+ tcon->fsUnixInfo.Capability)))
+ written2 = generic_file_aio_write(iocb, iov, nr_segs, pos);
+ else
+ written2 = cifs_pagecache_writev(iocb, iov, nr_segs, pos,
+ cache_ex);
+ /* errors occured during writing - invalidate the page cache */
+ if (written2 < 0) {
+ rc = cifs_invalidate_mapping(inode);
+ if (rc)
+ written = (ssize_t)rc;
+ else
+ iocb->ki_pos = saved_pos;
+ }
+ return written;
}
static struct cifs_readdata *
@@ -2892,7 +2942,7 @@ cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
down_read(&cinode->lock_sem);
if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
tcon->ses->server->vals->shared_lock_type,
- NULL, true))
+ NULL, CIFS_READ_OP))
rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
up_read(&cinode->lock_sem);
return rc;
@@ -3536,7 +3586,7 @@ void cifs_oplock_break(struct work_struct *work)
if (cinode->clientCanCacheRead == 0) {
rc = filemap_fdatawait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc);
- invalidate_remote_inode(inode);
+ cifs_invalidate_mapping(inode);
}
cFYI(1, "Oplock flush inode %p rc %d", inode, rc);
}