blob: 321b489aa90f1fd0bc25f1d95f9ab5d300c942c7 [file] [log] [blame]
David Howells08e0e7c2007-04-26 15:55:03 -07001/* AFS File Server client stubs
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
David Howells08e0e7c2007-04-26 15:55:03 -07003 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/init.h>
13#include <linux/sched.h>
David Howells08e0e7c2007-04-26 15:55:03 -070014#include <linux/circ_buf.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include "internal.h"
David Howells08e0e7c2007-04-26 15:55:03 -070016#include "afs_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070017
Linus Torvalds1da177e2005-04-16 15:20:36 -070018/*
David Howells08e0e7c2007-04-26 15:55:03 -070019 * decode an AFSFetchStatus block
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 */
David Howells08e0e7c2007-04-26 15:55:03 -070021static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
22 struct afs_vnode *vnode)
Linus Torvalds1da177e2005-04-16 15:20:36 -070023{
David Howells08e0e7c2007-04-26 15:55:03 -070024 const __be32 *bp = *_bp;
25 umode_t mode;
26 u64 data_version;
27 u32 changed = 0; /* becomes non-zero if ctime-type changes seen */
28
29#define EXTRACT(DST) \
30 do { \
31 u32 x = ntohl(*bp++); \
32 changed |= DST - x; \
33 DST = x; \
34 } while (0)
35
36 vnode->status.if_version = ntohl(*bp++);
37 EXTRACT(vnode->status.type);
38 vnode->status.nlink = ntohl(*bp++);
39 EXTRACT(vnode->status.size);
40 data_version = ntohl(*bp++);
41 EXTRACT(vnode->status.author);
42 EXTRACT(vnode->status.owner);
43 EXTRACT(vnode->status.caller_access); /* call ticket dependent */
44 EXTRACT(vnode->status.anon_access);
45 EXTRACT(vnode->status.mode);
46 vnode->status.parent.vid = vnode->fid.vid;
47 EXTRACT(vnode->status.parent.vnode);
48 EXTRACT(vnode->status.parent.unique);
49 bp++; /* seg size */
50 vnode->status.mtime_client = ntohl(*bp++);
51 vnode->status.mtime_server = ntohl(*bp++);
52 bp++; /* group */
53 bp++; /* sync counter */
54 data_version |= (u64) ntohl(*bp++) << 32;
55 bp++; /* spare2 */
56 bp++; /* spare3 */
57 bp++; /* spare4 */
58 *_bp = bp;
59
60 if (changed) {
61 _debug("vnode changed");
62 set_bit(AFS_VNODE_CHANGED, &vnode->flags);
63 vnode->vfs_inode.i_uid = vnode->status.owner;
64 vnode->vfs_inode.i_size = vnode->status.size;
65 vnode->vfs_inode.i_version = vnode->fid.unique;
66
67 vnode->status.mode &= S_IALLUGO;
68 mode = vnode->vfs_inode.i_mode;
69 mode &= ~S_IALLUGO;
70 mode |= vnode->status.mode;
71 vnode->vfs_inode.i_mode = mode;
72 }
73
74 _debug("vnode time %lx, %lx",
75 vnode->status.mtime_client, vnode->status.mtime_server);
76 vnode->vfs_inode.i_ctime.tv_sec = vnode->status.mtime_server;
77 vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime;
78 vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime;
79
80 if (vnode->status.data_version != data_version) {
81 _debug("vnode modified %llx", data_version);
82 vnode->status.data_version = data_version;
83 set_bit(AFS_VNODE_MODIFIED, &vnode->flags);
84 set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 }
David Howellsec268152007-04-26 15:49:28 -070086}
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Linus Torvalds1da177e2005-04-16 15:20:36 -070088/*
David Howells08e0e7c2007-04-26 15:55:03 -070089 * decode an AFSCallBack block
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 */
David Howells08e0e7c2007-04-26 15:55:03 -070091static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092{
David Howells08e0e7c2007-04-26 15:55:03 -070093 const __be32 *bp = *_bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
David Howells08e0e7c2007-04-26 15:55:03 -070095 vnode->cb_version = ntohl(*bp++);
96 vnode->cb_expiry = ntohl(*bp++);
97 vnode->cb_type = ntohl(*bp++);
98 vnode->cb_expires = vnode->cb_expiry + get_seconds();
99 *_bp = bp;
David Howellsec268152007-04-26 15:49:28 -0700100}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*
David Howells08e0e7c2007-04-26 15:55:03 -0700103 * decode an AFSVolSync block
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 */
David Howells08e0e7c2007-04-26 15:55:03 -0700105static void xdr_decode_AFSVolSync(const __be32 **_bp,
106 struct afs_volsync *volsync)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107{
David Howells08e0e7c2007-04-26 15:55:03 -0700108 const __be32 *bp = *_bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
David Howells08e0e7c2007-04-26 15:55:03 -0700110 volsync->creation = ntohl(*bp++);
111 bp++; /* spare2 */
112 bp++; /* spare3 */
113 bp++; /* spare4 */
114 bp++; /* spare5 */
115 bp++; /* spare6 */
116 *_bp = bp;
David Howellsec268152007-04-26 15:49:28 -0700117}
David Howells08e0e7c2007-04-26 15:55:03 -0700118
119/*
120 * deliver reply data to an FS.FetchStatus
121 */
122static int afs_deliver_fs_fetch_status(struct afs_call *call,
123 struct sk_buff *skb, bool last)
124{
125 const __be32 *bp;
126
127 _enter(",,%u", last);
128
129 afs_transfer_reply(call, skb);
130 if (!last)
131 return 0;
132
133 if (call->reply_size != call->reply_max)
134 return -EBADMSG;
135
136 /* unmarshall the reply once we've received all of it */
137 bp = call->buffer;
138 xdr_decode_AFSFetchStatus(&bp, call->reply);
139 xdr_decode_AFSCallBack(&bp, call->reply);
140 if (call->reply2)
141 xdr_decode_AFSVolSync(&bp, call->reply2);
142
143 _leave(" = 0 [done]");
144 return 0;
145}
146
147/*
148 * FS.FetchStatus operation type
149 */
150static const struct afs_call_type afs_RXFSFetchStatus = {
David Howells00d3b7a2007-04-26 15:57:07 -0700151 .name = "FS.FetchStatus",
David Howells08e0e7c2007-04-26 15:55:03 -0700152 .deliver = afs_deliver_fs_fetch_status,
153 .abort_to_error = afs_abort_to_error,
154 .destructor = afs_flat_call_destructor,
155};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157/*
158 * fetch the status information for a file
159 */
David Howells08e0e7c2007-04-26 15:55:03 -0700160int afs_fs_fetch_file_status(struct afs_server *server,
David Howells00d3b7a2007-04-26 15:57:07 -0700161 struct key *key,
David Howells08e0e7c2007-04-26 15:55:03 -0700162 struct afs_vnode *vnode,
163 struct afs_volsync *volsync,
164 const struct afs_wait_mode *wait_mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
David Howells08e0e7c2007-04-26 15:55:03 -0700166 struct afs_call *call;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 __be32 *bp;
168
David Howells00d3b7a2007-04-26 15:57:07 -0700169 _enter(",%x,,,", key_serial(key));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
David Howells08e0e7c2007-04-26 15:55:03 -0700171 call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, 120);
172 if (!call)
173 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174
David Howells00d3b7a2007-04-26 15:57:07 -0700175 call->key = key;
David Howells08e0e7c2007-04-26 15:55:03 -0700176 call->reply = vnode;
177 call->reply2 = volsync;
178 call->service_id = FS_SERVICE;
179 call->port = htons(AFS_FS_PORT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
181 /* marshall the parameters */
David Howells08e0e7c2007-04-26 15:55:03 -0700182 bp = call->request;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 bp[0] = htonl(FSFETCHSTATUS);
184 bp[1] = htonl(vnode->fid.vid);
185 bp[2] = htonl(vnode->fid.vnode);
186 bp[3] = htonl(vnode->fid.unique);
187
David Howells08e0e7c2007-04-26 15:55:03 -0700188 return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
David Howellsec268152007-04-26 15:49:28 -0700189}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191/*
David Howells08e0e7c2007-04-26 15:55:03 -0700192 * deliver reply data to an FS.FetchData
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 */
David Howells08e0e7c2007-04-26 15:55:03 -0700194static int afs_deliver_fs_fetch_data(struct afs_call *call,
195 struct sk_buff *skb, bool last)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
David Howells08e0e7c2007-04-26 15:55:03 -0700197 const __be32 *bp;
198 struct page *page;
199 void *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 int ret;
David Howells08e0e7c2007-04-26 15:55:03 -0700201
202 _enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
203
204 switch (call->unmarshall) {
205 case 0:
206 call->offset = 0;
207 call->unmarshall++;
208
209 /* extract the returned data length */
210 case 1:
211 _debug("extract data length");
212 ret = afs_extract_data(call, skb, last, &call->tmp, 4);
213 switch (ret) {
214 case 0: break;
215 case -EAGAIN: return 0;
216 default: return ret;
217 }
218
219 call->count = ntohl(call->tmp);
220 _debug("DATA length: %u", call->count);
221 if (call->count > PAGE_SIZE)
222 return -EBADMSG;
223 call->offset = 0;
224 call->unmarshall++;
225
226 if (call->count < PAGE_SIZE) {
227 buffer = kmap_atomic(call->reply3, KM_USER0);
228 memset(buffer + PAGE_SIZE - call->count, 0,
229 call->count);
230 kunmap_atomic(buffer, KM_USER0);
231 }
232
233 /* extract the returned data */
234 case 2:
235 _debug("extract data");
236 page = call->reply3;
237 buffer = kmap_atomic(page, KM_USER0);
238 ret = afs_extract_data(call, skb, last, buffer, call->count);
239 kunmap_atomic(buffer, KM_USER0);
240 switch (ret) {
241 case 0: break;
242 case -EAGAIN: return 0;
243 default: return ret;
244 }
245
246 call->offset = 0;
247 call->unmarshall++;
248
249 /* extract the metadata */
250 case 3:
251 ret = afs_extract_data(call, skb, last, call->buffer, 120);
252 switch (ret) {
253 case 0: break;
254 case -EAGAIN: return 0;
255 default: return ret;
256 }
257
258 bp = call->buffer;
259 xdr_decode_AFSFetchStatus(&bp, call->reply);
260 xdr_decode_AFSCallBack(&bp, call->reply);
261 if (call->reply2)
262 xdr_decode_AFSVolSync(&bp, call->reply2);
263
264 call->offset = 0;
265 call->unmarshall++;
266
267 case 4:
268 _debug("trailer");
269 if (skb->len != 0)
270 return -EBADMSG;
271 break;
272 }
273
274 if (!last)
275 return 0;
276
277 _leave(" = 0 [done]");
278 return 0;
279}
280
281/*
282 * FS.FetchData operation type
283 */
284static const struct afs_call_type afs_RXFSFetchData = {
David Howells00d3b7a2007-04-26 15:57:07 -0700285 .name = "FS.FetchData",
David Howells08e0e7c2007-04-26 15:55:03 -0700286 .deliver = afs_deliver_fs_fetch_data,
287 .abort_to_error = afs_abort_to_error,
288 .destructor = afs_flat_call_destructor,
289};
290
291/*
292 * fetch data from a file
293 */
294int afs_fs_fetch_data(struct afs_server *server,
David Howells00d3b7a2007-04-26 15:57:07 -0700295 struct key *key,
David Howells08e0e7c2007-04-26 15:55:03 -0700296 struct afs_vnode *vnode,
297 off_t offset, size_t length,
298 struct page *buffer,
299 struct afs_volsync *volsync,
300 const struct afs_wait_mode *wait_mode)
301{
302 struct afs_call *call;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 __be32 *bp;
304
David Howells08e0e7c2007-04-26 15:55:03 -0700305 _enter("");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
David Howells08e0e7c2007-04-26 15:55:03 -0700307 call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, 120);
308 if (!call)
309 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
David Howells00d3b7a2007-04-26 15:57:07 -0700311 call->key = key;
David Howells08e0e7c2007-04-26 15:55:03 -0700312 call->reply = vnode;
313 call->reply2 = volsync;
314 call->reply3 = buffer;
315 call->service_id = FS_SERVICE;
316 call->port = htons(AFS_FS_PORT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
318 /* marshall the parameters */
David Howells08e0e7c2007-04-26 15:55:03 -0700319 bp = call->request;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 bp[0] = htonl(FSFETCHDATA);
David Howells08e0e7c2007-04-26 15:55:03 -0700321 bp[1] = htonl(vnode->fid.vid);
322 bp[2] = htonl(vnode->fid.vnode);
323 bp[3] = htonl(vnode->fid.unique);
324 bp[4] = htonl(offset);
325 bp[5] = htonl(length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
David Howells08e0e7c2007-04-26 15:55:03 -0700327 return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
David Howellsec268152007-04-26 15:49:28 -0700328}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330/*
David Howells08e0e7c2007-04-26 15:55:03 -0700331 * deliver reply data to an FS.GiveUpCallBacks
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 */
David Howells08e0e7c2007-04-26 15:55:03 -0700333static int afs_deliver_fs_give_up_callbacks(struct afs_call *call,
334 struct sk_buff *skb, bool last)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335{
David Howells08e0e7c2007-04-26 15:55:03 -0700336 _enter(",{%u},%d", skb->len, last);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
David Howells08e0e7c2007-04-26 15:55:03 -0700338 if (skb->len > 0)
339 return -EBADMSG; /* shouldn't be any reply data */
340 return 0;
341}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
David Howells08e0e7c2007-04-26 15:55:03 -0700343/*
344 * FS.GiveUpCallBacks operation type
345 */
346static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
David Howells00d3b7a2007-04-26 15:57:07 -0700347 .name = "FS.GiveUpCallBacks",
David Howells08e0e7c2007-04-26 15:55:03 -0700348 .deliver = afs_deliver_fs_give_up_callbacks,
349 .abort_to_error = afs_abort_to_error,
350 .destructor = afs_flat_call_destructor,
351};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
David Howells08e0e7c2007-04-26 15:55:03 -0700353/*
354 * give up a set of callbacks
355 * - the callbacks are held in the server->cb_break ring
356 */
357int afs_fs_give_up_callbacks(struct afs_server *server,
358 const struct afs_wait_mode *wait_mode)
359{
360 struct afs_call *call;
361 size_t ncallbacks;
362 __be32 *bp, *tp;
363 int loop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
David Howells08e0e7c2007-04-26 15:55:03 -0700365 ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
366 ARRAY_SIZE(server->cb_break));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
David Howells08e0e7c2007-04-26 15:55:03 -0700368 _enter("{%zu},", ncallbacks);
369
370 if (ncallbacks == 0)
371 return 0;
372 if (ncallbacks > AFSCBMAX)
373 ncallbacks = AFSCBMAX;
374
375 _debug("break %zu callbacks", ncallbacks);
376
377 call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks,
378 12 + ncallbacks * 6 * 4, 0);
379 if (!call)
380 return -ENOMEM;
381
382 call->service_id = FS_SERVICE;
383 call->port = htons(AFS_FS_PORT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385 /* marshall the parameters */
David Howells08e0e7c2007-04-26 15:55:03 -0700386 bp = call->request;
387 tp = bp + 2 + ncallbacks * 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 *bp++ = htonl(FSGIVEUPCALLBACKS);
David Howells08e0e7c2007-04-26 15:55:03 -0700389 *bp++ = htonl(ncallbacks);
390 *tp++ = htonl(ncallbacks);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
David Howells08e0e7c2007-04-26 15:55:03 -0700392 atomic_sub(ncallbacks, &server->cb_break_n);
393 for (loop = ncallbacks; loop > 0; loop--) {
394 struct afs_callback *cb =
395 &server->cb_break[server->cb_break_tail];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
David Howells08e0e7c2007-04-26 15:55:03 -0700397 *bp++ = htonl(cb->fid.vid);
398 *bp++ = htonl(cb->fid.vnode);
399 *bp++ = htonl(cb->fid.unique);
400 *tp++ = htonl(cb->version);
401 *tp++ = htonl(cb->expiry);
402 *tp++ = htonl(cb->type);
403 smp_mb();
404 server->cb_break_tail =
405 (server->cb_break_tail + 1) &
406 (ARRAY_SIZE(server->cb_break) - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 }
408
David Howells08e0e7c2007-04-26 15:55:03 -0700409 ASSERT(ncallbacks > 0);
410 wake_up_nr(&server->cb_break_waitq, ncallbacks);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
David Howells08e0e7c2007-04-26 15:55:03 -0700412 return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);
David Howellsec268152007-04-26 15:49:28 -0700413}