xref: /openbmc/linux/fs/afs/fsclient.c (revision da4aa36d)
108e0e7c8SDavid Howells /* AFS File Server client stubs
21da177e4SLinus Torvalds  *
308e0e7c8SDavid Howells  * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
41da177e4SLinus Torvalds  * Written by David Howells (dhowells@redhat.com)
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
71da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
81da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
91da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/init.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/sched.h>
1508e0e7c8SDavid Howells #include <linux/circ_buf.h>
161da177e4SLinus Torvalds #include "internal.h"
1708e0e7c8SDavid Howells #include "afs_fs.h"
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /*
20260a9803SDavid Howells  * decode an AFSFid block
21260a9803SDavid Howells  */
22260a9803SDavid Howells static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
23260a9803SDavid Howells {
24260a9803SDavid Howells 	const __be32 *bp = *_bp;
25260a9803SDavid Howells 
26260a9803SDavid Howells 	fid->vid		= ntohl(*bp++);
27260a9803SDavid Howells 	fid->vnode		= ntohl(*bp++);
28260a9803SDavid Howells 	fid->unique		= ntohl(*bp++);
29260a9803SDavid Howells 	*_bp = bp;
30260a9803SDavid Howells }
31260a9803SDavid Howells 
32260a9803SDavid Howells /*
3308e0e7c8SDavid Howells  * decode an AFSFetchStatus block
341da177e4SLinus Torvalds  */
3508e0e7c8SDavid Howells static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
36260a9803SDavid Howells 				      struct afs_file_status *status,
3731143d5dSDavid Howells 				      struct afs_vnode *vnode,
3831143d5dSDavid Howells 				      afs_dataversion_t *store_version)
391da177e4SLinus Torvalds {
4031143d5dSDavid Howells 	afs_dataversion_t expected_version;
4108e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
4208e0e7c8SDavid Howells 	umode_t mode;
43260a9803SDavid Howells 	u64 data_version, size;
4408e0e7c8SDavid Howells 	u32 changed = 0; /* becomes non-zero if ctime-type changes seen */
4508e0e7c8SDavid Howells 
4608e0e7c8SDavid Howells #define EXTRACT(DST)				\
4708e0e7c8SDavid Howells 	do {					\
4808e0e7c8SDavid Howells 		u32 x = ntohl(*bp++);		\
4908e0e7c8SDavid Howells 		changed |= DST - x;		\
5008e0e7c8SDavid Howells 		DST = x;			\
5108e0e7c8SDavid Howells 	} while (0)
5208e0e7c8SDavid Howells 
53260a9803SDavid Howells 	status->if_version = ntohl(*bp++);
54260a9803SDavid Howells 	EXTRACT(status->type);
55260a9803SDavid Howells 	EXTRACT(status->nlink);
56260a9803SDavid Howells 	size = ntohl(*bp++);
5708e0e7c8SDavid Howells 	data_version = ntohl(*bp++);
58260a9803SDavid Howells 	EXTRACT(status->author);
59260a9803SDavid Howells 	EXTRACT(status->owner);
60260a9803SDavid Howells 	EXTRACT(status->caller_access); /* call ticket dependent */
61260a9803SDavid Howells 	EXTRACT(status->anon_access);
62260a9803SDavid Howells 	EXTRACT(status->mode);
63260a9803SDavid Howells 	EXTRACT(status->parent.vnode);
64260a9803SDavid Howells 	EXTRACT(status->parent.unique);
6508e0e7c8SDavid Howells 	bp++; /* seg size */
66260a9803SDavid Howells 	status->mtime_client = ntohl(*bp++);
67260a9803SDavid Howells 	status->mtime_server = ntohl(*bp++);
68260a9803SDavid Howells 	EXTRACT(status->group);
6908e0e7c8SDavid Howells 	bp++; /* sync counter */
7008e0e7c8SDavid Howells 	data_version |= (u64) ntohl(*bp++) << 32;
71e8d6c554SDavid Howells 	EXTRACT(status->lock_count);
72260a9803SDavid Howells 	size |= (u64) ntohl(*bp++) << 32;
7308e0e7c8SDavid Howells 	bp++; /* spare 4 */
7408e0e7c8SDavid Howells 	*_bp = bp;
7508e0e7c8SDavid Howells 
76260a9803SDavid Howells 	if (size != status->size) {
77260a9803SDavid Howells 		status->size = size;
78260a9803SDavid Howells 		changed |= true;
79260a9803SDavid Howells 	}
80260a9803SDavid Howells 	status->mode &= S_IALLUGO;
8108e0e7c8SDavid Howells 
82260a9803SDavid Howells 	_debug("vnode time %lx, %lx",
83260a9803SDavid Howells 	       status->mtime_client, status->mtime_server);
84260a9803SDavid Howells 
85260a9803SDavid Howells 	if (vnode) {
86260a9803SDavid Howells 		status->parent.vid = vnode->fid.vid;
87260a9803SDavid Howells 		if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
88260a9803SDavid Howells 			_debug("vnode changed");
89260a9803SDavid Howells 			i_size_write(&vnode->vfs_inode, size);
90260a9803SDavid Howells 			vnode->vfs_inode.i_uid = status->owner;
91260a9803SDavid Howells 			vnode->vfs_inode.i_gid = status->group;
92d6e43f75SDavid Howells 			vnode->vfs_inode.i_generation = vnode->fid.unique;
93bfe86848SMiklos Szeredi 			set_nlink(&vnode->vfs_inode, status->nlink);
94260a9803SDavid Howells 
9508e0e7c8SDavid Howells 			mode = vnode->vfs_inode.i_mode;
9608e0e7c8SDavid Howells 			mode &= ~S_IALLUGO;
97260a9803SDavid Howells 			mode |= status->mode;
98260a9803SDavid Howells 			barrier();
9908e0e7c8SDavid Howells 			vnode->vfs_inode.i_mode = mode;
10008e0e7c8SDavid Howells 		}
10108e0e7c8SDavid Howells 
102260a9803SDavid Howells 		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_server;
10308e0e7c8SDavid Howells 		vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
10408e0e7c8SDavid Howells 		vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
105d6e43f75SDavid Howells 		vnode->vfs_inode.i_version	= data_version;
106260a9803SDavid Howells 	}
10708e0e7c8SDavid Howells 
10831143d5dSDavid Howells 	expected_version = status->data_version;
10931143d5dSDavid Howells 	if (store_version)
11031143d5dSDavid Howells 		expected_version = *store_version;
11131143d5dSDavid Howells 
11231143d5dSDavid Howells 	if (expected_version != data_version) {
113260a9803SDavid Howells 		status->data_version = data_version;
114260a9803SDavid Howells 		if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
115260a9803SDavid Howells 			_debug("vnode modified %llx on {%x:%u}",
116ba3e0e1aSDavid S. Miller 			       (unsigned long long) data_version,
117ba3e0e1aSDavid S. Miller 			       vnode->fid.vid, vnode->fid.vnode);
11808e0e7c8SDavid Howells 			set_bit(AFS_VNODE_MODIFIED, &vnode->flags);
11908e0e7c8SDavid Howells 			set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
1201da177e4SLinus Torvalds 		}
12131143d5dSDavid Howells 	} else if (store_version) {
12231143d5dSDavid Howells 		status->data_version = data_version;
123ec26815aSDavid Howells 	}
124260a9803SDavid Howells }
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds /*
12708e0e7c8SDavid Howells  * decode an AFSCallBack block
1281da177e4SLinus Torvalds  */
12908e0e7c8SDavid Howells static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
1301da177e4SLinus Torvalds {
13108e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
1321da177e4SLinus Torvalds 
13308e0e7c8SDavid Howells 	vnode->cb_version	= ntohl(*bp++);
13408e0e7c8SDavid Howells 	vnode->cb_expiry	= ntohl(*bp++);
13508e0e7c8SDavid Howells 	vnode->cb_type		= ntohl(*bp++);
13608e0e7c8SDavid Howells 	vnode->cb_expires	= vnode->cb_expiry + get_seconds();
13708e0e7c8SDavid Howells 	*_bp = bp;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
140260a9803SDavid Howells static void xdr_decode_AFSCallBack_raw(const __be32 **_bp,
141260a9803SDavid Howells 				       struct afs_callback *cb)
142260a9803SDavid Howells {
143260a9803SDavid Howells 	const __be32 *bp = *_bp;
144260a9803SDavid Howells 
145260a9803SDavid Howells 	cb->version	= ntohl(*bp++);
146260a9803SDavid Howells 	cb->expiry	= ntohl(*bp++);
147260a9803SDavid Howells 	cb->type	= ntohl(*bp++);
148260a9803SDavid Howells 	*_bp = bp;
149260a9803SDavid Howells }
150260a9803SDavid Howells 
1511da177e4SLinus Torvalds /*
15208e0e7c8SDavid Howells  * decode an AFSVolSync block
1531da177e4SLinus Torvalds  */
15408e0e7c8SDavid Howells static void xdr_decode_AFSVolSync(const __be32 **_bp,
15508e0e7c8SDavid Howells 				  struct afs_volsync *volsync)
1561da177e4SLinus Torvalds {
15708e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
1581da177e4SLinus Torvalds 
15908e0e7c8SDavid Howells 	volsync->creation = ntohl(*bp++);
16008e0e7c8SDavid Howells 	bp++; /* spare2 */
16108e0e7c8SDavid Howells 	bp++; /* spare3 */
16208e0e7c8SDavid Howells 	bp++; /* spare4 */
16308e0e7c8SDavid Howells 	bp++; /* spare5 */
16408e0e7c8SDavid Howells 	bp++; /* spare6 */
16508e0e7c8SDavid Howells 	*_bp = bp;
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds 
16808e0e7c8SDavid Howells /*
16931143d5dSDavid Howells  * encode the requested attributes into an AFSStoreStatus block
17031143d5dSDavid Howells  */
17131143d5dSDavid Howells static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr)
17231143d5dSDavid Howells {
17331143d5dSDavid Howells 	__be32 *bp = *_bp;
17431143d5dSDavid Howells 	u32 mask = 0, mtime = 0, owner = 0, group = 0, mode = 0;
17531143d5dSDavid Howells 
17631143d5dSDavid Howells 	mask = 0;
17731143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MTIME) {
17831143d5dSDavid Howells 		mask |= AFS_SET_MTIME;
17931143d5dSDavid Howells 		mtime = attr->ia_mtime.tv_sec;
18031143d5dSDavid Howells 	}
18131143d5dSDavid Howells 
18231143d5dSDavid Howells 	if (attr->ia_valid & ATTR_UID) {
18331143d5dSDavid Howells 		mask |= AFS_SET_OWNER;
18431143d5dSDavid Howells 		owner = attr->ia_uid;
18531143d5dSDavid Howells 	}
18631143d5dSDavid Howells 
18731143d5dSDavid Howells 	if (attr->ia_valid & ATTR_GID) {
18831143d5dSDavid Howells 		mask |= AFS_SET_GROUP;
18931143d5dSDavid Howells 		group = attr->ia_gid;
19031143d5dSDavid Howells 	}
19131143d5dSDavid Howells 
19231143d5dSDavid Howells 	if (attr->ia_valid & ATTR_MODE) {
19331143d5dSDavid Howells 		mask |= AFS_SET_MODE;
19431143d5dSDavid Howells 		mode = attr->ia_mode & S_IALLUGO;
19531143d5dSDavid Howells 	}
19631143d5dSDavid Howells 
19731143d5dSDavid Howells 	*bp++ = htonl(mask);
19831143d5dSDavid Howells 	*bp++ = htonl(mtime);
19931143d5dSDavid Howells 	*bp++ = htonl(owner);
20031143d5dSDavid Howells 	*bp++ = htonl(group);
20131143d5dSDavid Howells 	*bp++ = htonl(mode);
20231143d5dSDavid Howells 	*bp++ = 0;		/* segment size */
20331143d5dSDavid Howells 	*_bp = bp;
20431143d5dSDavid Howells }
20531143d5dSDavid Howells 
20631143d5dSDavid Howells /*
20745222b9eSDavid Howells  * decode an AFSFetchVolumeStatus block
20845222b9eSDavid Howells  */
20945222b9eSDavid Howells static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp,
21045222b9eSDavid Howells 					    struct afs_volume_status *vs)
21145222b9eSDavid Howells {
21245222b9eSDavid Howells 	const __be32 *bp = *_bp;
21345222b9eSDavid Howells 
21445222b9eSDavid Howells 	vs->vid			= ntohl(*bp++);
21545222b9eSDavid Howells 	vs->parent_id		= ntohl(*bp++);
21645222b9eSDavid Howells 	vs->online		= ntohl(*bp++);
21745222b9eSDavid Howells 	vs->in_service		= ntohl(*bp++);
21845222b9eSDavid Howells 	vs->blessed		= ntohl(*bp++);
21945222b9eSDavid Howells 	vs->needs_salvage	= ntohl(*bp++);
22045222b9eSDavid Howells 	vs->type		= ntohl(*bp++);
22145222b9eSDavid Howells 	vs->min_quota		= ntohl(*bp++);
22245222b9eSDavid Howells 	vs->max_quota		= ntohl(*bp++);
22345222b9eSDavid Howells 	vs->blocks_in_use	= ntohl(*bp++);
22445222b9eSDavid Howells 	vs->part_blocks_avail	= ntohl(*bp++);
22545222b9eSDavid Howells 	vs->part_max_blocks	= ntohl(*bp++);
22645222b9eSDavid Howells 	*_bp = bp;
22745222b9eSDavid Howells }
22845222b9eSDavid Howells 
22945222b9eSDavid Howells /*
23008e0e7c8SDavid Howells  * deliver reply data to an FS.FetchStatus
23108e0e7c8SDavid Howells  */
23208e0e7c8SDavid Howells static int afs_deliver_fs_fetch_status(struct afs_call *call,
23308e0e7c8SDavid Howells 				       struct sk_buff *skb, bool last)
23408e0e7c8SDavid Howells {
235260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
23608e0e7c8SDavid Howells 	const __be32 *bp;
2371da177e4SLinus Torvalds 
23808e0e7c8SDavid Howells 	_enter(",,%u", last);
2391da177e4SLinus Torvalds 
24008e0e7c8SDavid Howells 	afs_transfer_reply(call, skb);
24108e0e7c8SDavid Howells 	if (!last)
24208e0e7c8SDavid Howells 		return 0;
2431da177e4SLinus Torvalds 
24408e0e7c8SDavid Howells 	if (call->reply_size != call->reply_max)
24508e0e7c8SDavid Howells 		return -EBADMSG;
2461da177e4SLinus Torvalds 
24708e0e7c8SDavid Howells 	/* unmarshall the reply once we've received all of it */
24808e0e7c8SDavid Howells 	bp = call->buffer;
24931143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
250260a9803SDavid Howells 	xdr_decode_AFSCallBack(&bp, vnode);
25108e0e7c8SDavid Howells 	if (call->reply2)
25208e0e7c8SDavid Howells 		xdr_decode_AFSVolSync(&bp, call->reply2);
2531da177e4SLinus Torvalds 
25408e0e7c8SDavid Howells 	_leave(" = 0 [done]");
25508e0e7c8SDavid Howells 	return 0;
256ec26815aSDavid Howells }
25708e0e7c8SDavid Howells 
25808e0e7c8SDavid Howells /*
25908e0e7c8SDavid Howells  * FS.FetchStatus operation type
26008e0e7c8SDavid Howells  */
26108e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchStatus = {
26200d3b7a4SDavid Howells 	.name		= "FS.FetchStatus",
26308e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_status,
26408e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
26508e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
26608e0e7c8SDavid Howells };
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds /*
2691da177e4SLinus Torvalds  * fetch the status information for a file
2701da177e4SLinus Torvalds  */
27108e0e7c8SDavid Howells int afs_fs_fetch_file_status(struct afs_server *server,
27200d3b7a4SDavid Howells 			     struct key *key,
2731da177e4SLinus Torvalds 			     struct afs_vnode *vnode,
27408e0e7c8SDavid Howells 			     struct afs_volsync *volsync,
27508e0e7c8SDavid Howells 			     const struct afs_wait_mode *wait_mode)
2761da177e4SLinus Torvalds {
27708e0e7c8SDavid Howells 	struct afs_call *call;
2781da177e4SLinus Torvalds 	__be32 *bp;
2791da177e4SLinus Torvalds 
280416351f2SDavid Howells 	_enter(",%x,{%x:%u},,",
281260a9803SDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
2821da177e4SLinus Torvalds 
283260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
28408e0e7c8SDavid Howells 	if (!call)
28508e0e7c8SDavid Howells 		return -ENOMEM;
2861da177e4SLinus Torvalds 
28700d3b7a4SDavid Howells 	call->key = key;
28808e0e7c8SDavid Howells 	call->reply = vnode;
28908e0e7c8SDavid Howells 	call->reply2 = volsync;
29008e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
29108e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	/* marshall the parameters */
29408e0e7c8SDavid Howells 	bp = call->request;
2951da177e4SLinus Torvalds 	bp[0] = htonl(FSFETCHSTATUS);
2961da177e4SLinus Torvalds 	bp[1] = htonl(vnode->fid.vid);
2971da177e4SLinus Torvalds 	bp[2] = htonl(vnode->fid.vnode);
2981da177e4SLinus Torvalds 	bp[3] = htonl(vnode->fid.unique);
2991da177e4SLinus Torvalds 
30008e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
301ec26815aSDavid Howells }
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds /*
30408e0e7c8SDavid Howells  * deliver reply data to an FS.FetchData
3051da177e4SLinus Torvalds  */
30608e0e7c8SDavid Howells static int afs_deliver_fs_fetch_data(struct afs_call *call,
30708e0e7c8SDavid Howells 				     struct sk_buff *skb, bool last)
3081da177e4SLinus Torvalds {
309260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
31008e0e7c8SDavid Howells 	const __be32 *bp;
31108e0e7c8SDavid Howells 	struct page *page;
31208e0e7c8SDavid Howells 	void *buffer;
3131da177e4SLinus Torvalds 	int ret;
3141da177e4SLinus Torvalds 
31508e0e7c8SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
3161da177e4SLinus Torvalds 
31708e0e7c8SDavid Howells 	switch (call->unmarshall) {
31808e0e7c8SDavid Howells 	case 0:
31908e0e7c8SDavid Howells 		call->offset = 0;
32008e0e7c8SDavid Howells 		call->unmarshall++;
321b9b1f8d5SDavid Howells 		if (call->operation_ID != FSFETCHDATA64) {
322b9b1f8d5SDavid Howells 			call->unmarshall++;
323b9b1f8d5SDavid Howells 			goto no_msw;
324b9b1f8d5SDavid Howells 		}
3251da177e4SLinus Torvalds 
326b9b1f8d5SDavid Howells 		/* extract the upper part of the returned data length of an
327b9b1f8d5SDavid Howells 		 * FSFETCHDATA64 op (which should always be 0 using this
328b9b1f8d5SDavid Howells 		 * client) */
32908e0e7c8SDavid Howells 	case 1:
330b9b1f8d5SDavid Howells 		_debug("extract data length (MSW)");
331b9b1f8d5SDavid Howells 		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
332b9b1f8d5SDavid Howells 		switch (ret) {
333b9b1f8d5SDavid Howells 		case 0:		break;
334b9b1f8d5SDavid Howells 		case -EAGAIN:	return 0;
335b9b1f8d5SDavid Howells 		default:	return ret;
336b9b1f8d5SDavid Howells 		}
337b9b1f8d5SDavid Howells 
338b9b1f8d5SDavid Howells 		call->count = ntohl(call->tmp);
339b9b1f8d5SDavid Howells 		_debug("DATA length MSW: %u", call->count);
340b9b1f8d5SDavid Howells 		if (call->count > 0)
341b9b1f8d5SDavid Howells 			return -EBADMSG;
342b9b1f8d5SDavid Howells 		call->offset = 0;
343b9b1f8d5SDavid Howells 		call->unmarshall++;
344b9b1f8d5SDavid Howells 
345b9b1f8d5SDavid Howells 	no_msw:
346b9b1f8d5SDavid Howells 		/* extract the returned data length */
347b9b1f8d5SDavid Howells 	case 2:
34808e0e7c8SDavid Howells 		_debug("extract data length");
34908e0e7c8SDavid Howells 		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
35008e0e7c8SDavid Howells 		switch (ret) {
35108e0e7c8SDavid Howells 		case 0:		break;
35208e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
35308e0e7c8SDavid Howells 		default:	return ret;
3541da177e4SLinus Torvalds 		}
3551da177e4SLinus Torvalds 
35608e0e7c8SDavid Howells 		call->count = ntohl(call->tmp);
35708e0e7c8SDavid Howells 		_debug("DATA length: %u", call->count);
35808e0e7c8SDavid Howells 		if (call->count > PAGE_SIZE)
35908e0e7c8SDavid Howells 			return -EBADMSG;
36008e0e7c8SDavid Howells 		call->offset = 0;
36108e0e7c8SDavid Howells 		call->unmarshall++;
3621da177e4SLinus Torvalds 
36308e0e7c8SDavid Howells 		/* extract the returned data */
364b9b1f8d5SDavid Howells 	case 3:
36508e0e7c8SDavid Howells 		_debug("extract data");
366416351f2SDavid Howells 		if (call->count > 0) {
36708e0e7c8SDavid Howells 			page = call->reply3;
368da4aa36dSCong Wang 			buffer = kmap_atomic(page);
369416351f2SDavid Howells 			ret = afs_extract_data(call, skb, last, buffer,
370416351f2SDavid Howells 					       call->count);
371da4aa36dSCong Wang 			kunmap_atomic(buffer);
37208e0e7c8SDavid Howells 			switch (ret) {
37308e0e7c8SDavid Howells 			case 0:		break;
37408e0e7c8SDavid Howells 			case -EAGAIN:	return 0;
37508e0e7c8SDavid Howells 			default:	return ret;
3761da177e4SLinus Torvalds 			}
377416351f2SDavid Howells 		}
3781da177e4SLinus Torvalds 
37908e0e7c8SDavid Howells 		call->offset = 0;
38008e0e7c8SDavid Howells 		call->unmarshall++;
38108e0e7c8SDavid Howells 
38208e0e7c8SDavid Howells 		/* extract the metadata */
383b9b1f8d5SDavid Howells 	case 4:
384260a9803SDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer,
385260a9803SDavid Howells 				       (21 + 3 + 6) * 4);
38608e0e7c8SDavid Howells 		switch (ret) {
38708e0e7c8SDavid Howells 		case 0:		break;
38808e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
38908e0e7c8SDavid Howells 		default:	return ret;
390ec26815aSDavid Howells 		}
3911da177e4SLinus Torvalds 
39208e0e7c8SDavid Howells 		bp = call->buffer;
39331143d5dSDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
394260a9803SDavid Howells 		xdr_decode_AFSCallBack(&bp, vnode);
39508e0e7c8SDavid Howells 		if (call->reply2)
39608e0e7c8SDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply2);
3971da177e4SLinus Torvalds 
39808e0e7c8SDavid Howells 		call->offset = 0;
39908e0e7c8SDavid Howells 		call->unmarshall++;
4001da177e4SLinus Torvalds 
401b9b1f8d5SDavid Howells 	case 5:
40208e0e7c8SDavid Howells 		_debug("trailer");
40308e0e7c8SDavid Howells 		if (skb->len != 0)
40408e0e7c8SDavid Howells 			return -EBADMSG;
4051da177e4SLinus Torvalds 		break;
4061da177e4SLinus Torvalds 	}
4071da177e4SLinus Torvalds 
40808e0e7c8SDavid Howells 	if (!last)
40908e0e7c8SDavid Howells 		return 0;
4101da177e4SLinus Torvalds 
411416351f2SDavid Howells 	if (call->count < PAGE_SIZE) {
412416351f2SDavid Howells 		_debug("clear");
413416351f2SDavid Howells 		page = call->reply3;
414da4aa36dSCong Wang 		buffer = kmap_atomic(page);
415416351f2SDavid Howells 		memset(buffer + call->count, 0, PAGE_SIZE - call->count);
416da4aa36dSCong Wang 		kunmap_atomic(buffer);
417416351f2SDavid Howells 	}
418416351f2SDavid Howells 
41908e0e7c8SDavid Howells 	_leave(" = 0 [done]");
42008e0e7c8SDavid Howells 	return 0;
421ec26815aSDavid Howells }
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds /*
42408e0e7c8SDavid Howells  * FS.FetchData operation type
4251da177e4SLinus Torvalds  */
42608e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchData = {
42700d3b7a4SDavid Howells 	.name		= "FS.FetchData",
42808e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
42908e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
43008e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
43108e0e7c8SDavid Howells };
43208e0e7c8SDavid Howells 
433b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSFetchData64 = {
434b9b1f8d5SDavid Howells 	.name		= "FS.FetchData64",
435b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
436b9b1f8d5SDavid Howells 	.abort_to_error	= afs_abort_to_error,
437b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
438b9b1f8d5SDavid Howells };
439b9b1f8d5SDavid Howells 
440b9b1f8d5SDavid Howells /*
441b9b1f8d5SDavid Howells  * fetch data from a very large file
442b9b1f8d5SDavid Howells  */
443b9b1f8d5SDavid Howells static int afs_fs_fetch_data64(struct afs_server *server,
444b9b1f8d5SDavid Howells 			       struct key *key,
445b9b1f8d5SDavid Howells 			       struct afs_vnode *vnode,
446b9b1f8d5SDavid Howells 			       off_t offset, size_t length,
447b9b1f8d5SDavid Howells 			       struct page *buffer,
448b9b1f8d5SDavid Howells 			       const struct afs_wait_mode *wait_mode)
449b9b1f8d5SDavid Howells {
450b9b1f8d5SDavid Howells 	struct afs_call *call;
451b9b1f8d5SDavid Howells 	__be32 *bp;
452b9b1f8d5SDavid Howells 
453b9b1f8d5SDavid Howells 	_enter("");
454b9b1f8d5SDavid Howells 
455b9b1f8d5SDavid Howells 	ASSERTCMP(length, <, ULONG_MAX);
456b9b1f8d5SDavid Howells 
457b9b1f8d5SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
458b9b1f8d5SDavid Howells 	if (!call)
459b9b1f8d5SDavid Howells 		return -ENOMEM;
460b9b1f8d5SDavid Howells 
461b9b1f8d5SDavid Howells 	call->key = key;
462b9b1f8d5SDavid Howells 	call->reply = vnode;
463b9b1f8d5SDavid Howells 	call->reply2 = NULL; /* volsync */
464b9b1f8d5SDavid Howells 	call->reply3 = buffer;
465b9b1f8d5SDavid Howells 	call->service_id = FS_SERVICE;
466b9b1f8d5SDavid Howells 	call->port = htons(AFS_FS_PORT);
467b9b1f8d5SDavid Howells 	call->operation_ID = FSFETCHDATA64;
468b9b1f8d5SDavid Howells 
469b9b1f8d5SDavid Howells 	/* marshall the parameters */
470b9b1f8d5SDavid Howells 	bp = call->request;
471b9b1f8d5SDavid Howells 	bp[0] = htonl(FSFETCHDATA64);
472b9b1f8d5SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
473b9b1f8d5SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
474b9b1f8d5SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
475b9b1f8d5SDavid Howells 	bp[4] = htonl(upper_32_bits(offset));
476b9b1f8d5SDavid Howells 	bp[5] = htonl((u32) offset);
477b9b1f8d5SDavid Howells 	bp[6] = 0;
478b9b1f8d5SDavid Howells 	bp[7] = htonl((u32) length);
479b9b1f8d5SDavid Howells 
480b9b1f8d5SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
481b9b1f8d5SDavid Howells }
482b9b1f8d5SDavid Howells 
48308e0e7c8SDavid Howells /*
48408e0e7c8SDavid Howells  * fetch data from a file
48508e0e7c8SDavid Howells  */
48608e0e7c8SDavid Howells int afs_fs_fetch_data(struct afs_server *server,
48700d3b7a4SDavid Howells 		      struct key *key,
4881da177e4SLinus Torvalds 		      struct afs_vnode *vnode,
48908e0e7c8SDavid Howells 		      off_t offset, size_t length,
49008e0e7c8SDavid Howells 		      struct page *buffer,
49108e0e7c8SDavid Howells 		      const struct afs_wait_mode *wait_mode)
4921da177e4SLinus Torvalds {
49308e0e7c8SDavid Howells 	struct afs_call *call;
49408e0e7c8SDavid Howells 	__be32 *bp;
4951da177e4SLinus Torvalds 
496b9b1f8d5SDavid Howells 	if (upper_32_bits(offset) || upper_32_bits(offset + length))
497b9b1f8d5SDavid Howells 		return afs_fs_fetch_data64(server, key, vnode, offset, length,
498b9b1f8d5SDavid Howells 					   buffer, wait_mode);
499b9b1f8d5SDavid Howells 
50008e0e7c8SDavid Howells 	_enter("");
5011da177e4SLinus Torvalds 
502260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
50308e0e7c8SDavid Howells 	if (!call)
50408e0e7c8SDavid Howells 		return -ENOMEM;
5051da177e4SLinus Torvalds 
50600d3b7a4SDavid Howells 	call->key = key;
50708e0e7c8SDavid Howells 	call->reply = vnode;
508260a9803SDavid Howells 	call->reply2 = NULL; /* volsync */
50908e0e7c8SDavid Howells 	call->reply3 = buffer;
51008e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
51108e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
512b9b1f8d5SDavid Howells 	call->operation_ID = FSFETCHDATA;
5131da177e4SLinus Torvalds 
5141da177e4SLinus Torvalds 	/* marshall the parameters */
51508e0e7c8SDavid Howells 	bp = call->request;
51608e0e7c8SDavid Howells 	bp[0] = htonl(FSFETCHDATA);
51708e0e7c8SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
51808e0e7c8SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
51908e0e7c8SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
52008e0e7c8SDavid Howells 	bp[4] = htonl(offset);
52108e0e7c8SDavid Howells 	bp[5] = htonl(length);
5221da177e4SLinus Torvalds 
52308e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
5241da177e4SLinus Torvalds }
5251da177e4SLinus Torvalds 
52608e0e7c8SDavid Howells /*
52708e0e7c8SDavid Howells  * deliver reply data to an FS.GiveUpCallBacks
52808e0e7c8SDavid Howells  */
52908e0e7c8SDavid Howells static int afs_deliver_fs_give_up_callbacks(struct afs_call *call,
53008e0e7c8SDavid Howells 					    struct sk_buff *skb, bool last)
53108e0e7c8SDavid Howells {
53208e0e7c8SDavid Howells 	_enter(",{%u},%d", skb->len, last);
5331da177e4SLinus Torvalds 
53408e0e7c8SDavid Howells 	if (skb->len > 0)
53508e0e7c8SDavid Howells 		return -EBADMSG; /* shouldn't be any reply data */
53608e0e7c8SDavid Howells 	return 0;
5371da177e4SLinus Torvalds }
5381da177e4SLinus Torvalds 
53908e0e7c8SDavid Howells /*
54008e0e7c8SDavid Howells  * FS.GiveUpCallBacks operation type
54108e0e7c8SDavid Howells  */
54208e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
54300d3b7a4SDavid Howells 	.name		= "FS.GiveUpCallBacks",
54408e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_give_up_callbacks,
54508e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
54608e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
54708e0e7c8SDavid Howells };
5481da177e4SLinus Torvalds 
54908e0e7c8SDavid Howells /*
55008e0e7c8SDavid Howells  * give up a set of callbacks
55108e0e7c8SDavid Howells  * - the callbacks are held in the server->cb_break ring
55208e0e7c8SDavid Howells  */
55308e0e7c8SDavid Howells int afs_fs_give_up_callbacks(struct afs_server *server,
55408e0e7c8SDavid Howells 			     const struct afs_wait_mode *wait_mode)
55508e0e7c8SDavid Howells {
55608e0e7c8SDavid Howells 	struct afs_call *call;
55708e0e7c8SDavid Howells 	size_t ncallbacks;
55808e0e7c8SDavid Howells 	__be32 *bp, *tp;
55908e0e7c8SDavid Howells 	int loop;
5601da177e4SLinus Torvalds 
56108e0e7c8SDavid Howells 	ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
56208e0e7c8SDavid Howells 			      ARRAY_SIZE(server->cb_break));
56308e0e7c8SDavid Howells 
56408e0e7c8SDavid Howells 	_enter("{%zu},", ncallbacks);
56508e0e7c8SDavid Howells 
56608e0e7c8SDavid Howells 	if (ncallbacks == 0)
56708e0e7c8SDavid Howells 		return 0;
56808e0e7c8SDavid Howells 	if (ncallbacks > AFSCBMAX)
56908e0e7c8SDavid Howells 		ncallbacks = AFSCBMAX;
57008e0e7c8SDavid Howells 
57108e0e7c8SDavid Howells 	_debug("break %zu callbacks", ncallbacks);
57208e0e7c8SDavid Howells 
57308e0e7c8SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks,
57408e0e7c8SDavid Howells 				   12 + ncallbacks * 6 * 4, 0);
57508e0e7c8SDavid Howells 	if (!call)
57608e0e7c8SDavid Howells 		return -ENOMEM;
57708e0e7c8SDavid Howells 
57808e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
57908e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
58008e0e7c8SDavid Howells 
58108e0e7c8SDavid Howells 	/* marshall the parameters */
58208e0e7c8SDavid Howells 	bp = call->request;
58308e0e7c8SDavid Howells 	tp = bp + 2 + ncallbacks * 3;
58408e0e7c8SDavid Howells 	*bp++ = htonl(FSGIVEUPCALLBACKS);
58508e0e7c8SDavid Howells 	*bp++ = htonl(ncallbacks);
58608e0e7c8SDavid Howells 	*tp++ = htonl(ncallbacks);
58708e0e7c8SDavid Howells 
58808e0e7c8SDavid Howells 	atomic_sub(ncallbacks, &server->cb_break_n);
58908e0e7c8SDavid Howells 	for (loop = ncallbacks; loop > 0; loop--) {
59008e0e7c8SDavid Howells 		struct afs_callback *cb =
59108e0e7c8SDavid Howells 			&server->cb_break[server->cb_break_tail];
59208e0e7c8SDavid Howells 
59308e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vid);
59408e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vnode);
59508e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.unique);
59608e0e7c8SDavid Howells 		*tp++ = htonl(cb->version);
59708e0e7c8SDavid Howells 		*tp++ = htonl(cb->expiry);
59808e0e7c8SDavid Howells 		*tp++ = htonl(cb->type);
59908e0e7c8SDavid Howells 		smp_mb();
60008e0e7c8SDavid Howells 		server->cb_break_tail =
60108e0e7c8SDavid Howells 			(server->cb_break_tail + 1) &
60208e0e7c8SDavid Howells 			(ARRAY_SIZE(server->cb_break) - 1);
603ec26815aSDavid Howells 	}
60408e0e7c8SDavid Howells 
60508e0e7c8SDavid Howells 	ASSERT(ncallbacks > 0);
60608e0e7c8SDavid Howells 	wake_up_nr(&server->cb_break_waitq, ncallbacks);
60708e0e7c8SDavid Howells 
60808e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
60908e0e7c8SDavid Howells }
610260a9803SDavid Howells 
611260a9803SDavid Howells /*
612260a9803SDavid Howells  * deliver reply data to an FS.CreateFile or an FS.MakeDir
613260a9803SDavid Howells  */
614260a9803SDavid Howells static int afs_deliver_fs_create_vnode(struct afs_call *call,
615260a9803SDavid Howells 				       struct sk_buff *skb, bool last)
616260a9803SDavid Howells {
617260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
618260a9803SDavid Howells 	const __be32 *bp;
619260a9803SDavid Howells 
620260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
621260a9803SDavid Howells 
622260a9803SDavid Howells 	afs_transfer_reply(call, skb);
623260a9803SDavid Howells 	if (!last)
624260a9803SDavid Howells 		return 0;
625260a9803SDavid Howells 
626260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
627260a9803SDavid Howells 		return -EBADMSG;
628260a9803SDavid Howells 
629260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
630260a9803SDavid Howells 	bp = call->buffer;
631260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
63231143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL);
63331143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
634260a9803SDavid Howells 	xdr_decode_AFSCallBack_raw(&bp, call->reply4);
635260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
636260a9803SDavid Howells 
637260a9803SDavid Howells 	_leave(" = 0 [done]");
638260a9803SDavid Howells 	return 0;
639260a9803SDavid Howells }
640260a9803SDavid Howells 
641260a9803SDavid Howells /*
642260a9803SDavid Howells  * FS.CreateFile and FS.MakeDir operation type
643260a9803SDavid Howells  */
644260a9803SDavid Howells static const struct afs_call_type afs_RXFSCreateXXXX = {
645260a9803SDavid Howells 	.name		= "FS.CreateXXXX",
646260a9803SDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
647260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
648260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
649260a9803SDavid Howells };
650260a9803SDavid Howells 
651260a9803SDavid Howells /*
652260a9803SDavid Howells  * create a file or make a directory
653260a9803SDavid Howells  */
654260a9803SDavid Howells int afs_fs_create(struct afs_server *server,
655260a9803SDavid Howells 		  struct key *key,
656260a9803SDavid Howells 		  struct afs_vnode *vnode,
657260a9803SDavid Howells 		  const char *name,
658260a9803SDavid Howells 		  umode_t mode,
659260a9803SDavid Howells 		  struct afs_fid *newfid,
660260a9803SDavid Howells 		  struct afs_file_status *newstatus,
661260a9803SDavid Howells 		  struct afs_callback *newcb,
662260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
663260a9803SDavid Howells {
664260a9803SDavid Howells 	struct afs_call *call;
665260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
666260a9803SDavid Howells 	__be32 *bp;
667260a9803SDavid Howells 
668260a9803SDavid Howells 	_enter("");
669260a9803SDavid Howells 
670260a9803SDavid Howells 	namesz = strlen(name);
671260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
672260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (6 * 4);
673260a9803SDavid Howells 
674260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSCreateXXXX, reqsz,
675260a9803SDavid Howells 				   (3 + 21 + 21 + 3 + 6) * 4);
676260a9803SDavid Howells 	if (!call)
677260a9803SDavid Howells 		return -ENOMEM;
678260a9803SDavid Howells 
679260a9803SDavid Howells 	call->key = key;
680260a9803SDavid Howells 	call->reply = vnode;
681260a9803SDavid Howells 	call->reply2 = newfid;
682260a9803SDavid Howells 	call->reply3 = newstatus;
683260a9803SDavid Howells 	call->reply4 = newcb;
684260a9803SDavid Howells 	call->service_id = FS_SERVICE;
685260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
686260a9803SDavid Howells 
687260a9803SDavid Howells 	/* marshall the parameters */
688260a9803SDavid Howells 	bp = call->request;
689260a9803SDavid Howells 	*bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE);
690260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
691260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
692260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
693260a9803SDavid Howells 	*bp++ = htonl(namesz);
694260a9803SDavid Howells 	memcpy(bp, name, namesz);
695260a9803SDavid Howells 	bp = (void *) bp + namesz;
696260a9803SDavid Howells 	if (padsz > 0) {
697260a9803SDavid Howells 		memset(bp, 0, padsz);
698260a9803SDavid Howells 		bp = (void *) bp + padsz;
699260a9803SDavid Howells 	}
700260a9803SDavid Howells 	*bp++ = htonl(AFS_SET_MODE);
701260a9803SDavid Howells 	*bp++ = 0; /* mtime */
702260a9803SDavid Howells 	*bp++ = 0; /* owner */
703260a9803SDavid Howells 	*bp++ = 0; /* group */
704260a9803SDavid Howells 	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
705260a9803SDavid Howells 	*bp++ = 0; /* segment size */
706260a9803SDavid Howells 
707260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
708260a9803SDavid Howells }
709260a9803SDavid Howells 
710260a9803SDavid Howells /*
711260a9803SDavid Howells  * deliver reply data to an FS.RemoveFile or FS.RemoveDir
712260a9803SDavid Howells  */
713260a9803SDavid Howells static int afs_deliver_fs_remove(struct afs_call *call,
714260a9803SDavid Howells 				 struct sk_buff *skb, bool last)
715260a9803SDavid Howells {
716260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
717260a9803SDavid Howells 	const __be32 *bp;
718260a9803SDavid Howells 
719260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
720260a9803SDavid Howells 
721260a9803SDavid Howells 	afs_transfer_reply(call, skb);
722260a9803SDavid Howells 	if (!last)
723260a9803SDavid Howells 		return 0;
724260a9803SDavid Howells 
725260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
726260a9803SDavid Howells 		return -EBADMSG;
727260a9803SDavid Howells 
728260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
729260a9803SDavid Howells 	bp = call->buffer;
73031143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
731260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
732260a9803SDavid Howells 
733260a9803SDavid Howells 	_leave(" = 0 [done]");
734260a9803SDavid Howells 	return 0;
735260a9803SDavid Howells }
736260a9803SDavid Howells 
737260a9803SDavid Howells /*
738260a9803SDavid Howells  * FS.RemoveDir/FS.RemoveFile operation type
739260a9803SDavid Howells  */
740260a9803SDavid Howells static const struct afs_call_type afs_RXFSRemoveXXXX = {
741260a9803SDavid Howells 	.name		= "FS.RemoveXXXX",
742260a9803SDavid Howells 	.deliver	= afs_deliver_fs_remove,
743260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
744260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
745260a9803SDavid Howells };
746260a9803SDavid Howells 
747260a9803SDavid Howells /*
748260a9803SDavid Howells  * remove a file or directory
749260a9803SDavid Howells  */
750260a9803SDavid Howells int afs_fs_remove(struct afs_server *server,
751260a9803SDavid Howells 		  struct key *key,
752260a9803SDavid Howells 		  struct afs_vnode *vnode,
753260a9803SDavid Howells 		  const char *name,
754260a9803SDavid Howells 		  bool isdir,
755260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
756260a9803SDavid Howells {
757260a9803SDavid Howells 	struct afs_call *call;
758260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
759260a9803SDavid Howells 	__be32 *bp;
760260a9803SDavid Howells 
761260a9803SDavid Howells 	_enter("");
762260a9803SDavid Howells 
763260a9803SDavid Howells 	namesz = strlen(name);
764260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
765260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz;
766260a9803SDavid Howells 
767260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4);
768260a9803SDavid Howells 	if (!call)
769260a9803SDavid Howells 		return -ENOMEM;
770260a9803SDavid Howells 
771260a9803SDavid Howells 	call->key = key;
772260a9803SDavid Howells 	call->reply = vnode;
773260a9803SDavid Howells 	call->service_id = FS_SERVICE;
774260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
775260a9803SDavid Howells 
776260a9803SDavid Howells 	/* marshall the parameters */
777260a9803SDavid Howells 	bp = call->request;
778260a9803SDavid Howells 	*bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE);
779260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
780260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
781260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
782260a9803SDavid Howells 	*bp++ = htonl(namesz);
783260a9803SDavid Howells 	memcpy(bp, name, namesz);
784260a9803SDavid Howells 	bp = (void *) bp + namesz;
785260a9803SDavid Howells 	if (padsz > 0) {
786260a9803SDavid Howells 		memset(bp, 0, padsz);
787260a9803SDavid Howells 		bp = (void *) bp + padsz;
788260a9803SDavid Howells 	}
789260a9803SDavid Howells 
790260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
791260a9803SDavid Howells }
792260a9803SDavid Howells 
793260a9803SDavid Howells /*
794260a9803SDavid Howells  * deliver reply data to an FS.Link
795260a9803SDavid Howells  */
796260a9803SDavid Howells static int afs_deliver_fs_link(struct afs_call *call,
797260a9803SDavid Howells 			       struct sk_buff *skb, bool last)
798260a9803SDavid Howells {
799260a9803SDavid Howells 	struct afs_vnode *dvnode = call->reply, *vnode = call->reply2;
800260a9803SDavid Howells 	const __be32 *bp;
801260a9803SDavid Howells 
802260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
803260a9803SDavid Howells 
804260a9803SDavid Howells 	afs_transfer_reply(call, skb);
805260a9803SDavid Howells 	if (!last)
806260a9803SDavid Howells 		return 0;
807260a9803SDavid Howells 
808260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
809260a9803SDavid Howells 		return -EBADMSG;
810260a9803SDavid Howells 
811260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
812260a9803SDavid Howells 	bp = call->buffer;
81331143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
81431143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode, NULL);
815260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
816260a9803SDavid Howells 
817260a9803SDavid Howells 	_leave(" = 0 [done]");
818260a9803SDavid Howells 	return 0;
819260a9803SDavid Howells }
820260a9803SDavid Howells 
821260a9803SDavid Howells /*
822260a9803SDavid Howells  * FS.Link operation type
823260a9803SDavid Howells  */
824260a9803SDavid Howells static const struct afs_call_type afs_RXFSLink = {
825260a9803SDavid Howells 	.name		= "FS.Link",
826260a9803SDavid Howells 	.deliver	= afs_deliver_fs_link,
827260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
828260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
829260a9803SDavid Howells };
830260a9803SDavid Howells 
831260a9803SDavid Howells /*
832260a9803SDavid Howells  * make a hard link
833260a9803SDavid Howells  */
834260a9803SDavid Howells int afs_fs_link(struct afs_server *server,
835260a9803SDavid Howells 		struct key *key,
836260a9803SDavid Howells 		struct afs_vnode *dvnode,
837260a9803SDavid Howells 		struct afs_vnode *vnode,
838260a9803SDavid Howells 		const char *name,
839260a9803SDavid Howells 		const struct afs_wait_mode *wait_mode)
840260a9803SDavid Howells {
841260a9803SDavid Howells 	struct afs_call *call;
842260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
843260a9803SDavid Howells 	__be32 *bp;
844260a9803SDavid Howells 
845260a9803SDavid Howells 	_enter("");
846260a9803SDavid Howells 
847260a9803SDavid Howells 	namesz = strlen(name);
848260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
849260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (3 * 4);
850260a9803SDavid Howells 
851260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
852260a9803SDavid Howells 	if (!call)
853260a9803SDavid Howells 		return -ENOMEM;
854260a9803SDavid Howells 
855260a9803SDavid Howells 	call->key = key;
856260a9803SDavid Howells 	call->reply = dvnode;
857260a9803SDavid Howells 	call->reply2 = vnode;
858260a9803SDavid Howells 	call->service_id = FS_SERVICE;
859260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
860260a9803SDavid Howells 
861260a9803SDavid Howells 	/* marshall the parameters */
862260a9803SDavid Howells 	bp = call->request;
863260a9803SDavid Howells 	*bp++ = htonl(FSLINK);
864260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vid);
865260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vnode);
866260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.unique);
867260a9803SDavid Howells 	*bp++ = htonl(namesz);
868260a9803SDavid Howells 	memcpy(bp, name, namesz);
869260a9803SDavid Howells 	bp = (void *) bp + namesz;
870260a9803SDavid Howells 	if (padsz > 0) {
871260a9803SDavid Howells 		memset(bp, 0, padsz);
872260a9803SDavid Howells 		bp = (void *) bp + padsz;
873260a9803SDavid Howells 	}
874260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
875260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
876260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
877260a9803SDavid Howells 
878260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
879260a9803SDavid Howells }
880260a9803SDavid Howells 
881260a9803SDavid Howells /*
882260a9803SDavid Howells  * deliver reply data to an FS.Symlink
883260a9803SDavid Howells  */
884260a9803SDavid Howells static int afs_deliver_fs_symlink(struct afs_call *call,
885260a9803SDavid Howells 				  struct sk_buff *skb, bool last)
886260a9803SDavid Howells {
887260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
888260a9803SDavid Howells 	const __be32 *bp;
889260a9803SDavid Howells 
890260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
891260a9803SDavid Howells 
892260a9803SDavid Howells 	afs_transfer_reply(call, skb);
893260a9803SDavid Howells 	if (!last)
894260a9803SDavid Howells 		return 0;
895260a9803SDavid Howells 
896260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
897260a9803SDavid Howells 		return -EBADMSG;
898260a9803SDavid Howells 
899260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
900260a9803SDavid Howells 	bp = call->buffer;
901260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
90231143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL);
90331143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
904260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
905260a9803SDavid Howells 
906260a9803SDavid Howells 	_leave(" = 0 [done]");
907260a9803SDavid Howells 	return 0;
908260a9803SDavid Howells }
909260a9803SDavid Howells 
910260a9803SDavid Howells /*
911260a9803SDavid Howells  * FS.Symlink operation type
912260a9803SDavid Howells  */
913260a9803SDavid Howells static const struct afs_call_type afs_RXFSSymlink = {
914260a9803SDavid Howells 	.name		= "FS.Symlink",
915260a9803SDavid Howells 	.deliver	= afs_deliver_fs_symlink,
916260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
917260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
918260a9803SDavid Howells };
919260a9803SDavid Howells 
920260a9803SDavid Howells /*
921260a9803SDavid Howells  * create a symbolic link
922260a9803SDavid Howells  */
923260a9803SDavid Howells int afs_fs_symlink(struct afs_server *server,
924260a9803SDavid Howells 		   struct key *key,
925260a9803SDavid Howells 		   struct afs_vnode *vnode,
926260a9803SDavid Howells 		   const char *name,
927260a9803SDavid Howells 		   const char *contents,
928260a9803SDavid Howells 		   struct afs_fid *newfid,
929260a9803SDavid Howells 		   struct afs_file_status *newstatus,
930260a9803SDavid Howells 		   const struct afs_wait_mode *wait_mode)
931260a9803SDavid Howells {
932260a9803SDavid Howells 	struct afs_call *call;
933260a9803SDavid Howells 	size_t namesz, reqsz, padsz, c_namesz, c_padsz;
934260a9803SDavid Howells 	__be32 *bp;
935260a9803SDavid Howells 
936260a9803SDavid Howells 	_enter("");
937260a9803SDavid Howells 
938260a9803SDavid Howells 	namesz = strlen(name);
939260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
940260a9803SDavid Howells 
941260a9803SDavid Howells 	c_namesz = strlen(contents);
942260a9803SDavid Howells 	c_padsz = (4 - (c_namesz & 3)) & 3;
943260a9803SDavid Howells 
944260a9803SDavid Howells 	reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
945260a9803SDavid Howells 
946260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSSymlink, reqsz,
947260a9803SDavid Howells 				   (3 + 21 + 21 + 6) * 4);
948260a9803SDavid Howells 	if (!call)
949260a9803SDavid Howells 		return -ENOMEM;
950260a9803SDavid Howells 
951260a9803SDavid Howells 	call->key = key;
952260a9803SDavid Howells 	call->reply = vnode;
953260a9803SDavid Howells 	call->reply2 = newfid;
954260a9803SDavid Howells 	call->reply3 = newstatus;
955260a9803SDavid Howells 	call->service_id = FS_SERVICE;
956260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
957260a9803SDavid Howells 
958260a9803SDavid Howells 	/* marshall the parameters */
959260a9803SDavid Howells 	bp = call->request;
960260a9803SDavid Howells 	*bp++ = htonl(FSSYMLINK);
961260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
962260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
963260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
964260a9803SDavid Howells 	*bp++ = htonl(namesz);
965260a9803SDavid Howells 	memcpy(bp, name, namesz);
966260a9803SDavid Howells 	bp = (void *) bp + namesz;
967260a9803SDavid Howells 	if (padsz > 0) {
968260a9803SDavid Howells 		memset(bp, 0, padsz);
969260a9803SDavid Howells 		bp = (void *) bp + padsz;
970260a9803SDavid Howells 	}
971260a9803SDavid Howells 	*bp++ = htonl(c_namesz);
972260a9803SDavid Howells 	memcpy(bp, contents, c_namesz);
973260a9803SDavid Howells 	bp = (void *) bp + c_namesz;
974260a9803SDavid Howells 	if (c_padsz > 0) {
975260a9803SDavid Howells 		memset(bp, 0, c_padsz);
976260a9803SDavid Howells 		bp = (void *) bp + c_padsz;
977260a9803SDavid Howells 	}
978260a9803SDavid Howells 	*bp++ = htonl(AFS_SET_MODE);
979260a9803SDavid Howells 	*bp++ = 0; /* mtime */
980260a9803SDavid Howells 	*bp++ = 0; /* owner */
981260a9803SDavid Howells 	*bp++ = 0; /* group */
982260a9803SDavid Howells 	*bp++ = htonl(S_IRWXUGO); /* unix mode */
983260a9803SDavid Howells 	*bp++ = 0; /* segment size */
984260a9803SDavid Howells 
985260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
986260a9803SDavid Howells }
987260a9803SDavid Howells 
988260a9803SDavid Howells /*
989260a9803SDavid Howells  * deliver reply data to an FS.Rename
990260a9803SDavid Howells  */
991260a9803SDavid Howells static int afs_deliver_fs_rename(struct afs_call *call,
992260a9803SDavid Howells 				  struct sk_buff *skb, bool last)
993260a9803SDavid Howells {
994260a9803SDavid Howells 	struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2;
995260a9803SDavid Howells 	const __be32 *bp;
996260a9803SDavid Howells 
997260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
998260a9803SDavid Howells 
999260a9803SDavid Howells 	afs_transfer_reply(call, skb);
1000260a9803SDavid Howells 	if (!last)
1001260a9803SDavid Howells 		return 0;
1002260a9803SDavid Howells 
1003260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
1004260a9803SDavid Howells 		return -EBADMSG;
1005260a9803SDavid Howells 
1006260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
1007260a9803SDavid Howells 	bp = call->buffer;
100831143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode, NULL);
1009260a9803SDavid Howells 	if (new_dvnode != orig_dvnode)
101031143d5dSDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode,
101131143d5dSDavid Howells 					  NULL);
1012260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
1013260a9803SDavid Howells 
1014260a9803SDavid Howells 	_leave(" = 0 [done]");
1015260a9803SDavid Howells 	return 0;
1016260a9803SDavid Howells }
1017260a9803SDavid Howells 
1018260a9803SDavid Howells /*
1019260a9803SDavid Howells  * FS.Rename operation type
1020260a9803SDavid Howells  */
1021260a9803SDavid Howells static const struct afs_call_type afs_RXFSRename = {
1022260a9803SDavid Howells 	.name		= "FS.Rename",
1023260a9803SDavid Howells 	.deliver	= afs_deliver_fs_rename,
1024260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1025260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
1026260a9803SDavid Howells };
1027260a9803SDavid Howells 
1028260a9803SDavid Howells /*
1029260a9803SDavid Howells  * create a symbolic link
1030260a9803SDavid Howells  */
1031260a9803SDavid Howells int afs_fs_rename(struct afs_server *server,
1032260a9803SDavid Howells 		  struct key *key,
1033260a9803SDavid Howells 		  struct afs_vnode *orig_dvnode,
1034260a9803SDavid Howells 		  const char *orig_name,
1035260a9803SDavid Howells 		  struct afs_vnode *new_dvnode,
1036260a9803SDavid Howells 		  const char *new_name,
1037260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
1038260a9803SDavid Howells {
1039260a9803SDavid Howells 	struct afs_call *call;
1040260a9803SDavid Howells 	size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
1041260a9803SDavid Howells 	__be32 *bp;
1042260a9803SDavid Howells 
1043260a9803SDavid Howells 	_enter("");
1044260a9803SDavid Howells 
1045260a9803SDavid Howells 	o_namesz = strlen(orig_name);
1046260a9803SDavid Howells 	o_padsz = (4 - (o_namesz & 3)) & 3;
1047260a9803SDavid Howells 
1048260a9803SDavid Howells 	n_namesz = strlen(new_name);
1049260a9803SDavid Howells 	n_padsz = (4 - (n_namesz & 3)) & 3;
1050260a9803SDavid Howells 
1051260a9803SDavid Howells 	reqsz = (4 * 4) +
1052260a9803SDavid Howells 		4 + o_namesz + o_padsz +
1053260a9803SDavid Howells 		(3 * 4) +
1054260a9803SDavid Howells 		4 + n_namesz + n_padsz;
1055260a9803SDavid Howells 
1056260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
1057260a9803SDavid Howells 	if (!call)
1058260a9803SDavid Howells 		return -ENOMEM;
1059260a9803SDavid Howells 
1060260a9803SDavid Howells 	call->key = key;
1061260a9803SDavid Howells 	call->reply = orig_dvnode;
1062260a9803SDavid Howells 	call->reply2 = new_dvnode;
1063260a9803SDavid Howells 	call->service_id = FS_SERVICE;
1064260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
1065260a9803SDavid Howells 
1066260a9803SDavid Howells 	/* marshall the parameters */
1067260a9803SDavid Howells 	bp = call->request;
1068260a9803SDavid Howells 	*bp++ = htonl(FSRENAME);
1069260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vid);
1070260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vnode);
1071260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.unique);
1072260a9803SDavid Howells 	*bp++ = htonl(o_namesz);
1073260a9803SDavid Howells 	memcpy(bp, orig_name, o_namesz);
1074260a9803SDavid Howells 	bp = (void *) bp + o_namesz;
1075260a9803SDavid Howells 	if (o_padsz > 0) {
1076260a9803SDavid Howells 		memset(bp, 0, o_padsz);
1077260a9803SDavid Howells 		bp = (void *) bp + o_padsz;
1078260a9803SDavid Howells 	}
1079260a9803SDavid Howells 
1080260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vid);
1081260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vnode);
1082260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.unique);
1083260a9803SDavid Howells 	*bp++ = htonl(n_namesz);
1084260a9803SDavid Howells 	memcpy(bp, new_name, n_namesz);
1085260a9803SDavid Howells 	bp = (void *) bp + n_namesz;
1086260a9803SDavid Howells 	if (n_padsz > 0) {
1087260a9803SDavid Howells 		memset(bp, 0, n_padsz);
1088260a9803SDavid Howells 		bp = (void *) bp + n_padsz;
1089260a9803SDavid Howells 	}
1090260a9803SDavid Howells 
1091260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
1092260a9803SDavid Howells }
109331143d5dSDavid Howells 
109431143d5dSDavid Howells /*
109531143d5dSDavid Howells  * deliver reply data to an FS.StoreData
109631143d5dSDavid Howells  */
109731143d5dSDavid Howells static int afs_deliver_fs_store_data(struct afs_call *call,
109831143d5dSDavid Howells 				     struct sk_buff *skb, bool last)
109931143d5dSDavid Howells {
110031143d5dSDavid Howells 	struct afs_vnode *vnode = call->reply;
110131143d5dSDavid Howells 	const __be32 *bp;
110231143d5dSDavid Howells 
110331143d5dSDavid Howells 	_enter(",,%u", last);
110431143d5dSDavid Howells 
110531143d5dSDavid Howells 	afs_transfer_reply(call, skb);
110631143d5dSDavid Howells 	if (!last) {
110731143d5dSDavid Howells 		_leave(" = 0 [more]");
110831143d5dSDavid Howells 		return 0;
110931143d5dSDavid Howells 	}
111031143d5dSDavid Howells 
111131143d5dSDavid Howells 	if (call->reply_size != call->reply_max) {
111231143d5dSDavid Howells 		_leave(" = -EBADMSG [%u != %u]",
111331143d5dSDavid Howells 		       call->reply_size, call->reply_max);
111431143d5dSDavid Howells 		return -EBADMSG;
111531143d5dSDavid Howells 	}
111631143d5dSDavid Howells 
111731143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
111831143d5dSDavid Howells 	bp = call->buffer;
111931143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode,
112031143d5dSDavid Howells 				  &call->store_version);
112131143d5dSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
112231143d5dSDavid Howells 
112331143d5dSDavid Howells 	afs_pages_written_back(vnode, call);
112431143d5dSDavid Howells 
112531143d5dSDavid Howells 	_leave(" = 0 [done]");
112631143d5dSDavid Howells 	return 0;
112731143d5dSDavid Howells }
112831143d5dSDavid Howells 
112931143d5dSDavid Howells /*
113031143d5dSDavid Howells  * FS.StoreData operation type
113131143d5dSDavid Howells  */
113231143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData = {
113331143d5dSDavid Howells 	.name		= "FS.StoreData",
113431143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_data,
113531143d5dSDavid Howells 	.abort_to_error	= afs_abort_to_error,
113631143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
113731143d5dSDavid Howells };
113831143d5dSDavid Howells 
1139b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64 = {
1140b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1141b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_data,
1142b9b1f8d5SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1143b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1144b9b1f8d5SDavid Howells };
1145b9b1f8d5SDavid Howells 
1146b9b1f8d5SDavid Howells /*
1147b9b1f8d5SDavid Howells  * store a set of pages to a very large file
1148b9b1f8d5SDavid Howells  */
1149b9b1f8d5SDavid Howells static int afs_fs_store_data64(struct afs_server *server,
1150b9b1f8d5SDavid Howells 			       struct afs_writeback *wb,
1151b9b1f8d5SDavid Howells 			       pgoff_t first, pgoff_t last,
1152b9b1f8d5SDavid Howells 			       unsigned offset, unsigned to,
1153b9b1f8d5SDavid Howells 			       loff_t size, loff_t pos, loff_t i_size,
1154b9b1f8d5SDavid Howells 			       const struct afs_wait_mode *wait_mode)
1155b9b1f8d5SDavid Howells {
1156b9b1f8d5SDavid Howells 	struct afs_vnode *vnode = wb->vnode;
1157b9b1f8d5SDavid Howells 	struct afs_call *call;
1158b9b1f8d5SDavid Howells 	__be32 *bp;
1159b9b1f8d5SDavid Howells 
1160b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
1161b9b1f8d5SDavid Howells 	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
1162b9b1f8d5SDavid Howells 
1163b9b1f8d5SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSStoreData64,
1164b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1165b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1166b9b1f8d5SDavid Howells 	if (!call)
1167b9b1f8d5SDavid Howells 		return -ENOMEM;
1168b9b1f8d5SDavid Howells 
1169b9b1f8d5SDavid Howells 	call->wb = wb;
1170b9b1f8d5SDavid Howells 	call->key = wb->key;
1171b9b1f8d5SDavid Howells 	call->reply = vnode;
1172b9b1f8d5SDavid Howells 	call->service_id = FS_SERVICE;
1173b9b1f8d5SDavid Howells 	call->port = htons(AFS_FS_PORT);
1174b9b1f8d5SDavid Howells 	call->mapping = vnode->vfs_inode.i_mapping;
1175b9b1f8d5SDavid Howells 	call->first = first;
1176b9b1f8d5SDavid Howells 	call->last = last;
1177b9b1f8d5SDavid Howells 	call->first_offset = offset;
1178b9b1f8d5SDavid Howells 	call->last_to = to;
1179b9b1f8d5SDavid Howells 	call->send_pages = true;
1180b9b1f8d5SDavid Howells 	call->store_version = vnode->status.data_version + 1;
1181b9b1f8d5SDavid Howells 
1182b9b1f8d5SDavid Howells 	/* marshall the parameters */
1183b9b1f8d5SDavid Howells 	bp = call->request;
1184b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1185b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1186b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1187b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1188b9b1f8d5SDavid Howells 
1189b9b1f8d5SDavid Howells 	*bp++ = 0; /* mask */
1190b9b1f8d5SDavid Howells 	*bp++ = 0; /* mtime */
1191b9b1f8d5SDavid Howells 	*bp++ = 0; /* owner */
1192b9b1f8d5SDavid Howells 	*bp++ = 0; /* group */
1193b9b1f8d5SDavid Howells 	*bp++ = 0; /* unix mode */
1194b9b1f8d5SDavid Howells 	*bp++ = 0; /* segment size */
1195b9b1f8d5SDavid Howells 
1196b9b1f8d5SDavid Howells 	*bp++ = htonl(pos >> 32);
1197b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) pos);
1198b9b1f8d5SDavid Howells 	*bp++ = htonl(size >> 32);
1199b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) size);
1200b9b1f8d5SDavid Howells 	*bp++ = htonl(i_size >> 32);
1201b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) i_size);
1202b9b1f8d5SDavid Howells 
1203b9b1f8d5SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
1204b9b1f8d5SDavid Howells }
1205b9b1f8d5SDavid Howells 
120631143d5dSDavid Howells /*
120731143d5dSDavid Howells  * store a set of pages
120831143d5dSDavid Howells  */
120931143d5dSDavid Howells int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
121031143d5dSDavid Howells 		      pgoff_t first, pgoff_t last,
121131143d5dSDavid Howells 		      unsigned offset, unsigned to,
121231143d5dSDavid Howells 		      const struct afs_wait_mode *wait_mode)
121331143d5dSDavid Howells {
121431143d5dSDavid Howells 	struct afs_vnode *vnode = wb->vnode;
121531143d5dSDavid Howells 	struct afs_call *call;
121631143d5dSDavid Howells 	loff_t size, pos, i_size;
121731143d5dSDavid Howells 	__be32 *bp;
121831143d5dSDavid Howells 
121931143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
122031143d5dSDavid Howells 	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
122131143d5dSDavid Howells 
122231143d5dSDavid Howells 	size = to - offset;
122331143d5dSDavid Howells 	if (first != last)
122431143d5dSDavid Howells 		size += (loff_t)(last - first) << PAGE_SHIFT;
122531143d5dSDavid Howells 	pos = (loff_t)first << PAGE_SHIFT;
122631143d5dSDavid Howells 	pos += offset;
122731143d5dSDavid Howells 
122831143d5dSDavid Howells 	i_size = i_size_read(&vnode->vfs_inode);
122931143d5dSDavid Howells 	if (pos + size > i_size)
123031143d5dSDavid Howells 		i_size = size + pos;
123131143d5dSDavid Howells 
123231143d5dSDavid Howells 	_debug("size %llx, at %llx, i_size %llx",
123331143d5dSDavid Howells 	       (unsigned long long) size, (unsigned long long) pos,
123431143d5dSDavid Howells 	       (unsigned long long) i_size);
123531143d5dSDavid Howells 
1236b9b1f8d5SDavid Howells 	if (pos >> 32 || i_size >> 32 || size >> 32 || (pos + size) >> 32)
1237b9b1f8d5SDavid Howells 		return afs_fs_store_data64(server, wb, first, last, offset, to,
1238b9b1f8d5SDavid Howells 					   size, pos, i_size, wait_mode);
123931143d5dSDavid Howells 
124031143d5dSDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSStoreData,
124131143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
124231143d5dSDavid Howells 				   (21 + 6) * 4);
124331143d5dSDavid Howells 	if (!call)
124431143d5dSDavid Howells 		return -ENOMEM;
124531143d5dSDavid Howells 
124631143d5dSDavid Howells 	call->wb = wb;
124731143d5dSDavid Howells 	call->key = wb->key;
124831143d5dSDavid Howells 	call->reply = vnode;
124931143d5dSDavid Howells 	call->service_id = FS_SERVICE;
125031143d5dSDavid Howells 	call->port = htons(AFS_FS_PORT);
125131143d5dSDavid Howells 	call->mapping = vnode->vfs_inode.i_mapping;
125231143d5dSDavid Howells 	call->first = first;
125331143d5dSDavid Howells 	call->last = last;
125431143d5dSDavid Howells 	call->first_offset = offset;
125531143d5dSDavid Howells 	call->last_to = to;
125631143d5dSDavid Howells 	call->send_pages = true;
125731143d5dSDavid Howells 	call->store_version = vnode->status.data_version + 1;
125831143d5dSDavid Howells 
125931143d5dSDavid Howells 	/* marshall the parameters */
126031143d5dSDavid Howells 	bp = call->request;
126131143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
126231143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
126331143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
126431143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
126531143d5dSDavid Howells 
126631143d5dSDavid Howells 	*bp++ = 0; /* mask */
126731143d5dSDavid Howells 	*bp++ = 0; /* mtime */
126831143d5dSDavid Howells 	*bp++ = 0; /* owner */
126931143d5dSDavid Howells 	*bp++ = 0; /* group */
127031143d5dSDavid Howells 	*bp++ = 0; /* unix mode */
127131143d5dSDavid Howells 	*bp++ = 0; /* segment size */
127231143d5dSDavid Howells 
127331143d5dSDavid Howells 	*bp++ = htonl(pos);
127431143d5dSDavid Howells 	*bp++ = htonl(size);
127531143d5dSDavid Howells 	*bp++ = htonl(i_size);
127631143d5dSDavid Howells 
127731143d5dSDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
127831143d5dSDavid Howells }
127931143d5dSDavid Howells 
128031143d5dSDavid Howells /*
128131143d5dSDavid Howells  * deliver reply data to an FS.StoreStatus
128231143d5dSDavid Howells  */
128331143d5dSDavid Howells static int afs_deliver_fs_store_status(struct afs_call *call,
128431143d5dSDavid Howells 				       struct sk_buff *skb, bool last)
128531143d5dSDavid Howells {
128631143d5dSDavid Howells 	afs_dataversion_t *store_version;
128731143d5dSDavid Howells 	struct afs_vnode *vnode = call->reply;
128831143d5dSDavid Howells 	const __be32 *bp;
128931143d5dSDavid Howells 
129031143d5dSDavid Howells 	_enter(",,%u", last);
129131143d5dSDavid Howells 
129231143d5dSDavid Howells 	afs_transfer_reply(call, skb);
129331143d5dSDavid Howells 	if (!last) {
129431143d5dSDavid Howells 		_leave(" = 0 [more]");
129531143d5dSDavid Howells 		return 0;
129631143d5dSDavid Howells 	}
129731143d5dSDavid Howells 
129831143d5dSDavid Howells 	if (call->reply_size != call->reply_max) {
129931143d5dSDavid Howells 		_leave(" = -EBADMSG [%u != %u]",
130031143d5dSDavid Howells 		       call->reply_size, call->reply_max);
130131143d5dSDavid Howells 		return -EBADMSG;
130231143d5dSDavid Howells 	}
130331143d5dSDavid Howells 
130431143d5dSDavid Howells 	/* unmarshall the reply once we've received all of it */
130531143d5dSDavid Howells 	store_version = NULL;
130631143d5dSDavid Howells 	if (call->operation_ID == FSSTOREDATA)
130731143d5dSDavid Howells 		store_version = &call->store_version;
130831143d5dSDavid Howells 
130931143d5dSDavid Howells 	bp = call->buffer;
131031143d5dSDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, store_version);
131131143d5dSDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
131231143d5dSDavid Howells 
131331143d5dSDavid Howells 	_leave(" = 0 [done]");
131431143d5dSDavid Howells 	return 0;
131531143d5dSDavid Howells }
131631143d5dSDavid Howells 
131731143d5dSDavid Howells /*
131831143d5dSDavid Howells  * FS.StoreStatus operation type
131931143d5dSDavid Howells  */
132031143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreStatus = {
132131143d5dSDavid Howells 	.name		= "FS.StoreStatus",
132231143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
132331143d5dSDavid Howells 	.abort_to_error	= afs_abort_to_error,
132431143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
132531143d5dSDavid Howells };
132631143d5dSDavid Howells 
132731143d5dSDavid Howells static const struct afs_call_type afs_RXFSStoreData_as_Status = {
132831143d5dSDavid Howells 	.name		= "FS.StoreData",
132931143d5dSDavid Howells 	.deliver	= afs_deliver_fs_store_status,
133031143d5dSDavid Howells 	.abort_to_error	= afs_abort_to_error,
133131143d5dSDavid Howells 	.destructor	= afs_flat_call_destructor,
133231143d5dSDavid Howells };
133331143d5dSDavid Howells 
1334b9b1f8d5SDavid Howells static const struct afs_call_type afs_RXFSStoreData64_as_Status = {
1335b9b1f8d5SDavid Howells 	.name		= "FS.StoreData64",
1336b9b1f8d5SDavid Howells 	.deliver	= afs_deliver_fs_store_status,
1337b9b1f8d5SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1338b9b1f8d5SDavid Howells 	.destructor	= afs_flat_call_destructor,
1339b9b1f8d5SDavid Howells };
1340b9b1f8d5SDavid Howells 
1341b9b1f8d5SDavid Howells /*
1342b9b1f8d5SDavid Howells  * set the attributes on a very large file, using FS.StoreData rather than
1343b9b1f8d5SDavid Howells  * FS.StoreStatus so as to alter the file size also
1344b9b1f8d5SDavid Howells  */
1345b9b1f8d5SDavid Howells static int afs_fs_setattr_size64(struct afs_server *server, struct key *key,
1346b9b1f8d5SDavid Howells 				 struct afs_vnode *vnode, struct iattr *attr,
1347b9b1f8d5SDavid Howells 				 const struct afs_wait_mode *wait_mode)
1348b9b1f8d5SDavid Howells {
1349b9b1f8d5SDavid Howells 	struct afs_call *call;
1350b9b1f8d5SDavid Howells 	__be32 *bp;
1351b9b1f8d5SDavid Howells 
1352b9b1f8d5SDavid Howells 	_enter(",%x,{%x:%u},,",
1353b9b1f8d5SDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
1354b9b1f8d5SDavid Howells 
1355b9b1f8d5SDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1356b9b1f8d5SDavid Howells 
1357b9b1f8d5SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSStoreData64_as_Status,
1358b9b1f8d5SDavid Howells 				   (4 + 6 + 3 * 2) * 4,
1359b9b1f8d5SDavid Howells 				   (21 + 6) * 4);
1360b9b1f8d5SDavid Howells 	if (!call)
1361b9b1f8d5SDavid Howells 		return -ENOMEM;
1362b9b1f8d5SDavid Howells 
1363b9b1f8d5SDavid Howells 	call->key = key;
1364b9b1f8d5SDavid Howells 	call->reply = vnode;
1365b9b1f8d5SDavid Howells 	call->service_id = FS_SERVICE;
1366b9b1f8d5SDavid Howells 	call->port = htons(AFS_FS_PORT);
1367b9b1f8d5SDavid Howells 	call->store_version = vnode->status.data_version + 1;
1368b9b1f8d5SDavid Howells 	call->operation_ID = FSSTOREDATA;
1369b9b1f8d5SDavid Howells 
1370b9b1f8d5SDavid Howells 	/* marshall the parameters */
1371b9b1f8d5SDavid Howells 	bp = call->request;
1372b9b1f8d5SDavid Howells 	*bp++ = htonl(FSSTOREDATA64);
1373b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1374b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1375b9b1f8d5SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1376b9b1f8d5SDavid Howells 
1377b9b1f8d5SDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
1378b9b1f8d5SDavid Howells 
1379b9b1f8d5SDavid Howells 	*bp++ = 0;				/* position of start of write */
1380b9b1f8d5SDavid Howells 	*bp++ = 0;
1381b9b1f8d5SDavid Howells 	*bp++ = 0;				/* size of write */
1382b9b1f8d5SDavid Howells 	*bp++ = 0;
1383b9b1f8d5SDavid Howells 	*bp++ = htonl(attr->ia_size >> 32);	/* new file length */
1384b9b1f8d5SDavid Howells 	*bp++ = htonl((u32) attr->ia_size);
1385b9b1f8d5SDavid Howells 
1386b9b1f8d5SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
1387b9b1f8d5SDavid Howells }
1388b9b1f8d5SDavid Howells 
138931143d5dSDavid Howells /*
139031143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus
139131143d5dSDavid Howells  * so as to alter the file size also
139231143d5dSDavid Howells  */
139331143d5dSDavid Howells static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
139431143d5dSDavid Howells 			       struct afs_vnode *vnode, struct iattr *attr,
139531143d5dSDavid Howells 			       const struct afs_wait_mode *wait_mode)
139631143d5dSDavid Howells {
139731143d5dSDavid Howells 	struct afs_call *call;
139831143d5dSDavid Howells 	__be32 *bp;
139931143d5dSDavid Howells 
140031143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
140131143d5dSDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
140231143d5dSDavid Howells 
140331143d5dSDavid Howells 	ASSERT(attr->ia_valid & ATTR_SIZE);
1404b9b1f8d5SDavid Howells 	if (attr->ia_size >> 32)
1405b9b1f8d5SDavid Howells 		return afs_fs_setattr_size64(server, key, vnode, attr,
1406b9b1f8d5SDavid Howells 					     wait_mode);
140731143d5dSDavid Howells 
140831143d5dSDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSStoreData_as_Status,
140931143d5dSDavid Howells 				   (4 + 6 + 3) * 4,
141031143d5dSDavid Howells 				   (21 + 6) * 4);
141131143d5dSDavid Howells 	if (!call)
141231143d5dSDavid Howells 		return -ENOMEM;
141331143d5dSDavid Howells 
141431143d5dSDavid Howells 	call->key = key;
141531143d5dSDavid Howells 	call->reply = vnode;
141631143d5dSDavid Howells 	call->service_id = FS_SERVICE;
141731143d5dSDavid Howells 	call->port = htons(AFS_FS_PORT);
141831143d5dSDavid Howells 	call->store_version = vnode->status.data_version + 1;
141931143d5dSDavid Howells 	call->operation_ID = FSSTOREDATA;
142031143d5dSDavid Howells 
142131143d5dSDavid Howells 	/* marshall the parameters */
142231143d5dSDavid Howells 	bp = call->request;
142331143d5dSDavid Howells 	*bp++ = htonl(FSSTOREDATA);
142431143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
142531143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
142631143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
142731143d5dSDavid Howells 
142831143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
142931143d5dSDavid Howells 
143031143d5dSDavid Howells 	*bp++ = 0;				/* position of start of write */
143131143d5dSDavid Howells 	*bp++ = 0;				/* size of write */
143231143d5dSDavid Howells 	*bp++ = htonl(attr->ia_size);		/* new file length */
143331143d5dSDavid Howells 
143431143d5dSDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
143531143d5dSDavid Howells }
143631143d5dSDavid Howells 
143731143d5dSDavid Howells /*
143831143d5dSDavid Howells  * set the attributes on a file, using FS.StoreData if there's a change in file
143931143d5dSDavid Howells  * size, and FS.StoreStatus otherwise
144031143d5dSDavid Howells  */
144131143d5dSDavid Howells int afs_fs_setattr(struct afs_server *server, struct key *key,
144231143d5dSDavid Howells 		   struct afs_vnode *vnode, struct iattr *attr,
144331143d5dSDavid Howells 		   const struct afs_wait_mode *wait_mode)
144431143d5dSDavid Howells {
144531143d5dSDavid Howells 	struct afs_call *call;
144631143d5dSDavid Howells 	__be32 *bp;
144731143d5dSDavid Howells 
144831143d5dSDavid Howells 	if (attr->ia_valid & ATTR_SIZE)
144931143d5dSDavid Howells 		return afs_fs_setattr_size(server, key, vnode, attr,
145031143d5dSDavid Howells 					   wait_mode);
145131143d5dSDavid Howells 
145231143d5dSDavid Howells 	_enter(",%x,{%x:%u},,",
145331143d5dSDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
145431143d5dSDavid Howells 
145531143d5dSDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSStoreStatus,
145631143d5dSDavid Howells 				   (4 + 6) * 4,
145731143d5dSDavid Howells 				   (21 + 6) * 4);
145831143d5dSDavid Howells 	if (!call)
145931143d5dSDavid Howells 		return -ENOMEM;
146031143d5dSDavid Howells 
146131143d5dSDavid Howells 	call->key = key;
146231143d5dSDavid Howells 	call->reply = vnode;
146331143d5dSDavid Howells 	call->service_id = FS_SERVICE;
146431143d5dSDavid Howells 	call->port = htons(AFS_FS_PORT);
146531143d5dSDavid Howells 	call->operation_ID = FSSTORESTATUS;
146631143d5dSDavid Howells 
146731143d5dSDavid Howells 	/* marshall the parameters */
146831143d5dSDavid Howells 	bp = call->request;
146931143d5dSDavid Howells 	*bp++ = htonl(FSSTORESTATUS);
147031143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vid);
147131143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
147231143d5dSDavid Howells 	*bp++ = htonl(vnode->fid.unique);
147331143d5dSDavid Howells 
147431143d5dSDavid Howells 	xdr_encode_AFS_StoreStatus(&bp, attr);
147531143d5dSDavid Howells 
147631143d5dSDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
147731143d5dSDavid Howells }
147845222b9eSDavid Howells 
147945222b9eSDavid Howells /*
148045222b9eSDavid Howells  * deliver reply data to an FS.GetVolumeStatus
148145222b9eSDavid Howells  */
148245222b9eSDavid Howells static int afs_deliver_fs_get_volume_status(struct afs_call *call,
148345222b9eSDavid Howells 					    struct sk_buff *skb, bool last)
148445222b9eSDavid Howells {
148545222b9eSDavid Howells 	const __be32 *bp;
148645222b9eSDavid Howells 	char *p;
148745222b9eSDavid Howells 	int ret;
148845222b9eSDavid Howells 
148945222b9eSDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
149045222b9eSDavid Howells 
149145222b9eSDavid Howells 	switch (call->unmarshall) {
149245222b9eSDavid Howells 	case 0:
149345222b9eSDavid Howells 		call->offset = 0;
149445222b9eSDavid Howells 		call->unmarshall++;
149545222b9eSDavid Howells 
149645222b9eSDavid Howells 		/* extract the returned status record */
149745222b9eSDavid Howells 	case 1:
149845222b9eSDavid Howells 		_debug("extract status");
149945222b9eSDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer,
150045222b9eSDavid Howells 				       12 * 4);
150145222b9eSDavid Howells 		switch (ret) {
150245222b9eSDavid Howells 		case 0:		break;
150345222b9eSDavid Howells 		case -EAGAIN:	return 0;
150445222b9eSDavid Howells 		default:	return ret;
150545222b9eSDavid Howells 		}
150645222b9eSDavid Howells 
150745222b9eSDavid Howells 		bp = call->buffer;
150845222b9eSDavid Howells 		xdr_decode_AFSFetchVolumeStatus(&bp, call->reply2);
150945222b9eSDavid Howells 		call->offset = 0;
151045222b9eSDavid Howells 		call->unmarshall++;
151145222b9eSDavid Howells 
151245222b9eSDavid Howells 		/* extract the volume name length */
151345222b9eSDavid Howells 	case 2:
151445222b9eSDavid Howells 		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
151545222b9eSDavid Howells 		switch (ret) {
151645222b9eSDavid Howells 		case 0:		break;
151745222b9eSDavid Howells 		case -EAGAIN:	return 0;
151845222b9eSDavid Howells 		default:	return ret;
151945222b9eSDavid Howells 		}
152045222b9eSDavid Howells 
152145222b9eSDavid Howells 		call->count = ntohl(call->tmp);
152245222b9eSDavid Howells 		_debug("volname length: %u", call->count);
152345222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
152445222b9eSDavid Howells 			return -EBADMSG;
152545222b9eSDavid Howells 		call->offset = 0;
152645222b9eSDavid Howells 		call->unmarshall++;
152745222b9eSDavid Howells 
152845222b9eSDavid Howells 		/* extract the volume name */
152945222b9eSDavid Howells 	case 3:
153045222b9eSDavid Howells 		_debug("extract volname");
153145222b9eSDavid Howells 		if (call->count > 0) {
153245222b9eSDavid Howells 			ret = afs_extract_data(call, skb, last, call->reply3,
153345222b9eSDavid Howells 					       call->count);
153445222b9eSDavid Howells 			switch (ret) {
153545222b9eSDavid Howells 			case 0:		break;
153645222b9eSDavid Howells 			case -EAGAIN:	return 0;
153745222b9eSDavid Howells 			default:	return ret;
153845222b9eSDavid Howells 			}
153945222b9eSDavid Howells 		}
154045222b9eSDavid Howells 
154145222b9eSDavid Howells 		p = call->reply3;
154245222b9eSDavid Howells 		p[call->count] = 0;
154345222b9eSDavid Howells 		_debug("volname '%s'", p);
154445222b9eSDavid Howells 
154545222b9eSDavid Howells 		call->offset = 0;
154645222b9eSDavid Howells 		call->unmarshall++;
154745222b9eSDavid Howells 
154845222b9eSDavid Howells 		/* extract the volume name padding */
154945222b9eSDavid Howells 		if ((call->count & 3) == 0) {
155045222b9eSDavid Howells 			call->unmarshall++;
155145222b9eSDavid Howells 			goto no_volname_padding;
155245222b9eSDavid Howells 		}
155345222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
155445222b9eSDavid Howells 
155545222b9eSDavid Howells 	case 4:
155645222b9eSDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer,
155745222b9eSDavid Howells 				       call->count);
155845222b9eSDavid Howells 		switch (ret) {
155945222b9eSDavid Howells 		case 0:		break;
156045222b9eSDavid Howells 		case -EAGAIN:	return 0;
156145222b9eSDavid Howells 		default:	return ret;
156245222b9eSDavid Howells 		}
156345222b9eSDavid Howells 
156445222b9eSDavid Howells 		call->offset = 0;
156545222b9eSDavid Howells 		call->unmarshall++;
156645222b9eSDavid Howells 	no_volname_padding:
156745222b9eSDavid Howells 
156845222b9eSDavid Howells 		/* extract the offline message length */
156945222b9eSDavid Howells 	case 5:
157045222b9eSDavid Howells 		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
157145222b9eSDavid Howells 		switch (ret) {
157245222b9eSDavid Howells 		case 0:		break;
157345222b9eSDavid Howells 		case -EAGAIN:	return 0;
157445222b9eSDavid Howells 		default:	return ret;
157545222b9eSDavid Howells 		}
157645222b9eSDavid Howells 
157745222b9eSDavid Howells 		call->count = ntohl(call->tmp);
157845222b9eSDavid Howells 		_debug("offline msg length: %u", call->count);
157945222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
158045222b9eSDavid Howells 			return -EBADMSG;
158145222b9eSDavid Howells 		call->offset = 0;
158245222b9eSDavid Howells 		call->unmarshall++;
158345222b9eSDavid Howells 
158445222b9eSDavid Howells 		/* extract the offline message */
158545222b9eSDavid Howells 	case 6:
158645222b9eSDavid Howells 		_debug("extract offline");
158745222b9eSDavid Howells 		if (call->count > 0) {
158845222b9eSDavid Howells 			ret = afs_extract_data(call, skb, last, call->reply3,
158945222b9eSDavid Howells 					       call->count);
159045222b9eSDavid Howells 			switch (ret) {
159145222b9eSDavid Howells 			case 0:		break;
159245222b9eSDavid Howells 			case -EAGAIN:	return 0;
159345222b9eSDavid Howells 			default:	return ret;
159445222b9eSDavid Howells 			}
159545222b9eSDavid Howells 		}
159645222b9eSDavid Howells 
159745222b9eSDavid Howells 		p = call->reply3;
159845222b9eSDavid Howells 		p[call->count] = 0;
159945222b9eSDavid Howells 		_debug("offline '%s'", p);
160045222b9eSDavid Howells 
160145222b9eSDavid Howells 		call->offset = 0;
160245222b9eSDavid Howells 		call->unmarshall++;
160345222b9eSDavid Howells 
160445222b9eSDavid Howells 		/* extract the offline message padding */
160545222b9eSDavid Howells 		if ((call->count & 3) == 0) {
160645222b9eSDavid Howells 			call->unmarshall++;
160745222b9eSDavid Howells 			goto no_offline_padding;
160845222b9eSDavid Howells 		}
160945222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
161045222b9eSDavid Howells 
161145222b9eSDavid Howells 	case 7:
161245222b9eSDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer,
161345222b9eSDavid Howells 				       call->count);
161445222b9eSDavid Howells 		switch (ret) {
161545222b9eSDavid Howells 		case 0:		break;
161645222b9eSDavid Howells 		case -EAGAIN:	return 0;
161745222b9eSDavid Howells 		default:	return ret;
161845222b9eSDavid Howells 		}
161945222b9eSDavid Howells 
162045222b9eSDavid Howells 		call->offset = 0;
162145222b9eSDavid Howells 		call->unmarshall++;
162245222b9eSDavid Howells 	no_offline_padding:
162345222b9eSDavid Howells 
162445222b9eSDavid Howells 		/* extract the message of the day length */
162545222b9eSDavid Howells 	case 8:
162645222b9eSDavid Howells 		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
162745222b9eSDavid Howells 		switch (ret) {
162845222b9eSDavid Howells 		case 0:		break;
162945222b9eSDavid Howells 		case -EAGAIN:	return 0;
163045222b9eSDavid Howells 		default:	return ret;
163145222b9eSDavid Howells 		}
163245222b9eSDavid Howells 
163345222b9eSDavid Howells 		call->count = ntohl(call->tmp);
163445222b9eSDavid Howells 		_debug("motd length: %u", call->count);
163545222b9eSDavid Howells 		if (call->count >= AFSNAMEMAX)
163645222b9eSDavid Howells 			return -EBADMSG;
163745222b9eSDavid Howells 		call->offset = 0;
163845222b9eSDavid Howells 		call->unmarshall++;
163945222b9eSDavid Howells 
164045222b9eSDavid Howells 		/* extract the message of the day */
164145222b9eSDavid Howells 	case 9:
164245222b9eSDavid Howells 		_debug("extract motd");
164345222b9eSDavid Howells 		if (call->count > 0) {
164445222b9eSDavid Howells 			ret = afs_extract_data(call, skb, last, call->reply3,
164545222b9eSDavid Howells 					       call->count);
164645222b9eSDavid Howells 			switch (ret) {
164745222b9eSDavid Howells 			case 0:		break;
164845222b9eSDavid Howells 			case -EAGAIN:	return 0;
164945222b9eSDavid Howells 			default:	return ret;
165045222b9eSDavid Howells 			}
165145222b9eSDavid Howells 		}
165245222b9eSDavid Howells 
165345222b9eSDavid Howells 		p = call->reply3;
165445222b9eSDavid Howells 		p[call->count] = 0;
165545222b9eSDavid Howells 		_debug("motd '%s'", p);
165645222b9eSDavid Howells 
165745222b9eSDavid Howells 		call->offset = 0;
165845222b9eSDavid Howells 		call->unmarshall++;
165945222b9eSDavid Howells 
166045222b9eSDavid Howells 		/* extract the message of the day padding */
166145222b9eSDavid Howells 		if ((call->count & 3) == 0) {
166245222b9eSDavid Howells 			call->unmarshall++;
166345222b9eSDavid Howells 			goto no_motd_padding;
166445222b9eSDavid Howells 		}
166545222b9eSDavid Howells 		call->count = 4 - (call->count & 3);
166645222b9eSDavid Howells 
166745222b9eSDavid Howells 	case 10:
166845222b9eSDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer,
166945222b9eSDavid Howells 				       call->count);
167045222b9eSDavid Howells 		switch (ret) {
167145222b9eSDavid Howells 		case 0:		break;
167245222b9eSDavid Howells 		case -EAGAIN:	return 0;
167345222b9eSDavid Howells 		default:	return ret;
167445222b9eSDavid Howells 		}
167545222b9eSDavid Howells 
167645222b9eSDavid Howells 		call->offset = 0;
167745222b9eSDavid Howells 		call->unmarshall++;
167845222b9eSDavid Howells 	no_motd_padding:
167945222b9eSDavid Howells 
168045222b9eSDavid Howells 	case 11:
168145222b9eSDavid Howells 		_debug("trailer %d", skb->len);
168245222b9eSDavid Howells 		if (skb->len != 0)
168345222b9eSDavid Howells 			return -EBADMSG;
168445222b9eSDavid Howells 		break;
168545222b9eSDavid Howells 	}
168645222b9eSDavid Howells 
168745222b9eSDavid Howells 	if (!last)
168845222b9eSDavid Howells 		return 0;
168945222b9eSDavid Howells 
169045222b9eSDavid Howells 	_leave(" = 0 [done]");
169145222b9eSDavid Howells 	return 0;
169245222b9eSDavid Howells }
169345222b9eSDavid Howells 
169445222b9eSDavid Howells /*
169545222b9eSDavid Howells  * destroy an FS.GetVolumeStatus call
169645222b9eSDavid Howells  */
169745222b9eSDavid Howells static void afs_get_volume_status_call_destructor(struct afs_call *call)
169845222b9eSDavid Howells {
169945222b9eSDavid Howells 	kfree(call->reply3);
170045222b9eSDavid Howells 	call->reply3 = NULL;
170145222b9eSDavid Howells 	afs_flat_call_destructor(call);
170245222b9eSDavid Howells }
170345222b9eSDavid Howells 
170445222b9eSDavid Howells /*
170545222b9eSDavid Howells  * FS.GetVolumeStatus operation type
170645222b9eSDavid Howells  */
170745222b9eSDavid Howells static const struct afs_call_type afs_RXFSGetVolumeStatus = {
170845222b9eSDavid Howells 	.name		= "FS.GetVolumeStatus",
170945222b9eSDavid Howells 	.deliver	= afs_deliver_fs_get_volume_status,
171045222b9eSDavid Howells 	.abort_to_error	= afs_abort_to_error,
171145222b9eSDavid Howells 	.destructor	= afs_get_volume_status_call_destructor,
171245222b9eSDavid Howells };
171345222b9eSDavid Howells 
171445222b9eSDavid Howells /*
171545222b9eSDavid Howells  * fetch the status of a volume
171645222b9eSDavid Howells  */
171745222b9eSDavid Howells int afs_fs_get_volume_status(struct afs_server *server,
171845222b9eSDavid Howells 			     struct key *key,
171945222b9eSDavid Howells 			     struct afs_vnode *vnode,
172045222b9eSDavid Howells 			     struct afs_volume_status *vs,
172145222b9eSDavid Howells 			     const struct afs_wait_mode *wait_mode)
172245222b9eSDavid Howells {
172345222b9eSDavid Howells 	struct afs_call *call;
172445222b9eSDavid Howells 	__be32 *bp;
172545222b9eSDavid Howells 	void *tmpbuf;
172645222b9eSDavid Howells 
172745222b9eSDavid Howells 	_enter("");
172845222b9eSDavid Howells 
172945222b9eSDavid Howells 	tmpbuf = kmalloc(AFSOPAQUEMAX, GFP_KERNEL);
173045222b9eSDavid Howells 	if (!tmpbuf)
173145222b9eSDavid Howells 		return -ENOMEM;
173245222b9eSDavid Howells 
173345222b9eSDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSGetVolumeStatus, 2 * 4, 12 * 4);
173445222b9eSDavid Howells 	if (!call) {
173545222b9eSDavid Howells 		kfree(tmpbuf);
173645222b9eSDavid Howells 		return -ENOMEM;
173745222b9eSDavid Howells 	}
173845222b9eSDavid Howells 
173945222b9eSDavid Howells 	call->key = key;
174045222b9eSDavid Howells 	call->reply = vnode;
174145222b9eSDavid Howells 	call->reply2 = vs;
174245222b9eSDavid Howells 	call->reply3 = tmpbuf;
174345222b9eSDavid Howells 	call->service_id = FS_SERVICE;
174445222b9eSDavid Howells 	call->port = htons(AFS_FS_PORT);
174545222b9eSDavid Howells 
174645222b9eSDavid Howells 	/* marshall the parameters */
174745222b9eSDavid Howells 	bp = call->request;
174845222b9eSDavid Howells 	bp[0] = htonl(FSGETVOLUMESTATUS);
174945222b9eSDavid Howells 	bp[1] = htonl(vnode->fid.vid);
175045222b9eSDavid Howells 
175145222b9eSDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
175245222b9eSDavid Howells }
1753e8d6c554SDavid Howells 
1754e8d6c554SDavid Howells /*
1755e8d6c554SDavid Howells  * deliver reply data to an FS.SetLock, FS.ExtendLock or FS.ReleaseLock
1756e8d6c554SDavid Howells  */
1757e8d6c554SDavid Howells static int afs_deliver_fs_xxxx_lock(struct afs_call *call,
1758e8d6c554SDavid Howells 				    struct sk_buff *skb, bool last)
1759e8d6c554SDavid Howells {
1760e8d6c554SDavid Howells 	const __be32 *bp;
1761e8d6c554SDavid Howells 
1762e8d6c554SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
1763e8d6c554SDavid Howells 
1764e8d6c554SDavid Howells 	afs_transfer_reply(call, skb);
1765e8d6c554SDavid Howells 	if (!last)
1766e8d6c554SDavid Howells 		return 0;
1767e8d6c554SDavid Howells 
1768e8d6c554SDavid Howells 	if (call->reply_size != call->reply_max)
1769e8d6c554SDavid Howells 		return -EBADMSG;
1770e8d6c554SDavid Howells 
1771e8d6c554SDavid Howells 	/* unmarshall the reply once we've received all of it */
1772e8d6c554SDavid Howells 	bp = call->buffer;
1773e8d6c554SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
1774e8d6c554SDavid Howells 
1775e8d6c554SDavid Howells 	_leave(" = 0 [done]");
1776e8d6c554SDavid Howells 	return 0;
1777e8d6c554SDavid Howells }
1778e8d6c554SDavid Howells 
1779e8d6c554SDavid Howells /*
1780e8d6c554SDavid Howells  * FS.SetLock operation type
1781e8d6c554SDavid Howells  */
1782e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSSetLock = {
1783e8d6c554SDavid Howells 	.name		= "FS.SetLock",
1784e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1785e8d6c554SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1786e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1787e8d6c554SDavid Howells };
1788e8d6c554SDavid Howells 
1789e8d6c554SDavid Howells /*
1790e8d6c554SDavid Howells  * FS.ExtendLock operation type
1791e8d6c554SDavid Howells  */
1792e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSExtendLock = {
1793e8d6c554SDavid Howells 	.name		= "FS.ExtendLock",
1794e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1795e8d6c554SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1796e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1797e8d6c554SDavid Howells };
1798e8d6c554SDavid Howells 
1799e8d6c554SDavid Howells /*
1800e8d6c554SDavid Howells  * FS.ReleaseLock operation type
1801e8d6c554SDavid Howells  */
1802e8d6c554SDavid Howells static const struct afs_call_type afs_RXFSReleaseLock = {
1803e8d6c554SDavid Howells 	.name		= "FS.ReleaseLock",
1804e8d6c554SDavid Howells 	.deliver	= afs_deliver_fs_xxxx_lock,
1805e8d6c554SDavid Howells 	.abort_to_error	= afs_abort_to_error,
1806e8d6c554SDavid Howells 	.destructor	= afs_flat_call_destructor,
1807e8d6c554SDavid Howells };
1808e8d6c554SDavid Howells 
1809e8d6c554SDavid Howells /*
1810e8d6c554SDavid Howells  * get a lock on a file
1811e8d6c554SDavid Howells  */
1812e8d6c554SDavid Howells int afs_fs_set_lock(struct afs_server *server,
1813e8d6c554SDavid Howells 		    struct key *key,
1814e8d6c554SDavid Howells 		    struct afs_vnode *vnode,
1815e8d6c554SDavid Howells 		    afs_lock_type_t type,
1816e8d6c554SDavid Howells 		    const struct afs_wait_mode *wait_mode)
1817e8d6c554SDavid Howells {
1818e8d6c554SDavid Howells 	struct afs_call *call;
1819e8d6c554SDavid Howells 	__be32 *bp;
1820e8d6c554SDavid Howells 
1821e8d6c554SDavid Howells 	_enter("");
1822e8d6c554SDavid Howells 
1823e8d6c554SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSSetLock, 5 * 4, 6 * 4);
1824e8d6c554SDavid Howells 	if (!call)
1825e8d6c554SDavid Howells 		return -ENOMEM;
1826e8d6c554SDavid Howells 
1827e8d6c554SDavid Howells 	call->key = key;
1828e8d6c554SDavid Howells 	call->reply = vnode;
1829e8d6c554SDavid Howells 	call->service_id = FS_SERVICE;
1830e8d6c554SDavid Howells 	call->port = htons(AFS_FS_PORT);
1831e8d6c554SDavid Howells 
1832e8d6c554SDavid Howells 	/* marshall the parameters */
1833e8d6c554SDavid Howells 	bp = call->request;
1834e8d6c554SDavid Howells 	*bp++ = htonl(FSSETLOCK);
1835e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1836e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1837e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1838e8d6c554SDavid Howells 	*bp++ = htonl(type);
1839e8d6c554SDavid Howells 
1840e8d6c554SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
1841e8d6c554SDavid Howells }
1842e8d6c554SDavid Howells 
1843e8d6c554SDavid Howells /*
1844e8d6c554SDavid Howells  * extend a lock on a file
1845e8d6c554SDavid Howells  */
1846e8d6c554SDavid Howells int afs_fs_extend_lock(struct afs_server *server,
1847e8d6c554SDavid Howells 		       struct key *key,
1848e8d6c554SDavid Howells 		       struct afs_vnode *vnode,
1849e8d6c554SDavid Howells 		       const struct afs_wait_mode *wait_mode)
1850e8d6c554SDavid Howells {
1851e8d6c554SDavid Howells 	struct afs_call *call;
1852e8d6c554SDavid Howells 	__be32 *bp;
1853e8d6c554SDavid Howells 
1854e8d6c554SDavid Howells 	_enter("");
1855e8d6c554SDavid Howells 
1856e8d6c554SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSExtendLock, 4 * 4, 6 * 4);
1857e8d6c554SDavid Howells 	if (!call)
1858e8d6c554SDavid Howells 		return -ENOMEM;
1859e8d6c554SDavid Howells 
1860e8d6c554SDavid Howells 	call->key = key;
1861e8d6c554SDavid Howells 	call->reply = vnode;
1862e8d6c554SDavid Howells 	call->service_id = FS_SERVICE;
1863e8d6c554SDavid Howells 	call->port = htons(AFS_FS_PORT);
1864e8d6c554SDavid Howells 
1865e8d6c554SDavid Howells 	/* marshall the parameters */
1866e8d6c554SDavid Howells 	bp = call->request;
1867e8d6c554SDavid Howells 	*bp++ = htonl(FSEXTENDLOCK);
1868e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1869e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1870e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1871e8d6c554SDavid Howells 
1872e8d6c554SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
1873e8d6c554SDavid Howells }
1874e8d6c554SDavid Howells 
1875e8d6c554SDavid Howells /*
1876e8d6c554SDavid Howells  * release a lock on a file
1877e8d6c554SDavid Howells  */
1878e8d6c554SDavid Howells int afs_fs_release_lock(struct afs_server *server,
1879e8d6c554SDavid Howells 			struct key *key,
1880e8d6c554SDavid Howells 			struct afs_vnode *vnode,
1881e8d6c554SDavid Howells 			const struct afs_wait_mode *wait_mode)
1882e8d6c554SDavid Howells {
1883e8d6c554SDavid Howells 	struct afs_call *call;
1884e8d6c554SDavid Howells 	__be32 *bp;
1885e8d6c554SDavid Howells 
1886e8d6c554SDavid Howells 	_enter("");
1887e8d6c554SDavid Howells 
1888e8d6c554SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSReleaseLock, 4 * 4, 6 * 4);
1889e8d6c554SDavid Howells 	if (!call)
1890e8d6c554SDavid Howells 		return -ENOMEM;
1891e8d6c554SDavid Howells 
1892e8d6c554SDavid Howells 	call->key = key;
1893e8d6c554SDavid Howells 	call->reply = vnode;
1894e8d6c554SDavid Howells 	call->service_id = FS_SERVICE;
1895e8d6c554SDavid Howells 	call->port = htons(AFS_FS_PORT);
1896e8d6c554SDavid Howells 
1897e8d6c554SDavid Howells 	/* marshall the parameters */
1898e8d6c554SDavid Howells 	bp = call->request;
1899e8d6c554SDavid Howells 	*bp++ = htonl(FSRELEASELOCK);
1900e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
1901e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
1902e8d6c554SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
1903e8d6c554SDavid Howells 
1904e8d6c554SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
1905e8d6c554SDavid Howells }
1906