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