b271e5e061b2beac95893a650c7d3021d866a2fe
[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 #ifdef SALVSYNC_BUILD_CLIENT
356 static int VScheduleSalvage_r(Volume * vp);
357 #endif
358
359 /* Volume hash table */
360 static void VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp);
361 static void VHashBeginExclusive_r(VolumeHashChainHead * head);
362 static void VHashEndExclusive_r(VolumeHashChainHead * head);
363 static void VHashWait_r(VolumeHashChainHead * head);
364
365 /* shutdown */
366 static int ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass);
367 static int ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
368                                 struct rx_queue ** idx);
369 static void ShutdownController(vshutdown_thread_t * params);
370 static void ShutdownCreateSchedule(vshutdown_thread_t * params);
371
372 /* VLRU */
373 static void VLRU_ComputeConstants(void);
374 static void VInitVLRU(void);
375 static void VLRU_Init_Node_r(Volume * vp);
376 static void VLRU_Add_r(Volume * vp);
377 static void VLRU_Delete_r(Volume * vp);
378 static void VLRU_UpdateAccess_r(Volume * vp);
379 static void * VLRU_ScannerThread(void * args);
380 static void VLRU_Scan_r(int idx);
381 static void VLRU_Promote_r(int idx);
382 static void VLRU_Demote_r(int idx);
383 static void VLRU_SwitchQueues(Volume * vp, int new_idx, int append);
384
385 /* soft detach */
386 static int VCheckSoftDetach(Volume * vp, afs_uint32 thresh);
387 static int VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh);
388 static int VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh);
389
390
391 pthread_key_t VThread_key;
392 VThreadOptions_t VThread_defaults = {
393     0                           /**< allow salvsync */
394 };
395 #endif /* AFS_DEMAND_ATTACH_FS */
396
397
398 struct Lock vol_listLock;       /* Lock obtained when listing volumes:  
399                                  * prevents a volume from being missed 
400                                  * if the volume is attached during a 
401                                  * list volumes */
402
403
404 static int TimeZoneCorrection;  /* Number of seconds west of GMT */
405
406 /* Common message used when the volume goes off line */
407 char *VSalvageMessage =
408     "Files in this volume are currently unavailable; call operations";
409
410 int VInit;                      /* 0 - uninitialized,
411                                  * 1 - initialized but not all volumes have been attached,
412                                  * 2 - initialized and all volumes have been attached,
413                                  * 3 - initialized, all volumes have been attached, and
414                                  * VConnectFS() has completed. */
415
416
417 bit32 VolumeCacheCheck;         /* Incremented everytime a volume goes on line--
418                                  * used to stamp volume headers and in-core
419                                  * vnodes.  When the volume goes on-line the
420                                  * vnode will be invalidated
421                                  * access only with VOL_LOCK held */
422
423
424
425
426 /***************************************************/
427 /* Startup routines                                */
428 /***************************************************/
429
430 int
431 VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVnodes,
432                    int connect, afs_uint32 volcache)
433 {
434     int errors = 0;             /* Number of errors while finding vice partitions. */
435     struct timeval tv;
436     struct timezone tz;
437
438     programType = pt;
439
440     memset(&VStats, 0, sizeof(VStats));
441     VStats.hdr_cache_size = 200;
442
443     VInitPartitionPackage();
444     VInitVolumeHash();
445 #ifdef AFS_DEMAND_ATTACH_FS
446     if (programType == fileServer) {
447         VInitVLRU();
448     } else {
449         VLRU_SetOptions(VLRU_SET_ENABLED, 0);
450     }
451     assert(pthread_key_create(&VThread_key, NULL) == 0);
452 #endif
453
454 #ifdef AFS_PTHREAD_ENV
455     assert(pthread_mutex_init(&vol_glock_mutex, NULL) == 0);
456     assert(pthread_mutex_init(&vol_trans_mutex, NULL) == 0);
457     assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
458     assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
459 #else /* AFS_PTHREAD_ENV */
460     IOMGR_Initialize();
461 #endif /* AFS_PTHREAD_ENV */
462     Lock_Init(&vol_listLock);
463
464     srandom(time(0));           /* For VGetVolumeInfo */
465     gettimeofday(&tv, &tz);
466     TimeZoneCorrection = tz.tz_minuteswest * 60;
467
468 #ifdef AFS_DEMAND_ATTACH_FS
469     assert(pthread_mutex_init(&vol_salvsync_mutex, NULL) == 0);
470 #endif /* AFS_DEMAND_ATTACH_FS */
471
472     /* Ok, we have done enough initialization that fileserver can 
473      * start accepting calls, even though the volumes may not be 
474      * available just yet.
475      */
476     VInit = 1;
477
478 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_SERVER)
479     if (programType == salvageServer) {
480         SALVSYNC_salvInit();
481     }
482 #endif /* AFS_DEMAND_ATTACH_FS */
483 #ifdef FSSYNC_BUILD_SERVER
484     if (programType == fileServer) {
485         FSYNC_fsInit();
486     }
487 #endif
488 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
489     if (programType == fileServer) {
490         /* establish a connection to the salvager at this point */
491         assert(VConnectSALV() != 0);
492     }
493 #endif /* AFS_DEMAND_ATTACH_FS */
494
495     if (volcache > VStats.hdr_cache_size)
496         VStats.hdr_cache_size = volcache;
497     VInitVolumeHeaderCache(VStats.hdr_cache_size);
498
499     VInitVnodes(vLarge, nLargeVnodes);
500     VInitVnodes(vSmall, nSmallVnodes);
501
502
503     errors = VAttachPartitions();
504     if (errors)
505         return -1;
506
507     if (programType == fileServer) {
508         struct DiskPartition64 *diskP;
509 #ifdef AFS_PTHREAD_ENV
510         struct vinitvolumepackage_thread_t params;
511         struct diskpartition_queue_t * dpq;
512         int i, threads, parts;
513         pthread_t tid;
514         pthread_attr_t attrs;
515
516         assert(pthread_cond_init(&params.thread_done_cv,NULL) == 0);
517         queue_Init(&params);
518         params.n_threads_complete = 0;
519
520         /* create partition work queue */
521         for (parts=0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
522             dpq = (diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
523             assert(dpq != NULL);
524             dpq->diskP = diskP;
525             queue_Append(&params,dpq);
526         }
527
528         threads = MIN(parts, vol_attach_threads);
529
530         if (threads > 1) {
531             /* spawn off a bunch of initialization threads */
532             assert(pthread_attr_init(&attrs) == 0);
533             assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
534
535             Log("VInitVolumePackage: beginning parallel fileserver startup\n");
536 #ifdef AFS_DEMAND_ATTACH_FS
537             Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
538                 threads, parts);
539 #else /* AFS_DEMAND_ATTACH_FS */
540             Log("VInitVolumePackage: using %d threads to attach volumes on %d partitions\n",
541                 threads, parts);
542 #endif /* AFS_DEMAND_ATTACH_FS */
543
544             VOL_LOCK;
545             for (i=0; i < threads; i++) {
546                 assert(pthread_create
547                        (&tid, &attrs, &VInitVolumePackageThread,
548                         &params) == 0);
549             }
550
551             while(params.n_threads_complete < threads) {
552                 VOL_CV_WAIT(&params.thread_done_cv);
553             }
554             VOL_UNLOCK;
555
556             assert(pthread_attr_destroy(&attrs) == 0);
557         } else {
558             /* if we're only going to run one init thread, don't bother creating
559              * another LWP */
560             Log("VInitVolumePackage: beginning single-threaded fileserver startup\n");
561 #ifdef AFS_DEMAND_ATTACH_FS
562             Log("VInitVolumePackage: using 1 thread to pre-attach volumes on %d partition(s)\n",
563                 parts);
564 #else /* AFS_DEMAND_ATTACH_FS */
565             Log("VInitVolumePackage: using 1 thread to attach volumes on %d partition(s)\n",
566                 parts);
567 #endif /* AFS_DEMAND_ATTACH_FS */
568
569             VInitVolumePackageThread(&params);
570         }
571
572         assert(pthread_cond_destroy(&params.thread_done_cv) == 0);
573
574 #else /* AFS_PTHREAD_ENV */
575
576         /* Attach all the volumes in this partition */
577         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
578             int nAttached = 0, nUnattached = 0;
579             assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
580         }
581 #endif /* AFS_PTHREAD_ENV */
582     }
583
584     VInit = 2;                  /* Initialized, and all volumes have been attached */
585 #ifdef FSSYNC_BUILD_CLIENT
586     if (programType == volumeUtility && connect) {
587         if (!VConnectFS()) {
588             Log("Unable to connect to file server; will retry at need\n");
589             /*exit(1);*/
590         }
591     }
592 #ifdef AFS_DEMAND_ATTACH_FS
593     else if (programType == salvageServer) {
594         if (!VConnectFS()) {
595             Log("Unable to connect to file server; aborted\n");
596             exit(1);
597         }
598     }
599 #endif /* AFS_DEMAND_ATTACH_FS */
600 #endif /* FSSYNC_BUILD_CLIENT */
601     return 0;
602 }
603
604 #ifdef AFS_PTHREAD_ENV
605 static void *
606 VInitVolumePackageThread(void * args) {
607
608     struct DiskPartition64 *diskP;
609     struct vinitvolumepackage_thread_t * params;
610     struct diskpartition_queue_t * dpq;
611
612     params = (vinitvolumepackage_thread_t *) args;
613
614
615     VOL_LOCK;
616     /* Attach all the volumes in this partition */
617     while (queue_IsNotEmpty(params)) {
618         int nAttached = 0, nUnattached = 0;
619
620         dpq = queue_First(params,diskpartition_queue_t);
621         queue_Remove(dpq);
622         VOL_UNLOCK;
623         diskP = dpq->diskP;
624         free(dpq);
625
626         assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
627
628         VOL_LOCK;
629     }
630
631     params->n_threads_complete++;
632     pthread_cond_signal(&params->thread_done_cv);
633     VOL_UNLOCK;
634     return NULL;
635 }
636 #endif /* AFS_PTHREAD_ENV */
637
638 /*
639  * attach all volumes on a given disk partition
640  */
641 static int
642 VAttachVolumesByPartition(struct DiskPartition64 *diskP, int * nAttached, int * nUnattached)
643 {
644   DIR * dirp;
645   struct dirent * dp;
646   int ret = 0;
647
648   Log("Partition %s: attaching volumes\n", diskP->name);
649   dirp = opendir(VPartitionPath(diskP));
650   if (!dirp) {
651     Log("opendir on Partition %s failed!\n", diskP->name);
652     return 1;
653   }
654
655   while ((dp = readdir(dirp))) {
656     char *p;
657     p = strrchr(dp->d_name, '.');
658     if (p != NULL && strcmp(p, VHDREXT) == 0) {
659       Error error;
660       Volume *vp;
661 #ifdef AFS_DEMAND_ATTACH_FS
662       vp = VPreAttachVolumeByName(&error, diskP->name, dp->d_name);
663 #else /* AFS_DEMAND_ATTACH_FS */
664       vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
665                                V_VOLUPD);
666 #endif /* AFS_DEMAND_ATTACH_FS */
667       (*(vp ? nAttached : nUnattached))++;
668       if (error == VOFFLINE)
669         Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
670       else if (LogLevel >= 5) {
671         Log("Partition %s: attached volume %d (%s)\n",
672             diskP->name, VolumeNumber(dp->d_name),
673             dp->d_name);
674       }
675 #if !defined(AFS_DEMAND_ATTACH_FS)
676       if (vp) {
677         VPutVolume(vp);
678       }
679 #endif /* AFS_DEMAND_ATTACH_FS */
680     }
681   }
682
683   Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, *nAttached, *nUnattached);
684   closedir(dirp);
685   return ret;
686 }
687
688
689 /***************************************************/
690 /* Shutdown routines                               */
691 /***************************************************/
692
693 /*
694  * demand attach fs
695  * highly multithreaded volume package shutdown
696  *
697  * with the demand attach fileserver extensions,
698  * VShutdown has been modified to be multithreaded.
699  * In order to achieve optimal use of many threads,
700  * the shutdown code involves one control thread and
701  * n shutdown worker threads.  The control thread
702  * periodically examines the number of volumes available
703  * for shutdown on each partition, and produces a worker
704  * thread allocation schedule.  The idea is to eliminate
705  * redundant scheduling computation on the workers by
706  * having a single master scheduler.
707  *
708  * The scheduler's objectives are:
709  * (1) fairness
710  *   each partition with volumes remaining gets allocated
711  *   at least 1 thread (assuming sufficient threads)
712  * (2) performance
713  *   threads are allocated proportional to the number of
714  *   volumes remaining to be offlined.  This ensures that
715  *   the OS I/O scheduler has many requests to elevator
716  *   seek on partitions that will (presumably) take the
717  *   longest amount of time (from now) to finish shutdown
718  * (3) keep threads busy
719  *   when there are extra threads, they are assigned to
720  *   partitions using a simple round-robin algorithm
721  *
722  * In the future, we may wish to add the ability to adapt
723  * to the relative performance patterns of each disk
724  * partition.
725  *
726  *
727  * demand attach fs
728  * multi-step shutdown process
729  *
730  * demand attach shutdown is a four-step process. Each
731  * shutdown "pass" shuts down increasingly more difficult
732  * volumes.  The main purpose is to achieve better cache
733  * utilization during shutdown.
734  *
735  * pass 0
736  *   shutdown volumes in the unattached, pre-attached
737  *   and error states
738  * pass 1
739  *   shutdown attached volumes with cached volume headers
740  * pass 2
741  *   shutdown all volumes in non-exclusive states
742  * pass 3
743  *   shutdown all remaining volumes
744  */
745
746 #ifdef AFS_DEMAND_ATTACH_FS
747
748 void
749 VShutdown_r(void)
750 {
751     int i;
752     struct DiskPartition64 * diskP;
753     struct diskpartition_queue_t * dpq;
754     vshutdown_thread_t params;
755     pthread_t tid;
756     pthread_attr_t attrs;
757
758     memset(&params, 0, sizeof(vshutdown_thread_t));
759
760     for (params.n_parts=0, diskP = DiskPartitionList;
761          diskP; diskP = diskP->next, params.n_parts++);
762
763     Log("VShutdown:  shutting down on-line volumes on %d partition%s...\n", 
764         params.n_parts, params.n_parts > 1 ? "s" : "");
765
766     if (vol_attach_threads > 1) {
767         /* prepare for parallel shutdown */
768         params.n_threads = vol_attach_threads;
769         assert(pthread_mutex_init(&params.lock, NULL) == 0);
770         assert(pthread_cond_init(&params.cv, NULL) == 0);
771         assert(pthread_cond_init(&params.master_cv, NULL) == 0);
772         assert(pthread_attr_init(&attrs) == 0);
773         assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
774         queue_Init(&params);
775
776         /* setup the basic partition information structures for
777          * parallel shutdown */
778         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
779             /* XXX debug */
780             struct rx_queue * qp, * nqp;
781             Volume * vp;
782             int count = 0;
783
784             VVByPListWait_r(diskP);
785             VVByPListBeginExclusive_r(diskP);
786
787             /* XXX debug */
788             for (queue_Scan(&diskP->vol_list, qp, nqp, rx_queue)) {
789                 vp = (Volume *)((char *)qp - offsetof(Volume, vol_list));
790                 if (vp->header)
791                     count++;
792             }
793             Log("VShutdown: partition %s has %d volumes with attached headers\n",
794                 VPartitionPath(diskP), count);
795                 
796
797             /* build up the pass 0 shutdown work queue */
798             dpq = (struct diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
799             assert(dpq != NULL);
800             dpq->diskP = diskP;
801             queue_Prepend(&params, dpq);
802
803             params.part_pass_head[diskP->index] = queue_First(&diskP->vol_list, rx_queue);
804         }
805
806         Log("VShutdown:  beginning parallel fileserver shutdown\n");
807         Log("VShutdown:  using %d threads to offline volumes on %d partition%s\n",
808             vol_attach_threads, params.n_parts, params.n_parts > 1 ? "s" : "" );
809
810         /* do pass 0 shutdown */
811         assert(pthread_mutex_lock(&params.lock) == 0);
812         for (i=0; i < params.n_threads; i++) {
813             assert(pthread_create
814                    (&tid, &attrs, &VShutdownThread,
815                     &params) == 0);
816         }
817         
818         /* wait for all the pass 0 shutdowns to complete */
819         while (params.n_threads_complete < params.n_threads) {
820             assert(pthread_cond_wait(&params.master_cv, &params.lock) == 0);
821         }
822         params.n_threads_complete = 0;
823         params.pass = 1;
824         assert(pthread_cond_broadcast(&params.cv) == 0);
825         assert(pthread_mutex_unlock(&params.lock) == 0);
826
827         Log("VShutdown:  pass 0 completed using the 1 thread per partition algorithm\n");
828         Log("VShutdown:  starting passes 1 through 3 using finely-granular mp-fast algorithm\n");
829
830         /* run the parallel shutdown scheduler. it will drop the glock internally */
831         ShutdownController(&params);
832         
833         /* wait for all the workers to finish pass 3 and terminate */
834         while (params.pass < 4) {
835             VOL_CV_WAIT(&params.cv);
836         }
837         
838         assert(pthread_attr_destroy(&attrs) == 0);
839         assert(pthread_cond_destroy(&params.cv) == 0);
840         assert(pthread_cond_destroy(&params.master_cv) == 0);
841         assert(pthread_mutex_destroy(&params.lock) == 0);
842
843         /* drop the VByPList exclusive reservations */
844         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
845             VVByPListEndExclusive_r(diskP);
846             Log("VShutdown:  %s stats : (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
847                 VPartitionPath(diskP),
848                 params.stats[0][diskP->index],
849                 params.stats[1][diskP->index],
850                 params.stats[2][diskP->index],
851                 params.stats[3][diskP->index]);
852         }
853
854         Log("VShutdown:  shutdown finished using %d threads\n", params.n_threads);
855     } else {
856         /* if we're only going to run one shutdown thread, don't bother creating
857          * another LWP */
858         Log("VShutdown:  beginning single-threaded fileserver shutdown\n");
859
860         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
861             VShutdownByPartition_r(diskP);
862         }
863     }
864
865     Log("VShutdown:  complete.\n");
866 }
867
868 #else /* AFS_DEMAND_ATTACH_FS */
869
870 void
871 VShutdown_r(void)
872 {
873     int i;
874     register Volume *vp, *np;
875     register afs_int32 code;
876     Log("VShutdown:  shutting down on-line volumes...\n");
877     for (i = 0; i < VolumeHashTable.Size; i++) {
878         /* try to hold first volume in the hash table */
879         for (queue_Scan(&VolumeHashTable.Table[i],vp,np,Volume)) {
880             code = VHold_r(vp);
881             if (code == 0) {
882                 if (LogLevel >= 5)
883                     Log("VShutdown:  Attempting to take volume %u offline.\n",
884                         vp->hashid);
885                 
886                 /* next, take the volume offline (drops reference count) */
887                 VOffline_r(vp, "File server was shut down");
888             }
889         }
890     }
891     Log("VShutdown:  complete.\n");
892 }
893 #endif /* AFS_DEMAND_ATTACH_FS */
894
895
896 void
897 VShutdown(void)
898 {
899     VOL_LOCK;
900     VShutdown_r();
901     VOL_UNLOCK;
902 }
903
904 #ifdef AFS_DEMAND_ATTACH_FS
905 /*
906  * demand attach fs
907  * shutdown control thread
908  */
909 static void
910 ShutdownController(vshutdown_thread_t * params)
911 {
912     /* XXX debug */
913     struct DiskPartition64 * diskP;
914     Device id;
915     vshutdown_thread_t shadow;
916
917     ShutdownCreateSchedule(params);
918
919     while ((params->pass < 4) &&
920            (params->n_threads_complete < params->n_threads)) {
921         /* recompute schedule once per second */
922
923         memcpy(&shadow, params, sizeof(vshutdown_thread_t));
924
925         VOL_UNLOCK;
926         /* XXX debug */
927         Log("ShutdownController:  schedule version=%d, vol_remaining=%d, pass=%d\n",
928             shadow.schedule_version, shadow.vol_remaining, shadow.pass);
929         Log("ShutdownController:  n_threads_complete=%d, n_parts_done_pass=%d\n",
930             shadow.n_threads_complete, shadow.n_parts_done_pass);
931         for (diskP = DiskPartitionList; diskP; diskP=diskP->next) {
932             id = diskP->index;
933             Log("ShutdownController:  part[%d] : (len=%d, thread_target=%d, done_pass=%d, pass_head=%p)\n",
934                 id, 
935                 diskP->vol_list.len,
936                 shadow.part_thread_target[id], 
937                 shadow.part_done_pass[id], 
938                 shadow.part_pass_head[id]);
939         }
940
941         sleep(1);
942         VOL_LOCK;
943
944         ShutdownCreateSchedule(params);
945     }
946 }
947
948 /* create the shutdown thread work schedule.
949  * this scheduler tries to implement fairness
950  * by allocating at least 1 thread to each 
951  * partition with volumes to be shutdown,
952  * and then it attempts to allocate remaining
953  * threads based upon the amount of work left
954  */
955 static void
956 ShutdownCreateSchedule(vshutdown_thread_t * params)
957 {
958     struct DiskPartition64 * diskP;
959     int sum, thr_workload, thr_left;
960     int part_residue[VOLMAXPARTS+1];
961     Device id;
962
963     /* compute the total number of outstanding volumes */
964     sum = 0;
965     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
966         sum += diskP->vol_list.len;
967     }
968     
969     params->schedule_version++;
970     params->vol_remaining = sum;
971
972     if (!sum)
973         return;
974
975     /* compute average per-thread workload */
976     thr_workload = sum / params->n_threads;
977     if (sum % params->n_threads)
978         thr_workload++;
979
980     thr_left = params->n_threads;
981     memset(&part_residue, 0, sizeof(part_residue));
982
983     /* for fairness, give every partition with volumes remaining
984      * at least one thread */
985     for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
986         id = diskP->index;
987         if (diskP->vol_list.len) {
988             params->part_thread_target[id] = 1;
989             thr_left--;
990         } else {
991             params->part_thread_target[id] = 0;
992         }
993     }
994
995     if (thr_left && thr_workload) {
996         /* compute length-weighted workloads */
997         int delta;
998
999         for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
1000             id = diskP->index;
1001             delta = (diskP->vol_list.len / thr_workload) -
1002                 params->part_thread_target[id];
1003             if (delta < 0) {
1004                 continue;
1005             }
1006             if (delta < thr_left) {
1007                 params->part_thread_target[id] += delta;
1008                 thr_left -= delta;
1009             } else {
1010                 params->part_thread_target[id] += thr_left;
1011                 thr_left = 0;
1012                 break;
1013             }
1014         }
1015     }
1016
1017     if (thr_left) {
1018         /* try to assign any leftover threads to partitions that
1019          * had volume lengths closer to needing thread_target+1 */
1020         int max_residue, max_id = 0;
1021
1022         /* compute the residues */
1023         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1024             id = diskP->index;
1025             part_residue[id] = diskP->vol_list.len - 
1026                 (params->part_thread_target[id] * thr_workload);
1027         }
1028
1029         /* now try to allocate remaining threads to partitions with the
1030          * highest residues */
1031         while (thr_left) {
1032             max_residue = 0;
1033             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1034                 id = diskP->index;
1035                 if (part_residue[id] > max_residue) {
1036                     max_residue = part_residue[id];
1037                     max_id = id;
1038                 }
1039             }
1040
1041             if (!max_residue) {
1042                 break;
1043             }
1044
1045             params->part_thread_target[max_id]++;
1046             thr_left--;
1047             part_residue[max_id] = 0;
1048         }
1049     }
1050
1051     if (thr_left) {
1052         /* punt and give any remaining threads equally to each partition */
1053         int alloc;
1054         if (thr_left >= params->n_parts) {
1055             alloc = thr_left / params->n_parts;
1056             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1057                 id = diskP->index;
1058                 params->part_thread_target[id] += alloc;
1059                 thr_left -= alloc;
1060             }
1061         }
1062
1063         /* finish off the last of the threads */
1064         for (diskP = DiskPartitionList; thr_left && diskP; diskP = diskP->next) {
1065             id = diskP->index;
1066             params->part_thread_target[id]++;
1067             thr_left--;
1068         }
1069     }
1070 }
1071
1072 /* worker thread for parallel shutdown */
1073 static void *
1074 VShutdownThread(void * args)
1075 {
1076     vshutdown_thread_t * params;
1077     int found, pass, schedule_version_save, count;
1078     struct DiskPartition64 *diskP;
1079     struct diskpartition_queue_t * dpq;
1080     Device id;
1081
1082     params = (vshutdown_thread_t *) args;
1083
1084     /* acquire the shutdown pass 0 lock */
1085     assert(pthread_mutex_lock(&params->lock) == 0);
1086
1087     /* if there's still pass 0 work to be done,
1088      * get a work entry, and do a pass 0 shutdown */
1089     if (queue_IsNotEmpty(params)) {
1090         dpq = queue_First(params, diskpartition_queue_t);
1091         queue_Remove(dpq);
1092         assert(pthread_mutex_unlock(&params->lock) == 0);
1093         diskP = dpq->diskP;
1094         free(dpq);
1095         id = diskP->index;
1096
1097         count = 0;
1098         while (ShutdownVolumeWalk_r(diskP, 0, &params->part_pass_head[id]))
1099             count++;
1100         params->stats[0][diskP->index] = count;
1101         assert(pthread_mutex_lock(&params->lock) == 0);
1102     }
1103
1104     params->n_threads_complete++;
1105     if (params->n_threads_complete == params->n_threads) {
1106       /* notify control thread that all workers have completed pass 0 */
1107       assert(pthread_cond_signal(&params->master_cv) == 0);
1108     }
1109     while (params->pass == 0) {
1110       assert(pthread_cond_wait(&params->cv, &params->lock) == 0);
1111     }
1112
1113     /* switch locks */
1114     assert(pthread_mutex_unlock(&params->lock) == 0);
1115     VOL_LOCK;
1116
1117     pass = params->pass;
1118     assert(pass > 0);
1119
1120     /* now escalate through the more complicated shutdowns */
1121     while (pass <= 3) {
1122         schedule_version_save = params->schedule_version;
1123         found = 0;
1124         /* find a disk partition to work on */
1125         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1126             id = diskP->index;
1127             if (params->part_thread_target[id] && !params->part_done_pass[id]) {
1128                 params->part_thread_target[id]--;
1129                 found = 1;
1130                 break;
1131             }
1132         }
1133         
1134         if (!found) {
1135             /* hmm. for some reason the controller thread couldn't find anything for 
1136              * us to do. let's see if there's anything we can do */
1137             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1138                 id = diskP->index;
1139                 if (diskP->vol_list.len && !params->part_done_pass[id]) {
1140                     found = 1;
1141                     break;
1142                 } else if (!params->part_done_pass[id]) {
1143                     params->part_done_pass[id] = 1;
1144                     params->n_parts_done_pass++;
1145                     if (pass == 3) {
1146                         Log("VShutdown:  done shutting down volumes on partition %s.\n",
1147                             VPartitionPath(diskP));
1148                     }
1149                 }
1150             }
1151         }
1152         
1153         /* do work on this partition until either the controller
1154          * creates a new schedule, or we run out of things to do
1155          * on this partition */
1156         if (found) {
1157             count = 0;
1158             while (!params->part_done_pass[id] &&
1159                    (schedule_version_save == params->schedule_version)) {
1160                 /* ShutdownVolumeWalk_r will drop the glock internally */
1161                 if (!ShutdownVolumeWalk_r(diskP, pass, &params->part_pass_head[id])) {
1162                     if (!params->part_done_pass[id]) {
1163                         params->part_done_pass[id] = 1;
1164                         params->n_parts_done_pass++;
1165                         if (pass == 3) {
1166                             Log("VShutdown:  done shutting down volumes on partition %s.\n",
1167                                 VPartitionPath(diskP));
1168                         }
1169                     }
1170                     break;
1171                 }
1172                 count++;
1173             }
1174
1175             params->stats[pass][id] += count;
1176         } else {
1177             /* ok, everyone is done this pass, proceed */
1178
1179             /* barrier lock */
1180             params->n_threads_complete++;
1181             while (params->pass == pass) {
1182                 if (params->n_threads_complete == params->n_threads) {
1183                     /* we are the last thread to complete, so we will
1184                      * reinitialize worker pool state for the next pass */
1185                     params->n_threads_complete = 0;
1186                     params->n_parts_done_pass = 0;
1187                     params->pass++;
1188                     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1189                         id = diskP->index;
1190                         params->part_done_pass[id] = 0;
1191                         params->part_pass_head[id] = queue_First(&diskP->vol_list, rx_queue);
1192                     }
1193
1194                     /* compute a new thread schedule before releasing all the workers */
1195                     ShutdownCreateSchedule(params);
1196
1197                     /* wake up all the workers */
1198                     assert(pthread_cond_broadcast(&params->cv) == 0);
1199
1200                     VOL_UNLOCK;
1201                     Log("VShutdown:  pass %d completed using %d threads on %d partitions\n",
1202                         pass, params->n_threads, params->n_parts);
1203                     VOL_LOCK;
1204                 } else {
1205                     VOL_CV_WAIT(&params->cv);
1206                 }
1207             }
1208             pass = params->pass;
1209         }
1210         
1211         /* for fairness */
1212         VOL_UNLOCK;
1213         pthread_yield();
1214         VOL_LOCK;
1215     }
1216
1217     VOL_UNLOCK;
1218
1219     return NULL;
1220 }
1221
1222 /* shut down all volumes on a given disk partition 
1223  *
1224  * note that this function will not allow mp-fast
1225  * shutdown of a partition */
1226 int
1227 VShutdownByPartition_r(struct DiskPartition64 * dp)
1228 {
1229     int pass;
1230     int pass_stats[4];
1231     int total;
1232
1233     /* wait for other exclusive ops to finish */
1234     VVByPListWait_r(dp);
1235
1236     /* begin exclusive access */
1237     VVByPListBeginExclusive_r(dp);
1238
1239     /* pick the low-hanging fruit first,
1240      * then do the complicated ones last 
1241      * (has the advantage of keeping
1242      *  in-use volumes up until the bitter end) */
1243     for (pass = 0, total=0; pass < 4; pass++) {
1244         pass_stats[pass] = ShutdownVByPForPass_r(dp, pass);
1245         total += pass_stats[pass];
1246     }
1247
1248     /* end exclusive access */
1249     VVByPListEndExclusive_r(dp);
1250
1251     Log("VShutdownByPartition:  shut down %d volumes on %s (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
1252         total, VPartitionPath(dp), pass_stats[0], pass_stats[1], pass_stats[2], pass_stats[3]);
1253
1254     return 0;
1255 }
1256
1257 /* internal shutdown functionality
1258  *
1259  * for multi-pass shutdown:
1260  * 0 to only "shutdown" {pre,un}attached and error state volumes
1261  * 1 to also shutdown attached volumes w/ volume header loaded
1262  * 2 to also shutdown attached volumes w/o volume header loaded
1263  * 3 to also shutdown exclusive state volumes 
1264  *
1265  * caller MUST hold exclusive access on the hash chain
1266  * because we drop vol_glock_mutex internally
1267  * 
1268  * this function is reentrant for passes 1--3 
1269  * (e.g. multiple threads can cooperate to 
1270  *  shutdown a partition mp-fast)
1271  *
1272  * pass 0 is not scaleable because the volume state data is
1273  * synchronized by vol_glock mutex, and the locking overhead
1274  * is too high to drop the lock long enough to do linked list
1275  * traversal
1276  */
1277 static int
1278 ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass)
1279 {
1280     struct rx_queue * q = queue_First(&dp->vol_list, rx_queue);
1281     register int i = 0;
1282
1283     while (ShutdownVolumeWalk_r(dp, pass, &q))
1284         i++;
1285
1286     return i;
1287 }
1288
1289 /* conditionally shutdown one volume on partition dp
1290  * returns 1 if a volume was shutdown in this pass,
1291  * 0 otherwise */
1292 static int
1293 ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
1294                      struct rx_queue ** idx)
1295 {
1296     struct rx_queue *qp, *nqp;
1297     Volume * vp;
1298
1299     qp = *idx;
1300
1301     for (queue_ScanFrom(&dp->vol_list, qp, qp, nqp, rx_queue)) {
1302         vp = (Volume *) (((char *)qp) - offsetof(Volume, vol_list));
1303         
1304         switch (pass) {
1305         case 0:
1306             if ((V_attachState(vp) != VOL_STATE_UNATTACHED) &&
1307                 (V_attachState(vp) != VOL_STATE_ERROR) &&
1308                 (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1309                 break;
1310             }
1311         case 1:
1312             if ((V_attachState(vp) == VOL_STATE_ATTACHED) &&
1313                 (vp->header == NULL)) {
1314                 break;
1315             }
1316         case 2:
1317             if (VIsExclusiveState(V_attachState(vp))) {
1318                 break;
1319             }
1320         case 3:
1321             *idx = nqp;
1322             DeleteVolumeFromVByPList_r(vp);
1323             VShutdownVolume_r(vp);
1324             vp = NULL;
1325             return 1;
1326         }
1327     }
1328
1329     return 0;
1330 }
1331
1332 /*
1333  * shutdown a specific volume
1334  */
1335 /* caller MUST NOT hold a heavyweight ref on vp */
1336 int
1337 VShutdownVolume_r(Volume * vp)
1338 {
1339     int code;
1340
1341     VCreateReservation_r(vp);
1342
1343     if (LogLevel >= 5) {
1344         Log("VShutdownVolume_r:  vid=%u, device=%d, state=%hu\n",
1345             vp->hashid, vp->partition->device, V_attachState(vp));
1346     }
1347
1348     /* wait for other blocking ops to finish */
1349     VWaitExclusiveState_r(vp);
1350
1351     assert(VIsValidState(V_attachState(vp)));
1352     
1353     switch(V_attachState(vp)) {
1354     case VOL_STATE_SALVAGING:
1355         /* Leave salvaging volumes alone. Any in-progress salvages will
1356          * continue working after viced shuts down. This is intentional.
1357          */
1358
1359     case VOL_STATE_PREATTACHED:
1360     case VOL_STATE_ERROR:
1361         VChangeState_r(vp, VOL_STATE_UNATTACHED);
1362     case VOL_STATE_UNATTACHED:
1363         break;
1364     case VOL_STATE_GOING_OFFLINE:
1365     case VOL_STATE_SHUTTING_DOWN:
1366     case VOL_STATE_ATTACHED:
1367         code = VHold_r(vp);
1368         if (!code) {
1369             if (LogLevel >= 5)
1370                 Log("VShutdown:  Attempting to take volume %u offline.\n",
1371                     vp->hashid);
1372
1373             /* take the volume offline (drops reference count) */
1374             VOffline_r(vp, "File server was shut down");
1375         }
1376         break;
1377     default:
1378         break;
1379     }
1380     
1381     VCancelReservation_r(vp);
1382     vp = NULL;
1383     return 0;
1384 }
1385 #endif /* AFS_DEMAND_ATTACH_FS */
1386
1387
1388 /***************************************************/
1389 /* Header I/O routines                             */
1390 /***************************************************/
1391
1392 /* open a descriptor for the inode (h),
1393  * read in an on-disk structure into buffer (to) of size (size),
1394  * verify versionstamp in structure has magic (magic) and
1395  * optionally verify version (version) if (version) is nonzero
1396  */
1397 static void
1398 ReadHeader(Error * ec, IHandle_t * h, char *to, int size, bit32 magic,
1399            bit32 version)
1400 {
1401     struct versionStamp *vsn;
1402     FdHandle_t *fdP;
1403
1404     *ec = 0;
1405     if (h == NULL) {
1406         *ec = VSALVAGE;
1407         return;
1408     }
1409
1410     fdP = IH_OPEN(h);
1411     if (fdP == NULL) {
1412         *ec = VSALVAGE;
1413         return;
1414     }
1415
1416     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1417         *ec = VSALVAGE;
1418         FDH_REALLYCLOSE(fdP);
1419         return;
1420     }
1421     vsn = (struct versionStamp *)to;
1422     if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
1423         *ec = VSALVAGE;
1424         FDH_REALLYCLOSE(fdP);
1425         return;
1426     }
1427     FDH_CLOSE(fdP);
1428
1429     /* Check is conditional, in case caller wants to inspect version himself */
1430     if (version && vsn->version != version) {
1431         *ec = VSALVAGE;
1432     }
1433 }
1434
1435 void
1436 WriteVolumeHeader_r(Error * ec, Volume * vp)
1437 {
1438     IHandle_t *h = V_diskDataHandle(vp);
1439     FdHandle_t *fdP;
1440
1441     *ec = 0;
1442
1443     fdP = IH_OPEN(h);
1444     if (fdP == NULL) {
1445         *ec = VSALVAGE;
1446         return;
1447     }
1448     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1449         *ec = VSALVAGE;
1450         FDH_REALLYCLOSE(fdP);
1451         return;
1452     }
1453     if (FDH_WRITE(fdP, (char *)&V_disk(vp), sizeof(V_disk(vp)))
1454         != sizeof(V_disk(vp))) {
1455         *ec = VSALVAGE;
1456         FDH_REALLYCLOSE(fdP);
1457         return;
1458     }
1459     FDH_CLOSE(fdP);
1460 }
1461
1462 /* VolumeHeaderToDisk
1463  * Allows for storing 64 bit inode numbers in on-disk volume header
1464  * file.
1465  */
1466 /* convert in-memory representation of a volume header to the
1467  * on-disk representation of a volume header */
1468 void
1469 VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
1470 {
1471
1472     memset(dh, 0, sizeof(VolumeDiskHeader_t));
1473     dh->stamp = h->stamp;
1474     dh->id = h->id;
1475     dh->parent = h->parent;
1476
1477 #ifdef AFS_64BIT_IOPS_ENV
1478     dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
1479     dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
1480     dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
1481     dh->smallVnodeIndex_hi =
1482         (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
1483     dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
1484     dh->largeVnodeIndex_hi =
1485         (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
1486     dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
1487     dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
1488 #else
1489     dh->volumeInfo_lo = h->volumeInfo;
1490     dh->smallVnodeIndex_lo = h->smallVnodeIndex;
1491     dh->largeVnodeIndex_lo = h->largeVnodeIndex;
1492     dh->linkTable_lo = h->linkTable;
1493 #endif
1494 }
1495
1496 /* DiskToVolumeHeader
1497  * Converts an on-disk representation of a volume header to
1498  * the in-memory representation of a volume header.
1499  *
1500  * Makes the assumption that AFS has *always* 
1501  * zero'd the volume header file so that high parts of inode
1502  * numbers are 0 in older (SGI EFS) volume header files.
1503  */
1504 void
1505 DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
1506 {
1507     memset(h, 0, sizeof(VolumeHeader_t));
1508     h->stamp = dh->stamp;
1509     h->id = dh->id;
1510     h->parent = dh->parent;
1511
1512 #ifdef AFS_64BIT_IOPS_ENV
1513     h->volumeInfo =
1514         (Inode) dh->volumeInfo_lo | ((Inode) dh->volumeInfo_hi << 32);
1515
1516     h->smallVnodeIndex =
1517         (Inode) dh->smallVnodeIndex_lo | ((Inode) dh->
1518                                           smallVnodeIndex_hi << 32);
1519
1520     h->largeVnodeIndex =
1521         (Inode) dh->largeVnodeIndex_lo | ((Inode) dh->
1522                                           largeVnodeIndex_hi << 32);
1523     h->linkTable =
1524         (Inode) dh->linkTable_lo | ((Inode) dh->linkTable_hi << 32);
1525 #else
1526     h->volumeInfo = dh->volumeInfo_lo;
1527     h->smallVnodeIndex = dh->smallVnodeIndex_lo;
1528     h->largeVnodeIndex = dh->largeVnodeIndex_lo;
1529     h->linkTable = dh->linkTable_lo;
1530 #endif
1531 }
1532
1533
1534 /***************************************************/
1535 /* Volume Attachment routines                      */
1536 /***************************************************/
1537
1538 #ifdef AFS_DEMAND_ATTACH_FS
1539 /**
1540  * pre-attach a volume given its path.
1541  *
1542  * @param[out] ec         outbound error code
1543  * @param[in]  partition  partition path string
1544  * @param[in]  name       volume id string
1545  *
1546  * @return volume object pointer
1547  *
1548  * @note A pre-attached volume will only have its partition
1549  *       and hashid fields initialized.  At first call to 
1550  *       VGetVolume, the volume will be fully attached.
1551  *
1552  */
1553 Volume *
1554 VPreAttachVolumeByName(Error * ec, char *partition, char *name)
1555 {
1556     Volume * vp;
1557     VOL_LOCK;
1558     vp = VPreAttachVolumeByName_r(ec, partition, name);
1559     VOL_UNLOCK;
1560     return vp;
1561 }
1562
1563 /**
1564  * pre-attach a volume given its path.
1565  *
1566  * @param[out] ec         outbound error code
1567  * @param[in]  partition  path to vice partition
1568  * @param[in]  name       volume id string
1569  *
1570  * @return volume object pointer
1571  *
1572  * @pre VOL_LOCK held
1573  *
1574  * @internal volume package internal use only.
1575  */
1576 Volume *
1577 VPreAttachVolumeByName_r(Error * ec, char *partition, char *name)
1578 {
1579     return VPreAttachVolumeById_r(ec, 
1580                                   partition,
1581                                   VolumeNumber(name));
1582 }
1583
1584 /**
1585  * pre-attach a volume given its path and numeric volume id.
1586  *
1587  * @param[out] ec          error code return
1588  * @param[in]  partition   path to vice partition
1589  * @param[in]  volumeId    numeric volume id
1590  *
1591  * @return volume object pointer
1592  *
1593  * @pre VOL_LOCK held
1594  *
1595  * @internal volume package internal use only.
1596  */
1597 Volume *
1598 VPreAttachVolumeById_r(Error * ec, 
1599                        char * partition,
1600                        VolId volumeId)
1601 {
1602     Volume *vp;
1603     struct DiskPartition64 *partp;
1604
1605     *ec = 0;
1606
1607     assert(programType == fileServer);
1608
1609     if (!(partp = VGetPartition_r(partition, 0))) {
1610         *ec = VNOVOL;
1611         Log("VPreAttachVolumeById_r:  Error getting partition (%s)\n", partition);
1612         return NULL;
1613     }
1614
1615     vp = VLookupVolume_r(ec, volumeId, NULL);
1616     if (*ec) {
1617         return NULL;
1618     }
1619
1620     return VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
1621 }
1622
1623 /**
1624  * preattach a volume.
1625  *
1626  * @param[out] ec     outbound error code
1627  * @param[in]  partp  pointer to partition object
1628  * @param[in]  vp     pointer to volume object
1629  * @param[in]  vid    volume id
1630  *
1631  * @return volume object pointer
1632  *
1633  * @pre VOL_LOCK is held.
1634  *
1635  * @warning Returned volume object pointer does not have to
1636  *          equal the pointer passed in as argument vp.  There
1637  *          are potential race conditions which can result in
1638  *          the pointers having different values.  It is up to
1639  *          the caller to make sure that references are handled
1640  *          properly in this case.
1641  *
1642  * @note If there is already a volume object registered with
1643  *       the same volume id, its pointer MUST be passed as 
1644  *       argument vp.  Failure to do so will result in a silent
1645  *       failure to preattach.
1646  *
1647  * @internal volume package internal use only.
1648  */
1649 Volume * 
1650 VPreAttachVolumeByVp_r(Error * ec, 
1651                        struct DiskPartition64 * partp, 
1652                        Volume * vp,
1653                        VolId vid)
1654 {
1655     Volume *nvp = NULL;
1656
1657     *ec = 0;
1658
1659     /* check to see if pre-attach already happened */
1660     if (vp && 
1661         (V_attachState(vp) != VOL_STATE_UNATTACHED) && 
1662         (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
1663         !VIsErrorState(V_attachState(vp))) {
1664         /*
1665          * pre-attach is a no-op in all but the following cases:
1666          *
1667          *   - volume is unattached
1668          *   - volume is in an error state
1669          *   - volume is pre-attached
1670          */
1671         Log("VPreattachVolumeByVp_r: volume %u not in quiescent state\n", vid);
1672         goto done;
1673     } else if (vp) {
1674         /* we're re-attaching a volume; clear out some old state */
1675         memset(&vp->salvage, 0, sizeof(struct VolumeOnlineSalvage));
1676
1677         if (V_partition(vp) != partp) {
1678             /* XXX potential race */
1679             DeleteVolumeFromVByPList_r(vp);
1680         }
1681     } else {
1682         /* if we need to allocate a new Volume struct,
1683          * go ahead and drop the vol glock, otherwise
1684          * do the basic setup synchronised, as it's
1685          * probably not worth dropping the lock */
1686         VOL_UNLOCK;
1687
1688         /* allocate the volume structure */
1689         vp = nvp = (Volume *) malloc(sizeof(Volume));
1690         assert(vp != NULL);
1691         memset(vp, 0, sizeof(Volume));
1692         queue_Init(&vp->vnode_list);
1693         assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1694     }
1695
1696     /* link the volume with its associated vice partition */
1697     vp->device = partp->device;
1698     vp->partition = partp;
1699
1700     vp->hashid = vid;
1701     vp->specialStatus = 0;
1702
1703     /* if we dropped the lock, reacquire the lock,
1704      * check for pre-attach races, and then add
1705      * the volume to the hash table */
1706     if (nvp) {
1707         VOL_LOCK;
1708         nvp = VLookupVolume_r(ec, vid, NULL);
1709         if (*ec) {
1710             free(vp);
1711             vp = NULL;
1712             goto done;
1713         } else if (nvp) { /* race detected */
1714             free(vp);
1715             vp = nvp;
1716             goto done;
1717         } else {
1718           /* hack to make up for VChangeState_r() decrementing 
1719            * the old state counter */
1720           VStats.state_levels[0]++;
1721         }
1722     }
1723
1724     /* put pre-attached volume onto the hash table
1725      * and bring it up to the pre-attached state */
1726     AddVolumeToHashTable(vp, vp->hashid);
1727     AddVolumeToVByPList_r(vp);
1728     VLRU_Init_Node_r(vp);
1729     VChangeState_r(vp, VOL_STATE_PREATTACHED);
1730
1731     if (LogLevel >= 5)
1732         Log("VPreAttachVolumeByVp_r:  volume %u pre-attached\n", vp->hashid);
1733
1734   done:
1735     if (*ec)
1736         return NULL;
1737     else
1738         return vp;
1739 }
1740 #endif /* AFS_DEMAND_ATTACH_FS */
1741
1742 /* Attach an existing volume, given its pathname, and return a
1743    pointer to the volume header information.  The volume also
1744    normally goes online at this time.  An offline volume
1745    must be reattached to make it go online */
1746 Volume *
1747 VAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
1748 {
1749     Volume *retVal;
1750     VOL_LOCK;
1751     retVal = VAttachVolumeByName_r(ec, partition, name, mode);
1752     VOL_UNLOCK;
1753     return retVal;
1754 }
1755
1756 Volume *
1757 VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
1758 {
1759     register Volume *vp = NULL;
1760     int fd, n;
1761     struct afs_stat status;
1762     struct VolumeDiskHeader diskHeader;
1763     struct VolumeHeader iheader;
1764     struct DiskPartition64 *partp;
1765     char path[64];
1766     int isbusy = 0;
1767     VolId volumeId;
1768 #ifdef AFS_DEMAND_ATTACH_FS
1769     VolumeStats stats_save;
1770     Volume *svp = NULL;
1771 #endif /* AFS_DEMAND_ATTACH_FS */
1772
1773     *ec = 0;
1774    
1775     volumeId = VolumeNumber(name);
1776
1777     if (!(partp = VGetPartition_r(partition, 0))) {
1778         *ec = VNOVOL;
1779         Log("VAttachVolume: Error getting partition (%s)\n", partition);
1780         goto done;
1781     }
1782
1783     if (programType == volumeUtility) {
1784         assert(VInit == 3);
1785         VLockPartition_r(partition);
1786     } else if (programType == fileServer) {
1787 #ifdef AFS_DEMAND_ATTACH_FS
1788         /* lookup the volume in the hash table */
1789         vp = VLookupVolume_r(ec, volumeId, NULL);
1790         if (*ec) {
1791             return NULL;
1792         }
1793
1794         if (vp) {
1795             /* save any counters that are supposed to
1796              * be monotonically increasing over the
1797              * lifetime of the fileserver */
1798             memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
1799         } else {
1800             memset(&stats_save, 0, sizeof(VolumeStats));
1801         }
1802
1803         /* if there's something in the hash table, and it's not
1804          * in the pre-attach state, then we may need to detach
1805          * it before proceeding */
1806         if (vp && (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1807             VCreateReservation_r(vp);
1808             VWaitExclusiveState_r(vp);
1809
1810             /* at this point state must be one of:
1811              *   - UNATTACHED
1812              *   - ATTACHED
1813              *   - SHUTTING_DOWN
1814              *   - GOING_OFFLINE
1815              *   - SALVAGING
1816              *   - ERROR
1817              */
1818
1819             if (vp->specialStatus == VBUSY)
1820                 isbusy = 1;
1821             
1822             /* if it's already attached, see if we can return it */
1823             if (V_attachState(vp) == VOL_STATE_ATTACHED) {
1824                 VGetVolumeByVp_r(ec, vp);
1825                 if (V_inUse(vp) == fileServer) {
1826                     VCancelReservation_r(vp);
1827                     return vp;
1828                 }
1829
1830                 /* otherwise, we need to detach, and attempt to re-attach */
1831                 VDetachVolume_r(ec, vp);
1832                 if (*ec) {
1833                     Log("VAttachVolume: Error detaching old volume instance (%s)\n", name);
1834                 }
1835             } else {
1836                 /* if it isn't fully attached, delete from the hash tables,
1837                    and let the refcounter handle the rest */
1838                 DeleteVolumeFromHashTable(vp);
1839                 DeleteVolumeFromVByPList_r(vp);
1840             }
1841
1842             VCancelReservation_r(vp);
1843             vp = NULL;
1844         }
1845
1846         /* pre-attach volume if it hasn't been done yet */
1847         if (!vp || 
1848             (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
1849             (V_attachState(vp) == VOL_STATE_ERROR)) {
1850             svp = vp;
1851             vp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
1852             if (*ec) {
1853                 return NULL;
1854             }
1855         }
1856
1857         assert(vp != NULL);
1858
1859         /* handle pre-attach races 
1860          *
1861          * multiple threads can race to pre-attach a volume,
1862          * but we can't let them race beyond that
1863          * 
1864          * our solution is to let the first thread to bring
1865          * the volume into an exclusive state win; the other
1866          * threads just wait until it finishes bringing the
1867          * volume online, and then they do a vgetvolumebyvp
1868          */
1869         if (svp && (svp != vp)) {
1870             /* wait for other exclusive ops to finish */
1871             VCreateReservation_r(vp);
1872             VWaitExclusiveState_r(vp);
1873
1874             /* get a heavyweight ref, kill the lightweight ref, and return */
1875             VGetVolumeByVp_r(ec, vp);
1876             VCancelReservation_r(vp);
1877             return vp;
1878         }
1879
1880         /* at this point, we are chosen as the thread to do
1881          * demand attachment for this volume. all other threads
1882          * doing a getvolume on vp->hashid will block until we finish */
1883
1884         /* make sure any old header cache entries are invalidated
1885          * before proceeding */
1886         FreeVolumeHeader(vp);
1887
1888         VChangeState_r(vp, VOL_STATE_ATTACHING);
1889
1890         /* restore any saved counters */
1891         memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
1892 #else /* AFS_DEMAND_ATTACH_FS */
1893         vp = VGetVolume_r(ec, volumeId);
1894         if (vp) {
1895             if (V_inUse(vp) == fileServer)
1896                 return vp;
1897             if (vp->specialStatus == VBUSY)
1898                 isbusy = 1;
1899             VDetachVolume_r(ec, vp);
1900             if (*ec) {
1901                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
1902             }
1903             vp = NULL;
1904         }
1905 #endif /* AFS_DEMAND_ATTACH_FS */
1906     }
1907
1908     *ec = 0;
1909     strcpy(path, VPartitionPath(partp));
1910
1911     VOL_UNLOCK;
1912
1913     strcat(path, "/");
1914     strcat(path, name);
1915     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
1916         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
1917         if (fd > -1)
1918             close(fd);
1919         *ec = VNOVOL;
1920         VOL_LOCK;
1921         goto done;
1922     }
1923     n = read(fd, &diskHeader, sizeof(diskHeader));
1924     close(fd);
1925     if (n != sizeof(diskHeader)
1926         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1927         Log("VAttachVolume: Error reading volume header %s\n", path);
1928         *ec = VSALVAGE;
1929         VOL_LOCK;
1930         goto done;
1931     }
1932     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
1933         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
1934         *ec = VSALVAGE;
1935         VOL_LOCK;
1936         goto done;
1937     }
1938
1939     DiskToVolumeHeader(&iheader, &diskHeader);
1940 #ifdef FSSYNC_BUILD_CLIENT
1941     if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
1942         VOL_LOCK;
1943         if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, NULL)
1944             != SYNC_OK) {
1945             Log("VAttachVolume: attach of volume %u apparently denied by file server\n", iheader.id);
1946             *ec = VNOVOL;       /* XXXX */
1947             goto done;
1948         }
1949         VOL_UNLOCK;
1950     }
1951 #endif
1952
1953     if (!vp) {
1954       vp = (Volume *) calloc(1, sizeof(Volume));
1955       assert(vp != NULL);
1956       vp->device = partp->device;
1957       vp->partition = partp;
1958       queue_Init(&vp->vnode_list);
1959 #ifdef AFS_DEMAND_ATTACH_FS
1960       assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1961 #endif /* AFS_DEMAND_ATTACH_FS */
1962     }
1963
1964     /* attach2 is entered without any locks, and returns
1965      * with vol_glock_mutex held */
1966     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
1967
1968     if (programType == volumeUtility && vp) {
1969         if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
1970             /* mark volume header as in use so that volser crashes lead to a
1971              * salvage attempt */
1972             VUpdateVolume_r(ec, vp, 0);
1973         }
1974 #ifdef AFS_DEMAND_ATTACH_FS
1975         /* for dafs, we should tell the fileserver, except for V_PEEK
1976          * where we know it is not necessary */
1977         if (mode == V_PEEK) {
1978             vp->needsPutBack = 0;
1979         } else {
1980             vp->needsPutBack = 1;
1981         }
1982 #else /* !AFS_DEMAND_ATTACH_FS */
1983         /* duplicate computation in fssync.c about whether the server
1984          * takes the volume offline or not.  If the volume isn't
1985          * offline, we must not return it when we detach the volume,
1986          * or the server will abort */
1987         if (mode == V_READONLY || mode == V_PEEK
1988             || (!VolumeWriteable(vp) && (mode == V_CLONE || mode == V_DUMP)))
1989             vp->needsPutBack = 0;
1990         else
1991             vp->needsPutBack = 1;
1992 #endif /* !AFS_DEMAND_ATTACH_FS */
1993     }
1994     /* OK, there's a problem here, but one that I don't know how to
1995      * fix right now, and that I don't think should arise often.
1996      * Basically, we should only put back this volume to the server if
1997      * it was given to us by the server, but since we don't have a vp,
1998      * we can't run the VolumeWriteable function to find out as we do
1999      * above when computing vp->needsPutBack.  So we send it back, but
2000      * there's a path in VAttachVolume on the server which may abort
2001      * if this volume doesn't have a header.  Should be pretty rare
2002      * for all of that to happen, but if it does, probably the right
2003      * fix is for the server to allow the return of readonly volumes
2004      * that it doesn't think are really checked out. */
2005 #ifdef FSSYNC_BUILD_CLIENT
2006     if (programType == volumeUtility && vp == NULL &&
2007         mode != V_SECRETLY && mode != V_PEEK) {
2008         FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_ON, 0, NULL);
2009     } else 
2010 #endif
2011     if (programType == fileServer && vp) {
2012 #ifdef AFS_DEMAND_ATTACH_FS
2013         /* 
2014          * we can get here in cases where we don't "own"
2015          * the volume (e.g. volume owned by a utility).
2016          * short circuit around potential disk header races.
2017          */
2018         if (V_attachState(vp) != VOL_STATE_ATTACHED) {
2019             goto done;
2020         }
2021 #endif
2022         V_needsCallback(vp) = 0;
2023 #ifdef  notdef
2024         if (VInit >= 2 && V_BreakVolumeCallbacks) {
2025             Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
2026             (*V_BreakVolumeCallbacks) (V_id(vp));
2027         }
2028 #endif
2029         VUpdateVolume_r(ec, vp, 0);
2030         if (*ec) {
2031             Log("VAttachVolume: Error updating volume\n");
2032             if (vp)
2033                 VPutVolume_r(vp);
2034             goto done;
2035         }
2036         if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2037 #ifndef AFS_DEMAND_ATTACH_FS
2038             /* This is a hack: by temporarily setting the incore
2039              * dontSalvage flag ON, the volume will be put back on the
2040              * Update list (with dontSalvage OFF again).  It will then
2041              * come back in N minutes with DONT_SALVAGE eventually
2042              * set.  This is the way that volumes that have never had
2043              * it set get it set; or that volumes that have been
2044              * offline without DONT SALVAGE having been set also
2045              * eventually get it set */
2046             V_dontSalvage(vp) = DONT_SALVAGE;
2047 #endif /* !AFS_DEMAND_ATTACH_FS */
2048             VAddToVolumeUpdateList_r(ec, vp);
2049             if (*ec) {
2050                 Log("VAttachVolume: Error adding volume to update list\n");
2051                 if (vp)
2052                     VPutVolume_r(vp);
2053                 goto done;
2054             }
2055         }
2056         if (LogLevel)
2057             Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2058                 V_name(vp));
2059     }
2060
2061   done:
2062     if (programType == volumeUtility) {
2063         VUnlockPartition_r(partition);
2064     }
2065     if (*ec) {
2066 #ifdef AFS_DEMAND_ATTACH_FS
2067         /* attach failed; make sure we're in error state */
2068         if (vp && !VIsErrorState(V_attachState(vp))) {
2069             VChangeState_r(vp, VOL_STATE_ERROR);
2070         }
2071 #endif /* AFS_DEMAND_ATTACH_FS */
2072         return NULL;
2073     } else {
2074         return vp;
2075     }
2076 }
2077
2078 #ifdef AFS_DEMAND_ATTACH_FS
2079 /* VAttachVolumeByVp_r
2080  *
2081  * finish attaching a volume that is
2082  * in a less than fully attached state
2083  */
2084 /* caller MUST hold a ref count on vp */
2085 static Volume *
2086 VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
2087 {
2088     char name[VMAXPATHLEN];
2089     int fd, n, reserve = 0;
2090     struct afs_stat status;
2091     struct VolumeDiskHeader diskHeader;
2092     struct VolumeHeader iheader;
2093     struct DiskPartition64 *partp;
2094     char path[64];
2095     int isbusy = 0;
2096     VolId volumeId;
2097     Volume * nvp = NULL;
2098     VolumeStats stats_save;
2099     *ec = 0;
2100
2101     /* volume utility should never call AttachByVp */
2102     assert(programType == fileServer);
2103    
2104     volumeId = vp->hashid;
2105     partp = vp->partition;
2106     VolumeExternalName_r(volumeId, name, sizeof(name));
2107
2108
2109     /* if another thread is performing a blocking op, wait */
2110     VWaitExclusiveState_r(vp);
2111
2112     memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
2113
2114     /* if it's already attached, see if we can return it */
2115     if (V_attachState(vp) == VOL_STATE_ATTACHED) {
2116         VGetVolumeByVp_r(ec, vp);
2117         if (V_inUse(vp) == fileServer) {
2118             return vp;
2119         } else {
2120             if (vp->specialStatus == VBUSY)
2121                 isbusy = 1;
2122             VDetachVolume_r(ec, vp);
2123             if (*ec) {
2124                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
2125             }
2126             vp = NULL;
2127         }
2128     }
2129
2130     /* pre-attach volume if it hasn't been done yet */
2131     if (!vp || 
2132         (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
2133         (V_attachState(vp) == VOL_STATE_ERROR)) {
2134         nvp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
2135         if (*ec) {
2136             return NULL;
2137         }
2138         if (nvp != vp) {
2139             reserve = 1;
2140             VCreateReservation_r(nvp);
2141             vp = nvp;
2142         }
2143     }
2144     
2145     assert(vp != NULL);
2146     VChangeState_r(vp, VOL_STATE_ATTACHING);
2147
2148     /* restore monotonically increasing stats */
2149     memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
2150
2151     *ec = 0;
2152
2153
2154     /* compute path to disk header, 
2155      * read in header, 
2156      * and verify magic and version stamps */
2157     strcpy(path, VPartitionPath(partp));
2158
2159     VOL_UNLOCK;
2160
2161     strcat(path, "/");
2162     strcat(path, name);
2163     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
2164         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
2165         if (fd > -1)
2166             close(fd);
2167         *ec = VNOVOL;
2168         VOL_LOCK;
2169         goto done;
2170     }
2171     n = read(fd, &diskHeader, sizeof(diskHeader));
2172     close(fd);
2173     if (n != sizeof(diskHeader)
2174         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
2175         Log("VAttachVolume: Error reading volume header %s\n", path);
2176         *ec = VSALVAGE;
2177         VOL_LOCK;
2178         goto done;
2179     }
2180     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
2181         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
2182         *ec = VSALVAGE;
2183         VOL_LOCK;
2184         goto done;
2185     }
2186
2187     /* convert on-disk header format to in-memory header format */
2188     DiskToVolumeHeader(&iheader, &diskHeader);
2189
2190     /* do volume attach
2191      *
2192      * NOTE: attach2 is entered without any locks, and returns
2193      * with vol_glock_mutex held */
2194     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
2195
2196     /*
2197      * the event that an error was encountered, or
2198      * the volume was not brought to an attached state
2199      * for any reason, skip to the end.  We cannot
2200      * safely call VUpdateVolume unless we "own" it.
2201      */
2202     if (*ec || 
2203         (vp == NULL) ||
2204         (V_attachState(vp) != VOL_STATE_ATTACHED)) {
2205         goto done;
2206     }
2207
2208     V_needsCallback(vp) = 0;
2209     VUpdateVolume_r(ec, vp, 0);
2210     if (*ec) {
2211         Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
2212         VPutVolume_r(vp);
2213         goto done;
2214     }
2215     if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2216 #ifndef AFS_DEMAND_ATTACH_FS
2217         /* This is a hack: by temporarily setting the incore
2218          * dontSalvage flag ON, the volume will be put back on the
2219          * Update list (with dontSalvage OFF again).  It will then
2220          * come back in N minutes with DONT_SALVAGE eventually
2221          * set.  This is the way that volumes that have never had
2222          * it set get it set; or that volumes that have been
2223          * offline without DONT SALVAGE having been set also
2224          * eventually get it set */
2225         V_dontSalvage(vp) = DONT_SALVAGE;
2226 #endif /* !AFS_DEMAND_ATTACH_FS */
2227         VAddToVolumeUpdateList_r(ec, vp);
2228         if (*ec) {
2229             Log("VAttachVolume: Error adding volume %u to update list\n", vp->hashid);
2230             if (vp)
2231                 VPutVolume_r(vp);
2232             goto done;
2233         }
2234     }
2235     if (LogLevel)
2236         Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2237             V_name(vp));
2238   done:
2239     if (reserve) {
2240         VCancelReservation_r(nvp);
2241         reserve = 0;
2242     }
2243     if (*ec && (*ec != VOFFLINE) && (*ec != VSALVAGE)) {
2244         if (vp && !VIsErrorState(V_attachState(vp))) {
2245             VChangeState_r(vp, VOL_STATE_ERROR);
2246         }
2247         return NULL;
2248     } else {
2249         return vp;
2250     }
2251 }
2252 #endif /* AFS_DEMAND_ATTACH_FS */
2253
2254 /*
2255  * called without any locks held
2256  * returns with vol_glock_mutex held
2257  */
2258 private Volume * 
2259 attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * header,
2260         struct DiskPartition64 * partp, register Volume * vp, int isbusy, int mode)
2261 {
2262     vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
2263     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
2264             header->largeVnodeIndex);
2265     IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
2266             header->smallVnodeIndex);
2267     IH_INIT(vp->diskDataHandle, partp->device, header->parent,
2268             header->volumeInfo);
2269     IH_INIT(vp->linkHandle, partp->device, header->parent, header->linkTable);
2270     vp->shuttingDown = 0;
2271     vp->goingOffline = 0;
2272     vp->nUsers = 1;
2273 #ifdef AFS_DEMAND_ATTACH_FS
2274     vp->stats.last_attach = FT_ApproxTime();
2275     vp->stats.attaches++;
2276 #endif
2277
2278     VOL_LOCK;
2279     IncUInt64(&VStats.attaches);
2280     vp->cacheCheck = ++VolumeCacheCheck;
2281     /* just in case this ever rolls over */
2282     if (!vp->cacheCheck)
2283         vp->cacheCheck = ++VolumeCacheCheck;
2284     GetVolumeHeader(vp);
2285     VOL_UNLOCK;
2286
2287 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
2288     /* demand attach changes the V_PEEK mechanism
2289      *
2290      * we can now suck the current disk data structure over
2291      * the fssync interface without going to disk
2292      *
2293      * (technically, we don't need to restrict this feature
2294      *  to demand attach fileservers.  However, I'm trying
2295      *  to limit the number of common code changes)
2296      */
2297     if (programType != fileServer && mode == V_PEEK) {
2298         SYNC_response res;
2299         res.payload.len = sizeof(VolumeDiskData);
2300         res.payload.buf = &vp->header->diskstuff;
2301
2302         if (FSYNC_VolOp(volumeId,
2303                         partp->name,
2304                         FSYNC_VOL_QUERY_HDR,
2305                         FSYNC_WHATEVER,
2306                         &res) == SYNC_OK) {
2307             goto disk_header_loaded;
2308         }
2309     }
2310 #endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
2311     (void)ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
2312                      sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
2313
2314 #ifdef AFS_DEMAND_ATTACH_FS
2315     /* update stats */
2316     VOL_LOCK;
2317     IncUInt64(&VStats.hdr_loads);
2318     IncUInt64(&vp->stats.hdr_loads);
2319     VOL_UNLOCK;
2320 #endif /* AFS_DEMAND_ATTACH_FS */
2321     
2322     if (*ec) {
2323         Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
2324     }
2325
2326 #ifdef AFS_DEMAND_ATTACH_FS
2327 # ifdef FSSYNC_BUILD_CLIENT
2328  disk_header_loaded:
2329 #endif
2330     if (!*ec) {
2331
2332         /* check for pending volume operations */
2333         if (vp->pending_vol_op) {
2334             /* see if the pending volume op requires exclusive access */
2335             switch (vp->pending_vol_op->vol_op_state) {
2336             case FSSYNC_VolOpPending:
2337                 /* this should never happen */
2338                 assert(vp->pending_vol_op->vol_op_state != FSSYNC_VolOpPending);
2339                 break;
2340
2341             case FSSYNC_VolOpRunningUnknown:
2342                 if (VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2343                     vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
2344                     break;
2345                 } else {
2346                     vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
2347                     /* fall through to take volume offline */
2348                 }
2349
2350             case FSSYNC_VolOpRunningOffline:
2351                 /* mark the volume down */
2352                 *ec = VOFFLINE;
2353                 VChangeState_r(vp, VOL_STATE_UNATTACHED);
2354                 if (V_offlineMessage(vp)[0] == '\0')
2355                     strlcpy(V_offlineMessage(vp),
2356                             "A volume utility is running.", 
2357                             sizeof(V_offlineMessage(vp)));
2358                 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
2359
2360                 /* check to see if we should set the specialStatus flag */
2361                 if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
2362                     vp->specialStatus = VBUSY;
2363                 }
2364             default:
2365                 break;
2366             }
2367         }
2368
2369         V_attachFlags(vp) |= VOL_HDR_LOADED;
2370         vp->stats.last_hdr_load = vp->stats.last_attach;
2371     }
2372 #endif /* AFS_DEMAND_ATTACH_FS */
2373
2374     if (!*ec) {
2375         struct IndexFileHeader iHead;
2376
2377 #if OPENAFS_VOL_STATS
2378         /*
2379          * We just read in the diskstuff part of the header.  If the detailed
2380          * volume stats area has not yet been initialized, we should bzero the
2381          * area and mark it as initialized.
2382          */
2383         if (!(V_stat_initialized(vp))) {
2384             memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
2385             V_stat_initialized(vp) = 1;
2386         }
2387 #endif /* OPENAFS_VOL_STATS */
2388
2389         (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
2390                          (char *)&iHead, sizeof(iHead),
2391                          SMALLINDEXMAGIC, SMALLINDEXVERSION);
2392
2393         if (*ec) {
2394             Log("VAttachVolume: Error reading smallVnode vol header %s; error=%u\n", path, *ec);
2395         }
2396     }
2397
2398     if (!*ec) {
2399         struct IndexFileHeader iHead;
2400
2401         (void)ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
2402                          (char *)&iHead, sizeof(iHead),
2403                          LARGEINDEXMAGIC, LARGEINDEXVERSION);
2404
2405         if (*ec) {
2406             Log("VAttachVolume: Error reading largeVnode vol header %s; error=%u\n", path, *ec);
2407         }
2408     }
2409
2410 #ifdef AFS_NAMEI_ENV
2411     if (!*ec) {
2412         struct versionStamp stamp;
2413
2414         (void)ReadHeader(ec, V_linkHandle(vp), (char *)&stamp,
2415                          sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION);
2416
2417         if (*ec) {
2418             Log("VAttachVolume: Error reading namei vol header %s; error=%u\n", path, *ec);
2419         }
2420     }
2421 #endif /* AFS_NAMEI_ENV */
2422
2423 #if defined(AFS_DEMAND_ATTACH_FS)
2424     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
2425         VOL_LOCK;
2426         if (programType == fileServer) {
2427             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2428             vp->nUsers = 0;
2429         } else {
2430             Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2431             FreeVolume(vp);
2432             *ec = VSALVAGE;
2433         }
2434         return NULL;
2435     } else if (*ec) {
2436         /* volume operation in progress */
2437         VOL_LOCK;
2438         return NULL;
2439     }
2440 #else /* AFS_DEMAND_ATTACH_FS */
2441     if (*ec) {
2442         Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2443         VOL_LOCK;
2444         FreeVolume(vp);
2445         return NULL;
2446     }
2447 #endif /* AFS_DEMAND_ATTACH_FS */
2448
2449     if (V_needsSalvaged(vp)) {
2450         if (vp->specialStatus)
2451             vp->specialStatus = 0;
2452         VOL_LOCK;
2453 #if defined(AFS_DEMAND_ATTACH_FS)
2454         if (programType == fileServer) {
2455             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2456             vp->nUsers = 0;
2457         } else {
2458             Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
2459             FreeVolume(vp);
2460             *ec = VSALVAGE;
2461         }
2462 #else /* AFS_DEMAND_ATTACH_FS */
2463         FreeVolume(vp);
2464         *ec = VSALVAGE;
2465 #endif /* AFS_DEMAND_ATTACH_FS */
2466         return NULL;
2467     }
2468
2469     VOL_LOCK;
2470     if (programType == fileServer) {
2471 #ifndef FAST_RESTART
2472         if (V_inUse(vp) && VolumeWriteable(vp)) {
2473             if (!V_needsSalvaged(vp)) {
2474                 V_needsSalvaged(vp) = 1;
2475                 VUpdateVolume_r(ec, vp, 0);
2476             }
2477 #if defined(AFS_DEMAND_ATTACH_FS)
2478             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2479             vp->nUsers = 0;
2480 #else /* AFS_DEMAND_ATTACH_FS */
2481             Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
2482             FreeVolume(vp);
2483             *ec = VSALVAGE;
2484 #endif /* AFS_DEMAND_ATTACH_FS */
2485             return NULL;
2486         }
2487 #endif /* FAST_RESTART */
2488
2489         if (V_destroyMe(vp) == DESTROY_ME) {
2490 #if defined(AFS_DEMAND_ATTACH_FS)
2491             /* schedule a salvage so the volume goes away on disk */
2492             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2493             VChangeState_r(vp, VOL_STATE_ERROR);
2494             vp->nUsers = 0;
2495 #endif /* AFS_DEMAND_ATTACH_FS */
2496             FreeVolume(vp);
2497             Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
2498             *ec = VNOVOL;
2499             return NULL;
2500         }
2501     }
2502
2503     vp->nextVnodeUnique = V_uniquifier(vp);
2504     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
2505 #ifndef BITMAP_LATER
2506     if (programType == fileServer && VolumeWriteable(vp)) {
2507         int i;
2508         for (i = 0; i < nVNODECLASSES; i++) {
2509             VGetBitmap_r(ec, vp, i);
2510             if (*ec) {
2511 #ifdef AFS_DEMAND_ATTACH_FS
2512                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2513                 vp->nUsers = 0;
2514 #else /* AFS_DEMAND_ATTACH_FS */
2515                 FreeVolume(vp);
2516 #endif /* AFS_DEMAND_ATTACH_FS */
2517                 Log("VAttachVolume: error getting bitmap for volume (%s)\n",
2518                     path);
2519                 return NULL;
2520             }
2521         }
2522     }
2523 #endif /* BITMAP_LATER */
2524
2525     if (programType == fileServer) {
2526         if (vp->specialStatus)
2527             vp->specialStatus = 0;
2528         if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
2529             V_inUse(vp) = fileServer;
2530             V_offlineMessage(vp)[0] = '\0';
2531         }
2532     } else {
2533         if ((mode != V_PEEK) && (mode != V_SECRETLY))
2534             V_inUse(vp) = programType;
2535         V_checkoutMode(vp) = mode;
2536     }
2537
2538     AddVolumeToHashTable(vp, V_id(vp));
2539 #ifdef AFS_DEMAND_ATTACH_FS
2540     if ((programType != fileServer) ||
2541         (V_inUse(vp) == fileServer)) {
2542         AddVolumeToVByPList_r(vp);
2543         VLRU_Add_r(vp);
2544         VChangeState_r(vp, VOL_STATE_ATTACHED);
2545     } else {
2546         VChangeState_r(vp, VOL_STATE_UNATTACHED);
2547     }
2548 #endif
2549     return vp;
2550 }
2551
2552 /* Attach an existing volume.
2553    The volume also normally goes online at this time.
2554    An offline volume must be reattached to make it go online.
2555  */
2556
2557 Volume *
2558 VAttachVolume(Error * ec, VolumeId volumeId, int mode)
2559 {
2560     Volume *retVal;
2561     VOL_LOCK;
2562     retVal = VAttachVolume_r(ec, volumeId, mode);
2563     VOL_UNLOCK;
2564     return retVal;
2565 }
2566
2567 Volume *
2568 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
2569 {
2570     char *part, *name;
2571     VGetVolumePath(ec, volumeId, &part, &name);
2572     if (*ec) {
2573         register Volume *vp;
2574         Error error;
2575         vp = VGetVolume_r(&error, volumeId);
2576         if (vp) {
2577             assert(V_inUse(vp) == 0);
2578             VDetachVolume_r(ec, vp);
2579         }
2580         return NULL;
2581     }
2582     return VAttachVolumeByName_r(ec, part, name, mode);
2583 }
2584
2585 /* Increment a reference count to a volume, sans context swaps.  Requires
2586  * possibly reading the volume header in from the disk, since there's
2587  * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
2588  *
2589  * N.B. This call can fail if we can't read in the header!!  In this case
2590  * we still guarantee we won't context swap, but the ref count won't be
2591  * incremented (otherwise we'd violate the invariant).
2592  */
2593 /* NOTE: with the demand attach fileserver extensions, the global lock
2594  * is dropped within VHold */
2595 #ifdef AFS_DEMAND_ATTACH_FS
2596 static int
2597 VHold_r(register Volume * vp)
2598 {
2599     Error error;
2600
2601     VCreateReservation_r(vp);
2602     VWaitExclusiveState_r(vp);
2603
2604     LoadVolumeHeader(&error, vp);
2605     if (error) {
2606         VCancelReservation_r(vp);
2607         return error;
2608     }
2609     vp->nUsers++;
2610     VCancelReservation_r(vp);
2611     return 0;
2612 }
2613 #else /* AFS_DEMAND_ATTACH_FS */
2614 static int
2615 VHold_r(register Volume * vp)
2616 {
2617     Error error;
2618
2619     LoadVolumeHeader(&error, vp);
2620     if (error)
2621         return error;
2622     vp->nUsers++;
2623     return 0;
2624 }
2625 #endif /* AFS_DEMAND_ATTACH_FS */
2626
2627 #if 0
2628 static int
2629 VHold(register Volume * vp)
2630 {
2631     int retVal;
2632     VOL_LOCK;
2633     retVal = VHold_r(vp);
2634     VOL_UNLOCK;
2635     return retVal;
2636 }
2637 #endif
2638
2639
2640 /***************************************************/
2641 /* get and put volume routines                     */
2642 /***************************************************/
2643
2644 /**
2645  * put back a heavyweight reference to a volume object.
2646  *
2647  * @param[in] vp  volume object pointer
2648  *
2649  * @pre VOL_LOCK held
2650  *
2651  * @post heavyweight volume reference put back.
2652  *       depending on state, volume may have been taken offline,
2653  *       detached, salvaged, freed, etc.
2654  *
2655  * @internal volume package internal use only
2656  */
2657 void
2658 VPutVolume_r(register Volume * vp)
2659 {
2660     assert(--vp->nUsers >= 0);
2661     if (vp->nUsers == 0) {
2662         VCheckOffline(vp);
2663         ReleaseVolumeHeader(vp->header);
2664 #ifdef AFS_DEMAND_ATTACH_FS
2665         if (!VCheckDetach(vp)) {
2666             VCheckSalvage(vp);
2667             VCheckFree(vp);
2668         }
2669 #else /* AFS_DEMAND_ATTACH_FS */
2670         VCheckDetach(vp);
2671 #endif /* AFS_DEMAND_ATTACH_FS */
2672     }
2673 }
2674
2675 void
2676 VPutVolume(register Volume * vp)
2677 {
2678     VOL_LOCK;
2679     VPutVolume_r(vp);
2680     VOL_UNLOCK;
2681 }
2682
2683
2684 /* Get a pointer to an attached volume.  The pointer is returned regardless
2685    of whether or not the volume is in service or on/off line.  An error
2686    code, however, is returned with an indication of the volume's status */
2687 Volume *
2688 VGetVolume(Error * ec, Error * client_ec, VolId volumeId)
2689 {
2690     Volume *retVal;
2691     VOL_LOCK;
2692     retVal = GetVolume(ec, client_ec, volumeId, NULL, 0);
2693     VOL_UNLOCK;
2694     return retVal;
2695 }
2696
2697 Volume *
2698 VGetVolume_r(Error * ec, VolId volumeId)
2699 {
2700     return GetVolume(ec, NULL, volumeId, NULL, 0);
2701 }
2702
2703 /* try to get a volume we've previously looked up */
2704 /* for demand attach fs, caller MUST NOT hold a ref count on vp */
2705 Volume * 
2706 VGetVolumeByVp_r(Error * ec, Volume * vp)
2707 {
2708     return GetVolume(ec, NULL, vp->hashid, vp, 0);
2709 }
2710
2711 /* private interface for getting a volume handle
2712  * volumeId must be provided.
2713  * hint is an optional parameter to speed up hash lookups
2714  * flags is not used at this time
2715  */
2716 /* for demand attach fs, caller MUST NOT hold a ref count on hint */
2717 static Volume *
2718 GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags)
2719 {
2720     Volume *vp = hint;
2721     /* pull this profiling/debugging code out of regular builds */
2722 #ifdef notdef
2723 #define VGET_CTR_INC(x) x++
2724     unsigned short V0 = 0, V1 = 0, V2 = 0, V3 = 0, V5 = 0, V6 =
2725         0, V7 = 0, V8 = 0, V9 = 0;
2726     unsigned short V10 = 0, V11 = 0, V12 = 0, V13 = 0, V14 = 0, V15 = 0;
2727 #else
2728 #define VGET_CTR_INC(x)
2729 #endif
2730 #ifdef AFS_DEMAND_ATTACH_FS
2731     Volume *avp, * rvp = hint;
2732 #endif
2733
2734     /* 
2735      * if VInit is zero, the volume package dynamic
2736      * data structures have not been initialized yet,
2737      * and we must immediately return an error
2738      */
2739     if (VInit == 0) {
2740         vp = NULL;
2741         *ec = VOFFLINE;
2742         if (client_ec) {
2743             *client_ec = VOFFLINE;
2744         }
2745         goto not_inited;
2746     }
2747
2748 #ifdef AFS_DEMAND_ATTACH_FS
2749     if (rvp) {
2750         VCreateReservation_r(rvp);
2751     }
2752 #endif /* AFS_DEMAND_ATTACH_FS */
2753
2754     for (;;) {
2755         *ec = 0;
2756         if (client_ec)
2757             *client_ec = 0;
2758         VGET_CTR_INC(V0);
2759
2760         vp = VLookupVolume_r(ec, volumeId, vp);
2761         if (*ec) {
2762             vp = NULL;
2763             break;
2764         }
2765
2766 #ifdef AFS_DEMAND_ATTACH_FS
2767         if (rvp && (rvp != vp)) {
2768             /* break reservation on old vp */
2769             VCancelReservation_r(rvp);
2770             rvp = NULL;
2771         }
2772 #endif /* AFS_DEMAND_ATTACH_FS */
2773
2774         if (!vp) {
2775             VGET_CTR_INC(V1);
2776             if (VInit < 2) {
2777                 VGET_CTR_INC(V2);
2778                 /* Until we have reached an initialization level of 2
2779                  * we don't know whether this volume exists or not.
2780                  * We can't sleep and retry later because before a volume
2781                  * is attached, the caller tries to get it first.  Just
2782                  * return VOFFLINE and the caller can choose whether to
2783                  * retry the command or not. */
2784                 *ec = VOFFLINE;
2785                 break;
2786             }
2787
2788             *ec = VNOVOL;
2789             break;
2790         }
2791
2792         VGET_CTR_INC(V3);
2793         IncUInt64(&VStats.hdr_gets);
2794         
2795 #ifdef AFS_DEMAND_ATTACH_FS
2796         /* block if someone else is performing an exclusive op on this volume */
2797         if (rvp != vp) {
2798             rvp = vp;
2799             VCreateReservation_r(rvp);
2800         }
2801         VWaitExclusiveState_r(vp);
2802
2803         /* short circuit with VNOVOL in the following circumstances:
2804          *
2805          *   - VOL_STATE_ERROR
2806          *   - VOL_STATE_SHUTTING_DOWN
2807          */
2808         if ((V_attachState(vp) == VOL_STATE_ERROR) ||
2809             (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN) ||
2810             (V_attachState(vp) == VOL_STATE_GOING_OFFLINE)) {
2811             *ec = VNOVOL;
2812             vp = NULL;
2813             break;
2814         }
2815
2816         /*
2817          * short circuit with VOFFLINE in the following circumstances:
2818          *
2819          *   - VOL_STATE_UNATTACHED
2820          */
2821        if (V_attachState(vp) == VOL_STATE_UNATTACHED) {
2822            if (vp->specialStatus) {
2823                *ec = vp->specialStatus;
2824            } else {
2825                *ec = VOFFLINE;
2826            }
2827            vp = NULL;
2828            break;
2829        }
2830
2831         /* allowable states:
2832          *   - PREATTACHED
2833          *   - ATTACHED
2834          *   - SALVAGING
2835          */
2836
2837         if (vp->salvage.requested) {
2838             VUpdateSalvagePriority_r(vp);
2839         }
2840
2841         if (V_attachState(vp) == VOL_STATE_PREATTACHED) {
2842             avp = VAttachVolumeByVp_r(ec, vp, 0);
2843             if (avp) {
2844                 if (vp != avp) {
2845                     /* VAttachVolumeByVp_r can return a pointer
2846                      * != the vp passed to it under certain
2847                      * conditions; make sure we don't leak
2848                      * reservations if that happens */
2849                     vp = avp;
2850                     VCancelReservation_r(rvp);
2851                     rvp = avp;
2852                     VCreateReservation_r(rvp);
2853                 }
2854                 VPutVolume_r(avp);
2855             }
2856             if (*ec) {
2857                 int endloop = 0;
2858                 switch (*ec) {
2859                 case VSALVAGING:
2860                     break;
2861                 case VOFFLINE:
2862                     if (!vp->pending_vol_op) {
2863                         endloop = 1;
2864                     }
2865                     break;
2866                 default:
2867                     *ec = VNOVOL;
2868                     endloop = 1;
2869                 }
2870                 if (endloop) {
2871                     vp = NULL;
2872                     break;
2873                 }
2874             }
2875         }
2876
2877         if ((V_attachState(vp) == VOL_STATE_SALVAGING) ||
2878             (*ec == VSALVAGING)) {
2879             if (client_ec) {
2880                 /* see CheckVnode() in afsfileprocs.c for an explanation
2881                  * of this error code logic */
2882                 afs_uint32 now = FT_ApproxTime();
2883                 if ((vp->stats.last_salvage + (10 * 60)) >= now) {
2884                     *client_ec = VBUSY;
2885                 } else {
2886                     *client_ec = VRESTARTING;
2887                 }
2888             }
2889             *ec = VSALVAGING;
2890             vp = NULL;
2891             break;
2892         }
2893 #endif
2894
2895         LoadVolumeHeader(ec, vp);
2896         if (*ec) {
2897             VGET_CTR_INC(V6);
2898             /* Only log the error if it was a totally unexpected error.  Simply
2899              * a missing inode is likely to be caused by the volume being deleted */
2900             if (errno != ENXIO || LogLevel)
2901                 Log("Volume %u: couldn't reread volume header\n",
2902                     vp->hashid);
2903 #ifdef AFS_DEMAND_ATTACH_FS
2904             if (programType == fileServer) {
2905                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2906             } else {
2907                 FreeVolume(vp);
2908                 vp = NULL;
2909             }
2910 #else /* AFS_DEMAND_ATTACH_FS */
2911             FreeVolume(vp);
2912             vp = NULL;
2913 #endif /* AFS_DEMAND_ATTACH_FS */
2914             break;
2915         }
2916
2917 #ifdef AFS_DEMAND_ATTACH_FS
2918         /*
2919          * this test MUST happen after the volume header is loaded
2920          */
2921         
2922          /* only valid before/during demand attachment */
2923          assert(!vp->pending_vol_op || vp->pending_vol_op->vol_op_state != FSSYNC_VolOpRunningUnknown);
2924         
2925          /* deny getvolume due to running mutually exclusive vol op */
2926          if (vp->pending_vol_op && vp->pending_vol_op->vol_op_state==FSSYNC_VolOpRunningOffline) {
2927            /* 
2928             * volume cannot remain online during this volume operation.
2929             * notify client. 
2930             */
2931            if (vp->specialStatus) {
2932                /*
2933                 * special status codes outrank normal VOFFLINE code
2934                 */
2935                *ec = vp->specialStatus;
2936                if (client_ec) {
2937                    *client_ec = vp->specialStatus;
2938                }
2939            } else {
2940                if (client_ec) {
2941                    /* see CheckVnode() in afsfileprocs.c for an explanation
2942                     * of this error code logic */
2943                    afs_uint32 now = FT_ApproxTime();
2944                    if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
2945                        *client_ec = VBUSY;
2946                    } else {
2947                        *client_ec = VRESTARTING;
2948                    }
2949                }
2950                *ec = VOFFLINE;
2951            }
2952            VChangeState_r(vp, VOL_STATE_UNATTACHED);
2953            FreeVolumeHeader(vp);
2954            vp = NULL;
2955            break;
2956         }
2957 #endif /* AFS_DEMAND_ATTACH_FS */
2958         
2959         VGET_CTR_INC(V7);
2960         if (vp->shuttingDown) {
2961             VGET_CTR_INC(V8);
2962             *ec = VNOVOL;
2963             vp = NULL;
2964             break;
2965         }
2966
2967         if (programType == fileServer) {
2968             VGET_CTR_INC(V9);
2969             if (vp->goingOffline) {
2970                 VGET_CTR_INC(V10);
2971 #ifdef AFS_DEMAND_ATTACH_FS
2972                 /* wait for the volume to go offline */
2973                 if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
2974                     VWaitStateChange_r(vp);
2975                 }
2976 #elif defined(AFS_PTHREAD_ENV)
2977                 VOL_CV_WAIT(&vol_put_volume_cond);
2978 #else /* AFS_PTHREAD_ENV */
2979                 LWP_WaitProcess(VPutVolume);
2980 #endif /* AFS_PTHREAD_ENV */
2981                 continue;
2982             }
2983             if (vp->specialStatus) {
2984                 VGET_CTR_INC(V11);
2985                 *ec = vp->specialStatus;
2986             } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
2987                 VGET_CTR_INC(V12);
2988                 *ec = VNOVOL;
2989             } else if (V_inUse(vp) == 0) {
2990                 VGET_CTR_INC(V13);
2991                 *ec = VOFFLINE;
2992             } else {
2993                 VGET_CTR_INC(V14);
2994             }
2995         }
2996         break;
2997     }
2998     VGET_CTR_INC(V15);
2999
3000 #ifdef AFS_DEMAND_ATTACH_FS
3001     /* if no error, bump nUsers */
3002     if (vp) {
3003         vp->nUsers++;
3004         VLRU_UpdateAccess_r(vp);
3005     }
3006     if (rvp) {
3007         VCancelReservation_r(rvp);
3008         rvp = NULL;
3009     }
3010     if (client_ec && !*client_ec) {
3011         *client_ec = *ec;
3012     }
3013 #else /* AFS_DEMAND_ATTACH_FS */
3014     /* if no error, bump nUsers */
3015     if (vp) {
3016         vp->nUsers++;
3017     }
3018     if (client_ec) {
3019         *client_ec = *ec;
3020     }
3021 #endif /* AFS_DEMAND_ATTACH_FS */
3022
3023  not_inited:
3024     assert(vp || *ec);
3025     return vp;
3026 }
3027
3028
3029 /***************************************************/
3030 /* Volume offline/detach routines                  */
3031 /***************************************************/
3032
3033 /* caller MUST hold a heavyweight ref on vp */
3034 #ifdef AFS_DEMAND_ATTACH_FS
3035 void
3036 VTakeOffline_r(register Volume * vp)
3037 {
3038     Error error;
3039
3040     assert(vp->nUsers > 0);
3041     assert(programType == fileServer);
3042
3043     VCreateReservation_r(vp);
3044     VWaitExclusiveState_r(vp);
3045
3046     vp->goingOffline = 1;
3047     V_needsSalvaged(vp) = 1;
3048
3049     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
3050     VCancelReservation_r(vp);
3051 }
3052 #else /* AFS_DEMAND_ATTACH_FS */
3053 void
3054 VTakeOffline_r(register Volume * vp)
3055 {
3056     assert(vp->nUsers > 0);
3057     assert(programType == fileServer);
3058
3059     vp->goingOffline = 1;
3060     V_needsSalvaged(vp) = 1;
3061 }
3062 #endif /* AFS_DEMAND_ATTACH_FS */
3063
3064 void
3065 VTakeOffline(register Volume * vp)
3066 {
3067     VOL_LOCK;
3068     VTakeOffline_r(vp);
3069     VOL_UNLOCK;
3070 }
3071
3072 /**
3073  * force a volume offline.
3074  *
3075  * @param[in] vp     volume object pointer
3076  * @param[in] flags  flags (see note below)
3077  *
3078  * @note the flag VOL_FORCEOFF_NOUPDATE is a recursion control flag
3079  *       used when VUpdateVolume_r needs to call VForceOffline_r
3080  *       (which in turn would normally call VUpdateVolume_r)
3081  *
3082  * @see VUpdateVolume_r
3083  *
3084  * @pre VOL_LOCK must be held.
3085  *      for DAFS, caller must hold ref.
3086  *
3087  * @note for DAFS, it _is safe_ to call this function from an
3088  *       exclusive state
3089  *
3090  * @post needsSalvaged flag is set.
3091  *       for DAFS, salvage is requested.
3092  *       no further references to the volume through the volume 
3093  *       package will be honored.
3094  *       all file descriptor and vnode caches are invalidated.
3095  *
3096  * @warning this is a heavy-handed interface.  it results in
3097  *          a volume going offline regardless of the current 
3098  *          reference count state.
3099  *
3100  * @internal  volume package internal use only
3101  */
3102 void
3103 VForceOffline_r(Volume * vp, int flags)
3104 {
3105     Error error;
3106     if (!V_inUse(vp)) {
3107 #ifdef AFS_DEMAND_ATTACH_FS
3108         VChangeState_r(vp, VOL_STATE_ERROR);
3109 #endif
3110         return;
3111     }
3112
3113     strcpy(V_offlineMessage(vp),
3114            "Forced offline due to internal error: volume needs to be salvaged");
3115     Log("Volume %u forced offline:  it needs salvaging!\n", V_id(vp));
3116
3117     V_inUse(vp) = 0;
3118     vp->goingOffline = 0;
3119     V_needsSalvaged(vp) = 1;
3120     if (!(flags & VOL_FORCEOFF_NOUPDATE)) {
3121         VUpdateVolume_r(&error, vp, VOL_UPDATE_NOFORCEOFF);
3122     }
3123
3124 #ifdef AFS_DEMAND_ATTACH_FS
3125     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
3126 #endif /* AFS_DEMAND_ATTACH_FS */
3127
3128 #ifdef AFS_PTHREAD_ENV
3129     assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3130 #else /* AFS_PTHREAD_ENV */
3131     LWP_NoYieldSignal(VPutVolume);
3132 #endif /* AFS_PTHREAD_ENV */
3133
3134     VReleaseVolumeHandles_r(vp);
3135 }
3136
3137 /**
3138  * force a volume offline.
3139  *
3140  * @param[in] vp  volume object pointer
3141  *
3142  * @see VForceOffline_r
3143  */
3144 void
3145 VForceOffline(Volume * vp)
3146 {
3147     VOL_LOCK;
3148     VForceOffline_r(vp, 0);
3149     VOL_UNLOCK;
3150 }
3151
3152 /* The opposite of VAttachVolume.  The volume header is written to disk, with
3153    the inUse bit turned off.  A copy of the header is maintained in memory,
3154    however (which is why this is VOffline, not VDetach).
3155  */
3156 void
3157 VOffline_r(Volume * vp, char *message)
3158 {
3159 #ifndef AFS_DEMAND_ATTACH_FS
3160     Error error;
3161     VolumeId vid = V_id(vp);
3162 #endif
3163
3164     assert(programType != volumeUtility);
3165     if (!V_inUse(vp)) {
3166         VPutVolume_r(vp);
3167         return;
3168     }
3169     if (V_offlineMessage(vp)[0] == '\0')
3170         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
3171     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
3172
3173     vp->goingOffline = 1;
3174 #ifdef AFS_DEMAND_ATTACH_FS
3175     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
3176     VCreateReservation_r(vp);
3177     VPutVolume_r(vp);
3178
3179     /* wait for the volume to go offline */
3180     if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
3181         VWaitStateChange_r(vp);
3182     }
3183     VCancelReservation_r(vp);
3184 #else /* AFS_DEMAND_ATTACH_FS */
3185     VPutVolume_r(vp);
3186     vp = VGetVolume_r(&error, vid);     /* Wait for it to go offline */
3187     if (vp)                     /* In case it was reattached... */
3188         VPutVolume_r(vp);
3189 #endif /* AFS_DEMAND_ATTACH_FS */
3190 }
3191
3192 #ifdef AFS_DEMAND_ATTACH_FS
3193 /**
3194  * Take a volume offline in order to perform a volume operation.
3195  *
3196  * @param[inout] ec       address in which to store error code
3197  * @param[in]    vp       volume object pointer
3198  * @param[in]    message  volume offline status message
3199  *
3200  * @pre
3201  *    - VOL_LOCK is held
3202  *    - caller MUST hold a heavyweight ref on vp
3203  *
3204  * @post
3205  *    - volume is taken offline
3206  *    - if possible, volume operation is promoted to running state
3207  *    - on failure, *ec is set to nonzero
3208  *
3209  * @note Although this function does not return any value, it may
3210  *       still fail to promote our pending volume operation to
3211  *       a running state.  Any caller MUST check the value of *ec,
3212  *       and MUST NOT blindly assume success.
3213  *
3214  * @warning if the caller does not hold a lightweight ref on vp,
3215  *          then it MUST NOT reference vp after this function
3216  *          returns to the caller.
3217  *
3218  * @internal volume package internal use only
3219  */
3220 void
3221 VOfflineForVolOp_r(Error *ec, Volume *vp, char *message)
3222 {
3223     assert(vp->pending_vol_op);
3224     if (!V_inUse(vp)) {
3225         VPutVolume_r(vp);
3226         *ec = 1;
3227         return;
3228     }
3229     if (V_offlineMessage(vp)[0] == '\0')
3230         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
3231     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
3232
3233     vp->goingOffline = 1;
3234     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
3235     VCreateReservation_r(vp);
3236     VPutVolume_r(vp);
3237
3238     /* Wait for the volume to go offline */
3239     while (!VIsOfflineState(V_attachState(vp))) {
3240         /* do not give corrupted volumes to the volserver */
3241         if (vp->salvage.requested && vp->pending_vol_op->com.programType != salvageServer) {
3242            *ec = 1; 
3243            goto error;
3244         }
3245         VWaitStateChange_r(vp);
3246     }
3247     *ec = 0; 
3248  error:
3249     VCancelReservation_r(vp);
3250 }
3251 #endif /* AFS_DEMAND_ATTACH_FS */
3252
3253 void
3254 VOffline(Volume * vp, char *message)
3255 {
3256     VOL_LOCK;
3257     VOffline_r(vp, message);
3258     VOL_UNLOCK;
3259 }
3260
3261 /* This gets used for the most part by utility routines that don't want
3262  * to keep all the volume headers around.  Generally, the file server won't
3263  * call this routine, because then the offline message in the volume header
3264  * (or other information) won't be available to clients. For NAMEI, also
3265  * close the file handles.  However, the fileserver does call this during
3266  * an attach following a volume operation.
3267  */
3268 void
3269 VDetachVolume_r(Error * ec, Volume * vp)
3270 {
3271     VolumeId volume;
3272     struct DiskPartition64 *tpartp;
3273     int notifyServer = 0;
3274     int  useDone = FSYNC_VOL_ON;
3275
3276     *ec = 0;                    /* always "succeeds" */
3277     if (programType == volumeUtility) {
3278         notifyServer = vp->needsPutBack;
3279         if (V_destroyMe(vp) == DESTROY_ME)
3280             useDone = FSYNC_VOL_DONE;
3281 #ifdef AFS_DEMAND_ATTACH_FS
3282         else if (!V_blessed(vp) || !V_inService(vp))
3283             useDone = FSYNC_VOL_LEAVE_OFF;
3284 #endif
3285     }
3286     tpartp = vp->partition;
3287     volume = V_id(vp);
3288     DeleteVolumeFromHashTable(vp);
3289     vp->shuttingDown = 1;
3290 #ifdef AFS_DEMAND_ATTACH_FS
3291     DeleteVolumeFromVByPList_r(vp);
3292     VLRU_Delete_r(vp);
3293     VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
3294 #else
3295     if (programType != fileServer) 
3296         V_inUse(vp) = 0;
3297 #endif /* AFS_DEMAND_ATTACH_FS */
3298     VPutVolume_r(vp);
3299     /* Will be detached sometime in the future--this is OK since volume is offline */
3300
3301     /* XXX the following code should really be moved to VCheckDetach() since the volume
3302      * is not technically detached until the refcounts reach zero
3303      */
3304 #ifdef FSSYNC_BUILD_CLIENT
3305     if (programType == volumeUtility && notifyServer) {
3306         /* 
3307          * Note:  The server is not notified in the case of a bogus volume 
3308          * explicitly to make it possible to create a volume, do a partial 
3309          * restore, then abort the operation without ever putting the volume 
3310          * online.  This is essential in the case of a volume move operation 
3311          * between two partitions on the same server.  In that case, there 
3312          * would be two instances of the same volume, one of them bogus, 
3313          * which the file server would attempt to put on line 
3314          */
3315         FSYNC_VolOp(volume, tpartp->name, useDone, 0, NULL);
3316         /* XXX this code path is only hit by volume utilities, thus
3317          * V_BreakVolumeCallbacks will always be NULL.  if we really
3318          * want to break callbacks in this path we need to use FSYNC_VolOp() */
3319 #ifdef notdef
3320         /* Dettaching it so break all callbacks on it */
3321         if (V_BreakVolumeCallbacks) {
3322             Log("volume %u detached; breaking all call backs\n", volume);
3323             (*V_BreakVolumeCallbacks) (volume);
3324         }
3325 #endif
3326     }
3327 #endif /* FSSYNC_BUILD_CLIENT */
3328 }
3329
3330 void
3331 VDetachVolume(Error * ec, Volume * vp)
3332 {
3333     VOL_LOCK;
3334     VDetachVolume_r(ec, vp);
3335     VOL_UNLOCK;
3336 }
3337
3338
3339 /***************************************************/
3340 /* Volume fd/inode handle closing routines         */
3341 /***************************************************/
3342
3343 /* For VDetachVolume, we close all cached file descriptors, but keep
3344  * the Inode handles in case we need to read from a busy volume.
3345  */
3346 /* for demand attach, caller MUST hold ref count on vp */
3347 static void
3348 VCloseVolumeHandles_r(Volume * vp)
3349 {
3350 #ifdef AFS_DEMAND_ATTACH_FS
3351     VolState state_save;
3352
3353     state_save = VChangeState_r(vp, VOL_STATE_OFFLINING);
3354 #endif
3355
3356     /* demand attach fs
3357      *
3358      * XXX need to investigate whether we can perform
3359      * DFlushVolume outside of vol_glock_mutex... 
3360      *
3361      * VCloseVnodeFiles_r drops the glock internally */
3362     DFlushVolume(V_id(vp));
3363     VCloseVnodeFiles_r(vp);
3364
3365 #ifdef AFS_DEMAND_ATTACH_FS
3366     VOL_UNLOCK;
3367 #endif
3368
3369     /* Too time consuming and unnecessary for the volserver */
3370     if (programType != volumeUtility) {
3371         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3372         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3373         IH_CONDSYNC(vp->diskDataHandle);
3374 #ifdef AFS_NT40_ENV
3375         IH_CONDSYNC(vp->linkHandle);
3376 #endif /* AFS_NT40_ENV */
3377     }
3378
3379     IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
3380     IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
3381     IH_REALLYCLOSE(vp->diskDataHandle);
3382     IH_REALLYCLOSE(vp->linkHandle);
3383
3384 #ifdef AFS_DEMAND_ATTACH_FS
3385     VOL_LOCK;
3386     VChangeState_r(vp, state_save);
3387 #endif
3388 }
3389
3390 /* For both VForceOffline and VOffline, we close all relevant handles.
3391  * For VOffline, if we re-attach the volume, the files may possible be
3392  * different than before. 
3393  */
3394 /* for demand attach, caller MUST hold a ref count on vp */
3395 static void
3396 VReleaseVolumeHandles_r(Volume * vp)
3397 {
3398 #ifdef AFS_DEMAND_ATTACH_FS
3399     VolState state_save;
3400
3401     state_save = VChangeState_r(vp, VOL_STATE_DETACHING);
3402 #endif
3403
3404     /* XXX need to investigate whether we can perform
3405      * DFlushVolume outside of vol_glock_mutex... */
3406     DFlushVolume(V_id(vp));
3407
3408     VReleaseVnodeFiles_r(vp); /* releases the glock internally */
3409
3410 #ifdef AFS_DEMAND_ATTACH_FS
3411     VOL_UNLOCK;
3412 #endif
3413
3414     /* Too time consuming and unnecessary for the volserver */
3415     if (programType != volumeUtility) {
3416         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3417         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3418         IH_CONDSYNC(vp->diskDataHandle);
3419 #ifdef AFS_NT40_ENV
3420         IH_CONDSYNC(vp->linkHandle);
3421 #endif /* AFS_NT40_ENV */
3422     }
3423
3424     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
3425     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
3426     IH_RELEASE(vp->diskDataHandle);
3427     IH_RELEASE(vp->linkHandle);
3428
3429 #ifdef AFS_DEMAND_ATTACH_FS
3430     VOL_LOCK;
3431     VChangeState_r(vp, state_save);
3432 #endif
3433 }
3434
3435
3436 /***************************************************/
3437 /* Volume write and fsync routines                 */
3438 /***************************************************/
3439
3440 void
3441 VUpdateVolume_r(Error * ec, Volume * vp, int flags)
3442 {
3443 #ifdef AFS_DEMAND_ATTACH_FS
3444     VolState state_save;
3445
3446     if (flags & VOL_UPDATE_WAIT) {
3447         VCreateReservation_r(vp);
3448         VWaitExclusiveState_r(vp);
3449     }
3450 #endif
3451
3452     *ec = 0;
3453     if (programType == fileServer)
3454         V_uniquifier(vp) =
3455             (V_inUse(vp) ? V_nextVnodeUnique(vp) +
3456              200 : V_nextVnodeUnique(vp));
3457
3458 #ifdef AFS_DEMAND_ATTACH_FS
3459     state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3460     VOL_UNLOCK;
3461 #endif
3462
3463     WriteVolumeHeader_r(ec, vp);
3464
3465 #ifdef AFS_DEMAND_ATTACH_FS
3466     VOL_LOCK;
3467     VChangeState_r(vp, state_save);
3468     if (flags & VOL_UPDATE_WAIT) {
3469         VCancelReservation_r(vp);
3470     }
3471 #endif
3472
3473     if (*ec) {
3474         Log("VUpdateVolume: error updating volume header, volume %u (%s)\n",
3475             V_id(vp), V_name(vp));
3476         /* try to update on-disk header, 
3477          * while preventing infinite recursion */
3478         if (!(flags & VOL_UPDATE_NOFORCEOFF)) {
3479             VForceOffline_r(vp, VOL_FORCEOFF_NOUPDATE);
3480         }
3481     }
3482 }
3483
3484 void
3485 VUpdateVolume(Error * ec, Volume * vp)
3486 {
3487     VOL_LOCK;
3488     VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3489     VOL_UNLOCK;
3490 }
3491
3492 void
3493 VSyncVolume_r(Error * ec, Volume * vp, int flags)
3494 {
3495     FdHandle_t *fdP;
3496     int code;
3497 #ifdef AFS_DEMAND_ATTACH_FS
3498     VolState state_save;
3499 #endif
3500
3501     if (flags & VOL_SYNC_WAIT) {
3502         VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3503     } else {
3504         VUpdateVolume_r(ec, vp, 0);
3505     }
3506     if (!*ec) {
3507 #ifdef AFS_DEMAND_ATTACH_FS
3508         state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3509         VOL_UNLOCK;
3510 #endif
3511         fdP = IH_OPEN(V_diskDataHandle(vp));
3512         assert(fdP != NULL);
3513         code = FDH_SYNC(fdP);
3514         assert(code == 0);
3515         FDH_CLOSE(fdP);
3516 #ifdef AFS_DEMAND_ATTACH_FS
3517         VOL_LOCK;
3518         VChangeState_r(vp, state_save);
3519 #endif
3520     }
3521 }
3522
3523 void
3524 VSyncVolume(Error * ec, Volume * vp)
3525 {
3526     VOL_LOCK;
3527     VSyncVolume_r(ec, vp, VOL_SYNC_WAIT);
3528     VOL_UNLOCK;
3529 }
3530
3531
3532 /***************************************************/
3533 /* Volume dealloaction routines                    */
3534 /***************************************************/
3535
3536 #ifdef AFS_DEMAND_ATTACH_FS
3537 static void
3538 FreeVolume(Volume * vp)
3539 {
3540     /* free the heap space, iff it's safe.
3541      * otherwise, pull it out of the hash table, so it
3542      * will get deallocated when all refs to it go away */
3543     if (!VCheckFree(vp)) {
3544         DeleteVolumeFromHashTable(vp);
3545         DeleteVolumeFromVByPList_r(vp);
3546
3547         /* make sure we invalidate the header cache entry */
3548         FreeVolumeHeader(vp);
3549     }
3550 }
3551 #endif /* AFS_DEMAND_ATTACH_FS */
3552
3553 static void
3554 ReallyFreeVolume(Volume * vp)
3555 {
3556     int i;
3557     if (!vp)
3558         return;
3559 #ifdef AFS_DEMAND_ATTACH_FS
3560     /* debug */
3561     VChangeState_r(vp, VOL_STATE_FREED);
3562     if (vp->pending_vol_op)
3563         free(vp->pending_vol_op);
3564 #endif /* AFS_DEMAND_ATTACH_FS */
3565     for (i = 0; i < nVNODECLASSES; i++)
3566         if (vp->vnodeIndex[i].bitmap)
3567             free(vp->vnodeIndex[i].bitmap);
3568     FreeVolumeHeader(vp);
3569 #ifndef AFS_DEMAND_ATTACH_FS
3570     DeleteVolumeFromHashTable(vp);
3571 #endif /* AFS_DEMAND_ATTACH_FS */
3572     free(vp);
3573 }
3574
3575 /* check to see if we should shutdown this volume
3576  * returns 1 if volume was freed, 0 otherwise */
3577 #ifdef AFS_DEMAND_ATTACH_FS
3578 static int
3579 VCheckDetach(register Volume * vp)
3580 {
3581     int ret = 0;
3582     Error ec = 0;
3583
3584     if (vp->nUsers || vp->nWaiters)
3585         return ret;
3586
3587     if (vp->shuttingDown) {
3588         ret = 1;
3589         if ((programType != fileServer) &&
3590             (V_inUse(vp) == programType) &&
3591             ((V_checkoutMode(vp) == V_VOLUPD) ||
3592              (V_checkoutMode(vp) == V_SECRETLY) ||
3593              ((V_checkoutMode(vp) == V_CLONE) &&
3594               (VolumeWriteable(vp))))) {
3595             V_inUse(vp) = 0;
3596             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
3597             if (ec) {
3598                 Log("VCheckDetach: volume header update for volume %u "
3599                     "failed with errno %d\n", vp->hashid, errno);
3600             }
3601         }
3602         VReleaseVolumeHandles_r(vp);
3603         VCheckSalvage(vp);
3604         ReallyFreeVolume(vp);
3605         if (programType == fileServer) {
3606             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3607         }
3608     }
3609     return ret;
3610 }
3611 #else /* AFS_DEMAND_ATTACH_FS */
3612 static int
3613 VCheckDetach(register Volume * vp)
3614 {
3615     int ret = 0;
3616     Error ec = 0;
3617
3618     if (vp->nUsers)
3619         return ret;
3620
3621     if (vp->shuttingDown) {
3622         ret = 1;
3623         if ((programType != fileServer) &&
3624             (V_inUse(vp) == programType) &&
3625             ((V_checkoutMode(vp) == V_VOLUPD) ||
3626              (V_checkoutMode(vp) == V_SECRETLY) ||
3627              ((V_checkoutMode(vp) == V_CLONE) &&
3628               (VolumeWriteable(vp))))) {
3629             V_inUse(vp) = 0;
3630             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
3631             if (ec) {
3632                 Log("VCheckDetach: volume header update for volume %u failed with errno %d\n",
3633                     vp->hashid, errno);
3634             }
3635         }
3636         VReleaseVolumeHandles_r(vp);
3637         ReallyFreeVolume(vp);
3638         if (programType == fileServer) {
3639 #if defined(AFS_PTHREAD_ENV)
3640             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3641 #else /* AFS_PTHREAD_ENV */
3642             LWP_NoYieldSignal(VPutVolume);
3643 #endif /* AFS_PTHREAD_ENV */
3644         }
3645     }
3646     return ret;
3647 }
3648 #endif /* AFS_DEMAND_ATTACH_FS */
3649
3650 /* check to see if we should offline this volume
3651  * return 1 if volume went offline, 0 otherwise */
3652 #ifdef AFS_DEMAND_ATTACH_FS
3653 static int
3654 VCheckOffline(register Volume * vp)
3655 {
3656     int ret = 0;
3657
3658     if (vp->goingOffline && !vp->nUsers) {
3659         Error error;
3660         assert(programType == fileServer);
3661         assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
3662                (V_attachState(vp) != VOL_STATE_FREED) &&
3663                (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
3664                (V_attachState(vp) != VOL_STATE_UNATTACHED));
3665
3666         /* valid states:
3667          *
3668          * VOL_STATE_GOING_OFFLINE
3669          * VOL_STATE_SHUTTING_DOWN
3670          * VIsErrorState(V_attachState(vp))
3671          * VIsExclusiveState(V_attachState(vp))
3672          */
3673
3674         VCreateReservation_r(vp);
3675         VChangeState_r(vp, VOL_STATE_OFFLINING);
3676
3677         ret = 1;
3678         /* must clear the goingOffline flag before we drop the glock */
3679         vp->goingOffline = 0;
3680         V_inUse(vp) = 0;
3681
3682         VLRU_Delete_r(vp);
3683
3684         /* perform async operations */
3685         VUpdateVolume_r(&error, vp, 0);
3686         VCloseVolumeHandles_r(vp);
3687
3688         if (LogLevel) {
3689             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3690                 V_name(vp));
3691             if (V_offlineMessage(vp)[0])
3692                 Log(" (%s)", V_offlineMessage(vp));
3693             Log("\n");
3694         }
3695
3696         /* invalidate the volume header cache entry */
3697         FreeVolumeHeader(vp);
3698
3699         /* if nothing changed state to error or salvaging,
3700          * drop state to unattached */
3701         if (!VIsErrorState(V_attachState(vp))) {
3702             VChangeState_r(vp, VOL_STATE_UNATTACHED);
3703         }
3704         VCancelReservation_r(vp);
3705         /* no usage of vp is safe beyond this point */
3706     }
3707     return ret;
3708 }
3709 #else /* AFS_DEMAND_ATTACH_FS */
3710 static int
3711 VCheckOffline(register Volume * vp)
3712 {
3713     int ret = 0;
3714
3715     if (vp->goingOffline && !vp->nUsers) {
3716         Error error;
3717         assert(programType == fileServer);
3718
3719         ret = 1;
3720         vp->goingOffline = 0;
3721         V_inUse(vp) = 0;
3722         VUpdateVolume_r(&error, vp, 0);
3723         VCloseVolumeHandles_r(vp);
3724         if (LogLevel) {
3725             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3726                 V_name(vp));
3727             if (V_offlineMessage(vp)[0])
3728                 Log(" (%s)", V_offlineMessage(vp));
3729             Log("\n");
3730         }
3731         FreeVolumeHeader(vp);
3732 #ifdef AFS_PTHREAD_ENV
3733         assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3734 #else /* AFS_PTHREAD_ENV */
3735         LWP_NoYieldSignal(VPutVolume);
3736 #endif /* AFS_PTHREAD_ENV */
3737     }
3738     return ret;
3739 }
3740 #endif /* AFS_DEMAND_ATTACH_FS */
3741
3742 /***************************************************/
3743 /* demand attach fs ref counting routines          */
3744 /***************************************************/
3745
3746 #ifdef AFS_DEMAND_ATTACH_FS
3747 /* the following two functions handle reference counting for
3748  * asynchronous operations on volume structs.
3749  *
3750  * their purpose is to prevent a VDetachVolume or VShutdown
3751  * from free()ing the Volume struct during an async i/o op */
3752
3753 /* register with the async volume op ref counter */
3754 /* VCreateReservation_r moved into inline code header because it 
3755  * is now needed in vnode.c -- tkeiser 11/20/2007 
3756  */
3757
3758 /**
3759  * decrement volume-package internal refcount.
3760  *
3761  * @param vp  volume object pointer
3762  *
3763  * @internal volume package internal use only
3764  *
3765  * @pre 
3766  *    @arg VOL_LOCK is held
3767  *    @arg lightweight refcount held
3768  *
3769  * @post volume waiters refcount is decremented; volume may
3770  *       have been deallocated/shutdown/offlined/salvaged/
3771  *       whatever during the process
3772  *
3773  * @warning once you have tossed your last reference (you can acquire
3774  *          lightweight refs recursively) it is NOT SAFE to reference
3775  *          a volume object pointer ever again
3776  *
3777  * @see VCreateReservation_r
3778  *
3779  * @note DEMAND_ATTACH_FS only
3780  */
3781 void
3782 VCancelReservation_r(Volume * vp)
3783 {
3784     assert(--vp->nWaiters >= 0);
3785     if (vp->nWaiters == 0) {
3786         VCheckOffline(vp);
3787         if (!VCheckDetach(vp)) {
3788             VCheckSalvage(vp);
3789             VCheckFree(vp);
3790         }
3791     }
3792 }
3793
3794 /* check to see if we should free this volume now
3795  * return 1 if volume was freed, 0 otherwise */
3796 static int
3797 VCheckFree(Volume * vp)
3798 {
3799     int ret = 0;
3800     if ((vp->nUsers == 0) &&
3801         (vp->nWaiters == 0) &&
3802         !(V_attachFlags(vp) & (VOL_IN_HASH | 
3803                                VOL_ON_VBYP_LIST | 
3804                                VOL_IS_BUSY |
3805                                VOL_ON_VLRU))) {
3806         ReallyFreeVolume(vp);
3807         ret = 1;
3808     }
3809     return ret;
3810 }
3811 #endif /* AFS_DEMAND_ATTACH_FS */
3812
3813
3814 /***************************************************/
3815 /* online volume operations routines               */
3816 /***************************************************/
3817
3818 #ifdef AFS_DEMAND_ATTACH_FS
3819 /**
3820  * register a volume operation on a given volume.
3821  *
3822  * @param[in] vp       volume object
3823  * @param[in] vopinfo  volume operation info object
3824  *
3825  * @pre VOL_LOCK is held
3826  *
3827  * @post volume operation info object attached to volume object.
3828  *       volume operation statistics updated.
3829  *
3830  * @note by "attached" we mean a copy of the passed in object is made
3831  *
3832  * @internal volume package internal use only
3833  */
3834 int
3835 VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3836 {
3837     FSSYNC_VolOp_info * info;
3838
3839     /* attach a vol op info node to the volume struct */
3840     info = (FSSYNC_VolOp_info *) malloc(sizeof(FSSYNC_VolOp_info));
3841     assert(info != NULL);
3842     memcpy(info, vopinfo, sizeof(FSSYNC_VolOp_info));
3843     vp->pending_vol_op = info;
3844
3845     /* update stats */
3846     vp->stats.last_vol_op = FT_ApproxTime();
3847     vp->stats.vol_ops++;
3848     IncUInt64(&VStats.vol_ops);
3849
3850     return 0;
3851 }
3852
3853 /**
3854  * deregister the volume operation attached to this volume.
3855  *
3856  * @param[in] vp  volume object pointer
3857  *
3858  * @pre VOL_LOCK is held
3859  *
3860  * @post the volume operation info object is detached from the volume object
3861  *
3862  * @internal volume package internal use only
3863  */
3864 int
3865 VDeregisterVolOp_r(Volume * vp)
3866 {
3867     if (vp->pending_vol_op) {
3868         free(vp->pending_vol_op);
3869         vp->pending_vol_op = NULL;
3870     }
3871     return 0;
3872 }
3873 #endif /* AFS_DEMAND_ATTACH_FS */
3874
3875 /**
3876  * determine whether it is safe to leave a volume online during
3877  * the volume operation described by the vopinfo object.
3878  *
3879  * @param[in] vp        volume object
3880  * @param[in] vopinfo   volume operation info object
3881  *
3882  * @return whether it is safe to leave volume online
3883  *    @retval 0  it is NOT SAFE to leave the volume online
3884  *    @retval 1  it is safe to leave the volume online during the operation
3885  *
3886  * @pre
3887  *    @arg VOL_LOCK is held
3888  *    @arg disk header attached to vp (heavyweight ref on vp will guarantee
3889  *         this condition is met)
3890  *
3891  * @internal volume package internal use only
3892  */
3893 int
3894 VVolOpLeaveOnline_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3895 {
3896     return (vopinfo->vol_op_state == FSSYNC_VolOpRunningOnline ||
3897             (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3898             (vopinfo->com.reason == V_READONLY ||
3899              (!VolumeWriteable(vp) &&
3900               (vopinfo->com.reason == V_CLONE ||
3901                vopinfo->com.reason == V_DUMP)))));
3902 }
3903
3904 /**
3905  * determine whether VBUSY should be set during this volume operation.
3906  *
3907  * @param[in] vp        volume object
3908  * @param[in] vopinfo   volume operation info object
3909  *
3910  * @return whether VBUSY should be set
3911  *   @retval 0  VBUSY does NOT need to be set
3912  *   @retval 1  VBUSY SHOULD be set
3913  *
3914  * @pre VOL_LOCK is held
3915  *
3916  * @internal volume package internal use only
3917  */
3918 int
3919 VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3920 {
3921     return ((vopinfo->com.command == FSYNC_VOL_OFF &&
3922             vopinfo->com.reason == FSYNC_SALVAGE) ||
3923             (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3924             (vopinfo->com.reason == V_CLONE ||
3925              vopinfo->com.reason == V_DUMP)));
3926 }
3927
3928
3929 /***************************************************/
3930 /* online salvager routines                        */
3931 /***************************************************/
3932 #if defined(AFS_DEMAND_ATTACH_FS)
3933 #define SALVAGE_PRIO_UPDATE_INTERVAL 3      /**< number of seconds between prio updates */
3934 #define SALVAGE_COUNT_MAX 16                /**< number of online salvages we
3935                                              *   allow before moving the volume
3936                                              *   into a permanent error state
3937                                              *
3938                                              *   once this threshold is reached,
3939                                              *   the operator will have to manually
3940                                              *   issue a 'bos salvage' to bring
3941                                              *   the volume back online
3942                                              */
3943
3944 /**
3945  * check whether a salvage needs to be performed on this volume.
3946  *
3947  * @param[in] vp   pointer to volume object
3948  *
3949  * @return status code
3950  *    @retval 0 no salvage scheduled
3951  *    @retval 1 a salvage has been scheduled with the salvageserver
3952  *
3953  * @pre VOL_LOCK is held
3954  *
3955  * @post if salvage request flag is set and nUsers and nWaiters are zero,
3956  *       then a salvage will be requested
3957  *
3958  * @note this is one of the event handlers called by VCancelReservation_r
3959  *
3960  * @see VCancelReservation_r
3961  *
3962  * @internal volume package internal use only.
3963  */
3964 static int
3965 VCheckSalvage(register Volume * vp)
3966 {
3967     int ret = 0;
3968 #ifdef SALVSYNC_BUILD_CLIENT
3969     if (vp->nUsers || vp->nWaiters)
3970         return ret;
3971     if (vp->salvage.requested) {
3972         VScheduleSalvage_r(vp);
3973         ret = 1;
3974     }
3975 #endif /* SALVSYNC_BUILD_CLIENT */
3976     return ret;
3977 }
3978
3979 /**
3980  * request volume salvage.
3981  *
3982  * @param[out] ec      computed client error code
3983  * @param[in]  vp      volume object pointer
3984  * @param[in]  reason  reason code (passed to salvageserver via SALVSYNC)
3985  * @param[in]  flags   see flags note below
3986  *
3987  * @note flags:
3988  *       VOL_SALVAGE_INVALIDATE_HEADER causes volume header cache entry 
3989  *                                     to be invalidated.
3990  *
3991  * @pre VOL_LOCK is held.
3992  *
3993  * @post volume state is changed.
3994  *       for fileserver, salvage will be requested once refcount reaches zero.
3995  *
3996  * @return operation status code
3997  *   @retval 0  volume salvage will occur
3998  *   @retval 1  volume salvage could not be scheduled
3999  *
4000  * @note DAFS fileserver only
4001  *
4002  * @note this call does not synchronously schedule a volume salvage.  rather,
4003  *       it sets volume state so that when volume refcounts reach zero, a
4004  *       volume salvage will occur.  by "refcounts", we mean both nUsers and 
4005  *       nWaiters must be zero.
4006  *
4007  * @internal volume package internal use only.
4008  */
4009 int
4010 VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
4011 {
4012     int code = 0;
4013     /*
4014      * for DAFS volume utilities, transition to error state
4015      * (at some point in the future, we should consider
4016      *  making volser talk to salsrv)
4017      */
4018     if (programType != fileServer) {
4019         VChangeState_r(vp, VOL_STATE_ERROR);
4020         *ec = VSALVAGE;
4021         return 1;
4022     }
4023
4024     if (!vp->salvage.requested) {
4025         vp->salvage.requested = 1;
4026         vp->salvage.reason = reason;
4027         vp->stats.last_salvage = FT_ApproxTime();
4028         if (VIsSalvager(V_inUse(vp))) {
4029             Log("VRequestSalvage: volume %u appears to be salvaging, but we\n", vp->hashid);
4030             Log("  didn't request a salvage. Forcing it offline waiting for the\n");
4031             Log("  salvage to finish; if you are sure no salvage is running,\n");
4032             Log("  run a salvage manually.\n");
4033
4034             /* make sure neither VScheduleSalvage_r nor
4035              * VUpdateSalvagePriority_r try to schedule another salvage */
4036             vp->salvage.requested = vp->salvage.scheduled = 0;
4037
4038             /* these stats aren't correct, but doing this makes them
4039              * slightly closer to being correct */
4040             vp->stats.salvages++;
4041             vp->stats.last_salvage_req = FT_ApproxTime();
4042             IncUInt64(&VStats.salvages);
4043
4044             VChangeState_r(vp, VOL_STATE_ERROR);
4045             *ec = VSALVAGE;
4046             code = 1;
4047
4048         } else if (vp->stats.salvages < SALVAGE_COUNT_MAX) {
4049             VChangeState_r(vp, VOL_STATE_SALVAGING);
4050             *ec = VSALVAGING;
4051         } else {
4052             Log("VRequestSalvage: volume %u online salvaged too many times; forced offline.\n", vp->hashid);
4053             VChangeState_r(vp, VOL_STATE_ERROR);
4054             *ec = VSALVAGE;
4055             code = 1;
4056         }
4057         if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
4058             /* Instead of ReleaseVolumeHeader, we do FreeVolumeHeader() 
4059                so that the the next VAttachVolumeByVp_r() invocation 
4060                of attach2() will pull in a cached header 
4061                entry and fail, then load a fresh one from disk and attach 
4062                it to the volume.             
4063             */
4064             FreeVolumeHeader(vp);
4065         }
4066     }
4067     return code;
4068 }
4069
4070 /**
4071  * update salvageserver scheduling priority for a volume.
4072  *
4073  * @param[in] vp  pointer to volume object
4074  *
4075  * @return operation status
4076  *   @retval 0  success
4077  *   @retval 1  request denied, or SALVSYNC communications failure
4078  *
4079  * @pre VOL_LOCK is held.
4080  *
4081  * @post in-core salvage priority counter is incremented.  if at least
4082  *       SALVAGE_PRIO_UPDATE_INTERVAL seconds have elapsed since the
4083  *       last SALVSYNC_RAISEPRIO request, we contact the salvageserver
4084  *       to update its priority queue.  if no salvage is scheduled,
4085  *       this function is a no-op.
4086  *
4087  * @note DAFS fileserver only
4088  *
4089  * @note this should be called whenever a VGetVolume fails due to a 
4090  *       pending salvage request
4091  *
4092  * @todo should set exclusive state and drop glock around salvsync call
4093  *
4094  * @internal volume package internal use only.
4095  */
4096 static int
4097 VUpdateSalvagePriority_r(Volume * vp)
4098 {
4099     int ret=0;
4100
4101 #ifdef SALVSYNC_BUILD_CLIENT
4102     afs_uint32 now;
4103     int code;
4104
4105     vp->salvage.prio++;
4106     now = FT_ApproxTime();
4107
4108     /* update the salvageserver priority queue occasionally so that
4109      * frequently requested volumes get moved to the head of the queue 
4110      */
4111     if ((vp->salvage.scheduled) &&
4112         (vp->stats.last_salvage_req < (now-SALVAGE_PRIO_UPDATE_INTERVAL))) {
4113         code = SALVSYNC_SalvageVolume(vp->hashid,
4114                                       VPartitionPath(vp->partition),
4115                                       SALVSYNC_RAISEPRIO,
4116                                       vp->salvage.reason,
4117                                       vp->salvage.prio,
4118                                       NULL);
4119         vp->stats.last_salvage_req = now;
4120         if (code != SYNC_OK) {
4121             ret = 1;
4122         }
4123     }
4124 #endif /* SALVSYNC_BUILD_CLIENT */
4125     return ret;
4126 }
4127
4128
4129 #ifdef SALVSYNC_BUILD_CLIENT
4130 /**
4131  * schedule a salvage with the salvage server.
4132  *
4133  * @param[in] vp  pointer to volume object
4134  *
4135  * @return operation status
4136  *    @retval 0 salvage scheduled successfully
4137  *    @retval 1 salvage not scheduled, or SALVSYNC com error
4138  *
4139  * @pre 
4140  *    @arg VOL_LOCK is held.
4141  *    @arg nUsers and nWaiters should be zero.
4142  *
4143  * @post salvageserver is sent a salvage request
4144  *
4145  * @note DAFS fileserver only
4146  *
4147  * @internal volume package internal use only.
4148  */
4149 static int
4150 VScheduleSalvage_r(Volume * vp)
4151 {
4152     int ret=0;
4153     int code;
4154     VolState state_save;
4155     VThreadOptions_t * thread_opts;
4156     char partName[16];
4157
4158     if (vp->nWaiters || vp->nUsers) {
4159         return 1;
4160     }
4161
4162     /* prevent endless salvage,attach,salvage,attach,... loops */
4163     if (vp->stats.salvages >= SALVAGE_COUNT_MAX)
4164         return 1;
4165
4166     /*
4167      * don't perform salvsync ops on certain threads
4168      */
4169     thread_opts = pthread_getspecific(VThread_key);
4170     if (thread_opts == NULL) {
4171         thread_opts = &VThread_defaults;
4172     }
4173     if (thread_opts->disallow_salvsync) {
4174         return 1;
4175     }
4176
4177     /*
4178      * XXX the scheduling process should really be done asynchronously
4179      *     to avoid fssync deadlocks
4180      */
4181     if (!vp->salvage.scheduled) {
4182         /* if we haven't previously scheduled a salvage, do so now 
4183          *
4184          * set the volume to an exclusive state and drop the lock
4185          * around the SALVSYNC call
4186          *
4187          * note that we do NOT acquire a reservation here -- doing so
4188          * could result in unbounded recursion
4189          */
4190         strlcpy(partName, VPartitionPath(vp->partition), sizeof(partName));
4191         state_save = VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
4192         VOL_UNLOCK;
4193
4194         /* can't use V_id() since there's no guarantee
4195          * we have the disk data header at this point */
4196         code = SALVSYNC_SalvageVolume(vp->hashid,
4197                                       partName,
4198                                       SALVSYNC_SALVAGE,
4199                                       vp->salvage.reason,
4200                                       vp->salvage.prio,
4201                                       NULL);
4202         VOL_LOCK;
4203         VChangeState_r(vp, state_save);
4204
4205         if (code == SYNC_OK) {
4206             vp->salvage.scheduled = 1;
4207             vp->stats.salvages++;
4208             vp->stats.last_salvage_req = FT_ApproxTime();
4209             IncUInt64(&VStats.salvages);
4210         } else {
4211             ret = 1;
4212             switch(code) {
4213             case SYNC_BAD_COMMAND:
4214             case SYNC_COM_ERROR:
4215                 break;
4216             case SYNC_DENIED:
4217                 Log("VScheduleSalvage_r:  SALVSYNC request denied\n");
4218                 break;
4219             default:
4220                 Log("VScheduleSalvage_r:  SALVSYNC unknown protocol error\n");
4221                 break;
4222             }
4223         }
4224     }
4225     return ret;
4226 }
4227
4228 /**
4229  * connect to the salvageserver SYNC service.
4230  *
4231  * @return operation status
4232  *    @retval 0 failure
4233  *    @retval 1 success
4234  *
4235  * @post connection to salvageserver SYNC service established
4236  *
4237  * @see VConnectSALV_r
4238  * @see VDisconnectSALV
4239  * @see VReconnectSALV
4240  */
4241 int
4242 VConnectSALV(void)
4243 {
4244     int retVal;
4245     VOL_LOCK;
4246     retVal = VConnectSALV_r();
4247     VOL_UNLOCK;
4248     return retVal;
4249 }
4250
4251 /**
4252  * connect to the salvageserver SYNC service.
4253  *
4254  * @return operation status
4255  *    @retval 0 failure
4256  *    @retval 1 success
4257  *
4258  * @pre VOL_LOCK is held.
4259  *
4260  * @post connection to salvageserver SYNC service established
4261  *
4262  * @see VConnectSALV
4263  * @see VDisconnectSALV_r
4264  * @see VReconnectSALV_r
4265  * @see SALVSYNC_clientInit
4266  *
4267  * @internal volume package internal use only.
4268  */
4269 int
4270 VConnectSALV_r(void)
4271 {
4272     return SALVSYNC_clientInit();
4273 }
4274
4275 /**
4276  * disconnect from the salvageserver SYNC service.
4277  *
4278  * @return operation status
4279  *    @retval 0 success
4280  *
4281  * @pre client should have a live connection to the salvageserver
4282  *
4283  * @post connection to salvageserver SYNC service destroyed
4284  *
4285  * @see VDisconnectSALV_r
4286  * @see VConnectSALV
4287  * @see VReconnectSALV
4288  */
4289 int
4290 VDisconnectSALV(void)
4291 {
4292     VOL_LOCK;
4293     VDisconnectSALV_r();
4294     VOL_UNLOCK;
4295     return 0;
4296 }
4297
4298 /**
4299  * disconnect from the salvageserver SYNC service.
4300  *
4301  * @return operation status
4302  *    @retval 0 success
4303  *
4304  * @pre 
4305  *    @arg VOL_LOCK is held.
4306  *    @arg client should have a live connection to the salvageserver.
4307  *
4308  * @post connection to salvageserver SYNC service destroyed
4309  *
4310  * @see VDisconnectSALV
4311  * @see VConnectSALV_r
4312  * @see VReconnectSALV_r
4313  * @see SALVSYNC_clientFinis
4314  *
4315  * @internal volume package internal use only.
4316  */
4317 int
4318 VDisconnectSALV_r(void)
4319
4320     return SALVSYNC_clientFinis();
4321 }
4322
4323 /**
4324  * disconnect and then re-connect to the salvageserver SYNC service.
4325  *
4326  * @return operation status
4327  *    @retval 0 failure
4328  *    @retval 1 success
4329  *
4330  * @pre client should have a live connection to the salvageserver
4331  *
4332  * @post old connection is dropped, and a new one is established
4333  *
4334  * @see VConnectSALV
4335  * @see VDisconnectSALV
4336  * @see VReconnectSALV_r
4337  */
4338 int
4339 VReconnectSALV(void)
4340 {
4341     int retVal;
4342     VOL_LOCK;
4343     retVal = VReconnectSALV_r();
4344     VOL_UNLOCK;
4345     return retVal;
4346 }
4347
4348 /**
4349  * disconnect and then re-connect to the salvageserver SYNC service.
4350  *
4351  * @return operation status
4352  *    @retval 0 failure
4353  *    @retval 1 success
4354  *
4355  * @pre 
4356  *    @arg VOL_LOCK is held.
4357  *    @arg client should have a live connection to the salvageserver.
4358  *
4359  * @post old connection is dropped, and a new one is established
4360  *
4361  * @see VConnectSALV_r
4362  * @see VDisconnectSALV
4363  * @see VReconnectSALV
4364  * @see SALVSYNC_clientReconnect
4365  *
4366  * @internal volume package internal use only.
4367  */
4368 int
4369 VReconnectSALV_r(void)
4370 {
4371     return SALVSYNC_clientReconnect();
4372 }
4373 #endif /* SALVSYNC_BUILD_CLIENT */
4374 #endif /* AFS_DEMAND_ATTACH_FS */
4375
4376
4377 /***************************************************/
4378 /* FSSYNC routines                                 */
4379 /***************************************************/
4380
4381 /* This must be called by any volume utility which needs to run while the
4382    file server is also running.  This is separated from VInitVolumePackage so
4383    that a utility can fork--and each of the children can independently
4384    initialize communication with the file server */
4385 #ifdef FSSYNC_BUILD_CLIENT
4386 /**
4387  * connect to the fileserver SYNC service.
4388  *
4389  * @return operation status
4390  *    @retval 0 failure
4391  *    @retval 1 success
4392  *
4393  * @pre 
4394  *    @arg VInit must equal 2.
4395  *    @arg Program Type must not be fileserver or salvager.
4396  *
4397  * @post connection to fileserver SYNC service established
4398  *
4399  * @see VConnectFS_r
4400  * @see VDisconnectFS
4401  * @see VChildProcReconnectFS
4402  */
4403 int
4404 VConnectFS(void)
4405 {
4406     int retVal;
4407     VOL_LOCK;
4408     retVal = VConnectFS_r();
4409     VOL_UNLOCK;
4410     return retVal;
4411 }
4412
4413 /**
4414  * connect to the fileserver SYNC service.
4415  *
4416  * @return operation status
4417  *    @retval 0 failure
4418  *    @retval 1 success
4419  *
4420  * @pre 
4421  *    @arg VInit must equal 2.
4422  *    @arg Program Type must not be fileserver or salvager.
4423  *    @arg VOL_LOCK is held.
4424  *
4425  * @post connection to fileserver SYNC service established
4426  *
4427  * @see VConnectFS
4428  * @see VDisconnectFS_r
4429  * @see VChildProcReconnectFS_r
4430  *
4431  * @internal volume package internal use only.
4432  */
4433 int
4434 VConnectFS_r(void)
4435 {
4436     int rc;
4437     assert((VInit == 2) && 
4438            (programType != fileServer) &&
4439            (programType != salvager));
4440     rc = FSYNC_clientInit();
4441     if (rc)
4442         VInit = 3;
4443     return rc;
4444 }
4445
4446 /**
4447  * disconnect from the fileserver SYNC service.
4448  *
4449  * @pre 
4450  *    @arg client should have a live connection to the fileserver.
4451  *    @arg VOL_LOCK is held.
4452  *    @arg Program Type must not be fileserver or salvager.
4453  *
4454  * @post connection to fileserver SYNC service destroyed
4455  *
4456  * @see VDisconnectFS
4457  * @see VConnectFS_r
4458  * @see VChildProcReconnectFS_r
4459  *
4460  * @internal volume package internal use only.
4461  */
4462 void
4463 VDisconnectFS_r(void)
4464 {
4465     assert((programType != fileServer) &&
4466            (programType != salvager));
4467     FSYNC_clientFinis();
4468     VInit = 2;
4469 }
4470
4471 /**
4472  * disconnect from the fileserver SYNC service.
4473  *
4474  * @pre
4475  *    @arg client should have a live connection to the fileserver.
4476  *    @arg Program Type must not be fileserver or salvager.
4477  *
4478  * @post connection to fileserver SYNC service destroyed
4479  *
4480  * @see VDisconnectFS_r
4481  * @see VConnectFS
4482  * @see VChildProcReconnectFS
4483  */
4484 void
4485 VDisconnectFS(void)
4486 {
4487     VOL_LOCK;
4488     VDisconnectFS_r();
4489     VOL_UNLOCK;
4490 }
4491
4492 /**
4493  * connect to the fileserver SYNC service from a child process following a fork.
4494  *
4495  * @return operation status
4496  *    @retval 0 failure
4497  *    @retval 1 success
4498  *
4499  * @pre
4500  *    @arg VOL_LOCK is held.
4501  *    @arg current FSYNC handle is shared with a parent process
4502  *
4503  * @post current FSYNC handle is discarded and a new connection to the
4504  *       fileserver SYNC service is established
4505  *
4506  * @see VChildProcReconnectFS
4507  * @see VConnectFS_r
4508  * @see VDisconnectFS_r
4509  *
4510  * @internal volume package internal use only.
4511  */
4512 int
4513 VChildProcReconnectFS_r(void)
4514 {
4515     return FSYNC_clientChildProcReconnect();
4516 }
4517
4518 /**
4519  * connect to the fileserver SYNC service from a child process following a fork.
4520  *
4521  * @return operation status
4522  *    @retval 0 failure
4523  *    @retval 1 success
4524  *
4525  * @pre current FSYNC handle is shared with a parent process
4526  *
4527  * @post current FSYNC handle is discarded and a new connection to the
4528  *       fileserver SYNC service is established
4529  *
4530  * @see VChildProcReconnectFS_r
4531  * @see VConnectFS
4532  * @see VDisconnectFS
4533  */
4534 int
4535 VChildProcReconnectFS(void)
4536 {
4537     int ret;
4538     VOL_LOCK;
4539     ret = VChildProcReconnectFS_r();
4540     VOL_UNLOCK;
4541     return ret;
4542 }
4543 #endif /* FSSYNC_BUILD_CLIENT */
4544
4545
4546 /***************************************************/
4547 /* volume bitmap routines                          */
4548 /***************************************************/
4549
4550 /*
4551  * For demand attach fs, flags parameter controls
4552  * locking behavior.  If (flags & VOL_ALLOC_BITMAP_WAIT)
4553  * is set, then this function will create a reservation
4554  * and block on any other exclusive operations.  Otherwise,
4555  * this function assumes the caller already has exclusive
4556  * access to vp, and we just change the volume state.
4557  */
4558 VnodeId
4559 VAllocBitmapEntry_r(Error * ec, Volume * vp, 
4560                     struct vnodeIndex *index, int flags)
4561 {
4562     VnodeId ret;
4563     register byte *bp, *ep;
4564 #ifdef AFS_DEMAND_ATTACH_FS
4565     VolState state_save;
4566 #endif /* AFS_DEMAND_ATTACH_FS */
4567
4568     *ec = 0;
4569
4570     /* This test is probably redundant */
4571     if (!VolumeWriteable(vp)) {
4572         *ec = (bit32) VREADONLY;
4573         return 0;
4574     }
4575
4576 #ifdef AFS_DEMAND_ATTACH_FS
4577     if (flags & VOL_ALLOC_BITMAP_WAIT) {
4578         VCreateReservation_r(vp);
4579         VWaitExclusiveState_r(vp);
4580     }
4581     state_save = VChangeState_r(vp, VOL_STATE_GET_BITMAP);