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