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);
60 return itstate_sel(&iter, errno);
64 __dirent_realsize(struct ext2b_dirent* dirent)
66 return sizeof(*dirent) - sizeof(dirent->name) + dirent->name_len;
69 #define DIRENT_INSERT 0
70 #define DIRENT_APPEND 1
72 #define DIRENT_ALIGNMENT sizeof(int)
79 struct ext2_dnode result;
80 size_t new_prev_reclen;
85 static inline void must_inline
86 __init_locator(struct dirent_locator* loc, size_t search_size)
88 *loc = (struct dirent_locator) { .search_size = search_size };
92 __find_free_dirent_slot(struct v_inode* inode, struct dirent_locator* loc)
94 struct ext2_iterator dbit;
95 struct ext2b_dirent *dir = NULL;
96 struct ext2_dnode* result;
98 bbuf_t prev_buf = bbuf_null;
101 size_t sz = 0, aligned = 0;
102 unsigned int rec = 0, total_rec = 0;
103 unsigned int dir_size;
105 aligned = ROUNDUP(loc->search_size, DIRENT_ALIGNMENT);
106 result = &loc->result;
108 ext2db_itbegin(&dbit, inode, DBIT_MODE_BLOCK);
110 while (!found && ext2db_itnext(&dbit))
114 dir = (struct ext2b_dirent*)offset(dbit.data, rec);
116 sz = dir->rec_len - __dirent_realsize(dir);
117 sz = ROUNDDOWN(sz, DIRENT_ALIGNMENT);
118 if ((signed)sz >= (signed)aligned) {
124 total_rec += dir->rec_len;
125 } while(rec < dbit.blksz);
127 if (likely(prev_buf)) {
128 fsblock_put(prev_buf);
131 prev_buf = fsblock_take(dbit.sel_buf);
134 ext2_debug("dr_find_slot: found=%d, blk_off=%d, off=%d, gap=%d, blk=%d/%d",
135 found, rec, total_rec, sz, dbit.pos - 1, dbit.end_pos);
137 loc->db_pos = dbit.pos - 1;
139 if (blkbuf_nullbuf(prev_buf)) {
140 // this dir is brand new
141 loc->state = DIRENT_APPEND;
145 dir_size = ROUNDUP(__dirent_realsize(dir), 4);
146 loc->new_prev_reclen = dir_size;
148 result->prev = (struct ext2_dnode_sub) {
149 .buf = fsblock_take(prev_buf),
154 // if prev is the last, and no more space left behind.
155 assert_fs(rec == dbit.blksz);
157 result->self.buf = bbuf_null;
160 loc->state = DIRENT_APPEND;
165 dir = (struct ext2b_dirent*)offset(dbit.data, rec);
167 result->self = (struct ext2_dnode_sub) {
168 .buf = fsblock_take(dbit.sel_buf),
174 loc->state = DIRENT_INSERT;
177 return itstate_sel(&dbit, 0);
181 __release_dnode_blocks(struct ext2_dnode* e_dno)
183 fsblock_put(e_dno->prev.buf);
184 fsblock_put(e_dno->self.buf);
188 __destruct_ext2_dnode(struct ext2_dnode* e_dno)
190 __release_dnode_blocks(e_dno);
195 __check_special(struct v_dnode* dnode)
197 return HSTR_EQ(&dnode->name, &vfs_dot)
198 || HSTR_EQ(&dnode->name, &vfs_ddot);
202 __check_empty_dir(struct v_inode* dir_ino)
204 struct ext2_iterator iter;
205 struct ext2b_dirent* dir;
207 ext2dr_itbegin(&iter, dir_ino);
208 while (ext2dr_itnext(&iter))
211 if (strneq(dir->name, vfs_dot.value, 1)) {
215 if (strneq(dir->name, vfs_ddot.value, 2)) {
228 ext2dr_itbegin(struct ext2_iterator* iter, struct v_inode* inode)
230 *iter = (struct ext2_iterator){
233 .blksz = inode->sb->blksize
236 iter->sel_buf = ext2db_get(inode, 0);
237 ext2_itcheckbuf(iter);
241 ext2dr_itreset(struct ext2_iterator* iter)
243 fsblock_put(iter->sel_buf);
244 iter->sel_buf = ext2db_get(iter->inode, 0);
245 ext2_itcheckbuf(iter);
251 ext2dr_itffw(struct ext2_iterator* iter, int count)
254 while (i < count && ext2dr_itnext(iter)) {
262 ext2dr_itend(struct ext2_iterator* iter)
265 fsblock_put(iter->sel_buf);
270 ext2dr_itnext(struct ext2_iterator* iter)
272 struct ext2b_dirent* d;
273 unsigned int blkpos, db_index;
278 if (iter->has_error) {
282 if (likely(iter->dirent)) {
285 assert_fs(!(d->rec_len % 4));
286 iter->pos += d->rec_len;
288 if (!d->rec_len || !d->inode) {
293 blkpos = iter->pos % iter->blksz;
294 db_index = iter->pos / iter->blksz;
296 if (unlikely(iter->pos >= iter->blksz)) {
299 buf = ext2db_get(iter->inode, db_index);
302 if (!buf || !ext2_itcheckbuf(iter)) {
307 d = (struct ext2b_dirent*)offset(blkbuf_data(buf), blkpos);
314 ext2dr_open(struct v_inode* this, struct v_file* file)
316 struct ext2_file* e_file;
318 e_file = EXT2_FILE(file);
320 ext2dr_itbegin(&e_file->iter, this);
322 return itstate_sel(&e_file->iter, 0);
326 ext2dr_close(struct v_inode* this, struct v_file* file)
328 struct ext2_file* e_file;
330 e_file = EXT2_FILE(file);
332 ext2dr_itend(&e_file->iter);
338 ext2dr_lookup(struct v_inode* inode, struct v_dnode* dnode)
341 struct ext2b_dirent* dir;
342 struct ext2_dnode* e_dnode;
343 struct v_inode* dir_inode;
345 e_dnode = valloc(sizeof(struct ext2_dnode));
346 errno = __find_dirent_byname(inode, &dnode->name, e_dnode);
352 dir = e_dnode->self.dirent;
353 if (!(dir_inode = vfs_i_find(inode->sb, dir->inode))) {
354 dir_inode = vfs_i_alloc(inode->sb);
355 ext2ino_fill(dir_inode, dir->inode);
358 dnode->data = e_dnode;
359 vfs_assign_inode(dnode, dir_inode);
370 #define check_imode(val, imode) (((val) & (imode)) == (imode))
372 static inline unsigned int
373 __imode_to_filetype(unsigned int imode)
375 if (check_imode(imode, IMODE_IFLNK)) {
379 if (check_imode(imode, IMODE_IFBLK)) {
383 if (check_imode(imode, IMODE_IFCHR)) {
387 if (check_imode(imode, IMODE_IFDIR)) {
391 if (check_imode(imode, IMODE_IFREG)) {
399 __dir_filetype(struct v_superblock* vsb, struct ext2b_dirent* dir)
404 if (ext2_feature(vsb, FEAT_FILETYPE)) {
405 type = dir->file_type;
408 struct ext2_fast_inode e_fino;
410 errno = ext2ino_get_fast(vsb, dir->inode, &e_fino);
415 type = __imode_to_filetype(e_fino.ino->i_mode);
417 fsblock_put(e_fino.buf);
420 if (type == FT_DIR) {
424 if (type == FT_SYM) {
432 ext2dr_read(struct v_file *file, struct dir_context *dctx)
434 struct ext2_file* e_file;
435 struct ext2b_dirent* dir;
436 struct ext2_iterator* iter;
437 struct v_superblock* vsb;
440 e_file = EXT2_FILE(file);
441 vsb = file->inode->sb;
442 iter = &e_file->iter;
444 if (!ext2dr_itnext(&e_file->iter)) {
445 return itstate_sel(iter, 0);
448 dir = e_file->iter.dirent;
449 dirtype = __dir_filetype(vsb, dir);
454 fsapi_dir_report(dctx, dir->name, dir->name_len, dirtype);
460 ext2dr_seek(struct v_file* file, size_t offset)
462 struct ext2_file* e_file;
463 struct ext2_iterator* iter;
466 e_file = EXT2_FILE(file);
467 iter = &e_file->iter;
470 if (offset == fpos) {
475 fpos = ext2dr_itffw(iter, fpos - offset);
479 if (!offset || offset < fpos) {
480 ext2dr_itreset(iter);
483 fpos = ext2dr_itffw(iter, offset);
485 return itstate_sel(iter, 0);
489 ext2dr_insert(struct v_inode* this, struct ext2b_dirent* dirent,
490 struct ext2_dnode** e_dno_out)
493 size_t size, new_reclen, old_reclen;
494 struct ext2_dnode* e_dno;
495 struct ext2b_dirent* prev_dirent;
496 struct dirent_locator locator;
499 size = __dirent_realsize(dirent);
500 __init_locator(&locator, size);
502 errno = __find_free_dirent_slot(this, &locator);
507 e_dno = &locator.result;
508 new_reclen = locator.new_prev_reclen;
509 old_reclen = fsapi_block_size(this->sb);
511 if (locator.state != DIRENT_INSERT)
513 if ((errno = ext2db_acquire(this, locator.db_pos, &buf)))
516 this->fsize += fsapi_block_size(this->sb);
517 ext2ino_update(this);
519 e_dno->self.buf = buf;
520 e_dno->self.dirent = block_buffer(buf, struct ext2b_dirent);
532 | | dirent | | | size
533 old_reclen | +--------+ | -
544 prev_dirent = e_dno->prev.dirent;
545 old_reclen = prev_dirent->rec_len;
546 old_reclen -= new_reclen;
548 prev_dirent->rec_len = new_reclen;
549 fsblock_dirty(e_dno->prev.buf);
552 ext2_debug("dr_insert: state=%d, blk=%d, prev_rlen=%d, new_rlen=%d",
553 locator.state, locator.db_pos, new_reclen, old_reclen);
555 assert_fs(new_reclen > 0);
556 assert_fs(old_reclen > 0);
558 dirent->rec_len = old_reclen;
560 memcpy(e_dno->self.dirent, dirent, size);
561 fsblock_dirty(e_dno->self.buf);
564 __release_dnode_blocks(e_dno);
573 __release_dnode_blocks(e_dno);
578 ext2dr_remove(struct ext2_dnode* e_dno)
580 struct ext2_dnode_sub *dir_prev, *dir;
581 assert(e_dno->prev.dirent);
583 dir_prev = &e_dno->prev;
586 dir_prev->dirent->rec_len += dir->dirent->rec_len;
587 dir->dirent->rec_len = 0;
588 dir->dirent->inode = 0;
590 fsblock_dirty(dir_prev->buf);
591 fsblock_dirty(dir->buf);
593 __destruct_ext2_dnode(e_dno);
599 ext2_rmdir(struct v_inode* this, struct v_dnode* dnode)
602 struct v_inode* self;
603 struct ext2_dnode* e_dno;
606 e_dno = EXT2_DNO(dnode);
608 if (__check_special(dnode)) {
612 if (!__check_empty_dir(self)) {
616 if ((errno = ext2ino_free(self))) {
620 return ext2dr_remove(e_dno);
624 __d_insert(struct v_inode* parent, struct v_inode* self,
625 struct ext2b_dirent* dirent,
626 struct hstr* name, struct ext2_dnode** e_dno_out)
628 ext2dr_setup_dirent(dirent, self, name);
630 dirent->inode = self->id;
631 return ext2dr_insert(parent, dirent, e_dno_out);
635 ext2_mkdir(struct v_inode* this, struct v_dnode* dnode)
638 struct ext2_inode *e_contain, *e_created;
639 struct v_inode* i_created;
640 struct ext2_dnode* e_dno = NULL;
641 struct ext2b_dirent dirent;
643 e_contain = EXT2_INO(this);
645 errno = ext2ino_make(this->sb, VFS_IFDIR, e_contain, &i_created);
650 e_created = EXT2_INO(i_created);
652 if ((errno = __d_insert(this, i_created, &dirent, &dnode->name, &e_dno))) {
656 // link the created dir inode to dirent
657 ext2ino_linkto(e_created, &dirent);
661 // we don't need ext2ino_linkto here.
663 if ((errno = __d_insert(i_created, i_created, &dirent, &vfs_dot, NULL))) {
667 if ((errno = __d_insert(i_created, this, &dirent, &vfs_ddot, NULL))) {
671 vfs_assign_inode(dnode, i_created);
675 __destruct_ext2_dnode(e_dno);
679 ext2ino_free(i_created);
680 vfs_i_free(i_created);
686 ext2dr_setup_dirent(struct ext2b_dirent* dirent,
687 struct v_inode* inode, struct hstr* name)
691 imode = EXT2_INO(inode)->ino->i_mode;
692 *dirent = (struct ext2b_dirent){
693 .name_len = name->len
696 strncpy(dirent->name, name->value, name->len);
698 if (ext2_feature(inode->sb, FEAT_FILETYPE)) {
699 dirent->file_type = __imode_to_filetype(imode);
704 ext2_rename(struct v_inode* from_inode, struct v_dnode* from_dnode,
705 struct v_dnode* to_dnode)
708 struct v_inode* to_parent;
710 if (EXT2_DNO(to_dnode)) {
711 errno = ext2_unlink(to_dnode->inode, to_dnode);
717 errno = ext2_link(from_inode, to_dnode);
722 return ext2_unlink(from_inode, from_dnode);