<commit_before>/*
 * Copyright (c) 2016 - 2022 <NAME>
 * This file is part of Onyx, and is released under the terms of the MIT License
 * check LICENSE at the root directory for more information
 *
 * SPDX-License-Identifier: MIT
 */

#include "ext2.h"

#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#include <onyx/buffer.h>
#include <onyx/compiler.h>
#include <onyx/cred.h>
#include <onyx/dentry.h>
#include <onyx/dev.h>
#include <onyx/fs_mount.h>
#include <onyx/limits.h>
#include <onyx/log.h>
#include <onyx/pagecache.h>
#include <onyx/panic.h>
#include <onyx/vfs.h>
#include <onyx/vm.h>

struct inode *ext2_open(struct dentry *dir, const char *name);
off_t ext2_getdirent(struct dirent *buf, off_t off, struct file *f);
struct inode *ext2_creat(const char *path, int mode, struct dentry *dir);
char *ext2_readlink(struct file *ino);
void ext2_close(struct inode *ino);
struct inode *ext2_mknod(const char *name, mode_t mode, dev_t dev, struct dentry *dir);
struct inode *ext2_mkdir(const char *name, mode_t mode, struct dentry *dir);
int ext2_link_fops(struct file *target, const char *name, struct dentry *dir);
int ext2_unlink(const char *name, int flags, struct dentry *dir);
int ext2_fallocate(int mode, off_t off, off_t len, struct file *f);
int ext2_ftruncate(size_t len, struct file *f);
ssize_t ext2_readpage(struct page *page, size_t off, struct inode *ino);
ssize_t ext2_writepage(struct page *page, size_t off, struct inode *ino);
int ext2_prepare_write(inode *ino, struct page *page, size_t page_off, size_t offset, size_t len);
int ext2_link(struct inode *target, const char *name, struct inode *dir);
inode *ext2_symlink(const char *name, const char *dest, dentry *dir);

struct file_ops ext2_ops = {.open = ext2_open,
                            .close = ext2_close,
                            .getdirent = ext2_getdirent,
                            .creat = ext2_creat,
                            .link = ext2_link_fops,
                            .symlink = ext2_symlink,
                            .ftruncate = ext2_ftruncate,
                            .mkdir = ext2_mkdir,
                            .mknod = ext2_mknod,
                            .readlink = ext2_readlink,
                            .unlink = ext2_unlink,
                            .fallocate = ext2_fallocate,
                            .readpage = ext2_readpage,
                            .writepage = ext2_writepage,
                            .prepare_write = ext2_prepare_write};

void ext2_delete_inode(struct inode *inode_, uint32_t inum, struct ext2_superblock *fs)
{
    struct ext2_inode *inode = ext2_get_inode_from_node(inode_);

    inode->i_dtime = clock_get_posix_time();
    ext2_free_inode_space(inode_, fs);

    inode->i_links = 0;
    fs->update_inode(inode, inum);

    uint32_t block_group = (inum - 1) / fs->inodes_per_block_group;

    if (S_ISDIR(inode->i_mode))
        fs->block_groups[block_group].dec_used_dirs();

    fs->free_inode(inum);
}

void ext2_close(struct inode *vfs_ino)
{
    struct ext2_inode *inode = ext2_get_inode_from_node(vfs_ino);

    /* TODO: It would be better, cache-wise and memory allocator-wise if we
     * had ext2_inode incorporate a struct inode inside it, and have everything in the same
     * location.
     * TODO: We're also storing a lot of redudant info in ext2_inode(we already have most stuff in
     * the regular struct inode).
     */
    free(inode);
}

ssize_t ext2_writepage(page *page, size_t off, inode *ino)
{
    auto buf = block_buf_from_page(page);
    auto sb = ext2_superblock_from_inode(ino);

    assert(buf != nullptr);

    while (buf)
    {
        page_iov v[1];
        v->length = buf->block_size;
        v->page = buf->this_page;
        v->page_off = buf->page_off;

#if 0
		printk("Writing to block %lu\n", buf->block_nr);
#endif

        if (sb_write_bio(sb, v, 1, buf->block_nr) < 0)
        {
            sb->error("Error writing back page");
            return -EIO;
        }

        buf = buf->next;
    }

    return PAGE_SIZE;
}

ssize_t ext2_readpage(struct page *page, size_t off, struct inode *ino)
{
    bool is_buffer = page->flags & PAGE_FLAG_BUFFER;

    assert(is_buffer == true);

    auto raw_inode = ext2_get_inode_from_node(ino);
    auto sb = ext2_superblock_from_inode(ino);
    auto nr_blocks = PAGE_SIZE / sb->block_size;
    auto base_block_index = off / sb->block_size;

    auto curr_off = 0;

    for (size_t i = 0; i < nr_blocks; i++)
    {
        struct block_buf *b = nullptr;
        if (is_buffer && !(b = page_add_blockbuf(page, curr_off)))
        {
            page_destroy_block_bufs(page);
            return -ENOMEM;
        }

        auto res = ext2_get_block_from_inode(raw_inode, base_block_index + i, sb);
        if (res.has_error())
        {
            page_destroy_block_bufs(page);
            return -ENOMEM;
        }

        /* TODO: Coalesce reads */
        page_iov v[1];
        v->page = page;
        v->length = sb->block_size;
        v->page_off = curr_off;

        if (sb_read_bio(sb, v, 1, res.value()) < 0)
        {
            page_destroy_block_bufs(page);
            return -EIO;
        }

        if (is_buffer)
        {
            b->block_nr = res.value();
            b->block_size = sb->block_size;
            b->dev = sb->s_bdev;
        }

        curr_off += sb->block_size;
    }

    return min(PAGE_SIZE, ino->i_size - off);
}

struct ext2_inode_info *ext2_cache_inode_info(struct inode *ino, struct ext2_inode *fs_ino)
{<commit_msg>ext2: Don't read hole-blocks into the page cache<commit_after>/*
 * Copyright (c) 2016 - 2022 <NAME>
 * This file is part of Onyx, and is released under the terms of the MIT License
 * check LICENSE at the root directory for more information
 *
 * SPDX-License-Identifier: MIT
 */

#include "ext2.h"

#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#include <onyx/buffer.h>
#include <onyx/compiler.h>
#include <onyx/cred.h>
#include <onyx/dentry.h>
#include <onyx/dev.h>
#include <onyx/fs_mount.h>
#include <onyx/limits.h>
#include <onyx/log.h>
#include <onyx/pagecache.h>
#include <onyx/panic.h>
#include <onyx/vfs.h>
#include <onyx/vm.h>

struct inode *ext2_open(struct dentry *dir, const char *name);
off_t ext2_getdirent(struct dirent *buf, off_t off, struct file *f);
struct inode *ext2_creat(const char *path, int mode, struct dentry *dir);
char *ext2_readlink(struct file *ino);
void ext2_close(struct inode *ino);
struct inode *ext2_mknod(const char *name, mode_t mode, dev_t dev, struct dentry *dir);
struct inode *ext2_mkdir(const char *name, mode_t mode, struct dentry *dir);
int ext2_link_fops(struct file *target, const char *name, struct dentry *dir);
int ext2_fallocate(int mode, off_t off, off_t len, struct file *f);
int ext2_ftruncate(size_t len, struct file *f);
ssize_t ext2_readpage(struct page *page, size_t off, struct inode *ino);
ssize_t ext2_writepage(struct page *page, size_t off, struct inode *ino);
int ext2_prepare_write(inode *ino, struct page *page, size_t page_off, size_t offset, size_t len);
int ext2_link(struct inode *target, const char *name, struct inode *dir);
inode *ext2_symlink(const char *name, const char *dest, dentry *dir);

struct file_ops ext2_ops = {.open = ext2_open,
                            .close = ext2_close,
                            .getdirent = ext2_getdirent,
                            .creat = ext2_creat,
                            .link = ext2_link_fops,
                            .symlink = ext2_symlink,
                            .ftruncate = ext2_ftruncate,
                            .mkdir = ext2_mkdir,
                            .mknod = ext2_mknod,
                            .readlink = ext2_readlink,
                            .unlink = ext2_unlink,
                            .fallocate = ext2_fallocate,
                            .readpage = ext2_readpage,
                            .writepage = ext2_writepage,
                            .prepare_write = ext2_prepare_write};

void ext2_delete_inode(struct inode *inode_, uint32_t inum, struct ext2_superblock *fs)
{
    struct ext2_inode *inode = ext2_get_inode_from_node(inode_);

    inode->i_dtime = clock_get_posix_time();
    ext2_free_inode_space(inode_, fs);

    inode->i_links = 0;
    fs->update_inode(inode, inum);

    uint32_t block_group = (inum - 1) / fs->inodes_per_block_group;

    if (S_ISDIR(inode->i_mode))
        fs->block_groups[block_group].dec_used_dirs();

    fs->free_inode(inum);
}

void ext2_close(struct inode *vfs_ino)
{
    struct ext2_inode *inode = ext2_get_inode_from_node(vfs_ino);

    /* TODO: It would be better, cache-wise and memory allocator-wise if we
     * had ext2_inode incorporate a struct inode inside it, and have everything in the same
     * location.
     * TODO: We're also storing a lot of redudant info in ext2_inode(we already have most stuff in
     * the regular struct inode).
     */
    free(inode);
}

ssize_t ext2_writepage(page *page, size_t off, inode *ino)
{
    auto buf = block_buf_from_page(page);
    auto sb = ext2_superblock_from_inode(ino);

    assert(buf != nullptr);

    while (buf)
    {
        page_iov v[1];
        v->length = buf->block_size;
        v->page = buf->this_page;
        v->page_off = buf->page_off;

#if 0
		printk("Writing to block %lu\n", buf->block_nr);
#endif

        if (sb_write_bio(sb, v, 1, buf->block_nr) < 0)
        {
            sb->error("Error writing back page");
            return -EIO;
        }

        buf = buf->next;
    }

    return PAGE_SIZE;
}

ssize_t ext2_readpage(struct page *page, size_t off, struct inode *ino)
{
    bool is_buffer = page->flags & PAGE_FLAG_BUFFER;

    assert(is_buffer == true);

    auto raw_inode = ext2_get_inode_from_node(ino);
    auto sb = ext2_superblock_from_inode(ino);
    auto nr_blocks = PAGE_SIZE / sb->block_size;
    auto base_block_index = off / sb->block_size;

    auto curr_off = 0;

    for (size_t i = 0; i < nr_blocks; i++)
    {
        struct block_buf *b = nullptr;
        if (is_buffer && !(b = page_add_blockbuf(page, curr_off)))
        {
            page_destroy_block_bufs(page);
            return -ENOMEM;
        }

        auto res = ext2_get_block_from_inode(raw_inode, base_block_index + i, sb);
        if (res.has_error())
        {
            page_destroy_block_bufs(page);
            return -ENOMEM;
        }

        auto block = res.value();
        if (block != EXT2_ERR_INV_BLOCK)
        {
            /* TODO: Coalesce reads */
            page_iov v[1];
            v->page = page;
            v->length = sb->block_size;
            v->page_off = curr_off;

            if (sb_read_bio(sb, v, 1, block) < 0)
            {
                page_destroy_block_bufs(page);
                return -EIO;
            }
        }
        else
        {
            // Zero the block, since it's a hole
            memset((char *) PAGE_TO_VIRT(page) + curr_off, 0, sb->block_size);
        }

        if (is_buffer)
        {
            b->block_nr = res.value();
            b->block_size = sb->block_size;
            b->dev = sb->s_bdev;
        }

        curr_off += sb->block_size;
    }

    return min(PAGE_SIZE, ino->i_size - off);
}

struct ext2_inode_info *ext2_cache_inode_info(struct inode *ino, struct ext2_inode *fs_ino)
{
{'fraction_non_alphanumeric': 0.11002530320216386, 'fraction_numerical': 0.013262368030712852, 'mean_word_length': 2.38211861906167, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 104, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7124979', 'n_tokens_mistral': 3989, 'n_tokens_neox': 3905, 'n_words': 983}