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