5871d07d8f6822b79c479c3f30e0dfc305487eeb
[lunaix-os.git] / lunaix-os / kernel / fs / ext2 / inode.c
1 #include <lunaix/fs/api.h>
2 #include <lunaix/mm/valloc.h>
3
4 #include <klibc/string.h>
5
6 #include "ext2.h"
7
8 static struct v_inode_ops ext2_inode_ops = {
9     .dir_lookup = ext2dr_lookup,
10     .open  = ext2_open_inode,
11     .mkdir = ext2_mkdir,
12     .rmdir = ext2_rmdir,
13     .read_symlink = ext2_get_symlink,
14     .set_symlink  = ext2_set_symlink,
15     .rename = ext2_rename,
16     .link = ext2_link,
17     .unlink = ext2_unlink,
18     .create = ext2_create,
19     .sync = ext2_sync_inode
20 };
21
22 static struct v_file_ops ext2_file_ops = {
23     .close = ext2_close_inode,
24     
25     .read = ext2_inode_read,
26     .read_page = ext2_inode_read_page,
27     
28     .write = ext2_inode_write,
29     .write_page = ext2_inode_write_page,
30     
31     .readdir = ext2dr_read,
32     .seek = ext2_seek_inode,
33     .sync = ext2_file_sync
34 };
35
36 #define to_tag(e_ino, val)        \
37         (((val) >> (e_ino)->inds_lgents) | (1 << msbiti))
38 #define valid_tag(tag)      ((tag) & (1 << msbiti))
39
40 static void
41 __btlb_insert(struct ext2_inode* e_inode, unsigned int blkid, bbuf_t buf)
42 {
43     struct ext2_btlb* btlb;
44     struct ext2_btlb_entry* btlbe = NULL;
45     unsigned int cap_sel;
46  
47     if (unlikely(!blkid)) {
48         return;
49     }
50
51     btlb = e_inode->btlb;
52
53     for (int i = 0; i < BTLB_SETS; i++)
54     {
55         if (valid_tag(btlb->buffer[i].tag)) {
56             continue;
57         }
58
59         btlbe = &btlb->buffer[i];
60         goto found;
61     }
62
63     /*
64         we have triggered the capacity miss.
65         since most file operation is heavily linear and strong
66         locality, we place our bet on it and avoid go through
67         the whole overhead of LRU eviction stuff. Just a trival
68         random eviction will do the fine job
69     */
70     cap_sel = hash_32(blkid, ilog2(BTLB_SETS));
71     btlbe = &btlb->buffer[cap_sel];
72
73     fsblock_put(btlbe->block);
74
75 found:
76     btlbe->tag = to_tag(e_inode, blkid);
77     btlbe->block = fsblock_take(buf);
78 }
79
80 static bbuf_t
81 __btlb_hit(struct ext2_inode* e_inode, unsigned int blkid)
82 {
83     struct ext2_btlb* btlb;
84     struct ext2_btlb_entry* btlbe = NULL;
85     unsigned int in_tag, ref_cnts;
86
87     btlb = e_inode->btlb;
88     in_tag = to_tag(e_inode, blkid);
89
90     for (int i = 0; i < BTLB_SETS; i++)
91     {
92         btlbe = &btlb->buffer[i];
93
94         if (btlbe->tag != in_tag) {
95             continue;
96         }
97         
98         ref_cnts = blkbuf_refcounts(btlbe->block);
99         if (!ref_cnts) {
100             btlbe->tag = 0;
101             btlbe->block = bbuf_null;
102             break;
103         }
104
105         return fsblock_take(btlbe->block);
106     }
107
108     return NULL;
109 }
110
111 static void
112 __btlb_flushall(struct ext2_inode* e_inode)
113 {
114     struct ext2_btlb* btlb;
115     struct ext2_btlb_entry* btlbe = NULL;
116
117     btlb = e_inode->btlb;
118
119     for (int i = 0; i < BTLB_SETS; i++)
120     {
121         btlbe = &btlb->buffer[i];
122         if (!valid_tag(btlbe->tag)) {
123             continue;
124         }
125
126         btlbe->tag = 0;
127         fsblock_put(btlbe->block);
128     }
129 }
130
131 void
132 ext2db_itbegin(struct ext2_iterator* iter, struct v_inode* inode)
133 {
134     struct ext2_inode* e_ino;
135
136     e_ino = EXT2_INO(inode);
137     *iter = (struct ext2_iterator){
138         .pos = 0,
139         .inode = inode,
140         .blksz = inode->sb->blksize,
141         .end_pos = ICEIL(e_ino->isize, inode->sb->blksize)
142     };
143 }
144
145 void
146 ext2db_itreset(struct ext2_iterator* iter)
147 {
148     if (likely(iter->sel_buf)) {
149         fsblock_put(iter->sel_buf);
150         iter->sel_buf = NULL;
151     }
152
153     iter->pos = 0;
154 }
155
156 int
157 ext2db_itffw(struct ext2_iterator* iter, int count)
158 {
159     iter->pos += count;
160     return count;
161 }
162
163 void
164 ext2db_itend(struct ext2_iterator* iter)
165 {
166     if (likely(iter->sel_buf)) {
167         fsblock_put(iter->sel_buf);
168         iter->sel_buf = NULL;
169     }
170 }
171
172 bool
173 ext2db_itnext(struct ext2_iterator* iter)
174 {
175     bbuf_t buf;
176
177     if (unlikely(iter->has_error)) {
178         return false;
179     }
180
181     if (unlikely(iter->pos > iter->end_pos)) {
182         return false;
183     }
184
185     if (likely(iter->sel_buf)) {
186         fsblock_put(iter->sel_buf);
187     }
188
189     buf = ext2db_get(iter->inode, iter->pos);
190     iter->sel_buf = buf;
191
192     if (!buf || !ext2_itcheckbuf(iter)) {
193         return false;
194     }
195
196     iter->pos++;
197     iter->data = blkbuf_data(buf);
198
199     return true;
200 }
201
202 void
203 ext2ino_init(struct v_superblock* vsb, struct v_inode* inode)
204 {
205     // Placeholder, to make vsb happy
206 }
207
208 static void
209 __destruct_ext2_inode(struct ext2_inode* e_inode)
210 {
211     __btlb_flushall(e_inode);
212
213     fsblock_put(e_inode->ind_ord1);
214     fsblock_put(e_inode->buf);
215
216     ext2gd_put(e_inode->blk_grp);
217
218     vfree_safe(e_inode->symlink);
219     vfree(e_inode->btlb);
220     vfree(e_inode);
221 }
222
223 static void
224 ext2_destruct_inode(struct v_inode* inode)
225 {
226     struct ext2_inode* e_inode;
227
228     e_inode = EXT2_INO(inode);
229
230     assert(e_inode);
231     __destruct_ext2_inode(e_inode);
232 }
233
234 static inline void
235 __ext2ino_fill_common(struct v_inode* inode, ino_t ino_id)
236 {
237     fsapi_inode_setid(inode, ino_id, ino_id);
238     fsapi_inode_setfops(inode, &ext2_file_ops);
239     fsapi_inode_setops(inode, &ext2_inode_ops);
240     fsapi_inode_setdector(inode, ext2_destruct_inode);
241 }
242
243
244 static unsigned int
245 __translate_vfs_itype(unsigned int v_itype)
246 {
247     unsigned int e_itype = IMODE_IFREG;
248
249     if (v_itype == VFS_IFFILE) {
250         e_itype = IMODE_IFREG;
251     }
252     else if (check_itype(v_itype, VFS_IFDIR)) {
253         e_itype = IMODE_IFDIR;
254         e_itype |= IMODE_UEX;
255     }
256     else if (check_itype(v_itype, VFS_IFSEQDEV)) {
257         e_itype = IMODE_IFCHR;
258     }
259     else if (check_itype(v_itype, VFS_IFVOLDEV)) {
260         e_itype = IMODE_IFBLK;
261     }
262     
263     if (check_itype(v_itype, VFS_IFSYMLINK)) {
264         e_itype |= IMODE_IFLNK;
265     }
266
267     // FIXME we keep this until we have our own user manager
268     e_itype |= (IMODE_URD | IMODE_GRD | IMODE_ORD);
269     return e_itype;
270 }
271
272 int
273 ext2ino_fill(struct v_inode* inode, ino_t ino_id)
274 {
275     struct ext2_sbinfo* sb;
276     struct ext2_inode* e_ino;
277     struct v_superblock* vsb;
278     struct ext2b_inode* b_ino;
279     unsigned int type = VFS_IFFILE;
280     int errno = 0;
281
282     vsb = inode->sb;
283     sb = EXT2_SB(vsb);
284
285     if ((errno = ext2ino_get(vsb, ino_id, &e_ino))) {
286         return errno;
287     }
288     
289     b_ino = e_ino->ino;
290     ino_id = e_ino->ino_id;
291
292     fsapi_inode_setsize(inode, e_ino->isize);
293     
294     fsapi_inode_settime(inode, b_ino->i_ctime, 
295                                b_ino->i_mtime, 
296                                b_ino->i_atime);
297     
298     fsapi_inode_setaccess(inode, b_ino->i_mode & IMODE_ACL_MASK);
299     fsapi_inode_setowner(inode, b_ino->i_uid,
300                                 b_ino->i_gid);
301     
302     __ext2ino_fill_common(inode, ino_id);
303
304     if (check_itype(b_ino->i_mode, IMODE_IFLNK)) {
305         type = VFS_IFSYMLINK;
306     }
307     else if (check_itype(b_ino->i_mode, IMODE_IFDIR)) {
308         type = VFS_IFDIR;
309     }
310     else if (check_itype(b_ino->i_mode, IMODE_IFCHR)) {
311         type = VFS_IFSEQDEV;
312     }
313     else if (check_itype(b_ino->i_mode, IMODE_IFBLK)) {
314         type = VFS_IFVOLDEV;
315     }
316
317     fsapi_inode_settype(inode, type);
318
319     fsapi_inode_complete(inode, e_ino);
320
321     return 0;
322 }
323
324 static int
325 __get_group_desc(struct v_superblock* vsb, int ino, 
326                  struct ext2_gdesc** gd_out)
327 {
328     unsigned int blkgrp_id;
329     struct ext2_sbinfo* sb;
330     
331     sb = EXT2_SB(vsb);
332
333     blkgrp_id = to_fsblock_id(ino) / sb->raw->s_ino_per_grp;
334     return ext2gd_take(vsb, blkgrp_id, gd_out);
335 }
336
337 static struct ext2b_inode*
338 __get_raw_inode(struct v_superblock* vsb, struct ext2_gdesc* gd, 
339                 bbuf_t* buf_out, int ino_index)
340 {
341     bbuf_t ino_tab;
342     struct ext2_sbinfo* sb;
343     struct ext2b_inode* b_inode;
344     unsigned int ino_tab_sel, ino_tab_off, tab_partlen;
345
346     assert(buf_out);
347
348     sb = gd->sb;
349     tab_partlen = sb->block_size / sb->raw->s_ino_size;
350     ino_tab_sel = ino_index / tab_partlen;
351     ino_tab_off = ino_index % tab_partlen;
352
353     ino_tab = fsblock_get(vsb, gd->info->bg_ino_tab + ino_tab_sel);
354     if (blkbuf_errbuf(ino_tab)) {
355         return NULL;
356     }
357
358     b_inode = (struct ext2b_inode*)blkbuf_data(ino_tab);
359     b_inode = &b_inode[ino_tab_off];
360     
361     *buf_out = ino_tab;
362     
363     return b_inode;
364 }
365
366 static struct ext2_inode*
367 __create_inode(struct v_superblock* vsb, struct ext2_gdesc* gd, int ino_index)
368 {
369     bbuf_t ino_tab;
370     struct ext2_sbinfo* sb;
371     struct ext2b_inode* b_inode;
372     struct ext2_inode* inode;
373     unsigned int ind_ents;
374     size_t inds_blks;
375
376     sb = gd->sb;
377     b_inode = __get_raw_inode(vsb, gd, &ino_tab, ino_index);
378     if (!b_inode) {
379         return NULL;
380     }
381     
382     inode            = vzalloc(sizeof(*inode));
383     inode->btlb      = vzalloc(sizeof(struct ext2_btlb));
384     inode->buf       = ino_tab;
385     inode->ino       = b_inode;
386     inode->blk_grp   = gd;
387     inode->isize     = b_inode->i_size;
388
389     if (ext2_feature(vsb, FEAT_LARGE_FILE)) {
390         inode->isize |= (size_t)((u64_t)(b_inode->i_size_h32) << 32);
391     }
392
393     if (b_inode->i_blocks) {
394         inds_blks  = (size_t)b_inode->i_blocks;
395         inds_blks -= ICEIL(inode->isize, 512);
396         inds_blks /= (sb->block_size / 512);
397
398         inode->indirect_blocks = inds_blks;
399     }
400
401     ind_ents = sb->block_size / sizeof(int);
402     assert(is_pot(ind_ents));
403
404     inode->inds_lgents = ilog2(ind_ents);
405     inode->ino_id = gd->ino_base + to_ext2ino_id(ino_index);
406
407     return inode;
408 }
409
410 int
411 ext2ino_get_fast(struct v_superblock* vsb, 
412                  unsigned int ino, struct ext2_fast_inode* fast_ino)
413 {
414     int errno;
415     bbuf_t ino_tab;
416     struct ext2_gdesc* gd;
417     struct ext2_sbinfo* sb;
418     struct ext2b_inode* b_inode;
419     unsigned int ino_rel_id;
420
421     sb = EXT2_SB(vsb);
422     errno = __get_group_desc(vsb, ino, &gd);
423     if (errno) {
424         return errno;
425     }
426
427     ino_rel_id  = to_fsblock_id(ino) % sb->raw->s_ino_per_grp;
428     b_inode = __get_raw_inode(vsb, gd, &ino_tab, ino_rel_id);
429
430     fast_ino->buf = ino_tab;
431     fast_ino->ino = b_inode;
432
433     return 0;
434 }
435
436 int
437 ext2ino_get(struct v_superblock* vsb, 
438             unsigned int ino, struct ext2_inode** out)
439 {
440     struct ext2_sbinfo* sb;
441     struct ext2_inode* inode;
442     struct ext2_gdesc* gd;
443     struct ext2b_inode* b_inode;
444     unsigned int ino_rel_id;
445     unsigned int tab_partlen;
446     unsigned int ind_ents, prima_ind;
447     int errno = 0;
448     
449     sb = EXT2_SB(vsb);
450
451     if ((errno = __get_group_desc(vsb, ino, &gd))) {
452         return errno;
453     }
454
455     ino_rel_id  = to_fsblock_id(ino) % sb->raw->s_ino_per_grp;
456     inode = __create_inode(vsb, gd, ino_rel_id);
457     if (!inode) {
458         return EIO;
459     }
460     
461     b_inode = inode->ino;
462     prima_ind = b_inode->i_block.ind1;
463     *out = inode;
464
465     if (!prima_ind) {
466         return errno;
467     }
468
469     inode->ind_ord1 = fsblock_get(vsb, prima_ind);
470     if (blkbuf_errbuf(inode->ind_ord1)) {
471         vfree(inode->btlb);
472         vfree(inode);
473         *out = NULL;
474         return EIO;
475     }
476
477     return errno;
478 }
479
480 int
481 ext2ino_alloc(struct v_superblock* vsb, 
482                  struct ext2_inode* hint, struct ext2_inode** out)
483 {
484     int free_ino_idx;
485     struct ext2_gdesc* gd;
486     struct ext2_inode* inode;
487
488     free_ino_idx = ALLOC_FAIL;
489     if (hint) {
490         gd = hint->blk_grp;
491         free_ino_idx = ext2gd_alloc_inode(gd);
492     }
493
494     // locality hinted alloc failed, try entire fs
495     if (!valid_bmp_slot(free_ino_idx)) {
496         free_ino_idx = ext2ino_alloc_slot(vsb, &gd);
497     }
498
499     if (!valid_bmp_slot(free_ino_idx)) {
500         return EDQUOT;
501     }
502
503     inode = __create_inode(vsb, gd, free_ino_idx);
504     if (!inode) {
505         // what a shame!
506         ext2gd_free_inode(gd, free_ino_idx);
507         return EIO;
508     }
509
510     memset(inode->ino, 0, sizeof(*inode->ino));
511     fsblock_dirty(inode->buf);
512
513     *out = inode;
514     return 0;
515 }
516
517 static inline int
518 __free_block_at(struct v_superblock *vsb, unsigned int block_pos)
519 {
520     int errno, gd_index;
521     struct ext2_gdesc* gd;
522     struct ext2_sbinfo * sb;
523
524     if (!block_pos) {
525         return 0;
526     }
527
528     block_pos = ext2_datablock(vsb, block_pos);
529
530     sb = EXT2_SB(vsb);
531     gd_index = block_pos / sb->raw->s_blk_per_grp;
532
533     if ((errno = ext2gd_take(vsb, gd_index, &gd))) {
534         return errno;
535     }
536
537     assert(block_pos >= gd->base);
538     ext2gd_free_block(gd, block_pos - gd->base);
539
540     ext2gd_put(gd);
541     return 0;
542 }
543
544 static int
545 __free_recurisve_from(struct v_superblock *vsb, struct ext2_inode* inode,
546                       struct walk_stack* stack, int depth)
547 {
548     bbuf_t tab;
549     int idx, len, errno;
550     u32_t* db_tab;
551
552     int ind_entries = 1 << inode->inds_lgents;
553     int max_len[] = { 15, ind_entries, ind_entries, ind_entries }; 
554
555     u32_t* tables  = stack->tables;
556     u32_t* indices = stack->indices;
557
558     if (depth > MAX_INDS_DEPTH || !tables[depth]) {
559         return 0;
560     }
561
562     idx = indices[depth];
563     len = max_len[depth];
564     tab = fsblock_get(vsb, ext2_datablock(vsb, tables[depth]));
565
566     if (blkbuf_errbuf(tab)) {
567         return EIO;
568     }
569
570     db_tab = blkbuf_data(tab);
571     if (depth == 0) {
572         int offset = offsetof(struct ext2b_inode, i_block_arr);
573         db_tab = offset(db_tab, offset);
574     }
575     
576     errno = 0;
577     indices[depth] = 0;
578
579     for (; idx < len; idx++)
580     {
581         u32_t db_id = db_tab[idx];
582
583         if (!db_id) {
584             continue;
585         }
586
587         if (depth >= MAX_INDS_DEPTH) {
588             goto cont;
589         }
590
591         tables[depth] = db_id;
592         errno = __free_recurisve_from(vsb, inode, stack, depth + 1);
593         if (errno) {
594             break;
595         }
596
597 cont:
598         __free_block_at(vsb, db_id);
599         db_tab[idx] = 0;
600     }
601
602     fsblock_dirty(tab);
603     fsblock_put(tab);
604     return errno;
605 }
606
607 int
608 ext2ino_free(struct v_inode* inode)
609 {
610     int errno = 0;
611     unsigned int ino_slot;
612     struct ext2_inode*  e_ino;
613     struct ext2_gdesc*  e_gd;
614     struct ext2b_inode* b_ino;
615     struct ext2_sbinfo* sb;
616
617     sb    = EXT2_SB(inode->sb);
618     e_ino = EXT2_INO(inode);
619     b_ino = e_ino->ino;
620     e_gd  = e_ino->blk_grp;
621
622     assert_fs(b_ino->i_lnk_cnt > 0);
623     fsblock_dirty(e_ino->buf);
624
625     b_ino->i_lnk_cnt--;
626     if (b_ino->i_lnk_cnt >= 1) {
627         return 0;
628     }
629
630     ext2ino_resizing(inode, 0);
631
632     ino_slot = e_ino->ino_id;
633     ino_slot = to_fsblock_id(ino_slot - e_gd->base);
634     ext2gd_free_inode(e_ino->blk_grp, ino_slot);
635
636     __destruct_ext2_inode(e_ino);
637
638     inode->data = NULL;
639
640     return errno;
641 }
642
643 static void
644 __update_inode_access_metadata(struct ext2b_inode* b_ino, 
645                         struct v_inode* inode)
646 {
647     b_ino->i_ctime = inode->ctime;
648     b_ino->i_atime = inode->atime;
649     b_ino->i_mtime = inode->mtime;
650 }
651
652 static inline void
653 __update_inode_size(struct v_inode* inode, size_t size)
654 {
655     struct ext2b_inode* b_ino;
656     struct ext2_inode*  e_ino;
657
658     e_ino = EXT2_INO(inode);
659     b_ino = e_ino->ino;
660
661     e_ino->isize = size;
662     
663     if (ext2_feature(inode->sb, FEAT_LARGE_FILE)) {
664         b_ino->i_size_l32 = (unsigned int)size;
665         b_ino->i_size_h32 = (unsigned int)((u64_t)size >> 32);
666     }
667     else {
668         b_ino->i_size  = size;
669     }
670
671     b_ino->i_blocks = ICEIL(size, 512);
672     b_ino->i_blocks += e_ino->indirect_blocks;
673 }
674
675 int
676 ext2ino_make(struct v_superblock* vsb, unsigned int itype, 
677              struct ext2_inode* hint, struct v_inode** out)
678 {
679     int errno = 0;
680     struct ext2_inode* e_ino;
681     struct ext2b_inode* b_ino;
682     struct v_inode* inode;
683
684     errno = ext2ino_alloc(vsb, hint, &e_ino);
685     if (errno) {
686         return errno;
687     }
688
689     b_ino = e_ino->ino;
690     inode = vfs_i_alloc(vsb);
691     
692     __ext2ino_fill_common(inode, e_ino->ino_id);
693
694     __update_inode_access_metadata(b_ino, inode);
695     b_ino->i_mode  = __translate_vfs_itype(itype);
696
697     fsapi_inode_settype(inode, itype);
698     fsapi_inode_complete(inode, e_ino);
699
700     *out = inode;
701     return errno;
702 }
703
704 int
705 ext2_create(struct v_inode* this, struct v_dnode* dnode, unsigned int itype)
706 {
707     int errno;
708     struct v_inode* created;
709     
710     errno = ext2ino_make(this->sb, itype, EXT2_INO(this), &created);
711     if (errno) {
712         return errno;
713     }
714
715     return ext2_link(created, dnode);
716 }
717
718 int
719 ext2_link(struct v_inode* this, struct v_dnode* new_name)
720 {
721     int errno = 0;
722     struct v_inode* parent;
723     struct ext2_inode* e_ino;
724     struct ext2_dnode* e_dno;
725     struct ext2b_dirent dirent;
726     
727     e_ino  = EXT2_INO(this);
728     parent = fsapi_dnode_parent(new_name);
729
730     ext2dr_setup_dirent(&dirent, this, &new_name->name);
731     ext2ino_linkto(e_ino, &dirent);
732     
733     errno = ext2dr_insert(parent, &dirent, &e_dno);
734     if (errno) {
735         goto done;
736     }
737
738     new_name->data = e_dno;
739     vfs_assign_inode(new_name, this);
740
741 done:
742     return errno;
743 }
744
745 int
746 ext2_unlink(struct v_inode* this, struct v_dnode* name)
747 {
748     int errno = 0;
749     struct ext2_inode* e_ino;
750     struct ext2_dnode* e_dno;
751
752     e_ino = EXT2_INO(this);
753     e_dno = EXT2_DNO(name);
754
755     assert_fs(e_dno);
756     assert_fs(e_dno->self.dirent->inode == e_ino->ino_id);
757     
758     errno = ext2dr_remove(e_dno);
759     if (errno) {
760         return errno;
761     }
762
763     return ext2ino_free(this);
764 }
765
766 void
767 ext2ino_update(struct v_inode* inode)
768 {
769     struct ext2_inode* e_ino;
770     
771     e_ino = EXT2_INO(inode);
772     __update_inode_access_metadata(e_ino->ino, inode);
773
774     fsblock_dirty(e_ino->buf);
775 }
776
777 /* ******************* Data Blocks ******************* */
778
779 static inline void
780 __walkstate_set_stack(struct walk_state* state, int depth,
781                       bbuf_t tab, unsigned int index)
782 {
783     state->stack.tables[depth] = fsblock_id(tab);
784     state->stack.indices[depth] = index;
785 }
786
787 /**
788  * @brief Walk the indrection chain given the position of data block
789  *        relative to the inode. Upon completed, walk_state will be
790  *        populated with result. On error, walk_state is untouched.
791  * 
792  *        Note, the result will always be one above the stopping level. 
793  *        That means, if your pos is pointed directly to file-content block
794  *        (i.e., a leaf block), then the state is the indirect block that
795  *        containing the ID of that leaf block.
796  *        
797  *        If `resolve` is set, it will resolve any absence encountered
798  *        during the walk by allocating and chaining indirect block.
799  *        It require the file system is mounted writable.
800  * 
801  * @param inode     inode to walk
802  * @param pos       flattened data block position to be located
803  * @param state     contain the walk result
804  * @param resolve   whether to auto allocate the indirection structure during 
805  *                  walk if `pos` is not exist.
806  * @return int 
807  */
808 static int
809 __walk_indirects(struct v_inode* inode, unsigned int pos,
810                  struct walk_state* state, bool resolve, bool full_walk)
811 {
812     int errno;
813     int inds, stride, shifts, level;
814     unsigned int *slotref, index, next, mask;
815     struct ext2_inode* e_inode;
816     struct ext2b_inode* b_inode;
817     struct v_superblock* vsb;
818     bbuf_t table, next_table;
819
820     e_inode = EXT2_INO(inode);
821     b_inode = e_inode->ino;
822     vsb = inode->sb;
823     level = 0;
824     resolve = resolve && !EXT2_SB(vsb)->read_only;
825
826     if (pos < 12) {
827         index = pos;
828         slotref = &b_inode->i_block_arr[pos];
829         table = fsblock_take(e_inode->buf);
830         inds = 0;
831         goto _return;
832     }
833
834     pos -= 12;
835     stride = e_inode->inds_lgents;
836     if (!(pos >> stride)) {
837         inds = 1;
838     }
839     else if (!(pos >> (stride * 2))) {
840         inds = 2;
841     }
842     else if (!(pos >> (stride * 3))) {
843         inds = 3;
844     }
845     else {
846         fail("unrealistic block pos");
847     }
848
849     // bTLB cache the last level indirect block
850     if (!full_walk && (table = __btlb_hit(e_inode, pos))) {
851         level = inds;
852         index = pos & ((1 << stride) - 1);
853         slotref = &block_buffer(table, u32_t)[index];
854         goto _return;
855     }
856
857     shifts = stride * (inds - 1);
858     mask = ((1 << stride) - 1) << shifts;
859
860     index   = 12 + inds - 1;
861     slotref = &b_inode->i_block.inds[inds - 1];
862     table   = fsblock_take(e_inode->buf);
863
864     for (; level < inds; level++)
865     {
866         __walkstate_set_stack(state, level, table, index);
867
868         next = *slotref;
869         if (!next) {
870             if (!resolve) {
871                 goto _return;
872             }
873
874             if ((errno = ext2db_alloc(inode, &next_table))) {
875                 fsblock_put(table);
876                 return errno;
877             }
878
879             e_inode->indirect_blocks++;
880             *slotref = fsblock_id(next_table);
881             fsblock_dirty(table);
882         }
883         else {
884             next_table = fsblock_get(vsb, next);
885         }
886
887         fsblock_put(table);
888         table = next_table;
889
890         if (blkbuf_errbuf(table)) {
891             return EIO;
892         }
893
894         assert(shifts >= 0);
895
896         index = (pos & mask) >> shifts;
897
898         slotref = &block_buffer(table, u32_t)[index];
899
900         shifts -= stride;
901         mask  = mask >> stride;
902     }
903
904     __btlb_insert(e_inode, pos, table);
905
906 _return:
907     assert(blkbuf_refcounts(table) >= 1);
908     assert_fs(table);
909     assert_fs(slotref);
910
911     state->slot_ref = slotref;
912     state->table = table;
913     state->level = level;
914     state->indirections = inds;
915
916     __walkstate_set_stack(state, level, table, index);
917
918     return 0;
919 }
920
921 bbuf_t
922 ext2db_get(struct v_inode* inode, unsigned int data_pos)
923 {
924     int errno;
925     unsigned int blkid;
926     struct walk_state state;
927
928     ext2walk_init_state(&state);
929
930     errno = __walk_indirects(inode, data_pos, &state, false, false);
931     if (errno) {
932         return (bbuf_t)INVL_BUFFER;
933     }
934
935     blkid = *state.slot_ref;
936     
937     ext2walk_free_state(&state);
938     
939     if (!blkid) {
940         return NULL;
941     }
942
943     return fsblock_get(inode->sb, blkid);
944 }
945
946 int
947 ext2db_acquire(struct v_inode* inode, unsigned int data_pos, bbuf_t* out)
948 {
949     int errno = 0;
950     bbuf_t buf;
951     unsigned int block_id;
952     struct walk_state state;
953
954     ext2walk_init_state(&state);
955
956     errno = __walk_indirects(inode, data_pos, &state, true, false);
957     if (errno) {
958         return errno;
959     }
960
961     block_id = *state.slot_ref;
962     if (block_id) {
963         buf = fsblock_get(inode->sb, block_id);
964         goto done;
965     }
966
967     errno = ext2db_alloc(inode, &buf);
968     if (errno) {
969         ext2walk_free_state(&state);
970         return errno;
971     }
972
973     *state.slot_ref = fsblock_id(buf);
974     fsblock_dirty(state.table);
975
976 done:
977     ext2walk_free_state(&state);
978
979     if (blkbuf_errbuf(buf)) {
980         return EIO;
981     }
982
983     *out = buf;
984     return 0;
985 }
986
987 int
988 ext2db_alloc(struct v_inode* inode, bbuf_t* out)
989 {
990     int free_ino_idx;
991     struct ext2_gdesc* gd;
992     struct ext2_inode* e_inode;
993     struct v_superblock* vsb;
994
995     free_ino_idx = ALLOC_FAIL;
996     e_inode = EXT2_INO(inode);
997     vsb = inode->sb;
998
999     gd = e_inode->blk_grp;
1000     free_ino_idx = ext2gd_alloc_block(gd);
1001
1002     // locality alloc failed, try entire fs
1003     if (!valid_bmp_slot(free_ino_idx)) {
1004         free_ino_idx = ext2db_alloc_slot(vsb, &gd);
1005     }
1006
1007     if (!valid_bmp_slot(free_ino_idx)) {
1008         return EDQUOT;
1009     }
1010
1011     free_ino_idx += gd->base;
1012     free_ino_idx = ext2_datablock(vsb, free_ino_idx);
1013     free_ino_idx = to_ext2ino_id(free_ino_idx);
1014     
1015     bbuf_t buf = fsblock_get(vsb, free_ino_idx);
1016     if (blkbuf_errbuf(buf)) {
1017         return EIO;
1018     }
1019
1020     *out = buf;
1021     return 0;
1022 }
1023
1024 void
1025 ext2db_free_pos(struct v_inode* inode, unsigned int block_pos)
1026 {
1027     struct ext2_inode* e_inode;
1028     struct ext2_gdesc* gd;
1029
1030     e_inode = EXT2_INO(inode);
1031     gd = e_inode->blk_grp;
1032
1033     assert(block_pos >= gd->base);
1034     
1035     block_pos -= gd->base;
1036
1037     ext2gd_free_block(gd, block_pos);
1038 }
1039
1040 int
1041 ext2db_free(struct v_inode* inode, bbuf_t buf)
1042 {
1043     assert(blkbuf_not_shared(buf));
1044
1045     ext2db_free_pos(inode, blkbuf_id(buf));
1046     fsblock_put(buf);
1047
1048     return 0;
1049 }
1050
1051 int
1052 ext2ino_resizing(struct v_inode* inode, size_t new_size)
1053 {
1054     int errno;
1055     unsigned int pos;
1056     size_t oldsize;
1057     struct walk_state state;
1058     struct ext2_inode*  e_ino;
1059     struct ext2b_inode* b_ino;
1060
1061     e_ino = EXT2_INO(inode);
1062     b_ino = e_ino->ino;
1063     oldsize = e_ino->isize;
1064
1065     if (oldsize == new_size) {
1066         return 0;
1067     }
1068
1069     __update_inode_size(inode, new_size);
1070     fsblock_dirty(e_ino->buf);
1071
1072     if (check_symlink_node(inode)) {
1073         return 0;
1074     }
1075
1076     if (oldsize < new_size) {
1077         return 0;
1078     }
1079
1080     ext2walk_init_state(&state);
1081
1082     pos   = new_size / fsapi_block_size(inode->sb);
1083     errno = __walk_indirects(inode, pos, &state, false, true);
1084     if (errno) {
1085         return errno;
1086     }
1087
1088     errno = __free_recurisve_from(inode->sb, e_ino, &state.stack, 0);
1089
1090     ext2walk_free_state(&state);
1091     return errno;
1092 }