aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@etersoft.ru>2012-09-19 09:22:44 -0400
committerSteve French <smfrench@gmail.com>2012-09-24 22:46:33 -0400
commit579f9053236c796d718162c37c72bb3bd32d008c (patch)
tree4de2901dcba0a49b782068b70b5b28df90b20f4b /fs/cifs
parent1b4b55a1d9404f51aacf1ff19887eb911484057d (diff)
CIFS: Check for mandatory brlocks on read/write
Currently CIFS code accept read/write ops on mandatory locked area when two processes use the same file descriptor - it's wrong. Fix this by serializing io and brlock operations on the inode. Signed-off-by: Pavel Shilovsky <pshilovsky@etersoft.ru> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifsproto.h4
-rw-r--r--fs/cifs/file.c121
2 files changed, 102 insertions, 23 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 15e38dc389fc..c758ee7b0307 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -180,6 +180,10 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data,
180extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); 180extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
181extern void cifs_umount(struct cifs_sb_info *); 181extern void cifs_umount(struct cifs_sb_info *);
182extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); 182extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
183extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
184 __u64 length, __u8 type,
185 struct cifsLockInfo **conf_lock,
186 bool rw_check);
183 187
184#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) 188#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
185extern void cifs_dfs_release_automount_timer(void); 189extern void cifs_dfs_release_automount_timer(void);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 1adff75dfffc..2e2e4f9aeb63 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -707,7 +707,7 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
707static bool 707static bool
708cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, 708cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
709 __u64 length, __u8 type, struct cifsFileInfo *cfile, 709 __u64 length, __u8 type, struct cifsFileInfo *cfile,
710 struct cifsLockInfo **conf_lock) 710 struct cifsLockInfo **conf_lock, bool rw_check)
711{ 711{
712 struct cifsLockInfo *li; 712 struct cifsLockInfo *li;
713 struct cifsFileInfo *cur_cfile = fdlocks->cfile; 713 struct cifsFileInfo *cur_cfile = fdlocks->cfile;
@@ -717,19 +717,24 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
717 if (offset + length <= li->offset || 717 if (offset + length <= li->offset ||
718 offset >= li->offset + li->length) 718 offset >= li->offset + li->length)
719 continue; 719 continue;
720 if (rw_check && server->ops->compare_fids(cfile, cur_cfile) &&
721 current->tgid == li->pid)
722 continue;
720 if ((type & server->vals->shared_lock_type) && 723 if ((type & server->vals->shared_lock_type) &&
721 ((server->ops->compare_fids(cfile, cur_cfile) && 724 ((server->ops->compare_fids(cfile, cur_cfile) &&
722 current->tgid == li->pid) || type == li->type)) 725 current->tgid == li->pid) || type == li->type))
723 continue; 726 continue;
724 *conf_lock = li; 727 if (conf_lock)
728 *conf_lock = li;
725 return true; 729 return true;
726 } 730 }
727 return false; 731 return false;
728} 732}
729 733
730static bool 734bool
731cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, 735cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
732 __u8 type, struct cifsLockInfo **conf_lock) 736 __u8 type, struct cifsLockInfo **conf_lock,
737 bool rw_check)
733{ 738{
734 bool rc = false; 739 bool rc = false;
735 struct cifs_fid_locks *cur; 740 struct cifs_fid_locks *cur;
@@ -737,7 +742,7 @@ cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
737 742
738 list_for_each_entry(cur, &cinode->llist, llist) { 743 list_for_each_entry(cur, &cinode->llist, llist) {
739 rc = cifs_find_fid_lock_conflict(cur, offset, length, type, 744 rc = cifs_find_fid_lock_conflict(cur, offset, length, type,
740 cfile, conf_lock); 745 cfile, conf_lock, rw_check);
741 if (rc) 746 if (rc)
742 break; 747 break;
743 } 748 }
@@ -765,7 +770,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
765 down_read(&cinode->lock_sem); 770 down_read(&cinode->lock_sem);
766 771
767 exist = cifs_find_lock_conflict(cfile, offset, length, type, 772 exist = cifs_find_lock_conflict(cfile, offset, length, type,
768 &conf_lock); 773 &conf_lock, false);
769 if (exist) { 774 if (exist) {
770 flock->fl_start = conf_lock->offset; 775 flock->fl_start = conf_lock->offset;
771 flock->fl_end = conf_lock->offset + conf_lock->length - 1; 776 flock->fl_end = conf_lock->offset + conf_lock->length - 1;
@@ -812,7 +817,7 @@ try_again:
812 down_write(&cinode->lock_sem); 817 down_write(&cinode->lock_sem);
813 818
814 exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, 819 exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
815 lock->type, &conf_lock); 820 lock->type, &conf_lock, false);
816 if (!exist && cinode->can_cache_brlcks) { 821 if (!exist && cinode->can_cache_brlcks) {
817 list_add_tail(&lock->llist, &cfile->llist->locks); 822 list_add_tail(&lock->llist, &cfile->llist->locks);
818 up_write(&cinode->lock_sem); 823 up_write(&cinode->lock_sem);
@@ -2394,15 +2399,58 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
2394 return written; 2399 return written;
2395} 2400}
2396 2401
2397ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, 2402static ssize_t
2398 unsigned long nr_segs, loff_t pos) 2403cifs_writev(struct kiocb *iocb, const struct iovec *iov,
2404 unsigned long nr_segs, loff_t pos)
2399{ 2405{
2400 struct inode *inode; 2406 struct file *file = iocb->ki_filp;
2407 struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
2408 struct inode *inode = file->f_mapping->host;
2409 struct cifsInodeInfo *cinode = CIFS_I(inode);
2410 struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
2411 ssize_t rc = -EACCES;
2401 2412
2402 inode = iocb->ki_filp->f_path.dentry->d_inode; 2413 BUG_ON(iocb->ki_pos != pos);
2403 2414
2404 if (CIFS_I(inode)->clientCanCacheAll) 2415 sb_start_write(inode->i_sb);
2405 return generic_file_aio_write(iocb, iov, nr_segs, pos); 2416
2417 /*
2418 * We need to hold the sem to be sure nobody modifies lock list
2419 * with a brlock that prevents writing.
2420 */
2421 down_read(&cinode->lock_sem);
2422 if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
2423 server->vals->exclusive_lock_type, NULL,
2424 true)) {
2425 mutex_lock(&inode->i_mutex);
2426 rc = __generic_file_aio_write(iocb, iov, nr_segs,
2427 &iocb->ki_pos);
2428 mutex_unlock(&inode->i_mutex);
2429 }
2430
2431 if (rc > 0 || rc == -EIOCBQUEUED) {
2432 ssize_t err;
2433
2434 err = generic_write_sync(file, pos, rc);
2435 if (err < 0 && rc > 0)
2436 rc = err;
2437 }
2438
2439 up_read(&cinode->lock_sem);
2440 sb_end_write(inode->i_sb);
2441 return rc;
2442}
2443
2444ssize_t
2445cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
2446 unsigned long nr_segs, loff_t pos)
2447{
2448 struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
2449 struct cifsInodeInfo *cinode = CIFS_I(inode);
2450 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
2451 struct cifsFileInfo *cfile = (struct cifsFileInfo *)
2452 iocb->ki_filp->private_data;
2453 struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
2406 2454
2407 /* 2455 /*
2408 * In strict cache mode we need to write the data to the server exactly 2456 * In strict cache mode we need to write the data to the server exactly
@@ -2411,7 +2459,15 @@ ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
2411 * not on the region from pos to ppos+len-1. 2459 * not on the region from pos to ppos+len-1.
2412 */ 2460 */
2413 2461
2414 return cifs_user_writev(iocb, iov, nr_segs, pos); 2462 if (!cinode->clientCanCacheAll)
2463 return cifs_user_writev(iocb, iov, nr_segs, pos);
2464
2465 if (cap_unix(tcon->ses) &&
2466 (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
2467 ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
2468 return generic_file_aio_write(iocb, iov, nr_segs, pos);
2469
2470 return cifs_writev(iocb, iov, nr_segs, pos);
2415} 2471}
2416 2472
2417static struct cifs_readdata * 2473static struct cifs_readdata *
@@ -2746,15 +2802,17 @@ ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
2746 return read; 2802 return read;
2747} 2803}
2748 2804
2749ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, 2805ssize_t
2750 unsigned long nr_segs, loff_t pos) 2806cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
2807 unsigned long nr_segs, loff_t pos)
2751{ 2808{
2752 struct inode *inode; 2809 struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
2753 2810 struct cifsInodeInfo *cinode = CIFS_I(inode);
2754 inode = iocb->ki_filp->f_path.dentry->d_inode; 2811 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
2755 2812 struct cifsFileInfo *cfile = (struct cifsFileInfo *)
2756 if (CIFS_I(inode)->clientCanCacheRead) 2813 iocb->ki_filp->private_data;
2757 return generic_file_aio_read(iocb, iov, nr_segs, pos); 2814 struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
2815 int rc = -EACCES;
2758 2816
2759 /* 2817 /*
2760 * In strict cache mode we need to read from the server all the time 2818 * In strict cache mode we need to read from the server all the time
@@ -2764,8 +2822,25 @@ ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
2764 * on pages affected by this read but not on the region from pos to 2822 * on pages affected by this read but not on the region from pos to
2765 * pos+len-1. 2823 * pos+len-1.
2766 */ 2824 */
2825 if (!cinode->clientCanCacheRead)
2826 return cifs_user_readv(iocb, iov, nr_segs, pos);
2767 2827
2768 return cifs_user_readv(iocb, iov, nr_segs, pos); 2828 if (cap_unix(tcon->ses) &&
2829 (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
2830 ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
2831 return generic_file_aio_read(iocb, iov, nr_segs, pos);
2832
2833 /*
2834 * We need to hold the sem to be sure nobody modifies lock list
2835 * with a brlock that prevents reading.
2836 */
2837 down_read(&cinode->lock_sem);
2838 if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
2839 tcon->ses->server->vals->shared_lock_type,
2840 NULL, true))
2841 rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
2842 up_read(&cinode->lock_sem);
2843 return rc;
2769} 2844}
2770 2845
2771static ssize_t 2846static ssize_t