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: "Virtual File System 2"
subtitle: "for real this time."
tags: [osdev]
tags: [osdev, filesystems]
Once again, several months have passed since I wrote anything here.
I also worked very little on the kernel during this time, though. So no
@@ -33,25 +33,26 @@ generated on-the-fly by the kernel, network connections(?) etc.
Further, I want the VFS to be independent of any disk file system, e.g.
the VFS shouldn't have user-group-other read-write-execute tuples just
because it's designed with ext2 in mind. It might have those tuples -
because it's designed with ext2 in mind. It might have those tuples -
I haven't decided yet - but it it does it won't be because ext2 uses
them. Nor will the VFS be designed with ext2 or any other disk file
system in mind.
The VFS should offer the functions
open() // Open or create a file
close() // Close a file
read() // Read data from opened file
write() // Write data to opened file
move() // Move a file or directory
link() // Put a file or directory in path tree
unlink() // Remove a file or directory from path tree
stat() // Get more info about a file or directory
isatty() // Returns true if the file is a terminal
mkdir() // Create a directory
readdir() // Get a directory entry
finddir() // Find a file by name from a directory
:::c
open() // Open or create a file
close() // Close a file
read() // Read data from opened file
write() // Write data to opened file
move() // Move a file or directory
link() // Put a file or directory in path tree
unlink() // Remove a file or directory from path tree
stat() // Get more info about a file or directory
isatty() // Returns true if the file is a terminal
mkdir() // Create a directory
readdir() // Get a directory entry
finddir() // Find a file by name from a directory
for all files and directories regardless of their underlying device or
driver.
@@ -96,20 +97,18 @@ Example:
A good starting point for the inode structure might be some pointers to
allow it to be placed in a tree, then.
:::c
struct vfs_node_st;
typedef vfs_node_t * INODE;
struct vfs_node_st;
typedef vfs_node_t * INODE;
typedef struct vfs_node_st
{
char name[VFS_NAME_SZ];
INODE parent;
INODE child;
INODE older, younger;
uint32_t type;
} vfs_node_t;
typedef struct vfs_node_st
{
char name[VFS_NAME_SZ];
INODE parent;
INODE child;
INODE older, younger;
uint32_t type;
} vfs_node_t;
This does waste a bit of memory, since most inodes that are used by the
system won't be in the VFS tree, but four `size_t` isn't that much,
@@ -125,113 +124,118 @@ to maintain.
Then, we need a way to keep track of the driver, i.e. the functions
called to access the file. To do this, I define a new struct:
typedef struct vfs_driver_st
{
uint32_t (*open)(INODE, uint32_t);
uint32_t (*close)(INODE);
uint32_t (*read)(INODE, void *, uint32_t, uint32_t);
uint32_t (*write)(INODE, void *, uint32_t, uint32_t);
uint32_t (*link)(INODE, INODE, const char *);
uint32_t (*unlink)(INODE, const char *);
uint32_t (*stat)(INODE, struct stat *st);
uint32_t (*isatty)(INODE);
uint32_t (*mkdir)(INODE, const char *);
dirent_t *(*readdir)(INODE, uint32_t);
INODE (*finddir)(INODE, const char *);
} vfs_driver_t;
:::c
typedef struct vfs_driver_st
{
uint32_t (*open)(INODE, uint32_t);
uint32_t (*close)(INODE);
uint32_t (*read)(INODE, void *, uint32_t, uint32_t);
uint32_t (*write)(INODE, void *, uint32_t, uint32_t);
uint32_t (*link)(INODE, INODE, const char *);
uint32_t (*unlink)(INODE, const char *);
uint32_t (*stat)(INODE, struct stat *st);
uint32_t (*isatty)(INODE);
uint32_t (*mkdir)(INODE, const char *);
dirent_t *(*readdir)(INODE, uint32_t);
INODE (*finddir)(INODE, const char *);
} vfs_driver_t;
and add `vfs_driver_t *d` to the inode struct. I also added a length
value, a void pointer for arbitrary data used by the drivers and a flags
value - also for use by the drivers. The
inode struct now looks like this:
typedef struct vfs_node_st
{
char name[VFS_NAME_SZ];
void *parent;
void *child;
void *older, *younger;
uint32_t type;
vfs_driver_t *d;
void *data;
uint32_t flags;
uint32_t length;
}
:::c
typedef struct vfs_node_st
{
char name[VFS_NAME_SZ];
void *parent;
void *child;
void *older, *younger;
uint32_t type;
vfs_driver_t *d;
void *data;
uint32_t flags;
uint32_t length;
}
###Vfs functions
Next, I create some wrapper functions to call the driver functions.
uint32_t vfs_open(INODE ino, uint32_t mode)
{
if(ino->d->open)
return ino->d->open(ino, mode);
return 0;
}
:::c
uint32_t vfs_open(INODE ino, uint32_t mode)
{
if(ino->d->open)
return ino->d->open(ino, mode);
return 0;
}
and similar for all functions except `readdir` and `finddir` which
contain code to handle `.` and `..` for mount roots.
dirent_t *vfs_readdir(INODE ino, uint32_t num)
{
if(ino->type & FS_MOUNT)
{
if(num == 0)
{
dirent_t *ret = calloc(1, sizeof(dirent_t));
ret->ino = ino;
strcpy(ret->name, ".");
return ret;
} else if(num == 1) {
dirent_t *ret = calloc(1, sizeof(dirent_t));
ret->ino = ino->parent;
strcpy(ret->name, "..");
return ret;
}
}
if(ino->d->readdir)
return ino->d->readdir(ino, num);
return 0;
}
:::c
dirent_t *vfs_readdir(INODE ino, uint32_t num)
{
if(ino->type & FS_MOUNT)
{
if(num == 0)
{
dirent_t *ret = calloc(1, sizeof(dirent_t));
ret->ino = ino;
strcpy(ret->name, ".");
return ret;
} else if(num == 1) {
dirent_t *ret = calloc(1, sizeof(dirent_t));
ret->ino = ino->parent;
strcpy(ret->name, "..");
return ret;
}
}
if(ino->d->readdir)
return ino->d->readdir(ino, num);
return 0;
}
 
INODE vfs_finddir(INODE ino, const char *name)
{
if(ino->type & FS_MOUNT)
{
if(!strcmp(name, "."))
{
return ino;
} else if(!strcmp(name, "..")) {
return ino->parent;
}
}
if(ino->d->finddir)
return ino->d->finddir(ino, name);
if(ino->d->readdir)
{
// Backup solution
int num = 0;
dirent_t *de;
while(1)
{
de = vfs_readdir(ino, num);
if(!de)
return 0;
if(!strcmp(name, de->name))
break;
free(de->name);
free(de);
num++;
}
INODE ret = de->ino;
free(de->name);
free(de);
return ret;
}
return 0;
}
:::c
INODE vfs_finddir(INODE ino, const char *name)
{
if(ino->type & FS_MOUNT)
{
if(!strcmp(name, "."))
{
return ino;
} else if(!strcmp(name, "..")) {
return ino->parent;
}
}
if(ino->d->finddir)
return ino->d->finddir(ino, name);
if(ino->d->readdir)
{
// Backup solution
int num = 0;
dirent_t *de;
while(1)
{
de = vfs_readdir(ino, num);
if(!de)
return 0;
if(!strcmp(name, de->name))
break;
free(de->name);
free(de);
num++;
}
INODE ret = de->ino;
free(de->name);
free(de);
return ret;
}
return 0;
}
Finally, I needed a function for mounting filesystems in the mount tree
and the `namei` function, which can actually be combined since they both
@@ -241,38 +245,39 @@ _Warning:_ Pointer-pointers ahead!
First: a function for traversing the mount tree as far as possible
INODE vfs_find_root(char **path)
{
// Find closest point in mount tree
INODE current = vfs_root;
INODE mount = current;
char *name;
while((name = strsep(path, "/")))
{
current = current->child;
while(current)
{
if(!strcmp(current->name, name))
{
mount = current;
break;
}
current = current->olderyounger;
}
if(!current)
{
if(*path)
{
*path = *path - 1;
*path[0] = '/';
}
*path = name;
break;
}
}
return (INODE)mount;
}
:::c
INODE vfs_find_root(char **path)
{
// Find closest point in mount tree
INODE current = vfs_root;
INODE mount = current;
char *name;
while((name = strsep(path, "/")))
{
current = current->child;
while(current)
{
if(!strcmp(current->name, name))
{
mount = current;
break;
}
current = current->olderyounger;
}
if(!current)
{
if(*path)
{
*path = *path - 1;
*path[0] = '/';
}
*path = name;
break;
}
}
return (INODE)mount;
}
Pretty self explanatory. No? Well, `strsep` is a library function which
picks out one part of the path at a time and also advances the `path`
@@ -284,69 +289,69 @@ The namei/mount function then uses this as a starting point:
INODE vfs_namei_mount(const char *path, INODE root)
{
char *npath = strdup(path);
char *pth = &npath[1];
// Find closest point in mount tree
INODE current = vfs_find_root(&pth);
char *name;
while(current && (name = strsep(&pth, "/")))
{
// Go through the path
INODE next = vfs_finddir(current, name);
if(root)
{
// If we want to mount someting
if(!next)
{
// Create last part of path if it doesn't exist
// But only if it is the last part.
if(pth)
return 0;
next = calloc(1, sizeof(vfs_node_t));
strcpy(next->name, name);
next->type = FS_DIRECTORY;
}
// Add path to mount tree
next->parent = current;
next->older = current->child;
current->child = next;
}
if(!next)
return 0;
if(!current->parent)
free(current);
current = next;
}
free(npath);
if(root && current->type == FS_DIRECTORY)
{
// Replace node in mount tree
root->parent = current->parent;
if(root->parent->child == current)
root->parent->child = root;
root->older = current->older;
if(root->older)
root->older->younger = current;
root->younger = current->younger;
if(root->younger)
root->younger->older = current;
strcpy(root->name, current->name);
root->type = FS_MOUNT;
if(current == vfs_root)
vfs_root = root;
free(current);
}
return current;
}
{: .lang-c}
:::c
INODE vfs_namei_mount(const char *path, INODE root)
{
char *npath = strdup(path);
char *pth = &npath[1];
// Find closest point in mount tree
INODE current = vfs_find_root(&pth);
char *name;
while(current && (name = strsep(&pth, "/")))
{
// Go through the path
INODE next = vfs_finddir(current, name);
if(root)
{
// If we want to mount someting
if(!next)
{
// Create last part of path if it doesn't exist
// But only if it is the last part.
if(pth)
return 0;
next = calloc(1, sizeof(vfs_node_t));
strcpy(next->name, name);
next->type = FS_DIRECTORY;
}
// Add path to mount tree
next->parent = current;
next->older = current->child;
current->child = next;
}
if(!next)
return 0;
if(!current->parent)
free(current);
current = next;
}
free(npath);
if(root && current->type == FS_DIRECTORY)
{
// Replace node in mount tree
root->parent = current->parent;
if(root->parent->child == current)
root->parent->child = root;
root->older = current->older;
if(root->older)
root->older->younger = current;
root->younger = current->younger;
if(root->younger)
root->younger->older = current;
strcpy(root->name, current->name);
root->type = FS_MOUNT;
if(current == vfs_root)
vfs_root = root;
free(current);
}
return current;
}
Note how `pth` is changed by `vfs_find_root()` to only contain the part
of the path that wasn't found. After that, we ask each node for the next
@@ -360,46 +365,47 @@ the final inode is returned.
I also made two simple wrappers for this function:
INODE vfs_namei(const char *path)
{
return vfs_namei_mount(path, 0);
}
INODE vfs_mount(const char *path, INODE root)
{
return vfs_namei_mount(path, root);
}
{: .lang-c}
:::c
INODE vfs_namei(const char *path)
{
return vfs_namei_mount(path, 0);
}
INODE vfs_mount(const char *path, INODE root)
{
return vfs_namei_mount(path, root);
}
And finally, a function for unmounting file systems:
INODE vfs_umount(const char *path)
{
char *npath = strdup(path);
char *pth = &npath[1];
INODE ino = vfs_find_root(&pth);
if(!ino || pth)
{
free(npath);
return 0;
}
if(ino->child)
{
free(npath);
return 0;
} else {
// Remove node from mount tree
if(ino->parent->child == ino)
ino->parent->child = ino->older;
if(ino->younger)
ino->younger->older = ino->older;
if(ino->older)
ino->older->younger = ino->younger;
free(npath);
return ino;
}
}
:::c
INODE vfs_umount(const char *path)
{
char *npath = strdup(path);
char *pth = &npath[1];
INODE ino = vfs_find_root(&pth);
if(!ino || pth)
{
free(npath);
return 0;
}
if(ino->child)
{
free(npath);
return 0;
} else {
// Remove node from mount tree
if(ino->parent->child == ino)
ino->parent->child = ino->older;
if(ino->younger)
ino->younger->older = ino->older;
if(ino->older)
ino->older->younger = ino->younger;
free(npath);
return ino;
}
}
And that's it for now. A lot of code this time, but that's because I
don't want to push my changes to github quite yet, so I can't give you a