Initial git import.
authorChristophe Varoqui <cvaroqui@hera.kernel.org>
Sun, 1 May 2005 22:05:22 +0000 (15:05 -0700)
committerChristophe Varoqui <cvaroqui@hera.kernel.org>
Sun, 1 May 2005 22:05:22 +0000 (15:05 -0700)
Release 0.4.5-pre2

124 files changed:
AUTHOR [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
FAQ [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.inc [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
devmap_name/Makefile [new file with mode: 0644]
devmap_name/devmap_name.8 [new file with mode: 0644]
devmap_name/devmap_name.c [new file with mode: 0644]
kpartx/ChangeLog [new file with mode: 0644]
kpartx/Makefile [new file with mode: 0644]
kpartx/README [new file with mode: 0644]
kpartx/bsd.c [new file with mode: 0644]
kpartx/byteorder.h [new file with mode: 0644]
kpartx/crc32.c [new file with mode: 0644]
kpartx/crc32.h [new file with mode: 0644]
kpartx/devmapper.c [new file with mode: 0644]
kpartx/devmapper.h [new file with mode: 0644]
kpartx/dos.c [new file with mode: 0644]
kpartx/dos.h [new file with mode: 0644]
kpartx/efi.h [new file with mode: 0644]
kpartx/gpt.c [new file with mode: 0644]
kpartx/gpt.c.orig [new file with mode: 0644]
kpartx/gpt.h [new file with mode: 0644]
kpartx/kpartx.8 [new file with mode: 0644]
kpartx/kpartx.c [new file with mode: 0644]
kpartx/kpartx.h [new file with mode: 0644]
kpartx/lopart.c [new file with mode: 0644]
kpartx/lopart.h [new file with mode: 0644]
kpartx/solaris.c [new file with mode: 0644]
kpartx/sysmacros.h [new file with mode: 0644]
kpartx/unixware.c [new file with mode: 0644]
kpartx/xstrncpy.c [new file with mode: 0644]
kpartx/xstrncpy.h [new file with mode: 0644]
libcheckers/Makefile [new file with mode: 0644]
libcheckers/checkers.h [new file with mode: 0644]
libcheckers/emc_clariion.c [new file with mode: 0644]
libcheckers/path_state.h [new file with mode: 0644]
libcheckers/readsector0.c [new file with mode: 0644]
libcheckers/selector.c [new file with mode: 0644]
libcheckers/tur.c [new file with mode: 0644]
libmultipath/Makefile [new file with mode: 0644]
libmultipath/blacklist.c [new file with mode: 0644]
libmultipath/blacklist.h [new file with mode: 0644]
libmultipath/cache.c [new file with mode: 0644]
libmultipath/cache.h [new file with mode: 0644]
libmultipath/callout.c [new file with mode: 0644]
libmultipath/callout.h [new file with mode: 0644]
libmultipath/config.c [new file with mode: 0644]
libmultipath/config.h [new file with mode: 0644]
libmultipath/debug.c [new file with mode: 0644]
libmultipath/debug.h [new file with mode: 0644]
libmultipath/defaults.c [new file with mode: 0644]
libmultipath/defaults.h [new file with mode: 0644]
libmultipath/devmapper.c [new file with mode: 0644]
libmultipath/devmapper.h [new file with mode: 0644]
libmultipath/dict.c [new file with mode: 0644]
libmultipath/dict.h [new file with mode: 0644]
libmultipath/discovery.c [new file with mode: 0644]
libmultipath/discovery.h [new file with mode: 0644]
libmultipath/dmparser.c [new file with mode: 0644]
libmultipath/dmparser.h [new file with mode: 0644]
libmultipath/hwtable.c [new file with mode: 0644]
libmultipath/hwtable.h [new file with mode: 0644]
libmultipath/memory.c [new file with mode: 0644]
libmultipath/memory.h [new file with mode: 0644]
libmultipath/parser.c [new file with mode: 0644]
libmultipath/parser.h [new file with mode: 0644]
libmultipath/pgpolicies.c [new file with mode: 0644]
libmultipath/pgpolicies.h [new file with mode: 0644]
libmultipath/propsel.c [new file with mode: 0644]
libmultipath/propsel.h [new file with mode: 0644]
libmultipath/regex.c [new file with mode: 0644]
libmultipath/regex.h [new file with mode: 0644]
libmultipath/sg_include.h [new file with mode: 0644]
libmultipath/structs.c [new file with mode: 0644]
libmultipath/structs.h [new file with mode: 0644]
libmultipath/uevent.c [new file with mode: 0644]
libmultipath/uevent.h [new file with mode: 0644]
libmultipath/util.c [new file with mode: 0644]
libmultipath/util.h [new file with mode: 0644]
libmultipath/vector.c [new file with mode: 0644]
libmultipath/vector.h [new file with mode: 0644]
multipath-tools.spec.in [new file with mode: 0644]
multipath.conf.annotated [new file with mode: 0644]
multipath.conf.synthetic [new file with mode: 0644]
multipath/01_udev [new file with mode: 0755]
multipath/02_multipath [new file with mode: 0755]
multipath/Makefile [new file with mode: 0644]
multipath/dev_t.h [new file with mode: 0644]
multipath/main.c [new file with mode: 0644]
multipath/main.h [new file with mode: 0644]
multipath/multipath.8 [new file with mode: 0644]
multipath/multipath.dev [new file with mode: 0644]
multipath/multipath.rules [new file with mode: 0644]
multipathd/Makefile [new file with mode: 0644]
multipathd/clone_platform.h [new file with mode: 0644]
multipathd/copy.c [new file with mode: 0644]
multipathd/copy.h [new file with mode: 0644]
multipathd/log.c [new file with mode: 0644]
multipathd/log.h [new file with mode: 0644]
multipathd/log_pthread.c [new file with mode: 0644]
multipathd/log_pthread.h [new file with mode: 0644]
multipathd/main.c [new file with mode: 0644]
multipathd/main.h [new file with mode: 0644]
multipathd/multipathd.8 [new file with mode: 0644]
multipathd/multipathd.init.debian [new file with mode: 0644]
multipathd/multipathd.init.redhat [new file with mode: 0644]
multipathd/pidfile.c [new file with mode: 0644]
multipathd/pidfile.h [new file with mode: 0644]
path_priority/Makefile [new file with mode: 0644]
path_priority/pp_alua/LICENSE [new file with mode: 0644]
path_priority/pp_alua/Makefile [new file with mode: 0644]
path_priority/pp_alua/main.c [new file with mode: 0644]
path_priority/pp_alua/rtpg.c [new file with mode: 0644]
path_priority/pp_alua/rtpg.h [new file with mode: 0644]
path_priority/pp_alua/spc3.h [new file with mode: 0644]
path_priority/pp_balance_units/Makefile [new file with mode: 0644]
path_priority/pp_balance_units/pp_balance_units.c [new file with mode: 0644]
path_priority/pp_emc/Makefile [new file with mode: 0644]
path_priority/pp_emc/pp_emc.c [new file with mode: 0644]
path_priority/pp_random [new file with mode: 0755]

diff --git a/AUTHOR b/AUTHOR
new file mode 100644 (file)
index 0000000..4e8eeef
--- /dev/null
+++ b/AUTHOR
@@ -0,0 +1 @@
+Christophe Varoqui, <christophe.varoqui@free.fr>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..9e31bbf
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,483 @@
+
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 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 library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           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 Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the 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 a program 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.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  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, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+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.
+\f
+  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.
+\f
+  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.
+\f
+  6. As an exception to the Sections above, you may also compile 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) 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.
+
+    c) 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.
+
+    d) 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 source code 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.
+\f
+  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 to
+this License.
+\f
+  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 Library 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.
+\f
+  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
+\f
+     Appendix: 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 Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library 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/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..2f19064
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1068 @@
+2005-05-23 multipath-tools-0.4.5
+
+       * [libmultipath] default_prio and prio_callout keyword can be
+         explicitly set to "none". Suggested by Kiyoshi Ueda, NEC
+       * [path_prio] don't exit pp_balance_units with error when
+         find_controler() is not successful. It just means no other
+         path is currently active on this controler.
+       * [path_prio] move balance_units in its own dir
+       * [multipathd] proactively fail_path upon checker up->down
+         transitions. Suggested by Edward Goggin, EMC
+       * [libmultipath] .priority is clearly an int, not an unsigned
+         int. /bin/false is now personna non grata as a prio callout.
+         Kiyoshi Ueda, NEC
+       * [libmultipath] callout.c argv parsing fix. Kiyoshi Ueda,
+         NEC
+       * [multipathd] check return codes in init_paths(), split out
+         init_event().
+       * [libmultipath] add find_slot(vec, addr) to vector lib.
+       * [multipath] remove signal sending
+       * [multipathd] use uevent to do paths list housekeeping for
+         checkers. Remove signal handling.
+       * [libmultipath] add uevent.[ch]
+
+2005-04-23 multipath-tools-0.4.4
+
+       * [path_prio] clarify pp_alua licensing. Stefan Bader, IBM.
+       * [devmap_name] add a target_type filter (suggested by Hannes)
+         and set DM task device by major:minor rather than parsing
+         the full map list.
+       * [libmultipath] propagate an error on getprio callout
+         failures, so that multipath can mark the map as immutable.
+         Reported by Lars Marowsky-Brée, Suse.
+       * [libmultipath] move push_callout() from dict.c to config.c
+         Use it in store_hwe() to get in multipathd's ramfs the
+         callout defined in hwtable.c when no config file is used.
+         Reported by Lars Marowsky-Brée, Suse.
+       * [checkers] zero sense buffers before use in EMC checker.
+         Lars Marowsky-Brée, Suse.
+       * [all] pre-release bugfixing effort from Alasdair, Hannes, 
+         Lars, Benjamin Marzinski
+       * [multipathd] set oom_adj to -17 when kernel permits.
+         Immune to OOM killer ? agk says : watch out for mem
+         leaks :/
+       * [multipathd] safety nets from udevd : exit early if
+         not root, chdir to / to avoid pining a mount.
+       * [multipathd] multipathd could loose events coming from
+         sighup or DM waitev. Add a pending_event counter to
+         track that.
+       * [path_prio] add pp_emc from Lars M Bree, Suse.
+       * [path_prio] add pp_alua from Stefan Bader, IBM.
+       * [libmultipath] add config.c:load_config(), which sucks
+         a big chunk of code out of multipath/main.c.
+       * [libmultipath] don't allocate memory in :
+               * devmapper.c:dm_get_map()
+               * devmapper.c:dm_get_status()
+       * [libmultipath] devinfo() a la carte fetching
+       * [libmultipath] merge keepalived memory audit framework
+         (Thanks again, M. Cassen). Already found and fixed a
+         couple of leaks.
+       * [libmultipath] flatten/optimize dm_map_present() and
+         dm_mapname(). Inspired by Alasdair Kergon, RedHat.
+       * [kpartx] dm_map_name() free before use bugfix. Kiyoshi
+         Ueda, NEC
+       * [kpartx] add hotplug mode. To enable name the binary
+         "kpartx.dev". Kiyoshi Ueda, NEC
+       * [multipathd] don't loose events in event waiter thread.
+         Suggested and prototyped by Edward Goggin, EMC
+       * [libmultipath] add return values to vector manipulation
+         fonctions. Mem alloc return code audit.
+       * [libmultipath] Use "config->udev_dir + path->dev" as
+         a path devnode to open instead of mknod'ing for each
+         one. Fix some DoS issues regarding usage of /tmp in
+         libmultipath/discovery.c:opennode(). Kill unlinknode()
+       * [multipathd] merged the redhat init script and stop
+         installing one on "make install"
+       * [libmultipath] fold safe_sprintf.h into util.h
+       * [libmultipath] move blacklist to a real regex matcher
+         Example config files updated : check yours !!
+       * [multipath] fix path group compare logic to not stop
+         comparing at first path in each PG.
+       * [multipathd] check if pidfile is a dead remnent of a
+         crashed daemon. If so, overwrite it. Suggested by
+         Alasdair Kergon, RedHat. Code heavily based on work
+         by Andrew Tridgell, Samba Fame.
+       * [build] dropped libdevmapper/ and libsysfs/ from the
+         package. klibc build is now broken until distributors
+         provide klibc compiled static libraries in their
+         respective packages.
+       * [libmultipath] dm_task_no_open_count() before each DM
+         ioctl. Not doing that is reported to cause deadlocks
+         in kernel-space. Reported by Edward Goggin, EMC, fix
+         suggested by Alasdair Kergon, RedHat
+         Note minimal libdevmapper version bumped up to 1.01.
+       * [multipath] switched to condlog(). "make DEBUG=N" is
+         deprecated. Debug is spat with "-v3" or more.
+       * [multipathd] "multipathd -vN" cmdline arg to control
+         daemon verbosity. 0 < N < 4. "make LOGLEVEL=N" is
+         deprecated.
+       * [libmultipath] provide a common condlog() primitive to
+         switch lib messages to syslog or stdout depending on
+         who uses the lib (daemon or tool).
+       * [kpartx] give kpartx a private, slim copy of devmap.[ch]
+       * [multipath] allow wwid in blacklist{} config section.
+         Kiyoshi Ueda, NEC.
+       * [multipathd] set mode value before use (S_IRWXU). Fixes
+         RedHat Bugzilla Bug 150665.
+       * [all] add ->fd to "struct path *". remove fd from all
+         checker context declaration. remove lots of duplicate
+         logic. Now a fd is opened only once for path. It should
+         also bring a bit safety in contended memory scenarii
+       * [libcheckers] remove redundant sg_include.h
+       * [libmultipath] merge multipath:dict.[ch] with
+         multipathd:dict.[ch] into libmultipath/. move config.h
+         there too, add some helper functions to alloc/free
+         "struct config *" in a new config.c. Start using a
+         config in the daemon.
+       * [libmultipath] move dm_geteventnr(), dm_get_maps() and
+         dm_switchgroup() in devmapper.[ch]
+       * [libmultipath] move path discovery logic in
+         libmultipath. merge devinfo.[ch] and sysfs_devinfo.[ch]
+         into discovery.[ch]
+       * [libmultipath] move config.h in libmultipath. Move
+         find_[mp|hw]e in a new config.c. Move "struct hwtable"
+         declaration in config.h. Move propsel.[ch] in the
+         lib too.
+       * [multipathd] use libmultipath:dm_type() instead of
+         duplacate and bogus devmap discovery code.
+       * [multipathd] asynchronous & non-blocking logger
+         thread. Implementation split into a generic log
+         lib and a pthread wrapper for locking and events.
+         An ipc wrapper could be easily created by
+         interested parties.
+       * [multipath] add "multipath -l -v2 [devname|devt]"
+         support in complement to [mapname|wwid]
+       * [kpartx] suppress loop.h private copy. Should fix
+         the reported build problems
+       * [multipath] do sysfs_get_mnt_path() only one time
+         and store in a global var.
+       * [multipath] further path discovery optimzation
+       * [multipath] purge superfluous includes in main.c
+       * [libmultipath] introduce a cache file. expiry set
+         to 5 secondes to covert the hotplug event storms.
+       * [multipath] split get_pathvec_sysfs(). Introduce
+         get_refwwid() and filter_pathvec()
+
+2005-03-19 multipath-tools-0.4.3
+
+       * [libmultipath] rename find_[mp|hw] to find_[mp|hw]e and
+         introduce a real find_mp().
+       * [priority] provison for recursive compilation of prio
+         subdirs, in preparation of merging more signicant
+         prioritizers. Stephan Bader, IBM
+       * [libmultipath] add a netapp controler to the hwtable
+       * [libmultipath] blacklist() not to discard sda[0-9]*
+         when sda is blacklisted
+       * [multipath] add a rr_min_io keyword in config file.
+         Suggested by Igor Feoktistov, NetApp
+       * [multipath] stop trying to avoid running in parallel
+       * [multipath] bump up params size to 1024
+       * [multipathd] put prio callouts in to ramfs. Stephan
+         Bader, IBM
+       * [multipath] simplify multibus pgpolicy : no need to
+         copy mp->paths into mp->pg->paths then free source :
+         just copy the ptr and set source to NULL.
+       * [multipath] sort PG by descending prio value in
+         group_by_prio. Stephan Bader, IBM
+       * [multipath] fix a bug in group_by_prio that lead to
+         creation of multiple PG for a single prio value
+       * [multipath] don't store multipaths in a vector anymore :
+         free the "struct multipath" after usage.
+       * [multipath] multiple optimizations in the exec plan
+       * [multipath] allow "multipath -l -v2 [mapname|wwid]"
+       * [build] rip off klibc and move to klcc, at last.
+         Good job hpa. multipath-tools now depend on klibc
+         > 1.0 to build with BUILD=klibc flag.
+       * [multipath] never reload a map if no path is up in the
+         computed new map
+       * [multipath] don't flush maps with open count != 0
+       * [libmultipath] add "int *dm_get_opencount(char *map)"
+         to devmapper.c
+       * [multipath] plug leaks and optimize devinfo.c. From
+         Edward Goggin, EMC
+       * [multipath] fix the multipath.dev hotplug script to not
+         do kpartx stuff in the same run as multipath stuff.
+         Igor Feoktistov, NetApp, noted the devmap symlink was
+         not yet present for kpartx to use.
+       * [devmap_name] accept major:minor synthax
+       * [libmultipath] add "char *dm_mapname(int maj, int min)",
+         needed to fail paths from checker thread
+       * [libmultipath] move dm_reinstate() in the lib, and add
+         dm_fail_path()
+       * [multipathd] mark failed path as failed upon DM
+         event. This should fix the design bug noticed by
+         Ramesh Caushik, Intel, where the daemon didn't run
+         multipath when a path went down and up in between 2
+         checks.
+       * [libmultipath] allow NULL as a pathvec in disassemble_map
+         as is passed only for memory optimization
+       * [libmultipath] add structs.c to store alloc_*() and
+         free_*()
+       * [libmultipath] move dmparser.[ch] to the lib.
+         remove devinfo.[ch] dependency.
+       * [build] fix compilation glitch with BUILD=klibc,
+         flags to force use of local libs, remove the link
+         dependency in klibc, try to guess kernel sources
+         and build dirs. Stefan Bader, IBM
+       * [libmultipath] find_hw matching logic to take str
+         lengths in account. Noticed by Ramesh Caushik, Intel
+       * [multipath] select_action matching logic to take str 
+         length in account.
+       * [multipath] lookup mp alias name earlier (in coalesce)
+         Edward Goggin, EMC, noticed we tried to use it before
+         it was set up.
+
+2005-01-23 multipath-tools-0.4.2
+
+       * [libmultipath] add symmetrix controler family to the
+         hwtable. Edward Goggin, EMC
+       * [libmultipath] factorize core structs (path, ...)
+         and defaults (pidfile, configfile, ...). Convert
+         callers.
+       * [multipath] fix dmparser to properly fetch non-default
+         hwhandler. Edward Goggin, EMC
+       * [multipath] fix devt2devname matching 8:1 with 8:10
+         for example. Edward Goggin, EMC
+       * [multipath] switch_pg upon devmap create or reload
+         Noticed by Ake.
+       * [libmultipath] move find_hw() the library. Convert
+         users. Now multipathd understand '*' as a product
+         string
+       * [multipath] dissaemble_map() fix to avoid to
+         interpret 'D' as a disable PG (not 'F'). Edward
+         Goggin, EMC
+       * [multipath] find_path() fix to avoid matching 8:1
+         with 8:10 for example. Edward Goggin, EMC
+       * [libmultipath] move some sysfs fetching routines
+         to library, under sysfs_devinfo.[ch]. Convert
+         callers.
+       * [multipath] fix -v0 : avoids the daemon waiting
+         for the initial multipath run to complete, which
+         will never happen because of a flooded pipe
+       * [multipathd] add scsi_id to default binvec
+       * [libmultipath] move hwtable related logic to the
+         library. Convert multipath and multipathd
+       * [multipath] move first blacklist call down after
+         setup_default_blist()
+       * [libmultipath] move basename() to the lib. Convert
+         multipath and multipathd.
+       * [libmultipath] move blacklist related logic to the
+         library. Convert multipath and multipathd
+       * [multipath] fix bug in the default hardware table
+         matching logic (Lars M-B, Suse)
+       * [multipath] allow "*" as scsi model string wildcard
+         (Lars M-B, Suse)
+       * [multipath] provide a macro to fill all hwe fields,
+         use it to declare Clariion models (Lars M-B, Suse)
+       * [multipath] use DEFAULT_GETUID instead of hardcoded
+         *and* incorrect "/bin/scsi_id -g -s" (Lars M-B, Suse)
+       * [multipath] kill superfluous suspend before table
+         reload. The code was unsafe, as spotted by Edward
+         Goggin (EMC)
+       * [multipath] exit early if device parameter is
+         blacklisted
+       * [multipath] don't check for prefix in initrd's
+         multipath.dev : this is the tool responsability to
+         exit early based on its blacklist.
+       * [multipath] don't signal the daemon in initrd
+         (Guido Guenther, Debian tracker)
+       * [multipath] better fail to run kpartx in initrd
+         than crashing the whole system. So don't sleep
+         waiting for udev to create the DM node. Maybe udev
+         has made progress I this regard ... (noticed by
+         Paul Wagland, Debian tracker)
+       * [multipath] don't reinstate when listing, ie list
+         implies dry_run
+       * [checkers] fix the emc checker (Hergen Lange)
+       * [multipath] node_name fetching shouldn't exit on
+         error. FC SAN are not the only multipathed context
+         (noticed by Ramesh Caushik)
+
+2004-12-20 multipath-tools-0.4.1
+
+       * [multipath] bump SERIAL_SIZE to 19
+       * [multipath] add a new group_by_node_name pgpolicy
+       * [multipath] move getopt policy parser to
+         get_policy_id()
+       * [multipath] remove get_evpd_wwid()
+       * [checkers] fix the wwn_set test in emc checker
+         (Hergen Lange)
+       * [checkers] treat the emc checker in the name to 
+         index translator function (Hergen Lange)
+       * [multipath] print to stderr DM unmet requirement
+         (Guido Guenther)
+       * [multipath] fix realloc return value store not
+         propagated to caller by merge_word() (Nicola Ranaldo)
+
+2004-12-10 multipath-tools-0.4.0
+
+       * [checkers] forgot to return back to caller the newly
+         allocated context. Lead to fd leak notably.
+       * [checkers] heavy check logic fix
+       * [checkers] really malloc the checker context size,
+         not the pointer size (stupidy may kill)
+       * [multipathd] check more sysfs calls return values
+       * [multipathd] search for sysfs mount point only once,
+         not on each updatepaths() iteration
+       * [multipathd] plug (char *) leak in the daemon
+       * [multipath] change pgcmp logic : we want to reload a
+         map only if a path is in computed map but not in cur
+         map (ie accept to load a map if it brings more paths)
+       * [multipath] undust coalesce_paths()
+       * [multipath] don't print unchanged multipath
+       * [multipath] store the action to take in the multipath
+         struct
+       * [multipath] print mp size with kB, MB, GB or TB units
+       * [multipath] compilation fix for merge_words() (Andy)
+       * [multipath] don't feed the kernel DM maps with paths of
+         different sizes : DM fails and we end up with an empty
+         map ... not fun
+       * [multipath] cover a new corner case : path listed in
+         status string but disappeared from sysfs
+       * [multipath] remove the "-D" command line flag : now
+         we can pass major:minor directly as last argument, like
+         device names or device map names. Update multipathd
+         accordingly.
+       * [multipath] try reinstate again paths after a switchpg
+       * [multipath] reinstate condition change : 
+
+2004-12-05 multipath-tools-0.3.9
+
+       * [multipath] add a "-l" flag to list the current
+         multipath maps and their status info
+       * [priority] zalloc controler to avoid random path_count
+         at allocation time
+       * [multipath] add configlet pointers in struct multipath
+         to avoid searching for an entry over and over again
+       * [multipath] new reinstate policy : on multipath exec,
+         reinstate all failed paths the checkers report as ready
+         if they belong to enabled path groups (not disabled, not
+         active path group)
+       * [multipath] fork a print_mp() out of print_all_mp()
+       * [multipath] introduce PG priority, which is the sum of
+         its path priorities. Set first_pg in the map string to
+         the highest prio PG index.
+       * [multipath] assemble maps scaning PG top down now that
+         PG vector is unsorted
+       * [multipath] move select_*() to propsel.c
+       * [multipath] move devinfo() to devinfo.c
+       * [multipath] move h/b/t/l fetching to sysfs_devinfo()
+       * [multipath] move devt2devname() to devinfo.c so we can
+         use it from dmparser.c too
+       * [multipath] introduce select_alias() and clarify a bit
+         of code
+       * [multipath] don't sort PG anymore. We want the map as
+         static as possible.
+       * [multipath] fix a segfault in apply_format() triggered
+         when no config file found.
+       * [multipath] kill unused vars all over the place
+       * [multipath] add a struct pathgroup in struct multipath
+         Store the pathvec in it. We now have a place to store
+         PG status, etc ...
+       * [multipath] new dmparser.c, with disassemble_map(),
+         disassemble_status()
+       * [multipath] suppress *selector_args keywords. Merge
+         in the selector string. Update config file templates.
+
+2004-11-26 multipath-tools-0.3.8
+
+       * [priority] teach multipath to read callout keywords
+         formatted as /sbin/scsi_id -g -u -s /block/%n
+         Apply one substitutions out of :
+               * %n : blockdev basename (ie sdb)
+               * %d : blockdev major:minor string (ie 8:16)
+         update sample config files
+       * [priority] fix find_controler(). Now works, verified
+         on IBM T200 at OSDL (thanks again, Dave). Add to the
+         main build process
+       * [multipath] add a controler specific "prio_callout"
+         keyword. Noticed by Ake
+       * [multipath] normalize the debug ouput
+       * [multipath] add select_getuid(). De-spaghetti
+         devinfo() thanks to that helper.
+       * [libmultipath] add VECTOR_LAST_SLOT macro.
+         multipath/dict.h now use it heavily.
+       * [multipath] policies selectors speedup and cleanup
+         (pgpolicy, features, hwhandler, selector)
+       * [multipath] new "flush" command flag
+       * [libmultipath] add dm_type() and dm_flush_maps()
+       * [multipath] move dm_get_map() to libmultipath
+       * [multipath] rename iopolicy to pgpolicy everywhere.
+         Dual terminology was getting confusing.
+       * [multipath] assemble_map() to always set next_pg to 1
+         for now.
+       * [multipath] update config file to show new keywords.
+         Add an IBM array tested at OSDL.
+       * [multipath] fork select_iopolicy() from setup_map()
+       * [multipath] introduce select_features() and 
+         select_hwhandler(). Should merge select_* one day ...
+       * [multipath] add features and hardware_handler keywords
+         and use them in the map setup
+       * [build] make clean really clean. Noticed by Dave Olien,
+         OSDL
+       * [multipath] group_by_serial bugfix
+       * [multipath] dm_addmap() return value fix. Now multipath
+         really creates the maps
+       * [multipath] try dm_log_init_verbose() instead of dup()
+         + close() to silence libdevmapper (Ake at umu)
+       * [libcheckers] remove checkpath() wrapper, obsoleted by
+         the "fd in context" changes
+       * [multipathd] let pathcheckers allocate their context.
+         No more over or unneeded allocation.  Suggested by Lars,
+         Suse
+       * [multipathd] store the pathcheckers fd in their context.
+         No more open / close on each check. Suggested by Lars,
+         Suse
+
+2004-11-05 multipath-tools-0.3.7
+
+       * [multipathd] fix off by one memory allocation (Hannes,
+         Suse)
+       * [multipathd] introduce a default callout handler that
+         just remembers to put the callout in ramfs, even if the
+         daemon has no direct use of them. multipath need some
+         that where forgotten, so parse them and use that default
+         handler.
+       * [libcheckers] emc_clariion checker update (Lars, Suse)
+       * [build] exit build process on failure (Lars, Suse)
+       * [kpartx] exit early if DM prereq not met
+       * [multipath] exit early if DM prereq not met
+       * [libmultipath] new dm_prereq() fn to check out if all DM
+         prerequisites are met
+       * [libmultipath] move callout.[ch] function in there.
+         multipath and multipathd impacted
+       * [libmultipath] move dm_* function in there. kpartx,
+         multipath are impacted
+       * [priority] pp_balance_lun should use DM_DEVICE_TABLE ioctl
+         instead of DM_DEVICE_STATUS to find out paths from the
+         primary path groups.
+       * [klibc] drop in "Stable" version 0.190
+       * [build] add manpages for kpartx and multipathd (Patrick
+         Caulfield)
+       * [build] use system's sysfs for multipathd linking
+       * [build] make glibc the default build
+       * [build] "make BUILD=klibc" is enough, deprecate the 
+         "make BUILD=klibc klibc" synthax
+
+2004-10-30 multipath-tools-0.3.6
+
+       * Patrick Caulfield took over debian packaging. Showing
+         evident expertise, his first wish is to see debian/
+         disappear. :) So be it.
+       * [libmultipath] add a vector_foreach_slot macro. Still
+         needs an iterator but saves 1 line per loop occurence and
+         tame this UPPERCASE MACROS bad taste.
+       * [multipathd] don't load sg anymore on multipathd startup
+       * [multipathd] change killall for kill `cat $PIDFILE` in
+         init script (Jaime Peñalba & Cesar Solera)
+       * [multipathd] the fork fallback was borked (just exiting)
+         noticed by Jaime Peñalba & Cesar Solera
+       * [multipathd] try without the FLOATING_STACKS flag. Does
+         it matter anyway ?
+       * [multipathd] merge clone_platform.h from LTP and cover
+         the hppa special case.
+       * [multipath] since we will be able to create a devmap with
+         paths too small, don't rely anymore on the first path's
+         size blindly : verify the path is up, before assigning its
+         size to the multipath
+       * [priority] add a path priority fetcher to balance LU accross
+         controlers based on the controler serial detection. Untested
+         but provides a good example of what can be done with the
+         priority framework.
+       * [priority] create subdir and drop a test pp_random
+       * [multipath] add dev_t reporting to print_path() to ease
+         devmap decoding by humans
+       * [multipath] change default path priority to 1
+       * [multipath] add wits to the sort_by_prio policy, so that
+         sort_pathvec_by_prio() is now useless. Remove it.
+       * [multipath] invert sort_pg_by_summed_prio sort order :
+         highest prio leftmost
+       * [libmultipath] add vector_del_slot
+       * revert multipath.rules change : devmap_name still takes
+         "major minor" and not "major:minor" as argument
+       * Makefile refinement : you can now enter any tool directory
+         and build from here, deps are solved
+
+2004-10-26 multipath-tools-0.3.5
+
+       * [multipathd] fix broken test for path going up or shaky
+         that kept executing multipath when it shouldn't
+       * change multipath.dev to exit early when udev' DEVNAME is
+         a devmap (/dev/dm-*). This avoids a recursion case when
+         the kernel devmapper keeps removing a map after multipath
+         configures it.
+       * change multipath.rules to follow the new -D synthax
+       * [multipath] "-D major minor" synthax changed to 
+         "-D major:minor" to match the sysfs attribute value.
+         This change removes a few translations in multipath and
+         multipathd.
+       * [multipath] fix segfault in test if conf->dev is a devmap
+         (the one forwarded by MikeAnd)
+       * SG_IO ioctl seem to work in lk 2.6.10+, so remove all sg
+         device knowledge and advertise (here) the new dependency.
+       * [multipath] remove unused do_tur()
+       * [multipath] fix sort_pg_by_summed_prio(), and don't add up
+         failed path priority
+
+2004-10-26 multipath-tools-0.3.4
+
+       * [multipathd] exec multipath precisely : pass in the path
+         or the devmap to update. No more full reconfiguration, and
+         really use the reinstate feature of multipath.
+       * [multipathd] check all paths, not only failed ones. Path
+         checker now trigger on state change (formerly triggred on
+         state == UP condition)
+       * [multipathd] incremental updatepaths() instead of scrap /
+         refresh all logic.
+       * [multipathd] path checkers now take *msg and *context
+         params. consensus w/ lmb at suse. tur.c modified as example
+       * [multipath] assemble maps in PG vector descending order to
+         fit the layered policies design
+       * [multipath] stop playing with strings in pgpolicies, as it
+         uses more memory and looses info for no gain
+       * [multipath] remove lk2.4 scsi ioctl scsi_type remnant
+       * [multipath] layered pgpolicies : (see pgpolicies.c)
+               * group_by_status
+               * group_by_serial | multibus | failover | group_by_prio
+               * sort_pg_by_summed_prio
+         thus remove duplicated failedpath logic in pgpolicies
+       * [libmultipath] add vector_insert_slot
+       * [checkers] framework for arbitrate checkers return values
+       * [multipathd] scrap yet another reinvented wheel in the 
+         name of the LOG macro :  learn the existance of setloglevel
+         and LOG_UPTO macro
+       * glibc make with "make BUILD=glibc", asked by lmb at suse
+
+2004-10-20 multipath-tools-0.3.3
+
+       * [checkers] add the emc_clariion path checker (lmb at Suse)
+       * [multipath] introduce safe_snprintf macro to complement the
+         safe_sprintf. Needed to cover the sizeof(pointer) cases
+         pointed by Dave Olien at OSDL
+       * [multipath] move to the common libchecker framework and
+         activate the selector
+       * [multipath] fix an iopolicy selector bug (initialized lun
+         iopolicy overrode controler-wide iopolicy)
+       * [multipathd] cleanly separate out the checker selector, as
+         done with iopolicy selector
+       * [multipathd] move out the checkers into a common libcheckers
+       * [multipath] fix the anti-parallel-exec logic : use a write 
+         lease for the task. From Dave Olien at osdl.
+       * [multipath] fix reinstate : pass a devt, not a devname
+
+2004-10-16 multipath-tools-0.3.2
+
+       * [multipath] add path reinstate logic :
+               * if a path is given as multipath arg
+               * if the map containing that path already exists
+               * if this map is the same as the that would be
+                 created by this multipath run
+               * THEN reinstate the path
+         multipathd is is thus unchanged, while now supporting
+         reinstate
+       * audit and ensafe all sprintf usage
+       * [multipath] fix the annoying \n after each dev_t in
+         params string reporting
+       * [multipath] print out devmaps in "-v2 -d" mode
+       * [kpartx] bump up the params string size (lmb at suse)
+       * [kpartx] replace sprintf by snprintf (lmb at suse)
+       * [kpartx] initialize some more vars (lmb at suse)
+       * [multipath] mp->pg == NULL safety net before calling
+         assemble_map() (for Andy who happen to hit the bug)
+       * [multipath] last rampant bug in map CREATE or UPDATE switch
+         logic due to the device alias feature
+       * [kpartx] zeroe "struct slice all" (lmb at suse)
+
+2004-10-11 multipath-tools-0.3.1
+
+       * [kpartx] move back to getopt, originaly removed from the 
+         original partx because of lack of implementation in klibc
+       * [kpartx] don't map extended partitions
+       * [kpartx] add a -p command flag to allow admin to force a
+         delimiting string between disk name and part number. When
+         specified always use it, when unspecified use 'p' as a delim
+         when last char of disk name is a digit, NUL otherwise.
+       * [kpartx] clean up
+       * bump klibc to 0.182
+       * one step further : use klibc MCONFIG for all klibc specific
+         FLAGS definitions, ie massive Makefile.inc cleanup
+       * follow the klibc compilation rules by appending its OPTFLAGS
+         to multipath-tools' CFLAGS. This corrects the segfaults seen
+         on i386 where klibc is built with regparm=3 and tools are not
+       * [multipathd] fall back to fork when clone not available
+         like in Debian Woody
+       * [kpartx] move .start and .size from uint to ulong (Ake)
+       * briefly document system-disk-on-multipath in the FAQ file
+
+2004-10-06 multipath-tools-0.3.0
+
+       * first cut at making scripts to create multipath-aware initrds
+         those scripts are tested on Debian SID, and must be copied into
+         /etc/mkinitrd/scripts. it works here.
+       * [multipath] verify presence of the /sys/block/... node before
+         reading sysfs attributes. Avoids libsysfs and scsi_id stderr
+         garbage
+       * [multipath] move down the stderr close (Ake Sandgren at umu.se)
+       * [multipath] don't care about 0-sized mp (Ake Sandgren at umu.se)
+       * [multipath] bump mp size field to ulong (Ake Sandgren at umu.se)
+       * [multipath] replace quiet/verbose flags by a verbosity one.
+         introduce a new verbosity level : 1 == print only devmap names
+         thus we can feed kpartx with that output
+       * [multipath] update man page to reflect the hotplug.d -> dev.d
+         transition and replace the obsolete group_by_tur policy by the
+         forgotten group_by_prio
+       * [multipath] provide a /etc/udev/rules.d/multipath.rules for
+         multipath devices naming. Cleaner than the previously suggested
+         rule addition in the main udev.rules
+       * [multipath] move out of hotplug.d to dev.d : kill synchronisation
+         problems between device node creation and multipath execution.
+         Incidentally the unfriendly $DEVPATH param become a friendly
+         $DEVNAME (simply /dev/sdb)
+       * [multipath] rework the iopolicies name-to-id & id-to-name
+         translations. kills the last compilation warning here too
+       * [kpartx] kill last compilation warnings
+       * bump klibc to 0.181
+       * add the debian/ packaging dir (make deb)
+       * prototype __clone & __clone2
+
+2004-09-24 multipath-tools-0.2.9
+
+       * [multipathd] finally tame the clone compilation glitch on IA64
+         move from sys_clone to __clone / __clone2
+       * [kpartx] rework from Stephan Bader, IBM :
+               * handle s390x arch
+               * endianness fixes
+               * push the partname string size to handle wwwids
+               * quieten implicit cast warnings
+       * [multipath] add an 'alias' multipath keyword for friendlier device
+         names. This was "asked" by OSDL' CGL board of secret reviewers
+       * [multipath] last pass with JBOD and parallel SCSI support :
+         hard-code scsi_id as a fallback when disk strings doesn't match
+         any hwtable entry
+       * [multipath & multipathd] change the parser to not coalesce
+         consecutive spaces (Patrick Mansfield)
+       * [multipath] remove the [UN]: output prefix, so that stdout can be
+         easily fed to a tool like dmsetup
+       * [multipathd] DEBUG=3 logs more readable/usefull
+       * [multipathd] add a multipath_tool config keyword
+       * [multipathd] move to execute_program() like multipath already did
+       * [multipath] don't print the "no path" msg in quiet mode
+       * [multipathd] include linux/unistd.h for _syscall2
+         definition on RedHat systems. Remove superfluous
+         asm/unistd.h include
+       * [libsysfs] forked : last version uses mntent, which
+         klibc doesn't provide. That, plus the fact we use
+         only 1/3 of the lib, pushed me to freeze the version
+         and strip all unused stuff.
+       * [multipathd] prepare_namespace() cleanup : no more "multipath"
+         special casing since we push it to binvec vector, like the other
+         callouts detected in the config file.
+
+2004-08-13 multipath-tools-0.2.8
+
+       * [multipathd] setsched prio to RT
+       * [multipath] massive include cleanup
+       * [multipath] add a "default_prio_callout" keyword
+         first user will be SPC-3 ALUA priority field fetcher
+         from IBM
+       * [multipath] reenable stdout on some error paths
+       * [build] spilt KERNEL_DIR into KERNEL_SOURCE &
+         KERNEL_BUILD as per 2.6 and SuSe convention
+       * [klibc] kill warnings due to awk parsing wrong locale in
+         arch/i386/MCONFIG
+       * [multipath] implement a generic group_by_prio pgpolicy
+       * [multipath] fix the broken failover pgpolicy
+
+2004-07-24 multipath-tools-0.2.7
+
+       * [multipath] args parser moved to getopt
+         <genanr@emsphone.com>
+       * [multipath] zero conf->hotplugdev at allocation
+         <genanr@emsphone.com>
+       * [multipath] clean up failed devmap creation attempt
+       * [libs] update to libdevmapper 1.00.19
+       * [multipath] framework for claimed device skipping
+         still lacks a reliable way to know if the device is
+         claimed and by who (fs, swap, md, dm, ...). If you
+         think it is valid to let libdevmapper hit the wall,
+         please speak up and tell so.
+       * [multipath] shut down stderr when calling into libdm
+       * [multipath] reformat the verbose output
+       * [multipath] framework for path priority handling (ALUA)
+       * [multipath] kill all reference to group_by_tur
+       * [multipath] integrate path state logic into multibus &
+         failover pgpolicies. This obsoletes the group_by_tur one
+         which is now the same as multibus.
+       * [multipath] zalloc mp structs to avoid garbage in ->size
+       * bump version requisite for scsi_id to 0.6 to support the new
+         '-u' flag (s/ /_/ for proper JBOD device map naming)
+       * [multipath] correct the for(;;) limits to accept 1-slot
+         pathvecs
+       * [multipath] push WWID_SIZE to 64 char (scsi_id w/ JBODs)
+       * [multipath] add a exit_tool() wrapper fn for runfile unlink
+       * [multipath] add a "default_path_grouping_policy" keyword in the
+         "defaults" block.
+       * [multipath] add a "default_getuid_callout" keyword in the
+         "defaults" block. Now multipath is going to work with JBODs
+       * [multipath] fix segfault when pathvec is empty after
+         get_pathvec()
+       * move to template based specfile to avoid regular version skew
+
+2004-07-16 multipath-tools-0.2.6
+
+       * [multipathd] implement the system-disk-on-SAN safety net
+       * [multipathd] add exit_daemon() wrapper function
+       * [multipathd] mlockall() all daemon threads
+       * [multipath] fix a bug in the mp_iopolicy_handler that kept
+         the iopolicy per LUN override from working
+       * [multipath] display the tur bit value in print_path
+         as requested by SUN
+       * try to open /$udev/reverse/$major:$minor before falling back
+         to mknod
+       * add "udev_dir" to the defaults block in the config file
+       * merge "daemon" & "device_maps" config blocks into a new
+         "defaults" block
+       * [multipath] properly comment the config file
+       * [multipath] generalize the dbg() macro usage
+         Makefile now has a DEBUG flag
+       * [multipath] move to callout based WWID fetching. Default to
+         scsi_id callout. I merged execute_program from udev for that
+         (so credit goes to GregKH)
+       * [multipath] get rid of "devnodes in /dev" assumption
+         ie move to "maj:min" device mapper target synthax
+
+2004-07-10 multipath-tools-0.2.5
+
+       * [multipathd] fix misbehaviour noted by <genanr@emsphone.com>
+         improper tar directive in Makefile on some systems
+       * [multipathd] fix bug noted by <genanr@emsphone.com>
+         get_devmaps fills a private vector and forget to pass its
+         address to caller
+       * [multipath] extend EVPD 0x83 id fetching logic.
+         Code borrowed from scsi_id (thanks goes to Patrick
+         Mansfield @IBM) and merged by Hannes Reinecke @SUSE
+       * [multipathd] fix regression noted by <genanr@emsphone.com>
+         (segfault when no config file)
+
+2004-06-20 multipath-tools-0.2.4
+
+       * [multipathd] break free from system's libsysfs for now
+         as it is not that common these days
+       * [multipath] introduce per LUN policies in the config
+         file : path_grouping_policy, path_selector and
+         path_selector_args are supported.
+         See updated sample config file.
+       * [multipath] move ->iopolicy to multipath struct (from
+         path struct)
+       * [multipath] fill the voids left in the config file with
+         defaults
+       * [multipath] group config & flags in a global struct *
+       * [multipath] fix segfault when no config file (was a 
+         regression since hwtable vectorisation in 0.2.2)
+       * [multipath] default path selector override in config file
+       * [multipath] don't play with strings in pgpolicies, leave
+         that to a new assemble_map fn. policies now use vector
+       * [multipathd] compilation fix for gentoo (Franck Denis)
+       * [multipath] strcmp fix (Franck Denis)
+
+2004-06-14 multipath-tools-0.2.3
+
+       * [multipath] group_by_serial try to be smart with LUN
+         balancing across controlers (for STK / LSI) :
+         1st multipath : 1st pg made of path through 1st controler
+         2nd multipath : 1st pg made of path through 2nd controler
+         3rd multipath : 1st pg made of path through 1st controler
+         ...
+       * [multipath] drop .pindex[] in struct multipath in favor
+         of a *paths vector : much cleaner
+       * [multipath] fix group_by_serial pgpolicy broken by
+         vectorisation in 0.2.2
+       * add a StorageTek array in the sample multipath.conf
+       * [multipathd] strcmp fix from Franck Denis
+       * [multipathd] convert to vector api
+       * [multipathd] add a configfile option for path checking
+         interval. See sample configfile for synthax.
+
+2004-06-07 multipath-tools-0.2.2
+
+       * [multipath] leave out 2.4 compat code. Is there
+         interest anyway ?
+       * [multipath] convert all_paths table to vector api.
+         Rename to pathvec. Get rid of max_devs
+       * [multipath] convert mp table to vector api
+       * convert blacklist to vector api
+       * 2.6.7-rc? adds _user annotations to scsi/sg.h, causing
+         compilation breakage. Add a "#define _user" in all
+         sg_include.h (and remove cruft)
+       * merge a real parser (from keepalived) courtesy of 
+         Alexandre Cassen. Now multipath and multipathd share a
+         config file. This comes with a nice vector lib.
+       * devnode blacklist moved from hardcoded to config file
+       * Guy Coates noted -O2 CFLAGS lead to multipathd crashes
+         on IA64. Remove the needless optimisation for now.
+
+2004-06-05 multipath-tools-0.2.1
+
+       * [multipath] add a flag to inihibit the final SIGHUP to
+         multipathd. Needed to avoid recursion with the correction
+         below
+       * [multipathd] devmap event now triggers a multipath exec
+         in addition to the usual updatepaths()
+       * [multipathd] move checkers from sg_io on BLK onto CHR
+         readsector0 goes from read to sg_read
+       * [multipathd] rely on sysfs for failedpaths enum and no
+         longer on the device mapper
+       * [multipathd] convert get_lun_strings from ioctl to sysfs
+         so we can benefit from strings persistency for failed
+         paths
+       * [multipath] readconfig() to take only 8 char from vendor
+         string (ake)
+       * [multipath] remove unecessery and wrong getuid == NULL
+         check from devinfo() (ake)
+       * [multipathd] make readsector0 open path O_DIRECT
+       * [multipathd] sizeof(path) -> sizeof(struct path) (MikeC)
+       * [Makefile] don't try to install and uninstall libs
+       * [devmap_name] kill the wrong trailing '\n'
+         (Mike Christie)
+       * [kpartx] works with device nodes outside /dev
+       * [kpartx] correctly display the delimiter in partition
+         name outputs
+
+2004-05-17 multipath-tools-0.2.0
+
+       * change the default klibc by greg's :
+         corrects the segfaults reported by Ling Hwa Hing
+
+2004-05-16 multipath-tools-0.1.9
+
+       * break free from udev : package klibc and libsysfs
+       * add a spec file and a "make rpm" rule
+       * pensum on klibc changes needed :
+               * mmap.c & fork.c : invert includes
+               * make clean wipes .*.d
+               * auto create the linux symlink
+               * remove tools and specfiles (files and Makefile
+                 targets)
+
+2004-05-15 multipath-tools-0.1.8
+
+       * Makefiles cleanup and factorisation
+       * Compilation fixes for non-ix86 archs, tested on x86_64
+       * strip execs harder for a 10% size reduction
+       * blacklist /dev/fd* and /dev/loop*
+       * dmadm works with sysfs nodes with '!' (cciss for ex)
+
+2004-05-10 multipath-tools-0.1.7
+
+       * bugfixes from Andy <genanr@emsphone.com> :
+               * read the last line of the config file
+               * add an entry for the 3PARData storage ctlrs
+               * read the last char of vendor and model strings
+
+2004-04-25 multipath-tools-0.1.6
+
+       * add the dmadm WIP tool (read MD superblocks and create
+         corresponding devmaps when possible)
+       * plug fd leak in TUR path checker
+
+2004-03-25 multipath-tools-0.1.5
+
+       * kpartx to manage the nested bdevs as /dev/cciss/c0d0.
+         parts are named sysfs style : cciss!c0d0p*
+       * kpartx loop support
+       * kpartx do DM updates if part maps already present
+       * merge kpartx for partitioned multipath support
+       * add get_null_uid to getuid methods. assign it the "0" index
+         devices with this getuid are thus ignored by multipath.
+         warning : change /etc/multipath.conf (get_evpd_wwid == 1)
+       * mv all_scsi_ids out of the 2.6 code path, into the 2.4 one
+       * unlink runfile on malloc exit path
+       * update multipath manpage (MikeC)
+
+2004-03-17 multipath-tools-0.1.4
+
+       * multipath clean up
+               * split default hw table in hwtable.h
+               * split grouping policies in pgpolocies.c
+               * pass *mp to setup_map instead of mp[]+index
+       * ensure defhwtable is used if /etc/multipath.conf is buggy
+       * hwtable is not global anymore
+       * unlink the runfile in various error paths
+
+2004-03-13 multipath-tools-0.1.3
+
+       * multipath config tool now has a config file parser
+         (did I say I make the ugliest parsers ?)
+       * example multipath.conf to put in /etc (manualy)
+
+2004-03-12 multipath-tools-0.1.2
+
+       * detach the per devmap waiter threads
+       * set the thread stack size to lower limits
+         (VSZ down to 4MB from 85 MB here)
+
+2004-03-06 multipath-tools-0.1.1
+
+       * include dlist.h in multipath main.c (PM Hahn)
+       * typo in hotplug script (PM Hahn)
+       * pass -9 opt to gzip for manpages (PM Hahn)
+
+2004-03-05 multipath-tools-0.1.0
+
+       * add the group_by_tur policy
+       * add the multipathd daemon for pathchecking & DM hot-reconfig
+       * multipath doesn't run twice
+       * massive cleanups, and code restructuring
+       * Avoid Kernel Bug when passing too small a buffer in do_inq()
+       * Sync with 2.6.3-udm4 target synthax (no more PG prio)
+
+2004-02-21 multipath-018
+
+       * From the Debian SID inclusion review (Philipp Matthias Hahn)
+               * use DESTDIR install prefix in the Makefile
+               * add man pages for devmap_name & multipath
+               * correct libsysfs.h includes
+               * fork the hotplug script in its own shell
+       * Sync with the kernel device mapper code as of 2.6.3-udm3
+         ie. Remove the test interval parameter and its uses
+       * Remove superfluous scsi parameter passed from hotplug
+       * Add the man pages to the [un]install targets
+
+2004-02-17 multipath-017
+
+       * remove the restrictive -f flag.
+         Introduce a more generic "-m iopolicy" one.
+       * remove useless "int with_sysfs" in env struct 
+
+2004-02-04 multipath-016
+
+       * add a GROUP_BY_SERIAL flag. This should be useful for
+         controlers that activate they spare paths on simple IO
+         submition with a penalty. The StorageWorks HW defaults to
+         this mode, even if the MULTIBUS mode is OK.
+       * remove unused sg_err.c
+       * big restructuring : split devinfo.c from main.c. Export :
+               * void basename (char *, char *);
+               * int get_serial (int, char *);
+               * int get_lun_strings (char *, char *, char *, char *);
+               * int get_evpd_wwid(char *, char *);
+               * long get_disk_size (char *);
+       * stop passing struct env as param
+       * add devmap_name proggy for udev to name devmaps as per their
+         internal DM name and not only by their sysfs enum name (dm-*)
+         The corresponding udev.rules line is :
+         KERNEL="dm-[0-9]*", PROGRAM="/sbin/devmap_name %M %m", \
+         NAME="%k", SYMLINK="%c"
+       * remove make_dm_node fn & call. Rely on udev for this.
+       * don't rely on the linux symlink in the udev/klibc dir since
+         udev build doesn't use it anymore. This corrects build breakage
+
+2004-01-19 multipath-013
+
+       * update the DM target synthax to the 2.6.0-udm5 style
+
+2003-12-29 multipath-012
+
+       * check hotplug event refers to a block device; if not exit early
+       * refresh doc
+       * add the uninstall target in Makefile
+
+2003-12-22 multipath-010
+
+       * tweak the install target in Makefile
+       * stop passing fds as argument : this change enable a strict
+         segregation of ugly 2.4 code
+       * sysfs version of get_lun_strings()
+       * be careful about the return of get_unique_id() since errors 
+         formerly caught up by if(open()) in the caller fn are now returned
+         by get_unique_id()
+       * send get_serial() in unused.c
+       * introduce dm-simplecmd for RESUME & SUSPEND requests
+       * split add_map() in setup_map() & dm-addmap()
+       * setup_map() correctly submits "SUSPEND-RELOAD-RESUME or CREATE"
+         sequences instead of the bogus "RELOAD or CREATE"
+       * don't print .sg_dev if equal to .dev (2.6) in print_path()
+       * since the kernel code handles defective paths, remove all
+         code to cope with them :
+               * move do_tur() to unused.c
+               * remove .state from path struct
+               * remove .state settings & conditionals
+       * add a cmdline switch to force maps to failover mode,
+         ie 1 path per priority group
+       * add default policies to the whitelist array (spread io ==
+         MULTIBUS / io forced to 1 path == FAILOVER)
+       * move get_disk_size() call out of add_map() to coalesce()
+       * comment tricky coalesce() fn
+       * bogus unsused.c file renamed to unused.c
+
+2003-12-20 multipath-010
+
+       * big ChangeLog update
+       * start to give a little control over target params :
+         introduce cmdline arg -i to control polling interval
+       * cope with hotplug-style calling convention :
+         ie "multipath scsi $DEVPATH" ... to avoid messing with
+         online maps not concerned by an event
+       * example hotplug agent to drop in /etc/hotplug.d/scsi
+       * revert the run & resched patch : unless someone proves me
+         wrong, this was overdesigned
+       * move commented out functions in unused.c
+       * update multipath target params to "udm[23] style"
+       * mp target now supports nr_path == 1, so do we
+       * add gratuitous free()
+       * push version forward
+
+2003-12-15 multipath-009
+
+       * Make the HW-specific get_unique_id switch pretty
+       * Prepare to field-test by whitelisting all known fibre array,
+         try to fetch WWID from the standard EVPD 0x83 off 8 for everyone
+       * configure the multipath target with round-robin path selector and
+         conservative default for a start (udm1 style) :
+         yes it makes this release the firstreally useful one.
+       * temporarily disable map creation for single path device
+         due to current restrictive defaults in the kernel target.
+         Sistina should work it out.
+       * correct the strncmp logic in blacklist function.
+       * update the Makefiles to autodetect libgcc.a & gcc includes
+         "ulibc-style". Factorisation of udevdirs & others niceties
+       * drop a hint about absent /dev/sd? on failed open()
+       * implement a reschedule flag in /var/run.
+         Last thing the prog do before exit is check if a call to multipath
+         was done (but canceled by /var/run/multipath.run check) during its
+         execution. If so restart themain loop.
+       * implement a blacklist of sysfs bdev to not bother with for now
+         (hd,md, dm, sr, scd, ram, raw).
+         This avoid sending SG_IO to unappropiate devices.
+       * Adds a /var/run/multipath.run handling to avoid simultaneous runs.
+       * Remove a commented-out "printf"
+       * drop a libdevmapper copy in extras/multipath;
+         maybe discussions w/Sistina folks will bring a better solution
+         in the future.
+       * drop a putchar usage in libdevmapper to compile cleanly with klibc
+       * drop another such usage of my own in main.c
+       * massage the Makefile to compile libdevmapper against klibc
+       * use "ld" to produce the binary rather than "gcc -static"
+       * stop being stupid w/ uneeded major, minor & dev in main.c:dm_mk_node()
+       * reverse to creating striped target for now because the multipath
+         target is more hairy than expected initialy
+       * push the version code to 009 to be in synch w/ udev
+
+2003-11-27 multipath-007
+
+       * removes sg_err.[ch] deps
+       * makes sure the core code play nice with klibc
+       * port the sysfs calls to dlist helpers
+       * links against udev's sysfs (need libsysfs.a & dlist.a)
+       * finally define DM_TARGET as "multipath" as Joe posted the code today
+         (not tested yet)
+       * push version forward (do you want it in sync with udev version?)
+
+2003-11-19 multipath-006
+
+       * merged in udev-006 tree
+
+2003-09-18 multipath-0.0.1
+
+       * multipath 0.0.1 released.
+       * Initial release.
diff --git a/FAQ b/FAQ
new file mode 100644 (file)
index 0000000..75216a0
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,63 @@
+1. How to set up System-on-multipath ?
+======================================
+
+prerequisite : udev and multipath-tools installed
+
+here are the steps on a Debian SID system :
+
+* add dm-mpath and dm-multipath to /etc/mkinitrd/modules
+* copy $tools_dir/multipath/0[12]_* to /etc/mkinitrd/scripts
+* define a friendly alias for your multipathed root disk
+  (in /etc/multipath.conf). Example : "system"
+* enable busybox in /etc/mkinitrd/mkinitrd.conf and set ROOT
+  to any valid block-device (but not a /dev/dm-* one, due to
+  an mkintrd misbelief that all dm-* are managed by LVM2)
+* run mkinitrd
+* in /boot/grub/menu.lst, define the root= kernel parameter
+  Example : root=/dev/system1
+* modify /etc/fstab to reference /dev/system* partitions
+
+At reboot, you should see some info like :
+
+path /dev/sda : multipath system
+...
+gpt: 0 slices
+dos: 5 slices
+reduced size of partition #2 to 63
+Added system1 : 0 70685937 linear /dev/system 63
+Added system2 : 0 63 linear /dev/system 7068600
+Added system5 : 0 995967 linear /dev/system 70686063
+...
+
+2. How does it compare to commercial product XXX ?
+==================================================
+
+Here are a few distinctive features :
+
+* you can mix HBA models, even different vendors, different speed ...
+* you can mix storage controllers on your SAN, and access them all, applying
+  different path grouping policy
+* completely event-driven model : no administration burden if you accept the
+  default behaviours
+* extensible : you can plug your own policies if the available ones don't fill
+  your needs
+* supports root FS on multipathed SAN
+* free, open-source software
+
+3. LVM2 doesn't see my multipathed devices as PV, what's up ?
+=============================================================
+
+By default, lvm2 does not consider device-mapper block devices (such as a
+dm-crypt device) for use as physical volumes.
+
+In order to use a dm-crypt device as an lvm2 pv, add this line to the
+devices block in /etc/lvm/lvm.conf:
+
+types = [ "device-mapper", 16 ]
+
+If /etc/lvm/lvm.conf does not exist, you can create one based on your 
+current/default config like so:
+
+lvm dumpconfig > /etc/lvm/lvm.conf
+
+(tip from Christophe Saout)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..4919bfa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,66 @@
+# Makefile
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr>
+
+BUILD = glibc
+
+#
+# Try to supply the linux kernel headers.
+#
+ifeq    ($(KRNLSRC),)
+KRNLLIB = /lib/modules/$(shell uname -r)
+ifeq    ($(shell test -r $(KRNLLIB)/source && echo 1),1)
+KRNLSRC = $(KRNLLIB)/source
+KRNLOBJ = $(KRNLLIB)/build
+else
+KRNLSRC = $(KRNLLIB)/build
+KRNLOBJ = $(KRNLLIB)/build
+endif
+endif
+export KRNLSRC
+export KRNLOBJ
+
+BUILDDIRS = libmultipath libcheckers path_priority \
+           devmap_name multipath multipathd kpartx
+ALLDIRS        = $(shell find . -type d -maxdepth 1 -mindepth 1)
+
+VERSION = $(shell basename ${PWD} | cut -d'-' -f3)
+INSTALLDIRS = devmap_name multipath multipathd kpartx path_priority
+
+all: recurse
+
+recurse:
+       @for dir in $(BUILDDIRS); do \
+       $(MAKE) -C $$dir BUILD=$(BUILD) VERSION=$(VERSION) \
+               KRNLSRC=$(KRNLSRC) KRNLOBJ=$(KRNLOBJ) || exit $?; \
+       done
+
+recurse_clean:
+       @for dir in $(ALLDIRS); do\
+       $(MAKE) -C $$dir clean || exit $?; \
+       done
+
+recurse_install:
+       @for dir in $(INSTALLDIRS); do\
+       $(MAKE) -C $$dir install || exit $?; \
+       done
+
+recurse_uninstall:
+       @for dir in $(INSTALLDIRS); do\
+       $(MAKE) -C $$dir uninstall || exit $?; \
+       done
+
+clean: recurse_clean
+       rm -f multipath-tools.spec
+       rm -rf rpms
+
+install:       recurse_install
+
+uninstall:     recurse_uninstall
+
+release:
+       sed -e "s/__VERSION__/${VERSION}/" \
+       multipath-tools.spec.in > multipath-tools.spec
+
+rpm: release
+       rpmbuild -bb multipath-tools.spec
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644 (file)
index 0000000..37a0847
--- /dev/null
@@ -0,0 +1,34 @@
+# Makefile.inc
+#
+# Copyright (C) 2004 Christophe Varoqui, <christophe.varoqui@free.fr>
+
+#
+# Allow to force some libraries to be used statically. (Uncomment one of the
+# following lines or define the values when calling make.)
+#
+# WITH_LOCAL_LIBDM     = 1
+# WITH_LOCAL_LIBSYSFS  = 1
+
+ifeq ($(TOPDIR),)
+       TOPDIR  = ..
+endif
+
+ifeq ($(strip $(BUILD)),klibc)
+       CC = klcc
+       klibcdir = /usr/lib/klibc
+       libdm    = $(klibcdir)/lib/libdevmapper.a
+       libsysfs = $(klibcdir)/lib/libsysfs.a
+endif
+
+prefix      = 
+exec_prefix = $(prefix)
+bindir      = $(exec_prefix)/sbin
+checkersdir = $(TOPDIR)/libcheckers
+multipathdir = $(TOPDIR)/libmultipath
+mandir      = /usr/share/man/man8
+
+GZIP        = /bin/gzip -9 -c
+STRIP       = strip --strip-all -R .comment -R .note
+
+CHECKERSLIB = $(checkersdir)/libcheckers
+MULTIPATHLIB = $(multipathdir)/libmultipath
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..64bf214
--- /dev/null
+++ b/README
@@ -0,0 +1,52 @@
+Dependancies :
+==============
+
+These libs have been dropped in the multipath tree :
+  
+o libdevmapper : comes with device-mapper-XXXX.tar.gz
+  See www.sistina.com
+o libsysfs : comes with sysutils or udev
+  See ftp.kernel.org/pub/linux/utils/kernel/hotplug/
+o klibc
+  See ftp.kernel.org/pub/linux/libs/klibc/
+
+External :
+
+o Linux kernel 2.6.10-rc with udm2 patchset (or greater)
+  ftp://sources.redhat.com/pub/dm/
+  
+How it works :
+==============
+
+Get a path list in sysfs.
+
+For each path, a wwid is retrieved by a callout program.
+Only White Listed HW can retrieve this info.
+
+Coalesce the paths according to pluggable policies and store
+ the result in mp array.
+
+Feed the maps to the kernel device mapper.
+
+The naming of the corresponding block device is handeld 
+by udev with the help of the devmap_name proggy. It is 
+called by the following rule in /etc/udev/udev.rules :
+
+KERNEL="dm-[0-9]*", PROGRAM="/sbin/devmap_name %M %m", \
+NAME="%k", SYMLINK="%c"
+
+Notes :
+=======
+
+o 2.4.21 version of DM does not like even segment size.
+  if you enconter pbs with this, upgrade DM.
+
+Credits :
+=========
+
+o Heavy cut'n paste from sg_utils. Thanks goes to D. 
+  Gilbert.
+o Light cut'n paste from dmsetup. Thanks Joe Thornber.
+o Greg KH for the nice sysfs API.
+o The klibc guys (Starving Linux Artists :), espacially
+  for their nice Makefiles and specfile
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..f9ee0c8
--- /dev/null
+++ b/TODO
@@ -0,0 +1,3 @@
+Things to do
+
+o activate group dm mesg fn
diff --git a/devmap_name/Makefile b/devmap_name/Makefile
new file mode 100644 (file)
index 0000000..32edfea
--- /dev/null
@@ -0,0 +1,45 @@
+# Makefile
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr>
+BUILD = glibc
+
+include ../Makefile.inc
+
+OBJS = devmap_name.o
+CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes
+
+ifeq ($(strip $(BUILD)),klibc)
+       OBJS += $(libdm)
+else
+       LDFLAGS = -ldevmapper
+endif
+
+EXEC = devmap_name
+
+all: $(BUILD)
+
+prepare:
+       rm -f core *.o *.gz
+
+glibc: prepare $(OBJS)
+       $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS)
+       $(STRIP) $(EXEC)
+       $(GZIP) $(EXEC).8 > $(EXEC).8.gz
+       
+klibc: prepare $(OBJS)
+       $(CC) -static -o $(EXEC) $(OBJS)
+       $(STRIP) $(EXEC)
+       $(GZIP) $(EXEC).8 > $(EXEC).8.gz
+
+install:
+       install -d $(DESTDIR)$(bindir)
+       install -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+       install -d $(DESTDIR)$(mandir)
+       install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
+
+uninstall:
+       rm $(DESTDIR)$(bindir)/$(EXEC)
+       rm $(DESTDIR)$(mandir)/$(EXEC).8.gz
+
+clean:
+       rm -f core *.o $(EXEC) *.gz
diff --git a/devmap_name/devmap_name.8 b/devmap_name/devmap_name.8
new file mode 100644 (file)
index 0000000..f4f03c3
--- /dev/null
@@ -0,0 +1,30 @@
+.TH DEVMAP_NAME 8 "February 2004" "" "Linux Administrator's Manual"
+.SH NAME
+devmap_name \- Query device-mapper name
+.SH SYNOPSIS
+.BI devmap_name " major minor"
+.SH DESCRIPTION
+.B devmap_name
+queries the device-mapper for the name for the device
+specified by
+.I major
+and
+.I minor
+number.
+.br
+.B devmap_name
+can be called from
+.B udev
+by the following rule in
+.IR /etc/udev/udev.rules :
+.sp
+.nf
+KERNEL="dm-[0-9]*", PROGRAM="/sbin/devmap_name %M %m", \\
+       NAME="%k", SYMLINK="%c"
+.fi
+.SH "SEE ALSO"
+.BR udev (8),
+.BR dmsetup (8)
+.SH AUTHORS
+.B devmap_name
+was developed by Christophe Varoqui, <christophe.varoqui@free.fr> and others.
diff --git a/devmap_name/devmap_name.c b/devmap_name/devmap_name.c
new file mode 100644 (file)
index 0000000..6a2124e
--- /dev/null
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <linux/kdev_t.h>
+#include <libdevmapper.h>
+
+static void usage(char * progname) {
+       fprintf(stderr, "usage : %s [-t target type] dev_t\n", progname);
+       fprintf(stderr, "where dev_t is either 'major minor' or 'major:minor'\n");
+       exit(1);
+}
+
+int dm_target_type(int major, int minor, char *type)
+{
+       struct dm_task *dmt;
+       void *next = NULL;
+       uint64_t start, length;
+       char *target_type = NULL;
+       char *params;
+       int r = 1;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
+               return 1;
+
+       if (!dm_task_set_major(dmt, major) ||
+           !dm_task_set_minor(dmt, minor))
+               goto bad;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto bad;
+
+       if (!type)
+               goto good;
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &target_type, &params);
+               if (target_type && strcmp(target_type, type))
+                       goto bad;
+       } while (next);
+
+good:
+       printf("%s\n", dm_task_get_name(dmt));
+       r = 0;
+bad:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+int main(int argc, char **argv)
+{
+       int c;
+       int major, minor;
+       char *target_type = NULL;
+
+       while ((c = getopt(argc, argv, "t:")) != -1) {
+               switch (c) {
+               case 't':
+                       target_type = optarg;
+                       break;
+               default:
+                       usage(argv[0]);
+                       return 1;
+                       break;
+               }
+       }
+
+       /* sanity check */
+       if (optind == argc - 2) {
+               major = atoi(argv[argc - 2]);
+               minor = atoi(argv[argc - 1]);
+       } else if (optind != argc - 1 ||
+                  2 != sscanf(argv[argc - 1], "%i:%i", &major, &minor))
+               usage(argv[0]);
+
+       if (dm_target_type(major, minor, target_type))
+               return 1;
+                                                                                
+       return 0;
+}
+
diff --git a/kpartx/ChangeLog b/kpartx/ChangeLog
new file mode 100644 (file)
index 0000000..cd0c6c1
--- /dev/null
@@ -0,0 +1,9 @@
+002:
+* convert to kpartx name everywhere
+* remove all HDGEO ioctl code
+* now work with files by mapping loops on the fly
+* merged and massage lopart.[ch] from lomount.[ch]
+  (due credit to original author here : hpa ?)
+* added a fn find_loop_by_file in lopart.[ch]
+001:
+* Initial release
diff --git a/kpartx/Makefile b/kpartx/Makefile
new file mode 100644 (file)
index 0000000..44f327e
--- /dev/null
@@ -0,0 +1,52 @@
+# Makefile
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr>
+#
+BUILD=glibc
+
+include ../Makefile.inc
+
+CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes -I.
+
+ifeq ($(strip $(BUILD)),klibc)
+       OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o gpt.o crc32.o \
+              lopart.o xstrncpy.o devmapper.o \
+              $(MULTIPATHLIB)-$(BUILD).a $(libdm)
+else
+       LDFLAGS = -ldevmapper
+       OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o \
+              gpt.o crc32.o lopart.o xstrncpy.o devmapper.o
+endif
+
+EXEC = kpartx
+
+all: $(BUILD)
+
+prepare:
+       rm -f core *.o *.gz
+
+glibc: prepare $(OBJS)
+       $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS)
+       $(STRIP) $(EXEC)
+       $(GZIP) $(EXEC).8 > $(EXEC).8.gz
+       
+klibc: prepare $(OBJS)
+       $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC)
+       $(STRIP) $(EXEC)
+       $(GZIP) $(EXEC).8 > $(EXEC).8.gz
+
+$(MULTIPATHLIB)-$(BUILD).a:
+       make -C $(multipathdir) BUILD=$(BUILD)
+
+install:
+       install -d $(DESTDIR)$(bindir)
+       install -m 755 $(EXEC) $(DESTDIR)$(bindir)
+       install -d $(DESTDIR)$(mandir)
+       install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
+
+uninstall:
+       rm -f $(DESTDIR)$(bindir)/$(EXEC)
+       rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz
+
+clean:
+       rm -f core *.o $(EXEC) *.gz
diff --git a/kpartx/README b/kpartx/README
new file mode 100644 (file)
index 0000000..e0680b1
--- /dev/null
@@ -0,0 +1,9 @@
+This version of partx is intented to be build 
+static against klibc.
+
+It creates partitions as device maps.
+
+With due respect to the original authors,
+
+have fun,
+cvaroqui
diff --git a/kpartx/bsd.c b/kpartx/bsd.c
new file mode 100644 (file)
index 0000000..3ae2dc4
--- /dev/null
@@ -0,0 +1,83 @@
+#include "kpartx.h"
+#include <stdio.h>
+
+#define BSD_DISKMAGIC  (0x82564557UL)  /* The disk magic number */
+#define XBSD_MAXPARTITIONS     16
+#define BSD_FS_UNUSED          0
+
+struct bsd_disklabel {
+       unsigned int    d_magic;        /* the magic number */
+       short int       d_type;         /* drive type */
+       short int       d_subtype;      /* controller/d_type specific */
+       char    d_typename[16];         /* type name, e.g. "eagle" */
+       char    d_packname[16];         /* pack identifier */ 
+       unsigned int    d_secsize;      /* # of bytes per sector */
+       unsigned int    d_nsectors;     /* # of data sectors per track */
+       unsigned int    d_ntracks;      /* # of tracks per cylinder */
+       unsigned int    d_ncylinders;   /* # of data cylinders per unit */
+       unsigned int    d_secpercyl;    /* # of data sectors per cylinder */
+       unsigned int    d_secperunit;   /* # of data sectors per unit */
+       unsigned short  d_sparespertrack;/* # of spare sectors per track */
+       unsigned short  d_sparespercyl; /* # of spare sectors per cylinder */
+       unsigned int    d_acylinders;   /* # of alt. cylinders per unit */
+       unsigned short  d_rpm;          /* rotational speed */
+       unsigned short  d_interleave;   /* hardware sector interleave */
+       unsigned short  d_trackskew;    /* sector 0 skew, per track */
+       unsigned short  d_cylskew;      /* sector 0 skew, per cylinder */
+       unsigned int    d_headswitch;   /* head switch time, usec */
+       unsigned int    d_trkseek;      /* track-to-track seek, usec */
+       unsigned int    d_flags;        /* generic flags */
+       unsigned int    d_drivedata[5]; /* drive-type specific information */
+       unsigned int    d_spare[5];     /* reserved for future use */
+       unsigned int    d_magic2;       /* the magic number (again) */
+       unsigned short  d_checksum;     /* xor of data incl. partitions */
+
+                       /* filesystem and partition information: */
+       unsigned short  d_npartitions;  /* number of partitions in following */
+       unsigned int    d_bbsize;       /* size of boot area at sn0, bytes */
+       unsigned int    d_sbsize;       /* max size of fs superblock, bytes */
+       struct  bsd_partition {         /* the partition table */
+               unsigned int    p_size;   /* number of sectors in partition */
+               unsigned int    p_offset; /* starting sector */
+               unsigned int    p_fsize;  /* filesystem basic fragment size */
+               unsigned char   p_fstype; /* filesystem type, see below */
+               unsigned char   p_frag;   /* filesystem fragments per block */
+               unsigned short  p_cpg;    /* filesystem cylinders per group */
+       } d_partitions[XBSD_MAXPARTITIONS];/* actually may be more */
+};
+
+int
+read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) {
+       struct bsd_disklabel *l;
+       struct bsd_partition *p;
+       unsigned int offset = all.start;
+       int max_partitions;
+       char *bp;
+       int n = 0;
+
+       bp = getblock(fd, offset+1);    /* 1 sector suffices */
+       if (bp == NULL)
+               return -1;
+
+       l = (struct bsd_disklabel *) bp;
+       if (l->d_magic != BSD_DISKMAGIC)
+               return -1;
+
+       max_partitions = 16;
+       if (l->d_npartitions < max_partitions)
+               max_partitions = l->d_npartitions;
+       for (p = l->d_partitions; p - l->d_partitions <  max_partitions; p++) {
+               if (p->p_fstype == BSD_FS_UNUSED)
+                       /* nothing */;
+               else if (n < ns) {
+                       sp[n].start = p->p_offset;
+                       sp[n].size = p->p_size;
+                       n++;
+               } else {
+                       fprintf(stderr,
+                               "bsd_partition: too many slices\n");
+                       break;
+               }
+       }
+       return n;
+}
diff --git a/kpartx/byteorder.h b/kpartx/byteorder.h
new file mode 100644 (file)
index 0000000..0f6ade1
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef BYTEORDER_H_INCLUDED
+#define BYTEORDER_H_INCLUDED
+
+#if defined (__s390__) || defined (__s390x__)
+#define le32_to_cpu(x) ( \
+               (*(((unsigned char *) &(x)))) + \
+               (*(((unsigned char *) &(x))+1) << 8) + \
+               (*(((unsigned char *) &(x))+2) << 16) + \
+               (*(((unsigned char *) &(x))+3) << 24) \
+       )
+#else
+#define le32_to_cpu(x) (x)
+#endif
+
+#endif                         /* BYTEORDER_H_INCLUDED */
diff --git a/kpartx/crc32.c b/kpartx/crc32.c
new file mode 100644 (file)
index 0000000..42d803d
--- /dev/null
@@ -0,0 +1,393 @@
+/* 
+ * crc32.c
+ * This code is in the public domain; copyright abandoned.
+ * Liability for non-performance of this code is limited to the amount
+ * you paid for it.  Since it is distributed for free, your refund will
+ * be very very small.  If it breaks, you get to keep both pieces.
+ */
+
+#include "crc32.h"
+
+#if __GNUC__ >= 3      /* 2.x has "attribute", but only 3.0 has "pure */
+#define attribute(x) __attribute__(x)
+#else
+#define attribute(x)
+#endif
+
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/* How many bits at a time to use.  Requires a table of 4<<CRC_xx_BITS bytes. */
+/* For less performance-sensitive, use 4 */
+#define CRC_LE_BITS 8
+#define CRC_BE_BITS 8
+
+/*
+ * Little-endian CRC computation.  Used with serial bit streams sent
+ * lsbit-first.  Be sure to use cpu_to_le32() to append the computed CRC.
+ */
+#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
+# error CRC_LE_BITS must be a power of 2 between 1 and 8
+#endif
+
+#if CRC_LE_BITS == 1
+/*
+ * In fact, the table-based code will work in this case, but it can be
+ * simplified by inlining the table in ?: form.
+ */
+#define crc32init_le()
+#define crc32cleanup_le()
+/**
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len)
+{
+       int i;
+       while (len--) {
+               crc ^= *p++;
+               for (i = 0; i < 8; i++)
+                       crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+       }
+       return crc;
+}
+#else                          /* Table-based approach */
+
+static uint32_t *crc32table_le;
+/**
+ * crc32init_le() - allocate and initialize LE table data
+ *
+ * crc is the crc of the byte i; other entries are filled in based on the
+ * fact that crctable[i^j] = crctable[i] ^ crctable[j].
+ *
+ */
+static int
+crc32init_le(void)
+{
+       unsigned i, j;
+       uint32_t crc = 1;
+
+       crc32table_le =
+               malloc((1 << CRC_LE_BITS) * sizeof(uint32_t));
+       if (!crc32table_le)
+               return 1;
+       crc32table_le[0] = 0;
+
+       for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) {
+               crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+               for (j = 0; j < 1 << CRC_LE_BITS; j += 2 * i)
+                       crc32table_le[i + j] = crc ^ crc32table_le[j];
+       }
+       return 0;
+}
+
+/**
+ * crc32cleanup_le(): free LE table data
+ */
+static void
+crc32cleanup_le(void)
+{
+       if (crc32table_le) free(crc32table_le);
+       crc32table_le = NULL;
+}
+
+/**
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len)
+{
+       while (len--) {
+# if CRC_LE_BITS == 8
+               crc = (crc >> 8) ^ crc32table_le[(crc ^ *p++) & 255];
+# elif CRC_LE_BITS == 4
+               crc ^= *p++;
+               crc = (crc >> 4) ^ crc32table_le[crc & 15];
+               crc = (crc >> 4) ^ crc32table_le[crc & 15];
+# elif CRC_LE_BITS == 2
+               crc ^= *p++;
+               crc = (crc >> 2) ^ crc32table_le[crc & 3];
+               crc = (crc >> 2) ^ crc32table_le[crc & 3];
+               crc = (crc >> 2) ^ crc32table_le[crc & 3];
+               crc = (crc >> 2) ^ crc32table_le[crc & 3];
+# endif
+       }
+       return crc;
+}
+#endif
+
+/*
+ * Big-endian CRC computation.  Used with serial bit streams sent
+ * msbit-first.  Be sure to use cpu_to_be32() to append the computed CRC.
+ */
+#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
+# error CRC_BE_BITS must be a power of 2 between 1 and 8
+#endif
+
+#if CRC_BE_BITS == 1
+/*
+ * In fact, the table-based code will work in this case, but it can be
+ * simplified by inlining the table in ?: form.
+ */
+#define crc32init_be()
+#define crc32cleanup_be()
+
+/**
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len)
+{
+       int i;
+       while (len--) {
+               crc ^= *p++ << 24;
+               for (i = 0; i < 8; i++)
+                       crc =
+                           (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE :
+                                         0);
+       }
+       return crc;
+}
+
+#else                          /* Table-based approach */
+static uint32_t *crc32table_be;
+
+/**
+ * crc32init_be() - allocate and initialize BE table data
+ */
+static int
+crc32init_be(void)
+{
+       unsigned i, j;
+       uint32_t crc = 0x80000000;
+
+       crc32table_be =
+               malloc((1 << CRC_BE_BITS) * sizeof(uint32_t));
+       if (!crc32table_be)
+               return 1;
+       crc32table_be[0] = 0;
+
+       for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
+               crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+               for (j = 0; j < i; j++)
+                       crc32table_be[i + j] = crc ^ crc32table_be[j];
+       }
+       return 0;
+}
+
+/**
+ * crc32cleanup_be(): free BE table data
+ */
+static void
+crc32cleanup_be(void)
+{
+       if (crc32table_be) free(crc32table_be);
+       crc32table_be = NULL;
+}
+
+
+/**
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len)
+{
+       while (len--) {
+# if CRC_BE_BITS == 8
+               crc = (crc << 8) ^ crc32table_be[(crc >> 24) ^ *p++];
+# elif CRC_BE_BITS == 4
+               crc ^= *p++ << 24;
+               crc = (crc << 4) ^ crc32table_be[crc >> 28];
+               crc = (crc << 4) ^ crc32table_be[crc >> 28];
+# elif CRC_BE_BITS == 2
+               crc ^= *p++ << 24;
+               crc = (crc << 2) ^ crc32table_be[crc >> 30];
+               crc = (crc << 2) ^ crc32table_be[crc >> 30];
+               crc = (crc << 2) ^ crc32table_be[crc >> 30];
+               crc = (crc << 2) ^ crc32table_be[crc >> 30];
+# endif
+       }
+       return crc;
+}
+#endif
+
+/*
+ * A brief CRC tutorial.
+ *
+ * A CRC is a long-division remainder.  You add the CRC to the message,
+ * and the whole thing (message+CRC) is a multiple of the given
+ * CRC polynomial.  To check the CRC, you can either check that the
+ * CRC matches the recomputed value, *or* you can check that the
+ * remainder computed on the message+CRC is 0.  This latter approach
+ * is used by a lot of hardware implementations, and is why so many
+ * protocols put the end-of-frame flag after the CRC.
+ *
+ * It's actually the same long division you learned in school, except that
+ * - We're working in binary, so the digits are only 0 and 1, and
+ * - When dividing polynomials, there are no carries.  Rather than add and
+ *   subtract, we just xor.  Thus, we tend to get a bit sloppy about
+ *   the difference between adding and subtracting.
+ *
+ * A 32-bit CRC polynomial is actually 33 bits long.  But since it's
+ * 33 bits long, bit 32 is always going to be set, so usually the CRC
+ * is written in hex with the most significant bit omitted.  (If you're
+ * familiar with the IEEE 754 floating-point format, it's the same idea.)
+ *
+ * Note that a CRC is computed over a string of *bits*, so you have
+ * to decide on the endianness of the bits within each byte.  To get
+ * the best error-detecting properties, this should correspond to the
+ * order they're actually sent.  For example, standard RS-232 serial is
+ * little-endian; the most significant bit (sometimes used for parity)
+ * is sent last.  And when appending a CRC word to a message, you should
+ * do it in the right order, matching the endianness.
+ *
+ * Just like with ordinary division, the remainder is always smaller than
+ * the divisor (the CRC polynomial) you're dividing by.  Each step of the
+ * division, you take one more digit (bit) of the dividend and append it
+ * to the current remainder.  Then you figure out the appropriate multiple
+ * of the divisor to subtract to being the remainder back into range.
+ * In binary, it's easy - it has to be either 0 or 1, and to make the
+ * XOR cancel, it's just a copy of bit 32 of the remainder.
+ *
+ * When computing a CRC, we don't care about the quotient, so we can
+ * throw the quotient bit away, but subtract the appropriate multiple of
+ * the polynomial from the remainder and we're back to where we started,
+ * ready to process the next bit.
+ *
+ * A big-endian CRC written this way would be coded like:
+ * for (i = 0; i < input_bits; i++) {
+ *     multiple = remainder & 0x80000000 ? CRCPOLY : 0;
+ *     remainder = (remainder << 1 | next_input_bit()) ^ multiple;
+ * }
+ * Notice how, to get at bit 32 of the shifted remainder, we look
+ * at bit 31 of the remainder *before* shifting it.
+ *
+ * But also notice how the next_input_bit() bits we're shifting into
+ * the remainder don't actually affect any decision-making until
+ * 32 bits later.  Thus, the first 32 cycles of this are pretty boring.
+ * Also, to add the CRC to a message, we need a 32-bit-long hole for it at
+ * the end, so we have to add 32 extra cycles shifting in zeros at the
+ * end of every message,
+ *
+ * So the standard trick is to rearrage merging in the next_input_bit()
+ * until the moment it's needed.  Then the first 32 cycles can be precomputed,
+ * and merging in the final 32 zero bits to make room for the CRC can be
+ * skipped entirely.
+ * This changes the code to:
+ * for (i = 0; i < input_bits; i++) {
+ *      remainder ^= next_input_bit() << 31;
+ *     multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
+ *     remainder = (remainder << 1) ^ multiple;
+ * }
+ * With this optimization, the little-endian code is simpler:
+ * for (i = 0; i < input_bits; i++) {
+ *      remainder ^= next_input_bit();
+ *     multiple = (remainder & 1) ? CRCPOLY : 0;
+ *     remainder = (remainder >> 1) ^ multiple;
+ * }
+ *
+ * Note that the other details of endianness have been hidden in CRCPOLY
+ * (which must be bit-reversed) and next_input_bit().
+ *
+ * However, as long as next_input_bit is returning the bits in a sensible
+ * order, we can actually do the merging 8 or more bits at a time rather
+ * than one bit at a time:
+ * for (i = 0; i < input_bytes; i++) {
+ *     remainder ^= next_input_byte() << 24;
+ *     for (j = 0; j < 8; j++) {
+ *             multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
+ *             remainder = (remainder << 1) ^ multiple;
+ *     }
+ * }
+ * Or in little-endian:
+ * for (i = 0; i < input_bytes; i++) {
+ *     remainder ^= next_input_byte();
+ *     for (j = 0; j < 8; j++) {
+ *             multiple = (remainder & 1) ? CRCPOLY : 0;
+ *             remainder = (remainder << 1) ^ multiple;
+ *     }
+ * }
+ * If the input is a multiple of 32 bits, you can even XOR in a 32-bit
+ * word at a time and increase the inner loop count to 32.
+ *
+ * You can also mix and match the two loop styles, for example doing the
+ * bulk of a message byte-at-a-time and adding bit-at-a-time processing
+ * for any fractional bytes at the end.
+ *
+ * The only remaining optimization is to the byte-at-a-time table method.
+ * Here, rather than just shifting one bit of the remainder to decide
+ * in the correct multiple to subtract, we can shift a byte at a time.
+ * This produces a 40-bit (rather than a 33-bit) intermediate remainder,
+ * but again the multiple of the polynomial to subtract depends only on
+ * the high bits, the high 8 bits in this case.  
+ *
+ * The multile we need in that case is the low 32 bits of a 40-bit
+ * value whose high 8 bits are given, and which is a multiple of the
+ * generator polynomial.  This is simply the CRC-32 of the given
+ * one-byte message.
+ *
+ * Two more details: normally, appending zero bits to a message which
+ * is already a multiple of a polynomial produces a larger multiple of that
+ * polynomial.  To enable a CRC to detect this condition, it's common to
+ * invert the CRC before appending it.  This makes the remainder of the
+ * message+crc come out not as zero, but some fixed non-zero value.
+ *
+ * The same problem applies to zero bits prepended to the message, and
+ * a similar solution is used.  Instead of starting with a remainder of
+ * 0, an initial remainder of all ones is used.  As long as you start
+ * the same way on decoding, it doesn't make a difference.
+ */
+
+
+/**
+ * init_crc32(): generates CRC32 tables
+ * 
+ * On successful initialization, use count is increased.
+ * This guarantees that the library functions will stay resident
+ * in memory, and prevents someone from 'rmmod crc32' while
+ * a driver that needs it is still loaded.
+ * This also greatly simplifies drivers, as there's no need
+ * to call an initialization/cleanup function from each driver.
+ * Since crc32.o is a library module, there's no requirement
+ * that the user can unload it.
+ */
+int
+init_crc32(void)
+{
+       int rc1, rc2, rc;
+       rc1 = crc32init_le();
+       rc2 = crc32init_be();
+       rc = rc1 || rc2;
+       return rc;
+}
+
+/**
+ * cleanup_crc32(): frees crc32 data when no longer needed
+ */
+void
+cleanup_crc32(void)
+{
+       crc32cleanup_le();
+       crc32cleanup_be();
+}
diff --git a/kpartx/crc32.h b/kpartx/crc32.h
new file mode 100644 (file)
index 0000000..a4505b8
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * crc32.h
+ */
+#ifndef _CRC32_H
+#define _CRC32_H
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+extern int init_crc32(void);
+extern void cleanup_crc32(void);
+extern uint32_t  crc32_le(uint32_t crc, unsigned char const *p, size_t len);
+extern uint32_t  crc32_be(uint32_t crc, unsigned char const *p, size_t len);
+
+#define crc32(seed, data, length)  crc32_le(seed, (unsigned char const *)data, length)
+#define ether_crc_le(length, data) crc32_le(~0, data, length)
+#define ether_crc(length, data)    crc32_be(~0, data, length)
+
+#endif /* _CRC32_H */
diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c
new file mode 100644 (file)
index 0000000..9db8b93
--- /dev/null
@@ -0,0 +1,140 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libdevmapper.h>
+#include <ctype.h>
+#include <linux/kdev_t.h>
+
+extern int
+dm_prereq (char * str, int x, int y, int z)
+{
+       int r = 1;
+       struct dm_task *dmt;
+       struct dm_versions *target;
+       struct dm_versions *last_target;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
+               return 1;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       target = dm_task_get_versions(dmt);
+
+       /* Fetch targets and print 'em */
+       do {
+               last_target = target;
+
+               if (!strncmp(str, target->name, strlen(str)) &&
+                   /* dummy prereq on multipath version */
+                   target->version[0] >= x &&
+                   target->version[1] >= y &&
+                   target->version[2] >= z
+                  )
+                       r = 0;
+
+               target = (void *) target + target->next;
+       } while (last_target != target);
+
+       out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+extern int
+dm_simplecmd (int task, const char *name) {
+       int r = 0;
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create(task)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, name))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       r = dm_task_run(dmt);
+
+       out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+extern int
+dm_addmap (int task, const char *name, const char *target,
+          const char *params, unsigned long size) {
+       int r = 0;
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create (task)))
+               return 0;
+
+       if (!dm_task_set_name (dmt, name))
+               goto addout;
+
+       if (!dm_task_add_target (dmt, 0, size, target, params))
+               goto addout;
+
+       dm_task_no_open_count(dmt);
+
+       r = dm_task_run (dmt);
+
+       addout:
+       dm_task_destroy (dmt);
+       return r;
+}
+
+extern int
+dm_map_present (char * str)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_info info;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, str))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info))
+               goto out;
+
+       if (info.exists)
+               r = 1;
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+
+const char *
+dm_mapname(int major, int minor)
+{
+       struct dm_task *dmt;
+       const char *mapname;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+               return NULL;
+
+       dm_task_no_open_count(dmt);
+       dm_task_set_major(dmt, major);
+       dm_task_set_minor(dmt, minor);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       mapname = strdup(dm_task_get_name(dmt));
+out:
+       dm_task_destroy(dmt);
+       return mapname;
+}
+
diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h
new file mode 100644 (file)
index 0000000..ffd243f
--- /dev/null
@@ -0,0 +1,5 @@
+int dm_prereq (char *, int, int, int);
+int dm_simplecmd (int, const char *);
+int dm_addmap (int, const char *, const char *, const char *, unsigned long);
+int dm_map_present (char *);
+const char * dm_mapname(int major, int minor);
diff --git a/kpartx/dos.c b/kpartx/dos.c
new file mode 100644 (file)
index 0000000..2f4e8a9
--- /dev/null
@@ -0,0 +1,112 @@
+#include "kpartx.h"
+#include "byteorder.h"
+#include <stdio.h>
+#include "dos.h"
+
+static int
+is_extended(int type) {
+       return (type == 5 || type == 0xf || type == 0x85);
+}
+
+static int
+read_extended_partition(int fd, struct partition *ep,
+                       struct slice *sp, int ns)
+{
+       struct partition *p;
+       unsigned long start, here;
+       unsigned char *bp;
+       int loopct = 0;
+       int moretodo = 1;
+       int i, n=0;
+
+       here = start = le32_to_cpu(ep->start_sect);
+
+       while (moretodo) {
+               moretodo = 0;
+               if (++loopct > 100)
+                       return n;
+
+               bp = getblock(fd, here);
+               if (bp == NULL)
+                       return n;
+
+               if (bp[510] != 0x55 || bp[511] != 0xaa)
+                       return n;
+
+               p = (struct partition *) (bp + 0x1be);
+
+               for (i=0; i<2; i++, p++) {
+                       if (p->nr_sects == 0 || is_extended(p->sys_type))
+                               continue;
+                       if (n < ns) {
+                               sp[n].start = here + le32_to_cpu(p->start_sect);
+                               sp[n].size = le32_to_cpu(p->nr_sects);
+                               n++;
+                       } else {
+                               fprintf(stderr,
+                                   "dos_extd_partition: too many slices\n");
+                               return n;
+                       }
+                       loopct = 0;
+               }
+
+               p -= 2;
+               for (i=0; i<2; i++, p++) {
+                       if(p->nr_sects != 0 && is_extended(p->sys_type)) {
+                               here = start + le32_to_cpu(p->start_sect);
+                               moretodo = 1;
+                               break;
+                       }
+               }
+       }
+       return n;
+}
+
+static int
+is_gpt(int type) {
+       return (type == 0xEE);
+}
+
+int
+read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
+       struct partition *p;
+       unsigned long offset = all.start;
+       int i, n=0;
+       unsigned char *bp;
+
+       bp = getblock(fd, offset);
+       if (bp == NULL)
+               return -1;
+
+       if (bp[510] != 0x55 || bp[511] != 0xaa)
+               return -1;
+
+       p = (struct partition *) (bp + 0x1be);
+       for (i=0; i<4; i++) {
+               if (is_gpt(p->sys_type)) {
+                       return 0;
+               }
+               p++;
+       }
+       p = (struct partition *) (bp + 0x1be);
+       for (i=0; i<4; i++) {
+               /* always add, even if zero length */
+               if (n < ns) {
+                       sp[n].start =  le32_to_cpu(p->start_sect);
+                       sp[n].size = le32_to_cpu(p->nr_sects);
+                       n++;
+               } else {
+                       fprintf(stderr,
+                               "dos_partition: too many slices\n");
+                       break;
+               }
+               p++;
+       }
+       p = (struct partition *) (bp + 0x1be);
+       for (i=0; i<4; i++) {
+               if (is_extended(p->sys_type))
+                       n += read_extended_partition(fd, p, sp+n, ns-n);
+               p++;
+       }
+       return n;
+}
diff --git a/kpartx/dos.h b/kpartx/dos.h
new file mode 100644 (file)
index 0000000..f45e7f6
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef DOS_H_INCLUDED
+#define DOS_H_INCLUDED
+
+struct partition {
+       unsigned char boot_ind; /* 0x80 - active */
+       unsigned char bh, bs, bc;
+       unsigned char sys_type;
+       unsigned char eh, es, ec;
+       unsigned int start_sect;
+       unsigned int nr_sects;
+} __attribute__((packed));
+
+#endif                         /* DOS_H_INCLUDED */
diff --git a/kpartx/efi.h b/kpartx/efi.h
new file mode 100644 (file)
index 0000000..1cbd961
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+  efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars
+  Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.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 will 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
+ */
+
+#ifndef EFI_H
+#define EFI_H
+
+/*
+ * Extensible Firmware Interface
+ * Based on 'Extensible Firmware Interface Specification'
+ *      version 1.02, 12 December, 2000
+ */
+#include <stdint.h>
+#include <string.h>
+
+typedef struct {
+       uint8_t  b[16];
+} efi_guid_t;
+
+#define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \
+((efi_guid_t) \
+{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+  (b) & 0xff, ((b) >> 8) & 0xff, \
+  (c) & 0xff, ((c) >> 8) & 0xff, \
+  (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
+
+
+/******************************************************
+ * GUIDs
+ ******************************************************/
+#define NULL_GUID \
+EFI_GUID( 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+
+static inline int
+efi_guidcmp(efi_guid_t left, efi_guid_t right)
+{
+       return memcmp(&left, &right, sizeof (efi_guid_t));
+}
+
+typedef uint16_t efi_char16_t;         /* UNICODE character */
+
+#endif /* EFI_H */
diff --git a/kpartx/gpt.c b/kpartx/gpt.c
new file mode 100644 (file)
index 0000000..dc846ca
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+    gpt.[ch]
+
+    Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com> 
+
+    EFI GUID Partition Table handling
+    Per Intel EFI Specification v1.02
+    http://developer.intel.com/technology/efi/efi.htm
+
+    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 will 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
+*/
+
+#define _FILE_OFFSET_BITS 64
+
+#include "gpt.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <endian.h>
+#include <byteswap.h>
+#include "crc32.h"
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#  define __le16_to_cpu(x) (x)
+#  define __le32_to_cpu(x) (x)
+#  define __le64_to_cpu(x) (x)
+#  define __cpu_to_le32(x) (x)
+#elif BYTE_ORDER == BIG_ENDIAN
+#  define __le16_to_cpu(x) bswap_16(x)
+#  define __le32_to_cpu(x) bswap_32(x)
+#  define __le64_to_cpu(x) bswap_64(x)
+#  define __cpu_to_le32(x) bswap_32(x)
+#endif
+
+#define BLKGETLASTSECT  _IO(0x12,108)   /* get last sector of block device */
+#define BLKGETSIZE _IO(0x12,96)                /* return device size */
+#define BLKSSZGET  _IO(0x12,104)       /* get block device sector size */
+#define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t))   /* return device size in bytes (u64 *arg) */
+
+struct blkdev_ioctl_param {
+        unsigned int block;
+        size_t content_length;
+        char * block_contents;
+};
+
+/**
+ * efi_crc32() - EFI version of crc32 function
+ * @buf: buffer to calculate crc32 of
+ * @len - length of buf
+ *
+ * Description: Returns EFI-style CRC32 value for @buf
+ * 
+ * This function uses the little endian Ethernet polynomial
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ * Note, the EFI Specification, v1.02, has a reference to
+ * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
+ */
+static inline uint32_t
+efi_crc32(const void *buf, unsigned long len)
+{
+       return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+/**
+ * is_pmbr_valid(): test Protective MBR for validity
+ * @mbr: pointer to a legacy mbr structure
+ *
+ * Description: Returns 1 if PMBR is valid, 0 otherwise.
+ * Validity depends on two things:
+ *  1) MSDOS signature is in the last two bytes of the MBR
+ *  2) One partition of type 0xEE is found
+ */
+static int
+is_pmbr_valid(legacy_mbr *mbr)
+{
+       int i, found = 0, signature = 0;
+       if (!mbr)
+               return 0;
+       signature = (__le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE);
+       for (i = 0; signature && i < 4; i++) {
+               if (mbr->partition[i].sys_type ==
+                    EFI_PMBR_OSTYPE_EFI_GPT) {
+                       found = 1;
+                       break;
+               }
+       }
+       return (signature && found);
+}
+
+
+/************************************************************
+ * get_sector_size
+ * Requires:
+ *  - filedes is an open file descriptor, suitable for reading
+ * Modifies: nothing
+ * Returns:
+ *  sector size, or 512.
+ ************************************************************/
+static int
+get_sector_size(int filedes)
+{
+       int rc, sector_size = 512;
+
+       rc = ioctl(filedes, BLKSSZGET, &sector_size);
+       if (rc)
+               sector_size = 512;
+       return sector_size;
+}
+
+/************************************************************
+ * _get_num_sectors
+ * Requires:
+ *  - filedes is an open file descriptor, suitable for reading
+ * Modifies: nothing
+ * Returns:
+ *  Last LBA value on success 
+ *  0 on error
+ *
+ * Try getting BLKGETSIZE64 and BLKSSZGET first,
+ * then BLKGETSIZE if necessary.
+ *  Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64
+ *  which returns the number of 512-byte sectors, not the size of
+ *  the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3.
+ ************************************************************/
+static uint64_t
+_get_num_sectors(int filedes)
+{
+       unsigned long sectors=0;
+       int rc;
+#if 0
+        uint64_t bytes=0;
+
+       rc = ioctl(filedes, BLKGETSIZE64, &bytes);
+       if (!rc)
+               return bytes / get_sector_size(filedes);
+#endif
+        rc = ioctl(filedes, BLKGETSIZE, &sectors);
+        if (rc)
+                return 0;
+        
+       return sectors;
+}
+
+/************************************************************
+ * last_lba(): return number of last logical block of device
+ * 
+ * @fd
+ * 
+ * Description: returns Last LBA value on success, 0 on error.
+ * Notes: The value st_blocks gives the size of the file
+ *        in 512-byte blocks, which is OK if
+ *        EFI_BLOCK_SIZE_SHIFT == 9.
+ ************************************************************/
+
+static uint64_t
+last_lba(int filedes)
+{
+       int rc;
+       uint64_t sectors = 0;
+       struct stat s;
+       memset(&s, 0, sizeof (s));
+       rc = fstat(filedes, &s);
+       if (rc == -1) {
+               fprintf(stderr, "last_lba() could not stat: %s\n",
+                       strerror(errno));
+               return 0;
+       }
+
+       if (S_ISBLK(s.st_mode)) {
+               sectors = _get_num_sectors(filedes);
+       } else {
+               fprintf(stderr,
+                       "last_lba(): I don't know how to handle files with mode %x\n",
+                       s.st_mode);
+               sectors = 1;
+       }
+
+       return sectors - 1;
+}
+
+
+static ssize_t
+read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count)
+{
+        int rc;
+        struct blkdev_ioctl_param ioctl_param;
+
+        if (!buffer) return 0; 
+
+        ioctl_param.block = 0; /* read the last sector */
+        ioctl_param.content_length = count;
+        ioctl_param.block_contents = buffer;
+
+        rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param);
+        if (rc == -1) perror("read failed");
+
+        return !rc;
+}
+
+static ssize_t
+read_lba(int fd, uint64_t lba, void *buffer, size_t bytes)
+{
+       int sector_size = get_sector_size(fd);
+       off_t offset = lba * sector_size;
+        ssize_t bytesread;
+
+       lseek(fd, offset, SEEK_SET);
+       bytesread = read(fd, buffer, bytes);
+
+        /* Kludge.  This is necessary to read/write the last
+           block of an odd-sized disk, until Linux 2.5.x kernel fixes.
+           This is only used by gpt.c, and only to read
+           one sector, so we don't have to be fancy.
+        */
+        if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd)) {
+                bytesread = read_lastoddsector(fd, lba, buffer, bytes);
+        }
+        return bytesread;
+}
+
+/**
+ * alloc_read_gpt_entries(): reads partition entries from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a buffer into which the GPT will be put  
+ * Description: Returns ptes on success,  NULL on error.
+ * Allocates space for PTEs based on information found in @gpt.
+ * Notes: remember to free pte when you're done!
+ */
+static gpt_entry *
+alloc_read_gpt_entries(int fd, gpt_header * gpt)
+{
+       gpt_entry *pte;
+        size_t count = __le32_to_cpu(gpt->num_partition_entries) *
+                __le32_to_cpu(gpt->sizeof_partition_entry);
+
+        if (!count) return NULL;
+
+       pte = (gpt_entry *)malloc(count);
+       if (!pte)
+               return NULL;
+       memset(pte, 0, count);
+
+       if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte,
+                      count)) {
+               free(pte);
+               return NULL;
+       }
+       return pte;
+}
+
+/**
+ * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the Logical Block Address of the partition table
+ * 
+ * Description: returns GPT header on success, NULL on error.   Allocates
+ * and fills a GPT header starting at @ from @bdev.
+ * Note: remember to free gpt when finished with it.
+ */
+static gpt_header *
+alloc_read_gpt_header(int fd, uint64_t lba)
+{
+       gpt_header *gpt;
+       gpt = (gpt_header *)
+           malloc(sizeof (gpt_header));
+       if (!gpt)
+               return NULL;
+       memset(gpt, 0, sizeof (*gpt));
+       if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) {
+               free(gpt);
+               return NULL;
+       }
+
+       return gpt;
+}
+
+/**
+ * is_gpt_valid() - tests one GPT header and PTEs for validity
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the logical block address of the GPT header to test
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ *
+ * Description: returns 1 if valid,  0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ */
+static int
+is_gpt_valid(int fd, uint64_t lba,
+             gpt_header ** gpt, gpt_entry ** ptes)
+{
+       int rc = 0;             /* default to not valid */
+       uint32_t crc, origcrc;
+
+       if (!gpt || !ptes)
+                return 0;
+       if (!(*gpt = alloc_read_gpt_header(fd, lba)))
+               return 0;
+
+       /* Check the GUID Partition Table signature */
+       if (__le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
+               /* 
+                  printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n",
+                  __le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE);
+                */
+               free(*gpt);
+               *gpt = NULL;
+               return rc;
+       }
+
+       /* Check the GUID Partition Table Header CRC */
+       origcrc = __le32_to_cpu((*gpt)->header_crc32);
+       (*gpt)->header_crc32 = 0;
+       crc = efi_crc32(*gpt, __le32_to_cpu((*gpt)->header_size));
+       if (crc != origcrc) {
+               // printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc);
+               (*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+               free(*gpt);
+               *gpt = NULL;
+               return 0;
+       }
+       (*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+
+       /* Check that the my_lba entry points to the LBA
+        * that contains the GPT we read */
+       if (__le64_to_cpu((*gpt)->my_lba) != lba) {
+               /*
+               printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n",
+                               __le64_to_cpu((*gpt)->my_lba), lba);
+                */
+               free(*gpt);
+               *gpt = NULL;
+               return 0;
+       }
+
+       if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) {
+               free(*gpt);
+               *gpt = NULL;
+               return 0;
+       }
+
+       /* Check the GUID Partition Entry Array CRC */
+       crc = efi_crc32(*ptes,
+                        __le32_to_cpu((*gpt)->num_partition_entries) *
+                       __le32_to_cpu((*gpt)->sizeof_partition_entry));
+       if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
+               // printf("GUID Partitition Entry Array CRC check failed.\n");
+               free(*gpt);
+               *gpt = NULL;
+               free(*ptes);
+               *ptes = NULL;
+               return 0;
+       }
+
+       /* We're done, all's well */
+       return 1;
+}
+/**
+ * compare_gpts() - Search disk for valid GPT headers and PTEs
+ * @pgpt is the primary GPT header
+ * @agpt is the alternate GPT header
+ * @lastlba is the last LBA number
+ * Description: Returns nothing.  Sanity checks pgpt and agpt fields
+ * and prints warnings on discrepancies.
+ * 
+ */
+static void
+compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba)
+{
+       int error_found = 0;
+       if (!pgpt || !agpt)
+               return;
+       if (__le64_to_cpu(pgpt->my_lba) != __le64_to_cpu(agpt->alternate_lba)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Primary header LBA != Alt. header alternate_lba\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->my_lba),
+                       __le64_to_cpu(agpt->alternate_lba));
+#endif
+       }
+       if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Primary header alternate_lba != Alt. header my_lba\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->alternate_lba),
+                       __le64_to_cpu(agpt->my_lba));
+#endif
+       }
+       if (__le64_to_cpu(pgpt->first_usable_lba) !=
+            __le64_to_cpu(agpt->first_usable_lba)) {
+               error_found++;
+               fprintf(stderr,  "GPT:first_usable_lbas don't match.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->first_usable_lba),
+                       __le64_to_cpu(agpt->first_usable_lba));
+#endif
+       }
+       if (__le64_to_cpu(pgpt->last_usable_lba) !=
+            __le64_to_cpu(agpt->last_usable_lba)) {
+               error_found++;
+               fprintf(stderr,  "GPT:last_usable_lbas don't match.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->last_usable_lba),
+                       __le64_to_cpu(agpt->last_usable_lba));
+#endif
+       }
+       if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
+               error_found++;
+               fprintf(stderr,  "GPT:disk_guids don't match.\n");
+       }
+       if (__le32_to_cpu(pgpt->num_partition_entries) !=
+            __le32_to_cpu(agpt->num_partition_entries)) {
+               error_found++;
+               fprintf(stderr,  "GPT:num_partition_entries don't match: "
+                      "0x%x != 0x%x\n",
+                      __le32_to_cpu(pgpt->num_partition_entries),
+                      __le32_to_cpu(agpt->num_partition_entries));
+       }
+       if (__le32_to_cpu(pgpt->sizeof_partition_entry) !=
+            __le32_to_cpu(agpt->sizeof_partition_entry)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:sizeof_partition_entry values don't match: "
+                      "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->sizeof_partition_entry),
+                      __le32_to_cpu(agpt->sizeof_partition_entry));
+       }
+       if (__le32_to_cpu(pgpt->partition_entry_array_crc32) !=
+            __le32_to_cpu(agpt->partition_entry_array_crc32)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:partition_entry_array_crc32 values don't match: "
+                      "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->partition_entry_array_crc32),
+                      __le32_to_cpu(agpt->partition_entry_array_crc32));
+       }
+       if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->alternate_lba), lastlba);
+#endif
+       }
+
+       if (__le64_to_cpu(agpt->my_lba) != lastlba) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Alternate GPT header not at the end of the disk.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(agpt->my_lba), lastlba);
+#endif
+       }
+
+       if (error_found)
+               fprintf(stderr, 
+                      "GPT: Use GNU Parted to correct GPT errors.\n");
+       return;
+}
+
+/**
+ * find_valid_gpt() - Search disk for valid GPT headers and PTEs
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ * Description: Returns 1 if valid, 0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ * Validity depends on finding either the Primary GPT header and PTEs valid,
+ * or the Alternate GPT header and PTEs valid, and the PMBR valid.
+ */
+static int
+find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes)
+{
+        extern int force_gpt;
+       int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
+       gpt_header *pgpt = NULL, *agpt = NULL;
+       gpt_entry *pptes = NULL, *aptes = NULL;
+       legacy_mbr *legacymbr = NULL;
+       uint64_t lastlba;
+       if (!gpt || !ptes)
+               return 0;
+
+       lastlba = last_lba(fd);
+       good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA,
+                                &pgpt, &pptes);
+        if (good_pgpt) {
+               good_agpt = is_gpt_valid(fd,
+                                         __le64_to_cpu(pgpt->alternate_lba),
+                                        &agpt, &aptes);
+                if (!good_agpt) {
+                        good_agpt = is_gpt_valid(fd, lastlba,
+                                                 &agpt, &aptes);
+                }
+        }
+        else {
+                good_agpt = is_gpt_valid(fd, lastlba,
+                                         &agpt, &aptes);
+        }
+
+        /* The obviously unsuccessful case */
+        if (!good_pgpt && !good_agpt) {
+                goto fail;
+        }
+
+       /* This will be added to the EFI Spec. per Intel after v1.02. */
+        legacymbr = malloc(sizeof (*legacymbr));
+        if (legacymbr) {
+                memset(legacymbr, 0, sizeof (*legacymbr));
+                read_lba(fd, 0, (uint8_t *) legacymbr,
+                         sizeof (*legacymbr));
+                good_pmbr = is_pmbr_valid(legacymbr);
+                free(legacymbr);
+                legacymbr=NULL;
+        }
+
+        /* Failure due to bad PMBR */
+        if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) {
+                fprintf(stderr,
+                       "  Warning: Disk has a valid GPT signature "
+                       "but invalid PMBR.\n"
+                       "  Assuming this disk is *not* a GPT disk anymore.\n"
+                       "  Use gpt kernel option to override.  "
+                       "Use GNU Parted to correct disk.\n");
+                goto fail;
+        }
+
+        /* Would fail due to bad PMBR, but force GPT anyhow */
+        if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) {
+                fprintf(stderr, 
+                       "  Warning: Disk has a valid GPT signature but "
+                       "invalid PMBR.\n"
+                       "  Use GNU Parted to correct disk.\n"
+                       "  gpt option taken, disk treated as GPT.\n");
+        }
+
+        compare_gpts(pgpt, agpt, lastlba);
+
+        /* The good cases */
+        if (good_pgpt && (good_pmbr || force_gpt)) {
+                *gpt  = pgpt;
+                *ptes = pptes;
+                if (agpt)  { free(agpt);   agpt = NULL; }
+                if (aptes) { free(aptes); aptes = NULL; }
+                if (!good_agpt) {
+                        fprintf(stderr, 
+                              "Alternate GPT is invalid, "
+                               "using primary GPT.\n");
+                }
+                return 1;
+        }
+        else if (good_agpt && (good_pmbr || force_gpt)) {
+                *gpt  = agpt;
+                *ptes = aptes;
+                if (pgpt)  { free(pgpt);   pgpt = NULL; }
+                if (pptes) { free(pptes); pptes = NULL; }
+                fprintf(stderr, 
+                       "Primary GPT is invalid, using alternate GPT.\n");
+                return 1;
+        }
+
+ fail:
+        if (pgpt)  { free(pgpt);   pgpt=NULL; }
+        if (agpt)  { free(agpt);   agpt=NULL; }
+        if (pptes) { free(pptes); pptes=NULL; }
+        if (aptes) { free(aptes); aptes=NULL; }
+        *gpt = NULL;
+        *ptes = NULL;
+        return 0;
+}
+
+/**
+ * read_gpt_pt() 
+ * @fd
+ * @all - slice with start/size of whole disk
+ *
+ *  0 if this isn't our partition table
+ *  number of partitions if successful
+ *
+ */
+int
+read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns)
+{
+       gpt_header *gpt = NULL;
+       gpt_entry *ptes = NULL;
+       uint32_t i;
+       int n = 0;
+        int last_used_index=-1;
+
+       if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) {
+               if (gpt)
+                       free (gpt);
+               if (ptes)
+                       free (ptes);
+               return 0;
+       }
+
+       for (i = 0; i < __le32_to_cpu(gpt->num_partition_entries) && i < ns; i++) {
+               if (!efi_guidcmp (NULL_GUID, ptes[i].partition_type_guid)) {
+                       sp[n].start = 0;
+                       sp[n].size = 0;
+                       n++;
+               } else {
+                       sp[n].start = __le64_to_cpu(ptes[i].starting_lba);
+                       sp[n].size  = __le64_to_cpu(ptes[i].ending_lba) -
+                               __le64_to_cpu(ptes[i].starting_lba) + 1;
+                        last_used_index=n;
+                       n++;
+               }
+       }
+       free (ptes);
+       free (gpt);
+       return last_used_index+1;
+}
diff --git a/kpartx/gpt.c.orig b/kpartx/gpt.c.orig
new file mode 100644 (file)
index 0000000..d5e2dd5
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+    gpt.[ch]
+
+    Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com> 
+
+    EFI GUID Partition Table handling
+    Per Intel EFI Specification v1.02
+    http://developer.intel.com/technology/efi/efi.htm
+
+    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 will 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
+*/
+
+#define _FILE_OFFSET_BITS 64
+
+#include "gpt.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <asm/byteorder.h>
+#include "crc32.h"
+
+#define BLKGETLASTSECT  _IO(0x12,108)   /* get last sector of block device */
+#define BLKGETSIZE _IO(0x12,96)                /* return device size */
+#define BLKSSZGET  _IO(0x12,104)       /* get block device sector size */
+#define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t))   /* return device size in bytes (u64 *arg) */
+
+struct blkdev_ioctl_param {
+        unsigned int block;
+        size_t content_length;
+        char * block_contents;
+};
+
+/**
+ * efi_crc32() - EFI version of crc32 function
+ * @buf: buffer to calculate crc32 of
+ * @len - length of buf
+ *
+ * Description: Returns EFI-style CRC32 value for @buf
+ * 
+ * This function uses the little endian Ethernet polynomial
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ * Note, the EFI Specification, v1.02, has a reference to
+ * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
+ */
+static inline uint32_t
+efi_crc32(const void *buf, unsigned long len)
+{
+       return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+/**
+ * is_pmbr_valid(): test Protective MBR for validity
+ * @mbr: pointer to a legacy mbr structure
+ *
+ * Description: Returns 1 if PMBR is valid, 0 otherwise.
+ * Validity depends on two things:
+ *  1) MSDOS signature is in the last two bytes of the MBR
+ *  2) One partition of type 0xEE is found
+ */
+static int
+is_pmbr_valid(legacy_mbr *mbr)
+{
+       int i, found = 0, signature = 0;
+       if (!mbr)
+               return 0;
+       signature = (__le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE);
+       for (i = 0; signature && i < 4; i++) {
+               if (mbr->partition[i].sys_type ==
+                    EFI_PMBR_OSTYPE_EFI_GPT) {
+                       found = 1;
+                       break;
+               }
+       }
+       return (signature && found);
+}
+
+
+/************************************************************
+ * get_sector_size
+ * Requires:
+ *  - filedes is an open file descriptor, suitable for reading
+ * Modifies: nothing
+ * Returns:
+ *  sector size, or 512.
+ ************************************************************/
+static int
+get_sector_size(int filedes)
+{
+       int rc, sector_size = 512;
+
+       rc = ioctl(filedes, BLKSSZGET, &sector_size);
+       if (rc)
+               sector_size = 512;
+       return sector_size;
+}
+
+/************************************************************
+ * _get_num_sectors
+ * Requires:
+ *  - filedes is an open file descriptor, suitable for reading
+ * Modifies: nothing
+ * Returns:
+ *  Last LBA value on success 
+ *  0 on error
+ *
+ * Try getting BLKGETSIZE64 and BLKSSZGET first,
+ * then BLKGETSIZE if necessary.
+ *  Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64
+ *  which returns the number of 512-byte sectors, not the size of
+ *  the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3.
+ ************************************************************/
+static uint64_t
+_get_num_sectors(int filedes)
+{
+       unsigned long sectors=0;
+       int rc;
+#if 0
+        uint64_t bytes=0;
+
+       rc = ioctl(filedes, BLKGETSIZE64, &bytes);
+       if (!rc)
+               return bytes / get_sector_size(filedes);
+#endif
+        rc = ioctl(filedes, BLKGETSIZE, &sectors);
+        if (rc)
+                return 0;
+        
+       return sectors;
+}
+
+/************************************************************
+ * last_lba(): return number of last logical block of device
+ * 
+ * @fd
+ * 
+ * Description: returns Last LBA value on success, 0 on error.
+ * Notes: The value st_blocks gives the size of the file
+ *        in 512-byte blocks, which is OK if
+ *        EFI_BLOCK_SIZE_SHIFT == 9.
+ ************************************************************/
+
+static uint64_t
+last_lba(int filedes)
+{
+       int rc;
+       uint64_t sectors = 0;
+       struct stat s;
+       memset(&s, 0, sizeof (s));
+       rc = fstat(filedes, &s);
+       if (rc == -1) {
+               fprintf(stderr, "last_lba() could not stat: %s\n",
+                       strerror(errno));
+               return 0;
+       }
+
+       if (S_ISBLK(s.st_mode)) {
+               sectors = _get_num_sectors(filedes);
+       } else {
+               fprintf(stderr,
+                       "last_lba(): I don't know how to handle files with mode %x\n",
+                       s.st_mode);
+               sectors = 1;
+       }
+
+       return sectors - 1;
+}
+
+
+static ssize_t
+read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count)
+{
+        int rc;
+        struct blkdev_ioctl_param ioctl_param;
+
+        if (!buffer) return 0; 
+
+        ioctl_param.block = 0; /* read the last sector */
+        ioctl_param.content_length = count;
+        ioctl_param.block_contents = buffer;
+
+        rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param);
+        if (rc == -1) perror("read failed");
+
+        return !rc;
+}
+
+static ssize_t
+read_lba(int fd, uint64_t lba, void *buffer, size_t bytes)
+{
+       int sector_size = get_sector_size(fd);
+       off_t offset = lba * sector_size;
+        ssize_t bytesread;
+
+       lseek(fd, offset, SEEK_SET);
+       bytesread = read(fd, buffer, bytes);
+
+        /* Kludge.  This is necessary to read/write the last
+           block of an odd-sized disk, until Linux 2.5.x kernel fixes.
+           This is only used by gpt.c, and only to read
+           one sector, so we don't have to be fancy.
+        */
+        if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd)) {
+                bytesread = read_lastoddsector(fd, lba, buffer, bytes);
+        }
+        return bytesread;
+}
+
+/**
+ * alloc_read_gpt_entries(): reads partition entries from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a buffer into which the GPT will be put  
+ * Description: Returns ptes on success,  NULL on error.
+ * Allocates space for PTEs based on information found in @gpt.
+ * Notes: remember to free pte when you're done!
+ */
+static gpt_entry *
+alloc_read_gpt_entries(int fd, gpt_header * gpt)
+{
+       gpt_entry *pte;
+        size_t count = __le32_to_cpu(gpt->num_partition_entries) *
+                __le32_to_cpu(gpt->sizeof_partition_entry);
+
+        if (!count) return NULL;
+
+       pte = (gpt_entry *)malloc(count);
+       if (!pte)
+               return NULL;
+       memset(pte, 0, count);
+
+       if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte,
+                      count)) {
+               free(pte);
+               return NULL;
+       }
+       return pte;
+}
+
+/**
+ * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the Logical Block Address of the partition table
+ * 
+ * Description: returns GPT header on success, NULL on error.   Allocates
+ * and fills a GPT header starting at @ from @bdev.
+ * Note: remember to free gpt when finished with it.
+ */
+static gpt_header *
+alloc_read_gpt_header(int fd, uint64_t lba)
+{
+       gpt_header *gpt;
+       gpt = (gpt_header *)
+           malloc(sizeof (gpt_header));
+       if (!gpt)
+               return NULL;
+       memset(gpt, 0, sizeof (*gpt));
+       if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) {
+               free(gpt);
+               return NULL;
+       }
+
+       return gpt;
+}
+
+/**
+ * is_gpt_valid() - tests one GPT header and PTEs for validity
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the logical block address of the GPT header to test
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ *
+ * Description: returns 1 if valid,  0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ */
+static int
+is_gpt_valid(int fd, uint64_t lba,
+             gpt_header ** gpt, gpt_entry ** ptes)
+{
+       int rc = 0;             /* default to not valid */
+       uint32_t crc, origcrc;
+
+       if (!gpt || !ptes)
+                return 0;
+       if (!(*gpt = alloc_read_gpt_header(fd, lba)))
+               return 0;
+
+       /* Check the GUID Partition Table signature */
+       if (__le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
+               /* 
+                  printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n",
+                  __le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE);
+                */
+               free(*gpt);
+               *gpt = NULL;
+               return rc;
+       }
+
+       /* Check the GUID Partition Table Header CRC */
+       origcrc = __le32_to_cpu((*gpt)->header_crc32);
+       (*gpt)->header_crc32 = 0;
+       crc = efi_crc32(*gpt, __le32_to_cpu((*gpt)->header_size));
+       if (crc != origcrc) {
+               // printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc);
+               (*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+               free(*gpt);
+               *gpt = NULL;
+               return 0;
+       }
+       (*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+
+       /* Check that the my_lba entry points to the LBA
+        * that contains the GPT we read */
+       if (__le64_to_cpu((*gpt)->my_lba) != lba) {
+               /*
+               printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n",
+                               __le64_to_cpu((*gpt)->my_lba), lba);
+                */
+               free(*gpt);
+               *gpt = NULL;
+               return 0;
+       }
+
+       if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) {
+               free(*gpt);
+               *gpt = NULL;
+               return 0;
+       }
+
+       /* Check the GUID Partition Entry Array CRC */
+       crc = efi_crc32(*ptes,
+                        __le32_to_cpu((*gpt)->num_partition_entries) *
+                       __le32_to_cpu((*gpt)->sizeof_partition_entry));
+       if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
+               // printf("GUID Partitition Entry Array CRC check failed.\n");
+               free(*gpt);
+               *gpt = NULL;
+               free(*ptes);
+               *ptes = NULL;
+               return 0;
+       }
+
+       /* We're done, all's well */
+       return 1;
+}
+/**
+ * compare_gpts() - Search disk for valid GPT headers and PTEs
+ * @pgpt is the primary GPT header
+ * @agpt is the alternate GPT header
+ * @lastlba is the last LBA number
+ * Description: Returns nothing.  Sanity checks pgpt and agpt fields
+ * and prints warnings on discrepancies.
+ * 
+ */
+static void
+compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba)
+{
+       int error_found = 0;
+       if (!pgpt || !agpt)
+               return;
+       if (__le64_to_cpu(pgpt->my_lba) != __le64_to_cpu(agpt->alternate_lba)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Primary header LBA != Alt. header alternate_lba\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->my_lba),
+                       __le64_to_cpu(agpt->alternate_lba));
+#endif
+       }
+       if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Primary header alternate_lba != Alt. header my_lba\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->alternate_lba),
+                       __le64_to_cpu(agpt->my_lba));
+#endif
+       }
+       if (__le64_to_cpu(pgpt->first_usable_lba) !=
+            __le64_to_cpu(agpt->first_usable_lba)) {
+               error_found++;
+               fprintf(stderr,  "GPT:first_usable_lbas don't match.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->first_usable_lba),
+                       __le64_to_cpu(agpt->first_usable_lba));
+#endif
+       }
+       if (__le64_to_cpu(pgpt->last_usable_lba) !=
+            __le64_to_cpu(agpt->last_usable_lba)) {
+               error_found++;
+               fprintf(stderr,  "GPT:last_usable_lbas don't match.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->last_usable_lba),
+                       __le64_to_cpu(agpt->last_usable_lba));
+#endif
+       }
+       if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
+               error_found++;
+               fprintf(stderr,  "GPT:disk_guids don't match.\n");
+       }
+       if (__le32_to_cpu(pgpt->num_partition_entries) !=
+            __le32_to_cpu(agpt->num_partition_entries)) {
+               error_found++;
+               fprintf(stderr,  "GPT:num_partition_entries don't match: "
+                      "0x%x != 0x%x\n",
+                      __le32_to_cpu(pgpt->num_partition_entries),
+                      __le32_to_cpu(agpt->num_partition_entries));
+       }
+       if (__le32_to_cpu(pgpt->sizeof_partition_entry) !=
+            __le32_to_cpu(agpt->sizeof_partition_entry)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:sizeof_partition_entry values don't match: "
+                      "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->sizeof_partition_entry),
+                      __le32_to_cpu(agpt->sizeof_partition_entry));
+       }
+       if (__le32_to_cpu(pgpt->partition_entry_array_crc32) !=
+            __le32_to_cpu(agpt->partition_entry_array_crc32)) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:partition_entry_array_crc32 values don't match: "
+                      "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->partition_entry_array_crc32),
+                      __le32_to_cpu(agpt->partition_entry_array_crc32));
+       }
+       if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(pgpt->alternate_lba), lastlba);
+#endif
+       }
+
+       if (__le64_to_cpu(agpt->my_lba) != lastlba) {
+               error_found++;
+               fprintf(stderr, 
+                      "GPT:Alternate GPT header not at the end of the disk.\n");
+#ifdef DEBUG
+               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+                      __le64_to_cpu(agpt->my_lba), lastlba);
+#endif
+       }
+
+       if (error_found)
+               fprintf(stderr, 
+                      "GPT: Use GNU Parted to correct GPT errors.\n");
+       return;
+}
+
+/**
+ * find_valid_gpt() - Search disk for valid GPT headers and PTEs
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ * Description: Returns 1 if valid, 0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ * Validity depends on finding either the Primary GPT header and PTEs valid,
+ * or the Alternate GPT header and PTEs valid, and the PMBR valid.
+ */
+static int
+find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes)
+{
+        extern int force_gpt;
+       int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
+       gpt_header *pgpt = NULL, *agpt = NULL;
+       gpt_entry *pptes = NULL, *aptes = NULL;
+       legacy_mbr *legacymbr = NULL;
+       uint64_t lastlba;
+       if (!gpt || !ptes)
+               return 0;
+
+       lastlba = last_lba(fd);
+       good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA,
+                                &pgpt, &pptes);
+        if (good_pgpt) {
+               good_agpt = is_gpt_valid(fd,
+                                         __le64_to_cpu(pgpt->alternate_lba),
+                                        &agpt, &aptes);
+                if (!good_agpt) {
+                        good_agpt = is_gpt_valid(fd, lastlba,
+                                                 &agpt, &aptes);
+                }
+        }
+        else {
+                good_agpt = is_gpt_valid(fd, lastlba,
+                                         &agpt, &aptes);
+        }
+
+        /* The obviously unsuccessful case */
+        if (!good_pgpt && !good_agpt) {
+                goto fail;
+        }
+
+       /* This will be added to the EFI Spec. per Intel after v1.02. */
+        legacymbr = malloc(sizeof (*legacymbr));
+        if (legacymbr) {
+                memset(legacymbr, 0, sizeof (*legacymbr));
+                read_lba(fd, 0, (uint8_t *) legacymbr,
+                         sizeof (*legacymbr));
+                good_pmbr = is_pmbr_valid(legacymbr);
+                free(legacymbr);
+                legacymbr=NULL;
+        }
+
+        /* Failure due to bad PMBR */
+        if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) {
+                fprintf(stderr,
+                       "  Warning: Disk has a valid GPT signature "
+                       "but invalid PMBR.\n"
+                       "  Assuming this disk is *not* a GPT disk anymore.\n"
+                       "  Use gpt kernel option to override.  "
+                       "Use GNU Parted to correct disk.\n");
+                goto fail;
+        }
+
+        /* Would fail due to bad PMBR, but force GPT anyhow */
+        if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) {
+                fprintf(stderr, 
+                       "  Warning: Disk has a valid GPT signature but "
+                       "invalid PMBR.\n"
+                       "  Use GNU Parted to correct disk.\n"
+                       "  gpt option taken, disk treated as GPT.\n");
+        }
+
+        compare_gpts(pgpt, agpt, lastlba);
+
+        /* The good cases */
+        if (good_pgpt && (good_pmbr || force_gpt)) {
+                *gpt  = pgpt;
+                *ptes = pptes;
+                if (agpt)  { free(agpt);   agpt = NULL; }
+                if (aptes) { free(aptes); aptes = NULL; }
+                if (!good_agpt) {
+                        fprintf(stderr, 
+                              "Alternate GPT is invalid, "
+                               "using primary GPT.\n");
+                }
+                return 1;
+        }
+        else if (good_agpt && (good_pmbr || force_gpt)) {
+                *gpt  = agpt;
+                *ptes = aptes;
+                if (pgpt)  { free(pgpt);   pgpt = NULL; }
+                if (pptes) { free(pptes); pptes = NULL; }
+                fprintf(stderr, 
+                       "Primary GPT is invalid, using alternate GPT.\n");
+                return 1;
+        }
+
+ fail:
+        if (pgpt)  { free(pgpt);   pgpt=NULL; }
+        if (agpt)  { free(agpt);   agpt=NULL; }
+        if (pptes) { free(pptes); pptes=NULL; }
+        if (aptes) { free(aptes); aptes=NULL; }
+        *gpt = NULL;
+        *ptes = NULL;
+        return 0;
+}
+
+/**
+ * read_gpt_pt() 
+ * @fd
+ * @all - slice with start/size of whole disk
+ *
+ *  0 if this isn't our partition table
+ *  number of partitions if successful
+ *
+ */
+int
+read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns)
+{
+       gpt_header *gpt = NULL;
+       gpt_entry *ptes = NULL;
+       uint32_t i;
+       int n = 0;
+        int last_used_index=-1;
+
+       if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) {
+               if (gpt)
+                       free (gpt);
+               if (ptes)
+                       free (ptes);
+               return 0;
+       }
+
+       for (i = 0; i < __le32_to_cpu(gpt->num_partition_entries) && i < ns; i++) {
+               if (!efi_guidcmp (NULL_GUID, ptes[i].partition_type_guid)) {
+                       sp[n].start = 0;
+                       sp[n].size = 0;
+                       n++;
+               } else {
+                       sp[n].start = __le64_to_cpu(ptes[i].starting_lba);
+                       sp[n].size  = __le64_to_cpu(ptes[i].ending_lba) -
+                               __le64_to_cpu(ptes[i].starting_lba) + 1;
+                        last_used_index=n;
+                       n++;
+               }
+       }
+       free (ptes);
+       free (gpt);
+       return last_used_index+1;
+}
diff --git a/kpartx/gpt.h b/kpartx/gpt.h
new file mode 100644 (file)
index 0000000..a073b42
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+    gpt.[ch]
+
+    Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com> 
+
+    EFI GUID Partition Table handling
+    Per Intel EFI Specification v1.02
+    http://developer.intel.com/technology/efi/efi.htm
+
+    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 will 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
+*/
+
+#ifndef _GPT_H
+#define _GPT_H
+
+
+#include <inttypes.h>
+#include "kpartx.h"
+#include "dos.h"
+#include "efi.h"
+
+#define EFI_PMBR_OSTYPE_EFI 0xEF
+#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
+#define MSDOS_MBR_SIGNATURE 0xaa55
+#define GPT_BLOCK_SIZE 512
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+typedef struct _gpt_header {
+       uint64_t signature;
+       uint32_t revision;
+       uint32_t header_size;
+       uint32_t header_crc32;
+       uint32_t reserved1;
+       uint64_t my_lba;
+       uint64_t alternate_lba;
+       uint64_t first_usable_lba;
+       uint64_t last_usable_lba;
+       efi_guid_t disk_guid;
+       uint64_t partition_entry_lba;
+       uint32_t num_partition_entries;
+       uint32_t sizeof_partition_entry;
+       uint32_t partition_entry_array_crc32;
+       uint8_t reserved2[GPT_BLOCK_SIZE - 92];
+} __attribute__ ((packed)) gpt_header;
+
+typedef struct _gpt_entry_attributes {
+       uint64_t required_to_function:1;
+       uint64_t reserved:47;
+        uint64_t type_guid_specific:16;
+} __attribute__ ((packed)) gpt_entry_attributes;
+
+typedef struct _gpt_entry {
+       efi_guid_t partition_type_guid;
+       efi_guid_t unique_partition_guid;
+       uint64_t starting_lba;
+       uint64_t ending_lba;
+       gpt_entry_attributes attributes;
+       efi_char16_t partition_name[72 / sizeof(efi_char16_t)];
+} __attribute__ ((packed)) gpt_entry;
+
+
+/* 
+   These values are only defaults.  The actual on-disk structures
+   may define different sizes, so use those unless creating a new GPT disk!
+*/
+
+#define GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE 16384
+/* 
+   Number of actual partition entries should be calculated
+   as: 
+*/
+#define GPT_DEFAULT_RESERVED_PARTITION_ENTRIES \
+        (GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE / \
+         sizeof(gpt_entry))
+
+
+/* Protected Master Boot Record  & Legacy MBR share same structure */
+/* Needs to be packed because the u16s force misalignment. */
+
+typedef struct _legacy_mbr {
+       uint8_t bootcode[440];
+       uint32_t unique_mbr_signature;
+       uint16_t unknown;
+       struct partition partition[4];
+       uint16_t signature;
+} __attribute__ ((packed)) legacy_mbr;
+
+
+#define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+/* Functions */
+int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns);
+
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4 
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/kpartx/kpartx.8 b/kpartx/kpartx.8
new file mode 100644 (file)
index 0000000..259ce3f
--- /dev/null
@@ -0,0 +1,39 @@
+.TH KPARTX 8 "February 2004" "" "Linux Administrator's Manual"
+.SH NAME
+kpartx \- Create device maps from partition tables
+.SH SYNOPSIS
+.B kpartx
+.RB [\| \-a\ \c
+.BR |\ -d\ |\ -l \|]
+.RB [\| \-v \|]
+.RB wholedisk
+.SH DESCRIPTION
+This tool, derived from util-linux' partx, reads partition
+tables on specified device and create device maps over partitions 
+segments detected. It is called from hotplug upon device maps 
+creation and deletion.
+.SH OPTIONS
+.TP
+.B \-a
+Add partition mappings
+.TP
+.B \-d
+Delete partition mappings
+.TP
+.B \-l
+List partition mappings that would be added -a
+.TP
+.B \-p
+set device name-partition number delimiter
+.TP
+.B \-v
+Operate verbosely
+.SH "SEE ALSO"
+.BR multipath (8)
+.BR multipathd (8)
+.BR hotplug (8)
+.SH "AUTHORS"
+This man page was assembled By Patrick Caulfield
+for the Debian project. From documentation provided
+by the multipath author Christophe Varoqui, <christophe.varoqui@free.fr> and others.
+
diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c
new file mode 100644 (file)
index 0000000..5d714bf
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * Given a block device and a partition table type,
+ * try to parse the partition table, and list the
+ * contents. Optionally add or remove partitions.
+ *
+ * Read wholedisk and add all partitions:
+ *     kpartx [-a|-d|-l] [-v] wholedisk
+ *
+ * aeb, 2000-03-21
+ * cva, 2002-10-26
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <libdevmapper.h>
+#include <linux/kdev_t.h>
+
+#include "devmapper.h"
+#include "crc32.h"
+#include "lopart.h"
+#include "kpartx.h"
+
+#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+#define READ_SIZE      1024
+#define MAXTYPES       64
+#define MAXSLICES      256
+#define DM_TARGET      "linear"
+#define LO_NAME_SIZE    64
+#define PARTNAME_SIZE  128
+#define DELIM_SIZE     8
+
+struct slice slices[MAXSLICES];
+
+enum action { LIST, ADD, DELETE };
+
+struct pt {
+       char *type;
+       ptreader *fn;
+} pts[MAXTYPES];
+
+int ptct = 0;
+
+static void
+addpts(char *t, ptreader f)
+{
+       if (ptct >= MAXTYPES) {
+               fprintf(stderr, "addpts: too many types\n");
+               exit(1);
+       }
+       pts[ptct].type = t;
+       pts[ptct].fn = f;
+       ptct++;
+}
+
+static void
+initpts(void)
+{
+       addpts("gpt", read_gpt_pt);
+       addpts("dos", read_dos_pt);
+       addpts("bsd", read_bsd_pt);
+       addpts("solaris", read_solaris_pt);
+       addpts("unixware", read_unixware_pt);
+}
+
+static char short_opts[] = "ladgvnp:t:";
+
+/* Used in gpt.c */
+int force_gpt=0;
+
+static int
+usage(void) {
+       printf("usage : kpartx [-a|-d|-l] [-v] wholedisk\n");
+       printf("\t-a add partition devmappings\n");
+       printf("\t-d del partition devmappings\n");
+       printf("\t-l list partitions devmappings that would be added by -a\n");
+       printf("\t-p set device name-partition number delimiter\n");
+       printf("\t-v verbose\n");
+       return 1;
+}
+
+static void
+set_delimiter (char * device, char * delimiter)
+{
+       char * p = device;
+
+       while (*(p++) != 0x0)
+               continue;
+
+       if (isdigit(*(p - 2)))
+               *delimiter = 'p';
+}
+
+static void
+strip_slash (char * device)
+{
+       char * p = device;
+
+       while (*(p++) != 0x0) {
+               
+               if (*p == '/')
+                       *p = '!';
+       }
+}
+
+static int
+find_devname_offset (char * device)
+{
+       char *p, *q = NULL;
+       
+       p = device;
+       
+       while (*p++)
+               if (*p == '/')
+                       q = p;
+
+       return (int)(q - device) + 1;
+}
+
+static char *
+get_hotplug_device(void)
+{
+       unsigned int major, minor, off, len;
+       const char *mapname;
+       char *devname = NULL;
+       char *device = NULL;
+       char *var = NULL;
+       struct stat buf;
+
+       var = getenv("ACTION");
+
+       if (!var || strcmp(var, "add"))
+               return NULL;
+
+       /* Get dm mapname for hotpluged device. */
+       if (!(devname = getenv("DEVNAME")))
+               return NULL;
+
+       if (stat(devname, &buf))
+               return NULL;
+
+       major = (unsigned int)MAJOR(buf.st_rdev);
+       minor = (unsigned int)MINOR(buf.st_rdev);
+
+       if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */
+               return NULL;
+
+       off = find_devname_offset(devname);
+       len = strlen(mapname);
+
+       /* Dirname + mapname + \0 */
+       if (!(device = (char *)malloc(sizeof(char) * (off + len + 1))))
+               return NULL;
+
+       /* Create new device name. */
+       snprintf(device, off + 1, "%s", devname);
+       snprintf(device + off, len + 1, "%s", mapname);
+
+       if (strlen(device) != (off + len))
+               return NULL;
+
+       return device;
+}
+
+int
+main(int argc, char **argv){
+        int fd, i, j, k, n, op, off, arg;
+       struct slice all;
+       struct pt *ptp;
+       enum action what = LIST;
+       char *p, *type, *diskdevice, *device, *progname;
+       int lower, upper;
+       int verbose = 0;
+       char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16];
+       char * loopdev = NULL;
+       char * delim = NULL;
+       int loopro = 0;
+       int hotplug = 0;
+       struct stat buf;
+
+       initpts();
+       init_crc32();
+
+       lower = upper = 0;
+       type = device = diskdevice = NULL;
+       memset(&all, 0, sizeof(all));
+       memset(&partname, 0, sizeof(partname));
+       
+       /* Check whether hotplug mode. */
+       progname = strrchr(argv[0], '/');
+
+       if (!progname)
+               progname = argv[0];
+       else
+               progname++;
+
+       if (!strcmp(progname, "kpartx.dev")) { /* Hotplug mode */
+               hotplug = 1;
+
+               /* Setup for original kpartx variables */
+               if (!(device = get_hotplug_device()))
+                       exit(1);
+
+               diskdevice = device;
+               what = ADD;
+       } else if (argc < 2) {
+               usage();
+               exit(1);
+       }
+
+       while ((arg = getopt(argc, argv, short_opts)) != EOF) switch(arg) {
+               case 'g':
+                       force_gpt=1;
+                       break;
+               case 't':
+                       type = optarg;
+                       break;
+               case 'v':
+                       verbose = 1;
+                       break;
+               case 'n':
+                       p = optarg;
+                       lower = atoi(p);
+                       if ((p[1] == '-') && p[2])
+                               upper = atoi(p+2);
+                       else
+                               upper = lower;
+                       break;
+               case 'p':
+                       delim = optarg;
+                       break;
+               case 'l':
+                       what = LIST;
+                       break;
+               case 'a':
+                       what = ADD;
+                       break;
+               case 'd':
+                       what = DELETE;
+                       break;
+               default:
+                       usage();
+                       exit(1);
+       }
+
+       if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE)) {
+               fprintf(stderr, "device mapper prerequisites not met\n"); 
+               exit(1);
+       }
+
+       if (hotplug) {
+               /* already got [disk]device */
+       } else if (optind == argc-2) {
+               device = argv[optind];
+               diskdevice = argv[optind+1];
+       } else if (optind == argc-1) {
+               diskdevice = device = argv[optind];
+       } else {
+               usage();
+               exit(1);
+       }
+
+       if (stat(device, &buf)) {
+               printf("failed to stat() %s\n", device);
+               exit (1);
+       }
+
+       if (S_ISREG (buf.st_mode)) {
+               loopdev = malloc(LO_NAME_SIZE * sizeof(char));
+               
+               if (!loopdev)
+                       exit(1);
+
+               /* already looped file ? */
+               loopdev = find_loop_by_file(device);
+
+               if (!loopdev && what == DELETE)
+                       exit (0);
+                               
+               if (!loopdev) {
+                       loopdev = find_unused_loop_device();
+
+                       if (set_loop(loopdev, device, 0, &loopro)) {
+                               fprintf(stderr, "can't set up loop\n");
+                               exit (1);
+                       }
+               }
+               device = loopdev;
+       }
+
+       if (delim == NULL) {
+               delim = malloc(DELIM_SIZE);
+               memset(delim, 0, DELIM_SIZE);
+               set_delimiter(device, delim);
+       }
+       
+       off = find_devname_offset(device);
+       fd = open(device, O_RDONLY);
+
+       if (fd == -1) {
+               perror(device);
+               exit(1);
+       }
+       if (!lower)
+               lower = 1;
+
+       /* add/remove partitions to the kernel devmapper tables */
+       for (i = 0; i < ptct; i++) {
+               ptp = &pts[i];
+
+               if (type && strcmp(type, ptp->type) > 0)
+                       continue;
+               
+               /* here we get partitions */
+               n = ptp->fn(fd, all, slices, SIZE(slices));
+
+#ifdef DEBUG
+               if (n >= 0)
+                       printf("%s: %d slices\n", ptp->type, n);
+#endif
+
+               if (n > 0)
+                       close(fd);
+               else
+                       continue;
+
+               /*
+                * test for overlap, as in the case of an extended partition
+                * zero their size to avoid mapping
+                */
+               for (j=0; j<n; j++) {
+                       for (k=j+1; k<n; k++) {
+                               if (slices[k].start > slices[j].start &&
+                                   slices[k].start < slices[j].start +
+                                   slices[j].size)
+                                       slices[j].size = 0;
+                       }
+               }
+
+               switch(what) {
+               case LIST:
+                       for (j = 0; j < n; j++) {
+                               if (slices[j].size == 0)
+                                       continue;
+
+                               printf("%s%s%d : 0 %lu %s %lu\n",
+                                       device + off, delim, j+1,
+                                       (unsigned long) slices[j].size, device,
+                                       (unsigned long) slices[j].start);
+                       }
+                       break;
+
+               case DELETE:
+                       for (j = 0; j < n; j++) {
+                               if (safe_sprintf(partname, "%s%s%d",
+                                            device + off , delim, j+1)) {
+                                       fprintf(stderr, "partname too small\n");
+                                       exit(1);
+                               }
+                               strip_slash(partname);
+
+                               if (!slices[j].size || !dm_map_present(partname))
+                                       continue;
+
+                               if (!dm_simplecmd(DM_DEVICE_REMOVE, partname))
+                                       continue;
+
+                               if (verbose)
+                                       printf("del devmap : %s\n", partname);
+                       }
+
+                       if (S_ISREG (buf.st_mode)) {
+                               if (del_loop(device)) {
+                                       if (verbose)
+                                               printf("can't del loop : %s\n",
+                                                       device);
+                                       exit(1);
+                               }
+                               printf("loop deleted : %s\n", device);
+                       }
+                       break;
+
+               case ADD:
+                       for (j=0; j<n; j++) {
+                               if (slices[j].size == 0)
+                                       continue;
+
+                               if (safe_sprintf(partname, "%s%s%d",
+                                            device + off , delim, j+1)) {
+                                       fprintf(stderr, "partname too small\n");
+                                       exit(1);
+                               }
+                               strip_slash(partname);
+                               
+                               if (safe_sprintf(params, "%s %lu", device,
+                                            (unsigned long)slices[j].start)) {
+                                       fprintf(stderr, "params too small\n");
+                                       exit(1);
+                               }
+
+                               op = (dm_map_present(partname) ?
+                                       DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+
+                               dm_addmap(op, partname, DM_TARGET, params,
+                                         slices[j].size);
+
+                               if (op == DM_DEVICE_RELOAD)
+                                       dm_simplecmd(DM_DEVICE_RESUME,
+                                                       partname);
+
+                               if (verbose)
+                                       printf("add map %s : 0 %lu %s %s\n",
+                                               partname, slices[j].size,
+                                               DM_TARGET, params);
+                       }
+                       break;
+
+               default:
+                       break;
+
+               }
+               if (n > 0)
+                       break;
+       }
+       return 0;
+}
+
+void *
+xmalloc (size_t size) {
+       void *t;
+
+       if (size == 0)
+               return NULL;
+
+       t = malloc (size);
+
+       if (t == NULL) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+
+       return t;
+}
+
+/*
+ * sseek: seek to specified sector
+ */
+#if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
+       && !defined (__s390x__)
+#include <linux/unistd.h>       /* _syscall */
+static
+_syscall5(int,  _llseek,  uint,  fd, ulong, hi, ulong, lo,
+         long long *, res, uint, wh);
+#endif
+
+static int
+sseek(int fd, unsigned int secnr) {
+       long long in, out;
+       in = ((long long) secnr << 9);
+       out = 1;
+
+#if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
+       && !defined (__s390x__)
+       if (_llseek (fd, in>>32, in & 0xffffffff, &out, SEEK_SET) != 0
+           || out != in)
+#else
+       if ((out = lseek(fd, in, SEEK_SET)) != in)
+#endif
+       {
+               fprintf(stderr, "llseek error\n");
+               return -1;
+       }
+       return 0;
+}
+
+static
+struct block {
+       unsigned int secnr;
+       char *block;
+       struct block *next;
+} *blockhead;
+
+char *
+getblock (int fd, unsigned int secnr) {
+       struct block *bp;
+
+       for (bp = blockhead; bp; bp = bp->next)
+
+               if (bp->secnr == secnr)
+                       return bp->block;
+
+       if (sseek(fd, secnr))
+               return NULL;
+
+       bp = xmalloc(sizeof(struct block));
+       bp->secnr = secnr;
+       bp->next = blockhead;
+       blockhead = bp;
+       bp->block = (char *) xmalloc(READ_SIZE);
+       
+       if (read(fd, bp->block, READ_SIZE) != READ_SIZE) {
+               fprintf(stderr, "read error, sector %d\n", secnr);
+               bp->block = NULL;
+       }
+
+       return bp->block;
+}
diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h
new file mode 100644 (file)
index 0000000..8108021
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _KPARTX_H
+#define _KPARTX_H
+
+/*
+ * For each partition type there is a routine that takes
+ * a block device and a range, and returns the list of
+ * slices found there in the supplied array SP that can
+ * hold NS entries. The return value is the number of
+ * entries stored, or -1 if the appropriate type is not
+ * present.
+ */
+
+#define likely(x)       __builtin_expect(!!(x), 1)
+#define unlikely(x)     __builtin_expect(!!(x), 0)
+
+#define safe_sprintf(var, format, args...)     \
+       snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
+
+/*
+ * units: 512 byte sectors
+ */
+struct slice {
+       unsigned long start;
+       unsigned long size;
+};
+
+typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns);
+
+extern ptreader read_dos_pt;
+extern ptreader read_bsd_pt;
+extern ptreader read_solaris_pt;
+extern ptreader read_unixware_pt;
+extern ptreader read_gpt_pt;
+
+char *getblock(int fd, unsigned int secnr);
+
+static inline int
+four2int(unsigned char *p) {
+       return p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
+}
+
+#endif /* _KPARTX_H */
diff --git a/kpartx/lopart.c b/kpartx/lopart.c
new file mode 100644 (file)
index 0000000..26b0ec1
--- /dev/null
@@ -0,0 +1,294 @@
+/* Taken from Ted's losetup.c - Mitch <m.dsouza@mrc-apu.cam.ac.uk> */
+/* Added vfs mount options - aeb - 960223 */
+/* Removed lomount - aeb - 960224 */
+
+/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixed strerr(errno) in gettext calls
+ */
+
+#define PROC_DEVICES   "/proc/devices"
+
+/*
+ * losetup.c - setup and control loop devices
+ */
+
+#include "kpartx.h"
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sysmacros.h>
+#include <linux/loop.h>
+
+#include "lopart.h"
+#include "xstrncpy.h"
+
+#if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
+        && !defined (__s390x__)
+#define int2ptr(x)     ((void *) ((int) x))
+#else
+#define int2ptr(x)     ((void *) ((long) x))
+#endif
+
+static char *
+xstrdup (const char *s)
+{
+       char *t;
+
+       if (s == NULL)
+               return NULL;
+
+       t = strdup (s);
+
+       if (t == NULL) {
+               fprintf(stderr, "not enough memory");
+               exit(1);
+       }
+
+       return t;
+}
+
+extern int
+is_loop_device (const char *device)
+{
+       struct stat statbuf;
+       int loopmajor;
+#if 1
+       loopmajor = 7;
+#else
+       FILE *procdev;
+       char line[100], *cp;
+
+       loopmajor = 0;
+
+       if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
+               
+               while (fgets (line, sizeof(line), procdev)) {
+                       
+                       if ((cp = strstr (line, " loop\n")) != NULL) {
+                               *cp='\0';
+                               loopmajor=atoi(line);
+                               break;
+                       }
+               }
+
+               fclose(procdev);
+       }
+#endif
+       return (loopmajor && stat(device, &statbuf) == 0 &&
+               S_ISBLK(statbuf.st_mode) &&
+               major(statbuf.st_rdev) == loopmajor);
+}
+
+#define SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+extern char *
+find_loop_by_file (const char * filename)
+{
+       char dev[20];
+       char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
+       int i, j, fd;
+       struct stat statbuf;
+       struct loop_info loopinfo;
+
+       for (j = 0; j < SIZE(loop_formats); j++) {
+
+               for (i = 0; i < 256; i++) {
+                       sprintf (dev, loop_formats[j], i);
+
+                       if (stat (dev, &statbuf) != 0 ||
+                           !S_ISBLK(statbuf.st_mode))
+                               continue;
+
+                       fd = open (dev, O_RDONLY);
+
+                       if (fd < 0)
+                               break;
+
+                       if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) {
+                               close (fd);
+                               continue;
+                       }
+
+                       if (0 == strcmp(filename, loopinfo.lo_name)) {
+                               close (fd);
+                               return xstrdup(dev); /*found */
+                       }
+
+                       close (fd);
+                       continue;
+               }
+       }
+       return NULL;
+}
+
+extern char *
+find_unused_loop_device (void)
+{
+       /* Just creating a device, say in /tmp, is probably a bad idea -
+          people might have problems with backup or so.
+          So, we just try /dev/loop[0-7]. */
+
+       char dev[20];
+       char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
+       int i, j, fd, somedev = 0, someloop = 0, loop_known = 0;
+       struct stat statbuf;
+       struct loop_info loopinfo;
+       FILE *procdev;
+
+       for (j = 0; j < SIZE(loop_formats); j++) {
+
+           for(i = 0; i < 256; i++) {
+               sprintf(dev, loop_formats[j], i);
+
+               if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
+                       somedev++;
+                       fd = open (dev, O_RDONLY);
+
+                       if (fd >= 0) {
+
+                               if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
+                                       someloop++;             /* in use */
+
+                               else if (errno == ENXIO) {
+                                       close (fd);
+                                       return xstrdup(dev);/* probably free */
+                               }
+
+                               close (fd);
+                       }
+                       
+                       /* continue trying as long as devices exist */
+                       continue;
+               }
+               break;
+           }
+       }
+
+       /* Nothing found. Why not? */
+       if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
+               char line[100];
+
+               while (fgets (line, sizeof(line), procdev))
+
+                       if (strstr (line, " loop\n")) {
+                               loop_known = 1;
+                               break;
+                       }
+
+               fclose(procdev);
+
+               if (!loop_known)
+                       loop_known = -1;
+       }
+
+       if (!somedev)
+               fprintf(stderr, "mount: could not find any device /dev/loop#");
+
+       else if (!someloop) {
+
+           if (loop_known == 1)
+               fprintf(stderr,
+                   "mount: Could not find any loop device.\n"
+                   "       Maybe /dev/loop# has a wrong major number?");
+           
+           else if (loop_known == -1)
+               fprintf(stderr,
+                   "mount: Could not find any loop device, and, according to %s,\n"
+                   "       this kernel does not know about the loop device.\n"
+                   "       (If so, then recompile or `insmod loop.o'.)",
+                     PROC_DEVICES);
+
+           else
+               fprintf(stderr,
+                   "mount: Could not find any loop device. Maybe this kernel does not know\n"
+                   "       about the loop device (then recompile or `insmod loop.o'), or\n"
+                   "       maybe /dev/loop# has the wrong major number?");
+
+       } else
+               fprintf(stderr, "mount: could not find any free loop device");
+       
+       return 0;
+}
+
+extern int
+set_loop (const char *device, const char *file, int offset, int *loopro)
+{
+       struct loop_info loopinfo;
+       int fd, ffd, mode;
+
+       mode = (*loopro ? O_RDONLY : O_RDWR);
+
+       if ((ffd = open (file, mode)) < 0) {
+
+               if (!*loopro && errno == EROFS)
+                       ffd = open (file, mode = O_RDONLY);
+
+               if (ffd < 0) {
+                       perror (file);
+                       return 1;
+               }
+       }
+
+       if ((fd = open (device, mode)) < 0) {
+               perror (device);
+               return 1;
+       }
+
+       *loopro = (mode == O_RDONLY);
+       memset (&loopinfo, 0, sizeof (loopinfo));
+
+       xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE);
+       loopinfo.lo_offset = offset;
+       loopinfo.lo_encrypt_type = LO_CRYPT_NONE;
+       loopinfo.lo_encrypt_key_size = 0;
+
+       if (ioctl (fd, LOOP_SET_FD, int2ptr(ffd)) < 0) {
+               perror ("ioctl: LOOP_SET_FD");
+               close (fd);
+               close (ffd);
+               return 1;
+       }
+
+       if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) {
+               (void) ioctl (fd, LOOP_CLR_FD, 0);
+               perror ("ioctl: LOOP_SET_STATUS");
+               close (fd);
+               close (ffd);
+               return 1;
+       }
+
+       close (fd);
+       close (ffd);
+       return 0;
+}
+
+extern int 
+del_loop (const char *device)
+{
+       int fd;
+
+       if ((fd = open (device, O_RDONLY)) < 0) {
+               int errsv = errno;
+               fprintf(stderr, "loop: can't delete device %s: %s\n",
+                       device, strerror (errsv));
+               return 1;
+       }
+
+       if (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
+               perror ("ioctl: LOOP_CLR_FD");
+               close (fd);
+               return 1;
+       }
+
+       close (fd);
+       return 0;
+}
diff --git a/kpartx/lopart.h b/kpartx/lopart.h
new file mode 100644 (file)
index 0000000..a512353
--- /dev/null
@@ -0,0 +1,6 @@
+extern int verbose;
+extern int set_loop (const char *, const char *, int, int *);
+extern int del_loop (const char *);
+extern int is_loop_device (const char *);
+extern char * find_unused_loop_device (void);
+extern char * find_loop_by_file (const char *);
diff --git a/kpartx/solaris.c b/kpartx/solaris.c
new file mode 100644 (file)
index 0000000..e3000e9
--- /dev/null
@@ -0,0 +1,71 @@
+#include "kpartx.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>              /* time_t */
+
+#define SOLARIS_X86_NUMSLICE   8
+#define SOLARIS_X86_VTOC_SANE  (0x600DDEEEUL)
+
+//typedef int daddr_t;         /* or long - check */
+
+struct solaris_x86_slice {
+       unsigned short  s_tag;          /* ID tag of partition */
+       unsigned short  s_flag;         /* permision flags */
+       daddr_t         s_start;        /* start sector no of partition */
+       long            s_size;         /* # of blocks in partition */
+};
+
+struct solaris_x86_vtoc {
+       unsigned long v_bootinfo[3];    /* info for mboot */
+       unsigned long v_sanity;         /* to verify vtoc sanity */
+       unsigned long v_version;        /* layout version */
+       char    v_volume[8];            /* volume name */
+       unsigned short  v_sectorsz;     /* sector size in bytes */
+       unsigned short  v_nparts;       /* number of partitions */
+       unsigned long v_reserved[10];   /* free space */
+       struct solaris_x86_slice
+               v_slice[SOLARIS_X86_NUMSLICE];   /* slice headers */
+       time_t  timestamp[SOLARIS_X86_NUMSLICE]; /* timestamp */
+       char    v_asciilabel[128];      /* for compatibility */
+};
+
+int
+read_solaris_pt(int fd, struct slice all, struct slice *sp, int ns) {
+       struct solaris_x86_vtoc *v;
+       struct solaris_x86_slice *s;
+       unsigned int offset = all.start;
+       int i, n;
+       char *bp;
+
+       bp = getblock(fd, offset+1);    /* 1 sector suffices */
+       if (bp == NULL)
+               return -1;
+
+       v = (struct solaris_x86_vtoc *) bp;
+       if(v->v_sanity != SOLARIS_X86_VTOC_SANE)
+               return -1;
+
+       if(v->v_version != 1) {
+               fprintf(stderr, "Cannot handle solaris version %ld vtoc\n",
+                      v->v_version);
+               return 0;
+       }
+
+       for(i=0, n=0; i<SOLARIS_X86_NUMSLICE; i++) {
+               s = &v->v_slice[i];
+
+               if (s->s_size == 0)
+                       continue;
+               if (n < ns) {
+                       sp[n].start = offset + s->s_start;
+                       sp[n].size = s->s_size;
+                       n++;
+               } else {
+                       fprintf(stderr,
+                               "solaris_x86_partition: too many slices\n");
+                       break;
+               }
+       }
+       return n;
+}
+
diff --git a/kpartx/sysmacros.h b/kpartx/sysmacros.h
new file mode 100644 (file)
index 0000000..171b33d
--- /dev/null
@@ -0,0 +1,9 @@
+/* versions to be used with > 16-bit dev_t - leave unused for now */
+
+#ifndef major
+#define major(dev)     ((dev) >> 8)
+#endif
+
+#ifndef minor
+#define minor(dev)     ((dev) & 0xff)
+#endif
diff --git a/kpartx/unixware.c b/kpartx/unixware.c
new file mode 100644 (file)
index 0000000..41cc957
--- /dev/null
@@ -0,0 +1,83 @@
+#include "kpartx.h"
+#include <stdio.h>
+
+#define UNIXWARE_FS_UNUSED     0
+#define UNIXWARE_NUMSLICE      16
+#define UNIXWARE_DISKMAGIC     (0xCA5E600D)
+#define UNIXWARE_DISKMAGIC2    (0x600DDEEE)
+
+struct unixware_slice {
+       unsigned short s_label;         /* label */
+       unsigned short s_flags;         /* permission flags */
+       unsigned int   start_sect;      /* starting sector */
+       unsigned int   nr_sects;        /* number of sectors in slice */
+};
+
+struct unixware_disklabel {
+       unsigned int   d_type;          /* drive type */
+       unsigned char  d_magic[4];      /* the magic number */
+       unsigned int   d_version;       /* version number */
+       char    d_serial[12];           /* serial number of the device */
+       unsigned int   d_ncylinders;    /* # of data cylinders per device */
+       unsigned int   d_ntracks;       /* # of tracks per cylinder */
+       unsigned int   d_nsectors;      /* # of data sectors per track */
+       unsigned int   d_secsize;       /* # of bytes per sector */
+       unsigned int   d_part_start;    /* # of first sector of this partition */
+       unsigned int   d_unknown1[12];  /* ? */
+       unsigned int   d_alt_tbl;       /* byte offset of alternate table */
+       unsigned int   d_alt_len;       /* byte length of alternate table */
+       unsigned int   d_phys_cyl;      /* # of physical cylinders per device */
+       unsigned int   d_phys_trk;      /* # of physical tracks per cylinder */
+       unsigned int   d_phys_sec;      /* # of physical sectors per track */
+       unsigned int   d_phys_bytes;    /* # of physical bytes per sector */
+       unsigned int   d_unknown2;      /* ? */
+       unsigned int   d_unknown3;      /* ? */
+       unsigned int   d_pad[8];        /* pad */
+
+       struct unixware_vtoc {
+               unsigned char   v_magic[4];     /* the magic number */
+               unsigned int    v_version;      /* version number */
+               char    v_name[8];              /* volume name */
+               unsigned short  v_nslices;      /* # of slices */
+               unsigned short  v_unknown1;     /* ? */
+               unsigned int    v_reserved[10]; /* reserved */
+               struct unixware_slice
+                   v_slice[UNIXWARE_NUMSLICE]; /* slice headers */
+       } vtoc;
+
+};  /* 408 */
+
+int
+read_unixware_pt(int fd, struct slice all, struct slice *sp, int ns) {
+       struct unixware_disklabel *l;
+       struct unixware_slice *p;
+       unsigned int offset = all.start;
+       char *bp;
+       int n = 0;
+
+       bp = getblock(fd, offset+29);   /* 1 sector suffices */
+       if (bp == NULL)
+               return -1;
+
+       l = (struct unixware_disklabel *) bp;
+       if (four2int(l->d_magic) != UNIXWARE_DISKMAGIC ||
+           four2int(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2)
+               return -1;
+
+       p = &l->vtoc.v_slice[1];        /* slice 0 is the whole disk. */
+       while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
+               if (p->s_label == UNIXWARE_FS_UNUSED)
+                       /* nothing */;
+               else if (n < ns) {
+                       sp[n].start = p->start_sect;
+                       sp[n].size = p->nr_sects;
+                       n++;
+               } else {
+                       fprintf(stderr,
+                               "unixware_partition: too many slices\n");
+                       break;
+               }
+               p++;
+       }
+       return n;
+}
diff --git a/kpartx/xstrncpy.c b/kpartx/xstrncpy.c
new file mode 100644 (file)
index 0000000..7975426
--- /dev/null
@@ -0,0 +1,10 @@
+/* NUL-terminated version of strncpy() */
+#include <string.h>
+#include "xstrncpy.h"
+
+/* caller guarantees n > 0 */
+void
+xstrncpy(char *dest, const char *src, size_t n) {
+       strncpy(dest, src, n-1);
+       dest[n-1] = 0;
+}
diff --git a/kpartx/xstrncpy.h b/kpartx/xstrncpy.h
new file mode 100644 (file)
index 0000000..05c8fa2
--- /dev/null
@@ -0,0 +1 @@
+extern void xstrncpy(char *dest, const char *src, size_t n);
diff --git a/libcheckers/Makefile b/libcheckers/Makefile
new file mode 100644 (file)
index 0000000..7539ce3
--- /dev/null
@@ -0,0 +1,27 @@
+# Makefile
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr>
+#
+BUILD = glibc
+
+include ../Makefile.inc
+
+OBJS = readsector0.o tur.o selector.o emc_clariion.o
+
+all: $(BUILD)
+
+prepare:
+       rm -f core *.o *.gz
+
+klibc: prepare $(OBJS)
+       ar rs libcheckers-klibc.a *.o
+
+glibc: prepare $(OBJS)
+       ar rs libcheckers-glibc.a *.o
+
+install:
+
+uninstall:
+
+clean:
+       rm -f core *.a *.o *.gz
diff --git a/libcheckers/checkers.h b/libcheckers/checkers.h
new file mode 100644 (file)
index 0000000..a66d894
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _CHECKERS_H
+#define _CHECKERS_H
+
+#define CHECKER_NAME_SIZE 16
+#define DEVNODE_SIZE 256
+#define MAX_CHECKER_MSG_SIZE 256
+
+enum checkers {
+       CHECKER_RESERVED,
+       TUR,
+       READSECTOR0,
+       EMC_CLARIION
+};
+
+#define MSG(a) if (msg != NULL) \
+                       snprintf(msg, MAX_CHECKER_MSG_SIZE, "%s\n", a);
+
+int get_checker_id (char *);
+void *get_checker_addr (int);
+int get_checker_name (char *, int);
+
+int emc_clariion (int fd, char * msg, void ** ctxt);
+int readsector0 (int fd, char * msg, void ** ctxt);
+int tur (int fd, char * msg, void ** ctxt);
+
+#endif /* _CHECKERS_H */
diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c
new file mode 100644 (file)
index 0000000..790168b
--- /dev/null
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "path_state.h"
+#include "checkers.h"
+
+#include "../libmultipath/sg_include.h"
+
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define HEAVY_CHECK_COUNT       10
+
+struct emc_clariion_checker_context {
+       int run_count;
+       char wwn[16];
+       unsigned wwn_set;
+};
+
+int emc_clariion(int fd, char *msg, void **context)
+{
+       unsigned char sense_buffer[256] = { 0, };
+       unsigned char sb[128] = { 0, };
+       unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0,
+                                               sizeof(sb), 0};
+       struct sg_io_hdr io_hdr;
+       struct emc_clariion_checker_context * ctxt = NULL;
+       int ret;
+
+       /*
+        * caller passed in a context : use its address
+        */
+       if (context)
+               ctxt = (struct emc_clariion_checker_context *) (*context);
+
+       /*
+        * passed in context is uninitialized or volatile context :
+        * initialize it
+        */
+       if (!ctxt) {
+               ctxt = malloc(sizeof(struct emc_clariion_checker_context));
+               memset(ctxt, 0, sizeof(struct emc_clariion_checker_context));
+
+               if (!ctxt) {
+                       MSG("cannot allocate context");
+                       return -1;
+               }
+               if (context)
+                       *context = ctxt;
+       }
+       ctxt->run_count++;
+
+       if ((ctxt->run_count % HEAVY_CHECK_COUNT) == 0) {
+               ctxt->run_count = 0;
+               /* do stuff */
+       }
+
+       if (fd <= 0) {
+               MSG("no usable fd");
+               ret = -1;
+               goto out;
+       }
+       memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+       io_hdr.interface_id = 'S';
+       io_hdr.cmd_len = sizeof (inqCmdBlk);
+       io_hdr.mx_sb_len = sizeof (sb);
+       io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+       io_hdr.dxfer_len = sizeof (sense_buffer);
+       io_hdr.dxferp = sense_buffer;
+       io_hdr.cmdp = inqCmdBlk;
+       io_hdr.sbp = sb;
+       io_hdr.timeout = 60000;
+       io_hdr.pack_id = 0;
+       if (ioctl(fd, SG_IO, &io_hdr) < 0) {
+               MSG("emc_clariion_checker: sending query command failed");
+               ret = PATH_DOWN;
+               goto out;
+       }
+       if (io_hdr.info & SG_INFO_OK_MASK) {
+               MSG("emc_clariion_checker: query command indicates error");
+               ret = PATH_DOWN;
+               goto out;
+       }
+       if (/* Verify the code page - right page & revision */
+           sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) {
+               MSG("emc_clariion_checker: Path unit report page in unknown format");
+               ret = PATH_DOWN;
+               goto out;
+       }
+
+       if ( /* Effective initiator type */
+               sense_buffer[27] != 0x03
+               /* Failover mode should be set to 1 */        
+               || (sense_buffer[28] & 0x07) != 0x04
+               /* Arraycommpath should be set to 1 */
+               || (sense_buffer[30] & 0x04) != 0x04) {
+               MSG("emc_clariion_checker: Path not correctly configured for failover");
+               ret = PATH_DOWN;
+               goto out;
+       }
+
+       if ( /* LUN operations should indicate normal operations */
+               sense_buffer[48] != 0x00) {
+               MSG("emc_clariion_checker: Path not available for normal operations");
+               ret = PATH_SHAKY;
+               goto out;
+       }
+
+#if 0
+       /* This is not actually an error as the failover to this group
+        * _would_ bind the path */
+       if ( /* LUN should at least be bound somewhere */
+               sense_buffer[4] != 0x00) {
+               ret = PATH_UP;
+               goto out;
+       }
+#endif 
+       
+       /*
+        * store the LUN WWN there and compare that it indeed did not
+        * change in between, to protect against the path suddenly
+        * pointing somewhere else.
+        */
+       if (context && ctxt->wwn_set) {
+               if (memcmp(ctxt->wwn, &sense_buffer[10], 16) != 0) {
+                       MSG("emc_clariion_checker: Logical Unit WWN has changed!");
+                       ret = PATH_DOWN;
+                       goto out;
+               }
+       } else {
+               memcpy(ctxt->wwn, &sense_buffer[10], 16);
+               ctxt->wwn_set = 1;
+       }
+       
+       
+       MSG("emc_clariion_checker: Path healthy");
+        ret = PATH_UP;
+out:
+       /*
+        * caller told us he doesn't want to keep the context :
+        * free it
+        */
+       if (!context)
+               free(ctxt);
+
+       return(ret);
+}
diff --git a/libcheckers/path_state.h b/libcheckers/path_state.h
new file mode 100644 (file)
index 0000000..ffdc09c
--- /dev/null
@@ -0,0 +1,4 @@
+#define PATH_UNCHECKED 0
+#define PATH_DOWN      1
+#define PATH_UP                2
+#define PATH_SHAKY     3
diff --git a/libcheckers/readsector0.c b/libcheckers/readsector0.c
new file mode 100644 (file)
index 0000000..50ab467
--- /dev/null
@@ -0,0 +1,140 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "path_state.h"
+#include "checkers.h"
+
+#include "../libmultipath/sg_include.h"
+
+#define SENSE_BUFF_LEN 32
+#define DEF_TIMEOUT 60000
+
+#define MSG_READSECTOR0_UP     "readsector0 checker reports path is up"
+#define MSG_READSECTOR0_DOWN   "readsector0 checker reports path is down"
+
+struct readsector0_checker_context {
+       void * dummy;
+};
+
+static int
+sg_read (int sg_fd, unsigned char * buff)
+{
+       /* defaults */
+       int blocks = 1;
+       long long start_block = 0;
+       int bs = 512;
+       int cdbsz = 10;
+       int * diop = NULL;
+
+       unsigned char rdCmd[cdbsz];
+       unsigned char senseBuff[SENSE_BUFF_LEN];
+       struct sg_io_hdr io_hdr;
+       int res;
+       int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+       int sz_ind;
+       
+       memset(rdCmd, 0, cdbsz);
+       sz_ind = 1;
+       rdCmd[0] = rd_opcode[sz_ind];
+       rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff);
+       rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff);
+       rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff);
+       rdCmd[5] = (unsigned char)(start_block & 0xff);
+       rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff);
+       rdCmd[8] = (unsigned char)(blocks & 0xff);
+
+       memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+       io_hdr.interface_id = 'S';
+       io_hdr.cmd_len = cdbsz;
+       io_hdr.cmdp = rdCmd;
+       io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+       io_hdr.dxfer_len = bs * blocks;
+       io_hdr.dxferp = buff;
+       io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+       io_hdr.sbp = senseBuff;
+       io_hdr.timeout = DEF_TIMEOUT;
+       io_hdr.pack_id = (int)start_block;
+       if (diop && *diop)
+       io_hdr.flags |= SG_FLAG_DIRECT_IO;
+
+       while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno));
+
+       if (res < 0) {
+               if (ENOMEM == errno) {
+                       return PATH_UP;
+               }
+               return PATH_DOWN;
+       }
+
+       if ((0 == io_hdr.status) &&
+           (0 == io_hdr.host_status) &&
+           (0 == io_hdr.driver_status)) {
+               return PATH_UP;
+       } else {
+               return PATH_DOWN;
+       }
+}
+
+extern int
+readsector0 (int fd, char *msg, void **context)
+{
+       char buf[512];
+       struct readsector0_checker_context * ctxt = NULL;
+       int ret;
+
+       /*
+        * caller passed in a context : use its address
+        */
+       if (context)
+               ctxt = (struct readsector0_checker_context *) (*context);
+
+       /*
+        * passed in context is uninitialized or volatile context :
+        * initialize it
+        */
+       if (!ctxt) {
+               ctxt = malloc(sizeof(struct readsector0_checker_context));
+               memset(ctxt, 0, sizeof(struct readsector0_checker_context));
+
+               if (!ctxt) {
+                       MSG("cannot allocate context");
+                       return -1;
+               }
+               if (context)
+                       *context = ctxt;
+       }
+       if (fd <= 0) {
+               MSG("no usable fd");
+               ret = -1;
+               goto out;
+       }
+       ret = sg_read(fd, &buf[0]);
+
+       switch (ret)
+       {
+       case PATH_DOWN:
+               MSG(MSG_READSECTOR0_DOWN);
+               break;
+       case PATH_UP:
+               MSG(MSG_READSECTOR0_UP);
+               break;
+       default:
+               break;
+       }
+out:
+       /*
+        * caller told us he doesn't want to keep the context :
+        * free it
+        */
+       if (!context)
+               free(ctxt);
+
+       return ret;
+}
diff --git a/libcheckers/selector.c b/libcheckers/selector.c
new file mode 100644 (file)
index 0000000..e759258
--- /dev/null
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "checkers.h"
+
+extern int
+get_checker_id (char * str)
+{
+       if (0 == strncmp(str, "tur", 3))
+               return TUR;
+       if (0 == strncmp(str, "readsector0", 11))
+               return READSECTOR0;
+       if (0 == strncmp(str, "emc_clariion", 12))
+               return EMC_CLARIION;
+       return -1;
+}
+
+extern void * 
+get_checker_addr (int id)
+{
+       int (*checker) (int, char *, void **);
+
+       switch (id) {
+       case TUR:
+               checker = &tur;
+               break;
+       case READSECTOR0:
+               checker = &readsector0;
+               break;
+       case EMC_CLARIION:
+               checker = &emc_clariion;
+               break;
+       default:
+               checker = NULL;
+               break;
+       }
+       return checker;
+}
+
+extern int
+get_checker_name (char * str, int id)
+{
+       char * s;
+
+       switch (id) {
+       case TUR:
+               s = "tur";
+               break;
+       case READSECTOR0:
+               s = "readsector0";
+               break;
+       case EMC_CLARIION:
+               s = "emc_clariion";
+               break;
+       default:
+               s = "undefined";
+               break;
+       }
+       if (snprintf(str, CHECKER_NAME_SIZE, "%s", s) >= CHECKER_NAME_SIZE) {
+               fprintf(stderr, "checker_name too small\n");
+               return 1;
+       }
+       return 0;
+}
diff --git a/libcheckers/tur.c b/libcheckers/tur.c
new file mode 100644 (file)
index 0000000..3c76f41
--- /dev/null
@@ -0,0 +1,100 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "path_state.h"
+#include "checkers.h"
+
+#include "../libmultipath/sg_include.h"
+
+#define TUR_CMD_LEN 6
+#define HEAVY_CHECK_COUNT       10
+
+#define MSG_TUR_UP     "tur checker reports path is up"
+#define MSG_TUR_DOWN   "tur checker reports path is down"
+
+struct tur_checker_context {
+       int run_count;
+};
+
+
+extern int
+tur (int fd, char *msg, void **context)
+{
+        unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 };
+        struct sg_io_hdr io_hdr;
+        unsigned char sense_buffer[32];
+       struct tur_checker_context * ctxt = NULL;
+       int ret;
+
+       /*
+        * caller passed in a context : use its address
+        */
+       if (context)
+               ctxt = (struct tur_checker_context *) (*context);
+
+       /*
+        * passed in context is uninitialized or volatile context :
+        * initialize it
+        */
+       if (!ctxt) {
+               ctxt = malloc(sizeof(struct tur_checker_context));
+               memset(ctxt, 0, sizeof(struct tur_checker_context));
+
+               if (!ctxt) {
+                       MSG("cannot allocate context");
+                       return -1;
+               }
+               if (context)
+                       *context = ctxt;
+       }
+       ctxt->run_count++;
+
+       if ((ctxt->run_count % HEAVY_CHECK_COUNT) == 0) {
+               ctxt->run_count = 0;
+               /* do stuff */
+       }
+       if (fd <= 0) {
+               MSG("no usable fd");
+               ret = -1;
+               goto out;
+       }
+       
+        memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof (turCmdBlk);
+        io_hdr.mx_sb_len = sizeof (sense_buffer);
+        io_hdr.dxfer_direction = SG_DXFER_NONE;
+        io_hdr.cmdp = turCmdBlk;
+        io_hdr.sbp = sense_buffer;
+        io_hdr.timeout = 20000;
+        io_hdr.pack_id = 0;
+        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
+               MSG(MSG_TUR_DOWN);
+                ret = PATH_DOWN;
+               goto out;
+        }
+        if (io_hdr.info & SG_INFO_OK_MASK) {
+               MSG(MSG_TUR_DOWN);
+                ret = PATH_DOWN;
+               goto out;
+        }
+       MSG(MSG_TUR_UP);
+        ret = PATH_UP;
+
+out:
+       /*
+        * caller told us he doesn't want to keep the context :
+        * free it
+        */
+       if (!context)
+               free(ctxt);
+
+       return(ret);
+}
diff --git a/libmultipath/Makefile b/libmultipath/Makefile
new file mode 100644 (file)
index 0000000..778297d
--- /dev/null
@@ -0,0 +1,36 @@
+# Makefile
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr>
+#
+BUILD = glibc
+
+include ../Makefile.inc
+
+OBJS = memory.o parser.o vector.o devmapper.o callout.o \
+       hwtable.o blacklist.o util.o dmparser.o config.o \
+       structs.o cache.o discovery.o propsel.o dict.o \
+       pgpolicies.o debug.o regex.o defaults.o uevent.o
+
+CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes
+
+ifeq ($(strip $(DAEMON)),1)
+       CFLAGS += -DDAEMON
+endif
+
+all: $(BUILD)
+
+prepare:
+       rm -f core *.o *.gz
+
+klibc: prepare $(OBJS)
+       ar rs libmultipath-klibc.a *.o
+
+glibc: prepare $(OBJS)
+       ar rs libmultipath-glibc.a *.o
+
+install:
+
+uninstall:
+
+clean:
+       rm -f core *.a *.o *.gz
diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
new file mode 100644 (file)
index 0000000..4ba9ef4
--- /dev/null
@@ -0,0 +1,111 @@
+#include <stdio.h>
+
+#include "memory.h"
+#include "vector.h"
+#include "util.h"
+#include "debug.h"
+#include "regex.h"
+#include "blacklist.h"
+
+static int
+store_ble (vector blist, char * str)
+{
+       struct blentry * ble;
+       
+       if (!str)
+               return 0;
+
+       ble = (struct blentry *)MALLOC(sizeof(struct blentry));
+
+       if (!ble)
+               goto out;
+
+       ble->preg = MALLOC(sizeof(regex_t));
+
+       if (!ble->preg)
+               goto out1;
+
+       ble->str = (char *)MALLOC(strlen(str) + 1);
+
+       if (!ble->str)
+               goto out2;
+
+       strcpy(ble->str, str);
+
+       if (regcomp((regex_t *)ble->preg, ble->str, REG_EXTENDED|REG_NOSUB))
+               goto out3;
+
+       if (!vector_alloc_slot(blist))
+               goto out3;
+
+       vector_set_slot(blist, ble);
+       return 0;
+out3:
+       FREE(ble->str);
+out2:
+       FREE(ble->preg);
+out1:
+       FREE(ble);
+out:
+       return 1;
+}
+
+int
+setup_default_blist (vector blist)
+{
+       int r = 0;
+
+       r += store_ble(blist, "(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*");
+       r += store_ble(blist, "hd[a-z]");
+       r += store_ble(blist, "cciss!c[0-9]d[0-9]*");
+
+       return r;
+}
+
+int
+blacklist (vector blist, char * dev)
+{
+       int i;
+       struct blentry *ble;
+
+       vector_foreach_slot (blist, ble, i) {
+               if (!regexec(ble->preg, dev, 0, NULL, 0)) {
+                       condlog(3, "%s blacklisted", dev);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int
+store_regex (vector blist, char * regex)
+{
+       if (!blist)
+               return 1;
+
+       if (!regex)
+               return 1;
+
+       return store_ble(blist, regex);
+}      
+
+void
+free_blacklist (vector blist)
+{
+       struct blentry * ble;
+       int i;
+
+       if (!blist)
+               return;
+
+       vector_foreach_slot (blist, ble, i) {
+               if (ble->str)
+                       FREE(ble->str);
+
+               if (ble->preg)
+                       FREE(ble->preg);
+
+               FREE(ble);
+       }
+       vector_free(blist);
+}
diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h
new file mode 100644 (file)
index 0000000..1b0fc05
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _BLACKLIST_H
+#define _BLACKLIST_H
+
+#define BLIST_ENTRY_SIZE 255
+
+struct blentry {
+       char * str;
+       void * preg;
+};
+
+int setup_default_blist (vector blist);
+int blacklist (vector blist, char * dev);
+int store_regex (vector blist, char * regex);
+void free_blacklist (vector blist);
+
+#endif /* _BLACKLIST_H */
diff --git a/libmultipath/cache.c b/libmultipath/cache.c
new file mode 100644 (file)
index 0000000..de5bb17
--- /dev/null
@@ -0,0 +1,129 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <time.h>
+
+#include "vector.h"
+#include "structs.h"
+#include "debug.h"
+#include "cache.h"
+
+static void
+revoke_cache_info(struct path * pp)
+{
+       pp->fd = 0;
+}
+
+static int
+lock_fd (int fd, int flag)
+{
+       struct flock fl;
+
+       fl.l_type = flag;
+       fl.l_whence = 0;
+       fl.l_start = 0;
+       fl.l_len = 0;
+
+       alarm(MAX_WAIT);
+
+       if (fcntl(fd, F_SETLKW, &fl) == -1) {
+               condlog(0, "can't take a write lease on cache file\n");
+               return 1;
+       }
+       alarm(0);
+       return 0;
+}
+
+int
+cache_load (vector pathvec)
+{
+       int fd;
+       int r = 1;
+       off_t record_len;
+       struct path record;
+       struct path * pp;
+
+       fd = open(CACHE_FILE, O_RDONLY);
+
+       if (fd < 0)
+               return 1;
+
+       if (lock_fd(fd, F_RDLCK))
+               goto out;
+
+       record_len = sizeof(struct path);
+
+       while (read(fd, &record, record_len)) {
+               pp = alloc_path();
+
+               if (!pp)
+                       goto out;
+
+               if (!vector_alloc_slot(pathvec)) {
+                       free_path(pp);
+                       goto out;
+               }
+               vector_set_slot(pathvec, pp);
+               memcpy(pp, &record, record_len);
+               revoke_cache_info(pp);
+       }
+       r = 0;
+       lock_fd(fd, F_UNLCK);
+out:
+       close(fd);
+       return r;
+}
+
+int
+cache_dump (vector pathvec)
+{
+       int i;
+       int fd;
+       int r = 1;
+       off_t record_len;
+       struct path * pp;
+
+       fd = open(CACHE_TMPFILE, O_RDWR|O_CREAT, 0600);
+
+       if (fd < 0)
+               return 1;
+
+       if (lock_fd(fd, F_WRLCK))
+               goto out;
+
+       ftruncate(fd, 0); 
+       record_len = sizeof(struct path);
+
+       vector_foreach_slot (pathvec, pp, i) {
+               if (write(fd, pp, record_len) < record_len)
+                       goto out1;
+       }
+       rename(CACHE_TMPFILE, CACHE_FILE);
+       r = 0;
+out1:
+       lock_fd(fd, F_UNLCK);
+out:
+       close(fd);
+       return r;
+}
+
+int
+cache_cold (int expire)
+{
+       time_t t;
+       struct stat s;
+
+       if (time(&t) < 0)
+               return 1;
+
+       if(stat(CACHE_FILE, &s))
+               return 1;
+
+       if ((t - s.st_mtime) < expire)
+               return 0;
+
+       return 1;
+}
diff --git a/libmultipath/cache.h b/libmultipath/cache.h
new file mode 100644 (file)
index 0000000..aafdb4c
--- /dev/null
@@ -0,0 +1,8 @@
+#define CACHE_FILE     "/var/cache/multipath/.multipath.cache"
+#define CACHE_TMPFILE  "/var/cache/multipath/.multipath.cache.tmp"
+#define CACHE_EXPIRE   5
+#define MAX_WAIT       5
+
+int cache_load (vector pathvec);
+int cache_dump (vector pathvec);
+int cache_cold (int expire);
diff --git a/libmultipath/callout.c b/libmultipath/callout.c
new file mode 100644 (file)
index 0000000..f891a69
--- /dev/null
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#define PROGRAM_SIZE   100
+#define FIELD_PROGRAM
+
+#define strfieldcpy(to, from) \
+do { \
+       to[sizeof(to)-1] = '\0'; \
+       strncpy(to, from, sizeof(to)-1); \
+} while (0)
+
+int execute_program(char *path, char *value, int len)
+{
+       int retval;
+       int count;
+       int status;
+       int fds[2];
+       pid_t pid;
+       char *pos;
+       char arg[PROGRAM_SIZE];
+       char *argv[sizeof(arg) / 2];
+       int i;
+
+       i = 0;
+
+       if (strchr(path, ' ')) {
+               strfieldcpy(arg, path);
+               pos = arg;
+               while (pos != NULL) {
+                       if (pos[0] == '\'') {
+                               /* don't separate if in apostrophes */
+                               pos++;
+                               argv[i] = strsep(&pos, "\'");
+                               while (pos[0] == ' ')
+                                       pos++;
+                       } else {
+                               argv[i] = strsep(&pos, " ");
+                       }
+                       i++;
+               }
+       } else {
+               argv[i++] = path;
+       }
+       argv[i] =  NULL;
+
+       retval = pipe(fds);
+
+       if (retval != 0)
+               return -1;
+
+
+       pid = fork();
+
+       switch(pid) {
+       case 0:
+               /* child */
+               close(STDOUT_FILENO);
+
+               /* dup write side of pipe to STDOUT */
+               dup(fds[1]);
+
+               retval = execv(argv[0], argv);
+
+               exit(-1);
+       case -1:
+               return -1;
+       default:
+               /* parent reads from fds[0] */
+               close(fds[1]);
+               retval = 0;
+               i = 0;
+               while (1) {
+                       count = read(fds[0], value + i, len - i-1);
+                       if (count <= 0)
+                               break;
+
+                       i += count;
+                       if (i >= len-1) {
+                               retval = -1;
+                               break;
+                       }
+               }
+
+               if (count < 0)
+                       retval = -1;
+
+               if (i > 0 && value[i-1] == '\n')
+                       i--;
+               value[i] = '\0';
+
+               wait(&status);
+               close(fds[0]);
+
+               if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
+                       retval = -1;
+       }
+       return retval;
+}
diff --git a/libmultipath/callout.h b/libmultipath/callout.h
new file mode 100644 (file)
index 0000000..a6a731d
--- /dev/null
@@ -0,0 +1 @@
+int execute_program(char *, char *, int);
diff --git a/libmultipath/config.c b/libmultipath/config.c
new file mode 100644 (file)
index 0000000..1de363d
--- /dev/null
@@ -0,0 +1,464 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "memory.h"
+#include "util.h"
+#include "debug.h"
+#include "parser.h"
+#include "dict.h"
+#include "hwtable.h"
+#include "vector.h"
+#include "blacklist.h"
+#include "defaults.h"
+#include "config.h"
+
+#include "../libcheckers/checkers.h"
+
+/*
+ * helper function to draw a list of callout binaries found in the config file
+ */
+extern int
+push_callout(char * callout)
+{
+       int i;
+       char * bin;
+       char * p;
+
+       /*
+        * purge command line arguments
+        */
+       p = callout;
+
+       while (*p != ' ' && *p != '\0')
+               p++;
+
+       if (!conf->binvec)
+               conf->binvec = vector_alloc();
+
+
+       if (!conf->binvec)
+               return 1;
+
+       /*
+        * if this callout is already stored in binvec, don't store it twice
+        */
+       vector_foreach_slot (conf->binvec, bin, i)
+               if (memcmp(bin, callout, p - callout) == 0)
+                       return 0;
+
+       /*
+        * else, store it
+        */
+       bin = MALLOC((p - callout) + 1);
+
+       if (!bin)
+               return 1;
+
+       strncpy(bin, callout, p - callout);
+
+       if (!vector_alloc_slot(conf->binvec))
+               return 1;
+
+       vector_set_slot(conf->binvec, bin);
+
+       return 0;
+}
+
+struct hwentry *
+find_hwe (vector hwtable, char * vendor, char * product)
+{
+       int i;
+       struct hwentry * hwe;
+
+       vector_foreach_slot (hwtable, hwe, i) {
+               if (strcmp_chomp(hwe->vendor, vendor) == 0 &&
+                   (hwe->product[0] == '*' ||
+                   strcmp_chomp(hwe->product, product) == 0))
+                       return hwe;
+       }
+       return NULL;
+}
+
+extern struct mpentry *
+find_mpe (char * wwid)
+{
+       int i;
+       struct mpentry * mpe;
+
+       if (!wwid)
+               return NULL;
+
+       vector_foreach_slot (conf->mptable, mpe, i)
+               if (mpe->wwid && strcmp(mpe->wwid, wwid) == 0)
+                       return mpe;
+
+       return NULL;
+}
+
+extern char *
+get_mpe_wwid (char * alias)
+{
+       int i;
+       struct mpentry * mpe;
+
+       if (!alias)
+               return NULL;
+
+       vector_foreach_slot (conf->mptable, mpe, i)
+               if (mpe->alias && strcmp(mpe->alias, alias) == 0)
+                       return mpe->wwid;
+
+       return NULL;
+}
+
+void
+free_hwe (struct hwentry * hwe)
+{
+       if (!hwe)
+               return;
+
+       if (hwe->vendor)
+               FREE(hwe->vendor);
+
+       if (hwe->product)
+               FREE(hwe->product);
+
+       if (hwe->selector)
+               FREE(hwe->selector);
+
+       if (hwe->getuid)
+               FREE(hwe->getuid);
+
+       if (hwe->getprio)
+               FREE(hwe->getprio);
+
+       if (hwe->features)
+               FREE(hwe->features);
+
+       if (hwe->hwhandler)
+               FREE(hwe->hwhandler);
+
+       FREE(hwe);
+}
+
+void
+free_hwtable (vector hwtable)
+{
+       int i;
+       struct hwentry * hwe;
+
+       if (!hwtable)
+               return;
+
+       vector_foreach_slot (hwtable, hwe, i)
+               free_hwe(hwe);
+
+       vector_free(hwtable);
+}
+
+void
+free_mpe (struct mpentry * mpe)
+{
+       if (!mpe)
+               return;
+
+       if (mpe->wwid)
+               FREE(mpe->wwid);
+
+       if (mpe->selector)
+               FREE(mpe->selector);
+
+       if (mpe->getuid)
+               FREE(mpe->getuid);
+
+       if (mpe->alias)
+               FREE(mpe->alias);
+
+       FREE(mpe);
+}
+
+void
+free_mptable (vector mptable)
+{
+       int i;
+       struct mpentry * mpe;
+
+       if (!mptable)
+               return;
+
+       vector_foreach_slot (mptable, mpe, i)
+               free_mpe(mpe);
+
+       vector_free(mptable);
+}
+
+static struct hwentry *
+alloc_hwe (void)
+{
+       return (struct hwentry *)MALLOC(sizeof(struct hwentry));
+}
+
+static char *
+set_param_str(char * str)
+{
+       char * dst;
+       int len;
+
+       if (!str)
+               return NULL;
+
+       len = strlen(str);
+
+       if (!len)
+               return NULL;
+
+       dst = (char *)MALLOC(len + 1);
+
+       if (!dst)
+               return NULL;
+
+       strcpy(dst, str);
+       return dst;
+}
+
+int
+store_hwe (vector hwtable, char * vendor, char * product, int pgp,
+          char * getuid)
+{
+       struct hwentry * hwe;
+
+       hwe = alloc_hwe();
+
+       if (!hwe)
+               return 1;
+
+       hwe->vendor = set_param_str(vendor);
+
+       if (!hwe->vendor)
+               goto out;
+       
+       hwe->product = set_param_str(product);
+
+       if (!hwe->product)
+               goto out;
+       
+       if (pgp)
+               hwe->pgpolicy = pgp;
+
+       if (getuid) {
+               hwe->getuid = set_param_str(getuid);
+               push_callout(getuid);
+       } else {
+               hwe->getuid = set_default(DEFAULT_GETUID);
+               push_callout(DEFAULT_GETUID);
+       }
+
+       if (!hwe->getuid)
+               goto out;
+       
+       if (!vector_alloc_slot(hwtable))
+               goto out;
+
+       vector_set_slot(hwtable, hwe);
+       return 0;
+out:
+       free_hwe(hwe);
+       return 1;
+}
+
+int
+store_hwe_ext (vector hwtable, char * vendor, char * product, int pgp,
+          char * getuid, char * getprio, char * hwhandler,
+          char * features, char * checker)
+{
+       struct hwentry * hwe;
+
+       hwe = alloc_hwe();
+
+       if (!hwe)
+               return 1;
+
+       hwe->vendor = set_param_str(vendor);
+
+       if (!hwe->vendor)
+               goto out;
+       
+       hwe->product = set_param_str(product);
+
+       if (!hwe->product)
+               goto out;
+       
+       if (pgp)
+               hwe->pgpolicy = pgp;
+
+       if (getuid) {
+               hwe->getuid = set_param_str(getuid);
+               push_callout(getuid);
+       } else {
+               hwe->getuid = set_default(DEFAULT_GETUID);
+               push_callout(DEFAULT_GETUID);
+       }
+
+       if (!hwe->getuid)
+               goto out;
+       
+       if (getprio) {
+               hwe->getprio = set_param_str(getprio);
+               push_callout(getprio);
+       } else
+               hwe->getprio = NULL;
+
+       if (hwhandler)  
+               hwe->hwhandler = set_param_str(hwhandler);
+       else
+               hwe->hwhandler = set_default(DEFAULT_HWHANDLER);
+
+       if (!hwe->hwhandler)
+               goto out;
+
+       if (features)
+               hwe->features = set_param_str(features);
+       else
+               hwe->features = set_default(DEFAULT_FEATURES);
+
+       if (!hwe->features)
+               goto out;
+
+       if (checker)
+               hwe->checker_index = get_checker_id(checker);
+       else
+               hwe->checker_index = get_checker_id(DEFAULT_CHECKER);
+
+       if (!vector_alloc_slot(hwtable))
+               goto out;
+
+       vector_set_slot(hwtable, hwe);
+       return 0;
+out:
+       free_hwe(hwe);
+       return 1;
+}
+
+struct config *
+alloc_config (void)
+{
+       return (struct config *)MALLOC(sizeof(struct config));
+}
+
+void
+free_config (struct config * conf)
+{
+       if (!conf)
+               return;
+
+       if (conf->dev)
+               FREE(conf->dev);
+
+       if (conf->multipath)
+               FREE(conf->multipath);
+
+       if (conf->udev_dir)
+               FREE(conf->udev_dir);
+
+       if (conf->default_selector)
+               FREE(conf->default_selector);
+
+       if (conf->default_getuid)
+               FREE(conf->default_getuid);
+
+       if (conf->default_getprio)
+               FREE(conf->default_getprio);
+
+       if (conf->default_features)
+               FREE(conf->default_features);
+
+       if (conf->default_hwhandler)
+               FREE(conf->default_hwhandler);
+
+       free_blacklist(conf->blist);
+       free_mptable(conf->mptable);
+       free_hwtable(conf->hwtable);
+       free_strvec(conf->binvec);
+
+       FREE(conf);
+}
+
+int
+load_config (char * file)
+{
+       conf = alloc_config();
+
+       if (!conf)
+               return 1;
+
+       /*
+        * internal defaults
+        */
+       conf->verbosity = 2;
+       conf->signal = 1;               /* 1 == Send a signal to multipathd */
+       conf->dev_type = DEV_NONE;
+       conf->minio = 1000;
+
+       /*
+        * read the config file
+        */
+       if (filepresent(file)) {
+               if (init_data(file, init_keywords)) {
+                       condlog(0, "error parsing config file");
+                       goto out;
+               }
+       }
+       
+       /*
+        * fill the voids left in the config file
+        */
+       if (conf->hwtable == NULL) {
+               conf->hwtable = vector_alloc();
+               
+               if (!conf->hwtable)
+                       goto out;
+               
+               if (setup_default_hwtable(conf->hwtable))
+                       goto out;
+       }
+       if (conf->blist == NULL) {
+               conf->blist = vector_alloc();
+               
+               if (!conf->blist)
+                       goto out;
+               
+               if (setup_default_blist(conf->blist))
+                       goto out;
+       }
+       if (conf->mptable == NULL) {
+               conf->mptable = vector_alloc();
+
+               if (!conf->mptable)
+                       goto out;
+       }
+       if (conf->default_selector == NULL)
+               conf->default_selector = set_default(DEFAULT_SELECTOR);
+
+       if (conf->udev_dir == NULL)
+               conf->udev_dir = set_default(DEFAULT_UDEVDIR);
+
+       if (conf->default_getuid == NULL)
+               conf->default_getuid = set_default(DEFAULT_GETUID);
+
+       if (conf->default_features == NULL)
+               conf->default_features = set_default(DEFAULT_FEATURES);
+
+       if (conf->default_hwhandler == NULL)
+               conf->default_hwhandler = set_default(DEFAULT_HWHANDLER);
+
+       if (!conf->default_selector  || !conf->udev_dir         ||
+           !conf->default_getuid    || !conf->default_features ||
+           !conf->default_hwhandler)
+               goto out;
+
+       return 0;
+out:
+       free_config(conf);
+       return 1;
+}
+
diff --git a/libmultipath/config.h b/libmultipath/config.h
new file mode 100644 (file)
index 0000000..455ee25
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#ifndef _VECTOR_H
+#include "vector.h"
+#endif
+
+enum devtypes {
+       DEV_NONE,
+       DEV_DEVT,
+       DEV_DEVNODE,
+       DEV_DEVMAP
+};
+
+struct hwentry {
+       int selector_args;
+       int pgpolicy;
+       int checker_index;
+
+       char * vendor;
+       char * product;
+       char * selector;
+       char * getuid;
+       char * getprio;
+       char * features;
+       char * hwhandler;
+};
+
+struct mpentry {
+       int selector_args;
+       int pgpolicy;
+
+       char * wwid;
+       char * selector;
+       char * getuid;
+       char * alias;
+};
+
+struct config {
+       int verbosity;
+       int dry_run;
+       int list;
+       int signal;
+       int pgpolicy_flag;
+       int with_sysfs;
+       int default_selector_args;
+       int default_pgpolicy;
+       int dev_type;
+       int minio;
+       int checkint;
+
+       char * dev;
+       char * multipath;
+       char * udev_dir;
+       char * default_selector;
+       char * default_getuid;
+       char * default_getprio;
+       char * default_features;
+       char * default_hwhandler;
+
+       vector mptable;
+       vector hwtable;
+       vector blist;
+       vector binvec;
+};
+
+struct config * conf;
+
+extern int push_callout(char * callout);
+
+struct hwentry * find_hwe (vector hwtable, char * vendor, char * product);
+struct mpentry * find_mpe (char * wwid);
+char * get_mpe_wwid (char * alias);
+
+void free_hwe (struct hwentry * hwe);
+void free_hwtable (vector hwtable);
+void free_mpe (struct mpentry * mpe);
+void free_mptable (vector mptable);
+
+int store_hwe (vector hwtable, char * vendor, char * product, int pgp,
+               char * getuid);
+int store_hwe_ext (vector hwtable, char * vendor, char * product, int pgp,
+               char * getuid, char * getprio, char * hwhandler,
+               char * features, char * checker);
+
+int load_config (char * file);
+struct config * alloc_config (void);
+void free_config (struct config * conf);
+
+#endif
diff --git a/libmultipath/debug.c b/libmultipath/debug.c
new file mode 100644 (file)
index 0000000..dc69d6f
--- /dev/null
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "config.h"
+
+void condlog (int prio, char * fmt, ...)
+{
+       va_list ap;
+       int thres;
+
+       if (!conf)
+               thres = 0;
+       else
+               thres = conf->verbosity;
+
+       va_start(ap, fmt);
+
+       if (prio <= thres) {
+               vfprintf(stdout, fmt, ap);
+               fprintf(stdout, "\n");
+       }
+       va_end(ap);
+}
diff --git a/libmultipath/debug.h b/libmultipath/debug.h
new file mode 100644 (file)
index 0000000..727a9fd
--- /dev/null
@@ -0,0 +1,8 @@
+void condlog (int prio, char * fmt, ...);
+
+#if DAEMON
+#include <pthread.h>
+#include "../multipathd/log_pthread.h"
+#define condlog(prio, fmt, args...) \
+       log_safe(prio + 3, fmt, ##args)
+#endif
diff --git a/libmultipath/defaults.c b/libmultipath/defaults.c
new file mode 100644 (file)
index 0000000..3b8ecff
--- /dev/null
@@ -0,0 +1,20 @@
+#include <string.h>
+
+#include "memory.h"
+
+char *
+set_default (char * str)
+{
+       int len;
+       char * p;
+
+       len = strlen(str);
+       p = MALLOC(len + 1);
+
+       if (!p)
+               return NULL;
+
+       strncat(p, str, len);
+
+       return p;
+}
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
new file mode 100644 (file)
index 0000000..9a8178d
--- /dev/null
@@ -0,0 +1,13 @@
+#define DEFAULT_GETUID         "/sbin/scsi_id -g -u -s /block/%n"
+#define DEFAULT_UDEVDIR                "/dev"
+#define DEFAULT_SELECTOR       "round-robin 0"
+#define DEFAULT_FEATURES       "0"
+#define DEFAULT_HWHANDLER      "0"
+#define DEFAULT_CHECKER                "readsector0"
+
+#define DEFAULT_TARGET         "multipath"
+#define DEFAULT_PIDFILE                "/var/run/multipathd.pid"
+#define DEFAULT_RUNFILE                "/var/run/multipath.run"
+#define DEFAULT_CONFIGFILE     "/etc/multipath.conf"
+
+char * set_default (char * str);
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
new file mode 100644 (file)
index 0000000..98739ac
--- /dev/null
@@ -0,0 +1,485 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libdevmapper.h>
+#include <ctype.h>
+#include <linux/kdev_t.h>
+
+#include "vector.h"
+#include "structs.h"
+#include "debug.h"
+#include "memory.h"
+
+extern int
+dm_prereq (char * str, int x, int y, int z)
+{
+       int r = 1;
+       struct dm_task *dmt;
+       struct dm_versions *target;
+       struct dm_versions *last_target;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
+               return 1;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       target = dm_task_get_versions(dmt);
+
+       /* Fetch targets and print 'em */
+       do {
+               last_target = target;
+
+               if (!strncmp(str, target->name, strlen(str)) &&
+                   /* dummy prereq on multipath version */
+                   target->version[0] >= x &&
+                   target->version[1] >= y &&
+                   target->version[2] >= z
+                  )
+                       r = 0;
+
+               target = (void *) target + target->next;
+       } while (last_target != target);
+
+       out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+extern int
+dm_simplecmd (int task, const char *name) {
+       int r = 0;
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create (task)))
+               return 0;
+
+       if (!dm_task_set_name (dmt, name))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       r = dm_task_run (dmt);
+
+       out:
+       dm_task_destroy (dmt);
+       return r;
+}
+
+extern int
+dm_addmap (int task, const char *name, const char *target,
+          const char *params, unsigned long size) {
+       int r = 0;
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create (task)))
+               return 0;
+
+       if (!dm_task_set_name (dmt, name))
+               goto addout;
+
+       if (!dm_task_add_target (dmt, 0, size, target, params))
+               goto addout;
+
+       dm_task_no_open_count(dmt);
+
+       r = dm_task_run (dmt);
+
+       addout:
+       dm_task_destroy (dmt);
+       return r;
+}
+
+extern int
+dm_map_present (char * str)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_info info;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, str))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info))
+               goto out;
+
+       if (info.exists)
+               r = 1;
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+extern int
+dm_get_map(char * name, unsigned long * size, char * outparams)
+{
+       int r = 1;
+       struct dm_task *dmt;
+       void *next = NULL;
+       uint64_t start, length;
+       char *target_type = NULL;
+       char *params = NULL;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
+               return 1;
+
+       if (!dm_task_set_name(dmt, name))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       /* Fetch 1st target */
+       next = dm_get_next_target(dmt, next, &start, &length,
+                                 &target_type, &params);
+
+       if (size)
+               *size = length;
+
+       if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
+               r = 0;
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+extern int
+dm_get_status(char * name, char * outstatus)
+{
+       int r = 1;
+       struct dm_task *dmt;
+       void *next = NULL;
+       uint64_t start, length;
+       char *target_type;
+       char *status;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
+               return 1;
+
+       if (!dm_task_set_name(dmt, name))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       /* Fetch 1st target */
+       next = dm_get_next_target(dmt, next, &start, &length,
+                                 &target_type, &status);
+
+       if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
+               r = 0;
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+extern int
+dm_type(char * name, char * type)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       void *next = NULL;
+       uint64_t start, length;
+       char *target_type = NULL;
+       char *params;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, name))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       /* Fetch 1st target */
+       next = dm_get_next_target(dmt, next, &start, &length,
+                                 &target_type, &params);
+
+       if (0 == strcmp(target_type, type))
+               r = 1;
+
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+int
+dm_get_opencount (char * mapname)
+{
+       int r = -1;
+       struct dm_task *dmt;
+       struct dm_info info;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, mapname))
+               goto out;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info))
+               goto out;
+
+       r = info.open_count;
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+       
+extern int
+dm_flush_maps (char * type)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_names *names;
+       unsigned next = 0;
+
+       if (!(dmt = dm_task_create (DM_DEVICE_LIST)))
+               return 0;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run (dmt))
+               goto out;
+
+       if (!(names = dm_task_get_names (dmt)))
+               goto out;
+
+       if (!names->dev)
+               goto out;
+
+       do {
+               if (dm_type(names->name, type) &&
+                   dm_get_opencount(names->name) == 0 &&
+                   !dm_simplecmd(DM_DEVICE_REMOVE, names->name))
+                       r++;
+
+               next = names->next;
+               names = (void *) names + next;
+       } while (next);
+
+       out:
+       dm_task_destroy (dmt);
+       return r;
+}
+
+int
+dm_fail_path(char * mapname, char * path)
+{
+       int r = 1;
+       struct dm_task *dmt;
+       char str[32];
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
+               return 1;
+
+       if (!dm_task_set_name(dmt, mapname))
+               goto out;
+
+       if (!dm_task_set_sector(dmt, 0))
+               goto out;
+
+       if (snprintf(str, 32, "fail_path %s\n", path) > 32)
+               goto out;
+
+       if (!dm_task_set_message(dmt, str))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       r = 0;
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+int
+dm_reinstate(char * mapname, char * path)
+{
+       int r = 1;
+       struct dm_task *dmt;
+       char str[32];
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
+               return 1;
+
+       if (!dm_task_set_name(dmt, mapname))
+               goto out;
+
+       if (!dm_task_set_sector(dmt, 0))
+               goto out;
+
+       if (snprintf(str, 32, "reinstate_path %s\n", path) > 32)
+               goto out;
+
+       if (!dm_task_set_message(dmt, str))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       r = 0;
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+int
+dm_switchgroup(char * mapname, int index)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       char str[24];
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, mapname))
+               goto out;
+
+       if (!dm_task_set_sector(dmt, 0))
+               goto out;
+
+       snprintf(str, 24, "switch_group %i\n", index);
+       condlog(3, "message %s 0 %s", mapname, str);
+
+       if (!dm_task_set_message(dmt, str))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       r = 1;
+
+       out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+int
+dm_get_maps (vector mp, char * type)
+{
+       struct multipath * mpp;
+       int r = 1;
+       struct dm_task *dmt;
+       struct dm_names *names;
+       unsigned next = 0;
+
+       if (!type || !mp)
+               return 1;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+               return 1;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!(names = dm_task_get_names(dmt)))
+               goto out;
+
+       if (!names->dev) {
+               r = 0; /* this is perfectly valid */
+               goto out;
+       }
+
+       do {
+               if (dm_type(names->name, type)) {
+                       mpp = (struct multipath *)
+                               MALLOC(sizeof(struct multipath));
+
+                       if (!mpp)
+                               goto out;
+
+                       if (dm_get_map(names->name, &mpp->size, mpp->params))
+                               goto out1;
+
+                       if (dm_get_status(names->name, mpp->status))
+                               goto out1;
+
+                       mpp->alias = MALLOC(strlen(names->name) + 1);
+
+                       if (!mpp->alias)
+                               goto out1;
+
+                       strncat(mpp->alias, names->name, strlen(names->name));
+
+                       if (!vector_alloc_slot(mp))
+                               goto out1;
+                       
+                       vector_set_slot(mp, mpp);
+                       mpp = NULL;
+               }
+                next = names->next;
+                names = (void *) names + next;
+       } while (next);
+
+       r = 0;
+       goto out;
+out1:
+       free_multipath(mpp, KEEP_PATHS);
+out:
+       dm_task_destroy (dmt);
+       return r;
+}
+
+int
+dm_geteventnr (char *name)
+{
+       struct dm_task *dmt;
+       struct dm_info info;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, name))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info)) {
+               info.event_nr = 0;
+               goto out;
+       }
+
+       if (!info.exists) {
+               info.event_nr = 0;
+               goto out;
+       }
+
+out:
+       dm_task_destroy(dmt);
+
+       return info.event_nr;
+}
diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
new file mode 100644 (file)
index 0000000..207ad4f
--- /dev/null
@@ -0,0 +1,13 @@
+int dm_prereq (char *, int, int, int);
+int dm_simplecmd (int, const char *);
+int dm_addmap (int, const char *, const char *, const char *, unsigned long);
+int dm_map_present (char *);
+int dm_get_map(char *, unsigned long *, char *);
+int dm_get_status(char *, char *);
+int dm_type(char *, char *);
+int dm_flush_maps (char *);
+int dm_fail_path(char * mapname, char * path);
+int dm_reinstate(char * mapname, char * path);
+int dm_switchgroup(char * mapname, int index);
+int dm_get_maps (vector mp, char * type);
+int dm_geteventnr (char *name);
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
new file mode 100644 (file)
index 0000000..2c1e8ba
--- /dev/null
@@ -0,0 +1,492 @@
+#include "vector.h"
+#include "hwtable.h"
+#include "structs.h"
+#include "parser.h"
+#include "config.h"
+#include "debug.h"
+#include "memory.h"
+#include "pgpolicies.h"
+#include "blacklist.h"
+
+#include "../libcheckers/checkers.h"
+
+/*
+ * default block handlers
+ */
+static int
+multipath_tool_handler(vector strvec)
+{
+       conf->multipath = set_value(strvec);
+
+       if (!conf->multipath)
+               return 1;
+
+       return push_callout(conf->multipath);
+}
+
+static int
+polling_interval_handler(vector strvec)
+{
+       char * buff;
+
+       buff = VECTOR_SLOT(strvec, 1);
+       conf->checkint = atoi(buff);
+
+       return 0;
+}
+
+static int
+udev_dir_handler(vector strvec)
+{
+       conf->udev_dir = set_value(strvec);
+
+       if (!conf->udev_dir)
+               return 1;
+
+       return 0;
+}
+
+static int
+def_selector_handler(vector strvec)
+{
+       conf->default_selector = set_value(strvec);
+
+       if (!conf->default_selector)
+               return 1;
+
+       return 0;
+}
+
+static int
+def_pgpolicy_handler(vector strvec)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       conf->default_pgpolicy = get_pgpolicy_id(buff);
+       FREE(buff);
+
+       return 0;
+}
+
+static int
+def_getuid_callout_handler(vector strvec)
+{
+       conf->default_getuid = set_value(strvec);
+
+       if (!conf->default_getuid)
+               return 1;
+       
+       return push_callout(conf->default_getuid);
+}
+
+static int
+def_prio_callout_handler(vector strvec)
+{
+       conf->default_getprio = set_value(strvec);
+
+       if (!conf->default_getprio)
+               return 1;
+       
+       if (!strncmp(conf->default_getprio, "none", 4)) {
+               FREE(conf->default_getprio);
+               conf->default_getprio = NULL;
+               return 0;
+       }
+               
+       return push_callout(conf->default_getprio);
+}
+
+static int
+def_features_handler(vector strvec)
+{
+       conf->default_features = set_value(strvec);
+
+       if (!conf->default_features)
+               return 1;
+
+       return 0;
+}
+
+static int
+def_minio_handler(vector strvec)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       conf->minio = atoi(buff);
+       FREE(buff);
+
+       return 0;
+}
+
+/*
+ * blacklist block handlers
+ */
+static int
+blacklist_handler(vector strvec)
+{
+       conf->blist = vector_alloc();
+
+       if (!conf->blist)
+               return 1;
+
+       return 0;
+}
+
+static int
+ble_handler(vector strvec)
+{
+       char * buff;
+       int ret;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       ret = store_regex(conf->blist, buff);
+       FREE(buff);
+
+       return ret;
+}
+
+/*
+ * devices block handlers
+ */
+static int
+devices_handler(vector strvec)
+{
+       conf->hwtable = vector_alloc();
+
+       if (!conf->hwtable)
+               return 1;
+
+       return 0;
+}
+
+static int
+device_handler(vector strvec)
+{
+       struct hwentry * hwe;
+
+       hwe = (struct hwentry *)MALLOC(sizeof(struct hwentry));
+
+       if (!hwe)
+               return 1;
+
+       if (!vector_alloc_slot(conf->hwtable)) {
+               FREE(hwe);
+               return 1;
+       }
+       vector_set_slot(conf->hwtable, hwe);
+
+       return 0;
+}
+
+static int
+vendor_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+       if (!hwe)
+               return 1;
+       
+       hwe->vendor = set_value(strvec);
+
+       if (!hwe->vendor)
+               return 1;
+
+       return 0;
+}
+
+static int
+product_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+       if (!hwe)
+               return 1;
+       
+       hwe->product = set_value(strvec);
+
+       if (!hwe->product)
+               return 1;
+
+       return 0;
+}
+
+static int
+hw_pgpolicy_handler(vector strvec)
+{
+       char * buff;
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       hwe->pgpolicy = get_pgpolicy_id(buff);
+       FREE(buff);
+
+       return 0;
+}
+
+static int
+hw_getuid_callout_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+       hwe->getuid = set_value(strvec);
+
+       if (!hwe->getuid)
+               return 1;
+
+       return push_callout(hwe->getuid);
+}
+
+static int
+hw_selector_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+       
+       if (!hwe)
+               return 1;
+
+       hwe->selector = set_value(strvec);
+
+       if (!hwe->selector)
+               return 1;
+
+       return 0;
+}
+
+static int
+hw_path_checker_handler(vector strvec)
+{
+       char * buff;
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+       if (!hwe)
+               return 1;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+       
+       hwe->checker_index = get_checker_id(buff);
+       FREE(buff);
+
+       return 0;
+}
+
+static int
+hw_features_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+       
+       if (!hwe)
+               return 1;
+
+       hwe->features = set_value(strvec);
+
+       if (!hwe->features)
+               return 1;
+
+       return 0;
+}
+
+static int
+hw_handler_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+       
+       if (!hwe)
+               return 1;
+
+       hwe->hwhandler = set_value(strvec);
+
+       if (!hwe->hwhandler)
+               return 1;
+
+       return 0;
+}
+
+static int
+prio_callout_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+       
+       if (!hwe)
+               return 1;
+
+       hwe->getprio = set_value(strvec);
+
+       if (!hwe->getprio)
+               return 1;
+
+       if (!strncmp(hwe->getprio, "none", 4)) {
+               FREE(hwe->getprio);
+               hwe->getprio = NULL;
+               return 0;
+       }
+
+       return push_callout(hwe->getprio);
+}
+
+/*
+ * multipaths block handlers
+ */
+static int
+multipaths_handler(vector strvec)
+{
+       conf->mptable = vector_alloc();
+
+       if (!conf->mptable)
+               return 1;
+
+       return 0;
+}
+
+static int
+multipath_handler(vector strvec)
+{
+       struct mpentry * mpe;
+
+       mpe = (struct mpentry *)MALLOC(sizeof(struct mpentry));
+
+       if (!mpe)
+               return 1;
+
+       if (!vector_alloc_slot(conf->mptable)) {
+               FREE(mpe);
+               return 1;
+       }
+       vector_set_slot(conf->mptable, mpe);
+
+       return 0;
+}
+
+static int
+wwid_handler(vector strvec)
+{
+       struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
+
+       if (!mpe)
+               return 1;
+
+       mpe->wwid = set_value(strvec);
+
+       if (!mpe->wwid)
+               return 1;
+
+       return 0;
+}
+
+static int
+alias_handler(vector strvec)
+{
+       struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
+
+       if (!mpe)
+               return 1;
+
+        mpe->alias = set_value(strvec);
+
+       if (!mpe->alias)
+               return 1;
+
+       return 0;
+}
+
+static int
+mp_pgpolicy_handler(vector strvec)
+{
+       char * buff;
+       struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
+
+       if (!mpe)
+               return 1;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       mpe->pgpolicy = get_pgpolicy_id(buff);
+       FREE(buff);
+
+       return 0;
+}
+
+static int
+mp_selector_handler(vector strvec)
+{
+       struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
+       
+       if (!mpe)
+               return 1;
+
+       mpe->selector = set_value(strvec);
+
+       if (!mpe->selector)
+               return 1;
+
+       return 0;
+}
+
+vector
+init_keywords(void)
+{
+       keywords = vector_alloc();
+
+       install_keyword_root("defaults", NULL);
+       install_keyword("polling_interval", &polling_interval_handler);
+       install_keyword("multipath_tool", &multipath_tool_handler);
+       install_keyword("udev_dir", &udev_dir_handler);
+       install_keyword("default_selector", &def_selector_handler);
+       install_keyword("default_path_grouping_policy", &def_pgpolicy_handler);
+       install_keyword("default_getuid_callout", &def_getuid_callout_handler);
+       install_keyword("default_prio_callout", &def_prio_callout_handler);
+       install_keyword("default_features", &def_features_handler);
+       install_keyword("rr_min_io", &def_minio_handler);
+       
+       install_keyword_root("devnode_blacklist", &blacklist_handler);
+       install_keyword("devnode", &ble_handler);
+       install_keyword("wwid", &ble_handler);
+
+       install_keyword_root("devices", &devices_handler);
+       install_keyword("device", &device_handler);
+       install_sublevel();
+       install_keyword("vendor", &vendor_handler);
+       install_keyword("product", &product_handler);
+       install_keyword("path_grouping_policy", &hw_pgpolicy_handler);
+       install_keyword("getuid_callout", &hw_getuid_callout_handler);
+       install_keyword("path_selector", &hw_selector_handler);
+       install_keyword("path_checker", &hw_path_checker_handler);
+       install_keyword("features", &hw_features_handler);
+       install_keyword("hardware_handler", &hw_handler_handler);
+       install_keyword("prio_callout", &prio_callout_handler);
+       install_sublevel_end();
+
+       install_keyword_root("multipaths", &multipaths_handler);
+       install_keyword("multipath", &multipath_handler);
+       install_sublevel();
+       install_keyword("wwid", &wwid_handler);
+       install_keyword("alias", &alias_handler);
+       install_keyword("path_grouping_policy", &mp_pgpolicy_handler);
+       install_keyword("path_selector", &mp_selector_handler);
+       install_sublevel_end();
+
+       return keywords;
+}
diff --git a/libmultipath/dict.h b/libmultipath/dict.h
new file mode 100644 (file)
index 0000000..ac35edc
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _DICT_H
+#define _DICT_H
+
+#ifndef _VECTOR_H
+#include "vector.h"
+#endif
+
+vector init_keywords(void);
+
+#endif /* _DICT_H */
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
new file mode 100644 (file)
index 0000000..87860eb
--- /dev/null
@@ -0,0 +1,531 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <sysfs/dlist.h>
+#include <sysfs/libsysfs.h>
+
+#include "vector.h"
+#include "memory.h"
+#include "blacklist.h"
+#include "util.h"
+#include "structs.h"
+#include "callout.h"
+#include "config.h"
+#include "debug.h"
+#include "propsel.h"
+#include "sg_include.h"
+#include "discovery.h"
+
+#define readattr(a,b) \
+       sysfs_read_attribute_value(a, b, sizeof(b))
+
+int
+store_pathinfo (vector pathvec, vector hwtable, char * devname)
+{
+       struct path * pp;
+
+       pp = alloc_path();
+
+       if (!pp)
+               return 1;
+
+       if(safe_sprintf(pp->dev, "%s", devname)) {
+               fprintf(stderr, "pp->dev too small\n");
+               goto out;
+       }
+       if (store_path(pathvec, pp))
+               goto out;
+
+       pathinfo(pp, hwtable, DI_ALL);
+
+       return 0;
+out:
+       free_path(pp);
+       return 1;
+}
+int
+path_discovery (vector pathvec, struct config * conf, int flag)
+{
+       struct sysfs_directory * sdir;
+       struct sysfs_directory * devp;
+       char path[FILE_NAME_SIZE];
+       struct path * pp;
+       int r = 1;
+
+       if(safe_sprintf(path, "%s/block", sysfs_path)) {
+               fprintf(stderr, "path too small\n");
+               exit(1);
+       }
+       sdir = sysfs_open_directory(path);
+       sysfs_read_directory(sdir);
+
+       dlist_for_each_data(sdir->subdirs, devp, struct sysfs_directory) {
+               if (blacklist(conf->blist, devp->name))
+                       continue;
+
+               if(safe_sprintf(path, "%s/block/%s/device", sysfs_path,
+                               devp->name)) {
+                       fprintf(stderr, "path too small\n");
+                       exit(1);
+               }
+                               
+               if (!filepresent(path))
+                       continue;
+
+               pp = find_path_by_dev(pathvec, devp->name);
+
+               if (!pp) {
+                       /*
+                        * new path : alloc, store and fetch info
+                        */
+                       if (store_pathinfo(pathvec, conf->hwtable, devp->name))
+                               goto out;
+               } else {
+                       /*
+                        * path already known :
+                        * refresh only what the caller wants
+                        */
+                       pathinfo(pp, conf->hwtable, flag);
+               }
+       }
+       r = 0;
+out:
+       sysfs_close_directory(sdir);
+       return r;
+}
+
+#define declare_sysfs_get_str(fname, fmt) \
+extern int \
+sysfs_get_##fname (char * sysfs_path, char * dev, char * buff, int len) \
+{ \
+       char attr_path[SYSFS_PATH_SIZE]; \
+       char attr_buff[SYSFS_PATH_SIZE]; \
+       int attr_len; \
+\
+       if(safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+               return 1; \
+       if (0 > sysfs_read_attribute_value(attr_path, attr_buff, sizeof(attr_buff))) \
+               return 1; \
+\
+       attr_len = strlen(attr_buff); \
+       if (attr_len < 2 || attr_len - 1 > len) \
+               return 1; \
+\
+       strncpy(buff, attr_buff, attr_len - 1); \
+       buff[attr_len - 1] = '\0'; \
+       return 0; \
+}
+
+declare_sysfs_get_str(vendor, "%s/block/%s/device/vendor");
+declare_sysfs_get_str(model, "%s/block/%s/device/model");
+declare_sysfs_get_str(rev, "%s/block/%s/device/rev");
+declare_sysfs_get_str(dev, "%s/block/%s/dev");
+
+#define declare_sysfs_get_val(fname, fmt) \
+extern unsigned long  \
+sysfs_get_##fname (char * sysfs_path, char * dev) \
+{ \
+       char attr_path[SYSFS_PATH_SIZE]; \
+       char attr_buff[SYSFS_PATH_SIZE]; \
+\
+       if(safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+               return 0; \
+       if (0 > sysfs_read_attribute_value(attr_path, attr_buff, sizeof(attr_buff))) \
+               return 0; \
+\
+       return strtoul(attr_buff, NULL, 0); \
+}
+
+declare_sysfs_get_val(size, "%s/block/%s/size");
+
+static int
+opennode (char * dev, int mode)
+{
+       char devpath[FILE_NAME_SIZE];
+       int fd;
+
+       if (safe_sprintf(devpath, "%s/%s", conf->udev_dir, dev)) {
+               fprintf(stderr, "devpath too small\n");
+               return -1;
+       }
+       fd = open(devpath, mode);
+
+       if (fd <= 0)
+               condlog(0, "open(%s) failed", devpath);
+       
+       return fd;
+}
+
+#if 0
+int
+get_claimed(int fd)
+{
+       /*
+        * FIXME : O_EXCL always fails ?
+        */
+       return 0;
+}      
+#endif
+
+extern int
+devt2devname (char *devname, char *devt)
+{
+       struct sysfs_directory * sdir;
+       struct sysfs_directory * devp;
+       char block_path[FILE_NAME_SIZE];
+       char attr_path[FILE_NAME_SIZE];
+       char attr_value[16];
+       int len;
+
+       if(safe_sprintf(block_path, "%s/block", sysfs_path)) {
+               fprintf(stderr, "block_path too small\n");
+               exit(1);
+       }
+       sdir = sysfs_open_directory(block_path);
+       sysfs_read_directory(sdir);
+
+       dlist_for_each_data (sdir->subdirs, devp, struct sysfs_directory) {
+               if(safe_sprintf(attr_path, "%s/%s/dev",
+                               block_path, devp->name)) {
+                       fprintf(stderr, "attr_path too small\n");
+                       exit(1);
+               }
+               sysfs_read_attribute_value(attr_path, attr_value,
+                                          sizeof(attr_value));
+
+               len = strlen(attr_value);
+
+               /* discard newline */
+               if (len > 1) len--;
+
+               if (strlen(devt) == len &&
+                   strncmp(attr_value, devt, len) == 0) {
+                       if(safe_sprintf(attr_path, "%s/%s",
+                                       block_path, devp->name)) {
+                               fprintf(stderr, "attr_path too small\n");
+                               exit(1);
+                       }
+                       sysfs_get_name_from_path(attr_path, devname,
+                                                FILE_NAME_SIZE);
+                       sysfs_close_directory(sdir);
+                       return 0;
+               }
+       }
+       sysfs_close_directory(sdir);
+       return 1;
+}
+
+static int
+do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+       void *resp, int mx_resp_len, int noisy)
+{
+        unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
+            { INQUIRY_CMD, 0, 0, 0, 0, 0 };
+        unsigned char sense_b[SENSE_BUFF_LEN];
+        struct sg_io_hdr io_hdr;
+                                                                                                                 
+        if (cmddt)
+                inqCmdBlk[1] |= 2;
+        if (evpd)
+                inqCmdBlk[1] |= 1;
+        inqCmdBlk[2] = (unsigned char) pg_op;
+       inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff);
+       inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff);
+        memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof (inqCmdBlk);
+        io_hdr.mx_sb_len = sizeof (sense_b);
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = mx_resp_len;
+        io_hdr.dxferp = resp;
+        io_hdr.cmdp = inqCmdBlk;
+        io_hdr.sbp = sense_b;
+        io_hdr.timeout = DEF_TIMEOUT;
+        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
+                return -1;
+        /* treat SG_ERR here to get rid of sg_err.[ch] */
+        io_hdr.status &= 0x7e;
+        if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
+            (0 == io_hdr.driver_status))
+                return 0;
+        if ((SCSI_CHECK_CONDITION == io_hdr.status) ||
+            (SCSI_COMMAND_TERMINATED == io_hdr.status) ||
+            (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) {
+                if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
+                        int sense_key;
+                        unsigned char * sense_buffer = io_hdr.sbp;
+                        if (sense_buffer[0] & 0x2)
+                                sense_key = sense_buffer[1] & 0xf;
+                        else
+                                sense_key = sense_buffer[2] & 0xf;
+                        if(RECOVERED_ERROR == sense_key)
+                                return 0;
+                }
+        }
+        return -1;
+}
+
+int
+get_serial (char * str, int fd)
+{
+        int len;
+        char buff[MX_ALLOC_LEN + 1];
+
+       if (fd < 0)
+                return 0;
+
+       if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
+               len = buff[3];
+               if (len > 0) {
+                       memcpy(str, buff + 4, len);
+                       buff[len] = '\0';
+               }
+               return 1;
+       }
+        return 0;
+}
+
+extern int
+sysfs_pathinfo(struct path * curpath)
+{
+       char attr_path[FILE_NAME_SIZE];
+       char attr_buff[FILE_NAME_SIZE];
+
+       if (sysfs_get_vendor(sysfs_path, curpath->dev,
+                            curpath->vendor_id, SCSI_VENDOR_SIZE))
+               return 1;
+       condlog(3, "vendor = %s", curpath->vendor_id);
+
+       if (sysfs_get_model(sysfs_path, curpath->dev,
+                           curpath->product_id, SCSI_PRODUCT_SIZE))
+               return 1;
+       condlog(3, "product = %s", curpath->product_id);
+
+       if (sysfs_get_rev(sysfs_path, curpath->dev,
+                         curpath->rev, SCSI_REV_SIZE))
+               return 1;
+       condlog(3, "rev = %s", curpath->rev);
+
+       if (sysfs_get_dev(sysfs_path, curpath->dev,
+                         curpath->dev_t, BLK_DEV_SIZE))
+               return 1;
+       condlog(3, "dev_t = %s", curpath->dev_t);
+
+       curpath->size = sysfs_get_size(sysfs_path, curpath->dev);
+
+       if (curpath->size == 0)
+               return 1;
+       condlog(3, "size = %lu", curpath->size);
+
+       /*
+        * host / bus / target / lun
+        */
+       if(safe_sprintf(attr_path, "%s/block/%s/device",
+                       sysfs_path, curpath->dev)) {
+               fprintf(stderr, "attr_path too small\n");
+               return 1;
+       }
+       if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff)))
+               return 1;
+       basename(attr_buff, attr_path);
+       sscanf(attr_path, "%i:%i:%i:%i",
+                       &curpath->sg_id.host_no,
+                       &curpath->sg_id.channel,
+                       &curpath->sg_id.scsi_id,
+                       &curpath->sg_id.lun);
+       condlog(3, "h:b:t:l = %i:%i:%i:%i",
+                       curpath->sg_id.host_no,
+                       curpath->sg_id.channel,
+                       curpath->sg_id.scsi_id,
+                       curpath->sg_id.lun);
+
+       /*
+        * target node name
+        */
+       if(safe_sprintf(attr_path,
+                       "%s/class/fc_transport/target%i:%i:%i/node_name",
+                       sysfs_path,
+                       curpath->sg_id.host_no,
+                       curpath->sg_id.channel,
+                       curpath->sg_id.scsi_id)) {
+               fprintf(stderr, "attr_path too small\n");
+               return 1;
+       }
+       if (0 <= readattr(attr_path, attr_buff) && strlen(attr_buff) > 0)
+               strncpy(curpath->tgt_node_name, attr_buff,
+                       strlen(attr_buff) - 1);
+       condlog(3, "tgt_node_name = %s", curpath->tgt_node_name);
+
+       return 0;
+}
+
+static int
+apply_format (char * string, char * cmd, struct path * pp)
+{
+       char * pos;
+       char * dst;
+       char * p;
+       int len;
+       int myfree;
+
+       if (!string)
+               return 1;
+
+       if (!cmd)
+               return 1;
+
+       dst = cmd;
+
+       if (!dst)
+               return 1;
+
+       p = dst;
+       pos = strchr(string, '%');
+       myfree = CALLOUT_MAX_SIZE;
+
+       if (!pos) {
+               strcpy(dst, string);
+               return 0;
+       }
+<