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