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