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