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