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