Rename printf cast helpers and clean up format string warnings
[openafs.git] / src / vol / volume.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) 2005-2008 Sine Nomine Associates
10  */
11
12 /* 1/1/89: NB:  this stuff is all going to be replaced.  Don't take it too seriously */
13 /*
14
15         System:         VICE-TWO
16         Module:         volume.c
17         Institution:    The Information Technology Center, Carnegie-Mellon University
18
19  */
20
21 #include <afsconfig.h>
22 #include <afs/param.h>
23
24
25 #include <rx/xdr.h>
26 #include <afs/afsint.h>
27 #include <ctype.h>
28 #ifndef AFS_NT40_ENV
29 #include <sys/param.h>
30 #if !defined(AFS_SGI_ENV)
31 #ifdef  AFS_OSF_ENV
32 #include <ufs/fs.h>
33 #else /* AFS_OSF_ENV */
34 #ifdef AFS_VFSINCL_ENV
35 #define VFS
36 #ifdef  AFS_SUN5_ENV
37 #include <sys/fs/ufs_fs.h>
38 #else
39 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
40 #include <ufs/ufs/dinode.h>
41 #include <ufs/ffs/fs.h>
42 #else
43 #include <ufs/fs.h>
44 #endif
45 #endif
46 #else /* AFS_VFSINCL_ENV */
47 #if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
48 #include <sys/fs.h>
49 #endif
50 #endif /* AFS_VFSINCL_ENV */
51 #endif /* AFS_OSF_ENV */
52 #endif /* AFS_SGI_ENV */
53 #endif /* AFS_NT40_ENV */
54 #include <errno.h>
55 #include <sys/stat.h>
56 #include <stdio.h>
57 #ifdef AFS_NT40_ENV
58 #include <fcntl.h>
59 #else
60 #include <sys/file.h>
61 #endif
62 #include <dirent.h>
63 #ifdef  AFS_AIX_ENV
64 #include <sys/vfs.h>
65 #include <fcntl.h>
66 #else
67 #ifdef  AFS_HPUX_ENV
68 #include <fcntl.h>
69 #include <mntent.h>
70 #else
71 #if     defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
72 #ifdef  AFS_SUN5_ENV
73 #include <sys/mnttab.h>
74 #include <sys/mntent.h>
75 #else
76 #include <mntent.h>
77 #endif
78 #else
79 #ifndef AFS_NT40_ENV
80 #if defined(AFS_SGI_ENV)
81 #include <fcntl.h>
82 #include <mntent.h>
83
84 #else
85 #ifndef AFS_LINUX20_ENV
86 #include <fstab.h>              /* Need to find in libc 5, present in libc 6 */
87 #endif
88 #endif
89 #endif /* AFS_SGI_ENV */
90 #endif
91 #endif /* AFS_HPUX_ENV */
92 #endif
93 #ifndef AFS_NT40_ENV
94 #include <netdb.h>
95 #include <netinet/in.h>
96 #include <sys/wait.h>
97 #include <setjmp.h>
98 #ifndef ITIMER_REAL
99 #include <sys/time.h>
100 #endif /* ITIMER_REAL */
101 #endif /* AFS_NT40_ENV */
102 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
103 #include <string.h>
104 #else
105 #include <strings.h>
106 #endif
107
108 #include "nfs.h"
109 #include <afs/errors.h>
110 #include "lock.h"
111 #include "lwp.h"
112 #include <afs/afssyscalls.h>
113 #include "ihandle.h"
114 #include <afs/afsutil.h>
115 #ifdef AFS_NT40_ENV
116 #include <io.h>
117 #endif
118 #include "daemon_com.h"
119 #include "fssync.h"
120 #include "salvsync.h"
121 #include "vnode.h"
122 #include "volume.h"
123 #include "partition.h"
124 #include "volume_inline.h"
125 #ifdef AFS_PTHREAD_ENV
126 #include <assert.h>
127 #else /* AFS_PTHREAD_ENV */
128 #include "afs/assert.h"
129 #endif /* AFS_PTHREAD_ENV */
130 #include "vutils.h"
131 #ifndef AFS_NT40_ENV
132 #include <dir/dir.h>
133 #include <unistd.h>
134 #endif
135
136 #if !defined(offsetof)
137 #include <stddef.h>
138 #endif
139
140 #ifdef O_LARGEFILE
141 #define afs_stat        stat64
142 #define afs_fstat       fstat64
143 #define afs_open        open64
144 #else /* !O_LARGEFILE */
145 #define afs_stat        stat
146 #define afs_fstat       fstat
147 #define afs_open        open
148 #endif /* !O_LARGEFILE */
149
150 #ifdef AFS_PTHREAD_ENV
151 pthread_mutex_t vol_glock_mutex;
152 pthread_mutex_t vol_trans_mutex;
153 pthread_cond_t vol_put_volume_cond;
154 pthread_cond_t vol_sleep_cond;
155 int vol_attach_threads = 1;
156 #endif /* AFS_PTHREAD_ENV */
157
158 #ifdef AFS_DEMAND_ATTACH_FS
159 pthread_mutex_t vol_salvsync_mutex;
160 #endif /* AFS_DEMAND_ATTACH_FS */
161
162 #ifdef  AFS_OSF_ENV
163 extern void *calloc(), *realloc();
164 #endif
165
166 /*@printflike@*/ extern void Log(const char *format, ...);
167
168 /* Forward declarations */
169 static Volume *attach2(Error * ec, VolId vid, char *path,
170                        register struct VolumeHeader *header,
171                        struct DiskPartition64 *partp, Volume * vp, 
172                        int isbusy, int mode);
173 static void ReallyFreeVolume(Volume * vp);
174 #ifdef AFS_DEMAND_ATTACH_FS
175 static void FreeVolume(Volume * vp);
176 #else /* !AFS_DEMAND_ATTACH_FS */
177 #define FreeVolume(vp) ReallyFreeVolume(vp)
178 static void VScanUpdateList(void);
179 #endif /* !AFS_DEMAND_ATTACH_FS */
180 static void VInitVolumeHeaderCache(afs_uint32 howMany);
181 static int GetVolumeHeader(register Volume * vp);
182 static void ReleaseVolumeHeader(register struct volHeader *hd);
183 static void FreeVolumeHeader(register Volume * vp);
184 static void AddVolumeToHashTable(register Volume * vp, int hashid);
185 static void DeleteVolumeFromHashTable(register Volume * vp);
186 #if 0
187 static int VHold(Volume * vp);
188 #endif
189 static int VHold_r(Volume * vp);
190 static void VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class);
191 static void VReleaseVolumeHandles_r(Volume * vp);
192 static void VCloseVolumeHandles_r(Volume * vp);
193 static void LoadVolumeHeader(Error * ec, Volume * vp);
194 static int VCheckOffline(register Volume * vp);
195 static int VCheckDetach(register Volume * vp);
196 static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags);
197 #ifdef AFS_DEMAND_ATTACH_FS
198 static int VolumeExternalName_r(VolumeId volumeId, char * name, size_t len);
199 #endif
200
201 int LogLevel;                   /* Vice loglevel--not defined as extern so that it will be
202                                  * defined when not linked with vice, XXXX */
203 ProgramType programType;        /* The type of program using the package */
204
205 /* extended volume package statistics */
206 VolPkgStats VStats;
207
208 #ifdef VOL_LOCK_DEBUG
209 pthread_t vol_glock_holder = 0;
210 #endif
211
212
213 #define VOLUME_BITMAP_GROWSIZE  16      /* bytes, => 128vnodes */
214                                         /* Must be a multiple of 4 (1 word) !! */
215
216 /* this parameter needs to be tunable at runtime.
217  * 128 was really inadequate for largish servers -- at 16384 volumes this
218  * puts average chain length at 128, thus an average 65 deref's to find a volptr.
219  * talk about bad spatial locality...
220  *
221  * an AVL or splay tree might work a lot better, but we'll just increase
222  * the default hash table size for now
223  */
224 #define DEFAULT_VOLUME_HASH_SIZE 256   /* Must be a power of 2!! */
225 #define DEFAULT_VOLUME_HASH_MASK (DEFAULT_VOLUME_HASH_SIZE-1)
226 #define VOLUME_HASH(volumeId) (volumeId&(VolumeHashTable.Mask))
227
228 /*
229  * turn volume hash chains into partially ordered lists.
230  * when the threshold is exceeded between two adjacent elements,
231  * perform a chain rebalancing operation.
232  *
233  * keep the threshold high in order to keep cache line invalidates
234  * low "enough" on SMPs
235  */
236 #define VOLUME_HASH_REORDER_THRESHOLD 200
237
238 /*
239  * when possible, don't just reorder single elements, but reorder
240  * entire chains of elements at once.  a chain of elements that
241  * exceed the element previous to the pivot by at least CHAIN_THRESH 
242  * accesses are moved in front of the chain whose elements have at
243  * least CHAIN_THRESH less accesses than the pivot element
244  */
245 #define VOLUME_HASH_REORDER_CHAIN_THRESH (VOLUME_HASH_REORDER_THRESHOLD / 2)
246
247 #include "rx/rx_queue.h"
248
249
250 VolumeHashTable_t VolumeHashTable = {
251     DEFAULT_VOLUME_HASH_SIZE,
252     DEFAULT_VOLUME_HASH_MASK,
253     NULL
254 };
255
256
257 static void VInitVolumeHash(void);
258
259
260 #ifndef AFS_HAVE_FFS
261 /* This macro is used where an ffs() call does not exist. Was in util/ffs.c */
262 ffs(x)
263 {
264     afs_int32 ffs_i;
265     afs_int32 ffs_tmp = x;
266     if (ffs_tmp == 0)
267         return (-1);
268     else
269         for (ffs_i = 1;; ffs_i++) {
270             if (ffs_tmp & 1)
271                 return (ffs_i);
272             else
273                 ffs_tmp >>= 1;
274         }
275 }
276 #endif /* !AFS_HAVE_FFS */
277
278 #ifdef AFS_PTHREAD_ENV
279 typedef struct diskpartition_queue_t {
280     struct rx_queue queue;
281     struct DiskPartition64 * diskP;
282 } diskpartition_queue_t;
283 typedef struct vinitvolumepackage_thread_t {
284     struct rx_queue queue;
285     pthread_cond_t thread_done_cv;
286     int n_threads_complete;
287 } vinitvolumepackage_thread_t;
288 static void * VInitVolumePackageThread(void * args);
289 #endif /* AFS_PTHREAD_ENV */
290
291 static int VAttachVolumesByPartition(struct DiskPartition64 *diskP, 
292                                      int * nAttached, int * nUnattached);
293
294
295 #ifdef AFS_DEMAND_ATTACH_FS
296 /* demand attach fileserver extensions */
297
298 /* XXX
299  * in the future we will support serialization of VLRU state into the fs_state
300  * disk dumps
301  *
302  * these structures are the beginning of that effort
303  */
304 struct VLRU_DiskHeader {
305     struct versionStamp stamp;            /* magic and structure version number */
306     afs_uint32 mtime;                     /* time of dump to disk */
307     afs_uint32 num_records;               /* number of VLRU_DiskEntry records */
308 };
309
310 struct VLRU_DiskEntry {
311     afs_uint32 vid;                       /* volume ID */
312     afs_uint32 idx;                       /* generation */
313     afs_uint32 last_get;                  /* timestamp of last get */
314 };
315
316 struct VLRU_StartupQueue {
317     struct VLRU_DiskEntry * entry;
318     int num_entries;
319     int next_idx;
320 };
321
322 typedef struct vshutdown_thread_t {
323     struct rx_queue q;
324     pthread_mutex_t lock;
325     pthread_cond_t cv;
326     pthread_cond_t master_cv;
327     int n_threads;
328     int n_threads_complete;
329     int vol_remaining;
330     int schedule_version;
331     int pass;
332     byte n_parts;
333     byte n_parts_done_pass;
334     byte part_thread_target[VOLMAXPARTS+1];
335     byte part_done_pass[VOLMAXPARTS+1];
336     struct rx_queue * part_pass_head[VOLMAXPARTS+1];
337     int stats[4][VOLMAXPARTS+1];
338 } vshutdown_thread_t;
339 static void * VShutdownThread(void * args);
340
341
342 static Volume * VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode);
343 static int VCheckFree(Volume * vp);
344
345 /* VByP List */
346 static void AddVolumeToVByPList_r(Volume * vp);
347 static void DeleteVolumeFromVByPList_r(Volume * vp);
348 static void VVByPListBeginExclusive_r(struct DiskPartition64 * dp);
349 static void VVByPListEndExclusive_r(struct DiskPartition64 * dp);
350 static void VVByPListWait_r(struct DiskPartition64 * dp);
351
352 /* online salvager */
353 static int VCheckSalvage(register Volume * vp);
354 static int VUpdateSalvagePriority_r(Volume * vp);
355 static int VScheduleSalvage_r(Volume * vp);
356 static int VCancelSalvage_r(Volume * vp, int reason);
357
358 /* Volume hash table */
359 static void VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp);
360 static void VHashBeginExclusive_r(VolumeHashChainHead * head);
361 static void VHashEndExclusive_r(VolumeHashChainHead * head);
362 static void VHashWait_r(VolumeHashChainHead * head);
363
364 /* shutdown */
365 static int ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass);
366 static int ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
367                                 struct rx_queue ** idx);
368 static void ShutdownController(vshutdown_thread_t * params);
369 static void ShutdownCreateSchedule(vshutdown_thread_t * params);
370
371 /* VLRU */
372 static void VLRU_ComputeConstants(void);
373 static void VInitVLRU(void);
374 static void VLRU_Init_Node_r(volatile Volume * vp);
375 static void VLRU_Add_r(volatile Volume * vp);
376 static void VLRU_Delete_r(volatile Volume * vp);
377 static void VLRU_UpdateAccess_r(volatile Volume * vp);
378 static void * VLRU_ScannerThread(void * args);
379 static void VLRU_Scan_r(int idx);
380 static void VLRU_Promote_r(int idx);
381 static void VLRU_Demote_r(int idx);
382 static void VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append);
383
384 /* soft detach */
385 static int VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh);
386 static int VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh);
387 static int VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh);
388
389
390 pthread_key_t VThread_key;
391 VThreadOptions_t VThread_defaults = {
392     0                           /**< allow salvsync */
393 };
394 #endif /* AFS_DEMAND_ATTACH_FS */
395
396
397 struct Lock vol_listLock;       /* Lock obtained when listing volumes:  
398                                  * prevents a volume from being missed 
399                                  * if the volume is attached during a 
400                                  * list volumes */
401
402
403 static int TimeZoneCorrection;  /* Number of seconds west of GMT */
404
405 /* Common message used when the volume goes off line */
406 char *VSalvageMessage =
407     "Files in this volume are currently unavailable; call operations";
408
409 int VInit;                      /* 0 - uninitialized,
410                                  * 1 - initialized but not all volumes have been attached,
411                                  * 2 - initialized and all volumes have been attached,
412                                  * 3 - initialized, all volumes have been attached, and
413                                  * VConnectFS() has completed. */
414
415
416 bit32 VolumeCacheCheck;         /* Incremented everytime a volume goes on line--
417                                  * used to stamp volume headers and in-core
418                                  * vnodes.  When the volume goes on-line the
419                                  * vnode will be invalidated
420                                  * access only with VOL_LOCK held */
421
422
423
424
425 /***************************************************/
426 /* Startup routines                                */
427 /***************************************************/
428
429 int
430 VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVnodes,
431                    int connect, afs_uint32 volcache)
432 {
433     int errors = 0;             /* Number of errors while finding vice partitions. */
434     struct timeval tv;
435     struct timezone tz;
436
437     programType = pt;
438
439     memset(&VStats, 0, sizeof(VStats));
440     VStats.hdr_cache_size = 200;
441
442     VInitPartitionPackage();
443     VInitVolumeHash();
444 #ifdef AFS_DEMAND_ATTACH_FS
445     if (programType == fileServer) {
446         VInitVLRU();
447     } else {
448         VLRU_SetOptions(VLRU_SET_ENABLED, 0);
449     }
450     assert(pthread_key_create(&VThread_key, NULL) == 0);
451 #endif
452
453 #ifdef AFS_PTHREAD_ENV
454     assert(pthread_mutex_init(&vol_glock_mutex, NULL) == 0);
455     assert(pthread_mutex_init(&vol_trans_mutex, NULL) == 0);
456     assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
457     assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
458 #else /* AFS_PTHREAD_ENV */
459     IOMGR_Initialize();
460 #endif /* AFS_PTHREAD_ENV */
461     Lock_Init(&vol_listLock);
462
463     srandom(time(0));           /* For VGetVolumeInfo */
464     gettimeofday(&tv, &tz);
465     TimeZoneCorrection = tz.tz_minuteswest * 60;
466
467 #ifdef AFS_DEMAND_ATTACH_FS
468     assert(pthread_mutex_init(&vol_salvsync_mutex, NULL) == 0);
469 #endif /* AFS_DEMAND_ATTACH_FS */
470
471     /* Ok, we have done enough initialization that fileserver can 
472      * start accepting calls, even though the volumes may not be 
473      * available just yet.
474      */
475     VInit = 1;
476
477 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_SERVER)
478     if (programType == salvageServer) {
479         SALVSYNC_salvInit();
480     }
481 #endif /* AFS_DEMAND_ATTACH_FS */
482 #ifdef FSSYNC_BUILD_SERVER
483     if (programType == fileServer) {
484         FSYNC_fsInit();
485     }
486 #endif
487 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
488     if (programType == fileServer) {
489         /* establish a connection to the salvager at this point */
490         assert(VConnectSALV() != 0);
491     }
492 #endif /* AFS_DEMAND_ATTACH_FS */
493
494     if (volcache > VStats.hdr_cache_size)
495         VStats.hdr_cache_size = volcache;
496     VInitVolumeHeaderCache(VStats.hdr_cache_size);
497
498     VInitVnodes(vLarge, nLargeVnodes);
499     VInitVnodes(vSmall, nSmallVnodes);
500
501
502     errors = VAttachPartitions();
503     if (errors)
504         return -1;
505
506     if (programType == fileServer) {
507         struct DiskPartition64 *diskP;
508 #ifdef AFS_PTHREAD_ENV
509         struct vinitvolumepackage_thread_t params;
510         struct diskpartition_queue_t * dpq;
511         int i, threads, parts;
512         pthread_t tid;
513         pthread_attr_t attrs;
514
515         assert(pthread_cond_init(&params.thread_done_cv,NULL) == 0);
516         queue_Init(&params);
517         params.n_threads_complete = 0;
518
519         /* create partition work queue */
520         for (parts=0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
521             dpq = (diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
522             assert(dpq != NULL);
523             dpq->diskP = diskP;
524             queue_Append(&params,dpq);
525         }
526
527         threads = MIN(parts, vol_attach_threads);
528
529         if (threads > 1) {
530             /* spawn off a bunch of initialization threads */
531             assert(pthread_attr_init(&attrs) == 0);
532             assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
533
534             Log("VInitVolumePackage: beginning parallel fileserver startup\n");
535 #ifdef AFS_DEMAND_ATTACH_FS
536             Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
537                 threads, parts);
538 #else /* AFS_DEMAND_ATTACH_FS */
539             Log("VInitVolumePackage: using %d threads to attach volumes on %d partitions\n",
540                 threads, parts);
541 #endif /* AFS_DEMAND_ATTACH_FS */
542
543             VOL_LOCK;
544             for (i=0; i < threads; i++) {
545                 assert(pthread_create
546                        (&tid, &attrs, &VInitVolumePackageThread,
547                         &params) == 0);
548             }
549
550             while(params.n_threads_complete < threads) {
551                 VOL_CV_WAIT(&params.thread_done_cv);
552             }
553             VOL_UNLOCK;
554
555             assert(pthread_attr_destroy(&attrs) == 0);
556         } else {
557             /* if we're only going to run one init thread, don't bother creating
558              * another LWP */
559             Log("VInitVolumePackage: beginning single-threaded fileserver startup\n");
560 #ifdef AFS_DEMAND_ATTACH_FS
561             Log("VInitVolumePackage: using 1 thread to pre-attach volumes on %d partition(s)\n",
562                 parts);
563 #else /* AFS_DEMAND_ATTACH_FS */
564             Log("VInitVolumePackage: using 1 thread to attach volumes on %d partition(s)\n",
565                 parts);
566 #endif /* AFS_DEMAND_ATTACH_FS */
567
568             VInitVolumePackageThread(&params);
569         }
570
571         assert(pthread_cond_destroy(&params.thread_done_cv) == 0);
572
573 #else /* AFS_PTHREAD_ENV */
574
575         /* Attach all the volumes in this partition */
576         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
577             int nAttached = 0, nUnattached = 0;
578             assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
579         }
580 #endif /* AFS_PTHREAD_ENV */
581     }
582
583     VInit = 2;                  /* Initialized, and all volumes have been attached */
584 #ifdef FSSYNC_BUILD_CLIENT
585     if (programType == volumeUtility && connect) {
586         if (!VConnectFS()) {
587             Log("Unable to connect to file server; will retry at need\n");
588             /*exit(1);*/
589         }
590     }
591 #ifdef AFS_DEMAND_ATTACH_FS
592     else if (programType == salvageServer) {
593         if (!VConnectFS()) {
594             Log("Unable to connect to file server; aborted\n");
595             exit(1);
596         }
597     }
598 #endif /* AFS_DEMAND_ATTACH_FS */
599 #endif /* FSSYNC_BUILD_CLIENT */
600     return 0;
601 }
602
603 #ifdef AFS_PTHREAD_ENV
604 static void *
605 VInitVolumePackageThread(void * args) {
606     int errors = 0;             /* Number of errors while finding vice partitions. */
607
608     DIR *dirp;
609     struct dirent *dp;
610     struct DiskPartition64 *diskP;
611     struct vinitvolumepackage_thread_t * params;
612     struct diskpartition_queue_t * dpq;
613
614     params = (vinitvolumepackage_thread_t *) args;
615
616
617     VOL_LOCK;
618     /* Attach all the volumes in this partition */
619     while (queue_IsNotEmpty(params)) {
620         int nAttached = 0, nUnattached = 0;
621
622         dpq = queue_First(params,diskpartition_queue_t);
623         queue_Remove(dpq);
624         VOL_UNLOCK;
625         diskP = dpq->diskP;
626         free(dpq);
627
628         assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
629
630         VOL_LOCK;
631     }
632
633     params->n_threads_complete++;
634     pthread_cond_signal(&params->thread_done_cv);
635     VOL_UNLOCK;
636     return NULL;
637 }
638 #endif /* AFS_PTHREAD_ENV */
639
640 /*
641  * attach all volumes on a given disk partition
642  */
643 static int
644 VAttachVolumesByPartition(struct DiskPartition64 *diskP, int * nAttached, int * nUnattached)
645 {
646   DIR * dirp;
647   struct dirent * dp;
648   int ret = 0;
649
650   Log("Partition %s: attaching volumes\n", diskP->name);
651   dirp = opendir(VPartitionPath(diskP));
652   if (!dirp) {
653     Log("opendir on Partition %s failed!\n", diskP->name);
654     return 1;
655   }
656
657   while ((dp = readdir(dirp))) {
658     char *p;
659     p = strrchr(dp->d_name, '.');
660     if (p != NULL && strcmp(p, VHDREXT) == 0) {
661       Error error;
662       Volume *vp;
663 #ifdef AFS_DEMAND_ATTACH_FS
664       vp = VPreAttachVolumeByName(&error, diskP->name, dp->d_name);
665 #else /* AFS_DEMAND_ATTACH_FS */
666       vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
667                                V_VOLUPD);
668 #endif /* AFS_DEMAND_ATTACH_FS */
669       (*(vp ? nAttached : nUnattached))++;
670       if (error == VOFFLINE)
671         Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
672       else if (LogLevel >= 5) {
673         Log("Partition %s: attached volume %d (%s)\n",
674             diskP->name, VolumeNumber(dp->d_name),
675             dp->d_name);
676       }
677 #if !defined(AFS_DEMAND_ATTACH_FS)
678       if (vp) {
679         VPutVolume(vp);
680       }
681 #endif /* AFS_DEMAND_ATTACH_FS */
682     }
683   }
684
685   Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, *nAttached, *nUnattached);
686   closedir(dirp);
687   return ret;
688 }
689
690
691 /***************************************************/
692 /* Shutdown routines                               */
693 /***************************************************/
694
695 /*
696  * demand attach fs
697  * highly multithreaded volume package shutdown
698  *
699  * with the demand attach fileserver extensions,
700  * VShutdown has been modified to be multithreaded.
701  * In order to achieve optimal use of many threads,
702  * the shutdown code involves one control thread and
703  * n shutdown worker threads.  The control thread
704  * periodically examines the number of volumes available
705  * for shutdown on each partition, and produces a worker
706  * thread allocation schedule.  The idea is to eliminate
707  * redundant scheduling computation on the workers by
708  * having a single master scheduler.
709  *
710  * The scheduler's objectives are:
711  * (1) fairness
712  *   each partition with volumes remaining gets allocated
713  *   at least 1 thread (assuming sufficient threads)
714  * (2) performance
715  *   threads are allocated proportional to the number of
716  *   volumes remaining to be offlined.  This ensures that
717  *   the OS I/O scheduler has many requests to elevator
718  *   seek on partitions that will (presumably) take the
719  *   longest amount of time (from now) to finish shutdown
720  * (3) keep threads busy
721  *   when there are extra threads, they are assigned to
722  *   partitions using a simple round-robin algorithm
723  *
724  * In the future, we may wish to add the ability to adapt
725  * to the relative performance patterns of each disk
726  * partition.
727  *
728  *
729  * demand attach fs
730  * multi-step shutdown process
731  *
732  * demand attach shutdown is a four-step process. Each
733  * shutdown "pass" shuts down increasingly more difficult
734  * volumes.  The main purpose is to achieve better cache
735  * utilization during shutdown.
736  *
737  * pass 0
738  *   shutdown volumes in the unattached, pre-attached
739  *   and error states
740  * pass 1
741  *   shutdown attached volumes with cached volume headers
742  * pass 2
743  *   shutdown all volumes in non-exclusive states
744  * pass 3
745  *   shutdown all remaining volumes
746  */
747
748 void
749 VShutdown_r(void)
750 {
751     int i;
752     register Volume *vp, *np;
753     register afs_int32 code;
754 #ifdef AFS_DEMAND_ATTACH_FS
755     struct DiskPartition64 * diskP;
756     struct diskpartition_queue_t * dpq;
757     vshutdown_thread_t params;
758     pthread_t tid;
759     pthread_attr_t attrs;
760
761     memset(&params, 0, sizeof(vshutdown_thread_t));
762
763     for (params.n_parts=0, diskP = DiskPartitionList;
764          diskP; diskP = diskP->next, params.n_parts++);
765
766     Log("VShutdown:  shutting down on-line volumes on %d partition%s...\n", 
767         params.n_parts, params.n_parts > 1 ? "s" : "");
768
769     if (vol_attach_threads > 1) {
770         /* prepare for parallel shutdown */
771         params.n_threads = vol_attach_threads;
772         assert(pthread_mutex_init(&params.lock, NULL) == 0);
773         assert(pthread_cond_init(&params.cv, NULL) == 0);
774         assert(pthread_cond_init(&params.master_cv, NULL) == 0);
775         assert(pthread_attr_init(&attrs) == 0);
776         assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
777         queue_Init(&params);
778
779         /* setup the basic partition information structures for
780          * parallel shutdown */
781         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
782             /* XXX debug */
783             struct rx_queue * qp, * nqp;
784             Volume * vp;
785             int count = 0;
786
787             VVByPListWait_r(diskP);
788             VVByPListBeginExclusive_r(diskP);
789
790             /* XXX debug */
791             for (queue_Scan(&diskP->vol_list, qp, nqp, rx_queue)) {
792                 vp = (Volume *)((char *)qp - offsetof(Volume, vol_list));
793                 if (vp->header)
794                     count++;
795             }
796             Log("VShutdown: partition %s has %d volumes with attached headers\n",
797                 VPartitionPath(diskP), count);
798                 
799
800             /* build up the pass 0 shutdown work queue */
801             dpq = (struct diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
802             assert(dpq != NULL);
803             dpq->diskP = diskP;
804             queue_Prepend(&params, dpq);
805
806             params.part_pass_head[diskP->index] = queue_First(&diskP->vol_list, rx_queue);
807         }
808
809         Log("VShutdown:  beginning parallel fileserver shutdown\n");
810         Log("VShutdown:  using %d threads to offline volumes on %d partition%s\n",
811             vol_attach_threads, params.n_parts, params.n_parts > 1 ? "s" : "" );
812
813         /* do pass 0 shutdown */
814         assert(pthread_mutex_lock(&params.lock) == 0);
815         for (i=0; i < params.n_threads; i++) {
816             assert(pthread_create
817                    (&tid, &attrs, &VShutdownThread,
818                     &params) == 0);
819         }
820         
821         /* wait for all the pass 0 shutdowns to complete */
822         while (params.n_threads_complete < params.n_threads) {
823             assert(pthread_cond_wait(&params.master_cv, &params.lock) == 0);
824         }
825         params.n_threads_complete = 0;
826         params.pass = 1;
827         assert(pthread_cond_broadcast(&params.cv) == 0);
828         assert(pthread_mutex_unlock(&params.lock) == 0);
829
830         Log("VShutdown:  pass 0 completed using the 1 thread per partition algorithm\n");
831         Log("VShutdown:  starting passes 1 through 3 using finely-granular mp-fast algorithm\n");
832
833         /* run the parallel shutdown scheduler. it will drop the glock internally */
834         ShutdownController(&params);
835         
836         /* wait for all the workers to finish pass 3 and terminate */
837         while (params.pass < 4) {
838             VOL_CV_WAIT(&params.cv);
839         }
840         
841         assert(pthread_attr_destroy(&attrs) == 0);
842         assert(pthread_cond_destroy(&params.cv) == 0);
843         assert(pthread_cond_destroy(&params.master_cv) == 0);
844         assert(pthread_mutex_destroy(&params.lock) == 0);
845
846         /* drop the VByPList exclusive reservations */
847         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
848             VVByPListEndExclusive_r(diskP);
849             Log("VShutdown:  %s stats : (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
850                 VPartitionPath(diskP),
851                 params.stats[0][diskP->index],
852                 params.stats[1][diskP->index],
853                 params.stats[2][diskP->index],
854                 params.stats[3][diskP->index]);
855         }
856
857         Log("VShutdown:  shutdown finished using %d threads\n", params.n_threads);
858     } else {
859         /* if we're only going to run one shutdown thread, don't bother creating
860          * another LWP */
861         Log("VShutdown:  beginning single-threaded fileserver shutdown\n");
862
863         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
864             VShutdownByPartition_r(diskP);
865         }
866     }
867
868     Log("VShutdown:  complete.\n");
869 #else /* AFS_DEMAND_ATTACH_FS */
870     Log("VShutdown:  shutting down on-line volumes...\n");
871     for (i = 0; i < VolumeHashTable.Size; i++) {
872         /* try to hold first volume in the hash table */
873         for (queue_Scan(&VolumeHashTable.Table[i],vp,np,Volume)) {
874             code = VHold_r(vp);
875             if (code == 0) {
876                 if (LogLevel >= 5)
877                     Log("VShutdown:  Attempting to take volume %u offline.\n",
878                         vp->hashid);
879                 
880                 /* next, take the volume offline (drops reference count) */
881                 VOffline_r(vp, "File server was shut down");
882             }
883         }
884     }
885     Log("VShutdown:  complete.\n");
886 #endif /* AFS_DEMAND_ATTACH_FS */
887 }
888
889 void
890 VShutdown(void)
891 {
892     VOL_LOCK;
893     VShutdown_r();
894     VOL_UNLOCK;
895 }
896
897 #ifdef AFS_DEMAND_ATTACH_FS
898 /*
899  * demand attach fs
900  * shutdown control thread
901  */
902 static void
903 ShutdownController(vshutdown_thread_t * params)
904 {
905     /* XXX debug */
906     struct DiskPartition64 * diskP;
907     Device id;
908     vshutdown_thread_t shadow;
909
910     ShutdownCreateSchedule(params);
911
912     while ((params->pass < 4) &&
913            (params->n_threads_complete < params->n_threads)) {
914         /* recompute schedule once per second */
915
916         memcpy(&shadow, params, sizeof(vshutdown_thread_t));
917
918         VOL_UNLOCK;
919         /* XXX debug */
920         Log("ShutdownController:  schedule version=%d, vol_remaining=%d, pass=%d\n",
921             shadow.schedule_version, shadow.vol_remaining, shadow.pass);
922         Log("ShutdownController:  n_threads_complete=%d, n_parts_done_pass=%d\n",
923             shadow.n_threads_complete, shadow.n_parts_done_pass);
924         for (diskP = DiskPartitionList; diskP; diskP=diskP->next) {
925             id = diskP->index;
926             Log("ShutdownController:  part[%d] : (len=%d, thread_target=%d, done_pass=%d, pass_head=%p)\n",
927                 id, 
928                 diskP->vol_list.len,
929                 shadow.part_thread_target[id], 
930                 shadow.part_done_pass[id], 
931                 shadow.part_pass_head[id]);
932         }
933
934         sleep(1);
935         VOL_LOCK;
936
937         ShutdownCreateSchedule(params);
938     }
939 }
940
941 /* create the shutdown thread work schedule.
942  * this scheduler tries to implement fairness
943  * by allocating at least 1 thread to each 
944  * partition with volumes to be shutdown,
945  * and then it attempts to allocate remaining
946  * threads based upon the amount of work left
947  */
948 static void
949 ShutdownCreateSchedule(vshutdown_thread_t * params)
950 {
951     struct DiskPartition64 * diskP;
952     int sum, thr_workload, thr_left;
953     int part_residue[VOLMAXPARTS+1];
954     Device id;
955
956     /* compute the total number of outstanding volumes */
957     sum = 0;
958     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
959         sum += diskP->vol_list.len;
960     }
961     
962     params->schedule_version++;
963     params->vol_remaining = sum;
964
965     if (!sum)
966         return;
967
968     /* compute average per-thread workload */
969     thr_workload = sum / params->n_threads;
970     if (sum % params->n_threads)
971         thr_workload++;
972
973     thr_left = params->n_threads;
974     memset(&part_residue, 0, sizeof(part_residue));
975
976     /* for fairness, give every partition with volumes remaining
977      * at least one thread */
978     for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
979         id = diskP->index;
980         if (diskP->vol_list.len) {
981             params->part_thread_target[id] = 1;
982             thr_left--;
983         } else {
984             params->part_thread_target[id] = 0;
985         }
986     }
987
988     if (thr_left && thr_workload) {
989         /* compute length-weighted workloads */
990         int delta;
991
992         for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
993             id = diskP->index;
994             delta = (diskP->vol_list.len / thr_workload) -
995                 params->part_thread_target[id];
996             if (delta < 0) {
997                 continue;
998             }
999             if (delta < thr_left) {
1000                 params->part_thread_target[id] += delta;
1001                 thr_left -= delta;
1002             } else {
1003                 params->part_thread_target[id] += thr_left;
1004                 thr_left = 0;
1005                 break;
1006             }
1007         }
1008     }
1009
1010     if (thr_left) {
1011         /* try to assign any leftover threads to partitions that
1012          * had volume lengths closer to needing thread_target+1 */
1013         int max_residue, max_id;
1014
1015         /* compute the residues */
1016         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1017             id = diskP->index;
1018             part_residue[id] = diskP->vol_list.len - 
1019                 (params->part_thread_target[id] * thr_workload);
1020         }
1021
1022         /* now try to allocate remaining threads to partitions with the
1023          * highest residues */
1024         while (thr_left) {
1025             max_residue = 0;
1026             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1027                 id = diskP->index;
1028                 if (part_residue[id] > max_residue) {
1029                     max_residue = part_residue[id];
1030                     max_id = id;
1031                 }
1032             }
1033
1034             if (!max_residue) {
1035                 break;
1036             }
1037
1038             params->part_thread_target[max_id]++;
1039             thr_left--;
1040             part_residue[max_id] = 0;
1041         }
1042     }
1043
1044     if (thr_left) {
1045         /* punt and give any remaining threads equally to each partition */
1046         int alloc;
1047         if (thr_left >= params->n_parts) {
1048             alloc = thr_left / params->n_parts;
1049             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1050                 id = diskP->index;
1051                 params->part_thread_target[id] += alloc;
1052                 thr_left -= alloc;
1053             }
1054         }
1055
1056         /* finish off the last of the threads */
1057         for (diskP = DiskPartitionList; thr_left && diskP; diskP = diskP->next) {
1058             id = diskP->index;
1059             params->part_thread_target[id]++;
1060             thr_left--;
1061         }
1062     }
1063 }
1064
1065 /* worker thread for parallel shutdown */
1066 static void *
1067 VShutdownThread(void * args)
1068 {
1069     struct rx_queue *qp;
1070     Volume * vp;
1071     vshutdown_thread_t * params;
1072     int part, code, found, pass, schedule_version_save, count;
1073     struct DiskPartition64 *diskP;
1074     struct diskpartition_queue_t * dpq;
1075     Device id;
1076
1077     params = (vshutdown_thread_t *) args;
1078
1079     /* acquire the shutdown pass 0 lock */
1080     assert(pthread_mutex_lock(&params->lock) == 0);
1081
1082     /* if there's still pass 0 work to be done,
1083      * get a work entry, and do a pass 0 shutdown */
1084     if (queue_IsNotEmpty(params)) {
1085         dpq = queue_First(params, diskpartition_queue_t);
1086         queue_Remove(dpq);
1087         assert(pthread_mutex_unlock(&params->lock) == 0);
1088         diskP = dpq->diskP;
1089         free(dpq);
1090         id = diskP->index;
1091
1092         count = 0;
1093         while (ShutdownVolumeWalk_r(diskP, 0, &params->part_pass_head[id]))
1094             count++;
1095         params->stats[0][diskP->index] = count;
1096         assert(pthread_mutex_lock(&params->lock) == 0);
1097     }
1098
1099     params->n_threads_complete++;
1100     if (params->n_threads_complete == params->n_threads) {
1101       /* notify control thread that all workers have completed pass 0 */
1102       assert(pthread_cond_signal(&params->master_cv) == 0);
1103     }
1104     while (params->pass == 0) {
1105       assert(pthread_cond_wait(&params->cv, &params->lock) == 0);
1106     }
1107
1108     /* switch locks */
1109     assert(pthread_mutex_unlock(&params->lock) == 0);
1110     VOL_LOCK;
1111
1112     pass = params->pass;
1113     assert(pass > 0);
1114
1115     /* now escalate through the more complicated shutdowns */
1116     while (pass <= 3) {
1117         schedule_version_save = params->schedule_version;
1118         found = 0;
1119         /* find a disk partition to work on */
1120         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1121             id = diskP->index;
1122             if (params->part_thread_target[id] && !params->part_done_pass[id]) {
1123                 params->part_thread_target[id]--;
1124                 found = 1;
1125                 break;
1126             }
1127         }
1128         
1129         if (!found) {
1130             /* hmm. for some reason the controller thread couldn't find anything for 
1131              * us to do. let's see if there's anything we can do */
1132             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1133                 id = diskP->index;
1134                 if (diskP->vol_list.len && !params->part_done_pass[id]) {
1135                     found = 1;
1136                     break;
1137                 } else if (!params->part_done_pass[id]) {
1138                     params->part_done_pass[id] = 1;
1139                     params->n_parts_done_pass++;
1140                     if (pass == 3) {
1141                         Log("VShutdown:  done shutting down volumes on partition %s.\n",
1142                             VPartitionPath(diskP));
1143                     }
1144                 }
1145             }
1146         }
1147         
1148         /* do work on this partition until either the controller
1149          * creates a new schedule, or we run out of things to do
1150          * on this partition */
1151         if (found) {
1152             count = 0;
1153             while (!params->part_done_pass[id] &&
1154                    (schedule_version_save == params->schedule_version)) {
1155                 /* ShutdownVolumeWalk_r will drop the glock internally */
1156                 if (!ShutdownVolumeWalk_r(diskP, pass, &params->part_pass_head[id])) {
1157                     if (!params->part_done_pass[id]) {
1158                         params->part_done_pass[id] = 1;
1159                         params->n_parts_done_pass++;
1160                         if (pass == 3) {
1161                             Log("VShutdown:  done shutting down volumes on partition %s.\n",
1162                                 VPartitionPath(diskP));
1163                         }
1164                     }
1165                     break;
1166                 }
1167                 count++;
1168             }
1169
1170             params->stats[pass][id] += count;
1171         } else {
1172             /* ok, everyone is done this pass, proceed */
1173
1174             /* barrier lock */
1175             params->n_threads_complete++;
1176             while (params->pass == pass) {
1177                 if (params->n_threads_complete == params->n_threads) {
1178                     /* we are the last thread to complete, so we will
1179                      * reinitialize worker pool state for the next pass */
1180                     params->n_threads_complete = 0;
1181                     params->n_parts_done_pass = 0;
1182                     params->pass++;
1183                     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1184                         id = diskP->index;
1185                         params->part_done_pass[id] = 0;
1186                         params->part_pass_head[id] = queue_First(&diskP->vol_list, rx_queue);
1187                     }
1188
1189                     /* compute a new thread schedule before releasing all the workers */
1190                     ShutdownCreateSchedule(params);
1191
1192                     /* wake up all the workers */
1193                     assert(pthread_cond_broadcast(&params->cv) == 0);
1194
1195                     VOL_UNLOCK;
1196                     Log("VShutdown:  pass %d completed using %d threads on %d partitions\n",
1197                         pass, params->n_threads, params->n_parts);
1198                     VOL_LOCK;
1199                 } else {
1200                     VOL_CV_WAIT(&params->cv);
1201                 }
1202             }
1203             pass = params->pass;
1204         }
1205         
1206         /* for fairness */
1207         VOL_UNLOCK;
1208         pthread_yield();
1209         VOL_LOCK;
1210     }
1211
1212     VOL_UNLOCK;
1213
1214     return NULL;
1215 }
1216
1217 /* shut down all volumes on a given disk partition 
1218  *
1219  * note that this function will not allow mp-fast
1220  * shutdown of a partition */
1221 int
1222 VShutdownByPartition_r(struct DiskPartition64 * dp)
1223 {
1224     int pass, retVal;
1225     int pass_stats[4];
1226     int total;
1227
1228     /* wait for other exclusive ops to finish */
1229     VVByPListWait_r(dp);
1230
1231     /* begin exclusive access */
1232     VVByPListBeginExclusive_r(dp);
1233
1234     /* pick the low-hanging fruit first,
1235      * then do the complicated ones last 
1236      * (has the advantage of keeping
1237      *  in-use volumes up until the bitter end) */
1238     for (pass = 0, total=0; pass < 4; pass++) {
1239         pass_stats[pass] = ShutdownVByPForPass_r(dp, pass);
1240         total += pass_stats[pass];
1241     }
1242
1243     /* end exclusive access */
1244     VVByPListEndExclusive_r(dp);
1245
1246     Log("VShutdownByPartition:  shut down %d volumes on %s (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
1247         total, VPartitionPath(dp), pass_stats[0], pass_stats[1], pass_stats[2], pass_stats[3]);
1248
1249     return retVal;
1250 }
1251
1252 /* internal shutdown functionality
1253  *
1254  * for multi-pass shutdown:
1255  * 0 to only "shutdown" {pre,un}attached and error state volumes
1256  * 1 to also shutdown attached volumes w/ volume header loaded
1257  * 2 to also shutdown attached volumes w/o volume header loaded
1258  * 3 to also shutdown exclusive state volumes 
1259  *
1260  * caller MUST hold exclusive access on the hash chain
1261  * because we drop vol_glock_mutex internally
1262  * 
1263  * this function is reentrant for passes 1--3 
1264  * (e.g. multiple threads can cooperate to 
1265  *  shutdown a partition mp-fast)
1266  *
1267  * pass 0 is not scaleable because the volume state data is
1268  * synchronized by vol_glock mutex, and the locking overhead
1269  * is too high to drop the lock long enough to do linked list
1270  * traversal
1271  */
1272 static int
1273 ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass)
1274 {
1275     struct rx_queue * q = queue_First(&dp->vol_list, rx_queue);
1276     register int i = 0;
1277
1278     while (ShutdownVolumeWalk_r(dp, pass, &q))
1279         i++;
1280
1281     return i;
1282 }
1283
1284 /* conditionally shutdown one volume on partition dp
1285  * returns 1 if a volume was shutdown in this pass,
1286  * 0 otherwise */
1287 static int
1288 ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
1289                      struct rx_queue ** idx)
1290 {
1291     struct rx_queue *qp, *nqp;
1292     Volume * vp;
1293
1294     qp = *idx;
1295
1296     for (queue_ScanFrom(&dp->vol_list, qp, qp, nqp, rx_queue)) {
1297         vp = (Volume *) (((char *)qp) - offsetof(Volume, vol_list));
1298         
1299         switch (pass) {
1300         case 0:
1301             if ((V_attachState(vp) != VOL_STATE_UNATTACHED) &&
1302                 (V_attachState(vp) != VOL_STATE_ERROR) &&
1303                 (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1304                 break;
1305             }
1306         case 1:
1307             if ((V_attachState(vp) == VOL_STATE_ATTACHED) &&
1308                 (vp->header == NULL)) {
1309                 break;
1310             }
1311         case 2:
1312             if (VIsExclusiveState(V_attachState(vp))) {
1313                 break;
1314             }
1315         case 3:
1316             *idx = nqp;
1317             DeleteVolumeFromVByPList_r(vp);
1318             VShutdownVolume_r(vp);
1319             vp = NULL;
1320             return 1;
1321         }
1322     }
1323
1324     return 0;
1325 }
1326
1327 /*
1328  * shutdown a specific volume
1329  */
1330 /* caller MUST NOT hold a heavyweight ref on vp */
1331 int
1332 VShutdownVolume_r(Volume * vp)
1333 {
1334     int code;
1335
1336     VCreateReservation_r(vp);
1337
1338     if (LogLevel >= 5) {
1339         Log("VShutdownVolume_r:  vid=%u, device=%d, state=%hu\n",
1340             vp->hashid, vp->partition->device, V_attachState(vp));
1341     }
1342
1343     /* wait for other blocking ops to finish */
1344     VWaitExclusiveState_r(vp);
1345
1346     assert(VIsValidState(V_attachState(vp)));
1347     
1348     switch(V_attachState(vp)) {
1349     case VOL_STATE_SALVAGING:
1350         /* make sure salvager knows we don't want
1351          * the volume back */
1352         VCancelSalvage_r(vp, SALVSYNC_SHUTDOWN);
1353     case VOL_STATE_PREATTACHED:
1354     case VOL_STATE_ERROR:
1355         VChangeState_r(vp, VOL_STATE_UNATTACHED);
1356     case VOL_STATE_UNATTACHED:
1357         break;
1358     case VOL_STATE_GOING_OFFLINE:
1359     case VOL_STATE_SHUTTING_DOWN:
1360     case VOL_STATE_ATTACHED:
1361         code = VHold_r(vp);
1362         if (!code) {
1363             if (LogLevel >= 5)
1364                 Log("VShutdown:  Attempting to take volume %u offline.\n",
1365                     vp->hashid);
1366
1367             /* take the volume offline (drops reference count) */
1368             VOffline_r(vp, "File server was shut down");
1369         }
1370         break;
1371     }
1372     
1373     VCancelReservation_r(vp);
1374     vp = NULL;
1375     return 0;
1376 }
1377 #endif /* AFS_DEMAND_ATTACH_FS */
1378
1379
1380 /***************************************************/
1381 /* Header I/O routines                             */
1382 /***************************************************/
1383
1384 /* open a descriptor for the inode (h),
1385  * read in an on-disk structure into buffer (to) of size (size),
1386  * verify versionstamp in structure has magic (magic) and
1387  * optionally verify version (version) if (version) is nonzero
1388  */
1389 static void
1390 ReadHeader(Error * ec, IHandle_t * h, char *to, int size, bit32 magic,
1391            bit32 version)
1392 {
1393     struct versionStamp *vsn;
1394     FdHandle_t *fdP;
1395
1396     *ec = 0;
1397     if (h == NULL) {
1398         *ec = VSALVAGE;
1399         return;
1400     }
1401
1402     fdP = IH_OPEN(h);
1403     if (fdP == NULL) {
1404         *ec = VSALVAGE;
1405         return;
1406     }
1407
1408     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1409         *ec = VSALVAGE;
1410         FDH_REALLYCLOSE(fdP);
1411         return;
1412     }
1413     vsn = (struct versionStamp *)to;
1414     if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
1415         *ec = VSALVAGE;
1416         FDH_REALLYCLOSE(fdP);
1417         return;
1418     }
1419     FDH_CLOSE(fdP);
1420
1421     /* Check is conditional, in case caller wants to inspect version himself */
1422     if (version && vsn->version != version) {
1423         *ec = VSALVAGE;
1424     }
1425 }
1426
1427 void
1428 WriteVolumeHeader_r(Error * ec, Volume * vp)
1429 {
1430     IHandle_t *h = V_diskDataHandle(vp);
1431     FdHandle_t *fdP;
1432
1433     *ec = 0;
1434
1435     fdP = IH_OPEN(h);
1436     if (fdP == NULL) {
1437         *ec = VSALVAGE;
1438         return;
1439     }
1440     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1441         *ec = VSALVAGE;
1442         FDH_REALLYCLOSE(fdP);
1443         return;
1444     }
1445     if (FDH_WRITE(fdP, (char *)&V_disk(vp), sizeof(V_disk(vp)))
1446         != sizeof(V_disk(vp))) {
1447         *ec = VSALVAGE;
1448         FDH_REALLYCLOSE(fdP);
1449         return;
1450     }
1451     FDH_CLOSE(fdP);
1452 }
1453
1454 /* VolumeHeaderToDisk
1455  * Allows for storing 64 bit inode numbers in on-disk volume header
1456  * file.
1457  */
1458 /* convert in-memory representation of a volume header to the
1459  * on-disk representation of a volume header */
1460 void
1461 VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
1462 {
1463
1464     memset((char *)dh, 0, sizeof(VolumeDiskHeader_t));
1465     dh->stamp = h->stamp;
1466     dh->id = h->id;
1467     dh->parent = h->parent;
1468
1469 #ifdef AFS_64BIT_IOPS_ENV
1470     dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
1471     dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
1472     dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
1473     dh->smallVnodeIndex_hi =
1474         (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
1475     dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
1476     dh->largeVnodeIndex_hi =
1477         (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
1478     dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
1479     dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
1480 #else
1481     dh->volumeInfo_lo = h->volumeInfo;
1482     dh->smallVnodeIndex_lo = h->smallVnodeIndex;
1483     dh->largeVnodeIndex_lo = h->largeVnodeIndex;
1484     dh->linkTable_lo = h->linkTable;
1485 #endif
1486 }
1487
1488 /* DiskToVolumeHeader
1489  * Converts an on-disk representation of a volume header to
1490  * the in-memory representation of a volume header.
1491  *
1492  * Makes the assumption that AFS has *always* 
1493  * zero'd the volume header file so that high parts of inode
1494  * numbers are 0 in older (SGI EFS) volume header files.
1495  */
1496 void
1497 DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
1498 {
1499     memset((char *)h, 0, sizeof(VolumeHeader_t));
1500     h->stamp = dh->stamp;
1501     h->id = dh->id;
1502     h->parent = dh->parent;
1503
1504 #ifdef AFS_64BIT_IOPS_ENV
1505     h->volumeInfo =
1506         (Inode) dh->volumeInfo_lo | ((Inode) dh->volumeInfo_hi << 32);
1507
1508     h->smallVnodeIndex =
1509         (Inode) dh->smallVnodeIndex_lo | ((Inode) dh->
1510                                           smallVnodeIndex_hi << 32);
1511
1512     h->largeVnodeIndex =
1513         (Inode) dh->largeVnodeIndex_lo | ((Inode) dh->
1514                                           largeVnodeIndex_hi << 32);
1515     h->linkTable =
1516         (Inode) dh->linkTable_lo | ((Inode) dh->linkTable_hi << 32);
1517 #else
1518     h->volumeInfo = dh->volumeInfo_lo;
1519     h->smallVnodeIndex = dh->smallVnodeIndex_lo;
1520     h->largeVnodeIndex = dh->largeVnodeIndex_lo;
1521     h->linkTable = dh->linkTable_lo;
1522 #endif
1523 }
1524
1525
1526 /***************************************************/
1527 /* Volume Attachment routines                      */
1528 /***************************************************/
1529
1530 #ifdef AFS_DEMAND_ATTACH_FS
1531 /**
1532  * pre-attach a volume given its path.
1533  *
1534  * @param[out] ec         outbound error code
1535  * @param[in]  partition  partition path string
1536  * @param[in]  name       volume id string
1537  *
1538  * @return volume object pointer
1539  *
1540  * @note A pre-attached volume will only have its partition
1541  *       and hashid fields initialized.  At first call to 
1542  *       VGetVolume, the volume will be fully attached.
1543  *
1544  */
1545 Volume *
1546 VPreAttachVolumeByName(Error * ec, char *partition, char *name)
1547 {
1548     Volume * vp;
1549     VOL_LOCK;
1550     vp = VPreAttachVolumeByName_r(ec, partition, name);
1551     VOL_UNLOCK;
1552     return vp;
1553 }
1554
1555 /**
1556  * pre-attach a volume given its path.
1557  *
1558  * @param[out] ec         outbound error code
1559  * @param[in]  partition  path to vice partition
1560  * @param[in]  name       volume id string
1561  *
1562  * @return volume object pointer
1563  *
1564  * @pre VOL_LOCK held
1565  *
1566  * @internal volume package internal use only.
1567  */
1568 Volume *
1569 VPreAttachVolumeByName_r(Error * ec, char *partition, char *name)
1570 {
1571     return VPreAttachVolumeById_r(ec, 
1572                                   partition,
1573                                   VolumeNumber(name));
1574 }
1575
1576 /**
1577  * pre-attach a volume given its path and numeric volume id.
1578  *
1579  * @param[out] ec          error code return
1580  * @param[in]  partition   path to vice partition
1581  * @param[in]  volumeId    numeric volume id
1582  *
1583  * @return volume object pointer
1584  *
1585  * @pre VOL_LOCK held
1586  *
1587  * @internal volume package internal use only.
1588  */
1589 Volume *
1590 VPreAttachVolumeById_r(Error * ec, 
1591                        char * partition,
1592                        VolId volumeId)
1593 {
1594     Volume *vp;
1595     struct DiskPartition64 *partp;
1596
1597     *ec = 0;
1598
1599     assert(programType == fileServer);
1600
1601     if (!(partp = VGetPartition_r(partition, 0))) {
1602         *ec = VNOVOL;
1603         Log("VPreAttachVolumeById_r:  Error getting partition (%s)\n", partition);
1604         return NULL;
1605     }
1606
1607     vp = VLookupVolume_r(ec, volumeId, NULL);
1608     if (*ec) {
1609         return NULL;
1610     }
1611
1612     return VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
1613 }
1614
1615 /**
1616  * preattach a volume.
1617  *
1618  * @param[out] ec     outbound error code
1619  * @param[in]  partp  pointer to partition object
1620  * @param[in]  vp     pointer to volume object
1621  * @param[in]  vid    volume id
1622  *
1623  * @return volume object pointer
1624  *
1625  * @pre VOL_LOCK is held.
1626  *
1627  * @warning Returned volume object pointer does not have to
1628  *          equal the pointer passed in as argument vp.  There
1629  *          are potential race conditions which can result in
1630  *          the pointers having different values.  It is up to
1631  *          the caller to make sure that references are handled
1632  *          properly in this case.
1633  *
1634  * @note If there is already a volume object registered with
1635  *       the same volume id, its pointer MUST be passed as 
1636  *       argument vp.  Failure to do so will result in a silent
1637  *       failure to preattach.
1638  *
1639  * @internal volume package internal use only.
1640  */
1641 Volume * 
1642 VPreAttachVolumeByVp_r(Error * ec, 
1643                        struct DiskPartition64 * partp, 
1644                        Volume * vp,
1645                        VolId vid)
1646 {
1647     Volume *nvp = NULL;
1648
1649     *ec = 0;
1650
1651     /* check to see if pre-attach already happened */
1652     if (vp && 
1653         (V_attachState(vp) != VOL_STATE_UNATTACHED) && 
1654         (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
1655         !VIsErrorState(V_attachState(vp))) {
1656         /*
1657          * pre-attach is a no-op in all but the following cases:
1658          *
1659          *   - volume is unattached
1660          *   - volume is in an error state
1661          *   - volume is pre-attached
1662          */
1663         Log("VPreattachVolumeByVp_r: volume %u not in quiescent state\n", vid);
1664         goto done;
1665     } else if (vp) {
1666         /* we're re-attaching a volume; clear out some old state */
1667         memset(&vp->salvage, 0, sizeof(struct VolumeOnlineSalvage));
1668
1669         if (V_partition(vp) != partp) {
1670             /* XXX potential race */
1671             DeleteVolumeFromVByPList_r(vp);
1672         }
1673     } else {
1674         /* if we need to allocate a new Volume struct,
1675          * go ahead and drop the vol glock, otherwise
1676          * do the basic setup synchronised, as it's
1677          * probably not worth dropping the lock */
1678         VOL_UNLOCK;
1679
1680         /* allocate the volume structure */
1681         vp = nvp = (Volume *) malloc(sizeof(Volume));
1682         assert(vp != NULL);
1683         memset(vp, 0, sizeof(Volume));
1684         queue_Init(&vp->vnode_list);
1685         assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1686     }
1687
1688     /* link the volume with its associated vice partition */
1689     vp->device = partp->device;
1690     vp->partition = partp;
1691
1692     vp->hashid = vid;
1693     vp->specialStatus = 0;
1694
1695     /* if we dropped the lock, reacquire the lock,
1696      * check for pre-attach races, and then add
1697      * the volume to the hash table */
1698     if (nvp) {
1699         VOL_LOCK;
1700         nvp = VLookupVolume_r(ec, vid, NULL);
1701         if (*ec) {
1702             free(vp);
1703             vp = NULL;
1704             goto done;
1705         } else if (nvp) { /* race detected */
1706             free(vp);
1707             vp = nvp;
1708             goto done;
1709         } else {
1710           /* hack to make up for VChangeState_r() decrementing 
1711            * the old state counter */
1712           VStats.state_levels[0]++;
1713         }
1714     }
1715
1716     /* put pre-attached volume onto the hash table
1717      * and bring it up to the pre-attached state */
1718     AddVolumeToHashTable(vp, vp->hashid);
1719     AddVolumeToVByPList_r(vp);
1720     VLRU_Init_Node_r(vp);
1721     VChangeState_r(vp, VOL_STATE_PREATTACHED);
1722
1723     if (LogLevel >= 5)
1724         Log("VPreAttachVolumeByVp_r:  volume %u pre-attached\n", vp->hashid);
1725
1726   done:
1727     if (*ec)
1728         return NULL;
1729     else
1730         return vp;
1731 }
1732 #endif /* AFS_DEMAND_ATTACH_FS */
1733
1734 /* Attach an existing volume, given its pathname, and return a
1735    pointer to the volume header information.  The volume also
1736    normally goes online at this time.  An offline volume
1737    must be reattached to make it go online */
1738 Volume *
1739 VAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
1740 {
1741     Volume *retVal;
1742     VOL_LOCK;
1743     retVal = VAttachVolumeByName_r(ec, partition, name, mode);
1744     VOL_UNLOCK;
1745     return retVal;
1746 }
1747
1748 Volume *
1749 VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
1750 {
1751     register Volume *vp = NULL;
1752     int fd, n;
1753     struct afs_stat status;
1754     struct VolumeDiskHeader diskHeader;
1755     struct VolumeHeader iheader;
1756     struct DiskPartition64 *partp;
1757     char path[64];
1758     int isbusy = 0;
1759     VolId volumeId;
1760 #ifdef AFS_DEMAND_ATTACH_FS
1761     VolumeStats stats_save;
1762     Volume *svp = NULL;
1763 #endif /* AFS_DEMAND_ATTACH_FS */
1764
1765     *ec = 0;
1766    
1767     volumeId = VolumeNumber(name);
1768
1769     if (!(partp = VGetPartition_r(partition, 0))) {
1770         *ec = VNOVOL;
1771         Log("VAttachVolume: Error getting partition (%s)\n", partition);
1772         goto done;
1773     }
1774
1775     if (programType == volumeUtility) {
1776         assert(VInit == 3);
1777         VLockPartition_r(partition);
1778     } else if (programType == fileServer) {
1779 #ifdef AFS_DEMAND_ATTACH_FS
1780         /* lookup the volume in the hash table */
1781         vp = VLookupVolume_r(ec, volumeId, NULL);
1782         if (*ec) {
1783             return NULL;
1784         }
1785
1786         if (vp) {
1787             /* save any counters that are supposed to
1788              * be monotonically increasing over the
1789              * lifetime of the fileserver */
1790             memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
1791         } else {
1792             memset(&stats_save, 0, sizeof(VolumeStats));
1793         }
1794
1795         /* if there's something in the hash table, and it's not
1796          * in the pre-attach state, then we may need to detach
1797          * it before proceeding */
1798         if (vp && (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1799             VCreateReservation_r(vp);
1800             VWaitExclusiveState_r(vp);
1801
1802             /* at this point state must be one of:
1803              *   - UNATTACHED
1804              *   - ATTACHED
1805              *   - SHUTTING_DOWN
1806              *   - GOING_OFFLINE
1807              *   - SALVAGING
1808              *   - ERROR
1809              */
1810
1811             if (vp->specialStatus == VBUSY)
1812                 isbusy = 1;
1813             
1814             /* if it's already attached, see if we can return it */
1815             if (V_attachState(vp) == VOL_STATE_ATTACHED) {
1816                 VGetVolumeByVp_r(ec, vp);
1817                 if (V_inUse(vp) == fileServer) {
1818                     VCancelReservation_r(vp);
1819                     return vp;
1820                 }
1821
1822                 /* otherwise, we need to detach, and attempt to re-attach */
1823                 VDetachVolume_r(ec, vp);
1824                 if (*ec) {
1825                     Log("VAttachVolume: Error detaching old volume instance (%s)\n", name);
1826                 }
1827             } else {
1828                 /* if it isn't fully attached, delete from the hash tables,
1829                    and let the refcounter handle the rest */
1830                 DeleteVolumeFromHashTable(vp);
1831                 DeleteVolumeFromVByPList_r(vp);
1832             }
1833
1834             VCancelReservation_r(vp);
1835             vp = NULL;
1836         }
1837
1838         /* pre-attach volume if it hasn't been done yet */
1839         if (!vp || 
1840             (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
1841             (V_attachState(vp) == VOL_STATE_ERROR)) {
1842             svp = vp;
1843             vp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
1844             if (*ec) {
1845                 return NULL;
1846             }
1847         }
1848
1849         assert(vp != NULL);
1850
1851         /* handle pre-attach races 
1852          *
1853          * multiple threads can race to pre-attach a volume,
1854          * but we can't let them race beyond that
1855          * 
1856          * our solution is to let the first thread to bring
1857          * the volume into an exclusive state win; the other
1858          * threads just wait until it finishes bringing the
1859          * volume online, and then they do a vgetvolumebyvp
1860          */
1861         if (svp && (svp != vp)) {
1862             /* wait for other exclusive ops to finish */
1863             VCreateReservation_r(vp);
1864             VWaitExclusiveState_r(vp);
1865
1866             /* get a heavyweight ref, kill the lightweight ref, and return */
1867             VGetVolumeByVp_r(ec, vp);
1868             VCancelReservation_r(vp);
1869             return vp;
1870         }
1871
1872         /* at this point, we are chosen as the thread to do
1873          * demand attachment for this volume. all other threads
1874          * doing a getvolume on vp->hashid will block until we finish */
1875
1876         /* make sure any old header cache entries are invalidated
1877          * before proceeding */
1878         FreeVolumeHeader(vp);
1879
1880         VChangeState_r(vp, VOL_STATE_ATTACHING);
1881
1882         /* restore any saved counters */
1883         memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
1884 #else /* AFS_DEMAND_ATTACH_FS */
1885         vp = VGetVolume_r(ec, volumeId);
1886         if (vp) {
1887             if (V_inUse(vp) == fileServer)
1888                 return vp;
1889             if (vp->specialStatus == VBUSY)
1890                 isbusy = 1;
1891             VDetachVolume_r(ec, vp);
1892             if (*ec) {
1893                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
1894             }
1895             vp = NULL;
1896         }
1897 #endif /* AFS_DEMAND_ATTACH_FS */
1898     }
1899
1900     *ec = 0;
1901     strcpy(path, VPartitionPath(partp));
1902
1903     VOL_UNLOCK;
1904
1905     strcat(path, "/");
1906     strcat(path, name);
1907     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
1908         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
1909         if (fd > -1)
1910             close(fd);
1911         *ec = VNOVOL;
1912         VOL_LOCK;
1913         goto done;
1914     }
1915     n = read(fd, &diskHeader, sizeof(diskHeader));
1916     close(fd);
1917     if (n != sizeof(diskHeader)
1918         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1919         Log("VAttachVolume: Error reading volume header %s\n", path);
1920         *ec = VSALVAGE;
1921         VOL_LOCK;
1922         goto done;
1923     }
1924     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
1925         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
1926         *ec = VSALVAGE;
1927         VOL_LOCK;
1928         goto done;
1929     }
1930
1931     DiskToVolumeHeader(&iheader, &diskHeader);
1932 #ifdef FSSYNC_BUILD_CLIENT
1933     if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
1934         VOL_LOCK;
1935         if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, NULL)
1936             != SYNC_OK) {
1937             Log("VAttachVolume: attach of volume %u apparently denied by file server\n", iheader.id);
1938             *ec = VNOVOL;       /* XXXX */
1939             goto done;
1940         }
1941         VOL_UNLOCK;
1942     }
1943 #endif
1944
1945     if (!vp) {
1946       vp = (Volume *) calloc(1, sizeof(Volume));
1947       assert(vp != NULL);
1948       vp->device = partp->device;
1949       vp->partition = partp;
1950       queue_Init(&vp->vnode_list);
1951 #ifdef AFS_DEMAND_ATTACH_FS
1952       assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1953 #endif /* AFS_DEMAND_ATTACH_FS */
1954     }
1955
1956     /* attach2 is entered without any locks, and returns
1957      * with vol_glock_mutex held */
1958     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
1959
1960     if (programType == volumeUtility && vp) {
1961         if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
1962             /* mark volume header as in use so that volser crashes lead to a
1963              * salvage attempt */
1964             VUpdateVolume_r(ec, vp, 0);
1965         }
1966 #ifdef AFS_DEMAND_ATTACH_FS
1967         /* for dafs, we should tell the fileserver, except for V_PEEK
1968          * where we know it is not necessary */
1969         if (mode == V_PEEK) {
1970             vp->needsPutBack = 0;
1971         } else {
1972             vp->needsPutBack = 1;
1973         }
1974 #else /* !AFS_DEMAND_ATTACH_FS */
1975         /* duplicate computation in fssync.c about whether the server
1976          * takes the volume offline or not.  If the volume isn't
1977          * offline, we must not return it when we detach the volume,
1978          * or the server will abort */
1979         if (mode == V_READONLY || mode == V_PEEK
1980             || (!VolumeWriteable(vp) && (mode == V_CLONE || mode == V_DUMP)))
1981             vp->needsPutBack = 0;
1982         else
1983             vp->needsPutBack = 1;
1984 #endif /* !AFS_DEMAND_ATTACH_FS */
1985     }
1986     /* OK, there's a problem here, but one that I don't know how to
1987      * fix right now, and that I don't think should arise often.
1988      * Basically, we should only put back this volume to the server if
1989      * it was given to us by the server, but since we don't have a vp,
1990      * we can't run the VolumeWriteable function to find out as we do
1991      * above when computing vp->needsPutBack.  So we send it back, but
1992      * there's a path in VAttachVolume on the server which may abort
1993      * if this volume doesn't have a header.  Should be pretty rare
1994      * for all of that to happen, but if it does, probably the right
1995      * fix is for the server to allow the return of readonly volumes
1996      * that it doesn't think are really checked out. */
1997 #ifdef FSSYNC_BUILD_CLIENT
1998     if (programType == volumeUtility && vp == NULL &&
1999         mode != V_SECRETLY && mode != V_PEEK) {
2000         FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_ON, 0, NULL);
2001     } else 
2002 #endif
2003     if (programType == fileServer && vp) {
2004 #ifdef AFS_DEMAND_ATTACH_FS
2005         /* 
2006          * we can get here in cases where we don't "own"
2007          * the volume (e.g. volume owned by a utility).
2008          * short circuit around potential disk header races.
2009          */
2010         if (V_attachState(vp) != VOL_STATE_ATTACHED) {
2011             goto done;
2012         }
2013 #endif
2014         V_needsCallback(vp) = 0;
2015 #ifdef  notdef
2016         if (VInit >= 2 && V_BreakVolumeCallbacks) {
2017             Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
2018             (*V_BreakVolumeCallbacks) (V_id(vp));
2019         }
2020 #endif
2021         VUpdateVolume_r(ec, vp, 0);
2022         if (*ec) {
2023             Log("VAttachVolume: Error updating volume\n");
2024             if (vp)
2025                 VPutVolume_r(vp);
2026             goto done;
2027         }
2028         if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2029 #ifndef AFS_DEMAND_ATTACH_FS
2030             /* This is a hack: by temporarily setting the incore
2031              * dontSalvage flag ON, the volume will be put back on the
2032              * Update list (with dontSalvage OFF again).  It will then
2033              * come back in N minutes with DONT_SALVAGE eventually
2034              * set.  This is the way that volumes that have never had
2035              * it set get it set; or that volumes that have been
2036              * offline without DONT SALVAGE having been set also
2037              * eventually get it set */
2038             V_dontSalvage(vp) = DONT_SALVAGE;
2039 #endif /* !AFS_DEMAND_ATTACH_FS */
2040             VAddToVolumeUpdateList_r(ec, vp);
2041             if (*ec) {
2042                 Log("VAttachVolume: Error adding volume to update list\n");
2043                 if (vp)
2044                     VPutVolume_r(vp);
2045                 goto done;
2046             }
2047         }
2048         if (LogLevel)
2049             Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2050                 V_name(vp));
2051     }
2052
2053   done:
2054     if (programType == volumeUtility) {
2055         VUnlockPartition_r(partition);
2056     }
2057     if (*ec) {
2058 #ifdef AFS_DEMAND_ATTACH_FS
2059         /* attach failed; make sure we're in error state */
2060         if (vp && !VIsErrorState(V_attachState(vp))) {
2061             VChangeState_r(vp, VOL_STATE_ERROR);
2062         }
2063 #endif /* AFS_DEMAND_ATTACH_FS */
2064         return NULL;
2065     } else {
2066         return vp;
2067     }
2068 }
2069
2070 #ifdef AFS_DEMAND_ATTACH_FS
2071 /* VAttachVolumeByVp_r
2072  *
2073  * finish attaching a volume that is
2074  * in a less than fully attached state
2075  */
2076 /* caller MUST hold a ref count on vp */
2077 static Volume *
2078 VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
2079 {
2080     char name[VMAXPATHLEN];
2081     int fd, n, reserve = 0;
2082     struct afs_stat status;
2083     struct VolumeDiskHeader diskHeader;
2084     struct VolumeHeader iheader;
2085     struct DiskPartition64 *partp;
2086     char path[64];
2087     int isbusy = 0;
2088     VolId volumeId;
2089     Volume * nvp;
2090     VolumeStats stats_save;
2091     *ec = 0;
2092
2093     /* volume utility should never call AttachByVp */
2094     assert(programType == fileServer);
2095    
2096     volumeId = vp->hashid;
2097     partp = vp->partition;
2098     VolumeExternalName_r(volumeId, name, sizeof(name));
2099
2100
2101     /* if another thread is performing a blocking op, wait */
2102     VWaitExclusiveState_r(vp);
2103
2104     memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
2105
2106     /* if it's already attached, see if we can return it */
2107     if (V_attachState(vp) == VOL_STATE_ATTACHED) {
2108         VGetVolumeByVp_r(ec, vp);
2109         if (V_inUse(vp) == fileServer) {
2110             return vp;
2111         } else {
2112             if (vp->specialStatus == VBUSY)
2113                 isbusy = 1;
2114             VDetachVolume_r(ec, vp);
2115             if (*ec) {
2116                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
2117             }
2118             vp = NULL;
2119         }
2120     }
2121
2122     /* pre-attach volume if it hasn't been done yet */
2123     if (!vp || 
2124         (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
2125         (V_attachState(vp) == VOL_STATE_ERROR)) {
2126         nvp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
2127         if (*ec) {
2128             return NULL;
2129         }
2130         if (nvp != vp) {
2131             reserve = 1;
2132             VCreateReservation_r(nvp);
2133             vp = nvp;
2134         }
2135     }
2136     
2137     assert(vp != NULL);
2138     VChangeState_r(vp, VOL_STATE_ATTACHING);
2139
2140     /* restore monotonically increasing stats */
2141     memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
2142
2143     *ec = 0;
2144
2145
2146     /* compute path to disk header, 
2147      * read in header, 
2148      * and verify magic and version stamps */
2149     strcpy(path, VPartitionPath(partp));
2150
2151     VOL_UNLOCK;
2152
2153     strcat(path, "/");
2154     strcat(path, name);
2155     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
2156         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
2157         if (fd > -1)
2158             close(fd);
2159         *ec = VNOVOL;
2160         VOL_LOCK;
2161         goto done;
2162     }
2163     n = read(fd, &diskHeader, sizeof(diskHeader));
2164     close(fd);
2165     if (n != sizeof(diskHeader)
2166         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
2167         Log("VAttachVolume: Error reading volume header %s\n", path);
2168         *ec = VSALVAGE;
2169         VOL_LOCK;
2170         goto done;
2171     }
2172     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
2173         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
2174         *ec = VSALVAGE;
2175         VOL_LOCK;
2176         goto done;
2177     }
2178
2179     /* convert on-disk header format to in-memory header format */
2180     DiskToVolumeHeader(&iheader, &diskHeader);
2181
2182     /* do volume attach
2183      *
2184      * NOTE: attach2 is entered without any locks, and returns
2185      * with vol_glock_mutex held */
2186     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
2187
2188     /*
2189      * the event that an error was encountered, or
2190      * the volume was not brought to an attached state
2191      * for any reason, skip to the end.  We cannot
2192      * safely call VUpdateVolume unless we "own" it.
2193      */
2194     if (*ec || 
2195         (vp == NULL) ||
2196         (V_attachState(vp) != VOL_STATE_ATTACHED)) {
2197         goto done;
2198     }
2199
2200     V_needsCallback(vp) = 0;
2201     VUpdateVolume_r(ec, vp, 0);
2202     if (*ec) {
2203         Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
2204         VPutVolume_r(vp);
2205         goto done;
2206     }
2207     if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2208 #ifndef AFS_DEMAND_ATTACH_FS
2209         /* This is a hack: by temporarily setting the incore
2210          * dontSalvage flag ON, the volume will be put back on the
2211          * Update list (with dontSalvage OFF again).  It will then
2212          * come back in N minutes with DONT_SALVAGE eventually
2213          * set.  This is the way that volumes that have never had
2214          * it set get it set; or that volumes that have been
2215          * offline without DONT SALVAGE having been set also
2216          * eventually get it set */
2217         V_dontSalvage(vp) = DONT_SALVAGE;
2218 #endif /* !AFS_DEMAND_ATTACH_FS */
2219         VAddToVolumeUpdateList_r(ec, vp);
2220         if (*ec) {
2221             Log("VAttachVolume: Error adding volume %u to update list\n", vp->hashid);
2222             if (vp)
2223                 VPutVolume_r(vp);
2224             goto done;
2225         }
2226     }
2227     if (LogLevel)
2228         Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2229             V_name(vp));
2230   done:
2231     if (reserve) {
2232         VCancelReservation_r(nvp);
2233         reserve = 0;
2234     }
2235     if (*ec && (*ec != VOFFLINE) && (*ec != VSALVAGE)) {
2236         if (vp && !VIsErrorState(V_attachState(vp))) {
2237             VChangeState_r(vp, VOL_STATE_ERROR);
2238         }
2239         return NULL;
2240     } else {
2241         return vp;
2242     }
2243 }
2244 #endif /* AFS_DEMAND_ATTACH_FS */
2245
2246 /*
2247  * called without any locks held
2248  * returns with vol_glock_mutex held
2249  */
2250 private Volume * 
2251 attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * header,
2252         struct DiskPartition64 * partp, register Volume * vp, int isbusy, int mode)
2253 {
2254     vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
2255     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
2256             header->largeVnodeIndex);
2257     IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
2258             header->smallVnodeIndex);
2259     IH_INIT(vp->diskDataHandle, partp->device, header->parent,
2260             header->volumeInfo);
2261     IH_INIT(vp->linkHandle, partp->device, header->parent, header->linkTable);
2262     vp->shuttingDown = 0;
2263     vp->goingOffline = 0;
2264     vp->nUsers = 1;
2265 #ifdef AFS_DEMAND_ATTACH_FS
2266     vp->stats.last_attach = FT_ApproxTime();
2267     vp->stats.attaches++;
2268 #endif
2269
2270     VOL_LOCK;
2271     IncUInt64(&VStats.attaches);
2272     vp->cacheCheck = ++VolumeCacheCheck;
2273     /* just in case this ever rolls over */
2274     if (!vp->cacheCheck)
2275         vp->cacheCheck = ++VolumeCacheCheck;
2276     GetVolumeHeader(vp);
2277     VOL_UNLOCK;
2278
2279 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
2280     /* demand attach changes the V_PEEK mechanism
2281      *
2282      * we can now suck the current disk data structure over
2283      * the fssync interface without going to disk
2284      *
2285      * (technically, we don't need to restrict this feature
2286      *  to demand attach fileservers.  However, I'm trying
2287      *  to limit the number of common code changes)
2288      */
2289     if (programType != fileServer && mode == V_PEEK) {
2290         SYNC_response res;
2291         res.payload.len = sizeof(VolumeDiskData);
2292         res.payload.buf = &vp->header->diskstuff;
2293
2294         if (FSYNC_VolOp(volumeId,
2295                         partp->name,
2296                         FSYNC_VOL_QUERY_HDR,
2297                         FSYNC_WHATEVER,
2298                         &res) == SYNC_OK) {
2299             goto disk_header_loaded;
2300         }
2301     }
2302 #endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
2303     (void)ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
2304                      sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
2305
2306 #ifdef AFS_DEMAND_ATTACH_FS
2307     /* update stats */
2308     VOL_LOCK;
2309     IncUInt64(&VStats.hdr_loads);
2310     IncUInt64(&vp->stats.hdr_loads);
2311     VOL_UNLOCK;
2312 #endif /* AFS_DEMAND_ATTACH_FS */
2313     
2314     if (*ec) {
2315         Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
2316     }
2317
2318 #ifdef AFS_DEMAND_ATTACH_FS
2319  disk_header_loaded:
2320     if (!*ec) {
2321
2322         /* check for pending volume operations */
2323         if (vp->pending_vol_op) {
2324             /* see if the pending volume op requires exclusive access */
2325             switch (vp->pending_vol_op->vol_op_state) {
2326             case FSSYNC_VolOpPending:
2327                 /* this should never happen */
2328                 assert(vp->pending_vol_op->vol_op_state != FSSYNC_VolOpPending);
2329                 break;
2330
2331             case FSSYNC_VolOpRunningUnknown:
2332                 if (VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2333                     vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
2334                     break;
2335                 } else {
2336                     vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
2337                     /* fall through to take volume offline */
2338                 }
2339
2340             case FSSYNC_VolOpRunningOffline:
2341                 /* mark the volume down */
2342                 *ec = VOFFLINE;
2343                 VChangeState_r(vp, VOL_STATE_UNATTACHED);
2344                 if (V_offlineMessage(vp)[0] == '\0')
2345                     strlcpy(V_offlineMessage(vp),
2346                             "A volume utility is running.", 
2347                             sizeof(V_offlineMessage(vp)));
2348                 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
2349
2350                 /* check to see if we should set the specialStatus flag */
2351                 if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
2352                     vp->specialStatus = VBUSY;
2353                 }
2354             }
2355         }
2356
2357         V_attachFlags(vp) |= VOL_HDR_LOADED;
2358         vp->stats.last_hdr_load = vp->stats.last_attach;
2359     }
2360 #endif /* AFS_DEMAND_ATTACH_FS */
2361
2362     if (!*ec) {
2363         struct IndexFileHeader iHead;
2364
2365 #if OPENAFS_VOL_STATS
2366         /*
2367          * We just read in the diskstuff part of the header.  If the detailed
2368          * volume stats area has not yet been initialized, we should bzero the
2369          * area and mark it as initialized.
2370          */
2371         if (!(V_stat_initialized(vp))) {
2372             memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
2373             V_stat_initialized(vp) = 1;
2374         }
2375 #endif /* OPENAFS_VOL_STATS */
2376
2377         (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
2378                          (char *)&iHead, sizeof(iHead),
2379                          SMALLINDEXMAGIC, SMALLINDEXVERSION);
2380
2381         if (*ec) {
2382             Log("VAttachVolume: Error reading smallVnode vol header %s; error=%u\n", path, *ec);
2383         }
2384     }
2385
2386     if (!*ec) {
2387         struct IndexFileHeader iHead;
2388
2389         (void)ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
2390                          (char *)&iHead, sizeof(iHead),
2391                          LARGEINDEXMAGIC, LARGEINDEXVERSION);
2392
2393         if (*ec) {
2394             Log("VAttachVolume: Error reading largeVnode vol header %s; error=%u\n", path, *ec);
2395         }
2396     }
2397
2398 #ifdef AFS_NAMEI_ENV
2399     if (!*ec) {
2400         struct versionStamp stamp;
2401
2402         (void)ReadHeader(ec, V_linkHandle(vp), (char *)&stamp,
2403                          sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION);
2404
2405         if (*ec) {
2406             Log("VAttachVolume: Error reading namei vol header %s; error=%u\n", path, *ec);
2407         }
2408     }
2409 #endif /* AFS_NAMEI_ENV */
2410
2411 #if defined(AFS_DEMAND_ATTACH_FS)
2412     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
2413         VOL_LOCK;
2414         if (programType == fileServer) {
2415             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2416             vp->nUsers = 0;
2417         } else {
2418             Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2419             FreeVolume(vp);
2420             *ec = VSALVAGE;
2421         }
2422         return NULL;
2423     } else if (*ec) {
2424         /* volume operation in progress */
2425         VOL_LOCK;
2426         return NULL;
2427     }
2428 #else /* AFS_DEMAND_ATTACH_FS */
2429     if (*ec) {
2430         Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2431         VOL_LOCK;
2432         FreeVolume(vp);
2433         return NULL;
2434     }
2435 #endif /* AFS_DEMAND_ATTACH_FS */
2436
2437     if (V_needsSalvaged(vp)) {
2438         if (vp->specialStatus)
2439             vp->specialStatus = 0;
2440         VOL_LOCK;
2441 #if defined(AFS_DEMAND_ATTACH_FS)
2442         if (programType == fileServer) {
2443             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2444             vp->nUsers = 0;
2445         } else {
2446             Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
2447             FreeVolume(vp);
2448             *ec = VSALVAGE;
2449         }
2450 #else /* AFS_DEMAND_ATTACH_FS */
2451         FreeVolume(vp);
2452         *ec = VSALVAGE;
2453 #endif /* AFS_DEMAND_ATTACH_FS */
2454         return NULL;
2455     }
2456
2457     VOL_LOCK;
2458     if (programType == fileServer) {
2459 #ifndef FAST_RESTART
2460         if (V_inUse(vp) && VolumeWriteable(vp)) {
2461             if (!V_needsSalvaged(vp)) {
2462                 V_needsSalvaged(vp) = 1;
2463                 VUpdateVolume_r(ec, vp, 0);
2464             }
2465 #if defined(AFS_DEMAND_ATTACH_FS)
2466             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2467             vp->nUsers = 0;
2468 #else /* AFS_DEMAND_ATTACH_FS */
2469             Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
2470             FreeVolume(vp);
2471             *ec = VSALVAGE;
2472 #endif /* AFS_DEMAND_ATTACH_FS */
2473             return NULL;
2474         }
2475 #endif /* FAST_RESTART */
2476
2477         if (V_destroyMe(vp) == DESTROY_ME) {
2478 #if defined(AFS_DEMAND_ATTACH_FS)
2479             /* schedule a salvage so the volume goes away on disk */
2480             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2481             VChangeState_r(vp, VOL_STATE_ERROR);
2482             vp->nUsers = 0;
2483 #endif /* AFS_DEMAND_ATTACH_FS */
2484             FreeVolume(vp);
2485             Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
2486             *ec = VNOVOL;
2487             return NULL;
2488         }
2489     }
2490
2491     vp->nextVnodeUnique = V_uniquifier(vp);
2492     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
2493 #ifndef BITMAP_LATER
2494     if (programType == fileServer && VolumeWriteable(vp)) {
2495         int i;
2496         for (i = 0; i < nVNODECLASSES; i++) {
2497             VGetBitmap_r(ec, vp, i);
2498             if (*ec) {
2499 #ifdef AFS_DEMAND_ATTACH_FS
2500                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2501                 vp->nUsers = 0;
2502 #else /* AFS_DEMAND_ATTACH_FS */
2503                 FreeVolume(vp);
2504 #endif /* AFS_DEMAND_ATTACH_FS */
2505                 Log("VAttachVolume: error getting bitmap for volume (%s)\n",
2506                     path);
2507                 return NULL;
2508             }
2509         }
2510     }
2511 #endif /* BITMAP_LATER */
2512
2513     if (programType == fileServer) {
2514         if (vp->specialStatus)
2515             vp->specialStatus = 0;
2516         if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
2517             V_inUse(vp) = fileServer;
2518             V_offlineMessage(vp)[0] = '\0';
2519         }
2520     } else {
2521         if ((mode != V_PEEK) && (mode != V_SECRETLY))
2522             V_inUse(vp) = programType;
2523         V_checkoutMode(vp) = mode;
2524     }
2525
2526     AddVolumeToHashTable(vp, V_id(vp));
2527 #ifdef AFS_DEMAND_ATTACH_FS
2528     if ((programType != fileServer) ||
2529         (V_inUse(vp) == fileServer)) {
2530         AddVolumeToVByPList_r(vp);
2531         VLRU_Add_r(vp);
2532         VChangeState_r(vp, VOL_STATE_ATTACHED);
2533     } else {
2534         VChangeState_r(vp, VOL_STATE_UNATTACHED);
2535     }
2536 #endif
2537     return vp;
2538 }
2539
2540 /* Attach an existing volume.
2541    The volume also normally goes online at this time.
2542    An offline volume must be reattached to make it go online.
2543  */
2544
2545 Volume *
2546 VAttachVolume(Error * ec, VolumeId volumeId, int mode)
2547 {
2548     Volume *retVal;
2549     VOL_LOCK;
2550     retVal = VAttachVolume_r(ec, volumeId, mode);
2551     VOL_UNLOCK;
2552     return retVal;
2553 }
2554
2555 Volume *
2556 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
2557 {
2558     char *part, *name;
2559     VGetVolumePath(ec, volumeId, &part, &name);
2560     if (*ec) {
2561         register Volume *vp;
2562         Error error;
2563         vp = VGetVolume_r(&error, volumeId);
2564         if (vp) {
2565             assert(V_inUse(vp) == 0);
2566             VDetachVolume_r(ec, vp);
2567         }
2568         return NULL;
2569     }
2570     return VAttachVolumeByName_r(ec, part, name, mode);
2571 }
2572
2573 /* Increment a reference count to a volume, sans context swaps.  Requires
2574  * possibly reading the volume header in from the disk, since there's
2575  * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
2576  *
2577  * N.B. This call can fail if we can't read in the header!!  In this case
2578  * we still guarantee we won't context swap, but the ref count won't be
2579  * incremented (otherwise we'd violate the invariant).
2580  */
2581 /* NOTE: with the demand attach fileserver extensions, the global lock
2582  * is dropped within VHold */
2583 #ifdef AFS_DEMAND_ATTACH_FS
2584 static int
2585 VHold_r(register Volume * vp)
2586 {
2587     Error error;
2588
2589     VCreateReservation_r(vp);
2590     VWaitExclusiveState_r(vp);
2591
2592     LoadVolumeHeader(&error, vp);
2593     if (error) {
2594         VCancelReservation_r(vp);
2595         return error;
2596     }
2597     vp->nUsers++;
2598     VCancelReservation_r(vp);
2599     return 0;
2600 }
2601 #else /* AFS_DEMAND_ATTACH_FS */
2602 static int
2603 VHold_r(register Volume * vp)
2604 {
2605     Error error;
2606
2607     LoadVolumeHeader(&error, vp);
2608     if (error)
2609         return error;
2610     vp->nUsers++;
2611     return 0;
2612 }
2613 #endif /* AFS_DEMAND_ATTACH_FS */
2614
2615 #if 0
2616 static int
2617 VHold(register Volume * vp)
2618 {
2619     int retVal;
2620     VOL_LOCK;
2621     retVal = VHold_r(vp);
2622     VOL_UNLOCK;
2623     return retVal;
2624 }
2625 #endif
2626
2627
2628 /***************************************************/
2629 /* get and put volume routines                     */
2630 /***************************************************/
2631
2632 /**
2633  * put back a heavyweight reference to a volume object.
2634  *
2635  * @param[in] vp  volume object pointer
2636  *
2637  * @pre VOL_LOCK held
2638  *
2639  * @post heavyweight volume reference put back.
2640  *       depending on state, volume may have been taken offline,
2641  *       detached, salvaged, freed, etc.
2642  *
2643  * @internal volume package internal use only
2644  */
2645 void
2646 VPutVolume_r(register Volume * vp)
2647 {
2648     assert(--vp->nUsers >= 0);
2649     if (vp->nUsers == 0) {
2650         VCheckOffline(vp);
2651         ReleaseVolumeHeader(vp->header);
2652 #ifdef AFS_DEMAND_ATTACH_FS
2653         if (!VCheckDetach(vp)) {
2654             VCheckSalvage(vp);
2655             VCheckFree(vp);
2656         }
2657 #else /* AFS_DEMAND_ATTACH_FS */
2658         VCheckDetach(vp);
2659 #endif /* AFS_DEMAND_ATTACH_FS */
2660     }
2661 }
2662
2663 void
2664 VPutVolume(register Volume * vp)
2665 {
2666     VOL_LOCK;
2667     VPutVolume_r(vp);
2668     VOL_UNLOCK;
2669 }
2670
2671
2672 /* Get a pointer to an attached volume.  The pointer is returned regardless
2673    of whether or not the volume is in service or on/off line.  An error
2674    code, however, is returned with an indication of the volume's status */
2675 Volume *
2676 VGetVolume(Error * ec, Error * client_ec, VolId volumeId)
2677 {
2678     Volume *retVal;
2679     VOL_LOCK;
2680     retVal = GetVolume(ec, client_ec, volumeId, NULL, 0);
2681     VOL_UNLOCK;
2682     return retVal;
2683 }
2684
2685 Volume *
2686 VGetVolume_r(Error * ec, VolId volumeId)
2687 {
2688     return GetVolume(ec, NULL, volumeId, NULL, 0);
2689 }
2690
2691 /* try to get a volume we've previously looked up */
2692 /* for demand attach fs, caller MUST NOT hold a ref count on vp */
2693 Volume * 
2694 VGetVolumeByVp_r(Error * ec, Volume * vp)
2695 {
2696     return GetVolume(ec, NULL, vp->hashid, vp, 0);
2697 }
2698
2699 /* private interface for getting a volume handle
2700  * volumeId must be provided.
2701  * hint is an optional parameter to speed up hash lookups
2702  * flags is not used at this time
2703  */
2704 /* for demand attach fs, caller MUST NOT hold a ref count on hint */
2705 static Volume *
2706 GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags)
2707 {
2708     Volume *vp = hint;
2709     /* pull this profiling/debugging code out of regular builds */
2710 #ifdef notdef
2711 #define VGET_CTR_INC(x) x++
2712     unsigned short V0 = 0, V1 = 0, V2 = 0, V3 = 0, V5 = 0, V6 =
2713         0, V7 = 0, V8 = 0, V9 = 0;
2714     unsigned short V10 = 0, V11 = 0, V12 = 0, V13 = 0, V14 = 0, V15 = 0;
2715 #else
2716 #define VGET_CTR_INC(x)
2717 #endif
2718 #ifdef AFS_DEMAND_ATTACH_FS
2719     Volume *avp, * rvp = hint;
2720 #endif
2721
2722     /* 
2723      * if VInit is zero, the volume package dynamic
2724      * data structures have not been initialized yet,
2725      * and we must immediately return an error
2726      */
2727     if (VInit == 0) {
2728         vp = NULL;
2729         *ec = VOFFLINE;
2730         if (client_ec) {
2731             *client_ec = VOFFLINE;
2732         }
2733         goto not_inited;
2734     }
2735
2736 #ifdef AFS_DEMAND_ATTACH_FS
2737     if (rvp) {
2738         VCreateReservation_r(rvp);
2739     }
2740 #endif /* AFS_DEMAND_ATTACH_FS */
2741
2742     for (;;) {
2743         *ec = 0;
2744         if (client_ec)
2745             *client_ec = 0;
2746         VGET_CTR_INC(V0);
2747
2748         vp = VLookupVolume_r(ec, volumeId, vp);
2749         if (*ec) {
2750             vp = NULL;
2751             break;
2752         }
2753
2754 #ifdef AFS_DEMAND_ATTACH_FS
2755         if (rvp && (rvp != vp)) {
2756             /* break reservation on old vp */
2757             VCancelReservation_r(rvp);
2758             rvp = NULL;
2759         }
2760 #endif /* AFS_DEMAND_ATTACH_FS */
2761
2762         if (!vp) {
2763             VGET_CTR_INC(V1);
2764             if (VInit < 2) {
2765                 VGET_CTR_INC(V2);
2766                 /* Until we have reached an initialization level of 2
2767                  * we don't know whether this volume exists or not.
2768                  * We can't sleep and retry later because before a volume
2769                  * is attached, the caller tries to get it first.  Just
2770                  * return VOFFLINE and the caller can choose whether to
2771                  * retry the command or not. */
2772                 *ec = VOFFLINE;
2773                 break;
2774             }
2775
2776             *ec = VNOVOL;
2777             break;
2778         }
2779
2780         VGET_CTR_INC(V3);
2781         IncUInt64(&VStats.hdr_gets);
2782         
2783 #ifdef AFS_DEMAND_ATTACH_FS
2784         /* block if someone else is performing an exclusive op on this volume */
2785         if (rvp != vp) {
2786             rvp = vp;
2787             VCreateReservation_r(rvp);
2788         }
2789         VWaitExclusiveState_r(vp);
2790
2791         /* short circuit with VNOVOL in the following circumstances:
2792          *
2793          *   - VOL_STATE_ERROR
2794          *   - VOL_STATE_SHUTTING_DOWN
2795          */
2796         if ((V_attachState(vp) == VOL_STATE_ERROR) ||
2797             (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN) ||
2798             (V_attachState(vp) == VOL_STATE_GOING_OFFLINE)) {
2799             *ec = VNOVOL;
2800             vp = NULL;
2801             break;
2802         }
2803
2804         /*
2805          * short circuit with VOFFLINE in the following circumstances:
2806          *
2807          *   - VOL_STATE_UNATTACHED
2808          */
2809        if (V_attachState(vp) == VOL_STATE_UNATTACHED) {
2810            if (vp->specialStatus) {
2811                *ec = vp->specialStatus;
2812            } else {
2813                *ec = VOFFLINE;
2814            }
2815            vp = NULL;
2816            break;
2817        }
2818
2819         /* allowable states:
2820          *   - PREATTACHED
2821          *   - ATTACHED
2822          *   - SALVAGING
2823          */
2824
2825         if (vp->salvage.requested) {
2826             VUpdateSalvagePriority_r(vp);
2827         }
2828
2829         if (V_attachState(vp) == VOL_STATE_PREATTACHED) {
2830             avp = VAttachVolumeByVp_r(ec, vp, 0);
2831             if (avp) {
2832                 if (vp != avp) {
2833                     /* VAttachVolumeByVp_r can return a pointer
2834                      * != the vp passed to it under certain
2835                      * conditions; make sure we don't leak
2836                      * reservations if that happens */
2837                     vp = avp;
2838                     VCancelReservation_r(rvp);
2839                     rvp = avp;
2840                     VCreateReservation_r(rvp);
2841                 }
2842                 VPutVolume_r(avp);
2843             }
2844             if (*ec) {
2845                 int endloop = 0;
2846                 switch (*ec) {
2847                 case VSALVAGING:
2848                     break;
2849                 case VOFFLINE:
2850                     if (!vp->pending_vol_op) {
2851                         endloop = 1;
2852                     }
2853                     break;
2854                 default:
2855                     *ec = VNOVOL;
2856                     endloop = 1;
2857                 }
2858                 if (endloop) {
2859                     vp = NULL;
2860                     break;
2861                 }
2862             }
2863         }
2864
2865         if ((V_attachState(vp) == VOL_STATE_SALVAGING) ||
2866             (*ec == VSALVAGING)) {
2867             if (client_ec) {
2868                 /* see CheckVnode() in afsfileprocs.c for an explanation
2869                  * of this error code logic */
2870                 afs_uint32 now = FT_ApproxTime();
2871                 if ((vp->stats.last_salvage + (10 * 60)) >= now) {
2872                     *client_ec = VBUSY;
2873                 } else {
2874                     *client_ec = VRESTARTING;
2875                 }
2876             }
2877             *ec = VSALVAGING;
2878             vp = NULL;
2879             break;
2880         }
2881 #endif
2882
2883         LoadVolumeHeader(ec, vp);
2884         if (*ec) {
2885             VGET_CTR_INC(V6);
2886             /* Only log the error if it was a totally unexpected error.  Simply
2887              * a missing inode is likely to be caused by the volume being deleted */
2888             if (errno != ENXIO || LogLevel)
2889                 Log("Volume %u: couldn't reread volume header\n",
2890                     vp->hashid);
2891 #ifdef AFS_DEMAND_ATTACH_FS
2892             if (programType == fileServer) {
2893                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2894             } else {
2895                 FreeVolume(vp);
2896                 vp = NULL;
2897             }
2898 #else /* AFS_DEMAND_ATTACH_FS */
2899             FreeVolume(vp);
2900             vp = NULL;
2901 #endif /* AFS_DEMAND_ATTACH_FS */
2902             break;
2903         }
2904
2905 #ifdef AFS_DEMAND_ATTACH_FS
2906         /*
2907          * this test MUST happen after the volume header is loaded
2908          */
2909         
2910          /* only valid before/during demand attachment */
2911          assert(!vp->pending_vol_op || vp->pending_vol_op != FSSYNC_VolOpRunningUnknown);
2912         
2913          /* deny getvolume due to running mutually exclusive vol op */
2914          if (vp->pending_vol_op && vp->pending_vol_op->vol_op_state==FSSYNC_VolOpRunningOffline) {
2915            /* 
2916             * volume cannot remain online during this volume operation.
2917             * notify client. 
2918             */
2919            if (vp->specialStatus) {
2920                /*
2921                 * special status codes outrank normal VOFFLINE code
2922                 */
2923                *ec = vp->specialStatus;
2924                if (client_ec) {
2925                    *client_ec = vp->specialStatus;
2926                }
2927            } else {
2928                if (client_ec) {
2929                    /* see CheckVnode() in afsfileprocs.c for an explanation
2930                     * of this error code logic */
2931                    afs_uint32 now = FT_ApproxTime();
2932                    if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
2933                        *client_ec = VBUSY;
2934                    } else {
2935                        *client_ec = VRESTARTING;
2936                    }
2937                }
2938                *ec = VOFFLINE;
2939            }
2940            VChangeState_r(vp, VOL_STATE_UNATTACHED);
2941            FreeVolumeHeader(vp);
2942            vp = NULL;
2943            break;
2944         }
2945 #endif /* AFS_DEMAND_ATTACH_FS */
2946         
2947         VGET_CTR_INC(V7);
2948         if (vp->shuttingDown) {
2949             VGET_CTR_INC(V8);
2950             *ec = VNOVOL;
2951             vp = NULL;
2952             break;
2953         }
2954
2955         if (programType == fileServer) {
2956             VGET_CTR_INC(V9);
2957             if (vp->goingOffline) {
2958                 VGET_CTR_INC(V10);
2959 #ifdef AFS_DEMAND_ATTACH_FS
2960                 /* wait for the volume to go offline */
2961                 if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
2962                     VWaitStateChange_r(vp);
2963                 }
2964 #elif defined(AFS_PTHREAD_ENV)
2965                 VOL_CV_WAIT(&vol_put_volume_cond);
2966 #else /* AFS_PTHREAD_ENV */
2967                 LWP_WaitProcess(VPutVolume);
2968 #endif /* AFS_PTHREAD_ENV */
2969                 continue;
2970             }
2971             if (vp->specialStatus) {
2972                 VGET_CTR_INC(V11);
2973                 *ec = vp->specialStatus;
2974             } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
2975                 VGET_CTR_INC(V12);
2976                 *ec = VNOVOL;
2977             } else if (V_inUse(vp) == 0) {
2978                 VGET_CTR_INC(V13);
2979                 *ec = VOFFLINE;
2980             } else {
2981                 VGET_CTR_INC(V14);
2982             }
2983         }
2984         break;
2985     }
2986     VGET_CTR_INC(V15);
2987
2988 #ifdef AFS_DEMAND_ATTACH_FS
2989     /* if no error, bump nUsers */
2990     if (vp) {
2991         vp->nUsers++;
2992         VLRU_UpdateAccess_r(vp);
2993     }
2994     if (rvp) {
2995         VCancelReservation_r(rvp);
2996         rvp = NULL;
2997     }
2998     if (client_ec && !*client_ec) {
2999         *client_ec = *ec;
3000     }
3001 #else /* AFS_DEMAND_ATTACH_FS */
3002     /* if no error, bump nUsers */
3003     if (vp) {
3004         vp->nUsers++;
3005     }
3006     if (client_ec) {
3007         *client_ec = *ec;
3008     }
3009 #endif /* AFS_DEMAND_ATTACH_FS */
3010
3011  not_inited:
3012     assert(vp || *ec);
3013     return vp;
3014 }
3015
3016
3017 /***************************************************/
3018 /* Volume offline/detach routines                  */
3019 /***************************************************/
3020
3021 /* caller MUST hold a heavyweight ref on vp */
3022 #ifdef AFS_DEMAND_ATTACH_FS
3023 void
3024 VTakeOffline_r(register Volume * vp)
3025 {
3026     Error error;
3027
3028     assert(vp->nUsers > 0);
3029     assert(programType == fileServer);
3030
3031     VCreateReservation_r(vp);
3032     VWaitExclusiveState_r(vp);
3033
3034     vp->goingOffline = 1;
3035     V_needsSalvaged(vp) = 1;
3036
3037     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
3038     VCancelReservation_r(vp);
3039 }
3040 #else /* AFS_DEMAND_ATTACH_FS */
3041 void
3042 VTakeOffline_r(register Volume * vp)
3043 {
3044     assert(vp->nUsers > 0);
3045     assert(programType == fileServer);
3046
3047     vp->goingOffline = 1;
3048     V_needsSalvaged(vp) = 1;
3049 }
3050 #endif /* AFS_DEMAND_ATTACH_FS */
3051
3052 void
3053 VTakeOffline(register Volume * vp)
3054 {
3055     VOL_LOCK;
3056     VTakeOffline_r(vp);
3057     VOL_UNLOCK;
3058 }
3059
3060 /**
3061  * force a volume offline.
3062  *
3063  * @param[in] vp     volume object pointer
3064  * @param[in] flags  flags (see note below)
3065  *
3066  * @note the flag VOL_FORCEOFF_NOUPDATE is a recursion control flag
3067  *       used when VUpdateVolume_r needs to call VForceOffline_r
3068  *       (which in turn would normally call VUpdateVolume_r)
3069  *
3070  * @see VUpdateVolume_r
3071  *
3072  * @pre VOL_LOCK must be held.
3073  *      for DAFS, caller must hold ref.
3074  *
3075  * @note for DAFS, it _is safe_ to call this function from an
3076  *       exclusive state
3077  *
3078  * @post needsSalvaged flag is set.
3079  *       for DAFS, salvage is requested.
3080  *       no further references to the volume through the volume 
3081  *       package will be honored.
3082  *       all file descriptor and vnode caches are invalidated.
3083  *
3084  * @warning this is a heavy-handed interface.  it results in
3085  *          a volume going offline regardless of the current 
3086  *          reference count state.
3087  *
3088  * @internal  volume package internal use only
3089  */
3090 void
3091 VForceOffline_r(Volume * vp, int flags)
3092 {
3093     Error error;
3094     if (!V_inUse(vp)) {
3095 #ifdef AFS_DEMAND_ATTACH_FS
3096         VChangeState_r(vp, VOL_STATE_ERROR);
3097 #endif
3098         return;
3099     }
3100
3101     strcpy(V_offlineMessage(vp),
3102            "Forced offline due to internal error: volume needs to be salvaged");
3103     Log("Volume %u forced offline:  it needs salvaging!\n", V_id(vp));
3104
3105     V_inUse(vp) = 0;
3106     vp->goingOffline = 0;
3107     V_needsSalvaged(vp) = 1;
3108     if (!(flags & VOL_FORCEOFF_NOUPDATE)) {
3109         VUpdateVolume_r(&error, vp, VOL_UPDATE_NOFORCEOFF);
3110     }
3111
3112 #ifdef AFS_DEMAND_ATTACH_FS
3113     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
3114 #endif /* AFS_DEMAND_ATTACH_FS */
3115
3116 #ifdef AFS_PTHREAD_ENV
3117     assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3118 #else /* AFS_PTHREAD_ENV */
3119     LWP_NoYieldSignal(VPutVolume);
3120 #endif /* AFS_PTHREAD_ENV */
3121
3122     VReleaseVolumeHandles_r(vp);
3123 }
3124
3125 /**
3126  * force a volume offline.
3127  *
3128  * @param[in] vp  volume object pointer
3129  *
3130  * @see VForceOffline_r
3131  */
3132 void
3133 VForceOffline(Volume * vp)
3134 {
3135     VOL_LOCK;
3136     VForceOffline_r(vp, 0);
3137     VOL_UNLOCK;
3138 }
3139
3140 /* The opposite of VAttachVolume.  The volume header is written to disk, with
3141    the inUse bit turned off.  A copy of the header is maintained in memory,
3142    however (which is why this is VOffline, not VDetach).
3143  */
3144 void
3145 VOffline_r(Volume * vp, char *message)
3146 {
3147     Error error;
3148     VolumeId vid = V_id(vp);
3149
3150     assert(programType != volumeUtility);
3151     if (!V_inUse(vp)) {
3152         VPutVolume_r(vp);
3153         return;
3154     }
3155     if (V_offlineMessage(vp)[0] == '\0')
3156         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
3157     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
3158
3159     vp->goingOffline = 1;
3160 #ifdef AFS_DEMAND_ATTACH_FS
3161     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
3162     VCreateReservation_r(vp);
3163     VPutVolume_r(vp);
3164
3165     /* wait for the volume to go offline */
3166     if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
3167         VWaitStateChange_r(vp);
3168     }
3169     VCancelReservation_r(vp);
3170 #else /* AFS_DEMAND_ATTACH_FS */
3171     VPutVolume_r(vp);
3172     vp = VGetVolume_r(&error, vid);     /* Wait for it to go offline */
3173     if (vp)                     /* In case it was reattached... */
3174         VPutVolume_r(vp);
3175 #endif /* AFS_DEMAND_ATTACH_FS */
3176 }
3177
3178 #ifdef AFS_DEMAND_ATTACH_FS
3179 /**
3180  * Take a volume offline in order to perform a volume operation.
3181  *
3182  * @param[inout] ec       address in which to store error code
3183  * @param[in]    vp       volume object pointer
3184  * @param[in]    message  volume offline status message
3185  *
3186  * @pre
3187  *    - VOL_LOCK is held
3188  *    - caller MUST hold a heavyweight ref on vp
3189  *
3190  * @post
3191  *    - volume is taken offline
3192  *    - if possible, volume operation is promoted to running state
3193  *    - on failure, *ec is set to nonzero
3194  *
3195  * @note Although this function does not return any value, it may
3196  *       still fail to promote our pending volume operation to
3197  *       a running state.  Any caller MUST check the value of *ec,
3198  *       and MUST NOT blindly assume success.
3199  *
3200  * @warning if the caller does not hold a lightweight ref on vp,
3201  *          then it MUST NOT reference vp after this function
3202  *          returns to the caller.
3203  *
3204  * @internal volume package internal use only
3205  */
3206 void
3207 VOfflineForVolOp_r(Error *ec, Volume *vp, char *message)
3208 {
3209     assert(vp->pending_vol_op);
3210     if (!V_inUse(vp)) {
3211         VPutVolume_r(vp);
3212         *ec = 1;
3213         return;
3214     }
3215     if (V_offlineMessage(vp)[0] == '\0')
3216         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
3217     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
3218
3219     vp->goingOffline = 1;
3220     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
3221     VCreateReservation_r(vp);
3222     VPutVolume_r(vp);
3223
3224     /* Wait for the volume to go offline */
3225     while (!VIsOfflineState(V_attachState(vp))) {
3226         /* do not give corrupted volumes to the volserver */
3227         if (vp->salvage.requested && vp->pending_vol_op->com.programType != salvageServer) {
3228            *ec = 1; 
3229            goto error;
3230         }
3231         VWaitStateChange_r(vp);
3232     }
3233     *ec = 0; 
3234  error:
3235     VCancelReservation_r(vp);
3236 }
3237 #endif /* AFS_DEMAND_ATTACH_FS */
3238
3239 void
3240 VOffline(Volume * vp, char *message)
3241 {
3242     VOL_LOCK;
3243     VOffline_r(vp, message);
3244     VOL_UNLOCK;
3245 }
3246
3247 /* This gets used for the most part by utility routines that don't want
3248  * to keep all the volume headers around.  Generally, the file server won't
3249  * call this routine, because then the offline message in the volume header
3250  * (or other information) won't be available to clients. For NAMEI, also
3251  * close the file handles.  However, the fileserver does call this during
3252  * an attach following a volume operation.
3253  */
3254 void
3255 VDetachVolume_r(Error * ec, Volume * vp)
3256 {
3257     VolumeId volume;
3258     struct DiskPartition64 *tpartp;
3259     int notifyServer = 0;
3260     int  useDone = FSYNC_VOL_ON;
3261
3262     *ec = 0;                    /* always "succeeds" */
3263     if (programType == volumeUtility) {
3264         notifyServer = vp->needsPutBack;
3265         if (V_destroyMe(vp) == DESTROY_ME)
3266             useDone = FSYNC_VOL_DONE;
3267 #ifdef AFS_DEMAND_ATTACH_FS
3268         else if (!V_blessed(vp) || !V_inService(vp))
3269             useDone = FSYNC_VOL_LEAVE_OFF;
3270 #endif
3271     }
3272     tpartp = vp->partition;
3273     volume = V_id(vp);
3274     DeleteVolumeFromHashTable(vp);
3275     vp->shuttingDown = 1;
3276 #ifdef AFS_DEMAND_ATTACH_FS
3277     DeleteVolumeFromVByPList_r(vp);
3278     VLRU_Delete_r(vp);
3279     VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
3280 #else
3281     if (programType != fileServer) 
3282         V_inUse(vp) = 0;
3283 #endif /* AFS_DEMAND_ATTACH_FS */
3284     VPutVolume_r(vp);
3285     /* Will be detached sometime in the future--this is OK since volume is offline */
3286
3287     /* XXX the following code should really be moved to VCheckDetach() since the volume
3288      * is not technically detached until the refcounts reach zero
3289      */
3290 #ifdef FSSYNC_BUILD_CLIENT
3291     if (programType == volumeUtility && notifyServer) {
3292         /* 
3293          * Note:  The server is not notified in the case of a bogus volume 
3294          * explicitly to make it possible to create a volume, do a partial 
3295          * restore, then abort the operation without ever putting the volume 
3296          * online.  This is essential in the case of a volume move operation 
3297          * between two partitions on the same server.  In that case, there 
3298          * would be two instances of the same volume, one of them bogus, 
3299          * which the file server would attempt to put on line 
3300          */
3301         FSYNC_VolOp(volume, tpartp->name, useDone, 0, NULL);
3302         /* XXX this code path is only hit by volume utilities, thus
3303          * V_BreakVolumeCallbacks will always be NULL.  if we really
3304          * want to break callbacks in this path we need to use FSYNC_VolOp() */
3305 #ifdef notdef
3306         /* Dettaching it so break all callbacks on it */
3307         if (V_BreakVolumeCallbacks) {
3308             Log("volume %u detached; breaking all call backs\n", volume);
3309             (*V_BreakVolumeCallbacks) (volume);
3310         }
3311 #endif
3312     }
3313 #endif /* FSSYNC_BUILD_CLIENT */
3314 }
3315
3316 void
3317 VDetachVolume(Error * ec, Volume * vp)
3318 {
3319     VOL_LOCK;
3320     VDetachVolume_r(ec, vp);
3321     VOL_UNLOCK;
3322 }
3323
3324
3325 /***************************************************/
3326 /* Volume fd/inode handle closing routines         */
3327 /***************************************************/
3328
3329 /* For VDetachVolume, we close all cached file descriptors, but keep
3330  * the Inode handles in case we need to read from a busy volume.
3331  */
3332 /* for demand attach, caller MUST hold ref count on vp */
3333 static void
3334 VCloseVolumeHandles_r(Volume * vp)
3335 {
3336 #ifdef AFS_DEMAND_ATTACH_FS
3337     VolState state_save;
3338
3339     state_save = VChangeState_r(vp, VOL_STATE_OFFLINING);
3340 #endif
3341
3342     /* demand attach fs
3343      *
3344      * XXX need to investigate whether we can perform
3345      * DFlushVolume outside of vol_glock_mutex... 
3346      *
3347      * VCloseVnodeFiles_r drops the glock internally */
3348     DFlushVolume(V_id(vp));
3349     VCloseVnodeFiles_r(vp);
3350
3351 #ifdef AFS_DEMAND_ATTACH_FS
3352     VOL_UNLOCK;
3353 #endif
3354
3355     /* Too time consuming and unnecessary for the volserver */
3356     if (programType != volumeUtility) {
3357         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3358         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3359         IH_CONDSYNC(vp->diskDataHandle);
3360 #ifdef AFS_NT40_ENV
3361         IH_CONDSYNC(vp->linkHandle);
3362 #endif /* AFS_NT40_ENV */
3363     }
3364
3365     IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
3366     IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
3367     IH_REALLYCLOSE(vp->diskDataHandle);
3368     IH_REALLYCLOSE(vp->linkHandle);
3369
3370 #ifdef AFS_DEMAND_ATTACH_FS
3371     VOL_LOCK;
3372     VChangeState_r(vp, state_save);
3373 #endif
3374 }
3375
3376 /* For both VForceOffline and VOffline, we close all relevant handles.
3377  * For VOffline, if we re-attach the volume, the files may possible be
3378  * different than before. 
3379  */
3380 /* for demand attach, caller MUST hold a ref count on vp */
3381 static void
3382 VReleaseVolumeHandles_r(Volume * vp)
3383 {
3384 #ifdef AFS_DEMAND_ATTACH_FS
3385     VolState state_save;
3386
3387     state_save = VChangeState_r(vp, VOL_STATE_DETACHING);
3388 #endif
3389
3390     /* XXX need to investigate whether we can perform
3391      * DFlushVolume outside of vol_glock_mutex... */
3392     DFlushVolume(V_id(vp));
3393
3394     VReleaseVnodeFiles_r(vp); /* releases the glock internally */
3395
3396 #ifdef AFS_DEMAND_ATTACH_FS
3397     VOL_UNLOCK;
3398 #endif
3399
3400     /* Too time consuming and unnecessary for the volserver */
3401     if (programType != volumeUtility) {
3402         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3403         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3404         IH_CONDSYNC(vp->diskDataHandle);
3405 #ifdef AFS_NT40_ENV
3406         IH_CONDSYNC(vp->linkHandle);
3407 #endif /* AFS_NT40_ENV */
3408     }
3409
3410     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
3411     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
3412     IH_RELEASE(vp->diskDataHandle);
3413     IH_RELEASE(vp->linkHandle);
3414
3415 #ifdef AFS_DEMAND_ATTACH_FS
3416     VOL_LOCK;
3417     VChangeState_r(vp, state_save);
3418 #endif
3419 }
3420
3421
3422 /***************************************************/
3423 /* Volume write and fsync routines                 */
3424 /***************************************************/
3425
3426 void
3427 VUpdateVolume_r(Error * ec, Volume * vp, int flags)
3428 {
3429 #ifdef AFS_DEMAND_ATTACH_FS
3430     VolState state_save;
3431
3432     if (flags & VOL_UPDATE_WAIT) {
3433         VCreateReservation_r(vp);
3434         VWaitExclusiveState_r(vp);
3435     }
3436 #endif
3437
3438     *ec = 0;
3439     if (programType == fileServer)
3440         V_uniquifier(vp) =
3441             (V_inUse(vp) ? V_nextVnodeUnique(vp) +
3442              200 : V_nextVnodeUnique(vp));
3443
3444 #ifdef AFS_DEMAND_ATTACH_FS
3445     state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3446     VOL_UNLOCK;
3447 #endif
3448
3449     WriteVolumeHeader_r(ec, vp);
3450
3451 #ifdef AFS_DEMAND_ATTACH_FS
3452     VOL_LOCK;
3453     VChangeState_r(vp, state_save);
3454     if (flags & VOL_UPDATE_WAIT) {
3455         VCancelReservation_r(vp);
3456     }
3457 #endif
3458
3459     if (*ec) {
3460         Log("VUpdateVolume: error updating volume header, volume %u (%s)\n",
3461             V_id(vp), V_name(vp));
3462         /* try to update on-disk header, 
3463          * while preventing infinite recursion */
3464         if (!(flags & VOL_UPDATE_NOFORCEOFF)) {
3465             VForceOffline_r(vp, VOL_FORCEOFF_NOUPDATE);
3466         }
3467     }
3468 }
3469
3470 void
3471 VUpdateVolume(Error * ec, Volume * vp)
3472 {
3473     VOL_LOCK;
3474     VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3475     VOL_UNLOCK;
3476 }
3477
3478 void
3479 VSyncVolume_r(Error * ec, Volume * vp, int flags)
3480 {
3481     FdHandle_t *fdP;
3482     int code;
3483 #ifdef AFS_DEMAND_ATTACH_FS
3484     VolState state_save;
3485 #endif
3486
3487     if (flags & VOL_SYNC_WAIT) {
3488         VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3489     } else {
3490         VUpdateVolume_r(ec, vp, 0);
3491     }
3492     if (!*ec) {
3493 #ifdef AFS_DEMAND_ATTACH_FS
3494         state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3495         VOL_UNLOCK;
3496 #endif
3497         fdP = IH_OPEN(V_diskDataHandle(vp));
3498         assert(fdP != NULL);
3499         code = FDH_SYNC(fdP);
3500         assert(code == 0);
3501         FDH_CLOSE(fdP);
3502 #ifdef AFS_DEMAND_ATTACH_FS
3503         VOL_LOCK;
3504         VChangeState_r(vp, state_save);
3505 #endif
3506     }
3507 }
3508
3509 void
3510 VSyncVolume(Error * ec, Volume * vp)
3511 {
3512     VOL_LOCK;
3513     VSyncVolume_r(ec, vp, VOL_SYNC_WAIT);
3514     VOL_UNLOCK;
3515 }
3516
3517
3518 /***************************************************/
3519 /* Volume dealloaction routines                    */
3520 /***************************************************/
3521
3522 #ifdef AFS_DEMAND_ATTACH_FS
3523 static void
3524 FreeVolume(Volume * vp)
3525 {
3526     /* free the heap space, iff it's safe.
3527      * otherwise, pull it out of the hash table, so it
3528      * will get deallocated when all refs to it go away */
3529     if (!VCheckFree(vp)) {
3530         DeleteVolumeFromHashTable(vp);
3531         DeleteVolumeFromVByPList_r(vp);
3532
3533         /* make sure we invalidate the header cache entry */
3534         FreeVolumeHeader(vp);
3535     }
3536 }
3537 #endif /* AFS_DEMAND_ATTACH_FS */
3538
3539 static void
3540 ReallyFreeVolume(Volume * vp)
3541 {
3542     int i;
3543     if (!vp)
3544         return;
3545 #ifdef AFS_DEMAND_ATTACH_FS
3546     /* debug */
3547     VChangeState_r(vp, VOL_STATE_FREED);
3548     if (vp->pending_vol_op)
3549         free(vp->pending_vol_op);
3550 #endif /* AFS_DEMAND_ATTACH_FS */
3551     for (i = 0; i < nVNODECLASSES; i++)
3552         if (vp->vnodeIndex[i].bitmap)
3553             free(vp->vnodeIndex[i].bitmap);
3554     FreeVolumeHeader(vp);
3555 #ifndef AFS_DEMAND_ATTACH_FS
3556     DeleteVolumeFromHashTable(vp);
3557 #endif /* AFS_DEMAND_ATTACH_FS */
3558     free(vp);
3559 }
3560
3561 /* check to see if we should shutdown this volume
3562  * returns 1 if volume was freed, 0 otherwise */
3563 #ifdef AFS_DEMAND_ATTACH_FS
3564 static int
3565 VCheckDetach(register Volume * vp)
3566 {
3567     int ret = 0;
3568     Error ec = 0;
3569
3570     if (vp->nUsers || vp->nWaiters)
3571         return ret;
3572
3573     if (vp->shuttingDown) {
3574         ret = 1;
3575         if ((programType != fileServer) &&
3576             (V_inUse(vp) == programType) &&
3577             ((V_checkoutMode(vp) == V_VOLUPD) ||
3578              (V_checkoutMode(vp) == V_SECRETLY) ||
3579              ((V_checkoutMode(vp) == V_CLONE) &&
3580               (VolumeWriteable(vp))))) {
3581             V_inUse(vp) = 0;
3582             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
3583             if (ec) {
3584                 Log("VCheckDetach: volume header update for volume %u "
3585                     "failed with errno %d\n", vp->hashid, errno);
3586             }
3587         }
3588         VReleaseVolumeHandles_r(vp);
3589         VCheckSalvage(vp);
3590         ReallyFreeVolume(vp);
3591         if (programType == fileServer) {
3592             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3593         }
3594     }
3595     return ret;
3596 }
3597 #else /* AFS_DEMAND_ATTACH_FS */
3598 static int
3599 VCheckDetach(register Volume * vp)
3600 {
3601     int ret = 0;
3602     Error ec = 0;
3603
3604     if (vp->nUsers)
3605         return ret;
3606
3607     if (vp->shuttingDown) {
3608         ret = 1;
3609         if ((programType != fileServer) &&
3610             (V_inUse(vp) == programType) &&
3611             ((V_checkoutMode(vp) == V_VOLUPD) ||
3612              (V_checkoutMode(vp) == V_SECRETLY) ||
3613              ((V_checkoutMode(vp) == V_CLONE) &&
3614               (VolumeWriteable(vp))))) {
3615             V_inUse(vp) = 0;
3616             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
3617             if (ec) {
3618                 Log("VCheckDetach: volume header update for volume %u failed with errno %d\n",
3619                     vp->hashid, errno);
3620             }
3621         }
3622         VReleaseVolumeHandles_r(vp);
3623         ReallyFreeVolume(vp);
3624         if (programType == fileServer) {
3625 #if defined(AFS_PTHREAD_ENV)
3626             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3627 #else /* AFS_PTHREAD_ENV */
3628             LWP_NoYieldSignal(VPutVolume);
3629 #endif /* AFS_PTHREAD_ENV */
3630         }
3631     }
3632     return ret;
3633 }
3634 #endif /* AFS_DEMAND_ATTACH_FS */
3635
3636 /* check to see if we should offline this volume
3637  * return 1 if volume went offline, 0 otherwise */
3638 #ifdef AFS_DEMAND_ATTACH_FS
3639 static int
3640 VCheckOffline(register Volume * vp)
3641 {
3642     int ret = 0;
3643
3644     if (vp->goingOffline && !vp->nUsers) {
3645         Error error;
3646         assert(programType == fileServer);
3647         assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
3648                (V_attachState(vp) != VOL_STATE_FREED) &&
3649                (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
3650                (V_attachState(vp) != VOL_STATE_UNATTACHED));
3651
3652         /* valid states:
3653          *
3654          * VOL_STATE_GOING_OFFLINE
3655          * VOL_STATE_SHUTTING_DOWN
3656          * VIsErrorState(V_attachState(vp))
3657          * VIsExclusiveState(V_attachState(vp))
3658          */
3659
3660         VCreateReservation_r(vp);
3661         VChangeState_r(vp, VOL_STATE_OFFLINING);
3662
3663         ret = 1;
3664         /* must clear the goingOffline flag before we drop the glock */
3665         vp->goingOffline = 0;
3666         V_inUse(vp) = 0;
3667
3668         VLRU_Delete_r(vp);
3669
3670         /* perform async operations */
3671         VUpdateVolume_r(&error, vp, 0);
3672         VCloseVolumeHandles_r(vp);
3673
3674         if (LogLevel) {
3675             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3676                 V_name(vp));
3677             if (V_offlineMessage(vp)[0])
3678                 Log(" (%s)", V_offlineMessage(vp));
3679             Log("\n");
3680         }
3681
3682         /* invalidate the volume header cache entry */
3683         FreeVolumeHeader(vp);
3684
3685         /* if nothing changed state to error or salvaging,
3686          * drop state to unattached */
3687         if (!VIsErrorState(V_attachState(vp))) {
3688             VChangeState_r(vp, VOL_STATE_UNATTACHED);
3689         }
3690         VCancelReservation_r(vp);
3691         /* no usage of vp is safe beyond this point */
3692     }
3693     return ret;
3694 }
3695 #else /* AFS_DEMAND_ATTACH_FS */
3696 static int
3697 VCheckOffline(register Volume * vp)
3698 {
3699     Volume * rvp = NULL;
3700     int ret = 0;
3701
3702     if (vp->goingOffline && !vp->nUsers) {
3703         Error error;
3704         assert(programType == fileServer);
3705
3706         ret = 1;
3707         vp->goingOffline = 0;
3708         V_inUse(vp) = 0;
3709         VUpdateVolume_r(&error, vp, 0);
3710         VCloseVolumeHandles_r(vp);
3711         if (LogLevel) {
3712             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3713                 V_name(vp));
3714             if (V_offlineMessage(vp)[0])
3715                 Log(" (%s)", V_offlineMessage(vp));
3716             Log("\n");
3717         }
3718         FreeVolumeHeader(vp);
3719 #ifdef AFS_PTHREAD_ENV
3720         assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3721 #else /* AFS_PTHREAD_ENV */
3722         LWP_NoYieldSignal(VPutVolume);
3723 #endif /* AFS_PTHREAD_ENV */
3724     }
3725     return ret;
3726 }
3727 #endif /* AFS_DEMAND_ATTACH_FS */
3728
3729 /***************************************************/
3730 /* demand attach fs ref counting routines          */
3731 /***************************************************/
3732
3733 #ifdef AFS_DEMAND_ATTACH_FS
3734 /* the following two functions handle reference counting for
3735  * asynchronous operations on volume structs.
3736  *
3737  * their purpose is to prevent a VDetachVolume or VShutdown
3738  * from free()ing the Volume struct during an async i/o op */
3739
3740 /* register with the async volume op ref counter */
3741 /* VCreateReservation_r moved into inline code header because it 
3742  * is now needed in vnode.c -- tkeiser 11/20/2007 
3743  */
3744
3745 /**
3746  * decrement volume-package internal refcount.
3747  *
3748  * @param vp  volume object pointer
3749  *
3750  * @internal volume package internal use only
3751  *
3752  * @pre 
3753  *    @arg VOL_LOCK is held
3754  *    @arg lightweight refcount held
3755  *
3756  * @post volume waiters refcount is decremented; volume may
3757  *       have been deallocated/shutdown/offlined/salvaged/
3758  *       whatever during the process
3759  *
3760  * @warning once you have tossed your last reference (you can acquire
3761  *          lightweight refs recursively) it is NOT SAFE to reference
3762  *          a volume object pointer ever again
3763  *
3764  * @see VCreateReservation_r
3765  *
3766  * @note DEMAND_ATTACH_FS only
3767  */
3768 void
3769 VCancelReservation_r(Volume * vp)
3770 {
3771     assert(--vp->nWaiters >= 0);
3772     if (vp->nWaiters == 0) {
3773         VCheckOffline(vp);
3774         if (!VCheckDetach(vp)) {
3775             VCheckSalvage(vp);
3776             VCheckFree(vp);
3777         }
3778     }
3779 }
3780
3781 /* check to see if we should free this volume now
3782  * return 1 if volume was freed, 0 otherwise */
3783 static int
3784 VCheckFree(Volume * vp)
3785 {
3786     int ret = 0;
3787     if ((vp->nUsers == 0) &&
3788         (vp->nWaiters == 0) &&
3789         !(V_attachFlags(vp) & (VOL_IN_HASH | 
3790                                VOL_ON_VBYP_LIST | 
3791                                VOL_IS_BUSY |
3792                                VOL_ON_VLRU))) {
3793         ReallyFreeVolume(vp);
3794         ret = 1;
3795     }
3796     return ret;
3797 }
3798 #endif /* AFS_DEMAND_ATTACH_FS */
3799
3800
3801 /***************************************************/
3802 /* online volume operations routines               */
3803 /***************************************************/
3804
3805 #ifdef AFS_DEMAND_ATTACH_FS
3806 /**
3807  * register a volume operation on a given volume.
3808  *
3809  * @param[in] vp       volume object