2 * Copyright 2000, International Business Machines Corporation and others.
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
11 * afs_vnop_dirops.c - make and remove directories
20 #include <afsconfig.h>
21 #include "afs/param.h"
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"
31 extern afs_rwlock_t afs_xvcache
;
32 extern afs_rwlock_t afs_xcbhash
;
34 /* don't set CDirty in here because RPC is called synchronously */
37 afs_mkdir(OSI_VC_DECL(adp
), char *aname
, struct vattr
*attrs
,
38 struct vcache
**avcp
, afs_ucred_t
*acred
)
40 struct vrequest
*treq
= NULL
;
43 struct rx_connection
*rxconn
;
44 struct VenusFid newFid
;
46 struct dcache
*new_dc
;
47 afs_size_t offset
, len
;
49 struct AFSStoreStatus InStatus
;
50 struct AFSFetchStatus
*OutFidStatus
, *OutDirStatus
;
51 struct AFSCallBack CallBack
;
52 struct AFSVolSync tsync
;
54 struct afs_fakestat_state fakestate
;
58 AFS_STATCNT(afs_mkdir
);
59 afs_Trace2(afs_iclSetp
, CM_TRACE_MKDIR
, ICL_TYPE_POINTER
, adp
,
60 ICL_TYPE_STRING
, aname
);
62 OutFidStatus
= osi_AllocSmallSpace(sizeof(struct AFSFetchStatus
));
63 OutDirStatus
= osi_AllocSmallSpace(sizeof(struct AFSFetchStatus
));
64 memset(&InStatus
, 0, sizeof(InStatus
));
66 if ((code
= afs_CreateReq(&treq
, acred
)))
68 afs_InitFakeStat(&fakestate
);
70 if (strlen(aname
) > AFSNAMEMAX
) {
75 if (!afs_ENameOK(aname
)) {
82 code
= afs_EvalFakeStat(&adp
, &fakestate
, treq
);
85 code
= afs_VerifyVCache(adp
, treq
);
89 /** If the volume is read-only, return error without making an RPC to the
92 if (adp
->f
.states
& CRO
) {
97 if (AFS_IS_DISCONNECTED
&& !AFS_IS_DISCON_RW
) {
98 /*printf("Network is down in afs_mkdir\n");*/
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);
109 if (!AFS_IS_DISCON_RW
) {
111 tc
= afs_Conn(&adp
->f
.fid
, treq
, SHARED_LOCK
, &rxconn
);
113 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR
);
117 RXAFS_MakeDir(rxconn
,
118 (struct AFSFid
*)&adp
->f
.fid
.Fid
,
121 (struct AFSFid
*)&newFid
.Fid
,
128 CallBack
.ExpirationTime
+= now
;
129 /* DON'T forget to Set the callback value... */
133 (tc
, rxconn
, code
, &adp
->f
.fid
, treq
, AFS_STATS_FS_RPCIDX_MAKEDIR
,
138 afs_StaleVCache(adp
);
140 ReleaseWriteLock(&adp
->lock
);
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);
156 /* XXX: If mount point???*/
158 /* Operations with the actual dir's cache entry are further
159 * down, where the dir entry gets created.
161 } /* if (!AFS_IS_DISCON_RW) */
163 /* otherwise, we should see if we can make the change to the dir locally */
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
);
172 ZapDCE(tdc
); /* surprise error -- use invalid value */
177 ReleaseWriteLock(&tdc
->lock
);
181 if (AFS_IS_DISCON_RW
)
182 /* We will have to settle with the local link count. */
183 adp
->f
.m
.LinkCount
++;
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. */
192 /* Generate a new vcache and fill it. */
193 tvc
= afs_NewVCache(&newFid
, NULL
);
201 ObtainWriteLock(&tvc
->lock
, 738);
202 afs_GenDisconStatus(adp
, tvc
, &newFid
, attrs
, treq
, VDIR
);
203 ReleaseWriteLock(&tvc
->lock
);
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);
209 /* printf("afs_mkdir: can't get new dcache for dir.\n"); */
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); */
221 afs_PutDCache(new_dc
);
223 ObtainWriteLock(&tvc
->lock
, 731);
224 /* Update length in the vcache. */
225 tvc
->f
.m
.Length
= new_dc
->f
.chunkBytes
;
227 afs_DisconAddDirty(tvc
, VDisconCreate
, 1);
228 ReleaseWriteLock(&tvc
->lock
);
230 /* now we're done with parent dir, create the real dir's cache entry */
231 tvc
= afs_GetVCache(&newFid
, treq
, NULL
, NULL
);
237 /* For some reason, we cannot fetch the vcache for our
238 * newly-created dir. */
241 } /* if (AFS_DISCON_RW) */
246 afs_PutFakeStat(&fakestate
);
247 code
= afs_CheckCode(code
, treq
, 26);
248 afs_DestroyReq(treq
);
250 osi_FreeSmallSpace(OutFidStatus
);
251 osi_FreeSmallSpace(OutDirStatus
);
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
,
262 afs_rmdir(OSI_VC_DECL(adp
), char *aname
, afs_ucred_t
*acred
)
265 struct vrequest
*treq
= NULL
;
267 struct vcache
*tvc
= NULL
;
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
;
278 AFS_STATCNT(afs_rmdir
);
280 afs_Trace2(afs_iclSetp
, CM_TRACE_RMDIR
, ICL_TYPE_POINTER
, adp
,
281 ICL_TYPE_STRING
, aname
);
283 if ((code
= afs_CreateReq(&treq
, acred
)))
285 afs_InitFakeStat(&fakestate
);
287 if (strlen(aname
) > AFSNAMEMAX
) {
294 code
= afs_EvalFakeStat(&adp
, &fakestate
, treq
);
298 code
= afs_VerifyVCache(adp
, treq
);
302 /** If the volume is read-only, return error without making an RPC to the
305 if (adp
->f
.states
& CRO
) {
310 if (AFS_IS_DISCONNECTED
&& !AFS_IS_DISCON_RW
) {
311 /* Disconnected read only mode. */
316 tdc
= afs_GetDCache(adp
, (afs_size_t
) 0, treq
, &offset
, &len
, 1); /* test for error below */
317 ObtainWriteLock(&adp
->lock
, 154);
319 ObtainSharedLock(&tdc
->lock
, 633);
320 if (tdc
&& (adp
->f
.states
& CForeign
)) {
321 struct VenusFid unlinkFid
;
323 unlinkFid
.Fid
.Vnode
= 0;
324 code
= afs_dir_Lookup(tdc
, aname
, &unlinkFid
.Fid
);
326 afs_int32 cached
= 0;
328 unlinkFid
.Cell
= adp
->f
.fid
.Cell
;
329 unlinkFid
.Fid
.Volume
= adp
->f
.fid
.Fid
.Volume
;
330 if (unlinkFid
.Fid
.Unique
== 0) {
332 afs_LookupVCache(&unlinkFid
, treq
, &cached
, adp
, aname
);
334 ObtainReadLock(&afs_xvcache
);
335 tvc
= afs_FindVCache(&unlinkFid
, 0, 1 /* do xstats */ );
336 ReleaseReadLock(&afs_xvcache
);
341 if (!AFS_IS_DISCON_RW
) {
342 /* Not disconnected, can connect to server. */
344 tc
= afs_Conn(&adp
->f
.fid
, treq
, SHARED_LOCK
, &rxconn
);
346 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR
);
349 RXAFS_RemoveDir(rxconn
,
350 (struct AFSFid
*)&adp
->f
.fid
.Fid
,
359 (tc
, rxconn
, code
, &adp
->f
.fid
, treq
, AFS_STATS_FS_RPCIDX_REMOVEDIR
,
364 ReleaseSharedLock(&tdc
->lock
);
369 afs_StaleVCache(adp
);
371 ReleaseWriteLock(&adp
->lock
);
375 /* here if rpc worked; update the in-core link count */
376 adp
->f
.m
.LinkCount
= OutDirStatus
.LinkCount
;
382 ReleaseWriteLock(&adp
->lock
);
383 /* printf("afs_rmdir: No local dcache!\n"); */
389 /* Find the vcache. */
390 struct VenusFid tfid
;
392 tfid
.Cell
= adp
->f
.fid
.Cell
;
393 tfid
.Fid
.Volume
= adp
->f
.fid
.Fid
.Volume
;
394 code
= afs_dir_Lookup(tdc
, aname
, &tfid
.Fid
);
396 ObtainSharedLock(&afs_xvcache
, 764);
397 tvc
= afs_FindVCache(&tfid
, 0, 1 /* do xstats */ );
398 ReleaseSharedLock(&afs_xvcache
);
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
);
410 if (tvc
->f
.m
.LinkCount
> 2) {
411 /* This dir contains more than . and .., thus it can't be
414 ReleaseSharedLock(&tdc
->lock
);
417 ReleaseWriteLock(&adp
->lock
);
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)
426 if (!adp
->f
.shadow
.vnode
&& !(adp
->f
.ddirty_flags
& VDisconCreate
)) {
427 afs_MakeShadowDir(adp
, tdc
);
430 adp
->f
.m
.LinkCount
--;
431 } /* if (!AFS_IS_DISCON_RW) */
434 UpgradeSToWLock(&tdc
->lock
, 634);
435 if (AFS_IS_DISCON_RW
|| afs_LocalHero(adp
, tdc
, &OutDirStatus
, 1)) {
436 /* we can do it locally */
437 code
= afs_dir_Delete(tdc
, aname
);
439 ZapDCE(tdc
); /* surprise error -- invalid value */
444 ReleaseWriteLock(&tdc
->lock
);
445 afs_PutDCache(tdc
); /* drop ref count */
449 osi_dnlc_purgedp(tvc
); /* get rid of any entries for this directory */
451 osi_dnlc_remove(adp
, aname
, 0);
454 ObtainWriteLock(&tvc
->lock
, 155);
455 tvc
->f
.states
&= ~CUnique
; /* For the dfs xlator */
456 if (AFS_IS_DISCON_RW
) {
457 if (tvc
->f
.ddirty_flags
& VDisconCreate
) {
458 /* If we we were created whilst disconnected, removal doesn't
459 * need to get logged. Just go away gracefully */
460 afs_DisconRemoveDirty(tvc
);
462 afs_DisconAddDirty(tvc
, VDisconRemove
, 1);
465 ReleaseWriteLock(&tvc
->lock
);
468 ReleaseWriteLock(&adp
->lock
);
469 /* don't worry about link count since dirs can not be hardlinked */
474 afs_PutFakeStat(&fakestate
);
475 code
= afs_CheckCode(code
, treq
, 27);
476 afs_DestroyReq(treq
);