not-cplusplus-20050530
[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
10 /* 1/1/89: NB:  this stuff is all going to be replaced.  Don't take it too seriously */
11 /*
12
13         System:         VICE-TWO
14         Module:         volume.c
15         Institution:    The Information Technology Center, Carnegie-Mellon University
16
17  */
18
19 #include <afsconfig.h>
20 #include <afs/param.h>
21
22 RCSID
23     ("$Header$");
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 #ifdef AFS_SGI_EFS_IOPS_ENV
84 #define ROOTINO EFS_ROOTINO
85 #include <sys/fs/efs.h>
86 #include "sgiefs/efs.h"         /* until 5.1 release */
87 #endif
88
89
90 #else
91 #ifndef AFS_LINUX20_ENV
92 #include <fstab.h>              /* Need to find in libc 5, present in libc 6 */
93 #endif
94 #endif
95 #endif /* AFS_SGI_ENV */
96 #endif
97 #endif /* AFS_HPUX_ENV */
98 #endif
99 #ifndef AFS_NT40_ENV
100 #include <netdb.h>
101 #include <netinet/in.h>
102 #include <sys/wait.h>
103 #include <setjmp.h>
104 #ifndef ITIMER_REAL
105 #include <sys/time.h>
106 #endif /* ITIMER_REAL */
107 #endif /* AFS_NT40_ENV */
108 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
109 #include <string.h>
110 #else
111 #include <strings.h>
112 #endif
113
114 #include "nfs.h"
115 #include <afs/errors.h>
116 #include "lock.h"
117 #include "lwp.h"
118 #include <afs/afssyscalls.h>
119 #include "ihandle.h"
120 #include <afs/afsutil.h>
121 #ifdef AFS_NT40_ENV
122 #include <io.h>
123 #endif
124 #include "vnode.h"
125 #include "volume.h"
126 #include "partition.h"
127 #ifdef AFS_PTHREAD_ENV
128 #include <assert.h>
129 #else /* AFS_PTHREAD_ENV */
130 #include "afs/assert.h"
131 #endif /* AFS_PTHREAD_ENV */
132 #include "vutils.h"
133 #include "fssync.h"
134 #ifndef AFS_NT40_ENV
135 #include <unistd.h>
136 #endif
137
138 #ifdef O_LARGEFILE
139 #define afs_stat        stat64
140 #define afs_fstat       fstat64
141 #define afs_open        open64
142 #else /* !O_LARGEFILE */
143 #define afs_stat        stat
144 #define afs_fstat       fstat
145 #define afs_open        open
146 #endif /* !O_LARGEFILE */
147
148 #ifdef AFS_PTHREAD_ENV
149 pthread_mutex_t vol_glock_mutex;
150 pthread_mutex_t vol_attach_mutex;
151 pthread_mutex_t vol_fsync_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_OSF_ENV
159 extern void *calloc(), *realloc();
160 #endif
161
162 /*@printflike@*/ extern void Log(const char *format, ...);
163
164 /* Forward declarations */
165 static Volume *attach2(Error * ec, char *path,
166                        register struct VolumeHeader *header,
167                        struct DiskPartition *partp, int isbusy);
168 static void FreeVolume(Volume * vp);
169 static void VScanUpdateList(void);
170 static void InitLRU(int howMany);
171 static int GetVolumeHeader(register Volume * vp);
172 static void ReleaseVolumeHeader(register struct volHeader *hd);
173 static void FreeVolumeHeader(register Volume * vp);
174 static void AddVolumeToHashTable(register Volume * vp, int hashid);
175 static void DeleteVolumeFromHashTable(register Volume * vp);
176 static int VHold(Volume * vp);
177 static int VHold_r(Volume * vp);
178 static void GetBitmap(Error * ec, Volume * vp, VnodeClass class);
179 static void GetVolumePath(Error * ec, VolId volumeId, char **partitionp,
180                           char **namep);
181 static void VReleaseVolumeHandles_r(Volume * vp);
182 static void VCloseVolumeHandles_r(Volume * vp);
183
184 int LogLevel;                   /* Vice loglevel--not defined as extern so that it will be
185                                  * defined when not linked with vice, XXXX */
186 ProgramType programType;        /* The type of program using the package */
187
188
189 #define VOLUME_BITMAP_GROWSIZE  16      /* bytes, => 128vnodes */
190                                         /* Must be a multiple of 4 (1 word) !! */
191 #define VOLUME_HASH_TABLE_SIZE 128      /* Must be a power of 2!! */
192 #define VOLUME_HASH(volumeId) (volumeId&(VOLUME_HASH_TABLE_SIZE-1))
193 private Volume *VolumeHashTable[VOLUME_HASH_TABLE_SIZE];
194
195 #ifndef AFS_HAVE_FFS
196 /* This macro is used where an ffs() call does not exist. Was in util/ffs.c */
197 ffs(x)
198 {
199     afs_int32 ffs_i;
200     afs_int32 ffs_tmp = x;
201     if (ffs_tmp == 0)
202         return (-1);
203     else
204         for (ffs_i = 1;; ffs_i++) {
205             if (ffs_tmp & 1)
206                 return (ffs_i);
207             else
208                 ffs_tmp >>= 1;
209         }
210 }
211 #endif /* !AFS_HAVE_FFS */
212
213 #ifdef AFS_PTHREAD_ENV
214 #include "rx/rx_queue.h"
215 typedef struct diskpartition_queue_t {
216     struct rx_queue queue;
217     struct DiskPartition * diskP;
218 } diskpartition_queue_t;
219 typedef struct vinitvolumepackage_thread_t {
220     struct rx_queue queue;
221     pthread_cond_t thread_done_cv;
222     int n_threads_complete;
223 } vinitvolumepackage_thread_t;
224 static void * VInitVolumePackageThread(void * args);
225 #endif /* AFS_PTHREAD_ENV */
226
227 struct Lock vol_listLock;       /* Lock obtained when listing volumes:  prevents a volume from being missed if the volume is attached during a list volumes */
228
229 extern struct Lock FSYNC_handler_lock;
230
231 static int TimeZoneCorrection;  /* Number of seconds west of GMT */
232
233 /* Common message used when the volume goes off line */
234 char *VSalvageMessage =
235     "Files in this volume are currently unavailable; call operations";
236
237 int VInit;                      /* 0 - uninitialized,
238                                  * 1 - initialized but not all volumes have been attached,
239                                  * 2 - initialized and all volumes have been attached,
240                                  * 3 - initialized, all volumes have been attached, and
241                                  * VConnectFS() has completed. */
242
243
244 bit32 VolumeCacheCheck;         /* Incremented everytime a volume goes on line--
245                                  * used to stamp volume headers and in-core
246                                  * vnodes.  When the volume goes on-line the
247                                  * vnode will be invalidated
248                                  * access only with VOL_LOCK held */
249
250 int VolumeCacheSize = 200, VolumeGets = 0, VolumeReplacements = 0, Vlooks = 0;
251
252
253 int
254 VInitVolumePackage(ProgramType pt, int nLargeVnodes, int nSmallVnodes,
255                    int connect, int volcache)
256 {
257     int errors = 0;             /* Number of errors while finding vice partitions. */
258     struct timeval tv;
259     struct timezone tz;
260
261     programType = pt;
262
263 #ifdef AFS_PTHREAD_ENV
264     assert(pthread_mutex_init(&vol_glock_mutex, NULL) == 0);
265     assert(pthread_mutex_init(&vol_attach_mutex, NULL) == 0);
266     assert(pthread_mutex_init(&vol_fsync_mutex, NULL) == 0);
267     assert(pthread_mutex_init(&vol_trans_mutex, NULL) == 0);
268     assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
269     assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
270 #else /* AFS_PTHREAD_ENV */
271     IOMGR_Initialize();
272 #endif /* AFS_PTHREAD_ENV */
273     Lock_Init(&vol_listLock);
274     Lock_Init(&FSYNC_handler_lock);
275     srandom(time(0));           /* For VGetVolumeInfo */
276     gettimeofday(&tv, &tz);
277     TimeZoneCorrection = tz.tz_minuteswest * 60;
278
279     /* Ok, we have done enough initialization that fileserver can 
280      * start accepting calls, even though the volumes may not be 
281      * available just yet.
282      */
283     VInit = 1;
284
285     if (programType == fileServer) {
286         /* File server or "stand" */
287         FSYNC_fsInit();
288     }
289
290     if (volcache > VolumeCacheSize)
291         VolumeCacheSize = volcache;
292     InitLRU(VolumeCacheSize);
293
294     VInitVnodes(vLarge, nLargeVnodes);
295     VInitVnodes(vSmall, nSmallVnodes);
296
297
298     errors = VAttachPartitions();
299     if (errors)
300         return -1;
301
302     if (programType == fileServer) {
303         struct DiskPartition *diskP;
304 #ifdef AFS_PTHREAD_ENV
305         struct vinitvolumepackage_thread_t params;
306         struct diskpartition_queue_t * dpq;
307         int i, len;
308         pthread_t tid;
309         pthread_attr_t attrs;
310
311         assert(pthread_cond_init(&params.thread_done_cv,NULL) == 0);
312         queue_Init(&params);
313         params.n_threads_complete = 0;
314
315         /* create partition work queue */
316         for (len=0, diskP = DiskPartitionList; diskP; diskP = diskP->next, len++) {
317             dpq = (diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
318             assert(dpq != NULL);
319             dpq->diskP = diskP;
320             queue_Prepend(&params,dpq);
321         }
322
323         assert(pthread_attr_init(&attrs) == 0);
324         assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
325
326         len = MIN(len, vol_attach_threads);
327         
328         VOL_LOCK;
329         for (i=0; i < len; i++) {
330             assert(pthread_create
331                    (&tid, &attrs, &VInitVolumePackageThread,
332                     &params) == 0);
333         }
334
335         while(params.n_threads_complete < len) {
336             pthread_cond_wait(&params.thread_done_cv,&vol_glock_mutex);
337         }
338         VOL_UNLOCK;
339
340         assert(pthread_cond_destroy(&params.thread_done_cv) == 0);
341
342 #else /* AFS_PTHREAD_ENV */
343         DIR *dirp;
344         struct dirent *dp;
345
346         /* Attach all the volumes in this partition */
347         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
348             int nAttached = 0, nUnattached = 0;
349             Log("Partition %s: attaching volumes\n", diskP->name);
350             dirp = opendir(VPartitionPath(diskP));
351             assert(dirp);
352             while ((dp = readdir(dirp))) {
353                 char *p;
354                 p = strrchr(dp->d_name, '.');
355                 if (p != NULL && strcmp(p, VHDREXT) == 0) {
356                     Error error;
357                     Volume *vp;
358                     vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
359                                              V_VOLUPD);
360                     (*(vp ? &nAttached : &nUnattached))++;
361                     if (error == VOFFLINE)
362                         Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
363                     else if (LogLevel >= 5) {
364                         Log("Partition %s: attached volume %d (%s)\n",
365                             diskP->name, VolumeNumber(dp->d_name),
366                             dp->d_name);
367                     }
368                     if (vp) {
369                         VPutVolume(vp);
370                     }
371                 }
372             }
373             Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, nAttached, nUnattached);
374             closedir(dirp);
375         }
376 #endif /* AFS_PTHREAD_ENV */
377     }
378
379     VInit = 2;                  /* Initialized, and all volumes have been attached */
380     if (programType == volumeUtility && connect) {
381         if (!VConnectFS()) {
382             Log("Unable to connect to file server; aborted\n");
383             Lock_Destroy(&FSYNC_handler_lock);
384             exit(1);
385         }
386     }
387     return 0;
388 }
389
390 #ifdef AFS_PTHREAD_ENV
391 static void *
392 VInitVolumePackageThread(void * args) {
393     int errors = 0;             /* Number of errors while finding vice partitions. */
394
395     DIR *dirp;
396     struct dirent *dp;
397     struct DiskPartition *diskP;
398     struct vinitvolumepackage_thread_t * params;
399     struct diskpartition_queue_t * dpq;
400
401     params = (vinitvolumepackage_thread_t *) args;
402
403
404     VOL_LOCK;
405     /* Attach all the volumes in this partition */
406     while (queue_IsNotEmpty(params)) {
407         int nAttached = 0, nUnattached = 0;
408
409         dpq = queue_First(params,diskpartition_queue_t);
410         queue_Remove(dpq);
411         VOL_UNLOCK;
412         diskP = dpq->diskP;
413         free(dpq);
414
415         Log("Partition %s: attaching volumes\n", diskP->name);
416         dirp = opendir(VPartitionPath(diskP));
417         assert(dirp);
418         while ((dp = readdir(dirp))) {
419             char *p;
420             p = strrchr(dp->d_name, '.');
421             if (p != NULL && strcmp(p, VHDREXT) == 0) {
422                 Error error;
423                 Volume *vp;
424                 vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
425                                          V_VOLUPD);
426                 (*(vp ? &nAttached : &nUnattached))++;
427                 if (error == VOFFLINE)
428                     Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
429                 else if (LogLevel >= 5) {
430                     Log("Partition %s: attached volume %d (%s)\n",
431                         diskP->name, VolumeNumber(dp->d_name),
432                         dp->d_name);
433                 }
434                 if (vp) {
435                     VPutVolume(vp);
436                 }
437             }
438         }
439         Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, nAttached, nUnattached);
440         closedir(dirp);
441         VOL_LOCK;
442     }
443
444     params->n_threads_complete++;
445     pthread_cond_signal(&params->thread_done_cv);
446     VOL_UNLOCK;
447     return NULL;
448 }
449 #endif /* AFS_PTHREAD_ENV */
450
451 /* This must be called by any volume utility which needs to run while the
452    file server is also running.  This is separated from VInitVolumePackage so
453    that a utility can fork--and each of the children can independently
454    initialize communication with the file server */
455 int
456 VConnectFS(void)
457 {
458     int retVal;
459     VOL_LOCK;
460     retVal = VConnectFS_r();
461     VOL_UNLOCK;
462     return retVal;
463 }
464
465 int
466 VConnectFS_r(void)
467 {
468     int rc;
469     assert(VInit == 2 && programType == volumeUtility);
470     rc = FSYNC_clientInit();
471     if (rc)
472         VInit = 3;
473     return rc;
474 }
475
476 void
477 VDisconnectFS_r(void)
478 {
479     assert(programType == volumeUtility);
480     FSYNC_clientFinis();
481     VInit = 2;
482 }
483
484 void
485 VDisconnectFS(void)
486 {
487     VOL_LOCK;
488     VDisconnectFS_r();
489     VOL_UNLOCK;
490 }
491
492 void
493 VShutdown_r(void)
494 {
495     int i;
496     register Volume *vp, *np;
497     register afs_int32 code;
498
499     Log("VShutdown:  shutting down on-line volumes...\n");
500     for (i = 0; i < VOLUME_HASH_TABLE_SIZE; i++) {
501         /* try to hold first volume in the hash table */
502         for (vp = VolumeHashTable[i]; vp; vp = vp->hashNext) {
503             code = VHold_r(vp);
504             if (code == 0)
505                 break;          /* got it */
506             /* otherwise we go around again, trying another volume */
507         }
508         while (vp) {
509             if (LogLevel >= 5)
510                 Log("VShutdown:  Attempting to take volume %u offline.\n",
511                     vp->hashid);
512             /* first compute np before releasing vp, in case vp disappears
513              * after releasing.  Hold it, so it doesn't disapear.  If we
514              * can't hold it, try the next one in the chain.  Invariant
515              * at the top of this loop is that vp is held (has extra ref count).
516              */
517             for (np = vp->hashNext; np; np = np->hashNext) {
518                 code = VHold_r(np);
519                 if (code == 0)
520                     break;      /* got it */
521             }
522             /* next, take the volume offline (drops reference count) */
523             VOffline_r(vp, "File server was shut down");
524             vp = np;            /* next guy to try */
525         }
526     }
527     Log("VShutdown:  complete.\n");
528 }
529
530 void
531 VShutdown(void)
532 {
533     VOL_LOCK;
534     VShutdown_r();
535     VOL_UNLOCK;
536 }
537
538
539 static void
540 ReadHeader(Error * ec, IHandle_t * h, char *to, int size, bit32 magic,
541            bit32 version)
542 {
543     struct versionStamp *vsn;
544     FdHandle_t *fdP;
545
546     *ec = 0;
547     if (h == NULL) {
548         *ec = VSALVAGE;
549         return;
550     }
551
552     fdP = IH_OPEN(h);
553     if (fdP == NULL) {
554         *ec = VSALVAGE;
555         return;
556     }
557
558     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
559         *ec = VSALVAGE;
560         FDH_REALLYCLOSE(fdP);
561         return;
562     }
563     vsn = (struct versionStamp *)to;
564     if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
565         *ec = VSALVAGE;
566         FDH_REALLYCLOSE(fdP);
567         return;
568     }
569     FDH_CLOSE(fdP);
570
571     /* Check is conditional, in case caller wants to inspect version himself */
572     if (version && vsn->version != version) {
573         *ec = VSALVAGE;
574     }
575 }
576
577 /* VolumeHeaderToDisk
578  * Allows for storing 64 bit inode numbers in on-disk volume header
579  * file.
580  */
581 void
582 VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
583 {
584
585     memset((char *)dh, 0, sizeof(VolumeDiskHeader_t));
586     dh->stamp = h->stamp;
587     dh->id = h->id;
588     dh->parent = h->parent;
589
590 #ifdef AFS_64BIT_IOPS_ENV
591     dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
592     dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
593     dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
594     dh->smallVnodeIndex_hi =
595         (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
596     dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
597     dh->largeVnodeIndex_hi =
598         (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
599     dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
600     dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
601 #else
602     dh->volumeInfo_lo = h->volumeInfo;
603     dh->smallVnodeIndex_lo = h->smallVnodeIndex;
604     dh->largeVnodeIndex_lo = h->largeVnodeIndex;
605     dh->linkTable_lo = h->linkTable;
606 #endif
607 }
608
609 /* DiskToVolumeHeader
610  * Reads volume header file from disk, convering 64 bit inodes
611  * if required. Makes the assumption that AFS has *always* 
612  * zero'd the volume header file so that high parts of inode
613  * numbers are 0 in older (SGI EFS) volume header files.
614  */
615 void
616 DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
617 {
618     memset((char *)h, 0, sizeof(VolumeHeader_t));
619     h->stamp = dh->stamp;
620     h->id = dh->id;
621     h->parent = dh->parent;
622
623 #ifdef AFS_64BIT_IOPS_ENV
624     h->volumeInfo =
625         (Inode) dh->volumeInfo_lo | ((Inode) dh->volumeInfo_hi << 32);
626
627     h->smallVnodeIndex =
628         (Inode) dh->smallVnodeIndex_lo | ((Inode) dh->
629                                           smallVnodeIndex_hi << 32);
630
631     h->largeVnodeIndex =
632         (Inode) dh->largeVnodeIndex_lo | ((Inode) dh->
633                                           largeVnodeIndex_hi << 32);
634     h->linkTable =
635         (Inode) dh->linkTable_lo | ((Inode) dh->linkTable_hi << 32);
636 #else
637     h->volumeInfo = dh->volumeInfo_lo;
638     h->smallVnodeIndex = dh->smallVnodeIndex_lo;
639     h->largeVnodeIndex = dh->largeVnodeIndex_lo;
640     h->linkTable = dh->linkTable_lo;
641 #endif
642 }
643
644
645 void
646 WriteVolumeHeader_r(ec, vp)
647      Error *ec;
648      Volume *vp;
649 {
650     IHandle_t *h = V_diskDataHandle(vp);
651     FdHandle_t *fdP;
652
653     *ec = 0;
654
655     fdP = IH_OPEN(h);
656     if (fdP == NULL) {
657         *ec = VSALVAGE;
658         return;
659     }
660     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
661         *ec = VSALVAGE;
662         FDH_REALLYCLOSE(fdP);
663         return;
664     }
665     if (FDH_WRITE(fdP, (char *)&V_disk(vp), sizeof(V_disk(vp)))
666         != sizeof(V_disk(vp))) {
667         *ec = VSALVAGE;
668         FDH_REALLYCLOSE(fdP);
669         return;
670     }
671     FDH_CLOSE(fdP);
672 }
673
674 /* Attach an existing volume, given its pathname, and return a
675    pointer to the volume header information.  The volume also
676    normally goes online at this time.  An offline volume
677    must be reattached to make it go online */
678 Volume *
679 VAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
680 {
681     Volume *retVal;
682     VATTACH_LOCK;
683     VOL_LOCK;
684     retVal = VAttachVolumeByName_r(ec, partition, name, mode);
685     VOL_UNLOCK;
686     VATTACH_UNLOCK;
687     return retVal;
688 }
689
690 Volume *
691 VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
692 {
693     register Volume *vp;
694     int fd, n;
695     struct afs_stat status;
696     struct VolumeDiskHeader diskHeader;
697     struct VolumeHeader iheader;
698     struct DiskPartition *partp;
699     char path[64];
700     int isbusy = 0;
701     *ec = 0;
702     if (programType == volumeUtility) {
703         assert(VInit == 3);
704         VLockPartition_r(partition);
705     }
706     if (programType == fileServer) {
707         vp = VGetVolume_r(ec, VolumeNumber(name));
708         if (vp) {
709             if (V_inUse(vp))
710                 return vp;
711             if (vp->specialStatus == VBUSY)
712                 isbusy = 1;
713             VDetachVolume_r(ec, vp);
714             if (*ec) {
715                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
716             }
717         }
718     }
719
720     if (!(partp = VGetPartition_r(partition, 0))) {
721         *ec = VNOVOL;
722         Log("VAttachVolume: Error getting partition (%s)\n", partition);
723         goto done;
724     }
725
726     *ec = 0;
727     strcpy(path, VPartitionPath(partp));
728     strcat(path, "/");
729     strcat(path, name);
730     VOL_UNLOCK;
731     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
732         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
733         if (fd > -1)
734             close(fd);
735         VOL_LOCK;
736         *ec = VNOVOL;
737         goto done;
738     }
739     n = read(fd, &diskHeader, sizeof(diskHeader));
740     close(fd);
741     VOL_LOCK;
742     if (n != sizeof(diskHeader)
743         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
744         Log("VAttachVolume: Error reading volume header %s\n", path);
745         *ec = VSALVAGE;
746         goto done;
747     }
748     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
749         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
750         *ec = VSALVAGE;
751         goto done;
752     }
753
754     DiskToVolumeHeader(&iheader, &diskHeader);
755     if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
756         if (FSYNC_askfs(iheader.id, partition, FSYNC_NEEDVOLUME, mode)
757             == FSYNC_DENIED) {
758             Log("VAttachVolume: attach of volume %u apparently denied by file server\n", iheader.id);
759             *ec = VNOVOL;       /* XXXX */
760             goto done;
761         }
762     }
763
764     vp = attach2(ec, path, &iheader, partp, isbusy);
765     if (programType == volumeUtility && vp) {
766         /* duplicate computation in fssync.c about whether the server
767          * takes the volume offline or not.  If the volume isn't
768          * offline, we must not return it when we detach the volume,
769          * or the server will abort */
770         if (mode == V_READONLY || mode == V_PEEK
771             || (!VolumeWriteable(vp) && (mode == V_CLONE || mode == V_DUMP)))
772             vp->needsPutBack = 0;
773         else
774             vp->needsPutBack = 1;
775     }
776     /* OK, there's a problem here, but one that I don't know how to
777      * fix right now, and that I don't think should arise often.
778      * Basically, we should only put back this volume to the server if
779      * it was given to us by the server, but since we don't have a vp,
780      * we can't run the VolumeWriteable function to find out as we do
781      * above when computing vp->needsPutBack.  So we send it back, but
782      * there's a path in VAttachVolume on the server which may abort
783      * if this volume doesn't have a header.  Should be pretty rare
784      * for all of that to happen, but if it does, probably the right
785      * fix is for the server to allow the return of readonly volumes
786      * that it doesn't think are really checked out. */
787     if (programType == volumeUtility && vp == NULL &&
788         mode != V_SECRETLY && mode != V_PEEK) {
789         FSYNC_askfs(iheader.id, partition, FSYNC_ON, 0);
790     } else if (programType == fileServer && vp) {
791         V_needsCallback(vp) = 0;
792 #ifdef  notdef
793         if (VInit >= 2 && V_BreakVolumeCallbacks) {
794             Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
795             (*V_BreakVolumeCallbacks) (V_id(vp));
796         }
797 #endif
798         VUpdateVolume_r(ec, vp);
799         if (*ec) {
800             Log("VAttachVolume: Error updating volume\n");
801             if (vp)
802                 VPutVolume_r(vp);
803             goto done;
804         }
805         if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
806             /* This is a hack: by temporarily settint the incore
807              * dontSalvage flag ON, the volume will be put back on the
808              * Update list (with dontSalvage OFF again).  It will then
809              * come back in N minutes with DONT_SALVAGE eventually
810              * set.  This is the way that volumes that have never had
811              * it set get it set; or that volumes that have been
812              * offline without DONT SALVAGE having been set also
813              * eventually get it set */
814             V_dontSalvage(vp) = DONT_SALVAGE;
815             VAddToVolumeUpdateList_r(ec, vp);
816             if (*ec) {
817                 Log("VAttachVolume: Error adding volume to update list\n");
818                 if (vp)
819                     VPutVolume_r(vp);
820                 goto done;
821             }
822         }
823         if (LogLevel)
824             Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
825                 V_name(vp));
826     }
827   done:
828     if (programType == volumeUtility) {
829         VUnlockPartition_r(partition);
830     }
831     if (*ec)
832         return NULL;
833     else
834         return vp;
835 }
836
837 private Volume *
838 attach2(Error * ec, char *path, register struct VolumeHeader * header,
839         struct DiskPartition * partp, int isbusy)
840 {
841     register Volume *vp;
842
843     VOL_UNLOCK;
844
845     vp = (Volume *) calloc(1, sizeof(Volume));
846     assert(vp != NULL);
847     vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
848     vp->device = partp->device;
849     vp->partition = partp;
850     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
851             header->largeVnodeIndex);
852     IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
853             header->smallVnodeIndex);
854     IH_INIT(vp->diskDataHandle, partp->device, header->parent,
855             header->volumeInfo);
856     IH_INIT(vp->linkHandle, partp->device, header->parent, header->linkTable);
857     vp->shuttingDown = 0;
858     vp->goingOffline = 0;
859     vp->nUsers = 1;
860
861     VOL_LOCK;
862     vp->cacheCheck = ++VolumeCacheCheck;
863     /* just in case this ever rolls over */
864     if (!vp->cacheCheck)
865         vp->cacheCheck = ++VolumeCacheCheck;
866     GetVolumeHeader(vp);
867     VOL_UNLOCK;
868
869     (void)ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
870                      sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
871
872     VOL_LOCK;
873     if (*ec) {
874         Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
875     }
876     if (!*ec) {
877         struct IndexFileHeader iHead;
878
879 #if OPENAFS_VOL_STATS
880         /*
881          * We just read in the diskstuff part of the header.  If the detailed
882          * volume stats area has not yet been initialized, we should bzero the
883          * area and mark it as initialized.
884          */
885         if (!(V_stat_initialized(vp))) {
886             memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
887             V_stat_initialized(vp) = 1;
888         }
889 #endif /* OPENAFS_VOL_STATS */
890         VOL_UNLOCK;
891         (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
892                          (char *)&iHead, sizeof(iHead),
893                          SMALLINDEXMAGIC, SMALLINDEXVERSION);
894         VOL_LOCK;
895         if (*ec) {
896             Log("VAttachVolume: Error reading smallVnode vol header %s; error=%u\n", path, *ec);
897         }
898     }
899     if (!*ec) {
900         struct IndexFileHeader iHead;
901         VOL_UNLOCK;
902         (void)ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
903                          (char *)&iHead, sizeof(iHead),
904                          LARGEINDEXMAGIC, LARGEINDEXVERSION);
905         VOL_LOCK;
906         if (*ec) {
907             Log("VAttachVolume: Error reading largeVnode vol header %s; error=%u\n", path, *ec);
908         }
909     }
910 #ifdef AFS_NAMEI_ENV
911     if (!*ec) {
912         struct versionStamp stamp;
913         VOL_UNLOCK;
914         (void)ReadHeader(ec, V_linkHandle(vp), (char *)&stamp,
915                          sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION);
916         VOL_LOCK;
917         if (*ec) {
918             Log("VAttachVolume: Error reading namei vol header %s; error=%u\n", path, *ec);
919         }
920     }
921 #endif
922     if (*ec) {
923         Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
924         FreeVolume(vp);
925         return NULL;
926     }
927     if (V_needsSalvaged(vp)) {
928         if (vp->specialStatus)
929             vp->specialStatus = 0;
930         Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
931         *ec = VSALVAGE;
932         FreeVolume(vp);
933         return NULL;
934     }
935     if (programType == fileServer) {
936 #ifndef FAST_RESTART
937         if (V_inUse(vp) && VolumeWriteable(vp)) {
938             if (!V_needsSalvaged(vp)) {
939                 V_needsSalvaged(vp) = 1;
940                 VUpdateVolume_r(ec, vp);
941             }
942             FreeVolume(vp);
943             Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
944             *ec = VSALVAGE;
945             return NULL;
946         }
947 #endif /* FAST_RESTART */
948         if (V_destroyMe(vp) == DESTROY_ME) {
949             FreeVolume(vp);
950             Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
951             *ec = VNOVOL;
952             return NULL;
953         }
954     }
955
956     AddVolumeToHashTable(vp, V_id(vp));
957     vp->nextVnodeUnique = V_uniquifier(vp);
958     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
959 #ifndef BITMAP_LATER
960     if (programType == fileServer && VolumeWriteable(vp)) {
961         int i;
962         for (i = 0; i < nVNODECLASSES; i++) {
963             VOL_UNLOCK;
964             GetBitmap(ec, vp, i);
965             VOL_LOCK;
966             if (*ec) {
967                 FreeVolume(vp);
968                 Log("VAttachVolume: error getting bitmap for volume (%s)\n",
969                     path);
970                 return NULL;
971             }
972         }
973     }
974 #endif /* BITMAP_LATER */
975
976     if (programType == fileServer) {
977         if (vp->specialStatus)
978             vp->specialStatus = 0;
979         if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
980             V_inUse(vp) = 1;
981             V_offlineMessage(vp)[0] = '\0';
982         }
983     }
984
985     return vp;
986 }
987
988 /* Attach an existing volume.
989    The volume also normally goes online at this time.
990    An offline volume must be reattached to make it go online.
991  */
992
993 Volume *
994 VAttachVolume(Error * ec, VolumeId volumeId, int mode)
995 {
996     Volume *retVal;
997     VATTACH_LOCK;
998     VOL_LOCK;
999     retVal = VAttachVolume_r(ec, volumeId, mode);
1000     VOL_UNLOCK;
1001     VATTACH_UNLOCK;
1002     return retVal;
1003 }
1004
1005 Volume *
1006 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
1007 {
1008     char *part, *name;
1009     GetVolumePath(ec, volumeId, &part, &name);
1010     if (*ec) {
1011         register Volume *vp;
1012         Error error;
1013         vp = VGetVolume_r(&error, volumeId);
1014         if (vp) {
1015             assert(V_inUse(vp) == 0);
1016             VDetachVolume_r(ec, vp);
1017         }
1018         return NULL;
1019     }
1020     return VAttachVolumeByName_r(ec, part, name, mode);
1021 }
1022
1023 /* Increment a reference count to a volume, sans context swaps.  Requires
1024  * possibly reading the volume header in from the disk, since there's
1025  * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
1026  *
1027  * N.B. This call can fail if we can't read in the header!!  In this case
1028  * we still guarantee we won't context swap, but the ref count won't be
1029  * incremented (otherwise we'd violate the invariant).
1030  */
1031 static int
1032 VHold_r(register Volume * vp)
1033 {
1034     Error error;
1035
1036     if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
1037         VolumeReplacements++;
1038         ReadHeader(&error, V_diskDataHandle(vp), (char *)&V_disk(vp),
1039                    sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
1040         if (error)
1041             return error;
1042     }
1043     vp->nUsers++;
1044     return 0;
1045 }
1046
1047 static int
1048 VHold(register Volume * vp)
1049 {
1050     int retVal;
1051     VOL_LOCK;
1052     retVal = VHold_r(vp);
1053     VOL_UNLOCK;
1054     return retVal;
1055 }
1056
1057 void
1058 VTakeOffline_r(register Volume * vp)
1059 {
1060     assert(vp->nUsers > 0);
1061     assert(programType == fileServer);
1062     vp->goingOffline = 1;
1063     V_needsSalvaged(vp) = 1;
1064 }
1065
1066 void
1067 VTakeOffline(register Volume * vp)
1068 {
1069     VOL_LOCK;
1070     VTakeOffline_r(vp);
1071     VOL_UNLOCK;
1072 }
1073
1074 void
1075 VPutVolume_r(register Volume * vp)
1076 {
1077     assert(--vp->nUsers >= 0);
1078     if (vp->nUsers == 0) {
1079         ReleaseVolumeHeader(vp->header);
1080         if (vp->goingOffline) {
1081             Error error;
1082             assert(programType == fileServer);
1083             vp->goingOffline = 0;
1084             V_inUse(vp) = 0;
1085             VUpdateVolume_r(&error, vp);
1086             VCloseVolumeHandles_r(vp);
1087             if (LogLevel) {
1088                 Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
1089                     V_name(vp));
1090                 if (V_offlineMessage(vp)[0])
1091                     Log(" (%s)", V_offlineMessage(vp));
1092                 Log("\n");
1093             }
1094 #ifdef AFS_PTHREAD_ENV
1095             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
1096 #else /* AFS_PTHREAD_ENV */
1097             LWP_NoYieldSignal(VPutVolume);
1098 #endif /* AFS_PTHREAD_ENV */
1099         }
1100         if (vp->shuttingDown) {
1101             VReleaseVolumeHandles_r(vp);
1102             FreeVolume(vp);
1103             if (programType == fileServer)
1104 #ifdef AFS_PTHREAD_ENV
1105                 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
1106 #else /* AFS_PTHREAD_ENV */
1107                 LWP_NoYieldSignal(VPutVolume);
1108 #endif /* AFS_PTHREAD_ENV */
1109         }
1110     }
1111 }
1112
1113 void
1114 VPutVolume(register Volume * vp)
1115 {
1116     VOL_LOCK;
1117     VPutVolume_r(vp);
1118     VOL_UNLOCK;
1119 }
1120
1121 /* Get a pointer to an attached volume.  The pointer is returned regardless
1122    of whether or not the volume is in service or on/off line.  An error
1123    code, however, is returned with an indication of the volume's status */
1124 Volume *
1125 VGetVolume(Error * ec, VolId volumeId)
1126 {
1127     Volume *retVal;
1128     VOL_LOCK;
1129     retVal = VGetVolume_r(ec, volumeId);
1130     VOL_UNLOCK;
1131     return retVal;
1132 }
1133
1134 Volume *
1135 VGetVolume_r(Error * ec, VolId volumeId)
1136 {
1137     Volume *vp;
1138     unsigned short V0 = 0, V1 = 0, V2 = 0, V3 = 0, V4 = 0, V5 = 0, V6 =
1139         0, V7 = 0, V8 = 0, V9 = 0;
1140     unsigned short V10 = 0, V11 = 0, V12 = 0, V13 = 0, V14 = 0, V15 = 0;
1141
1142     for (;;) {
1143         *ec = 0;
1144         V0++;
1145         for (vp = VolumeHashTable[VOLUME_HASH(volumeId)];
1146              vp && vp->hashid != volumeId; vp = vp->hashNext)
1147             Vlooks++;
1148
1149         if (!vp) {
1150             V1++;
1151             if (VInit < 2) {
1152                 V2++;
1153                 /* Until we have reached an initialization level of 2
1154                  * we don't know whether this volume exists or not.
1155                  * We can't sleep and retry later because before a volume
1156                  * is attached, the caller tries to get it first.  Just
1157                  * return VOFFLINE and the caller can choose whether to
1158                  * retry the command or not. */
1159                 *ec = VOFFLINE;
1160                 break;
1161             }
1162
1163             *ec = VNOVOL;
1164             break;
1165         }
1166
1167         V3++;
1168         VolumeGets++;
1169         if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
1170             V5++;
1171             VolumeReplacements++;
1172             ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
1173                        sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
1174                        VOLUMEINFOVERSION);
1175             if (*ec) {
1176                 V6++;
1177                 /* Only log the error if it was a totally unexpected error.  Simply
1178                  * a missing inode is likely to be caused by the volume being deleted */
1179                 if (errno != ENXIO || LogLevel)
1180                     Log("Volume %u: couldn't reread volume header\n",
1181                         vp->hashid);
1182                 FreeVolume(vp);
1183                 vp = NULL;
1184                 break;
1185             }
1186         }
1187         V7++;
1188         if (vp->shuttingDown) {
1189             V8++;
1190             *ec = VNOVOL;
1191             vp = NULL;
1192             break;
1193         }
1194         if (programType == fileServer) {
1195             V9++;
1196             if (vp->goingOffline) {
1197                 V10++;
1198 #ifdef AFS_PTHREAD_ENV
1199                 pthread_cond_wait(&vol_put_volume_cond, &vol_glock_mutex);
1200 #else /* AFS_PTHREAD_ENV */
1201                 LWP_WaitProcess(VPutVolume);
1202 #endif /* AFS_PTHREAD_ENV */
1203                 continue;
1204             }
1205             if (vp->specialStatus) {
1206                 V11++;
1207                 *ec = vp->specialStatus;
1208             } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
1209                 V12++;
1210                 *ec = VNOVOL;
1211             } else if (V_inUse(vp) == 0) {
1212                 V13++;
1213                 *ec = VOFFLINE;
1214             } else {
1215                 V14++;
1216             }
1217         }
1218         break;
1219     }
1220     V15++;
1221     /* if no error, bump nUsers */
1222     if (vp)
1223         vp->nUsers++;
1224
1225     assert(vp || *ec);
1226     return vp;
1227 }
1228
1229
1230 /* For both VForceOffline and VOffline, we close all relevant handles.
1231  * For VOffline, if we re-attach the volume, the files may possible be
1232  * different than before. 
1233  */
1234 static void
1235 VReleaseVolumeHandles_r(Volume * vp)
1236 {
1237     DFlushVolume(V_id(vp));
1238     VReleaseVnodeFiles_r(vp);
1239
1240     /* Too time consuming and unnecessary for the volserver */
1241     if (programType != volumeUtility) {
1242         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1243         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1244         IH_CONDSYNC(vp->diskDataHandle);
1245 #ifdef AFS_NT40_ENV
1246         IH_CONDSYNC(vp->linkHandle);
1247 #endif /* AFS_NT40_ENV */
1248     }
1249
1250     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
1251     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
1252     IH_RELEASE(vp->diskDataHandle);
1253     IH_RELEASE(vp->linkHandle);
1254 }
1255
1256 /* Force the volume offline, set the salvage flag.  No further references to
1257  * the volume through the volume package will be honored. */
1258 void
1259 VForceOffline_r(Volume * vp)
1260 {
1261     Error error;
1262     if (!V_inUse(vp))
1263         return;
1264     strcpy(V_offlineMessage(vp),
1265            "Forced offline due to internal error: volume needs to be salvaged");
1266     Log("Volume %u forced offline:  it needs salvaging!\n", V_id(vp));
1267     V_inUse(vp) = 0;
1268     vp->goingOffline = 0;
1269     V_needsSalvaged(vp) = 1;
1270     VUpdateVolume_r(&error, vp);
1271 #ifdef AFS_PTHREAD_ENV
1272     assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
1273 #else /* AFS_PTHREAD_ENV */
1274     LWP_NoYieldSignal(VPutVolume);
1275 #endif /* AFS_PTHREAD_ENV */
1276
1277     VReleaseVolumeHandles_r(vp);
1278
1279 }
1280
1281 void
1282 VForceOffline(Volume * vp)
1283 {
1284     VOL_LOCK;
1285     VForceOffline_r(vp);
1286     VOL_UNLOCK;
1287 }
1288
1289 /* The opposite of VAttachVolume.  The volume header is written to disk, with
1290    the inUse bit turned off.  A copy of the header is maintained in memory,
1291    however (which is why this is VOffline, not VDetach).
1292  */
1293 void
1294 VOffline_r(Volume * vp, char *message)
1295 {
1296     Error error;
1297     VolumeId vid = V_id(vp);
1298     assert(programType != volumeUtility);
1299     if (!V_inUse(vp)) {
1300         VPutVolume_r(vp);
1301         return;
1302     }
1303     if (V_offlineMessage(vp)[0] == '\0')
1304         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
1305     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
1306     vp->goingOffline = 1;
1307     VPutVolume_r(vp);
1308     vp = VGetVolume_r(&error, vid);     /* Wait for it to go offline */
1309     if (vp)                     /* In case it was reattached... */
1310         VPutVolume_r(vp);
1311 }
1312
1313 void
1314 VOffline(Volume * vp, char *message)
1315 {
1316     VOL_LOCK;
1317     VOffline_r(vp, message);
1318     VOL_UNLOCK;
1319 }
1320
1321 /* For VDetachVolume, we close all cached file descriptors, but keep
1322  * the Inode handles in case we need to read from a busy volume.
1323  */
1324 static void
1325 VCloseVolumeHandles_r(Volume * vp)
1326 {
1327     DFlushVolume(V_id(vp));
1328     VCloseVnodeFiles_r(vp);
1329
1330     /* Too time consuming and unnecessary for the volserver */
1331     if (programType != volumeUtility) {
1332         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1333         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1334         IH_CONDSYNC(vp->diskDataHandle);
1335 #ifdef AFS_NT40_ENV
1336         IH_CONDSYNC(vp->linkHandle);
1337 #endif /* AFS_NT40_ENV */
1338     }
1339
1340     IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
1341     IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
1342     IH_REALLYCLOSE(vp->diskDataHandle);
1343     IH_REALLYCLOSE(vp->linkHandle);
1344 }
1345
1346 /* This gets used for the most part by utility routines that don't want
1347  * to keep all the volume headers around.  Generally, the file server won't
1348  * call this routine, because then the offline message in the volume header
1349  * (or other information) will still be available to clients. For NAMEI, also
1350  * close the file handles.
1351  */
1352 void
1353 VDetachVolume_r(Error * ec, Volume * vp)
1354 {
1355     VolumeId volume;
1356     struct DiskPartition *tpartp;
1357     int notifyServer, useDone;
1358
1359     *ec = 0;                    /* always "succeeds" */
1360     if (programType == volumeUtility) {
1361         notifyServer = vp->needsPutBack;
1362         useDone = (V_destroyMe(vp) == DESTROY_ME);
1363     }
1364     tpartp = vp->partition;
1365     volume = V_id(vp);
1366     DeleteVolumeFromHashTable(vp);
1367     vp->shuttingDown = 1;
1368     VPutVolume_r(vp);
1369     /* Will be detached sometime in the future--this is OK since volume is offline */
1370
1371     if (programType == volumeUtility && notifyServer) {
1372         /* 
1373          * Note:  The server is not notified in the case of a bogus volume 
1374          * explicitly to make it possible to create a volume, do a partial 
1375          * restore, then abort the operation without ever putting the volume 
1376          * online.  This is essential in the case of a volume move operation 
1377          * between two partitions on the same server.  In that case, there 
1378          * would be two instances of the same volume, one of them bogus, 
1379          * which the file server would attempt to put on line 
1380          */
1381         if (useDone)
1382             /* don't put online */
1383             FSYNC_askfs(volume, tpartp->name, FSYNC_DONE, 0);
1384         else {
1385             /* fs can use it again */
1386             FSYNC_askfs(volume, tpartp->name, FSYNC_ON, 0);
1387             /* Dettaching it so break all callbacks on it */
1388             if (V_BreakVolumeCallbacks) {
1389                 Log("volume %u detached; breaking all call backs\n", volume);
1390                 (*V_BreakVolumeCallbacks) (volume);
1391             }
1392         }
1393     }
1394 }
1395
1396 void
1397 VDetachVolume(Error * ec, Volume * vp)
1398 {
1399     VOL_LOCK;
1400     VDetachVolume_r(ec, vp);
1401     VOL_UNLOCK;
1402 }
1403
1404
1405 VnodeId
1406 VAllocBitmapEntry_r(Error * ec, Volume * vp, register struct vnodeIndex
1407                     *index)
1408 {
1409     register byte *bp, *ep;
1410     *ec = 0;
1411     /* This test is probably redundant */
1412     if (!VolumeWriteable(vp)) {
1413         *ec = (bit32) VREADONLY;
1414         return 0;
1415     }
1416 #ifdef BITMAP_LATER
1417     if ((programType == fileServer) && !index->bitmap) {
1418         int i;
1419         int wasVBUSY = 0;
1420         if (vp->specialStatus == VBUSY) {
1421             if (vp->goingOffline) {     /* vos dump waiting for the volume to
1422                                          * go offline. We probably come here
1423                                          * from AddNewReadableResidency */
1424                 wasVBUSY = 1;
1425             } else {
1426                 VOL_UNLOCK;
1427                 while (vp->specialStatus == VBUSY)
1428 #ifdef AFS_PTHREAD_ENV
1429                     sleep(2);
1430 #else /* AFS_PTHREAD_ENV */
1431                     IOMGR_Sleep(2);
1432 #endif /* AFS_PTHREAD_ENV */
1433                 VOL_LOCK;
1434             }
1435         }
1436         if (!index->bitmap) {
1437             vp->specialStatus = VBUSY;  /* Stop anyone else from using it. */
1438             for (i = 0; i < nVNODECLASSES; i++) {
1439                 VOL_UNLOCK;
1440                 GetBitmap(ec, vp, i);
1441                 VOL_LOCK;
1442                 if (*ec) {
1443                     vp->specialStatus = 0;
1444                     vp->shuttingDown = 1;       /* Let who has it free it. */
1445                     return NULL;
1446                 }
1447             }
1448             if (!wasVBUSY)
1449                 vp->specialStatus = 0;  /* Allow others to have access. */
1450         }
1451     }
1452 #endif /* BITMAP_LATER */
1453     bp = index->bitmap + index->bitmapOffset;
1454     ep = index->bitmap + index->bitmapSize;
1455     while (bp < ep) {
1456         if ((*(bit32 *) bp) != (bit32) 0xffffffff) {
1457             int o;
1458             index->bitmapOffset = (afs_uint32) (bp - index->bitmap);
1459             while (*bp == 0xff)
1460                 bp++;
1461             o = ffs(~*bp) - 1;  /* ffs is documented in BSTRING(3) */
1462             *bp |= (1 << o);
1463             return (VnodeId) ((bp - index->bitmap) * 8 + o);
1464         }
1465         bp += sizeof(bit32) /* i.e. 4 */ ;
1466     }
1467     /* No bit map entry--must grow bitmap */
1468     bp = (byte *)
1469         realloc(index->bitmap, index->bitmapSize + VOLUME_BITMAP_GROWSIZE);
1470     assert(bp != NULL);
1471     index->bitmap = bp;
1472     bp += index->bitmapSize;
1473     memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
1474     index->bitmapOffset = index->bitmapSize;
1475     index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
1476     *bp = 1;
1477     return index->bitmapOffset * 8;
1478 }
1479
1480 VnodeId
1481 VAllocBitmapEntry(Error * ec, Volume * vp, register struct vnodeIndex * index)
1482 {
1483     VnodeId retVal;
1484     VOL_LOCK;
1485     retVal = VAllocBitmapEntry_r(ec, vp, index);
1486     VOL_UNLOCK;
1487     return retVal;
1488 }
1489
1490 void
1491 VFreeBitMapEntry_r(Error * ec, register struct vnodeIndex *index,
1492                    unsigned bitNumber)
1493 {
1494     unsigned int offset;
1495     *ec = 0;
1496 #ifdef BITMAP_LATER
1497     if (!index->bitmap)
1498         return;
1499 #endif /* BITMAP_LATER */
1500     offset = bitNumber >> 3;
1501     if (offset >= index->bitmapSize) {
1502         *ec = VNOVNODE;
1503         return;
1504     }
1505     if (offset < index->bitmapOffset)
1506         index->bitmapOffset = offset & ~3;      /* Truncate to nearest bit32 */
1507     *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
1508 }
1509
1510 void
1511 VFreeBitMapEntry(Error * ec, register struct vnodeIndex *index,
1512                  unsigned bitNumber)
1513 {
1514     VOL_LOCK;
1515     VFreeBitMapEntry_r(ec, index, bitNumber);
1516     VOL_UNLOCK;
1517 }
1518
1519 void
1520 VUpdateVolume_r(Error * ec, Volume * vp)
1521 {
1522     *ec = 0;
1523     if (programType == fileServer)
1524         V_uniquifier(vp) =
1525             (V_inUse(vp) ? V_nextVnodeUnique(vp) +
1526              200 : V_nextVnodeUnique(vp));
1527     /*printf("Writing volume header for '%s'\n", V_name(vp)); */
1528     WriteVolumeHeader_r(ec, vp);
1529     if (*ec) {
1530         Log("VUpdateVolume: error updating volume header, volume %u (%s)\n",
1531             V_id(vp), V_name(vp));
1532         VForceOffline_r(vp);
1533     }
1534 }
1535
1536 void
1537 VUpdateVolume(Error * ec, Volume * vp)
1538 {
1539     VOL_LOCK;
1540     VUpdateVolume_r(ec, vp);
1541     VOL_UNLOCK;
1542 }
1543
1544 void
1545 VSyncVolume_r(Error * ec, Volume * vp)
1546 {
1547     FdHandle_t *fdP;
1548     VUpdateVolume_r(ec, vp);
1549     if (!ec) {
1550         int code;
1551         fdP = IH_OPEN(V_diskDataHandle(vp));
1552         assert(fdP != NULL);
1553         code = FDH_SYNC(fdP);
1554         assert(code == 0);
1555         FDH_CLOSE(fdP);
1556     }
1557 }
1558
1559 void
1560 VSyncVolume(Error * ec, Volume * vp)
1561 {
1562     VOL_LOCK;
1563     VSyncVolume_r(ec, vp);
1564     VOL_UNLOCK;
1565 }
1566
1567 static void
1568 FreeVolume(Volume * vp)
1569 {
1570     int i;
1571     if (!vp)
1572         return;
1573     for (i = 0; i < nVNODECLASSES; i++)
1574         if (vp->vnodeIndex[i].bitmap)
1575             free(vp->vnodeIndex[i].bitmap);
1576     FreeVolumeHeader(vp);
1577     DeleteVolumeFromHashTable(vp);
1578     free(vp);
1579 }
1580
1581 static void
1582 GetBitmap(Error * ec, Volume * vp, VnodeClass class)
1583 {
1584     StreamHandle_t *file;
1585     int nVnodes;
1586     int size;
1587     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
1588     struct vnodeIndex *vip = &vp->vnodeIndex[class];
1589     struct VnodeDiskObject *vnode;
1590     unsigned int unique = 0;
1591     FdHandle_t *fdP;
1592 #ifdef BITMAP_LATER
1593     byte *BitMap = 0;
1594 #endif /* BITMAP_LATER */
1595
1596     *ec = 0;
1597
1598     fdP = IH_OPEN(vip->handle);
1599     assert(fdP != NULL);
1600     file = FDH_FDOPEN(fdP, "r");
1601     assert(file != NULL);
1602     vnode = (VnodeDiskObject *) malloc(vcp->diskSize);
1603     assert(vnode != NULL);
1604     size = OS_SIZE(fdP->fd_fd);
1605     assert(size != -1);
1606     nVnodes = (size <= vcp->diskSize ? 0 : size - vcp->diskSize)
1607         >> vcp->logSize;
1608     vip->bitmapSize = ((nVnodes / 8) + 10) / 4 * 4;     /* The 10 is a little extra so
1609                                                          * a few files can be created in this volume,
1610                                                          * the whole thing is rounded up to nearest 4
1611                                                          * bytes, because the bit map allocator likes
1612                                                          * it that way */
1613 #ifdef BITMAP_LATER
1614     BitMap = (byte *) calloc(1, vip->bitmapSize);
1615     assert(BitMap != NULL);
1616 #else /* BITMAP_LATER */
1617     vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
1618     assert(vip->bitmap != NULL);
1619     vip->bitmapOffset = 0;
1620 #endif /* BITMAP_LATER */
1621     if (STREAM_SEEK(file, vcp->diskSize, 0) != -1) {
1622         int bitNumber = 0;
1623         for (bitNumber = 0; bitNumber < nVnodes + 100; bitNumber++) {
1624             if (STREAM_READ(vnode, vcp->diskSize, 1, file) != 1)
1625                 break;
1626             if (vnode->type != vNull) {
1627                 if (vnode->vnodeMagic != vcp->magic) {
1628                     Log("GetBitmap: addled vnode index in volume %s; volume needs salvage\n", V_name(vp));
1629                     *ec = VSALVAGE;
1630                     break;
1631                 }
1632 #ifdef BITMAP_LATER
1633                 *(BitMap + (bitNumber >> 3)) |= (1 << (bitNumber & 0x7));
1634 #else /* BITMAP_LATER */
1635                 *(vip->bitmap + (bitNumber >> 3)) |= (1 << (bitNumber & 0x7));
1636 #endif /* BITMAP_LATER */
1637                 if (unique <= vnode->uniquifier)
1638                     unique = vnode->uniquifier + 1;
1639             }
1640 #ifndef AFS_PTHREAD_ENV
1641             if ((bitNumber & 0x00ff) == 0x0ff) {        /* every 256 iterations */
1642                 IOMGR_Poll();
1643             }
1644 #endif /* !AFS_PTHREAD_ENV */
1645         }
1646     }
1647     if (vp->nextVnodeUnique < unique) {
1648         Log("GetBitmap: bad volume uniquifier for volume %s; volume needs salvage\n", V_name(vp));
1649         *ec = VSALVAGE;
1650     }
1651     /* Paranoia, partly justified--I think fclose after fdopen
1652      * doesn't seem to close fd.  In any event, the documentation
1653      * doesn't specify, so it's safer to close it twice.
1654      */
1655     STREAM_CLOSE(file);
1656     FDH_CLOSE(fdP);
1657     free(vnode);
1658 #ifdef BITMAP_LATER
1659     /* There may have been a racing condition with some other thread, both
1660      * creating the bitmaps for this volume. If the other thread was faster
1661      * the pointer to bitmap should already be filled and we can free ours.
1662      */
1663     if (vip->bitmap == NULL) {
1664         vip->bitmap = BitMap;
1665         vip->bitmapOffset = 0;
1666     } else
1667         free((byte *) BitMap);
1668 #endif /* BITMAP_LATER */
1669 }
1670
1671 static void
1672 GetVolumePath(Error * ec, VolId volumeId, char **partitionp, char **namep)
1673 {
1674     static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
1675     char path[VMAXPATHLEN];
1676     int found = 0;
1677     struct DiskPartition *dp;
1678
1679     *ec = 0;
1680     name[0] = '/';
1681     (void)afs_snprintf(&name[1], (sizeof name) - 1, VFORMAT, volumeId);
1682     for (dp = DiskPartitionList; dp; dp = dp->next) {
1683         struct afs_stat status;
1684         strcpy(path, VPartitionPath(dp));
1685         strcat(path, name);
1686         if (afs_stat(path, &status) == 0) {
1687             strcpy(partition, dp->name);
1688             found = 1;
1689             break;
1690         }
1691     }
1692     if (!found) {
1693         *ec = VNOVOL;
1694         *partitionp = *namep = NULL;
1695     } else {
1696         *partitionp = partition;
1697         *namep = name;
1698     }
1699 }
1700
1701 int
1702 VolumeNumber(char *name)
1703 {
1704     if (*name == '/')
1705         name++;
1706     return atoi(name + 1);
1707 }
1708
1709 char *
1710 VolumeExternalName(VolumeId volumeId)
1711 {
1712     static char name[VMAXPATHLEN];
1713     (void)afs_snprintf(name, sizeof name, VFORMAT, volumeId);
1714     return name;
1715 }
1716
1717 #if OPENAFS_VOL_STATS
1718 #define OneDay  (86400)         /* 24 hours' worth of seconds */
1719 #else
1720 #define OneDay  (24*60*60)      /* 24 hours */
1721 #endif /* OPENAFS_VOL_STATS */
1722
1723 #define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
1724
1725 /*------------------------------------------------------------------------
1726  * [export] VAdjustVolumeStatistics
1727  *
1728  * Description:
1729  *      If we've passed midnight, we need to update all the day use
1730  *      statistics as well as zeroing the detailed volume statistics
1731  *      (if we are implementing them).
1732  *
1733  * Arguments:
1734  *      vp : Pointer to the volume structure describing the lucky
1735  *              volume being considered for update.
1736  *
1737  * Returns:
1738  *      0 (always!)
1739  *
1740  * Environment:
1741  *      Nothing interesting.
1742  *
1743  * Side Effects:
1744  *      As described.
1745  *------------------------------------------------------------------------*/
1746
1747 int
1748 VAdjustVolumeStatistics_r(register Volume * vp)
1749 {
1750     unsigned int now = FT_ApproxTime();
1751
1752     if (now - V_dayUseDate(vp) > OneDay) {
1753         register ndays, i;
1754
1755         ndays = (now - V_dayUseDate(vp)) / OneDay;
1756         for (i = 6; i > ndays - 1; i--)
1757             V_weekUse(vp)[i] = V_weekUse(vp)[i - ndays];
1758         for (i = 0; i < ndays - 1 && i < 7; i++)
1759             V_weekUse(vp)[i] = 0;
1760         if (ndays <= 7)
1761             V_weekUse(vp)[ndays - 1] = V_dayUse(vp);
1762         V_dayUse(vp) = 0;
1763         V_dayUseDate(vp) = Midnight(now);
1764
1765 #if OPENAFS_VOL_STATS
1766         /*
1767          * All we need to do is bzero the entire VOL_STATS_BYTES of
1768          * the detailed volume statistics area.
1769          */
1770         memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
1771 #endif /* OPENAFS_VOL_STATS */
1772     }
1773
1774     /*It's been more than a day of collection */
1775     /*
1776      * Always return happily.
1777      */
1778     return (0);
1779 }                               /*VAdjustVolumeStatistics */
1780
1781 int
1782 VAdjustVolumeStatistics(register Volume * vp)
1783 {
1784     int retVal;
1785     VOL_LOCK;
1786     retVal = VAdjustVolumeStatistics_r(vp);
1787     VOL_UNLOCK;
1788     return retVal;
1789 }
1790
1791 void
1792 VBumpVolumeUsage_r(register Volume * vp)
1793 {
1794     unsigned int now = FT_ApproxTime();
1795     if (now - V_dayUseDate(vp) > OneDay)
1796         VAdjustVolumeStatistics_r(vp);
1797     /*
1798      * Save the volume header image to disk after every 128 bumps to dayUse.
1799      */
1800     if ((V_dayUse(vp)++ & 127) == 0) {
1801         Error error;
1802         VUpdateVolume_r(&error, vp);
1803     }
1804 }
1805
1806 void
1807 VBumpVolumeUsage(register Volume * vp)
1808 {
1809     VOL_LOCK;
1810     VBumpVolumeUsage_r(vp);
1811     VOL_UNLOCK;
1812 }
1813
1814 void
1815 VSetDiskUsage_r(void)
1816 {
1817     static int FifteenMinuteCounter = 0;
1818
1819     while (VInit < 2) {
1820         /* NOTE: Don't attempt to access the partitions list until the
1821          * initialization level indicates that all volumes are attached,
1822          * which implies that all partitions are initialized. */
1823 #ifdef AFS_PTHREAD_ENV
1824         sleep(10);
1825 #else /* AFS_PTHREAD_ENV */
1826         IOMGR_Sleep(10);
1827 #endif /* AFS_PTHREAD_ENV */
1828     }
1829
1830     VResetDiskUsage_r();
1831     if (++FifteenMinuteCounter == 3) {
1832         FifteenMinuteCounter = 0;
1833         VScanUpdateList();
1834     }
1835 }
1836
1837 void
1838 VSetDiskUsage(void)
1839 {
1840     VOL_LOCK;
1841     VSetDiskUsage_r();
1842     VOL_UNLOCK;
1843 }
1844
1845 /* The number of minutes that a volume hasn't been updated before the
1846  * "Dont salvage" flag in the volume header will be turned on */
1847 #define SALVAGE_INTERVAL        (10*60)
1848
1849 static VolumeId *UpdateList;    /* Pointer to array of Volume ID's */
1850 static int nUpdatedVolumes;     /* Updated with entry in UpdateList, salvage after crash flag on */
1851 static int updateSize;          /* number of entries possible */
1852 #define UPDATE_LIST_SIZE 100    /* size increment */
1853
1854 void
1855 VAddToVolumeUpdateList_r(Error * ec, Volume * vp)
1856 {
1857     *ec = 0;
1858     vp->updateTime = FT_ApproxTime();
1859     if (V_dontSalvage(vp) == 0)
1860         return;
1861     V_dontSalvage(vp) = 0;
1862     VSyncVolume_r(ec, vp);
1863     if (*ec)
1864         return;
1865     if (!UpdateList) {
1866         updateSize = UPDATE_LIST_SIZE;
1867         UpdateList = (VolumeId *) malloc(sizeof(VolumeId) * updateSize);
1868     } else {
1869         if (nUpdatedVolumes == updateSize) {
1870             updateSize += UPDATE_LIST_SIZE;
1871             UpdateList =
1872                 (VolumeId *) realloc(UpdateList,
1873                                      sizeof(VolumeId) * updateSize);
1874         }
1875     }
1876     assert(UpdateList != NULL);
1877     UpdateList[nUpdatedVolumes++] = V_id(vp);
1878 }
1879
1880 static void
1881 VScanUpdateList(void)
1882 {
1883     register int i, gap;
1884     register Volume *vp;
1885     Error error;
1886     afs_uint32 now = FT_ApproxTime();
1887     /* Be careful with this code, since it works with interleaved calls to AddToVolumeUpdateList */
1888     for (i = gap = 0; i < nUpdatedVolumes; i++) {
1889         vp = VGetVolume_r(&error, UpdateList[i - gap] = UpdateList[i]);
1890         if (error) {
1891             gap++;
1892         } else if (vp->nUsers == 1 && now - vp->updateTime > SALVAGE_INTERVAL) {
1893             V_dontSalvage(vp) = DONT_SALVAGE;
1894             VUpdateVolume_r(&error, vp);        /* No need to fsync--not critical */
1895             gap++;
1896         }
1897         if (vp)
1898             VPutVolume_r(vp);
1899 #ifndef AFS_PTHREAD_ENV
1900         IOMGR_Poll();
1901 #endif /* !AFS_PTHREAD_ENV */
1902     }
1903     nUpdatedVolumes -= gap;
1904 }
1905
1906 /***************************************************/
1907 /* Add on routines to manage a volume header cache */
1908 /***************************************************/
1909
1910 static struct volHeader *volumeLRU;
1911
1912 /* Allocate a bunch of headers; string them together */
1913 static void
1914 InitLRU(int howMany)
1915 {
1916     register struct volHeader *hp;
1917     if (programType != fileServer)
1918         return;
1919     hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
1920     while (howMany--)
1921         ReleaseVolumeHeader(hp++);
1922 }
1923
1924 /* Get a volume header from the LRU list; update the old one if necessary */
1925 /* Returns 1 if there was already a header, which is removed from the LRU list */
1926 static int
1927 GetVolumeHeader(register Volume * vp)
1928 {
1929     Error error;
1930     register struct volHeader *hd;
1931     int old;
1932     static int everLogged = 0;
1933
1934     old = (vp->header != NULL); /* old == volume already has a header */
1935     if (programType != fileServer) {
1936         if (!vp->header) {
1937             hd = (struct volHeader *)calloc(1, sizeof(*vp->header));
1938             assert(hd != NULL);
1939             vp->header = hd;
1940             hd->back = vp;
1941         }
1942     } else {
1943         if (old) {
1944             hd = vp->header;
1945             if (volumeLRU == hd)
1946                 volumeLRU = hd->next;
1947             assert(hd->back == vp);
1948         } else {
1949             if (volumeLRU)
1950                 /* not currently in use and least recently used */
1951                 hd = volumeLRU->prev;
1952             else {
1953                 hd = (struct volHeader *)calloc(1, sizeof(*vp->header));
1954                 /* make it look like single elt LRU */
1955                 hd->prev = hd->next = hd;
1956                 if (!everLogged) {
1957                     Log("****Allocated more volume headers, probably leak****\n");
1958                     everLogged = 1;
1959                 }
1960             }
1961             if (hd->back) {
1962                 if (hd->diskstuff.inUse) {
1963                     WriteVolumeHeader_r(&error, hd->back);
1964                     /* Ignore errors; catch them later */
1965                 }
1966                 hd->back->header = 0;
1967             }
1968             hd->back = vp;
1969             vp->header = hd;
1970         }
1971         if (hd->next) {         /* hd->next != 0 --> in LRU chain (we zero it later) */
1972             hd->prev->next = hd->next;  /* pull hd out of LRU list */
1973             hd->next->prev = hd->prev;  /* if hd only element, this is noop */
1974         }
1975         hd->next = hd->prev = 0;
1976         /* if not in LRU chain, next test won't be true */
1977         if (hd == volumeLRU)    /* last header item, turn into empty list */
1978             volumeLRU = NULL;
1979     }
1980     return old;
1981 }
1982
1983 /* Put it at the top of the LRU chain */
1984 static void
1985 ReleaseVolumeHeader(register struct volHeader *hd)
1986 {
1987     if (programType != fileServer)
1988         return;
1989     if (!hd || hd->next)        /* no header, or header already released */
1990         return;
1991     if (!volumeLRU) {
1992         hd->next = hd->prev = hd;
1993     } else {
1994         hd->prev = volumeLRU->prev;
1995         hd->next = volumeLRU;
1996         hd->prev->next = hd->next->prev = hd;
1997     }
1998     volumeLRU = hd;
1999 }
2000
2001 static void
2002 FreeVolumeHeader(register Volume * vp)
2003 {
2004     register struct volHeader *hd = vp->header;
2005     if (!hd)
2006         return;
2007     if (programType == fileServer) {
2008         ReleaseVolumeHeader(hd);
2009         hd->back = 0;
2010     } else {
2011         free(hd);
2012     }
2013     vp->header = 0;
2014 }
2015
2016
2017 /***************************************************/
2018 /* Routines to add volume to hash chain, delete it */
2019 /***************************************************/
2020
2021 static void
2022 AddVolumeToHashTable(register Volume * vp, int hashid)
2023 {
2024     int hash = VOLUME_HASH(hashid);
2025     vp->hashid = hashid;
2026     vp->hashNext = VolumeHashTable[hash];
2027     VolumeHashTable[hash] = vp;
2028     vp->vnodeHashOffset = VolumeHashOffset_r();
2029 }
2030
2031 static void
2032 DeleteVolumeFromHashTable(register Volume * vp)
2033 {
2034     int hash = VOLUME_HASH(vp->hashid);
2035     if (VolumeHashTable[hash] == vp)
2036         VolumeHashTable[hash] = vp->hashNext;
2037     else {
2038         Volume *tvp = VolumeHashTable[hash];
2039         if (tvp == NULL)
2040             return;
2041         while (tvp->hashNext && tvp->hashNext != vp)
2042             tvp = tvp->hashNext;
2043         if (tvp->hashNext == NULL)
2044             return;
2045         tvp->hashNext = vp->hashNext;
2046     }
2047     vp->hashid = 0;
2048 }
2049
2050 void
2051 VPrintCacheStats_r(void)
2052 {
2053     register struct VnodeClassInfo *vcp;
2054     vcp = &VnodeClassInfo[vLarge];
2055     Log("Large vnode cache, %d entries, %d allocs, %d gets (%d reads), %d writes\n", vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
2056     vcp = &VnodeClassInfo[vSmall];
2057     Log("Small vnode cache,%d entries, %d allocs, %d gets (%d reads), %d writes\n", vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
2058     Log("Volume header cache, %d entries, %d gets, %d replacements\n",
2059         VolumeCacheSize, VolumeGets, VolumeReplacements);
2060 }
2061
2062 void
2063 VPrintCacheStats(void)
2064 {
2065     VOL_LOCK;
2066     VPrintCacheStats_r();
2067     VOL_UNLOCK;
2068 }
2069