1 #include <lunaix/mm/valloc.h>
2 #include <lunaix/spike.h>
3 #include <klibc/string.h>
8 aligned_reclen(struct ext2b_dirent* dirent)
10 return !(dirent->rec_len % 4);
14 __find_dirent_byname(struct v_inode* inode, struct hstr* name,
15 struct ext2_dnode* e_dnode_out)
18 struct ext2_iterator iter;
19 struct ext2b_dirent *dir = NULL, *prev = NULL;
20 bbuf_t prev_buf = NULL;
22 ext2dr_itbegin(&iter, inode);
24 while (ext2dr_itnext(&iter)) {
27 if (dir->name_len != name->len) {
31 if (strneq(dir->name, name->value, name->len)) {
38 fsblock_put(prev_buf);
40 prev_buf = fsblock_take(iter.sel_buf);
47 e_dnode_out->self = (struct ext2_dnode_sub) {
48 .buf = fsblock_take(iter.sel_buf),
52 e_dnode_out->prev = (struct ext2_dnode_sub) {
53 .buf = fsblock_take(prev_buf),
58 fsblock_put(prev_buf);
61 return itstate_sel(&iter, errno);
65 __dirent_realsize(struct ext2b_dirent* dirent)
67 return sizeof(*dirent) - sizeof(dirent->name) + dirent->name_len;
70 #define DIRENT_INSERT 0
71 #define DIRENT_APPEND 1
73 #define DIRENT_ALIGNMENT sizeof(int)
80 struct ext2_dnode result;
81 size_t new_prev_reclen;
86 static inline void must_inline
87 __init_locator(struct dirent_locator* loc, size_t search_size)
89 *loc = (struct dirent_locator) { .search_size = search_size };
93 __find_free_dirent_slot(struct v_inode* inode, struct dirent_locator* loc)
95 struct ext2_iterator dbit;
96 struct ext2b_dirent *dir = NULL;
97 struct ext2_dnode* result;
99 bbuf_t prev_buf = bbuf_null;
102 size_t sz = 0, aligned = 0;
103 unsigned int rec = 0, total_rec = 0;
104 unsigned int dir_size;
106 aligned = ROUNDUP(loc->search_size, DIRENT_ALIGNMENT);
107 result = &loc->result;
109 ext2db_itbegin(&dbit, inode, DBIT_MODE_BLOCK);
111 while (!found && ext2db_itnext(&dbit))
115 dir = (struct ext2b_dirent*)offset(dbit.data, rec);
117 sz = dir->rec_len - __dirent_realsize(dir);
118 sz = ROUNDDOWN(sz, DIRENT_ALIGNMENT);
119 if ((signed)sz >= (signed)aligned) {
125 total_rec += dir->rec_len;
126 } while(rec < dbit.blksz);
128 if (likely(prev_buf)) {
129 fsblock_put(prev_buf);
132 prev_buf = fsblock_take(dbit.sel_buf);
135 ext2_debug("dr_find_slot: found=%d, blk_off=%d, off=%d, gap=%d, blk=%d/%d",
136 found, rec, total_rec, sz, dbit.pos - 1, dbit.end_pos);
138 loc->db_pos = dbit.pos - 1;
140 if (blkbuf_nullbuf(prev_buf)) {
141 // this dir is brand new
142 loc->state = DIRENT_APPEND;
146 dir_size = ROUNDUP(__dirent_realsize(dir), 4);
147 loc->new_prev_reclen = dir_size;
149 result->prev = (struct ext2_dnode_sub) {
150 .buf = fsblock_take(prev_buf),
155 // if prev is the last, and no more space left behind.
156 assert_fs(rec == dbit.blksz);
158 result->self.buf = bbuf_null;
161 loc->state = DIRENT_APPEND;
166 dir = (struct ext2b_dirent*)offset(dbit.data, rec);
168 result->self = (struct ext2_dnode_sub) {
169 .buf = fsblock_take(dbit.sel_buf),
175 loc->state = DIRENT_INSERT;
178 return itstate_sel(&dbit, 0);
182 __release_dnode_blocks(struct ext2_dnode* e_dno)
184 fsblock_put(e_dno->prev.buf);
185 fsblock_put(e_dno->self.buf);
189 __destruct_ext2_dnode(struct ext2_dnode* e_dno)
191 __release_dnode_blocks(e_dno);
196 __check_special(struct v_dnode* dnode)
198 return HSTR_EQ(&dnode->name, &vfs_dot)
199 || HSTR_EQ(&dnode->name, &vfs_ddot);
203 __check_empty_dir(struct v_inode* dir_ino)
205 struct ext2_iterator iter;
206 struct ext2b_dirent* dir;
208 ext2dr_itbegin(&iter, dir_ino);
209 while (ext2dr_itnext(&iter))
212 if (strneq(dir->name, vfs_dot.value, 1)) {
216 if (strneq(dir->name, vfs_ddot.value, 2)) {
229 ext2dr_itbegin(struct ext2_iterator* iter, struct v_inode* inode)
231 *iter = (struct ext2_iterator){
234 .blksz = inode->sb->blksize
237 iter->sel_buf = ext2db_get(inode, 0);
238 ext2_itcheckbuf(iter);
242 ext2dr_itreset(struct ext2_iterator* iter)
244 fsblock_put(iter->sel_buf);
245 iter->sel_buf = ext2db_get(iter->inode, 0);
246 ext2_itcheckbuf(iter);
252 ext2dr_itffw(struct ext2_iterator* iter, int count)
255 while (i < count && ext2dr_itnext(iter)) {
263 ext2dr_itend(struct ext2_iterator* iter)
266 fsblock_put(iter->sel_buf);
271 ext2dr_itnext(struct ext2_iterator* iter)
273 struct ext2b_dirent* d;
274 unsigned int blkpos, db_index;
279 if (iter->has_error) {
283 if (likely(iter->dirent)) {
286 assert_fs(!(d->rec_len % 4));
287 iter->pos += d->rec_len;
289 if (!d->rec_len || !d->inode) {
294 blkpos = iter->pos % iter->blksz;
295 db_index = iter->pos / iter->blksz;
297 if (unlikely(iter->pos >= iter->blksz)) {
300 buf = ext2db_get(iter->inode, db_index);
303 if (!buf || !ext2_itcheckbuf(iter)) {
308 d = (struct ext2b_dirent*)offset(blkbuf_data(buf), blkpos);
315 ext2dr_open(struct v_inode* this, struct v_file* file)
317 struct ext2_file* e_file;
319 e_file = EXT2_FILE(file);
321 ext2dr_itbegin(&e_file->iter, this);
323 return itstate_sel(&e_file->iter, 0);
327 ext2dr_close(struct v_inode* this, struct v_file* file)
329 struct ext2_file* e_file;
331 e_file = EXT2_FILE(file);
333 ext2dr_itend(&e_file->iter);
339 ext2dr_lookup(struct v_inode* inode, struct v_dnode* dnode)
342 struct ext2b_dirent* dir;
343 struct ext2_dnode* e_dnode;
344 struct v_inode* dir_inode;
346 e_dnode = valloc(sizeof(struct ext2_dnode));
347 errno = __find_dirent_byname(inode, &dnode->name, e_dnode);
353 dir = e_dnode->self.dirent;
354 if (!(dir_inode = vfs_i_find(inode->sb, dir->inode))) {
355 dir_inode = vfs_i_alloc(inode->sb);
356 ext2ino_fill(dir_inode, dir->inode);
359 dnode->data = e_dnode;
360 vfs_assign_inode(dnode, dir_inode);
371 #define check_imode(val, imode) (((val) & (imode)) == (imode))
373 static inline unsigned int
374 __imode_to_filetype(unsigned int imode)
376 if (check_imode(imode, IMODE_IFLNK)) {
380 if (check_imode(imode, IMODE_IFBLK)) {
384 if (check_imode(imode, IMODE_IFCHR)) {
388 if (check_imode(imode, IMODE_IFDIR)) {
392 if (check_imode(imode, IMODE_IFREG)) {
400 __dir_filetype(struct v_superblock* vsb, struct ext2b_dirent* dir)
405 if (ext2_feature(vsb, FEAT_FILETYPE)) {
406 type = dir->file_type;
409 struct ext2_fast_inode e_fino;
411 errno = ext2ino_get_fast(vsb, dir->inode, &e_fino);
416 type = __imode_to_filetype(e_fino.ino->i_mode);
418 fsblock_put(e_fino.buf);
421 if (type == FT_DIR) {
425 if (type == FT_SYM) {
433 ext2dr_read(struct v_file *file, struct dir_context *dctx)
435 struct ext2_file* e_file;
436 struct ext2b_dirent* dir;
437 struct ext2_iterator* iter;
438 struct v_superblock* vsb;
441 e_file = EXT2_FILE(file);
442 vsb = file->inode->sb;
443 iter = &e_file->iter;
445 if (!ext2dr_itnext(&e_file->iter)) {
446 return itstate_sel(iter, 0);
449 dir = e_file->iter.dirent;
450 dirtype = __dir_filetype(vsb, dir);
455 fsapi_dir_report(dctx, dir->name, dir->name_len, dirtype);
461 ext2dr_seek(struct v_file* file, size_t offset)
463 struct ext2_file* e_file;
464 struct ext2_iterator* iter;
467 e_file = EXT2_FILE(file);
468 iter = &e_file->iter;
471 if (offset == fpos) {
476 fpos = ext2dr_itffw(iter, fpos - offset);
480 if (!offset || offset < fpos) {
481 ext2dr_itreset(iter);
484 fpos = ext2dr_itffw(iter, offset);
486 return itstate_sel(iter, 0);
490 ext2dr_insert(struct v_inode* this, struct ext2b_dirent* dirent,
491 struct ext2_dnode** e_dno_out)
494 size_t size, new_reclen, old_reclen;
495 struct ext2_dnode* e_dno;
496 struct ext2b_dirent* prev_dirent;
497 struct dirent_locator locator;
500 size = __dirent_realsize(dirent);
501 __init_locator(&locator, size);
503 errno = __find_free_dirent_slot(this, &locator);
508 e_dno = &locator.result;
509 new_reclen = locator.new_prev_reclen;
510 old_reclen = fsapi_block_size(this->sb);
512 if (locator.state != DIRENT_INSERT)
514 if ((errno = ext2db_acquire(this, locator.db_pos, &buf)))
517 this->fsize += fsapi_block_size(this->sb);
518 ext2ino_update(this);
520 e_dno->self.buf = buf;
521 e_dno->self.dirent = block_buffer(buf, struct ext2b_dirent);
533 | | dirent | | | size
534 old_reclen | +--------+ | -
545 prev_dirent = e_dno->prev.dirent;
546 old_reclen = prev_dirent->rec_len;
547 old_reclen -= new_reclen;
549 prev_dirent->rec_len = new_reclen;
550 fsblock_dirty(e_dno->prev.buf);
553 ext2_debug("dr_insert: state=%d, blk=%d, prev_rlen=%d, new_rlen=%d",
554 locator.state, locator.db_pos, new_reclen, old_reclen);
556 assert_fs(new_reclen > 0);
557 assert_fs(old_reclen > 0);
559 dirent->rec_len = old_reclen;
561 memcpy(e_dno->self.dirent, dirent, size);
562 fsblock_dirty(e_dno->self.buf);
565 __release_dnode_blocks(e_dno);
568 *e_dno_out = valloc(sizeof(*e_dno));
569 memcpy(*e_dno_out, e_dno, sizeof(*e_dno));
575 __release_dnode_blocks(e_dno);
580 ext2dr_remove(struct ext2_dnode* e_dno)
582 struct ext2_dnode_sub *dir_prev, *dir;
583 assert(e_dno->prev.dirent);
585 dir_prev = &e_dno->prev;
588 dir_prev->dirent->rec_len += dir->dirent->rec_len;
589 dir->dirent->rec_len = 0;
590 dir->dirent->inode = 0;
592 fsblock_dirty(dir_prev->buf);
593 fsblock_dirty(dir->buf);
595 __destruct_ext2_dnode(e_dno);
601 ext2_rmdir(struct v_inode* this, struct v_dnode* dnode)
604 struct v_inode* self;
605 struct ext2_dnode* e_dno;
608 e_dno = EXT2_DNO(dnode);
610 if (__check_special(dnode)) {
614 if (!__check_empty_dir(self)) {
618 if ((errno = ext2ino_free(self))) {
622 return ext2dr_remove(e_dno);
626 __d_insert(struct v_inode* parent, struct v_inode* self,
627 struct ext2b_dirent* dirent,
628 struct hstr* name, struct ext2_dnode** e_dno_out)
630 ext2dr_setup_dirent(dirent, self, name);
632 dirent->inode = self->id;
633 return ext2dr_insert(parent, dirent, e_dno_out);
637 ext2_mkdir(struct v_inode* this, struct v_dnode* dnode)
640 struct ext2_inode *e_contain, *e_created;
641 struct v_inode* i_created;
642 struct ext2_dnode* e_dno = NULL;
643 struct ext2b_dirent dirent;
645 e_contain = EXT2_INO(this);
647 errno = ext2ino_make(this->sb, VFS_IFDIR, e_contain, &i_created);
652 e_created = EXT2_INO(i_created);
654 if ((errno = __d_insert(this, i_created, &dirent, &dnode->name, &e_dno))) {
658 // link the created dir inode to dirent
659 ext2ino_linkto(e_created, &dirent);
663 // we don't need ext2ino_linkto here.
665 if ((errno = __d_insert(i_created, i_created, &dirent, &vfs_dot, NULL))) {
669 if ((errno = __d_insert(i_created, this, &dirent, &vfs_ddot, NULL))) {
673 vfs_assign_inode(dnode, i_created);
677 __destruct_ext2_dnode(e_dno);
681 ext2ino_free(i_created);
682 vfs_i_free(i_created);
688 ext2dr_setup_dirent(struct ext2b_dirent* dirent,
689 struct v_inode* inode, struct hstr* name)
693 imode = EXT2_INO(inode)->ino->i_mode;
694 *dirent = (struct ext2b_dirent){
695 .name_len = name->len
698 strncpy(dirent->name, name->value, name->len);
700 if (ext2_feature(inode->sb, FEAT_FILETYPE)) {
701 dirent->file_type = __imode_to_filetype(imode);
706 ext2_rename(struct v_inode* from_inode, struct v_dnode* from_dnode,
707 struct v_dnode* to_dnode)
710 struct v_inode* to_parent;
712 if (EXT2_DNO(to_dnode)) {
713 errno = ext2_unlink(to_dnode->inode, to_dnode);
719 errno = ext2_link(from_inode, to_dnode);
724 return ext2_unlink(from_inode, from_dnode);