Commit bd127e84 authored by David Howells's avatar David Howells

afs: Handle better the server returning excess or short data

When an AFS server is given an FS.FetchData{,64} request to read data from
a file, it is permitted by the protocol to return more or less than was
requested.  kafs currently relies on the latter behaviour in readpage{,s}
to handle a partial page at the end of the file (we just ask for a whole
page and clear space beyond the short read).

However, we don't handle all cases.  Add:

 (1) Handle excess data by discarding it rather than aborting.  Note that
     we use a common static buffer to discard into so that the decryption
     algorithm advances the PCBC state.

 (2) Handle a short read that affects more than just the last page.

Note that if a read comes up unexpectedly short of long, it's possible that
the server's copy of the file changed - in which case the data version
number will have been incremented and the callback will have been broken -
in which case all the pages currently attached to the inode will be zapped
anyway at some point.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 97567a23
......@@ -184,10 +184,13 @@ int afs_page_filler(void *data, struct page *page)
if (!req)
goto enomem;
/* We request a full page. If the page is a partial one at the
* end of the file, the server will return a short read and the
* unmarshalling code will clear the unfilled space.
*/
atomic_set(&req->usage, 1);
req->pos = (loff_t)page->index << PAGE_SHIFT;
req->len = min_t(size_t, i_size_read(inode) - req->pos,
PAGE_SIZE);
req->len = PAGE_SIZE;
req->nr_pages = 1;
req->pages[0] = page;
get_page(page);
......
......@@ -16,6 +16,12 @@
#include "internal.h"
#include "afs_fs.h"
/*
* We need somewhere to discard into in case the server helpfully returns more
* than we asked for in FS.FetchData{,64}.
*/
static u8 afs_discard_buffer[64];
/*
* decode an AFSFid block
*/
......@@ -353,12 +359,6 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
req->actual_len |= ntohl(call->tmp);
_debug("DATA length: %llu", req->actual_len);
/* Check that the server didn't want to send us extra. We
* might want to just discard instead, but that requires
* cooperation from AF_RXRPC.
*/
if (req->actual_len > req->len)
return -EBADMSG;
req->remain = req->actual_len;
call->offset = req->pos & (PAGE_SIZE - 1);
......@@ -368,6 +368,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
call->unmarshall++;
begin_page:
ASSERTCMP(req->index, <, req->nr_pages);
if (req->remain > PAGE_SIZE - call->offset)
size = PAGE_SIZE - call->offset;
else
......@@ -390,18 +391,37 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
if (req->page_done)
req->page_done(call, req);
if (req->remain > 0) {
req->index++;
call->offset = 0;
req->index++;
if (req->index >= req->nr_pages)
goto begin_discard;
goto begin_page;
}
}
goto no_more_data;
/* Discard any excess data the server gave us */
begin_discard:
case 4:
size = min_t(size_t, sizeof(afs_discard_buffer), req->remain);
call->count = size;
_debug("extract discard %u/%llu %zu/%u",
req->remain, req->actual_len, call->offset, call->count);
call->offset = 0;
ret = afs_extract_data(call, afs_discard_buffer, call->count, true);
req->remain -= call->offset;
if (ret < 0)
return ret;
if (req->remain > 0)
goto begin_discard;
no_more_data:
call->offset = 0;
call->unmarshall++;
call->unmarshall = 5;
/* extract the metadata */
case 4:
case 5:
ret = afs_extract_data(call, call->buffer,
(21 + 3 + 6) * 4, false);
if (ret < 0)
......@@ -416,16 +436,17 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
call->offset = 0;
call->unmarshall++;
case 5:
case 6:
break;
}
if (call->count < PAGE_SIZE) {
buffer = kmap(req->pages[req->index]);
memset(buffer + call->count, 0, PAGE_SIZE - call->count);
kunmap(req->pages[req->index]);
for (; req->index < req->nr_pages; req->index++) {
if (call->count < PAGE_SIZE)
zero_user_segment(req->pages[req->index],
call->count, PAGE_SIZE);
if (req->page_done)
req->page_done(call, req);
call->count = 0;
}
_leave(" = 0 [done]");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment