diff options
author | Pavel Shilovsky <pshilovsky@etersoft.ru> | 2012-09-19 09:22:44 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:33 -0400 |
commit | 579f9053236c796d718162c37c72bb3bd32d008c (patch) | |
tree | 4de2901dcba0a49b782068b70b5b28df90b20f4b /fs/cifs | |
parent | 1b4b55a1d9404f51aacf1ff19887eb911484057d (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.h | 4 | ||||
-rw-r--r-- | fs/cifs/file.c | 121 |
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, | |||
180 | extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); | 180 | extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); |
181 | extern void cifs_umount(struct cifs_sb_info *); | 181 | extern void cifs_umount(struct cifs_sb_info *); |
182 | extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); | 182 | extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); |
183 | extern 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) |
185 | extern void cifs_dfs_release_automount_timer(void); | 189 | extern 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) | |||
707 | static bool | 707 | static bool |
708 | cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, | 708 | cifs_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 | ||
730 | static bool | 734 | bool |
731 | cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, | 735 | cifs_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 | ||
2397 | ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, | 2402 | static ssize_t |
2398 | unsigned long nr_segs, loff_t pos) | 2403 | cifs_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 | |||
2444 | ssize_t | ||
2445 | cifs_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 | ||
2417 | static struct cifs_readdata * | 2473 | static 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 | ||
2749 | ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, | 2805 | ssize_t |
2750 | unsigned long nr_segs, loff_t pos) | 2806 | cifs_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 | ||
2771 | static ssize_t | 2846 | static ssize_t |