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