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.