vol-fssync-server-socket-20090320
[openafs.git] / src / vol / fssync-server.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 #ifndef AFS_PTHREAD_ENV
19 #define USUAL_PRIORITY (LWP_MAX_PRIORITY - 2)
20
21 /*
22  * stack size increased from 8K because the HP machine seemed to have trouble
23  * with the smaller stack
24  */
25 #define USUAL_STACK_SIZE        (24 * 1024)
26 #endif /* !AFS_PTHREAD_ENV */
27
28 /*
29    fssync-server.c
30    File server synchronization with external volume utilities.
31    server-side implementation
32  */
33
34 /* This controls the size of an fd_set; it must be defined early before
35  * the system headers define that type and the macros that operate on it.
36  * Its value should be as large as the maximum file descriptor limit we
37  * are likely to run into on any platform.  Right now, that is 65536
38  * which is the default hard fd limit on Solaris 9 */
39 #ifndef _WIN32
40 #define FD_SETSIZE 65536
41 #endif
42
43 #include <afsconfig.h>
44 #include <afs/param.h>
45
46 RCSID
47     ("$Header$");
48
49 #include <sys/types.h>
50 #include <stdio.h>
51 #ifdef AFS_NT40_ENV
52 #include <winsock2.h>
53 #include <time.h>
54 #else
55 #include <sys/param.h>
56 #include <sys/socket.h>
57 #include <netinet/in.h>
58 #include <netdb.h>
59 #include <sys/time.h>
60 #endif
61 #include <errno.h>
62 #ifdef AFS_PTHREAD_ENV
63 #include <assert.h>
64 #else /* AFS_PTHREAD_ENV */
65 #include <afs/assert.h>
66 #endif /* AFS_PTHREAD_ENV */
67 #include <signal.h>
68 #include <string.h>
69
70 #include <rx/xdr.h>
71 #include <afs/afsint.h>
72 #include "nfs.h"
73 #include <afs/errors.h>
74 #include "daemon_com.h"
75 #include "fssync.h"
76 #include "lwp.h"
77 #include "lock.h"
78 #include <afs/afssyscalls.h>
79 #include "ihandle.h"
80 #include "vnode.h"
81 #include "volume.h"
82 #include "volume_inline.h"
83 #include "partition.h"
84
85 #ifdef HAVE_POLL
86 #include <sys/poll.h>
87 #endif /* HAVE_POLL */
88
89 #ifdef USE_UNIX_SOCKETS
90 #include <sys/un.h>
91 #include <afs/afsutil.h>
92 #endif /* USE_UNIX_SOCKETS */
93
94 #ifdef FSSYNC_BUILD_SERVER
95
96 /*@printflike@*/ extern void Log(const char *format, ...);
97
98 int (*V_BreakVolumeCallbacks) (VolumeId volume);
99
100 #define MAXHANDLERS     4       /* Up to 4 clients; must be at least 2, so that
101                                  * move = dump+restore can run on single server */
102 #define MAXOFFLINEVOLUMES 128   /* This needs to be as big as the maximum
103                                  * number that would be offline for 1 operation.
104                                  * Current winner is salvage, which needs all
105                                  * cloned read-only copies offline when salvaging
106                                  * a single read-write volume */
107
108
109
110 static struct offlineInfo OfflineVolumes[MAXHANDLERS][MAXOFFLINEVOLUMES];
111
112 /**
113  * fssync server socket handle.
114  */
115 static SYNC_server_state_t fssync_server_state = 
116     { -1,                       /* file descriptor */
117       FSSYNC_ENDPOINT_DECL,     /* server endpoint */
118       FSYNC_PROTO_VERSION,      /* protocol version */
119       5,                        /* bind() retry limit */
120       100,                      /* listen() queue depth */
121       "FSSYNC",                 /* protocol name string */
122     };
123
124
125 /* Forward declarations */
126 static void * FSYNC_sync(void *);
127 static void FSYNC_newconnection(osi_socket afd);
128 static void FSYNC_com(osi_socket fd);
129 static void FSYNC_Drop(osi_socket fd);
130 static void AcceptOn(void);
131 static void AcceptOff(void);
132 static void InitHandler(void);
133 static int AddHandler(osi_socket fd, void (*aproc)(osi_socket));
134 static int FindHandler(osi_socket afd);
135 static int FindHandler_r(osi_socket afd);
136 static int RemoveHandler(osi_socket afd);
137 #if defined(HAVE_POLL) && defined (AFS_PTHREAD_ENV)
138 static void CallHandler(struct pollfd *fds, int nfds, int mask);
139 static void GetHandler(struct pollfd *fds, int maxfds, int events, int *nfds);
140 #else
141 static void CallHandler(fd_set * fdsetp);
142 static void GetHandler(fd_set * fdsetp, int *maxfdp);
143 #endif
144 extern int LogLevel;
145
146 static afs_int32 FSYNC_com_VolOp(osi_socket fd, SYNC_command * com, SYNC_response * res);
147
148 #ifdef AFS_DEMAND_ATTACH_FS
149 static afs_int32 FSYNC_com_VolError(FSSYNC_VolOp_command * com, SYNC_response * res);
150 #endif
151 static afs_int32 FSYNC_com_VolOn(FSSYNC_VolOp_command * com, SYNC_response * res);
152 static afs_int32 FSYNC_com_VolOff(FSSYNC_VolOp_command * com, SYNC_response * res);
153 static afs_int32 FSYNC_com_VolMove(FSSYNC_VolOp_command * com, SYNC_response * res);
154 static afs_int32 FSYNC_com_VolBreakCBKs(FSSYNC_VolOp_command * com, SYNC_response * res);
155 static afs_int32 FSYNC_com_VolDone(FSSYNC_VolOp_command * com, SYNC_response * res);
156 static afs_int32 FSYNC_com_VolQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
157 static afs_int32 FSYNC_com_VolHdrQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
158 #ifdef AFS_DEMAND_ATTACH_FS
159 static afs_int32 FSYNC_com_VolOpQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
160 #endif /* AFS_DEMAND_ATTACH_FS */
161
162 static afs_int32 FSYNC_com_VnQry(osi_socket fd, SYNC_command * com, SYNC_response * res);
163
164 static afs_int32 FSYNC_com_StatsOp(osi_socket fd, SYNC_command * com, SYNC_response * res);
165
166 static afs_int32 FSYNC_com_StatsOpGeneral(FSSYNC_StatsOp_command * scom, SYNC_response * res);
167
168 #ifdef AFS_DEMAND_ATTACH_FS
169 static afs_int32 FSYNC_com_StatsOpViceP(FSSYNC_StatsOp_command * scom, SYNC_response * res);
170 static afs_int32 FSYNC_com_StatsOpHash(FSSYNC_StatsOp_command * scom, SYNC_response * res);
171 static afs_int32 FSYNC_com_StatsOpHdr(FSSYNC_StatsOp_command * scom, SYNC_response * res);
172 static afs_int32 FSYNC_com_StatsOpVLRU(FSSYNC_StatsOp_command * scom, SYNC_response * res);
173 #endif
174
175 static void FSYNC_com_to_info(FSSYNC_VolOp_command * vcom, FSSYNC_VolOp_info * info);
176
177 static int FSYNC_partMatch(FSSYNC_VolOp_command * vcom, Volume * vp, int match_anon);
178
179
180 /*
181  * This lock controls access to the handler array. The overhead
182  * is minimal in non-preemptive environments.
183  */
184 struct Lock FSYNC_handler_lock;
185
186 void
187 FSYNC_fsInit(void)
188 {
189 #ifdef AFS_PTHREAD_ENV
190     pthread_t tid;
191     pthread_attr_t tattr;
192 #else /* AFS_PTHREAD_ENV */
193     PROCESS pid;
194 #endif /* AFS_PTHREAD_ENV */
195
196     Lock_Init(&FSYNC_handler_lock);
197
198 #ifdef AFS_PTHREAD_ENV
199     assert(pthread_attr_init(&tattr) == 0);
200     assert(pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED) == 0);
201     assert(pthread_create(&tid, &tattr, FSYNC_sync, NULL) == 0);
202 #else /* AFS_PTHREAD_ENV */
203     assert(LWP_CreateProcess
204            (FSYNC_sync, USUAL_STACK_SIZE, USUAL_PRIORITY, (void *)0,
205             "FSYNC_sync", &pid) == LWP_SUCCESS);
206 #endif /* AFS_PTHREAD_ENV */
207 }
208
209 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
210 static struct pollfd FSYNC_readfds[MAXHANDLERS];
211 #else
212 static fd_set FSYNC_readfds;
213 #endif
214
215
216 static void *
217 FSYNC_sync(void * args)
218 {
219     extern int VInit;
220     int code;
221 #ifdef AFS_PTHREAD_ENV
222     int tid;
223 #endif
224     SYNC_server_state_t * state = &fssync_server_state;
225 #ifdef AFS_DEMAND_ATTACH_FS
226     VThreadOptions_t * thread_opts;
227 #endif
228
229     SYNC_getAddr(&state->endpoint, &state->addr);
230     SYNC_cleanupSock(state);
231
232 #ifndef AFS_NT40_ENV
233     (void)signal(SIGPIPE, SIG_IGN);
234 #endif
235
236 #ifdef AFS_PTHREAD_ENV
237     /* set our 'thread-id' so that the host hold table works */
238     MUTEX_ENTER(&rx_stats_mutex);       /* protects rxi_pthread_hinum */
239     tid = ++rxi_pthread_hinum;
240     MUTEX_EXIT(&rx_stats_mutex);
241     pthread_setspecific(rx_thread_id_key, (void *)tid);
242     Log("Set thread id %d for FSYNC_sync\n", tid);
243 #endif /* AFS_PTHREAD_ENV */
244
245     while (!VInit) {
246         /* Let somebody else run until level > 0.  That doesn't mean that 
247          * all volumes have been attached. */
248 #ifdef AFS_PTHREAD_ENV
249         pthread_yield();
250 #else /* AFS_PTHREAD_ENV */
251         LWP_DispatchProcess();
252 #endif /* AFS_PTHREAD_ENV */
253     }
254     state->fd = SYNC_getSock(&state->endpoint);
255     code = SYNC_bindSock(state);
256     assert(!code);
257
258 #ifdef AFS_DEMAND_ATTACH_FS
259     /*
260      * make sure the volume package is incapable of recursively executing
261      * salvsync calls on this thread, since there is a possibility of
262      * deadlock.
263      */
264     thread_opts = malloc(sizeof(VThreadOptions_t));
265     if (thread_opts == NULL) {
266         Log("failed to allocate memory for thread-specific volume package options structure\n");
267         return NULL;
268     }
269     memcpy(thread_opts, &VThread_defaults, sizeof(VThread_defaults));
270     thread_opts->disallow_salvsync = 1;
271     assert(pthread_setspecific(VThread_key, thread_opts) == 0);
272 #endif
273
274     InitHandler();
275     AcceptOn();
276
277     for (;;) {
278 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
279         int nfds;
280         GetHandler(FSYNC_readfds, MAXHANDLERS, POLLIN|POLLPRI, &nfds);
281         if (poll(FSYNC_readfds, nfds, -1) >=1)
282             CallHandler(FSYNC_readfds, nfds, POLLIN|POLLPRI);
283 #else
284         int maxfd;
285         GetHandler(&FSYNC_readfds, &maxfd);
286         /* Note: check for >= 1 below is essential since IOMGR_select
287          * doesn't have exactly same semantics as select.
288          */
289 #ifdef AFS_PTHREAD_ENV
290         if (select(maxfd + 1, &FSYNC_readfds, NULL, NULL, NULL) >= 1)
291 #else /* AFS_PTHREAD_ENV */
292         if (IOMGR_Select(maxfd + 1, &FSYNC_readfds, NULL, NULL, NULL) >= 1)
293 #endif /* AFS_PTHREAD_ENV */
294             CallHandler(&FSYNC_readfds);
295 #endif
296     }
297     return NULL; /* hush now, little gcc */
298 }
299
300 static void
301 FSYNC_newconnection(osi_socket afd)
302 {
303 #ifdef USE_UNIX_SOCKETS
304     struct sockaddr_un other;
305 #else  /* USE_UNIX_SOCKETS */
306     struct sockaddr_in other;
307 #endif
308     osi_socket fd;
309     socklen_t junk;
310     junk = sizeof(other);
311     fd = accept(afd, (struct sockaddr *)&other, &junk);
312     if (fd == -1) {
313         Log("FSYNC_newconnection:  accept failed, errno==%d\n", errno);
314         assert(1 == 2);
315     } else if (!AddHandler(fd, FSYNC_com)) {
316         AcceptOff();
317         assert(AddHandler(fd, FSYNC_com));
318     }
319 }
320
321 /* this function processes commands from an fssync file descriptor (fd) */
322 afs_int32 FS_cnt = 0;
323 static void
324 FSYNC_com(osi_socket fd)
325 {
326     SYNC_command com;
327     SYNC_response res;
328     SYNC_PROTO_BUF_DECL(com_buf);
329     SYNC_PROTO_BUF_DECL(res_buf);
330
331     memset(&res.hdr, 0, sizeof(res.hdr));
332
333     com.payload.buf = (void *)com_buf;
334     com.payload.len = SYNC_PROTO_MAX_LEN;
335     res.hdr.response_len = sizeof(res.hdr);
336     res.payload.len = SYNC_PROTO_MAX_LEN;
337     res.payload.buf = (void *)res_buf;
338
339     FS_cnt++;
340     if (SYNC_getCom(&fssync_server_state, fd, &com)) {
341         Log("FSYNC_com:  read failed; dropping connection (cnt=%d)\n", FS_cnt);
342         FSYNC_Drop(fd);
343         return;
344     }
345
346     if (com.recv_len < sizeof(com.hdr)) {
347         Log("FSSYNC_com:  invalid protocol message length (%u)\n", com.recv_len);
348         res.hdr.response = SYNC_COM_ERROR;
349         res.hdr.reason = SYNC_REASON_MALFORMED_PACKET;
350         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
351         goto respond;
352     }
353
354     if (com.hdr.proto_version != FSYNC_PROTO_VERSION) {
355         Log("FSYNC_com:  invalid protocol version (%u)\n", com.hdr.proto_version);
356         res.hdr.response = SYNC_COM_ERROR;
357         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
358         goto respond;
359     }
360
361     if (com.hdr.command == SYNC_COM_CHANNEL_CLOSE) {
362         res.hdr.response = SYNC_OK;
363         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
364         goto respond;
365     }
366
367     res.hdr.com_seq = com.hdr.com_seq;
368
369     VOL_LOCK;
370     switch (com.hdr.command) {
371     case FSYNC_VOL_ON:
372     case FSYNC_VOL_ATTACH:
373     case FSYNC_VOL_LEAVE_OFF:
374     case FSYNC_VOL_OFF:
375     case FSYNC_VOL_FORCE_ERROR:
376     case FSYNC_VOL_LISTVOLUMES:
377     case FSYNC_VOL_NEEDVOLUME:
378     case FSYNC_VOL_MOVE:
379     case FSYNC_VOL_BREAKCBKS:
380     case FSYNC_VOL_DONE:
381     case FSYNC_VOL_QUERY:
382     case FSYNC_VOL_QUERY_HDR:
383     case FSYNC_VOL_QUERY_VOP:
384         res.hdr.response = FSYNC_com_VolOp(fd, &com, &res);
385         break;
386     case FSYNC_VOL_STATS_GENERAL:
387     case FSYNC_VOL_STATS_VICEP:
388     case FSYNC_VOL_STATS_HASH:
389     case FSYNC_VOL_STATS_HDR:
390     case FSYNC_VOL_STATS_VLRU:
391         res.hdr.response = FSYNC_com_StatsOp(fd, &com, &res);
392         break;
393     case FSYNC_VOL_QUERY_VNODE:
394         res.hdr.response = FSYNC_com_VnQry(fd, &com, &res);
395         break;
396     default:
397         res.hdr.response = SYNC_BAD_COMMAND;
398         break;
399     }
400     VOL_UNLOCK;
401
402  respond:
403     SYNC_putRes(&fssync_server_state, fd, &res);
404     if (res.hdr.flags & SYNC_FLAG_CHANNEL_SHUTDOWN) {
405         FSYNC_Drop(fd);
406     }
407 }
408
409 static afs_int32
410 FSYNC_com_VolOp(osi_socket fd, SYNC_command * com, SYNC_response * res)
411 {
412     int i;
413     afs_int32 code = SYNC_OK;
414     FSSYNC_VolOp_command vcom;
415
416     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_VolOp_hdr))) {
417         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
418         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
419         return SYNC_COM_ERROR;
420     }
421
422     vcom.hdr = &com->hdr;
423     vcom.vop = (FSSYNC_VolOp_hdr *) com->payload.buf;
424     vcom.com = com;
425
426     vcom.volumes = OfflineVolumes[FindHandler(fd)];
427     for (vcom.v = NULL, i = 0; i < MAXOFFLINEVOLUMES; i++) {
428         if ((vcom.volumes[i].volumeID == vcom.vop->volume) &&
429             (strncmp(vcom.volumes[i].partName, vcom.vop->partName,
430                      sizeof(vcom.volumes[i].partName)) == 0)) {
431             vcom.v = &vcom.volumes[i];
432             break;
433         }
434     }
435
436     switch (com->hdr.command) {
437     case FSYNC_VOL_ON:
438     case FSYNC_VOL_ATTACH:
439     case FSYNC_VOL_LEAVE_OFF:
440         code = FSYNC_com_VolOn(&vcom, res);
441         break;
442     case FSYNC_VOL_OFF:
443     case FSYNC_VOL_NEEDVOLUME:
444         code = FSYNC_com_VolOff(&vcom, res);
445         break;
446     case FSYNC_VOL_LISTVOLUMES:
447         code = SYNC_OK;
448         break;
449     case FSYNC_VOL_MOVE:
450         code = FSYNC_com_VolMove(&vcom, res);
451         break;
452     case FSYNC_VOL_BREAKCBKS:
453         code = FSYNC_com_VolBreakCBKs(&vcom, res);
454         break;
455     case FSYNC_VOL_DONE:
456         code = FSYNC_com_VolDone(&vcom, res);
457         break;
458     case FSYNC_VOL_QUERY:
459         code = FSYNC_com_VolQuery(&vcom, res);
460         break;
461     case FSYNC_VOL_QUERY_HDR:
462         code = FSYNC_com_VolHdrQuery(&vcom, res);
463         break;
464 #ifdef AFS_DEMAND_ATTACH_FS
465     case FSYNC_VOL_FORCE_ERROR:
466         code = FSYNC_com_VolError(&vcom, res);
467         break;
468     case FSYNC_VOL_QUERY_VOP:
469         code = FSYNC_com_VolOpQuery(&vcom, res);
470         break;
471 #endif /* AFS_DEMAND_ATTACH_FS */
472     default:
473         code = SYNC_BAD_COMMAND;
474     }
475
476     return code;
477 }
478
479 /**
480  * service an FSYNC request to bring a volume online.
481  *
482  * @param[in]   vcom  pointer command object
483  * @param[out]  res   object in which to store response packet
484  *
485  * @return operation status
486  *   @retval SYNC_OK volume transitioned online
487  *   @retval SYNC_FAILED invalid command protocol message
488  *   @retval SYNC_DENIED operation could not be completed
489  *
490  * @note this is an FSYNC RPC server stub
491  *
492  * @note this procedure handles the following FSSYNC command codes:
493  *       - FSYNC_VOL_ON
494  *       - FSYNC_VOL_ATTACH
495  *       - FSYNC_VOL_LEAVE_OFF
496  *
497  * @note the supplementary reason code contains additional details.
498  *       When SYNC_DENIED is returned, the specific reason is
499  *       placed in the response packet reason field.
500  *
501  * @internal
502  */
503 static afs_int32
504 FSYNC_com_VolOn(FSSYNC_VolOp_command * vcom, SYNC_response * res)
505 {
506     afs_int32 code = SYNC_OK;
507     char tvolName[VMAXPATHLEN];
508     Volume * vp;
509     Error error;
510
511     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
512         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
513         code = SYNC_FAILED;
514         goto done;
515     }
516
517     /* so, we need to attach the volume */
518
519 #ifdef AFS_DEMAND_ATTACH_FS
520     /* check DAFS permissions */
521     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
522     if (vp &&
523         FSYNC_partMatch(vcom, vp, 1) &&
524         vp->pending_vol_op && 
525         (vcom->hdr->programType != vp->pending_vol_op->com.programType)) {
526         /* a different program has this volume checked out. deny. */
527         Log("FSYNC_VolOn: WARNING: program type %u has attempted to manipulate "
528             "state for volume %u using command code %u while the volume is " 
529             "checked out by program type %u for command code %u.\n",
530             vcom->hdr->programType,
531             vcom->vop->volume,
532             vcom->hdr->command,
533             vp->pending_vol_op->com.programType,
534             vp->pending_vol_op->com.command);
535         code = SYNC_DENIED;
536         res->hdr.reason = FSYNC_EXCLUSIVE;
537         goto done;
538     }
539 #endif
540
541     if (vcom->v)
542         vcom->v->volumeID = 0;
543
544
545     if (vcom->hdr->command == FSYNC_VOL_LEAVE_OFF) {
546         /* nothing much to do if we're leaving the volume offline */
547 #ifdef AFS_DEMAND_ATTACH_FS
548         if (vp) {
549             if (FSYNC_partMatch(vcom, vp, 1)) {
550                 if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
551                     (V_attachState(vp) == VOL_STATE_PREATTACHED)) {
552                     VChangeState_r(vp, VOL_STATE_UNATTACHED);
553                     VDeregisterVolOp_r(vp);
554                 } else {
555                     code = SYNC_DENIED;
556                     res->hdr.reason = FSYNC_BAD_STATE;
557                 }
558             } else {
559                 code = SYNC_DENIED;
560                 res->hdr.reason = FSYNC_WRONG_PART;
561             }
562         } else {
563             code = SYNC_DENIED;
564             res->hdr.reason = FSYNC_UNKNOWN_VOLID;
565         }
566 #endif
567         goto done;
568     }
569
570 #ifdef AFS_DEMAND_ATTACH_FS
571     /* first, check to see whether we have such a volume defined */
572     vp = VPreAttachVolumeById_r(&error,
573                                 vcom->vop->partName,
574                                 vcom->vop->volume);
575     if (vp) {
576         VDeregisterVolOp_r(vp);
577     }
578 #else /* !AFS_DEMAND_ATTACH_FS */
579     tvolName[0] = '/';
580     snprintf(&tvolName[1], sizeof(tvolName)-1, VFORMAT, afs_cast_uint32(vcom->vop->volume));
581     tvolName[sizeof(tvolName)-1] = '\0';
582
583     vp = VAttachVolumeByName_r(&error, vcom->vop->partName, tvolName,
584                                V_VOLUPD);
585     if (vp)
586         VPutVolume_r(vp);
587     if (error) {
588         code = SYNC_DENIED;
589         res->hdr.reason = error;
590     }
591 #endif /* !AFS_DEMAND_ATTACH_FS */
592
593  done:
594     return code;
595 }
596
597 /**
598  * service an FSYNC request to take a volume offline.
599  *
600  * @param[in]   vcom  pointer command object
601  * @param[out]  res   object in which to store response packet
602  *
603  * @return operation status
604  *   @retval SYNC_OK volume transitioned offline
605  *   @retval SYNC_FAILED invalid command protocol message
606  *   @retval SYNC_DENIED operation could not be completed
607  *
608  * @note this is an FSYNC RPC server stub
609  *
610  * @note this procedure handles the following FSSYNC command codes:
611  *       - FSYNC_VOL_OFF 
612  *       - FSYNC_VOL_NEEDVOLUME
613  *
614  * @note the supplementary reason code contains additional details.
615  *       When SYNC_DENIED is returned, the specific reason is
616  *       placed in the response packet reason field.
617  *
618  * @internal
619  */
620 static afs_int32
621 FSYNC_com_VolOff(FSSYNC_VolOp_command * vcom, SYNC_response * res)
622 {
623     FSSYNC_VolOp_info info;
624     afs_int32 code = SYNC_OK;
625     int i;
626     Volume * vp;
627     Error error;
628 #ifdef AFS_DEMAND_ATTACH_FS
629     int reserved = 0;
630     Volume *nvp;
631 #endif
632
633     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
634         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
635         code = SYNC_FAILED;
636         goto done;
637     }
638
639     /* not already offline, we need to find a slot for newly offline volume */
640     if (vcom->hdr->programType == debugUtility) {
641         /* debug utilities do not have their operations tracked */
642         vcom->v = NULL;
643     } else {
644         if (!vcom->v) {
645             for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
646                 if (vcom->volumes[i].volumeID == 0) {
647                     vcom->v = &vcom->volumes[i];
648                     break;
649                 }
650             }
651         }
652         if (!vcom->v) {
653             goto deny;
654         }
655     }
656
657     FSYNC_com_to_info(vcom, &info);
658
659 #ifdef AFS_DEMAND_ATTACH_FS
660     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
661 #else
662     vp = VGetVolume_r(&error, vcom->vop->volume);
663 #endif
664
665     if (vp) {
666             if (!FSYNC_partMatch(vcom, vp, 1)) {
667             /* volume on desired partition is not online, so we
668              * should treat this as an offline volume.
669              */
670 #ifndef AFS_DEMAND_ATTACH_FS
671             VPutVolume_r(vp);
672 #endif
673             vp = NULL;
674             goto done;
675         }
676     }
677
678 #ifdef AFS_DEMAND_ATTACH_FS
679     if (vp) {
680         ProgramType type = (ProgramType) vcom->hdr->programType;
681
682         /* do initial filtering of requests */
683
684         /* enforce mutual exclusion for volume ops */
685         if (vp->pending_vol_op) {
686             if (vp->pending_vol_op->com.programType != type) {
687                 Log("volume %u already checked out\n", vp->hashid);
688                 /* XXX debug */
689                 Log("vp->vop = { com = { ver=%u, prog=%d, com=%d, reason=%d, len=%u, flags=0x%x }, vop = { vol=%u, part='%s' } }\n",
690                     vp->pending_vol_op->com.proto_version, 
691                     vp->pending_vol_op->com.programType,
692                     vp->pending_vol_op->com.command,
693                     vp->pending_vol_op->com.reason,
694                     vp->pending_vol_op->com.command_len,
695                     vp->pending_vol_op->com.flags,
696                     vp->pending_vol_op->vop.volume,
697                     vp->pending_vol_op->vop.partName );
698                 Log("vcom = { com = { ver=%u, prog=%d, com=%d, reason=%d, len=%u, flags=0x%x } , vop = { vol=%u, part='%s' } }\n",
699                     vcom->hdr->proto_version,
700                     vcom->hdr->programType,
701                     vcom->hdr->command,
702                     vcom->hdr->reason,
703                     vcom->hdr->command_len,
704                     vcom->hdr->flags,
705                     vcom->vop->volume,
706                     vcom->vop->partName);
707                 res->hdr.reason = FSYNC_EXCLUSIVE;
708                 goto deny;
709             } else {
710                 Log("warning: volume %u recursively checked out by programType id %d\n",
711                     vp->hashid, vcom->hdr->programType);
712             }
713         }
714
715         /* filter based upon requestor
716          *
717          * volume utilities are not allowed to check out volumes
718          * which are in an error state
719          *
720          * unknown utility programs will be denied on principal
721          */
722         switch (type) {
723         case salvageServer:
724             /* it is possible for the salvageserver to checkout a 
725              * volume for salvage before its scheduling request
726              * has been sent to the salvageserver */
727             if (vp->salvage.requested && !vp->salvage.scheduled) {
728                 vp->salvage.scheduled = 1;
729             }
730         case debugUtility:
731             break;
732
733         case volumeUtility:
734             if (VIsErrorState(V_attachState(vp))) {
735                 goto deny;
736             }
737             if (vp->salvage.requested) {
738                 goto deny;
739             }
740             break;
741
742         default:
743             Log("bad program type passed to FSSYNC\n");
744             goto deny;
745         }
746
747         /* short circuit for offline volume states
748          * so we can avoid I/O penalty of attachment */
749         switch (V_attachState(vp)) {
750         case VOL_STATE_UNATTACHED:
751         case VOL_STATE_PREATTACHED:
752         case VOL_STATE_SALVAGING:
753         case VOL_STATE_ERROR:
754             /* register the volume operation metadata with the volume
755              *
756              * if the volume is currently pre-attached, attach2()
757              * will evaluate the vol op metadata to determine whether
758              * attaching the volume would be safe */
759             VRegisterVolOp_r(vp, &info);
760             vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningUnknown;
761             goto done;
762         default:
763             break;
764         }
765
766         /* convert to heavyweight ref */
767         nvp = VGetVolumeByVp_r(&error, vp);
768
769         if (!nvp) {
770             Log("FSYNC_com_VolOff: failed to get heavyweight reference to volume %u\n",
771                 vcom->vop->volume);
772             res->hdr.reason = FSYNC_VOL_PKG_ERROR;
773             goto deny;
774         } else if (nvp != vp) {
775             /* i don't think this should ever happen, but just in case... */
776             Log("FSYNC_com_VolOff: warning: potentially dangerous race detected\n");
777             vp = nvp;
778         }
779
780         /* register the volume operation metadata with the volume */
781         VRegisterVolOp_r(vp, &info);
782
783     }
784 #endif /* AFS_DEMAND_ATTACH_FS */
785
786     if (vp) {
787         if (VVolOpLeaveOnline_r(vp, &info)) {
788             VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT);       /* At least get volume stats right */
789             if (LogLevel) {
790                 Log("FSYNC: Volume %u (%s) was left on line for an external %s request\n", 
791                     V_id(vp), V_name(vp), 
792                     vcom->hdr->reason == V_CLONE ? "clone" : 
793                     vcom->hdr->reason == V_READONLY ? "readonly" : 
794                     vcom->hdr->reason == V_DUMP ? "dump" : 
795                     "UNKNOWN");
796             }
797 #ifdef AFS_DEMAND_ATTACH_FS
798             vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
799 #endif
800             VPutVolume_r(vp);
801         } else {
802             if (VVolOpSetVBusy_r(vp, &info)) {
803                 vp->specialStatus = VBUSY;
804             }
805
806             /* remember what volume we got, so we can keep track of how
807              * many volumes the volserver or whatever is using.  Note that
808              * vp is valid since leaveonline is only set when vp is valid.
809              */
810             if (vcom->v) {
811                 vcom->v->volumeID = vcom->vop->volume;
812                 strlcpy(vcom->v->partName, vp->partition->name, sizeof(vcom->v->partName));
813             }
814
815 #ifdef AFS_DEMAND_ATTACH_FS
816             VOfflineForVolOp_r(&error, vp, "A volume utility is running.");
817             if (error==0) {
818                 assert(vp->nUsers==0);
819                 vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline; 
820             }
821             else {
822                 VDeregisterVolOp_r(vp);
823                 code = SYNC_DENIED;
824             }
825 #else
826             VOffline_r(vp, "A volume utility is running.");
827 #endif
828             vp = NULL;
829         }
830     }
831
832  done:
833     return code;
834
835  deny:
836     return SYNC_DENIED;
837 }
838
839 /**
840  * service an FSYNC request to mark a volume as moved.
841  *
842  * @param[in]   vcom  pointer command object
843  * @param[out]  res   object in which to store response packet
844  *
845  * @return operation status
846  *   @retval SYNC_OK volume marked as moved to a remote server
847  *   @retval SYNC_FAILED invalid command protocol message
848  *   @retval SYNC_DENIED current volume state does not permit this operation
849  *
850  * @note this is an FSYNC RPC server stub
851  *
852  * @note this operation also breaks all callbacks for the given volume
853  *
854  * @note this procedure handles the following FSSYNC command codes:
855  *       - FSYNC_VOL_MOVE
856  *
857  * @note the supplementary reason code contains additional details.  For
858  *       instance, SYNC_OK is still returned when the partition specified
859  *       does not match the one registered in the volume object -- reason
860  *       will be FSYNC_WRONG_PART in this case.
861  *
862  * @internal
863  */
864 static afs_int32
865 FSYNC_com_VolMove(FSSYNC_VolOp_command * vcom, SYNC_response * res)
866 {
867     afs_int32 code = SYNC_DENIED;
868     Error error;
869     Volume * vp;
870
871     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
872         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
873         code = SYNC_FAILED;
874         goto done;
875     }
876
877     /* Yuch:  the "reason" for the move is the site it got moved to... */
878     /* still set specialStatus so we stop sending back VBUSY.
879      * also should still break callbacks.  Note that I don't know
880      * how to tell if we should break all or not, so we just do it
881      * since it doesn't matter much if we do an extra break
882      * volume callbacks on a volume move within the same server */
883 #ifdef AFS_DEMAND_ATTACH_FS
884     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
885 #else
886     vp = VGetVolume_r(&error, vcom->vop->volume);
887 #endif
888     if (vp) {
889         if (FSYNC_partMatch(vcom, vp, 1)) {
890 #ifdef AFS_DEMAND_ATTACH_FS
891             if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
892                 (V_attachState(vp) == VOL_STATE_PREATTACHED)) {
893 #endif
894                 code = SYNC_OK;
895                 vp->specialStatus = VMOVED;
896 #ifdef AFS_DEMAND_ATTACH_FS
897             } else {
898                 res->hdr.reason = FSYNC_BAD_STATE;
899             }
900 #endif
901         } else {
902             res->hdr.reason = FSYNC_WRONG_PART;
903         }
904         VPutVolume_r(vp);
905     } else {
906         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
907     }
908
909     if ((code == SYNC_OK) && (V_BreakVolumeCallbacks != NULL)) {
910         Log("fssync: volume %u moved to %x; breaking all call backs\n",
911             vcom->vop->volume, vcom->hdr->reason);
912         VOL_UNLOCK;
913         (*V_BreakVolumeCallbacks) (vcom->vop->volume);
914         VOL_LOCK;
915     }
916
917
918  done:
919     return code;
920 }
921
922 /**
923  * service an FSYNC request to mark a volume as destroyed.
924  *
925  * @param[in]   vcom  pointer command object
926  * @param[out]  res   object in which to store response packet
927  *
928  * @return operation status
929  *   @retval SYNC_OK volume marked as destroyed
930  *   @retval SYNC_FAILED invalid command protocol message
931  *   @retval SYNC_DENIED current volume state does not permit this operation
932  *
933  * @note this is an FSYNC RPC server stub
934  *
935  * @note this procedure handles the following FSSYNC command codes:
936  *       - FSYNC_VOL_DONE
937  *
938  * @note the supplementary reason code contains additional details.  For
939  *       instance, SYNC_OK is still returned when the partition specified
940  *       does not match the one registered in the volume object -- reason
941  *       will be FSYNC_WRONG_PART in this case.
942  *
943  * @internal
944  */
945 static afs_int32
946 FSYNC_com_VolDone(FSSYNC_VolOp_command * vcom, SYNC_response * res)
947 {
948     afs_int32 code = SYNC_FAILED;
949 #ifdef AFS_DEMAND_ATTACH_FS
950     Error error;
951     Volume * vp;
952 #endif
953
954     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
955         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
956         goto done;
957     }
958
959     /* don't try to put online, this call is made only after deleting
960      * a volume, in which case we want to remove the vol # from the
961      * OfflineVolumes array only */
962     if (vcom->v)
963         vcom->v->volumeID = 0;
964
965 #ifdef AFS_DEMAND_ATTACH_FS
966     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
967     if (vp) {
968         if (FSYNC_partMatch(vcom, vp, 1)) {
969             if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
970                 (V_attachState(vp) == VOL_STATE_PREATTACHED)) {
971                 VChangeState_r(vp, VOL_STATE_UNATTACHED);
972                 VDeregisterVolOp_r(vp);
973                 code = SYNC_OK;
974             } else {
975                 code = SYNC_DENIED;
976                 res->hdr.reason = FSYNC_BAD_STATE;
977             }
978         } else {
979             code = SYNC_OK; /* XXX is this really a good idea? */
980             res->hdr.reason = FSYNC_WRONG_PART;
981         }
982     } else {
983         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
984     }
985 #endif
986
987  done:
988     return code;
989 }
990
991 #ifdef AFS_DEMAND_ATTACH_FS
992 /**
993  * service an FSYNC request to transition a volume to the hard error state.
994  *
995  * @param[in]   vcom  pointer command object
996  * @param[out]  res   object in which to store response packet
997  *
998  * @return operation status
999  *   @retval SYNC_OK volume transitioned to hard error state
1000  *   @retval SYNC_FAILED invalid command protocol message
1001  *   @retval SYNC_DENIED (see note)
1002  *
1003  * @note this is an FSYNC RPC server stub
1004  *
1005  * @note this procedure handles the following FSSYNC command codes:
1006  *       - FSYNC_VOL_FORCE_ERROR
1007  *
1008  * @note SYNC_DENIED is returned in the following cases:
1009  *        - no partition name is specified (reason field set to
1010  *          FSYNC_WRONG_PART).
1011  *        - volume id not known to fileserver (reason field set
1012  *          to FSYNC_UNKNOWN_VOLID).
1013  *
1014  * @note demand attach fileserver only
1015  *
1016  * @internal
1017  */
1018 static afs_int32
1019 FSYNC_com_VolError(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1020 {
1021     Error error;
1022     Volume * vp;
1023     afs_int32 code = SYNC_FAILED;
1024
1025     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1026         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1027         goto done;
1028     }
1029
1030     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1031     if (vp) {
1032         if (FSYNC_partMatch(vcom, vp, 0)) {
1033             /* null out salvsync control state, as it's no longer relevant */
1034             memset(&vp->salvage, 0, sizeof(vp->salvage));
1035             VChangeState_r(vp, VOL_STATE_ERROR);
1036             code = SYNC_OK;
1037         } else {
1038             res->hdr.reason = FSYNC_WRONG_PART;
1039         }
1040     } else {
1041         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1042     }
1043
1044  done:
1045     return code;
1046 }
1047 #endif /* AFS_DEMAND_ATTACH_FS */
1048
1049 /**
1050  * service an FSYNC request to break all callbacks for this volume.
1051  *
1052  * @param[in]   vcom  pointer command object
1053  * @param[out]  res   object in which to store response packet
1054  *
1055  * @return operation status
1056  *   @retval SYNC_OK callback breaks scheduled for volume
1057  *
1058  * @note this is an FSYNC RPC server stub
1059  *
1060  * @note this procedure handles the following FSSYNC command codes:
1061  *       - FSYNC_VOL_BREAKCBKS
1062  *
1063  * @note demand attach fileserver only
1064  *
1065  * @todo should do partition matching
1066  *
1067  * @internal
1068  */
1069 static afs_int32
1070 FSYNC_com_VolBreakCBKs(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1071 {
1072     /* if the volume is being restored, break all callbacks on it */
1073     if (V_BreakVolumeCallbacks) {
1074         Log("fssync: breaking all call backs for volume %u\n",
1075             vcom->vop->volume);
1076         VOL_UNLOCK;
1077         (*V_BreakVolumeCallbacks) (vcom->vop->volume);
1078         VOL_LOCK;
1079     }
1080     return SYNC_OK;
1081 }
1082
1083 /**
1084  * service an FSYNC request to return the Volume object.
1085  *
1086  * @param[in]   vcom  pointer command object
1087  * @param[out]  res   object in which to store response packet
1088  *
1089  * @return operation status
1090  *   @retval SYNC_OK      volume object returned to caller
1091  *   @retval SYNC_FAILED  bad command packet, or failed to locate volume object
1092  *
1093  * @note this is an FSYNC RPC server stub
1094  *
1095  * @note this procedure handles the following FSSYNC command codes:
1096  *       - FSYNC_VOL_QUERY
1097  *
1098  * @internal
1099  */
1100 static afs_int32
1101 FSYNC_com_VolQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1102 {
1103     afs_int32 code = SYNC_FAILED;
1104     Error error;
1105     Volume * vp;
1106
1107     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1108         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1109         goto done;
1110     }
1111
1112 #ifdef AFS_DEMAND_ATTACH_FS
1113     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1114 #else /* !AFS_DEMAND_ATTACH_FS */
1115     vp = VGetVolume_r(&error, vcom->vop->volume);
1116 #endif /* !AFS_DEMAND_ATTACH_FS */
1117
1118     if (vp) {
1119         if (FSYNC_partMatch(vcom, vp, 1)) {
1120             if (res->payload.len >= sizeof(Volume)) {
1121                 memcpy(res->payload.buf, vp, sizeof(Volume));
1122                 res->hdr.response_len += sizeof(Volume);
1123                 code = SYNC_OK;
1124             } else {
1125                 res->hdr.reason = SYNC_REASON_PAYLOAD_TOO_BIG;
1126             }
1127         } else {
1128             res->hdr.reason = FSYNC_WRONG_PART;
1129         }
1130 #ifndef AFS_DEMAND_ATTACH_FS
1131         VPutVolume_r(vp);
1132 #endif
1133     } else {
1134         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1135     }
1136
1137  done:
1138     return code;
1139 }
1140
1141 /**
1142  * service an FSYNC request to return the Volume header.
1143  *
1144  * @param[in]   vcom  pointer command object
1145  * @param[out]  res   object in which to store response packet
1146  *
1147  * @return operation status
1148  *   @retval SYNC_OK volume header returned to caller
1149  *   @retval SYNC_FAILED  bad command packet, or failed to locate volume header
1150  *
1151  * @note this is an FSYNC RPC server stub
1152  *
1153  * @note this procedure handles the following FSSYNC command codes:
1154  *       - FSYNC_VOL_QUERY_HDR
1155  *
1156  * @internal
1157  */
1158 static afs_int32
1159 FSYNC_com_VolHdrQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1160 {
1161     afs_int32 code = SYNC_FAILED;
1162     Error error;
1163     Volume * vp;
1164
1165     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1166         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1167         goto done;
1168     }
1169     if (res->payload.len < sizeof(VolumeDiskData)) {
1170         res->hdr.reason = SYNC_REASON_PAYLOAD_TOO_BIG;
1171         goto done;
1172     }
1173
1174 #ifdef AFS_DEMAND_ATTACH_FS
1175     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1176 #else /* !AFS_DEMAND_ATTACH_FS */
1177     vp = VGetVolume_r(&error, vcom->vop->volume);
1178 #endif
1179
1180     if (vp) {
1181         if (FSYNC_partMatch(vcom, vp, 1)) {
1182 #ifdef AFS_DEMAND_ATTACH_FS
1183             if ((vp->header == NULL) ||
1184                 !(V_attachFlags(vp) & VOL_HDR_ATTACHED) ||
1185                 !(V_attachFlags(vp) & VOL_HDR_LOADED)) {
1186                 res->hdr.reason = FSYNC_HDR_NOT_ATTACHED;
1187                 goto done;
1188             }
1189 #else /* !AFS_DEMAND_ATTACH_FS */
1190             if (!vp || !vp->header) {
1191                 res->hdr.reason = FSYNC_HDR_NOT_ATTACHED;
1192                 goto done;
1193             }
1194 #endif /* !AFS_DEMAND_ATTACH_FS */
1195         } else {
1196             res->hdr.reason = FSYNC_WRONG_PART;
1197             goto done;
1198         }
1199     } else {
1200         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1201         goto done;
1202     }
1203
1204     memcpy(res->payload.buf, &V_disk(vp), sizeof(VolumeDiskData));
1205     res->hdr.response_len += sizeof(VolumeDiskData);
1206 #ifndef AFS_DEMAND_ATTACH_FS
1207     VPutVolume_r(vp);
1208 #endif
1209     code = SYNC_OK;
1210
1211  done:
1212     return code;
1213 }
1214
1215 #ifdef AFS_DEMAND_ATTACH_FS
1216 static afs_int32
1217 FSYNC_com_VolOpQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1218 {
1219     afs_int32 code = SYNC_OK;
1220     Error error;
1221     Volume * vp;
1222
1223     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1224
1225     if (vp && vp->pending_vol_op) {
1226         assert(sizeof(FSSYNC_VolOp_info) <= res->payload.len);
1227         memcpy(res->payload.buf, vp->pending_vol_op, sizeof(FSSYNC_VolOp_info));
1228         res->hdr.response_len += sizeof(FSSYNC_VolOp_info);
1229     } else {
1230         if (vp) {
1231             res->hdr.reason = FSYNC_NO_PENDING_VOL_OP;
1232         } else {
1233             res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1234         }
1235         code = SYNC_FAILED;
1236     }
1237     return code;
1238 }
1239 #endif /* AFS_DEMAND_ATTACH_FS */
1240
1241 static afs_int32
1242 FSYNC_com_VnQry(osi_socket fd, SYNC_command * com, SYNC_response * res)
1243 {
1244     afs_int32 code = SYNC_OK;
1245     FSSYNC_VnQry_hdr * qry = com->payload.buf;
1246     Volume * vp;
1247     Vnode * vnp;
1248     Error error;
1249
1250     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_VnQry_hdr))) {
1251         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1252         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
1253         return SYNC_COM_ERROR;
1254     }
1255
1256 #ifdef AFS_DEMAND_ATTACH_FS
1257     vp = VLookupVolume_r(&error, qry->volume, NULL);
1258 #else /* !AFS_DEMAND_ATTACH_FS */
1259     vp = VGetVolume_r(&error, qry->volume);
1260 #endif /* !AFS_DEMAND_ATTACH_FS */
1261
1262     if (!vp) {
1263         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1264         code = SYNC_FAILED;
1265         goto done;
1266     }
1267
1268     vnp = VLookupVnode(vp, qry->vnode);
1269     if (!vnp) {
1270         res->hdr.reason = FSYNC_UNKNOWN_VNID;
1271         code = SYNC_FAILED;
1272         goto cleanup;
1273     }
1274
1275     if (Vn_class(vnp)->residentSize > res->payload.len) {
1276         res->hdr.reason = SYNC_REASON_ENCODING_ERROR;
1277         code = SYNC_FAILED;
1278         goto cleanup;
1279     }
1280
1281     memcpy(res->payload.buf, vnp, Vn_class(vnp)->residentSize);
1282     res->hdr.response_len += Vn_class(vnp)->residentSize;
1283
1284  cleanup:
1285 #ifndef AFS_DEMAND_ATTACH_FS
1286     VPutVolume_r(vp);
1287 #endif
1288
1289  done:
1290     return code;
1291 }
1292
1293 static afs_int32
1294 FSYNC_com_StatsOp(osi_socket fd, SYNC_command * com, SYNC_response * res)
1295 {
1296     afs_int32 code = SYNC_OK;
1297     FSSYNC_StatsOp_command scom;
1298
1299     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_StatsOp_hdr))) {
1300         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1301         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
1302         return SYNC_COM_ERROR;
1303     }
1304
1305     scom.hdr = &com->hdr;
1306     scom.sop = (FSSYNC_StatsOp_hdr *) com->payload.buf;
1307     scom.com = com;
1308
1309     switch (com->hdr.command) {
1310     case FSYNC_VOL_STATS_GENERAL:
1311         code = FSYNC_com_StatsOpGeneral(&scom, res);
1312         break;
1313 #ifdef AFS_DEMAND_ATTACH_FS
1314         /* statistics for the following subsystems are only tracked
1315          * for demand attach fileservers */
1316     case FSYNC_VOL_STATS_VICEP:
1317         code = FSYNC_com_StatsOpViceP(&scom, res);
1318         break;
1319     case FSYNC_VOL_STATS_HASH:
1320         code = FSYNC_com_StatsOpHash(&scom, res);
1321         break;
1322     case FSYNC_VOL_STATS_HDR:
1323         code = FSYNC_com_StatsOpHdr(&scom, res);
1324         break;
1325     case FSYNC_VOL_STATS_VLRU:
1326         code = FSYNC_com_StatsOpVLRU(&scom, res);
1327         break;
1328 #endif /* AFS_DEMAND_ATTACH_FS */
1329     default:
1330         code = SYNC_BAD_COMMAND;
1331     }
1332
1333     return code;
1334 }
1335
1336 static afs_int32
1337 FSYNC_com_StatsOpGeneral(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1338 {
1339     afs_int32 code = SYNC_OK;
1340
1341     memcpy(res->payload.buf, &VStats, sizeof(VStats));
1342     res->hdr.response_len += sizeof(VStats);
1343
1344     return code;
1345 }
1346
1347 #ifdef AFS_DEMAND_ATTACH_FS
1348 static afs_int32
1349 FSYNC_com_StatsOpViceP(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1350 {
1351     afs_int32 code = SYNC_OK;
1352     struct DiskPartition64 * dp;
1353     struct DiskPartitionStats64 * stats;
1354
1355     if (SYNC_verifyProtocolString(scom->sop->args.partName, sizeof(scom->sop->args.partName))) {
1356         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1357         code = SYNC_FAILED;
1358         goto done;
1359     }
1360
1361     dp = VGetPartition_r(scom->sop->args.partName, 0);
1362     if (!dp) {
1363         code = SYNC_FAILED;
1364     } else {
1365         stats = (struct DiskPartitionStats64 *) res->payload.buf;
1366         stats->free = dp->free;
1367         stats->totalUsable = dp->totalUsable;
1368         stats->minFree = dp->minFree;
1369         stats->f_files = dp->f_files;
1370         stats->vol_list_len = dp->vol_list.len;
1371         
1372         res->hdr.response_len += sizeof(struct DiskPartitionStats64);
1373     }
1374
1375  done:
1376     return code;
1377 }
1378
1379 static afs_int32
1380 FSYNC_com_StatsOpHash(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1381 {
1382     afs_int32 code = SYNC_OK;
1383     struct VolumeHashChainStats * stats;
1384     struct VolumeHashChainHead * head;
1385
1386     if (scom->sop->args.hash_bucket >= VolumeHashTable.Size) {
1387         return SYNC_FAILED;
1388     }
1389
1390     head = &VolumeHashTable.Table[scom->sop->args.hash_bucket];
1391     stats = (struct VolumeHashChainStats *) res->payload.buf;
1392     stats->table_size = VolumeHashTable.Size;
1393     stats->chain_len = head->len;
1394     stats->chain_cacheCheck = head->cacheCheck;
1395     stats->chain_busy = head->busy;
1396     AssignInt64(head->looks, &stats->chain_looks);
1397     AssignInt64(head->gets, &stats->chain_gets);
1398     AssignInt64(head->reorders, &stats->chain_reorders);
1399
1400     res->hdr.response_len += sizeof(struct VolumeHashChainStats);
1401     
1402     return code;
1403 }
1404
1405 static afs_int32
1406 FSYNC_com_StatsOpHdr(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1407 {
1408     afs_int32 code = SYNC_OK;
1409
1410     memcpy(res->payload.buf, &volume_hdr_LRU.stats, sizeof(volume_hdr_LRU.stats));
1411     res->hdr.response_len += sizeof(volume_hdr_LRU.stats);
1412
1413     return code;
1414 }
1415
1416 static afs_int32
1417 FSYNC_com_StatsOpVLRU(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1418 {
1419     afs_int32 code = SYNC_OK;
1420
1421     code = SYNC_BAD_COMMAND;
1422
1423     return code;
1424 }
1425 #endif /* AFS_DEMAND_ATTACH_FS */
1426
1427 /**
1428  * populate an FSSYNC_VolOp_info object from a command packet object.
1429  *
1430  * @param[in]   vcom  pointer to command packet
1431  * @param[out]  info  pointer to info object which will be populated
1432  *
1433  * @note FSSYNC_VolOp_info objects are attached to Volume objects when
1434  *       a volume operation is commenced.
1435  *
1436  * @internal
1437  */
1438 static void
1439 FSYNC_com_to_info(FSSYNC_VolOp_command * vcom, FSSYNC_VolOp_info * info)
1440 {
1441     memcpy(&info->com, vcom->hdr, sizeof(SYNC_command_hdr));
1442     memcpy(&info->vop, vcom->vop, sizeof(FSSYNC_VolOp_hdr));
1443     info->vol_op_state = FSSYNC_VolOpPending;
1444 }
1445
1446 /**
1447  * check whether command packet partition name matches volume 
1448  * object's partition name.
1449  *
1450  * @param[in] vcom        pointer to command packet
1451  * @param[in] vp          pointer to volume object
1452  * @param[in] match_anon  anon matching control flag (see note below)
1453  *
1454  * @return whether partitions match
1455  *   @retval 0  partitions do NOT match
1456  *   @retval 1  partitions match
1457  *
1458  * @note if match_anon is non-zero, then this function will return a
1459  *       positive match for a zero-length partition string in the
1460  *       command packet.
1461  *
1462  * @internal
1463  */
1464 static int 
1465 FSYNC_partMatch(FSSYNC_VolOp_command * vcom, Volume * vp, int match_anon)
1466 {
1467     return ((match_anon && vcom->vop->partName[0] == 0) ||
1468             (strncmp(vcom->vop->partName, V_partition(vp)->name, 
1469                      sizeof(vcom->vop->partName)) == 0));
1470 }
1471
1472
1473 static void
1474 FSYNC_Drop(osi_socket fd)
1475 {
1476     struct offlineInfo *p;
1477     int i;
1478     Error error;
1479     char tvolName[VMAXPATHLEN];
1480
1481     VOL_LOCK;
1482     p = OfflineVolumes[FindHandler(fd)];
1483     for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
1484         if (p[i].volumeID) {
1485
1486             Volume *vp;
1487
1488             tvolName[0] = '/';
1489             sprintf(&tvolName[1], VFORMAT, afs_cast_uint32(p[i].volumeID));
1490             vp = VAttachVolumeByName_r(&error, p[i].partName, tvolName,
1491                                        V_VOLUPD);
1492             if (vp)
1493                 VPutVolume_r(vp);
1494             p[i].volumeID = 0;
1495         }
1496     }
1497     VOL_UNLOCK;
1498     RemoveHandler(fd);
1499 #ifdef AFS_NT40_ENV
1500     closesocket(fd);
1501 #else
1502     close(fd);
1503 #endif
1504     AcceptOn();
1505 }
1506
1507 static int AcceptHandler = -1;  /* handler id for accept, if turned on */
1508
1509 static void
1510 AcceptOn(void)
1511 {
1512     if (AcceptHandler == -1) {
1513         assert(AddHandler(fssync_server_state.fd, FSYNC_newconnection));
1514         AcceptHandler = FindHandler(fssync_server_state.fd);
1515     }
1516 }
1517
1518 static void
1519 AcceptOff(void)
1520 {
1521     if (AcceptHandler != -1) {
1522         assert(RemoveHandler(fssync_server_state.fd));
1523         AcceptHandler = -1;
1524     }
1525 }
1526
1527 /* The multiple FD handling code. */
1528
1529 static osi_socket HandlerFD[MAXHANDLERS];
1530 static void (*HandlerProc[MAXHANDLERS]) (osi_socket);
1531
1532 static void
1533 InitHandler(void)
1534 {
1535     register int i;
1536     ObtainWriteLock(&FSYNC_handler_lock);
1537     for (i = 0; i < MAXHANDLERS; i++) {
1538         HandlerFD[i] = -1;
1539         HandlerProc[i] = 0;
1540     }
1541     ReleaseWriteLock(&FSYNC_handler_lock);
1542 }
1543
1544 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
1545 static void
1546 CallHandler(struct pollfd *fds, int nfds, int mask)
1547 {
1548     int i;
1549     int handler;
1550     ObtainReadLock(&FSYNC_handler_lock);
1551     for (i = 0; i < nfds; i++) {
1552         if (fds[i].revents & mask) {
1553             handler = FindHandler_r(fds[i].fd);
1554             ReleaseReadLock(&FSYNC_handler_lock);
1555             (*HandlerProc[handler]) (fds[i].fd);
1556             ObtainReadLock(&FSYNC_handler_lock);
1557         }
1558     }
1559     ReleaseReadLock(&FSYNC_handler_lock);
1560 }
1561 #else
1562 static void
1563 CallHandler(fd_set * fdsetp)
1564 {
1565     register int i;
1566     ObtainReadLock(&FSYNC_handler_lock);
1567     for (i = 0; i < MAXHANDLERS; i++) {
1568         if (HandlerFD[i] >= 0 && FD_ISSET(HandlerFD[i], fdsetp)) {
1569             ReleaseReadLock(&FSYNC_handler_lock);
1570             (*HandlerProc[i]) (HandlerFD[i]);
1571             ObtainReadLock(&FSYNC_handler_lock);
1572         }
1573     }
1574     ReleaseReadLock(&FSYNC_handler_lock);
1575 }
1576 #endif
1577
1578 static int
1579 AddHandler(osi_socket afd, void (*aproc) (osi_socket))
1580 {
1581     register int i;
1582     ObtainWriteLock(&FSYNC_handler_lock);
1583     for (i = 0; i < MAXHANDLERS; i++)
1584         if (HandlerFD[i] == -1)
1585             break;
1586     if (i >= MAXHANDLERS) {
1587         ReleaseWriteLock(&FSYNC_handler_lock);
1588         return 0;
1589     }
1590     HandlerFD[i] = afd;
1591     HandlerProc[i] = aproc;
1592     ReleaseWriteLock(&FSYNC_handler_lock);
1593     return 1;
1594 }
1595
1596 static int
1597 FindHandler(register osi_socket afd)
1598 {
1599     register int i;
1600     ObtainReadLock(&FSYNC_handler_lock);
1601     for (i = 0; i < MAXHANDLERS; i++)
1602         if (HandlerFD[i] == afd) {
1603             ReleaseReadLock(&FSYNC_handler_lock);
1604             return i;
1605         }
1606     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
1607     assert(1 == 2);
1608     return -1;                  /* satisfy compiler */
1609 }
1610
1611 static int
1612 FindHandler_r(register osi_socket afd)
1613 {
1614     register int i;
1615     for (i = 0; i < MAXHANDLERS; i++)
1616         if (HandlerFD[i] == afd) {
1617             return i;
1618         }
1619     assert(1 == 2);
1620     return -1;                  /* satisfy compiler */
1621 }
1622
1623 static int
1624 RemoveHandler(register osi_socket afd)
1625 {
1626     ObtainWriteLock(&FSYNC_handler_lock);
1627     HandlerFD[FindHandler_r(afd)] = -1;
1628     ReleaseWriteLock(&FSYNC_handler_lock);
1629     return 1;
1630 }
1631
1632 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
1633 static void
1634 GetHandler(struct pollfd *fds, int maxfds, int events, int *nfds)
1635 {
1636     int i;
1637     int fdi = 0;
1638     ObtainReadLock(&FSYNC_handler_lock);
1639     for (i = 0; i < MAXHANDLERS; i++)
1640         if (HandlerFD[i] != -1) {
1641             assert(fdi<maxfds);
1642             fds[fdi].fd = HandlerFD[i];
1643             fds[fdi].events = events;
1644             fds[fdi].revents = 0;
1645             fdi++;
1646         }
1647     *nfds = fdi;
1648     ReleaseReadLock(&FSYNC_handler_lock);
1649 }
1650 #else
1651 static void
1652 GetHandler(fd_set * fdsetp, int *maxfdp)
1653 {
1654     register int i;
1655     register osi_socket maxfd = -1;
1656     FD_ZERO(fdsetp);
1657     ObtainReadLock(&FSYNC_handler_lock);        /* just in case */
1658     for (i = 0; i < MAXHANDLERS; i++)
1659         if (HandlerFD[i] != -1) {
1660             FD_SET(HandlerFD[i], fdsetp);
1661             if (maxfd < HandlerFD[i] || maxfd == (osi_socket)-1)
1662                 maxfd = HandlerFD[i];
1663         }
1664     *maxfdp = maxfd;
1665     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
1666 }
1667 #endif /* HAVE_POLL && AFS_PTHREAD_ENV */
1668
1669 #endif /* FSSYNC_BUILD_SERVER */