diff options
| author | Richard Maw <richard.maw@codethink.co.uk> | 2014-06-12 12:04:01 +0100 |
|---|---|---|
| committer | Richard Maw <richard.maw@codethink.co.uk> | 2014-06-12 12:04:01 +0100 |
| commit | c5c1033c5c7deda8abe3448ec81bbb33c72219e0 (patch) | |
| tree | 6e5cef29b85161eea8a7488a029f5a32f982c6ab /libmount | |
| parent | 462008f79be9e195670b202cb43827b8aeb1e60b (diff) | |
| parent | 2fb567c080e1762ec6a2147564f03068f55d4f14 (diff) | |
| download | util-linux-baserock/morph.tar.gz | |
Merge branch 'baserock/richardmaw/yakshave/util-linux-blkid' into baserock/morphbaserock/morph
Reviewed-by: Lars Wirzenius
Reviewed-by: Sam Thursfield
Diffstat (limited to 'libmount')
34 files changed, 5312 insertions, 598 deletions
diff --git a/libmount/COPYING b/libmount/COPYING index 41fe6fd62..be1a5b3a1 100644 --- a/libmount/COPYING +++ b/libmount/COPYING @@ -4,5 +4,5 @@ License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. -The complete text of the license is available at the -Documentation/licenses/COPYING.LGPLv2.1 file. +The complete text of the license is available in the +../Documentation/licenses/COPYING.LGPLv2.1 file. diff --git a/libmount/Makemodule.am b/libmount/Makemodule.am index 81d550a47..8546c2f9b 100644 --- a/libmount/Makemodule.am +++ b/libmount/Makemodule.am @@ -1,6 +1,7 @@ if BUILD_LIBMOUNT include libmount/src/Makemodule.am +include libmount/python/Makemodule.am if ENABLE_GTK_DOC # Docs uses separate Makefiles @@ -11,5 +12,4 @@ pkgconfig_DATA += libmount/mount.pc PATHFILES += libmount/mount.pc EXTRA_DIST += libmount/COPYING - endif # BUILD_LIBMOUNT diff --git a/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt index 2c3dab722..6affbfbb6 100644 --- a/libmount/docs/libmount-sections.txt +++ b/libmount/docs/libmount-sections.txt @@ -3,6 +3,8 @@ libmnt_cache mnt_new_cache mnt_free_cache +mnt_ref_cache +mnt_unref_cache mnt_cache_device_has_tag mnt_cache_find_tag_value mnt_cache_read_tags @@ -36,11 +38,14 @@ mnt_context_enable_verbose mnt_context_get_cache mnt_context_get_fs mnt_context_get_fstab +mnt_context_get_fstab_userdata mnt_context_get_fstype +mnt_context_get_fs_userdata mnt_context_get_helper_status mnt_context_get_lock mnt_context_get_mflags mnt_context_get_mtab +mnt_context_get_mtab_userdata mnt_context_get_options mnt_context_get_optsmode mnt_context_get_source @@ -167,11 +172,15 @@ libmnt_fs mnt_copy_fs mnt_free_fs mnt_free_mntent +mnt_ref_fs +mnt_unref_fs mnt_fs_append_attributes +mnt_fs_append_comment mnt_fs_append_options mnt_fs_get_attribute mnt_fs_get_attributes mnt_fs_get_bindsrc +mnt_fs_get_comment mnt_fs_get_devno mnt_fs_get_freq mnt_fs_get_fs_options @@ -209,6 +218,7 @@ mnt_fs_prepend_options mnt_fs_print_debug mnt_fs_set_attributes mnt_fs_set_bindsrc +mnt_fs_set_comment mnt_fs_set_freq mnt_fs_set_fstype mnt_fs_set_options @@ -279,9 +289,14 @@ libmnt_table mnt_free_table mnt_new_table mnt_reset_table +mnt_ref_table +mnt_unref_table mnt_new_table_from_dir mnt_new_table_from_file mnt_table_add_fs +mnt_table_append_intro_comment +mnt_table_append_trailing_comment +mnt_table_enable_comments mnt_table_find_devno mnt_table_find_mountpoint mnt_table_find_next_fs @@ -290,10 +305,16 @@ mnt_table_find_source mnt_table_find_srcpath mnt_table_find_tag mnt_table_find_target +mnt_table_first_fs mnt_table_get_cache +mnt_table_get_intro_comment mnt_table_get_nents mnt_table_get_root_fs +mnt_table_get_trailing_comment +mnt_table_get_userdata +mnt_table_is_empty mnt_table_is_fs_mounted +mnt_table_last_fs mnt_table_next_child_fs mnt_table_next_fs mnt_table_parse_dir @@ -304,8 +325,12 @@ mnt_table_parse_stream mnt_table_parse_swaps mnt_table_remove_fs mnt_table_set_cache +mnt_table_set_intro_comment mnt_table_set_iter mnt_table_set_parser_errcb +mnt_table_set_trailing_comment +mnt_table_set_userdata +mnt_table_with_comments </SECTION> <SECTION> @@ -322,6 +347,8 @@ mnt_diff_tables libmnt_update mnt_free_update mnt_new_update +mnt_table_replace_file +mnt_table_write_file mnt_update_force_rdonly mnt_update_get_filename mnt_update_get_fs @@ -343,6 +370,7 @@ mnt_has_regular_mtab mnt_mangle mnt_match_fstype mnt_match_options +mnt_tag_is_valid mnt_unmangle </SECTION> diff --git a/libmount/python/Makemodule.am b/libmount/python/Makemodule.am new file mode 100644 index 000000000..efe210efd --- /dev/null +++ b/libmount/python/Makemodule.am @@ -0,0 +1,33 @@ +EXTRA_DIST += libmount/python/__init__.py + +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_SCRIPTS = libmount/python/__init__.py + +pylibmount_la_SOURCES = \ + libmount/python/pylibmount.c \ + libmount/python/pylibmount.h \ + libmount/python/fs.c \ + libmount/python/tab.c \ + libmount/python/context.c + +pylibmount_la_LIBADD = libmount.la $(PYTHON_LIBS) + +pylibmount_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(PYTHON_CFLAGS) \ + -I$(ul_libmount_incdir) \ + -fno-strict-aliasing #-ggdb3 -O0 + +pylibmount_la_LDFLAGS = \ + -avoid-version -module -shared -export-dynamic + +CLEANFILES += *.img + +endif # BUILD_PYLIBMOUNT diff --git a/libmount/python/__init__.py b/libmount/python/__init__.py new file mode 100644 index 000000000..09104e2e2 --- /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 000000000..408c8bd75 --- /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; + else { + 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" + }; + + 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" \ + "canonicalies paths when search in fstab and when prepare source and target paths\n" \ + "for mount(2) syscall.\n" \ + "\n" \ + "This fuction 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; + } + else 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; + } + else 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; + } + else 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; + } + else 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 (behaviour 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 (behaviour 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 infors libmount that used from [u]mount.type helper.\n" \ + "\n" \ + "The function also calls Cxt.disable_helpers() to avoid recursive\n" \ + "mount.type helpers calling. It you really want to call another\n" \ + "mount.type helper from your helper than 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*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc) Context_repr, + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Context_HELP, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Context_methods, /* tp_methods */ + Context_members, /* tp_members */ + Context_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Context_init, /* tp_init */ + 0, /* 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 000000000..0789d3797 --- /dev/null +++ b/libmount/python/fs.c @@ -0,0 +1,850 @@ +/* + * 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)); +} + +#define Fs_print_debug_HELP "print_debug()\n\n" +static PyObject *Fs_print_debug(FsObject *self) +{ + PySys_WriteStdout("------ fs: %p\n", self->fs); + PySys_WriteStdout("source: %s\n", mnt_fs_get_source(self->fs)); + PySys_WriteStdout("target: %s\n", mnt_fs_get_target(self->fs)); + PySys_WriteStdout("fstype: %s\n", mnt_fs_get_fstype(self->fs)); + + if (mnt_fs_get_options(self->fs)) + PySys_WriteStdout("optstr: %s\n", mnt_fs_get_options(self->fs)); + if (mnt_fs_get_vfs_options(self->fs)) + PySys_WriteStdout("VFS-optstr: %s\n", mnt_fs_get_vfs_options(self->fs)); + if (mnt_fs_get_fs_options(self->fs)) + PySys_WriteStdout("FS-opstr: %s\n", mnt_fs_get_fs_options(self->fs)); + if (mnt_fs_get_user_options(self->fs)) + PySys_WriteStdout("user-optstr: %s\n", mnt_fs_get_user_options(self->fs)); + if (mnt_fs_get_optional_fields(self->fs)) + PySys_WriteStdout("optional-fields: '%s'\n", mnt_fs_get_optional_fields(self->fs)); + if (mnt_fs_get_attributes(self->fs)) + PySys_WriteStdout("attributes: %s\n", mnt_fs_get_attributes(self->fs)); + + if (mnt_fs_get_root(self->fs)) + PySys_WriteStdout("root: %s\n", mnt_fs_get_root(self->fs)); + + if (mnt_fs_get_swaptype(self->fs)) + PySys_WriteStdout("swaptype: %s\n", mnt_fs_get_swaptype(self->fs)); + 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)) + PySys_WriteStdout("bindsrc: %s\n", mnt_fs_get_bindsrc(self->fs)); + 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)) + PySys_WriteStdout("comment: '%s'\n", 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; + + } else 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; + } else 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, PyObject *args, PyObject *kwds) +{ + unsigned long flags; + char *kwlist[] = {"flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "k", kwlist, &flags)) { + PyErr_SetString(PyExc_TypeError, ARG_ERR); + return NULL; + } + return PyObjectResultInt(mnt_fs_get_propagation(self->fs, &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_VARARGS|METH_KEYWORDS, 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, "destrutor 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 encapsualing 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; + + } else 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*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)Fs_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Fs_HELP, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Fs_methods, /* tp_methods */ + Fs_members, /* tp_members */ + Fs_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Fs_init, /* tp_init */ + 0, /* 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/pylibmount.c b/libmount/python/pylibmount.c new file mode 100644 index 000000000..c538bb543 --- /dev/null +++ b/libmount/python/pylibmount.c @@ -0,0 +1,301 @@ +/* + * 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_APPLYFLAGS: + PyErr_SetString(LibmountError, "Failed to apply MS_PROPAGATION flags"); + break; + case MNT_ERR_MOUNTOPT: + PyErr_SetString(LibmountError, "Failed to parse/use userspace mount options"); + break; + case MNT_ERR_NOFSTAB: + PyErr_SetString(LibmountError, "Failed to detect filesystem type"); + break; + case MNT_ERR_NOFSTYPE: + PyErr_SetString(LibmountError, "Required mount source undefined"); + break; + case MNT_ERR_NOSOURCE: + PyErr_SetString(LibmountError, "Loopdev setup failed"); + break; + case MNT_ERR_AMBIFS: + PyErr_SetString(LibmountError, "Libblkid detected more filesystems on the device"); + 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. Contex.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 +PyObject * PyInit_pylibmount(void); +PyObject * 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"); + + pylibmount_debug_mask = 0; + if (str) + pylibmount_debug_mask = strtoul(str, 0, 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); + Context_AddModuleObject(m); + + /* + * 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); + + /* + * 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 000000000..1a08796dc --- /dev/null +++ b/libmount/python/pylibmount.h @@ -0,0 +1,126 @@ +#ifndef UTIL_LINUX_PYLIBMOUNT_H +#define UTIL_LINUX_PYLIBMOUNT_H + +#include <Python.h> +#include <structmember.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); + +/* + * context.c + */ +typedef struct { + PyObject_HEAD + + struct libmnt_context *cxt; + PyObject *table_errcb; + +} ContextObjext; + +extern PyTypeObject ContextType; +extern void Context_AddModuleObject(PyObject *mod); + +/* + * 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 000000000..6f255aef7 --- /dev/null +++ b/libmount/python/tab.c @@ -0,0 +1,781 @@ +/* + * 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 preformed. 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; + } else 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, "destrutor 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 = PyEval_CallObject(((TableObject *)obj)->errcb, arglist); + 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 encapsualing 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*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc) Table_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Table_HELP, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Table_methods, /* tp_methods */ + Table_members, /* tp_members */ + Table_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Table_init, /* tp_init */ + 0, /* 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 000000000..7aea444b1 --- /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 000000000..1f63b9554 --- /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 000000000..a16e9e341 --- /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)) diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index 494e02a34..9101811f5 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -26,23 +26,26 @@ libmount_la_SOURCES = \ libmount/src/test.c \ libmount/src/utils.c \ libmount/src/version.c \ - $(mountinc_HEADERS) + $(nodist_mountinc_HEADERS) nodist_libmount_la_SOURCES = libmount/src/mountP.h libmount_la_LIBADD = libcommon.la libblkid.la $(SELINUX_LIBS) libmount_la_CFLAGS = \ + $(SOLIB_CFLAGS) \ -I$(ul_libblkid_incdir) \ -I$(ul_libmount_incdir) \ -I$(top_srcdir)/libmount/src libmount_la_DEPENDENCIES = \ - $(libmount_la_LIBADD) \ + libcommon.la \ + libblkid.la \ libmount/src/libmount.sym \ libmount/src/libmount.h.in libmount_la_LDFLAGS = \ + $(SOLIB_LDFLAGS) \ -Wl,--version-script=$(top_srcdir)/libmount/src/libmount.sym \ -version-info $(LIBMOUNT_VERSION_INFO) diff --git a/libmount/src/cache.c b/libmount/src/cache.c index 7b651228a..92f469456 100644 --- a/libmount/src/cache.c +++ b/libmount/src/cache.c @@ -10,7 +10,7 @@ * @title: Cache * @short_description: paths and tags (UUID/LABEL) caching * - * The cache is a very simple API for work with tags (LABEL, UUID, ...) and + * The cache is a very simple API for working with tags (LABEL, UUID, ...) and * paths. The cache uses libblkid as a backend for TAGs resolution. * * All returned paths are always canonicalized. @@ -48,6 +48,7 @@ struct libmnt_cache { struct mnt_cache_entry *ents; size_t nents; size_t nallocs; + int refcount; /* blkid_evaluate_tag() works in two ways: * @@ -72,6 +73,7 @@ struct libmnt_cache *mnt_new_cache(void) if (!cache) return NULL; DBG(CACHE, mnt_debug_h(cache, "alloc")); + cache->refcount = 1; return cache; } @@ -79,7 +81,8 @@ struct libmnt_cache *mnt_new_cache(void) * mnt_free_cache: * @cache: pointer to struct libmnt_cache instance * - * Deallocates the cache. + * Deallocates the cache. This function does not care about reference count. Don't + * use this function directly -- it's better to use use mnt_unref_cache(). */ void mnt_free_cache(struct libmnt_cache *cache) { @@ -89,6 +92,7 @@ void mnt_free_cache(struct libmnt_cache *cache) return; DBG(CACHE, mnt_debug_h(cache, "free")); + WARN_REFCOUNT(CACHE, cache, cache->refcount); for (i = 0; i < cache->nents; i++) { struct mnt_cache_entry *e = &cache->ents[i]; @@ -102,7 +106,39 @@ void mnt_free_cache(struct libmnt_cache *cache) free(cache); } -/* note that the @key could be tha same pointer as @value */ +/** + * mnt_ref_cache: + * @cache: cache pointer + * + * Increments reference counter. + */ +void mnt_ref_cache(struct libmnt_cache *cache) +{ + if (cache) { + cache->refcount++; + /*DBG(CACHE, mnt_debug_h(cache, "ref=%d", cache->refcount));*/ + } +} + +/** + * mnt_unref_cache: + * @cache: cache pointer + * + * De-increments reference counter, on zero the cache is automatically + * deallocated by mnt_free_cache(). + */ +void mnt_unref_cache(struct libmnt_cache *cache) +{ + if (cache) { + cache->refcount--; + /*DBG(CACHE, mnt_debug_h(cache, "unref=%d", cache->refcount));*/ + if (cache->refcount <= 0) + mnt_free_cache(cache); + } +} + + +/* note that the @key could be the same pointer as @value */ static int cache_add_entry(struct libmnt_cache *cache, char *key, char *value, int flag) { @@ -135,7 +171,7 @@ static int cache_add_entry(struct libmnt_cache *cache, char *key, return 0; } -/* add tag to the cache, @devname has to be allocated string */ +/* add tag to the cache, @devname has to be an allocated string */ static int cache_add_tag(struct libmnt_cache *cache, const char *tagname, const char *tagval, char *devname, int flag) { @@ -270,13 +306,13 @@ int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname) DBG(CACHE, mnt_debug_h(cache, "tags for %s requested", devname)); - /* check is device is already cached */ + /* check if device is already cached */ for (i = 0; i < cache->nents; i++) { struct mnt_cache_entry *e = &cache->ents[i]; if (!(e->flag & MNT_CACHE_TAGREAD)) continue; if (strcmp(e->value, devname) == 0) - /* tags has been already read */ + /* tags have already been read */ return 0; } @@ -335,7 +371,7 @@ error: * @token: tag name (e.g "LABEL") * @value: tag value * - * Look up @cache to check it @tag+@value are associated with @devname. + * Look up @cache to check if @tag+@value are associated with @devname. * * Returns: 1 on success or 0. */ @@ -421,7 +457,7 @@ char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache) rc = blkid_do_safeprobe(pr); - DBG(CACHE, mnt_debug_h(cache, "liblkid rc=%d", rc)); + DBG(CACHE, mnt_debug_h(cache, "libblkid rc=%d", rc)); if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL)) type = strdup(data); @@ -493,7 +529,7 @@ error: * - /dev/loopN to the loop backing filename * - empty path (NULL) to 'none' * - * Returns: new allocated string with path, result has to be always deallocated + * Returns: newly allocated string with path, result always has to be deallocated * by free(). */ char *mnt_pretty_path(const char *path, struct libmnt_cache *cache) @@ -583,22 +619,18 @@ error: char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache) { char *cn = NULL; + char *t = NULL, *v = NULL; if (!spec) return NULL; - if (strchr(spec, '=')) { - char *tag, *val; - - if (!blkid_parse_tag_string(spec, &tag, &val)) { - cn = mnt_resolve_tag(tag, val, cache); - - free(tag); - free(val); - } - } else + if (blkid_parse_tag_string(spec, &t, &v) == 0 && mnt_valid_tagname(t)) + cn = mnt_resolve_tag(t, v, cache); + else cn = mnt_resolve_path(spec, cache); + free(t); + free(v); return cn; } @@ -624,7 +656,7 @@ int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[]) p = mnt_resolve_path(line, cache); printf("%s : %s\n", line, p); } - mnt_free_cache(cache); + mnt_unref_cache(cache); return 0; } @@ -647,7 +679,7 @@ int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[]) p = mnt_resolve_spec(line, cache); printf("%s : %s\n", line, p); } - mnt_free_cache(cache); + mnt_unref_cache(cache); return 0; } @@ -663,6 +695,7 @@ int test_read_tags(struct libmnt_test *ts, int argc, char *argv[]) while(fgets(line, sizeof(line), stdin)) { size_t sz = strlen(line); + char *t = NULL, *v = NULL; if (sz > 0 && line[sz - 1] == '\n') line[sz - 1] = '\0'; @@ -674,16 +707,14 @@ int test_read_tags(struct libmnt_test *ts, int argc, char *argv[]) if (mnt_cache_read_tags(cache, line) < 0) fprintf(stderr, "%s: read tags failed\n", line); - } else if (strchr(line, '=')) { - char *tag, *val; + } else if (blkid_parse_tag_string(line, &t, &v) == 0) { const char *cn = NULL; - if (!blkid_parse_tag_string(line, &tag, &val)) { - cn = cache_find_tag(cache, tag, val); + if (mnt_valid_tagname(t)) + cn = cache_find_tag(cache, t, v); + free(t); + free(v); - free(tag); - free(val); - } if (cn) printf("%s: %s\n", line, cn); else @@ -700,7 +731,7 @@ int test_read_tags(struct libmnt_test *ts, int argc, char *argv[]) e->key + strlen(e->key) + 1); } - mnt_free_cache(cache); + mnt_unref_cache(cache); return 0; } diff --git a/libmount/src/context.c b/libmount/src/context.c index af16f6472..8dca3c12a 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -89,10 +89,8 @@ void mnt_free_context(struct libmnt_context *cxt) free(cxt->fstype_pattern); free(cxt->optstr_pattern); - if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) - mnt_free_table(cxt->fstab); - if (!(cxt->flags & MNT_FL_EXTERN_CACHE)) - mnt_free_cache(cxt->cache); + mnt_unref_table(cxt->fstab); + mnt_unref_cache(cxt->cache); mnt_context_clear_loopdev(cxt); mnt_free_lock(cxt->lock); @@ -108,11 +106,11 @@ void mnt_free_context(struct libmnt_context *cxt) * mnt_reset_context: * @cxt: mount context * - * Resets all information in the context that are directly related to - * the latest mount (spec, source, target, mount options, ....) + * Resets all information in the context that is directly related to + * the latest mount (spec, source, target, mount options, ...). * - * The match patters, cached fstab, cached canonicalized paths and tags and - * [e]uid are not reseted. You have to use + * The match patterns, cached fstab, cached canonicalized paths and tags and + * [e]uid are not reset. You have to use * * mnt_context_set_fstab(cxt, NULL); * mnt_context_set_cache(cxt, NULL); @@ -120,7 +118,7 @@ void mnt_free_context(struct libmnt_context *cxt) * mnt_context_set_options_pattern(cxt, NULL); * * - * to reset these stuff. + * to reset this stuff. * * Returns: 0 on success, negative number in case of error. */ @@ -136,10 +134,8 @@ int mnt_reset_context(struct libmnt_context *cxt) fl = cxt->flags; - if (!(cxt->flags & MNT_FL_EXTERN_FS)) - mnt_free_fs(cxt->fs); - - mnt_free_table(cxt->mtab); + mnt_unref_fs(cxt->fs); + mnt_unref_table(cxt->mtab); free(cxt->helper); free(cxt->orig_user); @@ -164,9 +160,7 @@ int mnt_reset_context(struct libmnt_context *cxt) mnt_context_reset_status(cxt); mnt_context_set_tabfilter(cxt, NULL, NULL); - /* restore non-resetable flags */ - cxt->flags |= (fl & MNT_FL_EXTERN_FSTAB); - cxt->flags |= (fl & MNT_FL_EXTERN_CACHE); + /* restore non-resettable flags */ cxt->flags |= (fl & MNT_FL_NOMTAB); cxt->flags |= (fl & MNT_FL_FAKE); cxt->flags |= (fl & MNT_FL_SLOPPY); @@ -225,7 +219,7 @@ static int set_flag(struct libmnt_context *cxt, int flag, int enable) * mnt_context_is_restricted: * @cxt: mount context * - * Returns: 0 for unrestricted mount (user is root), or 1 for non-root mounts + * Returns: 0 for an unrestricted mount (user is root), or 1 for non-root mounts */ int mnt_context_is_restricted(struct libmnt_context *cxt) { @@ -235,9 +229,9 @@ int mnt_context_is_restricted(struct libmnt_context *cxt) /** * mnt_context_set_optsmode * @cxt: mount context - * @mode: MNT_OMASK_* flags + * @mode: MNT_OMODE_* flags * - * Controls how to use mount optionsmsource and target paths from fstab/mtab. + * Controls how to use mount optionssource and target paths from fstab/mtab. * * @MNT_OMODE_IGNORE: ignore mtab/fstab options * @@ -247,7 +241,7 @@ int mnt_context_is_restricted(struct libmnt_context *cxt) * * @MNT_OMODE_REPLACE: replace existing options with options from mtab/fstab * - * @MNT_OMODE_FORCE: always read mtab/fstab (although source and target is defined) + * @MNT_OMODE_FORCE: always read mtab/fstab (although source and target are defined) * * @MNT_OMODE_FSTAB: read from fstab * @@ -262,8 +256,8 @@ int mnt_context_is_restricted(struct libmnt_context *cxt) * Notes: * * - MNT_OMODE_USER is always used if mount context is in restricted mode - * - MNT_OMODE_AUTO is used if nothing other is defined - * - the flags are eavaluated in this order: MNT_OMODE_NOTAB, MNT_OMODE_FORCE, + * - MNT_OMODE_AUTO is used if nothing else is defined + * - the flags are evaluated in this order: MNT_OMODE_NOTAB, MNT_OMODE_FORCE, * MNT_OMODE_FSTAB, MNT_OMODE_MTAB and then the mount options from fstab/mtab * are set according to MNT_OMODE_{IGNORE,APPEND,PREPAND,REPLACE} * @@ -282,7 +276,7 @@ int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode) * mnt_context_get_optsmode * @cxt: mount context * - * Returns: MNT_OMASK_* mask or zero. + * Returns: MNT_OMODE_* mask or zero. */ int mnt_context_get_optsmode(struct libmnt_context *cxt) @@ -297,12 +291,12 @@ int mnt_context_get_optsmode(struct libmnt_context *cxt) * @disable: TRUE or FALSE * * Enable/disable paths canonicalization and tags evaluation. The libmount context - * canonicalies paths when search in fstab and when prepare source and target paths + * canonicalizes paths when searching in fstab and when preparing source and target paths * for mount(2) syscall. * - * This fuction has effect to the private (within context) fstab instance only + * This fuction has an effect on the private (within context) fstab instance only * (see mnt_context_set_fstab()). If you want to use an external fstab then you - * need manage your private struct libmnt_cache (see mnt_table_set_cache(fstab, + * need to manage your private struct libmnt_cache (see mnt_table_set_cache(fstab, * NULL). * * Returns: 0 on success, negative number in case of error. @@ -316,7 +310,7 @@ int mnt_context_disable_canonicalize(struct libmnt_context *cxt, int disable) * mnt_context_is_nocanonicalize: * @cxt: mount context * - * Returns: 1 if no-canonicalize mode enabled or 0. + * Returns: 1 if no-canonicalize mode is enabled or 0. */ int mnt_context_is_nocanonicalize(struct libmnt_context *cxt) { @@ -389,10 +383,13 @@ int mnt_context_is_parent(struct libmnt_context *cxt) * mnt_context_is_child: * @cxt: mount context * - * Return: 1 if mount -F enabled and the current context is child, or 0 + * Return: 1 f the current context is child, or 0 */ int mnt_context_is_child(struct libmnt_context *cxt) { + /* See mnt_fork_context(), the for fork flag is always disabled + * for children to avoid recursive forking. + */ return !mnt_context_is_fork(cxt) && cxt->pid; } @@ -415,7 +412,7 @@ int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable) * mnt_context_is_rdonly_umount * @cxt: mount context * - * See also mnt_context_enable_rdonly_umount() and see umount(8) man page, + * See also mnt_context_enable_rdonly_umount() and umount(8) man page, * option -r. * * Returns: 1 if read-only remount failed umount(2) is enables or 0 @@ -607,7 +604,7 @@ int mnt_context_is_verbose(struct libmnt_context *cxt) * @cxt: mount context * @enable: TRUE or FALSE * - * Enable/disable loop delete (destroy) after umount (see umount(8), option -d) + * Enable/disable the loop delete (destroy) after umount (see umount(8), option -d) * * Returns: 0 on success, negative number in case of error. */ @@ -633,12 +630,13 @@ int mnt_context_is_loopdel(struct libmnt_context *cxt) * @fs: filesystem description * * The mount context uses private @fs by default. This function allows to - * overwrite the private @fs with an external instance. Note that the external - * @fs instance is not deallocated by mnt_free_context() or mnt_reset_context(). + * overwrite the private @fs with an external instance. This function + * increments @fs reference counter (and deincrement reference counter of the + * old fs). * * The @fs will be modified by mnt_context_set_{source,target,options,fstype} - * functions, If the @fs is NULL then all current FS specific setting (source, - * target, etc., exclude spec) is reseted. + * functions, If the @fs is NULL, then all current FS specific settings (source, + * target, etc., exclude spec) are reset. * * Returns: 0 on success, negative number in case of error. */ @@ -646,10 +644,9 @@ int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs) { if (!cxt) return -EINVAL; - if (!(cxt->flags & MNT_FL_EXTERN_FS)) - mnt_free_fs(cxt->fs); - set_flag(cxt, MNT_FL_EXTERN_FS, fs != NULL); + mnt_ref_fs(fs); /* new */ + mnt_unref_fs(cxt->fs); /* old */ cxt->fs = fs; return 0; } @@ -662,25 +659,64 @@ int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs) * Note that the FS is modified by mnt_context_set_{source,target,options,fstype} * functions. * - * Returns: pointer to FS description or NULL in case of calloc() errrr. + * Returns: pointer to FS description or NULL in case of a calloc() error. */ struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt) { assert(cxt); if (!cxt) return NULL; - if (!cxt->fs) { + if (!cxt->fs) cxt->fs = mnt_new_fs(); - cxt->flags &= ~MNT_FL_EXTERN_FS; - } return cxt->fs; } /** + * mnt_context_get_fs_userdata: + * @cxt: mount context + * + * Returns: pointer to userdata or NULL. + */ +void *mnt_context_get_fs_userdata(struct libmnt_context *cxt) +{ + assert(cxt); + return cxt->fs ? mnt_fs_get_userdata(cxt->fs) : NULL; +} + +/** + * mnt_context_get_fstab_userdata: + * @cxt: mount context + * + * Returns: pointer to userdata or NULL. + */ +void *mnt_context_get_fstab_userdata(struct libmnt_context *cxt) +{ + assert(cxt); + return cxt->fstab ? mnt_table_get_userdata(cxt->fstab) : NULL; +} + +/** + * mnt_context_get_mtab_userdata: + * @cxt: mount context + * + * Returns: pointer to userdata or NULL. + */ +void *mnt_context_get_mtab_userdata(struct libmnt_context *cxt) +{ + assert(cxt); + return cxt->mtab ? mnt_table_get_userdata(cxt->mtab) : NULL; +} + +/** * mnt_context_set_source: * @cxt: mount context * @source: mount source (device, directory, UUID, LABEL, ...) * + * Note that libmount does not interpret "nofail" (MNT_MS_NOFAIL) + * mount option. The real return code is always returned, when + * the device does not exist then it's usually MNT_ERR_NOSOURCE + * from libmount or ENOENT, ENOTDIR, ENOTBLK, ENXIO from moun(2). + * * Returns: 0 on success, negative number in case of error. */ int mnt_context_set_source(struct libmnt_context *cxt, const char *source) @@ -693,7 +729,7 @@ int mnt_context_set_source(struct libmnt_context *cxt, const char *source) * mnt_context_get_source: * @cxt: mount context * - * Returns: returns pointer or NULL in case of error pr if not set. + * Returns: returns pointer or NULL in case of error or if not set. */ const char *mnt_context_get_source(struct libmnt_context *cxt) { @@ -718,7 +754,7 @@ int mnt_context_set_target(struct libmnt_context *cxt, const char *target) * mnt_context_get_target: * @cxt: mount context * - * Returns: returns pointer or NULL in case of error pr if not set. + * Returns: returns pointer or NULL in case of error or if not set. */ const char *mnt_context_get_target(struct libmnt_context *cxt) { @@ -731,8 +767,8 @@ const char *mnt_context_get_target(struct libmnt_context *cxt) * @cxt: mount context * @fstype: filesystem type * - * Note that the @fstype has to be the real FS type. For patterns with - * comma-separated list of filesystems or for "nofs" notation use + * Note that the @fstype has to be a FS type. For patterns with + * comma-separated list of filesystems or for the "nofs" notation, use * mnt_context_set_fstype_pattern(). * * Returns: 0 on success, negative number in case of error. @@ -747,7 +783,7 @@ int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype) * mnt_context_get_fstype: * @cxt: mount context * - * Returns: pointer or NULL in case of error pr if not set. + * Returns: pointer or NULL in case of error or if not set. */ const char *mnt_context_get_fstype(struct libmnt_context *cxt) { @@ -788,8 +824,8 @@ int mnt_context_append_options(struct libmnt_context *cxt, const char *optstr) * This function returns mount options set by mnt_context_set_options() or * mnt_context_append_options(). * - * Note that *after* mnt_context_prepare_mount() may the mount options string - * also includes options set by mnt_context_set_mflags() or another options + * Note that *after* mnt_context_prepare_mount(), the mount options string + * may also include options set by mnt_context_set_mflags() or other options * generated by this library. * * Returns: pointer or NULL @@ -857,15 +893,19 @@ int mnt_context_set_options_pattern(struct libmnt_context *cxt, const char *patt * @cxt: mount context * @tb: fstab * - * The mount context reads /etc/fstab to the the private struct libmnt_table by default. + * The mount context reads /etc/fstab to the private struct libmnt_table by default. * This function allows to overwrite the private fstab with an external - * instance. Note that the external instance is not deallocated by mnt_free_context(). + * instance. + * + * This function modify the @tb reference counter. This function does not set + * the cache for the @tb. You have to explicitly call mnt_table_set_cache(tb, + * mnt_context_get_cache(cxt)); * * The fstab is used read-only and is not modified, it should be possible to - * share the fstab between more mount contexts (TODO: tests it.) + * share the fstab between more mount contexts (TODO: test it.) * - * If the @tb argument is NULL then the current private fstab instance is - * reseted. + * If the @tb argument is NULL, then the current private fstab instance is + * reset. * * Returns: 0 on success, negative number in case of error. */ @@ -874,10 +914,10 @@ int mnt_context_set_fstab(struct libmnt_context *cxt, struct libmnt_table *tb) assert(cxt); if (!cxt) return -EINVAL; - if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) - mnt_free_table(cxt->fstab); - set_flag(cxt, MNT_FL_EXTERN_FSTAB, tb != NULL); + mnt_ref_table(tb); /* new */ + mnt_unref_table(cxt->fstab); /* old */ + cxt->fstab = tb; return 0; } @@ -893,8 +933,6 @@ int mnt_context_set_fstab(struct libmnt_context *cxt, struct libmnt_table *tb) */ int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb) { - struct libmnt_cache *cache; - assert(cxt); if (!cxt) return -EINVAL; @@ -906,18 +944,12 @@ int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb) return -ENOMEM; if (cxt->table_errcb) mnt_table_set_parser_errcb(cxt->fstab, cxt->table_errcb); - cxt->flags &= ~MNT_FL_EXTERN_FSTAB; + mnt_table_set_cache(cxt->fstab, mnt_context_get_cache(cxt)); rc = mnt_table_parse_fstab(cxt->fstab, NULL); if (rc) return rc; } - cache = mnt_context_get_cache(cxt); - - /* never touch an external fstab */ - if (!(cxt->flags & MNT_FL_EXTERN_FSTAB)) - mnt_table_set_cache(cxt->fstab, cache); - if (tb) *tb = cxt->fstab; return 0; @@ -929,14 +961,12 @@ int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb) * @tb: returns mtab * * See also mnt_table_parse_mtab() for more details about mtab/mountinfo. The - * result will deallocated by mnt_free_context(@cxt). + * result will be deallocated by mnt_free_context(@cxt). * * Returns: 0 on success, negative number in case of error. */ int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb) { - struct libmnt_cache *cache; - assert(cxt); if (!cxt) return -EINVAL; @@ -954,14 +984,12 @@ int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb) cxt->table_fltrcb, cxt->table_fltrcb_data); + mnt_table_set_cache(cxt->mtab, mnt_context_get_cache(cxt)); rc = mnt_table_parse_mtab(cxt->mtab, cxt->mtab_path); if (rc) return rc; } - cache = mnt_context_get_cache(cxt); - mnt_table_set_cache(cxt->mtab, cache); - if (tb) *tb = cxt->mtab; @@ -971,8 +999,8 @@ int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb) } /* - * Allows to specify filter for tab file entries. The filter is called by - * table parser. Currently used for mtab and utab only. + * Allows to specify a filter for tab file entries. The filter is called by + * the table parser. Currently used for mtab and utab only. */ int mnt_context_set_tabfilter(struct libmnt_context *cxt, int (*fltr)(struct libmnt_fs *, void *), @@ -1004,7 +1032,7 @@ int mnt_context_set_tabfilter(struct libmnt_context *cxt, * callback and cache for tags and paths is set according to the @cxt setting. * See also mnt_table_parse_file(). * - * It's strongly recommended use mnt_context_get_mtab() and + * It's strongly recommended to use the mnt_context_get_mtab() and * mnt_context_get_fstab() functions for mtab and fstab files. This function * does not care about LIBMOUNT_* env.variables and does not merge userspace * options. @@ -1016,7 +1044,6 @@ int mnt_context_set_tabfilter(struct libmnt_context *cxt, int mnt_context_get_table(struct libmnt_context *cxt, const char *filename, struct libmnt_table **tb) { - struct libmnt_cache *cache; int rc; assert(cxt); @@ -1033,14 +1060,11 @@ int mnt_context_get_table(struct libmnt_context *cxt, rc = mnt_table_parse_file(*tb, filename); if (rc) { - mnt_free_table(*tb); + mnt_unref_table(*tb); return rc; } - cache = mnt_context_get_cache(cxt); - if (cache) - mnt_table_set_cache(*tb, cache); - + mnt_table_set_cache(*tb, mnt_context_get_cache(cxt)); return 0; } @@ -1065,6 +1089,11 @@ int mnt_context_set_tables_errcb(struct libmnt_context *cxt, if (!cxt) return -EINVAL; + if (cxt->mtab) + mnt_table_set_parser_errcb(cxt->mtab, cb); + if (cxt->fstab) + mnt_table_set_parser_errcb(cxt->fstab, cb); + cxt->table_errcb = cb; return 0; } @@ -1074,12 +1103,15 @@ int mnt_context_set_tables_errcb(struct libmnt_context *cxt, * @cxt: mount context * @cache: cache instance or nULL * - * The mount context maintains a private struct libmnt_cache by default. This function - * allows to overwrite the private cache with an external instance. Note that - * the external instance is not deallocated by mnt_free_context(). + * The mount context maintains a private struct libmnt_cache by default. This + * function allows to overwrite the private cache with an external instance. + * This function increments cache reference counter. * - * If the @cache argument is NULL then the current private cache instance is - * reseted. + * If the @cache argument is NULL, then the current cache instance is reset. + * This function apply the cache to fstab and mtab instances (if already + * exists). + * + * The old cache instance reference counter is de-incremented. * * Returns: 0 on success, negative number in case of error. */ @@ -1087,11 +1119,17 @@ int mnt_context_set_cache(struct libmnt_context *cxt, struct libmnt_cache *cache { if (!cxt) return -EINVAL; - if (!(cxt->flags & MNT_FL_EXTERN_CACHE)) - mnt_free_cache(cxt->cache); - set_flag(cxt, MNT_FL_EXTERN_CACHE, cache != NULL); + mnt_ref_cache(cache); /* new */ + mnt_unref_cache(cxt->cache); /* old */ + cxt->cache = cache; + + if (cxt->mtab) + mnt_table_set_cache(cxt->mtab, cache); + if (cxt->fstab) + mnt_table_set_cache(cxt->fstab, cache); + return 0; } @@ -1110,10 +1148,9 @@ struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt) return NULL; if (!cxt->cache) { - cxt->cache = mnt_new_cache(); - if (!cxt->cache) - return NULL; - cxt->flags &= ~MNT_FL_EXTERN_CACHE; + struct libmnt_cache *cache = mnt_new_cache(); + mnt_context_set_cache(cxt, cache); + mnt_unref_cache(cache); } return cxt->cache; } @@ -1125,7 +1162,7 @@ struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt) * @release: callback to release (delallocate) password * * Sets callbacks for encryption password (e.g encrypted loopdev). This - * function is deprecated (encrypted loops are no ore supported). + * function is deprecated (encrypted loops are no longer supported). * * Returns: 0 on success, negative number in case of error. */ @@ -1151,7 +1188,7 @@ int mnt_context_set_passwd_cb(struct libmnt_context *cxt, * * The default behavior is to ignore all signals (except SIGALRM and * SIGTRAP for mtab udate) when the lock is locked. If this behavior - * is unacceptable then use: + * is unacceptable, then use: * * lc = mnt_context_get_lock(cxt); * if (lc) @@ -1197,7 +1234,7 @@ struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt) * * mnt_context_set_options(cxt, "noexec,nosuid"); * - * these both calls have the same effect. + * both of these calls have the same effect. * * Returns: 0 on success, negative number in case of error. */ @@ -1227,7 +1264,7 @@ int mnt_context_set_mflags(struct libmnt_context *cxt, unsigned long flags) * @flags: returns MS_* mount flags * * Converts mount options string to MS_* flags and bitewise-OR the result with - * already defined flags (see mnt_context_set_mflags()). + * the already defined flags (see mnt_context_set_mflags()). * * Returns: 0 on success, negative number in case of error. */ @@ -1268,7 +1305,7 @@ int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags) * * Sets userspace mount flags. * - * See also notest for mnt_context_set_mflags(). + * See also notes for mnt_context_set_mflags(). * * Returns: 0 on success, negative number in case of error. */ @@ -1287,7 +1324,7 @@ int mnt_context_set_user_mflags(struct libmnt_context *cxt, unsigned long flags) * @flags: returns mount flags * * Converts mount options string to MNT_MS_* flags and bitewise-OR the result - * with already defined flags (see mnt_context_set_user_mflags()). + * with the already defined flags (see mnt_context_set_user_mflags()). * * Returns: 0 on success, negative number in case of error. */ @@ -1321,7 +1358,7 @@ int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags * function allows to overwrite this behavior, and @data will be used instead * of mount options. * - * The libmount does not deallocated the data by mnt_free_context(). Note that + * The libmount does not deallocate the data by mnt_free_context(). Note that * NULL is also valid mount data. * * Returns: 0 on success, negative number in case of error. @@ -1362,7 +1399,7 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt) return mnt_fs_set_source(cxt->fs, "none"); /* ignore filesystems without source or filesystems - * where the source is quasi-path (//foo/bar) + * where the source is a quasi-path (//foo/bar) */ if (!src || mnt_fs_is_netfs(cxt->fs)) return 0; @@ -1504,8 +1541,8 @@ int mnt_context_prepare_target(struct libmnt_context *cxt) } /* - * It's usully no error when we're not able to detect filesystem type -- we - * will try to use types from /{etc,proc}/filesystems. + * It's usually no error when we're not able to detect the filesystem type -- we + * will try to use the types from /{etc,proc}/filesystems. */ int mnt_context_guess_fstype(struct libmnt_context *cxt) { @@ -1595,6 +1632,7 @@ int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name, if (mnt_context_is_nohelpers(cxt) || !type || !strcmp(type, "none") + || strstr(type, "/..") /* don't try to smuggle path */ || mnt_fs_is_swaparea(cxt->fs)) return 0; @@ -1798,8 +1836,8 @@ static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb, * * mount /foo/bar * - * the path could be a mountpoint as well as source (for - * example bind mount, symlink to device, ...). + * the path could be a mountpoint as well as a source (for + * example bind mount, symlink to a device, ...). */ if (src && !mnt_fs_get_tag(cxt->fs, NULL, NULL)) fs = mnt_table_find_target(tb, src, direction); @@ -1869,7 +1907,7 @@ int mnt_context_apply_fstab(struct libmnt_context *cxt) DBG(CXT, mnt_debug_h(cxt, "force fstab usage for non-root users!")); cxt->optsmode = MNT_OMODE_USER; } else if (cxt->optsmode == 0) { - DBG(CXT, mnt_debug_h(cxt, "use default optmode")); + DBG(CXT, mnt_debug_h(cxt, "use default optsmode")); cxt->optsmode = MNT_OMODE_AUTO; } else if (cxt->optsmode & MNT_OMODE_NOTAB) { cxt->optsmode &= ~MNT_OMODE_FSTAB; @@ -1944,7 +1982,7 @@ int mnt_context_tab_applied(struct libmnt_context *cxt) } /* - * This is not public function! + * This is not a public function! * * Returns 1 if *only propagation flags* change is requested. */ @@ -1974,7 +2012,7 @@ int mnt_context_propagation_only(struct libmnt_context *cxt) * Global libmount status. * * The real exit code of the mount.type helper has to be tested by - * mnt_context_get_helper_status(). The mnt_context_get_status() only inform + * mnt_context_get_helper_status(). The mnt_context_get_status() only informs * that exec() has been successful. * * Returns: 1 if mount.type or mount(2) syscall has been successfully called. @@ -2046,7 +2084,7 @@ int mnt_context_get_syscall_errno(struct libmnt_context *cxt) * * The @status should be 0 on success, or negative number on error (-errno). * - * This function should be used only if [u]mount(2) syscall is NOT called by + * This function should only be used if the [u]mount(2) syscall is NOT called by * libmount code. * * Returns: 0 or negative number in case of error. @@ -2086,11 +2124,11 @@ int mnt_context_strerror(struct libmnt_context *cxt __attribute__((__unused__)), * @action: MNT_ACT_{UMOUNT,MOUNT} * @flags: not used now * - * This function infors libmount that used from [u]mount.type helper. + * This function informs libmount that used from [u]mount.type helper. * * The function also calls mnt_context_disable_helpers() to avoid recursive * mount.type helpers calling. It you really want to call another - * mount.type helper from your helper than you have to explicitly enable this + * mount.type helper from your helper, then you have to explicitly enable this * feature by: * * mnt_context_disable_helpers(cxt, FALSE); @@ -2120,7 +2158,7 @@ int mnt_context_init_helper(struct libmnt_context *cxt, int action, * @c: getopt() result * @arg: getopt() optarg * - * This function applies [u]mount.type command line option (for example parsed + * This function applies the [u]mount.type command line option (for example parsed * by getopt or getopt_long) to @cxt. All unknown options are ignored and * then 1 is returned. * @@ -2145,7 +2183,7 @@ int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg) * @fs: filesystem * @mounted: returns 1 for mounted and 0 for non-mounted filesystems * - * Please, read mnt_table_is_fs_mounted() description! + * Please, read the mnt_table_is_fs_mounted() description! * * Returns: 0 on success and negative number in case of error. */ @@ -2308,7 +2346,7 @@ int test_mount(struct libmnt_test *ts, int argc, char *argv[]) mnt_context_set_target(cxt, argv[idx++]); } - /* this is unnecessary! -- libmount is able to internaly + /* this is unnecessary! -- libmount is able to internally * create and manage the lock */ lock = mnt_context_get_lock(cxt); @@ -2317,7 +2355,7 @@ int test_mount(struct libmnt_test *ts, int argc, char *argv[]) rc = mnt_context_mount(cxt); if (rc) - printf("failed to mount: %m\n"); + warn("failed to mount"); else printf("successfully mounted\n"); diff --git a/libmount/src/context_loopdev.c b/libmount/src/context_loopdev.c index 576b7f55a..44a7216b8 100644 --- a/libmount/src/context_loopdev.c +++ b/libmount/src/context_loopdev.c @@ -47,10 +47,10 @@ int mnt_context_is_loopdev(struct libmnt_context *cxt) /* Automatically create a loop device from a regular file if a * filesystem is not specified or the filesystem is known for libblkid * (these filesystems work with block devices only). The file size - * should be at least 1KiB otherwise we will create empty loopdev where - * is no mountable filesystem... + * should be at least 1KiB, otherwise we will create an empty loopdev with + * no mountable filesystem... * - * Note that there is not a restriction (on kernel side) that prevents regular + * Note that there is no restriction (on kernel side) that would prevent a regular * file as a mount(2) source argument. A filesystem that is able to mount * regular files could be implemented. */ @@ -73,7 +73,7 @@ int mnt_context_is_loopdev(struct libmnt_context *cxt) } -/* Check, if there already exists a mounted loop device on the mountpoint node +/* Check if there already exists a mounted loop device on the mountpoint node * with the same parameters. */ static int __attribute__((nonnull)) @@ -86,6 +86,8 @@ is_mounted_same_loopfile(struct libmnt_context *cxt, struct libmnt_iter itr; struct libmnt_fs *fs; struct libmnt_cache *cache; + const char *bf; + int rc = 0; assert(cxt); assert(cxt->fs); @@ -100,39 +102,36 @@ is_mounted_same_loopfile(struct libmnt_context *cxt, cache = mnt_context_get_cache(cxt); mnt_reset_iter(&itr, MNT_ITER_BACKWARD); - /* Search for mountpoint node in mtab, procceed if any of these has the + bf = cache ? mnt_resolve_path(backing_file, cache) : backing_file; + + /* Search for a mountpoint node in mtab, proceed if any of these have the * loop option set or the device is a loop device */ - while (mnt_table_next_fs(tb, &itr, &fs) == 0) { + while (rc == 0 && mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *src = mnt_fs_get_source(fs); const char *opts = mnt_fs_get_user_options(fs); char *val; size_t len; - int res = 0; if (!src || !mnt_fs_match_target(fs, target, cache)) continue; + rc = 0; + if (strncmp(src, "/dev/loop", 9) == 0) { - res = loopdev_is_used((char *) src, backing_file, - offset, LOOPDEV_FL_OFFSET); + rc = loopdev_is_used((char *) src, bf, offset, LOOPDEV_FL_OFFSET); } else if (opts && (cxt->user_mountflags & MNT_MS_LOOP) && mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) { val = strndup(val, len); - res = loopdev_is_used((char *) val, backing_file, - offset, LOOPDEV_FL_OFFSET); + rc = loopdev_is_used((char *) val, bf, offset, LOOPDEV_FL_OFFSET); free(val); } - - if (res) { - DBG(CXT, mnt_debug_h(cxt, "%s already mounted", backing_file)); - return 1; - } } - - return 0; + if (rc) + DBG(CXT, mnt_debug_h(cxt, "%s already mounted", backing_file)); + return rc; } int mnt_context_setup_loopdev(struct libmnt_context *cxt) @@ -282,7 +281,7 @@ int mnt_context_setup_loopdev(struct libmnt_context *cxt) if ((cxt->user_mountflags & MNT_MS_LOOP) && loopcxt_is_autoclear(&lc)) { /* - * autoclear flag accepted by kernel, don't store + * autoclear flag accepted by the kernel, don't store * the "loop=" option to mtab. */ cxt->user_mountflags &= ~MNT_MS_LOOP; @@ -298,7 +297,7 @@ int mnt_context_setup_loopdev(struct libmnt_context *cxt) mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY); /* we have to keep the device open until mount(1), - * otherwise it will auto-cleared by kernel + * otherwise it will be auto-cleared by kernel */ cxt->loopdev_fd = loopcxt_get_fd(&lc); loopcxt_set_fd(&lc, -1, 0); diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index d6691eb6a..89d1f4f29 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -21,6 +21,7 @@ #include "linux_version.h" #include "mountP.h" +#include "strutils.h" /* * Kernel supports only one MS_PROPAGATION flag change by one mount(2) syscall, @@ -101,6 +102,30 @@ static int init_propagation(struct libmnt_context *cxt) return 0; } +#if defined(HAVE_LIBSELINUX) || defined(HAVE_SMACK) +struct libmnt_optname { + const char *name; + size_t namesz; +}; + +#define DEF_OPTNAME(n) { .name = n, .namesz = sizeof(n) - 1 } +#define DEF_OPTNAME_LAST { .name = NULL } + +static int is_option(const char *name, size_t namesz, + const struct libmnt_optname *names) +{ + const struct libmnt_optname *p; + + for (p = names; p && p->name; p++) { + if (p->namesz == namesz + && strncmp(name, p->name, namesz) == 0) + return 1; + } + + return 0; +} +#endif /* HAVE_LIBSELINUX || HAVE_SMACK */ + /* * this has to be called after mnt_context_evaluate_permissions() */ @@ -113,6 +138,25 @@ static int fix_optstr(struct libmnt_context *cxt) struct libmnt_fs *fs; #ifdef HAVE_LIBSELINUX int se_fix = 0, se_rem = 0; + static const struct libmnt_optname selinux_options[] = { + DEF_OPTNAME("context"), + DEF_OPTNAME("fscontext"), + DEF_OPTNAME("defcontext"), + DEF_OPTNAME("rootcontext"), + DEF_OPTNAME("seclabel"), + DEF_OPTNAME_LAST + }; +#endif +#ifdef HAVE_SMACK + int sm_rem = 0; + static const struct libmnt_optname smack_options[] = { + DEF_OPTNAME("smackfsdef"), + DEF_OPTNAME("smackfsfloor"), + DEF_OPTNAME("smackfshat"), + DEF_OPTNAME("smackfsroot"), + DEF_OPTNAME("smackfstransmute"), + DEF_OPTNAME_LAST + }; #endif assert(cxt); assert(cxt->fs); @@ -129,8 +173,8 @@ static int fix_optstr(struct libmnt_context *cxt) /* * The "user" options is our business (so we can modify the option), - * but exception is command line for /sbin/mount.<type> helpers. Let's - * save the original user=<name> to call the helpers with unchanged + * the exception is command line for /sbin/mount.<type> helpers. Let's + * save the original user=<name> to call the helpers with an unchanged * "user" setting. */ if (cxt->user_mountflags & MNT_MS_USER) { @@ -168,7 +212,6 @@ static int fix_optstr(struct libmnt_context *cxt) free(fs->user_optstr); fs->user_optstr = NULL; } - if (cxt->mountflags & MS_PROPAGATION) { rc = init_propagation(cxt); if (rc) @@ -198,13 +241,15 @@ static int fix_optstr(struct libmnt_context *cxt) if (!se_rem) { /* de-duplicate SELinux options */ - mnt_optstr_deduplicate_option(&fs->fs_optstr, "context"); - mnt_optstr_deduplicate_option(&fs->fs_optstr, "fscontext"); - mnt_optstr_deduplicate_option(&fs->fs_optstr, "defcontext"); - mnt_optstr_deduplicate_option(&fs->fs_optstr, "rootcontext"); - mnt_optstr_deduplicate_option(&fs->fs_optstr, "seclabel"); + const struct libmnt_optname *p; + for (p = selinux_options; p && p->name; p++) + mnt_optstr_deduplicate_option(&fs->fs_optstr, p->name); } #endif +#ifdef HAVE_SMACK + if (access("/sys/fs/smackfs", F_OK) != 0) + sm_rem = 1; +#endif while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { if (namesz == 3 && !strncmp(name, "uid", 3)) @@ -212,12 +257,9 @@ static int fix_optstr(struct libmnt_context *cxt) else if (namesz == 3 && !strncmp(name, "gid", 3)) rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next); #ifdef HAVE_LIBSELINUX - else if ((se_rem || se_fix) && - namesz >= 7 && (!strncmp(name, "context", 7) || - !strncmp(name, "fscontext", 9) || - !strncmp(name, "defcontext", 10) || - !strncmp(name, "rootcontext", 11) || - !strncmp(name, "seclabel", 8))) { + else if ((se_rem || se_fix) + && is_option(name, namesz, selinux_options)) { + if (se_rem) { /* remove context= option */ next = name; @@ -231,6 +273,15 @@ static int fix_optstr(struct libmnt_context *cxt) val, valsz, &next); } #endif +#ifdef HAVE_SMACK + else if (sm_rem && is_option(name, namesz, smack_options)) { + + next = name; + rc = mnt_optstr_remove_option_at(&fs->fs_optstr, + name, + val ? val + valsz : name + namesz); + } +#endif if (rc) goto done; } @@ -255,7 +306,7 @@ done: } /* - * Converts already evaluated and fixed options to the form that is compatible + * Converts the already evaluated and fixed options to the form that is compatible * with /sbin/mount.type helpers. */ static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr) @@ -278,13 +329,13 @@ static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr) if (cxt->user_mountflags & MNT_MS_USER) { /* * This is unnecessary for real user-mounts as mount.<type> - * helpers have to always follow fstab rather than mount - * options on command line. + * helpers always have to follow fstab rather than mount + * options on the command line. * - * But if you call mount.<type> as root then the helper follows - * command line. If there is (for example) "user,exec" in fstab + * However, if you call mount.<type> as root, then the helper follows + * the command line. If there is (for example) "user,exec" in fstab, * then we have to manually append the "exec" back to the options - * string, bacause there is nothing like MS_EXEC (we have only + * string, bacause there is nothing like MS_EXEC (we only have * MS_NOEXEC in mount flags and we don't care about the original * mount string in libmount for VFS options). */ @@ -330,7 +381,7 @@ err: /* * this has to be called before fix_optstr() * - * Note that user=<name> maybe be used by some filesystems as filesystem + * Note that user=<name> may be used by some filesystems as a filesystem * specific option (e.g. cifs). Yes, developers of such filesystems have * allocated pretty hot place in hell... */ @@ -361,7 +412,7 @@ static int evaluate_permissions(struct libmnt_context *cxt) /* * user mount */ - if (!(cxt->flags & MNT_FL_TAB_APPLIED)) + if (!mnt_context_tab_applied(cxt)) { DBG(CXT, mnt_debug_h(cxt, "perms: fstab not applied, ignore user mount")); return -EPERM; @@ -433,7 +484,7 @@ static int evaluate_permissions(struct libmnt_context *cxt) /* * mnt_context_helper_setopt() backend * - * This function applies mount.type command line option (for example parsed + * This function applies the mount.type command line option (for example parsed * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and * then 1 is returned. * @@ -519,7 +570,7 @@ static int exec_helper(struct libmnt_context *cxt) /* * TODO: remove the exception for "nfs", -s is documented - * for years should be usable everywhere. + * for years and should be usable everywhere. */ if (mnt_context_is_sloppy(cxt) && type && startswith(type, "nfs")) @@ -651,7 +702,7 @@ static int do_mount(struct libmnt_context *cxt, const char *try_type) return -EINVAL; if (!src) { /* unnecessary, should be already resolved in - * mnt_context_prepare_srcpath(), but for sure... */ + * mnt_context_prepare_srcpath(), but to be sure... */ DBG(CXT, mnt_debug_h(cxt, "WARNING: source is NULL -- using \"none\"!")); src = "none"; } @@ -831,10 +882,10 @@ int mnt_context_prepare_mount(struct libmnt_context *cxt) * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount(). * * Note that this function could be called only once. If you want to mount - * another source or target than you have to call mnt_reset_context(). + * another source or target, then you have to call mnt_reset_context(). * - * If you want to call mount(2) for the same source and target with a different - * mount flags or fstype then call mnt_context_reset_status() and then try + * If you want to call mount(2) for the same source and target with different + * mount flags or fstype, then call mnt_context_reset_status() and then try * again mnt_context_do_mount(). * * WARNING: non-zero return code does not mean that mount(2) syscall or @@ -867,7 +918,7 @@ int mnt_context_do_mount(struct libmnt_context *cxt) type = mnt_fs_get_fstype(cxt->fs); if (type) { if (strchr(type, ',')) - /* this only happens if fstab contains list of filesystems */ + /* this only happens if fstab contains a list of filesystems */ res = do_mount_by_pattern(cxt, type); else res = do_mount(cxt, NULL); @@ -935,7 +986,7 @@ int mnt_context_finalize_mount(struct libmnt_context *cxt) * mnt_context_mount: * @cxt: mount context * - * High-level, mounts filesystem by mount(2) or fork()+exec(/sbin/mount.type). + * High-level, mounts the filesystem by mount(2) or fork()+exec(/sbin/mount.type). * * This is similar to: * @@ -945,10 +996,10 @@ int mnt_context_finalize_mount(struct libmnt_context *cxt) * * See also mnt_context_disable_helpers(). * - * Note that this function could be called only once. If you want to mount with - * different setting than you have to call mnt_reset_context(). It's NOT enough - * to call mnt_context_reset_status() if you want call this function more than - * once, whole context has to be reseted. + * Note that this function should be called only once. If you want to mount with + * different settings, then you have to call mnt_reset_context(). It's NOT enough + * to call mnt_context_reset_status(). If you want to call this function more than + * once, the whole context has to be reset. * * WARNING: non-zero return code does not mean that mount(2) syscall or * mount.type helper wasn't successfully called. @@ -973,11 +1024,6 @@ int mnt_context_mount(struct libmnt_context *cxt) rc = mnt_context_prepare_update(cxt); if (!rc) rc = mnt_context_do_mount(cxt); - - /* TODO: if mtab update is expected then check if the - * target is really mounted read-write to avoid 'ro' in - * mtab and 'rw' in /proc/mounts. - */ if (!rc) rc = mnt_context_update_tabs(cxt); return rc; @@ -989,7 +1035,7 @@ int mnt_context_mount(struct libmnt_context *cxt) * @itr: iterator * @fs: returns the current filesystem * @mntrc: returns the return code from mnt_context_mount() - * @ignored: returns 1 for not matching and 2 for already mounted filesystems + * @ignored: returns 1 for non-matching and 2 for already mounted filesystems * * This function tries to mount the next filesystem from fstab (as returned by * mnt_context_get_fstab()). See also mnt_context_set_fstab(). @@ -1057,11 +1103,11 @@ int mnt_context_next_mount(struct libmnt_context *cxt, /* ignore noauto filesystems */ (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) || - /* ignore filesystems not match with options patterns */ + /* ignore filesystems which don't match options patterns */ (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs, cxt->fstype_pattern)) || - /* ignore filesystems not match with type patterns */ + /* ignore filesystems which don't match type patterns */ (cxt->optstr_pattern && !mnt_fs_match_options(*fs, cxt->optstr_pattern))) { if (ignored) diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index 4a8659c9c..dc16852c0 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.c @@ -23,7 +23,7 @@ * umount2 flags */ #ifndef MNT_FORCE -# define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */ +# define MNT_FORCE 0x00000001 /* Attempt to forcibly umount */ #endif #ifndef MNT_DETACH @@ -39,8 +39,8 @@ #endif /* - * Called by mtab parser to filter out entries, nonzero means that - * entry has to be filter out. + * Called by mtab parser to filter out entries, non-zero means that + * an entry has to be filtered out. */ static int mtab_filter(struct libmnt_fs *fs, void *data) { @@ -71,19 +71,25 @@ int mnt_context_find_umount_fs(struct libmnt_context *cxt, struct libmnt_cache *cache = NULL; char *cn_tgt = NULL, *loopdev = NULL; + if (pfs) + *pfs = NULL; + if (!cxt || !tgt || !pfs) return -EINVAL; DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS for '%s'", tgt)); + if (!*tgt) + return 1; /* empty string is not an error */ + /* - * The mtab file maybe huge and on systems with utab we have to merge + * The mtab file may be huge and on systems with utab we have to merge * userspace mount options into /proc/self/mountinfo. This all is * expensive. The mtab filter allows to filter out entries, then * mtab and utab are very tiny files. * * *but*... the filter uses mnt_fs_streq_{target,srcpath} functions - * where LABEL, UUID or symlinks are to canonicalized. It means that + * where LABEL, UUID or symlinks are canonicalized. It means that * it's usable only for canonicalized stuff (e.g. kernel mountinfo). */ if (!cxt->mtab_writable && *tgt == '/' && @@ -92,7 +98,7 @@ int mnt_context_find_umount_fs(struct libmnt_context *cxt, struct stat st; if (stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)) { - /* we'll canonicalized /proc/self/mountinfo */ + /* we'll canonicalize /proc/self/mountinfo */ cache = mnt_context_get_cache(cxt); cn_tgt = mnt_resolve_path(tgt, cache); if (cn_tgt) @@ -155,7 +161,10 @@ try_loopdev: struct stat st; if (stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) { - int count = loopdev_count_by_backing_file(tgt, &loopdev); + int count; + const char *bf = cache ? mnt_resolve_path(tgt, cache) : tgt; + + count = loopdev_count_by_backing_file(bf, &loopdev); if (count == 1) { DBG(CXT, mnt_debug_h(cxt, "umount: %s --> %s (retry)", tgt, loopdev)); @@ -169,15 +178,21 @@ try_loopdev: } } - *pfs = fs; + if (pfs) + *pfs = fs; free(loopdev); + DBG(CXT, mnt_debug_h(cxt, "umount fs: %s", fs ? mnt_fs_get_target(fs) : + "<not found>")); return fs ? 0 : 1; err: free(loopdev); return rc; } +/* this is umount replacement to mnt_context_apply_fstab(), use + * mnt_context_tab_applied() to check result. + */ static int lookup_umount_fs(struct libmnt_context *cxt) { const char *tgt; @@ -197,8 +212,8 @@ static int lookup_umount_fs(struct libmnt_context *cxt) if (rc < 0) return rc; if (rc == 1 || !fs) { - DBG(CXT, mnt_debug_h(cxt, "umount: cannot find %s in mtab", tgt)); - return 0; + DBG(CXT, mnt_debug_h(cxt, "umount: cannot find '%s' in mtab", tgt)); + return 0; /* this is correct! */ } if (fs != cxt->fs) { @@ -237,7 +252,7 @@ static int is_associated_fs(const char *devname, struct libmnt_fs *fs) if (!src) return 0; - /* check for offset option in @fs */ + /* check for the offset option in @fs */ optstr = (char *) mnt_fs_get_user_options(fs); if (optstr && @@ -300,7 +315,7 @@ static int evaluate_permissions(struct libmnt_context *cxt) DBG(CXT, mnt_debug_h(cxt, "umount: evaluating permissions")); - if (!(cxt->flags & MNT_FL_TAB_APPLIED)) { + if (!mnt_context_tab_applied(cxt)) { DBG(CXT, mnt_debug_h(cxt, "cannot find %s in mtab and you are not root", mnt_fs_get_target(cxt->fs))); @@ -317,7 +332,7 @@ static int evaluate_permissions(struct libmnt_context *cxt) } /* - * User mounts has to be in /etc/fstab + * User mounts have to be in /etc/fstab */ rc = mnt_context_get_fstab(cxt, &fstab); if (rc) @@ -336,18 +351,20 @@ static int evaluate_permissions(struct libmnt_context *cxt) * /dev/sda1 /mnt/zip auto user,noauto 0 0 * /dev/sda4 /mnt/zip auto user,noauto 0 0 * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail. - * So, we must not look for file, but for the pair (dev,file) in fstab. + * So, we must not look for the file, but for the pair (dev,file) in fstab. */ fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD); if (!fs) { /* * It's possible that there is /path/file.img in fstab and - * /dev/loop0 in mtab -- then we have to check releation + * /dev/loop0 in mtab -- then we have to check the relation * between loopdev and the file. */ fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD); if (fs) { - const char *dev = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */ + struct libmnt_cache *cache = mnt_context_get_cache(cxt); + const char *sp = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */ + const char *dev = sp && cache ? mnt_resolve_path(sp, cache) : sp; if (!dev || !is_associated_fs(dev, fs)) fs = NULL; @@ -384,7 +401,7 @@ static int evaluate_permissions(struct libmnt_context *cxt) return 0; } /* - * Check user=<username> setting from mtab if there is user, owner or + * Check user=<username> setting from mtab if there is a user, owner or * group option in /etc/fstab */ if (u_flags & (MNT_MS_USER | MNT_MS_OWNER | MNT_MS_GROUP)) { @@ -409,6 +426,8 @@ static int evaluate_permissions(struct libmnt_context *cxt) if (optstr && !mnt_optstr_get_option(optstr, "user", &mtab_user, &sz) && sz) ok = !strncmp(curr_user, mtab_user, sz); + + free(curr_user); } if (ok) { @@ -539,7 +558,7 @@ int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg) return rc; } -/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */ +/* Check whether the kernel supports the UMOUNT_NOFOLLOW flag */ static int umount_nofollow_support(void) { int res = umount2("", UMOUNT_UNUSED); @@ -860,7 +879,7 @@ int mnt_context_umount(struct libmnt_context *cxt) * mnt_context_set_options_pattern() to simulate umount -a -O pattern * mnt_context_set_fstype_pattern() to simulate umount -a -t pattern * - * If the filesystem is not mounted or does not match defined criteria, + * If the filesystem is not mounted or does not match the defined criteria, * then the function mnt_context_next_umount() returns zero, but the @ignored is * non-zero. Note that the root filesystem is always ignored. * @@ -909,11 +928,11 @@ int mnt_context_next_umount(struct libmnt_context *cxt, DBG(CXT, mnt_debug_h(cxt, "next-umount: trying %s", tgt)); - /* ignore filesystems not match with options patterns */ + /* ignore filesystems which don't match options patterns */ if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs, cxt->fstype_pattern)) || - /* ignore filesystems not match with type patterns */ + /* ignore filesystems which don't match type patterns */ (cxt->optstr_pattern && !mnt_fs_match_options(*fs, cxt->optstr_pattern))) { if (ignored) diff --git a/libmount/src/fs.c b/libmount/src/fs.c index c95cdc7e7..3ab614503 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -21,6 +21,9 @@ /** * mnt_new_fs: * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the filesystem. + * * Returns: newly allocated struct libmnt_fs. */ struct libmnt_fs *mnt_new_fs(void) @@ -29,8 +32,9 @@ struct libmnt_fs *mnt_new_fs(void) if (!fs) return NULL; - /*DBG(FS, mnt_debug_h(fs, "alloc"));*/ + fs->refcount = 1; INIT_LIST_HEAD(&fs->ents); + /*DBG(FS, mnt_debug_h(fs, "alloc"));*/ return fs; } @@ -38,7 +42,10 @@ struct libmnt_fs *mnt_new_fs(void) * mnt_free_fs: * @fs: fs pointer * - * Deallocates the fs. + * Deallocates the fs. This function does not care about reference count. Don't + * use this function directly -- it's better to use use mnt_unref_fs(). + * + * The reference counting is supported since util-linux v2.24. */ void mnt_free_fs(struct libmnt_fs *fs) { @@ -47,6 +54,7 @@ void mnt_free_fs(struct libmnt_fs *fs) list_del(&fs->ents); /*DBG(FS, mnt_debug_h(fs, "free"));*/ + WARN_REFCOUNT(FS, fs, fs->refcount); free(fs->source); free(fs->bindsrc); @@ -62,6 +70,7 @@ void mnt_free_fs(struct libmnt_fs *fs) free(fs->user_optstr); free(fs->attrs); free(fs->opt_fields); + free(fs->comment); free(fs); } @@ -74,8 +83,46 @@ void mnt_free_fs(struct libmnt_fs *fs) */ void mnt_reset_fs(struct libmnt_fs *fs) { - if (fs) - memset(fs, 0, sizeof(*fs)); + int ref; + + if (!fs) + return; + + ref = fs->refcount; + memset(fs, 0, sizeof(*fs)); + INIT_LIST_HEAD(&fs->ents); + fs->refcount = ref; +} + +/** + * mnt_ref_fs: + * @fs: fs pointer + * + * Increments reference counter. + */ +void mnt_ref_fs(struct libmnt_fs *fs) +{ + if (fs) { + fs->refcount++; + /*DBG(FS, mnt_debug_h(fs, "ref=%d", fs->refcount));*/ + } +} + +/** + * mnt_unref_fs: + * @fs: fs pointer + * + * De-increments reference counter, on zero the @fs is automatically + * deallocated by mnt_free_fs(). + */ +void mnt_unref_fs(struct libmnt_fs *fs) +{ + if (fs) { + fs->refcount--; + /*DBG(FS, mnt_debug_h(fs, "unref=%d", fs->refcount));*/ + if (fs->refcount <= 0) + mnt_free_fs(fs); + } } static inline int update_str(char **dest, const char *src) @@ -106,7 +153,7 @@ static inline int cpy_str_at_offset(void *new, const void *old, size_t offset) char **n = (char **) (new + offset); if (*n) - return 0; /* already set, not overwrite */ + return 0; /* already set, don't overwrite */ return update_str(n, *o); } @@ -117,7 +164,7 @@ static inline int cpy_str_at_offset(void *new, const void *old, size_t offset) * @src: source FS * * If @dest is NULL, then a new FS is allocated, if any @dest field is already - * set then the field is NOT overwrited. + * set, then the field is NOT overwritten. * * This function does not copy userdata (se mnt_fs_set_userdata()). A new copy is * not linked with any existing mnt_tab. @@ -250,7 +297,12 @@ err: void *mnt_fs_get_userdata(struct libmnt_fs *fs) { assert(fs); - return fs ? fs->userdata : NULL; + + if (!fs) + return NULL; + + /*DBG(FS, mnt_debug_h(fs, "get userdata [%p]", fs->userdata));*/ + return fs->userdata; } /** @@ -267,6 +319,8 @@ int mnt_fs_set_userdata(struct libmnt_fs *fs, void *data) assert(fs); if (!fs) return -EINVAL; + + /*DBG(FS, mnt_debug_h(fs, "set userdata [%p]", fs->userdata));*/ fs->userdata = data; return 0; } @@ -310,7 +364,7 @@ const char *mnt_fs_get_source(struct libmnt_fs *fs) } /* - * Used by parser ONLY (@source has to be freed on error) + * Used by the parser ONLY (@source has to be freed on error) */ int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source) { @@ -318,9 +372,12 @@ int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source) assert(fs); - if (source && *source != '/' && strchr(source, '=')) { - if (blkid_parse_tag_string(source, &t, &v) != 0) - return -1; + if (source && blkid_parse_tag_string(source, &t, &v) == 0 && + !mnt_valid_tagname(t)) { + /* parsable but unknown tag -- ignore */ + free(t); + free(v); + t = v = NULL; } if (fs->source != source) @@ -370,7 +427,7 @@ int mnt_fs_set_source(struct libmnt_fs *fs, const char *source) * @fs: fs * @path: source path * - * Compares @fs source path with @path. The tailing slash is ignored. + * Compares @fs source path with @path. The trailing slash is ignored. * See also mnt_fs_match_source(). * * Returns: 1 if @fs source path equal to @path, otherwise 0. @@ -399,7 +456,7 @@ int mnt_fs_streq_srcpath(struct libmnt_fs *fs, const char *path) * @fs: fs * @path: mount point * - * Compares @fs target path with @path. The tailing slash is ignored. + * Compares @fs target path with @path. The trailing slash is ignored. * See also mnt_fs_match_target(). * * Returns: 1 if @fs target path equal to @path, otherwise 0. @@ -418,8 +475,8 @@ int mnt_fs_streq_target(struct libmnt_fs *fs, const char *path) * * "TAG" is NAME=VALUE (e.g. LABEL=foo) * - * The TAG is the first column in the fstab file. The TAG or "srcpath" has to - * be always set for all entries. + * The TAG is the first column in the fstab file. The TAG or "srcpath" always has + * to be set for all entries. * * See also mnt_fs_get_source(). * @@ -441,7 +498,7 @@ int mnt_fs_streq_target(struct libmnt_fs *fs, const char *path) * </programlisting> * </informalexample> * - * Returns: 0 on success or negative number in case that a TAG is not defined. + * Returns: 0 on success or negative number in case a TAG is not defined. */ int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, const char **value) { @@ -502,10 +559,10 @@ static int mnt_fs_get_flags(struct libmnt_fs *fs) /** * mnt_fs_get_propagation: * @fs: mountinfo entry - * @flags: returns propagation MS_* flags as present in mountinfo file + * @flags: returns propagation MS_* flags as present in the mountinfo file * - * Note that this function set @flags to zero if not found any propagation flag - * in mountinfo file. The kernel default is MS_PRIVATE, this flag is not stored + * Note that this function sets @flags to zero if no propagation flags are found + * in the mountinfo file. The kernel default is MS_PRIVATE, this flag is not stored * in the mountinfo file. * * Returns: 0 on success or negative number in case of error. @@ -594,7 +651,7 @@ const char *mnt_fs_get_fstype(struct libmnt_fs *fs) return fs ? fs->fstype : NULL; } -/* Used by struct libmnt_file parser only */ +/* Used by the struct libmnt_file parser only */ int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype) { assert(fs); @@ -670,7 +727,7 @@ static char *merge_optstr(const char *vfs, const char *fs) if (!strcmp(vfs, fs)) return strdup(vfs); /* e.g. "aaa" and "aaa" */ - /* leave space for leading "r[ow],", "," and trailing zero */ + /* leave space for the leading "r[ow],", "," and the trailing zero */ sz = strlen(vfs) + strlen(fs) + 5; res = malloc(sz); if (!res) @@ -701,7 +758,7 @@ static char *merge_optstr(const char *vfs, const char *fs) * mnt_fs_strdup_options: * @fs: fstab/mtab/mountinfo entry pointer * - * Merges all mount options (VFS, FS and userspace) to the one options string + * Merges all mount options (VFS, FS and userspace) to one options string * and returns the result. This function does not modigy @fs. * * Returns: pointer to string (can be freed by free(3)) or NULL in case of error. @@ -757,10 +814,10 @@ const char *mnt_fs_get_optional_fields(struct libmnt_fs *fs) * @fs: fstab/mtab/mountinfo entry pointer * @optstr: options string * - * Splits @optstr to VFS, FS and userspace mount options and update relevat + * Splits @optstr to VFS, FS and userspace mount options and updates relevant * parts of @fs. * - * Returns: 0 on success, or negative number icase of error. + * Returns: 0 on success, or negative number in case of error. */ int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr) { @@ -774,8 +831,12 @@ int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr) if (rc) return rc; n = strdup(optstr); - if (!n) + if (!n) { + free(u); + free(v); + free(f); return -ENOMEM; + } } free(fs->fs_optstr); @@ -799,7 +860,7 @@ int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr) * Parses (splits) @optstr and appends results to VFS, FS and userspace lists * of options. * - * If @optstr is NULL then @fs is not modified and 0 is returned. + * If @optstr is NULL, then @fs is not modified and 0 is returned. * * Returns: 0 on success or negative number in case of error. */ @@ -815,6 +876,9 @@ int mnt_fs_append_options(struct libmnt_fs *fs, const char *optstr) return 0; rc = mnt_split_optstr((char *) optstr, &u, &v, &f, 0, 0); + if (rc) + return rc; + if (!rc && v) rc = mnt_optstr_append_option(&fs->vfs_optstr, v, NULL); if (!rc && f) @@ -836,10 +900,10 @@ int mnt_fs_append_options(struct libmnt_fs *fs, const char *optstr) * @fs: fstab/mtab/mountinfo entry * @optstr: mount options * - * Parses (splits) @optstr and prepands results to VFS, FS and userspace lists + * Parses (splits) @optstr and prepends the results to VFS, FS and userspace lists * of options. * - * If @optstr is NULL then @fs is not modified and 0 is returned. + * If @optstr is NULL, then @fs is not modified and 0 is returned. * * Returns: 0 on success or negative number in case of error. */ @@ -855,6 +919,9 @@ int mnt_fs_prepend_options(struct libmnt_fs *fs, const char *optstr) return 0; rc = mnt_split_optstr((char *) optstr, &u, &v, &f, 0, 0); + if (rc) + return rc; + if (!rc && v) rc = mnt_optstr_prepend_option(&fs->vfs_optstr, v, NULL); if (!rc && f) @@ -925,10 +992,10 @@ const char *mnt_fs_get_attributes(struct libmnt_fs *fs) * @optstr: options string * * Sets mount attributes. The attributes are mount(2) and mount(8) independent - * options, these options are not send to kernel and are not interpreted by + * options, these options are not sent to the kernel and are not interpreted by * libmount. The attributes are stored in /run/mount/utab only. * - * The atrtributes are managed by libmount in userspace only. It's possible + * The attributes are managed by libmount in userspace only. It's possible * that information stored in userspace will not be available for libmount * after CLONE_FS unshare. Be careful, and don't use attributes if possible. * @@ -1221,7 +1288,7 @@ dev_t mnt_fs_get_devno(struct libmnt_fs *fs) * mnt_fs_get_tid: * @fs: /proc/tid/mountinfo entry * - * Returns: TID (task ID) for filesystems read from mountinfo file + * Returns: TID (task ID) for filesystems read from the mountinfo file */ pid_t mnt_fs_get_tid(struct libmnt_fs *fs) { @@ -1233,10 +1300,10 @@ pid_t mnt_fs_get_tid(struct libmnt_fs *fs) * mnt_fs_get_option: * @fs: fstab/mtab/mountinfo entry pointer * @name: option name - * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL + * @value: returns pointer to the beginning of the value (e.g. name=VALUE) or NULL * @valsz: returns size of options value or 0 * - * Returns: 0 on success, 1 when not found the @name or negative number in case of error. + * Returns: 0 on success, 1 when @name not found or negative number in case of error. */ int mnt_fs_get_option(struct libmnt_fs *fs, const char *name, char **value, size_t *valsz) @@ -1259,10 +1326,10 @@ int mnt_fs_get_option(struct libmnt_fs *fs, const char *name, * mnt_fs_get_attribute: * @fs: fstab/mtab/mountinfo entry pointer * @name: option name - * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL + * @value: returns pointer to the beginning of the value (e.g. name=VALUE) or NULL * @valsz: returns size of options value or 0 * - * Returns: 0 on success, 1 when not found the @name or negative number in case of error. + * Returns: 0 on success, 1 when @name not found or negative number in case of error. */ int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name, char **value, size_t *valsz) @@ -1278,6 +1345,67 @@ int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name, } /** + * mnt_fs_get_comment: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: 0 on success, 1 when not found the @name or negative number in case of error. + */ +const char *mnt_fs_get_comment(struct libmnt_fs *fs) +{ + assert(fs); + if (!fs) + return NULL; + return fs->comment; +} + +/** + * mnt_fs_set_comment: + * @fs: fstab entry pointer + * @comm: comment string + * + * Note that the comment has to be terminated by '\n' (new line), otherwise + * the whole filesystem entry will be written as a comment to the tabfile (e.g. + * fstab). + * + * Returns: 0 on success or <0 in case of error. + */ +int mnt_fs_set_comment(struct libmnt_fs *fs, const char *comm) +{ + char *p = NULL; + + assert(fs); + if (!fs) + return -EINVAL; + if (comm) { + p = strdup(comm); + if (!p) + return -ENOMEM; + } + + free(fs->comment); + fs->comment = p; + return 0; +} + +/** + * mnt_fs_append_comment: + * @fs: fstab entry pointer + * @comm: comment string + * + * See also mnt_fs_set_comment(). + * + * Returns: 0 on success or <0 in case of error. + */ +int mnt_fs_append_comment(struct libmnt_fs *fs, const char *comm) +{ + assert(fs); + if (!fs) + return -EINVAL; + + return append_string(&fs->comment, comm); +} + +/** * mnt_fs_match_target: * @fs: filesystem * @target: mountpoint path @@ -1291,7 +1419,7 @@ int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name, * * The 2nd and 3rd attempts are not performed when @cache is NULL. * - * Returns: 1 if @fs target is equal to @target else 0. + * Returns: 1 if @fs target is equal to @target, else 0. */ int mnt_fs_match_target(struct libmnt_fs *fs, const char *target, struct libmnt_cache *cache) @@ -1326,7 +1454,7 @@ int mnt_fs_match_target(struct libmnt_fs *fs, const char *target, * @source: tag or path (device or so) or NULL * @cache: tags/paths cache or NULL * - * Possible are four attempts: + * Four attempts are possible: * 1) compare @source with @fs->source * 2) compare realpath(@source) with @fs->source * 3) compare realpath(@source) with realpath(@fs->source) @@ -1335,7 +1463,7 @@ int mnt_fs_match_target(struct libmnt_fs *fs, const char *target, * The 2nd, 3rd and 4th attempts are not performed when @cache is NULL. The * 2nd and 3rd attempts are not performed if @fs->source is tag. * - * Returns: 1 if @fs source is equal to @source else 0. + * Returns: 1 if @fs source is equal to @source, else 0. */ int mnt_fs_match_source(struct libmnt_fs *fs, const char *source, struct libmnt_cache *cache) @@ -1379,14 +1507,14 @@ int mnt_fs_match_source(struct libmnt_fs *fs, const char *source, return 1; } if (src || mnt_fs_get_tag(fs, &t, &v)) - /* src path does not match and tag is not defined */ + /* src path does not match and the tag is not defined */ return 0; /* read @source's tags to the cache */ if (mnt_cache_read_tags(cache, cn) < 0) { if (errno == EACCES) { /* we don't have permissions to read TAGs from - * @source, but can translate @fs tag to devname. + * @source, but can translate the @fs tag to devname. * * (because libblkid uses udev symlinks and this is * accessible for non-root uses) @@ -1398,7 +1526,7 @@ int mnt_fs_match_source(struct libmnt_fs *fs, const char *source, return 0; } - /* 4) has the @source a tag that matches with tag from @fs ? */ + /* 4) has the @source a tag that matches with the tag from @fs ? */ if (mnt_cache_device_has_tag(cache, cn, t, v)) return 1; @@ -1412,7 +1540,7 @@ int mnt_fs_match_source(struct libmnt_fs *fs, const char *source, * * For more details see mnt_match_fstype(). * - * Returns: 1 if @fs type is matching to @types else 0. The function returns + * Returns: 1 if @fs type is matching to @types, else 0. The function returns * 0 when types is NULL. */ int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types) @@ -1428,7 +1556,7 @@ int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types) * * For more details see mnt_match_options(). * - * Returns: 1 if @fs type is matching to @options else 0. The function returns + * Returns: 1 if @fs type is matching to @options, else 0. The function returns * 0 when types is NULL. */ int mnt_fs_match_options(struct libmnt_fs *fs, const char *options) @@ -1493,6 +1621,8 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file) minor(mnt_fs_get_devno(fs))); if (mnt_fs_get_tid(fs)) fprintf(file, "tid: %d\n", mnt_fs_get_tid(fs)); + if (mnt_fs_get_comment(fs)) + fprintf(file, "comment: '%s'\n", mnt_fs_get_comment(fs)); return 0; } @@ -1501,7 +1631,7 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file) * mnt_free_mntent: * @mnt: mount entry * - * Deallocates "mntent.h" mount entry. + * Deallocates the "mntent.h" mount entry. */ void mnt_free_mntent(struct mntent *mnt) { @@ -1519,11 +1649,11 @@ void mnt_free_mntent(struct mntent *mnt) * @fs: filesystem * @mnt: mount description (as described in mntent.h) * - * Copies information from @fs to struct mntent @mnt. If @mnt is already set + * Copies the information from @fs to struct mntent @mnt. If @mnt is already set, * then the struct mntent items are reallocated and updated. See also * mnt_free_mntent(). * - * Returns: 0 on success and negative number in case of error. + * Returns: 0 on success and a negative number in case of error. */ int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt) { diff --git a/libmount/src/init.c b/libmount/src/init.c index 4e5f489c4..a14637d75 100644 --- a/libmount/src/init.c +++ b/libmount/src/init.c @@ -8,7 +8,7 @@ /** * SECTION: init * @title: Library initialization - * @short_description: initialize debuging + * @short_description: initialize debugging */ #include <stdarg.h> @@ -19,13 +19,13 @@ int libmount_debug_mask; /** * mnt_init_debug: - * @mask: debug mask (0xffff to enable full debuging) + * @mask: debug mask (0xffff to enable full debugging) * - * If the @mask is not specified then this function reads - * LIBMOUNT_DEBUG environment variable to get the mask. + * If the @mask is not specified, then this function reads + * the LIBMOUNT_DEBUG environment variable to get the mask. * - * Already initialized debugging stuff cannot be changed. It does not - * have effect to call this function twice. + * Already initialized debugging stuff cannot be changed. Calling + * this function twice has no effect. */ void mnt_init_debug(int mask) { @@ -40,7 +40,7 @@ void mnt_init_debug(int mask) libmount_debug_mask |= MNT_DEBUG_INIT; - if (libmount_debug_mask && libmount_debug_mask != MNT_DEBUG_INIT) { + if (libmount_debug_mask != MNT_DEBUG_INIT) { const char *ver = NULL; const char **features = NULL, **p; diff --git a/libmount/src/iter.c b/libmount/src/iter.c index d7b8adbef..016f88e35 100644 --- a/libmount/src/iter.c +++ b/libmount/src/iter.c @@ -10,8 +10,8 @@ * @title: Iterator * @short_description: unified iterator * - * The iterator keeps direction and last position for access to the internal - * library tables/lists. + * The iterator keeps the direction and the last position + * for access to the internal library tables/lists. */ #include <stdio.h> #include <string.h> @@ -38,7 +38,7 @@ struct libmnt_iter *mnt_new_iter(int direction) * mnt_free_iter: * @itr: iterator pointer * - * Deallocates iterator. + * Deallocates the iterator. */ void mnt_free_iter(struct libmnt_iter *itr) { @@ -48,9 +48,9 @@ void mnt_free_iter(struct libmnt_iter *itr) /** * mnt_reset_iter: * @itr: iterator pointer - * @direction: MNT_INTER_{FOR,BACK}WARD or -1 to keep the derection unchanged + * @direction: MNT_INTER_{FOR,BACK}WARD or -1 to keep the direction unchanged * - * Resets iterator. + * Resets the iterator. */ void mnt_reset_iter(struct libmnt_iter *itr, int direction) { diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index db479e186..8c8e739c9 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -41,7 +41,7 @@ struct libmnt_cache; /** * libmnt_lock: * - * Stores information about locked file (e.g. /etc/mtab) + * Stores information about the locked file (e.g. /etc/mtab) */ struct libmnt_lock; @@ -197,6 +197,7 @@ extern char *mnt_mangle(const char *str) extern char *mnt_unmangle(const char *str) __ul_attribute__((warn_unused_result)); +extern int mnt_tag_is_valid(const char *tag); extern int mnt_fstype_is_netfs(const char *type); extern int mnt_fstype_is_pseudofs(const char *type); @@ -216,6 +217,9 @@ extern struct libmnt_cache *mnt_new_cache(void) __ul_attribute__((warn_unused_result)); extern void mnt_free_cache(struct libmnt_cache *cache); +extern void mnt_ref_cache(struct libmnt_cache *cache); +extern void mnt_unref_cache(struct libmnt_cache *cache); + extern int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname); extern int mnt_cache_device_has_tag(struct libmnt_cache *cache, @@ -302,6 +306,8 @@ extern int mnt_lock_block_signals(struct libmnt_lock *ml, int enable); extern struct libmnt_fs *mnt_new_fs(void) __ul_attribute__((warn_unused_result)); extern void mnt_free_fs(struct libmnt_fs *fs); +extern void mnt_ref_fs(struct libmnt_fs *fs); +extern void mnt_unref_fs(struct libmnt_fs *fs); extern void mnt_reset_fs(struct libmnt_fs *fs); extern struct libmnt_fs *mnt_copy_fs(struct libmnt_fs *dest, @@ -369,6 +375,10 @@ extern off_t mnt_fs_get_size(struct libmnt_fs *fs); extern off_t mnt_fs_get_usedsize(struct libmnt_fs *fs); extern int mnt_fs_get_priority(struct libmnt_fs *fs); +extern const char *mnt_fs_get_comment(struct libmnt_fs *fs); +extern int mnt_fs_set_comment(struct libmnt_fs *fs, const char *comm); +extern int mnt_fs_append_comment(struct libmnt_fs *fs, const char *comm); + extern int mnt_fs_match_target(struct libmnt_fs *fs, const char *target, struct libmnt_cache *cache); extern int mnt_fs_match_source(struct libmnt_fs *fs, const char *source, @@ -406,12 +416,31 @@ extern struct libmnt_table *mnt_new_table(void) __ul_attribute__((warn_unused_result)); extern void mnt_free_table(struct libmnt_table *tb); +extern void mnt_ref_table(struct libmnt_table *tb); +extern void mnt_unref_table(struct libmnt_table *tb); + extern int mnt_reset_table(struct libmnt_table *tb); extern int mnt_table_get_nents(struct libmnt_table *tb); +extern int mnt_table_is_empty(struct libmnt_table *tb); + +extern int mnt_table_set_userdata(struct libmnt_table *tb, void *data); +extern void *mnt_table_get_userdata(struct libmnt_table *tb); + +extern void mnt_table_enable_comments(struct libmnt_table *tb, int enable); +extern int mnt_table_with_comments(struct libmnt_table *tb); +extern const char *mnt_table_get_intro_comment(struct libmnt_table *tb); +extern int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm); +extern int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm); +extern int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm); +extern const char *mnt_table_get_trailing_comment(struct libmnt_table *tb); +extern int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm); + extern int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc); extern struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb); extern int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs); extern int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs); +extern int mnt_table_first_fs(struct libmnt_table *tb, struct libmnt_fs **fs); +extern int mnt_table_last_fs(struct libmnt_table *tb, struct libmnt_fs **fs); extern int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs); extern int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, @@ -449,6 +478,9 @@ extern struct libmnt_update *mnt_new_update(void) __ul_attribute__((warn_unused_result)); extern void mnt_free_update(struct libmnt_update *upd); +extern int mnt_table_replace_file(struct libmnt_table *tb, const char *filename); +extern int mnt_table_write_file(struct libmnt_table *tb, FILE *file); + extern int mnt_update_is_ready(struct libmnt_update *upd); extern int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags, const char *target, struct libmnt_fs *fs); @@ -578,6 +610,10 @@ extern const char *mnt_context_get_source(struct libmnt_context *cxt); extern const char *mnt_context_get_target(struct libmnt_context *cxt); extern const char *mnt_context_get_fstype(struct libmnt_context *cxt); +extern void *mnt_context_get_mtab_userdata(struct libmnt_context *cxt); +extern void *mnt_context_get_fstab_userdata(struct libmnt_context *cxt); +extern void *mnt_context_get_fs_userdata(struct libmnt_context *cxt); + extern int mnt_context_set_options(struct libmnt_context *cxt, const char *optstr); extern int mnt_context_append_options(struct libmnt_context *cxt, const char *optstr); @@ -715,10 +751,10 @@ extern int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status #define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */ #endif #ifndef MS_BIND -#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */ +#define MS_BIND 0x1000 /* 4096: Mount existing tree elsewhere as well */ #endif #ifndef MS_MOVE -#define MS_MOVE 0x2000 /* 8192: Atomically move tree */ +#define MS_MOVE 0x2000 /* 8192: Atomically move the tree */ #endif #ifndef MS_REC #define MS_REC 0x4000 /* 16384: Recursive loopback */ diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index 2ea483989..d1180d35b 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -256,3 +256,38 @@ global: mnt_context_find_umount_fs; mnt_table_find_mountpoint; } MOUNT_2.22; + +MOUNT_2.24 { +global: + mnt_context_get_fstab_userdata; + mnt_context_get_fs_userdata; + mnt_context_get_mtab_userdata; + mnt_fs_append_comment; + mnt_fs_get_comment; + mnt_fs_set_comment; + mnt_ref_cache; + mnt_ref_fs; + mnt_ref_table; + mnt_table_append_intro_comment; + mnt_table_append_trailing_comment; + mnt_table_enable_comments; + mnt_table_first_fs; + mnt_table_get_intro_comment; + mnt_table_get_trailing_comment; + mnt_table_get_userdata; + mnt_table_is_empty; + mnt_table_last_fs; + mnt_table_replace_file; + mnt_table_set_intro_comment; + mnt_table_set_trailing_comment; + mnt_table_set_userdata; + mnt_table_with_comments; + mnt_table_write_file; + mnt_unref_cache; + mnt_unref_fs; + mnt_unref_table; +} MOUNT_2.23; + +MOUNT_2.25 { + mnt_tag_is_valid; +} MOUNT_2.24; diff --git a/libmount/src/lock.c b/libmount/src/lock.c index e73edf54b..d9b6f5939 100644 --- a/libmount/src/lock.c +++ b/libmount/src/lock.c @@ -10,9 +10,9 @@ * @title: Locking * @short_description: locking methods for /etc/mtab or another libmount files * - * The mtab lock is backwardly compatible with the standard linux /etc/mtab + * The mtab lock is backwards compatible with the standard linux /etc/mtab * locking. Note, it's necessary to use the same locking schema in all - * application that access the file. + * applications that access the file. */ #include <sys/time.h> #include <time.h> @@ -21,6 +21,8 @@ #include <limits.h> #include <sys/file.h> +#include "strutils.h" +#include "closestream.h" #include "pathnames.h" #include "mountP.h" @@ -246,7 +248,7 @@ static void mnt_lockalrm_handler(int sig __attribute__((__unused__))) /* * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt - * fcntl() to avoid never ending waiting. + * fcntl() to avoid neverending waiting. * * Returns: 0 on success, 1 on timeout, -errno on error. */ @@ -292,7 +294,7 @@ static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t m * soon as the lock file is deleted by the first mount, and immediately * afterwards a third mount comes, creates a new /etc/mtab~, applies * flock to that, and also proceeds, so that the second and third mount - * now both are scribbling in /etc/mtab. + * are now both scribbling in /etc/mtab. * * The new code uses a link() instead of a creat(), where we proceed * only if it was us that created the lock, and hence we always have @@ -307,13 +309,13 @@ static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t m * The original mount locking code has used sleep(1) between attempts and * maximal number of attempts has been 5. * - * There was very small number of attempts and extremely long waiting (1s) + * There was a very small number of attempts and extremely long waiting (1s) * that is useless on machines with large number of mount processes. * - * Now we wait few thousand microseconds between attempts and we have a global - * time limit (30s) rather than limit for number of attempts. The advantage + * Now we wait for a few thousand microseconds between attempts and we have a global + * time limit (30s) rather than a limit for the number of attempts. The advantage * is that this method also counts time which we spend in fcntl(F_SETLKW) and - * number of attempts is not restricted. + * the number of attempts is not restricted. * -- kzak@redhat.com [Mar-2007] * * @@ -326,7 +328,7 @@ static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t m * -- kzak@redhat.com [May-2009] */ -/* maximum seconds between first and last attempt */ +/* maximum seconds between the first and the last attempt */ #define MOUNTLOCK_MAXTIME 30 /* sleep time (in microseconds, max=999999) between attempts */ @@ -339,9 +341,9 @@ static void unlock_mtab(struct libmnt_lock *ml) if (!ml->locked && ml->lockfile && ml->linkfile) { - /* We have (probably) all files, but we don't own the lock, + /* We (probably) have all the files, but we don't own the lock, * Really? Check it! Maybe ml->locked wasn't set properly - * because code was interrupted by signal. Paranoia? Yes. + * because the code was interrupted by a signal. Paranoia? Yes. * * We own the lock when linkfile == lockfile. */ @@ -491,10 +493,10 @@ failed: * mnt_lock_file * @ml: pointer to struct libmnt_lock instance * - * Creates lock file (e.g. /etc/mtab~). Note that this function may + * Creates a lock file (e.g. /etc/mtab~). Note that this function may * use alarm(). * - * Your application has to always call mnt_unlock_file() before exit. + * Your application always has to call mnt_unlock_file() before exit. * * Traditional mtab locking scheme: * @@ -521,7 +523,7 @@ int mnt_lock_file(struct libmnt_lock *ml) * mnt_unlock_file: * @ml: lock struct * - * Unlocks the file. The function could be called independently on the + * Unlocks the file. The function could be called independently of the * lock status (for example from exit(3)). */ void mnt_unlock_file(struct libmnt_lock *ml) @@ -573,7 +575,9 @@ void increment_data(const char *filename, int verbose, int loopno) err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename); fprintf(f, "%ld", num); - fclose(f); + + if (close_stream(f) != 0) + err(EXIT_FAILURE, "write failed: %s", filename); if (verbose) fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(), @@ -667,7 +671,7 @@ int test_lock(struct libmnt_test *ts, int argc, char *argv[]) mnt_free_lock(lock); lock = NULL; - /* The mount command usually finish after mtab update. We + /* The mount command usually finishes after a mtab update. We * simulate this via short sleep -- it's also enough to make * concurrent processes happy. */ diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index e064a6849..9362c0042 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -55,6 +55,13 @@ # include <stdio.h> # include <stdarg.h> +# define WARN_REFCOUNT(m, o, r) \ + do { \ + if ((MNT_DEBUG_ ## m) & libmount_debug_mask && r != 0) \ + fprintf(stderr, "%d: libmount: %8s: [%p]: *** deallocates with refcount=%d\n", \ + getpid(), # m, o, r); \ + } while (0) + # define ON_DBG(m, x) do { \ if ((MNT_DEBUG_ ## m) & libmount_debug_mask) { \ x; \ @@ -100,6 +107,7 @@ mnt_debug_h(void *handler, const char *mesg, ...) } #else /* !CONFIG_LIBMOUNT_DEBUG */ +# define WARN_REFCOUNT(m,o,r) do { ; } while (0) # define ON_DBG(m,x) do { ; } while (0) # define DBG(m,x) do { ; } while (0) # define DBG_FLUSH do { ; } while(0) @@ -129,13 +137,11 @@ extern int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]); #endif /* utils.c */ -extern int endswith(const char *s, const char *sx) - __attribute__((nonnull)); -extern int startswith(const char *s, const char *sx) - __attribute__((nonnull)); - extern char *stripoff_last_component(char *path); +extern int mnt_valid_tagname(const char *tagname); +extern int append_string(char **a, const char *b); + extern int is_file_empty(const char *name); extern int mkdir_p(const char *path, mode_t mode); @@ -200,12 +206,13 @@ struct libmnt_iter { /* - * This struct represents one entry in mtab/fstab/mountinfo file. + * This struct represents one entry in a mtab/fstab/mountinfo file. * (note that fstab[1] means the first column from fstab, and so on...) */ struct libmnt_fs { struct list_head ents; + int refcount; /* reference counter */ int id; /* mountinfo[1]: ID */ int parent; /* mountinfo[2]: parent */ dev_t devno; /* mountinfo[3]: st_dev */ @@ -240,6 +247,8 @@ struct libmnt_fs { int flags; /* MNT_FS_* flags */ pid_t tid; /* /proc/<tid>/mountinfo otherwise zero */ + char *comment; /* fstab comment */ + void *userdata; /* library independent data */ }; @@ -261,7 +270,11 @@ struct libmnt_fs { */ struct libmnt_table { int fmt; /* MNT_FMT_* file format */ - int nents; /* number of valid entries */ + int nents; /* number of entries */ + int refcount; /* reference counter */ + int comms; /* enable/disable comment parsing */ + char *comm_intro; /* First comment in file */ + char *comm_tail; /* Last comment in file */ struct libmnt_cache *cache; /* canonicalized paths/tags cache */ @@ -273,6 +286,7 @@ struct libmnt_table { struct list_head ents; /* list of entries (libmnt_fs) */ + void *userdata; }; extern struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt); @@ -373,17 +387,13 @@ struct libmnt_context #define MNT_FL_FORK (1 << 12) #define MNT_FL_NOSWAPMATCH (1 << 13) -#define MNT_FL_EXTERN_FS (1 << 15) /* cxt->fs is not private */ -#define MNT_FL_EXTERN_FSTAB (1 << 16) /* cxt->fstab is not private */ -#define MNT_FL_EXTERN_CACHE (1 << 17) /* cxt->cache is not private */ - #define MNT_FL_MOUNTDATA (1 << 20) #define MNT_FL_TAB_APPLIED (1 << 21) /* mtab/fstab merged to cxt->fs */ #define MNT_FL_MOUNTFLAGS_MERGED (1 << 22) /* MS_* flags was read from optstr */ #define MNT_FL_SAVED_USER (1 << 23) #define MNT_FL_PREPARED (1 << 24) #define MNT_FL_HELPER (1 << 25) /* [u]mount.<type> */ -#define MNT_FL_LOOPDEV_READY (1 << 26) /* /dev/loop<N> initialized by library */ +#define MNT_FL_LOOPDEV_READY (1 << 26) /* /dev/loop<N> initialized by the library */ #define MNT_FL_MOUNTOPTS_FIXED (1 << 27) /* default flags */ diff --git a/libmount/src/optmap.c b/libmount/src/optmap.c index 7c0a23508..5b25b8f29 100644 --- a/libmount/src/optmap.c +++ b/libmount/src/optmap.c @@ -58,6 +58,7 @@ * mount/mount.h. */ #include "mountP.h" +#include "strutils.h" /* * fs-independent mount flags (built-in MNT_LINUX_MAP) @@ -78,7 +79,7 @@ static const struct libmnt_optmap linux_flags_map[] = { "dirsync", MS_DIRSYNC }, /* synchronous directory modifications */ { "remount", MS_REMOUNT, MNT_NOMTAB }, /* alter flags of mounted FS */ - { "bind", MS_BIND }, /* Remount part of tree elsewhere */ + { "bind", MS_BIND }, /* Remount part of the tree elsewhere */ { "rbind", MS_BIND | MS_REC }, /* Idem, plus mounted subtrees */ #ifdef MS_NOSUB { "sub", MS_NOSUB, MNT_INVERT }, /* allow submounts */ @@ -133,7 +134,7 @@ static const struct libmnt_optmap userspace_opts_map[] = { "defaults", 0, 0 }, /* default options */ { "auto", MNT_MS_NOAUTO, MNT_NOHLPS | MNT_INVERT | MNT_NOMTAB }, /* Can be mounted using -a */ - { "noauto", MNT_MS_NOAUTO, MNT_NOHLPS | MNT_NOMTAB }, /* Can only be mounted explicitly */ + { "noauto", MNT_MS_NOAUTO, MNT_NOHLPS | MNT_NOMTAB }, /* Can only be mounted explicitly */ { "user[=]", MNT_MS_USER }, /* Allow ordinary user to mount (mtab) */ { "nouser", MNT_MS_USER, MNT_INVERT | MNT_NOMTAB }, /* Forbid ordinary user to mount */ @@ -148,7 +149,7 @@ static const struct libmnt_optmap userspace_opts_map[] = { "nogroup", MNT_MS_GROUP, MNT_INVERT | MNT_NOMTAB }, /* Device group has no special privs */ /* - * Note that traditional init scripts assume _netdev option in /etc/mtab to + * Note that traditional init scripts assume the _netdev option in /etc/mtab to * umount network block devices on shutdown. */ { "_netdev", MNT_MS_NETDEV }, /* Device requires network */ @@ -194,7 +195,7 @@ const struct libmnt_optmap *mnt_get_builtin_optmap(int id) } /* - * Lookups for the @name in @maps and returns a map and in @mapent + * Looks up the @name in @maps and returns a map and in @mapent * returns the map entry */ const struct libmnt_optmap *mnt_optmap_get_entry( diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c index 5e9e70807..2cd55685e 100644 --- a/libmount/src/optstr.c +++ b/libmount/src/optstr.c @@ -8,10 +8,10 @@ /** * SECTION: optstr * @title: Options string - * @short_description: low-level API for work with mount options + * @short_description: low-level API for working with mount options * - * This is simple and low-level API to work with mount options that are stored - * in string. + * This is a simple and low-level API to working with mount options that are stored + * in a string. */ #include <ctype.h> @@ -39,8 +39,8 @@ struct libmnt_optloc { (e && (e)->name && !strchr((e)->name, '=') && !((e)->mask & MNT_PREFIX)) /* - * Parses the first option from @optstr. The @optstr pointer is set to begin of - * the next option. + * Parses the first option from @optstr. The @optstr pointer is set to the beginning + * of the next option. * * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success. */ @@ -72,12 +72,12 @@ static int mnt_optstr_parse_next(char **optstr, char **name, size_t *namesz, for (p = optstr0; p && *p; p++) { if (!start) - start = p; /* begin of the option item */ + start = p; /* beginning of the option item */ if (*p == '"') open_quote ^= 1; /* reverse the status */ if (open_quote) continue; /* still in quoted block */ - if (!sep && *p == '=') + if (!sep && p > start && *p == '=') sep = p; /* name and value separator */ if (*p == ',') stop = p; /* terminate the option item */ @@ -111,7 +111,7 @@ error: } /* - * Locates the first option that match with @name. The @end is set to + * Locates the first option that matches @name. The @end is set to the * char behind the option (it means ',' or \0). * * Returns negative number on parse error, 1 when not found and 0 on success. @@ -150,11 +150,11 @@ static int mnt_optstr_locate_option(char *optstr, const char *name, /** * mnt_optstr_next_option: - * @optstr: option string, returns position to next option - * @name: returns option name - * @namesz: returns option name length - * @value: returns option value or NULL - * @valuesz: returns option value length or zero + * @optstr: option string, returns the position of the next option + * @name: returns the option name + * @namesz: returns the option name length + * @value: returns the option value or NULL + * @valuesz: returns the option value length or zero * * Parses the first option in @optstr. * @@ -214,11 +214,11 @@ static int __mnt_optstr_append_option(char **optstr, /** * mnt_optstr_append_option: - * @optstr: option string or NULL, returns reallocated string + * @optstr: option string or NULL, returns a reallocated string * @name: value name * @value: value * - * Returns: 0 on success or -1 in case of error. After error the @optstr should + * Returns: 0 on success or -1 in case of error. After an error the @optstr should * be unmodified. */ int mnt_optstr_append_option(char **optstr, const char *name, const char *value) @@ -238,11 +238,11 @@ int mnt_optstr_append_option(char **optstr, const char *name, const char *value) /** * mnt_optstr_prepend_option: - * @optstr: option string or NULL, returns reallocated string + * @optstr: option string or NULL, returns a reallocated string * @name: value name * @value: value * - * Returns: 0 on success or -1 in case of error. After error the @optstr should + * Returns: 0 on success or -1 in case of error. After an error the @optstr should * be unmodified. */ int mnt_optstr_prepend_option(char **optstr, const char *name, const char *value) @@ -275,9 +275,9 @@ int mnt_optstr_prepend_option(char **optstr, const char *name, const char *value /** * mnt_optstr_get_option: - * @optstr: string with comma separated list of options + * @optstr: string with a comma separated list of options * @name: requested option name - * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL + * @value: returns a pointer to the beginning of the value (e.g. name=VALUE) or NULL * @valsz: returns size of the value or 0 * * Returns: 0 on success, 1 when not found the @name or negative number in case @@ -306,7 +306,7 @@ int mnt_optstr_get_option(const char *optstr, const char *name, /** * mnt_optstr_deduplicate_option: - * @optstr: string with comma separated list of options + * @optstr: string with a comma separated list of options * @name: requested option name * * Removes all instances of @name except the last one. @@ -331,12 +331,12 @@ int mnt_optstr_deduplicate_option(char **optstr, const char *name) rc = mnt_optstr_locate_option(opt, name, &ol); if (!rc) { if (begin) { - /* remove previous instance */ + /* remove the previous instance */ size_t shift = strlen(*optstr); mnt_optstr_remove_option_at(optstr, begin, end); - /* now all offset are not valied anymore - recount */ + /* now all the offsets are not valid anymore - recount */ shift -= strlen(*optstr); ol.begin -= shift; ol.end -= shift; @@ -351,7 +351,7 @@ int mnt_optstr_deduplicate_option(char **optstr, const char *name) } /* - * The result never starts or ends with comma or contains two commas + * The result never starts or ends with a comma or contains two commas * (e.g. ",aaa,bbb" or "aaa,,bbb" or "aaa,") */ int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end) @@ -387,14 +387,14 @@ insert_value(char **str, char *pos, const char *substr, char **next) /* is it necessary to prepend '=' before the substring ? */ sep = !(pos > *str && *(pos - 1) == '='); - /* save an offset of the place where we need add substr */ + /* save an offset of the place where we need to add substr */ posoff = pos - *str; p = realloc(*str, strsz + sep + subsz + 1); if (!p) return -ENOMEM; - /* zeroize new allocated memory -- valgind loves is... */ + /* zeroize the newly allocated memory -- valgrind loves us... */ memset(p + strsz, 0, sep + subsz + 1); /* set pointers to the reallocated string */ @@ -402,7 +402,7 @@ insert_value(char **str, char *pos, const char *substr, char **next) pos = p + posoff; if (possz) - /* create a room for new substring */ + /* create a room for the new substring */ memmove(pos + subsz + sep, pos, possz + 1); if (sep) *pos++ = '='; @@ -420,11 +420,11 @@ insert_value(char **str, char *pos, const char *substr, char **next) /** * mnt_optstr_set_option: - * @optstr: string with comma separated list of options + * @optstr: string with a comma separated list of options * @name: requested option * @value: new value or NULL * - * Set or unset option @value. + * Set or unset the option @value. * * Returns: 0 on success, 1 when not found the @name or negative number in case * of error. @@ -473,7 +473,7 @@ int mnt_optstr_set_option(char **optstr, const char *name, const char *value) /** * mnt_optstr_remove_option: - * @optstr: string with comma separated list of options + * @optstr: string with a comma separated list of options * @name: requested option name * * Returns: 0 on success, 1 when not found the @name or negative number in case @@ -510,13 +510,13 @@ int mnt_optstr_remove_option(char **optstr, const char *name) * * mnt_split_optstr(optstr, &u, NULL, NULL, MNT_NOMTAB, 0); * - * returns all userspace options, the options that does not belong to + * returns all userspace options, the options that do not belong to * mtab are ignored. * * Note that FS options are all options that are undefined in MNT_USERSPACE_MAP * or MNT_LINUX_MAP. * - * Returns: 0 on success, or negative number in case of error. + * Returns: 0 on success, or a negative number in case of error. */ int mnt_split_optstr(const char *optstr, char **user, char **vfs, char **fs, int ignore_user, int ignore_vfs) @@ -540,7 +540,7 @@ int mnt_split_optstr(const char *optstr, char **user, char **vfs, if (user) *user = NULL; - while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { + while (!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { int rc = 0; const struct libmnt_optmap *ent = NULL; const struct libmnt_optmap *m = @@ -567,12 +567,18 @@ int mnt_split_optstr(const char *optstr, char **user, char **vfs, rc = __mnt_optstr_append_option(fs, name, namesz, val, valsz); if (rc) { - if (vfs) + if (vfs) { free(*vfs); - if (fs) + *vfs = NULL; + } + if (fs) { free(*fs); - if (user) + *fs = NULL; + } + if (user) { free(*user); + *user = NULL; + } return rc; } } @@ -582,21 +588,21 @@ int mnt_split_optstr(const char *optstr, char **user, char **vfs, /** * mnt_optstr_get_options - * @optstr: string with comma separated list of options + * @optstr: string with a comma separated list of options * @subset: returns newly allocated string with options * @map: options map * @ignore: mask of the options that should be ignored * - * Extracts options from @optstr that belongs to the @map, for example: + * Extracts options from @optstr that belong to the @map, for example: * * mnt_optstr_get_options(optstr, &p, * mnt_get_builtin_optmap(MNT_LINUX_MAP), * MNT_NOMTAB); * - * the 'p' returns all VFS options, the options that does not belong to mtab + * the 'p' returns all VFS options, the options that do not belong to mtab * are ignored. * - * Returns: 0 on success, or negative number in case of error. + * Returns: 0 on success, or a negative number in case of error. */ int mnt_optstr_get_options(const char *optstr, char **subset, const struct libmnt_optmap *map, int ignore) @@ -652,8 +658,8 @@ int mnt_optstr_get_options(const char *optstr, char **subset, * * "bind,noexec,foo,bar" --returns-> MS_BIND|MS_NOEXEC * - * Note that @flags are not zeroized by this function! This function set/unset - * bites in the @flags only. + * Note that @flags are not zeroized by this function! This function sets/unsets + * bits in the @flags only. * * Returns: 0 on success or negative number in case of error */ @@ -747,8 +753,8 @@ int mnt_optstr_apply_flags(char **optstr, unsigned long flags, fl = flags; /* - * There is a convetion that 'rw/ro' flags is always at the begin of - * the string (athough the 'rw' is unnecessary). + * There is a convention that 'rw/ro' flags are always at the beginning of + * the string (although the 'rw' is unnecessary). */ if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP)) { const char *o = (fl & MS_RDONLY) ? "ro" : "rw"; @@ -774,7 +780,7 @@ int mnt_optstr_apply_flags(char **optstr, unsigned long flags, if (next && *next) { /* * scan @optstr and remove options that are missing in - * the @flags + * @flags */ while(!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { @@ -856,7 +862,7 @@ err: * modify @optstr and returns zero if libmount is compiled without SELinux * support. * - * Returns: 0 on success, negative number in case of error. + * Returns: 0 on success, a negative number in case of error. */ #ifndef HAVE_LIBSELINUX int mnt_optstr_fix_secontext(char **optstr __attribute__ ((__unused__)), @@ -910,7 +916,7 @@ int mnt_optstr_fix_secontext(char **optstr, return -EINVAL; - /* create quoted string from the raw context */ + /* create a quoted string from the raw context */ sz = strlen((char *) raw); if (!sz) return -EINVAL; @@ -947,8 +953,8 @@ static int set_uint_value(char **optstr, unsigned int num, } /* - * @optstr: string with comma separated list of options - * @value: pointer to the begin of the uid value + * @optstr: string with a comma separated list of options + * @value: pointer to the beginning of the uid value * @valsz: size of the value * @next: returns pointer to the next option (optional argument) @@ -958,7 +964,7 @@ static int set_uint_value(char **optstr, unsigned int num, * if (!mnt_optstr_get_option(optstr, "uid", &val, &valsz)) * mnt_optstr_fix_uid(&optstr, val, valsz, NULL); * - * Returns: 0 on success, negative number in case of error. + * Returns: 0 on success, a negative number in case of error. */ int mnt_optstr_fix_uid(char **optstr, char *value, size_t valsz, char **next) { @@ -998,14 +1004,14 @@ int mnt_optstr_fix_uid(char **optstr, char *value, size_t valsz, char **next) } /* - * @optstr: string with comma separated list of options - * @value: pointer to the begin of the uid value + * @optstr: string with a comma separated list of options + * @value: pointer to the beginning of the uid value * @valsz: size of the value * @next: returns pointer to the next option (optional argument) * Translates "groupname" or "usergid" to the real GID. * - * Returns: 0 on success, negative number in case of error. + * Returns: 0 on success, a negative number in case of error. */ int mnt_optstr_fix_gid(char **optstr, char *value, size_t valsz, char **next) { @@ -1328,7 +1334,7 @@ int main(int argc, char *argv[]) { struct libmnt_test tss[] = { { "--append", test_append, "<optstr> <name> [<value>] append value to optstr" }, - { "--prepend",test_prepend,"<optstr> <name> [<value>] prepend value to optstr" }, + { "--prepend",test_prepend,"<optstr> <name> [<value>] prepend value to optstr" }, { "--set", test_set, "<optstr> <name> [<value>] (un)set value" }, { "--get", test_get, "<optstr> <name> search name in optstr" }, { "--remove", test_remove, "<optstr> <name> remove name in optstr" }, diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 11a297814..b023504ad 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -11,9 +11,9 @@ * @short_description: container for entries from fstab, mtab or mountinfo * * Note that mnt_table_find_* functions are mount(8) compatible. These functions - * try to find an entry in more iterations where the first attempt is always + * try to find an entry in more iterations, where the first attempt is always * based on comparison with unmodified (non-canonicalized or un-evaluated) - * paths or tags. For example fstab with two entries: + * paths or tags. For example a fstab with two entries: * <informalexample> * <programlisting> * LABEL=foo /foo auto rw @@ -39,7 +39,7 @@ * mnt_table_find_source(tb, "UUID=anyuuid", &fs); * </programlisting> * </informalexample> - * will returns the first entry (if UUID matches with the device). + * will return the first entry (if UUID matches with the device). */ #include <blkid.h> @@ -47,6 +47,8 @@ #include "strutils.h" #include "loopdev.h" +static int is_mountinfo(struct libmnt_table *tb); + /** * mnt_new_table: * @@ -66,7 +68,7 @@ struct libmnt_table *mnt_new_table(void) return NULL; DBG(TAB, mnt_debug_h(tb, "alloc")); - + tb->refcount = 1; INIT_LIST_HEAD(&tb->ents); return tb; } @@ -75,7 +77,8 @@ struct libmnt_table *mnt_new_table(void) * mnt_reset_table: * @tb: tab pointer * - * Dealocates all entries (filesystems) from the table + * Removes all entries (filesystems) from the table. The filesystems with zero + * reference count will be deallocated. * * Returns: 0 on success or negative number in case of error. */ @@ -89,7 +92,7 @@ int mnt_reset_table(struct libmnt_table *tb) while (!list_empty(&tb->ents)) { struct libmnt_fs *fs = list_entry(tb->ents.next, struct libmnt_fs, ents); - mnt_free_fs(fs); + mnt_table_remove_fs(tb, fs); } tb->nents = 0; @@ -97,10 +100,46 @@ int mnt_reset_table(struct libmnt_table *tb) } /** + * mnt_ref_table: + * @tb: table pointer + * + * Increments reference counter. + */ +void mnt_ref_table(struct libmnt_table *tb) +{ + if (tb) { + tb->refcount++; + /*DBG(FS, mnt_debug_h(tb, "ref=%d", tb->refcount));*/ + } +} + +/** + * mnt_unref_table: + * @tb: table pointer + * + * De-increments reference counter, on zero the @tb is automatically + * deallocated by mnt_free_table(). + */ +void mnt_unref_table(struct libmnt_table *tb) +{ + if (tb) { + tb->refcount--; + /*DBG(FS, mnt_debug_h(tb, "unref=%d", tb->refcount));*/ + if (tb->refcount <= 0) + mnt_free_table(tb); + } +} + + +/** * mnt_free_table: * @tb: tab pointer * - * Deallocates tab struct and all entries. + * Deallocates the table. This function does not care about reference count. Don't + * use this function directly -- it's better to use use mnt_unref_table(). + * + * The table entries (filesystems) are unrefrenced by mnt_reset_table() and + * cache by mnt_unref_cache(). */ void mnt_free_table(struct libmnt_table *tb) { @@ -109,7 +148,12 @@ void mnt_free_table(struct libmnt_table *tb) mnt_reset_table(tb); + WARN_REFCOUNT(TAB, tb, tb->refcount); DBG(TAB, mnt_debug_h(tb, "free")); + + mnt_unref_cache(tb->cache); + free(tb->comm_intro); + free(tb->comm_tail); free(tb); } @@ -117,26 +161,228 @@ void mnt_free_table(struct libmnt_table *tb) * mnt_table_get_nents: * @tb: pointer to tab * - * Returns: number of valid entries in tab. + * Returns: number of entries in table. */ int mnt_table_get_nents(struct libmnt_table *tb) { - assert(tb); return tb ? tb->nents : 0; } /** + * mnt_table_is_empty: + * @tb: pointer to tab + * + * Returns: 1 if the table is without filesystems, or 0. + */ +int mnt_table_is_empty(struct libmnt_table *tb) +{ + assert(tb); + return tb == NULL || list_empty(&tb->ents) ? 1 : 0; +} + +/** + * mnt_table_set_userdata: + * @tb: pointer to tab + * @data: pointer to user data + * + * Sets pointer to the private user data. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_set_userdata(struct libmnt_table *tb, void *data) +{ + assert(tb); + if (!tb) + return -EINVAL; + + tb->userdata = data; + return 0; +} + +/** + * mnt_table_get_userdata: + * @tb: pointer to tab + * + * Returns: pointer to user's data. + */ +void *mnt_table_get_userdata(struct libmnt_table *tb) +{ + assert(tb); + return tb ? tb->userdata : NULL; +} + +/** + * mnt_table_enable_comments: + * @tb: pointer to tab + * @enable: TRUE or FALSE + * + * Enables parsing of comments. + * + * The initial (intro) file comment is accessible by + * mnt_table_get_intro_comment(). The intro and the comment of the first fstab + * entry has to be separated by blank line. The filesystem comments are + * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible + * by mnt_table_get_trailing_comment(). + * + * <informalexample> + * <programlisting> + * # + * # Intro comment + * # + * + * # this comments belongs to the first fs + * LABEL=foo /mnt/foo auto defaults 1 2 + * # this comments belongs to the second fs + * LABEL=bar /mnt/bar auto defaults 1 2 + * # tailing comment + * </programlisting> + * </informalexample> + */ +void mnt_table_enable_comments(struct libmnt_table *tb, int enable) +{ + assert(tb); + if (tb) + tb->comms = enable; +} + +/** + * mnt_table_with_comments: + * @tb: pointer to table + * + * Returns: 1 if comments parsing is enabled, or 0. + */ +int mnt_table_with_comments(struct libmnt_table *tb) +{ + assert(tb); + return tb ? tb->comms : 0; +} + +/** + * mnt_table_get_intro_comment: + * @tb: pointer to tab + * + * Returns: initial comment in tb + */ +const char *mnt_table_get_intro_comment(struct libmnt_table *tb) +{ + assert(tb); + return tb ? tb->comm_intro : NULL; +} + +/** + * mnt_table_set_into_comment: + * @tb: pointer to tab + * @comm: comment or NULL + * + * Sets the initial comment in tb. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm) +{ + char *p = NULL; + + assert(tb); + if (!tb) + return -EINVAL; + if (comm) { + p = strdup(comm); + if (!p) + return -ENOMEM; + } + free(tb->comm_intro); + tb->comm_intro = p; + return 0; +} + +/** + * mnt_table_append_into_comment: + * @tb: pointer to tab + * @comm: comment of NULL + * + * Appends the initial comment in tb. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm) +{ + assert(tb); + if (!tb) + return -EINVAL; + return append_string(&tb->comm_intro, comm); +} + +/** + * mnt_table_get_trailing_comment: + * @tb: pointer to tab + * + * Returns: table trailing comment + */ +const char *mnt_table_get_trailing_comment(struct libmnt_table *tb) +{ + assert(tb); + return tb ? tb->comm_tail : NULL; +} + +/** + * mnt_table_set_trailing_comment + * @tb: pointer to tab + * @comm: comment string + * + * Sets the trailing comment in table. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm) +{ + char *p = NULL; + + assert(tb); + if (!tb) + return -EINVAL; + if (comm) { + p = strdup(comm); + if (!p) + return -ENOMEM; + } + free(tb->comm_tail); + tb->comm_tail = p; + return 0; +} + +/** + * mnt_table_append_trailing_comment: + * @tb: pointer to tab + * @comm: comment of NULL + * + * Appends to the trailing table comment. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm) +{ + assert(tb); + if (!tb) + return -EINVAL; + return append_string(&tb->comm_tail, comm); +} + +/** * mnt_table_set_cache: * @tb: pointer to tab * @mpc: pointer to struct libmnt_cache instance * - * Setups a cache for canonicalized paths and evaluated tags (LABEL/UUID). The + * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The * cache is recommended for mnt_table_find_*() functions. * * The cache could be shared between more tabs. Be careful when you share the * same cache between more threads -- currently the cache does not provide any * locking method. * + * This function increments cache refrence counter. It's recomented to use + * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache + * referenced by @tb only. + * * See also mnt_new_cache(). * * Returns: 0 on success or negative number in case of error. @@ -146,6 +392,9 @@ int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc) assert(tb); if (!tb) return -EINVAL; + + mnt_ref_cache(mpc); /* new */ + mnt_unref_cache(tb->cache); /* old */ tb->cache = mpc; return 0; } @@ -167,7 +416,9 @@ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb) * @tb: tab pointer * @fs: new entry * - * Adds a new entry to tab. + * Adds a new entry to tab and increment @fs reference counter. Don't forget to + * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs + * referenced by the table only. * * Returns: 0 on success or negative number in case of error. */ @@ -179,11 +430,12 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) if (!tb || !fs) return -EINVAL; + mnt_ref_fs(fs); list_add_tail(&fs->ents, &tb->ents); + tb->nents++; DBG(TAB, mnt_debug_h(tb, "add entry: %s %s", mnt_fs_get_source(fs), mnt_fs_get_target(fs))); - tb->nents++; return 0; } @@ -192,6 +444,10 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) * @tb: tab pointer * @fs: new entry * + * Removes the @fs from the table and de-increment reference counter of the @fs. The + * filesystem with zero reference counter will be deallocated. Don't forget to use + * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later. + * * Returns: 0 on success or negative number in case of error. */ int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs) @@ -201,7 +457,11 @@ int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs) if (!tb || !fs) return -EINVAL; + list_del(&fs->ents); + INIT_LIST_HEAD(&fs->ents); /* otherwise FS still points to the list */ + + mnt_unref_fs(fs); tb->nents--; return 0; } @@ -211,18 +471,18 @@ int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs) * @tb: mountinfo file (/proc/self/mountinfo) * @root: returns pointer to the root filesystem (/) * - * The function uses parent ID from mountinfo file to determine root filesystem + * The function uses the parent ID from the mountinfo file to determine the root filesystem * (the filesystem with the smallest ID). The function is designed mostly for - * applications where is necessary to sort mountpoints by IDs to get the tree + * applications where it is necessary to sort mountpoints by IDs to get the tree * of the mountpoints (e.g. findmnt default output). * - * If you're not sure than use + * If you're not sure, then use * * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD); * - * this is more robust and usable for arbitrary tab file (including fstab). + * this is more robust and usable for arbitrary tab files (including fstab). * - * Returns: 0 on success or less then zero case of error. + * Returns: 0 on success or negative number in case of error. */ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) { @@ -233,16 +493,16 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) assert(tb); assert(root); - if (!tb || !root) + if (!tb || !root || !is_mountinfo(tb)) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "lookup root fs")); + *root = NULL; + mnt_reset_iter(&itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { int id = mnt_fs_get_parent_id(fs); - if (!id) - break; /* @tab is not mountinfo file? */ if (!*root || id < root_id) { *root = fs; @@ -250,7 +510,7 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) } } - return root_id ? 0 : -EINVAL; + return *root ? 0 : -EINVAL; } /** @@ -260,10 +520,10 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) * @parent: parental FS * @chld: returns the next child filesystem * - * Note that filesystems are returned in the order how was mounted (according to + * Note that filesystems are returned in the order of mounting (according to * IDs in /proc/self/mountinfo). * - * Returns: 0 on success, negative number in case of error or 1 at end of list. + * Returns: 0 on success, negative number in case of error or 1 at the end of list. */ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *parent, struct libmnt_fs **chld) @@ -271,15 +531,13 @@ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs; int parent_id, lastchld_id = 0, chld_id = 0; - if (!tb || !itr || !parent) + if (!tb || !itr || !parent || !is_mountinfo(tb)) return -EINVAL; - DBG(TAB, mnt_debug_h(tb, "lookup next child of %s", + DBG(TAB, mnt_debug_h(tb, "lookup next child of '%s'", mnt_fs_get_target(parent))); parent_id = mnt_fs_get_id(parent); - if (!parent_id) - return -EINVAL; /* get ID of the previously returned child */ if (itr->head && itr->p != itr->head) { @@ -298,7 +556,7 @@ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, id = mnt_fs_get_id(fs); - /* avoid infinite loop. This only happens in rare cases + /* avoid an infinite loop. This only happens in rare cases * such as in early userspace when the rootfs is its own parent */ if (id == parent_id) continue; @@ -310,7 +568,7 @@ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, } } - if (!chld_id) + if (!*chld) return 1; /* end of iterator */ /* set the iterator to the @chld for the next call */ @@ -325,7 +583,7 @@ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, * @itr: iterator * @fs: returns the next tab entry * - * Returns: 0 on success, negative number in case of error or 1 at end of list. + * Returns: 0 on success, negative number in case of error or 1 at the end of list. * * Example: * <informalexample> @@ -334,11 +592,10 @@ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, * const char *dir = mnt_fs_get_target(fs); * printf("mount point: %s\n", dir); * } - * mnt_free_table(fi); * </programlisting> * </informalexample> * - * lists all mountpoints from fstab in backward order. + * lists all mountpoints from fstab in reverse order. */ int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs) { @@ -363,14 +620,54 @@ int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct l } /** + * mnt_table_first_fs: + * @tb: tab pointer + * @fs: returns the first tab entry + * + * Returns: 0 on success, negative number in case of error or 1 at the end of list. + */ +int mnt_table_first_fs(struct libmnt_table *tb, struct libmnt_fs **fs) +{ + assert(tb); + assert(fs); + + if (!tb || !fs) + return -EINVAL; + if (list_empty(&tb->ents)) + return 1; + *fs = list_first_entry(&tb->ents, struct libmnt_fs, ents); + return 0; +} + +/** + * mnt_table_last_fs: + * @tb: tab pointer + * @fs: returns the last tab entry + * + * Returns: 0 on success, negative number in case of error or 1 at the end of list. + */ +int mnt_table_last_fs(struct libmnt_table *tb, struct libmnt_fs **fs) +{ + assert(tb); + assert(fs); + + if (!tb || !fs) + return -EINVAL; + if (list_empty(&tb->ents)) + return 1; + *fs = list_last_entry(&tb->ents, struct libmnt_fs, ents); + return 0; +} + +/** * mnt_table_find_next_fs: * @tb: table * @itr: iterator - * @match_func: function returns 1 or 0 + * @match_func: function returning 1 or 0 * @userdata: extra data for match_func * @fs: returns pointer to the next matching table entry * - * This function allows search in @tb. + * This function allows searching in @tb. * * Returns: negative number in case of error, 1 at end of table or 0 o success. */ @@ -431,7 +728,7 @@ int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct * @path: directory * @direction: MNT_ITER_{FORWARD,BACKWARD} * - * Same like mnt_get_mountpoint(), but this function does not rely on + * Same as mnt_get_mountpoint(), except this function does not rely on * st_dev numbers. * * Returns: a tab entry or NULL. @@ -442,12 +739,12 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, { char *mnt; - if (!tb || !path) + if (!tb || !path || !*path) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) return NULL; - DBG(TAB, mnt_debug_h(tb, "lookup MOUNTPOINT: %s", path)); + DBG(TAB, mnt_debug_h(tb, "lookup MOUNTPOINT: '%s'", path)); mnt = strdup(path); if (!mnt) @@ -478,10 +775,10 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, * @path: mountpoint directory * @direction: MNT_ITER_{FORWARD,BACKWARD} * - * Try to lookup an entry in given tab, possible are three iterations, first - * with @path, second with realpath(@path) and third with realpath(@path) + * Try to lookup an entry in the given tab, three iterations are possible, the first + * with @path, the second with realpath(@path) and the third with realpath(@path) * against realpath(fs->target). The 2nd and 3rd iterations are not performed - * when @tb cache is not set (see mnt_table_set_cache()). + * when the @tb cache is not set (see mnt_table_set_cache()). * * Returns: a tab entry or NULL. */ @@ -494,12 +791,12 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat assert(tb); assert(path); - if (!tb || !path) + if (!tb || !path || !*path) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) return NULL; - DBG(TAB, mnt_debug_h(tb, "lookup TARGET: %s", path)); + DBG(TAB, mnt_debug_h(tb, "lookup TARGET: '%s'", path)); /* native @target */ mnt_reset_iter(&itr, direction); @@ -510,6 +807,8 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) return NULL; + DBG(TAB, mnt_debug_h(tb, "lookup canonical TARGET: '%s'", cn)); + /* canonicalized paths in struct libmnt_table */ mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { @@ -519,7 +818,7 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat /* non-canonicaled path in struct libmnt_table * -- note that mountpoint in /proc/self/mountinfo is already - * canonicalized by kernel + * canonicalized by the kernel */ mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { @@ -545,11 +844,11 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat * @path: source path (devname or dirname) or NULL * @direction: MNT_ITER_{FORWARD,BACKWARD} * - * Try to lookup an entry in given tab, possible are four iterations, first - * with @path, second with realpath(@path), third with tags (LABEL, UUID, ..) - * from @path and fourth with realpath(@path) against realpath(entry->srcpath). + * Try to lookup an entry in the given tab, four iterations are possible, the first + * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..) + * from @path and the fourth with realpath(@path) against realpath(entry->srcpath). * - * The 2nd, 3rd and 4th iterations are not performed when @tb cache is not + * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not * set (see mnt_table_set_cache()). * * Note that NULL is a valid source path; it will be replaced with "none". The @@ -561,17 +860,17 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa { struct libmnt_iter itr; struct libmnt_fs *fs = NULL; - int ntags = 0; + int ntags = 0, nents; char *cn; const char *p; assert(tb); - if (!tb || !path) + if (!tb || !path || !*path) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) return NULL; - DBG(TAB, mnt_debug_h(tb, "lookup srcpath: %s", path)); + DBG(TAB, mnt_debug_h(tb, "lookup SRCPATH: '%s'", path)); /* native paths */ mnt_reset_iter(&itr, direction); @@ -585,8 +884,12 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) return NULL; + DBG(TAB, mnt_debug_h(tb, "lookup canonical SRCPATH: '%s'", cn)); + + nents = mnt_table_get_nents(tb); + /* canonicalized paths in struct libmnt_table */ - if (ntags < mnt_table_get_nents(tb)) { + if (ntags < nents) { mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (mnt_fs_streq_srcpath(fs, cn)) @@ -612,9 +915,9 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa return fs; } } else if (rc < 0 && errno == EACCES) { - /* @path is unaccessible, try evaluate all TAGs in @tb + /* @path is inaccessible, try evaluating all TAGs in @tb * by udev symlinks -- this could be expensive on systems - * with huge fstab/mtab */ + * with a huge fstab/mtab */ while(mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *t, *v, *x; if (mnt_fs_get_tag(fs, &t, &v)) @@ -629,7 +932,7 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa } /* non-canonicalized paths in struct libmnt_table */ - if (ntags <= mnt_table_get_nents(tb)) { + if (ntags <= nents) { mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs)) @@ -655,9 +958,9 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa * @val: tag value * @direction: MNT_ITER_{FORWARD,BACKWARD} * - * Try to lookup an entry in given tab, first attempt is lookup by @tag and + * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and * @val, for the second attempt the tag is evaluated (converted to the device - * name) and mnt_table_find_srcpath() is preformed. The second attempt is not + * name) and mnt_table_find_srcpath() is performed. The second attempt is not * performed when @tb cache is not set (see mnt_table_set_cache()). * Returns: a tab entry or NULL. @@ -672,7 +975,7 @@ struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, assert(tag); assert(val); - if (!tb || !tag || !val) + if (!tb || !tag || !*tag || !val) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) return NULL; @@ -703,16 +1006,17 @@ struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, * @source: TAG or path * @direction: MNT_ITER_{FORWARD,BACKWARD} * - * This is high-level API for mnt_table_find_{srcpath,tag}. You needn't to care - * about @source format (device, LABEL, UUID, ...). This function parses @source - * and calls mnt_table_find_tag() or mnt_table_find_srcpath(). + * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care + * about the @source format (device, LABEL, UUID, ...). This function parses + * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath(). * * Returns: a tab entry or NULL. */ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, const char *source, int direction) { - struct libmnt_fs *fs = NULL; + struct libmnt_fs *fs; + char *t = NULL, *v = NULL; assert(tb); @@ -721,20 +1025,15 @@ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) return NULL; - DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s", source)); - - if (source && strchr(source, '=')) { - char *tag, *val; + DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: '%s'", source)); - if (blkid_parse_tag_string(source, &tag, &val) == 0) { - - fs = mnt_table_find_tag(tb, tag, val, direction); - - free(tag); - free(val); - } - } else + if (blkid_parse_tag_string(source, &t, &v) || !mnt_valid_tagname(t)) fs = mnt_table_find_srcpath(tb, source, direction); + else + fs = mnt_table_find_tag(tb, t, v, direction); + + free(t); + free(v); return fs; } @@ -747,7 +1046,7 @@ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, * @direction: MNT_ITER_{FORWARD,BACKWARD} * * This function is implemented by mnt_fs_match_source() and - * mnt_fs_match_target() functions. It means that this is more expensive that + * mnt_fs_match_target() functions. It means that this is more expensive than * others mnt_table_find_* function, because every @tab entry is fully evaluated. * * Returns: a tab entry or NULL. @@ -761,7 +1060,7 @@ struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *sourc assert(tb); assert(target); - if (!tb || !target) + if (!tb || !target || !*target || !source || !*source) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) return NULL; @@ -785,7 +1084,7 @@ struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *sourc * @devno: device number * @direction: MNT_ITER_{FORWARD,BACKWARD} * - * Note that zero could be valid device number for root pseudo filesystem (e.g. + * Note that zero could be a valid device number for the root pseudo filesystem (e.g. * tmpfs). * * Returns: a tab entry or NULL. @@ -819,7 +1118,7 @@ struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb, * tb: /proc/self/mountinfo * fs: filesystem * mountflags: MS_BIND or 0 - * fsroot: fs-root that will be probably used in the mountinfo file + * fsroot: fs-root that will probably be used in the mountinfo file * for @fs after mount(2) * * For btrfs subvolumes this function returns NULL, but @fsroot properly set. @@ -841,7 +1140,7 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, assert(fs); assert(fsroot); - DBG(TAB, mnt_debug("lookup fs-root for %s", mnt_fs_get_source(fs))); + DBG(TAB, mnt_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs))); fstype = mnt_fs_get_fstype(fs); @@ -945,20 +1244,20 @@ static int is_mountinfo(struct libmnt_table *tb) } /** - * mnt_table_is_mounted: + * mnt_table_is_fs__mounted: * @tb: /proc/self/mountinfo file * @fstab_fs: /etc/fstab entry * * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is - * ignored. This function explicitly compares source, target and root of the + * ignored. This function explicitly compares the source, target and root of the * filesystems. * * Note that source and target are canonicalized only if a cache for @tb is * defined (see mnt_table_set_cache()). The target canonicalization may - * triggers automount on autofs mountpoints! + * trigger automount on autofs mountpoints! * * Don't use it if you want to know if a device is mounted, just use - * mnt_table_find_source() for the device. + * mnt_table_find_source() on the device. * * This function is designed mostly for "mount -a". * @@ -973,6 +1272,7 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) const char *src = NULL, *tgt = NULL; char *xtgt = NULL; int rc = 0; + dev_t devno = 0; assert(tb); assert(fstab_fs); @@ -980,7 +1280,7 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) DBG(FS, mnt_debug_h(fstab_fs, "is FS mounted? [target=%s]", mnt_fs_get_target(fstab_fs))); - if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_get_nents(tb) == 0) { + if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_is_empty(tb)) { DBG(FS, mnt_debug_h(fstab_fs, "- ignore (swap or no data)")); return 0; } @@ -1004,6 +1304,14 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) if (src && tb->cache && !mnt_fs_is_pseudofs(fstab_fs)) src = mnt_resolve_spec(src, tb->cache); + if (src && root) { + struct stat st; + + devno = mnt_fs_get_devno(fstab_fs); + if (!devno && stat(src, &st) == 0 && S_ISBLK(st.st_mode)) + devno = st.st_rdev; + } + tgt = mnt_fs_get_target(fstab_fs); if (!tgt || !src) { @@ -1014,7 +1322,12 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) while (mnt_table_next_fs(tb, &itr, &fs) == 0) { - if (!mnt_fs_streq_srcpath(fs, src)) { + int eq = mnt_fs_streq_srcpath(fs, src); + + if (!eq && devno && mnt_fs_get_devno(fs) == devno) + eq = 1; + + if (!eq) { /* The source does not match. Maybe the source is a loop * device backing file. */ @@ -1046,7 +1359,7 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) } /* - * Compare target, try to minimize number of situations when we + * Compare target, try to minimize the number of situations when we * need to canonicalize the path to avoid readlink() on * mountpoints. */ @@ -1076,10 +1389,10 @@ static int parser_errcb(struct libmnt_table *tb, const char *filename, int line) { fprintf(stderr, "%s:%d: parse error\n", filename, line); - return 1; /* all errors are recoverable -- this is default */ + return 1; /* all errors are recoverable -- this is the default */ } -struct libmnt_table *create_table(const char *file) +struct libmnt_table *create_table(const char *file, int comments) { struct libmnt_table *tb; @@ -1089,6 +1402,7 @@ struct libmnt_table *create_table(const char *file) if (!tb) goto err; + mnt_table_enable_comments(tb, comments); mnt_table_set_parser_errcb(tb, parser_errcb); if (mnt_table_parse_file(tb, file) != 0) @@ -1096,7 +1410,7 @@ struct libmnt_table *create_table(const char *file) return tb; err: fprintf(stderr, "%s: parsing failed\n", file); - mnt_free_table(tb); + mnt_unref_table(tb); return NULL; } @@ -1106,7 +1420,7 @@ int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) struct libmnt_fs *fs; int rc = -1; - tb = create_table(argv[1]); + tb = create_table(argv[1], FALSE); if (!tb) return -1; @@ -1123,10 +1437,10 @@ int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) printf("COPY:\n"); mnt_fs_print_debug(fs, stdout); - mnt_free_fs(fs); + mnt_unref_fs(fs); rc = 0; done: - mnt_free_table(tb); + mnt_unref_table(tb); return rc; } @@ -1136,8 +1450,12 @@ int test_parse(struct libmnt_test *ts, int argc, char *argv[]) struct libmnt_iter *itr = NULL; struct libmnt_fs *fs; int rc = -1; + int parse_comments = FALSE; - tb = create_table(argv[1]); + if (argc == 3 && !strcmp(argv[2], "--comments")) + parse_comments = TRUE; + + tb = create_table(argv[1], parse_comments); if (!tb) return -1; @@ -1145,12 +1463,20 @@ int test_parse(struct libmnt_test *ts, int argc, char *argv[]) if (!itr) goto done; + if (mnt_table_get_intro_comment(tb)) + fprintf(stdout, "Initial comment:\n\"%s\"\n", + mnt_table_get_intro_comment(tb)); + while(mnt_table_next_fs(tb, itr, &fs) == 0) mnt_fs_print_debug(fs, stdout); + + if (mnt_table_get_trailing_comment(tb)) + fprintf(stdout, "Trailing comment:\n\"%s\"\n", + mnt_table_get_trailing_comment(tb)); rc = 0; done: mnt_free_iter(itr); - mnt_free_table(tb); + mnt_unref_table(tb); return rc; } @@ -1169,7 +1495,7 @@ int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) file = argv[1], find = argv[2], what = argv[3]; - tb = create_table(file); + tb = create_table(file, FALSE); if (!tb) goto done; @@ -1178,6 +1504,7 @@ int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) if (!mpc) goto done; mnt_table_set_cache(tb, mpc); + mnt_unref_cache(mpc); if (strcasecmp(find, "source") == 0) fs = mnt_table_find_source(tb, what, dr); @@ -1191,8 +1518,7 @@ int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) rc = 0; } done: - mnt_free_table(tb); - mnt_free_cache(mpc); + mnt_unref_table(tb); return rc; } @@ -1213,13 +1539,14 @@ int test_find_pair(struct libmnt_test *ts, int argc, char *argv[]) struct libmnt_cache *mpc = NULL; int rc = -1; - tb = create_table(argv[1]); + tb = create_table(argv[1], FALSE); if (!tb) return -1; mpc = mnt_new_cache(); if (!mpc) goto done; mnt_table_set_cache(tb, mpc); + mnt_unref_cache(mpc); fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD); if (!fs) @@ -1228,8 +1555,7 @@ int test_find_pair(struct libmnt_test *ts, int argc, char *argv[]) mnt_fs_print_debug(fs, stdout); rc = 0; done: - mnt_free_table(tb); - mnt_free_cache(mpc); + mnt_unref_table(tb); return rc; } @@ -1247,6 +1573,7 @@ int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) if (!mpc) goto done; mnt_table_set_cache(tb, mpc); + mnt_unref_cache(mpc); fs = mnt_table_find_mountpoint(tb, argv[1], MNT_ITER_BACKWARD); if (!fs) @@ -1255,8 +1582,7 @@ int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) mnt_fs_print_debug(fs, stdout); rc = 0; done: - mnt_free_table(tb); - mnt_free_cache(mpc); + mnt_unref_table(tb); return rc; } @@ -1274,7 +1600,7 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) return -1; } - fstab = create_table(argv[1]); + fstab = create_table(argv[1], FALSE); if (!fstab) goto done; @@ -1286,6 +1612,7 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) if (!mpc) goto done; mnt_table_set_cache(tb, mpc); + mnt_unref_cache(mpc); while(mnt_table_next_fs(fstab, itr, &fs) == 0) { if (mnt_table_is_fs_mounted(tb, fs)) @@ -1300,9 +1627,8 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) rc = 0; done: - mnt_free_table(tb); - mnt_free_table(fstab); - mnt_free_cache(mpc); + mnt_unref_table(tb); + mnt_unref_table(fstab); mnt_free_iter(itr); return rc; } @@ -1310,7 +1636,7 @@ done: int main(int argc, char *argv[]) { struct libmnt_test tss[] = { - { "--parse", test_parse, "<file> parse and print tab" }, + { "--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>" }, diff --git a/libmount/src/tab_diff.c b/libmount/src/tab_diff.c index f01f889f8..052278233 100644 --- a/libmount/src/tab_diff.c +++ b/libmount/src/tab_diff.c @@ -25,7 +25,7 @@ struct libmnt_tabdiff { int nchanges; /* number of changes */ struct list_head changes; /* list with modified entries */ - struct list_head unused; /* list with unuused entries */ + struct list_head unused; /* list with unused entries */ }; /** @@ -54,6 +54,8 @@ static void free_tabdiff_entry(struct tabdiff_entry *de) if (!de) return; list_del(&de->changes); + mnt_unref_fs(de->new_fs); + mnt_unref_fs(de->old_fs); free(de); } @@ -89,7 +91,7 @@ void mnt_free_tabdiff(struct libmnt_tabdiff *df) * * The options @old_fs, @new_fs and @oper are optional. * - * Returns: 0 on success, negative number in case of error or 1 at end of list. + * Returns: 0 on success, negative number in case of error or 1 at the end of list. */ int mnt_tabdiff_next_change(struct libmnt_tabdiff *df, struct libmnt_iter *itr, struct libmnt_fs **old_fs, struct libmnt_fs **new_fs, int *oper) @@ -124,9 +126,9 @@ static int tabdiff_reset(struct libmnt_tabdiff *df) { assert(df); - DBG(DIFF, mnt_debug_h(df, "reseting")); + DBG(DIFF, mnt_debug_h(df, "resetting")); - /* zeroize all entries and move them to the list of unuused + /* zeroize all entries and move them to the list of unused */ while (!list_empty(&df->changes)) { struct tabdiff_entry *de = list_entry(df->changes.next, @@ -135,6 +137,9 @@ static int tabdiff_reset(struct libmnt_tabdiff *df) list_del(&de->changes); list_add_tail(&de->changes, &df->unused); + mnt_unref_fs(de->new_fs); + mnt_unref_fs(de->old_fs); + de->new_fs = de->old_fs = NULL; de->oper = 0; } @@ -164,6 +169,12 @@ static int tabdiff_add_entry(struct libmnt_tabdiff *df, struct libmnt_fs *old, INIT_LIST_HEAD(&de->changes); + mnt_ref_fs(new); + mnt_ref_fs(old); + + mnt_unref_fs(de->new_fs); + mnt_unref_fs(de->old_fs); + de->old_fs = old; de->new_fs = new; de->oper = oper; @@ -280,6 +291,8 @@ int mnt_diff_tables(struct libmnt_tabdiff *df, struct libmnt_table *old_tab, de = tabdiff_get_mount(df, src, mnt_fs_get_id(fs)); if (de) { + mnt_ref_fs(fs); + mnt_unref_fs(de->old_fs); de->oper = MNT_TABDIFF_MOVE; de->old_fs = fs; } else @@ -342,8 +355,8 @@ int test_diff(struct libmnt_test *ts, int argc, char *argv[]) rc = 0; done: - mnt_free_table(tb_old); - mnt_free_table(tb_new); + mnt_unref_table(tb_old); + mnt_unref_table(tb_new); mnt_free_tabdiff(diff); mnt_free_iter(itr); return rc; diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index e930fd841..38c48fdfa 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -47,7 +47,7 @@ static int next_number(char **s, int *num) *s = end; - /* valid end of number is space or terminator */ + /* valid end of number is a space or a terminator */ if (*end == ' ' || *end == '\t' || *end == '\0') return 0; return -1; @@ -82,7 +82,7 @@ static int mnt_parse_table_line(struct libmnt_fs *fs, char *s) if (optstr && *optstr) unmangle_string(optstr); - /* note that __foo functions does not reallocate the string + /* note that __foo functions do not reallocate the string */ rc = __mnt_fs_set_source_ptr(fs, src); if (!rc) { @@ -128,7 +128,7 @@ static int mnt_parse_table_line(struct libmnt_fs *fs, char *s) } /* - * Parses one line from mountinfo file + * Parses one line from a mountinfo file */ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) { @@ -158,7 +158,7 @@ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) /* (7) optional fields, terminated by " - " */ p = strstr(s, " - "); if (!p) { - DBG(TAB, mnt_debug("mountinfo parse error: not found separator")); + DBG(TAB, mnt_debug("mountinfo parse error: separator not found")); return -EINVAL; } if (p > s + 1) @@ -174,9 +174,20 @@ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) &fs->fs_optstr); if (rc >= 10) { + size_t sz; + fs->flags |= MNT_FS_KERNEL; fs->devno = makedev(maj, min); + /* remove "(deleted)" suffix */ + sz = strlen(fs->target); + if (sz > PATH_DELETED_SUFFIX_SZ) { + char *p = fs->target + (sz - PATH_DELETED_SUFFIX_SZ); + + if (strcmp(p, PATH_DELETED_SUFFIX) == 0) + *p = '\0'; + } + unmangle_string(fs->root); unmangle_string(fs->target); unmangle_string(fs->vfs_optstr); @@ -192,7 +203,7 @@ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) src = NULL; } - /* merge VFS and FS options to the one string */ + /* merge VFS and FS options to one string */ fs->optstr = mnt_fs_strdup_options(fs); if (!fs->optstr) rc = -ENOMEM; @@ -323,8 +334,8 @@ static int mnt_parse_swaps_line(struct libmnt_fs *fs, char *s) /* * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*) * - * Note that we aren't trying to guess utab file format, because this file has - * to be always parsed by private libmount routines with explicitly defined + * Note that we aren't trying to guess the utab file format, because this file + * always has to be parsed by private libmount routines with an explicitly defined * format. * * mountinfo: "<number> <number> ... " @@ -344,10 +355,82 @@ static int guess_table_format(char *line) return MNT_FMT_FSTAB; /* fstab, mtab or /proc/mounts */ } +static int is_comment_line(char *line) +{ + char *p = skip_spaces(line); + + if (p && (*p == '#' || *p == '\n')) + return 1; + return 0; +} + +/* returns 1 if the last line in the @str is blank */ +static int is_terminated_by_blank(const char *str) +{ + size_t sz = str ? strlen(str) : 0; + const char *p = sz ? str + (sz - 1) : NULL; + + if (!sz || !p || *p != '\n') + return 0; /* empty or not terminated by '\n' */ + if (p == str) + return 1; /* only '\n' */ + p--; + while (p >= str && (*p == ' ' || *p == '\t')) + p--; + return *p == '\n' ? 1 : 0; +} + +/* + * Reads the next line from the file. + * + * Returns 0 if the line is a comment + * 1 if the line is not a comment + * <0 on error + */ +static int next_comment_line(char *buf, size_t bufsz, + FILE *f, char **last, int *nlines) +{ + if (fgets(buf, bufsz, f) == NULL) + return feof(f) ? 1 : -EINVAL; + + ++*nlines; + *last = strchr(buf, '\n'); + + return is_comment_line(buf) ? 0 : 1; +} + +static int append_comment(struct libmnt_table *tb, + struct libmnt_fs *fs, + const char *comm, + int eof) +{ + int rc, intro = mnt_table_get_nents(tb) == 0; + + if (intro && is_terminated_by_blank(mnt_table_get_intro_comment(tb))) + intro = 0; + + DBG(TAB, mnt_debug_h(tb, "appending %s comment", + intro ? "intro" : + eof ? "trailing" : "fs")); + if (intro) + rc = mnt_table_append_intro_comment(tb, comm); + else if (eof) { + rc = mnt_table_set_trailing_comment(tb, + mnt_fs_get_comment(fs)); + if (!rc) + rc = mnt_table_append_trailing_comment(tb, comm); + if (!rc) + rc = mnt_fs_set_comment(fs, NULL); + } else + rc = mnt_fs_append_comment(fs, comm); + return rc; +} + /* * Read and parse the next line from {fs,m}tab or mountinfo */ -static int mnt_table_parse_next(struct libmnt_table *tb, FILE *f, struct libmnt_fs *fs, +static int mnt_table_parse_next(struct libmnt_table *tb, FILE *f, + struct libmnt_fs *fs, const char *filename, int *nlines) { char buf[BUFSIZ]; @@ -366,7 +449,7 @@ next_line: ++*nlines; s = strchr (buf, '\n'); if (!s) { - /* Missing final newline? Otherwise extremely */ + /* Missing final newline? Otherwise an extremely */ /* long line - assume file was corrupted */ if (feof(f)) { DBG(TAB, mnt_debug_h(tb, @@ -379,6 +462,26 @@ next_line: goto err; } } + + /* comments parser */ + if (tb->comms + && (tb->fmt == MNT_FMT_GUESS || tb->fmt == MNT_FMT_FSTAB) + && is_comment_line(buf)) { + do { + rc = append_comment(tb, fs, buf, feof(f)); + if (!rc) + rc = next_comment_line(buf, + sizeof(buf), + f, &s, nlines); + } while (rc == 0); + + if (rc == 1 && feof(f)) + rc = append_comment(tb, fs, NULL, 1); + if (rc < 0) + return rc; + + } + *s = '\0'; if (--s >= buf && *s == '\r') *s = '\0'; @@ -420,7 +523,7 @@ err: tb->fmt == MNT_FMT_FSTAB ? "tab" : "utab")); /* by default all errors are recoverable, otherwise behavior depends on - * errcb() function. See mnt_table_set_parser_errcb(). + * the errcb() function. See mnt_table_set_parser_errcb(). */ return tb->errcb ? tb->errcb(tb, filename, *nlines) : 1; } @@ -461,7 +564,7 @@ static int kernel_fs_postparse(struct libmnt_table *tb, int rc = 0; const char *src = mnt_fs_get_srcpath(fs); - /* This is filesystem description from /proc, so we're in some process + /* This is a filesystem description from /proc, so we're in some process * namespace. Let's remember the process PID. */ if (filename && *tid == -1) @@ -538,8 +641,9 @@ int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filenam if (rc == 0 && tb->fmt == MNT_FMT_MOUNTINFO) rc = kernel_fs_postparse(tb, fs, &tid, filename); } + mnt_unref_fs(fs); + if (rc) { - mnt_free_fs(fs); if (rc == 1) continue; /* recoverable error */ if (feof(f)) @@ -561,10 +665,10 @@ err: * @tb: tab pointer * @filename: file * - * Parses whole table (e.g. /etc/mtab) and appends new records to the @tab. + * Parses the whole table (e.g. /etc/mtab) and appends new records to the @tab. * * The libmount parser ignores broken (syntax error) lines, these lines are - * reported to caller by errcb() function (see mnt_table_set_parser_errcb()). + * reported to the caller by the errcb() function (see mnt_table_set_parser_errcb()). * * Returns: 0 on success, negative number in case of error. */ @@ -663,7 +767,7 @@ static int __mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) if (n <= 0) return 0; - /* let use "at" functions rather than play crazy games with paths... */ + /* let's use "at" functions rather than playing crazy games with paths... */ dir = opendir(dirname); if (!dir) { r = -errno; @@ -704,7 +808,7 @@ out: * * The directory: * - files are sorted by strverscmp(3) - * - files that starts with "." are ignored (e.g. ".10foo.fstab") + * - files that start with "." are ignored (e.g. ".10foo.fstab") * - files without the ".fstab" extension are ignored * * Returns: 0 on success or negative number in case of error. @@ -728,7 +832,7 @@ struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt) if (tb) { tb->fmt = fmt; if (mnt_table_parse_file(tb, filename) != 0) { - mnt_free_table(tb); + mnt_unref_table(tb); tb = NULL; } } @@ -740,7 +844,7 @@ struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt) * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path * * Same as mnt_new_table() + mnt_table_parse_file(). Use this function for private - * files only. This function does not allow to use error callback, so you + * files only. This function does not allow using the error callback, so you * cannot provide any feedback to end-users about broken records in files (e.g. * fstab). * @@ -767,7 +871,7 @@ struct libmnt_table *mnt_new_table_from_dir(const char *dirname) return NULL; tb = mnt_new_table(); if (tb && mnt_table_parse_dir(tb, dirname) != 0) { - mnt_free_table(tb); + mnt_unref_table(tb); tb = NULL; } return tb; @@ -779,13 +883,13 @@ struct libmnt_table *mnt_new_table_from_dir(const char *dirname) * @cb: pointer to callback function * * The error callback function is called by table parser (mnt_table_parse_file()) - * in case of syntax error. The callback function could be used for errors + * in case of a syntax error. The callback function could be used for error * evaluation, libmount will continue/stop parsing according to callback return * codes: * * <0 : fatal error (abort parsing) - * 0 : success (parsing continue) - * >0 : recoverable error (the line is ignored, parsing continue). + * 0 : success (parsing continues) + * >0 : recoverable error (the line is ignored, parsing continues). * * Returns: 0 on success or negative number in case of error. */ @@ -800,7 +904,7 @@ int mnt_table_set_parser_errcb(struct libmnt_table *tb, } /* - * Filter out entries during tab file parsing. If @cb returns 1 then the entry + * Filter out entries during tab file parsing. If @cb returns 1, then the entry * is ignored. */ int mnt_table_set_parser_fltrcb(struct libmnt_table *tb, @@ -851,7 +955,7 @@ int mnt_table_parse_swaps(struct libmnt_table *tb, const char *filename) * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL * * This function parses /etc/fstab and appends new lines to the @tab. If the - * @filename is a directory then mnt_table_parse_dir() is called. + * @filename is a directory, then mnt_table_parse_dir() is called. * * See also mnt_table_set_parser_errcb(). * @@ -884,7 +988,7 @@ int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename) } /* - * This function uses @uf to found corresponding record in @tb, then the record + * This function uses @uf to find a corresponding record in @tb, then the record * from @tb is updated (user specific mount options are added). * * Note that @uf must contain only user specific mount options instead of @@ -986,7 +1090,7 @@ int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename) if (mnt_table_get_nents(tb) == 0) return 0; /* empty, ignore utab */ /* - * try to read user specific information from /run/mount/utabs + * try to read the user specific information from /run/mount/utabs */ utab = mnt_get_utab_path(); if (!utab || is_file_empty(utab)) @@ -1005,11 +1109,11 @@ int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename) mnt_reset_iter(&itr, MNT_ITER_BACKWARD); - /* merge user options into mountinfo from kernel */ + /* merge user options into mountinfo from the kernel */ while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0) mnt_table_merge_user_fs(tb, u_fs); } - mnt_free_table(u_tb); + mnt_unref_table(u_tb); return 0; } diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c index 1e7f32be0..13f6f6ebe 100644 --- a/libmount/src/tab_update.c +++ b/libmount/src/tab_update.c @@ -10,10 +10,10 @@ * @title: Tables update * @short_description: userspace mount information management * - * The struct libmnt_update provides abstraction to manage mount options in - * userspace independently on system configuration. This low-level API works on - * system with and without /etc/mtab. On systems without the regular /etc/mtab - * file are userspace mount options (e.g. user=) stored to the /run/mount/utab + * The struct libmnt_update provides an abstraction to manage mount options in + * userspace independently of system configuration. This low-level API works on + * systems both with and without /etc/mtab. On systems without the regular /etc/mtab + * file, the userspace mount options (e.g. user=) are stored in the /run/mount/utab * file. * * It's recommended to use high-level struct libmnt_context API. @@ -70,8 +70,8 @@ void mnt_free_update(struct libmnt_update *upd) DBG(UPDATE, mnt_debug_h(upd, "free")); - mnt_free_fs(upd->fs); - mnt_free_table(upd->mountinfo); + mnt_unref_fs(upd->fs); + mnt_unref_table(upd->mountinfo); free(upd->target); free(upd->filename); free(upd); @@ -125,7 +125,7 @@ int mnt_update_set_filename(struct libmnt_update *upd, const char *filename, * mnt_update_get_filename: * @upd: update * - * This function returns file name (e.g. /etc/mtab) for the up-dated file. + * This function returns the file name (e.g. /etc/mtab) of the up-dated file. * * Returns: pointer to filename that will be updated or NULL in case of error. */ @@ -140,7 +140,7 @@ const char *mnt_update_get_filename(struct libmnt_update *upd) * @upd: update handler * * Returns: 1 if entry described by @upd is successfully prepared and will be - * written to mtab/utab file. + * written to the mtab/utab file. */ int mnt_update_is_ready(struct libmnt_update *upd) { @@ -180,7 +180,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags, DBG(UPDATE, mnt_fs_print_debug(fs, stderr)); } - mnt_free_fs(upd->fs); + mnt_unref_fs(upd->fs); free(upd->target); upd->ready = FALSE; upd->fs = NULL; @@ -287,10 +287,10 @@ int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly) } /* - * Allocates utab entry (upd->fs) for mount/remount. This function should be + * Allocates an utab entry (upd->fs) for mount/remount. This function should be * called *before* mount(2) syscall. The @fs is used as a read-only template. * - * Returns: 0 on success, negative number on error, 1 if utabs update is + * Returns: 0 on success, negative number on error, 1 if utab's update is * unnecessary. */ static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, @@ -350,14 +350,14 @@ static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, return 0; err: free(u); - mnt_free_fs(upd->fs); + mnt_unref_fs(upd->fs); upd->fs = NULL; return rc; } /* * Sets fs-root and fs-type to @upd->fs according to the @fs template and - * @mountfalgs. For MS_BIND mountflag it reads information about source + * @mountfalgs. For MS_BIND mountflag it reads information about the source * filesystem from /proc/self/mountinfo. */ static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, @@ -408,13 +408,14 @@ err: */ static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs) { - const char *o, *src, *fstype; + const char *o, *src, *fstype, *comm; char *m1, *m2, *m3, *m4; int rc; assert(fs); assert(f); + comm = mnt_fs_get_comment(fs); src = mnt_fs_get_source(fs); fstype = mnt_fs_get_fstype(fs); o = mnt_fs_get_options(fs); @@ -425,6 +426,8 @@ static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs) m4 = o ? mangle(o) : "rw"; if (m1 && m2 && m3 && m4) { + if (comm) + fputs(comm, f); rc = fprintf(f, "%s %s %s %s %d %d\n", m1, m2, m3, m4, mnt_fs_get_freq(fs), @@ -527,6 +530,10 @@ static int update_table(struct libmnt_update *upd, struct libmnt_table *tb) struct libmnt_fs *fs; mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + if (tb->comms && mnt_table_get_intro_comment(tb)) + fputs(mnt_table_get_intro_comment(tb), f); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (upd->userspace_only) rc = fprintf_utab_fs(f, fs); @@ -538,6 +545,8 @@ static int update_table(struct libmnt_update *upd, struct libmnt_table *tb) goto leave; } } + if (tb->comms && mnt_table_get_trailing_comment(tb)) + fputs(mnt_table_get_trailing_comment(tb), f); if (fflush(f) != 0) { rc = -errno; @@ -570,6 +579,99 @@ leave: return rc; } +/** + * mnt_table_write_file + * @tb: parsed file (e.g. fstab) + * @file: target + * + * This function writes @tb to @file. + * + * Returns: 0 on success, negative number on error. + */ +int mnt_table_write_file(struct libmnt_table *tb, FILE *file) +{ + int rc = 0; + struct libmnt_iter itr; + struct libmnt_fs *fs; + + if (tb->comms && mnt_table_get_intro_comment(tb)) + fputs(mnt_table_get_intro_comment(tb), file); + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + rc = fprintf_mtab_fs(file, fs); + if (rc) + return rc; + } + if (tb->comms && mnt_table_get_trailing_comment(tb)) + fputs(mnt_table_get_trailing_comment(tb), file); + + if (fflush(file) != 0) + rc = -errno; + + DBG(TAB, mnt_debug_h(tb, "write file done [rc=%d]", rc)); + return rc; +} + +/** + * mnt_table_replace_file + * @tb: parsed file (e.g. fstab) + * @filename: target + * + * This function replaces @file by the new content from @tb. + * + * Returns: 0 on success, negative number on error. + */ +int mnt_table_replace_file(struct libmnt_table *tb, const char *filename) +{ + int fd, rc = 0; + FILE *f; + char *uq = NULL; + + DBG(TAB, mnt_debug_h(tb, "%s: replacing", filename)); + + fd = mnt_open_uniq_filename(filename, &uq); + if (fd < 0) + return fd; /* error */ + + f = fdopen(fd, "w" UL_CLOEXECSTR); + if (f) { + struct stat st; + + mnt_table_write_file(tb, f); + + if (fflush(f) != 0) { + rc = -errno; + DBG(UPDATE, mnt_debug("%s: fflush failed: %m", uq)); + goto leave; + } + + rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0; + + if (!rc && stat(filename, &st) == 0) + /* Copy uid/gid from the present file before renaming. */ + rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0; + + fclose(f); + f = NULL; + + if (!rc) + rc = rename(uq, filename) ? -errno : 0; + } else { + rc = -errno; + close(fd); + } + +leave: + if (f) + fclose(f); + unlink(uq); + free(uq); + + DBG(TAB, mnt_debug_h(tb, "replace done [rc=%d]", rc)); + return rc; +} + static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd) { struct libmnt_fs *fs; @@ -581,6 +683,8 @@ static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd) return -ENOMEM; mnt_table_add_fs(tb, fs); + mnt_unref_fs(fs); + return update_table(upd, tb); } @@ -606,7 +710,7 @@ static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc) if (lc) mnt_unlock_file(lc); - mnt_free_table(tb); + mnt_unref_table(tb); return rc; } @@ -632,13 +736,12 @@ static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc if (rem) { mnt_table_remove_fs(tb, rem); rc = update_table(upd, tb); - mnt_free_fs(rem); } } if (lc) mnt_unlock_file(lc); - mnt_free_table(tb); + mnt_unref_table(tb); return rc; } @@ -670,7 +773,7 @@ static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *l if (lc) mnt_unlock_file(lc); - mnt_free_table(tb); + mnt_unref_table(tb); return rc; } @@ -712,7 +815,7 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock * if (lc) mnt_unlock_file(lc); - mnt_free_table(tb); + mnt_unref_table(tb); return rc; } @@ -724,7 +827,7 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock * * High-level API to update /etc/mtab (or private /run/mount/utab file). * * The @lc lock is optional and will be created if necessary. Note that - * the automatically created lock blocks all signals. + * an automatically created lock blocks all signals. * * See also mnt_lock_block_signals() and mnt_context_get_lock(). * @@ -794,7 +897,7 @@ static int update(const char *target, struct libmnt_fs *fs, unsigned long mountf goto done; } - /* [... here should be mount(2) call ...] */ + /* [... mount(2) call should be here...] */ rc = mnt_update_table(upd, NULL); done: @@ -815,7 +918,7 @@ static int test_add(struct libmnt_test *ts, int argc, char *argv[]) mnt_fs_set_options(fs, argv[4]); rc = update(NULL, fs, 0); - mnt_free_fs(fs); + mnt_unref_fs(fs); return rc; } @@ -838,7 +941,7 @@ static int test_move(struct libmnt_test *ts, int argc, char *argv[]) rc = update(NULL, fs, MS_MOVE); - mnt_free_fs(fs); + mnt_unref_fs(fs); return rc; } @@ -853,17 +956,42 @@ static int test_remount(struct libmnt_test *ts, int argc, char *argv[]) mnt_fs_set_options(fs, argv[2]); rc = update(NULL, fs, MS_REMOUNT); - mnt_free_fs(fs); + mnt_unref_fs(fs); + return rc; +} + +static int test_replace(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_fs *fs = mnt_new_fs(); + struct libmnt_table *tb = mnt_new_table(); + int rc; + + if (argc < 3) + return -1; + + mnt_table_enable_comments(tb, TRUE); + mnt_table_parse_fstab(tb, NULL); + + mnt_fs_set_source(fs, argv[1]); + mnt_fs_set_target(fs, argv[2]); + mnt_fs_append_comment(fs, "# this is new filesystem\n"); + + mnt_table_add_fs(tb, fs); + mnt_unref_fs(fs); + + rc = mnt_table_replace_file(tb, mnt_get_fstab_path()); + mnt_unref_table(tb); return rc; } int main(int argc, char *argv[]) { struct libmnt_test tss[] = { - { "--add", test_add, "<src> <target> <type> <options> add line to mtab" }, + { "--add", test_add, "<src> <target> <type> <options> add a line to mtab" }, { "--remove", test_remove, "<target> MS_REMOUNT mtab change" }, { "--move", test_move, "<old_target> <target> MS_MOVE mtab change" }, { "--remount",test_remount, "<target> <options> MS_REMOUNT mtab change" }, + { "--replace",test_replace, "<src> <target> Add a line to LIBMOUNT_FSTAB and replace the original file" }, { NULL } }; diff --git a/libmount/src/utils.c b/libmount/src/utils.c index c328414f2..4111b595b 100644 --- a/libmount/src/utils.c +++ b/libmount/src/utils.c @@ -14,6 +14,7 @@ #include <fcntl.h> #include <pwd.h> #include <grp.h> +#include <blkid.h> #include "strutils.h" #include "pathnames.h" @@ -23,39 +24,33 @@ #include "env.h" #include "match.h" -int endswith(const char *s, const char *sx) +int append_string(char **a, const char *b) { - ssize_t off; + size_t al, bl; + char *tmp; - assert(s); - assert(sx); + assert(a); - off = strlen(s); - if (!off) + if (!b || !*b) return 0; - off -= strlen(sx); - if (off < 0) - return 0; - - return !strcmp(s + off, sx); -} - -int startswith(const char *s, const char *sx) -{ - size_t off; - - assert(s); - assert(sx); + if (!*a) { + *a = strdup(b); + return !*a ? -ENOMEM : 0; + } - off = strlen(sx); - if (!off) - return 0; + al = strlen(*a); + bl = strlen(b); - return !strncmp(s, sx, off); + tmp = realloc(*a, al + bl + 1); + if (!tmp) + return -ENOMEM; + *a = tmp; + memcpy((*a) + al, b, bl + 1); + return 0; } /* - * Return 1 if the file does not accessible of empty + * Return 1 if the file is not accessible or empty */ int is_file_empty(const char *name) { @@ -65,6 +60,34 @@ int is_file_empty(const char *name) return (stat(name, &st) != 0 || st.st_size == 0); } +int mnt_valid_tagname(const char *tagname) +{ + if (tagname && *tagname && ( + strcmp("UUID", tagname) == 0 || + strcmp("LABEL", tagname) == 0 || + strcmp("PARTUUID", tagname) == 0 || + strcmp("PARTLABEL", tagname) == 0)) + return 1; + + return 0; +} + +/** + * mnt_tag_is_valid: + * @tag: NAME=value string + * + * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0. + */ +int mnt_tag_is_valid(const char *tag) +{ + char *t = NULL; + int rc = tag && blkid_parse_tag_string(tag, &t, NULL) == 0 + && mnt_valid_tagname(t); + + free(t); + return rc; +} + int mnt_parse_offset(const char *str, size_t len, uintmax_t *res) { char *p; @@ -105,8 +128,8 @@ char *stripoff_last_component(char *path) } /* - * Note that the @target has to be absolute path (so at least "/"). The - * @filename returns allocated buffer with last path component, for example: + * Note that the @target has to be an absolute path (so at least "/"). The + * @filename returns an allocated buffer with the last path component, for example: * * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test" */ @@ -159,8 +182,9 @@ int mnt_chdir_to_parent(const char *target, char **filename) if (!last || !*last) memcpy(*filename, ".", 2); else - memcpy(*filename, last, strlen(last) + 1); - } + memmove(*filename, last, strlen(last) + 1); + } else + free(buf); return 0; err: free(buf); @@ -168,7 +192,7 @@ err: } /* - * Check if @path is on read-only filesystem independently on file permissions. + * Check if @path is on a read-only filesystem independently of file permissions. */ int mnt_is_readonly(const char *path) { @@ -187,7 +211,7 @@ int mnt_is_readonly(const char *path) * accessible for the current rUID. (Note that euidaccess(2) does not * check for EROFS at all). * - * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind) + * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind) */ { struct timespec times[2]; @@ -208,7 +232,7 @@ int mnt_is_readonly(const char *path) * * Encode @str to be compatible with fstab/mtab * - * Returns: new allocated string or NULL in case of error. + * Returns: newly allocated string or NULL in case of error. */ char *mnt_mangle(const char *str) { @@ -221,7 +245,7 @@ char *mnt_mangle(const char *str) * * Decode @str from fstab/mtab * - * Returns: new allocated string or NULL in case of error. + * Returns: newly allocated string or NULL in case of error. */ char *mnt_unmangle(const char *str) { @@ -250,8 +274,9 @@ int mnt_fstype_is_pseudofs(const char *type) "devpts", "devtmpfs", "dlmfs", - "fuse.gvfs-fuse-daemon", + "efivarfs", "fusectl", + "fuse.gvfs-fuse-daemon", "hugetlbfs", "mqueue", "nfsd", @@ -300,7 +325,7 @@ int mnt_fstype_is_netfs(const char *type) * @type: filesystem type * @pattern: filesystem name or comma delimited list of names * - * The @pattern list of filesystem can be prefixed with a global + * The @pattern list of filesystems can be prefixed with a global * "no" prefix to invert matching of the whole list. The "no" could * also be used for individual items in the @pattern list. So, * "nofoo,bar" has the same meaning as "nofoo,nobar". @@ -358,14 +383,14 @@ static int check_option(const char *haystack, size_t len, * @optstr: options string * @pattern: comma delimited list of options * - * The "no" could used for individual items in the @options list. The "no" + * The "no" could be used for individual items in the @options list. The "no" * prefix does not have a global meaning. * * Unlike fs type matching, nonetdev,user and nonetdev,nouser have * DIFFERENT meanings; each option is matched explicitly as specified. * - * The "no" prefix interpretation could be disable by "+" prefix, for example - * "+noauto" matches if @optstr literally contains "noauto" string. + * The "no" prefix interpretation could be disabled by the "+" prefix, for example + * "+noauto" matches if @optstr literally contains the "noauto" string. * * "xxx,yyy,zzz" : "nozzz" -> False * @@ -495,7 +520,7 @@ static int get_filesystems(const char *filename, char ***filesystems, const char } /* - * Always check @filesystems pointer! + * Always check the @filesystems pointer! * * man mount: * @@ -520,7 +545,7 @@ int mnt_get_filesystems(char ***filesystems, const char *pattern) rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern); if (rc == 1 && *filesystems) - rc = 0; /* not found /proc/filesystems */ + rc = 0; /* /proc/filesystems not found */ return rc; } @@ -536,7 +561,7 @@ static size_t get_pw_record_size(void) } /* - * Returns allocated string with username or NULL. + * Returns an allocated string with username or NULL. */ char *mnt_get_username(const uid_t uid) { @@ -662,8 +687,8 @@ static int try_write(const char *filename) * @mtab: returns path to mtab * @writable: returns 1 if the file is writable * - * If the file does not exist and @writable argument is not NULL then it will - * try to create the file + * If the file does not exist and @writable argument is not NULL, then it will + * try to create the file. * * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check * errno for more details). @@ -684,7 +709,7 @@ int mnt_has_regular_mtab(const char **mtab, int *writable) rc = lstat(filename, &st); if (rc == 0) { - /* file exist */ + /* file exists */ if (S_ISREG(st.st_mode)) { if (writable) *writable = !try_write(filename); @@ -708,7 +733,7 @@ done: /* * Don't export this to libmount API -- utab is private library stuff. * - * If the file does not exist and @writable argument is not NULL then it will + * If the file does not exist and @writable argument is not NULL, then it will * try to create the directory (e.g. /run/mount) and the file. * * Returns: 1 if utab is a regular file, and 0 in case of @@ -730,13 +755,13 @@ int mnt_has_regular_utab(const char **utab, int *writable) rc = lstat(filename, &st); if (rc == 0) { - /* file exist */ + /* file exists */ if (S_ISREG(st.st_mode)) { if (writable) *writable = !try_write(filename); return 1; } - goto done; /* it's not regular file */ + goto done; /* it's not a regular file */ } if (writable) { @@ -788,7 +813,7 @@ const char *mnt_get_fstab_path(void) /** * mnt_get_mtab_path: * - * This function returns *default* location of the mtab file. The result does + * This function returns the *default* location of the mtab file. The result does * not have to be writable. See also mnt_has_regular_mtab(). * * Returns: path to /etc/mtab or $LIBMOUNT_MTAB. @@ -819,7 +844,7 @@ const char *mnt_get_utab_path(void) } -/* returns file descriptor or -errno, @name returns uniques filename +/* returns file descriptor or -errno, @name returns a unique filename */ int mnt_open_uniq_filename(const char *filename, char **name) { @@ -836,12 +861,14 @@ int mnt_open_uniq_filename(const char *filename, char **name) if (rc <= 0) return -errno; - /* This is for very old glibc and for compatibility with Posix where is + /* This is for very old glibc and for compatibility with Posix, which says * nothing about mkstemp() mode. All sane glibc use secure mode (0600). */ oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP| S_IROTH|S_IWOTH|S_IXOTH); fd = mkostemp(n, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC); + if (fd < 0) + fd = -errno; umask(oldmode); if (fd >= 0 && name) @@ -849,7 +876,7 @@ int mnt_open_uniq_filename(const char *filename, char **name) else free(n); - return fd < 0 ? -errno : fd; + return fd; } /** @@ -859,7 +886,7 @@ int mnt_open_uniq_filename(const char *filename, char **name) * This function finds the mountpoint that a given path resides in. @path * should be canonicalized. The returned pointer should be freed by the caller. * - * Returns: allocated string with target of the mounted device or NULL on error + * Returns: allocated string with the target of the mounted device or NULL on error */ char *mnt_get_mountpoint(const char *path) { @@ -929,7 +956,7 @@ char *mnt_get_fs_root(const char *path, const char *mnt) /* * Search for @name kernel command parametr. * - * Returns newly allocated string with parameter argument if the @name is + * Returns newly allocated string with a parameter argument if the @name is * specified as "name=" or returns pointer to @name or returns NULL if not * found. * @@ -979,7 +1006,7 @@ char *mnt_get_kernel_cmdline_option(const char *name) if (p != buf && !isblank(*(p - 1))) continue; /* no space before the option */ if (!val && *(p + len) != '\0' && !isblank(*(p + len))) - continue; /* no space behind the option */ + continue; /* no space after the option */ if (val) { char *v = p + len; @@ -1070,6 +1097,18 @@ int test_endswith(struct libmnt_test *ts, int argc, char *argv[]) return 0; } +int test_appendstr(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *str = strdup(argv[1]); + const char *ap = argv[2]; + + append_string(&str, ap); + printf("new string: '%s'\n", str); + + free(str); + return 0; +} + int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) { char *path = canonicalize_path(argv[1]), @@ -1165,6 +1204,7 @@ int main(int argc, char *argv[]) { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" }, { "--starts-with", test_startswith, "<string> <prefix>" }, { "--ends-with", test_endswith, "<string> <prefix>" }, + { "--append-string", test_appendstr, "<string> <appendix>" }, { "--mountpoint", test_mountpoint, "<path>" }, { "--fs-root", test_fsroot, "<path>" }, { "--cd-parent", test_chdir, "<path>" }, diff --git a/libmount/src/version.c b/libmount/src/version.c index 00b7c03e7..b72956230 100644 --- a/libmount/src/version.c +++ b/libmount/src/version.c @@ -10,7 +10,7 @@ /** * SECTION: version * @title: Version functions - * @short_description: functions to get library version. + * @short_description: functions to get the library version. */ #include <ctype.h> @@ -70,11 +70,11 @@ int mnt_get_library_version(const char **ver_string) /** * mnt_get_library_features: - * @features: returns pointer to the static array of strings, the array is + * @features: returns a pointer to the static array of strings, the array is * terminated by NULL. * * Returns: number of items in the features array not including the last NULL, - * or less then zero in case of error + * or less than zero in case of error * * Example: * <informalexample> |
