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