toaruos/kernel/fs/tmpfs.c
Kevin Lange 16da56ea9b General support for open() flags
Includes truncation support in tmpfs, and changes the signure of the VFS
open() method. Also tweaked some comment style in the VFS.
2013-04-24 00:19:08 -07:00

421 lines
10 KiB
C

#include <system.h>
#include <logging.h>
#include <fs.h>
#include <version.h>
#include <process.h>
/* 1KB */
#define BLOCKSIZE 1024
#define TMPFS_TYPE_FILE 1
#define TMPFS_TYPE_DIR 2
static uint8_t volatile lock = 0;
struct tmpfs_file {
char * name;
int type;
int mask;
int uid;
int gid;
unsigned int atime;
unsigned int mtime;
unsigned int ctime;
size_t length;
size_t block_count;
size_t pointers;
char ** blocks;
};
struct tmpfs_dir;
struct tmpfs_dir {
char * name;
int type;
int mask;
int uid;
int gid;
unsigned int atime;
unsigned int mtime;
unsigned int ctime;
list_t * files;
struct tmpfs_dir * parent;
};
char empty_block[BLOCKSIZE] = {0};
struct tmpfs_dir * tmpfs_root = NULL;
fs_node_t * tmpfs_from_dir(struct tmpfs_dir * d);
static struct tmpfs_file * tmpfs_file_new(char * name) {
spin_lock(&lock);
struct tmpfs_file * t = malloc(sizeof(struct tmpfs_file));
t->name = strdup(name);
t->type = TMPFS_TYPE_FILE;
t->length = 0;
t->pointers = 2;
t->block_count = 0;
t->mask = 0;
t->uid = 0;
t->gid = 0;
t->atime = now();
t->mtime = t->atime;
t->ctime = t->ctime;
t->blocks = malloc(t->pointers * sizeof(char *));
for (size_t i = 0; i < t->pointers; ++i) {
t->blocks[i] = NULL;
}
spin_unlock(&lock);
return t;
}
static struct tmpfs_dir * tmpfs_dir_new(char * name, struct tmpfs_dir * parent) {
spin_lock(&lock);
struct tmpfs_dir * d = malloc(sizeof(struct tmpfs_dir));
d->name = strdup(name);
d->type = TMPFS_TYPE_DIR;
d->mask = 0;
d->uid = 0;
d->gid = 0;
d->atime = now();
d->mtime = d->atime;
d->ctime = d->ctime;
d->files = list_create();
spin_unlock(&lock);
return d;
}
static void tmpfs_file_free(struct tmpfs_file * t) {
for (size_t i = 0; i < t->block_count; ++i) {
free(t->blocks[i]);
}
}
static void tmpfs_file_blocks_embiggen(struct tmpfs_file * t) {
t->pointers *= 2;
debug_print(INFO, "Embiggening file %s to %d blocks", t->name, t->pointers);
t->blocks = realloc(t->blocks, sizeof(char *) * t->pointers);
}
static char * tmpfs_file_getset_block(struct tmpfs_file * t, size_t blockid, int create) {
debug_print(INFO, "Reading block %d from file %s", blockid, t->name);
if (create) {
spin_lock(&lock);
while (blockid >= t->pointers) {
tmpfs_file_blocks_embiggen(t);
}
while (blockid >= t->block_count) {
debug_print(INFO, "Allocating block %d for file %s", blockid, t->name);
t->blocks[t->block_count] = malloc(BLOCKSIZE);
t->block_count += 1;
}
spin_unlock(&lock);
} else {
if (blockid >= t->block_count) {
debug_print(CRITICAL, "This will probably end badly.");
return NULL;
}
}
debug_print(WARNING, "Using block %d->0x%x (of %d) on file %s", blockid, t->blocks[blockid], t->block_count, t->name);
return t->blocks[blockid];
}
static uint32_t read_tmpfs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
t->atime = now();
uint32_t end;
if (offset + size > t->length) {
end = t->length;
} else {
end = offset + size;
}
debug_print(INFO, "reading from %d to %d", offset, end);
uint32_t start_block = offset / BLOCKSIZE;
uint32_t end_block = end / BLOCKSIZE;
uint32_t end_size = end - end_block * BLOCKSIZE;
uint32_t size_to_read = end - offset;
if (start_block == end_block && offset == end) return 0;
if (end_size == 0) {
end_block--;
}
if (start_block == end_block) {
void *buf = tmpfs_file_getset_block(t, start_block, 0);
memcpy(buffer, (uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), size_to_read);
return size_to_read;
} else {
uint32_t block_offset;
uint32_t blocks_read = 0;
for (block_offset = start_block; block_offset < end_block; block_offset++, blocks_read++) {
if (block_offset == start_block) {
void *buf = tmpfs_file_getset_block(t, block_offset, 0);
memcpy(buffer, (uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), BLOCKSIZE - (offset % BLOCKSIZE));
} else {
void *buf = tmpfs_file_getset_block(t, block_offset, 0);
memcpy(buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), buf, BLOCKSIZE);
}
}
void *buf = tmpfs_file_getset_block(t, end_block, 0);
memcpy(buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), buf, end_size);
}
return size_to_read;
}
static uint32_t write_tmpfs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
t->atime = now();
t->mtime = t->atime;
uint32_t end;
if (offset + size > t->length) {
t->length = offset + size;
}
end = offset + size;
uint32_t start_block = offset / BLOCKSIZE;
uint32_t end_block = end / BLOCKSIZE;
uint32_t end_size = end - end_block * BLOCKSIZE;
uint32_t size_to_read = end - offset;
if (end_size == 0) {
end_block--;
}
if (start_block == end_block) {
void *buf = tmpfs_file_getset_block(t, start_block, 1);
memcpy((uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), buffer, size_to_read);
return size_to_read;
} else {
uint32_t block_offset;
uint32_t blocks_read = 0;
for (block_offset = start_block; block_offset < end_block; block_offset++, blocks_read++) {
if (block_offset == start_block) {
void *buf = tmpfs_file_getset_block(t, block_offset, 1);
memcpy((uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), buffer, BLOCKSIZE - (offset % BLOCKSIZE));
} else {
void *buf = tmpfs_file_getset_block(t, block_offset, 1);
memcpy(buf, buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), BLOCKSIZE);
}
}
void *buf = tmpfs_file_getset_block(t, end_block, 1);
memcpy(buf, buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), end_size);
}
return size_to_read;
}
static int chmod_tmpfs(fs_node_t * node, int mode) {
struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
/* XXX permissions */
t->mask = mode;
return 0;
}
static void open_tmpfs(fs_node_t * node, unsigned int flags) {
struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
debug_print(WARNING, "---- Opened TMPFS file %s with flags 0x%x ----", t->name, flags);
if (flags & O_TRUNC) {
debug_print(WARNING, "Truncating file %s", t->name);
for (size_t i = 0; i < t->block_count; ++i) {
free(t->blocks[i]);
t->blocks[i] = 0;
}
t->block_count = 0;
t->length = 0;
}
return;
}
static fs_node_t * tmpfs_from_file(struct tmpfs_file * t) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->inode = 0;
strcpy(fnode->name, t->name);
fnode->device = t;
fnode->mask = t->mask;
fnode->uid = t->uid;
fnode->gid = t->gid;
fnode->atime = t->atime;
fnode->ctime = t->ctime;
fnode->mtime = t->mtime;
fnode->flags = FS_FILE;
fnode->read = read_tmpfs;
fnode->write = write_tmpfs;
fnode->open = open_tmpfs;
fnode->close = NULL;
fnode->readdir = NULL;
fnode->finddir = NULL;
fnode->chmod = chmod_tmpfs;
fnode->length = t->length;
return fnode;
}
static struct dirent * readdir_tmpfs(fs_node_t *node, uint32_t index) {
struct tmpfs_dir * d = (struct tmpfs_dir *)node->device;
uint32_t i = 0;
debug_print(NOTICE, "tmpfs - readdir id=%d", index);
if (index >= d->files->length) return NULL;
foreach(f, d->files) {
if (i == index) {
struct tmpfs_file * t = (struct tmpfs_file *)f->value;
struct dirent * out = malloc(sizeof(struct dirent));
memset(out, 0x00, sizeof(struct dirent));
out->ino = (uint32_t)t;
strcpy(out->name, t->name);
return out;
} else {
++i;
}
}
return NULL;
}
static fs_node_t * finddir_tmpfs(fs_node_t * node, char * name) {
if (!name) return NULL;
struct tmpfs_dir * d = (struct tmpfs_dir *)node->device;
spin_lock(&lock);
foreach(f, d->files) {
struct tmpfs_file * t = (struct tmpfs_file *)f->value;
if (!strcmp(name, t->name)) {
spin_unlock(&lock);
switch (t->type) {
case TMPFS_TYPE_FILE:
return tmpfs_from_file(t);
case TMPFS_TYPE_DIR:
return tmpfs_from_dir((struct tmpfs_dir *)t);
}
}
}
spin_unlock(&lock);
return NULL;
}
static void unlink_tmpfs(fs_node_t * node, char * name) {
struct tmpfs_dir * d = (struct tmpfs_dir *)node->device;
int i = -1, j = 0;
spin_lock(&lock);
foreach(f, d->files) {
struct tmpfs_file * t = (struct tmpfs_file *)f->value;
if (!strcmp(name, t->name)) {
tmpfs_file_free(t);
free(t);
i = j;
break;
}
j++;
}
if (i >= 0) {
list_remove(d->files, i);
}
spin_unlock(&lock);
return;
}
void create_tmpfs(fs_node_t *parent, char *name, uint16_t permission) {
if (!name) return;
struct tmpfs_dir * d = (struct tmpfs_dir *)parent->device;
debug_print(CRITICAL, "Creating TMPFS file %s in %s", name, d->name);
spin_lock(&lock);
foreach(f, d->files) {
struct tmpfs_file * t = (struct tmpfs_file *)f->value;
if (!strcmp(name, t->name)) {
spin_unlock(&lock);
debug_print(WARNING, "... already exists.");
return; /* Already exists */
}
}
spin_unlock(&lock);
debug_print(NOTICE, "... creating a new file.");
struct tmpfs_file * t = tmpfs_file_new(name);
t->mask = permission;
t->uid = current_process->user;
t->gid = current_process->user;
list_insert(d->files, t);
}
void mkdir_tmpfs(fs_node_t * parent, char * name, uint16_t permission) {
if (!name) return;
struct tmpfs_dir * d = (struct tmpfs_dir *)parent->device;
debug_print(CRITICAL, "Creating TMPFS directory %s (in %s)", name, d->name);
spin_lock(&lock);
foreach(f, d->files) {
struct tmpfs_file * t = (struct tmpfs_file *)f->value;
if (!strcmp(name, t->name)) {
spin_unlock(&lock);
debug_print(WARNING, "... already exists.");
return; /* Already exists */
}
}
spin_unlock(&lock);
debug_print(NOTICE, "... creating a new directory.");
struct tmpfs_dir * out = tmpfs_dir_new(name, d);
out->mask = permission;
out->uid = current_process->user;
out->gid = current_process->user;
list_insert(d->files, out);
}
fs_node_t * tmpfs_from_dir(struct tmpfs_dir * d) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->inode = 0;
strcpy(fnode->name, "tmp");
fnode->mask = d->mask;
fnode->uid = d->uid;
fnode->gid = d->gid;
fnode->device = d;
fnode->atime = d->atime;
fnode->mtime = d->mtime;
fnode->ctime = d->ctime;
fnode->flags = FS_DIRECTORY;
fnode->read = NULL;
fnode->write = NULL;
fnode->open = NULL;
fnode->close = NULL;
fnode->readdir = readdir_tmpfs;
fnode->finddir = finddir_tmpfs;
fnode->create = create_tmpfs;
fnode->unlink = unlink_tmpfs;
fnode->mkdir = mkdir_tmpfs;
return fnode;
}
fs_node_t * tmpfs_create() {
tmpfs_root = tmpfs_dir_new("tmp", NULL);
tmpfs_root->mask = 0777;
tmpfs_root->uid = 0;
tmpfs_root->gid = 0;
return tmpfs_from_dir(tmpfs_root);
}