#include "pfs_structs.h"
#include "buffer.h"
#include "device_struct.h"
#include <kmalloc.h>
#include <verify_area.h>
#include <segment.h>
#include <errno.h>
#include <enums.h>
#include <config.h>
#include <string.h>
#include <stddef.h>
static inline char get_basename_char(const char *fs_p)
{
char c;
return (c = get_fs_byte(fs_p)) && c != '/' ? c : '\0';
}
static inline unsigned int get_basename_len(const char *fs_basename)
{
unsigned int len;
char c;
len = 0;
while ((c = get_basename_char(fs_basename++)))
len++;
return len;
}
int pfs_name_match(const char *fs_basename, const char *name)
{
char c;
while ((c = get_basename_char(fs_basename++)))
if (c != *name++)
return 0;
return !*name;
}
char *pfs_create_name(const char *fs_basename)
{
unsigned int len;
char *name;
len = get_basename_len(fs_basename);
if (!(name = kmalloc(len + 1)))
return NULL;
memcpy_fromfs(name, fs_basename, len);
name[len] = '\0';
return name;
}
void pfs_set_dir_entry_info_name(struct pfs_dir_entry_info_struct *dir_entry_info, char *name)
{
dir_entry_info->name_ptr = name;
dir_entry_info->name = dir_entry_info->name_ptr;
}
int pfs_set_dir_entry_info_name_dup(struct pfs_dir_entry_info_struct *dir_entry_info, const char *name)
{
unsigned int size;
size = strlen(name) + 1;
if (size <= sizeof dir_entry_info->name_buf) {
dir_entry_info->name_ptr = NULL;
strcpy(dir_entry_info->name_buf, name);
dir_entry_info->name = dir_entry_info->name_buf;
}
else {
if (!(dir_entry_info->name_ptr = kmalloc(size)))
return -ENOMEM;
strcpy(dir_entry_info->name_ptr, name);
dir_entry_info->name = dir_entry_info->name_ptr;
}
return 0;
}
void pfs_free_dir_entry_info_name(struct pfs_dir_entry_info_struct *dir_entry_info)
{
if (dir_entry_info->name_ptr)
kfree(dir_entry_info->name_ptr, strlen(dir_entry_info->name_ptr) + 1);
}
void pfs_init_dir(struct pfs_dir_struct *dir)
{
dir->first_entry = NULL;
dir->last_entry = NULL;
}
void pfs_insert_dir_entry(struct pfs_dir_entry_struct *dir_entry, struct pfs_dir_struct *dir)
{
dir_entry->previous = dir->last_entry;
dir_entry->next = NULL;
if (dir->last_entry)
dir->last_entry->next = dir_entry;
else
dir->first_entry = dir_entry;
dir->last_entry = dir_entry;
}
void pfs_remove_dir_entry(struct pfs_dir_entry_struct *dir_entry, struct pfs_dir_struct *dir)
{
if (dir_entry->previous)
dir_entry->previous->next = dir_entry->next;
else
dir->first_entry = dir_entry->next;
if (dir_entry->next)
dir_entry->next->previous = dir_entry->previous;
else
dir->last_entry = dir_entry->previous;
}
int pfs_file_exists(const struct pfs_dir_struct *dir, const char *fs_basename)
{
const struct pfs_dir_entry_struct *dir_entry;
for (dir_entry = dir->first_entry; dir_entry; dir_entry = dir_entry->next)
if (pfs_name_match(fs_basename, dir_entry->info.name))
return 1;
return 0;
}
int pfs_find(const struct pfs_dir_struct *dir, const char *fs_basename, unsigned long *id, int *type)
{
const struct pfs_dir_entry_struct *dir_entry;
for (dir_entry = dir->first_entry; dir_entry; dir_entry = dir_entry->next)
if (pfs_name_match(fs_basename, dir_entry->info.name)) {
*id = dir_entry->info.id;
*type = dir_entry->info.type;
return 1;
}
return 0;
}
int pfs_read_name(const char *name, unsigned long pos, char *fs_buf, unsigned int count)
{
const char *s;
int n;
s = &name[pos];
n = 0;
while (1) {
put_fs_byte(*s, fs_buf++);
n++;
count--;
if (!count || !*s)
break;
s++;
}
return n;
}
int pfs_read_dir(const struct pfs_dir_struct *dir, unsigned long pos, char *fs_buf, unsigned int count)
{
const struct pfs_dir_entry_struct *dir_entry;
unsigned int len;
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_WRITE))
return -EFAULT;
dir_entry = dir->first_entry;
while (1) {
if (!dir_entry)
return 0;
len = strlen(dir_entry->info.name);
if (pos < len + 1)
break;
pos -= len + 1;
dir_entry = dir_entry->next;
}
return pfs_read_name(dir_entry->info.name, pos, fs_buf, count);
}
int pfs_read_zeros(char *fs_buf, unsigned int count)
{
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_WRITE))
return -EFAULT;
memset_tofs(fs_buf, '\0', count);
return count;
}
int pfs_read_buf(const char *buf, unsigned int buf_size, unsigned long pos, char *fs_buf, unsigned int count)
{
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_WRITE))
return -EFAULT;
if (pos >= buf_size)
return 0;
if (count > buf_size - pos)
count = buf_size - pos;
memcpy_tofs(fs_buf, buf + pos, count);
return count;
}
int pfs_read_block(const struct device_struct *device, unsigned long pos, char *fs_buf, unsigned int count)
{
unsigned int n;
int block;
char *buf;
unsigned int size;
int retval;
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_WRITE))
return -EFAULT;
n = 0;
for (block = pos / BLOCK_SIZE; ; block++) {
if (!(buf = buffer_get(device, block))) {
retval = -EIO;
goto end;
}
if (!buffer_update(buf)) {
retval = -EIO;
goto release;
}
size = BLOCK_SIZE - pos % BLOCK_SIZE;
if (count - n <= size) {
memcpy_tofs(fs_buf, buf + pos % BLOCK_SIZE, count - n);
retval = count;
goto release;
}
else {
memcpy_tofs(fs_buf, buf + pos % BLOCK_SIZE, size);
n += size;
fs_buf += size;
pos += size;
}
buffer_release(buf);
}
release:
buffer_release(buf);
end:
return retval;
}
int pfs_write_block(const struct device_struct *device, unsigned long pos, const char *fs_buf, unsigned int count)
{
unsigned int n, p;
int block;
char *buf;
unsigned int size;
int retval;
if (!buffer_is_writable(device))
return -EPERM;
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_READ))
return -EFAULT;
n = 0;
for (block = pos / BLOCK_SIZE; ; block++) {
if (!(buf = buffer_get(device, block))) {
retval = -EIO;
goto end;
}
if (!buffer_update(buf)) {
retval = -EIO;
goto release;
}
p = n;
size = BLOCK_SIZE - pos % BLOCK_SIZE;
if (size > count - p)
size = count - p;
memcpy_fromfs(buf + pos % BLOCK_SIZE, fs_buf, size);
p += size;
fs_buf += size;
pos += size;
buffer_dirty(buf);
n = p;
if (n == count) {
retval = n;
goto release;
}
buffer_release(buf);
}
release:
buffer_release(buf);
end:
buffer_sync(device);
return retval;
}