2 * Copyright 2000, International Business Machines Corporation and others.
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
17 #include "../afs/param.h" /* Should be always first */
18 #include "../afs/sysincludes.h" /* Standard vendor system headers */
19 #include "../afs/afsincludes.h" /* Afs-based standard headers */
20 #include "../afs/afs_stats.h" /* statistics */
21 #include "../afs/afs_cbqueue.h"
22 #include "../afs/nfsclient.h"
23 #include "../afs/afs_osidnlc.h"
25 extern afs_rwlock_t afs_xcbhash;
27 /* Note that we don't set CDirty here, this is OK because the rename
28 * RPC is called synchronously. */
30 afsrename(aodp, aname1, andp, aname2, acred)
31 register struct vcache *aodp, *andp;
32 char *aname1, *aname2;
33 struct AFS_UCRED *acred; {
35 register struct conn *tc;
36 register afs_int32 code;
38 int oneDir, doLocally;
39 afs_int32 offset, len;
40 struct VenusFid unlinkFid, fileFid;
42 struct dcache *tdc1, *tdc2;
43 struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
44 struct AFSVolSync tsync;
47 AFS_STATCNT(afs_rename);
48 afs_Trace4(afs_iclSetp, CM_TRACE_RENAME, ICL_TYPE_POINTER, aodp,
49 ICL_TYPE_STRING, aname1, ICL_TYPE_POINTER, andp,
50 ICL_TYPE_STRING, aname2);
52 if (code = afs_InitReq(&treq, acred)) return code;
54 /* verify the latest versions of the stat cache entries */
56 code = afs_VerifyVCache(aodp, &treq);
58 code = afs_VerifyVCache(andp, &treq);
61 /* lock in appropriate order, after some checks */
62 if (aodp->fid.Cell != andp->fid.Cell || aodp->fid.Fid.Volume != andp->fid.Fid.Volume) {
67 if (andp->fid.Fid.Vnode == aodp->fid.Fid.Vnode) {
68 if (!strcmp(aname1, aname2)) {
69 /* Same directory and same name; this is a noop and just return success
70 * to save cycles and follow posix standards */
75 ObtainWriteLock(&andp->lock,147);
76 oneDir = 1; /* only one dude locked */
78 else if ((andp->states & CRO) || (aodp->states & CRO)) {
82 else if (andp->fid.Fid.Vnode < aodp->fid.Fid.Vnode) {
83 ObtainWriteLock(&andp->lock,148); /* lock smaller one first */
84 ObtainWriteLock(&aodp->lock,149);
87 ObtainWriteLock(&aodp->lock,150); /* lock smaller one first */
88 ObtainWriteLock(&andp->lock,557);
91 osi_dnlc_remove (aodp, aname1, 0);
92 osi_dnlc_remove (andp, aname2, 0);
93 afs_symhint_inval(aodp);
94 afs_symhint_inval(andp);
96 /* before doing the rename, lookup the fileFid, just in case we
97 * don't make it down the path below that looks it up. We always need
98 * fileFid in order to handle ".." invalidation at the very end.
101 tdc1 = afs_GetDCache(aodp, 0, &treq, &offset, &len, 0);
106 * Make sure that the data in the cache is current. We may have
107 * received a callback while we were waiting for the write lock.
109 else if (!(aodp->states & CStatd)
110 || !hsame(aodp->m.DataVersion, tdc1->f.versionNo)) {
111 ReleaseWriteLock(&aodp->lock);
112 if (!oneDir) ReleaseWriteLock(&andp->lock);
118 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
120 if (tdc1) afs_PutDCache(tdc1);
121 ReleaseWriteLock(&aodp->lock);
122 if (!oneDir) ReleaseWriteLock(&andp->lock);
127 /* locks are now set, proceed to do the real work */
129 tc = afs_Conn(&aodp->fid, &treq, SHARED_LOCK);
131 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
132 #ifdef RX_ENABLE_LOCKS
134 #endif /* RX_ENABLE_LOCKS */
135 code = RXAFS_Rename(tc->id, (struct AFSFid *) &aodp->fid.Fid, aname1,
136 (struct AFSFid *) &andp->fid.Fid, aname2,
137 &OutOldDirStatus, &OutNewDirStatus, &tsync);
138 #ifdef RX_ENABLE_LOCKS
140 #endif /* RX_ENABLE_LOCKS */
145 (afs_Analyze(tc, code, &andp->fid, &treq,
146 AFS_STATS_FS_RPCIDX_RENAME, SHARED_LOCK, (struct cell *)0));
148 returnCode = code; /* remember for later */
150 /* Now we try to do things locally. This is really loathsome code. */
151 unlinkFid.Fid.Vnode = 0;
154 /* don't use GetDCache because we don't want to worry about what happens if
155 we have to stat the file (updating the stat block) before finishing
156 local hero stuff (which may put old (from rename) data version number
157 back in the cache entry).
158 In any event, we don't really care if the data is not
159 in the cache; if it isn't, we won't do the update locally. */
160 tdc1 = afs_FindDCache(aodp, 0);
161 if (!oneDir) tdc2 = afs_FindDCache(andp, 0);
163 /* see if version numbers increased properly */
166 /* number increases by 1 for whole rename operation */
167 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) {
172 /* two separate dirs, each increasing by 1 */
173 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1))
175 if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1))
180 DZap(&tdc1->f.inode);
184 DZap(&tdc2->f.inode);
188 /* now really do the work */
190 /* first lookup the fid of the dude we're moving */
191 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
193 /* delete the source */
194 code = afs_dir_Delete(&tdc1->f.inode, aname1);
196 /* first see if target is there */
198 afs_dir_Lookup(&tdc2->f.inode, aname2, &unlinkFid.Fid) == 0) {
199 /* target already exists, and will be unlinked by server */
200 code = afs_dir_Delete(&tdc2->f.inode, aname2);
203 code = afs_dir_Create(&tdc2->f.inode, aname2, &fileFid.Fid);
207 DZap(&tdc1->f.inode);
210 DZap(&tdc2->f.inode);
214 if (tdc1) afs_PutDCache(tdc1);
215 if ((!oneDir) && tdc2) afs_PutDCache(tdc2);
217 /* update dir link counts */
218 aodp->m.LinkCount = OutOldDirStatus.LinkCount;
220 andp->m.LinkCount = OutNewDirStatus.LinkCount;
223 else { /* operation failed (code != 0) */
225 /* if failed, server might have done something anyway, and
226 * assume that we know about it */
227 ObtainWriteLock(&afs_xcbhash, 498);
228 afs_DequeueCallback(aodp);
229 afs_DequeueCallback(andp);
230 andp->states &= ~CStatd;
231 aodp->states &= ~CStatd;
232 ReleaseWriteLock(&afs_xcbhash);
233 osi_dnlc_purgedp(andp);
234 osi_dnlc_purgedp(aodp);
239 ReleaseWriteLock(&aodp->lock);
240 if (!oneDir) ReleaseWriteLock(&andp->lock);
247 /* now, some more details. if unlinkFid.Fid.Vnode then we should decrement
248 the link count on this file. Note that if fileFid is a dir, then we don't
249 have to invalidate its ".." entry, since its DataVersion # should have
250 changed. However, interface is not good enough to tell us the
251 *file*'s new DataVersion, so we're stuck. Our hack: delete mark
252 the data as having an "unknown" version (effectively discarding the ".."
254 if (unlinkFid.Fid.Vnode) {
255 unlinkFid.Fid.Volume = aodp->fid.Fid.Volume;
256 unlinkFid.Cell = aodp->fid.Cell;
257 tvc = (struct vcache *)0;
258 if (!unlinkFid.Fid.Unique) {
259 tvc = afs_LookupVCache(&unlinkFid, &treq, (afs_int32 *)0, WRITE_LOCK,
262 if (!tvc) /* lookup failed or wasn't called */
263 tvc = afs_GetVCache(&unlinkFid, &treq, (afs_int32 *)0,
264 (struct vcache*)0, WRITE_LOCK);
267 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
268 afs_BozonLock(&tvc->pvnLock, tvc); /* Since afs_TryToSmush will do a pvn_vptrunc */
270 ObtainWriteLock(&tvc->lock,151);
272 tvc->states &= ~CUnique; /* For the dfs xlator */
273 if (tvc->m.LinkCount == 0 && !osi_Active(tvc)) {
274 /* if this was last guy (probably) discard from cache.
275 * We have to be careful to not get rid of the stat
276 * information, since otherwise operations will start
277 * failing even if the file was still open (or
278 * otherwise active), and the server no longer has the
279 * info. If the file still has valid links, we'll get
280 * a break-callback msg from the server, so it doesn't
281 * matter that we don't discard the status info */
282 if (!AFS_NFSXLATORREQ(acred)) afs_TryToSmush(tvc, acred, 0);
284 ReleaseWriteLock(&tvc->lock);
285 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
286 afs_BozonUnlock(&tvc->pvnLock, tvc);
288 afs_PutVCache(tvc, WRITE_LOCK);
292 /* now handle ".." invalidation */
294 fileFid.Fid.Volume = aodp->fid.Fid.Volume;
295 fileFid.Cell = aodp->fid.Cell;
296 if (!fileFid.Fid.Unique)
297 tvc = afs_LookupVCache(&fileFid, &treq, (afs_int32 *)0, WRITE_LOCK, andp, aname2);
299 tvc = afs_GetVCache(&fileFid, &treq, (afs_int32 *)0,
300 (struct vcache*)0, WRITE_LOCK);
301 if (tvc && (vType(tvc) == VDIR)) {
302 ObtainWriteLock(&tvc->lock,152);
303 tdc1 = afs_FindDCache(tvc, 0);
305 ZapDCE(tdc1); /* mark as unknown */
306 DZap(&tdc1->f.inode);
307 afs_PutDCache(tdc1); /* put it back */
309 osi_dnlc_remove(tvc, "..", 0);
310 ReleaseWriteLock(&tvc->lock);
311 afs_PutVCache(tvc, WRITE_LOCK);
313 /* True we shouldn't come here since tvc SHOULD be a dir, but we
314 * 'syntactically' need to unless we change the 'if' above...
316 afs_PutVCache(tvc, WRITE_LOCK);
321 code = afs_CheckCode(code, &treq, 25);
326 afs_rename(fndp, tndp)
327 struct nameidata *fndp, *tndp; {
328 register struct vcache *aodp = (struct vcache *)fndp->ni_dvp;
329 char *aname1 = fndp->ni_dent.d_name;
330 register struct vcache *andp = (struct vcache *)tndp->ni_dvp;
331 char *aname2 = tndp->ni_dent.d_name;
332 struct ucred *acred = tndp->ni_cred;
333 #else /* AFS_OSF_ENV */
334 #if defined(AFS_SGI_ENV)
335 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, npnp, acred)
336 struct pathname *npnp;
338 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, acred)
341 register struct vcache *andp;
342 char *aname1, *aname2;
343 struct AFS_UCRED *acred; {
345 register afs_int32 code;
348 code = afsrename(aodp, aname1, andp, aname2, acred);
350 AFS_RELE(tndp->ni_dvp);
351 if (tndp->ni_vp != NULL) {
352 AFS_RELE(tndp->ni_vp);
354 AFS_RELE(fndp->ni_dvp);
355 AFS_RELE(fndp->ni_vp);
356 #endif /* AFS_OSF_ENV */