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