Update markdown and tabs and stuff

This commit is contained in:
2016-10-22 17:07:50 +02:00
parent 67e817490e
commit a27daafa0a
39 changed files with 2249 additions and 2139 deletions

View File

@@ -1,7 +1,7 @@
layout: post
title: "TAR Filesystem"
subtitle: "Almost a useful system"
tags: [osdev]
tags: [osdev, filesystems]
It's finally time to implement an actuall filesystem, and all the hard
work with the VFS framework will pay off.
@@ -19,25 +19,26 @@ is preceded by a data block in human-readable format.
In the POSIX standard of 1988, the data block has the following format:
typedef struct
{
unsigned char name[100];
unsigned char mode[8];
unsigned char uid[8];
unsigned char gid[8];
unsigned char size[12];
unsigned char mtime[12];
unsigned char checksum[8];
unsigned char type[1];
unsigned char linkname[100];
unsigned char tar_indicator[6];
unsigned char tar_version[2];
unsigned char owner[32];
unsigned char group[32];
unsigned char device_major[8];
unsigned char device_minor[8];
unsigned char prefix[155];
}__attribute__((packed)) tar_header_t;
:::c
typedef struct
{
unsigned char name[100];
unsigned char mode[8];
unsigned char uid[8];
unsigned char gid[8];
unsigned char size[12];
unsigned char mtime[12];
unsigned char checksum[8];
unsigned char type[1];
unsigned char linkname[100];
unsigned char tar_indicator[6];
unsigned char tar_version[2];
unsigned char owner[32];
unsigned char group[32];
unsigned char device_major[8];
unsigned char device_minor[8];
unsigned char prefix[155];
}__attribute__((packed)) tar_header_t;
The data block is 500 bytes long, but the data starts 512 (one standard
disk sector) bytes after the start of the data block
@@ -56,52 +57,53 @@ are loaded into ram and you might want to access them randomly.
Instead, I reshape the file list into a file tree:
tree_t *build_tar_tree(tar_header_t *tar)
{
...
while(tar->name[0])
{
tartree_add_node(tree, tar, (char *)&tar->name);
uint32_t size;
sscanf((char *)&tar->size, "%o", &size);
tar = (tar_header_t *)((size_t)tar + size + 512);
if((size_t)tar % 512)
tar = (tar_header_t *)((uint32_t)tar + 512 - ((uint32_t)tar%512))
}
...
}
void tartree_add_node(tree_t *tree, tar_header_t *tar, char *path)
{
...
tree_node_t *node = tree->root;
char *p;
for(p = strtok(path, "/"); p; p = strtok(NULL, "/"))
{
int found = 0;
list_t *l;
for_each_in_list(&node->children)
{
...
if(!strcmp(entry->name, p))
{
found = 1;
node = tn;
break;
}
}
if(!found)
{
...
tarfs_entry_t *n = malloc(sizeof(tar_entry_t));
n->name = strdup(p);
n->tar = tar;
...
tree_make_child(node, new);
node = new;
}
}
}
:::c
tree_t *build_tar_tree(tar_header_t *tar)
{
...
while(tar->name[0])
{
tartree_add_node(tree, tar, (char *)&tar->name);
uint32_t size;
sscanf((char *)&tar->size, "%o", &size);
tar = (tar_header_t *)((size_t)tar + size + 512);
if((size_t)tar % 512)
tar = (tar_header_t *)((uint32_t)tar + 512 - ((uint32_t)tar%512))
}
...
}
void tartree_add_node(tree_t *tree, tar_header_t *tar, char *path)
{
...
tree_node_t *node = tree->root;
char *p;
for(p = strtok(path, "/"); p; p = strtok(NULL, "/"))
{
int found = 0;
list_t *l;
for_each_in_list(&node->children)
{
...
if(!strcmp(entry->name, p))
{
found = 1;
node = tn;
break;
}
}
if(!found)
{
...
tarfs_entry_t *n = malloc(sizeof(tar_entry_t));
n->name = strdup(p);
n->tar = tar;
...
tree_make_child(node, new);
node = new;
}
}
}
Note that this assumes that the files and directories of the tar archive
are in top-down order, e.g. that `/bin` is before `/bin/echo`, otherwise
@@ -112,24 +114,26 @@ strange.
The tarfs driver makes use of the data field in the vfs node to store
the tar tree.
INODE tarfs_init(tar_header_t *tar)
{
vfs_node_t *node = calloc(1, sizeof(vfs_node_t));
strcpy(node->name, "tarfs");
node->d = &tarfs_driver;
node->type = FS_DIRECTORY;
tree_t *tar_tree = build_tar_tree(tar);
node->data = tar_tree->root;
free(tar_tree);
return node;
}
:::c
INODE tarfs_init(tar_header_t *tar)
{
vfs_node_t *node = calloc(1, sizeof(vfs_node_t));
strcpy(node->name, "tarfs");
node->d = &tarfs_driver;
node->type = FS_DIRECTORY;
tree_t *tar_tree = build_tar_tree(tar);
node->data = tar_tree->root;
free(tar_tree);
return node;
}
I then add the following to my kernel initialization function:
tar_header_t *tarfs_location = assert_higher((tar_header_t *)mods[0].mod_start);
vfs_mount("/", tarfs_init(tarfs_location));
:::c
tar_header_t *tarfs_location = assert_higher((tar_header_t *)mods[0].mod_start);
vfs_mount("/", tarfs_init(tarfs_location));
where `mods` is the multiboot modules table as loaded by grub.
@@ -137,30 +141,31 @@ where `mods` is the multiboot modules table as loaded by grub.
Finally, the tarfs driver functions. For now I only need to implement
`read()` and `finddir()`.
INODE tar_finddir(INODE dir, const char *name)
{
tree_node_t *tn = (tree_node_t *)dir->data;
list_t *l;
for_each_in_list(&tn->children, l)
{
tree_node_t *cn = list_entry(l, tree_node_t, siblings);
...
if(!strcmp(entry->name, name)
{
INODE node = calloc(1, sizeof(vfs_node_t));
strcpy(node->name, entry->name);
node->d = &tarfs_driver;
node->data = (void *)cn;
sscanf((char *)&entry->tar->size, "%o", &node->length);
if(entry->tar->type[0] == TAR_TYPE_DIR)
node->type = FS_DIRECTORY;
else
node->type = FS_FILE;
return node;
}
}
return 0;
}
:::c
INODE tar_finddir(INODE dir, const char *name)
{
tree_node_t *tn = (tree_node_t *)dir->data;
list_t *l;
for_each_in_list(&tn->children, l)
{
tree_node_t *cn = list_entry(l, tree_node_t, siblings);
...
if(!strcmp(entry->name, name)
{
INODE node = calloc(1, sizeof(vfs_node_t));
strcpy(node->name, entry->name);
node->d = &tarfs_driver;
node->data = (void *)cn;
sscanf((char *)&entry->tar->size, "%o", &node->length);
if(entry->tar->type[0] == TAR_TYPE_DIR)
node->type = FS_DIRECTORY;
else
node->type = FS_FILE;
return node;
}
}
return 0;
}
`Finddir` allocates space for a new inode for each file that is searched
for. It's up to the caller to free the inode when it's done with it.
@@ -170,39 +175,42 @@ it at a few places... Someday I will clean up all my memory leaks.
Anyway, `finddir` also finds the right node in the tarfs tree and puts
it in the `data` field of the inode.
uint32_t read_tar(INODE node, void *buffer, uint32_t size, uint32_t offset)
{
tree_node_t *tn = (tree_node_t *)node->data;
tarfs_entry_t *te = (tarfs_entry_t *)tn->item;
tar_header_t *tar = te->tar;
uint32_t tsz;
sscanf((char *)&tar->size, "%o", &tsz);
if(offset > tsz) return EOF;
if((size + offset) > tsz) size = tsz - offset;
offset = offset + (uint32_t)tar + 512;
memcpy(buffer, (void *)offset, size);
if(size == tsz - offset)
((char *)buffer)[size] = EOF;
return size;
}
:::c
uint32_t read_tar(INODE node, void *buffer, uint32_t size, uint32_t offset)
{
tree_node_t *tn = (tree_node_t *)node->data;
tarfs_entry_t *te = (tarfs_entry_t *)tn->item;
tar_header_t *tar = te->tar;
uint32_t tsz;
sscanf((char *)&tar->size, "%o", &tsz);
if(offset > tsz) return EOF;
if((size + offset) > tsz) size = tsz - offset;
offset = offset + (uint32_t)tar + 512;
memcpy(buffer, (void *)offset, size);
if(size == tsz - offset)
((char *)buffer)[size] = EOF;
return size;
}
###Using it
Now, all I need to do in order to make read-only files accessible to my
kernel is put them in a directory and run
$ tar -cf tarfs.tar tarfs/*
:::bash
$ tar -cf tarfs.tar tarfs/*
and then make sure `tarfs.tar` is loaded as a multiboot module by qemu
$ qemu-system-i386 -kernel kernel -initrd tarfs.tar
:::bash
$ qemu-system-i386 -kernel kernel -initrd tarfs.tar
or by adding a line to the grub `menu.lst` file.
module /boot/tarfs.tar
module /boot/tarfs.tar
###Final note
While writing this post, I got back to polishing this code and added