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