death to register
[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
21 #include "afs/sysincludes.h"    /* Standard vendor system headers */
22 #include "afsincludes.h"        /* Afs-based standard headers */
23 #include "afs/afs_stats.h"      /* statistics */
24 #include "afs/afs_cbqueue.h"
25 #include "afs/nfsclient.h"
26 #include "afs/afs_osidnlc.h"
27
28 extern afs_rwlock_t afs_xcbhash;
29
30 /* Note that we don't set CDirty here, this is OK because the rename
31  * RPC is called synchronously. */
32
33 int
34 afsrename(struct vcache *aodp, char *aname1, struct vcache *andp,
35           char *aname2, afs_ucred_t *acred, struct vrequest *areq)
36 {
37     struct afs_conn *tc;
38     afs_int32 code = 0;
39     afs_int32 returnCode;
40     int oneDir, doLocally;
41     afs_size_t offset, len;
42     struct VenusFid unlinkFid, fileFid;
43     struct vcache *tvc;
44     struct dcache *tdc1, *tdc2;
45     struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
46     struct AFSVolSync tsync;
47     XSTATS_DECLS;
48     AFS_STATCNT(afs_rename);
49     afs_Trace4(afs_iclSetp, CM_TRACE_RENAME, ICL_TYPE_POINTER, aodp,
50                ICL_TYPE_STRING, aname1, ICL_TYPE_POINTER, andp,
51                ICL_TYPE_STRING, aname2);
52
53     if (strlen(aname1) > AFSNAMEMAX || strlen(aname2) > AFSNAMEMAX) {
54         code = ENAMETOOLONG;
55         goto done;
56     }
57
58     /* verify the latest versions of the stat cache entries */
59   tagain:
60     code = afs_VerifyVCache(aodp, areq);
61     if (code)
62         goto done;
63     code = afs_VerifyVCache(andp, areq);
64     if (code)
65         goto done;
66
67     /* lock in appropriate order, after some checks */
68     if (aodp->f.fid.Cell != andp->f.fid.Cell
69         || aodp->f.fid.Fid.Volume != andp->f.fid.Fid.Volume) {
70         code = EXDEV;
71         goto done;
72     }
73     oneDir = 0;
74     code = 0;
75     if (andp->f.fid.Fid.Vnode == aodp->f.fid.Fid.Vnode) {
76         if (!strcmp(aname1, aname2)) {
77             /* Same directory and same name; this is a noop and just return success
78              * to save cycles and follow posix standards */
79
80             code = 0;
81             goto done;
82         }
83         
84         if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
85             code = ENETDOWN;
86             goto done;
87         }
88         
89         ObtainWriteLock(&andp->lock, 147);
90         tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0);
91         if (!tdc1) {
92             code = ENOENT;
93         } else {
94             ObtainWriteLock(&tdc1->lock, 643);
95         }
96         tdc2 = tdc1;
97         oneDir = 1;             /* only one dude locked */
98     } else if ((andp->f.states & CRO) || (aodp->f.states & CRO)) {
99         code = EROFS;
100         goto done;
101     } else if (andp->f.fid.Fid.Vnode < aodp->f.fid.Fid.Vnode) {
102         ObtainWriteLock(&andp->lock, 148);      /* lock smaller one first */
103         ObtainWriteLock(&aodp->lock, 149);
104         tdc2 = afs_FindDCache(andp, (afs_size_t) 0);
105         if (tdc2)
106             ObtainWriteLock(&tdc2->lock, 644);
107         tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0);
108         if (tdc1)
109             ObtainWriteLock(&tdc1->lock, 645);
110         else
111             code = ENOENT;
112     } else {
113         ObtainWriteLock(&aodp->lock, 150);      /* lock smaller one first */
114         ObtainWriteLock(&andp->lock, 557);
115         tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0);
116         if (tdc1)
117             ObtainWriteLock(&tdc1->lock, 646);
118         else
119             code = ENOENT;
120         tdc2 = afs_FindDCache(andp, (afs_size_t) 0);
121         if (tdc2)
122             ObtainWriteLock(&tdc2->lock, 647);
123     }
124
125     osi_dnlc_remove(aodp, aname1, 0);
126     osi_dnlc_remove(andp, aname2, 0);
127
128     /*
129      * Make sure that the data in the cache is current. We may have
130      * received a callback while we were waiting for the write lock.
131      */
132     if (tdc1) {
133         if (!(aodp->f.states & CStatd)
134             || !hsame(aodp->f.m.DataVersion, tdc1->f.versionNo)) {
135
136             ReleaseWriteLock(&aodp->lock);
137             if (!oneDir) {
138                 if (tdc2) {
139                     ReleaseWriteLock(&tdc2->lock);
140                     afs_PutDCache(tdc2);
141                 }
142                 ReleaseWriteLock(&andp->lock);
143             }
144             ReleaseWriteLock(&tdc1->lock);
145             afs_PutDCache(tdc1);
146             goto tagain;
147         }
148     }
149
150     if (code == 0)
151         code = afs_dir_Lookup(tdc1, aname1, &fileFid.Fid);
152     if (code) {
153         if (tdc1) {
154             ReleaseWriteLock(&tdc1->lock);
155             afs_PutDCache(tdc1);
156         }
157         ReleaseWriteLock(&aodp->lock);
158         if (!oneDir) {
159             if (tdc2) {
160                 ReleaseWriteLock(&tdc2->lock);
161                 afs_PutDCache(tdc2);
162             }
163             ReleaseWriteLock(&andp->lock);
164         }
165         goto done;
166     }
167
168     if (!AFS_IS_DISCON_RW) {
169         /* Connected. */
170         do {
171             tc = afs_Conn(&aodp->f.fid, areq, SHARED_LOCK);
172             if (tc) {
173                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
174                 RX_AFS_GUNLOCK();
175                 code =
176                     RXAFS_Rename(tc->id,
177                                         (struct AFSFid *)&aodp->f.fid.Fid,
178                                         aname1,
179                                         (struct AFSFid *)&andp->f.fid.Fid,
180                                         aname2,
181                                         &OutOldDirStatus,
182                                         &OutNewDirStatus,
183                                         &tsync);
184                 RX_AFS_GLOCK();
185                 XSTATS_END_TIME;
186             } else
187                 code = -1;
188
189         } while (afs_Analyze
190              (tc, code, &andp->f.fid, areq, AFS_STATS_FS_RPCIDX_RENAME,
191               SHARED_LOCK, NULL));
192
193     } else {
194         /* Disconnected. */
195
196         /* Seek moved file vcache. */
197         fileFid.Cell = aodp->f.fid.Cell;
198         fileFid.Fid.Volume = aodp->f.fid.Fid.Volume;
199         ObtainSharedLock(&afs_xvcache, 754);
200         tvc = afs_FindVCache(&fileFid, 0 , 1);
201         ReleaseSharedLock(&afs_xvcache);
202
203         if (tvc) {
204             /* XXX - We're locking this vcache whilst holding dcaches. Ooops */
205             ObtainWriteLock(&tvc->lock, 750);
206             if (!(tvc->f.ddirty_flags & (VDisconRename|VDisconCreate))) {
207                 /* If the vnode was created locally, then we don't care
208                  * about recording the rename - we'll do it automatically
209                  * on replay. If we've already renamed, we've already stored
210                  * the required information about where we came from.
211                  */
212                 
213                 if (!aodp->f.shadow.vnode) {
214                     /* Make shadow copy of parent dir only. */
215                     afs_MakeShadowDir(aodp, tdc1);
216                 }
217
218                 /* Save old parent dir fid so it will be searchable
219                  * in the shadow dir.
220                  */
221                 tvc->f.oldParent.vnode = aodp->f.fid.Fid.Vnode;
222                 tvc->f.oldParent.unique = aodp->f.fid.Fid.Unique;
223
224                 afs_DisconAddDirty(tvc, 
225                                    VDisconRename 
226                                      | (oneDir ? VDisconRenameSameDir:0), 
227                                    1);
228             }
229
230             ReleaseWriteLock(&tvc->lock);
231             afs_PutVCache(tvc);
232         } else {
233             code = ENOENT;
234         }                       /* if (tvc) */
235     }                           /* if !(AFS_IS_DISCON_RW)*/
236     returnCode = code;          /* remember for later */
237
238     /* Now we try to do things locally.  This is really loathsome code. */
239     unlinkFid.Fid.Vnode = 0;
240     if (code == 0) {
241         /*  In any event, we don't really care if the data (tdc2) is not
242          * in the cache; if it isn't, we won't do the update locally.  */
243         /* see if version numbers increased properly */
244         doLocally = 1;
245         if (!AFS_IS_DISCON_RW) {
246             if (oneDir) {
247                 /* number increases by 1 for whole rename operation */
248                 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) {
249                     doLocally = 0;
250                 }
251             } else {
252                 /* two separate dirs, each increasing by 1 */
253                 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1))
254                     doLocally = 0;
255                 if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1))
256                     doLocally = 0;
257                 if (!doLocally) {
258                     if (tdc1) {
259                         ZapDCE(tdc1);
260                         DZap(tdc1);
261                     }
262                     if (tdc2) {
263                         ZapDCE(tdc2);
264                         DZap(tdc2);
265                     }
266                 }
267             }
268         }                       /* if (!AFS_IS_DISCON_RW) */
269
270         /* now really do the work */
271         if (doLocally) {
272             /* first lookup the fid of the dude we're moving */
273             code = afs_dir_Lookup(tdc1, aname1, &fileFid.Fid);
274             if (code == 0) {
275                 /* delete the source */
276                 code = afs_dir_Delete(tdc1, aname1);
277             }
278             /* first see if target is there */
279             if (code == 0
280                 && afs_dir_Lookup(tdc2, aname2,
281                                   &unlinkFid.Fid) == 0) {
282                 /* target already exists, and will be unlinked by server */
283                 code = afs_dir_Delete(tdc2, aname2);
284             }
285             if (code == 0) {
286                 ObtainWriteLock(&afs_xdcache, 292);
287                 code = afs_dir_Create(tdc2, aname2, &fileFid.Fid);
288                 ReleaseWriteLock(&afs_xdcache);
289             }
290             if (code != 0) {
291                 ZapDCE(tdc1);
292                 DZap(tdc1);
293                 if (!oneDir) {
294                     ZapDCE(tdc2);
295                     DZap(tdc2);
296                 }
297             }
298         }
299
300
301         /* update dir link counts */
302         if (AFS_IS_DISCON_RW) {
303             if (!oneDir) {
304                 aodp->f.m.LinkCount--;
305                 andp->f.m.LinkCount++;
306             }
307             /* If we're in the same directory, link count doesn't change */
308         } else {
309             aodp->f.m.LinkCount = OutOldDirStatus.LinkCount;
310             if (!oneDir)
311                 andp->f.m.LinkCount = OutNewDirStatus.LinkCount;
312         }
313
314     } else {                    /* operation failed (code != 0) */
315         if (code < 0) {
316             /* if failed, server might have done something anyway, and 
317              * assume that we know about it */
318             ObtainWriteLock(&afs_xcbhash, 498);
319             afs_DequeueCallback(aodp);
320             afs_DequeueCallback(andp);
321             andp->f.states &= ~CStatd;
322             aodp->f.states &= ~CStatd;
323             ReleaseWriteLock(&afs_xcbhash);
324             osi_dnlc_purgedp(andp);
325             osi_dnlc_purgedp(aodp);
326         }
327     }
328
329     /* release locks */
330     if (tdc1) {
331         ReleaseWriteLock(&tdc1->lock);
332         afs_PutDCache(tdc1);
333     }
334
335     if ((!oneDir) && tdc2) {
336         ReleaseWriteLock(&tdc2->lock);
337         afs_PutDCache(tdc2);
338     }
339
340     ReleaseWriteLock(&aodp->lock);
341
342     if (!oneDir) {
343         ReleaseWriteLock(&andp->lock);
344     }
345
346     if (returnCode) {
347         code = returnCode;
348         goto done;
349     }
350
351     /* now, some more details.  if unlinkFid.Fid.Vnode then we should decrement
352      * the link count on this file.  Note that if fileFid is a dir, then we don't
353      * have to invalidate its ".." entry, since its DataVersion # should have
354      * changed. However, interface is not good enough to tell us the
355      * *file*'s new DataVersion, so we're stuck.  Our hack: delete mark
356      * the data as having an "unknown" version (effectively discarding the ".."
357      * entry */
358     if (unlinkFid.Fid.Vnode) {
359
360         unlinkFid.Fid.Volume = aodp->f.fid.Fid.Volume;
361         unlinkFid.Cell = aodp->f.fid.Cell;
362         tvc = NULL;
363         if (!unlinkFid.Fid.Unique) {
364             tvc = afs_LookupVCache(&unlinkFid, areq, NULL, aodp, aname1);
365         }
366         if (!tvc)               /* lookup failed or wasn't called */
367             tvc = afs_GetVCache(&unlinkFid, areq, NULL, NULL);
368
369         if (tvc) {
370 #ifdef AFS_BOZONLOCK_ENV
371             afs_BozonLock(&tvc->pvnLock, tvc);  /* Since afs_TryToSmush will do a pvn_vptrunc */
372 #endif
373             ObtainWriteLock(&tvc->lock, 151);
374             tvc->f.m.LinkCount--;
375             tvc->f.states &= ~CUnique;  /* For the dfs xlator */
376             if (tvc->f.m.LinkCount == 0 && !osi_Active(tvc)) {
377                 /* if this was last guy (probably) discard from cache.
378                  * We have to be careful to not get rid of the stat
379                  * information, since otherwise operations will start
380                  * failing even if the file was still open (or
381                  * otherwise active), and the server no longer has the
382                  * info.  If the file still has valid links, we'll get
383                  * a break-callback msg from the server, so it doesn't
384                  * matter that we don't discard the status info */
385                 if (!AFS_NFSXLATORREQ(acred))
386                     afs_TryToSmush(tvc, acred, 0);
387             }
388             ReleaseWriteLock(&tvc->lock);
389 #ifdef AFS_BOZONLOCK_ENV
390             afs_BozonUnlock(&tvc->pvnLock, tvc);
391 #endif
392             afs_PutVCache(tvc);
393         }
394     }
395
396     /* now handle ".." invalidation */
397     if (!oneDir) {
398         fileFid.Fid.Volume = aodp->f.fid.Fid.Volume;
399         fileFid.Cell = aodp->f.fid.Cell;
400         if (!fileFid.Fid.Unique)
401             tvc = afs_LookupVCache(&fileFid, areq, NULL, andp, aname2);
402         else
403             tvc = afs_GetVCache(&fileFid, areq, NULL, (struct vcache *)0);
404         if (tvc && (vType(tvc) == VDIR)) {
405             ObtainWriteLock(&tvc->lock, 152);
406             tdc1 = afs_FindDCache(tvc, (afs_size_t) 0);
407             if (tdc1) {
408                 if (AFS_IS_DISCON_RW) {
409                     /* If disconnected, we need to fix (not discard) the "..".*/
410                     afs_dir_ChangeFid(tdc1,
411                         "..",
412                         &aodp->f.fid.Fid.Vnode,
413                         &andp->f.fid.Fid.Vnode);
414                 } else {
415                     ObtainWriteLock(&tdc1->lock, 648);
416                     ZapDCE(tdc1);       /* mark as unknown */
417                     DZap(tdc1);
418                     ReleaseWriteLock(&tdc1->lock);
419                     afs_PutDCache(tdc1);        /* put it back */
420                 }
421             }
422             osi_dnlc_remove(tvc, "..", 0);
423             ReleaseWriteLock(&tvc->lock);
424             afs_PutVCache(tvc);
425         } else if (AFS_IS_DISCON_RW && tvc && (vType(tvc) == VREG)) {
426             /* XXX - Should tvc not get locked here? */
427             tvc->f.parent.vnode = andp->f.fid.Fid.Vnode;
428             tvc->f.parent.unique = andp->f.fid.Fid.Unique;
429         } else if (tvc) {
430             /* True we shouldn't come here since tvc SHOULD be a dir, but we
431              * 'syntactically' need to unless  we change the 'if' above...
432              */
433             afs_PutVCache(tvc);
434         }
435     }
436     code = returnCode;
437   done:
438     return code;
439 }
440
441 int
442 #if defined(AFS_SGI_ENV)
443 afs_rename(OSI_VC_DECL(aodp), char *aname1, struct vcache *andp, char *aname2, struct pathname *npnp, afs_ucred_t *acred)
444 #else
445 afs_rename(OSI_VC_DECL(aodp), char *aname1, struct vcache *andp, char *aname2, afs_ucred_t *acred)
446 #endif
447 {
448     afs_int32 code;
449     struct afs_fakestat_state ofakestate;
450     struct afs_fakestat_state nfakestate;
451     struct vrequest treq;
452     OSI_VC_CONVERT(aodp);
453
454     code = afs_InitReq(&treq, acred);
455     if (code)
456         return code;
457     afs_InitFakeStat(&ofakestate);
458     afs_InitFakeStat(&nfakestate);
459
460     AFS_DISCON_LOCK();
461     
462     code = afs_EvalFakeStat(&aodp, &ofakestate, &treq);
463     if (code)
464         goto done;
465     code = afs_EvalFakeStat(&andp, &nfakestate, &treq);
466     if (code)
467         goto done;
468     code = afsrename(aodp, aname1, andp, aname2, acred, &treq);
469   done:
470     afs_PutFakeStat(&ofakestate);
471     afs_PutFakeStat(&nfakestate);
472
473     AFS_DISCON_UNLOCK();
474     
475     code = afs_CheckCode(code, &treq, 25);
476     return code;
477 }