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 <afsconfig.h>
18 #include "../afs/param.h"
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"
29 extern afs_rwlock_t afs_xcbhash;
31 /* Note that we don't set CDirty here, this is OK because the rename
32 * RPC is called synchronously. */
34 afsrename(aodp, aname1, andp, aname2, acred)
35 register struct vcache *aodp, *andp;
36 char *aname1, *aname2;
37 struct AFS_UCRED *acred; {
39 register struct conn *tc;
40 register afs_int32 code;
42 int oneDir, doLocally;
43 afs_size_t offset, len;
44 struct VenusFid unlinkFid, fileFid;
46 struct dcache *tdc1, *tdc2;
47 struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
48 struct AFSVolSync tsync;
51 AFS_STATCNT(afs_rename);
52 afs_Trace4(afs_iclSetp, CM_TRACE_RENAME, ICL_TYPE_POINTER, aodp,
53 ICL_TYPE_STRING, aname1, ICL_TYPE_POINTER, andp,
54 ICL_TYPE_STRING, aname2);
56 if (code = afs_InitReq(&treq, acred)) return code;
58 /* verify the latest versions of the stat cache entries */
60 code = afs_VerifyVCache(aodp, &treq);
62 code = afs_VerifyVCache(andp, &treq);
65 /* lock in appropriate order, after some checks */
66 if (aodp->fid.Cell != andp->fid.Cell || aodp->fid.Fid.Volume != andp->fid.Fid.Volume) {
72 if (andp->fid.Fid.Vnode == aodp->fid.Fid.Vnode) {
73 if (!strcmp(aname1, aname2)) {
74 /* Same directory and same name; this is a noop and just return success
75 * to save cycles and follow posix standards */
80 ObtainWriteLock(&andp->lock,147);
81 tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, &treq, &offset, &len, 0);
85 ObtainWriteLock(&tdc1->lock, 643);
88 oneDir = 1; /* only one dude locked */
90 else if ((andp->states & CRO) || (aodp->states & CRO)) {
94 else if (andp->fid.Fid.Vnode < aodp->fid.Fid.Vnode) {
95 ObtainWriteLock(&andp->lock,148); /* lock smaller one first */
96 ObtainWriteLock(&aodp->lock,149);
97 tdc2 = afs_FindDCache(andp, 0);
98 if (tdc2) ObtainWriteLock(&tdc2->lock, 644);
99 tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, &treq, &offset, &len, 0);
101 ObtainWriteLock(&tdc1->lock, 645);
106 ObtainWriteLock(&aodp->lock,150); /* lock smaller one first */
107 ObtainWriteLock(&andp->lock,557);
108 tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, &treq, &offset, &len, 0);
110 ObtainWriteLock(&tdc1->lock, 646);
113 tdc2 = afs_FindDCache(andp, 0);
114 if (tdc2) ObtainWriteLock(&tdc2->lock, 647);
117 osi_dnlc_remove (aodp, aname1, 0);
118 osi_dnlc_remove (andp, aname2, 0);
119 afs_symhint_inval(aodp);
120 afs_symhint_inval(andp);
123 * Make sure that the data in the cache is current. We may have
124 * received a callback while we were waiting for the write lock.
127 if (!(aodp->states & CStatd)
128 || !hsame(aodp->m.DataVersion, tdc1->f.versionNo)) {
130 ReleaseWriteLock(&aodp->lock);
133 ReleaseWriteLock(&tdc2->lock);
136 ReleaseWriteLock(&andp->lock);
138 ReleaseWriteLock(&tdc1->lock);
145 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
148 ReleaseWriteLock(&tdc1->lock);
151 ReleaseWriteLock(&aodp->lock);
154 ReleaseWriteLock(&tdc2->lock);
157 ReleaseWriteLock(&andp->lock);
162 /* locks are now set, proceed to do the real work */
164 tc = afs_Conn(&aodp->fid, &treq, SHARED_LOCK);
166 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
167 #ifdef RX_ENABLE_LOCKS
169 #endif /* RX_ENABLE_LOCKS */
170 code = RXAFS_Rename(tc->id, (struct AFSFid *) &aodp->fid.Fid, aname1,
171 (struct AFSFid *) &andp->fid.Fid, aname2,
172 &OutOldDirStatus, &OutNewDirStatus, &tsync);
173 #ifdef RX_ENABLE_LOCKS
175 #endif /* RX_ENABLE_LOCKS */
180 (afs_Analyze(tc, code, &andp->fid, &treq,
181 AFS_STATS_FS_RPCIDX_RENAME, SHARED_LOCK, (struct cell *)0));
183 returnCode = code; /* remember for later */
185 /* Now we try to do things locally. This is really loathsome code. */
186 unlinkFid.Fid.Vnode = 0;
188 /* In any event, we don't really care if the data (tdc2) is not
189 in the cache; if it isn't, we won't do the update locally. */
190 /* see if version numbers increased properly */
193 /* number increases by 1 for whole rename operation */
194 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) {
199 /* two separate dirs, each increasing by 1 */
200 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1))
202 if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1))
207 DZap(&tdc1->f.inode);
211 DZap(&tdc2->f.inode);
215 /* now really do the work */
217 /* first lookup the fid of the dude we're moving */
218 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
220 /* delete the source */
221 code = afs_dir_Delete(&tdc1->f.inode, aname1);
223 /* first see if target is there */
225 afs_dir_Lookup(&tdc2->f.inode, aname2, &unlinkFid.Fid) == 0) {
226 /* target already exists, and will be unlinked by server */
227 code = afs_dir_Delete(&tdc2->f.inode, aname2);
230 code = afs_dir_Create(&tdc2->f.inode, aname2, &fileFid.Fid);
234 DZap(&tdc1->f.inode);
237 DZap(&tdc2->f.inode);
242 /* update dir link counts */
243 aodp->m.LinkCount = OutOldDirStatus.LinkCount;
245 andp->m.LinkCount = OutNewDirStatus.LinkCount;
248 else { /* operation failed (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);
265 ReleaseWriteLock(&tdc1->lock);
269 if ((!oneDir) && tdc2) {
270 ReleaseWriteLock(&tdc2->lock);
274 ReleaseWriteLock(&aodp->lock);
275 if (!oneDir) ReleaseWriteLock(&andp->lock);
282 /* now, some more details. if unlinkFid.Fid.Vnode then we should decrement
283 the link count on this file. Note that if fileFid is a dir, then we don't
284 have to invalidate its ".." entry, since its DataVersion # should have
285 changed. However, interface is not good enough to tell us the
286 *file*'s new DataVersion, so we're stuck. Our hack: delete mark
287 the data as having an "unknown" version (effectively discarding the ".."
289 if (unlinkFid.Fid.Vnode) {
290 unlinkFid.Fid.Volume = aodp->fid.Fid.Volume;
291 unlinkFid.Cell = aodp->fid.Cell;
292 tvc = (struct vcache *)0;
293 if (!unlinkFid.Fid.Unique) {
294 tvc = afs_LookupVCache(&unlinkFid, &treq, (afs_int32 *)0, WRITE_LOCK,
297 if (!tvc) /* lookup failed or wasn't called */
298 tvc = afs_GetVCache(&unlinkFid, &treq, (afs_int32 *)0,
299 (struct vcache*)0, WRITE_LOCK);
302 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
303 afs_BozonLock(&tvc->pvnLock, tvc); /* Since afs_TryToSmush will do a pvn_vptrunc */
305 ObtainWriteLock(&tvc->lock,151);
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)) afs_TryToSmush(tvc, acred, 0);
319 ReleaseWriteLock(&tvc->lock);
320 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
321 afs_BozonUnlock(&tvc->pvnLock, tvc);
323 afs_PutVCache(tvc, WRITE_LOCK);
327 /* now handle ".." invalidation */
329 fileFid.Fid.Volume = aodp->fid.Fid.Volume;
330 fileFid.Cell = aodp->fid.Cell;
331 if (!fileFid.Fid.Unique)
332 tvc = afs_LookupVCache(&fileFid, &treq, (afs_int32 *)0, WRITE_LOCK, andp, aname2);
334 tvc = afs_GetVCache(&fileFid, &treq, (afs_int32 *)0,
335 (struct vcache*)0, WRITE_LOCK);
336 if (tvc && (vType(tvc) == VDIR)) {
337 ObtainWriteLock(&tvc->lock,152);
338 tdc1 = afs_FindDCache(tvc, 0);
340 ObtainWriteLock(&tdc1->lock, 648);
341 ZapDCE(tdc1); /* mark as unknown */
342 DZap(&tdc1->f.inode);
343 ReleaseWriteLock(&tdc1->lock);
344 afs_PutDCache(tdc1); /* put it back */
346 osi_dnlc_remove(tvc, "..", 0);
347 ReleaseWriteLock(&tvc->lock);
348 afs_PutVCache(tvc, WRITE_LOCK);
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...
353 afs_PutVCache(tvc, WRITE_LOCK);
358 code = afs_CheckCode(code, &treq, 25);
363 afs_rename(fndp, tndp)
364 struct nameidata *fndp, *tndp; {
365 register struct vcache *aodp = (struct vcache *)fndp->ni_dvp;
366 char *aname1 = fndp->ni_dent.d_name;
367 register struct vcache *andp = (struct vcache *)tndp->ni_dvp;
368 char *aname2 = tndp->ni_dent.d_name;
369 struct ucred *acred = tndp->ni_cred;
370 #else /* AFS_OSF_ENV */
371 #if defined(AFS_SGI_ENV)
372 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, npnp, acred)
373 struct pathname *npnp;
375 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, acred)
378 register struct vcache *andp;
379 char *aname1, *aname2;
380 struct AFS_UCRED *acred; {
382 register afs_int32 code;
385 code = afsrename(aodp, aname1, andp, aname2, acred);
387 AFS_RELE(tndp->ni_dvp);
388 if (tndp->ni_vp != NULL) {
389 AFS_RELE(tndp->ni_vp);
391 AFS_RELE(fndp->ni_dvp);
392 AFS_RELE(fndp->ni_vp);
393 #endif /* AFS_OSF_ENV */