xref: /openbmc/linux/fs/afs/fsclient.c (revision f044c884)
108e0e7c8SDavid Howells /* AFS File Server client stubs
21da177e4SLinus Torvalds  *
308e0e7c8SDavid Howells  * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
41da177e4SLinus Torvalds  * Written by David Howells (dhowells@redhat.com)
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
71da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
81da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
91da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/init.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/sched.h>
1508e0e7c8SDavid Howells #include <linux/circ_buf.h>
161da177e4SLinus Torvalds #include "internal.h"
1708e0e7c8SDavid Howells #include "afs_fs.h"
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /*
206db3ac3cSDavid Howells  * We need somewhere to discard into in case the server helpfully returns more
216db3ac3cSDavid Howells  * than we asked for in FS.FetchData{,64}.
226db3ac3cSDavid Howells  */
236db3ac3cSDavid Howells static u8 afs_discard_buffer[64];
246db3ac3cSDavid Howells 
256db3ac3cSDavid Howells /*
26260a9803SDavid Howells  * decode an AFSFid block
27260a9803SDavid Howells  */
28260a9803SDavid Howells static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
29260a9803SDavid Howells {
30260a9803SDavid Howells 	const __be32 *bp = *_bp;
31260a9803SDavid Howells 
32260a9803SDavid Howells 	fid->vid		= ntohl(*bp++);
33260a9803SDavid Howells 	fid->vnode		= ntohl(*bp++);
34260a9803SDavid Howells 	fid->unique		= ntohl(*bp++);
35260a9803SDavid Howells 	*_bp = bp;
36260a9803SDavid Howells }
37260a9803SDavid Howells 
38260a9803SDavid Howells /*
3908e0e7c8SDavid Howells  * decode an AFSFetchStatus block
401da177e4SLinus Torvalds  */
4108e0e7c8SDavid Howells static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
42260a9803SDavid Howells 				      struct afs_file_status *status,
4331143d5dSDavid Howells 				      struct afs_vnode *vnode,
4431143d5dSDavid Howells 				      afs_dataversion_t *store_version)
451da177e4SLinus Torvalds {
4631143d5dSDavid Howells 	afs_dataversion_t expected_version;
4708e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
4808e0e7c8SDavid Howells 	umode_t mode;
49260a9803SDavid Howells 	u64 data_version, size;
5008e0e7c8SDavid Howells 	u32 changed = 0; /* becomes non-zero if ctime-type changes seen */
51a0a5386aSEric W. Biederman 	kuid_t owner;
52a0a5386aSEric W. Biederman 	kgid_t group;
5308e0e7c8SDavid Howells 
5408e0e7c8SDavid Howells #define EXTRACT(DST)				\
5508e0e7c8SDavid Howells 	do {					\
5608e0e7c8SDavid Howells 		u32 x = ntohl(*bp++);		\
5708e0e7c8SDavid Howells 		changed |= DST - x;		\
5808e0e7c8SDavid Howells 		DST = x;			\
5908e0e7c8SDavid Howells 	} while (0)
6008e0e7c8SDavid Howells 
61260a9803SDavid Howells 	status->if_version = ntohl(*bp++);
62260a9803SDavid Howells 	EXTRACT(status->type);
63260a9803SDavid Howells 	EXTRACT(status->nlink);
64260a9803SDavid Howells 	size = ntohl(*bp++);
6508e0e7c8SDavid Howells 	data_version = ntohl(*bp++);
66260a9803SDavid Howells 	EXTRACT(status->author);
67a0a5386aSEric W. Biederman 	owner = make_kuid(&init_user_ns, ntohl(*bp++));
68a0a5386aSEric W. Biederman 	changed |= !uid_eq(owner, status->owner);
69a0a5386aSEric W. Biederman 	status->owner = owner;
70260a9803SDavid Howells 	EXTRACT(status->caller_access); /* call ticket dependent */
71260a9803SDavid Howells 	EXTRACT(status->anon_access);
72260a9803SDavid Howells 	EXTRACT(status->mode);
73260a9803SDavid Howells 	EXTRACT(status->parent.vnode);
74260a9803SDavid Howells 	EXTRACT(status->parent.unique);
7508e0e7c8SDavid Howells 	bp++; /* seg size */
76260a9803SDavid Howells 	status->mtime_client = ntohl(*bp++);
77260a9803SDavid Howells 	status->mtime_server = ntohl(*bp++);
78a0a5386aSEric W. Biederman 	group = make_kgid(&init_user_ns, ntohl(*bp++));
79a0a5386aSEric W. Biederman 	changed |= !gid_eq(group, status->group);
80a0a5386aSEric W. Biederman 	status->group = group;
8108e0e7c8SDavid Howells 	bp++; /* sync counter */
8208e0e7c8SDavid Howells 	data_version |= (u64) ntohl(*bp++) << 32;
83e8d6c554SDavid Howells 	EXTRACT(status->lock_count);
84260a9803SDavid Howells 	size |= (u64) ntohl(*bp++) << 32;
8508e0e7c8SDavid Howells 	bp++; /* spare 4 */
8608e0e7c8SDavid Howells 	*_bp = bp;
8708e0e7c8SDavid Howells 
88260a9803SDavid Howells 	if (size != status->size) {
89260a9803SDavid Howells 		status->size = size;
90260a9803SDavid Howells 		changed |= true;
91260a9803SDavid Howells 	}
92260a9803SDavid Howells 	status->mode &= S_IALLUGO;
9308e0e7c8SDavid Howells 
94260a9803SDavid Howells 	_debug("vnode time %lx, %lx",
95260a9803SDavid Howells 	       status->mtime_client, status->mtime_server);
96260a9803SDavid Howells 
97260a9803SDavid Howells 	if (vnode) {
98260a9803SDavid Howells 		status->parent.vid = vnode->fid.vid;
99260a9803SDavid Howells 		if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
100260a9803SDavid Howells 			_debug("vnode changed");
101260a9803SDavid Howells 			i_size_write(&vnode->vfs_inode, size);
102260a9803SDavid Howells 			vnode->vfs_inode.i_uid = status->owner;
103260a9803SDavid Howells 			vnode->vfs_inode.i_gid = status->group;
104d6e43f75SDavid Howells 			vnode->vfs_inode.i_generation = vnode->fid.unique;
105bfe86848SMiklos Szeredi 			set_nlink(&vnode->vfs_inode, status->nlink);
106260a9803SDavid Howells 
10708e0e7c8SDavid Howells 			mode = vnode->vfs_inode.i_mode;
10808e0e7c8SDavid Howells 			mode &= ~S_IALLUGO;
109260a9803SDavid Howells 			mode |= status->mode;
110260a9803SDavid Howells 			barrier();
11108e0e7c8SDavid Howells 			vnode->vfs_inode.i_mode = mode;
11208e0e7c8SDavid Howells 		}
11308e0e7c8SDavid Howells 
114ab94f5d0SMarc Dionne 		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_client;
11508e0e7c8SDavid Howells 		vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
11608e0e7c8SDavid Howells 		vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
117d6e43f75SDavid Howells 		vnode->vfs_inode.i_version	= data_version;
118260a9803SDavid Howells 	}
11908e0e7c8SDavid Howells 
12031143d5dSDavid Howells 	expected_version = status->data_version;
12131143d5dSDavid Howells 	if (store_version)
12231143d5dSDavid Howells 		expected_version = *store_version;
12331143d5dSDavid Howells 
12431143d5dSDavid Howells 	if (expected_version != data_version) {
125260a9803SDavid Howells 		status->data_version = data_version;
126260a9803SDavid Howells 		if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
127260a9803SDavid Howells 			_debug("vnode modified %llx on {%x:%u}",
128ba3e0e1aSDavid S. Miller 			       (unsigned long long) data_version,
129ba3e0e1aSDavid S. Miller 			       vnode->fid.vid, vnode->fid.vnode);
13008e0e7c8SDavid Howells 			set_bit(AFS_VNODE_MODIFIED, &vnode->flags);
13108e0e7c8SDavid Howells 			set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
1321da177e4SLinus Torvalds 		}
13331143d5dSDavid Howells 	} else if (store_version) {
13431143d5dSDavid Howells 		status->data_version = data_version;
135ec26815aSDavid Howells 	}
136260a9803SDavid Howells }
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds /*
13908e0e7c8SDavid Howells  * decode an AFSCallBack block
1401da177e4SLinus Torvalds  */
14108e0e7c8SDavid Howells static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
1421da177e4SLinus Torvalds {
14308e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
1441da177e4SLinus Torvalds 
14508e0e7c8SDavid Howells 	vnode->cb_version	= ntohl(*bp++);
14608e0e7c8SDavid Howells 	vnode->cb_expiry	= ntohl(*bp++);
14708e0e7c8SDavid Howells 	vnode->cb_type		= ntohl(*bp++);
14856e71431STina Ruchandani 	vnode->cb_expires	= vnode->cb_expiry + ktime_get_real_seconds();
14908e0e7c8SDavid Howells 	*_bp = bp;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
152260a9803SDavid Howells static void xdr_decode_AFSCallBack_raw(const __be32 **_bp,
153260a9803SDavid Howells 				       struct afs_callback *cb)
154260a9803SDavid Howells {
155260a9803SDavid Howells 	const __be32 *bp = *_bp;
156260a9803SDavid Howells 
157260a9803SDavid Howells 	cb->version	= ntohl(*bp++);
158260a9803SDavid Howells 	cb->expiry	= ntohl(*bp++);
159260a9803SDavid Howells 	cb->type	= ntohl(*bp++);
160260a9803SDavid Howells 	*_bp = bp;
161260a9803SDavid Howells }
162260a9803SDavid Howells 
1631da177e4SLinus Torvalds /*
16408e0e7c8SDavid Howells  * decode an AFSVolSync block
1651da177e4SLinus Torvalds  */
16608e0e7c8SDavid Howells static void xdr_decode_AFSVolSync(const __be32 **_bp,
16708e0e7c8SDavid Howells 				  struct afs_volsync *volsync)
1681da177e4SLinus Torvalds {
16908e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
1701da177e4SLinus Torvalds 
17108e0e7c8SDavid Howells 	volsync->creation = ntohl(*bp++);
17208e0e7c8SDavid Howells 	bp++; /* spare2 */
17308e0e7c8SDavid Howells 	bp++; /* spare3 */
17408e0e7c8SDavid Howells 	bp++; /* spare4 */
17508e0e7c8SDavid Howells 	bp++; /* spare5 */
17608e0e7c8SDavid Howells 	bp++; /* spare6 */
17708e0e7c8SDavid Howells 	*_bp = bp;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
18008e0e7c8SDavid Howells /*
18131143d5dSDavid Howells  * encode the requested attributes into an AFSStoreStatus block
18231143d5dSDavid Howells  */
18331143d5dSDavid Howells static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr)
18431143d5dSDavid Howells {
18531143d5dSDavid Howells 	__be32 *bp = *_bp;
18631143d5dSDavid Howells 	u32 mask = 0, mtime = 0, owner = 0, group = 0, mode = 0;
18731143d5dSDavid Howells 
18831143d5dSDavid Howells 	mask = 0;
18931143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MTIME) {
19031143d5dSDavid Howells 		mask |= AFS_SET_MTIME;
19131143d5dSDavid Howells 		mtime = attr->ia_mtime.tv_sec;
19231143d5dSDavid Howells 	}
19331143d5dSDavid Howells 
19431143d5dSDavid Howells 	if (attr->ia_valid & ATTR_UID) {
19531143d5dSDavid Howells 		mask |= AFS_SET_OWNER;
196a0a5386aSEric W. Biederman 		owner = from_kuid(&init_user_ns, attr->ia_uid);
19731143d5dSDavid Howells 	}
19831143d5dSDavid Howells 
19931143d5dSDavid Howells 	if (attr->ia_valid & ATTR_GID) {
20031143d5dSDavid Howells 		mask |= AFS_SET_GROUP;
201a0a5386aSEric W. Biederman 		group = from_kgid(&init_user_ns, attr->ia_gid);
20231143d5dSDavid Howells 	}
20331143d5dSDavid Howells 
20431143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MODE) {
20531143d5dSDavid Howells 		mask |= AFS_SET_MODE;
20631143d5dSDavid Howells 		mode = attr->ia_mode & S_IALLUGO;
20731143d5dSDavid Howells 	}
20831143d5dSDavid Howells 
20931143d5dSDavid Howells 	*bp++ = htonl(mask);
21031143d5dSDavid Howells 	*bp++ = htonl(mtime);
21131143d5dSDavid Howells 	*bp++ = htonl(owner);
21231143d5dSDavid Howells 	*bp++ = htonl(group);
21331143d5dSDavid Howells 	*bp++ = htonl(mode);
21431143d5dSDavid Howells 	*bp++ = 0;		/* segment size */
21531143d5dSDavid Howells 	*_bp = bp;
21631143d5dSDavid Howells }
21731143d5dSDavid Howells 
21831143d5dSDavid Howells /*
21945222b9eSDavid Howells  * decode an AFSFetchVolumeStatus block
22045222b9eSDavid Howells  */
22145222b9eSDavid Howells static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp,
22245222b9eSDavid Howells 					    struct afs_volume_status *vs)
22345222b9eSDavid Howells {
22445222b9eSDavid Howells 	const __be32 *bp = *_bp;
22545222b9eSDavid Howells 
22645222b9eSDavid Howells 	vs->vid			= ntohl(*bp++);
22745222b9eSDavid Howells 	vs->parent_id		= ntohl(*bp++);
22845222b9eSDavid Howells 	vs->online		= ntohl(*bp++);
22945222b9eSDavid Howells 	vs->in_service		= ntohl(*bp++);
23045222b9eSDavid Howells 	vs->blessed		= ntohl(*bp++);
23145222b9eSDavid Howells 	vs->needs_salvage	= ntohl(*bp++);
23245222b9eSDavid Howells 	vs->type		= ntohl(*bp++);
23345222b9eSDavid Howells 	vs->min_quota		= ntohl(*bp++);
23445222b9eSDavid Howells 	vs->max_quota		= ntohl(*bp++);
23545222b9eSDavid Howells 	vs->blocks_in_use	= ntohl(*bp++);
23645222b9eSDavid Howells 	vs->part_blocks_avail	= ntohl(*bp++);
23745222b9eSDavid Howells 	vs->part_max_blocks	= ntohl(*bp++);
23845222b9eSDavid Howells 	*_bp = bp;
23945222b9eSDavid Howells }
24045222b9eSDavid Howells 
24145222b9eSDavid Howells /*
24208e0e7c8SDavid Howells  * deliver reply data to an FS.FetchStatus
24308e0e7c8SDavid Howells  */
244d001648eSDavid Howells static int afs_deliver_fs_fetch_status(struct afs_call *call)
24508e0e7c8SDavid Howells {
246260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
24708e0e7c8SDavid Howells 	const __be32 *bp;
248372ee163SDavid Howells 	int ret;
2491da177e4SLinus Torvalds 
250d001648eSDavid Howells 	_enter("");
2511da177e4SLinus Torvalds 
252d001648eSDavid Howells 	ret = afs_transfer_reply(call);
253372ee163SDavid Howells 	if (ret < 0)
254372ee163SDavid Howells 		return ret;
2551da177e4SLinus Torvalds 
25608e0e7c8SDavid Howells 	/* unmarshall the reply once we've received all of it */
25708e0e7c8SDavid Howells 	bp = call->buffer;
25831143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
259260a9803SDavid Howells 	xdr_decode_AFSCallBack(&bp, vnode);
26008e0e7c8SDavid Howells 	if (call->reply2)
26108e0e7c8SDavid Howells 		xdr_decode_AFSVolSync(&bp, call->reply2);
2621da177e4SLinus Torvalds 
26308e0e7c8SDavid Howells 	_leave(" = 0 [done]");
26408e0e7c8SDavid Howells 	return 0;
265ec26815aSDavid Howells }
26608e0e7c8SDavid Howells 
26708e0e7c8SDavid Howells /*
26808e0e7c8SDavid Howells  * FS.FetchStatus operation type
26908e0e7c8SDavid Howells  */
27008e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchStatus = {
27100d3b7a4SDavid Howells 	.name		= "FS.FetchStatus",
27208e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_status,
27308e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
27408e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
27508e0e7c8SDavid Howells };
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds /*
2781da177e4SLinus Torvalds  * fetch the status information for a file
2791da177e4SLinus Torvalds  */
28008e0e7c8SDavid Howells int afs_fs_fetch_file_status(struct afs_server *server,
28100d3b7a4SDavid Howells 			     struct key *key,
2821da177e4SLinus Torvalds 			     struct afs_vnode *vnode,
28308e0e7c8SDavid Howells 			     struct afs_volsync *volsync,
28456ff9c83SDavid Howells 			     bool async)
2851da177e4SLinus Torvalds {
28608e0e7c8SDavid Howells 	struct afs_call *call;
287f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
2881da177e4SLinus Torvalds 	__be32 *bp;
2891da177e4SLinus Torvalds 
290416351f2SDavid Howells 	_enter(",%x,{%x:%u},,",
291260a9803SDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
2921da177e4SLinus Torvalds 
293f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
29408e0e7c8SDavid Howells 	if (!call)
29508e0e7c8SDavid Howells 		return -ENOMEM;
2961da177e4SLinus Torvalds 
29700d3b7a4SDavid Howells 	call->key = key;
29808e0e7c8SDavid Howells 	call->reply = vnode;
29908e0e7c8SDavid Howells 	call->reply2 = volsync;
30008e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
30108e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	/* marshall the parameters */
30408e0e7c8SDavid Howells 	bp = call->request;
3051da177e4SLinus Torvalds 	bp[0] = htonl(FSFETCHSTATUS);
3061da177e4SLinus Torvalds 	bp[1] = htonl(vnode->fid.vid);
3071da177e4SLinus Torvalds 	bp[2] = htonl(vnode->fid.vnode);
3081da177e4SLinus Torvalds 	bp[3] = htonl(vnode->fid.unique);
3091da177e4SLinus Torvalds 
31056ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
311ec26815aSDavid Howells }
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds /*
31408e0e7c8SDavid Howells  * deliver reply data to an FS.FetchData
3151da177e4SLinus Torvalds  */
316d001648eSDavid Howells static int afs_deliver_fs_fetch_data(struct afs_call *call)
3171da177e4SLinus Torvalds {
318260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
319196ee9cdSDavid Howells 	struct afs_read *req = call->reply3;
32008e0e7c8SDavid Howells 	const __be32 *bp;
321196ee9cdSDavid Howells 	unsigned int size;
32208e0e7c8SDavid Howells 	void *buffer;
3231da177e4SLinus Torvalds 	int ret;
3241da177e4SLinus Torvalds 
3256a0e3999SDavid Howells 	_enter("{%u,%zu/%u;%llu/%llu}",
326196ee9cdSDavid Howells 	       call->unmarshall, call->offset, call->count,
327196ee9cdSDavid Howells 	       req->remain, req->actual_len);
3281da177e4SLinus Torvalds 
32908e0e7c8SDavid Howells 	switch (call->unmarshall) {
33008e0e7c8SDavid Howells 	case 0:
331196ee9cdSDavid Howells 		req->actual_len = 0;
33208e0e7c8SDavid Howells 		call->offset = 0;
33308e0e7c8SDavid Howells 		call->unmarshall++;
334b9b1f8d5SDavid Howells 		if (call->operation_ID != FSFETCHDATA64) {
335b9b1f8d5SDavid Howells 			call->unmarshall++;
336b9b1f8d5SDavid Howells 			goto no_msw;
337b9b1f8d5SDavid Howells 		}
3381da177e4SLinus Torvalds 
339b9b1f8d5SDavid Howells 		/* extract the upper part of the returned data length of an
340b9b1f8d5SDavid Howells 		 * FSFETCHDATA64 op (which should always be 0 using this
341b9b1f8d5SDavid Howells 		 * client) */
34208e0e7c8SDavid Howells 	case 1:
343b9b1f8d5SDavid Howells 		_debug("extract data length (MSW)");
344d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
345372ee163SDavid Howells 		if (ret < 0)
346372ee163SDavid Howells 			return ret;
347b9b1f8d5SDavid Howells 
348196ee9cdSDavid Howells 		req->actual_len = ntohl(call->tmp);
349196ee9cdSDavid Howells 		req->actual_len <<= 32;
350b9b1f8d5SDavid Howells 		call->offset = 0;
351b9b1f8d5SDavid Howells 		call->unmarshall++;
352b9b1f8d5SDavid Howells 
353b9b1f8d5SDavid Howells 	no_msw:
354b9b1f8d5SDavid Howells 		/* extract the returned data length */
355b9b1f8d5SDavid Howells 	case 2:
35608e0e7c8SDavid Howells 		_debug("extract data length");
357d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
358372ee163SDavid Howells 		if (ret < 0)
359372ee163SDavid Howells 			return ret;
3601da177e4SLinus Torvalds 
361196ee9cdSDavid Howells 		req->actual_len |= ntohl(call->tmp);
362196ee9cdSDavid Howells 		_debug("DATA length: %llu", req->actual_len);
363196ee9cdSDavid Howells 
364196ee9cdSDavid Howells 		req->remain = req->actual_len;
365196ee9cdSDavid Howells 		call->offset = req->pos & (PAGE_SIZE - 1);
366196ee9cdSDavid Howells 		req->index = 0;
367196ee9cdSDavid Howells 		if (req->actual_len == 0)
368196ee9cdSDavid Howells 			goto no_more_data;
36908e0e7c8SDavid Howells 		call->unmarshall++;
3701da177e4SLinus Torvalds 
371196ee9cdSDavid Howells 	begin_page:
3726db3ac3cSDavid Howells 		ASSERTCMP(req->index, <, req->nr_pages);
373196ee9cdSDavid Howells 		if (req->remain > PAGE_SIZE - call->offset)
374196ee9cdSDavid Howells 			size = PAGE_SIZE - call->offset;
375196ee9cdSDavid Howells 		else
376196ee9cdSDavid Howells 			size = req->remain;
377196ee9cdSDavid Howells 		call->count = call->offset + size;
378196ee9cdSDavid Howells 		ASSERTCMP(call->count, <=, PAGE_SIZE);
379196ee9cdSDavid Howells 		req->remain -= size;
380196ee9cdSDavid Howells 
38108e0e7c8SDavid Howells 		/* extract the returned data */
382b9b1f8d5SDavid Howells 	case 3:
3836a0e3999SDavid Howells 		_debug("extract data %llu/%llu %zu/%u",
384196ee9cdSDavid Howells 		       req->remain, req->actual_len, call->offset, call->count);
385196ee9cdSDavid Howells 
386196ee9cdSDavid Howells 		buffer = kmap(req->pages[req->index]);
387196ee9cdSDavid Howells 		ret = afs_extract_data(call, buffer, call->count, true);
388196ee9cdSDavid Howells 		kunmap(req->pages[req->index]);
389372ee163SDavid Howells 		if (ret < 0)
390372ee163SDavid Howells 			return ret;
391196ee9cdSDavid Howells 		if (call->offset == PAGE_SIZE) {
392196ee9cdSDavid Howells 			if (req->page_done)
393196ee9cdSDavid Howells 				req->page_done(call, req);
39429f06985SDavid Howells 			req->index++;
395196ee9cdSDavid Howells 			if (req->remain > 0) {
396196ee9cdSDavid Howells 				call->offset = 0;
397e8e581a8SDavid Howells 				if (req->index >= req->nr_pages) {
398e8e581a8SDavid Howells 					call->unmarshall = 4;
3996db3ac3cSDavid Howells 					goto begin_discard;
400e8e581a8SDavid Howells 				}
401196ee9cdSDavid Howells 				goto begin_page;
402196ee9cdSDavid Howells 			}
403416351f2SDavid Howells 		}
4046db3ac3cSDavid Howells 		goto no_more_data;
4056db3ac3cSDavid Howells 
4066db3ac3cSDavid Howells 		/* Discard any excess data the server gave us */
4076db3ac3cSDavid Howells 	begin_discard:
4086db3ac3cSDavid Howells 	case 4:
4096a0e3999SDavid Howells 		size = min_t(loff_t, sizeof(afs_discard_buffer), req->remain);
4106db3ac3cSDavid Howells 		call->count = size;
4116a0e3999SDavid Howells 		_debug("extract discard %llu/%llu %zu/%u",
4126db3ac3cSDavid Howells 		       req->remain, req->actual_len, call->offset, call->count);
4136db3ac3cSDavid Howells 
4146db3ac3cSDavid Howells 		call->offset = 0;
4156db3ac3cSDavid Howells 		ret = afs_extract_data(call, afs_discard_buffer, call->count, true);
4166db3ac3cSDavid Howells 		req->remain -= call->offset;
4176db3ac3cSDavid Howells 		if (ret < 0)
4186db3ac3cSDavid Howells 			return ret;
4196db3ac3cSDavid Howells 		if (req->remain > 0)
4206db3ac3cSDavid Howells 			goto begin_discard;
4211da177e4SLinus Torvalds 
422196ee9cdSDavid Howells 	no_more_data:
42308e0e7c8SDavid Howells 		call->offset = 0;
4246db3ac3cSDavid Howells 		call->unmarshall = 5;
42508e0e7c8SDavid Howells 
42608e0e7c8SDavid Howells 		/* extract the metadata */
4276db3ac3cSDavid Howells 	case 5:
428d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
429d001648eSDavid Howells 				       (21 + 3 + 6) * 4, false);
430372ee163SDavid Howells 		if (ret < 0)
431372ee163SDavid Howells 			return ret;
4321da177e4SLinus Torvalds 
43308e0e7c8SDavid Howells 		bp = call->buffer;
43431143d5dSDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
435260a9803SDavid Howells 		xdr_decode_AFSCallBack(&bp, vnode);
43608e0e7c8SDavid Howells 		if (call->reply2)
43708e0e7c8SDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply2);
4381da177e4SLinus Torvalds 
43908e0e7c8SDavid Howells 		call->offset = 0;
44008e0e7c8SDavid Howells 		call->unmarshall++;
4411da177e4SLinus Torvalds 
4426db3ac3cSDavid Howells 	case 6:
4431da177e4SLinus Torvalds 		break;
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds 
4466db3ac3cSDavid Howells 	for (; req->index < req->nr_pages; req->index++) {
4476db3ac3cSDavid Howells 		if (call->count < PAGE_SIZE)
4486db3ac3cSDavid Howells 			zero_user_segment(req->pages[req->index],
4496db3ac3cSDavid Howells 					  call->count, PAGE_SIZE);
450196ee9cdSDavid Howells 		if (req->page_done)
451196ee9cdSDavid Howells 			req->page_done(call, req);
4526db3ac3cSDavid Howells 		call->count = 0;
453416351f2SDavid Howells 	}
454416351f2SDavid Howells 
45508e0e7c8SDavid Howells 	_leave(" = 0 [done]");
45608e0e7c8SDavid Howells 	return 0;
457ec26815aSDavid Howells }
4581da177e4SLinus Torvalds 
459196ee9cdSDavid Howells static void afs_fetch_data_destructor(struct afs_call *call)
460196ee9cdSDavid Howells {
461196ee9cdSDavid Howells 	struct afs_read *req = call->reply3;
462196ee9cdSDavid Howells 
463196ee9cdSDavid Howells 	afs_put_read(req);
464196ee9cdSDavid Howells 	afs_flat_call_destructor(call);
465196ee9cdSDavid Howells }
466196ee9cdSDavid Howells 
4671da177e4SLinus Torvalds /*
46808e0e7c8SDavid Howells  * FS.FetchData operation type
4691da177e4SLinus Torvalds  */
47008e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchData = {
47100d3b7a4SDavid Howells 	.name		= "FS.FetchData",
47208e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
47308e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
474196ee9cdSDavid Howells 	.destructor	= afs_fetch_data_destructor,
47508e0e7c8SDavid Howells };
47608e0e7c8SDavid Howells 
477b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSFetchData64 = {
478b9b1f8d5SDavid Howells 	.name		= "FS.FetchData64",
479b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
480b9b1f8d5SDavid Howells 	.abort_to_error	= afs_abort_to_error,
481196ee9cdSDavid Howells 	.destructor	= afs_fetch_data_destructor,
482b9b1f8d5SDavid Howells };
483b9b1f8d5SDavid Howells 
484b9b1f8d5SDavid Howells /*
485b9b1f8d5SDavid Howells  * fetch data from a very large file
486b9b1f8d5SDavid Howells  */
487b9b1f8d5SDavid Howells static int afs_fs_fetch_data64(struct afs_server *server,
488b9b1f8d5SDavid Howells 			       struct key *key,
489b9b1f8d5SDavid Howells 			       struct afs_vnode *vnode,
490196ee9cdSDavid Howells 			       struct afs_read *req,
49156ff9c83SDavid Howells 			       bool async)
492b9b1f8d5SDavid Howells {
493b9b1f8d5SDavid Howells 	struct afs_call *call;
494f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
495b9b1f8d5SDavid Howells 	__be32 *bp;
496b9b1f8d5SDavid Howells 
497b9b1f8d5SDavid Howells 	_enter("");
498b9b1f8d5SDavid Howells 
499f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
500b9b1f8d5SDavid Howells 	if (!call)
501b9b1f8d5SDavid Howells 		return -ENOMEM;
502b9b1f8d5SDavid Howells 
503b9b1f8d5SDavid Howells 	call->key = key;
504b9b1f8d5SDavid Howells 	call->reply = vnode;
505b9b1f8d5SDavid Howells 	call->reply2 = NULL; /* volsync */
506196ee9cdSDavid Howells 	call->reply3 = req;
507b9b1f8d5SDavid Howells 	call->service_id = FS_SERVICE;
508b9b1f8d5SDavid Howells 	call->port = htons(AFS_FS_PORT);
509b9b1f8d5SDavid Howells 	call->operation_ID = FSFETCHDATA64;
510b9b1f8d5SDavid Howells 
511b9b1f8d5SDavid Howells 	/* marshall the parameters */
512b9b1f8d5SDavid Howells 	bp = call->request;
513b9b1f8d5SDavid Howells 	bp[0] = htonl(FSFETCHDATA64);
514b9b1f8d5SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
515b9b1f8d5SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
516b9b1f8d5SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
517196ee9cdSDavid Howells 	bp[4] = htonl(upper_32_bits(req->pos));
518196ee9cdSDavid Howells 	bp[5] = htonl(lower_32_bits(req->pos));
519b9b1f8d5SDavid Howells 	bp[6] = 0;
520196ee9cdSDavid Howells 	bp[7] = htonl(lower_32_bits(req->len));
521b9b1f8d5SDavid Howells 
522196ee9cdSDavid Howells 	atomic_inc(&req->usage);
52356ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
524b9b1f8d5SDavid Howells }
525b9b1f8d5SDavid Howells 
52608e0e7c8SDavid Howells /*
52708e0e7c8SDavid Howells  * fetch data from a file
52808e0e7c8SDavid Howells  */
52908e0e7c8SDavid Howells int afs_fs_fetch_data(struct afs_server *server,
53000d3b7a4SDavid Howells 		      struct key *key,
5311da177e4SLinus Torvalds 		      struct afs_vnode *vnode,
532196ee9cdSDavid Howells 		      struct afs_read *req,
53356ff9c83SDavid Howells 		      bool async)
5341da177e4SLinus Torvalds {
53508e0e7c8SDavid Howells 	struct afs_call *call;
536f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
53708e0e7c8SDavid Howells 	__be32 *bp;
5381da177e4SLinus Torvalds 
539196ee9cdSDavid Howells 	if (upper_32_bits(req->pos) ||
540196ee9cdSDavid Howells 	    upper_32_bits(req->len) ||
541196ee9cdSDavid Howells 	    upper_32_bits(req->pos + req->len))
54256ff9c83SDavid Howells 		return afs_fs_fetch_data64(server, key, vnode, req, async);
543b9b1f8d5SDavid Howells 
54408e0e7c8SDavid Howells 	_enter("");
5451da177e4SLinus Torvalds 
546f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
54708e0e7c8SDavid Howells 	if (!call)
54808e0e7c8SDavid Howells 		return -ENOMEM;
5491da177e4SLinus Torvalds 
55000d3b7a4SDavid Howells 	call->key = key;
55108e0e7c8SDavid Howells 	call->reply = vnode;
552260a9803SDavid Howells 	call->reply2 = NULL; /* volsync */
553196ee9cdSDavid Howells 	call->reply3 = req;
55408e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
55508e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
556b9b1f8d5SDavid Howells 	call->operation_ID = FSFETCHDATA;
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	/* marshall the parameters */
55908e0e7c8SDavid Howells 	bp = call->request;
56008e0e7c8SDavid Howells 	bp[0] = htonl(FSFETCHDATA);
56108e0e7c8SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
56208e0e7c8SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
56308e0e7c8SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
564196ee9cdSDavid Howells 	bp[4] = htonl(lower_32_bits(req->pos));
565196ee9cdSDavid Howells 	bp[5] = htonl(lower_32_bits(req->len));
5661da177e4SLinus Torvalds 
567196ee9cdSDavid Howells 	atomic_inc(&req->usage);
56856ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
57108e0e7c8SDavid Howells /*
57208e0e7c8SDavid Howells  * deliver reply data to an FS.GiveUpCallBacks
57308e0e7c8SDavid Howells  */
574d001648eSDavid Howells static int afs_deliver_fs_give_up_callbacks(struct afs_call *call)
57508e0e7c8SDavid Howells {
576d001648eSDavid Howells 	_enter("");
5771da177e4SLinus Torvalds 
578372ee163SDavid Howells 	/* shouldn't be any reply data */
579d001648eSDavid Howells 	return afs_extract_data(call, NULL, 0, false);
5801da177e4SLinus Torvalds }
5811da177e4SLinus Torvalds 
58208e0e7c8SDavid Howells /*
58308e0e7c8SDavid Howells  * FS.GiveUpCallBacks operation type
58408e0e7c8SDavid Howells  */
58508e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
58600d3b7a4SDavid Howells 	.name		= "FS.GiveUpCallBacks",
58708e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_give_up_callbacks,
58808e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
58908e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
59008e0e7c8SDavid Howells };
5911da177e4SLinus Torvalds 
59208e0e7c8SDavid Howells /*
59308e0e7c8SDavid Howells  * give up a set of callbacks
59408e0e7c8SDavid Howells  * - the callbacks are held in the server->cb_break ring
59508e0e7c8SDavid Howells  */
596f044c884SDavid Howells int afs_fs_give_up_callbacks(struct afs_net *net,
597f044c884SDavid Howells 			     struct afs_server *server,
59856ff9c83SDavid Howells 			     bool async)
59908e0e7c8SDavid Howells {
60008e0e7c8SDavid Howells 	struct afs_call *call;
60108e0e7c8SDavid Howells 	size_t ncallbacks;
60208e0e7c8SDavid Howells 	__be32 *bp, *tp;
60308e0e7c8SDavid Howells 	int loop;
6041da177e4SLinus Torvalds 
60508e0e7c8SDavid Howells 	ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
60608e0e7c8SDavid Howells 			      ARRAY_SIZE(server->cb_break));
60708e0e7c8SDavid Howells 
60808e0e7c8SDavid Howells 	_enter("{%zu},", ncallbacks);
60908e0e7c8SDavid Howells 
61008e0e7c8SDavid Howells 	if (ncallbacks == 0)
61108e0e7c8SDavid Howells 		return 0;
61208e0e7c8SDavid Howells 	if (ncallbacks > AFSCBMAX)
61308e0e7c8SDavid Howells 		ncallbacks = AFSCBMAX;
61408e0e7c8SDavid Howells 
61508e0e7c8SDavid Howells 	_debug("break %zu callbacks", ncallbacks);
61608e0e7c8SDavid Howells 
617f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGiveUpCallBacks,
61808e0e7c8SDavid Howells 				   12 + ncallbacks * 6 * 4, 0);
61908e0e7c8SDavid Howells 	if (!call)
62008e0e7c8SDavid Howells 		return -ENOMEM;
62108e0e7c8SDavid Howells 
62208e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
62308e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
62408e0e7c8SDavid Howells 
62508e0e7c8SDavid Howells 	/* marshall the parameters */
62608e0e7c8SDavid Howells 	bp = call->request;
62708e0e7c8SDavid Howells 	tp = bp + 2 + ncallbacks * 3;
62808e0e7c8SDavid Howells 	*bp++ = htonl(FSGIVEUPCALLBACKS);
62908e0e7c8SDavid Howells 	*bp++ = htonl(ncallbacks);
63008e0e7c8SDavid Howells 	*tp++ = htonl(ncallbacks);
63108e0e7c8SDavid Howells 
63208e0e7c8SDavid Howells 	atomic_sub(ncallbacks, &server->cb_break_n);
63308e0e7c8SDavid Howells 	for (loop = ncallbacks; loop > 0; loop--) {
63408e0e7c8SDavid Howells 		struct afs_callback *cb =
63508e0e7c8SDavid Howells 			&server->cb_break[server->cb_break_tail];
63608e0e7c8SDavid Howells 
63708e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vid);
63808e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vnode);
63908e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.unique);
64008e0e7c8SDavid Howells 		*tp++ = htonl(cb->version);
64108e0e7c8SDavid Howells 		*tp++ = htonl(cb->expiry);
64208e0e7c8SDavid Howells 		*tp++ = htonl(cb->type);
64308e0e7c8SDavid Howells 		smp_mb();
64408e0e7c8SDavid Howells 		server->cb_break_tail =
64508e0e7c8SDavid Howells 			(server->cb_break_tail + 1) &
64608e0e7c8SDavid Howells 			(ARRAY_SIZE(server->cb_break) - 1);
647ec26815aSDavid Howells 	}
64808e0e7c8SDavid Howells 
64908e0e7c8SDavid Howells 	ASSERT(ncallbacks > 0);
65008e0e7c8SDavid Howells 	wake_up_nr(&server->cb_break_waitq, ncallbacks);
65108e0e7c8SDavid Howells 
65256ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
65308e0e7c8SDavid Howells }
654260a9803SDavid Howells 
655260a9803SDavid Howells /*
656260a9803SDavid Howells  * deliver reply data to an FS.CreateFile or an FS.MakeDir
657260a9803SDavid Howells  */
658d001648eSDavid Howells static int afs_deliver_fs_create_vnode(struct afs_call *call)
659260a9803SDavid Howells {
660260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
661260a9803SDavid Howells 	const __be32 *bp;
662372ee163SDavid Howells 	int ret;
663260a9803SDavid Howells 
664d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
665260a9803SDavid Howells 
666d001648eSDavid Howells 	ret = afs_transfer_reply(call);
667372ee163SDavid Howells 	if (ret < 0)
668372ee163SDavid Howells 		return ret;
669260a9803SDavid Howells 
670260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
671260a9803SDavid Howells 	bp = call->buffer;
672260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
67331143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL);
67431143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
675260a9803SDavid Howells 	xdr_decode_AFSCallBack_raw(&bp, call->reply4);
676260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
677260a9803SDavid Howells 
678260a9803SDavid Howells 	_leave(" = 0 [done]");
679260a9803SDavid Howells 	return 0;
680260a9803SDavid Howells }
681260a9803SDavid Howells 
682260a9803SDavid Howells /*
683260a9803SDavid Howells  * FS.CreateFile and FS.MakeDir operation type
684260a9803SDavid Howells  */
685260a9803SDavid Howells static const struct afs_call_type afs_RXFSCreateXXXX = {
686260a9803SDavid Howells 	.name		= "FS.CreateXXXX",
687260a9803SDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
688260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
689260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
690260a9803SDavid Howells };
691260a9803SDavid Howells 
692260a9803SDavid Howells /*
693260a9803SDavid Howells  * create a file or make a directory
694260a9803SDavid Howells  */
695260a9803SDavid Howells int afs_fs_create(struct afs_server *server,
696260a9803SDavid Howells 		  struct key *key,
697260a9803SDavid Howells 		  struct afs_vnode *vnode,
698260a9803SDavid Howells 		  const char *name,
699260a9803SDavid Howells 		  umode_t mode,
700260a9803SDavid Howells 		  struct afs_fid *newfid,
701260a9803SDavid Howells 		  struct afs_file_status *newstatus,
702260a9803SDavid Howells 		  struct afs_callback *newcb,
70356ff9c83SDavid Howells 		  bool async)
704260a9803SDavid Howells {
705260a9803SDavid Howells 	struct afs_call *call;
706f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
707260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
708260a9803SDavid Howells 	__be32 *bp;
709260a9803SDavid Howells 
710260a9803SDavid Howells 	_enter("");
711260a9803SDavid Howells 
712260a9803SDavid Howells 	namesz = strlen(name);
713260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
714260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (6 * 4);
715260a9803SDavid Howells 
716f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSCreateXXXX, reqsz,
717260a9803SDavid Howells 				   (3 + 21 + 21 + 3 + 6) * 4);
718260a9803SDavid Howells 	if (!call)
719260a9803SDavid Howells 		return -ENOMEM;
720260a9803SDavid Howells 
721260a9803SDavid Howells 	call->key = key;
722260a9803SDavid Howells 	call->reply = vnode;
723260a9803SDavid Howells 	call->reply2 = newfid;
724260a9803SDavid Howells 	call->reply3 = newstatus;
725260a9803SDavid Howells 	call->reply4 = newcb;
726260a9803SDavid Howells 	call->service_id = FS_SERVICE;
727260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
728260a9803SDavid Howells 
729260a9803SDavid Howells 	/* marshall the parameters */
730260a9803SDavid Howells 	bp = call->request;
731260a9803SDavid Howells 	*bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE);
732260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
733260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
734260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
735260a9803SDavid Howells 	*bp++ = htonl(namesz);
736260a9803SDavid Howells 	memcpy(bp, name, namesz);
737260a9803SDavid Howells 	bp = (void *) bp + namesz;
738260a9803SDavid Howells 	if (padsz > 0) {
739260a9803SDavid Howells 		memset(bp, 0, padsz);
740260a9803SDavid Howells 		bp = (void *) bp + padsz;
741260a9803SDavid Howells 	}
742ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
743ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
744260a9803SDavid Howells 	*bp++ = 0; /* owner */
745260a9803SDavid Howells 	*bp++ = 0; /* group */
746260a9803SDavid Howells 	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
747260a9803SDavid Howells 	*bp++ = 0; /* segment size */
748260a9803SDavid Howells 
74956ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
750260a9803SDavid Howells }
751260a9803SDavid Howells 
752260a9803SDavid Howells /*
753260a9803SDavid Howells  * deliver reply data to an FS.RemoveFile or FS.RemoveDir
754260a9803SDavid Howells  */
755d001648eSDavid Howells static int afs_deliver_fs_remove(struct afs_call *call)
756260a9803SDavid Howells {
757260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
758260a9803SDavid Howells 	const __be32 *bp;
759372ee163SDavid Howells 	int ret;
760260a9803SDavid Howells 
761d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
762260a9803SDavid Howells 
763d001648eSDavid Howells 	ret = afs_transfer_reply(call);
764372ee163SDavid Howells 	if (ret < 0)
765372ee163SDavid Howells 		return ret;
766260a9803SDavid Howells 
767260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
768260a9803SDavid Howells 	bp = call->buffer;
76931143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
770260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
771260a9803SDavid Howells 
772260a9803SDavid Howells 	_leave(" = 0 [done]");
773260a9803SDavid Howells 	return 0;
774260a9803SDavid Howells }
775260a9803SDavid Howells 
776260a9803SDavid Howells /*
777260a9803SDavid Howells  * FS.RemoveDir/FS.RemoveFile operation type
778260a9803SDavid Howells  */
779260a9803SDavid Howells static const struct afs_call_type afs_RXFSRemoveXXXX = {
780260a9803SDavid Howells 	.name		= "FS.RemoveXXXX",
781260a9803SDavid Howells 	.deliver	= afs_deliver_fs_remove,
782260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
783260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
784260a9803SDavid Howells };
785260a9803SDavid Howells 
786260a9803SDavid Howells /*
787260a9803SDavid Howells  * remove a file or directory
788260a9803SDavid Howells  */
789260a9803SDavid Howells int afs_fs_remove(struct afs_server *server,
790260a9803SDavid Howells 		  struct key *key,
791260a9803SDavid Howells 		  struct afs_vnode *vnode,
792260a9803SDavid Howells 		  const char *name,
793260a9803SDavid Howells 		  bool isdir,
79456ff9c83SDavid Howells 		  bool async)
795260a9803SDavid Howells {
796260a9803SDavid Howells 	struct afs_call *call;
797f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
798260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
799260a9803SDavid Howells 	__be32 *bp;
800260a9803SDavid Howells 
801260a9803SDavid Howells 	_enter("");
802260a9803SDavid Howells 
803260a9803SDavid Howells 	namesz = strlen(name);
804260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
805260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz;
806260a9803SDavid Howells 
807f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4);
808260a9803SDavid Howells 	if (!call)
809260a9803SDavid Howells 		return -ENOMEM;
810260a9803SDavid Howells 
811260a9803SDavid Howells 	call->key = key;
812260a9803SDavid Howells 	call->reply = vnode;
813260a9803SDavid Howells 	call->service_id = FS_SERVICE;
814260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
815260a9803SDavid Howells 
816260a9803SDavid Howells 	/* marshall the parameters */
817260a9803SDavid Howells 	bp = call->request;
818260a9803SDavid Howells 	*bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE);
819260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
820260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
821260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
822260a9803SDavid Howells 	*bp++ = htonl(namesz);
823260a9803SDavid Howells 	memcpy(bp, name, namesz);
824260a9803SDavid Howells 	bp = (void *) bp + namesz;
825260a9803SDavid Howells 	if (padsz > 0) {
826260a9803SDavid Howells 		memset(bp, 0, padsz);
827260a9803SDavid Howells 		bp = (void *) bp + padsz;
828260a9803SDavid Howells 	}
829260a9803SDavid Howells 
83056ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
831260a9803SDavid Howells }
832260a9803SDavid Howells 
833260a9803SDavid Howells /*
834260a9803SDavid Howells  * deliver reply data to an FS.Link
835260a9803SDavid Howells  */
836d001648eSDavid Howells static int afs_deliver_fs_link(struct afs_call *call)
837260a9803SDavid Howells {
838260a9803SDavid Howells 	struct afs_vnode *dvnode = call->reply, *vnode = call->reply2;
839260a9803SDavid Howells 	const __be32 *bp;
840372ee163SDavid Howells 	int ret;
841260a9803SDavid Howells 
842d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
843260a9803SDavid Howells 
844d001648eSDavid Howells 	ret = afs_transfer_reply(call);
845372ee163SDavid Howells 	if (ret < 0)
846372ee163SDavid Howells 		return ret;
847260a9803SDavid Howells 
848260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
849260a9803SDavid Howells 	bp = call->buffer;
85031143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
85131143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode, NULL);
852260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
853260a9803SDavid Howells 
854260a9803SDavid Howells 	_leave(" = 0 [done]");
855260a9803SDavid Howells 	return 0;
856260a9803SDavid Howells }
857260a9803SDavid Howells 
858260a9803SDavid Howells /*
859260a9803SDavid Howells  * FS.Link operation type
860260a9803SDavid Howells  */
861260a9803SDavid Howells static const struct afs_call_type afs_RXFSLink = {
862260a9803SDavid Howells 	.name		= "FS.Link",
863260a9803SDavid Howells 	.deliver	= afs_deliver_fs_link,
864260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
865260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
866260a9803SDavid Howells };
867260a9803SDavid Howells 
868260a9803SDavid Howells /*
869260a9803SDavid Howells  * make a hard link
870260a9803SDavid Howells  */
871260a9803SDavid Howells int afs_fs_link(struct afs_server *server,
872260a9803SDavid Howells 		struct key *key,
873260a9803SDavid Howells 		struct afs_vnode *dvnode,
874260a9803SDavid Howells 		struct afs_vnode *vnode,
875260a9803SDavid Howells 		const char *name,
87656ff9c83SDavid Howells 		bool async)
877260a9803SDavid Howells {
878260a9803SDavid Howells 	struct afs_call *call;
879f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
880260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
881260a9803SDavid Howells 	__be32 *bp;
882260a9803SDavid Howells 
883260a9803SDavid Howells 	_enter("");
884260a9803SDavid Howells 
885260a9803SDavid Howells 	namesz = strlen(name);
886260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
887260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (3 * 4);
888260a9803SDavid Howells 
889f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
890260a9803SDavid Howells 	if (!call)
891260a9803SDavid Howells 		return -ENOMEM;
892260a9803SDavid Howells 
893260a9803SDavid Howells 	call->key = key;
894260a9803SDavid Howells 	call->reply = dvnode;
895260a9803SDavid Howells 	call->reply2 = vnode;
896260a9803SDavid Howells 	call->service_id = FS_SERVICE;
897260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
898260a9803SDavid Howells 
899260a9803SDavid Howells 	/* marshall the parameters */
900260a9803SDavid Howells 	bp = call->request;
901260a9803SDavid Howells 	*bp++ = htonl(FSLINK);
902260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vid);
903260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vnode);
904260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.unique);
905260a9803SDavid Howells 	*bp++ = htonl(namesz);
906260a9803SDavid Howells 	memcpy(bp, name, namesz);
907260a9803SDavid Howells 	bp = (void *) bp + namesz;
908260a9803SDavid Howells 	if (padsz > 0) {
909260a9803SDavid Howells 		memset(bp, 0, padsz);
910260a9803SDavid Howells 		bp = (void *) bp + padsz;
911260a9803SDavid Howells 	}
912260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
913260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
914260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
915260a9803SDavid Howells 
91656ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
917260a9803SDavid Howells }
918260a9803SDavid Howells 
919260a9803SDavid Howells /*
920260a9803SDavid Howells  * deliver reply data to an FS.Symlink
921260a9803SDavid Howells  */
922d001648eSDavid Howells static int afs_deliver_fs_symlink(struct afs_call *call)
923260a9803SDavid Howells {
924260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
925260a9803SDavid Howells 	const __be32 *bp;
926372ee163SDavid Howells 	int ret;
927260a9803SDavid Howells 
928d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
929260a9803SDavid Howells 
930d001648eSDavid Howells 	ret = afs_transfer_reply(call);
931372ee163SDavid Howells 	if (ret < 0)
932372ee163SDavid Howells 		return ret;
933260a9803SDavid Howells 
934260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
935260a9803SDavid Howells 	bp = call->buffer;
936260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
93731143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL);
93831143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
939260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
940260a9803SDavid Howells 
941260a9803SDavid Howells 	_leave(" = 0 [done]");
942260a9803SDavid Howells 	return 0;
943260a9803SDavid Howells }
944260a9803SDavid Howells 
945260a9803SDavid Howells /*
946260a9803SDavid Howells  * FS.Symlink operation type
947260a9803SDavid Howells  */
948260a9803SDavid Howells static const struct afs_call_type afs_RXFSSymlink = {
949260a9803SDavid Howells 	.name		= "FS.Symlink",
950260a9803SDavid Howells 	.deliver	= afs_deliver_fs_symlink,
951260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
952260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
953260a9803SDavid Howells };
954260a9803SDavid Howells 
955260a9803SDavid Howells /*
956260a9803SDavid Howells  * create a symbolic link
957260a9803SDavid Howells  */
958260a9803SDavid Howells int afs_fs_symlink(struct afs_server *server,
959260a9803SDavid Howells 		   struct key *key,
960260a9803SDavid Howells 		   struct afs_vnode *vnode,
961260a9803SDavid Howells 		   const char *name,
962260a9803SDavid Howells 		   const char *contents,
963260a9803SDavid Howells 		   struct afs_fid *newfid,
964260a9803SDavid Howells 		   struct afs_file_status *newstatus,
96556ff9c83SDavid Howells 		   bool async)
966260a9803SDavid Howells {
967260a9803SDavid Howells 	struct afs_call *call;
968f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
969260a9803SDavid Howells 	size_t namesz, reqsz, padsz, c_namesz, c_padsz;
970260a9803SDavid Howells 	__be32 *bp;
971260a9803SDavid Howells 
972260a9803SDavid Howells 	_enter("");
973260a9803SDavid Howells 
974260a9803SDavid Howells 	namesz = strlen(name);
975260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
976260a9803SDavid Howells 
977260a9803SDavid Howells 	c_namesz = strlen(contents);
978260a9803SDavid Howells 	c_padsz = (4 - (c_namesz & 3)) & 3;
979260a9803SDavid Howells 
980260a9803SDavid Howells 	reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
981260a9803SDavid Howells 
982f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSSymlink, reqsz,
983260a9803SDavid Howells 				   (3 + 21 + 21 + 6) * 4);
984260a9803SDavid Howells 	if (!call)
985260a9803SDavid Howells 		return -ENOMEM;
986260a9803SDavid Howells 
987260a9803SDavid Howells 	call->key = key;
988260a9803SDavid Howells 	call->reply = vnode;
989260a9803SDavid Howells 	call->reply2 = newfid;
990260a9803SDavid Howells 	call->reply3 = newstatus;
991260a9803SDavid Howells 	call->service_id = FS_SERVICE;
992260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
993260a9803SDavid Howells 
994260a9803SDavid Howells 	/* marshall the parameters */
995260a9803SDavid Howells 	bp = call->request;
996260a9803SDavid Howells 	*bp++ = htonl(FSSYMLINK);
997260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
998260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
999260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1000260a9803SDavid Howells 	*bp++ = htonl(namesz);
1001260a9803SDavid Howells 	memcpy(bp, name, namesz);
1002260a9803SDavid Howells 	bp = (void *) bp + namesz;
1003260a9803SDavid Howells 	if (padsz > 0) {
1004260a9803SDavid Howells 		memset(bp, 0, padsz);
1005260a9803SDavid Howells 		bp = (void *) bp + padsz;
1006260a9803SDavid Howells 	}
1007260a9803SDavid Howells 	*bp++ = htonl(c_namesz);
1008260a9803SDavid Howells 	memcpy(bp, contents, c_namesz);
1009260a9803SDavid Howells 	bp = (void *) bp + c_namesz;
1010260a9803SDavid Howells 	if (c_padsz > 0) {
1011260a9803SDavid Howells 		memset(bp, 0, c_padsz);
1012260a9803SDavid Howells 		bp = (void *) bp + c_padsz;
1013260a9803SDavid Howells 	}
1014ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
1015ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
1016260a9803SDavid Howells 	*bp++ = 0; /* owner */
1017260a9803SDavid Howells 	*bp++ = 0; /* group */
1018260a9803SDavid Howells 	*bp++ = htonl(S_IRWXUGO); /* unix mode */
1019260a9803SDavid Howells 	*bp++ = 0; /* segment size */
1020260a9803SDavid Howells 
102156ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
1022260a9803SDavid Howells }
1023260a9803SDavid Howells 
1024260a9803SDavid Howells /*
1025260a9803SDavid Howells  * deliver reply data to an FS.Rename
1026260a9803SDavid Howells  */
1027d001648eSDavid Howells static int afs_deliver_fs_rename(struct afs_call *call)
1028260a9803SDavid Howells {
1029260a9803SDavid Howells 	struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2;
1030260a9803SDavid Howells 	const __be32 *bp;
1031372ee163SDavid Howells 	int ret;
1032260a9803SDavid Howells 
1033d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
1034260a9803SDavid Howells 
1035d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1036372ee163SDavid Howells 	if (ret < 0)
1037372ee163SDavid Howells 		return ret;
1038260a9803SDavid Howells 
1039260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
1040260a9803SDavid Howells 	bp = call->buffer;
104131143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode, NULL);
1042260a9803SDavid Howells 	if (new_dvnode != orig_dvnode)
104331143d5dSDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode,
104431143d5dSDavid Howells 					  NULL);
1045260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
1046260a9803SDavid Howells 
1047260a9803SDavid Howells 	_leave(" = 0 [done]");
1048260a9803SDavid Howells 	return 0;
1049260a9803SDavid Howells }
1050260a9803SDavid Howells 
1051260a9803SDavid Howells /*
1052260a9803SDavid Howells  * FS.Rename operation type
1053260a9803SDavid Howells  */
1054260a9803SDavid Howells static const struct afs_call_type afs_RXFSRename = {
1055260a9803SDavid Howells 	.name		= "FS.Rename",
1056260a9803SDavid Howells 	.deliver	= afs_deliver_fs_rename,
1057260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1058260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
1059260a9803SDavid Howells };
1060260a9803SDavid Howells 
1061260a9803SDavid Howells /*
1062260a9803SDavid Howells  * create a symbolic link
1063260a9803SDavid Howells  */
1064260a9803SDavid Howells int afs_fs_rename(struct afs_server *server,
1065260a9803SDavid Howells 		  struct key *key,
1066260a9803SDavid Howells 		  struct afs_vnode *orig_dvnode,
1067260a9803SDavid Howells 		  const char *orig_name,
1068260a9803SDavid Howells 		  struct afs_vnode *new_dvnode,
1069260a9803SDavid Howells 		  const char *new_name,
107056ff9c83SDavid Howells 		  bool async)
1071260a9803SDavid Howells {
1072260a9803SDavid Howells 	struct afs_call *call;
1073f044c884SDavid Howells 	struct afs_net *net = afs_v2net(orig_dvnode);
1074260a9803SDavid Howells 	size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
1075260a9803SDavid Howells 	__be32 *bp;
1076260a9803SDavid Howells 
1077260a9803SDavid Howells 	_enter("");
1078260a9803SDavid Howells 
1079260a9803SDavid Howells 	o_namesz = strlen(orig_name);
1080260a9803SDavid Howells 	o_padsz = (4 - (o_namesz & 3)) & 3;
1081260a9803SDavid Howells 
1082260a9803SDavid Howells 	n_namesz = strlen(new_name);
1083260a9803SDavid Howells 	n_padsz = (4 - (n_namesz & 3)) & 3;
1084260a9803SDavid Howells 
1085260a9803SDavid Howells 	reqsz = (4 * 4) +
1086260a9803SDavid Howells 		4 + o_namesz + o_padsz +
1087260a9803SDavid Howells 		(3 * 4) +
1088260a9803SDavid Howells 		4 + n_namesz + n_padsz;
1089260a9803SDavid Howells 
1090f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
1091260a9803SDavid Howells 	if (!call)
1092260a9803SDavid Howells 		return -ENOMEM;
1093260a9803SDavid Howells 
1094260a9803SDavid Howells 	call->key = key;
1095260a9803SDavid Howells 	call->reply = orig_dvnode;
1096260a9803SDavid Howells 	call->reply2 = new_dvnode;
1097260a9803SDavid Howells 	call->service_id = FS_SERVICE;
1098260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
1099260a9803SDavid Howells 
1100260a9803SDavid Howells 	/* marshall the parameters */
1101260a9803SDavid Howells 	bp = call->request;
1102260a9803SDavid Howells 	*bp++ = htonl(FSRENAME);
1103260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vid);
1104260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vnode);
1105260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.unique);
1106260a9803SDavid Howells 	*bp++ = htonl(o_namesz);
1107260a9803SDavid Howells 	memcpy(bp, orig_name, o_namesz);
1108260a9803SDavid Howells 	bp = (void *) bp + o_namesz;
1109260a9803SDavid Howells 	if (o_padsz > 0) {
1110260a9803SDavid Howells 		memset(bp, 0, o_padsz);
1111260a9803SDavid Howells 		bp = (void *) bp + o_padsz;
1112260a9803SDavid Howells 	}
1113260a9803SDavid Howells 
1114260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vid);
1115260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vnode);
1116260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.unique);
1117260a9803SDavid Howells 	*bp++ = htonl(n_namesz);
1118260a9803SDavid Howells 	memcpy(bp, new_name, n_namesz);
1119260a9803SDavid Howells 	bp = (void *) bp + n_namesz;
1120260a9803SDavid Howells 	if (n_padsz > 0) {
1121260a9803SDavid Howells 		memset(bp, 0, n_padsz);
1122260a9803SDavid Howells 		bp = (void *) bp + n_padsz;
1123260a9803SDavid Howells 	}
1124260a9803SDavid Howells 
112556ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
1126260a9803SDavid Howells }
112731143d5dSDavid Howells 
112831143d5dSDavid Howells /*
112931143d5dSDavid Howells  * deliver reply data to an FS.StoreData
113031143d5dSDavid Howells  */
1131d001648eSDavid Howells static int afs_deliver_fs_store_data(struct afs_call *call)
113231143d5dSDavid Howells {
113331143d5dSDavid Howells 	struct afs_vnode *vnode = call->reply;
113431143d5dSDavid Howells 	const __be32 *bp;
1135372ee163SDavid Howells 	int ret;
113631143d5dSDavid Howells 
1137d001648eSDavid Howells 	_enter("");
113831143d5dSDavid Howells 
1139d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1140372ee163SDavid Howells 	if (ret < 0)
1141372ee163SDavid Howells 		return ret;
114231143d5dSDavid Howells 
114331143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
114431143d5dSDavid Howells 	bp = call->buffer;
114531143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode,
114631143d5dSDavid Howells 				  &call->store_version);
114731143d5dSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
114831143d5dSDavid Howells 
114931143d5dSDavid Howells 	afs_pages_written_back(vnode, call);
115031143d5dSDavid Howells 
115131143d5dSDavid Howells 	_leave(" = 0 [done]");
115231143d5dSDavid Howells 	return 0;
115331143d5dSDavid Howells }
115431143d5dSDavid Howells 
115531143d5dSDavid Howells /*
115631143d5dSDavid Howells  * FS.StoreData operation type
115731143d5dSDavid Howells  */
115831143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData = {
115931143d5dSDavid Howells 	.name		= "FS.StoreData",
116031143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_data,
116131143d5dSDavid Howells 	.abort_to_error	= afs_abort_to_error,
116231143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
116331143d5dSDavid Howells };
116431143d5dSDavid Howells 
1165b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64 = {
1166b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1167b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_data,
1168b9b1f8d5SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1169b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1170b9b1f8d5SDavid Howells };
1171b9b1f8d5SDavid Howells 
1172b9b1f8d5SDavid Howells /*
1173b9b1f8d5SDavid Howells  * store a set of pages to a very large file
1174b9b1f8d5SDavid Howells  */
1175b9b1f8d5SDavid Howells static int afs_fs_store_data64(struct afs_server *server,
1176b9b1f8d5SDavid Howells 			       struct afs_writeback *wb,
1177b9b1f8d5SDavid Howells 			       pgoff_t first, pgoff_t last,
1178b9b1f8d5SDavid Howells 			       unsigned offset, unsigned to,
1179b9b1f8d5SDavid Howells 			       loff_t size, loff_t pos, loff_t i_size,
118056ff9c83SDavid Howells 			       bool async)
1181b9b1f8d5SDavid Howells {
1182b9b1f8d5SDavid Howells 	struct afs_vnode *vnode = wb->vnode;
1183b9b1f8d5SDavid Howells 	struct afs_call *call;
1184f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1185b9b1f8d5SDavid Howells 	__be32 *bp;
1186b9b1f8d5SDavid Howells 
1187b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
1188b9b1f8d5SDavid Howells 	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
1189b9b1f8d5SDavid Howells 
1190f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData64,
1191b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1192b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1193b9b1f8d5SDavid Howells 	if (!call)
1194b9b1f8d5SDavid Howells 		return -ENOMEM;
1195b9b1f8d5SDavid Howells 
1196b9b1f8d5SDavid Howells 	call->wb = wb;
1197b9b1f8d5SDavid Howells 	call->key = wb->key;
1198b9b1f8d5SDavid Howells 	call->reply = vnode;
1199b9b1f8d5SDavid Howells 	call->service_id = FS_SERVICE;
1200b9b1f8d5SDavid Howells 	call->port = htons(AFS_FS_PORT);
1201b9b1f8d5SDavid Howells 	call->mapping = vnode->vfs_inode.i_mapping;
1202b9b1f8d5SDavid Howells 	call->first = first;
1203b9b1f8d5SDavid Howells 	call->last = last;
1204b9b1f8d5SDavid Howells 	call->first_offset = offset;
1205b9b1f8d5SDavid Howells 	call->last_to = to;
1206b9b1f8d5SDavid Howells 	call->send_pages = true;
1207b9b1f8d5SDavid Howells 	call->store_version = vnode->status.data_version + 1;
1208b9b1f8d5SDavid Howells 
1209b9b1f8d5SDavid Howells 	/* marshall the parameters */
1210b9b1f8d5SDavid Howells 	bp = call->request;
1211b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1212b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1213b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1214b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1215b9b1f8d5SDavid Howells 
1216ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MTIME); /* mask */
1217ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
1218b9b1f8d5SDavid Howells 	*bp++ = 0; /* owner */
1219b9b1f8d5SDavid Howells 	*bp++ = 0; /* group */
1220b9b1f8d5SDavid Howells 	*bp++ = 0; /* unix mode */
1221b9b1f8d5SDavid Howells 	*bp++ = 0; /* segment size */
1222b9b1f8d5SDavid Howells 
1223b9b1f8d5SDavid Howells 	*bp++ = htonl(pos >> 32);
1224b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) pos);
1225b9b1f8d5SDavid Howells 	*bp++ = htonl(size >> 32);
1226b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) size);
1227b9b1f8d5SDavid Howells 	*bp++ = htonl(i_size >> 32);
1228b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) i_size);
1229b9b1f8d5SDavid Howells 
123056ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
1231b9b1f8d5SDavid Howells }
1232b9b1f8d5SDavid Howells 
123331143d5dSDavid Howells /*
123431143d5dSDavid Howells  * store a set of pages
123531143d5dSDavid Howells  */
123631143d5dSDavid Howells int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
123731143d5dSDavid Howells 		      pgoff_t first, pgoff_t last,
123831143d5dSDavid Howells 		      unsigned offset, unsigned to,
123956ff9c83SDavid Howells 		      bool async)
124031143d5dSDavid Howells {
124131143d5dSDavid Howells 	struct afs_vnode *vnode = wb->vnode;
124231143d5dSDavid Howells 	struct afs_call *call;
1243f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
124431143d5dSDavid Howells 	loff_t size, pos, i_size;
124531143d5dSDavid Howells 	__be32 *bp;
124631143d5dSDavid Howells 
124731143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
124831143d5dSDavid Howells 	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
124931143d5dSDavid Howells 
1250146a1192SDavid Howells 	size = (loff_t)to - (loff_t)offset;
125131143d5dSDavid Howells 	if (first != last)
125231143d5dSDavid Howells 		size += (loff_t)(last - first) << PAGE_SHIFT;
125331143d5dSDavid Howells 	pos = (loff_t)first << PAGE_SHIFT;
125431143d5dSDavid Howells 	pos += offset;
125531143d5dSDavid Howells 
125631143d5dSDavid Howells 	i_size = i_size_read(&vnode->vfs_inode);
125731143d5dSDavid Howells 	if (pos + size > i_size)
125831143d5dSDavid Howells 		i_size = size + pos;
125931143d5dSDavid Howells 
126031143d5dSDavid Howells 	_debug("size %llx, at %llx, i_size %llx",
126131143d5dSDavid Howells 	       (unsigned long long) size, (unsigned long long) pos,
126231143d5dSDavid Howells 	       (unsigned long long) i_size);
126331143d5dSDavid Howells 
1264b9b1f8d5SDavid Howells 	if (pos >> 32 || i_size >> 32 || size >> 32 || (pos + size) >> 32)
1265b9b1f8d5SDavid Howells 		return afs_fs_store_data64(server, wb, first, last, offset, to,
126656ff9c83SDavid Howells 					   size, pos, i_size, async);
126731143d5dSDavid Howells 
1268f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData,
126931143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
127031143d5dSDavid Howells 				   (21 + 6) * 4);
127131143d5dSDavid Howells 	if (!call)
127231143d5dSDavid Howells 		return -ENOMEM;
127331143d5dSDavid Howells 
127431143d5dSDavid Howells 	call->wb = wb;
127531143d5dSDavid Howells 	call->key = wb->key;
127631143d5dSDavid Howells 	call->reply = vnode;
127731143d5dSDavid Howells 	call->service_id = FS_SERVICE;
127831143d5dSDavid Howells 	call->port = htons(AFS_FS_PORT);
127931143d5dSDavid Howells 	call->mapping = vnode->vfs_inode.i_mapping;
128031143d5dSDavid Howells 	call->first = first;
128131143d5dSDavid Howells 	call->last = last;
128231143d5dSDavid Howells 	call->first_offset = offset;
128331143d5dSDavid Howells 	call->last_to = to;
128431143d5dSDavid Howells 	call->send_pages = true;
128531143d5dSDavid Howells 	call->store_version = vnode->status.data_version + 1;
128631143d5dSDavid Howells 
128731143d5dSDavid Howells 	/* marshall the parameters */
128831143d5dSDavid Howells 	bp = call->request;
128931143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
129031143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
129131143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
129231143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
129331143d5dSDavid Howells 
1294ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MTIME); /* mask */
1295ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
129631143d5dSDavid Howells 	*bp++ = 0; /* owner */
129731143d5dSDavid Howells 	*bp++ = 0; /* group */
129831143d5dSDavid Howells 	*bp++ = 0; /* unix mode */
129931143d5dSDavid Howells 	*bp++ = 0; /* segment size */
130031143d5dSDavid Howells 
130131143d5dSDavid Howells 	*bp++ = htonl(pos);
130231143d5dSDavid Howells 	*bp++ = htonl(size);
130331143d5dSDavid Howells 	*bp++ = htonl(i_size);
130431143d5dSDavid Howells 
130556ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
130631143d5dSDavid Howells }
130731143d5dSDavid Howells 
130831143d5dSDavid Howells /*
130931143d5dSDavid Howells  * deliver reply data to an FS.StoreStatus
131031143d5dSDavid Howells  */
1311d001648eSDavid Howells static int afs_deliver_fs_store_status(struct afs_call *call)
131231143d5dSDavid Howells {
131331143d5dSDavid Howells 	afs_dataversion_t *store_version;
131431143d5dSDavid Howells 	struct afs_vnode *vnode = call->reply;
131531143d5dSDavid Howells 	const __be32 *bp;
1316372ee163SDavid Howells 	int ret;
131731143d5dSDavid Howells 
1318d001648eSDavid Howells 	_enter("");
131931143d5dSDavid Howells 
1320d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1321372ee163SDavid Howells 	if (ret < 0)
1322372ee163SDavid Howells 		return ret;
132331143d5dSDavid Howells 
132431143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
132531143d5dSDavid Howells 	store_version = NULL;
132631143d5dSDavid Howells 	if (call->operation_ID == FSSTOREDATA)
132731143d5dSDavid Howells 		store_version = &call->store_version;
132831143d5dSDavid Howells 
132931143d5dSDavid Howells 	bp = call->buffer;
133031143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, store_version);
133131143d5dSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
133231143d5dSDavid Howells 
133331143d5dSDavid Howells 	_leave(" = 0 [done]");
133431143d5dSDavid Howells 	return 0;
133531143d5dSDavid Howells }
133631143d5dSDavid Howells 
133731143d5dSDavid Howells /*
133831143d5dSDavid Howells  * FS.StoreStatus operation type
133931143d5dSDavid Howells  */
134031143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreStatus = {
134131143d5dSDavid Howells 	.name		= "FS.StoreStatus",
134231143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
134331143d5dSDavid Howells 	.abort_to_error	= afs_abort_to_error,
134431143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
134531143d5dSDavid Howells };
134631143d5dSDavid Howells 
134731143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData_as_Status = {
134831143d5dSDavid Howells 	.name		= "FS.StoreData",
134931143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
135031143d5dSDavid Howells 	.abort_to_error	= afs_abort_to_error,
135131143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
135231143d5dSDavid Howells };
135331143d5dSDavid Howells 
1354b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64_as_Status = {
1355b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1356b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_status,
1357b9b1f8d5SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1358b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1359b9b1f8d5SDavid Howells };
1360b9b1f8d5SDavid Howells 
1361b9b1f8d5SDavid Howells /*
1362b9b1f8d5SDavid Howells  * set the attributes on a very large file, using FS.StoreData rather than
1363b9b1f8d5SDavid Howells  * FS.StoreStatus so as to alter the file size also
1364b9b1f8d5SDavid Howells  */
1365b9b1f8d5SDavid Howells static int afs_fs_setattr_size64(struct afs_server *server, struct key *key,
1366b9b1f8d5SDavid Howells 				 struct afs_vnode *vnode, struct iattr *attr,
136756ff9c83SDavid Howells 				 bool async)
1368b9b1f8d5SDavid Howells {
1369b9b1f8d5SDavid Howells 	struct afs_call *call;
1370f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1371b9b1f8d5SDavid Howells 	__be32 *bp;
1372b9b1f8d5SDavid Howells 
1373b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
1374b9b1f8d5SDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
1375b9b1f8d5SDavid Howells 
1376b9b1f8d5SDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1377b9b1f8d5SDavid Howells 
1378f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData64_as_Status,
1379b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1380b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1381b9b1f8d5SDavid Howells 	if (!call)
1382b9b1f8d5SDavid Howells 		return -ENOMEM;
1383b9b1f8d5SDavid Howells 
1384b9b1f8d5SDavid Howells 	call->key = key;
1385b9b1f8d5SDavid Howells 	call->reply = vnode;
1386b9b1f8d5SDavid Howells 	call->service_id = FS_SERVICE;
1387b9b1f8d5SDavid Howells 	call->port = htons(AFS_FS_PORT);
1388b9b1f8d5SDavid Howells 	call->store_version = vnode->status.data_version + 1;
1389b9b1f8d5SDavid Howells 	call->operation_ID = FSSTOREDATA;
1390b9b1f8d5SDavid Howells 
1391b9b1f8d5SDavid Howells 	/* marshall the parameters */
1392b9b1f8d5SDavid Howells 	bp = call->request;
1393b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1394b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1395b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1396b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1397b9b1f8d5SDavid Howells 
1398b9b1f8d5SDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
1399b9b1f8d5SDavid Howells 
1400b9b1f8d5SDavid Howells 	*bp++ = 0;				/* position of start of write */
1401b9b1f8d5SDavid Howells 	*bp++ = 0;
1402b9b1f8d5SDavid Howells 	*bp++ = 0;				/* size of write */
1403b9b1f8d5SDavid Howells 	*bp++ = 0;
1404b9b1f8d5SDavid Howells 	*bp++ = htonl(attr->ia_size >> 32);	/* new file length */
1405b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) attr->ia_size);
1406b9b1f8d5SDavid Howells 
140756ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
1408b9b1f8d5SDavid Howells }
1409b9b1f8d5SDavid Howells 
141031143d5dSDavid Howells /*
141131143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus
141231143d5dSDavid Howells  * so as to alter the file size also
141331143d5dSDavid Howells  */
141431143d5dSDavid Howells static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
141531143d5dSDavid Howells 			       struct afs_vnode *vnode, struct iattr *attr,
141656ff9c83SDavid Howells 			       bool async)
141731143d5dSDavid Howells {
141831143d5dSDavid Howells 	struct afs_call *call;
1419f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
142031143d5dSDavid Howells 	__be32 *bp;
142131143d5dSDavid Howells 
142231143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
142331143d5dSDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
142431143d5dSDavid Howells 
142531143d5dSDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1426b9b1f8d5SDavid Howells 	if (attr->ia_size >> 32)
1427b9b1f8d5SDavid Howells 		return afs_fs_setattr_size64(server, key, vnode, attr,
142856ff9c83SDavid Howells 					     async);
142931143d5dSDavid Howells 
1430f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData_as_Status,
143131143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
143231143d5dSDavid Howells 				   (21 + 6) * 4);
143331143d5dSDavid Howells 	if (!call)
143431143d5dSDavid Howells 		return -ENOMEM;
143531143d5dSDavid Howells 
143631143d5dSDavid Howells 	call->key = key;
143731143d5dSDavid Howells 	call->reply = vnode;
143831143d5dSDavid Howells 	call->service_id = FS_SERVICE;
143931143d5dSDavid Howells 	call->port = htons(AFS_FS_PORT);
144031143d5dSDavid Howells 	call->store_version = vnode->status.data_version + 1;
144131143d5dSDavid Howells 	call->operation_ID = FSSTOREDATA;
144231143d5dSDavid Howells 
144331143d5dSDavid Howells 	/* marshall the parameters */
144431143d5dSDavid Howells 	bp = call->request;
144531143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
144631143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
144731143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
144831143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
144931143d5dSDavid Howells 
145031143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
145131143d5dSDavid Howells 
145231143d5dSDavid Howells 	*bp++ = 0;				/* position of start of write */
145331143d5dSDavid Howells 	*bp++ = 0;				/* size of write */
145431143d5dSDavid Howells 	*bp++ = htonl(attr->ia_size);		/* new file length */
145531143d5dSDavid Howells 
145656ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
145731143d5dSDavid Howells }
145831143d5dSDavid Howells 
145931143d5dSDavid Howells /*
146031143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData if there's a change in file
146131143d5dSDavid Howells  * size, and FS.StoreStatus otherwise
146231143d5dSDavid Howells  */
146331143d5dSDavid Howells int afs_fs_setattr(struct afs_server *server, struct key *key,
146431143d5dSDavid Howells 		   struct afs_vnode *vnode, struct iattr *attr,
146556ff9c83SDavid Howells 		   bool async)
146631143d5dSDavid Howells {
146731143d5dSDavid Howells 	struct afs_call *call;
1468f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
146931143d5dSDavid Howells 	__be32 *bp;
147031143d5dSDavid Howells 
147131143d5dSDavid Howells 	if (attr->ia_valid & ATTR_SIZE)
147231143d5dSDavid Howells 		return afs_fs_setattr_size(server, key, vnode, attr,
147356ff9c83SDavid Howells 					   async);
147431143d5dSDavid Howells 
147531143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
147631143d5dSDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
147731143d5dSDavid Howells 
1478f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreStatus,
147931143d5dSDavid Howells 				   (4 + 6) * 4,
148031143d5dSDavid Howells 				   (21 + 6) * 4);
148131143d5dSDavid Howells 	if (!call)
148231143d5dSDavid Howells 		return -ENOMEM;
148331143d5dSDavid Howells 
148431143d5dSDavid Howells 	call->key = key;
148531143d5dSDavid Howells 	call->reply = vnode;
148631143d5dSDavid Howells 	call->service_id = FS_SERVICE;
148731143d5dSDavid Howells 	call->port = htons(AFS_FS_PORT);
148831143d5dSDavid Howells 	call->operation_ID = FSSTORESTATUS;
148931143d5dSDavid Howells 
149031143d5dSDavid Howells 	/* marshall the parameters */
149131143d5dSDavid Howells 	bp = call->request;
149231143d5dSDavid Howells 	*bp++ = htonl(FSSTORESTATUS);
149331143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
149431143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
149531143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
149631143d5dSDavid Howells 
149731143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
149831143d5dSDavid Howells 
149956ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
150031143d5dSDavid Howells }
150145222b9eSDavid Howells 
150245222b9eSDavid Howells /*
150345222b9eSDavid Howells  * deliver reply data to an FS.GetVolumeStatus
150445222b9eSDavid Howells  */
1505d001648eSDavid Howells static int afs_deliver_fs_get_volume_status(struct afs_call *call)
150645222b9eSDavid Howells {
150745222b9eSDavid Howells 	const __be32 *bp;
150845222b9eSDavid Howells 	char *p;
150945222b9eSDavid Howells 	int ret;
151045222b9eSDavid Howells 
1511d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
151245222b9eSDavid Howells 
151345222b9eSDavid Howells 	switch (call->unmarshall) {
151445222b9eSDavid Howells 	case 0:
151545222b9eSDavid Howells 		call->offset = 0;
151645222b9eSDavid Howells 		call->unmarshall++;
151745222b9eSDavid Howells 
151845222b9eSDavid Howells 		/* extract the returned status record */
151945222b9eSDavid Howells 	case 1:
152045222b9eSDavid Howells 		_debug("extract status");
1521d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1522d001648eSDavid Howells 				       12 * 4, true);
1523372ee163SDavid Howells 		if (ret < 0)
1524372ee163SDavid Howells 			return ret;
152545222b9eSDavid Howells 
152645222b9eSDavid Howells 		bp = call->buffer;
152745222b9eSDavid Howells 		xdr_decode_AFSFetchVolumeStatus(&bp, call->reply2);
152845222b9eSDavid Howells 		call->offset = 0;
152945222b9eSDavid Howells 		call->unmarshall++;
153045222b9eSDavid Howells 
153145222b9eSDavid Howells 		/* extract the volume name length */
153245222b9eSDavid Howells 	case 2:
1533d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1534372ee163SDavid Howells 		if (ret < 0)
1535372ee163SDavid Howells 			return ret;
153645222b9eSDavid Howells 
153745222b9eSDavid Howells 		call->count = ntohl(call->tmp);
153845222b9eSDavid Howells 		_debug("volname length: %u", call->count);
153945222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
154045222b9eSDavid Howells 			return -EBADMSG;
154145222b9eSDavid Howells 		call->offset = 0;
154245222b9eSDavid Howells 		call->unmarshall++;
154345222b9eSDavid Howells 
154445222b9eSDavid Howells 		/* extract the volume name */
154545222b9eSDavid Howells 	case 3:
154645222b9eSDavid Howells 		_debug("extract volname");
154745222b9eSDavid Howells 		if (call->count > 0) {
1548d001648eSDavid Howells 			ret = afs_extract_data(call, call->reply3,
1549d001648eSDavid Howells 					       call->count, true);
1550372ee163SDavid Howells 			if (ret < 0)
1551372ee163SDavid Howells 				return ret;
155245222b9eSDavid Howells 		}
155345222b9eSDavid Howells 
155445222b9eSDavid Howells 		p = call->reply3;
155545222b9eSDavid Howells 		p[call->count] = 0;
155645222b9eSDavid Howells 		_debug("volname '%s'", p);
155745222b9eSDavid Howells 
155845222b9eSDavid Howells 		call->offset = 0;
155945222b9eSDavid Howells 		call->unmarshall++;
156045222b9eSDavid Howells 
156145222b9eSDavid Howells 		/* extract the volume name padding */
156245222b9eSDavid Howells 		if ((call->count & 3) == 0) {
156345222b9eSDavid Howells 			call->unmarshall++;
156445222b9eSDavid Howells 			goto no_volname_padding;
156545222b9eSDavid Howells 		}
156645222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
156745222b9eSDavid Howells 
156845222b9eSDavid Howells 	case 4:
1569d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1570d001648eSDavid Howells 				       call->count, true);
1571372ee163SDavid Howells 		if (ret < 0)
1572372ee163SDavid Howells 			return ret;
157345222b9eSDavid Howells 
157445222b9eSDavid Howells 		call->offset = 0;
157545222b9eSDavid Howells 		call->unmarshall++;
157645222b9eSDavid Howells 	no_volname_padding:
157745222b9eSDavid Howells 
157845222b9eSDavid Howells 		/* extract the offline message length */
157945222b9eSDavid Howells 	case 5:
1580d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1581372ee163SDavid Howells 		if (ret < 0)
1582372ee163SDavid Howells 			return ret;
158345222b9eSDavid Howells 
158445222b9eSDavid Howells 		call->count = ntohl(call->tmp);
158545222b9eSDavid Howells 		_debug("offline msg length: %u", call->count);
158645222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
158745222b9eSDavid Howells 			return -EBADMSG;
158845222b9eSDavid Howells 		call->offset = 0;
158945222b9eSDavid Howells 		call->unmarshall++;
159045222b9eSDavid Howells 
159145222b9eSDavid Howells 		/* extract the offline message */
159245222b9eSDavid Howells 	case 6:
159345222b9eSDavid Howells 		_debug("extract offline");
159445222b9eSDavid Howells 		if (call->count > 0) {
1595d001648eSDavid Howells 			ret = afs_extract_data(call, call->reply3,
1596d001648eSDavid Howells 					       call->count, true);
1597372ee163SDavid Howells 			if (ret < 0)
1598372ee163SDavid Howells 				return ret;
159945222b9eSDavid Howells 		}
160045222b9eSDavid Howells 
160145222b9eSDavid Howells 		p = call->reply3;
160245222b9eSDavid Howells 		p[call->count] = 0;
160345222b9eSDavid Howells 		_debug("offline '%s'", p);
160445222b9eSDavid Howells 
160545222b9eSDavid Howells 		call->offset = 0;
160645222b9eSDavid Howells 		call->unmarshall++;
160745222b9eSDavid Howells 
160845222b9eSDavid Howells 		/* extract the offline message padding */
160945222b9eSDavid Howells 		if ((call->count & 3) == 0) {
161045222b9eSDavid Howells 			call->unmarshall++;
161145222b9eSDavid Howells 			goto no_offline_padding;
161245222b9eSDavid Howells 		}
161345222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
161445222b9eSDavid Howells 
161545222b9eSDavid Howells 	case 7:
1616d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1617d001648eSDavid Howells 				       call->count, true);
1618372ee163SDavid Howells 		if (ret < 0)
1619372ee163SDavid Howells 			return ret;
162045222b9eSDavid Howells 
162145222b9eSDavid Howells 		call->offset = 0;
162245222b9eSDavid Howells 		call->unmarshall++;
162345222b9eSDavid Howells 	no_offline_padding:
162445222b9eSDavid Howells 
162545222b9eSDavid Howells 		/* extract the message of the day length */
162645222b9eSDavid Howells 	case 8:
1627d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1628372ee163SDavid Howells 		if (ret < 0)
1629372ee163SDavid Howells 			return ret;
163045222b9eSDavid Howells 
163145222b9eSDavid Howells 		call->count = ntohl(call->tmp);
163245222b9eSDavid Howells 		_debug("motd length: %u", call->count);
163345222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
163445222b9eSDavid Howells 			return -EBADMSG;
163545222b9eSDavid Howells 		call->offset = 0;
163645222b9eSDavid Howells 		call->unmarshall++;
163745222b9eSDavid Howells 
163845222b9eSDavid Howells 		/* extract the message of the day */
163945222b9eSDavid Howells 	case 9:
164045222b9eSDavid Howells 		_debug("extract motd");
164145222b9eSDavid Howells 		if (call->count > 0) {
1642d001648eSDavid Howells 			ret = afs_extract_data(call, call->reply3,
1643d001648eSDavid Howells 					       call->count, true);
1644372ee163SDavid Howells 			if (ret < 0)
1645372ee163SDavid Howells 				return ret;
164645222b9eSDavid Howells 		}
164745222b9eSDavid Howells 
164845222b9eSDavid Howells 		p = call->reply3;
164945222b9eSDavid Howells 		p[call->count] = 0;
165045222b9eSDavid Howells 		_debug("motd '%s'", p);
165145222b9eSDavid Howells 
165245222b9eSDavid Howells 		call->offset = 0;
165345222b9eSDavid Howells 		call->unmarshall++;
165445222b9eSDavid Howells 
165545222b9eSDavid Howells 		/* extract the message of the day padding */
1656d001648eSDavid Howells 		call->count = (4 - (call->count & 3)) & 3;
165745222b9eSDavid Howells 
165845222b9eSDavid Howells 	case 10:
1659d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1660d001648eSDavid Howells 				       call->count, false);
1661372ee163SDavid Howells 		if (ret < 0)
1662372ee163SDavid Howells 			return ret;
166345222b9eSDavid Howells 
166445222b9eSDavid Howells 		call->offset = 0;
166545222b9eSDavid Howells 		call->unmarshall++;
166645222b9eSDavid Howells 	case 11:
166745222b9eSDavid Howells 		break;
166845222b9eSDavid Howells 	}
166945222b9eSDavid Howells 
167045222b9eSDavid Howells 	_leave(" = 0 [done]");
167145222b9eSDavid Howells 	return 0;
167245222b9eSDavid Howells }
167345222b9eSDavid Howells 
167445222b9eSDavid Howells /*
167545222b9eSDavid Howells  * destroy an FS.GetVolumeStatus call
167645222b9eSDavid Howells  */
167745222b9eSDavid Howells static void afs_get_volume_status_call_destructor(struct afs_call *call)
167845222b9eSDavid Howells {
167945222b9eSDavid Howells 	kfree(call->reply3);
168045222b9eSDavid Howells 	call->reply3 = NULL;
168145222b9eSDavid Howells 	afs_flat_call_destructor(call);
168245222b9eSDavid Howells }
168345222b9eSDavid Howells 
168445222b9eSDavid Howells /*
168545222b9eSDavid Howells  * FS.GetVolumeStatus operation type
168645222b9eSDavid Howells  */
168745222b9eSDavid Howells static const struct afs_call_type afs_RXFSGetVolumeStatus = {
168845222b9eSDavid Howells 	.name		= "FS.GetVolumeStatus",
168945222b9eSDavid Howells 	.deliver	= afs_deliver_fs_get_volume_status,
169045222b9eSDavid Howells 	.abort_to_error	= afs_abort_to_error,
169145222b9eSDavid Howells 	.destructor	= afs_get_volume_status_call_destructor,
169245222b9eSDavid Howells };
169345222b9eSDavid Howells 
169445222b9eSDavid Howells /*
169545222b9eSDavid Howells  * fetch the status of a volume
169645222b9eSDavid Howells  */
169745222b9eSDavid Howells int afs_fs_get_volume_status(struct afs_server *server,
169845222b9eSDavid Howells 			     struct key *key,
169945222b9eSDavid Howells 			     struct afs_vnode *vnode,
170045222b9eSDavid Howells 			     struct afs_volume_status *vs,
170156ff9c83SDavid Howells 			     bool async)
170245222b9eSDavid Howells {
170345222b9eSDavid Howells 	struct afs_call *call;
1704f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
170545222b9eSDavid Howells 	__be32 *bp;
170645222b9eSDavid Howells 	void *tmpbuf;
170745222b9eSDavid Howells 
170845222b9eSDavid Howells 	_enter("");
170945222b9eSDavid Howells 
171045222b9eSDavid Howells 	tmpbuf = kmalloc(AFSOPAQUEMAX, GFP_KERNEL);
171145222b9eSDavid Howells 	if (!tmpbuf)
171245222b9eSDavid Howells 		return -ENOMEM;
171345222b9eSDavid Howells 
1714f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGetVolumeStatus, 2 * 4, 12 * 4);
171545222b9eSDavid Howells 	if (!call) {
171645222b9eSDavid Howells 		kfree(tmpbuf);
171745222b9eSDavid Howells 		return -ENOMEM;
171845222b9eSDavid Howells 	}
171945222b9eSDavid Howells 
172045222b9eSDavid Howells 	call->key = key;
172145222b9eSDavid Howells 	call->reply = vnode;
172245222b9eSDavid Howells 	call->reply2 = vs;
172345222b9eSDavid Howells 	call->reply3 = tmpbuf;
172445222b9eSDavid Howells 	call->service_id = FS_SERVICE;
172545222b9eSDavid Howells 	call->port = htons(AFS_FS_PORT);
172645222b9eSDavid Howells 
172745222b9eSDavid Howells 	/* marshall the parameters */
172845222b9eSDavid Howells 	bp = call->request;
172945222b9eSDavid Howells 	bp[0] = htonl(FSGETVOLUMESTATUS);
173045222b9eSDavid Howells 	bp[1] = htonl(vnode->fid.vid);
173145222b9eSDavid Howells 
173256ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
173345222b9eSDavid Howells }
1734e8d6c554SDavid Howells 
1735e8d6c554SDavid Howells /*
1736e8d6c554SDavid Howells  * deliver reply data to an FS.SetLock, FS.ExtendLock or FS.ReleaseLock
1737e8d6c554SDavid Howells  */
1738d001648eSDavid Howells static int afs_deliver_fs_xxxx_lock(struct afs_call *call)
1739e8d6c554SDavid Howells {
1740e8d6c554SDavid Howells 	const __be32 *bp;
1741372ee163SDavid Howells 	int ret;
1742e8d6c554SDavid Howells 
1743d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
1744e8d6c554SDavid Howells 
1745d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1746372ee163SDavid Howells 	if (ret < 0)
1747372ee163SDavid Howells 		return ret;
1748e8d6c554SDavid Howells 
1749e8d6c554SDavid Howells 	/* unmarshall the reply once we've received all of it */
1750e8d6c554SDavid Howells 	bp = call->buffer;
1751e8d6c554SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
1752e8d6c554SDavid Howells 
1753e8d6c554SDavid Howells 	_leave(" = 0 [done]");
1754e8d6c554SDavid Howells 	return 0;
1755e8d6c554SDavid Howells }
1756e8d6c554SDavid Howells 
1757e8d6c554SDavid Howells /*
1758e8d6c554SDavid Howells  * FS.SetLock operation type
1759e8d6c554SDavid Howells  */
1760e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSSetLock = {
1761e8d6c554SDavid Howells 	.name		= "FS.SetLock",
1762e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1763e8d6c554SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1764e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1765e8d6c554SDavid Howells };
1766e8d6c554SDavid Howells 
1767e8d6c554SDavid Howells /*
1768e8d6c554SDavid Howells  * FS.ExtendLock operation type
1769e8d6c554SDavid Howells  */
1770e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSExtendLock = {
1771e8d6c554SDavid Howells 	.name		= "FS.ExtendLock",
1772e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1773e8d6c554SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1774e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1775e8d6c554SDavid Howells };
1776e8d6c554SDavid Howells 
1777e8d6c554SDavid Howells /*
1778e8d6c554SDavid Howells  * FS.ReleaseLock operation type
1779e8d6c554SDavid Howells  */
1780e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSReleaseLock = {
1781e8d6c554SDavid Howells 	.name		= "FS.ReleaseLock",
1782e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1783e8d6c554SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1784e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1785e8d6c554SDavid Howells };
1786e8d6c554SDavid Howells 
1787e8d6c554SDavid Howells /*
1788e8d6c554SDavid Howells  * get a lock on a file
1789e8d6c554SDavid Howells  */
1790e8d6c554SDavid Howells int afs_fs_set_lock(struct afs_server *server,
1791e8d6c554SDavid Howells 		    struct key *key,
1792e8d6c554SDavid Howells 		    struct afs_vnode *vnode,
1793e8d6c554SDavid Howells 		    afs_lock_type_t type,
179456ff9c83SDavid Howells 		    bool async)
1795e8d6c554SDavid Howells {
1796e8d6c554SDavid Howells 	struct afs_call *call;
1797f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1798e8d6c554SDavid Howells 	__be32 *bp;
1799e8d6c554SDavid Howells 
1800e8d6c554SDavid Howells 	_enter("");
1801e8d6c554SDavid Howells 
1802f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSSetLock, 5 * 4, 6 * 4);
1803e8d6c554SDavid Howells 	if (!call)
1804e8d6c554SDavid Howells 		return -ENOMEM;
1805e8d6c554SDavid Howells 
1806e8d6c554SDavid Howells 	call->key = key;
1807e8d6c554SDavid Howells 	call->reply = vnode;
1808e8d6c554SDavid Howells 	call->service_id = FS_SERVICE;
1809e8d6c554SDavid Howells 	call->port = htons(AFS_FS_PORT);
1810e8d6c554SDavid Howells 
1811e8d6c554SDavid Howells 	/* marshall the parameters */
1812e8d6c554SDavid Howells 	bp = call->request;
1813e8d6c554SDavid Howells 	*bp++ = htonl(FSSETLOCK);
1814e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1815e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1816e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1817e8d6c554SDavid Howells 	*bp++ = htonl(type);
1818e8d6c554SDavid Howells 
181956ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
1820e8d6c554SDavid Howells }
1821e8d6c554SDavid Howells 
1822e8d6c554SDavid Howells /*
1823e8d6c554SDavid Howells  * extend a lock on a file
1824e8d6c554SDavid Howells  */
1825e8d6c554SDavid Howells int afs_fs_extend_lock(struct afs_server *server,
1826e8d6c554SDavid Howells 		       struct key *key,
1827e8d6c554SDavid Howells 		       struct afs_vnode *vnode,
182856ff9c83SDavid Howells 		       bool async)
1829e8d6c554SDavid Howells {
1830e8d6c554SDavid Howells 	struct afs_call *call;
1831f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1832e8d6c554SDavid Howells 	__be32 *bp;
1833e8d6c554SDavid Howells 
1834e8d6c554SDavid Howells 	_enter("");
1835e8d6c554SDavid Howells 
1836f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSExtendLock, 4 * 4, 6 * 4);
1837e8d6c554SDavid Howells 	if (!call)
1838e8d6c554SDavid Howells 		return -ENOMEM;
1839e8d6c554SDavid Howells 
1840e8d6c554SDavid Howells 	call->key = key;
1841e8d6c554SDavid Howells 	call->reply = vnode;
1842e8d6c554SDavid Howells 	call->service_id = FS_SERVICE;
1843e8d6c554SDavid Howells 	call->port = htons(AFS_FS_PORT);
1844e8d6c554SDavid Howells 
1845e8d6c554SDavid Howells 	/* marshall the parameters */
1846e8d6c554SDavid Howells 	bp = call->request;
1847e8d6c554SDavid Howells 	*bp++ = htonl(FSEXTENDLOCK);
1848e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1849e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1850e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1851e8d6c554SDavid Howells 
185256ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
1853e8d6c554SDavid Howells }
1854e8d6c554SDavid Howells 
1855e8d6c554SDavid Howells /*
1856e8d6c554SDavid Howells  * release a lock on a file
1857e8d6c554SDavid Howells  */
1858e8d6c554SDavid Howells int afs_fs_release_lock(struct afs_server *server,
1859e8d6c554SDavid Howells 			struct key *key,
1860e8d6c554SDavid Howells 			struct afs_vnode *vnode,
186156ff9c83SDavid Howells 			bool async)
1862e8d6c554SDavid Howells {
1863e8d6c554SDavid Howells 	struct afs_call *call;
1864f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1865e8d6c554SDavid Howells 	__be32 *bp;
1866e8d6c554SDavid Howells 
1867e8d6c554SDavid Howells 	_enter("");
1868e8d6c554SDavid Howells 
1869f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSReleaseLock, 4 * 4, 6 * 4);
1870e8d6c554SDavid Howells 	if (!call)
1871e8d6c554SDavid Howells 		return -ENOMEM;
1872e8d6c554SDavid Howells 
1873e8d6c554SDavid Howells 	call->key = key;
1874e8d6c554SDavid Howells 	call->reply = vnode;
1875e8d6c554SDavid Howells 	call->service_id = FS_SERVICE;
1876e8d6c554SDavid Howells 	call->port = htons(AFS_FS_PORT);
1877e8d6c554SDavid Howells 
1878e8d6c554SDavid Howells 	/* marshall the parameters */
1879e8d6c554SDavid Howells 	bp = call->request;
1880e8d6c554SDavid Howells 	*bp++ = htonl(FSRELEASELOCK);
1881e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1882e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1883e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1884e8d6c554SDavid Howells 
188556ff9c83SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, async);
1886e8d6c554SDavid Howells }
1887