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
11 #include "afs/sysincludes.h"
12 #include "afsincludes.h"
13 #include "afs/afs_stats.h" /* statistics */
15 #include "afs/afs_cbqueue.h"
17 #define dv_match(vc, fstat) \
18 ((vc->f.m.DataVersion.low == fstat.DataVersion) && \
19 (vc->f.m.DataVersion.high == fstat.dataVersionHigh))
21 /*! Circular queue of dirty vcaches */
22 struct afs_q afs_disconDirty
;
24 /*! Circular queue of vcaches with shadow directories */
25 struct afs_q afs_disconShadow
;
27 /*! Locks both of these lists. Must be write locked for anything other than
29 afs_rwlock_t afs_disconDirtyLock
;
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 */
37 /*! Vnode number. On file creation, use the current value and increment it.
39 afs_uint32 afs_DisconVnode
= 2;
41 /*! Conflict policy. */
49 afs_int32 afs_ConflictPolicy
= SERVER_WINS
;
51 static void afs_DisconDiscardAllShadows(int, afs_ucred_t
*);
52 void afs_DbgListDirEntries(struct VenusFid
*afid
);
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.
62 * \return The found dcache or NULL.
65 afs_FindDCacheByFid(struct VenusFid
*afid
)
68 struct dcache
*tdc
= NULL
;
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
);
79 ReleaseReadLock(&tdc
->tlock
);
80 if (!FidCmp(&tdc
->f
.fid
, afid
)) {
81 break; /* leaving refCount high for caller */
85 index
= afs_dvnextTbl
[index
];
87 ReleaseWriteLock(&afs_xdcache
);
95 * Generate a store status from a dirty vcache entry.
97 * \param avc Dirty vcache entry.
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.
103 * \return Mask of operations.
106 afs_GenStoreStatus(struct vcache
*avc
, struct AFSStoreStatus
*astat
)
108 if (!avc
|| !astat
|| !avc
->f
.ddirty_flags
)
111 /* Clean up store stat. */
112 memset(astat
, 0, sizeof(struct AFSStoreStatus
));
114 if (avc
->f
.ddirty_flags
& VDisconSetTime
) {
115 /* Update timestamp. */
116 astat
->ClientModTime
= avc
->f
.m
.Date
;
117 astat
->Mask
|= AFS_SETMODTIME
;
120 if (avc
->f
.ddirty_flags
& VDisconSetMode
) {
121 /* Copy the mode bits. */
122 astat
->UnixModeBits
= avc
->f
.m
.Mode
;
123 astat
->Mask
|= AFS_SETMODE
;
126 /* XXX: more to come... ?*/
132 * Hook for filtering the local dir fid by searching the "." entry.
134 * \param hdata The fid to be filled.
137 get_parent_dir_fid_hook(void *hdata
, char *aname
, afs_int32 vnode
,
140 struct VenusFid
*tfid
= (struct VenusFid
*) hdata
;
142 if ((aname
[0] == '.') && (aname
[1] == '.') && !aname
[2]) {
143 tfid
->Fid
.Vnode
= vnode
;
144 tfid
->Fid
.Unique
= unique
;
152 * Get a the dir's fid by looking in the vcache for simple files and
153 * in the ".." entry for directories.
155 * \param avc The file's vhash entry.
156 * \param afid Put the fid here.
158 * \return 0 on success, -1 on failure
161 afs_GetParentDirFid(struct vcache
*avc
, struct VenusFid
*afid
)
165 afid
->Cell
= avc
->f
.fid
.Cell
;
166 afid
->Fid
.Volume
= avc
->f
.fid
.Fid
.Volume
;
168 switch (vType(avc
)) {
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
;
176 /* If dir or parent dir created locally*/
177 tdc
= afs_FindDCacheByFid(&avc
->f
.fid
);
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
);
183 if (afid
->Fid
.Unique
== 0) {
198 struct VenusFid
*fid
;
204 * Hook that searches a certain fid's name.
206 * \param hdata NameAndFid structure containin a pointer to a fid
207 * and an allocate name. The name will be filled when hit.
210 get_vnode_name_hook(void *hdata
, char *aname
, afs_int32 vnode
,
213 struct NameAndFid
*nf
= (struct NameAndFid
*) hdata
;
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;
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.
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.
238 afs_GetVnodeName(struct vcache
*avc
, struct VenusFid
*afid
, char *aname
,
243 struct vcache
*parent_vc
;
244 struct NameAndFid tnf
;
245 struct VenusFid parent_fid
;
246 struct VenusFid shadow_fid
;
248 /* List dir contents and get it's tdc. */
250 /* For deleted files, get the shadow dir's tdc: */
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
;
260 parent_fid
.Fid
.Vnode
= afid
->Fid
.Vnode
;
261 parent_fid
.Fid
.Unique
= afid
->Fid
.Unique
;
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
);
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
;
277 afs_PutVCache(parent_vc
);
279 /* Get shadow dir's dcache. */
280 tdc
= afs_FindDCacheByFid(&shadow_fid
);
284 /* For normal files, look into the current dir's entry. */
285 tdc
= afs_FindDCacheByFid(afid
);
289 tnf
.fid
= &avc
->f
.fid
;
292 afs_dir_EnumerateDir(tdc
, &get_vnode_name_hook
, &tnf
);
294 if (tnf
.name_len
== -1)
297 /* printf("Directory dcache not found!\n"); */
304 struct DirtyChildrenCount
{
310 * Lookup dirty deleted vnodes in this dir.
313 chk_del_children_hook(void *hdata
, char *aname
, afs_int32 vnode
,
316 struct VenusFid tfid
;
317 struct DirtyChildrenCount
*v
= (struct DirtyChildrenCount
*) hdata
;
320 if ((aname
[0] == '.') && !aname
[1])
321 /* Skip processing this dir again.
322 * It would result in an endless loop.
326 if ((aname
[0] == '.') && (aname
[1] == '.') && !aname
[2])
327 /* Don't process parent dir. */
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
;
336 ObtainSharedLock(&afs_xvcache
, 757);
337 tvc
= afs_FindVCache(&tfid
, 0, 1);
338 ReleaseSharedLock(&afs_xvcache
);
340 /* Count unfinished dirty children. */
342 ObtainReadLock(&tvc
->lock
);
343 if (tvc
->f
.ddirty_flags
)
345 ReleaseReadLock(&tvc
->lock
);
354 * Check if entries have been deleted in a vnode's shadow
357 * \return Returns the number of dirty children.
359 * \note afs_DDirtyVCListLock must be write locked.
362 afs_CheckDeletedChildren(struct vcache
*avc
)
365 struct DirtyChildrenCount dcc
;
366 struct VenusFid shadow_fid
;
368 if (!avc
->f
.shadow
.vnode
)
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
;
379 /* Get shadow dir's dcache. */
380 tdc
= afs_FindDCacheByFid(&shadow_fid
);
383 afs_dir_EnumerateDir(tdc
, &chk_del_children_hook
, &dcc
);
391 * Changes a file's parent fid references.
394 fix_children_fids_hook(void *hdata
, char *aname
, afs_int32 vnode
,
397 struct VenusFid tfid
;
398 struct VenusFid
*afid
= (struct VenusFid
*) hdata
;
400 struct dcache
*tdc
= NULL
;
402 if ((aname
[0] == '.') && !aname
[1])
405 if ((aname
[0] == '.') && (aname
[1] == '.') && !aname
[2])
408 tfid
.Cell
= afid
->Cell
;
409 tfid
.Fid
.Volume
= afid
->Fid
.Volume
;
410 tfid
.Fid
.Vnode
= vnode
;
411 tfid
.Fid
.Unique
= unique
;
414 /* vnode's parity indicates that it's a file. */
416 /* Get the vcache. */
417 ObtainSharedLock(&afs_xvcache
, 759);
418 tvc
= afs_FindVCache(&tfid
, 0, 1);
419 ReleaseSharedLock(&afs_xvcache
);
421 /* Change the fields. */
423 tvc
->f
.parent
.vnode
= afid
->Fid
.Vnode
;
424 tvc
->f
.parent
.unique
= afid
->Fid
.Unique
;
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
);
433 /* Change the .. entry fid. */
434 afs_dir_ChangeFid(tdc
, "..", NULL
, &afid
->Fid
.Vnode
);
437 } /* if (!(vnode % 2))*/
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.
449 * \note The dir's fid hasn't been changed yet, it is still referenced
452 * \param old_fid The current dir's fid.
453 * \param new_fid The new dir's fid.
456 afs_FixChildrenFids(struct VenusFid
*old_fid
, struct VenusFid
*new_fid
)
460 /* Get shadow dir's dcache. */
461 tdc
= afs_FindDCacheByFid(old_fid
);
462 /* Change the fids. */
464 afs_dir_EnumerateDir(tdc
, &fix_children_fids_hook
, new_fid
);
470 list_dir_hook(void *hdata
, char *aname
, afs_int32 vnode
, afs_int32 unique
)
472 /* printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique); */
477 afs_DbgListDirEntries(struct VenusFid
*afid
)
481 /* Get shadow dir's dcache. */
482 tdc
= afs_FindDCacheByFid(afid
);
484 afs_dir_EnumerateDir(tdc
, &list_dir_hook
, NULL
);
490 * Find the parent vcache for a given child
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
498 * \return An error code. 0 indicates success, EAGAIN that the vnode should
499 * be deferred to later in the resync process
503 afs_GetParentVCache(struct vcache
*avc
, int deleted
, struct VenusFid
*afid
,
504 char *aname
, struct vcache
**adp
)
510 if (afs_GetParentDirFid(avc
, afid
)) {
511 /* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */
515 code
= afs_GetVnodeName(avc
, afid
, aname
, deleted
);
517 /* printf("afs_GetParentVCache: Couldn't find file name\n"); */
521 ObtainSharedLock(&afs_xvcache
, 766);
522 *adp
= afs_FindVCache(afid
, 0, 1);
523 ReleaseSharedLock(&afs_xvcache
);
525 /* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */
530 if ((*adp
)->f
.ddirty_flags
& VDisconCreate
) {
531 /* printf("afs_GetParentVCache: deferring until parent exists\n"); */
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.
552 afs_ProcessOpRename(struct vcache
*avc
, struct vrequest
*areq
)
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
;
559 struct rx_connection
*rxconn
;
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
;
570 old_name
= afs_osi_Alloc(AFSNAMEMAX
);
572 /* printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n"); */
575 code
= afs_GetVnodeName(avc
, &old_pdir_fid
, old_name
, 1);
577 /* printf("afs_ProcessOpRename: Couldn't find old name.\n"); */
581 /* Alloc data first. */
582 new_name
= afs_osi_Alloc(AFSNAMEMAX
);
584 /* printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); */
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.
593 memcpy(&new_pdir_fid
, &old_pdir_fid
, sizeof(struct VenusFid
));
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"); */
603 /* And finally get the new name. */
604 code
= afs_GetVnodeName(avc
, &new_pdir_fid
, new_name
, 0);
606 /* printf("afs_ProcessOpRename: Couldn't find new name.\n"); */
610 /* Send to data to server. */
612 tc
= afs_Conn(&old_pdir_fid
, areq
, SHARED_LOCK
, &rxconn
);
614 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME
);
616 code
= RXAFS_Rename(rxconn
,
617 (struct AFSFid
*)&old_pdir_fid
.Fid
,
619 (struct AFSFid
*)&new_pdir_fid
.Fid
,
629 } while (afs_Analyze(tc
,
634 AFS_STATS_FS_RPCIDX_RENAME
,
638 /* if (code) printf("afs_ProcessOpRename: server code=%u\n", code); */
641 afs_osi_Free(new_name
, AFSNAMEMAX
);
643 afs_osi_Free(old_name
, AFSNAMEMAX
);
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.
652 * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
655 afs_ProcessOpCreate(struct vcache
*avc
, struct vrequest
*areq
,
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
;
667 struct rx_connection
*rxconn
;
668 afs_int32 hash
, new_hash
, index
;
673 tname
= afs_osi_Alloc(AFSNAMEMAX
);
676 memset(&InStatus
, 0, sizeof(InStatus
));
678 code
= afs_GetParentVCache(avc
, 0, &pdir_fid
, tname
, &tdp
);
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.
686 if (vType(avc
) == VLNK
) {
689 struct osi_file
*tfile
;
691 tdc
= afs_GetDCache(avc
, 0, areq
, &offset
, &tlen
, 0);
703 tlen
++; /* space for NULL */
704 ttargetName
= afs_osi_Alloc(tlen
);
710 ObtainReadLock(&tdc
->lock
);
711 tfile
= afs_CFileOpen(&tdc
->f
.inode
);
713 code
= afs_CFileRead(tfile
, 0, ttargetName
, tlen
);
714 ttargetName
[tlen
-1] = '\0';
715 afs_CFileClose(tfile
);
716 ReleaseReadLock(&tdc
->lock
);
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;
729 tc
= afs_Conn(&tdp
->f
.fid
, areq
, SHARED_LOCK
, &rxconn
);
731 switch (vType(avc
)) {
733 /* Make file on server. */
734 op
= AFS_STATS_FS_RPCIDX_CREATEFILE
;
735 XSTATS_START_TIME(op
);
737 code
= RXAFS_CreateFile(rxconn
,
738 (struct AFSFid
*)&tdp
->f
.fid
.Fid
,
740 (struct AFSFid
*) &newFid
.Fid
,
741 &OutFidStatus
, &OutDirStatus
,
747 /* Make dir on server. */
748 op
= AFS_STATS_FS_RPCIDX_MAKEDIR
;
749 XSTATS_START_TIME(op
);
751 code
= RXAFS_MakeDir(rxconn
, (struct AFSFid
*) &tdp
->f
.fid
.Fid
,
753 (struct AFSFid
*) &newFid
.Fid
,
754 &OutFidStatus
, &OutDirStatus
,
760 /* Make symlink on server. */
761 op
= AFS_STATS_FS_RPCIDX_SYMLINK
;
762 XSTATS_START_TIME(op
);
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
);
773 op
= AFS_STATS_FS_RPCIDX_CREATEFILE
;
779 } while (afs_Analyze(tc
, rxconn
, code
, &tdp
->f
.fid
, areq
, op
, SHARED_LOCK
, NULL
));
781 /* TODO: Handle errors. */
783 /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */
787 /* The rpc doesn't set the cell number. */
788 newFid
.Cell
= avc
->f
.fid
.Cell
;
791 * Change the fid in the dir entry.
794 /* Seek the dir's dcache. */
795 tdc
= afs_FindDCacheByFid(&tdp
->f
.fid
);
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
);
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.
806 tdc
= afs_FindDCacheByFid(&avc
->f
.fid
);
808 afs_dir_ChangeFid(tdc
, ".", &avc
->f
.fid
.Fid
.Vnode
,
811 if (avc
->f
.m
.LinkCount
>= 2)
812 /* For non empty dirs, fix children's parentVnode and
813 * parentUnique reference.
815 afs_FixChildrenFids(&avc
->f
.fid
, &newFid
);
821 /* Recompute hash chain positions for vnode and dcaches.
822 * Then change to the new FID.
825 /* The vcache goes first. */
826 ObtainWriteLock(&afs_xvcache
, 735);
829 hash
= VCHash(&avc
->f
.fid
);
831 new_hash
= VCHash(&newFid
);
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.
837 if (afs_vhashT
[hash
] == avc
) {
838 /* First in hash chain (might be the only one). */
839 afs_vhashT
[hash
] = avc
->hnext
;
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
;
848 } /* if (!afs_vhashT[i]->hnext) */
849 QRemove(&avc
->vhashq
);
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
);
856 ReleaseWriteLock(&afs_xvcache
);
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
);
865 ReleaseWriteLock(&afs_xdcache
);
869 ReleaseReadLock(&tdc
->tlock
);
870 if (afs_indexUnique
[index
] == avc
->f
.fid
.Fid
.Unique
) {
871 if (!FidCmp(&tdc
->f
.fid
, &avc
->f
.fid
)) {
873 /* Safer but slower. */
874 afs_HashOutDCache(tdc
, 0);
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
;
881 new_hash
= DVHash(&newFid
);
882 afs_dvnextTbl
[tdc
->index
] = afs_dvhashTbl
[new_hash
];
883 afs_dvhashTbl
[new_hash
] = tdc
->index
;
885 afs_indexUnique
[tdc
->index
] = newFid
.Fid
.Unique
;
886 memcpy(&tdc
->f
.fid
, &newFid
, sizeof(struct VenusFid
));
888 } /* if uniquifier match */
890 } /* for all dcaches in this hash bucket */
891 ReleaseWriteLock(&afs_xdcache
);
893 /* Now we can set the new fid. */
894 memcpy(&avc
->f
.fid
, &newFid
, sizeof(struct VenusFid
));
899 afs_osi_Free(tname
, AFSNAMEMAX
);
901 afs_osi_Free(ttargetName
, tlen
);
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
910 * \param avc The deleted vcache
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).
917 * \note avc must be write locked.
920 afs_ProcessOpRemove(struct vcache
*avc
, struct vrequest
*areq
)
923 struct AFSFetchStatus OutDirStatus
;
924 struct VenusFid pdir_fid
;
925 struct AFSVolSync tsync
;
927 struct rx_connection
*rxconn
;
928 struct vcache
*tdp
= NULL
;
932 tname
= afs_osi_Alloc(AFSNAMEMAX
);
934 /* printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n"); */
938 code
= afs_GetParentVCache(avc
, 1, &pdir_fid
, tname
, &tdp
);
942 if ((vType(avc
) == VDIR
) && (afs_CheckDeletedChildren(avc
))) {
943 /* Deleted children of this dir remain unsynchronized.
950 if (vType(avc
) == VREG
|| vType(avc
) == VLNK
) {
951 /* Remove file on server. */
953 tc
= afs_Conn(&pdir_fid
, areq
, SHARED_LOCK
, &rxconn
);
955 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE
);
957 code
= RXAFS_RemoveFile(rxconn
,
967 } while (afs_Analyze(tc
,
972 AFS_STATS_FS_RPCIDX_REMOVEFILE
,
976 } else if (vType(avc
) == VDIR
) {
977 /* Remove dir on server. */
979 tc
= afs_Conn(&pdir_fid
, areq
, SHARED_LOCK
, &rxconn
);
981 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR
);
983 code
= RXAFS_RemoveDir(rxconn
,
992 } while (afs_Analyze(tc
,
997 AFS_STATS_FS_RPCIDX_REMOVEDIR
,
1001 } /* if (vType(avc) == VREG) */
1003 /* if (code) printf("afs_ProcessOpRemove: server returned code=%u\n", code); */
1006 afs_osi_Free(tname
, AFSNAMEMAX
);
1011 * Send disconnected file changes to the server.
1013 * \note Call with vnode locked both locally and on the server.
1015 * \param avc Vnode that gets synchronized to the server.
1016 * \param areq Used for obtaining a conn struct.
1018 * \return 0 for success. On failure, other error codes.
1021 afs_SendChanges(struct vcache
*avc
, struct vrequest
*areq
)
1023 struct afs_conn
*tc
;
1024 struct rx_connection
*rxconn
;
1025 struct AFSStoreStatus sstat
;
1026 struct AFSFetchStatus fstat
;
1027 struct AFSVolSync tsync
;
1032 /* Start multiplexing dirty operations from ddirty_flags field: */
1033 if (avc
->f
.ddirty_flags
& VDisconSetAttrMask
) {
1035 /* Turn dirty vc data into a new store status... */
1036 if (afs_GenStoreStatus(avc
, &sstat
) > 0) {
1038 tc
= afs_Conn(&avc
->f
.fid
, areq
, SHARED_LOCK
, &rxconn
);
1040 /* ... and send it. */
1041 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS
);
1043 code
= RXAFS_StoreStatus(rxconn
,
1044 (struct AFSFid
*) &avc
->f
.fid
.Fid
,
1054 } while (afs_Analyze(tc
,
1059 AFS_STATS_FS_RPCIDX_STORESTATUS
,
1063 } /* if (afs_GenStoreStatus() > 0)*/
1064 } /* disconnected SETATTR */
1069 if (avc
->f
.ddirty_flags
&
1073 | VDisconWriteOsiFlush
)) {
1077 tc
= afs_Conn(&avc
->f
.fid
, areq
, SHARED_LOCK
, &rxconn
);
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
)
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
);
1093 } while (afs_Analyze(tc
,
1098 AFS_STATS_FS_RPCIDX_STOREDATA
,
1102 } /* disconnected TRUNC | WRITE */
1108 * All files that have been dirty before disconnection are going to
1109 * be replayed back to the server.
1111 * \param areq Request from the user.
1112 * \param acred User credentials.
1114 * \return If all files synchronized succesfully, return 0, otherwise
1117 * \note For now, it's the request from the PDiscon pioctl.
1121 afs_ResyncDisconFiles(struct vrequest
*areq
, afs_ucred_t
*acred
)
1123 struct afs_conn
*tc
;
1124 struct rx_connection
*rxconn
;
1126 struct AFSFetchStatus fstat
;
1127 struct AFSCallBack callback
;
1128 struct AFSVolSync tsync
;
1130 afs_int32 start
= 0;
1132 /*AFS_STATCNT(afs_ResyncDisconFiles);*/
1134 ObtainWriteLock(&afs_disconDirtyLock
, 707);
1136 while (!QEmpty(&afs_disconDirty
)) {
1137 tvc
= QEntry(QPrev(&afs_disconDirty
), struct vcache
, dirtyq
);
1139 /* Can't lock tvc whilst holding the discon dirty lock */
1140 ReleaseWriteLock(&afs_disconDirtyLock
);
1142 /* Get local write lock. */
1143 ObtainWriteLock(&tvc
->lock
, 705);
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.
1150 code
= afs_ProcessOpRemove(tvc
, areq
);
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
);
1159 tvc
->f
.ddirty_flags
&= ~VDisconCreate
;
1160 tvc
->f
.ddirty_flags
|= VDisconCreated
;
1163 /* Get server write lock. */
1165 tc
= afs_Conn(&tvc
->f
.fid
, areq
, SHARED_LOCK
, &rxconn
);
1167 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK
);
1169 code
= RXAFS_SetLock(rxconn
,
1170 (struct AFSFid
*)&tvc
->f
.fid
.Fid
,
1178 } while (afs_Analyze(tc
,
1183 AFS_STATS_FS_RPCIDX_SETLOCK
,
1190 if (tvc
->f
.ddirty_flags
& VDisconRename
) {
1191 /* If we're renaming the file, do so now */
1192 code
= afs_ProcessOpRename(tvc
, areq
);
1194 goto unlock_srv_file
;
1197 /* Issue a FetchStatus to get info about DV and callbacks. */
1199 tc
= afs_Conn(&tvc
->f
.fid
, areq
, SHARED_LOCK
, &rxconn
);
1201 tvc
->callback
= tc
->parent
->srvr
->server
;
1203 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS
);
1205 code
= RXAFS_FetchStatus(rxconn
,
1206 (struct AFSFid
*)&tvc
->f
.fid
.Fid
,
1215 } while (afs_Analyze(tc
,
1220 AFS_STATS_FS_RPCIDX_FETCHSTATUS
,
1225 goto unlock_srv_file
;
1228 if ((dv_match(tvc
, fstat
) && (tvc
->f
.m
.Date
== fstat
.ServerModTime
)) ||
1229 (afs_ConflictPolicy
== CLIENT_WINS
) ||
1230 (tvc
->f
.ddirty_flags
& VDisconCreated
)) {
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.
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.
1240 afs_UpdateStatus(tvc
, &tvc
->f
.fid
, areq
, &fstat
, &callback
, start
);
1241 code
= afs_SendChanges(tvc
, areq
);
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
;
1249 /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */
1250 } /* if DV match or client wins policy */
1253 /* Release server write lock. */
1256 tc
= afs_Conn(&tvc
->f
.fid
, areq
, SHARED_LOCK
, &rxconn
);
1258 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK
);
1260 ucode
= RXAFS_ReleaseLock(rxconn
,
1261 (struct AFSFid
*) &tvc
->f
.fid
.Fid
,
1267 } while (afs_Analyze(tc
,
1272 AFS_STATS_FS_RPCIDX_RELEASELOCK
,
1277 ObtainWriteLock(&afs_disconDirtyLock
, 710);
1279 /* Replayed successfully - pull the vcache from the
1280 * disconnected list */
1281 tvc
->f
.ddirty_flags
= 0;
1282 QRemove(&tvc
->dirtyq
);
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
);
1291 /* Failed - keep state as is, and let the user know we died */
1293 ReleaseWriteLock(&tvc
->lock
);
1298 /* Release local write lock. */
1299 ReleaseWriteLock(&tvc
->lock
);
1303 ReleaseWriteLock(&afs_disconDirtyLock
);
1307 /* Dispose of all of the shadow directories */
1308 afs_DisconDiscardAllShadows(0, acred
);
1310 ReleaseWriteLock(&afs_disconDirtyLock
);
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
1322 * \note afs_disconDirtyLock must be held on entry. It will be released
1327 afs_DisconDiscardAllShadows(int squash
, afs_ucred_t
*acred
)
1331 while (!QEmpty(&afs_disconShadow
)) {
1332 tvc
= QEntry(QNext(&afs_disconShadow
), struct vcache
, shadowq
);
1334 /* Must release the dirty lock to be able to get a vcache lock */
1335 ReleaseWriteLock(&afs_disconDirtyLock
);
1336 ObtainWriteLock(&tvc
->lock
, 706);
1339 afs_ResetVCache(tvc
, acred
, 0);
1341 afs_DeleteShadowDir(tvc
);
1343 ReleaseWriteLock(&tvc
->lock
);
1344 ObtainWriteLock(&afs_disconDirtyLock
, 709);
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.
1357 afs_DisconDiscardAll(afs_ucred_t
*acred
)
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
);
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);
1375 afs_DisconDiscardAllShadows(1, acred
);
1377 ReleaseWriteLock(&afs_disconDirtyLock
);
1381 * Print list of disconnected files.
1383 * \note Call with afs_DDirtyVCListLock read locked.
1386 afs_DbgDisconFiles(void)
1392 afs_warn("List of dirty files: \n");
1394 ObtainReadLock(&afs_disconDirtyLock
);
1395 for (q
= QPrev(&afs_disconDirty
); q
!= &afs_disconDirty
; q
= QPrev(q
)) {
1396 tvc
= QEntry(q
, struct vcache
, dirtyq
);
1398 afs_warn("Cell=%u Volume=%u VNode=%u Unique=%u\n",
1400 tvc
->f
.fid
.Fid
.Volume
,
1401 tvc
->f
.fid
.Fid
.Vnode
,
1402 tvc
->f
.fid
.Fid
.Unique
);
1406 osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
1408 ReleaseReadLock(&afs_disconDirtyLock
);
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
1419 * \note Don't forget to fill in afid with Cell and Volume.
1422 afs_GenShadowFid(struct VenusFid
*afid
)
1424 afs_uint32 i
, index
, max_unique
= 1;
1425 struct vcache
*tvc
= NULL
;
1427 /* Try generating a fid that isn't used in the vhash. */
1429 /* Shadow Fids are always directories */
1430 afid
->Fid
.Vnode
= afs_DisconVnode
+ 1;
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
];
1440 ReleaseWriteLock(&afs_xdcache
);
1441 afid
->Fid
.Unique
= max_unique
+ 1;
1442 afs_DisconVnode
+= 2;
1443 if (!afs_DisconVnode
)
1444 afs_DisconVnode
= 2;
1446 /* Is this a used vnode? */
1447 ObtainSharedLock(&afs_xvcache
, 762);
1448 tvc
= afs_FindVCache(afid
, 0, 1);
1449 ReleaseSharedLock(&afs_xvcache
);
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.
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
1466 * \note The cell number must be completed somewhere else.
1469 afs_GenFakeFid(struct VenusFid
*afid
, afs_uint32 avtype
, int lock
)
1472 afs_uint32 max_unique
= 0, i
;
1476 afid
->Fid
.Vnode
= afs_DisconVnode
+ 1;
1480 afid
->Fid
.Vnode
= afs_DisconVnode
;
1485 ObtainWriteLock(&afs_xvcache
, 736);
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
;
1492 ReleaseWriteLock(&afs_xvcache
);
1494 afid
->Fid
.Unique
= max_unique
+ 1;
1495 afs_DisconVnode
+= 2;
1496 if (!afs_DisconVnode
)
1497 afs_DisconVnode
= 2;
1501 * Fill in stats for a newly created file/directory.
1503 * \param adp The parent dir's vcache.
1504 * \param avc The created vnode.
1505 * \param afid The new fid.
1508 * \param file_type Specify if file or directory.
1510 * \note Call with avc write locked.
1513 afs_GenDisconStatus(struct vcache
*adp
, struct vcache
*avc
,
1514 struct VenusFid
*afid
, struct vattr
*attrs
,
1515 struct vrequest
*areq
, int file_type
)
1518 memcpy(&avc
->f
.fid
, afid
, sizeof(struct VenusFid
));
1519 avc
->f
.m
.Mode
= attrs
->va_mode
;
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?
1526 avc
->f
.m
.Group
= adp
->f
.m
.Group
;
1527 avc
->f
.m
.Owner
= adp
->f
.m
.Owner
;
1529 afs_SetDataVersion(avc
, &zero
);
1530 avc
->f
.m
.Length
= attrs
->va_size
;
1531 avc
->f
.m
.Date
= osi_Time();
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
;
1541 vSetType(avc
, VDIR
);
1542 avc
->f
.m
.Mode
|= S_IFDIR
;
1543 avc
->f
.m
.LinkCount
= 2;
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
;
1556 avc
->f
.anyAccess
= adp
->f
.anyAccess
;
1557 afs_AddAxs(avc
->Access
, areq
->uid
, adp
->Access
->axess
);
1559 avc
->callback
= NULL
;
1560 avc
->f
.states
|= CStatd
;
1561 avc
->f
.states
&= ~CBulkFetching
;