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