~swisschili/bluejay

480f17691d1c846ea8d6fc1d6b971d6fcd92503c — swissChili 9 months ago d98781b
Add ext2 VFS implementation
M .dir-locals.el => .dir-locals.el +4 -2
@@ 1,3 1,5 @@
((nil . ((eval
		  . (setq-local flycheck-clang-args
						(jmk-arguments-for (expand-file-name buffer-file-name)))))))
		  . (setq-local flycheck-clang-include-path
						(jmk-includes-for (expand-file-name buffer-file-name))
						flycheck-clang-args
						(jmk-other-flags-for (expand-file-name buffer-file-name)))))))

M include/kernel/dri/fs/ext2/ext2.h => include/kernel/dri/fs/ext2/ext2.h +19 -2
@@ 264,6 264,11 @@ enum
	EXT2_FT_SYMLINK,
};

inline uint ext2_block_size(struct ext2_superblock *sb)
{
	return 1024 << sb->block_size_shift;
}

/// Read a file system block (0-indexed), if necessary multiple disk
/// blocks will be read automatically
void ext2_read_block(struct ext2_superblock *sb, void *buffer,


@@ 300,9 305,19 @@ bool ext2_set_inode(struct ext2_superblock *sb, uint number,
					struct ext2_inode *inode);

/// Load a block group descriptor for a certain block group
struct ext2_block_group_descriptor ext2_load_block_group_descriptor(
struct ext2_block_group_descriptor ext2_load_bgd(
	struct ext2_superblock *sb, uint block_group);

/// Write a block group descriptor for a certain block group
void ext2_write_bgd(struct ext2_superblock *sb, uint block_group,
					struct ext2_block_group_descriptor *d);

/// Load or write a BGD
void ext2_load_or_write_bgd(struct ext2_superblock *sb,
							uint block_group,
							struct ext2_block_group_descriptor *d,
							bool set);

/// List the contents of a directory dir. Calls `cb` for each item. If
/// `dir` is not a directory, returns false. Otherwise returns true.
/// if cb returns true, ls will continue. Otherwise it will stop.


@@ 324,7 339,9 @@ bool ext2_read_inode_block(struct ext2_superblock *sb,
bool ext2_write_inode_block(struct ext2_superblock *sb, struct ext2_inode *dir,
							void *buffer, uint block);

ssize_t ext2_read_inode(struct ext2_superblock *sb, struct ext2_inode *inode, void *buffer, ssize_t size);
ssize_t ext2_read_inode(struct ext2_superblock *sb,
						struct ext2_inode *inode, void *buffer,
						ssize_t size);

/**
 * @brief Set a block in a bitmap

A include/kernel/dri/fs/ext2/ext2_vfs.h => include/kernel/dri/fs/ext2/ext2_vfs.h +10 -0
@@ 0,0 1,10 @@
#pragma once

#include "ext2.h"
#include <vfs.h>

// VFS specific EXT2 functions

/// ext2 inode -> vfs node
struct fs_node *ext2_inode2vfs(struct ext2_superblock *sb, uint inode,
							   char *name, uint name_len);

M include/kernel/vfs.h => include/kernel/vfs.h +12 -3
@@ 4,8 4,12 @@

struct fs_vtable;

#define FS_MAX_NAME_LEN 128

struct fs_node
{
	/** length of file name */
	uint name_len;
	/** file name */
	char name[128];
	/** identifier */


@@ 21,7 25,11 @@ struct fs_node
	/** size in bytes */
	size_t size;
	/** reserved for driver */
	uint dri_res;
	union
	{
		uint dri_res_i;
		void *dri_res_p;
	};

	struct fs_vtable *vtable;



@@ 33,6 41,7 @@ struct fs_dirent
{
	// EXT2 supports up to 256 byte file names, so we will do the same
	char name[256];
	uint name_len;
	uint inode;
};



@@ 42,7 51,7 @@ typedef void (*fs_open_t)(struct fs_node *node);
typedef void (*fs_close_t)(struct fs_node *node);

typedef bool (*fs_readdir_t)(struct fs_node *node, uint index, struct fs_dirent *dirent);
typedef struct fs_node *(*fs_finddir_t)(struct fs_node *node, char *name);
typedef struct fs_node *(*fs_finddir_t)(struct fs_node *node, char *name, uint name_len);

struct fs_vtable
{


@@ 78,7 87,7 @@ void fs_open(struct fs_node *node);
void fs_close(struct fs_node *node);

bool fs_readdir(struct fs_node *node, uint index, struct fs_dirent *out);
struct fs_node *fs_finddir(struct fs_node *node, char *name);
struct fs_node *fs_finddir(struct fs_node *node, char *name, uint name_len);

/* Returns the following error codes:
 * 0: success

M share/jmk/dir-locals.el => share/jmk/dir-locals.el +4 -2
@@ 1,3 1,5 @@
((nil . ((eval
		  . (setq-local flycheck-clang-args
						(jmk-arguments-for (expand-file-name buffer-file-name)))))))
		  . (setq-local flycheck-clang-include-path
						(jmk-includes-for (expand-file-name buffer-file-name))
						flycheck-clang-args
						(jmk-other-flags-for (expand-file-name buffer-file-name)))))))

M share/jmk/jmk-flycheck.el => share/jmk/jmk-flycheck.el +8 -0
@@ 35,3 35,11 @@ Returns nil if nothing can be found"
	(message "includes: %s" names)
	names))

(defun jmk-other-flags-for (p)
  (let* ((args (jmk-arguments-for p))
		 (not-includes (cl-remove-if-not (lambda (arg)
										   (string-prefix-p "-I" arg))
					   args))
		 (stripped (mapcar #'string-trim not-includes)))
	stripped))


M src/kernel/dri/fs/ext2/Jmk => src/kernel/dri/fs/ext2/Jmk +3 -1
@@ 1,3 1,5 @@
# -*- mode:makefile -*-

init(ext2, ext2.a)

preset(freestanding)


@@ 10,7 12,7 @@ archetype(c)

CFLAGS += -I$(ROOT)/include/kernel

OBJECTS = ext2.o
OBJECTS = ext2.o ext2_vfs.o

type(static_lib)


M src/kernel/dri/fs/ext2/ext2.c => src/kernel/dri/fs/ext2/ext2.c +53 -11
@@ 19,11 19,6 @@ const uchar ext2_s_to_ft[] =
};
#undef F

inline uint ext2_block_size(struct ext2_superblock *sb)
{
	return 1024 << sb->block_size_shift;
}

void ext2_read_block(struct ext2_superblock *sb, void *buffer, uint block)
{
	uint block_size = ext2_block_size(sb) / 512;


@@ 68,9 63,26 @@ uint ext2_num_block_groups(struct ext2_superblock *sb)
	}
}

struct ext2_block_group_descriptor ext2_load_block_group_descriptor(
void ext2_write_bgd(struct ext2_superblock *sb, uint block_group,
					struct ext2_block_group_descriptor *d)
{
	ext2_load_or_write_bgd(sb, block_group, d, true);
}

struct ext2_block_group_descriptor ext2_load_bgd(
	struct ext2_superblock *sb, uint block_group)
{
	struct ext2_block_group_descriptor bgd;
	ext2_load_or_write_bgd(sb, block_group, &bgd, false);

	return bgd;
}

void ext2_load_or_write_bgd(struct ext2_superblock *sb,
							uint block_group,
							struct ext2_block_group_descriptor *d,
							bool set)
{
	/**
	 * The BGDT (not to be confused with the GDT) is located the block after the
	 * superblock. On any block size EXCEPT 1024 (the minimum, remember that the


@@ 95,9 107,17 @@ struct ext2_block_group_descriptor ext2_load_block_group_descriptor(

	uint lba = (block_size / 512) * bgdt_block + hd_page;

	ata_pio_read_sectors(&descriptors, lba, 1);
	ata_pio_read_sectors(descriptors, lba, 1);

	return descriptors[bgd_offset];
	if (set)
	{
		descriptors[bgd_offset] = *d;
		ata_pio_write_sectors(lba, 1, (ushort *)descriptors);
	}
	else
	{
		*d = descriptors[bgd_offset];
	}
}

static bool print_entry(uint inode, const char *name, uint l, void *sb)


@@ 194,7 214,7 @@ bool ext2_get_or_set_inode(struct ext2_superblock *sb, uint number,

	// Load this from the block group descriptor table
	struct ext2_block_group_descriptor descriptor =
		ext2_load_block_group_descriptor(sb, block_group);
		ext2_load_bgd(sb, block_group);

	// We need to figure out what FS block the inode is on, we know how many
	// inodes there are total in this BGD and the number per page, so this is


@@ 579,7 599,7 @@ uint ext2_first_free_inode(struct ext2_superblock *sb)
	for (int bg_num = 0; bg_num < num_block_groups; bg_num++)
	{
		struct ext2_block_group_descriptor bgd =
			ext2_load_block_group_descriptor(sb, 0);
			ext2_load_bgd(sb, 0);

		const uint block_size = ext2_block_size(sb);
		// + 1 because we need to round up (ie 1025 for 1024 size blocks will


@@ 609,7 629,7 @@ uint ext2_first_free_block(struct ext2_superblock *sb)
	for (int bg_num = 0; bg_num < num_block_groups; bg_num++)
	{
		struct ext2_block_group_descriptor bgd =
			ext2_load_block_group_descriptor(sb, 0);
			ext2_load_bgd(sb, 0);

		const uint block_size = ext2_block_size(sb);
		// + 1 because we need to round up (ie 1025 for 1024 size blocks will


@@ 706,3 726,25 @@ uint ext2_dir_find(struct ext2_superblock *sb, struct ext2_inode *dir,

	return d.inode;
}

uint ext2_alloc_new_block(struct ext2_superblock *sb,
						  uint block_group)
{
	struct ext2_block_group_descriptor bgd =
		ext2_load_bgd(sb, block_group);

	if (bgd.unallocated_blocks == 0)
		// TODO: handle out of blocks
		return ext2_alloc_new_block(sb, block_group + 1);

	// We can safely pass ~0 here as long as the FS is well formed
	// because we know there is at least 1 free block
	uint block = ext2_first_zero_bit(sb, bgd.block_bitmap, ~0, 0);

	ext2_set_in_bitmap(sb, bgd.block_bitmap, block, true);
	bgd.unallocated_blocks--;

	ext2_write_bgd(sb, block_group, &bgd);

	return block;
}

A src/kernel/dri/fs/ext2/ext2_vfs.c => src/kernel/dri/fs/ext2/ext2_vfs.c +281 -0
@@ 0,0 1,281 @@
#include <dri/fs/ext2/ext2.h>
#include <dri/fs/ext2/ext2_vfs.h>
#include <sync.h>
#include <alloc.h>
#include <log.h>
#include <io.h>

struct ext2_fs_dirent
{
	struct fs_node *node;
	char name[256];
	uint name_len;
	uint inode;

	struct ext2_fs_dirent *next;
};

struct ext2_fs_data
{
	struct ext2_superblock *sb;

	/// Cached inode
	struct ext2_inode *in;

	/// Reference count for the inode cache. Once this hits 0 we free
	/// in. Only used for **files**, directories **always** cache
	/// their inode.
	uint rc;

	semaphore_t lock;

	/// Entries (if this is a directory)
	struct ext2_fs_dirent *dirent;
};

uint ext2_file_read(struct fs_node *node, size_t ofs, size_t size,
					uchar *buffer)
{
	struct ext2_fs_data *d = node->dri_res_p;
	const uint block_size = ext2_block_size(d->sb);

	// temporary buffer
	uchar *tmp = malloc(block_size);

	uint num_read = 0;

	if (!buffer)
	{
		kprintf(WARN "ext2_file_read out of memory!\n");
		return 0;
	}

	uint block_start = ofs / block_size;
	uint block_offset = ofs % block_size;
	int to_read;

	do
	{
		// How much from this block to read
		to_read = MIN(block_size - block_offset, size);
		ext2_read_inode_block(d->sb, d->in, tmp, block_start);

		// Copy that to the user buffer
		memcpy(buffer, tmp + block_offset, to_read);
		buffer += to_read;
		size -= to_read;
		num_read += to_read;

		// Move to the next block
		block_start++;
		block_offset = 0;
	}
	while (to_read > 0);

	free(tmp);
	return num_read;
}

void ext2_file_open(struct fs_node *node)
{
	struct ext2_fs_data *d = node->dri_res_p;

	sm_wait(d->lock);

	if (d->rc++ == 0)
	{
		d->in = malloc(sizeof(struct ext2_inode));

		if (d->in == NULL)
		{
			// lol
			kpanic("ext2_file_open: ENOMEM");
		}

		if (!ext2_find_inode(d->sb, node->inode, d->in))
		{
			kprintf(ERROR "ext2_file_open: can't find inode %d\n",
					node->inode);

			free(d->in);
			goto done;
		}
	}

done:
	sm_signal(d->lock);
}

void ext2_file_close(struct fs_node *node)
{
	struct ext2_fs_data *d = node->dri_res_p;

	sm_wait(d->lock);

	if (--d->rc == 0)
	{
		free(d->in);
		d->in = NULL;
	}

	sm_signal(d->lock);
}

struct fs_vtable ext2_file_vfs_vt =
{
	.read = ext2_file_read,
	.write = NULL,
	.open = ext2_file_open,
	.close = ext2_file_close,
	.readdir = NULL,
	.finddir = NULL,
};

bool ext2_dir_readdir(struct fs_node *node, uint index, struct fs_dirent *dirent)
{
	struct ext2_fs_data *d = node->dri_res_p;
	struct ext2_fs_dirent *dent = d->dirent;

	uint i;

	for (i = 0; i < index && dent; i++, dent = dent->next)
	{
	}

	if (i == index)
	{
		memcpy(dirent->name, dent->name, 256);
		dirent->name_len = dent->name_len;
		dirent->inode = dent->inode;

		return true;
	}

	return false;
}

struct fs_node *ext2_dir_finddir(struct fs_node *node, char *name, uint name_len)
{
	name_len = MIN(name_len, 256);
	struct ext2_fs_data *d = node->dri_res_p;
	struct ext2_fs_dirent *dent = d->dirent;

	for (; dent; dent = dent->next)
	{
		if (strncmp(dent->name, name, name_len) == 0)
		{
			return dent->node;
		}
	}

	return NULL;
}

struct fs_vtable ext2_dir_vfs_vt =
{
	.read = NULL,
	.write = NULL,
	.open = NULL,
	.close = NULL,
	.readdir = ext2_dir_readdir,
	.finddir = ext2_dir_finddir,
};

struct ext2_fs_dirent_to_fs_data
{
	struct ext2_fs_data *d;
	struct ext2_fs_dirent *last;
	struct ext2_superblock *sb;
};

static bool ext2_dirent_to_fs_node_cb(uint inode, const char *name,
							   uint name_len, void *data)
{
	struct ext2_fs_dirent_to_fs_data *d = data;

	struct ext2_fs_dirent *dent =
		malloc(sizeof(struct ext2_fs_dirent));

	dent->node = ext2_inode2vfs(d->sb, inode, (char *)name, name_len);
	dent->name_len = name_len;
	memcpy(dent->name, name, MIN(name_len, 256));
	dent->inode = inode;

	if (d->last)
	{
		d->last->next = dent;
		d->last = dent;
	}
	else
	{
		d->last = d->d->dirent = dent;
	}

	return true;
}

struct fs_node *ext2_inode2vfs(struct ext2_superblock *sb, uint inode,
							   char *name, uint name_len)
{
	struct ext2_inode in;

	if (!ext2_find_inode(sb, inode, &in))
	{
		// Something has gone terribly wrong!
		return NULL;
	}

	struct ext2_fs_data *d = malloc(sizeof(struct ext2_fs_data));

	d->sb = sb;
	d->rc = 0;
	d->lock = sm_new();
	d->in = NULL;
	d->dirent = NULL;

	struct fs_node *n = malloc(sizeof(struct fs_node));

	n->name_len = MIN(name_len, FS_MAX_NAME_LEN);
	memcpy(n->name, name, n->name_len);
	n->inode = inode;
	n->dri_res_p = d;

	n->mask = in.mode & 0xfff;
	n->gid = in.gid;
	n->uid = in.uid;
	n->size = in.size;

	switch (in.mode & EXT2_F_TYPE)
	{
	case EXT2_S_IFREG:
	{
		n->flags = FS_FILE;
		n->vtable = &ext2_file_vfs_vt;

		break;
	}

	case EXT2_S_IFDIR:
	{
		n->flags = FS_DIRECTORY;
		n->vtable = &ext2_dir_vfs_vt;

		struct ext2_fs_dirent_to_fs_data data;
		data.d = d;
		data.last = NULL;

		ext2_dir_ls(sb, &in, ext2_dirent_to_fs_node_cb, &data);

		break;
	}

	default:
	{
		kprintf(ERROR "ext2_inode2vfs: unimplemented for dir type%d\n",
				EXT2_S_TO_FT(in.mode));
		kpanic("Unimplemented");
	}
	}

	return n;
}

M src/kernel/vfs.c => src/kernel/vfs.c +6 -6
@@ 45,13 45,13 @@ bool fs_readdir(struct fs_node *node, uint index, struct fs_dirent *out)
	return node->vtable->readdir(node, index, out);
}

struct fs_node *fs_finddir(struct fs_node *node, char *name)
struct fs_node *fs_finddir(struct fs_node *node, char *name, uint name_len)
{
	if (!node || !node->vtable || !node->vtable->finddir ||
		(node->flags & 7) != FS_DIRECTORY)
		return NULL;

	return node->vtable->finddir(node, name);
	return node->vtable->finddir(node, name, name_len);
}

bool root_readdir(struct fs_node *node, uint index, struct fs_dirent *out)


@@ 68,9 68,9 @@ bool root_readdir(struct fs_node *node, uint index, struct fs_dirent *out)
		return false;
}

struct fs_node *root_finddir(struct fs_node *node, char *name)
struct fs_node *root_finddir(struct fs_node *node, char *name, uint name_len)
{
	if (!strcmp(name, "dev"))
	if (!strncmp(name, "dev", name_len))
	{
		return &dev;
	}


@@ 90,12 90,12 @@ bool dev_readdir(struct fs_node *node, uint index, struct fs_dirent *out)
		return false;
}

struct fs_node *dev_finddir(struct fs_node *node, char *name)
struct fs_node *dev_finddir(struct fs_node *node, char *name, uint name_len)
{
	if (node != &dev)
		return NULL;

	if (!strcmp(name, "initrd"))
	if (!strncmp(name, "initrd", MIN(name_len, FS_MAX_NAME_LEN)))
	{
		return &initrd;
	}

M src/kernel/vfs_initrd.c => src/kernel/vfs_initrd.c +9 -4
@@ 31,15 31,20 @@ bool initrd_readdir(struct fs_node *node, uint i, struct fs_dirent *out)
	return true;
}

struct fs_node *initrd_finddir(struct fs_node *node, char *name)
struct fs_node *initrd_finddir(struct fs_node *node, char *name,
							   uint name_len)
{
	if (node != &real_initrd)
		return NULL;

	for (int i = 0; i < num_initrd_files; i++)
	{
		if (strcmp(entries[i].name, name) == 0)
		if (strncmp(entries[i].name, name,
					MIN(FS_MAX_NAME_LEN, name_len))
			== 0)
		{
			return &initrd_content[i];
		}
	}
	return NULL;
}


@@ 58,7 63,7 @@ uint initrd_read(struct fs_node *node, size_t offset, size_t size, uchar *buffer
		return 0;

	size = MIN(size, node->size - offset);
	memcpy(buffer, entries[node->dri_res].data, size);
	memcpy(buffer, entries[node->dri_res_i].data, size);

	return size;
}


@@ 104,7 109,7 @@ void init_initrd_vfs(uchar *data)
		entries[i].size = f->size;

		initrd_content[i] = (struct fs_node){
			.dri_res = i,
			.dri_res_i = i,
			.flags = FS_FILE,
			.mask = 0b101001001,
			.uid = 0,