dafs-20060317
[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 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
78 #ifdef HAVE_STRING_H
79 #include <string.h>
80 #else
81 #ifdef HAVE_STRINGS_H
82 #include <strings.h>
83 #endif
84 #endif
85
86
87 #include <rx/xdr.h>
88 #include <afs/afsint.h>
89 #include "nfs.h"
90 #include <afs/errors.h>
91 #include "daemon_com.h"
92 #include "fssync.h"
93 #include "lwp.h"
94 #include "lock.h"
95 #include <afs/afssyscalls.h>
96 #include "ihandle.h"
97 #include "vnode.h"
98 #include "volume.h"
99 #include "partition.h"
100
101
102 #ifdef FSSYNC_BUILD_SERVER
103
104 /*@printflike@*/ extern void Log(const char *format, ...);
105
106 #ifdef osi_Assert
107 #undef osi_Assert
108 #endif
109 #define osi_Assert(e) (void)(e)
110
111 int (*V_BreakVolumeCallbacks) ();
112
113 #define MAXHANDLERS     4       /* Up to 4 clients; must be at least 2, so that
114                                  * move = dump+restore can run on single server */
115 #define MAXOFFLINEVOLUMES 128   /* This needs to be as big as the maximum
116                                  * number that would be offline for 1 operation.
117                                  * Current winner is salvage, which needs all
118                                  * cloned read-only copies offline when salvaging
119                                  * a single read-write volume */
120
121 #define MAX_BIND_TRIES  5       /* Number of times to retry socket bind */
122
123
124
125 static struct offlineInfo OfflineVolumes[MAXHANDLERS][MAXOFFLINEVOLUMES];
126
127 static int AcceptSd = -1;       /* Socket used by server for accepting connections */
128
129 static int getport();
130
131 /* Forward declarations */
132 static void FSYNC_sync();
133 static void FSYNC_newconnection();
134 static void FSYNC_com();
135 static void FSYNC_Drop();
136 static void AcceptOn();
137 static void AcceptOff();
138 static void InitHandler();
139 static void CallHandler(fd_set * fdsetp);
140 static int AddHandler();
141 static int FindHandler();
142 static int FindHandler_r();
143 static int RemoveHandler();
144 static void GetHandler(fd_set * fdsetp, int *maxfdp);
145
146 extern int LogLevel;
147
148 static afs_int32 FSYNC_com_VolOp(int fd, SYNC_command * com, SYNC_response * res);
149
150 static afs_int32 FSYNC_com_VolOn(FSSYNC_VolOp_command * com, SYNC_response * res);
151 static afs_int32 FSYNC_com_VolOff(FSSYNC_VolOp_command * com, SYNC_response * res);
152 static afs_int32 FSYNC_com_VolMove(FSSYNC_VolOp_command * com, SYNC_response * res);
153 static afs_int32 FSYNC_com_VolBreakCBKs(FSSYNC_VolOp_command * com, SYNC_response * res);
154 static afs_int32 FSYNC_com_VolDone(FSSYNC_VolOp_command * com, SYNC_response * res);
155 static afs_int32 FSYNC_com_VolQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
156 static afs_int32 FSYNC_com_VolHdrQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
157 #ifdef AFS_DEMAND_ATTACH_FS
158 static afs_int32 FSYNC_com_VolOpQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
159 #endif /* AFS_DEMAND_ATTACH_FS */
160
161 static afs_int32 FSYNC_com_StatsOp(int fd, SYNC_command * com, SYNC_response * res);
162
163 static afs_int32 FSYNC_com_StatsOpGeneral(FSSYNC_StatsOp_command * scom, SYNC_response * res);
164 static afs_int32 FSYNC_com_StatsOpViceP(FSSYNC_StatsOp_command * scom, SYNC_response * res);
165 static afs_int32 FSYNC_com_StatsOpHash(FSSYNC_StatsOp_command * scom, SYNC_response * res);
166 static afs_int32 FSYNC_com_StatsOpHdr(FSSYNC_StatsOp_command * scom, SYNC_response * res);
167 static afs_int32 FSYNC_com_StatsOpVLRU(FSSYNC_StatsOp_command * scom, SYNC_response * res);
168
169
170 static void FSYNC_com_to_info(FSSYNC_VolOp_command * vcom, FSSYNC_VolOp_info * info);
171
172
173 /*
174  * This lock controls access to the handler array. The overhead
175  * is minimal in non-preemptive environments.
176  */
177 struct Lock FSYNC_handler_lock;
178
179 void
180 FSYNC_fsInit(void)
181 {
182 #ifdef AFS_PTHREAD_ENV
183     pthread_t tid;
184     pthread_attr_t tattr;
185 #else /* AFS_PTHREAD_ENV */
186     PROCESS pid;
187 #endif /* AFS_PTHREAD_ENV */
188
189     Lock_Init(&FSYNC_handler_lock);
190
191 #ifdef AFS_PTHREAD_ENV
192     assert(pthread_attr_init(&tattr) == 0);
193     assert(pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED) == 0);
194     assert(pthread_create(&tid, &tattr, FSYNC_sync, NULL) == 0);
195 #else /* AFS_PTHREAD_ENV */
196     assert(LWP_CreateProcess
197            (FSYNC_sync, USUAL_STACK_SIZE, USUAL_PRIORITY, (void *)0,
198             "FSYNC_sync", &pid) == LWP_SUCCESS);
199 #endif /* AFS_PTHREAD_ENV */
200 }
201
202 static fd_set FSYNC_readfds;
203
204 static int
205 getport(struct sockaddr_in *addr)
206 {
207     int sd;
208
209     memset(addr, 0, sizeof(*addr));
210     assert((sd = socket(AF_INET, SOCK_STREAM, 0)) >= 0);
211 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
212     addr->sin_len = sizeof(struct sockaddr_in);
213 #endif
214     addr->sin_addr.s_addr = htonl(0x7f000001);
215     addr->sin_family = AF_INET; /* was localhost->h_addrtype */
216     addr->sin_port = htons(2040);       /* XXXX htons not _really_ neccessary */
217
218     return sd;
219 }
220
221
222 static void
223 FSYNC_sync()
224 {
225     struct sockaddr_in addr;
226     int on = 1;
227     extern int VInit;
228     int code;
229     int numTries;
230 #ifdef AFS_PTHREAD_ENV
231     int tid;
232 #endif
233
234 #ifndef AFS_NT40_ENV
235     (void)signal(SIGPIPE, SIG_IGN);
236 #endif
237
238 #ifdef AFS_PTHREAD_ENV
239     /* set our 'thread-id' so that the host hold table works */
240     MUTEX_ENTER(&rx_stats_mutex);       /* protects rxi_pthread_hinum */
241     tid = ++rxi_pthread_hinum;
242     MUTEX_EXIT(&rx_stats_mutex);
243     pthread_setspecific(rx_thread_id_key, (void *)tid);
244     Log("Set thread id %d for FSYNC_sync\n", tid);
245 #endif /* AFS_PTHREAD_ENV */
246
247     while (!VInit) {
248         /* Let somebody else run until level > 0.  That doesn't mean that 
249          * all volumes have been attached. */
250 #ifdef AFS_PTHREAD_ENV
251         pthread_yield();
252 #else /* AFS_PTHREAD_ENV */
253         LWP_DispatchProcess();
254 #endif /* AFS_PTHREAD_ENV */
255     }
256     AcceptSd = getport(&addr);
257     /* Reuseaddr needed because system inexplicably leaves crud lying around */
258     code =
259         setsockopt(AcceptSd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
260                    sizeof(on));
261     if (code)
262         Log("FSYNC_sync: setsockopt failed with (%d)\n", errno);
263
264     for (numTries = 0; numTries < MAX_BIND_TRIES; numTries++) {
265         if ((code =
266              bind(AcceptSd, (struct sockaddr *)&addr, sizeof(addr))) == 0)
267             break;
268         Log("FSYNC_sync: bind failed with (%d), will sleep and retry\n",
269             errno);
270         sleep(5);
271     }
272     assert(!code);
273     listen(AcceptSd, 100);
274     InitHandler();
275     AcceptOn();
276     for (;;) {
277         int maxfd;
278         GetHandler(&FSYNC_readfds, &maxfd);
279         /* Note: check for >= 1 below is essential since IOMGR_select
280          * doesn't have exactly same semantics as select.
281          */
282 #ifdef AFS_PTHREAD_ENV
283         if (select(maxfd + 1, &FSYNC_readfds, NULL, NULL, NULL) >= 1)
284 #else /* AFS_PTHREAD_ENV */
285         if (IOMGR_Select(maxfd + 1, &FSYNC_readfds, NULL, NULL, NULL) >= 1)
286 #endif /* AFS_PTHREAD_ENV */
287             CallHandler(&FSYNC_readfds);
288     }
289 }
290
291 static void
292 FSYNC_newconnection(int afd)
293 {
294     struct sockaddr_in other;
295     int junk, fd;
296     junk = sizeof(other);
297     fd = accept(afd, (struct sockaddr *)&other, &junk);
298     if (fd == -1) {
299         Log("FSYNC_newconnection:  accept failed, errno==%d\n", errno);
300         assert(1 == 2);
301     } else if (!AddHandler(fd, FSYNC_com)) {
302         AcceptOff();
303         assert(AddHandler(fd, FSYNC_com));
304     }
305 }
306
307 /* this function processes commands from an fssync file descriptor (fd) */
308 afs_int32 FS_cnt = 0;
309 static void
310 FSYNC_com(int fd)
311 {
312     SYNC_command com;
313     SYNC_response res;
314     SYNC_PROTO_BUF_DECL(com_buf);
315     SYNC_PROTO_BUF_DECL(res_buf);
316
317     memset(&res.hdr, 0, sizeof(res.hdr));
318
319     com.payload.buf = (void *)com_buf;
320     com.payload.len = SYNC_PROTO_MAX_LEN;
321     res.hdr.response_len = sizeof(res.hdr);
322     res.hdr.proto_version = FSYNC_PROTO_VERSION;
323     res.payload.len = SYNC_PROTO_MAX_LEN;
324     res.payload.buf = (void *)res_buf;
325
326     FS_cnt++;
327     if (SYNC_getCom(fd, &com)) {
328         Log("FSYNC_com:  read failed; dropping connection (cnt=%d)\n", FS_cnt);
329         FSYNC_Drop(fd);
330         return;
331     }
332
333     if (com.hdr.proto_version != FSYNC_PROTO_VERSION) {
334         Log("FSYNC_com:  invalid protocol version (%u)\n", com.hdr.proto_version);
335         res.hdr.response = SYNC_COM_ERROR;
336         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
337         goto respond;
338     }
339
340     VOL_LOCK;
341     switch (com.hdr.command) {
342     case FSYNC_VOL_ON:
343     case FSYNC_VOL_OFF:
344     case FSYNC_VOL_LISTVOLUMES:
345     case FSYNC_VOL_NEEDVOLUME:
346     case FSYNC_VOL_MOVE:
347     case FSYNC_VOL_BREAKCBKS:
348     case FSYNC_VOL_DONE:
349     case FSYNC_VOL_QUERY:
350     case FSYNC_VOL_QUERY_HDR:
351     case FSYNC_VOL_QUERY_VOP:
352         res.hdr.response = FSYNC_com_VolOp(fd, &com, &res);
353         break;
354     case FSYNC_VOL_STATS_GENERAL:
355     case FSYNC_VOL_STATS_VICEP:
356     case FSYNC_VOL_STATS_HASH:
357     case FSYNC_VOL_STATS_HDR:
358     case FSYNC_VOL_STATS_VLRU:
359         res.hdr.response = FSYNC_com_StatsOp(fd, &com, &res);
360         break;
361     case SYNC_COM_CHANNEL_CLOSE:
362         res.hdr.response = SYNC_OK;
363         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
364         break;
365     default:
366         res.hdr.response = SYNC_BAD_COMMAND;
367         break;
368     }
369     VOL_UNLOCK;
370
371  respond:
372     SYNC_putRes(fd, &res);
373     if (res.hdr.flags & SYNC_FLAG_CHANNEL_SHUTDOWN) {
374         FSYNC_Drop(fd);
375     }
376 }
377
378 static afs_int32
379 FSYNC_com_VolOp(int fd, SYNC_command * com, SYNC_response * res)
380 {
381     int i;
382     afs_int32 code = SYNC_OK;
383     FSSYNC_VolOp_command vcom;
384
385     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_VolOp_hdr))) {
386         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
387         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
388         return SYNC_COM_ERROR;
389     }
390
391     vcom.hdr = &com->hdr;
392     vcom.vop = (FSSYNC_VolOp_hdr *) com->payload.buf;
393     vcom.com = com;
394
395     vcom.volumes = OfflineVolumes[FindHandler(fd)];
396     for (vcom.v = NULL, i = 0; i < MAXOFFLINEVOLUMES; i++) {
397         if ((vcom.volumes[i].volumeID == vcom.vop->volume) &&
398             (strncmp(vcom.volumes[i].partName, vcom.vop->partName,
399                      sizeof(vcom.volumes[i].partName)) == 0)) {
400             vcom.v = &vcom.volumes[i];
401             break;
402         }
403     }
404
405     switch (com->hdr.command) {
406     case FSYNC_VOL_ON:
407         code = FSYNC_com_VolOn(&vcom, res);
408         break;
409     case FSYNC_VOL_OFF:
410     case FSYNC_VOL_NEEDVOLUME:
411         code = FSYNC_com_VolOff(&vcom, res);
412         break;
413     case FSYNC_VOL_LISTVOLUMES:
414         code = SYNC_OK;
415         break;
416     case FSYNC_VOL_MOVE:
417         code = FSYNC_com_VolMove(&vcom, res);
418         break;
419     case FSYNC_VOL_BREAKCBKS:
420         code = FSYNC_com_VolBreakCBKs(&vcom, res);
421         break;
422     case FSYNC_VOL_DONE:
423         code = FSYNC_com_VolDone(&vcom, res);
424         break;
425     case FSYNC_VOL_QUERY:
426         code = FSYNC_com_VolQuery(&vcom, res);
427         break;
428     case FSYNC_VOL_QUERY_HDR:
429         code = FSYNC_com_VolHdrQuery(&vcom, res);
430         break;
431 #ifdef AFS_DEMAND_ATTACH_FS
432     case FSYNC_VOL_QUERY_VOP:
433         code = FSYNC_com_VolOpQuery(&vcom, res);
434         break;
435 #endif /* AFS_DEMAND_ATTACH_FS */
436     default:
437         code = SYNC_BAD_COMMAND;
438     }
439
440     return code;
441 }
442
443 static afs_int32
444 FSYNC_com_VolOn(FSSYNC_VolOp_command * vcom, SYNC_response * res)
445 {
446     afs_int32 code = SYNC_OK;
447     char tvolName[VMAXPATHLEN];
448     Volume * vp;
449     Error error;
450
451     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
452         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
453         code = SYNC_FAILED;
454         goto done;
455     }
456
457     /*
458       This is where a detatched volume gets reattached. However in the
459       special case where the volume is merely busy, it is already
460       attatched and it is only necessary to clear the busy flag. See
461       defect #2080 for details.
462     */
463
464     /* is the volume already attatched? */
465 #ifdef  notdef
466     /*
467      * XXX With the following enabled we had bizarre problems where the backup id would
468      * be reset to 0; that was due to the interaction between fileserver/volserver in that they
469      * both keep volumes in memory and the changes wouldn't be made to the fileserver. Some of
470      * the problems were due to refcnt changes as result of VGetVolume/VPutVolume which would call
471      * VOffline, etc. when we don't want to; someday the whole #2080 issue should be revisited to
472      * be done right XXX
473      */
474     vp = VGetVolume_r(&error, vcom->vop->volume);
475     if (vp) {
476         /* yep, is the BUSY flag set? */
477         if (vp->specialStatus == VBUSY) {
478
479             /* yep, clear BUSY flag */
480
481             vp->specialStatus = 0;
482             /* make sure vol is online */
483             if (vcom->v) {
484                 vcom->v->volumeID = 0;
485                 V_inUse(vp) = 1;        /* online */
486             }
487             VPutVolume_r(vp);
488             break;
489         }
490         VPutVolume_r(vp);
491     }
492 #endif /* notdef */
493
494     /* so, we need to attach the volume */
495
496     if (vcom->v)
497         vcom->v->volumeID = 0;
498     tvolName[0] = '/';
499     snprintf(&tvolName[1], sizeof(tvolName)-1, VFORMAT, vcom->vop->volume);
500     tvolName[sizeof(tvolName)-1] = '\0';
501
502 #ifdef AFS_DEMAND_ATTACH_FS
503     vp = VPreAttachVolumeByName_r(&error, vcom->vop->partName, tvolName,
504                                   V_VOLUPD);
505     if (vp && vp->pending_vol_op) {
506         VDeregisterVolOp_r(vp, vp->pending_vol_op);
507     }
508 #else /* AFS_DEMAND_ATTACH_FS */
509     vp = VAttachVolumeByName_r(&error, vcom->vop->partName, tvolName,
510                                V_VOLUPD);
511     if (vp)
512         VPutVolume_r(vp);
513 #endif /* AFS_DEMAND_ATTACH_FS */
514
515     if (error) {
516         code = SYNC_DENIED;
517         res->hdr.reason = error;
518     }
519
520  done:
521     return code;
522 }
523
524 static afs_int32
525 FSYNC_com_VolOff(FSSYNC_VolOp_command * vcom, SYNC_response * res)
526 {
527     FSSYNC_VolOp_info info;
528     afs_int32 code = SYNC_OK;
529     int i;
530     Volume * vp, * nvp;
531     Error error;
532
533     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
534         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
535         code = SYNC_FAILED;
536         goto done;
537     }
538
539     /* not already offline, we need to find a slot for newly offline volume */
540     if (vcom->hdr->programType == debugUtility) {
541         /* debug utilities do not have their operations tracked */
542         vcom->v = NULL;
543     } else {
544         if (!vcom->v) {
545             for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
546                 if (vcom->volumes[i].volumeID == 0) {
547                     vcom->v = &vcom->volumes[i];
548                     break;
549                 }
550             }
551         }
552         if (!vcom->v) {
553             goto deny;
554         }
555     }
556
557     FSYNC_com_to_info(vcom, &info);
558
559 #ifdef AFS_DEMAND_ATTACH_FS
560     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
561 #else
562     vp = VGetVolume_r(&error, vcom->vop->volume);
563 #endif
564
565     if (vp) {
566         if ((vcom->vop->partName[0] != 0) &&
567             (strncmp(vcom->vop->partName, vp->partition->name, 
568                     sizeof(vcom->vop->partName)) != 0)) {
569             /* volume on desired partition is not online, so we
570              * should treat this as an offline volume.
571              */
572 #ifndef AFS_DEMAND_ATTACH_FS
573             VPutVolume_r(vp);
574 #endif
575             vp = NULL;
576             goto done;
577         }
578     }
579
580 #ifdef AFS_DEMAND_ATTACH_FS
581     if (vp) {
582         ProgramType type = (ProgramType) vcom->hdr->programType;
583
584         /* do initial filtering of requests */
585
586         /* enforce mutual exclusion for volume ops */
587         if (vp->pending_vol_op) {
588             if (vp->pending_vol_op->com.programType != type) {
589                 Log("volume %u already checked out\n", vp->hashid);
590                 /* XXX debug */
591                 Log("vp->vop = { com = { ver=%u, prog=%d, com=%d, reason=%d, len=%u, flags=0x%x }, vop = { vol=%u, part='%s' } }\n",
592                     vp->pending_vol_op->com.proto_version, 
593                     vp->pending_vol_op->com.programType,
594                     vp->pending_vol_op->com.command,
595                     vp->pending_vol_op->com.reason,
596                     vp->pending_vol_op->com.command_len,
597                     vp->pending_vol_op->com.flags,
598                     vp->pending_vol_op->vop.volume,
599                     vp->pending_vol_op->vop.partName );
600                 Log("vcom = { com = { ver=%u, prog=%d, com=%d, reason=%d, len=%u, flags=0x%x } , vop = { vol=%u, part='%s' } }\n",
601                     vcom->hdr->proto_version,
602                     vcom->hdr->programType,
603                     vcom->hdr->command,
604                     vcom->hdr->reason,
605                     vcom->hdr->command_len,
606                     vcom->hdr->flags,
607                     vcom->vop->volume,
608                     vcom->vop->partName);
609                 res->hdr.reason = FSYNC_EXCLUSIVE;
610                 goto deny;
611             } else {
612                 Log("warning: volume %u recursively checked out by programType id %d\n",
613                     vp->hashid, vcom->hdr->programType);
614             }
615         }
616
617         /* filter based upon requestor
618          *
619          * volume utilities are not allowed to check out volumes
620          * which are in an error state
621          *
622          * unknown utility programs will be denied on principal
623          */
624         switch (type) {
625         case salvageServer:
626         case debugUtility:
627             /* give the salvageserver lots of liberty */
628             break;
629         case volumeUtility:
630             if ((V_attachState(vp) == VOL_STATE_ERROR) ||
631                 (V_attachState(vp) == VOL_STATE_SALVAGING)) {
632                 goto deny;
633             }
634             break;
635         default:
636             Log("bad program type passed to FSSYNC\n");
637             goto deny;
638         }
639
640         /* short circuit for offline volume states
641          * so we can avoid I/O penalty of attachment */
642         switch (V_attachState(vp)) {
643         case VOL_STATE_UNATTACHED:
644         case VOL_STATE_PREATTACHED:
645         case VOL_STATE_SALVAGING:
646         case VOL_STATE_ERROR:
647             /* register the volume operation metadata with the volume
648              *
649              * if the volume is currently pre-attached, attach2()
650              * will evaluate the vol op metadata to determine whether
651              * attaching the volume would be safe */
652             VRegisterVolOp_r(vp, &info);
653             goto done;
654         default:
655             break;
656         }
657
658         /* convert to heavyweight ref */
659         nvp = VGetVolumeByVp_r(&error, vp);
660
661         /* register the volume operation metadata with the volume */
662         VRegisterVolOp_r(vp, &info);
663
664         if (!nvp) {
665             Log("FSYNC_com_VolOff: failed to get heavyweight reference to volume %u\n",
666                 vcom->vop->volume);
667             res->hdr.reason = FSYNC_VOL_PKG_ERROR;
668             goto deny;
669         }
670         vp = nvp;
671     }
672 #endif /* AFS_DEMAND_ATTACH_FS */
673
674     if (vp) {
675         if (VVolOpLeaveOnline_r(vp, &info)) {
676             VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT);       /* At least get volume stats right */
677             if (LogLevel) {
678                 Log("FSYNC: Volume %u (%s) was left on line for an external %s request\n", 
679                     V_id(vp), V_name(vp), 
680                     vcom->hdr->reason == V_CLONE ? "clone" : 
681                     vcom->hdr->reason == V_READONLY ? "readonly" : 
682                     vcom->hdr->reason == V_DUMP ? "dump" : 
683                     "UNKNOWN");
684             }
685             VPutVolume_r(vp);
686         } else {
687             if (VVolOpSetVBusy_r(vp, &info)) {
688                 vp->specialStatus = VBUSY;
689             }
690
691             /* remember what volume we got, so we can keep track of how
692              * many volumes the volserver or whatever is using.  Note that
693              * vp is valid since leaveonline is only set when vp is valid.
694              */
695             if (vcom->v) {
696                 vcom->v->volumeID = vcom->vop->volume;
697                 strlcpy(vcom->v->partName, vp->partition->name, sizeof(vcom->v->partName));
698             }
699
700             VOffline_r(vp, "A volume utility is running.");
701             vp = NULL;
702         }
703     }
704
705  done:
706     return code;
707
708  deny:
709     return SYNC_DENIED;
710 }
711
712 static afs_int32
713 FSYNC_com_VolMove(FSSYNC_VolOp_command * vcom, SYNC_response * res)
714 {
715     Error error;
716     Volume * vp;
717
718     /* Yuch:  the "reason" for the move is the site it got moved to... */
719     /* still set specialStatus so we stop sending back VBUSY.
720      * also should still break callbacks.  Note that I don't know
721      * how to tell if we should break all or not, so we just do it
722      * since it doesn't matter much if we do an extra break
723      * volume callbacks on a volume move within the same server */
724 #ifdef AFS_DEMAND_ATTACH_FS
725     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
726 #else
727     vp = VGetVolume_r(&error, vcom->vop->volume);
728 #endif
729     if (vp) {
730         vp->specialStatus = VMOVED;
731 #ifndef AFS_DEMAND_ATTACH_FS
732         VPutVolume_r(vp);
733 #endif
734     }
735
736     if (V_BreakVolumeCallbacks) {
737         Log("fssync: volume %u moved to %x; breaking all call backs\n",
738             vcom->vop->volume, vcom->hdr->reason);
739         VOL_UNLOCK;
740         (*V_BreakVolumeCallbacks) (vcom->vop->volume);
741         VOL_LOCK;
742     }
743
744     return SYNC_OK;
745 }
746
747 static afs_int32
748 FSYNC_com_VolDone(FSSYNC_VolOp_command * vcom, SYNC_response * res)
749 {
750 #ifdef AFS_DEMAND_ATTACH_FS
751     Error error;
752     Volume * vp;
753 #endif
754
755     /* don't try to put online, this call is made only after deleting
756      * a volume, in which case we want to remove the vol # from the
757      * OfflineVolumes array only */
758     if (vcom->v)
759         vcom->v->volumeID = 0;
760
761 #ifdef AFS_DEMAND_ATTACH_FS
762     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
763     if (vp && vp->pending_vol_op) {
764         VDeregisterVolOp_r(vp, vp->pending_vol_op);
765     }
766 #endif
767
768     return SYNC_OK;
769 }
770
771 static afs_int32
772 FSYNC_com_VolBreakCBKs(FSSYNC_VolOp_command * vcom, SYNC_response * res)
773 {
774     /* if the volume is being restored, break all callbacks on it */
775     if (V_BreakVolumeCallbacks) {
776         Log("fssync: breaking all call backs for volume %u\n",
777             vcom->vop->volume);
778         VOL_UNLOCK;
779         (*V_BreakVolumeCallbacks) (vcom->vop->volume);
780         VOL_LOCK;
781     }
782     return SYNC_OK;
783 }
784
785 static afs_int32
786 FSYNC_com_VolQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
787 {
788     afs_int32 code = SYNC_OK;
789     Error error;
790     Volume * vp;
791
792 #ifdef AFS_DEMAND_ATTACH_FS
793     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
794 #else /* !AFS_DEMAND_ATTACH_FS */
795     vp = VGetVolume_r(&error, vcom->vop->volume);
796 #endif /* !AFS_DEMAND_ATTACH_FS */
797
798     if (vp) {
799         assert(sizeof(Volume) <= res->payload.len);
800         memcpy(res->payload.buf, vp, sizeof(Volume));
801         res->hdr.response_len += sizeof(Volume);
802 #ifndef AFS_DEMAND_ATTACH_FS
803         VPutVolume_r(vp);
804 #endif
805     } else {
806         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
807         code = SYNC_FAILED;
808     }
809     return code;
810 }
811
812 static afs_int32
813 FSYNC_com_VolHdrQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
814 {
815     afs_int32 code = SYNC_OK;
816     Error error;
817     Volume * vp;
818     int hdr_ok = 0;
819
820 #ifdef AFS_DEMAND_ATTACH_FS
821     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
822     if (vp &&
823         (vp->header != NULL) &&
824         (V_attachFlags(vp) & VOL_HDR_ATTACHED) &&
825         (V_attachFlags(vp) & VOL_HDR_LOADED)) {
826         hdr_ok = 1;
827     }
828 #else /* !AFS_DEMAND_ATTACH_FS */
829     vp = VGetVolume_r(&error, vcom->vop->volume);
830     if (vp && vp->header) {
831         hdr_ok = 1;
832     }
833 #endif /* !AFS_DEMAND_ATTACH_FS */
834
835  load_done:
836     if (hdr_ok) {
837         assert(sizeof(VolumeDiskData) <= res->payload.len);
838         memcpy(res->payload.buf, &V_disk(vp), sizeof(VolumeDiskData));
839         res->hdr.response_len += sizeof(VolumeDiskData);
840 #ifndef AFS_DEMAND_ATTACH_FS
841         VPutVolume_r(vp);
842 #endif
843     } else {
844         if (vp) {
845             res->hdr.reason = FSYNC_HDR_NOT_ATTACHED;
846         } else {
847             res->hdr.reason = FSYNC_UNKNOWN_VOLID;
848         }
849         code = SYNC_FAILED;
850     }
851     return code;
852 }
853
854 #ifdef AFS_DEMAND_ATTACH_FS
855 static afs_int32
856 FSYNC_com_VolOpQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
857 {
858     afs_int32 code = SYNC_OK;
859     Error error;
860     Volume * vp;
861
862     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
863
864     if (vp && vp->pending_vol_op) {
865         assert(sizeof(FSSYNC_VolOp_info) <= res->payload.len);
866         memcpy(res->payload.buf, vp->pending_vol_op, sizeof(FSSYNC_VolOp_info));
867         res->hdr.response_len += sizeof(FSSYNC_VolOp_info);
868     } else {
869         if (vp) {
870             res->hdr.reason = FSYNC_NO_PENDING_VOL_OP;
871         } else {
872             res->hdr.reason = FSYNC_UNKNOWN_VOLID;
873         }
874         code = SYNC_FAILED;
875     }
876     return code;
877 }
878 #endif /* AFS_DEMAND_ATTACH_FS */
879
880 static afs_int32
881 FSYNC_com_StatsOp(int fd, SYNC_command * com, SYNC_response * res)
882 {
883     int i;
884     afs_int32 code = SYNC_OK;
885     FSSYNC_StatsOp_command scom;
886
887     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_StatsOp_hdr))) {
888         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
889         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
890         return SYNC_COM_ERROR;
891     }
892
893     scom.hdr = &com->hdr;
894     scom.sop = (FSSYNC_StatsOp_hdr *) com->payload.buf;
895     scom.com = com;
896
897     switch (com->hdr.command) {
898     case FSYNC_VOL_STATS_GENERAL:
899         code = FSYNC_com_StatsOpGeneral(&scom, res);
900         break;
901 #ifdef AFS_DEMAND_ATTACH_FS
902         /* statistics for the following subsystems are only tracked
903          * for demand attach fileservers */
904     case FSYNC_VOL_STATS_VICEP:
905         code = FSYNC_com_StatsOpViceP(&scom, res);
906         break;
907     case FSYNC_VOL_STATS_HASH:
908         code = FSYNC_com_StatsOpHash(&scom, res);
909         break;
910     case FSYNC_VOL_STATS_HDR:
911         code = FSYNC_com_StatsOpHdr(&scom, res);
912         break;
913     case FSYNC_VOL_STATS_VLRU:
914         code = FSYNC_com_StatsOpVLRU(&scom, res);
915         break;
916 #endif /* AFS_DEMAND_ATTACH_FS */
917     default:
918         code = SYNC_BAD_COMMAND;
919     }
920
921     return code;
922 }
923
924 static afs_int32
925 FSYNC_com_StatsOpGeneral(FSSYNC_StatsOp_command * scom, SYNC_response * res)
926 {
927     afs_int32 code = SYNC_OK;
928
929     memcpy(res->payload.buf, &VStats, sizeof(VStats));
930     res->hdr.response_len += sizeof(VStats);
931
932     return code;
933 }
934
935 #ifdef AFS_DEMAND_ATTACH_FS
936 static afs_int32
937 FSYNC_com_StatsOpViceP(FSSYNC_StatsOp_command * scom, SYNC_response * res)
938 {
939     afs_int32 code = SYNC_OK;
940     struct DiskPartition * dp;
941     struct DiskPartitionStats * stats;
942
943     if (SYNC_verifyProtocolString(scom->sop->args.partName, sizeof(scom->sop->args.partName))) {
944         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
945         code = SYNC_FAILED;
946         goto done;
947     }
948
949     dp = VGetPartition_r(scom->sop->args.partName, 0);
950     if (!dp) {
951         code = SYNC_FAILED;
952     } else {
953         stats = (struct DiskPartitionStats *) res->payload.buf;
954         stats->free = dp->free;
955         stats->totalUsable = dp->totalUsable;
956         stats->minFree = dp->minFree;
957         stats->f_files = dp->f_files;
958         stats->vol_list_len = dp->vol_list.len;
959         
960         res->hdr.response_len += sizeof(struct DiskPartitionStats);
961     }
962
963  done:
964     return code;
965 }
966
967 static afs_int32
968 FSYNC_com_StatsOpHash(FSSYNC_StatsOp_command * scom, SYNC_response * res)
969 {
970     afs_int32 code = SYNC_OK;
971     struct VolumeHashChainStats * stats;
972     struct VolumeHashChainHead * head;
973
974     if (scom->sop->args.hash_bucket >= VolumeHashTable.Size) {
975         return SYNC_FAILED;
976     }
977
978     head = &VolumeHashTable.Table[scom->sop->args.hash_bucket];
979     stats = (struct VolumeHashChainStats *) res->payload.buf;
980     stats->table_size = VolumeHashTable.Size;
981     stats->chain_len = head->len;
982     stats->chain_cacheCheck = head->cacheCheck;
983     stats->chain_busy = head->busy;
984     AssignInt64(head->looks, &stats->chain_looks);
985     AssignInt64(head->gets, &stats->chain_gets);
986     AssignInt64(head->reorders, &stats->chain_reorders);
987
988     res->hdr.response_len += sizeof(struct VolumeHashChainStats);
989     
990     return code;
991 }
992
993 static afs_int32
994 FSYNC_com_StatsOpHdr(FSSYNC_StatsOp_command * scom, SYNC_response * res)
995 {
996     afs_int32 code = SYNC_OK;
997
998     memcpy(res->payload.buf, &volume_hdr_LRU.stats, sizeof(volume_hdr_LRU.stats));
999     res->hdr.response_len += sizeof(volume_hdr_LRU.stats);
1000
1001     return code;
1002 }
1003
1004 static afs_int32
1005 FSYNC_com_StatsOpVLRU(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1006 {
1007     afs_int32 code = SYNC_OK;
1008
1009     code = SYNC_BAD_COMMAND;
1010
1011     return code;
1012 }
1013 #endif /* AFS_DEMAND_ATTACH_FS */
1014
1015 static void
1016 FSYNC_com_to_info(FSSYNC_VolOp_command * vcom, FSSYNC_VolOp_info * info)
1017 {
1018     memcpy(&info->com, vcom->hdr, sizeof(SYNC_command_hdr));
1019     memcpy(&info->vop, vcom->vop, sizeof(FSSYNC_VolOp_hdr));
1020 }
1021
1022 static void
1023 FSYNC_Drop(int fd)
1024 {
1025     struct offlineInfo *p;
1026     int i;
1027     Error error;
1028     char tvolName[VMAXPATHLEN];
1029
1030     VOL_LOCK;
1031     p = OfflineVolumes[FindHandler(fd)];
1032     for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
1033         if (p[i].volumeID) {
1034
1035             Volume *vp;
1036
1037             tvolName[0] = '/';
1038             sprintf(&tvolName[1], VFORMAT, p[i].volumeID);
1039             vp = VAttachVolumeByName_r(&error, p[i].partName, tvolName,
1040                                        V_VOLUPD);
1041             if (vp)
1042                 VPutVolume_r(vp);
1043             p[i].volumeID = 0;
1044         }
1045     }
1046     VOL_UNLOCK;
1047     RemoveHandler(fd);
1048 #ifdef AFS_NT40_ENV
1049     closesocket(fd);
1050 #else
1051     close(fd);
1052 #endif
1053     AcceptOn();
1054 }
1055
1056 static int AcceptHandler = -1;  /* handler id for accept, if turned on */
1057
1058 static void
1059 AcceptOn()
1060 {
1061     if (AcceptHandler == -1) {
1062         assert(AddHandler(AcceptSd, FSYNC_newconnection));
1063         AcceptHandler = FindHandler(AcceptSd);
1064     }
1065 }
1066
1067 static void
1068 AcceptOff()
1069 {
1070     if (AcceptHandler != -1) {
1071         assert(RemoveHandler(AcceptSd));
1072         AcceptHandler = -1;
1073     }
1074 }
1075
1076 /* The multiple FD handling code. */
1077
1078 static int HandlerFD[MAXHANDLERS];
1079 static int (*HandlerProc[MAXHANDLERS]) ();
1080
1081 static void
1082 InitHandler()
1083 {
1084     register int i;
1085     ObtainWriteLock(&FSYNC_handler_lock);
1086     for (i = 0; i < MAXHANDLERS; i++) {
1087         HandlerFD[i] = -1;
1088         HandlerProc[i] = 0;
1089     }
1090     ReleaseWriteLock(&FSYNC_handler_lock);
1091 }
1092
1093 static void
1094 CallHandler(fd_set * fdsetp)
1095 {
1096     register int i;
1097     ObtainReadLock(&FSYNC_handler_lock);
1098     for (i = 0; i < MAXHANDLERS; i++) {
1099         if (HandlerFD[i] >= 0 && FD_ISSET(HandlerFD[i], fdsetp)) {
1100             ReleaseReadLock(&FSYNC_handler_lock);
1101             (*HandlerProc[i]) (HandlerFD[i]);
1102             ObtainReadLock(&FSYNC_handler_lock);
1103         }
1104     }
1105     ReleaseReadLock(&FSYNC_handler_lock);
1106 }
1107
1108 static int
1109 AddHandler(int afd, int (*aproc) ())
1110 {
1111     register int i;
1112     ObtainWriteLock(&FSYNC_handler_lock);
1113     for (i = 0; i < MAXHANDLERS; i++)
1114         if (HandlerFD[i] == -1)
1115             break;
1116     if (i >= MAXHANDLERS) {
1117         ReleaseWriteLock(&FSYNC_handler_lock);
1118         return 0;
1119     }
1120     HandlerFD[i] = afd;
1121     HandlerProc[i] = aproc;
1122     ReleaseWriteLock(&FSYNC_handler_lock);
1123     return 1;
1124 }
1125
1126 static int
1127 FindHandler(register int afd)
1128 {
1129     register int i;
1130     ObtainReadLock(&FSYNC_handler_lock);
1131     for (i = 0; i < MAXHANDLERS; i++)
1132         if (HandlerFD[i] == afd) {
1133             ReleaseReadLock(&FSYNC_handler_lock);
1134             return i;
1135         }
1136     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
1137     assert(1 == 2);
1138     return -1;                  /* satisfy compiler */
1139 }
1140
1141 static int
1142 FindHandler_r(register int afd)
1143 {
1144     register int i;
1145     for (i = 0; i < MAXHANDLERS; i++)
1146         if (HandlerFD[i] == afd) {
1147             return i;
1148         }
1149     assert(1 == 2);
1150     return -1;                  /* satisfy compiler */
1151 }
1152
1153 static int
1154 RemoveHandler(register int afd)
1155 {
1156     ObtainWriteLock(&FSYNC_handler_lock);
1157     HandlerFD[FindHandler_r(afd)] = -1;
1158     ReleaseWriteLock(&FSYNC_handler_lock);
1159     return 1;
1160 }
1161
1162 static void
1163 GetHandler(fd_set * fdsetp, int *maxfdp)
1164 {
1165     register int i;
1166     register int maxfd = -1;
1167     FD_ZERO(fdsetp);
1168     ObtainReadLock(&FSYNC_handler_lock);        /* just in case */
1169     for (i = 0; i < MAXHANDLERS; i++)
1170         if (HandlerFD[i] != -1) {
1171             FD_SET(HandlerFD[i], fdsetp);
1172             if (maxfd < HandlerFD[i])
1173                 maxfd = HandlerFD[i];
1174         }
1175     *maxfdp = maxfd;
1176     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
1177 }
1178
1179 #endif /* FSSYNC_BUILD_SERVER */