2 * This software has been released under the terms of the IBM Public
3 * License. For details, see the LICENSE file in the top-level source
4 * directory or online at http://www.openafs.org/dl/license10.html
11 #include "afs/sysincludes.h"
12 #include "afsincludes.h"
13 #include "afs/afs_stats.h" /* statistics */
15 #include "afs/afs_cbqueue.h"
17 #define dv_match(vc, fstat) \
18 ((vc->f.m.DataVersion.low == fstat.DataVersion) && \
19 (vc->f.m.DataVersion.high == fstat.dataVersionHigh))
21 /*! Circular queue of dirty vcaches */
22 struct afs_q afs_disconDirty;
24 /*! Circular queue of vcaches with shadow directories */
25 struct afs_q afs_disconShadow;
27 /*! Locks both of these lists. Must be write locked for anything other than
29 afs_rwlock_t afs_disconDirtyLock;
31 extern afs_int32 *afs_dvhashTbl; /*Data cache hash table */
32 extern afs_int32 *afs_dchashTbl; /*Data cache hash table */
33 extern afs_int32 *afs_dvnextTbl; /*Dcache hash table links */
34 extern afs_int32 *afs_dcnextTbl; /*Dcache hash table links */
35 extern struct dcache **afs_indexTable; /*Pointers to dcache entries */
37 /*! Vnode number. On file creation, use the current value and increment it.
39 afs_uint32 afs_DisconVnode = 2;
41 /*! Conflict policy. */
49 afs_int32 afs_ConflictPolicy = SERVER_WINS;
51 static void afs_DisconDiscardAllShadows(int, afs_ucred_t *);
52 void afs_DbgListDirEntries(struct VenusFid *afid);
56 * Find the first dcache of a file that has the specified fid.
57 * Similar to afs_FindDCache, only that it takes a fid instead
58 * of a vcache and it can get the first dcache.
62 * \return The found dcache or NULL.
65 afs_FindDCacheByFid(struct VenusFid *afid)
68 struct dcache *tdc = NULL;
71 ObtainWriteLock(&afs_xdcache, 758);
72 for (index = afs_dvhashTbl[i]; index != NULLIDX;) {
73 if (afs_indexUnique[index] == afid->Fid.Unique) {
74 tdc = afs_GetValidDSlot(index);
79 ReleaseReadLock(&tdc->tlock);
80 if (!FidCmp(&tdc->f.fid, afid)) {
81 break; /* leaving refCount high for caller */
85 index = afs_dvnextTbl[index];
87 ReleaseWriteLock(&afs_xdcache);
95 * Generate a store status from a dirty vcache entry.
97 * \param avc Dirty vcache entry.
100 * \note The vnode must be share locked. It is called only on resync,
101 * where the vnode is write locked locally and and the server.
103 * \return Mask of operations.
106 afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
108 if (!avc || !astat || !avc->f.ddirty_flags)
111 /* Clean up store stat. */
112 memset(astat, 0, sizeof(struct AFSStoreStatus));
114 if (avc->f.ddirty_flags & VDisconSetTime) {
115 /* Update timestamp. */
116 astat->ClientModTime = avc->f.m.Date;
117 astat->Mask |= AFS_SETMODTIME;
120 if (avc->f.ddirty_flags & VDisconSetMode) {
121 /* Copy the mode bits. */
122 astat->UnixModeBits = avc->f.m.Mode;
123 astat->Mask |= AFS_SETMODE;
126 /* XXX: more to come... ?*/
132 * Hook for filtering the local dir fid by searching the "." entry.
134 * \param hdata The fid to be filled.
137 get_parent_dir_fid_hook(void *hdata, char *aname, afs_int32 vnode,
140 struct VenusFid *tfid = (struct VenusFid *) hdata;
142 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) {
143 tfid->Fid.Vnode = vnode;
144 tfid->Fid.Unique = unique;
152 * Get a the dir's fid by looking in the vcache for simple files and
153 * in the ".." entry for directories.
155 * \param avc The file's vhash entry.
156 * \param afid Put the fid here.
158 * \return 0 on success, -1 on failure
161 afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
165 afid->Cell = avc->f.fid.Cell;
166 afid->Fid.Volume = avc->f.fid.Fid.Volume;
168 switch (vType(avc)) {
171 /* Normal files have the dir fid embedded in the vcache. */
172 afid->Fid.Vnode = avc->f.parent.vnode;
173 afid->Fid.Unique = avc->f.parent.unique;
176 /* If dir or parent dir created locally*/
177 tdc = afs_FindDCacheByFid(&avc->f.fid);
179 afid->Fid.Unique = 0;
180 /* Lookup each entry for the fid. It should be the first. */
181 afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid);
183 if (afid->Fid.Unique == 0) {
198 struct VenusFid *fid;
204 * Hook that searches a certain fid's name.
206 * \param hdata NameAndFid structure containin a pointer to a fid
207 * and an allocate name. The name will be filled when hit.
210 get_vnode_name_hook(void *hdata, char *aname, afs_int32 vnode,
213 struct NameAndFid *nf = (struct NameAndFid *) hdata;
215 if ((nf->fid->Fid.Vnode == vnode) &&
216 (nf->fid->Fid.Unique == unique)) {
217 nf->name_len = strlen(aname);
218 memcpy(nf->name, aname, nf->name_len);
219 nf->name[nf->name_len] = 0;
228 * Try to get a vnode's name by comparing all parent dir's entries
229 * to the given fid. It can also return the dir's dcache.
231 * \param avc The file's vcache.
232 * \param afid The parent dir's fid.
233 * \param aname A preallocated string for the name.
234 * \param deleted Has this file been deleted? If yes, use the shadow
235 * dir for looking up the name.
238 afs_GetVnodeName(struct vcache *avc, struct VenusFid *afid, char *aname,
243 struct vcache *parent_vc;
244 struct NameAndFid tnf;
245 struct VenusFid parent_fid;
246 struct VenusFid shadow_fid;
248 /* List dir contents and get it's tdc. */
250 /* For deleted files, get the shadow dir's tdc: */
252 /* Get the parent dir's vcache that contains the shadow fid. */
253 parent_fid.Cell = avc->f.fid.Cell;
254 parent_fid.Fid.Volume = avc->f.fid.Fid.Volume;
255 if (avc->f.ddirty_flags & VDisconRename) {
256 /* For renames the old dir fid is needed. */
257 parent_fid.Fid.Vnode = avc->f.oldParent.vnode;
258 parent_fid.Fid.Unique = avc->f.oldParent.unique;
260 parent_fid.Fid.Vnode = afid->Fid.Vnode;
261 parent_fid.Fid.Unique = afid->Fid.Unique;
264 /* Get the parent dir's vcache that contains the shadow fid. */
265 ObtainSharedLock(&afs_xvcache, 755);
266 parent_vc = afs_FindVCache(&parent_fid, 0, 1);
267 ReleaseSharedLock(&afs_xvcache);
272 shadow_fid.Cell = parent_vc->f.fid.Cell;
273 shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume;
274 shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode;
275 shadow_fid.Fid.Unique = parent_vc->f.shadow.unique;
277 afs_PutVCache(parent_vc);
279 /* Get shadow dir's dcache. */
280 tdc = afs_FindDCacheByFid(&shadow_fid);
284 /* For normal files, look into the current dir's entry. */
285 tdc = afs_FindDCacheByFid(afid);
289 tnf.fid = &avc->f.fid;
292 afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
294 if (tnf.name_len == -1)
297 /* printf("Directory dcache not found!\n"); */
304 struct DirtyChildrenCount {
310 * Lookup dirty deleted vnodes in this dir.
313 chk_del_children_hook(void *hdata, char *aname, afs_int32 vnode,
316 struct VenusFid tfid;
317 struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
320 if ((aname[0] == '.') && !aname[1])
321 /* Skip processing this dir again.
322 * It would result in an endless loop.
326 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
327 /* Don't process parent dir. */
330 /* Get this file's vcache. */
331 tfid.Cell = v->vc->f.fid.Cell;
332 tfid.Fid.Volume = v->vc->f.fid.Fid.Volume;
333 tfid.Fid.Vnode = vnode;
334 tfid.Fid.Unique = unique;
336 ObtainSharedLock(&afs_xvcache, 757);
337 tvc = afs_FindVCache(&tfid, 0, 1);
338 ReleaseSharedLock(&afs_xvcache);
340 /* Count unfinished dirty children. */
342 ObtainReadLock(&tvc->lock);
343 if (tvc->f.ddirty_flags)
345 ReleaseReadLock(&tvc->lock);
354 * Check if entries have been deleted in a vnode's shadow
357 * \return Returns the number of dirty children.
359 * \note afs_DDirtyVCListLock must be write locked.
362 afs_CheckDeletedChildren(struct vcache *avc)
365 struct DirtyChildrenCount dcc;
366 struct VenusFid shadow_fid;
368 if (!avc->f.shadow.vnode)
372 shadow_fid.Cell = avc->f.fid.Cell;
373 shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume;
374 shadow_fid.Fid.Vnode = avc->f.shadow.vnode;
375 shadow_fid.Fid.Unique = avc->f.shadow.unique;
379 /* Get shadow dir's dcache. */
380 tdc = afs_FindDCacheByFid(&shadow_fid);
383 afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc);
391 * Changes a file's parent fid references.
394 fix_children_fids_hook(void *hdata, char *aname, afs_int32 vnode,
397 struct VenusFid tfid;
398 struct VenusFid *afid = (struct VenusFid *) hdata;
400 struct dcache *tdc = NULL;
402 if ((aname[0] == '.') && !aname[1])
405 if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
408 tfid.Cell = afid->Cell;
409 tfid.Fid.Volume = afid->Fid.Volume;
410 tfid.Fid.Vnode = vnode;
411 tfid.Fid.Unique = unique;
414 /* vnode's parity indicates that it's a file. */
416 /* Get the vcache. */
417 ObtainSharedLock(&afs_xvcache, 759);
418 tvc = afs_FindVCache(&tfid, 0, 1);
419 ReleaseSharedLock(&afs_xvcache);
421 /* Change the fields. */
423 tvc->f.parent.vnode = afid->Fid.Vnode;
424 tvc->f.parent.unique = afid->Fid.Unique;
429 /* It's a dir. Fix this dir's .. entry to contain the new fid. */
430 /* Seek the dir's dcache. */
431 tdc = afs_FindDCacheByFid(&tfid);
433 /* Change the .. entry fid. */
434 afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode);
437 } /* if (!(vnode % 2))*/
443 * Fixes the parentVnode and parentUnique fields of all
444 * files (not dirs) contained in the directory pointed by
445 * old_fid. This is useful on resync, when a locally created dir
446 * get's a new fid and all the children references must be updated
447 * to reflect the new fid.
449 * \note The dir's fid hasn't been changed yet, it is still referenced
452 * \param old_fid The current dir's fid.
453 * \param new_fid The new dir's fid.
456 afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
460 /* Get shadow dir's dcache. */
461 tdc = afs_FindDCacheByFid(old_fid);
462 /* Change the fids. */
464 afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid);
470 list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique)
472 /* printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique); */
477 afs_DbgListDirEntries(struct VenusFid *afid)
481 /* Get shadow dir's dcache. */
482 tdc = afs_FindDCacheByFid(afid);
484 afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL);
490 * Find the parent vcache for a given child
492 * \param avc The vcache whose parent is required
493 * \param afid Fid structure in which parent's fid should be stored
494 * \param aname An AFSNAMEMAX sized buffer to hold the parents name
495 * \param adp A pointer to a struct vcache* which will be set to the
498 * \return An error code. 0 indicates success, EAGAIN that the vnode should
499 * be deferred to later in the resync process
503 afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid,
504 char *aname, struct vcache **adp)
510 if (afs_GetParentDirFid(avc, afid)) {
511 /* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */
515 code = afs_GetVnodeName(avc, afid, aname, deleted);
517 /* printf("afs_GetParentVCache: Couldn't find file name\n"); */
521 ObtainSharedLock(&afs_xvcache, 766);
522 *adp = afs_FindVCache(afid, 0, 1);
523 ReleaseSharedLock(&afs_xvcache);
525 /* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */
530 if ((*adp)->f.ddirty_flags & VDisconCreate) {
531 /* printf("afs_GetParentVCache: deferring until parent exists\n"); */
546 * Handles file renaming on reconnection:
547 * - Get the old name from the old dir's shadow dir.
548 * - Get the new name from the current dir.
549 * - Old dir fid and new dir fid are collected along the way.
552 afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
554 struct VenusFid old_pdir_fid, new_pdir_fid;
555 char *old_name = NULL, *new_name = NULL;
556 struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
557 struct AFSVolSync tsync;
559 struct rx_connection *rxconn;
563 /* Get old dir vcache. */
564 old_pdir_fid.Cell = avc->f.fid.Cell;
565 old_pdir_fid.Fid.Volume = avc->f.fid.Fid.Volume;
566 old_pdir_fid.Fid.Vnode = avc->f.oldParent.vnode;
567 old_pdir_fid.Fid.Unique = avc->f.oldParent.unique;
570 old_name = afs_osi_Alloc(AFSNAMEMAX);
572 /* printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n"); */
575 code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1);
577 /* printf("afs_ProcessOpRename: Couldn't find old name.\n"); */
581 /* Alloc data first. */
582 new_name = afs_osi_Alloc(AFSNAMEMAX);
584 /* printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); */
589 if (avc->f.ddirty_flags & VDisconRenameSameDir) {
590 /* If we're in the same dir, don't do the lookups all over again,
591 * just copy fid and vcache from the old dir.
593 memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid));
595 /* Get parent dir's FID.*/
596 if (afs_GetParentDirFid(avc, &new_pdir_fid)) {
597 /* printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n"); */
603 /* And finally get the new name. */
604 code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0);
606 /* printf("afs_ProcessOpRename: Couldn't find new name.\n"); */
610 /* Send to data to server. */
612 tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK, &rxconn);
614 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
616 code = RXAFS_Rename(rxconn,
617 (struct AFSFid *)&old_pdir_fid.Fid,
619 (struct AFSFid *)&new_pdir_fid.Fid,
629 } while (afs_Analyze(tc,
634 AFS_STATS_FS_RPCIDX_RENAME,
638 /* if (code) printf("afs_ProcessOpRename: server code=%u\n", code); */
641 afs_osi_Free(new_name, AFSNAMEMAX);
643 afs_osi_Free(old_name, AFSNAMEMAX);
648 * Handles all the reconnection details:
649 * - Get all the details about the vnode: name, fid, and parent dir fid.
650 * - Send data to server.
652 * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
655 afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq,
658 char *tname = NULL, *ttargetName = NULL;
659 struct AFSStoreStatus InStatus;
660 struct AFSFetchStatus OutFidStatus, OutDirStatus;
661 struct VenusFid pdir_fid, newFid;
662 struct AFSCallBack CallBack;
663 struct AFSVolSync tsync;
664 struct vcache *tdp = NULL, *tvc = NULL;
665 struct dcache *tdc = NULL;
667 struct rx_connection *rxconn;
668 afs_int32 hash, new_hash, index;
673 tname = afs_osi_Alloc(AFSNAMEMAX);
676 memset(&InStatus, 0, sizeof(InStatus));
678 code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp);
682 /* This data may also be in linkData, but then we have to deal with
683 * the joy of terminating NULLs and . and file modes. So just get
684 * it from the dcache where it won't have been fiddled with.
686 if (vType(avc) == VLNK) {
689 struct osi_file *tfile;
691 tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0);
703 tlen++; /* space for NULL */
704 ttargetName = afs_osi_Alloc(tlen);
710 ObtainReadLock(&tdc->lock);
711 tfile = afs_CFileOpen(&tdc->f.inode);
713 code = afs_CFileRead(tfile, 0, ttargetName, tlen);
714 ttargetName[tlen-1] = '\0';
715 afs_CFileClose(tfile);
716 ReleaseReadLock(&tdc->lock);
721 InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
722 InStatus.ClientModTime = avc->f.m.Date;
723 InStatus.Owner = avc->f.m.Owner;
724 InStatus.Group = (afs_int32) afs_cr_gid(acred);
725 /* Only care about protection bits. */
726 InStatus.UnixModeBits = avc->f.m.Mode & 0xffff;
729 tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK, &rxconn);
731 switch (vType(avc)) {
733 /* Make file on server. */
734 op = AFS_STATS_FS_RPCIDX_CREATEFILE;
735 XSTATS_START_TIME(op);
737 code = RXAFS_CreateFile(rxconn,
738 (struct AFSFid *)&tdp->f.fid.Fid,
740 (struct AFSFid *) &newFid.Fid,
741 &OutFidStatus, &OutDirStatus,
747 /* Make dir on server. */
748 op = AFS_STATS_FS_RPCIDX_MAKEDIR;
749 XSTATS_START_TIME(op);
751 code = RXAFS_MakeDir(rxconn, (struct AFSFid *) &tdp->f.fid.Fid,
753 (struct AFSFid *) &newFid.Fid,
754 &OutFidStatus, &OutDirStatus,
760 /* Make symlink on server. */
761 op = AFS_STATS_FS_RPCIDX_SYMLINK;
762 XSTATS_START_TIME(op);
764 code = RXAFS_Symlink(rxconn,
765 (struct AFSFid *) &tdp->f.fid.Fid,
766 tname, ttargetName, &InStatus,
767 (struct AFSFid *) &newFid.Fid,
768 &OutFidStatus, &OutDirStatus, &tsync);
773 op = AFS_STATS_FS_RPCIDX_CREATEFILE;
779 } while (afs_Analyze(tc, rxconn, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL));
781 /* TODO: Handle errors. */
783 /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */
787 /* The rpc doesn't set the cell number. */
788 newFid.Cell = avc->f.fid.Cell;
791 * Change the fid in the dir entry.
794 /* Seek the dir's dcache. */
795 tdc = afs_FindDCacheByFid(&tdp->f.fid);
797 /* And now change the fid in the parent dir entry. */
798 afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode);
802 if (vType(avc) == VDIR) {
803 /* Change fid in the dir for the "." entry. ".." has alredy been
804 * handled by afs_FixChildrenFids when processing the parent dir.
806 tdc = afs_FindDCacheByFid(&avc->f.fid);
808 afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode,
811 if (avc->f.m.LinkCount >= 2)
812 /* For non empty dirs, fix children's parentVnode and
813 * parentUnique reference.
815 afs_FixChildrenFids(&avc->f.fid, &newFid);
821 /* Recompute hash chain positions for vnode and dcaches.
822 * Then change to the new FID.
825 /* The vcache goes first. */
826 ObtainWriteLock(&afs_xvcache, 735);
829 hash = VCHash(&avc->f.fid);
831 new_hash = VCHash(&newFid);
833 /* Remove hash from old position. */
834 /* XXX: not checking array element contents. It shouldn't be empty.
835 * If it oopses, then something else might be wrong.
837 if (afs_vhashT[hash] == avc) {
838 /* First in hash chain (might be the only one). */
839 afs_vhashT[hash] = avc->hnext;
841 /* More elements in hash chain. */
842 for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
843 if (tvc->hnext == avc) {
844 tvc->hnext = avc->hnext;
848 } /* if (!afs_vhashT[i]->hnext) */
849 QRemove(&avc->vhashq);
851 /* Insert hash in new position. */
852 avc->hnext = afs_vhashT[new_hash];
853 afs_vhashT[new_hash] = avc;
854 QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);
856 ReleaseWriteLock(&afs_xvcache);
858 /* Do the same thing for all dcaches. */
859 hash = DVHash(&avc->f.fid);
860 ObtainWriteLock(&afs_xdcache, 743);
861 for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
862 hash = afs_dvnextTbl[index];
863 tdc = afs_GetValidDSlot(index);
865 ReleaseWriteLock(&afs_xdcache);
869 ReleaseReadLock(&tdc->tlock);
870 if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
871 if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
873 /* Safer but slower. */
874 afs_HashOutDCache(tdc, 0);
876 /* Put dcache in new positions in the dchash and dvhash. */
877 new_hash = DCHash(&newFid, tdc->f.chunk);
878 afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
879 afs_dchashTbl[new_hash] = tdc->index;
881 new_hash = DVHash(&newFid);
882 afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
883 afs_dvhashTbl[new_hash] = tdc->index;
885 afs_indexUnique[tdc->index] = newFid.Fid.Unique;
886 memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
888 } /* if uniquifier match */
890 } /* for all dcaches in this hash bucket */
891 ReleaseWriteLock(&afs_xdcache);
893 /* Now we can set the new fid. */
894 memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid));
899 afs_osi_Free(tname, AFSNAMEMAX);
901 afs_osi_Free(ttargetName, tlen);
906 * Remove a vnode on the server, be it file or directory.
907 * Not much to do here only get the parent dir's fid and call the
910 * \param avc The deleted vcache
913 * \note The vcache refcount should be dropped because it points to
914 * a deleted vnode and has served it's purpouse, but we drop refcount
915 * on shadow dir deletio (we still need it for that).
917 * \note avc must be write locked.
920 afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
923 struct AFSFetchStatus OutDirStatus;
924 struct VenusFid pdir_fid;
925 struct AFSVolSync tsync;
927 struct rx_connection *rxconn;
928 struct vcache *tdp = NULL;
932 tname = afs_osi_Alloc(AFSNAMEMAX);
934 /* printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n"); */
938 code = afs_GetParentVCache(avc, 1, &pdir_fid, tname, &tdp);
942 if ((vType(avc) == VDIR) && (afs_CheckDeletedChildren(avc))) {
943 /* Deleted children of this dir remain unsynchronized.
950 if (vType(avc) == VREG || vType(avc) == VLNK) {
951 /* Remove file on server. */
953 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK, &rxconn);
955 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
957 code = RXAFS_RemoveFile(rxconn,
967 } while (afs_Analyze(tc,
972 AFS_STATS_FS_RPCIDX_REMOVEFILE,
976 } else if (vType(avc) == VDIR) {
977 /* Remove dir on server. */
979 tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK, &rxconn);
981 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
983 code = RXAFS_RemoveDir(rxconn,
992 } while (afs_Analyze(tc,
997 AFS_STATS_FS_RPCIDX_REMOVEDIR,
1001 } /* if (vType(avc) == VREG) */
1003 /* if (code) printf("afs_ProcessOpRemove: server returned code=%u\n", code); */
1006 afs_osi_Free(tname, AFSNAMEMAX);
1011 * Send disconnected file changes to the server.
1013 * \note Call with vnode locked both locally and on the server.
1015 * \param avc Vnode that gets synchronized to the server.
1016 * \param areq Used for obtaining a conn struct.
1018 * \return 0 for success. On failure, other error codes.
1021 afs_SendChanges(struct vcache *avc, struct vrequest *areq)
1023 struct afs_conn *tc;
1024 struct rx_connection *rxconn;
1025 struct AFSStoreStatus sstat;
1026 struct AFSFetchStatus fstat;
1027 struct AFSVolSync tsync;
1032 /* Start multiplexing dirty operations from ddirty_flags field: */
1033 if (avc->f.ddirty_flags & VDisconSetAttrMask) {
1035 /* Turn dirty vc data into a new store status... */
1036 if (afs_GenStoreStatus(avc, &sstat) > 0) {
1038 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
1040 /* ... and send it. */
1041 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS);
1043 code = RXAFS_StoreStatus(rxconn,
1044 (struct AFSFid *) &avc->f.fid.Fid,
1054 } while (afs_Analyze(tc,
1059 AFS_STATS_FS_RPCIDX_STORESTATUS,
1063 } /* if (afs_GenStoreStatus() > 0)*/
1064 } /* disconnected SETATTR */
1069 if (avc->f.ddirty_flags &
1073 | VDisconWriteOsiFlush)) {
1077 tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
1079 /* Set storing flags. XXX: A tad inefficient ... */
1080 if (avc->f.ddirty_flags & VDisconWriteClose)
1081 flags |= AFS_LASTSTORE;
1082 if (avc->f.ddirty_flags & VDisconWriteOsiFlush)
1083 flags |= (AFS_SYNC | AFS_LASTSTORE);
1084 if (avc->f.ddirty_flags & VDisconWriteFlush)
1087 /* Try to send store to server. */
1088 /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */
1089 code = afs_StoreAllSegments(avc, areq, flags);
1093 } while (afs_Analyze(tc,
1098 AFS_STATS_FS_RPCIDX_STOREDATA,
1102 } /* disconnected TRUNC | WRITE */
1108 * All files that have been dirty before disconnection are going to
1109 * be replayed back to the server.
1111 * \param areq Request from the user.
1112 * \param acred User credentials.
1114 * \return If all files synchronized succesfully, return 0, otherwise
1117 * \note For now, it's the request from the PDiscon pioctl.
1121 afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred)
1123 struct afs_conn *tc;
1124 struct rx_connection *rxconn;
1126 struct AFSFetchStatus fstat;
1127 struct AFSCallBack callback;
1128 struct AFSVolSync tsync;
1130 afs_int32 start = 0;
1132 /*AFS_STATCNT(afs_ResyncDisconFiles);*/
1134 ObtainWriteLock(&afs_disconDirtyLock, 707);
1136 while (!QEmpty(&afs_disconDirty)) {
1137 tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1139 /* Can't lock tvc whilst holding the discon dirty lock */
1140 ReleaseWriteLock(&afs_disconDirtyLock);
1142 /* Get local write lock. */
1143 ObtainWriteLock(&tvc->lock, 705);
1145 if (tvc->f.ddirty_flags & VDisconRemove) {
1146 /* Delete the file on the server and just move on
1147 * to the next file. After all, it has been deleted
1148 * we can't replay any other operation it.
1150 code = afs_ProcessOpRemove(tvc, areq);
1153 } else if (tvc->f.ddirty_flags & VDisconCreate) {
1154 /* For newly created files, we don't need a server lock. */
1155 code = afs_ProcessOpCreate(tvc, areq, acred);
1159 tvc->f.ddirty_flags &= ~VDisconCreate;
1160 tvc->f.ddirty_flags |= VDisconCreated;
1163 /* Get server write lock. */
1165 tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
1167 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
1169 code = RXAFS_SetLock(rxconn,
1170 (struct AFSFid *)&tvc->f.fid.Fid,
1178 } while (afs_Analyze(tc,
1183 AFS_STATS_FS_RPCIDX_SETLOCK,
1190 if (tvc->f.ddirty_flags & VDisconRename) {
1191 /* If we're renaming the file, do so now */
1192 code = afs_ProcessOpRename(tvc, areq);
1194 goto unlock_srv_file;
1197 /* Issue a FetchStatus to get info about DV and callbacks. */
1199 tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
1201 tvc->callback = tc->parent->srvr->server;
1203 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
1205 code = RXAFS_FetchStatus(rxconn,
1206 (struct AFSFid *)&tvc->f.fid.Fid,
1215 } while (afs_Analyze(tc,
1220 AFS_STATS_FS_RPCIDX_FETCHSTATUS,
1225 goto unlock_srv_file;
1228 if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) ||
1229 (afs_ConflictPolicy == CLIENT_WINS) ||
1230 (tvc->f.ddirty_flags & VDisconCreated)) {
1232 * Send changes to the server if there's data version match, or
1233 * client wins policy has been selected or file has been created
1234 * but doesn't have it's the contents on to the server yet.
1237 * XXX: Checking server attr changes by timestamp might not the
1238 * most elegant solution, but it's the most viable one that we could find.
1240 afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start);
1241 code = afs_SendChanges(tvc, areq);
1243 } else if (afs_ConflictPolicy == SERVER_WINS) {
1244 /* DV mismatch, apply collision resolution policy. */
1245 /* Discard this files chunks and remove from current dir. */
1246 afs_ResetVCache(tvc, acred, 0);
1247 tvc->f.truncPos = AFS_NOTRUNC;
1249 /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */
1250 } /* if DV match or client wins policy */
1253 /* Release server write lock. */
1256 tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
1258 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
1260 ucode = RXAFS_ReleaseLock(rxconn,
1261 (struct AFSFid *) &tvc->f.fid.Fid,
1267 } while (afs_Analyze(tc,
1272 AFS_STATS_FS_RPCIDX_RELEASELOCK,
1277 ObtainWriteLock(&afs_disconDirtyLock, 710);
1279 /* Replayed successfully - pull the vcache from the
1280 * disconnected list */
1281 tvc->f.ddirty_flags = 0;
1282 QRemove(&tvc->dirtyq);
1285 if (code == EAGAIN) {
1286 /* Operation was deferred. Pull it from the current place in
1287 * the list, and stick it at the end again */
1288 QRemove(&tvc->dirtyq);
1289 QAdd(&afs_disconDirty, &tvc->dirtyq);
1291 /* Failed - keep state as is, and let the user know we died */
1293 ReleaseWriteLock(&tvc->lock);
1298 /* Release local write lock. */
1299 ReleaseWriteLock(&tvc->lock);
1303 ReleaseWriteLock(&afs_disconDirtyLock);
1307 /* Dispose of all of the shadow directories */
1308 afs_DisconDiscardAllShadows(0, acred);
1310 ReleaseWriteLock(&afs_disconDirtyLock);
1315 * Discard all of our shadow directory copies. If squash is true, then
1316 * we also invalidate the vcache holding the shadow directory, to ensure
1317 * that any disconnected changes are deleted
1322 * \note afs_disconDirtyLock must be held on entry. It will be released
1327 afs_DisconDiscardAllShadows(int squash, afs_ucred_t *acred)
1331 while (!QEmpty(&afs_disconShadow)) {
1332 tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq);
1334 /* Must release the dirty lock to be able to get a vcache lock */
1335 ReleaseWriteLock(&afs_disconDirtyLock);
1336 ObtainWriteLock(&tvc->lock, 706);
1339 afs_ResetVCache(tvc, acred, 0);
1341 afs_DeleteShadowDir(tvc);
1343 ReleaseWriteLock(&tvc->lock);
1344 ObtainWriteLock(&afs_disconDirtyLock, 709);
1349 * This function throws away the whole disconnected state, allowing
1350 * the cache manager to reconnect to a server if we get into a state
1351 * where reconiliation is impossible.
1357 afs_DisconDiscardAll(afs_ucred_t *acred)
1361 ObtainWriteLock(&afs_disconDirtyLock, 717);
1362 while (!QEmpty(&afs_disconDirty)) {
1363 tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
1364 QRemove(&tvc->dirtyq);
1365 ReleaseWriteLock(&afs_disconDirtyLock);
1367 ObtainWriteLock(&tvc->lock, 718);
1368 afs_ResetVCache(tvc, acred, 0);
1369 tvc->f.truncPos = AFS_NOTRUNC;
1370 ReleaseWriteLock(&tvc->lock);
1371 ObtainWriteLock(&afs_disconDirtyLock, 719);
1375 afs_DisconDiscardAllShadows(1, acred);
1377 ReleaseWriteLock(&afs_disconDirtyLock);
1381 * Print list of disconnected files.
1383 * \note Call with afs_DDirtyVCListLock read locked.
1386 afs_DbgDisconFiles(void)
1392 afs_warn("List of dirty files: \n");
1394 ObtainReadLock(&afs_disconDirtyLock);
1395 for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) {
1396 tvc = QEntry(q, struct vcache, dirtyq);
1398 afs_warn("Cell=%u Volume=%u VNode=%u Unique=%u\n",
1400 tvc->f.fid.Fid.Volume,
1401 tvc->f.fid.Fid.Vnode,
1402 tvc->f.fid.Fid.Unique);
1406 osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
1408 ReleaseReadLock(&afs_disconDirtyLock);
1412 * Generate a fake fid for a disconnected shadow dir.
1413 * Similar to afs_GenFakeFid, only that it uses the dhash
1414 * to search for a uniquifier because a shadow dir lives only
1419 * \note Don't forget to fill in afid with Cell and Volume.
1422 afs_GenShadowFid(struct VenusFid *afid)
1424 afs_uint32 i, index, max_unique = 1;
1425 struct vcache *tvc = NULL;
1427 /* Try generating a fid that isn't used in the vhash. */
1429 /* Shadow Fids are always directories */
1430 afid->Fid.Vnode = afs_DisconVnode + 1;
1433 ObtainWriteLock(&afs_xdcache, 737);
1434 for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
1435 i = afs_dvnextTbl[index];
1436 if (afs_indexUnique[index] > max_unique)
1437 max_unique = afs_indexUnique[index];
1440 ReleaseWriteLock(&afs_xdcache);
1441 afid->Fid.Unique = max_unique + 1;
1442 afs_DisconVnode += 2;
1443 if (!afs_DisconVnode)
1444 afs_DisconVnode = 2;
1446 /* Is this a used vnode? */
1447 ObtainSharedLock(&afs_xvcache, 762);
1448 tvc = afs_FindVCache(afid, 0, 1);
1449 ReleaseSharedLock(&afs_xvcache);
1456 * Generate a fake fid (vnode and uniquifier) for a vcache
1457 * (either dir or normal file). The vnode is generated via
1458 * afs_DisconVNode and the uniquifier by getting the highest
1459 * uniquifier on a hash chain and incrementing it by one.
1461 * \param afid The fid structre that will be filled.
1462 * \param avtype Vnode type: VDIR/VREG.
1463 * \param lock True indicates that xvcache may be obtained,
1464 * False that it is already held
1466 * \note The cell number must be completed somewhere else.
1469 afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype, int lock)
1472 afs_uint32 max_unique = 0, i;
1476 afid->Fid.Vnode = afs_DisconVnode + 1;
1480 afid->Fid.Vnode = afs_DisconVnode;
1485 ObtainWriteLock(&afs_xvcache, 736);
1487 for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
1488 if (tvc->f.fid.Fid.Unique > max_unique)
1489 max_unique = tvc->f.fid.Fid.Unique;
1492 ReleaseWriteLock(&afs_xvcache);
1494 afid->Fid.Unique = max_unique + 1;
1495 afs_DisconVnode += 2;
1496 if (!afs_DisconVnode)
1497 afs_DisconVnode = 2;
1501 * Fill in stats for a newly created file/directory.
1503 * \param adp The parent dir's vcache.
1504 * \param avc The created vnode.
1505 * \param afid The new fid.
1508 * \param file_type Specify if file or directory.
1510 * \note Call with avc write locked.
1513 afs_GenDisconStatus(struct vcache *adp, struct vcache *avc,
1514 struct VenusFid *afid, struct vattr *attrs,
1515 struct vrequest *areq, int file_type)
1518 memcpy(&avc->f.fid, afid, sizeof(struct VenusFid));
1519 avc->f.m.Mode = attrs->va_mode;
1521 * avc->f.m.Owner = attrs->va_uid;
1522 * But now we use the parent dir's ownership,
1523 * there's no other way to get a server owner id.
1524 * XXX: Does it really matter?
1526 avc->f.m.Group = adp->f.m.Group;
1527 avc->f.m.Owner = adp->f.m.Owner;
1529 afs_SetDataVersion(avc, &zero);
1530 avc->f.m.Length = attrs->va_size;
1531 avc->f.m.Date = osi_Time();
1534 vSetType(avc, VREG);
1535 avc->f.m.Mode |= S_IFREG;
1536 avc->f.m.LinkCount = 1;
1537 avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
1538 avc->f.parent.unique = adp->f.fid.Fid.Unique;
1541 vSetType(avc, VDIR);
1542 avc->f.m.Mode |= S_IFDIR;
1543 avc->f.m.LinkCount = 2;
1546 vSetType(avc, VLNK);
1547 avc->f.m.Mode |= S_IFLNK;
1548 if ((avc->f.m.Mode & 0111) == 0)
1549 avc->mvstat = AFS_MVSTAT_MTPT;
1550 avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
1551 avc->f.parent.unique = adp->f.fid.Fid.Unique;
1556 avc->f.anyAccess = adp->f.anyAccess;
1557 afs_AddAxs(avc->Access, areq->uid, adp->Access->axess);
1559 avc->callback = NULL;
1560 avc->f.states |= CStatd;
1561 avc->f.states &= ~CBulkFetching;