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