vol-check-malloc-return-20030208
[openafs.git] / src / vol / vnode.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 /*
11         System:         VICE-TWO
12         Module:         vnode.c
13         Institution:    The Information Technology Center, Carnegie-Mellon University
14
15  */
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19 RCSID("$Header$");
20
21 #include <errno.h>
22 #include <stdio.h>
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #else
26 #ifdef HAVE_STRINGS_H
27 #include <strings.h>
28 #endif
29 #endif
30 #ifdef AFS_PTHREAD_ENV
31 #include <assert.h>
32 #else /* AFS_PTHREAD_ENV */
33 #include <afs/assert.h>
34 #endif /* AFS_PTHREAD_ENV */
35
36 #include <rx/xdr.h>
37 #include <afs/afsint.h>
38 #include "nfs.h"
39 #include <afs/errors.h>
40 #include "lock.h"
41 #include "lwp.h"
42 #include <afs/afssyscalls.h>
43 #include "ihandle.h"
44 #include "vnode.h"
45 #include "partition.h"
46 #include "volume.h"
47 #if defined(AFS_SGI_ENV)
48 #include "sys/types.h"
49 #include "fcntl.h"
50 #undef min
51 #undef max
52 #include "stdlib.h"
53 #endif
54 #ifdef AFS_NT40_ENV
55 #include <fcntl.h>
56 #include "ntops.h"
57 #else
58 #include <sys/file.h>
59 #ifdef  AFS_SUN5_ENV
60 #include <sys/fcntl.h>
61 #endif
62 #include <unistd.h>
63 #endif /* AFS_NT40_ENV */
64 #include <sys/stat.h>
65
66
67 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
68
69 private int moveHash();
70 void StickOnLruChain_r();
71 void VPutVnode_r();
72
73 #define BAD_IGET        -1000
74
75 /* There are two separate vnode queue types defined here:
76  * Each hash conflict chain -- is singly linked, with a single head
77  * pointer. New entries are added at the beginning. Old
78  * entries are removed by linear search, which generally
79  * only occurs after a disk read).
80  * LRU chain -- is doubly linked, single head pointer.
81  * Entries are added at the head, reclaimed from the tail,
82  * or removed from anywhere in the queue.
83  */
84
85
86 /* Vnode hash table.  Find hash chain by taking lower bits of
87  * (volume_hash_offset + vnode).
88  * This distributes the root inodes of the volumes over the
89  * hash table entries and also distributes the vnodes of
90  * volumes reasonably fairly.  The volume_hash_offset field
91  * for each volume is established as the volume comes on line
92  * by using the VOLUME_HASH_OFFSET macro.  This distributes the
93  * volumes fairly among the cache entries, both when servicing
94  * a small number of volumes and when servicing a large number.
95  */
96
97 /* logging stuff for finding bugs */
98 #define THELOGSIZE      5120
99 static afs_int32 theLog[THELOGSIZE];
100 static afs_int32 vnLogPtr=0;
101 VNLog(aop, anparms, av1, av2, av3, av4)
102 afs_int32 aop, anparms;
103 afs_int32 av1, av2, av3,av4; {
104     register afs_int32 temp;
105     afs_int32 data[4];
106
107     /* copy data to array */
108     data[0] = av1;
109     data[1] = av2;
110     data[2] = av3;
111     data[3] = av4;
112     if (anparms>4) anparms = 4; /* do bounds checking */
113
114     temp = (aop<<16) | anparms;
115     theLog[vnLogPtr++] = temp;
116     if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
117     for(temp=0;temp<anparms;temp++) {
118         theLog[vnLogPtr++] = data[temp];
119         if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
120     }
121 }
122
123 /* VolumeHashOffset -- returns a new value to be stored in the
124  * volumeHashOffset of a Volume structure.  Called when a
125  * volume is initialized.  Sets the volumeHashOffset so that
126  * vnode cache entries are distributed reasonably between
127  * volumes (the root vnodes of the volumes will hash to
128  * different values, and spacing is maintained between volumes
129  * when there are not many volumes represented), and spread
130  * equally amongst vnodes within a single volume.
131  */
132 int VolumeHashOffset_r() {
133     static int nextVolumeHashOffset = 0;
134     /* hashindex Must be power of two in size */
135 #   define hashShift 3
136 #   define hashMask ((1<<hashShift)-1)
137     static byte hashindex[1<<hashShift] = {0,128,64,192,32,160,96,224};
138     int offset;
139     offset = hashindex[nextVolumeHashOffset&hashMask]
140            + (nextVolumeHashOffset>>hashShift);
141     nextVolumeHashOffset++;
142     return offset;
143 }
144
145 /* Change hashindex (above) if you change this constant */
146 #define VNODE_HASH_TABLE_SIZE 256
147 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
148 #define VNODE_HASH(volumeptr,vnodenumber)\
149     ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
150
151 /* Code to invalidate a vnode entry.  Called when we've damaged a vnode, and want
152     to prevent future VGetVnode's from applying to it.  Leaves it in the same hash bucket
153     but that shouldn't be important.  */
154 VInvalidateVnode_r(avnode)
155 register struct Vnode *avnode; {
156     avnode->changed_newTime = 0;    /* don't let it get flushed out again */
157     avnode->changed_oldTime = 0;
158     avnode->delete = 0;     /* it isn't deleted, erally */
159     avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
160 }
161
162 /* Not normally called by general client; called by volume.c */
163 VInitVnodes(class,nVnodes)
164     VnodeClass class;
165     int nVnodes;
166 {
167     byte *va;
168     register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
169
170     vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
171     vcp->cacheSize = nVnodes;
172     switch(class) {
173       case vSmall:
174         assert(CHECKSIZE_SMALLVNODE);
175         vcp->lruHead = NULL;
176         vcp->residentSize = SIZEOF_SMALLVNODE;
177         vcp->diskSize = SIZEOF_SMALLDISKVNODE;
178         vcp->magic = SMALLVNODEMAGIC;
179         break;
180       case vLarge:
181         vcp->lruHead = NULL;
182         vcp->residentSize = SIZEOF_LARGEVNODE;
183         vcp->diskSize = SIZEOF_LARGEDISKVNODE;
184         vcp->magic = LARGEVNODEMAGIC;
185         break;
186     }
187     {   int s = vcp->diskSize-1;
188         int n = 0;
189         while (s)
190             s >>= 1, n++;
191         vcp->logSize = n;
192     }
193
194     if (nVnodes == 0)
195             return 0;
196
197     va = (byte *) calloc(nVnodes,vcp->residentSize);
198     assert (va != NULL);
199     while (nVnodes--) {
200         Vnode *vnp = (Vnode *) va;
201         vnp->nUsers = 0;    /* no context switches */
202         Lock_Init(&vnp->lock);
203         vnp->changed_oldTime = 0;
204         vnp->changed_newTime = 0;
205         vnp->volumePtr = NULL;
206         vnp->cacheCheck = 0;
207         vnp->delete = vnp->vnodeNumber = 0;
208 #ifdef AFS_PTHREAD_ENV
209         vnp->writer = (pthread_t) 0;
210 #else /* AFS_PTHREAD_ENV */
211         vnp->writer = (PROCESS) 0;
212 #endif /* AFS_PTHREAD_ENV */
213         vnp->hashIndex = 0;
214         vnp->handle = NULL;
215         if (vcp->lruHead == NULL)
216             vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
217         else {
218             vnp->lruNext = vcp->lruHead;
219             vnp->lruPrev = vcp->lruHead->lruPrev;
220             vcp->lruHead->lruPrev = vnp;
221             vnp->lruPrev->lruNext = vnp;
222             vcp->lruHead = vnp;
223         }
224         va += vcp->residentSize;
225     }
226     return 0;
227 }
228
229
230 /* allocate an *unused* vnode from the LRU chain, going backwards of course.  It shouldn't
231     be necessary to specify that nUsers == 0 since if it is in the list, nUsers
232     should be 0.  Things shouldn't be in lruq unless no one is using them.  */
233 Vnode *VGetFreeVnode_r(vcp)
234 struct VnodeClassInfo *vcp; {
235     register Vnode *vnp;
236
237     vnp = vcp->lruHead->lruPrev;
238     if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
239         Abort("locked vnode in lruq");
240     VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
241     IH_RELEASE(vnp->handle);
242     return vnp;
243 }
244
245 static mlkReason=0;
246 static mlkLastAlloc = 0;
247 static mlkLastOver = 0;
248 static mlkLastDelete = 0;
249
250 Vnode *VAllocVnode(ec,vp,type)
251     Error *ec;
252     Volume *vp;
253     VnodeType type;
254 {
255     Vnode *retVal;
256     VOL_LOCK
257     retVal = VAllocVnode_r(ec, vp, type);
258     VOL_UNLOCK
259     return retVal;
260 }
261
262 Vnode *VAllocVnode_r(ec,vp,type)
263     Error *ec;
264     Volume *vp;
265     VnodeType type;
266 {
267     register Vnode *vnp;
268     VnodeId vnodeNumber;
269     int newHash, bitNumber;
270     register struct VnodeClassInfo *vcp;
271     VnodeClass class;
272     Unique unique;
273
274     *ec = 0;
275     if (programType == fileServer && !V_inUse(vp)) {
276         if (vp->specialStatus) {
277             *ec = vp->specialStatus;
278         } else {
279             *ec = VOFFLINE;
280         }
281         return NULL;
282     }
283     class = vnodeTypeToClass(type);
284     vcp = &VnodeClassInfo[class];
285
286     if (!VolumeWriteable(vp)) {
287         *ec = VREADONLY;
288         return NULL;
289     }
290
291     unique = vp->nextVnodeUnique++;
292     if (!unique)
293        unique = vp->nextVnodeUnique++;
294
295     if (vp->nextVnodeUnique > V_uniquifier(vp)) {
296         VUpdateVolume_r(ec,vp);
297         if (*ec)
298             return NULL;
299     }
300
301     if (programType == fileServer) {
302         VAddToVolumeUpdateList_r(ec, vp);
303         if (*ec)
304             return NULL;
305     }
306
307     /* Find a slot in the bit map */
308     bitNumber = VAllocBitmapEntry_r(ec,vp,&vp->vnodeIndex[class]);
309     if (*ec)
310         return NULL;
311     vnodeNumber = bitNumberToVnodeNumber(bitNumber,class);
312
313     VNLog(2, 1, vnodeNumber);
314     /* Prepare to move it to the new hash chain */
315     newHash = VNODE_HASH(vp, vnodeNumber);
316     for (vnp = VnodeHashTable[newHash];
317          vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
318                  || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
319          vnp = vnp->hashNext
320         );
321     if (vnp) {
322         /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
323             so we may have to wait for it below */
324         VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
325
326         /* If first user, remove it from the LRU chain.  We can assume that
327            there is at least one item in the queue */
328         if (++vnp->nUsers == 1) {
329             if (vnp == vcp->lruHead)
330                 vcp->lruHead = vcp->lruHead->lruNext;
331             vnp->lruPrev->lruNext = vnp->lruNext;
332             vnp->lruNext->lruPrev = vnp->lruPrev;
333             if (vnp == vcp->lruHead || vcp->lruHead == NULL)
334                 Abort("VGetVnode: lru chain addled!\n");
335             /* This won't block */
336             ObtainWriteLock(&vnp->lock);
337         } else {
338             /* follow locking hierarchy */
339             VOL_UNLOCK
340             ObtainWriteLock(&vnp->lock);
341             VOL_LOCK
342         }
343 #ifdef AFS_PTHREAD_ENV
344         vnp->writer = pthread_self();
345 #else /* AFS_PTHREAD_ENV */
346         LWP_CurrentProcess(&vnp->writer);
347 #endif /* AFS_PTHREAD_ENV */
348     }
349     else {
350         vnp = VGetFreeVnode_r(vcp);
351         /* Remove vnode from LRU chain and grab a write lock */
352         if (vnp == vcp->lruHead)
353             vcp->lruHead = vcp->lruHead->lruNext;
354         vnp->lruPrev->lruNext = vnp->lruNext;
355         vnp->lruNext->lruPrev = vnp->lruPrev;
356         if (vnp == vcp->lruHead || vcp->lruHead == NULL)
357             Abort("VGetVnode: lru chain addled!\n");
358         /* Initialize the header fields so noone allocates another
359            vnode with the same number */
360         vnp->vnodeNumber = vnodeNumber;
361         vnp->volumePtr = vp;
362         vnp->cacheCheck = vp->cacheCheck;
363         vnp->nUsers = 1;
364         moveHash(vnp, newHash);
365         /* This will never block */
366         ObtainWriteLock(&vnp->lock);
367 #ifdef AFS_PTHREAD_ENV
368         vnp->writer = pthread_self();
369 #else /* AFS_PTHREAD_ENV */
370         LWP_CurrentProcess(&vnp->writer);
371 #endif /* AFS_PTHREAD_ENV */
372         /* Sanity check:  is this vnode really not in use? */
373         { 
374           int size;
375           IHandle_t *ihP = vp->vnodeIndex[class].handle;
376           FdHandle_t *fdP;
377           off_t off = vnodeIndexOffset(vcp, vnodeNumber);
378
379           VOL_UNLOCK
380           fdP = IH_OPEN(ihP);
381           if (fdP == NULL)
382               Abort("VAllocVnode: can't open index file!\n");
383           if ((size = FDH_SIZE(fdP)) < 0)
384               Abort("VAllocVnode: can't stat index file!\n");
385           if (FDH_SEEK(fdP, off, SEEK_SET) < 0)
386               Abort("VAllocVnode: can't seek on index file!\n");
387           if (off < size) {
388               if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) == vcp->diskSize) {
389                   if (vnp->disk.type != vNull)
390                       Abort("VAllocVnode:  addled bitmap or index!\n");
391               }
392           } else {
393               /* growing file - grow in a reasonable increment */
394               char *buf = (char *)malloc(16*1024);
395               if (!buf) Abort("VAllocVnode: malloc failed\n");
396               memset(buf, 0, 16*1024);
397               FDH_WRITE(fdP, buf, 16*1024);
398               free(buf);
399           }
400           FDH_CLOSE(fdP);
401           VOL_LOCK
402       }
403         VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
404     }
405
406     VNLog(5, 1, (afs_int32) vnp);
407 #ifdef AFS_PTHREAD_ENV
408     vnp->writer = pthread_self();
409 #else /* AFS_PTHREAD_ENV */
410     LWP_CurrentProcess(&vnp->writer);
411 #endif /* AFS_PTHREAD_ENV */
412     memset(&vnp->disk, 0, sizeof(vnp->disk));
413     vnp->changed_newTime = 0; /* set this bit when vnode is updated */
414     vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
415     vnp->delete = 0;
416     vnp->disk.vnodeMagic = vcp->magic;
417     vnp->disk.type = type;
418     vnp->disk.uniquifier = unique;
419     vnp->handle = NULL;
420     vcp->allocs++;
421     return vnp;
422 }
423     
424 Vnode *VGetVnode(ec,vp,vnodeNumber,locktype)
425     Error *ec;
426     Volume *vp;
427     VnodeId vnodeNumber;
428     int locktype;       /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
429 {
430     Vnode *retVal;
431     VOL_LOCK
432     retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
433     VOL_UNLOCK
434     return retVal;
435 }
436
437 Vnode *VGetVnode_r(ec,vp,vnodeNumber,locktype)
438     Error *ec;
439     Volume *vp;
440     VnodeId vnodeNumber;
441     int locktype;       /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
442 {
443     register Vnode *vnp;
444     int newHash;
445     VnodeClass class;
446     struct VnodeClassInfo *vcp;
447
448     *ec = 0;
449     mlkReason = 0;  /* last call didn't fail */
450
451     if (vnodeNumber == 0) {
452         *ec = VNOVNODE;
453         mlkReason = 1;
454         return NULL;
455     }
456
457     VNLog(100, 1, vnodeNumber);
458     if (programType == fileServer && !V_inUse(vp)) {
459         *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
460
461         /* If the volume is VBUSY (being cloned or dumped) and this is
462          * a READ operation, then don't fail.
463          */
464         if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
465            mlkReason = 2;
466            return NULL;
467         }
468         *ec = 0;
469     }
470     class = vnodeIdToClass(vnodeNumber);
471     vcp = &VnodeClassInfo[class];
472     if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
473         *ec = VREADONLY;
474         mlkReason = 3;
475         return NULL;
476     }
477
478     if (locktype == WRITE_LOCK && programType == fileServer) {
479         VAddToVolumeUpdateList_r(ec, vp);
480         if (*ec) {
481             mlkReason = 1000 + *ec;
482             return NULL;
483         }
484     }
485
486     /* See whether the vnode is in the cache. */
487     newHash = VNODE_HASH(vp, vnodeNumber);
488     for (vnp = VnodeHashTable[newHash];
489          vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
490                  || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
491          vnp = vnp->hashNext
492         );
493     vcp->gets++;
494     if (vnp == NULL) {
495         int     n;
496         IHandle_t *ihP = vp->vnodeIndex[class].handle;
497         FdHandle_t *fdP;
498         /* Not in cache; tentatively grab most distantly used one from the LRU
499            chain */
500         vcp->reads++;
501         vnp = VGetFreeVnode_r(vcp);
502         /* Remove it from the old hash chain */
503         moveHash(vnp, newHash);
504         /* Remove it from the LRU chain */
505         if (vnp == vcp->lruHead)
506             vcp->lruHead = vcp->lruHead->lruNext;
507         if (vnp == vcp->lruHead || vcp->lruHead == NULL)
508             Abort("VGetVnode: lru chain addled!\n");
509         vnp->lruPrev->lruNext = vnp->lruNext;
510         vnp->lruNext->lruPrev = vnp->lruPrev;
511         /* Initialize */
512         vnp->changed_newTime = vnp->changed_oldTime = 0;
513         vnp->delete = 0;
514         vnp->vnodeNumber = vnodeNumber;
515         vnp->volumePtr = vp;
516         vnp->cacheCheck = vp->cacheCheck;
517         vnp->nUsers = 1;
518
519         /* This will never block */
520         ObtainWriteLock(&vnp->lock);
521 #ifdef AFS_PTHREAD_ENV
522         vnp->writer = pthread_self();
523 #else /* AFS_PTHREAD_ENV */
524           LWP_CurrentProcess(&vnp->writer);
525 #endif /* AFS_PTHREAD_ENV */
526
527         /* Read vnode from volume index */
528         VOL_UNLOCK
529         fdP = IH_OPEN(ihP);
530         if (fdP == NULL) {
531             Log("VGetVnode: can't open index dev=%d, i=%s\n",
532                 vp->device, PrintInode(NULL,
533                                        vp->vnodeIndex[class].handle->ih_ino));
534             *ec = VIO;
535             mlkReason=9;
536         }
537         else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber),
538                          SEEK_SET) < 0) {
539             Log ("VGetVnode: can't seek on index file vn=%d\n",vnodeNumber);
540             *ec = VIO;
541             mlkReason=10;
542             FDH_REALLYCLOSE(fdP);
543         }
544         else if ((n = FDH_READ(fdP, (char*)&vnp->disk, vcp->diskSize))
545                  != vcp->diskSize) {
546             /* Don't take volume off line if the inumber is out of range
547                or the inode table is full. */
548             FDH_REALLYCLOSE(fdP);
549             VOL_LOCK
550             if(n == BAD_IGET) {
551                 Log("VGetVnode: bad inumber %s\n",
552                     PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
553                 *ec = VIO;
554                 mlkReason = 4;
555             }
556             /* Check for disk errors.  Anything else just means that the vnode
557                is not allocated */
558             if (n == -1 && errno == EIO) {
559                 Log("VGetVnode: Couldn't read vnode %d, volume %u (%s); volume needs salvage\n",  
560                     vnodeNumber, V_id(vp), V_name(vp));
561                 VForceOffline_r(vp);
562                 *ec = VSALVAGE;
563                 mlkReason = 4;
564             } else {
565                 mlkReason = 5;
566                 *ec = VIO;
567             }
568             VInvalidateVnode_r(vnp);
569             if (vnp->nUsers-- == 1)
570                 StickOnLruChain_r(vnp,vcp);
571             ReleaseWriteLock(&vnp->lock);
572             return NULL;
573         }
574         FDH_CLOSE(fdP);
575         VOL_LOCK
576         /* Quick check to see that the data is reasonable */
577         if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
578             if (vnp->disk.type == vNull) {
579                 *ec = VNOVNODE;
580                 mlkReason = 6;
581                 VInvalidateVnode_r(vnp);
582                 if (vnp->nUsers-- == 1)
583                     StickOnLruChain_r(vnp,vcp);
584                 ReleaseWriteLock(&vnp->lock);
585                 return NULL;    /* The vnode is not allocated */
586             }
587             else {
588                 struct vnodeIndex *index = &vp->vnodeIndex[class];
589                 int bitNumber = vnodeIdToBitNumber(vnodeNumber);
590                 int offset = bitNumber >> 3;
591
592                 /* Test to see if vnode number is valid. */
593                 if ((offset >= index->bitmapSize) ||
594                     ((*(index->bitmap+offset) & (1<<(bitNumber&0x7))) == 0)) {
595                     Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n",
596                         vnodeNumber, V_id(vp), V_name(vp));
597                     mlkReason = 11;
598                     *ec = VNOVNODE;
599                 }
600                 else {
601                     Log("VGetVnode: Bad magic number, vnode %d, volume %u (%s); volume needs salvage\n",  
602                 vnodeNumber, V_id(vp), V_name(vp));
603                     vp->goingOffline = 1;       /* used to call VOffline, but that would mess
604                                                    up the volume ref count if called here */
605                     *ec = VSALVAGE;
606                     mlkReason = 7;
607                 }
608                 VInvalidateVnode_r(vnp);
609                 if (vnp->nUsers-- == 1)
610                     StickOnLruChain_r(vnp,vcp);
611                 ReleaseWriteLock(&vnp->lock);
612                 return NULL;
613             }
614         }
615         IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
616         ReleaseWriteLock(&vnp->lock);
617     } else {
618         VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
619         if (++vnp->nUsers == 1) {
620         /* First user.  Remove it from the LRU chain.  We can assume that
621            there is at least one item in the queue */
622             if (vnp == vcp->lruHead)
623                 vcp->lruHead = vcp->lruHead->lruNext;
624             if (vnp == vcp->lruHead || vcp->lruHead == NULL)
625                 Abort("VGetVnode: lru chain addled!\n");
626             vnp->lruPrev->lruNext = vnp->lruNext;
627             vnp->lruNext->lruPrev = vnp->lruPrev;
628         }
629     }
630     VOL_UNLOCK
631     if (locktype == READ_LOCK)
632         ObtainReadLock(&vnp->lock);
633     else {
634         ObtainWriteLock(&vnp->lock);
635 #ifdef AFS_PTHREAD_ENV
636         vnp->writer = pthread_self();
637 #else /* AFS_PTHREAD_ENV */
638         LWP_CurrentProcess(&vnp->writer);
639 #endif /* AFS_PTHREAD_ENV */
640     }
641     VOL_LOCK
642     /* Check that the vnode hasn't been removed while we were obtaining
643        the lock */
644     VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
645     if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)){
646         if (vnp->nUsers-- == 1)
647             StickOnLruChain_r(vnp,vcp);
648         if (locktype == READ_LOCK)
649             ReleaseReadLock(&vnp->lock);
650         else
651             ReleaseWriteLock(&vnp->lock);
652         *ec = VNOVNODE;
653         mlkReason = 8;
654         /* vnode is labelled correctly by now, so we don't have to invalidate it */
655         return NULL;
656     }
657     if (programType == fileServer)
658         VBumpVolumeUsage_r(vnp->volumePtr);/* Hack; don't know where it should be
659                                               called from.  Maybe VGetVolume */
660     return vnp;
661 }
662
663
664 int  TrustVnodeCacheEntry = 1;
665 /* This variable is bogus--when it's set to 0, the hash chains fill
666    up with multiple versions of the same vnode.  Should fix this!! */
667 void
668 VPutVnode(ec,vnp)
669     Error *ec;
670     register Vnode *vnp;
671 {
672     VOL_LOCK
673     VPutVnode_r(ec,vnp);
674     VOL_UNLOCK
675 }
676
677 void
678 VPutVnode_r(ec,vnp)
679     Error *ec;
680     register Vnode *vnp;
681 {
682     int writeLocked, offset;
683     VnodeClass class;
684     struct VnodeClassInfo *vcp;
685     int code;
686
687     *ec = 0;
688     assert (vnp->nUsers != 0);
689     class = vnodeIdToClass(vnp->vnodeNumber);
690     vcp = &VnodeClassInfo[class];
691     assert(vnp->disk.vnodeMagic == vcp->magic);
692     VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
693
694     writeLocked = WriteLocked(&vnp->lock);
695     if (writeLocked) {
696 #ifdef AFS_PTHREAD_ENV
697         pthread_t thisProcess = pthread_self();
698 #else /* AFS_PTHREAD_ENV */
699         PROCESS thisProcess;
700         LWP_CurrentProcess(&thisProcess);
701 #endif /* AFS_PTHREAD_ENV */
702         VNLog(201, 2, (afs_int32) vnp,
703               ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) | vnp->delete);
704         if (thisProcess != vnp->writer)
705             Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
706         if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
707             Volume *vp = vnp->volumePtr;
708             afs_int32 now = FT_ApproxTime();
709             assert(vnp->cacheCheck == vp->cacheCheck);
710
711             if (vnp->delete) {
712                 /* No longer any directory entries for this vnode. Free the Vnode */
713                 memset(&vnp->disk, 0, sizeof (vnp->disk));
714                 mlkLastDelete = vnp->vnodeNumber;
715                 /* delete flag turned off further down */
716                 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
717             } else if (vnp->changed_newTime) {
718                 vnp->disk.serverModifyTime = now;
719             }
720             if (vnp->changed_newTime)
721                 V_updateDate(vp) = vp->updateTime = now;
722
723             /* The vnode has been changed. Write it out to disk */
724             if (!V_inUse(vp)) {
725                 assert(V_needsSalvaged(vp));
726                 *ec = VSALVAGE;
727             } else {
728                 IHandle_t *ihP = vp->vnodeIndex[class].handle;
729                 FdHandle_t *fdP;
730                 VOL_UNLOCK
731                 fdP = IH_OPEN(ihP);
732                 if (fdP == NULL)
733                     Abort("VPutVnode: can't open index file!\n");
734                 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
735                 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
736                    Abort("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
737                          fdP, offset, errno);
738                 }
739                 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
740                 if (code != vcp->diskSize) {
741                     /* Don't force volume offline if the inumber is out of
742                      * range or the inode table is full.
743                      */
744                     VOL_LOCK
745                     if (code == BAD_IGET) {
746                         Log("VPutVnode: bad inumber %s\n",
747                             PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
748                         *ec = VIO;
749                     } else {
750                         Log("VPutVnode: Couldn't write vnode %d, volume %u (%s) (error %d)\n",
751                             vnp->vnodeNumber, V_id(vnp->volumePtr),
752                             V_name(vnp->volumePtr), code);
753                         VForceOffline_r(vp);
754                         *ec = VSALVAGE;
755                     }
756                     VOL_UNLOCK
757                     FDH_REALLYCLOSE(fdP);
758                 } else {
759                     FDH_CLOSE(fdP);
760                 }
761                 VOL_LOCK
762
763                 /* If the vnode is to be deleted, and we wrote the vnode out,
764                  * free its bitmap entry. Do after the vnode is written so we
765                  * don't allocate from bitmap before the vnode is written
766                  * (doing so could cause a "addled bitmap" message).
767                  */
768                 if (vnp->delete && !*ec) {
769                     VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
770                                        vnodeIdToBitNumber(vnp->vnodeNumber));
771                 }
772             }
773             vcp->writes++;
774             vnp->changed_newTime = vnp->changed_oldTime = 0;
775         }
776     } else { /* Not write locked */
777         if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
778             Abort("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n", vnp);
779     }
780
781     /* Do not look at disk portion of vnode after this point; it may
782        have been deleted above */
783     if (vnp->nUsers-- == 1)
784         StickOnLruChain_r(vnp,vcp);
785     vnp->delete = 0;
786
787     if (writeLocked)
788         ReleaseWriteLock(&vnp->lock);
789     else
790         ReleaseReadLock(&vnp->lock);
791 }
792
793 /*
794  * Make an attempt to convert a vnode lock from write to read.
795  * Do nothing if the vnode isn't write locked or the vnode has
796  * been deleted.
797  */
798 int VVnodeWriteToRead(ec,vnp)
799     Error *ec;
800     register Vnode *vnp;
801 {
802     int retVal;
803     VOL_LOCK
804     retVal = VVnodeWriteToRead_r(ec, vnp);
805     VOL_UNLOCK
806     return retVal;
807 }
808
809 int VVnodeWriteToRead_r(ec,vnp)
810     Error *ec;
811     register Vnode *vnp;
812 {
813     int writeLocked;
814     VnodeClass class;
815     struct VnodeClassInfo *vcp;
816     int code;
817 #ifdef AFS_PTHREAD_ENV
818     pthread_t thisProcess;
819 #else /* AFS_PTHREAD_ENV */
820     PROCESS thisProcess;
821 #endif /* AFS_PTHREAD_ENV */
822
823     *ec = 0;
824     assert (vnp->nUsers != 0);
825     class = vnodeIdToClass(vnp->vnodeNumber);
826     vcp = &VnodeClassInfo[class];
827     assert(vnp->disk.vnodeMagic == vcp->magic);
828     writeLocked = WriteLocked(&vnp->lock);
829     VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
830
831     if (!writeLocked) {
832         return 0;
833     }
834
835 #ifdef AFS_PTHREAD_ENV
836     thisProcess = pthread_self();
837 #else /* AFS_PTHREAD_ENV */
838     LWP_CurrentProcess(&thisProcess);
839 #endif /* AFS_PTHREAD_ENV */
840
841     VNLog(301, 2, (afs_int32) vnp,
842           ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) |
843           vnp->delete);
844     if (thisProcess != vnp->writer)
845         Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
846     if (vnp->delete) {
847         return 0;
848     }
849     if (vnp->changed_oldTime || vnp->changed_newTime) {
850         Volume *vp = vnp->volumePtr;
851         afs_int32 now = FT_ApproxTime();
852         assert(vnp->cacheCheck == vp->cacheCheck);
853         if (vnp->changed_newTime)
854             vnp->disk.serverModifyTime = now;
855         if (vnp->changed_newTime)
856             V_updateDate(vp) = vp->updateTime = now;
857
858         /* The inode has been changed.  Write it out to disk */
859         if (!V_inUse(vp)) {
860             assert(V_needsSalvaged(vp));
861             *ec = VSALVAGE;
862         } else {
863             IHandle_t *ihP = vp->vnodeIndex[class].handle;
864             FdHandle_t *fdP;
865             off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
866             VOL_UNLOCK
867             fdP = IH_OPEN(ihP);
868             if (fdP == NULL)
869                 Abort("VPutVnode: can't open index file!\n");
870             code = FDH_SEEK(fdP, off, SEEK_SET);
871             if (code < 0)
872                 Abort("VPutVnode: can't seek on index file!\n");
873             code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
874             if (code != vcp->diskSize) {
875                 /*
876                  * Don't force volume offline if the inumber is out of
877                  * range or the inode table is full.
878                  */
879                 VOL_LOCK
880                 if(code == BAD_IGET)
881                 {
882                             Log("VPutVnode: bad inumber %d\n",
883                                     vp->vnodeIndex[class].handle->ih_ino);
884                             *ec = VIO;
885                 } else {
886                     Log("VPutVnode: Couldn't write vnode %d, volume %u (%s)\n",
887                         vnp->vnodeNumber, V_id(vnp->volumePtr),
888                         V_name(vnp->volumePtr));
889                         VForceOffline_r(vp);
890                     *ec = VSALVAGE;
891                 }
892                 VOL_UNLOCK
893             }
894             FDH_CLOSE(fdP);
895             VOL_LOCK
896         }
897         vcp->writes++;
898         vnp->changed_newTime = vnp->changed_oldTime = 0;
899     }
900
901     ConvertWriteToReadLock(&vnp->lock);
902     return 0;
903 }
904
905 /* Move the vnode, vnp, to the new hash table given by the
906    hash table index, newHash */
907 static int moveHash(vnp, newHash)
908     register Vnode *vnp;
909     bit32 newHash;
910 {
911     Vnode *tvnp;
912  /* Remove it from the old hash chain */
913     tvnp = VnodeHashTable[vnp->hashIndex];
914     if (tvnp == vnp)
915         VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
916     else {
917         while (tvnp && tvnp->hashNext != vnp)
918             tvnp = tvnp->hashNext;
919         if (tvnp)
920             tvnp->hashNext = vnp->hashNext;
921     }
922  /* Add it to the new hash chain */
923     vnp->hashNext = VnodeHashTable[newHash];
924     VnodeHashTable[newHash] = vnp;
925     vnp->hashIndex = newHash;
926     return 0;
927 }
928
929 void
930 StickOnLruChain_r(vnp,vcp)
931     register Vnode *vnp;
932     register struct VnodeClassInfo *vcp;
933 {
934  /* Add it to the circular LRU list */
935     if (vcp->lruHead == NULL)
936         Abort("VPutVnode: vcp->lruHead==NULL");
937     else {
938         vnp->lruNext = vcp->lruHead;
939         vnp->lruPrev = vcp->lruHead->lruPrev;
940         vcp->lruHead->lruPrev = vnp;
941         vnp->lruPrev->lruNext = vnp;
942         vcp->lruHead = vnp;
943     }
944  /* If the vnode was just deleted, put it at the end of the chain so it
945     will be reused immediately */
946     if (vnp->delete)
947         vcp->lruHead = vnp->lruNext;
948  /* If caching is turned off, set volumeptr to NULL to invalidate the
949     entry */
950     if (!TrustVnodeCacheEntry)
951         vnp->volumePtr = NULL;
952 }
953
954 /* VCloseVnodeFiles - called when a volume is going off line. All open
955  * files for vnodes in that volume are closed. This might be excessive,
956  * since we may only be taking one volume of a volume group offline.
957  */
958 void VCloseVnodeFiles_r(Volume *vp)
959 {
960     int i;
961     Vnode *vnp;
962
963     for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
964         for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
965             if (vnp->volumePtr == vp) {
966                 IH_REALLYCLOSE(vnp->handle);
967             }
968         }
969     }
970 }
971
972 /* VReleaseVnodeFiles - called when a volume is going detached. All open
973  * files for vnodes in that volume are closed and all inode handles
974  * for vnodes in that volume are released.
975  */
976 void VReleaseVnodeFiles_r(Volume *vp)
977 {
978     int i;
979     Vnode *vnp;
980
981     for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
982         for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
983             if (vnp->volumePtr == vp) {
984                 IH_RELEASE(vnp->handle);
985             }
986         }
987     }
988 }