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