Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ceph: fix reading inline data when i_size > PAGE_SIZE

when inode has inline data but its size > PAGE_SIZE (it was truncated
to larger size), previous direct read code return -EIO. This patch adds
code to return zeros for data whose offset > PAGE_SIZE.

Signed-off-by: Yan, Zheng <zyan@redhat.com>

authored by

Yan, Zheng and committed by
Ilya Dryomov
fcc02d2a 86d8f67b

+26 -15
+12 -7
fs/ceph/addr.c
··· 196 196 u64 len = PAGE_CACHE_SIZE; 197 197 198 198 if (off >= i_size_read(inode)) { 199 - zero_user_segment(page, err, PAGE_CACHE_SIZE); 199 + zero_user_segment(page, 0, PAGE_CACHE_SIZE); 200 200 SetPageUptodate(page); 201 201 return 0; 202 202 } 203 203 204 - /* 205 - * Uptodate inline data should have been added into page cache 206 - * while getting Fcr caps. 207 - */ 208 - if (ci->i_inline_version != CEPH_INLINE_NONE) 209 - return -EINVAL; 204 + if (ci->i_inline_version != CEPH_INLINE_NONE) { 205 + /* 206 + * Uptodate inline data should have been added 207 + * into page cache while getting Fcr caps. 208 + */ 209 + if (off == 0) 210 + return -EINVAL; 211 + zero_user_segment(page, 0, PAGE_CACHE_SIZE); 212 + SetPageUptodate(page); 213 + return 0; 214 + } 210 215 211 216 err = ceph_readpage_from_fscache(inode, page); 212 217 if (err == 0)
+14 -8
fs/ceph/file.c
··· 879 879 880 880 i_size = i_size_read(inode); 881 881 if (retry_op == READ_INLINE) { 882 - /* does not support inline data > PAGE_SIZE */ 883 - if (i_size > PAGE_CACHE_SIZE) { 884 - ret = -EIO; 885 - } else if (iocb->ki_pos < i_size) { 882 + BUG_ON(ret > 0 || read > 0); 883 + if (iocb->ki_pos < i_size && 884 + iocb->ki_pos < PAGE_CACHE_SIZE) { 886 885 loff_t end = min_t(loff_t, i_size, 887 886 iocb->ki_pos + len); 887 + end = min_t(loff_t, end, PAGE_CACHE_SIZE); 888 888 if (statret < end) 889 889 zero_user_segment(page, statret, end); 890 890 ret = copy_page_to_iter(page, 891 891 iocb->ki_pos & ~PAGE_MASK, 892 892 end - iocb->ki_pos, to); 893 893 iocb->ki_pos += ret; 894 - } else { 895 - ret = 0; 894 + read += ret; 895 + } 896 + if (iocb->ki_pos < i_size && read < len) { 897 + size_t zlen = min_t(size_t, len - read, 898 + i_size - iocb->ki_pos); 899 + ret = iov_iter_zero(zlen, to); 900 + iocb->ki_pos += ret; 901 + read += ret; 896 902 } 897 903 __free_pages(page, 0); 898 - return ret; 904 + return read; 899 905 } 900 906 901 907 /* hit EOF or hole? */ 902 908 if (retry_op == CHECK_EOF && iocb->ki_pos < i_size && 903 - ret < len) { 909 + ret < len) { 904 910 dout("sync_read hit hole, ppos %lld < size %lld" 905 911 ", reading more\n", iocb->ki_pos, 906 912 inode->i_size);