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_SLOT_MID 0
70 #define DIRENT_SLOT_LAST 1
71 #define DIRENT_SLOT_EMPTY 2
74 __find_free_dirent_slot(struct v_inode* inode, size_t size,
75 struct ext2_dnode* e_dnode_out, size_t *reclen)
77 struct ext2_iterator iter;
78 struct ext2b_dirent *dir = NULL;
79 bbuf_t prev_buf = bbuf_null;
82 ext2db_itbegin(&iter, inode);
85 unsigned int rec = 0, total_rec = 0;
87 while (!found && ext2db_itnext(&iter))
91 dir = (struct ext2b_dirent*)offset(iter.data, rec);
93 sz = dir->rec_len - __dirent_realsize(dir);
94 sz = ROUNDDOWN(sz, 4);
101 total_rec += dir->rec_len;
102 } while(rec < iter.blksz);
104 if (likely(prev_buf)) {
105 fsblock_put(prev_buf);
108 prev_buf = fsblock_take(iter.sel_buf);
111 if (blkbuf_nullbuf(prev_buf)) {
112 // this dir is brand new
113 return DIRENT_SLOT_EMPTY;
116 e_dnode_out->prev = (struct ext2_dnode_sub) {
117 .buf = fsblock_take(prev_buf),
122 // if prev is the last, and no more space left behind.
123 assert_fs(rec == iter.blksz);
125 e_dnode_out->self.buf = bbuf_null;
127 return itstate_sel(&iter, DIRENT_SLOT_LAST);
130 unsigned int dir_size;
132 dir_size = ROUNDUP(__dirent_realsize(dir), 4);
135 rec = total_rec + dir_size;
136 dir = (struct ext2b_dirent*)offset(iter.data, rec);
138 e_dnode_out->self = (struct ext2_dnode_sub) {
139 .buf = fsblock_take(iter.sel_buf),
144 return DIRENT_SLOT_MID;
148 __destruct_ext2_dnode(struct ext2_dnode* e_dno)
150 fsblock_put(e_dno->prev.buf);
151 fsblock_put(e_dno->self.buf);
156 __check_special(struct v_dnode* dnode)
158 return HSTR_EQ(&dnode->name, &vfs_dot)
159 || HSTR_EQ(&dnode->name, &vfs_ddot);
163 __check_empty_dir(struct v_inode* dir_ino)
165 struct ext2_iterator iter;
166 struct ext2b_dirent* dir;
168 ext2dr_itbegin(&iter, dir_ino);
169 while (ext2dr_itnext(&iter))
172 if (strneq(dir->name, vfs_dot.value, 1)) {
176 if (strneq(dir->name, vfs_ddot.value, 2)) {
189 ext2dr_itbegin(struct ext2_iterator* iter, struct v_inode* inode)
191 *iter = (struct ext2_iterator){
194 .blksz = inode->sb->blksize
197 iter->sel_buf = ext2db_get(inode, 0);
198 ext2_itcheckbuf(iter);
202 ext2dr_itreset(struct ext2_iterator* iter)
204 fsblock_put(iter->sel_buf);
205 iter->sel_buf = ext2db_get(iter->inode, 0);
206 ext2_itcheckbuf(iter);
212 ext2dr_itffw(struct ext2_iterator* iter, int count)
215 while (i < count && ext2dr_itnext(iter)) {
223 ext2dr_itend(struct ext2_iterator* iter)
226 fsblock_put(iter->sel_buf);
231 ext2dr_itnext(struct ext2_iterator* iter)
233 struct ext2b_dirent* d;
234 unsigned int blkpos, db_index;
239 if (iter->has_error) {
243 if (likely(iter->dirent)) {
246 assert_fs(!(d->rec_len % 4));
247 iter->pos += d->rec_len;
249 if (!d->rec_len || !d->inode) {
254 blkpos = iter->pos % iter->blksz;
255 db_index = iter->pos / iter->blksz;
257 if (unlikely(iter->pos >= iter->blksz)) {
260 buf = ext2db_get(iter->inode, db_index);
263 if (!buf || !ext2_itcheckbuf(iter)) {
268 d = (struct ext2b_dirent*)offset(blkbuf_data(buf), blkpos);
275 ext2dr_open(struct v_inode* this, struct v_file* file)
277 struct ext2_file* e_file;
279 e_file = EXT2_FILE(file);
281 ext2dr_itbegin(&e_file->iter, this);
283 return itstate_sel(&e_file->iter, 0);
287 ext2dr_close(struct v_inode* this, struct v_file* file)
289 struct ext2_file* e_file;
291 e_file = EXT2_FILE(file);
293 ext2dr_itend(&e_file->iter);
299 ext2dr_lookup(struct v_inode* inode, struct v_dnode* dnode)
302 struct ext2b_dirent* dir;
303 struct ext2_dnode* e_dnode;
304 struct v_inode* dir_inode;
306 e_dnode = valloc(sizeof(struct ext2_dnode));
307 errno = __find_dirent_byname(inode, &dnode->name, e_dnode);
313 dir = e_dnode->self.dirent;
314 if (!(dir_inode = vfs_i_find(inode->sb, dir->inode))) {
315 dir_inode = vfs_i_alloc(inode->sb);
316 ext2ino_fill(dir_inode, dir->inode);
319 dnode->data = e_dnode;
320 vfs_assign_inode(dnode, dir_inode);
331 #define check_imode(val, imode) (((val) & (imode)) == (imode))
333 static inline unsigned int
334 __imode_to_filetype(unsigned int imode)
336 if (check_imode(imode, IMODE_IFLNK)) {
340 if (check_imode(imode, IMODE_IFBLK)) {
344 if (check_imode(imode, IMODE_IFCHR)) {
348 if (check_imode(imode, IMODE_IFDIR)) {
352 if (check_imode(imode, IMODE_IFREG)) {
360 __dir_filetype(struct v_superblock* vsb, struct ext2b_dirent* dir)
365 if (ext2_feature(vsb, FEAT_FILETYPE)) {
366 type = dir->file_type;
369 struct ext2_fast_inode e_fino;
371 errno = ext2ino_get_fast(vsb, dir->inode, &e_fino);
376 type = __imode_to_filetype(e_fino.ino->i_mode);
378 fsblock_put(e_fino.buf);
381 if (type == FT_DIR) {
385 if (type == FT_SYM) {
393 ext2dr_read(struct v_file *file, struct dir_context *dctx)
395 struct ext2_file* e_file;
396 struct ext2b_dirent* dir;
397 struct ext2_iterator* iter;
398 struct v_superblock* vsb;
401 e_file = EXT2_FILE(file);
402 vsb = file->inode->sb;
403 iter = &e_file->iter;
405 if (!ext2dr_itnext(&e_file->iter)) {
406 return itstate_sel(iter, 0);
409 dir = e_file->iter.dirent;
410 dirtype = __dir_filetype(vsb, dir);
415 fsapi_dir_report(dctx, dir->name, dir->name_len, dirtype);
421 ext2dr_seek(struct v_file* file, size_t offset)
423 struct ext2_file* e_file;
424 struct ext2_iterator* iter;
427 e_file = EXT2_FILE(file);
428 iter = &e_file->iter;
431 if (offset == fpos) {
436 fpos = ext2dr_itffw(iter, fpos - offset);
440 if (!offset || offset < fpos) {
441 ext2dr_itreset(iter);
444 fpos = ext2dr_itffw(iter, offset);
446 return itstate_sel(iter, 0);
450 ext2dr_insert(struct v_inode* this, struct ext2b_dirent* dirent,
451 struct ext2_dnode** e_dno_out)
454 size_t size, new_reclen, old_reclen;
455 struct ext2_inode* e_self;
456 struct ext2_dnode* e_dno;
457 struct ext2b_dirent* prev_dirent;
460 e_self = EXT2_INO(this);
461 e_dno = vzalloc(sizeof(*e_dno));
463 size = __dirent_realsize(dirent);
464 errno = __find_free_dirent_slot(this, size, e_dno, &new_reclen);
469 if (errno == DIRENT_SLOT_EMPTY) {
470 if ((errno = ext2db_acquire(this, 0, &buf))) {
474 this->fsize += fsapi_block_size(this->sb);
475 ext2ino_update(this);
477 old_reclen = fsapi_block_size(this->sb);
478 e_dno->self.buf = buf;
479 e_dno->self.dirent = blkbuf_data(buf);
484 prev_dirent = e_dno->prev.dirent;
485 old_reclen = prev_dirent->rec_len;
487 if (errno == DIRENT_SLOT_LAST) {
488 // prev is last record
489 if ((errno = ext2db_alloc(this, &buf))) {
493 this->fsize += fsapi_block_size(this->sb);
494 ext2ino_update(this);
496 new_reclen = __dirent_realsize(prev_dirent);
497 new_reclen = ROUNDUP(new_reclen, sizeof(int));
498 e_dno->self = (struct ext2_dnode_sub) {
500 .dirent = block_buffer(buf, struct ext2b_dirent)
512 | | dirent | | | size
513 old_reclen | +--------+ | -
522 old_reclen -= new_reclen;
523 prev_dirent->rec_len = new_reclen;
524 fsblock_dirty(e_dno->prev.buf);
527 dirent->rec_len = ROUNDUP(old_reclen, sizeof(int));
528 memcpy(e_dno->self.dirent, dirent, size);
529 fsblock_dirty(e_dno->self.buf);
532 __destruct_ext2_dnode(e_dno);
541 __destruct_ext2_dnode(e_dno);
546 ext2dr_remove(struct ext2_dnode* e_dno)
548 struct ext2_dnode_sub *dir_prev, *dir;
549 assert(e_dno->prev.dirent);
551 dir_prev = &e_dno->prev;
554 dir_prev->dirent->rec_len += dir->dirent->rec_len;
555 dir->dirent->rec_len = 0;
556 dir->dirent->inode = 0;
558 fsblock_dirty(dir_prev->buf);
559 fsblock_dirty(dir->buf);
561 __destruct_ext2_dnode(e_dno);
567 ext2_rmdir(struct v_inode* this, struct v_dnode* dnode)
570 struct v_inode* self;
571 struct ext2_dnode* e_dno;
574 e_dno = EXT2_DNO(dnode);
576 if (__check_special(dnode)) {
580 if (!__check_empty_dir(self)) {
584 if ((errno = ext2ino_free(self))) {
588 return ext2dr_remove(e_dno);
592 __d_insert(struct v_inode* parent, struct v_inode* self,
593 struct ext2b_dirent* dirent,
594 struct hstr* name, struct ext2_dnode** e_dno_out)
596 ext2dr_setup_dirent(dirent, self, name);
598 dirent->inode = self->id;
599 return ext2dr_insert(parent, dirent, e_dno_out);
603 ext2_mkdir(struct v_inode* this, struct v_dnode* dnode)
606 struct ext2_inode *e_contain, *e_created;
607 struct v_inode* i_created;
608 struct ext2_dnode* e_dno = NULL;
609 struct ext2b_dirent dirent;
611 e_contain = EXT2_INO(this);
613 errno = ext2ino_make(this->sb, VFS_IFDIR, e_contain, &i_created);
618 e_created = EXT2_INO(i_created);
620 if ((errno = __d_insert(this, i_created, &dirent, &dnode->name, &e_dno))) {
624 // link the created dir inode to dirent
625 ext2ino_linkto(e_created, &dirent);
629 // we don't need ext2ino_linkto here.
631 if ((errno = __d_insert(i_created, i_created, &dirent, &vfs_dot, NULL))) {
635 if ((errno = __d_insert(i_created, this, &dirent, &vfs_ddot, NULL))) {
639 vfs_assign_inode(dnode, i_created);
643 __destruct_ext2_dnode(e_dno);
647 ext2ino_free(i_created);
648 vfs_i_free(i_created);
654 ext2dr_setup_dirent(struct ext2b_dirent* dirent,
655 struct v_inode* inode, struct hstr* name)
659 imode = EXT2_INO(inode)->ino->i_mode;
660 *dirent = (struct ext2b_dirent){
661 .name_len = name->len
664 strncpy(dirent->name, name->value, name->len);
666 if (ext2_feature(inode->sb, FEAT_FILETYPE)) {
667 dirent->file_type = __imode_to_filetype(imode);
672 ext2_rename(struct v_inode* from_inode, struct v_dnode* from_dnode,
673 struct v_dnode* to_dnode)
676 struct v_inode* to_parent;
678 if (EXT2_DNO(to_dnode)) {
679 errno = ext2_unlink(to_dnode->inode, to_dnode);
685 errno = ext2_link(from_inode, to_dnode);
690 return ext2_unlink(from_inode, from_dnode);