BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / blob / master / vfs / file.c

// Related

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);
}