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
12 #include "afs/sysincludes.h"
13 #include "afsincludes.h"
14 #include "afs/afs_stats.h" /* statistics */
16 #include "afs/afs_cbqueue.h"
20 #define dv_match(vc, fstat) \
21 ((vc->m.DataVersion.low == fstat.DataVersion) && \
22 (vc->m.DataVersion.high == fstat.dataVersionHigh))
24 /*! Circular queue of dirty vcaches */
25 struct afs_q afs_disconDirty;
27 /*! Circular queue of vcaches with shadow directories */
28 struct afs_q afs_disconShadow;
30 /*! Locks both of these lists. Must be write locked for anything other than
32 afs_rwlock_t afs_disconDirtyLock;
34 extern afs_int32 *afs_dvhashTbl; /*Data cache hash table */
35 extern afs_int32 *afs_dchashTbl; /*Data cache hash table */
36 extern afs_int32 *afs_dvnextTbl; /*Dcache hash table links */
37 extern afs_int32 *afs_dcnextTbl; /*Dcache hash table links */
38 extern struct dcache **afs_indexTable; /*Pointers to dcache entries */
40 /*! Vnode number. On file creation, use the current value and increment it.
42 afs_uint32 afs_DisconVnode = 2;
44 /*! Conflict policy. */
52 afs_int32 afs_ConflictPolicy = SERVER_WINS;
54 static void afs_DisconResetVCache(struct vcache *, struct AFS_UCRED *);
55 static void afs_DisconDiscardAllShadows(int, struct AFS_UCRED *);
58 * Find the first dcache of a file that has the specified fid.
59 * Similar to afs_FindDCache, only that it takes a fid instead
60 * of a vcache and it can get the first dcache.
64 * \return The found dcache or NULL.
66 struct dcache *afs_FindDCacheByFid(struct VenusFid *afid)
69 struct dcache *tdc = NULL;
72 ObtainWriteLock(&afs_xdcache, 758);
73 for (index = afs_dvhashTbl[i]; index != NULLIDX;) {
74 if (afs_indexUnique[index] == afid->Fid.Unique) {
75 tdc = afs_GetDSlot(index, NULL);
76 ReleaseReadLock(&tdc->tlock);
77 if (!FidCmp(&tdc->f.fid, afid)) {
78 break; /* leaving refCount high for caller */
82 index = afs_dvnextTbl[index];
84 ReleaseWriteLock(&afs_xdcache);
92 * Generate a store status from a dirty vcache entry.
94 * \param avc Dirty vcache entry.
97 * \note The vnode must be share locked. It is called only on resync,
98 * where the vnode is write locked locally and and the server.
100 * \return Mask of operations.
102 int afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
104 if (!avc || !astat || !avc->ddirty_flags)
107 /* Clean up store stat. */
108 memset(astat, 0, sizeof(struct AFSStoreStatus));
110 if (avc->ddirty_flags & VDisconSetTime) {
111 /* Update timestamp. */
112 astat->ClientModTime = avc->m.Date;
113 astat->Mask |= AFS_SETMODTIME;
116 if (avc->ddirty_flags & VDisconSetMode) {
117 /* Copy the mode bits. */
118 astat->UnixModeBits = avc->m.Mode;
119 astat->Mask |= AFS_SETMODE;
122 /* XXX: more to come... ?*/
128 * Hook for filtering the local dir fid by searching the "." entry.
130 * \param hdata The fid to be filled.
132 int get_parent_dir_fid_hook(void *hdata,
137 struct VenusFid *tfid = (struct VenusFid *) hdata;
139 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) {
140 tfid->Fid.Vnode = vnode;
141 tfid->Fid.Unique = unique;
149 * Get a the dir's fid by looking in the vcache for simple files and
150 * in the ".." entry for directories.
152 * \param avc The file's vhash entry.
153 * \param afid Put the fid here.
155 * \return 0 on success, -1 on failure
157 int afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
161 afid->Cell = avc->fid.Cell;
162 afid->Fid.Volume = avc->fid.Fid.Volume;
164 if (vType(avc) == VREG) {
165 /* Normal files have the dir fid embedded in the vcache. */
166 afid->Fid.Vnode = avc->parentVnode;
167 afid->Fid.Unique = avc->parentUnique;
169 } else if (vType(avc) == VDIR) {
170 /* If dir or parent dir created locally*/
171 tdc = afs_FindDCacheByFid(&avc->fid);
173 /* Lookup each entry for the fid. It should be the first. */
174 afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid);
185 struct VenusFid *fid;
191 * Hook that searches a certain fid's name.
193 * \param hdata NameAndFid structure containin a pointer to a fid
194 * and an allocate name. The name will be filled when hit.
196 int get_vnode_name_hook(void *hdata,
201 struct NameAndFid *nf = (struct NameAndFid *) hdata;
203 if ((nf->fid->Fid.Vnode == vnode) &&
204 (nf->fid->Fid.Unique == unique)) {
205 nf->name_len = strlen(aname);
206 memcpy(nf->name, aname, nf->name_len);
207 nf->name[nf->name_len] = 0;
216 * Try to get a vnode's name by comparing all parent dir's entries
217 * to the given fid. It can also return the dir's dcache.
219 * \param avc The file's vcache.
220 * \param afid The parent dir's fid.
221 * \param aname A preallocated string for the name.
222 * \param deleted Has this file been deleted? If yes, use the shadow
223 * dir for looking up the name.
225 int afs_GetVnodeName(struct vcache *avc,
226 struct VenusFid *afid,
232 struct vcache *parent_vc;
233 struct NameAndFid tnf;
234 struct VenusFid parent_fid;
235 struct VenusFid shadow_fid;
237 /* List dir contents and get it's tdc. */
239 /* For deleted files, get the shadow dir's tdc: */
241 /* Get the parent dir's vcache that contains the shadow fid. */
242 parent_fid.Cell = avc->fid.Cell;
243 parent_fid.Fid.Volume = avc->fid.Fid.Volume;
244 if (avc->ddirty_flags & VDisconRename) {
245 /* For renames the old dir fid is needed. */
246 parent_fid.Fid.Vnode = avc->oldVnode;
247 parent_fid.Fid.Unique = avc->oldUnique;
249 parent_fid.Fid.Vnode = afid->Fid.Vnode;
250 parent_fid.Fid.Unique = afid->Fid.Unique;
253 /* Get the parent dir's vcache that contains the shadow fid. */
254 ObtainSharedLock(&afs_xvcache, 755);
255 parent_vc = afs_FindVCache(&parent_fid, 0, 1);
256 ReleaseSharedLock(&afs_xvcache);
261 shadow_fid.Cell = parent_vc->fid.Cell;
262 shadow_fid.Fid.Volume = parent_vc->fid.Fid.Volume;
263 shadow_fid.Fid.Vnode = parent_vc->shVnode;
264 shadow_fid.Fid.Unique = parent_vc->shUnique;
266 afs_PutVCache(parent_vc);
268 /* Get shadow dir's dcache. */
269 tdc = afs_FindDCacheByFid(&shadow_fid);
273 /* For normal files, look into the current dir's entry. */
274 tdc = afs_FindDCacheByFid(afid);
281 afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
283 if (tnf.name_len == -1)
292 struct DirtyChildrenCount {
298 * Lookup dirty deleted vnodes in this dir.
300 int chk_del_children_hook(void *hdata,
305 struct VenusFid tfid;
306 struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
309 if ((aname[0] == '.') && !aname[1])
310 /* Skip processing this dir again.
311 * It would result in an endless loop.
315 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
316 /* Don't process parent dir. */
319 /* Get this file's vcache. */
320 tfid.Cell = v->vc->fid.Cell;
321 tfid.Fid.Volume = v->vc->fid.Fid.Volume;
322 tfid.Fid.Vnode = vnode;
323 tfid.Fid.Unique = unique;
325 ObtainSharedLock(&afs_xvcache, 757);
326 tvc = afs_FindVCache(&tfid, 0, 1);
327 ReleaseSharedLock(&afs_xvcache);
329 /* Count unfinished dirty children. */
331 ObtainReadLock(&tvc->lock);
332 if (tvc->ddirty_flags || tvc->shVnode)
334 ReleaseReadLock(&tvc->lock);
343 * Check if entries have been deleted in a vnode's shadow
346 * \return Returns the number of dirty children.
348 * \note afs_DDirtyVCListLock must be write locked.
350 int afs_CheckDeletedChildren(struct vcache *avc)
353 struct DirtyChildrenCount dcc;
354 struct VenusFid shadow_fid;
360 shadow_fid.Cell = avc->fid.Cell;
361 shadow_fid.Fid.Volume = avc->fid.Fid.Volume;
362 shadow_fid.Fid.Vnode = avc->shVnode;
363 shadow_fid.Fid.Unique = avc->shUnique;
367 /* Get shadow dir's dcache. */
368 tdc = afs_FindDCacheByFid(&shadow_fid);
371 afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc);
379 * Changes a file's parent fid references.
381 int fix_children_fids_hook(void *hdata,
386 struct VenusFid tfid;
387 struct VenusFid *afid = (struct VenusFid *) hdata;
389 struct dcache *tdc = NULL;
391 if ((aname[0] == '.') && !aname[1])
394 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
397 tfid.Cell = afid->Cell;
398 tfid.Fid.Volume = afid->Fid.Volume;
399 tfid.Fid.Vnode = vnode;
400 tfid.Fid.Unique = unique;
403 /* vnode's parity indicates that it's a file. */
405 /* Get the vcache. */
406 ObtainSharedLock(&afs_xvcache, 759);
407 tvc = afs_FindVCache(&tfid, 0, 1);
408 ReleaseSharedLock(&afs_xvcache);
410 /* Change the fields. */
412 tvc->parentVnode = afid->Fid.Vnode;
413 tvc->parentUnique = afid->Fid.Unique;
418 /* It's a dir. Fix this dir's .. entry to contain the new fid. */
419 /* Seek the dir's dcache. */
420 tdc = afs_FindDCacheByFid(&tfid);
422 /* Change the .. entry fid. */
423 afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode);
426 } /* if (!(vnode % 2))*/
432 * Fixes the parentVnode and parentUnique fields of all
433 * files (not dirs) contained in the directory pointed by
434 * old_fid. This is useful on resync, when a locally created dir
435 * get's a new fid and all the children references must be updated
436 * to reflect the new fid.
438 * \note The dir's fid hasn't been changed yet, it is still referenced
441 * \param old_fid The current dir's fid.
442 * \param new_fid The new dir's fid.
444 void afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
448 /* Get shadow dir's dcache. */
449 tdc = afs_FindDCacheByFid(old_fid);
450 /* Change the fids. */
452 afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid);
457 int list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique)
459 printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique);
463 void afs_DbgListDirEntries(struct VenusFid *afid)
467 /* Get shadow dir's dcache. */
468 tdc = afs_FindDCacheByFid(afid);
470 afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL);
476 * Handles file renaming on reconnection:
477 * - Get the old name from the old dir's shadow dir.
478 * - Get the new name from the current dir.
479 * - Old dir fid and new dir fid are collected along the way.
481 int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
483 struct VenusFid old_pdir_fid, new_pdir_fid;
484 char *old_name = NULL, *new_name = NULL;
485 struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
486 struct AFSVolSync tsync;
491 /* Get old dir vcache. */
492 old_pdir_fid.Cell = avc->fid.Cell;
493 old_pdir_fid.Fid.Volume = avc->fid.Fid.Volume;
494 old_pdir_fid.Fid.Vnode = avc->oldVnode;
495 old_pdir_fid.Fid.Unique = avc->oldUnique;
498 old_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
500 printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n");
503 code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1);
505 printf("afs_ProcessOpRename: Couldn't find old name.\n");
509 /* Alloc data first. */
510 new_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
512 printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n");
517 if (avc->ddirty_flags & VDisconRenameSameDir) {
518 /* If we're in the same dir, don't do the lookups all over again,
519 * just copy fid and vcache from the old dir.
521 memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid));
523 /* Get parent dir's FID.*/
524 new_pdir_fid.Fid.Unique = 0;
525 afs_GetParentDirFid(avc, &new_pdir_fid);
526 if (!new_pdir_fid.Fid.Unique) {
527 printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n");
533 /* And finally get the new name. */
534 code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0);
536 printf("afs_ProcessOpRename: Couldn't find new name.\n");
540 /* Send to data to server. */
542 tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK);
544 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
546 code = RXAFS_Rename(tc->id,
547 (struct AFSFid *)&old_pdir_fid.Fid,
549 (struct AFSFid *)&new_pdir_fid.Fid,
559 } while (afs_Analyze(tc,
563 AFS_STATS_FS_RPCIDX_RENAME,
568 printf("afs_ProcessOpRename: server code=%u\n", code);
571 afs_osi_Free(new_name, AFSNAMEMAX);
573 afs_osi_Free(old_name, AFSNAMEMAX);
578 * Handles all the reconnection details:
579 * - Get all the details about the vnode: name, fid, and parent dir fid.
580 * - Send data to server.
582 * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
584 int afs_ProcessOpCreate(struct vcache *avc,
585 struct vrequest *areq,
586 struct AFS_UCRED *acred)
589 struct AFSStoreStatus InStatus;
590 struct AFSFetchStatus OutFidStatus, OutDirStatus;
591 struct VenusFid pdir_fid, newFid;
592 struct server *hostp = NULL;
593 struct AFSCallBack CallBack;
594 struct AFSVolSync tsync;
595 struct vcache *tdp = NULL, *tvc = NULL;
596 struct dcache *tdc = NULL;
598 afs_int32 now, hash, new_hash, index;
602 /* Get parent dir's FID. */
603 pdir_fid.Fid.Unique = 0;
604 afs_GetParentDirFid(avc, &pdir_fid);
605 if (!pdir_fid.Fid.Unique) {
606 printf("afs_ProcessOpCreate: Couldn't find parent dir's FID.\n");
610 tname = afs_osi_Alloc(AFSNAMEMAX);
612 printf("afs_ProcessOpCreate: Couldn't alloc space for file name\n");
616 /* Get vnode's name. */
617 code = afs_GetVnodeName(avc, &pdir_fid, tname, 0);
619 printf("afs_ProcessOpCreate: Couldn't find file name\n");
623 /* Get parent dir vcache. */
624 ObtainSharedLock(&afs_xvcache, 760);
625 tdp = afs_FindVCache(&pdir_fid, 0, 1);
626 ReleaseSharedLock(&afs_xvcache);
628 printf("afs_ProcessOpCreate: Couldn't find parent dir's vcache\n");
633 if (tdp->ddirty_flags & VDisconCreate) {
634 /* If the parent dir has been created locally, defer
635 * this vnode for later */
636 printf("afs_ProcessOpCreate: deferring this vcache\n");
642 InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
643 InStatus.ClientModTime = avc->m.Date;
644 InStatus.Owner = avc->m.Owner;
645 InStatus.Group = (afs_int32) acred->cr_gid;
646 /* Only care about protection bits. */
647 InStatus.UnixModeBits = avc->m.Mode & 0xffff;
649 /* Connect to server. */
650 if (vType(avc) == VREG) {
651 /* Make file on server. */
653 tc = afs_Conn(&tdp->fid, areq, SHARED_LOCK);
655 /* Remember for callback processing. */
656 hostp = tc->srvr->server;
658 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE);
660 code = RXAFS_CreateFile(tc->id,
661 (struct AFSFid *)&tdp->fid.Fid,
664 (struct AFSFid *) &newFid.Fid,
671 CallBack.ExpirationTime += now;
674 } while (afs_Analyze(tc,
678 AFS_STATS_FS_RPCIDX_CREATEFILE,
682 } else if (vType(avc) == VDIR) {
683 /* Make dir on server. */
685 tc = afs_Conn(&tdp->fid, areq, SHARED_LOCK);
687 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR);
690 code = RXAFS_MakeDir(tc->id,
691 (struct AFSFid *) &tdp->fid.Fid,
694 (struct AFSFid *) &newFid.Fid,
701 CallBack.ExpirationTime += now;
702 /* DON'T forget to set the callback at some point. */
705 } while (afs_Analyze(tc,
709 AFS_STATS_FS_RPCIDX_MAKEDIR,
712 } /* Do server changes. */
714 /* TODO: Handle errors. */
716 printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code);
720 /* The rpc doesn't set the cell number. */
721 newFid.Cell = avc->fid.Cell;
724 * Change the fid in the dir entry.
727 /* Seek the dir's dcache. */
728 tdc = afs_FindDCacheByFid(&tdp->fid);
730 /* And now change the fid in the parent dir entry. */
731 afs_dir_ChangeFid(tdc, tname, &avc->fid.Fid.Vnode, &newFid.Fid.Vnode);
735 if (vType(avc) == VDIR) {
736 /* Change fid in the dir for the "." entry. ".." has alredy been
737 * handled by afs_FixChildrenFids when processing the parent dir.
739 tdc = afs_FindDCacheByFid(&avc->fid);
741 afs_dir_ChangeFid(tdc, ".", &avc->fid.Fid.Vnode, &newFid.Fid.Vnode);
743 if (avc->m.LinkCount >= 2)
744 /* For non empty dirs, fix children's parentVnode and parentUnique
747 afs_FixChildrenFids(&avc->fid, &newFid);
753 /* Recompute hash chain positions for vnode and dcaches.
754 * Then change to the new FID.
757 /* The vcache goes first. */
758 ObtainWriteLock(&afs_xvcache, 735);
761 hash = VCHash(&avc->fid);
763 new_hash = VCHash(&newFid);
765 /* Remove hash from old position. */
766 /* XXX: not checking array element contents. It shouldn't be empty.
767 * If it oopses, then something else might be wrong.
769 if (afs_vhashT[hash] == avc) {
770 /* First in hash chain (might be the only one). */
771 afs_vhashT[hash] = avc->hnext;
773 /* More elements in hash chain. */
774 for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
775 if (tvc->hnext == avc) {
776 tvc->hnext = avc->hnext;
780 } /* if (!afs_vhashT[i]->hnext) */
781 QRemove(&avc->vhashq);
783 /* Insert hash in new position. */
784 avc->hnext = afs_vhashT[new_hash];
785 afs_vhashT[new_hash] = avc;
786 QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);
788 ReleaseWriteLock(&afs_xvcache);
790 /* Do the same thing for all dcaches. */
791 hash = DVHash(&avc->fid);
792 ObtainWriteLock(&afs_xdcache, 743);
793 for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
794 hash = afs_dvnextTbl[index];
795 tdc = afs_GetDSlot(index, NULL);
796 ReleaseReadLock(&tdc->tlock);
797 if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
798 if (!FidCmp(&tdc->f.fid, &avc->fid)) {
800 /* Safer but slower. */
801 afs_HashOutDCache(tdc, 0);
803 /* Put dcache in new positions in the dchash and dvhash. */
804 new_hash = DCHash(&newFid, tdc->f.chunk);
805 afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
806 afs_dchashTbl[new_hash] = tdc->index;
808 new_hash = DVHash(&newFid);
809 afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
810 afs_dvhashTbl[new_hash] = tdc->index;
812 afs_indexUnique[tdc->index] = newFid.Fid.Unique;
813 memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
815 } /* if uniquifier match */
818 } /* for all dcaches in this hash bucket */
819 ReleaseWriteLock(&afs_xdcache);
821 /* Now we can set the new fid. */
822 memcpy(&avc->fid, &newFid, sizeof(struct VenusFid));
825 /* Unset parent dir CStat flag, so it will get refreshed on next
828 ObtainWriteLock(&tdp->lock, 745);
829 tdp->states &= ~CStatd;
830 ReleaseWriteLock(&tdp->lock);
835 afs_osi_Free(tname, AFSNAMEMAX);
840 * Remove a vnode on the server, be it file or directory.
841 * Not much to do here only get the parent dir's fid and call the
844 * \param avc The deleted vcache
847 * \note The vcache refcount should be dropped because it points to
848 * a deleted vnode and has served it's purpouse, but we drop refcount
849 * on shadow dir deletio (we still need it for that).
851 * \note avc must be write locked.
853 int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
856 struct AFSFetchStatus OutDirStatus;
857 struct VenusFid pdir_fid;
858 struct AFSVolSync tsync;
860 struct vcache *tdp = NULL;
864 /* Get parent dir's FID. */
865 pdir_fid.Fid.Unique = 0;
866 afs_GetParentDirFid(avc, &pdir_fid);
867 if (!pdir_fid.Fid.Unique) {
868 printf("afs_ProcessOpRemove: Couldn't find parent dir's FID.\n");
872 tname = afs_osi_Alloc(AFSNAMEMAX);
874 printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n");
879 code = afs_GetVnodeName(avc, &pdir_fid, tname, 1);
881 printf("afs_ProcessOpRemove: Couldn't find file name\n");
885 if ((vType(avc) == VDIR) && (afs_CheckDeletedChildren(avc))) {
886 /* Deleted children of this dir remain unsynchronized.
893 if (vType(avc) == VREG) {
894 /* Remove file on server. */
896 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
898 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
900 code = RXAFS_RemoveFile(tc->id,
910 } while (afs_Analyze(tc,
914 AFS_STATS_FS_RPCIDX_REMOVEFILE,
918 } else if (vType(avc) == VDIR) {
919 /* Remove dir on server. */
921 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
923 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
925 code = RXAFS_RemoveDir(tc->id,
934 } while (afs_Analyze(tc,
938 AFS_STATS_FS_RPCIDX_REMOVEDIR,
942 } /* if (vType(avc) == VREG) */
945 printf("afs_ProcessOpRemove: server returned code=%u\n", code);
947 /* Remove the statd flag from parent dir's vcache. */
948 ObtainSharedLock(&afs_xvcache, 761);
949 tdp = afs_FindVCache(&pdir_fid, 0, 1);
950 ReleaseSharedLock(&afs_xvcache);
952 ObtainWriteLock(&tdp->lock, 746);
953 tdp->states &= ~CStatd;
954 ReleaseWriteLock(&tdp->lock);
958 afs_osi_Free(tname, AFSNAMEMAX);
963 * Send disconnected file changes to the server.
965 * \note Call with vnode locked both locally and on the server.
967 * \param avc Vnode that gets synchronized to the server.
968 * \param areq Used for obtaining a conn struct.
970 * \return 0 for success. On failure, other error codes.
972 int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
975 struct AFSStoreStatus sstat;
976 struct AFSFetchStatus fstat;
977 struct AFSVolSync tsync;
982 /* Start multiplexing dirty operations from ddirty_flags field: */
983 if (avc->ddirty_flags & VDisconSetAttrMask) {
985 /* Turn dirty vc data into a new store status... */
986 if (afs_GenStoreStatus(avc, &sstat) > 0) {
988 tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
990 /* ... and send it. */
991 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS);
993 code = RXAFS_StoreStatus(tc->id,
994 (struct AFSFid *) &avc->fid.Fid,
1004 } while (afs_Analyze(tc,
1008 AFS_STATS_FS_RPCIDX_STORESTATUS,
1012 } /* if (afs_GenStoreStatus() > 0)*/
1013 } /* disconnected SETATTR */
1018 if (avc->ddirty_flags &
1022 | VDisconWriteOsiFlush)) {
1026 tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
1028 /* Set storing flags. XXX: A tad inefficient ... */
1029 if (avc->ddirty_flags & VDisconWriteClose)
1030 flags |= AFS_LASTSTORE;
1031 if (avc->ddirty_flags & VDisconWriteOsiFlush)
1032 flags |= (AFS_SYNC | AFS_LASTSTORE);
1033 if (avc->ddirty_flags & VDisconWriteFlush)
1036 /* Try to send store to server. */
1037 /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */
1038 code = afs_StoreAllSegments(avc, areq, flags);
1042 } while (afs_Analyze(tc,
1046 AFS_STATS_FS_RPCIDX_STOREDATA,
1050 } /* disconnected TRUNC | WRITE */
1056 * All files that have been dirty before disconnection are going to
1057 * be replayed back to the server.
1059 * \param areq Request from the user.
1060 * \param acred User credentials.
1062 * \return If all files synchronized succesfully, return 0, otherwise
1065 * \note For now, it's the request from the PDiscon pioctl.
1068 int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred)
1070 struct afs_conn *tc;
1071 struct vcache *tvc, *tmp;
1072 struct AFSFetchStatus fstat;
1073 struct AFSCallBack callback;
1074 struct AFSVolSync tsync;
1077 afs_int32 start = 0;
1079 //AFS_STATCNT(afs_ResyncDisconFiles);
1081 ObtainWriteLock(&afs_disconDirtyLock, 707);
1083 while (!QEmpty(&afs_disconDirty)) {
1084 tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1086 /* Can't lock tvc whilst holding the discon dirty lock */
1087 ReleaseWriteLock(&afs_disconDirtyLock);
1089 /* Get local write lock. */
1090 ObtainWriteLock(&tvc->lock, 705);
1092 if (tvc->ddirty_flags & VDisconRemove) {
1093 /* Delete the file on the server and just move on
1094 * to the next file. After all, it has been deleted
1095 * we can't replay any other operation it.
1097 code = afs_ProcessOpRemove(tvc, areq);
1100 } else if (tvc->ddirty_flags & VDisconCreate) {
1101 /* For newly created files, we don't need a server lock. */
1102 code = afs_ProcessOpCreate(tvc, areq, acred);
1106 tvc->ddirty_flags &= ~VDisconCreate;
1107 tvc->ddirty_flags |= VDisconCreated;
1110 /* Get server write lock. */
1112 tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
1114 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
1116 code = RXAFS_SetLock(tc->id,
1117 (struct AFSFid *)&tvc->fid.Fid,
1125 } while (afs_Analyze(tc,
1129 AFS_STATS_FS_RPCIDX_SETLOCK,
1136 if (tvc->ddirty_flags & VDisconRename) {
1137 /* If we're renaming the file, do so now */
1138 code = afs_ProcessOpRename(tvc, areq);
1140 goto unlock_srv_file;
1143 /* Issue a FetchStatus to get info about DV and callbacks. */
1145 tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
1147 tvc->callback = tc->srvr->server;
1149 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
1151 code = RXAFS_FetchStatus(tc->id,
1152 (struct AFSFid *)&tvc->fid.Fid,
1161 } while (afs_Analyze(tc,
1165 AFS_STATS_FS_RPCIDX_FETCHSTATUS,
1170 goto unlock_srv_file;
1173 if ((dv_match(tvc, fstat) && (tvc->m.Date == fstat.ServerModTime)) ||
1174 (afs_ConflictPolicy == CLIENT_WINS) ||
1175 (tvc->ddirty_flags & VDisconCreated)) {
1177 * Send changes to the server if there's data version match, or
1178 * client wins policy has been selected or file has been created
1179 * but doesn't have it's the contents on to the server yet.
1182 * XXX: Checking server attr changes by timestamp might not the
1183 * most elegant solution, but it's the most viable one that we could find.
1185 afs_UpdateStatus(tvc, &tvc->fid, areq, &fstat, &callback, start);
1186 code = afs_SendChanges(tvc, areq);
1188 } else if (afs_ConflictPolicy == SERVER_WINS) {
1189 /* DV mismatch, apply collision resolution policy. */
1190 /* Discard this files chunks and remove from current dir. */
1191 afs_ResetVCache(tvc, acred);
1192 tvc->truncPos = AFS_NOTRUNC;
1194 printf("afs_ResyncDisconFiles: no resolution policy selected.\n");
1195 } /* if DV match or client wins policy */
1198 /* Release server write lock. */
1200 tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
1202 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
1204 ucode = RXAFS_ReleaseLock(tc->id,
1205 (struct AFSFid *) &tvc->fid.Fid,
1211 } while (afs_Analyze(tc,
1215 AFS_STATS_FS_RPCIDX_RELEASELOCK,
1220 ObtainWriteLock(&afs_disconDirtyLock, 710);
1222 /* Replayed successfully - pull the vcache from the
1223 * disconnected list */
1224 tvc->ddirty_flags = 0;
1225 QRemove(&tvc->dirtyq);
1228 if (code == EAGAIN) {
1229 /* Operation was deferred. Pull it from the current place in
1230 * the list, and stick it at the end again */
1231 QRemove(&tvc->dirtyq);
1232 QAdd(&afs_disconDirty, &tvc->dirtyq);
1234 /* Failed - keep state as is, and let the user know we died */
1235 ReleaseWriteLock(&tvc->lock);
1240 /* Release local write lock. */
1241 ReleaseWriteLock(&tvc->lock);
1245 ReleaseWriteLock(&afs_disconDirtyLock);
1249 /* Dispose of all of the shadow directories */
1250 afs_DisconDiscardAllShadows(0, acred);
1252 ReleaseWriteLock(&afs_disconDirtyLock);
1257 * Discard all of our shadow directory copies. If squash is true, then
1258 * we also invalidate the vcache holding the shadow directory, to ensure
1259 * that any disconnected changes are deleted
1264 * \note afs_disconDirtyLock must be held on entry. It will be released
1269 afs_DisconDiscardAllShadows(int squash, struct AFS_UCRED *acred) {
1272 while (!QEmpty(&afs_disconShadow)) {
1273 tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq);
1275 /* Must release the dirty lock to be able to get a vcache lock */
1276 ReleaseWriteLock(&afs_disconDirtyLock);
1277 ObtainWriteLock(&tvc->lock, 706);
1279 afs_DeleteShadowDir(tvc);
1284 afs_ResetVCache(tvc, acred);
1286 ObtainWriteLock(&afs_disconDirtyLock, 709);
1287 QRemove(&tvc->shadowq);
1289 ReleaseWriteLock(&tvc->lock);
1294 * This function throws away the whole disconnected state, allowing
1295 * the cache manager to reconnect to a server if we get into a state
1296 * where reconiliation is impossible.
1302 afs_DisconDiscardAll(struct AFS_UCRED *acred) {
1305 ObtainWriteLock(&afs_disconDirtyLock, 717);
1306 while (!QEmpty(&afs_disconDirty)) {
1307 tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1308 ReleaseWriteLock(&afs_disconDirtyLock);
1310 ObtainWriteLock(&tvc->lock, 718);
1311 afs_ResetVCache(tvc, acred);
1312 tvc->truncPos = AFS_NOTRUNC;
1313 ReleaseWriteLock(&tvc->lock);
1315 ObtainWriteLock(&afs_disconDirtyLock, 719);
1318 afs_DisconDiscardAllShadows(1, acred);
1320 ReleaseWriteLock(&afs_disconDirtyLock);
1324 * Print list of disconnected files.
1326 * \note Call with afs_DDirtyVCListLock read locked.
1328 void afs_DbgDisconFiles()
1334 printf("List of dirty files: \n");
1336 ObtainReadLock(&afs_disconDirtyLock);
1337 for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) {
1338 tvc = QEntry(q, struct vcache, dirtyq);
1340 printf("Cell=%u Volume=%u VNode=%u Unique=%u\n",
1342 tvc->fid.Fid.Volume,
1344 tvc->fid.Fid.Unique);
1348 osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
1350 ReleaseReadLock(&afs_disconDirtyLock);
1354 * Generate a fake fid for a disconnected shadow dir.
1355 * Similar to afs_GenFakeFid, only that it uses the dhash
1356 * to search for a uniquifier because a shadow dir lives only
1361 * \note Don't forget to fill in afid with Cell and Volume.
1363 void afs_GenShadowFid(struct VenusFid *afid)
1365 afs_uint32 i, index, max_unique = 1;
1366 struct vcache *tvc = NULL;
1368 /* Try generating a fid that isn't used in the vhash. */
1370 afid->Fid.Vnode = afs_DisconVnode + 1;
1373 ObtainWriteLock(&afs_xdcache, 737);
1374 for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
1375 i = afs_dvnextTbl[index];
1376 if (afs_indexUnique[index] > max_unique)
1377 max_unique = afs_indexUnique[index];
1380 ReleaseWriteLock(&afs_xdcache);
1381 afid->Fid.Unique = max_unique + 1;
1382 afs_DisconVnode += 2;
1383 if (!afs_DisconVnode)
1384 afs_DisconVnode = 2;
1386 /* Is this a used vnode? */
1387 ObtainSharedLock(&afs_xvcache, 762);
1388 tvc = afs_FindVCache(afid, 0, 1);
1389 ReleaseSharedLock(&afs_xvcache);
1396 * Generate a fake fid (vnode and uniquifier) for a vcache
1397 * (either dir or normal file). The vnode is generated via
1398 * afs_DisconVNode and the uniquifier by getting the highest
1399 * uniquifier on a hash chain and incrementing it by one.
1401 * \param avc afid The fid structre that will be filled.
1402 * \param avtype Vnode type: VDIR/VREG.
1404 * \note The cell number must be completed somewhere else.
1406 void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype)
1409 afs_uint32 max_unique = 1, i;
1412 afid->Fid.Vnode = afs_DisconVnode + 1;
1413 else if (avtype == VREG)
1414 afid->Fid.Vnode = afs_DisconVnode;
1416 ObtainWriteLock(&afs_xvcache, 736);
1418 for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
1419 if (tvc->fid.Fid.Unique > max_unique)
1420 max_unique = tvc->fid.Fid.Unique;
1422 ReleaseWriteLock(&afs_xvcache);
1424 afid->Fid.Unique = max_unique + 1;
1425 afs_DisconVnode += 2;
1426 if (!afs_DisconVnode)
1427 afs_DisconVnode = 2;
1431 * Fill in stats for a newly created file/directory.
1433 * \param adp The parent dir's vcache.
1434 * \param avc The created vnode.
1435 * \param afid The new fid.
1438 * \param file_type Specify if file or directory.
1440 * \note Call with avc write locked.
1442 void afs_GenDisconStatus(
1445 struct VenusFid *afid,
1446 struct vattr *attrs,
1447 struct vrequest *areq,
1450 memcpy(&avc->fid, afid, sizeof(struct VenusFid));
1451 avc->m.Mode = attrs->va_mode;
1453 * avc->m.Owner = attrs->va_uid;
1454 * But now we use the parent dir's ownership,
1455 * there's no other way to get a server owner id.
1456 * XXX: Does it really matter?
1458 avc->m.Group = adp->m.Group;
1459 avc->m.Owner = adp->m.Owner;
1460 hset64(avc->m.DataVersion, 0, 0);
1461 avc->m.Length = attrs->va_size;
1462 avc->m.Date = osi_Time();
1463 if (file_type == VREG) {
1464 vSetType(avc, VREG);
1465 avc->m.Mode |= S_IFREG;
1466 avc->m.LinkCount = 1;
1467 } else if (file_type == VDIR) {
1468 vSetType(avc, VDIR);
1469 avc->m.Mode |= S_IFDIR;
1470 avc->m.LinkCount = 2;
1473 avc->anyAccess = adp->anyAccess;
1474 afs_AddAxs(avc->Access, areq->uid, adp->Access->axess);
1476 avc->callback = NULL;
1477 avc->states |= CStatd;
1478 avc->states &= ~CBulkFetching;