dcache-finegrained-locks-20011121
[openafs.git] / src / afs / VNOPS / afs_vnop_create.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_create
13  * afs_LocalHero
14  */
15
16 #include <afsconfig.h>
17 #include "../afs/param.h"
18
19 RCSID("$Header$");
20
21 #include "../afs/sysincludes.h" /* Standard vendor system headers */
22 #include "../afs/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"
27
28 extern afs_rwlock_t afs_xvcache;
29 extern afs_rwlock_t afs_xcbhash;
30
31 /* question: does afs_create need to set CDirty in the adp or the avc?
32  * I think we can get away without it, but I'm not sure.  Note that
33  * afs_setattr is called in here for truncation.
34  */
35 #ifdef  AFS_OSF_ENV
36 afs_create(ndp, attrs)
37     struct nameidata *ndp;
38     struct vattr *attrs; {
39     register struct vcache *adp = (struct vcache *)ndp->ni_dvp;
40     char *aname = ndp->ni_dent.d_name;
41     enum vcexcl aexcl = NONEXCL; /* XXX - create called properly */
42     int amode = 0; /* XXX - checked in higher level */
43     struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
44     struct ucred *acred = ndp->ni_cred;
45 #else   /* AFS_OSF_ENV */
46 #ifdef AFS_SGI64_ENV
47 afs_create(OSI_VC_ARG(adp), aname, attrs, flags, amode, avcp, acred)
48     int flags;
49 #else /* AFS_SGI64_ENV */
50 afs_create(OSI_VC_ARG(adp), aname, attrs, aexcl, amode, avcp, acred)
51     enum vcexcl aexcl;
52 #endif /* AFS_SGI64_ENV */
53     OSI_VC_DECL(adp);
54     char *aname;
55     struct vattr *attrs;
56     int amode;
57     struct vcache **avcp;
58     struct AFS_UCRED *acred; {
59 #endif /* AFS_OSF_ENV */
60     afs_int32 origCBs, origZaps, finalZaps;
61     struct vrequest treq;
62     register afs_int32 code;
63     register struct conn *tc;
64     struct VenusFid newFid;
65     struct AFSStoreStatus InStatus;
66     struct AFSFetchStatus OutFidStatus, OutDirStatus;
67     struct AFSVolSync tsync;
68     struct AFSCallBack CallBack;
69     afs_int32 now;
70     struct dcache *tdc;
71     afs_size_t offset, len;
72     struct server *hostp=0;
73     struct vcache *tvc;
74     struct volume*      volp = 0;
75     XSTATS_DECLS
76     OSI_VC_CONVERT(adp)
77
78
79     AFS_STATCNT(afs_create);
80     if (code = afs_InitReq(&treq, acred)) return code;
81     afs_Trace3(afs_iclSetp, CM_TRACE_CREATE, ICL_TYPE_POINTER, adp,
82                ICL_TYPE_STRING, aname, ICL_TYPE_INT32, amode);
83
84 #ifdef AFS_SGI65_ENV
85     /* If avcp is passed not null, it's the old reference to this file.
86      * We can use this to avoid create races. For now, just decrement
87      * the reference count on it.
88      */
89     if (*avcp) {
90         AFS_RELE((struct vnode*)(*avcp));
91         *avcp = NULL;
92     }
93 #endif
94
95     if (!afs_ENameOK(aname)) {
96         code = EINVAL;
97         goto done;
98     }
99 #if     defined(AFS_SUN5_ENV)
100     if ((attrs->va_type == VBLK) || (attrs->va_type == VCHR)) {
101 #else
102     if ((attrs->va_type == VBLK) || (attrs->va_type == VCHR) || (attrs->va_type == VSOCK)) {
103 #endif
104         /* We don't support special devices */
105         code = EINVAL;          
106         goto done;
107     }
108 tagain:
109     code = afs_VerifyVCache(adp, &treq);
110     if (code) goto done;
111
112     /** If the volume is read-only, return error without making an RPC to the
113       * fileserver
114       */
115     if ( adp->states & CRO ) {
116         code = EROFS;
117         goto done;
118     }
119
120     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);
121     ObtainWriteLock(&adp->lock,135);
122     if (tdc) ObtainSharedLock(&tdc->lock,630);
123
124     /*
125      * Make sure that the data in the cache is current. We may have
126      * received a callback while we were waiting for the write lock.
127      */
128     if (!(adp->states & CStatd)
129         || (tdc && !hsame(adp->m.DataVersion, tdc->f.versionNo))) {
130         ReleaseWriteLock(&adp->lock);
131         if (tdc) {
132             ReleaseSharedLock(&tdc->lock);
133             afs_PutDCache(tdc);
134         }
135         goto tagain;
136     }
137     if (tdc) {
138         /* see if file already exists.  If it does, we only set 
139          * the size attributes (to handle O_TRUNC) */
140         code = afs_dir_Lookup(&tdc->f.inode, aname, &newFid.Fid); /* use dnlc first xxx */
141         if (code == 0) {
142             ReleaseSharedLock(&tdc->lock);
143             afs_PutDCache(tdc);
144             ReleaseWriteLock(&adp->lock);
145 #ifdef AFS_SGI64_ENV
146             if (flags & VEXCL) {
147 #else
148             if (aexcl != NONEXCL) {
149 #endif
150                 code = EEXIST;      /* file exists in excl mode open */
151                 goto done;
152             }
153             /* found the file, so use it */
154             newFid.Cell = adp->fid.Cell;
155             newFid.Fid.Volume = adp->fid.Fid.Volume;
156             tvc = (struct vcache *)0;
157             if (newFid.Fid.Unique == 0) {
158                 tvc = afs_LookupVCache(&newFid, &treq, (afs_int32 *)0, 
159                                        WRITE_LOCK, adp, aname); 
160             }
161             if (!tvc)  /* lookup failed or wasn't called */
162                tvc = afs_GetVCache(&newFid, &treq, (afs_int32 *)0,
163                                    (struct vcache*)0, WRITE_LOCK);
164
165             if (tvc) {
166                 /* if the thing exists, we need the right access to open it.
167                  * we must check that here, since no other checks are
168                  * made by the open system call */
169                 len = attrs->va_size;   /* only do the truncate */
170                 /*
171                  * We used to check always for READ access before; the
172                  * problem is that we will fail if the existing file
173                  * has mode -w-w-w, which is wrong.
174                  */
175                 if ((amode & VREAD) && 
176                       !afs_AccessOK(tvc, PRSFS_READ, &treq, CHECK_MODE_BITS)) { 
177                    afs_PutVCache(tvc, READ_LOCK);
178                    code = EACCES;
179                    goto done;
180                 }
181
182 #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
183                 if ((amode & VWRITE) || (attrs->va_mask & AT_SIZE)) 
184 #else
185                 if ((amode & VWRITE) || len != 0xffffffff) 
186 #endif
187                   {
188                     /* needed for write access check */
189                     tvc->parentVnode = adp->fid.Fid.Vnode;
190                     tvc->parentUnique = adp->fid.Fid.Unique;
191                     /* need write mode for these guys */
192                     if (!afs_AccessOK(tvc, PRSFS_WRITE, &treq, CHECK_MODE_BITS)) {
193                         afs_PutVCache(tvc, READ_LOCK);
194                         code = EACCES;
195                         goto done;
196                     }
197                 }
198 #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
199                 if (attrs->va_mask & AT_SIZE) 
200 #else
201                 if (len != 0xffffffff) 
202 #endif
203                   {
204                     if (vType(tvc) != VREG) {
205                         afs_PutVCache(tvc, READ_LOCK);
206                         code = EISDIR;
207                         goto done;
208                     }
209                     /* do a truncate */
210 #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
211                     attrs->va_mask = AT_SIZE;
212 #else
213                     VATTR_NULL(attrs);
214 #endif
215                     attrs->va_size = len;
216                     ObtainWriteLock(&tvc->lock,136);
217                     tvc->states |= CCreating;
218                     ReleaseWriteLock(&tvc->lock);
219 #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
220 #if defined(AFS_SGI64_ENV)
221                     code = afs_setattr(VNODE_TO_FIRST_BHV((vnode_t*)tvc),
222                                        attrs, 0, acred);
223 #else
224                     code = afs_setattr(tvc, attrs, 0, acred);
225 #endif /* AFS_SGI64_ENV */
226 #else /* SUN5 || SGI */
227                     code = afs_setattr(tvc, attrs, acred);
228 #endif /* SUN5 || SGI */
229                     ObtainWriteLock(&tvc->lock,137);
230                     tvc->states &= ~CCreating;
231                     ReleaseWriteLock(&tvc->lock);
232                     if (code) {
233                         afs_PutVCache(tvc, 0);
234                         goto done;
235                     }
236                 }
237                 *avcp = tvc;
238             }
239             else code = ENOENT;  /* shouldn't get here */
240             /* make sure vrefCount bumped only if code == 0 */
241             goto done;
242         }
243     }
244
245     /* if we create the file, we don't do any access checks, since
246      * that's how O_CREAT is supposed to work */
247     if (adp->states & CForeign) {
248         origCBs = afs_allCBs;   
249         origZaps = afs_allZaps; 
250     } else {
251         origCBs = afs_evenCBs; /* if changes, we don't really have a callback */
252         origZaps = afs_evenZaps;  /* number of even numbered vnodes discarded */
253     }
254     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
255     InStatus.ClientModTime = osi_Time();
256     InStatus.Group = (afs_int32)acred->cr_gid;
257     if (AFS_NFSXLATORREQ(acred)) {
258         /*
259          * XXX The following is mainly used to fix a bug in the HP-UX
260          * nfs client where they create files with mode of 0 without
261          * doing any setattr later on to fix it.  * XXX
262          */
263 #if     defined(AFS_AIX_ENV)
264         if (attrs->va_mode != -1) {
265 #else
266 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
267         if (attrs->va_mask & AT_MODE) {
268 #else
269         if (attrs->va_mode != ((unsigned short)-1)) {
270 #endif
271 #endif
272             if (!attrs->va_mode)
273                 attrs->va_mode = 0x1b6;     /* XXX default mode: rw-rw-rw XXX */
274         }
275     }
276     InStatus.UnixModeBits = attrs->va_mode & 0xffff;   /* only care about protection bits */
277     do {
278         tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
279         if (tc) {
280             hostp = tc->srvr->server;       /* remember for callback processing */
281             now = osi_Time();
282             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE);
283 #ifdef RX_ENABLE_LOCKS
284             AFS_GUNLOCK();
285 #endif /* RX_ENABLE_LOCKS */
286             code = RXAFS_CreateFile(tc->id, (struct AFSFid *) &adp->fid.Fid,
287                                     aname, &InStatus, (struct AFSFid *)
288                                     &newFid.Fid, &OutFidStatus,
289                                     &OutDirStatus, &CallBack, &tsync);
290 #ifdef RX_ENABLE_LOCKS
291             AFS_GLOCK();
292 #endif /* RX_ENABLE_LOCKS */
293             XSTATS_END_TIME;
294             CallBack.ExpirationTime += now;
295         }
296         else code = -1;
297     } while
298       (afs_Analyze(tc, code, &adp->fid, &treq,
299                    AFS_STATS_FS_RPCIDX_CREATEFILE, SHARED_LOCK, (struct cell *)0));
300
301 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV)
302     if (code == EEXIST && aexcl == NONEXCL) {
303         /* This lookup was handled in the common vn_open code in the
304            vnode layer */
305         if (tdc) {
306             ReleaseSharedLock(&tdc->lock);
307             afs_PutDCache(tdc);
308         }
309         ReleaseWriteLock(&adp->lock);
310         goto done;
311     }
312 #else   /* AFS_OSF_ENV */
313 #ifdef AFS_SGI64_ENV
314     if (code == EEXIST && !(flags & VEXCL)) {
315 #else /* AFS_SGI64_ENV */
316     if (code == EEXIST && aexcl == NONEXCL) {
317 #endif /* AFS_SGI64_ENV */
318         /* if we get an EEXIST in nonexcl mode, just do a lookup */
319         if (tdc) {
320             ReleaseSharedLock(&tdc->lock);
321             afs_PutDCache(tdc);
322         }
323         ReleaseWriteLock(&adp->lock);
324 #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
325 #if defined(AFS_SGI64_ENV)
326         code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t*)adp), aname, avcp,
327                           (struct pathname *)0, 0,
328                           (struct vnode *)0, acred);
329 #else
330         code = afs_lookup(adp, aname, avcp, (struct pathname *)0, 0,
331                           (struct vnode *)0, acred);
332 #endif /* AFS_SGI64_ENV */
333 #else /* SUN5 || SGI */
334         code = afs_lookup(adp, aname, avcp, acred);
335 #endif /* SUN5 || SGI */
336         goto done;
337     }
338 #endif /* AFS_OSF_ENV */
339     if (code) {
340         if (code < 0) {
341           ObtainWriteLock(&afs_xcbhash, 488);
342           afs_DequeueCallback(adp);
343           adp->states &= ~CStatd;
344           ReleaseWriteLock(&afs_xcbhash);
345           osi_dnlc_purgedp(adp);
346         }
347         ReleaseWriteLock(&adp->lock);
348         if (tdc) {
349             ReleaseSharedLock(&tdc->lock);
350             afs_PutDCache(tdc);
351         }
352         goto done;
353     }
354     /* otherwise, we should see if we can make the change to the dir locally */
355     if (tdc) UpgradeSToWLock(&tdc->lock, 631);
356     if (afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
357         /* we can do it locally */
358         code = afs_dir_Create(&tdc->f.inode, aname, &newFid.Fid);
359         if (code) {
360            ZapDCE(tdc);
361            DZap(&tdc->f.inode);
362         }
363     }
364     if (tdc) {
365         ReleaseWriteLock(&tdc->lock);
366         afs_PutDCache(tdc);
367     }
368     newFid.Cell = adp->fid.Cell;
369     newFid.Fid.Volume = adp->fid.Fid.Volume;
370     ReleaseWriteLock(&adp->lock);
371     volp = afs_FindVolume(&newFid, READ_LOCK);
372
373     /* New tricky optimistic callback handling algorithm for file creation works
374         as follows.  We create the file essentially with no locks set at all.  File
375         server may thus handle operations from others cache managers as well as from
376         this very own cache manager that reference the file in question before
377         we managed to create the cache entry.  However, if anyone else changes
378         any of the status information for a file, we'll see afs_evenCBs increase
379         (files always have even fids).  If someone on this workstation manages
380         to do something to the file, they'll end up having to create a cache
381         entry for the new file.  Either we'll find it once we've got the afs_xvcache
382         lock set, or it was also *deleted* the vnode before we got there, in which case
383         we will find evenZaps has changed, too.  Thus, we only assume we have the right
384         status information if no callbacks or vnode removals have occurred to even
385         numbered files from the time the call started until the time that we got the xvcache
386         lock set.  Of course, this also assumes that any call that modifies a file first
387         gets a write lock on the file's vnode, but if that weren't true, the whole cache manager
388         would fail, since no call would be able to update the local vnode status after modifying
389         a file on a file server. */
390     ObtainWriteLock(&afs_xvcache,138);
391     if (adp->states & CForeign) 
392         finalZaps = afs_allZaps;            /* do this before calling newvcache */
393     else
394         finalZaps = afs_evenZaps;           /* do this before calling newvcache */
395     /* don't need to call RemoveVCB, since only path leaving a callback is the
396        one where we pass through afs_NewVCache.  Can't have queued a VCB unless
397        we created and freed an entry between file creation time and here, and the
398        freeing of the vnode will change evenZaps.  Don't need to update the VLRU
399        queue, since the find will only succeed in the event of a create race, and 
400        then the vcache will be at the front of the VLRU queue anyway...  */
401     if (!(tvc = afs_FindVCache(&newFid, 0, WRITE_LOCK, 
402                                0, DO_STATS))) {
403         tvc = afs_NewVCache(&newFid, hostp, 0, WRITE_LOCK);
404         if (tvc) {
405             int finalCBs;
406             ObtainWriteLock(&tvc->lock,139);
407
408             ObtainWriteLock(&afs_xcbhash, 489);
409             finalCBs = afs_evenCBs;
410             /* add the callback in */
411             if (adp->states & CForeign) {
412                 tvc->states |= CForeign;
413                 finalCBs = afs_allCBs;
414             }
415             if (origCBs == finalCBs && origZaps == finalZaps) {
416                 tvc->states |= CStatd;  /* we've fake entire thing, so don't stat */
417                 tvc->states &= ~CBulkFetching;
418                 tvc->cbExpires = CallBack.ExpirationTime;
419                 afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp);
420             }
421             else {
422               afs_DequeueCallback(tvc);
423               tvc->states &= ~(CStatd | CUnique);       
424               tvc->callback = 0;
425               if (tvc->fid.Fid.Vnode & 1 || (vType(tvc) == VDIR))
426                 osi_dnlc_purgedp(tvc);
427             }
428             ReleaseWriteLock(&afs_xcbhash);
429             afs_ProcessFS(tvc, &OutFidStatus, &treq);
430             ReleaseWriteLock(&tvc->lock);
431             *avcp = tvc;
432             code = 0;
433         }
434         else code = ENOENT;
435     }
436     else {
437       /* otherwise cache entry already exists, someone else must
438        * have created it.  Comments used to say:  "don't need write
439        * lock to *clear* these flags" but we should do it anyway.
440        * Code used to clear stat bit and callback, but I don't see 
441        * the point -- we didn't have a create race, somebody else just
442        * snuck into NewVCache before we got here, probably a racing 
443        * lookup.
444        */
445         *avcp = tvc;
446         code = 0;
447     }
448     ReleaseWriteLock(&afs_xvcache);
449
450 done:
451     if ( volp )
452         afs_PutVolume(volp, READ_LOCK);
453
454     if (code == 0) {
455         afs_AddMarinerName(aname, *avcp);
456         /* return the new status in vattr */
457         afs_CopyOutAttrs(*avcp, attrs);
458     }
459
460 #ifdef  AFS_OSF_ENV
461     if (!code && !strcmp(aname, "core"))
462         tvc->states |= CCore1;
463     afs_PutVCache(adp, 0);
464 #endif  /* AFS_OSF_ENV */
465
466     code = afs_CheckCode(code, &treq, 20);
467     return code;
468 }
469
470
471 /*
472  * Check to see if we can track the change locally: requires that
473  * we have sufficiently recent info in data cache.  If so, we
474  * know the new DataVersion number, and place it correctly in both the
475  * data and stat cache entries.  This routine returns 1 if we should
476  * do the operation locally, and 0 otherwise.
477  *
478  * This routine must be called with the stat cache entry write-locked,
479  * and dcache entry write-locked.
480  */
481 afs_LocalHero(avc, adc, astat, aincr)
482     register struct vcache *avc;
483     register AFSFetchStatus *astat;
484     register struct dcache *adc;
485     register int aincr; {
486     register afs_int32 ok;
487     afs_hyper_t avers;
488
489     AFS_STATCNT(afs_LocalHero);
490     hset64(avers, astat->dataVersionHigh, astat->DataVersion);
491     /* this *is* the version number, no matter what */
492     if (adc) {
493         ok = (hsame(avc->m.DataVersion, adc->f.versionNo) && avc->callback 
494               && (avc->states & CStatd) && avc->cbExpires >= osi_Time());
495     }
496     else {
497         ok = 0;
498     }
499 #if defined(AFS_SGI_ENV)
500         osi_Assert(avc->v.v_type == VDIR);
501 #endif
502     /* The bulk status code used the length as a sequence number.  */
503     /* Don't update the vcache entry unless the stats are current. */
504     if (avc->states & CStatd) {
505         hset(avc->m.DataVersion, avers);
506 #ifdef AFS_64BIT_CLIENT
507         FillInt64(avc->m.Length, astat->Length_hi, astat->Length);
508 #else /* AFS_64BIT_ENV */
509         avc->m.Length = astat->Length;
510 #endif /* AFS_64BIT_ENV */
511         avc->m.Date = astat->ClientModTime;
512     }
513     if (ok) {
514         /* we've been tracking things correctly */
515         adc->dflags |= DFEntryMod;
516         adc->f.versionNo = avers;
517         return 1;
518     }
519     else {
520         if (adc) {
521             ZapDCE(adc);
522             DZap(&adc->f.inode);
523         }
524         if (avc->states & CStatd) {
525             osi_dnlc_purgedp(avc);
526         }
527         return 0;
528     }
529 }