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