disconnected-shadow-directory-fixes-20090121
[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 afs_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         do {
106             tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
107             if (tc) {
108                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR);
109                 now = osi_Time();
110                 RX_AFS_GUNLOCK();
111                 code =
112                     RXAFS_MakeDir(tc->id,
113                                 (struct AFSFid *)&adp->fid.Fid,
114                                 aname,
115                                 &InStatus,
116                                 (struct AFSFid *)&newFid.Fid,
117                                 &OutFidStatus,
118                                 &OutDirStatus,
119                                 &CallBack,
120                                 &tsync);
121                 RX_AFS_GLOCK();
122                 XSTATS_END_TIME;
123                 CallBack.ExpirationTime += now;
124                 /* DON'T forget to Set the callback value... */
125             } else
126                 code = -1;
127         } while (afs_Analyze
128                     (tc, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_MAKEDIR,
129                      SHARED_LOCK, NULL));
130
131         if (code) {
132             if (code < 0) {
133                 ObtainWriteLock(&afs_xcbhash, 490);
134                 afs_DequeueCallback(adp);
135                 adp->states &= ~CStatd;
136                 ReleaseWriteLock(&afs_xcbhash);
137                 osi_dnlc_purgedp(adp);
138             }
139             ReleaseWriteLock(&adp->lock);
140             if (tdc)
141                 afs_PutDCache(tdc);
142             goto done;
143         }
144
145     } else {
146 #if defined(AFS_DISCON_ENV)
147         /* Disconnected. */
148
149         /* We have the dir entry now, we can use it while disconnected. */
150         if (adp->mvid == NULL) {
151             /* If not mount point, generate a new fid. */
152             newFid.Cell = adp->fid.Cell;
153             newFid.Fid.Volume = adp->fid.Fid.Volume;
154             afs_GenFakeFid(&newFid, VDIR);
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 #endif
162     }                   /* if (!AFS_IS_DISCON_RW) */
163
164     /* otherwise, we should see if we can make the change to the dir locally */
165     if (tdc)
166         ObtainWriteLock(&tdc->lock, 632);
167     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
168         /* we can do it locally */
169         ObtainWriteLock(&afs_xdcache, 294);
170         code = afs_dir_Create(tdc, aname, &newFid.Fid);
171         ReleaseWriteLock(&afs_xdcache);
172         if (code) {
173             ZapDCE(tdc);        /* surprise error -- use invalid value */
174             DZap(tdc);
175         }
176     }
177     if (tdc) {
178         ReleaseWriteLock(&tdc->lock);
179         afs_PutDCache(tdc);
180     }
181
182     if (AFS_IS_DISCON_RW)
183         /* We will have to settle with the local link count. */
184         adp->m.LinkCount++;
185     else
186         adp->m.LinkCount = OutDirStatus.LinkCount;
187     newFid.Cell = adp->fid.Cell;
188     newFid.Fid.Volume = adp->fid.Fid.Volume;
189     ReleaseWriteLock(&adp->lock);
190     if (AFS_IS_DISCON_RW) {
191 #if defined(AFS_DISCON_ENV)
192         /* When disconnected, we have to create the full dir here. */
193
194         /* Generate a new vcache and fill it. */
195         tvc = afs_NewVCache(&newFid, NULL);
196         if (tvc) {
197             *avcp = tvc;
198         } else {
199             code = ENOENT;
200             goto done;
201         }
202
203         ObtainWriteLock(&tvc->lock, 738);
204         afs_GenDisconStatus(adp, tvc, &newFid, attrs, &treq, VDIR);
205         ReleaseWriteLock(&tvc->lock);
206
207         /* And now make an empty dir, containing . and .. : */
208         /* Get a new dcache for it first. */
209         new_dc = afs_GetDCache(tvc, (afs_size_t) 0, &treq, &offset, &len, 1);
210         if (!new_dc) {
211             printf("afs_mkdir: can't get new dcache for dir.\n");
212             code = ENOENT;
213             goto done;
214         }
215
216         ObtainWriteLock(&afs_xdcache, 739);
217         code = afs_dir_MakeDir(new_dc,
218                                 (afs_int32 *) &newFid.Fid,
219                                 (afs_int32) &adp->fid.Fid);
220         ReleaseWriteLock(&afs_xdcache);
221         if (code)
222             printf("afs_mkdir: afs_dirMakeDir code = %u\n", code);
223
224         afs_PutDCache(new_dc);
225
226         ObtainWriteLock(&tvc->lock, 731);
227         afs_DisconAddDirty(tvc, VDisconCreate, 1);
228         /* Update length in the vcache. */
229         tvc->m.Length = new_dc->f.chunkBytes;
230         ReleaseWriteLock(&tvc->lock);
231 #endif                          /* #ifdef AFS_DISCON_ENV */
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   done2:
248     return code;
249 }
250
251
252 int
253 /* don't set CDirty in here because RPC is called synchronously */
254 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
255 afs_rmdir(OSI_VC_DECL(adp), char *aname, struct vnode *cdirp, 
256           struct AFS_UCRED *acred)
257 #else
258 afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred)
259 #endif
260 {
261     struct vrequest treq;
262     register struct dcache *tdc;
263     register struct vcache *tvc = NULL;
264     register afs_int32 code;
265     register struct afs_conn *tc;
266     afs_size_t offset, len;
267     struct AFSFetchStatus OutDirStatus;
268     struct AFSVolSync tsync;
269     struct afs_fakestat_state fakestate;
270     XSTATS_DECLS;
271     OSI_VC_CONVERT(adp);
272
273     AFS_STATCNT(afs_rmdir);
274
275     afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp,
276                ICL_TYPE_STRING, aname);
277
278     if ((code = afs_InitReq(&treq, acred)))
279         goto done2;
280     afs_InitFakeStat(&fakestate);
281
282     if (strlen(aname) > AFSNAMEMAX) {
283         code = ENAMETOOLONG;
284         goto done;
285     }
286
287     AFS_DISCON_LOCK();
288
289     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
290     if (code)
291         goto done;
292
293     code = afs_VerifyVCache(adp, &treq);
294     if (code)
295         goto done;
296
297     /** If the volume is read-only, return error without making an RPC to the
298       * fileserver
299       */
300     if (adp->states & CRO) {
301         code = EROFS;
302         goto done;
303     }
304
305    if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
306         /* Disconnected read only mode. */
307         code = ENETDOWN;
308         goto done;
309     }
310
311     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);  /* test for error below */
312     ObtainWriteLock(&adp->lock, 154);
313     if (tdc)
314         ObtainSharedLock(&tdc->lock, 633);
315     if (tdc && (adp->states & CForeign)) {
316         struct VenusFid unlinkFid;
317
318         unlinkFid.Fid.Vnode = 0;
319         code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid);
320         if (code == 0) {
321             afs_int32 cached = 0;
322
323             unlinkFid.Cell = adp->fid.Cell;
324             unlinkFid.Fid.Volume = adp->fid.Fid.Volume;
325             if (unlinkFid.Fid.Unique == 0) {
326                 tvc =
327                     afs_LookupVCache(&unlinkFid, &treq, &cached, 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->fid, &treq, SHARED_LOCK);
340             if (tc) {
341                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
342                 RX_AFS_GUNLOCK();
343                 code =
344                     RXAFS_RemoveDir(tc->id,
345                                 (struct AFSFid *)&adp->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, code, &adp->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                 ObtainWriteLock(&afs_xcbhash, 491);
365                 afs_DequeueCallback(adp);
366                 adp->states &= ~CStatd;
367                 ReleaseWriteLock(&afs_xcbhash);
368                 osi_dnlc_purgedp(adp);
369             }
370             ReleaseWriteLock(&adp->lock);
371             goto done;
372         }
373
374         /* here if rpc worked; update the in-core link count */
375         adp->m.LinkCount = OutDirStatus.LinkCount;
376
377     } else {
378 #if defined(AFS_DISCON_ENV)
379         /* Disconnected. */
380
381         if (!tdc) {
382             ReleaseWriteLock(&adp->lock);
383             printf("afs_rmdir: No local dcache!\n");
384             code = ENETDOWN;
385             goto done;
386         }
387         
388         if (!tvc) {
389             /* Find the vcache. */
390             struct VenusFid tfid;
391
392             tfid.Cell = adp->fid.Cell;
393             tfid.Fid.Volume = adp->fid.Fid.Volume;
394             code = afs_dir_Lookup(tdc, aname, &tfid.Fid);
395
396             ObtainSharedLock(&afs_xvcache, 764);
397             tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ );
398             ReleaseSharedLock(&afs_xvcache);
399             
400             if (!tvc) {
401                 printf("afs_rmdir: Can't find dir's vcache!\n");
402                 ReleaseSharedLock(&tdc->lock);
403                 afs_PutDCache(tdc);     /* drop ref count */
404                 ReleaseWriteLock(&adp->lock);
405                 code = ENETDOWN;
406                 goto done;
407             }
408         }
409
410         if (tvc->m.LinkCount > 2) {
411             /* This dir contains more than . and .., thus it can't be
412              * deleted.
413              */
414             ReleaseSharedLock(&tdc->lock);
415             afs_PutDCache(tdc);
416             afs_PutVCache(tvc);
417             ReleaseWriteLock(&adp->lock);
418             code = ENOTEMPTY;
419             goto done;
420         }
421
422         /* Make a shadow copy of the parent dir (if not done already).
423          * If we were created locally, then we don't need to have a shadow
424          * directory (as there's no server state to remember)
425          */
426         if (!adp->shVnode && !(adp->ddirty_flags & VDisconCreate)) {
427             afs_MakeShadowDir(adp, tdc);
428         }
429
430         adp->m.LinkCount--;
431 #endif                          /* #ifdef AFS_DISCON_ENV */
432     }                           /* if (!AFS_IS_DISCON_RW) */
433
434     if (tdc)
435         UpgradeSToWLock(&tdc->lock, 634);
436     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
437         /* we can do it locally */
438         code = afs_dir_Delete(tdc, aname);
439         if (code) {
440             ZapDCE(tdc);        /* surprise error -- invalid value */
441             DZap(tdc);
442         }
443     }
444     if (tdc) {
445         ReleaseWriteLock(&tdc->lock);
446         afs_PutDCache(tdc);     /* drop ref count */
447     }
448
449     if (tvc)
450         osi_dnlc_purgedp(tvc);  /* get rid of any entries for this directory */
451     else
452         osi_dnlc_remove(adp, aname, 0);
453
454     if (tvc) {
455         ObtainWriteLock(&tvc->lock, 155);
456         tvc->states &= ~CUnique;        /* For the dfs xlator */
457 #if AFS_DISCON_ENV
458         if (AFS_IS_DISCON_RW) {
459             if (tvc->ddirty_flags & VDisconCreate) {
460                 /* If we we were created whilst disconnected, removal doesn't
461                  * need to get logged. Just go away gracefully */
462                 afs_DisconRemoveDirty(tvc);
463             } else {
464                 afs_DisconAddDirty(tvc, VDisconRemove, 1);
465             }
466         }
467 #endif
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   done3:
478     afs_PutFakeStat(&fakestate);
479     code = afs_CheckCode(code, &treq, 27);
480   done2:
481     return code;
482 }