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