• Tejun Heo's avatar
    [PATCH] fs: error case fix in __generic_file_aio_read · 39e88ca2
    Tejun Heo authored
    When __generic_file_aio_read() hits an error during reading, it reports the
    error iff nothing has successfully been read yet.  This is condition - when
    an error occurs, if nothing has been read/written, report the error code;
    otherwise, report the amount of bytes successfully transferred upto that
    point.
    
    This corner case can be exposed by performing readv(2) with the following
    iov.
    
     iov[0] = len0 @ ptr0
     iov[1] = len1 @ NULL (or any other invalid pointer)
     iov[2] = len2 @ ptr2
    
    When file size is enough, performing above readv(2) results in
    
     len0 bytes from file_pos @ ptr0
     len2 bytes from file_pos + len0 @ ptr2
    
    And the return value is len0 + len2.  Test program is attached to this
    mail.
    
    This patch makes __generic_file_aio_read()'s error handling identical to
    other functions.
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/uio.h>
    #include <errno.h>
    #include <string.h>
    
    int main(int argc, char **argv)
    {
    	const char *path;
    	struct stat stbuf;
    	size_t len0, len1;
    	void *buf0, *buf1;
    	struct iovec iov[3];
    	int fd, i;
    	ssize_t ret;
    
    	if (argc < 2) {
    		fprintf(stderr, "Usage: testreadv path (better be a "
    			"small text file)\n");
    		return 1;
    	}
    	path = argv[1];
    
    	if (stat(path, &stbuf) < 0) {
    		perror("stat");
    		return 1;
    	}
    
    	len0 = stbuf.st_size / 2;
    	len1 = stbuf.st_size - len0;
    
    	if (!len0 || !len1) {
    		fprintf(stderr, "Dude, file is too small\n");
    		return 1;
    	}
    
    	if ((fd = open(path, O_RDONLY)) < 0) {
    		perror("open");
    		return 1;
    	}
    
    	if (!(buf0 = malloc(len0)) || !(buf1 = malloc(len1))) {
    		perror("malloc");
    		return 1;
    	}
    
    	memset(buf0, 0, len0);
    	memset(buf1, 0, len1);
    
    	iov[0].iov_base = buf0;
    	iov[0].iov_len = len0;
    	iov[1].iov_base = NULL;
    	iov[1].iov_len = len1;
    	iov[2].iov_base = buf1;
    	iov[2].iov_len = len1;
    
    	printf("vector ");
    	for (i = 0; i < 3; i++)
    		printf("%p:%zu ", iov[i].iov_base, iov[i].iov_len);
    	printf("\n");
    
    	ret = readv(fd, iov, 3);
    	if (ret < 0)
    		perror("readv");
    
    	printf("readv returned %zd\nbuf0 = [%s]\nbuf1 = [%s]\n",
    	       ret, (char *)buf0, (char *)buf1);
    
    	return 0;
    }
    Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
    Cc: Benjamin LaHaise <bcrl@kvack.org>
    Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
    39e88ca2
filemap.c 56.5 KB