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