Ukernel prototypes
[openafs.git] / src / afs / VNOPS / afs_vnop_remove.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 /*
11  * Implements:
12  * afs_Wire (DUX)
13  * FetchWholeEnchilada
14  * afs_IsWired (DUX)
15  * afsremove
16  * afs_remove
17  * afs_newname
18  *
19  */
20 #include <afsconfig.h>
21 #include "afs/param.h"
22
23
24 #include "afs/sysincludes.h"    /* Standard vendor system headers */
25 #include "afsincludes.h"        /* Afs-based standard headers */
26 #include "afs/afs_stats.h"      /* statistics */
27 #include "afs/afs_cbqueue.h"
28 #include "afs/nfsclient.h"
29 #include "afs/afs_osidnlc.h"
30
31
32 extern afs_rwlock_t afs_xvcache;
33 extern afs_rwlock_t afs_xcbhash;
34
35
36 #ifdef  AFS_OSF_ENV
37 /*
38  *  Wire down file in cache: prefetch all data, and turn on CWired flag
39  *  so that callbacks/callback expirations are (temporarily) ignored
40  *  and cache file(s) are kept in cache. File will be unwired when
41  *  afs_inactive is called (ie no one has VN_HOLD on vnode), or when
42  *  afs_IsWired notices that the file is no longer Active.
43  */
44 afs_Wire(avc, areq)
45 #else /* AFS_OSF_ENV */
46 static void
47 FetchWholeEnchilada(register struct vcache *avc, struct vrequest *areq)
48 #endif
49 {
50     register afs_int32 nextChunk;
51     register struct dcache *tdc;
52     afs_size_t pos, offset, len;
53
54     AFS_STATCNT(FetchWholeEnchilada);
55     if ((avc->f.states & CStatd) == 0)
56         return;                 /* don't know size */
57     for (nextChunk = 0; nextChunk < 1024; nextChunk++) {        /* sanity check on N chunks */
58         pos = AFS_CHUNKTOBASE(nextChunk);
59 #if     defined(AFS_OSF_ENV)
60         if (pos >= avc->f.m.Length)
61             break;              /* all done */
62 #else /* AFS_OSF_ENV */
63         if (pos >= avc->f.m.Length)
64             return;             /* all done */
65 #endif
66         tdc = afs_GetDCache(avc, pos, areq, &offset, &len, 0);
67         if (!tdc)
68 #if     defined(AFS_OSF_ENV)
69             break;
70 #else /* AFS_OSF_ENV */
71             return;
72 #endif
73         afs_PutDCache(tdc);
74     }
75 #if defined(AFS_OSF_ENV)
76     avc->f.states |= CWired;
77 #endif /* AFS_OSF_ENV */
78 }
79
80 #if     defined(AFS_OSF_ENV)
81 /*
82  *  Tests whether file is wired down, after unwiring the file if it
83  *  is found to be inactive (ie not open and not being paged from).
84  */
85 afs_IsWired(register struct vcache *avc)
86 {
87     if (avc->f.states & CWired) {
88         if (osi_Active(avc)) {
89             return 1;
90         }
91         avc->f.states &= ~CWired;
92     }
93     return 0;
94 }
95 #endif /* AFS_OSF_ENV */
96
97 int
98 afsremove(register struct vcache *adp, register struct dcache *tdc,
99           register struct vcache *tvc, char *aname, struct AFS_UCRED *acred,
100           struct vrequest *treqp)
101 {
102     register afs_int32 code = 0;
103     register struct afs_conn *tc;
104     struct AFSFetchStatus OutDirStatus;
105     struct AFSVolSync tsync;
106     XSTATS_DECLS;
107     if (!AFS_IS_DISCONNECTED) {
108         do {
109             tc = afs_Conn(&adp->f.fid, treqp, SHARED_LOCK);
110             if (tc) {
111                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
112                 RX_AFS_GUNLOCK();
113                 code =
114                     RXAFS_RemoveFile(tc->id, (struct AFSFid *)&adp->f.fid.Fid,
115                                      aname, &OutDirStatus, &tsync);
116                 RX_AFS_GLOCK();
117                 XSTATS_END_TIME;
118             } else
119                 code = -1;
120         } while (afs_Analyze
121                  (tc, code, &adp->f.fid, treqp, AFS_STATS_FS_RPCIDX_REMOVEFILE,
122                   SHARED_LOCK, NULL));
123     }
124
125     osi_dnlc_remove(adp, aname, tvc);
126
127     if (code) {
128         if (tdc) {
129             ReleaseSharedLock(&tdc->lock);
130             afs_PutDCache(tdc);
131         }
132
133         if (tvc)
134             afs_PutVCache(tvc);
135
136         if (code < 0) {
137             ObtainWriteLock(&afs_xcbhash, 497);
138             afs_DequeueCallback(adp);
139             adp->f.states &= ~CStatd;
140             ReleaseWriteLock(&afs_xcbhash);
141             osi_dnlc_purgedp(adp);
142         }
143         ReleaseWriteLock(&adp->lock);
144         code = afs_CheckCode(code, treqp, 21);
145         return code;
146     }
147     if (tdc)
148         UpgradeSToWLock(&tdc->lock, 637);
149     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
150         /* we can do it locally */
151         code = afs_dir_Delete(tdc, aname);
152         if (code) {
153             ZapDCE(tdc);        /* surprise error -- invalid value */
154             DZap(tdc);
155         }
156     }
157     if (tdc) {
158         ReleaseWriteLock(&tdc->lock);
159         afs_PutDCache(tdc);     /* drop ref count */
160     }
161     ReleaseWriteLock(&adp->lock);
162
163     /* now, get vnode for unlinked dude, and see if we should force it
164      * from cache.  adp is now the deleted files vnode.  Note that we
165      * call FindVCache instead of GetVCache since if the file's really
166      * gone, we won't be able to fetch the status info anyway.  */
167     if (tvc) {
168 #ifdef AFS_BOZONLOCK_ENV
169         afs_BozonLock(&tvc->pvnLock, tvc);
170         /* Since afs_TryToSmush will do a pvn_vptrunc */
171 #endif
172         ObtainWriteLock(&tvc->lock, 141);
173         /* note that callback will be broken on the deleted file if there are
174          * still >0 links left to it, so we'll get the stat right */
175         tvc->f.m.LinkCount--;
176         tvc->f.states &= ~CUnique;      /* For the dfs xlator */
177         if (tvc->f.m.LinkCount == 0 && !osi_Active(tvc)) {
178             if (!AFS_NFSXLATORREQ(acred))
179                 afs_TryToSmush(tvc, acred, 0);
180         }
181         ReleaseWriteLock(&tvc->lock);
182 #ifdef AFS_BOZONLOCK_ENV
183         afs_BozonUnlock(&tvc->pvnLock, tvc);
184 #endif
185         afs_PutVCache(tvc);
186     }
187     return (0);
188 }
189
190 char *
191 afs_newname(void)
192 {
193     char *name, *sp, *p = ".__afs";
194     afs_int32 rd = afs_random() & 0xffff;
195
196     sp = name = osi_AllocSmallSpace(AFS_SMALLOCSIZ);
197     while (*p != '\0')
198         *sp++ = *p++;
199     while (rd) {
200         *sp++ = "0123456789ABCDEF"[rd & 0x0f];
201         rd >>= 4;
202     }
203     *sp = '\0';
204     return (name);
205 }
206
207 /* these variables appear to exist for debugging purposes */
208 struct vcache *Tadp1, *Ttvc;
209 int Tadpr, Ttvcr;
210 char *Tnam;
211 char *Tnam1;
212
213 /* Note that we don't set CDirty here, this is OK because the unlink
214  * RPC is called synchronously */
215 int
216 afs_remove(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred)
217 {
218     struct vrequest treq;
219     register struct dcache *tdc;
220     struct VenusFid unlinkFid;
221     register afs_int32 code;
222     register struct vcache *tvc;
223     afs_size_t offset, len;
224     struct afs_fakestat_state fakestate;
225     OSI_VC_CONVERT(adp);
226
227     AFS_STATCNT(afs_remove);
228     afs_Trace2(afs_iclSetp, CM_TRACE_REMOVE, ICL_TYPE_POINTER, adp,
229                ICL_TYPE_STRING, aname);
230
231 #ifdef  AFS_OSF_ENV
232     tvc = (struct vcache *)ndp->ni_vp;  /* should never be null */
233 #endif
234
235     if ((code = afs_InitReq(&treq, acred))) {
236 #ifdef  AFS_OSF_ENV
237         afs_PutVCache(tvc);
238 #endif
239         return code;
240     }
241
242     afs_InitFakeStat(&fakestate);
243     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
244     if (code) {
245         afs_PutFakeStat(&fakestate);
246 #ifdef  AFS_OSF_ENV
247         afs_PutVCache(tvc);
248 #endif
249         return code;
250     }
251
252     /* Check if this is dynroot */
253     if (afs_IsDynroot(adp)) {
254         code = afs_DynrootVOPRemove(adp, acred, aname);
255         afs_PutFakeStat(&fakestate);
256 #ifdef  AFS_OSF_ENV
257         afs_PutVCache(tvc);
258 #endif
259         return code;
260     }
261     if (afs_IsDynrootMount(adp)) {
262         return ENOENT;
263     }
264
265     if (strlen(aname) > AFSNAMEMAX) {
266         afs_PutFakeStat(&fakestate);
267 #ifdef  AFS_OSF_ENV
268         afs_PutVCache(tvc);
269 #endif
270         return ENAMETOOLONG;
271     }
272   tagain:
273     code = afs_VerifyVCache(adp, &treq);
274 #ifdef  AFS_OSF_ENV
275     tvc = VTOAFS(ndp->ni_vp);   /* should never be null */
276     if (code) {
277         afs_PutVCache(tvc);
278         afs_PutFakeStat(&fakestate);
279         return afs_CheckCode(code, &treq, 22);
280     }
281 #else /* AFS_OSF_ENV */
282     tvc = NULL;
283     if (code) {
284         code = afs_CheckCode(code, &treq, 23);
285         afs_PutFakeStat(&fakestate);
286         return code;
287     }
288 #endif
289
290     /** If the volume is read-only, return error without making an RPC to the
291       * fileserver
292       */
293     if (adp->f.states & CRO) {
294 #ifdef  AFS_OSF_ENV
295         afs_PutVCache(tvc);
296 #endif
297         code = EROFS;
298         afs_PutFakeStat(&fakestate);
299         return code;
300     }
301
302     /* If we're running disconnected without logging, go no further... */
303     if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
304 #ifdef  AFS_OSF_ENV
305         afs_PutVCache(tvc);
306 #endif
307         code = ENETDOWN;
308         afs_PutFakeStat(&fakestate);
309         return code;
310     }
311     
312     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);  /* test for error below */
313     ObtainWriteLock(&adp->lock, 142);
314     if (tdc)
315         ObtainSharedLock(&tdc->lock, 638);
316
317     /*
318      * Make sure that the data in the cache is current. We may have
319      * received a callback while we were waiting for the write lock.
320      */
321     if (!(adp->f.states & CStatd)
322         || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) {
323         ReleaseWriteLock(&adp->lock);
324         if (tdc) {
325             ReleaseSharedLock(&tdc->lock);
326             afs_PutDCache(tdc);
327         }
328         goto tagain;
329     }
330
331     unlinkFid.Fid.Vnode = 0;
332     if (!tvc) {
333         tvc = osi_dnlc_lookup(adp, aname, WRITE_LOCK);
334     }
335     /* This should not be necessary since afs_lookup() has already
336      * done the work.
337      */
338     if (!tvc)
339         if (tdc) {
340             code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid);
341             if (code == 0) {
342                 afs_int32 cached = 0;
343
344                 unlinkFid.Cell = adp->f.fid.Cell;
345                 unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume;
346                 if (unlinkFid.Fid.Unique == 0) {
347                     tvc =
348                         afs_LookupVCache(&unlinkFid, &treq, &cached, adp,
349                                          aname);
350                 } else {
351                     ObtainReadLock(&afs_xvcache);
352                     tvc = afs_FindVCache(&unlinkFid, 0, DO_STATS);
353                     ReleaseReadLock(&afs_xvcache);
354                 }
355             }
356         }
357
358 #if defined(AFS_DISCON_ENV)
359     if (AFS_IS_DISCON_RW) {
360         if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) {
361             /* Make shadow copy of parent dir. */
362             afs_MakeShadowDir(adp, tdc);
363         }
364
365         /* Can't hold a dcache lock whilst we're getting a vcache one */
366         if (tdc)
367             ReleaseSharedLock(&tdc->lock);
368
369         /* XXX - We're holding adp->lock still, and we've got no 
370          * guarantee about whether the ordering matches the lock hierarchy */
371         ObtainWriteLock(&tvc->lock, 713);
372
373         /* If we were locally created, then we don't need to do very
374          * much beyond ensuring that we don't exist anymore */  
375         if (tvc->f.ddirty_flags & VDisconCreate) {
376             afs_DisconRemoveDirty(tvc);
377         } else {
378             /* Add removed file vcache to dirty list. */
379             afs_DisconAddDirty(tvc, VDisconRemove, 1);
380         }
381         adp->f.m.LinkCount--;
382         ReleaseWriteLock(&tvc->lock);
383         if (tdc)
384             ObtainSharedLock(&tdc->lock, 714);
385      }
386 #endif
387
388     if (tvc && osi_Active(tvc)) {
389         /* about to delete whole file, prefetch it first */
390         ReleaseWriteLock(&adp->lock);
391         if (tdc)
392             ReleaseSharedLock(&tdc->lock);
393         ObtainWriteLock(&tvc->lock, 143);
394 #if     defined(AFS_OSF_ENV)
395         afs_Wire(tvc, &treq);
396 #else /* AFS_OSF_ENV */
397         FetchWholeEnchilada(tvc, &treq);
398 #endif
399         ReleaseWriteLock(&tvc->lock);
400         ObtainWriteLock(&adp->lock, 144);
401         /* Technically I don't think we need this back, but let's hold it 
402            anyway; The "got" reference should actually be sufficient. */
403         if (tdc) 
404             ObtainSharedLock(&tdc->lock, 640);
405     }
406
407     osi_dnlc_remove(adp, aname, tvc);
408
409     Tadp1 = adp;
410 #ifndef AFS_DARWIN80_ENV
411     Tadpr = VREFCOUNT(adp);
412 #endif
413     Ttvc = tvc;
414     Tnam = aname;
415     Tnam1 = 0;
416 #ifndef AFS_DARWIN80_ENV
417     if (tvc)
418         Ttvcr = VREFCOUNT(tvc);
419 #endif
420 #ifdef  AFS_AIX_ENV
421     if (tvc && VREFCOUNT_GT(tvc, 2) && tvc->opens > 0
422         && !(tvc->f.states & CUnlinked)) {
423 #else
424     if (tvc && VREFCOUNT_GT(tvc, 1) && tvc->opens > 0
425         && !(tvc->f.states & CUnlinked)) {
426 #endif
427         char *unlname = afs_newname();
428
429         ReleaseWriteLock(&adp->lock);
430         if (tdc)
431             ReleaseSharedLock(&tdc->lock);
432         code = afsrename(adp, aname, adp, unlname, acred, &treq);
433         Tnam1 = unlname;
434         if (!code) {
435             struct VenusFid *oldmvid = NULL;
436             if (tvc->mvid) 
437                 oldmvid = tvc->mvid;
438             tvc->mvid = (struct VenusFid *)unlname;
439             if (oldmvid)
440                 osi_FreeSmallSpace(oldmvid);
441             crhold(acred);
442             if (tvc->uncred) {
443                 crfree(tvc->uncred);
444             }
445             tvc->uncred = acred;
446             tvc->f.states |= CUnlinked;
447 #if defined(AFS_DISCON_ENV)
448             /* if rename succeeded, remove should not */
449             ObtainWriteLock(&tvc->lock, 715);
450             if (tvc->f.ddirty_flags & VDisconRemove) {
451                 tvc->f.ddirty_flags &= ~VDisconRemove;
452             }
453             ReleaseWriteLock(&tvc->lock);
454 #endif
455         } else {
456             osi_FreeSmallSpace(unlname);
457         }
458         if (tdc)
459             afs_PutDCache(tdc);
460         afs_PutVCache(tvc);
461     } else {
462         code = afsremove(adp, tdc, tvc, aname, acred, &treq);
463     }
464     afs_PutFakeStat(&fakestate);
465 #ifndef AFS_DARWIN80_ENV
466     /* we can't track by thread, it's not exported in the KPI; only do
467        this on !macos */
468     osi_Assert(!WriteLocked(&adp->lock) || (adp->lock.pid_writer != MyPidxx));
469 #endif
470     return code;
471 }
472
473
474 /* afs_remunlink -- This tries to delete the file at the server after it has
475  *     been renamed when unlinked locally but now has been finally released.
476  *
477  * CAUTION -- may be called with avc unheld. */
478
479 int
480 afs_remunlink(register struct vcache *avc, register int doit)
481 {
482     struct AFS_UCRED *cred;
483     char *unlname;
484     struct vcache *adp;
485     struct vrequest treq;
486     struct VenusFid dirFid;
487     register struct dcache *tdc;
488     afs_int32 code = 0;
489
490     if (NBObtainWriteLock(&avc->lock, 423))
491         return 0;
492 #if defined(AFS_DARWIN80_ENV)
493     if (vnode_get(AFSTOV(avc))) {
494         ReleaseWriteLock(&avc->lock);
495         return 0;
496     }
497 #endif
498
499     if (avc->mvid && (doit || (avc->f.states & CUnlinkedDel))) {
500         if ((code = afs_InitReq(&treq, avc->uncred))) {
501             ReleaseWriteLock(&avc->lock);
502         } else {
503             /* Must bump the refCount because GetVCache may block.
504              * Also clear mvid so no other thread comes here if we block.
505              */
506             unlname = (char *)avc->mvid;
507             avc->mvid = NULL;
508             cred = avc->uncred;
509             avc->uncred = NULL;
510
511 #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV)
512             VREF(AFSTOV(avc));
513 #else
514             VN_HOLD(AFSTOV(avc));
515 #endif
516
517             /* We'll only try this once. If it fails, just release the vnode.
518              * Clear after doing hold so that NewVCache doesn't find us yet.
519              */
520             avc->f.states &= ~(CUnlinked | CUnlinkedDel);
521
522             ReleaseWriteLock(&avc->lock);
523
524             dirFid.Cell = avc->f.fid.Cell;
525             dirFid.Fid.Volume = avc->f.fid.Fid.Volume;
526             dirFid.Fid.Vnode = avc->f.parent.vnode;
527             dirFid.Fid.Unique = avc->f.parent.unique;
528             adp = afs_GetVCache(&dirFid, &treq, NULL, NULL);
529
530             if (adp) {
531                 tdc = afs_FindDCache(adp, (afs_size_t) 0);
532                 ObtainWriteLock(&adp->lock, 159);
533                 if (tdc)
534                     ObtainSharedLock(&tdc->lock, 639);
535
536                 /* afsremove releases the adp & tdc locks, and does vn_rele(avc) */
537                 code = afsremove(adp, tdc, avc, unlname, cred, &treq);
538                 afs_PutVCache(adp);
539             } else {
540                 /* we failed - and won't be back to try again. */
541                 afs_PutVCache(avc);
542             }
543             osi_FreeSmallSpace(unlname);
544             crfree(cred);
545         }
546     } else {
547 #if defined(AFS_DARWIN80_ENV)
548         vnode_put(AFSTOV(avc));
549 #endif
550         ReleaseWriteLock(&avc->lock);
551     }
552
553     return code;
554 }