Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57fd0b77 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull AFS fixes from David Howells:
 "Fixes to the AFS filesystem in the kernel.

  They fix a variety of bugs. These include some issues fixed for
  consistency with other AFS implementations:

   - handle AFS mode bits better

   - use the client mtime rather than the server mtime in the protocol

   - handle the server returning more or less data than was requested in
     a FetchData call

   - distinguish mountpoints from symlinks based on the mode bits rather
     than preemptively reading every symlink to find out what it
     actually represents

  One other notable change for the user is that files are now flushed on
  close analogously with other network filesystems"

* tag 'afs-20170316' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: (28 commits)
  afs: Don't wait for page writeback with the page lock held
  afs: ->writepage() shouldn't call clear_page_dirty_for_io()
  afs: Fix abort on signal while waiting for call completion
  afs: Fix an off-by-one error in afs_send_pages()
  afs: Fix afs_kill_pages()
  afs: Fix page leak in afs_write_begin()
  afs: Don't set PG_error on local EINTR or ENOMEM when filling a page
  afs: Populate and use client modification time
  afs: Better abort and net error handling
  afs: Invalid op ID should abort with RXGEN_OPCODE
  afs: Fix the maths in afs_fs_store_data()
  afs: Use a bvec rather than a kvec in afs_send_pages()
  afs: Make struct afs_read::remain 64-bit
  afs: Fix AFS read bug
  afs: Prevent callback expiry timer overflow
  afs: Migrate vlocation fields to 64-bit
  afs: security: Replace rcu_assign_pointer() with RCU_INIT_POINTER()
  afs: inode: Replace rcu_assign_pointer() with RCU_INIT_POINTER()
  afs: Distinguish mountpoints from symlinks by file mode alone
  afs: Flush outstanding writes when an fd is closed
  ...
parents c79d5ff0 c5051c7b
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -362,7 +362,7 @@ static void afs_callback_updater(struct work_struct *work)
{
	struct afs_server *server;
	struct afs_vnode *vnode, *xvnode;
	time_t now;
	time64_t now;
	long timeout;
	int ret;

@@ -370,7 +370,7 @@ static void afs_callback_updater(struct work_struct *work)

	_enter("");

	now = get_seconds();
	now = ktime_get_real_seconds();

	/* find the first vnode to update */
	spin_lock(&server->cb_lock);
@@ -424,7 +424,8 @@ static void afs_callback_updater(struct work_struct *work)

	/* and then reschedule */
	_debug("reschedule");
	vnode->update_at = get_seconds() + afs_vnode_update_timeout;
	vnode->update_at = ktime_get_real_seconds() +
			afs_vnode_update_timeout;

	spin_lock(&server->cb_lock);

+5 −6
Original line number Diff line number Diff line
@@ -187,7 +187,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)
	struct afs_callback *cb;
	struct afs_server *server;
	__be32 *bp;
	u32 tmp;
	int ret, loop;

	_enter("{%u}", call->unmarshall);
@@ -249,9 +248,9 @@ static int afs_deliver_cb_callback(struct afs_call *call)
		if (ret < 0)
			return ret;

		tmp = ntohl(call->tmp);
		_debug("CB count: %u", tmp);
		if (tmp != call->count && tmp != 0)
		call->count2 = ntohl(call->tmp);
		_debug("CB count: %u", call->count2);
		if (call->count2 != call->count && call->count2 != 0)
			return -EBADMSG;
		call->offset = 0;
		call->unmarshall++;
@@ -259,14 +258,14 @@ static int afs_deliver_cb_callback(struct afs_call *call)
	case 4:
		_debug("extract CB array");
		ret = afs_extract_data(call, call->buffer,
				       call->count * 3 * 4, false);
				       call->count2 * 3 * 4, false);
		if (ret < 0)
			return ret;

		_debug("unmarshall CB array");
		cb = call->request;
		bp = call->buffer;
		for (loop = call->count; loop > 0; loop--, cb++) {
		for (loop = call->count2; loop > 0; loop--, cb++) {
			cb->version	= ntohl(*bp++);
			cb->expiry	= ntohl(*bp++);
			cb->type	= ntohl(*bp++);
+16 −4
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ static int afs_readpages(struct file *filp, struct address_space *mapping,

const struct file_operations afs_file_operations = {
	.open		= afs_open,
	.flush		= afs_flush,
	.release	= afs_release,
	.llseek		= generic_file_llseek,
	.read_iter	= generic_file_read_iter,
@@ -184,10 +185,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);
@@ -208,7 +212,13 @@ int afs_page_filler(void *data, struct page *page)
			fscache_uncache_page(vnode->cache, page);
#endif
			BUG_ON(PageFsCache(page));

			if (ret == -EINTR ||
			    ret == -ENOMEM ||
			    ret == -ERESTARTSYS ||
			    ret == -EAGAIN)
				goto error;
			goto io_error;
		}

		SetPageUptodate(page);
@@ -227,10 +237,12 @@ int afs_page_filler(void *data, struct page *page)
	_leave(" = 0");
	return 0;

io_error:
	SetPageError(page);
	goto error;
enomem:
	ret = -ENOMEM;
error:
	SetPageError(page);
	unlock_page(page);
	_leave(" = %d", ret);
	return ret;
+50 −27
Original line number Diff line number Diff line
@@ -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
 */
@@ -105,7 +111,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
			vnode->vfs_inode.i_mode = mode;
		}

		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_server;
		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_client;
		vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
		vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
		vnode->vfs_inode.i_version	= data_version;
@@ -139,7 +145,7 @@ static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
	vnode->cb_version	= ntohl(*bp++);
	vnode->cb_expiry	= ntohl(*bp++);
	vnode->cb_type		= ntohl(*bp++);
	vnode->cb_expires	= vnode->cb_expiry + get_seconds();
	vnode->cb_expires	= vnode->cb_expiry + ktime_get_real_seconds();
	*_bp = bp;
}

@@ -315,7 +321,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
	void *buffer;
	int ret;

	_enter("{%u,%zu/%u;%u/%llu}",
	_enter("{%u,%zu/%u;%llu/%llu}",
	       call->unmarshall, call->offset, call->count,
	       req->remain, req->actual_len);

@@ -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
@@ -378,7 +379,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)

		/* extract the returned data */
	case 3:
		_debug("extract data %u/%llu %zu/%u",
		_debug("extract data %llu/%llu %zu/%u",
		       req->remain, req->actual_len, call->offset, call->count);

		buffer = kmap(req->pages[req->index]);
@@ -389,19 +390,40 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
		if (call->offset == PAGE_SIZE) {
			if (req->page_done)
				req->page_done(call, req);
			if (req->remain > 0) {
			req->index++;
			if (req->remain > 0) {
				call->offset = 0;
				if (req->index >= req->nr_pages) {
					call->unmarshall = 4;
					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(loff_t, sizeof(afs_discard_buffer), req->remain);
		call->count = size;
		_debug("extract discard %llu/%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 +438,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]");
@@ -711,8 +734,8 @@ int afs_fs_create(struct afs_server *server,
		memset(bp, 0, padsz);
		bp = (void *) bp + padsz;
	}
	*bp++ = htonl(AFS_SET_MODE);
	*bp++ = 0; /* mtime */
	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
	*bp++ = 0; /* owner */
	*bp++ = 0; /* group */
	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
@@ -980,8 +1003,8 @@ int afs_fs_symlink(struct afs_server *server,
		memset(bp, 0, c_padsz);
		bp = (void *) bp + c_padsz;
	}
	*bp++ = htonl(AFS_SET_MODE);
	*bp++ = 0; /* mtime */
	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
	*bp++ = 0; /* owner */
	*bp++ = 0; /* group */
	*bp++ = htonl(S_IRWXUGO); /* unix mode */
@@ -1180,8 +1203,8 @@ static int afs_fs_store_data64(struct afs_server *server,
	*bp++ = htonl(vnode->fid.vnode);
	*bp++ = htonl(vnode->fid.unique);

	*bp++ = 0; /* mask */
	*bp++ = 0; /* mtime */
	*bp++ = htonl(AFS_SET_MTIME); /* mask */
	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
	*bp++ = 0; /* owner */
	*bp++ = 0; /* group */
	*bp++ = 0; /* unix mode */
@@ -1213,7 +1236,7 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
	_enter(",%x,{%x:%u},,",
	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);

	size = to - offset;
	size = (loff_t)to - (loff_t)offset;
	if (first != last)
		size += (loff_t)(last - first) << PAGE_SHIFT;
	pos = (loff_t)first << PAGE_SHIFT;
@@ -1257,8 +1280,8 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
	*bp++ = htonl(vnode->fid.vnode);
	*bp++ = htonl(vnode->fid.unique);

	*bp++ = 0; /* mask */
	*bp++ = 0; /* mtime */
	*bp++ = htonl(AFS_SET_MTIME); /* mask */
	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
	*bp++ = 0; /* owner */
	*bp++ = 0; /* group */
	*bp++ = 0; /* unix mode */
+22 −20
Original line number Diff line number Diff line
@@ -54,8 +54,21 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
		inode->i_fop	= &afs_dir_file_operations;
		break;
	case AFS_FTYPE_SYMLINK:
		/* Symlinks with a mode of 0644 are actually mountpoints. */
		if ((vnode->status.mode & 0777) == 0644) {
			inode->i_flags |= S_AUTOMOUNT;

			spin_lock(&vnode->lock);
			set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
			spin_unlock(&vnode->lock);

			inode->i_mode	= S_IFDIR | 0555;
			inode->i_op	= &afs_mntpt_inode_operations;
			inode->i_fop	= &afs_mntpt_file_operations;
		} else {
			inode->i_mode	= S_IFLNK | vnode->status.mode;
			inode->i_op	= &page_symlink_inode_operations;
		}
		inode_nohighmem(inode);
		break;
	default:
@@ -70,27 +83,15 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)

	set_nlink(inode, vnode->status.nlink);
	inode->i_uid		= vnode->status.owner;
	inode->i_gid		= GLOBAL_ROOT_GID;
	inode->i_gid            = vnode->status.group;
	inode->i_size		= vnode->status.size;
	inode->i_ctime.tv_sec	= vnode->status.mtime_server;
	inode->i_ctime.tv_sec	= vnode->status.mtime_client;
	inode->i_ctime.tv_nsec	= 0;
	inode->i_atime		= inode->i_mtime = inode->i_ctime;
	inode->i_blocks		= 0;
	inode->i_generation	= vnode->fid.unique;
	inode->i_version	= vnode->status.data_version;
	inode->i_mapping->a_ops	= &afs_fs_aops;

	/* check to see whether a symbolic link is really a mountpoint */
	if (vnode->status.type == AFS_FTYPE_SYMLINK) {
		afs_mntpt_check_symlink(vnode, key);

		if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) {
			inode->i_mode	= S_IFDIR | vnode->status.mode;
			inode->i_op	= &afs_mntpt_inode_operations;
			inode->i_fop	= &afs_mntpt_file_operations;
		}
	}

	return 0;
}

@@ -245,12 +246,13 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
			vnode->cb_version = 0;
			vnode->cb_expiry = 0;
			vnode->cb_type = 0;
			vnode->cb_expires = get_seconds();
			vnode->cb_expires = ktime_get_real_seconds();
		} else {
			vnode->cb_version = cb->version;
			vnode->cb_expiry = cb->expiry;
			vnode->cb_type = cb->type;
			vnode->cb_expires = vnode->cb_expiry + get_seconds();
			vnode->cb_expires = vnode->cb_expiry +
				ktime_get_real_seconds();
		}
	}

@@ -323,7 +325,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
	    !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
	    !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) &&
	    !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
		if (vnode->cb_expires < get_seconds() + 10) {
		if (vnode->cb_expires < ktime_get_real_seconds() + 10) {
			_debug("callback expired");
			set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
		} else {
@@ -444,7 +446,7 @@ void afs_evict_inode(struct inode *inode)

	mutex_lock(&vnode->permits_lock);
	permits = vnode->permits;
	rcu_assign_pointer(vnode->permits, NULL);
	RCU_INIT_POINTER(vnode->permits, NULL);
	mutex_unlock(&vnode->permits_lock);
	if (permits)
		call_rcu(&permits->rcu, afs_zap_permits);
Loading