forked from Minki/linux
afs: Handle a short write to an AFS page
Handle the situation where afs_write_begin() is told to expect that a full-page write will be made, but this doesn't happen (EFAULT, CTRL-C, etc.), and so afs_write_end() sees a partial write took place. Currently, no attempt is to deal with the discrepency. Fix this by loading the gap from the server. Reported-by: Al Viro <viro@ZenIV.linux.org.uk> Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
parent
3448e65217
commit
e8e581a88c
@ -393,8 +393,10 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
|
||||
if (req->remain > 0) {
|
||||
call->offset = 0;
|
||||
req->index++;
|
||||
if (req->index >= req->nr_pages)
|
||||
if (req->index >= req->nr_pages) {
|
||||
call->unmarshall = 4;
|
||||
goto begin_discard;
|
||||
}
|
||||
goto begin_page;
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ struct afs_call_type {
|
||||
*/
|
||||
struct afs_read {
|
||||
loff_t pos; /* Where to start reading */
|
||||
loff_t len; /* How much to read */
|
||||
loff_t len; /* How much we're asking for */
|
||||
loff_t actual_len; /* How much we're actually getting */
|
||||
atomic_t usage;
|
||||
unsigned int remain; /* Amount remaining */
|
||||
|
@ -84,10 +84,9 @@ void afs_put_writeback(struct afs_writeback *wb)
|
||||
* partly or wholly fill a page that's under preparation for writing
|
||||
*/
|
||||
static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
|
||||
loff_t pos, struct page *page)
|
||||
loff_t pos, unsigned int len, struct page *page)
|
||||
{
|
||||
struct afs_read *req;
|
||||
loff_t i_size;
|
||||
int ret;
|
||||
|
||||
_enter(",,%llu", (unsigned long long)pos);
|
||||
@ -99,16 +98,11 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
|
||||
|
||||
atomic_set(&req->usage, 1);
|
||||
req->pos = pos;
|
||||
req->len = len;
|
||||
req->nr_pages = 1;
|
||||
req->pages[0] = page;
|
||||
get_page(page);
|
||||
|
||||
i_size = i_size_read(&vnode->vfs_inode);
|
||||
if (pos + PAGE_SIZE > i_size)
|
||||
req->len = i_size - pos;
|
||||
else
|
||||
req->len = PAGE_SIZE;
|
||||
|
||||
ret = afs_vnode_fetch_data(vnode, key, req);
|
||||
afs_put_read(req);
|
||||
if (ret < 0) {
|
||||
@ -164,7 +158,7 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
|
||||
/* page won't leak in error case: it eventually gets cleaned off LRU */
|
||||
|
||||
if (!PageUptodate(page) && len != PAGE_SIZE) {
|
||||
ret = afs_fill_page(vnode, key, index << PAGE_SHIFT, page);
|
||||
ret = afs_fill_page(vnode, key, pos & PAGE_MASK, PAGE_SIZE, page);
|
||||
if (ret < 0) {
|
||||
kfree(candidate);
|
||||
_leave(" = %d [prep]", ret);
|
||||
@ -258,7 +252,9 @@ int afs_write_end(struct file *file, struct address_space *mapping,
|
||||
struct page *page, void *fsdata)
|
||||
{
|
||||
struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
|
||||
struct key *key = file->private_data;
|
||||
loff_t i_size, maybe_i_size;
|
||||
int ret;
|
||||
|
||||
_enter("{%x:%u},{%lx}",
|
||||
vnode->fid.vid, vnode->fid.vnode, page->index);
|
||||
@ -274,6 +270,20 @@ int afs_write_end(struct file *file, struct address_space *mapping,
|
||||
spin_unlock(&vnode->writeback_lock);
|
||||
}
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
if (copied < len) {
|
||||
/* Try and load any missing data from the server. The
|
||||
* unmarshalling routine will take care of clearing any
|
||||
* bits that are beyond the EOF.
|
||||
*/
|
||||
ret = afs_fill_page(vnode, key, pos + copied,
|
||||
len - copied, page);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
|
||||
set_page_dirty(page);
|
||||
if (PageDirty(page))
|
||||
_debug("dirtied");
|
||||
|
Loading…
Reference in New Issue
Block a user