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