]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - fs/aufs/vdir.c
x86/mm: Add TLB purge to free pmd/pte page interfaces
[mirror_ubuntu-bionic-kernel.git] / fs / aufs / vdir.c
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 }