]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - fs/aufs/vdir.c
UBUNTU: SAUCE: Revert "audit: fix auditd/kernel connection state tracking"
[mirror_ubuntu-zesty-kernel.git] / fs / aufs / vdir.c
CommitLineData
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
24static unsigned int calc_size(int nlen)
25{
26 return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t));
27}
28
29static 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 */
41static 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
49static 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 */
57unsigned 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 */
75int 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
94static 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
107static 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
116static 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
125static 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
143void au_nhash_wh_free(struct au_nhash *whlist)
144{
145 au_nhash_do_free(whlist, au_nhash_wh_do_free);
146}
147
148static 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
155int 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
173static 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
192static 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 */
199int 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 */
216static 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
232static 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
243int 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
269out:
270 return err;
271}
272
273static 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
298out:
299 return err;
300}
301
302static 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
347out:
348 return err;
349}
350
351/* ---------------------------------------------------------------------- */
352
353void 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
372static 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
405out_free:
406 au_cache_dfree_vdir(vdir);
407out:
408 vdir = ERR_PTR(err);
409 return vdir;
410}
411
412static 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
450struct 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
461static 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
506out:
507 if (!arg->err)
508 arg->vdir->vd_jiffy = jiffies;
509 /* smp_mb(); */
510 AuTraceErr(arg->err);
511 return arg->err;
512}
513
514static 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
551out:
552 AuTraceErr(err);
553 return err;
554#else
555 return 0;
556#endif
557}
558
559static 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
628out_delist:
629 au_nhash_de_free(&arg->delist);
630out:
631 return err;
632}
633
634static 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
690out:
691 return err;
692}
693
694static 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
743out:
744 rerr = reinit_vdir(tgt);
745 BUG_ON(rerr);
746 return err;
747}
748
749int 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
785out:
786 return err;
787}
788
789static 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 */
801static 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
850out:
851 /* smp_mb(); */
852 AuTraceErr(!valid);
853 return valid;
854}
855
856int 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}