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