viced: fix get-statistics64 buffer overflow
[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
24 #include "afs/sysincludes.h"    /* Standard vendor system headers */
25 #include "afsincludes.h"        /* Afs-based standard headers */
26 #include "afs/afs_stats.h"      /* statistics */
27 #include "afs/afs_cbqueue.h"
28 #include "afs/nfsclient.h"
29 #include "afs/afs_osidnlc.h"
30
31 extern afs_rwlock_t afs_xvcache;
32 extern afs_rwlock_t afs_xcbhash;
33
34 /* don't set CDirty in here because RPC is called synchronously */
35
36 int
37 afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, 
38      struct vcache **avcp, afs_ucred_t *acred)
39 {
40     struct vrequest treq;
41     afs_int32 code;
42     struct afs_conn *tc;
43     struct rx_connection *rxconn;
44     struct VenusFid newFid;
45     struct dcache *tdc;
46     struct dcache *new_dc;
47     afs_size_t offset, len;
48     struct vcache *tvc;
49     struct AFSStoreStatus InStatus;
50     struct AFSFetchStatus OutFidStatus, OutDirStatus;
51     struct AFSCallBack CallBack;
52     struct AFSVolSync tsync;
53     afs_int32 now;
54     struct afs_fakestat_state fakestate;
55     XSTATS_DECLS;
56     OSI_VC_CONVERT(adp);
57
58     AFS_STATCNT(afs_mkdir);
59     afs_Trace2(afs_iclSetp, CM_TRACE_MKDIR, ICL_TYPE_POINTER, adp,
60                ICL_TYPE_STRING, aname);
61
62     if ((code = afs_InitReq(&treq, acred)))
63         goto done2;
64     afs_InitFakeStat(&fakestate);
65
66     if (strlen(aname) > AFSNAMEMAX) {
67         code = ENAMETOOLONG;
68         goto done3;
69     }
70
71     if (!afs_ENameOK(aname)) {
72         code = EINVAL;
73         goto done3;
74     }
75     
76     AFS_DISCON_LOCK();
77
78     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
79     if (code)
80         goto done;
81     code = afs_VerifyVCache(adp, &treq);
82     if (code)
83         goto done;
84
85     /** If the volume is read-only, return error without making an RPC to the
86       * fileserver
87       */
88     if (adp->f.states & CRO) {
89         code = EROFS;
90         goto done;
91     }
92    
93     if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
94         /*printf("Network is down in afs_mkdir\n");*/
95         code = ENETDOWN;
96         goto done;
97     }
98     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
99     InStatus.ClientModTime = osi_Time();
100     InStatus.UnixModeBits = attrs->va_mode & 0xffff;    /* only care about protection bits */
101     InStatus.Group = (afs_int32) afs_cr_gid(acred);
102     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);
103     ObtainWriteLock(&adp->lock, 153);
104
105     if (!AFS_IS_DISCON_RW) {
106         do {
107             tc = afs_Conn(&adp->f.fid, &treq, SHARED_LOCK, &rxconn);
108             if (tc) {
109                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR);
110                 now = osi_Time();
111                 RX_AFS_GUNLOCK();
112                 code =
113                     RXAFS_MakeDir(rxconn,
114                                 (struct AFSFid *)&adp->f.fid.Fid,
115                                 aname,
116                                 &InStatus,
117                                 (struct AFSFid *)&newFid.Fid,
118                                 &OutFidStatus,
119                                 &OutDirStatus,
120                                 &CallBack,
121                                 &tsync);
122                 RX_AFS_GLOCK();
123                 XSTATS_END_TIME;
124                 CallBack.ExpirationTime += now;
125                 /* DON'T forget to Set the callback value... */
126             } else
127                 code = -1;
128         } while (afs_Analyze
129                     (tc, rxconn, code, &adp->f.fid, &treq, AFS_STATS_FS_RPCIDX_MAKEDIR,
130                      SHARED_LOCK, NULL));
131
132         if (code) {
133             if (code < 0) {
134                 ObtainWriteLock(&afs_xcbhash, 490);
135                 afs_DequeueCallback(adp);
136                 adp->f.states &= ~CStatd;
137                 ReleaseWriteLock(&afs_xcbhash);
138                 osi_dnlc_purgedp(adp);
139             }
140             ReleaseWriteLock(&adp->lock);
141             if (tdc)
142                 afs_PutDCache(tdc);
143             goto done;
144         }
145
146     } else {
147         /* Disconnected. */
148
149         /* We have the dir entry now, we can use it while disconnected. */
150         if (adp->mvid == NULL) {
151             /* If not mount point, generate a new fid. */
152             newFid.Cell = adp->f.fid.Cell;
153             newFid.Fid.Volume = adp->f.fid.Fid.Volume;
154             afs_GenFakeFid(&newFid, VDIR, 1);
155         }
156         /* XXX: If mount point???*/
157
158         /* Operations with the actual dir's cache entry are further
159          * down, where the dir entry gets created.
160          */
161     }                   /* if (!AFS_IS_DISCON_RW) */
162
163     /* otherwise, we should see if we can make the change to the dir locally */
164     if (tdc)
165         ObtainWriteLock(&tdc->lock, 632);
166     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
167         /* we can do it locally */
168         ObtainWriteLock(&afs_xdcache, 294);
169         code = afs_dir_Create(tdc, aname, &newFid.Fid);
170         ReleaseWriteLock(&afs_xdcache);
171         if (code) {
172             ZapDCE(tdc);        /* surprise error -- use invalid value */
173             DZap(tdc);
174         }
175     }
176     if (tdc) {
177         ReleaseWriteLock(&tdc->lock);
178         afs_PutDCache(tdc);
179     }
180
181     if (AFS_IS_DISCON_RW)
182         /* We will have to settle with the local link count. */
183         adp->f.m.LinkCount++;
184     else
185         adp->f.m.LinkCount = OutDirStatus.LinkCount;
186     newFid.Cell = adp->f.fid.Cell;
187     newFid.Fid.Volume = adp->f.fid.Fid.Volume;
188     ReleaseWriteLock(&adp->lock);
189     if (AFS_IS_DISCON_RW) {
190         /* When disconnected, we have to create the full dir here. */
191
192         /* Generate a new vcache and fill it. */
193         tvc = afs_NewVCache(&newFid, NULL);
194         if (tvc) {
195             *avcp = tvc;
196         } else {
197             code = ENOENT;
198             goto done;
199         }
200
201         ObtainWriteLock(&tvc->lock, 738);
202         afs_GenDisconStatus(adp, tvc, &newFid, attrs, &treq, VDIR);
203         ReleaseWriteLock(&tvc->lock);
204
205         /* And now make an empty dir, containing . and .. : */
206         /* Get a new dcache for it first. */
207         new_dc = afs_GetDCache(tvc, (afs_size_t) 0, &treq, &offset, &len, 1);
208         if (!new_dc) {
209             /* printf("afs_mkdir: can't get new dcache for dir.\n"); */
210             code = ENOENT;
211             goto done;
212         }
213
214         ObtainWriteLock(&afs_xdcache, 739);
215         code = afs_dir_MakeDir(new_dc,
216                                (afs_int32 *) &newFid.Fid,
217                                (afs_int32 *) &adp->f.fid.Fid);
218         ReleaseWriteLock(&afs_xdcache);
219         /* if (code) printf("afs_mkdir: afs_dirMakeDir code = %u\n", code); */
220
221         afs_PutDCache(new_dc);
222
223         ObtainWriteLock(&tvc->lock, 731);
224         /* Update length in the vcache. */
225         tvc->f.m.Length = new_dc->f.chunkBytes;
226
227         afs_DisconAddDirty(tvc, VDisconCreate, 1);
228         ReleaseWriteLock(&tvc->lock);
229     } else {
230         /* now we're done with parent dir, create the real dir's cache entry */
231         tvc = afs_GetVCache(&newFid, &treq, NULL, NULL);
232         if (tvc) {
233             code = 0;
234             *avcp = tvc;
235         } else
236             code = ENOENT;
237     }                           /* if (AFS_DISCON_RW) */
238
239   done:
240     AFS_DISCON_UNLOCK();
241   done3:
242     afs_PutFakeStat(&fakestate);
243     code = afs_CheckCode(code, &treq, 26);
244   done2:
245     return code;
246 }
247
248
249 int
250 /* don't set CDirty in here because RPC is called synchronously */
251 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
252 afs_rmdir(OSI_VC_DECL(adp), char *aname, struct vnode *cdirp, 
253           afs_ucred_t *acred)
254 #else
255 afs_rmdir(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred)
256 #endif
257 {
258     struct vrequest treq;
259     struct dcache *tdc;
260     struct vcache *tvc = NULL;
261     afs_int32 code;
262     struct afs_conn *tc;
263     afs_size_t offset, len;
264     struct AFSFetchStatus OutDirStatus;
265     struct AFSVolSync tsync;
266     struct afs_fakestat_state fakestate;
267     struct rx_connection *rxconn;
268     XSTATS_DECLS;
269     OSI_VC_CONVERT(adp);
270
271     AFS_STATCNT(afs_rmdir);
272
273     afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp,
274                ICL_TYPE_STRING, aname);
275
276     if ((code = afs_InitReq(&treq, acred)))
277         goto done2;
278     afs_InitFakeStat(&fakestate);
279
280     if (strlen(aname) > AFSNAMEMAX) {
281         code = ENAMETOOLONG;
282         goto done;
283     }
284
285     AFS_DISCON_LOCK();
286
287     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
288     if (code)
289         goto done;
290
291     code = afs_VerifyVCache(adp, &treq);
292     if (code)
293         goto done;
294
295     /** If the volume is read-only, return error without making an RPC to the
296       * fileserver
297       */
298     if (adp->f.states & CRO) {
299         code = EROFS;
300         goto done;
301     }
302
303    if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
304         /* Disconnected read only mode. */
305         code = ENETDOWN;
306         goto done;
307     }
308
309     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);  /* test for error below */
310     ObtainWriteLock(&adp->lock, 154);
311     if (tdc)
312         ObtainSharedLock(&tdc->lock, 633);
313     if (tdc && (adp->f.states & CForeign)) {
314         struct VenusFid unlinkFid;
315
316         unlinkFid.Fid.Vnode = 0;
317         code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid);
318         if (code == 0) {
319             afs_int32 cached = 0;
320
321             unlinkFid.Cell = adp->f.fid.Cell;
322             unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume;
323             if (unlinkFid.Fid.Unique == 0) {
324                 tvc =
325                     afs_LookupVCache(&unlinkFid, &treq, &cached, adp, aname);
326             } else {
327                 ObtainReadLock(&afs_xvcache);
328                 tvc = afs_FindVCache(&unlinkFid, 0, 1 /* do xstats */ );
329                 ReleaseReadLock(&afs_xvcache);
330             }
331         }
332     }
333
334     if (!AFS_IS_DISCON_RW) {
335         /* Not disconnected, can connect to server. */
336         do {
337             tc = afs_Conn(&adp->f.fid, &treq, SHARED_LOCK, &rxconn);
338             if (tc) {
339                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
340                 RX_AFS_GUNLOCK();
341                 code =
342                     RXAFS_RemoveDir(rxconn,
343                                 (struct AFSFid *)&adp->f.fid.Fid,
344                                 aname,
345                                 &OutDirStatus,
346                                 &tsync);
347                 RX_AFS_GLOCK();
348                 XSTATS_END_TIME;
349             } else
350                 code = -1;
351         } while (afs_Analyze
352                  (tc, rxconn, code, &adp->f.fid, &treq, AFS_STATS_FS_RPCIDX_REMOVEDIR,
353                  SHARED_LOCK, NULL));
354
355         if (code) {
356             if (tdc) {
357                 ReleaseSharedLock(&tdc->lock);
358                 afs_PutDCache(tdc);
359             }
360
361             if (code < 0) {
362                 ObtainWriteLock(&afs_xcbhash, 491);
363                 afs_DequeueCallback(adp);
364                 adp->f.states &= ~CStatd;
365                 ReleaseWriteLock(&afs_xcbhash);
366                 osi_dnlc_purgedp(adp);
367             }
368             ReleaseWriteLock(&adp->lock);
369             goto done;
370         }
371
372         /* here if rpc worked; update the in-core link count */
373         adp->f.m.LinkCount = OutDirStatus.LinkCount;
374
375     } else {
376         /* Disconnected. */
377
378         if (!tdc) {
379             ReleaseWriteLock(&adp->lock);
380             /* printf("afs_rmdir: No local dcache!\n"); */
381             code = ENETDOWN;
382             goto done;
383         }
384         
385         if (!tvc) {
386             /* Find the vcache. */
387             struct VenusFid tfid;
388
389             tfid.Cell = adp->f.fid.Cell;
390             tfid.Fid.Volume = adp->f.fid.Fid.Volume;
391             code = afs_dir_Lookup(tdc, aname, &tfid.Fid);
392
393             ObtainSharedLock(&afs_xvcache, 764);
394             tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ );
395             ReleaseSharedLock(&afs_xvcache);
396             
397             if (!tvc) {
398                 /* printf("afs_rmdir: Can't find dir's vcache!\n"); */
399                 ReleaseSharedLock(&tdc->lock);
400                 afs_PutDCache(tdc);     /* drop ref count */
401                 ReleaseWriteLock(&adp->lock);
402                 code = ENETDOWN;
403                 goto done;
404             }
405         }
406
407         if (tvc->f.m.LinkCount > 2) {
408             /* This dir contains more than . and .., thus it can't be
409              * deleted.
410              */
411             ReleaseSharedLock(&tdc->lock);
412             afs_PutDCache(tdc);
413             afs_PutVCache(tvc);
414             ReleaseWriteLock(&adp->lock);
415             code = ENOTEMPTY;
416             goto done;
417         }
418
419         /* Make a shadow copy of the parent dir (if not done already).
420          * If we were created locally, then we don't need to have a shadow
421          * directory (as there's no server state to remember)
422          */
423         if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) {
424             afs_MakeShadowDir(adp, tdc);
425         }
426
427         adp->f.m.LinkCount--;
428     }                           /* if (!AFS_IS_DISCON_RW) */
429
430     if (tdc)
431         UpgradeSToWLock(&tdc->lock, 634);
432     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
433         /* we can do it locally */
434         code = afs_dir_Delete(tdc, aname);
435         if (code) {
436             ZapDCE(tdc);        /* surprise error -- invalid value */
437             DZap(tdc);
438         }
439     }
440     if (tdc) {
441         ReleaseWriteLock(&tdc->lock);
442         afs_PutDCache(tdc);     /* drop ref count */
443     }
444
445     if (tvc)
446         osi_dnlc_purgedp(tvc);  /* get rid of any entries for this directory */
447     else
448         osi_dnlc_remove(adp, aname, 0);
449
450     if (tvc) {
451         ObtainWriteLock(&tvc->lock, 155);
452         tvc->f.states &= ~CUnique;      /* For the dfs xlator */
453         if (AFS_IS_DISCON_RW) {
454             if (tvc->f.ddirty_flags & VDisconCreate) {
455                 /* If we we were created whilst disconnected, removal doesn't
456                  * need to get logged. Just go away gracefully */
457                 afs_DisconRemoveDirty(tvc);
458             } else {
459                 afs_DisconAddDirty(tvc, VDisconRemove, 1);
460             }
461         }
462         ReleaseWriteLock(&tvc->lock);
463         afs_PutVCache(tvc);
464     }
465     ReleaseWriteLock(&adp->lock);
466     /* don't worry about link count since dirs can not be hardlinked */
467     code = 0;
468
469   done:
470     AFS_DISCON_UNLOCK();
471     afs_PutFakeStat(&fakestate);
472     code = afs_CheckCode(code, &treq, 27);
473   done2:
474     return code;
475 }