diff options
| -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 2d1208f964eb..edafc28883af 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
| @@ -6153,6 +6153,7 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io | |||
| 6153 | unsigned long nr_segs) | 6153 | unsigned long nr_segs) |
| 6154 | { | 6154 | { |
| 6155 | int seg; | 6155 | int seg; |
| 6156 | int i; | ||
| 6156 | size_t size; | 6157 | size_t size; |
| 6157 | unsigned long addr; | 6158 | unsigned long addr; |
| 6158 | unsigned blocksize_mask = root->sectorsize - 1; | 6159 | unsigned blocksize_mask = root->sectorsize - 1; |
| @@ -6167,8 +6168,22 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io | |||
| 6167 | addr = (unsigned long)iov[seg].iov_base; | 6168 | addr = (unsigned long)iov[seg].iov_base; |
| 6168 | size = iov[seg].iov_len; | 6169 | size = iov[seg].iov_len; |
| 6169 | end += size; | 6170 | end += size; |
| 6170 | if ((addr & blocksize_mask) || (size & blocksize_mask)) | 6171 | if ((addr & blocksize_mask) || (size & blocksize_mask)) |
| 6171 | goto out; | 6172 | goto out; |
| 6173 | |||
| 6174 | /* If this is a write we don't need to check anymore */ | ||
| 6175 | if (rw & WRITE) | ||
| 6176 | continue; | ||
| 6177 | |||
| 6178 | /* | ||
| 6179 | * Check to make sure we don't have duplicate iov_base's in this | ||
| 6180 | * iovec, if so return EINVAL, otherwise we'll get csum errors | ||
| 6181 | * when reading back. | ||
| 6182 | */ | ||
| 6183 | for (i = seg + 1; i < nr_segs; i++) { | ||
| 6184 | if (iov[seg].iov_base == iov[i].iov_base) | ||
| 6185 | goto out; | ||
| 6186 | } | ||
| 6172 | } | 6187 | } |
| 6173 | retval = 0; | 6188 | retval = 0; |
| 6174 | out: | 6189 | out: |
