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