diff options
-rw-r--r-- | .gitignore | 153 | ||||
-rw-r--r-- | context.c | 237 | ||||
-rw-r--r-- | fdisk.c | 59 | ||||
-rw-r--r-- | fdisk.h | 40 | ||||
-rw-r--r-- | label.c | 147 | ||||
-rw-r--r-- | partition.c | 150 | ||||
-rw-r--r-- | setup.py | 10 |
7 files changed, 796 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06109db --- /dev/null +++ b/.gitignore @@ -0,0 +1,153 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/context.c b/context.c new file mode 100644 index 0000000..8c0d3fa --- /dev/null +++ b/context.c @@ -0,0 +1,237 @@ +#include "fdisk.h" + +static PyMemberDef Context_members[] = { + { NULL } +}; + + +static void Context_dealloc(ContextObject *self) +{ + if (!self->cxt) /* if init fails */ + return; + + fdisk_unref_context(self->cxt); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject *Context_new(PyTypeObject *type, + PyObject *args __attribute__((unused)), + PyObject *kwds __attribute__((unused))) +{ + ContextObject *self = (ContextObject*) type->tp_alloc(type, 0); + + if (self) { + self->cxt = NULL; + self->lb = NULL; + self->tb = NULL; + } + + return (PyObject *)self; +} + +#define Context_HELP "Context(device=None, details=None)" +static int Context_init(ContextObject *self, PyObject *args, PyObject *kwds) +{ + char *device = NULL; + int details, rc = 0; + char *kwlist[] = { + "device", "details", + NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args, + kwds, "|sp", kwlist, + &device, &details)) { + PyErr_SetString(PyExc_TypeError, ARG_ERR); + return -1; + } + + if (self->cxt) + fdisk_unref_context(self->cxt); + + self->cxt = fdisk_new_context(); + if (!self->cxt) { + PyErr_SetString(PyExc_MemoryError, "Couldn't allocate context"); + return -1; + } + + /* XXX: always readonly */ + if (device && (rc = fdisk_assign_device(self->cxt, device, 1))) + return -1; + if (details && (rc = fdisk_enable_details(self->cxt, details))) + return -1; + + fdisk_get_partitions(self->cxt, &self->tb); + + return 0; +} + + +#define Context_assign_device_HELP "assign_device(device)\n\n" \ + "Open the device, discovery topology, geometry, detect disklabel " \ + "and switch the current label driver to reflect the probing result. " +static PyObject *Context_assign_device(ContextObject *self, PyObject *args, PyObject *kwds) +{ + char *fname; + + if (!PyArg_ParseTuple(args, "s", &fname)) { + PyErr_SetString(PyExc_TypeError, ARG_ERR); + return NULL; + } + + /* Assert self->cxt */ + + /* XXX: readonly */ + fdisk_assign_device(self->cxt, fname, 1); + + self->lb = fdisk_get_label(self->cxt, NULL); + fdisk_get_partitions(self->cxt, &self->tb); + + /* XXX: check rc*/ + Py_INCREF(Py_None); + return Py_None; +} +#define Context_partition_to_string_HELP "partition_to_string(pa, field)\n\n" \ + "Retrieve partition field using fdisk_partition_to_string." \ + "Field constants are available as FDISK_LABEL_*" +static PyObject *Context_partition_to_string(ContextObject *self, PyObject *args, PyObject *kwds) +{ + struct fdisk_partition *pa; + enum fdisk_fieldtype field; + PartitionObject *part; + PyObject *ret; + char *data; + + if (!PyArg_ParseTuple(args, "O!i", &PartitionType, &part, &field)) { + PyErr_SetString(PyExc_TypeError, ARG_ERR); + return NULL; + } + + pa = part->pa; + + fdisk_partition_to_string(pa, self->cxt, field, &data); + ret = Py_BuildValue("s", data); + free(data); + + return ret; +} +static PyMethodDef Context_methods[] = { + {"assign_device", (PyCFunction)Context_assign_device, METH_VARARGS, Context_assign_device_HELP}, + {"partition_to_string", (PyCFunction)Context_partition_to_string, METH_VARARGS, Context_partition_to_string_HELP}, + {NULL} +}; + + +static PyObject *Context_get_nsectors(ContextObject *self) +{ + return PyLong_FromUnsignedLong(fdisk_get_nsectors(self->cxt)); +} +static PyObject *Context_get_sector_size(ContextObject *self) +{ + return PyLong_FromUnsignedLong(fdisk_get_sector_size(self->cxt)); +} +static PyObject *Context_get_devname(ContextObject *self) +{ + return PyObjectResultStr(fdisk_get_devname(self->cxt)); +} +static PyObject *Context_get_label(ContextObject *self) +{ + struct fdisk_context *cxt = self->cxt; + + if (fdisk_has_label(cxt)) { + return PyObjectResultLabel(fdisk_get_label(cxt, NULL)); + } else { + Py_RETURN_NONE; + } +} +static PyObject *Context_get_nparts(ContextObject *self) +{ + return PyLong_FromLong(fdisk_table_get_nents(self->tb)); +} +static PyObject *Context_get_parts(ContextObject *self) +{ + PyObject *p, *list = PyList_New(0); /* XXX: null if failed*/ + struct fdisk_partition *pa; + struct fdisk_iter *itr; + struct fdisk_table *tb; + /* char *data; */ + + tb = self->tb; + itr = fdisk_new_iter(FDISK_ITER_FORWARD); + + while(fdisk_table_next_partition(tb, itr, &pa) == 0) { + /* const char *name = fdisk_partition_get_name(pa);*/ + p = PyObjectResultPartition(pa); + PyList_Append(list, p); + /*free(data);*/ + } + + fdisk_free_iter(itr); + + return list; +} +static PyGetSetDef Context_getseters[] = { + {"nsectors", (getter)Context_get_nsectors, NULL, "context number of sectors", NULL}, + {"sector_size", (getter)Context_get_sector_size, NULL, "context sector size", NULL}, + {"devname", (getter)Context_get_devname, NULL, "context devname", NULL}, + {"label", (getter)Context_get_label, NULL, "context label type", NULL}, + {"nparts", (getter)Context_get_nparts, NULL, "context label number of existing partitions", NULL}, + {"parts", (getter)Context_get_parts, NULL, "context partitions", NULL}, + {NULL} +}; + +static PyObject *Context_repr(ContextObject *self) +{ + return PyUnicode_FromFormat("<libfdisk.Context object at %p, details=%s>", + self, fdisk_is_details(self->cxt) ? "True" : "False"); +} + +PyTypeObject ContextType = { + PyVarObject_HEAD_INIT(NULL, 0) + "libfdisk.Context", /*tp_name*/ + sizeof(ContextObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Context_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + NULL, /*tp_getattr*/ + NULL, /*tp_setattr*/ + NULL, /*tp_compare*/ + (reprfunc) Context_repr, + NULL, /*tp_as_number*/ + NULL, /*tp_as_sequence*/ + NULL, /*tp_as_mapping*/ + NULL, /*tp_hash */ + NULL, /*tp_call*/ + NULL, /*tp_str*/ + NULL, /*tp_getattro*/ + NULL, /*tp_setattro*/ + NULL, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Context_HELP, /* tp_doc */ + NULL, /* tp_traverse */ + NULL, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + Context_methods, /* tp_methods */ + Context_members, /* tp_members */ + Context_getseters, /* tp_getset */ + NULL, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Context_init, /* tp_init */ + NULL, /* tp_alloc */ + Context_new, /* tp_new */ +}; + +void Context_AddModuleObject(PyObject *mod) +{ + if (PyType_Ready(&ContextType) < 0) + return; + + Py_INCREF(&ContextType); + PyModule_AddObject(mod, "Context", (PyObject *)&ContextType); +} @@ -0,0 +1,59 @@ +#include <dirent.h> + +#include "fdisk.h" + +PyObject *PyObjectResultStr(const char *s) +{ + PyObject *result; + if (!s) + /* TODO: maybe lie about it and return "": + * which would allow for + * fs = libmount.Fs() + * fs.comment += "comment" + return Py_BuildValue("s", ""); */ + Py_RETURN_NONE; + result = Py_BuildValue("s", s); + if (!result) + PyErr_SetString(PyExc_RuntimeError, CONSTRUCT_ERR); + return result; +} + +static PyMethodDef FdiskMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + +static struct PyModuleDef fdiskmodule = { + PyModuleDef_HEAD_INIT, + "fdisk", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + FdiskMethods +}; + +PyMODINIT_FUNC +PyInit_fdisk(void) +{ + PyObject *m = PyModule_Create(&fdiskmodule); + + PyModule_AddIntConstant(m, "FDISK_SIZEUNIT_BYTES", FDISK_SIZEUNIT_BYTES); + PyModule_AddIntConstant(m, "FDISK_SIZEUNIT_HUMAN", FDISK_SIZEUNIT_HUMAN); + + PyModule_AddIntConstant(m, "FDISK_FIELD_DEVICE", FDISK_FIELD_DEVICE); + PyModule_AddIntConstant(m, "FDISK_FIELD_SIZE", FDISK_FIELD_SIZE); + PyModule_AddIntConstant(m, "FDISK_FIELD_TYPE", FDISK_FIELD_TYPE); + PyModule_AddIntConstant(m, "FDISK_FIELD_TYPEID", FDISK_FIELD_TYPEID); + PyModule_AddIntConstant(m, "FDISK_FIELD_FSTYPE", FDISK_FIELD_FSTYPE); + + PyModule_AddIntConstant(m, "FDISK_DISKLABEL_DOS", FDISK_DISKLABEL_DOS); + PyModule_AddIntConstant(m, "FDISK_DISKLABEL_GPT", FDISK_DISKLABEL_GPT); + PyModule_AddIntConstant(m, "FDISK_ITER_FORWARD", FDISK_ITER_FORWARD); + PyModule_AddIntConstant(m, "FDISK_ITER_BACKWARD", FDISK_ITER_BACKWARD); + + Context_AddModuleObject(m); + Label_AddModuleObject(m); + + + return m; +} @@ -0,0 +1,40 @@ +#ifndef UTIL_LINUX_PYLIBFDISK_H +#define UTIL_LINUX_PYLIBFDISK_H + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <structmember.h> + +#include <libfdisk/libfdisk.h> + +#define CONSTRUCT_ERR "Error during object construction" +#define ARG_ERR "Invalid number or type of arguments" + +typedef struct { + PyObject_HEAD + struct fdisk_context *cxt; + struct fdisk_label *lb; + struct fdisk_table *tb; +} ContextObject; + +typedef struct { + PyObject_HEAD + struct fdisk_label *lb; +} LabelObject; + +typedef struct { + PyObject_HEAD + struct fdisk_partition *pa; +} PartitionObject; + +extern PyTypeObject ContextType; +extern PyTypeObject PartitionType; + +extern void Context_AddModuleObject(PyObject *mod); +extern void Label_AddModuleObject(PyObject *mod); + +extern PyObject *PyObjectResultStr(const char *s); +extern PyObject *PyObjectResultLabel(struct fdisk_label *lb); +extern PyObject *PyObjectResultPartition(struct fdisk_partition *pa); + +#endif @@ -0,0 +1,147 @@ +#include "fdisk.h" + + +static PyMemberDef Label_members[] = { + { NULL } +}; + + +static void Label_dealloc(LabelObject *self) +{ + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject *Label_new(PyTypeObject *type, + PyObject *args __attribute__((unused)), + PyObject *kwds __attribute__((unused))) +{ + LabelObject *self = (LabelObject*) type->tp_alloc(type, 0); + + if (self) { + self->lb = NULL; + } + + return (PyObject *)self; +} + +#define Label_HELP "Label(context=None)" +static int Label_init(LabelObject *self, PyObject *args, PyObject *kwds) +{ + ContextObject *cxt = NULL; + struct fdisk_label *lb; + char *kwlist[] = { + "context", + NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args, + kwds, "|O!", kwlist, + &ContextType, &cxt)) { + PyErr_SetString(PyExc_TypeError, "Error"); + return -1; + } + + if (cxt && (lb = fdisk_get_label(cxt->cxt, NULL))) { + self->lb = lb; + } + + return 0; +} + + +static PyMethodDef Label_methods[] = { + {NULL} +}; + + +static PyObject *Label_get_type(LabelObject *self) +{ + return PyLong_FromLong(fdisk_label_get_type(self->lb)); +} +static PyObject *Label_get_name(LabelObject *self) +{ + return PyObjectResultStr(fdisk_label_get_name(self->lb)); +} +static PyGetSetDef Label_getseters[] = { + {"type", (getter)Label_get_type, NULL, "label type", NULL}, + {"name", (getter)Label_get_name, NULL, "label name", NULL}, + {NULL} +}; + +static PyObject *Label_repr(LabelObject *self) +{ + return PyUnicode_FromFormat("<libfdisk.Label object at %p, name=%s>", + self, fdisk_label_get_name(self->lb)); +} + +PyTypeObject LabelType = { + PyVarObject_HEAD_INIT(NULL, 0) + "libfdisk.Label", /*tp_name*/ + sizeof(LabelObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Label_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + NULL, /*tp_getattr*/ + NULL, /*tp_setattr*/ + NULL, /*tp_compare*/ + (reprfunc) Label_repr, + NULL, /*tp_as_number*/ + NULL, /*tp_as_sequence*/ + NULL, /*tp_as_mapping*/ + NULL, /*tp_hash */ + NULL, /*tp_call*/ + NULL, /*tp_str*/ + NULL, /*tp_getattro*/ + NULL, /*tp_setattro*/ + NULL, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Label_HELP, /* tp_doc */ + NULL, /* tp_traverse */ + NULL, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + Label_methods, /* tp_methods */ + Label_members, /* tp_members */ + Label_getseters, /* tp_getset */ + NULL, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Label_init, /* tp_init */ + NULL, /* tp_alloc */ + Label_new, /* tp_new */ +}; + +PyObject *PyObjectResultLabel(struct fdisk_label *lb) +{ + LabelObject *result; + + if (!lb) { + PyErr_SetString(PyExc_AssertionError, "lb assert failed"); + return NULL; + } + + + result = PyObject_New(LabelObject, &LabelType); + if (!result) { + PyErr_SetString(PyExc_MemoryError, "Couldn't allocate Label object"); + return NULL; + } + + /* Py_INCREF(result); */ + + result->lb = lb; + return (PyObject *) result; +} + +void Label_AddModuleObject(PyObject *mod) +{ + if (PyType_Ready(&LabelType) < 0) + return; + + Py_INCREF(&LabelType); + PyModule_AddObject(mod, "Label", (PyObject *)&LabelType); +} diff --git a/partition.c b/partition.c new file mode 100644 index 0000000..48f410a --- /dev/null +++ b/partition.c @@ -0,0 +1,150 @@ +#include "fdisk.h" + + +static PyMemberDef Partition_members[] = { + { NULL } +}; + + +static void Partition_dealloc(PartitionObject *self) +{ + if (self->pa) + fdisk_unref_partition(self->pa); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject *Partition_new(PyTypeObject *type, + PyObject *args __attribute__((unused)), + PyObject *kwds __attribute__((unused))) +{ + PartitionObject *self = (PartitionObject*) type->tp_alloc(type, 0); + + if (self) + self->pa = NULL; + + return (PyObject *)self; +} + +#define Partition_HELP "Partition()" +static int Partition_init(PartitionObject *self, PyObject *args, PyObject *kwds) +{ + /* + char *kwlist[] = { + "context", + NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args, + kwds, "|O!", kwlist, + &ContextType, &cxt)) { + PyErr_SetString(PyExc_TypeError, "Error"); + return -1; + } + */ + + self->pa = fdisk_new_partition(); + + return 0; +} + + +static PyMethodDef Partition_methods[] = { + {NULL} +}; + + +static PyObject *Partition_get_partno(PartitionObject *self) +{ + if (fdisk_partition_has_partno(self->pa)) { + return PyLong_FromSize_t(fdisk_partition_get_partno(self->pa)); + } + // Py_RETURN_NONE; + return Py_BuildValue("%d", -1); +} +static PyObject *Partition_get_size(PartitionObject *self) +{ + if (fdisk_partition_has_size(self->pa)) { + return PyLong_FromUnsignedLongLong(fdisk_partition_get_size(self->pa)); + } + Py_RETURN_NONE; +} +static PyGetSetDef Partition_getseters[] = { + {"partno", (getter)Partition_get_partno, NULL, "partition number", NULL}, + {"size", (getter)Partition_get_size, NULL, "number of sectors", NULL}, + {NULL} +}; + +static PyObject *Partition_repr(PartitionObject *self) +{ + return PyUnicode_FromFormat("<libfdisk.Partition object at %p>", + self); +} + +PyTypeObject PartitionType = { + PyVarObject_HEAD_INIT(NULL, 0) + "libfdisk.Partition", /*tp_name*/ + sizeof(PartitionObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Partition_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + NULL, /*tp_getattr*/ + NULL, /*tp_setattr*/ + NULL, /*tp_compare*/ + (reprfunc) Partition_repr, + NULL, /*tp_as_number*/ + NULL, /*tp_as_sequence*/ + NULL, /*tp_as_mapping*/ + NULL, /*tp_hash */ + NULL, /*tp_call*/ + NULL, /*tp_str*/ + NULL, /*tp_getattro*/ + NULL, /*tp_setattro*/ + NULL, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Partition_HELP, /* tp_doc */ + NULL, /* tp_traverse */ + NULL, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + Partition_methods, /* tp_methods */ + Partition_members, /* tp_members */ + Partition_getseters, /* tp_getset */ + NULL, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Partition_init, /* tp_init */ + NULL, /* tp_alloc */ + Partition_new, /* tp_new */ +}; + +PyObject *PyObjectResultPartition(struct fdisk_partition *pa) +{ + PartitionObject *result; + + if (!pa) { + PyErr_SetString(PyExc_AssertionError, "pa assert failed"); + return NULL; + } + + result = PyObject_New(PartitionObject, &PartitionType); + if (!result) { + PyErr_SetString(PyExc_MemoryError, "Couldn't allocate Partition object"); + return NULL; + } + + result->pa = pa; + return (PyObject *) result; +} + +void Partition_AddModuleObject(PyObject *mod) +{ + if (PyType_Ready(&PartitionType) < 0) + return; + + Py_INCREF(&PartitionType); + PyModule_AddObject(mod, "Partition", (PyObject *)&PartitionType); +} diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..aed3664 --- /dev/null +++ b/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, Extension + +libfdisk = Extension('fdisk', + libraries = ['fdisk'], + sources = ['fdisk.c', 'context.c', 'label.c', 'partition.c']) + +setup (name = 'libfdisk', + version = '1.0', + description = 'Python bindings for libfdisk', + ext_modules = [libfdisk]) |