diff options
Diffstat (limited to 'libmount')
35 files changed, 14266 insertions, 0 deletions
diff --git a/libmount/.gitignore b/libmount/.gitignore new file mode 100644 index 000000000..7d203f5f9 --- /dev/null +++ b/libmount/.gitignore @@ -0,0 +1,2 @@ +test_* +libmount.h diff --git a/libmount/COPYING.libmount b/libmount/COPYING.libmount new file mode 100644 index 000000000..89d4489ce --- /dev/null +++ b/libmount/COPYING.libmount @@ -0,0 +1,508 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libmount/Makefile.am b/libmount/Makefile.am new file mode 100644 index 000000000..26111c662 --- /dev/null +++ b/libmount/Makefile.am @@ -0,0 +1,13 @@ +include $(top_srcdir)/config/include-Makefile.am + +SUBDIRS = src samples + +if ENABLE_GTK_DOC +SUBDIRS += docs +endif + +# pkg-config stuff +pkgconfigdir = $(usrlib_execdir)/pkgconfig +pkgconfig_DATA = mount.pc + +EXTRA_DIST = COPYING.libmount mount.pc.in diff --git a/libmount/docs/.gitignore b/libmount/docs/.gitignore new file mode 100644 index 000000000..917c84810 --- /dev/null +++ b/libmount/docs/.gitignore @@ -0,0 +1,17 @@ +*.args +*.bak +*-decl-list.txt +*-decl.txt +*.hierarchy +html/* +*.interfaces +*-overrides.txt +*.prerequisites +*.signals +*.stamp +tmpl/* +*-undeclared.txt +*-undocumented.txt +*-unused.txt +version.xml +xml/* diff --git a/libmount/docs/Makefile.am b/libmount/docs/Makefile.am new file mode 100644 index 000000000..3e087d294 --- /dev/null +++ b/libmount/docs/Makefile.am @@ -0,0 +1,101 @@ +include $(top_srcdir)/config/include-Makefile.am + +## Process this file with automake to produce Makefile.in + +# We require automake 1.10 at least. +AUTOMAKE_OPTIONS = 1.10 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=libmount + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +#DOC_MODULE_VERSION=2 + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=../src + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space mount + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(ul_libmount_incdir)/libmount.h +CFILE_GLOB=$(ul_libmount_srcdir)/*.c + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files to ignore when scanning. Use base file name, no paths +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES=mountP.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files = $(builddir)/version.xml + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS= +GTKDOC_LIBS= + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/config/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += version.xml.in + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you want your docs-status tested during 'make check' +if ENABLE_GTK_DOC +#TESTS_ENVIRONMENT = cd $(srcsrc) +#TESTS = $(GTKDOC_CHECK) +endif diff --git a/libmount/docs/libmount-docs.xml b/libmount/docs/libmount-docs.xml new file mode 100644 index 000000000..75802a282 --- /dev/null +++ b/libmount/docs/libmount-docs.xml @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" + "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" +[ + <!ENTITY version SYSTEM "version.xml"> +]> +<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> + <bookinfo> + <title>libmount Reference Manual</title> + <releaseinfo>for libmount version &version;</releaseinfo> + <copyright> + <year>2010</year> + <holder>Karel Zak <kzak@redhat.com></holder> + </copyright> + </bookinfo> + + <part id="gtk"> + <title>libmount Overview</title> + <partintro> + <para> +The libmount library is used to parse /etc/fstab, /etc/mtab and +/proc/self/mountinfo files, manage the mtab file, evaluate mount options, etc. + </para> + <para> +The library is part of the util-linux package since version 2.18 and is +available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/. + </para> + </partintro> + </part> + + <part> + <title>High-level API</title> + <xi:include href="xml/context.xml"/> + </part> + <part> + <title>Files parsing</title> + <xi:include href="xml/tab.xml"/> + <xi:include href="xml/fs.xml"/> + </part> + <part> + <title>Mount options</title> + <xi:include href="xml/optstr.xml"/> + <xi:include href="xml/optmap.xml"/> + </part> + <part> + <title>Mtab management</title> + <xi:include href="xml/lock.xml"/> + <xi:include href="xml/update.xml"/> + </part> + <part> + <title>Misc</title> + <xi:include href="xml/init.xml"/> + <xi:include href="xml/cache.xml"/> + <xi:include href="xml/iter.xml"/> + <xi:include href="xml/utils.xml"/> + <xi:include href="xml/version.xml"/> + </part> + + <index id="api-index-full"> + <title>API Index</title> + <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> + </index> +</book> diff --git a/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt new file mode 100644 index 000000000..f79374d90 --- /dev/null +++ b/libmount/docs/libmount-sections.txt @@ -0,0 +1,226 @@ +<SECTION> +<FILE>init</FILE> +mnt_init_debug +</SECTION> + +<SECTION> +<FILE>version</FILE> +mnt_parse_version_string +mnt_get_library_version +</SECTION> + +<SECTION> +<FILE>utils</FILE> +mnt_fstype_is_netfs +mnt_fstype_is_pseudofs +mnt_get_fstab_path +mnt_get_mtab_path +mnt_has_regular_mtab +mnt_mangle +mnt_match_fstype +mnt_match_options +mnt_unmangle +</SECTION> + +<SECTION> +<FILE>cache</FILE> +mnt_cache_device_has_tag +mnt_cache_find_tag_value +mnt_cache_read_tags +mnt_free_cache +mnt_get_fstype +mnt_new_cache +mnt_resolve_path +mnt_resolve_spec +mnt_resolve_tag +</SECTION> + +<SECTION> +<FILE>optstr</FILE> +mnt_optstr_append_option +mnt_optstr_apply_flags +mnt_optstr_get_flags +mnt_optstr_get_option +mnt_optstr_get_options +mnt_optstr_next_option +mnt_optstr_prepend_option +mnt_optstr_remove_option +mnt_optstr_set_option +mnt_split_optstr +</SECTION> + +<SECTION> +<FILE>iter</FILE> +mnt_free_iter +mnt_iter_get_direction +mnt_new_iter +mnt_reset_iter +</SECTION> + +<SECTION> +<FILE>optmap</FILE> +mnt_get_builtin_optmap +</SECTION> + +<SECTION> +<FILE>lock</FILE> +mnt_free_lock +mnt_lock_file +mnt_new_lock +mnt_unlock_file +</SECTION> + +<SECTION> +<FILE>fs</FILE> +mnt_copy_fs +mnt_free_fs +mnt_free_mntent +mnt_fs_append_attributes +mnt_fs_append_fs_options +mnt_fs_append_options +mnt_fs_append_user_options +mnt_fs_append_vfs_options +mnt_fs_get_attribute +mnt_fs_get_attributes +mnt_fs_get_bindsrc +mnt_fs_get_devno +mnt_fs_get_freq +mnt_fs_get_fs_options +mnt_fs_get_fstype +mnt_fs_get_id +mnt_fs_get_option +mnt_fs_get_parent_id +mnt_fs_get_passno +mnt_fs_get_root +mnt_fs_get_source +mnt_fs_get_srcpath +mnt_fs_get_tag +mnt_fs_get_target +mnt_fs_get_userdata +mnt_fs_get_user_options +mnt_fs_get_vfs_options +mnt_fs_match_fstype +mnt_fs_match_options +mnt_fs_match_source +mnt_fs_match_target +mnt_fs_prepend_attributes +mnt_fs_prepend_fs_options +mnt_fs_prepend_options +mnt_fs_prepend_user_options +mnt_fs_prepend_vfs_options +mnt_fs_print_debug +mnt_fs_set_attributes +mnt_fs_set_bindsrc +mnt_fs_set_freq +mnt_fs_set_fs_options +mnt_fs_set_fstype +mnt_fs_set_options +mnt_fs_set_passno +mnt_fs_set_root +mnt_fs_set_source +mnt_fs_set_target +mnt_fs_set_userdata +mnt_fs_set_user_options +mnt_fs_set_vfs_options +mnt_fs_strdup_options +mnt_new_fs +mnt_fs_to_mntent +</SECTION> + +<SECTION> +<FILE>tab</FILE> +mnt_free_table +mnt_new_table +mnt_new_table_from_dir +mnt_new_table_from_file +mnt_table_add_fs +mnt_table_find_next_fs +mnt_table_find_pair +mnt_table_find_source +mnt_table_find_srcpath +mnt_table_find_tag +mnt_table_find_target +mnt_table_get_cache +mnt_table_get_name +mnt_table_get_nents +mnt_table_get_root_fs +mnt_table_next_child_fs +mnt_table_next_fs +mnt_table_parse_file +mnt_table_parse_fstab +mnt_table_parse_mtab +mnt_table_parse_stream +mnt_table_remove_fs +mnt_table_set_cache +mnt_table_set_iter +mnt_table_set_parser_errcb +</SECTION> + +<SECTION> +<FILE>update</FILE> +mnt_free_update +mnt_new_update +mnt_update_force_rdonly +mnt_update_get_filename +mnt_update_get_fs +mnt_update_get_mflags +mnt_update_is_ready +mnt_update_set_fs +mnt_update_table +</SECTION> + +<SECTION> +<FILE>context</FILE> +mnt_context_init_helper +mnt_context_helper_setopt +mnt_context_append_options +mnt_context_apply_fstab +mnt_context_disable_canonicalize +mnt_context_disable_helpers +mnt_context_disable_mtab +mnt_context_do_mount +mnt_context_do_umount +mnt_context_enable_fake +mnt_context_enable_force +mnt_context_enable_lazy +mnt_context_enable_loopdel +mnt_context_enable_rdonly_umount +mnt_context_enable_sloppy +mnt_context_enable_verbose +mnt_context_finalize_mount +mnt_context_finalize_umount +mnt_context_get_cache +mnt_context_get_fs +mnt_context_get_fstab +mnt_context_get_fstype +mnt_context_get_lock +mnt_context_get_mflags +mnt_context_get_mtab +mnt_context_get_source +mnt_context_get_status +mnt_context_get_target +mnt_context_get_user_mflags +mnt_context_is_restricted +mnt_context_mount +mnt_context_prepare_mount +mnt_context_prepare_umount +mnt_context_set_cache +mnt_context_set_fs +mnt_context_set_fstab +mnt_context_set_fstype +mnt_context_set_fstype_pattern +mnt_context_set_mflags +mnt_context_set_mountdata +mnt_context_set_options +mnt_context_set_options_pattern +mnt_context_set_optsmode +mnt_context_set_source +mnt_context_set_target +mnt_context_set_user_mflags +mnt_context_strerror +mnt_context_umount +mnt_free_context +mnt_new_context +mnt_reset_context +</SECTION> + diff --git a/libmount/docs/libmount.types b/libmount/docs/libmount.types new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/libmount/docs/libmount.types diff --git a/libmount/docs/version.xml.in b/libmount/docs/version.xml.in new file mode 100644 index 000000000..d78bda934 --- /dev/null +++ b/libmount/docs/version.xml.in @@ -0,0 +1 @@ +@VERSION@ diff --git a/libmount/mount.pc.in b/libmount/mount.pc.in new file mode 100644 index 000000000..2c327978b --- /dev/null +++ b/libmount/mount.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@usrlib_execdir@ +includedir=@includedir@ + +Name: mount +Description: mount library +Version: @LIBMOUNT_VERSION@ +Requires.private: blkid +Cflags: -I${includedir}/libmount +Libs: -L${libdir} -lmount diff --git a/libmount/samples/.gitignore b/libmount/samples/.gitignore new file mode 100644 index 000000000..6008ee321 --- /dev/null +++ b/libmount/samples/.gitignore @@ -0,0 +1,2 @@ +mount +mountpoint diff --git a/libmount/samples/Makefile.am b/libmount/samples/Makefile.am new file mode 100644 index 000000000..ee13d8b77 --- /dev/null +++ b/libmount/samples/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/config/include-Makefile.am + +AM_CPPFLAGS += -I$(ul_libmount_incdir) +AM_LDFLAGS += $(ul_libmount_la) + +noinst_PROGRAMS = mount mountpoint + diff --git a/libmount/samples/mount.c b/libmount/samples/mount.c new file mode 100644 index 000000000..086391e90 --- /dev/null +++ b/libmount/samples/mount.c @@ -0,0 +1,423 @@ +/* + * mount(8) -- mount a filesystem + * + * Copyright (C) 2011 Red Hat, Inc. All rights reserved. + * Written by Karel Zak <kzak@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/types.h> + +#include <libmount.h> + +#include "nls.h" +#include "c.h" + +/*** TODO: DOCS: + * + * -p, --pass-fd is unsupported + * --guess-fstype is unsupported + * -c = --no-canonicalize + */ + +/* exit status */ +#define EX_SUCCESS 0 +#define EX_USAGE 1 /* incorrect invocation or permission */ +#define EX_SYSERR 2 /* out of memory, cannot fork, ... */ +#define EX_SOFTWARE 4 /* internal mount bug or wrong version */ +#define EX_USER 8 /* user interrupt */ +#define EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */ +#define EX_FAIL 32 /* mount failure */ +#define EX_SOMEOK 64 /* some mount succeeded */ + +static struct libmnt_lock *lock; + +static void lock_atexit_cleanup(void) +{ + if (lock) + mnt_unlock_file(lock); +} + +static void __attribute__((__noreturn__)) exit_non_root(const char *option) +{ + const uid_t ruid = getuid(); + const uid_t euid = geteuid(); + + if (ruid == 0 && euid != 0) { + /* user is root, but setuid to non-root */ + if (option) + errx(EX_USAGE, _("only root can use \"--%s\" option " + "(effective UID is %u)"), + option, euid); + errx(EX_USAGE, _("only root can do that " + "(effective UID is %u)"), euid); + } + if (option) + errx(EX_USAGE, _("only root can use \"--%s\" option"), option); + errx(EX_USAGE, _("only root can do that")); +} + +static void __attribute__((__noreturn__)) print_version(void) +{ + const char *ver = NULL; + + mnt_get_library_version(&ver); + + printf("%s from %s (libmount %s)\n", + program_invocation_short_name, PACKAGE_STRING, ver); + exit(EX_SUCCESS); +} + +static const char *opt_to_longopt(int c, const struct option *opts) +{ + const struct option *o; + + for (o = opts; o->name; o++) + if (o->val == c) + return o->name; + return NULL; +} + +static int print_all(struct libmnt_context *cxt, char *pattern, int show_label) +{ + int rc = 0; + struct libmnt_table *tb; + struct libmnt_iter *itr = NULL; + struct libmnt_fs *fs; + struct libmnt_cache *cache = NULL; + + rc = mnt_context_get_mtab(cxt, &tb); + if (rc) + goto done; + + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!itr) + goto done; + + if (show_label) + cache = mnt_new_cache(); + + while(mnt_table_next_fs(tb, itr, &fs) == 0) { + const char *type = mnt_fs_get_fstype(fs); + const char *src = mnt_fs_get_source(fs); + const char *optstr = mnt_fs_get_options(fs); + + if (type && pattern && !mnt_match_fstype(type, pattern)) + continue; + + /* TODO: print loop backing file instead of device name */ + + printf ("%s on %s", src ? : "none", mnt_fs_get_target(fs)); + if (type) + printf (" type %s", type); + if (optstr) + printf (" (%s)", optstr); + if (show_label && src) { + char *lb = mnt_cache_find_tag_value(cache, src, "LABEL"); + if (lb) + printf (" [%s]", lb); + } + fputc('\n', stdout); + } +done: + mnt_free_cache(cache); + mnt_free_iter(itr); + return rc; +} + +static void __attribute__((__noreturn__)) usage(FILE *out) +{ + fprintf(out, _("Usage:\n" + " %1$s [-lhV]\n" + " %1$s -a [options]\n" + " %1$s [options] <source> | <directory>\n" + " %1$s [options] <source> <directory>\n" + " %1$s <operation> <mountpoint> [<target>]\n"), + program_invocation_short_name); + + fprintf(out, _( + "\nOptions:\n" + " -a, --all mount all filesystems mentioned in fstab\n" + " -f, --fake dry run, skip mount(2) syscall\n" + " -F, --fork fork off for each device (use with -a)\n" + " -h, --help this help\n" + " -n, --no-mtab don't write to /etc/mtab\n" + " -r, --read-only mount the filesystem read-only (same as -o ro)\n" + " -v, --verbose verbose mode\n" + " -V, --version print version string\n" + " -w, --read-write mount the filesystem read-write (default)\n" + " -o, --options <list> comma separated string of mount options\n" + " -O, --test-opts <list> limit the set of filesystems (use with -a)\n" + " -t, --types <list> indicate the filesystem type\n" + " -c, --no-canonicalize don't canonicalize paths\n" + " -i, --internal-only don't call the mount.<type> helpers\n" + " -l, --show-labels lists all mounts with LABELs\n" + + "\nSource:\n" + " -L, --label <label> synonym for LABEL=<label>\n" + " -U, --uuid <uuid> synonym for UUID=<uuid>\n" + " LABEL=<label> specifies device by filesystem label\n" + " UUID=<uuid> specifies device by filesystem UUID\n" + " <device> specifies device by path\n" + " <directory> mountpoint for bind mounts (see --bind/rbind)\n" + " <file> regular file for loopdev setup\n" + + "\nOperations:\n" + " -B, --bind mount a subtree somewhere else (same as -o bind)\n" + " -M, --move move a subtree to some other place\n" + " -R, --rbind mount a subtree and all submounts somewhere else\n" + " --make-shared mark a subtree as shared\n" + " --make-slave mark a subtree as slave\n" + " --make-private mark a subtree as private\n" + " --make-unbindable mark a subtree as unbindable\n" + " --make-rshared recursively mark a whole subtree as shared\n" + " --make-rslave recursively mark a whole subtree as slave\n" + " --make-rprivate recursively mark a whole subtree as private\n" + " --make-runbindable recursively mark a whole subtree as unbindable\n" + )); + + fprintf(out, _("\nFor more information see mount(8).\n")); + + exit(out == stderr ? EX_USAGE : EX_SUCCESS); +} + +int main(int argc, char **argv) +{ + int c, rc = EXIT_FAILURE, all = 0, show_labels = 0; + struct libmnt_context *cxt; + char *source = NULL, *srcbuf = NULL; + char *types = NULL; + unsigned long oper = 0; + + static const struct option longopts[] = { + { "all", 0, 0, 'a' }, + { "fake", 0, 0, 'f' }, + { "fork", 0, 0, 'F' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "read-only", 0, 0, 'r' }, + { "ro", 0, 0, 'r' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-write", 0, 0, 'w' }, + { "rw", 0, 0, 'w' }, + { "options", 1, 0, 'o' }, + { "test-opts", 1, 0, 'O' }, + { "types", 1, 0, 't' }, + { "uuid", 1, 0, 'U' }, + { "label", 1, 0, 'L'}, + { "bind", 0, 0, 'B' }, + { "move", 0, 0, 'M' }, + { "rbind", 0, 0, 'R' }, + { "make-shared", 0, 0, 136 }, + { "make-slave", 0, 0, 137 }, + { "make-private", 0, 0, 138 }, + { "make-unbindable", 0, 0, 139 }, + { "make-rshared", 0, 0, 140 }, + { "make-rslave", 0, 0, 141 }, + { "make-rprivate", 0, 0, 142 }, + { "make-runbindable", 0, 0, 143 }, + { "no-canonicalize", 0, 0, 'c' }, + { "internal-only", 0, 0, 'i' }, + { "show-labels", 0, 0, 'l' }, + { NULL, 0, 0, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + mnt_init_debug(0); + cxt = mnt_new_context(); + if (!cxt) + err(EX_SYSERR, _("libmount context allocation failed")); + + while ((c = getopt_long(argc, argv, "aBcfFhilL:Mno:O:rRsU:vVwt:", + longopts, NULL)) != -1) { + + /* only few options are allowed for non-root users */ + if (mnt_context_is_restricted(cxt) && !strchr("hlLUVv", c)) + exit_non_root(opt_to_longopt(c, longopts)); + + switch(c) { + case 'a': + all = 1; + err(EX_FAIL, "-a not implemented yet"); /* TODO */ + break; + case 'c': + mnt_context_disable_canonicalize(cxt, TRUE); + break; + case 'f': + mnt_context_enable_fake(cxt, TRUE); + break; + case 'F': + err(EX_FAIL, "-F not implemented yet"); /* TODO */ + break; + case 'h': + usage(stdout); + break; + case 'i': + mnt_context_disable_helpers(cxt, TRUE); + break; + case 'n': + mnt_context_disable_mtab(cxt, TRUE); + break; + case 'r': + if (mnt_context_append_options(cxt, "ro")) + err(EX_SYSERR, _("failed to append options")); + break; + case 'v': + mnt_context_enable_verbose(cxt, TRUE); + break; + case 'V': + print_version(); + break; + case 'w': + if (mnt_context_append_options(cxt, "rw")) + err(EX_SYSERR, _("failed to append options")); + break; + case 'o': + if (mnt_context_append_options(cxt, optarg)) + err(EX_SYSERR, _("failed to append options")); + break; + case 'O': + if (mnt_context_set_options_pattern(cxt, optarg)) + err(EX_SYSERR, _("failed to set options pattern")); + break; + case 'L': + case 'U': + if (source) + errx(EX_USAGE, _("only one <source> may be specified")); + if (asprintf(&srcbuf, "%s=\"%s\"", + c == 'L' ? "LABEL" : "UUID", optarg) <= 0) + err(EX_SYSERR, _("failed to allocate source buffer")); + source = srcbuf; + break; + case 'l': + show_labels = 1; + break; + case 't': + types = optarg; + break; + case 's': + mnt_context_enable_sloppy(cxt, TRUE); + break; + case 'B': + oper = MS_BIND; + break; + case 'M': + oper = MS_MOVE; + break; + case 'R': + oper = (MS_BIND | MS_REC); + break; + case 136: + oper = MS_SHARED; + break; + case 137: + oper = MS_SLAVE; + break; + case 138: + oper = MS_PRIVATE; + break; + case 139: + oper = MS_UNBINDABLE; + break; + case 140: + oper = (MS_SHARED | MS_REC); + break; + case 141: + oper = (MS_SLAVE | MS_REC); + break; + case 142: + oper = (MS_PRIVATE | MS_REC); + break; + case 143: + oper = (MS_UNBINDABLE | MS_REC); + break; + default: + usage(stderr); + break; + } + } + + argc -= optind; + argv += optind; + + if (!source && !argc && !all) { + if (oper) + usage(stderr); + if (!print_all(cxt, types, show_labels)) + rc = EX_SUCCESS; + goto done; + } + + if (oper && (types || all || source)) + usage(stderr); + + if (types && (strchr(types, ',') || strncmp(types, "no", 2) == 0)) + mnt_context_set_fstype_pattern(cxt, types); + else if (types) + mnt_context_set_fstype(cxt, types); + + if (argc == 0 && source) { + /* mount -L|-U */ + mnt_context_set_source(cxt, source); + + } else if (argc == 1) { + /* mount [-L|-U] <target> + * mount <source|target> */ + if (source) { + if (mnt_context_is_restricted(cxt)) + exit_non_root(NULL); + mnt_context_set_source(cxt, source); + } + mnt_context_set_target(cxt, argv[0]); + + } else if (argc == 2 && !source) { + /* mount <source> <target> */ + if (mnt_context_is_restricted(cxt)) + exit_non_root(NULL); + mnt_context_set_source(cxt, argv[0]); + mnt_context_set_target(cxt, argv[1]); + } else + usage(stderr); + + if (oper) + mnt_context_set_mflags(cxt, oper); + + lock = mnt_context_get_lock(cxt); + if (lock) + atexit(lock_atexit_cleanup); + + rc = mnt_context_mount(cxt); + if (rc) { + /* TODO: call mnt_context_strerror() */ + rc = EX_FAIL; + } else + rc = EX_SUCCESS; +done: + free(srcbuf); + mnt_free_context(cxt); + return rc; +} + diff --git a/libmount/samples/mountpoint.c b/libmount/samples/mountpoint.c new file mode 100644 index 000000000..92d283054 --- /dev/null +++ b/libmount/samples/mountpoint.c @@ -0,0 +1,174 @@ +/* + * mountpoint(1) - see if a directory is a mountpoint + * + * This is libmount based reimplementation of the mountpoit(1) + * from sysvinit project. + * + * + * Copyright (C) 2011 Red Hat, Inc. All rights reserved. + * Written by Karel Zak <kzak@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + + +#include <libmount.h> + +#include "nls.h" +#include "xalloc.h" +#include "c.h" + +static int quiet; + +static char *dir_to_device(const char *spec) +{ + struct libmnt_table *tb = mnt_new_table_from_file("/proc/self/mountinfo"); + struct libmnt_fs *fs; + char *res = NULL; + + if (!tb) + return NULL; + + fs = mnt_table_find_target(tb, spec, MNT_ITER_BACKWARD); + if (fs && mnt_fs_get_target(fs)) + res = xstrdup(mnt_fs_get_source(fs)); + + mnt_free_table(tb); + return res; +} + +static int print_devno(const char *devname, struct stat *st) +{ + struct stat stbuf; + + if (!st && stat(devname, &stbuf) == 0) + st = &stbuf; + if (!st) + return -1; + if (!S_ISBLK(st->st_mode)) { + if (!quiet) + warnx(_("%s: not a block device"), devname); + return -1; + } + printf("%u:%u\n", major(st->st_rdev), minor(st->st_rdev)); + return 0; +} + +static void __attribute__((__noreturn__)) usage(FILE *out) +{ + fprintf(out, _("Usage:\n" + " %1$s [-qd] /path/to/directory\n" + " %1$s -x /dev/device\n"), + program_invocation_short_name); + + fprintf(out, _( + "\nOptions:\n" + " -q, --quiet quiet mode - don't print anything\n" + " -d, --fs-devno print maj:min device number of the filesystem\n" + " -x, --devno print maj:min device number of the block device\n" + " -h, --help this help\n" + )); + + fprintf(out, _("\nFor more information see mountpoint(1).\n")); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + int c, fs_devno = 0, dev_devno = 0, rc = 0; + char *spec; + struct stat st; + + static const struct option longopts[] = { + { "quiet", 0, 0, 'q' }, + { "fs-devno", 0, 0, 'd' }, + { "devno", 0, 0, 'x' }, + { "help", 0, 0, 'h' }, + { NULL, 0, 0, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + mnt_init_debug(0); + + while ((c = getopt_long(argc, argv, "qdxh", longopts, NULL)) != -1) { + + switch(c) { + case 'q': + quiet = 1; + break; + case 'd': + fs_devno = 1; + break; + case 'x': + dev_devno = 1; + break; + case 'h': + usage(stdout); + break; + default: + usage(stderr); + break; + } + } + + if (optind + 1 != argc) + usage(stderr); + + spec = argv[optind++]; + + if (stat(spec, &st)) { + if (!quiet) + err(EXIT_FAILURE, "%s", spec); + return EXIT_FAILURE; + } + if (dev_devno) + rc = print_devno(spec, &st); + else { + char *src; + + if (!S_ISDIR(st.st_mode)) { + if (!quiet) + errx(EXIT_FAILURE, _("%s: not a directory"), spec); + return EXIT_FAILURE; + } + src = dir_to_device(spec); + if (!src) { + if (!quiet) + printf(_("%s is not a mountpoint\n"), spec); + return EXIT_FAILURE; + } + if (fs_devno) + rc = print_devno(src, NULL); + else if (!quiet) + printf(_("%s is a mountpoint\n"), spec); + free(src); + } + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/libmount/src/Makefile.am b/libmount/src/Makefile.am new file mode 100644 index 000000000..56b0c207d --- /dev/null +++ b/libmount/src/Makefile.am @@ -0,0 +1,67 @@ +include $(top_srcdir)/config/include-Makefile.am + +AM_CPPFLAGS += -I$(ul_libmount_incdir) \ + -I$(ul_libmount_srcdir) \ + -I$(ul_libblkid_incdir) + +# includes +mountincdir = $(includedir)/libmount +nodist_mountinc_HEADERS = libmount.h + +usrlib_exec_LTLIBRARIES = libmount.la +libmount_la_SOURCES = mountP.h version.c utils.c test.c init.c cache.c \ + optstr.c optmap.c iter.c lock.c \ + fs.c tab.c tab_parse.c tab_update.c tab_diff.c \ + context.c context_mount.c context_umount.c \ + $(mountinc_HEADERS) \ + $(top_srcdir)/lib/at.c \ + $(top_srcdir)/include/list.h \ + $(top_srcdir)/lib/mangle.c \ + $(top_srcdir)/lib/canonicalize.c \ + $(top_srcdir)/lib/strutils.c \ + $(top_srcdir)/lib/env.c + +nodist_libmount_la_SOURCES = mountP.h + +libmount_la_LIBADD = $(ul_libblkid_la) $(SELINUX_LIBS) + +libmount_la_DEPENDENCIES = $(libmount_la_LIBADD) libmount.sym libmount.h.in + +libmount_la_LDFLAGS = -Wl,--version-script=$(ul_libmount_srcdir)/libmount.sym \ + -version-info $(LIBMOUNT_VERSION_INFO) + +TESTS_LIBS = -luuid + +if HAVE_SELINUX +TESTS_LIBS += $(SELINUX_LIBS) +endif + +EXTRA_DIST = libmount.sym libmount.h.in +CLEANFILES = $(tests) + +# move lib from $(usrlib_execdir) to $(libdir) if needed +install-exec-hook: + if test "$(usrlib_execdir)" != "$(libdir)"; then \ + mkdir -p $(DESTDIR)$(libdir); \ + mv $(DESTDIR)$(usrlib_execdir)/libmount.so.* $(DESTDIR)$(libdir); \ + so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libmount.so); \ + so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \ + (cd $(DESTDIR)$(usrlib_execdir) && \ + rm -f libmount.so && \ + $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libmount.so); \ + fi + +uninstall-hook: + rm -f $(DESTDIR)$(libdir)/libmount.so* + + +tests = test_version test_cache test_optstr test_lock \ + test_tab test_utils test_tab_update test_context \ + test_tab_diff + +tests: all $(tests) +test_%: %.c + $(AM_V_CC)$(COMPILE) -DTEST_PROGRAM $< .libs/libmount.a \ + $(ul_libblkid_builddir)/.libs/libblkid.a -o $@ \ + $(TESTS_LIBS) + diff --git a/libmount/src/cache.c b/libmount/src/cache.c new file mode 100644 index 000000000..4775069c0 --- /dev/null +++ b/libmount/src/cache.c @@ -0,0 +1,667 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: cache + * @title: Cache + * @short_description: paths and tags (UUID/LABEL) caching + * + * The cache is a very simple API for work with tags (LABEL, UUID, ...) and + * paths. The cache uses libblkid as a backend from TAGs resolution. + * + * All returned paths are always canonicalized. + */ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <blkid.h> + +#include "canonicalize.h" +#include "mountP.h" + +/* + * Canonicalized (resolved) paths & tags cache + */ +#define MNT_CACHE_CHUNKSZ 128 + +#define MNT_CACHE_ISTAG (1 << 1) /* entry is TAG */ +#define MNT_CACHE_ISPATH (1 << 2) /* entry is path */ +#define MNT_CACHE_TAGREAD (1 << 3) /* tag read by mnt_cache_read_tags() */ + +/* path cache entry */ +struct mnt_cache_entry { + char *native; /* the original path */ + char *real; /* canonicalized path */ + int flag; +}; + +struct libmnt_cache { + struct mnt_cache_entry *ents; + size_t nents; + size_t nallocs; + + /* blkid_evaluate_tag() works in two ways: + * + * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks, + * then the blkid_cache is NULL. + * + * 2/ all tags are read from /etc/blkid.tab and verified by /dev + * scanning, then the blkid_cache is not NULL and then it's + * better to reuse the blkid_cache. + */ + blkid_cache bc; + blkid_probe pr; + + char *filename; +}; + +/** + * mnt_new_cache: + * + * Returns: new struct libmnt_cache instance or NULL in case of ENOMEM error. + */ +struct libmnt_cache *mnt_new_cache(void) +{ + struct libmnt_cache *cache = calloc(1, sizeof(*cache)); + if (!cache) + return NULL; + DBG(CACHE, mnt_debug_h(cache, "alloc")); + return cache; +} + +/** + * mnt_free_cache: + * @cache: pointer to struct libmnt_cache instance + * + * Deallocates the cache. + */ +void mnt_free_cache(struct libmnt_cache *cache) +{ + int i; + + if (!cache) + return; + + DBG(CACHE, mnt_debug_h(cache, "free")); + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (e->real != e->native) + free(e->real); + free(e->native); + } + free(cache->ents); + free(cache->filename); + if (cache->bc) + blkid_put_cache(cache->bc); + blkid_free_probe(cache->pr); + free(cache); +} + +/* note that the @native could be tha same pointer as @real */ +static int mnt_cache_add_entry(struct libmnt_cache *cache, char *native, + char *real, int flag) +{ + struct mnt_cache_entry *e; + + assert(cache); + assert(real); + assert(native); + + if (cache->nents == cache->nallocs) { + size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ; + + e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry)); + if (!e) + return -ENOMEM; + cache->ents = e; + cache->nallocs = sz; + } + + e = &cache->ents[cache->nents]; + e->native = native; + e->real = real; + e->flag = flag; + cache->nents++; + + DBG(CACHE, mnt_debug_h(cache, "add entry [%2zd] (%s): %s: %s", + cache->nents, + (flag & MNT_CACHE_ISPATH) ? "path" : "tag", + real, native)); + return 0; +} + +/* add tag to the cache, @real has to be allocated string */ +static int mnt_cache_add_tag(struct libmnt_cache *cache, const char *token, + const char *value, char *real, int flag) +{ + size_t tksz, vlsz; + char *native; + int rc; + + assert(cache); + assert(real); + assert(token); + assert(value); + + /* add into cache -- cache format for TAGs is + * native = "NAME\0VALUE\0" + * real = "/dev/foo" + */ + tksz = strlen(token); + vlsz = strlen(value); + + native = malloc(tksz + vlsz + 2); + if (!native) + return -ENOMEM; + + memcpy(native, token, tksz + 1); /* include '\0' */ + memcpy(native + tksz + 1, value, vlsz + 1); + + rc = mnt_cache_add_entry(cache, native, real, flag | MNT_CACHE_ISTAG); + if (!rc) + return 0; + + free(native); + return rc; +} + + +/* + * Returns cached canonicalized path or NULL. + */ +static const char *mnt_cache_find_path(struct libmnt_cache *cache, const char *path) +{ + int i; + + assert(cache); + assert(path); + + if (!cache || !path) + return NULL; + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_ISPATH)) + continue; + if (strcmp(path, e->native) == 0) + return e->real; + } + return NULL; +} + +/* + * Returns cached path or NULL. + */ +static const char *mnt_cache_find_tag(struct libmnt_cache *cache, + const char *token, const char *value) +{ + int i; + size_t tksz; + + assert(cache); + assert(token); + assert(value); + + if (!cache || !token || !value) + return NULL; + + tksz = strlen(token); + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_ISTAG)) + continue; + if (strcmp(token, e->native) == 0 && + strcmp(value, e->native + tksz + 1) == 0) + return e->real; + } + return NULL; +} + +/* + * returns (in @res) blkid prober, the @cache argument is optional + */ +static int mnt_cache_get_probe(struct libmnt_cache *cache, const char *devname, + blkid_probe *res) +{ + blkid_probe pr = cache ? cache->pr : NULL; + + assert(devname); + assert(res); + + if (cache && cache->pr && (!cache->filename || + strcmp(devname, cache->filename))) { + blkid_free_probe(cache->pr); + free(cache->filename); + cache->filename = NULL; + pr = cache->pr = NULL; + } + + if (!pr) { + pr = blkid_new_probe_from_filename(devname); + if (!pr) + return -1; + if (cache) { + cache->pr = pr; + cache->filename = strdup(devname); + if (!cache->filename) + return -ENOMEM; + } + + } + + *res = pr; + return 0; +} + +/** + * mnt_cache_read_tags + * @cache: pointer to struct libmnt_cache instance + * @devname: path device + * + * Reads @devname LABEL and UUID to the @cache. + * + * Returns: 0 if at least one tag was added, 1 if no tag was added or + * negative number in case of error. + */ +int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname) +{ + int i, ntags = 0, rc; + blkid_probe pr; + const char *tags[] = { "LABEL", "UUID", "TYPE" }; + + assert(cache); + assert(devname); + + if (!cache || !devname) + return -EINVAL; + + DBG(CACHE, mnt_debug_h(cache, "tags for %s requested", devname)); + + /* check is 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->real, devname) == 0) + /* tags has been already read */ + return 0; + } + + rc = mnt_cache_get_probe(cache, devname, &pr); + if (rc) + return rc; + + blkid_probe_enable_superblocks(cache->pr, 1); + + blkid_probe_set_superblocks_flags(cache->pr, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | + BLKID_SUBLKS_TYPE); + + if (blkid_do_safeprobe(cache->pr)) + goto error; + + DBG(CACHE, mnt_debug_h(cache, "reading tags for: %s", devname)); + + for (i = 0; i < ARRAY_SIZE(tags); i++) { + const char *data; + char *dev; + + if (blkid_probe_lookup_value(cache->pr, tags[i], &data, NULL)) + continue; + if (mnt_cache_find_tag(cache, tags[i], data)) + continue; /* already cached */ + + dev = strdup(devname); + if (!dev) + goto error; + if (mnt_cache_add_tag(cache, tags[i], data, dev, + MNT_CACHE_TAGREAD)) { + free(dev); + goto error; + } + ntags++; + } + + return ntags ? 0 : 1; +error: + return -1; +} + +/** + * mnt_cache_device_has_tag: + * @cache: paths cache + * @devname: path to the device + * @token: tag name (e.g "LABEL") + * @value: tag value + * + * Look up @cache to check it @tag+@value are associated with @devname. + * + * Returns: 1 on success or 0. + */ +int mnt_cache_device_has_tag(struct libmnt_cache *cache, const char *devname, + const char *token, const char *value) +{ + const char *path = mnt_cache_find_tag(cache, token, value); + + if (path && strcmp(path, devname) == 0) + return 1; + return 0; +} + +/** + * mnt_cache_find_tag_value: + * @cache: cache for results + * @devname: device name + * @token: tag name ("LABEL" or "UUID") + * + * Returns: LABEL or UUID for the @devname or NULL in case of error. + */ +char *mnt_cache_find_tag_value(struct libmnt_cache *cache, + const char *devname, const char *token) +{ + int i; + + if (!cache || !devname || !token) + return NULL; + + if (mnt_cache_read_tags(cache, devname) != 0) + return NULL; + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_ISTAG)) + continue; + if (strcmp(e->real, devname) == 0 && /* dev name */ + strcmp(token, e->native) == 0) /* tag name */ + return e->native + strlen(token) + 1; /* tag value */ + } + + return NULL; +} + +/** + * mnt_get_fstype: + * @devname: device name + * @ambi: returns TRUE if probing result is ambivalent (optional argument) + * @cache: cache for results or NULL + * + * Returns: filesystem type or NULL in case of error. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache) +{ + blkid_probe pr; + const char *data; + char *type = NULL; + int rc; + + DBG(CACHE, mnt_debug_h(cache, "get %s FS type", devname)); + + if (cache) + return mnt_cache_find_tag_value(cache, devname, "TYPE"); + + if (mnt_cache_get_probe(NULL, devname, &pr)) + return NULL; + + blkid_probe_enable_superblocks(pr, 1); + + blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE); + + rc = blkid_do_safeprobe(pr); + + if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL)) + type = strdup(data); + + if (ambi) + *ambi = rc == -2 ? TRUE : FALSE; + + blkid_free_probe(pr); + return type; +} + +/** + * mnt_resolve_path: + * @path: "native" path + * @cache: cache for results or NULL + * + * Returns: absolute path or NULL in case of error. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_resolve_path(const char *path, struct libmnt_cache *cache) +{ + char *p = NULL; + char *native = NULL; + char *real = NULL; + + assert(path); + + /*DBG(CACHE, mnt_debug_h(cache, "resolving path %s", path));*/ + + if (!path) + return NULL; + if (cache) + p = (char *) mnt_cache_find_path(cache, path); + + if (!p) { + p = canonicalize_path(path); + + if (p && cache) { + real = p; + native = strcmp(path, p) == 0 ? real : strdup(path); + + if (!native || !real) + goto error; + + if (mnt_cache_add_entry(cache, native, real, + MNT_CACHE_ISPATH)) + goto error; + } + } + + return p; +error: + if (real != native) + free(real); + free(native); + return NULL; +} + +/** + * mnt_resolve_tag: + * @token: tag name + * @value: tag value + * @cache: for results or NULL + * + * Returns: device name or NULL in case of error. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_resolve_tag(const char *token, const char *value, + struct libmnt_cache *cache) +{ + char *p = NULL; + + assert(token); + assert(value); + + /*DBG(CACHE, mnt_debug_h(cache, "resolving tag token=%s value=%s", + token, value));*/ + + if (!token || !value) + return NULL; + + if (cache) + p = (char *) mnt_cache_find_tag(cache, token, value); + + if (!p) { + /* returns newly allocated string */ + p = blkid_evaluate_tag(token, value, cache ? &cache->bc : NULL); + + if (p && cache && + mnt_cache_add_tag(cache, token, value, p, 0)) + goto error; + } + + return p; +error: + free(p); + return NULL; +} + + + +/** + * mnt_resolve_spec: + * @spec: path or tag + * @cache: paths cache + * + * Returns: canonicalized path or NULL. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache) +{ + char *cn = 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 + cn = mnt_resolve_path(spec, cache); + + return cn; +} + + +#ifdef TEST_PROGRAM + +int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[]) +{ + char line[BUFSIZ]; + struct libmnt_cache *cache; + + cache = mnt_new_cache(); + if (!cache) + return -ENOMEM; + + while(fgets(line, sizeof(line), stdin)) { + size_t sz = strlen(line); + char *p; + + if (line[sz - 1] == '\n') + line[sz - 1] = '\0'; + + p = mnt_resolve_path(line, cache); + printf("%s : %s\n", line, p); + } + mnt_free_cache(cache); + return 0; +} + +int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[]) +{ + char line[BUFSIZ]; + struct libmnt_cache *cache; + + cache = mnt_new_cache(); + if (!cache) + return -ENOMEM; + + while(fgets(line, sizeof(line), stdin)) { + size_t sz = strlen(line); + char *p; + + if (line[sz - 1] == '\n') + line[sz - 1] = '\0'; + + p = mnt_resolve_spec(line, cache); + printf("%s : %s\n", line, p); + } + mnt_free_cache(cache); + return 0; +} + +int test_read_tags(struct libmnt_test *ts, int argc, char *argv[]) +{ + char line[BUFSIZ]; + struct libmnt_cache *cache; + int i; + + cache = mnt_new_cache(); + if (!cache) + return -ENOMEM; + + while(fgets(line, sizeof(line), stdin)) { + size_t sz = strlen(line); + + if (line[sz - 1] == '\n') + line[sz - 1] = '\0'; + + if (!strcmp(line, "quit")) + break; + + if (*line == '/') { + if (mnt_cache_read_tags(cache, line) < 0) + fprintf(stderr, "%s: read tags faild\n", line); + + } else if (strchr(line, '=')) { + char *tag, *val; + const char *cn = NULL; + + if (!blkid_parse_tag_string(line, &tag, &val)) { + cn = mnt_cache_find_tag(cache, tag, val); + + free(tag); + free(val); + } + if (cn) + printf("%s: %s\n", line, cn); + else + printf("%s: not cached\n", line); + } + } + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_ISTAG)) + continue; + + printf("%15s : %5s : %s\n", e->real, e->native, + e->native + strlen(e->native) + 1); + } + + mnt_free_cache(cache); + return 0; + +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test ts[] = { + { "--resolve-path", test_resolve_path, " resolve paths from stdin" }, + { "--resolve-spec", test_resolve_spec, " evaluate specs from stdin" }, + { "--read-tags", test_read_tags, " read devname or TAG from stdin (\"quit\" to exit)" }, + { NULL } + }; + + return mnt_run_test(ts, argc, argv); +} +#endif diff --git a/libmount/src/context.c b/libmount/src/context.c new file mode 100644 index 000000000..6b0edc55b --- /dev/null +++ b/libmount/src/context.c @@ -0,0 +1,1741 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: context + * @title: Mount/umount context + * @short_description: high-level API to mount/umount devices. + * + * <informalexample> + * <programlisting> + * struct libmnt_context *cxt = mnt_new_context(); + * + * mnt_context_set_options(cxt, "aaa,bbb,ccc=CCC"); + * mnt_context_set_mflags(cxt, MS_NOATIME|MS_NOEXEC); + * mnt_context_set_target(cxt, "/mnt/foo"); + * + * if (!mnt_context_mount(cxt)) + * printf("successfully mounted\n"); + * mnt_free_context(cxt); + * + * </programlisting> + * </informalexample> + * + * This code is similar to: + * + * mount -o aaa,bbb,ccc=CCC,noatime,noexec /mnt/foo + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "c.h" +#include "mountP.h" + +/** + * mnt_new_context: + * + * Returns: newly allocated mount context + */ +struct libmnt_context *mnt_new_context(void) +{ + struct libmnt_context *cxt; + uid_t ruid, euid; + + cxt = calloc(1, sizeof(*cxt)); + if (!cxt) + return NULL; + + ruid = getuid(); + euid = geteuid(); + + cxt->syscall_status = 1; /* not called yet */ + cxt->helper_exec_status = 1; + + /* if we're really root and aren't running setuid */ + cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1; + + DBG(CXT, mnt_debug_h(cxt, "allocate %s", + cxt->restricted ? "[RESTRICTED]" : "")); + + mnt_has_regular_mtab(&cxt->mtab_path, &cxt->mtab_writable); + + if (!cxt->mtab_writable) + /* use /run/mount/utab if /etc/mtab is useless */ + mnt_has_regular_utab(&cxt->utab_path, &cxt->utab_writable); + + return cxt; +} + +/** + * mnt_free_context: + * @cxt: mount context + * + * Deallocates context struct. + */ +void mnt_free_context(struct libmnt_context *cxt) +{ + if (!cxt) + return; + + mnt_reset_context(cxt); + + DBG(CXT, mnt_debug_h(cxt, "free")); + + 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_free_lock(cxt->lock); + mnt_free_update(cxt->update); + + free(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, ....) + * + * The match patters, cached fstab, cached canonicalized paths and tags and + * [e]uid are not reseted. You have to use + * + * mnt_context_set_fstab(cxt, NULL); + * mnt_context_set_cache(cxt, NULL); + * mnt_context_set_fstype_pattern(cxt, NULL); + * mnt_context_set_options_pattern(cxt, NULL); + * + * + * to reset these stuff. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_reset_context(struct libmnt_context *cxt) +{ + int fl; + + if (!cxt) + return -EINVAL; + + fl = cxt->flags; + + if (!(cxt->flags & MNT_FL_EXTERN_FS)) + mnt_free_fs(cxt->fs); + + mnt_free_table(cxt->mtab); + + free(cxt->helper); + free(cxt->orig_user); + + cxt->fs = NULL; + cxt->mtab = NULL; + cxt->ambi = 0; + cxt->helper = NULL; + cxt->orig_user = NULL; + cxt->mountflags = 0; + cxt->user_mountflags = 0; + cxt->mountdata = NULL; + cxt->flags = MNT_FL_DEFAULT; + cxt->syscall_status = 1; + cxt->helper_exec_status = 1; + cxt->helper_status = 0; + + /* restore non-resetable flags */ + cxt->flags |= (fl & MNT_FL_EXTERN_FSTAB); + cxt->flags |= (fl & MNT_FL_EXTERN_CACHE); + + return 0; +} + +static int set_flag(struct libmnt_context *cxt, int flag, int enable) +{ + if (!cxt) + return -EINVAL; + if (enable) + cxt->flags |= flag; + else + cxt->flags &= ~flag; + return 0; +} + +/** + * mnt_context_is_restricted: + * @cxt: mount context + * + * Returns: 0 for unrestricted mount (user is root), or 1 for non-root mounts + */ +int mnt_context_is_restricted(struct libmnt_context *cxt) +{ + assert(cxt); + return cxt->restricted; +} + +/** + * mnt_context_set_optsmode + * @cxt: mount context + * @mode: mask, see MNT_OMASK_* flags in libmount mount.h + * + * Controls how to use mount options from fstab/mtab. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode) +{ + if (!cxt) + return -EINVAL; + cxt->optsmode = mode; + return 0; +} + +/** + * mnt_context_get_optsmode + * @cxt: mount context + * + * Returns: MNT_OMASK_* mask or zero. + */ + +int mnt_context_get_optsmode(struct libmnt_context *cxt) +{ + return cxt ? cxt->optsmode : 0; +} + +/** + * mnt_context_disable_canonicalize: + * @cxt: mount context + * @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 + * for mount(2) syscall. + * + * This fuction has effect to the private 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, NULL). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_disable_canonicalize(struct libmnt_context *cxt, int disable) +{ + return set_flag(cxt, MNT_FL_NOCANONICALIZE, disable); +} + +/** + * mnt_context_enable_lazy: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable lazy umount (see umount(8) man page, option -l). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_LAZY, enable); +} + +/** + * mnt_context_is_lazy: + * @cxt: mount context + * + * Returns: 1 if lazy umount is enabled or 0 + */ +int mnt_context_is_lazy(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_LAZY) ? 1 : 0; +} + + +/** + * mnt_context_enable_rdonly_umount: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable read-only remount on failed umount(2) + * (see umount(8) man page, option -r). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_RDONLY_UMOUNT, enable); +} + +/** + * mnt_context_is_rdonly_umount + * @cxt: mount context + * + * See also mnt_context_enable_rdonly_umount() and see umount(8) man page, + * option -r. + * + * Returns: 1 if read-only remount failed umount(2) is enables or 0 + */ +int mnt_context_is_rdonly_umount(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_RDONLY_UMOUNT) ? 1 : 0; +} + +/** + * mnt_context_disable_helpers: + * @cxt: mount context + * @disable: TRUE or FALSE + * + * Enable/disable /sbin/[u]mount.* helpers (see mount(8) man page, option -i). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_disable_helpers(struct libmnt_context *cxt, int disable) +{ + return set_flag(cxt, MNT_FL_NOHELPERS, disable); +} + +/** + * mnt_context_enable_sloppy: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Set/unset sloppy mounting (see mount(8) man page, option -s). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_sloppy(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_SLOPPY, enable); +} + +/** + * mnt_context_is_sloppy: + * @cxt: mount context + * + * Returns: 1 if sloppy flag is enabled or 0 + */ +int mnt_context_is_sloppy(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_SLOPPY) ? 1 : 0; +} + +/** + * mnt_context_enable_fake: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable fake mounting (see mount(8) man page, option -f). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_fake(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_FAKE, enable); +} + +/** + * mnt_context_is_fake: + * @cxt: mount context + * + * Returns: 1 if fake flag is enabled or 0 + */ +int mnt_context_is_fake(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_FAKE) ? 1 : 0; +} + +/** + * mnt_context_disable_mtab: + * @cxt: mount context + * @disable: TRUE or FALSE + * + * Disable/enable mtab update (see mount(8) man page, option -n). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable) +{ + return set_flag(cxt, MNT_FL_NOMTAB, disable); +} + +/** + * mnt_context_is_nomtab + * @cxt: mount context + * + * Returns: 1 if no-mtab is enabled or 0 + */ +int mnt_context_is_nomtab(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_NOMTAB) ? 1 : 0; +} + +/** + * mnt_context_enable_force: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable force umounting (see umount(8) man page, option -f). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_force(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_FORCE, enable); +} + +/** + * mnt_context_is_force + * @cxt: mount context + * + * Returns: 1 if force umounting flag is enabled or 0 + */ +int mnt_context_is_force(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_FORCE) ? 1 : 0; +} + +/** + * mnt_context_enable_verbose: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable verbose output (TODO: not implemented yet) + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_VERBOSE, enable); +} + +/** + * mnt_context_is_verbose + * @cxt: mount context + * + * Returns: 1 if verbose flag is enabled or 0 + */ +int mnt_context_is_verbose(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_VERBOSE) ? 1 : 0; +} + +/** + * mnt_context_enable_loopdel: + * @cxt: mount context + * @enable: TRUE or FALSE + * + * Enable/disable loop delete (destroy) after umount (see umount(8), option -d) + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_enable_loopdel(struct libmnt_context *cxt, int enable) +{ + return set_flag(cxt, MNT_FL_LOOPDEL, enable); +} + +/** + * mnt_context_set_fs: + * @cxt: mount context + * @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(). + * + * 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. + * + * Returns: 0 on success, negative number in case of error. + */ +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); + cxt->fs = fs; + return 0; +} + +/** + * mnt_context_get_fs: + * @cxt: mount context + * + * The FS contains the basic description of mountpoint, fs type and so on. + * 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. + */ +struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt) +{ + if (!cxt) + return NULL; + if (!cxt->fs) { + cxt->fs = mnt_new_fs(); + cxt->flags &= ~MNT_FL_EXTERN_FS; + } + return cxt->fs; +} + +/** + * mnt_context_set_source: + * @cxt: mount context + * @source: mount source (device, directory, UUID, LABEL, ...) + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_source(struct libmnt_context *cxt, const char *source) +{ + return mnt_fs_set_source(mnt_context_get_fs(cxt), source); +} + +/** + * mnt_context_get_source: + * @cxt: mount context + * + * Returns: returns pointer or NULL in case of error pr if not set. + */ +const char *mnt_context_get_source(struct libmnt_context *cxt) +{ + return mnt_fs_get_source(mnt_context_get_fs(cxt)); +} + +/** + * mnt_context_set_target: + * @cxt: mount context + * @target: mountpoint + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_target(struct libmnt_context *cxt, const char *target) +{ + return mnt_fs_set_target(mnt_context_get_fs(cxt), target); +} + +/** + * mnt_context_get_target: + * @cxt: mount context + * + * Returns: returns pointer or NULL in case of error pr if not set. + */ +const char *mnt_context_get_target(struct libmnt_context *cxt) +{ + return mnt_fs_get_target(mnt_context_get_fs(cxt)); +} + +/** + * mnt_context_set_fstype: + * @cxt: mount context + * @fstype: filesystem type + * + * Note that the @fstype has to be the real FS type. For comma-separated list of + * filesystems or for "nofs" notation use mnt_context_set_fstype_pattern(). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype) +{ + if (fstype && strchr(fstype, ',')) + return -EINVAL; + return mnt_fs_set_fstype(mnt_context_get_fs(cxt), fstype); +} + +/** + * mnt_context_get_fstype: + * @cxt: mount context + * + * Returns: returns pointer or NULL in case of error pr if not set. + */ +const char *mnt_context_get_fstype(struct libmnt_context *cxt) +{ + return mnt_fs_get_fstype(mnt_context_get_fs(cxt)); +} + +/** + * mnt_context_set_options: + * @cxt: mount context + * @optstr: comma delimited mount options + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_options(struct libmnt_context *cxt, const char *optstr) +{ + return mnt_fs_set_options(mnt_context_get_fs(cxt), optstr); +} + +/** + * mnt_context_append_options: + * @cxt: mount context + * @optstr: comma delimited mount options + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_append_options(struct libmnt_context *cxt, const char *optstr) +{ + return mnt_fs_append_options(mnt_context_get_fs(cxt), optstr); +} + +/** + * mnt_context_set_fstype_pattern: + * @cxt: mount context + * @pattern: FS name pattern (or NULL to reset the current setting) + * + * See mount(8), option -t. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_fstype_pattern(struct libmnt_context *cxt, const char *pattern) +{ + char *p = NULL; + + if (!cxt) + return -EINVAL; + if (pattern) { + p = strdup(pattern); + if (!p) + return -ENOMEM; + } + free(cxt->fstype_pattern); + cxt->fstype_pattern = p; + return 0; +} + +/** + * mnt_context_set_options_pattern: + * @cxt: mount context + * @pattern: options pattern (or NULL to reset the current setting) + * + * See mount(8), option -O. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_options_pattern(struct libmnt_context *cxt, const char *pattern) +{ + char *p = NULL; + + if (!cxt) + return -EINVAL; + if (pattern) { + p = strdup(pattern); + if (!p) + return -ENOMEM; + } + free(cxt->optstr_pattern); + cxt->optstr_pattern = p; + return 0; +} + +/** + * mnt_context_set_fstab: + * @cxt: mount context + * @tb: fstab + * + * The mount context reads /etc/fstab to the 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(). + * + * 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.) + * + * If the @tb argument is NULL then the current private fstab instance is + * reseted. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_fstab(struct libmnt_context *cxt, struct libmnt_table *tb) +{ + 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); + cxt->fstab = tb; + return 0; +} + +/** + * mnt_context_get_fstab: + * @cxt: mount context + * @tb: returns fstab + * + * See also mnt_table_parse_fstab() for more details about fstab. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb) +{ + struct libmnt_cache *cache; + + if (!cxt) + return -EINVAL; + + if (!cxt->fstab) { + int rc; + + cxt->fstab = mnt_new_table(); + if (!cxt->fstab) + return -ENOMEM; + cxt->flags &= ~MNT_FL_EXTERN_FSTAB; + 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; +} + +/** + * mnt_context_get_mtab: + * @cxt: mount context + * @tb: returns mtab + * + * See also mnt_table_parse_mtab() for more details about mtab/mountinfo. The + * result will 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; + + if (!cxt) + return -EINVAL; + + if (!cxt->mtab) { + int rc; + + cxt->mtab = mnt_new_table(); + if (!cxt->mtab) + return -ENOMEM; + 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; + return 0; +} + +/** + * mnt_context_set_cache: + * @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(). + * + * If the @cache argument is NULL then the current private cache instance is + * reseted. + * + * Returns: 0 on success, negative number in case of error. + */ +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); + cxt->cache = cache; + return 0; +} + +/** + * mnt_context_get_cache + * @cxt: mount context + * + * See also mnt_context_set_cache(). + * + * Returns: pointer to cache or NULL if canonicalization is disabled. + */ +struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt) +{ + if (!cxt || (cxt->flags & MNT_FL_NOCANONICALIZE)) + return NULL; + + if (!cxt->cache) { + cxt->cache = mnt_new_cache(); + if (!cxt->cache) + return NULL; + cxt->flags &= ~MNT_FL_EXTERN_CACHE; + } + return cxt->cache; +} + +/** + * mnt_context_get_lock: + * @cxt: mount context + * + * The libmount applications don't have to care about mtab locking, but with a + * small exception: the application has to be able to remove the lock file when + * interrupted by signal or signals have to be ignored when the lock is locked. + * + * 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: + * + * lc = mnt_context_get_lock(cxt); + * if (lc) + * mnt_lock_block_signals(lc, FALSE); + * + * and don't forget to call mnt_unlock_file(lc) before exit. + * + * Returns: pointer to lock struct or NULL. + */ +struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt) +{ + /* + * DON'T call this function within libmount, it will always allocate + * the lock. The mnt_update_* functions are able to allocate the lock + * only when mtab/utab update is really necessary. + */ + if (!cxt || (cxt->flags & MNT_FL_NOMTAB)) + return NULL; + + if (!cxt->lock) { + cxt->lock = mnt_new_lock(cxt->mtab_writable ? + cxt->mtab_path : cxt->utab_path, 0); + if (cxt->lock) + mnt_lock_block_signals(cxt->lock, TRUE); + } + return cxt->lock; +} + +/** + * mnt_context_set_mflags: + * @cxt: mount context + * @flags: mount(2) flags (MS_* flags) + * + * Sets mount flags (see mount(2) man page). + * + * Note that mount context allows to define mount options by mount flags. It + * means you can for example use + * + * mnt_context_set_mflags(cxt, MS_NOEXEC | MS_NOSUID); + * + * rather than + * + * mnt_context_set_options(cxt, "noexec,nosuid"); + * + * these both calls have the same effect. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_mflags(struct libmnt_context *cxt, unsigned long flags) +{ + if (!cxt) + return -EINVAL; + cxt->mountflags = flags; + return 0; +} + +/** + * mnt_context_get_mflags: + * @cxt: mount context + * @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()). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags) +{ + int rc = 0; + if (!cxt || !flags) + return -EINVAL; + + *flags = 0; + if (!(cxt->flags & MNT_FL_MOUNTFLAGS_MERGED) && cxt->fs) { + const char *o = mnt_fs_get_options(cxt->fs); + if (o) + rc = mnt_optstr_get_flags(o, flags, + mnt_get_builtin_optmap(MNT_LINUX_MAP)); + } + if (!rc) + *flags |= cxt->mountflags; + return rc; +} + +/** + * mnt_context_set_user_mflags: + * @cxt: mount context + * @flags: mount(2) flags (MNT_MS_* flags, e.g. MNT_MS_LOOP) + * + * Sets userspace mount flags. + * + * See also notest for mnt_context_set_mflags(). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_user_mflags(struct libmnt_context *cxt, unsigned long flags) +{ + if (!cxt) + return -EINVAL; + cxt->user_mountflags = flags; + return 0; +} + +/** + * mnt_context_get_user_mflags: + * @cxt: mount context + * @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()). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags) +{ + int rc = 0; + if (!cxt || !flags) + return -EINVAL; + + *flags = 0; + if (!(cxt->flags & MNT_FL_MOUNTFLAGS_MERGED) && cxt->fs) { + const char *o = mnt_fs_get_user_options(cxt->fs); + if (o) + rc = mnt_optstr_get_flags(o, flags, + mnt_get_builtin_optmap(MNT_USERSPACE_MAP)); + } + if (!rc) + *flags |= cxt->user_mountflags; + return rc; +} + +static int is_loop(struct libmnt_context *cxt) +{ + unsigned long fl = 0; + + if (cxt->user_mountflags & MNT_MS_LOOP) + return 1; + if (!mnt_context_get_mflags(cxt, &fl) && (fl & MNT_MS_LOOP)) + return 1; + + /* TODO: + * - support MNT_MS_{OFFSET,SIZELIMIT,ENCRYPTION} + */ + return 0; +} + +/** + * mnt_context_set_mountdata: + * @cxt: mount context + * @data: mount(2) data + * + * The mount context generates mountdata from mount options by default. This + * 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 + * NULL is also valid mount data. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_set_mountdata(struct libmnt_context *cxt, void *data) +{ + if (!cxt) + return -EINVAL; + cxt->mountdata = data; + cxt->flags |= MNT_FL_MOUNTDATA; + return 0; +} + +/* + * Translates LABEL/UUID/path to mountable path + */ +int mnt_context_prepare_srcpath(struct libmnt_context *cxt) +{ + const char *path = NULL; + struct libmnt_cache *cache; + const char *t, *v, *src; + int rc = 0; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt || !cxt->fs) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "preparing source path")); + + src = mnt_fs_get_source(cxt->fs); + + /* ignore filesystems without source or filesystems + * where the source is quasi-path (//foo/bar) + */ + if (!src || (cxt->fs->flags & MNT_FS_NET)) + return 0; + + DBG(CXT, mnt_debug_h(cxt, "srcpath '%s'", src)); + + cache = mnt_context_get_cache(cxt); + + if (!mnt_fs_get_tag(cxt->fs, &t, &v)) { + /* + * Source is TAG (evaluate) + */ + if (cache) + path = mnt_resolve_tag(t, v, cache); + + rc = path ? mnt_fs_set_source(cxt->fs, path) : -EINVAL; + + } else if (cache) { + /* + * Source is PATH (canonicalize) + */ + path = mnt_resolve_path(src, cache); + if (path && strcmp(path, src)) + rc = mnt_fs_set_source(cxt->fs, path); + } + + if (rc) { + DBG(CXT, mnt_debug_h(cxt, "failed to prepare srcpath [rc=%d]", rc)); + return rc; + } + + if (!path) + path = src; + + if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) || + (cxt->fs->flags & MNT_FS_PSEUDO)) { + DBG(CXT, mnt_debug_h(cxt, "PROPAGATION/pseudo FS source: %s", path)); + return rc; + } + + /* + * Initialize loop device + */ + if (is_loop(cxt)) { + ; /* TODO */ + } + + DBG(CXT, mnt_debug_h(cxt, "final srcpath '%s'", path)); + return 0; +} + +int mnt_context_prepare_target(struct libmnt_context *cxt) +{ + const char *tgt; + struct libmnt_cache *cache; + int rc = 0; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt || !cxt->fs) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "preparing target path")); + + tgt = mnt_fs_get_target(cxt->fs); + if (!tgt) + return 0; + + cache = mnt_context_get_cache(cxt); + if (cache) { + char *path = mnt_resolve_path(tgt, cache); + if (strcmp(path, tgt)) + rc = mnt_fs_set_target(cxt->fs, path); + } + + if (rc) + DBG(CXT, mnt_debug_h(cxt, "failed to prepare target")); + else + DBG(CXT, mnt_debug_h(cxt, "final target '%s'", + mnt_fs_get_target(cxt->fs))); + return 0; +} + +int mnt_context_guess_fstype(struct libmnt_context *cxt) +{ + char *type; + const char *dev; + int rc = -EINVAL; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt || !cxt->fs) + return -EINVAL; + + if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) + goto none; + + type = (char *) mnt_fs_get_fstype(cxt->fs); + if (type && !strcmp(type, "auto")) { + mnt_fs_set_fstype(cxt->fs, NULL); + type = NULL; + } + + if (type) + goto done; + if (cxt->flags & MS_REMOUNT) + goto none; + if (cxt->fstype_pattern) + goto done; + + dev = mnt_fs_get_srcpath(cxt->fs); + if (!dev) + goto err; + + if (access(dev, F_OK) == 0) { + struct libmnt_cache *cache = mnt_context_get_cache(cxt); + + type = mnt_get_fstype(dev, &cxt->ambi, cache); + if (type) { + rc = mnt_fs_set_fstype(cxt->fs, type); + if (!cache) + free(type); /* type is not cached */ + } + } else { + if (strchr(dev, ':') != NULL) + rc = mnt_fs_set_fstype(cxt->fs, "nfs"); + else if (!strncmp(dev, "//", 2)) + rc = mnt_fs_set_fstype(cxt->fs, "cifs"); + } + if (rc) + goto err; +done: + DBG(CXT, mnt_debug_h(cxt, "FS type: %s", + mnt_fs_get_fstype(cxt->fs))); + return 0; +none: + return mnt_fs_set_fstype(cxt->fs, "none"); +err: + DBG(CXT, mnt_debug_h(cxt, "failed to detect FS type")); + return rc; +} + +/* + * The default is to use fstype from cxt->fs, this could be overwritten by + * @type. The @act is MNT_ACT_{MOUNT,UMOUNT}. + * + * Returns: 0 on success or negative number in case of error. Note that success + * does not mean that there is any usable helper, you have to check cxt->helper. + */ +int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name, + const char *type) +{ + char search_path[] = FS_SEARCH_PATH; /* from config.h */ + char *p = NULL, *path; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!type) + type = mnt_fs_get_fstype(cxt->fs); + + if ((cxt->flags & MNT_FL_NOHELPERS) || !type || + !strcmp(type, "none") || (cxt->fs->flags & MNT_FS_SWAP)) + return 0; + + path = strtok_r(search_path, ":", &p); + while (path) { + char helper[PATH_MAX]; + struct stat st; + int rc; + + rc = snprintf(helper, sizeof(helper), "%s/%s.%s", + path, name, type); + path = strtok_r(NULL, ":", &p); + + if (rc >= sizeof(helper) || rc < 0) + continue; + + rc = stat(helper, &st); + if (rc == -1 && errno == ENOENT && strchr(type, '.')) { + /* If type ends with ".subtype" try without it */ + *strrchr(helper, '.') = '\0'; + rc = stat(helper, &st); + } + + DBG(CXT, mnt_debug_h(cxt, "%-25s ... %s", helper, + rc ? "not found" : "found")); + if (rc) + continue; + + if (cxt->helper) + free(cxt->helper); + cxt->helper = strdup(helper); + if (!cxt->helper) + return -ENOMEM; + return 0; + } + + return 0; +} + +int mnt_context_merge_mflags(struct libmnt_context *cxt) +{ + unsigned long fl = 0; + int rc; + + assert(cxt); + + DBG(CXT, mnt_debug_h(cxt, "merging mount flags")); + + rc = mnt_context_get_mflags(cxt, &fl); + if (rc) + return rc; + cxt->mountflags = fl; + + /* TODO: if cxt->fs->fs_optstr contains 'ro' then set the MS_RDONLY to + * mount flags, it's possible that superblock is read-only, but VFS is + * read-write. + */ + + fl = 0; + rc = mnt_context_get_user_mflags(cxt, &fl); + if (rc) + return rc; + cxt->user_mountflags = fl; + + DBG(CXT, mnt_debug_h(cxt, "final flags: VFS=%08lx user=%08lx", + cxt->mountflags, cxt->user_mountflags)); + + cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED; + return 0; +} + +/* + * Prepare /etc/mtab or /run/mount/utab + */ +int mnt_context_prepare_update(struct libmnt_context *cxt) +{ + int rc; + const char *target; + + assert(cxt); + assert(cxt->fs); + assert(cxt->action); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + DBG(CXT, mnt_debug_h(cxt, "prepare update")); + + if (cxt->mountflags & MS_PROPAGATION) { + DBG(CXT, mnt_debug_h(cxt, "skip update: MS_PROPAGATION")); + return 0; + } + + target = mnt_fs_get_target(cxt->fs); + + if (cxt->action == MNT_ACT_UMOUNT && target && !strcmp(target, "/")) + /* Don't try to touch mtab if umounting root FS */ + cxt->flags |= MNT_FL_NOMTAB; + + if (cxt->flags & MNT_FL_NOMTAB) { + DBG(CXT, mnt_debug_h(cxt, "skip update: NOMTAB flag")); + return 0; + } + if (cxt->helper) { + DBG(CXT, mnt_debug_h(cxt, "skip update: external helper")); + return 0; + } + if (!cxt->mtab_writable && !cxt->utab_writable) { + DBG(CXT, mnt_debug_h(cxt, "skip update: no writable destination")); + return 0; + } + /* 0 = success, 1 = not called yet */ + if (cxt->syscall_status != 1 && cxt->syscall_status != 0) { + DBG(CXT, mnt_debug_h(cxt, + "skip update: syscall failed [status=%d]", + cxt->syscall_status)); + return 0; + } + if (!cxt->update) { + cxt->update = mnt_new_update(); + if (!cxt->update) + return -ENOMEM; + + mnt_update_set_filename(cxt->update, + cxt->mtab_writable ? cxt->mtab_path : cxt->utab_path, + !cxt->mtab_writable); + } + + if (cxt->action == MNT_ACT_UMOUNT) + rc = mnt_update_set_fs(cxt->update, cxt->mountflags, + mnt_fs_get_target(cxt->fs), NULL); + else + rc = mnt_update_set_fs(cxt->update, cxt->mountflags, + NULL, cxt->fs); + + return rc < 0 ? rc : 0; +} + +int mnt_context_update_tabs(struct libmnt_context *cxt) +{ + unsigned long fl; + + assert(cxt); + + if (cxt->flags & MNT_FL_NOMTAB) { + DBG(CXT, mnt_debug_h(cxt, "don't update: NOMTAB flag")); + return 0; + } + if (cxt->helper) { + DBG(CXT, mnt_debug_h(cxt, "don't update: external helper")); + return 0; + } + if (!cxt->update || !mnt_update_is_ready(cxt->update)) { + DBG(CXT, mnt_debug_h(cxt, "don't update: no update prepared")); + return 0; + } + if (cxt->syscall_status) { + DBG(CXT, mnt_debug_h(cxt, "don't update: syscall failed/not called")); + return 0; + } + + fl = mnt_update_get_mflags(cxt->update); + if ((cxt->mountflags & MS_RDONLY) != (fl & MS_RDONLY)) + /* + * fix MS_RDONLY in options + */ + mnt_update_force_rdonly(cxt->update, + cxt->mountflags & MS_RDONLY); + + return mnt_update_table(cxt->update, cxt->lock); +} + +static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb, + int direction) +{ + struct libmnt_fs *fs = NULL; + const char *src = NULL, *tgt = NULL; + int rc; + + assert(cxt); + assert(cxt->fs); + + if (!cxt->fs) + return -EINVAL; + + src = mnt_fs_get_source(cxt->fs); + tgt = mnt_fs_get_target(cxt->fs); + + if (tgt && src) + fs = mnt_table_find_pair(tb, src, tgt, direction); + else { + if (src) + fs = mnt_table_find_source(tb, src, direction); + else if (tgt) + fs = mnt_table_find_target(tb, tgt, direction); + + if (!fs) { + /* swap source and target (if @src is not LABEL/UUID), + * for example in + * + * mount /foo/bar + * + * the path could be a mountpoint as well as source (for + * example bind mount, symlink to device, ...). + */ + if (src && !mnt_fs_get_tag(cxt->fs, NULL, NULL)) + fs = mnt_table_find_target(tb, src, direction); + if (!fs && tgt) + fs = mnt_table_find_source(tb, tgt, direction); + } + } + + if (!fs) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "apply entry:")); + DBG(CXT, mnt_fs_print_debug(fs, stderr)); + + /* copy from tab to our FS description + */ + rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs)); + if (!rc) + rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs)); + + if (!rc && !mnt_fs_get_fstype(cxt->fs)) + rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs)); + + if (rc) + return rc; + + if (cxt->optsmode & MNT_OMODE_IGNORE) + ; + else if (cxt->optsmode & MNT_OMODE_REPLACE) + rc = mnt_fs_set_options(cxt->fs, mnt_fs_get_options(fs)); + + else if (cxt->optsmode & MNT_OMODE_APPEND) + rc = mnt_fs_append_options(cxt->fs, mnt_fs_get_options(fs)); + + else if (cxt->optsmode & MNT_OMODE_PREPEND) + rc = mnt_fs_prepend_options(cxt->fs, mnt_fs_get_options(fs)); + + if (!rc) + cxt->flags |= MNT_FL_TAB_APPLIED; + return rc; +} + +/** + * mnt_context_apply_fstab: + * @cxt: mount context + * + * This function is optional. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_apply_fstab(struct libmnt_context *cxt) +{ + int rc = -1; + struct libmnt_table *tab = NULL; + const char *src = NULL, *tgt = NULL; + + assert(cxt); + assert(cxt->fs); + + if (!cxt) + return -EINVAL; + + if (cxt->flags & MNT_FL_TAB_APPLIED) + return 0; + + if (mnt_context_is_restricted(cxt)) { + DBG(CXT, mnt_debug_h(cxt, "force fstab usage for non-root users")); + cxt->optsmode = MNT_OMODE_USER; + + } else if (cxt->optsmode == 0) + cxt->optsmode = MNT_OMODE_AUTO; + + if (cxt->fs) { + src = mnt_fs_get_source(cxt->fs); + tgt = mnt_fs_get_target(cxt->fs); + } + + /* fstab is not required if source and target are specified */ + if (src && tgt && !(cxt->optsmode & MNT_OMODE_FORCE)) { + DBG(CXT, mnt_debug_h(cxt, "fstab not required -- skip")); + return 0; + } + + DBG(CXT, mnt_debug_h(cxt, + "trying to apply fstab (src=%s, target=%s)", src, tgt)); + + /* let's initialize cxt->fs */ + mnt_context_get_fs(cxt); + + /* try fstab */ + if (cxt->optsmode & MNT_OMODE_FSTAB) { + rc = mnt_context_get_fstab(cxt, &tab); + if (!rc) + rc = apply_table(cxt, tab, MNT_ITER_FORWARD); + } + + /* try mtab */ + if (rc == -1 && (cxt->optsmode & MNT_OMODE_MTAB)) { + rc = mnt_context_get_mtab(cxt, &tab); + if (!rc) + rc = apply_table(cxt, tab, MNT_ITER_BACKWARD); + } + if (rc) + DBG(CXT, mnt_debug_h(cxt, "failed to found entry in fstab/mtab")); + return rc; +} + +/** + * mnt_context_get_status: + * @cxt: mount context + * + * Returns: 0 if /sbin/mount.type or mount(2) syscall was successfull. + */ +int mnt_context_get_status(struct libmnt_context *cxt) +{ + return cxt && (!cxt->syscall_status || !cxt->helper_exec_status); +} + +/** + * mnt_context_set_syscall_status: + * @cxt: mount context + * @status: mount(2) return code + * + * The @status should be 0 on succcess, or negative number on error (-1 or + * -errno). + * + * This function should be used if [u]mount(2) syscall was NOT called by + * libmount (by mnt_context_mount() or mnt_context_do_mount()) only. + * + * Returns: 0 or negative number in case of error. + */ +int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status) +{ + if (!cxt) + return -EINVAL; + + DBG(CXT, mnt_debug_h(cxt, "syscall status set to: %d", status)); + cxt->syscall_status = status; + return 0; +} + +/** + * mnt_context_strerror + * @cxt: context + * @buf: buffer + * @bufsiz: size of the buffer + * + * Returns: 0 or negative number in case of error. + */ +int mnt_context_strerror(struct libmnt_context *cxt, char *buf, size_t bufsiz) +{ + /* TODO: based on cxt->syscall_errno or cxt->helper_status */ + return 0; +} + +/** + * mnt_context_init_helper + * @cxt: mount context + * @action: MNT_ACT_{UMOUNT,MOUNT} + * @flags: not used + * + * This function infors 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 + * feature by: + * + * mnt_context_disable_helpers(cxt, FALSE); + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_context_init_helper(struct libmnt_context *cxt, int action, int flags) +{ + int rc = mnt_context_disable_helpers(cxt, TRUE); + + if (!rc) + rc = set_flag(cxt, MNT_FL_HELPER, 1); + if (!rc) + cxt->action = action; + + DBG(CXT, mnt_debug_h(cxt, "initialized for [u]mount.<type> helper [rc=%d]", rc)); + return rc; +} + +/** + * mnt_context_helper_setopt: + * @cxr: context + * @c: getopt() result + * @arg: getopt() optarg + * + * This function applies [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. + * + * Returns: negative number on error, 1 if @c is unknown option, 0 on success. + */ +int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg) +{ + if (cxt) { + switch(cxt->action) { + case MNT_ACT_MOUNT: + return mnt_context_mount_setopt(cxt, c, arg); + case MNT_ACT_UMOUNT: + return mnt_context_umount_setopt(cxt, c, arg); + } + } + return -EINVAL; +} + +#ifdef TEST_PROGRAM + +struct libmnt_lock *lock; + +static void lock_fallback(void) +{ + if (lock) + mnt_unlock_file(lock); +} + +int test_mount(struct libmnt_test *ts, int argc, char *argv[]) +{ + int idx = 1, rc = 0; + struct libmnt_context *cxt; + + if (argc < 2) + return -EINVAL; + + cxt = mnt_new_context(); + if (!cxt) + return -ENOMEM; + + if (!strcmp(argv[idx], "-o")) { + mnt_context_set_options(cxt, argv[idx + 1]); + idx += 2; + } + if (!strcmp(argv[idx], "-t")) { + /* TODO: use mnt_context_set_fstype_pattern() */ + mnt_context_set_fstype(cxt, argv[idx + 1]); + idx += 2; + } + + if (argc == idx + 1) + /* mount <mountpont>|<device> */ + mnt_context_set_target(cxt, argv[idx++]); + + else if (argc == idx + 2) { + /* mount <device> <mountpoint> */ + mnt_context_set_source(cxt, argv[idx++]); + mnt_context_set_target(cxt, argv[idx++]); + } + + /* this is unnecessary -- libmount is able to internaly + * create and manage the lock + */ + lock = mnt_context_get_lock(cxt); + if (lock) + atexit(lock_fallback); + + rc = mnt_context_mount(cxt); + if (rc) + printf("failed to mount %s\n", strerror(errno)); + else + printf("successfully mounted\n"); + + mnt_free_context(cxt); + return rc; +} + +int test_umount(struct libmnt_test *ts, int argc, char *argv[]) +{ + int idx = 1, rc = 0; + struct libmnt_context *cxt; + + if (argc < 2) + return -EINVAL; + + cxt = mnt_new_context(); + if (!cxt) + return -ENOMEM; + + if (!strcmp(argv[idx], "-t")) { + mnt_context_set_fstype(cxt, argv[idx + 1]); + idx += 2; + } + + if (!strcmp(argv[idx], "-f")) { + mnt_context_enable_force(cxt, TRUE); + idx++; + } + + if (!strcmp(argv[idx], "-l")) { + mnt_context_enable_lazy(cxt, TRUE); + idx++; + } + + if (!strcmp(argv[idx], "-r")) { + mnt_context_enable_rdonly_umount(cxt, TRUE); + idx++; + } + + if (argc == idx + 1) { + /* mount <mountpont>|<device> */ + mnt_context_set_target(cxt, argv[idx++]); + } else { + rc = -EINVAL; + goto err; + } + + lock = mnt_context_get_lock(cxt); + if (lock) + atexit(lock_fallback); + + rc = mnt_context_umount(cxt); + if (rc) + printf("failed to umount\n"); + else + printf("successfully umounted\n"); +err: + mnt_free_context(cxt); + return rc; +} + +int test_flags(struct libmnt_test *ts, int argc, char *argv[]) +{ + int idx = 1, rc = 0; + struct libmnt_context *cxt; + const char *opt = NULL; + unsigned long flags = 0; + + if (argc < 2) + return -EINVAL; + + cxt = mnt_new_context(); + if (!cxt) + return -ENOMEM; + + if (!strcmp(argv[idx], "-o")) { + mnt_context_set_options(cxt, argv[idx + 1]); + idx += 2; + } + + if (argc == idx + 1) + /* mount <mountpont>|<device> */ + mnt_context_set_target(cxt, argv[idx++]); + + rc = mnt_context_prepare_mount(cxt); + if (rc) + printf("failed to prepare mount %s\n", strerror(-rc)); + + opt = mnt_fs_get_options(cxt->fs); + if (opt) + fprintf(stdout, "options: %s\n", opt); + + mnt_context_get_mflags(cxt, &flags); + fprintf(stdout, "flags: %08lx\n", flags); + + mnt_free_context(cxt); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--mount", test_mount, "[-o <opts>] [-t <type>] <spec>|<src> <target>" }, + { "--umount", test_umount, "[-t <type>] [-f][-l][-r] <src>|<target>" }, + { "--flags", test_flags, "[-o <opts>] <spec>" }, + { NULL }}; + + + umask(S_IWGRP|S_IWOTH); /* to be compatible with mount(8) */ + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c new file mode 100644 index 000000000..58c3ec10e --- /dev/null +++ b/libmount/src/context_mount.c @@ -0,0 +1,639 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#ifdef HAVE_LIBSELINUX +#include <selinux/selinux.h> +#include <selinux/context.h> +#endif + +#include <sys/wait.h> +#include <sys/mount.h> + +#include "c.h" +#include "mountP.h" + +/* + * this has to be called after mnt_context_evaluate_permissions() + */ +static int fix_optstr(struct libmnt_context *cxt) +{ + int rc = 0, rem_se = 0; + char *next; + char *name, *val; + size_t namesz, valsz; + struct libmnt_fs *fs; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt) + return -EINVAL; + if (!cxt->fs) + return 0; + + DBG(CXT, mnt_debug_h(cxt, "mount: fixing optstr")); + + fs = cxt->fs; + + /* The propagation flags should not be used together with any other flags */ + if (cxt->mountflags & MS_PROPAGATION) + cxt->mountflags &= MS_PROPAGATION; + + if (!mnt_optstr_get_option(fs->user_optstr, "user", &val, &valsz)) { + if (val) { + cxt->orig_user = strndup(val, valsz); + if (!cxt->orig_user) { + rc = -ENOMEM; + goto done; + } + } + cxt->flags |= MNT_FL_SAVED_USER; + } + + /* + * Sync mount options with mount flags + */ + rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags, + mnt_get_builtin_optmap(MNT_LINUX_MAP)); + if (rc) + goto done; + + rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags, + mnt_get_builtin_optmap(MNT_USERSPACE_MAP)); + if (rc) + goto done; + + next = fs->fs_optstr; + +#ifdef HAVE_LIBSELINUX + rem_se = (cxt->mountflags & MS_REMOUNT) || !is_selinux_enabled(); +#endif + while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { + + if (namesz == 3 && !strncmp(name, "uid", 3)) + rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next); + else if (namesz == 3 && !strncmp(name, "gid", 3)) + rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next); +#ifdef HAVE_LIBSELINUX + else if (namesz >= 7 && (!strncmp(name, "context", 7) || + !strncmp(name, "fscontext", 9) || + !strncmp(name, "defcontext", 10) || + !strncmp(name, "rootcontext", 11))) { + if (rem_se) { + /* remove context= option */ + next = name; + rc = mnt_optstr_remove_option_at(&fs->fs_optstr, + name, val + valsz); + } else + rc = mnt_optstr_fix_secontext(&fs->fs_optstr, + val, valsz, &next); + } +#endif + if (rc) + goto done; + } + + if (!rc && cxt->user_mountflags && MNT_MS_USER) + rc = mnt_optstr_fix_user(&fs->user_optstr); + + /* refresh merged optstr */ + free(fs->optstr); + fs->optstr = NULL; + fs->optstr = mnt_fs_strdup_options(fs); +done: + DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: " + "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'", rc, + fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr)); + return rc; +} + +/* + * Converts already evalulated 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) +{ + int rc = 0; + + assert(cxt); + assert(cxt->fs); + assert(optstr); + + *optstr = mnt_fs_strdup_options(cxt->fs); + if (!*optstr) + return -ENOMEM; + + if (cxt->flags & MNT_FL_SAVED_USER) + rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user); + if (rc) { + free(*optstr); + *optstr = NULL; + } + return rc; +} + + +/* + * this has to be called before fix_optstr() + */ +static int evaluate_permissions(struct libmnt_context *cxt) +{ + unsigned long u_flags = 0; + const char *srcpath; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt) + return -EINVAL; + if (!cxt->fs) + return 0; + + DBG(CXT, mnt_debug_h(cxt, "mount: evaluating permissions")); + + mnt_context_get_user_mflags(cxt, &u_flags); + + if (!mnt_context_is_restricted(cxt)) { + /* + * superuser mount + */ + cxt->user_mountflags &= ~MNT_MS_OWNER; + cxt->user_mountflags &= ~MNT_MS_GROUP; + cxt->user_mountflags &= ~MNT_MS_USER; + cxt->user_mountflags &= ~MNT_MS_USERS; + } else { + /* + * user mount + */ + if (!(cxt->flags & MNT_FL_TAB_APPLIED)) + { + DBG(CXT, mnt_debug_h(cxt, "fstab not applied, ignore user mount")); + return -EPERM; + } + + /* + * Note that MS_OWNERSECURE and MS_SECURE mount options + * are applied by mnt_optstr_get_flags() from mnt_context_merge_mflags() + */ + + srcpath = mnt_fs_get_srcpath(cxt->fs); + if (!srcpath) + return -EINVAL; + + /* + * MS_OWNER: Allow owners to mount when fstab contains the + * owner option. Note that this should never be used in a high + * security environment, but may be useful to give people at + * the console the possibility of mounting a floppy. MS_GROUP: + * Allow members of device group to mount. (Martin Dickopp) + */ + if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) { + struct stat sb; + + if (strncmp(srcpath, "/dev/", 5) == 0 && + stat(srcpath, &sb) == 0 && + (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) || + ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid)))) + + cxt->user_mountflags |= MNT_MS_USER; + } + + if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) { + DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS")); + return -EPERM; + } + } + + return 0; +} + +/* + * mnt_context_helper_setopt() backend + * + * This function applies 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. + * + * Returns: negative number on error, 1 if @c is unknown option, 0 on success. + */ +int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg) +{ + int rc = -EINVAL; + + assert(cxt); + assert(cxt->action == MNT_ACT_MOUNT); + + switch(c) { + case 'f': + rc = mnt_context_enable_fake(cxt, TRUE); + break; + case 'n': + rc = mnt_context_disable_mtab(cxt, TRUE); + break; + case 'r': + rc = mnt_context_append_options(cxt, "ro"); + break; + case 'v': + rc = mnt_context_enable_verbose(cxt, TRUE); + break; + case 'w': + rc = mnt_context_append_options(cxt, "rw"); + break; + case 'o': + if (arg) + rc = mnt_context_append_options(cxt, arg); + break; + case 's': + rc = mnt_context_enable_sloppy(cxt, TRUE); + break; + case 't': + if (arg) + rc = mnt_context_set_fstype(cxt, arg); + break; + default: + return 1; + break; + } + + return rc; +} + +static int exec_helper(struct libmnt_context *cxt) +{ + char *o = NULL; + int rc; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + DBG(CXT, mnt_debug_h(cxt, "mount: executing helper %s", cxt->helper)); + + rc = generate_helper_optstr(cxt, &o); + if (rc) + return -EINVAL; + + DBG_FLUSH; + + switch (fork()) { + case 0: + { + const char *args[12], *type; + int i = 0; + + if (setgid(getgid()) < 0) + exit(EXIT_FAILURE); + + if (setuid(getuid()) < 0) + exit(EXIT_FAILURE); + + type = mnt_fs_get_fstype(cxt->fs); + + args[i++] = cxt->helper; /* 1 */ + args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */ + args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */ + + if (mnt_context_is_sloppy(cxt)) + args[i++] = "-s"; /* 4 */ + if (mnt_context_is_fake(cxt)) + args[i++] = "-f"; /* 5 */ + if (mnt_context_is_nomtab(cxt)) + args[i++] = "-n"; /* 6 */ + if (mnt_context_is_verbose(cxt)) + args[i++] = "-v"; /* 7 */ + if (o) { + args[i++] = "-o"; /* 8 */ + args[i++] = o; /* 9 */ + } + if (type && !endswith(cxt->helper, type)) { + args[i++] = "-t"; /* 10 */ + args[i++] = type; /* 11 */ + } + args[i] = NULL; /* 12 */ +#ifdef CONFIG_LIBMOUNT_DEBUG + i = 0; + for (i = 0; args[i]; i++) + DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"", + i, args[i])); +#endif + DBG_FLUSH; + execv(cxt->helper, (char * const *) args); + exit(EXIT_FAILURE); + } + default: + { + int st; + wait(&st); + cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; + + DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]", + cxt->helper, cxt->helper_status)); + cxt->helper_exec_status = rc = 0; + break; + } + + case -1: + cxt->helper_exec_status = rc = -errno; + DBG(CXT, mnt_debug_h(cxt, "fork() failed")); + break; + } + + return rc; +} + +/* + * The default is to use fstype from cxt->fs, this could be overwritten by + * @try_type argument. + */ +static int do_mount(struct libmnt_context *cxt, const char *try_type) +{ + int rc = 0; + const char *src, *target, *type; + unsigned long flags; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (try_type && !cxt->helper) { + rc = mnt_context_prepare_helper(cxt, "mount", try_type); + if (!rc) + return rc; + } + if (cxt->helper) + return exec_helper(cxt); + + flags = cxt->mountflags; + src = mnt_fs_get_srcpath(cxt->fs); + target = mnt_fs_get_target(cxt->fs); + + if (!src || !target) + return -EINVAL; + + type = try_type ? : mnt_fs_get_fstype(cxt->fs); + + if (!(flags & MS_MGC_MSK)) + flags |= MS_MGC_VAL; + + DBG(CXT, mnt_debug_h(cxt, "%smount(2) " + "[source=%s, target=%s, type=%s, " + " mountflags=0x%08lx, mountdata=%s]", + (cxt->flags & MNT_FL_FAKE) ? "(FAKE) " : "", + src, target, type, + flags, cxt->mountdata ? "yes" : "<none>")); + + if (cxt->flags & MNT_FL_FAKE) + cxt->syscall_status = 0; + else { + if (mount(src, target, type, flags, cxt->mountdata)) { + cxt->syscall_status = -errno; + DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]", + -cxt->syscall_status)); + return -cxt->syscall_status; + } + DBG(CXT, mnt_debug_h(cxt, "mount(2) success")); + cxt->syscall_status = 0; + } + + if (try_type && cxt->update) { + struct libmnt_fs *fs = mnt_update_get_fs(cxt->update); + if (fs) + rc = mnt_fs_set_fstype(fs, try_type); + } + + /* TODO: check if the result is really read-only/read-write + * and if necessary update cxt->mountflags + */ + + return rc; +} + +static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern) +{ + int neg = pattern && strncmp(pattern, "no", 2) == 0; + int rc = -EINVAL; + char **filesystems, **fp; + + assert(cxt); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!neg && pattern) { + /* + * try all types from the list + */ + char *p, *p0; + + DBG(CXT, mnt_debug_h(cxt, "tring mount by FS pattern list")); + + p0 = p = strdup(pattern); + if (!p) + return -ENOMEM; + do { + char *end = strchr(p, ','); + if (end) + *end = '\0'; + rc = do_mount(cxt, p); + p = end ? end + 1 : NULL; + } while (!mnt_context_get_status(cxt) && p); + + free(p0); + + if (mnt_context_get_status(cxt)) + return rc; + } + + /* + * try /etc/filesystems and /proc/filesystems + */ + DBG(CXT, mnt_debug_h(cxt, "tring mount by filesystems lists")); + + rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL); + if (rc) + return rc; + + for (fp = filesystems; *fp; fp++) { + rc = do_mount(cxt, *fp); + if (mnt_context_get_status(cxt)) + break; + } + mnt_free_filesystems(filesystems); + return rc; +} + +/** + * mnt_context_prepare_mount: + * @cxt: context + * + * Prepare context for mounting, unnecessary for mnt_context_mount(). + * + * Returns: negative number on error, zero on success + */ +int mnt_context_prepare_mount(struct libmnt_context *cxt) +{ + int rc = -EINVAL; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper_exec_status == 1); + assert(cxt->syscall_status == 1); + + if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP)) + return -EINVAL; + if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs)) + return -EINVAL; + if (cxt->flags & MNT_FL_PREPARED) + return 0; + + cxt->action = MNT_ACT_MOUNT; + + DBG(CXT, mnt_debug_h(cxt, "mount: preparing")); + + /* TODO: fstab is unnecessary for MS_{MOVE,BIND,STARED,...} */ + rc = mnt_context_apply_fstab(cxt); + if (!rc) + rc = mnt_context_merge_mflags(cxt); + if (!rc) + rc = evaluate_permissions(cxt); + if (!rc) + rc = fix_optstr(cxt); + if (!rc) + rc = mnt_context_prepare_srcpath(cxt); + if (!rc) + rc = mnt_context_prepare_target(cxt); + if (!rc) + rc = mnt_context_guess_fstype(cxt); + if (!rc) + rc = mnt_context_prepare_helper(cxt, "mount", NULL); + if (rc) { + DBG(CXT, mnt_debug_h(cxt, "mount: preparing failed")); + return rc; + } + cxt->flags |= MNT_FL_PREPARED; + return rc; +} + +/** + * mnt_context_do_mount + * @cxt: context + * + * Call mount(2) or mount.<type> helper. Unnecessary for mnt_context_mount(). + * + * WARNING: non-zero return code does not mean that mount(2) syscall or + * umount.type helper wasn't sucessfully called. + * + * Check mnt_context_get_status() after error! +* + * Returns: 0 on success; + * >0 in case of mount(2) error (returns syscall errno), + * <0 in case of other errors. + */ +int mnt_context_do_mount(struct libmnt_context *cxt) +{ + const char *type; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper_exec_status == 1); + assert(cxt->syscall_status == 1); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + assert((cxt->flags & MNT_FL_PREPARED)); + assert((cxt->action == MNT_ACT_MOUNT)); + + DBG(CXT, mnt_debug_h(cxt, "mount: do mount")); + + if (!(cxt->flags & MNT_FL_MOUNTDATA)) + cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs); + + type = mnt_fs_get_fstype(cxt->fs); + if (type) + return do_mount(cxt, NULL); + + return do_mount_by_pattern(cxt, cxt->fstype_pattern); +} + +/** + * mnt_context_finalize_mount: + * @cxt: context + * + * Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called + * after mnt_context_do_mount(). See also mnt_context_set_syscall_status(). + * + * Returns: negative number on error, 0 on success. + */ +int mnt_context_finalize_mount(struct libmnt_context *cxt) +{ + int rc; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + assert((cxt->flags & MNT_FL_PREPARED)); + + rc = mnt_context_prepare_update(cxt); + if (!rc) + rc = mnt_context_update_tabs(cxt);; + return rc; +} + +/** + * mnt_context_mount: + * @cxt: mount context + * + * High-level, mounts filesystem by mount(2) or fork()+exec(/sbin/mount.type). + * + * This is similar to: + * + * mnt_context_prepare_mount(cxt); + * mnt_context_do_mount(cxt); + * mnt_context_finalize_mount(cxt); + * + * See also mnt_context_disable_helpers(). + * + * WARNING: non-zero return code does not mean that mount(2) syscall or + * mount.type helper wasn't sucessfully called. + * + * Check mnt_context_get_status() after error! +* + * Returns: 0 on success; + * >0 in case of mount(2) error (returns syscall errno), + * <0 in case of other errors. + */ +int mnt_context_mount(struct libmnt_context *cxt) +{ + int rc; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper_exec_status == 1); + assert(cxt->syscall_status == 1); + + rc = mnt_context_prepare_mount(cxt); + if (!rc) + 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; +} + diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c new file mode 100644 index 000000000..e10af52f2 --- /dev/null +++ b/libmount/src/context_umount.c @@ -0,0 +1,739 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <sys/wait.h> +#include <sys/mount.h> + +#include "c.h" +#include "pathnames.h" +#include "strutils.h" +#include "mountP.h" + +/* + * umount2 flags + */ +#ifndef MNT_FORCE +# define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */ +#endif + +#ifndef MNT_DETACH +# define MNT_DETACH 0x00000002 /* Just detach from the tree */ +#endif + +#ifndef UMOUNT_NOFOLLOW +# define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */ +#endif + +#ifndef UMOUNT_UNUSED +# define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */ +#endif + + +static int lookup_umount_fs(struct libmnt_context *cxt) +{ + int rc; + const char *tgt; + struct libmnt_table *mtab = NULL; + struct libmnt_fs *fs; + + assert(cxt); + assert(cxt->fs); + + DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS")); + + tgt = mnt_fs_get_target(cxt->fs); + if (!tgt) { + DBG(CXT, mnt_debug_h(cxt, "umount: undefined target")); + return -EINVAL; + } + rc = mnt_context_get_mtab(cxt, &mtab); + if (rc) { + DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab")); + return rc; + } + fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD); + if (!fs) { + /* maybe the option is source rather than target (mountpoint) */ + fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD); + + if (fs) { + struct libmnt_fs *fs1 = mnt_table_find_target(mtab, + mnt_fs_get_target(fs), + MNT_ITER_BACKWARD); + if (!fs1) { + DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!")); + return -EINVAL; + } + if (fs != fs1) { + /* Something was stacked over `file' on the + * same mount point. */ + DBG(CXT, mnt_debug_h(cxt, + "umount: %s: %s is mounted " + "over it on the same point", + tgt, mnt_fs_get_source(fs1))); + return -EINVAL; + } + } + } + + if (!fs) { + DBG(CXT, mnt_debug_h(cxt, "umount: cannot found %s in mtab", tgt)); + return 0; + } + + /* copy from mtab to our FS description + */ + mnt_fs_set_source(cxt->fs, NULL); + mnt_fs_set_target(cxt->fs, NULL); + + if (!mnt_copy_fs(cxt->fs, fs)) { + DBG(CXT, mnt_debug_h(cxt, "umount: failed to copy FS")); + return -errno; + } + + DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied")); + cxt->flags |= MNT_FL_TAB_APPLIED; + return rc; +} + +/* check if @devname is loopdev and if the device is associated + * with a source from @fstab_fs + * + * TODO : move this to loopdev.c + */ +static int mnt_loopdev_associated_fs(const char *devname, struct libmnt_fs *fs) +{ + uintmax_t offset = 0; + const char *src; + char *val, *optstr; + size_t valsz; + + /* check if it begins with /dev/loop */ + if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP))) + return 0; + + src = mnt_fs_get_srcpath(fs); + if (!src) + return 0; + + /* check for offset option in @fs */ + optstr = (char *) mnt_fs_get_user_options(fs); + if (optstr && !mnt_optstr_get_option(optstr, "offset=", &val, &valsz)) { + int rc; + + val = strndup(val, valsz); + if (!val) + return 0; + rc = strtosize(val, &offset); + free(val); + if (rc) + return 0; + } + + /* TODO: + * if (mnt_loopdev_associated_file(devname, src, offset)) + * return 1; + */ + return 0; +} + +static int prepare_helper_from_options(struct libmnt_context *cxt, + const char *name) +{ + char *suffix = NULL; + const char *opts; + size_t valsz; + + if (cxt->flags & MNT_FL_NOHELPERS) + return 0; + + opts = mnt_fs_get_user_options(cxt->fs); + if (!opts) + return 0; + + if (mnt_optstr_get_option((char *) opts, name, &suffix, &valsz)) + return 0; + + suffix = strndup(suffix, valsz); + if (!suffix) + return -ENOMEM; + + DBG(CXT, mnt_debug_h(cxt, "umount: umount.%s %s requested", suffix, name)); + + return mnt_context_prepare_helper(cxt, "umount", suffix); +} + +/* + * Note that cxt->fs contains relevant mtab entry! + */ +static int evaluate_permissions(struct libmnt_context *cxt) +{ + struct libmnt_table *fstab; + unsigned long u_flags = 0; + const char *tgt, *src, *optstr; + int rc, ok = 0; + struct libmnt_fs *fs; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!cxt || !cxt->fs) + return -EINVAL; + + if (!mnt_context_is_restricted(cxt)) + return 0; /* superuser mount */ + + DBG(CXT, mnt_debug_h(cxt, "umount: evaluating permissions")); + + if (!(cxt->flags & MNT_FL_TAB_APPLIED)) { + DBG(CXT, mnt_debug_h(cxt, + "cannot found %s in mtab and you are not root", + mnt_fs_get_target(cxt->fs))); + goto eperm; + } + + if (cxt->user_mountflags & MNT_MS_UHELPER) { + /* on uhelper= mount option based helper */ + rc = prepare_helper_from_options(cxt, "uhelper"); + if (rc) + return rc; + if (cxt->helper) + return 0; /* we'll call /sbin/umount.<uhelper> */ + } + + /* + * User mounts has to be in /etc/fstab + */ + rc = mnt_context_get_fstab(cxt, &fstab); + if (rc) + return rc; + + tgt = mnt_fs_get_target(cxt->fs); + src = mnt_fs_get_source(cxt->fs); + + if (mnt_fs_get_bindsrc(cxt->fs)) { + src = mnt_fs_get_bindsrc(cxt->fs); + DBG(CXT, mnt_debug_h(cxt, + "umount: using bind source: %s", src)); + } + + /* If fstab contains the two lines + * /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. + */ + 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 + * 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 */ + + if (!dev || !mnt_loopdev_associated_fs(dev, fs)) + fs = NULL; + } + if (!fs) { + DBG(CXT, mnt_debug_h(cxt, + "umount %s: mtab disagrees with fstab", + tgt)); + goto eperm; + } + } + + /* + * User mounting and unmounting is allowed only if fstab contains one + * of the options `user', `users' or `owner' or `group'. + * + * The option `users' allows arbitrary users to mount and unmount - + * this may be a security risk. + * + * The options `user', `owner' and `group' only allow unmounting by the + * user that mounted (visible in mtab). + */ + optstr = mnt_fs_get_user_options(fs); /* FSTAB mount options! */ + if (!optstr) + goto eperm; + + if (mnt_optstr_get_flags(optstr, &u_flags, + mnt_get_builtin_optmap(MNT_USERSPACE_MAP))) + goto eperm; + + if (u_flags & MNT_MS_USERS) { + DBG(CXT, mnt_debug_h(cxt, + "umount: promiscuous setting ('users') in fstab")); + return 0; + } + /* + * Check user=<username> setting from mtab if there is user, owner or + * group option in /etc/fstab + */ + if ((u_flags & MNT_MS_USER) || (u_flags & MNT_MS_OWNER) || + (u_flags & MNT_MS_GROUP)) { + + char *curr_user = NULL; + char *mtab_user = NULL; + size_t sz; + + DBG(CXT, mnt_debug_h(cxt, + "umount: checking user=<username> from mtab")); + + curr_user = mnt_get_username(getuid()); + + if (!curr_user) { + DBG(CXT, mnt_debug_h(cxt, "umount %s: cannot " + "convert %d to username", tgt, getuid())); + goto eperm; + } + + /* get options from mtab */ + optstr = mnt_fs_get_user_options(cxt->fs); + if (optstr && !mnt_optstr_get_option((char *) optstr, + "user", &mtab_user, &sz) && sz) + ok = !strncmp(curr_user, mtab_user, sz); + } + + if (ok) { + DBG(CXT, mnt_debug_h(cxt, "umount %s is allowed", tgt)); + return 0; + } +eperm: + DBG(CXT, mnt_debug_h(cxt, "umount is not allowed for you")); + return -EPERM; +} + +static int exec_helper(struct libmnt_context *cxt) +{ + int rc; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + assert(cxt->helper_exec_status == 1); + + DBG_FLUSH; + + switch (fork()) { + case 0: + { + const char *args[10], *type; + int i = 0; + + if (setgid(getgid()) < 0) + exit(EXIT_FAILURE); + + if (setuid(getuid()) < 0) + exit(EXIT_FAILURE); + + type = mnt_fs_get_fstype(cxt->fs); + + args[i++] = cxt->helper; /* 1 */ + args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */ + + if (cxt->flags & MNT_FL_NOMTAB) + args[i++] = "-n"; /* 3 */ + if (cxt->flags & MNT_FL_LAZY) + args[i++] = "-l"; /* 4 */ + if (cxt->flags & MNT_FL_FORCE) + args[i++] = "-f"; /* 5 */ + if (cxt->flags & MNT_FL_VERBOSE) + args[i++] = "-v"; /* 6 */ + if (cxt->flags & MNT_FL_RDONLY_UMOUNT) + args[i++] = "-r"; /* 7 */ + if (type && !endswith(cxt->helper, type)) { + args[i++] = "-t"; /* 8 */ + args[i++] = (char *) type; /* 9 */ + } + + args[i] = NULL; /* 10 */ +#ifdef CONFIG_LIBMOUNT_DEBUG + i = 0; + for (i = 0; args[i]; i++) + DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"", + i, args[i])); +#endif + DBG_FLUSH; + execv(cxt->helper, (char * const *) args); + exit(EXIT_FAILURE); + } + default: + { + int st; + wait(&st); + cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; + + DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]", + cxt->helper, cxt->helper_status)); + cxt->helper_exec_status = rc = 0; + break; + } + + case -1: + cxt->helper_exec_status = rc = -errno; + DBG(CXT, mnt_debug_h(cxt, "fork() failed")); + break; + } + + return rc; +} + +/* + * mnt_context_helper_setopt() backend. + * + * This function applies umount.<type> command line option (for example parsed + * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and + * then 1 is returned. + * + * Returns: negative number on error, 1 if @c is unknown option, 0 on success. + */ +int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg) +{ + int rc = -EINVAL; + + assert(cxt); + assert(cxt->action == MNT_ACT_UMOUNT); + + switch(c) { + case 'n': + rc = mnt_context_disable_mtab(cxt, TRUE); + break; + case 'l': + rc = mnt_context_enable_lazy(cxt, TRUE); + break; + case 'f': + rc = mnt_context_enable_fake(cxt, TRUE); + break; + case 'v': + rc = mnt_context_enable_verbose(cxt, TRUE); + break; + case 'r': + rc = mnt_context_enable_rdonly_umount(cxt, TRUE); + break; + case 't': + if (arg) + rc = mnt_context_set_fstype(cxt, arg); + break; + default: + return 1; + break; + } + + return rc; +} + +/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */ +static int umount_nofollow_support(void) +{ + int res = umount2("", UMOUNT_UNUSED); + if (res != -1 || errno != EINVAL) + return 0; + + res = umount2("", UMOUNT_NOFOLLOW); + if (res != -1 || errno != ENOENT) + return 0; + + return 1; +} + +static int do_umount(struct libmnt_context *cxt) +{ + int rc = 0, flags = 0; + const char *src, *target; + char *tgtbuf = NULL; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + assert(cxt->syscall_status == 1); + + if (cxt->helper) + return exec_helper(cxt); + + src = mnt_fs_get_srcpath(cxt->fs); + target = mnt_fs_get_target(cxt->fs); + + if (!target) + return -EINVAL; + + if (cxt->flags & MNT_FL_FAKE) + return 0; + + DBG(CXT, mnt_debug_h(cxt, "do umount")); + + if (cxt->restricted) { + /* + * extra paranoa for non-root users + * -- chdir to the parent of the mountpoint and use NOFOLLOW + * flag to avoid races and symlink attacks. + */ + if (umount_nofollow_support()) + flags |= UMOUNT_NOFOLLOW; + + rc = mnt_chdir_to_parent(target, &tgtbuf); + if (rc) + return rc; + target = tgtbuf; + } + + if (cxt->flags & MNT_FL_LAZY) + flags |= MNT_DETACH; + + else if (cxt->flags & MNT_FL_FORCE) + flags |= MNT_FORCE; + + DBG(CXT, mnt_debug_h(cxt, "umount(2) [target='%s', flags=0x%08x]", + target, flags)); + + rc = flags ? umount2(target, flags) : umount(target); + if (rc < 0) + cxt->syscall_status = -errno; + + free(tgtbuf); + + /* + * try remount read-only + */ + if (rc < 0 && cxt->syscall_status == -EBUSY && + (cxt->flags & MNT_FL_RDONLY_UMOUNT) && src) { + + cxt->mountflags |= MS_REMOUNT | MS_RDONLY; + cxt->flags &= ~MNT_FL_LOOPDEL; + DBG(CXT, mnt_debug_h(cxt, + "umount(2) failed [errno=%d] -- tring remount read-only", + -cxt->syscall_status)); + + rc = mount(src, mnt_fs_get_target(cxt->fs), NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (rc < 0) { + cxt->syscall_status = -errno; + DBG(CXT, mnt_debug_h(cxt, + "read-only re-mount(2) failed [errno=%d]", + -cxt->syscall_status)); + + return -cxt->syscall_status; + } + cxt->syscall_status = 0; + DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) success")); + return 0; + } + + if (rc < 0) { + DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d]", + -cxt->syscall_status)); + return -cxt->syscall_status; + } + + cxt->syscall_status = 0; + DBG(CXT, mnt_debug_h(cxt, "umount(2) success")); + return 0; +} + +/** + * mnt_context_prepare_umount: + * @cxt: mount context + * + * Prepare context for umounting, unnecessary for mnt_context_umount(). + * + * Returns: 0 on success, and negative number in case of error. + */ +int mnt_context_prepare_umount(struct libmnt_context *cxt) +{ + int rc; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper_exec_status == 1); + assert(cxt->syscall_status == 1); + + if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP)) + return -EINVAL; + if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs)) + return -EINVAL; + if (cxt->flags & MNT_FL_PREPARED) + return 0; + + free(cxt->helper); /* be paranoid */ + cxt->helper = NULL; + cxt->action = MNT_ACT_UMOUNT; + + rc = lookup_umount_fs(cxt); + if (!rc) + rc = mnt_context_merge_mflags(cxt); + if (!rc) + rc = evaluate_permissions(cxt); + if (!rc) + rc = mnt_context_prepare_target(cxt); + + if (!rc && !cxt->helper) { + + if (cxt->user_mountflags & MNT_MS_HELPER) + /* on helper= mount option based helper */ + rc = prepare_helper_from_options(cxt, "helper"); + + if (!rc && !cxt->helper) + /* on fstype based helper */ + rc = mnt_context_prepare_helper(cxt, "umount", NULL); + } + +/* TODO + if ((cxt->flags & MNT_FL_LOOPDEL) && + (!mnt_is_loopdev(src) || mnt_loopdev_is_autoclear(src))) + cxt->flags &= ~MNT_FL_LOOPDEL; +*/ + if (rc) { + DBG(CXT, mnt_debug_h(cxt, "umount: preparing failed")); + return rc; + } + cxt->flags |= MNT_FL_PREPARED; + return rc; +} + +/** + * mnt_context_do_umount: + * @cxt: mount context + * + * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type). + * Unnecessary for mnt_context_umount(). + * + * See also mnt_context_disable_helpers(). + * + * WARNING: non-zero return code does not mean that umount(2) syscall or + * umount.type helper wasn't sucessfully called. + * + * Check mnt_context_get_status() after error! +* + * Returns: 0 on success; + * >0 in case of umount(2) error (returns syscall errno), + * <0 in case of other errors. + */ +int mnt_context_do_umount(struct libmnt_context *cxt) +{ + int rc; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper_exec_status == 1); + assert(cxt->syscall_status == 1); + assert((cxt->flags & MNT_FL_PREPARED)); + assert((cxt->action == MNT_ACT_UMOUNT)); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + rc = do_umount(cxt); + if (rc) + return rc; +/* TODO + if (cxt->flags & MNT_FL_LOOPDEL) + rc = mnt_loopdev_clean(mnt_fs_get_source(cxt->fs)); +*/ + if (cxt->flags & MNT_FL_NOMTAB) + return rc; + + if ((cxt->flags & MNT_FL_RDONLY_UMOUNT) && + (cxt->mountflags & (MS_RDONLY | MS_REMOUNT))) { + /* + * fix options, remount --> read-only mount + */ + const char *o = mnt_fs_get_options(cxt->fs); + char *n = o ? strdup(o) : NULL; + + DBG(CXT, mnt_debug_h(cxt, "fix remount-on-umount update")); + + if (n) + mnt_optstr_remove_option(&n, "rw"); + rc = mnt_optstr_prepend_option(&n, "ro", NULL); + if (!rc) + rc = mnt_fs_set_options(cxt->fs, n); + + /* use "remount" instead of "umount" in /etc/mtab */ + if (!rc && cxt->update && cxt->mtab_writable) + rc = mnt_update_set_fs(cxt->update, + cxt->mountflags, NULL, cxt->fs); + } + + return rc; +} + +/** + * mnt_context_finalize_umount: + * @cxt: context + * + * Mtab update, etc. Unnecessary for mnt_context_umount(), but should be called + * after mnt_context_do_umount(). See also mnt_context_set_syscall_status(). + * + * Returns: negative number on error, 0 on success. + */ +int mnt_context_finalize_umount(struct libmnt_context *cxt) +{ + int rc; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_PREPARED)); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + rc = mnt_context_prepare_update(cxt); + if (!rc) + rc = mnt_context_update_tabs(cxt);; + return rc; +} + + +/** + * mnt_context_umount: + * @cxt: umount context + * + * High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type). + * + * This is similar to: + * + * mnt_context_prepare_umount(cxt); + * mnt_context_do_umount(cxt); + * mnt_context_finalize_umount(cxt); + * + * See also mnt_context_disable_helpers(). + * + * WARNING: non-zero return code does not mean that umount(2) syscall or + * umount.type helper wasn't sucessfully called. + * + * Check mnt_context_get_status() after error! + * + * Returns: 0 on success; + * >0 in case of umount(2) error (returns syscall errno), + * <0 in case of other errors. + */ +int mnt_context_umount(struct libmnt_context *cxt) +{ + int rc; + + assert(cxt); + assert(cxt->fs); + assert(cxt->helper_exec_status == 1); + assert(cxt->syscall_status == 1); + + rc = mnt_context_prepare_umount(cxt); + if (!rc) + rc = mnt_context_prepare_update(cxt); + if (!rc) + rc = mnt_context_do_umount(cxt); + if (!rc) + rc = mnt_context_update_tabs(cxt); + return rc; +} diff --git a/libmount/src/fs.c b/libmount/src/fs.c new file mode 100644 index 000000000..02f401d95 --- /dev/null +++ b/libmount/src/fs.c @@ -0,0 +1,1349 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: fs + * @title: Filesystem + * @short_description: struct libmnt_fs represents one entry in fstab/mtab/mountinfo + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <blkid.h> +#include <stddef.h> + +#include "nls.h" +#include "mountP.h" + +/** + * mnt_new_fs: + * + * Returns: newly allocated struct libmnt_fs. + */ +struct libmnt_fs *mnt_new_fs(void) +{ + struct libmnt_fs *fs = calloc(1, sizeof(*fs)); + if (!fs) + return NULL; + + /*DBG(FS, mnt_debug_h(fs, "alloc"));*/ + INIT_LIST_HEAD(&fs->ents); + return fs; +} + +/** + * mnt_free_fs: + * @fs: fs pointer + * + * Deallocates the fs. + */ +void mnt_free_fs(struct libmnt_fs *fs) +{ + if (!fs) + return; + list_del(&fs->ents); + + /*DBG(FS, mnt_debug_h(fs, "free"));*/ + + free(fs->source); + free(fs->bindsrc); + free(fs->tagname); + free(fs->tagval); + free(fs->root); + free(fs->target); + free(fs->fstype); + free(fs->optstr); + free(fs->vfs_optstr); + free(fs->fs_optstr); + free(fs->user_optstr); + free(fs->attrs); + + free(fs); +} + +/** + * mnt_reset_fs: + * @fs: fs pointer + * + * Resets (zeroize) @fs. + */ +void mnt_reset_fs(struct libmnt_fs *fs) +{ + if (fs) + memset(fs, 0, sizeof(*fs)); +} + +static inline int update_str(char **dest, const char *src) +{ + size_t sz; + char *x; + + assert(dest); + + if (!src) { + free(*dest); + *dest = NULL; + return 0; /* source (old) is empty */ + } + + sz = strlen(src) + 1; + x = realloc(*dest, sz); + if (!x) + return -ENOMEM; + *dest = x; + memcpy(*dest, src, sz); + return 0; +} + +static inline int cpy_str_at_offset(void *new, const void *old, size_t offset) +{ + char **o = (char **) (old + offset); + char **n = (char **) (new + offset); + + if (*n) + return 0; /* already set, not overwrite */ + + return update_str(n, *o); +} + +/** + * mnt_copy_fs: + * @dest: destination FS + * @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. + * + * This function does not copy userdata (se mnt_fs_set_userdata()). A new copy is + * not linked with any existing mnt_tab. + * + * Returns: @dest or NULL in case of error + */ +struct libmnt_fs *mnt_copy_fs(struct libmnt_fs *dest, + const struct libmnt_fs *src) +{ + const struct libmnt_fs *org = dest; + + if (!dest) { + dest = mnt_new_fs(); + if (!dest) + return NULL; + } + + /*DBG(FS, mnt_debug_h(dest, "copy from %p", src));*/ + + dest->id = src->id; + dest->parent = src->parent; + dest->devno = src->devno; + + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, source))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, tagname))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, tagval))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, root))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, target))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, fstype))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, optstr))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, vfs_optstr))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, fs_optstr))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, user_optstr))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, attrs))) + goto err; + if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, bindsrc))) + goto err; + + dest->freq = src->freq; + dest->passno = src->passno; + dest->flags = src->flags; + + return dest; +err: + if (!org) + mnt_free_fs(dest); + return NULL; +} + +/* + * This function copies all @fs description except information that does not + * belong to /etc/mtab (e.g. VFS and userspace mount options with MNT_NOMTAB + * mask). + * + * Returns: copy of @fs. + */ +struct libmnt_fs *mnt_copy_mtab_fs(const struct libmnt_fs *fs) +{ + struct libmnt_fs *n = mnt_new_fs(); + + if (!n) + return NULL; + + if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, source))) + goto err; + if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, target))) + goto err; + if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, fstype))) + goto err; + + if (fs->vfs_optstr) { + char *p = NULL; + mnt_optstr_get_options(fs->vfs_optstr, &p, + mnt_get_builtin_optmap(MNT_LINUX_MAP), + MNT_NOMTAB); + n->vfs_optstr = p; + } + + if (fs->user_optstr) { + char *p = NULL; + mnt_optstr_get_options(fs->user_optstr, &p, + mnt_get_builtin_optmap(MNT_USERSPACE_MAP), + MNT_NOMTAB); + n->user_optstr = p; + } + + if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, fs_optstr))) + goto err; + + /* we cannot copy original optstr, the new optstr has to be without + * non-mtab options -- so, let's generate a new string */ + n->optstr = mnt_fs_strdup_options(n); + + n->freq = fs->freq; + n->passno = fs->passno; + n->flags = fs->flags; + + return n; +err: + mnt_free_fs(n); + return NULL; + +} + +/** + * mnt_fs_get_userdata: + * @fs: struct libmnt_file instance + * + * Returns: private data set by mnt_fs_set_userdata() or NULL. + */ +void *mnt_fs_get_userdata(struct libmnt_fs *fs) +{ + return fs ? fs->userdata : NULL; +} + +/** + * mnt_fs_set_userdata: + * @fs: struct libmnt_file instance + * @data: user data + * + * The "userdata" are library independent data. + * + * Returns: 0 or negative number in case of error (if @fs is NULL). + */ +int mnt_fs_set_userdata(struct libmnt_fs *fs, void *data) +{ + if (!fs) + return -EINVAL; + fs->userdata = data; + return 0; +} + +/** + * mnt_fs_get_srcpath: + * @fs: struct libmnt_file (fstab/mtab/mountinfo) fs + * + * The mount "source path" is: + * - a directory for 'bind' mounts (in fstab or mtab only) + * - a device name for standard mounts + * + * See also mnt_fs_get_tag() and mnt_fs_get_source(). + * + * Returns: mount source path or NULL in case of error or when the path + * is not defined. + */ +const char *mnt_fs_get_srcpath(struct libmnt_fs *fs) +{ + assert(fs); + if (!fs) + return NULL; + + /* fstab-like fs */ + if (fs->tagname) + return NULL; /* the source contains a "NAME=value" */ + return fs->source; +} + +/** + * mnt_fs_get_source: + * @fs: struct libmnt_file (fstab/mtab/mountinfo) fs + * + * Returns: mount source. Note that the source could be unparsed TAG + * (LABEL/UUID). See also mnt_fs_get_srcpath() and mnt_fs_get_tag(). + */ +const char *mnt_fs_get_source(struct libmnt_fs *fs) +{ + return fs ? fs->source : NULL; +} + +/* Used by parser struct libmnt_file ONLY (@source has to be allocated) */ +int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source) +{ + char *t = NULL, *v = NULL; + + assert(fs); + + if (source && !strcmp(source, "none")) + source = NULL; + + if (source && strchr(source, '=')) { + if (blkid_parse_tag_string(source, &t, &v) != 0) + return -1; + } + + if (fs->source != source) + free(fs->source); + + free(fs->tagname); + free(fs->tagval); + + fs->source = source; + fs->tagname = t; + fs->tagval = v; + return 0; +} + +/** + * mnt_fs_set_source: + * @fs: fstab/mtab/mountinfo entry + * @source: new source + * + * This function creates a private copy (strdup()) of @source. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_source(struct libmnt_fs *fs, const char *source) +{ + char *p = NULL; + int rc; + + if (!fs) + return -EINVAL; + if (source) { + p = strdup(source); + if (!p) + return -ENOMEM; + } + + rc = __mnt_fs_set_source_ptr(fs, p); + if (rc) + free(p); + return rc; +} + +/** + * mnt_fs_get_tag: + * @fs: fs + * @name: returns pointer to NAME string + * @value: returns pointer to VALUE string + * + * "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. + * + * See also mnt_fs_get_source(). + * + * <informalexample> + * <programlisting> + * char *src; + * struct libmnt_fs *fs = mnt_table_find_target(tb, "/home", MNT_ITER_FORWARD); + * + * if (!fs) + * goto err; + * + * src = mnt_fs_get_srcpath(fs); + * if (!src) { + * char *tag, *val; + * if (mnt_fs_get_tag(fs, &tag, &val) == 0) + * printf("%s: %s\n", tag, val); // LABEL or UUID + * } else + * printf("device: %s\n", src); // device or bind path + * </programlisting> + * </informalexample> + * + * Returns: 0 on success or negative number in case that a TAG is not defined. + */ +int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, const char **value) +{ + if (fs == NULL || !fs->tagname) + return -EINVAL; + if (name) + *name = fs->tagname; + if (value) + *value = fs->tagval; + return 0; +} + +/** + * mnt_fs_get_target: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: pointer to mountpoint path or NULL + */ +const char *mnt_fs_get_target(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->target : NULL; +} + +/** + * mnt_fs_set_target: + * @fs: fstab/mtab/mountinfo entry + * @target: mountpoint + * + * This function creates a private copy (strdup()) of @target. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_target(struct libmnt_fs *fs, const char *target) +{ + char *p = NULL; + + assert(fs); + + if (!fs) + return -EINVAL; + if (target) { + p = strdup(target); + if (!p) + return -ENOMEM; + } + free(fs->target); + fs->target = p; + + return 0; +} + +int __mnt_fs_get_flags(struct libmnt_fs *fs) +{ + return fs ? fs->flags : 0; +} + +int __mnt_fs_set_flags(struct libmnt_fs *fs, int flags) +{ + if (fs) { + fs->flags = flags; + return 0; + } + return -EINVAL; +} + +/** + * mnt_fs_is_kernel: + * @fs: filesystem + * + * Returns: 1 if the filesystem description is read from kernel e.g. /proc/mounts. + */ +int mnt_fs_is_kernel(struct libmnt_fs *fs) +{ + return __mnt_fs_get_flags(fs) & MNT_FS_KERNEL; +} + +/** + * mnt_fs_get_fstype: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: pointer to filesystem type. + */ +const char *mnt_fs_get_fstype(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->fstype : NULL; +} + +/* Used by struct libmnt_file parser only */ +int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype) +{ + assert(fs); + + if (fstype != fs->fstype) + free(fs->fstype); + + fs->fstype = fstype; + fs->flags &= ~MNT_FS_PSEUDO; + fs->flags &= ~MNT_FS_NET; + + /* save info about pseudo filesystems */ + if (fs->fstype) { + if (mnt_fstype_is_pseudofs(fs->fstype)) + fs->flags |= MNT_FS_PSEUDO; + else if (mnt_fstype_is_netfs(fs->fstype)) + fs->flags |= MNT_FS_NET; + else if (!strcmp(fs->fstype, "swap")) + fs->flags |= MNT_FS_SWAP; + } + return 0; +} + +/** + * mnt_fs_set_fstype: + * @fs: fstab/mtab/mountinfo entry + * @fstype: filesystem type + * + * This function creates a private copy (strdup()) of @fstype. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_fstype(struct libmnt_fs *fs, const char *fstype) +{ + char *p = NULL; + + if (!fs) + return -EINVAL; + if (fstype) { + p = strdup(fstype); + if (!p) + return -ENOMEM; + } + return __mnt_fs_set_fstype_ptr(fs, p); +} + +/* + * Merges @vfs and @fs options strings into a new string. + * This function cares about 'ro/rw' options. The 'ro' is + * always used if @vfs or @fs is read-only. + * For example: + * + * mnt_merge_optstr("rw,noexec", "ro,journal=update") + * + * returns: "ro,noexec,journal=update" + * + * mnt_merge_optstr("rw,noexec", "rw,journal=update") + * + * returns: "rw,noexec,journal=update" + */ +static char *merge_optstr(const char *vfs, const char *fs) +{ + char *res, *p; + size_t sz; + int ro = 0, rw = 0; + + if (!vfs && !fs) + return NULL; + if (!vfs || !fs) + return strdup(fs ? fs : vfs); + if (!strcmp(vfs, fs)) + return strdup(vfs); /* e.g. "aaa" and "aaa" */ + + /* leave space for leading "r[ow],", "," and trailing zero */ + sz = strlen(vfs) + strlen(fs) + 5; + res = malloc(sz); + if (!res) + return NULL; + p = res + 3; /* make a room for rw/ro flag */ + + snprintf(p, sz - 3, "%s,%s", vfs, fs); + + /* remove 'rw' flags */ + rw += !mnt_optstr_remove_option(&p, "rw"); /* from vfs */ + rw += !mnt_optstr_remove_option(&p, "rw"); /* from fs */ + + /* remove 'ro' flags if necessary */ + if (rw != 2) { + ro += !mnt_optstr_remove_option(&p, "ro"); + if (ro + rw < 2) + ro += !mnt_optstr_remove_option(&p, "ro"); + } + + if (!strlen(p)) + memcpy(res, ro ? "ro" : "rw", 3); + else + memcpy(res, ro ? "ro," : "rw,", 3); + return res; +} + +/** + * mnt_fs_strdup_options: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Merges all mount options (VFS, FS and userspace) to the 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. + */ +char *mnt_fs_strdup_options(struct libmnt_fs *fs) +{ + char *res; + + assert(fs); + + errno = 0; + + if (fs->optstr) + return strdup(fs->optstr); + + res = merge_optstr(fs->vfs_optstr, fs->fs_optstr); + if (!res && errno) + return NULL; + if (fs->user_optstr) { + if (mnt_optstr_append_option(&res, fs->user_optstr, NULL)) { + free(res); + res = NULL; + } + } + return res; +} + +/** + * mnt_fs_get_options: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: pointer to string or NULL in case of error. + */ +const char *mnt_fs_get_options(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->optstr : NULL; +} + + +/** + * mnt_fs_set_options: + * @fs: fstab/mtab/mountinfo entry pointer + * @optstr: options string + * + * Splits @optstr to VFS, FS and userspace mount options and update relevat + * parts of @fs. + * + * Returns: 0 on success, or negative number icase of error. + */ +int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr) +{ + char *v = NULL, *f = NULL, *u = NULL, *n = NULL; + + assert(fs); + + if (!fs) + return -EINVAL; + if (optstr) { + int rc = mnt_split_optstr(optstr, &u, &v, &f, 0, 0); + if (rc) + return rc; + n = strdup(optstr); + if (!n) + return -ENOMEM; + } + + free(fs->fs_optstr); + free(fs->vfs_optstr); + free(fs->user_optstr); + free(fs->optstr); + + fs->fs_optstr = f; + fs->vfs_optstr = v; + fs->user_optstr = u; + fs->optstr = n; + + return 0; +} + +/** + * mnt_fs_append_options: + * @fs: fstab/mtab/mountinfo entry + * @optstr: mount options + * + * 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. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_append_options(struct libmnt_fs *fs, const char *optstr) +{ + char *v = NULL, *f = NULL, *u = NULL; + int rc; + + assert(fs); + + if (!fs) + return -EINVAL; + if (!optstr) + return 0; + + rc = mnt_split_optstr((char *) optstr, &u, &v, &f, 0, 0); + if (!rc && v) + rc = mnt_optstr_append_option(&fs->vfs_optstr, v, NULL); + if (!rc && f) + rc = mnt_optstr_append_option(&fs->fs_optstr, f, NULL); + if (!rc && u) + rc = mnt_optstr_append_option(&fs->user_optstr, u, NULL); + if (!rc) + rc = mnt_optstr_append_option(&fs->optstr, optstr, NULL); + + free(v); + free(f); + free(u); + + return rc; +} + +/** + * mnt_fs_prepend_options: + * @fs: fstab/mtab/mountinfo entry + * @optstr: mount options + * + * Parses (splits) @optstr and prepands results to VFS, FS and userspace lists + * of options. + * + * If @optstr is NULL then @fs is not modified and 0 is returned. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_prepend_options(struct libmnt_fs *fs, const char *optstr) +{ + char *v = NULL, *f = NULL, *u = NULL; + int rc; + + assert(fs); + + if (!fs) + return -EINVAL; + if (!optstr) + return 0; + + rc = mnt_split_optstr((char *) optstr, &u, &v, &f, 0, 0); + if (!rc && v) + rc = mnt_optstr_prepend_option(&fs->vfs_optstr, v, NULL); + if (!rc && f) + rc = mnt_optstr_prepend_option(&fs->fs_optstr, f, NULL); + if (!rc && u) + rc = mnt_optstr_prepend_option(&fs->user_optstr, u, NULL); + if (!rc) + rc = mnt_optstr_prepend_option(&fs->optstr, optstr, NULL); + + free(v); + free(f); + free(u); + + return rc; +} + +/* + * mnt_fs_get_fs_options: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: pointer to superblock (fs-depend) mount option string or NULL. + */ +const char *mnt_fs_get_fs_options(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->fs_optstr : NULL; +} + +/** + * mnt_fs_get_vfs_options: + * @fs: fstab/mtab entry pointer + * + * Returns: pointer to fs-independent (VFS) mount option string or NULL. + */ +const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->vfs_optstr : NULL; +} + +/** + * mnt_fs_get_user_options: + * @fs: fstab/mtab entry pointer + * + * Returns: pointer to userspace mount option string or NULL. + */ +const char *mnt_fs_get_user_options(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->user_optstr : NULL; +} + +/** + * mnt_fs_get_attributes: + * @fs: fstab/mtab entry pointer + * + * Returns: pointer to attributes string or NULL. + */ +const char *mnt_fs_get_attributes(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->attrs : NULL; +} + +/** + * mnt_fs_set_attributes: + * @fs: fstab/mtab/mountinfo entry + * @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 + * libmount. The attributes are stored in /run/mount/utab only. + * + * The atrtributes 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 carefull, and don't use attributes if possible. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_attributes(struct libmnt_fs *fs, const char *optstr) +{ + char *p = NULL; + + if (!fs) + return -EINVAL; + if (optstr) { + p = strdup(optstr); + if (!p) + return -ENOMEM; + } + free(fs->attrs); + fs->attrs = p; + + return 0; +} + +/** + * mnt_fs_append_attributes + * @fs: fstab/mtab/mountinfo entry + * @optstr: options string + * + * Appends mount attributes. (See mnt_fs_set_attributes()). + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_append_attributes(struct libmnt_fs *fs, const char *optstr) +{ + if (!fs) + return -EINVAL; + if (!optstr) + return 0; + return mnt_optstr_append_option(&fs->attrs, optstr, NULL); +} + +/** + * mnt_fs_prepend_attributes + * @fs: fstab/mtab/mountinfo entry + * @optstr: options string + * + * Prepends mount attributes. (See mnt_fs_set_attributes()). + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_prepend_attributes(struct libmnt_fs *fs, const char *optstr) +{ + if (!fs) + return -EINVAL; + if (!optstr) + return 0; + return mnt_optstr_prepend_option(&fs->attrs, optstr, NULL); +} + + +/** + * mnt_fs_get_freq: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: dump frequency in days. + */ +int mnt_fs_get_freq(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->freq : 0; +} + +/** + * mnt_fs_set_freq: + * @fs: fstab/mtab entry pointer + * @freq: dump frequency in days + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_freq(struct libmnt_fs *fs, int freq) +{ + assert(fs); + if (!fs) + return -EINVAL; + fs->freq = freq; + return 0; +} + +/** + * mnt_fs_get_passno: + * @fs: fstab/mtab entry pointer + * + * Returns: "pass number on parallel fsck". + */ +int mnt_fs_get_passno(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->passno: 0; +} + +/** + * mnt_fs_set_passno: + * @fs: fstab/mtab entry pointer + * @passno: pass number + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_passno(struct libmnt_fs *fs, int passno) +{ + assert(fs); + if (!fs) + return -EINVAL; + fs->passno = passno; + return 0; +} + +/** + * mnt_fs_get_root: + * @fs: /proc/self/mountinfo entry + * + * Returns: root of the mount within the filesystem or NULL + */ +const char *mnt_fs_get_root(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->root : NULL; +} + +/** + * mnt_fs_set_root: + * @fs: mountinfo entry + * @root: path + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_root(struct libmnt_fs *fs, const char *root) +{ + char *p = NULL; + + assert(fs); + if (!fs) + return -EINVAL; + if (root) { + p = strdup(root); + if (!p) + return -ENOMEM; + } + free(fs->root); + fs->root = p; + return 0; +} + +/** + * mnt_fs_get_bindsrc: + * @fs: /run/mount/utab entry + * + * Returns: full path that was used for mount(2) on MS_BIND + */ +const char *mnt_fs_get_bindsrc(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->bindsrc : NULL; +} + +/** + * mnt_fs_set_bindsrc: + * @fs: filesystem + * @src: path + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src) +{ + char *p = NULL; + + assert(fs); + if (!fs) + return -EINVAL; + if (src) { + p = strdup(src); + if (!p) + return -ENOMEM; + } + free(fs->bindsrc); + fs->bindsrc = p; + return 0; +} + +/** + * mnt_fs_get_id: + * @fs: /proc/self/mountinfo entry + * + * Returns: mount ID (unique identifier of the mount) or negative number in case of error. + */ +int mnt_fs_get_id(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->id : -EINVAL; +} + +/** + * mnt_fs_get_parent_id: + * @fs: /proc/self/mountinfo entry + * + * Returns: parent mount ID or negative number in case of error. + */ +int mnt_fs_get_parent_id(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->parent : -EINVAL; +} + +/** + * mnt_fs_get_devno: + * @fs: /proc/self/mountinfo entry + * + * Returns: value of st_dev for files on filesystem or 0 in case of error. + */ +dev_t mnt_fs_get_devno(struct libmnt_fs *fs) +{ + assert(fs); + return fs ? fs->devno : 0; +} + +/** + * 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 + * @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. + */ +int mnt_fs_get_option(struct libmnt_fs *fs, const char *name, + char **value, size_t *valsz) +{ + char rc = 1; + + if (fs->fs_optstr) + rc = mnt_optstr_get_option(fs->fs_optstr, name, value, valsz); + if (rc == 1 && fs->vfs_optstr) + rc = mnt_optstr_get_option(fs->vfs_optstr, name, value, valsz); + if (rc == 1 && fs->user_optstr) + rc = mnt_optstr_get_option(fs->user_optstr, name, value, valsz); + return rc; +} + +/** + * 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 + * @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. + */ +int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name, + char **value, size_t *valsz) +{ + char rc = 1; + + if (fs->attrs) + rc = mnt_optstr_get_option(fs->attrs, name, value, valsz); + return rc; +} + +/** + * mnt_fs_match_target: + * @fs: filesystem + * @target: mountpoint path + * @cache: tags/paths cache or NULL + * + * Possible are three attempts: + * 1) compare @target with @fs->target + * 2) realpath(@target) with @fs->target + * 3) realpath(@target) with realpath(@fs->target). + * + * The 2nd and 3rd attempts are not performed when @cache is NULL. + * + * 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) +{ + int rc = 0; + + if (!fs || !target || !fs->target) + return 0; + + /* 1) native paths */ + rc = !strcmp(target, fs->target); + + if (!rc && cache) { + /* 2) - canonicalized and non-canonicalized */ + char *cn = mnt_resolve_path(target, cache); + rc = (cn && strcmp(cn, fs->target) == 0); + + /* 3) - canonicalized and canonicalized */ + if (!rc && cn) { + char *tcn = mnt_resolve_path(fs->target, cache); + rc = (tcn && strcmp(cn, tcn) == 0); + } + } + + return rc; +} + +/** + * mnt_fs_match_source: + * @fs: filesystem + * @source: tag or path (device or so) or NULL + * @cache: tags/paths cache or NULL + * + * Possible are four attempts: + * 1) compare @source with @fs->source + * 2) compare realpath(@source) with @fs->source + * 3) compare realpath(@source) with realpath(@fs->source) + * 4) compare realpath(@source) with evaluated tag from @fs->source + * + * 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. + * + * Note that valid source path is NULL; the libmount uses NULL instead of + * "none". The "none" is used in /proc/{mounts,self/mountninfo} for pseudo + * filesystems. + * + * 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) +{ + char *cn; + const char *src, *t, *v; + + if (!fs) + return 0; + + /* undefined source -- "none" in /proc */ + if (source == NULL && fs->source == NULL) + return 1; + + if (source == NULL || fs->source == NULL) + return 0; + + /* 1) native paths/tags */ + if (!strcmp(source, fs->source)) + return 1; + + if (!cache) + return 0; + if (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO)) + return 0; + + cn = mnt_resolve_spec(source, cache); + if (!cn) + return 0; + + /* 2) canonicalized and native */ + src = mnt_fs_get_srcpath(fs); + if (src && !strcmp(cn, src)) + return 1; + + /* 3) canonicalized and canonicalized */ + if (src) { + src = mnt_resolve_path(src, cache); + if (src && !strcmp(cn, src)) + return 1; + } + if (src || mnt_fs_get_tag(fs, &t, &v)) + /* src path does not match and 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. + * + * (because libblkid uses udev symlinks and this is + * accessible for non-root uses) + */ + char *x = mnt_resolve_tag(t, v, cache); + if (x && !strcmp(x, cn)) + return 1; + } + return 0; + } + + /* 4) has the @source a tag that matches with tag from @fs ? */ + if (mnt_cache_device_has_tag(cache, cn, t, v)) + return 1; + + return 0; +} + +/** + * mnt_fs_match_fstype: + * @fs: filesystem + * @types: filesystem name or comma delimited list of filesystems + * + * For more details see mnt_match_fstype(). + * + * 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) +{ + return mnt_match_fstype(fs->fstype, types); +} + +/** + * mnt_fs_match_options: + * @fs: filesystem + * @options: comma delimited list of options (and nooptions) + * + * For more details see mnt_match_options(). + * + * 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) +{ + char *o = mnt_fs_strdup_options(fs); + int rc = 0; + + if (o) + rc = mnt_match_options(o, options); + free(o); + return rc; +} + +/** + * mnt_fs_print_debug + * @fs: fstab/mtab/mountinfo entry + * @file: output + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file) +{ + if (!fs) + return -EINVAL; + fprintf(file, "------ fs: %p\n", fs); + fprintf(file, "source: %s\n", mnt_fs_get_source(fs)); + fprintf(file, "target: %s\n", mnt_fs_get_target(fs)); + fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs)); + + if (mnt_fs_get_options(fs)) + fprintf(file, "optstr: %s\n", mnt_fs_get_options(fs)); + if (mnt_fs_get_vfs_options(fs)) + fprintf(file, "VFS-optstr: %s\n", mnt_fs_get_vfs_options(fs)); + if (mnt_fs_get_fs_options(fs)) + fprintf(file, "FS-opstr: %s\n", mnt_fs_get_fs_options(fs)); + if (mnt_fs_get_user_options(fs)) + fprintf(file, "user-optstr: %s\n", mnt_fs_get_user_options(fs)); + if (mnt_fs_get_attributes(fs)) + fprintf(file, "attributes: %s\n", mnt_fs_get_attributes(fs)); + + if (mnt_fs_get_root(fs)) + fprintf(file, "root: %s\n", mnt_fs_get_root(fs)); + if (mnt_fs_get_bindsrc(fs)) + fprintf(file, "bindsrc: %s\n", mnt_fs_get_bindsrc(fs)); + if (mnt_fs_get_freq(fs)) + fprintf(file, "freq: %d\n", mnt_fs_get_freq(fs)); + if (mnt_fs_get_passno(fs)) + fprintf(file, "pass: %d\n", mnt_fs_get_passno(fs)); + if (mnt_fs_get_id(fs)) + fprintf(file, "id: %d\n", mnt_fs_get_id(fs)); + if (mnt_fs_get_parent_id(fs)) + fprintf(file, "parent: %d\n", mnt_fs_get_parent_id(fs)); + if (mnt_fs_get_devno(fs)) + fprintf(file, "devno: %d:%d\n", major(mnt_fs_get_devno(fs)), + minor(mnt_fs_get_devno(fs))); + return 0; +} + +/** + * mnt_free_mntent: + * @mnt: mount entry + * + * Deallocates "mntent.h" mount entry. + */ +void mnt_free_mntent(struct mntent *mnt) +{ + if (mnt) { + free(mnt->mnt_fsname); + free(mnt->mnt_dir); + free(mnt->mnt_type); + free(mnt->mnt_opts); + free(mnt); + } +} + +/** + * mnt_fs_to_mntent: + * @fs: filesystem + * @mnt: mount description (as described in mntent.h) + * + * Copies 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. + */ +int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt) +{ + int rc; + struct mntent *m; + + if (!fs || !mnt) + return -EINVAL; + + m = *mnt; + if (!m) { + m = calloc(1, sizeof(*m)); + if (!m) + return -ENOMEM; + } + + if ((rc = update_str(&m->mnt_fsname, mnt_fs_get_source(fs)))) + goto err; + if ((rc = update_str(&m->mnt_dir, mnt_fs_get_target(fs)))) + goto err; + if ((rc = update_str(&m->mnt_type, mnt_fs_get_fstype(fs)))) + goto err; + + errno = 0; + m->mnt_opts = mnt_fs_strdup_options(fs); + if (!m->mnt_opts && errno) { + rc = -errno; + goto err; + } + + m->mnt_freq = mnt_fs_get_freq(fs); + m->mnt_passno = mnt_fs_get_passno(fs); + + if (!m->mnt_fsname) { + m->mnt_fsname = strdup("none"); + if (!m->mnt_fsname) + goto err; + } + *mnt = m; + + return 0; +err: + if (m != *mnt) + mnt_free_mntent(m); + return rc; +} diff --git a/libmount/src/init.c b/libmount/src/init.c new file mode 100644 index 000000000..d80a2d8ca --- /dev/null +++ b/libmount/src/init.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: init + * @title: Library initialization + * @short_description: initialize debuging + */ + +#include <stdlib.h> +#include <stdarg.h> + +#include "mountP.h" + +int libmount_debug_mask; + +/** + * mnt_init_debug: + * @mask: debug mask (0xffff to enable full debuging) + * + * If the @mask is not specified then this function reads + * 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. + */ +void mnt_init_debug(int mask) +{ + if (libmount_debug_mask & MNT_DEBUG_INIT) + return; + if (!mask) { + char *str = getenv("LIBMOUNT_DEBUG"); + if (str) + libmount_debug_mask = strtoul(str, 0, 0); + } else + libmount_debug_mask = mask; + + if (libmount_debug_mask) + printf("libmount: debug mask set to 0x%04x.\n", + libmount_debug_mask); + libmount_debug_mask |= MNT_DEBUG_INIT; +} diff --git a/libmount/src/iter.c b/libmount/src/iter.c new file mode 100644 index 000000000..99fedd17b --- /dev/null +++ b/libmount/src/iter.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: iter + * @title: Iterator + * @short_description: unified iterator + * + * The iterator keeps direction and last position for access to the internal + * library tables/lists. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "mountP.h" + +/** + * mnt_new_iter: + * @direction: MNT_INTER_{FOR,BACK}WARD direction + * + * Returns: newly allocated generic libmount iterator. + */ +struct libmnt_iter *mnt_new_iter(int direction) +{ + struct libmnt_iter *itr = calloc(1, sizeof(*itr)); + if (!itr) + return NULL; + itr->direction = direction; + return itr; +} + +/** + * mnt_free_iter: + * @itr: iterator pointer + * + * Deallocates iterator. + */ +void mnt_free_iter(struct libmnt_iter *itr) +{ + free(itr); +} + +/** + * mnt_reset_iter: + * @itr: iterator pointer + * @direction: MNT_INTER_{FOR,BACK}WARD or -1 to keep the derection unchanged + * + * Resets iterator. + */ +void mnt_reset_iter(struct libmnt_iter *itr, int direction) +{ + assert(itr); + + if (direction == -1) + direction = itr->direction; + + if (itr) { + memset(itr, 0, sizeof(*itr)); + itr->direction = direction; + } +} + +/** + * mnt_iter_get_direction: + * @itr: iterator pointer + * + * Returns: MNT_INTER_{FOR,BACK}WARD or negative number in case of error. + */ +int mnt_iter_get_direction(struct libmnt_iter *itr) +{ + assert(itr); + return itr ? itr->direction : -EINVAL; +} diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in new file mode 100644 index 000000000..15222083f --- /dev/null +++ b/libmount/src/libmount.h.in @@ -0,0 +1,562 @@ +/* + * mount.h - libmount API + * + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LIBMOUNT_MOUNT_H +#define _LIBMOUNT_MOUNT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef USE_UNSTABLE_LIBMOUNT_API +# warning libmount API is not stable yet! +#endif + +#include <stdio.h> +#include <mntent.h> +#include <sys/types.h> + +#define LIBMOUNT_VERSION "@LIBMOUNT_VERSION@" + +/** + * libmnt_cache: + * + * Stores canonicalized paths and evaluated tags + */ +struct libmnt_cache; + +/** + * libmnt_lock: + * + * Stores information about locked file (e.g. /etc/mtab) + */ +struct libmnt_lock; + +/** + * libmnt_iter: + * + * Generic iterator (stores state about lists) + */ +struct libmnt_iter; + +/** + * libmnt_optmap: + * + * Mount options description (map) + */ +struct libmnt_optmap +{ + const char *name; /* option name[=%<type>] (e.g. "loop[=%s]") */ + int id; /* option ID or MS_* flags (e.g MS_RDONLY) */ + int mask; /* MNT_{NOMTAB,INVERT,...} mask */ +}; + +/* + * mount options map masks + */ +#define MNT_INVERT (1 << 1) /* invert the mountflag */ +#define MNT_NOMTAB (1 << 2) /* skip in the mtab option string */ +#define MNT_PREFIX (1 << 3) /* prefix used for some options (e.g. "x-foo") */ + +/** + * libmnt_fs: + * + * Parsed fstab/mtab/mountinfo entry + */ +struct libmnt_fs; + +/** + * libmnt_table: + * + * List of struct libmnt_fs entries (parsed fstab/mtab/mountinfo) + */ +struct libmnt_table; + +/** + * libmnt_update + * + * /etc/mtab or utab update description + */ +struct libmnt_update; + +/** + * libmnt_context + * + * Mount/umount status + */ +struct libmnt_context; + +/* + * Actions + */ +enum { + MNT_ACT_MOUNT = 1, + MNT_ACT_UMOUNT +}; + +/* init.c */ +extern void mnt_init_debug(int mask); + +/* version.c */ +extern int mnt_parse_version_string(const char *ver_string); +extern int mnt_get_library_version(const char **ver_string); + +/* utils.c */ +extern char *mnt_mangle(const char *str); +extern char *mnt_unmangle(const char *str); +extern int mnt_fstype_is_netfs(const char *type); +extern int mnt_fstype_is_pseudofs(const char *type); +extern int mnt_match_fstype(const char *type, const char *pattern); +extern int mnt_match_options(const char *optstr, const char *pattern); +extern const char *mnt_get_fstab_path(void); +extern const char *mnt_get_mtab_path(void); +extern int mnt_has_regular_mtab(const char **mtab, int *writable); + +/* cache.c */ +extern struct libmnt_cache *mnt_new_cache(void); +extern void mnt_free_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, + const char *devname, + const char *token, + const char *value); + +extern char *mnt_cache_find_tag_value(struct libmnt_cache *cache, + const char *devname, const char *token); + +extern char *mnt_get_fstype(const char *devname, int *ambi, + struct libmnt_cache *cache); +extern char *mnt_resolve_path(const char *path, struct libmnt_cache *cache); +extern char *mnt_resolve_tag(const char *token, const char *value, + struct libmnt_cache *cache); +extern char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache); + +/* optstr.c */ +extern int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz, + char **value, size_t *valuesz); +extern int mnt_optstr_append_option(char **optstr, const char *name, + const char *value); +extern int mnt_optstr_prepend_option(char **optstr, const char *name, + const char *value); +extern int mnt_optstr_get_option(char *optstr, const char *name, + char **value, size_t *valsz); +extern int mnt_optstr_set_option(char **optstr, const char *name, + const char *value); +extern int mnt_optstr_remove_option(char **optstr, const char *name); + +extern int mnt_split_optstr(const char *optstr, + char **user, char **vfs, char **fs, + int ignore_user, int ignore_vfs); + +extern int mnt_optstr_get_options(const char *optstr, char **subset, + const struct libmnt_optmap *map, int ignore); + +extern int mnt_optstr_get_flags(const char *optstr, unsigned long *flags, + const struct libmnt_optmap *map); +extern int mnt_optstr_apply_flags(char **optstr, unsigned long flags, + const struct libmnt_optmap *map); + +/* iter.c */ +enum { + + MNT_ITER_FORWARD = 0, + MNT_ITER_BACKWARD +}; +extern struct libmnt_iter *mnt_new_iter(int direction); +extern void mnt_free_iter(struct libmnt_iter *itr); +extern void mnt_reset_iter(struct libmnt_iter *itr, int direction); +extern int mnt_iter_get_direction(struct libmnt_iter *itr); + +/* optmap.c */ +enum { + MNT_LINUX_MAP = 1, + MNT_USERSPACE_MAP +}; +extern const struct libmnt_optmap *mnt_get_builtin_optmap(int id); + +/* lock.c */ +extern struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id); +extern void mnt_free_lock(struct libmnt_lock *ml); +extern void mnt_unlock_file(struct libmnt_lock *ml); +extern int mnt_lock_file(struct libmnt_lock *ml); +extern int mnt_lock_block_signals(struct libmnt_lock *ml, int enable); + +/* fs.c */ +extern struct libmnt_fs *mnt_new_fs(void); +extern void mnt_free_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, + const struct libmnt_fs *src); +extern void *mnt_fs_get_userdata(struct libmnt_fs *fs); +extern int mnt_fs_set_userdata(struct libmnt_fs *fs, void *data); +extern const char *mnt_fs_get_source(struct libmnt_fs *fs); +extern int mnt_fs_set_source(struct libmnt_fs *fs, const char *source); +extern const char *mnt_fs_get_srcpath(struct libmnt_fs *fs); +extern int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, + const char **value); +extern const char *mnt_fs_get_target(struct libmnt_fs *fs); +extern int mnt_fs_set_target(struct libmnt_fs *fs, const char *target); +extern const char *mnt_fs_get_fstype(struct libmnt_fs *fs); +extern int mnt_fs_set_fstype(struct libmnt_fs *fs, const char *fstype); + +extern char *mnt_fs_strdup_options(struct libmnt_fs *fs); +extern const char *mnt_fs_get_options(struct libmnt_fs *fs); +extern int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr); +extern int mnt_fs_append_options(struct libmnt_fs *fs, const char *optstr); +extern int mnt_fs_prepend_options(struct libmnt_fs *fs, const char *optstr); + +extern int mnt_fs_get_option(struct libmnt_fs *fs, const char *name, + char **value, size_t *valsz); + + +extern const char *mnt_fs_get_fs_options(struct libmnt_fs *fs); +extern const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs); +extern const char *mnt_fs_get_user_options(struct libmnt_fs *fs); + +extern const char *mnt_fs_get_attributes(struct libmnt_fs *fs); +extern int mnt_fs_set_attributes(struct libmnt_fs *fs, const char *optstr); +extern int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name, + char **value, size_t *valsz); +extern int mnt_fs_append_attributes(struct libmnt_fs *fs, const char *optstr); +extern int mnt_fs_prepend_attributes(struct libmnt_fs *fs, const char *optstr); + +extern int mnt_fs_get_freq(struct libmnt_fs *fs); +extern int mnt_fs_set_freq(struct libmnt_fs *fs, int freq); +extern int mnt_fs_get_passno(struct libmnt_fs *fs); +extern int mnt_fs_set_passno(struct libmnt_fs *fs, int passno); +extern const char *mnt_fs_get_root(struct libmnt_fs *fs); +extern int mnt_fs_set_root(struct libmnt_fs *fs, const char *root); +extern const char *mnt_fs_get_bindsrc(struct libmnt_fs *fs); +extern int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src); +extern int mnt_fs_get_id(struct libmnt_fs *fs); +extern int mnt_fs_get_parent_id(struct libmnt_fs *fs); +extern dev_t mnt_fs_get_devno(struct libmnt_fs *fs); + +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, + struct libmnt_cache *cache); +extern int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types); +extern int mnt_fs_match_options(struct libmnt_fs *fs, const char *options); +extern int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file); + +extern int mnt_fs_is_kernel(struct libmnt_fs *fs); + +extern void mnt_free_mntent(struct mntent *mnt); +extern int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt); + +/* tab-parse.c */ +extern struct libmnt_table *mnt_new_table_from_file(const char *filename); +extern struct libmnt_table *mnt_new_table_from_dir(const char *dirname); +extern int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, + const char *filename); +extern int mnt_table_parse_file(struct libmnt_table *tb, const char *filename); +extern int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename); +extern int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename); +extern int mnt_table_set_parser_errcb(struct libmnt_table *tb, + int (*cb)(struct libmnt_table *tb, const char *filename, int line)); + +/* tab.c */ +extern struct libmnt_table *mnt_new_table(void); +extern void mnt_free_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_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc); +extern struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb); +extern const char *mnt_table_get_name(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_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, + struct libmnt_fs *parent, struct libmnt_fs **chld); +extern int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root); +extern int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, + struct libmnt_fs *fs); + +extern struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, + const char *path, int direction); +extern struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, + const char *path, int direction); +extern struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, + const char *val, int direction); +extern struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, + const char *source, int direction); +extern struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, + const char *source, + const char *target, int direction); + +extern int mnt_table_find_next_fs(struct libmnt_table *tb, + struct libmnt_iter *itr, + int (*match_func)(struct libmnt_fs *, void *), void *userdata, + struct libmnt_fs **fs); + +extern int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs); + + +/* tab_update.c */ +extern struct libmnt_update *mnt_new_update(void); +extern void mnt_free_update(struct libmnt_update *upd); +extern int mnt_update_is_ready(struct libmnt_update *upd); +extern int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mflags, + const char *target, struct libmnt_fs *fs); +extern int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc); +extern unsigned long mnt_update_get_mflags(struct libmnt_update *upd); +extern int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly); +extern const char *mnt_update_get_filename(struct libmnt_update *upd); +extern struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd); + +/* tab_diff.c */ +enum { + MNT_TABDIFF_MOUNT = 1, + MNT_TABDIFF_UMOUNT, + MNT_TABDIFF_MOVE, + MNT_TABDIFF_REMOUNT, + MNT_TABDIFF_PROPAGATION, /* not implemented yet (TODO) */ +}; + +extern struct libmnt_tabdiff *mnt_new_tabdiff(void); +extern void mnt_free_tabdiff(struct libmnt_tabdiff *df); + +extern int mnt_diff_tables(struct libmnt_tabdiff *df, + struct libmnt_table *old, + struct libmnt_table *new); + +extern 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); + +/* context.c */ + +/* mode for mount options from fstab */ +enum { + MNT_OMODE_IGNORE = (1 << 1), /* ignore mtab/fstab options */ + MNT_OMODE_APPEND = (1 << 2), /* append mtab/fstab options to existing options */ + MNT_OMODE_PREPEND = (1 << 3), /* prepend mtab/fstab options to existing options */ + MNT_OMODE_REPLACE = (1 << 4), /* replace existing options with options from mtab/fstab */ + + MNT_OMODE_FORCE = (1 << 5), /* always read mtab/fstab options */ + + MNT_OMODE_FSTAB = (1 << 10), /* read from fstab */ + MNT_OMODE_MTAB = (1 << 11), /* read from mtab if fstab not enabled or failed */ + + /* default */ + MNT_OMODE_AUTO = (MNT_OMODE_PREPEND | MNT_OMODE_FSTAB | MNT_OMODE_MTAB), + /* non-root users */ + MNT_OMODE_USER = (MNT_OMODE_REPLACE | MNT_OMODE_FORCE | MNT_OMODE_FSTAB) +}; + +extern struct libmnt_context *mnt_new_context(void); +extern void mnt_free_context(struct libmnt_context *cxt); +extern int mnt_reset_context(struct libmnt_context *cxt); +extern int mnt_context_is_restricted(struct libmnt_context *cxt); + +extern int mnt_context_init_helper(struct libmnt_context *cxt, + int action, int flags); +extern int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg); + +extern int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode); +extern int mnt_context_disable_canonicalize(struct libmnt_context *cxt, int disable); +extern int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable); +extern int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable); +extern int mnt_context_disable_helpers(struct libmnt_context *cxt, int disable); +extern int mnt_context_enable_sloppy(struct libmnt_context *cxt, int enable); +extern int mnt_context_enable_fake(struct libmnt_context *cxt, int enable); +extern int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable); +extern int mnt_context_enable_force(struct libmnt_context *cxt, int enable); +extern int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable); +extern int mnt_context_enable_loopdel(struct libmnt_context *cxt, int enable); + +extern int mnt_context_get_optsmode(struct libmnt_context *cxt); +extern int mnt_context_is_lazy(struct libmnt_context *cxt); +extern int mnt_context_is_rdonly_umount(struct libmnt_context *cxt); +extern int mnt_context_is_sloppy(struct libmnt_context *cxt); +extern int mnt_context_is_fake(struct libmnt_context *cxt); +extern int mnt_context_is_nomtab(struct libmnt_context *cxt); +extern int mnt_context_is_force(struct libmnt_context *cxt); +extern int mnt_context_is_verbose(struct libmnt_context *cxt); + +extern int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs); +extern struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt); +extern int mnt_context_set_source(struct libmnt_context *cxt, const char *source); +extern int mnt_context_set_target(struct libmnt_context *cxt, const char *target); +extern int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype); +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 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); +extern int mnt_context_set_fstype_pattern(struct libmnt_context *cxt, + const char *pattern); +extern int mnt_context_set_options_pattern(struct libmnt_context *cxt, + const char *pattern); + +extern int mnt_context_set_fstab(struct libmnt_context *cxt, + struct libmnt_table *tb); +extern int mnt_context_get_fstab(struct libmnt_context *cxt, + struct libmnt_table **tb); +extern int mnt_context_get_mtab(struct libmnt_context *cxt, + struct libmnt_table **tb); +extern int mnt_context_set_cache(struct libmnt_context *cxt, + struct libmnt_cache *cache); +extern struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt); +extern struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt); + +extern int mnt_context_set_mflags(struct libmnt_context *cxt, + unsigned long flags); +extern int mnt_context_get_mflags(struct libmnt_context *cxt, + unsigned long *flags); +extern int mnt_context_set_user_mflags(struct libmnt_context *cxt, + unsigned long flags); +extern int mnt_context_get_user_mflags(struct libmnt_context *cxt, + unsigned long *flags); + +extern int mnt_context_set_mountdata(struct libmnt_context *cxt, void *data); +extern int mnt_context_apply_fstab(struct libmnt_context *cxt); +extern int mnt_context_get_status(struct libmnt_context *cxt); +extern int mnt_context_strerror(struct libmnt_context *cxt, char *buf, + size_t bufsiz); + +extern int mnt_context_mount(struct libmnt_context *cxt); +extern int mnt_context_umount(struct libmnt_context *cxt); + +extern int mnt_context_prepare_mount(struct libmnt_context *cxt); +extern int mnt_context_do_mount(struct libmnt_context *cxt); +extern int mnt_context_finalize_mount(struct libmnt_context *cxt); + +extern int mnt_context_prepare_umount(struct libmnt_context *cxt); +extern int mnt_context_do_umount(struct libmnt_context *cxt); +extern int mnt_context_finalize_umount(struct libmnt_context *cxt); + +extern int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status); + +/* + * mount(8) userspace options masks (MNT_MAP_USERSPACE map) + */ +#define MNT_MS_NOAUTO (1 << 2) +#define MNT_MS_USER (1 << 3) +#define MNT_MS_USERS (1 << 4) +#define MNT_MS_OWNER (1 << 5) +#define MNT_MS_GROUP (1 << 6) +#define MNT_MS_NETDEV (1 << 7) +#define MNT_MS_COMMENT (1 << 8) +#define MNT_MS_LOOP (1 << 9) +#define MNT_MS_NOFAIL (1 << 10) +#define MNT_MS_UHELPER (1 << 11) +#define MNT_MS_HELPER (1 << 12) +#define MNT_MS_XCOMMENT (1 << 13) + +/* + * mount(2) MS_* masks (MNT_MAP_LINUX map) + */ +#ifndef MS_RDONLY +#define MS_RDONLY 1 /* Mount read-only */ +#endif +#ifndef MS_NOSUID +#define MS_NOSUID 2 /* Ignore suid and sgid bits */ +#endif +#ifndef MS_NODEV +#define MS_NODEV 4 /* Disallow access to device special files */ +#endif +#ifndef MS_NOEXEC +#define MS_NOEXEC 8 /* Disallow program execution */ +#endif +#ifndef MS_SYNCHRONOUS +#define MS_SYNCHRONOUS 16 /* Writes are synced at once */ +#endif +#ifndef MS_REMOUNT +#define MS_REMOUNT 32 /* Alter flags of a mounted FS */ +#endif +#ifndef MS_MANDLOCK +#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */ +#endif +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 /* Directory modifications are synchronous */ +#endif +#ifndef MS_NOATIME +#define MS_NOATIME 0x400 /* 1024: Do not update access times. */ +#endif +#ifndef MS_NODIRATIME +#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 */ +#endif +#ifndef MS_MOVE +#define MS_MOVE 0x2000 /* 8192: Atomically move tree */ +#endif +#ifndef MS_REC +#define MS_REC 0x4000 /* 16384: Recursive loopback */ +#endif +#ifndef MS_SILENT +#define MS_SILENT 0x8000 /* 32768 */ +#endif +#ifndef MS_RELATIME +#define MS_RELATIME 0x200000 /* 200000: Update access times relative + to mtime/ctime */ +#endif +#ifndef MS_UNBINDABLE +#define MS_UNBINDABLE (1<<17) /* 131072 unbindable*/ +#endif +#ifndef MS_PRIVATE +#define MS_PRIVATE (1<<18) /* 262144 Private*/ +#endif +#ifndef MS_SLAVE +#define MS_SLAVE (1<<19) /* 524288 Slave*/ +#endif +#ifndef MS_SHARED +#define MS_SHARED (1<<20) /* 1048576 Shared*/ +#endif +#ifndef MS_I_VERSION +#define MS_I_VERSION (1<<23) /* update inode I_version field */ +#endif +#ifndef MS_STRICTATIME +#define MS_STRICTATIME (1<<24) /* strict atime semantics */ +#endif + +/* + * Magic mount flag number. Had to be or-ed to the flag values. + */ +#ifndef MS_MGC_VAL +#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */ +#endif +#ifndef MS_MGC_MSK +#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ +#endif + + +/* Shared-subtree options */ +#define MS_PROPAGATION (MS_SHARED|MS_SLAVE|MS_UNBINDABLE|MS_PRIVATE) + +/* Options that we make ordinary users have by default. */ +#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV) + +/* Options that we make owner-mounted devices have by default */ +#define MS_OWNERSECURE (MS_NOSUID|MS_NODEV) + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMOUNT_MOUNT_H */ diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym new file mode 100644 index 000000000..a49891650 --- /dev/null +++ b/libmount/src/libmount.sym @@ -0,0 +1,199 @@ +/* + * The symbol versioning ensures that a new application requiring symbol foo; + * can't run with old libblkid.so not providing foo; + * version info can't enforce this since we never change the SONAME. + */ +MOUNT_2.19 { +global: + mnt_cache_device_has_tag; + mnt_cache_find_tag_value; + mnt_cache_read_tags; + mnt_context_append_options; + mnt_context_apply_fstab; + mnt_context_disable_canonicalize; + mnt_context_disable_helpers; + mnt_context_disable_mtab; + mnt_context_do_mount; + mnt_context_do_umount; + mnt_context_enable_fake; + mnt_context_enable_force; + mnt_context_enable_lazy; + mnt_context_enable_loopdel; + mnt_context_enable_rdonly_umount; + mnt_context_enable_sloppy; + mnt_context_enable_verbose; + mnt_context_finalize_mount; + mnt_context_finalize_umount; + mnt_context_get_cache; + mnt_context_get_fs; + mnt_context_get_fstab; + mnt_context_get_fstype; + mnt_context_get_lock; + mnt_context_get_mflags; + mnt_context_get_mtab; + mnt_context_get_optsmode; + mnt_context_get_source; + mnt_context_get_status; + mnt_context_get_target; + mnt_context_get_user_mflags; + mnt_context_helper_setopt; + mnt_context_init_helper; + mnt_context_is_fake; + mnt_context_is_force; + mnt_context_is_lazy; + mnt_context_is_nomtab; + mnt_context_is_rdonly_umount; + mnt_context_is_restricted; + mnt_context_is_sloppy; + mnt_context_is_verbose; + mnt_context_mount; + mnt_context_prepare_mount; + mnt_context_prepare_umount; + mnt_context_set_cache; + mnt_context_set_fs; + mnt_context_set_fstab; + mnt_context_set_fstype; + mnt_context_set_fstype_pattern; + mnt_context_set_mflags; + mnt_context_set_mountdata; + mnt_context_set_options; + mnt_context_set_options_pattern; + mnt_context_set_optsmode; + mnt_context_set_source; + mnt_context_set_syscall_status; + mnt_context_set_target; + mnt_context_set_user_mflags; + mnt_context_strerror; + mnt_context_umount; + mnt_copy_fs; + mnt_free_cache; + mnt_free_context; + mnt_free_fs; + mnt_free_iter; + mnt_free_lock; + mnt_free_mntent; + mnt_free_table; + mnt_free_update; + mnt_fs_append_attributes; + mnt_fs_append_options; + mnt_fs_get_attribute; + mnt_fs_get_attributes; + mnt_fs_get_bindsrc; + mnt_fs_get_devno; + mnt_fs_get_freq; + mnt_fs_get_fs_options; + mnt_fs_get_fstype; + mnt_fs_get_id; + mnt_fs_get_option; + mnt_fs_get_options; + mnt_fs_get_parent_id; + mnt_fs_get_passno; + mnt_fs_get_root; + mnt_fs_get_source; + mnt_fs_get_srcpath; + mnt_fs_get_tag; + mnt_fs_get_target; + mnt_fs_get_userdata; + mnt_fs_get_user_options; + mnt_fs_get_vfs_options; + mnt_fs_is_kernel; + mnt_fs_match_fstype; + mnt_fs_match_options; + mnt_fs_match_source; + mnt_fs_match_target; + mnt_fs_prepend_attributes; + mnt_fs_prepend_options; + mnt_fs_print_debug; + mnt_fs_set_attributes; + mnt_fs_set_bindsrc; + mnt_fs_set_freq; + mnt_fs_set_fstype; + mnt_fs_set_options; + mnt_fs_set_passno; + mnt_fs_set_root; + mnt_fs_set_source; + mnt_fs_set_target; + mnt_fs_set_userdata; + mnt_fs_strdup_options; + mnt_fs_to_mntent; + mnt_fstype_is_netfs; + mnt_fstype_is_pseudofs; + mnt_get_builtin_optmap; + mnt_get_fstab_path; + mnt_get_fstype; + mnt_get_library_version; + mnt_get_mtab_path; + mnt_has_regular_mtab; + mnt_init_debug; + mnt_iter_get_direction; + mnt_lock_block_signals; + mnt_lock_file; + mnt_mangle; + mnt_match_fstype; + mnt_match_options; + mnt_new_cache; + mnt_new_context; + mnt_new_fs; + mnt_new_iter; + mnt_new_lock; + mnt_new_table; + mnt_new_table_from_dir; + mnt_new_table_from_file; + mnt_new_update; + mnt_optstr_append_option; + mnt_optstr_apply_flags; + mnt_optstr_get_flags; + mnt_optstr_get_option; + mnt_optstr_get_options; + mnt_optstr_next_option; + mnt_optstr_prepend_option; + mnt_optstr_remove_option; + mnt_optstr_set_option; + mnt_parse_version_string; + mnt_reset_context; + mnt_reset_fs; + mnt_reset_iter; + mnt_reset_table; + mnt_resolve_path; + mnt_resolve_spec; + mnt_resolve_tag; + mnt_split_optstr; + mnt_table_add_fs; + mnt_table_find_next_fs; + mnt_table_find_pair; + mnt_table_find_source; + mnt_table_find_srcpath; + mnt_table_find_tag; + mnt_table_find_target; + mnt_table_get_cache; + mnt_table_get_name; + mnt_table_get_nents; + mnt_table_get_root_fs; + mnt_table_next_child_fs; + mnt_table_next_fs; + mnt_table_parse_file; + mnt_table_parse_fstab; + mnt_table_parse_mtab; + mnt_table_parse_stream; + mnt_table_remove_fs; + mnt_table_set_cache; + mnt_table_set_iter; + mnt_table_set_parser_errcb; + mnt_unlock_file; + mnt_unmangle; + mnt_update_force_rdonly; + mnt_update_get_filename; + mnt_update_get_fs; + mnt_update_get_mflags; + mnt_update_is_ready; + mnt_update_set_fs; + mnt_update_table; + mnt_new_tabdiff; + mnt_free_tabdiff; + mnt_diff_tables; + mnt_tabdiff_next_change; + mnt_table_is_fs_mounted; +local: + *; +}; + diff --git a/libmount/src/lock.c b/libmount/src/lock.c new file mode 100644 index 000000000..53c1115ec --- /dev/null +++ b/libmount/src/lock.c @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: lock + * @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 + * locking. Note, it's necessary to use the same locking schema in all + * application that access the file. + */ +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/file.h> + +#include "pathnames.h" +#include "nls.h" +#include "c.h" + +#include "mountP.h" + +/* + * lock handler + */ +struct libmnt_lock { + char *lockfile; /* path to lock file (e.g. /etc/mtab~) */ + char *linkfile; /* path to link file (e.g. /etc/mtab~.<id>) */ + int lockfile_fd; /* lock file descriptor */ + + int locked : 1; /* do we own the lock? */ + int sigblock : 1; /* block signals when locked */ + int simplelock : 1; /* use flock rather than normal mtab lock */ + + sigset_t oldsigmask; +}; + + +/** + * mnt_new_lock: + * @datafile: the file that should be covered by the lock + * @id: unique linkfile identifier or 0 (default is getpid()) + * + * Returns: newly allocated lock handler or NULL on case of error. + */ +struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id) +{ + struct libmnt_lock *ml = NULL; + char *lo = NULL, *ln = NULL; + size_t losz; + + if (!datafile) + return NULL; + + /* for flock we use "foo.lock, for mtab "foo~" + */ + losz = strlen(datafile) + sizeof(".lock"); + lo = malloc(losz); + if (!lo) + goto err; + + /* default is mtab~ lock */ + snprintf(lo, losz, "%s~", datafile); + + if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) { + ln = NULL; + goto err; + } + ml = calloc(1, sizeof(*ml) ); + if (!ml) + goto err; + + ml->lockfile_fd = -1; + ml->linkfile = ln; + ml->lockfile = lo; + + DBG(LOCKS, mnt_debug_h(ml, "alloc: default linkfile=%s, lockfile=%s", ln, lo)); + return ml; +err: + free(lo); + free(ln); + free(ml); + return NULL; +} + + +/** + * mnt_free_lock: + * @ml: struct libmnt_lock handler + * + * Deallocates mnt_lock. + */ +void mnt_free_lock(struct libmnt_lock *ml) +{ + if (!ml) + return; + DBG(LOCKS, mnt_debug_h(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : "")); + free(ml->lockfile); + free(ml->linkfile); + free(ml); +} + +/** + * mnt_lock_block_signals: + * @ml: struct libmnt_lock handler + * @enable: TRUE/FALSE + * + * Block/unblock signals when the lock is locked, the signals are not blocked + * by default. + * + * Returns: <0 on error, 0 on success. + */ +int mnt_lock_block_signals(struct libmnt_lock *ml, int enable) +{ + if (!ml) + return -EINVAL; + DBG(LOCKS, mnt_debug_h(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED")); + ml->sigblock = enable ? 1 : 0; + return 0; +} + +/* don't export this to API + */ +int mnt_lock_use_simplelock(struct libmnt_lock *ml, int enable) +{ + size_t sz; + + if (!ml) + return -EINVAL; + + assert(ml->lockfile); + + DBG(LOCKS, mnt_debug_h(ml, "flock: %s", enable ? "ENABLED" : "DISABLED")); + ml->simplelock = enable ? 1 : 0; + + sz = strlen(ml->lockfile); + + /* Change lock name: + * + * flock: "<name>.lock" + * mtab lock: "<name>~" + */ + if (ml->simplelock && endswith(ml->lockfile, "~")) + memcpy(ml->lockfile + sz - 1, ".lock", 6); + + else if (!ml->simplelock && endswith(ml->lockfile, ".lock")) + memcpy(ml->lockfile + sz - 5, "~", 2); + + DBG(LOCKS, mnt_debug_h(ml, "new lock filename: '%s'", ml->lockfile)); + return 0; +} + +/* + * Returns path to lockfile. + */ +static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml) +{ + return ml ? ml->lockfile : NULL; +} + +/* + * Note that the filename is generated by mnt_new_lock() and depends on + * getpid() or 'id' argument of the mnt_new_lock() function. + * + * Returns: unique (per process/thread) path to linkfile. + */ +static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml) +{ + return ml ? ml->linkfile : NULL; +} + +/* + * Simple flocking + */ +static void unlock_simplelock(struct libmnt_lock *ml) +{ + assert(ml); + assert(ml->simplelock); + + if (ml->lockfile_fd >= 0) { + DBG(LOCKS, mnt_debug_h(ml, "%s: unflocking", + mnt_lock_get_lockfile(ml))); + close(ml->lockfile_fd); + } +} + +static int lock_simplelock(struct libmnt_lock *ml) +{ + const char *lfile; + int rc; + + assert(ml); + assert(ml->simplelock); + + lfile = mnt_lock_get_lockfile(ml); + + DBG(LOCKS, mnt_debug_h(ml, "%s: locking", lfile)); + + if (ml->sigblock) { + sigset_t sigs; + sigemptyset(&ml->oldsigmask); + sigfillset(&sigs); + sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask); + } + + ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, + S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); + if (ml->lockfile_fd < 0) { + rc = -errno; + goto err; + } + + while (flock(ml->lockfile_fd, LOCK_EX) < 0) { + int errsv; + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + errsv = errno; + close(ml->lockfile_fd); + ml->lockfile_fd = -1; + rc = -errsv; + goto err; + } + ml->locked = 1; + return 0; +err: + if (ml->sigblock) + sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL); + return rc; +} + +/* + * traditional mtab locking + */ + +static void mnt_lockalrm_handler(int sig) +{ + /* do nothing, say nothing, be nothing */ +} + +/* + * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt + * fcntl() to avoid never ending waiting. + * + * Returns: 0 on success, 1 on timeout, -errno on error. + */ +static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime) +{ + struct timeval now; + struct sigaction sa, osa; + int ret = 0; + + gettimeofday(&now, NULL); + + if (now.tv_sec >= maxtime) + return 1; /* timeout */ + + /* setup ALARM handler -- we don't want to wait forever */ + sa.sa_flags = 0; + sa.sa_handler = mnt_lockalrm_handler; + sigfillset (&sa.sa_mask); + + sigaction(SIGALRM, &sa, &osa); + + DBG(LOCKS, mnt_debug_h(ml, "(%d) waiting for F_SETLKW", getpid())); + + alarm(maxtime - now.tv_sec); + if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1) + ret = errno == EINTR ? 1 : -errno; + alarm(0); + + /* restore old sigaction */ + sigaction(SIGALRM, &osa, NULL); + + DBG(LOCKS, mnt_debug_h(ml, "(%d) leaving mnt_wait_setlkw(), rc=%d", + getpid(), ret)); + return ret; +} + +/* + * Create the mtab lock file. + * + * The old code here used flock on a lock file /etc/mtab~ and deleted + * this lock file afterwards. However, as rgooch remarks, that has a + * race: a second mount may be waiting on the lock and proceed as + * 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. + * + * 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 + * to delete the lock afterwards. Now the use of flock() is in principle + * superfluous, but avoids an arbitrary sleep(). + * + * Where does the link point to? Obvious choices are mtab and mtab~~. + * HJLu points out that the latter leads to races. Right now we use + * mtab~.<pid> instead. + * + * + * 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) + * 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 + * is that this method also counts time which we spend in fcntl(F_SETLKW) and + * number of attempts is not restricted. + * -- kzak@redhat.com [Mar-2007] + * + * + * This mtab locking code has been refactored and moved to libmount. The mtab + * locking is really not perfect (e.g. SIGALRM), but it's stable, reliable and + * backwardly compatible code. + * + * Don't forget that this code has to be compatible with 3rd party mounts + * (/sbin/mount.<foo>) and has to work with NFS. + * -- kzak@redhat.com [May-2009] + */ + +/* maximum seconds between first and last attempt */ +#define MOUNTLOCK_MAXTIME 30 + +/* sleep time (in microseconds, max=999999) between attempts */ +#define MOUNTLOCK_WAITTIME 5000 + +static void unlock_mtab(struct libmnt_lock *ml) +{ + if (!ml) + return; + + if (!ml->locked && ml->lockfile && ml->linkfile) + { + /* We have (probably) all 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. + * + * We own the lock when linkfile == lockfile. + */ + struct stat lo, li; + + if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) && + lo.st_dev == li.st_dev && lo.st_ino == li.st_ino) + ml->locked = 1; + } + + if (ml->linkfile) + unlink(ml->linkfile); + if (ml->lockfile_fd >= 0) + close(ml->lockfile_fd); + if (ml->locked && ml->lockfile) { + unlink(ml->lockfile); + DBG(LOCKS, mnt_debug_h(ml, "unlink %s", ml->lockfile)); + } +} + +static int lock_mtab(struct libmnt_lock *ml) +{ + int i, rc = -1; + struct timespec waittime; + struct timeval maxtime; + const char *lockfile, *linkfile; + + if (!ml) + return -EINVAL; + if (ml->locked) + return 0; + + lockfile = mnt_lock_get_lockfile(ml); + if (!lockfile) + return -EINVAL; + linkfile = mnt_lock_get_linkfile(ml); + if (!linkfile) + return -EINVAL; + + if (ml->sigblock) { + /* + * Block all signals when locked, mnt_unlock_file() will + * restore the old mask. + */ + sigset_t sigs; + + sigemptyset(&ml->oldsigmask); + sigfillset(&sigs); + sigdelset(&sigs, SIGTRAP); + sigdelset(&sigs, SIGALRM); + sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask); + } + + i = open(linkfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); + if (i < 0) { + /* linkfile does not exist (as a file) and we cannot create it. + * Read-only or full filesystem? Too many files open in the system? + */ + if (errno > 0) + rc = -errno; + goto failed; + } + close(i); + + gettimeofday(&maxtime, NULL); + maxtime.tv_sec += MOUNTLOCK_MAXTIME; + + waittime.tv_sec = 0; + waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME); + + /* Repeat until it was us who made the link */ + while (!ml->locked) { + struct timeval now; + struct flock flock; + int j; + + j = link(linkfile, lockfile); + if (j == 0) + ml->locked = 1; + + if (j < 0 && errno != EEXIST) { + if (errno > 0) + rc = -errno; + goto failed; + } + ml->lockfile_fd = open(lockfile, O_WRONLY); + + if (ml->lockfile_fd < 0) { + /* Strange... Maybe the file was just deleted? */ + int errsv = errno; + gettimeofday(&now, NULL); + if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) { + ml->locked = 0; + continue; + } + if (errsv > 0) + rc = -errsv; + goto failed; + } + + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + if (ml->locked) { + /* We made the link. Now claim the lock. */ + if (fcntl (ml->lockfile_fd, F_SETLK, &flock) == -1) { + DBG(LOCKS, mnt_debug_h(ml, + "%s: can't F_SETLK lockfile, errno=%d\n", + lockfile, errno)); + /* proceed, since it was us who created the lockfile anyway */ + } + break; + } else { + /* Someone else made the link. Wait. */ + int err = mnt_wait_mtab_lock(ml, &flock, maxtime.tv_sec); + + if (err == 1) { + DBG(LOCKS, mnt_debug_h(ml, + "%s: can't create link: time out (perhaps " + "there is a stale lock file?)", lockfile)); + rc = -ETIMEDOUT; + goto failed; + + } else if (err < 0) { + rc = err; + goto failed; + } + nanosleep(&waittime, NULL); + close(ml->lockfile_fd); + ml->lockfile_fd = -1; + } + } + DBG(LOCKS, mnt_debug_h(ml, "%s: (%d) successfully locked", + lockfile, getpid())); + unlink(linkfile); + return 0; + +failed: + mnt_unlock_file(ml); + return rc; +} + + +/** + * mnt_lock_file + * @ml: pointer to struct libmnt_lock instance + * + * Creates lock file (e.g. /etc/mtab~). Note that this function may + * use alarm(). + * + * Your application has to always call mnt_unlock_file() before exit. + * + * Traditional mtab locking scheme: + * + * 1. create linkfile (e.g. /etc/mtab~.$PID) + * 2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~) + * 3. a) link() success: setups F_SETLK lock (see fcnlt(2)) + * b) link() failed: wait (max 30s) on F_SETLKW lock, goto 2. + * + * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case + * of stale lock file). + */ +int mnt_lock_file(struct libmnt_lock *ml) +{ + if (!ml) + return -EINVAL; + + if (ml->simplelock) + return lock_simplelock(ml); + + return lock_mtab(ml); +} + +/** + * mnt_unlock_file: + * @ml: lock struct + * + * Unlocks the file. The function could be called independently on the + * lock status (for example from exit(3)). + */ +void mnt_unlock_file(struct libmnt_lock *ml) +{ + if (!ml) + return; + + DBG(LOCKS, mnt_debug_h(ml, "(%d) %s", getpid(), + ml->locked ? "unlocking" : "cleaning")); + + if (ml->simplelock) + unlock_simplelock(ml); + else + unlock_mtab(ml); + + ml->locked = 0; + ml->lockfile_fd = -1; + + if (ml->sigblock) { + DBG(LOCKS, mnt_debug_h(ml, "restoring sigmask")); + sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL); + } +} + +#ifdef TEST_PROGRAM + +struct libmnt_lock *lock; + +/* + * read number from @filename, increment the number and + * write the number back to the file + */ +void increment_data(const char *filename, int verbose, int loopno) +{ + long num; + FILE *f; + char buf[256]; + + if (!(f = fopen(filename, "r"))) + err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename); + + if (!fgets(buf, sizeof(buf), f)) + err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename); + + fclose(f); + num = atol(buf) + 1; + + if (!(f = fopen(filename, "w"))) + err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename); + + fprintf(f, "%ld", num); + fclose(f); + + if (verbose) + fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(), + filename, num - 1, num, loopno); +} + +void clean_lock(void) +{ + if (!lock) + return; + mnt_unlock_file(lock); + mnt_free_lock(lock); +} + +void sig_handler(int sig) +{ + errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig)); +} + +int test_lock(struct libmnt_test *ts, int argc, char *argv[]) +{ + time_t synctime = 0; + unsigned int usecs; + struct timeval tv; + const char *datafile = NULL; + int verbose = 0, loops = 0, l, idx = 1; + + if (argc < 3) + return -EINVAL; + + if (strcmp(argv[idx], "--synctime") == 0) { + synctime = (time_t) atol(argv[idx + 1]); + idx += 2; + } + if (idx < argc && strcmp(argv[idx], "--verbose") == 0) { + verbose = 1; + idx++; + } + + if (idx < argc) + datafile = argv[idx++]; + if (idx < argc) + loops = atoi(argv[idx++]); + + if (!datafile || !loops) + return -EINVAL; + + if (verbose) + fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n", + getpid(), (int) synctime, datafile, loops); + + atexit(clean_lock); + + /* be paranoid and call exit() (=clean_lock()) for all signals */ + { + int sig = 0; + struct sigaction sa; + + sa.sa_handler = sig_handler; + sa.sa_flags = 0; + sigfillset(&sa.sa_mask); + + while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD) + sigaction (sig, &sa, (struct sigaction *) 0); + } + + /* start the test in exactly defined time */ + if (synctime) { + gettimeofday(&tv, NULL); + if (synctime && synctime - tv.tv_sec > 1) { + usecs = ((synctime - tv.tv_sec) * 1000000UL) - + (1000000UL - tv.tv_usec); + usleep(usecs); + } + } + + for (l = 0; l < loops; l++) { + lock = mnt_new_lock(datafile, 0); + if (!lock) + return -1; + + if (mnt_lock_file(lock) != 0) { + fprintf(stderr, "%d: failed to lock %s file\n", + getpid(), datafile); + return -1; + } + + increment_data(datafile, verbose, l); + + mnt_unlock_file(lock); + mnt_free_lock(lock); + lock = NULL; + + /* The mount command usually finish after mtab update. We + * simulate this via short sleep -- it's also enough to make + * concurrent processes happy. + */ + if (synctime) + usleep(25000); + } + + return 0; +} + +/* + * Note that this test should be executed from a script that creates many + * parallel processes, otherwise this test does not make sense. + */ +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> " + "increment a number in datafile" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h new file mode 100644 index 000000000..0a8c7522c --- /dev/null +++ b/libmount/src/mountP.h @@ -0,0 +1,357 @@ +/* + * mountP.h - private library header file + * + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#ifndef _LIBMOUNT_PRIVATE_H +#define _LIBMOUNT_PRIVATE_H + +#include <sys/types.h> +#include <errno.h> +#include "c.h" + +#define USE_UNSTABLE_LIBMOUNT_API + +#include "libmount.h" +#include "list.h" + +/* features */ +#define CONFIG_LIBMOUNT_ASSERT +#define CONFIG_LIBMOUNT_DEBUG + +#ifdef CONFIG_LIBMOUNT_ASSERT +#include <assert.h> +#endif + +/* + * Debug + */ +#if defined(TEST_PROGRAM) && !defined(LIBMOUNT_DEBUG) +#define CONFIG_LIBMOUNT_DEBUG +#endif + +#define MNT_DEBUG_INIT (1 << 1) +#define MNT_DEBUG_CACHE (1 << 2) +#define MNT_DEBUG_OPTIONS (1 << 3) +#define MNT_DEBUG_LOCKS (1 << 4) +#define MNT_DEBUG_TAB (1 << 5) +#define MNT_DEBUG_FS (1 << 6) +#define MNT_DEBUG_OPTS (1 << 7) +#define MNT_DEBUG_UPDATE (1 << 8) +#define MNT_DEBUG_UTILS (1 << 9) +#define MNT_DEBUG_CXT (1 << 10) +#define MNT_DEBUG_DIFF (1 << 11) +#define MNT_DEBUG_ALL 0xFFFF + +#ifdef CONFIG_LIBMOUNT_DEBUG +# include <stdio.h> +# include <stdarg.h> + +# define DBG(m,x) do { \ + if ((MNT_DEBUG_ ## m) & libmount_debug_mask) {\ + fprintf(stderr, "libmount: %8s: ", # m); \ + x; \ + } \ + } while(0) + +# define DBG_FLUSH do { fflush(stderr); } while(0) + +extern int libmount_debug_mask; + +static inline void __attribute__ ((__format__ (__printf__, 1, 2))) +mnt_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))) +mnt_debug_h(void *handler, const char *mesg, ...) +{ + va_list ap; + + fprintf(stderr, "[%p]: ", handler); + va_start(ap, mesg); + vfprintf(stderr, mesg, ap); + va_end(ap); + fputc('\n', stderr); +} + +#else /* !CONFIG_LIBMOUNT_DEBUG */ +# define DBG(m,x) do { ; } while(0) +# define DBG_FLUSH do { ; } while(0) +#endif + +/* extension for files in the /etc/fstab.d directory */ +#define MNT_MNTTABDIR_EXT ".fstab" + +/* library private paths */ +#define MNT_RUNTIME_TOPDIR "/run" +#define MNT_RUNTIME_TOPDIR_OLD "/dev" + +#define MNT_PATH_UTAB MNT_RUNTIME_TOPDIR "/mount/utab" +#define MNT_PATH_UTAB_OLD MNT_RUNTIME_TOPDIR_OLD "/.mount/utab" + +#define MNT_UTAB_HEADER "# libmount utab file\n" + +#ifdef TEST_PROGRAM +struct libmnt_test { + const char *name; + int (*body)(struct libmnt_test *ts, int argc, char *argv[]); + const char *usage; +}; + +/* test.c */ +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); +extern int startswith(const char *s, const char *sx); + +extern int mnt_chdir_to_parent(const char *target, char **filename); +extern char *mnt_get_username(const uid_t uid); +extern int mnt_get_uid(const char *username, uid_t *uid); +extern int mnt_get_gid(const char *groupname, gid_t *gid); +extern int mnt_in_group(gid_t gid); + +extern char *mnt_get_mountpoint(const char *path); +extern char *mnt_get_fs_root(const char *path, const char *mountpoint); +extern int mnt_open_uniq_filename(const char *filename, char **name); +extern int mnt_has_regular_utab(const char **utab, int *writable); +extern const char *mnt_get_utab_path(void); + +extern int mnt_get_filesystems(char ***filesystems, const char *pattern); +extern void mnt_free_filesystems(char **filesystems); + +/* tab.c */ +extern struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, + struct libmnt_fs *fs, + unsigned long mountflags, + char **fsroot); + +/* + * Generic iterator + */ +struct libmnt_iter { + struct list_head *p; /* current position */ + struct list_head *head; /* start position */ + int direction; /* MNT_ITER_{FOR,BACK}WARD */ +}; + +#define IS_ITER_FORWARD(_i) ((_i)->direction == MNT_ITER_FORWARD) +#define IS_ITER_BACKWARD(_i) ((_i)->direction == MNT_ITER_BACKWARD) + +#define MNT_ITER_INIT(itr, list) \ + do { \ + (itr)->p = IS_ITER_FORWARD(itr) ? \ + (list)->next : (list)->prev; \ + (itr)->head = (list); \ + } while(0) + +#define MNT_ITER_ITERATE(itr, res, restype, member) \ + do { \ + res = list_entry((itr)->p, restype, member); \ + (itr)->p = IS_ITER_FORWARD(itr) ? \ + (itr)->p->next : (itr)->p->prev; \ + } while(0) + + +/* + * This struct represents one entry in 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 id; /* mountinfo[1]: ID */ + int parent; /* mountinfo[2]: parent */ + dev_t devno; /* mountinfo[3]: st_dev */ + + char *bindsrc; /* utab, full path from fstab[1] for bind mounts */ + + char *source; /* fstab[1], mountinfo[10]: + * source dev, file, dir or TAG */ + char *tagname; /* fstab[1]: tag name - "LABEL", "UUID", ..*/ + char *tagval; /* tag value */ + + char *root; /* mountinfo[4]: root of the mount within the FS */ + char *target; /* mountinfo[5], fstab[2]: mountpoint */ + char *fstype; /* mountinfo[9], fstab[3]: filesystem type */ + + char *optstr; /* fstab[4], merged options */ + char *vfs_optstr; /* mountinfo[6]: fs-independent (VFS) options */ + char *fs_optstr; /* mountinfo[11]: fs-dependent options */ + char *user_optstr; /* userspace mount options */ + char *attrs; /* mount attributes */ + + int freq; /* fstab[5]: dump frequency in days */ + int passno; /* fstab[6]: pass number on parallel fsck */ + + int flags; /* MNT_FS_* flags */ + + void *userdata; /* library independent data */ +}; + +/* + * fs flags + */ +#define MNT_FS_PSEUDO (1 << 1) /* pseudo filesystem */ +#define MNT_FS_NET (1 << 2) /* network filesystem */ +#define MNT_FS_SWAP (1 << 3) /* swap device */ +#define MNT_FS_KERNEL (1 << 4) /* data from /proc/{mounts,self/mountinfo} */ +#define MNT_FS_MERGED (1 << 5) /* already merged data from /run/mount/utab */ + +extern int __mnt_fs_get_flags(struct libmnt_fs *fs); +extern int __mnt_fs_set_flags(struct libmnt_fs *fs, int flags); + + +/* + * mtab/fstab/mountinfo file + */ +struct libmnt_table { + int fmt; /* MNT_FMT_* file format */ + int nents; /* number of valid entries */ + + struct libmnt_cache *cache; /* canonicalized paths/tags cache */ + + int (*errcb)(struct libmnt_table *tb, + const char *filename, int line); + + struct list_head ents; /* list of entries (libmnt_fs) */ +}; + +extern struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt); + +/* + * Tab file format + */ +enum { + MNT_FMT_GUESS, + MNT_FMT_FSTAB, /* /etc/{fs,m}tab */ + MNT_FMT_MTAB = MNT_FMT_FSTAB, /* alias */ + MNT_FMT_MOUNTINFO, /* /proc/#/mountinfo */ + MNT_FMT_UTAB /* /dev/.mount/utab */ +}; + + +/* + * Mount context -- high-level API + */ +struct libmnt_context +{ + int action; /* MNT_ACT_{MOUNT,UMOUNT} */ + int restricted; /* root or not? */ + + char *fstype_pattern; /* for mnt_match_fstype() */ + char *optstr_pattern; /* for mnt_match_options() */ + + struct libmnt_fs *fs; /* filesystem description (type, mountpoint, device, ...) */ + + struct libmnt_table *fstab; /* fstab (or mtab for some remounts) entries */ + struct libmnt_table *mtab; /* mtab entries */ + + int optsmode; /* fstab optstr mode MNT_OPTSMODE_{AUTO,FORCE,IGNORE} */ + + unsigned long mountflags; /* final mount(2) flags */ + const void *mountdata; /* final mount(2) data, string or binary data */ + + unsigned long user_mountflags; /* MNT_MS_* (loop=, user=, ...) */ + + struct libmnt_cache *cache; /* paths cache */ + struct libmnt_lock *lock; /* mtab lock */ + struct libmnt_update *update;/* mtab/utab update */ + + const char *mtab_path; /* path to mtab */ + int mtab_writable; /* is mtab writable */ + + const char *utab_path; /* path to utab */ + int utab_writable; /* is utab writable */ + + int flags; /* private context flags */ + int ambi; /* libblkid returns ambivalent result */ + + char *helper; /* name of the used /sbin/[u]mount.<type> helper */ + int helper_status; /* helper wait(2) status */ + int helper_exec_status; /* 1: not called yet, 0: success, <0: -errno */ + + char *orig_user; /* original (non-fixed) user= option */ + + int syscall_status; /* 1: not called yet, 0: success, <0: -errno */ +}; + +/* flags */ +#define MNT_FL_NOMTAB (1 << 1) +#define MNT_FL_FAKE (1 << 2) +#define MNT_FL_SLOPPY (1 << 3) +#define MNT_FL_VERBOSE (1 << 4) +#define MNT_FL_NOHELPERS (1 << 5) +#define MNT_FL_LOOPDEL (1 << 6) +#define MNT_FL_LAZY (1 << 7) +#define MNT_FL_FORCE (1 << 8) +#define MNT_FL_NOCANONICALIZE (1 << 9) +#define MNT_FL_RDONLY_UMOUNT (1 << 11) /* remount,ro after EBUSY umount(2) */ + +#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> */ + +/* default flags */ +#define MNT_FL_DEFAULT 0 + +/* lock.c */ +extern int mnt_lock_use_simplelock(struct libmnt_lock *ml, int enable); + +/* optmap.c */ +extern const struct libmnt_optmap *mnt_optmap_get_entry( + struct libmnt_optmap const **maps, + int nmaps, const char *name, + size_t namelen, + const struct libmnt_optmap **mapent); + +/* optstr.c */ +extern int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end); +extern int mnt_optstr_fix_gid(char **optstr, char *value, size_t valsz, char **next); +extern int mnt_optstr_fix_uid(char **optstr, char *value, size_t valsz, char **next); +extern int mnt_optstr_fix_secontext(char **optstr, char *value, size_t valsz, char **next); +extern int mnt_optstr_fix_user(char **optstr); + +/* fs.c */ +extern struct libmnt_fs *mnt_copy_mtab_fs(const struct libmnt_fs *fs); +extern int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source); +extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype); + +/* context.c */ +extern int mnt_context_prepare_srcpath(struct libmnt_context *cxt); +extern int mnt_context_prepare_target(struct libmnt_context *cxt); +extern int mnt_context_guess_fstype(struct libmnt_context *cxt); +extern int mnt_context_prepare_helper(struct libmnt_context *cxt, + const char *name, const char *type); +extern int mnt_context_prepare_update(struct libmnt_context *cxt); +extern struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt); +extern int mnt_context_merge_mflags(struct libmnt_context *cxt); +extern int mnt_context_update_tabs(struct libmnt_context *cxt); + +extern int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg); +extern int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg); + +/* tab_update.c */ +extern struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd); +extern int mnt_update_set_filename(struct libmnt_update *upd, + const char *filename, int userspace_only); + +#endif /* _LIBMOUNT_PRIVATE_H */ diff --git a/libmount/src/optmap.c b/libmount/src/optmap.c new file mode 100644 index 000000000..7db6d0314 --- /dev/null +++ b/libmount/src/optmap.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: optmap + * @title: Option maps + * @short_description: description for mount options + * + * The mount(2) linux syscall uses two arguments for mount options: + * + * @mountflags: (see MS_* macros in linux/fs.h) + * + * @mountdata: (usully a comma separated string of options) + * + * The libmount uses options-map(s) to describe mount options. + * + * The option description (map entry) includes: + * + * @name: and argument name + * + * @id: (in the map unique identifier or a mountflags, e.g MS_RDONLY) + * + * @mask: (MNT_INVERT, MNT_NOMTAB) + * + * The option argument value is defined by: + * + * "=" -- required argument, e.g "comment=" + * + * "[=]" -- optional argument, e.g. "loop[=]" + * + * Example: + * + * <informalexample> + * <programlisting> + * #define MY_MS_FOO (1 << 1) + * #define MY_MS_BAR (1 << 2) + * + * libmnt_optmap myoptions[] = { + * { "foo", MY_MS_FOO }, + * { "nofoo", MY_MS_FOO | MNT_INVERT }, + * { "bar=", MY_MS_BAR }, + * { NULL } + * }; + * </programlisting> + * </informalexample> + * + * The libmount defines two basic built-in options maps: + * + * @MNT_LINUX_MAP: fs-independent kernel mount options (usually MS_* flags) + * + * @MNT_USERSPACE_MAP: userspace specific mount options (e.g. "user", "loop") + * + * For more details about option map struct see "struct mnt_optmap" in + * mount/mount.h. + */ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> + +#include "nls.h" +#include "mountP.h" + +/* + * fs-independent mount flags (built-in MNT_LINUX_MAP) + */ +static const struct libmnt_optmap linux_flags_map[] = +{ + { "ro", MS_RDONLY }, /* read-only */ + { "rw", MS_RDONLY, MNT_INVERT }, /* read-write */ + { "exec", MS_NOEXEC, MNT_INVERT }, /* permit execution of binaries */ + { "noexec", MS_NOEXEC }, /* don't execute binaries */ + { "suid", MS_NOSUID, MNT_INVERT }, /* honor suid executables */ + { "nosuid", MS_NOSUID }, /* don't honor suid executables */ + { "dev", MS_NODEV, MNT_INVERT }, /* interpret device files */ + { "nodev", MS_NODEV }, /* don't interpret devices */ + + { "sync", MS_SYNCHRONOUS }, /* synchronous I/O */ + { "async", MS_SYNCHRONOUS, MNT_INVERT },/* asynchronous I/O */ + + { "dirsync", MS_DIRSYNC }, /* synchronous directory modifications */ + { "remount", MS_REMOUNT, MNT_NOMTAB }, /* alter flags of mounted FS */ + { "bind", MS_BIND }, /* Remount part of tree elsewhere */ + { "rbind", MS_BIND | MS_REC }, /* Idem, plus mounted subtrees */ +#ifdef MS_NOSUB + { "sub", MS_NOSUB, MNT_INVERT }, /* allow submounts */ + { "nosub", MS_NOSUB }, /* don't allow submounts */ +#endif +#ifdef MS_SILENT + { "silent", MS_SILENT }, /* be quiet */ + { "loud", MS_SILENT, MNT_INVERT }, /* print out messages. */ +#endif +#ifdef MS_MANDLOCK + { "mand", MS_MANDLOCK }, /* Allow mandatory locks on this FS */ + { "nomand", MS_MANDLOCK, MNT_INVERT }, /* Forbid mandatory locks on this FS */ +#endif +#ifdef MS_NOATIME + { "atime", MS_NOATIME, MNT_INVERT }, /* Update access time */ + { "noatime", MS_NOATIME }, /* Do not update access time */ +#endif +#ifdef MS_I_VERSION + { "iversion", MS_I_VERSION }, /* Update inode I_version time */ + { "noiversion", MS_I_VERSION, MNT_INVERT},/* Don't update inode I_version time */ +#endif +#ifdef MS_NODIRATIME + { "diratime", MS_NODIRATIME, MNT_INVERT }, /* Update dir access times */ + { "nodiratime", MS_NODIRATIME }, /* Do not update dir access times */ +#endif +#ifdef MS_RELATIME + { "relatime", MS_RELATIME }, /* Update access times relative to mtime/ctime */ + { "norelatime", MS_RELATIME, MNT_INVERT }, /* Update access time without regard to mtime/ctime */ +#endif +#ifdef MS_STRICTATIME + { "strictatime", MS_STRICTATIME }, /* Strict atime semantics */ + { "nostrictatime", MS_STRICTATIME, MNT_INVERT }, /* kernel default atime */ +#endif + { NULL, 0, 0 } +}; + +/* + * userspace mount option (built-in MNT_USERSPACE_MAP) + * + * TODO: offset=, sizelimit=, encryption=, vfs= + */ +static const struct libmnt_optmap userspace_opts_map[] = +{ + { "defaults", 0, 0 }, /* default options */ + + { "auto", MNT_MS_NOAUTO, MNT_INVERT | MNT_NOMTAB }, /* Can be mounted using -a */ + { "noauto", MNT_MS_NOAUTO, 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 */ + + { "users", MNT_MS_USERS, MNT_NOMTAB }, /* Allow ordinary users to mount */ + { "nousers", MNT_MS_USERS, MNT_INVERT | MNT_NOMTAB }, /* Forbid ordinary users to mount */ + + { "owner", MNT_MS_OWNER, MNT_NOMTAB }, /* Let the owner of the device mount */ + { "noowner", MNT_MS_OWNER, MNT_INVERT | MNT_NOMTAB }, /* Device owner has no special privs */ + + { "group", MNT_MS_GROUP, MNT_NOMTAB }, /* Let the group of the device mount */ + { "nogroup", MNT_MS_GROUP, MNT_INVERT | MNT_NOMTAB }, /* Device group has no special privs */ + + { "_netdev", MNT_MS_NETDEV }, /* Device requires network */ + + { "comment=", MNT_MS_COMMENT, MNT_NOMTAB }, /* fstab comment only */ + { "x-", MNT_MS_XCOMMENT, MNT_NOMTAB | MNT_PREFIX }, /* x- options */ + + { "loop[=]", MNT_MS_LOOP }, /* use the loop device */ + + { "nofail", MNT_MS_NOFAIL, MNT_NOMTAB }, /* Do not fail if ENOENT on dev */ + + { "uhelper=", MNT_MS_UHELPER }, /* /sbin/umount.<helper> */ + + { "helper=", MNT_MS_HELPER }, /* /sbin/umount.<helper> */ + + { NULL, 0, 0 } +}; + +/** + * mnt_get_builtin_map: + * @id: map id -- MNT_LINUX_MAP or MNT_USERSPACE_MAP + * + * MNT_LINUX_MAP - Linux kernel fs-independent mount options + * (usually MS_* flags, see linux/fs.h) + * + * MNT_USERSPACE_MAP - userpace mount(8) specific mount options + * (e.g user=, _netdev, ...) + * + * Returns: static built-in libmount map. + */ +const struct libmnt_optmap *mnt_get_builtin_optmap(int id) +{ + assert(id); + + if (id == MNT_LINUX_MAP) + return linux_flags_map; + else if (id == MNT_USERSPACE_MAP) + return userspace_opts_map; + return NULL; +} + +/* + * Lookups for the @name in @maps and returns a map and in @mapent + * returns the map entry + */ +const struct libmnt_optmap *mnt_optmap_get_entry( + struct libmnt_optmap const **maps, + int nmaps, + const char *name, + size_t namelen, + const struct libmnt_optmap **mapent) +{ + int i; + + assert(maps); + assert(nmaps); + assert(name); + assert(namelen); + + if (mapent) + *mapent = NULL; + + for (i = 0; i < nmaps; i++) { + const struct libmnt_optmap *map = maps[i]; + const struct libmnt_optmap *ent; + const char *p; + + for (ent = map; ent && ent->name; ent++) { + if (ent->mask & MNT_PREFIX) { + if (startswith(name, ent->name)) { + if (mapent) + *mapent = ent; + return map; + } + continue; + } + if (strncmp(ent->name, name, namelen)) + continue; + p = ent->name + namelen; + if (*p == '\0' || *p == '=' || *p == '[') { + if (mapent) + *mapent = ent; + return map; + } + } + } + return NULL; +} + diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c new file mode 100644 index 000000000..406afc4b7 --- /dev/null +++ b/libmount/src/optstr.c @@ -0,0 +1,1224 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: optstr + * @title: Options string + * @short_description: low-level API for work with mount options + * + * This is simple and low-level API to work with mount options that are stored + * in string. This API is independent on the high-level options container and + * option maps. + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> + +#ifdef HAVE_LIBSELINUX +#include <selinux/selinux.h> +#include <selinux/context.h> +#endif + +#include "nls.h" +#include "mountP.h" + +/* + * Option location + */ +struct libmnt_optloc { + char *begin; + char *end; + char *value; + size_t valsz; + size_t namesz; +}; + +#define mnt_init_optloc(_ol) (memset((_ol), 0, sizeof(struct libmnt_optloc))) + +/* + * Parses the first option from @optstr. The @optstr pointer is set to begin of + * the next option. + * + * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success. + */ +static int mnt_optstr_parse_next(char **optstr, char **name, size_t *namesz, + char **value, size_t *valsz) +{ + int open_quote = 0; + char *start = NULL, *stop = NULL, *p, *sep = NULL; + char *optstr0; + + assert(optstr); + assert(*optstr); + + optstr0 = *optstr; + + if (name) + *name = NULL; + if (namesz) + *namesz = 0; + if (value) + *value = NULL; + if (valsz) + *valsz = 0; + + for (p = optstr0; p && *p; p++) { + if (!start) + start = p; /* begin of the option item */ + if (*p == '"') + open_quote ^= 1; /* reverse the status */ + if (open_quote) + continue; /* still in quoted block */ + if (!sep && *p == '=') + sep = p; /* name and value separator */ + if (*p == ',') + stop = p; /* terminate the option item */ + else if (*(p + 1) == '\0') + stop = p + 1; /* end of optstr */ + if (!start || !stop) + continue; + if (stop <= start) + goto error; + + if (name) + *name = start; + if (namesz) + *namesz = sep ? sep - start : stop - start; + *optstr = *stop ? stop + 1 : stop; + + if (sep) { + if (value) + *value = sep + 1; + if (valsz) + *valsz = stop - sep - 1; + } + return 0; + } + + return 1; /* end of optstr */ + +error: + DBG(OPTIONS, mnt_debug("parse error: \"%s\"", optstr0)); + return -EINVAL; +} + +/* + * Locates the first option that match with @name. The @end is set to + * char behind the option (it means ',' or \0). + * + * Returns negative number on parse error, 1 when not found and 0 on success. + */ +static int mnt_optstr_locate_option(char *optstr, const char *name, + struct libmnt_optloc *ol) +{ + char *n; + size_t namesz, nsz; + int rc; + + if (!optstr) + return 1; + + assert(name); + assert(optstr); + + namesz = strlen(name); + + do { + rc = mnt_optstr_parse_next(&optstr, &n, &nsz, + &ol->value, &ol->valsz); + if (rc) + break; + + if (namesz == nsz && strncmp(n, name, nsz) == 0) { + ol->begin = n; + ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr; + ol->namesz = nsz; + return 0; + } + } while(1); + + return rc; +} + +/** + * 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 + * + * Parses the first option in @optstr. + * + * Returns: 0 on success, 1 at the end of @optstr or negative number in case of + * error. + */ +int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz, + char **value, size_t *valuesz) +{ + if (!optstr || !*optstr) + return -EINVAL; + return mnt_optstr_parse_next(optstr, name, namesz, value, valuesz); +} + +static int __mnt_optstr_append_option(char **optstr, + const char *name, size_t nsz, + const char *value, size_t vsz) +{ + char *p; + size_t sz, osz; + + assert(name); + + osz = *optstr ? strlen(*optstr) : 0; + + sz = osz + nsz + 1; /* 1: '\0' */ + if (osz) + sz++; /* ',' options separator */ + if (vsz) + sz += vsz + 1; /* 1: '=' */ + + p = realloc(*optstr, sz); + if (!p) + return -ENOMEM; + *optstr = p; + + if (osz) { + p += osz; + *p++ = ','; + } + + memcpy(p, name, nsz); + p += nsz; + + if (vsz) { + *p++ = '='; + memcpy(p, value, vsz); + p += vsz; + } + *p = '\0'; + + return 0; +} + +/** + * mnt_optstr_append_option: + * @optstr: option string or NULL, returns reallocated string + * @name: value name + * @value: value + * + * Returns: 0 on success or -1 in case of error. After error the @optstr should + * be unmodified. + */ +int mnt_optstr_append_option(char **optstr, const char *name, const char *value) +{ + size_t vsz, nsz; + + if (!name) + return 0; + + nsz = strlen(name); + vsz = value ? strlen(value) : 0; + + return __mnt_optstr_append_option(optstr, name, nsz, value, vsz); +} + +/** + * mnt_optstr_prepend_option: + * @optstr: option string or NULL, returns reallocated string + * @name: value name + * @value: value + * + * Returns: 0 on success or -1 in case of error. After error the @optstr should + * be unmodified. + */ +int mnt_optstr_prepend_option(char **optstr, const char *name, const char *value) +{ + int rc = 0; + char *tmp = *optstr; + + *optstr = NULL; + + rc = mnt_optstr_append_option(optstr, name, value); + if (!rc && tmp && *tmp) + rc = mnt_optstr_append_option(optstr, tmp, NULL); + if (!rc) { + free(tmp); + return 0; + } + + free(*optstr); + *optstr = tmp; + + DBG(OPTIONS, mnt_debug("failed to prepend '%s[=%s]' to '%s'", + name, value, *optstr)); + return rc; +} + +/** + * mnt_optstr_get_option: + * @optstr: string with comma separated list of options + * @name: requested option name + * @value: returns pointer to the begin 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 + * of error. + */ +int mnt_optstr_get_option(char *optstr, const char *name, + char **value, size_t *valsz) +{ + struct libmnt_optloc ol; + int rc; + + mnt_init_optloc(&ol); + + rc = mnt_optstr_locate_option(optstr, name, &ol); + if (!rc) { + if (value) + *value = ol.value; + if (valsz) + *valsz = ol.valsz; + } + return rc; +} + +/* + * The result never starts or ends with 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) +{ + size_t sz; + + if (!optstr || !begin || !end) + return -EINVAL; + + if ((begin == *optstr || *(begin - 1) == ',') && *end == ',') + end++; + + sz = strlen(end); + + memmove(begin, end, sz + 1); + if (!*begin && *(begin - 1) == ',') + *(begin - 1) = '\0'; + + return 0; +} + +/* insert 'substr' or '=substr' to @str on position @pos */ +static int insert_value(char **str, char *pos, const char *substr, char **next) +{ + size_t subsz = strlen(substr); /* substring size */ + size_t strsz = strlen(*str); + size_t possz = strlen(pos); + size_t posoff; + char *p = NULL; + int sep; + + /* is it necessary to prepend '=' before the substring ? */ + sep = !(pos > *str && *(pos - 1) == '='); + + /* save an offset of the place where we need add substr */ + posoff = pos - *str; + + p = realloc(*str, strsz + sep + subsz + 1); + if (!p) + return -ENOMEM; + + /* zeroize new allocated memory -- valgind loves is... */ + memset(p + strsz, 0, sep + subsz + 1); + + /* set pointers to the reallocated string */ + *str = p; + pos = p + posoff; + + if (possz) + /* create a room for new substring */ + memmove(pos + subsz + sep, pos, possz + 1); + if (sep) + *pos++ = '='; + + memcpy(pos, substr, subsz); + + if (next) { + /* set pointer to the next option */ + *next = pos + subsz + sep + 1; + if (**next == ',') + (*next)++; + } + return 0; +} + +/** + * mnt_optstr_set_option: + * @optstr: string with comma separated list of options + * @name: requested option + * @value: new value or NULL + * + * Set or unset option @value. + * + * Returns: 0 on success, 1 when not found the @name or negative number in case + * of error. + */ +int mnt_optstr_set_option(char **optstr, const char *name, const char *value) +{ + struct libmnt_optloc ol; + char *nameend; + int rc = 1; + + if (!optstr) + return -EINVAL; + + mnt_init_optloc(&ol); + + if (*optstr) + rc = mnt_optstr_locate_option(*optstr, name, &ol); + if (rc < 0) + return rc; /* parse error */ + if (rc == 1) + return mnt_optstr_append_option(optstr, name, value); /* not found */ + + nameend = ol.begin + ol.namesz; + + if (value == NULL && ol.value && ol.valsz) + /* remove unwanted "=value" */ + mnt_optstr_remove_option_at(optstr, nameend, ol.end); + + else if (value && ol.value == NULL) + /* insert "=value" */ + rc = insert_value(optstr, nameend, value, NULL); + + else if (value && ol.value && strlen(value) == ol.valsz) + /* simply replace =value */ + memcpy(ol.value, value, ol.valsz); + + else if (value && ol.value) { + mnt_optstr_remove_option_at(optstr, nameend, ol.end); + rc = insert_value(optstr, nameend, value, NULL); + } + return rc; +} + +/** + * mnt_optstr_remove_option: + * @optstr: string with comma separated list of options + * @name: requested option name + * + * Returns: 0 on success, 1 when not found the @name or negative number in case + * of error. + */ +int mnt_optstr_remove_option(char **optstr, const char *name) +{ + struct libmnt_optloc ol; + int rc; + + mnt_init_optloc(&ol); + + rc = mnt_optstr_locate_option(*optstr, name, &ol); + if (rc != 0) + return rc; + + mnt_optstr_remove_option_at(optstr, ol.begin, ol.end); + return 0; +} + +/** + * mnt_split_optstr: + * @optstr: string with comma separated list of options + * @user: returns newly allocated string with userspace options + * @vfs: returns newly allocated string with VFS options + * @fs: returns newly allocated string with FS options + * @ignore_user: option mask for options that should be ignored + * @ignore_vfs: option mask for options that should be ignored + * + * For example: + * + * mnt_split_optstr(optstr, &u, NULL, NULL, MNT_NOMTAB, 0); + * + * returns all userspace options, the options that does 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. + */ +int mnt_split_optstr(const char *optstr, char **user, char **vfs, + char **fs, int ignore_user, int ignore_vfs) +{ + char *name, *val, *str = (char *) optstr; + size_t namesz, valsz; + struct libmnt_optmap const *maps[2]; + + assert(optstr); + + if (!optstr) + return -EINVAL; + + maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP); + maps[1] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); + + if (vfs) + *vfs = NULL; + if (fs) + *fs = NULL; + if (user) + *user = NULL; + + while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { + int rc = 0; + const struct libmnt_optmap *ent = NULL; + const struct libmnt_optmap *m = + mnt_optmap_get_entry(maps, 2, name, namesz, &ent); + + if (ent && !ent->id) + continue; /* ignore undefined options (comments) */ + + if (ent && m && m == maps[0] && vfs) { + if (ignore_vfs && (ent->mask & ignore_vfs)) + continue; + rc = __mnt_optstr_append_option(vfs, name, namesz, + val, valsz); + } else if (ent && m && m == maps[1] && user) { + if (ignore_user && (ent->mask & ignore_user)) + continue; + rc = __mnt_optstr_append_option(user, name, namesz, + val, valsz); + } else if (!m && fs) + rc = __mnt_optstr_append_option(fs, name, namesz, + val, valsz); + if (rc) { + if (vfs) + free(*vfs); + if (fs) + free(*fs); + if (user) + free(*user); + return rc; + } + } + + return 0; +} + +/** + * mnt_optstr_get_options + * @optstr: string with 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: + * + * mnt_split_optstr_by_map(optstr, &p, + * mnt_get_builtin_optmap(MNT_LINUX_MAP), + * MNT_NOMTAB); + * + * returns all VFS options, the options that does not belong to mtab + * are ignored. + * + * Returns: 0 on success, or negative number in case of error. + */ +int mnt_optstr_get_options(const char *optstr, char **subset, + const struct libmnt_optmap *map, int ignore) +{ + struct libmnt_optmap const *maps[1]; + char *name, *val, *str = (char *) optstr; + size_t namesz, valsz; + + if (!optstr || !subset) + return -EINVAL; + + maps[0] = map; + *subset = NULL; + + while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { + int rc = 0; + const struct libmnt_optmap *ent; + + mnt_optmap_get_entry(maps, 1, name, namesz, &ent); + + if (!ent || !ent->id) + continue; /* ignore undefined options (comments) */ + + if (ignore && (ent->mask & ignore)) + continue; + rc = __mnt_optstr_append_option(subset, name, namesz, val, valsz); + if (rc) { + free(*subset); + return rc; + } + } + + return 0; +} + + +/** + * mnt_optstr_get_flags: + * @optstr: string with comma separated list of options + * @flags: returns mount flags + * @map: options map + * + * Returns in @flags IDs of options from @optstr as defined in the @map. + * + * For example: + * + * "bind,exec,foo,bar" --returns-> MS_BIND + * + * "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. + * + * Returns: 0 on success or negative number in case of error + */ +int mnt_optstr_get_flags(const char *optstr, unsigned long *flags, + const struct libmnt_optmap *map) +{ + struct libmnt_optmap const *maps[2]; + char *name, *str = (char *) optstr; + size_t namesz = 0; + int nmaps = 0; + + assert(optstr); + + if (!optstr || !flags || !map) + return -EINVAL; + + maps[nmaps++] = map; + + if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP)) + /* + * Add userspace map -- the "user" is interpreted as + * MS_NO{EXEC,SUID,DEV}. + */ + maps[nmaps++] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); + + while(!mnt_optstr_next_option(&str, &name, &namesz, NULL, NULL)) { + const struct libmnt_optmap *ent; + const struct libmnt_optmap *m; + + m = mnt_optmap_get_entry(maps, nmaps, name, namesz, &ent); + if (!m || !ent || !ent->id) + continue; + + if (m == map) { /* requested map */ + if (ent->mask & MNT_INVERT) + *flags &= ~ent->id; + else + *flags |= ent->id; + + } else if (nmaps == 2 && m == maps[1]) { + /* + * Special case -- translate "user" to MS_ options + */ + if (ent->mask & MNT_INVERT) + continue; + if (ent->id & (MNT_MS_OWNER | MNT_MS_GROUP)) + *flags |= MS_OWNERSECURE; + else if (ent->id & (MNT_MS_USER | MNT_MS_USERS)) + *flags |= MS_SECURE; + } + } + + return 0; +} + +/** + * mnt_optstr_apply_flags: + * @optstr: string with comma separated list of options + * @flags: returns mount flags + * @map: options map + * + * Removes/adds options to the @optstr according to flags. For example: + * + * MS_NOATIME and "foo,bar,noexec" --returns-> "foo,bar,noatime" + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_optstr_apply_flags(char **optstr, unsigned long flags, + const struct libmnt_optmap *map) +{ + struct libmnt_optmap const *maps[1]; + char *name, *next, *val; + size_t namesz = 0, valsz = 0; + unsigned long fl; + int rc = 0; + + assert(optstr); + + if (!optstr || !map) + return -EINVAL; + + DBG(CXT, mnt_debug("appling 0x%08lu flags '%s'", flags, *optstr)); + + maps[0] = map; + next = *optstr; + fl = flags; + + /* + * There is a convetion that 'rw/ro' flags is always at the begin of + * the string (athough the 'rw' is unnecessary). + */ + if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP)) { + const char *o = (fl & MS_RDONLY) ? "ro" : "rw"; + + if (next && + (!strncmp(next, "rw", 2) || !strncmp(next, "ro", 2)) && + (*(next + 2) == '\0' || *(next + 2) == ',')) { + + /* already set, be paranoid and fix it */ + memcpy(next, o, 2); + } else { + rc = mnt_optstr_prepend_option(optstr, o, NULL); + if (rc) + goto err; + next = *optstr; /* because realloc() */ + } + fl &= ~MS_RDONLY; + next += 2; + if (*next == ',') + next++; + } + + if (next && *next) { + /* + * scan @optstr and remove options that are missing in + * the @flags + */ + while(!mnt_optstr_next_option(&next, &name, &namesz, + &val, &valsz)) { + const struct libmnt_optmap *ent; + + if (mnt_optmap_get_entry(maps, 1, name, namesz, &ent)) { + /* + * remove unwanted option (rw/ro is already set) + */ + if (!ent->id) + continue; + if (ent->id == MS_RDONLY || + (ent->mask & MNT_INVERT) || + !(fl & ent->id)) { + + char *end = val ? val + valsz : + name + namesz; + next = name; + rc = mnt_optstr_remove_option_at( + optstr, name, end); + if (rc) + goto err; + } + if (!(ent->mask & MNT_INVERT)) + fl &= ~ent->id; + } + } + } + + /* add missing options */ + if (fl) { + const struct libmnt_optmap *ent; + char *p; + + for (ent = map; ent && ent->name; ent++) { + if ((ent->mask & MNT_INVERT) || !(fl & ent->id)) + continue; + + /* don't add options which require values (e.g. offset=%d) */ + p = strchr(ent->name, '='); + if (p) { + if (*(p - 1) == '[') + p--; /* name[=] */ + else + continue; /* name= */ + + p = strndup(ent->name, p - ent->name); + if (!p) { + rc = -ENOMEM; + goto err; + } + mnt_optstr_append_option(optstr, p, NULL); + free(p); + } else + mnt_optstr_append_option(optstr, ent->name, NULL); + } + } + + return rc; +err: + DBG(CXT, mnt_debug("failed to apply flags [rc=%d]", rc)); + return rc; +} + +/* + * @optstr: string with comma separated list of options + * @value: pointer to the begin of the context value + * @valsz: size of the value + * @next: returns pointer to the next option (optional argument) + * + * Translates SELinux context from human to raw format. The function does not + * modify @optstr and returns zero if libmount is compiled without SELinux + * support. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_optstr_fix_secontext(char **optstr, char *value, size_t valsz, char **next) +{ + int rc = 0; + +#ifdef HAVE_LIBSELINUX + security_context_t raw = NULL; + char *p, *val, *begin, *end; + size_t sz; + + if (!optstr || !*optstr || !value || !valsz) + return -EINVAL; + + DBG(CXT, mnt_debug("fixing SELinux context")); + + begin = value; + end = value + valsz; + + /* the selinux contexts are quoted */ + if (*value == '"') { + if (valsz <= 2 || *(value + valsz - 1) != '"') + return -EINVAL; /* improperly quoted option string */ + value++; + valsz -= 2; + } + + p = strndup(value, valsz); + if (!p) + return -ENOMEM; + + + /* translate the context */ + rc = selinux_trans_to_raw_context((security_context_t) p, &raw); + + DBG(CXT, mnt_debug("SELinux context '%s' translated to '%s'", + p, rc == -1 ? "FAILED" : (char *) raw)); + + free(p); + if (rc == -1 || !raw) + return -EINVAL; + + + /* create quoted string from the raw context */ + sz = strlen((char *) raw); + if (!sz) + return -EINVAL; + + p = val = malloc(valsz + 3); + if (!val) + return -ENOMEM; + + *p++ = '"'; + memcpy(p, raw, sz); + p += sz; + *p++ = '"'; + *p = '\0'; + + freecon(raw); + + /* set new context */ + mnt_optstr_remove_option_at(optstr, begin, end); + rc = insert_value(optstr, begin, val, next); + free(val); +#endif + return rc; +} + +static int set_uint_value(char **optstr, unsigned int num, + char *begin, char *end, char **next) +{ + char buf[40]; + snprintf(buf, sizeof(buf), "%u", num); + + mnt_optstr_remove_option_at(optstr, begin, end); + return insert_value(optstr, begin, buf, next); +} + +/* + * @optstr: string with comma separated list of options + * @value: pointer to the begin of the uid value + * @valsz: size of the value + * @next: returns pointer to the next option (optional argument) + + * Translates "<username>" or "useruid" to the real UID. + * + * For example: + * 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. + */ +int mnt_optstr_fix_uid(char **optstr, char *value, size_t valsz, char **next) +{ + int rc = 0; + char *end; + + if (!optstr || !*optstr || !value || !valsz) + return -EINVAL; + + DBG(CXT, mnt_debug("fixing uid")); + + end = value + valsz; + + if (valsz == 7 && !strncmp(value, "useruid", 7) && + (*(value + 7) == ',' || !*(value + 7))) + rc = set_uint_value(optstr, getuid(), value, end, next); + + else if (!isdigit(*value)) { + uid_t id; + char *p = strndup(value, valsz); + if (!p) + return -ENOMEM; + rc = mnt_get_uid(p, &id); + free(p); + + if (!rc) + rc = set_uint_value(optstr, id, value, end, next); + + } else if (next) { + /* nothing */ + *next = value + valsz; + if (**next == ',') + (*next)++; + } + + return rc; +} + +/* + * @optstr: string with comma separated list of options + * @value: pointer to the begin 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. + */ +int mnt_optstr_fix_gid(char **optstr, char *value, size_t valsz, char **next) +{ + int rc = 0; + char *end; + + if (!optstr || !*optstr || !value || !valsz) + return -EINVAL; + + DBG(CXT, mnt_debug("fixing gid")); + + end = value + valsz; + + if (valsz == 7 && !strncmp(value, "usergid", 7) && + (*(value + 7) == ',' || !*(value + 7))) + rc = set_uint_value(optstr, getgid(), value, end, next); + + else if (!isdigit(*value)) { + gid_t id; + char *p = strndup(value, valsz); + if (!p) + return -ENOMEM; + rc = mnt_get_gid(p, &id); + free(p); + + if (!rc) + rc = set_uint_value(optstr, id, value, end, next); + + } else if (next) { + /* nothing */ + *next = value + valsz; + if (**next == ',') + (*next)++; + } + return rc; +} + +/* + * Converts "user" to "user=<username>". + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_optstr_fix_user(char **optstr) +{ + char *username; + struct libmnt_optloc ol; + int rc = 0; + + DBG(CXT, mnt_debug("fixing user")); + + mnt_init_optloc(&ol); + + rc = mnt_optstr_locate_option(*optstr, "user", &ol); + if (rc) + return rc == 1 ? 0 : rc; /* 1: user= not found */ + + username = mnt_get_username(getuid()); + if (!username) + return -ENOMEM; + + if (!ol.valsz || (ol.value && strncmp(ol.value, username, ol.valsz))) { + if (ol.valsz) + /* remove old value */ + mnt_optstr_remove_option_at(optstr, ol.value, ol.end); + + rc = insert_value(optstr, ol.value ? ol.value : ol.end, + username, NULL); + } + + free(username); + return rc; +} + +#ifdef TEST_PROGRAM + +int test_append(struct libmnt_test *ts, int argc, char *argv[]) +{ + const char *value = NULL, *name; + char *optstr; + int rc; + + if (argc < 3) + return -EINVAL; + optstr = strdup(argv[1]); + name = argv[2]; + + if (argc == 4) + value = argv[3]; + + rc = mnt_optstr_append_option(&optstr, name, value); + if (!rc) + printf("result: >%s<\n", optstr); + return rc; +} + +int test_prepend(struct libmnt_test *ts, int argc, char *argv[]) +{ + const char *value = NULL, *name; + char *optstr; + int rc; + + if (argc < 3) + return -EINVAL; + optstr = strdup(argv[1]); + name = argv[2]; + + if (argc == 4) + value = argv[3]; + + rc = mnt_optstr_prepend_option(&optstr, name, value); + if (!rc) + printf("result: >%s<\n", optstr); + return rc; +} + +int test_split(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr, *user = NULL, *fs = NULL, *vfs = NULL; + int rc; + + if (argc < 2) + return -EINVAL; + + optstr = strdup(argv[1]); + + rc = mnt_split_optstr(optstr, &user, &vfs, &fs, 0, 0); + if (!rc) { + printf("user : %s\n", user); + printf("vfs : %s\n", vfs); + printf("fs : %s\n", fs); + } + + free(user); + free(vfs); + free(fs); + free(optstr); + return rc; +} + +int test_flags(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr; + int rc; + unsigned long fl = 0; + + if (argc < 2) + return -EINVAL; + + optstr = strdup(argv[1]); + + rc = mnt_optstr_get_flags(optstr, &fl, mnt_get_builtin_optmap(MNT_LINUX_MAP)); + if (rc) + return rc; + printf("mountflags: 0x%08lx\n", fl); + + fl = 0; + rc = mnt_optstr_get_flags(optstr, &fl, mnt_get_builtin_optmap(MNT_USERSPACE_MAP)); + if (rc) + return rc; + printf("userspace-mountflags: 0x%08lx\n", fl); + + free(optstr); + return rc; +} + +int test_apply(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr; + int rc, map; + unsigned long flags; + + if (argc < 4) + return -EINVAL; + + if (!strcmp(argv[1], "--user")) + map = MNT_USERSPACE_MAP; + else if (!strcmp(argv[1], "--linux")) + map = MNT_LINUX_MAP; + else { + fprintf(stderr, "unknown option '%s'\n", argv[1]); + return -EINVAL; + } + + optstr = strdup(argv[2]); + flags = strtoul(argv[3], NULL, 16); + + printf("flags: 0x%08lx\n", flags); + + rc = mnt_optstr_apply_flags(&optstr, flags, mnt_get_builtin_optmap(map)); + printf("optstr: %s\n", optstr); + + free(optstr); + return rc; +} + +int test_set(struct libmnt_test *ts, int argc, char *argv[]) +{ + const char *value = NULL, *name; + char *optstr; + int rc; + + if (argc < 3) + return -EINVAL; + optstr = strdup(argv[1]); + name = argv[2]; + + if (argc == 4) + value = argv[3]; + + rc = mnt_optstr_set_option(&optstr, name, value); + if (!rc) + printf("result: >%s<\n", optstr); + free(optstr); + return rc; +} + +int test_get(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr; + const char *name; + char *val = NULL; + size_t sz = 0; + int rc; + + if (argc < 2) + return -EINVAL; + optstr = argv[1]; + name = argv[2]; + + rc = mnt_optstr_get_option(optstr, name, &val, &sz); + if (rc == 0) { + printf("found; name: %s", name); + if (sz) { + printf(", argument: size=%zd data=", sz); + if (fwrite(val, 1, sz, stdout) != sz) + return -1; + } + printf("\n"); + } else if (rc == 1) + printf("%s: not found\n", name); + else + printf("parse error: %s\n", optstr); + return rc; +} + +int test_remove(struct libmnt_test *ts, int argc, char *argv[]) +{ + const char *name; + char *optstr; + int rc; + + if (argc < 3) + return -EINVAL; + optstr = strdup(argv[1]); + name = argv[2]; + + rc = mnt_optstr_remove_option(&optstr, name); + if (!rc) + printf("result: >%s<\n", optstr); + return rc; +} + +int test_fix(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr; + int rc = 0; + char *name, *val, *next; + size_t valsz, namesz; + + if (argc < 2) + return -EINVAL; + + next = optstr = strdup(argv[1]); + + printf("optstr: %s\n", optstr); + + while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { + + if (!strncmp(name, "uid", 3)) + rc = mnt_optstr_fix_uid(&optstr, val, valsz, &next); + else if (!strncmp(name, "gid", 3)) + rc = mnt_optstr_fix_gid(&optstr, val, valsz, &next); + else if (!strncmp(name, "context", 7)) + rc = mnt_optstr_fix_secontext(&optstr, val, valsz, &next); + if (rc) + break; + } + if (rc) + rc = mnt_optstr_fix_user(&optstr); + + printf("fixed: %s\n", optstr); + + free(optstr); + return rc; + +} + +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" }, + { "--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" }, + { "--split", test_split, "<optstr> split into FS, VFS and userspace" }, + { "--flags", test_flags, "<optstr> convert options to MS_* flags" }, + { "--apply", test_apply, "--{linux,user} <optstr> <mask> apply mask to optstr" }, + { "--fix", test_fix, "<optstr> fix uid=, gid=, user, and context=" }, + + { NULL } + }; + return mnt_run_test(tss, argc, argv); +} +#endif /* TEST_PROGRAM */ diff --git a/libmount/src/tab.c b/libmount/src/tab.c new file mode 100644 index 000000000..227c5aa43 --- /dev/null +++ b/libmount/src/tab.c @@ -0,0 +1,1095 @@ +/* + * Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: tab + * @title: Table of filesystems + * @short_description: container for entries from fstab/mtab/mountinfo + * + * + * Note that mnt_table_find_* functions are mount(8) compatible. These functions + * try to found 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: + * <informalexample> + * <programlisting> + * LABEL=foo /foo auto rw + * /dev/foo /foo auto rw + * </programlisting> + * </informalexample> + * + * where both lines are used for the *same* device, then + * <informalexample> + * <programlisting> + * mnt_table_find_source(tb, "/dev/foo", &fs); + * </programlisting> + * </informalexample> + * will returns the second line, and + * <informalexample> + * <programlisting> + * mnt_table_find_source(tb, "LABEL=foo", &fs); + * </programlisting> + * </informalexample> + * will returns the first entry, and + * <informalexample> + * <programlisting> + * mnt_table_find_source(tb, "UUID=anyuuid", &fs); + * </programlisting> + * </informalexample> + * will returns the first entry (if UUID matches with the device). + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <blkid.h> + +#include "nls.h" +#include "mountP.h" +#include "c.h" + +/** + * mnt_new_table: + * + * The tab is a container for struct libmnt_fs entries that usually represents a fstab, + * mtab or mountinfo file from your system. + * + * See also mnt_table_parse_file(). + * + * Returns: newly allocated tab struct. + */ +struct libmnt_table *mnt_new_table(void) +{ + struct libmnt_table *tb = NULL; + + tb = calloc(1, sizeof(*tb)); + if (!tb) + return NULL; + + DBG(TAB, mnt_debug_h(tb, "alloc")); + + INIT_LIST_HEAD(&tb->ents); + return tb; +} + +/** + * mnt_reset_table: + * @tb: tab pointer + * + * Dealocates all entries (filesystems) from the table + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_reset_table(struct libmnt_table *tb) +{ + if (!tb) + return -EINVAL; + + DBG(TAB, mnt_debug_h(tb, "reset")); + + while (!list_empty(&tb->ents)) { + struct libmnt_fs *fs = list_entry(tb->ents.next, + struct libmnt_fs, ents); + mnt_free_fs(fs); + } + + tb->nents = 0; + return 0; +} + +/** + * mnt_free_table: + * @tb: tab pointer + * + * Deallocates tab struct and all entries. + */ +void mnt_free_table(struct libmnt_table *tb) +{ + if (!tb) + return; + + mnt_reset_table(tb); + + DBG(TAB, mnt_debug_h(tb, "free")); + free(tb); +} + +/** + * mnt_table_get_nents: + * @tb: pointer to tab + * + * Returns: number of valid entries in tab. + */ +int mnt_table_get_nents(struct libmnt_table *tb) +{ + assert(tb); + return tb ? tb->nents : 0; +} + +/** + * 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 + * 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. + * + * See also mnt_new_cache(). + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc) +{ + assert(tb); + if (!tb) + return -EINVAL; + tb->cache = mpc; + return 0; +} + +/** + * mnt_table_get_cache: + * @tb: pointer to tab + * + * Returns: pointer to struct libmnt_cache instance or NULL. + */ +struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb) +{ + assert(tb); + return tb ? tb->cache : NULL; +} + +/** + * mnt_table_add_fs: + * @tb: tab pointer + * @fs: new entry + * + * Adds a new entry to tab. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) +{ + assert(tb); + assert(fs); + + if (!tb || !fs) + return -EINVAL; + + list_add_tail(&fs->ents, &tb->ents); + + DBG(TAB, mnt_debug_h(tb, "add entry: %s %s", + mnt_fs_get_source(fs), mnt_fs_get_target(fs))); + tb->nents++; + return 0; +} + +/** + * mnt_table_remove_fs: + * @tb: tab pointer + * @fs: new entry + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs) +{ + assert(tb); + assert(fs); + + if (!tb || !fs) + return -EINVAL; + list_del(&fs->ents); + tb->nents--; + return 0; +} + +/** + * mnt_table_get_root_fs: + * @tb: mountinfo file (/proc/self/mountinfo) + * @root: returns pointer to the root filesystem (/) + * + * Returns: 0 on success or -1 case of error. + */ +int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) +{ + struct libmnt_iter itr; + struct libmnt_fs *fs; + int root_id = 0; + + assert(tb); + assert(root); + + if (!tb || !root) + return -EINVAL; + + DBG(TAB, mnt_debug_h(tb, "lookup root fs")); + + 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; + root_id = id; + } + } + + return root_id ? 0 : -EINVAL; +} + +/** + * mnt_table_next_child_fs: + * @tb: mountinfo file (/proc/self/mountinfo) + * @itr: iterator + * @parent: parental FS + * @chld: returns the next child filesystem + * + * Note that filesystems are returned in the order how was mounted (according to + * IDs in /proc/self/mountinfo). + * + * Returns: 0 on success, negative number in case of error or 1 at 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) +{ + struct libmnt_fs *fs; + int parent_id, lastchld_id = 0, chld_id = 0; + + if (!tb || !itr || !parent) + return -EINVAL; + + 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) { + MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents); + lastchld_id = mnt_fs_get_id(fs); + } + + *chld = NULL; + + mnt_reset_iter(itr, MNT_ITER_FORWARD); + while(mnt_table_next_fs(tb, itr, &fs) == 0) { + int id; + + if (mnt_fs_get_parent_id(fs) != parent_id) + continue; + + id = mnt_fs_get_id(fs); + + if ((!lastchld_id || id > lastchld_id) && + (!*chld || id < chld_id)) { + *chld = fs; + chld_id = id; + } + } + + if (!chld_id) + return 1; /* end of iterator */ + + /* set the iterator to the @chld for the next call */ + mnt_table_set_iter(tb, itr, *chld); + + return 0; +} + +/** + * mnt_table_next_fs: + * @tb: tab pointer + * @itr: iterator + * @fs: returns the next tab entry + * + * Returns: 0 on success, negative number in case of error or 1 at end of list. + * + * Example: + * <informalexample> + * <programlisting> + * while(mnt_table_next_fs(tb, itr, &fs) == 0) { + * 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. + */ +int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs) +{ + int rc = 1; + + assert(tb); + assert(itr); + assert(fs); + + if (!tb || !itr || !fs) + return -EINVAL; + *fs = NULL; + + if (!itr->head) + MNT_ITER_INIT(itr, &tb->ents); + if (itr->p != itr->head) { + MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents); + rc = 0; + } + + return rc; +} + +/** + * mnt_table_find_next_fs: + * @tb: table + * @itr: iterator + * @match_func: function returns 1 or 0 + * @userdata: extra data for match_func + * @fs: returns pointer to the next matching table entry + * + * This function allows search in @tb. + * + * Returns: negative number in case of error, 1 at end of table or 0 o success. + */ +int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, + int (*match_func)(struct libmnt_fs *, void *), void *userdata, + struct libmnt_fs **fs) +{ + if (!tb || !itr || !fs || !match_func) + return -EINVAL; + + DBG(TAB, mnt_debug_h(tb, "lookup next fs")); + + if (!itr->head) + MNT_ITER_INIT(itr, &tb->ents); + + do { + if (itr->p != itr->head) + MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents); + else + break; /* end */ + + if (match_func(*fs, userdata)) + return 0; + } while(1); + + *fs = NULL; + return 1; +} + +/** + * mnt_table_set_iter: + * @tb: tab pointer + * @itr: iterator + * @fs: tab entry + * + * Sets @iter to the position of @fs in the file @tb. + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs) +{ + assert(tb); + assert(itr); + assert(fs); + + if (!tb || !itr || !fs) + return -EINVAL; + + MNT_ITER_INIT(itr, &tb->ents); + itr->p = &fs->ents; + + return 0; +} + +/** + * mnt_table_find_target: + * @tb: tab pointer + * @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) + * against realpath(fs->target). The 2nd and 3rd iterations are not performed + * when @tb cache is not set (see mnt_table_set_cache()). + * + * Returns: a tab entry or NULL. + */ +struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction) +{ + struct libmnt_iter itr; + struct libmnt_fs *fs = NULL; + char *cn; + + assert(tb); + assert(path); + + if (!tb || !path) + return NULL; + + DBG(TAB, mnt_debug_h(tb, "lookup TARGET: %s", path)); + + /* native @target */ + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) + if (fs->target && strcmp(fs->target, path) == 0) + return fs; + + if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) + return NULL; + + /* canonicalized paths in struct libmnt_table */ + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + if (fs->target && strcmp(fs->target, cn) == 0) + return fs; + } + + /* non-canonicaled path in struct libmnt_table */ + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + char *p; + + if (!fs->target || !(fs->flags & MNT_FS_SWAP) || + (*fs->target == '/' && *(fs->target + 1) == '\0')) + continue; + + p = mnt_resolve_path(fs->target, tb->cache); + if (strcmp(cn, p) == 0) + return fs; + } + return NULL; +} + +/** + * mnt_table_find_srcpath: + * @tb: tab pointer + * @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). + * + * The 2nd, 3rd and 4th iterations are not performed when @tb cache is not + * set (see mnt_table_set_cache()). + * + * Note that valid source path is NULL; the libmount uses NULL instead of + * "none". The "none" is used in /proc/{mounts,self/mountninfo} for pseudo + * filesystems. + * + * Returns: a tab entry or NULL. + */ +struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction) +{ + struct libmnt_iter itr; + struct libmnt_fs *fs = NULL; + int ntags = 0; + char *cn; + const char *p; + + assert(tb); + + DBG(TAB, mnt_debug_h(tb, "lookup srcpath: %s", path)); + + /* native paths */ + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + const char *src = mnt_fs_get_source(fs); + + p = mnt_fs_get_srcpath(fs); + + if (path == NULL && src == NULL) + return fs; /* source is "none" */ + if (p && strcmp(p, path) == 0) + return fs; + if (!p && src) + ntags++; /* mnt_fs_get_srcpath() returs nothing, it's TAG */ + } + + if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) + return NULL; + + /* canonicalized paths in struct libmnt_table */ + if (ntags < mnt_table_get_nents(tb)) { + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + p = mnt_fs_get_srcpath(fs); + if (p && strcmp(p, cn) == 0) + return fs; + } + } + + /* evaluated tag */ + if (ntags) { + int rc = mnt_cache_read_tags(tb->cache, cn); + + mnt_reset_iter(&itr, direction); + + if (rc == 0) { + /* @path's TAGs are in the cache */ + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + const char *t, *v; + + if (mnt_fs_get_tag(fs, &t, &v)) + continue; + + if (mnt_cache_device_has_tag(tb->cache, cn, t, v)) + return fs; + } + } else if (rc < 0 && errno == EACCES) { + /* @path is unaccessible, try evaluate all TAGs in @tb + * by udev symlinks -- this could be expensive on systems + * with 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)) + continue; + x = mnt_resolve_tag(t, v, tb->cache); + if (x && !strcmp(x, cn)) + return fs; + } + } + } + + /* non-canonicalized paths in struct libmnt_table */ + if (ntags <= mnt_table_get_nents(tb)) { + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + if (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO)) + continue; + p = mnt_fs_get_srcpath(fs); + if (p) + p = mnt_resolve_path(p, tb->cache); + if (p && strcmp(cn, p) == 0) + return fs; + } + } + + return NULL; +} + + +/** + * mnt_table_find_tag: + * @tb: tab pointer + * @tag: tag name (e.g "LABEL", "UUID", ...) + * @val: tag value + * @direction: MNT_ITER_{FORWARD,BACKWARD} + * + * Try to lookup an entry in given tab, first attempt is 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 + * performed when @tb cache is not set (see mnt_table_set_cache()). + + * Returns: a tab entry or NULL. + */ +struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, + const char *val, int direction) +{ + struct libmnt_iter itr; + struct libmnt_fs *fs = NULL; + + assert(tb); + assert(tag); + assert(val); + + if (!tb || !tag || !val) + return NULL; + + DBG(TAB, mnt_debug_h(tb, "lookup by TAG: %s %s", tag, val)); + + /* look up by TAG */ + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + if (fs->tagname && fs->tagval && + strcmp(fs->tagname, tag) == 0 && + strcmp(fs->tagval, val) == 0) + return fs; + } + + if (tb->cache) { + /* look up by device name */ + char *cn = mnt_resolve_tag(tag, val, tb->cache); + if (cn) + return mnt_table_find_srcpath(tb, cn, direction); + } + return NULL; +} + +/** + * mnt_table_find_source: + * @tb: tab pointer + * @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(). + * + * 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; + + assert(tb); + + if (!tb) + return NULL; + + DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s", source)); + + if (source && strchr(source, '=')) { + char *tag, *val; + + if (blkid_parse_tag_string(source, &tag, &val) == 0) { + + fs = mnt_table_find_tag(tb, tag, val, direction); + + free(tag); + free(val); + } + } else + fs = mnt_table_find_srcpath(tb, source, direction); + + return fs; +} + +/** + * mnt_table_find_pair + * @tb: tab pointer + * @source: TAG or path + * @target: mountpoint + * @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 + * others mnt_table_find_* function, because every @tab entry is fully evaluated. + * + * Returns: a tab entry or NULL. + */ +struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source, + const char *target, int direction) +{ + struct libmnt_fs *fs = NULL; + struct libmnt_iter itr; + + assert(tb); + assert(target); + + if (!tb || !target) + return NULL; + + DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s TARGET: %s", source, target)); + + mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + + if (mnt_fs_match_target(fs, target, tb->cache) && + mnt_fs_match_source(fs, source, tb->cache)) + return fs; + } + + return NULL; +} + +/* + * @tb: /proc/self/mountinfo + * @fs: filesystem + * @mountflags: MS_BIND or 0 + * @fsroot: fs-root that will be probably used in the mountinfo file + * for @fs after mount(2) + * + * For btrfs subvolumes this function returns NULL, but @fsroot properly set. + * + * Returns: entry from @tb that will be used as a source for @fs if the @fs is + * bindmount. + */ +struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, + struct libmnt_fs *fs, + unsigned long mountflags, + char **fsroot) +{ + char *root = NULL, *mnt = NULL; + const char *fstype; + struct libmnt_fs *src_fs = NULL; + + assert(fs); + assert(fsroot); + + DBG(TAB, mnt_debug("lookup fs-root for %s", mnt_fs_get_source(fs))); + + fstype = mnt_fs_get_fstype(fs); + + if (tb && (mountflags & MS_BIND)) { + const char *src, *src_root; + + DBG(TAB, mnt_debug("fs-root for bind")); + + src = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache); + if (!src) + goto err; + + mnt = mnt_get_mountpoint(src); + if (!mnt) + goto err; + + root = mnt_get_fs_root(src, mnt); + + src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD); + if (!src_fs) { + DBG(TAB, mnt_debug("not found '%s' in mountinfo -- using default", mnt)); + goto dflt; + } + + /* on btrfs the subvolume is used as fs-root in + * /proc/self/mountinfo, so we have to get the original subvolume + * name from src_fs and prepend the subvolume name to the + * fs-root path + */ + src_root = mnt_fs_get_root(src_fs); + if (src_root && !startswith(root, src_root)) { + size_t sz = strlen(root) + strlen(src_root) + 1; + char *tmp = malloc(sz); + + if (!tmp) + goto err; + snprintf(tmp, sz, "%s%s", src_root, root); + free(root); + root = tmp; + } + } + + /* + * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path + */ + else if (fstype && !strcmp(fstype, "btrfs")) { + char *vol = NULL, *p; + size_t sz, volsz = 0; + + if (mnt_fs_get_option(fs, "subvol", &vol, &volsz)) + goto dflt; + + DBG(TAB, mnt_debug("setting FS root: btrfs subvol")); + + sz = volsz; + if (*vol != '/') + sz++; + root = malloc(sz + 1); + if (!root) + goto err; + p = root; + if (*vol != '/') + *p++ = '/'; + memcpy(p, vol, volsz); + *(root + sz) = '\0'; + } +dflt: + if (!root) { + root = strdup("/"); + if (!root) + goto err; + } + *fsroot = root; + + DBG(TAB, mnt_debug("FS root result: %s", root)); + + free(mnt); + return src_fs; +err: + free(root); + free(mnt); + return NULL; +} + +/** + * mnt_table_is_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. + * + * TODO: check for loopdev (see mount/mount.c is_fstab_entry_mounted(). + * + * Returns: 0 or 1 + */ +int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) +{ + char *root = NULL; + struct libmnt_fs *src_fs; + const char *src, *tgt; + int flags = 0, rc = 0; + + assert(tb); + assert(fstab_fs); + + if (fstab_fs->flags & MNT_FS_SWAP) + return 0; + + if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0) + flags = MS_BIND; + + src_fs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root); + if (src_fs) + src = mnt_fs_get_srcpath(src_fs); + else + src = mnt_resolve_spec(mnt_fs_get_source(fstab_fs), tb->cache); + + tgt = mnt_fs_get_target(fstab_fs); + + if (tgt || src || root) { + struct libmnt_iter itr; + struct libmnt_fs *fs; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + const char *s = mnt_fs_get_srcpath(fs), + *t = mnt_fs_get_target(fs), + *r = mnt_fs_get_root(fs); + + if (s && t && r && !strcmp(t, tgt) && + !strcmp(s, src) && !strcmp(r, root)) + break; + } + if (fs) + rc = 1; /* success */ + } + + free(root); + return rc; +} + +#ifdef TEST_PROGRAM + +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 */ +} + +struct libmnt_table *create_table(const char *file) +{ + struct libmnt_table *tb; + + if (!file) + return NULL; + tb = mnt_new_table(); + if (!tb) + goto err; + + mnt_table_set_parser_errcb(tb, parser_errcb); + + if (mnt_table_parse_file(tb, file) != 0) + goto err; + return tb; +err: + fprintf(stderr, "%s: parsing failed\n", file); + mnt_free_table(tb); + return NULL; +} + +int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_table *tb; + struct libmnt_fs *fs; + int rc = -1; + + tb = create_table(argv[1]); + if (!tb) + return -1; + + fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD); + if (!fs) + goto done; + + printf("ORIGINAL:\n"); + mnt_fs_print_debug(fs, stdout); + + fs = mnt_copy_fs(NULL, fs); + if (!fs) + goto done; + + printf("COPY:\n"); + mnt_fs_print_debug(fs, stdout); + mnt_free_fs(fs); + rc = 0; +done: + mnt_free_table(tb); + return rc; +} + +int test_parse(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_table *tb = NULL; + struct libmnt_iter *itr = NULL; + struct libmnt_fs *fs; + int rc = -1; + + tb = create_table(argv[1]); + if (!tb) + return -1; + + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!itr) + goto done; + + while(mnt_table_next_fs(tb, itr, &fs) == 0) + mnt_fs_print_debug(fs, stdout); + rc = 0; +done: + mnt_free_iter(itr); + mnt_free_table(tb); + return rc; +} + +int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) +{ + struct libmnt_table *tb; + struct libmnt_fs *fs = NULL; + struct libmnt_cache *mpc = NULL; + const char *file, *find, *what; + int rc = -1; + + if (argc != 4) { + fprintf(stderr, "try --help\n"); + return -EINVAL; + } + + file = argv[1], find = argv[2], what = argv[3]; + + tb = create_table(file); + if (!tb) + goto done; + + /* create a cache for canonicalized paths */ + mpc = mnt_new_cache(); + if (!mpc) + goto done; + mnt_table_set_cache(tb, mpc); + + if (strcasecmp(find, "source") == 0) + fs = mnt_table_find_source(tb, what, dr); + else if (strcasecmp(find, "target") == 0) + fs = mnt_table_find_target(tb, what, dr); + + if (!fs) + fprintf(stderr, "%s: not found %s '%s'\n", file, find, what); + else { + mnt_fs_print_debug(fs, stdout); + rc = 0; + } +done: + mnt_free_table(tb); + mnt_free_cache(mpc); + return rc; +} + +int test_find_bw(struct libmnt_test *ts, int argc, char *argv[]) +{ + return test_find(ts, argc, argv, MNT_ITER_BACKWARD); +} + +int test_find_fw(struct libmnt_test *ts, int argc, char *argv[]) +{ + return test_find(ts, argc, argv, MNT_ITER_FORWARD); +} + +int test_find_pair(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_table *tb; + struct libmnt_fs *fs; + int rc = -1; + + tb = create_table(argv[1]); + if (!tb) + return -1; + + fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD); + if (!fs) + goto done; + + mnt_fs_print_debug(fs, stdout); + rc = 0; +done: + mnt_free_table(tb); + return rc; +} + +static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_table *tb = NULL, *fstab = NULL; + struct libmnt_fs *fs; + struct libmnt_iter *itr = NULL; + int rc; + + tb = mnt_new_table_from_file("/proc/self/mountinfo"); + if (!tb) { + fprintf(stderr, "failed to parse mountinfo\n"); + return -1; + } + + fstab = create_table(argv[1]); + if (!fstab) + goto done; + + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!itr) + goto done; + + while(mnt_table_next_fs(fstab, itr, &fs) == 0) { + if (mnt_table_is_fs_mounted(tb, fs)) + printf("%s already mounted on %s\n", + mnt_fs_get_source(fs), + mnt_fs_get_target(fs)); + else + printf("%s not mounted on %s\n", + mnt_fs_get_source(fs), + mnt_fs_get_target(fs)); + } + + rc = 0; +done: + mnt_free_table(tb); + mnt_free_table(fstab); + mnt_free_iter(itr); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--parse", test_parse, "<file> 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>" }, + { "--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" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/libmount/src/tab_diff.c b/libmount/src/tab_diff.c new file mode 100644 index 000000000..d1a17bcb9 --- /dev/null +++ b/libmount/src/tab_diff.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2011 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: tabdiff + * @title: monitor mountinfo file + * @short_description: monitor changes in the list of the mounted filesystems + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mountP.h" + +struct tabdiff_entry { + int oper; /* MNT_TABDIFF_* flags; */ + + struct libmnt_fs *old_fs; /* pointer to the old FS */ + struct libmnt_fs *new_fs; /* pointer to the new FS */ + + struct list_head changes; +}; + +struct libmnt_tabdiff { + int nchanges; /* number of changes */ + + struct list_head changes; /* list with modified entries */ + struct list_head unused; /* list with unuused entries */ +}; + +/** + * mnt_new_tabdiff: + * + * Allocates a new table diff struct. + * + * Returns: new diff handler or NULL. + */ +struct libmnt_tabdiff *mnt_new_tabdiff(void) +{ + struct libmnt_tabdiff *df = calloc(1, sizeof(*df)); + + if (!df) + return NULL; + + DBG(DIFF, mnt_debug_h(df, "alloc")); + + INIT_LIST_HEAD(&df->changes); + INIT_LIST_HEAD(&df->unused); + return df; +} + +static void free_tabdiff_entry(struct tabdiff_entry *de) +{ + if (!de) + return; + list_del(&de->changes); + free(de); +} + +/** + * mnt_free_tabdiff: + * @df: tab diff + * + * Deallocates tab diff struct and all entries. + */ +void mnt_free_tabdiff(struct libmnt_tabdiff *df) +{ + if (!df) + return; + + DBG(DIFF, mnt_debug_h(df, "free")); + + while (!list_empty(&df->changes)) { + struct tabdiff_entry *de = list_entry(df->changes.next, + struct tabdiff_entry, changes); + free_tabdiff_entry(de); + } + + free(df); +} + +/** + * mnt_tabdiff_next_change: + * @df: tabdiff pointer + * @itr: iterator + * @old_fs: returns the old entry or NULL if new entry added + * @new_fs: returns the new entry or NULL if old entry removed + * @oper: MNT_TABDIFF_{MOVE,UMOUNT,REMOUNT,MOUNT} flags + * + * 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. + */ +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) +{ + int rc = 1; + struct tabdiff_entry *de = NULL; + + assert(df); + assert(df); + + if (!df || !itr) + return -EINVAL; + + if (!itr->head) + MNT_ITER_INIT(itr, &df->changes); + if (itr->p != itr->head) { + MNT_ITER_ITERATE(itr, de, struct tabdiff_entry, changes); + rc = 0; + } + + if (old_fs) + *old_fs = de ? de->old_fs : NULL; + if (new_fs) + *new_fs = de ? de->new_fs : NULL; + if (oper) + *oper = de ? de->oper : 0; + + return rc; +} + +static int tabdiff_reset(struct libmnt_tabdiff *df) +{ + assert(df); + + DBG(DIFF, mnt_debug_h(df, "reseting")); + + /* zeroize all entries and move them to the list of unuused + */ + while (!list_empty(&df->changes)) { + struct tabdiff_entry *de = list_entry(df->changes.next, + struct tabdiff_entry, changes); + + list_del(&de->changes); + list_add_tail(&de->changes, &df->unused); + + de->new_fs = de->old_fs = NULL; + de->oper = 0; + } + + df->nchanges = 0; + return 0; +} + +static int tabdiff_add_entry(struct libmnt_tabdiff *df, struct libmnt_fs *old, + struct libmnt_fs *new, int oper) +{ + struct tabdiff_entry *de; + + assert(df); + + DBG(DIFF, mnt_debug_h(df, "add change on %s", + mnt_fs_get_target(new ? new : old))); + + if (!list_empty(&df->unused)) { + de = list_entry(df->unused.next, struct tabdiff_entry, changes); + list_del(&de->changes); + } else { + de = calloc(1, sizeof(*de)); + if (!de) + return -ENOMEM; + } + + INIT_LIST_HEAD(&de->changes); + + de->old_fs = old; + de->new_fs = new; + de->oper = oper; + + list_add_tail(&de->changes, &df->changes); + df->nchanges++; + return 0; +} + +static struct tabdiff_entry *tabdiff_get_mount(struct libmnt_tabdiff *df, + const char *src, + int id) +{ + struct list_head *p; + + assert(df); + + list_for_each(p, &df->changes) { + struct tabdiff_entry *de; + + de = list_entry(p, struct tabdiff_entry, changes); + + if (de->oper == MNT_TABDIFF_MOUNT && de->new_fs && + mnt_fs_get_id(de->new_fs) == id) { + + const char *s = mnt_fs_get_source(de->new_fs); + + if (s == NULL && src == NULL) + return de; + if (s && src && strcmp(s, src) == 0) + return de; + } + } + return NULL; +} + +/** + * mnt_diff_tables: + * @df: diff handler + * @old: old table + * @new: new table + * + * Compares @old and @new, the result is stored in @df and accessible by + * mnt_tabdiff_next_change(). + * + * Returns: number of changes, negative number in case of error. + */ +int mnt_diff_tables(struct libmnt_tabdiff *df, struct libmnt_table *old, + struct libmnt_table *new) +{ + struct libmnt_fs *fs; + struct libmnt_iter itr; + int no, nn; + + if (!df || !old || !new) + return -EINVAL; + + tabdiff_reset(df); + + no = mnt_table_get_nents(old); + nn = mnt_table_get_nents(new); + + if (!no && !nn) /* both tables are empty */ + return 0; + + DBG(DIFF, mnt_debug_h(df, "analyze new=%p (%d entries), " + "old=%p (%d entries)", + new, nn, old, no)); + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + /* all mounted or umounted */ + if (!no && nn) { + while(mnt_table_next_fs(new, &itr, &fs) == 0) + tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT); + goto done; + + } else if (no && !nn) { + while(mnt_table_next_fs(old, &itr, &fs) == 0) + tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT); + goto done; + } + + /* search newly mounted or modified */ + while(mnt_table_next_fs(new, &itr, &fs) == 0) { + struct libmnt_fs *o_fs; + const char *src = mnt_fs_get_source(fs), + *tgt = mnt_fs_get_target(fs); + + o_fs = mnt_table_find_pair(old, src, tgt, MNT_ITER_FORWARD); + if (!o_fs) + /* 'fs' is not in the old table -- so newly mounted */ + tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT); + else { + /* is modified? */ + const char *o1 = mnt_fs_get_options(o_fs), + *o2 = mnt_fs_get_options(fs); + + if (o1 && o2 && strcmp(o1, o2)) + tabdiff_add_entry(df, o_fs, fs, MNT_TABDIFF_REMOUNT); + } + } + + /* search umounted or moved */ + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while(mnt_table_next_fs(old, &itr, &fs) == 0) { + const char *src = mnt_fs_get_source(fs), + *tgt = mnt_fs_get_target(fs); + + if (!mnt_table_find_pair(new, src, tgt, MNT_ITER_FORWARD)) { + struct tabdiff_entry *de; + + de = tabdiff_get_mount(df, src, mnt_fs_get_id(fs)); + if (de) { + de->oper = MNT_TABDIFF_MOVE; + de->old_fs = fs; + } else + tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT); + } + } +done: + DBG(DIFF, mnt_debug_h(df, "%d changes detected", df->nchanges)); + return df->nchanges; +} + +#ifdef TEST_PROGRAM + +int test_diff(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_table *tb_old = NULL, *tb_new = NULL; + struct libmnt_tabdiff *diff = NULL; + struct libmnt_iter *itr; + struct libmnt_fs *old, *new; + int rc = -1, change; + + tb_old = mnt_new_table_from_file(argv[1]); + tb_new = mnt_new_table_from_file(argv[2]); + diff = mnt_new_tabdiff(); + itr = mnt_new_iter(MNT_ITER_FORWARD); + + if (!tb_old || !tb_new || !diff || !itr) { + warnx("failed to allocate resources"); + goto done; + } + + rc = mnt_diff_tables(diff, tb_old, tb_new); + if (rc < 0) + goto done; + + while(mnt_tabdiff_next_change(diff, itr, &old, &new, &change) == 0) { + + printf("%s on %s: ", mnt_fs_get_source(new ? new : old), + mnt_fs_get_target(new ? new : old)); + + switch(change) { + case MNT_TABDIFF_MOVE: + printf("MOVED to %s\n", mnt_fs_get_target(new)); + break; + case MNT_TABDIFF_UMOUNT: + printf("UMOUNTED\n"); + break; + case MNT_TABDIFF_REMOUNT: + printf("REMOUNTED from '%s' to '%s'\n", + mnt_fs_get_options(old), + mnt_fs_get_options(new)); + break; + case MNT_TABDIFF_MOUNT: + printf("MOUNTED\n"); + break; + default: + printf("unknown change!\n"); + } + } + + rc = 0; +done: + mnt_free_table(tb_old); + mnt_free_table(tb_new); + mnt_free_tabdiff(diff); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--diff", test_diff, "<old> <new> prints change" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c new file mode 100644 index 000000000..7419f3742 --- /dev/null +++ b/libmount/src/tab_parse.c @@ -0,0 +1,745 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> + +#include "nls.h" +#include "at.h" +#include "mangle.h" +#include "mountP.h" +#include "pathnames.h" + +static inline char *skip_spaces(char *s) +{ + assert(s); + + while (*s == ' ' || *s == '\t') + s++; + return s; +} + +static int next_number(char **s, int *num) +{ + char *end = NULL; + + assert(num); + assert(s); + + *s = skip_spaces(*s); + if (!**s) + return -1; + *num = strtol(*s, &end, 10); + if (end == NULL || *s == end) + return -1; + + *s = end; + + /* valid end of number is space or terminator */ + if (*end == ' ' || *end == '\t' || *end == '\0') + return 0; + return -1; +} + +/* + * Parses one line from {fs,m}tab + */ +static int mnt_parse_table_line(struct libmnt_fs *fs, char *s) +{ + int rc, n = 0; + char *src, *fstype, *optstr; + + rc = sscanf(s, "%ms " /* (1) source */ + "%ms " /* (2) target */ + "%ms " /* (3) FS type */ + "%ms " /* (4) options */ + "%n", /* byte count */ + &src, + &fs->target, + &fstype, + &optstr, + &n); + + if (rc == 4) { + unmangle_string(src); + unmangle_string(fs->target); + unmangle_string(fstype); + unmangle_string(optstr); + + rc = __mnt_fs_set_source_ptr(fs, src); + if (!rc) + rc = __mnt_fs_set_fstype_ptr(fs, fstype); + if (!rc) + rc = mnt_fs_set_options(fs, optstr); + free(optstr); + } else { + DBG(TAB, mnt_debug("tab parse error: [sscanf rc=%d]: '%s'", rc, s)); + rc = -EINVAL; + } + + if (rc) + return rc; /* error */ + + fs->passno = fs->freq = 0; + s = skip_spaces(s + n); + if (*s) { + if (next_number(&s, &fs->freq) != 0) { + if (*s) { + DBG(TAB, mnt_debug("tab parse error: [freq]")); + rc = -EINVAL; + } + } else if (next_number(&s, &fs->passno) != 0 && *s) { + DBG(TAB, mnt_debug("tab parse error: [passno]")); + rc = -EINVAL; + } + } + + return rc; +} + +/* + * Parses one line from mountinfo file + */ +static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) +{ + int rc, end = 0; + unsigned int maj, min; + char *fstype, *src, *p; + + rc = sscanf(s, "%u " /* (1) id */ + "%u " /* (2) parent */ + "%u:%u " /* (3) maj:min */ + "%ms " /* (4) mountroot */ + "%ms " /* (5) target */ + "%ms" /* (6) vfs options (fs-independent) */ + "%n", /* number of read bytes */ + + &fs->id, + &fs->parent, + &maj, &min, + &fs->root, + &fs->target, + &fs->vfs_optstr, + &end); + + if (rc >= 7 && end > 0) + s += end; + + /* (7) optional fields, terminated by " - " */ + p = strstr(s, " - "); + if (!p) { + DBG(TAB, mnt_debug("mountinfo parse error: not found separator")); + return -EINVAL; + } + s = p + 3; + + rc += sscanf(s, "%ms " /* (8) FS type */ + "%ms " /* (9) source */ + "%ms", /* (10) fs options (fs specific) */ + + &fstype, + &src, + &fs->fs_optstr); + + if (rc >= 10) { + fs->flags |= MNT_FS_KERNEL; + fs->devno = makedev(maj, min); + + unmangle_string(fs->root); + unmangle_string(fs->target); + unmangle_string(fs->vfs_optstr); + unmangle_string(fstype); + + if (!strcmp(src, "none")) { + free(src); + src = NULL; + } else + unmangle_string(src); + + if (!strcmp(fs->fs_optstr, "none")) { + free(fs->fs_optstr); + fs->fs_optstr = NULL; + } else + unmangle_string(fs->fs_optstr); + + rc = __mnt_fs_set_fstype_ptr(fs, fstype); + if (!rc) + rc = __mnt_fs_set_source_ptr(fs, src); + + /* merge VFS and FS options to the one string */ + fs->optstr = mnt_fs_strdup_options(fs); + if (!fs->optstr) + rc = -ENOMEM; + } else { + DBG(TAB, mnt_debug( + "mountinfo parse error [sscanf rc=%d]: '%s'", rc, s)); + rc = -EINVAL; + } + return rc; +} + +/* + * Parses one line from utab file + */ +static int mnt_parse_utab_line(struct libmnt_fs *fs, const char *s) +{ + const char *p = s; + + assert(fs); + assert(s); + assert(!fs->source); + assert(!fs->target); + + while (p && *p) { + char *end = NULL; + + while (*p == ' ') p++; + if (!*p) + break; + + if (!fs->source && !strncmp(p, "SRC=", 4)) { + char *v = unmangle(p + 4, &end); + if (!v) + goto enomem; + if (strcmp(v, "none")) + __mnt_fs_set_source_ptr(fs, v); + + } else if (!fs->target && !strncmp(p, "TARGET=", 7)) { + fs->target = unmangle(p + 7, &end); + if (!fs->target) + goto enomem; + + } else if (!fs->root && !strncmp(p, "ROOT=", 5)) { + fs->root = unmangle(p + 5, &end); + if (!fs->root) + goto enomem; + + } else if (!fs->bindsrc && !strncmp(p, "BINDSRC=", 8)) { + fs->bindsrc = unmangle(p + 8, &end); + if (!fs->bindsrc) + goto enomem; + + } else if (!fs->user_optstr && !strncmp(p, "OPTS=", 5)) { + fs->user_optstr = unmangle(p + 5, &end); + if (!fs->user_optstr) + goto enomem; + + } else if (!fs->attrs && !strncmp(p, "ATTRS=", 6)) { + fs->attrs = unmangle(p + 6, &end); + if (!fs->attrs) + goto enomem; + + } else { + /* unknown variable */ + while (*p && *p != ' ') p++; + } + if (end) + p = end; + } + + return 0; +enomem: + DBG(TAB, mnt_debug("utab parse error: ENOMEM")); + return -ENOMEM; +} + +/* + * 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 + * format. + * + * mountinfo: "<number> <number> ... " + */ +static int guess_table_format(char *line) +{ + unsigned int a, b; + + if (sscanf(line, "%u %u", &a, &b) == 2) + return MNT_FMT_MOUNTINFO; + return MNT_FMT_FSTAB; +} + +/* + * 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, + const char *filename, int *nlines) +{ + char buf[BUFSIZ]; + char *s; + + assert(tb); + assert(f); + assert(fs); + + /* read the next non-blank non-comment line */ + do { + if (fgets(buf, sizeof(buf), f) == NULL) + return -EINVAL; + ++*nlines; + s = index (buf, '\n'); + if (!s) { + /* Missing final newline? Otherwise extremely */ + /* long line - assume file was corrupted */ + if (feof(f)) { + DBG(TAB, mnt_debug_h(tb, + "%s: no final newline", filename)); + s = index (buf, '\0'); + } else { + DBG(TAB, mnt_debug_h(tb, + "%s:%d: missing newline at line", + filename, *nlines)); + goto err; + } + } + *s = '\0'; + if (--s >= buf && *s == '\r') + *s = '\0'; + s = skip_spaces(buf); + } while (*s == '\0' || *s == '#'); + + if (tb->fmt == MNT_FMT_GUESS) + tb->fmt = guess_table_format(s); + + if (tb->fmt == MNT_FMT_FSTAB) { + if (mnt_parse_table_line(fs, s) != 0) + goto err; + + } else if (tb->fmt == MNT_FMT_MOUNTINFO) { + if (mnt_parse_mountinfo_line(fs, s) != 0) + goto err; + + } else if (tb->fmt == MNT_FMT_UTAB) { + if (mnt_parse_utab_line(fs, s) != 0) + goto err; + } + + + /*DBG(TAB, mnt_fs_print_debug(fs, stderr));*/ + + return 0; +err: + DBG(TAB, mnt_debug_h(tb, "%s:%d: %s parse error", filename, *nlines, + tb->fmt == MNT_FMT_MOUNTINFO ? "mountinfo" : + tb->fmt == MNT_FMT_FSTAB ? "fstab" : "utab")); + + /* by default all errors are recoverable, otherwise behavior depends on + * errcb() function. See mnt_table_set_parser_errcb(). + */ + return tb->errcb ? tb->errcb(tb, filename, *nlines) : 1; +} + +/** + * mnt_table_parse_stream: + * @tb: tab pointer + * @f: file stream + * @filename: filename used for debug and error messages + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename) +{ + int nlines = 0; + int rc = -1; + int flags = 0; + + assert(tb); + assert(f); + assert(filename); + + DBG(TAB, mnt_debug_h(tb, "%s: start parsing (%d entries)", + filename, mnt_table_get_nents(tb))); + + /* necessary for /proc/mounts only, the /proc/self/mountinfo + * parser sets the flag properly + */ + if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0) + flags = MNT_FS_KERNEL; + + while (!feof(f)) { + struct libmnt_fs *fs = mnt_new_fs(); + + if (!fs) + goto err; + + rc = mnt_table_parse_next(tb, f, fs, filename, &nlines); + if (!rc) { + rc = mnt_table_add_fs(tb, fs); + fs->flags |= flags; + } + if (rc) { + mnt_free_fs(fs); + if (rc == 1) + continue; /* recoverable error */ + if (feof(f)) + break; + goto err; /* fatal error */ + } + } + + DBG(TAB, mnt_debug_h(tb, "%s: stop parsing (%d entries)", + filename, mnt_table_get_nents(tb))); + return 0; +err: + DBG(TAB, mnt_debug_h(tb, "%s: parse error (rc=%d)", filename, rc)); + return rc; +} + +/** + * mnt_table_parse_file: + * @tb: tab pointer + * @filename: file + * + * Parses 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()). + * + * Returns: 0 on success, negative number in case of error. + */ +int mnt_table_parse_file(struct libmnt_table *tb, const char *filename) +{ + FILE *f; + int rc; + + assert(tb); + assert(filename); + + if (!filename || !tb) + return -EINVAL; + + f = fopen(filename, "r"); + if (f) { + rc = mnt_table_parse_stream(tb, f, filename); + fclose(f); + } else + return -errno; + + return rc; +} + +static int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) +{ + int n = 0, i; + DIR *dir = NULL; + struct dirent **namelist = NULL; + + /* TODO: it would be nice to have a scandir() implementation that + * is able to use already opened directory */ + n = scandir(dirname, &namelist, NULL, versionsort); + if (n <= 0) + return 0; + + /* let use "at" functions rather than play crazy games with paths... */ + dir = opendir(dirname); + if (!dir) + return -errno; + + for (i = 0; i < n; i++) { + struct dirent *d = namelist[i]; + struct stat st; + size_t namesz; + FILE *f; + +#ifdef _DIRENT_HAVE_D_TYPE + if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG && + d->d_type != DT_LNK) + continue; +#endif + if (*d->d_name == '.') + continue; + +#define MNT_MNTTABDIR_EXTSIZ (sizeof(MNT_MNTTABDIR_EXT) - 1) + + namesz = strlen(d->d_name); + if (!namesz || namesz < MNT_MNTTABDIR_EXTSIZ + 1 || + strcmp(d->d_name + (namesz - MNT_MNTTABDIR_EXTSIZ), + MNT_MNTTABDIR_EXT)) + continue; + + if (fstat_at(dirfd(dir), _PATH_MNTTAB_DIR, d->d_name, &st, 0) || + !S_ISREG(st.st_mode)) + continue; + + f = fopen_at(dirfd(dir), _PATH_MNTTAB_DIR, + d->d_name, O_RDONLY, "r"); + if (f) { + mnt_table_parse_stream(tb, f, d->d_name); + fclose(f); + } + } + + for (i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + if (dir) + closedir(dir); + return 0; +} + +struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt) +{ + struct libmnt_table *tb; + struct stat st; + + assert(filename); + + if (!filename) + return NULL; + if (stat(filename, &st)) + return NULL; + tb = mnt_new_table(); + if (tb) { + tb->fmt = fmt; + if (mnt_table_parse_file(tb, filename) != 0) { + mnt_free_table(tb); + tb = NULL; + } + } + return tb; +} + +/** + * mnt_new_table_from_file: + * @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 + * cannot provide any feedback to end-users about broken records in files (e.g. + * fstab). + * + * Returns: newly allocated tab on success and NULL in case of error. + */ +struct libmnt_table *mnt_new_table_from_file(const char *filename) +{ + return __mnt_new_table_from_file(filename, MNT_FMT_GUESS); +} + +/** + * mnt_new_table_from_dir + * @dirname: for example /etc/fstab.d + * + * Returns: newly allocated tab on success and NULL in case of error. + */ +struct libmnt_table *mnt_new_table_from_dir(const char *dirname) +{ + struct libmnt_table *tb; + + assert(dirname); + + if (!dirname) + return NULL; + tb = mnt_new_table(); + if (tb && mnt_table_parse_dir(tb, dirname) != 0) { + mnt_free_table(tb); + tb = NULL; + } + return tb; +} + +/** + * mnt_table_set_parser_errcb: + * @tb: pointer to table + * @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 + * 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). + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_set_parser_errcb(struct libmnt_table *tb, + int (*cb)(struct libmnt_table *tb, const char *filename, int line)) +{ + assert(tb); + tb->errcb = cb; + return 0; +} + +/** + * mnt_table_parse_fstab: + * @tb: table + * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL + * + * This function parses /etc/fstab or /etc/fstab.d and appends new lines to the + * @tab. If the system contains classic fstab file and also fstab.d directory + * then the fstab file is parsed before the fstab.d directory. + * + * The fstab.d directory: + * - files are sorted by strverscmp(3) + * - files that starts with "." are ignored (e.g. ".10foo.fstab") + * - files without the ".fstab" extension are ignored + * + * See also mnt_table_set_parser_errcb(). + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename) +{ + FILE *f; + + assert(tb); + + if (!tb) + return -EINVAL; + if (!filename) + filename = mnt_get_fstab_path(); + + tb->fmt = MNT_FMT_FSTAB; + + f = fopen(filename, "r"); + if (f) { + int rc = mnt_table_parse_stream(tb, f, filename); + fclose(f); + + if (rc) + return rc; + + if (strcmp(filename, _PATH_MNTTAB)) + /* /etc/fstab.d sould be used together with /etc/fstab only */ + return 0; + } + + if (!access(_PATH_MNTTAB_DIR, R_OK)) + return mnt_table_parse_dir(tb, _PATH_MNTTAB_DIR); + return 0; +} + +/* + * This function uses @uf to found 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 + * VFS options (note that FS options are ignored). + * + * Returns modified filesystem (from @tb) or NULL. + */ +static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf) +{ + struct libmnt_fs *fs; + struct libmnt_iter itr; + const char *optstr, *src, *target, *root, *attrs; + + assert(tb); + assert(uf); + if (!tb || !uf) + return NULL; + + DBG(TAB, mnt_debug_h(tb, "merging user fs")); + + src = mnt_fs_get_srcpath(uf); + target = mnt_fs_get_target(uf); + optstr = mnt_fs_get_user_options(uf); + attrs = mnt_fs_get_attributes(uf); + root = mnt_fs_get_root(uf); + + if (!src || !target || !root || (!attrs && !optstr)) + return NULL; + + mnt_reset_iter(&itr, MNT_ITER_BACKWARD); + + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + const char *s = mnt_fs_get_srcpath(fs), + *t = mnt_fs_get_target(fs), + *r = mnt_fs_get_root(fs); + + if (fs->flags & MNT_FS_MERGED) + continue; + + if (s && t && r && !strcmp(t, target) && + !strcmp(s, src) && !strcmp(r, root)) + break; + } + + if (fs) { + DBG(TAB, mnt_debug_h(tb, "found fs -- appending user optstr")); + mnt_fs_append_options(fs, optstr); + mnt_fs_append_attributes(fs, attrs); + mnt_fs_set_bindsrc(fs, mnt_fs_get_bindsrc(uf)); + fs->flags |= MNT_FS_MERGED; + + DBG(TAB, mnt_debug_h(tb, "found fs:")); + DBG(TAB, mnt_fs_print_debug(fs, stderr)); + } + return fs; +} + +/** + * mnt_table_parse_mtab: + * @tb: table + * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL + * + * This function parses /etc/mtab or /proc/self/mountinfo + + * /run/mount/utabs or /proc/mounts. + * + * See also mnt_table_set_parser_errcb(). + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename) +{ + int rc; + const char *utab = NULL; + + if (mnt_has_regular_mtab(&filename, NULL)) { + + DBG(TAB, mnt_debug_h(tb, "force %s usage", filename)); + + rc = mnt_table_parse_file(tb, filename); + if (!rc) + return 0; + filename = NULL; /* failed */ + } + + /* + * useless /etc/mtab + * -- read kernel information from /proc/self/mountinfo + */ + tb->fmt = MNT_FMT_MOUNTINFO; + rc = mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO); + if (rc) { + /* hmm, old kernel? ...try /proc/mounts */ + tb->fmt = MNT_FMT_MTAB; + return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS); + } + + /* + * try to read user specific information from /run/mount/utabs + */ + utab = mnt_get_utab_path(); + if (utab) { + struct libmnt_table *u_tb = __mnt_new_table_from_file(utab, MNT_FMT_UTAB); + + if (u_tb) { + struct libmnt_fs *u_fs; + struct libmnt_iter itr; + + mnt_reset_iter(&itr, MNT_ITER_BACKWARD); + + /* merge user options into mountinfo from 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); + } + } + return 0; +} diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c new file mode 100644 index 000000000..88125bfbb --- /dev/null +++ b/libmount/src/tab_update.c @@ -0,0 +1,860 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: update + * @title: mtab managment + * @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 file. + * + * It's recommended to use high-level struct libmnt_context API. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <signal.h> + +#include "c.h" +#include "mountP.h" +#include "mangle.h" +#include "pathnames.h" + +struct libmnt_update { + char *target; + struct libmnt_fs *fs; + char *filename; + unsigned long mountflags; + int userspace_only; + int ready; + + struct libmnt_table *mountinfo; +}; + +static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags); +static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags); + +/** + * mnt_new_update: + * + * Returns: newly allocated update handler + */ +struct libmnt_update *mnt_new_update(void) +{ + struct libmnt_update *upd; + + upd = calloc(1, sizeof(*upd)); + if (!upd) + return NULL; + + DBG(UPDATE, mnt_debug_h(upd, "allocate")); + return upd; +} + +/** + * mnt_free_update: + * @upd: update + * + * Deallocates struct libmnt_update handler. + */ +void mnt_free_update(struct libmnt_update *upd) +{ + if (!upd) + return; + + DBG(UPDATE, mnt_debug_h(upd, "free")); + + mnt_free_fs(upd->fs); + mnt_free_table(upd->mountinfo); + free(upd->target); + free(upd->filename); + free(upd); +} + +/* + * Returns 0 on success, <0 in case of error. + */ +int mnt_update_set_filename(struct libmnt_update *upd, const char *filename, + int userspace_only) +{ + const char *path = NULL; + int rw = 0; + + assert(upd); + + /* filename explicitly defined */ + if (filename) { + char *p = strdup(filename); + if (!p) + return -ENOMEM; + + upd->userspace_only = userspace_only; + free(upd->filename); + upd->filename = p; + } + + if (upd->filename) + return 0; + + /* detect tab filename -- /etc/mtab or /run/mount/utab + */ + mnt_has_regular_mtab(&path, &rw); + if (!rw) { + path = NULL; + mnt_has_regular_utab(&path, &rw); + if (!rw) + return -EACCES; + upd->userspace_only = TRUE; + } + upd->filename = strdup(path); + if (!upd->filename) + return -ENOMEM; + + return 0; +} + +/** + * mnt_update_get_filename: + * @upd: update + * + * This function returns file name (e.g. /etc/mtab) for the up-dated file. + * + * Returns: pointer to filename that will be updated or NULL in case of error. + */ +const char *mnt_update_get_filename(struct libmnt_update *upd) +{ + return upd ? upd->filename : NULL; +} + +/** + * mnt_update_is_ready: + * @upd: update handler + * + * Returns: 1 if entry described by @upd is successfully prepared and will be + * written to mtab/utab file. + */ +int mnt_update_is_ready(struct libmnt_update *upd) +{ + return upd ? upd->ready : FALSE; +} + +/** + * mnt_update_set_fs: + * @upd: update handler + * @mountflags: MS_* flags + * @target: umount target, must be NULL for mount + * @fs: mount filesystem description, must be NULL for umount + * + * Returns: <0 in case on error, 0 on success, 1 if update is unnecessary. + */ +int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags, + const char *target, struct libmnt_fs *fs) +{ + int rc; + + assert(upd); + assert(target || fs); + + if (!upd) + return -EINVAL; + if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs))) + return -EINVAL; + if (target && fs) + return -EINVAL; + + DBG(UPDATE, mnt_debug_h(upd, + "reseting FS [fs=0x%p, target=%s, flags=0x%08lx]", + fs, target, mountflags)); + if (fs) { + DBG(UPDATE, mnt_debug_h(upd, "FS template:")); + DBG(UPDATE, mnt_fs_print_debug(fs, stderr)); + } + + mnt_free_fs(upd->fs); + free(upd->target); + upd->ready = FALSE; + upd->fs = NULL; + upd->target = NULL; + upd->mountflags = 0; + + if (mountflags & MS_PROPAGATION) + return 1; + + upd->mountflags = mountflags; + + rc = mnt_update_set_filename(upd, NULL, 0); + if (rc) { + DBG(UPDATE, mnt_debug_h(upd, "no writable file available [rc=%d]", rc)); + return rc; /* error or no file available (rc = 1) */ + } + if (target) { + upd->target = strdup(target); + if (!upd->target) + return -ENOMEM; + + } else if (fs) { + if (upd->userspace_only && !(mountflags & MS_MOVE)) { + int rc = utab_new_entry(upd, fs, mountflags); + if (rc) + return rc; + } else { + upd->fs = mnt_copy_mtab_fs(fs); + if (!upd->fs) + return -ENOMEM; + + } + } + + + DBG(UPDATE, mnt_debug_h(upd, "ready")); + upd->ready = TRUE; + return 0; +} + +/** + * mnt_update_get_fs: + * @upd: update + * + * Returns: update filesystem entry or NULL + */ +struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd) +{ + return upd ? upd->fs : NULL; +} + +/** + * mnt_update_get_mflags: + * @upd: update + * + * Returns: mount flags as was set by mnt_update_set_fs() + */ +unsigned long mnt_update_get_mflags(struct libmnt_update *upd) +{ + return upd ? upd->mountflags : 0; +} + +/** + * mnt_update_force_rdonly: + * @upd: update + * @rdonly: is read-only? + * + * Returns: 0 on success and negative number in case of error. + */ +int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly) +{ + int rc = 0; + + if (!upd || !upd->fs) + return -EINVAL; + + if (rdonly && (upd->mountflags & MS_RDONLY)) + return 0; + if (!rdonly && !(upd->mountflags & MS_RDONLY)) + return 0; + + if (!upd->userspace_only) { + /* /etc/mtab -- we care about VFS options there */ + const char *o = mnt_fs_get_options(upd->fs); + char *n = o ? strdup(o) : NULL; + + if (n) + mnt_optstr_remove_option(&n, rdonly ? "rw" : "ro"); + if (!mnt_optstr_prepend_option(&n, rdonly ? "ro" : "rw", NULL)) + rc = mnt_fs_set_options(upd->fs, n); + + free(n); + } + + if (rdonly) + upd->mountflags &= ~MS_RDONLY; + else + upd->mountflags |= MS_RDONLY; + + return rc; +} + +/* + * Allocates 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 + * unnecessary. + */ +static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, + unsigned long mountflags) +{ + int rc = 0; + const char *o = NULL, *a = NULL; + char *u = NULL; + + assert(fs); + assert(upd); + assert(upd->fs == NULL); + assert(!(mountflags & MS_MOVE)); + + DBG(UPDATE, mnt_debug("prepare utab entry")); + + o = mnt_fs_get_user_options(fs); + a = mnt_fs_get_attributes(fs); + upd->fs = NULL; + + if (o) { + /* remove non-mtab options */ + rc = mnt_optstr_get_options(o, &u, + mnt_get_builtin_optmap(MNT_USERSPACE_MAP), + MNT_NOMTAB); + if (rc) + goto err; + } + + if (!u && !a) { + DBG(UPDATE, mnt_debug("utab entry unnecessary (no options)")); + return 1; + } + + /* allocate the entry */ + upd->fs = mnt_copy_fs(NULL, fs); + if (!upd->fs) { + rc = -ENOMEM; + goto err; + } + + rc = mnt_fs_set_options(upd->fs, u); + if (rc) + goto err; + rc = mnt_fs_set_attributes(upd->fs, a); + if (rc) + goto err; + + if (!(mountflags & MS_REMOUNT)) { + rc = set_fs_root(upd, fs, mountflags); + if (rc) + goto err; + } + + free(u); + DBG(UPDATE, mnt_debug("utab entry OK")); + return 0; +err: + free(u); + mnt_free_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 + * filesystem from /proc/self/mountinfo. + */ +static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, + unsigned long mountflags) +{ + struct libmnt_fs *src_fs; + char *fsroot = NULL; + const char *src; + int rc = 0; + + DBG(UPDATE, mnt_debug("setting FS root")); + + assert(upd); + assert(upd->fs); + assert(fs); + + if (mountflags & MS_BIND) { + if (!upd->mountinfo) + upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); + + src = mnt_fs_get_srcpath(fs); + if (src) { + rc = mnt_fs_set_bindsrc(upd->fs, src); + if (rc) + goto err; + } + } + + src_fs = mnt_table_get_fs_root(upd->mountinfo, fs, + mountflags, &fsroot); + if (src_fs) { + src = mnt_fs_get_srcpath(src_fs); + rc = mnt_fs_set_source(upd->fs, src); + if (rc) + goto err; + + mnt_fs_set_fstype(upd->fs, mnt_fs_get_fstype(src_fs)); + } + + upd->fs->root = fsroot; + return 0; +err: + free(fsroot); + return rc; +} + +/* mtab and fstab update -- returns zero on success + */ +static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs) +{ + char *o; + char *m1, *m2, *m3, *m4; + int rc; + + assert(fs); + assert(f); + + o = mnt_fs_strdup_options(fs); + if (!o) + return -ENOMEM; + + m1 = mangle(mnt_fs_get_source(fs)); + m2 = mangle(mnt_fs_get_target(fs)); + m3 = mangle(mnt_fs_get_fstype(fs)); + m4 = mangle(o); + + if (m1 && m2 && m3 && m4) { + rc = fprintf(f, "%s %s %s %s %d %d\n", + m1, m2, m3, m4, + mnt_fs_get_freq(fs), + mnt_fs_get_passno(fs)); + if (rc > 0) + rc = 0; + } else + rc = -ENOMEM; + + free(o); + free(m1); + free(m2); + free(m3); + free(m4); + + return rc; +} + +static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs) +{ + char *p; + int rc = 0; + + assert(fs); + assert(f); + + if (!fs || !f) + return -EINVAL; + + p = mangle(mnt_fs_get_source(fs)); + if (p) { + rc = fprintf(f, "SRC=%s ", p); + free(p); + } + if (rc >= 0) { + p = mangle(mnt_fs_get_target(fs)); + if (p) { + rc = fprintf(f, "TARGET=%s ", p); + free(p); + } + } + if (rc >= 0) { + p = mangle(mnt_fs_get_root(fs)); + if (p) { + rc = fprintf(f, "ROOT=%s ", p); + free(p); + } + } + if (rc >= 0) { + p = mangle(mnt_fs_get_bindsrc(fs)); + if (p) { + rc = fprintf(f, "BINDSRC=%s ", p); + free(p); + } + } + if (rc >= 0) { + p = mangle(mnt_fs_get_attributes(fs)); + if (p) { + rc = fprintf(f, "ATTRS=%s ", p); + free(p); + } + } + if (rc >= 0) { + p = mangle(mnt_fs_get_user_options(fs)); + if (p) { + rc = fprintf(f, "OPTS=%s", p); + free(p); + } + } + if (rc >= 0) + rc = fprintf(f, "\n"); + + if (rc > 0) + rc = 0; /* success */ + return rc; +} + +static int update_table(struct libmnt_update *upd, struct libmnt_table *tb) +{ + FILE *f; + int rc, fd; + char *uq = NULL; + + if (!tb || !upd->filename) + return -EINVAL; + + DBG(UPDATE, mnt_debug_h(upd, "%s: updating", upd->filename)); + + fd = mnt_open_uniq_filename(upd->filename, &uq); + if (fd < 0) + return fd; /* error */ + + f = fdopen(fd, "w"); + if (f) { + struct stat st; + struct libmnt_iter itr; + struct libmnt_fs *fs; + int fd; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + if (upd->userspace_only) + rc = fprintf_utab_fs(f, fs); + else + rc = fprintf_mtab_fs(f, fs); + if (rc) { + DBG(UPDATE, mnt_debug_h(upd, + "%s: write entry failed: %m", uq)); + goto leave; + } + } + + if (fflush(f) != 0) { + rc = -errno; + DBG(UPDATE, mnt_debug_h(upd, "%s: fflush failed: %m", uq)); + goto leave; + } + + fd = fileno(f); + rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0; + + if (!rc && stat(upd->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); + rc = rename(uq, upd->filename) ? -errno : 0; + } else { + rc = -errno; + close(fd); + } + +leave: + unlink(uq); /* be paranoid */ + free(uq); + return rc; +} + +static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb; + int rc = 0; + + assert(upd); + assert(upd->fs); + + DBG(UPDATE, mnt_debug_h(upd, "%s: add entry", upd->filename)); + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return rc; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); + if (tb) { + struct libmnt_fs *fs = mnt_copy_fs(NULL, upd->fs); + if (!fs) + rc = -ENOMEM; + else { + mnt_table_add_fs(tb, fs); + rc = update_table(upd, tb); + } + } + + if (lc) + mnt_unlock_file(lc); + + mnt_free_table(tb); + return rc; +} + +static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb; + int rc = 0; + + assert(upd); + assert(upd->target); + + DBG(UPDATE, mnt_debug_h(upd, "%s: remove entry", upd->filename)); + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return rc; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); + if (tb) { + struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD); + 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); + return rc; +} + +static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb = NULL; + int rc = 0; + + DBG(UPDATE, mnt_debug_h(upd, "%s: modify target", upd->filename)); + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return rc; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); + if (tb) { + struct libmnt_fs *cur = mnt_table_find_target(tb, + mnt_fs_get_srcpath(upd->fs), MNT_ITER_BACKWARD); + if (cur) { + rc = mnt_fs_set_target(cur, mnt_fs_get_target(upd->fs)); + if (!rc) + rc = update_table(upd, tb); + } + } + + if (lc) + mnt_unlock_file(lc); + + mnt_free_table(tb); + return rc; +} + +static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb = NULL; + int rc = 0; + struct libmnt_fs *fs; + + assert(upd); + assert(upd->fs); + + DBG(UPDATE, mnt_debug_h(upd, "%s: modify options", upd->filename)); + + fs = upd->fs; + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return rc; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); + if (tb) { + struct libmnt_fs *cur = mnt_table_find_target(tb, + mnt_fs_get_target(fs), + MNT_ITER_BACKWARD); + if (cur) { + if (upd->userspace_only) + rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs)); + if (!rc) + rc = mnt_fs_set_options(cur, mnt_fs_get_options(fs)); + if (!rc) + rc = update_table(upd, tb); + } + } + + if (lc) + mnt_unlock_file(lc); + + mnt_free_table(tb); + return rc; +} + +/** + * mnt_update_table: + * @upd: update + * @lc: lock or NULL + * + * 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. + * + * See also mnt_lock_block_signals() and mnt_context_get_lock(). + * + * Returns: 0 on success, negative number on error. + */ +int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_lock *lc0 = lc; + int rc = -EINVAL; + + assert(upd); + + if (!upd->filename || !upd) + return -EINVAL; + if (!upd->ready) + return 0; + + DBG(UPDATE, mnt_debug_h(upd, "%s: update tab", upd->filename)); + if (upd->fs) { + DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr)); + } + if (!lc) { + lc = mnt_new_lock(upd->filename, 0); + if (lc) + mnt_lock_block_signals(lc, TRUE); + } + if (lc && upd->userspace_only) + mnt_lock_use_simplelock(lc, TRUE); /* use flock */ + + if (!upd->fs && upd->target) + rc = update_remove_entry(upd, lc); /* umount */ + else if (upd->mountflags & MS_MOVE) + rc = update_modify_target(upd, lc); /* move */ + else if (upd->mountflags & MS_REMOUNT) + rc = update_modify_options(upd, lc); /* remount */ + else if (upd->fs) + rc = update_add_entry(upd, lc); /* mount */ + + upd->ready = FALSE; + DBG(UPDATE, mnt_debug_h(upd, "%s: update tab: done [rc=%d]", + upd->filename, rc)); + if (lc != lc0) + mnt_free_lock(lc); + return rc; +} + +#ifdef TEST_PROGRAM + +static int update(const char *target, struct libmnt_fs *fs, unsigned long mountflags) +{ + int rc; + struct libmnt_update *upd; + + DBG(UPDATE, mnt_debug("update test")); + + upd = mnt_new_update(); + if (!upd) + return -ENOMEM; + + rc = mnt_update_set_fs(upd, mountflags, target, fs); + if (rc == 1) { + /* update is unnecessary */ + rc = 0; + goto done; + } + if (rc) { + fprintf(stderr, "failed to set FS\n"); + goto done; + } + + /* [... here should be mount(2) call ...] */ + + rc = mnt_update_table(upd, NULL); +done: + return rc; +} + +static int test_add(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_fs *fs = mnt_new_fs(); + int rc; + + if (argc < 5 || !fs) + return -1; + mnt_fs_set_source(fs, argv[1]); + mnt_fs_set_target(fs, argv[2]); + mnt_fs_set_fstype(fs, argv[3]); + mnt_fs_set_options(fs, argv[4]); + + rc = update(NULL, fs, 0); + mnt_free_fs(fs); + return rc; +} + +static int test_remove(struct libmnt_test *ts, int argc, char *argv[]) +{ + if (argc < 2) + return -1; + return update(argv[1], NULL, 0); +} + +static int test_move(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_fs *fs = mnt_new_fs(); + int rc; + + if (argc < 3) + return -1; + mnt_fs_set_source(fs, argv[1]); + mnt_fs_set_target(fs, argv[2]); + + rc = update(NULL, fs, MS_MOVE); + + mnt_free_fs(fs); + return rc; +} + +static int test_remount(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_fs *fs = mnt_new_fs(); + int rc; + + if (argc < 3) + return -1; + mnt_fs_set_target(fs, argv[1]); + mnt_fs_set_options(fs, argv[2]); + + rc = update(NULL, fs, MS_REMOUNT); + mnt_free_fs(fs); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--add", test_add, "<src> <target> <type> <options> add 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" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/libmount/src/test.c b/libmount/src/test.c new file mode 100644 index 000000000..2da00b40d --- /dev/null +++ b/libmount/src/test.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Routines for TEST_PROGRAMs + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifndef TEST_PROGRAM +#define TEST_PROGRAM +#endif + +#include "mountP.h" + +int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]) +{ + int rc = -1; + struct libmnt_test *ts; + + assert(tests); + assert(argc); + assert(argv); + + if (argc < 2 || + strcmp(argv[1], "--help") == 0 || + strcmp(argv[1], "-h") == 0) + goto usage; + + mnt_init_debug(0); + + for (ts = tests; ts->name; ts++) { + if (strcmp(ts->name, argv[1]) == 0) { + rc = ts->body(ts, argc - 1, argv + 1); + if (rc) + printf("FAILED [rc=%d]", rc); + break; + } + } + + if (rc < 0 && ts->name == NULL) + goto usage; + + return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +usage: + printf("\nUsage:\n\t%s <test> [testoptions]\nTests:\n", + program_invocation_short_name); + for (ts = tests; ts->name; ts++) { + printf("\t%-15s", ts->name); + if (ts->usage) + printf(" %s\n", ts->usage); + } + printf("\n"); + return EXIT_FAILURE; +} diff --git a/libmount/src/utils.c b/libmount/src/utils.c new file mode 100644 index 000000000..8f0e49c9b --- /dev/null +++ b/libmount/src/utils.c @@ -0,0 +1,899 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: utils + * @title: Utils + * @short_description: misc utils. + */ +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <ctype.h> +#include <sys/types.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> + +#include "strutils.h" +#include "pathnames.h" +#include "mountP.h" +#include "mangle.h" +#include "canonicalize.h" +#include "env.h" + +int endswith(const char *s, const char *sx) +{ + ssize_t off; + + assert(s); + assert(sx); + + off = strlen(s); + if (!off) + 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); + + off = strlen(sx); + if (!off) + return 0; + + return !strncmp(s, sx, off); +} + +/* returns basename and keeps dirname in the @path, if @path is "/" (root) + * then returns empty string */ +static char *stripoff_last_component(char *path) +{ + char *p = path ? strrchr(path, '/') : NULL; + + if (!p) + return NULL; + *p = '\0'; + return ++p; +} + +/* Note that the @target has to be absolute path (so at least "/") + */ +int mnt_chdir_to_parent(const char *target, char **filename) +{ + char *path, *last = NULL; + char cwd[PATH_MAX]; + int rc = -EINVAL; + + if (!target || *target != '/') + return -EINVAL; + + path = strdup(target); + if (!path) + return -ENOMEM; + + if (*(path + 1) != '\0') { + last = stripoff_last_component(path); + if (!last) + goto err; + } + if (!*path) + *path = '/'; /* root */ + + if (chdir(path) == -1) { + DBG(UTILS, mnt_debug("failed to chdir to %s: %m", path)); + rc = -errno; + goto err; + } + if (!getcwd(cwd, sizeof(cwd))) { + DBG(UTILS, mnt_debug("failed to obtain current directory: %m")); + rc = -errno; + goto err; + } + if (strcmp(cwd, path) != 0) { + DBG(UTILS, mnt_debug("path moved (%s -> %s)", path, cwd)); + goto err; + } + + DBG(CXT, mnt_debug("current directory moved to %s", path)); + + *filename = path; + + if (!last || !*last) + memcpy(*filename, ".", 2); + else + memcpy(*filename, last, strlen(last) + 1); + return 0; +err: + free(path); + return rc; +} + +/** + * mnt_mangle: + * @str: string + * + * Encode @str to be compatible with fstab/mtab + * + * Returns: new allocated string or NULL in case of error. + */ +char *mnt_mangle(const char *str) +{ + return mangle(str); +} + +/** + * mnt_unmangle: + * @str: string + * + * Decode @str from fstab/mtab + * + * Returns: new allocated string or NULL in case of error. + */ +char *mnt_unmangle(const char *str) +{ + return unmangle(str, NULL); +} + +/** + * mnt_fstype_is_pseudofs: + * @type: filesystem name + * + * Returns: 1 for filesystems like proc, sysfs, ... or 0. + */ +int mnt_fstype_is_pseudofs(const char *type) +{ + if (!type) + return 0; + if (strcmp(type, "none") == 0 || + strcmp(type, "proc") == 0 || + strcmp(type, "tmpfs") == 0 || + strcmp(type, "sysfs") == 0 || + strcmp(type, "devpts") == 0|| + strcmp(type, "cgroup") == 0 || + strcmp(type, "devfs") == 0 || + strcmp(type, "dlmfs") == 0 || + strcmp(type, "cpuset") == 0 || + strcmp(type, "securityfs") == 0 || + strcmp(type, "rpc_pipefs") == 0 || + strcmp(type, "fusectl") == 0 || + strcmp(type, "binfmt_misc") == 0 || + strcmp(type, "fuse.gvfs-fuse-daemon") == 0 || + strcmp(type, "debugfs") == 0 || + strcmp(type, "spufs") == 0) + return 1; + return 0; +} + +/** + * mnt_fstype_is_netfs: + * @type: filesystem name + * + * Returns: 1 for filesystems like cifs, nfs, ... or 0. + */ +int mnt_fstype_is_netfs(const char *type) +{ + if (!type) + return 0; + if (strcmp(type, "cifs") == 0 || + strcmp(type, "smbfs") == 0 || + strncmp(type,"nfs", 3) == 0 || + strcmp(type, "afs") == 0 || + strcmp(type, "ncpfs") == 0 || + strncmp(type,"9p", 2) == 0) + return 1; + return 0; +} + +/** + * mnt_match_fstype: + * @type: filesystem type + * @pattern: filesystem name or comma delimited list of names + * + * The @pattern list of filesystem 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". + * + * "bar" : "nofoo,bar" -> False (global "no" prefix) + * + * "bar" : "foo,bar" -> True + * + * "bar" : "foo,nobar" -> False + * + * Returns: 1 if type is matching, else 0. This function also returns + * 0 if @pattern is NULL and @type is non-NULL. + */ +int mnt_match_fstype(const char *type, const char *pattern) +{ + int no = 0; /* negated types list */ + int len; + const char *p; + + if (!pattern && !type) + return 1; + if (!pattern) + return 0; + + if (!strncmp(pattern, "no", 2)) { + no = 1; + pattern += 2; + } + + /* Does type occur in types, separated by commas? */ + len = strlen(type); + p = pattern; + while(1) { + if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) && + (p[len+2] == 0 || p[len+2] == ',')) + return 0; + if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ',')) + return !no; + p = strchr(p,','); + if (!p) + break; + p++; + } + return no; +} + + +/* Returns 1 if needle found or noneedle not found in haystack + * Otherwise returns 0 + */ +static int check_option(const char *haystack, size_t len, + const char *needle, size_t needle_len) +{ + const char *p; + int no = 0; + + if (needle_len >= 2 && !strncmp(needle, "no", 2)) { + no = 1; + needle += 2; + needle_len -= 2; + } + + for (p = haystack; p && p < haystack + len; p++) { + char *sep = strchr(p, ','); + size_t plen = sep ? sep - p : len - (p - haystack); + + if (plen == needle_len) { + if (!strncmp(p, needle, plen)) + return !no; /* foo or nofoo was found */ + } + p += plen; + } + + return no; /* foo or nofoo was not found */ +} + +/** + * mnt_match_options: + * @optstr: options string + * @pattern: comma delimited list of options + * + * The "no" could 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. + * + * "xxx,yyy,zzz" : "nozzz" -> False + * + * "xxx,yyy,zzz" : "xxx,noeee" -> True + * + * Returns: 1 if pattern is matching, else 0. This function also returns 0 + * if @pattern is NULL and @optstr is non-NULL. + */ +int mnt_match_options(const char *optstr, const char *pattern) +{ + const char *p; + size_t len, optstr_len = 0; + + if (!pattern && !optstr) + return 1; + if (!pattern) + return 0; + + len = strlen(pattern); + if (optstr) + optstr_len = strlen(optstr); + + for (p = pattern; p < pattern + len; p++) { + char *sep = strchr(p, ','); + size_t plen = sep ? sep - p : len - (p - pattern); + + if (!plen) + continue; /* if two ',' appear in a row */ + + if (!check_option(optstr, optstr_len, p, plen)) + return 0; /* any match failure means failure */ + + p += plen; + } + + /* no match failures in list means success */ + return 1; +} + +void mnt_free_filesystems(char **filesystems) +{ + char **p; + + if (!filesystems) + return; + for (p = filesystems; *p; p++) + free(*p); + free(filesystems); +} + +static int add_filesystem(char ***filesystems, char *name) +{ + int n = 0; + + assert(filesystems); + assert(name); + + if (*filesystems) { + char **p; + for (n = 0, p = *filesystems; *p; p++, n++) { + if (strcmp(*p, name) == 0) + return 0; + } + } + + #define MYCHUNK 16 + + if (n == 0 || !((n + 1) % MYCHUNK)) { + size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK; + char **x = realloc(*filesystems, items * sizeof(char *)); + + if (!x) + goto err; + *filesystems = x; + } + name = strdup(name); + if (!name) + goto err; + (*filesystems)[n] = name; + (*filesystems)[n + 1] = NULL; + return 0; +err: + mnt_free_filesystems(*filesystems); + return -ENOMEM; +} + +static int get_filesystems(const char *filename, char ***filesystems, const char *pattern) +{ + FILE *f; + char line[128]; + + f = fopen(filename, "r"); + if (!f) + return 0; + + while (fgets(line, sizeof(line), f)) { + char name[sizeof(line)]; + int rc; + + if (*line == '#' || strncmp(line, "nodev", 5) == 0) + continue; + if (sscanf(line, " %128[^\n ]\n", name) != 1) + continue; + if (pattern && !mnt_match_fstype(name, pattern)) + continue; + rc = add_filesystem(filesystems, name); + if (rc) + return rc; + } + return 0; +} + +int mnt_get_filesystems(char ***filesystems, const char *pattern) +{ + int rc; + + if (!filesystems) + return -EINVAL; + *filesystems = NULL; + + rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern); + if (rc) + return rc; + return get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern); +} + +/* + * Returns allocated string with username or NULL. + */ +char *mnt_get_username(const uid_t uid) +{ + struct passwd pwd; + struct passwd *res; +#ifdef _SC_GETPW_R_SIZE_MAX + size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + size_t sz = 0; +#endif + char *buf, *username = NULL; + + if (sz <= 0) + sz = 16384; /* Should be more than enough */ + + buf = malloc(sz); + if (!buf) + return NULL; + + if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res) + username = strdup(pwd.pw_name); + + free(buf); + return username; +} + +int mnt_get_uid(const char *username, uid_t *uid) +{ + int rc = -1; + struct passwd pwd; + struct passwd *pw; + size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX); + char *buf; + + if (!username || !uid) + return -EINVAL; + if (sz <= 0) + sz = 16384; /* Should be more than enough */ + + buf = malloc(sz); + if (!buf) + return -ENOMEM; + + if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) { + *uid= pw->pw_uid; + rc = 0; + } else { + DBG(UTILS, mnt_debug( + "cannot convert '%s' username to UID", username)); + } + + free(buf); + return rc; +} + +int mnt_get_gid(const char *groupname, gid_t *gid) +{ + int rc = -1; + struct group grp; + struct group *gr; + size_t sz = sysconf(_SC_GETGR_R_SIZE_MAX); + char *buf; + + if (!groupname || !gid) + return -EINVAL; + if (sz <= 0) + sz = 16384; /* Should be more than enough */ + + buf = malloc(sz); + if (!buf) + return -ENOMEM; + + if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) { + *gid= gr->gr_gid; + rc = 0; + } else { + DBG(UTILS, mnt_debug( + "cannot convert '%s' groupname to GID", groupname)); + } + + free(buf); + return rc; +} + +int mnt_in_group(gid_t gid) +{ + int rc = 0, n, i; + gid_t *grps = NULL; + + if (getgid() == gid) + return 1; + + n = getgroups(0, NULL); + if (n <= 0) + goto done; + + grps = malloc(n * sizeof(*grps)); + if (!grps) + goto done; + + if (getgroups(n, grps) == n) { + for (i = 0; i < n; i++) { + if (grps[i] == gid) { + rc = 1; + break; + } + } + } +done: + free(grps); + return rc; +} + +static int try_write(const char *filename) +{ + int fd; + + if (!filename) + return -EINVAL; + + fd = open(filename, O_RDWR|O_CREAT, S_IWUSR| \ + S_IRUSR|S_IRGRP|S_IROTH); + if (fd >= 0) { + close(fd); + return 0; + } + return -errno; +} + +/** + * mnt_has_regular_mtab: + * @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 + * + * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check + * errno for more details). + */ +int mnt_has_regular_mtab(const char **mtab, int *writable) +{ + struct stat st; + int rc; + const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path(); + + if (writable) + *writable = 0; + if (mtab && !*mtab) + *mtab = filename; + + DBG(UTILS, mnt_debug("mtab: %s", filename)); + + rc = lstat(filename, &st); + + if (rc == 0) { + /* file exist */ + if (S_ISREG(st.st_mode)) { + if (writable) + *writable = !try_write(filename); + return 1; + } + goto done; + } + + /* try to create the file */ + if (writable) { + *writable = !try_write(filename); + if (*writable) + return 1; + } + +done: + DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename)); + return 0; +} + +/* + * 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 + * 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 + * error (check errno for more details). + */ +int mnt_has_regular_utab(const char **utab, int *writable) +{ + struct stat st; + int rc; + const char *filename = utab && *utab ? *utab : mnt_get_utab_path(); + + if (writable) + *writable = 0; + if (utab && !*utab) + *utab = filename; + + DBG(UTILS, mnt_debug("utab: %s", filename)); + + rc = lstat(filename, &st); + + if (rc == 0) { + /* file exist */ + if (S_ISREG(st.st_mode)) { + if (writable) + *writable = !try_write(filename); + return 1; + } + goto done; /* it's not regular file */ + } + + if (writable) { + char *dirname = strdup(filename); + + if (!dirname) + goto done; + + stripoff_last_component(dirname); /* remove filename */ + + rc = mkdir(dirname, S_IWUSR| + S_IRUSR|S_IRGRP|S_IROTH| + S_IXUSR|S_IXGRP|S_IXOTH); + free(dirname); + if (rc && errno != EEXIST) + goto done; /* probably EACCES */ + + *writable = !try_write(filename); + if (*writable) + return 1; + } +done: + DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename)); + return 0; +} + +/** + * mnt_get_fstab_path: + * + * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB. + */ +const char *mnt_get_fstab_path(void) +{ + const char *p = safe_getenv("LIBMOUNT_FSTAB"); + return p ? : _PATH_MNTTAB; +} + +/** + * mnt_get_mtab_path: + * + * This function returns *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. + */ +const char *mnt_get_mtab_path(void) +{ + const char *p = safe_getenv("LIBMOUNT_MTAB"); + return p ? : _PATH_MOUNTED; +} + +/* + * Don't export this to libmount API -- utab is private library stuff. + * + * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB. + */ +const char *mnt_get_utab_path(void) +{ + struct stat st; + const char *p = safe_getenv("LIBMOUNT_UTAB"); + + if (p) + return p; + + if (stat(MNT_RUNTIME_TOPDIR, &st) == 0) + return MNT_PATH_UTAB; + + return MNT_PATH_UTAB_OLD; +} + + +/* returns file descriptor or -errno, @name returns uniques filename + */ +int mnt_open_uniq_filename(const char *filename, char **name) +{ + int rc, fd; + char *n; + + assert(filename); + + if (name) + *name = NULL; + + rc = asprintf(&n, "%s.XXXXXX", filename); + if (rc <= 0) + return -errno; + + fd = mkstemp(n); + if (fd >= 0 && name) + *name = n; + else + free(n); + + return fd < 0 ? -errno : fd; +} + +char *mnt_get_mountpoint(const char *path) +{ + char *mnt = strdup(path); + struct stat st; + dev_t dir, base; + + if (!mnt) + return NULL; + if (*mnt == '/' && *(mnt + 1) == '\0') + goto done; + + if (stat(mnt, &st)) + goto err; + base = st.st_dev; + + do { + char *p = stripoff_last_component(mnt); + + if (!p) + break; + if (stat(*mnt ? mnt : "/", &st)) + goto err; + dir = st.st_dev; + if (dir != base) { + *(p - 1) = '/'; + goto done; + } + base = dir; + } while (mnt && *(mnt + 1) != '\0'); + + memcpy(mnt, "/", 2); +done: + DBG(UTILS, mnt_debug("%s mountpoint is %s", path, mnt)); + return mnt; +err: + free(mnt); + return NULL; +} + +char *mnt_get_fs_root(const char *path, const char *mnt) +{ + char *m = (char *) mnt, *res; + const char *p; + size_t sz; + + if (!m) + m = mnt_get_mountpoint(path); + if (!m) + return NULL; + + sz = strlen(m); + p = sz > 1 ? path + sz : path; + + if (m != mnt) + free(m); + + res = *p ? strdup(p) : strdup("/"); + DBG(UTILS, mnt_debug("%s fs-root is %s", path, res)); + return res; +} + +#ifdef TEST_PROGRAM +int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *type = argv[1]; + char *pattern = argv[2]; + + printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH"); + return 0; +} + +int test_match_options(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr = argv[1]; + char *pattern = argv[2]; + + printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH"); + return 0; +} + +int test_startswith(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr = argv[1]; + char *pattern = argv[2]; + + printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT"); + return 0; +} + +int test_endswith(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr = argv[1]; + char *pattern = argv[2]; + + printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT"); + return 0; +} + +int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *path = canonicalize_path(argv[1]), + *mnt = path ? mnt_get_mountpoint(path) : NULL; + + printf("%s: %s\n", argv[1], mnt ? : "unknown"); + free(mnt); + free(path); + return 0; +} + +int test_fsroot(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *path = canonicalize_path(argv[1]), + *mnt = path ? mnt_get_fs_root(path, NULL) : NULL; + + printf("%s: %s\n", argv[1], mnt ? : "unknown"); + free(mnt); + free(path); + return 0; +} + +int test_filesystems(struct libmnt_test *ts, int argc, char *argv[]) +{ + char **filesystems = NULL; + int rc; + + rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL); + if (!rc) { + char **p; + for (p = filesystems; *p; p++) + printf("%s\n", *p); + mnt_free_filesystems(filesystems); + } + return rc; +} + +int test_chdir(struct libmnt_test *ts, int argc, char *argv[]) +{ + int rc; + char *path = canonicalize_path(argv[1]), + *last = NULL; + + if (!path) + return -errno; + + rc = mnt_chdir_to_parent(path, &last); + if (!rc) { + printf("path='%s', abs='%s', last='%s'\n", + argv[1], path, last); + } + free(path); + free(last); + return rc; +} + + +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" }, + { "--match-options", test_match_options, "<options> <pattern> options matching" }, + { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" }, + { "--starts-with", test_startswith, "<string> <prefix>" }, + { "--ends-with", test_endswith, "<string> <prefix>" }, + { "--mountpoint", test_mountpoint, "<path>" }, + { "--fs-root", test_fsroot, "<path>" }, + { "--cd-parent", test_chdir, "<path>" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/libmount/src/version.c b/libmount/src/version.c new file mode 100644 index 000000000..a3b6d0bcd --- /dev/null +++ b/libmount/src/version.c @@ -0,0 +1,86 @@ +/* + * version.c - Return the version of the blkid library + * + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * [Based on libblkid/version.c by Theodore Ts'o] + * + * See COPYING.libmount for the License of this software. + */ + +/** + * SECTION: version + * @title: Version functions + * @short_description: functions to get library version. + */ + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "mountP.h" + +static const char *lib_version = LIBMOUNT_VERSION; + +/** + * mnt_parse_version_string: + * @ver_string: version string (e.g "2.18.0") + * + * Returns: release version code. + */ +int mnt_parse_version_string(const char *ver_string) +{ + const char *cp; + int version = 0; + + for (cp = ver_string; *cp; cp++) { + if (*cp == '.') + continue; + if (!isdigit(*cp)) + break; + version = (version * 10) + (*cp - '0'); + } + return version; +} + +/** + * mnt_get_library_version: + * @ver_string: return pointer to the static library version string + * + * Returns: release version number. + */ +int mnt_get_library_version(const char **ver_string) +{ + if (ver_string) + *ver_string = lib_version; + + return mnt_parse_version_string(lib_version); +} + +#ifdef TEST_PROGRAM +int test_version(struct libmnt_test *ts, int argc, char *argv[]) +{ + const char *ver; + + mnt_get_library_version(&ver); + + printf("Library version: %s\n", ver); + printf("Library API version: " LIBMOUNT_VERSION "\n"); + + if (mnt_get_library_version(NULL) == + mnt_parse_version_string(LIBMOUNT_VERSION)) + return 0; + + return -1; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test ts[] = { + { "--print", test_version, "print versions" }, + { NULL } + }; + + return mnt_run_test(ts, argc, argv); +} +#endif |