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