a627b5021278584210d09df6983957b1410262d4
[openafs.git] / src / afs / VNOPS / afs_vnop_rename.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Implements:
12  * afsrename
13  * afs_rename
14  *
15  */
16
17 #include "../afs/param.h"       /* Should be always first */
18 #include "../afs/sysincludes.h" /* Standard vendor system headers */
19 #include "../afs/afsincludes.h" /* Afs-based standard headers */
20 #include "../afs/afs_stats.h" /* statistics */
21 #include "../afs/afs_cbqueue.h"
22 #include "../afs/nfsclient.h"
23 #include "../afs/afs_osidnlc.h"
24
25 extern afs_rwlock_t afs_xcbhash;
26
27 /* Note that we don't set CDirty here, this is OK because the rename
28  * RPC is called synchronously. */
29
30 afsrename(aodp, aname1, andp, aname2, acred)
31     register struct vcache *aodp, *andp;
32     char *aname1, *aname2;
33     struct AFS_UCRED *acred; {
34     struct vrequest treq;
35     register struct conn *tc;
36     register afs_int32 code;
37     afs_int32 returnCode;
38     int oneDir, doLocally;
39     afs_int32 offset, len;
40     struct VenusFid unlinkFid, fileFid;
41     struct vcache *tvc;
42     struct dcache *tdc1, *tdc2;
43     struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
44     struct AFSVolSync tsync;
45     XSTATS_DECLS
46
47     AFS_STATCNT(afs_rename);
48     afs_Trace4(afs_iclSetp, CM_TRACE_RENAME, ICL_TYPE_POINTER, aodp, 
49                ICL_TYPE_STRING, aname1, ICL_TYPE_POINTER, andp,
50                ICL_TYPE_STRING, aname2);
51
52     if (code = afs_InitReq(&treq, acred)) return code;
53
54     /* verify the latest versions of the stat cache entries */
55 tagain:
56     code = afs_VerifyVCache(aodp, &treq);
57     if (code) goto done;
58     code = afs_VerifyVCache(andp, &treq);
59     if (code) goto done;
60     
61     /* lock in appropriate order, after some checks */
62     if (aodp->fid.Cell != andp->fid.Cell || aodp->fid.Fid.Volume != andp->fid.Fid.Volume) {
63         code = EXDEV;
64         goto done;
65     }
66     oneDir = 0;
67     if (andp->fid.Fid.Vnode == aodp->fid.Fid.Vnode) {
68         if (!strcmp(aname1, aname2)) {
69             /* Same directory and same name; this is a noop and just return success
70              * to save cycles and follow posix standards */
71
72             code = 0;
73             goto done;
74         }
75         ObtainWriteLock(&andp->lock,147);
76         oneDir = 1;         /* only one dude locked */
77     }
78     else if ((andp->states & CRO) || (aodp->states & CRO)) {
79        code = EROFS;
80        goto done;
81      }
82     else if (andp->fid.Fid.Vnode < aodp->fid.Fid.Vnode) {
83         ObtainWriteLock(&andp->lock,148);       /* lock smaller one first */
84         ObtainWriteLock(&aodp->lock,149);
85     }
86     else {
87         ObtainWriteLock(&aodp->lock,150);       /* lock smaller one first */
88         ObtainWriteLock(&andp->lock,557);
89     }
90
91     osi_dnlc_remove (aodp, aname1, 0);
92     osi_dnlc_remove (andp, aname2, 0);
93     afs_symhint_inval(aodp); 
94     afs_symhint_inval(andp);
95
96     /* before doing the rename, lookup the fileFid, just in case we
97      * don't make it down the path below that looks it up.  We always need
98      * fileFid in order to handle ".." invalidation at the very end.
99      */
100     code = 0;
101     tdc1 = afs_GetDCache(aodp, 0, &treq, &offset, &len, 0);
102     if (!tdc1) {
103         code = ENOENT;
104     }
105     /*
106      * Make sure that the data in the cache is current. We may have
107      * received a callback while we were waiting for the write lock.
108      */
109     else if (!(aodp->states & CStatd)
110         || !hsame(aodp->m.DataVersion, tdc1->f.versionNo)) {
111         ReleaseWriteLock(&aodp->lock);
112         if (!oneDir) ReleaseWriteLock(&andp->lock);
113         afs_PutDCache(tdc1);
114         goto tagain;
115     }
116
117     if (code == 0)
118         code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
119     if (code) {
120         if (tdc1) afs_PutDCache(tdc1);
121         ReleaseWriteLock(&aodp->lock);
122         if (!oneDir) ReleaseWriteLock(&andp->lock);
123         goto done;
124     }
125     afs_PutDCache(tdc1);
126
127     /* locks are now set, proceed to do the real work */
128     do {
129         tc = afs_Conn(&aodp->fid, &treq, SHARED_LOCK);
130         if (tc) {
131           XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
132 #ifdef RX_ENABLE_LOCKS
133           AFS_GUNLOCK();
134 #endif /* RX_ENABLE_LOCKS */
135           code = RXAFS_Rename(tc->id, (struct AFSFid *) &aodp->fid.Fid, aname1,
136                                 (struct AFSFid *) &andp->fid.Fid, aname2,
137                                 &OutOldDirStatus, &OutNewDirStatus, &tsync);
138 #ifdef RX_ENABLE_LOCKS
139           AFS_GLOCK();
140 #endif /* RX_ENABLE_LOCKS */
141           XSTATS_END_TIME;
142         } else code = -1;
143
144     } while
145         (afs_Analyze(tc, code, &andp->fid, &treq,
146                      AFS_STATS_FS_RPCIDX_RENAME, SHARED_LOCK, (struct cell *)0));
147
148     returnCode = code;      /* remember for later */
149     
150     /* Now we try to do things locally.  This is really loathsome code. */
151     unlinkFid.Fid.Vnode = 0;
152     if (code == 0) {
153         tdc1 = tdc2 = 0;
154         /* don't use GetDCache because we don't want to worry about what happens if
155             we have to stat the file (updating the stat block) before finishing
156             local hero stuff (which may put old (from rename) data version number
157             back in the cache entry).
158             In any event, we don't really care if the data is not
159             in the cache; if it isn't, we won't do the update locally.  */
160         tdc1 = afs_FindDCache(aodp, 0);
161         if (!oneDir) tdc2 = afs_FindDCache(andp, 0);
162         else tdc2 = tdc1;
163         /* see if version numbers increased properly */
164         doLocally = 1;
165         if (oneDir) {
166             /* number increases by 1 for whole rename operation */
167             if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) {
168                 doLocally = 0;
169             }
170         }
171         else {
172             /* two separate dirs, each increasing by 1 */
173             if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1))
174                 doLocally = 0;
175             if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1))
176                 doLocally = 0;
177             if (!doLocally) {
178                 if (tdc1) {
179                     ZapDCE(tdc1);
180                     DZap(&tdc1->f.inode);
181                 }
182                 if (tdc2) {
183                     ZapDCE(tdc2);
184                     DZap(&tdc2->f.inode);
185                 }
186             }
187         }
188         /* now really do the work */
189         if (doLocally) {
190             /* first lookup the fid of the dude we're moving */
191             code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
192             if (code == 0) {
193                 /* delete the source */
194                 code = afs_dir_Delete(&tdc1->f.inode, aname1);
195             }
196             /* first see if target is there */
197             if (code == 0 &&
198                 afs_dir_Lookup(&tdc2->f.inode, aname2, &unlinkFid.Fid) == 0) {
199                 /* target already exists, and will be unlinked by server */
200                 code = afs_dir_Delete(&tdc2->f.inode, aname2);
201             }
202             if (code == 0) {
203                 code = afs_dir_Create(&tdc2->f.inode, aname2, &fileFid.Fid);
204             }
205             if (code != 0) {
206                 ZapDCE(tdc1);
207                 DZap(&tdc1->f.inode);
208                 if (!oneDir) {
209                     ZapDCE(tdc2);
210                     DZap(&tdc2->f.inode);
211                 }
212             }
213         }
214         if (tdc1) afs_PutDCache(tdc1);
215         if ((!oneDir) && tdc2) afs_PutDCache(tdc2);
216
217         /* update dir link counts */
218         aodp->m.LinkCount = OutOldDirStatus.LinkCount;
219         if (!oneDir)
220             andp->m.LinkCount = OutNewDirStatus.LinkCount;
221
222     }
223     else {      /* operation failed (code != 0) */
224         if (code < 0) { 
225             /* if failed, server might have done something anyway, and 
226              * assume that we know about it */
227           ObtainWriteLock(&afs_xcbhash, 498);
228           afs_DequeueCallback(aodp);
229           afs_DequeueCallback(andp);
230           andp->states &= ~CStatd;
231           aodp->states &= ~CStatd;
232           ReleaseWriteLock(&afs_xcbhash);
233           osi_dnlc_purgedp(andp);
234           osi_dnlc_purgedp(aodp);
235         }
236     }
237
238     /* release locks */
239     ReleaseWriteLock(&aodp->lock);
240     if (!oneDir) ReleaseWriteLock(&andp->lock);
241     
242     if (returnCode) {
243         code = returnCode;
244         goto done;
245     }
246
247     /* now, some more details.  if unlinkFid.Fid.Vnode then we should decrement
248         the link count on this file.  Note that if fileFid is a dir, then we don't
249         have to invalidate its ".." entry, since its DataVersion # should have
250         changed. However, interface is not good enough to tell us the
251         *file*'s new DataVersion, so we're stuck.  Our hack: delete mark
252         the data as having an "unknown" version (effectively discarding the ".."
253         entry */
254     if (unlinkFid.Fid.Vnode) {
255         unlinkFid.Fid.Volume = aodp->fid.Fid.Volume;
256         unlinkFid.Cell = aodp->fid.Cell;
257         tvc = (struct vcache *)0;
258         if (!unlinkFid.Fid.Unique) {
259             tvc = afs_LookupVCache(&unlinkFid, &treq, (afs_int32 *)0, WRITE_LOCK,
260                                    aodp, aname1);
261         }
262         if (!tvc) /* lookup failed or wasn't called */
263            tvc = afs_GetVCache(&unlinkFid, &treq, (afs_int32 *)0,
264                                (struct vcache*)0, WRITE_LOCK);
265
266         if (tvc) {
267 #if     defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
268             afs_BozonLock(&tvc->pvnLock, tvc);  /* Since afs_TryToSmush will do a pvn_vptrunc */
269 #endif
270             ObtainWriteLock(&tvc->lock,151);
271             tvc->m.LinkCount--;
272             tvc->states &= ~CUnique;            /* For the dfs xlator */
273             if (tvc->m.LinkCount == 0 && !osi_Active(tvc)) {
274                 /* if this was last guy (probably) discard from cache.
275                  * We have to be careful to not get rid of the stat
276                  * information, since otherwise operations will start
277                  * failing even if the file was still open (or
278                  * otherwise active), and the server no longer has the
279                  * info.  If the file still has valid links, we'll get
280                  * a break-callback msg from the server, so it doesn't
281                  * matter that we don't discard the status info */
282                 if (!AFS_NFSXLATORREQ(acred)) afs_TryToSmush(tvc, acred, 0);
283             }
284             ReleaseWriteLock(&tvc->lock);
285 #if     defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV)  || defined(AFS_SUN5_ENV)
286             afs_BozonUnlock(&tvc->pvnLock, tvc);
287 #endif
288             afs_PutVCache(tvc, WRITE_LOCK);
289         }
290     }
291
292     /* now handle ".." invalidation */
293     if (!oneDir) {
294         fileFid.Fid.Volume = aodp->fid.Fid.Volume;
295         fileFid.Cell = aodp->fid.Cell;
296         if (!fileFid.Fid.Unique)
297             tvc = afs_LookupVCache(&fileFid, &treq, (afs_int32 *)0, WRITE_LOCK, andp, aname2);
298         else
299             tvc = afs_GetVCache(&fileFid, &treq, (afs_int32 *)0,
300                                 (struct vcache*)0, WRITE_LOCK);
301         if (tvc && (vType(tvc) == VDIR)) {
302             ObtainWriteLock(&tvc->lock,152);
303             tdc1 = afs_FindDCache(tvc, 0);
304             if (tdc1) {
305                 ZapDCE(tdc1);   /* mark as unknown */
306                 DZap(&tdc1->f.inode);
307                 afs_PutDCache(tdc1);    /* put it back */
308             }
309             osi_dnlc_remove(tvc, "..", 0);
310             ReleaseWriteLock(&tvc->lock);
311             afs_PutVCache(tvc, WRITE_LOCK);
312         } else if (tvc) {
313             /* True we shouldn't come here since tvc SHOULD be a dir, but we
314              * 'syntactically' need to unless  we change the 'if' above...
315              */
316             afs_PutVCache(tvc, WRITE_LOCK);
317         }
318     }
319     code = returnCode;
320 done:
321     code = afs_CheckCode(code, &treq, 25);
322     return code;
323 }
324
325 #ifdef  AFS_OSF_ENV
326 afs_rename(fndp, tndp)
327     struct nameidata *fndp, *tndp; {
328     register struct vcache *aodp = (struct vcache *)fndp->ni_dvp;
329     char *aname1 = fndp->ni_dent.d_name;
330     register struct vcache *andp = (struct vcache *)tndp->ni_dvp;
331     char *aname2 = tndp->ni_dent.d_name;
332     struct ucred *acred = tndp->ni_cred;
333 #else   /* AFS_OSF_ENV */
334 #if defined(AFS_SGI_ENV)
335 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, npnp, acred)
336     struct pathname *npnp;
337 #else
338 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, acred)
339 #endif
340     OSI_VC_DECL(aodp);
341     register struct vcache *andp;
342     char *aname1, *aname2;
343     struct AFS_UCRED *acred; {
344 #endif
345     register afs_int32 code;
346     OSI_VC_CONVERT(aodp)
347
348     code = afsrename(aodp, aname1, andp, aname2, acred);
349 #ifdef  AFS_OSF_ENV
350     AFS_RELE(tndp->ni_dvp);
351     if (tndp->ni_vp != NULL) {
352         AFS_RELE(tndp->ni_vp);
353     }
354     AFS_RELE(fndp->ni_dvp);
355     AFS_RELE(fndp->ni_vp);
356 #endif  /* AFS_OSF_ENV */
357     return code;
358 }