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