~ubuntu-branches/ubuntu/quantal/linux-lowlatency/quantal

« back to all changes in this revision

Viewing changes to ubuntu/aufs/vdir.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-04kado7d1u2er2rl
Tags: 3.2.0-16.25
Add new lowlatency kernel flavour

Show diffs side-by-side

added added

removed removed

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