rx: Provide Get/SetThreadNum functions
[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-2010 Sine Nomine Associates
10  */
11
12 /*
13         System:         VICE-TWO
14         Module:         fssync.c
15         Institution:    The Information Technology Center, Carnegie-Mellon University
16
17  */
18 #ifndef AFS_PTHREAD_ENV
19 #define USUAL_PRIORITY (LWP_MAX_PRIORITY - 2)
20
21 /*
22  * stack size increased from 8K because the HP machine seemed to have trouble
23  * with the smaller stack
24  */
25 #define USUAL_STACK_SIZE        (24 * 1024)
26 #endif /* !AFS_PTHREAD_ENV */
27
28 /*
29    fssync-server.c
30    File server synchronization with external volume utilities.
31    server-side implementation
32  */
33
34 /* This controls the size of an fd_set; it must be defined early before
35  * the system headers define that type and the macros that operate on it.
36  * Its value should be as large as the maximum file descriptor limit we
37  * are likely to run into on any platform.  Right now, that is 65536
38  * which is the default hard fd limit on Solaris 9 */
39 #ifndef _WIN32
40 #define FD_SETSIZE 65536
41 #endif
42
43 #include <afsconfig.h>
44 #include <afs/param.h>
45
46 #include <roken.h>
47
48 #include <rx/xdr.h>
49 #include <afs/afsint.h>
50 #include "nfs.h"
51 #include <afs/errors.h>
52 #include "daemon_com.h"
53 #include "daemon_com_inline.h"
54 #include "fssync.h"
55 #include "fssync_inline.h"
56 #include "salvsync.h"
57 #include "lwp.h"
58 #include "lock.h"
59 #include <afs/afssyscalls.h>
60 #include "ihandle.h"
61 #include "vnode.h"
62 #include "volume.h"
63 #include "volume_inline.h"
64 #include "partition.h"
65 #include "vg_cache.h"
66 #include "common.h"
67
68 #ifdef HAVE_POLL
69 #include <sys/poll.h>
70 #endif /* HAVE_POLL */
71
72 #ifdef USE_UNIX_SOCKETS
73 #include <sys/un.h>
74 #include <afs/afsutil.h>
75 #endif /* USE_UNIX_SOCKETS */
76
77 #ifdef FSSYNC_BUILD_SERVER
78
79 int (*V_BreakVolumeCallbacks) (VolumeId volume);
80
81 #define MAXHANDLERS     4       /* Up to 4 clients; must be at least 2, so that
82                                  * move = dump+restore can run on single server */
83 #define MAXOFFLINEVOLUMES 128   /* This needs to be as big as the maximum
84                                  * number that would be offline for 1 operation.
85                                  * Current winner is salvage, which needs all
86                                  * cloned read-only copies offline when salvaging
87                                  * a single read-write volume */
88
89
90
91 static struct offlineInfo OfflineVolumes[MAXHANDLERS][MAXOFFLINEVOLUMES];
92
93 /**
94  * fssync server socket handle.
95  */
96 static SYNC_server_state_t fssync_server_state =
97     { OSI_NULLSOCKET,                       /* file descriptor */
98       FSSYNC_ENDPOINT_DECL,     /* server endpoint */
99       FSYNC_PROTO_VERSION,      /* protocol version */
100       5,                        /* bind() retry limit */
101       100,                      /* listen() queue depth */
102       "FSSYNC",                 /* protocol name string */
103     };
104
105 #ifdef AFS_DEMAND_ATTACH_FS
106 /**
107  * a queue of volume pointers to salvage in the background.
108  */
109 struct fsync_salv_node {
110     struct rx_queue q;
111     Volume *vp;                     /**< volume to salvage */
112     unsigned char update_salv_prio; /**< whether we should update the salvage priority or not */
113 };
114 static struct {
115     struct rx_queue head;
116     pthread_cond_t cv;
117 } fsync_salv;
118
119 static void * FSYNC_salvageThread(void *);
120 static void FSYNC_backgroundSalvage(Volume *vp);
121 #endif /* AFS_DEMAND_ATTACH_FS */
122
123 /* Forward declarations */
124 static void * FSYNC_sync(void *);
125 static void FSYNC_newconnection(osi_socket afd);
126 static void FSYNC_com(osi_socket fd);
127 static void FSYNC_Drop(osi_socket fd);
128 static void AcceptOn(void);
129 static void AcceptOff(void);
130 static void InitHandler(void);
131 static int AddHandler(osi_socket fd, void (*aproc)(osi_socket));
132 static int FindHandler(osi_socket afd);
133 static int FindHandler_r(osi_socket afd);
134 static int RemoveHandler(osi_socket afd);
135 #if defined(HAVE_POLL) && defined (AFS_PTHREAD_ENV)
136 static void CallHandler(struct pollfd *fds, int nfds, int mask);
137 static void GetHandler(struct pollfd *fds, int maxfds, int events, int *nfds);
138 #else
139 static void CallHandler(fd_set * fdsetp);
140 static void GetHandler(fd_set * fdsetp, int *maxfdp);
141 #endif
142 extern int LogLevel;
143
144 static afs_int32 FSYNC_com_VolOp(osi_socket fd, SYNC_command * com, SYNC_response * res);
145
146 #ifdef AFS_DEMAND_ATTACH_FS
147 static afs_int32 FSYNC_com_VolError(FSSYNC_VolOp_command * com, SYNC_response * res);
148 #endif
149 static afs_int32 FSYNC_com_VolOn(FSSYNC_VolOp_command * com, SYNC_response * res);
150 static afs_int32 FSYNC_com_VolOff(FSSYNC_VolOp_command * com, SYNC_response * res);
151 static afs_int32 FSYNC_com_VolMove(FSSYNC_VolOp_command * com, SYNC_response * res);
152 static afs_int32 FSYNC_com_VolBreakCBKs(FSSYNC_VolOp_command * com, SYNC_response * res);
153 static afs_int32 FSYNC_com_VolDone(FSSYNC_VolOp_command * com, SYNC_response * res);
154 static afs_int32 FSYNC_com_VolQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
155 static afs_int32 FSYNC_com_VolHdrQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
156 #ifdef AFS_DEMAND_ATTACH_FS
157 static afs_int32 FSYNC_com_VGUpdate(osi_socket fd, SYNC_command * com, SYNC_response * res);
158 static afs_int32 FSYNC_com_VolOpQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
159 static afs_int32 FSYNC_com_VGQuery(FSSYNC_VolOp_command * com, SYNC_response * res);
160 static afs_int32 FSYNC_com_VGScan(FSSYNC_VolOp_command * com, SYNC_response * res);
161 static afs_int32 FSYNC_com_VGScanAll(FSSYNC_VolOp_command * com, SYNC_response * res);
162 #endif /* AFS_DEMAND_ATTACH_FS */
163
164 static afs_int32 FSYNC_com_VnQry(osi_socket fd, SYNC_command * com, SYNC_response * res);
165
166 static afs_int32 FSYNC_com_StatsOp(osi_socket fd, SYNC_command * com, SYNC_response * res);
167
168 static afs_int32 FSYNC_com_StatsOpGeneral(FSSYNC_StatsOp_command * scom, SYNC_response * res);
169
170 #ifdef AFS_DEMAND_ATTACH_FS
171 static afs_int32 FSYNC_com_StatsOpViceP(FSSYNC_StatsOp_command * scom, SYNC_response * res);
172 static afs_int32 FSYNC_com_StatsOpHash(FSSYNC_StatsOp_command * scom, SYNC_response * res);
173 static afs_int32 FSYNC_com_StatsOpHdr(FSSYNC_StatsOp_command * scom, SYNC_response * res);
174 static afs_int32 FSYNC_com_StatsOpVLRU(FSSYNC_StatsOp_command * scom, SYNC_response * res);
175 #endif
176
177 static void FSYNC_com_to_info(FSSYNC_VolOp_command * vcom, FSSYNC_VolOp_info * info);
178
179 static int FSYNC_partMatch(FSSYNC_VolOp_command * vcom, Volume * vp, int match_anon);
180
181
182 /*
183  * This lock controls access to the handler array. The overhead
184  * is minimal in non-preemptive environments.
185  */
186 struct Lock FSYNC_handler_lock;
187
188 void
189 FSYNC_fsInit(void)
190 {
191 #ifdef AFS_PTHREAD_ENV
192     pthread_t tid;
193     pthread_attr_t tattr;
194 #else /* AFS_PTHREAD_ENV */
195     PROCESS pid;
196 #endif /* AFS_PTHREAD_ENV */
197
198     Lock_Init(&FSYNC_handler_lock);
199
200 #ifdef AFS_PTHREAD_ENV
201     osi_Assert(pthread_attr_init(&tattr) == 0);
202     osi_Assert(pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED) == 0);
203     osi_Assert(pthread_create(&tid, &tattr, FSYNC_sync, NULL) == 0);
204 #else /* AFS_PTHREAD_ENV */
205     osi_Assert(LWP_CreateProcess
206            (FSYNC_sync, USUAL_STACK_SIZE, USUAL_PRIORITY, (void *)0,
207             "FSYNC_sync", &pid) == LWP_SUCCESS);
208 #endif /* AFS_PTHREAD_ENV */
209
210 #ifdef AFS_DEMAND_ATTACH_FS
211     queue_Init(&fsync_salv.head);
212     CV_INIT(&fsync_salv.cv, "fsync salv", CV_DEFAULT, 0);
213     osi_Assert(pthread_create(&tid, &tattr, FSYNC_salvageThread, NULL) == 0);
214 #endif /* AFS_DEMAND_ATTACH_FS */
215 }
216
217 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
218 static struct pollfd FSYNC_readfds[MAXHANDLERS];
219 #else
220 static fd_set FSYNC_readfds;
221 #endif
222
223
224 static void *
225 FSYNC_sync(void * args)
226 {
227     extern int VInit;
228     int code;
229 #ifdef AFS_PTHREAD_ENV
230     int tid;
231 #endif
232     SYNC_server_state_t * state = &fssync_server_state;
233 #ifdef AFS_DEMAND_ATTACH_FS
234     VThreadOptions_t * thread_opts;
235     int min_vinit = 2;
236 #else
237     /*
238      * For non-DAFS, only wait until we begin attaching volumes (instead
239      * of waiting until all volumes are attached), since it can take
240      * awhile until VInit == 2.
241      */
242     int min_vinit = 1;
243 #endif /* AFS_DEMAND_ATTACH_FS */
244
245     /* we must not be called before vol package initialization, since we use
246      * vol package mutexes and conds etc */
247     osi_Assert(VInit);
248
249     SYNC_getAddr(&state->endpoint, &state->addr);
250     SYNC_cleanupSock(state);
251
252 #ifndef AFS_NT40_ENV
253     (void)signal(SIGPIPE, SIG_IGN);
254 #endif
255
256 #ifdef AFS_PTHREAD_ENV
257     /* set our 'thread-id' so that the host hold table works */
258     tid = rx_SetThreadNum();
259     Log("Set thread id %d for FSYNC_sync\n", tid);
260 #endif /* AFS_PTHREAD_ENV */
261
262     VOL_LOCK;
263
264     while (VInit < min_vinit) {
265         /* Let somebody else run until all volumes have been preattached
266          * (DAFS), or we have started attaching volumes (non-DAFS). This
267          * doesn't mean that all volumes have been attached.
268          */
269 #ifdef AFS_PTHREAD_ENV
270         VOL_CV_WAIT(&vol_vinit_cond);
271 #else /* AFS_PTHREAD_ENV */
272         LWP_DispatchProcess();
273 #endif /* AFS_PTHREAD_ENV */
274     }
275
276     VOL_UNLOCK;
277
278     state->fd = SYNC_getSock(&state->endpoint);
279     code = SYNC_bindSock(state);
280     osi_Assert(!code);
281
282 #ifdef AFS_DEMAND_ATTACH_FS
283     /*
284      * make sure the volume package is incapable of recursively executing
285      * salvsync calls on this thread, since there is a possibility of
286      * deadlock.
287      */
288     thread_opts = malloc(sizeof(VThreadOptions_t));
289     if (thread_opts == NULL) {
290         Log("failed to allocate memory for thread-specific volume package options structure\n");
291         return NULL;
292     }
293     memcpy(thread_opts, &VThread_defaults, sizeof(VThread_defaults));
294     thread_opts->disallow_salvsync = 1;
295     osi_Assert(pthread_setspecific(VThread_key, thread_opts) == 0);
296
297     code = VVGCache_PkgInit();
298     osi_Assert(code == 0);
299 #endif
300
301     InitHandler();
302     AcceptOn();
303
304     for (;;) {
305 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
306         int nfds;
307         GetHandler(FSYNC_readfds, MAXHANDLERS, POLLIN|POLLPRI, &nfds);
308         if (poll(FSYNC_readfds, nfds, -1) >=1)
309             CallHandler(FSYNC_readfds, nfds, POLLIN|POLLPRI);
310 #else
311         int maxfd;
312 #ifdef AFS_PTHREAD_ENV
313         struct timeval s_timeout;
314 #endif
315         GetHandler(&FSYNC_readfds, &maxfd);
316         /* Note: check for >= 1 below is essential since IOMGR_select
317          * doesn't have exactly same semantics as select.
318          */
319 #ifdef AFS_PTHREAD_ENV
320         s_timeout.tv_sec = SYNC_SELECT_TIMEOUT;
321         s_timeout.tv_usec = 0;
322         if (select(maxfd + 1, &FSYNC_readfds, NULL, NULL, &s_timeout) >= 1)
323 #else /* AFS_PTHREAD_ENV */
324         if (IOMGR_Select(maxfd + 1, &FSYNC_readfds, NULL, NULL, NULL) >= 1)
325 #endif /* AFS_PTHREAD_ENV */
326             CallHandler(&FSYNC_readfds);
327 #endif
328     }
329     return NULL; /* hush now, little gcc */
330 }
331
332 #ifdef AFS_DEMAND_ATTACH_FS
333 /**
334  * thread for salvaging volumes in the background.
335  *
336  * Since FSSYNC handlers cannot issue SALVSYNC requests in order to avoid
337  * deadlock issues, this thread exists so code in the FSSYNC handler thread
338  * can hand off volumes to be salvaged in the background.
339  *
340  * @param[in] args  unused
341  *
342  * @note DEMAND_ATTACH_FS only
343  */
344 static void *
345 FSYNC_salvageThread(void * args)
346 {
347     Volume *vp;
348     struct fsync_salv_node *node;
349
350     VOL_LOCK;
351
352     for (;;) {
353         while (queue_IsEmpty(&fsync_salv.head)) {
354             VOL_CV_WAIT(&fsync_salv.cv);
355         }
356
357         node = queue_First(&fsync_salv.head, fsync_salv_node);
358         queue_Remove(node);
359
360         vp = node->vp;
361         if (node->update_salv_prio) {
362             if (VUpdateSalvagePriority_r(vp)) {
363                 ViceLog(0, ("FSYNC_salvageThread: unable to raise salvage priority "
364                             "for volume %lu\n", afs_printable_uint32_lu(vp->hashid)));
365             }
366         }
367
368         free(node);
369         node = NULL;
370
371         VCancelReservation_r(vp);
372     }
373
374     VOL_UNLOCK;
375
376     return NULL;
377 }
378
379 /**
380  * salvage a volume in the background.
381  *
382  * Salvages cannot be scheduled directly from the main FSYNC thread, so
383  * instead call this function to schedule a salvage asynchronously in the
384  * FSYNC_salvageThread thread.
385  *
386  * @param[in] vp  volume to pointer to salvage
387  *
388  * @pre VOL_LOCK held
389  *
390  * @note DEMAND_ATTACH_FS only
391  */
392 static void
393 FSYNC_backgroundSalvage(Volume *vp)
394 {
395     struct fsync_salv_node *node;
396     Error ec;
397
398     VCreateReservation_r(vp);
399
400     node = malloc(sizeof(struct fsync_salv_node));
401     node->vp = vp;
402
403     /* Save this value, to know if we should VUpdateSalvagePriority_r.
404      * We need to save it here, snce VRequestSalvage_r will change it. */
405     node->update_salv_prio = vp->salvage.requested;
406
407     if (VRequestSalvage_r(&ec, vp, SALVSYNC_ERROR, 0)) {
408         ViceLog(0, ("FSYNC_backgroundSalvage: unable to request salvage for volume %lu\n",
409                     afs_printable_uint32_lu(vp->hashid)));
410     }
411
412     queue_Append(&fsync_salv.head, node);
413     CV_BROADCAST(&fsync_salv.cv);
414 }
415 #endif /* AFS_DEMAND_ATTACH_FS */
416
417 static void
418 FSYNC_newconnection(osi_socket afd)
419 {
420 #ifdef USE_UNIX_SOCKETS
421     struct sockaddr_un other;
422 #else  /* USE_UNIX_SOCKETS */
423     struct sockaddr_in other;
424 #endif
425     osi_socket fd;
426     socklen_t junk;
427     junk = sizeof(other);
428     fd = accept(afd, (struct sockaddr *)&other, &junk);
429     if (fd == OSI_NULLSOCKET) {
430         Log("FSYNC_newconnection:  accept failed, errno==%d\n", errno);
431         osi_Assert(1 == 2);
432     } else if (!AddHandler(fd, FSYNC_com)) {
433         AcceptOff();
434         osi_Assert(AddHandler(fd, FSYNC_com));
435     }
436 }
437
438 /* this function processes commands from an fssync file descriptor (fd) */
439 afs_int32 FS_cnt = 0;
440 static void
441 FSYNC_com(osi_socket fd)
442 {
443     SYNC_command com;
444     SYNC_response res;
445     SYNC_PROTO_BUF_DECL(com_buf);
446     SYNC_PROTO_BUF_DECL(res_buf);
447
448     memset(&res.hdr, 0, sizeof(res.hdr));
449
450     com.payload.buf = (void *)com_buf;
451     com.payload.len = SYNC_PROTO_MAX_LEN;
452     res.hdr.response_len = sizeof(res.hdr);
453     res.payload.len = SYNC_PROTO_MAX_LEN;
454     res.payload.buf = (void *)res_buf;
455
456     FS_cnt++;
457     if (SYNC_getCom(&fssync_server_state, fd, &com)) {
458         Log("FSYNC_com:  read failed; dropping connection (cnt=%d)\n", FS_cnt);
459         FSYNC_Drop(fd);
460         return;
461     }
462
463     if (com.recv_len < sizeof(com.hdr)) {
464         Log("FSSYNC_com:  invalid protocol message length (%u)\n", com.recv_len);
465         res.hdr.response = SYNC_COM_ERROR;
466         res.hdr.reason = SYNC_REASON_MALFORMED_PACKET;
467         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
468         goto respond;
469     }
470
471     if (com.hdr.proto_version != FSYNC_PROTO_VERSION) {
472         Log("FSYNC_com:  invalid protocol version (%u)\n", com.hdr.proto_version);
473         res.hdr.response = SYNC_COM_ERROR;
474         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
475         goto respond;
476     }
477
478     if (com.hdr.command == SYNC_COM_CHANNEL_CLOSE) {
479         res.hdr.response = SYNC_OK;
480         res.hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
481
482         /* don't respond, just drop; senders of SYNC_COM_CHANNEL_CLOSE
483          * never wait for a response. */
484         goto done;
485     }
486
487     ViceLog(125, ("FSYNC_com: from fd %d got command %ld (%s) reason %ld (%s) "
488                   "pt %ld (%s) pid %ld\n", (int)fd,
489                   afs_printable_int32_ld(com.hdr.command),
490                   FSYNC_com2string(com.hdr.command),
491                   afs_printable_int32_ld(com.hdr.reason),
492                   FSYNC_reason2string(com.hdr.reason),
493                   afs_printable_int32_ld(com.hdr.programType),
494                   VPTypeToString(com.hdr.programType),
495                   afs_printable_int32_ld(com.hdr.pid)));
496
497     res.hdr.com_seq = com.hdr.com_seq;
498
499     VOL_LOCK;
500     switch (com.hdr.command) {
501     case FSYNC_VOL_ON:
502     case FSYNC_VOL_ATTACH:
503     case FSYNC_VOL_LEAVE_OFF:
504     case FSYNC_VOL_OFF:
505     case FSYNC_VOL_FORCE_ERROR:
506     case FSYNC_VOL_LISTVOLUMES:
507     case FSYNC_VOL_NEEDVOLUME:
508     case FSYNC_VOL_MOVE:
509     case FSYNC_VOL_BREAKCBKS:
510     case FSYNC_VOL_DONE:
511     case FSYNC_VOL_QUERY:
512     case FSYNC_VOL_QUERY_HDR:
513 #ifdef AFS_DEMAND_ATTACH_FS
514     case FSYNC_VOL_QUERY_VOP:
515     case FSYNC_VG_QUERY:
516     case FSYNC_VG_SCAN:
517     case FSYNC_VG_SCAN_ALL:
518 #endif
519         res.hdr.response = FSYNC_com_VolOp(fd, &com, &res);
520         break;
521     case FSYNC_VOL_STATS_GENERAL:
522     case FSYNC_VOL_STATS_VICEP:
523     case FSYNC_VOL_STATS_HASH:
524     case FSYNC_VOL_STATS_HDR:
525     case FSYNC_VOL_STATS_VLRU:
526         res.hdr.response = FSYNC_com_StatsOp(fd, &com, &res);
527         break;
528     case FSYNC_VOL_QUERY_VNODE:
529         res.hdr.response = FSYNC_com_VnQry(fd, &com, &res);
530         break;
531 #ifdef AFS_DEMAND_ATTACH_FS
532     case FSYNC_VG_ADD:
533     case FSYNC_VG_DEL:
534         res.hdr.response = FSYNC_com_VGUpdate(fd, &com, &res);
535         break;
536 #endif
537     default:
538         res.hdr.response = SYNC_BAD_COMMAND;
539         break;
540     }
541     VOL_UNLOCK;
542
543     ViceLog(125, ("FSYNC_com: fd %d responding with code %ld (%s) reason %ld "
544                   "(%s)\n", (int)fd,
545                   afs_printable_int32_ld(res.hdr.response),
546                   SYNC_res2string(res.hdr.response),
547                   afs_printable_int32_ld(res.hdr.reason),
548                   FSYNC_reason2string(res.hdr.reason)));
549
550  respond:
551     SYNC_putRes(&fssync_server_state, fd, &res);
552
553  done:
554     if (res.hdr.flags & SYNC_FLAG_CHANNEL_SHUTDOWN) {
555         FSYNC_Drop(fd);
556     }
557 }
558
559 static afs_int32
560 FSYNC_com_VolOp(osi_socket fd, SYNC_command * com, SYNC_response * res)
561 {
562     int i;
563     afs_int32 code = SYNC_OK;
564     FSSYNC_VolOp_command vcom;
565
566     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_VolOp_hdr))) {
567         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
568         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
569         return SYNC_COM_ERROR;
570     }
571
572     vcom.hdr = &com->hdr;
573     vcom.vop = (FSSYNC_VolOp_hdr *) com->payload.buf;
574     vcom.com = com;
575
576     vcom.volumes = OfflineVolumes[FindHandler(fd)];
577     for (vcom.v = NULL, i = 0; i < MAXOFFLINEVOLUMES; i++) {
578         if ((vcom.volumes[i].volumeID == vcom.vop->volume) &&
579             (strncmp(vcom.volumes[i].partName, vcom.vop->partName,
580                      sizeof(vcom.volumes[i].partName)) == 0)) {
581             vcom.v = &vcom.volumes[i];
582             break;
583         }
584     }
585
586     ViceLog(125, ("FSYNC_com_VolOp: fd %d got command for vol %lu part %.16s\n",
587                   (int)fd, afs_printable_uint32_lu(vcom.vop->volume),
588                   vcom.vop->partName));
589
590     switch (com->hdr.command) {
591     case FSYNC_VOL_ON:
592     case FSYNC_VOL_ATTACH:
593     case FSYNC_VOL_LEAVE_OFF:
594         code = FSYNC_com_VolOn(&vcom, res);
595         break;
596     case FSYNC_VOL_OFF:
597     case FSYNC_VOL_NEEDVOLUME:
598         code = FSYNC_com_VolOff(&vcom, res);
599         break;
600     case FSYNC_VOL_LISTVOLUMES:
601         code = SYNC_OK;
602         break;
603     case FSYNC_VOL_MOVE:
604         code = FSYNC_com_VolMove(&vcom, res);
605         break;
606     case FSYNC_VOL_BREAKCBKS:
607         code = FSYNC_com_VolBreakCBKs(&vcom, res);
608         break;
609     case FSYNC_VOL_DONE:
610         code = FSYNC_com_VolDone(&vcom, res);
611         break;
612     case FSYNC_VOL_QUERY:
613         code = FSYNC_com_VolQuery(&vcom, res);
614         break;
615     case FSYNC_VOL_QUERY_HDR:
616         code = FSYNC_com_VolHdrQuery(&vcom, res);
617         break;
618 #ifdef AFS_DEMAND_ATTACH_FS
619     case FSYNC_VOL_FORCE_ERROR:
620         code = FSYNC_com_VolError(&vcom, res);
621         break;
622     case FSYNC_VOL_QUERY_VOP:
623         code = FSYNC_com_VolOpQuery(&vcom, res);
624         break;
625     case FSYNC_VG_QUERY:
626         code = FSYNC_com_VGQuery(&vcom, res);
627         break;
628     case FSYNC_VG_SCAN:
629         code = FSYNC_com_VGScan(&vcom, res);
630         break;
631     case FSYNC_VG_SCAN_ALL:
632         code = FSYNC_com_VGScanAll(&vcom, res);
633         break;
634 #endif /* AFS_DEMAND_ATTACH_FS */
635     default:
636         code = SYNC_BAD_COMMAND;
637     }
638
639     return code;
640 }
641
642 /**
643  * service an FSYNC request to bring a volume online.
644  *
645  * @param[in]   vcom  pointer command object
646  * @param[out]  res   object in which to store response packet
647  *
648  * @return operation status
649  *   @retval SYNC_OK volume transitioned online
650  *   @retval SYNC_FAILED invalid command protocol message
651  *   @retval SYNC_DENIED operation could not be completed
652  *
653  * @note this is an FSYNC RPC server stub
654  *
655  * @note this procedure handles the following FSSYNC command codes:
656  *       - FSYNC_VOL_ON
657  *       - FSYNC_VOL_ATTACH
658  *       - FSYNC_VOL_LEAVE_OFF
659  *
660  * @note the supplementary reason code contains additional details.
661  *       When SYNC_DENIED is returned, the specific reason is
662  *       placed in the response packet reason field.
663  *
664  * @internal
665  */
666 static afs_int32
667 FSYNC_com_VolOn(FSSYNC_VolOp_command * vcom, SYNC_response * res)
668 {
669     afs_int32 code = SYNC_OK;
670 #ifndef AFS_DEMAND_ATTACH_FS
671     char tvolName[VMAXPATHLEN];
672 #endif
673     Volume * vp;
674     Error error;
675
676     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
677         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
678         code = SYNC_FAILED;
679         goto done;
680     }
681
682     /* so, we need to attach the volume */
683
684 #ifdef AFS_DEMAND_ATTACH_FS
685     /* check DAFS permissions */
686     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
687     if (vp &&
688         FSYNC_partMatch(vcom, vp, 1) &&
689         vp->pending_vol_op &&
690         (vcom->hdr->programType != vp->pending_vol_op->com.programType)) {
691         /* a different program has this volume checked out. deny. */
692         Log("FSYNC_VolOn: WARNING: program type %u has attempted to manipulate "
693             "state for volume %u using command code %u while the volume is "
694             "checked out by program type %u for command code %u.\n",
695             vcom->hdr->programType,
696             vcom->vop->volume,
697             vcom->hdr->command,
698             vp->pending_vol_op->com.programType,
699             vp->pending_vol_op->com.command);
700         code = SYNC_DENIED;
701         res->hdr.reason = FSYNC_EXCLUSIVE;
702         goto done;
703     }
704 #endif
705
706     if (vcom->v)
707         vcom->v->volumeID = 0;
708
709
710     if (vcom->hdr->command == FSYNC_VOL_LEAVE_OFF) {
711         /* nothing much to do if we're leaving the volume offline */
712 #ifdef AFS_DEMAND_ATTACH_FS
713         if (vp) {
714             VCreateReservation_r(vp);
715             VWaitExclusiveState_r(vp);
716         }
717         if (vp && V_attachState(vp) != VOL_STATE_DELETED) {
718             if (FSYNC_partMatch(vcom, vp, 1)) {
719                 if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
720                     (V_attachState(vp) == VOL_STATE_PREATTACHED)) {
721                     VChangeState_r(vp, VOL_STATE_UNATTACHED);
722                     VDeregisterVolOp_r(vp);
723                 } else {
724                     code = SYNC_DENIED;
725                     res->hdr.reason = FSYNC_BAD_STATE;
726                 }
727             } else {
728                 code = SYNC_DENIED;
729                 res->hdr.reason = FSYNC_WRONG_PART;
730             }
731         } else {
732             code = SYNC_FAILED;
733             res->hdr.reason = FSYNC_UNKNOWN_VOLID;
734         }
735
736         if (vp) {
737             VCancelReservation_r(vp);
738             vp = NULL;
739         }
740 #endif
741         goto done;
742     }
743
744 #ifdef AFS_DEMAND_ATTACH_FS
745     /* first, check to see whether we have such a volume defined */
746     vp = VPreAttachVolumeById_r(&error,
747                                 vcom->vop->partName,
748                                 vcom->vop->volume);
749     if (vp) {
750         VCreateReservation_r(vp);
751         VWaitExclusiveState_r(vp);
752         VDeregisterVolOp_r(vp);
753         VCancelReservation_r(vp);
754         vp = NULL;
755     }
756 #else /* !AFS_DEMAND_ATTACH_FS */
757     tvolName[0] = OS_DIRSEPC;
758     snprintf(&tvolName[1], sizeof(tvolName)-1, VFORMAT, afs_printable_uint32_lu(vcom->vop->volume));
759     tvolName[sizeof(tvolName)-1] = '\0';
760
761     vp = VAttachVolumeByName_r(&error, vcom->vop->partName, tvolName,
762                                V_VOLUPD);
763     if (vp)
764         VPutVolume_r(vp);
765     if (error) {
766         code = SYNC_DENIED;
767         res->hdr.reason = error;
768     }
769 #endif /* !AFS_DEMAND_ATTACH_FS */
770
771  done:
772     return code;
773 }
774
775 /**
776  * service an FSYNC request to take a volume offline.
777  *
778  * @param[in]   vcom  pointer command object
779  * @param[out]  res   object in which to store response packet
780  *
781  * @return operation status
782  *   @retval SYNC_OK volume transitioned offline
783  *   @retval SYNC_FAILED invalid command protocol message
784  *   @retval SYNC_DENIED operation could not be completed
785  *
786  * @note this is an FSYNC RPC server stub
787  *
788  * @note this procedure handles the following FSSYNC command codes:
789  *       - FSYNC_VOL_OFF
790  *       - FSYNC_VOL_NEEDVOLUME
791  *
792  * @note the supplementary reason code contains additional details.
793  *       When SYNC_DENIED is returned, the specific reason is
794  *       placed in the response packet reason field.
795  *
796  * @internal
797  */
798 static afs_int32
799 FSYNC_com_VolOff(FSSYNC_VolOp_command * vcom, SYNC_response * res)
800 {
801     FSSYNC_VolOp_info info;
802     afs_int32 code = SYNC_OK;
803     int i;
804     Volume * vp;
805     Error error;
806 #ifdef AFS_DEMAND_ATTACH_FS
807     Volume *nvp, *rvp = NULL;
808 #endif
809
810     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
811         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
812         code = SYNC_FAILED;
813         goto done;
814     }
815
816     /* not already offline, we need to find a slot for newly offline volume */
817     if (vcom->hdr->programType == debugUtility) {
818         /* debug utilities do not have their operations tracked */
819         vcom->v = NULL;
820     } else {
821         if (!vcom->v) {
822             for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
823                 if (vcom->volumes[i].volumeID == 0) {
824                     vcom->v = &vcom->volumes[i];
825                     break;
826                 }
827             }
828         }
829         if (!vcom->v) {
830             goto deny;
831         }
832     }
833
834     FSYNC_com_to_info(vcom, &info);
835
836 #ifdef AFS_DEMAND_ATTACH_FS
837     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
838 #else
839     vp = VGetVolume_r(&error, vcom->vop->volume);
840 #endif
841
842     if (vp) {
843             if (!FSYNC_partMatch(vcom, vp, 1)) {
844             /* volume on desired partition is not online, so we
845              * should treat this as an offline volume.
846              */
847 #ifndef AFS_DEMAND_ATTACH_FS
848             VPutVolume_r(vp);
849 #endif
850             vp = NULL;
851             goto done;
852         }
853     }
854
855 #ifdef AFS_DEMAND_ATTACH_FS
856     if (vp) {
857         ProgramType type = (ProgramType) vcom->hdr->programType;
858
859         /* do initial filtering of requests */
860
861         /* enforce mutual exclusion for volume ops */
862         if (vp->pending_vol_op) {
863             if (vp->pending_vol_op->com.programType != type) {
864                 if (vp->pending_vol_op->com.command == FSYNC_VOL_OFF &&
865                     vp->pending_vol_op->com.reason == FSYNC_SALVAGE) {
866
867                     Log("denying offline request for volume %lu; volume is salvaging\n",
868                         afs_printable_uint32_lu(vp->hashid));
869
870                     res->hdr.reason = FSYNC_SALVAGE;
871                     goto deny;
872                 }
873                 Log("volume %u already checked out\n", vp->hashid);
874                 /* XXX debug */
875                 Log("vp->vop = { com = { ver=%u, prog=%d, com=%d, reason=%d, len=%u, flags=0x%x }, vop = { vol=%u, part='%s' } }\n",
876                     vp->pending_vol_op->com.proto_version,
877                     vp->pending_vol_op->com.programType,
878                     vp->pending_vol_op->com.command,
879                     vp->pending_vol_op->com.reason,
880                     vp->pending_vol_op->com.command_len,
881                     vp->pending_vol_op->com.flags,
882                     vp->pending_vol_op->vop.volume,
883                     vp->pending_vol_op->vop.partName );
884                 Log("vcom = { com = { ver=%u, prog=%d, com=%d, reason=%d, len=%u, flags=0x%x } , vop = { vol=%u, part='%s' } }\n",
885                     vcom->hdr->proto_version,
886                     vcom->hdr->programType,
887                     vcom->hdr->command,
888                     vcom->hdr->reason,
889                     vcom->hdr->command_len,
890                     vcom->hdr->flags,
891                     vcom->vop->volume,
892                     vcom->vop->partName);
893                 res->hdr.reason = FSYNC_EXCLUSIVE;
894                 goto deny;
895             } else {
896                 Log("warning: volume %u recursively checked out by programType id %d\n",
897                     vp->hashid, vcom->hdr->programType);
898             }
899         }
900
901         /* wait for exclusive ops, so we have an accurate picture of the
902          * vol attach state */
903         VCreateReservation_r(vp);
904         VWaitExclusiveState_r(vp);
905         rvp = vp;
906
907         /* filter based upon requestor
908          *
909          * volume utilities / volserver are not allowed to check out
910          * volumes which are in an error state
911          *
912          * unknown utility programs will be denied on principal
913          */
914         switch (type) {
915         case salvageServer:
916         case volumeSalvager:
917             /* it is possible for the salvageserver to checkout a
918              * volume for salvage before its scheduling request
919              * has been sent to the salvageserver */
920             if (vp->salvage.requested && !vp->salvage.scheduled) {
921                 vp->salvage.scheduled = 1;
922             }
923
924             /* If the volume is in VOL_STATE_SALVAGE_REQ, we need to wait
925              * for the vol to go offline before we can give it away. Also
926              * make sure we don't come out with vp in an excl state. */
927             while (V_attachState(vp) == VOL_STATE_SALVAGE_REQ ||
928                    VIsExclusiveState(V_attachState(vp))) {
929
930                 VOL_CV_WAIT(&V_attachCV(vp));
931             }
932
933         case debugUtility:
934             break;
935
936         case volumeUtility:
937         case volumeServer:
938             if (VIsSalvaging(vp)) {
939                 Log("denying offline request for volume %lu; volume is in salvaging state\n",
940                     afs_printable_uint32_lu(vp->hashid));
941                 res->hdr.reason = FSYNC_SALVAGE;
942
943                 /* the volume hasn't been checked out yet by the salvager,
944                  * but we think the volume is salvaging; schedule a
945                  * a salvage to update the salvage priority */
946                 FSYNC_backgroundSalvage(vp);
947
948                 goto deny;
949             }
950             if (VIsErrorState(V_attachState(vp))) {
951                 goto deny;
952             }
953             break;
954
955         default:
956             Log("bad program type passed to FSSYNC\n");
957             goto deny;
958         }
959
960         /* short circuit for offline volume states
961          * so we can avoid I/O penalty of attachment */
962         switch (V_attachState(vp)) {
963         case VOL_STATE_UNATTACHED:
964         case VOL_STATE_PREATTACHED:
965         case VOL_STATE_SALVAGING:
966         case VOL_STATE_ERROR:
967             /* register the volume operation metadata with the volume
968              *
969              * if the volume is currently pre-attached, attach2()
970              * will evaluate the vol op metadata to determine whether
971              * attaching the volume would be safe */
972             VRegisterVolOp_r(vp, &info);
973             vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningUnknown;
974             /* fall through */
975
976         case VOL_STATE_DELETED:
977             goto done;
978         default:
979             break;
980         }
981
982         /* convert to heavyweight ref */
983         nvp = VGetVolumeByVp_r(&error, vp);
984         if (!nvp) {
985             /*
986              * It's possible for VGetVolumeByVp_r to have dropped and
987              * re-acquired VOL_LOCK, so volume state may have changed
988              * back to one of the states we tested for above. Since
989              * GetVolume can return NULL in some of those states, just
990              * test for the states again here.
991              */
992             switch (V_attachState(vp)) {
993             case VOL_STATE_UNATTACHED:
994             case VOL_STATE_PREATTACHED:
995             case VOL_STATE_SALVAGING:
996             case VOL_STATE_ERROR:
997                 /* register the volume operation metadata with the volume
998                  *
999                  * if the volume is currently pre-attached, attach2()
1000                  * will evaluate the vol op metadata to determine whether
1001                  * attaching the volume would be safe */
1002                 VRegisterVolOp_r(vp, &info);
1003                 vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningUnknown;
1004                 /* fall through */
1005
1006             case VOL_STATE_DELETED:
1007                 goto done;
1008             default:
1009                 break;
1010             }
1011
1012             Log("FSYNC_com_VolOff: failed to get heavyweight reference to volume %u (state=%u, flags=0x%x)\n",
1013                 vcom->vop->volume, V_attachState(vp), V_attachFlags(vp));
1014             res->hdr.reason = FSYNC_VOL_PKG_ERROR;
1015             goto deny;
1016         } else if (nvp != vp) {
1017             /* i don't think this should ever happen, but just in case... */
1018             Log("FSYNC_com_VolOff: warning: potentially dangerous race detected\n");
1019             vp = nvp;
1020         }
1021
1022         /* kill off lightweight ref to ensure we can't deadlock against ourselves later... */
1023         VCancelReservation_r(rvp);
1024         rvp = NULL;
1025
1026         /* register the volume operation metadata with the volume */
1027         VRegisterVolOp_r(vp, &info);
1028
1029     }
1030 #endif /* AFS_DEMAND_ATTACH_FS */
1031
1032     if (vp) {
1033         if (VVolOpLeaveOnline_r(vp, &info)) {
1034             VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT);       /* At least get volume stats right */
1035             if (LogLevel) {
1036                 Log("FSYNC: Volume %u (%s) was left on line for an external %s request\n",
1037                     V_id(vp), V_name(vp),
1038                     vcom->hdr->reason == V_CLONE ? "clone" :
1039                     vcom->hdr->reason == V_READONLY ? "readonly" :
1040                     vcom->hdr->reason == V_DUMP ? "dump" :
1041                     vcom->hdr->reason == FSYNC_SALVAGE ? "salvage" :
1042                     "UNKNOWN");
1043             }
1044 #ifdef AFS_DEMAND_ATTACH_FS
1045             vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
1046 #endif
1047             VPutVolume_r(vp);
1048         } else {
1049             if (VVolOpSetVBusy_r(vp, &info)) {
1050                 vp->specialStatus = VBUSY;
1051             }
1052
1053             /* remember what volume we got, so we can keep track of how
1054              * many volumes the volserver or whatever is using.  Note that
1055              * vp is valid since leaveonline is only set when vp is valid.
1056              */
1057             if (vcom->v) {
1058                 vcom->v->volumeID = vcom->vop->volume;
1059                 strlcpy(vcom->v->partName, vp->partition->name, sizeof(vcom->v->partName));
1060             }
1061
1062 #ifdef AFS_DEMAND_ATTACH_FS
1063             VCreateReservation_r(vp);
1064             VOfflineForVolOp_r(&error, vp, "A volume utility is running.");
1065             if (error==0) {
1066                 osi_Assert(vp->nUsers==0);
1067                 vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
1068             }
1069             else {
1070                 VWaitExclusiveState_r(vp);
1071                 VDeregisterVolOp_r(vp);
1072                 code = SYNC_DENIED;
1073             }
1074             VCancelReservation_r(vp);
1075 #else
1076             VOffline_r(vp, "A volume utility is running.");
1077 #endif
1078             vp = NULL;
1079         }
1080     }
1081     goto done;
1082
1083  deny:
1084     code = SYNC_DENIED;
1085
1086  done:
1087 #ifdef AFS_DEMAND_ATTACH_FS
1088     if (rvp) {
1089         VCancelReservation_r(rvp);
1090     }
1091 #endif
1092     return code;
1093 }
1094
1095 /**
1096  * service an FSYNC request to mark a volume as moved.
1097  *
1098  * @param[in]   vcom  pointer command object
1099  * @param[out]  res   object in which to store response packet
1100  *
1101  * @return operation status
1102  *   @retval SYNC_OK volume marked as moved to a remote server
1103  *   @retval SYNC_FAILED invalid command protocol message
1104  *   @retval SYNC_DENIED current volume state does not permit this operation
1105  *
1106  * @note this is an FSYNC RPC server stub
1107  *
1108  * @note this operation also breaks all callbacks for the given volume
1109  *
1110  * @note this procedure handles the following FSSYNC command codes:
1111  *       - FSYNC_VOL_MOVE
1112  *
1113  * @note the supplementary reason code contains additional details.  For
1114  *       instance, SYNC_OK is still returned when the partition specified
1115  *       does not match the one registered in the volume object -- reason
1116  *       will be FSYNC_WRONG_PART in this case.
1117  *
1118  * @internal
1119  */
1120 static afs_int32
1121 FSYNC_com_VolMove(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1122 {
1123     afs_int32 code = SYNC_DENIED;
1124     Error error;
1125     Volume * vp;
1126
1127     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1128         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1129         code = SYNC_FAILED;
1130         goto done;
1131     }
1132
1133     /* Yuch:  the "reason" for the move is the site it got moved to... */
1134     /* still set specialStatus so we stop sending back VBUSY.
1135      * also should still break callbacks.  Note that I don't know
1136      * how to tell if we should break all or not, so we just do it
1137      * since it doesn't matter much if we do an extra break
1138      * volume callbacks on a volume move within the same server */
1139 #ifdef AFS_DEMAND_ATTACH_FS
1140     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1141 #else
1142     vp = VGetVolume_r(&error, vcom->vop->volume);
1143 #endif
1144     if (vp) {
1145         if (FSYNC_partMatch(vcom, vp, 1)) {
1146 #ifdef AFS_DEMAND_ATTACH_FS
1147             if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
1148                 (V_attachState(vp) == VOL_STATE_PREATTACHED)) {
1149 #endif
1150                 code = SYNC_OK;
1151                 vp->specialStatus = VMOVED;
1152 #ifdef AFS_DEMAND_ATTACH_FS
1153             } else {
1154                 res->hdr.reason = FSYNC_BAD_STATE;
1155             }
1156 #endif
1157         } else {
1158             res->hdr.reason = FSYNC_WRONG_PART;
1159         }
1160 #ifndef AFS_DEMAND_ATTACH_FS
1161         VPutVolume_r(vp);
1162 #endif /* !AFS_DEMAND_ATTACH_FS */
1163     } else {
1164         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1165     }
1166
1167     if ((code == SYNC_OK) && (V_BreakVolumeCallbacks != NULL)) {
1168         Log("fssync: volume %u moved to %x; breaking all call backs\n",
1169             vcom->vop->volume, vcom->hdr->reason);
1170         VOL_UNLOCK;
1171         (*V_BreakVolumeCallbacks) (vcom->vop->volume);
1172         VOL_LOCK;
1173     }
1174
1175
1176  done:
1177     return code;
1178 }
1179
1180 /**
1181  * service an FSYNC request to mark a volume as destroyed.
1182  *
1183  * @param[in]   vcom  pointer command object
1184  * @param[out]  res   object in which to store response packet
1185  *
1186  * @return operation status
1187  *   @retval SYNC_OK volume marked as destroyed
1188  *   @retval SYNC_FAILED invalid command protocol message
1189  *   @retval SYNC_DENIED current volume state does not permit this operation
1190  *
1191  * @note this is an FSYNC RPC server stub
1192  *
1193  * @note this procedure handles the following FSSYNC command codes:
1194  *       - FSYNC_VOL_DONE
1195  *
1196  * @note the supplementary reason code contains additional details.  For
1197  *       instance, SYNC_OK is still returned when the partition specified
1198  *       does not match the one registered in the volume object -- reason
1199  *       will be FSYNC_WRONG_PART in this case.
1200  *
1201  * @internal
1202  */
1203 static afs_int32
1204 FSYNC_com_VolDone(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1205 {
1206     afs_int32 code = SYNC_FAILED;
1207     Error error;
1208     Volume * vp;
1209
1210     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1211         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1212         goto done;
1213     }
1214
1215     /* don't try to put online, this call is made only after deleting
1216      * a volume, in which case we want to remove the vol # from the
1217      * OfflineVolumes array only */
1218     if (vcom->v)
1219         vcom->v->volumeID = 0;
1220
1221     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1222     if (vp) {
1223         if (FSYNC_partMatch(vcom, vp, 1)) {
1224 #ifdef AFS_DEMAND_ATTACH_FS
1225             VCreateReservation_r(vp);
1226             VWaitExclusiveState_r(vp);
1227
1228             if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
1229                 (V_attachState(vp) == VOL_STATE_PREATTACHED) ||
1230                 VIsErrorState(V_attachState(vp))) {
1231
1232                 /* Change state to DELETED, not UNATTACHED, so clients get
1233                  * a VNOVOL error when they try to access from now on. */
1234
1235                 VChangeState_r(vp, VOL_STATE_DELETED);
1236                 VDeregisterVolOp_r(vp);
1237
1238                 /* Volume is gone; clear out old salvage stats */
1239                 memset(&vp->salvage, 0, sizeof(vp->salvage));
1240
1241                 /* Someday we should free the vp, too, after about 2 hours,
1242                  * possibly by putting the vp back on the VLRU. */
1243
1244                 code = SYNC_OK;
1245             } else if (V_attachState(vp) == VOL_STATE_DELETED) {
1246                 VDeregisterVolOp_r(vp);
1247                 res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1248
1249             } else {
1250                 code = SYNC_DENIED;
1251                 res->hdr.reason = FSYNC_BAD_STATE;
1252             }
1253
1254             VCancelReservation_r(vp);
1255             vp = NULL;
1256 #else /* AFS_DEMAND_ATTACH_FS */
1257             if (!vp->specialStatus) {
1258                 vp->specialStatus = VNOVOL;
1259             }
1260             code = SYNC_OK;
1261 #endif /* !AFS_DEMAND_ATTACH_FS */
1262         } else {
1263             code = SYNC_OK; /* XXX is this really a good idea? */
1264             res->hdr.reason = FSYNC_WRONG_PART;
1265         }
1266     } else {
1267         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1268     }
1269
1270  done:
1271     return code;
1272 }
1273
1274 #ifdef AFS_DEMAND_ATTACH_FS
1275 /**
1276  * service an FSYNC request to transition a volume to the hard error state.
1277  *
1278  * @param[in]   vcom  pointer command object
1279  * @param[out]  res   object in which to store response packet
1280  *
1281  * @return operation status
1282  *   @retval SYNC_OK volume transitioned to hard error state
1283  *   @retval SYNC_FAILED invalid command protocol message
1284  *   @retval SYNC_DENIED (see note)
1285  *
1286  * @note this is an FSYNC RPC server stub
1287  *
1288  * @note this procedure handles the following FSSYNC command codes:
1289  *       - FSYNC_VOL_FORCE_ERROR
1290  *
1291  * @note SYNC_DENIED is returned in the following cases:
1292  *        - no partition name is specified (reason field set to
1293  *          FSYNC_WRONG_PART).
1294  *        - volume id not known to fileserver (reason field set
1295  *          to FSYNC_UNKNOWN_VOLID).
1296  *
1297  * @note demand attach fileserver only
1298  *
1299  * @internal
1300  */
1301 static afs_int32
1302 FSYNC_com_VolError(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1303 {
1304     Error error;
1305     Volume * vp;
1306     afs_int32 code = SYNC_FAILED;
1307
1308     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1309         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1310         goto done;
1311     }
1312
1313     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1314
1315     if (!vp && vcom->hdr->reason == FSYNC_SALVAGE) {
1316         /* The requested volume doesn't seem to exist. However, it is possible
1317          * that this is triggered by trying to create or clone a volume that
1318          * was prevented from succeeding by a half-created volume in the way.
1319          * (e.g. we tried to create volume X, but volume X exists except that
1320          * its .vol header was deleted for some reason) So, still try to
1321          * a salvage for that volume ID. */
1322
1323         Log("FSYNC_com_VolError: attempting to schedule salvage for unknown "
1324             "volume %lu part %s\n", afs_printable_uint32_lu(vcom->vop->volume),
1325             vcom->vop->partName);
1326         vp = VPreAttachVolumeById_r(&error, vcom->vop->partName,
1327                                     vcom->vop->volume);
1328     }
1329
1330     if (vp) {
1331         if (FSYNC_partMatch(vcom, vp, 0)) {
1332             VCreateReservation_r(vp);
1333             VWaitExclusiveState_r(vp);
1334             VDeregisterVolOp_r(vp);
1335
1336             if (vcom->hdr->reason == FSYNC_SALVAGE) {
1337                 FSYNC_backgroundSalvage(vp);
1338             } else {
1339                 /* null out salvsync control state, as it's no longer relevant */
1340                 memset(&vp->salvage, 0, sizeof(vp->salvage));
1341                 VChangeState_r(vp, VOL_STATE_ERROR);
1342             }
1343
1344             VCancelReservation_r(vp);
1345             vp = NULL;
1346
1347             code = SYNC_OK;
1348         } else {
1349             res->hdr.reason = FSYNC_WRONG_PART;
1350         }
1351     } else {
1352         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1353     }
1354
1355  done:
1356     return code;
1357 }
1358 #endif /* AFS_DEMAND_ATTACH_FS */
1359
1360 /**
1361  * service an FSYNC request to break all callbacks for this volume.
1362  *
1363  * @param[in]   vcom  pointer command object
1364  * @param[out]  res   object in which to store response packet
1365  *
1366  * @return operation status
1367  *   @retval SYNC_OK callback breaks scheduled for volume
1368  *
1369  * @note this is an FSYNC RPC server stub
1370  *
1371  * @note this procedure handles the following FSSYNC command codes:
1372  *       - FSYNC_VOL_BREAKCBKS
1373  *
1374  * @note demand attach fileserver only
1375  *
1376  * @todo should do partition matching
1377  *
1378  * @internal
1379  */
1380 static afs_int32
1381 FSYNC_com_VolBreakCBKs(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1382 {
1383     /* if the volume is being restored, break all callbacks on it */
1384     if (V_BreakVolumeCallbacks) {
1385         Log("fssync: breaking all call backs for volume %u\n",
1386             vcom->vop->volume);
1387         VOL_UNLOCK;
1388         (*V_BreakVolumeCallbacks) (vcom->vop->volume);
1389         VOL_LOCK;
1390     }
1391     return SYNC_OK;
1392 }
1393
1394 /**
1395  * service an FSYNC request to return the Volume object.
1396  *
1397  * @param[in]   vcom  pointer command object
1398  * @param[out]  res   object in which to store response packet
1399  *
1400  * @return operation status
1401  *   @retval SYNC_OK      volume object returned to caller
1402  *   @retval SYNC_FAILED  bad command packet, or failed to locate volume object
1403  *
1404  * @note this is an FSYNC RPC server stub
1405  *
1406  * @note this procedure handles the following FSSYNC command codes:
1407  *       - FSYNC_VOL_QUERY
1408  *
1409  * @internal
1410  */
1411 static afs_int32
1412 FSYNC_com_VolQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1413 {
1414     afs_int32 code = SYNC_FAILED;
1415     Error error;
1416     Volume * vp;
1417
1418     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1419         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1420         goto done;
1421     }
1422
1423 #ifdef AFS_DEMAND_ATTACH_FS
1424     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1425 #else /* !AFS_DEMAND_ATTACH_FS */
1426     vp = VGetVolume_r(&error, vcom->vop->volume);
1427 #endif /* !AFS_DEMAND_ATTACH_FS */
1428
1429     if (vp) {
1430         if (FSYNC_partMatch(vcom, vp, 1)) {
1431             if (res->payload.len >= sizeof(Volume)) {
1432                 memcpy(res->payload.buf, vp, sizeof(Volume));
1433                 res->hdr.response_len += sizeof(Volume);
1434                 code = SYNC_OK;
1435             } else {
1436                 res->hdr.reason = SYNC_REASON_PAYLOAD_TOO_BIG;
1437             }
1438         } else {
1439             res->hdr.reason = FSYNC_WRONG_PART;
1440         }
1441 #ifndef AFS_DEMAND_ATTACH_FS
1442         VPutVolume_r(vp);
1443 #endif
1444     } else {
1445         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1446     }
1447
1448  done:
1449     return code;
1450 }
1451
1452 /**
1453  * service an FSYNC request to return the Volume header.
1454  *
1455  * @param[in]   vcom  pointer command object
1456  * @param[out]  res   object in which to store response packet
1457  *
1458  * @return operation status
1459  *   @retval SYNC_OK volume header returned to caller
1460  *   @retval SYNC_FAILED  bad command packet, or failed to locate volume header
1461  *
1462  * @note this is an FSYNC RPC server stub
1463  *
1464  * @note this procedure handles the following FSSYNC command codes:
1465  *       - FSYNC_VOL_QUERY_HDR
1466  *
1467  * @internal
1468  */
1469 static afs_int32
1470 FSYNC_com_VolHdrQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1471 {
1472     afs_int32 code = SYNC_FAILED;
1473     Error error;
1474     Volume * vp;
1475
1476     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1477         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1478         goto done;
1479     }
1480     if (res->payload.len < sizeof(VolumeDiskData)) {
1481         res->hdr.reason = SYNC_REASON_PAYLOAD_TOO_BIG;
1482         goto done;
1483     }
1484
1485 #ifdef AFS_DEMAND_ATTACH_FS
1486     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1487 #else /* !AFS_DEMAND_ATTACH_FS */
1488     vp = VGetVolume_r(&error, vcom->vop->volume);
1489 #endif
1490
1491     if (vp) {
1492         if (FSYNC_partMatch(vcom, vp, 1)) {
1493 #ifdef AFS_DEMAND_ATTACH_FS
1494             if ((vp->header == NULL) ||
1495                 !(V_attachFlags(vp) & VOL_HDR_ATTACHED) ||
1496                 !(V_attachFlags(vp) & VOL_HDR_LOADED)) {
1497                 res->hdr.reason = FSYNC_HDR_NOT_ATTACHED;
1498                 goto cleanup;
1499             }
1500 #else /* !AFS_DEMAND_ATTACH_FS */
1501             if (!vp || !vp->header) {
1502                 res->hdr.reason = FSYNC_HDR_NOT_ATTACHED;
1503                 goto cleanup;
1504             }
1505 #endif /* !AFS_DEMAND_ATTACH_FS */
1506         } else {
1507             res->hdr.reason = FSYNC_WRONG_PART;
1508             goto cleanup;
1509         }
1510     } else {
1511         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1512         goto done;
1513     }
1514
1515     memcpy(res->payload.buf, &V_disk(vp), sizeof(VolumeDiskData));
1516     res->hdr.response_len += sizeof(VolumeDiskData);
1517     code = SYNC_OK;
1518
1519  cleanup:
1520 #ifndef AFS_DEMAND_ATTACH_FS
1521     VPutVolume_r(vp);
1522 #endif
1523
1524  done:
1525     return code;
1526 }
1527
1528 #ifdef AFS_DEMAND_ATTACH_FS
1529 static afs_int32
1530 FSYNC_com_VolOpQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1531 {
1532     afs_int32 code = SYNC_OK;
1533     Error error;
1534     Volume * vp;
1535
1536     vp = VLookupVolume_r(&error, vcom->vop->volume, NULL);
1537
1538     if (vp) {
1539         VCreateReservation_r(vp);
1540         VWaitExclusiveState_r(vp);
1541     }
1542
1543     if (vp && vp->pending_vol_op) {
1544         if (!FSYNC_partMatch(vcom, vp, 1)) {
1545             res->hdr.reason = FSYNC_WRONG_PART;
1546             code = SYNC_FAILED;
1547         } else {
1548             osi_Assert(sizeof(FSSYNC_VolOp_info) <= res->payload.len);
1549             memcpy(res->payload.buf, vp->pending_vol_op, sizeof(FSSYNC_VolOp_info));
1550             res->hdr.response_len += sizeof(FSSYNC_VolOp_info);
1551         }
1552     } else {
1553         if (!vp || V_attachState(vp) == VOL_STATE_DELETED) {
1554             res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1555         } else if (!FSYNC_partMatch(vcom, vp, 1)) {
1556             res->hdr.reason = FSYNC_WRONG_PART;
1557         } else {
1558             res->hdr.reason = FSYNC_NO_PENDING_VOL_OP;
1559         }
1560         code = SYNC_FAILED;
1561     }
1562
1563     if (vp) {
1564         VCancelReservation_r(vp);
1565     }
1566     return code;
1567 }
1568
1569 static afs_int32
1570 FSYNC_com_VGQuery(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1571 {
1572     afs_int32 code = SYNC_FAILED;
1573     int rc;
1574     struct DiskPartition64 * dp;
1575
1576     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1577         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1578         goto done;
1579     }
1580
1581     dp = VGetPartition_r(vcom->vop->partName, 0);
1582     if (dp == NULL) {
1583         res->hdr.reason = FSYNC_BAD_PART;
1584         goto done;
1585     }
1586
1587     osi_Assert(sizeof(FSSYNC_VGQry_response_t) <= res->payload.len);
1588
1589     rc = VVGCache_query_r(dp, vcom->vop->volume, res->payload.buf);
1590     switch (rc) {
1591     case 0:
1592         res->hdr.response_len += sizeof(FSSYNC_VGQry_response_t);
1593         code = SYNC_OK;
1594         break;
1595     case EAGAIN:
1596         res->hdr.reason = FSYNC_PART_SCANNING;
1597         break;
1598     case ENOENT:
1599         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1600         break;
1601     default:
1602         break;
1603     }
1604
1605  done:
1606     return code;
1607 }
1608
1609 static afs_int32
1610 FSYNC_com_VGUpdate(osi_socket fd, SYNC_command * com, SYNC_response * res)
1611 {
1612     afs_int32 code = SYNC_FAILED;
1613     struct DiskPartition64 * dp;
1614     FSSYNC_VGUpdate_command_t * vgucom;
1615     int rc;
1616
1617     if (com->recv_len != (sizeof(com->hdr) + sizeof(*vgucom))) {
1618         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1619         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
1620         code = SYNC_COM_ERROR;
1621         goto done;
1622     }
1623
1624     vgucom = com->payload.buf;
1625
1626     ViceLog(125, ("FSYNC_com_VGUpdate: fd %d got command for parent %lu child "
1627                   "%lu partName %.16s\n", (int)fd,
1628                   afs_printable_uint32_lu(vgucom->parent),
1629                   afs_printable_uint32_lu(vgucom->child),
1630                   vgucom->partName));
1631
1632     if (SYNC_verifyProtocolString(vgucom->partName, sizeof(vgucom->partName))) {
1633         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1634         goto done;
1635     }
1636
1637     dp = VGetPartition_r(vgucom->partName, 0);
1638     if (dp == NULL) {
1639         res->hdr.reason = FSYNC_BAD_PART;
1640         goto done;
1641     }
1642
1643     switch(com->hdr.command) {
1644     case FSYNC_VG_ADD:
1645         rc = VVGCache_entry_add_r(dp, vgucom->parent, vgucom->child, NULL);
1646         break;
1647
1648     case FSYNC_VG_DEL:
1649         rc = VVGCache_entry_del_r(dp, vgucom->parent, vgucom->child);
1650         break;
1651
1652     default:
1653         Log("FSYNC_com_VGUpdate called improperly\n");
1654         rc = -1;
1655         break;
1656     }
1657
1658     /* EINVAL means the partition VGC doesn't exist at all; not really
1659      * an error */
1660     if (rc == 0 || rc == EINVAL) {
1661         code = SYNC_OK;
1662     }
1663
1664     if (rc == ENOENT) {
1665         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1666     } else {
1667         res->hdr.reason = FSYNC_WHATEVER;
1668     }
1669
1670  done:
1671     return code;
1672 }
1673
1674 static afs_int32
1675 FSYNC_com_VGScan(FSSYNC_VolOp_command * vcom, SYNC_response * res)
1676 {
1677     afs_int32 code = SYNC_FAILED;
1678     struct DiskPartition64 * dp;
1679
1680     if (SYNC_verifyProtocolString(vcom->vop->partName, sizeof(vcom->vop->partName))) {
1681         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1682         goto done;
1683     }
1684
1685     dp = VGetPartition_r(vcom->vop->partName, 0);
1686     if (dp == NULL) {
1687         res->hdr.reason = FSYNC_BAD_PART;
1688         goto done;
1689     }
1690
1691     if (VVGCache_scanStart_r(dp) == 0) {
1692         code = SYNC_OK;
1693     }
1694
1695  done:
1696     return code;
1697 }
1698
1699 static afs_int32
1700 FSYNC_com_VGScanAll(FSSYNC_VolOp_command * com, SYNC_response * res)
1701 {
1702     afs_int32 code = SYNC_FAILED;
1703
1704     if (VVGCache_scanStart_r(NULL) == 0) {
1705         code = SYNC_OK;
1706     }
1707
1708     return code;
1709 }
1710 #endif /* AFS_DEMAND_ATTACH_FS */
1711
1712 static afs_int32
1713 FSYNC_com_VnQry(osi_socket fd, SYNC_command * com, SYNC_response * res)
1714 {
1715     afs_int32 code = SYNC_OK;
1716     FSSYNC_VnQry_hdr * qry = com->payload.buf;
1717     Volume * vp;
1718     Vnode * vnp;
1719     Error error;
1720
1721     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_VnQry_hdr))) {
1722         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1723         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
1724         return SYNC_COM_ERROR;
1725     }
1726
1727     ViceLog(125, ("FSYNC_com_VnQry: fd %d got command for vol %lu vnode %lu "
1728                   "uniq %lu spare %lu partName %.16s\n", (int)fd,
1729                   afs_printable_uint32_lu(qry->volume),
1730                   afs_printable_uint32_lu(qry->vnode),
1731                   afs_printable_uint32_lu(qry->unique),
1732                   afs_printable_uint32_lu(qry->spare),
1733                   qry->partName));
1734
1735 #ifdef AFS_DEMAND_ATTACH_FS
1736     vp = VLookupVolume_r(&error, qry->volume, NULL);
1737 #else /* !AFS_DEMAND_ATTACH_FS */
1738     vp = VGetVolume_r(&error, qry->volume);
1739 #endif /* !AFS_DEMAND_ATTACH_FS */
1740
1741     if (!vp) {
1742         res->hdr.reason = FSYNC_UNKNOWN_VOLID;
1743         code = SYNC_FAILED;
1744         goto done;
1745     }
1746
1747     vnp = VLookupVnode(vp, qry->vnode);
1748     if (!vnp) {
1749         res->hdr.reason = FSYNC_UNKNOWN_VNID;
1750         code = SYNC_FAILED;
1751         goto cleanup;
1752     }
1753
1754     if (Vn_class(vnp)->residentSize > res->payload.len) {
1755         res->hdr.reason = SYNC_REASON_ENCODING_ERROR;
1756         code = SYNC_FAILED;
1757         goto cleanup;
1758     }
1759
1760     memcpy(res->payload.buf, vnp, Vn_class(vnp)->residentSize);
1761     res->hdr.response_len += Vn_class(vnp)->residentSize;
1762
1763  cleanup:
1764 #ifndef AFS_DEMAND_ATTACH_FS
1765     VPutVolume_r(vp);
1766 #endif
1767
1768  done:
1769     return code;
1770 }
1771
1772 static afs_int32
1773 FSYNC_com_StatsOp(osi_socket fd, SYNC_command * com, SYNC_response * res)
1774 {
1775     afs_int32 code = SYNC_OK;
1776     FSSYNC_StatsOp_command scom;
1777
1778     if (com->recv_len != (sizeof(com->hdr) + sizeof(FSSYNC_StatsOp_hdr))) {
1779         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1780         res->hdr.flags |= SYNC_FLAG_CHANNEL_SHUTDOWN;
1781         return SYNC_COM_ERROR;
1782     }
1783
1784     scom.hdr = &com->hdr;
1785     scom.sop = (FSSYNC_StatsOp_hdr *) com->payload.buf;
1786     scom.com = com;
1787
1788     ViceLog(125, ("FSYNC_com_StatsOp: fd %d got command for stats: "
1789                   "{vlru_generation = %lu, hash_bucket = %lu, partName = "
1790                   "%.16s}\n", (int)fd,
1791                   afs_printable_uint32_lu(scom.sop->args.vlru_generation),
1792                   afs_printable_uint32_lu(scom.sop->args.hash_bucket),
1793                   scom.sop->args.partName));
1794
1795     switch (com->hdr.command) {
1796     case FSYNC_VOL_STATS_GENERAL:
1797         code = FSYNC_com_StatsOpGeneral(&scom, res);
1798         break;
1799 #ifdef AFS_DEMAND_ATTACH_FS
1800         /* statistics for the following subsystems are only tracked
1801          * for demand attach fileservers */
1802     case FSYNC_VOL_STATS_VICEP:
1803         code = FSYNC_com_StatsOpViceP(&scom, res);
1804         break;
1805     case FSYNC_VOL_STATS_HASH:
1806         code = FSYNC_com_StatsOpHash(&scom, res);
1807         break;
1808     case FSYNC_VOL_STATS_HDR:
1809         code = FSYNC_com_StatsOpHdr(&scom, res);
1810         break;
1811     case FSYNC_VOL_STATS_VLRU:
1812         code = FSYNC_com_StatsOpVLRU(&scom, res);
1813         break;
1814 #endif /* AFS_DEMAND_ATTACH_FS */
1815     default:
1816         code = SYNC_BAD_COMMAND;
1817     }
1818
1819     return code;
1820 }
1821
1822 static afs_int32
1823 FSYNC_com_StatsOpGeneral(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1824 {
1825     afs_int32 code = SYNC_OK;
1826
1827     memcpy(res->payload.buf, &VStats, sizeof(VStats));
1828     res->hdr.response_len += sizeof(VStats);
1829
1830     return code;
1831 }
1832
1833 #ifdef AFS_DEMAND_ATTACH_FS
1834 static afs_int32
1835 FSYNC_com_StatsOpViceP(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1836 {
1837     afs_int32 code = SYNC_OK;
1838     struct DiskPartition64 * dp;
1839     struct DiskPartitionStats64 * stats;
1840
1841     if (SYNC_verifyProtocolString(scom->sop->args.partName, sizeof(scom->sop->args.partName))) {
1842         res->hdr.reason = SYNC_REASON_MALFORMED_PACKET;
1843         code = SYNC_FAILED;
1844         goto done;
1845     }
1846
1847     dp = VGetPartition_r(scom->sop->args.partName, 0);
1848     if (!dp) {
1849         code = SYNC_FAILED;
1850     } else {
1851         stats = (struct DiskPartitionStats64 *) res->payload.buf;
1852         stats->free = dp->free;
1853         stats->totalUsable = dp->totalUsable;
1854         stats->minFree = dp->minFree;
1855         stats->f_files = dp->f_files;
1856         stats->vol_list_len = dp->vol_list.len;
1857
1858         res->hdr.response_len += sizeof(struct DiskPartitionStats64);
1859     }
1860
1861  done:
1862     return code;
1863 }
1864
1865 static afs_int32
1866 FSYNC_com_StatsOpHash(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1867 {
1868     afs_int32 code = SYNC_OK;
1869     struct VolumeHashChainStats * stats;
1870     struct VolumeHashChainHead * head;
1871
1872     if (scom->sop->args.hash_bucket >= VolumeHashTable.Size) {
1873         return SYNC_FAILED;
1874     }
1875
1876     head = &VolumeHashTable.Table[scom->sop->args.hash_bucket];
1877     stats = (struct VolumeHashChainStats *) res->payload.buf;
1878     stats->table_size = VolumeHashTable.Size;
1879     stats->chain_len = head->len;
1880     stats->chain_cacheCheck = head->cacheCheck;
1881     stats->chain_busy = head->busy;
1882     AssignInt64(head->looks, &stats->chain_looks);
1883     AssignInt64(head->gets, &stats->chain_gets);
1884     AssignInt64(head->reorders, &stats->chain_reorders);
1885
1886     res->hdr.response_len += sizeof(struct VolumeHashChainStats);
1887
1888     return code;
1889 }
1890
1891 static afs_int32
1892 FSYNC_com_StatsOpHdr(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1893 {
1894     afs_int32 code = SYNC_OK;
1895
1896     memcpy(res->payload.buf, &volume_hdr_LRU.stats, sizeof(volume_hdr_LRU.stats));
1897     res->hdr.response_len += sizeof(volume_hdr_LRU.stats);
1898
1899     return code;
1900 }
1901
1902 static afs_int32
1903 FSYNC_com_StatsOpVLRU(FSSYNC_StatsOp_command * scom, SYNC_response * res)
1904 {
1905     afs_int32 code = SYNC_OK;
1906
1907     code = SYNC_BAD_COMMAND;
1908
1909     return code;
1910 }
1911 #endif /* AFS_DEMAND_ATTACH_FS */
1912
1913 /**
1914  * populate an FSSYNC_VolOp_info object from a command packet object.
1915  *
1916  * @param[in]   vcom  pointer to command packet
1917  * @param[out]  info  pointer to info object which will be populated
1918  *
1919  * @note FSSYNC_VolOp_info objects are attached to Volume objects when
1920  *       a volume operation is commenced.
1921  *
1922  * @internal
1923  */
1924 static void
1925 FSYNC_com_to_info(FSSYNC_VolOp_command * vcom, FSSYNC_VolOp_info * info)
1926 {
1927     memcpy(&info->com, vcom->hdr, sizeof(SYNC_command_hdr));
1928     memcpy(&info->vop, vcom->vop, sizeof(FSSYNC_VolOp_hdr));
1929     info->vol_op_state = FSSYNC_VolOpPending;
1930 }
1931
1932 /**
1933  * check whether command packet partition name matches volume
1934  * object's partition name.
1935  *
1936  * @param[in] vcom        pointer to command packet
1937  * @param[in] vp          pointer to volume object
1938  * @param[in] match_anon  anon matching control flag (see note below)
1939  *
1940  * @return whether partitions match
1941  *   @retval 0  partitions do NOT match
1942  *   @retval 1  partitions match
1943  *
1944  * @note if match_anon is non-zero, then this function will return a
1945  *       positive match for a zero-length partition string in the
1946  *       command packet.
1947  *
1948  * @internal
1949  */
1950 static int
1951 FSYNC_partMatch(FSSYNC_VolOp_command * vcom, Volume * vp, int match_anon)
1952 {
1953     return ((match_anon && vcom->vop->partName[0] == 0) ||
1954             (strncmp(vcom->vop->partName, V_partition(vp)->name,
1955                      sizeof(vcom->vop->partName)) == 0));
1956 }
1957
1958
1959 static void
1960 FSYNC_Drop(osi_socket fd)
1961 {
1962     struct offlineInfo *p;
1963     int i;
1964     Error error;
1965     char tvolName[VMAXPATHLEN];
1966
1967     VOL_LOCK;
1968     p = OfflineVolumes[FindHandler(fd)];
1969     for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
1970         if (p[i].volumeID) {
1971
1972             Volume *vp;
1973
1974             tvolName[0] = OS_DIRSEPC;
1975             sprintf(&tvolName[1], VFORMAT, afs_printable_uint32_lu(p[i].volumeID));
1976             vp = VAttachVolumeByName_r(&error, p[i].partName, tvolName,
1977                                        V_VOLUPD);
1978             if (vp)
1979                 VPutVolume_r(vp);
1980             p[i].volumeID = 0;
1981         }
1982     }
1983     VOL_UNLOCK;
1984     RemoveHandler(fd);
1985     rk_closesocket(fd);
1986     AcceptOn();
1987 }
1988
1989 static int AcceptHandler = -1;  /* handler id for accept, if turned on */
1990
1991 static void
1992 AcceptOn(void)
1993 {
1994     if (AcceptHandler == -1) {
1995         osi_Assert(AddHandler(fssync_server_state.fd, FSYNC_newconnection));
1996         AcceptHandler = FindHandler(fssync_server_state.fd);
1997     }
1998 }
1999
2000 static void
2001 AcceptOff(void)
2002 {
2003     if (AcceptHandler != -1) {
2004         osi_Assert(RemoveHandler(fssync_server_state.fd));
2005         AcceptHandler = -1;
2006     }
2007 }
2008
2009 /* The multiple FD handling code. */
2010
2011 static osi_socket HandlerFD[MAXHANDLERS];
2012 static void (*HandlerProc[MAXHANDLERS]) (osi_socket);
2013
2014 static void
2015 InitHandler(void)
2016 {
2017     int i;
2018     ObtainWriteLock(&FSYNC_handler_lock);
2019     for (i = 0; i < MAXHANDLERS; i++) {
2020         HandlerFD[i] = OSI_NULLSOCKET;
2021         HandlerProc[i] = 0;
2022     }
2023     ReleaseWriteLock(&FSYNC_handler_lock);
2024 }
2025
2026 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
2027 static void
2028 CallHandler(struct pollfd *fds, int nfds, int mask)
2029 {
2030     int i;
2031     int handler;
2032     ObtainReadLock(&FSYNC_handler_lock);
2033     for (i = 0; i < nfds; i++) {
2034         if (fds[i].revents & mask) {
2035             handler = FindHandler_r(fds[i].fd);
2036             ReleaseReadLock(&FSYNC_handler_lock);
2037             (*HandlerProc[handler]) (fds[i].fd);
2038             ObtainReadLock(&FSYNC_handler_lock);
2039         }
2040     }
2041     ReleaseReadLock(&FSYNC_handler_lock);
2042 }
2043 #else
2044 static void
2045 CallHandler(fd_set * fdsetp)
2046 {
2047     int i;
2048     ObtainReadLock(&FSYNC_handler_lock);
2049     for (i = 0; i < MAXHANDLERS; i++) {
2050         if (HandlerFD[i] >= 0 && FD_ISSET(HandlerFD[i], fdsetp)) {
2051             ReleaseReadLock(&FSYNC_handler_lock);
2052             (*HandlerProc[i]) (HandlerFD[i]);
2053             ObtainReadLock(&FSYNC_handler_lock);
2054         }
2055     }
2056     ReleaseReadLock(&FSYNC_handler_lock);
2057 }
2058 #endif
2059
2060 static int
2061 AddHandler(osi_socket afd, void (*aproc) (osi_socket))
2062 {
2063     int i;
2064     ObtainWriteLock(&FSYNC_handler_lock);
2065     for (i = 0; i < MAXHANDLERS; i++)
2066         if (HandlerFD[i] == OSI_NULLSOCKET)
2067             break;
2068     if (i >= MAXHANDLERS) {
2069         ReleaseWriteLock(&FSYNC_handler_lock);
2070         return 0;
2071     }
2072     HandlerFD[i] = afd;
2073     HandlerProc[i] = aproc;
2074     ReleaseWriteLock(&FSYNC_handler_lock);
2075     return 1;
2076 }
2077
2078 static int
2079 FindHandler(osi_socket afd)
2080 {
2081     int i;
2082     ObtainReadLock(&FSYNC_handler_lock);
2083     for (i = 0; i < MAXHANDLERS; i++)
2084         if (HandlerFD[i] == afd) {
2085             ReleaseReadLock(&FSYNC_handler_lock);
2086             return i;
2087         }
2088     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
2089     osi_Assert(1 == 2);
2090     return -1;                  /* satisfy compiler */
2091 }
2092
2093 static int
2094 FindHandler_r(osi_socket afd)
2095 {
2096     int i;
2097     for (i = 0; i < MAXHANDLERS; i++)
2098         if (HandlerFD[i] == afd) {
2099             return i;
2100         }
2101     osi_Assert(1 == 2);
2102     return -1;                  /* satisfy compiler */
2103 }
2104
2105 static int
2106 RemoveHandler(osi_socket afd)
2107 {
2108     ObtainWriteLock(&FSYNC_handler_lock);
2109     HandlerFD[FindHandler_r(afd)] = OSI_NULLSOCKET;
2110     ReleaseWriteLock(&FSYNC_handler_lock);
2111     return 1;
2112 }
2113
2114 #if defined(HAVE_POLL) && defined(AFS_PTHREAD_ENV)
2115 static void
2116 GetHandler(struct pollfd *fds, int maxfds, int events, int *nfds)
2117 {
2118     int i;
2119     int fdi = 0;
2120     ObtainReadLock(&FSYNC_handler_lock);
2121     for (i = 0; i < MAXHANDLERS; i++)
2122         if (HandlerFD[i] != OSI_NULLSOCKET) {
2123             osi_Assert(fdi<maxfds);
2124             fds[fdi].fd = HandlerFD[i];
2125             fds[fdi].events = events;
2126             fds[fdi].revents = 0;
2127             fdi++;
2128         }
2129     *nfds = fdi;
2130     ReleaseReadLock(&FSYNC_handler_lock);
2131 }
2132 #else
2133 static void
2134 GetHandler(fd_set * fdsetp, int *maxfdp)
2135 {
2136     int i;
2137     int maxfd = -1;
2138     FD_ZERO(fdsetp);
2139     ObtainReadLock(&FSYNC_handler_lock);        /* just in case */
2140     for (i = 0; i < MAXHANDLERS; i++)
2141         if (HandlerFD[i] != OSI_NULLSOCKET) {
2142             FD_SET(HandlerFD[i], fdsetp);
2143 #ifndef AFS_NT40_ENV
2144             /* On Windows the nfds parameter to select() is ignored */
2145             if (maxfd < HandlerFD[i] || maxfd == (int)-1)
2146                 maxfd = HandlerFD[i];
2147 #endif /* AFS_NT40_ENV */
2148         }
2149     *maxfdp = maxfd;
2150     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
2151 }
2152 #endif /* HAVE_POLL && AFS_PTHREAD_ENV */
2153
2154 #endif /* FSSYNC_BUILD_SERVER */