]>
Commit | Line | Data |
---|---|---|
c088e31d SF |
1 | /* |
2 | * Copyright (C) 2005-2017 Junjiro R. Okajima | |
3 | * | |
4 | * This program, aufs is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | /* | |
19 | * virtual or vertical directory | |
20 | */ | |
21 | ||
22 | #include "aufs.h" | |
23 | ||
24 | static unsigned int calc_size(int nlen) | |
25 | { | |
26 | return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); | |
27 | } | |
28 | ||
29 | static int set_deblk_end(union au_vdir_deblk_p *p, | |
30 | union au_vdir_deblk_p *deblk_end) | |
31 | { | |
32 | if (calc_size(0) <= deblk_end->deblk - p->deblk) { | |
33 | p->de->de_str.len = 0; | |
34 | /* smp_mb(); */ | |
35 | return 0; | |
36 | } | |
37 | return -1; /* error */ | |
38 | } | |
39 | ||
40 | /* returns true or false */ | |
41 | static int is_deblk_end(union au_vdir_deblk_p *p, | |
42 | union au_vdir_deblk_p *deblk_end) | |
43 | { | |
44 | if (calc_size(0) <= deblk_end->deblk - p->deblk) | |
45 | return !p->de->de_str.len; | |
46 | return 1; | |
47 | } | |
48 | ||
49 | static unsigned char *last_deblk(struct au_vdir *vdir) | |
50 | { | |
51 | return vdir->vd_deblk[vdir->vd_nblk - 1]; | |
52 | } | |
53 | ||
54 | /* ---------------------------------------------------------------------- */ | |
55 | ||
56 | /* estimate the appropriate size for name hash table */ | |
57 | unsigned int au_rdhash_est(loff_t sz) | |
58 | { | |
59 | unsigned int n; | |
60 | ||
61 | n = UINT_MAX; | |
62 | sz >>= 10; | |
63 | if (sz < n) | |
64 | n = sz; | |
65 | if (sz < AUFS_RDHASH_DEF) | |
66 | n = AUFS_RDHASH_DEF; | |
67 | /* pr_info("n %u\n", n); */ | |
68 | return n; | |
69 | } | |
70 | ||
71 | /* | |
72 | * the allocated memory has to be freed by | |
73 | * au_nhash_wh_free() or au_nhash_de_free(). | |
74 | */ | |
75 | int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) | |
76 | { | |
77 | struct hlist_head *head; | |
78 | unsigned int u; | |
79 | size_t sz; | |
80 | ||
81 | sz = sizeof(*nhash->nh_head) * num_hash; | |
82 | head = kmalloc(sz, gfp); | |
83 | if (head) { | |
84 | nhash->nh_num = num_hash; | |
85 | nhash->nh_head = head; | |
86 | for (u = 0; u < num_hash; u++) | |
87 | INIT_HLIST_HEAD(head++); | |
88 | return 0; /* success */ | |
89 | } | |
90 | ||
91 | return -ENOMEM; | |
92 | } | |
93 | ||
94 | static void nhash_count(struct hlist_head *head) | |
95 | { | |
96 | #if 0 | |
97 | unsigned long n; | |
98 | struct hlist_node *pos; | |
99 | ||
100 | n = 0; | |
101 | hlist_for_each(pos, head) | |
102 | n++; | |
103 | pr_info("%lu\n", n); | |
104 | #endif | |
105 | } | |
106 | ||
107 | static void au_nhash_wh_do_free(struct hlist_head *head) | |
108 | { | |
109 | struct au_vdir_wh *pos; | |
110 | struct hlist_node *node; | |
111 | ||
112 | hlist_for_each_entry_safe(pos, node, head, wh_hash) | |
113 | kfree(pos); | |
114 | } | |
115 | ||
116 | static void au_nhash_de_do_free(struct hlist_head *head) | |
117 | { | |
118 | struct au_vdir_dehstr *pos; | |
119 | struct hlist_node *node; | |
120 | ||
121 | hlist_for_each_entry_safe(pos, node, head, hash) | |
122 | au_cache_free_vdir_dehstr(pos); | |
123 | } | |
124 | ||
125 | static void au_nhash_do_free(struct au_nhash *nhash, | |
126 | void (*free)(struct hlist_head *head)) | |
127 | { | |
128 | unsigned int n; | |
129 | struct hlist_head *head; | |
130 | ||
131 | n = nhash->nh_num; | |
132 | if (!n) | |
133 | return; | |
134 | ||
135 | head = nhash->nh_head; | |
136 | while (n-- > 0) { | |
137 | nhash_count(head); | |
138 | free(head++); | |
139 | } | |
140 | kfree(nhash->nh_head); | |
141 | } | |
142 | ||
143 | void au_nhash_wh_free(struct au_nhash *whlist) | |
144 | { | |
145 | au_nhash_do_free(whlist, au_nhash_wh_do_free); | |
146 | } | |
147 | ||
148 | static void au_nhash_de_free(struct au_nhash *delist) | |
149 | { | |
150 | au_nhash_do_free(delist, au_nhash_de_do_free); | |
151 | } | |
152 | ||
153 | /* ---------------------------------------------------------------------- */ | |
154 | ||
155 | int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, | |
156 | int limit) | |
157 | { | |
158 | int num; | |
159 | unsigned int u, n; | |
160 | struct hlist_head *head; | |
161 | struct au_vdir_wh *pos; | |
162 | ||
163 | num = 0; | |
164 | n = whlist->nh_num; | |
165 | head = whlist->nh_head; | |
166 | for (u = 0; u < n; u++, head++) | |
167 | hlist_for_each_entry(pos, head, wh_hash) | |
168 | if (pos->wh_bindex == btgt && ++num > limit) | |
169 | return 1; | |
170 | return 0; | |
171 | } | |
172 | ||
173 | static struct hlist_head *au_name_hash(struct au_nhash *nhash, | |
174 | unsigned char *name, | |
175 | unsigned int len) | |
176 | { | |
177 | unsigned int v; | |
178 | /* const unsigned int magic_bit = 12; */ | |
179 | ||
180 | AuDebugOn(!nhash->nh_num || !nhash->nh_head); | |
181 | ||
182 | v = 0; | |
183 | if (len > 8) | |
184 | len = 8; | |
185 | while (len--) | |
186 | v += *name++; | |
187 | /* v = hash_long(v, magic_bit); */ | |
188 | v %= nhash->nh_num; | |
189 | return nhash->nh_head + v; | |
190 | } | |
191 | ||
192 | static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, | |
193 | int nlen) | |
194 | { | |
195 | return str->len == nlen && !memcmp(str->name, name, nlen); | |
196 | } | |
197 | ||
198 | /* returns found or not */ | |
199 | int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) | |
200 | { | |
201 | struct hlist_head *head; | |
202 | struct au_vdir_wh *pos; | |
203 | struct au_vdir_destr *str; | |
204 | ||
205 | head = au_name_hash(whlist, name, nlen); | |
206 | hlist_for_each_entry(pos, head, wh_hash) { | |
207 | str = &pos->wh_str; | |
208 | AuDbg("%.*s\n", str->len, str->name); | |
209 | if (au_nhash_test_name(str, name, nlen)) | |
210 | return 1; | |
211 | } | |
212 | return 0; | |
213 | } | |
214 | ||
215 | /* returns found(true) or not */ | |
216 | static int test_known(struct au_nhash *delist, char *name, int nlen) | |
217 | { | |
218 | struct hlist_head *head; | |
219 | struct au_vdir_dehstr *pos; | |
220 | struct au_vdir_destr *str; | |
221 | ||
222 | head = au_name_hash(delist, name, nlen); | |
223 | hlist_for_each_entry(pos, head, hash) { | |
224 | str = pos->str; | |
225 | AuDbg("%.*s\n", str->len, str->name); | |
226 | if (au_nhash_test_name(str, name, nlen)) | |
227 | return 1; | |
228 | } | |
229 | return 0; | |
230 | } | |
231 | ||
232 | static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, | |
233 | unsigned char d_type) | |
234 | { | |
235 | #ifdef CONFIG_AUFS_SHWH | |
236 | wh->wh_ino = ino; | |
237 | wh->wh_type = d_type; | |
238 | #endif | |
239 | } | |
240 | ||
241 | /* ---------------------------------------------------------------------- */ | |
242 | ||
243 | int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, | |
244 | unsigned int d_type, aufs_bindex_t bindex, | |
245 | unsigned char shwh) | |
246 | { | |
247 | int err; | |
248 | struct au_vdir_destr *str; | |
249 | struct au_vdir_wh *wh; | |
250 | ||
251 | AuDbg("%.*s\n", nlen, name); | |
252 | AuDebugOn(!whlist->nh_num || !whlist->nh_head); | |
253 | ||
254 | err = -ENOMEM; | |
255 | wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); | |
256 | if (unlikely(!wh)) | |
257 | goto out; | |
258 | ||
259 | err = 0; | |
260 | wh->wh_bindex = bindex; | |
261 | if (shwh) | |
262 | au_shwh_init_wh(wh, ino, d_type); | |
263 | str = &wh->wh_str; | |
264 | str->len = nlen; | |
265 | memcpy(str->name, name, nlen); | |
266 | hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); | |
267 | /* smp_mb(); */ | |
268 | ||
269 | out: | |
270 | return err; | |
271 | } | |
272 | ||
273 | static int append_deblk(struct au_vdir *vdir) | |
274 | { | |
275 | int err; | |
276 | unsigned long ul; | |
277 | const unsigned int deblk_sz = vdir->vd_deblk_sz; | |
278 | union au_vdir_deblk_p p, deblk_end; | |
279 | unsigned char **o; | |
280 | ||
281 | err = -ENOMEM; | |
282 | o = au_krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), | |
283 | GFP_NOFS, /*may_shrink*/0); | |
284 | if (unlikely(!o)) | |
285 | goto out; | |
286 | ||
287 | vdir->vd_deblk = o; | |
288 | p.deblk = kmalloc(deblk_sz, GFP_NOFS); | |
289 | if (p.deblk) { | |
290 | ul = vdir->vd_nblk++; | |
291 | vdir->vd_deblk[ul] = p.deblk; | |
292 | vdir->vd_last.ul = ul; | |
293 | vdir->vd_last.p.deblk = p.deblk; | |
294 | deblk_end.deblk = p.deblk + deblk_sz; | |
295 | err = set_deblk_end(&p, &deblk_end); | |
296 | } | |
297 | ||
298 | out: | |
299 | return err; | |
300 | } | |
301 | ||
302 | static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, | |
303 | unsigned int d_type, struct au_nhash *delist) | |
304 | { | |
305 | int err; | |
306 | unsigned int sz; | |
307 | const unsigned int deblk_sz = vdir->vd_deblk_sz; | |
308 | union au_vdir_deblk_p p, *room, deblk_end; | |
309 | struct au_vdir_dehstr *dehstr; | |
310 | ||
311 | p.deblk = last_deblk(vdir); | |
312 | deblk_end.deblk = p.deblk + deblk_sz; | |
313 | room = &vdir->vd_last.p; | |
314 | AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk | |
315 | || !is_deblk_end(room, &deblk_end)); | |
316 | ||
317 | sz = calc_size(nlen); | |
318 | if (unlikely(sz > deblk_end.deblk - room->deblk)) { | |
319 | err = append_deblk(vdir); | |
320 | if (unlikely(err)) | |
321 | goto out; | |
322 | ||
323 | p.deblk = last_deblk(vdir); | |
324 | deblk_end.deblk = p.deblk + deblk_sz; | |
325 | /* smp_mb(); */ | |
326 | AuDebugOn(room->deblk != p.deblk); | |
327 | } | |
328 | ||
329 | err = -ENOMEM; | |
330 | dehstr = au_cache_alloc_vdir_dehstr(); | |
331 | if (unlikely(!dehstr)) | |
332 | goto out; | |
333 | ||
334 | dehstr->str = &room->de->de_str; | |
335 | hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); | |
336 | room->de->de_ino = ino; | |
337 | room->de->de_type = d_type; | |
338 | room->de->de_str.len = nlen; | |
339 | memcpy(room->de->de_str.name, name, nlen); | |
340 | ||
341 | err = 0; | |
342 | room->deblk += sz; | |
343 | if (unlikely(set_deblk_end(room, &deblk_end))) | |
344 | err = append_deblk(vdir); | |
345 | /* smp_mb(); */ | |
346 | ||
347 | out: | |
348 | return err; | |
349 | } | |
350 | ||
351 | /* ---------------------------------------------------------------------- */ | |
352 | ||
353 | void au_vdir_free(struct au_vdir *vdir) | |
354 | { | |
355 | unsigned char **deblk; | |
356 | ||
357 | deblk = vdir->vd_deblk; | |
358 | while (vdir->vd_nblk--) | |
359 | kfree(*deblk++); | |
360 | kfree(vdir->vd_deblk); | |
361 | au_cache_free_vdir(vdir); | |
362 | } | |
363 | ||
364 | static struct au_vdir *alloc_vdir(struct file *file) | |
365 | { | |
366 | struct au_vdir *vdir; | |
367 | struct super_block *sb; | |
368 | int err; | |
369 | ||
370 | sb = file->f_path.dentry->d_sb; | |
371 | SiMustAnyLock(sb); | |
372 | ||
373 | err = -ENOMEM; | |
374 | vdir = au_cache_alloc_vdir(); | |
375 | if (unlikely(!vdir)) | |
376 | goto out; | |
377 | ||
378 | vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); | |
379 | if (unlikely(!vdir->vd_deblk)) | |
380 | goto out_free; | |
381 | ||
382 | vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; | |
383 | if (!vdir->vd_deblk_sz) { | |
384 | /* estimate the appropriate size for deblk */ | |
385 | vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); | |
386 | /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ | |
387 | } | |
388 | vdir->vd_nblk = 0; | |
389 | vdir->vd_version = 0; | |
390 | vdir->vd_jiffy = 0; | |
391 | err = append_deblk(vdir); | |
392 | if (!err) | |
393 | return vdir; /* success */ | |
394 | ||
395 | kfree(vdir->vd_deblk); | |
396 | ||
397 | out_free: | |
398 | au_cache_free_vdir(vdir); | |
399 | out: | |
400 | vdir = ERR_PTR(err); | |
401 | return vdir; | |
402 | } | |
403 | ||
404 | static int reinit_vdir(struct au_vdir *vdir) | |
405 | { | |
406 | int err; | |
407 | union au_vdir_deblk_p p, deblk_end; | |
408 | ||
409 | while (vdir->vd_nblk > 1) { | |
410 | kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); | |
411 | /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ | |
412 | vdir->vd_nblk--; | |
413 | } | |
414 | p.deblk = vdir->vd_deblk[0]; | |
415 | deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; | |
416 | err = set_deblk_end(&p, &deblk_end); | |
417 | /* keep vd_dblk_sz */ | |
418 | vdir->vd_last.ul = 0; | |
419 | vdir->vd_last.p.deblk = vdir->vd_deblk[0]; | |
420 | vdir->vd_version = 0; | |
421 | vdir->vd_jiffy = 0; | |
422 | /* smp_mb(); */ | |
423 | return err; | |
424 | } | |
425 | ||
426 | /* ---------------------------------------------------------------------- */ | |
427 | ||
428 | #define AuFillVdir_CALLED 1 | |
429 | #define AuFillVdir_WHABLE (1 << 1) | |
430 | #define AuFillVdir_SHWH (1 << 2) | |
431 | #define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) | |
432 | #define au_fset_fillvdir(flags, name) \ | |
433 | do { (flags) |= AuFillVdir_##name; } while (0) | |
434 | #define au_fclr_fillvdir(flags, name) \ | |
435 | do { (flags) &= ~AuFillVdir_##name; } while (0) | |
436 | ||
437 | #ifndef CONFIG_AUFS_SHWH | |
438 | #undef AuFillVdir_SHWH | |
439 | #define AuFillVdir_SHWH 0 | |
440 | #endif | |
441 | ||
442 | struct fillvdir_arg { | |
443 | struct dir_context ctx; | |
444 | struct file *file; | |
445 | struct au_vdir *vdir; | |
446 | struct au_nhash delist; | |
447 | struct au_nhash whlist; | |
448 | aufs_bindex_t bindex; | |
449 | unsigned int flags; | |
450 | int err; | |
451 | }; | |
452 | ||
453 | static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, | |
454 | loff_t offset __maybe_unused, u64 h_ino, | |
455 | unsigned int d_type) | |
456 | { | |
457 | struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); | |
458 | char *name = (void *)__name; | |
459 | struct super_block *sb; | |
460 | ino_t ino; | |
461 | const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); | |
462 | ||
463 | arg->err = 0; | |
464 | sb = arg->file->f_path.dentry->d_sb; | |
465 | au_fset_fillvdir(arg->flags, CALLED); | |
466 | /* smp_mb(); */ | |
467 | if (nlen <= AUFS_WH_PFX_LEN | |
468 | || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { | |
469 | if (test_known(&arg->delist, name, nlen) | |
470 | || au_nhash_test_known_wh(&arg->whlist, name, nlen)) | |
471 | goto out; /* already exists or whiteouted */ | |
472 | ||
473 | arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); | |
474 | if (!arg->err) { | |
475 | if (unlikely(nlen > AUFS_MAX_NAMELEN)) | |
476 | d_type = DT_UNKNOWN; | |
477 | arg->err = append_de(arg->vdir, name, nlen, ino, | |
478 | d_type, &arg->delist); | |
479 | } | |
480 | } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { | |
481 | name += AUFS_WH_PFX_LEN; | |
482 | nlen -= AUFS_WH_PFX_LEN; | |
483 | if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) | |
484 | goto out; /* already whiteouted */ | |
485 | ||
486 | if (shwh) | |
487 | arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, | |
488 | &ino); | |
489 | if (!arg->err) { | |
490 | if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) | |
491 | d_type = DT_UNKNOWN; | |
492 | arg->err = au_nhash_append_wh | |
493 | (&arg->whlist, name, nlen, ino, d_type, | |
494 | arg->bindex, shwh); | |
495 | } | |
496 | } | |
497 | ||
498 | out: | |
499 | if (!arg->err) | |
500 | arg->vdir->vd_jiffy = jiffies; | |
501 | /* smp_mb(); */ | |
502 | AuTraceErr(arg->err); | |
503 | return arg->err; | |
504 | } | |
505 | ||
506 | static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, | |
507 | struct au_nhash *whlist, struct au_nhash *delist) | |
508 | { | |
509 | #ifdef CONFIG_AUFS_SHWH | |
510 | int err; | |
511 | unsigned int nh, u; | |
512 | struct hlist_head *head; | |
513 | struct au_vdir_wh *pos; | |
514 | struct hlist_node *n; | |
515 | char *p, *o; | |
516 | struct au_vdir_destr *destr; | |
517 | ||
518 | AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); | |
519 | ||
520 | err = -ENOMEM; | |
521 | o = p = (void *)__get_free_page(GFP_NOFS); | |
522 | if (unlikely(!p)) | |
523 | goto out; | |
524 | ||
525 | err = 0; | |
526 | nh = whlist->nh_num; | |
527 | memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); | |
528 | p += AUFS_WH_PFX_LEN; | |
529 | for (u = 0; u < nh; u++) { | |
530 | head = whlist->nh_head + u; | |
531 | hlist_for_each_entry_safe(pos, n, head, wh_hash) { | |
532 | destr = &pos->wh_str; | |
533 | memcpy(p, destr->name, destr->len); | |
534 | err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, | |
535 | pos->wh_ino, pos->wh_type, delist); | |
536 | if (unlikely(err)) | |
537 | break; | |
538 | } | |
539 | } | |
540 | ||
541 | free_page((unsigned long)o); | |
542 | ||
543 | out: | |
544 | AuTraceErr(err); | |
545 | return err; | |
546 | #else | |
547 | return 0; | |
548 | #endif | |
549 | } | |
550 | ||
551 | static int au_do_read_vdir(struct fillvdir_arg *arg) | |
552 | { | |
553 | int err; | |
554 | unsigned int rdhash; | |
555 | loff_t offset; | |
556 | aufs_bindex_t bbot, bindex, btop; | |
557 | unsigned char shwh; | |
558 | struct file *hf, *file; | |
559 | struct super_block *sb; | |
560 | ||
561 | file = arg->file; | |
562 | sb = file->f_path.dentry->d_sb; | |
563 | SiMustAnyLock(sb); | |
564 | ||
565 | rdhash = au_sbi(sb)->si_rdhash; | |
566 | if (!rdhash) | |
567 | rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); | |
568 | err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); | |
569 | if (unlikely(err)) | |
570 | goto out; | |
571 | err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); | |
572 | if (unlikely(err)) | |
573 | goto out_delist; | |
574 | ||
575 | err = 0; | |
576 | arg->flags = 0; | |
577 | shwh = 0; | |
578 | if (au_opt_test(au_mntflags(sb), SHWH)) { | |
579 | shwh = 1; | |
580 | au_fset_fillvdir(arg->flags, SHWH); | |
581 | } | |
582 | btop = au_fbtop(file); | |
583 | bbot = au_fbbot_dir(file); | |
584 | for (bindex = btop; !err && bindex <= bbot; bindex++) { | |
585 | hf = au_hf_dir(file, bindex); | |
586 | if (!hf) | |
587 | continue; | |
588 | ||
589 | offset = vfsub_llseek(hf, 0, SEEK_SET); | |
590 | err = offset; | |
591 | if (unlikely(offset)) | |
592 | break; | |
593 | ||
594 | arg->bindex = bindex; | |
595 | au_fclr_fillvdir(arg->flags, WHABLE); | |
596 | if (shwh | |
597 | || (bindex != bbot | |
598 | && au_br_whable(au_sbr_perm(sb, bindex)))) | |
599 | au_fset_fillvdir(arg->flags, WHABLE); | |
600 | do { | |
601 | arg->err = 0; | |
602 | au_fclr_fillvdir(arg->flags, CALLED); | |
603 | /* smp_mb(); */ | |
604 | err = vfsub_iterate_dir(hf, &arg->ctx); | |
605 | if (err >= 0) | |
606 | err = arg->err; | |
607 | } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); | |
608 | ||
609 | /* | |
610 | * dir_relax() may be good for concurrency, but aufs should not | |
611 | * use it since it will cause a lockdep problem. | |
612 | */ | |
613 | } | |
614 | ||
615 | if (!err && shwh) | |
616 | err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); | |
617 | ||
618 | au_nhash_wh_free(&arg->whlist); | |
619 | ||
620 | out_delist: | |
621 | au_nhash_de_free(&arg->delist); | |
622 | out: | |
623 | return err; | |
624 | } | |
625 | ||
626 | static int read_vdir(struct file *file, int may_read) | |
627 | { | |
628 | int err; | |
629 | unsigned long expire; | |
630 | unsigned char do_read; | |
631 | struct fillvdir_arg arg = { | |
632 | .ctx = { | |
633 | .actor = fillvdir | |
634 | } | |
635 | }; | |
636 | struct inode *inode; | |
637 | struct au_vdir *vdir, *allocated; | |
638 | ||
639 | err = 0; | |
640 | inode = file_inode(file); | |
641 | IMustLock(inode); | |
642 | IiMustWriteLock(inode); | |
643 | SiMustAnyLock(inode->i_sb); | |
644 | ||
645 | allocated = NULL; | |
646 | do_read = 0; | |
647 | expire = au_sbi(inode->i_sb)->si_rdcache; | |
648 | vdir = au_ivdir(inode); | |
649 | if (!vdir) { | |
650 | do_read = 1; | |
651 | vdir = alloc_vdir(file); | |
652 | err = PTR_ERR(vdir); | |
653 | if (IS_ERR(vdir)) | |
654 | goto out; | |
655 | err = 0; | |
656 | allocated = vdir; | |
657 | } else if (may_read | |
658 | && (inode->i_version != vdir->vd_version | |
659 | || time_after(jiffies, vdir->vd_jiffy + expire))) { | |
660 | do_read = 1; | |
661 | err = reinit_vdir(vdir); | |
662 | if (unlikely(err)) | |
663 | goto out; | |
664 | } | |
665 | ||
666 | if (!do_read) | |
667 | return 0; /* success */ | |
668 | ||
669 | arg.file = file; | |
670 | arg.vdir = vdir; | |
671 | err = au_do_read_vdir(&arg); | |
672 | if (!err) { | |
673 | /* file->f_pos = 0; */ /* todo: ctx->pos? */ | |
674 | vdir->vd_version = inode->i_version; | |
675 | vdir->vd_last.ul = 0; | |
676 | vdir->vd_last.p.deblk = vdir->vd_deblk[0]; | |
677 | if (allocated) | |
678 | au_set_ivdir(inode, allocated); | |
679 | } else if (allocated) | |
680 | au_vdir_free(allocated); | |
681 | ||
682 | out: | |
683 | return err; | |
684 | } | |
685 | ||
686 | static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) | |
687 | { | |
688 | int err, rerr; | |
689 | unsigned long ul, n; | |
690 | const unsigned int deblk_sz = src->vd_deblk_sz; | |
691 | ||
692 | AuDebugOn(tgt->vd_nblk != 1); | |
693 | ||
694 | err = -ENOMEM; | |
695 | if (tgt->vd_nblk < src->vd_nblk) { | |
696 | unsigned char **p; | |
697 | ||
698 | p = au_krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, | |
699 | GFP_NOFS, /*may_shrink*/0); | |
700 | if (unlikely(!p)) | |
701 | goto out; | |
702 | tgt->vd_deblk = p; | |
703 | } | |
704 | ||
705 | if (tgt->vd_deblk_sz != deblk_sz) { | |
706 | unsigned char *p; | |
707 | ||
708 | tgt->vd_deblk_sz = deblk_sz; | |
709 | p = au_krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS, | |
710 | /*may_shrink*/1); | |
711 | if (unlikely(!p)) | |
712 | goto out; | |
713 | tgt->vd_deblk[0] = p; | |
714 | } | |
715 | memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); | |
716 | tgt->vd_version = src->vd_version; | |
717 | tgt->vd_jiffy = src->vd_jiffy; | |
718 | ||
719 | n = src->vd_nblk; | |
720 | for (ul = 1; ul < n; ul++) { | |
721 | tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, | |
722 | GFP_NOFS); | |
723 | if (unlikely(!tgt->vd_deblk[ul])) | |
724 | goto out; | |
725 | tgt->vd_nblk++; | |
726 | } | |
727 | tgt->vd_nblk = n; | |
728 | tgt->vd_last.ul = tgt->vd_last.ul; | |
729 | tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; | |
730 | tgt->vd_last.p.deblk += src->vd_last.p.deblk | |
731 | - src->vd_deblk[src->vd_last.ul]; | |
732 | /* smp_mb(); */ | |
733 | return 0; /* success */ | |
734 | ||
735 | out: | |
736 | rerr = reinit_vdir(tgt); | |
737 | BUG_ON(rerr); | |
738 | return err; | |
739 | } | |
740 | ||
741 | int au_vdir_init(struct file *file) | |
742 | { | |
743 | int err; | |
744 | struct inode *inode; | |
745 | struct au_vdir *vdir_cache, *allocated; | |
746 | ||
747 | /* test file->f_pos here instead of ctx->pos */ | |
748 | err = read_vdir(file, !file->f_pos); | |
749 | if (unlikely(err)) | |
750 | goto out; | |
751 | ||
752 | allocated = NULL; | |
753 | vdir_cache = au_fvdir_cache(file); | |
754 | if (!vdir_cache) { | |
755 | vdir_cache = alloc_vdir(file); | |
756 | err = PTR_ERR(vdir_cache); | |
757 | if (IS_ERR(vdir_cache)) | |
758 | goto out; | |
759 | allocated = vdir_cache; | |
760 | } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { | |
761 | /* test file->f_pos here instead of ctx->pos */ | |
762 | err = reinit_vdir(vdir_cache); | |
763 | if (unlikely(err)) | |
764 | goto out; | |
765 | } else | |
766 | return 0; /* success */ | |
767 | ||
768 | inode = file_inode(file); | |
769 | err = copy_vdir(vdir_cache, au_ivdir(inode)); | |
770 | if (!err) { | |
771 | file->f_version = inode->i_version; | |
772 | if (allocated) | |
773 | au_set_fvdir_cache(file, allocated); | |
774 | } else if (allocated) | |
775 | au_vdir_free(allocated); | |
776 | ||
777 | out: | |
778 | return err; | |
779 | } | |
780 | ||
781 | static loff_t calc_offset(struct au_vdir *vdir) | |
782 | { | |
783 | loff_t offset; | |
784 | union au_vdir_deblk_p p; | |
785 | ||
786 | p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; | |
787 | offset = vdir->vd_last.p.deblk - p.deblk; | |
788 | offset += vdir->vd_deblk_sz * vdir->vd_last.ul; | |
789 | return offset; | |
790 | } | |
791 | ||
792 | /* returns true or false */ | |
793 | static int seek_vdir(struct file *file, struct dir_context *ctx) | |
794 | { | |
795 | int valid; | |
796 | unsigned int deblk_sz; | |
797 | unsigned long ul, n; | |
798 | loff_t offset; | |
799 | union au_vdir_deblk_p p, deblk_end; | |
800 | struct au_vdir *vdir_cache; | |
801 | ||
802 | valid = 1; | |
803 | vdir_cache = au_fvdir_cache(file); | |
804 | offset = calc_offset(vdir_cache); | |
805 | AuDbg("offset %lld\n", offset); | |
806 | if (ctx->pos == offset) | |
807 | goto out; | |
808 | ||
809 | vdir_cache->vd_last.ul = 0; | |
810 | vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; | |
811 | if (!ctx->pos) | |
812 | goto out; | |
813 | ||
814 | valid = 0; | |
815 | deblk_sz = vdir_cache->vd_deblk_sz; | |
816 | ul = div64_u64(ctx->pos, deblk_sz); | |
817 | AuDbg("ul %lu\n", ul); | |
818 | if (ul >= vdir_cache->vd_nblk) | |
819 | goto out; | |
820 | ||
821 | n = vdir_cache->vd_nblk; | |
822 | for (; ul < n; ul++) { | |
823 | p.deblk = vdir_cache->vd_deblk[ul]; | |
824 | deblk_end.deblk = p.deblk + deblk_sz; | |
825 | offset = ul; | |
826 | offset *= deblk_sz; | |
827 | while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { | |
828 | unsigned int l; | |
829 | ||
830 | l = calc_size(p.de->de_str.len); | |
831 | offset += l; | |
832 | p.deblk += l; | |
833 | } | |
834 | if (!is_deblk_end(&p, &deblk_end)) { | |
835 | valid = 1; | |
836 | vdir_cache->vd_last.ul = ul; | |
837 | vdir_cache->vd_last.p = p; | |
838 | break; | |
839 | } | |
840 | } | |
841 | ||
842 | out: | |
843 | /* smp_mb(); */ | |
844 | AuTraceErr(!valid); | |
845 | return valid; | |
846 | } | |
847 | ||
848 | int au_vdir_fill_de(struct file *file, struct dir_context *ctx) | |
849 | { | |
850 | unsigned int l, deblk_sz; | |
851 | union au_vdir_deblk_p deblk_end; | |
852 | struct au_vdir *vdir_cache; | |
853 | struct au_vdir_de *de; | |
854 | ||
855 | vdir_cache = au_fvdir_cache(file); | |
856 | if (!seek_vdir(file, ctx)) | |
857 | return 0; | |
858 | ||
859 | deblk_sz = vdir_cache->vd_deblk_sz; | |
860 | while (1) { | |
861 | deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; | |
862 | deblk_end.deblk += deblk_sz; | |
863 | while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { | |
864 | de = vdir_cache->vd_last.p.de; | |
865 | AuDbg("%.*s, off%lld, i%lu, dt%d\n", | |
866 | de->de_str.len, de->de_str.name, ctx->pos, | |
867 | (unsigned long)de->de_ino, de->de_type); | |
868 | if (unlikely(!dir_emit(ctx, de->de_str.name, | |
869 | de->de_str.len, de->de_ino, | |
870 | de->de_type))) { | |
871 | /* todo: ignore the error caused by udba? */ | |
872 | /* return err; */ | |
873 | return 0; | |
874 | } | |
875 | ||
876 | l = calc_size(de->de_str.len); | |
877 | vdir_cache->vd_last.p.deblk += l; | |
878 | ctx->pos += l; | |
879 | } | |
880 | if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { | |
881 | vdir_cache->vd_last.ul++; | |
882 | vdir_cache->vd_last.p.deblk | |
883 | = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; | |
884 | ctx->pos = deblk_sz * vdir_cache->vd_last.ul; | |
885 | continue; | |
886 | } | |
887 | break; | |
888 | } | |
889 | ||
890 | /* smp_mb(); */ | |
891 | return 0; | |
892 | } |