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 /*! Global list of dirty vcaches. */
25 /*! Last added element. */
26 struct vcache *afs_DDirtyVCList = NULL;
28 struct vcache *afs_DDirtyVCListStart = NULL;
29 /*! Previous element in the list. */
30 struct vcache *afs_DDirtyVCListPrev = NULL;
32 /*! Locks list of dirty vcaches. */
33 afs_rwlock_t afs_DDirtyVCListLock;
35 extern afs_int32 *afs_dvhashTbl; /*Data cache hash table */
36 extern afs_int32 *afs_dchashTbl; /*Data cache hash table */
37 extern afs_int32 *afs_dvnextTbl; /*Dcache hash table links */
38 extern afs_int32 *afs_dcnextTbl; /*Dcache hash table links */
39 extern struct dcache **afs_indexTable; /*Pointers to dcache entries */
41 /*! Vnode number. On file creation, use the current
42 * value and increment it.
44 afs_uint32 afs_DisconVnode = 2;
46 /*! Conflict policy. */
54 afs_int32 afs_ConflictPolicy = SERVER_WINS;
57 * Find the first dcache of a file that has the specified fid.
58 * Similar to afs_FindDCache, only that it takes a fid instead
59 * of a vcache and it can get the first dcache.
63 * \return The found dcache or NULL.
65 struct dcache *afs_FindDCacheByFid(register struct VenusFid *afid)
67 register afs_int32 i, index;
68 register 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_GetDSlot(index, NULL);
75 ReleaseReadLock(&tdc->tlock);
76 if (!FidCmp(&tdc->f.fid, afid)) {
77 break; /* leaving refCount high for caller */
81 index = afs_dvnextTbl[index];
83 ReleaseWriteLock(&afs_xdcache);
91 * Generate a store status from a dirty vcache entry.
93 * \param avc Dirty vcache entry.
96 * \note The vnode must be share locked. It is called only on resync,
97 * where the vnode is write locked locally and and the server.
99 * \return Mask of operations.
101 int afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
103 if (!avc || !astat || !avc->ddirty_flags)
106 /* Clean up store stat. */
107 memset(astat, 0, sizeof(struct AFSStoreStatus));
109 if (avc->ddirty_flags & VDisconSetTime) {
110 /* Update timestamp. */
111 astat->ClientModTime = avc->m.Date;
112 astat->Mask |= AFS_SETMODTIME;
115 if (avc->ddirty_flags & VDisconSetMode) {
116 /* Copy the mode bits. */
117 astat->UnixModeBits = avc->m.Mode;
118 astat->Mask |= AFS_SETMODE;
121 /* XXX: more to come... ?*/
127 * Hook for filtering the local dir fid by searching the "." entry.
129 * \param hdata The fid to be filled.
131 int get_parent_dir_fid_hook(void *hdata,
136 struct VenusFid *tfid = (struct VenusFid *) hdata;
138 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) {
139 tfid->Fid.Vnode = vnode;
140 tfid->Fid.Unique = unique;
148 * Get a the dir's fid by looking in the vcache for simple files and
149 * in the ".." entry for directories.
151 * \param avc The file's vhash entry.
152 * \param afid Put the fid here.
154 int afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
158 afid->Cell = avc->fid.Cell;
159 afid->Fid.Volume = avc->fid.Fid.Volume;
161 if (vType(avc) == VREG) {
162 /* Normal files have the dir fid embedded in the vcache. */
163 afid->Fid.Vnode = avc->parentVnode;
164 afid->Fid.Unique = avc->parentUnique;
166 } else if (vType(avc) == VDIR) {
167 /* If dir or parent dir created locally*/
168 tdc = afs_FindDCacheByFid(&avc->fid);
170 /* Lookup each entry for the fid. It should be the first. */
171 afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid);
180 struct VenusFid *fid;
186 * Hook that searches a certain fid's name.
188 * \param hdata NameAndFid structure containin a pointer to a fid
189 * and an allocate name. The name will be filled when hit.
191 int get_vnode_name_hook(void *hdata,
196 struct NameAndFid *nf = (struct NameAndFid *) hdata;
198 if ((nf->fid->Fid.Vnode == vnode) &&
199 (nf->fid->Fid.Unique == unique)) {
200 nf->name_len = strlen(aname);
201 memcpy(nf->name, aname, nf->name_len);
202 nf->name[nf->name_len] = 0;
211 * Try to get a vnode's name by comparing all parent dir's entries
212 * to the given fid. It can also return the dir's dcache.
214 * \param avc The file's vcache.
215 * \param afid The parent dir's fid.
216 * \param aname A preallocated string for the name.
217 * \param deleted Has this file been deleted? If yes, use the shadow
218 * dir for looking up the name.
220 int afs_GetVnodeName(struct vcache *avc,
221 struct VenusFid *afid,
227 struct vcache *parent_vc;
228 struct NameAndFid tnf;
229 struct VenusFid parent_fid;
230 struct VenusFid shadow_fid;
232 /* List dir contents and get it's tdc. */
234 /* For deleted files, get the shadow dir's tdc: */
236 /* Get the parent dir's vcache that contains the shadow fid. */
237 parent_fid.Cell = avc->fid.Cell;
238 parent_fid.Fid.Volume = avc->fid.Fid.Volume;
239 if (avc->ddirty_flags & VDisconRename) {
240 /* For renames the old dir fid is needed. */
241 parent_fid.Fid.Vnode = avc->oldVnode;
242 parent_fid.Fid.Unique = avc->oldUnique;
244 parent_fid.Fid.Vnode = afid->Fid.Vnode;
245 parent_fid.Fid.Unique = afid->Fid.Unique;
248 /* Get the parent dir's vcache that contains the shadow fid. */
249 ObtainSharedLock(&afs_xvcache, 755);
250 parent_vc = afs_FindVCache(&parent_fid, 0, 1);
251 ReleaseSharedLock(&afs_xvcache);
256 shadow_fid.Cell = parent_vc->fid.Cell;
257 shadow_fid.Fid.Volume = parent_vc->fid.Fid.Volume;
258 shadow_fid.Fid.Vnode = parent_vc->shVnode;
259 shadow_fid.Fid.Unique = parent_vc->shUnique;
261 afs_PutVCache(parent_vc);
263 /* Get shadow dir's dcache. */
264 tdc = afs_FindDCacheByFid(&shadow_fid);
268 /* For normal files, look into the current dir's entry. */
269 tdc = afs_FindDCacheByFid(afid);
276 afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
285 struct DirtyChildrenCount {
291 * Lookup dirty deleted vnodes in this dir.
293 int chk_del_children_hook(void *hdata,
298 struct VenusFid tfid;
299 struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
302 if ((aname[0] == '.') && !aname[1])
303 /* Skip processing this dir again.
304 * It would result in an endless loop.
308 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
309 /* Don't process parent dir. */
312 /* Get this file's vcache. */
313 tfid.Cell = v->vc->fid.Cell;
314 tfid.Fid.Volume = v->vc->fid.Fid.Volume;
315 tfid.Fid.Vnode = vnode;
316 tfid.Fid.Unique = unique;
318 ObtainSharedLock(&afs_xvcache, 757);
319 tvc = afs_FindVCache(&tfid, 0, 1);
320 ReleaseSharedLock(&afs_xvcache);
322 /* Count unfinished dirty children. VDisconShadowed can still be set,
323 * because we need it to remove the shadow dir.
325 if (tvc && tvc->ddirty_flags) {
334 * Check if entries have been deleted in a vnode's shadow
337 * \return Returns the number of dirty children.
339 * \note afs_DDirtyVCListLock must be write locked.
341 int afs_CheckDeletedChildren(struct vcache *avc)
344 struct DirtyChildrenCount dcc;
345 struct VenusFid shadow_fid;
347 if (!(avc->ddirty_flags & VDisconShadowed))
351 shadow_fid.Cell = avc->fid.Cell;
352 shadow_fid.Fid.Volume = avc->fid.Fid.Volume;
353 shadow_fid.Fid.Vnode = avc->shVnode;
354 shadow_fid.Fid.Unique = avc->shUnique;
358 /* Get shadow dir's dcache. */
359 tdc = afs_FindDCacheByFid(&shadow_fid);
362 afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc);
370 * Changes a file's parent fid references.
372 int fix_children_fids_hook(void *hdata,
377 struct VenusFid tfid;
378 struct VenusFid *afid = (struct VenusFid *) hdata;
380 struct dcache *tdc = NULL;
382 if ((aname[0] == '.') && !aname[1])
385 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
388 tfid.Cell = afid->Cell;
389 tfid.Fid.Volume = afid->Fid.Volume;
390 tfid.Fid.Vnode = vnode;
391 tfid.Fid.Unique = unique;
394 /* vnode's parity indicates that it's a file. */
396 /* Get the vcache. */
397 ObtainSharedLock(&afs_xvcache, 759);
398 tvc = afs_FindVCache(&tfid, 0, 1);
399 ReleaseSharedLock(&afs_xvcache);
401 /* Change the fields. */
403 tvc->parentVnode = afid->Fid.Vnode;
404 tvc->parentUnique = afid->Fid.Unique;
409 /* It's a dir. Fix this dir's .. entry to contain the new fid. */
410 /* Seek the dir's dcache. */
411 tdc = afs_FindDCacheByFid(&tfid);
413 /* Change the .. entry fid. */
414 afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode);
417 } /* if (!(vnode % 2))*/
423 * Fixes the parentVnode and parentUnique fields of all
424 * files (not dirs) contained in the directory pointed by
425 * old_fid. This is useful on resync, when a locally created dir
426 * get's a new fid and all the children references must be updated
427 * to reflect the new fid.
429 * \note The dir's fid hasn't been changed yet, it is still referenced
432 * \param old_fid The current dir's fid.
433 * \param new_fid The new dir's fid.
435 void afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
439 /* Get shadow dir's dcache. */
440 tdc = afs_FindDCacheByFid(old_fid);
441 /* Change the fids. */
443 afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid);
448 int list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique)
450 printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique);
454 void afs_DbgListDirEntries(struct VenusFid *afid)
458 /* Get shadow dir's dcache. */
459 tdc = afs_FindDCacheByFid(afid);
461 afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL);
467 * Handles file renaming on reconnection:
468 * - Get the old name from the old dir's shadow dir.
469 * - Get the new name from the current dir.
470 * - Old dir fid and new dir fid are collected along the way.
472 int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
474 struct VenusFid old_pdir_fid, new_pdir_fid;
475 char *old_name, *new_name;
476 struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
477 struct AFSVolSync tsync;
482 /* Get old dir vcache. */
483 old_pdir_fid.Cell = avc->fid.Cell;
484 old_pdir_fid.Fid.Volume = avc->fid.Fid.Volume;
485 old_pdir_fid.Fid.Vnode = avc->oldVnode;
486 old_pdir_fid.Fid.Unique = avc->oldUnique;
489 old_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
491 printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n");
494 code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1);
496 printf("afs_ProcessOpRename: Couldn't find old name.\n");
501 /* Alloc data first. */
502 new_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
504 printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n");
509 if (avc->ddirty_flags & VDisconRenameSameDir) {
510 /* If we're in the same dir, don't do the lookups all over again,
511 * just copy fid and vcache from the old dir.
513 memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid));
515 /* Get parent dir's FID.*/
516 new_pdir_fid.Fid.Unique = 0;
517 afs_GetParentDirFid(avc, &new_pdir_fid);
518 if (!new_pdir_fid.Fid.Unique) {
519 printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n");
525 /* And finally get the new name. */
526 code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0);
528 printf("afs_ProcessOpRename: Couldn't find new name.\n");
533 /* Send to data to server. */
535 tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK);
537 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
539 code = RXAFS_Rename(tc->id,
540 (struct AFSFid *)&old_pdir_fid.Fid,
542 (struct AFSFid *)&new_pdir_fid.Fid,
552 } while (afs_Analyze(tc,
556 AFS_STATS_FS_RPCIDX_RENAME,
561 printf("afs_ProcessOpRename: server code=%u\n", code);
563 afs_osi_Free(new_name, AFSNAMEMAX);
565 afs_osi_Free(old_name, AFSNAMEMAX);
570 * Handles all the reconnection details:
571 * - Get all the details about the vnode: name, fid, and parent dir fid.
572 * - Send data to server.
574 * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
576 int afs_ProcessOpCreate(struct vcache *avc,
577 struct vrequest *areq,
578 struct AFS_UCRED *acred)
581 struct AFSStoreStatus InStatus;
582 struct AFSFetchStatus OutFidStatus, OutDirStatus;
583 struct VenusFid pdir_fid, newFid;
584 struct server *hostp = NULL;
585 struct AFSCallBack CallBack;
586 struct AFSVolSync tsync;
587 struct vcache *tdp = NULL, *tvc = NULL;
588 struct dcache *tdc = NULL;
590 afs_int32 now, hash, new_hash, index;
594 /* Get parent dir's FID. */
595 pdir_fid.Fid.Unique = 0;
596 afs_GetParentDirFid(avc, &pdir_fid);
597 if (!pdir_fid.Fid.Unique) {
598 printf("afs_ProcessOpCreate: Couldn't find parent dir'sFID.\n");
602 tname = afs_osi_Alloc(AFSNAMEMAX);
604 printf("afs_ProcessOpCreate: Couldn't find file name\n");
608 /* Get vnode's name. */
609 code = afs_GetVnodeName(avc, &pdir_fid, tname, 0);
611 printf("afs_ProcessOpCreate: Couldn't find file name\n");
616 /* Get parent dir vcache. */
617 ObtainSharedLock(&afs_xvcache, 760);
618 tdp = afs_FindVCache(&pdir_fid, 0, 1);
619 ReleaseSharedLock(&afs_xvcache);
621 printf("afs_ProcessOpCreate: Couldn't find parent dir's vcache\n");
626 if (tdp->ddirty_flags & VDisconCreate) {
627 /* If the parent dir has been created locally, defer
628 * this vnode for later by moving it to the end.
630 afs_DDirtyVCList->ddirty_next = avc;
631 afs_DDirtyVCList = avc;
632 printf("afs_ProcessOpRemove: deferring this vcache\n");
638 InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
639 InStatus.ClientModTime = avc->m.Date;
640 InStatus.Owner = avc->m.Owner;
641 InStatus.Group = (afs_int32) acred->cr_gid;
642 /* Only care about protection bits. */
643 InStatus.UnixModeBits = avc->m.Mode & 0xffff;
645 /* Connect to server. */
646 if (vType(avc) == VREG) {
647 /* Make file on server. */
649 tc = afs_Conn(&tdp->fid, areq, SHARED_LOCK);
651 /* Remember for callback processing. */
652 hostp = tc->srvr->server;
654 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE);
656 code = RXAFS_CreateFile(tc->id,
657 (struct AFSFid *)&tdp->fid.Fid,
660 (struct AFSFid *) &newFid.Fid,
667 CallBack.ExpirationTime += now;
670 } while (afs_Analyze(tc,
674 AFS_STATS_FS_RPCIDX_CREATEFILE,
678 } else if (vType(avc) == VDIR) {
679 /* Make dir on server. */
681 tc = afs_Conn(&tdp->fid, areq, SHARED_LOCK);
683 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR);
686 code = RXAFS_MakeDir(tc->id,
687 (struct AFSFid *) &tdp->fid.Fid,
690 (struct AFSFid *) &newFid.Fid,
697 CallBack.ExpirationTime += now;
698 /* DON'T forget to set the callback at some point. */
701 } while (afs_Analyze(tc,
705 AFS_STATS_FS_RPCIDX_MAKEDIR,
708 } /* Do server changes. */
710 /* TODO: Handle errors. */
712 printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code);
717 /* The rpc doesn't set the cell number. */
718 newFid.Cell = avc->fid.Cell;
721 * Change the fid in the dir entry.
724 /* Seek the dir's dcache. */
725 tdc = afs_FindDCacheByFid(&tdp->fid);
727 /* And now change the fid in the parent dir entry. */
728 afs_dir_ChangeFid(tdc, tname, &avc->fid.Fid.Vnode, &newFid.Fid.Vnode);
732 if (vType(avc) == VDIR) {
733 /* Change fid in the dir for the "." entry. ".." has alredy been
734 * handled by afs_FixChildrenFids when processing the parent dir.
736 tdc = afs_FindDCacheByFid(&avc->fid);
738 afs_dir_ChangeFid(tdc, ".", &avc->fid.Fid.Vnode, &newFid.Fid.Vnode);
740 if (avc->m.LinkCount >= 2)
741 /* For non empty dirs, fix children's parentVnode and parentUnique
744 afs_FixChildrenFids(&avc->fid, &newFid);
750 /* Recompute hash chain positions for vnode and dcaches.
751 * Then change to the new FID.
754 /* The vcache goes first. */
755 ObtainWriteLock(&afs_xvcache, 735);
758 hash = VCHash(&avc->fid);
760 new_hash = VCHash(&newFid);
762 /* Remove hash from old position. */
763 /* XXX: not checking array element contents. It shouldn't be empty.
764 * If it oopses, then something else might be wrong.
766 if (afs_vhashT[hash] == avc) {
767 /* First in hash chain (might be the only one). */
768 afs_vhashT[hash] = avc->hnext;
770 /* More elements in hash chain. */
771 //for (tvc = afs_vhashT[hash]; tdp; tdp = tdp->hnext) {
772 for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
773 if (tvc->hnext == avc) {
774 tvc->hnext = avc->hnext;
778 } /* if (!afs_vhashT[i]->hnext) */
779 QRemove(&afs_vhashTV[hash]);
781 /* Insert hash in new position. */
782 avc->hnext = afs_vhashT[new_hash];
783 afs_vhashT[new_hash] = avc;
784 QAdd(&afs_vhashTV[new_hash], &avc->vhashq);
786 ReleaseWriteLock(&afs_xvcache);
788 /* Do the same thing for all dcaches. */
789 hash = DVHash(&avc->fid);
790 ObtainWriteLock(&afs_xdcache, 743);
791 for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
792 hash = afs_dvnextTbl[index];
793 tdc = afs_GetDSlot(index, NULL);
794 ReleaseReadLock(&tdc->tlock);
795 if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
796 if (!FidCmp(&tdc->f.fid, &avc->fid)) {
798 /* Safer but slower. */
799 afs_HashOutDCache(tdc, 0);
801 /* Put dcache in new positions in the dchash and dvhash. */
802 new_hash = DCHash(&newFid, tdc->f.chunk);
803 afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
804 afs_dchashTbl[new_hash] = tdc->index;
806 new_hash = DVHash(&newFid);
807 afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
808 afs_dvhashTbl[new_hash] = tdc->index;
810 afs_indexUnique[tdc->index] = newFid.Fid.Unique;
811 memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
812 //afs_MaybeWakeupTruncateDaemon();
814 } /* if uniquifier match */
817 } /* for all dcaches in this hash bucket */
818 ReleaseWriteLock(&afs_xdcache);
820 /* Now we can set the new fid. */
821 memcpy(&avc->fid, &newFid, sizeof(struct VenusFid));
824 /* Unset parent dir CStat flag, so it will get refreshed on next
827 ObtainWriteLock(&tdp->lock, 745);
828 tdp->states &= ~CStatd;
829 ReleaseWriteLock(&tdp->lock);
834 afs_osi_Free(tname, AFSNAMEMAX);
839 * Remove a vnode on the server, be it file or directory.
840 * Not much to do here only get the parent dir's fid and call the
843 * \param avc The deleted vcache
846 * \note The vcache refcount should be dropped because it points to
847 * a deleted vnode and has served it's purpouse, but we drop refcount
848 * on shadow dir deletio (we still need it for that).
850 * \note avc must be write locked.
852 int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
855 struct AFSFetchStatus OutDirStatus;
856 struct VenusFid pdir_fid;
857 struct AFSVolSync tsync;
859 struct vcache *tdp = NULL;
863 /* Get parent dir's FID. */
864 pdir_fid.Fid.Unique = 0;
865 afs_GetParentDirFid(avc, &pdir_fid);
866 if (!pdir_fid.Fid.Unique) {
867 printf("afs_ProcessOpRemove: Couldn't find parent dir's FID.\n");
871 tname = afs_osi_Alloc(AFSNAMEMAX);
873 printf("afs_ProcessOpRemove: Couldn't find file name\n");
878 code = afs_GetVnodeName(avc, &pdir_fid, tname, 1);
880 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.
889 afs_DDirtyVCList->ddirty_next = avc;
890 afs_DDirtyVCList = avc;
895 if (vType(avc) == VREG) {
896 /* Remove file on server. */
898 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
900 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
902 code = RXAFS_RemoveFile(tc->id,
912 } while (afs_Analyze(tc,
916 AFS_STATS_FS_RPCIDX_REMOVEFILE,
920 } else if (vType(avc) == VDIR) {
921 /* Remove dir on server. */
923 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
925 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
927 code = RXAFS_RemoveDir(tc->id,
936 } while (afs_Analyze(tc,
940 AFS_STATS_FS_RPCIDX_REMOVEDIR,
944 } /* if (vType(avc) == VREG) */
947 printf("afs_ProcessOpRemove: server returned code=%u\n", code);
949 /* Remove the statd flag from parent dir's vcache. */
950 ObtainSharedLock(&afs_xvcache, 761);
951 tdp = afs_FindVCache(&pdir_fid, 0, 1);
952 ReleaseSharedLock(&afs_xvcache);
954 ObtainWriteLock(&tdp->lock, 746);
955 tdp->states &= ~CStatd;
956 ReleaseWriteLock(&tdp->lock);
960 afs_osi_Free(tname, AFSNAMEMAX);
965 * Send disconnected file changes to the server.
967 * \note Call with vnode locked both locally and on the server.
969 * \param avc Vnode that gets synchronized to the server.
970 * \param areq Used for obtaining a conn struct.
972 * \return 0 for success. On failure, other error codes.
974 int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
977 struct AFSStoreStatus sstat;
978 struct AFSFetchStatus fstat;
979 struct AFSVolSync tsync;
984 /* Start multiplexing dirty operations from ddirty_flags field: */
985 if (avc->ddirty_flags & VDisconSetAttrMask) {
987 /* Turn dirty vc data into a new store status... */
988 if (afs_GenStoreStatus(avc, &sstat) > 0) {
990 tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
992 /* ... and send it. */
993 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS);
995 code = RXAFS_StoreStatus(tc->id,
996 (struct AFSFid *) &avc->fid.Fid,
1006 } while (afs_Analyze(tc,
1010 AFS_STATS_FS_RPCIDX_STORESTATUS,
1014 } /* if (afs_GenStoreStatus() > 0)*/
1015 } /* disconnected SETATTR */
1020 if (avc->ddirty_flags &
1024 | VDisconWriteOsiFlush)) {
1028 tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
1030 /* Set storing flags. XXX: A tad inefficient ... */
1031 if (avc->ddirty_flags & VDisconWriteClose)
1032 flags |= AFS_LASTSTORE;
1033 if (avc->ddirty_flags & VDisconWriteOsiFlush)
1034 flags |= (AFS_SYNC | AFS_LASTSTORE);
1035 if (avc->ddirty_flags & VDisconWriteFlush)
1038 /* Try to send store to server. */
1039 /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */
1040 code = afs_StoreAllSegments(avc, areq, flags);
1044 } while (afs_Analyze(tc,
1048 AFS_STATS_FS_RPCIDX_STOREDATA,
1052 } /* disconnected TRUNC | WRITE */
1058 * All files that have been dirty before disconnection are going to
1059 * be replayed back to the server.
1061 * \param areq Request from the user.
1062 * \param acred User credentials.
1064 * \return If all files synchronized succesfully, return 0, otherwise
1067 * \note For now, it's the request from the PDiscon pioctl.
1070 int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred)
1073 struct vcache *tvc, *tmp;
1074 struct AFSFetchStatus fstat;
1075 struct AFSCallBack callback;
1076 struct AFSVolSync tsync;
1077 struct vcache *shList, *shListStart;
1079 int sync_failed = 0;
1082 afs_int32 start = 0;
1084 //AFS_STATCNT(afs_ResyncDisconFiles);
1086 shList = shListStart = NULL;
1088 ObtainReadLock(&afs_DDirtyVCListLock);
1090 tvc = afs_DDirtyVCListStart;
1093 /* Get local write lock. */
1094 ObtainWriteLock(&tvc->lock, 704);
1097 if ((tvc->ddirty_flags & VDisconRemove) &&
1098 (tvc->ddirty_flags & VDisconCreate)) {
1099 /* Data created and deleted locally. The server doesn't
1100 * need to know about this, so we'll just skip this file
1101 * from the dirty list.
1105 } else if (tvc->ddirty_flags & VDisconRemove) {
1106 /* Delete the file on the server and just move on
1107 * to the next file. After all, it has been deleted
1108 * we can't replay any other operation it.
1110 code = afs_ProcessOpRemove(tvc, areq);
1111 if (code == ENOTEMPTY)
1115 } else if (tvc->ddirty_flags & VDisconCreate) {
1116 /* For newly created files, we don't need a server lock. */
1117 code = afs_ProcessOpCreate(tvc, areq, acred);
1118 if (code == ENOTEMPTY)
1124 /* Get server write lock. */
1126 tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
1128 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
1130 code = RXAFS_SetLock(tc->id,
1131 (struct AFSFid *)&tvc->fid.Fid,
1139 } while (afs_Analyze(tc,
1143 AFS_STATS_FS_RPCIDX_SETLOCK,
1152 if ((tvc->ddirty_flags & VDisconRename) &&
1153 !(tvc->ddirty_flags & VDisconCreate)) {
1154 /* Rename file only if it hasn't been created locally. */
1155 code = afs_ProcessOpRename(tvc, areq);
1160 /* Issue a FetchStatus to get info about DV and callbacks. */
1162 tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
1164 tvc->callback = tc->srvr->server;
1166 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
1168 code = RXAFS_FetchStatus(tc->id,
1169 (struct AFSFid *)&tvc->fid.Fid,
1178 } while (afs_Analyze(tc,
1182 AFS_STATS_FS_RPCIDX_FETCHSTATUS,
1188 goto unlock_srv_file;
1191 if ((dv_match(tvc, fstat) && (tvc->m.Date == fstat.ServerModTime)) ||
1192 (afs_ConflictPolicy == CLIENT_WINS) ||
1193 (tvc->ddirty_flags & VDisconCreate)) {
1195 * Send changes to the server if there's data version match, or
1196 * client wins policy has been selected or file has been created
1197 * but doesn't have it's the contents on to the server yet.
1200 * XXX: Checking server attr changes by timestamp might not the
1201 * most elegant solution, but it's the most viable one that we could find.
1203 afs_UpdateStatus(tvc, &tvc->fid, areq, &fstat, &callback, start);
1204 code = afs_SendChanges(tvc, areq);
1206 } else if (afs_ConflictPolicy == SERVER_WINS) {
1207 /* DV mismatch, apply collision resolution policy. */
1208 /* Dequeue whatever callback is on top (XXX: propably none). */
1209 ObtainWriteLock(&afs_xcbhash, 706);
1210 afs_DequeueCallback(tvc);
1211 tvc->callback = NULL;
1212 tvc->states &= ~(CStatd | CDirty | CUnique);
1213 ReleaseWriteLock(&afs_xcbhash);
1215 /* Save metadata. File length gets updated as well because we
1216 * just removed CDirty from the avc.
1218 //afs_ProcessFS(tvc, &fstat, areq);
1220 /* Discard this files chunks and remove from current dir. */
1221 afs_TryToSmush(tvc, acred, 1);
1222 osi_dnlc_purgedp(tvc);
1223 if (tvc->linkData && !(tvc->states & CCore)) {
1224 /* Take care of symlinks. */
1225 afs_osi_Free(tvc->linkData, strlen(tvc->linkData) + 1);
1226 tvc->linkData = NULL;
1229 /* Otherwise file content's won't be synchronized. */
1230 tvc->truncPos = AFS_NOTRUNC;
1233 printf("afs_ResyncDisconFiles: no resolution policy selected.\n");
1234 } /* if DV match or client wins policy */
1238 printf("Sync FAILED.\n");
1242 /* Release server write lock. */
1244 tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
1246 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
1248 code = RXAFS_ReleaseLock(tc->id,
1249 (struct AFSFid *) &tvc->fid.Fid,
1255 } while (afs_Analyze(tc,
1259 AFS_STATS_FS_RPCIDX_RELEASELOCK,
1264 /* Pop this dirty vc out. */
1266 tvc = tvc->ddirty_next;
1269 /* Vnode not deferred. Clean it up. */
1271 if (tmp->ddirty_flags == VDisconShadowed) {
1272 /* Dirs that have only the shadow flag set might still
1273 * be used so keep them in a different list, that gets
1274 * deleted after resync is done.
1277 shListStart = shList = tmp;
1279 shList->ddirty_next = tmp;
1282 } else if (tmp->ddirty_flags & VDisconShadowed)
1283 /* We can discard the shadow dir now. */
1284 afs_DeleteShadowDir(tmp);
1286 if (tmp->ddirty_flags & VDisconRemove)
1287 /* Drop the refcount on the deleted vnodes,
1288 * because we don't need them anymore.
1292 /* Only if sync was successfull,
1293 * clear flags and dirty references.
1295 tmp->ddirty_next = NULL;
1296 tmp->ddirty_flags = 0;
1300 tmp->ddirty_next = NULL;
1302 } /* if (!defered) */
1304 /* Release local write lock. */
1305 ReleaseWriteLock(&tmp->lock);
1308 /* Delete the rest of shadow dirs. */
1311 ObtainWriteLock(&tvc->lock, 764);
1313 afs_DeleteShadowDir(tvc);
1318 tvc = tvc->ddirty_next;
1319 tmp->ddirty_next = NULL;
1321 ReleaseWriteLock(&tmp->lock);
1324 if (ret_code == 0) {
1325 /* NULLIFY dirty list only if resync complete. */
1326 afs_DDirtyVCListStart = NULL;
1327 afs_DDirtyVCList = NULL;
1329 ReleaseReadLock(&afs_DDirtyVCListLock);
1335 * Print list of disconnected files.
1337 * \note Call with afs_DDirtyVCListLock read locked.
1339 void afs_DbgDisconFiles()
1344 tvc = afs_DDirtyVCListStart;
1345 printf("List of dirty files: \n");
1347 printf("Cell=%u Volume=%u VNode=%u Unique=%u\n",
1349 tvc->fid.Fid.Volume,
1351 tvc->fid.Fid.Unique);
1352 tvc = tvc->ddirty_next;
1355 osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
1360 * Generate a fake fid for a disconnected shadow dir.
1361 * Similar to afs_GenFakeFid, only that it uses the dhash
1362 * to search for a uniquifier because a shadow dir lives only
1367 * \note Don't forget to fill in afid with Cell and Volume.
1369 void afs_GenShadowFid(struct VenusFid *afid)
1371 afs_uint32 i, index, max_unique = 1;
1372 struct vcache *tvc = NULL;
1374 /* Try generating a fid that isn't used in the vhash. */
1376 afid->Fid.Vnode = afs_DisconVnode + 1;
1379 ObtainWriteLock(&afs_xdcache, 737);
1380 for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
1381 i = afs_dvnextTbl[index];
1382 if (afs_indexUnique[index] > max_unique)
1383 max_unique = afs_indexUnique[index];
1386 ReleaseWriteLock(&afs_xdcache);
1387 afid->Fid.Unique = max_unique + 1;
1388 afs_DisconVnode += 2;
1389 if (!afs_DisconVnode)
1390 afs_DisconVnode = 2;
1392 /* Is this a used vnode? */
1393 ObtainSharedLock(&afs_xvcache, 762);
1394 tvc = afs_FindVCache(afid, 0, 1);
1395 ReleaseSharedLock(&afs_xvcache);
1402 * Generate a fake fid (vnode and uniquifier) for a vcache
1403 * (either dir or normal file). The vnode is generated via
1404 * afs_DisconVNode and the uniquifier by getting the highest
1405 * uniquifier on a hash chain and incrementing it by one.
1407 * \param avc afid The fid structre that will be filled.
1408 * \param avtype Vnode type: VDIR/VREG.
1410 * \note The cell number must be completed somewhere else.
1412 void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype)
1415 afs_uint32 max_unique = 1, i;
1418 afid->Fid.Vnode = afs_DisconVnode + 1;
1419 else if (avtype == VREG)
1420 afid->Fid.Vnode = afs_DisconVnode;
1422 ObtainWriteLock(&afs_xvcache, 736);
1424 for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
1425 if (tvc->fid.Fid.Unique > max_unique)
1426 max_unique = tvc->fid.Fid.Unique;
1428 ReleaseWriteLock(&afs_xvcache);
1430 afid->Fid.Unique = max_unique + 1;
1431 afs_DisconVnode += 2;
1432 if (!afs_DisconVnode)
1433 afs_DisconVnode = 2;
1437 * Fill in stats for a newly created file/directory.
1439 * \param adp The parent dir's vcache.
1440 * \param avc The created vnode.
1441 * \param afid The new fid.
1444 * \param file_type Specify if file or directory.
1446 * \note Call with avc write locked.
1448 void afs_GenDisconStatus(
1451 struct VenusFid *afid,
1452 struct vattr *attrs,
1453 struct vrequest *areq,
1456 memcpy(&avc->fid, afid, sizeof(struct VenusFid));
1457 avc->m.Mode = attrs->va_mode;
1459 * avc->m.Owner = attrs->va_uid;
1460 * But now we use the parent dir's ownership,
1461 * there's no other way to get a server owner id.
1462 * XXX: Does it really matter?
1464 avc->m.Group = adp->m.Group;
1465 avc->m.Owner = adp->m.Owner;
1466 hset64(avc->m.DataVersion, 0, 0);
1467 avc->m.Length = attrs->va_size;
1468 avc->m.Date = osi_Time();
1469 if (file_type == VREG) {
1470 vSetType(avc, VREG);
1471 avc->m.Mode |= S_IFREG;
1472 avc->m.LinkCount = 1;
1473 } else if (file_type == VDIR) {
1474 vSetType(avc, VDIR);
1475 avc->m.Mode |= S_IFDIR;
1476 avc->m.LinkCount = 2;
1479 avc->anyAccess = adp->anyAccess;
1480 afs_AddAxs(avc->Access, areq->uid, adp->Access->axess);
1482 avc->callback = NULL;
1483 avc->states |= CStatd;
1484 avc->states &= ~CBulkFetching;