xref: /openbmc/linux/fs/afs/fsclient.c (revision ba3e0e1a)
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 
209260a9803SDavid Howells 	_enter(",%x,{%x:%d},,",
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 		if (call->count < PAGE_SIZE) {
26908e0e7c8SDavid Howells 			buffer = kmap_atomic(call->reply3, KM_USER0);
27008e0e7c8SDavid Howells 			memset(buffer + PAGE_SIZE - call->count, 0,
27108e0e7c8SDavid Howells 			       call->count);
27208e0e7c8SDavid Howells 			kunmap_atomic(buffer, KM_USER0);
2731da177e4SLinus Torvalds 		}
2741da177e4SLinus Torvalds 
27508e0e7c8SDavid Howells 		/* extract the returned data */
27608e0e7c8SDavid Howells 	case 2:
27708e0e7c8SDavid Howells 		_debug("extract data");
27808e0e7c8SDavid Howells 		page = call->reply3;
27908e0e7c8SDavid Howells 		buffer = kmap_atomic(page, KM_USER0);
28008e0e7c8SDavid Howells 		ret = afs_extract_data(call, skb, last, buffer, call->count);
28108e0e7c8SDavid Howells 		kunmap_atomic(buffer, KM_USER0);
28208e0e7c8SDavid Howells 		switch (ret) {
28308e0e7c8SDavid Howells 		case 0:		break;
28408e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
28508e0e7c8SDavid Howells 		default:	return ret;
2861da177e4SLinus Torvalds 		}
2871da177e4SLinus Torvalds 
28808e0e7c8SDavid Howells 		call->offset = 0;
28908e0e7c8SDavid Howells 		call->unmarshall++;
29008e0e7c8SDavid Howells 
29108e0e7c8SDavid Howells 		/* extract the metadata */
29208e0e7c8SDavid Howells 	case 3:
293260a9803SDavid Howells 		ret = afs_extract_data(call, skb, last, call->buffer,
294260a9803SDavid Howells 				       (21 + 3 + 6) * 4);
29508e0e7c8SDavid Howells 		switch (ret) {
29608e0e7c8SDavid Howells 		case 0:		break;
29708e0e7c8SDavid Howells 		case -EAGAIN:	return 0;
29808e0e7c8SDavid Howells 		default:	return ret;
299ec26815aSDavid Howells 		}
3001da177e4SLinus Torvalds 
30108e0e7c8SDavid Howells 		bp = call->buffer;
302260a9803SDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
303260a9803SDavid Howells 		xdr_decode_AFSCallBack(&bp, vnode);
30408e0e7c8SDavid Howells 		if (call->reply2)
30508e0e7c8SDavid Howells 			xdr_decode_AFSVolSync(&bp, call->reply2);
3061da177e4SLinus Torvalds 
30708e0e7c8SDavid Howells 		call->offset = 0;
30808e0e7c8SDavid Howells 		call->unmarshall++;
3091da177e4SLinus Torvalds 
31008e0e7c8SDavid Howells 	case 4:
31108e0e7c8SDavid Howells 		_debug("trailer");
31208e0e7c8SDavid Howells 		if (skb->len != 0)
31308e0e7c8SDavid Howells 			return -EBADMSG;
3141da177e4SLinus Torvalds 		break;
3151da177e4SLinus Torvalds 	}
3161da177e4SLinus Torvalds 
31708e0e7c8SDavid Howells 	if (!last)
31808e0e7c8SDavid Howells 		return 0;
3191da177e4SLinus Torvalds 
32008e0e7c8SDavid Howells 	_leave(" = 0 [done]");
32108e0e7c8SDavid Howells 	return 0;
322ec26815aSDavid Howells }
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds /*
32508e0e7c8SDavid Howells  * FS.FetchData operation type
3261da177e4SLinus Torvalds  */
32708e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSFetchData = {
32800d3b7a4SDavid Howells 	.name		= "FS.FetchData",
32908e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_fetch_data,
33008e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
33108e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
33208e0e7c8SDavid Howells };
33308e0e7c8SDavid Howells 
33408e0e7c8SDavid Howells /*
33508e0e7c8SDavid Howells  * fetch data from a file
33608e0e7c8SDavid Howells  */
33708e0e7c8SDavid Howells int afs_fs_fetch_data(struct afs_server *server,
33800d3b7a4SDavid Howells 		      struct key *key,
3391da177e4SLinus Torvalds 		      struct afs_vnode *vnode,
34008e0e7c8SDavid Howells 		      off_t offset, size_t length,
34108e0e7c8SDavid Howells 		      struct page *buffer,
34208e0e7c8SDavid Howells 		      const struct afs_wait_mode *wait_mode)
3431da177e4SLinus Torvalds {
34408e0e7c8SDavid Howells 	struct afs_call *call;
34508e0e7c8SDavid Howells 	__be32 *bp;
3461da177e4SLinus Torvalds 
34708e0e7c8SDavid Howells 	_enter("");
3481da177e4SLinus Torvalds 
349260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
35008e0e7c8SDavid Howells 	if (!call)
35108e0e7c8SDavid Howells 		return -ENOMEM;
3521da177e4SLinus Torvalds 
35300d3b7a4SDavid Howells 	call->key = key;
35408e0e7c8SDavid Howells 	call->reply = vnode;
355260a9803SDavid Howells 	call->reply2 = NULL; /* volsync */
35608e0e7c8SDavid Howells 	call->reply3 = buffer;
35708e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
35808e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 	/* marshall the parameters */
36108e0e7c8SDavid Howells 	bp = call->request;
36208e0e7c8SDavid Howells 	bp[0] = htonl(FSFETCHDATA);
36308e0e7c8SDavid Howells 	bp[1] = htonl(vnode->fid.vid);
36408e0e7c8SDavid Howells 	bp[2] = htonl(vnode->fid.vnode);
36508e0e7c8SDavid Howells 	bp[3] = htonl(vnode->fid.unique);
36608e0e7c8SDavid Howells 	bp[4] = htonl(offset);
36708e0e7c8SDavid Howells 	bp[5] = htonl(length);
3681da177e4SLinus Torvalds 
36908e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
37208e0e7c8SDavid Howells /*
37308e0e7c8SDavid Howells  * deliver reply data to an FS.GiveUpCallBacks
37408e0e7c8SDavid Howells  */
37508e0e7c8SDavid Howells static int afs_deliver_fs_give_up_callbacks(struct afs_call *call,
37608e0e7c8SDavid Howells 					    struct sk_buff *skb, bool last)
37708e0e7c8SDavid Howells {
37808e0e7c8SDavid Howells 	_enter(",{%u},%d", skb->len, last);
3791da177e4SLinus Torvalds 
38008e0e7c8SDavid Howells 	if (skb->len > 0)
38108e0e7c8SDavid Howells 		return -EBADMSG; /* shouldn't be any reply data */
38208e0e7c8SDavid Howells 	return 0;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds 
38508e0e7c8SDavid Howells /*
38608e0e7c8SDavid Howells  * FS.GiveUpCallBacks operation type
38708e0e7c8SDavid Howells  */
38808e0e7c8SDavid Howells static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
38900d3b7a4SDavid Howells 	.name		= "FS.GiveUpCallBacks",
39008e0e7c8SDavid Howells 	.deliver	= afs_deliver_fs_give_up_callbacks,
39108e0e7c8SDavid Howells 	.abort_to_error	= afs_abort_to_error,
39208e0e7c8SDavid Howells 	.destructor	= afs_flat_call_destructor,
39308e0e7c8SDavid Howells };
3941da177e4SLinus Torvalds 
39508e0e7c8SDavid Howells /*
39608e0e7c8SDavid Howells  * give up a set of callbacks
39708e0e7c8SDavid Howells  * - the callbacks are held in the server->cb_break ring
39808e0e7c8SDavid Howells  */
39908e0e7c8SDavid Howells int afs_fs_give_up_callbacks(struct afs_server *server,
40008e0e7c8SDavid Howells 			     const struct afs_wait_mode *wait_mode)
40108e0e7c8SDavid Howells {
40208e0e7c8SDavid Howells 	struct afs_call *call;
40308e0e7c8SDavid Howells 	size_t ncallbacks;
40408e0e7c8SDavid Howells 	__be32 *bp, *tp;
40508e0e7c8SDavid Howells 	int loop;
4061da177e4SLinus Torvalds 
40708e0e7c8SDavid Howells 	ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
40808e0e7c8SDavid Howells 			      ARRAY_SIZE(server->cb_break));
40908e0e7c8SDavid Howells 
41008e0e7c8SDavid Howells 	_enter("{%zu},", ncallbacks);
41108e0e7c8SDavid Howells 
41208e0e7c8SDavid Howells 	if (ncallbacks == 0)
41308e0e7c8SDavid Howells 		return 0;
41408e0e7c8SDavid Howells 	if (ncallbacks > AFSCBMAX)
41508e0e7c8SDavid Howells 		ncallbacks = AFSCBMAX;
41608e0e7c8SDavid Howells 
41708e0e7c8SDavid Howells 	_debug("break %zu callbacks", ncallbacks);
41808e0e7c8SDavid Howells 
41908e0e7c8SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks,
42008e0e7c8SDavid Howells 				   12 + ncallbacks * 6 * 4, 0);
42108e0e7c8SDavid Howells 	if (!call)
42208e0e7c8SDavid Howells 		return -ENOMEM;
42308e0e7c8SDavid Howells 
42408e0e7c8SDavid Howells 	call->service_id = FS_SERVICE;
42508e0e7c8SDavid Howells 	call->port = htons(AFS_FS_PORT);
42608e0e7c8SDavid Howells 
42708e0e7c8SDavid Howells 	/* marshall the parameters */
42808e0e7c8SDavid Howells 	bp = call->request;
42908e0e7c8SDavid Howells 	tp = bp + 2 + ncallbacks * 3;
43008e0e7c8SDavid Howells 	*bp++ = htonl(FSGIVEUPCALLBACKS);
43108e0e7c8SDavid Howells 	*bp++ = htonl(ncallbacks);
43208e0e7c8SDavid Howells 	*tp++ = htonl(ncallbacks);
43308e0e7c8SDavid Howells 
43408e0e7c8SDavid Howells 	atomic_sub(ncallbacks, &server->cb_break_n);
43508e0e7c8SDavid Howells 	for (loop = ncallbacks; loop > 0; loop--) {
43608e0e7c8SDavid Howells 		struct afs_callback *cb =
43708e0e7c8SDavid Howells 			&server->cb_break[server->cb_break_tail];
43808e0e7c8SDavid Howells 
43908e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vid);
44008e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.vnode);
44108e0e7c8SDavid Howells 		*bp++ = htonl(cb->fid.unique);
44208e0e7c8SDavid Howells 		*tp++ = htonl(cb->version);
44308e0e7c8SDavid Howells 		*tp++ = htonl(cb->expiry);
44408e0e7c8SDavid Howells 		*tp++ = htonl(cb->type);
44508e0e7c8SDavid Howells 		smp_mb();
44608e0e7c8SDavid Howells 		server->cb_break_tail =
44708e0e7c8SDavid Howells 			(server->cb_break_tail + 1) &
44808e0e7c8SDavid Howells 			(ARRAY_SIZE(server->cb_break) - 1);
449ec26815aSDavid Howells 	}
45008e0e7c8SDavid Howells 
45108e0e7c8SDavid Howells 	ASSERT(ncallbacks > 0);
45208e0e7c8SDavid Howells 	wake_up_nr(&server->cb_break_waitq, ncallbacks);
45308e0e7c8SDavid Howells 
45408e0e7c8SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
45508e0e7c8SDavid Howells }
456260a9803SDavid Howells 
457260a9803SDavid Howells /*
458260a9803SDavid Howells  * deliver reply data to an FS.CreateFile or an FS.MakeDir
459260a9803SDavid Howells  */
460260a9803SDavid Howells static int afs_deliver_fs_create_vnode(struct afs_call *call,
461260a9803SDavid Howells 				       struct sk_buff *skb, bool last)
462260a9803SDavid Howells {
463260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
464260a9803SDavid Howells 	const __be32 *bp;
465260a9803SDavid Howells 
466260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
467260a9803SDavid Howells 
468260a9803SDavid Howells 	afs_transfer_reply(call, skb);
469260a9803SDavid Howells 	if (!last)
470260a9803SDavid Howells 		return 0;
471260a9803SDavid Howells 
472260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
473260a9803SDavid Howells 		return -EBADMSG;
474260a9803SDavid Howells 
475260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
476260a9803SDavid Howells 	bp = call->buffer;
477260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
478260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL);
479260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
480260a9803SDavid Howells 	xdr_decode_AFSCallBack_raw(&bp, call->reply4);
481260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
482260a9803SDavid Howells 
483260a9803SDavid Howells 	_leave(" = 0 [done]");
484260a9803SDavid Howells 	return 0;
485260a9803SDavid Howells }
486260a9803SDavid Howells 
487260a9803SDavid Howells /*
488260a9803SDavid Howells  * FS.CreateFile and FS.MakeDir operation type
489260a9803SDavid Howells  */
490260a9803SDavid Howells static const struct afs_call_type afs_RXFSCreateXXXX = {
491260a9803SDavid Howells 	.name		= "FS.CreateXXXX",
492260a9803SDavid Howells 	.deliver	= afs_deliver_fs_create_vnode,
493260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
494260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
495260a9803SDavid Howells };
496260a9803SDavid Howells 
497260a9803SDavid Howells /*
498260a9803SDavid Howells  * create a file or make a directory
499260a9803SDavid Howells  */
500260a9803SDavid Howells int afs_fs_create(struct afs_server *server,
501260a9803SDavid Howells 		  struct key *key,
502260a9803SDavid Howells 		  struct afs_vnode *vnode,
503260a9803SDavid Howells 		  const char *name,
504260a9803SDavid Howells 		  umode_t mode,
505260a9803SDavid Howells 		  struct afs_fid *newfid,
506260a9803SDavid Howells 		  struct afs_file_status *newstatus,
507260a9803SDavid Howells 		  struct afs_callback *newcb,
508260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
509260a9803SDavid Howells {
510260a9803SDavid Howells 	struct afs_call *call;
511260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
512260a9803SDavid Howells 	__be32 *bp;
513260a9803SDavid Howells 
514260a9803SDavid Howells 	_enter("");
515260a9803SDavid Howells 
516260a9803SDavid Howells 	namesz = strlen(name);
517260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
518260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (6 * 4);
519260a9803SDavid Howells 
520260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSCreateXXXX, reqsz,
521260a9803SDavid Howells 				   (3 + 21 + 21 + 3 + 6) * 4);
522260a9803SDavid Howells 	if (!call)
523260a9803SDavid Howells 		return -ENOMEM;
524260a9803SDavid Howells 
525260a9803SDavid Howells 	call->key = key;
526260a9803SDavid Howells 	call->reply = vnode;
527260a9803SDavid Howells 	call->reply2 = newfid;
528260a9803SDavid Howells 	call->reply3 = newstatus;
529260a9803SDavid Howells 	call->reply4 = newcb;
530260a9803SDavid Howells 	call->service_id = FS_SERVICE;
531260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
532260a9803SDavid Howells 
533260a9803SDavid Howells 	/* marshall the parameters */
534260a9803SDavid Howells 	bp = call->request;
535260a9803SDavid Howells 	*bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE);
536260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
537260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
538260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
539260a9803SDavid Howells 	*bp++ = htonl(namesz);
540260a9803SDavid Howells 	memcpy(bp, name, namesz);
541260a9803SDavid Howells 	bp = (void *) bp + namesz;
542260a9803SDavid Howells 	if (padsz > 0) {
543260a9803SDavid Howells 		memset(bp, 0, padsz);
544260a9803SDavid Howells 		bp = (void *) bp + padsz;
545260a9803SDavid Howells 	}
546260a9803SDavid Howells 	*bp++ = htonl(AFS_SET_MODE);
547260a9803SDavid Howells 	*bp++ = 0; /* mtime */
548260a9803SDavid Howells 	*bp++ = 0; /* owner */
549260a9803SDavid Howells 	*bp++ = 0; /* group */
550260a9803SDavid Howells 	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
551260a9803SDavid Howells 	*bp++ = 0; /* segment size */
552260a9803SDavid Howells 
553260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
554260a9803SDavid Howells }
555260a9803SDavid Howells 
556260a9803SDavid Howells /*
557260a9803SDavid Howells  * deliver reply data to an FS.RemoveFile or FS.RemoveDir
558260a9803SDavid Howells  */
559260a9803SDavid Howells static int afs_deliver_fs_remove(struct afs_call *call,
560260a9803SDavid Howells 				 struct sk_buff *skb, bool last)
561260a9803SDavid Howells {
562260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
563260a9803SDavid Howells 	const __be32 *bp;
564260a9803SDavid Howells 
565260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
566260a9803SDavid Howells 
567260a9803SDavid Howells 	afs_transfer_reply(call, skb);
568260a9803SDavid Howells 	if (!last)
569260a9803SDavid Howells 		return 0;
570260a9803SDavid Howells 
571260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
572260a9803SDavid Howells 		return -EBADMSG;
573260a9803SDavid Howells 
574260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
575260a9803SDavid Howells 	bp = call->buffer;
576260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
577260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
578260a9803SDavid Howells 
579260a9803SDavid Howells 	_leave(" = 0 [done]");
580260a9803SDavid Howells 	return 0;
581260a9803SDavid Howells }
582260a9803SDavid Howells 
583260a9803SDavid Howells /*
584260a9803SDavid Howells  * FS.RemoveDir/FS.RemoveFile operation type
585260a9803SDavid Howells  */
586260a9803SDavid Howells static const struct afs_call_type afs_RXFSRemoveXXXX = {
587260a9803SDavid Howells 	.name		= "FS.RemoveXXXX",
588260a9803SDavid Howells 	.deliver	= afs_deliver_fs_remove,
589260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
590260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
591260a9803SDavid Howells };
592260a9803SDavid Howells 
593260a9803SDavid Howells /*
594260a9803SDavid Howells  * remove a file or directory
595260a9803SDavid Howells  */
596260a9803SDavid Howells int afs_fs_remove(struct afs_server *server,
597260a9803SDavid Howells 		  struct key *key,
598260a9803SDavid Howells 		  struct afs_vnode *vnode,
599260a9803SDavid Howells 		  const char *name,
600260a9803SDavid Howells 		  bool isdir,
601260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
602260a9803SDavid Howells {
603260a9803SDavid Howells 	struct afs_call *call;
604260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
605260a9803SDavid Howells 	__be32 *bp;
606260a9803SDavid Howells 
607260a9803SDavid Howells 	_enter("");
608260a9803SDavid Howells 
609260a9803SDavid Howells 	namesz = strlen(name);
610260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
611260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz;
612260a9803SDavid Howells 
613260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4);
614260a9803SDavid Howells 	if (!call)
615260a9803SDavid Howells 		return -ENOMEM;
616260a9803SDavid Howells 
617260a9803SDavid Howells 	call->key = key;
618260a9803SDavid Howells 	call->reply = vnode;
619260a9803SDavid Howells 	call->service_id = FS_SERVICE;
620260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
621260a9803SDavid Howells 
622260a9803SDavid Howells 	/* marshall the parameters */
623260a9803SDavid Howells 	bp = call->request;
624260a9803SDavid Howells 	*bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE);
625260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
626260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
627260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
628260a9803SDavid Howells 	*bp++ = htonl(namesz);
629260a9803SDavid Howells 	memcpy(bp, name, namesz);
630260a9803SDavid Howells 	bp = (void *) bp + namesz;
631260a9803SDavid Howells 	if (padsz > 0) {
632260a9803SDavid Howells 		memset(bp, 0, padsz);
633260a9803SDavid Howells 		bp = (void *) bp + padsz;
634260a9803SDavid Howells 	}
635260a9803SDavid Howells 
636260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
637260a9803SDavid Howells }
638260a9803SDavid Howells 
639260a9803SDavid Howells /*
640260a9803SDavid Howells  * deliver reply data to an FS.Link
641260a9803SDavid Howells  */
642260a9803SDavid Howells static int afs_deliver_fs_link(struct afs_call *call,
643260a9803SDavid Howells 			       struct sk_buff *skb, bool last)
644260a9803SDavid Howells {
645260a9803SDavid Howells 	struct afs_vnode *dvnode = call->reply, *vnode = call->reply2;
646260a9803SDavid Howells 	const __be32 *bp;
647260a9803SDavid Howells 
648260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
649260a9803SDavid Howells 
650260a9803SDavid Howells 	afs_transfer_reply(call, skb);
651260a9803SDavid Howells 	if (!last)
652260a9803SDavid Howells 		return 0;
653260a9803SDavid Howells 
654260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
655260a9803SDavid Howells 		return -EBADMSG;
656260a9803SDavid Howells 
657260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
658260a9803SDavid Howells 	bp = call->buffer;
659260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
660260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode);
661260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
662260a9803SDavid Howells 
663260a9803SDavid Howells 	_leave(" = 0 [done]");
664260a9803SDavid Howells 	return 0;
665260a9803SDavid Howells }
666260a9803SDavid Howells 
667260a9803SDavid Howells /*
668260a9803SDavid Howells  * FS.Link operation type
669260a9803SDavid Howells  */
670260a9803SDavid Howells static const struct afs_call_type afs_RXFSLink = {
671260a9803SDavid Howells 	.name		= "FS.Link",
672260a9803SDavid Howells 	.deliver	= afs_deliver_fs_link,
673260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
674260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
675260a9803SDavid Howells };
676260a9803SDavid Howells 
677260a9803SDavid Howells /*
678260a9803SDavid Howells  * make a hard link
679260a9803SDavid Howells  */
680260a9803SDavid Howells int afs_fs_link(struct afs_server *server,
681260a9803SDavid Howells 		struct key *key,
682260a9803SDavid Howells 		struct afs_vnode *dvnode,
683260a9803SDavid Howells 		struct afs_vnode *vnode,
684260a9803SDavid Howells 		const char *name,
685260a9803SDavid Howells 		const struct afs_wait_mode *wait_mode)
686260a9803SDavid Howells {
687260a9803SDavid Howells 	struct afs_call *call;
688260a9803SDavid Howells 	size_t namesz, reqsz, padsz;
689260a9803SDavid Howells 	__be32 *bp;
690260a9803SDavid Howells 
691260a9803SDavid Howells 	_enter("");
692260a9803SDavid Howells 
693260a9803SDavid Howells 	namesz = strlen(name);
694260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
695260a9803SDavid Howells 	reqsz = (5 * 4) + namesz + padsz + (3 * 4);
696260a9803SDavid Howells 
697260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
698260a9803SDavid Howells 	if (!call)
699260a9803SDavid Howells 		return -ENOMEM;
700260a9803SDavid Howells 
701260a9803SDavid Howells 	call->key = key;
702260a9803SDavid Howells 	call->reply = dvnode;
703260a9803SDavid Howells 	call->reply2 = vnode;
704260a9803SDavid Howells 	call->service_id = FS_SERVICE;
705260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
706260a9803SDavid Howells 
707260a9803SDavid Howells 	/* marshall the parameters */
708260a9803SDavid Howells 	bp = call->request;
709260a9803SDavid Howells 	*bp++ = htonl(FSLINK);
710260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vid);
711260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.vnode);
712260a9803SDavid Howells 	*bp++ = htonl(dvnode->fid.unique);
713260a9803SDavid Howells 	*bp++ = htonl(namesz);
714260a9803SDavid Howells 	memcpy(bp, name, namesz);
715260a9803SDavid Howells 	bp = (void *) bp + namesz;
716260a9803SDavid Howells 	if (padsz > 0) {
717260a9803SDavid Howells 		memset(bp, 0, padsz);
718260a9803SDavid Howells 		bp = (void *) bp + padsz;
719260a9803SDavid Howells 	}
720260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
721260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
722260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
723260a9803SDavid Howells 
724260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
725260a9803SDavid Howells }
726260a9803SDavid Howells 
727260a9803SDavid Howells /*
728260a9803SDavid Howells  * deliver reply data to an FS.Symlink
729260a9803SDavid Howells  */
730260a9803SDavid Howells static int afs_deliver_fs_symlink(struct afs_call *call,
731260a9803SDavid Howells 				  struct sk_buff *skb, bool last)
732260a9803SDavid Howells {
733260a9803SDavid Howells 	struct afs_vnode *vnode = call->reply;
734260a9803SDavid Howells 	const __be32 *bp;
735260a9803SDavid Howells 
736260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
737260a9803SDavid Howells 
738260a9803SDavid Howells 	afs_transfer_reply(call, skb);
739260a9803SDavid Howells 	if (!last)
740260a9803SDavid Howells 		return 0;
741260a9803SDavid Howells 
742260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
743260a9803SDavid Howells 		return -EBADMSG;
744260a9803SDavid Howells 
745260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
746260a9803SDavid Howells 	bp = call->buffer;
747260a9803SDavid Howells 	xdr_decode_AFSFid(&bp, call->reply2);
748260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL);
749260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode);
750260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
751260a9803SDavid Howells 
752260a9803SDavid Howells 	_leave(" = 0 [done]");
753260a9803SDavid Howells 	return 0;
754260a9803SDavid Howells }
755260a9803SDavid Howells 
756260a9803SDavid Howells /*
757260a9803SDavid Howells  * FS.Symlink operation type
758260a9803SDavid Howells  */
759260a9803SDavid Howells static const struct afs_call_type afs_RXFSSymlink = {
760260a9803SDavid Howells 	.name		= "FS.Symlink",
761260a9803SDavid Howells 	.deliver	= afs_deliver_fs_symlink,
762260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
763260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
764260a9803SDavid Howells };
765260a9803SDavid Howells 
766260a9803SDavid Howells /*
767260a9803SDavid Howells  * create a symbolic link
768260a9803SDavid Howells  */
769260a9803SDavid Howells int afs_fs_symlink(struct afs_server *server,
770260a9803SDavid Howells 		   struct key *key,
771260a9803SDavid Howells 		   struct afs_vnode *vnode,
772260a9803SDavid Howells 		   const char *name,
773260a9803SDavid Howells 		   const char *contents,
774260a9803SDavid Howells 		   struct afs_fid *newfid,
775260a9803SDavid Howells 		   struct afs_file_status *newstatus,
776260a9803SDavid Howells 		   const struct afs_wait_mode *wait_mode)
777260a9803SDavid Howells {
778260a9803SDavid Howells 	struct afs_call *call;
779260a9803SDavid Howells 	size_t namesz, reqsz, padsz, c_namesz, c_padsz;
780260a9803SDavid Howells 	__be32 *bp;
781260a9803SDavid Howells 
782260a9803SDavid Howells 	_enter("");
783260a9803SDavid Howells 
784260a9803SDavid Howells 	namesz = strlen(name);
785260a9803SDavid Howells 	padsz = (4 - (namesz & 3)) & 3;
786260a9803SDavid Howells 
787260a9803SDavid Howells 	c_namesz = strlen(contents);
788260a9803SDavid Howells 	c_padsz = (4 - (c_namesz & 3)) & 3;
789260a9803SDavid Howells 
790260a9803SDavid Howells 	reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
791260a9803SDavid Howells 
792260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSSymlink, reqsz,
793260a9803SDavid Howells 				   (3 + 21 + 21 + 6) * 4);
794260a9803SDavid Howells 	if (!call)
795260a9803SDavid Howells 		return -ENOMEM;
796260a9803SDavid Howells 
797260a9803SDavid Howells 	call->key = key;
798260a9803SDavid Howells 	call->reply = vnode;
799260a9803SDavid Howells 	call->reply2 = newfid;
800260a9803SDavid Howells 	call->reply3 = newstatus;
801260a9803SDavid Howells 	call->service_id = FS_SERVICE;
802260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
803260a9803SDavid Howells 
804260a9803SDavid Howells 	/* marshall the parameters */
805260a9803SDavid Howells 	bp = call->request;
806260a9803SDavid Howells 	*bp++ = htonl(FSSYMLINK);
807260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vid);
808260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.vnode);
809260a9803SDavid Howells 	*bp++ = htonl(vnode->fid.unique);
810260a9803SDavid Howells 	*bp++ = htonl(namesz);
811260a9803SDavid Howells 	memcpy(bp, name, namesz);
812260a9803SDavid Howells 	bp = (void *) bp + namesz;
813260a9803SDavid Howells 	if (padsz > 0) {
814260a9803SDavid Howells 		memset(bp, 0, padsz);
815260a9803SDavid Howells 		bp = (void *) bp + padsz;
816260a9803SDavid Howells 	}
817260a9803SDavid Howells 	*bp++ = htonl(c_namesz);
818260a9803SDavid Howells 	memcpy(bp, contents, c_namesz);
819260a9803SDavid Howells 	bp = (void *) bp + c_namesz;
820260a9803SDavid Howells 	if (c_padsz > 0) {
821260a9803SDavid Howells 		memset(bp, 0, c_padsz);
822260a9803SDavid Howells 		bp = (void *) bp + c_padsz;
823260a9803SDavid Howells 	}
824260a9803SDavid Howells 	*bp++ = htonl(AFS_SET_MODE);
825260a9803SDavid Howells 	*bp++ = 0; /* mtime */
826260a9803SDavid Howells 	*bp++ = 0; /* owner */
827260a9803SDavid Howells 	*bp++ = 0; /* group */
828260a9803SDavid Howells 	*bp++ = htonl(S_IRWXUGO); /* unix mode */
829260a9803SDavid Howells 	*bp++ = 0; /* segment size */
830260a9803SDavid Howells 
831260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
832260a9803SDavid Howells }
833260a9803SDavid Howells 
834260a9803SDavid Howells /*
835260a9803SDavid Howells  * deliver reply data to an FS.Rename
836260a9803SDavid Howells  */
837260a9803SDavid Howells static int afs_deliver_fs_rename(struct afs_call *call,
838260a9803SDavid Howells 				  struct sk_buff *skb, bool last)
839260a9803SDavid Howells {
840260a9803SDavid Howells 	struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2;
841260a9803SDavid Howells 	const __be32 *bp;
842260a9803SDavid Howells 
843260a9803SDavid Howells 	_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
844260a9803SDavid Howells 
845260a9803SDavid Howells 	afs_transfer_reply(call, skb);
846260a9803SDavid Howells 	if (!last)
847260a9803SDavid Howells 		return 0;
848260a9803SDavid Howells 
849260a9803SDavid Howells 	if (call->reply_size != call->reply_max)
850260a9803SDavid Howells 		return -EBADMSG;
851260a9803SDavid Howells 
852260a9803SDavid Howells 	/* unmarshall the reply once we've received all of it */
853260a9803SDavid Howells 	bp = call->buffer;
854260a9803SDavid Howells 	xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode);
855260a9803SDavid Howells 	if (new_dvnode != orig_dvnode)
856260a9803SDavid Howells 		xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode);
857260a9803SDavid Howells 	/* xdr_decode_AFSVolSync(&bp, call->replyX); */
858260a9803SDavid Howells 
859260a9803SDavid Howells 	_leave(" = 0 [done]");
860260a9803SDavid Howells 	return 0;
861260a9803SDavid Howells }
862260a9803SDavid Howells 
863260a9803SDavid Howells /*
864260a9803SDavid Howells  * FS.Rename operation type
865260a9803SDavid Howells  */
866260a9803SDavid Howells static const struct afs_call_type afs_RXFSRename = {
867260a9803SDavid Howells 	.name		= "FS.Rename",
868260a9803SDavid Howells 	.deliver	= afs_deliver_fs_rename,
869260a9803SDavid Howells 	.abort_to_error	= afs_abort_to_error,
870260a9803SDavid Howells 	.destructor	= afs_flat_call_destructor,
871260a9803SDavid Howells };
872260a9803SDavid Howells 
873260a9803SDavid Howells /*
874260a9803SDavid Howells  * create a symbolic link
875260a9803SDavid Howells  */
876260a9803SDavid Howells int afs_fs_rename(struct afs_server *server,
877260a9803SDavid Howells 		  struct key *key,
878260a9803SDavid Howells 		  struct afs_vnode *orig_dvnode,
879260a9803SDavid Howells 		  const char *orig_name,
880260a9803SDavid Howells 		  struct afs_vnode *new_dvnode,
881260a9803SDavid Howells 		  const char *new_name,
882260a9803SDavid Howells 		  const struct afs_wait_mode *wait_mode)
883260a9803SDavid Howells {
884260a9803SDavid Howells 	struct afs_call *call;
885260a9803SDavid Howells 	size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
886260a9803SDavid Howells 	__be32 *bp;
887260a9803SDavid Howells 
888260a9803SDavid Howells 	_enter("");
889260a9803SDavid Howells 
890260a9803SDavid Howells 	o_namesz = strlen(orig_name);
891260a9803SDavid Howells 	o_padsz = (4 - (o_namesz & 3)) & 3;
892260a9803SDavid Howells 
893260a9803SDavid Howells 	n_namesz = strlen(new_name);
894260a9803SDavid Howells 	n_padsz = (4 - (n_namesz & 3)) & 3;
895260a9803SDavid Howells 
896260a9803SDavid Howells 	reqsz = (4 * 4) +
897260a9803SDavid Howells 		4 + o_namesz + o_padsz +
898260a9803SDavid Howells 		(3 * 4) +
899260a9803SDavid Howells 		4 + n_namesz + n_padsz;
900260a9803SDavid Howells 
901260a9803SDavid Howells 	call = afs_alloc_flat_call(&afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
902260a9803SDavid Howells 	if (!call)
903260a9803SDavid Howells 		return -ENOMEM;
904260a9803SDavid Howells 
905260a9803SDavid Howells 	call->key = key;
906260a9803SDavid Howells 	call->reply = orig_dvnode;
907260a9803SDavid Howells 	call->reply2 = new_dvnode;
908260a9803SDavid Howells 	call->service_id = FS_SERVICE;
909260a9803SDavid Howells 	call->port = htons(AFS_FS_PORT);
910260a9803SDavid Howells 
911260a9803SDavid Howells 	/* marshall the parameters */
912260a9803SDavid Howells 	bp = call->request;
913260a9803SDavid Howells 	*bp++ = htonl(FSRENAME);
914260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vid);
915260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.vnode);
916260a9803SDavid Howells 	*bp++ = htonl(orig_dvnode->fid.unique);
917260a9803SDavid Howells 	*bp++ = htonl(o_namesz);
918260a9803SDavid Howells 	memcpy(bp, orig_name, o_namesz);
919260a9803SDavid Howells 	bp = (void *) bp + o_namesz;
920260a9803SDavid Howells 	if (o_padsz > 0) {
921260a9803SDavid Howells 		memset(bp, 0, o_padsz);
922260a9803SDavid Howells 		bp = (void *) bp + o_padsz;
923260a9803SDavid Howells 	}
924260a9803SDavid Howells 
925260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vid);
926260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.vnode);
927260a9803SDavid Howells 	*bp++ = htonl(new_dvnode->fid.unique);
928260a9803SDavid Howells 	*bp++ = htonl(n_namesz);
929260a9803SDavid Howells 	memcpy(bp, new_name, n_namesz);
930260a9803SDavid Howells 	bp = (void *) bp + n_namesz;
931260a9803SDavid Howells 	if (n_padsz > 0) {
932260a9803SDavid Howells 		memset(bp, 0, n_padsz);
933260a9803SDavid Howells 		bp = (void *) bp + n_padsz;
934260a9803SDavid Howells 	}
935260a9803SDavid Howells 
936260a9803SDavid Howells 	return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
937260a9803SDavid Howells }
938