Nucleus
Barry System headers (remove libc dependency) 18495cf (3 years, 2 months ago)
/*
* This file implements the File object and associated functions. A file
* represents an opened file, and holds a reference the the underlying inode.
* Most of the functions here are just wrappers around the file operations.
* They perform the necessary validation to ensure the operation can be called
* on the particular file. If no operation is found, they may implement a
* generic version.
*/
#include <sys/errno.h>
#include <sys/stat.h>
#include <nucleus/lib.h>
#include <nucleus/object.h>
#include <nucleus/vfs.h>
static void file_delete(Object *);
static void file_copy(Object *, Object *);
/* File object type */
ObjectType fileType = {
.name = "FILE",
.size = sizeof(File),
.delete = file_delete,
.copy = file_copy,
};
/* Destroy a file */
static void
file_delete(Object *obj)
{
File *file = (void *) obj;
if (file->inode)
put(file->inode);
if (file->path)
destroy_list(file->path);
}
/* Copy a file */
static void
file_copy(Object *a, Object *b)
{
File *parent = (void *) a, *child = (void *) b;
if (parent->inode)
child->inode = copy(parent->inode);
child->flags = parent->flags;
child->pos = parent->pos;
child->ops = parent->ops;
if (parent->path)
child->path = copy_list(parent->path);
}
/* Open a file */
int
file_open(File *file)
{
if (!file->ops || !file->ops->open)
return -EINVAL;
return file->ops->open(file);
}
/* Read a file */
size_t
file_read(File *file, char *buf, size_t size)
{
size_t count = 0;
/* Irregular files cannot be cached */
if (!S_ISREG(file->inode->mode)) {
if (file->ops && file->ops->read)
count = file->ops->read(file, buf, size, file->pos);
goto end;
}
if (file->pos > file->inode->size)
goto end;
if (file->pos + size > file->inode->size)
size = file->inode->size - file->pos;
/* Read with cache */
Page *page;
char *data;
uint16_t min, indent = file->pos % PAGE_SIZE;
while (size) {
min = (size > PAGE_SIZE) ? PAGE_SIZE : size;
page = find_page(file->inode->pages,
file->pos + (count % PAGE_SIZE));
if (page) {
/* Cache hit */
data = map_page(page);
memcpy(buf + count, data + indent, min);
} else if (file->ops && file->ops->read) {
/* Cache miss */
file->ops->read(file, buf + count, min,
file->pos + count);
}
indent = 0;
size -= min;
count += min;
}
end:
lock(file);
file->pos += count;
unlock(file);
return count;
}
/* Write a file */
size_t
file_write(File *file, char *buf, size_t size)
{
size_t count = 0;
/* Irregular files cannot be cached */
if (!S_ISREG(file->inode->mode)) {
if (file->ops && file->ops->write)
count = file->ops->write(file, buf, size, file->pos);
goto end;
}
if (file->pos + size > file->inode->size)
file->inode->size = size + file->pos;
/* Write with cache */
Page *page;
char *data;
uint16_t min, indent = file->pos % PAGE_SIZE;
while (size) {
min = (size > PAGE_SIZE) ? PAGE_SIZE : size;
page = find_page(file->inode->pages,
file->pos + (count % PAGE_SIZE));
if (page) {
/* Cache hit */
data = map_page(page);
memcpy(data + indent, buf + count, min);
} else if (file->ops && file->ops->write) {
/* Cache miss */
file->ops->read(file, buf + count, min,
file->pos + count);
}
indent = 0;
size -= min;
count += min;
}
end:
lock(file);
file->pos += count;
unlock(file);
return count;
}
/* I/O Control */
int
file_ioctl(File *file, unsigned long request, uintptr_t argp)
{
if (!file->ops)
return -EINVAL;
if (!file->ops->ioctl)
return -ENOTTY;
return file->ops->ioctl(file, request, argp);
}
/* Read a directory entry from a directory */
int
file_readdir(File *file, struct dirent *dent, off_t index)
{
if (!file->ops || !file->ops->readdir)
return -EINVAL;
return file->ops->readdir(file, dent, index);
}
/* Map a file into memory */
void
file_mmap(File *file, void *addr, size_t len, off_t offset)
{
if (!file->ops)
return;
if (file->ops->mmap)
file->ops->mmap(file, addr, len, offset);
else if (S_ISREG(file->inode->mode))
file->ops->read(file, addr, len, offset);
}