DEVEL15-kill-uninitialized-variable-warnings-20081026
[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  * Portions Copyright (c) 2005-2008 Sine Nomine Associates
10  */
11
12 /*
13         System:         VICE-TWO
14         Module:         vnode.c
15         Institution:    The Information Technology Center, Carnegie-Mellon University
16
17  */
18 #include <afsconfig.h>
19 #include <afs/param.h>
20 #define MAXINT     (~(1<<((sizeof(int)*8)-1)))
21
22 RCSID
23     ("$Header$");
24
25 #include <errno.h>
26 #include <stdio.h>
27 #include <string.h>
28 #ifdef AFS_PTHREAD_ENV
29 #include <assert.h>
30 #else /* AFS_PTHREAD_ENV */
31 #include <afs/assert.h>
32 #endif /* AFS_PTHREAD_ENV */
33
34 #include <rx/xdr.h>
35 #include "rx/rx_queue.h"
36 #include <afs/afsint.h>
37 #include "nfs.h"
38 #include <afs/errors.h>
39 #include "lock.h"
40 #include "lwp.h"
41 #include <afs/afssyscalls.h>
42 #include "ihandle.h"
43 #include "vnode.h"
44 #include "volume.h"
45 #include "volume_inline.h"
46 #include "vnode_inline.h"
47 #include "partition.h"
48 #include "salvsync.h"
49 #if defined(AFS_SGI_ENV)
50 #include "sys/types.h"
51 #include "fcntl.h"
52 #undef min
53 #undef max
54 #include "stdlib.h"
55 #endif
56 #ifdef AFS_NT40_ENV
57 #include <fcntl.h>
58 #include "ntops.h"
59 #else
60 #include <sys/file.h>
61 #ifdef  AFS_SUN5_ENV
62 #include <sys/fcntl.h>
63 #endif
64 #include <unistd.h>
65 #endif /* AFS_NT40_ENV */
66 #include <sys/stat.h>
67
68 /*@printflike@*/ extern void Log(const char *format, ...);
69
70 /*@printflike@*/ extern void Abort(const char *format, ...);
71
72
73 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
74
75 private void StickOnLruChain_r(register Vnode * vnp,
76                                register struct VnodeClassInfo *vcp);
77
78 extern int LogLevel;
79
80
81
82
83 #define BAD_IGET        -1000
84
85 /* There are two separate vnode queue types defined here:
86  * Each hash conflict chain -- is singly linked, with a single head
87  * pointer. New entries are added at the beginning. Old
88  * entries are removed by linear search, which generally
89  * only occurs after a disk read).
90  * LRU chain -- is doubly linked, single head pointer.
91  * Entries are added at the head, reclaimed from the tail,
92  * or removed from anywhere in the queue.
93  */
94
95
96 /* Vnode hash table.  Find hash chain by taking lower bits of
97  * (volume_hash_offset + vnode).
98  * This distributes the root inodes of the volumes over the
99  * hash table entries and also distributes the vnodes of
100  * volumes reasonably fairly.  The volume_hash_offset field
101  * for each volume is established as the volume comes on line
102  * by using the VOLUME_HASH_OFFSET macro.  This distributes the
103  * volumes fairly among the cache entries, both when servicing
104  * a small number of volumes and when servicing a large number.
105  */
106
107 /* logging stuff for finding bugs */
108 #define THELOGSIZE      5120
109 static afs_int32 theLog[THELOGSIZE];
110 static afs_int32 vnLogPtr = 0;
111 void
112 VNLog(afs_int32 aop, afs_int32 anparms, afs_int32 av1, afs_int32 av2, 
113       afs_int32 av3, afs_int32 av4)
114 {
115     register afs_int32 temp;
116     afs_int32 data[4];
117
118     /* copy data to array */
119     data[0] = av1;
120     data[1] = av2;
121     data[2] = av3;
122     data[3] = av4;
123     if (anparms > 4)
124         anparms = 4;            /* do bounds checking */
125
126     temp = (aop << 16) | anparms;
127     theLog[vnLogPtr++] = temp;
128     if (vnLogPtr >= THELOGSIZE)
129         vnLogPtr = 0;
130     for (temp = 0; temp < anparms; temp++) {
131         theLog[vnLogPtr++] = data[temp];
132         if (vnLogPtr >= THELOGSIZE)
133             vnLogPtr = 0;
134     }
135 }
136
137 /* VolumeHashOffset -- returns a new value to be stored in the
138  * volumeHashOffset of a Volume structure.  Called when a
139  * volume is initialized.  Sets the volumeHashOffset so that
140  * vnode cache entries are distributed reasonably between
141  * volumes (the root vnodes of the volumes will hash to
142  * different values, and spacing is maintained between volumes
143  * when there are not many volumes represented), and spread
144  * equally amongst vnodes within a single volume.
145  */
146 int
147 VolumeHashOffset_r(void)
148 {
149     static int nextVolumeHashOffset = 0;
150     /* hashindex Must be power of two in size */
151 #   define hashShift 3
152 #   define hashMask ((1<<hashShift)-1)
153     static byte hashindex[1 << hashShift] =
154         { 0, 128, 64, 192, 32, 160, 96, 224 };
155     int offset;
156     offset = hashindex[nextVolumeHashOffset & hashMask]
157         + (nextVolumeHashOffset >> hashShift);
158     nextVolumeHashOffset++;
159     return offset;
160 }
161
162 /* Change hashindex (above) if you change this constant */
163 #define VNODE_HASH_TABLE_SIZE 256
164 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
165 #define VNODE_HASH(volumeptr,vnodenumber)\
166     ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
167
168
169 /**
170  * add a vnode to the volume's vnode list.
171  *
172  * @param[in] vp   volume object pointer
173  * @param[in] vnp  vnode object pointer
174  *
175  * @note for DAFS, it may seem like we should be acquiring a lightweight ref
176  *       on vp, but this would actually break things.  Right now, this is ok
177  *       because we destroy all vnode cache contents during during volume
178  *       detach.
179  *
180  * @pre VOL_LOCK held
181  *
182  * @internal volume package internal use only
183  */
184 void
185 AddToVVnList(Volume * vp, Vnode * vnp)
186 {
187     if (queue_IsOnQueue(vnp))
188         return;
189
190     Vn_volume(vnp) = vp;
191     Vn_cacheCheck(vnp) = vp->cacheCheck;
192     queue_Append(&vp->vnode_list, vnp);
193     Vn_stateFlags(vnp) |= VN_ON_VVN;
194 }
195
196 /**
197  * delete a vnode from the volume's vnode list.
198  *
199  * @pre VOL_LOCK held
200  *
201  * @internal volume package internal use only
202  */
203 void
204 DeleteFromVVnList(register Vnode * vnp)
205 {
206     Vn_volume(vnp) = NULL;
207
208     if (!queue_IsOnQueue(vnp))
209         return;
210
211     queue_Remove(vnp);
212     Vn_stateFlags(vnp) &= ~(VN_ON_VVN);
213 }
214
215 /**
216  * add a vnode to the end of the lru.
217  *
218  * @param[in] vcp  vnode class info object pointer
219  * @param[in] vnp  vnode object pointer
220  *
221  * @internal vnode package internal use only
222  */
223 void
224 AddToVnLRU(struct VnodeClassInfo * vcp, Vnode * vnp)
225 {
226     if (Vn_stateFlags(vnp) & VN_ON_LRU) {
227         return;
228     }
229
230     /* Add it to the circular LRU list */
231     if (vcp->lruHead == NULL)
232         Abort("VPutVnode: vcp->lruHead==NULL");
233     else {
234         vnp->lruNext = vcp->lruHead;
235         vnp->lruPrev = vcp->lruHead->lruPrev;
236         vcp->lruHead->lruPrev = vnp;
237         vnp->lruPrev->lruNext = vnp;
238         vcp->lruHead = vnp;
239     }
240
241     /* If the vnode was just deleted, put it at the end of the chain so it
242      * will be reused immediately */
243     if (vnp->delete)
244         vcp->lruHead = vnp->lruNext;
245
246     Vn_stateFlags(vnp) |= VN_ON_LRU;
247 }
248
249 /**
250  * delete a vnode from the lru.
251  *
252  * @param[in] vcp  vnode class info object pointer
253  * @param[in] vnp  vnode object pointer
254  *
255  * @internal vnode package internal use only
256  */
257 void
258 DeleteFromVnLRU(struct VnodeClassInfo * vcp, Vnode * vnp)
259 {
260     if (!(Vn_stateFlags(vnp) & VN_ON_LRU)) {
261         return;
262     }
263
264     if (vnp == vcp->lruHead)
265         vcp->lruHead = vcp->lruHead->lruNext;
266
267     if ((vnp == vcp->lruHead) || 
268         (vcp->lruHead == NULL))
269         Abort("DeleteFromVnLRU: lru chain addled!\n");
270
271     vnp->lruPrev->lruNext = vnp->lruNext;
272     vnp->lruNext->lruPrev = vnp->lruPrev;
273
274     Vn_stateFlags(vnp) &= ~(VN_ON_LRU);
275 }
276
277 /**
278  * add a vnode to the vnode hash table.
279  *
280  * @param[in] vnp  vnode object pointer
281  *
282  * @pre VOL_LOCK held
283  *
284  * @post vnode on hash
285  *
286  * @internal vnode package internal use only
287  */
288 void
289 AddToVnHash(Vnode * vnp)
290 {
291     unsigned int newHash;
292
293     if (!(Vn_stateFlags(vnp) & VN_ON_HASH)) {
294         newHash = VNODE_HASH(Vn_volume(vnp), Vn_id(vnp));
295         vnp->hashNext = VnodeHashTable[newHash];
296         VnodeHashTable[newHash] = vnp;
297         vnp->hashIndex = newHash;
298
299         Vn_stateFlags(vnp) |= VN_ON_HASH;
300     }
301 }
302
303 /**
304  * delete a vnode from the vnode hash table.
305  *
306  * @param[in] vnp
307  * @param[in] hash
308  *
309  * @pre VOL_LOCK held
310  *
311  * @post vnode removed from hash
312  *
313  * @internal vnode package internal use only
314  */
315 void
316 DeleteFromVnHash(Vnode * vnp)
317 {
318     Vnode * tvnp;
319
320     if (Vn_stateFlags(vnp) & VN_ON_HASH) {
321         tvnp = VnodeHashTable[vnp->hashIndex];
322         if (tvnp == vnp)
323             VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
324         else {
325             while (tvnp && tvnp->hashNext != vnp)
326                 tvnp = tvnp->hashNext;
327             if (tvnp)
328                 tvnp->hashNext = vnp->hashNext;
329         }
330
331         vnp->hashNext = NULL;
332         vnp->hashIndex = 0;
333         Vn_stateFlags(vnp) &= ~(VN_ON_HASH);
334     }
335 }
336
337
338 /**
339  * invalidate a vnode cache entry.
340  *
341  * @param[in] avnode   vnode object pointer
342  *
343  * @pre VOL_LOCK held
344  *
345  * @post vnode metadata invalidated.
346  *       vnode removed from hash table.
347  *       DAFS: vnode state set to VN_STATE_INVALID.
348  *
349  * @internal vnode package internal use only
350  */
351 void
352 VInvalidateVnode_r(register struct Vnode *avnode)
353 {
354     avnode->changed_newTime = 0;        /* don't let it get flushed out again */
355     avnode->changed_oldTime = 0;
356     avnode->delete = 0;         /* it isn't deleted, really */
357     avnode->cacheCheck = 0;     /* invalid: prevents future vnode searches from working */
358     DeleteFromVnHash(avnode);
359 #ifdef AFS_DEMAND_ATTACH_FS
360     VnChangeState_r(avnode, VN_STATE_INVALID);
361 #endif
362 }
363
364
365 /**
366  * initialize vnode cache for a given vnode class.
367  *
368  * @param[in] class    vnode class
369  * @param[in] nVnodes  size of cache
370  *
371  * @post vnode cache allocated and initialized
372  *
373  * @internal volume package internal use only
374  *
375  * @note generally called by VInitVolumePackage_r
376  *
377  * @see VInitVolumePackage_r
378  */
379 int
380 VInitVnodes(VnodeClass class, int nVnodes)
381 {
382     byte *va;
383     register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
384
385     vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
386     vcp->cacheSize = nVnodes;
387     switch (class) {
388     case vSmall:
389         assert(CHECKSIZE_SMALLVNODE);
390         vcp->lruHead = NULL;
391         vcp->residentSize = SIZEOF_SMALLVNODE;
392         vcp->diskSize = SIZEOF_SMALLDISKVNODE;
393         vcp->magic = SMALLVNODEMAGIC;
394         break;
395     case vLarge:
396         vcp->lruHead = NULL;
397         vcp->residentSize = SIZEOF_LARGEVNODE;
398         vcp->diskSize = SIZEOF_LARGEDISKVNODE;
399         vcp->magic = LARGEVNODEMAGIC;
400         break;
401     }
402     {
403         int s = vcp->diskSize - 1;
404         int n = 0;
405         while (s)
406             s >>= 1, n++;
407         vcp->logSize = n;
408     }
409
410     if (nVnodes == 0)
411         return 0;
412
413     va = (byte *) calloc(nVnodes, vcp->residentSize);
414     assert(va != NULL);
415     while (nVnodes--) {
416         Vnode *vnp = (Vnode *) va;
417         Vn_refcount(vnp) = 0;   /* no context switches */
418         Vn_stateFlags(vnp) |= VN_ON_LRU;
419 #ifdef AFS_DEMAND_ATTACH_FS
420         assert(pthread_cond_init(&Vn_stateCV(vnp), NULL) == 0);
421         Vn_state(vnp) = VN_STATE_INVALID;
422         Vn_readers(vnp) = 0;
423 #else /* !AFS_DEMAND_ATTACH_FS */
424         Lock_Init(&vnp->lock);
425 #endif /* !AFS_DEMAND_ATTACH_FS */
426         vnp->changed_oldTime = 0;
427         vnp->changed_newTime = 0;
428         Vn_volume(vnp) = NULL;
429         Vn_cacheCheck(vnp) = 0;
430         vnp->delete = Vn_id(vnp) = 0;
431 #ifdef AFS_PTHREAD_ENV
432         vnp->writer = (pthread_t) 0;
433 #else /* AFS_PTHREAD_ENV */
434         vnp->writer = (PROCESS) 0;
435 #endif /* AFS_PTHREAD_ENV */
436         vnp->hashIndex = 0;
437         vnp->handle = NULL;
438         Vn_class(vnp) = vcp;
439         if (vcp->lruHead == NULL)
440             vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
441         else {
442             vnp->lruNext = vcp->lruHead;
443             vnp->lruPrev = vcp->lruHead->lruPrev;
444             vcp->lruHead->lruPrev = vnp;
445             vnp->lruPrev->lruNext = vnp;
446             vcp->lruHead = vnp;
447         }
448         va += vcp->residentSize;
449     }
450     return 0;
451 }
452
453
454 /**
455  * allocate an unused vnode from the lru chain.
456  *
457  * @param[in] vcp  vnode class info object pointer
458  *
459  * @pre VOL_LOCK is held
460  *
461  * @post vnode object is removed from lru, and vnode hash table.
462  *       vnode is disassociated from volume object.
463  *       state is set to VN_STATE_INVALID.
464  *       inode handle is released.
465  *
466  * @note we traverse backwards along the lru circlist.  It shouldn't 
467  *       be necessary to specify that nUsers == 0 since if it is in the list, 
468  *       nUsers should be 0.  Things shouldn't be in lruq unless no one is 
469  *       using them.
470  *
471  * @warning DAFS: VOL_LOCK is dropped while doing inode handle release
472  *
473  * @return vnode object pointer
474  */
475 Vnode *
476 VGetFreeVnode_r(struct VnodeClassInfo * vcp)
477 {
478     register Vnode *vnp;
479
480     vnp = vcp->lruHead->lruPrev;
481 #ifdef AFS_DEMAND_ATTACH_FS
482     if (Vn_refcount(vnp) != 0 || VnIsExclusiveState(Vn_state(vnp)) ||
483         Vn_readers(vnp) != 0)
484         Abort("VGetFreeVnode_r: in-use vnode in lruq");
485 #else
486     if (Vn_refcount(vnp) != 0 || CheckLock(&vnp->lock))
487         Abort("VGetFreeVnode_r: locked vnode in lruq");
488 #endif
489     VNLog(1, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
490
491     /* 
492      * it's going to be overwritten soon enough.
493      * remove from LRU, delete hash entry, and 
494      * disassociate from old parent volume before
495      * we have a chance to drop the vol glock
496      */
497     DeleteFromVnLRU(vcp, vnp);
498     DeleteFromVnHash(vnp);
499     if (Vn_volume(vnp)) {
500         DeleteFromVVnList(vnp);
501     }
502
503     /* drop the file descriptor */
504     if (vnp->handle) {
505 #ifdef AFS_DEMAND_ATTACH_FS
506         VnChangeState_r(vnp, VN_STATE_RELEASING);
507         VOL_UNLOCK;
508 #endif
509         IH_RELEASE(vnp->handle);
510 #ifdef AFS_DEMAND_ATTACH_FS
511         VOL_LOCK;
512 #endif
513     }
514
515 #ifdef AFS_DEMAND_ATTACH_FS
516     VnChangeState_r(vnp, VN_STATE_INVALID);
517 #endif
518
519     return vnp;
520 }
521
522
523 /**
524  * lookup a vnode in the vnode cache hash table.
525  *
526  * @param[in] vp       pointer to volume object
527  * @param[in] vnodeId  vnode id
528  *
529  * @pre VOL_LOCK held
530  *
531  * @post matching vnode object or NULL is returned
532  *
533  * @return vnode object pointer
534  *   @retval NULL   no matching vnode object was found in the cache
535  *
536  * @internal vnode package internal use only
537  *
538  * @note this symbol is exported strictly for fssync debug protocol use
539  */
540 Vnode *
541 VLookupVnode(Volume * vp, VnodeId vnodeId)
542 {
543     Vnode * vnp;
544     unsigned int newHash;
545
546     newHash = VNODE_HASH(vp, vnodeId);
547     for (vnp = VnodeHashTable[newHash];
548          (vnp && 
549           ((Vn_id(vnp) != vnodeId) || 
550            (Vn_volume(vnp) != vp) ||
551            (vp->cacheCheck != Vn_cacheCheck(vnp))));
552          vnp = vnp->hashNext);
553
554     return vnp;
555 }
556
557
558 Vnode *
559 VAllocVnode(Error * ec, Volume * vp, VnodeType type)
560 {
561     Vnode *retVal;
562     VOL_LOCK;
563     retVal = VAllocVnode_r(ec, vp, type);
564     VOL_UNLOCK;
565     return retVal;
566 }
567
568 /**
569  * allocate a new vnode.
570  *
571  * @param[out] ec    error code return
572  * @param[in]  vp    volume object pointer
573  * @param[in]  type  desired vnode type
574  *
575  * @return vnode object pointer
576  *
577  * @pre VOL_LOCK held;
578  *      heavyweight ref held on vp
579  *
580  * @post vnode allocated and returned
581  */
582 Vnode *
583 VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
584 {
585     register Vnode *vnp;
586     VnodeId vnodeNumber;
587     int bitNumber, code;
588     register struct VnodeClassInfo *vcp;
589     VnodeClass class;
590     Unique unique;
591 #ifdef AFS_DEMAND_ATTACH_FS
592     VolState vol_state_save;
593 #endif
594
595     *ec = 0;
596
597 #ifdef AFS_DEMAND_ATTACH_FS
598     /*
599      * once a volume has entered an error state, don't permit
600      * further operations to proceed
601      *  -- tkeiser 11/21/2007
602      */
603     VWaitExclusiveState_r(vp);
604     if (VIsErrorState(V_attachState(vp))) {
605         /* XXX is VSALVAGING acceptable here? */
606         *ec = DAFS_VSALVAGE;
607         return NULL;
608     }
609 #endif
610
611     if (programType == fileServer && !V_inUse(vp)) {
612         if (vp->specialStatus) {
613             *ec = vp->specialStatus;
614         } else {
615             *ec = VOFFLINE;
616         }
617         return NULL;
618     }
619     class = vnodeTypeToClass(type);
620     vcp = &VnodeClassInfo[class];
621
622     if (!VolumeWriteable(vp)) {
623         *ec = (bit32) VREADONLY;
624         return NULL;
625     }
626
627     unique = vp->nextVnodeUnique++;
628     if (!unique)
629         unique = vp->nextVnodeUnique++;
630
631     if (vp->nextVnodeUnique > V_uniquifier(vp)) {
632         VUpdateVolume_r(ec, vp, 0);
633         if (*ec)
634             return NULL;
635     }
636
637     if (programType == fileServer) {
638         VAddToVolumeUpdateList_r(ec, vp);
639         if (*ec)
640             return NULL;
641     }
642
643     /* Find a slot in the bit map */
644     bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
645                                     VOL_ALLOC_BITMAP_WAIT);
646     if (*ec)
647         return NULL;
648     vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
649
650     /*
651      * DAFS:
652      * at this point we should be assured that V_attachState(vp) is non-exclusive
653      */
654
655  vnrehash:
656     VNLog(2, 1, vnodeNumber, 0, 0, 0);
657     /* Prepare to move it to the new hash chain */
658     vnp = VLookupVnode(vp, vnodeNumber);
659     if (vnp) {
660         /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
661          * so we may have to wait for it below */
662         VNLog(3, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
663
664         VnCreateReservation_r(vnp);
665         if (Vn_refcount(vnp) == 1) {
666             /* we're the only user */
667             /* This won't block */
668             VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
669         } else {
670             /* other users present; follow locking hierarchy */
671             VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, MIGHT_DEADLOCK);
672
673 #ifdef AFS_DEMAND_ATTACH_FS
674             /*
675              * DAFS:
676              * vnode was cached, wait for any existing exclusive ops to finish.
677              * once we have reacquired the lock, re-verify volume state.
678              *
679              * note: any vnode error state is related to the old vnode; disregard.
680              */
681             VnWaitQuiescent_r(vnp);
682             if (VIsErrorState(V_attachState(vp))) {
683                 VnUnlock(vnp, WRITE_LOCK);
684                 VnCancelReservation_r(vnp);
685                 *ec = DAFS_VSALVAGE;
686                 return NULL;
687             }
688 #endif
689
690             /*
691              * verify state of the world hasn't changed
692              *
693              * (technically, this should never happen because cachecheck
694              *  is only updated during a volume attach, which should not
695              *  happen when refs are held)
696              */
697             if (Vn_volume(vnp)->cacheCheck != Vn_cacheCheck(vnp)) {
698                 VnUnlock(vnp, WRITE_LOCK);
699                 VnCancelReservation_r(vnp);
700                 goto vnrehash;
701             }
702         }
703
704     } else {
705         /* no such vnode in the cache */
706
707         vnp = VGetFreeVnode_r(vcp);
708
709         /* Initialize the header fields so noone allocates another
710          * vnode with the same number */
711         Vn_id(vnp) = vnodeNumber;
712         VnCreateReservation_r(vnp);
713         AddToVVnList(vp, vnp);
714 #ifdef AFS_DEMAND_ATTACH_FS
715         AddToVnHash(vnp);
716 #endif
717
718         /* This will never block (guaranteed by check in VGetFreeVnode_r() */
719         VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
720
721 #ifdef AFS_DEMAND_ATTACH_FS
722         VnChangeState_r(vnp, VN_STATE_ALLOC);
723 #endif
724
725         /* Sanity check:  is this vnode really not in use? */
726         {
727             int size;
728             IHandle_t *ihP = vp->vnodeIndex[class].handle;
729             FdHandle_t *fdP;
730             off_t off = vnodeIndexOffset(vcp, vnodeNumber);
731
732             /* XXX we have a potential race here if two threads
733              * allocate new vnodes at the same time, and they
734              * both decide it's time to extend the index
735              * file size...
736              */
737 #ifdef AFS_DEMAND_ATTACH_FS
738             /*
739              * this race has been eliminated for the DAFS case
740              * using exclusive state VOL_STATE_VNODE_ALLOC
741              *
742              * if this becomes a bottleneck, there are ways to
743              * improve parallelism for this code path
744              *   -- tkeiser 11/28/2007 
745              */
746             VCreateReservation_r(vp);
747             VWaitExclusiveState_r(vp);
748             vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_ALLOC);
749 #endif
750
751             VOL_UNLOCK;
752             fdP = IH_OPEN(ihP);
753             if (fdP == NULL) {
754                 Log("VAllocVnode: can't open index file!\n");
755                 goto error_encountered;
756             }
757             if ((size = FDH_SIZE(fdP)) < 0) {
758                 Log("VAllocVnode: can't stat index file!\n");
759                 goto error_encountered;
760             }
761             if (FDH_SEEK(fdP, off, SEEK_SET) < 0) {
762                 Log("VAllocVnode: can't seek on index file!\n");
763                 goto error_encountered;
764             }
765             if (off + vcp->diskSize <= size) {
766                 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) != vcp->diskSize) {
767                     Log("VAllocVnode: can't read index file!\n");
768                     goto error_encountered;
769                 }
770                 if (vnp->disk.type != vNull) {
771                     Log("VAllocVnode:  addled bitmap or index!\n");
772                     goto error_encountered;
773                 }
774             } else {
775                 /* growing file - grow in a reasonable increment */
776                 char *buf = (char *)malloc(16 * 1024);
777                 if (!buf)
778                     Abort("VAllocVnode: malloc failed\n");
779                 memset(buf, 0, 16 * 1024);
780                 (void)FDH_WRITE(fdP, buf, 16 * 1024);
781                 free(buf);
782             }
783             FDH_CLOSE(fdP);
784             VOL_LOCK;
785
786 #ifdef AFS_DEMAND_ATTACH_FS
787             VChangeState_r(vp, vol_state_save);
788             VCancelReservation_r(vp);
789 #endif
790             goto sane;
791
792
793         error_encountered:
794 #ifdef AFS_DEMAND_ATTACH_FS
795             /* 
796              * close the file handle
797              * acquire VOL_LOCK
798              * invalidate the vnode
799              * free up the bitmap entry (although salvager should take care of it)
800              * salvage the volume
801              * drop vnode lock and refs
802              */
803             if (fdP)
804                 FDH_CLOSE(fdP);
805             VOL_LOCK;
806             VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class], bitNumber);
807             VInvalidateVnode_r(vnp);
808             VnUnlock(vnp, WRITE_LOCK);
809             VnCancelReservation_r(vnp);
810             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
811             VCancelReservation_r(vp);
812             return NULL;
813 #else
814             assert(1 == 2);
815 #endif
816
817         }
818     sane:
819         VNLog(4, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
820 #ifndef AFS_DEMAND_ATTACH_FS
821         AddToVnHash(vnp);
822 #endif
823     }
824
825     VNLog(5, 1, (afs_int32) vnp, 0, 0, 0);
826     memset(&vnp->disk, 0, sizeof(vnp->disk));
827     vnp->changed_newTime = 0;   /* set this bit when vnode is updated */
828     vnp->changed_oldTime = 0;   /* set this on CopyOnWrite. */
829     vnp->delete = 0;
830     vnp->disk.vnodeMagic = vcp->magic;
831     vnp->disk.type = type;
832     vnp->disk.uniquifier = unique;
833     vnp->handle = NULL;
834     vcp->allocs++;
835     vp->header->diskstuff.filecount++;
836 #ifdef AFS_DEMAND_ATTACH_FS
837     VnChangeState_r(vnp, VN_STATE_EXCLUSIVE);
838 #endif
839     return vnp;
840 }
841
842 /**
843  * load a vnode from disk.
844  *
845  * @param[out] ec     client error code return
846  * @param[in]  vp     volume object pointer
847  * @param[in]  vnp    vnode object pointer
848  * @param[in]  vcp    vnode class info object pointer
849  * @param[in]  class  vnode class enumeration
850  *
851  * @pre vnode is registered in appropriate data structures;
852  *      caller holds a ref on vnode; VOL_LOCK is held
853  *
854  * @post vnode data is loaded from disk.
855  *       vnode state is set to VN_STATE_ONLINE.
856  *       on failure, vnode is invalidated.
857  *
858  * @internal vnode package internal use only
859  */
860 static void
861 VnLoad(Error * ec, Volume * vp, Vnode * vnp, 
862        struct VnodeClassInfo * vcp, VnodeClass class)
863 {
864     /* vnode not cached */
865     Error error;
866     int n, dosalv = 1;
867     IHandle_t *ihP = vp->vnodeIndex[class].handle;
868     FdHandle_t *fdP;
869
870     *ec = 0;
871     vcp->reads++;
872
873 #ifdef AFS_DEMAND_ATTACH_FS
874     VnChangeState_r(vnp, VN_STATE_LOAD);
875 #endif
876
877     /* This will never block */
878     VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
879
880     VOL_UNLOCK;
881     fdP = IH_OPEN(ihP);
882     if (fdP == NULL) {
883         Log("VnLoad: can't open index dev=%u, i=%s\n", vp->device,
884             PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
885         *ec = VIO;
886         goto error_encountered_nolock;
887     } else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, Vn_id(vnp)), SEEK_SET)
888                < 0) {
889         Log("VnLoad: can't seek on index file vn=%u\n", Vn_id(vnp));
890         *ec = VIO;
891         goto error_encountered_nolock;
892     } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
893                != vcp->diskSize) {
894         /* Don't take volume off line if the inumber is out of range
895          * or the inode table is full. */
896         if (n == BAD_IGET) {
897             Log("VnLoad: bad inumber %s\n",
898                 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
899             *ec = VIO;
900             dosalv = 0;
901         } else if (n == -1 && errno == EIO) {
902             /* disk error; salvage */
903             Log("VnLoad: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", Vn_id(vnp), V_id(vp), V_name(vp));
904         } else {
905             /* vnode is not allocated */
906             if (LogLevel >= 5) 
907                 Log("VnLoad: Couldn't read vnode %u, volume %u (%s); read %d bytes, errno %d\n", 
908                     Vn_id(vnp), V_id(vp), V_name(vp), n, errno);
909             *ec = VIO;
910             dosalv = 0;
911         }
912         goto error_encountered_nolock;
913     }
914     FDH_CLOSE(fdP);
915     VOL_LOCK;
916
917     /* Quick check to see that the data is reasonable */
918     if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
919         if (vnp->disk.type == vNull) {
920             *ec = VNOVNODE;
921             dosalv = 0;
922         } else {
923             struct vnodeIndex *index = &vp->vnodeIndex[class];
924             unsigned int bitNumber = vnodeIdToBitNumber(Vn_id(vnp));
925             unsigned int offset = bitNumber >> 3;
926
927             /* Test to see if vnode number is valid. */
928             if ((offset >= index->bitmapSize)
929                 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
930                     == 0)) {
931                 Log("VnLoad: Request for unallocated vnode %u, volume %u (%s) denied.\n", Vn_id(vnp), V_id(vp), V_name(vp));
932                 *ec = VNOVNODE;
933                 dosalv = 0;
934             } else {
935                 Log("VnLoad: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n", Vn_id(vnp), V_id(vp), V_name(vp));
936             }
937         }
938         goto error_encountered;
939     }
940
941     IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
942     VnUnlock(vnp, WRITE_LOCK);
943 #ifdef AFS_DEMAND_ATTACH_FS
944     VnChangeState_r(vnp, VN_STATE_ONLINE);
945 #endif
946     return;
947
948
949  error_encountered_nolock:
950     if (fdP) {
951         FDH_REALLYCLOSE(fdP);
952     }
953     VOL_LOCK;
954
955  error_encountered:
956     if (dosalv) {
957 #ifdef AFS_DEMAND_ATTACH_FS
958         VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
959 #else
960         VForceOffline_r(vp, 0);
961         error = VSALVAGE;
962 #endif
963         if (!*ec)
964             *ec = error;
965     }
966
967     VInvalidateVnode_r(vnp);
968     VnUnlock(vnp, WRITE_LOCK);
969 }
970
971 /**
972  * store a vnode to disk.
973  *
974  * @param[out] ec     error code output
975  * @param[in]  vp     volume object pointer
976  * @param[in]  vnp    vnode object pointer
977  * @param[in]  vcp    vnode class info object pointer
978  * @param[in]  class  vnode class enumeration
979  *
980  * @pre VOL_LOCK held.
981  *      caller holds refs to volume and vnode.
982  *      DAFS: caller is responsible for performing state sanity checks.
983  *
984  * @post vnode state is stored to disk.
985  *
986  * @internal vnode package internal use only
987  */
988 static void
989 VnStore(Error * ec, Volume * vp, Vnode * vnp, 
990         struct VnodeClassInfo * vcp, VnodeClass class)
991 {
992     int offset, code;
993     IHandle_t *ihP = vp->vnodeIndex[class].handle;
994     FdHandle_t *fdP;
995 #ifdef AFS_DEMAND_ATTACH_FS
996     VnState vn_state_save;
997 #endif
998
999     *ec = 0;
1000
1001 #ifdef AFS_DEMAND_ATTACH_FS
1002     vn_state_save = VnChangeState_r(vnp, VN_STATE_STORE);
1003 #endif
1004
1005     offset = vnodeIndexOffset(vcp, Vn_id(vnp));
1006     VOL_UNLOCK;
1007     fdP = IH_OPEN(ihP);
1008     if (fdP == NULL) {
1009         Log("VnStore: can't open index file!\n");
1010         goto error_encountered;
1011     }
1012     if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
1013         Log("VnStore: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
1014             fdP, offset, errno);
1015         goto error_encountered;
1016     }
1017
1018     code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
1019     if (code != vcp->diskSize) {
1020         /* Don't force volume offline if the inumber is out of
1021          * range or the inode table is full.
1022          */
1023         FDH_REALLYCLOSE(fdP);
1024         if (code == BAD_IGET) {
1025             Log("VnStore: bad inumber %s\n",
1026                 PrintInode(NULL,
1027                            vp->vnodeIndex[class].handle->ih_ino));
1028             *ec = VIO;
1029             VOL_LOCK;
1030 #ifdef AFS_DEMAND_ATTACH_FS
1031             VnChangeState_r(vnp, VN_STATE_ERROR);
1032 #endif
1033         } else {
1034             Log("VnStore: Couldn't write vnode %u, volume %u (%s) (error %d)\n", Vn_id(vnp), V_id(Vn_volume(vnp)), V_name(Vn_volume(vnp)), code);
1035 #ifdef AFS_DEMAND_ATTACH_FS
1036             goto error_encountered;
1037 #else
1038             VOL_LOCK;
1039             VForceOffline_r(vp, 0);
1040             *ec = VSALVAGE;
1041 #endif
1042         }
1043         return;
1044     } else {
1045         FDH_CLOSE(fdP);
1046     }
1047
1048     VOL_LOCK;
1049 #ifdef AFS_DEMAND_ATTACH_FS
1050     VnChangeState_r(vnp, vn_state_save);
1051 #endif
1052     return;
1053     
1054  error_encountered:
1055 #ifdef AFS_DEMAND_ATTACH_FS
1056     /* XXX instead of dumping core, let's try to request a salvage
1057      * and just fail the putvnode */
1058     if (fdP)
1059         FDH_CLOSE(fdP);
1060     VOL_LOCK;
1061     VnChangeState_r(vnp, VN_STATE_ERROR);
1062     VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1063 #else
1064     assert(1 == 2);
1065 #endif
1066 }
1067
1068 /**
1069  * get a handle to a vnode object.
1070  *
1071  * @param[out] ec           error code
1072  * @param[in]  vp           volume object
1073  * @param[in]  vnodeNumber  vnode id
1074  * @param[in]  locktype     type of lock to acquire
1075  *
1076  * @return vnode object pointer
1077  *
1078  * @see VGetVnode_r
1079  */
1080 Vnode *
1081 VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
1082 {                               /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
1083     Vnode *retVal;
1084     VOL_LOCK;
1085     retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
1086     VOL_UNLOCK;
1087     return retVal;
1088 }
1089
1090 /**
1091  * get a handle to a vnode object.
1092  *
1093  * @param[out] ec           error code
1094  * @param[in]  vp           volume object
1095  * @param[in]  vnodeNumber  vnode id
1096  * @param[in]  locktype     type of lock to acquire
1097  *
1098  * @return vnode object pointer
1099  *
1100  * @internal vnode package internal use only
1101  *
1102  * @pre VOL_LOCK held.
1103  *      heavyweight ref held on volume object.
1104  */
1105 Vnode *
1106 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
1107 {                               /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
1108     register Vnode *vnp;
1109     int code;
1110     VnodeClass class;
1111     struct VnodeClassInfo *vcp;
1112     Volume * oldvp = NULL;
1113
1114     *ec = 0;
1115
1116     if (vnodeNumber == 0) {
1117         *ec = VNOVNODE;
1118         return NULL;
1119     }
1120
1121     VNLog(100, 1, vnodeNumber, 0, 0, 0);
1122
1123 #ifdef AFS_DEMAND_ATTACH_FS
1124     /*
1125      * once a volume has entered an error state, don't permit
1126      * further operations to proceed
1127      *  -- tkeiser 11/21/2007
1128      */
1129     VWaitExclusiveState_r(vp);
1130     if (VIsErrorState(V_attachState(vp))) {
1131         /* XXX is VSALVAGING acceptable here? */
1132         *ec = VSALVAGING;
1133         return NULL;
1134     }
1135 #endif
1136
1137     if (programType == fileServer && !V_inUse(vp)) {
1138         *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
1139
1140         /* If the volume is VBUSY (being cloned or dumped) and this is
1141          * a READ operation, then don't fail.
1142          */
1143         if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
1144             return NULL;
1145         }
1146         *ec = 0;
1147     }
1148     class = vnodeIdToClass(vnodeNumber);
1149     vcp = &VnodeClassInfo[class];
1150     if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
1151         *ec = (bit32) VREADONLY;
1152         return NULL;
1153     }
1154
1155     if (locktype == WRITE_LOCK && programType == fileServer) {
1156         VAddToVolumeUpdateList_r(ec, vp);
1157         if (*ec) {
1158             return NULL;
1159         }
1160     }
1161
1162     vcp->gets++;
1163
1164     /* See whether the vnode is in the cache. */
1165     vnp = VLookupVnode(vp, vnodeNumber);
1166     if (vnp) {
1167         /* vnode is in cache */
1168
1169         VNLog(101, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
1170         VnCreateReservation_r(vnp);
1171
1172 #ifdef AFS_DEMAND_ATTACH_FS
1173         /*
1174          * this is the one DAFS case where we may run into contention.
1175          * here's the basic control flow:
1176          *
1177          * if locktype is READ_LOCK:
1178          *   wait until vnode is not exclusive
1179          *   set to VN_STATE_READ
1180          *   increment read count
1181          *   done
1182          * else
1183          *   wait until vnode is quiescent
1184          *   set to VN_STATE_EXCLUSIVE
1185          *   done
1186          */
1187         if (locktype == READ_LOCK) {
1188             VnWaitExclusiveState_r(vnp);
1189         } else {
1190             VnWaitQuiescent_r(vnp);
1191         }
1192
1193         if (VnIsErrorState(Vn_state(vnp))) {
1194             VnCancelReservation_r(vnp);
1195             *ec = VSALVAGE;
1196             return NULL;
1197         }
1198 #endif /* AFS_DEMAND_ATTACH_FS */
1199     } else {
1200         /* vnode not cached */
1201
1202         /* Not in cache; tentatively grab most distantly used one from the LRU
1203          * chain */
1204         vcp->reads++;
1205         vnp = VGetFreeVnode_r(vcp);
1206
1207         /* Initialize */
1208         vnp->changed_newTime = vnp->changed_oldTime = 0;
1209         vnp->delete = 0;
1210         Vn_id(vnp) = vnodeNumber;
1211         VnCreateReservation_r(vnp);
1212         AddToVVnList(vp, vnp);
1213 #ifdef AFS_DEMAND_ATTACH_FS
1214         AddToVnHash(vnp);
1215 #endif
1216
1217         /*
1218          * XXX for non-DAFS, there is a serious
1219          * race condition here:
1220          *
1221          * two threads can race to load a vnode.  the net
1222          * result is two struct Vnodes can be allocated
1223          * and hashed, which point to the same underlying
1224          * disk data store.  conflicting vnode locks can
1225          * thus be held concurrently.
1226          *
1227          * for non-DAFS to be safe, VOL_LOCK really shouldn't
1228          * be dropped in VnLoad.  Of course, this would likely
1229          * lead to an unacceptable slow-down.
1230          */
1231
1232         VnLoad(ec, vp, vnp, vcp, class);
1233         if (*ec) {
1234             VnCancelReservation_r(vnp);
1235             return NULL;
1236         }
1237 #ifndef AFS_DEMAND_ATTACH_FS
1238         AddToVnHash(vnp);
1239 #endif
1240         /*
1241          * DAFS:
1242          * there is no possibility for contention. we "own" this vnode.
1243          */
1244     }
1245
1246     /*
1247      * DAFS:
1248      * it is imperative that nothing drop vol lock between here
1249      * and the VnBeginRead/VnChangeState stanza below
1250      */
1251
1252     VnLock(vnp, locktype, VOL_LOCK_HELD, MIGHT_DEADLOCK);
1253
1254     /* Check that the vnode hasn't been removed while we were obtaining
1255      * the lock */
1256     VNLog(102, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
1257     if ((vnp->disk.type == vNull) || (Vn_cacheCheck(vnp) == 0)) {
1258         VnUnlock(vnp, locktype);
1259         VnCancelReservation_r(vnp);
1260         *ec = VNOVNODE;
1261         /* vnode is labelled correctly by now, so we don't have to invalidate it */
1262         return NULL;
1263     }
1264
1265 #ifdef AFS_DEMAND_ATTACH_FS
1266     if (locktype == READ_LOCK) {
1267         VnBeginRead_r(vnp);
1268     } else {
1269         VnChangeState_r(vnp, VN_STATE_EXCLUSIVE);
1270     }
1271 #endif
1272
1273     if (programType == fileServer)
1274         VBumpVolumeUsage_r(Vn_volume(vnp));     /* Hack; don't know where it should be
1275                                                  * called from.  Maybe VGetVolume */
1276     return vnp;
1277 }
1278
1279
1280 int TrustVnodeCacheEntry = 1;
1281 /* This variable is bogus--when it's set to 0, the hash chains fill
1282    up with multiple versions of the same vnode.  Should fix this!! */
1283 void
1284 VPutVnode(Error * ec, register Vnode * vnp)
1285 {
1286     VOL_LOCK;
1287     VPutVnode_r(ec, vnp);
1288     VOL_UNLOCK;
1289 }
1290
1291 /**
1292  * put back a handle to a vnode object.
1293  *
1294  * @param[out] ec   client error code
1295  * @param[in]  vnp  vnode object pointer
1296  *
1297  * @pre VOL_LOCK held.
1298  *      ref held on vnode.
1299  *
1300  * @post ref dropped on vnode.
1301  *       if vnode was modified or deleted, it is written out to disk
1302  *       (assuming a write lock was held).
1303  *
1304  * @internal volume package internal use only
1305  */
1306 void
1307 VPutVnode_r(Error * ec, register Vnode * vnp)
1308 {
1309     int writeLocked;
1310     VnodeClass class;
1311     struct VnodeClassInfo *vcp;
1312     int code;
1313
1314     *ec = 0;
1315     assert(Vn_refcount(vnp) != 0);
1316     class = vnodeIdToClass(Vn_id(vnp));
1317     vcp = &VnodeClassInfo[class];
1318     assert(vnp->disk.vnodeMagic == vcp->magic);
1319     VNLog(200, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
1320
1321 #ifdef AFS_DEMAND_ATTACH_FS
1322     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
1323 #else
1324     writeLocked = WriteLocked(&vnp->lock);
1325 #endif
1326
1327     if (writeLocked) {
1328         /* sanity checks */
1329 #ifdef AFS_PTHREAD_ENV
1330         pthread_t thisProcess = pthread_self();
1331 #else /* AFS_PTHREAD_ENV */
1332         PROCESS thisProcess;
1333         LWP_CurrentProcess(&thisProcess);
1334 #endif /* AFS_PTHREAD_ENV */
1335         VNLog(201, 2, (afs_int32) vnp,
1336               ((vnp->changed_newTime) << 1) | ((vnp->
1337                                                 changed_oldTime) << 1) | vnp->
1338               delete, 0, 0);
1339         if (thisProcess != vnp->writer)
1340             Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
1341                   vnp);
1342
1343
1344         if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
1345             Volume *vp = Vn_volume(vnp);
1346             afs_uint32 now = FT_ApproxTime();
1347             assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
1348
1349             if (vnp->delete) {
1350                 /* No longer any directory entries for this vnode. Free the Vnode */
1351                 memset(&vnp->disk, 0, sizeof(vnp->disk));
1352                 /* delete flag turned off further down */
1353                 VNLog(202, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
1354             } else if (vnp->changed_newTime) {
1355                 vnp->disk.serverModifyTime = now;
1356             }
1357             if (vnp->changed_newTime)
1358             {
1359                 V_updateDate(vp) = vp->updateTime = now;
1360                 if(V_volUpCounter(vp)<MAXINT)
1361                         V_volUpCounter(vp)++;
1362             }
1363
1364             /* The vnode has been changed. Write it out to disk */
1365             if (!V_inUse(vp)) {
1366 #ifdef AFS_DEMAND_ATTACH_FS
1367                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1368 #else
1369                 assert(V_needsSalvaged(vp));
1370                 *ec = VSALVAGE;
1371 #endif
1372             } else {
1373                 VnStore(ec, vp, vnp, vcp, class);
1374
1375                 /* If the vnode is to be deleted, and we wrote the vnode out,
1376                  * free its bitmap entry. Do after the vnode is written so we
1377                  * don't allocate from bitmap before the vnode is written
1378                  * (doing so could cause a "addled bitmap" message).
1379                  */
1380                 if (vnp->delete && !*ec) {
1381                     if (Vn_volume(vnp)->header->diskstuff.filecount-- < 1)
1382                         Vn_volume(vnp)->header->diskstuff.filecount = 0;
1383                     VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
1384                                        vnodeIdToBitNumber(Vn_id(vnp)));
1385                 }
1386             }
1387             vcp->writes++;
1388             vnp->changed_newTime = vnp->changed_oldTime = 0;
1389         }
1390 #ifdef AFS_DEMAND_ATTACH_FS
1391         VnChangeState_r(vnp, VN_STATE_ONLINE);
1392 #endif
1393     } else {                    /* Not write locked */
1394         if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
1395             Abort
1396                 ("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n",
1397                  vnp);
1398 #ifdef AFS_DEMAND_ATTACH_FS
1399         VnEndRead_r(vnp);
1400 #endif
1401     }
1402
1403     /* Do not look at disk portion of vnode after this point; it may
1404      * have been deleted above */
1405     vnp->delete = 0;
1406     VnUnlock(vnp, ((writeLocked) ? WRITE_LOCK : READ_LOCK));
1407     VnCancelReservation_r(vnp);
1408 }
1409
1410 /*
1411  * Make an attempt to convert a vnode lock from write to read.
1412  * Do nothing if the vnode isn't write locked or the vnode has
1413  * been deleted.
1414  */
1415 int
1416 VVnodeWriteToRead(Error * ec, register Vnode * vnp)
1417 {
1418     int retVal;
1419     VOL_LOCK;
1420     retVal = VVnodeWriteToRead_r(ec, vnp);
1421     VOL_UNLOCK;
1422     return retVal;
1423 }
1424
1425 /**
1426  * convert vnode handle from mutually exclusive to shared access.
1427  *
1428  * @param[out] ec   client error code
1429  * @param[in]  vnp  vnode object pointer
1430  *
1431  * @return unspecified use (see out argument 'ec' for error code return)
1432  *
1433  * @pre VOL_LOCK held.
1434  *      ref held on vnode.
1435  *      write lock held on vnode.
1436  *
1437  * @post read lock held on vnode.
1438  *       if vnode was modified, it has been written to disk.
1439  *
1440  * @internal volume package internal use only
1441  */
1442 int
1443 VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
1444 {
1445     int writeLocked;
1446     VnodeClass class;
1447     struct VnodeClassInfo *vcp;
1448     int code;
1449 #ifdef AFS_PTHREAD_ENV
1450     pthread_t thisProcess;
1451 #else /* AFS_PTHREAD_ENV */
1452     PROCESS thisProcess;
1453 #endif /* AFS_PTHREAD_ENV */
1454
1455     *ec = 0;
1456     assert(Vn_refcount(vnp) != 0);
1457     class = vnodeIdToClass(Vn_id(vnp));
1458     vcp = &VnodeClassInfo[class];
1459     assert(vnp->disk.vnodeMagic == vcp->magic);
1460     VNLog(300, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
1461
1462 #ifdef AFS_DEMAND_ATTACH_FS
1463     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
1464 #else
1465     writeLocked = WriteLocked(&vnp->lock);
1466 #endif
1467     if (!writeLocked) {
1468         return 0;
1469     }
1470
1471
1472     VNLog(301, 2, (afs_int32) vnp,
1473           ((vnp->changed_newTime) << 1) | ((vnp->
1474                                             changed_oldTime) << 1) | vnp->
1475           delete, 0, 0);
1476
1477     /* sanity checks */
1478 #ifdef AFS_PTHREAD_ENV
1479     thisProcess = pthread_self();
1480 #else /* AFS_PTHREAD_ENV */
1481     LWP_CurrentProcess(&thisProcess);
1482 #endif /* AFS_PTHREAD_ENV */
1483     if (thisProcess != vnp->writer)
1484         Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
1485               (int)vnp);
1486
1487     if (vnp->delete) {
1488         return 0;
1489     }
1490     if (vnp->changed_oldTime || vnp->changed_newTime) {
1491         Volume *vp = Vn_volume(vnp);
1492         afs_uint32 now = FT_ApproxTime();
1493         assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
1494         if (vnp->changed_newTime)
1495             vnp->disk.serverModifyTime = now;
1496         if (vnp->changed_newTime)
1497             V_updateDate(vp) = vp->updateTime = now;
1498
1499         /* The inode has been changed.  Write it out to disk */
1500         if (!V_inUse(vp)) {
1501 #ifdef AFS_DEMAND_ATTACH_FS
1502             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1503 #else
1504             assert(V_needsSalvaged(vp));
1505             *ec = VSALVAGE;
1506 #endif
1507         } else {
1508             VnStore(ec, vp, vnp, vcp, class);
1509         }
1510     sane:
1511         vcp->writes++;
1512         vnp->changed_newTime = vnp->changed_oldTime = 0;
1513     }
1514
1515     vnp->writer = 0;
1516 #ifdef AFS_DEMAND_ATTACH_FS
1517     VnChangeState_r(vnp, VN_STATE_ONLINE);
1518     VnBeginRead_r(vnp);
1519 #else
1520     ConvertWriteToReadLock(&vnp->lock);
1521 #endif
1522     return 0;
1523 }
1524
1525 /* VCloseVnodeFiles - called when a volume is going off line. All open
1526  * files for vnodes in that volume are closed. This might be excessive,
1527  * since we may only be taking one volume of a volume group offline.
1528  */
1529 void
1530 VCloseVnodeFiles_r(Volume * vp)
1531 {
1532     int i;
1533     Vnode *vnp, *nvnp;
1534 #ifdef AFS_DEMAND_ATTACH_FS
1535     VolState vol_state_save;
1536
1537     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_CLOSE);
1538     VOL_UNLOCK;
1539 #endif /* AFS_DEMAND_ATTACH_FS */
1540
1541     for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
1542         IH_REALLYCLOSE(vnp->handle);
1543         DeleteFromVVnList(vnp);
1544     }
1545
1546 #ifdef AFS_DEMAND_ATTACH_FS
1547     VOL_LOCK;
1548     VChangeState_r(vp, vol_state_save);
1549 #endif /* AFS_DEMAND_ATTACH_FS */
1550 }
1551
1552 /**
1553  * shut down all vnode cache state for a given volume.
1554  *
1555  * @param[in] vp  volume object pointer
1556  *
1557  * @pre VOL_LOCK is held
1558  *
1559  * @post all file descriptors closed.
1560  *       all inode handles released.
1561  *       all vnode cache objects disassociated from volume.
1562  *
1563  * @note for DAFS, these operations are performed outside the vol glock under
1564  *       volume exclusive state VOL_STATE_VNODE_RELEASE.  Please further note
1565  *       that it would be a bug to acquire and release a volume reservation
1566  *       during this exclusive operation.  This is due to the fact that we are
1567  *       generally called during the refcount 1->0 transition.
1568  *
1569  * @internal this routine is internal to the volume package
1570  */
1571 void
1572 VReleaseVnodeFiles_r(Volume * vp)
1573 {
1574     int i;
1575     Vnode *vnp, *nvnp;
1576 #ifdef AFS_DEMAND_ATTACH_FS
1577     VolState vol_state_save;
1578
1579     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_RELEASE);
1580     VOL_UNLOCK;
1581 #endif /* AFS_DEMAND_ATTACH_FS */
1582
1583     for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
1584         IH_RELEASE(vnp->handle);
1585         DeleteFromVVnList(vnp);
1586     }
1587
1588 #ifdef AFS_DEMAND_ATTACH_FS
1589     VOL_LOCK;
1590     VChangeState_r(vp, vol_state_save);
1591 #endif /* AFS_DEMAND_ATTACH_FS */
1592 }