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