xref: /openbmc/linux/fs/afs/fsclient.c (revision 416351f2)
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>
131da177e4SLinus Torvalds #include <linux/sched.h>
1408e0e7c8SDavid Howells #include <linux/circ_buf.h>
151da177e4SLinus Torvalds #include "internal.h"
1608e0e7c8SDavid Howells #include "afs_fs.h"
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds /*
19260a9803SDavid Howells  * decode an AFSFid block
20260a9803SDavid Howells  */
21260a9803SDavid Howells static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
22260a9803SDavid Howells {
23260a9803SDavid Howells 	const __be32 *bp = *_bp;
24260a9803SDavid Howells 
25260a9803SDavid Howells 	fid->vid		= ntohl(*bp++);
26260a9803SDavid Howells 	fid->vnode		= ntohl(*bp++);
27260a9803SDavid Howells 	fid->unique		= ntohl(*bp++);
28260a9803SDavid Howells 	*_bp = bp;
29260a9803SDavid Howells }
30260a9803SDavid Howells 
31260a9803SDavid Howells /*
3208e0e7c8SDavid Howells  * decode an AFSFetchStatus block
331da177e4SLinus Torvalds  */
3408e0e7c8SDavid Howells static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
35260a9803SDavid Howells 				      struct afs_file_status *status,
3608e0e7c8SDavid Howells 				      struct afs_vnode *vnode)
371da177e4SLinus Torvalds {
3808e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
3908e0e7c8SDavid Howells 	umode_t mode;
40260a9803SDavid Howells 	u64 data_version, size;
4108e0e7c8SDavid Howells 	u32 changed = 0; /* becomes non-zero if ctime-type changes seen */
4208e0e7c8SDavid Howells 
4308e0e7c8SDavid Howells #define EXTRACT(DST)				\
4408e0e7c8SDavid Howells 	do {					\
4508e0e7c8SDavid Howells 		u32 x = ntohl(*bp++);		\
4608e0e7c8SDavid Howells 		changed |= DST - x;		\
4708e0e7c8SDavid Howells 		DST = x;			\
4808e0e7c8SDavid Howells 	} while (0)
4908e0e7c8SDavid Howells 
50260a9803SDavid Howells 	status->if_version = ntohl(*bp++);
51260a9803SDavid Howells 	EXTRACT(status->type);
52260a9803SDavid Howells 	EXTRACT(status->nlink);
53260a9803SDavid Howells 	size = ntohl(*bp++);
5408e0e7c8SDavid Howells 	data_version = ntohl(*bp++);
55260a9803SDavid Howells 	EXTRACT(status->author);
56260a9803SDavid Howells 	EXTRACT(status->owner);
57260a9803SDavid Howells 	EXTRACT(status->caller_access); /* call ticket dependent */
58260a9803SDavid Howells 	EXTRACT(status->anon_access);
59260a9803SDavid Howells 	EXTRACT(status->mode);
60260a9803SDavid Howells 	EXTRACT(status->parent.vnode);
61260a9803SDavid Howells 	EXTRACT(status->parent.unique);
6208e0e7c8SDavid Howells 	bp++; /* seg size */
63260a9803SDavid Howells 	status->mtime_client = ntohl(*bp++);
64260a9803SDavid Howells 	status->mtime_server = ntohl(*bp++);
65260a9803SDavid Howells 	EXTRACT(status->group);
6608e0e7c8SDavid Howells 	bp++; /* sync counter */
6708e0e7c8SDavid Howells 	data_version |= (u64) ntohl(*bp++) << 32;
68260a9803SDavid Howells 	bp++; /* lock count */
69260a9803SDavid Howells 	size |= (u64) ntohl(*bp++) << 32;
7008e0e7c8SDavid Howells 	bp++; /* spare 4 */
7108e0e7c8SDavid Howells 	*_bp = bp;
7208e0e7c8SDavid Howells 
73260a9803SDavid Howells 	if (size != status->size) {
74260a9803SDavid Howells 		status->size = size;
75260a9803SDavid Howells 		changed |= true;
76260a9803SDavid Howells 	}
77260a9803SDavid Howells 	status->mode &= S_IALLUGO;
7808e0e7c8SDavid Howells 
79260a9803SDavid Howells 	_debug("vnode time %lx, %lx",
80260a9803SDavid Howells 	       status->mtime_client, status->mtime_server);
81260a9803SDavid Howells 
82260a9803SDavid Howells 	if (vnode) {
83260a9803SDavid Howells 		status->parent.vid = vnode->fid.vid;
84260a9803SDavid Howells 		if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
85260a9803SDavid Howells 			_debug("vnode changed");
86260a9803SDavid Howells 			i_size_write(&vnode->vfs_inode, size);
87260a9803SDavid Howells 			vnode->vfs_inode.i_uid = status->owner;
88260a9803SDavid Howells 			vnode->vfs_inode.i_gid = status->group;
89260a9803SDavid Howells 			vnode->vfs_inode.i_version = vnode->fid.unique;
90260a9803SDavid Howells 			vnode->vfs_inode.i_nlink = status->nlink;
91260a9803SDavid Howells 
9208e0e7c8SDavid Howells 			mode = vnode->vfs_inode.i_mode;
9308e0e7c8SDavid Howells 			mode &= ~S_IALLUGO;
94260a9803SDavid Howells 			mode |= status->mode;
95260a9803SDavid Howells 			barrier();
9608e0e7c8SDavid Howells 			vnode->vfs_inode.i_mode = mode;
9708e0e7c8SDavid Howells 		}
9808e0e7c8SDavid Howells 
99260a9803SDavid Howells 		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_server;
10008e0e7c8SDavid Howells 		vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
10108e0e7c8SDavid Howells 		vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
102260a9803SDavid Howells 	}
10308e0e7c8SDavid Howells 
104260a9803SDavid Howells 	if (status->data_version != data_version) {
105260a9803SDavid Howells 		status->data_version = data_version;
106260a9803SDavid Howells 		if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
107260a9803SDavid Howells 			_debug("vnode modified %llx on {%x:%u}",
108ba3e0e1aSDavid S. Miller 			       (unsigned long long) data_version,
109ba3e0e1aSDavid S. Miller 			       vnode->fid.vid, vnode->fid.vnode);
11008e0e7c8SDavid Howells 			set_bit(AFS_VNODE_MODIFIED, &vnode->flags);
11108e0e7c8SDavid Howells 			set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
1121da177e4SLinus Torvalds 		}
113ec26815aSDavid Howells 	}
114260a9803SDavid Howells }
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds /*
11708e0e7c8SDavid Howells  * decode an AFSCallBack block
1181da177e4SLinus Torvalds  */
11908e0e7c8SDavid Howells static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
1201da177e4SLinus Torvalds {
12108e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
1221da177e4SLinus Torvalds 
12308e0e7c8SDavid Howells 	vnode->cb_version	= ntohl(*bp++);
12408e0e7c8SDavid Howells 	vnode->cb_expiry	= ntohl(*bp++);
12508e0e7c8SDavid Howells 	vnode->cb_type		= ntohl(*bp++);
12608e0e7c8SDavid Howells 	vnode->cb_expires	= vnode->cb_expiry + get_seconds();
12708e0e7c8SDavid Howells 	*_bp = bp;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
130260a9803SDavid Howells static void xdr_decode_AFSCallBack_raw(const __be32 **_bp,
131260a9803SDavid Howells 				       struct afs_callback *cb)
132260a9803SDavid Howells {
133260a9803SDavid Howells 	const __be32 *bp = *_bp;
134260a9803SDavid Howells 
135260a9803SDavid Howells 	cb->version	= ntohl(*bp++);
136260a9803SDavid Howells 	cb->expiry	= ntohl(*bp++);
137260a9803SDavid Howells 	cb->type	= ntohl(*bp++);
138260a9803SDavid Howells 	*_bp = bp;
139260a9803SDavid Howells }
140260a9803SDavid Howells 
1411da177e4SLinus Torvalds /*
14208e0e7c8SDavid Howells  * decode an AFSVolSync block
1431da177e4SLinus Torvalds  */
14408e0e7c8SDavid Howells static void xdr_decode_AFSVolSync(const __be32 **_bp,
14508e0e7c8SDavid Howells 				  struct afs_volsync *volsync)
1461da177e4SLinus Torvalds {
14708e0e7c8SDavid Howells 	const __be32 *bp = *_bp;
1481da177e4SLinus Torvalds 
14908e0e7c8SDavid Howells 	volsync->creation = ntohl(*bp++);
15008e0e7c8SDavid Howells 	bp++; /* spare2 */
15108e0e7c8SDavid Howells 	bp++; /* spare3 */
15208e0e7c8SDavid Howells 	bp++; /* spare4 */
15308e0e7c8SDavid Howells 	bp++; /* spare5 */
15408e0e7c8SDavid Howells 	bp++; /* spare6 */
15508e0e7c8SDavid Howells 	*_bp = bp;
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds 
15808e0e7c8SDavid Howells /*
15908e0e7c8SDavid Howells  * deliver reply data to an FS.FetchStatus
16008e0e7c8SDavid Howells  */
16108e0e7c8SDavid Howells static int afs_deliver_fs_fetch_status(struct afs_call *call,
16208e0e7c8SDavid Howells 				       struct sk_buff *skb, bool last)
16308e0e7c8SDavid Howells {
164260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
16508e0e7c8SDavid Howells 	const __be32 *bp;
1661da177e4SLinus Torvalds 
16708e0e7c8SDavid Howells 	_enter(",,%u", last);
1681da177e4SLinus Torvalds 
16908e0e7c8SDavid Howells 	afs_transfer_reply(call, skb);
17008e0e7c8SDavid Howells 	if (!last)
17108e0e7c8SDavid Howells 		return 0;
1721da177e4SLinus Torvalds 
17308e0e7c8SDavid Howells 	if (call->reply_size != call->reply_max)
17408e0e7c8SDavid Howells 		return -EBADMSG;
1751da177e4SLinus Torvalds 
17608e0e7c8SDavid Howells 	/* unmarshall the reply once we've received all of it */
17708e0e7c8SDavid Howells 	bp = call->buffer;
178260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
179260a9803SDavid Howells 	xdr_decode_AFSCallBack(&bp, vnode);
18008e0e7c8SDavid Howells 	if (call->reply2)
18108e0e7c8SDavid Howells 		xdr_decode_AFSVolSync(&bp, call->reply2);
1821da177e4SLinus Torvalds 
18308e0e7c8SDavid Howells 	_leave(" = 0 [done]");
18408e0e7c8SDavid Howells 	return 0;
185ec26815aSDavid Howells }
18608e0e7c8SDavid Howells 
18708e0e7c8SDavid Howells /*
18808e0e7c8SDavid Howells  * FS.FetchStatus operation type
18908e0e7c8SDavid Howells  */
19008e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchStatus = {
19100d3b7a4SDavid Howells 	.name		= "FS.FetchStatus",
19208e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_status,
19308e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
19408e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
19508e0e7c8SDavid Howells };
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /*
1981da177e4SLinus Torvalds  * fetch the status information for a file
1991da177e4SLinus Torvalds  */
20008e0e7c8SDavid Howells int afs_fs_fetch_file_status(struct afs_server *server,
20100d3b7a4SDavid Howells 			     struct key *key,
2021da177e4SLinus Torvalds 			     struct afs_vnode *vnode,
20308e0e7c8SDavid Howells 			     struct afs_volsync *volsync,
20408e0e7c8SDavid Howells 			     const struct afs_wait_mode *wait_mode)
2051da177e4SLinus Torvalds {
20608e0e7c8SDavid Howells 	struct afs_call *call;
2071da177e4SLinus Torvalds 	__be32 *bp;
2081da177e4SLinus Torvalds 
209416351f2SDavid Howells 	_enter(",%x,{%x:%u},,",
210260a9803SDavid Howells 	       key_serial(key), vnode->fid.vid, vnode->fid.vnode);
2111da177e4SLinus Torvalds 
212260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
21308e0e7c8SDavid Howells 	if (!call)
21408e0e7c8SDavid Howells 		return -ENOMEM;
2151da177e4SLinus Torvalds 
21600d3b7a4SDavid Howells 	call->key = key;
21708e0e7c8SDavid Howells 	call->reply = vnode;
21808e0e7c8SDavid Howells 	call->reply2 = volsync;
21908e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
22008e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 	/* marshall the parameters */
22308e0e7c8SDavid Howells 	bp = call->request;
2241da177e4SLinus Torvalds 	bp[0] = htonl(FSFETCHSTATUS);
2251da177e4SLinus Torvalds 	bp[1] = htonl(vnode->fid.vid);
2261da177e4SLinus Torvalds 	bp[2] = htonl(vnode->fid.vnode);
2271da177e4SLinus Torvalds 	bp[3] = htonl(vnode->fid.unique);
2281da177e4SLinus Torvalds 
22908e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
230ec26815aSDavid Howells }
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds /*
23308e0e7c8SDavid Howells  * deliver reply data to an FS.FetchData
2341da177e4SLinus Torvalds  */
23508e0e7c8SDavid Howells static int afs_deliver_fs_fetch_data(struct afs_call *call,
23608e0e7c8SDavid Howells 				     struct sk_buff *skb, bool last)
2371da177e4SLinus Torvalds {
238260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
23908e0e7c8SDavid Howells 	const __be32 *bp;
24008e0e7c8SDavid Howells 	struct page *page;
24108e0e7c8SDavid Howells 	void *buffer;
2421da177e4SLinus Torvalds 	int ret;
2431da177e4SLinus Torvalds 
24408e0e7c8SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
2451da177e4SLinus Torvalds 
24608e0e7c8SDavid Howells 	switch (call->unmarshall) {
24708e0e7c8SDavid Howells 	case 0:
24808e0e7c8SDavid Howells 		call->offset = 0;
24908e0e7c8SDavid Howells 		call->unmarshall++;
2501da177e4SLinus Torvalds 
25108e0e7c8SDavid Howells 		/* extract the returned data length */
25208e0e7c8SDavid Howells 	case 1:
25308e0e7c8SDavid Howells 		_debug("extract data length");
25408e0e7c8SDavid Howells 		ret = afs_extract_data(call, skb, last, &call->tmp, 4);
25508e0e7c8SDavid Howells 		switch (ret) {
25608e0e7c8SDavid Howells 		case 0:		break;
25708e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
25808e0e7c8SDavid Howells 		default:	return ret;
2591da177e4SLinus Torvalds 		}
2601da177e4SLinus Torvalds 
26108e0e7c8SDavid Howells 		call->count = ntohl(call->tmp);
26208e0e7c8SDavid Howells 		_debug("DATA length: %u", call->count);
26308e0e7c8SDavid Howells 		if (call->count > PAGE_SIZE)
26408e0e7c8SDavid Howells 			return -EBADMSG;
26508e0e7c8SDavid Howells 		call->offset = 0;
26608e0e7c8SDavid Howells 		call->unmarshall++;
2671da177e4SLinus Torvalds 
26808e0e7c8SDavid Howells 		/* extract the returned data */
26908e0e7c8SDavid Howells 	case 2:
27008e0e7c8SDavid Howells 		_debug("extract data");
271416351f2SDavid Howells 		if (call->count > 0) {
27208e0e7c8SDavid Howells 			page = call->reply3;
27308e0e7c8SDavid Howells 			buffer = kmap_atomic(page, KM_USER0);
274416351f2SDavid Howells 			ret = afs_extract_data(call, skb, last, buffer,
275416351f2SDavid Howells 					       call->count);
27608e0e7c8SDavid Howells 			kunmap_atomic(buffer, KM_USER0);
27708e0e7c8SDavid Howells 			switch (ret) {
27808e0e7c8SDavid Howells 			case 0:		break;
27908e0e7c8SDavid Howells 			case -EAGAIN:	return 0;
28008e0e7c8SDavid Howells 			default:	return ret;
2811da177e4SLinus Torvalds 			}
282416351f2SDavid Howells 		}
2831da177e4SLinus Torvalds 
28408e0e7c8SDavid Howells 		call->offset = 0;
28508e0e7c8SDavid Howells 		call->unmarshall++;
28608e0e7c8SDavid Howells 
28708e0e7c8SDavid Howells 		/* extract the metadata */
28808e0e7c8SDavid Howells 	case 3:
289260a9803SDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer,
290260a9803SDavid Howells 				       (21 + 3 + 6) * 4);
29108e0e7c8SDavid Howells 		switch (ret) {
29208e0e7c8SDavid Howells 		case 0:		break;
29308e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
29408e0e7c8SDavid Howells 		default:	return ret;
295ec26815aSDavid Howells 		}
2961da177e4SLinus Torvalds 
29708e0e7c8SDavid Howells 		bp = call->buffer;
298260a9803SDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
299260a9803SDavid Howells 		xdr_decode_AFSCallBack(&bp, vnode);
30008e0e7c8SDavid Howells 		if (call->reply2)
30108e0e7c8SDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply2);
3021da177e4SLinus Torvalds 
30308e0e7c8SDavid Howells 		call->offset = 0;
30408e0e7c8SDavid Howells 		call->unmarshall++;
3051da177e4SLinus Torvalds 
30608e0e7c8SDavid Howells 	case 4:
30708e0e7c8SDavid Howells 		_debug("trailer");
30808e0e7c8SDavid Howells 		if (skb->len != 0)
30908e0e7c8SDavid Howells 			return -EBADMSG;
3101da177e4SLinus Torvalds 		break;
3111da177e4SLinus Torvalds 	}
3121da177e4SLinus Torvalds 
31308e0e7c8SDavid Howells 	if (!last)
31408e0e7c8SDavid Howells 		return 0;
3151da177e4SLinus Torvalds 
316416351f2SDavid Howells 	if (call->count < PAGE_SIZE) {
317416351f2SDavid Howells 		_debug("clear");
318416351f2SDavid Howells 		page = call->reply3;
319416351f2SDavid Howells 		buffer = kmap_atomic(page, KM_USER0);
320416351f2SDavid Howells 		memset(buffer + call->count, 0, PAGE_SIZE - call->count);
321416351f2SDavid Howells 		kunmap_atomic(buffer, KM_USER0);
322416351f2SDavid Howells 	}
323416351f2SDavid Howells 
32408e0e7c8SDavid Howells 	_leave(" = 0 [done]");
32508e0e7c8SDavid Howells 	return 0;
326ec26815aSDavid Howells }
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds /*
32908e0e7c8SDavid Howells  * FS.FetchData operation type
3301da177e4SLinus Torvalds  */
33108e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchData = {
33200d3b7a4SDavid Howells 	.name		= "FS.FetchData",
33308e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
33408e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
33508e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
33608e0e7c8SDavid Howells };
33708e0e7c8SDavid Howells 
33808e0e7c8SDavid Howells /*
33908e0e7c8SDavid Howells  * fetch data from a file
34008e0e7c8SDavid Howells  */
34108e0e7c8SDavid Howells int afs_fs_fetch_data(struct afs_server *server,
34200d3b7a4SDavid Howells 		      struct key *key,
3431da177e4SLinus Torvalds 		      struct afs_vnode *vnode,
34408e0e7c8SDavid Howells 		      off_t offset, size_t length,
34508e0e7c8SDavid Howells 		      struct page *buffer,
34608e0e7c8SDavid Howells 		      const struct afs_wait_mode *wait_mode)
3471da177e4SLinus Torvalds {
34808e0e7c8SDavid Howells 	struct afs_call *call;
34908e0e7c8SDavid Howells 	__be32 *bp;
3501da177e4SLinus Torvalds 
35108e0e7c8SDavid Howells 	_enter("");
3521da177e4SLinus Torvalds 
353260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
35408e0e7c8SDavid Howells 	if (!call)
35508e0e7c8SDavid Howells 		return -ENOMEM;
3561da177e4SLinus Torvalds 
35700d3b7a4SDavid Howells 	call->key = key;
35808e0e7c8SDavid Howells 	call->reply = vnode;
359260a9803SDavid Howells 	call->reply2 = NULL; /* volsync */
36008e0e7c8SDavid Howells 	call->reply3 = buffer;
36108e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
36208e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 	/* marshall the parameters */
36508e0e7c8SDavid Howells 	bp = call->request;
36608e0e7c8SDavid Howells 	bp[0] = htonl(FSFETCHDATA);
36708e0e7c8SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
36808e0e7c8SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
36908e0e7c8SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
37008e0e7c8SDavid Howells 	bp[4] = htonl(offset);
37108e0e7c8SDavid Howells 	bp[5] = htonl(length);
3721da177e4SLinus Torvalds 
37308e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds 
37608e0e7c8SDavid Howells /*
37708e0e7c8SDavid Howells  * deliver reply data to an FS.GiveUpCallBacks
37808e0e7c8SDavid Howells  */
37908e0e7c8SDavid Howells static int afs_deliver_fs_give_up_callbacks(struct afs_call *call,
38008e0e7c8SDavid Howells 					    struct sk_buff *skb, bool last)
38108e0e7c8SDavid Howells {
38208e0e7c8SDavid Howells 	_enter(",{%u},%d", skb->len, last);
3831da177e4SLinus Torvalds 
38408e0e7c8SDavid Howells 	if (skb->len > 0)
38508e0e7c8SDavid Howells 		return -EBADMSG; /* shouldn't be any reply data */
38608e0e7c8SDavid Howells 	return 0;
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
38908e0e7c8SDavid Howells /*
39008e0e7c8SDavid Howells  * FS.GiveUpCallBacks operation type
39108e0e7c8SDavid Howells  */
39208e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
39300d3b7a4SDavid Howells 	.name		= "FS.GiveUpCallBacks",
39408e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_give_up_callbacks,
39508e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
39608e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
39708e0e7c8SDavid Howells };
3981da177e4SLinus Torvalds 
39908e0e7c8SDavid Howells /*
40008e0e7c8SDavid Howells  * give up a set of callbacks
40108e0e7c8SDavid Howells  * - the callbacks are held in the server->cb_break ring
40208e0e7c8SDavid Howells  */
40308e0e7c8SDavid Howells int afs_fs_give_up_callbacks(struct afs_server *server,
40408e0e7c8SDavid Howells 			     const struct afs_wait_mode *wait_mode)
40508e0e7c8SDavid Howells {
40608e0e7c8SDavid Howells 	struct afs_call *call;
40708e0e7c8SDavid Howells 	size_t ncallbacks;
40808e0e7c8SDavid Howells 	__be32 *bp, *tp;
40908e0e7c8SDavid Howells 	int loop;
4101da177e4SLinus Torvalds 
41108e0e7c8SDavid Howells 	ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
41208e0e7c8SDavid Howells 			      ARRAY_SIZE(server->cb_break));
41308e0e7c8SDavid Howells 
41408e0e7c8SDavid Howells 	_enter("{%zu},", ncallbacks);
41508e0e7c8SDavid Howells 
41608e0e7c8SDavid Howells 	if (ncallbacks == 0)
41708e0e7c8SDavid Howells 		return 0;
41808e0e7c8SDavid Howells 	if (ncallbacks > AFSCBMAX)
41908e0e7c8SDavid Howells 		ncallbacks = AFSCBMAX;
42008e0e7c8SDavid Howells 
42108e0e7c8SDavid Howells 	_debug("break %zu callbacks", ncallbacks);
42208e0e7c8SDavid Howells 
42308e0e7c8SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks,
42408e0e7c8SDavid Howells 				   12 + ncallbacks * 6 * 4, 0);
42508e0e7c8SDavid Howells 	if (!call)
42608e0e7c8SDavid Howells 		return -ENOMEM;
42708e0e7c8SDavid Howells 
42808e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
42908e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
43008e0e7c8SDavid Howells 
43108e0e7c8SDavid Howells 	/* marshall the parameters */
43208e0e7c8SDavid Howells 	bp = call->request;
43308e0e7c8SDavid Howells 	tp = bp + 2 + ncallbacks * 3;
43408e0e7c8SDavid Howells 	*bp++ = htonl(FSGIVEUPCALLBACKS);
43508e0e7c8SDavid Howells 	*bp++ = htonl(ncallbacks);
43608e0e7c8SDavid Howells 	*tp++ = htonl(ncallbacks);
43708e0e7c8SDavid Howells 
43808e0e7c8SDavid Howells 	atomic_sub(ncallbacks, &server->cb_break_n);
43908e0e7c8SDavid Howells 	for (loop = ncallbacks; loop > 0; loop--) {
44008e0e7c8SDavid Howells 		struct afs_callback *cb =
44108e0e7c8SDavid Howells 			&server->cb_break[server->cb_break_tail];
44208e0e7c8SDavid Howells 
44308e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vid);
44408e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vnode);
44508e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.unique);
44608e0e7c8SDavid Howells 		*tp++ = htonl(cb->version);
44708e0e7c8SDavid Howells 		*tp++ = htonl(cb->expiry);
44808e0e7c8SDavid Howells 		*tp++ = htonl(cb->type);
44908e0e7c8SDavid Howells 		smp_mb();
45008e0e7c8SDavid Howells 		server->cb_break_tail =
45108e0e7c8SDavid Howells 			(server->cb_break_tail + 1) &
45208e0e7c8SDavid Howells 			(ARRAY_SIZE(server->cb_break) - 1);
453ec26815aSDavid Howells 	}
45408e0e7c8SDavid Howells 
45508e0e7c8SDavid Howells 	ASSERT(ncallbacks > 0);
45608e0e7c8SDavid Howells 	wake_up_nr(&server->cb_break_waitq, ncallbacks);
45708e0e7c8SDavid Howells 
45808e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
45908e0e7c8SDavid Howells }
460260a9803SDavid Howells 
461260a9803SDavid Howells /*
462260a9803SDavid Howells  * deliver reply data to an FS.CreateFile or an FS.MakeDir
463260a9803SDavid Howells  */
464260a9803SDavid Howells static int afs_deliver_fs_create_vnode(struct afs_call *call,
465260a9803SDavid Howells 				       struct sk_buff *skb, bool last)
466260a9803SDavid Howells {
467260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
468260a9803SDavid Howells 	const __be32 *bp;
469260a9803SDavid Howells 
470260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
471260a9803SDavid Howells 
472260a9803SDavid Howells 	afs_transfer_reply(call, skb);
473260a9803SDavid Howells 	if (!last)
474260a9803SDavid Howells 		return 0;
475260a9803SDavid Howells 
476260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
477260a9803SDavid Howells 		return -EBADMSG;
478260a9803SDavid Howells 
479260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
480260a9803SDavid Howells 	bp = call->buffer;
481260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
482260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL);
483260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
484260a9803SDavid Howells 	xdr_decode_AFSCallBack_raw(&bp, call->reply4);
485260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
486260a9803SDavid Howells 
487260a9803SDavid Howells 	_leave(" = 0 [done]");
488260a9803SDavid Howells 	return 0;
489260a9803SDavid Howells }
490260a9803SDavid Howells 
491260a9803SDavid Howells /*
492260a9803SDavid Howells  * FS.CreateFile and FS.MakeDir operation type
493260a9803SDavid Howells  */
494260a9803SDavid Howells static const struct afs_call_type afs_RXFSCreateXXXX = {
495260a9803SDavid Howells 	.name		= "FS.CreateXXXX",
496260a9803SDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
497260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
498260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
499260a9803SDavid Howells };
500260a9803SDavid Howells 
501260a9803SDavid Howells /*
502260a9803SDavid Howells  * create a file or make a directory
503260a9803SDavid Howells  */
504260a9803SDavid Howells int afs_fs_create(struct afs_server *server,
505260a9803SDavid Howells 		  struct key *key,
506260a9803SDavid Howells 		  struct afs_vnode *vnode,
507260a9803SDavid Howells 		  const char *name,
508260a9803SDavid Howells 		  umode_t mode,
509260a9803SDavid Howells 		  struct afs_fid *newfid,
510260a9803SDavid Howells 		  struct afs_file_status *newstatus,
511260a9803SDavid Howells 		  struct afs_callback *newcb,
512260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
513260a9803SDavid Howells {
514260a9803SDavid Howells 	struct afs_call *call;
515260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
516260a9803SDavid Howells 	__be32 *bp;
517260a9803SDavid Howells 
518260a9803SDavid Howells 	_enter("");
519260a9803SDavid Howells 
520260a9803SDavid Howells 	namesz = strlen(name);
521260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
522260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (6 * 4);
523260a9803SDavid Howells 
524260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSCreateXXXX, reqsz,
525260a9803SDavid Howells 				   (3 + 21 + 21 + 3 + 6) * 4);
526260a9803SDavid Howells 	if (!call)
527260a9803SDavid Howells 		return -ENOMEM;
528260a9803SDavid Howells 
529260a9803SDavid Howells 	call->key = key;
530260a9803SDavid Howells 	call->reply = vnode;
531260a9803SDavid Howells 	call->reply2 = newfid;
532260a9803SDavid Howells 	call->reply3 = newstatus;
533260a9803SDavid Howells 	call->reply4 = newcb;
534260a9803SDavid Howells 	call->service_id = FS_SERVICE;
535260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
536260a9803SDavid Howells 
537260a9803SDavid Howells 	/* marshall the parameters */
538260a9803SDavid Howells 	bp = call->request;
539260a9803SDavid Howells 	*bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE);
540260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
541260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
542260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
543260a9803SDavid Howells 	*bp++ = htonl(namesz);
544260a9803SDavid Howells 	memcpy(bp, name, namesz);
545260a9803SDavid Howells 	bp = (void *) bp + namesz;
546260a9803SDavid Howells 	if (padsz > 0) {
547260a9803SDavid Howells 		memset(bp, 0, padsz);
548260a9803SDavid Howells 		bp = (void *) bp + padsz;
549260a9803SDavid Howells 	}
550260a9803SDavid Howells 	*bp++ = htonl(AFS_SET_MODE);
551260a9803SDavid Howells 	*bp++ = 0; /* mtime */
552260a9803SDavid Howells 	*bp++ = 0; /* owner */
553260a9803SDavid Howells 	*bp++ = 0; /* group */
554260a9803SDavid Howells 	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
555260a9803SDavid Howells 	*bp++ = 0; /* segment size */
556260a9803SDavid Howells 
557260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
558260a9803SDavid Howells }
559260a9803SDavid Howells 
560260a9803SDavid Howells /*
561260a9803SDavid Howells  * deliver reply data to an FS.RemoveFile or FS.RemoveDir
562260a9803SDavid Howells  */
563260a9803SDavid Howells static int afs_deliver_fs_remove(struct afs_call *call,
564260a9803SDavid Howells 				 struct sk_buff *skb, bool last)
565260a9803SDavid Howells {
566260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
567260a9803SDavid Howells 	const __be32 *bp;
568260a9803SDavid Howells 
569260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
570260a9803SDavid Howells 
571260a9803SDavid Howells 	afs_transfer_reply(call, skb);
572260a9803SDavid Howells 	if (!last)
573260a9803SDavid Howells 		return 0;
574260a9803SDavid Howells 
575260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
576260a9803SDavid Howells 		return -EBADMSG;
577260a9803SDavid Howells 
578260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
579260a9803SDavid Howells 	bp = call->buffer;
580260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
581260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
582260a9803SDavid Howells 
583260a9803SDavid Howells 	_leave(" = 0 [done]");
584260a9803SDavid Howells 	return 0;
585260a9803SDavid Howells }
586260a9803SDavid Howells 
587260a9803SDavid Howells /*
588260a9803SDavid Howells  * FS.RemoveDir/FS.RemoveFile operation type
589260a9803SDavid Howells  */
590260a9803SDavid Howells static const struct afs_call_type afs_RXFSRemoveXXXX = {
591260a9803SDavid Howells 	.name		= "FS.RemoveXXXX",
592260a9803SDavid Howells 	.deliver	= afs_deliver_fs_remove,
593260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
594260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
595260a9803SDavid Howells };
596260a9803SDavid Howells 
597260a9803SDavid Howells /*
598260a9803SDavid Howells  * remove a file or directory
599260a9803SDavid Howells  */
600260a9803SDavid Howells int afs_fs_remove(struct afs_server *server,
601260a9803SDavid Howells 		  struct key *key,
602260a9803SDavid Howells 		  struct afs_vnode *vnode,
603260a9803SDavid Howells 		  const char *name,
604260a9803SDavid Howells 		  bool isdir,
605260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
606260a9803SDavid Howells {
607260a9803SDavid Howells 	struct afs_call *call;
608260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
609260a9803SDavid Howells 	__be32 *bp;
610260a9803SDavid Howells 
611260a9803SDavid Howells 	_enter("");
612260a9803SDavid Howells 
613260a9803SDavid Howells 	namesz = strlen(name);
614260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
615260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz;
616260a9803SDavid Howells 
617260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4);
618260a9803SDavid Howells 	if (!call)
619260a9803SDavid Howells 		return -ENOMEM;
620260a9803SDavid Howells 
621260a9803SDavid Howells 	call->key = key;
622260a9803SDavid Howells 	call->reply = vnode;
623260a9803SDavid Howells 	call->service_id = FS_SERVICE;
624260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
625260a9803SDavid Howells 
626260a9803SDavid Howells 	/* marshall the parameters */
627260a9803SDavid Howells 	bp = call->request;
628260a9803SDavid Howells 	*bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE);
629260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
630260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
631260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
632260a9803SDavid Howells 	*bp++ = htonl(namesz);
633260a9803SDavid Howells 	memcpy(bp, name, namesz);
634260a9803SDavid Howells 	bp = (void *) bp + namesz;
635260a9803SDavid Howells 	if (padsz > 0) {
636260a9803SDavid Howells 		memset(bp, 0, padsz);
637260a9803SDavid Howells 		bp = (void *) bp + padsz;
638260a9803SDavid Howells 	}
639260a9803SDavid Howells 
640260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
641260a9803SDavid Howells }
642260a9803SDavid Howells 
643260a9803SDavid Howells /*
644260a9803SDavid Howells  * deliver reply data to an FS.Link
645260a9803SDavid Howells  */
646260a9803SDavid Howells static int afs_deliver_fs_link(struct afs_call *call,
647260a9803SDavid Howells 			       struct sk_buff *skb, bool last)
648260a9803SDavid Howells {
649260a9803SDavid Howells 	struct afs_vnode *dvnode = call->reply, *vnode = call->reply2;
650260a9803SDavid Howells 	const __be32 *bp;
651260a9803SDavid Howells 
652260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
653260a9803SDavid Howells 
654260a9803SDavid Howells 	afs_transfer_reply(call, skb);
655260a9803SDavid Howells 	if (!last)
656260a9803SDavid Howells 		return 0;
657260a9803SDavid Howells 
658260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
659260a9803SDavid Howells 		return -EBADMSG;
660260a9803SDavid Howells 
661260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
662260a9803SDavid Howells 	bp = call->buffer;
663260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
664260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode);
665260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
666260a9803SDavid Howells 
667260a9803SDavid Howells 	_leave(" = 0 [done]");
668260a9803SDavid Howells 	return 0;
669260a9803SDavid Howells }
670260a9803SDavid Howells 
671260a9803SDavid Howells /*
672260a9803SDavid Howells  * FS.Link operation type
673260a9803SDavid Howells  */
674260a9803SDavid Howells static const struct afs_call_type afs_RXFSLink = {
675260a9803SDavid Howells 	.name		= "FS.Link",
676260a9803SDavid Howells 	.deliver	= afs_deliver_fs_link,
677260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
678260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
679260a9803SDavid Howells };
680260a9803SDavid Howells 
681260a9803SDavid Howells /*
682260a9803SDavid Howells  * make a hard link
683260a9803SDavid Howells  */
684260a9803SDavid Howells int afs_fs_link(struct afs_server *server,
685260a9803SDavid Howells 		struct key *key,
686260a9803SDavid Howells 		struct afs_vnode *dvnode,
687260a9803SDavid Howells 		struct afs_vnode *vnode,
688260a9803SDavid Howells 		const char *name,
689260a9803SDavid Howells 		const struct afs_wait_mode *wait_mode)
690260a9803SDavid Howells {
691260a9803SDavid Howells 	struct afs_call *call;
692260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
693260a9803SDavid Howells 	__be32 *bp;
694260a9803SDavid Howells 
695260a9803SDavid Howells 	_enter("");
696260a9803SDavid Howells 
697260a9803SDavid Howells 	namesz = strlen(name);
698260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
699260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (3 * 4);
700260a9803SDavid Howells 
701260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
702260a9803SDavid Howells 	if (!call)
703260a9803SDavid Howells 		return -ENOMEM;
704260a9803SDavid Howells 
705260a9803SDavid Howells 	call->key = key;
706260a9803SDavid Howells 	call->reply = dvnode;
707260a9803SDavid Howells 	call->reply2 = vnode;
708260a9803SDavid Howells 	call->service_id = FS_SERVICE;
709260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
710260a9803SDavid Howells 
711260a9803SDavid Howells 	/* marshall the parameters */
712260a9803SDavid Howells 	bp = call->request;
713260a9803SDavid Howells 	*bp++ = htonl(FSLINK);
714260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vid);
715260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vnode);
716260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.unique);
717260a9803SDavid Howells 	*bp++ = htonl(namesz);
718260a9803SDavid Howells 	memcpy(bp, name, namesz);
719260a9803SDavid Howells 	bp = (void *) bp + namesz;
720260a9803SDavid Howells 	if (padsz > 0) {
721260a9803SDavid Howells 		memset(bp, 0, padsz);
722260a9803SDavid Howells 		bp = (void *) bp + padsz;
723260a9803SDavid Howells 	}
724260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
725260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
726260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
727260a9803SDavid Howells 
728260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
729260a9803SDavid Howells }
730260a9803SDavid Howells 
731260a9803SDavid Howells /*
732260a9803SDavid Howells  * deliver reply data to an FS.Symlink
733260a9803SDavid Howells  */
734260a9803SDavid Howells static int afs_deliver_fs_symlink(struct afs_call *call,
735260a9803SDavid Howells 				  struct sk_buff *skb, bool last)
736260a9803SDavid Howells {
737260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
738260a9803SDavid Howells 	const __be32 *bp;
739260a9803SDavid Howells 
740260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
741260a9803SDavid Howells 
742260a9803SDavid Howells 	afs_transfer_reply(call, skb);
743260a9803SDavid Howells 	if (!last)
744260a9803SDavid Howells 		return 0;
745260a9803SDavid Howells 
746260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
747260a9803SDavid Howells 		return -EBADMSG;
748260a9803SDavid Howells 
749260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
750260a9803SDavid Howells 	bp = call->buffer;
751260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
752260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL);
753260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
754260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
755260a9803SDavid Howells 
756260a9803SDavid Howells 	_leave(" = 0 [done]");
757260a9803SDavid Howells 	return 0;
758260a9803SDavid Howells }
759260a9803SDavid Howells 
760260a9803SDavid Howells /*
761260a9803SDavid Howells  * FS.Symlink operation type
762260a9803SDavid Howells  */
763260a9803SDavid Howells static const struct afs_call_type afs_RXFSSymlink = {
764260a9803SDavid Howells 	.name		= "FS.Symlink",
765260a9803SDavid Howells 	.deliver	= afs_deliver_fs_symlink,
766260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
767260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
768260a9803SDavid Howells };
769260a9803SDavid Howells 
770260a9803SDavid Howells /*
771260a9803SDavid Howells  * create a symbolic link
772260a9803SDavid Howells  */
773260a9803SDavid Howells int afs_fs_symlink(struct afs_server *server,
774260a9803SDavid Howells 		   struct key *key,
775260a9803SDavid Howells 		   struct afs_vnode *vnode,
776260a9803SDavid Howells 		   const char *name,
777260a9803SDavid Howells 		   const char *contents,
778260a9803SDavid Howells 		   struct afs_fid *newfid,
779260a9803SDavid Howells 		   struct afs_file_status *newstatus,
780260a9803SDavid Howells 		   const struct afs_wait_mode *wait_mode)
781260a9803SDavid Howells {
782260a9803SDavid Howells 	struct afs_call *call;
783260a9803SDavid Howells 	size_t namesz, reqsz, padsz, c_namesz, c_padsz;
784260a9803SDavid Howells 	__be32 *bp;
785260a9803SDavid Howells 
786260a9803SDavid Howells 	_enter("");
787260a9803SDavid Howells 
788260a9803SDavid Howells 	namesz = strlen(name);
789260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
790260a9803SDavid Howells 
791260a9803SDavid Howells 	c_namesz = strlen(contents);
792260a9803SDavid Howells 	c_padsz = (4 - (c_namesz & 3)) & 3;
793260a9803SDavid Howells 
794260a9803SDavid Howells 	reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
795260a9803SDavid Howells 
796260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSSymlink, reqsz,
797260a9803SDavid Howells 				   (3 + 21 + 21 + 6) * 4);
798260a9803SDavid Howells 	if (!call)
799260a9803SDavid Howells 		return -ENOMEM;
800260a9803SDavid Howells 
801260a9803SDavid Howells 	call->key = key;
802260a9803SDavid Howells 	call->reply = vnode;
803260a9803SDavid Howells 	call->reply2 = newfid;
804260a9803SDavid Howells 	call->reply3 = newstatus;
805260a9803SDavid Howells 	call->service_id = FS_SERVICE;
806260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
807260a9803SDavid Howells 
808260a9803SDavid Howells 	/* marshall the parameters */
809260a9803SDavid Howells 	bp = call->request;
810260a9803SDavid Howells 	*bp++ = htonl(FSSYMLINK);
811260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
812260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
813260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
814260a9803SDavid Howells 	*bp++ = htonl(namesz);
815260a9803SDavid Howells 	memcpy(bp, name, namesz);
816260a9803SDavid Howells 	bp = (void *) bp + namesz;
817260a9803SDavid Howells 	if (padsz > 0) {
818260a9803SDavid Howells 		memset(bp, 0, padsz);
819260a9803SDavid Howells 		bp = (void *) bp + padsz;
820260a9803SDavid Howells 	}
821260a9803SDavid Howells 	*bp++ = htonl(c_namesz);
822260a9803SDavid Howells 	memcpy(bp, contents, c_namesz);
823260a9803SDavid Howells 	bp = (void *) bp + c_namesz;
824260a9803SDavid Howells 	if (c_padsz > 0) {
825260a9803SDavid Howells 		memset(bp, 0, c_padsz);
826260a9803SDavid Howells 		bp = (void *) bp + c_padsz;
827260a9803SDavid Howells 	}
828260a9803SDavid Howells 	*bp++ = htonl(AFS_SET_MODE);
829260a9803SDavid Howells 	*bp++ = 0; /* mtime */
830260a9803SDavid Howells 	*bp++ = 0; /* owner */
831260a9803SDavid Howells 	*bp++ = 0; /* group */
832260a9803SDavid Howells 	*bp++ = htonl(S_IRWXUGO); /* unix mode */
833260a9803SDavid Howells 	*bp++ = 0; /* segment size */
834260a9803SDavid Howells 
835260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
836260a9803SDavid Howells }
837260a9803SDavid Howells 
838260a9803SDavid Howells /*
839260a9803SDavid Howells  * deliver reply data to an FS.Rename
840260a9803SDavid Howells  */
841260a9803SDavid Howells static int afs_deliver_fs_rename(struct afs_call *call,
842260a9803SDavid Howells 				  struct sk_buff *skb, bool last)
843260a9803SDavid Howells {
844260a9803SDavid Howells 	struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2;
845260a9803SDavid Howells 	const __be32 *bp;
846260a9803SDavid Howells 
847260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
848260a9803SDavid Howells 
849260a9803SDavid Howells 	afs_transfer_reply(call, skb);
850260a9803SDavid Howells 	if (!last)
851260a9803SDavid Howells 		return 0;
852260a9803SDavid Howells 
853260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
854260a9803SDavid Howells 		return -EBADMSG;
855260a9803SDavid Howells 
856260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
857260a9803SDavid Howells 	bp = call->buffer;
858260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode);
859260a9803SDavid Howells 	if (new_dvnode != orig_dvnode)
860260a9803SDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode);
861260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
862260a9803SDavid Howells 
863260a9803SDavid Howells 	_leave(" = 0 [done]");
864260a9803SDavid Howells 	return 0;
865260a9803SDavid Howells }
866260a9803SDavid Howells 
867260a9803SDavid Howells /*
868260a9803SDavid Howells  * FS.Rename operation type
869260a9803SDavid Howells  */
870260a9803SDavid Howells static const struct afs_call_type afs_RXFSRename = {
871260a9803SDavid Howells 	.name		= "FS.Rename",
872260a9803SDavid Howells 	.deliver	= afs_deliver_fs_rename,
873260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
874260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
875260a9803SDavid Howells };
876260a9803SDavid Howells 
877260a9803SDavid Howells /*
878260a9803SDavid Howells  * create a symbolic link
879260a9803SDavid Howells  */
880260a9803SDavid Howells int afs_fs_rename(struct afs_server *server,
881260a9803SDavid Howells 		  struct key *key,
882260a9803SDavid Howells 		  struct afs_vnode *orig_dvnode,
883260a9803SDavid Howells 		  const char *orig_name,
884260a9803SDavid Howells 		  struct afs_vnode *new_dvnode,
885260a9803SDavid Howells 		  const char *new_name,
886260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
887260a9803SDavid Howells {
888260a9803SDavid Howells 	struct afs_call *call;
889260a9803SDavid Howells 	size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
890260a9803SDavid Howells 	__be32 *bp;
891260a9803SDavid Howells 
892260a9803SDavid Howells 	_enter("");
893260a9803SDavid Howells 
894260a9803SDavid Howells 	o_namesz = strlen(orig_name);
895260a9803SDavid Howells 	o_padsz = (4 - (o_namesz & 3)) & 3;
896260a9803SDavid Howells 
897260a9803SDavid Howells 	n_namesz = strlen(new_name);
898260a9803SDavid Howells 	n_padsz = (4 - (n_namesz & 3)) & 3;
899260a9803SDavid Howells 
900260a9803SDavid Howells 	reqsz = (4 * 4) +
901260a9803SDavid Howells 		4 + o_namesz + o_padsz +
902260a9803SDavid Howells 		(3 * 4) +
903260a9803SDavid Howells 		4 + n_namesz + n_padsz;
904260a9803SDavid Howells 
905260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
906260a9803SDavid Howells 	if (!call)
907260a9803SDavid Howells 		return -ENOMEM;
908260a9803SDavid Howells 
909260a9803SDavid Howells 	call->key = key;
910260a9803SDavid Howells 	call->reply = orig_dvnode;
911260a9803SDavid Howells 	call->reply2 = new_dvnode;
912260a9803SDavid Howells 	call->service_id = FS_SERVICE;
913260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
914260a9803SDavid Howells 
915260a9803SDavid Howells 	/* marshall the parameters */
916260a9803SDavid Howells 	bp = call->request;
917260a9803SDavid Howells 	*bp++ = htonl(FSRENAME);
918260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vid);
919260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vnode);
920260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.unique);
921260a9803SDavid Howells 	*bp++ = htonl(o_namesz);
922260a9803SDavid Howells 	memcpy(bp, orig_name, o_namesz);
923260a9803SDavid Howells 	bp = (void *) bp + o_namesz;
924260a9803SDavid Howells 	if (o_padsz > 0) {
925260a9803SDavid Howells 		memset(bp, 0, o_padsz);
926260a9803SDavid Howells 		bp = (void *) bp + o_padsz;
927260a9803SDavid Howells 	}
928260a9803SDavid Howells 
929260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vid);
930260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vnode);
931260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.unique);
932260a9803SDavid Howells 	*bp++ = htonl(n_namesz);
933260a9803SDavid Howells 	memcpy(bp, new_name, n_namesz);
934260a9803SDavid Howells 	bp = (void *) bp + n_namesz;
935260a9803SDavid Howells 	if (n_padsz > 0) {
936260a9803SDavid Howells 		memset(bp, 0, n_padsz);
937260a9803SDavid Howells 		bp = (void *) bp + n_padsz;
938260a9803SDavid Howells 	}
939260a9803SDavid Howells 
940260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
941260a9803SDavid Howells }
942