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->f.m.DataVersion.low == fstat.DataVersion) && \
22 (vc->f.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 *);
56 void afs_DbgListDirEntries(struct VenusFid *afid);
60 * Find the first dcache of a file that has the specified fid.
61 * Similar to afs_FindDCache, only that it takes a fid instead
62 * of a vcache and it can get the first dcache.
66 * \return The found dcache or NULL.
68 struct dcache *afs_FindDCacheByFid(struct VenusFid *afid)
71 struct dcache *tdc = NULL;
74 ObtainWriteLock(&afs_xdcache, 758);
75 for (index = afs_dvhashTbl[i]; index != NULLIDX;) {
76 if (afs_indexUnique[index] == afid->Fid.Unique) {
77 tdc = afs_GetDSlot(index, NULL);
78 ReleaseReadLock(&tdc->tlock);
79 if (!FidCmp(&tdc->f.fid, afid)) {
80 break; /* leaving refCount high for caller */
84 index = afs_dvnextTbl[index];
86 ReleaseWriteLock(&afs_xdcache);
94 * Generate a store status from a dirty vcache entry.
96 * \param avc Dirty vcache entry.
99 * \note The vnode must be share locked. It is called only on resync,
100 * where the vnode is write locked locally and and the server.
102 * \return Mask of operations.
104 int afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
106 if (!avc || !astat || !avc->f.ddirty_flags)
109 /* Clean up store stat. */
110 memset(astat, 0, sizeof(struct AFSStoreStatus));
112 if (avc->f.ddirty_flags & VDisconSetTime) {
113 /* Update timestamp. */
114 astat->ClientModTime = avc->f.m.Date;
115 astat->Mask |= AFS_SETMODTIME;
118 if (avc->f.ddirty_flags & VDisconSetMode) {
119 /* Copy the mode bits. */
120 astat->UnixModeBits = avc->f.m.Mode;
121 astat->Mask |= AFS_SETMODE;
124 /* XXX: more to come... ?*/
130 * Hook for filtering the local dir fid by searching the "." entry.
132 * \param hdata The fid to be filled.
134 int get_parent_dir_fid_hook(void *hdata,
139 struct VenusFid *tfid = (struct VenusFid *) hdata;
141 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) {
142 tfid->Fid.Vnode = vnode;
143 tfid->Fid.Unique = unique;
151 * Get a the dir's fid by looking in the vcache for simple files and
152 * in the ".." entry for directories.
154 * \param avc The file's vhash entry.
155 * \param afid Put the fid here.
157 * \return 0 on success, -1 on failure
159 int afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
163 afid->Cell = avc->f.fid.Cell;
164 afid->Fid.Volume = avc->f.fid.Fid.Volume;
166 switch (vType(avc)) {
169 /* Normal files have the dir fid embedded in the vcache. */
170 afid->Fid.Vnode = avc->f.parent.vnode;
171 afid->Fid.Unique = avc->f.parent.unique;
174 /* If dir or parent dir created locally*/
175 tdc = afs_FindDCacheByFid(&avc->f.fid);
177 afid->Fid.Unique = 0;
178 /* Lookup each entry for the fid. It should be the first. */
179 afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid);
181 if (afid->Fid.Unique == 0) {
197 struct VenusFid *fid;
203 * Hook that searches a certain fid's name.
205 * \param hdata NameAndFid structure containin a pointer to a fid
206 * and an allocate name. The name will be filled when hit.
208 int get_vnode_name_hook(void *hdata,
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.
237 int afs_GetVnodeName(struct vcache *avc,
238 struct VenusFid *afid,
244 struct vcache *parent_vc;
245 struct NameAndFid tnf;
246 struct VenusFid parent_fid;
247 struct VenusFid shadow_fid;
249 /* List dir contents and get it's tdc. */
251 /* For deleted files, get the shadow dir's tdc: */
253 /* Get the parent dir's vcache that contains the shadow fid. */
254 parent_fid.Cell = avc->f.fid.Cell;
255 parent_fid.Fid.Volume = avc->f.fid.Fid.Volume;
256 if (avc->f.ddirty_flags & VDisconRename) {
257 /* For renames the old dir fid is needed. */
258 parent_fid.Fid.Vnode = avc->f.oldParent.vnode;
259 parent_fid.Fid.Unique = avc->f.oldParent.unique;
261 parent_fid.Fid.Vnode = afid->Fid.Vnode;
262 parent_fid.Fid.Unique = afid->Fid.Unique;
265 /* Get the parent dir's vcache that contains the shadow fid. */
266 ObtainSharedLock(&afs_xvcache, 755);
267 parent_vc = afs_FindVCache(&parent_fid, 0, 1);
268 ReleaseSharedLock(&afs_xvcache);
273 shadow_fid.Cell = parent_vc->f.fid.Cell;
274 shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume;
275 shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode;
276 shadow_fid.Fid.Unique = parent_vc->f.shadow.unique;
278 afs_PutVCache(parent_vc);
280 /* Get shadow dir's dcache. */
281 tdc = afs_FindDCacheByFid(&shadow_fid);
285 /* For normal files, look into the current dir's entry. */
286 tdc = afs_FindDCacheByFid(afid);
290 tnf.fid = &avc->f.fid;
293 afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
295 if (tnf.name_len == -1)
298 printf("Directory dcache not found!\n");
305 struct DirtyChildrenCount {
311 * Lookup dirty deleted vnodes in this dir.
313 int chk_del_children_hook(void *hdata,
318 struct VenusFid tfid;
319 struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
322 if ((aname[0] == '.') && !aname[1])
323 /* Skip processing this dir again.
324 * It would result in an endless loop.
328 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
329 /* Don't process parent dir. */
332 /* Get this file's vcache. */
333 tfid.Cell = v->vc->f.fid.Cell;
334 tfid.Fid.Volume = v->vc->f.fid.Fid.Volume;
335 tfid.Fid.Vnode = vnode;
336 tfid.Fid.Unique = unique;
338 ObtainSharedLock(&afs_xvcache, 757);
339 tvc = afs_FindVCache(&tfid, 0, 1);
340 ReleaseSharedLock(&afs_xvcache);
342 /* Count unfinished dirty children. */
344 ObtainReadLock(&tvc->lock);
345 if (tvc->f.ddirty_flags)
347 ReleaseReadLock(&tvc->lock);
356 * Check if entries have been deleted in a vnode's shadow
359 * \return Returns the number of dirty children.
361 * \note afs_DDirtyVCListLock must be write locked.
363 int afs_CheckDeletedChildren(struct vcache *avc)
366 struct DirtyChildrenCount dcc;
367 struct VenusFid shadow_fid;
369 if (!avc->f.shadow.vnode)
373 shadow_fid.Cell = avc->f.fid.Cell;
374 shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume;
375 shadow_fid.Fid.Vnode = avc->f.shadow.vnode;
376 shadow_fid.Fid.Unique = avc->f.shadow.unique;
380 /* Get shadow dir's dcache. */
381 tdc = afs_FindDCacheByFid(&shadow_fid);
384 afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc);
392 * Changes a file's parent fid references.
394 int fix_children_fids_hook(void *hdata,
399 struct VenusFid tfid;
400 struct VenusFid *afid = (struct VenusFid *) hdata;
402 struct dcache *tdc = NULL;
404 if ((aname[0] == '.') && !aname[1])
407 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
410 tfid.Cell = afid->Cell;
411 tfid.Fid.Volume = afid->Fid.Volume;
412 tfid.Fid.Vnode = vnode;
413 tfid.Fid.Unique = unique;
416 /* vnode's parity indicates that it's a file. */
418 /* Get the vcache. */
419 ObtainSharedLock(&afs_xvcache, 759);
420 tvc = afs_FindVCache(&tfid, 0, 1);
421 ReleaseSharedLock(&afs_xvcache);
423 /* Change the fields. */
425 tvc->f.parent.vnode = afid->Fid.Vnode;
426 tvc->f.parent.unique = afid->Fid.Unique;
431 /* It's a dir. Fix this dir's .. entry to contain the new fid. */
432 /* Seek the dir's dcache. */
433 tdc = afs_FindDCacheByFid(&tfid);
435 /* Change the .. entry fid. */
436 afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode);
439 } /* if (!(vnode % 2))*/
445 * Fixes the parentVnode and parentUnique fields of all
446 * files (not dirs) contained in the directory pointed by
447 * old_fid. This is useful on resync, when a locally created dir
448 * get's a new fid and all the children references must be updated
449 * to reflect the new fid.
451 * \note The dir's fid hasn't been changed yet, it is still referenced
454 * \param old_fid The current dir's fid.
455 * \param new_fid The new dir's fid.
457 void afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
461 /* Get shadow dir's dcache. */
462 tdc = afs_FindDCacheByFid(old_fid);
463 /* Change the fids. */
465 afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid);
470 int 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);
476 void afs_DbgListDirEntries(struct VenusFid *afid)
480 /* Get shadow dir's dcache. */
481 tdc = afs_FindDCacheByFid(afid);
483 afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL);
489 * Find the parent vcache for a given child
491 * \param avc The vcache whose parent is required
492 * \param afid Fid structure in which parent's fid should be stored
493 * \param aname An AFSNAMEMAX sized buffer to hold the parents name
494 * \param adp A pointer to a struct vcache* which will be set to the
497 * \return An error code. 0 indicates success, EAGAIN that the vnode should
498 * be deferred to later in the resync process
502 afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid,
503 char *aname, struct vcache **adp)
509 if (afs_GetParentDirFid(avc, afid)) {
510 printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n");
514 code = afs_GetVnodeName(avc, afid, aname, deleted);
516 printf("afs_GetParentVCache: Couldn't find file name\n");
520 ObtainSharedLock(&afs_xvcache, 766);
521 *adp = afs_FindVCache(afid, 0, 1);
522 ReleaseSharedLock(&afs_xvcache);
524 printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n");
529 if ((*adp)->f.ddirty_flags & VDisconCreate) {
530 printf("afs_GetParentVCache: deferring until parent exists\n");
543 * Handles file renaming on reconnection:
544 * - Get the old name from the old dir's shadow dir.
545 * - Get the new name from the current dir.
546 * - Old dir fid and new dir fid are collected along the way.
548 int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
550 struct VenusFid old_pdir_fid, new_pdir_fid;
551 char *old_name = NULL, *new_name = NULL;
552 struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
553 struct AFSVolSync tsync;
558 /* Get old dir vcache. */
559 old_pdir_fid.Cell = avc->f.fid.Cell;
560 old_pdir_fid.Fid.Volume = avc->f.fid.Fid.Volume;
561 old_pdir_fid.Fid.Vnode = avc->f.oldParent.vnode;
562 old_pdir_fid.Fid.Unique = avc->f.oldParent.unique;
565 old_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
567 printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n");
570 code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1);
572 printf("afs_ProcessOpRename: Couldn't find old name.\n");
576 /* Alloc data first. */
577 new_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
579 printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n");
584 if (avc->f.ddirty_flags & VDisconRenameSameDir) {
585 /* If we're in the same dir, don't do the lookups all over again,
586 * just copy fid and vcache from the old dir.
588 memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid));
590 /* Get parent dir's FID.*/
591 if (afs_GetParentDirFid(avc, &new_pdir_fid)) {
592 printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n");
598 /* And finally get the new name. */
599 code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0);
601 printf("afs_ProcessOpRename: Couldn't find new name.\n");
605 /* Send to data to server. */
607 tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK);
609 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
611 code = RXAFS_Rename(tc->id,
612 (struct AFSFid *)&old_pdir_fid.Fid,
614 (struct AFSFid *)&new_pdir_fid.Fid,
624 } while (afs_Analyze(tc,
628 AFS_STATS_FS_RPCIDX_RENAME,
633 printf("afs_ProcessOpRename: server code=%u\n", code);
636 afs_osi_Free(new_name, AFSNAMEMAX);
638 afs_osi_Free(old_name, AFSNAMEMAX);
643 * Handles all the reconnection details:
644 * - Get all the details about the vnode: name, fid, and parent dir fid.
645 * - Send data to server.
647 * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
649 int afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq,
650 struct AFS_UCRED *acred)
652 char *tname = NULL, *ttargetName = NULL;
653 struct AFSStoreStatus InStatus;
654 struct AFSFetchStatus OutFidStatus, OutDirStatus;
655 struct VenusFid pdir_fid, newFid;
656 struct AFSCallBack CallBack;
657 struct AFSVolSync tsync;
658 struct vcache *tdp = NULL, *tvc = NULL;
659 struct dcache *tdc = NULL;
661 afs_int32 hash, new_hash, index;
666 tname = afs_osi_Alloc(AFSNAMEMAX);
670 code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp);
674 /* This data may also be in linkData, but then we have to deal with
675 * the joy of terminating NULLs and . and file modes. So just get
676 * it from the dcache where it won't have been fiddled with.
678 if (vType(avc) == VLNK) {
681 struct osi_file *tfile;
683 tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0);
695 tlen++; /* space for NULL */
696 ttargetName = afs_osi_Alloc(tlen);
701 ObtainReadLock(&tdc->lock);
702 tfile = afs_CFileOpen(tdc->f.inode);
703 code = afs_CFileRead(tfile, 0, ttargetName, tlen);
704 ttargetName[tlen-1] = '\0';
705 afs_CFileClose(tfile);
706 ReleaseReadLock(&tdc->lock);
708 printf("Read target name as %s\n",ttargetName);
712 InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
713 InStatus.ClientModTime = avc->f.m.Date;
714 InStatus.Owner = avc->f.m.Owner;
715 InStatus.Group = (afs_int32) acred->cr_gid;
716 /* Only care about protection bits. */
717 InStatus.UnixModeBits = avc->f.m.Mode & 0xffff;
720 tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK);
722 switch (vType(avc)) {
724 /* Make file on server. */
725 op = AFS_STATS_FS_RPCIDX_CREATEFILE;
726 XSTATS_START_TIME(op);
728 code = RXAFS_CreateFile(tc->id,
729 (struct AFSFid *)&tdp->f.fid.Fid,
731 (struct AFSFid *) &newFid.Fid,
732 &OutFidStatus, &OutDirStatus,
738 /* Make dir on server. */
739 op = AFS_STATS_FS_RPCIDX_MAKEDIR;
740 XSTATS_START_TIME(op);
742 code = RXAFS_MakeDir(tc->id, (struct AFSFid *) &tdp->f.fid.Fid,
744 (struct AFSFid *) &newFid.Fid,
745 &OutFidStatus, &OutDirStatus,
751 /* Make symlink on server. */
752 op = AFS_STATS_FS_RPCIDX_SYMLINK;
753 XSTATS_START_TIME(op);
755 code = RXAFS_Symlink(tc->id,
756 (struct AFSFid *) &tdp->f.fid.Fid,
757 tname, ttargetName, &InStatus,
758 (struct AFSFid *) &newFid.Fid,
759 &OutFidStatus, &OutDirStatus, &tsync);
764 op = AFS_STATS_FS_RPCIDX_CREATEFILE;
770 } while (afs_Analyze(tc, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL));
772 /* TODO: Handle errors. */
774 printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code);
778 /* The rpc doesn't set the cell number. */
779 newFid.Cell = avc->f.fid.Cell;
782 * Change the fid in the dir entry.
785 /* Seek the dir's dcache. */
786 tdc = afs_FindDCacheByFid(&tdp->f.fid);
788 /* And now change the fid in the parent dir entry. */
789 afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode);
793 if (vType(avc) == VDIR) {
794 /* Change fid in the dir for the "." entry. ".." has alredy been
795 * handled by afs_FixChildrenFids when processing the parent dir.
797 tdc = afs_FindDCacheByFid(&avc->f.fid);
799 afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode,
802 if (avc->f.m.LinkCount >= 2)
803 /* For non empty dirs, fix children's parentVnode and
804 * parentUnique reference.
806 afs_FixChildrenFids(&avc->f.fid, &newFid);
812 /* Recompute hash chain positions for vnode and dcaches.
813 * Then change to the new FID.
816 /* The vcache goes first. */
817 ObtainWriteLock(&afs_xvcache, 735);
820 hash = VCHash(&avc->f.fid);
822 new_hash = VCHash(&newFid);
824 /* Remove hash from old position. */
825 /* XXX: not checking array element contents. It shouldn't be empty.
826 * If it oopses, then something else might be wrong.
828 if (afs_vhashT[hash] == avc) {
829 /* First in hash chain (might be the only one). */
830 afs_vhashT[hash] = avc->hnext;
832 /* More elements in hash chain. */
833 for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
834 if (tvc->hnext == avc) {
835 tvc->hnext = avc->hnext;
839 } /* if (!afs_vhashT[i]->hnext) */
840 QRemove(&avc->vhashq);
842 /* Insert hash in new position. */
843 avc->hnext = afs_vhashT[new_hash];
844 afs_vhashT[new_hash] = avc;
845 QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);
847 ReleaseWriteLock(&afs_xvcache);
849 /* Do the same thing for all dcaches. */
850 hash = DVHash(&avc->f.fid);
851 ObtainWriteLock(&afs_xdcache, 743);
852 for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
853 hash = afs_dvnextTbl[index];
854 tdc = afs_GetDSlot(index, NULL);
855 ReleaseReadLock(&tdc->tlock);
856 if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
857 if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
859 /* Safer but slower. */
860 afs_HashOutDCache(tdc, 0);
862 /* Put dcache in new positions in the dchash and dvhash. */
863 new_hash = DCHash(&newFid, tdc->f.chunk);
864 afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
865 afs_dchashTbl[new_hash] = tdc->index;
867 new_hash = DVHash(&newFid);
868 afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
869 afs_dvhashTbl[new_hash] = tdc->index;
871 afs_indexUnique[tdc->index] = newFid.Fid.Unique;
872 memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
874 } /* if uniquifier match */
877 } /* for all dcaches in this hash bucket */
878 ReleaseWriteLock(&afs_xdcache);
880 /* Now we can set the new fid. */
881 memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid));
886 afs_osi_Free(tname, AFSNAMEMAX);
888 afs_osi_Free(ttargetName, tlen);
893 * Remove a vnode on the server, be it file or directory.
894 * Not much to do here only get the parent dir's fid and call the
897 * \param avc The deleted vcache
900 * \note The vcache refcount should be dropped because it points to
901 * a deleted vnode and has served it's purpouse, but we drop refcount
902 * on shadow dir deletio (we still need it for that).
904 * \note avc must be write locked.
906 int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
909 struct AFSFetchStatus OutDirStatus;
910 struct VenusFid pdir_fid;
911 struct AFSVolSync tsync;
913 struct vcache *tdp = NULL;
917 tname = afs_osi_Alloc(AFSNAMEMAX);
919 printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n");
923 code = afs_GetParentVCache(avc, 1, &pdir_fid, tname, &tdp);
927 if ((vType(avc) == VDIR) && (afs_CheckDeletedChildren(avc))) {
928 /* Deleted children of this dir remain unsynchronized.
935 if (vType(avc) == VREG || vType(avc) == VLNK) {
936 /* Remove file on server. */
938 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
940 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
942 code = RXAFS_RemoveFile(tc->id,
952 } while (afs_Analyze(tc,
956 AFS_STATS_FS_RPCIDX_REMOVEFILE,
960 } else if (vType(avc) == VDIR) {
961 /* Remove dir on server. */
963 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
965 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
967 code = RXAFS_RemoveDir(tc->id,
976 } while (afs_Analyze(tc,
980 AFS_STATS_FS_RPCIDX_REMOVEDIR,
984 } /* if (vType(avc) == VREG) */
987 printf("afs_ProcessOpRemove: server returned code=%u\n", code);
990 afs_osi_Free(tname, AFSNAMEMAX);
995 * Send disconnected file changes to the server.
997 * \note Call with vnode locked both locally and on the server.
999 * \param avc Vnode that gets synchronized to the server.
1000 * \param areq Used for obtaining a conn struct.
1002 * \return 0 for success. On failure, other error codes.
1004 int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
1006 struct afs_conn *tc;
1007 struct AFSStoreStatus sstat;
1008 struct AFSFetchStatus fstat;
1009 struct AFSVolSync tsync;
1014 /* Start multiplexing dirty operations from ddirty_flags field: */
1015 if (avc->f.ddirty_flags & VDisconSetAttrMask) {
1017 /* Turn dirty vc data into a new store status... */
1018 if (afs_GenStoreStatus(avc, &sstat) > 0) {
1020 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
1022 /* ... and send it. */
1023 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS);
1025 code = RXAFS_StoreStatus(tc->id,
1026 (struct AFSFid *) &avc->f.fid.Fid,
1036 } while (afs_Analyze(tc,
1040 AFS_STATS_FS_RPCIDX_STORESTATUS,
1044 } /* if (afs_GenStoreStatus() > 0)*/
1045 } /* disconnected SETATTR */
1050 if (avc->f.ddirty_flags &
1054 | VDisconWriteOsiFlush)) {
1058 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
1060 /* Set storing flags. XXX: A tad inefficient ... */
1061 if (avc->f.ddirty_flags & VDisconWriteClose)
1062 flags |= AFS_LASTSTORE;
1063 if (avc->f.ddirty_flags & VDisconWriteOsiFlush)
1064 flags |= (AFS_SYNC | AFS_LASTSTORE);
1065 if (avc->f.ddirty_flags & VDisconWriteFlush)
1068 /* Try to send store to server. */
1069 /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */
1070 code = afs_StoreAllSegments(avc, areq, flags);
1074 } while (afs_Analyze(tc,
1078 AFS_STATS_FS_RPCIDX_STOREDATA,
1082 } /* disconnected TRUNC | WRITE */
1088 * All files that have been dirty before disconnection are going to
1089 * be replayed back to the server.
1091 * \param areq Request from the user.
1092 * \param acred User credentials.
1094 * \return If all files synchronized succesfully, return 0, otherwise
1097 * \note For now, it's the request from the PDiscon pioctl.
1100 int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred)
1102 struct afs_conn *tc;
1104 struct AFSFetchStatus fstat;
1105 struct AFSCallBack callback;
1106 struct AFSVolSync tsync;
1109 afs_int32 start = 0;
1111 //AFS_STATCNT(afs_ResyncDisconFiles);
1113 ObtainWriteLock(&afs_disconDirtyLock, 707);
1115 while (!QEmpty(&afs_disconDirty)) {
1116 tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1118 /* Can't lock tvc whilst holding the discon dirty lock */
1119 ReleaseWriteLock(&afs_disconDirtyLock);
1121 /* Get local write lock. */
1122 ObtainWriteLock(&tvc->lock, 705);
1124 if (tvc->f.ddirty_flags & VDisconRemove) {
1125 /* Delete the file on the server and just move on
1126 * to the next file. After all, it has been deleted
1127 * we can't replay any other operation it.
1129 code = afs_ProcessOpRemove(tvc, areq);
1132 } else if (tvc->f.ddirty_flags & VDisconCreate) {
1133 /* For newly created files, we don't need a server lock. */
1134 code = afs_ProcessOpCreate(tvc, areq, acred);
1138 tvc->f.ddirty_flags &= ~VDisconCreate;
1139 tvc->f.ddirty_flags |= VDisconCreated;
1142 /* Get server write lock. */
1144 tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
1146 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
1148 code = RXAFS_SetLock(tc->id,
1149 (struct AFSFid *)&tvc->f.fid.Fid,
1157 } while (afs_Analyze(tc,
1161 AFS_STATS_FS_RPCIDX_SETLOCK,
1168 if (tvc->f.ddirty_flags & VDisconRename) {
1169 /* If we're renaming the file, do so now */
1170 code = afs_ProcessOpRename(tvc, areq);
1172 goto unlock_srv_file;
1175 /* Issue a FetchStatus to get info about DV and callbacks. */
1177 tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
1179 tvc->callback = tc->srvr->server;
1181 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
1183 code = RXAFS_FetchStatus(tc->id,
1184 (struct AFSFid *)&tvc->f.fid.Fid,
1193 } while (afs_Analyze(tc,
1197 AFS_STATS_FS_RPCIDX_FETCHSTATUS,
1202 goto unlock_srv_file;
1205 if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) ||
1206 (afs_ConflictPolicy == CLIENT_WINS) ||
1207 (tvc->f.ddirty_flags & VDisconCreated)) {
1209 * Send changes to the server if there's data version match, or
1210 * client wins policy has been selected or file has been created
1211 * but doesn't have it's the contents on to the server yet.
1214 * XXX: Checking server attr changes by timestamp might not the
1215 * most elegant solution, but it's the most viable one that we could find.
1217 afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start);
1218 code = afs_SendChanges(tvc, areq);
1220 } else if (afs_ConflictPolicy == SERVER_WINS) {
1221 /* DV mismatch, apply collision resolution policy. */
1222 /* Discard this files chunks and remove from current dir. */
1223 afs_ResetVCache(tvc, acred);
1224 tvc->f.truncPos = AFS_NOTRUNC;
1226 printf("afs_ResyncDisconFiles: no resolution policy selected.\n");
1227 } /* if DV match or client wins policy */
1230 /* Release server write lock. */
1233 tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
1235 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
1237 ucode = RXAFS_ReleaseLock(tc->id,
1238 (struct AFSFid *) &tvc->f.fid.Fid,
1244 } while (afs_Analyze(tc,
1248 AFS_STATS_FS_RPCIDX_RELEASELOCK,
1253 ObtainWriteLock(&afs_disconDirtyLock, 710);
1255 /* Replayed successfully - pull the vcache from the
1256 * disconnected list */
1257 tvc->f.ddirty_flags = 0;
1258 QRemove(&tvc->dirtyq);
1261 if (code == EAGAIN) {
1262 /* Operation was deferred. Pull it from the current place in
1263 * the list, and stick it at the end again */
1264 QRemove(&tvc->dirtyq);
1265 QAdd(&afs_disconDirty, &tvc->dirtyq);
1267 /* Failed - keep state as is, and let the user know we died */
1269 ReleaseWriteLock(&tvc->lock);
1274 /* Release local write lock. */
1275 ReleaseWriteLock(&tvc->lock);
1279 ReleaseWriteLock(&afs_disconDirtyLock);
1283 /* Dispose of all of the shadow directories */
1284 afs_DisconDiscardAllShadows(0, acred);
1286 ReleaseWriteLock(&afs_disconDirtyLock);
1291 * Discard all of our shadow directory copies. If squash is true, then
1292 * we also invalidate the vcache holding the shadow directory, to ensure
1293 * that any disconnected changes are deleted
1298 * \note afs_disconDirtyLock must be held on entry. It will be released
1303 afs_DisconDiscardAllShadows(int squash, struct AFS_UCRED *acred) {
1306 while (!QEmpty(&afs_disconShadow)) {
1307 tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq);
1309 /* Must release the dirty lock to be able to get a vcache lock */
1310 ReleaseWriteLock(&afs_disconDirtyLock);
1311 ObtainWriteLock(&tvc->lock, 706);
1313 afs_DeleteShadowDir(tvc);
1314 tvc->f.shadow.vnode = 0;
1315 tvc->f.shadow.unique = 0;
1318 afs_ResetVCache(tvc, acred);
1320 ObtainWriteLock(&afs_disconDirtyLock, 709);
1321 QRemove(&tvc->shadowq);
1323 ReleaseWriteLock(&tvc->lock);
1328 * This function throws away the whole disconnected state, allowing
1329 * the cache manager to reconnect to a server if we get into a state
1330 * where reconiliation is impossible.
1336 afs_DisconDiscardAll(struct AFS_UCRED *acred) {
1339 ObtainWriteLock(&afs_disconDirtyLock, 717);
1340 while (!QEmpty(&afs_disconDirty)) {
1341 tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1342 ReleaseWriteLock(&afs_disconDirtyLock);
1344 ObtainWriteLock(&tvc->lock, 718);
1345 afs_ResetVCache(tvc, acred);
1346 tvc->f.truncPos = AFS_NOTRUNC;
1347 ReleaseWriteLock(&tvc->lock);
1349 ObtainWriteLock(&afs_disconDirtyLock, 719);
1352 afs_DisconDiscardAllShadows(1, acred);
1354 ReleaseWriteLock(&afs_disconDirtyLock);
1358 * Print list of disconnected files.
1360 * \note Call with afs_DDirtyVCListLock read locked.
1362 void afs_DbgDisconFiles()
1368 printf("List of dirty files: \n");
1370 ObtainReadLock(&afs_disconDirtyLock);
1371 for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) {
1372 tvc = QEntry(q, struct vcache, dirtyq);
1374 printf("Cell=%u Volume=%u VNode=%u Unique=%u\n",
1376 tvc->f.fid.Fid.Volume,
1377 tvc->f.fid.Fid.Vnode,
1378 tvc->f.fid.Fid.Unique);
1382 osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
1384 ReleaseReadLock(&afs_disconDirtyLock);
1388 * Generate a fake fid for a disconnected shadow dir.
1389 * Similar to afs_GenFakeFid, only that it uses the dhash
1390 * to search for a uniquifier because a shadow dir lives only
1395 * \note Don't forget to fill in afid with Cell and Volume.
1397 void afs_GenShadowFid(struct VenusFid *afid)
1399 afs_uint32 i, index, max_unique = 1;
1400 struct vcache *tvc = NULL;
1402 /* Try generating a fid that isn't used in the vhash. */
1404 /* Shadow Fids are always directories */
1405 afid->Fid.Vnode = afs_DisconVnode + 1;
1408 ObtainWriteLock(&afs_xdcache, 737);
1409 for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
1410 i = afs_dvnextTbl[index];
1411 if (afs_indexUnique[index] > max_unique)
1412 max_unique = afs_indexUnique[index];
1415 ReleaseWriteLock(&afs_xdcache);
1416 afid->Fid.Unique = max_unique + 1;
1417 afs_DisconVnode += 2;
1418 if (!afs_DisconVnode)
1419 afs_DisconVnode = 2;
1421 /* Is this a used vnode? */
1422 ObtainSharedLock(&afs_xvcache, 762);
1423 tvc = afs_FindVCache(afid, 0, 1);
1424 ReleaseSharedLock(&afs_xvcache);
1431 * Generate a fake fid (vnode and uniquifier) for a vcache
1432 * (either dir or normal file). The vnode is generated via
1433 * afs_DisconVNode and the uniquifier by getting the highest
1434 * uniquifier on a hash chain and incrementing it by one.
1436 * \param afid The fid structre that will be filled.
1437 * \param avtype Vnode type: VDIR/VREG.
1438 * \param lock True indicates that xvcache may be obtained,
1439 * False that it is already held
1441 * \note The cell number must be completed somewhere else.
1443 void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype, int lock)
1446 afs_uint32 max_unique = 0, i;
1450 afid->Fid.Vnode = afs_DisconVnode + 1;
1454 afid->Fid.Vnode = afs_DisconVnode;
1459 ObtainWriteLock(&afs_xvcache, 736);
1461 for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
1462 if (tvc->f.fid.Fid.Unique > max_unique)
1463 max_unique = tvc->f.fid.Fid.Unique;
1466 ReleaseWriteLock(&afs_xvcache);
1468 afid->Fid.Unique = max_unique + 1;
1469 afs_DisconVnode += 2;
1470 if (!afs_DisconVnode)
1471 afs_DisconVnode = 2;
1475 * Fill in stats for a newly created file/directory.
1477 * \param adp The parent dir's vcache.
1478 * \param avc The created vnode.
1479 * \param afid The new fid.
1482 * \param file_type Specify if file or directory.
1484 * \note Call with avc write locked.
1486 void afs_GenDisconStatus(struct vcache *adp, struct vcache *avc,
1487 struct VenusFid *afid, struct vattr *attrs,
1488 struct vrequest *areq, int file_type)
1490 memcpy(&avc->f.fid, afid, sizeof(struct VenusFid));
1491 avc->f.m.Mode = attrs->va_mode;
1493 * avc->f.m.Owner = attrs->va_uid;
1494 * But now we use the parent dir's ownership,
1495 * there's no other way to get a server owner id.
1496 * XXX: Does it really matter?
1498 avc->f.m.Group = adp->f.m.Group;
1499 avc->f.m.Owner = adp->f.m.Owner;
1500 hset64(avc->f.m.DataVersion, 0, 0);
1501 avc->f.m.Length = attrs->va_size;
1502 avc->f.m.Date = osi_Time();
1505 vSetType(avc, VREG);
1506 avc->f.m.Mode |= S_IFREG;
1507 avc->f.m.LinkCount = 1;
1508 avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
1509 avc->f.parent.unique = adp->f.fid.Fid.Unique;
1512 vSetType(avc, VDIR);
1513 avc->f.m.Mode |= S_IFDIR;
1514 avc->f.m.LinkCount = 2;
1517 vSetType(avc, VLNK);
1518 avc->f.m.Mode |= S_IFLNK;
1519 if ((avc->f.m.Mode & 0111) == 0)
1521 avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
1522 avc->f.parent.unique = adp->f.fid.Fid.Unique;
1527 avc->f.anyAccess = adp->f.anyAccess;
1528 afs_AddAxs(avc->Access, areq->uid, adp->Access->axess);
1530 avc->callback = NULL;
1531 avc->f.states |= CStatd;
1532 avc->f.states &= ~CBulkFetching;