afs: remove vestigial externs for afs_xcbhash
[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
33 /* don't set CDirty in here because RPC is called synchronously */
34
35 int
36 afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, 
37      struct vcache **avcp, afs_ucred_t *acred)
38 {
39     struct vrequest *treq = NULL;
40     afs_int32 code;
41     struct afs_conn *tc;
42     struct rx_connection *rxconn;
43     struct VenusFid newFid;
44     struct dcache *tdc;
45     struct dcache *new_dc;
46     afs_size_t offset, len;
47     struct vcache *tvc;
48     struct AFSStoreStatus InStatus;
49     struct AFSFetchStatus *OutFidStatus, *OutDirStatus;
50     struct AFSCallBack CallBack;
51     struct AFSVolSync tsync;
52     afs_int32 now;
53     struct afs_fakestat_state fakestate;
54     XSTATS_DECLS;
55     OSI_VC_CONVERT(adp);
56
57     AFS_STATCNT(afs_mkdir);
58     afs_Trace2(afs_iclSetp, CM_TRACE_MKDIR, ICL_TYPE_POINTER, adp,
59                ICL_TYPE_STRING, aname);
60
61     OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
62     OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
63     memset(&InStatus, 0, sizeof(InStatus));
64
65     if ((code = afs_CreateReq(&treq, acred)))
66         goto done2;
67     afs_InitFakeStat(&fakestate);
68
69     if (strlen(aname) > AFSNAMEMAX) {
70         code = ENAMETOOLONG;
71         goto done3;
72     }
73
74     if (!afs_ENameOK(aname)) {
75         code = EINVAL;
76         goto done3;
77     }
78     
79     AFS_DISCON_LOCK();
80
81     code = afs_EvalFakeStat(&adp, &fakestate, treq);
82     if (code)
83         goto done;
84     code = afs_VerifyVCache(adp, treq);
85     if (code)
86         goto done;
87
88     /** If the volume is read-only, return error without making an RPC to the
89       * fileserver
90       */
91     if (adp->f.states & CRO) {
92         code = EROFS;
93         goto done;
94     }
95    
96     if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
97         /*printf("Network is down in afs_mkdir\n");*/
98         code = ENETDOWN;
99         goto done;
100     }
101     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
102     InStatus.ClientModTime = osi_Time();
103     InStatus.UnixModeBits = attrs->va_mode & 0xffff;    /* only care about protection bits */
104     InStatus.Group = (afs_int32) afs_cr_gid(acred);
105     tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);
106     ObtainWriteLock(&adp->lock, 153);
107
108     if (!AFS_IS_DISCON_RW) {
109         do {
110             tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
111             if (tc) {
112                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR);
113                 now = osi_Time();
114                 RX_AFS_GUNLOCK();
115                 code =
116                     RXAFS_MakeDir(rxconn,
117                                 (struct AFSFid *)&adp->f.fid.Fid,
118                                 aname,
119                                 &InStatus,
120                                 (struct AFSFid *)&newFid.Fid,
121                                 OutFidStatus,
122                                 OutDirStatus,
123                                 &CallBack,
124                                 &tsync);
125                 RX_AFS_GLOCK();
126                 XSTATS_END_TIME;
127                 CallBack.ExpirationTime += now;
128                 /* DON'T forget to Set the callback value... */
129             } else
130                 code = -1;
131         } while (afs_Analyze
132                     (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_MAKEDIR,
133                      SHARED_LOCK, NULL));
134
135         if (code) {
136             if (code < 0) {
137                 afs_StaleVCache(adp);
138             }
139             ReleaseWriteLock(&adp->lock);
140             if (tdc)
141                 afs_PutDCache(tdc);
142             goto done;
143         }
144
145     } else {
146         /* Disconnected. */
147
148         /* We have the dir entry now, we can use it while disconnected. */
149         if (adp->mvid.target_root == NULL) {
150             /* If not mount point, generate a new fid. */
151             newFid.Cell = adp->f.fid.Cell;
152             newFid.Fid.Volume = adp->f.fid.Fid.Volume;
153             afs_GenFakeFid(&newFid, VDIR, 1);
154         }
155         /* XXX: If mount point???*/
156
157         /* Operations with the actual dir's cache entry are further
158          * down, where the dir entry gets created.
159          */
160     }                   /* if (!AFS_IS_DISCON_RW) */
161
162     /* otherwise, we should see if we can make the change to the dir locally */
163     if (tdc)
164         ObtainWriteLock(&tdc->lock, 632);
165     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) {
166         /* we can do it locally */
167         ObtainWriteLock(&afs_xdcache, 294);
168         code = afs_dir_Create(tdc, aname, &newFid.Fid);
169         ReleaseWriteLock(&afs_xdcache);
170         if (code) {
171             ZapDCE(tdc);        /* surprise error -- use invalid value */
172             DZap(tdc);
173         }
174     }
175     if (tdc) {
176         ReleaseWriteLock(&tdc->lock);
177         afs_PutDCache(tdc);
178     }
179
180     if (AFS_IS_DISCON_RW)
181         /* We will have to settle with the local link count. */
182         adp->f.m.LinkCount++;
183     else
184         adp->f.m.LinkCount = OutDirStatus->LinkCount;
185     newFid.Cell = adp->f.fid.Cell;
186     newFid.Fid.Volume = adp->f.fid.Fid.Volume;
187     ReleaseWriteLock(&adp->lock);
188     if (AFS_IS_DISCON_RW) {
189         /* When disconnected, we have to create the full dir here. */
190
191         /* Generate a new vcache and fill it. */
192         tvc = afs_NewVCache(&newFid, NULL);
193         if (tvc) {
194             *avcp = tvc;
195         } else {
196             code = EIO;
197             goto done;
198         }
199
200         ObtainWriteLock(&tvc->lock, 738);
201         afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VDIR);
202         ReleaseWriteLock(&tvc->lock);
203
204         /* And now make an empty dir, containing . and .. : */
205         /* Get a new dcache for it first. */
206         new_dc = afs_GetDCache(tvc, (afs_size_t) 0, treq, &offset, &len, 1);
207         if (!new_dc) {
208             /* printf("afs_mkdir: can't get new dcache for dir.\n"); */
209             code = EIO;
210             goto done;
211         }
212
213         ObtainWriteLock(&afs_xdcache, 739);
214         code = afs_dir_MakeDir(new_dc,
215                                (afs_int32 *) &newFid.Fid,
216                                (afs_int32 *) &adp->f.fid.Fid);
217         ReleaseWriteLock(&afs_xdcache);
218         /* if (code) printf("afs_mkdir: afs_dirMakeDir code = %u\n", code); */
219
220         afs_PutDCache(new_dc);
221
222         ObtainWriteLock(&tvc->lock, 731);
223         /* Update length in the vcache. */
224         tvc->f.m.Length = new_dc->f.chunkBytes;
225
226         afs_DisconAddDirty(tvc, VDisconCreate, 1);
227         ReleaseWriteLock(&tvc->lock);
228     } else {
229         /* now we're done with parent dir, create the real dir's cache entry */
230         tvc = afs_GetVCache(&newFid, treq);
231         if (tvc) {
232             code = 0;
233             *avcp = tvc;
234
235         } else {
236             /* For some reason, we cannot fetch the vcache for our
237              * newly-created dir. */
238             code = EIO;
239         }
240     }                           /* if (AFS_DISCON_RW) */
241
242   done:
243     AFS_DISCON_UNLOCK();
244   done3:
245     afs_PutFakeStat(&fakestate);
246     code = afs_CheckCode(code, treq, 26);
247     afs_DestroyReq(treq);
248   done2:
249     osi_FreeSmallSpace(OutFidStatus);
250     osi_FreeSmallSpace(OutDirStatus);
251     return code;
252 }
253
254
255 int
256 /* don't set CDirty in here because RPC is called synchronously */
257 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
258 afs_rmdir(OSI_VC_DECL(adp), char *aname, struct vnode *cdirp, 
259           afs_ucred_t *acred)
260 #else
261 afs_rmdir(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred)
262 #endif
263 {
264     struct vrequest *treq = NULL;
265     struct dcache *tdc;
266     struct vcache *tvc = NULL;
267     afs_int32 code;
268     struct afs_conn *tc;
269     afs_size_t offset, len;
270     struct AFSFetchStatus OutDirStatus;
271     struct AFSVolSync tsync;
272     struct afs_fakestat_state fakestate;
273     struct rx_connection *rxconn;
274     XSTATS_DECLS;
275     OSI_VC_CONVERT(adp);
276
277     AFS_STATCNT(afs_rmdir);
278
279     afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp,
280                ICL_TYPE_STRING, aname);
281
282     if ((code = afs_CreateReq(&treq, acred)))
283         goto done2;
284     afs_InitFakeStat(&fakestate);
285
286     if (strlen(aname) > AFSNAMEMAX) {
287         code = ENAMETOOLONG;
288         goto done;
289     }
290
291     AFS_DISCON_LOCK();
292
293     code = afs_EvalFakeStat(&adp, &fakestate, treq);
294     if (code)
295         goto done;
296
297     code = afs_VerifyVCache(adp, treq);
298     if (code)
299         goto done;
300
301     /** If the volume is read-only, return error without making an RPC to the
302       * fileserver
303       */
304     if (adp->f.states & CRO) {
305         code = EROFS;
306         goto done;
307     }
308
309    if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
310         /* Disconnected read only mode. */
311         code = ENETDOWN;
312         goto done;
313     }
314
315     tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);   /* test for error below */
316     ObtainWriteLock(&adp->lock, 154);
317     if (tdc)
318         ObtainSharedLock(&tdc->lock, 633);
319     if (tdc && (adp->f.states & CForeign)) {
320         struct VenusFid unlinkFid;
321
322         unlinkFid.Fid.Vnode = 0;
323         code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid);
324         if (code == 0) {
325             unlinkFid.Cell = adp->f.fid.Cell;
326             unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume;
327             if (unlinkFid.Fid.Unique == 0) {
328                 tvc =
329                     afs_LookupVCache(&unlinkFid, treq, adp, aname);
330             } else {
331                 ObtainReadLock(&afs_xvcache);
332                 tvc = afs_FindVCache(&unlinkFid, 0, 1 /* do xstats */ );
333                 ReleaseReadLock(&afs_xvcache);
334             }
335         }
336     }
337
338     if (!AFS_IS_DISCON_RW) {
339         /* Not disconnected, can connect to server. */
340         do {
341             tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
342             if (tc) {
343                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
344                 RX_AFS_GUNLOCK();
345                 code =
346                     RXAFS_RemoveDir(rxconn,
347                                 (struct AFSFid *)&adp->f.fid.Fid,
348                                 aname,
349                                 &OutDirStatus,
350                                 &tsync);
351                 RX_AFS_GLOCK();
352                 XSTATS_END_TIME;
353             } else
354                 code = -1;
355         } while (afs_Analyze
356                  (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_REMOVEDIR,
357                  SHARED_LOCK, NULL));
358
359         if (code) {
360             if (tdc) {
361                 ReleaseSharedLock(&tdc->lock);
362                 afs_PutDCache(tdc);
363             }
364
365             if (code < 0) {
366                 afs_StaleVCache(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     afs_DestroyReq(treq);
474   done2:
475     return code;
476 }