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