diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/inode.c | 17 |
1 files changed, 16 insertions, 1 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 24310c9cb14f..00d59c6a9769 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -6207,6 +6207,7 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io | |||
6207 | unsigned long nr_segs) | 6207 | unsigned long nr_segs) |
6208 | { | 6208 | { |
6209 | int seg; | 6209 | int seg; |
6210 | int i; | ||
6210 | size_t size; | 6211 | size_t size; |
6211 | unsigned long addr; | 6212 | unsigned long addr; |
6212 | unsigned blocksize_mask = root->sectorsize - 1; | 6213 | unsigned blocksize_mask = root->sectorsize - 1; |
@@ -6221,8 +6222,22 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io | |||
6221 | addr = (unsigned long)iov[seg].iov_base; | 6222 | addr = (unsigned long)iov[seg].iov_base; |
6222 | size = iov[seg].iov_len; | 6223 | size = iov[seg].iov_len; |
6223 | end += size; | 6224 | end += size; |
6224 | if ((addr & blocksize_mask) || (size & blocksize_mask)) | 6225 | if ((addr & blocksize_mask) || (size & blocksize_mask)) |
6225 | goto out; | 6226 | goto out; |
6227 | |||
6228 | /* If this is a write we don't need to check anymore */ | ||
6229 | if (rw & WRITE) | ||
6230 | continue; | ||
6231 | |||
6232 | /* | ||
6233 | * Check to make sure we don't have duplicate iov_base's in this | ||
6234 | * iovec, if so return EINVAL, otherwise we'll get csum errors | ||
6235 | * when reading back. | ||
6236 | */ | ||
6237 | for (i = seg + 1; i < nr_segs; i++) { | ||
6238 | if (iov[seg].iov_base == iov[i].iov_base) | ||
6239 | goto out; | ||
6240 | } | ||
6226 | } | 6241 | } |
6227 | retval = 0; | 6242 | retval = 0; |
6228 | out: | 6243 | out: |