Initial commit - Begun rewrite of website generator
This commit is contained in:
196
pages/2013-12-12-VFS-syscalls.md
Normal file
196
pages/2013-12-12-VFS-syscalls.md
Normal file
@@ -0,0 +1,196 @@
|
||||
title: "VFS syscalls"
|
||||
subtitle: "...so I put wrappers 'round your wrappers..."
|
||||
tags: [osdev]
|
||||
|
||||
[Last time](/blog/2013/12/Virtual-File-System2/) I started to rewrite
|
||||
the VFS layer of my hobby kernel - again. This time I'll take a look at
|
||||
the system call couplings.
|
||||
|
||||
Since a [while](/blog/2013/08/Catching-Up/), I've had a cross compiler
|
||||
and [newlib](http://sourceware.org/newlib) for my kernel, which means I
|
||||
have some basic syscall interfaces to start from.
|
||||
|
||||
Newlib requires the following syscalls:
|
||||
|
||||
:::c
|
||||
int close(int file)
|
||||
int fstat(int file, struct stat *st)
|
||||
int isatty(int file)
|
||||
int link(char *old, char *new)
|
||||
int lseek(int file, int ptr, int dir)
|
||||
int open(const char *name, int flags, int mode)
|
||||
int read(int file, char *ptr, int len)
|
||||
int stat(const char *file, struct stat *st)
|
||||
int unlink(char *name)
|
||||
int write(int file, char *ptr, int len)
|
||||
|
||||
###open and close
|
||||
Everything starts with `open`, so let's look at that first.
|
||||
|
||||
In order to keep track of the files that are opened by a process, we
|
||||
need a new data structure, though; the _file descriptor_.
|
||||
|
||||
:::c
|
||||
typedef struct
|
||||
{
|
||||
INODE ino;
|
||||
uint32_t offset;
|
||||
uint32_t flags;
|
||||
uint32_t users;
|
||||
} file_desc_t;
|
||||
|
||||
The file descriptor keeps track of our position in the file as well as
|
||||
the mode it was opened in. File descriptors can also be shared between
|
||||
processes (after a `fork()` for example), and it therefore has a use
|
||||
counter. Two macros are used to manipulate the use counter
|
||||
|
||||
:::c
|
||||
#define fd_get(fd) { (fd)->users++ }
|
||||
#define fd_put(fd) { (fd)->users--; if(!(fd)->users)free(fd) }
|
||||
|
||||
Each process descriptor has an array of pointers to file descriptors
|
||||
|
||||
:::c
|
||||
file_desc_t *fd[NUM_FILEDES];
|
||||
|
||||
`open` starts by finding a free file descriptor. It then finds the file,
|
||||
opens the file and returns the index of the file descriptor it used:
|
||||
|
||||
:::c
|
||||
int open(const char *name, int flags, int mode)
|
||||
{
|
||||
int fd;
|
||||
|
||||
// Find unused file descriptor
|
||||
process_t *p = current->proc;
|
||||
int i;
|
||||
for(i=0; i < NUM_FILEDES; i++)
|
||||
{
|
||||
if(p->fd[i])
|
||||
continue;
|
||||
fd = i;
|
||||
p->fd[fd] = calloc(1, sizeof(file_desc_t));
|
||||
fd_get(p->fd[fd]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find file
|
||||
INODE ino = vfs_namei(name);
|
||||
// Open file
|
||||
vfs_open(name, flags);
|
||||
|
||||
// Setup file descriptor
|
||||
p->fd[fd]->ino = ino;
|
||||
p->fd[fd]->offset = 0;
|
||||
p->fd[fd]->flags = flags;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
I stripped away all of the sanity checking and error handling code here.
|
||||
With that code, the function is more than twice as long.
|
||||
|
||||
`close` is even easier:
|
||||
|
||||
:::c
|
||||
int close(int file)
|
||||
{
|
||||
int retval = vfs_close(p->fd[file]->ino);
|
||||
if(!p->fd[file]->ino->parent)
|
||||
free(p->fd[file]->ino);
|
||||
fd_put(p->fd[file]);
|
||||
p->fd[file] = 0;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
I always check if an inode has a parent before freeing it. If it has a
|
||||
parent, it's part of the vfs mount tree, and should be kept around.
|
||||
|
||||
###read and write
|
||||
Next, let's look at read.
|
||||
It's actually really simple (excluding sanity checking and error
|
||||
handling):
|
||||
|
||||
:::c
|
||||
int read(int file, char *ptr, int len)
|
||||
{
|
||||
process_t *p = current->proc;
|
||||
INODE node = p->fd[file]->ino;
|
||||
int ret = vfs_read(node, ptr, len, p->fd[file]->offset);
|
||||
p->fd[file]->offset += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Write is pretty much the same:
|
||||
|
||||
:::c
|
||||
int write(int file, char *ptr, int len)
|
||||
{
|
||||
process_t *p = current->proc;
|
||||
INODE node = p->fd[file]->ino;
|
||||
int ret = vfs_write(node, ptr, len, p->fd[file]->offset);
|
||||
p->fd[file]->offset += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
###stat, fstat and isatty
|
||||
`fstat` and `isatty` just passes on the information to the corresponding
|
||||
vfs functions:
|
||||
|
||||
:::c
|
||||
int fstat(int file, struct stat *st)
|
||||
{
|
||||
process_t *p = current->proc;
|
||||
INODE node = p->fd[file]->ino;
|
||||
return vfs_fstat(node, st);
|
||||
}
|
||||
|
||||
:::c
|
||||
int isatty(int file)
|
||||
{
|
||||
process_t *p = current->proc;
|
||||
INODE node = p->fd[file]->ino;
|
||||
return vfs_isatty(node);
|
||||
}
|
||||
|
||||
`stat` performs a `namei` lookup to get the node instead of taking it
|
||||
from the process' file descriptor table.
|
||||
|
||||
:::c
|
||||
int stat(const char *file, struct stat *st)
|
||||
{
|
||||
INODE node = vfs_namei(file);
|
||||
int retval = vfs_fstat(node, st);
|
||||
if(!node->parent)
|
||||
free(node);
|
||||
return retval;
|
||||
}
|
||||
|
||||
###lseek
|
||||
The final function I'll look at now is `lseek` which sets the current
|
||||
position in the file:
|
||||
|
||||
:::c
|
||||
int lseek(int file, int ptr, int dir)
|
||||
{
|
||||
process_t *p = current->proc;
|
||||
if(dir == SEEK_SET)
|
||||
{
|
||||
p->fd[file]->offset = ptr;
|
||||
}
|
||||
if(dir == SEEK_CUR)
|
||||
{
|
||||
p->fd[file]->offset += ptr;
|
||||
}
|
||||
if(dir == SEEK_END)
|
||||
{
|
||||
p->fd[file]->offset = p->fd[file]->ino->length + ptr;
|
||||
}
|
||||
return p->fd[file]->offset;
|
||||
}
|
||||
|
||||
I'll leave `link` and `unlink` for now, and come back to them when
|
||||
I need them (i.e. I wish to implement a user writeable filesystem).
|
||||
|
||||
In the next post, we'll mount an actual filesystem.
|
||||
Reference in New Issue
Block a user