0d884bc59a4e8e2c556d3b4bbc8ada1aba7b8ab3
[openafs.git] / src / vol / fssync-client.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  *
9  * Portions Copyright (c) 2006,2008 Sine Nomine Associates
10  */
11
12 /*
13         System:         VICE-TWO
14         Module:         fssync.c
15         Institution:    The Information Technology Center, Carnegie-Mellon University
16
17  */
18
19 #ifndef AFS_PTHREAD_ENV
20 #define USUAL_PRIORITY (LWP_MAX_PRIORITY - 2)
21
22 /*
23  * stack size increased from 8K because the HP machine seemed to have trouble
24  * with the smaller stack
25  */
26 #define USUAL_STACK_SIZE        (24 * 1024)
27 #endif /* !AFS_PTHREAD_ENV */
28
29 /*
30    fssync-client.c
31    File server synchronization with external volume utilities.
32    client-side implementation
33  */
34
35 #include <afsconfig.h>
36 #include <afs/param.h>
37
38 #include <roken.h>
39
40 #include <afs/afs_assert.h>
41 #include <rx/xdr.h>
42 #include <afs/afsint.h>
43 #include <afs/errors.h>
44 #include <afs/afssyscalls.h>
45
46 #include "nfs.h"
47 #include "daemon_com.h"
48 #include "fssync.h"
49 #include "lwp.h"
50 #include "lock.h"
51 #include "ihandle.h"
52 #include "vnode.h"
53 #include "volume.h"
54 #include "partition.h"
55 #include "common.h"
56
57 #ifdef FSSYNC_BUILD_CLIENT
58
59 extern int LogLevel;
60
61 static SYNC_client_state fssync_state =
62     { -1,                    /* file descriptor */
63       FSSYNC_ENDPOINT_DECL,  /* server endpoint */
64       FSYNC_PROTO_VERSION,   /* protocol version */
65       5,                     /* connect retry limit */
66       120,                   /* hard timeout */
67       "FSSYNC",              /* protocol name string */
68     };
69
70 #ifdef AFS_PTHREAD_ENV
71 static pthread_mutex_t vol_fsync_mutex;
72 static volatile int vol_fsync_mutex_init = 0;
73 #define VFSYNC_LOCK MUTEX_ENTER(&vol_fsync_mutex)
74 #define VFSYNC_UNLOCK MUTEX_EXIT(&vol_fsync_mutex)
75 #else
76 #define VFSYNC_LOCK
77 #define VFSYNC_UNLOCK
78 #endif
79
80 int
81 FSYNC_clientInit(void)
82 {
83 #ifdef AFS_PTHREAD_ENV
84     /* this is safe since it gets called with VOL_LOCK held, or before we go multithreaded */
85     if (!vol_fsync_mutex_init) {
86         MUTEX_INIT(&vol_fsync_mutex, "vol fsync", MUTEX_DEFAULT, 0);
87         vol_fsync_mutex_init = 1;
88     }
89 #endif
90     return SYNC_connect(&fssync_state);
91 }
92
93 void
94 FSYNC_clientFinis(void)
95 {
96     SYNC_closeChannel(&fssync_state);
97 }
98
99 int
100 FSYNC_clientChildProcReconnect(void)
101 {
102     return SYNC_reconnect(&fssync_state);
103 }
104
105 /* fsync client interface */
106 afs_int32
107 FSYNC_askfs(SYNC_command * com, SYNC_response * res)
108 {
109     afs_int32 code;
110
111     VFSYNC_LOCK;
112     code = SYNC_ask(&fssync_state, com, res);
113     VFSYNC_UNLOCK;
114
115     switch (code) {
116     case SYNC_OK:
117     case SYNC_FAILED:
118         break;
119     case SYNC_COM_ERROR:
120     case SYNC_BAD_COMMAND:
121         Log("FSYNC_askfs: fatal FSSYNC protocol error; volume management functionality disabled until next fileserver restart\n");
122         break;
123     case SYNC_DENIED:
124         Log("FSYNC_askfs: FSSYNC request denied for reason=%d\n", res->hdr.reason);
125         break;
126     default:
127         Log("FSYNC_askfs: unknown protocol response %d\n", code);
128         break;
129     }
130     return code;
131 }
132
133
134 /**
135  *  FSSYNC volume operations client interface.
136  *
137  * @param[in]    volume     volume id
138  * @param[in]    partName   partition name string
139  * @param[in]    com        FSSYNC command code
140  * @param[in]    reason     FSSYNC reason sub-code
141  * @param[out]   res        response message
142  *
143  * @return operation status
144  *    @retval SYNC_OK  success
145  */
146 afs_int32
147 FSYNC_GenericOp(void * ext_hdr, size_t ext_len,
148               int command, int reason,
149               SYNC_response * res_in)
150 {
151     SYNC_response res_l, *res;
152     SYNC_command com;
153
154     if (res_in) {
155         res = res_in;
156     } else {
157         res = &res_l;
158         res_l.payload.buf = NULL;
159         res_l.payload.len = 0;
160     }
161
162     memset(&com, 0, sizeof(com));
163
164     com.hdr.programType = programType;
165     com.hdr.command = command;
166     com.hdr.reason = reason;
167     com.hdr.command_len = sizeof(com.hdr) + ext_len;
168     com.payload.buf = ext_hdr;
169     com.payload.len = ext_len;
170
171     return FSYNC_askfs(&com, res);
172 }
173
174 afs_int32
175 FSYNC_VolOp(VolumeId volume, char * partition,
176             int command, int reason,
177             SYNC_response * res)
178 {
179     FSSYNC_VolOp_hdr vcom;
180
181     memset(&vcom, 0, sizeof(vcom));
182
183     vcom.volume = volume;
184     if (partition)
185         strlcpy(vcom.partName, partition, sizeof(vcom.partName));
186
187     return FSYNC_GenericOp(&vcom, sizeof(vcom), command, reason, res);
188 }
189
190 /**
191  * verify that the fileserver still thinks we have a volume checked out.
192  *
193  * In DAFS, a non-fileserver program accesses a volume by checking it out from
194  * the fileserver (FSYNC_VOL_OFF or FSYNC_VOL_NEEDVOLUME), and then locks the
195  * volume. There is a possibility that the fileserver crashes or restarts for
196  * some reason between volume checkout and locking; if this happens, the
197  * fileserver could attach the volume before we had a chance to lock it. This
198  * function serves to detect if this has happened; it must be called after
199  * volume checkout and locking to make sure the fileserver still thinks we
200  * have the volume. (If it doesn't, we should try to check it out again.)
201  *
202  * @param[in] volume    volume ID
203  * @param[in] partition partition name string
204  * @param[in] command   the command that was used to checkout the volume
205  * @param[in] reason    the reason code used to checkout the volume
206  *
207  * @return operation status
208  *  @retval SYNC_OK the fileserver could not have attached the volume since
209  *                  it was checked out (either it thinks it is still checked
210  *                  out, or it doesn't know about the volume)
211  *  @retval SYNC_DENIED fileserver may have restarted since checkout; checkout
212  *                      should be reattempted
213  *  @retval SYNC_COM_ERROR internal/fatal error
214  */
215 afs_int32
216 FSYNC_VerifyCheckout(VolumeId volume, char * partition,
217                      afs_int32 command, afs_int32 reason)
218 {
219     SYNC_response res;
220     FSSYNC_VolOp_info vop;
221     afs_int32 code;
222     afs_int32 pid;
223
224     res.hdr.response_len = sizeof(res.hdr);
225     res.payload.buf = &vop;
226     res.payload.len = sizeof(vop);
227
228     code = FSYNC_VolOp(volume, partition, FSYNC_VOL_QUERY_VOP, FSYNC_WHATEVER, &res);
229     if (code != SYNC_OK) {
230         if (res.hdr.reason == FSYNC_NO_PENDING_VOL_OP) {
231             Log("FSYNC_VerifyCheckout: fileserver claims no vop for vol %lu "
232                 "part %s; fileserver may have restarted since checkout\n",
233                 afs_printable_uint32_lu(volume), partition);
234             return SYNC_DENIED;
235         }
236
237         if (res.hdr.reason == FSYNC_UNKNOWN_VOLID ||
238             res.hdr.reason == FSYNC_WRONG_PART) {
239             /* if the fileserver does not know about this volume on this
240              * partition, there's no way it could have attached it, so we're
241              * fine */
242             return SYNC_OK;
243         }
244
245         Log("FSYNC_VerifyCheckout: FSYNC_VOL_QUERY_VOP failed for vol %lu "
246             "part %s with code %ld reason %ld\n",
247             afs_printable_uint32_lu(volume), partition,
248             afs_printable_int32_ld(code),
249             afs_printable_int32_ld(res.hdr.reason));
250         return SYNC_COM_ERROR;
251     }
252
253     pid = getpid();
254
255     /* Check if the current vol op is us. Checking pid is probably enough, but
256      * be a little bit paranoid. We could also probably check tid, but I'm not
257      * completely confident of its reliability on all platforms (on pthread
258      * envs, we coerce a pthread_t to an afs_int32, which is not guaranteed
259      * to mean anything significant). */
260
261     if (vop.com.programType == programType && vop.com.pid == pid &&
262         vop.com.command == command && vop.com.reason == reason) {
263
264         /* looks like the current pending vol op is the same one as the one
265          * with which we checked it out. success. */
266         return SYNC_OK;
267     }
268
269     Log("FSYNC_VerifyCheckout: vop for vol %lu part %s does not match "
270         "expectations (got pt %ld pid %ld cmd %ld reason %ld, but expected "
271         "pt %ld pid %ld cmd %ld reason %ld); fileserver may have restarted "
272         "since checkout\n", afs_printable_uint32_lu(volume), partition,
273         afs_printable_int32_ld(vop.com.programType),
274         afs_printable_int32_ld(vop.com.pid),
275         afs_printable_int32_ld(vop.com.command),
276         afs_printable_int32_ld(vop.com.reason),
277         afs_printable_int32_ld(programType),
278         afs_printable_int32_ld(pid),
279         afs_printable_int32_ld(command),
280         afs_printable_int32_ld(reason));
281
282     return SYNC_DENIED;
283 }
284
285 afs_int32
286 FSYNC_StatsOp(FSSYNC_StatsOp_hdr * scom, int command, int reason,
287               SYNC_response * res)
288 {
289     return FSYNC_GenericOp(scom, sizeof(*scom), command, reason, res);
290 }
291
292 /**
293  * query the volume group cache.
294  *
295  * @param[in]  part     vice partition path
296  * @param[in]  volid    volume id
297  * @param[out] qry      query response object
298  * @param[out] res      SYNC response message
299  *
300  * @return operation status
301  *    @retval SYNC_OK success
302  */
303 afs_int32
304 FSYNC_VGCQuery(char * part,
305              VolumeId volid,
306              FSSYNC_VGQry_response_t * qry,
307              SYNC_response *res)
308 {
309     SYNC_response lres;
310
311     if (!res) {
312         res = &lres;
313     }
314
315     res->hdr.response_len = sizeof(res->hdr);
316     res->payload.buf = qry;
317     res->payload.len = sizeof(*qry);
318
319     return FSYNC_VolOp(volid, part, FSYNC_VG_QUERY, 0, res);
320 }
321
322 /**
323  * perform an update operation on the VGC.
324  *
325  * @param[in] parent    rw volume
326  * @param[in] child     volume id to add
327  * @param[in] partition name of vice partition on which this VG resides
328  * @param[in] opcode    FSSYNC VG cache opcode
329  * @param[in] reason    FSSYNC reason code
330  * @param[out] res      SYNC response message
331  *
332  * @return operation status
333  *    @retval SYNC_OK success
334  *
335  * @internal
336  */
337 static afs_int32
338 _FSYNC_VGCUpdate(char * partition,
339                  VolumeId parent,
340                  VolumeId child,
341                  int opcode,
342                  int reason,
343                  SYNC_response *res)
344 {
345     FSSYNC_VGUpdate_command_t vcom;
346
347     memset(&vcom, 0, sizeof(vcom));
348
349     vcom.parent = parent;
350     vcom.child = child;
351     if (partition)
352         strlcpy(vcom.partName, partition, sizeof(vcom.partName));
353
354     return FSYNC_GenericOp(&vcom, sizeof(vcom), opcode, reason, res);
355 }
356
357 /**
358  * Add volume to volume group cache.
359  *
360  * @param[in] parent    rw volume
361  * @param[in] child     volume id to add
362  * @param[in] partition name of vice partition on which this VG resides
363  * @param[in] reason    FSSYNC reason code
364  * @param[out] res      SYNC response message
365  *
366  * @return operation status
367  *    @retval SYNC_OK success
368  */
369 afs_int32
370 FSYNC_VGCAdd(char * partition,
371              VolumeId parent,
372              VolumeId child,
373              int reason,
374              SYNC_response *res)
375 {
376     return _FSYNC_VGCUpdate(partition, parent, child, FSYNC_VG_ADD, reason, res);
377 }
378
379 /**
380  * Delete volume from volume group cache.
381  *
382  * @param[in] parent    rw volume
383  * @param[in] child     volume id to add
384  * @param[in] partition name of vice partition on which this VG resides
385  * @param[in] reason    FSSYNC reason code
386  * @param[out] res      SYNC response message
387  *
388  * @return operation status
389  *    @retval SYNC_OK success
390  */
391 afs_int32
392 FSYNC_VGCDel(char * partition,
393              VolumeId parent,
394              VolumeId child,
395              int reason,
396              SYNC_response *res)
397 {
398     return _FSYNC_VGCUpdate(partition, parent, child, FSYNC_VG_DEL, reason, res);
399 }
400
401 /**
402  * perform an asynchronous volume group scan.
403  *
404  * @param[in] partition   vice partition string
405  * @param[in] reason      FSSYNC reason code
406  *
407  * @note if partition is NULL, all vice partitions will be scanned.
408  *
409  * @return operation status
410  *    @retval SYNC_OK success
411  */
412 afs_int32
413 FSYNC_VGCScan(char * partition, int reason)
414 {
415     int command;
416
417     if (partition == NULL) {
418         command = FSYNC_VG_SCAN_ALL;
419         partition = "";
420     } else {
421         command = FSYNC_VG_SCAN;
422     }
423
424     return FSYNC_VolOp(0, partition, command, reason, NULL);
425 }
426
427 #endif /* FSSYNC_BUILD_CLIENT */