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