]>
Commit | Line | Data |
---|---|---|
e14748e8 SF |
1 | /* |
2 | * Copyright (C) 2005-2016 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 | au_delayed_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_dfree_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 | au_delayed_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, int atonce) | |
354 | { | |
355 | unsigned char **deblk; | |
356 | ||
357 | deblk = vdir->vd_deblk; | |
358 | if (!atonce) { | |
359 | while (vdir->vd_nblk--) | |
360 | au_delayed_kfree(*deblk++); | |
361 | au_delayed_kfree(vdir->vd_deblk); | |
362 | au_cache_dfree_vdir(vdir); | |
363 | } else { | |
364 | /* not delayed */ | |
365 | while (vdir->vd_nblk--) | |
366 | kfree(*deblk++); | |
367 | kfree(vdir->vd_deblk); | |
368 | au_cache_free_vdir(vdir); | |
369 | } | |
370 | } | |
371 | ||
372 | static struct au_vdir *alloc_vdir(struct file *file) | |
373 | { | |
374 | struct au_vdir *vdir; | |
375 | struct super_block *sb; | |
376 | int err; | |
377 | ||
378 | sb = file->f_path.dentry->d_sb; | |
379 | SiMustAnyLock(sb); | |
380 | ||
381 | err = -ENOMEM; | |
382 | vdir = au_cache_alloc_vdir(); | |
383 | if (unlikely(!vdir)) | |
384 | goto out; | |
385 | ||
386 | vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); | |
387 | if (unlikely(!vdir->vd_deblk)) | |
388 | goto out_free; | |
389 | ||
390 | vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; | |
391 | if (!vdir->vd_deblk_sz) { | |
392 | /* estimate the appropriate size for deblk */ | |
393 | vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); | |
394 | /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ | |
395 | } | |
396 | vdir->vd_nblk = 0; | |
397 | vdir->vd_version = 0; | |
398 | vdir->vd_jiffy = 0; | |
399 | err = append_deblk(vdir); | |
400 | if (!err) | |
401 | return vdir; /* success */ | |
402 | ||
403 | au_delayed_kfree(vdir->vd_deblk); | |
404 | ||
405 | out_free: | |
406 | au_cache_dfree_vdir(vdir); | |
407 | out: | |
408 | vdir = ERR_PTR(err); | |
409 | return vdir; | |
410 | } | |
411 | ||
412 | static int reinit_vdir(struct au_vdir *vdir) | |
413 | { | |
414 | int err; | |
415 | union au_vdir_deblk_p p, deblk_end; | |
416 | ||
417 | while (vdir->vd_nblk > 1) { | |
418 | au_delayed_kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); | |
419 | /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ | |
420 | vdir->vd_nblk--; | |
421 | } | |
422 | p.deblk = vdir->vd_deblk[0]; | |
423 | deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; | |
424 | err = set_deblk_end(&p, &deblk_end); | |
425 | /* keep vd_dblk_sz */ | |
426 | vdir->vd_last.ul = 0; | |
427 | vdir->vd_last.p.deblk = vdir->vd_deblk[0]; | |
428 | vdir->vd_version = 0; | |
429 | vdir->vd_jiffy = 0; | |
430 | /* smp_mb(); */ | |
431 | return err; | |
432 | } | |
433 | ||
434 | /* ---------------------------------------------------------------------- */ | |
435 | ||
436 | #define AuFillVdir_CALLED 1 | |
437 | #define AuFillVdir_WHABLE (1 << 1) | |
438 | #define AuFillVdir_SHWH (1 << 2) | |
439 | #define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) | |
440 | #define au_fset_fillvdir(flags, name) \ | |
441 | do { (flags) |= AuFillVdir_##name; } while (0) | |
442 | #define au_fclr_fillvdir(flags, name) \ | |
443 | do { (flags) &= ~AuFillVdir_##name; } while (0) | |
444 | ||
445 | #ifndef CONFIG_AUFS_SHWH | |
446 | #undef AuFillVdir_SHWH | |
447 | #define AuFillVdir_SHWH 0 | |
448 | #endif | |
449 | ||
450 | struct fillvdir_arg { | |
451 | struct dir_context ctx; | |
452 | struct file *file; | |
453 | struct au_vdir *vdir; | |
454 | struct au_nhash delist; | |
455 | struct au_nhash whlist; | |
456 | aufs_bindex_t bindex; | |
457 | unsigned int flags; | |
458 | int err; | |
459 | }; | |
460 | ||
461 | static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, | |
462 | loff_t offset __maybe_unused, u64 h_ino, | |
463 | unsigned int d_type) | |
464 | { | |
465 | struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); | |
466 | char *name = (void *)__name; | |
467 | struct super_block *sb; | |
468 | ino_t ino; | |
469 | const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); | |
470 | ||
471 | arg->err = 0; | |
472 | sb = arg->file->f_path.dentry->d_sb; | |
473 | au_fset_fillvdir(arg->flags, CALLED); | |
474 | /* smp_mb(); */ | |
475 | if (nlen <= AUFS_WH_PFX_LEN | |
476 | || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { | |
477 | if (test_known(&arg->delist, name, nlen) | |
478 | || au_nhash_test_known_wh(&arg->whlist, name, nlen)) | |
479 | goto out; /* already exists or whiteouted */ | |
480 | ||
481 | arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); | |
482 | if (!arg->err) { | |
483 | if (unlikely(nlen > AUFS_MAX_NAMELEN)) | |
484 | d_type = DT_UNKNOWN; | |
485 | arg->err = append_de(arg->vdir, name, nlen, ino, | |
486 | d_type, &arg->delist); | |
487 | } | |
488 | } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { | |
489 | name += AUFS_WH_PFX_LEN; | |
490 | nlen -= AUFS_WH_PFX_LEN; | |
491 | if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) | |
492 | goto out; /* already whiteouted */ | |
493 | ||
494 | if (shwh) | |
495 | arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, | |
496 | &ino); | |
497 | if (!arg->err) { | |
498 | if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) | |
499 | d_type = DT_UNKNOWN; | |
500 | arg->err = au_nhash_append_wh | |
501 | (&arg->whlist, name, nlen, ino, d_type, | |
502 | arg->bindex, shwh); | |
503 | } | |
504 | } | |
505 | ||
506 | out: | |
507 | if (!arg->err) | |
508 | arg->vdir->vd_jiffy = jiffies; | |
509 | /* smp_mb(); */ | |
510 | AuTraceErr(arg->err); | |
511 | return arg->err; | |
512 | } | |
513 | ||
514 | static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, | |
515 | struct au_nhash *whlist, struct au_nhash *delist) | |
516 | { | |
517 | #ifdef CONFIG_AUFS_SHWH | |
518 | int err; | |
519 | unsigned int nh, u; | |
520 | struct hlist_head *head; | |
521 | struct au_vdir_wh *pos; | |
522 | struct hlist_node *n; | |
523 | char *p, *o; | |
524 | struct au_vdir_destr *destr; | |
525 | ||
526 | AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); | |
527 | ||
528 | err = -ENOMEM; | |
529 | o = p = (void *)__get_free_page(GFP_NOFS); | |
530 | if (unlikely(!p)) | |
531 | goto out; | |
532 | ||
533 | err = 0; | |
534 | nh = whlist->nh_num; | |
535 | memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); | |
536 | p += AUFS_WH_PFX_LEN; | |
537 | for (u = 0; u < nh; u++) { | |
538 | head = whlist->nh_head + u; | |
539 | hlist_for_each_entry_safe(pos, n, head, wh_hash) { | |
540 | destr = &pos->wh_str; | |
541 | memcpy(p, destr->name, destr->len); | |
542 | err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, | |
543 | pos->wh_ino, pos->wh_type, delist); | |
544 | if (unlikely(err)) | |
545 | break; | |
546 | } | |
547 | } | |
548 | ||
549 | au_delayed_free_page((unsigned long)o); | |
550 | ||
551 | out: | |
552 | AuTraceErr(err); | |
553 | return err; | |
554 | #else | |
555 | return 0; | |
556 | #endif | |
557 | } | |
558 | ||
559 | static int au_do_read_vdir(struct fillvdir_arg *arg) | |
560 | { | |
561 | int err; | |
562 | unsigned int rdhash; | |
563 | loff_t offset; | |
564 | aufs_bindex_t bbot, bindex, btop; | |
565 | unsigned char shwh; | |
566 | struct file *hf, *file; | |
567 | struct super_block *sb; | |
568 | ||
569 | file = arg->file; | |
570 | sb = file->f_path.dentry->d_sb; | |
571 | SiMustAnyLock(sb); | |
572 | ||
573 | rdhash = au_sbi(sb)->si_rdhash; | |
574 | if (!rdhash) | |
575 | rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); | |
576 | err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); | |
577 | if (unlikely(err)) | |
578 | goto out; | |
579 | err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); | |
580 | if (unlikely(err)) | |
581 | goto out_delist; | |
582 | ||
583 | err = 0; | |
584 | arg->flags = 0; | |
585 | shwh = 0; | |
586 | if (au_opt_test(au_mntflags(sb), SHWH)) { | |
587 | shwh = 1; | |
588 | au_fset_fillvdir(arg->flags, SHWH); | |
589 | } | |
590 | btop = au_fbtop(file); | |
591 | bbot = au_fbbot_dir(file); | |
592 | for (bindex = btop; !err && bindex <= bbot; bindex++) { | |
593 | hf = au_hf_dir(file, bindex); | |
594 | if (!hf) | |
595 | continue; | |
596 | ||
597 | offset = vfsub_llseek(hf, 0, SEEK_SET); | |
598 | err = offset; | |
599 | if (unlikely(offset)) | |
600 | break; | |
601 | ||
602 | arg->bindex = bindex; | |
603 | au_fclr_fillvdir(arg->flags, WHABLE); | |
604 | if (shwh | |
605 | || (bindex != bbot | |
606 | && au_br_whable(au_sbr_perm(sb, bindex)))) | |
607 | au_fset_fillvdir(arg->flags, WHABLE); | |
608 | do { | |
609 | arg->err = 0; | |
610 | au_fclr_fillvdir(arg->flags, CALLED); | |
611 | /* smp_mb(); */ | |
612 | err = vfsub_iterate_dir(hf, &arg->ctx); | |
613 | if (err >= 0) | |
614 | err = arg->err; | |
615 | } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); | |
616 | ||
617 | /* | |
618 | * dir_relax() may be good for concurrency, but aufs should not | |
619 | * use it since it will cause a lockdep problem. | |
620 | */ | |
621 | } | |
622 | ||
623 | if (!err && shwh) | |
624 | err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); | |
625 | ||
626 | au_nhash_wh_free(&arg->whlist); | |
627 | ||
628 | out_delist: | |
629 | au_nhash_de_free(&arg->delist); | |
630 | out: | |
631 | return err; | |
632 | } | |
633 | ||
634 | static int read_vdir(struct file *file, int may_read) | |
635 | { | |
636 | int err; | |
637 | unsigned long expire; | |
638 | unsigned char do_read; | |
639 | struct fillvdir_arg arg = { | |
640 | .ctx = { | |
641 | .actor = fillvdir | |
642 | } | |
643 | }; | |
644 | struct inode *inode; | |
645 | struct au_vdir *vdir, *allocated; | |
646 | ||
647 | err = 0; | |
648 | inode = file_inode(file); | |
649 | IMustLock(inode); | |
650 | IiMustWriteLock(inode); | |
651 | SiMustAnyLock(inode->i_sb); | |
652 | ||
653 | allocated = NULL; | |
654 | do_read = 0; | |
655 | expire = au_sbi(inode->i_sb)->si_rdcache; | |
656 | vdir = au_ivdir(inode); | |
657 | if (!vdir) { | |
658 | do_read = 1; | |
659 | vdir = alloc_vdir(file); | |
660 | err = PTR_ERR(vdir); | |
661 | if (IS_ERR(vdir)) | |
662 | goto out; | |
663 | err = 0; | |
664 | allocated = vdir; | |
665 | } else if (may_read | |
666 | && (inode->i_version != vdir->vd_version | |
667 | || time_after(jiffies, vdir->vd_jiffy + expire))) { | |
668 | do_read = 1; | |
669 | err = reinit_vdir(vdir); | |
670 | if (unlikely(err)) | |
671 | goto out; | |
672 | } | |
673 | ||
674 | if (!do_read) | |
675 | return 0; /* success */ | |
676 | ||
677 | arg.file = file; | |
678 | arg.vdir = vdir; | |
679 | err = au_do_read_vdir(&arg); | |
680 | if (!err) { | |
681 | /* file->f_pos = 0; */ /* todo: ctx->pos? */ | |
682 | vdir->vd_version = inode->i_version; | |
683 | vdir->vd_last.ul = 0; | |
684 | vdir->vd_last.p.deblk = vdir->vd_deblk[0]; | |
685 | if (allocated) | |
686 | au_set_ivdir(inode, allocated); | |
687 | } else if (allocated) | |
688 | au_vdir_free(allocated, /*atonce*/0); | |
689 | ||
690 | out: | |
691 | return err; | |
692 | } | |
693 | ||
694 | static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) | |
695 | { | |
696 | int err, rerr; | |
697 | unsigned long ul, n; | |
698 | const unsigned int deblk_sz = src->vd_deblk_sz; | |
699 | ||
700 | AuDebugOn(tgt->vd_nblk != 1); | |
701 | ||
702 | err = -ENOMEM; | |
703 | if (tgt->vd_nblk < src->vd_nblk) { | |
704 | unsigned char **p; | |
705 | ||
706 | p = au_krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, | |
707 | GFP_NOFS, /*may_shrink*/0); | |
708 | if (unlikely(!p)) | |
709 | goto out; | |
710 | tgt->vd_deblk = p; | |
711 | } | |
712 | ||
713 | if (tgt->vd_deblk_sz != deblk_sz) { | |
714 | unsigned char *p; | |
715 | ||
716 | tgt->vd_deblk_sz = deblk_sz; | |
717 | p = au_krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS, | |
718 | /*may_shrink*/1); | |
719 | if (unlikely(!p)) | |
720 | goto out; | |
721 | tgt->vd_deblk[0] = p; | |
722 | } | |
723 | memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); | |
724 | tgt->vd_version = src->vd_version; | |
725 | tgt->vd_jiffy = src->vd_jiffy; | |
726 | ||
727 | n = src->vd_nblk; | |
728 | for (ul = 1; ul < n; ul++) { | |
729 | tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, | |
730 | GFP_NOFS); | |
731 | if (unlikely(!tgt->vd_deblk[ul])) | |
732 | goto out; | |
733 | tgt->vd_nblk++; | |
734 | } | |
735 | tgt->vd_nblk = n; | |
736 | tgt->vd_last.ul = tgt->vd_last.ul; | |
737 | tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; | |
738 | tgt->vd_last.p.deblk += src->vd_last.p.deblk | |
739 | - src->vd_deblk[src->vd_last.ul]; | |
740 | /* smp_mb(); */ | |
741 | return 0; /* success */ | |
742 | ||
743 | out: | |
744 | rerr = reinit_vdir(tgt); | |
745 | BUG_ON(rerr); | |
746 | return err; | |
747 | } | |
748 | ||
749 | int au_vdir_init(struct file *file) | |
750 | { | |
751 | int err; | |
752 | struct inode *inode; | |
753 | struct au_vdir *vdir_cache, *allocated; | |
754 | ||
755 | /* test file->f_pos here instead of ctx->pos */ | |
756 | err = read_vdir(file, !file->f_pos); | |
757 | if (unlikely(err)) | |
758 | goto out; | |
759 | ||
760 | allocated = NULL; | |
761 | vdir_cache = au_fvdir_cache(file); | |
762 | if (!vdir_cache) { | |
763 | vdir_cache = alloc_vdir(file); | |
764 | err = PTR_ERR(vdir_cache); | |
765 | if (IS_ERR(vdir_cache)) | |
766 | goto out; | |
767 | allocated = vdir_cache; | |
768 | } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { | |
769 | /* test file->f_pos here instead of ctx->pos */ | |
770 | err = reinit_vdir(vdir_cache); | |
771 | if (unlikely(err)) | |
772 | goto out; | |
773 | } else | |
774 | return 0; /* success */ | |
775 | ||
776 | inode = file_inode(file); | |
777 | err = copy_vdir(vdir_cache, au_ivdir(inode)); | |
778 | if (!err) { | |
779 | file->f_version = inode->i_version; | |
780 | if (allocated) | |
781 | au_set_fvdir_cache(file, allocated); | |
782 | } else if (allocated) | |
783 | au_vdir_free(allocated, /*atonce*/0); | |
784 | ||
785 | out: | |
786 | return err; | |
787 | } | |
788 | ||
789 | static loff_t calc_offset(struct au_vdir *vdir) | |
790 | { | |
791 | loff_t offset; | |
792 | union au_vdir_deblk_p p; | |
793 | ||
794 | p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; | |
795 | offset = vdir->vd_last.p.deblk - p.deblk; | |
796 | offset += vdir->vd_deblk_sz * vdir->vd_last.ul; | |
797 | return offset; | |
798 | } | |
799 | ||
800 | /* returns true or false */ | |
801 | static int seek_vdir(struct file *file, struct dir_context *ctx) | |
802 | { | |
803 | int valid; | |
804 | unsigned int deblk_sz; | |
805 | unsigned long ul, n; | |
806 | loff_t offset; | |
807 | union au_vdir_deblk_p p, deblk_end; | |
808 | struct au_vdir *vdir_cache; | |
809 | ||
810 | valid = 1; | |
811 | vdir_cache = au_fvdir_cache(file); | |
812 | offset = calc_offset(vdir_cache); | |
813 | AuDbg("offset %lld\n", offset); | |
814 | if (ctx->pos == offset) | |
815 | goto out; | |
816 | ||
817 | vdir_cache->vd_last.ul = 0; | |
818 | vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; | |
819 | if (!ctx->pos) | |
820 | goto out; | |
821 | ||
822 | valid = 0; | |
823 | deblk_sz = vdir_cache->vd_deblk_sz; | |
824 | ul = div64_u64(ctx->pos, deblk_sz); | |
825 | AuDbg("ul %lu\n", ul); | |
826 | if (ul >= vdir_cache->vd_nblk) | |
827 | goto out; | |
828 | ||
829 | n = vdir_cache->vd_nblk; | |
830 | for (; ul < n; ul++) { | |
831 | p.deblk = vdir_cache->vd_deblk[ul]; | |
832 | deblk_end.deblk = p.deblk + deblk_sz; | |
833 | offset = ul; | |
834 | offset *= deblk_sz; | |
835 | while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { | |
836 | unsigned int l; | |
837 | ||
838 | l = calc_size(p.de->de_str.len); | |
839 | offset += l; | |
840 | p.deblk += l; | |
841 | } | |
842 | if (!is_deblk_end(&p, &deblk_end)) { | |
843 | valid = 1; | |
844 | vdir_cache->vd_last.ul = ul; | |
845 | vdir_cache->vd_last.p = p; | |
846 | break; | |
847 | } | |
848 | } | |
849 | ||
850 | out: | |
851 | /* smp_mb(); */ | |
852 | AuTraceErr(!valid); | |
853 | return valid; | |
854 | } | |
855 | ||
856 | int au_vdir_fill_de(struct file *file, struct dir_context *ctx) | |
857 | { | |
858 | unsigned int l, deblk_sz; | |
859 | union au_vdir_deblk_p deblk_end; | |
860 | struct au_vdir *vdir_cache; | |
861 | struct au_vdir_de *de; | |
862 | ||
863 | vdir_cache = au_fvdir_cache(file); | |
864 | if (!seek_vdir(file, ctx)) | |
865 | return 0; | |
866 | ||
867 | deblk_sz = vdir_cache->vd_deblk_sz; | |
868 | while (1) { | |
869 | deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; | |
870 | deblk_end.deblk += deblk_sz; | |
871 | while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { | |
872 | de = vdir_cache->vd_last.p.de; | |
873 | AuDbg("%.*s, off%lld, i%lu, dt%d\n", | |
874 | de->de_str.len, de->de_str.name, ctx->pos, | |
875 | (unsigned long)de->de_ino, de->de_type); | |
876 | if (unlikely(!dir_emit(ctx, de->de_str.name, | |
877 | de->de_str.len, de->de_ino, | |
878 | de->de_type))) { | |
879 | /* todo: ignore the error caused by udba? */ | |
880 | /* return err; */ | |
881 | return 0; | |
882 | } | |
883 | ||
884 | l = calc_size(de->de_str.len); | |
885 | vdir_cache->vd_last.p.deblk += l; | |
886 | ctx->pos += l; | |
887 | } | |
888 | if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { | |
889 | vdir_cache->vd_last.ul++; | |
890 | vdir_cache->vd_last.p.deblk | |
891 | = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; | |
892 | ctx->pos = deblk_sz * vdir_cache->vd_last.ul; | |
893 | continue; | |
894 | } | |
895 | break; | |
896 | } | |
897 | ||
898 | /* smp_mb(); */ | |
899 | return 0; | |
900 | } |