f6f7ded86e791f015c4e0bde95c04610ed51c644
[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 RCSID
24     ("$Header$");
25
26 #include "afs/sysincludes.h"    /* Standard vendor system headers */
27 #include "afsincludes.h"        /* Afs-based standard headers */
28 #include "afs/afs_stats.h"      /* statistics */
29 #include "afs/afs_cbqueue.h"
30 #include "afs/nfsclient.h"
31 #include "afs/afs_osidnlc.h"
32
33 extern afs_rwlock_t afs_xvcache;
34 extern afs_rwlock_t afs_xcbhash;
35
36 /* don't set CDirty in here because RPC is called synchronously */
37
38 int
39 afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, 
40      register struct vcache **avcp, struct AFS_UCRED *acred)
41 {
42     struct vrequest treq;
43     register afs_int32 code;
44     register struct conn *tc;
45     struct VenusFid newFid;
46     register struct dcache *tdc;
47     struct dcache *new_dc;
48     afs_size_t offset, len;
49     register struct vcache *tvc;
50     struct AFSStoreStatus InStatus;
51     struct AFSFetchStatus OutFidStatus, OutDirStatus;
52     struct AFSCallBack CallBack;
53     struct AFSVolSync tsync;
54     afs_int32 now;
55     struct afs_fakestat_state fakestate;
56     XSTATS_DECLS;
57     OSI_VC_CONVERT(adp);
58
59     AFS_STATCNT(afs_mkdir);
60     afs_Trace2(afs_iclSetp, CM_TRACE_MKDIR, ICL_TYPE_POINTER, adp,
61                ICL_TYPE_STRING, aname);
62
63     if ((code = afs_InitReq(&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->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     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
98     InStatus.ClientModTime = osi_Time();
99     InStatus.UnixModeBits = attrs->va_mode & 0xffff;    /* only care about protection bits */
100     InStatus.Group = (afs_int32) acred->cr_gid;
101     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);
102     ObtainWriteLock(&adp->lock, 153);
103
104     if (!AFS_IS_DISCON_RW) {
105
106         do {
107             tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
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(tc->id,
114                                 (struct AFSFid *)&adp->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, code, &adp->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->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 #if defined(AFS_DISCON_ENV)
148         /* Disconnected. */
149
150         /* We have the dir entry now, we can use it while disconnected. */
151         if (adp->mvid == NULL) {
152             /* If not mount point, generate a new fid. */
153             newFid.Cell = adp->fid.Cell;
154             newFid.Fid.Volume = adp->fid.Fid.Volume;
155             afs_GenFakeFid(&newFid, VDIR);
156         }
157         /* XXX: If mount point???*/
158
159         /* Operations with the actual dir's cache entry are further
160          * down, where the dir entry gets created.
161          */
162 #endif
163     }                   /* if (!AFS_IS_DISCON_RW) */
164
165     /* otherwise, we should see if we can make the change to the dir locally */
166     if (tdc)
167         ObtainWriteLock(&tdc->lock, 632);
168     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
169         /* we can do it locally */
170         ObtainWriteLock(&afs_xdcache, 294);
171         code = afs_dir_Create(tdc, aname, &newFid.Fid);
172         ReleaseWriteLock(&afs_xdcache);
173         if (code) {
174             ZapDCE(tdc);        /* surprise error -- use invalid value */
175             DZap(tdc);
176         }
177     }
178     if (tdc) {
179         ReleaseWriteLock(&tdc->lock);
180         afs_PutDCache(tdc);
181     }
182
183     if (AFS_IS_DISCON_RW)
184         /* We will have to settle with the local link count. */
185         adp->m.LinkCount++;
186     else
187         adp->m.LinkCount = OutDirStatus.LinkCount;
188     newFid.Cell = adp->fid.Cell;
189     newFid.Fid.Volume = adp->fid.Fid.Volume;
190     ReleaseWriteLock(&adp->lock);
191     if (AFS_IS_DISCON_RW) {
192 #if defined(AFS_DISCON_ENV)
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             code = 0;
199             *avcp = tvc;
200         } else {
201             code = ENOENT;
202             goto done;
203         }
204
205         ObtainWriteLock(&tvc->lock, 738);
206         afs_GenDisconStatus(adp, tvc, &newFid, attrs, &treq, VDIR);
207         ReleaseWriteLock(&tvc->lock);
208
209         /* And now make an empty dir, containing . and .. : */
210         /* Get a new dcache for it first. */
211         new_dc = afs_GetDCache(tvc, (afs_size_t) 0, &treq, &offset, &len, 1);
212         if (!new_dc) {
213             printf("afs_mkdir: can't get new dcache for dir.\n");
214             code = ENOENT;
215             goto done;
216         }
217
218         ObtainWriteLock(&afs_xdcache, 739);
219         code = afs_dir_MakeDir(new_dc,
220                                 (afs_int32 *) &newFid.Fid,
221                                 (afs_int32) &adp->fid.Fid);
222         ReleaseWriteLock(&afs_xdcache);
223         if (code)
224             printf("afs_mkdir: afs_dirMakeDir code = %u\n", code);
225
226         /* Add to dirty list. */
227         if (!tvc->ddirty_flags ||
228                 (tvc->ddirty_flags == VDisconShadowed)) {
229
230             /* Put it in the list only if it's fresh. */
231             ObtainWriteLock(&afs_DDirtyVCListLock, 730);
232             AFS_DISCON_ADD_DIRTY(tvc);
233             ReleaseWriteLock(&afs_DDirtyVCListLock);
234         }
235
236         ObtainWriteLock(&tvc->lock, 731);
237         /* Update length in the vcache. */
238         tvc->m.Length = new_dc->f.chunkBytes;
239         /* Set create flag. */
240         tvc->ddirty_flags |= VDisconCreate;
241         ReleaseWriteLock(&tvc->lock);
242 #endif                          /* #ifdef AFS_DISCON_ENV */
243     } else {
244         /* now we're done with parent dir, create the real dir's cache entry */
245         tvc = afs_GetVCache(&newFid, &treq, NULL, NULL);
246         if (tvc) {
247             code = 0;
248             *avcp = tvc;
249         } else
250             code = ENOENT;
251     }                           /* if (AFS_DISCON_RW) */
252
253   done:
254     AFS_DISCON_UNLOCK();
255   done3:
256     afs_PutFakeStat(&fakestate);
257     code = afs_CheckCode(code, &treq, 26);
258   done2:
259     return code;
260 }
261
262
263 int
264 /* don't set CDirty in here because RPC is called synchronously */
265 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
266 afs_rmdir(OSI_VC_DECL(adp), char *aname, struct vnode *cdirp, 
267           struct AFS_UCRED *acred)
268 #else
269 afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred)
270 #endif
271 {
272     struct vrequest treq;
273     register struct dcache *tdc;
274     register struct vcache *tvc = NULL;
275     register afs_int32 code;
276     register struct conn *tc;
277     afs_size_t offset, len;
278     struct AFSFetchStatus OutDirStatus;
279     struct AFSVolSync tsync;
280     struct afs_fakestat_state fakestate;
281     XSTATS_DECLS;
282     OSI_VC_CONVERT(adp);
283
284     AFS_STATCNT(afs_rmdir);
285
286     afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp,
287                ICL_TYPE_STRING, aname);
288
289     if ((code = afs_InitReq(&treq, acred)))
290         goto done2;
291     afs_InitFakeStat(&fakestate);
292
293     if (strlen(aname) > AFSNAMEMAX) {
294         code = ENAMETOOLONG;
295         goto done;
296     }
297
298     AFS_DISCON_LOCK();
299
300     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
301     if (code)
302         goto done;
303
304     code = afs_VerifyVCache(adp, &treq);
305     if (code)
306         goto done;
307
308     /** If the volume is read-only, return error without making an RPC to the
309       * fileserver
310       */
311     if (adp->states & CRO) {
312         code = EROFS;
313         goto done;
314     }
315
316    if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
317         /* Disconnected read only mode. */
318         code = ENETDOWN;
319         goto done;
320     }
321
322     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);  /* test for error below */
323     ObtainWriteLock(&adp->lock, 154);
324     if (tdc)
325         ObtainSharedLock(&tdc->lock, 633);
326     if (tdc && (adp->states & CForeign)) {
327         struct VenusFid unlinkFid;
328
329         unlinkFid.Fid.Vnode = 0;
330         code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid);
331         if (code == 0) {
332             afs_int32 cached = 0;
333
334             unlinkFid.Cell = adp->fid.Cell;
335             unlinkFid.Fid.Volume = adp->fid.Fid.Volume;
336             if (unlinkFid.Fid.Unique == 0) {
337                 tvc =
338                     afs_LookupVCache(&unlinkFid, &treq, &cached, adp, aname);
339             } else {
340                 ObtainReadLock(&afs_xvcache);
341                 tvc = afs_FindVCache(&unlinkFid, 0, 1 /* do xstats */ );
342                 ReleaseReadLock(&afs_xvcache);
343             }
344         }
345     }
346
347     if (!AFS_IS_DISCON_RW) {
348         /* Not disconnected, can connect to server. */
349         do {
350             tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
351             if (tc) {
352                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
353                 RX_AFS_GUNLOCK();
354                 code =
355                     RXAFS_RemoveDir(tc->id,
356                                 (struct AFSFid *)&adp->fid.Fid,
357                                 aname,
358                                 &OutDirStatus,
359                                 &tsync);
360                 RX_AFS_GLOCK();
361                 XSTATS_END_TIME;
362             } else
363                 code = -1;
364         } while (afs_Analyze
365                  (tc, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_REMOVEDIR,
366                  SHARED_LOCK, NULL));
367
368         if (code) {
369             if (tdc) {
370                 ReleaseSharedLock(&tdc->lock);
371                 afs_PutDCache(tdc);
372             }
373
374             if (code < 0) {
375                 ObtainWriteLock(&afs_xcbhash, 491);
376                 afs_DequeueCallback(adp);
377                 adp->states &= ~CStatd;
378                 ReleaseWriteLock(&afs_xcbhash);
379                 osi_dnlc_purgedp(adp);
380             }
381             ReleaseWriteLock(&adp->lock);
382             goto done;
383         }
384
385         /* here if rpc worked; update the in-core link count */
386         adp->m.LinkCount = OutDirStatus.LinkCount;
387
388     } else {
389 #if defined(AFS_DISCON_ENV)
390         /* Disconnected. */
391
392         if (!tvc) {
393             /* Find the vcache. */
394             struct VenusFid tfid;
395
396             tfid.Cell = adp->fid.Cell;
397             tfid.Fid.Volume = adp->fid.Fid.Volume;
398             code = afs_dir_Lookup(tdc, aname, &tfid.Fid);
399
400             ObtainSharedLock(&afs_xvcache, 764);
401             tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ );
402             ReleaseSharedLock(&afs_xvcache);
403
404             if (!tvc) {
405                 printf("afs_rmdir: Can't find dir's vcache!\n");
406                 ReleaseSharedLock(&tdc->lock);
407                 afs_PutDCache(tdc);     /* drop ref count */
408                 ReleaseWriteLock(&adp->lock);
409                 goto done;
410             }
411         }
412
413         if (tvc->m.LinkCount > 2) {
414             /* This dir contains more than . and .., thus it can't be
415              * deleted.
416              */
417             ReleaseSharedLock(&tdc->lock);
418             code = ENOTEMPTY;
419             goto done;
420         }
421
422         /* Make a shadow copy of the parent dir (if not done already).
423          * There's no need to make a shadow copy of the deleted directory
424          * because a dir must be empty in order to be rmdir'ed.
425          * If the deleted dir has no shadow, it means that it was empty.
426          */
427         if (!(adp->ddirty_flags & VDisconShadowed)) {
428             /* If tdc available, then it is locked.
429              * afs_MakeShadowDir unlocks it.
430              */
431             if (tdc)
432                 ReleaseSharedLock(&tdc->lock);
433             afs_MakeShadowDir(adp);
434             if (tdc)
435                 ObtainSharedLock(&tdc->lock, 732);
436         }
437
438         if (!tvc->ddirty_flags) {
439             /* Put it in the list only if it's fresh or has only been shadowed. */
440             ObtainWriteLock(&afs_DDirtyVCListLock, 728);
441             AFS_DISCON_ADD_DIRTY(tvc);
442             ReleaseWriteLock(&afs_DDirtyVCListLock);
443         }
444
445         /* Now add the vcache to the dirty list. */
446         ObtainWriteLock(&tvc->lock, 727);
447         tvc->ddirty_flags |= VDisconRemove;
448         ReleaseWriteLock(&tvc->lock);
449
450         adp->m.LinkCount--;
451 #endif                          /* #ifdef AFS_DISCON_ENV */
452     }                           /* if (!AFS_IS_DISCON_RW) */
453
454     if (tdc)
455         UpgradeSToWLock(&tdc->lock, 634);
456     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
457         /* we can do it locally */
458         code = afs_dir_Delete(tdc, aname);
459         if (code) {
460             ZapDCE(tdc);        /* surprise error -- invalid value */
461             DZap(tdc);
462         }
463     }
464     if (tdc) {
465         ReleaseWriteLock(&tdc->lock);
466         afs_PutDCache(tdc);     /* drop ref count */
467     }
468
469
470     if (tvc)
471         osi_dnlc_purgedp(tvc);  /* get rid of any entries for this directory */
472     else
473         osi_dnlc_remove(adp, aname, 0);
474
475     if (tvc) {
476         ObtainWriteLock(&tvc->lock, 155);
477         tvc->states &= ~CUnique;        /* For the dfs xlator */
478         ReleaseWriteLock(&tvc->lock);
479         /* If disconnected, keep this vcache around for resync. */
480         if (!AFS_IS_DISCON_RW)
481             afs_PutVCache(tvc);
482     }
483     ReleaseWriteLock(&adp->lock);
484     /* don't worry about link count since dirs can not be hardlinked */
485     code = 0;
486
487   done:
488     AFS_DISCON_UNLOCK();
489   done3:
490     afs_PutFakeStat(&fakestate);
491     code = afs_CheckCode(code, &treq, 27);
492   done2:
493     return code;
494 }