[PATCH linux-2.6] fs: error case fix in __generic_file_aio_read

From: Tejun Heo
Date: Tue Oct 04 2005 - 03:46:46 EST


Hello, guys.

When __generic_file_aio_read() hits an error during reading, it
reports the error iff nothing has successfully been read yet. This is
different from how other read/write functions deal with error
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.

Thanks.

Signed-off-by: Tejun Heo <htejun@xxxxxxxxx>

diff --git a/mm/filemap.c b/mm/filemap.c
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1030,8 +1030,8 @@ __generic_file_aio_read(struct kiocb *io
desc.error = 0;
do_generic_file_read(filp,ppos,&desc,file_read_actor);
retval += desc.written;
- if (!retval) {
- retval = desc.error;
+ if (desc.error) {
+ retval = retval ?: desc.error;
break;
}
}
#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;
}