1ddda679a20f8e6f9b7155254a3c69c1678bdb8c
[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(sizeof(struct AFSFetchStatus));
63     OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
64     memset(&InStatus, 0, sizeof(InStatus));
65
66     if ((code = afs_CreateReq(&treq, acred)))
67         goto done2;
68     afs_InitFakeStat(&fakestate);
69
70     if (strlen(aname) > AFSNAMEMAX) {
71         code = ENAMETOOLONG;
72         goto done3;
73     }
74
75     if (!afs_ENameOK(aname)) {
76         code = EINVAL;
77         goto done3;
78     }
79     
80     AFS_DISCON_LOCK();
81
82     code = afs_EvalFakeStat(&adp, &fakestate, treq);
83     if (code)
84         goto done;
85     code = afs_VerifyVCache(adp, treq);
86     if (code)
87         goto done;
88
89     /** If the volume is read-only, return error without making an RPC to the
90       * fileserver
91       */
92     if (adp->f.states & CRO) {
93         code = EROFS;
94         goto done;
95     }
96    
97     if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
98         /*printf("Network is down in afs_mkdir\n");*/
99         code = ENETDOWN;
100         goto done;
101     }
102     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
103     InStatus.ClientModTime = osi_Time();
104     InStatus.UnixModeBits = attrs->va_mode & 0xffff;    /* only care about protection bits */
105     InStatus.Group = (afs_int32) afs_cr_gid(acred);
106     tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);
107     ObtainWriteLock(&adp->lock, 153);
108
109     if (!AFS_IS_DISCON_RW) {
110         do {
111             tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
112             if (tc) {
113                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR);
114                 now = osi_Time();
115                 RX_AFS_GUNLOCK();
116                 code =
117                     RXAFS_MakeDir(rxconn,
118                                 (struct AFSFid *)&adp->f.fid.Fid,
119                                 aname,
120                                 &InStatus,
121                                 (struct AFSFid *)&newFid.Fid,
122                                 OutFidStatus,
123                                 OutDirStatus,
124                                 &CallBack,
125                                 &tsync);
126                 RX_AFS_GLOCK();
127                 XSTATS_END_TIME;
128                 CallBack.ExpirationTime += now;
129                 /* DON'T forget to Set the callback value... */
130             } else
131                 code = -1;
132         } while (afs_Analyze
133                     (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_MAKEDIR,
134                      SHARED_LOCK, NULL));
135
136         if (code) {
137             if (code < 0) {
138                 afs_StaleVCache(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.target_root == 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 = EIO;
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 = EIO;
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);
232         if (tvc) {
233             code = 0;
234             *avcp = tvc;
235
236         } else {
237             /* For some reason, we cannot fetch the vcache for our
238              * newly-created dir. */
239             code = EIO;
240         }
241     }                           /* if (AFS_DISCON_RW) */
242
243   done:
244     AFS_DISCON_UNLOCK();
245   done3:
246     afs_PutFakeStat(&fakestate);
247     code = afs_CheckCode(code, treq, 26);
248     afs_DestroyReq(treq);
249   done2:
250     osi_FreeSmallSpace(OutFidStatus);
251     osi_FreeSmallSpace(OutDirStatus);
252     return code;
253 }
254
255
256 int
257 /* don't set CDirty in here because RPC is called synchronously */
258 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
259 afs_rmdir(OSI_VC_DECL(adp), char *aname, struct vnode *cdirp, 
260           afs_ucred_t *acred)
261 #else
262 afs_rmdir(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred)
263 #endif
264 {
265     struct vrequest *treq = NULL;
266     struct dcache *tdc;
267     struct vcache *tvc = NULL;
268     afs_int32 code;
269     struct afs_conn *tc;
270     afs_size_t offset, len;
271     struct AFSFetchStatus OutDirStatus;
272     struct AFSVolSync tsync;
273     struct afs_fakestat_state fakestate;
274     struct rx_connection *rxconn;
275     XSTATS_DECLS;
276     OSI_VC_CONVERT(adp);
277
278     AFS_STATCNT(afs_rmdir);
279
280     afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp,
281                ICL_TYPE_STRING, aname);
282
283     if ((code = afs_CreateReq(&treq, acred)))
284         goto done2;
285     afs_InitFakeStat(&fakestate);
286
287     if (strlen(aname) > AFSNAMEMAX) {
288         code = ENAMETOOLONG;
289         goto done;
290     }
291
292     AFS_DISCON_LOCK();
293
294     code = afs_EvalFakeStat(&adp, &fakestate, treq);
295     if (code)
296         goto done;
297
298     code = afs_VerifyVCache(adp, treq);
299     if (code)
300         goto done;
301
302     /** If the volume is read-only, return error without making an RPC to the
303       * fileserver
304       */
305     if (adp->f.states & CRO) {
306         code = EROFS;
307         goto done;
308     }
309
310    if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
311         /* Disconnected read only mode. */
312         code = ENETDOWN;
313         goto done;
314     }
315
316     tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);   /* test for error below */
317     ObtainWriteLock(&adp->lock, 154);
318     if (tdc)
319         ObtainSharedLock(&tdc->lock, 633);
320     if (tdc && (adp->f.states & CForeign)) {
321         struct VenusFid unlinkFid;
322
323         unlinkFid.Fid.Vnode = 0;
324         code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid);
325         if (code == 0) {
326             unlinkFid.Cell = adp->f.fid.Cell;
327             unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume;
328             if (unlinkFid.Fid.Unique == 0) {
329                 tvc =
330                     afs_LookupVCache(&unlinkFid, treq, adp, aname);
331             } else {
332                 ObtainReadLock(&afs_xvcache);
333                 tvc = afs_FindVCache(&unlinkFid, 0, 1 /* do xstats */ );
334                 ReleaseReadLock(&afs_xvcache);
335             }
336         }
337     }
338
339     if (!AFS_IS_DISCON_RW) {
340         /* Not disconnected, can connect to server. */
341         do {
342             tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
343             if (tc) {
344                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
345                 RX_AFS_GUNLOCK();
346                 code =
347                     RXAFS_RemoveDir(rxconn,
348                                 (struct AFSFid *)&adp->f.fid.Fid,
349                                 aname,
350                                 &OutDirStatus,
351                                 &tsync);
352                 RX_AFS_GLOCK();
353                 XSTATS_END_TIME;
354             } else
355                 code = -1;
356         } while (afs_Analyze
357                  (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_REMOVEDIR,
358                  SHARED_LOCK, NULL));
359
360         if (code) {
361             if (tdc) {
362                 ReleaseSharedLock(&tdc->lock);
363                 afs_PutDCache(tdc);
364             }
365
366             if (code < 0) {
367                 afs_StaleVCache(adp);
368             }
369             ReleaseWriteLock(&adp->lock);
370             goto done;
371         }
372
373         /* here if rpc worked; update the in-core link count */
374         adp->f.m.LinkCount = OutDirStatus.LinkCount;
375
376     } else {
377         /* Disconnected. */
378
379         if (!tdc) {
380             ReleaseWriteLock(&adp->lock);
381             /* printf("afs_rmdir: No local dcache!\n"); */
382             code = ENETDOWN;
383             goto done;
384         }
385         
386         if (!tvc) {
387             /* Find the vcache. */
388             struct VenusFid tfid;
389
390             tfid.Cell = adp->f.fid.Cell;
391             tfid.Fid.Volume = adp->f.fid.Fid.Volume;
392             code = afs_dir_Lookup(tdc, aname, &tfid.Fid);
393
394             ObtainSharedLock(&afs_xvcache, 764);
395             tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ );
396             ReleaseSharedLock(&afs_xvcache);
397             
398             if (!tvc) {
399                 /* printf("afs_rmdir: Can't find dir's vcache!\n"); */
400                 ReleaseSharedLock(&tdc->lock);
401                 afs_PutDCache(tdc);     /* drop ref count */
402                 ReleaseWriteLock(&adp->lock);
403                 code = ENETDOWN;
404                 goto done;
405             }
406         }
407
408         if (tvc->f.m.LinkCount > 2) {
409             /* This dir contains more than . and .., thus it can't be
410              * deleted.
411              */
412             ReleaseSharedLock(&tdc->lock);
413             afs_PutDCache(tdc);
414             afs_PutVCache(tvc);
415             ReleaseWriteLock(&adp->lock);
416             code = ENOTEMPTY;
417             goto done;
418         }
419
420         /* Make a shadow copy of the parent dir (if not done already).
421          * If we were created locally, then we don't need to have a shadow
422          * directory (as there's no server state to remember)
423          */
424         if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) {
425             afs_MakeShadowDir(adp, tdc);
426         }
427
428         adp->f.m.LinkCount--;
429     }                           /* if (!AFS_IS_DISCON_RW) */
430
431     if (tdc)
432         UpgradeSToWLock(&tdc->lock, 634);
433     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
434         /* we can do it locally */
435         code = afs_dir_Delete(tdc, aname);
436         if (code) {
437             ZapDCE(tdc);        /* surprise error -- invalid value */
438             DZap(tdc);
439         }
440     }
441     if (tdc) {
442         ReleaseWriteLock(&tdc->lock);
443         afs_PutDCache(tdc);     /* drop ref count */
444     }
445
446     if (tvc)
447         osi_dnlc_purgedp(tvc);  /* get rid of any entries for this directory */
448     else
449         osi_dnlc_remove(adp, aname, 0);
450
451     if (tvc) {
452         ObtainWriteLock(&tvc->lock, 155);
453         tvc->f.states &= ~CUnique;      /* For the dfs xlator */
454         if (AFS_IS_DISCON_RW) {
455             if (tvc->f.ddirty_flags & VDisconCreate) {
456                 /* If we we were created whilst disconnected, removal doesn't
457                  * need to get logged. Just go away gracefully */
458                 afs_DisconRemoveDirty(tvc);
459             } else {
460                 afs_DisconAddDirty(tvc, VDisconRemove, 1);
461             }
462         }
463         ReleaseWriteLock(&tvc->lock);
464         afs_PutVCache(tvc);
465     }
466     ReleaseWriteLock(&adp->lock);
467     /* don't worry about link count since dirs can not be hardlinked */
468     code = 0;
469
470   done:
471     AFS_DISCON_UNLOCK();
472     afs_PutFakeStat(&fakestate);
473     code = afs_CheckCode(code, treq, 27);
474     afs_DestroyReq(treq);
475   done2:
476     return code;
477 }