Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * This software has been released under the terms of the IBM Public | |
3 | * License. For details, see the LICENSE file in the top-level source | |
4 | * directory or online at http://www.openafs.org/dl/license10.html | |
5 | */ | |
6 | ||
7 | #include <afsconfig.h> | |
8 | #include "afs/param.h" | |
9 | ||
10 | ||
11 | #include "afs/sysincludes.h" | |
12 | #include "afsincludes.h" | |
13 | #include "afs/afs_stats.h" /* statistics */ | |
14 | #include "afs/lock.h" | |
15 | #include "afs/afs_cbqueue.h" | |
16 | ||
17 | #define dv_match(vc, fstat) \ | |
18 | ((vc->f.m.DataVersion.low == fstat.DataVersion) && \ | |
19 | (vc->f.m.DataVersion.high == fstat.dataVersionHigh)) | |
20 | ||
21 | /*! Circular queue of dirty vcaches */ | |
22 | struct afs_q afs_disconDirty; | |
23 | ||
24 | /*! Circular queue of vcaches with shadow directories */ | |
25 | struct afs_q afs_disconShadow; | |
26 | ||
27 | /*! Locks both of these lists. Must be write locked for anything other than | |
28 | * list traversal */ | |
29 | afs_rwlock_t afs_disconDirtyLock; | |
30 | ||
31 | extern afs_int32 *afs_dvhashTbl; /*Data cache hash table */ | |
32 | extern afs_int32 *afs_dchashTbl; /*Data cache hash table */ | |
33 | extern afs_int32 *afs_dvnextTbl; /*Dcache hash table links */ | |
34 | extern afs_int32 *afs_dcnextTbl; /*Dcache hash table links */ | |
35 | extern struct dcache **afs_indexTable; /*Pointers to dcache entries */ | |
36 | ||
37 | /*! Vnode number. On file creation, use the current value and increment it. | |
38 | */ | |
39 | afs_uint32 afs_DisconVnode = 2; | |
40 | ||
41 | /*! Conflict policy. */ | |
42 | enum { | |
43 | CLIENT_WINS = 0, | |
44 | SERVER_WINS, | |
45 | LAST_CLOSER_WINS, | |
46 | ASK | |
47 | }; | |
48 | ||
49 | afs_int32 afs_ConflictPolicy = SERVER_WINS; | |
50 | ||
51 | static void afs_DisconDiscardAllShadows(int, afs_ucred_t *); | |
52 | void afs_DbgListDirEntries(struct VenusFid *afid); | |
53 | ||
54 | ||
55 | /*! | |
56 | * Find the first dcache of a file that has the specified fid. | |
57 | * Similar to afs_FindDCache, only that it takes a fid instead | |
58 | * of a vcache and it can get the first dcache. | |
59 | * | |
60 | * \param afid | |
61 | * | |
62 | * \return The found dcache or NULL. | |
63 | */ | |
64 | struct dcache * | |
65 | afs_FindDCacheByFid(struct VenusFid *afid) | |
66 | { | |
67 | afs_int32 i, index; | |
68 | struct dcache *tdc = NULL; | |
69 | ||
70 | i = DVHash(afid); | |
71 | ObtainWriteLock(&afs_xdcache, 758); | |
72 | for (index = afs_dvhashTbl[i]; index != NULLIDX;) { | |
73 | if (afs_indexUnique[index] == afid->Fid.Unique) { | |
74 | tdc = afs_GetValidDSlot(index); | |
75 | if (!tdc) { | |
76 | index = NULLIDX; | |
77 | break; | |
78 | } | |
79 | ReleaseReadLock(&tdc->tlock); | |
80 | if (!FidCmp(&tdc->f.fid, afid)) { | |
81 | break; /* leaving refCount high for caller */ | |
82 | } | |
83 | afs_PutDCache(tdc); | |
84 | } | |
85 | index = afs_dvnextTbl[index]; | |
86 | } | |
87 | ReleaseWriteLock(&afs_xdcache); | |
88 | ||
89 | if (index == NULLIDX) | |
90 | tdc = NULL; | |
91 | return tdc; | |
92 | } | |
93 | ||
94 | /*! | |
95 | * Generate a store status from a dirty vcache entry. | |
96 | * | |
97 | * \param avc Dirty vcache entry. | |
98 | * \param astat | |
99 | * | |
100 | * \note The vnode must be share locked. It is called only on resync, | |
101 | * where the vnode is write locked locally and and the server. | |
102 | * | |
103 | * \return Mask of operations. | |
104 | */ | |
105 | int | |
106 | afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat) | |
107 | { | |
108 | if (!avc || !astat || !avc->f.ddirty_flags) | |
109 | return 0; | |
110 | ||
111 | /* Clean up store stat. */ | |
112 | memset(astat, 0, sizeof(struct AFSStoreStatus)); | |
113 | ||
114 | if (avc->f.ddirty_flags & VDisconSetTime) { | |
115 | /* Update timestamp. */ | |
116 | astat->ClientModTime = avc->f.m.Date; | |
117 | astat->Mask |= AFS_SETMODTIME; | |
118 | } | |
119 | ||
120 | if (avc->f.ddirty_flags & VDisconSetMode) { | |
121 | /* Copy the mode bits. */ | |
122 | astat->UnixModeBits = avc->f.m.Mode; | |
123 | astat->Mask |= AFS_SETMODE; | |
124 | } | |
125 | ||
126 | /* XXX: more to come... ?*/ | |
127 | ||
128 | return astat->Mask; | |
129 | } | |
130 | ||
131 | /*! | |
132 | * Hook for filtering the local dir fid by searching the "." entry. | |
133 | * | |
134 | * \param hdata The fid to be filled. | |
135 | */ | |
136 | static int | |
137 | get_parent_dir_fid_hook(void *hdata, char *aname, afs_int32 vnode, | |
138 | afs_int32 unique) | |
139 | { | |
140 | struct VenusFid *tfid = (struct VenusFid *) hdata; | |
141 | ||
142 | if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) { | |
143 | tfid->Fid.Vnode = vnode; | |
144 | tfid->Fid.Unique = unique; | |
145 | return 1; | |
146 | } | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | /*! | |
152 | * Get a the dir's fid by looking in the vcache for simple files and | |
153 | * in the ".." entry for directories. | |
154 | * | |
155 | * \param avc The file's vhash entry. | |
156 | * \param afid Put the fid here. | |
157 | * | |
158 | * \return 0 on success, -1 on failure | |
159 | */ | |
160 | int | |
161 | afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid) | |
162 | { | |
163 | struct dcache *tdc; | |
164 | ||
165 | afid->Cell = avc->f.fid.Cell; | |
166 | afid->Fid.Volume = avc->f.fid.Fid.Volume; | |
167 | ||
168 | switch (vType(avc)) { | |
169 | case VREG: | |
170 | case VLNK: | |
171 | /* Normal files have the dir fid embedded in the vcache. */ | |
172 | afid->Fid.Vnode = avc->f.parent.vnode; | |
173 | afid->Fid.Unique = avc->f.parent.unique; | |
174 | break; | |
175 | case VDIR: | |
176 | /* If dir or parent dir created locally*/ | |
177 | tdc = afs_FindDCacheByFid(&avc->f.fid); | |
178 | if (tdc) { | |
179 | afid->Fid.Unique = 0; | |
180 | /* Lookup each entry for the fid. It should be the first. */ | |
181 | afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid); | |
182 | afs_PutDCache(tdc); | |
183 | if (afid->Fid.Unique == 0) { | |
184 | return -1; | |
185 | } | |
186 | } else { | |
187 | return -1; | |
188 | } | |
189 | break; | |
190 | default: | |
191 | return -1; | |
192 | } | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | struct NameAndFid { | |
198 | struct VenusFid *fid; | |
199 | char *name; | |
200 | int name_len; | |
201 | }; | |
202 | ||
203 | /*! | |
204 | * Hook that searches a certain fid's name. | |
205 | * | |
206 | * \param hdata NameAndFid structure containin a pointer to a fid | |
207 | * and an allocate name. The name will be filled when hit. | |
208 | */ | |
209 | static int | |
210 | get_vnode_name_hook(void *hdata, char *aname, afs_int32 vnode, | |
211 | afs_int32 unique) | |
212 | { | |
213 | struct NameAndFid *nf = (struct NameAndFid *) hdata; | |
214 | ||
215 | if ((nf->fid->Fid.Vnode == vnode) && | |
216 | (nf->fid->Fid.Unique == unique)) { | |
217 | nf->name_len = strlen(aname); | |
218 | memcpy(nf->name, aname, nf->name_len); | |
219 | nf->name[nf->name_len] = 0; | |
220 | ||
221 | return 1; | |
222 | } | |
223 | ||
224 | return 0; | |
225 | } | |
226 | ||
227 | /*! | |
228 | * Try to get a vnode's name by comparing all parent dir's entries | |
229 | * to the given fid. It can also return the dir's dcache. | |
230 | * | |
231 | * \param avc The file's vcache. | |
232 | * \param afid The parent dir's fid. | |
233 | * \param aname A preallocated string for the name. | |
234 | * \param deleted Has this file been deleted? If yes, use the shadow | |
235 | * dir for looking up the name. | |
236 | */ | |
237 | int | |
238 | afs_GetVnodeName(struct vcache *avc, struct VenusFid *afid, char *aname, | |
239 | int deleted) | |
240 | { | |
241 | int code = 0; | |
242 | struct dcache *tdc; | |
243 | struct vcache *parent_vc; | |
244 | struct NameAndFid tnf; | |
245 | struct VenusFid parent_fid; | |
246 | struct VenusFid shadow_fid; | |
247 | ||
248 | /* List dir contents and get it's tdc. */ | |
249 | if (deleted) { | |
250 | /* For deleted files, get the shadow dir's tdc: */ | |
251 | ||
252 | /* Get the parent dir's vcache that contains the shadow fid. */ | |
253 | parent_fid.Cell = avc->f.fid.Cell; | |
254 | parent_fid.Fid.Volume = avc->f.fid.Fid.Volume; | |
255 | if (avc->f.ddirty_flags & VDisconRename) { | |
256 | /* For renames the old dir fid is needed. */ | |
257 | parent_fid.Fid.Vnode = avc->f.oldParent.vnode; | |
258 | parent_fid.Fid.Unique = avc->f.oldParent.unique; | |
259 | } else { | |
260 | parent_fid.Fid.Vnode = afid->Fid.Vnode; | |
261 | parent_fid.Fid.Unique = afid->Fid.Unique; | |
262 | } | |
263 | ||
264 | /* Get the parent dir's vcache that contains the shadow fid. */ | |
265 | ObtainSharedLock(&afs_xvcache, 755); | |
266 | parent_vc = afs_FindVCache(&parent_fid, 0, 1); | |
267 | ReleaseSharedLock(&afs_xvcache); | |
268 | if (!parent_vc) { | |
269 | return ENETDOWN; | |
270 | } | |
271 | ||
272 | shadow_fid.Cell = parent_vc->f.fid.Cell; | |
273 | shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume; | |
274 | shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode; | |
275 | shadow_fid.Fid.Unique = parent_vc->f.shadow.unique; | |
276 | ||
277 | afs_PutVCache(parent_vc); | |
278 | ||
279 | /* Get shadow dir's dcache. */ | |
280 | tdc = afs_FindDCacheByFid(&shadow_fid); | |
281 | ||
282 | } else { | |
283 | ||
284 | /* For normal files, look into the current dir's entry. */ | |
285 | tdc = afs_FindDCacheByFid(afid); | |
286 | } /* if (deleted) */ | |
287 | ||
288 | if (tdc) { | |
289 | tnf.fid = &avc->f.fid; | |
290 | tnf.name_len = -1; | |
291 | tnf.name = aname; | |
292 | afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf); | |
293 | afs_PutDCache(tdc); | |
294 | if (tnf.name_len == -1) | |
295 | code = ENOENT; | |
296 | } else { | |
297 | /* printf("Directory dcache not found!\n"); */ | |
298 | code = ENETDOWN; | |
299 | } | |
300 | ||
301 | return code; | |
302 | } | |
303 | ||
304 | struct DirtyChildrenCount { | |
305 | struct vcache *vc; | |
306 | afs_uint32 count; | |
307 | }; | |
308 | ||
309 | /*! | |
310 | * Lookup dirty deleted vnodes in this dir. | |
311 | */ | |
312 | static int | |
313 | chk_del_children_hook(void *hdata, char *aname, afs_int32 vnode, | |
314 | afs_int32 unique) | |
315 | { | |
316 | struct VenusFid tfid; | |
317 | struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata; | |
318 | struct vcache *tvc; | |
319 | ||
320 | if ((aname[0] == '.') && !aname[1]) | |
321 | /* Skip processing this dir again. | |
322 | * It would result in an endless loop. | |
323 | */ | |
324 | return 0; | |
325 | ||
326 | if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) | |
327 | /* Don't process parent dir. */ | |
328 | return 0; | |
329 | ||
330 | /* Get this file's vcache. */ | |
331 | tfid.Cell = v->vc->f.fid.Cell; | |
332 | tfid.Fid.Volume = v->vc->f.fid.Fid.Volume; | |
333 | tfid.Fid.Vnode = vnode; | |
334 | tfid.Fid.Unique = unique; | |
335 | ||
336 | ObtainSharedLock(&afs_xvcache, 757); | |
337 | tvc = afs_FindVCache(&tfid, 0, 1); | |
338 | ReleaseSharedLock(&afs_xvcache); | |
339 | ||
340 | /* Count unfinished dirty children. */ | |
341 | if (tvc) { | |
342 | ObtainReadLock(&tvc->lock); | |
343 | if (tvc->f.ddirty_flags) | |
344 | v->count++; | |
345 | ReleaseReadLock(&tvc->lock); | |
346 | ||
347 | afs_PutVCache(tvc); | |
348 | } | |
349 | ||
350 | return 0; | |
351 | } | |
352 | ||
353 | /*! | |
354 | * Check if entries have been deleted in a vnode's shadow | |
355 | * dir. | |
356 | * | |
357 | * \return Returns the number of dirty children. | |
358 | * | |
359 | * \note afs_DDirtyVCListLock must be write locked. | |
360 | */ | |
361 | int | |
362 | afs_CheckDeletedChildren(struct vcache *avc) | |
363 | { | |
364 | struct dcache *tdc; | |
365 | struct DirtyChildrenCount dcc; | |
366 | struct VenusFid shadow_fid; | |
367 | ||
368 | if (!avc->f.shadow.vnode) | |
369 | /* Empty dir. */ | |
370 | return 0; | |
371 | ||
372 | shadow_fid.Cell = avc->f.fid.Cell; | |
373 | shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume; | |
374 | shadow_fid.Fid.Vnode = avc->f.shadow.vnode; | |
375 | shadow_fid.Fid.Unique = avc->f.shadow.unique; | |
376 | ||
377 | dcc.count = 0; | |
378 | ||
379 | /* Get shadow dir's dcache. */ | |
380 | tdc = afs_FindDCacheByFid(&shadow_fid); | |
381 | if (tdc) { | |
382 | dcc.vc = avc; | |
383 | afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc); | |
384 | afs_PutDCache(tdc); | |
385 | } | |
386 | ||
387 | return dcc.count; | |
388 | } | |
389 | ||
390 | /*! | |
391 | * Changes a file's parent fid references. | |
392 | */ | |
393 | static int | |
394 | fix_children_fids_hook(void *hdata, char *aname, afs_int32 vnode, | |
395 | afs_int32 unique) | |
396 | { | |
397 | struct VenusFid tfid; | |
398 | struct VenusFid *afid = (struct VenusFid *) hdata; | |
399 | struct vcache *tvc; | |
400 | struct dcache *tdc = NULL; | |
401 | ||
402 | if ((aname[0] == '.') && !aname[1]) | |
403 | return 0; | |
404 | ||
405 | if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) | |
406 | return 0; | |
407 | ||
408 | tfid.Cell = afid->Cell; | |
409 | tfid.Fid.Volume = afid->Fid.Volume; | |
410 | tfid.Fid.Vnode = vnode; | |
411 | tfid.Fid.Unique = unique; | |
412 | ||
413 | if (!(vnode % 2)) { | |
414 | /* vnode's parity indicates that it's a file. */ | |
415 | ||
416 | /* Get the vcache. */ | |
417 | ObtainSharedLock(&afs_xvcache, 759); | |
418 | tvc = afs_FindVCache(&tfid, 0, 1); | |
419 | ReleaseSharedLock(&afs_xvcache); | |
420 | ||
421 | /* Change the fields. */ | |
422 | if (tvc) { | |
423 | tvc->f.parent.vnode = afid->Fid.Vnode; | |
424 | tvc->f.parent.unique = afid->Fid.Unique; | |
425 | ||
426 | afs_PutVCache(tvc); | |
427 | } | |
428 | } else { | |
429 | /* It's a dir. Fix this dir's .. entry to contain the new fid. */ | |
430 | /* Seek the dir's dcache. */ | |
431 | tdc = afs_FindDCacheByFid(&tfid); | |
432 | if (tdc) { | |
433 | /* Change the .. entry fid. */ | |
434 | afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode); | |
435 | afs_PutDCache(tdc); | |
436 | } | |
437 | } /* if (!(vnode % 2))*/ | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | /*! | |
443 | * Fixes the parentVnode and parentUnique fields of all | |
444 | * files (not dirs) contained in the directory pointed by | |
445 | * old_fid. This is useful on resync, when a locally created dir | |
446 | * get's a new fid and all the children references must be updated | |
447 | * to reflect the new fid. | |
448 | * | |
449 | * \note The dir's fid hasn't been changed yet, it is still referenced | |
450 | * with the old fid. | |
451 | * | |
452 | * \param old_fid The current dir's fid. | |
453 | * \param new_fid The new dir's fid. | |
454 | */ | |
455 | void | |
456 | afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid) | |
457 | { | |
458 | struct dcache *tdc; | |
459 | ||
460 | /* Get shadow dir's dcache. */ | |
461 | tdc = afs_FindDCacheByFid(old_fid); | |
462 | /* Change the fids. */ | |
463 | if (tdc) { | |
464 | afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid); | |
465 | afs_PutDCache(tdc); | |
466 | } | |
467 | } | |
468 | ||
469 | static int | |
470 | list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique) | |
471 | { | |
472 | /* printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique); */ | |
473 | return 0; | |
474 | } | |
475 | ||
476 | void | |
477 | afs_DbgListDirEntries(struct VenusFid *afid) | |
478 | { | |
479 | struct dcache *tdc; | |
480 | ||
481 | /* Get shadow dir's dcache. */ | |
482 | tdc = afs_FindDCacheByFid(afid); | |
483 | if (tdc) { | |
484 | afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL); | |
485 | afs_PutDCache(tdc); | |
486 | } | |
487 | } | |
488 | ||
489 | /*! | |
490 | * Find the parent vcache for a given child | |
491 | * | |
492 | * \param avc The vcache whose parent is required | |
493 | * \param afid Fid structure in which parent's fid should be stored | |
494 | * \param aname An AFSNAMEMAX sized buffer to hold the parents name | |
495 | * \param adp A pointer to a struct vcache* which will be set to the | |
496 | * parent vcache | |
497 | * | |
498 | * \return An error code. 0 indicates success, EAGAIN that the vnode should | |
499 | * be deferred to later in the resync process | |
500 | */ | |
501 | ||
502 | int | |
503 | afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid, | |
504 | char *aname, struct vcache **adp) | |
505 | { | |
506 | int code; | |
507 | ||
508 | *adp = NULL; | |
509 | ||
510 | if (afs_GetParentDirFid(avc, afid)) { | |
511 | /* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */ | |
512 | return ENETDOWN; | |
513 | } | |
514 | ||
515 | code = afs_GetVnodeName(avc, afid, aname, deleted); | |
516 | if (code) { | |
517 | /* printf("afs_GetParentVCache: Couldn't find file name\n"); */ | |
518 | goto end; | |
519 | } | |
520 | ||
521 | ObtainSharedLock(&afs_xvcache, 766); | |
522 | *adp = afs_FindVCache(afid, 0, 1); | |
523 | ReleaseSharedLock(&afs_xvcache); | |
524 | if (!*adp) { | |
525 | /* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */ | |
526 | code = ENETDOWN; | |
527 | goto end; | |
528 | } | |
529 | ||
530 | if ((*adp)->f.ddirty_flags & VDisconCreate) { | |
531 | /* printf("afs_GetParentVCache: deferring until parent exists\n"); */ | |
532 | code = EAGAIN; | |
533 | goto end; | |
534 | } | |
535 | ||
536 | end: | |
537 | if (code && *adp) { | |
538 | afs_PutVCache(*adp); | |
539 | *adp = NULL; | |
540 | } | |
541 | return code; | |
542 | } | |
543 | ||
544 | ||
545 | /*! | |
546 | * Handles file renaming on reconnection: | |
547 | * - Get the old name from the old dir's shadow dir. | |
548 | * - Get the new name from the current dir. | |
549 | * - Old dir fid and new dir fid are collected along the way. | |
550 | * */ | |
551 | int | |
552 | afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) | |
553 | { | |
554 | struct VenusFid old_pdir_fid, new_pdir_fid; | |
555 | char *old_name = NULL, *new_name = NULL; | |
556 | struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus; | |
557 | struct AFSVolSync tsync; | |
558 | struct afs_conn *tc; | |
559 | struct rx_connection *rxconn; | |
560 | afs_uint32 code = 0; | |
561 | XSTATS_DECLS; | |
562 | ||
563 | /* Get old dir vcache. */ | |
564 | old_pdir_fid.Cell = avc->f.fid.Cell; | |
565 | old_pdir_fid.Fid.Volume = avc->f.fid.Fid.Volume; | |
566 | old_pdir_fid.Fid.Vnode = avc->f.oldParent.vnode; | |
567 | old_pdir_fid.Fid.Unique = avc->f.oldParent.unique; | |
568 | ||
569 | /* Get old name. */ | |
570 | old_name = afs_osi_Alloc(AFSNAMEMAX); | |
571 | if (!old_name) { | |
572 | /* printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n"); */ | |
573 | return ENOMEM; | |
574 | } | |
575 | code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1); | |
576 | if (code) { | |
577 | /* printf("afs_ProcessOpRename: Couldn't find old name.\n"); */ | |
578 | goto done; | |
579 | } | |
580 | ||
581 | /* Alloc data first. */ | |
582 | new_name = afs_osi_Alloc(AFSNAMEMAX); | |
583 | if (!new_name) { | |
584 | /* printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); */ | |
585 | code = ENOMEM; | |
586 | goto done; | |
587 | } | |
588 | ||
589 | if (avc->f.ddirty_flags & VDisconRenameSameDir) { | |
590 | /* If we're in the same dir, don't do the lookups all over again, | |
591 | * just copy fid and vcache from the old dir. | |
592 | */ | |
593 | memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid)); | |
594 | } else { | |
595 | /* Get parent dir's FID.*/ | |
596 | if (afs_GetParentDirFid(avc, &new_pdir_fid)) { | |
597 | /* printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n"); */ | |
598 | code = ENETDOWN; | |
599 | goto done; | |
600 | } | |
601 | } | |
602 | ||
603 | /* And finally get the new name. */ | |
604 | code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0); | |
605 | if (code) { | |
606 | /* printf("afs_ProcessOpRename: Couldn't find new name.\n"); */ | |
607 | goto done; | |
608 | } | |
609 | ||
610 | /* Send to data to server. */ | |
611 | do { | |
612 | tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK, &rxconn); | |
613 | if (tc) { | |
614 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME); | |
615 | RX_AFS_GUNLOCK(); | |
616 | code = RXAFS_Rename(rxconn, | |
617 | (struct AFSFid *)&old_pdir_fid.Fid, | |
618 | old_name, | |
619 | (struct AFSFid *)&new_pdir_fid.Fid, | |
620 | new_name, | |
621 | &OutOldDirStatus, | |
622 | &OutNewDirStatus, | |
623 | &tsync); | |
624 | RX_AFS_GLOCK(); | |
625 | XSTATS_END_TIME; | |
626 | } else | |
627 | code = -1; | |
628 | ||
629 | } while (afs_Analyze(tc, | |
630 | rxconn, | |
631 | code, | |
632 | &new_pdir_fid, | |
633 | areq, | |
634 | AFS_STATS_FS_RPCIDX_RENAME, | |
635 | SHARED_LOCK, | |
636 | NULL)); | |
637 | ||
638 | /* if (code) printf("afs_ProcessOpRename: server code=%u\n", code); */ | |
639 | done: | |
640 | if (new_name) | |
641 | afs_osi_Free(new_name, AFSNAMEMAX); | |
642 | if (old_name) | |
643 | afs_osi_Free(old_name, AFSNAMEMAX); | |
644 | return code; | |
645 | } | |
646 | ||
647 | /*! | |
648 | * Handles all the reconnection details: | |
649 | * - Get all the details about the vnode: name, fid, and parent dir fid. | |
650 | * - Send data to server. | |
651 | * - Handle errors. | |
652 | * - Reorder vhash and dcaches in their hashes, using the newly acquired fid. | |
653 | */ | |
654 | int | |
655 | afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq, | |
656 | afs_ucred_t *acred) | |
657 | { | |
658 | char *tname = NULL, *ttargetName = NULL; | |
659 | struct AFSStoreStatus InStatus; | |
660 | struct AFSFetchStatus OutFidStatus, OutDirStatus; | |
661 | struct VenusFid pdir_fid, newFid; | |
662 | struct AFSCallBack CallBack; | |
663 | struct AFSVolSync tsync; | |
664 | struct vcache *tdp = NULL, *tvc = NULL; | |
665 | struct dcache *tdc = NULL; | |
666 | struct afs_conn *tc; | |
667 | struct rx_connection *rxconn; | |
668 | afs_int32 hash, new_hash, index; | |
669 | afs_size_t tlen; | |
670 | int code, op = 0; | |
671 | XSTATS_DECLS; | |
672 | ||
673 | tname = afs_osi_Alloc(AFSNAMEMAX); | |
674 | if (!tname) | |
675 | return ENOMEM; | |
676 | memset(&InStatus, 0, sizeof(InStatus)); | |
677 | ||
678 | code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp); | |
679 | if (code) | |
680 | goto end; | |
681 | ||
682 | /* This data may also be in linkData, but then we have to deal with | |
683 | * the joy of terminating NULLs and . and file modes. So just get | |
684 | * it from the dcache where it won't have been fiddled with. | |
685 | */ | |
686 | if (vType(avc) == VLNK) { | |
687 | afs_size_t offset; | |
688 | struct dcache *tdc; | |
689 | struct osi_file *tfile; | |
690 | ||
691 | tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0); | |
692 | if (!tdc) { | |
693 | code = ENETDOWN; | |
694 | goto end; | |
695 | } | |
696 | ||
697 | if (tlen > 1024) { | |
698 | afs_PutDCache(tdc); | |
699 | code = EFAULT; | |
700 | goto end; | |
701 | } | |
702 | ||
703 | tlen++; /* space for NULL */ | |
704 | ttargetName = afs_osi_Alloc(tlen); | |
705 | if (!ttargetName) { | |
706 | afs_PutDCache(tdc); | |
707 | code = ENOMEM; | |
708 | goto end; | |
709 | } | |
710 | ObtainReadLock(&tdc->lock); | |
711 | tfile = afs_CFileOpen(&tdc->f.inode); | |
712 | osi_Assert(tfile); | |
713 | code = afs_CFileRead(tfile, 0, ttargetName, tlen); | |
714 | ttargetName[tlen-1] = '\0'; | |
715 | afs_CFileClose(tfile); | |
716 | ReleaseReadLock(&tdc->lock); | |
717 | afs_PutDCache(tdc); | |
718 | } | |
719 | ||
720 | /* Set status. */ | |
721 | InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; | |
722 | InStatus.ClientModTime = avc->f.m.Date; | |
723 | InStatus.Owner = avc->f.m.Owner; | |
724 | InStatus.Group = (afs_int32) afs_cr_gid(acred); | |
725 | /* Only care about protection bits. */ | |
726 | InStatus.UnixModeBits = avc->f.m.Mode & 0xffff; | |
727 | ||
728 | do { | |
729 | tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK, &rxconn); | |
730 | if (tc) { | |
731 | switch (vType(avc)) { | |
732 | case VREG: | |
733 | /* Make file on server. */ | |
734 | op = AFS_STATS_FS_RPCIDX_CREATEFILE; | |
735 | XSTATS_START_TIME(op); | |
736 | RX_AFS_GUNLOCK(); | |
737 | code = RXAFS_CreateFile(rxconn, | |
738 | (struct AFSFid *)&tdp->f.fid.Fid, | |
739 | tname, &InStatus, | |
740 | (struct AFSFid *) &newFid.Fid, | |
741 | &OutFidStatus, &OutDirStatus, | |
742 | &CallBack, &tsync); | |
743 | RX_AFS_GLOCK(); | |
744 | XSTATS_END_TIME; | |
745 | break; | |
746 | case VDIR: | |
747 | /* Make dir on server. */ | |
748 | op = AFS_STATS_FS_RPCIDX_MAKEDIR; | |
749 | XSTATS_START_TIME(op); | |
750 | RX_AFS_GUNLOCK(); | |
751 | code = RXAFS_MakeDir(rxconn, (struct AFSFid *) &tdp->f.fid.Fid, | |
752 | tname, &InStatus, | |
753 | (struct AFSFid *) &newFid.Fid, | |
754 | &OutFidStatus, &OutDirStatus, | |
755 | &CallBack, &tsync); | |
756 | RX_AFS_GLOCK(); | |
757 | XSTATS_END_TIME; | |
758 | break; | |
759 | case VLNK: | |
760 | /* Make symlink on server. */ | |
761 | op = AFS_STATS_FS_RPCIDX_SYMLINK; | |
762 | XSTATS_START_TIME(op); | |
763 | RX_AFS_GUNLOCK(); | |
764 | code = RXAFS_Symlink(rxconn, | |
765 | (struct AFSFid *) &tdp->f.fid.Fid, | |
766 | tname, ttargetName, &InStatus, | |
767 | (struct AFSFid *) &newFid.Fid, | |
768 | &OutFidStatus, &OutDirStatus, &tsync); | |
769 | RX_AFS_GLOCK(); | |
770 | XSTATS_END_TIME; | |
771 | break; | |
772 | default: | |
773 | op = AFS_STATS_FS_RPCIDX_CREATEFILE; | |
774 | code = 1; | |
775 | break; | |
776 | } | |
777 | } else | |
778 | code = -1; | |
779 | } while (afs_Analyze(tc, rxconn, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL)); | |
780 | ||
781 | /* TODO: Handle errors. */ | |
782 | if (code) { | |
783 | /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */ | |
784 | goto end; | |
785 | } | |
786 | ||
787 | /* The rpc doesn't set the cell number. */ | |
788 | newFid.Cell = avc->f.fid.Cell; | |
789 | ||
790 | /* | |
791 | * Change the fid in the dir entry. | |
792 | */ | |
793 | ||
794 | /* Seek the dir's dcache. */ | |
795 | tdc = afs_FindDCacheByFid(&tdp->f.fid); | |
796 | if (tdc) { | |
797 | /* And now change the fid in the parent dir entry. */ | |
798 | afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode); | |
799 | afs_PutDCache(tdc); | |
800 | } | |
801 | ||
802 | if (vType(avc) == VDIR) { | |
803 | /* Change fid in the dir for the "." entry. ".." has alredy been | |
804 | * handled by afs_FixChildrenFids when processing the parent dir. | |
805 | */ | |
806 | tdc = afs_FindDCacheByFid(&avc->f.fid); | |
807 | if (tdc) { | |
808 | afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode, | |
809 | &newFid.Fid.Vnode); | |
810 | ||
811 | if (avc->f.m.LinkCount >= 2) | |
812 | /* For non empty dirs, fix children's parentVnode and | |
813 | * parentUnique reference. | |
814 | */ | |
815 | afs_FixChildrenFids(&avc->f.fid, &newFid); | |
816 | ||
817 | afs_PutDCache(tdc); | |
818 | } | |
819 | } | |
820 | ||
821 | /* Recompute hash chain positions for vnode and dcaches. | |
822 | * Then change to the new FID. | |
823 | */ | |
824 | ||
825 | /* The vcache goes first. */ | |
826 | ObtainWriteLock(&afs_xvcache, 735); | |
827 | ||
828 | /* Old fid hash. */ | |
829 | hash = VCHash(&avc->f.fid); | |
830 | /* New fid hash. */ | |
831 | new_hash = VCHash(&newFid); | |
832 | ||
833 | /* Remove hash from old position. */ | |
834 | /* XXX: not checking array element contents. It shouldn't be empty. | |
835 | * If it oopses, then something else might be wrong. | |
836 | */ | |
837 | if (afs_vhashT[hash] == avc) { | |
838 | /* First in hash chain (might be the only one). */ | |
839 | afs_vhashT[hash] = avc->hnext; | |
840 | } else { | |
841 | /* More elements in hash chain. */ | |
842 | for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) { | |
843 | if (tvc->hnext == avc) { | |
844 | tvc->hnext = avc->hnext; | |
845 | break; | |
846 | } | |
847 | } | |
848 | } /* if (!afs_vhashT[i]->hnext) */ | |
849 | QRemove(&avc->vhashq); | |
850 | ||
851 | /* Insert hash in new position. */ | |
852 | avc->hnext = afs_vhashT[new_hash]; | |
853 | afs_vhashT[new_hash] = avc; | |
854 | QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq); | |
855 | ||
856 | ReleaseWriteLock(&afs_xvcache); | |
857 | ||
858 | /* Do the same thing for all dcaches. */ | |
859 | hash = DVHash(&avc->f.fid); | |
860 | ObtainWriteLock(&afs_xdcache, 743); | |
861 | for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) { | |
862 | hash = afs_dvnextTbl[index]; | |
863 | tdc = afs_GetValidDSlot(index); | |
864 | if (!tdc) { | |
865 | ReleaseWriteLock(&afs_xdcache); | |
866 | code = EIO; | |
867 | goto end; | |
868 | } | |
869 | ReleaseReadLock(&tdc->tlock); | |
870 | if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) { | |
871 | if (!FidCmp(&tdc->f.fid, &avc->f.fid)) { | |
872 | ||
873 | /* Safer but slower. */ | |
874 | afs_HashOutDCache(tdc, 0); | |
875 | ||
876 | /* Put dcache in new positions in the dchash and dvhash. */ | |
877 | new_hash = DCHash(&newFid, tdc->f.chunk); | |
878 | afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash]; | |
879 | afs_dchashTbl[new_hash] = tdc->index; | |
880 | ||
881 | new_hash = DVHash(&newFid); | |
882 | afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash]; | |
883 | afs_dvhashTbl[new_hash] = tdc->index; | |
884 | ||
885 | afs_indexUnique[tdc->index] = newFid.Fid.Unique; | |
886 | memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid)); | |
887 | } /* if fid match */ | |
888 | } /* if uniquifier match */ | |
889 | afs_PutDCache(tdc); | |
890 | } /* for all dcaches in this hash bucket */ | |
891 | ReleaseWriteLock(&afs_xdcache); | |
892 | ||
893 | /* Now we can set the new fid. */ | |
894 | memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid)); | |
895 | ||
896 | end: | |
897 | if (tdp) | |
898 | afs_PutVCache(tdp); | |
899 | afs_osi_Free(tname, AFSNAMEMAX); | |
900 | if (ttargetName) | |
901 | afs_osi_Free(ttargetName, tlen); | |
902 | return code; | |
903 | } | |
904 | ||
905 | /*! | |
906 | * Remove a vnode on the server, be it file or directory. | |
907 | * Not much to do here only get the parent dir's fid and call the | |
908 | * removal rpc. | |
909 | * | |
910 | * \param avc The deleted vcache | |
911 | * \param areq | |
912 | * | |
913 | * \note The vcache refcount should be dropped because it points to | |
914 | * a deleted vnode and has served it's purpouse, but we drop refcount | |
915 | * on shadow dir deletio (we still need it for that). | |
916 | * | |
917 | * \note avc must be write locked. | |
918 | */ | |
919 | int | |
920 | afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq) | |
921 | { | |
922 | char *tname = NULL; | |
923 | struct AFSFetchStatus OutDirStatus; | |
924 | struct VenusFid pdir_fid; | |
925 | struct AFSVolSync tsync; | |
926 | struct afs_conn *tc; | |
927 | struct rx_connection *rxconn; | |
928 | struct vcache *tdp = NULL; | |
929 | int code = 0; | |
930 | XSTATS_DECLS; | |
931 | ||
932 | tname = afs_osi_Alloc(AFSNAMEMAX); | |
933 | if (!tname) { | |
934 | /* printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n"); */ | |
935 | return ENOMEM; | |
936 | } | |
937 | ||
938 | code = afs_GetParentVCache(avc, 1, &pdir_fid, tname, &tdp); | |
939 | if (code) | |
940 | goto end; | |
941 | ||
942 | if ((vType(avc) == VDIR) && (afs_CheckDeletedChildren(avc))) { | |
943 | /* Deleted children of this dir remain unsynchronized. | |
944 | * Defer this vcache. | |
945 | */ | |
946 | code = EAGAIN; | |
947 | goto end; | |
948 | } | |
949 | ||
950 | if (vType(avc) == VREG || vType(avc) == VLNK) { | |
951 | /* Remove file on server. */ | |
952 | do { | |
953 | tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK, &rxconn); | |
954 | if (tc) { | |
955 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE); | |
956 | RX_AFS_GUNLOCK(); | |
957 | code = RXAFS_RemoveFile(rxconn, | |
958 | &pdir_fid.Fid, | |
959 | tname, | |
960 | &OutDirStatus, | |
961 | &tsync); | |
962 | ||
963 | RX_AFS_GLOCK(); | |
964 | XSTATS_END_TIME; | |
965 | } else | |
966 | code = -1; | |
967 | } while (afs_Analyze(tc, | |
968 | rxconn, | |
969 | code, | |
970 | &pdir_fid, | |
971 | areq, | |
972 | AFS_STATS_FS_RPCIDX_REMOVEFILE, | |
973 | SHARED_LOCK, | |
974 | NULL)); | |
975 | ||
976 | } else if (vType(avc) == VDIR) { | |
977 | /* Remove dir on server. */ | |
978 | do { | |
979 | tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK, &rxconn); | |
980 | if (tc) { | |
981 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR); | |
982 | RX_AFS_GUNLOCK(); | |
983 | code = RXAFS_RemoveDir(rxconn, | |
984 | &pdir_fid.Fid, | |
985 | tname, | |
986 | &OutDirStatus, | |
987 | &tsync); | |
988 | RX_AFS_GLOCK(); | |
989 | XSTATS_END_TIME; | |
990 | } else | |
991 | code = -1; | |
992 | } while (afs_Analyze(tc, | |
993 | rxconn, | |
994 | code, | |
995 | &pdir_fid, | |
996 | areq, | |
997 | AFS_STATS_FS_RPCIDX_REMOVEDIR, | |
998 | SHARED_LOCK, | |
999 | NULL)); | |
1000 | ||
1001 | } /* if (vType(avc) == VREG) */ | |
1002 | ||
1003 | /* if (code) printf("afs_ProcessOpRemove: server returned code=%u\n", code); */ | |
1004 | ||
1005 | end: | |
1006 | afs_osi_Free(tname, AFSNAMEMAX); | |
1007 | return code; | |
1008 | } | |
1009 | ||
1010 | /*! | |
1011 | * Send disconnected file changes to the server. | |
1012 | * | |
1013 | * \note Call with vnode locked both locally and on the server. | |
1014 | * | |
1015 | * \param avc Vnode that gets synchronized to the server. | |
1016 | * \param areq Used for obtaining a conn struct. | |
1017 | * | |
1018 | * \return 0 for success. On failure, other error codes. | |
1019 | */ | |
1020 | int | |
1021 | afs_SendChanges(struct vcache *avc, struct vrequest *areq) | |
1022 | { | |
1023 | struct afs_conn *tc; | |
1024 | struct rx_connection *rxconn; | |
1025 | struct AFSStoreStatus sstat; | |
1026 | struct AFSFetchStatus fstat; | |
1027 | struct AFSVolSync tsync; | |
1028 | int code = 0; | |
1029 | int flags = 0; | |
1030 | XSTATS_DECLS; | |
1031 | ||
1032 | /* Start multiplexing dirty operations from ddirty_flags field: */ | |
1033 | if (avc->f.ddirty_flags & VDisconSetAttrMask) { | |
1034 | /* Setattr OPS: */ | |
1035 | /* Turn dirty vc data into a new store status... */ | |
1036 | if (afs_GenStoreStatus(avc, &sstat) > 0) { | |
1037 | do { | |
1038 | tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn); | |
1039 | if (tc) { | |
1040 | /* ... and send it. */ | |
1041 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS); | |
1042 | RX_AFS_GUNLOCK(); | |
1043 | code = RXAFS_StoreStatus(rxconn, | |
1044 | (struct AFSFid *) &avc->f.fid.Fid, | |
1045 | &sstat, | |
1046 | &fstat, | |
1047 | &tsync); | |
1048 | ||
1049 | RX_AFS_GLOCK(); | |
1050 | XSTATS_END_TIME; | |
1051 | } else | |
1052 | code = -1; | |
1053 | ||
1054 | } while (afs_Analyze(tc, | |
1055 | rxconn, | |
1056 | code, | |
1057 | &avc->f.fid, | |
1058 | areq, | |
1059 | AFS_STATS_FS_RPCIDX_STORESTATUS, | |
1060 | SHARED_LOCK, | |
1061 | NULL)); | |
1062 | ||
1063 | } /* if (afs_GenStoreStatus() > 0)*/ | |
1064 | } /* disconnected SETATTR */ | |
1065 | ||
1066 | if (code) | |
1067 | return code; | |
1068 | ||
1069 | if (avc->f.ddirty_flags & | |
1070 | (VDisconTrunc | |
1071 | | VDisconWriteClose | |
1072 | | VDisconWriteFlush | |
1073 | | VDisconWriteOsiFlush)) { | |
1074 | ||
1075 | /* Truncate OP: */ | |
1076 | do { | |
1077 | tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn); | |
1078 | if (tc) { | |
1079 | /* Set storing flags. XXX: A tad inefficient ... */ | |
1080 | if (avc->f.ddirty_flags & VDisconWriteClose) | |
1081 | flags |= AFS_LASTSTORE; | |
1082 | if (avc->f.ddirty_flags & VDisconWriteOsiFlush) | |
1083 | flags |= (AFS_SYNC | AFS_LASTSTORE); | |
1084 | if (avc->f.ddirty_flags & VDisconWriteFlush) | |
1085 | flags |= AFS_SYNC; | |
1086 | ||
1087 | /* Try to send store to server. */ | |
1088 | /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */ | |
1089 | code = afs_StoreAllSegments(avc, areq, flags); | |
1090 | } else | |
1091 | code = -1; | |
1092 | ||
1093 | } while (afs_Analyze(tc, | |
1094 | rxconn, | |
1095 | code, | |
1096 | &avc->f.fid, | |
1097 | areq, | |
1098 | AFS_STATS_FS_RPCIDX_STOREDATA, | |
1099 | SHARED_LOCK, | |
1100 | NULL)); | |
1101 | ||
1102 | } /* disconnected TRUNC | WRITE */ | |
1103 | ||
1104 | return code; | |
1105 | } | |
1106 | ||
1107 | /*! | |
1108 | * All files that have been dirty before disconnection are going to | |
1109 | * be replayed back to the server. | |
1110 | * | |
1111 | * \param areq Request from the user. | |
1112 | * \param acred User credentials. | |
1113 | * | |
1114 | * \return If all files synchronized succesfully, return 0, otherwise | |
1115 | * return error code | |
1116 | * | |
1117 | * \note For now, it's the request from the PDiscon pioctl. | |
1118 | * | |
1119 | */ | |
1120 | int | |
1121 | afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred) | |
1122 | { | |
1123 | struct afs_conn *tc; | |
1124 | struct rx_connection *rxconn; | |
1125 | struct vcache *tvc; | |
1126 | struct AFSFetchStatus fstat; | |
1127 | struct AFSCallBack callback; | |
1128 | struct AFSVolSync tsync; | |
1129 | int code = 0; | |
1130 | afs_int32 start = 0; | |
1131 | XSTATS_DECLS; | |
1132 | /*AFS_STATCNT(afs_ResyncDisconFiles);*/ | |
1133 | ||
1134 | ObtainWriteLock(&afs_disconDirtyLock, 707); | |
1135 | ||
1136 | while (!QEmpty(&afs_disconDirty)) { | |
1137 | tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq); | |
1138 | ||
1139 | /* Can't lock tvc whilst holding the discon dirty lock */ | |
1140 | ReleaseWriteLock(&afs_disconDirtyLock); | |
1141 | ||
1142 | /* Get local write lock. */ | |
1143 | ObtainWriteLock(&tvc->lock, 705); | |
1144 | ||
1145 | if (tvc->f.ddirty_flags & VDisconRemove) { | |
1146 | /* Delete the file on the server and just move on | |
1147 | * to the next file. After all, it has been deleted | |
1148 | * we can't replay any other operation it. | |
1149 | */ | |
1150 | code = afs_ProcessOpRemove(tvc, areq); | |
1151 | goto next_file; | |
1152 | ||
1153 | } else if (tvc->f.ddirty_flags & VDisconCreate) { | |
1154 | /* For newly created files, we don't need a server lock. */ | |
1155 | code = afs_ProcessOpCreate(tvc, areq, acred); | |
1156 | if (code) | |
1157 | goto next_file; | |
1158 | ||
1159 | tvc->f.ddirty_flags &= ~VDisconCreate; | |
1160 | tvc->f.ddirty_flags |= VDisconCreated; | |
1161 | } | |
1162 | #if 0 | |
1163 | /* Get server write lock. */ | |
1164 | do { | |
1165 | tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn); | |
1166 | if (tc) { | |
1167 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK); | |
1168 | RX_AFS_GUNLOCK(); | |
1169 | code = RXAFS_SetLock(rxconn, | |
1170 | (struct AFSFid *)&tvc->f.fid.Fid, | |
1171 | LockWrite, | |
1172 | &tsync); | |
1173 | RX_AFS_GLOCK(); | |
1174 | XSTATS_END_TIME; | |
1175 | } else | |
1176 | code = -1; | |
1177 | ||
1178 | } while (afs_Analyze(tc, | |
1179 | rxconn, | |
1180 | code, | |
1181 | &tvc->f.fid, | |
1182 | areq, | |
1183 | AFS_STATS_FS_RPCIDX_SETLOCK, | |
1184 | SHARED_LOCK, | |
1185 | NULL)); | |
1186 | ||
1187 | if (code) | |
1188 | goto next_file; | |
1189 | #endif | |
1190 | if (tvc->f.ddirty_flags & VDisconRename) { | |
1191 | /* If we're renaming the file, do so now */ | |
1192 | code = afs_ProcessOpRename(tvc, areq); | |
1193 | if (code) | |
1194 | goto unlock_srv_file; | |
1195 | } | |
1196 | ||
1197 | /* Issue a FetchStatus to get info about DV and callbacks. */ | |
1198 | do { | |
1199 | tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn); | |
1200 | if (tc) { | |
1201 | tvc->callback = tc->parent->srvr->server; | |
1202 | start = osi_Time(); | |
1203 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS); | |
1204 | RX_AFS_GUNLOCK(); | |
1205 | code = RXAFS_FetchStatus(rxconn, | |
1206 | (struct AFSFid *)&tvc->f.fid.Fid, | |
1207 | &fstat, | |
1208 | &callback, | |
1209 | &tsync); | |
1210 | RX_AFS_GLOCK(); | |
1211 | XSTATS_END_TIME; | |
1212 | } else | |
1213 | code = -1; | |
1214 | ||
1215 | } while (afs_Analyze(tc, | |
1216 | rxconn, | |
1217 | code, | |
1218 | &tvc->f.fid, | |
1219 | areq, | |
1220 | AFS_STATS_FS_RPCIDX_FETCHSTATUS, | |
1221 | SHARED_LOCK, | |
1222 | NULL)); | |
1223 | ||
1224 | if (code) { | |
1225 | goto unlock_srv_file; | |
1226 | } | |
1227 | ||
1228 | if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) || | |
1229 | (afs_ConflictPolicy == CLIENT_WINS) || | |
1230 | (tvc->f.ddirty_flags & VDisconCreated)) { | |
1231 | /* | |
1232 | * Send changes to the server if there's data version match, or | |
1233 | * client wins policy has been selected or file has been created | |
1234 | * but doesn't have it's the contents on to the server yet. | |
1235 | */ | |
1236 | /* | |
1237 | * XXX: Checking server attr changes by timestamp might not the | |
1238 | * most elegant solution, but it's the most viable one that we could find. | |
1239 | */ | |
1240 | afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start); | |
1241 | code = afs_SendChanges(tvc, areq); | |
1242 | ||
1243 | } else if (afs_ConflictPolicy == SERVER_WINS) { | |
1244 | /* DV mismatch, apply collision resolution policy. */ | |
1245 | /* Discard this files chunks and remove from current dir. */ | |
1246 | afs_ResetVCache(tvc, acred, 0); | |
1247 | tvc->f.truncPos = AFS_NOTRUNC; | |
1248 | } else { | |
1249 | /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */ | |
1250 | } /* if DV match or client wins policy */ | |
1251 | ||
1252 | unlock_srv_file: | |
1253 | /* Release server write lock. */ | |
1254 | #if 0 | |
1255 | do { | |
1256 | tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn); | |
1257 | if (tc) { | |
1258 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK); | |
1259 | RX_AFS_GUNLOCK(); | |
1260 | ucode = RXAFS_ReleaseLock(rxconn, | |
1261 | (struct AFSFid *) &tvc->f.fid.Fid, | |
1262 | &tsync); | |
1263 | RX_AFS_GLOCK(); | |
1264 | XSTATS_END_TIME; | |
1265 | } else | |
1266 | ucode = -1; | |
1267 | } while (afs_Analyze(tc, | |
1268 | rxconn, | |
1269 | ucode, | |
1270 | &tvc->f.fid, | |
1271 | areq, | |
1272 | AFS_STATS_FS_RPCIDX_RELEASELOCK, | |
1273 | SHARED_LOCK, | |
1274 | NULL)); | |
1275 | #endif | |
1276 | next_file: | |
1277 | ObtainWriteLock(&afs_disconDirtyLock, 710); | |
1278 | if (code == 0) { | |
1279 | /* Replayed successfully - pull the vcache from the | |
1280 | * disconnected list */ | |
1281 | tvc->f.ddirty_flags = 0; | |
1282 | QRemove(&tvc->dirtyq); | |
1283 | afs_PutVCache(tvc); | |
1284 | } else { | |
1285 | if (code == EAGAIN) { | |
1286 | /* Operation was deferred. Pull it from the current place in | |
1287 | * the list, and stick it at the end again */ | |
1288 | QRemove(&tvc->dirtyq); | |
1289 | QAdd(&afs_disconDirty, &tvc->dirtyq); | |
1290 | } else { | |
1291 | /* Failed - keep state as is, and let the user know we died */ | |
1292 | ||
1293 | ReleaseWriteLock(&tvc->lock); | |
1294 | break; | |
1295 | } | |
1296 | } | |
1297 | ||
1298 | /* Release local write lock. */ | |
1299 | ReleaseWriteLock(&tvc->lock); | |
1300 | } /* while (tvc) */ | |
1301 | ||
1302 | if (code) { | |
1303 | ReleaseWriteLock(&afs_disconDirtyLock); | |
1304 | return code; | |
1305 | } | |
1306 | ||
1307 | /* Dispose of all of the shadow directories */ | |
1308 | afs_DisconDiscardAllShadows(0, acred); | |
1309 | ||
1310 | ReleaseWriteLock(&afs_disconDirtyLock); | |
1311 | return code; | |
1312 | } | |
1313 | ||
1314 | /*! | |
1315 | * Discard all of our shadow directory copies. If squash is true, then | |
1316 | * we also invalidate the vcache holding the shadow directory, to ensure | |
1317 | * that any disconnected changes are deleted | |
1318 | * | |
1319 | * \param squash | |
1320 | * \param acred | |
1321 | * | |
1322 | * \note afs_disconDirtyLock must be held on entry. It will be released | |
1323 | * and reobtained | |
1324 | */ | |
1325 | ||
1326 | static void | |
1327 | afs_DisconDiscardAllShadows(int squash, afs_ucred_t *acred) | |
1328 | { | |
1329 | struct vcache *tvc; | |
1330 | ||
1331 | while (!QEmpty(&afs_disconShadow)) { | |
1332 | tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq); | |
1333 | ||
1334 | /* Must release the dirty lock to be able to get a vcache lock */ | |
1335 | ReleaseWriteLock(&afs_disconDirtyLock); | |
1336 | ObtainWriteLock(&tvc->lock, 706); | |
1337 | ||
1338 | if (squash) | |
1339 | afs_ResetVCache(tvc, acred, 0); | |
1340 | ||
1341 | afs_DeleteShadowDir(tvc); | |
1342 | ||
1343 | ReleaseWriteLock(&tvc->lock); | |
1344 | ObtainWriteLock(&afs_disconDirtyLock, 709); | |
1345 | } /* while (tvc) */ | |
1346 | } | |
1347 | ||
1348 | /*! | |
1349 | * This function throws away the whole disconnected state, allowing | |
1350 | * the cache manager to reconnect to a server if we get into a state | |
1351 | * where reconiliation is impossible. | |
1352 | * | |
1353 | * \param acred | |
1354 | * | |
1355 | */ | |
1356 | void | |
1357 | afs_DisconDiscardAll(afs_ucred_t *acred) | |
1358 | { | |
1359 | struct vcache *tvc; | |
1360 | ||
1361 | ObtainWriteLock(&afs_disconDirtyLock, 717); | |
1362 | while (!QEmpty(&afs_disconDirty)) { | |
1363 | tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq); | |
1364 | QRemove(&tvc->dirtyq); | |
1365 | ReleaseWriteLock(&afs_disconDirtyLock); | |
1366 | ||
1367 | ObtainWriteLock(&tvc->lock, 718); | |
1368 | afs_ResetVCache(tvc, acred, 0); | |
1369 | tvc->f.truncPos = AFS_NOTRUNC; | |
1370 | ReleaseWriteLock(&tvc->lock); | |
1371 | ObtainWriteLock(&afs_disconDirtyLock, 719); | |
1372 | afs_PutVCache(tvc); | |
1373 | } | |
1374 | ||
1375 | afs_DisconDiscardAllShadows(1, acred); | |
1376 | ||
1377 | ReleaseWriteLock(&afs_disconDirtyLock); | |
1378 | } | |
1379 | ||
1380 | /*! | |
1381 | * Print list of disconnected files. | |
1382 | * | |
1383 | * \note Call with afs_DDirtyVCListLock read locked. | |
1384 | */ | |
1385 | void | |
1386 | afs_DbgDisconFiles(void) | |
1387 | { | |
1388 | struct vcache *tvc; | |
1389 | struct afs_q *q; | |
1390 | int i = 0; | |
1391 | ||
1392 | afs_warn("List of dirty files: \n"); | |
1393 | ||
1394 | ObtainReadLock(&afs_disconDirtyLock); | |
1395 | for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) { | |
1396 | tvc = QEntry(q, struct vcache, dirtyq); | |
1397 | ||
1398 | afs_warn("Cell=%u Volume=%u VNode=%u Unique=%u\n", | |
1399 | tvc->f.fid.Cell, | |
1400 | tvc->f.fid.Fid.Volume, | |
1401 | tvc->f.fid.Fid.Vnode, | |
1402 | tvc->f.fid.Fid.Unique); | |
1403 | ||
1404 | i++; | |
1405 | if (i >= 30) | |
1406 | osi_Panic("afs_DbgDisconFiles: loop in dirty list\n"); | |
1407 | } | |
1408 | ReleaseReadLock(&afs_disconDirtyLock); | |
1409 | } | |
1410 | ||
1411 | /*! | |
1412 | * Generate a fake fid for a disconnected shadow dir. | |
1413 | * Similar to afs_GenFakeFid, only that it uses the dhash | |
1414 | * to search for a uniquifier because a shadow dir lives only | |
1415 | * in the dcache. | |
1416 | * | |
1417 | * \param afid | |
1418 | * | |
1419 | * \note Don't forget to fill in afid with Cell and Volume. | |
1420 | */ | |
1421 | void | |
1422 | afs_GenShadowFid(struct VenusFid *afid) | |
1423 | { | |
1424 | afs_uint32 i, index, max_unique = 1; | |
1425 | struct vcache *tvc = NULL; | |
1426 | ||
1427 | /* Try generating a fid that isn't used in the vhash. */ | |
1428 | do { | |
1429 | /* Shadow Fids are always directories */ | |
1430 | afid->Fid.Vnode = afs_DisconVnode + 1; | |
1431 | ||
1432 | i = DVHash(afid); | |
1433 | ObtainWriteLock(&afs_xdcache, 737); | |
1434 | for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) { | |
1435 | i = afs_dvnextTbl[index]; | |
1436 | if (afs_indexUnique[index] > max_unique) | |
1437 | max_unique = afs_indexUnique[index]; | |
1438 | } | |
1439 | ||
1440 | ReleaseWriteLock(&afs_xdcache); | |
1441 | afid->Fid.Unique = max_unique + 1; | |
1442 | afs_DisconVnode += 2; | |
1443 | if (!afs_DisconVnode) | |
1444 | afs_DisconVnode = 2; | |
1445 | ||
1446 | /* Is this a used vnode? */ | |
1447 | ObtainSharedLock(&afs_xvcache, 762); | |
1448 | tvc = afs_FindVCache(afid, 0, 1); | |
1449 | ReleaseSharedLock(&afs_xvcache); | |
1450 | if (tvc) | |
1451 | afs_PutVCache(tvc); | |
1452 | } while (tvc); | |
1453 | } | |
1454 | ||
1455 | /*! | |
1456 | * Generate a fake fid (vnode and uniquifier) for a vcache | |
1457 | * (either dir or normal file). The vnode is generated via | |
1458 | * afs_DisconVNode and the uniquifier by getting the highest | |
1459 | * uniquifier on a hash chain and incrementing it by one. | |
1460 | * | |
1461 | * \param afid The fid structre that will be filled. | |
1462 | * \param avtype Vnode type: VDIR/VREG. | |
1463 | * \param lock True indicates that xvcache may be obtained, | |
1464 | * False that it is already held | |
1465 | * | |
1466 | * \note The cell number must be completed somewhere else. | |
1467 | */ | |
1468 | void | |
1469 | afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype, int lock) | |
1470 | { | |
1471 | struct vcache *tvc; | |
1472 | afs_uint32 max_unique = 0, i; | |
1473 | ||
1474 | switch (avtype) { | |
1475 | case VDIR: | |
1476 | afid->Fid.Vnode = afs_DisconVnode + 1; | |
1477 | break; | |
1478 | case VREG: | |
1479 | case VLNK: | |
1480 | afid->Fid.Vnode = afs_DisconVnode; | |
1481 | break; | |
1482 | } | |
1483 | ||
1484 | if (lock) | |
1485 | ObtainWriteLock(&afs_xvcache, 736); | |
1486 | i = VCHash(afid); | |
1487 | for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) { | |
1488 | if (tvc->f.fid.Fid.Unique > max_unique) | |
1489 | max_unique = tvc->f.fid.Fid.Unique; | |
1490 | } | |
1491 | if (lock) | |
1492 | ReleaseWriteLock(&afs_xvcache); | |
1493 | ||
1494 | afid->Fid.Unique = max_unique + 1; | |
1495 | afs_DisconVnode += 2; | |
1496 | if (!afs_DisconVnode) | |
1497 | afs_DisconVnode = 2; | |
1498 | } | |
1499 | ||
1500 | /*! | |
1501 | * Fill in stats for a newly created file/directory. | |
1502 | * | |
1503 | * \param adp The parent dir's vcache. | |
1504 | * \param avc The created vnode. | |
1505 | * \param afid The new fid. | |
1506 | * \param attrs | |
1507 | * \param areq | |
1508 | * \param file_type Specify if file or directory. | |
1509 | * | |
1510 | * \note Call with avc write locked. | |
1511 | */ | |
1512 | void | |
1513 | afs_GenDisconStatus(struct vcache *adp, struct vcache *avc, | |
1514 | struct VenusFid *afid, struct vattr *attrs, | |
1515 | struct vrequest *areq, int file_type) | |
1516 | { | |
1517 | afs_hyper_t zero; | |
1518 | memcpy(&avc->f.fid, afid, sizeof(struct VenusFid)); | |
1519 | avc->f.m.Mode = attrs->va_mode; | |
1520 | /* Used to do this: | |
1521 | * avc->f.m.Owner = attrs->va_uid; | |
1522 | * But now we use the parent dir's ownership, | |
1523 | * there's no other way to get a server owner id. | |
1524 | * XXX: Does it really matter? | |
1525 | */ | |
1526 | avc->f.m.Group = adp->f.m.Group; | |
1527 | avc->f.m.Owner = adp->f.m.Owner; | |
1528 | hzero(zero); | |
1529 | afs_SetDataVersion(avc, &zero); | |
1530 | avc->f.m.Length = attrs->va_size; | |
1531 | avc->f.m.Date = osi_Time(); | |
1532 | switch(file_type) { | |
1533 | case VREG: | |
1534 | vSetType(avc, VREG); | |
1535 | avc->f.m.Mode |= S_IFREG; | |
1536 | avc->f.m.LinkCount = 1; | |
1537 | avc->f.parent.vnode = adp->f.fid.Fid.Vnode; | |
1538 | avc->f.parent.unique = adp->f.fid.Fid.Unique; | |
1539 | break; | |
1540 | case VDIR: | |
1541 | vSetType(avc, VDIR); | |
1542 | avc->f.m.Mode |= S_IFDIR; | |
1543 | avc->f.m.LinkCount = 2; | |
1544 | break; | |
1545 | case VLNK: | |
1546 | vSetType(avc, VLNK); | |
1547 | avc->f.m.Mode |= S_IFLNK; | |
1548 | if ((avc->f.m.Mode & 0111) == 0) | |
1549 | avc->mvstat = AFS_MVSTAT_MTPT; | |
1550 | avc->f.parent.vnode = adp->f.fid.Fid.Vnode; | |
1551 | avc->f.parent.unique = adp->f.fid.Fid.Unique; | |
1552 | break; | |
1553 | default: | |
1554 | break; | |
1555 | } | |
1556 | avc->f.anyAccess = adp->f.anyAccess; | |
1557 | afs_AddAxs(avc->Access, areq->uid, adp->Access->axess); | |
1558 | ||
1559 | avc->callback = NULL; | |
1560 | avc->f.states |= CStatd; | |
1561 | avc->f.states &= ~CBulkFetching; | |
1562 | } |