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