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