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