b8ce1f9e3629d2d04e2a671353561d96304bb492
[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 = NULL;
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     OutFidStatus = osi_AllocSmallSpace(AFS_SMALLOCSIZ);
63     OutDirStatus = osi_AllocSmallSpace(AFS_SMALLOCSIZ);
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                 ObtainWriteLock(&afs_xcbhash, 490);
138                 afs_DequeueCallback(adp);
139                 adp->f.states &= ~CStatd;
140                 ReleaseWriteLock(&afs_xcbhash);
141                 osi_dnlc_purgedp(adp);
142             }
143             ReleaseWriteLock(&adp->lock);
144             if (tdc)
145                 afs_PutDCache(tdc);
146             goto done;
147         }
148
149     } else {
150         /* Disconnected. */
151
152         /* We have the dir entry now, we can use it while disconnected. */
153         if (adp->mvid == NULL) {
154             /* If not mount point, generate a new fid. */
155             newFid.Cell = adp->f.fid.Cell;
156             newFid.Fid.Volume = adp->f.fid.Fid.Volume;
157             afs_GenFakeFid(&newFid, VDIR, 1);
158         }
159         /* XXX: If mount point???*/
160
161         /* Operations with the actual dir's cache entry are further
162          * down, where the dir entry gets created.
163          */
164     }                   /* if (!AFS_IS_DISCON_RW) */
165
166     /* otherwise, we should see if we can make the change to the dir locally */
167     if (tdc)
168         ObtainWriteLock(&tdc->lock, 632);
169     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) {
170         /* we can do it locally */
171         ObtainWriteLock(&afs_xdcache, 294);
172         code = afs_dir_Create(tdc, aname, &newFid.Fid);
173         ReleaseWriteLock(&afs_xdcache);
174         if (code) {
175             ZapDCE(tdc);        /* surprise error -- use invalid value */
176             DZap(tdc);
177         }
178     }
179     if (tdc) {
180         ReleaseWriteLock(&tdc->lock);
181         afs_PutDCache(tdc);
182     }
183
184     if (AFS_IS_DISCON_RW)
185         /* We will have to settle with the local link count. */
186         adp->f.m.LinkCount++;
187     else
188         adp->f.m.LinkCount = OutDirStatus->LinkCount;
189     newFid.Cell = adp->f.fid.Cell;
190     newFid.Fid.Volume = adp->f.fid.Fid.Volume;
191     ReleaseWriteLock(&adp->lock);
192     if (AFS_IS_DISCON_RW) {
193         /* When disconnected, we have to create the full dir here. */
194
195         /* Generate a new vcache and fill it. */
196         tvc = afs_NewVCache(&newFid, NULL);
197         if (tvc) {
198             *avcp = tvc;
199         } else {
200             code = ENOENT;
201             goto done;
202         }
203
204         ObtainWriteLock(&tvc->lock, 738);
205         afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VDIR);
206         ReleaseWriteLock(&tvc->lock);
207
208         /* And now make an empty dir, containing . and .. : */
209         /* Get a new dcache for it first. */
210         new_dc = afs_GetDCache(tvc, (afs_size_t) 0, treq, &offset, &len, 1);
211         if (!new_dc) {
212             /* printf("afs_mkdir: can't get new dcache for dir.\n"); */
213             code = ENOENT;
214             goto done;
215         }
216
217         ObtainWriteLock(&afs_xdcache, 739);
218         code = afs_dir_MakeDir(new_dc,
219                                (afs_int32 *) &newFid.Fid,
220                                (afs_int32 *) &adp->f.fid.Fid);
221         ReleaseWriteLock(&afs_xdcache);
222         /* if (code) printf("afs_mkdir: afs_dirMakeDir code = %u\n", code); */
223
224         afs_PutDCache(new_dc);
225
226         ObtainWriteLock(&tvc->lock, 731);
227         /* Update length in the vcache. */
228         tvc->f.m.Length = new_dc->f.chunkBytes;
229
230         afs_DisconAddDirty(tvc, VDisconCreate, 1);
231         ReleaseWriteLock(&tvc->lock);
232     } else {
233         /* now we're done with parent dir, create the real dir's cache entry */
234         tvc = afs_GetVCache(&newFid, treq, NULL, NULL);
235         if (tvc) {
236             code = 0;
237             *avcp = tvc;
238         } else
239             code = ENOENT;
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             afs_int32 cached = 0;
326
327             unlinkFid.Cell = adp->f.fid.Cell;
328             unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume;
329             if (unlinkFid.Fid.Unique == 0) {
330                 tvc =
331                     afs_LookupVCache(&unlinkFid, treq, &cached, adp, aname);
332             } else {
333                 ObtainReadLock(&afs_xvcache);
334                 tvc = afs_FindVCache(&unlinkFid, 0, 1 /* do xstats */ );
335                 ReleaseReadLock(&afs_xvcache);
336             }
337         }
338     }
339
340     if (!AFS_IS_DISCON_RW) {
341         /* Not disconnected, can connect to server. */
342         do {
343             tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
344             if (tc) {
345                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
346                 RX_AFS_GUNLOCK();
347                 code =
348                     RXAFS_RemoveDir(rxconn,
349                                 (struct AFSFid *)&adp->f.fid.Fid,
350                                 aname,
351                                 &OutDirStatus,
352                                 &tsync);
353                 RX_AFS_GLOCK();
354                 XSTATS_END_TIME;
355             } else
356                 code = -1;
357         } while (afs_Analyze
358                  (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_REMOVEDIR,
359                  SHARED_LOCK, NULL));
360
361         if (code) {
362             if (tdc) {
363                 ReleaseSharedLock(&tdc->lock);
364                 afs_PutDCache(tdc);
365             }
366
367             if (code < 0) {
368                 ObtainWriteLock(&afs_xcbhash, 491);
369                 afs_DequeueCallback(adp);
370                 adp->f.states &= ~CStatd;
371                 ReleaseWriteLock(&afs_xcbhash);
372                 osi_dnlc_purgedp(adp);
373             }
374             ReleaseWriteLock(&adp->lock);
375             goto done;
376         }
377
378         /* here if rpc worked; update the in-core link count */
379         adp->f.m.LinkCount = OutDirStatus.LinkCount;
380
381     } else {
382         /* Disconnected. */
383
384         if (!tdc) {
385             ReleaseWriteLock(&adp->lock);
386             /* printf("afs_rmdir: No local dcache!\n"); */
387             code = ENETDOWN;
388             goto done;
389         }
390         
391         if (!tvc) {
392             /* Find the vcache. */
393             struct VenusFid tfid;
394
395             tfid.Cell = adp->f.fid.Cell;
396             tfid.Fid.Volume = adp->f.fid.Fid.Volume;
397             code = afs_dir_Lookup(tdc, aname, &tfid.Fid);
398
399             ObtainSharedLock(&afs_xvcache, 764);
400             tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ );
401             ReleaseSharedLock(&afs_xvcache);
402             
403             if (!tvc) {
404                 /* printf("afs_rmdir: Can't find dir's vcache!\n"); */
405                 ReleaseSharedLock(&tdc->lock);
406                 afs_PutDCache(tdc);     /* drop ref count */
407                 ReleaseWriteLock(&adp->lock);
408                 code = ENETDOWN;
409                 goto done;
410             }
411         }
412
413         if (tvc->f.m.LinkCount > 2) {
414             /* This dir contains more than . and .., thus it can't be
415              * deleted.
416              */
417             ReleaseSharedLock(&tdc->lock);
418             afs_PutDCache(tdc);
419             afs_PutVCache(tvc);
420             ReleaseWriteLock(&adp->lock);
421             code = ENOTEMPTY;
422             goto done;
423         }
424
425         /* Make a shadow copy of the parent dir (if not done already).
426          * If we were created locally, then we don't need to have a shadow
427          * directory (as there's no server state to remember)
428          */
429         if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) {
430             afs_MakeShadowDir(adp, tdc);
431         }
432
433         adp->f.m.LinkCount--;
434     }                           /* if (!AFS_IS_DISCON_RW) */
435
436     if (tdc)
437         UpgradeSToWLock(&tdc->lock, 634);
438     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
439         /* we can do it locally */
440         code = afs_dir_Delete(tdc, aname);
441         if (code) {
442             ZapDCE(tdc);        /* surprise error -- invalid value */
443             DZap(tdc);
444         }
445     }
446     if (tdc) {
447         ReleaseWriteLock(&tdc->lock);
448         afs_PutDCache(tdc);     /* drop ref count */
449     }
450
451     if (tvc)
452         osi_dnlc_purgedp(tvc);  /* get rid of any entries for this directory */
453     else
454         osi_dnlc_remove(adp, aname, 0);
455
456     if (tvc) {
457         ObtainWriteLock(&tvc->lock, 155);
458         tvc->f.states &= ~CUnique;      /* For the dfs xlator */
459         if (AFS_IS_DISCON_RW) {
460             if (tvc->f.ddirty_flags & VDisconCreate) {
461                 /* If we we were created whilst disconnected, removal doesn't
462                  * need to get logged. Just go away gracefully */
463                 afs_DisconRemoveDirty(tvc);
464             } else {
465                 afs_DisconAddDirty(tvc, VDisconRemove, 1);
466             }
467         }
468         ReleaseWriteLock(&tvc->lock);
469         afs_PutVCache(tvc);
470     }
471     ReleaseWriteLock(&adp->lock);
472     /* don't worry about link count since dirs can not be hardlinked */
473     code = 0;
474
475   done:
476     AFS_DISCON_UNLOCK();
477     afs_PutFakeStat(&fakestate);
478     code = afs_CheckCode(code, treq, 27);
479     afs_DestroyReq(treq);
480   done2:
481     return code;
482 }