Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2018 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <errno.h>
#include <kernel.h>
#include <limits.h>
#include <posix/pthread.h>
#include <posix/unistd.h>
#include <string.h>

BUILD_ASSERT_MSG(PATH_MAX > MAX_FILE_NAME,
		"PATH_MAX is less than MAX_FILE_NAME");

union file_desc {
	struct fs_file_t file;
	struct fs_dir_t	dir;
};

struct posix_fs_desc {
	union file_desc desc;
	bool is_dir;
	bool used;
};

static struct posix_fs_desc desc_array[CONFIG_POSIX_MAX_OPEN_FILES];

static struct fs_dirent fdirent;
static struct dirent pdirent;

static int posix_fs_alloc_fd(union file_desc **ptr, bool is_dir)
{
	int fd;
	unsigned int key = irq_lock();

	for (fd = 0; fd < CONFIG_POSIX_MAX_OPEN_FILES; fd++) {
		if (desc_array[fd].used == false) {
			*ptr = &desc_array[fd].desc;
			desc_array[fd].used = true;
			desc_array[fd].is_dir = is_dir;
			break;
		}
	}
	irq_unlock(key);

	if (fd >= CONFIG_POSIX_MAX_OPEN_FILES) {
		return -1;
	}

	return fd;
}

static int posix_fs_get_ptr(int fd, union file_desc **ptr, bool is_dir)
{
	int rc = 0;
	unsigned int key;

	if (fd < 0 || fd >= CONFIG_POSIX_MAX_OPEN_FILES) {
		return -1;
	}

	key = irq_lock();

	if ((desc_array[fd].used == true) &&
		(desc_array[fd].is_dir == is_dir)) {
		*ptr = &desc_array[fd].desc;
	} else {
		rc = -1;
	}
	irq_unlock(key);

	return rc;
}

static inline void posix_fs_free_ptr(struct posix_fs_desc *ptr)
{
	struct posix_fs_desc *desc = ptr;
	unsigned int key = irq_lock();

	desc->used = false;
	desc->is_dir = false;
	irq_unlock(key);
}

static inline void posix_fs_free_fd(int fd)
{
	posix_fs_free_ptr(&desc_array[fd]);
}

/**
 * @brief Open a file.
 *
 * See IEEE 1003.1
 */
int open(const char *name, int flags)
{
	int rc, fd;
	struct fs_file_t *ptr = NULL;

	ARG_UNUSED(flags);

	fd = posix_fs_alloc_fd((union file_desc **)&ptr, false);
	if ((fd < 0) || (ptr == NULL)) {
		errno = ENFILE;
		return -1;
	}
	memset(ptr, 0, sizeof(struct fs_file_t));

	rc = fs_open(ptr, name);
	if (rc < 0) {
		posix_fs_free_fd(fd);
		errno = -rc;
		return -1;
	}

	return fd;
}

/**
 * @brief Close a file descriptor.
 *
 * See IEEE 1003.1
 */
int close(int fd)
{
	int rc;
	struct fs_file_t *ptr = NULL;

	if (posix_fs_get_ptr(fd, (union file_desc **)&ptr, false)) {
		errno = EBADF;
		return -1;
	}

	rc = fs_close(ptr);

	/* Free file ptr memory */
	posix_fs_free_fd(fd);

	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	return 0;
}

/**
 * @brief Write to a file.
 *
 * See IEEE 1003.1
 */
ssize_t write(int fd, char *buffer, unsigned int count)
{
	ssize_t rc;
	struct fs_file_t *ptr = NULL;

	if (posix_fs_get_ptr(fd, (union file_desc **)&ptr, false)) {
		errno = EBADF;
		return -1;
	}

	rc = fs_write(ptr, buffer, count);
	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	return rc;
}

/**
 * @brief Read from a file.
 *
 * See IEEE 1003.1
 */
ssize_t read(int fd, char *buffer, unsigned int count)
{
	ssize_t rc;
	struct fs_file_t *ptr = NULL;

	if (posix_fs_get_ptr(fd, (union file_desc **)&ptr, false)) {
		errno = EBADF;
		return -1;
	}

	rc = fs_read(ptr, buffer, count);
	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	return rc;
}

/**
 * @brief Move read/write file offset.
 *
 * See IEEE 1003.1
 */
int lseek(int fd, int offset, int whence)
{
	int rc;
	struct fs_file_t *ptr = NULL;

	if (posix_fs_get_ptr(fd, (union file_desc **)&ptr, false)) {
		errno = EBADF;
		return -1;
	}

	rc = fs_seek(ptr, offset, whence);
	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	return 0;
}

/**
 * @brief Open a directory stream.
 *
 * See IEEE 1003.1
 */
DIR *opendir(const char *dirname)
{
	int rc, fd;
	struct fs_dir_t *ptr = NULL;

	fd = posix_fs_alloc_fd((union file_desc **)&ptr, true);
	if ((fd < 0) || (ptr == NULL)) {
		errno = EMFILE;
		return NULL;
	}
	memset(ptr, 0, sizeof(struct fs_dir_t));

	rc = fs_opendir(ptr, dirname);
	if (rc < 0) {
		posix_fs_free_fd(fd);
		errno = -rc;
		return NULL;
	}

	return ptr;
}

/**
 * @brief Close a directory stream.
 *
 * See IEEE 1003.1
 */
int closedir(DIR *dirp)
{
	int rc;

	if (dirp == NULL) {
		errno = EBADF;
		return -1;
	}

	rc = fs_closedir(dirp);

	/* Free file ptr memory */
	posix_fs_free_ptr((struct posix_fs_desc *)dirp);

	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	return 0;
}

/**
 * @brief Read a directory.
 *
 * See IEEE 1003.1
 */
struct dirent *readdir(DIR *dirp)
{
	int rc;

	if (dirp == NULL) {
		errno = EBADF;
		return NULL;
	}

	rc = fs_readdir(dirp, &fdirent);
	if (rc < 0) {
		errno = -rc;
		return NULL;
	}

	rc = strlen(fdirent.name);
	rc = (rc < PATH_MAX) ? rc : (PATH_MAX - 1);
	memcpy(pdirent.d_name, fdirent.name, rc);

	/* Make sure the name is NULL terminated */
	pdirent.d_name[rc] = '\0';
	return &pdirent;
}

/**
 * @brief Rename a file.
 *
 * See IEEE 1003.1
 */
int rename(const char *old, const char *new)
{
	int rc;

	rc = fs_rename(old, new);
	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	return 0;
}

/**
 * @brief Remove a directory entry.
 *
 * See IEEE 1003.1
 */
int unlink(const char *path)
{
	int rc;

	rc = fs_unlink(path);
	if (rc < 0) {
		errno = -rc;
		return -1;
	}
	return 0;
}

/**
 * @brief Get file status.
 *
 * See IEEE 1003.1
 */
int stat(const char *path, struct stat *buf)
{
	int rc;
	struct fs_statvfs stat;

	if (buf == NULL) {
		errno = EBADF;
		return -1;
	}

	rc = fs_statvfs(path, &stat);
	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	buf->st_size = stat.f_bsize * stat.f_blocks;
	buf->st_blksize = stat.f_bsize;
	buf->st_blocks = stat.f_blocks;
	return 0;
}

/**
 * @brief Make a directory.
 *
 * See IEEE 1003.1
 */
int mkdir(const char *path, mode_t mode)
{
	int rc;

	ARG_UNUSED(mode);

	rc = fs_mkdir(path);
	if (rc < 0) {
		errno = -rc;
		return -1;
	}

	return 0;
}