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