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"
21 #include "afs/sysincludes.h" /* Standard vendor system headers */
22 #include "afsincludes.h" /* Afs-based standard headers */
23 #include "afs/afs_stats.h" /* statistics */
24 #include "afs/afs_cbqueue.h"
25 #include "afs/nfsclient.h"
26 #include "afs/afs_osidnlc.h"
28 extern afs_rwlock_t afs_xcbhash
;
30 /* Note that we don't set CDirty here, this is OK because the rename
31 * RPC is called synchronously. */
34 afsrename(struct vcache
*aodp
, char *aname1
, struct vcache
*andp
,
35 char *aname2
, afs_ucred_t
*acred
, struct vrequest
*areq
)
40 int oneDir
, doLocally
;
41 afs_size_t offset
, len
;
42 struct VenusFid unlinkFid
, fileFid
;
44 struct dcache
*tdc1
, *tdc2
;
45 struct AFSFetchStatus
*OutOldDirStatus
, *OutNewDirStatus
;
46 struct AFSVolSync tsync
;
47 struct rx_connection
*rxconn
;
49 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
);
54 OutOldDirStatus
= osi_AllocSmallSpace(sizeof(struct AFSFetchStatus
));
55 OutNewDirStatus
= osi_AllocSmallSpace(sizeof(struct AFSFetchStatus
));
57 if (strlen(aname1
) > AFSNAMEMAX
|| strlen(aname2
) > AFSNAMEMAX
) {
62 /* verify the latest versions of the stat cache entries */
64 code
= afs_VerifyVCache(aodp
, areq
);
67 code
= afs_VerifyVCache(andp
, areq
);
71 /* lock in appropriate order, after some checks */
72 if (aodp
->f
.fid
.Cell
!= andp
->f
.fid
.Cell
73 || aodp
->f
.fid
.Fid
.Volume
!= andp
->f
.fid
.Fid
.Volume
) {
79 if (andp
->f
.fid
.Fid
.Vnode
== aodp
->f
.fid
.Fid
.Vnode
) {
80 if (!strcmp(aname1
, aname2
)) {
81 /* Same directory and same name; this is a noop and just return success
82 * to save cycles and follow posix standards */
88 if (AFS_IS_DISCONNECTED
&& !AFS_IS_DISCON_RW
) {
93 ObtainWriteLock(&andp
->lock
, 147);
94 tdc1
= afs_GetDCache(aodp
, (afs_size_t
) 0, areq
, &offset
, &len
, 0);
98 ObtainWriteLock(&tdc1
->lock
, 643);
101 oneDir
= 1; /* only one dude locked */
102 } else if ((andp
->f
.states
& CRO
) || (aodp
->f
.states
& CRO
)) {
105 } else if (andp
->f
.fid
.Fid
.Vnode
< aodp
->f
.fid
.Fid
.Vnode
) {
106 ObtainWriteLock(&andp
->lock
, 148); /* lock smaller one first */
107 ObtainWriteLock(&aodp
->lock
, 149);
108 tdc2
= afs_FindDCache(andp
, (afs_size_t
) 0);
110 ObtainWriteLock(&tdc2
->lock
, 644);
111 tdc1
= afs_GetDCache(aodp
, (afs_size_t
) 0, areq
, &offset
, &len
, 0);
113 ObtainWriteLock(&tdc1
->lock
, 645);
117 ObtainWriteLock(&aodp
->lock
, 150); /* lock smaller one first */
118 ObtainWriteLock(&andp
->lock
, 557);
119 tdc1
= afs_GetDCache(aodp
, (afs_size_t
) 0, areq
, &offset
, &len
, 0);
121 ObtainWriteLock(&tdc1
->lock
, 646);
124 tdc2
= afs_FindDCache(andp
, (afs_size_t
) 0);
126 ObtainWriteLock(&tdc2
->lock
, 647);
129 osi_dnlc_remove(aodp
, aname1
, 0);
130 osi_dnlc_remove(andp
, aname2
, 0);
133 * Make sure that the data in the cache is current. We may have
134 * received a callback while we were waiting for the write lock.
137 if (!(aodp
->f
.states
& CStatd
)
138 || !hsame(aodp
->f
.m
.DataVersion
, tdc1
->f
.versionNo
)) {
140 ReleaseWriteLock(&aodp
->lock
);
143 ReleaseWriteLock(&tdc2
->lock
);
146 ReleaseWriteLock(&andp
->lock
);
148 ReleaseWriteLock(&tdc1
->lock
);
155 code
= afs_dir_Lookup(tdc1
, aname1
, &fileFid
.Fid
);
158 ReleaseWriteLock(&tdc1
->lock
);
161 ReleaseWriteLock(&aodp
->lock
);
164 ReleaseWriteLock(&tdc2
->lock
);
167 ReleaseWriteLock(&andp
->lock
);
172 if (!AFS_IS_DISCON_RW
) {
175 tc
= afs_Conn(&aodp
->f
.fid
, areq
, SHARED_LOCK
, &rxconn
);
177 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME
);
181 (struct AFSFid
*)&aodp
->f
.fid
.Fid
,
183 (struct AFSFid
*)&andp
->f
.fid
.Fid
,
194 (tc
, rxconn
, code
, &andp
->f
.fid
, areq
, AFS_STATS_FS_RPCIDX_RENAME
,
200 /* Seek moved file vcache. */
201 fileFid
.Cell
= aodp
->f
.fid
.Cell
;
202 fileFid
.Fid
.Volume
= aodp
->f
.fid
.Fid
.Volume
;
203 ObtainSharedLock(&afs_xvcache
, 754);
204 tvc
= afs_FindVCache(&fileFid
, 0 , 1);
205 ReleaseSharedLock(&afs_xvcache
);
208 /* XXX - We're locking this vcache whilst holding dcaches. Ooops */
209 ObtainWriteLock(&tvc
->lock
, 750);
210 if (!(tvc
->f
.ddirty_flags
& (VDisconRename
|VDisconCreate
))) {
211 /* If the vnode was created locally, then we don't care
212 * about recording the rename - we'll do it automatically
213 * on replay. If we've already renamed, we've already stored
214 * the required information about where we came from.
217 if (!aodp
->f
.shadow
.vnode
) {
218 /* Make shadow copy of parent dir only. */
219 afs_MakeShadowDir(aodp
, tdc1
);
222 /* Save old parent dir fid so it will be searchable
225 tvc
->f
.oldParent
.vnode
= aodp
->f
.fid
.Fid
.Vnode
;
226 tvc
->f
.oldParent
.unique
= aodp
->f
.fid
.Fid
.Unique
;
228 afs_DisconAddDirty(tvc
,
230 | (oneDir
? VDisconRenameSameDir
:0),
234 ReleaseWriteLock(&tvc
->lock
);
239 } /* if !(AFS_IS_DISCON_RW)*/
240 returnCode
= code
; /* remember for later */
242 /* Now we try to do things locally. This is really loathsome code. */
243 unlinkFid
.Fid
.Vnode
= 0;
245 /* In any event, we don't really care if the data (tdc2) is not
246 * in the cache; if it isn't, we won't do the update locally. */
247 /* see if version numbers increased properly */
249 if (!AFS_IS_DISCON_RW
) {
251 /* number increases by 1 for whole rename operation */
252 if (!afs_LocalHero(aodp
, tdc1
, OutOldDirStatus
, 1)) {
256 /* two separate dirs, each increasing by 1 */
257 if (!afs_LocalHero(aodp
, tdc1
, OutOldDirStatus
, 1))
259 if (!afs_LocalHero(andp
, tdc2
, OutNewDirStatus
, 1))
272 } /* if (!AFS_IS_DISCON_RW) */
274 /* now really do the work */
276 /* first lookup the fid of the dude we're moving */
277 code
= afs_dir_Lookup(tdc1
, aname1
, &fileFid
.Fid
);
279 /* delete the source */
280 code
= afs_dir_Delete(tdc1
, aname1
);
282 /* first see if target is there */
284 && afs_dir_Lookup(tdc2
, aname2
,
285 &unlinkFid
.Fid
) == 0) {
286 /* target already exists, and will be unlinked by server */
287 code
= afs_dir_Delete(tdc2
, aname2
);
290 ObtainWriteLock(&afs_xdcache
, 292);
291 code
= afs_dir_Create(tdc2
, aname2
, &fileFid
.Fid
);
292 ReleaseWriteLock(&afs_xdcache
);
305 /* update dir link counts */
306 if (AFS_IS_DISCON_RW
) {
308 aodp
->f
.m
.LinkCount
--;
309 andp
->f
.m
.LinkCount
++;
311 /* If we're in the same directory, link count doesn't change */
313 aodp
->f
.m
.LinkCount
= OutOldDirStatus
->LinkCount
;
315 andp
->f
.m
.LinkCount
= OutNewDirStatus
->LinkCount
;
318 } else { /* operation failed (code != 0) */
320 /* if failed, server might have done something anyway, and
321 * assume that we know about it */
322 ObtainWriteLock(&afs_xcbhash
, 498);
323 afs_StaleVCacheFlags(aodp
, AFS_STALEVC_CBLOCKED
, 0);
324 afs_StaleVCacheFlags(andp
, AFS_STALEVC_CBLOCKED
, 0);
325 ReleaseWriteLock(&afs_xcbhash
);
331 ReleaseWriteLock(&tdc1
->lock
);
335 if ((!oneDir
) && tdc2
) {
336 ReleaseWriteLock(&tdc2
->lock
);
340 ReleaseWriteLock(&aodp
->lock
);
343 ReleaseWriteLock(&andp
->lock
);
351 /* now, some more details. if unlinkFid.Fid.Vnode then we should decrement
352 * the link count on this file. Note that if fileFid is a dir, then we don't
353 * have to invalidate its ".." entry, since its DataVersion # should have
354 * changed. However, interface is not good enough to tell us the
355 * *file*'s new DataVersion, so we're stuck. Our hack: delete mark
356 * the data as having an "unknown" version (effectively discarding the ".."
358 if (unlinkFid
.Fid
.Vnode
) {
360 unlinkFid
.Fid
.Volume
= aodp
->f
.fid
.Fid
.Volume
;
361 unlinkFid
.Cell
= aodp
->f
.fid
.Cell
;
363 if (!unlinkFid
.Fid
.Unique
) {
364 tvc
= afs_LookupVCache(&unlinkFid
, areq
, NULL
, aodp
, aname1
);
366 if (!tvc
) /* lookup failed or wasn't called */
367 tvc
= afs_GetVCache(&unlinkFid
, areq
, NULL
, NULL
);
370 ObtainWriteLock(&tvc
->lock
, 151);
371 tvc
->f
.m
.LinkCount
--;
372 tvc
->f
.states
&= ~CUnique
; /* For the dfs xlator */
373 if (tvc
->f
.m
.LinkCount
== 0 && !osi_Active(tvc
)) {
374 /* if this was last guy (probably) discard from cache.
375 * We have to be careful to not get rid of the stat
376 * information, since otherwise operations will start
377 * failing even if the file was still open (or
378 * otherwise active), and the server no longer has the
379 * info. If the file still has valid links, we'll get
380 * a break-callback msg from the server, so it doesn't
381 * matter that we don't discard the status info */
382 if (!AFS_NFSXLATORREQ(acred
))
383 afs_TryToSmush(tvc
, acred
, 0);
385 ReleaseWriteLock(&tvc
->lock
);
390 /* now handle ".." invalidation */
392 fileFid
.Fid
.Volume
= aodp
->f
.fid
.Fid
.Volume
;
393 fileFid
.Cell
= aodp
->f
.fid
.Cell
;
394 if (!fileFid
.Fid
.Unique
)
395 tvc
= afs_LookupVCache(&fileFid
, areq
, NULL
, andp
, aname2
);
397 tvc
= afs_GetVCache(&fileFid
, areq
, NULL
, (struct vcache
*)0);
398 if (tvc
&& (vType(tvc
) == VDIR
)) {
399 ObtainWriteLock(&tvc
->lock
, 152);
400 tdc1
= afs_FindDCache(tvc
, (afs_size_t
) 0);
402 if (AFS_IS_DISCON_RW
) {
403 /* If disconnected, we need to fix (not discard) the "..".*/
404 afs_dir_ChangeFid(tdc1
,
406 &aodp
->f
.fid
.Fid
.Vnode
,
407 &andp
->f
.fid
.Fid
.Vnode
);
409 ObtainWriteLock(&tdc1
->lock
, 648);
410 ZapDCE(tdc1
); /* mark as unknown */
412 ReleaseWriteLock(&tdc1
->lock
);
413 afs_PutDCache(tdc1
); /* put it back */
416 osi_dnlc_remove(tvc
, "..", 0);
417 ReleaseWriteLock(&tvc
->lock
);
419 } else if (AFS_IS_DISCON_RW
&& tvc
&& (vType(tvc
) == VREG
)) {
420 /* XXX - Should tvc not get locked here? */
421 tvc
->f
.parent
.vnode
= andp
->f
.fid
.Fid
.Vnode
;
422 tvc
->f
.parent
.unique
= andp
->f
.fid
.Fid
.Unique
;
424 /* True we shouldn't come here since tvc SHOULD be a dir, but we
425 * 'syntactically' need to unless we change the 'if' above...
432 osi_FreeSmallSpace(OutOldDirStatus
);
433 osi_FreeSmallSpace(OutNewDirStatus
);
438 #if defined(AFS_SGI_ENV)
439 afs_rename(OSI_VC_DECL(aodp
), char *aname1
, struct vcache
*andp
, char *aname2
, struct pathname
*npnp
, afs_ucred_t
*acred
)
441 afs_rename(OSI_VC_DECL(aodp
), char *aname1
, struct vcache
*andp
, char *aname2
, afs_ucred_t
*acred
)
445 struct afs_fakestat_state ofakestate
;
446 struct afs_fakestat_state nfakestate
;
447 struct vrequest
*treq
= NULL
;
448 OSI_VC_CONVERT(aodp
);
450 code
= afs_CreateReq(&treq
, acred
);
454 afs_InitFakeStat(&ofakestate
);
455 afs_InitFakeStat(&nfakestate
);
459 code
= afs_EvalFakeStat(&aodp
, &ofakestate
, treq
);
462 code
= afs_EvalFakeStat(&andp
, &nfakestate
, treq
);
465 code
= afsrename(aodp
, aname1
, andp
, aname2
, acred
, treq
);
467 afs_PutFakeStat(&ofakestate
);
468 afs_PutFakeStat(&nfakestate
);
472 code
= afs_CheckCode(code
, treq
, 25);
473 afs_DestroyReq(treq
);