From 1c9e36639b949cc765dab316825f9fec7af85a6e Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Tue, 1 Dec 2015 21:11:27 +0100 Subject: Computed relative and absolute paths. --- ChangeLog | 29 ++++++ plugins/pychrysa/common/Makefile.am | 3 +- plugins/pychrysa/common/fnv1a.c | 2 +- plugins/pychrysa/common/module.c | 2 + plugins/pychrysa/common/pathname.c | 203 ++++++++++++++++++++++++++++++++++++ plugins/pychrysa/common/pathname.h | 42 ++++++++ src/common/Makefile.am | 1 + src/common/extstr.h | 2 +- src/common/pathname.c | 173 ++++++++++++++++++++++++++++++ src/common/pathname.h | 36 +++++++ tests/common/__init__.py | 0 tests/common/pathname.py | 95 +++++++++++++++++ 12 files changed, 585 insertions(+), 3 deletions(-) create mode 100644 plugins/pychrysa/common/pathname.c create mode 100644 plugins/pychrysa/common/pathname.h create mode 100644 src/common/pathname.c create mode 100644 src/common/pathname.h create mode 100644 tests/common/__init__.py create mode 100644 tests/common/pathname.py diff --git a/ChangeLog b/ChangeLog index 57bbdb9..a06eeaf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +15-12-01 Cyrille Bagard + + * plugins/pychrysa/common/Makefile.am: + Add the 'pathname.[ch]' files to libpychrysacommon_la_SOURCES. + + * plugins/pychrysa/common/fnv1a.c: + Fix comments. + + * plugins/pychrysa/common/module.c: + Update code. + + * plugins/pychrysa/common/pathname.c: + * plugins/pychrysa/common/pathname.h: + New entries: provide bindings for Python. + + * src/common/Makefile.am: + Add the 'pathname.[ch]' files to libcommon_la_SOURCES. + + * src/common/extstr.h: + Typo. + + * src/common/pathname.c: + * src/common/pathname.h: + New entries: compute relative and absolute paths. + + * tests/common/__init__.py: + * tests/common/pathname.py: + New entries: define some new relative test cases. + 15-11-29 Cyrille Bagard * configure.ac: diff --git a/plugins/pychrysa/common/Makefile.am b/plugins/pychrysa/common/Makefile.am index 38d1697..93d92ac 100644 --- a/plugins/pychrysa/common/Makefile.am +++ b/plugins/pychrysa/common/Makefile.am @@ -3,7 +3,8 @@ noinst_LTLIBRARIES = libpychrysacommon.la libpychrysacommon_la_SOURCES = \ fnv1a.h fnv1a.c \ - module.h module.c + module.h module.c \ + pathname.h pathname.c libpychrysacommon_la_LDFLAGS = diff --git a/plugins/pychrysa/common/fnv1a.c b/plugins/pychrysa/common/fnv1a.c index 2befcbe..99d06fc 100644 --- a/plugins/pychrysa/common/fnv1a.c +++ b/plugins/pychrysa/common/fnv1a.c @@ -40,7 +40,7 @@ static PyObject *py_fnv1a_hash(PyObject *, PyObject *); /****************************************************************************** * * * Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* args = arguments fournis lors de l'appel à la fonction. * * * * Description : Détermine l'empreinte FNV1a d'une chaîne de caractères. * * * diff --git a/plugins/pychrysa/common/module.c b/plugins/pychrysa/common/module.c index bb4d47f..21781b5 100644 --- a/plugins/pychrysa/common/module.c +++ b/plugins/pychrysa/common/module.c @@ -26,6 +26,7 @@ #include "fnv1a.h" +#include "pathname.h" @@ -76,6 +77,7 @@ bool add_common_module_to_python_module(PyObject *super) result = true; result &= register_python_fnv1a(module); + result &= register_python_pathname(module); acmtpm_exit: diff --git a/plugins/pychrysa/common/pathname.c b/plugins/pychrysa/common/pathname.c new file mode 100644 index 0000000..4b36675 --- /dev/null +++ b/plugins/pychrysa/common/pathname.c @@ -0,0 +1,203 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * pathname.c - équivalent Python du fichier "common/pathname.c" + * + * Copyright (C) 2015 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * OpenIDA 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. + * + * OpenIDA 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "pathname.h" + + +#include +#include + + +#include + + +#include + + + +/* Calcule le chemin relatif entre deux fichiers donnés. */ +static PyObject *py_build_relative_filename(PyObject *, PyObject *); + +/* Calcule le chemin absolu d'un fichier par rapport à un autre. */ +static PyObject *py_build_absolute_filename(PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Calcule le chemin relatif entre deux fichiers donnés. * +* * +* Retour : Chemin relatif obtenu. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_build_relative_filename(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + const char *ref; /* Fichier de référence */ + const char *target; /* Fichier à cibler */ + int ret; /* Bilan de lecture des args. */ + char *relative; /* Chemin d'accès construit */ + + ret = PyArg_ParseTuple(args, "ss", &ref, &target); + if (!ret) Py_RETURN_NONE; + + relative = build_relative_filename(ref, target); + + result = PyUnicode_FromString(relative); + + free(relative); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = arguments fournis lors de l'appel à la fonction. * +* * +* Description : Calcule le chemin absolu d'un fichier par rapport à un autre.* +* * +* Retour : Chemin absolu obtenu. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_build_absolute_filename(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + const char *ref; /* Fichier de référence */ + const char *target; /* Fichier à cibler */ + int ret; /* Bilan de lecture des args. */ + char *relative; /* Chemin d'accès construit */ + + ret = PyArg_ParseTuple(args, "ss", &ref, &target); + if (!ret) Py_RETURN_NONE; + + relative = build_absolute_filename(ref, target); + + if (relative == NULL) + { + PyErr_SetString(PyExc_ValueError, _("Relative path is too deep.")); + result = NULL; + } + else + { + result = PyUnicode_FromString(relative); + + free(relative); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_pathname_type(void) +{ + static PyMethodDef py_pathname_methods[] = { + + { "build_relative_filename", py_build_relative_filename, + METH_VARARGS | METH_STATIC, + "build_relative_filename(ref, target, /)\n--\n\nCompute the relative path between two files." + }, + { "build_absolute_filename", py_build_absolute_filename, + METH_VARARGS | METH_STATIC, + "build_absolute_filename(ref, target, /)\n--\n\nCompute the absolute path for a file." + }, + { NULL } + + }; + + static PyTypeObject py_pathname_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.core.pathname", + .tp_basicsize = sizeof(PyObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT, + + .tp_doc = "Path manipulations in Python for Chrysalide.", + + .tp_methods = py_pathname_methods + + }; + + return &py_pathname_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.core.pathname'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool register_python_pathname(PyObject *module) +{ + PyTypeObject *py_pathname_type; /* Type Python pour 'pathname' */ + int ret; /* Bilan d'un appel */ + + py_pathname_type = get_python_pathname_type(); + + //py_pathname_type->tp_new = PyType_GenericNew; + + if (PyType_Ready(py_pathname_type) != 0) + return false; + + Py_INCREF(py_pathname_type); + ret = PyModule_AddObject(module, "pathname", (PyObject *)py_pathname_type); + + return (ret == 0); + +} diff --git a/plugins/pychrysa/common/pathname.h b/plugins/pychrysa/common/pathname.h new file mode 100644 index 0000000..fe9bc1b --- /dev/null +++ b/plugins/pychrysa/common/pathname.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * pathname.h - prototypes pour l'équivalent Python du fichier "common/pathname.c" + * + * Copyright (C) 2015 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * OpenIDA 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. + * + * OpenIDA 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_COMMON_PATHNAME_H +#define _PLUGINS_PYCHRYSALIDE_COMMON_PATHNAME_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_pathname_type(void); + +/* Prend en charge l'objet 'pychrysalide.common.pathname'. */ +bool register_python_pathname(PyObject *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_COMMON_PATHNAME_H */ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 7a35813..4c9c2a1 100755 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -15,6 +15,7 @@ libcommon_la_SOURCES = \ leb128.h leb128.c \ macros.h \ net.h net.c \ + pathname.h pathname.c \ sqlite.h sqlite.c \ xdg.h xdg.c \ xml.h xml.c diff --git a/src/common/extstr.h b/src/common/extstr.h index b8bd225..6542d2f 100644 --- a/src/common/extstr.h +++ b/src/common/extstr.h @@ -30,7 +30,7 @@ /* Complète une chaîne de caractères avec une autre. */ -char *stradd(char *str1, const char *str2); +char *stradd(char *, const char *); /* Complète une chaîne de caractères avec une autre. */ char *strnadd(char *, const char *, size_t); diff --git a/src/common/pathname.c b/src/common/pathname.c new file mode 100644 index 0000000..181fd1f --- /dev/null +++ b/src/common/pathname.c @@ -0,0 +1,173 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * pathname.c - manipulation de chemins de fichiers + * + * Copyright (C) 2015 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * OpenIDA 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. + * + * OpenIDA 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 Foobar. If not, see . + */ + + +#include "pathname.h" + + +#include +#include +#include +#include + + +#include "extstr.h" + + + +/****************************************************************************** +* * +* Paramètres : ref = fichier servant de référence aux calculs. * +* target = fichier absolu ciblé par la procédure. * +* * +* Description : Calcule le chemin relatif entre deux fichiers donnés. * +* * +* Retour : Chemin d'accès déterminé. * +* * +* Remarques : Les chemins de type 'a//b' ne sont pas supportés. * +* * +******************************************************************************/ + +char *build_relative_filename(const char *ref, const char *target) +{ + char *result; /* Chemin à retourner */ + size_t common; /* Taille de la partie commune */ + const char *found; /* Séparateur suivant rencontré*/ + size_t ref_next; /* Prochain séparateur #1 */ + size_t target_next; /* Prochain séparateur #2 */ + int ret; /* Bilan d'un appel */ + unsigned int levels; /* Niveaux de décalage */ + unsigned int i; /* Boucle de parcours #1 */ + + common = 0; + + /* Recherche d'une base commune */ + + while (1) + { + found = strchr(ref + common, G_DIR_SEPARATOR); + if (found == NULL) break; + + ref_next = found - ref; + + found = strchr(target + common, G_DIR_SEPARATOR); + if (found == NULL) break; + + target_next = found - target; + + /* Comparaison rapide sur la longeur du nom */ + if (ref_next != target_next) break; + + /* Comparaison sur une portion de chemin */ + ret = strncmp(ref + common, target + common, ref_next - common); + if (ret != 0) break; + + common = ref_next + 1; + + } + + /* Décompte du décalage entre la référence et la cible */ + + found = ref + common; + + for (levels = 0; ; levels++) + { + found = strchr(found, G_DIR_SEPARATOR); + if (found == NULL) break; + + found++; + + } + + /* Construction du résultat final */ + + result = strdup(target + common); + + for (i = 0; i < levels; i++) + { + result = strprep(result, G_DIR_SEPARATOR_S); + result = strprep(result, ".."); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : ref = fichier servant de référence aux calculs. * +* target = fichier relatif ciblé par la procédure. * +* * +* Description : Calcule le chemin absolu d'un fichier par rapport à un autre.* +* * +* Retour : Chemin d'accès déterminé ou NULL en cas d'erreur. * +* * +* Remarques : Les chemins de type 'a//b' ne sont pas supportés. * +* * +******************************************************************************/ + +char *build_absolute_filename(const char *ref, const char *target) +{ + char *result; /* Chemin à retourner */ + char *last_sep; /* Dernier séparateur trouvé */ + const char *target_base; /* Base de la relativité */ + + static const char upper[4] = { '.', '.', G_DIR_SEPARATOR, '\0' }; + + result = strdup(ref); + + last_sep = strrchr(result, G_DIR_SEPARATOR); + assert(last_sep != NULL); + + target_base = target; + + /* Remontée des répertoires */ + while (1) + { + if (strncmp(target_base, upper, 3) != 0) + break; + + target_base += 3; + + *last_sep = '\0'; + last_sep = strrchr(result, G_DIR_SEPARATOR); + + /* S'il devient impossible de remonter autant... */ + if (last_sep == NULL) break; + + } + + if (last_sep == NULL) + { + free(result); + result = NULL; + } + else + { + *(last_sep + 1) = '\0'; + result = stradd(result, target_base); + } + + return result; + +} diff --git a/src/common/pathname.h b/src/common/pathname.h new file mode 100644 index 0000000..744a11b --- /dev/null +++ b/src/common/pathname.h @@ -0,0 +1,36 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * pathname.h - prototypes pour la manipulation de chemins de fichiers + * + * Copyright (C) 2015 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * OpenIDA 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. + * + * OpenIDA 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 Foobar. If not, see . + */ + + +#ifndef _COMMON_PATHNAME_H +#define _COMMON_PATHNAME_H + + +/* Calcule le chemin relatif entre deux fichiers donnés. */ +char *build_relative_filename(const char *, const char *); + +/* Calcule le chemin absolu d'un fichier par rapport à un autre. */ +char *build_absolute_filename(const char *, const char *); + + + +#endif /* _COMMON_PATHNAME_H */ diff --git a/tests/common/__init__.py b/tests/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/common/pathname.py b/tests/common/pathname.py new file mode 100644 index 0000000..5cd238c --- /dev/null +++ b/tests/common/pathname.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3-dbg +# -*- coding: utf-8 -*- + + +# Tests minimalistes pour valider la construction de chemins relatifs et absolus. + + +from chrysacase import ChrysalideTestCase +from pychrysalide.common import pathname + + +class TestRestrictedContent(ChrysalideTestCase): + """TestCase for common.pathname.build*""" + + @classmethod + def setUpClass(cls): + + super(TestRestrictedContent, cls).setUpClass() + + cls._tests = [ + { + 'ref' : '/a/b/d', + 'target' : '/a/b/e/f', + 'relative' : 'e/f' + }, + { + 'ref' : '/a/b/c/d', + 'target' : '/a/b/f', + 'relative' : '../f' + }, + { + 'ref' : '/a/b/c/d', + 'target' : '/a/b/c/e', + 'relative' : 'e' + }, + { + 'ref' : '/a/bb/c/d', + 'target' : '/a/b/e/f', + 'relative' : '../../b/e/f' + }, + { + 'ref' : '/a/b/c/d', + 'target' : '/a/bb/e/f', + 'relative' : '../../bb/e/f' + }, + { + 'ref' : '/a/b/c/d', + 'target' : '/f', + 'relative' : '../../../f' + }, + { + 'ref' : '/z/b/c/d', + 'target' : '/a/b/e/f', + 'relative' : '../../../a/b/e/f' + }, + { + 'ref' : '/a/bbb/c/d', + 'target' : '/a/bbc/e/f', + 'relative' : '../../bbc/e/f' + } + ] + + + def testBuildingRelative(self): + """Build valid relative paths.""" + + build_relative = pathname.build_relative_filename + + for tst in self._tests: + + got = build_relative(tst['ref'], tst['target']) + + self.assertEqual(got, tst['relative']) + + + def testBuildingAbsolute(self): + """Build valid absolute paths.""" + + build_absolute = pathname.build_absolute_filename + + for tst in self._tests: + + got = build_absolute(tst['ref'], tst['relative']) + + self.assertEqual(got, tst['target']) + + + def testBuildingWrongAbsolute(self): + """Build invalid absolute paths.""" + + build_absolute = pathname.build_absolute_filename + + with self.assertRaisesRegex(Exception, 'Relative path is too deep.'): + + got = build_absolute('/a/b', '../../c') -- cgit v0.11.2-87-g4458