death to trailing whitespace
[openafs.git] / src / afs / afs_disconnected.c
1 /*
2  * This software has been released under the terms of the IBM Public
3  * License.  For details, see the LICENSE file in the top-level source
4  * directory or online at http://www.openafs.org/dl/license10.html
5  */
6
7 #include <afsconfig.h>
8 #include "afs/param.h"
9
10
11 #include "afs/sysincludes.h"
12 #include "afsincludes.h"
13 #include "afs/afs_stats.h"      /* statistics */
14 #include "afs/lock.h"
15 #include "afs/afs_cbqueue.h"
16
17 #define dv_match(vc, fstat)                              \
18         ((vc->f.m.DataVersion.low == fstat.DataVersion) && \
19         (vc->f.m.DataVersion.high == fstat.dataVersionHigh))
20
21 /*! Circular queue of dirty vcaches */
22 struct afs_q afs_disconDirty;
23
24 /*! Circular queue of vcaches with shadow directories */
25 struct afs_q afs_disconShadow;
26
27 /*! Locks both of these lists. Must be write locked for anything other than
28  *  list traversal */
29 afs_rwlock_t afs_disconDirtyLock;
30
31 extern afs_int32 *afs_dvhashTbl;        /*Data cache hash table */
32 extern afs_int32 *afs_dchashTbl;        /*Data cache hash table */
33 extern afs_int32 *afs_dvnextTbl;        /*Dcache hash table links */
34 extern afs_int32 *afs_dcnextTbl;        /*Dcache hash table links */
35 extern struct dcache **afs_indexTable;  /*Pointers to dcache entries */
36
37 /*! Vnode number. On file creation, use the current value and increment it.
38  */
39 afs_uint32 afs_DisconVnode = 2;
40
41 /*! Conflict policy. */
42 enum {
43         CLIENT_WINS = 0,
44         SERVER_WINS,
45         LAST_CLOSER_WINS,
46         ASK
47 };
48
49 afs_int32 afs_ConflictPolicy = SERVER_WINS;
50
51 static void afs_DisconDiscardAllShadows(int, afs_ucred_t *);
52 void afs_DbgListDirEntries(struct VenusFid *afid);
53
54
55 /*!
56  * Find the first dcache of a file that has the specified fid.
57  * Similar to afs_FindDCache, only that it takes a fid instead
58  * of a vcache and it can get the first dcache.
59  *
60  * \param afid
61  *
62  * \return The found dcache or NULL.
63  */
64 struct dcache *
65 afs_FindDCacheByFid(struct VenusFid *afid)
66 {
67     afs_int32 i, index;
68     struct dcache *tdc = NULL;
69
70     i = DVHash(afid);
71     ObtainWriteLock(&afs_xdcache, 758);
72     for (index = afs_dvhashTbl[i]; index != NULLIDX;) {
73         if (afs_indexUnique[index] == afid->Fid.Unique) {
74             tdc = afs_GetDSlot(index, NULL);
75             ReleaseReadLock(&tdc->tlock);
76             if (!FidCmp(&tdc->f.fid, afid)) {
77                 break;          /* leaving refCount high for caller */
78             }
79             afs_PutDCache(tdc);
80         }
81         index = afs_dvnextTbl[index];
82     }
83     ReleaseWriteLock(&afs_xdcache);
84
85     if (index == NULLIDX)
86         tdc = NULL;
87     return tdc;
88 }
89
90 /*!
91  * Generate a store status from a dirty vcache entry.
92  *
93  * \param avc Dirty vcache entry.
94  * \param astat
95  *
96  * \note The vnode must be share locked. It is called only on resync,
97  * where the vnode is write locked locally and and the server.
98  *
99  * \return Mask of operations.
100  */
101 int
102 afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
103 {
104     if (!avc || !astat || !avc->f.ddirty_flags)
105         return 0;
106
107     /* Clean up store stat. */
108     memset(astat, 0, sizeof(struct AFSStoreStatus));
109
110     if (avc->f.ddirty_flags & VDisconSetTime) {
111         /* Update timestamp. */
112         astat->ClientModTime = avc->f.m.Date;
113         astat->Mask |= AFS_SETMODTIME;
114     }
115
116     if (avc->f.ddirty_flags & VDisconSetMode) {
117         /* Copy the mode bits. */
118         astat->UnixModeBits = avc->f.m.Mode;
119         astat->Mask |= AFS_SETMODE;
120    }
121
122    /* XXX: more to come... ?*/
123
124    return astat->Mask;
125 }
126
127 /*!
128  * Hook for filtering the local dir fid by searching the "." entry.
129  *
130  * \param hdata The fid to be filled.
131  */
132 static int
133 get_parent_dir_fid_hook(void *hdata, char *aname, afs_int32 vnode,
134                         afs_int32 unique)
135 {
136     struct VenusFid *tfid = (struct VenusFid *) hdata;
137
138     if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) {
139         tfid->Fid.Vnode = vnode;
140         tfid->Fid.Unique = unique;
141         return 1;
142     }
143
144     return 0;
145 }
146
147 /*!
148  * Get a the dir's fid by looking in the vcache for simple files and
149  * in the ".." entry for directories.
150  *
151  * \param avc The file's vhash entry.
152  * \param afid Put the fid here.
153  *
154  * \return 0 on success, -1 on failure
155  */
156 int
157 afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
158 {
159     struct dcache *tdc;
160
161     afid->Cell = avc->f.fid.Cell;
162     afid->Fid.Volume = avc->f.fid.Fid.Volume;
163
164     switch (vType(avc)) {
165     case VREG:
166     case VLNK:
167         /* Normal files have the dir fid embedded in the vcache. */
168         afid->Fid.Vnode = avc->f.parent.vnode;
169         afid->Fid.Unique = avc->f.parent.unique;
170         break;
171     case VDIR:
172         /* If dir or parent dir created locally*/
173         tdc = afs_FindDCacheByFid(&avc->f.fid);
174         if (tdc) {
175             afid->Fid.Unique = 0;
176             /* Lookup each entry for the fid. It should be the first. */
177             afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid);
178             afs_PutDCache(tdc);
179             if (afid->Fid.Unique == 0) {
180                 return -1;
181             }
182         } else {
183             return -1;
184         }
185         break;
186     default:
187         return -1;
188         break;
189     }
190
191     return 0;
192 }
193
194 struct NameAndFid {
195     struct VenusFid *fid;
196     char *name;
197     int name_len;
198 };
199
200 /*!
201  * Hook that searches a certain fid's name.
202  *
203  * \param hdata NameAndFid structure containin a pointer to a fid
204  * and an allocate name. The name will be filled when hit.
205  */
206 static int
207 get_vnode_name_hook(void *hdata, char *aname, afs_int32 vnode,
208                     afs_int32 unique)
209 {
210     struct NameAndFid *nf = (struct NameAndFid *) hdata;
211
212     if ((nf->fid->Fid.Vnode == vnode) &&
213         (nf->fid->Fid.Unique == unique)) {
214         nf->name_len = strlen(aname);
215         memcpy(nf->name, aname, nf->name_len);
216         nf->name[nf->name_len] = 0;
217
218         return 1;
219     }
220
221     return 0;
222 }
223
224 /*!
225  * Try to get a vnode's name by comparing all parent dir's entries
226  * to the given fid. It can also return the dir's dcache.
227  *
228  * \param avc The file's vcache.
229  * \param afid The parent dir's fid.
230  * \param aname A preallocated string for the name.
231  * \param deleted Has this file been deleted? If yes, use the shadow
232  * dir for looking up the name.
233  */
234 int
235 afs_GetVnodeName(struct vcache *avc, struct VenusFid *afid, char *aname,
236                  int deleted)
237 {
238     int code = 0;
239     struct dcache *tdc;
240     struct vcache *parent_vc;
241     struct NameAndFid tnf;
242     struct VenusFid parent_fid;
243     struct VenusFid shadow_fid;
244
245     /* List dir contents and get it's tdc. */
246     if (deleted) {
247         /* For deleted files, get the shadow dir's tdc: */
248
249         /* Get the parent dir's vcache that contains the shadow fid. */
250         parent_fid.Cell = avc->f.fid.Cell;
251         parent_fid.Fid.Volume = avc->f.fid.Fid.Volume;
252         if (avc->f.ddirty_flags & VDisconRename) {
253             /* For renames the old dir fid is needed. */
254             parent_fid.Fid.Vnode = avc->f.oldParent.vnode;
255             parent_fid.Fid.Unique = avc->f.oldParent.unique;
256         } else {
257             parent_fid.Fid.Vnode = afid->Fid.Vnode;
258             parent_fid.Fid.Unique = afid->Fid.Unique;
259         }
260
261         /* Get the parent dir's vcache that contains the shadow fid. */
262         ObtainSharedLock(&afs_xvcache, 755);
263         parent_vc = afs_FindVCache(&parent_fid, 0, 1);
264         ReleaseSharedLock(&afs_xvcache);
265         if (!parent_vc) {
266             return ENOENT;
267         }
268
269         shadow_fid.Cell = parent_vc->f.fid.Cell;
270         shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume;
271         shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode;
272         shadow_fid.Fid.Unique = parent_vc->f.shadow.unique;
273
274         afs_PutVCache(parent_vc);
275
276         /* Get shadow dir's dcache. */
277         tdc = afs_FindDCacheByFid(&shadow_fid);
278
279     } else {
280
281         /* For normal files, look into the current dir's entry. */
282         tdc = afs_FindDCacheByFid(afid);
283     }                   /* if (deleted) */
284
285     if (tdc) {
286         tnf.fid = &avc->f.fid;
287         tnf.name_len = -1;
288         tnf.name = aname;
289         afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
290         afs_PutDCache(tdc);
291         if (tnf.name_len == -1)
292             code = ENOENT;
293     } else {
294         /* printf("Directory dcache not found!\n"); */
295         code = ENOENT;
296     }
297
298     return code;
299 }
300
301 struct DirtyChildrenCount {
302     struct vcache *vc;
303     afs_uint32 count;
304 };
305
306 /*!
307  * Lookup dirty deleted vnodes in this dir.
308  */
309 static int
310 chk_del_children_hook(void *hdata, char *aname, afs_int32 vnode,
311                       afs_int32 unique)
312 {
313     struct VenusFid tfid;
314     struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
315     struct vcache *tvc;
316
317     if ((aname[0] == '.') && !aname[1])
318         /* Skip processing this dir again.
319          * It would result in an endless loop.
320          */
321         return 0;
322
323     if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
324         /* Don't process parent dir. */
325         return 0;
326
327     /* Get this file's vcache. */
328     tfid.Cell = v->vc->f.fid.Cell;
329     tfid.Fid.Volume = v->vc->f.fid.Fid.Volume;
330     tfid.Fid.Vnode = vnode;
331     tfid.Fid.Unique = unique;
332
333     ObtainSharedLock(&afs_xvcache, 757);
334     tvc = afs_FindVCache(&tfid, 0, 1);
335     ReleaseSharedLock(&afs_xvcache);
336
337     /* Count unfinished dirty children. */
338     if (tvc) {
339         ObtainReadLock(&tvc->lock);
340         if (tvc->f.ddirty_flags)
341             v->count++;
342         ReleaseReadLock(&tvc->lock);
343
344         afs_PutVCache(tvc);
345     }
346
347     return 0;
348 }
349
350 /*!
351  * Check if entries have been deleted in a vnode's shadow
352  * dir.
353  *
354  * \return Returns the number of dirty children.
355  *
356  * \note afs_DDirtyVCListLock must be write locked.
357  */
358 int
359 afs_CheckDeletedChildren(struct vcache *avc)
360 {
361     struct dcache *tdc;
362     struct DirtyChildrenCount dcc;
363     struct VenusFid shadow_fid;
364
365     if (!avc->f.shadow.vnode)
366         /* Empty dir. */
367         return 0;
368
369     shadow_fid.Cell = avc->f.fid.Cell;
370     shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume;
371     shadow_fid.Fid.Vnode = avc->f.shadow.vnode;
372     shadow_fid.Fid.Unique = avc->f.shadow.unique;
373
374     dcc.count = 0;
375
376     /* Get shadow dir's dcache. */
377     tdc = afs_FindDCacheByFid(&shadow_fid);
378     if (tdc) {
379         dcc.vc = avc;
380         afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc);
381         afs_PutDCache(tdc);
382     }
383
384     return dcc.count;
385 }
386
387 /*!
388  * Changes a file's parent fid references.
389  */
390 static int
391 fix_children_fids_hook(void *hdata, char *aname, afs_int32 vnode,
392                        afs_int32 unique)
393 {
394     struct VenusFid tfid;
395     struct VenusFid *afid = (struct VenusFid *) hdata;
396     struct vcache *tvc;
397     struct dcache *tdc = NULL;
398
399     if ((aname[0] == '.') && !aname[1])
400         return 0;
401
402     if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
403         return 0;
404
405     tfid.Cell = afid->Cell;
406     tfid.Fid.Volume = afid->Fid.Volume;
407     tfid.Fid.Vnode = vnode;
408     tfid.Fid.Unique = unique;
409
410     if (!(vnode % 2)) {
411         /* vnode's parity indicates that it's a file. */
412
413         /* Get the vcache. */
414         ObtainSharedLock(&afs_xvcache, 759);
415         tvc = afs_FindVCache(&tfid, 0, 1);
416         ReleaseSharedLock(&afs_xvcache);
417
418         /* Change the fields. */
419         if (tvc) {
420             tvc->f.parent.vnode = afid->Fid.Vnode;
421             tvc->f.parent.unique = afid->Fid.Unique;
422
423             afs_PutVCache(tvc);
424         }
425     } else {
426         /* It's a dir. Fix this dir's .. entry to contain the new fid. */
427         /* Seek the dir's dcache. */
428         tdc = afs_FindDCacheByFid(&tfid);
429         if (tdc) {
430             /* Change the .. entry fid. */
431             afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode);
432             afs_PutDCache(tdc);
433         }
434     }                   /* if (!(vnode % 2))*/
435
436     return 0;
437 }
438
439 /*!
440  * Fixes the parentVnode and parentUnique fields of all
441  * files (not dirs) contained in the directory pointed by
442  * old_fid. This is useful on resync, when a locally created dir
443  * get's a new fid and all the children references must be updated
444  * to reflect the new fid.
445  *
446  * \note The dir's fid hasn't been changed yet, it is still referenced
447  * with the old fid.
448  *
449  * \param old_fid The current dir's fid.
450  * \param new_fid The new dir's fid.
451  */
452 void
453 afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
454 {
455     struct dcache *tdc;
456
457     /* Get shadow dir's dcache. */
458     tdc = afs_FindDCacheByFid(old_fid);
459     /* Change the fids. */
460     if (tdc) {
461         afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid);
462         afs_PutDCache(tdc);
463     }
464 }
465
466 static int
467 list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique)
468 {
469     /* printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique); */
470     return 0;
471 }
472
473 void
474 afs_DbgListDirEntries(struct VenusFid *afid)
475 {
476     struct dcache *tdc;
477
478     /* Get shadow dir's dcache. */
479     tdc = afs_FindDCacheByFid(afid);
480     if (tdc) {
481         afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL);
482         afs_PutDCache(tdc);
483     }
484 }
485
486 /*!
487  * Find the parent vcache for a given child
488  *
489  * \param avc   The vcache whose parent is required
490  * \param afid  Fid structure in which parent's fid should be stored
491  * \param aname An AFSNAMEMAX sized buffer to hold the parents name
492  * \param adp   A pointer to a struct vcache* which will be set to the
493  *              parent vcache
494  *
495  * \return An error code. 0 indicates success, EAGAIN that the vnode should
496  *         be deferred to later in the resync process
497  */
498
499 int
500 afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid,
501                     char *aname, struct vcache **adp)
502 {
503     int code;
504
505     *adp = NULL;
506
507     if (afs_GetParentDirFid(avc, afid)) {
508         /* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */
509         return ENOENT;
510     }
511
512     code = afs_GetVnodeName(avc, afid, aname, deleted);
513     if (code) {
514         /* printf("afs_GetParentVCache: Couldn't find file name\n"); */
515         goto end;
516     }
517
518     ObtainSharedLock(&afs_xvcache, 766);
519     *adp = afs_FindVCache(afid, 0, 1);
520     ReleaseSharedLock(&afs_xvcache);
521     if (!*adp) {
522         /* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */
523         code = ENOENT;
524         goto end;
525     }
526
527     if ((*adp)->f.ddirty_flags & VDisconCreate) {
528         /* printf("afs_GetParentVCache: deferring until parent exists\n"); */
529         code = EAGAIN;
530         goto end;
531     }
532
533 end:
534     if (code && *adp) {
535         afs_PutVCache(*adp);
536         *adp = NULL;
537     }
538     return code;
539 }
540
541
542 /*!
543  * Handles file renaming on reconnection:
544  * - Get the old name from the old dir's shadow dir.
545  * - Get the new name from the current dir.
546  * - Old dir fid and new dir fid are collected along the way.
547  * */
548 int
549 afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
550 {
551     struct VenusFid old_pdir_fid, new_pdir_fid;
552     char *old_name = NULL, *new_name = NULL;
553     struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
554     struct AFSVolSync tsync;
555     struct afs_conn *tc;
556     afs_uint32 code = 0;
557     XSTATS_DECLS;
558
559     /* Get old dir vcache. */
560     old_pdir_fid.Cell = avc->f.fid.Cell;
561     old_pdir_fid.Fid.Volume = avc->f.fid.Fid.Volume;
562     old_pdir_fid.Fid.Vnode = avc->f.oldParent.vnode;
563     old_pdir_fid.Fid.Unique = avc->f.oldParent.unique;
564
565     /* Get old name. */
566     old_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
567     if (!old_name) {
568         /* printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n"); */
569         return ENOMEM;
570     }
571     code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1);
572     if (code) {
573         /* printf("afs_ProcessOpRename: Couldn't find old name.\n"); */
574         goto done;
575     }
576
577     /* Alloc data first. */
578     new_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
579     if (!new_name) {
580         /* printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); */
581         code = ENOMEM;
582         goto done;
583     }
584
585     if (avc->f.ddirty_flags & VDisconRenameSameDir) {
586         /* If we're in the same dir, don't do the lookups all over again,
587          * just copy fid and vcache from the old dir.
588          */
589         memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid));
590     } else {
591         /* Get parent dir's FID.*/
592         if (afs_GetParentDirFid(avc, &new_pdir_fid)) {
593             /* printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n"); */
594             code = ENOENT;
595             goto done;
596         }
597     }
598
599     /* And finally get the new name. */
600     code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0);
601     if (code) {
602         /* printf("afs_ProcessOpRename: Couldn't find new name.\n"); */
603         goto done;
604     }
605
606     /* Send to data to server. */
607     do {
608         tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK);
609         if (tc) {
610             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
611             RX_AFS_GUNLOCK();
612             code = RXAFS_Rename(tc->id,
613                 (struct AFSFid *)&old_pdir_fid.Fid,
614                 old_name,
615                 (struct AFSFid *)&new_pdir_fid.Fid,
616                 new_name,
617                 &OutOldDirStatus,
618                 &OutNewDirStatus,
619                 &tsync);
620             RX_AFS_GLOCK();
621             XSTATS_END_TIME;
622         } else
623             code = -1;
624
625     } while (afs_Analyze(tc,
626                 code,
627                 &new_pdir_fid,
628                 areq,
629                 AFS_STATS_FS_RPCIDX_RENAME,
630                 SHARED_LOCK,
631                 NULL));
632
633     /* if (code) printf("afs_ProcessOpRename: server code=%u\n", code); */
634 done:
635     if (new_name)
636         afs_osi_Free(new_name, AFSNAMEMAX);
637     if (old_name)
638         afs_osi_Free(old_name, AFSNAMEMAX);
639     return code;
640 }
641
642 /*!
643  * Handles all the reconnection details:
644  * - Get all the details about the vnode: name, fid, and parent dir fid.
645  * - Send data to server.
646  * - Handle errors.
647  * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
648  */
649 int
650 afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq,
651                     afs_ucred_t *acred)
652 {
653     char *tname = NULL, *ttargetName = NULL;
654     struct AFSStoreStatus InStatus;
655     struct AFSFetchStatus OutFidStatus, OutDirStatus;
656     struct VenusFid pdir_fid, newFid;
657     struct AFSCallBack CallBack;
658     struct AFSVolSync tsync;
659     struct vcache *tdp = NULL, *tvc = NULL;
660     struct dcache *tdc = NULL;
661     struct afs_conn *tc;
662     afs_int32 hash, new_hash, index;
663     afs_size_t tlen;
664     int code, op = 0;
665     XSTATS_DECLS;
666
667     tname = afs_osi_Alloc(AFSNAMEMAX);
668     if (!tname)
669         return ENOMEM;
670
671     code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp);
672     if (code)
673         goto end;
674
675     /* This data may also be in linkData, but then we have to deal with
676      * the joy of terminating NULLs and . and file modes. So just get
677      * it from the dcache where it won't have been fiddled with.
678      */
679     if (vType(avc) == VLNK) {
680         afs_size_t offset;
681         struct dcache *tdc;
682         struct osi_file *tfile;
683
684         tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0);
685         if (!tdc) {
686             code = ENOENT;
687             goto end;
688         }
689
690         if (tlen > 1024) {
691             afs_PutDCache(tdc);
692             code = EFAULT;
693             goto end;
694         }
695
696         tlen++; /* space for NULL */
697         ttargetName = afs_osi_Alloc(tlen);
698         if (!ttargetName) {
699             afs_PutDCache(tdc);
700             return ENOMEM;
701         }
702         ObtainReadLock(&tdc->lock);
703         tfile = afs_CFileOpen(&tdc->f.inode);
704         code = afs_CFileRead(tfile, 0, ttargetName, tlen);
705         ttargetName[tlen-1] = '\0';
706         afs_CFileClose(tfile);
707         ReleaseReadLock(&tdc->lock);
708         afs_PutDCache(tdc);
709     }
710
711     /* Set status. */
712     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
713     InStatus.ClientModTime = avc->f.m.Date;
714     InStatus.Owner = avc->f.m.Owner;
715     InStatus.Group = (afs_int32) afs_cr_gid(acred);
716     /* Only care about protection bits. */
717     InStatus.UnixModeBits = avc->f.m.Mode & 0xffff;
718
719     do {
720         tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK);
721         if (tc) {
722             switch (vType(avc)) {
723             case VREG:
724                 /* Make file on server. */
725                 op = AFS_STATS_FS_RPCIDX_CREATEFILE;
726                 XSTATS_START_TIME(op);
727                 RX_AFS_GUNLOCK();
728                 code = RXAFS_CreateFile(tc->id,
729                                         (struct AFSFid *)&tdp->f.fid.Fid,
730                                         tname, &InStatus,
731                                         (struct AFSFid *) &newFid.Fid,
732                                         &OutFidStatus, &OutDirStatus,
733                                         &CallBack, &tsync);
734                 RX_AFS_GLOCK();
735                 XSTATS_END_TIME;
736                 break;
737             case VDIR:
738                 /* Make dir on server. */
739                 op = AFS_STATS_FS_RPCIDX_MAKEDIR;
740                 XSTATS_START_TIME(op);
741                 RX_AFS_GUNLOCK();
742                 code = RXAFS_MakeDir(tc->id, (struct AFSFid *) &tdp->f.fid.Fid,
743                                      tname, &InStatus,
744                                      (struct AFSFid *) &newFid.Fid,
745                                      &OutFidStatus, &OutDirStatus,
746                                      &CallBack, &tsync);
747                 RX_AFS_GLOCK();
748                 XSTATS_END_TIME;
749                 break;
750             case VLNK:
751                 /* Make symlink on server. */
752                 op = AFS_STATS_FS_RPCIDX_SYMLINK;
753                 XSTATS_START_TIME(op);
754                 RX_AFS_GUNLOCK();
755                 code = RXAFS_Symlink(tc->id,
756                                 (struct AFSFid *) &tdp->f.fid.Fid,
757                                 tname, ttargetName, &InStatus,
758                                 (struct AFSFid *) &newFid.Fid,
759                                 &OutFidStatus, &OutDirStatus, &tsync);
760                 RX_AFS_GLOCK();
761                 XSTATS_END_TIME;
762                 break;
763             default:
764                 op = AFS_STATS_FS_RPCIDX_CREATEFILE;
765                 code = 1;
766                 break;
767             }
768         } else
769             code = -1;
770     } while (afs_Analyze(tc, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL));
771
772     /* TODO: Handle errors. */
773     if (code) {
774         /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */
775         goto end;
776     }
777
778     /* The rpc doesn't set the cell number. */
779     newFid.Cell = avc->f.fid.Cell;
780
781     /*
782      * Change the fid in the dir entry.
783      */
784
785     /* Seek the dir's dcache. */
786     tdc = afs_FindDCacheByFid(&tdp->f.fid);
787     if (tdc) {
788         /* And now change the fid in the parent dir entry. */
789         afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode);
790         afs_PutDCache(tdc);
791     }
792
793     if (vType(avc) == VDIR) {
794         /* Change fid in the dir for the "." entry. ".." has alredy been
795          * handled by afs_FixChildrenFids when processing the parent dir.
796          */
797         tdc = afs_FindDCacheByFid(&avc->f.fid);
798         if (tdc) {
799             afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode,
800                               &newFid.Fid.Vnode);
801
802             if (avc->f.m.LinkCount >= 2)
803                 /* For non empty dirs, fix children's parentVnode and
804                  * parentUnique reference.
805                  */
806                 afs_FixChildrenFids(&avc->f.fid, &newFid);
807
808             afs_PutDCache(tdc);
809         }
810     }
811
812     /* Recompute hash chain positions for vnode and dcaches.
813      * Then change to the new FID.
814      */
815
816     /* The vcache goes first. */
817     ObtainWriteLock(&afs_xvcache, 735);
818
819     /* Old fid hash. */
820     hash = VCHash(&avc->f.fid);
821     /* New fid hash. */
822     new_hash = VCHash(&newFid);
823
824     /* Remove hash from old position. */
825     /* XXX: not checking array element contents. It shouldn't be empty.
826      * If it oopses, then something else might be wrong.
827      */
828     if (afs_vhashT[hash] == avc) {
829         /* First in hash chain (might be the only one). */
830         afs_vhashT[hash] = avc->hnext;
831     } else {
832         /* More elements in hash chain. */
833         for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
834             if (tvc->hnext == avc) {
835                 tvc->hnext = avc->hnext;
836                 break;
837             }
838         }
839     }                           /* if (!afs_vhashT[i]->hnext) */
840     QRemove(&avc->vhashq);
841
842     /* Insert hash in new position. */
843     avc->hnext = afs_vhashT[new_hash];
844     afs_vhashT[new_hash] = avc;
845     QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);
846
847     ReleaseWriteLock(&afs_xvcache);
848
849     /* Do the same thing for all dcaches. */
850     hash = DVHash(&avc->f.fid);
851     ObtainWriteLock(&afs_xdcache, 743);
852     for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
853         hash = afs_dvnextTbl[index];
854         tdc = afs_GetDSlot(index, NULL);
855         ReleaseReadLock(&tdc->tlock);
856         if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
857             if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
858
859                 /* Safer but slower. */
860                 afs_HashOutDCache(tdc, 0);
861
862                 /* Put dcache in new positions in the dchash and dvhash. */
863                 new_hash = DCHash(&newFid, tdc->f.chunk);
864                 afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
865                 afs_dchashTbl[new_hash] = tdc->index;
866
867                 new_hash = DVHash(&newFid);
868                 afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
869                 afs_dvhashTbl[new_hash] = tdc->index;
870
871                 afs_indexUnique[tdc->index] = newFid.Fid.Unique;
872                 memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
873            }                   /* if fid match */
874         }                       /* if uniquifier match */
875         if (tdc)
876             afs_PutDCache(tdc);
877     }                           /* for all dcaches in this hash bucket */
878     ReleaseWriteLock(&afs_xdcache);
879
880     /* Now we can set the new fid. */
881     memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid));
882
883 end:
884     if (tdp)
885         afs_PutVCache(tdp);
886     afs_osi_Free(tname, AFSNAMEMAX);
887     if (ttargetName)
888         afs_osi_Free(ttargetName, tlen);
889     return code;
890 }
891
892 /*!
893  * Remove a vnode on the server, be it file or directory.
894  * Not much to do here only get the parent dir's fid and call the
895  * removal rpc.
896  *
897  * \param avc The deleted vcache
898  * \param areq
899  *
900  * \note The vcache refcount should be dropped because it points to
901  * a deleted vnode and has served it's purpouse, but we drop refcount
902  * on shadow dir deletio (we still need it for that).
903  *
904  * \note avc must be write locked.
905  */
906 int
907 afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
908 {
909     char *tname = NULL;
910     struct AFSFetchStatus OutDirStatus;
911     struct VenusFid pdir_fid;
912     struct AFSVolSync tsync;
913     struct afs_conn *tc;
914     struct vcache *tdp = NULL;
915     int code = 0;
916     XSTATS_DECLS;
917
918     tname = afs_osi_Alloc(AFSNAMEMAX);
919     if (!tname) {
920         /* printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n"); */
921         return ENOMEM;
922     }
923
924     code = afs_GetParentVCache(avc, 1, &pdir_fid, tname, &tdp);
925     if (code)
926         goto end;
927
928     if ((vType(avc) == VDIR) && (afs_CheckDeletedChildren(avc))) {
929         /* Deleted children of this dir remain unsynchronized.
930          * Defer this vcache.
931          */
932         code = EAGAIN;
933         goto end;
934     }
935
936     if (vType(avc) == VREG || vType(avc) == VLNK) {
937         /* Remove file on server. */
938         do {
939             tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
940             if (tc) {
941                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
942                 RX_AFS_GUNLOCK();
943                 code = RXAFS_RemoveFile(tc->id,
944                                 &pdir_fid.Fid,
945                                 tname,
946                                 &OutDirStatus,
947                                 &tsync);
948
949                 RX_AFS_GLOCK();
950                 XSTATS_END_TIME;
951             } else
952                 code = -1;
953         } while (afs_Analyze(tc,
954                         code,
955                         &pdir_fid,
956                         areq,
957                         AFS_STATS_FS_RPCIDX_REMOVEFILE,
958                         SHARED_LOCK,
959                         NULL));
960
961     } else if (vType(avc) == VDIR) {
962         /* Remove dir on server. */
963         do {
964             tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
965             if (tc) {
966                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
967                 RX_AFS_GUNLOCK();
968                 code = RXAFS_RemoveDir(tc->id,
969                                 &pdir_fid.Fid,
970                                 tname,
971                                 &OutDirStatus,
972                                 &tsync);
973                 RX_AFS_GLOCK();
974                 XSTATS_END_TIME;
975            } else
976                 code = -1;
977         } while (afs_Analyze(tc,
978                         code,
979                         &pdir_fid,
980                         areq,
981                         AFS_STATS_FS_RPCIDX_REMOVEDIR,
982                         SHARED_LOCK,
983                         NULL));
984
985     }                           /* if (vType(avc) == VREG) */
986
987     /* if (code) printf("afs_ProcessOpRemove: server returned code=%u\n", code); */
988
989 end:
990     afs_osi_Free(tname, AFSNAMEMAX);
991     return code;
992 }
993
994 /*!
995  * Send disconnected file changes to the server.
996  *
997  * \note Call with vnode locked both locally and on the server.
998  *
999  * \param avc Vnode that gets synchronized to the server.
1000  * \param areq Used for obtaining a conn struct.
1001  *
1002  * \return 0 for success. On failure, other error codes.
1003  */
1004 int
1005 afs_SendChanges(struct vcache *avc, struct vrequest *areq)
1006 {
1007     struct afs_conn *tc;
1008     struct AFSStoreStatus sstat;
1009     struct AFSFetchStatus fstat;
1010     struct AFSVolSync tsync;
1011     int code = 0;
1012     int flags = 0;
1013     XSTATS_DECLS;
1014
1015     /* Start multiplexing dirty operations from ddirty_flags field: */
1016     if (avc->f.ddirty_flags & VDisconSetAttrMask) {
1017         /* Setattr OPS: */
1018         /* Turn dirty vc data into a new store status... */
1019         if (afs_GenStoreStatus(avc, &sstat) > 0) {
1020             do {
1021                 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
1022                 if (tc) {
1023                     /* ... and send it. */
1024                     XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS);
1025                     RX_AFS_GUNLOCK();
1026                     code = RXAFS_StoreStatus(tc->id,
1027                                 (struct AFSFid *) &avc->f.fid.Fid,
1028                                 &sstat,
1029                                 &fstat,
1030                                 &tsync);
1031
1032                     RX_AFS_GLOCK();
1033                     XSTATS_END_TIME;
1034                 } else
1035                     code = -1;
1036
1037         } while (afs_Analyze(tc,
1038                         code,
1039                         &avc->f.fid,
1040                         areq,
1041                         AFS_STATS_FS_RPCIDX_STORESTATUS,
1042                         SHARED_LOCK,
1043                         NULL));
1044
1045         }               /* if (afs_GenStoreStatus() > 0)*/
1046     }                   /* disconnected SETATTR */
1047
1048     if (code)
1049         return code;
1050
1051     if (avc->f.ddirty_flags &
1052         (VDisconTrunc
1053         | VDisconWriteClose
1054         | VDisconWriteFlush
1055         | VDisconWriteOsiFlush)) {
1056
1057         /* Truncate OP: */
1058         do {
1059             tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
1060             if (tc) {
1061                 /* Set storing flags. XXX: A tad inefficient ... */
1062                 if (avc->f.ddirty_flags & VDisconWriteClose)
1063                     flags |= AFS_LASTSTORE;
1064                 if (avc->f.ddirty_flags & VDisconWriteOsiFlush)
1065                     flags |= (AFS_SYNC | AFS_LASTSTORE);
1066                 if (avc->f.ddirty_flags & VDisconWriteFlush)
1067                     flags |= AFS_SYNC;
1068
1069                 /* Try to send store to server. */
1070                 /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */
1071                 code = afs_StoreAllSegments(avc, areq, flags);
1072             } else
1073                 code = -1;
1074
1075         } while (afs_Analyze(tc,
1076                         code,
1077                         &avc->f.fid,
1078                         areq,
1079                         AFS_STATS_FS_RPCIDX_STOREDATA,
1080                         SHARED_LOCK,
1081                         NULL));
1082
1083     }                   /* disconnected TRUNC | WRITE */
1084
1085     return code;
1086 }
1087
1088 /*!
1089  * All files that have been dirty before disconnection are going to
1090  * be replayed back to the server.
1091  *
1092  * \param areq Request from the user.
1093  * \param acred User credentials.
1094  *
1095  * \return If all files synchronized succesfully, return 0, otherwise
1096  * return error code
1097  *
1098  * \note For now, it's the request from the PDiscon pioctl.
1099  *
1100  */
1101 int
1102 afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred)
1103 {
1104     struct afs_conn *tc;
1105     struct vcache *tvc;
1106     struct AFSFetchStatus fstat;
1107     struct AFSCallBack callback;
1108     struct AFSVolSync tsync;
1109     int code = 0;
1110     afs_int32 start = 0;
1111     XSTATS_DECLS;
1112     /*AFS_STATCNT(afs_ResyncDisconFiles);*/
1113
1114     ObtainWriteLock(&afs_disconDirtyLock, 707);
1115
1116     while (!QEmpty(&afs_disconDirty)) {
1117         tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1118
1119         /* Can't lock tvc whilst holding the discon dirty lock */
1120         ReleaseWriteLock(&afs_disconDirtyLock);
1121
1122         /* Get local write lock. */
1123         ObtainWriteLock(&tvc->lock, 705);
1124
1125         if (tvc->f.ddirty_flags & VDisconRemove) {
1126             /* Delete the file on the server and just move on
1127              * to the next file. After all, it has been deleted
1128              * we can't replay any other operation it.
1129              */
1130             code = afs_ProcessOpRemove(tvc, areq);
1131             goto next_file;
1132
1133         } else if (tvc->f.ddirty_flags & VDisconCreate) {
1134             /* For newly created files, we don't need a server lock. */
1135             code = afs_ProcessOpCreate(tvc, areq, acred);
1136             if (code)
1137                 goto next_file;
1138
1139             tvc->f.ddirty_flags &= ~VDisconCreate;
1140             tvc->f.ddirty_flags |= VDisconCreated;
1141         }
1142 #if 0
1143         /* Get server write lock. */
1144         do {
1145             tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
1146             if (tc) {
1147                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
1148                 RX_AFS_GUNLOCK();
1149                 code = RXAFS_SetLock(tc->id,
1150                                         (struct AFSFid *)&tvc->f.fid.Fid,
1151                                         LockWrite,
1152                                         &tsync);
1153                 RX_AFS_GLOCK();
1154                 XSTATS_END_TIME;
1155            } else
1156                 code = -1;
1157
1158         } while (afs_Analyze(tc,
1159                         code,
1160                         &tvc->f.fid,
1161                         areq,
1162                         AFS_STATS_FS_RPCIDX_SETLOCK,
1163                         SHARED_LOCK,
1164                         NULL));
1165
1166         if (code)
1167             goto next_file;
1168 #endif
1169         if (tvc->f.ddirty_flags & VDisconRename) {
1170             /* If we're renaming the file, do so now */
1171             code = afs_ProcessOpRename(tvc, areq);
1172             if (code)
1173                 goto unlock_srv_file;
1174         }
1175
1176         /* Issue a FetchStatus to get info about DV and callbacks. */
1177         do {
1178             tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
1179             if (tc) {
1180                 tvc->callback = tc->srvr->server;
1181                 start = osi_Time();
1182                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
1183                 RX_AFS_GUNLOCK();
1184                 code = RXAFS_FetchStatus(tc->id,
1185                                 (struct AFSFid *)&tvc->f.fid.Fid,
1186                                 &fstat,
1187                                 &callback,
1188                                 &tsync);
1189                 RX_AFS_GLOCK();
1190                 XSTATS_END_TIME;
1191             } else
1192                 code = -1;
1193
1194         } while (afs_Analyze(tc,
1195                         code,
1196                         &tvc->f.fid,
1197                         areq,
1198                         AFS_STATS_FS_RPCIDX_FETCHSTATUS,
1199                         SHARED_LOCK,
1200                         NULL));
1201
1202         if (code) {
1203             goto unlock_srv_file;
1204         }
1205
1206         if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) ||
1207                 (afs_ConflictPolicy == CLIENT_WINS) ||
1208                 (tvc->f.ddirty_flags & VDisconCreated)) {
1209             /*
1210              * Send changes to the server if there's data version match, or
1211              * client wins policy has been selected or file has been created
1212              * but doesn't have it's the contents on to the server yet.
1213              */
1214            /*
1215             * XXX: Checking server attr changes by timestamp might not the
1216             * most elegant solution, but it's the most viable one that we could find.
1217             */
1218             afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start);
1219             code = afs_SendChanges(tvc, areq);
1220
1221         } else if (afs_ConflictPolicy == SERVER_WINS) {
1222             /* DV mismatch, apply collision resolution policy. */
1223             /* Discard this files chunks and remove from current dir. */
1224             afs_ResetVCache(tvc, acred);
1225             tvc->f.truncPos = AFS_NOTRUNC;
1226         } else {
1227             /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */
1228         }               /* if DV match or client wins policy */
1229
1230 unlock_srv_file:
1231         /* Release server write lock. */
1232 #if 0
1233         do {
1234             tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
1235             if (tc) {
1236                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
1237                 RX_AFS_GUNLOCK();
1238                 ucode = RXAFS_ReleaseLock(tc->id,
1239                                 (struct AFSFid *) &tvc->f.fid.Fid,
1240                                 &tsync);
1241                 RX_AFS_GLOCK();
1242                 XSTATS_END_TIME;
1243             } else
1244                 ucode = -1;
1245         } while (afs_Analyze(tc,
1246                         ucode,
1247                         &tvc->f.fid,
1248                         areq,
1249                         AFS_STATS_FS_RPCIDX_RELEASELOCK,
1250                         SHARED_LOCK,
1251                         NULL));
1252 #endif
1253 next_file:
1254         ObtainWriteLock(&afs_disconDirtyLock, 710);
1255         if (code == 0) {
1256             /* Replayed successfully - pull the vcache from the
1257              * disconnected list */
1258             tvc->f.ddirty_flags = 0;
1259             QRemove(&tvc->dirtyq);
1260             afs_PutVCache(tvc);
1261         } else {
1262             if (code == EAGAIN) {
1263                 /* Operation was deferred. Pull it from the current place in
1264                  * the list, and stick it at the end again */
1265                 QRemove(&tvc->dirtyq);
1266                 QAdd(&afs_disconDirty, &tvc->dirtyq);
1267             } else {
1268                 /* Failed - keep state as is, and let the user know we died */
1269
1270                 ReleaseWriteLock(&tvc->lock);
1271                 break;
1272             }
1273         }
1274
1275         /* Release local write lock. */
1276         ReleaseWriteLock(&tvc->lock);
1277     }                   /* while (tvc) */
1278
1279     if (code) {
1280         ReleaseWriteLock(&afs_disconDirtyLock);
1281         return code;
1282     }
1283
1284     /* Dispose of all of the shadow directories */
1285     afs_DisconDiscardAllShadows(0, acred);
1286
1287     ReleaseWriteLock(&afs_disconDirtyLock);
1288     return code;
1289 }
1290
1291 /*!
1292  * Discard all of our shadow directory copies. If squash is true, then
1293  * we also invalidate the vcache holding the shadow directory, to ensure
1294  * that any disconnected changes are deleted
1295  *
1296  * \param squash
1297  * \param acred
1298  *
1299  * \note afs_disconDirtyLock must be held on entry. It will be released
1300  * and reobtained
1301  */
1302
1303 static void
1304 afs_DisconDiscardAllShadows(int squash, afs_ucred_t *acred)
1305 {
1306    struct vcache *tvc;
1307
1308    while (!QEmpty(&afs_disconShadow)) {
1309         tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq);
1310
1311         /* Must release the dirty lock to be able to get a vcache lock */
1312         ReleaseWriteLock(&afs_disconDirtyLock);
1313         ObtainWriteLock(&tvc->lock, 706);
1314
1315         if (squash)
1316            afs_ResetVCache(tvc, acred);
1317
1318         afs_DeleteShadowDir(tvc);
1319
1320         ReleaseWriteLock(&tvc->lock);
1321         ObtainWriteLock(&afs_disconDirtyLock, 709);
1322     }                           /* while (tvc) */
1323 }
1324
1325 /*!
1326  * This function throws away the whole disconnected state, allowing
1327  * the cache manager to reconnect to a server if we get into a state
1328  * where reconiliation is impossible.
1329  *
1330  * \param acred
1331  *
1332  */
1333 void
1334 afs_DisconDiscardAll(afs_ucred_t *acred)
1335 {
1336     struct vcache *tvc;
1337
1338     ObtainWriteLock(&afs_disconDirtyLock, 717);
1339     while (!QEmpty(&afs_disconDirty)) {
1340         tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1341         QRemove(&tvc->dirtyq);
1342         ReleaseWriteLock(&afs_disconDirtyLock);
1343
1344         ObtainWriteLock(&tvc->lock, 718);
1345         afs_ResetVCache(tvc, acred);
1346         tvc->f.truncPos = AFS_NOTRUNC;
1347         ReleaseWriteLock(&tvc->lock);
1348         ObtainWriteLock(&afs_disconDirtyLock, 719);
1349         afs_PutVCache(tvc);
1350     }
1351
1352     afs_DisconDiscardAllShadows(1, acred);
1353
1354     ReleaseWriteLock(&afs_disconDirtyLock);
1355 }
1356
1357 /*!
1358  * Print list of disconnected files.
1359  *
1360  * \note Call with afs_DDirtyVCListLock read locked.
1361  */
1362 void
1363 afs_DbgDisconFiles(void)
1364 {
1365     struct vcache *tvc;
1366     struct afs_q *q;
1367     int i = 0;
1368
1369     afs_warn("List of dirty files: \n");
1370
1371     ObtainReadLock(&afs_disconDirtyLock);
1372     for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) {
1373         tvc = QEntry(q, struct vcache, dirtyq);
1374
1375         afs_warn("Cell=%u Volume=%u VNode=%u Unique=%u\n",
1376                 tvc->f.fid.Cell,
1377                 tvc->f.fid.Fid.Volume,
1378                 tvc->f.fid.Fid.Vnode,
1379                 tvc->f.fid.Fid.Unique);
1380
1381         i++;
1382         if (i >= 30)
1383             osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
1384     }
1385     ReleaseReadLock(&afs_disconDirtyLock);
1386 }
1387
1388 /*!
1389  * Generate a fake fid for a disconnected shadow dir.
1390  * Similar to afs_GenFakeFid, only that it uses the dhash
1391  * to search for a uniquifier because a shadow dir lives only
1392  * in the dcache.
1393  *
1394  * \param afid
1395  *
1396  * \note Don't forget to fill in afid with Cell and Volume.
1397  */
1398 void
1399 afs_GenShadowFid(struct VenusFid *afid)
1400 {
1401     afs_uint32 i, index, max_unique = 1;
1402     struct vcache *tvc = NULL;
1403
1404     /* Try generating a fid that isn't used in the vhash. */
1405     do {
1406         /* Shadow Fids are always directories */
1407         afid->Fid.Vnode = afs_DisconVnode + 1;
1408
1409         i = DVHash(afid);
1410         ObtainWriteLock(&afs_xdcache, 737);
1411         for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
1412             i = afs_dvnextTbl[index];
1413             if (afs_indexUnique[index] > max_unique)
1414                 max_unique = afs_indexUnique[index];
1415         }
1416
1417         ReleaseWriteLock(&afs_xdcache);
1418         afid->Fid.Unique = max_unique + 1;
1419         afs_DisconVnode += 2;
1420         if (!afs_DisconVnode)
1421             afs_DisconVnode = 2;
1422
1423         /* Is this a used vnode? */
1424         ObtainSharedLock(&afs_xvcache, 762);
1425         tvc = afs_FindVCache(afid, 0, 1);
1426         ReleaseSharedLock(&afs_xvcache);
1427         if (tvc)
1428             afs_PutVCache(tvc);
1429     } while (tvc);
1430 }
1431
1432 /*!
1433  * Generate a fake fid (vnode and uniquifier) for a vcache
1434  * (either dir or normal file). The vnode is generated via
1435  * afs_DisconVNode and the uniquifier by getting the highest
1436  * uniquifier on a hash chain and incrementing it by one.
1437  *
1438  * \param afid   The fid structre that will be filled.
1439  * \param avtype Vnode type: VDIR/VREG.
1440  * \param lock   True indicates that xvcache may be obtained,
1441  *               False that it is already held
1442  *
1443  * \note The cell number must be completed somewhere else.
1444  */
1445 void
1446 afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype, int lock)
1447 {
1448     struct vcache *tvc;
1449     afs_uint32 max_unique = 0, i;
1450
1451     switch (avtype) {
1452     case VDIR:
1453         afid->Fid.Vnode = afs_DisconVnode + 1;
1454         break;
1455     case VREG:
1456     case VLNK:
1457         afid->Fid.Vnode = afs_DisconVnode;
1458         break;
1459     }
1460
1461     if (lock)
1462         ObtainWriteLock(&afs_xvcache, 736);
1463     i = VCHash(afid);
1464     for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
1465         if (tvc->f.fid.Fid.Unique > max_unique)
1466             max_unique = tvc->f.fid.Fid.Unique;
1467     }
1468     if (lock)
1469         ReleaseWriteLock(&afs_xvcache);
1470
1471     afid->Fid.Unique = max_unique + 1;
1472     afs_DisconVnode += 2;
1473     if (!afs_DisconVnode)
1474         afs_DisconVnode = 2;
1475 }
1476
1477 /*!
1478  * Fill in stats for a newly created file/directory.
1479  *
1480  * \param adp The parent dir's vcache.
1481  * \param avc The created vnode.
1482  * \param afid The new fid.
1483  * \param attrs
1484  * \param areq
1485  * \param file_type Specify if file or directory.
1486  *
1487  * \note Call with avc write locked.
1488  */
1489 void
1490 afs_GenDisconStatus(struct vcache *adp, struct vcache *avc,
1491                     struct VenusFid *afid, struct vattr *attrs,
1492                     struct vrequest *areq, int file_type)
1493 {
1494     memcpy(&avc->f.fid, afid, sizeof(struct VenusFid));
1495     avc->f.m.Mode = attrs->va_mode;
1496     /* Used to do this:
1497      * avc->f.m.Owner = attrs->va_uid;
1498      * But now we use the parent dir's ownership,
1499      * there's no other way to get a server owner id.
1500      * XXX: Does it really matter?
1501      */
1502     avc->f.m.Group = adp->f.m.Group;
1503     avc->f.m.Owner = adp->f.m.Owner;
1504     hset64(avc->f.m.DataVersion, 0, 0);
1505     avc->f.m.Length = attrs->va_size;
1506     avc->f.m.Date = osi_Time();
1507     switch(file_type) {
1508       case VREG:
1509         vSetType(avc, VREG);
1510         avc->f.m.Mode |= S_IFREG;
1511         avc->f.m.LinkCount = 1;
1512         avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
1513         avc->f.parent.unique = adp->f.fid.Fid.Unique;
1514         break;
1515       case VDIR:
1516         vSetType(avc, VDIR);
1517         avc->f.m.Mode |= S_IFDIR;
1518         avc->f.m.LinkCount = 2;
1519         break;
1520       case VLNK:
1521         vSetType(avc, VLNK);
1522         avc->f.m.Mode |= S_IFLNK;
1523         if ((avc->f.m.Mode & 0111) == 0)
1524             avc->mvstat = 1;
1525         avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
1526         avc->f.parent.unique = adp->f.fid.Fid.Unique;
1527         break;
1528       default:
1529         break;
1530     }
1531     avc->f.anyAccess = adp->f.anyAccess;
1532     afs_AddAxs(avc->Access, areq->uid, adp->Access->axess);
1533
1534     avc->callback = NULL;
1535     avc->f.states |= CStatd;
1536     avc->f.states &= ~CBulkFetching;
1537 }