layout: post
title: "The debug file system"
subtitle: "It's still in the kernel!"
tags: [osdev, filesystems]

This far in my vfs rewrite, I've made a [vfs mount tree](/blog/2013/12/Virtual-File-System2/) and written some [file operation syscalls](/blog/2013/12/VFS-syscalls/). Now it's time to actually use this, by writing a filesystem that can be mounted and then manipulated through the syscall interface.

The filesystem I want to start with is what I call the `debug` file.

It is a single file, which mounts to `/` for now (later `/dev/debug`)
and cannot be read from. Writing to it will print the written text on
the screen.

Three functions are needed for newlib `fprintf` to be able to write to
it: `stat`, `isatty` and `write`. Those are all rather simple, since
they won't need to keep track of which file we're referencing.

    :::c
    uint32_t debug_stat(INODE node, struct stat *st)
    {
        memset(st, 0, sizeof(struct stat));
        st->st_mode = S_IFCHR;
        return 0;
    }

I don't care much about the stat for the debug file. Maybe I'll add some
creation time or so later...

    :::c
    uint32_t debug_isatty(INODE node)
    {
        return 1;
    }

The debug output is a terminal.

    :::c
    uint32_t debug_write(INODE node, void *buffer, uint32_t size, uint32_t offset)
    {
        char *buf = calloc(size + 1, 1);
        memcpy(buf, buffer, size);
        kdbg_puts(buf);
        free(buf);
        return size;
    }

`kdbg_puts` is a function I wrote
[a long time ago](/blog/2012/06/Kernel-Debug-Functions/) which prints a
string to the screen. The first two lines are just to make sure that the
data is null-terminated.

With this, I can define a driver for the "debug device":

    :::c
    vfs_driver_t debug_driver =
    {
        0, // open
        0, // close
        0, // read
        debug_write, // write
        0, // link
        0, // unlink
        debug_stat, // stat
        debug_isatty, // isatty
        0, // mkdir
        0, // readdir
        0 // finddir
    };

And then write a function to setup the device:

    :::c
    INODE debug_dev_init()
    {
        INODE node = calloc(1, sizeof(vfs_node_t));
        strcpy(node->name, "debug");
        node->d = &debug_driver;
        node->type = FS_CHARDEV;
        return node;
    }

Then, to activate it, all I need to do is add

    :::c
    vfs_init();
    vfs_mount("/", debug_dev_init());

in my kernel boot code. After that I can use the standard library
functions:

    :::c
    FILE *dbg = fopen("/", "w");
    fprintf(dbg, "Hello, world!\n");

or even:

    :::c
    fopen("/", "r");
    fopen("/", "w");
    printf("Hello, world!\n");

That's it for this time. Next time, we'll do some piping!

