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