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