(root)/
util-linux-2.39/
libmount/
python/
tab.c
       1  /*
       2   * Python bindings for the libmount library.
       3   *
       4   * Copyright (C) 2013, Red Hat, Inc. All rights reserved.
       5   * Written by Ondrej Oprala and Karel Zak
       6   *
       7   * This file is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 3 of the License, or (at your option) any later version.
      11   *
      12   * This file is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General Public
      18   * License along with this file; if not, write to the Free Software
      19   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20   */
      21  #include "pylibmount.h"
      22  
      23  static PyMemberDef Table_members[] = {
      24  	{ NULL }
      25  };
      26  
      27  static int Table_set_parser_errcb(TableObject *self, PyObject *func,
      28  				  void *closure __attribute__((unused)))
      29  {
      30  	PyObject *tmp;
      31  
      32  	if (!func) {
      33  		PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
      34  		return -1;
      35  	}
      36  
      37  	if (!PyCallable_Check(func))
      38  		return -1;
      39  
      40  	tmp = self->errcb;
      41  	Py_INCREF(func);
      42  	self->errcb = func;
      43  	Py_XDECREF(tmp);
      44  	return 0;
      45  }
      46  
      47  static PyObject *Table_get_intro_comment(TableObject *self,
      48  				void *closure __attribute__((unused)))
      49  {
      50  	return PyObjectResultStr(mnt_table_get_intro_comment(self->tab));
      51  }
      52  
      53  static int Table_set_intro_comment(TableObject *self, PyObject *value,
      54  				void *closure __attribute__((unused)))
      55  {
      56  	char *comment = NULL;
      57  	int rc = 0;
      58  
      59  	if (!value) {
      60  		PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
      61  		return -1;
      62  	}
      63  	if (!(comment = pystos(value)))
      64  		return -1;
      65  
      66  	if ((rc = mnt_table_set_intro_comment(self->tab, comment))) {
      67  		UL_RaiseExc(-rc);
      68  		return -1;
      69  	}
      70  	return 0;
      71  }
      72  
      73  static PyObject *Table_get_trailing_comment(TableObject *self,
      74  				void *closure __attribute__((unused)))
      75  {
      76  	return PyObjectResultStr(mnt_table_get_trailing_comment(self->tab));
      77  }
      78  
      79  static int Table_set_trailing_comment(TableObject *self, PyObject *value,
      80  				void *closure __attribute__((unused)))
      81  {
      82  	char *comment = NULL;
      83  	int rc = 0;
      84  
      85  	if (!value) {
      86  		PyErr_SetString(PyExc_TypeError, NODEL_ATTR);
      87  		return -1;
      88  	}
      89  	if (!(comment = pystos(value)))
      90  		return -1;
      91  
      92  	if ((rc = mnt_table_set_trailing_comment(self->tab, comment))) {
      93  		UL_RaiseExc(-rc);
      94  		return -1;
      95  	}
      96  	return 0;
      97  }
      98  
      99  #define Table_enable_comments_HELP "enable_comments(enable)\n\n" \
     100  	"Enables parsing of comments.\n\n" \
     101  	"The initial (intro) file comment is accessible by\n" \
     102  	"Tab.intro_comment. The intro and the comment of the first fstab" \
     103  	"entry has to be separated by blank line.  The filesystem comments are\n" \
     104  	"accessible by Fs.comment. The tailing fstab comment is accessible\n" \
     105  	"by Tab.trailing_comment.\n" \
     106  	"\n" \
     107  	"<informalexample>\n" \
     108  	"<programlisting>\n" \
     109  	"#\n" \
     110  	"# Intro comment\n" \
     111  	"#\n" \
     112  	"\n" \
     113  	"# this comments belongs to the first fs\n" \
     114  	"LABEL=foo /mnt/foo auto defaults 1 2\n" \
     115  	"# this comments belongs to the second fs\n" \
     116  	"LABEL=bar /mnt/bar auto defaults 1 2 \n" \
     117  	"# tailing comment\n" \
     118  	"</programlisting>\n" \
     119  	"</informalexample>"
     120  static PyObject *Table_enable_comments(TableObject *self, PyObject *args,
     121  					PyObject *kwds)
     122  {
     123  	int enable = 0;
     124  	char *kwlist[] = {"enable", NULL};
     125  
     126  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &enable)) {
     127  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     128  		return NULL;
     129  	}
     130  	mnt_table_enable_comments(self->tab, enable);
     131  	Py_INCREF(self);
     132  	return (PyObject *)self;
     133  }
     134  
     135  #define Table_replace_file_HELP "replace_file(filename)\n\n" \
     136  		"This function replaces filename with the new content from TableObject."
     137  static PyObject *Table_replace_file(TableObject *self, PyObject *args, PyObject *kwds)
     138  {
     139  	int rc;
     140  	char *filename = NULL;
     141  	char *kwlist[] = {"filename", NULL};
     142  
     143  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &filename)) {
     144  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     145  		return NULL;
     146  	}
     147  	rc = mnt_table_replace_file(self->tab, filename);
     148  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     149  }
     150  
     151  #define Table_write_file_HELP "write_file(path)\n\n" \
     152  		"This function writes tab to file(stream)"
     153  static PyObject *Table_write_file(TableObject *self, PyObject *args, PyObject *kwds)
     154  {
     155  	int rc;
     156  	//PyObject *stream = NULL;
     157  	FILE *f = NULL;
     158  	char *path = NULL;
     159  	char *kwlist[] = {"path", NULL};
     160  
     161  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
     162  					&path)) {
     163  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     164  		return NULL;
     165  	}
     166  	if (!(f = fopen(path, "w")))
     167  		return UL_RaiseExc(errno);
     168  	rc = mnt_table_write_file(self->tab, f);
     169  	fclose(f);
     170  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     171  }
     172  
     173  #define Table_find_devno_HELP "find_devno(devno, [direction])\n\n" \
     174  		"Note that zero could be valid device number for root pseudo " \
     175  		"filesystem (e.g. tmpfs)\n" \
     176  		"Returns a tab entry or None"
     177  static PyObject *Table_find_devno(TableObject *self, PyObject *args, PyObject *kwds)
     178  {
     179  	dev_t devno;
     180  	int direction = MNT_ITER_BACKWARD;
     181  	char *kwlist[] = {"devno", "direction", NULL};
     182  
     183  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "I|i", kwlist, &devno, &direction)) {
     184  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     185  		return NULL;
     186  	}
     187  	return PyObjectResultFs(mnt_table_find_devno(self->tab, devno, direction));
     188  }
     189  
     190  #define Table_find_mountpoint_HELP "find_mountpoint(path, [direction])\n\n" \
     191  		"Returns a tab entry or None."
     192  static PyObject *Table_find_mountpoint(TableObject *self, PyObject *args, PyObject *kwds)
     193  {
     194  	char *path;
     195  	int direction = MNT_ITER_BACKWARD;
     196  	char *kwlist[] = {"path", "direction", NULL};
     197  
     198  	if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &path, &direction)) {
     199  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     200  		return NULL;
     201  	}
     202  	return PyObjectResultFs(mnt_table_find_mountpoint(self->tab, path, direction));
     203  }
     204  
     205  #define Table_find_pair_HELP "find_pair(source, target, [direction])\n\n" \
     206  		"Returns a tab entry or None."
     207  static PyObject *Table_find_pair(TableObject *self, PyObject *args, PyObject *kwds)
     208  {
     209  	char *kwlist[] = {"source", "target", "direction", NULL};
     210  	char *source;
     211  	char *target;
     212  	int direction = MNT_ITER_BACKWARD;
     213  
     214  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &source, &target, &direction)) {
     215  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     216  		return NULL;
     217  	}
     218  	return PyObjectResultFs(mnt_table_find_pair(self->tab, source, target, direction));
     219  }
     220  
     221  #define Table_find_source_HELP "find_source(source, [direction])\n\n" \
     222  		"Returns a tab entry or None."
     223  static PyObject *Table_find_source(TableObject *self, PyObject *args, PyObject *kwds)
     224  {
     225  	char *kwlist[] = {"source", "direction", NULL};
     226  	char *source;
     227  	int direction = MNT_ITER_BACKWARD;
     228  
     229  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &source, &direction)) {
     230  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     231  		return NULL;
     232  	}
     233  	return PyObjectResultFs(mnt_table_find_source(self->tab, source, direction));
     234  }
     235  
     236  #define Table_find_target_HELP "find_target(target, [direction])\n\n" \
     237  		"Try to lookup an entry in given tab, possible are three iterations, first\n" \
     238  		"with path, second with realpath(path) and third with realpath(path)\n" \
     239  		"against realpath(fs->target). The 2nd and 3rd iterations are not performed\n" \
     240  		"when tb cache is not set (cache not implemented yet).\n" \
     241  		"\n" \
     242  		"Returns a tab entry or None."
     243  static PyObject *Table_find_target(TableObject *self, PyObject *args, PyObject *kwds)
     244  {
     245  	char *kwlist[] = {"target", "direction", NULL};
     246  	char *target;
     247  	int direction = MNT_ITER_BACKWARD;
     248  
     249  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &target, &direction)) {
     250  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     251  		return NULL;
     252  	}
     253  	return PyObjectResultFs(mnt_table_find_target(self->tab, target, direction));
     254  }
     255  
     256  #define Table_find_srcpath_HELP "find_srcpath(srcpath, [direction])\n\n" \
     257  		"Try to lookup an entry in given tab, possible are four iterations, first\n" \
     258  		"with path, second with realpath(path), third with tags (LABEL, UUID, ..)\n" \
     259  		"from path and fourth with realpath(path) against realpath(entry->srcpath).\n" \
     260  		"\n" \
     261  		"The 2nd, 3rd and 4th iterations are not performed when tb cache is not\n" \
     262  		"set (not implemented yet).\n" \
     263  		"\n" \
     264  		"Note that None is a valid source path; it will be replaced with \"none\". The\n" \
     265  		"\"none\" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.\n" \
     266  		"\n" \
     267  		"Returns a tab entry or None."
     268  static PyObject *Table_find_srcpath(TableObject *self, PyObject *args, PyObject *kwds)
     269  {
     270  	char *kwlist[] = {"srcpath", "direction", NULL};
     271  	char *srcpath;
     272  	int direction = MNT_ITER_BACKWARD;
     273  
     274  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &srcpath, &direction)) {
     275  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     276  		return NULL;
     277  	}
     278  	return PyObjectResultFs(mnt_table_find_srcpath(self->tab, srcpath, direction));
     279  }
     280  
     281  #define Table_find_tag_HELP "find_tag(tag, val, [direction])\n\n" \
     282  		"Try to lookup an entry in given tab, first attempt is lookup by tag and\n" \
     283  		"val, for the second attempt the tag is evaluated (converted to the device\n" \
     284  		"name) and Tab.find_srcpath() is performed. The second attempt is not\n" \
     285  		"performed when tb cache is not set (not implemented yet).\n" \
     286  		"\n" \
     287  		"Returns a tab entry or NULL."
     288  static PyObject *Table_find_tag(TableObject *self, PyObject *args, PyObject *kwds)
     289  {
     290  	char *kwlist[] = {"tag", "val", "direction", NULL};
     291  	char *tag;
     292  	char *val;
     293  	int direction = MNT_ITER_BACKWARD;
     294  
     295  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &tag, &val, &direction)) {
     296  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     297  		return NULL;
     298  	}
     299  	return PyObjectResultFs(mnt_table_find_tag(self->tab, tag, val, direction));
     300  }
     301  
     302  static PyObject *Table_get_nents(TableObject *self)
     303  {
     304  	return PyObjectResultInt(mnt_table_get_nents(self->tab));
     305  }
     306  
     307  #define Table_is_fs_mounted_HELP "is_fs_mounted(fstab_fs)\n\n" \
     308  		"Checks if the fstab_fs entry is already in the tb table. The \"swap\" is\n" \
     309  		"ignored. This function explicitly compares source, target and root of the\n" \
     310  		"filesystems.\n" \
     311  		"\n" \
     312  		"Note that source and target are canonicalized only if a cache for tb is\n" \
     313  		"defined (not implemented yet). The target canonicalization may\n" \
     314  		"trigger automount on autofs mountpoints!\n" \
     315  		"\n" \
     316  		"Don't use it if you want to know if a device is mounted, just use\n" \
     317  		"Tab.find_source() for the device.\n" \
     318  		"\n" \
     319  		"This function is designed mostly for \"mount -a\".\n" \
     320  		"\n" \
     321  		"Returns a boolean value."
     322  static PyObject *Table_is_fs_mounted(TableObject *self, PyObject *args, PyObject *kwds)
     323  {
     324  	FsObject *fs;
     325  	char *kwlist[] = {"fstab_fs", NULL};
     326  
     327  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &FsType, &fs)) {
     328  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     329  		return NULL;
     330  	}
     331  	return PyBool_FromLong(mnt_table_is_fs_mounted(self->tab, fs->fs));
     332  }
     333  
     334  #define Table_parse_file_HELP "parse_file(file)\n\n" \
     335  		"Parses whole table (e.g. /etc/mtab) and appends new records to the tab.\n" \
     336  		"\n" \
     337  		"The libmount parser ignores broken (syntax error) lines, these lines are\n" \
     338  		"reported to caller by errcb() function (see Tab.parser_errcb).\n" \
     339  		"\n" \
     340  		"Returns self or raises an exception in case of an error."
     341  static PyObject *Table_parse_file(TableObject *self, PyObject* args, PyObject *kwds)
     342  {
     343  	int rc;
     344  	char *file = NULL;
     345  	char *kwlist[] = {"file", NULL};
     346  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &file)) {
     347  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     348  		return NULL;
     349  	}
     350  	rc = mnt_table_parse_file(self->tab, file);
     351  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     352  }
     353  
     354  #define Table_parse_fstab_HELP "parse_fstab([fstab])\n\n" \
     355  		"This function parses /etc/fstab and appends new lines to the tab. If the\n" \
     356  		"filename is a directory then Tab.parse_dir() is called.\n" \
     357  		"\n" \
     358  		"See also Tab.parser_errcb.\n" \
     359  		"\n" \
     360  		"Returns self or raises an exception in case of an error."
     361  static PyObject *Table_parse_fstab(TableObject *self, PyObject* args, PyObject *kwds)
     362  {
     363  	int rc;
     364  	char *fstab = NULL;
     365  	char *kwlist[] = {"fstab", NULL};
     366  
     367  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &fstab)) {
     368  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     369  		return NULL;
     370  	}
     371  	rc = mnt_table_parse_fstab(self->tab, fstab);
     372  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     373  }
     374  
     375  #define Table_parse_mtab_HELP "parse_mtab([mtab])\n\n" \
     376  		"This function parses /etc/mtab or /proc/self/mountinfo\n" \
     377  		"/run/mount/utabs or /proc/mounts.\n" \
     378  		"\n" \
     379  		"See also Tab.parser_errcb().\n" \
     380  		"\n" \
     381  		"Returns self or raises an exception in case of an error."
     382  static PyObject *Table_parse_mtab(TableObject *self, PyObject* args, PyObject *kwds)
     383  {
     384  	int rc;
     385  	char *mtab = NULL;
     386  	char *kwlist[] = {"mtab", NULL};
     387  
     388  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &mtab)) {
     389  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     390  		return NULL;
     391  	}
     392  	rc = mnt_table_parse_mtab(self->tab, mtab);
     393  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     394  }
     395  
     396  #define Table_parse_dir_HELP "parse_dir(dir)\n\n" \
     397  		"The directory:\n" \
     398  		"- files are sorted by strverscmp(3)\n" \
     399  		"- files that start with \".\" are ignored (e.g. \".10foo.fstab\")\n" \
     400  		"- files without the \".fstab\" extension are ignored\n" \
     401  		"\n" \
     402  		"Returns self or raises an exception in case of an error."
     403  static PyObject *Table_parse_dir(TableObject *self, PyObject* args, PyObject *kwds)
     404  {
     405  	int rc;
     406  	char *dir = NULL;
     407  	char *kwlist[] = {"dir", NULL};
     408  
     409  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &dir)) {
     410  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     411  		return NULL;
     412  	}
     413  	rc = mnt_table_parse_dir(self->tab, dir);
     414  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     415  }
     416  
     417  #define Table_parse_swaps_HELP "parse_swaps(swaps)\n\n" \
     418  		"This function parses /proc/swaps and appends new lines to the tab"
     419  static PyObject *Table_parse_swaps(TableObject *self, PyObject* args, PyObject *kwds)
     420  {
     421  	int rc;
     422  	char *swaps = NULL;
     423  	char *kwlist[] = {"swaps", NULL};
     424  
     425  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &swaps)) {
     426  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     427  		return NULL;
     428  	}
     429  	rc = mnt_table_parse_swaps(self->tab, swaps);
     430  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     431  }
     432  
     433  #define Table_add_fs_HELP "add_fs(fs)\n\nAdds a new entry to tab.\n" \
     434  		"Returns self or raises an exception in case of an error."
     435  
     436  static PyObject *Table_add_fs(TableObject *self, PyObject* args, PyObject *kwds)
     437  {
     438  	int rc;
     439  	FsObject *fs = NULL;
     440  	char *kwlist[] = {"fs", NULL};
     441  
     442  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &FsType, &fs)) {
     443  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     444  		return NULL;
     445  	}
     446  	Py_INCREF(fs);
     447  	rc = mnt_table_add_fs(self->tab, fs->fs);
     448  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     449  }
     450  
     451  #define Table_remove_fs_HELP "remove_fs(fs)\n\n" \
     452  		"Returns self or raises an exception in case of an error."
     453  static PyObject *Table_remove_fs(TableObject *self, PyObject* args, PyObject *kwds)
     454  {
     455  	int rc;
     456  	FsObject *fs = NULL;
     457  	char *kwlist[] = {"fs", NULL};
     458  
     459  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &FsType, &fs)) {
     460  		PyErr_SetString(PyExc_TypeError, ARG_ERR);
     461  		return NULL;
     462  	}
     463  	rc = mnt_table_remove_fs(self->tab, fs->fs);
     464  	Py_DECREF(fs);
     465  	return rc ? UL_RaiseExc(-rc) : UL_IncRef(self);
     466  }
     467  
     468  #define Table_next_fs_HELP "next_fs()\n\n" \
     469  		"Returns the next Fs on success, raises an exception in case " \
     470  		"of an error and None at end of list.\n" \
     471  		"\n" \
     472  		"Example:\n" \
     473  		"<informalexample>\n" \
     474  		"<programlisting>\n" \
     475  		"import libmount\n" \
     476  		"import functools\n" \
     477  		"for fs in iter(functools.partial(tb.next_fs), None):\n" \
     478  		"    dir = Fs.target\n" \
     479  		"    print \"mount point: {:s}\".format(dir)\n" \
     480  		"\n" \
     481  		"</programlisting>\n" \
     482  		"</informalexample>\n" \
     483  		"\n" \
     484  		"lists all mountpoints from fstab in backward order."
     485  static PyObject *Table_next_fs(TableObject *self)
     486  {
     487  	struct libmnt_fs *fs;
     488  	int rc;
     489  
     490  	/* Reset the builtin iterator after reaching the end of the list */
     491  	rc = mnt_table_next_fs(self->tab, self->iter, &fs);
     492  	if (rc == 1) {
     493  		mnt_reset_iter(self->iter, MNT_ITER_FORWARD);
     494  		Py_RETURN_NONE;
     495  	}
     496  
     497  	if (rc)
     498  		return UL_RaiseExc(-rc);
     499  
     500  	return PyObjectResultFs(fs);
     501  }
     502  
     503  static PyMethodDef Table_methods[] = {
     504  	{"enable_comments", (PyCFunction)Table_enable_comments, METH_VARARGS|METH_KEYWORDS, Table_enable_comments_HELP},
     505  	{"find_pair", (PyCFunction)Table_find_pair, METH_VARARGS|METH_KEYWORDS, Table_find_pair_HELP},
     506  	{"find_source", (PyCFunction)Table_find_source, METH_VARARGS|METH_KEYWORDS, Table_find_source_HELP},
     507  	{"find_srcpath", (PyCFunction)Table_find_srcpath, METH_VARARGS|METH_KEYWORDS, Table_find_srcpath_HELP},
     508  	{"find_tag", (PyCFunction)Table_find_tag, METH_VARARGS|METH_KEYWORDS, Table_find_tag_HELP},
     509  	{"find_target", (PyCFunction)Table_find_target, METH_VARARGS|METH_KEYWORDS, Table_find_target_HELP},
     510  	{"find_devno", (PyCFunction)Table_find_devno, METH_VARARGS|METH_KEYWORDS, Table_find_devno_HELP},
     511  	{"find_mountpoint", (PyCFunction)Table_find_mountpoint, METH_VARARGS|METH_KEYWORDS, Table_find_mountpoint_HELP},
     512  	{"parse_file", (PyCFunction)Table_parse_file, METH_VARARGS|METH_KEYWORDS, Table_parse_file_HELP},
     513  	{"parse_fstab", (PyCFunction)Table_parse_fstab, METH_VARARGS|METH_KEYWORDS, Table_parse_fstab_HELP},
     514  	{"parse_mtab", (PyCFunction)Table_parse_mtab, METH_VARARGS|METH_KEYWORDS, Table_parse_mtab_HELP},
     515  	{"parse_dir", (PyCFunction)Table_parse_dir, METH_VARARGS|METH_KEYWORDS, Table_parse_dir_HELP},
     516  	{"parse_swaps", (PyCFunction)Table_parse_swaps, METH_VARARGS|METH_KEYWORDS, Table_parse_swaps_HELP},
     517  	{"is_fs_mounted", (PyCFunction)Table_is_fs_mounted, METH_VARARGS|METH_KEYWORDS, Table_is_fs_mounted_HELP},
     518  	{"add_fs", (PyCFunction)Table_add_fs, METH_VARARGS|METH_KEYWORDS, Table_add_fs_HELP},
     519  	{"remove_fs", (PyCFunction)Table_remove_fs, METH_VARARGS|METH_KEYWORDS, Table_remove_fs_HELP},
     520  	{"next_fs", (PyCFunction)Table_next_fs, METH_NOARGS, Table_next_fs_HELP},
     521  	{"write_file", (PyCFunction)Table_write_file, METH_VARARGS|METH_KEYWORDS, Table_write_file_HELP},
     522  	{"replace_file", (PyCFunction)Table_replace_file, METH_VARARGS|METH_KEYWORDS, Table_replace_file_HELP},
     523  	{NULL}
     524  };
     525  
     526  /* mnt_free_tab() with a few necessary additions */
     527  void Table_unref(struct libmnt_table *tab)
     528  {
     529  	struct libmnt_fs *fs;
     530  	struct libmnt_iter *iter;
     531  
     532  	if (!tab)
     533  		return;
     534  
     535  	DBG(TAB, pymnt_debug_h(tab, "un-referencing filesystems"));
     536  
     537  	iter = mnt_new_iter(MNT_ITER_BACKWARD);
     538  
     539  	/* remove pylibmount specific references to the entries */
     540  	while (mnt_table_next_fs(tab, iter, &fs) == 0)
     541  		Py_XDECREF(mnt_fs_get_userdata(fs));
     542  
     543  	DBG(TAB, pymnt_debug_h(tab, "un-referencing table"));
     544  
     545  	mnt_unref_table(tab);
     546  	mnt_free_iter(iter);
     547  }
     548  
     549  static void Table_destructor(TableObject *self)
     550  {
     551  	DBG(TAB, pymnt_debug_h(self->tab, "destructor py-obj: %p, py-refcnt=%d",
     552  				self, (int) ((PyObject *) self)->ob_refcnt));
     553  	Table_unref(self->tab);
     554  	self->tab = NULL;
     555  
     556  	mnt_free_iter(self->iter);
     557  	Py_XDECREF(self->errcb);
     558  	PyFree(self);
     559  }
     560  
     561  static PyObject *Table_new(PyTypeObject *type,
     562  			   PyObject *args __attribute__((unused)),
     563  			   PyObject *kwds __attribute__((unused)))
     564  {
     565  	TableObject *self = (TableObject*)type->tp_alloc(type, 0);
     566  
     567  	if (self) {
     568  		DBG(TAB, pymnt_debug_h(self, "new"));
     569  
     570  		self->tab = NULL;
     571  		self->iter = NULL;
     572  		self->errcb = NULL;
     573  	}
     574  	return (PyObject *)self;
     575  }
     576  
     577  /* explicit tab.__init__() serves as mnt_reset_table(tab) would in C
     578   * and as mnt_new_table{,_from_dir,_from_file}() with proper arguments */
     579  #define Table_HELP "Table(path=None, errcb=None)"
     580  static int Table_init(TableObject *self, PyObject *args, PyObject *kwds)
     581  {
     582  	struct libmnt_cache *cache;
     583  	char *path = NULL;
     584  	char *kwlist[] = {"path", "errcb", NULL};
     585  	PyObject *errcb = NULL;
     586  	struct stat buf;
     587  
     588  	memset (&buf, 0, sizeof(struct stat));
     589  
     590  	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sO",
     591  					kwlist, &path, &errcb))
     592  		return -1;
     593  
     594  	DBG(TAB, pymnt_debug_h(self, "init"));
     595  
     596  	Table_unref(self->tab);
     597  	self->tab = NULL;
     598  
     599  	if (self->iter)
     600  		mnt_reset_iter(self->iter, MNT_ITER_FORWARD);
     601  	else
     602  		self->iter = mnt_new_iter(MNT_ITER_FORWARD);
     603  
     604  	if (errcb) {
     605  		PyObject *tmp;
     606  		if (!PyCallable_Check(errcb))
     607  			return -1;
     608  		tmp = self->errcb;
     609  		Py_INCREF(errcb);
     610  		self->errcb = errcb;
     611  		Py_XDECREF(tmp);
     612  	} else {
     613  		Py_XDECREF(self->errcb);
     614  		self->errcb = NULL;
     615  	}
     616  
     617  	if (path) {
     618  		DBG(TAB, pymnt_debug_h(self, "init: path defined (%s)", path));
     619  
     620  		if (stat(path, &buf)) {
     621  			/* TODO: weird */
     622  			PyErr_SetFromErrno(PyExc_RuntimeError);
     623  			return -1;
     624  		}
     625  		if (S_ISREG(buf.st_mode))
     626  			self->tab = mnt_new_table_from_file(path);
     627  		else if (S_ISDIR(buf.st_mode))
     628  			self->tab = mnt_new_table_from_dir(path);
     629  	} else {
     630  		DBG(TAB, pymnt_debug_h(self, "init: allocate empty table"));
     631  		self->tab = mnt_new_table();
     632  	}
     633  
     634  	/* Always set custom handler when using libmount from python */
     635  	mnt_table_set_parser_errcb(self->tab, pymnt_table_parser_errcb);
     636  	mnt_table_set_userdata(self->tab, self);
     637  
     638  	cache = mnt_new_cache();		/* TODO: make it optional? */
     639  	if (!cache)
     640  		return -1;
     641  	mnt_table_set_cache(self->tab, cache);
     642  	mnt_unref_cache(cache);
     643  
     644  	return 0;
     645  }
     646  
     647  /* Handler for the tab->errcb callback */
     648  int pymnt_table_parser_errcb(struct libmnt_table *tb, const char *filename, int line)
     649  {
     650  	int rc = 0;
     651  	PyObject *obj;
     652  
     653  	obj = mnt_table_get_userdata(tb);
     654  	if (obj && ((TableObject*) obj)->errcb) {
     655  		PyObject *arglist, *result;
     656  
     657  		arglist = Py_BuildValue("(Osi)", obj, filename, line);
     658  		if (!arglist)
     659  			return -ENOMEM;
     660  
     661  		/* A python callback was set, so tb is definitely encapsulated in an object */
     662  		result = PyObject_Call(((TableObject *)obj)->errcb, arglist, NULL);
     663  		Py_DECREF(arglist);
     664  
     665  		if (!result)
     666  			return -EINVAL;
     667  		if (!PyArg_Parse(result, "i", &rc))
     668  			rc = -EINVAL;
     669  		Py_DECREF(result);
     670  	}
     671  	return rc;
     672  }
     673  
     674  PyObject *PyObjectResultTab(struct libmnt_table *tab)
     675  {
     676  	TableObject *result;
     677  
     678  	if (!tab) {
     679  		PyErr_SetString(LibmountError, "internal exception");
     680  		return NULL;
     681  	}
     682  
     683  	result = mnt_table_get_userdata(tab);
     684  	if (result) {
     685  		Py_INCREF(result);
     686  		DBG(TAB, pymnt_debug_h(tab, "result py-obj %p: already exists, py-refcnt=%d",
     687  				result, (int) ((PyObject *) result)->ob_refcnt));
     688  		return (PyObject *) result;
     689  	}
     690  
     691  	/* Creating an encapsulating object: increment the refcount, so that
     692  	 * code such as: cxt.get_fstab() doesn't call the destructor, which
     693  	 * would free our tab struct as well
     694  	 */
     695  	result = PyObject_New(TableObject, &TableType);
     696  	if (!result) {
     697  		UL_RaiseExc(ENOMEM);
     698  		return NULL;
     699  	}
     700  
     701  	Py_INCREF(result);
     702  	mnt_ref_table(tab);
     703  
     704  	DBG(TAB, pymnt_debug_h(tab, "result py-obj %p new, py-refcnt=%d",
     705  				result, (int) ((PyObject *) result)->ob_refcnt));
     706  	result->tab = tab;
     707  	result->iter = mnt_new_iter(MNT_ITER_FORWARD);
     708  	mnt_table_set_userdata(result->tab, result);
     709  	result->errcb = NULL;
     710  	return (PyObject *) result;
     711  }
     712  
     713  static PyGetSetDef Table_getseters[] = {
     714  	{"nents",		(getter)Table_get_nents, NULL, "number of valid entries in tab", NULL},
     715  	{"intro_comment",	(getter)Table_get_intro_comment, (setter)Table_set_intro_comment, "fstab intro comment", NULL},
     716  	{"trailing_comment",	(getter)Table_get_trailing_comment, (setter)Table_set_trailing_comment, "fstab trailing comment", NULL},
     717  	{"errcb",		NULL, (setter)Table_set_parser_errcb, "parser error callback", NULL},
     718  	{NULL}
     719  };
     720  
     721  
     722  static PyObject *Table_repr(TableObject *self)
     723  {
     724  	return PyUnicode_FromFormat(
     725  			"<libmount.Table object at %p, entries=%d, comments_enabled=%s, errcb=%s>",
     726  			self,
     727  			mnt_table_get_nents(self->tab),
     728  			mnt_table_with_comments(self->tab) ? "True" : "False",
     729  			self->errcb ? pystos(PyObject_Repr(self->errcb)) : "None");
     730  }
     731  
     732  PyTypeObject TableType = {
     733  	PyVarObject_HEAD_INIT(NULL, 0)
     734  	"libmount.Table", /*tp_name*/
     735  	sizeof(TableObject), /*tp_basicsize*/
     736  	0, /*tp_itemsize*/
     737  	(destructor)Table_destructor, /*tp_dealloc*/
     738  	0, /*tp_print*/
     739  	NULL, /*tp_getattr*/
     740  	NULL, /*tp_setattr*/
     741  	NULL, /*tp_compare*/
     742  	(reprfunc) Table_repr, /*tp_repr*/
     743  	NULL, /*tp_as_number*/
     744  	NULL, /*tp_as_sequence*/
     745  	NULL, /*tp_as_mapping*/
     746  	NULL, /*tp_hash */
     747  	NULL, /*tp_call*/
     748  	NULL, /*tp_str*/
     749  	NULL, /*tp_getattro*/
     750  	NULL, /*tp_setattro*/
     751  	NULL, /*tp_as_buffer*/
     752  	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
     753  	Table_HELP, /* tp_doc */
     754  	NULL, /* tp_traverse */
     755  	NULL, /* tp_clear */
     756  	NULL, /* tp_richcompare */
     757  	0, /* tp_weaklistoffset */
     758  	NULL, /* tp_iter */
     759  	NULL, /* tp_iternext */
     760  	Table_methods, /* tp_methods */
     761  	Table_members, /* tp_members */
     762  	Table_getseters, /* tp_getset */
     763  	NULL, /* tp_base */
     764  	NULL, /* tp_dict */
     765  	NULL, /* tp_descr_get */
     766  	NULL, /* tp_descr_set */
     767  	0, /* tp_dictoffset */
     768  	(initproc)Table_init, /* tp_init */
     769  	NULL, /* tp_alloc */
     770  	Table_new, /* tp_new */
     771  };
     772  
     773  void Table_AddModuleObject(PyObject *mod)
     774  {
     775  	if (PyType_Ready(&TableType) < 0)
     776  		return;
     777  
     778  	DBG(TAB, pymnt_debug("add to module"));
     779  
     780  	Py_INCREF(&TableType);
     781  	PyModule_AddObject(mod, "Table", (PyObject *)&TableType);
     782  }
     783