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