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