Commit | Line | Data |
---|---|---|
805e021f CE |
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, NULL, NULL); | |
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 | afs_int32 cached = 0; | |
327 | ||
328 | unlinkFid.Cell = adp->f.fid.Cell; | |
329 | unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume; | |
330 | if (unlinkFid.Fid.Unique == 0) { | |
331 | tvc = | |
332 | afs_LookupVCache(&unlinkFid, treq, &cached, adp, aname); | |
333 | } else { | |
334 | ObtainReadLock(&afs_xvcache); | |
335 | tvc = afs_FindVCache(&unlinkFid, 0, 1 /* do xstats */ ); | |
336 | ReleaseReadLock(&afs_xvcache); | |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | if (!AFS_IS_DISCON_RW) { | |
342 | /* Not disconnected, can connect to server. */ | |
343 | do { | |
344 | tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); | |
345 | if (tc) { | |
346 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR); | |
347 | RX_AFS_GUNLOCK(); | |
348 | code = | |
349 | RXAFS_RemoveDir(rxconn, | |
350 | (struct AFSFid *)&adp->f.fid.Fid, | |
351 | aname, | |
352 | &OutDirStatus, | |
353 | &tsync); | |
354 | RX_AFS_GLOCK(); | |
355 | XSTATS_END_TIME; | |
356 | } else | |
357 | code = -1; | |
358 | } while (afs_Analyze | |
359 | (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_REMOVEDIR, | |
360 | SHARED_LOCK, NULL)); | |
361 | ||
362 | if (code) { | |
363 | if (tdc) { | |
364 | ReleaseSharedLock(&tdc->lock); | |
365 | afs_PutDCache(tdc); | |
366 | } | |
367 | ||
368 | if (code < 0) { | |
369 | afs_StaleVCache(adp); | |
370 | } | |
371 | ReleaseWriteLock(&adp->lock); | |
372 | goto done; | |
373 | } | |
374 | ||
375 | /* here if rpc worked; update the in-core link count */ | |
376 | adp->f.m.LinkCount = OutDirStatus.LinkCount; | |
377 | ||
378 | } else { | |
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->f.fid.Cell; | |
393 | tfid.Fid.Volume = adp->f.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->f.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->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) { | |
427 | afs_MakeShadowDir(adp, tdc); | |
428 | } | |
429 | ||
430 | adp->f.m.LinkCount--; | |
431 | } /* if (!AFS_IS_DISCON_RW) */ | |
432 | ||
433 | if (tdc) | |
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); | |
438 | if (code) { | |
439 | ZapDCE(tdc); /* surprise error -- invalid value */ | |
440 | DZap(tdc); | |
441 | } | |
442 | } | |
443 | if (tdc) { | |
444 | ReleaseWriteLock(&tdc->lock); | |
445 | afs_PutDCache(tdc); /* drop ref count */ | |
446 | } | |
447 | ||
448 | if (tvc) | |
449 | osi_dnlc_purgedp(tvc); /* get rid of any entries for this directory */ | |
450 | else | |
451 | osi_dnlc_remove(adp, aname, 0); | |
452 | ||
453 | if (tvc) { | |
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); | |
461 | } else { | |
462 | afs_DisconAddDirty(tvc, VDisconRemove, 1); | |
463 | } | |
464 | } | |
465 | ReleaseWriteLock(&tvc->lock); | |
466 | afs_PutVCache(tvc); | |
467 | } | |
468 | ReleaseWriteLock(&adp->lock); | |
469 | /* don't worry about link count since dirs can not be hardlinked */ | |
470 | code = 0; | |
471 | ||
472 | done: | |
473 | AFS_DISCON_UNLOCK(); | |
474 | afs_PutFakeStat(&fakestate); | |
475 | code = afs_CheckCode(code, treq, 27); | |
476 | afs_DestroyReq(treq); | |
477 | done2: | |
478 | return code; | |
479 | } |