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_int32 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) {
71 if (andp->fid.Fid.Vnode == aodp->fid.Fid.Vnode) {
72 if (!strcmp(aname1, aname2)) {
73 /* Same directory and same name; this is a noop and just return success
74 * to save cycles and follow posix standards */
79 ObtainWriteLock(&andp->lock,147);
80 oneDir = 1; /* only one dude locked */
82 else if ((andp->states & CRO) || (aodp->states & CRO)) {
86 else if (andp->fid.Fid.Vnode < aodp->fid.Fid.Vnode) {
87 ObtainWriteLock(&andp->lock,148); /* lock smaller one first */
88 ObtainWriteLock(&aodp->lock,149);
91 ObtainWriteLock(&aodp->lock,150); /* lock smaller one first */
92 ObtainWriteLock(&andp->lock,557);
95 osi_dnlc_remove (aodp, aname1, 0);
96 osi_dnlc_remove (andp, aname2, 0);
97 afs_symhint_inval(aodp);
98 afs_symhint_inval(andp);
100 /* before doing the rename, lookup the fileFid, just in case we
101 * don't make it down the path below that looks it up. We always need
102 * fileFid in order to handle ".." invalidation at the very end.
105 tdc1 = afs_GetDCache(aodp, 0, &treq, &offset, &len, 0);
110 * Make sure that the data in the cache is current. We may have
111 * received a callback while we were waiting for the write lock.
113 else if (!(aodp->states & CStatd)
114 || !hsame(aodp->m.DataVersion, tdc1->f.versionNo)) {
115 ReleaseWriteLock(&aodp->lock);
116 if (!oneDir) ReleaseWriteLock(&andp->lock);
122 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
124 if (tdc1) afs_PutDCache(tdc1);
125 ReleaseWriteLock(&aodp->lock);
126 if (!oneDir) ReleaseWriteLock(&andp->lock);
131 /* locks are now set, proceed to do the real work */
133 tc = afs_Conn(&aodp->fid, &treq, SHARED_LOCK);
135 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
136 #ifdef RX_ENABLE_LOCKS
138 #endif /* RX_ENABLE_LOCKS */
139 code = RXAFS_Rename(tc->id, (struct AFSFid *) &aodp->fid.Fid, aname1,
140 (struct AFSFid *) &andp->fid.Fid, aname2,
141 &OutOldDirStatus, &OutNewDirStatus, &tsync);
142 #ifdef RX_ENABLE_LOCKS
144 #endif /* RX_ENABLE_LOCKS */
149 (afs_Analyze(tc, code, &andp->fid, &treq,
150 AFS_STATS_FS_RPCIDX_RENAME, SHARED_LOCK, (struct cell *)0));
152 returnCode = code; /* remember for later */
154 /* Now we try to do things locally. This is really loathsome code. */
155 unlinkFid.Fid.Vnode = 0;
158 /* don't use GetDCache because we don't want to worry about what happens if
159 we have to stat the file (updating the stat block) before finishing
160 local hero stuff (which may put old (from rename) data version number
161 back in the cache entry).
162 In any event, we don't really care if the data is not
163 in the cache; if it isn't, we won't do the update locally. */
164 tdc1 = afs_FindDCache(aodp, 0);
165 if (!oneDir) tdc2 = afs_FindDCache(andp, 0);
167 /* see if version numbers increased properly */
170 /* number increases by 1 for whole rename operation */
171 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) {
176 /* two separate dirs, each increasing by 1 */
177 if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1))
179 if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1))
184 DZap(&tdc1->f.inode);
188 DZap(&tdc2->f.inode);
192 /* now really do the work */
194 /* first lookup the fid of the dude we're moving */
195 code = afs_dir_Lookup(&tdc1->f.inode, aname1, &fileFid.Fid);
197 /* delete the source */
198 code = afs_dir_Delete(&tdc1->f.inode, aname1);
200 /* first see if target is there */
202 afs_dir_Lookup(&tdc2->f.inode, aname2, &unlinkFid.Fid) == 0) {
203 /* target already exists, and will be unlinked by server */
204 code = afs_dir_Delete(&tdc2->f.inode, aname2);
207 code = afs_dir_Create(&tdc2->f.inode, aname2, &fileFid.Fid);
211 DZap(&tdc1->f.inode);
214 DZap(&tdc2->f.inode);
218 if (tdc1) afs_PutDCache(tdc1);
219 if ((!oneDir) && tdc2) afs_PutDCache(tdc2);
221 /* update dir link counts */
222 aodp->m.LinkCount = OutOldDirStatus.LinkCount;
224 andp->m.LinkCount = OutNewDirStatus.LinkCount;
227 else { /* operation failed (code != 0) */
229 /* if failed, server might have done something anyway, and
230 * assume that we know about it */
231 ObtainWriteLock(&afs_xcbhash, 498);
232 afs_DequeueCallback(aodp);
233 afs_DequeueCallback(andp);
234 andp->states &= ~CStatd;
235 aodp->states &= ~CStatd;
236 ReleaseWriteLock(&afs_xcbhash);
237 osi_dnlc_purgedp(andp);
238 osi_dnlc_purgedp(aodp);
243 ReleaseWriteLock(&aodp->lock);
244 if (!oneDir) ReleaseWriteLock(&andp->lock);
251 /* now, some more details. if unlinkFid.Fid.Vnode then we should decrement
252 the link count on this file. Note that if fileFid is a dir, then we don't
253 have to invalidate its ".." entry, since its DataVersion # should have
254 changed. However, interface is not good enough to tell us the
255 *file*'s new DataVersion, so we're stuck. Our hack: delete mark
256 the data as having an "unknown" version (effectively discarding the ".."
258 if (unlinkFid.Fid.Vnode) {
259 unlinkFid.Fid.Volume = aodp->fid.Fid.Volume;
260 unlinkFid.Cell = aodp->fid.Cell;
261 tvc = (struct vcache *)0;
262 if (!unlinkFid.Fid.Unique) {
263 tvc = afs_LookupVCache(&unlinkFid, &treq, (afs_int32 *)0, WRITE_LOCK,
266 if (!tvc) /* lookup failed or wasn't called */
267 tvc = afs_GetVCache(&unlinkFid, &treq, (afs_int32 *)0,
268 (struct vcache*)0, WRITE_LOCK);
271 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
272 afs_BozonLock(&tvc->pvnLock, tvc); /* Since afs_TryToSmush will do a pvn_vptrunc */
274 ObtainWriteLock(&tvc->lock,151);
276 tvc->states &= ~CUnique; /* For the dfs xlator */
277 if (tvc->m.LinkCount == 0 && !osi_Active(tvc)) {
278 /* if this was last guy (probably) discard from cache.
279 * We have to be careful to not get rid of the stat
280 * information, since otherwise operations will start
281 * failing even if the file was still open (or
282 * otherwise active), and the server no longer has the
283 * info. If the file still has valid links, we'll get
284 * a break-callback msg from the server, so it doesn't
285 * matter that we don't discard the status info */
286 if (!AFS_NFSXLATORREQ(acred)) afs_TryToSmush(tvc, acred, 0);
288 ReleaseWriteLock(&tvc->lock);
289 #if defined(AFS_SUN_ENV) || defined(AFS_ALPHA_ENV) || defined(AFS_SUN5_ENV)
290 afs_BozonUnlock(&tvc->pvnLock, tvc);
292 afs_PutVCache(tvc, WRITE_LOCK);
296 /* now handle ".." invalidation */
298 fileFid.Fid.Volume = aodp->fid.Fid.Volume;
299 fileFid.Cell = aodp->fid.Cell;
300 if (!fileFid.Fid.Unique)
301 tvc = afs_LookupVCache(&fileFid, &treq, (afs_int32 *)0, WRITE_LOCK, andp, aname2);
303 tvc = afs_GetVCache(&fileFid, &treq, (afs_int32 *)0,
304 (struct vcache*)0, WRITE_LOCK);
305 if (tvc && (vType(tvc) == VDIR)) {
306 ObtainWriteLock(&tvc->lock,152);
307 tdc1 = afs_FindDCache(tvc, 0);
309 ZapDCE(tdc1); /* mark as unknown */
310 DZap(&tdc1->f.inode);
311 afs_PutDCache(tdc1); /* put it back */
313 osi_dnlc_remove(tvc, "..", 0);
314 ReleaseWriteLock(&tvc->lock);
315 afs_PutVCache(tvc, WRITE_LOCK);
317 /* True we shouldn't come here since tvc SHOULD be a dir, but we
318 * 'syntactically' need to unless we change the 'if' above...
320 afs_PutVCache(tvc, WRITE_LOCK);
325 code = afs_CheckCode(code, &treq, 25);
330 afs_rename(fndp, tndp)
331 struct nameidata *fndp, *tndp; {
332 register struct vcache *aodp = (struct vcache *)fndp->ni_dvp;
333 char *aname1 = fndp->ni_dent.d_name;
334 register struct vcache *andp = (struct vcache *)tndp->ni_dvp;
335 char *aname2 = tndp->ni_dent.d_name;
336 struct ucred *acred = tndp->ni_cred;
337 #else /* AFS_OSF_ENV */
338 #if defined(AFS_SGI_ENV)
339 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, npnp, acred)
340 struct pathname *npnp;
342 afs_rename(OSI_VC_ARG(aodp), aname1, andp, aname2, acred)
345 register struct vcache *andp;
346 char *aname1, *aname2;
347 struct AFS_UCRED *acred; {
349 register afs_int32 code;
352 code = afsrename(aodp, aname1, andp, aname2, acred);
354 AFS_RELE(tndp->ni_dvp);
355 if (tndp->ni_vp != NULL) {
356 AFS_RELE(tndp->ni_vp);
358 AFS_RELE(fndp->ni_dvp);
359 AFS_RELE(fndp->ni_vp);
360 #endif /* AFS_OSF_ENV */