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