Commit e8e581a8 authored by David Howells's avatar David Howells
Browse files

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: default avatarAl Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 3448e652
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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;
			}
		}
+1 −1
Original line number Diff line number Diff line
@@ -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 */
+19 −9
Original line number Diff line number Diff line
@@ -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");