summaryrefslogtreecommitdiffstats
path: root/libmount/python
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
commitcfe5e3905201349e9cf3f95d52ff4bd100bde37d (patch)
treed0baf160cbee3195249d095f85e52d20c21acf02 /libmount/python
parentInitial commit. (diff)
downloadutil-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.tar.xz
util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.zip
Adding upstream version 2.39.3.upstream/2.39.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libmount/python')
-rw-r--r--libmount/python/Makemodule.am35
-rw-r--r--libmount/python/__init__.py2
-rw-r--r--libmount/python/context.c1228
-rw-r--r--libmount/python/fs.c883
-rw-r--r--libmount/python/meson.build41
-rw-r--r--libmount/python/pylibmount.c326
-rw-r--r--libmount/python/pylibmount.h131
-rw-r--r--libmount/python/tab.c783
-rwxr-xr-xlibmount/python/test_mount_context.py173
-rwxr-xr-xlibmount/python/test_mount_tab.py164
-rwxr-xr-xlibmount/python/test_mount_tab_update.py59
11 files changed, 3825 insertions, 0 deletions
diff --git a/libmount/python/Makemodule.am b/libmount/python/Makemodule.am
new file mode 100644
index 0000000..b338ea0
--- /dev/null
+++ b/libmount/python/Makemodule.am
@@ -0,0 +1,35 @@
+if BUILD_PYLIBMOUNT
+
+pylibmountexecdir = $(pyexecdir)/libmount
+
+# Please, don't use $pythondir for the scripts. We have to use the same
+# directory for binary stuff as well as for the scripts otherwise it's
+# not possible to install 32-bit and 64-bit version on the same system.
+pylibmountexec_LTLIBRARIES = pylibmount.la
+pylibmountexec_PYTHON = libmount/python/__init__.py
+
+pylibmount_la_SOURCES = \
+ libmount/python/pylibmount.c \
+ libmount/python/pylibmount.h \
+ libmount/python/fs.c \
+ libmount/python/tab.c
+if LINUX
+pylibmount_la_SOURCES += libmount/python/context.c
+endif
+
+pylibmount_la_LIBADD = libmount.la $(PYTHON_LIBS)
+
+pylibmount_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(PYTHON_CFLAGS) $(PYTHON_WARN_CFLAGS) \
+ -I$(ul_libmount_incdir) \
+ -fno-strict-aliasing #-ggdb3 -O0
+
+pylibmount_la_LDFLAGS = \
+ -avoid-version -module -shared -export-dynamic
+
+dist_check_SCRIPTS += libmount/python/test_mount_context.py
+dist_check_SCRIPTS += libmount/python/test_mount_tab.py
+dist_check_SCRIPTS += libmount/python/test_mount_tab_update.py
+
+endif # BUILD_PYLIBMOUNT
diff --git a/libmount/python/__init__.py b/libmount/python/__init__.py
new file mode 100644
index 0000000..09104e2
--- /dev/null
+++ b/libmount/python/__init__.py
@@ -0,0 +1,2 @@
+from .pylibmount import *
+
diff --git a/libmount/python/context.c b/libmount/python/context.c
new file mode 100644
index 0000000..353a893
--- /dev/null
+++ b/libmount/python/context.c
@@ -0,0 +1,1228 @@
+/*
+ * Python bindings for the libmount library.
+ *
+ * Copyright (C) 2013, Red Hat, Inc. All rights reserved.
+ * Written by Ondrej Oprala and Karel Zak
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "pylibmount.h"
+
+static PyMemberDef Context_members[] = {
+ { NULL }
+};
+
+static PyObject *Context_set_tables_errcb(ContextObjext *self, PyObject *func,
+ void *closure __attribute__((unused)))
+{
+ if (!func) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return NULL;
+ }
+
+ if (!PyCallable_Check(func))
+ return NULL;
+
+ PyObject *tmp = self->table_errcb;
+ Py_INCREF(func);
+ self->table_errcb = func;
+ Py_XDECREF(tmp);
+
+ return UL_IncRef(self);
+}
+
+static void Context_dealloc(ContextObjext *self)
+{
+ if (!self->cxt) /* if init fails */
+ return;
+
+ Py_XDECREF(mnt_context_get_fs_userdata(self->cxt));
+ Py_XDECREF(mnt_context_get_fstab_userdata(self->cxt));
+ Py_XDECREF(mnt_context_get_mtab_userdata(self->cxt));
+
+ mnt_free_context(self->cxt);
+ PyFree(self);
+}
+
+static PyObject *Context_new(PyTypeObject *type,
+ PyObject *args __attribute__((unused)),
+ PyObject *kwds __attribute__((unused)))
+{
+ ContextObjext *self = (ContextObjext*) type->tp_alloc(type, 0);
+
+ if (self) {
+ self->cxt = NULL;
+ self->table_errcb = NULL;
+ }
+
+ return (PyObject *)self;
+}
+
+/*
+ * Note there is no pointer to encapsulating object needed here, since Cxt is
+ * on top of the Context(Table(Filesystem)) hierarchy
+ */
+#define Context_HELP "Context(source=None, target=None, fstype=None, " \
+ "options=None, mflags=0, fstype_pattern=None, " \
+ "options_pattern=None, fs=None, fstab=None, optsmode=0)"
+static int Context_init(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ char *source = NULL, *target = NULL, *fstype = NULL;
+ char *options = NULL, *fstype_pattern = NULL, *options_pattern = NULL;
+ unsigned long mflags = 0;
+ int optsmode = 0, syscall_status = 1;
+ FsObject *fs = NULL;
+ TableObject *fstab = NULL;
+ int rc = 0;
+ char *kwlist[] = {
+ "source", "target", "fstype",
+ "options", "mflags", "fstype_pattern",
+ "options_pattern", "fs", "fstab",
+ "optsmode", NULL
+ };
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "|sssskssO!O!i", kwlist,
+ &source, &target, &fstype, &options, &mflags,
+ &fstype_pattern, &options_pattern, &FsType, &fs,
+ &TableType, &fstab, &optsmode, &syscall_status)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+
+ if (self->cxt)
+ mnt_free_context(self->cxt);
+
+ self->cxt = mnt_new_context();
+ if (!self->cxt) {
+ PyErr_SetString(PyExc_MemoryError, MEMORY_ERR);
+ return -1;
+ }
+
+ if (source && (rc = mnt_context_set_source(self->cxt, source))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (target && (rc = mnt_context_set_target(self->cxt, target))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (fstype && (rc = mnt_context_set_fstype(self->cxt, fstype))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (options && (rc = mnt_context_set_options(self->cxt, options))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (fstype_pattern && (rc = mnt_context_set_fstype_pattern(self->cxt, fstype_pattern))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (options_pattern && (rc = mnt_context_set_options_pattern(self->cxt, options_pattern))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (fs && (rc = mnt_context_set_fs(self->cxt, fs->fs))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (fstab && (rc = mnt_context_set_fstab(self->cxt, fstab->tab))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ if (optsmode && (rc = mnt_context_set_optsmode(self->cxt, optsmode))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+
+ mnt_context_set_mflags(self->cxt, mflags);
+ mnt_context_set_optsmode(self->cxt, optsmode);
+ mnt_context_set_tables_errcb(self->cxt, pymnt_table_parser_errcb);
+
+ return 0;
+}
+
+#define Context_enable_fake_HELP "enable_fake(enable)\n\n" \
+ "Enable/disable fake mounting (see mount(8) man page, option -f).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_fake(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = { "enable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_fake(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_enable_force_HELP "enable_force(enable)\n\n" \
+ "Enable/disable force umounting (see umount(8) man page, option -f).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_force(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = { "enable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_force(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_enable_lazy_HELP "enable_lazy(enable)\n\n" \
+ "Enable/disable lazy umount (see umount(8) man page, option -l).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_lazy(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = { "enable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_lazy(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_enable_loopdel_HELP "enable_loopdel(enable)\n\n" \
+ "Enable/disable loop delete (destroy) after umount (see umount(8), option -d)\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_loopdel(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = { "enable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_loopdel(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_enable_rdonly_umount_HELP "enable_rdonly_umount(enable)\n\n" \
+ "Enable/disable read-only remount on failed umount(2)\n "\
+ "(see umount(8) man page, option -r).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_rdonly_umount(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = { "enable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_rdonly_umount(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_enable_sloppy_HELP "enable_sloppy(enable)\n\n" \
+ "Set/unset sloppy mounting (see mount(8) man page, option -s).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_sloppy(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = { "enable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_sloppy(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_enable_verbose_HELP "enable_verbose(enable)\n\n" \
+ "Enable/disable verbose output (TODO: not implemented yet)\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_verbose(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = { "enable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_verbose(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_enable_fork_HELP "enable_fork(enable)\n\n" \
+ "Enable/disable fork(2) call in Cxt.next_mount()(not yet implemented) (see mount(8) man\n" \
+ "page, option -F).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_enable_fork(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int enable;
+ char *kwlist[] = {"enable", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_enable_fork(self->cxt, enable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_disable_canonicalize_HELP "disable_canonicalize(disable)\n\n" \
+ "Enable/disable paths canonicalization and tags evaluation. The libmount context\n" \
+ "canonicalizes paths when searching fstab and when preparing source and target paths\n" \
+ "for mount(2) syscall.\n" \
+ "\n" \
+ "This function has effect to the private (within context) fstab instance only\n" \
+ "(see Cxt.fstab).\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_disable_canonicalize(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int disable;
+ char *kwlist[] = {"disable", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &disable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_disable_canonicalize(self->cxt, disable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_disable_helpers_HELP "disable_helpers(disable)\n\n" \
+ "Enable/disable /sbin/[u]mount.* helpers (see mount(8) man page, option -i).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_disable_helpers(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int disable;
+ char *kwlist[] = {"disable", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &disable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_disable_helpers(self->cxt, disable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_disable_mtab_HELP "disable_mtab(disable)\n\n" \
+ "Disable/enable mtab update (see mount(8) man page, option -n).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_disable_mtab(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int disable;
+ char *kwlist[] = {"disable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &disable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_disable_mtab(self->cxt, disable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_disable_swapmatch_HELP "disable_swapmatch(disable)\n\n" \
+ "Disable/enable swap between source and target for mount(8) if only one path\n" \
+ "is specified.\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_disable_swapmatch(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int disable;
+ char *kwlist[] = { "disable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &disable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_context_disable_swapmatch(self->cxt, disable);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+static int Context_set_source(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char *source;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(source = pystos(value)))
+ return -1;
+
+ rc = mnt_context_set_source(self->cxt, source);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static int Context_set_mountdata(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char *mountdata;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(mountdata = pystos(value)))
+ return -1;
+
+ rc = mnt_context_set_mountdata(self->cxt, mountdata);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static int Context_set_target(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char * target;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(target = pystos(value)))
+ return -1;
+
+ rc = mnt_context_set_target(self->cxt, target);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static int Context_set_fstype(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char * fstype;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(fstype = pystos(value)))
+ return -1;
+
+ rc = mnt_context_set_fstype(self->cxt, fstype);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static int Context_set_options(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char * options;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(options = pystos(value)))
+ return -1;
+
+ rc = mnt_context_set_options(self->cxt, options);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static int Context_set_fstype_pattern(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char * fstype_pattern;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(fstype_pattern = pystos(value)))
+ return -1;
+
+ rc = mnt_context_set_fstype_pattern(self->cxt, fstype_pattern);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static int Context_set_options_pattern(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char * options_pattern;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(options_pattern = pystos(value)))
+ return -1;
+
+ rc = mnt_context_set_options_pattern(self->cxt, options_pattern);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static int Context_set_fs(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ FsObject *fs;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!PyArg_Parse(value, "O!", &FsType, &fs)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+ Py_INCREF(fs);
+ Py_XDECREF(mnt_context_get_fs_userdata(self->cxt));
+
+ return mnt_context_set_fs(self->cxt, fs->fs);
+}
+
+static int Context_set_fstab(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ TableObject *fstab;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!PyArg_Parse(value, "O!", &TableType, &fstab)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+ Py_INCREF(fstab);
+ Py_XDECREF(mnt_context_get_fstab_userdata(self->cxt));
+
+ return mnt_context_set_fstab(self->cxt, fstab->tab);
+}
+
+static int Context_set_optsmode(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ int optsmode;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!PyLong_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+ optsmode = PyLong_AsLong(value);
+ return mnt_context_set_optsmode(self->cxt, optsmode);
+}
+
+static int Context_set_syscall_status(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ int syscall_status;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!PyLong_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+ syscall_status = PyLong_AsLong(value);
+ return mnt_context_set_syscall_status(self->cxt, syscall_status);
+}
+
+static int Context_set_user_mflags(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ unsigned long flags;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!PyLong_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+ flags = PyLong_AsUnsignedLong(value);
+ return mnt_context_set_mflags(self->cxt, flags);
+
+}
+
+static int Context_set_mflags(ContextObjext *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ unsigned long flags;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!PyLong_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+ flags = PyLong_AsUnsignedLong(value);
+ return mnt_context_set_mflags(self->cxt, flags);
+}
+
+/* returns a flags integer (behavior differs from C API) */
+static PyObject *Context_get_mflags(ContextObjext *self)
+{
+ unsigned long flags;
+
+ PyObject *result;
+ mnt_context_get_mflags(self->cxt, &flags);
+ result = Py_BuildValue("k", flags);
+
+ if (!result)
+ PyErr_SetString(PyExc_RuntimeError, CONSTRUCT_ERR);
+ return result;
+
+}
+/* returns a flags integer (behavior differs from C API) */
+static PyObject *Context_get_user_mflags(ContextObjext *self)
+{
+ unsigned long flags;
+
+ PyObject *result;
+ mnt_context_get_user_mflags(self->cxt, &flags);
+ result = Py_BuildValue("k", flags);
+
+ if (!result)
+ PyErr_SetString(PyExc_RuntimeError, CONSTRUCT_ERR);
+ return result;
+
+}
+#define Context_reset_status_HELP "reset_status()\n\n" \
+ "Resets mount(2) and mount.type statuses, so Cxt.do_mount() or\n" \
+ "Cxt.do_umount() could be again called with the same settings.\n" \
+ "\n" \
+ "BE CAREFUL -- after this soft reset the libmount will NOT parse mount\n" \
+ "options, evaluate permissions or apply stuff from fstab.\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_reset_status(ContextObjext *self)
+{
+ int rc = mnt_context_reset_status(self->cxt);
+
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_is_fake_HELP "is_fake()\n\n" \
+"Returns True if fake flag is enabled or False"
+static PyObject *Context_is_fake(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_fake(self->cxt));
+}
+
+#define Context_is_force_HELP "is_force()\n\n" \
+"Returns True if force umounting flag is enabled or False"
+static PyObject *Context_is_force(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_force(self->cxt));
+}
+
+#define Context_is_lazy_HELP "is_lazy()\n\n" \
+"Returns True if lazy umount is enabled or False"
+static PyObject *Context_is_lazy(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_lazy(self->cxt));
+}
+
+#define Context_is_nomtab_HELP "is_nomtab()\n\n" \
+ "Returns True if no-mtab is enabled or False"
+static PyObject *Context_is_nomtab(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_nomtab(self->cxt));
+}
+
+#define Context_is_rdonly_umount_HELP "is_rdonly_umount()\n\n" \
+ "Enable/disable read-only remount on failed umount(2)\n" \
+ "(see umount(8) man page, option -r).\n" \
+ "\n" \
+ "Returns self on success, raises an exception in case of error."
+static PyObject *Context_is_rdonly_umount(ContextObjext *self)
+{
+ int rc = mnt_context_is_rdonly_umount(self->cxt);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_is_restricted_HELP "is_restricted()\n\n" \
+ "Returns False for unrestricted mount (user is root), or True for non-root mounts"
+static PyObject *Context_is_restricted(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_restricted(self->cxt));
+}
+
+#define Context_is_sloppy_HELP "is_sloppy()\n\n" \
+ "Returns True if sloppy flag is enabled or False"
+static PyObject *Context_is_sloppy(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_sloppy(self->cxt));
+}
+
+#define Context_is_verbose_HELP "is_verbose()\n\n" \
+ "Returns True if verbose flag is enabled or False"
+static PyObject *Context_is_verbose(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_verbose(self->cxt));
+}
+#define Context_is_fs_mounted_HELP "is_fs_mounted(fs, mounted)\n\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_is_fs_mounted(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"fs", "mounted", NULL};
+ FsObject *fs;
+ int mounted;
+
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O!i", kwlist,
+ &FsType, &fs, &mounted)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyBool_FromLong(mnt_context_is_fs_mounted(self->cxt, fs->fs, &mounted));
+}
+
+#define Context_is_child_HELP "is_child()\n\n" \
+ "Returns True if mount -F enabled and the current context is child, or False"
+static PyObject *Context_is_child(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_child(self->cxt));
+}
+
+#define Context_is_fork_HELP "is_fork()\n\n" \
+ "Returns True if fork (mount -F) is enabled or False"
+static PyObject *Context_is_fork(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_fork(self->cxt));
+}
+
+#define Context_is_parent_HELP "is_parent()\n\n" \
+ "Returns True if mount -F enabled and the current context is parent, or False"
+static PyObject *Context_is_parent(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_parent(self->cxt));
+}
+
+#define Context_is_loopdel_HELP "is_loopdel()\n\n" \
+ "Returns True if loop device should be deleted after umount (umount -d) or False."
+static PyObject *Context_is_loopdel(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_loopdel(self->cxt));
+}
+
+#define Context_is_nocanonicalize_HELP "is_nocanonicalize()\n\n" \
+ "Returns True if no-canonicalize mode enabled or False."
+static PyObject *Context_is_nocanonicalize(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_nocanonicalize(self->cxt));
+}
+
+#define Context_is_nohelpers_HELP "is_nohelpers()\n\n" \
+ "Returns True if helpers are disabled (mount -i) or False."
+static PyObject *Context_is_nohelpers(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_nohelpers(self->cxt));
+}
+
+#define Context_syscall_called_HELP "syscall_called()\n\n" \
+ "Returns True if mount(2) syscall has been called, or False."
+static PyObject *Context_syscall_called(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_syscall_called(self->cxt));
+}
+
+#define Context_is_swapmatch_HELP "is_swapmatch()\n\n" \
+ "Returns True if swap between source and target is allowed (default is True) or False."
+static PyObject *Context_is_swapmatch(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_is_swapmatch(self->cxt));
+}
+
+#define Context_tab_applied_HELP "tab_applied()\n\n" \
+ "Returns True if fstab (or mtab) has been applied to the context, False otherwise."
+static PyObject *Context_tab_applied(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_tab_applied(self->cxt));
+}
+
+#define Context_apply_fstab_HELP "apply_fstab()\n\n" \
+ "This function is optional.\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_apply_fstab(ContextObjext *self)
+{
+ int rc = mnt_context_apply_fstab(self->cxt);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_helper_executed_HELP "helper_executed()\n\n" \
+ "Returns True if mount.type helper has been executed, or False."
+static PyObject *Context_helper_executed(ContextObjext *self)
+{
+ return PyBool_FromLong(mnt_context_helper_executed(self->cxt));
+}
+
+static PyObject *Context_get_source(ContextObjext *self)
+{
+ return PyObjectResultStr(mnt_context_get_source(self->cxt));
+}
+
+static PyObject *Context_get_target(ContextObjext *self)
+{
+ return PyObjectResultStr(mnt_context_get_target(self->cxt));
+}
+
+static PyObject *Context_get_options(ContextObjext *self)
+{
+ return PyObjectResultStr(mnt_context_get_options(self->cxt));
+}
+
+static PyObject *Context_get_fstype(ContextObjext *self)
+{
+ return PyObjectResultStr(mnt_context_get_fstype(self->cxt));
+}
+
+static PyObject *Context_get_fs(ContextObjext *self)
+{
+ return PyObjectResultFs(mnt_context_get_fs(self->cxt));
+}
+
+static PyObject *Context_get_fstab(ContextObjext *self)
+{
+ struct libmnt_table *tab = NULL;
+
+ if (mnt_context_get_fstab(self->cxt, &tab) != 0 || !tab)
+ return NULL;
+ return PyObjectResultTab(tab);
+}
+
+static PyObject *Context_get_mtab(ContextObjext *self)
+{
+ struct libmnt_table *tab = NULL;
+
+ if (mnt_context_get_mtab(self->cxt, &tab) != 0 || !tab)
+ return NULL;
+ return PyObjectResultTab(tab);
+}
+
+static PyObject *Context_get_optsmode(ContextObjext *self)
+{
+ return PyObjectResultInt(mnt_context_get_optsmode(self->cxt));
+}
+
+static PyObject *Context_get_status(ContextObjext *self)
+{
+ return PyObjectResultInt(mnt_context_get_status(self->cxt));
+}
+
+static PyObject *Context_get_syscall_errno(ContextObjext *self)
+{
+ return PyObjectResultInt(mnt_context_get_syscall_errno(self->cxt));
+}
+
+#define Context_do_mount_HELP "do_mount()\n\n" \
+ "Call mount(2) or mount.type helper. Unnecessary for Cxt.mount().\n" \
+ "\n" \
+ "Note that this function could be called only once. If you want to mount\n" \
+ "another source or target than you have to call Cxt.reset_context().\n" \
+ "\n" \
+ "If you want to call mount(2) for the same source and target with a different\n" \
+ "mount flags or fstype then call Cxt.reset_status() and then try\n" \
+ "again Cxt.do_mount().\n" \
+ "\n" \
+ "WARNING: non-zero return code does not mean that mount(2) syscall or\n" \
+ "mount.type helper wasn't successfully called.\n" \
+ "\n" \
+ "Check Cxt.status after error!\n" \
+ "\n" \
+ "Returns self on success or an exception in case of other errors."
+static PyObject *Context_do_mount(ContextObjext *self)
+{
+ int rc = mnt_context_do_mount(self->cxt);
+ return rc ? UL_RaiseExc(rc < 0 ? -rc : rc) : UL_IncRef(self);
+}
+
+#define Context_do_umount_HELP "do_umount()\n\n" \
+ "Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type).\n" \
+ "Unnecessary for Cxt.umount().\n" \
+ "\n" \
+ "See also Cxt.disable_helpers().\n" \
+ "\n" \
+ "WARNING: non-zero return code does not mean that umount(2) syscall or\n" \
+ "umount.type helper wasn't successfully called.\n" \
+ "\n" \
+ "Check Cxt.status after error!\n" \
+ "\n" \
+ "Returns self on success or an exception in case of other errors."
+static PyObject *Context_do_umount(ContextObjext *self)
+{
+ int rc = mnt_context_do_umount(self->cxt);
+ return rc ? UL_RaiseExc(rc < 0 ? -rc : rc) : UL_IncRef(self);
+}
+
+#define Context_mount_HELP "mount()\n\n" \
+ "High-level, mounts filesystem by mount(2) or fork()+exec(/sbin/mount.type).\n" \
+ "\n" \
+ "This is similar to:\n" \
+ "\n" \
+ "Cxt.prepare_mount();\n" \
+ "Cxt.do_mount();\n" \
+ "Cxt.finalize_mount();\n" \
+ "\n" \
+ "See also Cxt.disable_helper().\n" \
+ "\n" \
+ "Note that this function could be called only once. If you want to mount with\n" \
+ "different setting than you have to call Cxt.reset_context(). It's NOT enough\n" \
+ "to call Cxt.reset_status() if you want call this function more than\n" \
+ "once, whole context has to be reset.\n" \
+ "\n" \
+ "WARNING: non-zero return code does not mean that mount(2) syscall or\n" \
+ "mount.type helper wasn't successfully called.\n" \
+ "\n" \
+ "Check Cxt.status after error!\n" \
+ "\n" \
+ "Returns self on success or an exception in case of other errors."
+static PyObject *Context_mount(ContextObjext *self)
+{
+ int rc = mnt_context_mount(self->cxt);
+ return rc ? UL_RaiseExc(rc < 0 ? -rc : rc) : UL_IncRef(self);
+}
+
+#define Context_umount_HELP "umount()\n\n" \
+ "High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type).\n" \
+ "\n" \
+ "This is similar to:\n" \
+ "\n" \
+ "Cxt.prepare_umount();\n" \
+ "Cxt.do_umount();\n" \
+ "Cxt.finalize_umount();\n" \
+ "\n" \
+ "See also Cxt.disable_helpers().\n" \
+ "\n" \
+ "WARNING: non-zero return code does not mean that umount(2) syscall or\n" \
+ "umount.type helper wasn't successfully called.\n" \
+ "\n" \
+ "Check Cxt.status after error!\n" \
+ "\n" \
+ "Returns self on success or an exception in case of other errors."
+static PyObject *Context_umount(ContextObjext *self)
+{
+ int rc = mnt_context_umount(self->cxt);
+ return rc ? UL_RaiseExc(rc < 0 ? -rc : rc) : UL_IncRef(self);
+}
+
+#define Context_finalize_mount_HELP "finalize_mount()\n\n" \
+ "Mtab update, etc. Unnecessary for Cxt.mount(), but should be called\n" \
+ "after Cxt.do_mount(). See also Cxt.syscall_status.\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_finalize_mount(ContextObjext *self)
+{
+ int rc = mnt_context_finalize_mount(self->cxt);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_prepare_umount_HELP "prepare_umount()\n\n" \
+ "Prepare context for umounting, unnecessary for Cxt.umount().\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_prepare_umount(ContextObjext *self)
+{
+ int rc = mnt_context_prepare_umount(self->cxt);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_prepare_mount_HELP "prepare_mount()\n\n" \
+ "Prepare context for mounting, unnecessary for Cxt.mount().\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_prepare_mount(ContextObjext *self)
+{
+ int rc = mnt_context_prepare_mount(self->cxt);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_finalize_umount_HELP "finalize_umount()\n\n" \
+ "Mtab update, etc. Unnecessary for Cxt.umount(), but should be called\n" \
+ "after Cxt.do_umount(). See also Cxt.syscall_status.\n" \
+ "\n" \
+ "Returns self on success, raises LibmountError if target filesystem not found, or other exception on error."
+static PyObject *Context_finalize_umount(ContextObjext *self)
+{
+ int rc = mnt_context_finalize_umount(self->cxt);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_find_umount_fs_HELP "find_umount_fs(tgt, pfs)\n\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_find_umount_fs(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ char *kwlist[] = { "tgt", "pfs", NULL };
+ char *tgt = NULL;
+ FsObject *fs;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!", kwlist, &tgt, &FsType, &fs)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+
+ rc = mnt_context_find_umount_fs(self->cxt, tgt, &fs->fs);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_append_options_HELP "append_options(optstr)\n\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_append_options(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ char *kwlist[] = {"optstr", NULL};
+ char *optstr = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &optstr)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+
+ rc = mnt_context_append_options(self->cxt, optstr);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_helper_setopt_HELP "helper_setopt(c, arg)\n\n" \
+ "This function applies [u]mount.type command line option (for example parsed\n" \
+ "by getopt or getopt_long) to cxt. All unknown options are ignored and\n" \
+ "then ValueError is raised.\n" \
+ "\n" \
+ "Returns self on success, raises ValueError if c is unknown or other exception in case of an error."
+static PyObject *Context_helper_setopt(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int c;
+ char *arg;
+ char *kwlist[] = { "c", "arg", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist, &c, &arg)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+
+ rc = mnt_context_helper_setopt(self->cxt, c, arg);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Context_init_helper_HELP "init_helper(action, flags)\n\n" \
+ "This function informs libmount that it is used from [u]mount.type helper.\n" \
+ "\n" \
+ "The function also calls Cxt.disable_helpers() to avoid calling\n" \
+ "mount.type helpers recursively. If you really want to call another\n" \
+ "mount.type helper from your helper then you have to explicitly enable this\n" \
+ "feature by:\n" \
+ "\n" \
+ "Cxt.disable_helpers(False);\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Context_init_helper(ContextObjext *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ int action, flags;
+ char *kwlist[] = {"action", "flags", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, &action, &flags)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+
+ rc = mnt_context_init_helper(self->cxt, action, flags);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+static PyGetSetDef Context_getseters[] = {
+ {"tables_errcb", NULL, (setter)Context_set_tables_errcb, "error callback function", NULL},
+ {"status", (getter)Context_get_status, NULL, "status", NULL},
+ {"source", (getter)Context_get_source, (setter)Context_set_source, "source", NULL},
+ {"target", (getter)Context_get_target, (setter)Context_set_target, "target", NULL},
+ {"fstype", (getter)Context_get_fstype, (setter)Context_set_fstype, "fstype", NULL},
+ {"options", (getter)Context_get_options, (setter)Context_set_options, "options", NULL},
+ {"mflags", (getter)Context_get_mflags, (setter)Context_set_mflags, "mflags", NULL},
+ {"mountdata", NULL, (setter)Context_set_mountdata, "mountdata", NULL},
+ {"fstype_pattern", NULL, (setter)Context_set_fstype_pattern, "fstype_pattern", NULL},
+ {"options_pattern", NULL, (setter)Context_set_options_pattern, "options_pattern", NULL},
+ {"fs", (getter)Context_get_fs, (setter)Context_set_fs, "filesystem description (type, mountpoint, device, ...)", NULL},
+ {"mtab", (getter)Context_get_mtab, NULL, "mtab entries", NULL},
+ {"fstab", (getter)Context_get_fstab, (setter)Context_set_fstab, "fstab (or mtab for some remounts)", NULL},
+ {"optsmode", (getter)Context_get_optsmode, (setter)Context_set_optsmode, "fstab optstr mode MNT_OPTSMODE_{AUTO,FORCE,IGNORE}", NULL},
+ {"syscall_errno", (getter)Context_get_syscall_errno, (setter)Context_set_syscall_status, "1: not_called yet, 0: success, <0: -errno", NULL},
+ {"user_mflags", (getter)Context_get_user_mflags, (setter)Context_set_user_mflags, "user mflags", NULL},
+ {NULL}
+};
+static PyMethodDef Context_methods[] = {
+ {"find_umount_fs", (PyCFunction)Context_find_umount_fs, METH_VARARGS|METH_KEYWORDS, Context_find_umount_fs_HELP},
+ {"reset_status", (PyCFunction)Context_reset_status, METH_NOARGS, Context_reset_status_HELP},
+ {"helper_executed", (PyCFunction)Context_helper_executed, METH_NOARGS, Context_helper_executed_HELP},
+ {"init_helper", (PyCFunction)Context_init_helper, METH_VARARGS|METH_KEYWORDS, Context_init_helper_HELP},
+ {"helper_setopt", (PyCFunction)Context_helper_setopt, METH_VARARGS|METH_KEYWORDS, Context_helper_setopt_HELP},
+ {"append_options", (PyCFunction)Context_append_options, METH_VARARGS|METH_KEYWORDS, Context_append_options_HELP},
+ {"apply_fstab", (PyCFunction)Context_apply_fstab, METH_NOARGS, Context_apply_fstab_HELP},
+ {"disable_canonicalize", (PyCFunction)Context_disable_canonicalize, METH_VARARGS|METH_KEYWORDS, Context_disable_canonicalize_HELP},
+ {"disable_helpers", (PyCFunction)Context_disable_helpers, METH_VARARGS|METH_KEYWORDS, Context_disable_helpers_HELP},
+ {"disable_mtab", (PyCFunction)Context_disable_mtab, METH_VARARGS|METH_KEYWORDS, Context_disable_mtab_HELP},
+ {"do_mount", (PyCFunction)Context_do_mount, METH_NOARGS, Context_do_mount_HELP},
+ {"do_umount", (PyCFunction)Context_do_umount, METH_NOARGS , Context_do_umount_HELP},
+ {"enable_fake", (PyCFunction)Context_enable_fake, METH_VARARGS|METH_KEYWORDS, Context_enable_fake_HELP},
+ {"enable_force", (PyCFunction)Context_enable_force, METH_VARARGS|METH_KEYWORDS, Context_enable_force_HELP},
+ {"enable_lazy", (PyCFunction)Context_enable_lazy, METH_VARARGS|METH_KEYWORDS, Context_enable_lazy_HELP},
+ {"enable_loopdel", (PyCFunction)Context_enable_loopdel, METH_VARARGS|METH_KEYWORDS, Context_enable_loopdel_HELP},
+ {"enable_rdonly_umount", (PyCFunction)Context_enable_rdonly_umount, METH_VARARGS|METH_KEYWORDS, Context_enable_rdonly_umount_HELP},
+ {"enable_sloppy", (PyCFunction)Context_enable_sloppy, METH_VARARGS|METH_KEYWORDS, Context_enable_sloppy_HELP},
+ {"enable_verbose", (PyCFunction)Context_enable_verbose, METH_VARARGS|METH_KEYWORDS, Context_enable_verbose_HELP},
+ {"enable_fork", (PyCFunction)Context_enable_fork, METH_VARARGS|METH_KEYWORDS, Context_enable_fork_HELP},
+ {"finalize_mount", (PyCFunction)Context_finalize_mount, METH_NOARGS, Context_finalize_mount_HELP},
+ {"finalize_umount", (PyCFunction)Context_finalize_umount, METH_NOARGS, Context_finalize_umount_HELP},
+ {"is_fake", (PyCFunction)Context_is_fake, METH_NOARGS, Context_is_fake_HELP},
+ {"is_force", (PyCFunction)Context_is_force, METH_NOARGS, Context_is_force_HELP},
+ {"is_fork", (PyCFunction)Context_is_fork, METH_NOARGS, Context_is_fork_HELP},
+ {"is_fs_mounted", (PyCFunction)Context_is_fs_mounted, METH_VARARGS|METH_KEYWORDS, Context_is_fs_mounted_HELP},
+ {"is_lazy", (PyCFunction)Context_is_lazy, METH_NOARGS, Context_is_lazy_HELP},
+ {"is_nomtab", (PyCFunction)Context_is_nomtab, METH_NOARGS, Context_is_nomtab_HELP},
+ {"is_rdonly_umount", (PyCFunction)Context_is_rdonly_umount, METH_NOARGS, Context_is_rdonly_umount_HELP},
+ {"is_restricted", (PyCFunction)Context_is_restricted, METH_NOARGS, Context_is_restricted_HELP},
+ {"is_sloppy", (PyCFunction)Context_is_sloppy, METH_NOARGS, Context_is_sloppy_HELP},
+ {"is_verbose", (PyCFunction)Context_is_verbose, METH_NOARGS, Context_is_verbose_HELP},
+ {"is_child", (PyCFunction)Context_is_child, METH_NOARGS, Context_is_child_HELP},
+ {"is_parent", (PyCFunction)Context_is_parent, METH_NOARGS, Context_is_parent_HELP},
+ {"is_loopdel", (PyCFunction)Context_is_loopdel, METH_NOARGS, Context_is_loopdel_HELP},
+ {"is_nocanonicalize", (PyCFunction)Context_is_nocanonicalize, METH_NOARGS, Context_is_nocanonicalize_HELP},
+ {"is_nohelpers", (PyCFunction)Context_is_nohelpers, METH_NOARGS, Context_is_nohelpers_HELP},
+ {"is_swapmatch", (PyCFunction)Context_is_swapmatch, METH_NOARGS, Context_is_swapmatch_HELP},
+ {"mount", (PyCFunction)Context_mount, METH_NOARGS, Context_mount_HELP},
+ {"prepare_mount", (PyCFunction)Context_prepare_mount, METH_NOARGS, Context_prepare_mount_HELP},
+ {"prepare_umount", (PyCFunction)Context_prepare_umount, METH_NOARGS, Context_prepare_umount_HELP},
+ {"umount", (PyCFunction)Context_umount, METH_NOARGS, Context_umount_HELP},
+ {"syscall_called", (PyCFunction)Context_syscall_called, METH_NOARGS, Context_syscall_called_HELP},
+ {"disable_swapmatch", (PyCFunction)Context_disable_swapmatch, METH_VARARGS|METH_KEYWORDS, Context_disable_swapmatch_HELP},
+ {"tab_applied", (PyCFunction)Context_tab_applied, METH_NOARGS, Context_tab_applied_HELP},
+ {NULL}
+};
+
+static PyObject *Context_repr(ContextObjext *self)
+{
+ return PyUnicode_FromFormat("<libmount.Context object at %p, restricted=%s>",
+ self, mnt_context_is_restricted(self->cxt) ? "True" : "False");
+}
+
+PyTypeObject ContextType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "libmount.Context", /*tp_name*/
+ sizeof(ContextObjext), /*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;
+
+ DBG(CXT, pymnt_debug("add to module"));
+
+ Py_INCREF(&ContextType);
+ PyModule_AddObject(mod, "Context", (PyObject *)&ContextType);
+}
+
+
diff --git a/libmount/python/fs.c b/libmount/python/fs.c
new file mode 100644
index 0000000..e989124
--- /dev/null
+++ b/libmount/python/fs.c
@@ -0,0 +1,883 @@
+/*
+ * Python bindings for the libmount library.
+ *
+ * Copyright (C) 2013, Red Hat, Inc. All rights reserved.
+ * Written by Ondrej Oprala and Karel Zak
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * TODO:
+ * mnt_fs_match_{source,target}
+ * mnt_fs_get_{attribute,option}
+ */
+
+#include "pylibmount.h"
+#include <errno.h>
+
+#define Fs_HELP "Fs(source=None, root=None, target=None, fstype=None, options=None, attributes=None, freq=0, passno=0)"
+
+static PyMemberDef Fs_members[] = {
+ {NULL}
+};
+
+static PyObject *Fs_get_tag(FsObject *self)
+{
+ const char *tag = NULL, *val = NULL;
+ PyObject *result;
+
+ if (mnt_fs_get_tag(self->fs, &tag, &val) != 0)
+ return NULL;
+
+ result = Py_BuildValue("(ss)", tag, val);
+ if (!result)
+ PyErr_SetString(PyExc_RuntimeError, CONSTRUCT_ERR);
+ return result;
+}
+
+static PyObject *Fs_get_id(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_id(self->fs));
+}
+
+static PyObject *Fs_get_parent_id(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_parent_id(self->fs));
+}
+
+static PyObject *Fs_get_devno(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_devno(self->fs));
+}
+
+static void _dump_debug_string(const char *lead, const char *s, char quote)
+{
+ /* PySys_WriteStdout() will automatically truncate any '%s' token
+ * longer than a certain length (documented as 1000 bytes, but we
+ * give ourselves some margin here just in case). The only way I
+ * know to get around this is to print such strings in bite-sized
+ * chunks.
+ */
+ static const unsigned int _PY_MAX_LEN = 900;
+ static const char *_PY_MAX_LEN_FMT = "%.900s";
+ unsigned int len;
+
+ if (lead != NULL)
+ PySys_WriteStdout("%s", lead);
+
+ if (quote != 0)
+ PySys_WriteStdout("%c", quote);
+
+ for (len = strlen(s); len > _PY_MAX_LEN; len -= _PY_MAX_LEN, s += _PY_MAX_LEN)
+ PySys_WriteStdout(_PY_MAX_LEN_FMT, s);
+
+ if (len > 0)
+ PySys_WriteStdout(_PY_MAX_LEN_FMT, s);
+
+ if (quote != 0)
+ PySys_WriteStdout("%c\n", quote);
+ else
+ PySys_WriteStdout("\n");
+}
+
+#define Fs_print_debug_HELP "print_debug()\n\n"
+static PyObject *Fs_print_debug(FsObject *self)
+{
+ PySys_WriteStdout("------ fs: %p\n", self->fs);
+ _dump_debug_string("source: ", mnt_fs_get_source(self->fs), 0);
+ _dump_debug_string("target: ", mnt_fs_get_target(self->fs), 0);
+ _dump_debug_string("fstype: ", mnt_fs_get_fstype(self->fs), 0);
+
+ if (mnt_fs_get_options(self->fs))
+ _dump_debug_string("optstr: ", mnt_fs_get_options(self->fs), 0);
+ if (mnt_fs_get_vfs_options(self->fs))
+ _dump_debug_string("VFS-optstr: ", mnt_fs_get_vfs_options(self->fs), 0);
+ if (mnt_fs_get_fs_options(self->fs))
+ _dump_debug_string("FS-opstr: ", mnt_fs_get_fs_options(self->fs), 0);
+ if (mnt_fs_get_user_options(self->fs))
+ _dump_debug_string("user-optstr: ", mnt_fs_get_user_options(self->fs), 0);
+ if (mnt_fs_get_optional_fields(self->fs))
+ _dump_debug_string("optional-fields: ", mnt_fs_get_optional_fields(self->fs), '\'');
+ if (mnt_fs_get_attributes(self->fs))
+ _dump_debug_string("attributes: ", mnt_fs_get_attributes(self->fs), 0);
+
+ if (mnt_fs_get_root(self->fs))
+ _dump_debug_string("root: ", mnt_fs_get_root(self->fs), 0);
+
+ if (mnt_fs_get_swaptype(self->fs))
+ _dump_debug_string("swaptype: ", mnt_fs_get_swaptype(self->fs), 0);
+ if (mnt_fs_get_size(self->fs))
+ PySys_WriteStdout("size: %jd\n", mnt_fs_get_size(self->fs));
+ if (mnt_fs_get_usedsize(self->fs))
+ PySys_WriteStdout("usedsize: %jd\n", mnt_fs_get_usedsize(self->fs));
+ if (mnt_fs_get_priority(self->fs))
+ PySys_WriteStdout("priority: %d\n", mnt_fs_get_priority(self->fs));
+
+ if (mnt_fs_get_bindsrc(self->fs))
+ _dump_debug_string("bindsrc: ", mnt_fs_get_bindsrc(self->fs), 0);
+ if (mnt_fs_get_freq(self->fs))
+ PySys_WriteStdout("freq: %d\n", mnt_fs_get_freq(self->fs));
+ if (mnt_fs_get_passno(self->fs))
+ PySys_WriteStdout("pass: %d\n", mnt_fs_get_passno(self->fs));
+ if (mnt_fs_get_id(self->fs))
+ PySys_WriteStdout("id: %d\n", mnt_fs_get_id(self->fs));
+ if (mnt_fs_get_parent_id(self->fs))
+ PySys_WriteStdout("parent: %d\n", mnt_fs_get_parent_id(self->fs));
+ if (mnt_fs_get_devno(self->fs))
+ PySys_WriteStdout("devno: %d:%d\n", major(mnt_fs_get_devno(self->fs)),
+ minor(mnt_fs_get_devno(self->fs)));
+ if (mnt_fs_get_tid(self->fs))
+ PySys_WriteStdout("tid: %d\n", mnt_fs_get_tid(self->fs));
+ if (mnt_fs_get_comment(self->fs))
+ _dump_debug_string("comment: ", mnt_fs_get_comment(self->fs), '\'');
+ return UL_IncRef(self);
+}
+/*
+ ** Fs getters/setters
+ */
+
+static PyObject *Fs_get_comment(FsObject *self, void *closure __attribute__((unused)))
+{
+ return PyObjectResultStr(mnt_fs_get_comment(self->fs));
+}
+
+static int Fs_set_comment(FsObject *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char *comment = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(comment = pystos(value)))
+ return -1;
+
+ rc = mnt_fs_set_comment(self->fs, comment);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+/* source */
+static PyObject *Fs_get_source(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_source(self->fs));
+}
+
+static int Fs_set_source(FsObject *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char *source = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(source = pystos(value)))
+ return -1;
+
+ rc = mnt_fs_set_source(self->fs, source);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *Fs_get_srcpath(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_srcpath(self->fs));
+}
+
+static PyObject *Fs_get_root(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_root(self->fs));
+}
+
+static int Fs_set_root(FsObject *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char *root = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(root = pystos(value)))
+ return -1;
+
+ rc = mnt_fs_set_root(self->fs, root);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *Fs_get_target(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_target(self->fs));
+}
+
+static int Fs_set_target(FsObject *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ char *target = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(target = pystos(value)))
+ return -1;
+
+ rc = mnt_fs_set_target(self->fs, target);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *Fs_get_fstype(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_fstype(self->fs));
+}
+
+static int Fs_set_fstype(FsObject *self, PyObject *value,
+ void *closure __attribute__((unused)))
+{
+ char *fstype = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(fstype = pystos(value)))
+ return -1;
+
+ rc = mnt_fs_set_fstype(self->fs, fstype);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *Fs_get_options(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_options(self->fs));
+}
+
+static int Fs_set_options(FsObject *self, PyObject *value,
+ void *closure __attribute__((unused)))
+{
+ char *options = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(options = pystos(value)))
+ return -1;
+
+ rc = mnt_fs_set_options(self->fs, options);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *Fs_get_vfs_options(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_vfs_options(self->fs));
+}
+
+
+static PyObject *Fs_get_optional_fields(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_optional_fields(self->fs));
+}
+
+
+static PyObject *Fs_get_fs_options(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_fs_options(self->fs));
+}
+
+
+static PyObject *Fs_get_user_options(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_user_options(self->fs));
+}
+
+
+static PyObject *Fs_get_attributes(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_attributes(self->fs));
+}
+
+static int Fs_set_attributes(FsObject *self, PyObject *value,
+ void *closure __attribute__((unused)))
+{
+ char *attributes = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(attributes = pystos(value)))
+ return -1;
+
+ rc = mnt_fs_set_attributes(self->fs, attributes);
+ if (rc) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *Fs_get_freq(FsObject *self, void *closure __attribute__((unused)))
+{
+ return PyObjectResultInt(mnt_fs_get_freq(self->fs));
+}
+
+static int Fs_set_freq(FsObject *self, PyObject *value,
+ void *closure __attribute__((unused)))
+{
+ int freq = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+
+ }
+
+ if (!PyLong_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+
+ freq = PyLong_AsLong(value);
+ if (freq == -1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_RuntimeError, "type conversion failed");
+ return -1;
+ }
+ return mnt_fs_set_freq(self->fs, freq);
+}
+
+static PyObject *Fs_get_passno(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_passno(self->fs));
+}
+
+static int Fs_set_passno(FsObject *self, PyObject *value, void *closure __attribute__((unused)))
+{
+ int passno = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+
+ if (!PyLong_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return -1;
+ }
+
+ passno = PyLong_AsLong(value);
+ if (passno == -1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_RuntimeError, "type conversion failed");
+ return -1;
+ }
+ return mnt_fs_set_passno(self->fs, passno);
+}
+
+static PyObject *Fs_get_swaptype(FsObject *self)
+{
+ return PyObjectResultStr(mnt_fs_get_swaptype(self->fs));
+}
+
+static PyObject *Fs_get_size(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_size(self->fs));
+}
+
+static PyObject *Fs_get_usedsize(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_usedsize(self->fs));
+}
+
+static PyObject *Fs_get_priority(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_priority(self->fs));
+}
+
+#define Fs_get_propagation_HELP "get_propagation(flags)\n\n\
+Note that this function set flags to zero if not found any propagation flag\n\
+in mountinfo file. The kernel default is MS_PRIVATE, this flag is not stored\n\
+in the mountinfo file.\n\
+\n\
+Returns self or raises an exception in case of an error."
+static PyObject *Fs_get_propagation(FsObject *self)
+{
+ unsigned long flags;
+ int rc;
+
+ rc = mnt_fs_get_propagation(self->fs, &flags);
+ return rc ? UL_RaiseExc(-rc) : PyObjectResultInt(flags);
+}
+
+static PyObject *Fs_get_tid(FsObject *self)
+{
+ return PyObjectResultInt(mnt_fs_get_tid(self->fs));
+}
+
+#define Fs_is_kernel_HELP "is_kernel()\n\nReturns 1 if the filesystem " \
+ "description is read from kernel e.g. /proc/mounts."
+static PyObject *Fs_is_kernel(FsObject *self)
+{
+ return PyBool_FromLong(mnt_fs_is_kernel(self->fs));
+}
+
+#define Fs_is_netfs_HELP "is_netfs()\n\nReturns 1 if the filesystem is " \
+ "a network filesystem"
+static PyObject *Fs_is_netfs(FsObject *self)
+{
+ return PyBool_FromLong(mnt_fs_is_netfs(self->fs));
+}
+
+#define Fs_is_pseudofs_HELP "is_pseudofs()\n\nReturns 1 if the filesystem is "\
+ "a pseudo fs type (proc, cgroups)"
+static PyObject *Fs_is_pseudofs(FsObject *self)
+{
+ return PyBool_FromLong(mnt_fs_is_pseudofs(self->fs));
+}
+
+#define Fs_is_swaparea_HELP "is_swaparea()\n\nReturns 1 if the filesystem " \
+ "uses \"swap\" as a type"
+static PyObject *Fs_is_swaparea(FsObject *self)
+{
+ return PyBool_FromLong(mnt_fs_is_swaparea(self->fs));
+}
+
+#define Fs_append_attributes_HELP "append_attributes(optstr)\n\n" \
+ "Appends mount attributes."
+static PyObject *Fs_append_attributes(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"optstr", NULL};
+ char *optstr = NULL;
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &optstr)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_fs_append_attributes(self->fs, optstr);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Fs_append_options_HELP "append_options(optstr)\n\n" \
+ "Parses (splits) optstr and appends results to VFS, " \
+ "FS and userspace lists of options."
+static PyObject *Fs_append_options(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"optstr", NULL};
+ char *optstr = NULL;
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &optstr)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_fs_append_options(self->fs, optstr);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Fs_prepend_attributes_HELP "prepend_attributes(optstr)\n\n" \
+ "Prepends mount attributes."
+static PyObject *Fs_prepend_attributes(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"optstr", NULL};
+ char *optstr = NULL;
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &optstr)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_fs_prepend_attributes(self->fs, optstr);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Fs_prepend_options_HELP "prepend_options(optstr)\n\n" \
+ "Parses (splits) optstr and prepends results to VFS, " \
+ "FS and userspace lists of options."
+static PyObject *Fs_prepend_options(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"optstr", NULL};
+ char *optstr = NULL;
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &optstr)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_fs_prepend_options(self->fs, optstr);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Fs_match_fstype_HELP "match_fstype(pattern)\n\n" \
+ "pattern: filesystem name or comma delimited list(string) of names\n\n" \
+ "The pattern list of filesystem can be prefixed with a global\n" \
+ "\"no\" prefix to invert matching of the whole list. The \"no\" could\n" \
+ "also be used for individual items in the pattern list. So,\n" \
+ "\"nofoo,bar\" has the same meaning as \"nofoo,nobar\".\n" \
+ "\"bar\" : \"nofoo,bar\" -> False (global \"no\" prefix)\n" \
+ "\"bar\" : \"foo,bar\" -> True\n" \
+ "\"bar\" : \"foo,nobar\" -> False\n\n" \
+ "Returns True if type is matching, else False."
+static PyObject *Fs_match_fstype(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"pattern", NULL};
+ char *pattern = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &pattern)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyBool_FromLong(mnt_fs_match_fstype(self->fs, pattern));
+}
+
+#define Fs_match_options_HELP "match_options(options)\n\n" \
+ "options: comma delimited list of options (and nooptions)\n" \
+ "Returns True if fs type is matching to options else False."
+static PyObject *Fs_match_options(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"options", NULL};
+ char *options = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &options)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyBool_FromLong(mnt_fs_match_options(self->fs, options));
+}
+
+#define Fs_streq_srcpath_HELP "streq_srcpath(srcpath)\n\n" \
+ "Compares fs source path with path. The tailing slash is ignored.\n" \
+ "Returns True if fs source path equal to path, otherwise False."
+static PyObject *Fs_streq_srcpath(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"srcpath", NULL};
+ char *srcpath = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &srcpath)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyBool_FromLong(mnt_fs_streq_srcpath(self->fs, srcpath));
+}
+
+#define Fs_streq_target_HELP "streq_target(target)\n\n" \
+ "Compares fs target path with path. The tailing slash is ignored.\n" \
+ "See also Fs.match_target().\n" \
+ "Returns True if fs target path equal to path, otherwise False."
+static PyObject *Fs_streq_target(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"target", NULL};
+ char *target = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &target)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyBool_FromLong(mnt_fs_streq_target(self->fs, target));
+}
+
+#define Fs_copy_fs_HELP "copy_fs(dest=None)\n\n" \
+ "If dest is None, a new object is created, if any fs " \
+ "field is already set, then the field is NOT overwritten."
+static PyObject *Fs_copy_fs(FsObject *self, PyObject *args, PyObject *kwds);
+
+static PyMethodDef Fs_methods[] = {
+ {"get_propagation", (PyCFunction)Fs_get_propagation, METH_NOARGS, Fs_get_propagation_HELP},
+ {"mnt_fs_append_attributes", (PyCFunction)Fs_append_attributes, METH_VARARGS|METH_KEYWORDS, Fs_append_attributes_HELP},
+ {"append_options", (PyCFunction)Fs_append_options, METH_VARARGS|METH_KEYWORDS, Fs_append_options_HELP},
+ {"mnt_fs_prepend_attributes", (PyCFunction)Fs_prepend_attributes, METH_VARARGS|METH_KEYWORDS, Fs_prepend_attributes_HELP},
+ {"prepend_options", (PyCFunction)Fs_prepend_options, METH_VARARGS|METH_KEYWORDS, Fs_prepend_options_HELP},
+ {"copy_fs", (PyCFunction)Fs_copy_fs, METH_VARARGS|METH_KEYWORDS, Fs_copy_fs_HELP},
+ {"is_kernel", (PyCFunction)Fs_is_kernel, METH_NOARGS, Fs_is_kernel_HELP},
+ {"is_netfs", (PyCFunction)Fs_is_netfs, METH_NOARGS, Fs_is_netfs_HELP},
+ {"is_pseudofs", (PyCFunction)Fs_is_pseudofs, METH_NOARGS, Fs_is_pseudofs_HELP},
+ {"is_swaparea", (PyCFunction)Fs_is_swaparea, METH_NOARGS, Fs_is_swaparea_HELP},
+ {"match_fstype", (PyCFunction)Fs_match_fstype, METH_VARARGS|METH_KEYWORDS, Fs_match_fstype_HELP},
+ {"match_options", (PyCFunction)Fs_match_options, METH_VARARGS|METH_KEYWORDS, Fs_match_options_HELP},
+ {"streq_srcpath", (PyCFunction)Fs_streq_srcpath, METH_VARARGS|METH_KEYWORDS, Fs_streq_srcpath_HELP},
+ {"streq_target", (PyCFunction)Fs_streq_target, METH_VARARGS|METH_KEYWORDS, Fs_streq_target_HELP},
+ {"print_debug", (PyCFunction)Fs_print_debug, METH_NOARGS, Fs_print_debug_HELP},
+ {NULL}
+};
+
+static void Fs_destructor(FsObject *self)
+{
+ DBG(FS, pymnt_debug_h(self->fs, "destructor py-obj: %p, py-refcnt=%d",
+ self, (int) ((PyObject *) self)->ob_refcnt));
+ mnt_unref_fs(self->fs);
+ PyFree(self);
+}
+
+static PyObject *Fs_new(PyTypeObject *type, PyObject *args __attribute__((unused)),
+ PyObject *kwds __attribute__((unused)))
+{
+ FsObject *self = (FsObject*)type->tp_alloc(type, 0);
+
+ if (self) {
+ self->fs = NULL;
+ DBG(FS, pymnt_debug_h(self, "new"));
+ }
+ return (PyObject *) self;
+}
+
+static int Fs_init(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ char *source = NULL, *root = NULL, *target = NULL;
+ char *fstype = NULL, *options = NULL, *attributes =NULL;
+ int freq = 0; int passno = 0;
+ int rc = 0;
+ char *kwlist[] = {
+ "source", "root", "target",
+ "fstype", "options", "attributes",
+ "freq", "passno", NULL
+ };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssii", kwlist,
+ &source, &root, &target, &fstype, &options,
+ &attributes, &freq, &passno)) {
+ PyErr_SetString(PyExc_TypeError, "Invalid type");
+ return -1;
+ }
+
+ DBG(FS, pymnt_debug_h(self, "init"));
+
+ if (self->fs)
+ mnt_unref_fs(self->fs);
+
+ self->fs = mnt_new_fs(); /* new FS with refcount=1 */
+
+ if (source && (rc = mnt_fs_set_source(self->fs, source))) {
+ PyErr_SetString(PyExc_MemoryError, MEMORY_ERR);
+ return rc;
+ }
+ if (root && (rc = mnt_fs_set_root(self->fs, root))) {
+ PyErr_SetString(PyExc_MemoryError, MEMORY_ERR);
+ return rc;
+ }
+ if (target && (rc = mnt_fs_set_target(self->fs, target))) {
+ PyErr_SetString(PyExc_MemoryError, MEMORY_ERR);
+ return rc;
+ }
+ if (fstype && (rc = mnt_fs_set_fstype(self->fs, fstype))) {
+ PyErr_SetString(PyExc_MemoryError, MEMORY_ERR);
+ return rc;
+ }
+ if (options && (rc = mnt_fs_set_options(self->fs, options))) {
+ PyErr_SetString(PyExc_MemoryError, MEMORY_ERR);
+ return rc;
+ }
+ if (attributes && (rc = mnt_fs_set_attributes(self->fs, attributes))) {
+ PyErr_SetString(PyExc_MemoryError, MEMORY_ERR);
+ return rc;
+ }
+
+ mnt_fs_set_freq(self->fs, freq);
+ mnt_fs_set_passno(self->fs, passno);
+ mnt_fs_set_userdata(self->fs, self); /* store a pointer to self, convenient when resetting the table */
+ return 0;
+}
+
+/*
+ * missing:
+ * attribute
+ * option
+ */
+static PyGetSetDef Fs_getseters[] = {
+ {"id", (getter)Fs_get_id, NULL, "mountinfo[1]: ID", NULL},
+ {"parent", (getter)Fs_get_parent_id, NULL, "mountinfo[2]: parent", NULL},
+ {"devno", (getter)Fs_get_devno, NULL, "mountinfo[3]: st_dev", NULL},
+ {"comment", (getter)Fs_get_comment, (setter)Fs_set_comment, "fstab entry comment", NULL},
+ {"source", (getter)Fs_get_source, (setter)Fs_set_source, "fstab[1], mountinfo[10], swaps[1]: source dev, file, dir or TAG", NULL},
+ {"srcpath", (getter)Fs_get_srcpath, NULL, "mount source path or NULL in case of error or when the path is not defined.", NULL},
+ {"root", (getter)Fs_get_root, (setter)Fs_set_root, "mountinfo[4]: root of the mount within the FS", NULL},
+ {"target", (getter)Fs_get_target, (setter)Fs_set_target, "mountinfo[5], fstab[2]: mountpoint", NULL},
+ {"fstype", (getter)Fs_get_fstype, (setter)Fs_set_fstype, "mountinfo[9], fstab[3]: filesystem type", NULL},
+ {"options", (getter)Fs_get_options, (setter)Fs_set_options, "fstab[4]: merged options", NULL},
+ {"vfs_options", (getter)Fs_get_vfs_options, NULL, "mountinfo[6]: fs-independent (VFS) options", NULL},
+ {"opt_fields", (getter)Fs_get_optional_fields, NULL, "mountinfo[7]: optional fields", NULL},
+ {"fs_options", (getter)Fs_get_fs_options, NULL, "mountinfo[11]: fs-dependent options", NULL},
+ {"usr_options", (getter)Fs_get_user_options, NULL, "userspace mount options", NULL},
+ {"attributes", (getter)Fs_get_attributes, (setter)Fs_set_attributes, "mount attributes", NULL},
+ {"freq", (getter)Fs_get_freq, (setter)Fs_set_freq, "fstab[5]: dump frequency in days", NULL},
+ {"passno", (getter)Fs_get_passno, (setter)Fs_set_passno, "fstab[6]: pass number on parallel fsck", NULL},
+ {"swaptype", (getter)Fs_get_swaptype, NULL, "swaps[2]: device type", NULL},
+ {"size", (getter)Fs_get_size, NULL, "saps[3]: swaparea size", NULL},
+ {"usedsize", (getter)Fs_get_usedsize, NULL, "swaps[4]: used size", NULL},
+ {"priority", (getter)Fs_get_priority, NULL, "swaps[5]: swap priority", NULL},
+ {"tag", (getter)Fs_get_tag, NULL, "(Name, Value)", NULL},
+ {"tid", (getter)Fs_get_tid, NULL, "/proc/<tid>/mountinfo, otherwise zero", NULL},
+ {NULL}
+};
+
+static PyObject *Fs_repr(FsObject *self)
+{
+ const char *src = mnt_fs_get_source(self->fs),
+ *tgt = mnt_fs_get_target(self->fs),
+ *type = mnt_fs_get_fstype(self->fs);
+
+ return PyUnicode_FromFormat(
+ "<libmount.Fs object at %p, "
+ "source=%s, target=%s, fstype=%s>",
+ self,
+ src ? src : "None",
+ tgt ? tgt : "None",
+ type ? type : "None");
+}
+
+PyObject *PyObjectResultFs(struct libmnt_fs *fs)
+{
+ FsObject *result;
+
+ if (!fs) {
+ PyErr_SetString(LibmountError, "internal exception");
+ return NULL;
+ }
+
+ result = mnt_fs_get_userdata(fs);
+ if (result) {
+ Py_INCREF(result);
+ DBG(FS, pymnt_debug_h(fs, "result py-obj %p: already exists, py-refcnt=%d",
+ result, (int) ((PyObject *) result)->ob_refcnt));
+ return (PyObject *) result;
+ }
+
+ /* Creating an encapsulating object: increment the refcount, so that code
+ * such as tab.next_fs() doesn't call the destructor, which would free
+ * our fs struct as well
+ */
+ result = PyObject_New(FsObject, &FsType);
+ if (!result) {
+ UL_RaiseExc(ENOMEM);
+ return NULL;
+ }
+
+ Py_INCREF(result);
+ mnt_ref_fs(fs);
+
+ DBG(FS, pymnt_debug_h(fs, "result py-obj %p new, py-refcnt=%d",
+ result, (int) ((PyObject *) result)->ob_refcnt));
+ result->fs = fs;
+ mnt_fs_set_userdata(fs, result);
+ return (PyObject *) result;
+}
+
+static PyObject *Fs_copy_fs(FsObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *dest = NULL;
+ char *kwlist[] = {"dest", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &dest)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ if (PyObject_TypeCheck(dest, &FsType)) { /* existing object passed as argument */
+ if (!mnt_copy_fs(((FsObject *)dest)->fs, self->fs))
+ return NULL;
+ DBG(FS, pymnt_debug_h(dest, "copy data"));
+ return (PyObject *)dest;
+
+ }
+
+ if (dest == Py_None) { /* create new object */
+ FsObject *result = PyObject_New(FsObject, &FsType);
+
+ DBG(FS, pymnt_debug_h(result, "new copy"));
+ result->fs = mnt_copy_fs(NULL, self->fs);
+ mnt_fs_set_userdata(result->fs, result); /* keep a pointer to encapsulating object */
+ return (PyObject *)result;
+ }
+
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+}
+
+
+PyTypeObject FsType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "libmount.Fs", /*tp_name*/
+ sizeof(FsObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Fs_destructor, /*tp_dealloc*/
+ 0, /*tp_print*/
+ NULL, /*tp_getattr*/
+ NULL, /*tp_setattr*/
+ NULL, /*tp_compare*/
+ (reprfunc)Fs_repr, /*tp_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*/
+ Fs_HELP, /* tp_doc */
+ NULL, /* tp_traverse */
+ NULL, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ Fs_methods, /* tp_methods */
+ Fs_members, /* tp_members */
+ Fs_getseters, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Fs_init, /* tp_init */
+ NULL, /* tp_alloc */
+ Fs_new, /* tp_new */
+};
+
+void FS_AddModuleObject(PyObject *mod)
+{
+ if (PyType_Ready(&FsType) < 0)
+ return;
+
+ DBG(FS, pymnt_debug("add to module"));
+ Py_INCREF(&FsType);
+ PyModule_AddObject(mod, "Fs", (PyObject *)&FsType);
+}
+
diff --git a/libmount/python/meson.build b/libmount/python/meson.build
new file mode 100644
index 0000000..c5feb7b
--- /dev/null
+++ b/libmount/python/meson.build
@@ -0,0 +1,41 @@
+python_module = import('python')
+
+python = python_module.find_installation(
+ get_option('python'),
+ required : get_option('build-python'),
+ disabler : true)
+build_python = python.found()
+
+pylibmount_sources = '''
+ pylibmount.c
+ pylibmount.h
+ fs.c
+ tab.c
+'''.split()
+
+if LINUX
+ pylibmount_sources += 'context.c'
+endif
+
+python.extension_module(
+ 'pylibmount',
+ pylibmount_sources,
+ include_directories : [dir_include, dir_libmount],
+ subdir : 'libmount',
+ link_with : lib_mount,
+ dependencies : python.dependency(),
+ c_args : [
+ '-Wno-cast-function-type',
+
+ # https://github.com/util-linux/util-linux/issues/2366
+ python.language_version().version_compare('>=3.12') ?
+ [ '-Wno-error=redundant-decls' ] : [],
+ ],
+ install : true)
+
+if build_python
+ python.install_sources(
+ '__init__.py',
+ subdir : 'libmount',
+ pure : false)
+endif
diff --git a/libmount/python/pylibmount.c b/libmount/python/pylibmount.c
new file mode 100644
index 0000000..28856ea
--- /dev/null
+++ b/libmount/python/pylibmount.c
@@ -0,0 +1,326 @@
+/*
+ * Python bindings for the libmount library.
+ *
+ * Copyright (C) 2013, Red Hat, Inc. All rights reserved.
+ * Written by Ondrej Oprala and Karel Zak
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "pylibmount.h"
+
+/* Libmount-specific Exception class */
+PyObject *LibmountError;
+int pylibmount_debug_mask;
+
+PyObject *UL_IncRef(void *killme)
+{
+ Py_INCREF(killme);
+ return killme;
+}
+
+void PyFree(void *o)
+{
+#if PY_MAJOR_VERSION >= 3
+ Py_TYPE(o)->tp_free((PyObject *)o);
+#else
+ ((PyObject *)o)->ob_type->tp_free((PyObject *)o);
+#endif
+}
+
+/* Demultiplexer for various possible error conditions across the libmount library */
+void *UL_RaiseExc(int e)
+{
+ /* TODO: Do we need to deal with -1/1? */
+ switch (e) {
+ case ENOMEM:
+ PyErr_SetString(PyExc_MemoryError, strerror(e));
+ break;
+ case EINVAL:
+ PyErr_SetString(PyExc_TypeError, strerror(e));
+ break;
+ /* libmount-specific errors */
+ case MNT_ERR_NOFSTAB:
+ PyErr_SetString(LibmountError, "Not found required entry in fstab");
+ break;
+ case MNT_ERR_NOFSTYPE:
+ PyErr_SetString(LibmountError, "Lailed to detect filesystem type");
+ break;
+ case MNT_ERR_NOSOURCE:
+ PyErr_SetString(LibmountError, "Required mount source undefined");
+ break;
+ case MNT_ERR_LOOPDEV:
+ PyErr_SetString(LibmountError, "Loopdev setup failed");
+ break;
+ case MNT_ERR_APPLYFLAGS:
+ PyErr_SetString(LibmountError, "Failed to parse/use userspace mount options");
+ break;
+ case MNT_ERR_MOUNTOPT:
+ PyErr_SetString(LibmountError, "Failed to apply propagation flags");
+ break;
+ case MNT_ERR_AMBIFS:
+ PyErr_SetString(LibmountError, "Libblkid detected more filesystems on the device");
+ break;
+ case MNT_ERR_LOOPOVERLAP:
+ PyErr_SetString(LibmountError, "Detected overlapping loop device that cannot be re-use");
+ break;
+ case MNT_ERR_LOCK:
+ PyErr_SetString(LibmountError, "Failed to lock mtab/utab or so");
+ break;
+ case MNT_ERR_NAMESPACE:
+ PyErr_SetString(LibmountError, "Failed to switch namespace");
+ break;
+ /* some other errno */
+ default:
+ PyErr_SetString(PyExc_Exception, strerror(e));
+ break;
+ }
+ return NULL;
+}
+
+/*
+ * General functions
+ */
+PyObject *PyObjectResultInt(int i)
+{
+ PyObject *result = Py_BuildValue("i", i);
+ if (!result)
+ PyErr_SetString(PyExc_RuntimeError, CONSTRUCT_ERR);
+ return result;
+}
+
+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;
+}
+
+/* wrapper around a common use case for PyUnicode_AsASCIIString() */
+char *pystos(PyObject *pys)
+{
+#if PY_MAJOR_VERSION >= 3
+ if (!PyUnicode_Check(pys)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return (char *)PyUnicode_1BYTE_DATA(pys);
+#else
+ if (!PyString_Check(pys)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyString_AsString(pys);
+#endif
+}
+
+/*
+ * the libmount module
+ */
+#define PYLIBMOUNT_DESC \
+ "Python API for the util-linux libmount library.\n\n" \
+ "Please note that none of the classes' attributes may be deleted.\n" \
+ "This is not a complete mapping to the libmount C API, nor is it\n" \
+ "attempting to be one.\n" "Iterator functions only allow forward\n" \
+ "iteration for now. Context.get_{user_,}mflags() differs from the C API\n" \
+ "and returns the flags directly. Fs.get_tag() differs from the C API\n" \
+ "and returns a (tag, value) tuple. Every attribute is \"filtered\"" \
+ "through appropriate getters/setters, no values are set directly."
+
+
+struct module_state {
+ PyObject *error;
+};
+
+#if PY_MAJOR_VERSION >= 3
+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
+#else
+#define GETSTATE(m) (&_state)
+static struct module_state _state;
+#endif
+
+static PyObject *
+error_out(PyObject *m __attribute__((unused))) {
+ struct module_state *st = GETSTATE(m);
+ PyErr_SetString(st->error, "something bad happened");
+ return NULL;
+}
+
+static PyMethodDef pylibmount_methods[] = {
+ {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
+ {NULL, NULL}
+};
+
+#if PY_MAJOR_VERSION >= 3
+
+static int pylibmount_traverse(PyObject *m, visitproc visit, void *arg) {
+ Py_VISIT(GETSTATE(m)->error);
+ return 0;
+}
+
+static int pylibmount_clear(PyObject *m) {
+ Py_CLEAR(GETSTATE(m)->error);
+ return 0;
+}
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "pylibmount",
+ NULL,
+ sizeof(struct module_state),
+ pylibmount_methods,
+ NULL,
+ pylibmount_traverse,
+ pylibmount_clear,
+ NULL
+};
+#define INITERROR return NULL
+PyMODINIT_FUNC PyInit_pylibmount(void);
+PyMODINIT_FUNC PyInit_pylibmount(void)
+#else
+#define INITERROR return
+# ifndef PyMODINIT_FUNC
+# define PyMODINIT_FUNC void
+# endif
+PyMODINIT_FUNC initpylibmount(void);
+PyMODINIT_FUNC initpylibmount(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *m = PyModule_Create(&moduledef);
+#else
+ PyObject *m = Py_InitModule3("pylibmount", pylibmount_methods, PYLIBMOUNT_DESC);
+#endif
+
+ if (!m)
+ INITERROR;
+ /*
+ * init debug stuff
+ */
+ if (!(pylibmount_debug_mask & PYMNT_DEBUG_INIT)) {
+ char *str = getenv("PYLIBMOUNT_DEBUG");
+
+ errno = 0;
+ pylibmount_debug_mask = 0;
+ if (str)
+ pylibmount_debug_mask = strtoul(str, NULL, 0);
+ if (errno)
+ pylibmount_debug_mask = 0;
+
+ pylibmount_debug_mask |= PYMNT_DEBUG_INIT;
+ }
+
+ if (pylibmount_debug_mask && pylibmount_debug_mask != PYMNT_DEBUG_INIT)
+ DBG(INIT, pymnt_debug("library debug mask: 0x%04x",
+ pylibmount_debug_mask));
+ mnt_init_debug(0);
+
+ /*
+ * Add module objects
+ */
+ LibmountError = PyErr_NewException("libmount.Error", NULL, NULL);
+ Py_INCREF(LibmountError);
+ PyModule_AddObject(m, "Error", (PyObject *)LibmountError);
+
+ FS_AddModuleObject(m);
+ Table_AddModuleObject(m);
+#ifdef __linux__
+ Context_AddModuleObject(m);
+#endif
+
+ /*
+ * mount(8) userspace options masks (MNT_MAP_USERSPACE map)
+ */
+ PyModule_AddIntConstant(m, "MNT_MS_COMMENT", MNT_MS_COMMENT);
+ PyModule_AddIntConstant(m, "MNT_MS_GROUP", MNT_MS_GROUP);
+ PyModule_AddIntConstant(m, "MNT_MS_HELPER", MNT_MS_HELPER);
+ PyModule_AddIntConstant(m, "MNT_MS_LOOP", MNT_MS_LOOP);
+ PyModule_AddIntConstant(m, "MNT_MS_NETDEV", MNT_MS_NETDEV);
+ PyModule_AddIntConstant(m, "MNT_MS_NOAUTO", MNT_MS_NOAUTO);
+ PyModule_AddIntConstant(m, "MNT_MS_NOFAIL", MNT_MS_NOFAIL);
+ PyModule_AddIntConstant(m, "MNT_MS_OFFSET", MNT_MS_OFFSET);
+ PyModule_AddIntConstant(m, "MNT_MS_OWNER", MNT_MS_OWNER);
+ PyModule_AddIntConstant(m, "MNT_MS_SIZELIMIT", MNT_MS_SIZELIMIT);
+ PyModule_AddIntConstant(m, "MNT_MS_ENCRYPTION", MNT_MS_ENCRYPTION);
+ PyModule_AddIntConstant(m, "MNT_MS_UHELPER", MNT_MS_UHELPER);
+ PyModule_AddIntConstant(m, "MNT_MS_USER", MNT_MS_USER);
+ PyModule_AddIntConstant(m, "MNT_MS_USERS", MNT_MS_USERS);
+ PyModule_AddIntConstant(m, "MNT_MS_XCOMMENT", MNT_MS_XCOMMENT);
+ PyModule_AddIntConstant(m, "MNT_MS_HASH_DEVICE", MNT_MS_HASH_DEVICE);
+ PyModule_AddIntConstant(m, "MNT_MS_ROOT_HASH", MNT_MS_ROOT_HASH);
+ PyModule_AddIntConstant(m, "MNT_MS_HASH_OFFSET", MNT_MS_HASH_OFFSET);
+ PyModule_AddIntConstant(m, "MNT_MS_ROOT_HASH_FILE", MNT_MS_ROOT_HASH_FILE);
+ PyModule_AddIntConstant(m, "MNT_MS_FEC_DEVICE", MNT_MS_FEC_DEVICE);
+ PyModule_AddIntConstant(m, "MNT_MS_FEC_OFFSET", MNT_MS_FEC_OFFSET);
+ PyModule_AddIntConstant(m, "MNT_MS_FEC_ROOTS", MNT_MS_FEC_ROOTS);
+ PyModule_AddIntConstant(m, "MNT_MS_ROOT_HASH_SIG", MNT_MS_ROOT_HASH_SIG);
+
+ /*
+ * mount(2) MS_* masks (MNT_MAP_LINUX map)
+ */
+ PyModule_AddIntConstant(m, "MS_BIND", MS_BIND);
+ PyModule_AddIntConstant(m, "MS_DIRSYNC", MS_DIRSYNC);
+ PyModule_AddIntConstant(m, "MS_I_VERSION", MS_I_VERSION);
+ PyModule_AddIntConstant(m, "MS_MANDLOCK", MS_MANDLOCK);
+ PyModule_AddIntConstant(m, "MS_MGC_MSK", MS_MGC_MSK);
+ PyModule_AddIntConstant(m, "MS_MGC_VAL", MS_MGC_VAL);
+ PyModule_AddIntConstant(m, "MS_MOVE", MS_MOVE);
+ PyModule_AddIntConstant(m, "MS_NOATIME", MS_NOATIME);
+ PyModule_AddIntConstant(m, "MS_NODEV", MS_NODEV);
+ PyModule_AddIntConstant(m, "MS_NODIRATIME", MS_NODIRATIME);
+ PyModule_AddIntConstant(m, "MS_NOEXEC", MS_NOEXEC);
+ PyModule_AddIntConstant(m, "MS_NOSUID", MS_NOSUID);
+ PyModule_AddIntConstant(m, "MS_OWNERSECURE", MS_OWNERSECURE);
+ PyModule_AddIntConstant(m, "MS_PRIVATE", MS_PRIVATE);
+ PyModule_AddIntConstant(m, "MS_PROPAGATION", MS_PROPAGATION);
+ PyModule_AddIntConstant(m, "MS_RDONLY", MS_RDONLY);
+ PyModule_AddIntConstant(m, "MS_REC", MS_REC);
+ PyModule_AddIntConstant(m, "MS_RELATIME", MS_RELATIME);
+ PyModule_AddIntConstant(m, "MS_REMOUNT", MS_REMOUNT);
+ PyModule_AddIntConstant(m, "MS_SECURE", MS_SECURE);
+ PyModule_AddIntConstant(m, "MS_SHARED", MS_SHARED);
+ PyModule_AddIntConstant(m, "MS_SILENT", MS_SILENT);
+ PyModule_AddIntConstant(m, "MS_SLAVE", MS_SLAVE);
+ PyModule_AddIntConstant(m, "MS_STRICTATIME", MS_STRICTATIME);
+ PyModule_AddIntConstant(m, "MS_SYNCHRONOUS", MS_SYNCHRONOUS);
+ PyModule_AddIntConstant(m, "MS_UNBINDABLE", MS_UNBINDABLE);
+
+ /* Will we need these directly?
+ PyModule_AddIntConstant(m, "MNT_ERR_AMBIFS", MNT_ERR_AMBIFS);
+ PyModule_AddIntConstant(m, "MNT_ERR_APPLYFLAGS", MNT_ERR_APPLYFLAGS);
+ PyModule_AddIntConstant(m, "MNT_ERR_LOOPDEV", MNT_ERR_LOOPDEV);
+ PyModule_AddIntConstant(m, "MNT_ERR_MOUNTOPT", MNT_ERR_MOUNTOPT);
+ PyModule_AddIntConstant(m, "MNT_ERR_NOFSTAB", MNT_ERR_NOFSTAB);
+ PyModule_AddIntConstant(m, "MNT_ERR_NOFSTYPE", MNT_ERR_NOFSTYPE);
+ PyModule_AddIntConstant(m, "MNT_ERR_NOSOURCE", MNT_ERR_NOSOURCE);
+ */
+
+ /* Still useful for functions using iterators internally */
+ PyModule_AddIntConstant(m, "MNT_ITER_FORWARD", MNT_ITER_FORWARD);
+ PyModule_AddIntConstant(m, "MNT_ITER_BACKWARD", MNT_ITER_BACKWARD);
+
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
+}
+
diff --git a/libmount/python/pylibmount.h b/libmount/python/pylibmount.h
new file mode 100644
index 0000000..bf3278d
--- /dev/null
+++ b/libmount/python/pylibmount.h
@@ -0,0 +1,131 @@
+#ifndef UTIL_LINUX_PYLIBMOUNT_H
+#define UTIL_LINUX_PYLIBMOUNT_H
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "c.h"
+#include "libmount.h"
+
+#define CONFIG_PYLIBMOUNT_DEBUG
+
+#define PYMNT_DEBUG_INIT (1 << 1)
+#define PYMNT_DEBUG_TAB (1 << 2)
+#define PYMNT_DEBUG_FS (1 << 3)
+#define PYMNT_DEBUG_CXT (1 << 4)
+
+#ifdef CONFIG_PYLIBMOUNT_DEBUG
+# include <stdio.h>
+# include <stdarg.h>
+
+# define DBG(m, x) do { \
+ if ((PYMNT_DEBUG_ ## m) & pylibmount_debug_mask) { \
+ fprintf(stderr, "%d: pylibmount: %6s: ", getpid(), # m); \
+ x; \
+ } \
+ } while (0)
+
+extern int pylibmount_debug_mask;
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+pymnt_debug(const char *mesg, ...)
+{
+ va_list ap;
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+static inline void __attribute__ ((__format__ (__printf__, 2, 3)))
+pymnt_debug_h(void *handler, const char *mesg, ...)
+{
+ va_list ap;
+
+ if (handler)
+ fprintf(stderr, "[%p]: ", handler);
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+#else /* !CONFIG_PYLIBMOUNT_DEBUG */
+# define DBG(m,x) do { ; } while (0)
+#endif
+
+
+#define NODEL_ATTR "This attribute cannot be deleted"
+#define CONSTRUCT_ERR "Error during object construction"
+#define ARG_ERR "Invalid number or type of arguments"
+#define NOFS_ERR "No filesystems to mount"
+#define MEMORY_ERR strerror(ENOMEM)
+#define CONV_ERR "Type conversion failed"
+
+/*
+ * fs.c
+ */
+typedef struct {
+ PyObject_HEAD
+ struct libmnt_fs *fs;
+} FsObject;
+
+extern PyTypeObject FsType;
+
+extern PyObject *PyObjectResultFs(struct libmnt_fs *fs);
+extern void FS_AddModuleObject(PyObject *mod);
+
+/*
+ * tab.c
+ */
+typedef struct {
+ PyObject_HEAD
+
+ struct libmnt_table *tab;
+ struct libmnt_iter *iter;
+ PyObject *errcb;
+} TableObject;
+
+extern PyTypeObject TableType;
+
+extern PyObject *PyObjectResultTab(struct libmnt_table *tab);
+
+extern void Table_unref(struct libmnt_table *tab);
+extern void Table_AddModuleObject(PyObject *mod);
+
+extern int pymnt_table_parser_errcb(struct libmnt_table *tb, const char *filename, int line);
+
+#ifdef __linux__
+
+/*
+ * context.c
+ */
+typedef struct {
+ PyObject_HEAD
+
+ struct libmnt_context *cxt;
+ PyObject *table_errcb;
+
+} ContextObjext;
+
+extern PyTypeObject ContextType;
+extern void Context_AddModuleObject(PyObject *mod);
+
+#endif /* __linux__ */
+
+/*
+ * misc
+ */
+extern PyObject *LibmountError;
+extern PyObject *UL_IncRef(void *killme);
+extern void *UL_RaiseExc(int e);
+
+extern PyObject *PyObjectResultInt(int i);
+extern PyObject *PyObjectResultStr(const char *s);
+
+extern char *pystos(PyObject *pys);
+extern void PyFree(void *o);
+
+
+
+#endif /* UTIL_LINUX_PYLIBMOUNT */
diff --git a/libmount/python/tab.c b/libmount/python/tab.c
new file mode 100644
index 0000000..000bc13
--- /dev/null
+++ b/libmount/python/tab.c
@@ -0,0 +1,783 @@
+/*
+ * Python bindings for the libmount library.
+ *
+ * Copyright (C) 2013, Red Hat, Inc. All rights reserved.
+ * Written by Ondrej Oprala and Karel Zak
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "pylibmount.h"
+
+static PyMemberDef Table_members[] = {
+ { NULL }
+};
+
+static int Table_set_parser_errcb(TableObject *self, PyObject *func,
+ void *closure __attribute__((unused)))
+{
+ PyObject *tmp;
+
+ if (!func) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+
+ if (!PyCallable_Check(func))
+ return -1;
+
+ tmp = self->errcb;
+ Py_INCREF(func);
+ self->errcb = func;
+ Py_XDECREF(tmp);
+ return 0;
+}
+
+static PyObject *Table_get_intro_comment(TableObject *self,
+ void *closure __attribute__((unused)))
+{
+ return PyObjectResultStr(mnt_table_get_intro_comment(self->tab));
+}
+
+static int Table_set_intro_comment(TableObject *self, PyObject *value,
+ void *closure __attribute__((unused)))
+{
+ char *comment = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(comment = pystos(value)))
+ return -1;
+
+ if ((rc = mnt_table_set_intro_comment(self->tab, comment))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *Table_get_trailing_comment(TableObject *self,
+ void *closure __attribute__((unused)))
+{
+ return PyObjectResultStr(mnt_table_get_trailing_comment(self->tab));
+}
+
+static int Table_set_trailing_comment(TableObject *self, PyObject *value,
+ void *closure __attribute__((unused)))
+{
+ char *comment = NULL;
+ int rc = 0;
+
+ if (!value) {
+ PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
+ return -1;
+ }
+ if (!(comment = pystos(value)))
+ return -1;
+
+ if ((rc = mnt_table_set_trailing_comment(self->tab, comment))) {
+ UL_RaiseExc(-rc);
+ return -1;
+ }
+ return 0;
+}
+
+#define Table_enable_comments_HELP "enable_comments(enable)\n\n" \
+ "Enables parsing of comments.\n\n" \
+ "The initial (intro) file comment is accessible by\n" \
+ "Tab.intro_comment. The intro and the comment of the first fstab" \
+ "entry has to be separated by blank line. The filesystem comments are\n" \
+ "accessible by Fs.comment. The tailing fstab comment is accessible\n" \
+ "by Tab.trailing_comment.\n" \
+ "\n" \
+ "<informalexample>\n" \
+ "<programlisting>\n" \
+ "#\n" \
+ "# Intro comment\n" \
+ "#\n" \
+ "\n" \
+ "# this comments belongs to the first fs\n" \
+ "LABEL=foo /mnt/foo auto defaults 1 2\n" \
+ "# this comments belongs to the second fs\n" \
+ "LABEL=bar /mnt/bar auto defaults 1 2 \n" \
+ "# tailing comment\n" \
+ "</programlisting>\n" \
+ "</informalexample>"
+static PyObject *Table_enable_comments(TableObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ int enable = 0;
+ char *kwlist[] = {"enable", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ mnt_table_enable_comments(self->tab, enable);
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+#define Table_replace_file_HELP "replace_file(filename)\n\n" \
+ "This function replaces filename with the new content from TableObject."
+static PyObject *Table_replace_file(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ char *filename = NULL;
+ char *kwlist[] = {"filename", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &filename)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_table_replace_file(self->tab, filename);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_write_file_HELP "write_file(path)\n\n" \
+ "This function writes tab to file(stream)"
+static PyObject *Table_write_file(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ int rc;
+ //PyObject *stream = NULL;
+ FILE *f = NULL;
+ char *path = NULL;
+ char *kwlist[] = {"path", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &path)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ if (!(f = fopen(path, "w")))
+ return UL_RaiseExc(errno);
+ rc = mnt_table_write_file(self->tab, f);
+ fclose(f);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_find_devno_HELP "find_devno(devno, [direction])\n\n" \
+ "Note that zero could be valid device number for root pseudo " \
+ "filesystem (e.g. tmpfs)\n" \
+ "Returns a tab entry or None"
+static PyObject *Table_find_devno(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ dev_t devno;
+ int direction = MNT_ITER_BACKWARD;
+ char *kwlist[] = {"devno", "direction", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "I|i", kwlist, &devno, &direction)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyObjectResultFs(mnt_table_find_devno(self->tab, devno, direction));
+}
+
+#define Table_find_mountpoint_HELP "find_mountpoint(path, [direction])\n\n" \
+ "Returns a tab entry or None."
+static PyObject *Table_find_mountpoint(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ char *path;
+ int direction = MNT_ITER_BACKWARD;
+ char *kwlist[] = {"path", "direction", NULL};
+
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &path, &direction)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyObjectResultFs(mnt_table_find_mountpoint(self->tab, path, direction));
+}
+
+#define Table_find_pair_HELP "find_pair(source, target, [direction])\n\n" \
+ "Returns a tab entry or None."
+static PyObject *Table_find_pair(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"source", "target", "direction", NULL};
+ char *source;
+ char *target;
+ int direction = MNT_ITER_BACKWARD;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &source, &target, &direction)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyObjectResultFs(mnt_table_find_pair(self->tab, source, target, direction));
+}
+
+#define Table_find_source_HELP "find_source(source, [direction])\n\n" \
+ "Returns a tab entry or None."
+static PyObject *Table_find_source(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"source", "direction", NULL};
+ char *source;
+ int direction = MNT_ITER_BACKWARD;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &source, &direction)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyObjectResultFs(mnt_table_find_source(self->tab, source, direction));
+}
+
+#define Table_find_target_HELP "find_target(target, [direction])\n\n" \
+ "Try to lookup an entry in given tab, possible are three iterations, first\n" \
+ "with path, second with realpath(path) and third with realpath(path)\n" \
+ "against realpath(fs->target). The 2nd and 3rd iterations are not performed\n" \
+ "when tb cache is not set (cache not implemented yet).\n" \
+ "\n" \
+ "Returns a tab entry or None."
+static PyObject *Table_find_target(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"target", "direction", NULL};
+ char *target;
+ int direction = MNT_ITER_BACKWARD;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &target, &direction)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyObjectResultFs(mnt_table_find_target(self->tab, target, direction));
+}
+
+#define Table_find_srcpath_HELP "find_srcpath(srcpath, [direction])\n\n" \
+ "Try to lookup an entry in given tab, possible are four iterations, first\n" \
+ "with path, second with realpath(path), third with tags (LABEL, UUID, ..)\n" \
+ "from path and fourth with realpath(path) against realpath(entry->srcpath).\n" \
+ "\n" \
+ "The 2nd, 3rd and 4th iterations are not performed when tb cache is not\n" \
+ "set (not implemented yet).\n" \
+ "\n" \
+ "Note that None is a valid source path; it will be replaced with \"none\". The\n" \
+ "\"none\" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.\n" \
+ "\n" \
+ "Returns a tab entry or None."
+static PyObject *Table_find_srcpath(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"srcpath", "direction", NULL};
+ char *srcpath;
+ int direction = MNT_ITER_BACKWARD;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &srcpath, &direction)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyObjectResultFs(mnt_table_find_srcpath(self->tab, srcpath, direction));
+}
+
+#define Table_find_tag_HELP "find_tag(tag, val, [direction])\n\n" \
+ "Try to lookup an entry in given tab, first attempt is lookup by tag and\n" \
+ "val, for the second attempt the tag is evaluated (converted to the device\n" \
+ "name) and Tab.find_srcpath() is performed. The second attempt is not\n" \
+ "performed when tb cache is not set (not implemented yet).\n" \
+ "\n" \
+ "Returns a tab entry or NULL."
+static PyObject *Table_find_tag(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"tag", "val", "direction", NULL};
+ char *tag;
+ char *val;
+ int direction = MNT_ITER_BACKWARD;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &tag, &val, &direction)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyObjectResultFs(mnt_table_find_tag(self->tab, tag, val, direction));
+}
+
+static PyObject *Table_get_nents(TableObject *self)
+{
+ return PyObjectResultInt(mnt_table_get_nents(self->tab));
+}
+
+#define Table_is_fs_mounted_HELP "is_fs_mounted(fstab_fs)\n\n" \
+ "Checks if the fstab_fs entry is already in the tb table. The \"swap\" is\n" \
+ "ignored. This function explicitly compares source, target and root of the\n" \
+ "filesystems.\n" \
+ "\n" \
+ "Note that source and target are canonicalized only if a cache for tb is\n" \
+ "defined (not implemented yet). The target canonicalization may\n" \
+ "trigger automount on autofs mountpoints!\n" \
+ "\n" \
+ "Don't use it if you want to know if a device is mounted, just use\n" \
+ "Tab.find_source() for the device.\n" \
+ "\n" \
+ "This function is designed mostly for \"mount -a\".\n" \
+ "\n" \
+ "Returns a boolean value."
+static PyObject *Table_is_fs_mounted(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ FsObject *fs;
+ char *kwlist[] = {"fstab_fs", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &FsType, &fs)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ return PyBool_FromLong(mnt_table_is_fs_mounted(self->tab, fs->fs));
+}
+
+#define Table_parse_file_HELP "parse_file(file)\n\n" \
+ "Parses whole table (e.g. /etc/mtab) and appends new records to the tab.\n" \
+ "\n" \
+ "The libmount parser ignores broken (syntax error) lines, these lines are\n" \
+ "reported to caller by errcb() function (see Tab.parser_errcb).\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Table_parse_file(TableObject *self, PyObject* args, PyObject *kwds)
+{
+ int rc;
+ char *file = NULL;
+ char *kwlist[] = {"file", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &file)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_table_parse_file(self->tab, file);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_parse_fstab_HELP "parse_fstab([fstab])\n\n" \
+ "This function parses /etc/fstab and appends new lines to the tab. If the\n" \
+ "filename is a directory then Tab.parse_dir() is called.\n" \
+ "\n" \
+ "See also Tab.parser_errcb.\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Table_parse_fstab(TableObject *self, PyObject* args, PyObject *kwds)
+{
+ int rc;
+ char *fstab = NULL;
+ char *kwlist[] = {"fstab", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &fstab)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_table_parse_fstab(self->tab, fstab);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_parse_mtab_HELP "parse_mtab([mtab])\n\n" \
+ "This function parses /etc/mtab or /proc/self/mountinfo\n" \
+ "/run/mount/utabs or /proc/mounts.\n" \
+ "\n" \
+ "See also Tab.parser_errcb().\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Table_parse_mtab(TableObject *self, PyObject* args, PyObject *kwds)
+{
+ int rc;
+ char *mtab = NULL;
+ char *kwlist[] = {"mtab", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &mtab)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_table_parse_mtab(self->tab, mtab);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_parse_dir_HELP "parse_dir(dir)\n\n" \
+ "The directory:\n" \
+ "- files are sorted by strverscmp(3)\n" \
+ "- files that start with \".\" are ignored (e.g. \".10foo.fstab\")\n" \
+ "- files without the \".fstab\" extension are ignored\n" \
+ "\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Table_parse_dir(TableObject *self, PyObject* args, PyObject *kwds)
+{
+ int rc;
+ char *dir = NULL;
+ char *kwlist[] = {"dir", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &dir)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_table_parse_dir(self->tab, dir);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_parse_swaps_HELP "parse_swaps(swaps)\n\n" \
+ "This function parses /proc/swaps and appends new lines to the tab"
+static PyObject *Table_parse_swaps(TableObject *self, PyObject* args, PyObject *kwds)
+{
+ int rc;
+ char *swaps = NULL;
+ char *kwlist[] = {"swaps", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &swaps)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_table_parse_swaps(self->tab, swaps);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_add_fs_HELP "add_fs(fs)\n\nAdds a new entry to tab.\n" \
+ "Returns self or raises an exception in case of an error."
+
+static PyObject *Table_add_fs(TableObject *self, PyObject* args, PyObject *kwds)
+{
+ int rc;
+ FsObject *fs = NULL;
+ char *kwlist[] = {"fs", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &FsType, &fs)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ Py_INCREF(fs);
+ rc = mnt_table_add_fs(self->tab, fs->fs);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_remove_fs_HELP "remove_fs(fs)\n\n" \
+ "Returns self or raises an exception in case of an error."
+static PyObject *Table_remove_fs(TableObject *self, PyObject* args, PyObject *kwds)
+{
+ int rc;
+ FsObject *fs = NULL;
+ char *kwlist[] = {"fs", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &FsType, &fs)) {
+ PyErr_SetString(PyExc_TypeError, ARG_ERR);
+ return NULL;
+ }
+ rc = mnt_table_remove_fs(self->tab, fs->fs);
+ Py_DECREF(fs);
+ return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
+}
+
+#define Table_next_fs_HELP "next_fs()\n\n" \
+ "Returns the next Fs on success, raises an exception in case " \
+ "of an error and None at end of list.\n" \
+ "\n" \
+ "Example:\n" \
+ "<informalexample>\n" \
+ "<programlisting>\n" \
+ "import libmount\n" \
+ "import functools\n" \
+ "for fs in iter(functools.partial(tb.next_fs), None):\n" \
+ " dir = Fs.target\n" \
+ " print \"mount point: {:s}\".format(dir)\n" \
+ "\n" \
+ "</programlisting>\n" \
+ "</informalexample>\n" \
+ "\n" \
+ "lists all mountpoints from fstab in backward order."
+static PyObject *Table_next_fs(TableObject *self)
+{
+ struct libmnt_fs *fs;
+ int rc;
+
+ /* Reset the builtin iterator after reaching the end of the list */
+ rc = mnt_table_next_fs(self->tab, self->iter, &fs);
+ if (rc == 1) {
+ mnt_reset_iter(self->iter, MNT_ITER_FORWARD);
+ Py_RETURN_NONE;
+ }
+
+ if (rc)
+ return UL_RaiseExc(-rc);
+
+ return PyObjectResultFs(fs);
+}
+
+static PyMethodDef Table_methods[] = {
+ {"enable_comments", (PyCFunction)Table_enable_comments, METH_VARARGS|METH_KEYWORDS, Table_enable_comments_HELP},
+ {"find_pair", (PyCFunction)Table_find_pair, METH_VARARGS|METH_KEYWORDS, Table_find_pair_HELP},
+ {"find_source", (PyCFunction)Table_find_source, METH_VARARGS|METH_KEYWORDS, Table_find_source_HELP},
+ {"find_srcpath", (PyCFunction)Table_find_srcpath, METH_VARARGS|METH_KEYWORDS, Table_find_srcpath_HELP},
+ {"find_tag", (PyCFunction)Table_find_tag, METH_VARARGS|METH_KEYWORDS, Table_find_tag_HELP},
+ {"find_target", (PyCFunction)Table_find_target, METH_VARARGS|METH_KEYWORDS, Table_find_target_HELP},
+ {"find_devno", (PyCFunction)Table_find_devno, METH_VARARGS|METH_KEYWORDS, Table_find_devno_HELP},
+ {"find_mountpoint", (PyCFunction)Table_find_mountpoint, METH_VARARGS|METH_KEYWORDS, Table_find_mountpoint_HELP},
+ {"parse_file", (PyCFunction)Table_parse_file, METH_VARARGS|METH_KEYWORDS, Table_parse_file_HELP},
+ {"parse_fstab", (PyCFunction)Table_parse_fstab, METH_VARARGS|METH_KEYWORDS, Table_parse_fstab_HELP},
+ {"parse_mtab", (PyCFunction)Table_parse_mtab, METH_VARARGS|METH_KEYWORDS, Table_parse_mtab_HELP},
+ {"parse_dir", (PyCFunction)Table_parse_dir, METH_VARARGS|METH_KEYWORDS, Table_parse_dir_HELP},
+ {"parse_swaps", (PyCFunction)Table_parse_swaps, METH_VARARGS|METH_KEYWORDS, Table_parse_swaps_HELP},
+ {"is_fs_mounted", (PyCFunction)Table_is_fs_mounted, METH_VARARGS|METH_KEYWORDS, Table_is_fs_mounted_HELP},
+ {"add_fs", (PyCFunction)Table_add_fs, METH_VARARGS|METH_KEYWORDS, Table_add_fs_HELP},
+ {"remove_fs", (PyCFunction)Table_remove_fs, METH_VARARGS|METH_KEYWORDS, Table_remove_fs_HELP},
+ {"next_fs", (PyCFunction)Table_next_fs, METH_NOARGS, Table_next_fs_HELP},
+ {"write_file", (PyCFunction)Table_write_file, METH_VARARGS|METH_KEYWORDS, Table_write_file_HELP},
+ {"replace_file", (PyCFunction)Table_replace_file, METH_VARARGS|METH_KEYWORDS, Table_replace_file_HELP},
+ {NULL}
+};
+
+/* mnt_free_tab() with a few necessary additions */
+void Table_unref(struct libmnt_table *tab)
+{
+ struct libmnt_fs *fs;
+ struct libmnt_iter *iter;
+
+ if (!tab)
+ return;
+
+ DBG(TAB, pymnt_debug_h(tab, "un-referencing filesystems"));
+
+ iter = mnt_new_iter(MNT_ITER_BACKWARD);
+
+ /* remove pylibmount specific references to the entries */
+ while (mnt_table_next_fs(tab, iter, &fs) == 0)
+ Py_XDECREF(mnt_fs_get_userdata(fs));
+
+ DBG(TAB, pymnt_debug_h(tab, "un-referencing table"));
+
+ mnt_unref_table(tab);
+ mnt_free_iter(iter);
+}
+
+static void Table_destructor(TableObject *self)
+{
+ DBG(TAB, pymnt_debug_h(self->tab, "destructor py-obj: %p, py-refcnt=%d",
+ self, (int) ((PyObject *) self)->ob_refcnt));
+ Table_unref(self->tab);
+ self->tab = NULL;
+
+ mnt_free_iter(self->iter);
+ Py_XDECREF(self->errcb);
+ PyFree(self);
+}
+
+static PyObject *Table_new(PyTypeObject *type,
+ PyObject *args __attribute__((unused)),
+ PyObject *kwds __attribute__((unused)))
+{
+ TableObject *self = (TableObject*)type->tp_alloc(type, 0);
+
+ if (self) {
+ DBG(TAB, pymnt_debug_h(self, "new"));
+
+ self->tab = NULL;
+ self->iter = NULL;
+ self->errcb = NULL;
+ }
+ return (PyObject *)self;
+}
+
+/* explicit tab.__init__() serves as mnt_reset_table(tab) would in C
+ * and as mnt_new_table{,_from_dir,_from_file}() with proper arguments */
+#define Table_HELP "Table(path=None, errcb=None)"
+static int Table_init(TableObject *self, PyObject *args, PyObject *kwds)
+{
+ struct libmnt_cache *cache;
+ char *path = NULL;
+ char *kwlist[] = {"path", "errcb", NULL};
+ PyObject *errcb = NULL;
+ struct stat buf;
+
+ memset (&buf, 0, sizeof(struct stat));
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sO",
+ kwlist, &path, &errcb))
+ return -1;
+
+ DBG(TAB, pymnt_debug_h(self, "init"));
+
+ Table_unref(self->tab);
+ self->tab = NULL;
+
+ if (self->iter)
+ mnt_reset_iter(self->iter, MNT_ITER_FORWARD);
+ else
+ self->iter = mnt_new_iter(MNT_ITER_FORWARD);
+
+ if (errcb) {
+ PyObject *tmp;
+ if (!PyCallable_Check(errcb))
+ return -1;
+ tmp = self->errcb;
+ Py_INCREF(errcb);
+ self->errcb = errcb;
+ Py_XDECREF(tmp);
+ } else {
+ Py_XDECREF(self->errcb);
+ self->errcb = NULL;
+ }
+
+ if (path) {
+ DBG(TAB, pymnt_debug_h(self, "init: path defined (%s)", path));
+
+ if (stat(path, &buf)) {
+ /* TODO: weird */
+ PyErr_SetFromErrno(PyExc_RuntimeError);
+ return -1;
+ }
+ if (S_ISREG(buf.st_mode))
+ self->tab = mnt_new_table_from_file(path);
+ else if (S_ISDIR(buf.st_mode))
+ self->tab = mnt_new_table_from_dir(path);
+ } else {
+ DBG(TAB, pymnt_debug_h(self, "init: allocate empty table"));
+ self->tab = mnt_new_table();
+ }
+
+ /* Always set custom handler when using libmount from python */
+ mnt_table_set_parser_errcb(self->tab, pymnt_table_parser_errcb);
+ mnt_table_set_userdata(self->tab, self);
+
+ cache = mnt_new_cache(); /* TODO: make it optional? */
+ if (!cache)
+ return -1;
+ mnt_table_set_cache(self->tab, cache);
+ mnt_unref_cache(cache);
+
+ return 0;
+}
+
+/* Handler for the tab->errcb callback */
+int pymnt_table_parser_errcb(struct libmnt_table *tb, const char *filename, int line)
+{
+ int rc = 0;
+ PyObject *obj;
+
+ obj = mnt_table_get_userdata(tb);
+ if (obj && ((TableObject*) obj)->errcb) {
+ PyObject *arglist, *result;
+
+ arglist = Py_BuildValue("(Osi)", obj, filename, line);
+ if (!arglist)
+ return -ENOMEM;
+
+ /* A python callback was set, so tb is definitely encapsulated in an object */
+ result = PyObject_Call(((TableObject *)obj)->errcb, arglist, NULL);
+ Py_DECREF(arglist);
+
+ if (!result)
+ return -EINVAL;
+ if (!PyArg_Parse(result, "i", &rc))
+ rc = -EINVAL;
+ Py_DECREF(result);
+ }
+ return rc;
+}
+
+PyObject *PyObjectResultTab(struct libmnt_table *tab)
+{
+ TableObject *result;
+
+ if (!tab) {
+ PyErr_SetString(LibmountError, "internal exception");
+ return NULL;
+ }
+
+ result = mnt_table_get_userdata(tab);
+ if (result) {
+ Py_INCREF(result);
+ DBG(TAB, pymnt_debug_h(tab, "result py-obj %p: already exists, py-refcnt=%d",
+ result, (int) ((PyObject *) result)->ob_refcnt));
+ return (PyObject *) result;
+ }
+
+ /* Creating an encapsulating object: increment the refcount, so that
+ * code such as: cxt.get_fstab() doesn't call the destructor, which
+ * would free our tab struct as well
+ */
+ result = PyObject_New(TableObject, &TableType);
+ if (!result) {
+ UL_RaiseExc(ENOMEM);
+ return NULL;
+ }
+
+ Py_INCREF(result);
+ mnt_ref_table(tab);
+
+ DBG(TAB, pymnt_debug_h(tab, "result py-obj %p new, py-refcnt=%d",
+ result, (int) ((PyObject *) result)->ob_refcnt));
+ result->tab = tab;
+ result->iter = mnt_new_iter(MNT_ITER_FORWARD);
+ mnt_table_set_userdata(result->tab, result);
+ result->errcb = NULL;
+ return (PyObject *) result;
+}
+
+static PyGetSetDef Table_getseters[] = {
+ {"nents", (getter)Table_get_nents, NULL, "number of valid entries in tab", NULL},
+ {"intro_comment", (getter)Table_get_intro_comment, (setter)Table_set_intro_comment, "fstab intro comment", NULL},
+ {"trailing_comment", (getter)Table_get_trailing_comment, (setter)Table_set_trailing_comment, "fstab trailing comment", NULL},
+ {"errcb", NULL, (setter)Table_set_parser_errcb, "parser error callback", NULL},
+ {NULL}
+};
+
+
+static PyObject *Table_repr(TableObject *self)
+{
+ return PyUnicode_FromFormat(
+ "<libmount.Table object at %p, entries=%d, comments_enabled=%s, errcb=%s>",
+ self,
+ mnt_table_get_nents(self->tab),
+ mnt_table_with_comments(self->tab) ? "True" : "False",
+ self->errcb ? pystos(PyObject_Repr(self->errcb)) : "None");
+}
+
+PyTypeObject TableType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "libmount.Table", /*tp_name*/
+ sizeof(TableObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Table_destructor, /*tp_dealloc*/
+ 0, /*tp_print*/
+ NULL, /*tp_getattr*/
+ NULL, /*tp_setattr*/
+ NULL, /*tp_compare*/
+ (reprfunc) Table_repr, /*tp_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*/
+ Table_HELP, /* tp_doc */
+ NULL, /* tp_traverse */
+ NULL, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ Table_methods, /* tp_methods */
+ Table_members, /* tp_members */
+ Table_getseters, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Table_init, /* tp_init */
+ NULL, /* tp_alloc */
+ Table_new, /* tp_new */
+};
+
+void Table_AddModuleObject(PyObject *mod)
+{
+ if (PyType_Ready(&TableType) < 0)
+ return;
+
+ DBG(TAB, pymnt_debug("add to module"));
+
+ Py_INCREF(&TableType);
+ PyModule_AddObject(mod, "Table", (PyObject *)&TableType);
+}
+
diff --git a/libmount/python/test_mount_context.py b/libmount/python/test_mount_context.py
new file mode 100755
index 0000000..7aea444
--- /dev/null
+++ b/libmount/python/test_mount_context.py
@@ -0,0 +1,173 @@
+import os
+import sys
+import stat
+import errno
+
+# use "import libmount" for in a standard way installed python binding
+import pylibmount as mnt
+
+def usage(tss):
+ print("\nUsage:\n\t{:s} <test> [testoptions]\nTests:\n".format(sys.argv[0]))
+ for i in tss:
+ print("\t{15:-s}".format(i[0]))
+ if i[2] != "":
+ print(" {:s}\n".format(i[2]))
+
+ print("\n")
+ return 1
+
+def mnt_run_test(tss, argv):
+ rc = -1
+ if ((len(argv) < 2) or (argv[1] == "--help") or (argv[1] == "-h")):
+ return usage(tss)
+
+ #mnt_init_debug(0)
+
+ i=()
+ for i in tss:
+ if i[0] == argv[1]:
+ rc = i[1](i, argv[1:])
+ if rc:
+ print("FAILED [rc={:d}]".format(rc))
+ break
+
+ if ((rc < 0) and (i == ())):
+ return usage(tss)
+ return not not rc #because !!rc is too mainstream for python
+
+def test_mount(ts, argv):
+ idx = 1
+ rc = 0
+
+ if len(argv) < 2:
+ return -errno.EINVAL
+
+ cxt = mnt.Context()
+
+ if argv[idx] == "-o":
+ cxt.options = argv[idx+1]
+ idx += 2
+ if argv[idx] == "-t":
+ cxt.fstype = argv[idx+1]
+ idx += 2
+ if len(argv) == idx + 1:
+ cxt.target = argv[idx]
+ idx+=1
+ elif (len(argv) == idx + 2):
+ cxt.source = argv[idx]
+ idx += 1
+ cxt.target = argv[idx]
+ idx += 1
+
+ try:
+ cxt.mount()
+ except Exception:
+ print("failed to mount")
+ return -1
+ print("successfully mounted")
+ return rc
+
+def test_umount(ts, argv):
+ idx = 1
+ rc = 0
+ if len(argv) < 2:
+ return -errno.EINVAL
+
+ cxt = mnt.Context()
+
+ if argv[idx] == "-t":
+ cxt.options = argv[idx+1]
+ idx += 2
+
+ if argv[idx] == "-f":
+ cxt.enable_force(True)
+
+ if argv[idx] == "-l":
+ cxt.enable_lazy(True)
+ idx += 1
+ elif argv[idx] == "-r":
+ cxt.enable_rdonly_umount(True)
+ idx += 1
+
+ if len(argv) == idx + 1:
+ cxt.target = argv[idx]
+ idx += 1
+ else:
+ return -errno.EINVAL
+ try:
+ cxt.umount()
+ except Exception:
+ print("failed to umount")
+ return 1
+ print("successfully umounted")
+ return rc
+
+def test_flags(ts, argv):
+ idx = 1
+ rc = 0
+ opt = ""
+ flags = 0
+ cxt = mnt.Context()
+
+ if argv[idx] == "-o":
+ cxt.options = argv[idx + 1]
+ idx += 2
+ if len(argv) == idx + 1:
+ cxt.target = argv[idx]
+ idx += 1
+
+ try:
+ cxt.prepare_mount()
+ # catch ioerror here
+ except IOError as e:
+ print("failed to prepare mount {:s}".format(e.strerror))
+
+ opt = cxt.fs.options
+ if (opt):
+ print("options: {:s}", opt)
+
+ print("flags: {08:lx}".format(cxt.mflags()))
+ return rc
+
+def test_mountall(ts, argv):
+ mntrc = 1
+ ignored = 1
+ idx = 1
+ cxt = mnt.Context()
+
+ if len(argv) > 2:
+ if argv[idx] == "-O":
+ cxt.options_pattern = argv[idx+1]
+ idx += 2
+ if argv[idx] == "-t":
+ cxt.fstype_pattern = argv[idx+1]
+ idx += 2
+
+ i = ()
+ while (cxt.next_mount()):
+ tgt = i.target
+ if (ignored == 1):
+ print("{:s}: ignored: not match".format(tgt))
+ elif (ignored == 2):
+ print("{:s}: ignored: already mounted".format(tgt))
+ elif (not cxt.status):
+ if (mntrc > 0):
+ # ?? errno = mntrc
+ print("{:s}: mount failed".format(tgt))
+ else:
+ print("{:s}: mount failed".format(tgt))
+ else:
+ print("{:s}: successfully mounted".format(tgt))
+
+ return 0
+
+
+tss = (
+ ( "--mount", test_mount, "[-o <opts>] [-t <type>] <spec>|<src> <target>" ),
+ ( "--umount", test_umount, "[-t <type>] [-f][-l][-r] <src>|<target>" ),
+ ( "--mount-all", test_mountall, "[-O <pattern>] [-t <pattern] mount all filesystems from fstab" ),
+ ( "--flags", test_flags, "[-o <opts>] <spec>" )
+)
+os.umask(stat.S_IWGRP | stat.S_IWOTH) #to be compatible with mount(8)
+
+sys.exit(mnt_run_test(tss, sys.argv))
diff --git a/libmount/python/test_mount_tab.py b/libmount/python/test_mount_tab.py
new file mode 100755
index 0000000..1f63b95
--- /dev/null
+++ b/libmount/python/test_mount_tab.py
@@ -0,0 +1,164 @@
+import os
+import sys
+import stat
+import errno
+import functools as ft
+
+# use "import libmount" for in a standard way installed python binding
+import pylibmount as mnt
+
+def usage(tss):
+ print("\nUsage:\n\t{:s} <test> [testoptions]\nTests:\n".format(sys.argv[0]))
+ for i in tss:
+ print("\t{15:-s}".format(i[0]))
+ if i[2] != "":
+ print(" {:s}\n".format(i[2]))
+
+ print("\n")
+ return 1
+
+def mnt_run_test(tss, argv):
+ rc = -1
+ if ((len(argv) < 2) or (argv[1] == "--help") or (argv[1] == "-h")):
+ return usage(tss)
+
+ #mnt_init_debug(0)
+
+ i=()
+ for i in tss:
+ if i[0] == argv[1]:
+ rc = i[1](i, argv[1:])
+ if rc:
+ print("FAILED [rc={:d}]".format(rc))
+ break
+
+ if ((rc < 0) and (i == ())):
+ return usage(tss)
+ return not not rc #because !!rc is too mainstream for python
+
+def parser_errcb(tb, fname, line):
+ print("{:s}:{:d}: parse error".format(fname, line))
+ return 1
+
+def create_table(f, comments):
+ if not f:
+ return None
+
+ tb = mnt.Table()
+ tb.enable_comments(comments)
+ tb.errcb = parser_errcb
+
+ try:
+ tb.parse_file(f)
+ except Exception:
+ print("{:s}: parsing failed".format(f))
+ return None
+ return tb
+
+def test_copy_fs(ts, argv):
+ rc = -1
+ tb = create_table(argv[1], False)
+ fs = tb.find_target("/", mnt.MNT_ITER_FORWARD)
+ if not fs:
+ return rc
+
+ print("ORIGINAL:")
+ fs.print_debug()
+
+ fs = fs.copy_fs(None)
+ if not fs:
+ return rc
+ print("COPY:")
+ fs.print_debug()
+ return 0
+
+def test_parse(ts, argv):
+ parse_comments = False
+
+ if len(argv) == 3 and argv[2] == "--comments":
+ parse_comments = True
+ tb = create_table(argv[1], parse_comments)
+
+ if tb.intro_comment:
+ print("Initial comment:\n\"{:s}\"".format(tb.intro_comment))
+ #while ((fs = tb.next_fs()) != None):
+ for fs in iter(ft.partial(tb.next_fs), None):
+ fs.print_debug()
+ if tb.trailing_comment:
+ print("Trailing comment:\n\"{:s}\"".format(tb.trailing_comment))
+ return 0
+
+def test_find(ts, argv, dr):
+ if len(argv) != 4:
+ print("try --help")
+ return -errno.EINVAL
+
+ f, find, what = argv[1:]
+
+ tb = create_table(f, False)
+ if find.lower() == "source":
+ fs = tb.find_source(what, dr)
+ elif find.lower() == "target":
+ fs = tb.find_target(what, dr)
+
+ if not fs:
+ print("{:s}: not found {:s} '{:s}'".format(f, find, what))
+ else:
+ fs.print_debug()
+ return 0
+
+def test_find_fw(ts, argv):
+ return test_find(ts, argv, mnt.MNT_ITER_FORWARD)
+
+def test_find_bw(ts, argv):
+ return test_find(ts, argv, mnt.MNT_ITER_BACKWARD)
+
+def test_find_pair(ts, argv):
+ rc = -1
+ tb = create_table(argv[1], False)
+ fs = tb.find_pair(argv[2], argv[3], mnt.MNT_ITER_FORWARD)
+ if not fs:
+ return rc
+ fs.print_debug()
+ return 0
+
+def test_is_mounted(ts, argv):
+ rc = -1
+ tb = mnt.Tab(path="/proc/self/mountinfo")
+ if not tb:
+ print("failed to parse mountinto")
+ return rc
+
+ fstab = create_table(argv[1], False)
+ if not fstab:
+ return rc
+ fs = ()
+ for fs in ft.iter(tb.next_fs(), -1):
+ if tb.is_fs_mounted(fs):
+ print("{:s} already mounted on {:s}".format(fs.source, fs.target))
+ else:
+ print("{:s} not mounted on {:s}".format(fs.source, fs.target))
+ return 0
+
+def test_find_mountpoint(ts, argv):
+ rc = -1
+ tb = mnt.Table("/proc/self/mountinfo")
+ if not tb:
+ return rc
+ fs = tb.find_mountpoint(argv[1], mnt.MNT_ITER_BACKWARD)
+ if not fs:
+ return rc
+ fs.print_debug()
+ return 0
+
+
+tss = (
+ ( "--parse", test_parse, "<file> [--comments] parse and print(tab" ),
+ ( "--find-forward", test_find_fw, "<file> <source|target> <string>" ),
+ ( "--find-backward", test_find_bw, "<file> <source|target> <string>" ),
+ ( "--find-pair", test_find_pair, "<file> <source> <target>" ),
+ ( "--find-mountpoint", test_find_mountpoint, "<path>" ),
+ ( "--copy-fs", test_copy_fs, "<file> copy root FS from the file" ),
+ ( "--is-mounted", test_is_mounted, "<fstab> check what from <file> are already mounted" ),
+)
+sys.exit(mnt_run_test(tss, sys.argv))
diff --git a/libmount/python/test_mount_tab_update.py b/libmount/python/test_mount_tab_update.py
new file mode 100755
index 0000000..a16e9e3
--- /dev/null
+++ b/libmount/python/test_mount_tab_update.py
@@ -0,0 +1,59 @@
+import os
+import sys
+import stat
+import errno
+
+# use "import libmount" for in a standard way installed python binding
+import pylibmount as mnt
+
+def usage(tss):
+ print("\nUsage:\n\t{:s} <test> [testoptions]\nTests:\n".format(sys.argv[0]))
+ for i in tss:
+ print("\t{15:-s}".format(i[0]))
+ if i[2] != "":
+ print(" {:s}\n".format(i[2]))
+
+ print("\n")
+ return 1
+
+def mnt_run_test(tss, argv):
+ rc = -1
+ if ((len(argv) < 2) or (argv[1] == "--help") or (argv[1] == "-h")):
+ return usage(tss)
+
+ #mnt_init_debug(0)
+
+ i=()
+ for i in tss:
+ if i[0] == argv[1]:
+ rc = i[1](i, argv[1:])
+ if rc:
+ print("FAILED [rc={:d}]".format(rc))
+ break
+
+ if ((rc < 0) and (i == ())):
+ return usage(tss)
+ return not not rc #because !!rc is too mainstream for python
+
+def test_replace(ts, argv):
+ fs = mnt.Fs()
+ tb = mnt.Table()
+
+ if (len(argv) < 3):
+ return -1
+ tb.enable_comments(True)
+ tb.parse_fstab()
+
+ fs.source = argv[1]
+ fs.target = argv[2]
+ #TODO: resolve None + string
+ fs.comment = "# this is new filesystem\n"
+ tb.add_fs(fs)
+ tb.replace_file(os.environ["LIBMOUNT_FSTAB"])
+ return 0
+
+tss = (
+ ( "--replace",test_replace, "<src> <target> Add a line to LIBMOUNT_FSTAB and replace the original file" ),
+)
+
+sys.exit(mnt_run_test(tss, sys.argv))