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