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