disconnected-replay-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          * 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->shVnode) {
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         adp->m.LinkCount--;
439 #endif                          /* #ifdef AFS_DISCON_ENV */
440     }                           /* if (!AFS_IS_DISCON_RW) */
441
442     if (tdc)
443         UpgradeSToWLock(&tdc->lock, 634);
444     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
445         /* we can do it locally */
446         code = afs_dir_Delete(tdc, aname);
447         if (code) {
448             ZapDCE(tdc);        /* surprise error -- invalid value */
449             DZap(tdc);
450         }
451     }
452     if (tdc) {
453         ReleaseWriteLock(&tdc->lock);
454         afs_PutDCache(tdc);     /* drop ref count */
455     }
456
457     if (tvc)
458         osi_dnlc_purgedp(tvc);  /* get rid of any entries for this directory */
459     else
460         osi_dnlc_remove(adp, aname, 0);
461
462     if (tvc) {
463         ObtainWriteLock(&tvc->lock, 155);
464         tvc->states &= ~CUnique;        /* For the dfs xlator */
465 #if AFS_DISCON_ENV
466         if (AFS_IS_DISCON_RW) {
467             if (tvc->ddirty_flags & VDisconCreate) {
468                 /* If we we were created whilst disconnected, removal doesn't
469                  * need to get logged. Just go away gracefully */
470                 afs_DisconRemoveDirty(tvc);
471             } else {
472                 afs_DisconAddDirty(tvc, VDisconRemove, 1);
473             }
474         }
475 #endif
476         ReleaseWriteLock(&tvc->lock);
477         afs_PutVCache(tvc);
478     }
479     ReleaseWriteLock(&adp->lock);
480     /* don't worry about link count since dirs can not be hardlinked */
481     code = 0;
482
483   done:
484     AFS_DISCON_UNLOCK();
485   done3:
486     afs_PutFakeStat(&fakestate);
487     code = afs_CheckCode(code, &treq, 27);
488   done2:
489     return code;
490 }