summaryrefslogtreecommitdiffstats
path: root/libmount
diff options
context:
space:
mode:
authorKarel Zak2011-06-09 21:59:52 +0200
committerKarel Zak2011-06-09 21:59:52 +0200
commit2aefc0a8f267ddb57804755af9095d3cd5ceb0d7 (patch)
tree8601dca144fccbb9b6a895708868a520caccaddf /libmount
parentbuild-sys: use top-level directory for libuuid rather than shlibs/uuid (diff)
downloadkernel-qcow2-util-linux-2aefc0a8f267ddb57804755af9095d3cd5ceb0d7.tar.gz
kernel-qcow2-util-linux-2aefc0a8f267ddb57804755af9095d3cd5ceb0d7.tar.xz
kernel-qcow2-util-linux-2aefc0a8f267ddb57804755af9095d3cd5ceb0d7.zip
build-sys: use top-level directory for libmount rather than shlibs/mount
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'libmount')
-rw-r--r--libmount/.gitignore2
-rw-r--r--libmount/COPYING.libmount508
-rw-r--r--libmount/Makefile.am13
-rw-r--r--libmount/docs/.gitignore17
-rw-r--r--libmount/docs/Makefile.am101
-rw-r--r--libmount/docs/libmount-docs.xml63
-rw-r--r--libmount/docs/libmount-sections.txt226
-rw-r--r--libmount/docs/libmount.types0
-rw-r--r--libmount/docs/version.xml.in1
-rw-r--r--libmount/mount.pc.in11
-rw-r--r--libmount/samples/.gitignore2
-rw-r--r--libmount/samples/Makefile.am7
-rw-r--r--libmount/samples/mount.c423
-rw-r--r--libmount/samples/mountpoint.c174
-rw-r--r--libmount/src/Makefile.am67
-rw-r--r--libmount/src/cache.c667
-rw-r--r--libmount/src/context.c1741
-rw-r--r--libmount/src/context_mount.c639
-rw-r--r--libmount/src/context_umount.c739
-rw-r--r--libmount/src/fs.c1349
-rw-r--r--libmount/src/init.c46
-rw-r--r--libmount/src/iter.c78
-rw-r--r--libmount/src/libmount.h.in562
-rw-r--r--libmount/src/libmount.sym199
-rw-r--r--libmount/src/lock.c703
-rw-r--r--libmount/src/mountP.h357
-rw-r--r--libmount/src/optmap.c234
-rw-r--r--libmount/src/optstr.c1224
-rw-r--r--libmount/src/tab.c1095
-rw-r--r--libmount/src/tab_diff.c369
-rw-r--r--libmount/src/tab_parse.c745
-rw-r--r--libmount/src/tab_update.c860
-rw-r--r--libmount/src/test.c59
-rw-r--r--libmount/src/utils.c899
-rw-r--r--libmount/src/version.c86
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 &lt;kzak@redhat.com&gt;</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