xref: /openbmc/linux/fs/afs/fsclient.c (revision de52cf92)
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>
16a01179e6SJeff Layton #include <linux/iversion.h>
171da177e4SLinus Torvalds #include "internal.h"
1808e0e7c8SDavid Howells #include "afs_fs.h"
19dd9fbcb8SDavid Howells #include "xdr_fs.h"
201da177e4SLinus Torvalds 
21025db80cSDavid Howells static const struct afs_fid afs_zero_fid;
22025db80cSDavid Howells 
231da177e4SLinus Torvalds /*
246db3ac3cSDavid Howells  * We need somewhere to discard into in case the server helpfully returns more
256db3ac3cSDavid Howells  * than we asked for in FS.FetchData{,64}.
266db3ac3cSDavid Howells  */
276db3ac3cSDavid Howells static u8 afs_discard_buffer[64];
286db3ac3cSDavid Howells 
29d2ddc776SDavid Howells static inline void afs_use_fs_server(struct afs_call *call, struct afs_cb_interest *cbi)
30c435ee34SDavid Howells {
31d2ddc776SDavid Howells 	call->cbi = afs_get_cb_interest(cbi);
32c435ee34SDavid Howells }
33c435ee34SDavid Howells 
346db3ac3cSDavid Howells /*
35260a9803SDavid Howells  * decode an AFSFid block
36260a9803SDavid Howells  */
37260a9803SDavid Howells static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
38260a9803SDavid Howells {
39260a9803SDavid Howells 	const __be32 *bp = *_bp;
40260a9803SDavid Howells 
41260a9803SDavid Howells 	fid->vid		= ntohl(*bp++);
42260a9803SDavid Howells 	fid->vnode		= ntohl(*bp++);
43260a9803SDavid Howells 	fid->unique		= ntohl(*bp++);
44260a9803SDavid Howells 	*_bp = bp;
45260a9803SDavid Howells }
46260a9803SDavid Howells 
47260a9803SDavid Howells /*
48888b3384SDavid Howells  * Dump a bad file status record.
49888b3384SDavid Howells  */
50888b3384SDavid Howells static void xdr_dump_bad(const __be32 *bp)
51888b3384SDavid Howells {
52888b3384SDavid Howells 	__be32 x[4];
53888b3384SDavid Howells 	int i;
54888b3384SDavid Howells 
55888b3384SDavid Howells 	pr_notice("AFS XDR: Bad status record\n");
56888b3384SDavid Howells 	for (i = 0; i < 5 * 4 * 4; i += 16) {
57888b3384SDavid Howells 		memcpy(x, bp, 16);
58888b3384SDavid Howells 		bp += 4;
59888b3384SDavid Howells 		pr_notice("%03x: %08x %08x %08x %08x\n",
60888b3384SDavid Howells 			  i, ntohl(x[0]), ntohl(x[1]), ntohl(x[2]), ntohl(x[3]));
61888b3384SDavid Howells 	}
62888b3384SDavid Howells 
63888b3384SDavid Howells 	memcpy(x, bp, 4);
64888b3384SDavid Howells 	pr_notice("0x50: %08x\n", ntohl(x[0]));
65888b3384SDavid Howells }
66888b3384SDavid Howells 
67888b3384SDavid Howells /*
68dd9fbcb8SDavid Howells  * Update the core inode struct from a returned status record.
691da177e4SLinus Torvalds  */
70dd9fbcb8SDavid Howells void afs_update_inode_from_status(struct afs_vnode *vnode,
71260a9803SDavid Howells 				  struct afs_file_status *status,
720c3a5ac2SDavid Howells 				  const afs_dataversion_t *expected_version,
73dd9fbcb8SDavid Howells 				  u8 flags)
741da177e4SLinus Torvalds {
75dd9fbcb8SDavid Howells 	struct timespec t;
7608e0e7c8SDavid Howells 	umode_t mode;
7708e0e7c8SDavid Howells 
78dd9fbcb8SDavid Howells 	t.tv_sec = status->mtime_client;
79dd9fbcb8SDavid Howells 	t.tv_nsec = 0;
80dd9fbcb8SDavid Howells 	vnode->vfs_inode.i_ctime = t;
81dd9fbcb8SDavid Howells 	vnode->vfs_inode.i_mtime = t;
82dd9fbcb8SDavid Howells 	vnode->vfs_inode.i_atime = t;
83c435ee34SDavid Howells 
84dd9fbcb8SDavid Howells 	if (flags & (AFS_VNODE_META_CHANGED | AFS_VNODE_NOT_YET_SET)) {
85dd9fbcb8SDavid Howells 		vnode->vfs_inode.i_uid = make_kuid(&init_user_ns, status->owner);
86dd9fbcb8SDavid Howells 		vnode->vfs_inode.i_gid = make_kgid(&init_user_ns, status->group);
87bfe86848SMiklos Szeredi 		set_nlink(&vnode->vfs_inode, status->nlink);
88260a9803SDavid Howells 
8908e0e7c8SDavid Howells 		mode = vnode->vfs_inode.i_mode;
9008e0e7c8SDavid Howells 		mode &= ~S_IALLUGO;
91260a9803SDavid Howells 		mode |= status->mode;
92260a9803SDavid Howells 		barrier();
9308e0e7c8SDavid Howells 		vnode->vfs_inode.i_mode = mode;
9408e0e7c8SDavid Howells 	}
9508e0e7c8SDavid Howells 
96dd9fbcb8SDavid Howells 	if (!(flags & AFS_VNODE_NOT_YET_SET)) {
97dd9fbcb8SDavid Howells 		if (expected_version &&
98dd9fbcb8SDavid Howells 		    *expected_version != status->data_version) {
99dd9fbcb8SDavid Howells 			_debug("vnode modified %llx on {%x:%u} [exp %llx]",
100dd9fbcb8SDavid Howells 			       (unsigned long long) status->data_version,
101dd9fbcb8SDavid Howells 			       vnode->fid.vid, vnode->fid.vnode,
102dd9fbcb8SDavid Howells 			       (unsigned long long) *expected_version);
103a4ff7401SDavid Howells 			vnode->invalid_before = status->data_version;
104f3ddee8dSDavid Howells 			if (vnode->status.type == AFS_FTYPE_DIR) {
105f3ddee8dSDavid Howells 				if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
106f3ddee8dSDavid Howells 					afs_stat_v(vnode, n_inval);
107f3ddee8dSDavid Howells 			} else {
10808e0e7c8SDavid Howells 				set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
1091da177e4SLinus Torvalds 			}
11063a4681fSDavid Howells 		} else if (vnode->status.type == AFS_FTYPE_DIR) {
11163a4681fSDavid Howells 			/* Expected directory change is handled elsewhere so
11263a4681fSDavid Howells 			 * that we can locally edit the directory and save on a
11363a4681fSDavid Howells 			 * download.
11463a4681fSDavid Howells 			 */
11563a4681fSDavid Howells 			if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
11663a4681fSDavid Howells 				flags &= ~AFS_VNODE_DATA_CHANGED;
117ec26815aSDavid Howells 		}
118f3ddee8dSDavid Howells 	}
119c435ee34SDavid Howells 
120dd9fbcb8SDavid Howells 	if (flags & (AFS_VNODE_DATA_CHANGED | AFS_VNODE_NOT_YET_SET)) {
121dd9fbcb8SDavid Howells 		inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
122dd9fbcb8SDavid Howells 		i_size_write(&vnode->vfs_inode, status->size);
123dd9fbcb8SDavid Howells 	}
124dd9fbcb8SDavid Howells }
125dd9fbcb8SDavid Howells 
126dd9fbcb8SDavid Howells /*
127dd9fbcb8SDavid Howells  * decode an AFSFetchStatus block
128dd9fbcb8SDavid Howells  */
1295f702c8eSDavid Howells static int xdr_decode_AFSFetchStatus(struct afs_call *call,
1305f702c8eSDavid Howells 				     const __be32 **_bp,
131dd9fbcb8SDavid Howells 				     struct afs_file_status *status,
132dd9fbcb8SDavid Howells 				     struct afs_vnode *vnode,
133dd9fbcb8SDavid Howells 				     const afs_dataversion_t *expected_version,
134f3ddee8dSDavid Howells 				     struct afs_read *read_req)
135dd9fbcb8SDavid Howells {
136dd9fbcb8SDavid Howells 	const struct afs_xdr_AFSFetchStatus *xdr = (const void *)*_bp;
137684b0f68SDavid Howells 	bool inline_error = (call->operation_ID == afs_FS_InlineBulkStatus);
138dd9fbcb8SDavid Howells 	u64 data_version, size;
139dd9fbcb8SDavid Howells 	u32 type, abort_code;
140dd9fbcb8SDavid Howells 	u8 flags = 0;
141dd9fbcb8SDavid Howells 
142684b0f68SDavid Howells 	abort_code = ntohl(xdr->abort_code);
143684b0f68SDavid Howells 
144dd9fbcb8SDavid Howells 	if (xdr->if_version != htonl(AFS_FSTATUS_VERSION)) {
145684b0f68SDavid Howells 		if (xdr->if_version == htonl(0) &&
146684b0f68SDavid Howells 		    abort_code != 0 &&
147684b0f68SDavid Howells 		    inline_error) {
148684b0f68SDavid Howells 			/* The OpenAFS fileserver has a bug in FS.InlineBulkStatus
149684b0f68SDavid Howells 			 * whereby it doesn't set the interface version in the error
150684b0f68SDavid Howells 			 * case.
151684b0f68SDavid Howells 			 */
152684b0f68SDavid Howells 			status->abort_code = abort_code;
153de52cf92SAl Viro 			return 0;
154684b0f68SDavid Howells 		}
155684b0f68SDavid Howells 
156dd9fbcb8SDavid Howells 		pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
157dd9fbcb8SDavid Howells 		goto bad;
158dd9fbcb8SDavid Howells 	}
159dd9fbcb8SDavid Howells 
160684b0f68SDavid Howells 	if (abort_code != 0 && inline_error) {
161684b0f68SDavid Howells 		status->abort_code = abort_code;
162de52cf92SAl Viro 		return 0;
163684b0f68SDavid Howells 	}
164684b0f68SDavid Howells 
165dd9fbcb8SDavid Howells 	type = ntohl(xdr->type);
166dd9fbcb8SDavid Howells 	switch (type) {
167dd9fbcb8SDavid Howells 	case AFS_FTYPE_FILE:
168dd9fbcb8SDavid Howells 	case AFS_FTYPE_DIR:
169dd9fbcb8SDavid Howells 	case AFS_FTYPE_SYMLINK:
170dd9fbcb8SDavid Howells 		if (type != status->type &&
171dd9fbcb8SDavid Howells 		    vnode &&
172dd9fbcb8SDavid Howells 		    !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
173dd9fbcb8SDavid Howells 			pr_warning("Vnode %x:%x:%x changed type %u to %u\n",
174dd9fbcb8SDavid Howells 				   vnode->fid.vid,
175dd9fbcb8SDavid Howells 				   vnode->fid.vnode,
176dd9fbcb8SDavid Howells 				   vnode->fid.unique,
177dd9fbcb8SDavid Howells 				   status->type, type);
178dd9fbcb8SDavid Howells 			goto bad;
179dd9fbcb8SDavid Howells 		}
180dd9fbcb8SDavid Howells 		status->type = type;
181dd9fbcb8SDavid Howells 		break;
182dd9fbcb8SDavid Howells 	default:
183dd9fbcb8SDavid Howells 		goto bad;
184dd9fbcb8SDavid Howells 	}
185dd9fbcb8SDavid Howells 
186dd9fbcb8SDavid Howells #define EXTRACT_M(FIELD)					\
187dd9fbcb8SDavid Howells 	do {							\
188dd9fbcb8SDavid Howells 		u32 x = ntohl(xdr->FIELD);			\
189dd9fbcb8SDavid Howells 		if (status->FIELD != x) {			\
190dd9fbcb8SDavid Howells 			flags |= AFS_VNODE_META_CHANGED;	\
191dd9fbcb8SDavid Howells 			status->FIELD = x;			\
192dd9fbcb8SDavid Howells 		}						\
193dd9fbcb8SDavid Howells 	} while (0)
194dd9fbcb8SDavid Howells 
195dd9fbcb8SDavid Howells 	EXTRACT_M(nlink);
196dd9fbcb8SDavid Howells 	EXTRACT_M(author);
197dd9fbcb8SDavid Howells 	EXTRACT_M(owner);
198dd9fbcb8SDavid Howells 	EXTRACT_M(caller_access); /* call ticket dependent */
199dd9fbcb8SDavid Howells 	EXTRACT_M(anon_access);
200dd9fbcb8SDavid Howells 	EXTRACT_M(mode);
201dd9fbcb8SDavid Howells 	EXTRACT_M(group);
202dd9fbcb8SDavid Howells 
203dd9fbcb8SDavid Howells 	status->mtime_client = ntohl(xdr->mtime_client);
204dd9fbcb8SDavid Howells 	status->mtime_server = ntohl(xdr->mtime_server);
205dd9fbcb8SDavid Howells 	status->lock_count   = ntohl(xdr->lock_count);
206dd9fbcb8SDavid Howells 
207dd9fbcb8SDavid Howells 	size  = (u64)ntohl(xdr->size_lo);
208dd9fbcb8SDavid Howells 	size |= (u64)ntohl(xdr->size_hi) << 32;
209dd9fbcb8SDavid Howells 	status->size = size;
210dd9fbcb8SDavid Howells 
211dd9fbcb8SDavid Howells 	data_version  = (u64)ntohl(xdr->data_version_lo);
212dd9fbcb8SDavid Howells 	data_version |= (u64)ntohl(xdr->data_version_hi) << 32;
213dd9fbcb8SDavid Howells 	if (data_version != status->data_version) {
214dd9fbcb8SDavid Howells 		status->data_version = data_version;
215dd9fbcb8SDavid Howells 		flags |= AFS_VNODE_DATA_CHANGED;
216dd9fbcb8SDavid Howells 	}
217f3ddee8dSDavid Howells 
218f3ddee8dSDavid Howells 	if (read_req) {
219f3ddee8dSDavid Howells 		read_req->data_version = data_version;
220f3ddee8dSDavid Howells 		read_req->file_size = size;
221f3ddee8dSDavid Howells 	}
222dd9fbcb8SDavid Howells 
223dd9fbcb8SDavid Howells 	*_bp = (const void *)*_bp + sizeof(*xdr);
224dd9fbcb8SDavid Howells 
225dd9fbcb8SDavid Howells 	if (vnode) {
226dd9fbcb8SDavid Howells 		if (test_bit(AFS_VNODE_UNSET, &vnode->flags))
227dd9fbcb8SDavid Howells 			flags |= AFS_VNODE_NOT_YET_SET;
228dd9fbcb8SDavid Howells 		afs_update_inode_from_status(vnode, status, expected_version,
229dd9fbcb8SDavid Howells 					     flags);
230dd9fbcb8SDavid Howells 	}
231dd9fbcb8SDavid Howells 
232c875c76aSDavid Howells 	return 0;
233dd9fbcb8SDavid Howells 
234dd9fbcb8SDavid Howells bad:
235dd9fbcb8SDavid Howells 	xdr_dump_bad(*_bp);
236c875c76aSDavid Howells 	return afs_protocol_error(call, -EBADMSG);
237c875c76aSDavid Howells }
238c875c76aSDavid Howells 
239c875c76aSDavid Howells /*
240c875c76aSDavid Howells  * Decode the file status.  We need to lock the target vnode if we're going to
241c875c76aSDavid Howells  * update its status so that stat() sees the attributes update atomically.
242c875c76aSDavid Howells  */
243c875c76aSDavid Howells static int afs_decode_status(struct afs_call *call,
244c875c76aSDavid Howells 			     const __be32 **_bp,
245c875c76aSDavid Howells 			     struct afs_file_status *status,
246c875c76aSDavid Howells 			     struct afs_vnode *vnode,
247c875c76aSDavid Howells 			     const afs_dataversion_t *expected_version,
248c875c76aSDavid Howells 			     struct afs_read *read_req)
249c875c76aSDavid Howells {
250c875c76aSDavid Howells 	int ret;
251c875c76aSDavid Howells 
252c875c76aSDavid Howells 	if (!vnode)
253c875c76aSDavid Howells 		return xdr_decode_AFSFetchStatus(call, _bp, status, vnode,
254c875c76aSDavid Howells 						 expected_version, read_req);
255c875c76aSDavid Howells 
256c875c76aSDavid Howells 	write_seqlock(&vnode->cb_lock);
257c875c76aSDavid Howells 	ret = xdr_decode_AFSFetchStatus(call, _bp, status, vnode,
258c875c76aSDavid Howells 					expected_version, read_req);
259c875c76aSDavid Howells 	write_sequnlock(&vnode->cb_lock);
260c875c76aSDavid Howells 	return ret;
261260a9803SDavid Howells }
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds /*
26408e0e7c8SDavid Howells  * decode an AFSCallBack block
2651da177e4SLinus Torvalds  */
266c435ee34SDavid Howells static void xdr_decode_AFSCallBack(struct afs_call *call,
267c435ee34SDavid Howells 				   struct afs_vnode *vnode,
268c435ee34SDavid Howells 				   const __be32 **_bp)
2691da177e4SLinus Torvalds {
270d2ddc776SDavid Howells 	struct afs_cb_interest *old, *cbi = call->cbi;
27108e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
272c435ee34SDavid Howells 	u32 cb_expiry;
2731da177e4SLinus Torvalds 
274c435ee34SDavid Howells 	write_seqlock(&vnode->cb_lock);
275c435ee34SDavid Howells 
27668251f0aSDavid Howells 	if (call->cb_break == afs_cb_break_sum(vnode, cbi)) {
27708e0e7c8SDavid Howells 		vnode->cb_version	= ntohl(*bp++);
278c435ee34SDavid Howells 		cb_expiry		= ntohl(*bp++);
27908e0e7c8SDavid Howells 		vnode->cb_type		= ntohl(*bp++);
280c435ee34SDavid Howells 		vnode->cb_expires_at	= cb_expiry + ktime_get_real_seconds();
281d2ddc776SDavid Howells 		old = vnode->cb_interest;
282d2ddc776SDavid Howells 		if (old != call->cbi) {
283d2ddc776SDavid Howells 			vnode->cb_interest = cbi;
284d2ddc776SDavid Howells 			cbi = old;
285d2ddc776SDavid Howells 		}
286c435ee34SDavid Howells 		set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
287c435ee34SDavid Howells 	} else {
288c435ee34SDavid Howells 		bp += 3;
289c435ee34SDavid Howells 	}
290c435ee34SDavid Howells 
291c435ee34SDavid Howells 	write_sequnlock(&vnode->cb_lock);
292d2ddc776SDavid Howells 	call->cbi = cbi;
29308e0e7c8SDavid Howells 	*_bp = bp;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds 
296260a9803SDavid Howells static void xdr_decode_AFSCallBack_raw(const __be32 **_bp,
297260a9803SDavid Howells 				       struct afs_callback *cb)
298260a9803SDavid Howells {
299260a9803SDavid Howells 	const __be32 *bp = *_bp;
300260a9803SDavid Howells 
301260a9803SDavid Howells 	cb->version	= ntohl(*bp++);
302260a9803SDavid Howells 	cb->expiry	= ntohl(*bp++);
303260a9803SDavid Howells 	cb->type	= ntohl(*bp++);
304260a9803SDavid Howells 	*_bp = bp;
305260a9803SDavid Howells }
306260a9803SDavid Howells 
3071da177e4SLinus Torvalds /*
30808e0e7c8SDavid Howells  * decode an AFSVolSync block
3091da177e4SLinus Torvalds  */
31008e0e7c8SDavid Howells static void xdr_decode_AFSVolSync(const __be32 **_bp,
31108e0e7c8SDavid Howells 				  struct afs_volsync *volsync)
3121da177e4SLinus Torvalds {
31308e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
3141da177e4SLinus Torvalds 
31508e0e7c8SDavid Howells 	volsync->creation = ntohl(*bp++);
31608e0e7c8SDavid Howells 	bp++; /* spare2 */
31708e0e7c8SDavid Howells 	bp++; /* spare3 */
31808e0e7c8SDavid Howells 	bp++; /* spare4 */
31908e0e7c8SDavid Howells 	bp++; /* spare5 */
32008e0e7c8SDavid Howells 	bp++; /* spare6 */
32108e0e7c8SDavid Howells 	*_bp = bp;
3221da177e4SLinus Torvalds }
3231da177e4SLinus Torvalds 
32408e0e7c8SDavid Howells /*
32531143d5dSDavid Howells  * encode the requested attributes into an AFSStoreStatus block
32631143d5dSDavid Howells  */
32731143d5dSDavid Howells static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr)
32831143d5dSDavid Howells {
32931143d5dSDavid Howells 	__be32 *bp = *_bp;
33031143d5dSDavid Howells 	u32 mask = 0, mtime = 0, owner = 0, group = 0, mode = 0;
33131143d5dSDavid Howells 
33231143d5dSDavid Howells 	mask = 0;
33331143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MTIME) {
33431143d5dSDavid Howells 		mask |= AFS_SET_MTIME;
33531143d5dSDavid Howells 		mtime = attr->ia_mtime.tv_sec;
33631143d5dSDavid Howells 	}
33731143d5dSDavid Howells 
33831143d5dSDavid Howells 	if (attr->ia_valid & ATTR_UID) {
33931143d5dSDavid Howells 		mask |= AFS_SET_OWNER;
340a0a5386aSEric W. Biederman 		owner = from_kuid(&init_user_ns, attr->ia_uid);
34131143d5dSDavid Howells 	}
34231143d5dSDavid Howells 
34331143d5dSDavid Howells 	if (attr->ia_valid & ATTR_GID) {
34431143d5dSDavid Howells 		mask |= AFS_SET_GROUP;
345a0a5386aSEric W. Biederman 		group = from_kgid(&init_user_ns, attr->ia_gid);
34631143d5dSDavid Howells 	}
34731143d5dSDavid Howells 
34831143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MODE) {
34931143d5dSDavid Howells 		mask |= AFS_SET_MODE;
35031143d5dSDavid Howells 		mode = attr->ia_mode & S_IALLUGO;
35131143d5dSDavid Howells 	}
35231143d5dSDavid Howells 
35331143d5dSDavid Howells 	*bp++ = htonl(mask);
35431143d5dSDavid Howells 	*bp++ = htonl(mtime);
35531143d5dSDavid Howells 	*bp++ = htonl(owner);
35631143d5dSDavid Howells 	*bp++ = htonl(group);
35731143d5dSDavid Howells 	*bp++ = htonl(mode);
35831143d5dSDavid Howells 	*bp++ = 0;		/* segment size */
35931143d5dSDavid Howells 	*_bp = bp;
36031143d5dSDavid Howells }
36131143d5dSDavid Howells 
36231143d5dSDavid Howells /*
36345222b9eSDavid Howells  * decode an AFSFetchVolumeStatus block
36445222b9eSDavid Howells  */
36545222b9eSDavid Howells static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp,
36645222b9eSDavid Howells 					    struct afs_volume_status *vs)
36745222b9eSDavid Howells {
36845222b9eSDavid Howells 	const __be32 *bp = *_bp;
36945222b9eSDavid Howells 
37045222b9eSDavid Howells 	vs->vid			= ntohl(*bp++);
37145222b9eSDavid Howells 	vs->parent_id		= ntohl(*bp++);
37245222b9eSDavid Howells 	vs->online		= ntohl(*bp++);
37345222b9eSDavid Howells 	vs->in_service		= ntohl(*bp++);
37445222b9eSDavid Howells 	vs->blessed		= ntohl(*bp++);
37545222b9eSDavid Howells 	vs->needs_salvage	= ntohl(*bp++);
37645222b9eSDavid Howells 	vs->type		= ntohl(*bp++);
37745222b9eSDavid Howells 	vs->min_quota		= ntohl(*bp++);
37845222b9eSDavid Howells 	vs->max_quota		= ntohl(*bp++);
37945222b9eSDavid Howells 	vs->blocks_in_use	= ntohl(*bp++);
38045222b9eSDavid Howells 	vs->part_blocks_avail	= ntohl(*bp++);
38145222b9eSDavid Howells 	vs->part_max_blocks	= ntohl(*bp++);
38245222b9eSDavid Howells 	*_bp = bp;
38345222b9eSDavid Howells }
38445222b9eSDavid Howells 
38545222b9eSDavid Howells /*
38608e0e7c8SDavid Howells  * deliver reply data to an FS.FetchStatus
38708e0e7c8SDavid Howells  */
3885cf9dd55SDavid Howells static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call)
38908e0e7c8SDavid Howells {
39097e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
39108e0e7c8SDavid Howells 	const __be32 *bp;
392372ee163SDavid Howells 	int ret;
3931da177e4SLinus Torvalds 
394d001648eSDavid Howells 	ret = afs_transfer_reply(call);
395372ee163SDavid Howells 	if (ret < 0)
396372ee163SDavid Howells 		return ret;
3971da177e4SLinus Torvalds 
398c435ee34SDavid Howells 	_enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode);
399c435ee34SDavid Howells 
40008e0e7c8SDavid Howells 	/* unmarshall the reply once we've received all of it */
40108e0e7c8SDavid Howells 	bp = call->buffer;
402c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, &vnode->status, vnode,
403dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
4045f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
405c435ee34SDavid Howells 	xdr_decode_AFSCallBack(call, vnode, &bp);
40697e3043aSDavid Howells 	if (call->reply[1])
40797e3043aSDavid Howells 		xdr_decode_AFSVolSync(&bp, call->reply[1]);
4081da177e4SLinus Torvalds 
40908e0e7c8SDavid Howells 	_leave(" = 0 [done]");
41008e0e7c8SDavid Howells 	return 0;
411ec26815aSDavid Howells }
41208e0e7c8SDavid Howells 
41308e0e7c8SDavid Howells /*
41408e0e7c8SDavid Howells  * FS.FetchStatus operation type
41508e0e7c8SDavid Howells  */
4165cf9dd55SDavid Howells static const struct afs_call_type afs_RXFSFetchStatus_vnode = {
4175cf9dd55SDavid Howells 	.name		= "FS.FetchStatus(vnode)",
418025db80cSDavid Howells 	.op		= afs_FS_FetchStatus,
4195cf9dd55SDavid Howells 	.deliver	= afs_deliver_fs_fetch_status_vnode,
42008e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
42108e0e7c8SDavid Howells };
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds /*
4241da177e4SLinus Torvalds  * fetch the status information for a file
4251da177e4SLinus Torvalds  */
4260c3a5ac2SDavid Howells int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsync,
4270c3a5ac2SDavid Howells 			     bool new_inode)
4281da177e4SLinus Torvalds {
429d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
43008e0e7c8SDavid Howells 	struct afs_call *call;
431f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
4321da177e4SLinus Torvalds 	__be32 *bp;
4331da177e4SLinus Torvalds 
434416351f2SDavid Howells 	_enter(",%x,{%x:%u},,",
435d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
4361da177e4SLinus Torvalds 
4375cf9dd55SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus_vnode,
4385cf9dd55SDavid Howells 				   16, (21 + 3 + 6) * 4);
439d2ddc776SDavid Howells 	if (!call) {
440d2ddc776SDavid Howells 		fc->ac.error = -ENOMEM;
44108e0e7c8SDavid Howells 		return -ENOMEM;
442d2ddc776SDavid Howells 	}
4431da177e4SLinus Torvalds 
444d2ddc776SDavid Howells 	call->key = fc->key;
44597e3043aSDavid Howells 	call->reply[0] = vnode;
44697e3043aSDavid Howells 	call->reply[1] = volsync;
4470c3a5ac2SDavid Howells 	call->expected_version = new_inode ? 1 : vnode->status.data_version;
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	/* marshall the parameters */
45008e0e7c8SDavid Howells 	bp = call->request;
4511da177e4SLinus Torvalds 	bp[0] = htonl(FSFETCHSTATUS);
4521da177e4SLinus Torvalds 	bp[1] = htonl(vnode->fid.vid);
4531da177e4SLinus Torvalds 	bp[2] = htonl(vnode->fid.vnode);
4541da177e4SLinus Torvalds 	bp[3] = htonl(vnode->fid.unique);
4551da177e4SLinus Torvalds 
456d2ddc776SDavid Howells 	call->cb_break = fc->cb_break;
457d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
458025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
459d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
460ec26815aSDavid Howells }
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds /*
46308e0e7c8SDavid Howells  * deliver reply data to an FS.FetchData
4641da177e4SLinus Torvalds  */
465d001648eSDavid Howells static int afs_deliver_fs_fetch_data(struct afs_call *call)
4661da177e4SLinus Torvalds {
46797e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
46897e3043aSDavid Howells 	struct afs_read *req = call->reply[2];
46908e0e7c8SDavid Howells 	const __be32 *bp;
470196ee9cdSDavid Howells 	unsigned int size;
47108e0e7c8SDavid Howells 	void *buffer;
4721da177e4SLinus Torvalds 	int ret;
4731da177e4SLinus Torvalds 
4746a0e3999SDavid Howells 	_enter("{%u,%zu/%u;%llu/%llu}",
475196ee9cdSDavid Howells 	       call->unmarshall, call->offset, call->count,
476196ee9cdSDavid Howells 	       req->remain, req->actual_len);
4771da177e4SLinus Torvalds 
47808e0e7c8SDavid Howells 	switch (call->unmarshall) {
47908e0e7c8SDavid Howells 	case 0:
480196ee9cdSDavid Howells 		req->actual_len = 0;
48108e0e7c8SDavid Howells 		call->offset = 0;
48208e0e7c8SDavid Howells 		call->unmarshall++;
483b9b1f8d5SDavid Howells 		if (call->operation_ID != FSFETCHDATA64) {
484b9b1f8d5SDavid Howells 			call->unmarshall++;
485b9b1f8d5SDavid Howells 			goto no_msw;
486b9b1f8d5SDavid Howells 		}
4871da177e4SLinus Torvalds 
488b9b1f8d5SDavid Howells 		/* extract the upper part of the returned data length of an
489b9b1f8d5SDavid Howells 		 * FSFETCHDATA64 op (which should always be 0 using this
490b9b1f8d5SDavid Howells 		 * client) */
49108e0e7c8SDavid Howells 	case 1:
492b9b1f8d5SDavid Howells 		_debug("extract data length (MSW)");
493d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
494372ee163SDavid Howells 		if (ret < 0)
495372ee163SDavid Howells 			return ret;
496b9b1f8d5SDavid Howells 
497196ee9cdSDavid Howells 		req->actual_len = ntohl(call->tmp);
498196ee9cdSDavid Howells 		req->actual_len <<= 32;
499b9b1f8d5SDavid Howells 		call->offset = 0;
500b9b1f8d5SDavid Howells 		call->unmarshall++;
501b9b1f8d5SDavid Howells 
502b9b1f8d5SDavid Howells 	no_msw:
503b9b1f8d5SDavid Howells 		/* extract the returned data length */
504b9b1f8d5SDavid Howells 	case 2:
50508e0e7c8SDavid Howells 		_debug("extract data length");
506d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
507372ee163SDavid Howells 		if (ret < 0)
508372ee163SDavid Howells 			return ret;
5091da177e4SLinus Torvalds 
510196ee9cdSDavid Howells 		req->actual_len |= ntohl(call->tmp);
511196ee9cdSDavid Howells 		_debug("DATA length: %llu", req->actual_len);
512196ee9cdSDavid Howells 
513196ee9cdSDavid Howells 		req->remain = req->actual_len;
514196ee9cdSDavid Howells 		call->offset = req->pos & (PAGE_SIZE - 1);
515196ee9cdSDavid Howells 		req->index = 0;
516196ee9cdSDavid Howells 		if (req->actual_len == 0)
517196ee9cdSDavid Howells 			goto no_more_data;
51808e0e7c8SDavid Howells 		call->unmarshall++;
5191da177e4SLinus Torvalds 
520196ee9cdSDavid Howells 	begin_page:
5216db3ac3cSDavid Howells 		ASSERTCMP(req->index, <, req->nr_pages);
522196ee9cdSDavid Howells 		if (req->remain > PAGE_SIZE - call->offset)
523196ee9cdSDavid Howells 			size = PAGE_SIZE - call->offset;
524196ee9cdSDavid Howells 		else
525196ee9cdSDavid Howells 			size = req->remain;
526196ee9cdSDavid Howells 		call->count = call->offset + size;
527196ee9cdSDavid Howells 		ASSERTCMP(call->count, <=, PAGE_SIZE);
528196ee9cdSDavid Howells 		req->remain -= size;
529196ee9cdSDavid Howells 
53008e0e7c8SDavid Howells 		/* extract the returned data */
531b9b1f8d5SDavid Howells 	case 3:
5326a0e3999SDavid Howells 		_debug("extract data %llu/%llu %zu/%u",
533196ee9cdSDavid Howells 		       req->remain, req->actual_len, call->offset, call->count);
534196ee9cdSDavid Howells 
535196ee9cdSDavid Howells 		buffer = kmap(req->pages[req->index]);
536196ee9cdSDavid Howells 		ret = afs_extract_data(call, buffer, call->count, true);
537196ee9cdSDavid Howells 		kunmap(req->pages[req->index]);
538372ee163SDavid Howells 		if (ret < 0)
539372ee163SDavid Howells 			return ret;
540196ee9cdSDavid Howells 		if (call->offset == PAGE_SIZE) {
541196ee9cdSDavid Howells 			if (req->page_done)
542196ee9cdSDavid Howells 				req->page_done(call, req);
54329f06985SDavid Howells 			req->index++;
544196ee9cdSDavid Howells 			if (req->remain > 0) {
545196ee9cdSDavid Howells 				call->offset = 0;
546e8e581a8SDavid Howells 				if (req->index >= req->nr_pages) {
547e8e581a8SDavid Howells 					call->unmarshall = 4;
5486db3ac3cSDavid Howells 					goto begin_discard;
549e8e581a8SDavid Howells 				}
550196ee9cdSDavid Howells 				goto begin_page;
551196ee9cdSDavid Howells 			}
552416351f2SDavid Howells 		}
5536db3ac3cSDavid Howells 		goto no_more_data;
5546db3ac3cSDavid Howells 
5556db3ac3cSDavid Howells 		/* Discard any excess data the server gave us */
5566db3ac3cSDavid Howells 	begin_discard:
5576db3ac3cSDavid Howells 	case 4:
5586a0e3999SDavid Howells 		size = min_t(loff_t, sizeof(afs_discard_buffer), req->remain);
5596db3ac3cSDavid Howells 		call->count = size;
5606a0e3999SDavid Howells 		_debug("extract discard %llu/%llu %zu/%u",
5616db3ac3cSDavid Howells 		       req->remain, req->actual_len, call->offset, call->count);
5626db3ac3cSDavid Howells 
5636db3ac3cSDavid Howells 		call->offset = 0;
5646db3ac3cSDavid Howells 		ret = afs_extract_data(call, afs_discard_buffer, call->count, true);
5656db3ac3cSDavid Howells 		req->remain -= call->offset;
5666db3ac3cSDavid Howells 		if (ret < 0)
5676db3ac3cSDavid Howells 			return ret;
5686db3ac3cSDavid Howells 		if (req->remain > 0)
5696db3ac3cSDavid Howells 			goto begin_discard;
5701da177e4SLinus Torvalds 
571196ee9cdSDavid Howells 	no_more_data:
57208e0e7c8SDavid Howells 		call->offset = 0;
5736db3ac3cSDavid Howells 		call->unmarshall = 5;
57408e0e7c8SDavid Howells 
57508e0e7c8SDavid Howells 		/* extract the metadata */
5766db3ac3cSDavid Howells 	case 5:
577d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
578d001648eSDavid Howells 				       (21 + 3 + 6) * 4, false);
579372ee163SDavid Howells 		if (ret < 0)
580372ee163SDavid Howells 			return ret;
5811da177e4SLinus Torvalds 
58208e0e7c8SDavid Howells 		bp = call->buffer;
583c875c76aSDavid Howells 		if (afs_decode_status(call, &bp, &vnode->status, vnode,
584f3ddee8dSDavid Howells 				      &vnode->status.data_version, req) < 0)
5855f702c8eSDavid Howells 			return afs_protocol_error(call, -EBADMSG);
586c435ee34SDavid Howells 		xdr_decode_AFSCallBack(call, vnode, &bp);
58797e3043aSDavid Howells 		if (call->reply[1])
58897e3043aSDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply[1]);
5891da177e4SLinus Torvalds 
59008e0e7c8SDavid Howells 		call->offset = 0;
59108e0e7c8SDavid Howells 		call->unmarshall++;
5921da177e4SLinus Torvalds 
5936db3ac3cSDavid Howells 	case 6:
5941da177e4SLinus Torvalds 		break;
5951da177e4SLinus Torvalds 	}
5961da177e4SLinus Torvalds 
5976db3ac3cSDavid Howells 	for (; req->index < req->nr_pages; req->index++) {
5986db3ac3cSDavid Howells 		if (call->count < PAGE_SIZE)
5996db3ac3cSDavid Howells 			zero_user_segment(req->pages[req->index],
6006db3ac3cSDavid Howells 					  call->count, PAGE_SIZE);
601196ee9cdSDavid Howells 		if (req->page_done)
602196ee9cdSDavid Howells 			req->page_done(call, req);
6036db3ac3cSDavid Howells 		call->count = 0;
604416351f2SDavid Howells 	}
605416351f2SDavid Howells 
60608e0e7c8SDavid Howells 	_leave(" = 0 [done]");
60708e0e7c8SDavid Howells 	return 0;
608ec26815aSDavid Howells }
6091da177e4SLinus Torvalds 
610196ee9cdSDavid Howells static void afs_fetch_data_destructor(struct afs_call *call)
611196ee9cdSDavid Howells {
61297e3043aSDavid Howells 	struct afs_read *req = call->reply[2];
613196ee9cdSDavid Howells 
614196ee9cdSDavid Howells 	afs_put_read(req);
615196ee9cdSDavid Howells 	afs_flat_call_destructor(call);
616196ee9cdSDavid Howells }
617196ee9cdSDavid Howells 
6181da177e4SLinus Torvalds /*
61908e0e7c8SDavid Howells  * FS.FetchData operation type
6201da177e4SLinus Torvalds  */
62108e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchData = {
62200d3b7a4SDavid Howells 	.name		= "FS.FetchData",
623025db80cSDavid Howells 	.op		= afs_FS_FetchData,
62408e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
625196ee9cdSDavid Howells 	.destructor	= afs_fetch_data_destructor,
62608e0e7c8SDavid Howells };
62708e0e7c8SDavid Howells 
628b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSFetchData64 = {
629b9b1f8d5SDavid Howells 	.name		= "FS.FetchData64",
630025db80cSDavid Howells 	.op		= afs_FS_FetchData64,
631b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
632196ee9cdSDavid Howells 	.destructor	= afs_fetch_data_destructor,
633b9b1f8d5SDavid Howells };
634b9b1f8d5SDavid Howells 
635b9b1f8d5SDavid Howells /*
636b9b1f8d5SDavid Howells  * fetch data from a very large file
637b9b1f8d5SDavid Howells  */
638d2ddc776SDavid Howells static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, struct afs_read *req)
639b9b1f8d5SDavid Howells {
640d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
641b9b1f8d5SDavid Howells 	struct afs_call *call;
642f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
643b9b1f8d5SDavid Howells 	__be32 *bp;
644b9b1f8d5SDavid Howells 
645b9b1f8d5SDavid Howells 	_enter("");
646b9b1f8d5SDavid Howells 
647f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
648b9b1f8d5SDavid Howells 	if (!call)
649b9b1f8d5SDavid Howells 		return -ENOMEM;
650b9b1f8d5SDavid Howells 
651d2ddc776SDavid Howells 	call->key = fc->key;
65297e3043aSDavid Howells 	call->reply[0] = vnode;
65397e3043aSDavid Howells 	call->reply[1] = NULL; /* volsync */
65497e3043aSDavid Howells 	call->reply[2] = req;
6550c3a5ac2SDavid Howells 	call->expected_version = vnode->status.data_version;
656b9b1f8d5SDavid Howells 
657b9b1f8d5SDavid Howells 	/* marshall the parameters */
658b9b1f8d5SDavid Howells 	bp = call->request;
659b9b1f8d5SDavid Howells 	bp[0] = htonl(FSFETCHDATA64);
660b9b1f8d5SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
661b9b1f8d5SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
662b9b1f8d5SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
663196ee9cdSDavid Howells 	bp[4] = htonl(upper_32_bits(req->pos));
664196ee9cdSDavid Howells 	bp[5] = htonl(lower_32_bits(req->pos));
665b9b1f8d5SDavid Howells 	bp[6] = 0;
666196ee9cdSDavid Howells 	bp[7] = htonl(lower_32_bits(req->len));
667b9b1f8d5SDavid Howells 
668f3ddee8dSDavid Howells 	refcount_inc(&req->usage);
669d2ddc776SDavid Howells 	call->cb_break = fc->cb_break;
670d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
671025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
672d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
673b9b1f8d5SDavid Howells }
674b9b1f8d5SDavid Howells 
67508e0e7c8SDavid Howells /*
67608e0e7c8SDavid Howells  * fetch data from a file
67708e0e7c8SDavid Howells  */
678d2ddc776SDavid Howells int afs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
6791da177e4SLinus Torvalds {
680d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
68108e0e7c8SDavid Howells 	struct afs_call *call;
682f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
68308e0e7c8SDavid Howells 	__be32 *bp;
6841da177e4SLinus Torvalds 
685196ee9cdSDavid Howells 	if (upper_32_bits(req->pos) ||
686196ee9cdSDavid Howells 	    upper_32_bits(req->len) ||
687196ee9cdSDavid Howells 	    upper_32_bits(req->pos + req->len))
688d2ddc776SDavid Howells 		return afs_fs_fetch_data64(fc, req);
689b9b1f8d5SDavid Howells 
69008e0e7c8SDavid Howells 	_enter("");
6911da177e4SLinus Torvalds 
692f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
69308e0e7c8SDavid Howells 	if (!call)
69408e0e7c8SDavid Howells 		return -ENOMEM;
6951da177e4SLinus Torvalds 
696d2ddc776SDavid Howells 	call->key = fc->key;
69797e3043aSDavid Howells 	call->reply[0] = vnode;
69897e3043aSDavid Howells 	call->reply[1] = NULL; /* volsync */
69997e3043aSDavid Howells 	call->reply[2] = req;
7000c3a5ac2SDavid Howells 	call->expected_version = vnode->status.data_version;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	/* marshall the parameters */
70308e0e7c8SDavid Howells 	bp = call->request;
70408e0e7c8SDavid Howells 	bp[0] = htonl(FSFETCHDATA);
70508e0e7c8SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
70608e0e7c8SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
70708e0e7c8SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
708196ee9cdSDavid Howells 	bp[4] = htonl(lower_32_bits(req->pos));
709196ee9cdSDavid Howells 	bp[5] = htonl(lower_32_bits(req->len));
7101da177e4SLinus Torvalds 
711f3ddee8dSDavid Howells 	refcount_inc(&req->usage);
712d2ddc776SDavid Howells 	call->cb_break = fc->cb_break;
713d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
714025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
715d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
71608e0e7c8SDavid Howells }
717260a9803SDavid Howells 
718260a9803SDavid Howells /*
719260a9803SDavid Howells  * deliver reply data to an FS.CreateFile or an FS.MakeDir
720260a9803SDavid Howells  */
721d001648eSDavid Howells static int afs_deliver_fs_create_vnode(struct afs_call *call)
722260a9803SDavid Howells {
72397e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
724260a9803SDavid Howells 	const __be32 *bp;
725372ee163SDavid Howells 	int ret;
726260a9803SDavid Howells 
727d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
728260a9803SDavid Howells 
729d001648eSDavid Howells 	ret = afs_transfer_reply(call);
730372ee163SDavid Howells 	if (ret < 0)
731372ee163SDavid Howells 		return ret;
732260a9803SDavid Howells 
733260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
734260a9803SDavid Howells 	bp = call->buffer;
73597e3043aSDavid Howells 	xdr_decode_AFSFid(&bp, call->reply[1]);
736c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
737c875c76aSDavid Howells 	    afs_decode_status(call, &bp, &vnode->status, vnode,
738dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
7395f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
74097e3043aSDavid Howells 	xdr_decode_AFSCallBack_raw(&bp, call->reply[3]);
74197e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
742260a9803SDavid Howells 
743260a9803SDavid Howells 	_leave(" = 0 [done]");
744260a9803SDavid Howells 	return 0;
745260a9803SDavid Howells }
746260a9803SDavid Howells 
747260a9803SDavid Howells /*
748260a9803SDavid Howells  * FS.CreateFile and FS.MakeDir operation type
749260a9803SDavid Howells  */
750025db80cSDavid Howells static const struct afs_call_type afs_RXFSCreateFile = {
751025db80cSDavid Howells 	.name		= "FS.CreateFile",
752025db80cSDavid Howells 	.op		= afs_FS_CreateFile,
753025db80cSDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
754025db80cSDavid Howells 	.destructor	= afs_flat_call_destructor,
755025db80cSDavid Howells };
756025db80cSDavid Howells 
757025db80cSDavid Howells static const struct afs_call_type afs_RXFSMakeDir = {
758025db80cSDavid Howells 	.name		= "FS.MakeDir",
759025db80cSDavid Howells 	.op		= afs_FS_MakeDir,
760260a9803SDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
761260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
762260a9803SDavid Howells };
763260a9803SDavid Howells 
764260a9803SDavid Howells /*
765260a9803SDavid Howells  * create a file or make a directory
766260a9803SDavid Howells  */
7678b2a464cSDavid Howells int afs_fs_create(struct afs_fs_cursor *fc,
768260a9803SDavid Howells 		  const char *name,
769260a9803SDavid Howells 		  umode_t mode,
77063a4681fSDavid Howells 		  u64 current_data_version,
771260a9803SDavid Howells 		  struct afs_fid *newfid,
772260a9803SDavid Howells 		  struct afs_file_status *newstatus,
773d2ddc776SDavid Howells 		  struct afs_callback *newcb)
774260a9803SDavid Howells {
775d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
776260a9803SDavid Howells 	struct afs_call *call;
777f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
778260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
779260a9803SDavid Howells 	__be32 *bp;
780260a9803SDavid Howells 
781260a9803SDavid Howells 	_enter("");
782260a9803SDavid Howells 
783260a9803SDavid Howells 	namesz = strlen(name);
784260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
785260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (6 * 4);
786260a9803SDavid Howells 
787025db80cSDavid Howells 	call = afs_alloc_flat_call(
788025db80cSDavid Howells 		net, S_ISDIR(mode) ? &afs_RXFSMakeDir : &afs_RXFSCreateFile,
789025db80cSDavid Howells 		reqsz, (3 + 21 + 21 + 3 + 6) * 4);
790260a9803SDavid Howells 	if (!call)
791260a9803SDavid Howells 		return -ENOMEM;
792260a9803SDavid Howells 
793d2ddc776SDavid Howells 	call->key = fc->key;
79497e3043aSDavid Howells 	call->reply[0] = vnode;
79597e3043aSDavid Howells 	call->reply[1] = newfid;
79697e3043aSDavid Howells 	call->reply[2] = newstatus;
79797e3043aSDavid Howells 	call->reply[3] = newcb;
79863a4681fSDavid Howells 	call->expected_version = current_data_version + 1;
799260a9803SDavid Howells 
800260a9803SDavid Howells 	/* marshall the parameters */
801260a9803SDavid Howells 	bp = call->request;
802260a9803SDavid Howells 	*bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE);
803260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
804260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
805260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
806260a9803SDavid Howells 	*bp++ = htonl(namesz);
807260a9803SDavid Howells 	memcpy(bp, name, namesz);
808260a9803SDavid Howells 	bp = (void *) bp + namesz;
809260a9803SDavid Howells 	if (padsz > 0) {
810260a9803SDavid Howells 		memset(bp, 0, padsz);
811260a9803SDavid Howells 		bp = (void *) bp + padsz;
812260a9803SDavid Howells 	}
813ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
814ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
815260a9803SDavid Howells 	*bp++ = 0; /* owner */
816260a9803SDavid Howells 	*bp++ = 0; /* group */
817260a9803SDavid Howells 	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
818260a9803SDavid Howells 	*bp++ = 0; /* segment size */
819260a9803SDavid Howells 
820d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
821025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
822d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
823260a9803SDavid Howells }
824260a9803SDavid Howells 
825260a9803SDavid Howells /*
826260a9803SDavid Howells  * deliver reply data to an FS.RemoveFile or FS.RemoveDir
827260a9803SDavid Howells  */
828d001648eSDavid Howells static int afs_deliver_fs_remove(struct afs_call *call)
829260a9803SDavid Howells {
83097e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
831260a9803SDavid Howells 	const __be32 *bp;
832372ee163SDavid Howells 	int ret;
833260a9803SDavid Howells 
834d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
835260a9803SDavid Howells 
836d001648eSDavid Howells 	ret = afs_transfer_reply(call);
837372ee163SDavid Howells 	if (ret < 0)
838372ee163SDavid Howells 		return ret;
839260a9803SDavid Howells 
840260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
841260a9803SDavid Howells 	bp = call->buffer;
842c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, &vnode->status, vnode,
843dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
8445f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
84597e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
846260a9803SDavid Howells 
847260a9803SDavid Howells 	_leave(" = 0 [done]");
848260a9803SDavid Howells 	return 0;
849260a9803SDavid Howells }
850260a9803SDavid Howells 
851260a9803SDavid Howells /*
852260a9803SDavid Howells  * FS.RemoveDir/FS.RemoveFile operation type
853260a9803SDavid Howells  */
854025db80cSDavid Howells static const struct afs_call_type afs_RXFSRemoveFile = {
855025db80cSDavid Howells 	.name		= "FS.RemoveFile",
856025db80cSDavid Howells 	.op		= afs_FS_RemoveFile,
857025db80cSDavid Howells 	.deliver	= afs_deliver_fs_remove,
858025db80cSDavid Howells 	.destructor	= afs_flat_call_destructor,
859025db80cSDavid Howells };
860025db80cSDavid Howells 
861025db80cSDavid Howells static const struct afs_call_type afs_RXFSRemoveDir = {
862025db80cSDavid Howells 	.name		= "FS.RemoveDir",
863025db80cSDavid Howells 	.op		= afs_FS_RemoveDir,
864260a9803SDavid Howells 	.deliver	= afs_deliver_fs_remove,
865260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
866260a9803SDavid Howells };
867260a9803SDavid Howells 
868260a9803SDavid Howells /*
869260a9803SDavid Howells  * remove a file or directory
870260a9803SDavid Howells  */
87163a4681fSDavid Howells int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir,
87263a4681fSDavid Howells 		  u64 current_data_version)
873260a9803SDavid Howells {
874d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
875260a9803SDavid Howells 	struct afs_call *call;
876f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
877260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
878260a9803SDavid Howells 	__be32 *bp;
879260a9803SDavid Howells 
880260a9803SDavid Howells 	_enter("");
881260a9803SDavid Howells 
882260a9803SDavid Howells 	namesz = strlen(name);
883260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
884260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz;
885260a9803SDavid Howells 
886025db80cSDavid Howells 	call = afs_alloc_flat_call(
887025db80cSDavid Howells 		net, isdir ? &afs_RXFSRemoveDir : &afs_RXFSRemoveFile,
888025db80cSDavid Howells 		reqsz, (21 + 6) * 4);
889260a9803SDavid Howells 	if (!call)
890260a9803SDavid Howells 		return -ENOMEM;
891260a9803SDavid Howells 
892d2ddc776SDavid Howells 	call->key = fc->key;
89397e3043aSDavid Howells 	call->reply[0] = vnode;
89463a4681fSDavid Howells 	call->expected_version = current_data_version + 1;
895260a9803SDavid Howells 
896260a9803SDavid Howells 	/* marshall the parameters */
897260a9803SDavid Howells 	bp = call->request;
898260a9803SDavid Howells 	*bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE);
899260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
900260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
901260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
902260a9803SDavid Howells 	*bp++ = htonl(namesz);
903260a9803SDavid Howells 	memcpy(bp, name, namesz);
904260a9803SDavid Howells 	bp = (void *) bp + namesz;
905260a9803SDavid Howells 	if (padsz > 0) {
906260a9803SDavid Howells 		memset(bp, 0, padsz);
907260a9803SDavid Howells 		bp = (void *) bp + padsz;
908260a9803SDavid Howells 	}
909260a9803SDavid Howells 
910d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
911025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
912d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
913260a9803SDavid Howells }
914260a9803SDavid Howells 
915260a9803SDavid Howells /*
916260a9803SDavid Howells  * deliver reply data to an FS.Link
917260a9803SDavid Howells  */
918d001648eSDavid Howells static int afs_deliver_fs_link(struct afs_call *call)
919260a9803SDavid Howells {
92097e3043aSDavid Howells 	struct afs_vnode *dvnode = call->reply[0], *vnode = call->reply[1];
921260a9803SDavid Howells 	const __be32 *bp;
922372ee163SDavid Howells 	int ret;
923260a9803SDavid Howells 
924d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
925260a9803SDavid Howells 
926d001648eSDavid Howells 	ret = afs_transfer_reply(call);
927372ee163SDavid Howells 	if (ret < 0)
928372ee163SDavid Howells 		return ret;
929260a9803SDavid Howells 
930260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
931260a9803SDavid Howells 	bp = call->buffer;
932c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
933c875c76aSDavid Howells 	    afs_decode_status(call, &bp, &dvnode->status, dvnode,
934dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
9355f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
93697e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
937260a9803SDavid Howells 
938260a9803SDavid Howells 	_leave(" = 0 [done]");
939260a9803SDavid Howells 	return 0;
940260a9803SDavid Howells }
941260a9803SDavid Howells 
942260a9803SDavid Howells /*
943260a9803SDavid Howells  * FS.Link operation type
944260a9803SDavid Howells  */
945260a9803SDavid Howells static const struct afs_call_type afs_RXFSLink = {
946260a9803SDavid Howells 	.name		= "FS.Link",
947025db80cSDavid Howells 	.op		= afs_FS_Link,
948260a9803SDavid Howells 	.deliver	= afs_deliver_fs_link,
949260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
950260a9803SDavid Howells };
951260a9803SDavid Howells 
952260a9803SDavid Howells /*
953260a9803SDavid Howells  * make a hard link
954260a9803SDavid Howells  */
955d2ddc776SDavid Howells int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
95663a4681fSDavid Howells 		const char *name, u64 current_data_version)
957260a9803SDavid Howells {
958d2ddc776SDavid Howells 	struct afs_vnode *dvnode = fc->vnode;
959260a9803SDavid Howells 	struct afs_call *call;
960f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
961260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
962260a9803SDavid Howells 	__be32 *bp;
963260a9803SDavid Howells 
964260a9803SDavid Howells 	_enter("");
965260a9803SDavid Howells 
966260a9803SDavid Howells 	namesz = strlen(name);
967260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
968260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (3 * 4);
969260a9803SDavid Howells 
970f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
971260a9803SDavid Howells 	if (!call)
972260a9803SDavid Howells 		return -ENOMEM;
973260a9803SDavid Howells 
974d2ddc776SDavid Howells 	call->key = fc->key;
97597e3043aSDavid Howells 	call->reply[0] = dvnode;
97697e3043aSDavid Howells 	call->reply[1] = vnode;
97763a4681fSDavid Howells 	call->expected_version = current_data_version + 1;
978260a9803SDavid Howells 
979260a9803SDavid Howells 	/* marshall the parameters */
980260a9803SDavid Howells 	bp = call->request;
981260a9803SDavid Howells 	*bp++ = htonl(FSLINK);
982260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vid);
983260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vnode);
984260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.unique);
985260a9803SDavid Howells 	*bp++ = htonl(namesz);
986260a9803SDavid Howells 	memcpy(bp, name, namesz);
987260a9803SDavid Howells 	bp = (void *) bp + namesz;
988260a9803SDavid Howells 	if (padsz > 0) {
989260a9803SDavid Howells 		memset(bp, 0, padsz);
990260a9803SDavid Howells 		bp = (void *) bp + padsz;
991260a9803SDavid Howells 	}
992260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
993260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
994260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
995260a9803SDavid Howells 
996d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
997025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
998d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
999260a9803SDavid Howells }
1000260a9803SDavid Howells 
1001260a9803SDavid Howells /*
1002260a9803SDavid Howells  * deliver reply data to an FS.Symlink
1003260a9803SDavid Howells  */
1004d001648eSDavid Howells static int afs_deliver_fs_symlink(struct afs_call *call)
1005260a9803SDavid Howells {
100697e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
1007260a9803SDavid Howells 	const __be32 *bp;
1008372ee163SDavid Howells 	int ret;
1009260a9803SDavid Howells 
1010d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
1011260a9803SDavid Howells 
1012d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1013372ee163SDavid Howells 	if (ret < 0)
1014372ee163SDavid Howells 		return ret;
1015260a9803SDavid Howells 
1016260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
1017260a9803SDavid Howells 	bp = call->buffer;
101897e3043aSDavid Howells 	xdr_decode_AFSFid(&bp, call->reply[1]);
1019c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) ||
1020c875c76aSDavid Howells 	    afs_decode_status(call, &bp, &vnode->status, vnode,
1021dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
10225f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
102397e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
1024260a9803SDavid Howells 
1025260a9803SDavid Howells 	_leave(" = 0 [done]");
1026260a9803SDavid Howells 	return 0;
1027260a9803SDavid Howells }
1028260a9803SDavid Howells 
1029260a9803SDavid Howells /*
1030260a9803SDavid Howells  * FS.Symlink operation type
1031260a9803SDavid Howells  */
1032260a9803SDavid Howells static const struct afs_call_type afs_RXFSSymlink = {
1033260a9803SDavid Howells 	.name		= "FS.Symlink",
1034025db80cSDavid Howells 	.op		= afs_FS_Symlink,
1035260a9803SDavid Howells 	.deliver	= afs_deliver_fs_symlink,
1036260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
1037260a9803SDavid Howells };
1038260a9803SDavid Howells 
1039260a9803SDavid Howells /*
1040260a9803SDavid Howells  * create a symbolic link
1041260a9803SDavid Howells  */
10428b2a464cSDavid Howells int afs_fs_symlink(struct afs_fs_cursor *fc,
1043260a9803SDavid Howells 		   const char *name,
1044260a9803SDavid Howells 		   const char *contents,
104563a4681fSDavid Howells 		   u64 current_data_version,
1046260a9803SDavid Howells 		   struct afs_fid *newfid,
1047d2ddc776SDavid Howells 		   struct afs_file_status *newstatus)
1048260a9803SDavid Howells {
1049d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1050260a9803SDavid Howells 	struct afs_call *call;
1051f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1052260a9803SDavid Howells 	size_t namesz, reqsz, padsz, c_namesz, c_padsz;
1053260a9803SDavid Howells 	__be32 *bp;
1054260a9803SDavid Howells 
1055260a9803SDavid Howells 	_enter("");
1056260a9803SDavid Howells 
1057260a9803SDavid Howells 	namesz = strlen(name);
1058260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
1059260a9803SDavid Howells 
1060260a9803SDavid Howells 	c_namesz = strlen(contents);
1061260a9803SDavid Howells 	c_padsz = (4 - (c_namesz & 3)) & 3;
1062260a9803SDavid Howells 
1063260a9803SDavid Howells 	reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
1064260a9803SDavid Howells 
1065f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSSymlink, reqsz,
1066260a9803SDavid Howells 				   (3 + 21 + 21 + 6) * 4);
1067260a9803SDavid Howells 	if (!call)
1068260a9803SDavid Howells 		return -ENOMEM;
1069260a9803SDavid Howells 
1070d2ddc776SDavid Howells 	call->key = fc->key;
107197e3043aSDavid Howells 	call->reply[0] = vnode;
107297e3043aSDavid Howells 	call->reply[1] = newfid;
107397e3043aSDavid Howells 	call->reply[2] = newstatus;
107463a4681fSDavid Howells 	call->expected_version = current_data_version + 1;
1075260a9803SDavid Howells 
1076260a9803SDavid Howells 	/* marshall the parameters */
1077260a9803SDavid Howells 	bp = call->request;
1078260a9803SDavid Howells 	*bp++ = htonl(FSSYMLINK);
1079260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1080260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1081260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1082260a9803SDavid Howells 	*bp++ = htonl(namesz);
1083260a9803SDavid Howells 	memcpy(bp, name, namesz);
1084260a9803SDavid Howells 	bp = (void *) bp + namesz;
1085260a9803SDavid Howells 	if (padsz > 0) {
1086260a9803SDavid Howells 		memset(bp, 0, padsz);
1087260a9803SDavid Howells 		bp = (void *) bp + padsz;
1088260a9803SDavid Howells 	}
1089260a9803SDavid Howells 	*bp++ = htonl(c_namesz);
1090260a9803SDavid Howells 	memcpy(bp, contents, c_namesz);
1091260a9803SDavid Howells 	bp = (void *) bp + c_namesz;
1092260a9803SDavid Howells 	if (c_padsz > 0) {
1093260a9803SDavid Howells 		memset(bp, 0, c_padsz);
1094260a9803SDavid Howells 		bp = (void *) bp + c_padsz;
1095260a9803SDavid Howells 	}
1096ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
1097ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
1098260a9803SDavid Howells 	*bp++ = 0; /* owner */
1099260a9803SDavid Howells 	*bp++ = 0; /* group */
1100260a9803SDavid Howells 	*bp++ = htonl(S_IRWXUGO); /* unix mode */
1101260a9803SDavid Howells 	*bp++ = 0; /* segment size */
1102260a9803SDavid Howells 
1103d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1104025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1105d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1106260a9803SDavid Howells }
1107260a9803SDavid Howells 
1108260a9803SDavid Howells /*
1109260a9803SDavid Howells  * deliver reply data to an FS.Rename
1110260a9803SDavid Howells  */
1111d001648eSDavid Howells static int afs_deliver_fs_rename(struct afs_call *call)
1112260a9803SDavid Howells {
111397e3043aSDavid Howells 	struct afs_vnode *orig_dvnode = call->reply[0], *new_dvnode = call->reply[1];
1114260a9803SDavid Howells 	const __be32 *bp;
1115372ee163SDavid Howells 	int ret;
1116260a9803SDavid Howells 
1117d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
1118260a9803SDavid Howells 
1119d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1120372ee163SDavid Howells 	if (ret < 0)
1121372ee163SDavid Howells 		return ret;
1122260a9803SDavid Howells 
1123260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
1124260a9803SDavid Howells 	bp = call->buffer;
1125c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, &orig_dvnode->status, orig_dvnode,
1126dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
11275f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
1128dd9fbcb8SDavid Howells 	if (new_dvnode != orig_dvnode &&
1129c875c76aSDavid Howells 	    afs_decode_status(call, &bp, &new_dvnode->status, new_dvnode,
1130dd9fbcb8SDavid Howells 			      &call->expected_version_2, NULL) < 0)
11315f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
113297e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
1133260a9803SDavid Howells 
1134260a9803SDavid Howells 	_leave(" = 0 [done]");
1135260a9803SDavid Howells 	return 0;
1136260a9803SDavid Howells }
1137260a9803SDavid Howells 
1138260a9803SDavid Howells /*
1139260a9803SDavid Howells  * FS.Rename operation type
1140260a9803SDavid Howells  */
1141260a9803SDavid Howells static const struct afs_call_type afs_RXFSRename = {
1142260a9803SDavid Howells 	.name		= "FS.Rename",
1143025db80cSDavid Howells 	.op		= afs_FS_Rename,
1144260a9803SDavid Howells 	.deliver	= afs_deliver_fs_rename,
1145260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
1146260a9803SDavid Howells };
1147260a9803SDavid Howells 
1148260a9803SDavid Howells /*
1149260a9803SDavid Howells  * create a symbolic link
1150260a9803SDavid Howells  */
11518b2a464cSDavid Howells int afs_fs_rename(struct afs_fs_cursor *fc,
1152260a9803SDavid Howells 		  const char *orig_name,
1153260a9803SDavid Howells 		  struct afs_vnode *new_dvnode,
115463a4681fSDavid Howells 		  const char *new_name,
115563a4681fSDavid Howells 		  u64 current_orig_data_version,
115663a4681fSDavid Howells 		  u64 current_new_data_version)
1157260a9803SDavid Howells {
1158d2ddc776SDavid Howells 	struct afs_vnode *orig_dvnode = fc->vnode;
1159260a9803SDavid Howells 	struct afs_call *call;
1160f044c884SDavid Howells 	struct afs_net *net = afs_v2net(orig_dvnode);
1161260a9803SDavid Howells 	size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
1162260a9803SDavid Howells 	__be32 *bp;
1163260a9803SDavid Howells 
1164260a9803SDavid Howells 	_enter("");
1165260a9803SDavid Howells 
1166260a9803SDavid Howells 	o_namesz = strlen(orig_name);
1167260a9803SDavid Howells 	o_padsz = (4 - (o_namesz & 3)) & 3;
1168260a9803SDavid Howells 
1169260a9803SDavid Howells 	n_namesz = strlen(new_name);
1170260a9803SDavid Howells 	n_padsz = (4 - (n_namesz & 3)) & 3;
1171260a9803SDavid Howells 
1172260a9803SDavid Howells 	reqsz = (4 * 4) +
1173260a9803SDavid Howells 		4 + o_namesz + o_padsz +
1174260a9803SDavid Howells 		(3 * 4) +
1175260a9803SDavid Howells 		4 + n_namesz + n_padsz;
1176260a9803SDavid Howells 
1177f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
1178260a9803SDavid Howells 	if (!call)
1179260a9803SDavid Howells 		return -ENOMEM;
1180260a9803SDavid Howells 
1181d2ddc776SDavid Howells 	call->key = fc->key;
118297e3043aSDavid Howells 	call->reply[0] = orig_dvnode;
118397e3043aSDavid Howells 	call->reply[1] = new_dvnode;
118463a4681fSDavid Howells 	call->expected_version = current_orig_data_version + 1;
118563a4681fSDavid Howells 	call->expected_version_2 = current_new_data_version + 1;
1186260a9803SDavid Howells 
1187260a9803SDavid Howells 	/* marshall the parameters */
1188260a9803SDavid Howells 	bp = call->request;
1189260a9803SDavid Howells 	*bp++ = htonl(FSRENAME);
1190260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vid);
1191260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vnode);
1192260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.unique);
1193260a9803SDavid Howells 	*bp++ = htonl(o_namesz);
1194260a9803SDavid Howells 	memcpy(bp, orig_name, o_namesz);
1195260a9803SDavid Howells 	bp = (void *) bp + o_namesz;
1196260a9803SDavid Howells 	if (o_padsz > 0) {
1197260a9803SDavid Howells 		memset(bp, 0, o_padsz);
1198260a9803SDavid Howells 		bp = (void *) bp + o_padsz;
1199260a9803SDavid Howells 	}
1200260a9803SDavid Howells 
1201260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vid);
1202260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vnode);
1203260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.unique);
1204260a9803SDavid Howells 	*bp++ = htonl(n_namesz);
1205260a9803SDavid Howells 	memcpy(bp, new_name, n_namesz);
1206260a9803SDavid Howells 	bp = (void *) bp + n_namesz;
1207260a9803SDavid Howells 	if (n_padsz > 0) {
1208260a9803SDavid Howells 		memset(bp, 0, n_padsz);
1209260a9803SDavid Howells 		bp = (void *) bp + n_padsz;
1210260a9803SDavid Howells 	}
1211260a9803SDavid Howells 
1212d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1213025db80cSDavid Howells 	trace_afs_make_fs_call(call, &orig_dvnode->fid);
1214d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1215260a9803SDavid Howells }
121631143d5dSDavid Howells 
121731143d5dSDavid Howells /*
121831143d5dSDavid Howells  * deliver reply data to an FS.StoreData
121931143d5dSDavid Howells  */
1220d001648eSDavid Howells static int afs_deliver_fs_store_data(struct afs_call *call)
122131143d5dSDavid Howells {
122297e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
122331143d5dSDavid Howells 	const __be32 *bp;
1224372ee163SDavid Howells 	int ret;
122531143d5dSDavid Howells 
1226d001648eSDavid Howells 	_enter("");
122731143d5dSDavid Howells 
1228d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1229372ee163SDavid Howells 	if (ret < 0)
1230372ee163SDavid Howells 		return ret;
123131143d5dSDavid Howells 
123231143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
123331143d5dSDavid Howells 	bp = call->buffer;
1234c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, &vnode->status, vnode,
1235dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
12365f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
123797e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
123831143d5dSDavid Howells 
123931143d5dSDavid Howells 	afs_pages_written_back(vnode, call);
124031143d5dSDavid Howells 
124131143d5dSDavid Howells 	_leave(" = 0 [done]");
124231143d5dSDavid Howells 	return 0;
124331143d5dSDavid Howells }
124431143d5dSDavid Howells 
124531143d5dSDavid Howells /*
124631143d5dSDavid Howells  * FS.StoreData operation type
124731143d5dSDavid Howells  */
124831143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData = {
124931143d5dSDavid Howells 	.name		= "FS.StoreData",
1250025db80cSDavid Howells 	.op		= afs_FS_StoreData,
125131143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_data,
125231143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
125331143d5dSDavid Howells };
125431143d5dSDavid Howells 
1255b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64 = {
1256b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1257025db80cSDavid Howells 	.op		= afs_FS_StoreData64,
1258b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_data,
1259b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1260b9b1f8d5SDavid Howells };
1261b9b1f8d5SDavid Howells 
1262b9b1f8d5SDavid Howells /*
1263b9b1f8d5SDavid Howells  * store a set of pages to a very large file
1264b9b1f8d5SDavid Howells  */
12658b2a464cSDavid Howells static int afs_fs_store_data64(struct afs_fs_cursor *fc,
12664343d008SDavid Howells 			       struct address_space *mapping,
1267b9b1f8d5SDavid Howells 			       pgoff_t first, pgoff_t last,
1268b9b1f8d5SDavid Howells 			       unsigned offset, unsigned to,
1269d2ddc776SDavid Howells 			       loff_t size, loff_t pos, loff_t i_size)
1270b9b1f8d5SDavid Howells {
12714343d008SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1272b9b1f8d5SDavid Howells 	struct afs_call *call;
1273f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1274b9b1f8d5SDavid Howells 	__be32 *bp;
1275b9b1f8d5SDavid Howells 
1276b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
12774343d008SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
1278b9b1f8d5SDavid Howells 
1279f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData64,
1280b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1281b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1282b9b1f8d5SDavid Howells 	if (!call)
1283b9b1f8d5SDavid Howells 		return -ENOMEM;
1284b9b1f8d5SDavid Howells 
12854343d008SDavid Howells 	call->key = fc->key;
12864343d008SDavid Howells 	call->mapping = mapping;
128797e3043aSDavid Howells 	call->reply[0] = vnode;
1288b9b1f8d5SDavid Howells 	call->first = first;
1289b9b1f8d5SDavid Howells 	call->last = last;
1290b9b1f8d5SDavid Howells 	call->first_offset = offset;
1291b9b1f8d5SDavid Howells 	call->last_to = to;
1292b9b1f8d5SDavid Howells 	call->send_pages = true;
12930c3a5ac2SDavid Howells 	call->expected_version = vnode->status.data_version + 1;
1294b9b1f8d5SDavid Howells 
1295b9b1f8d5SDavid Howells 	/* marshall the parameters */
1296b9b1f8d5SDavid Howells 	bp = call->request;
1297b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1298b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1299b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1300b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1301b9b1f8d5SDavid Howells 
1302ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MTIME); /* mask */
1303ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
1304b9b1f8d5SDavid Howells 	*bp++ = 0; /* owner */
1305b9b1f8d5SDavid Howells 	*bp++ = 0; /* group */
1306b9b1f8d5SDavid Howells 	*bp++ = 0; /* unix mode */
1307b9b1f8d5SDavid Howells 	*bp++ = 0; /* segment size */
1308b9b1f8d5SDavid Howells 
1309b9b1f8d5SDavid Howells 	*bp++ = htonl(pos >> 32);
1310b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) pos);
1311b9b1f8d5SDavid Howells 	*bp++ = htonl(size >> 32);
1312b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) size);
1313b9b1f8d5SDavid Howells 	*bp++ = htonl(i_size >> 32);
1314b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) i_size);
1315b9b1f8d5SDavid Howells 
1316025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1317d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1318b9b1f8d5SDavid Howells }
1319b9b1f8d5SDavid Howells 
132031143d5dSDavid Howells /*
132131143d5dSDavid Howells  * store a set of pages
132231143d5dSDavid Howells  */
13234343d008SDavid Howells int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
132431143d5dSDavid Howells 		      pgoff_t first, pgoff_t last,
1325d2ddc776SDavid Howells 		      unsigned offset, unsigned to)
132631143d5dSDavid Howells {
13274343d008SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
132831143d5dSDavid Howells 	struct afs_call *call;
1329f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
133031143d5dSDavid Howells 	loff_t size, pos, i_size;
133131143d5dSDavid Howells 	__be32 *bp;
133231143d5dSDavid Howells 
133331143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
13344343d008SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
133531143d5dSDavid Howells 
1336146a1192SDavid Howells 	size = (loff_t)to - (loff_t)offset;
133731143d5dSDavid Howells 	if (first != last)
133831143d5dSDavid Howells 		size += (loff_t)(last - first) << PAGE_SHIFT;
133931143d5dSDavid Howells 	pos = (loff_t)first << PAGE_SHIFT;
134031143d5dSDavid Howells 	pos += offset;
134131143d5dSDavid Howells 
134231143d5dSDavid Howells 	i_size = i_size_read(&vnode->vfs_inode);
134331143d5dSDavid Howells 	if (pos + size > i_size)
134431143d5dSDavid Howells 		i_size = size + pos;
134531143d5dSDavid Howells 
134631143d5dSDavid Howells 	_debug("size %llx, at %llx, i_size %llx",
134731143d5dSDavid Howells 	       (unsigned long long) size, (unsigned long long) pos,
134831143d5dSDavid Howells 	       (unsigned long long) i_size);
134931143d5dSDavid Howells 
1350b9b1f8d5SDavid Howells 	if (pos >> 32 || i_size >> 32 || size >> 32 || (pos + size) >> 32)
13514343d008SDavid Howells 		return afs_fs_store_data64(fc, mapping, first, last, offset, to,
1352d2ddc776SDavid Howells 					   size, pos, i_size);
135331143d5dSDavid Howells 
1354f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData,
135531143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
135631143d5dSDavid Howells 				   (21 + 6) * 4);
135731143d5dSDavid Howells 	if (!call)
135831143d5dSDavid Howells 		return -ENOMEM;
135931143d5dSDavid Howells 
13604343d008SDavid Howells 	call->key = fc->key;
13614343d008SDavid Howells 	call->mapping = mapping;
136297e3043aSDavid Howells 	call->reply[0] = vnode;
136331143d5dSDavid Howells 	call->first = first;
136431143d5dSDavid Howells 	call->last = last;
136531143d5dSDavid Howells 	call->first_offset = offset;
136631143d5dSDavid Howells 	call->last_to = to;
136731143d5dSDavid Howells 	call->send_pages = true;
13680c3a5ac2SDavid Howells 	call->expected_version = vnode->status.data_version + 1;
136931143d5dSDavid Howells 
137031143d5dSDavid Howells 	/* marshall the parameters */
137131143d5dSDavid Howells 	bp = call->request;
137231143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
137331143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
137431143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
137531143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
137631143d5dSDavid Howells 
1377ab94f5d0SMarc Dionne 	*bp++ = htonl(AFS_SET_MTIME); /* mask */
1378ab94f5d0SMarc Dionne 	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
137931143d5dSDavid Howells 	*bp++ = 0; /* owner */
138031143d5dSDavid Howells 	*bp++ = 0; /* group */
138131143d5dSDavid Howells 	*bp++ = 0; /* unix mode */
138231143d5dSDavid Howells 	*bp++ = 0; /* segment size */
138331143d5dSDavid Howells 
138431143d5dSDavid Howells 	*bp++ = htonl(pos);
138531143d5dSDavid Howells 	*bp++ = htonl(size);
138631143d5dSDavid Howells 	*bp++ = htonl(i_size);
138731143d5dSDavid Howells 
1388d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1389025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1390d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
139131143d5dSDavid Howells }
139231143d5dSDavid Howells 
139331143d5dSDavid Howells /*
139431143d5dSDavid Howells  * deliver reply data to an FS.StoreStatus
139531143d5dSDavid Howells  */
1396d001648eSDavid Howells static int afs_deliver_fs_store_status(struct afs_call *call)
139731143d5dSDavid Howells {
139897e3043aSDavid Howells 	struct afs_vnode *vnode = call->reply[0];
139931143d5dSDavid Howells 	const __be32 *bp;
1400372ee163SDavid Howells 	int ret;
140131143d5dSDavid Howells 
1402d001648eSDavid Howells 	_enter("");
140331143d5dSDavid Howells 
1404d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1405372ee163SDavid Howells 	if (ret < 0)
1406372ee163SDavid Howells 		return ret;
140731143d5dSDavid Howells 
140831143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
140931143d5dSDavid Howells 	bp = call->buffer;
1410c875c76aSDavid Howells 	if (afs_decode_status(call, &bp, &vnode->status, vnode,
1411dd9fbcb8SDavid Howells 			      &call->expected_version, NULL) < 0)
14125f702c8eSDavid Howells 		return afs_protocol_error(call, -EBADMSG);
141397e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
141431143d5dSDavid Howells 
141531143d5dSDavid Howells 	_leave(" = 0 [done]");
141631143d5dSDavid Howells 	return 0;
141731143d5dSDavid Howells }
141831143d5dSDavid Howells 
141931143d5dSDavid Howells /*
142031143d5dSDavid Howells  * FS.StoreStatus operation type
142131143d5dSDavid Howells  */
142231143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreStatus = {
142331143d5dSDavid Howells 	.name		= "FS.StoreStatus",
1424025db80cSDavid Howells 	.op		= afs_FS_StoreStatus,
142531143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
142631143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
142731143d5dSDavid Howells };
142831143d5dSDavid Howells 
142931143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData_as_Status = {
143031143d5dSDavid Howells 	.name		= "FS.StoreData",
1431025db80cSDavid Howells 	.op		= afs_FS_StoreData,
143231143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
143331143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
143431143d5dSDavid Howells };
143531143d5dSDavid Howells 
1436b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64_as_Status = {
1437b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1438025db80cSDavid Howells 	.op		= afs_FS_StoreData64,
1439b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_status,
1440b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1441b9b1f8d5SDavid Howells };
1442b9b1f8d5SDavid Howells 
1443b9b1f8d5SDavid Howells /*
1444b9b1f8d5SDavid Howells  * set the attributes on a very large file, using FS.StoreData rather than
1445b9b1f8d5SDavid Howells  * FS.StoreStatus so as to alter the file size also
1446b9b1f8d5SDavid Howells  */
1447d2ddc776SDavid Howells static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr)
1448b9b1f8d5SDavid Howells {
1449d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1450b9b1f8d5SDavid Howells 	struct afs_call *call;
1451f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1452b9b1f8d5SDavid Howells 	__be32 *bp;
1453b9b1f8d5SDavid Howells 
1454b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
1455d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
1456b9b1f8d5SDavid Howells 
1457b9b1f8d5SDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1458b9b1f8d5SDavid Howells 
1459f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData64_as_Status,
1460b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1461b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1462b9b1f8d5SDavid Howells 	if (!call)
1463b9b1f8d5SDavid Howells 		return -ENOMEM;
1464b9b1f8d5SDavid Howells 
1465d2ddc776SDavid Howells 	call->key = fc->key;
146697e3043aSDavid Howells 	call->reply[0] = vnode;
14670c3a5ac2SDavid Howells 	call->expected_version = vnode->status.data_version + 1;
1468b9b1f8d5SDavid Howells 
1469b9b1f8d5SDavid Howells 	/* marshall the parameters */
1470b9b1f8d5SDavid Howells 	bp = call->request;
1471b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1472b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1473b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1474b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1475b9b1f8d5SDavid Howells 
1476b9b1f8d5SDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
1477b9b1f8d5SDavid Howells 
1478b9b1f8d5SDavid Howells 	*bp++ = 0;				/* position of start of write */
1479b9b1f8d5SDavid Howells 	*bp++ = 0;
1480b9b1f8d5SDavid Howells 	*bp++ = 0;				/* size of write */
1481b9b1f8d5SDavid Howells 	*bp++ = 0;
1482b9b1f8d5SDavid Howells 	*bp++ = htonl(attr->ia_size >> 32);	/* new file length */
1483b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) attr->ia_size);
1484b9b1f8d5SDavid Howells 
1485d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1486025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1487d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1488b9b1f8d5SDavid Howells }
1489b9b1f8d5SDavid Howells 
149031143d5dSDavid Howells /*
149131143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus
149231143d5dSDavid Howells  * so as to alter the file size also
149331143d5dSDavid Howells  */
1494d2ddc776SDavid Howells static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
149531143d5dSDavid Howells {
1496d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
149731143d5dSDavid Howells 	struct afs_call *call;
1498f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
149931143d5dSDavid Howells 	__be32 *bp;
150031143d5dSDavid Howells 
150131143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
1502d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
150331143d5dSDavid Howells 
150431143d5dSDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1505b9b1f8d5SDavid Howells 	if (attr->ia_size >> 32)
1506d2ddc776SDavid Howells 		return afs_fs_setattr_size64(fc, attr);
150731143d5dSDavid Howells 
1508f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreData_as_Status,
150931143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
151031143d5dSDavid Howells 				   (21 + 6) * 4);
151131143d5dSDavid Howells 	if (!call)
151231143d5dSDavid Howells 		return -ENOMEM;
151331143d5dSDavid Howells 
1514d2ddc776SDavid Howells 	call->key = fc->key;
151597e3043aSDavid Howells 	call->reply[0] = vnode;
15160c3a5ac2SDavid Howells 	call->expected_version = vnode->status.data_version + 1;
151731143d5dSDavid Howells 
151831143d5dSDavid Howells 	/* marshall the parameters */
151931143d5dSDavid Howells 	bp = call->request;
152031143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
152131143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
152231143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
152331143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
152431143d5dSDavid Howells 
152531143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
152631143d5dSDavid Howells 
152731143d5dSDavid Howells 	*bp++ = 0;				/* position of start of write */
152831143d5dSDavid Howells 	*bp++ = 0;				/* size of write */
152931143d5dSDavid Howells 	*bp++ = htonl(attr->ia_size);		/* new file length */
153031143d5dSDavid Howells 
1531d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1532025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1533d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
153431143d5dSDavid Howells }
153531143d5dSDavid Howells 
153631143d5dSDavid Howells /*
153731143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData if there's a change in file
153831143d5dSDavid Howells  * size, and FS.StoreStatus otherwise
153931143d5dSDavid Howells  */
1540d2ddc776SDavid Howells int afs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
154131143d5dSDavid Howells {
1542d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
154331143d5dSDavid Howells 	struct afs_call *call;
1544f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
154531143d5dSDavid Howells 	__be32 *bp;
154631143d5dSDavid Howells 
154731143d5dSDavid Howells 	if (attr->ia_valid & ATTR_SIZE)
1548d2ddc776SDavid Howells 		return afs_fs_setattr_size(fc, attr);
154931143d5dSDavid Howells 
155031143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
1551d2ddc776SDavid Howells 	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
155231143d5dSDavid Howells 
1553f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSStoreStatus,
155431143d5dSDavid Howells 				   (4 + 6) * 4,
155531143d5dSDavid Howells 				   (21 + 6) * 4);
155631143d5dSDavid Howells 	if (!call)
155731143d5dSDavid Howells 		return -ENOMEM;
155831143d5dSDavid Howells 
1559d2ddc776SDavid Howells 	call->key = fc->key;
156097e3043aSDavid Howells 	call->reply[0] = vnode;
15610c3a5ac2SDavid Howells 	call->expected_version = vnode->status.data_version;
156231143d5dSDavid Howells 
156331143d5dSDavid Howells 	/* marshall the parameters */
156431143d5dSDavid Howells 	bp = call->request;
156531143d5dSDavid Howells 	*bp++ = htonl(FSSTORESTATUS);
156631143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
156731143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
156831143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
156931143d5dSDavid Howells 
157031143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
157131143d5dSDavid Howells 
1572d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1573025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1574d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
157531143d5dSDavid Howells }
157645222b9eSDavid Howells 
157745222b9eSDavid Howells /*
157845222b9eSDavid Howells  * deliver reply data to an FS.GetVolumeStatus
157945222b9eSDavid Howells  */
1580d001648eSDavid Howells static int afs_deliver_fs_get_volume_status(struct afs_call *call)
158145222b9eSDavid Howells {
158245222b9eSDavid Howells 	const __be32 *bp;
158345222b9eSDavid Howells 	char *p;
158445222b9eSDavid Howells 	int ret;
158545222b9eSDavid Howells 
1586d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
158745222b9eSDavid Howells 
158845222b9eSDavid Howells 	switch (call->unmarshall) {
158945222b9eSDavid Howells 	case 0:
159045222b9eSDavid Howells 		call->offset = 0;
159145222b9eSDavid Howells 		call->unmarshall++;
159245222b9eSDavid Howells 
159345222b9eSDavid Howells 		/* extract the returned status record */
159445222b9eSDavid Howells 	case 1:
159545222b9eSDavid Howells 		_debug("extract status");
1596d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1597d001648eSDavid Howells 				       12 * 4, true);
1598372ee163SDavid Howells 		if (ret < 0)
1599372ee163SDavid Howells 			return ret;
160045222b9eSDavid Howells 
160145222b9eSDavid Howells 		bp = call->buffer;
160297e3043aSDavid Howells 		xdr_decode_AFSFetchVolumeStatus(&bp, call->reply[1]);
160345222b9eSDavid Howells 		call->offset = 0;
160445222b9eSDavid Howells 		call->unmarshall++;
160545222b9eSDavid Howells 
160645222b9eSDavid Howells 		/* extract the volume name length */
160745222b9eSDavid Howells 	case 2:
1608d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1609372ee163SDavid Howells 		if (ret < 0)
1610372ee163SDavid Howells 			return ret;
161145222b9eSDavid Howells 
161245222b9eSDavid Howells 		call->count = ntohl(call->tmp);
161345222b9eSDavid Howells 		_debug("volname length: %u", call->count);
161445222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
16155f702c8eSDavid Howells 			return afs_protocol_error(call, -EBADMSG);
161645222b9eSDavid Howells 		call->offset = 0;
161745222b9eSDavid Howells 		call->unmarshall++;
161845222b9eSDavid Howells 
161945222b9eSDavid Howells 		/* extract the volume name */
162045222b9eSDavid Howells 	case 3:
162145222b9eSDavid Howells 		_debug("extract volname");
162245222b9eSDavid Howells 		if (call->count > 0) {
162397e3043aSDavid Howells 			ret = afs_extract_data(call, call->reply[2],
1624d001648eSDavid Howells 					       call->count, true);
1625372ee163SDavid Howells 			if (ret < 0)
1626372ee163SDavid Howells 				return ret;
162745222b9eSDavid Howells 		}
162845222b9eSDavid Howells 
162997e3043aSDavid Howells 		p = call->reply[2];
163045222b9eSDavid Howells 		p[call->count] = 0;
163145222b9eSDavid Howells 		_debug("volname '%s'", p);
163245222b9eSDavid Howells 
163345222b9eSDavid Howells 		call->offset = 0;
163445222b9eSDavid Howells 		call->unmarshall++;
163545222b9eSDavid Howells 
163645222b9eSDavid Howells 		/* extract the volume name padding */
163745222b9eSDavid Howells 		if ((call->count & 3) == 0) {
163845222b9eSDavid Howells 			call->unmarshall++;
163945222b9eSDavid Howells 			goto no_volname_padding;
164045222b9eSDavid Howells 		}
164145222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
164245222b9eSDavid Howells 
164345222b9eSDavid Howells 	case 4:
1644d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1645d001648eSDavid Howells 				       call->count, true);
1646372ee163SDavid Howells 		if (ret < 0)
1647372ee163SDavid Howells 			return ret;
164845222b9eSDavid Howells 
164945222b9eSDavid Howells 		call->offset = 0;
165045222b9eSDavid Howells 		call->unmarshall++;
165145222b9eSDavid Howells 	no_volname_padding:
165245222b9eSDavid Howells 
165345222b9eSDavid Howells 		/* extract the offline message length */
165445222b9eSDavid Howells 	case 5:
1655d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1656372ee163SDavid Howells 		if (ret < 0)
1657372ee163SDavid Howells 			return ret;
165845222b9eSDavid Howells 
165945222b9eSDavid Howells 		call->count = ntohl(call->tmp);
166045222b9eSDavid Howells 		_debug("offline msg length: %u", call->count);
166145222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
16625f702c8eSDavid Howells 			return afs_protocol_error(call, -EBADMSG);
166345222b9eSDavid Howells 		call->offset = 0;
166445222b9eSDavid Howells 		call->unmarshall++;
166545222b9eSDavid Howells 
166645222b9eSDavid Howells 		/* extract the offline message */
166745222b9eSDavid Howells 	case 6:
166845222b9eSDavid Howells 		_debug("extract offline");
166945222b9eSDavid Howells 		if (call->count > 0) {
167097e3043aSDavid Howells 			ret = afs_extract_data(call, call->reply[2],
1671d001648eSDavid Howells 					       call->count, true);
1672372ee163SDavid Howells 			if (ret < 0)
1673372ee163SDavid Howells 				return ret;
167445222b9eSDavid Howells 		}
167545222b9eSDavid Howells 
167697e3043aSDavid Howells 		p = call->reply[2];
167745222b9eSDavid Howells 		p[call->count] = 0;
167845222b9eSDavid Howells 		_debug("offline '%s'", p);
167945222b9eSDavid Howells 
168045222b9eSDavid Howells 		call->offset = 0;
168145222b9eSDavid Howells 		call->unmarshall++;
168245222b9eSDavid Howells 
168345222b9eSDavid Howells 		/* extract the offline message padding */
168445222b9eSDavid Howells 		if ((call->count & 3) == 0) {
168545222b9eSDavid Howells 			call->unmarshall++;
168645222b9eSDavid Howells 			goto no_offline_padding;
168745222b9eSDavid Howells 		}
168845222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
168945222b9eSDavid Howells 
169045222b9eSDavid Howells 	case 7:
1691d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1692d001648eSDavid Howells 				       call->count, true);
1693372ee163SDavid Howells 		if (ret < 0)
1694372ee163SDavid Howells 			return ret;
169545222b9eSDavid Howells 
169645222b9eSDavid Howells 		call->offset = 0;
169745222b9eSDavid Howells 		call->unmarshall++;
169845222b9eSDavid Howells 	no_offline_padding:
169945222b9eSDavid Howells 
170045222b9eSDavid Howells 		/* extract the message of the day length */
170145222b9eSDavid Howells 	case 8:
1702d001648eSDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
1703372ee163SDavid Howells 		if (ret < 0)
1704372ee163SDavid Howells 			return ret;
170545222b9eSDavid Howells 
170645222b9eSDavid Howells 		call->count = ntohl(call->tmp);
170745222b9eSDavid Howells 		_debug("motd length: %u", call->count);
170845222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
17095f702c8eSDavid Howells 			return afs_protocol_error(call, -EBADMSG);
171045222b9eSDavid Howells 		call->offset = 0;
171145222b9eSDavid Howells 		call->unmarshall++;
171245222b9eSDavid Howells 
171345222b9eSDavid Howells 		/* extract the message of the day */
171445222b9eSDavid Howells 	case 9:
171545222b9eSDavid Howells 		_debug("extract motd");
171645222b9eSDavid Howells 		if (call->count > 0) {
171797e3043aSDavid Howells 			ret = afs_extract_data(call, call->reply[2],
1718d001648eSDavid Howells 					       call->count, true);
1719372ee163SDavid Howells 			if (ret < 0)
1720372ee163SDavid Howells 				return ret;
172145222b9eSDavid Howells 		}
172245222b9eSDavid Howells 
172397e3043aSDavid Howells 		p = call->reply[2];
172445222b9eSDavid Howells 		p[call->count] = 0;
172545222b9eSDavid Howells 		_debug("motd '%s'", p);
172645222b9eSDavid Howells 
172745222b9eSDavid Howells 		call->offset = 0;
172845222b9eSDavid Howells 		call->unmarshall++;
172945222b9eSDavid Howells 
173045222b9eSDavid Howells 		/* extract the message of the day padding */
1731d001648eSDavid Howells 		call->count = (4 - (call->count & 3)) & 3;
173245222b9eSDavid Howells 
173345222b9eSDavid Howells 	case 10:
1734d001648eSDavid Howells 		ret = afs_extract_data(call, call->buffer,
1735d001648eSDavid Howells 				       call->count, false);
1736372ee163SDavid Howells 		if (ret < 0)
1737372ee163SDavid Howells 			return ret;
173845222b9eSDavid Howells 
173945222b9eSDavid Howells 		call->offset = 0;
174045222b9eSDavid Howells 		call->unmarshall++;
174145222b9eSDavid Howells 	case 11:
174245222b9eSDavid Howells 		break;
174345222b9eSDavid Howells 	}
174445222b9eSDavid Howells 
174545222b9eSDavid Howells 	_leave(" = 0 [done]");
174645222b9eSDavid Howells 	return 0;
174745222b9eSDavid Howells }
174845222b9eSDavid Howells 
174945222b9eSDavid Howells /*
175045222b9eSDavid Howells  * destroy an FS.GetVolumeStatus call
175145222b9eSDavid Howells  */
175245222b9eSDavid Howells static void afs_get_volume_status_call_destructor(struct afs_call *call)
175345222b9eSDavid Howells {
175497e3043aSDavid Howells 	kfree(call->reply[2]);
175597e3043aSDavid Howells 	call->reply[2] = NULL;
175645222b9eSDavid Howells 	afs_flat_call_destructor(call);
175745222b9eSDavid Howells }
175845222b9eSDavid Howells 
175945222b9eSDavid Howells /*
176045222b9eSDavid Howells  * FS.GetVolumeStatus operation type
176145222b9eSDavid Howells  */
176245222b9eSDavid Howells static const struct afs_call_type afs_RXFSGetVolumeStatus = {
176345222b9eSDavid Howells 	.name		= "FS.GetVolumeStatus",
1764025db80cSDavid Howells 	.op		= afs_FS_GetVolumeStatus,
176545222b9eSDavid Howells 	.deliver	= afs_deliver_fs_get_volume_status,
176645222b9eSDavid Howells 	.destructor	= afs_get_volume_status_call_destructor,
176745222b9eSDavid Howells };
176845222b9eSDavid Howells 
176945222b9eSDavid Howells /*
177045222b9eSDavid Howells  * fetch the status of a volume
177145222b9eSDavid Howells  */
17728b2a464cSDavid Howells int afs_fs_get_volume_status(struct afs_fs_cursor *fc,
1773d2ddc776SDavid Howells 			     struct afs_volume_status *vs)
177445222b9eSDavid Howells {
1775d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
177645222b9eSDavid Howells 	struct afs_call *call;
1777f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
177845222b9eSDavid Howells 	__be32 *bp;
177945222b9eSDavid Howells 	void *tmpbuf;
178045222b9eSDavid Howells 
178145222b9eSDavid Howells 	_enter("");
178245222b9eSDavid Howells 
178345222b9eSDavid Howells 	tmpbuf = kmalloc(AFSOPAQUEMAX, GFP_KERNEL);
178445222b9eSDavid Howells 	if (!tmpbuf)
178545222b9eSDavid Howells 		return -ENOMEM;
178645222b9eSDavid Howells 
1787f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGetVolumeStatus, 2 * 4, 12 * 4);
178845222b9eSDavid Howells 	if (!call) {
178945222b9eSDavid Howells 		kfree(tmpbuf);
179045222b9eSDavid Howells 		return -ENOMEM;
179145222b9eSDavid Howells 	}
179245222b9eSDavid Howells 
1793d2ddc776SDavid Howells 	call->key = fc->key;
179497e3043aSDavid Howells 	call->reply[0] = vnode;
179597e3043aSDavid Howells 	call->reply[1] = vs;
179697e3043aSDavid Howells 	call->reply[2] = tmpbuf;
179745222b9eSDavid Howells 
179845222b9eSDavid Howells 	/* marshall the parameters */
179945222b9eSDavid Howells 	bp = call->request;
180045222b9eSDavid Howells 	bp[0] = htonl(FSGETVOLUMESTATUS);
180145222b9eSDavid Howells 	bp[1] = htonl(vnode->fid.vid);
180245222b9eSDavid Howells 
1803d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1804025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1805d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
180645222b9eSDavid Howells }
1807e8d6c554SDavid Howells 
1808e8d6c554SDavid Howells /*
1809e8d6c554SDavid Howells  * deliver reply data to an FS.SetLock, FS.ExtendLock or FS.ReleaseLock
1810e8d6c554SDavid Howells  */
1811d001648eSDavid Howells static int afs_deliver_fs_xxxx_lock(struct afs_call *call)
1812e8d6c554SDavid Howells {
1813e8d6c554SDavid Howells 	const __be32 *bp;
1814372ee163SDavid Howells 	int ret;
1815e8d6c554SDavid Howells 
1816d001648eSDavid Howells 	_enter("{%u}", call->unmarshall);
1817e8d6c554SDavid Howells 
1818d001648eSDavid Howells 	ret = afs_transfer_reply(call);
1819372ee163SDavid Howells 	if (ret < 0)
1820372ee163SDavid Howells 		return ret;
1821e8d6c554SDavid Howells 
1822e8d6c554SDavid Howells 	/* unmarshall the reply once we've received all of it */
1823e8d6c554SDavid Howells 	bp = call->buffer;
182497e3043aSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
1825e8d6c554SDavid Howells 
1826e8d6c554SDavid Howells 	_leave(" = 0 [done]");
1827e8d6c554SDavid Howells 	return 0;
1828e8d6c554SDavid Howells }
1829e8d6c554SDavid Howells 
1830e8d6c554SDavid Howells /*
1831e8d6c554SDavid Howells  * FS.SetLock operation type
1832e8d6c554SDavid Howells  */
1833e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSSetLock = {
1834e8d6c554SDavid Howells 	.name		= "FS.SetLock",
1835025db80cSDavid Howells 	.op		= afs_FS_SetLock,
1836e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1837e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1838e8d6c554SDavid Howells };
1839e8d6c554SDavid Howells 
1840e8d6c554SDavid Howells /*
1841e8d6c554SDavid Howells  * FS.ExtendLock operation type
1842e8d6c554SDavid Howells  */
1843e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSExtendLock = {
1844e8d6c554SDavid Howells 	.name		= "FS.ExtendLock",
1845025db80cSDavid Howells 	.op		= afs_FS_ExtendLock,
1846e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1847e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1848e8d6c554SDavid Howells };
1849e8d6c554SDavid Howells 
1850e8d6c554SDavid Howells /*
1851e8d6c554SDavid Howells  * FS.ReleaseLock operation type
1852e8d6c554SDavid Howells  */
1853e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSReleaseLock = {
1854e8d6c554SDavid Howells 	.name		= "FS.ReleaseLock",
1855025db80cSDavid Howells 	.op		= afs_FS_ReleaseLock,
1856e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1857e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1858e8d6c554SDavid Howells };
1859e8d6c554SDavid Howells 
1860e8d6c554SDavid Howells /*
1861d2ddc776SDavid Howells  * Set a lock on a file
1862e8d6c554SDavid Howells  */
1863d2ddc776SDavid Howells int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
1864e8d6c554SDavid Howells {
1865d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1866e8d6c554SDavid Howells 	struct afs_call *call;
1867f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1868e8d6c554SDavid Howells 	__be32 *bp;
1869e8d6c554SDavid Howells 
1870e8d6c554SDavid Howells 	_enter("");
1871e8d6c554SDavid Howells 
1872f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSSetLock, 5 * 4, 6 * 4);
1873e8d6c554SDavid Howells 	if (!call)
1874e8d6c554SDavid Howells 		return -ENOMEM;
1875e8d6c554SDavid Howells 
1876d2ddc776SDavid Howells 	call->key = fc->key;
187797e3043aSDavid Howells 	call->reply[0] = vnode;
1878e8d6c554SDavid Howells 
1879e8d6c554SDavid Howells 	/* marshall the parameters */
1880e8d6c554SDavid Howells 	bp = call->request;
1881e8d6c554SDavid Howells 	*bp++ = htonl(FSSETLOCK);
1882e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1883e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1884e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1885e8d6c554SDavid Howells 	*bp++ = htonl(type);
1886e8d6c554SDavid Howells 
1887d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1888025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1889d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1890e8d6c554SDavid Howells }
1891e8d6c554SDavid Howells 
1892e8d6c554SDavid Howells /*
1893e8d6c554SDavid Howells  * extend a lock on a file
1894e8d6c554SDavid Howells  */
1895d2ddc776SDavid Howells int afs_fs_extend_lock(struct afs_fs_cursor *fc)
1896e8d6c554SDavid Howells {
1897d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1898e8d6c554SDavid Howells 	struct afs_call *call;
1899f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1900e8d6c554SDavid Howells 	__be32 *bp;
1901e8d6c554SDavid Howells 
1902e8d6c554SDavid Howells 	_enter("");
1903e8d6c554SDavid Howells 
1904f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSExtendLock, 4 * 4, 6 * 4);
1905e8d6c554SDavid Howells 	if (!call)
1906e8d6c554SDavid Howells 		return -ENOMEM;
1907e8d6c554SDavid Howells 
1908d2ddc776SDavid Howells 	call->key = fc->key;
190997e3043aSDavid Howells 	call->reply[0] = vnode;
1910e8d6c554SDavid Howells 
1911e8d6c554SDavid Howells 	/* marshall the parameters */
1912e8d6c554SDavid Howells 	bp = call->request;
1913e8d6c554SDavid Howells 	*bp++ = htonl(FSEXTENDLOCK);
1914e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1915e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1916e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1917e8d6c554SDavid Howells 
1918d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1919025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1920d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1921e8d6c554SDavid Howells }
1922e8d6c554SDavid Howells 
1923e8d6c554SDavid Howells /*
1924e8d6c554SDavid Howells  * release a lock on a file
1925e8d6c554SDavid Howells  */
1926d2ddc776SDavid Howells int afs_fs_release_lock(struct afs_fs_cursor *fc)
1927e8d6c554SDavid Howells {
1928d2ddc776SDavid Howells 	struct afs_vnode *vnode = fc->vnode;
1929e8d6c554SDavid Howells 	struct afs_call *call;
1930f044c884SDavid Howells 	struct afs_net *net = afs_v2net(vnode);
1931e8d6c554SDavid Howells 	__be32 *bp;
1932e8d6c554SDavid Howells 
1933e8d6c554SDavid Howells 	_enter("");
1934e8d6c554SDavid Howells 
1935f044c884SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSReleaseLock, 4 * 4, 6 * 4);
1936e8d6c554SDavid Howells 	if (!call)
1937e8d6c554SDavid Howells 		return -ENOMEM;
1938e8d6c554SDavid Howells 
1939d2ddc776SDavid Howells 	call->key = fc->key;
194097e3043aSDavid Howells 	call->reply[0] = vnode;
1941e8d6c554SDavid Howells 
1942e8d6c554SDavid Howells 	/* marshall the parameters */
1943e8d6c554SDavid Howells 	bp = call->request;
1944e8d6c554SDavid Howells 	*bp++ = htonl(FSRELEASELOCK);
1945e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1946e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1947e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1948e8d6c554SDavid Howells 
1949d2ddc776SDavid Howells 	afs_use_fs_server(call, fc->cbi);
1950025db80cSDavid Howells 	trace_afs_make_fs_call(call, &vnode->fid);
1951d2ddc776SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
1952c435ee34SDavid Howells }
1953c435ee34SDavid Howells 
1954c435ee34SDavid Howells /*
1955c435ee34SDavid Howells  * Deliver reply data to an FS.GiveUpAllCallBacks operation.
1956c435ee34SDavid Howells  */
1957c435ee34SDavid Howells static int afs_deliver_fs_give_up_all_callbacks(struct afs_call *call)
1958c435ee34SDavid Howells {
1959c435ee34SDavid Howells 	return afs_transfer_reply(call);
1960c435ee34SDavid Howells }
1961c435ee34SDavid Howells 
1962c435ee34SDavid Howells /*
1963c435ee34SDavid Howells  * FS.GiveUpAllCallBacks operation type
1964c435ee34SDavid Howells  */
1965c435ee34SDavid Howells static const struct afs_call_type afs_RXFSGiveUpAllCallBacks = {
1966c435ee34SDavid Howells 	.name		= "FS.GiveUpAllCallBacks",
1967025db80cSDavid Howells 	.op		= afs_FS_GiveUpAllCallBacks,
1968c435ee34SDavid Howells 	.deliver	= afs_deliver_fs_give_up_all_callbacks,
1969c435ee34SDavid Howells 	.destructor	= afs_flat_call_destructor,
1970c435ee34SDavid Howells };
1971c435ee34SDavid Howells 
1972c435ee34SDavid Howells /*
1973c435ee34SDavid Howells  * Flush all the callbacks we have on a server.
1974c435ee34SDavid Howells  */
1975d2ddc776SDavid Howells int afs_fs_give_up_all_callbacks(struct afs_net *net,
1976d2ddc776SDavid Howells 				 struct afs_server *server,
19778b2a464cSDavid Howells 				 struct afs_addr_cursor *ac,
1978d2ddc776SDavid Howells 				 struct key *key)
1979c435ee34SDavid Howells {
1980c435ee34SDavid Howells 	struct afs_call *call;
1981c435ee34SDavid Howells 	__be32 *bp;
1982c435ee34SDavid Howells 
1983c435ee34SDavid Howells 	_enter("");
1984c435ee34SDavid Howells 
1985d2ddc776SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGiveUpAllCallBacks, 1 * 4, 0);
1986c435ee34SDavid Howells 	if (!call)
1987c435ee34SDavid Howells 		return -ENOMEM;
1988c435ee34SDavid Howells 
1989c435ee34SDavid Howells 	call->key = key;
1990c435ee34SDavid Howells 
1991c435ee34SDavid Howells 	/* marshall the parameters */
1992c435ee34SDavid Howells 	bp = call->request;
1993c435ee34SDavid Howells 	*bp++ = htonl(FSGIVEUPALLCALLBACKS);
1994c435ee34SDavid Howells 
1995c435ee34SDavid Howells 	/* Can't take a ref on server */
1996d2ddc776SDavid Howells 	return afs_make_call(ac, call, GFP_NOFS, false);
1997d2ddc776SDavid Howells }
1998d2ddc776SDavid Howells 
1999d2ddc776SDavid Howells /*
2000d2ddc776SDavid Howells  * Deliver reply data to an FS.GetCapabilities operation.
2001d2ddc776SDavid Howells  */
2002d2ddc776SDavid Howells static int afs_deliver_fs_get_capabilities(struct afs_call *call)
2003d2ddc776SDavid Howells {
2004d2ddc776SDavid Howells 	u32 count;
2005d2ddc776SDavid Howells 	int ret;
2006d2ddc776SDavid Howells 
2007d2ddc776SDavid Howells 	_enter("{%u,%zu/%u}", call->unmarshall, call->offset, call->count);
2008d2ddc776SDavid Howells 
2009d2ddc776SDavid Howells again:
2010d2ddc776SDavid Howells 	switch (call->unmarshall) {
2011d2ddc776SDavid Howells 	case 0:
2012d2ddc776SDavid Howells 		call->offset = 0;
2013d2ddc776SDavid Howells 		call->unmarshall++;
2014d2ddc776SDavid Howells 
2015d2ddc776SDavid Howells 		/* Extract the capabilities word count */
2016d2ddc776SDavid Howells 	case 1:
2017d2ddc776SDavid Howells 		ret = afs_extract_data(call, &call->tmp,
2018d2ddc776SDavid Howells 				       1 * sizeof(__be32),
2019d2ddc776SDavid Howells 				       true);
2020d2ddc776SDavid Howells 		if (ret < 0)
2021d2ddc776SDavid Howells 			return ret;
2022d2ddc776SDavid Howells 
2023d2ddc776SDavid Howells 		count = ntohl(call->tmp);
2024d2ddc776SDavid Howells 
2025d2ddc776SDavid Howells 		call->count = count;
2026d2ddc776SDavid Howells 		call->count2 = count;
2027d2ddc776SDavid Howells 		call->offset = 0;
2028d2ddc776SDavid Howells 		call->unmarshall++;
2029d2ddc776SDavid Howells 
2030d2ddc776SDavid Howells 		/* Extract capabilities words */
2031d2ddc776SDavid Howells 	case 2:
2032d2ddc776SDavid Howells 		count = min(call->count, 16U);
2033d2ddc776SDavid Howells 		ret = afs_extract_data(call, call->buffer,
2034d2ddc776SDavid Howells 				       count * sizeof(__be32),
2035d2ddc776SDavid Howells 				       call->count > 16);
2036d2ddc776SDavid Howells 		if (ret < 0)
2037d2ddc776SDavid Howells 			return ret;
2038d2ddc776SDavid Howells 
2039d2ddc776SDavid Howells 		/* TODO: Examine capabilities */
2040d2ddc776SDavid Howells 
2041d2ddc776SDavid Howells 		call->count -= count;
2042d2ddc776SDavid Howells 		if (call->count > 0)
2043d2ddc776SDavid Howells 			goto again;
2044d2ddc776SDavid Howells 		call->offset = 0;
2045d2ddc776SDavid Howells 		call->unmarshall++;
2046d2ddc776SDavid Howells 		break;
2047d2ddc776SDavid Howells 	}
2048d2ddc776SDavid Howells 
2049d2ddc776SDavid Howells 	_leave(" = 0 [done]");
2050d2ddc776SDavid Howells 	return 0;
2051d2ddc776SDavid Howells }
2052d2ddc776SDavid Howells 
2053d2ddc776SDavid Howells /*
2054d2ddc776SDavid Howells  * FS.GetCapabilities operation type
2055d2ddc776SDavid Howells  */
2056d2ddc776SDavid Howells static const struct afs_call_type afs_RXFSGetCapabilities = {
2057d2ddc776SDavid Howells 	.name		= "FS.GetCapabilities",
2058025db80cSDavid Howells 	.op		= afs_FS_GetCapabilities,
2059d2ddc776SDavid Howells 	.deliver	= afs_deliver_fs_get_capabilities,
2060d2ddc776SDavid Howells 	.destructor	= afs_flat_call_destructor,
2061d2ddc776SDavid Howells };
2062d2ddc776SDavid Howells 
2063d2ddc776SDavid Howells /*
2064d2ddc776SDavid Howells  * Probe a fileserver for the capabilities that it supports.  This can
2065d2ddc776SDavid Howells  * return up to 196 words.
2066d2ddc776SDavid Howells  */
2067d2ddc776SDavid Howells int afs_fs_get_capabilities(struct afs_net *net,
2068d2ddc776SDavid Howells 			    struct afs_server *server,
2069d2ddc776SDavid Howells 			    struct afs_addr_cursor *ac,
2070d2ddc776SDavid Howells 			    struct key *key)
2071d2ddc776SDavid Howells {
2072d2ddc776SDavid Howells 	struct afs_call *call;
2073d2ddc776SDavid Howells 	__be32 *bp;
2074d2ddc776SDavid Howells 
2075d2ddc776SDavid Howells 	_enter("");
2076d2ddc776SDavid Howells 
2077d2ddc776SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSGetCapabilities, 1 * 4, 16 * 4);
2078d2ddc776SDavid Howells 	if (!call)
2079d2ddc776SDavid Howells 		return -ENOMEM;
2080d2ddc776SDavid Howells 
2081d2ddc776SDavid Howells 	call->key = key;
2082d2ddc776SDavid Howells 
2083d2ddc776SDavid Howells 	/* marshall the parameters */
2084d2ddc776SDavid Howells 	bp = call->request;
2085d2ddc776SDavid Howells 	*bp++ = htonl(FSGETCAPABILITIES);
2086d2ddc776SDavid Howells 
2087d2ddc776SDavid Howells 	/* Can't take a ref on server */
2088025db80cSDavid Howells 	trace_afs_make_fs_call(call, NULL);
2089d2ddc776SDavid Howells 	return afs_make_call(ac, call, GFP_NOFS, false);
2090e8d6c554SDavid Howells }
20915cf9dd55SDavid Howells 
20925cf9dd55SDavid Howells /*
20935cf9dd55SDavid Howells  * Deliver reply data to an FS.FetchStatus with no vnode.
20945cf9dd55SDavid Howells  */
20955cf9dd55SDavid Howells static int afs_deliver_fs_fetch_status(struct afs_call *call)
20965cf9dd55SDavid Howells {
20975cf9dd55SDavid Howells 	struct afs_file_status *status = call->reply[1];
20985cf9dd55SDavid Howells 	struct afs_callback *callback = call->reply[2];
20995cf9dd55SDavid Howells 	struct afs_volsync *volsync = call->reply[3];
21005cf9dd55SDavid Howells 	struct afs_vnode *vnode = call->reply[0];
21015cf9dd55SDavid Howells 	const __be32 *bp;
21025cf9dd55SDavid Howells 	int ret;
21035cf9dd55SDavid Howells 
21045cf9dd55SDavid Howells 	ret = afs_transfer_reply(call);
21055cf9dd55SDavid Howells 	if (ret < 0)
21065cf9dd55SDavid Howells 		return ret;
21075cf9dd55SDavid Howells 
21085cf9dd55SDavid Howells 	_enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode);
21095cf9dd55SDavid Howells 
21105cf9dd55SDavid Howells 	/* unmarshall the reply once we've received all of it */
21115cf9dd55SDavid Howells 	bp = call->buffer;
2112c875c76aSDavid Howells 	afs_decode_status(call, &bp, status, vnode,
21130c3a5ac2SDavid Howells 			  &call->expected_version, NULL);
21145cf9dd55SDavid Howells 	callback[call->count].version	= ntohl(bp[0]);
21155cf9dd55SDavid Howells 	callback[call->count].expiry	= ntohl(bp[1]);
21165cf9dd55SDavid Howells 	callback[call->count].type	= ntohl(bp[2]);
21175cf9dd55SDavid Howells 	if (vnode)
21185cf9dd55SDavid Howells 		xdr_decode_AFSCallBack(call, vnode, &bp);
21195cf9dd55SDavid Howells 	else
21205cf9dd55SDavid Howells 		bp += 3;
21215cf9dd55SDavid Howells 	if (volsync)
21225cf9dd55SDavid Howells 		xdr_decode_AFSVolSync(&bp, volsync);
21235cf9dd55SDavid Howells 
21245cf9dd55SDavid Howells 	_leave(" = 0 [done]");
21255cf9dd55SDavid Howells 	return 0;
21265cf9dd55SDavid Howells }
21275cf9dd55SDavid Howells 
21285cf9dd55SDavid Howells /*
21295cf9dd55SDavid Howells  * FS.FetchStatus operation type
21305cf9dd55SDavid Howells  */
21315cf9dd55SDavid Howells static const struct afs_call_type afs_RXFSFetchStatus = {
21325cf9dd55SDavid Howells 	.name		= "FS.FetchStatus",
21335cf9dd55SDavid Howells 	.op		= afs_FS_FetchStatus,
21345cf9dd55SDavid Howells 	.deliver	= afs_deliver_fs_fetch_status,
21355cf9dd55SDavid Howells 	.destructor	= afs_flat_call_destructor,
21365cf9dd55SDavid Howells };
21375cf9dd55SDavid Howells 
21385cf9dd55SDavid Howells /*
21395cf9dd55SDavid Howells  * Fetch the status information for a fid without needing a vnode handle.
21405cf9dd55SDavid Howells  */
21415cf9dd55SDavid Howells int afs_fs_fetch_status(struct afs_fs_cursor *fc,
21425cf9dd55SDavid Howells 			struct afs_net *net,
21435cf9dd55SDavid Howells 			struct afs_fid *fid,
21445cf9dd55SDavid Howells 			struct afs_file_status *status,
21455cf9dd55SDavid Howells 			struct afs_callback *callback,
21465cf9dd55SDavid Howells 			struct afs_volsync *volsync)
21475cf9dd55SDavid Howells {
21485cf9dd55SDavid Howells 	struct afs_call *call;
21495cf9dd55SDavid Howells 	__be32 *bp;
21505cf9dd55SDavid Howells 
21515cf9dd55SDavid Howells 	_enter(",%x,{%x:%u},,",
21525cf9dd55SDavid Howells 	       key_serial(fc->key), fid->vid, fid->vnode);
21535cf9dd55SDavid Howells 
21545cf9dd55SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
21555cf9dd55SDavid Howells 	if (!call) {
21565cf9dd55SDavid Howells 		fc->ac.error = -ENOMEM;
21575cf9dd55SDavid Howells 		return -ENOMEM;
21585cf9dd55SDavid Howells 	}
21595cf9dd55SDavid Howells 
21605cf9dd55SDavid Howells 	call->key = fc->key;
21615cf9dd55SDavid Howells 	call->reply[0] = NULL; /* vnode for fid[0] */
21625cf9dd55SDavid Howells 	call->reply[1] = status;
21635cf9dd55SDavid Howells 	call->reply[2] = callback;
21645cf9dd55SDavid Howells 	call->reply[3] = volsync;
21650c3a5ac2SDavid Howells 	call->expected_version = 1; /* vnode->status.data_version */
21665cf9dd55SDavid Howells 
21675cf9dd55SDavid Howells 	/* marshall the parameters */
21685cf9dd55SDavid Howells 	bp = call->request;
21695cf9dd55SDavid Howells 	bp[0] = htonl(FSFETCHSTATUS);
21705cf9dd55SDavid Howells 	bp[1] = htonl(fid->vid);
21715cf9dd55SDavid Howells 	bp[2] = htonl(fid->vnode);
21725cf9dd55SDavid Howells 	bp[3] = htonl(fid->unique);
21735cf9dd55SDavid Howells 
21745cf9dd55SDavid Howells 	call->cb_break = fc->cb_break;
21755cf9dd55SDavid Howells 	afs_use_fs_server(call, fc->cbi);
21765cf9dd55SDavid Howells 	trace_afs_make_fs_call(call, fid);
21775cf9dd55SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
21785cf9dd55SDavid Howells }
21795cf9dd55SDavid Howells 
21805cf9dd55SDavid Howells /*
21815cf9dd55SDavid Howells  * Deliver reply data to an FS.InlineBulkStatus call
21825cf9dd55SDavid Howells  */
21835cf9dd55SDavid Howells static int afs_deliver_fs_inline_bulk_status(struct afs_call *call)
21845cf9dd55SDavid Howells {
21855cf9dd55SDavid Howells 	struct afs_file_status *statuses;
21865cf9dd55SDavid Howells 	struct afs_callback *callbacks;
21875cf9dd55SDavid Howells 	struct afs_vnode *vnode = call->reply[0];
21885cf9dd55SDavid Howells 	const __be32 *bp;
21895cf9dd55SDavid Howells 	u32 tmp;
21905cf9dd55SDavid Howells 	int ret;
21915cf9dd55SDavid Howells 
21925cf9dd55SDavid Howells 	_enter("{%u}", call->unmarshall);
21935cf9dd55SDavid Howells 
21945cf9dd55SDavid Howells 	switch (call->unmarshall) {
21955cf9dd55SDavid Howells 	case 0:
21965cf9dd55SDavid Howells 		call->offset = 0;
21975cf9dd55SDavid Howells 		call->unmarshall++;
21985cf9dd55SDavid Howells 
21995cf9dd55SDavid Howells 		/* Extract the file status count and array in two steps */
22005cf9dd55SDavid Howells 	case 1:
22015cf9dd55SDavid Howells 		_debug("extract status count");
22025cf9dd55SDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
22035cf9dd55SDavid Howells 		if (ret < 0)
22045cf9dd55SDavid Howells 			return ret;
22055cf9dd55SDavid Howells 
22065cf9dd55SDavid Howells 		tmp = ntohl(call->tmp);
22075cf9dd55SDavid Howells 		_debug("status count: %u/%u", tmp, call->count2);
22085cf9dd55SDavid Howells 		if (tmp != call->count2)
22095f702c8eSDavid Howells 			return afs_protocol_error(call, -EBADMSG);
22105cf9dd55SDavid Howells 
22115cf9dd55SDavid Howells 		call->count = 0;
22125cf9dd55SDavid Howells 		call->unmarshall++;
22135cf9dd55SDavid Howells 	more_counts:
22145cf9dd55SDavid Howells 		call->offset = 0;
22155cf9dd55SDavid Howells 
22165cf9dd55SDavid Howells 	case 2:
22175cf9dd55SDavid Howells 		_debug("extract status array %u", call->count);
22185cf9dd55SDavid Howells 		ret = afs_extract_data(call, call->buffer, 21 * 4, true);
22195cf9dd55SDavid Howells 		if (ret < 0)
22205cf9dd55SDavid Howells 			return ret;
22215cf9dd55SDavid Howells 
22225cf9dd55SDavid Howells 		bp = call->buffer;
22235cf9dd55SDavid Howells 		statuses = call->reply[1];
2224c875c76aSDavid Howells 		if (afs_decode_status(call, &bp, &statuses[call->count],
22255cf9dd55SDavid Howells 				      call->count == 0 ? vnode : NULL,
2226dd9fbcb8SDavid Howells 				      NULL, NULL) < 0)
22275f702c8eSDavid Howells 			return afs_protocol_error(call, -EBADMSG);
22285cf9dd55SDavid Howells 
22295cf9dd55SDavid Howells 		call->count++;
22305cf9dd55SDavid Howells 		if (call->count < call->count2)
22315cf9dd55SDavid Howells 			goto more_counts;
22325cf9dd55SDavid Howells 
22335cf9dd55SDavid Howells 		call->count = 0;
22345cf9dd55SDavid Howells 		call->unmarshall++;
22355cf9dd55SDavid Howells 		call->offset = 0;
22365cf9dd55SDavid Howells 
22375cf9dd55SDavid Howells 		/* Extract the callback count and array in two steps */
22385cf9dd55SDavid Howells 	case 3:
22395cf9dd55SDavid Howells 		_debug("extract CB count");
22405cf9dd55SDavid Howells 		ret = afs_extract_data(call, &call->tmp, 4, true);
22415cf9dd55SDavid Howells 		if (ret < 0)
22425cf9dd55SDavid Howells 			return ret;
22435cf9dd55SDavid Howells 
22445cf9dd55SDavid Howells 		tmp = ntohl(call->tmp);
22455cf9dd55SDavid Howells 		_debug("CB count: %u", tmp);
22465cf9dd55SDavid Howells 		if (tmp != call->count2)
22475f702c8eSDavid Howells 			return afs_protocol_error(call, -EBADMSG);
22485cf9dd55SDavid Howells 		call->count = 0;
22495cf9dd55SDavid Howells 		call->unmarshall++;
22505cf9dd55SDavid Howells 	more_cbs:
22515cf9dd55SDavid Howells 		call->offset = 0;
22525cf9dd55SDavid Howells 
22535cf9dd55SDavid Howells 	case 4:
22545cf9dd55SDavid Howells 		_debug("extract CB array");
22555cf9dd55SDavid Howells 		ret = afs_extract_data(call, call->buffer, 3 * 4, true);
22565cf9dd55SDavid Howells 		if (ret < 0)
22575cf9dd55SDavid Howells 			return ret;
22585cf9dd55SDavid Howells 
22595cf9dd55SDavid Howells 		_debug("unmarshall CB array");
22605cf9dd55SDavid Howells 		bp = call->buffer;
22615cf9dd55SDavid Howells 		callbacks = call->reply[2];
22625cf9dd55SDavid Howells 		callbacks[call->count].version	= ntohl(bp[0]);
22635cf9dd55SDavid Howells 		callbacks[call->count].expiry	= ntohl(bp[1]);
22645cf9dd55SDavid Howells 		callbacks[call->count].type	= ntohl(bp[2]);
22655cf9dd55SDavid Howells 		statuses = call->reply[1];
22665cf9dd55SDavid Howells 		if (call->count == 0 && vnode && statuses[0].abort_code == 0)
22675cf9dd55SDavid Howells 			xdr_decode_AFSCallBack(call, vnode, &bp);
22685cf9dd55SDavid Howells 		call->count++;
22695cf9dd55SDavid Howells 		if (call->count < call->count2)
22705cf9dd55SDavid Howells 			goto more_cbs;
22715cf9dd55SDavid Howells 
22725cf9dd55SDavid Howells 		call->offset = 0;
22735cf9dd55SDavid Howells 		call->unmarshall++;
22745cf9dd55SDavid Howells 
22755cf9dd55SDavid Howells 	case 5:
22765cf9dd55SDavid Howells 		ret = afs_extract_data(call, call->buffer, 6 * 4, false);
22775cf9dd55SDavid Howells 		if (ret < 0)
22785cf9dd55SDavid Howells 			return ret;
22795cf9dd55SDavid Howells 
22805cf9dd55SDavid Howells 		bp = call->buffer;
22815cf9dd55SDavid Howells 		if (call->reply[3])
22825cf9dd55SDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply[3]);
22835cf9dd55SDavid Howells 
22845cf9dd55SDavid Howells 		call->offset = 0;
22855cf9dd55SDavid Howells 		call->unmarshall++;
22865cf9dd55SDavid Howells 
22875cf9dd55SDavid Howells 	case 6:
22885cf9dd55SDavid Howells 		break;
22895cf9dd55SDavid Howells 	}
22905cf9dd55SDavid Howells 
22915cf9dd55SDavid Howells 	_leave(" = 0 [done]");
22925cf9dd55SDavid Howells 	return 0;
22935cf9dd55SDavid Howells }
22945cf9dd55SDavid Howells 
22955cf9dd55SDavid Howells /*
22965cf9dd55SDavid Howells  * FS.InlineBulkStatus operation type
22975cf9dd55SDavid Howells  */
22985cf9dd55SDavid Howells static const struct afs_call_type afs_RXFSInlineBulkStatus = {
22995cf9dd55SDavid Howells 	.name		= "FS.InlineBulkStatus",
23005cf9dd55SDavid Howells 	.op		= afs_FS_InlineBulkStatus,
23015cf9dd55SDavid Howells 	.deliver	= afs_deliver_fs_inline_bulk_status,
23025cf9dd55SDavid Howells 	.destructor	= afs_flat_call_destructor,
23035cf9dd55SDavid Howells };
23045cf9dd55SDavid Howells 
23055cf9dd55SDavid Howells /*
23065cf9dd55SDavid Howells  * Fetch the status information for up to 50 files
23075cf9dd55SDavid Howells  */
23085cf9dd55SDavid Howells int afs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
23095cf9dd55SDavid Howells 			      struct afs_net *net,
23105cf9dd55SDavid Howells 			      struct afs_fid *fids,
23115cf9dd55SDavid Howells 			      struct afs_file_status *statuses,
23125cf9dd55SDavid Howells 			      struct afs_callback *callbacks,
23135cf9dd55SDavid Howells 			      unsigned int nr_fids,
23145cf9dd55SDavid Howells 			      struct afs_volsync *volsync)
23155cf9dd55SDavid Howells {
23165cf9dd55SDavid Howells 	struct afs_call *call;
23175cf9dd55SDavid Howells 	__be32 *bp;
23185cf9dd55SDavid Howells 	int i;
23195cf9dd55SDavid Howells 
23205cf9dd55SDavid Howells 	_enter(",%x,{%x:%u},%u",
23215cf9dd55SDavid Howells 	       key_serial(fc->key), fids[0].vid, fids[1].vnode, nr_fids);
23225cf9dd55SDavid Howells 
23235cf9dd55SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXFSInlineBulkStatus,
23245cf9dd55SDavid Howells 				   (2 + nr_fids * 3) * 4,
23255cf9dd55SDavid Howells 				   21 * 4);
23265cf9dd55SDavid Howells 	if (!call) {
23275cf9dd55SDavid Howells 		fc->ac.error = -ENOMEM;
23285cf9dd55SDavid Howells 		return -ENOMEM;
23295cf9dd55SDavid Howells 	}
23305cf9dd55SDavid Howells 
23315cf9dd55SDavid Howells 	call->key = fc->key;
23325cf9dd55SDavid Howells 	call->reply[0] = NULL; /* vnode for fid[0] */
23335cf9dd55SDavid Howells 	call->reply[1] = statuses;
23345cf9dd55SDavid Howells 	call->reply[2] = callbacks;
23355cf9dd55SDavid Howells 	call->reply[3] = volsync;
23365cf9dd55SDavid Howells 	call->count2 = nr_fids;
23375cf9dd55SDavid Howells 
23385cf9dd55SDavid Howells 	/* marshall the parameters */
23395cf9dd55SDavid Howells 	bp = call->request;
23405cf9dd55SDavid Howells 	*bp++ = htonl(FSINLINEBULKSTATUS);
23415cf9dd55SDavid Howells 	*bp++ = htonl(nr_fids);
23425cf9dd55SDavid Howells 	for (i = 0; i < nr_fids; i++) {
23435cf9dd55SDavid Howells 		*bp++ = htonl(fids[i].vid);
23445cf9dd55SDavid Howells 		*bp++ = htonl(fids[i].vnode);
23455cf9dd55SDavid Howells 		*bp++ = htonl(fids[i].unique);
23465cf9dd55SDavid Howells 	}
23475cf9dd55SDavid Howells 
23485cf9dd55SDavid Howells 	call->cb_break = fc->cb_break;
23495cf9dd55SDavid Howells 	afs_use_fs_server(call, fc->cbi);
23505cf9dd55SDavid Howells 	trace_afs_make_fs_call(call, &fids[0]);
23515cf9dd55SDavid Howells 	return afs_make_call(&fc->ac, call, GFP_NOFS, false);
23525cf9dd55SDavid Howells }
2353