xref: /openbmc/linux/fs/afs/fsclient.c (revision 025db80c)
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 
19025db80cSDavid Howells static const struct afs_fid afs_zero_fid;
20025db80cSDavid Howells 
211da177e4SLinus Torvalds /*
226db3ac3cSDavid Howells  * We need somewhere to discard into in case the server helpfully returns more
236db3ac3cSDavid Howells  * than we asked for in FS.FetchData{,64}.
246db3ac3cSDavid Howells  */
256db3ac3cSDavid Howells static u8 afs_discard_buffer[64];
266db3ac3cSDavid Howells 
27d2ddc776SDavid Howells static inline void afs_use_fs_server(struct afs_call *call, struct afs_cb_interest *cbi)
28c435ee34SDavid Howells {
29d2ddc776SDavid Howells 	call->cbi = afs_get_cb_interest(cbi);
30c435ee34SDavid Howells }
31c435ee34SDavid Howells 
326db3ac3cSDavid Howells /*
33260a9803SDavid Howells  * decode an AFSFid block
34260a9803SDavid Howells  */
35260a9803SDavid Howells static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
36260a9803SDavid Howells {
37260a9803SDavid Howells 	const __be32 *bp = *_bp;
38260a9803SDavid Howells 
39260a9803SDavid Howells 	fid->vid		= ntohl(*bp++);
40260a9803SDavid Howells 	fid->vnode		= ntohl(*bp++);
41260a9803SDavid Howells 	fid->unique		= ntohl(*bp++);
42260a9803SDavid Howells 	*_bp = bp;
43260a9803SDavid Howells }
44260a9803SDavid Howells 
45260a9803SDavid Howells /*
4608e0e7c8SDavid Howells  * decode an AFSFetchStatus block
471da177e4SLinus Torvalds  */
4808e0e7c8SDavid Howells static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
49260a9803SDavid Howells 				      struct afs_file_status *status,
5031143d5dSDavid Howells 				      struct afs_vnode *vnode,
5131143d5dSDavid Howells 				      afs_dataversion_t *store_version)
521da177e4SLinus Torvalds {
5331143d5dSDavid Howells 	afs_dataversion_t expected_version;
5408e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
5508e0e7c8SDavid Howells 	umode_t mode;
56260a9803SDavid Howells 	u64 data_version, size;
57c435ee34SDavid Howells 	bool changed = false;
58a0a5386aSEric W. Biederman 	kuid_t owner;
59a0a5386aSEric W. Biederman 	kgid_t group;
6008e0e7c8SDavid Howells 
61d2ddc776SDavid Howells 	if (vnode)
62c435ee34SDavid Howells 		write_seqlock(&vnode->cb_lock);
63c435ee34SDavid Howells 
6408e0e7c8SDavid Howells #define EXTRACT(DST)				\
6508e0e7c8SDavid Howells 	do {					\
6608e0e7c8SDavid Howells 		u32 x = ntohl(*bp++);		\
67c435ee34SDavid Howells 		if (DST != x)			\
68c435ee34SDavid Howells 			changed |= true;	\
6908e0e7c8SDavid Howells 		DST = x;			\
7008e0e7c8SDavid Howells 	} while (0)
7108e0e7c8SDavid Howells 
72260a9803SDavid Howells 	status->if_version = ntohl(*bp++);
73260a9803SDavid Howells 	EXTRACT(status->type);
74260a9803SDavid Howells 	EXTRACT(status->nlink);
75260a9803SDavid Howells 	size = ntohl(*bp++);
7608e0e7c8SDavid Howells 	data_version = ntohl(*bp++);
77260a9803SDavid Howells 	EXTRACT(status->author);
78a0a5386aSEric W. Biederman 	owner = make_kuid(&init_user_ns, ntohl(*bp++));
79a0a5386aSEric W. Biederman 	changed |= !uid_eq(owner, status->owner);
80a0a5386aSEric W. Biederman 	status->owner = owner;
81260a9803SDavid Howells 	EXTRACT(status->caller_access); /* call ticket dependent */
82260a9803SDavid Howells 	EXTRACT(status->anon_access);
83260a9803SDavid Howells 	EXTRACT(status->mode);
84be080a6fSDavid Howells 	bp++; /* parent.vnode */
85be080a6fSDavid Howells 	bp++; /* parent.unique */
8608e0e7c8SDavid Howells 	bp++; /* seg size */
87260a9803SDavid Howells 	status->mtime_client = ntohl(*bp++);
88260a9803SDavid Howells 	status->mtime_server = ntohl(*bp++);
89a0a5386aSEric W. Biederman 	group = make_kgid(&init_user_ns, ntohl(*bp++));
90a0a5386aSEric W. Biederman 	changed |= !gid_eq(group, status->group);
91a0a5386aSEric W. Biederman 	status->group = group;
9208e0e7c8SDavid Howells 	bp++; /* sync counter */
9308e0e7c8SDavid Howells 	data_version |= (u64) ntohl(*bp++) << 32;
94e8d6c554SDavid Howells 	EXTRACT(status->lock_count);
95260a9803SDavid Howells 	size |= (u64) ntohl(*bp++) << 32;
9608e0e7c8SDavid Howells 	bp++; /* spare 4 */
9708e0e7c8SDavid Howells 	*_bp = bp;
9808e0e7c8SDavid Howells 
99260a9803SDavid Howells 	if (size != status->size) {
100260a9803SDavid Howells 		status->size = size;
101260a9803SDavid Howells 		changed |= true;
102260a9803SDavid Howells 	}
103260a9803SDavid Howells 	status->mode &= S_IALLUGO;
10408e0e7c8SDavid Howells 
105260a9803SDavid Howells 	_debug("vnode time %lx, %lx",
106260a9803SDavid Howells 	       status->mtime_client, status->mtime_server);
107260a9803SDavid Howells 
108260a9803SDavid Howells 	if (vnode) {
109260a9803SDavid Howells 		if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
110260a9803SDavid Howells 			_debug("vnode changed");
111260a9803SDavid Howells 			i_size_write(&vnode->vfs_inode, size);
112260a9803SDavid Howells 			vnode->vfs_inode.i_uid = status->owner;
113260a9803SDavid Howells 			vnode->vfs_inode.i_gid = status->group;
114d6e43f75SDavid Howells 			vnode->vfs_inode.i_generation = vnode->fid.unique;
115bfe86848SMiklos Szeredi 			set_nlink(&vnode->vfs_inode, status->nlink);
116260a9803SDavid Howells 
11708e0e7c8SDavid Howells 			mode = vnode->vfs_inode.i_mode;
11808e0e7c8SDavid Howells 			mode &= ~S_IALLUGO;
119260a9803SDavid Howells 			mode |= status->mode;
120260a9803SDavid Howells 			barrier();
12108e0e7c8SDavid Howells 			vnode->vfs_inode.i_mode = mode;
12208e0e7c8SDavid Howells 		}
12308e0e7c8SDavid Howells 
124ab94f5d0SMarc Dionne 		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_client;
12508e0e7c8SDavid Howells 		vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
12608e0e7c8SDavid Howells 		vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
127d6e43f75SDavid Howells 		vnode->vfs_inode.i_version	= data_version;
128260a9803SDavid Howells 	}
12908e0e7c8SDavid Howells 
13031143d5dSDavid Howells 	expected_version = status->data_version;
13131143d5dSDavid Howells 	if (store_version)
13231143d5dSDavid Howells 		expected_version = *store_version;
13331143d5dSDavid Howells 
13431143d5dSDavid Howells 	if (expected_version != data_version) {
135260a9803SDavid Howells 		status->data_version = data_version;
136260a9803SDavid Howells 		if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
137260a9803SDavid Howells 			_debug("vnode modified %llx on {%x:%u}",
138ba3e0e1aSDavid S. Miller 			       (unsigned long long) data_version,
139ba3e0e1aSDavid S. Miller 			       vnode->fid.vid, vnode->fid.vnode);
140c435ee34SDavid Howells 			set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
14108e0e7c8SDavid Howells 			set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
1421da177e4SLinus Torvalds 		}
14331143d5dSDavid Howells 	} else if (store_version) {
14431143d5dSDavid Howells 		status->data_version = data_version;
145ec26815aSDavid Howells 	}
146c435ee34SDavid Howells 
147d2ddc776SDavid Howells 	if (vnode)
148c435ee34SDavid Howells 		write_sequnlock(&vnode->cb_lock);
149260a9803SDavid Howells }
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds /*
15208e0e7c8SDavid Howells  * decode an AFSCallBack block
1531da177e4SLinus Torvalds  */
154c435ee34SDavid Howells static void xdr_decode_AFSCallBack(struct afs_call *call,
155c435ee34SDavid Howells 				   struct afs_vnode *vnode,
156c435ee34SDavid Howells 				   const __be32 **_bp)
1571da177e4SLinus Torvalds {
158d2ddc776SDavid Howells 	struct afs_cb_interest *old, *cbi = call->cbi;
15908e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
160c435ee34SDavid Howells 	u32 cb_expiry;
1611da177e4SLinus Torvalds 
162c435ee34SDavid Howells 	write_seqlock(&vnode->cb_lock);
163c435ee34SDavid Howells 
164d2ddc776SDavid Howells 	if (call->cb_break == (vnode->cb_break + cbi->server->cb_s_break)) {
16508e0e7c8SDavid Howells 		vnode->cb_version	= ntohl(*bp++);
166c435ee34SDavid Howells 		cb_expiry		= ntohl(*bp++);
16708e0e7c8SDavid Howells 		vnode->cb_type		= ntohl(*bp++);
168c435ee34SDavid Howells 		vnode->cb_expires_at	= cb_expiry + ktime_get_real_seconds();
169d2ddc776SDavid Howells 		old = vnode->cb_interest;
170d2ddc776SDavid Howells 		if (old != call->cbi) {
171d2ddc776SDavid Howells 			vnode->cb_interest = cbi;
172d2ddc776SDavid Howells 			cbi = old;
173d2ddc776SDavid Howells 		}
174c435ee34SDavid Howells 		set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
175c435ee34SDavid Howells 	} else {
176c435ee34SDavid Howells 		bp += 3;
177c435ee34SDavid Howells 	}
178c435ee34SDavid Howells 
179c435ee34SDavid Howells 	write_sequnlock(&vnode->cb_lock);
180d2ddc776SDavid Howells 	call->cbi = cbi;
18108e0e7c8SDavid Howells 	*_bp = bp;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds 
184260a9803SDavid Howells static void xdr_decode_AFSCallBack_raw(const __be32 **_bp,
185260a9803SDavid Howells 				       struct afs_callback *cb)
186260a9803SDavid Howells {
187260a9803SDavid Howells 	const __be32 *bp = *_bp;
188260a9803SDavid Howells 
189260a9803SDavid Howells 	cb->version	= ntohl(*bp++);
190260a9803SDavid Howells 	cb->expiry	= ntohl(*bp++);
191260a9803SDavid Howells 	cb->type	= ntohl(*bp++);
192260a9803SDavid Howells 	*_bp = bp;
193260a9803SDavid Howells }
194260a9803SDavid Howells 
1951da177e4SLinus Torvalds /*
19608e0e7c8SDavid Howells  * decode an AFSVolSync block
1971da177e4SLinus Torvalds  */
19808e0e7c8SDavid Howells static void xdr_decode_AFSVolSync(const __be32 **_bp,
19908e0e7c8SDavid Howells 				  struct afs_volsync *volsync)
2001da177e4SLinus Torvalds {
20108e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
2021da177e4SLinus Torvalds 
20308e0e7c8SDavid Howells 	volsync->creation = ntohl(*bp++);
20408e0e7c8SDavid Howells 	bp++; /* spare2 */
20508e0e7c8SDavid Howells 	bp++; /* spare3 */
20608e0e7c8SDavid Howells 	bp++; /* spare4 */
20708e0e7c8SDavid Howells 	bp++; /* spare5 */
20808e0e7c8SDavid Howells 	bp++; /* spare6 */
20908e0e7c8SDavid Howells 	*_bp = bp;
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
21208e0e7c8SDavid Howells /*
21331143d5dSDavid Howells  * encode the requested attributes into an AFSStoreStatus block
21431143d5dSDavid Howells  */
21531143d5dSDavid Howells static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr)
21631143d5dSDavid Howells {
21731143d5dSDavid Howells 	__be32 *bp = *_bp;
21831143d5dSDavid Howells 	u32 mask = 0, mtime = 0, owner = 0, group = 0, mode = 0;
21931143d5dSDavid Howells 
22031143d5dSDavid Howells 	mask = 0;
22131143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MTIME) {
22231143d5dSDavid Howells 		mask |= AFS_SET_MTIME;
22331143d5dSDavid Howells 		mtime = attr->ia_mtime.tv_sec;
22431143d5dSDavid Howells 	}
22531143d5dSDavid Howells 
22631143d5dSDavid Howells 	if (attr->ia_valid & ATTR_UID) {
22731143d5dSDavid Howells 		mask |= AFS_SET_OWNER;
228a0a5386aSEric W. Biederman 		owner = from_kuid(&init_user_ns, attr->ia_uid);
22931143d5dSDavid Howells 	}
23031143d5dSDavid Howells 
23131143d5dSDavid Howells 	if (attr->ia_valid & ATTR_GID) {
23231143d5dSDavid Howells 		mask |= AFS_SET_GROUP;
233a0a5386aSEric W. Biederman 		group = from_kgid(&init_user_ns, attr->ia_gid);
23431143d5dSDavid Howells 	}
23531143d5dSDavid Howells 
23631143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MODE) {
23731143d5dSDavid Howells 		mask |= AFS_SET_MODE;
23831143d5dSDavid Howells 		mode = attr->ia_mode & S_IALLUGO;
23931143d5dSDavid Howells 	}
24031143d5dSDavid Howells 
24131143d5dSDavid Howells 	*bp++ = htonl(mask);
24231143d5dSDavid Howells 	*bp++ = htonl(mtime);
24331143d5dSDavid Howells 	*bp++ = htonl(owner);
24431143d5dSDavid Howells 	*bp++ = htonl(group);
24531143d5dSDavid Howells 	*bp++ = htonl(mode);
24631143d5dSDavid Howells 	*bp++ = 0;		/* segment size */
24731143d5dSDavid Howells 	*_bp = bp;
24831143d5dSDavid Howells }
24931143d5dSDavid Howells 
25031143d5dSDavid Howells /*
25145222b9eSDavid Howells  * decode an AFSFetchVolumeStatus block
25245222b9eSDavid Howells  */
25345222b9eSDavid Howells static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp,
25445222b9eSDavid Howells 					    struct afs_volume_status *vs)
25545222b9eSDavid Howells {
25645222b9eSDavid Howells 	const __be32 *bp = *_bp;
25745222b9eSDavid Howells 
25845222b9eSDavid Howells 	vs->vid			= ntohl(*bp++);
25945222b9eSDavid Howells 	vs->parent_id		= ntohl(*bp++);
26045222b9eSDavid Howells 	vs->online		= ntohl(*bp++);
26145222b9eSDavid Howells 	vs->in_service		= ntohl(*bp++);
26245222b9eSDavid Howells 	vs->blessed		= ntohl(*bp++);
26345222b9eSDavid Howells 	vs->needs_salvage	= ntohl(*bp++);
26445222b9eSDavid Howells 	vs->type		= ntohl(*bp++);
26545222b9eSDavid Howells 	vs->min_quota		= ntohl(*bp++);
26645222b9eSDavid Howells 	vs->max_quota		= ntohl(*bp++);
26745222b9eSDavid Howells 	vs->blocks_in_use	= ntohl(*bp++);
26845222b9eSDavid Howells 	vs->part_blocks_avail	= ntohl(*bp++);
26945222b9eSDavid Howells 	vs->part_max_blocks	= ntohl(*bp++);
27045222b9eSDavid Howells 	*_bp = bp;
27145222b9eSDavid Howells }
27245222b9eSDavid Howells 
27345222b9eSDavid Howells /*
27408e0e7c8SDavid Howells  * deliver reply data to an FS.FetchStatus
27508e0e7c8SDavid Howells  */
276d001648eSDavid Howells static int afs_deliver_fs_fetch_status(struct afs_call *call)
27708e0e7c8SDavid Howells {
27897e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
27908e0e7c8SDavid Howells 	const __be32 *bp;
280372ee163SDavid Howells 	int ret;
2811da177e4SLinus Torvalds 
282d001648eSDavid Howells 	ret = afs_transfer_reply(call);
283372ee163SDavid Howells 	if (ret < 0)
284372ee163SDavid Howells 		return ret;
2851da177e4SLinus Torvalds 
286c435ee34SDavid Howells 	_enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode);
287c435ee34SDavid Howells 
28808e0e7c8SDavid Howells 	/* unmarshall the reply once we've received all of it */
28908e0e7c8SDavid Howells 	bp = call->buffer;
29031143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
291c435ee34SDavid Howells 	xdr_decode_AFSCallBack(call, vnode, &bp);
29297e3043aSDavid Howells 	if (call->reply[1])
29397e3043aSDavid Howells 		xdr_decode_AFSVolSync(&bp, call->reply[1]);
2941da177e4SLinus Torvalds 
29508e0e7c8SDavid Howells 	_leave(" = 0 [done]");
29608e0e7c8SDavid Howells 	return 0;
297ec26815aSDavid Howells }
29808e0e7c8SDavid Howells 
29908e0e7c8SDavid Howells /*
30008e0e7c8SDavid Howells  * FS.FetchStatus operation type
30108e0e7c8SDavid Howells  */
30208e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchStatus = {
30300d3b7a4SDavid Howells 	.name		= "FS.FetchStatus",
304025db80cSDavid Howells 	.op		= afs_FS_FetchStatus,
30508e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_status,
30608e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
30708e0e7c8SDavid Howells };
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds /*
3101da177e4SLinus Torvalds  * fetch the status information for a file
3111da177e4SLinus Torvalds  */
312d2ddc776SDavid Howells int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsync)
3131da177e4SLinus Torvalds {
314d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
31508e0e7c8SDavid Howells 	struct afs_call *call;
316f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
3171da177e4SLinus Torvalds 	__be32 *bp;
3181da177e4SLinus Torvalds 
319416351f2SDavid Howells 	_enter(",%x,{%x:%u},,",
320d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
3211da177e4SLinus Torvalds 
322f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
323d2ddc776SDavid Howells 	if (!call) {
324d2ddc776SDavid Howells 		fc->ac.error = -ENOMEM;
32508e0e7c8SDavid Howells 		return -ENOMEM;
326d2ddc776SDavid Howells 	}
3271da177e4SLinus Torvalds 
328d2ddc776SDavid Howells 	call->key = fc->key;
32997e3043aSDavid Howells 	call->reply[0] = vnode;
33097e3043aSDavid Howells 	call->reply[1] = volsync;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	/* marshall the parameters */
33308e0e7c8SDavid Howells 	bp = call->request;
3341da177e4SLinus Torvalds 	bp[0] = htonl(FSFETCHSTATUS);
3351da177e4SLinus Torvalds 	bp[1] = htonl(vnode->fid.vid);
3361da177e4SLinus Torvalds 	bp[2] = htonl(vnode->fid.vnode);
3371da177e4SLinus Torvalds 	bp[3] = htonl(vnode->fid.unique);
3381da177e4SLinus Torvalds 
339d2ddc776SDavid Howells 	call->cb_break = fc->cb_break;
340d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
341025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
342d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
343ec26815aSDavid Howells }
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds /*
34608e0e7c8SDavid Howells  * deliver reply data to an FS.FetchData
3471da177e4SLinus Torvalds  */
348d001648eSDavid Howells static int afs_deliver_fs_fetch_data(struct afs_call *call)
3491da177e4SLinus Torvalds {
35097e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
35197e3043aSDavid Howells 	struct afs_read *req = call->reply[2];
35208e0e7c8SDavid Howells 	const __be32 *bp;
353196ee9cdSDavid Howells 	unsigned int size;
35408e0e7c8SDavid Howells 	void *buffer;
3551da177e4SLinus Torvalds 	int ret;
3561da177e4SLinus Torvalds 
3576a0e3999SDavid Howells 	_enter("{%u,%zu/%u;%llu/%llu}",
358196ee9cdSDavid Howells 	       call->unmarshall, call->offset, call->count,
359196ee9cdSDavid Howells 	       req->remain, req->actual_len);
3601da177e4SLinus Torvalds 
36108e0e7c8SDavid Howells 	switch (call->unmarshall) {
36208e0e7c8SDavid Howells 	case 0:
363196ee9cdSDavid Howells 		req->actual_len = 0;
36408e0e7c8SDavid Howells 		call->offset = 0;
36508e0e7c8SDavid Howells 		call->unmarshall++;
366b9b1f8d5SDavid Howells 		if (call->operation_ID != FSFETCHDATA64) {
367b9b1f8d5SDavid Howells 			call->unmarshall++;
368b9b1f8d5SDavid Howells 			goto no_msw;
369b9b1f8d5SDavid Howells 		}
3701da177e4SLinus Torvalds 
371b9b1f8d5SDavid Howells 		/* extract the upper part of the returned data length of an
372b9b1f8d5SDavid Howells 		 * FSFETCHDATA64 op (which should always be 0 using this
373b9b1f8d5SDavid Howells 		 * client) */
37408e0e7c8SDavid Howells 	case 1:
375b9b1f8d5SDavid Howells 		_debug("extract data length (MSW)");
376d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
377372ee163SDavid Howells 		if (ret < 0)
378372ee163SDavid Howells 			return ret;
379b9b1f8d5SDavid Howells 
380196ee9cdSDavid Howells 		req->actual_len = ntohl(call->tmp);
381196ee9cdSDavid Howells 		req->actual_len <<= 32;
382b9b1f8d5SDavid Howells 		call->offset = 0;
383b9b1f8d5SDavid Howells 		call->unmarshall++;
384b9b1f8d5SDavid Howells 
385b9b1f8d5SDavid Howells 	no_msw:
386b9b1f8d5SDavid Howells 		/* extract the returned data length */
387b9b1f8d5SDavid Howells 	case 2:
38808e0e7c8SDavid Howells 		_debug("extract data length");
389d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
390372ee163SDavid Howells 		if (ret < 0)
391372ee163SDavid Howells 			return ret;
3921da177e4SLinus Torvalds 
393196ee9cdSDavid Howells 		req->actual_len |= ntohl(call->tmp);
394196ee9cdSDavid Howells 		_debug("DATA length: %llu", req->actual_len);
395196ee9cdSDavid Howells 
396196ee9cdSDavid Howells 		req->remain = req->actual_len;
397196ee9cdSDavid Howells 		call->offset = req->pos & (PAGE_SIZE - 1);
398196ee9cdSDavid Howells 		req->index = 0;
399196ee9cdSDavid Howells 		if (req->actual_len == 0)
400196ee9cdSDavid Howells 			goto no_more_data;
40108e0e7c8SDavid Howells 		call->unmarshall++;
4021da177e4SLinus Torvalds 
403196ee9cdSDavid Howells 	begin_page:
4046db3ac3cSDavid Howells 		ASSERTCMP(req->index, <, req->nr_pages);
405196ee9cdSDavid Howells 		if (req->remain > PAGE_SIZE - call->offset)
406196ee9cdSDavid Howells 			size = PAGE_SIZE - call->offset;
407196ee9cdSDavid Howells 		else
408196ee9cdSDavid Howells 			size = req->remain;
409196ee9cdSDavid Howells 		call->count = call->offset + size;
410196ee9cdSDavid Howells 		ASSERTCMP(call->count, <=, PAGE_SIZE);
411196ee9cdSDavid Howells 		req->remain -= size;
412196ee9cdSDavid Howells 
41308e0e7c8SDavid Howells 		/* extract the returned data */
414b9b1f8d5SDavid Howells 	case 3:
4156a0e3999SDavid Howells 		_debug("extract data %llu/%llu %zu/%u",
416196ee9cdSDavid Howells 		       req->remain, req->actual_len, call->offset, call->count);
417196ee9cdSDavid Howells 
418196ee9cdSDavid Howells 		buffer = kmap(req->pages[req->index]);
419196ee9cdSDavid Howells 		ret = afs_extract_data(call, buffer, call->count, true);
420196ee9cdSDavid Howells 		kunmap(req->pages[req->index]);
421372ee163SDavid Howells 		if (ret < 0)
422372ee163SDavid Howells 			return ret;
423196ee9cdSDavid Howells 		if (call->offset == PAGE_SIZE) {
424196ee9cdSDavid Howells 			if (req->page_done)
425196ee9cdSDavid Howells 				req->page_done(call, req);
42629f06985SDavid Howells 			req->index++;
427196ee9cdSDavid Howells 			if (req->remain > 0) {
428196ee9cdSDavid Howells 				call->offset = 0;
429e8e581a8SDavid Howells 				if (req->index >= req->nr_pages) {
430e8e581a8SDavid Howells 					call->unmarshall = 4;
4316db3ac3cSDavid Howells 					goto begin_discard;
432e8e581a8SDavid Howells 				}
433196ee9cdSDavid Howells 				goto begin_page;
434196ee9cdSDavid Howells 			}
435416351f2SDavid Howells 		}
4366db3ac3cSDavid Howells 		goto no_more_data;
4376db3ac3cSDavid Howells 
4386db3ac3cSDavid Howells 		/* Discard any excess data the server gave us */
4396db3ac3cSDavid Howells 	begin_discard:
4406db3ac3cSDavid Howells 	case 4:
4416a0e3999SDavid Howells 		size = min_t(loff_t, sizeof(afs_discard_buffer), req->remain);
4426db3ac3cSDavid Howells 		call->count = size;
4436a0e3999SDavid Howells 		_debug("extract discard %llu/%llu %zu/%u",
4446db3ac3cSDavid Howells 		       req->remain, req->actual_len, call->offset, call->count);
4456db3ac3cSDavid Howells 
4466db3ac3cSDavid Howells 		call->offset = 0;
4476db3ac3cSDavid Howells 		ret = afs_extract_data(call, afs_discard_buffer, call->count, true);
4486db3ac3cSDavid Howells 		req->remain -= call->offset;
4496db3ac3cSDavid Howells 		if (ret < 0)
4506db3ac3cSDavid Howells 			return ret;
4516db3ac3cSDavid Howells 		if (req->remain > 0)
4526db3ac3cSDavid Howells 			goto begin_discard;
4531da177e4SLinus Torvalds 
454196ee9cdSDavid Howells 	no_more_data:
45508e0e7c8SDavid Howells 		call->offset = 0;
4566db3ac3cSDavid Howells 		call->unmarshall = 5;
45708e0e7c8SDavid Howells 
45808e0e7c8SDavid Howells 		/* extract the metadata */
4596db3ac3cSDavid Howells 	case 5:
460d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
461d001648eSDavid Howells 				       (21 + 3 + 6) * 4, false);
462372ee163SDavid Howells 		if (ret < 0)
463372ee163SDavid Howells 			return ret;
4641da177e4SLinus Torvalds 
46508e0e7c8SDavid Howells 		bp = call->buffer;
46631143d5dSDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
467c435ee34SDavid Howells 		xdr_decode_AFSCallBack(call, vnode, &bp);
46897e3043aSDavid Howells 		if (call->reply[1])
46997e3043aSDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply[1]);
4701da177e4SLinus Torvalds 
47108e0e7c8SDavid Howells 		call->offset = 0;
47208e0e7c8SDavid Howells 		call->unmarshall++;
4731da177e4SLinus Torvalds 
4746db3ac3cSDavid Howells 	case 6:
4751da177e4SLinus Torvalds 		break;
4761da177e4SLinus Torvalds 	}
4771da177e4SLinus Torvalds 
4786db3ac3cSDavid Howells 	for (; req->index < req->nr_pages; req->index++) {
4796db3ac3cSDavid Howells 		if (call->count < PAGE_SIZE)
4806db3ac3cSDavid Howells 			zero_user_segment(req->pages[req->index],
4816db3ac3cSDavid Howells 					  call->count, PAGE_SIZE);
482196ee9cdSDavid Howells 		if (req->page_done)
483196ee9cdSDavid Howells 			req->page_done(call, req);
4846db3ac3cSDavid Howells 		call->count = 0;
485416351f2SDavid Howells 	}
486416351f2SDavid Howells 
48708e0e7c8SDavid Howells 	_leave(" = 0 [done]");
48808e0e7c8SDavid Howells 	return 0;
489ec26815aSDavid Howells }
4901da177e4SLinus Torvalds 
491196ee9cdSDavid Howells static void afs_fetch_data_destructor(struct afs_call *call)
492196ee9cdSDavid Howells {
49397e3043aSDavid Howells 	struct afs_read *req = call->reply[2];
494196ee9cdSDavid Howells 
495196ee9cdSDavid Howells 	afs_put_read(req);
496196ee9cdSDavid Howells 	afs_flat_call_destructor(call);
497196ee9cdSDavid Howells }
498196ee9cdSDavid Howells 
4991da177e4SLinus Torvalds /*
50008e0e7c8SDavid Howells  * FS.FetchData operation type
5011da177e4SLinus Torvalds  */
50208e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchData = {
50300d3b7a4SDavid Howells 	.name		= "FS.FetchData",
504025db80cSDavid Howells 	.op		= afs_FS_FetchData,
50508e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
506196ee9cdSDavid Howells 	.destructor	= afs_fetch_data_destructor,
50708e0e7c8SDavid Howells };
50808e0e7c8SDavid Howells 
509b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSFetchData64 = {
510b9b1f8d5SDavid Howells 	.name		= "FS.FetchData64",
511025db80cSDavid Howells 	.op		= afs_FS_FetchData64,
512b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
513196ee9cdSDavid Howells 	.destructor	= afs_fetch_data_destructor,
514b9b1f8d5SDavid Howells };
515b9b1f8d5SDavid Howells 
516b9b1f8d5SDavid Howells /*
517b9b1f8d5SDavid Howells  * fetch data from a very large file
518b9b1f8d5SDavid Howells  */
519d2ddc776SDavid Howells static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, struct afs_read *req)
520b9b1f8d5SDavid Howells {
521d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
522b9b1f8d5SDavid Howells 	struct afs_call *call;
523f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
524b9b1f8d5SDavid Howells 	__be32 *bp;
525b9b1f8d5SDavid Howells 
526b9b1f8d5SDavid Howells 	_enter("");
527b9b1f8d5SDavid Howells 
528f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
529b9b1f8d5SDavid Howells 	if (!call)
530b9b1f8d5SDavid Howells 		return -ENOMEM;
531b9b1f8d5SDavid Howells 
532d2ddc776SDavid Howells 	call->key = fc->key;
53397e3043aSDavid Howells 	call->reply[0] = vnode;
53497e3043aSDavid Howells 	call->reply[1] = NULL; /* volsync */
53597e3043aSDavid Howells 	call->reply[2] = req;
536b9b1f8d5SDavid Howells 
537b9b1f8d5SDavid Howells 	/* marshall the parameters */
538b9b1f8d5SDavid Howells 	bp = call->request;
539b9b1f8d5SDavid Howells 	bp[0] = htonl(FSFETCHDATA64);
540b9b1f8d5SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
541b9b1f8d5SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
542b9b1f8d5SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
543196ee9cdSDavid Howells 	bp[4] = htonl(upper_32_bits(req->pos));
544196ee9cdSDavid Howells 	bp[5] = htonl(lower_32_bits(req->pos));
545b9b1f8d5SDavid Howells 	bp[6] = 0;
546196ee9cdSDavid Howells 	bp[7] = htonl(lower_32_bits(req->len));
547b9b1f8d5SDavid Howells 
548196ee9cdSDavid Howells 	atomic_inc(&req->usage);
549d2ddc776SDavid Howells 	call->cb_break = fc->cb_break;
550d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
551025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
552d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
553b9b1f8d5SDavid Howells }
554b9b1f8d5SDavid Howells 
55508e0e7c8SDavid Howells /*
55608e0e7c8SDavid Howells  * fetch data from a file
55708e0e7c8SDavid Howells  */
558d2ddc776SDavid Howells int afs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
5591da177e4SLinus Torvalds {
560d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
56108e0e7c8SDavid Howells 	struct afs_call *call;
562f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
56308e0e7c8SDavid Howells 	__be32 *bp;
5641da177e4SLinus Torvalds 
565196ee9cdSDavid Howells 	if (upper_32_bits(req->pos) ||
566196ee9cdSDavid Howells 	    upper_32_bits(req->len) ||
567196ee9cdSDavid Howells 	    upper_32_bits(req->pos + req->len))
568d2ddc776SDavid Howells 		return afs_fs_fetch_data64(fc, req);
569b9b1f8d5SDavid Howells 
57008e0e7c8SDavid Howells 	_enter("");
5711da177e4SLinus Torvalds 
572f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
57308e0e7c8SDavid Howells 	if (!call)
57408e0e7c8SDavid Howells 		return -ENOMEM;
5751da177e4SLinus Torvalds 
576d2ddc776SDavid Howells 	call->key = fc->key;
57797e3043aSDavid Howells 	call->reply[0] = vnode;
57897e3043aSDavid Howells 	call->reply[1] = NULL; /* volsync */
57997e3043aSDavid Howells 	call->reply[2] = req;
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	/* marshall the parameters */
58208e0e7c8SDavid Howells 	bp = call->request;
58308e0e7c8SDavid Howells 	bp[0] = htonl(FSFETCHDATA);
58408e0e7c8SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
58508e0e7c8SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
58608e0e7c8SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
587196ee9cdSDavid Howells 	bp[4] = htonl(lower_32_bits(req->pos));
588196ee9cdSDavid Howells 	bp[5] = htonl(lower_32_bits(req->len));
5891da177e4SLinus Torvalds 
590196ee9cdSDavid Howells 	atomic_inc(&req->usage);
591d2ddc776SDavid Howells 	call->cb_break = fc->cb_break;
592d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
593025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
594d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
59508e0e7c8SDavid Howells }
596260a9803SDavid Howells 
597260a9803SDavid Howells /*
598260a9803SDavid Howells  * deliver reply data to an FS.CreateFile or an FS.MakeDir
599260a9803SDavid Howells  */
600d001648eSDavid Howells static int afs_deliver_fs_create_vnode(struct afs_call *call)
601260a9803SDavid Howells {
60297e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
603260a9803SDavid Howells 	const __be32 *bp;
604372ee163SDavid Howells 	int ret;
605260a9803SDavid Howells 
606d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
607260a9803SDavid Howells 
608d001648eSDavid Howells 	ret = afs_transfer_reply(call);
609372ee163SDavid Howells 	if (ret < 0)
610372ee163SDavid Howells 		return ret;
611260a9803SDavid Howells 
612260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
613260a9803SDavid Howells 	bp = call->buffer;
61497e3043aSDavid Howells 	xdr_decode_AFSFid(&bp, call->reply[1]);
61597e3043aSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL);
61631143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
61797e3043aSDavid Howells 	xdr_decode_AFSCallBack_raw(&bp, call->reply[3]);
61897e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
619260a9803SDavid Howells 
620260a9803SDavid Howells 	_leave(" = 0 [done]");
621260a9803SDavid Howells 	return 0;
622260a9803SDavid Howells }
623260a9803SDavid Howells 
624260a9803SDavid Howells /*
625260a9803SDavid Howells  * FS.CreateFile and FS.MakeDir operation type
626260a9803SDavid Howells  */
627025db80cSDavid Howells static const struct afs_call_type afs_RXFSCreateFile = {
628025db80cSDavid Howells 	.name		= "FS.CreateFile",
629025db80cSDavid Howells 	.op		= afs_FS_CreateFile,
630025db80cSDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
631025db80cSDavid Howells 	.destructor	= afs_flat_call_destructor,
632025db80cSDavid Howells };
633025db80cSDavid Howells 
634025db80cSDavid Howells static const struct afs_call_type afs_RXFSMakeDir = {
635025db80cSDavid Howells 	.name		= "FS.MakeDir",
636025db80cSDavid Howells 	.op		= afs_FS_MakeDir,
637260a9803SDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
638260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
639260a9803SDavid Howells };
640260a9803SDavid Howells 
641260a9803SDavid Howells /*
642260a9803SDavid Howells  * create a file or make a directory
643260a9803SDavid Howells  */
6448b2a464cSDavid Howells int afs_fs_create(struct afs_fs_cursor *fc,
645260a9803SDavid Howells 		  const char *name,
646260a9803SDavid Howells 		  umode_t mode,
647260a9803SDavid Howells 		  struct afs_fid *newfid,
648260a9803SDavid Howells 		  struct afs_file_status *newstatus,
649d2ddc776SDavid Howells 		  struct afs_callback *newcb)
650260a9803SDavid Howells {
651d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
652260a9803SDavid Howells 	struct afs_call *call;
653f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
654260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
655260a9803SDavid Howells 	__be32 *bp;
656260a9803SDavid Howells 
657260a9803SDavid Howells 	_enter("");
658260a9803SDavid Howells 
659260a9803SDavid Howells 	namesz = strlen(name);
660260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
661260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (6 * 4);
662260a9803SDavid Howells 
663025db80cSDavid Howells 	call = afs_alloc_flat_call(
664025db80cSDavid Howells 		net, S_ISDIR(mode) ? &afs_RXFSMakeDir : &afs_RXFSCreateFile,
665025db80cSDavid Howells 		reqsz, (3 + 21 + 21 + 3 + 6) * 4);
666260a9803SDavid Howells 	if (!call)
667260a9803SDavid Howells 		return -ENOMEM;
668260a9803SDavid Howells 
669d2ddc776SDavid Howells 	call->key = fc->key;
67097e3043aSDavid Howells 	call->reply[0] = vnode;
67197e3043aSDavid Howells 	call->reply[1] = newfid;
67297e3043aSDavid Howells 	call->reply[2] = newstatus;
67397e3043aSDavid Howells 	call->reply[3] = newcb;
674260a9803SDavid Howells 
675260a9803SDavid Howells 	/* marshall the parameters */
676260a9803SDavid Howells 	bp = call->request;
677260a9803SDavid Howells 	*bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE);
678260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
679260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
680260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
681260a9803SDavid Howells 	*bp++ = htonl(namesz);
682260a9803SDavid Howells 	memcpy(bp, name, namesz);
683260a9803SDavid Howells 	bp = (void *) bp + namesz;
684260a9803SDavid Howells 	if (padsz > 0) {
685260a9803SDavid Howells 		memset(bp, 0, padsz);
686260a9803SDavid Howells 		bp = (void *) bp + padsz;
687260a9803SDavid Howells 	}
688ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
689ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
690260a9803SDavid Howells 	*bp++ = 0; /* owner */
691260a9803SDavid Howells 	*bp++ = 0; /* group */
692260a9803SDavid Howells 	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
693260a9803SDavid Howells 	*bp++ = 0; /* segment size */
694260a9803SDavid Howells 
695d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
696025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
697d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
698260a9803SDavid Howells }
699260a9803SDavid Howells 
700260a9803SDavid Howells /*
701260a9803SDavid Howells  * deliver reply data to an FS.RemoveFile or FS.RemoveDir
702260a9803SDavid Howells  */
703d001648eSDavid Howells static int afs_deliver_fs_remove(struct afs_call *call)
704260a9803SDavid Howells {
70597e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
706260a9803SDavid Howells 	const __be32 *bp;
707372ee163SDavid Howells 	int ret;
708260a9803SDavid Howells 
709d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
710260a9803SDavid Howells 
711d001648eSDavid Howells 	ret = afs_transfer_reply(call);
712372ee163SDavid Howells 	if (ret < 0)
713372ee163SDavid Howells 		return ret;
714260a9803SDavid Howells 
715260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
716260a9803SDavid Howells 	bp = call->buffer;
71731143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
71897e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
719260a9803SDavid Howells 
720260a9803SDavid Howells 	_leave(" = 0 [done]");
721260a9803SDavid Howells 	return 0;
722260a9803SDavid Howells }
723260a9803SDavid Howells 
724260a9803SDavid Howells /*
725260a9803SDavid Howells  * FS.RemoveDir/FS.RemoveFile operation type
726260a9803SDavid Howells  */
727025db80cSDavid Howells static const struct afs_call_type afs_RXFSRemoveFile = {
728025db80cSDavid Howells 	.name		= "FS.RemoveFile",
729025db80cSDavid Howells 	.op		= afs_FS_RemoveFile,
730025db80cSDavid Howells 	.deliver	= afs_deliver_fs_remove,
731025db80cSDavid Howells 	.destructor	= afs_flat_call_destructor,
732025db80cSDavid Howells };
733025db80cSDavid Howells 
734025db80cSDavid Howells static const struct afs_call_type afs_RXFSRemoveDir = {
735025db80cSDavid Howells 	.name		= "FS.RemoveDir",
736025db80cSDavid Howells 	.op		= afs_FS_RemoveDir,
737260a9803SDavid Howells 	.deliver	= afs_deliver_fs_remove,
738260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
739260a9803SDavid Howells };
740260a9803SDavid Howells 
741260a9803SDavid Howells /*
742260a9803SDavid Howells  * remove a file or directory
743260a9803SDavid Howells  */
744d2ddc776SDavid Howells int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir)
745260a9803SDavid Howells {
746d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
747260a9803SDavid Howells 	struct afs_call *call;
748f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
749260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
750260a9803SDavid Howells 	__be32 *bp;
751260a9803SDavid Howells 
752260a9803SDavid Howells 	_enter("");
753260a9803SDavid Howells 
754260a9803SDavid Howells 	namesz = strlen(name);
755260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
756260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz;
757260a9803SDavid Howells 
758025db80cSDavid Howells 	call = afs_alloc_flat_call(
759025db80cSDavid Howells 		net, isdir ? &afs_RXFSRemoveDir : &afs_RXFSRemoveFile,
760025db80cSDavid Howells 		reqsz, (21 + 6) * 4);
761260a9803SDavid Howells 	if (!call)
762260a9803SDavid Howells 		return -ENOMEM;
763260a9803SDavid Howells 
764d2ddc776SDavid Howells 	call->key = fc->key;
76597e3043aSDavid Howells 	call->reply[0] = vnode;
766260a9803SDavid Howells 
767260a9803SDavid Howells 	/* marshall the parameters */
768260a9803SDavid Howells 	bp = call->request;
769260a9803SDavid Howells 	*bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE);
770260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
771260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
772260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
773260a9803SDavid Howells 	*bp++ = htonl(namesz);
774260a9803SDavid Howells 	memcpy(bp, name, namesz);
775260a9803SDavid Howells 	bp = (void *) bp + namesz;
776260a9803SDavid Howells 	if (padsz > 0) {
777260a9803SDavid Howells 		memset(bp, 0, padsz);
778260a9803SDavid Howells 		bp = (void *) bp + padsz;
779260a9803SDavid Howells 	}
780260a9803SDavid Howells 
781d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
782025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
783d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
784260a9803SDavid Howells }
785260a9803SDavid Howells 
786260a9803SDavid Howells /*
787260a9803SDavid Howells  * deliver reply data to an FS.Link
788260a9803SDavid Howells  */
789d001648eSDavid Howells static int afs_deliver_fs_link(struct afs_call *call)
790260a9803SDavid Howells {
79197e3043aSDavid Howells 	struct afs_vnode *dvnode = call->reply[0], *vnode = call->reply[1];
792260a9803SDavid Howells 	const __be32 *bp;
793372ee163SDavid Howells 	int ret;
794260a9803SDavid Howells 
795d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
796260a9803SDavid Howells 
797d001648eSDavid Howells 	ret = afs_transfer_reply(call);
798372ee163SDavid Howells 	if (ret < 0)
799372ee163SDavid Howells 		return ret;
800260a9803SDavid Howells 
801260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
802260a9803SDavid Howells 	bp = call->buffer;
80331143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
80431143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode, NULL);
80597e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
806260a9803SDavid Howells 
807260a9803SDavid Howells 	_leave(" = 0 [done]");
808260a9803SDavid Howells 	return 0;
809260a9803SDavid Howells }
810260a9803SDavid Howells 
811260a9803SDavid Howells /*
812260a9803SDavid Howells  * FS.Link operation type
813260a9803SDavid Howells  */
814260a9803SDavid Howells static const struct afs_call_type afs_RXFSLink = {
815260a9803SDavid Howells 	.name		= "FS.Link",
816025db80cSDavid Howells 	.op		= afs_FS_Link,
817260a9803SDavid Howells 	.deliver	= afs_deliver_fs_link,
818260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
819260a9803SDavid Howells };
820260a9803SDavid Howells 
821260a9803SDavid Howells /*
822260a9803SDavid Howells  * make a hard link
823260a9803SDavid Howells  */
824d2ddc776SDavid Howells int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
825d2ddc776SDavid Howells 		const char *name)
826260a9803SDavid Howells {
827d2ddc776SDavid Howells 	struct afs_vnode *dvnode = fc->vnode;
828260a9803SDavid Howells 	struct afs_call *call;
829f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
830260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
831260a9803SDavid Howells 	__be32 *bp;
832260a9803SDavid Howells 
833260a9803SDavid Howells 	_enter("");
834260a9803SDavid Howells 
835260a9803SDavid Howells 	namesz = strlen(name);
836260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
837260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (3 * 4);
838260a9803SDavid Howells 
839f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
840260a9803SDavid Howells 	if (!call)
841260a9803SDavid Howells 		return -ENOMEM;
842260a9803SDavid Howells 
843d2ddc776SDavid Howells 	call->key = fc->key;
84497e3043aSDavid Howells 	call->reply[0] = dvnode;
84597e3043aSDavid Howells 	call->reply[1] = vnode;
846260a9803SDavid Howells 
847260a9803SDavid Howells 	/* marshall the parameters */
848260a9803SDavid Howells 	bp = call->request;
849260a9803SDavid Howells 	*bp++ = htonl(FSLINK);
850260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vid);
851260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vnode);
852260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.unique);
853260a9803SDavid Howells 	*bp++ = htonl(namesz);
854260a9803SDavid Howells 	memcpy(bp, name, namesz);
855260a9803SDavid Howells 	bp = (void *) bp + namesz;
856260a9803SDavid Howells 	if (padsz > 0) {
857260a9803SDavid Howells 		memset(bp, 0, padsz);
858260a9803SDavid Howells 		bp = (void *) bp + padsz;
859260a9803SDavid Howells 	}
860260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
861260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
862260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
863260a9803SDavid Howells 
864d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
865025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
866d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
867260a9803SDavid Howells }
868260a9803SDavid Howells 
869260a9803SDavid Howells /*
870260a9803SDavid Howells  * deliver reply data to an FS.Symlink
871260a9803SDavid Howells  */
872d001648eSDavid Howells static int afs_deliver_fs_symlink(struct afs_call *call)
873260a9803SDavid Howells {
87497e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
875260a9803SDavid Howells 	const __be32 *bp;
876372ee163SDavid Howells 	int ret;
877260a9803SDavid Howells 
878d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
879260a9803SDavid Howells 
880d001648eSDavid Howells 	ret = afs_transfer_reply(call);
881372ee163SDavid Howells 	if (ret < 0)
882372ee163SDavid Howells 		return ret;
883260a9803SDavid Howells 
884260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
885260a9803SDavid Howells 	bp = call->buffer;
88697e3043aSDavid Howells 	xdr_decode_AFSFid(&bp, call->reply[1]);
88797e3043aSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL);
88831143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
88997e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
890260a9803SDavid Howells 
891260a9803SDavid Howells 	_leave(" = 0 [done]");
892260a9803SDavid Howells 	return 0;
893260a9803SDavid Howells }
894260a9803SDavid Howells 
895260a9803SDavid Howells /*
896260a9803SDavid Howells  * FS.Symlink operation type
897260a9803SDavid Howells  */
898260a9803SDavid Howells static const struct afs_call_type afs_RXFSSymlink = {
899260a9803SDavid Howells 	.name		= "FS.Symlink",
900025db80cSDavid Howells 	.op		= afs_FS_Symlink,
901260a9803SDavid Howells 	.deliver	= afs_deliver_fs_symlink,
902260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
903260a9803SDavid Howells };
904260a9803SDavid Howells 
905260a9803SDavid Howells /*
906260a9803SDavid Howells  * create a symbolic link
907260a9803SDavid Howells  */
9088b2a464cSDavid Howells int afs_fs_symlink(struct afs_fs_cursor *fc,
909260a9803SDavid Howells 		   const char *name,
910260a9803SDavid Howells 		   const char *contents,
911260a9803SDavid Howells 		   struct afs_fid *newfid,
912d2ddc776SDavid Howells 		   struct afs_file_status *newstatus)
913260a9803SDavid Howells {
914d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
915260a9803SDavid Howells 	struct afs_call *call;
916f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
917260a9803SDavid Howells 	size_t namesz, reqsz, padsz, c_namesz, c_padsz;
918260a9803SDavid Howells 	__be32 *bp;
919260a9803SDavid Howells 
920260a9803SDavid Howells 	_enter("");
921260a9803SDavid Howells 
922260a9803SDavid Howells 	namesz = strlen(name);
923260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
924260a9803SDavid Howells 
925260a9803SDavid Howells 	c_namesz = strlen(contents);
926260a9803SDavid Howells 	c_padsz = (4 - (c_namesz & 3)) & 3;
927260a9803SDavid Howells 
928260a9803SDavid Howells 	reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
929260a9803SDavid Howells 
930f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSSymlink, reqsz,
931260a9803SDavid Howells 				   (3 + 21 + 21 + 6) * 4);
932260a9803SDavid Howells 	if (!call)
933260a9803SDavid Howells 		return -ENOMEM;
934260a9803SDavid Howells 
935d2ddc776SDavid Howells 	call->key = fc->key;
93697e3043aSDavid Howells 	call->reply[0] = vnode;
93797e3043aSDavid Howells 	call->reply[1] = newfid;
93897e3043aSDavid Howells 	call->reply[2] = newstatus;
939260a9803SDavid Howells 
940260a9803SDavid Howells 	/* marshall the parameters */
941260a9803SDavid Howells 	bp = call->request;
942260a9803SDavid Howells 	*bp++ = htonl(FSSYMLINK);
943260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
944260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
945260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
946260a9803SDavid Howells 	*bp++ = htonl(namesz);
947260a9803SDavid Howells 	memcpy(bp, name, namesz);
948260a9803SDavid Howells 	bp = (void *) bp + namesz;
949260a9803SDavid Howells 	if (padsz > 0) {
950260a9803SDavid Howells 		memset(bp, 0, padsz);
951260a9803SDavid Howells 		bp = (void *) bp + padsz;
952260a9803SDavid Howells 	}
953260a9803SDavid Howells 	*bp++ = htonl(c_namesz);
954260a9803SDavid Howells 	memcpy(bp, contents, c_namesz);
955260a9803SDavid Howells 	bp = (void *) bp + c_namesz;
956260a9803SDavid Howells 	if (c_padsz > 0) {
957260a9803SDavid Howells 		memset(bp, 0, c_padsz);
958260a9803SDavid Howells 		bp = (void *) bp + c_padsz;
959260a9803SDavid Howells 	}
960ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
961ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
962260a9803SDavid Howells 	*bp++ = 0; /* owner */
963260a9803SDavid Howells 	*bp++ = 0; /* group */
964260a9803SDavid Howells 	*bp++ = htonl(S_IRWXUGO); /* unix mode */
965260a9803SDavid Howells 	*bp++ = 0; /* segment size */
966260a9803SDavid Howells 
967d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
968025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
969d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
970260a9803SDavid Howells }
971260a9803SDavid Howells 
972260a9803SDavid Howells /*
973260a9803SDavid Howells  * deliver reply data to an FS.Rename
974260a9803SDavid Howells  */
975d001648eSDavid Howells static int afs_deliver_fs_rename(struct afs_call *call)
976260a9803SDavid Howells {
97797e3043aSDavid Howells 	struct afs_vnode *orig_dvnode = call->reply[0], *new_dvnode = call->reply[1];
978260a9803SDavid Howells 	const __be32 *bp;
979372ee163SDavid Howells 	int ret;
980260a9803SDavid Howells 
981d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
982260a9803SDavid Howells 
983d001648eSDavid Howells 	ret = afs_transfer_reply(call);
984372ee163SDavid Howells 	if (ret < 0)
985372ee163SDavid Howells 		return ret;
986260a9803SDavid Howells 
987260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
988260a9803SDavid Howells 	bp = call->buffer;
98931143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode, NULL);
990260a9803SDavid Howells 	if (new_dvnode != orig_dvnode)
99131143d5dSDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode,
99231143d5dSDavid Howells 					  NULL);
99397e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
994260a9803SDavid Howells 
995260a9803SDavid Howells 	_leave(" = 0 [done]");
996260a9803SDavid Howells 	return 0;
997260a9803SDavid Howells }
998260a9803SDavid Howells 
999260a9803SDavid Howells /*
1000260a9803SDavid Howells  * FS.Rename operation type
1001260a9803SDavid Howells  */
1002260a9803SDavid Howells static const struct afs_call_type afs_RXFSRename = {
1003260a9803SDavid Howells 	.name		= "FS.Rename",
1004025db80cSDavid Howells 	.op		= afs_FS_Rename,
1005260a9803SDavid Howells 	.deliver	= afs_deliver_fs_rename,
1006260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
1007260a9803SDavid Howells };
1008260a9803SDavid Howells 
1009260a9803SDavid Howells /*
1010260a9803SDavid Howells  * create a symbolic link
1011260a9803SDavid Howells  */
10128b2a464cSDavid Howells int afs_fs_rename(struct afs_fs_cursor *fc,
1013260a9803SDavid Howells 		  const char *orig_name,
1014260a9803SDavid Howells 		  struct afs_vnode *new_dvnode,
1015d2ddc776SDavid Howells 		  const char *new_name)
1016260a9803SDavid Howells {
1017d2ddc776SDavid Howells 	struct afs_vnode *orig_dvnode = fc->vnode;
1018260a9803SDavid Howells 	struct afs_call *call;
1019f044c884SDavid Howells 	struct afs_net *net = afs_v2net(orig_dvnode);
1020260a9803SDavid Howells 	size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
1021260a9803SDavid Howells 	__be32 *bp;
1022260a9803SDavid Howells 
1023260a9803SDavid Howells 	_enter("");
1024260a9803SDavid Howells 
1025260a9803SDavid Howells 	o_namesz = strlen(orig_name);
1026260a9803SDavid Howells 	o_padsz = (4 - (o_namesz & 3)) & 3;
1027260a9803SDavid Howells 
1028260a9803SDavid Howells 	n_namesz = strlen(new_name);
1029260a9803SDavid Howells 	n_padsz = (4 - (n_namesz & 3)) & 3;
1030260a9803SDavid Howells 
1031260a9803SDavid Howells 	reqsz = (4 * 4) +
1032260a9803SDavid Howells 		4 + o_namesz + o_padsz +
1033260a9803SDavid Howells 		(3 * 4) +
1034260a9803SDavid Howells 		4 + n_namesz + n_padsz;
1035260a9803SDavid Howells 
1036f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
1037260a9803SDavid Howells 	if (!call)
1038260a9803SDavid Howells 		return -ENOMEM;
1039260a9803SDavid Howells 
1040d2ddc776SDavid Howells 	call->key = fc->key;
104197e3043aSDavid Howells 	call->reply[0] = orig_dvnode;
104297e3043aSDavid Howells 	call->reply[1] = new_dvnode;
1043260a9803SDavid Howells 
1044260a9803SDavid Howells 	/* marshall the parameters */
1045260a9803SDavid Howells 	bp = call->request;
1046260a9803SDavid Howells 	*bp++ = htonl(FSRENAME);
1047260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vid);
1048260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vnode);
1049260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.unique);
1050260a9803SDavid Howells 	*bp++ = htonl(o_namesz);
1051260a9803SDavid Howells 	memcpy(bp, orig_name, o_namesz);
1052260a9803SDavid Howells 	bp = (void *) bp + o_namesz;
1053260a9803SDavid Howells 	if (o_padsz > 0) {
1054260a9803SDavid Howells 		memset(bp, 0, o_padsz);
1055260a9803SDavid Howells 		bp = (void *) bp + o_padsz;
1056260a9803SDavid Howells 	}
1057260a9803SDavid Howells 
1058260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vid);
1059260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vnode);
1060260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.unique);
1061260a9803SDavid Howells 	*bp++ = htonl(n_namesz);
1062260a9803SDavid Howells 	memcpy(bp, new_name, n_namesz);
1063260a9803SDavid Howells 	bp = (void *) bp + n_namesz;
1064260a9803SDavid Howells 	if (n_padsz > 0) {
1065260a9803SDavid Howells 		memset(bp, 0, n_padsz);
1066260a9803SDavid Howells 		bp = (void *) bp + n_padsz;
1067260a9803SDavid Howells 	}
1068260a9803SDavid Howells 
1069d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1070025db80cSDavid Howells 	trace_afs_make_fs_call(call, &orig_dvnode->fid);
1071d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1072260a9803SDavid Howells }
107331143d5dSDavid Howells 
107431143d5dSDavid Howells /*
107531143d5dSDavid Howells  * deliver reply data to an FS.StoreData
107631143d5dSDavid Howells  */
1077d001648eSDavid Howells static int afs_deliver_fs_store_data(struct afs_call *call)
107831143d5dSDavid Howells {
107997e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
108031143d5dSDavid Howells 	const __be32 *bp;
1081372ee163SDavid Howells 	int ret;
108231143d5dSDavid Howells 
1083d001648eSDavid Howells 	_enter("");
108431143d5dSDavid Howells 
1085d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1086372ee163SDavid Howells 	if (ret < 0)
1087372ee163SDavid Howells 		return ret;
108831143d5dSDavid Howells 
108931143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
109031143d5dSDavid Howells 	bp = call->buffer;
109131143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode,
109231143d5dSDavid Howells 				  &call->store_version);
109397e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
109431143d5dSDavid Howells 
109531143d5dSDavid Howells 	afs_pages_written_back(vnode, call);
109631143d5dSDavid Howells 
109731143d5dSDavid Howells 	_leave(" = 0 [done]");
109831143d5dSDavid Howells 	return 0;
109931143d5dSDavid Howells }
110031143d5dSDavid Howells 
110131143d5dSDavid Howells /*
110231143d5dSDavid Howells  * FS.StoreData operation type
110331143d5dSDavid Howells  */
110431143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData = {
110531143d5dSDavid Howells 	.name		= "FS.StoreData",
1106025db80cSDavid Howells 	.op		= afs_FS_StoreData,
110731143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_data,
110831143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
110931143d5dSDavid Howells };
111031143d5dSDavid Howells 
1111b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64 = {
1112b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1113025db80cSDavid Howells 	.op		= afs_FS_StoreData64,
1114b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_data,
1115b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1116b9b1f8d5SDavid Howells };
1117b9b1f8d5SDavid Howells 
1118b9b1f8d5SDavid Howells /*
1119b9b1f8d5SDavid Howells  * store a set of pages to a very large file
1120b9b1f8d5SDavid Howells  */
11218b2a464cSDavid Howells static int afs_fs_store_data64(struct afs_fs_cursor *fc,
1122b9b1f8d5SDavid Howells 			       struct afs_writeback *wb,
1123b9b1f8d5SDavid Howells 			       pgoff_t first, pgoff_t last,
1124b9b1f8d5SDavid Howells 			       unsigned offset, unsigned to,
1125d2ddc776SDavid Howells 			       loff_t size, loff_t pos, loff_t i_size)
1126b9b1f8d5SDavid Howells {
1127b9b1f8d5SDavid Howells 	struct afs_vnode *vnode = wb->vnode;
1128b9b1f8d5SDavid Howells 	struct afs_call *call;
1129f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1130b9b1f8d5SDavid Howells 	__be32 *bp;
1131b9b1f8d5SDavid Howells 
1132b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
1133b9b1f8d5SDavid Howells 	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
1134b9b1f8d5SDavid Howells 
1135f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData64,
1136b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1137b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1138b9b1f8d5SDavid Howells 	if (!call)
1139b9b1f8d5SDavid Howells 		return -ENOMEM;
1140b9b1f8d5SDavid Howells 
1141b9b1f8d5SDavid Howells 	call->wb = wb;
1142b9b1f8d5SDavid Howells 	call->key = wb->key;
114397e3043aSDavid Howells 	call->reply[0] = vnode;
1144b9b1f8d5SDavid Howells 	call->mapping = vnode->vfs_inode.i_mapping;
1145b9b1f8d5SDavid Howells 	call->first = first;
1146b9b1f8d5SDavid Howells 	call->last = last;
1147b9b1f8d5SDavid Howells 	call->first_offset = offset;
1148b9b1f8d5SDavid Howells 	call->last_to = to;
1149b9b1f8d5SDavid Howells 	call->send_pages = true;
1150b9b1f8d5SDavid Howells 	call->store_version = vnode->status.data_version + 1;
1151b9b1f8d5SDavid Howells 
1152b9b1f8d5SDavid Howells 	/* marshall the parameters */
1153b9b1f8d5SDavid Howells 	bp = call->request;
1154b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1155b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1156b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1157b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1158b9b1f8d5SDavid Howells 
1159ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MTIME); /* mask */
1160ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
1161b9b1f8d5SDavid Howells 	*bp++ = 0; /* owner */
1162b9b1f8d5SDavid Howells 	*bp++ = 0; /* group */
1163b9b1f8d5SDavid Howells 	*bp++ = 0; /* unix mode */
1164b9b1f8d5SDavid Howells 	*bp++ = 0; /* segment size */
1165b9b1f8d5SDavid Howells 
1166b9b1f8d5SDavid Howells 	*bp++ = htonl(pos >> 32);
1167b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) pos);
1168b9b1f8d5SDavid Howells 	*bp++ = htonl(size >> 32);
1169b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) size);
1170b9b1f8d5SDavid Howells 	*bp++ = htonl(i_size >> 32);
1171b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) i_size);
1172b9b1f8d5SDavid Howells 
1173025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1174d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1175b9b1f8d5SDavid Howells }
1176b9b1f8d5SDavid Howells 
117731143d5dSDavid Howells /*
117831143d5dSDavid Howells  * store a set of pages
117931143d5dSDavid Howells  */
11808b2a464cSDavid Howells int afs_fs_store_data(struct afs_fs_cursor *fc, struct afs_writeback *wb,
118131143d5dSDavid Howells 		      pgoff_t first, pgoff_t last,
1182d2ddc776SDavid Howells 		      unsigned offset, unsigned to)
118331143d5dSDavid Howells {
118431143d5dSDavid Howells 	struct afs_vnode *vnode = wb->vnode;
118531143d5dSDavid Howells 	struct afs_call *call;
1186f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
118731143d5dSDavid Howells 	loff_t size, pos, i_size;
118831143d5dSDavid Howells 	__be32 *bp;
118931143d5dSDavid Howells 
119031143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
119131143d5dSDavid Howells 	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
119231143d5dSDavid Howells 
1193146a1192SDavid Howells 	size = (loff_t)to - (loff_t)offset;
119431143d5dSDavid Howells 	if (first != last)
119531143d5dSDavid Howells 		size += (loff_t)(last - first) << PAGE_SHIFT;
119631143d5dSDavid Howells 	pos = (loff_t)first << PAGE_SHIFT;
119731143d5dSDavid Howells 	pos += offset;
119831143d5dSDavid Howells 
119931143d5dSDavid Howells 	i_size = i_size_read(&vnode->vfs_inode);
120031143d5dSDavid Howells 	if (pos + size > i_size)
120131143d5dSDavid Howells 		i_size = size + pos;
120231143d5dSDavid Howells 
120331143d5dSDavid Howells 	_debug("size %llx, at %llx, i_size %llx",
120431143d5dSDavid Howells 	       (unsigned long long) size, (unsigned long long) pos,
120531143d5dSDavid Howells 	       (unsigned long long) i_size);
120631143d5dSDavid Howells 
1207b9b1f8d5SDavid Howells 	if (pos >> 32 || i_size >> 32 || size >> 32 || (pos + size) >> 32)
12088b2a464cSDavid Howells 		return afs_fs_store_data64(fc, wb, first, last, offset, to,
1209d2ddc776SDavid Howells 					   size, pos, i_size);
121031143d5dSDavid Howells 
1211f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData,
121231143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
121331143d5dSDavid Howells 				   (21 + 6) * 4);
121431143d5dSDavid Howells 	if (!call)
121531143d5dSDavid Howells 		return -ENOMEM;
121631143d5dSDavid Howells 
121731143d5dSDavid Howells 	call->wb = wb;
121831143d5dSDavid Howells 	call->key = wb->key;
121997e3043aSDavid Howells 	call->reply[0] = vnode;
122031143d5dSDavid Howells 	call->mapping = vnode->vfs_inode.i_mapping;
122131143d5dSDavid Howells 	call->first = first;
122231143d5dSDavid Howells 	call->last = last;
122331143d5dSDavid Howells 	call->first_offset = offset;
122431143d5dSDavid Howells 	call->last_to = to;
122531143d5dSDavid Howells 	call->send_pages = true;
122631143d5dSDavid Howells 	call->store_version = vnode->status.data_version + 1;
122731143d5dSDavid Howells 
122831143d5dSDavid Howells 	/* marshall the parameters */
122931143d5dSDavid Howells 	bp = call->request;
123031143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
123131143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
123231143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
123331143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
123431143d5dSDavid Howells 
1235ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MTIME); /* mask */
1236ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
123731143d5dSDavid Howells 	*bp++ = 0; /* owner */
123831143d5dSDavid Howells 	*bp++ = 0; /* group */
123931143d5dSDavid Howells 	*bp++ = 0; /* unix mode */
124031143d5dSDavid Howells 	*bp++ = 0; /* segment size */
124131143d5dSDavid Howells 
124231143d5dSDavid Howells 	*bp++ = htonl(pos);
124331143d5dSDavid Howells 	*bp++ = htonl(size);
124431143d5dSDavid Howells 	*bp++ = htonl(i_size);
124531143d5dSDavid Howells 
1246d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1247025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1248d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
124931143d5dSDavid Howells }
125031143d5dSDavid Howells 
125131143d5dSDavid Howells /*
125231143d5dSDavid Howells  * deliver reply data to an FS.StoreStatus
125331143d5dSDavid Howells  */
1254d001648eSDavid Howells static int afs_deliver_fs_store_status(struct afs_call *call)
125531143d5dSDavid Howells {
125631143d5dSDavid Howells 	afs_dataversion_t *store_version;
125797e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
125831143d5dSDavid Howells 	const __be32 *bp;
1259372ee163SDavid Howells 	int ret;
126031143d5dSDavid Howells 
1261d001648eSDavid Howells 	_enter("");
126231143d5dSDavid Howells 
1263d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1264372ee163SDavid Howells 	if (ret < 0)
1265372ee163SDavid Howells 		return ret;
126631143d5dSDavid Howells 
126731143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
126831143d5dSDavid Howells 	store_version = NULL;
126931143d5dSDavid Howells 	if (call->operation_ID == FSSTOREDATA)
127031143d5dSDavid Howells 		store_version = &call->store_version;
127131143d5dSDavid Howells 
127231143d5dSDavid Howells 	bp = call->buffer;
127331143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, store_version);
127497e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
127531143d5dSDavid Howells 
127631143d5dSDavid Howells 	_leave(" = 0 [done]");
127731143d5dSDavid Howells 	return 0;
127831143d5dSDavid Howells }
127931143d5dSDavid Howells 
128031143d5dSDavid Howells /*
128131143d5dSDavid Howells  * FS.StoreStatus operation type
128231143d5dSDavid Howells  */
128331143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreStatus = {
128431143d5dSDavid Howells 	.name		= "FS.StoreStatus",
1285025db80cSDavid Howells 	.op		= afs_FS_StoreStatus,
128631143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
128731143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
128831143d5dSDavid Howells };
128931143d5dSDavid Howells 
129031143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData_as_Status = {
129131143d5dSDavid Howells 	.name		= "FS.StoreData",
1292025db80cSDavid Howells 	.op		= afs_FS_StoreData,
129331143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
129431143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
129531143d5dSDavid Howells };
129631143d5dSDavid Howells 
1297b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64_as_Status = {
1298b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1299025db80cSDavid Howells 	.op		= afs_FS_StoreData64,
1300b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_status,
1301b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1302b9b1f8d5SDavid Howells };
1303b9b1f8d5SDavid Howells 
1304b9b1f8d5SDavid Howells /*
1305b9b1f8d5SDavid Howells  * set the attributes on a very large file, using FS.StoreData rather than
1306b9b1f8d5SDavid Howells  * FS.StoreStatus so as to alter the file size also
1307b9b1f8d5SDavid Howells  */
1308d2ddc776SDavid Howells static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr)
1309b9b1f8d5SDavid Howells {
1310d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1311b9b1f8d5SDavid Howells 	struct afs_call *call;
1312f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1313b9b1f8d5SDavid Howells 	__be32 *bp;
1314b9b1f8d5SDavid Howells 
1315b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
1316d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
1317b9b1f8d5SDavid Howells 
1318b9b1f8d5SDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1319b9b1f8d5SDavid Howells 
1320f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData64_as_Status,
1321b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1322b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1323b9b1f8d5SDavid Howells 	if (!call)
1324b9b1f8d5SDavid Howells 		return -ENOMEM;
1325b9b1f8d5SDavid Howells 
1326d2ddc776SDavid Howells 	call->key = fc->key;
132797e3043aSDavid Howells 	call->reply[0] = vnode;
1328b9b1f8d5SDavid Howells 	call->store_version = vnode->status.data_version + 1;
1329b9b1f8d5SDavid Howells 
1330b9b1f8d5SDavid Howells 	/* marshall the parameters */
1331b9b1f8d5SDavid Howells 	bp = call->request;
1332b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1333b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1334b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1335b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1336b9b1f8d5SDavid Howells 
1337b9b1f8d5SDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
1338b9b1f8d5SDavid Howells 
1339b9b1f8d5SDavid Howells 	*bp++ = 0;				/* position of start of write */
1340b9b1f8d5SDavid Howells 	*bp++ = 0;
1341b9b1f8d5SDavid Howells 	*bp++ = 0;				/* size of write */
1342b9b1f8d5SDavid Howells 	*bp++ = 0;
1343b9b1f8d5SDavid Howells 	*bp++ = htonl(attr->ia_size >> 32);	/* new file length */
1344b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) attr->ia_size);
1345b9b1f8d5SDavid Howells 
1346d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1347025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1348d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1349b9b1f8d5SDavid Howells }
1350b9b1f8d5SDavid Howells 
135131143d5dSDavid Howells /*
135231143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus
135331143d5dSDavid Howells  * so as to alter the file size also
135431143d5dSDavid Howells  */
1355d2ddc776SDavid Howells static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
135631143d5dSDavid Howells {
1357d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
135831143d5dSDavid Howells 	struct afs_call *call;
1359f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
136031143d5dSDavid Howells 	__be32 *bp;
136131143d5dSDavid Howells 
136231143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
1363d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
136431143d5dSDavid Howells 
136531143d5dSDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1366b9b1f8d5SDavid Howells 	if (attr->ia_size >> 32)
1367d2ddc776SDavid Howells 		return afs_fs_setattr_size64(fc, attr);
136831143d5dSDavid Howells 
1369f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData_as_Status,
137031143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
137131143d5dSDavid Howells 				   (21 + 6) * 4);
137231143d5dSDavid Howells 	if (!call)
137331143d5dSDavid Howells 		return -ENOMEM;
137431143d5dSDavid Howells 
1375d2ddc776SDavid Howells 	call->key = fc->key;
137697e3043aSDavid Howells 	call->reply[0] = vnode;
137731143d5dSDavid Howells 	call->store_version = vnode->status.data_version + 1;
137831143d5dSDavid Howells 
137931143d5dSDavid Howells 	/* marshall the parameters */
138031143d5dSDavid Howells 	bp = call->request;
138131143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
138231143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
138331143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
138431143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
138531143d5dSDavid Howells 
138631143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
138731143d5dSDavid Howells 
138831143d5dSDavid Howells 	*bp++ = 0;				/* position of start of write */
138931143d5dSDavid Howells 	*bp++ = 0;				/* size of write */
139031143d5dSDavid Howells 	*bp++ = htonl(attr->ia_size);		/* new file length */
139131143d5dSDavid Howells 
1392d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1393025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1394d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
139531143d5dSDavid Howells }
139631143d5dSDavid Howells 
139731143d5dSDavid Howells /*
139831143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData if there's a change in file
139931143d5dSDavid Howells  * size, and FS.StoreStatus otherwise
140031143d5dSDavid Howells  */
1401d2ddc776SDavid Howells int afs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
140231143d5dSDavid Howells {
1403d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
140431143d5dSDavid Howells 	struct afs_call *call;
1405f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
140631143d5dSDavid Howells 	__be32 *bp;
140731143d5dSDavid Howells 
140831143d5dSDavid Howells 	if (attr->ia_valid & ATTR_SIZE)
1409d2ddc776SDavid Howells 		return afs_fs_setattr_size(fc, attr);
141031143d5dSDavid Howells 
141131143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
1412d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
141331143d5dSDavid Howells 
1414f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreStatus,
141531143d5dSDavid Howells 				   (4 + 6) * 4,
141631143d5dSDavid Howells 				   (21 + 6) * 4);
141731143d5dSDavid Howells 	if (!call)
141831143d5dSDavid Howells 		return -ENOMEM;
141931143d5dSDavid Howells 
1420d2ddc776SDavid Howells 	call->key = fc->key;
142197e3043aSDavid Howells 	call->reply[0] = vnode;
142231143d5dSDavid Howells 
142331143d5dSDavid Howells 	/* marshall the parameters */
142431143d5dSDavid Howells 	bp = call->request;
142531143d5dSDavid Howells 	*bp++ = htonl(FSSTORESTATUS);
142631143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
142731143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
142831143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
142931143d5dSDavid Howells 
143031143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
143131143d5dSDavid Howells 
1432d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1433025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1434d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
143531143d5dSDavid Howells }
143645222b9eSDavid Howells 
143745222b9eSDavid Howells /*
143845222b9eSDavid Howells  * deliver reply data to an FS.GetVolumeStatus
143945222b9eSDavid Howells  */
1440d001648eSDavid Howells static int afs_deliver_fs_get_volume_status(struct afs_call *call)
144145222b9eSDavid Howells {
144245222b9eSDavid Howells 	const __be32 *bp;
144345222b9eSDavid Howells 	char *p;
144445222b9eSDavid Howells 	int ret;
144545222b9eSDavid Howells 
1446d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
144745222b9eSDavid Howells 
144845222b9eSDavid Howells 	switch (call->unmarshall) {
144945222b9eSDavid Howells 	case 0:
145045222b9eSDavid Howells 		call->offset = 0;
145145222b9eSDavid Howells 		call->unmarshall++;
145245222b9eSDavid Howells 
145345222b9eSDavid Howells 		/* extract the returned status record */
145445222b9eSDavid Howells 	case 1:
145545222b9eSDavid Howells 		_debug("extract status");
1456d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1457d001648eSDavid Howells 				       12 * 4, true);
1458372ee163SDavid Howells 		if (ret < 0)
1459372ee163SDavid Howells 			return ret;
146045222b9eSDavid Howells 
146145222b9eSDavid Howells 		bp = call->buffer;
146297e3043aSDavid Howells 		xdr_decode_AFSFetchVolumeStatus(&bp, call->reply[1]);
146345222b9eSDavid Howells 		call->offset = 0;
146445222b9eSDavid Howells 		call->unmarshall++;
146545222b9eSDavid Howells 
146645222b9eSDavid Howells 		/* extract the volume name length */
146745222b9eSDavid Howells 	case 2:
1468d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1469372ee163SDavid Howells 		if (ret < 0)
1470372ee163SDavid Howells 			return ret;
147145222b9eSDavid Howells 
147245222b9eSDavid Howells 		call->count = ntohl(call->tmp);
147345222b9eSDavid Howells 		_debug("volname length: %u", call->count);
147445222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
147545222b9eSDavid Howells 			return -EBADMSG;
147645222b9eSDavid Howells 		call->offset = 0;
147745222b9eSDavid Howells 		call->unmarshall++;
147845222b9eSDavid Howells 
147945222b9eSDavid Howells 		/* extract the volume name */
148045222b9eSDavid Howells 	case 3:
148145222b9eSDavid Howells 		_debug("extract volname");
148245222b9eSDavid Howells 		if (call->count > 0) {
148397e3043aSDavid Howells 			ret = afs_extract_data(call, call->reply[2],
1484d001648eSDavid Howells 					       call->count, true);
1485372ee163SDavid Howells 			if (ret < 0)
1486372ee163SDavid Howells 				return ret;
148745222b9eSDavid Howells 		}
148845222b9eSDavid Howells 
148997e3043aSDavid Howells 		p = call->reply[2];
149045222b9eSDavid Howells 		p[call->count] = 0;
149145222b9eSDavid Howells 		_debug("volname '%s'", p);
149245222b9eSDavid Howells 
149345222b9eSDavid Howells 		call->offset = 0;
149445222b9eSDavid Howells 		call->unmarshall++;
149545222b9eSDavid Howells 
149645222b9eSDavid Howells 		/* extract the volume name padding */
149745222b9eSDavid Howells 		if ((call->count & 3) == 0) {
149845222b9eSDavid Howells 			call->unmarshall++;
149945222b9eSDavid Howells 			goto no_volname_padding;
150045222b9eSDavid Howells 		}
150145222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
150245222b9eSDavid Howells 
150345222b9eSDavid Howells 	case 4:
1504d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1505d001648eSDavid Howells 				       call->count, true);
1506372ee163SDavid Howells 		if (ret < 0)
1507372ee163SDavid Howells 			return ret;
150845222b9eSDavid Howells 
150945222b9eSDavid Howells 		call->offset = 0;
151045222b9eSDavid Howells 		call->unmarshall++;
151145222b9eSDavid Howells 	no_volname_padding:
151245222b9eSDavid Howells 
151345222b9eSDavid Howells 		/* extract the offline message length */
151445222b9eSDavid Howells 	case 5:
1515d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1516372ee163SDavid Howells 		if (ret < 0)
1517372ee163SDavid Howells 			return ret;
151845222b9eSDavid Howells 
151945222b9eSDavid Howells 		call->count = ntohl(call->tmp);
152045222b9eSDavid Howells 		_debug("offline msg length: %u", call->count);
152145222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
152245222b9eSDavid Howells 			return -EBADMSG;
152345222b9eSDavid Howells 		call->offset = 0;
152445222b9eSDavid Howells 		call->unmarshall++;
152545222b9eSDavid Howells 
152645222b9eSDavid Howells 		/* extract the offline message */
152745222b9eSDavid Howells 	case 6:
152845222b9eSDavid Howells 		_debug("extract offline");
152945222b9eSDavid Howells 		if (call->count > 0) {
153097e3043aSDavid Howells 			ret = afs_extract_data(call, call->reply[2],
1531d001648eSDavid Howells 					       call->count, true);
1532372ee163SDavid Howells 			if (ret < 0)
1533372ee163SDavid Howells 				return ret;
153445222b9eSDavid Howells 		}
153545222b9eSDavid Howells 
153697e3043aSDavid Howells 		p = call->reply[2];
153745222b9eSDavid Howells 		p[call->count] = 0;
153845222b9eSDavid Howells 		_debug("offline '%s'", p);
153945222b9eSDavid Howells 
154045222b9eSDavid Howells 		call->offset = 0;
154145222b9eSDavid Howells 		call->unmarshall++;
154245222b9eSDavid Howells 
154345222b9eSDavid Howells 		/* extract the offline message padding */
154445222b9eSDavid Howells 		if ((call->count & 3) == 0) {
154545222b9eSDavid Howells 			call->unmarshall++;
154645222b9eSDavid Howells 			goto no_offline_padding;
154745222b9eSDavid Howells 		}
154845222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
154945222b9eSDavid Howells 
155045222b9eSDavid Howells 	case 7:
1551d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1552d001648eSDavid Howells 				       call->count, true);
1553372ee163SDavid Howells 		if (ret < 0)
1554372ee163SDavid Howells 			return ret;
155545222b9eSDavid Howells 
155645222b9eSDavid Howells 		call->offset = 0;
155745222b9eSDavid Howells 		call->unmarshall++;
155845222b9eSDavid Howells 	no_offline_padding:
155945222b9eSDavid Howells 
156045222b9eSDavid Howells 		/* extract the message of the day length */
156145222b9eSDavid Howells 	case 8:
1562d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1563372ee163SDavid Howells 		if (ret < 0)
1564372ee163SDavid Howells 			return ret;
156545222b9eSDavid Howells 
156645222b9eSDavid Howells 		call->count = ntohl(call->tmp);
156745222b9eSDavid Howells 		_debug("motd length: %u", call->count);
156845222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
156945222b9eSDavid Howells 			return -EBADMSG;
157045222b9eSDavid Howells 		call->offset = 0;
157145222b9eSDavid Howells 		call->unmarshall++;
157245222b9eSDavid Howells 
157345222b9eSDavid Howells 		/* extract the message of the day */
157445222b9eSDavid Howells 	case 9:
157545222b9eSDavid Howells 		_debug("extract motd");
157645222b9eSDavid Howells 		if (call->count > 0) {
157797e3043aSDavid Howells 			ret = afs_extract_data(call, call->reply[2],
1578d001648eSDavid Howells 					       call->count, true);
1579372ee163SDavid Howells 			if (ret < 0)
1580372ee163SDavid Howells 				return ret;
158145222b9eSDavid Howells 		}
158245222b9eSDavid Howells 
158397e3043aSDavid Howells 		p = call->reply[2];
158445222b9eSDavid Howells 		p[call->count] = 0;
158545222b9eSDavid Howells 		_debug("motd '%s'", p);
158645222b9eSDavid Howells 
158745222b9eSDavid Howells 		call->offset = 0;
158845222b9eSDavid Howells 		call->unmarshall++;
158945222b9eSDavid Howells 
159045222b9eSDavid Howells 		/* extract the message of the day padding */
1591d001648eSDavid Howells 		call->count = (4 - (call->count & 3)) & 3;
159245222b9eSDavid Howells 
159345222b9eSDavid Howells 	case 10:
1594d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1595d001648eSDavid Howells 				       call->count, false);
1596372ee163SDavid Howells 		if (ret < 0)
1597372ee163SDavid Howells 			return ret;
159845222b9eSDavid Howells 
159945222b9eSDavid Howells 		call->offset = 0;
160045222b9eSDavid Howells 		call->unmarshall++;
160145222b9eSDavid Howells 	case 11:
160245222b9eSDavid Howells 		break;
160345222b9eSDavid Howells 	}
160445222b9eSDavid Howells 
160545222b9eSDavid Howells 	_leave(" = 0 [done]");
160645222b9eSDavid Howells 	return 0;
160745222b9eSDavid Howells }
160845222b9eSDavid Howells 
160945222b9eSDavid Howells /*
161045222b9eSDavid Howells  * destroy an FS.GetVolumeStatus call
161145222b9eSDavid Howells  */
161245222b9eSDavid Howells static void afs_get_volume_status_call_destructor(struct afs_call *call)
161345222b9eSDavid Howells {
161497e3043aSDavid Howells 	kfree(call->reply[2]);
161597e3043aSDavid Howells 	call->reply[2] = NULL;
161645222b9eSDavid Howells 	afs_flat_call_destructor(call);
161745222b9eSDavid Howells }
161845222b9eSDavid Howells 
161945222b9eSDavid Howells /*
162045222b9eSDavid Howells  * FS.GetVolumeStatus operation type
162145222b9eSDavid Howells  */
162245222b9eSDavid Howells static const struct afs_call_type afs_RXFSGetVolumeStatus = {
162345222b9eSDavid Howells 	.name		= "FS.GetVolumeStatus",
1624025db80cSDavid Howells 	.op		= afs_FS_GetVolumeStatus,
162545222b9eSDavid Howells 	.deliver	= afs_deliver_fs_get_volume_status,
162645222b9eSDavid Howells 	.destructor	= afs_get_volume_status_call_destructor,
162745222b9eSDavid Howells };
162845222b9eSDavid Howells 
162945222b9eSDavid Howells /*
163045222b9eSDavid Howells  * fetch the status of a volume
163145222b9eSDavid Howells  */
16328b2a464cSDavid Howells int afs_fs_get_volume_status(struct afs_fs_cursor *fc,
1633d2ddc776SDavid Howells 			     struct afs_volume_status *vs)
163445222b9eSDavid Howells {
1635d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
163645222b9eSDavid Howells 	struct afs_call *call;
1637f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
163845222b9eSDavid Howells 	__be32 *bp;
163945222b9eSDavid Howells 	void *tmpbuf;
164045222b9eSDavid Howells 
164145222b9eSDavid Howells 	_enter("");
164245222b9eSDavid Howells 
164345222b9eSDavid Howells 	tmpbuf = kmalloc(AFSOPAQUEMAX, GFP_KERNEL);
164445222b9eSDavid Howells 	if (!tmpbuf)
164545222b9eSDavid Howells 		return -ENOMEM;
164645222b9eSDavid Howells 
1647f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGetVolumeStatus, 2 * 4, 12 * 4);
164845222b9eSDavid Howells 	if (!call) {
164945222b9eSDavid Howells 		kfree(tmpbuf);
165045222b9eSDavid Howells 		return -ENOMEM;
165145222b9eSDavid Howells 	}
165245222b9eSDavid Howells 
1653d2ddc776SDavid Howells 	call->key = fc->key;
165497e3043aSDavid Howells 	call->reply[0] = vnode;
165597e3043aSDavid Howells 	call->reply[1] = vs;
165697e3043aSDavid Howells 	call->reply[2] = tmpbuf;
165745222b9eSDavid Howells 
165845222b9eSDavid Howells 	/* marshall the parameters */
165945222b9eSDavid Howells 	bp = call->request;
166045222b9eSDavid Howells 	bp[0] = htonl(FSGETVOLUMESTATUS);
166145222b9eSDavid Howells 	bp[1] = htonl(vnode->fid.vid);
166245222b9eSDavid Howells 
1663d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1664025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1665d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
166645222b9eSDavid Howells }
1667e8d6c554SDavid Howells 
1668e8d6c554SDavid Howells /*
1669e8d6c554SDavid Howells  * deliver reply data to an FS.SetLock, FS.ExtendLock or FS.ReleaseLock
1670e8d6c554SDavid Howells  */
1671d001648eSDavid Howells static int afs_deliver_fs_xxxx_lock(struct afs_call *call)
1672e8d6c554SDavid Howells {
1673e8d6c554SDavid Howells 	const __be32 *bp;
1674372ee163SDavid Howells 	int ret;
1675e8d6c554SDavid Howells 
1676d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
1677e8d6c554SDavid Howells 
1678d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1679372ee163SDavid Howells 	if (ret < 0)
1680372ee163SDavid Howells 		return ret;
1681e8d6c554SDavid Howells 
1682e8d6c554SDavid Howells 	/* unmarshall the reply once we've received all of it */
1683e8d6c554SDavid Howells 	bp = call->buffer;
168497e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
1685e8d6c554SDavid Howells 
1686e8d6c554SDavid Howells 	_leave(" = 0 [done]");
1687e8d6c554SDavid Howells 	return 0;
1688e8d6c554SDavid Howells }
1689e8d6c554SDavid Howells 
1690e8d6c554SDavid Howells /*
1691e8d6c554SDavid Howells  * FS.SetLock operation type
1692e8d6c554SDavid Howells  */
1693e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSSetLock = {
1694e8d6c554SDavid Howells 	.name		= "FS.SetLock",
1695025db80cSDavid Howells 	.op		= afs_FS_SetLock,
1696e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1697e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1698e8d6c554SDavid Howells };
1699e8d6c554SDavid Howells 
1700e8d6c554SDavid Howells /*
1701e8d6c554SDavid Howells  * FS.ExtendLock operation type
1702e8d6c554SDavid Howells  */
1703e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSExtendLock = {
1704e8d6c554SDavid Howells 	.name		= "FS.ExtendLock",
1705025db80cSDavid Howells 	.op		= afs_FS_ExtendLock,
1706e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1707e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1708e8d6c554SDavid Howells };
1709e8d6c554SDavid Howells 
1710e8d6c554SDavid Howells /*
1711e8d6c554SDavid Howells  * FS.ReleaseLock operation type
1712e8d6c554SDavid Howells  */
1713e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSReleaseLock = {
1714e8d6c554SDavid Howells 	.name		= "FS.ReleaseLock",
1715025db80cSDavid Howells 	.op		= afs_FS_ReleaseLock,
1716e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1717e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1718e8d6c554SDavid Howells };
1719e8d6c554SDavid Howells 
1720e8d6c554SDavid Howells /*
1721d2ddc776SDavid Howells  * Set a lock on a file
1722e8d6c554SDavid Howells  */
1723d2ddc776SDavid Howells int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
1724e8d6c554SDavid Howells {
1725d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1726e8d6c554SDavid Howells 	struct afs_call *call;
1727f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1728e8d6c554SDavid Howells 	__be32 *bp;
1729e8d6c554SDavid Howells 
1730e8d6c554SDavid Howells 	_enter("");
1731e8d6c554SDavid Howells 
1732f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSSetLock, 5 * 4, 6 * 4);
1733e8d6c554SDavid Howells 	if (!call)
1734e8d6c554SDavid Howells 		return -ENOMEM;
1735e8d6c554SDavid Howells 
1736d2ddc776SDavid Howells 	call->key = fc->key;
173797e3043aSDavid Howells 	call->reply[0] = vnode;
1738e8d6c554SDavid Howells 
1739e8d6c554SDavid Howells 	/* marshall the parameters */
1740e8d6c554SDavid Howells 	bp = call->request;
1741e8d6c554SDavid Howells 	*bp++ = htonl(FSSETLOCK);
1742e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1743e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1744e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1745e8d6c554SDavid Howells 	*bp++ = htonl(type);
1746e8d6c554SDavid Howells 
1747d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1748025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1749d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1750e8d6c554SDavid Howells }
1751e8d6c554SDavid Howells 
1752e8d6c554SDavid Howells /*
1753e8d6c554SDavid Howells  * extend a lock on a file
1754e8d6c554SDavid Howells  */
1755d2ddc776SDavid Howells int afs_fs_extend_lock(struct afs_fs_cursor *fc)
1756e8d6c554SDavid Howells {
1757d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1758e8d6c554SDavid Howells 	struct afs_call *call;
1759f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1760e8d6c554SDavid Howells 	__be32 *bp;
1761e8d6c554SDavid Howells 
1762e8d6c554SDavid Howells 	_enter("");
1763e8d6c554SDavid Howells 
1764f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSExtendLock, 4 * 4, 6 * 4);
1765e8d6c554SDavid Howells 	if (!call)
1766e8d6c554SDavid Howells 		return -ENOMEM;
1767e8d6c554SDavid Howells 
1768d2ddc776SDavid Howells 	call->key = fc->key;
176997e3043aSDavid Howells 	call->reply[0] = vnode;
1770e8d6c554SDavid Howells 
1771e8d6c554SDavid Howells 	/* marshall the parameters */
1772e8d6c554SDavid Howells 	bp = call->request;
1773e8d6c554SDavid Howells 	*bp++ = htonl(FSEXTENDLOCK);
1774e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1775e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1776e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1777e8d6c554SDavid Howells 
1778d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1779025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1780d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1781e8d6c554SDavid Howells }
1782e8d6c554SDavid Howells 
1783e8d6c554SDavid Howells /*
1784e8d6c554SDavid Howells  * release a lock on a file
1785e8d6c554SDavid Howells  */
1786d2ddc776SDavid Howells int afs_fs_release_lock(struct afs_fs_cursor *fc)
1787e8d6c554SDavid Howells {
1788d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1789e8d6c554SDavid Howells 	struct afs_call *call;
1790f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1791e8d6c554SDavid Howells 	__be32 *bp;
1792e8d6c554SDavid Howells 
1793e8d6c554SDavid Howells 	_enter("");
1794e8d6c554SDavid Howells 
1795f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSReleaseLock, 4 * 4, 6 * 4);
1796e8d6c554SDavid Howells 	if (!call)
1797e8d6c554SDavid Howells 		return -ENOMEM;
1798e8d6c554SDavid Howells 
1799d2ddc776SDavid Howells 	call->key = fc->key;
180097e3043aSDavid Howells 	call->reply[0] = vnode;
1801e8d6c554SDavid Howells 
1802e8d6c554SDavid Howells 	/* marshall the parameters */
1803e8d6c554SDavid Howells 	bp = call->request;
1804e8d6c554SDavid Howells 	*bp++ = htonl(FSRELEASELOCK);
1805e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1806e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1807e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1808e8d6c554SDavid Howells 
1809d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1810025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1811d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1812c435ee34SDavid Howells }
1813c435ee34SDavid Howells 
1814c435ee34SDavid Howells /*
1815c435ee34SDavid Howells  * Deliver reply data to an FS.GiveUpAllCallBacks operation.
1816c435ee34SDavid Howells  */
1817c435ee34SDavid Howells static int afs_deliver_fs_give_up_all_callbacks(struct afs_call *call)
1818c435ee34SDavid Howells {
1819c435ee34SDavid Howells 	return afs_transfer_reply(call);
1820c435ee34SDavid Howells }
1821c435ee34SDavid Howells 
1822c435ee34SDavid Howells /*
1823c435ee34SDavid Howells  * FS.GiveUpAllCallBacks operation type
1824c435ee34SDavid Howells  */
1825c435ee34SDavid Howells static const struct afs_call_type afs_RXFSGiveUpAllCallBacks = {
1826c435ee34SDavid Howells 	.name		= "FS.GiveUpAllCallBacks",
1827025db80cSDavid Howells 	.op		= afs_FS_GiveUpAllCallBacks,
1828c435ee34SDavid Howells 	.deliver	= afs_deliver_fs_give_up_all_callbacks,
1829c435ee34SDavid Howells 	.destructor	= afs_flat_call_destructor,
1830c435ee34SDavid Howells };
1831c435ee34SDavid Howells 
1832c435ee34SDavid Howells /*
1833c435ee34SDavid Howells  * Flush all the callbacks we have on a server.
1834c435ee34SDavid Howells  */
1835d2ddc776SDavid Howells int afs_fs_give_up_all_callbacks(struct afs_net *net,
1836d2ddc776SDavid Howells 				 struct afs_server *server,
18378b2a464cSDavid Howells 				 struct afs_addr_cursor *ac,
1838d2ddc776SDavid Howells 				 struct key *key)
1839c435ee34SDavid Howells {
1840c435ee34SDavid Howells 	struct afs_call *call;
1841c435ee34SDavid Howells 	__be32 *bp;
1842c435ee34SDavid Howells 
1843c435ee34SDavid Howells 	_enter("");
1844c435ee34SDavid Howells 
1845d2ddc776SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGiveUpAllCallBacks, 1 * 4, 0);
1846c435ee34SDavid Howells 	if (!call)
1847c435ee34SDavid Howells 		return -ENOMEM;
1848c435ee34SDavid Howells 
1849c435ee34SDavid Howells 	call->key = key;
1850c435ee34SDavid Howells 
1851c435ee34SDavid Howells 	/* marshall the parameters */
1852c435ee34SDavid Howells 	bp = call->request;
1853c435ee34SDavid Howells 	*bp++ = htonl(FSGIVEUPALLCALLBACKS);
1854c435ee34SDavid Howells 
1855c435ee34SDavid Howells 	/* Can't take a ref on server */
1856d2ddc776SDavid Howells 	return afs_make_call(ac, call, GFP_NOFS, false);
1857d2ddc776SDavid Howells }
1858d2ddc776SDavid Howells 
1859d2ddc776SDavid Howells /*
1860d2ddc776SDavid Howells  * Deliver reply data to an FS.GetCapabilities operation.
1861d2ddc776SDavid Howells  */
1862d2ddc776SDavid Howells static int afs_deliver_fs_get_capabilities(struct afs_call *call)
1863d2ddc776SDavid Howells {
1864d2ddc776SDavid Howells 	u32 count;
1865d2ddc776SDavid Howells 	int ret;
1866d2ddc776SDavid Howells 
1867d2ddc776SDavid Howells 	_enter("{%u,%zu/%u}", call->unmarshall, call->offset, call->count);
1868d2ddc776SDavid Howells 
1869d2ddc776SDavid Howells again:
1870d2ddc776SDavid Howells 	switch (call->unmarshall) {
1871d2ddc776SDavid Howells 	case 0:
1872d2ddc776SDavid Howells 		call->offset = 0;
1873d2ddc776SDavid Howells 		call->unmarshall++;
1874d2ddc776SDavid Howells 
1875d2ddc776SDavid Howells 		/* Extract the capabilities word count */
1876d2ddc776SDavid Howells 	case 1:
1877d2ddc776SDavid Howells 		ret = afs_extract_data(call, &call->tmp,
1878d2ddc776SDavid Howells 				       1 * sizeof(__be32),
1879d2ddc776SDavid Howells 				       true);
1880d2ddc776SDavid Howells 		if (ret < 0)
1881d2ddc776SDavid Howells 			return ret;
1882d2ddc776SDavid Howells 
1883d2ddc776SDavid Howells 		count = ntohl(call->tmp);
1884d2ddc776SDavid Howells 
1885d2ddc776SDavid Howells 		call->count = count;
1886d2ddc776SDavid Howells 		call->count2 = count;
1887d2ddc776SDavid Howells 		call->offset = 0;
1888d2ddc776SDavid Howells 		call->unmarshall++;
1889d2ddc776SDavid Howells 
1890d2ddc776SDavid Howells 		/* Extract capabilities words */
1891d2ddc776SDavid Howells 	case 2:
1892d2ddc776SDavid Howells 		count = min(call->count, 16U);
1893d2ddc776SDavid Howells 		ret = afs_extract_data(call, call->buffer,
1894d2ddc776SDavid Howells 				       count * sizeof(__be32),
1895d2ddc776SDavid Howells 				       call->count > 16);
1896d2ddc776SDavid Howells 		if (ret < 0)
1897d2ddc776SDavid Howells 			return ret;
1898d2ddc776SDavid Howells 
1899d2ddc776SDavid Howells 		/* TODO: Examine capabilities */
1900d2ddc776SDavid Howells 
1901d2ddc776SDavid Howells 		call->count -= count;
1902d2ddc776SDavid Howells 		if (call->count > 0)
1903d2ddc776SDavid Howells 			goto again;
1904d2ddc776SDavid Howells 		call->offset = 0;
1905d2ddc776SDavid Howells 		call->unmarshall++;
1906d2ddc776SDavid Howells 		break;
1907d2ddc776SDavid Howells 	}
1908d2ddc776SDavid Howells 
1909d2ddc776SDavid Howells 	_leave(" = 0 [done]");
1910d2ddc776SDavid Howells 	return 0;
1911d2ddc776SDavid Howells }
1912d2ddc776SDavid Howells 
1913d2ddc776SDavid Howells /*
1914d2ddc776SDavid Howells  * FS.GetCapabilities operation type
1915d2ddc776SDavid Howells  */
1916d2ddc776SDavid Howells static const struct afs_call_type afs_RXFSGetCapabilities = {
1917d2ddc776SDavid Howells 	.name		= "FS.GetCapabilities",
1918025db80cSDavid Howells 	.op		= afs_FS_GetCapabilities,
1919d2ddc776SDavid Howells 	.deliver	= afs_deliver_fs_get_capabilities,
1920d2ddc776SDavid Howells 	.destructor	= afs_flat_call_destructor,
1921d2ddc776SDavid Howells };
1922d2ddc776SDavid Howells 
1923d2ddc776SDavid Howells /*
1924d2ddc776SDavid Howells  * Probe a fileserver for the capabilities that it supports.  This can
1925d2ddc776SDavid Howells  * return up to 196 words.
1926d2ddc776SDavid Howells  */
1927d2ddc776SDavid Howells int afs_fs_get_capabilities(struct afs_net *net,
1928d2ddc776SDavid Howells 			    struct afs_server *server,
1929d2ddc776SDavid Howells 			    struct afs_addr_cursor *ac,
1930d2ddc776SDavid Howells 			    struct key *key)
1931d2ddc776SDavid Howells {
1932d2ddc776SDavid Howells 	struct afs_call *call;
1933d2ddc776SDavid Howells 	__be32 *bp;
1934d2ddc776SDavid Howells 
1935d2ddc776SDavid Howells 	_enter("");
1936d2ddc776SDavid Howells 
1937d2ddc776SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGetCapabilities, 1 * 4, 16 * 4);
1938d2ddc776SDavid Howells 	if (!call)
1939d2ddc776SDavid Howells 		return -ENOMEM;
1940d2ddc776SDavid Howells 
1941d2ddc776SDavid Howells 	call->key = key;
1942d2ddc776SDavid Howells 
1943d2ddc776SDavid Howells 	/* marshall the parameters */
1944d2ddc776SDavid Howells 	bp = call->request;
1945d2ddc776SDavid Howells 	*bp++ = htonl(FSGETCAPABILITIES);
1946d2ddc776SDavid Howells 
1947d2ddc776SDavid Howells 	/* Can't take a ref on server */
1948025db80cSDavid Howells 	trace_afs_make_fs_call(call, NULL);
1949d2ddc776SDavid Howells 	return afs_make_call(ac, call, GFP_NOFS, false);
1950e8d6c554SDavid Howells }
1951