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