#include "node.h"
#include <kmalloc.h>
#include <verify_area.h>
#include <segment.h>
#include <errno.h>
#include <enums.h>
#include <config.h>
#include <util.h>
struct traverse_fo_struct;
struct traverse_fo_vtbl_struct {
void (*process)(struct traverse_fo_struct *traverse_fo, char c);
int (*get_retval)(struct traverse_fo_struct *traverse_fo);
};
struct traverse_fo_struct {
const struct traverse_fo_vtbl_struct *vptr;
};
struct get_path_length_fo_struct {
struct traverse_fo_struct traverse_fo;
unsigned long *length;
};
static void get_path_length_process(struct traverse_fo_struct *traverse_fo, char c)
{
struct get_path_length_fo_struct *get_path_length_fo;
get_path_length_fo = downcast(struct get_path_length_fo_struct, traverse_fo, traverse_fo);
(*get_path_length_fo->length)++;
}
static int get_path_length_get_retval(struct traverse_fo_struct *traverse_fo)
{
return 0;
}
static const struct traverse_fo_vtbl_struct get_path_length_traverse_fo_vtbl = {
.process = get_path_length_process,
.get_retval = get_path_length_get_retval
};
static void get_path_length_fo_init(struct get_path_length_fo_struct *get_path_length_fo, unsigned long *length)
{
get_path_length_fo->traverse_fo.vptr = &get_path_length_traverse_fo_vtbl;
get_path_length_fo->length = length;
(*get_path_length_fo->length) = 0;
}
struct write_path_fo_struct {
struct traverse_fo_struct traverse_fo;
unsigned long pos;
char *fs_buf;
unsigned int count;
unsigned long skip_count;
unsigned int n;
};
static void write_path_process(struct traverse_fo_struct *traverse_fo, char c)
{
struct write_path_fo_struct *write_path_fo;
write_path_fo = downcast(struct write_path_fo_struct, traverse_fo, traverse_fo);
if (write_path_fo->skip_count)
write_path_fo->skip_count--;
else if (write_path_fo->n < write_path_fo->count) {
put_fs_byte(c, write_path_fo->fs_buf++);
write_path_fo->n++;
write_path_fo->pos++;
}
}
static int write_path_get_retval(struct traverse_fo_struct *traverse_fo)
{
struct write_path_fo_struct *write_path_fo;
write_path_fo = downcast(struct write_path_fo_struct, traverse_fo, traverse_fo);
return write_path_fo->n;
}
static const struct traverse_fo_vtbl_struct write_path_traverse_fo_vtbl = {
.process = write_path_process,
.get_retval = write_path_get_retval
};
static void write_path_fo_init(struct write_path_fo_struct *write_path_fo, unsigned long pos, char *fs_buf, unsigned int count)
{
write_path_fo->traverse_fo.vptr = &write_path_traverse_fo_vtbl;
write_path_fo->pos = pos;
write_path_fo->fs_buf = fs_buf;
write_path_fo->count = count;
write_path_fo->skip_count = pos;
write_path_fo->n = 0;
}
struct write_path_kernel_fo_struct {
struct traverse_fo_struct traverse_fo;
char *buf;
};
static void write_path_kernel_process(struct traverse_fo_struct *traverse_fo, char c)
{
struct write_path_kernel_fo_struct *write_path_kernel_fo;
write_path_kernel_fo = downcast(struct write_path_kernel_fo_struct, traverse_fo, traverse_fo);
*write_path_kernel_fo->buf++ = c;
}
static int write_path_kernel_get_retval(struct traverse_fo_struct *traverse_fo)
{
return 0;
}
static const struct traverse_fo_vtbl_struct write_path_kernel_traverse_fo_vtbl = {
.process = write_path_kernel_process,
.get_retval = write_path_kernel_get_retval
};
static void write_path_kernel_fo_init(struct write_path_kernel_fo_struct *write_path_kernel_fo, char *buf)
{
write_path_kernel_fo->traverse_fo.vptr = &write_path_kernel_traverse_fo_vtbl;
write_path_kernel_fo->buf = buf;
}
static int traverse_internal(const struct file_struct *node, int next, struct traverse_fo_struct *traverse_fo)
{
char name[NAME_MAX + 1];
const char *s;
int rv;
if (node == root)
traverse_fo->vptr->process(traverse_fo, '/');
else {
if ((rv = traverse_internal(node_get_parent(node), 1, traverse_fo)) < 0)
return rv;
if ((rv = node_get_name(node, name)) < 0)
return rv;
for (s = name; *s; s++)
traverse_fo->vptr->process(traverse_fo, *s);
if (next)
traverse_fo->vptr->process(traverse_fo, '/');
}
return 0;
}
static int traverse(const struct file_struct *node, struct traverse_fo_struct *traverse_fo)
{
int rv;
if ((rv = traverse_internal(node, 0, traverse_fo)) < 0)
return rv;
return traverse_fo->vptr->get_retval(traverse_fo);
}
int get_path_length(const struct file_struct *node, unsigned long *length)
{
struct get_path_length_fo_struct get_path_length_fo;
get_path_length_fo_init(&get_path_length_fo, length);
return traverse(node, &get_path_length_fo.traverse_fo);
}
int write_path(const struct file_struct *node, unsigned long pos, char *fs_buf, unsigned int count)
{
struct write_path_fo_struct write_path_fo;
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_WRITE))
return -EFAULT;
write_path_fo_init(&write_path_fo, pos, fs_buf, count);
return traverse(node, &write_path_fo.traverse_fo);
}
int get_path(const struct file_struct *node, char **path)
{
unsigned long len;
struct get_path_length_fo_struct get_path_length_fo;
struct write_path_kernel_fo_struct write_path_kernel_fo;
int rv;
get_path_length_fo_init(&get_path_length_fo, &len);
if ((rv = traverse(node, &get_path_length_fo.traverse_fo)) < 0)
return rv;
if (!(*path = kmalloc(len + 1)))
return -ENOMEM;
write_path_kernel_fo_init(&write_path_kernel_fo, *path);
if ((rv = traverse(node, &write_path_kernel_fo.traverse_fo)) < 0)
kfree(*path, len + 1);
else
(*path)[len] = '\0';
return rv;
}