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