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