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