/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "node.h" #include #include #include #include #include #include #include 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; }; /* get_path_length */ 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; } /* write_path */ 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; } /* write_path_kernel */ 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; } /* traverse */ 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); } /* Fonctions exportees */ 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; }