BarryServer : Git

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

// Related

Nucleus

Barry System headers (remove libc dependency) 18495cf (3 years, 2 months ago)
/*
 * This file implements the Files object, which is the namespace for a task's
 * files.  It stores all the opened files for a task.  It also implements many
 * of the file-based system calls that need to use a file descriptor.
 */

#include <sys/errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <nucleus/lib.h>
#include <nucleus/memory.h>
#include <nucleus/object.h>
#include <nucleus/task.h>
#include <nucleus/vfs.h>

/* Structure for a Files namespace */
struct Files {
	Object obj;
	File *fd[NFILES];
};

static void files_delete(Object *);
static void files_copy(Object *, Object *);

/* Files object type */
ObjectType filesType = {
	.name = "FILES",
	.size = sizeof(Files),
	.delete = files_delete,
	.copy = files_copy,
};

/* Destroy a Files object */
static void
files_delete(Object *obj)
{
	Files *files = (void *) obj;
	int fd;
	for (fd = 0; fd < NFILES; fd++)
		if (files->fd[fd])
			put(files->fd[fd]);
}

/* Copy a Files object */
static void
files_copy(Object *a, Object *b)
{
	Files *parent = (void *) a, *child = (void *) b;
	File *file;
	int fd;
	for (fd = 0; fd < NFILES; fd++) {
		file = parent->fd[fd];
		if (!file)
			continue;
		child->fd[fd] = get(file);
	}
}

/* Get the file described by fd */
File *
get_file_by_fd(int fd)
{
	if (fd < 0 || fd >= NFILES)
		return NULL;
	return current->files->fd[fd];
}

/* Allocate file descriptor */
int
allocate_fd(void)
{
	int fd;
	for (fd = 0; fd < NFILES; fd++)
		if (!current->files->fd[fd])
			break;
	if (fd == NFILES)
		fd = -1;
	return fd;
}

/* Install a file in a file descriptor */
void
install_fd(int fd, File *file)
{
	current->files->fd[fd] = file;
}

/* Duplicate a file descriptor */
int
dup(int oldfd)
{
	int newfd = allocate_fd();
	if (newfd == -1)
		return -EMFILE;
	return dup2(oldfd, newfd);
}

/* Duplicate a file descriptor to a new file descriptor */
int
dup2(int oldfd, int newfd)
{
	/* Check file descriptors */
	File *oldfile = get_file_by_fd(oldfd);
	File *newfile = get_file_by_fd(newfd);
	if (!oldfile || newfd < 0 || newfd >= NFILES)
		return -EBADF;
	if (newfd != oldfd) {
		if (newfile)
			close(newfd);
		install_fd(newfd, get(oldfile));
	}
	return newfd;
}

/* Close a file */
int
close(int fd)
{
	if (!get_file_by_fd(fd))
		return -EBADF;
	put(current->files->fd[fd]);
	current->files->fd[fd] = NULL;
	return 0;
}

/* Read a file */
int
read(int fd, void *buf, size_t count)
{
	if (!buf)
		return -EFAULT;
	if (!verify_access(buf, count, PROT_WRITE))
		return -EFAULT;

	File *file = get_file_by_fd(fd);
	if (!file)
		return -EBADF;

	if (file->inode && S_ISDIR(file->inode->mode))
		return -EISDIR;

	return file_read(file, buf, count);
}

/* Write a file */
int
write(int fd, void *buf, size_t count)
{
	if (!buf)
		return -EFAULT;
	if (!verify_access(buf, count, PROT_READ))
		return -EFAULT;

	File *file = get_file_by_fd(fd);
	if (!file)
		return -EBADF;

	if (file->inode && S_ISDIR(file->inode->mode))
		return -EISDIR;

	return file_write(file, buf, count);
}

/* Move a file's position */
off_t
lseek(int fd, off_t offset, int whence)
{
	File *file = get_file_by_fd(fd);
	if (!file)
		return -EBADF;

	lock(file);
	switch (whence) {
	case SEEK_SET:
		file->pos = offset;
		break;
	case SEEK_CUR:
		file->pos += offset;
		break;
	case SEEK_END:
		file->pos = file->inode->size + offset;
		break;
	default:
		unlock(file);
		return -EINVAL;
	}
	unlock(file);
	return file->pos;
}

/* Perform I/O control on a file */
int
ioctl(int fd, unsigned long request, ...)
{
	File *file = get_file_by_fd(fd);
	if (!file)
		return -EBADF;

	va_list args;
	va_start(args, request);
	uintptr_t argp = va_arg(args, uintptr_t);
	va_end(args);

	return file_ioctl(file, request, argp);
}

/* Get directory entries */
size_t
getdents(int fd, void *buf, size_t count)
{
	if (!verify_access(buf, count, PROT_WRITE))
		return -EFAULT;
	if (count < sizeof(struct dirent))
		return -EINVAL;

	int err;
	size_t i, size = 0;
	struct dirent *dent = buf;
	File *file = get_file_by_fd(fd);
	if (!file)
		return -EBADF;
	if (!S_ISDIR(file->inode->mode))
		return -ENOTDIR;

	/* Read as many entries as possible */
	for (i = 0; i < count / sizeof(struct dirent); i++) {
		err = file_readdir(file, dent, i);
		if (err < 0)
			goto out;
		size += sizeof(struct dirent) + dent->d_namelen;
		*((char *) buf + size - 1) = '\0';
		dent = (void *) ((char *) buf + size);
	}

out:
	if (!i)
		return err;
	return size;
}