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