Initial fakestat support (fake mountpoint directory attributes).
[openafs.git] / src / afs / VNOPS / afs_vnop_dirops.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  * afs_vnop_dirops.c - make and remove directories
12  *
13  * Implements:
14  *
15  * afs_mkdir
16  * afs_rmdir
17  *
18  */
19
20 #include <afsconfig.h>
21 #include "../afs/param.h"
22
23 RCSID("$Header$");
24
25 #include "../afs/sysincludes.h" /* Standard vendor system headers */
26 #include "../afs/afsincludes.h" /* Afs-based standard headers */
27 #include "../afs/afs_stats.h" /* statistics */
28 #include "../afs/afs_cbqueue.h"
29 #include "../afs/nfsclient.h"
30 #include "../afs/afs_osidnlc.h"
31
32 extern afs_rwlock_t afs_xvcache;
33 extern afs_rwlock_t afs_xcbhash;
34
35 /* don't set CDirty in here because RPC is called synchronously */
36
37 #ifdef  AFS_OSF_ENV
38 afs_mkdir(ndp, attrs)
39     struct nameidata *ndp;
40     struct vattr *attrs; {
41     register struct vcache *adp = VTOAFS(ndp->ni_dvp);
42     char *aname = ndp->ni_dent.d_name;
43     register struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
44     struct ucred *acred = ndp->ni_cred;
45 #else   /* AFS_OSF_ENV */
46 afs_mkdir(OSI_VC_ARG(adp), aname, attrs, avcp, acred)
47     OSI_VC_DECL(adp);
48     register struct vcache **avcp;
49     char *aname;
50     struct vattr *attrs;
51     struct AFS_UCRED *acred; {
52 #endif
53     struct vrequest treq;
54     register afs_int32 code;
55     register struct conn *tc;
56     struct VenusFid newFid;
57     register struct dcache *tdc;
58     afs_size_t offset, len;
59     register struct vcache *tvc;
60     struct AFSStoreStatus InStatus;
61     struct AFSFetchStatus OutFidStatus, OutDirStatus;
62     struct AFSCallBack CallBack;
63     struct AFSVolSync tsync;
64     afs_int32 now;
65     struct afs_fakestat_state fakestate;
66     XSTATS_DECLS
67     OSI_VC_CONVERT(adp)
68
69     AFS_STATCNT(afs_mkdir);
70     afs_Trace2(afs_iclSetp, CM_TRACE_MKDIR, ICL_TYPE_POINTER, adp,
71                ICL_TYPE_STRING, aname);
72
73     if (code = afs_InitReq(&treq, acred)) 
74         goto done2;
75     afs_InitFakeStat(&fakestate);
76
77     if (strlen(aname) > AFSNAMEMAX) {
78         code = ENAMETOOLONG;
79         goto done;
80     }
81
82     if (!afs_ENameOK(aname)) {
83         code = EINVAL;
84         goto done;
85     }
86     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
87     if (code) goto done;
88     code = afs_VerifyVCache(adp, &treq);
89     if (code) goto done;
90
91     /** If the volume is read-only, return error without making an RPC to the
92       * fileserver
93       */
94     if ( adp->states & CRO ) {
95         code = EROFS;
96         goto done;
97     }
98
99     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
100     InStatus.ClientModTime = osi_Time();
101     InStatus.UnixModeBits = attrs->va_mode & 0xffff;   /* only care about protection bits */
102     InStatus.Group = (afs_int32)acred->cr_gid;
103     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);
104     ObtainWriteLock(&adp->lock,153);
105     do {
106         tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
107         if (tc) {
108           XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR);
109           now = osi_Time();
110 #ifdef RX_ENABLE_LOCKS
111           AFS_GUNLOCK();
112 #endif /* RX_ENABLE_LOCKS */
113           code = RXAFS_MakeDir(tc->id, (struct AFSFid *) &adp->fid.Fid, aname,
114                               &InStatus, (struct AFSFid *) &newFid.Fid,
115                               &OutFidStatus, &OutDirStatus, &CallBack, &tsync);
116 #ifdef RX_ENABLE_LOCKS
117           AFS_GLOCK();
118 #endif /* RX_ENABLE_LOCKS */
119           XSTATS_END_TIME;
120             CallBack.ExpirationTime += now;
121             /* DON'T forget to Set the callback value... */
122         }
123         else code = -1;
124     } while
125       (afs_Analyze(tc, code, &adp->fid, &treq,
126                    AFS_STATS_FS_RPCIDX_MAKEDIR, SHARED_LOCK, (struct cell *)0));
127
128     if (code) {
129         if (code < 0) {
130           ObtainWriteLock(&afs_xcbhash, 490);
131           afs_DequeueCallback(adp);
132           adp->states &= ~CStatd;
133           ReleaseWriteLock(&afs_xcbhash);
134           osi_dnlc_purgedp(adp);
135         }
136         ReleaseWriteLock(&adp->lock);
137         if (tdc) afs_PutDCache(tdc);
138         goto done;
139     }
140     /* otherwise, we should see if we can make the change to the dir locally */
141     if (tdc) ObtainWriteLock(&tdc->lock, 632);
142     if (afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
143         /* we can do it locally */
144         code = afs_dir_Create(&tdc->f.inode, aname, &newFid.Fid);
145         if (code) {
146             ZapDCE(tdc);        /* surprise error -- use invalid value */
147             DZap(&tdc->f.inode);
148         }
149     }
150     if (tdc) {
151         ReleaseWriteLock(&tdc->lock);
152         afs_PutDCache(tdc);
153     }
154     adp->m.LinkCount = OutDirStatus.LinkCount;
155     newFid.Cell = adp->fid.Cell;
156     newFid.Fid.Volume = adp->fid.Fid.Volume;
157     ReleaseWriteLock(&adp->lock);
158     /* now we're done with parent dir, create the real dir's cache entry */
159     tvc = afs_GetVCache(&newFid, &treq, (afs_int32 *)0, (struct vcache*)0, 0);
160     if (tvc) {
161         code = 0;
162         *avcp = tvc;
163     }
164     else code = ENOENT;
165 done:
166     afs_PutFakeStat(&fakestate);
167     code = afs_CheckCode(code, &treq, 26);
168 done2:
169 #ifdef  AFS_OSF_ENV
170     AFS_RELE(ndp->ni_dvp);
171 #endif  /* AFS_OSF_ENV */
172     return code;
173 }
174
175
176 #ifdef  AFS_OSF_ENV
177 afs_rmdir(ndp)
178     struct nameidata *ndp; {
179     register struct vcache *adp = VTOAFS(ndp->ni_dvp);
180     char *aname = ndp->ni_dent.d_name;
181     struct ucred *acred = ndp->ni_cred;
182 #else   /* AFS_OSF_ENV */
183 /* don't set CDirty in here because RPC is called synchronously */
184 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
185 afs_rmdir(OSI_VC_ARG(adp), aname, cdirp, acred)
186     struct vnode *cdirp;
187 #else
188 afs_rmdir(adp, aname, acred)
189 #endif
190     OSI_VC_DECL(adp);
191     char *aname;
192     struct AFS_UCRED *acred; {
193 #endif
194     struct vrequest treq;
195     register struct dcache *tdc;
196     register struct vcache *tvc = (struct vcache *)0;
197     register afs_int32 code;
198     register struct conn *tc;
199     afs_size_t offset, len;
200     struct AFSFetchStatus OutDirStatus;
201     struct AFSVolSync tsync;
202     struct afs_fakestat_state fakestate;
203     XSTATS_DECLS
204     OSI_VC_CONVERT(adp)
205
206     AFS_STATCNT(afs_rmdir);
207
208     afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp, 
209                ICL_TYPE_STRING, aname);
210
211     if (code = afs_InitReq(&treq, acred))
212         goto done2;
213     afs_InitFakeStat(&fakestate);
214
215     if (strlen(aname) > AFSNAMEMAX) {
216         code = ENAMETOOLONG;
217         goto done;
218     }
219
220     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
221     if (code)
222         goto done;
223
224     code = afs_VerifyVCache(adp, &treq);
225     if (code) goto done;
226
227     /** If the volume is read-only, return error without making an RPC to the
228       * fileserver
229       */
230     if ( adp->states & CRO ) {
231         code = EROFS;
232         goto done;
233     }
234
235     tdc = afs_GetDCache(adp, (afs_size_t) 0,    &treq, &offset, &len, 1);       /* test for error below */
236     ObtainWriteLock(&adp->lock,154);
237     if (tdc) ObtainSharedLock(&tdc->lock, 633);
238     if (tdc && (adp->states & CForeign)) {
239         struct VenusFid unlinkFid;
240
241         unlinkFid.Fid.Vnode = 0;
242         code = afs_dir_Lookup(&tdc->f.inode, aname, &unlinkFid.Fid);
243         if (code == 0) {        
244             afs_int32 cached=0;
245
246             unlinkFid.Cell = adp->fid.Cell;
247             unlinkFid.Fid.Volume = adp->fid.Fid.Volume;
248             if (unlinkFid.Fid.Unique == 0) {
249                 tvc = afs_LookupVCache(&unlinkFid, &treq, &cached, 
250                                        WRITE_LOCK, adp, aname);
251             } else {
252                 ObtainReadLock(&afs_xvcache);
253                 tvc = afs_FindVCache(&unlinkFid, 1, WRITE_LOCK, 
254                                      0, 1/* do xstats */);
255                 ReleaseReadLock(&afs_xvcache);
256             }
257         }
258     }
259
260     do {
261         tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
262         if (tc) {
263           XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
264 #ifdef RX_ENABLE_LOCKS
265           AFS_GUNLOCK();
266 #endif /* RX_ENABLE_LOCKS */
267             code = RXAFS_RemoveDir(tc->id, (struct AFSFid *) &adp->fid.Fid,
268                                    aname, &OutDirStatus, &tsync);
269 #ifdef RX_ENABLE_LOCKS
270           AFS_GLOCK();
271 #endif /* RX_ENABLE_LOCKS */
272           XSTATS_END_TIME;
273         }
274         else code = -1;
275     } while
276       (afs_Analyze(tc, code, &adp->fid, &treq,
277                    AFS_STATS_FS_RPCIDX_REMOVEDIR, SHARED_LOCK, (struct cell *)0));
278
279     if (code) {
280         if (tdc) {
281             ReleaseSharedLock(&tdc->lock);
282             afs_PutDCache(tdc);
283         }
284         if (code < 0) {
285           ObtainWriteLock(&afs_xcbhash, 491);
286           afs_DequeueCallback(adp);
287           adp->states &= ~CStatd;
288           ReleaseWriteLock(&afs_xcbhash);
289           osi_dnlc_purgedp(adp);
290         }
291         ReleaseWriteLock(&adp->lock);
292         goto done;
293     }
294     /* here if rpc worked; update the in-core link count */
295     adp->m.LinkCount = OutDirStatus.LinkCount;
296     if (tdc) UpgradeSToWLock(&tdc->lock, 634);
297     if (afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
298         /* we can do it locally */
299         code = afs_dir_Delete(&tdc->f.inode, aname);
300         if (code) { 
301             ZapDCE(tdc);        /* surprise error -- invalid value */
302             DZap(&tdc->f.inode);
303         }
304     }
305     if (tdc) {
306         ReleaseWriteLock(&tdc->lock);
307         afs_PutDCache(tdc);     /* drop ref count */
308     }
309
310
311     if (tvc) {
312         osi_dnlc_purgedp (tvc);  /* get rid of any entries for this directory */
313         afs_symhint_inval(tvc);
314     } else
315         osi_dnlc_remove (adp, aname, 0);
316
317     if (tvc) {
318         ObtainWriteLock(&tvc->lock,155);
319         tvc->states &= ~CUnique;                /* For the dfs xlator */
320         ReleaseWriteLock(&tvc->lock);
321         afs_PutVCache(tvc, WRITE_LOCK);
322     }
323     ReleaseWriteLock(&adp->lock);
324     /* don't worry about link count since dirs can not be hardlinked */
325     code = 0;
326
327 done:
328     afs_PutFakeStat(&fakestate);
329     code = afs_CheckCode(code, &treq, 27); 
330 done2:
331 #ifdef  AFS_OSF_ENV
332     afs_PutVCache(adp, 0);
333     afs_PutVCache(ndp->ni_vp, 0);
334 #endif  /* AFS_OSF_ENV */
335     return code;
336 }