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 if (strlen(aname1) > AFSNAMEMAX || strlen(aname2) > AFSNAMEMAX) {
63 /* verify the latest versions of the stat cache entries */
65 code = afs_VerifyVCache(aodp, &treq);
67 code = afs_VerifyVCache(andp, &treq);
70 /* lock in appropriate order, after some checks */
71 if (aodp->fid.Cell != andp->fid.Cell || aodp->fid.Fid.Volume != andp->fid.Fid.Volume) {
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 */
85 ObtainWriteLock(&andp->lock,147);
86 tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, &treq, &offset, &len, 0);
90 ObtainWriteLock(&tdc1->lock, 643);
93 oneDir = 1; /* only one dude locked */
95 else if ((andp->states & CRO) || (aodp->states & CRO)) {
99 else if (andp->fid.Fid.Vnode < aodp->fid.Fid.Vnode) {
100 ObtainWriteLock(&andp->lock,148); /* lock smaller one first */
101 ObtainWriteLock(&aodp->lock,149);
102 tdc2 = afs_FindDCache(andp, 0);
103 if (tdc2) ObtainWriteLock(&tdc2->lock, 644);
104 tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, &treq, &offset, &len, 0);
106 ObtainWriteLock(&tdc1->lock, 645);
111 ObtainWriteLock(&aodp->lock,150); /* lock smaller one first */
112 ObtainWriteLock(&andp->lock,557);
113 tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, &treq, &offset, &len, 0);
115 ObtainWriteLock(&tdc1->lock, 646);
118 tdc2 = afs_FindDCache(andp, 0);
119 if (tdc2) ObtainWriteLock(&tdc2->lock, 647);
122 osi_dnlc_remove (aodp, aname1, 0);
123 osi_dnlc_remove (andp, aname2, 0);
124 afs_symhint_inval(aodp);
125 afs_symhint_inval(andp);
128 * Make sure that the data in the cache is current. We may have
129 * received a callback while we were waiting for the write lock.
132 if (!(aodp->states & CStatd)
133 || !hsame(aodp->m.DataVersion, tdc1->f.versionNo)) {
135 ReleaseWriteLock(&aodp->lock);
138 ReleaseWriteLock(&tdc2->lock);
141 ReleaseWriteLock(&andp->lock);
143 ReleaseWriteLock(&tdc1->lock);
150 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
153 ReleaseWriteLock(&tdc1->lock);
156 ReleaseWriteLock(&aodp->lock);
159 ReleaseWriteLock(&tdc2->lock);
162 ReleaseWriteLock(&andp->lock);
167 /* locks are now set, proceed to do the real work */
169 tc = afs_Conn(&aodp->fid, &treq, SHARED_LOCK);
171 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
172 #ifdef RX_ENABLE_LOCKS
174 #endif /* RX_ENABLE_LOCKS */
175 code = RXAFS_Rename(tc->id, (struct AFSFid *) &aodp->fid.Fid, aname1,
176 (struct AFSFid *) &andp->fid.Fid, aname2,
177 &OutOldDirStatus, &OutNewDirStatus, &tsync);
178 #ifdef RX_ENABLE_LOCKS
180 #endif /* RX_ENABLE_LOCKS */
185 (afs_Analyze(tc, code, &andp->fid, &treq,
186 AFS_STATS_FS_RPCIDX_RENAME, SHARED_LOCK, (struct cell *)0));
188 returnCode = code; /* remember for later */
190 /* Now we try to do things locally. This is really loathsome code. */
191 unlinkFid.Fid.Vnode = 0;
193 /* In any event, we don't really care if the data (tdc2) is not
194 in the cache; if it isn't, we won't do the update locally. */
195 /* see if version numbers increased properly */
198 /* number increases by 1 for whole rename operation */
199 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) {
204 /* two separate dirs, each increasing by 1 */
205 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1))
207 if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1))
212 DZap(&tdc1->f.inode);
216 DZap(&tdc2->f.inode);
220 /* now really do the work */
222 /* first lookup the fid of the dude we're moving */
223 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
225 /* delete the source */
226 code = afs_dir_Delete(&tdc1->f.inode, aname1);
228 /* first see if target is there */
230 afs_dir_Lookup(&tdc2->f.inode, aname2, &unlinkFid.Fid) == 0) {
231 /* target already exists, and will be unlinked by server */
232 code = afs_dir_Delete(&tdc2->f.inode, aname2);
235 code = afs_dir_Create(&tdc2->f.inode, aname2, &fileFid.Fid);
239 DZap(&tdc1->f.inode);
242 DZap(&tdc2->f.inode);
247 /* update dir link counts */
248 aodp->m.LinkCount = OutOldDirStatus.LinkCount;
250 andp->m.LinkCount = OutNewDirStatus.LinkCount;
253 else { /* operation failed (code != 0) */
255 /* if failed, server might have done something anyway, and
256 * assume that we know about it */
257 ObtainWriteLock(&afs_xcbhash, 498);
258 afs_DequeueCallback(aodp);
259 afs_DequeueCallback(andp);
260 andp->states &= ~CStatd;
261 aodp->states &= ~CStatd;
262 ReleaseWriteLock(&afs_xcbhash);
263 osi_dnlc_purgedp(andp);
264 osi_dnlc_purgedp(aodp);
270 ReleaseWriteLock(&tdc1->lock);
274 if ((!oneDir) && tdc2) {
275 ReleaseWriteLock(&tdc2->lock);
279 ReleaseWriteLock(&aodp->lock);
280 if (!oneDir) ReleaseWriteLock(&andp->lock);
287 /* now, some more details. if unlinkFid.Fid.Vnode then we should decrement
288 the link count on this file. Note that if fileFid is a dir, then we don't
289 have to invalidate its ".." entry, since its DataVersion # should have
290 changed. However, interface is not good enough to tell us the
291 *file*'s new DataVersion, so we're stuck. Our hack: delete mark
292 the data as having an "unknown" version (effectively discarding the ".."
294 if (unlinkFid.Fid.Vnode) {
295 unlinkFid.Fid.Volume = aodp->fid.Fid.Volume;
296 unlinkFid.Cell = aodp->fid.Cell;
297 tvc = (struct vcache *)0;
298 if (!unlinkFid.Fid.Unique) {
299 tvc = afs_LookupVCache(&unlinkFid, &treq, (afs_int32 *)0, WRITE_LOCK,
302 if (!tvc) /* lookup failed or wasn't called */
303 tvc = afs_GetVCache(&unlinkFid, &treq, (afs_int32 *)0,
304 (struct vcache*)0, WRITE_LOCK);
307 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
308 afs_BozonLock(&tvc->pvnLock, tvc); /* Since afs_TryToSmush will do a pvn_vptrunc */
310 ObtainWriteLock(&tvc->lock,151);
312 tvc->states &= ~CUnique; /* For the dfs xlator */
313 if (tvc->m.LinkCount == 0 && !osi_Active(tvc)) {
314 /* if this was last guy (probably) discard from cache.
315 * We have to be careful to not get rid of the stat
316 * information, since otherwise operations will start
317 * failing even if the file was still open (or
318 * otherwise active), and the server no longer has the
319 * info. If the file still has valid links, we'll get
320 * a break-callback msg from the server, so it doesn't
321 * matter that we don't discard the status info */
322 if (!AFS_NFSXLATORREQ(acred)) afs_TryToSmush(tvc, acred, 0);
324 ReleaseWriteLock(&tvc->lock);
325 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
326 afs_BozonUnlock(&tvc->pvnLock, tvc);
328 afs_PutVCache(tvc, WRITE_LOCK);
332 /* now handle ".." invalidation */
334 fileFid.Fid.Volume = aodp->fid.Fid.Volume;
335 fileFid.Cell = aodp->fid.Cell;
336 if (!fileFid.Fid.Unique)
337 tvc = afs_LookupVCache(&fileFid, &treq, (afs_int32 *)0, WRITE_LOCK, andp, aname2);
339 tvc = afs_GetVCache(&fileFid, &treq, (afs_int32 *)0,
340 (struct vcache*)0, WRITE_LOCK);
341 if (tvc && (vType(tvc) == VDIR)) {
342 ObtainWriteLock(&tvc->lock,152);
343 tdc1 = afs_FindDCache(tvc, 0);
345 ObtainWriteLock(&tdc1->lock, 648);
346 ZapDCE(tdc1); /* mark as unknown */
347 DZap(&tdc1->f.inode);
348 ReleaseWriteLock(&tdc1->lock);
349 afs_PutDCache(tdc1); /* put it back */
351 osi_dnlc_remove(tvc, "..", 0);
352 ReleaseWriteLock(&tvc->lock);
353 afs_PutVCache(tvc, WRITE_LOCK);
355 /* True we shouldn't come here since tvc SHOULD be a dir, but we
356 * 'syntactically' need to unless we change the 'if' above...
358 afs_PutVCache(tvc, WRITE_LOCK);
363 code = afs_CheckCode(code, &treq, 25);
368 afs_rename(fndp, tndp)
369 struct nameidata *fndp, *tndp; {
370 register struct vcache *aodp = VTOAFS(fndp->ni_dvp);
371 char *aname1 = fndp->ni_dent.d_name;
372 register struct vcache *andp = VTOAFS(tndp->ni_dvp);
373 char *aname2 = tndp->ni_dent.d_name;
374 struct ucred *acred = tndp->ni_cred;
375 #else /* AFS_OSF_ENV */
376 #if defined(AFS_SGI_ENV)
377 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, npnp, acred)
378 struct pathname *npnp;
380 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, acred)
383 register struct vcache *andp;
384 char *aname1, *aname2;
385 struct AFS_UCRED *acred; {
387 register afs_int32 code;
390 code = afsrename(aodp, aname1, andp, aname2, acred);
392 AFS_RELE(tndp->ni_dvp);
393 if (tndp->ni_vp != NULL) {
394 AFS_RELE(tndp->ni_vp);
396 AFS_RELE(fndp->ni_dvp);
397 AFS_RELE(fndp->ni_vp);
398 #endif /* AFS_OSF_ENV */