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