1
0
mirror of git://sourceware.org/git/lvm2.git synced 2026-01-25 00:32:58 +03:00

Compare commits

..

24 Commits

Author SHA1 Message Date
Patrick Caulfield
cbe3e5991b Add locking to PV commands. 2001-12-07 09:37:18 +00:00
Patrick Caulfield
3335b34c44 Add locking to VG commands 2001-12-06 15:19:32 +00:00
Patrick Caulfield
6ffc854f9c Made active/open messages print with log_verbose rather than log_very_verbose
and made them a little more user-friendly. This is the way users will be told
which nodes an LV/VG is open on.
2001-12-06 15:17:37 +00:00
Patrick Caulfield
cbe47da378 Add return code to vg_unlock so it knows whether or not to reload the metadata.
Add locking to lvrename.
2001-12-06 13:13:07 +00:00
Patrick Caulfield
dd34a165a1 Add an extra parameter to the unlock functions to tell the cluster not to reload
the metadata at unlock time. We use this if the command failed.
2001-12-06 09:58:51 +00:00
Patrick Caulfield
b2a6905722 Lock LVs by vg/lv name 2001-12-05 14:57:41 +00:00
Patrick Caulfield
3af6e3b294 Return open count correctly when run locally. 2001-12-05 11:56:11 +00:00
Patrick Caulfield
43a6ba01b9 Locking for lvremove and a couple of typo fixes. 2001-12-05 11:55:45 +00:00
Patrick Caulfield
74646b92fb ADd locking to lvresize 2001-12-05 10:11:56 +00:00
Patrick Caulfield
09453f276f Lock by name rather than UUID. (just as well really, the UUIDs are too long for
SLM to lock by)
2001-12-03 14:27:29 +00:00
Patrick Caulfield
2ddd6ebf79 Add local lockfile-based locking 2001-12-03 11:06:13 +00:00
Patrick Caulfield
4da649182f Put back prototypes that got lost in the last merge I did. 2001-11-26 15:52:34 +00:00
Patrick Caulfield
fb38dc166f Add routines for checking for LV and VG activity around the cluster 2001-11-26 15:49:56 +00:00
Patrick Caulfield
3213274526 Send UUIDs round the cluster. Hmmm, need to resolve these at t'other end. 2001-11-23 13:59:25 +00:00
Patrick Caulfield
010c1715c4 Bring forward a few more changes so my tree works with the current devmapper 2001-11-23 13:53:23 +00:00
Patrick Caulfield
67a5321134 Bring Alasdairs 64bit fixes into this tree. 2001-11-23 12:00:52 +00:00
Patrick Caulfield
b590ecf83a Make shared library 2001-11-22 09:37:35 +00:00
Patrick Caulfield
cc296900ae Use new simplified API 2001-11-21 16:09:43 +00:00
Patrick Caulfield
89d9ee62d7 Simplify the API a fair bit and add some actual suspend/resume calls for local
use.
2001-11-21 16:08:46 +00:00
Patrick Caulfield
4cc7c16037 Add a couple of suspend API functions. 2001-11-21 16:07:05 +00:00
Patrick Caulfield
8032fb64cd The first command to call the locking API.
In fact the biggest diff here is to make sure the error path always calls unlock
as stated in the previous checkin. Southerners please take note(!)
2001-11-20 14:23:28 +00:00
Patrick Caulfield
75ef8629d5 - Add suspend flag to the locking API as we don't always want to suspend the
target LV.

- Removed lvm_ prefix from all functions because Joe told me to.

- Added super-high-level functions that keep the mainline code clean - comands
  now just call lock_lvm() and unlock_lvm() [lock/unlock are too glib!] and the
  library takes care of whether we are clustered or not.

It's important that client routines call unlock before finishing not because the
API can't clean up afterwards (it does) but because multiple LVM commands can be
issued from one lvm invocation and thus they are the same client as far as the
CLVMD is concerned.

The local locking paths currently don't do anything.

Thought: it might be worth renaming this bit of the lib "locking" to make it
less obvious that we have clustering code (of sorts) in here. Or maybe that's
just job security through obscurity.
2001-11-20 14:20:49 +00:00
Patrick Caulfield
7a6d29f5d2 Add cmgr into list of include files and objects to build. 2001-11-19 14:54:19 +00:00
Patrick Caulfield
1c1770003f Add cluster manager library. 2001-11-19 14:40:32 +00:00
222 changed files with 8815 additions and 18162 deletions

1
BUGS
View File

@@ -1 +0,0 @@
LVM2's device-mapper driver and ext3 are incompatible at the moment.

340
COPYING
View File

@@ -1,340 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 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.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, 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 software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
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 give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
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 Program, 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 Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) 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; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, 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 executable. 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.
If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.
5. 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 Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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.
7. 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 Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program 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 Program.
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.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 Program
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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -1,483 +0,0 @@
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.
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.
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.
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.
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.
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.
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.
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.
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
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!

36
INSTALL
View File

@@ -1,36 +0,0 @@
LVM2 installation
=================
1) Install device-mapper
Ensure the device-mapper has been installed on the machine.
The device-mapper should be in the kernel (look for 'device-mapper'
messages in the kernel logs) and /usr/include/libdevmapper.h should
be present.
The device-mapper is available from:
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
2) Generate custom makefiles.
Run the 'configure' script from the top directory.
If you do not have GNU readline (http://www.gnu.org/directory/readline.html)
installed use
./configure --disable-readline
3) Build and install LVM2.
Run 'make install' from the top directory.
4) Create a configuration file
The tools will work fine without a configuration file being
present, but you ought to review the example file in doc/example.conf.
For example, specifying the devices that LVM2 is to use should
make the tools run more efficiently - and avoid scanning /dev/cdrom!

18
INTRO
View File

@@ -1,18 +0,0 @@
An introduction to LVM2
=======================
Background
Compatibility with LVM1
New features
Missing features
Future enhancements

View File

@@ -22,13 +22,5 @@ VPATH = @srcdir@
SUBDIRS = include man lib tools
ifeq ($(MAKECMDGOALS),distclean)
SUBDIRS += test/mm test/device test/format1 test/regex test/filters
endif
include make.tmpl
lib: include
tools: include lib

27
README
View File

@@ -1,25 +1,2 @@
This directory contains a beta release of LMV2, the new version of
the userland LVM tools designed for the new device-mapper for
the Linux kernel.
The device-mapper needs to be installed before compiling these LVM2 tools.
For more information about LVM2 read the INTRO file.
Installation instructions are in INSTALL.
This is beta-quality software, released for testing purposes only.
There is no warranty - see COPYING and COPYING.LIB.
Tarballs are available from:
ftp://ftp.sistina.com/pub/LVM2/tools/
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
To access the CVS tree use:
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs login
CVS password: cvs1
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs checkout LVM2
Mailing list for discussion/bug reports etc.
lvm-devel@sistina.com
Subscribe from http://lists.sistina.com/mailman/listinfo/lvm-devel
This is pretty much empty so far...if you can't see subdirectories,
try 'cvs -f update'

53
TODO
View File

@@ -1,28 +1,35 @@
before 2.0
before alpha
------------
FIXME review
extra validation in format1 (must be done before alpha)
status flag review (confirm all use of ->status is consistent)
file system handling (being done)
error message review
correct handling of *existing* snapshots
review statics
review consistency checks for full compatibility with LVM1
iospace split/rename
before beta1
-----------
vgexport
vgimport
snapshots
pvmove
device-mapper support for 2.5 kernel series
review FIXMEs
extra validation & full consistency checks in format1 with LVM1
partial activation (aka VG quorum)
error message review
locking during metadata changes
format2 with atomic transactions
bidirectional format1/format2 migration tool
persistent minors
statistics target and tool support
review tool exit codes for LVM1 compatibility
autobackup/text format
export/import
striping
64bit review
LVM1 compatible tool exit codes
before 2.1
before 2.0
----------
e2fsadm
lvmsadc
lvmsar
pvdata
vgsplit
vgmknodes
partial activation
atomic transactions
bidirectional format1/format2 migration tool
persistent minors
stats
snapshots
pvmove

View File

@@ -1 +0,0 @@
0.95.05-cvs (2002-04-23)

241
configure vendored
View File

@@ -16,6 +16,8 @@ ac_help="$ac_help
--with-user=USER Set the owner of installed files "
ac_help="$ac_help
--with-group=GROUP Set the group owner of installed files "
ac_help="$ac_help
--with-kernel_dir=DIR linux kernel source in DIR [/usr/src/linux]"
ac_help="$ac_help
--enable-jobs=NUM Number of jobs to run simultaneously"
ac_help="$ac_help
@@ -559,7 +561,7 @@ do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:563: checking for $ac_word" >&5
echo "configure:565: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -591,7 +593,7 @@ done
# Extract the first word of "gcc", so it can be a program name with args.
set dummy gcc; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:595: checking for $ac_word" >&5
echo "configure:597: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -621,7 +623,7 @@ if test -z "$CC"; then
# Extract the first word of "cc", so it can be a program name with args.
set dummy cc; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:625: checking for $ac_word" >&5
echo "configure:627: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -672,7 +674,7 @@ fi
# Extract the first word of "cl", so it can be a program name with args.
set dummy cl; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:676: checking for $ac_word" >&5
echo "configure:678: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -704,7 +706,7 @@ fi
fi
echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
echo "configure:708: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
echo "configure:710: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
ac_ext=c
# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
@@ -715,12 +717,12 @@ cross_compiling=$ac_cv_prog_cc_cross
cat > conftest.$ac_ext << EOF
#line 719 "configure"
#line 721 "configure"
#include "confdefs.h"
main(){return(0);}
EOF
if { (eval echo configure:724: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:726: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
ac_cv_prog_cc_works=yes
# If we can't run a trivial program, we are probably using a cross compiler.
if (./conftest; exit) 2>/dev/null; then
@@ -746,12 +748,12 @@ if test $ac_cv_prog_cc_works = no; then
{ echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
fi
echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
echo "configure:750: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
echo "configure:752: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
cross_compiling=$ac_cv_prog_cc_cross
echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
echo "configure:755: checking whether we are using GNU C" >&5
echo "configure:757: checking whether we are using GNU C" >&5
if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -760,7 +762,7 @@ else
yes;
#endif
EOF
if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:764: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:766: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
ac_cv_prog_gcc=yes
else
ac_cv_prog_gcc=no
@@ -779,7 +781,7 @@ ac_test_CFLAGS="${CFLAGS+set}"
ac_save_CFLAGS="$CFLAGS"
CFLAGS=
echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
echo "configure:783: checking whether ${CC-cc} accepts -g" >&5
echo "configure:785: checking whether ${CC-cc} accepts -g" >&5
if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -822,7 +824,7 @@ fi
# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
# ./install, which can be erroneously created by make from ./install.sh.
echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
echo "configure:826: checking for a BSD compatible install" >&5
echo "configure:828: checking for a BSD compatible install" >&5
if test -z "$INSTALL"; then
if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
@@ -875,7 +877,7 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
echo "configure:879: checking whether ln -s works" >&5
echo "configure:881: checking whether ln -s works" >&5
if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -896,7 +898,7 @@ else
fi
echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
echo "configure:900: checking whether ${MAKE-make} sets \${MAKE}" >&5
echo "configure:902: checking whether ${MAKE-make} sets \${MAKE}" >&5
set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
@@ -925,7 +927,7 @@ fi
# Extract the first word of "ranlib", so it can be a program name with args.
set dummy ranlib; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:929: checking for $ac_word" >&5
echo "configure:931: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -958,12 +960,12 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h
do
ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6
echo "configure:962: checking for $ac_hdr that defines DIR" >&5
echo "configure:964: checking for $ac_hdr that defines DIR" >&5
if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 967 "configure"
#line 969 "configure"
#include "confdefs.h"
#include <sys/types.h>
#include <$ac_hdr>
@@ -971,7 +973,7 @@ int main() {
DIR *dirp = 0;
; return 0; }
EOF
if { (eval echo configure:975: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
if { (eval echo configure:977: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
eval "ac_cv_header_dirent_$ac_safe=yes"
else
@@ -996,7 +998,7 @@ done
# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
if test $ac_header_dirent = dirent.h; then
echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6
echo "configure:1000: checking for opendir in -ldir" >&5
echo "configure:1002: checking for opendir in -ldir" >&5
ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'`
if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
@@ -1004,7 +1006,7 @@ else
ac_save_LIBS="$LIBS"
LIBS="-ldir $LIBS"
cat > conftest.$ac_ext <<EOF
#line 1008 "configure"
#line 1010 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1015,7 +1017,7 @@ int main() {
opendir()
; return 0; }
EOF
if { (eval echo configure:1019: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1021: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_lib_$ac_lib_var=yes"
else
@@ -1037,7 +1039,7 @@ fi
else
echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6
echo "configure:1041: checking for opendir in -lx" >&5
echo "configure:1043: checking for opendir in -lx" >&5
ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'`
if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
@@ -1045,7 +1047,7 @@ else
ac_save_LIBS="$LIBS"
LIBS="-lx $LIBS"
cat > conftest.$ac_ext <<EOF
#line 1049 "configure"
#line 1051 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1056,7 +1058,7 @@ int main() {
opendir()
; return 0; }
EOF
if { (eval echo configure:1060: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1062: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_lib_$ac_lib_var=yes"
else
@@ -1079,7 +1081,7 @@ fi
fi
echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
echo "configure:1083: checking how to run the C preprocessor" >&5
echo "configure:1085: checking how to run the C preprocessor" >&5
# On Suns, sometimes $CPP names a directory.
if test -n "$CPP" && test -d "$CPP"; then
CPP=
@@ -1094,13 +1096,13 @@ else
# On the NeXT, cc -E runs the code through the compiler's parser,
# not just through cpp.
cat > conftest.$ac_ext <<EOF
#line 1098 "configure"
#line 1100 "configure"
#include "confdefs.h"
#include <assert.h>
Syntax Error
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:1104: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:1106: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
:
@@ -1111,13 +1113,13 @@ else
rm -rf conftest*
CPP="${CC-cc} -E -traditional-cpp"
cat > conftest.$ac_ext <<EOF
#line 1115 "configure"
#line 1117 "configure"
#include "confdefs.h"
#include <assert.h>
Syntax Error
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:1121: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:1123: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
:
@@ -1128,13 +1130,13 @@ else
rm -rf conftest*
CPP="${CC-cc} -nologo -E"
cat > conftest.$ac_ext <<EOF
#line 1132 "configure"
#line 1134 "configure"
#include "confdefs.h"
#include <assert.h>
Syntax Error
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:1138: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:1140: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
:
@@ -1159,12 +1161,12 @@ fi
echo "$ac_t""$CPP" 1>&6
echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
echo "configure:1163: checking for ANSI C header files" >&5
echo "configure:1165: checking for ANSI C header files" >&5
if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1168 "configure"
#line 1170 "configure"
#include "confdefs.h"
#include <stdlib.h>
#include <stdarg.h>
@@ -1172,7 +1174,7 @@ else
#include <float.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:1176: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:1178: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
@@ -1189,7 +1191,7 @@ rm -f conftest*
if test $ac_cv_header_stdc = yes; then
# SunOS 4.x string.h does not declare mem*, contrary to ANSI.
cat > conftest.$ac_ext <<EOF
#line 1193 "configure"
#line 1195 "configure"
#include "confdefs.h"
#include <string.h>
EOF
@@ -1207,7 +1209,7 @@ fi
if test $ac_cv_header_stdc = yes; then
# ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
cat > conftest.$ac_ext <<EOF
#line 1211 "configure"
#line 1213 "configure"
#include "confdefs.h"
#include <stdlib.h>
EOF
@@ -1228,7 +1230,7 @@ if test "$cross_compiling" = yes; then
:
else
cat > conftest.$ac_ext <<EOF
#line 1232 "configure"
#line 1234 "configure"
#include "confdefs.h"
#include <ctype.h>
#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
@@ -1239,7 +1241,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
exit (0); }
EOF
if { (eval echo configure:1243: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
if { (eval echo configure:1245: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
then
:
else
@@ -1266,17 +1268,17 @@ for ac_hdr in fcntl.h malloc.h sys/ioctl.h unistd.h
do
ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
echo "configure:1270: checking for $ac_hdr" >&5
echo "configure:1272: checking for $ac_hdr" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1275 "configure"
#line 1277 "configure"
#include "confdefs.h"
#include <$ac_hdr>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:1280: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:1282: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
@@ -1304,18 +1306,18 @@ done
echo $ac_n "checking for working const""... $ac_c" 1>&6
echo "configure:1308: checking for working const" >&5
echo "configure:1310: checking for working const" >&5
if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1313 "configure"
#line 1315 "configure"
#include "confdefs.h"
int main() {
/* Ultrix mips cc rejects this. */
typedef int charset[2]; const charset x = {0,0};
typedef int charset[2]; const charset x;
/* SunOS 4.1.1 cc rejects this. */
char const *const *ccp;
char **p;
@@ -1358,7 +1360,7 @@ ccp = (char const *const *) p;
; return 0; }
EOF
if { (eval echo configure:1362: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
if { (eval echo configure:1364: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
ac_cv_c_const=yes
else
@@ -1379,21 +1381,21 @@ EOF
fi
echo $ac_n "checking for inline""... $ac_c" 1>&6
echo "configure:1383: checking for inline" >&5
echo "configure:1385: checking for inline" >&5
if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_cv_c_inline=no
for ac_kw in inline __inline__ __inline; do
cat > conftest.$ac_ext <<EOF
#line 1390 "configure"
#line 1392 "configure"
#include "confdefs.h"
int main() {
} int $ac_kw foo() {
} $ac_kw foo() {
; return 0; }
EOF
if { (eval echo configure:1397: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
if { (eval echo configure:1399: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
ac_cv_c_inline=$ac_kw; break
else
@@ -1419,12 +1421,12 @@ EOF
esac
echo $ac_n "checking for off_t""... $ac_c" 1>&6
echo "configure:1423: checking for off_t" >&5
echo "configure:1425: checking for off_t" >&5
if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1428 "configure"
#line 1430 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if STDC_HEADERS
@@ -1452,12 +1454,12 @@ EOF
fi
echo $ac_n "checking for pid_t""... $ac_c" 1>&6
echo "configure:1456: checking for pid_t" >&5
echo "configure:1458: checking for pid_t" >&5
if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1461 "configure"
#line 1463 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if STDC_HEADERS
@@ -1485,12 +1487,12 @@ EOF
fi
echo $ac_n "checking for size_t""... $ac_c" 1>&6
echo "configure:1489: checking for size_t" >&5
echo "configure:1491: checking for size_t" >&5
if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1494 "configure"
#line 1496 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if STDC_HEADERS
@@ -1518,12 +1520,12 @@ EOF
fi
echo $ac_n "checking for st_rdev in struct stat""... $ac_c" 1>&6
echo "configure:1522: checking for st_rdev in struct stat" >&5
echo "configure:1524: checking for st_rdev in struct stat" >&5
if eval "test \"`echo '$''{'ac_cv_struct_st_rdev'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1527 "configure"
#line 1529 "configure"
#include "confdefs.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -1531,7 +1533,7 @@ int main() {
struct stat s; s.st_rdev;
; return 0; }
EOF
if { (eval echo configure:1535: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
if { (eval echo configure:1537: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
ac_cv_struct_st_rdev=yes
else
@@ -1552,12 +1554,12 @@ EOF
fi
echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
echo "configure:1556: checking whether time.h and sys/time.h may both be included" >&5
echo "configure:1558: checking whether time.h and sys/time.h may both be included" >&5
if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1561 "configure"
#line 1563 "configure"
#include "confdefs.h"
#include <sys/types.h>
#include <sys/time.h>
@@ -1566,7 +1568,7 @@ int main() {
struct tm *tp;
; return 0; }
EOF
if { (eval echo configure:1570: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
if { (eval echo configure:1572: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
ac_cv_header_time=yes
else
@@ -1607,6 +1609,15 @@ else
fi
# Check whether --with-kernel_dir or --without-kernel_dir was given.
if test "${with_kernel_dir+set}" = set; then
withval="$with_kernel_dir"
kernel_dir="$withval"
else
kernel_dir=/usr/src/linux
fi
# Check whether --enable-jobs or --disable-jobs was given.
if test "${enable_jobs+set}" = set; then
enableval="$enable_jobs"
@@ -1641,13 +1652,13 @@ fi;
if test $ac_cv_prog_gcc = yes; then
echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6
echo "configure:1645: checking whether ${CC-cc} needs -traditional" >&5
echo "configure:1656: checking whether ${CC-cc} needs -traditional" >&5
if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_pattern="Autoconf.*'x'"
cat > conftest.$ac_ext <<EOF
#line 1651 "configure"
#line 1662 "configure"
#include "confdefs.h"
#include <sgtty.h>
Autoconf TIOCGETP
@@ -1665,7 +1676,7 @@ rm -f conftest*
if test $ac_cv_prog_gcc_traditional = no; then
cat > conftest.$ac_ext <<EOF
#line 1669 "configure"
#line 1680 "configure"
#include "confdefs.h"
#include <termio.h>
Autoconf TCGETA
@@ -1687,12 +1698,12 @@ echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6
fi
echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
echo "configure:1691: checking return type of signal handlers" >&5
echo "configure:1702: checking return type of signal handlers" >&5
if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1696 "configure"
#line 1707 "configure"
#include "confdefs.h"
#include <sys/types.h>
#include <signal.h>
@@ -1709,7 +1720,7 @@ int main() {
int i;
; return 0; }
EOF
if { (eval echo configure:1713: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
if { (eval echo configure:1724: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
ac_cv_type_signal=void
else
@@ -1728,12 +1739,12 @@ EOF
echo $ac_n "checking for vprintf""... $ac_c" 1>&6
echo "configure:1732: checking for vprintf" >&5
echo "configure:1743: checking for vprintf" >&5
if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1737 "configure"
#line 1748 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char vprintf(); below. */
@@ -1756,7 +1767,7 @@ vprintf();
; return 0; }
EOF
if { (eval echo configure:1760: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1771: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_func_vprintf=yes"
else
@@ -1780,12 +1791,12 @@ fi
if test "$ac_cv_func_vprintf" != yes; then
echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
echo "configure:1784: checking for _doprnt" >&5
echo "configure:1795: checking for _doprnt" >&5
if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1789 "configure"
#line 1800 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char _doprnt(); below. */
@@ -1808,7 +1819,7 @@ _doprnt();
; return 0; }
EOF
if { (eval echo configure:1812: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1823: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_func__doprnt=yes"
else
@@ -1835,12 +1846,12 @@ fi
for ac_func in mkdir rmdir uname
do
echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
echo "configure:1839: checking for $ac_func" >&5
echo "configure:1850: checking for $ac_func" >&5
if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1844 "configure"
#line 1855 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $ac_func(); below. */
@@ -1863,7 +1874,7 @@ $ac_func();
; return 0; }
EOF
if { (eval echo configure:1867: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1878: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_func_$ac_func=yes"
else
@@ -1891,14 +1902,14 @@ done
if test x$READLINE = xyes; then
echo $ac_n "checking for library containing tgetent""... $ac_c" 1>&6
echo "configure:1895: checking for library containing tgetent" >&5
echo "configure:1906: checking for library containing tgetent" >&5
if eval "test \"`echo '$''{'ac_cv_search_tgetent'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_func_search_save_LIBS="$LIBS"
ac_cv_search_tgetent="no"
cat > conftest.$ac_ext <<EOF
#line 1902 "configure"
#line 1913 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1909,7 +1920,7 @@ int main() {
tgetent()
; return 0; }
EOF
if { (eval echo configure:1913: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1924: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_tgetent="none required"
else
@@ -1920,7 +1931,7 @@ rm -f conftest*
test "$ac_cv_search_tgetent" = "no" && for i in ncurses curses termcap termlib; do
LIBS="-l$i $ac_func_search_save_LIBS"
cat > conftest.$ac_ext <<EOF
#line 1924 "configure"
#line 1935 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1931,7 +1942,7 @@ int main() {
tgetent()
; return 0; }
EOF
if { (eval echo configure:1935: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1946: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_tgetent="-l$i"
break
@@ -1965,7 +1976,7 @@ fi
if test x$READLINE = xyes; then
echo $ac_n "checking for readline in -lreadline""... $ac_c" 1>&6
echo "configure:1969: checking for readline in -lreadline" >&5
echo "configure:1980: checking for readline in -lreadline" >&5
ac_lib_var=`echo readline'_'readline | sed 'y%./+-%__p_%'`
if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
@@ -1973,7 +1984,7 @@ else
ac_save_LIBS="$LIBS"
LIBS="-lreadline $LIBS"
cat > conftest.$ac_ext <<EOF
#line 1977 "configure"
#line 1988 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1984,7 +1995,7 @@ int main() {
readline()
; return 0; }
EOF
if { (eval echo configure:1988: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1999: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_lib_$ac_lib_var=yes"
else
@@ -2020,64 +2031,7 @@ package as well (which may be called readline-devel or something similar).
fi
echo $ac_n "checking for rl_completion_matches""... $ac_c" 1>&6
echo "configure:2025: checking for rl_completion_matches" >&5
if eval "test \"`echo '$''{'ac_cv_func_rl_completion_matches'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 2030 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char rl_completion_matches(); below. */
#include <assert.h>
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
builtin and then its argument prototype would still apply. */
char rl_completion_matches();
int main() {
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined (__stub_rl_completion_matches) || defined (__stub___rl_completion_matches)
choke me
#else
rl_completion_matches();
#endif
; return 0; }
EOF
if { (eval echo configure:2053: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_func_rl_completion_matches=yes"
else
echo "configure: failed program was:" >&5
cat conftest.$ac_ext >&5
rm -rf conftest*
eval "ac_cv_func_rl_completion_matches=no"
fi
rm -f conftest*
fi
if eval "test \"`echo '$ac_cv_func_'rl_completion_matches`\" = yes"; then
echo "$ac_t""yes" 1>&6
HAVE_RL_COMPLETION_MATCHES=yes
else
echo "$ac_t""no" 1>&6
HAVE_RL_COMPLETION_MATCHES=no
fi
fi
if test "-f VERSION"; then
LVM_VERSION="\"`cat VERSION`\""
else
LVM_VERSION="Unknown"
fi
@@ -2205,7 +2159,6 @@ include/Makefile \
lib/Makefile \
man/Makefile \
tools/Makefile \
tools/version.h \
test/mm/Makefile \
test/device/Makefile \
test/format1/Makefile \
@@ -2255,10 +2208,9 @@ s%@CPP@%$CPP%g
s%@JOBS@%$JOBS%g
s%@STATIC_LINK@%$STATIC_LINK%g
s%@READLINE@%$READLINE%g
s%@HAVE_RL_COMPLETION_MATCHES@%$HAVE_RL_COMPLETION_MATCHES%g
s%@kernel_dir@%$kernel_dir%g
s%@OWNER@%$OWNER%g
s%@GROUP@%$GROUP%g
s%@LVM_VERSION@%$LVM_VERSION%g
CEOF
EOF
@@ -2307,7 +2259,6 @@ include/Makefile \
lib/Makefile \
man/Makefile \
tools/Makefile \
tools/version.h \
test/mm/Makefile \
test/device/Makefile \
test/format1/Makefile \

View File

@@ -61,6 +61,12 @@ AC_ARG_WITH(group,
[ GROUP="$withval" ],
[ GROUP="root" ])
dnl -- Where the linux src tree is
AC_ARG_WITH(kernel_dir,
[ --with-kernel_dir=DIR linux kernel source in DIR [/usr/src/linux]],
[ kernel_dir="$withval" ],
[ kernel_dir=/usr/src/linux ])
AC_ARG_ENABLE(jobs, [ --enable-jobs=NUM Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=)
dnl Enables staticly linked tools
@@ -112,24 +118,15 @@ package as well (which may be called readline-devel or something similar).
)
exit
)
AC_CHECK_FUNC(rl_completion_matches, HAVE_RL_COMPLETION_MATCHES=yes,
HAVE_RL_COMPLETION_MATCHES=no)
fi
if test "-f VERSION"; then
LVM_VERSION="\"`cat VERSION`\""
else
LVM_VERSION="Unknown"
fi
AC_SUBST(JOBS)
AC_SUBST(STATIC_LINK)
AC_SUBST(READLINE)
AC_SUBST(HAVE_RL_COMPLETION_MATCHES)
AC_SUBST(kernel_dir)
AC_SUBST(OWNER)
AC_SUBST(GROUP)
AC_SUBST(LIBS)
AC_SUBST(LVM_VERSION)
dnl First and last lines should not contain files to generate in order to
dnl keep utility scripts running properly
AC_OUTPUT( \
@@ -139,7 +136,6 @@ include/Makefile \
lib/Makefile \
man/Makefile \
tools/Makefile \
tools/version.h \
test/mm/Makefile \
test/device/Makefile \
test/format1/Makefile \

23
debian/changelog vendored
View File

@@ -1,23 +0,0 @@
lvm2 (0.95.04cvs20020306-1) unstable; urgency=low
* CVS updated.
* Convert from debian native package.
-- Andres Salomon <dilinger@mp3revolution.net> Wed, 6 Mar 2002 00:43:21 -0500
lvm2 (0.95.04cvs20020304) unstable; urgency=low
* CVS updated.
* Enhance init script; create devmapper control device, etc.
* Add dmsetup as a suggestion.
* Add /etc/lvm/lvm.conf conffile.
* Add undocumented(7) for the commands missing manpages.
-- Andres Salomon <dilinger@mp3revolution.net> Mon, 4 Mar 2002 04:51:26 -0500
lvm2 (0.95.02cvs20020220) unstable; urgency=low
* Initial Release.
-- Andres Salomon <dilinger@mp3revolution.net> Wed, 20 Feb 2002 03:17:25 -0500

2
debian/conffiles vendored
View File

@@ -1,2 +0,0 @@
/etc/lvm/lvm.conf
/etc/init.d/lvm2

19
debian/control vendored
View File

@@ -1,19 +0,0 @@
Source: lvm2
Section: admin
Priority: optional
Maintainer: Andres Salomon <dilinger@mp3revolution.net>
Build-Depends: debhelper (>> 3.0.0), libdevmapper-dev, libreadline4-dev
Standards-Version: 3.5.2
Package: lvm2
Architecture: any
Depends: libdevmapper0
Replaces: lvm10, lvm-common
Provides: lvm-binaries
Suggests: dmsetup
Description: The Linux Logical Volume Manager
This is LVM2, the rewrite of The Linux Logical Volume Manager. LVM
supports enterprise level volume management of disk and disk subsystems
by grouping arbitrary disks into volume groups. The total capacity of
volume groups can be allocated to logical volumes, which are accessed as
regular block devices.

25
debian/copyright vendored
View File

@@ -1,25 +0,0 @@
This package was debianized by Andres Salomon <dilinger@mp3revolution.net> on
Wed, 20 Feb 2002 03:17:25 -0500.
It was downloaded from http://www.sistina.com/products_lvm.htm
Upstream Author(s): LVM Development Team
Copyright (c) 2001-2002 LVM Development Team
LVM2 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.
LVM2 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
On Debian systems, the full text of the GPL can be found in
/usr/share/common-licenses/GPL

4
debian/dirs vendored
View File

@@ -1,4 +0,0 @@
etc/lvm
usr/share/man/man5
usr/share/man/man8
sbin

8
debian/docs vendored
View File

@@ -1,8 +0,0 @@
BUGS
INTRO
README
TODO
VERSION
doc/example.conf
doc/pvmove_outline.txt
doc/testing.txt

65
debian/init.d vendored
View File

@@ -1,65 +0,0 @@
#! /bin/sh
#
# lvm2 This script handles LVM2 initialization/shutdown.
#
# Written by Andres Salomon <dilinger@mp3revolution.net>.
#
PATH=/sbin:/bin:/usr/sbin:/usr/bin
NAME=lvm2
DESC=LVM
test -x /sbin/vgchange || exit 0
modprobe dm-mod >/dev/null 2>&1
# Create necessary files in /dev for device-mapper
create_devfiles() {
DIR="/dev/device-mapper"
FILE="$DIR/control"
major=$(awk '$2 ~ /^misc$/ {print $1}' /proc/devices)
minor=$(awk "\$2 ~ /^$DM_NAME\$/ {print \$1}" /proc/misc)
if test ! -d $DIR; then
mkdir --mode=755 $DIR >/dev/null 2>&1
fi
if test ! -c $FILE -a ! -z "$minor"; then
mknod --mode=600 $FILE c $major $minor >/dev/null 2>&1
fi
}
case "$1" in
start)
echo -n "Initializing $DESC: "
create_devfiles
vgchange -a y
# # Mount all LVM devices
# for vg in $( vgchange -a y 2>/dev/null | grep active | awk -F\" '{print $2}' ); do
# MTPT=$( grep $vg /etc/fstab | awk '{print $2}' )
# mount $MTPT
# done
echo "$NAME."
;;
stop)
echo -n "Shutting down $DESC: "
# We don't really try all that hard to shut it down; far too many
# things that can keep it from successfully shutting down.
vgchange -a n
echo "$NAME."
;;
restart|force-reload)
echo -n "Restarting $DESC: "
vgchange -a n
sleep 1
vgchange -a y
echo "$NAME."
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0

26
debian/manpages vendored
View File

@@ -1,26 +0,0 @@
debian/lvm2/usr/share/man/man5/lvm.conf.5
debian/lvm2/usr/share/man/man8/lvchange.8
debian/lvm2/usr/share/man/man8/lvcreate.8
debian/lvm2/usr/share/man/man8/lvdisplay.8
debian/lvm2/usr/share/man/man8/lvextend.8
debian/lvm2/usr/share/man/man8/lvm.8
debian/lvm2/usr/share/man/man8/lvmchange.8
debian/lvm2/usr/share/man/man8/lvreduce.8
debian/lvm2/usr/share/man/man8/lvremove.8
debian/lvm2/usr/share/man/man8/lvrename.8
debian/lvm2/usr/share/man/man8/lvscan.8
debian/lvm2/usr/share/man/man8/pvchange.8
debian/lvm2/usr/share/man/man8/pvcreate.8
debian/lvm2/usr/share/man/man8/pvdisplay.8
debian/lvm2/usr/share/man/man8/pvscan.8
debian/lvm2/usr/share/man/man8/vgcfgbackup.8
debian/lvm2/usr/share/man/man8/vgchange.8
debian/lvm2/usr/share/man/man8/vgck.8
debian/lvm2/usr/share/man/man8/vgcreate.8
debian/lvm2/usr/share/man/man8/vgdisplay.8
debian/lvm2/usr/share/man/man8/vgextend.8
debian/lvm2/usr/share/man/man8/vgmerge.8
debian/lvm2/usr/share/man/man8/vgreduce.8
debian/lvm2/usr/share/man/man8/vgremove.8
debian/lvm2/usr/share/man/man8/vgrename.8
debian/lvm2/usr/share/man/man8/vgscan.8

49
debian/postinst vendored
View File

@@ -1,49 +0,0 @@
#! /bin/sh
# postinst script for lvm2
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
#
# quoting from the policy:
# Any necessary prompting should almost always be confined to the
# post-installation script, and should be protected with a conditional
# so that unnecessary prompting doesn't happen if a package's
# installation fails and the `postinst' is called with `abort-upgrade',
# `abort-remove' or `abort-deconfigure'.
case "$1" in
configure)
update-rc.d lvm2 start 25 S . start 50 0 6 . >/dev/null
/etc/init.d/lvm2 start
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

39
debian/postrm vendored
View File

@@ -1,39 +0,0 @@
#! /bin/sh
# postrm script for lvm2
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
purge)
rm -f /etc/init.d/lvm2
update-rc.d lvm2 remove >/dev/null
;;
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

120
debian/rules vendored
View File

@@ -1,120 +0,0 @@
#!/usr/bin/make -f
# Sample debian/rules that uses debhelper.
# GNU copyright 1997 by Joey Hess.
#
# This version is for a hypothetical package that builds an
# architecture-dependant package, as well as an architecture-independent
# package.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# This is the debhelper compatibility version to use.
export DH_COMPAT=3
# These are used for cross-compiling and for saving the configure script
# from having to guess our platform (since we know it already)
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
CFLAGS += -g
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
INSTALL_PROGRAM += -s
endif
configure: configure-stamp
configure-stamp:
dh_testdir
# Add here commands to configure the package.
./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/ --mandir=\$${prefix}/usr/share/man --infodir=\$${prefix}/usr/share/info
touch configure-stamp
build-arch: configure-stamp build-arch-stamp
build-arch-stamp:
dh_testdir
# Add here command to compile/build the package.
$(MAKE)
touch build-arch-stamp
build-indep: configure-stamp build-indep-stamp
build-indep-stamp:
dh_testdir
# Add here command to compile/build the arch indep package.
# It's ok not to do anything here, if you don't need to build
# anything for this package.
#/usr/bin/docbook-to-man debian/lvm2.sgml > lvm2.1
touch build-indep-stamp
build: build-arch build-indep
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
# Add here commands to clean up after the build process.
-$(MAKE) distclean
-test -r /usr/share/misc/config.sub && \
cp -f /usr/share/misc/config.sub config.sub
-test -r /usr/share/misc/config.guess && \
cp -f /usr/share/misc/config.guess config.guess
dh_clean
install: DH_OPTIONS=
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
# Add here commands to install the package into debian/lvm2.
$(MAKE) install prefix=$(CURDIR)/debian/lvm2
install -m 0644 doc/example.conf debian/lvm2/etc/lvm/lvm.conf
# Build architecture-independent files here.
# Pass -i to all debhelper commands in this target to reduce clutter.
binary-indep: build install
# nada.
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
# dh_installdebconf
dh_installdocs
dh_installexamples
# dh_installlogrotate -a
# dh_installemacsen -a
# dh_installpam -a
# dh_installmime -a
dh_installinit -n
dh_installcron
dh_installman
dh_installinfo
dh_undocumented
dh_installchangelogs
dh_strip
dh_link
dh_compress
dh_fixperms
dh_makeshlibs
dh_installdeb
# dh_perl -a
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure

14
debian/undocumented vendored
View File

@@ -1,14 +0,0 @@
e2fsadm.8
lvmdiskscan.8
lvmsadc.8
lvmsar.8
lvresize.8
pvdata.8
pvmove.8
pvresize.8
version.8
vgcfgrestore.8
vgexport.8
vgimport.8
vgmknodes.8
vgsplit.8

1
doc/README Normal file
View File

@@ -0,0 +1 @@
Wow! This is really incredible documentation!

View File

@@ -1,139 +0,0 @@
# This is an example configuration file for the LVM2 system. It
# contains the default settings that would be used if there was no
# /etc/lvm/lvm.conf file.
# Refer to 'man lvm.conf' for further information.
# This section allows the user to configure which block devices should
# be used by the LVM system.
devices {
# where do you want your volume groups to appear ?
dir = "/dev"
# An array of directories that contain the device nodes you wish
# to use with LVM2.
scan = "/dev"
# A very important option, that allows you to tune the LVM2 system
# to just look at a restricted set of devices that you're
# interested in.
# The filter consists of an array of regular expressions. These
# expressions can be delimited by a character of your choice, and
# prefixed with either an 'a' (for accept) or 'r' (for reject).
# ATM you cannot use anchors (^ or $) in your regular expression.
# Remember to run vgscan after you change this parameter.
# By default we accept every block device:
filter = "a/.*/"
# When testing I like to work with just loopback devices:
# filter = ["a/loop/", "r/.*/"]
# Or maybe all loops and ide drives except hdc:
# filter =["a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|"]
# The results of all the filtering are cached on disk to avoid
# rescanning dud devices (which can take a very long time). By
# default this cache file is hidden in the /etc/lvm directory, it
# is human readable to aid filter debugging.
cache = "/etc/lvm/.cache"
# You can turn off writing this cache file by setting this to 0.
write_cache_state = 1
}
# A section that allows the user to configure the nature of the
# information that LVM2 reports.
log {
# Where should the log of error and debug messages go ? By
# default there is no log.
#file = "/var/log/lvm2.log"
# Should we overwrite the last log. By default we append.
overwrite = 0
# There are 9 log levels, with 9 being the most verbose.
level = 3
# Controls the messages sent to stdout or stderr while running
# LVM2. There are three levels of verbosity, 3 being the most
# verbose.
verbose = 0
# Should we send log messages through syslog?
# 1 is yes; 0 is no.
syslog = 1
# Choose format of output messages
# Whether or not (1 or 0) to indent messages according to their severity
indent = 1
# Whether or not (1 or 0) to display the command name on each line output
command_names = 0
# A prefix to use before the message text (but after the command name,
# if selected)
prefix = " "
# To make the messages look similar to the original LVM use:
# indent = 0
# command_names = 1
# prefix = " -- "
}
# Configuration of metadata backups and archiving. In LVM2 when we
# talk about a 'backup' we mean making a copy of the metadata for the
# *current* system. The 'archive' contains old metadata configurations.
# Backups are stored in a human readeable text format.
backup {
# Should we maintain a backup of the current metadata configuration ?
# Use 1 for Yes; 0 for No.
# Think very hard before turning this off.
backup = 1
# Where shall we keep it ?
backup_dir = "/etc/lvm/backup"
# Should we maintain an archive of old metadata configurations.
# Use 1 for Yes; 0 for No.
# On by default. Think very hard before turning this off.
archive = 1
# Where should archived files go ?
archive_dir = "/etc/lvm/archive"
# What is the minimum number of archive files you wish to keep ?
retain_min = 10
# What is the minimum time you wish to keep an archive file for ?
retain_days = 30
}
# Settings for the running LVM2 in shell mode.
shell {
# Number of lines of history to store in ~/.lvm_history
history_size = 100
}
# Miscellaneous global settings
global {
# The file creation mask for any files and directories created.
# Interpreted as octal if the first digit is zero.
umask = 077
# Allow other users to read the files
#umask = 022
# Enabling test mode means that no changes to the on disk metadata
# will be made. Equivalent to having the -t option on every
# command. Defaults to off.
test = 0
}

View File

@@ -1,52 +0,0 @@
Let's say we have an LV, made up of three segments of different PV's,
I've also added in the device major:minor as this will be useful
later:
+-----------------------------+
| PV1 | PV2 | PV3 | 254:3
+----------+---------+--------+
Now our hero decides to PV move PV2 to PV4:
1. Suspend our LV (254:3), this starts queueing all io, and flushes
all pending io. Once the suspend has completed we are free to change
the mapping table.
2. Set up *another* (254:4) device with the mapping table of our LV.
3. Load a new mapping table into (254:3) that has identity targets for
parts that aren't moving, and a mirror target for parts that are.
4. Unsuspend (254:3)
So now we have:
destination of copy
+--------------------->--------------+
| |
+-----------------------------+ + -----------+
| Identity | mirror | Ident. | 254:3 | PV4 |
+----------+---------+--------+ +------------+
| | |
\/ \/ \/
+-----------------------------+
| PV1 | PV2 | PV3 | 254:4
+----------+---------+--------+
Any writes to segment2 of the LV get intercepted by the mirror target
who checks that that chunk has been copied to the new destination, if
it hasn't it queues the initial copy and defers the current io until
it has finished. Then the current io is written to *both* PV2 and the
PV4.
5. When the copying has completed 254:3 is suspended/pending flushed.
6. 254:4 is taken down
7. metadata is updated on disk
8. 254:3 has new mapping table loaded:
+-----------------------------+
| PV1 | PV4 | PV3 | 254:3
+----------+---------+--------+

View File

@@ -1,46 +0,0 @@
Here's how I test new LVM2 builds without interfering with the stable
LVM2 that is running the LV's on my development box.
1) Create a set of loopback devices.
2) Create a new directory to contain the LVM2 configuration files for
this setup. (I use /etc/lvm_loops)
3) Write a suitable lvm.conf file, this goes in the directory you just
created. eg, my /etc/lvm_loops/lvm.conf looks like:
log {
file="/tmp/lvm2_loop.log"
level=9
verbose=0
overwrite=1
}
devices {
scan = "/dev"
filter = ["a/loop/", "r/.*/"]
}
The important this to note is the devices section which makes sure that
only the loopback devices are considered for LVM2 operations.
4) When you want to use this test setup just set the environment
variable LVM_SYSTEM_DIR to point to your config directory
(/etc/lvm_loops in my case).
5) It's a good idea to do a vgscan to initialise the filters:
export LVM_SYSTEM_DIR=/etc/lvm_loops
./lvm vgscan
where ./lvm is the new build of LVM2 that I'm trying out.
7) Test away. Make sure that you are explicit about which lvm
executable you want to execute (eg, ./lvm if you are in
LVM2/tools).

1
driver/README Normal file
View File

@@ -0,0 +1 @@
The driver directory

104
driver/device-mapper/README Normal file
View File

@@ -0,0 +1,104 @@
The main goal of this driver is to support volume management in
general, not just for LVM. The kernel should provide general
services, not support specific applications. eg, The driver has no
concept of volume groups.
The driver does this by mapping sector ranges for the logical device
onto 'targets'.
When the logical device is accessed, the make_request function looks
up the correct target for the given sector, and then asks this target
to do the remapping.
A btree structure is used to hold the sector range -> target mapping.
Since we know all the entries in the btree in advance we can make a
very compact tree, omitting pointers to child nodes, (child nodes
locations can be calculated). Typical users would find they only have
a handful of targets for each logical volume LV.
Benchmarking with bonnie++ suggests that this is certainly no slower
than current LVM.
Target types are not hard coded, instead the register_mapping_type
function should be called. A target type is specified using three
functions (see the header):
dm_ctr_fn - takes a string and contructs a target specific piece of
context data.
dm_dtr_fn - destroy contexts.
dm_map_fn - function that takes a buffer_head and some previously
constructed context and performs the remapping.
Currently there are two two trivial mappers, which are automatically
registered: 'linear', and 'io_error'. Linear alone is enough to
implement most of LVM.
I do not like ioctl interfaces so this driver is currently controlled
through a /proc interface. /proc/device-mapper/control allows you to
create and remove devices by 'cat'ing a line of the following format:
create <device name> [minor no]
remove <device name>
If you're not using devfs you'll have to do the mknod'ing yourself,
otherwise the device will appear in /dev/device-mapper automatically.
/proc/device-mapper/<device name> accepts the mapping table:
begin
<sector start> <length> <target name> <target args>...
...
end
where <target args> are specific to the target type, eg. for a linear
mapping:
<sector start> <length> linear <major> <minor> <start>
and the io-err mapping:
<sector start> <length> io-err
The begin/end lines around the table are nasty, they should be handled
by open/close of the file.
The interface is far from complete, currently loading a table either
succeeds or fails, you have no way of knowing which line of the
mapping table was erroneous. Also there is no way to get status
information out, though this should be easy to add, either as another
/proc file, or just by reading the same /proc/device-mapper/<device>
file. I will be seperating the loading and validation of a table from
the binding of a valid table to a device.
It has been suggested that I should implement a little custom
filesystem rather than labouring with /proc. For example doing a
mkdir foo in /wherever/device-mapper would create a new device. People
waiting for a status change (eg, a mirror operation to complete) could
poll a file. Does the community find this an acceptable way to go ?
At the moment the table assumes 32 bit keys (sectors), the move to 64
bits will involve no interface changes, since the tables will be read
in as ascii data. A different table implementation can therefor be
provided at another time. Either just by changing offset_t to 64
bits, or maybe implementing a structure which looks up the keys in
stages (ie, 32 bits at a time).
More interesting targets:
striped mapping; given a stripe size and a number of device regions
this would stripe data across the regions. Especially useful, since
we could limit each striped region to a 32 bit area and then avoid
nasty 64 bit %'s.
mirror mapping; would set off a kernel thread slowly copying data from
one region to another, ensuring that any new writes got copied to both
destinations correctly. Enabling us to implement a live pvmove
correctly.

View File

@@ -0,0 +1,84 @@
/*
* device-mapper.h
*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
/*
* Changelog
*
* 14/08/2001 - First version [Joe Thornber]
*/
#ifndef DEVICE_MAPPER_H
#define DEVICE_MAPPER_H
#include <linux/major.h>
/* FIXME: Use value from local range for now, for co-existence with LVM 1 */
#define DM_BLK_MAJOR 124
struct dm_table;
struct dm_dev;
typedef unsigned int offset_t;
typedef void (*dm_error_fn)(const char *message, void *private);
/*
* constructor, destructor and map fn types
*/
typedef int (*dm_ctr_fn)(struct dm_table *t, offset_t b, offset_t l,
char *args, void **context);
typedef void (*dm_dtr_fn)(struct dm_table *t, void *c);
typedef int (*dm_map_fn)(struct buffer_head *bh, int rw, void *context);
typedef int (*dm_err_fn)(struct buffer_head *bh, int rw, void *context);
typedef char *(*dm_print_fn)(void *context);
/*
* Contructors should call this to make sure any
* destination devices are handled correctly
* (ie. opened/closed).
*/
int dm_table_get_device(struct dm_table *t, const char *path,
struct dm_dev **result);
void dm_table_put_device(struct dm_table *table, struct dm_dev *d);
/*
* information about a target type
*/
struct target_type {
const char *name;
struct module *module;
dm_ctr_fn ctr;
dm_dtr_fn dtr;
dm_map_fn map;
dm_err_fn err;
dm_print_fn print;
};
int dm_register_target(struct target_type *t);
int dm_unregister_target(struct target_type *t);
static inline char *next_token(char **p)
{
static const char *delim = " \t";
char *r;
do {
r = strsep(p, delim);
} while(r && *r == 0);
return r;
}
#endif /* DEVICE_MAPPER_H */
/*
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,134 @@
/*
* dm-linear.c
*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/device-mapper.h>
#include "dm.h"
/*
* linear: maps a linear range of a device.
*/
struct linear_c {
long delta; /* FIXME: we need a signed offset type */
struct dm_dev *dev;
};
/*
* construct a linear mapping.
* <dev_path> <offset>
*/
static int linear_ctr(struct dm_table *t, offset_t b, offset_t l,
char *args, void **context)
{
struct linear_c *lc;
unsigned int start;
int r = -EINVAL;
char *tok;
char *path;
char *p = args;
*context = "No device path given";
path = next_token(&p);
if (!path)
goto bad;
*context = "No initial offset given";
tok = next_token(&p);
if (!tok)
goto bad;
start = simple_strtoul(tok, NULL, 10);
*context = "Cannot allocate linear context private structure";
lc = kmalloc(sizeof(lc), GFP_KERNEL);
if (lc == NULL)
goto bad;
*context = "Cannot get target device";
r = dm_table_get_device(t, path, &lc->dev);
if (r)
goto bad_free;
lc->delta = (int) start - (int) b;
*context = lc;
return 0;
bad_free:
kfree(lc);
bad:
return r;
}
static void linear_dtr(struct dm_table *t, void *c)
{
struct linear_c *lc = (struct linear_c *) c;
dm_table_put_device(t, lc->dev);
kfree(c);
}
static int linear_map(struct buffer_head *bh, int rw, void *context)
{
struct linear_c *lc = (struct linear_c *) context;
bh->b_rdev = lc->dev->dev;
bh->b_rsector = bh->b_rsector + lc->delta;
return 1;
}
/*
* Debugging use only.
*/
static char *linear_print(void *context)
{
struct linear_c *lc = (struct linear_c *)context;
static char buf[256];
sprintf(buf, " %lu", lc->delta);
return buf;
}
static struct target_type linear_target = {
name: "linear",
module: THIS_MODULE,
ctr: linear_ctr,
dtr: linear_dtr,
map: linear_map,
print: linear_print,
};
static int __init linear_init(void)
{
int r = dm_register_target(&linear_target);
if (r < 0)
printk(KERN_ERR
"Device mapper: Linear: register failed %d\n", r);
return r;
}
static void __exit linear_exit(void)
{
int r = dm_unregister_target(&linear_target);
if (r < 0)
printk(KERN_ERR
"Device mapper: Linear: unregister failed %d\n", r);
}
module_init(linear_init);
module_exit(linear_exit);
MODULE_AUTHOR("Joe Thornber <thornber@uk.sistina.com>");
MODULE_DESCRIPTION("Device Mapper: Linear mapping");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,340 @@
/*
* dm-table.c
*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
/*
* Changelog
*
* 16/08/2001 - First version [Joe Thornber]
*/
#include "dm.h"
/* ceiling(n / size) * size */
static inline ulong round_up(ulong n, ulong size)
{
ulong r = n % size;
return n + (r ? (size - r) : 0);
}
/* ceiling(n / size) */
static inline ulong div_up(ulong n, ulong size)
{
return round_up(n, size) / size;
}
/* similar to ceiling(log_size(n)) */
static uint int_log(ulong n, ulong base)
{
int result = 0;
while (n > 1) {
n = div_up(n, base);
result++;
}
return result;
}
/*
* return the highest key that you could lookup
* from the n'th node on level l of the btree.
*/
static offset_t high(struct dm_table *t, int l, int n)
{
for (; l < t->depth - 1; l++)
n = get_child(n, CHILDREN_PER_NODE - 1);
if (n >= t->counts[l])
return (offset_t) -1;
return get_node(t, l, n)[KEYS_PER_NODE - 1];
}
/*
* fills in a level of the btree based on the
* highs of the level below it.
*/
static int setup_btree_index(int l, struct dm_table *t)
{
int n, k;
offset_t *node;
for (n = 0; n < t->counts[l]; n++) {
node = get_node(t, l, n);
for (k = 0; k < KEYS_PER_NODE; k++)
node[k] = high(t, l + 1, get_child(n, k));
}
return 0;
}
/*
* highs, and targets are managed as dynamic
* arrays during a table load.
*/
static int alloc_targets(struct dm_table *t, int num)
{
offset_t *n_highs;
struct target *n_targets;
int n = t->num_targets;
int size = (sizeof(struct target) + sizeof(offset_t)) * num;
n_highs = vmalloc(size);
if (!n_highs)
return -ENOMEM;
n_targets = (struct target *) (n_highs + num);
if (n) {
memcpy(n_highs, t->highs, sizeof(*n_highs) * n);
memcpy(n_targets, t->targets, sizeof(*n_targets) * n);
}
vfree(t->highs);
t->num_allocated = num;
t->highs = n_highs;
t->targets = n_targets;
return 0;
}
struct dm_table *dm_table_create(void)
{
struct dm_table *t = kmalloc(sizeof(struct dm_table), GFP_NOIO);
if (!t)
return 0;
memset(t, 0, sizeof(*t));
INIT_LIST_HEAD(&t->devices);
/* allocate a single nodes worth of targets to
begin with */
if (alloc_targets(t, KEYS_PER_NODE)) {
kfree(t);
t = 0;
}
return t;
}
static void free_devices(struct list_head *devices)
{
struct list_head *tmp, *next;
for (tmp = devices->next; tmp != devices; tmp = next) {
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
next = tmp->next;
kfree(dd);
}
}
void dm_table_destroy(struct dm_table *t)
{
int i;
/* free the indexes (see dm_table_complete) */
if (t->depth >= 2)
vfree(t->index[t->depth - 2]);
/* free the targets */
for (i = 0; i < t->num_targets; i++) {
struct target *tgt = &t->targets[i];
if (tgt->type->dtr)
tgt->type->dtr(t, tgt->private);
}
vfree(t->highs);
/* free the device list */
if (t->devices.next != &t->devices) {
WARN("there are still devices present, someone isn't "
"calling dm_table_remove_device");
free_devices(&t->devices);
}
kfree(t);
}
/*
* Checks to see if we need to extend
* highs or targets.
*/
static inline int check_space(struct dm_table *t)
{
if (t->num_targets >= t->num_allocated)
return alloc_targets(t, t->num_allocated * 2);
return 0;
}
/*
* convert a device path to a kdev_t.
*/
int lookup_device(const char *path, kdev_t *dev)
{
int r;
struct nameidata nd;
struct inode *inode;
if (!path_init(path, LOOKUP_FOLLOW, &nd))
return 0;
if ((r = path_walk(path, &nd)))
goto bad;
inode = nd.dentry->d_inode;
if (!inode) {
r = -ENOENT;
goto bad;
}
if (!S_ISBLK(inode->i_mode)) {
r = -EINVAL;
goto bad;
}
*dev = inode->i_rdev;
bad:
path_release(&nd);
return r;
}
/*
* see if we've already got a device in the list.
*/
static struct dm_dev *find_device(struct list_head *l, kdev_t dev)
{
struct list_head *tmp;
for (tmp = l->next; tmp != l; tmp = tmp->next) {
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
if (dd->dev == dev)
return dd;
}
return 0;
}
/*
* add a device to the list, or just increment the
* usage count if it's already present.
*/
int dm_table_get_device(struct dm_table *t, const char *path,
struct dm_dev **result)
{
int r;
kdev_t dev;
struct dm_dev *dd;
/* convert the path to a device */
if ((r = lookup_device(path, &dev)))
return r;
dd = find_device(&t->devices, dev);
if (!dd) {
dd = kmalloc(sizeof(*dd), GFP_KERNEL);
if (!dd)
return -ENOMEM;
dd->dev = dev;
dd->bd = 0;
atomic_set(&dd->count, 0);
list_add(&dd->list, &t->devices);
}
atomic_inc(&dd->count);
*result = dd;
return 0;
}
/*
* decrement a devices use count and remove it if
* neccessary.
*/
void dm_table_put_device(struct dm_table *t, struct dm_dev *dd)
{
if (atomic_dec_and_test(&dd->count)) {
list_del(&dd->list);
kfree(dd);
}
}
/*
* adds a target to the map
*/
int dm_table_add_target(struct dm_table *t, offset_t high,
struct target_type *type, void *private)
{
int r, n;
if ((r = check_space(t)))
return r;
n = t->num_targets++;
t->highs[n] = high;
t->targets[n].type = type;
t->targets[n].private = private;
return 0;
}
static int setup_indexes(struct dm_table *t)
{
int i, total = 0;
offset_t *indexes;
/* allocate the space for *all* the indexes */
for (i = t->depth - 2; i >= 0; i--) {
t->counts[i] = div_up(t->counts[i + 1], CHILDREN_PER_NODE);
total += t->counts[i];
}
if (!(indexes = vmalloc(NODE_SIZE * total)))
return -ENOMEM;
/* set up internal nodes, bottom-up */
for (i = t->depth - 2, total = 0; i >= 0; i--) {
t->index[i] = indexes + (KEYS_PER_NODE * t->counts[i]);
setup_btree_index(i, t);
}
return 0;
}
/*
* builds the btree to index the map
*/
int dm_table_complete(struct dm_table *t)
{
int leaf_nodes, r = 0;
/* how many indexes will the btree have ? */
leaf_nodes = div_up(t->num_targets, KEYS_PER_NODE);
t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
/* leaf layer has already been set up */
t->counts[t->depth - 1] = leaf_nodes;
t->index[t->depth - 1] = t->highs;
if (t->depth >= 2)
r = setup_indexes(t);
return r;
}
EXPORT_SYMBOL(dm_table_get_device);
EXPORT_SYMBOL(dm_table_put_device);

View File

@@ -0,0 +1,180 @@
/*
* dm-target.c
*
* Copyright (C) 2001 Sistina Software (UK) Limited
*
* This file is released under the GPL.
*/
/*
* 16/08/2001 - First Version [Joe Thornber]
*/
#include "dm.h"
#include <linux/kmod.h>
struct tt_internal {
struct target_type tt;
struct list_head list;
long use;
};
static LIST_HEAD(_targets);
static rwlock_t _lock = RW_LOCK_UNLOCKED;
#define DM_MOD_NAME_SIZE 32
static inline struct tt_internal *__find_target_type(const char *name)
{
struct list_head *tmp;
struct tt_internal *ti;
for(tmp = _targets.next; tmp != &_targets; tmp = tmp->next) {
ti = list_entry(tmp, struct tt_internal, list);
if (!strcmp(name, ti->tt.name))
return ti;
}
return 0;
}
static struct tt_internal *get_target_type(const char *name)
{
struct tt_internal *ti;
read_lock(&_lock);
ti = __find_target_type(name);
if (ti) {
if (ti->use == 0 && ti->tt.module)
__MOD_INC_USE_COUNT(ti->tt.module);
ti->use++;
}
read_unlock(&_lock);
return ti;
}
static void load_module(const char *name)
{
char module_name[DM_MOD_NAME_SIZE] = "dm-";
/* Length check for strcat() below */
if (strlen(name) > (DM_MOD_NAME_SIZE - 4))
return;
strcat(module_name, name);
request_module(module_name);
}
struct target_type *dm_get_target_type(const char *name)
{
struct tt_internal *ti = get_target_type(name);
if (!ti) {
load_module(name);
ti = get_target_type(name);
}
return ti ? &ti->tt : 0;
}
void dm_put_target_type(struct target_type *t)
{
struct tt_internal *ti = (struct tt_internal *) t;
read_lock(&_lock);
if (--ti->use == 0 && ti->tt.module)
__MOD_DEC_USE_COUNT(ti->tt.module);
if (ti->use < 0)
BUG();
read_unlock(&_lock);
}
static struct tt_internal *alloc_target(struct target_type *t)
{
struct tt_internal *ti = kmalloc(sizeof(*ti), GFP_KERNEL);
if (ti) {
memset(ti, 0, sizeof(*ti));
ti->tt = *t;
}
return ti;
}
int dm_register_target(struct target_type *t)
{
int rv = 0;
struct tt_internal *ti = alloc_target(t);
if (!ti)
return -ENOMEM;
write_lock(&_lock);
if (__find_target_type(t->name))
rv = -EEXIST;
else
list_add(&ti->list, &_targets);
write_unlock(&_lock);
return rv;
}
int dm_unregister_target(struct target_type *t)
{
struct tt_internal *ti = (struct tt_internal *) t;
int rv = -ETXTBSY;
write_lock(&_lock);
if (ti->use == 0) {
list_del(&ti->list);
kfree(ti);
rv = 0;
}
write_unlock(&_lock);
return rv;
}
/*
* io-err: always fails an io, useful for bringing
* up LV's that have holes in them.
*/
static int io_err_ctr(struct dm_table *t, offset_t b, offset_t l,
char *args, void **context)
{
*context = 0;
return 0;
}
static void io_err_dtr(struct dm_table *t, void *c)
{
/* empty */
}
static int io_err_map(struct buffer_head *bh, int rw, void *context)
{
buffer_IO_error(bh);
return 0;
}
static struct target_type error_target = {
name: "error",
ctr: io_err_ctr,
dtr: io_err_dtr,
map: io_err_map
};
int dm_target_init(void)
{
return dm_register_target(&error_target);
}
EXPORT_SYMBOL(dm_register_target);
EXPORT_SYMBOL(dm_unregister_target);

963
driver/device-mapper/dm.c Normal file
View File

@@ -0,0 +1,963 @@
/*
* device-mapper.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Changelog
*
* 14/08/2001 - First Version [Joe Thornber]
*/
#include "dm.h"
#include "dmfs.h"
#include <linux/blkpg.h>
#include <linux/blkdev.h>
#include <linux/kmod.h>
/* we only need this for the lv_bmap struct definition, not happy */
#include <linux/lvm.h>
#define MAX_DEVICES 64
#define DEFAULT_READ_AHEAD 64
static int major = 0;
const char *_name = "device-mapper";
int _version[3] = {0, 1, 0};
struct io_hook {
struct mapped_device *md;
struct target *target;
int rw;
void (*end_io)(struct buffer_head * bh, int uptodate);
void *context;
};
kmem_cache_t *_io_hook_cache;
#define rl down_read(&_dev_lock)
#define ru up_read(&_dev_lock)
#define wl down_write(&_dev_lock)
#define wu up_write(&_dev_lock)
struct rw_semaphore _dev_lock;
static struct mapped_device *_devs[MAX_DEVICES];
/* block device arrays */
static int _block_size[MAX_DEVICES];
static int _blksize_size[MAX_DEVICES];
static int _hardsect_size[MAX_DEVICES];
const char *_fs_dir = "device-mapper";
static devfs_handle_t _dev_dir;
static int request(request_queue_t *q, int rw, struct buffer_head *bh);
static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb);
/*
* setup and teardown the driver
*/
static int __init dm_init(void)
{
int ret = -ENOMEM;
init_rwsem(&_dev_lock);
_io_hook_cache = kmem_cache_create("dm io hooks",
sizeof(struct io_hook),
0, 0, NULL, NULL);
if (!_io_hook_cache)
goto err;
ret = dmfs_init();
if (ret < 0)
goto err_kmem_cache_destroy;
ret = dm_target_init();
if (ret)
goto err_kmem_cache_destroy;
ret = devfs_register_blkdev(major, _name, &dm_blk_dops);
if (ret < 0)
goto err_blkdev;
if (major == 0)
major = ret;
/* set up the arrays */
read_ahead[major] = DEFAULT_READ_AHEAD;
blk_size[major] = _block_size;
blksize_size[major] = _blksize_size;
hardsect_size[major] = _hardsect_size;
ret = -EIO;
blk_queue_make_request(BLK_DEFAULT_QUEUE(major), request);
_dev_dir = devfs_mk_dir(0, _fs_dir, NULL);
printk(KERN_INFO "%s %d.%d.%d initialised\n", _name,
_version[0], _version[1], _version[2]);
return 0;
err_blkdev:
printk(KERN_ERR "%s -- register_blkdev failed\n", _name);
err_kmem_cache_destroy:
kmem_cache_destroy(_io_hook_cache);
err:
return ret;
}
static void __exit dm_exit(void)
{
dmfs_exit();
if (kmem_cache_destroy(_io_hook_cache))
WARN("it looks like there are still some io_hooks allocated");
if (devfs_unregister_blkdev(major, _name) < 0)
printk(KERN_ERR "%s -- unregister_blkdev failed\n", _name);
read_ahead[major] = 0;
blk_size[major] = NULL;
blksize_size[major] = NULL;
hardsect_size[major] = NULL;
printk(KERN_INFO "%s %d.%d.%d cleaned up\n", _name,
_version[0], _version[1], _version[2]);
}
#ifdef CONFIG_HOTPLUG
static void dm_sbin_hotplug(struct mapped_device *md, char *action, int minor)
{
int i;
char *argv[3];
char *envp[7];
char name[DM_NAME_LEN + 16];
char dev_major[16], dev_minor[16];
if (!hotplug_path[0])
return;
if (!current->fs->root)
return;
sprintf(name, "DMNAME=%s\n", md->name);
sprintf(dev_major, "MAJOR=%d", major);
sprintf(dev_minor, "MINOR=%d", minor);
i = 0;
argv[i++] = hotplug_path;
argv[i++] = "devmap";
argv[i] = 0;
i = 0;
envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
envp[i++] = name;
envp[i++] = action;
envp[i++] = dev_minor;
envp[i++] = dev_major;
envp[i] = 0;
call_usermodehelper(argv[0], argv, envp);
}
#else
#define dm_sbin_hotplug(md, action, minor) do { } while(0)
#endif /* CONFIG_HOTPLUG */
/*
* block device functions
*/
static int dm_blk_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
struct mapped_device *md;
if (minor >= MAX_DEVICES)
return -ENXIO;
wl;
md = _devs[minor];
if (!md || !is_active(md)) {
wu;
return -ENXIO;
}
md->use_count++;
wu;
return 0;
}
static int dm_blk_close(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
struct mapped_device *md;
if (minor >= MAX_DEVICES)
return -ENXIO;
wl;
md = _devs[minor];
if (!md || md->use_count < 1) {
WARN("reference count in mapped_device incorrect");
wu;
return -ENXIO;
}
md->use_count--;
wu;
return 0;
}
/* In 512-byte units */
#define VOLUME_SIZE(minor) (_block_size[(minor)] >> 1)
static int dm_blk_ioctl(struct inode *inode, struct file *file,
uint command, ulong a)
{
int minor = MINOR(inode->i_rdev);
long size;
if (minor >= MAX_DEVICES)
return -ENXIO;
switch (command) {
case BLKSSZGET:
case BLKBSZGET:
case BLKROGET:
case BLKROSET:
case BLKRASET:
case BLKRAGET:
case BLKFLSBUF:
#if 0
case BLKELVSET:
case BLKELVGET:
#endif
return blk_ioctl(inode->i_rdev, command, a);
break;
case BLKGETSIZE:
size = VOLUME_SIZE(minor);
if (copy_to_user((void *) a, &size, sizeof (long)))
return -EFAULT;
break;
case BLKGETSIZE64:
size = VOLUME_SIZE(minor);
if (put_user((u64)size, (u64 *)a))
return -EFAULT;
break;
case BLKRRPART:
return -EINVAL;
case LV_BMAP:
return dm_user_bmap(inode, (struct lv_bmap *) a);
default:
WARN("unknown block ioctl %d", command);
return -EINVAL;
}
return 0;
}
static inline struct io_hook *alloc_io_hook(void)
{
return kmem_cache_alloc(_io_hook_cache, GFP_NOIO);
}
static inline void free_io_hook(struct io_hook *ih)
{
kmem_cache_free(_io_hook_cache, ih);
}
/*
* FIXME: need to decide if deferred_io's need
* their own slab, I say no for now since they are
* only used when the device is suspended.
*/
static inline struct deferred_io *alloc_deferred(void)
{
return kmalloc(sizeof(struct deferred_io), GFP_NOIO);
}
static inline void free_deferred(struct deferred_io *di)
{
kfree(di);
}
/*
* call a targets optional error function if
* an io failed.
*/
static inline int call_err_fn(struct io_hook *ih, struct buffer_head *bh)
{
dm_err_fn err = ih->target->type->err;
if (err)
return err(bh, ih->rw, ih->target->private);
return 0;
}
/*
* bh->b_end_io routine that decrements the
* pending count and then calls the original
* bh->b_end_io fn.
*/
static void dec_pending(struct buffer_head *bh, int uptodate)
{
struct io_hook *ih = bh->b_private;
if (!uptodate && call_err_fn(ih, bh))
return;
if (atomic_dec_and_test(&ih->md->pending))
/* nudge anyone waiting on suspend queue */
wake_up(&ih->md->wait);
bh->b_end_io = ih->end_io;
bh->b_private = ih->context;
free_io_hook(ih);
bh->b_end_io(bh, uptodate);
}
/*
* add the bh to the list of deferred io.
*/
static int queue_io(struct mapped_device *md, struct buffer_head *bh, int rw)
{
struct deferred_io *di = alloc_deferred();
if (!di)
return -ENOMEM;
wl;
if (test_bit(DM_ACTIVE, &md->state)) {
wu;
return 0;
}
di->bh = bh;
di->rw = rw;
di->next = md->deferred;
md->deferred = di;
wu;
return 1;
}
/*
* do the bh mapping for a given leaf
*/
static inline int __map_buffer(struct mapped_device *md,
struct buffer_head *bh, int rw, int leaf)
{
int r;
dm_map_fn fn;
void *context;
struct io_hook *ih = NULL;
struct target *ti = md->map->targets + leaf;
fn = ti->type->map;
context = ti->private;
ih = alloc_io_hook();
if (!ih)
return 0;
ih->md = md;
ih->rw = rw;
ih->target = ti;
ih->end_io = bh->b_end_io;
ih->context = bh->b_private;
r = fn(bh, rw, context);
if (r > 0) {
/* hook the end io request fn */
atomic_inc(&md->pending);
bh->b_end_io = dec_pending;
bh->b_private = ih;
} else if (r == 0)
/* we don't need to hook */
free_io_hook(ih);
else if (r < 0) {
free_io_hook(ih);
return 0;
}
return 1;
}
/*
* search the btree for the correct target.
*/
static inline int __find_node(struct dm_table *t, struct buffer_head *bh)
{
int l, n = 0, k = 0;
offset_t *node;
for (l = 0; l < t->depth; l++) {
n = get_child(n, k);
node = get_node(t, l, n);
for (k = 0; k < KEYS_PER_NODE; k++)
if (node[k] >= bh->b_rsector)
break;
}
return (KEYS_PER_NODE * n) + k;
}
static int request(request_queue_t *q, int rw, struct buffer_head *bh)
{
struct mapped_device *md;
int r, minor = MINOR(bh->b_rdev);
if (minor >= MAX_DEVICES)
goto bad_no_lock;
rl;
md = _devs[minor];
if (!md || !md->map)
goto bad;
/* if we're suspended we have to queue this io for later */
if (!test_bit(DM_ACTIVE, &md->state)) {
ru;
r = queue_io(md, bh, rw);
if (r < 0)
goto bad_no_lock;
else if (r > 0)
return 0; /* deferred successfully */
rl; /* FIXME: there's still a race here */
}
if (!__map_buffer(md, bh, rw, __find_node(md->map, bh)))
goto bad;
ru;
return 1;
bad:
ru;
bad_no_lock:
buffer_IO_error(bh);
return 0;
}
static int check_dev_size(int minor, unsigned long block)
{
/* FIXME: check this */
unsigned long max_sector = (_block_size[minor] << 1) + 1;
unsigned long sector = (block + 1) * (_blksize_size[minor] >> 9);
return (sector > max_sector) ? 0 : 1;
}
/*
* creates a dummy buffer head and maps it (for lilo).
*/
static int do_bmap(kdev_t dev, unsigned long block,
kdev_t *r_dev, unsigned long *r_block)
{
struct mapped_device *md;
struct buffer_head bh;
int minor = MINOR(dev), r;
struct target *t;
rl;
if ((minor >= MAX_DEVICES) || !(md = _devs[minor]) ||
!test_bit(DM_ACTIVE, &md->state)) {
r = -ENXIO;
goto out;
}
if (!check_dev_size(minor, block)) {
r = -EINVAL;
goto out;
}
/* setup dummy bh */
memset(&bh, 0, sizeof(bh));
bh.b_blocknr = block;
bh.b_dev = bh.b_rdev = dev;
bh.b_size = _blksize_size[minor];
bh.b_rsector = block * (bh.b_size >> 9);
/* find target */
t = md->map->targets + __find_node(md->map, &bh);
/* do the mapping */
r = t->type->map(&bh, READ, t->private);
*r_dev = bh.b_rdev;
*r_block = bh.b_rsector / (bh.b_size >> 9);
out:
ru;
return r;
}
/*
* marshals arguments and results between user and
* kernel space.
*/
static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb)
{
unsigned long block, r_block;
kdev_t r_dev;
int r;
if (get_user(block, &lvb->lv_block))
return -EFAULT;
if ((r = do_bmap(inode->i_rdev, block, &r_dev, &r_block)))
return r;
if (put_user(kdev_t_to_nr(r_dev), &lvb->lv_dev) ||
put_user(r_block, &lvb->lv_block))
return -EFAULT;
return 0;
}
/*
* see if the device with a specific minor # is
* free.
*/
static inline int __specific_dev(int minor)
{
if (minor > MAX_DEVICES) {
WARN("request for a mapped_device > than MAX_DEVICES");
return 0;
}
if (!_devs[minor])
return minor;
return -1;
}
/*
* find the first free device.
*/
static inline int __any_old_dev(void)
{
int i;
for (i = 0; i < MAX_DEVICES; i++)
if (!_devs[i])
return i;
return -1;
}
/*
* allocate and initialise a blank device.
*/
static struct mapped_device *alloc_dev(int minor)
{
struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL);
if (!md)
return 0;
memset(md, 0, sizeof (*md));
wl;
minor = (minor < 0) ? __any_old_dev() : __specific_dev(minor);
if (minor < 0) {
WARN("no free devices available");
wu;
kfree(md);
return 0;
}
md->dev = MKDEV(DM_BLK_MAJOR, minor);
md->name[0] = '\0';
md->state = 0;
init_waitqueue_head(&md->wait);
_devs[minor] = md;
wu;
return md;
}
/*
* open a device so we can use it as a map
* destination.
*/
static int open_dev(struct dm_dev *d)
{
int err;
if (d->bd)
BUG();
if (!(d->bd = bdget(kdev_t_to_nr(d->dev))))
return -ENOMEM;
if ((err = blkdev_get(d->bd, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE))) {
bdput(d->bd);
return err;
}
return 0;
}
/*
* close a device that we've been using.
*/
static void close_dev(struct dm_dev *d)
{
if (!d->bd)
return;
blkdev_put(d->bd, BDEV_FILE);
bdput(d->bd);
d->bd = 0;
}
/*
* Close a list of devices.
*/
static void close_devices(struct list_head *devices)
{
struct list_head *tmp;
for (tmp = devices->next; tmp != devices; tmp = tmp->next) {
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
close_dev(dd);
}
}
/*
* Open a list of devices.
*/
static int open_devices(struct list_head *devices)
{
int r = 0;
struct list_head *tmp;
for (tmp = devices->next; tmp != devices; tmp = tmp->next) {
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
if ((r = open_dev(dd)))
goto bad;
}
return 0;
bad:
close_devices(devices);
return r;
}
struct mapped_device *dm_find_by_minor(int minor)
{
struct mapped_device *md;
rl;
md = _devs[minor];
ru;
return md;
}
static int register_device(struct mapped_device *md)
{
md->devfs_entry =
devfs_register(_dev_dir, md->name, DEVFS_FL_CURRENT_OWNER,
MAJOR(md->dev), MINOR(md->dev),
S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
&dm_blk_dops, NULL);
return 0;
}
static int unregister_device(struct mapped_device *md)
{
devfs_unregister(md->devfs_entry);
return 0;
}
/*
* constructor for a new device
*/
struct mapped_device *dm_create(const char *name, int minor)
{
int r;
struct mapped_device *md;
if (minor >= MAX_DEVICES)
return ERR_PTR(-ENXIO);
if (!(md = alloc_dev(minor)))
return ERR_PTR(-ENXIO);
wl;
strcpy(md->name, name);
_devs[minor] = md;
if ((r = register_device(md))) {
wu;
return ERR_PTR(r);
}
wu;
dm_sbin_hotplug(md, "ACTION=create", minor);
return md;
}
/*
* destructor for the device. md->map is
* deliberately not destroyed, dm-fs should manage
* table objects.
*/
int dm_remove(struct mapped_device *md)
{
int minor, r;
wl;
if (md->use_count) {
wu;
return -EPERM;
}
if ((r = unregister_device(md))) {
wu;
return r;
}
minor = MINOR(md->dev);
_devs[minor] = 0;
wu;
dm_sbin_hotplug(md, "ACTION=remove", minor);
kfree(md);
return 0;
}
/*
* the hardsect size for a mapped device is the
* smallest hard sect size from the devices it
* maps onto.
*/
static int __find_hardsect_size(struct list_head *devices)
{
int result = INT_MAX, size;
struct list_head *tmp;
for (tmp = devices->next; tmp != devices; tmp = tmp->next) {
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
size = get_hardsect_size(dd->dev);
if (size < result)
result = size;
}
return result;
}
/*
* Bind a table to the device.
*/
void __bind(struct mapped_device *md, struct dm_table *t)
{
int minor = MINOR(md->dev);
md->map = t;
/* in k */
_block_size[minor] = (t->highs[t->num_targets - 1] + 1) >> 1;
_blksize_size[minor] = BLOCK_SIZE;
_hardsect_size[minor] = __find_hardsect_size(&t->devices);
register_disk(NULL, md->dev, 1, &dm_blk_dops, _block_size[minor]);
}
/*
* requeue the deferred buffer_heads by calling
* generic_make_request.
*/
static void __flush_deferred_io(struct mapped_device *md)
{
struct deferred_io *c, *n;
for (c = md->deferred, md->deferred = 0; c; c = n) {
n = c->next;
generic_make_request(c->rw, c->bh);
free_deferred(c);
}
}
/*
* make the device available for use, if was
* previously suspended rather than newly created
* then all queued io is flushed
*/
int dm_activate(struct mapped_device *md, struct dm_table *table)
{
int r;
/* check that the mapping has at least been loaded. */
if (!table->num_targets)
return -EINVAL;
wl;
/* you must be deactivated first */
if (is_active(md)) {
wu;
return -EPERM;
}
__bind(md, table);
if ((r = open_devices(&md->map->devices))) {
wu;
return r;
}
set_bit(DM_ACTIVE, &md->state);
__flush_deferred_io(md);
wu;
return 0;
}
/*
* Deactivate the device, the device must not be
* opened by anyone.
*/
int dm_deactivate(struct mapped_device *md)
{
rl;
if (md->use_count) {
ru;
return -EPERM;
}
fsync_dev(md->dev);
ru;
wl;
if (md->use_count) {
/* drat, somebody got in quick ... */
wu;
return -EPERM;
}
if (md->map)
close_devices(&md->map->devices);
md->map = NULL;
clear_bit(DM_ACTIVE, &md->state);
wu;
return 0;
}
/*
* We need to be able to change a mapping table
* under a mounted filesystem. for example we
* might want to move some data in the background.
* Before the table can be swapped with
* dm_bind_table, dm_suspend must be called to
* flush any in flight buffer_heads and ensure
* that any further io gets deferred.
*/
void dm_suspend(struct mapped_device *md)
{
DECLARE_WAITQUEUE(wait, current);
wl;
if (!is_active(md)) {
wu;
return;
}
clear_bit(DM_ACTIVE, &md->state);
wu;
/* wait for all the pending io to flush */
add_wait_queue(&md->wait, &wait);
current->state = TASK_UNINTERRUPTIBLE;
do {
wl;
if (!atomic_read(&md->pending))
break;
wu;
schedule();
} while (1);
current->state = TASK_RUNNING;
remove_wait_queue(&md->wait, &wait);
close_devices(&md->map->devices);
md->map = 0;
wu;
}
struct block_device_operations dm_blk_dops = {
open: dm_blk_open,
release: dm_blk_close,
ioctl: dm_blk_ioctl,
owner: THIS_MODULE,
};
/*
* module hooks
*/
module_init(dm_init);
module_exit(dm_exit);
MODULE_PARM(major, "i");
MODULE_PARM_DESC(major, "The major device number of the device-mapper");
MODULE_DESCRIPTION("device-mapper driver");
MODULE_AUTHOR("Joe Thornber <thornber@btconnect.com>");
MODULE_LICENSE("GPL");
/*
* Local variables:
* c-file-style: "linux"
* End:
*/

256
driver/device-mapper/dm.h Normal file
View File

@@ -0,0 +1,256 @@
/*
* dm.h
*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the GPL.
*/
/*
* Internal header file for device mapper
*
* Changelog
*
* 16/08/2001 - First version [Joe Thornber]
*/
/*
* This driver attempts to provide a generic way of specifying logical
* devices which are mapped onto other devices.
*
* It does this by mapping sections of the logical device onto 'targets'.
*
* When the logical device is accessed the make_request function looks up
* the correct target for the given sector, and then asks this target
* to do the remapping.
*
* (dm-table.c) A btree like structure is used to hold the sector
* range -> target mapping. Because we know all the entries in the
* btree in advance we can make a very compact tree, omitting pointers
* to child nodes, (child nodes locations can be calculated). Each
* node of the btree is 1 level cache line in size, this gives a small
* performance boost.
*
* A userland test program for the btree gave the following results on a
* 1 Gigahertz Athlon machine:
*
* entries in btree lookups per second
* ---------------- ------------------
* 5 25,000,000
* 1000 7,700,000
* 10,000,000 3,800,000
*
* Of course these results should be taken with a pinch of salt; the
* lookups were sequential and there were no other applications (other
* than X + emacs) running to give any pressure on the level 1 cache.
*
* Typical LVM users would find they have very few targets for each
* LV (probably less than 10).
*
* (dm-target.c) Target types are not hard coded, instead the
* register_mapping_type function should be called. A target type is
* specified using three functions (see the header):
*
* dm_ctr_fn - takes a string and contructs a target specific piece of
* context data.
* dm_dtr_fn - destroy contexts.
* dm_map_fn - function that takes a buffer_head and some previously
* constructed context and performs the remapping.
*
* Currently there are two two trivial mappers, which are
* automatically registered: 'linear', and 'io_error'. Linear alone
* is enough to implement most LVM features (omitting striped volumes
* and snapshots).
*
* (dm-fs.c) The driver is controlled through a /proc interface:
* /proc/device-mapper/control allows you to create and remove devices
* by 'cat'ing a line of the following format:
*
* create <device name> [minor no]
* remove <device name>
*
* /proc/device-mapper/<device name> accepts the mapping table:
*
* begin
* <sector start> <length> <target name> <target args>...
* ...
* end
*
* The begin/end lines are nasty, they should be handled by open/close
* for the file.
*
* At the moment the table assumes 32 bit keys (sectors), the move to
* 64 bits will involve no interface changes, since the tables will be
* read in as ascii data. A different table implementation can
* therefor be provided at another time. Either just by changing offset_t
* to 64 bits, or maybe implementing a structure which looks up the keys in
* stages (ie, 32 bits at a time).
*
* More interesting targets:
*
* striped mapping; given a stripe size and a number of device regions
* this would stripe data across the regions. Especially useful, since
* we could limit each striped region to a 32 bit area and then avoid
* nasty 64 bit %'s.
*
* mirror mapping (reflector ?); would set off a kernel thread slowly
* copying data from one region to another, ensuring that any new
* writes got copied to both destinations correctly. Great for
* implementing pvmove. Not sure how userland would be notified that
* the copying process had completed. Possibly by reading a /proc entry
* for the LV. Could also use poll() for this kind of thing.
*/
#ifndef DM_INTERNAL_H
#define DM_INTERNAL_H
#include <linux/version.h>
#include <linux/major.h>
#include <linux/iobuf.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/compatmac.h>
#include <linux/cache.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/ctype.h>
#include <linux/device-mapper.h>
#include <linux/list.h>
#define MAX_DEPTH 16
#define NODE_SIZE L1_CACHE_BYTES
#define KEYS_PER_NODE (NODE_SIZE / sizeof(offset_t))
#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
#define DM_NAME_LEN 128
#define MAX_TARGET_LINE 256
enum {
DM_BOUND = 0, /* device has been bound to a table */
DM_ACTIVE, /* device is running */
};
/*
* list of devices that a metadevice uses
* and hence should open/close.
*/
struct dm_dev {
atomic_t count;
struct list_head list;
kdev_t dev;
struct block_device *bd;
};
/*
* io that had to be deferred while we were
* suspended
*/
struct deferred_io {
int rw;
struct buffer_head *bh;
struct deferred_io *next;
};
/*
* btree leaf, these do the actual mapping
*/
struct target {
struct target_type *type;
void *private;
};
/*
* the btree
*/
struct dm_table {
/* btree table */
int depth;
int counts[MAX_DEPTH]; /* in nodes */
offset_t *index[MAX_DEPTH];
int num_targets;
int num_allocated;
offset_t *highs;
struct target *targets;
/* a list of devices used by this table */
struct list_head devices;
};
/*
* the actual device struct
*/
struct mapped_device {
kdev_t dev;
char name[DM_NAME_LEN];
int use_count;
int state;
/* a list of io's that arrived while we were suspended */
atomic_t pending;
wait_queue_head_t wait;
struct deferred_io *deferred;
struct dm_table *map;
/* used by dm-fs.c */
devfs_handle_t devfs_entry;
};
extern struct block_device_operations dm_blk_dops;
/* dm-target.c */
int dm_target_init(void);
struct target_type *dm_get_target_type(const char *name);
void dm_put_target_type(struct target_type *t);
/* dm.c */
struct mapped_device *dm_find_by_minor(int minor);
struct mapped_device *dm_create(const char *name, int minor);
int dm_remove(struct mapped_device *md);
int dm_activate(struct mapped_device *md, struct dm_table *t);
int dm_deactivate(struct mapped_device *md);
void dm_suspend(struct mapped_device *md);
/* dm-table.c */
struct dm_table *dm_table_create(void);
void dm_table_destroy(struct dm_table *t);
int dm_table_add_target(struct dm_table *t, offset_t high,
struct target_type *type, void *private);
int dm_table_complete(struct dm_table *t);
#define WARN(f, x...) printk(KERN_WARNING "device-mapper: " f "\n" , ## x)
/*
* calculate the index of the child node of the
* n'th node k'th key.
*/
static inline int get_child(int n, int k)
{
return (n * CHILDREN_PER_NODE) + k;
}
/*
* returns the n'th node of level l from table t.
*/
static inline offset_t *get_node(struct dm_table *t, int l, int n)
{
return t->index[l] + (n * KEYS_PER_NODE);
}
static inline int is_active(struct mapped_device *md)
{
return test_bit(DM_ACTIVE, &md->state);
}
#endif

View File

@@ -0,0 +1,154 @@
/*
* dmfs-error.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <linux/config.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include "dm.h"
#include "dmfs.h"
struct dmfs_error {
struct list_head list;
unsigned len;
char *msg;
};
static struct dmfs_error oom_error;
static struct list_head oom_list = {
next: &oom_error.list,
prev: &oom_error.list,
};
static struct dmfs_error oom_error = {
list: { next: &oom_list, prev: &oom_list },
len: 39,
msg: "Out of memory during creation of table\n",
};
void dmfs_add_error(struct inode *inode, unsigned num, char *str)
{
struct dmfs_i *dmi = DMFS_I(inode);
int len = strlen(str) + sizeof(struct dmfs_error) + 12;
struct dmfs_error *e = kmalloc(len, GFP_KERNEL);
if (e) {
e->msg = (char *)(e + 1);
e->len = sprintf(e->msg, "%8u: %s\n", num, str);
list_add(&e->list, &dmi->errors);
}
}
void dmfs_zap_errors(struct inode *inode)
{
struct dmfs_i *dmi = DMFS_I(inode);
struct dmfs_error *e;
while(!list_empty(&dmi->errors)) {
e = list_entry(dmi->errors.next, struct dmfs_error, list);
list_del(&e->list);
kfree(e);
}
}
static void *e_start(void *context, loff_t *pos)
{
struct list_head *p;
loff_t n = *pos;
struct dmfs_i *dmi = context;
down(&dmi->sem);
if (dmi->status) {
list_for_each(p, &oom_list)
if (n-- == 0)
return list_entry(p, struct dmfs_error, list);
} else {
list_for_each(p, &dmi->errors)
if (n-- == 0)
return list_entry(p, struct dmfs_error, list);
}
return NULL;
}
static void *e_next(void *context, void *v, loff_t *pos)
{
struct dmfs_i *dmi = context;
struct list_head *p = ((struct dmfs_error *)v)->list.next;
(*pos)++;
return (p == &dmi->errors) || (p == &oom_list) ? NULL
: list_entry(p, struct dmfs_error, list);
}
static void e_stop(void *context, void *v)
{
struct dmfs_i *dmi = context;
up(&dmi->sem);
}
static int show_error(struct seq_file *e, void *v)
{
struct dmfs_error *d = v;
seq_puts(e, d->msg);
return 0;
}
static struct seq_operations error_op = {
start: e_start,
next: e_next,
stop: e_stop,
show: show_error,
};
static int dmfs_error_open(struct inode *inode, struct file *file)
{
return seq_open(file, &error_op, DMFS_I(file->f_dentry->d_parent->d_inode));
}
static int dmfs_error_sync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static struct file_operations dmfs_error_file_operations = {
open: dmfs_error_open,
read: seq_read,
llseek: seq_lseek,
release: seq_release,
fsync: dmfs_error_sync,
};
static struct inode_operations dmfs_error_inode_operations = {
};
struct inode *dmfs_create_error(struct inode *dir, int mode)
{
struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFREG);
if (inode) {
inode->i_fop = &dmfs_error_file_operations;
inode->i_op = &dmfs_error_inode_operations;
}
return inode;
}

View File

@@ -0,0 +1,286 @@
/*
* dmfs-lv.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Heavily based upon ramfs */
#include <linux/config.h>
#include <linux/ctype.h>
#include <linux/fs.h>
#include "dm.h"
#include "dmfs.h"
extern struct address_space_operations dmfs_address_space_operations;
extern struct inode *dmfs_create_tdir(struct super_block *sb, int mode);
struct dentry *dmfs_verify_name(struct inode *dir, const char *name)
{
struct nameidata nd;
int err = -ENOENT;
struct file file;
struct dentry *dentry;
memset(&file, 0, sizeof(struct file));
if (!path_init(name, LOOKUP_FOLLOW, &nd))
return ERR_PTR(-EINVAL);
err = path_walk(name, &nd);
if (err)
goto err_out;
err = -EINVAL;
if (nd.mnt->mnt_sb != dir->i_sb)
goto err_out;
if (nd.dentry->d_parent->d_inode != dir)
goto err_out;
err = -ENODATA;
if (DMFS_I(nd.dentry->d_inode) == NULL ||
DMFS_I(nd.dentry->d_inode)->table == NULL)
goto err_out;
if (!list_empty(&(DMFS_I(nd.dentry->d_inode)->errors)))
goto err_out;
dentry = nd.dentry;
file.f_dentry = nd.dentry;
err = deny_write_access(&file);
if (err)
goto err_out;
dget(dentry);
path_release(&nd);
return dentry;
err_out:
path_release(&nd);
return ERR_PTR(err);
}
struct inode *dmfs_create_symlink(struct inode *dir, int mode)
{
struct inode *inode = dmfs_new_private_inode(dir->i_sb, mode | S_IFLNK);
if (inode) {
inode->i_mapping->a_ops = &dmfs_address_space_operations;
inode->i_op = &page_symlink_inode_operations;
}
return inode;
}
static int dmfs_lv_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct file file = { f_dentry: DMFS_I(inode)->dentry };
if (!(inode->i_mode & S_IFLNK))
return -EINVAL;
dm_suspend(DMFS_I(dir)->md);
allow_write_access(&file);
dput(DMFS_I(inode)->dentry);
DMFS_I(inode)->dentry = NULL;
inode->i_nlink--;
dput(dentry);
return 0;
}
static int dmfs_lv_symlink(struct inode *dir, struct dentry *dentry,
const char *symname)
{
struct inode *inode;
struct dentry *de;
int rv;
int l;
if (dentry->d_name.len != 6 ||
memcmp(dentry->d_name.name, "ACTIVE", 6) != 0)
return -EINVAL;
de = dmfs_verify_name(dir, symname);
if (IS_ERR(de))
return PTR_ERR(de);
inode = dmfs_create_symlink(dir, S_IRWXUGO);
if (inode == NULL) {
rv = -ENOSPC;
goto out_allow_write;
}
DMFS_I(inode)->dentry = de;
d_instantiate(dentry, inode);
dget(dentry);
l = strlen(symname) + 1;
rv = block_symlink(inode, symname, l);
if (rv)
goto out_dput;
rv = dm_activate(DMFS_I(dir)->md, DMFS_I(de->d_inode)->table);
if (rv)
goto out_dput;
return rv;
out_dput:
dput(dentry);
DMFS_I(inode)->dentry = NULL;
out_allow_write:
{
struct file file = { f_dentry: de };
allow_write_access(&file);
dput(de);
}
return rv;
}
static int is_identifier(const char *str, int len)
{
while(len--) {
if (!isalnum(*str) && *str != '_')
return 0;
str++;
}
return 1;
}
static int dmfs_lv_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct inode *inode;
int rv = -ENOSPC;
if (dentry->d_name.len >= DM_NAME_LEN)
return -EINVAL;
if (!is_identifier(dentry->d_name.name, dentry->d_name.len))
return -EPERM;
if (dentry->d_name.len == 6 &&
memcmp(dentry->d_name.name, "ACTIVE", 6) == 0)
return -EINVAL;
if (dentry->d_name.name[0] == '.')
return -EINVAL;
inode = dmfs_create_tdir(dir->i_sb, mode);
if (inode) {
d_instantiate(dentry, inode);
dget(dentry);
rv = 0;
}
return rv;
}
/*
* if u.generic_ip is not NULL, then it indicates an inode which
* represents a table. If it is NULL then the inode is a virtual
* file and should be deleted along with the directory.
*/
static inline int positive(struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
static int empty(struct dentry *dentry)
{
struct list_head *list;
spin_lock(&dcache_lock);
list = dentry->d_subdirs.next;
while(list != &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_child);
if (positive(de)) {
spin_unlock(&dcache_lock);
return 0;
}
list = list->next;
}
spin_unlock(&dcache_lock);
return 1;
}
static int dmfs_lv_rmdir(struct inode *dir, struct dentry *dentry)
{
int ret = -ENOTEMPTY;
if (empty(dentry)) {
struct inode *inode = dentry->d_inode;
inode->i_nlink--;
dput(dentry);
ret = 0;
}
return ret;
}
static struct dentry *dmfs_lv_lookup(struct inode *dir, struct dentry *dentry)
{
d_add(dentry, NULL);
return NULL;
}
static int dmfs_lv_sync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static struct file_operations dmfs_lv_file_operations = {
read: generic_read_dir,
readdir: dcache_readdir,
fsync: dmfs_lv_sync,
};
static struct inode_operations dmfs_lv_inode_operations = {
lookup: dmfs_lv_lookup,
unlink: dmfs_lv_unlink,
symlink: dmfs_lv_symlink,
mkdir: dmfs_lv_mkdir,
rmdir: dmfs_lv_rmdir,
};
struct inode *dmfs_create_lv(struct super_block *sb, int mode, struct dentry *dentry)
{
struct inode *inode = dmfs_new_private_inode(sb, mode | S_IFDIR);
struct mapped_device *md;
const char *name = dentry->d_name.name;
char tmp_name[DM_NAME_LEN + 1];
if (inode) {
inode->i_fop = &dmfs_lv_file_operations;
inode->i_op = &dmfs_lv_inode_operations;
memcpy(tmp_name, name, dentry->d_name.len);
tmp_name[dentry->d_name.len] = 0;
md = dm_create(tmp_name, -1);
if (IS_ERR(md)) {
iput(inode);
return ERR_PTR(PTR_ERR(md));
}
DMFS_I(inode)->md = md;
}
return inode;
}

View File

@@ -0,0 +1,158 @@
/*
* dmfs-root.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Heavily based upon ramfs */
#include <linux/config.h>
#include <linux/ctype.h>
#include <linux/fs.h>
#include "dm.h"
#include "dmfs.h"
extern struct inode *dmfs_create_lv(struct super_block *sb, int mode, struct dentry *dentry);
static int is_identifier(const char *str, int len)
{
while(len--) {
if (!isalnum(*str) && *str != '_')
return 0;
str++;
}
return 1;
}
static int dmfs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct inode *inode;
int rv = -ENOSPC;
if (dentry->d_name.len >= DM_NAME_LEN)
return -EINVAL;
if (!is_identifier(dentry->d_name.name, dentry->d_name.len))
return -EPERM;
if (dentry->d_name.name[0] == '.')
return -EINVAL;
inode = dmfs_create_lv(dir->i_sb, mode, dentry);
if (inode) {
d_instantiate(dentry, inode);
dget(dentry);
rv = 0;
}
return rv;
}
/*
* if u.generic_ip is not NULL, then it indicates an inode which
* represents a table. If it is NULL then the inode is a virtual
* file and should be deleted along with the directory.
*/
static inline int positive(struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
static int empty(struct dentry *dentry)
{
struct list_head *list;
spin_lock(&dcache_lock);
list = dentry->d_subdirs.next;
while(list != &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_child);
if (positive(de)) {
spin_unlock(&dcache_lock);
return 0;
}
list = list->next;
}
spin_unlock(&dcache_lock);
return 1;
}
static int dmfs_root_rmdir(struct inode *dir, struct dentry *dentry)
{
int ret = -ENOTEMPTY;
if (empty(dentry)) {
struct inode *inode = dentry->d_inode;
ret = dm_deactivate(DMFS_I(inode)->md);
if (ret == 0) {
inode->i_nlink--;
dput(dentry);
}
}
return ret;
}
static struct dentry *dmfs_root_lookup(struct inode *dir, struct dentry *dentry)
{
d_add(dentry, NULL);
return NULL;
}
static int dmfs_root_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
/* Can only rename - not move between directories! */
if (old_dir != new_dir)
return -EPERM;
return -EINVAL; /* FIXME: a change of LV name here */
}
static int dmfs_root_sync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static struct file_operations dmfs_root_file_operations = {
read: generic_read_dir,
readdir: dcache_readdir,
fsync: dmfs_root_sync,
};
static struct inode_operations dmfs_root_inode_operations = {
lookup: dmfs_root_lookup,
mkdir: dmfs_root_mkdir,
rmdir: dmfs_root_rmdir,
rename: dmfs_root_rename,
};
struct inode *dmfs_create_root(struct super_block *sb, int mode)
{
struct inode *inode = dmfs_new_inode(sb, mode | S_IFDIR);
if (inode) {
inode->i_fop = &dmfs_root_file_operations;
inode->i_op = &dmfs_root_inode_operations;
}
return inode;
}

View File

@@ -0,0 +1,57 @@
/*
* dmfs-status.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <linux/config.h>
#include <linux/fs.h>
#include "dm.h"
#include "dmfs.h"
static ssize_t dmfs_status_read(struct file *file, char *buf, size_t size, loff_t *pos)
{
return 0;
}
static int dmfs_status_sync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static struct file_operations dmfs_status_file_operations = {
read: dmfs_status_read,
fsync: dmfs_status_sync,
};
static struct inode_operations dmfs_status_inode_operations = {
};
struct inode *dmfs_create_status(struct inode *dir, int mode)
{
struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFREG);
if (inode) {
inode->i_fop = &dmfs_status_file_operations;
inode->i_op = &dmfs_status_inode_operations;
}
return inode;
}

View File

@@ -0,0 +1,162 @@
/*
* dmfs-super.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/init.h>
#include "dmfs.h"
#include "dm.h"
#define DMFS_MAGIC 0x444D4653
extern struct inode *dmfs_create_root(struct super_block *sb, int);
static int dmfs_statfs(struct super_block *sb, struct statfs *buf)
{
buf->f_type = sb->s_magic;
buf->f_bsize = sb->s_blocksize;
buf->f_namelen = DM_NAME_LEN - 1;
return 0;
}
static void dmfs_delete_inode(struct inode *inode)
{
struct dmfs_i *dmi = DMFS_I(inode);
if (dmi) {
if (dmi->md)
dm_remove(dmi->md);
if (dmi->table)
dm_table_destroy(dmi->table);
if (dmi->dentry)
dput(dmi->dentry);
if (!list_empty(&dmi->errors))
dmfs_zap_errors(inode);
kfree(dmi);
MOD_DEC_USE_COUNT; /* Don't remove */
}
inode->u.generic_ip = NULL;
clear_inode(inode);
}
static struct super_operations dmfs_super_operations = {
statfs: dmfs_statfs,
put_inode: force_delete,
delete_inode: dmfs_delete_inode,
};
static struct super_block *dmfs_read_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = DMFS_MAGIC;
sb->s_op = &dmfs_super_operations;
sb->s_maxbytes = MAX_NON_LFS;
inode = dmfs_create_root(sb, 0755);
if (IS_ERR(inode))
return NULL;
root = d_alloc_root(inode);
if (!root) {
iput(inode);
return NULL;
}
sb->s_root = root;
return sb;
}
struct inode *dmfs_new_inode(struct super_block *sb, int mode)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_rdev = NODEV;
inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
}
return inode;
}
struct inode *dmfs_new_private_inode(struct super_block *sb, int mode)
{
struct inode *inode = dmfs_new_inode(sb, mode);
struct dmfs_i *dmi;
if (inode) {
dmi = kmalloc(sizeof(struct dmfs_i), GFP_KERNEL);
if (dmi == NULL) {
iput(inode);
return NULL;
}
memset(dmi, 0, sizeof(struct dmfs_i));
init_MUTEX(&dmi->sem);
INIT_LIST_HEAD(&dmi->errors);
inode->u.generic_ip = dmi;
MOD_INC_USE_COUNT; /* Don't remove */
}
return inode;
}
static DECLARE_FSTYPE(dmfs_fstype, "dmfs", dmfs_read_super, FS_SINGLE);
static struct vfsmount *dmfs_mnt;
int __init dmfs_init(void)
{
int ret;
ret = register_filesystem(&dmfs_fstype);
if (ret < 0)
goto out;
dmfs_mnt = kern_mount(&dmfs_fstype);
if (IS_ERR(dmfs_mnt)) {
ret = PTR_ERR(dmfs_mnt);
unregister_filesystem(&dmfs_fstype);
} else {
MOD_DEC_USE_COUNT; /* Yes, this really is correct... */
}
out:
return ret;
}
int __exit dmfs_exit(void)
{
MOD_INC_USE_COUNT; /* So that it lands up being zero */
do_umount(dmfs_mnt, 0);
unregister_filesystem(&dmfs_fstype);
return 0;
}

View File

@@ -0,0 +1,353 @@
/*
* dmfs-table.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include "dm.h"
#include "dmfs.h"
static offset_t start_of_next_range(struct dm_table *t)
{
offset_t n = 0;
if (t->num_targets) {
n = t->highs[t->num_targets - 1] + 1;
}
return n;
}
static char *dmfs_parse_line(struct dm_table *t, char *str)
{
offset_t start, size, high;
void *context;
struct target_type *ttype;
int rv = 0;
char *msg;
int pos = 0;
char target[33];
static char *err_table[] = {
"Missing/Invalid start argument",
"Missing/Invalid size argument",
"Missing target type"
};
printk("dmfs_parse_line: (%s)\n", str);
rv = sscanf(str, "%d %d %32s%n", &start, &size, target, &pos);
if (rv < 3) {
msg = err_table[rv];
goto out;
}
str += pos;
while(*str && isspace(*str))
str++;
msg = "Gap in table";
if (start != start_of_next_range(t))
goto out;
msg = "Target type unknown";
ttype = dm_get_target_type(target);
if (ttype) {
msg = "This message should never appear (constructor error)";
rv = ttype->ctr(t, start, size, str, &context);
msg = context;
if (rv == 0) {
printk("dmfs_parse: %u %u %s %s\n", start, size,
ttype->name,
ttype->print ? ttype->print(context) : "-");
msg = "Error adding target to table";
high = start + (size - 1);
if (dm_table_add_target(t, high, ttype, context) == 0)
return NULL;
ttype->dtr(t, context);
}
dm_put_target_type(ttype);
}
out:
return msg;
}
static int dmfs_copy(char *dst, int dstlen, char *src, int srclen, int *flag)
{
int len = min(dstlen, srclen);
char *start = dst;
while(len) {
*dst = *src++;
if (*dst == '\n')
goto end_of_line;
dst++;
len--;
}
out:
return (dst - start);
end_of_line:
dst++;
*flag = 1;
goto out;
}
static int dmfs_line_is_not_comment(char *str)
{
while(*str) {
if (*str == '#')
break;
if (!isspace(*str))
return 1;
str++;
}
return 0;
}
struct dmfs_desc {
struct dm_table *table;
struct inode *inode;
char *tmp;
loff_t tmpl;
unsigned long lnum;
};
static int dmfs_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset, unsigned long size)
{
char *buf, *msg;
unsigned long count = desc->count, len, copied;
struct dmfs_desc *d = (struct dmfs_desc *)desc->buf;
if (size > count)
size = count;
len = size;
buf = kmap(page);
do {
int flag = 0;
copied = dmfs_copy(d->tmp + d->tmpl, PAGE_SIZE - d->tmpl - 1,
buf + offset, len, &flag);
offset += copied;
len -= copied;
if (d->tmpl + copied == PAGE_SIZE - 1)
goto line_too_long;
d->tmpl += copied;
if (flag || (len == 0 && count == size)) {
*(d->tmp + d->tmpl) = 0;
if (dmfs_line_is_not_comment(d->tmp)) {
msg = dmfs_parse_line(d->table, d->tmp);
if (msg) {
dmfs_add_error(d->inode, d->lnum, msg);
}
}
d->lnum++;
d->tmpl = 0;
}
} while(len > 0);
kunmap(page);
desc->count = count - size;
desc->written += size;
return size;
line_too_long:
printk(KERN_INFO "dmfs_read_actor: Line %lu too long\n", d->lnum);
kunmap(page);
return 0;
}
static struct dm_table *dmfs_parse(struct inode *inode, struct file *filp)
{
struct dm_table *t = NULL;
unsigned long page;
struct dmfs_desc d;
loff_t pos = 0;
if (inode->i_size == 0)
return NULL;
page = __get_free_page(GFP_NOFS);
if (page) {
t = dm_table_create();
if (t) {
read_descriptor_t desc;
desc.written = 0;
desc.count = inode->i_size;
desc.buf = (char *)&d;
d.table = t;
d.inode = inode;
d.tmp = (char *)page;
d.tmpl = 0;
d.lnum = 1;
do_generic_file_read(filp, &pos, &desc, dmfs_read_actor);
if (desc.written != inode->i_size) {
dm_table_destroy(t);
t = NULL;
}
}
free_page(page);
}
return t;
}
static int dmfs_table_release(struct inode *inode, struct file *f)
{
struct dentry *dentry = f->f_dentry;
struct inode *parent = dentry->d_parent->d_inode;
struct dmfs_i *dmi = DMFS_I(parent);
struct dm_table *table;
if (f->f_mode & FMODE_WRITE) {
down(&dmi->sem);
dmfs_zap_errors(dentry->d_parent->d_inode);
table = dmfs_parse(dentry->d_parent->d_inode, f);
if (table) {
if (dmi->table)
dm_table_destroy(dmi->table);
dmi->table = table;
}
up(&dmi->sem);
put_write_access(parent);
}
return 0;
}
static int dmfs_readpage(struct file *file, struct page *page)
{
if (!Page_Uptodate(page)) {
memset(kmap(page), 0, PAGE_CACHE_SIZE);
kunmap(page);
flush_dcache_page(page);
SetPageUptodate(page);
}
UnlockPage(page);
return 0;
}
static int dmfs_prepare_write(struct file *file, struct page *page,
unsigned offset, unsigned to)
{
void *addr = kmap(page);
if (!Page_Uptodate(page)) {
memset(addr, 0, PAGE_CACHE_SIZE);
flush_dcache_page(page);
SetPageUptodate(page);
}
SetPageDirty(page);
return 0;
}
static int dmfs_commit_write(struct file *file, struct page *page,
unsigned offset, unsigned to)
{
struct inode *inode = page->mapping->host;
loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to;
kunmap(page);
if (pos > inode->i_size)
inode->i_size = pos;
return 0;
}
/*
* There is a small race here in that two processes might call this at
* the same time and both fail. So its a fail safe race :-) This should
* move into namei.c (and thus use the spinlock and do this properly)
* at some stage if we continue to use this set of functions for ensuring
* exclusive write access to the file
*/
static int get_exclusive_write_access(struct inode *inode)
{
if (get_write_access(inode))
return -1;
if (atomic_read(&inode->i_writecount) != 1) {
put_write_access(inode);
return -1;
}
return 0;
}
static int dmfs_table_open(struct inode *inode, struct file *file)
{
struct dentry *dentry = file->f_dentry;
struct inode *parent = dentry->d_parent->d_inode;
if (file->f_mode & FMODE_WRITE) {
if (get_exclusive_write_access(parent))
return -EPERM;
}
return 0;
}
static int dmfs_table_sync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static int dmfs_table_revalidate(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct inode *parent = dentry->d_parent->d_inode;
inode->i_size = parent->i_size;
return 0;
}
struct address_space_operations dmfs_address_space_operations = {
readpage: dmfs_readpage,
writepage: fail_writepage,
prepare_write: dmfs_prepare_write,
commit_write: dmfs_commit_write,
};
static struct file_operations dmfs_table_file_operations = {
llseek: generic_file_llseek,
read: generic_file_read,
write: generic_file_write,
open: dmfs_table_open,
release: dmfs_table_release,
fsync: dmfs_table_sync,
};
static struct inode_operations dmfs_table_inode_operations = {
revalidate: dmfs_table_revalidate,
};
struct inode *dmfs_create_table(struct inode *dir, int mode)
{
struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFREG);
if (inode) {
inode->i_mapping = dir->i_mapping;
inode->i_mapping->a_ops = &dmfs_address_space_operations;
inode->i_fop = &dmfs_table_file_operations;
inode->i_op = &dmfs_table_inode_operations;
}
return inode;
}

View File

@@ -0,0 +1,138 @@
/*
* dmfs-tdir.c
*
* Copyright (C) 2001 Sistina Software
*
* This software 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, or (at
* your option) any later version.
*
* This software 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 GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Heavily based upon ramfs */
#include <linux/config.h>
#include <linux/fs.h>
#include "dm.h"
#include "dmfs.h"
extern struct inode *dmfs_create_error(struct inode *, int);
extern struct inode *dmfs_create_table(struct inode *, int);
extern struct inode *dmfs_create_status(struct inode *, int);
static int dmfs_tdir_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
inode->i_mapping = &inode->i_data;
inode->i_nlink--;
dput(dentry);
return 0;
}
static struct dentry *dmfs_tdir_lookup(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = NULL;
const char *name = dentry->d_name.name;
switch(dentry->d_name.len) {
case 5:
if (memcmp("table", name, 5) == 0) {
inode = dmfs_create_table(dir, 0600);
break;
}
if (memcmp("error", name, 5) == 0)
inode = dmfs_create_error(dir, 0600);
break;
case 6:
if (memcmp("status", name, 6) == 0)
inode = dmfs_create_status(dir, 0600);
break;
}
d_add(dentry, inode);
return NULL;
}
static int dmfs_tdir_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
int i;
struct dentry *dentry = filp->f_dentry;
i = filp->f_pos;
switch(i) {
case 0:
if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0)
break;
i++;
filp->f_pos++;
/* fallthrough */
case 1:
if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
break;
i++;
filp->f_pos++;
/* fallthrough */
case 2:
if (filldir(dirent, "table", 5, i, 2, DT_REG) < 0)
break;
i++;
filp->f_pos++;
/* fallthrough */
case 3:
if (filldir(dirent, "error", 5, i, 3, DT_REG) < 0)
break;
i++;
filp->f_pos++;
/* fallthrough */
case 4:
if (filldir(dirent, "status", 6, i, 4, DT_REG) < 0)
break;
i++;
filp->f_pos++;
}
return 0;
}
static int dmfs_tdir_sync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static struct file_operations dmfs_tdir_file_operations = {
read: generic_read_dir,
readdir: dmfs_tdir_readdir,
fsync: dmfs_tdir_sync,
};
static struct inode_operations dmfs_tdir_inode_operations = {
lookup: dmfs_tdir_lookup,
unlink: dmfs_tdir_unlink,
};
struct inode *dmfs_create_tdir(struct super_block *sb, int mode)
{
struct inode *inode = dmfs_new_private_inode(sb, mode | S_IFDIR);
if (inode) {
inode->i_fop = &dmfs_tdir_file_operations;
inode->i_op = &dmfs_tdir_inode_operations;
}
return inode;
}

View File

@@ -0,0 +1,27 @@
#ifndef LINUX_DMFS_H
#define LINUX_DMFS_H
struct dmfs_i {
struct semaphore sem;
struct dm_table *table;
struct mapped_device *md;
struct dentry *dentry;
struct list_head errors;
int status;
};
#define DMFS_I(inode) ((struct dmfs_i *)(inode)->u.generic_ip)
extern int dmfs_init(void) __init;
extern int dmfs_exit(void) __exit;
struct inode *dmfs_new_inode(struct super_block *sb, int mode);
struct inode *dmfs_new_private_inode(struct super_block *sb, int mode);
void dmfs_add_error(struct inode *inode, unsigned num, char *str);
void dmfs_zap_errors(struct inode *inode);
#endif /* LINUX_DMFS_H */

View File

@@ -0,0 +1,10 @@
--- linux-2.4.14/drivers/md/Config.in Mon Sep 24 16:29:14 2001
+++ linux/drivers/md/Config.in Tue Nov 6 11:25:35 2001
@@ -14,5 +14,7 @@
dep_tristate ' Multipath I/O support' CONFIG_MD_MULTIPATH $CONFIG_BLK_DEV_MD
dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD
+dep_tristate ' Device mapper support' CONFIG_BLK_DEV_DM $CONFIG_MD
+dep_tristate ' Device mapper linear target' CONFIG_BLK_DEV_DM_LINEAR $CONFIG_BLK_DEV_DM
endmenu

View File

@@ -0,0 +1,13 @@
--- uml_build/arch/um/config.in.orig Tue Jan 2 14:33:42 2001
+++ uml_build/arch/um/config.in Tue Jan 2 14:35:42 2001
@@ -15,6 +15,8 @@
bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
endmenu
+source drivers/md/Config.in
+
mainmenu_option next_comment
comment 'Processor features'
bool 'Symmetric multi-processing support' CONFIG_SMP

View File

@@ -0,0 +1,10 @@
--- linux-2.4.14/kernel/ksyms.c Tue Nov 6 11:18:14 2001
+++ linux/kernel/ksyms.c Wed Nov 7 11:26:45 2001
@@ -521,6 +521,7 @@
/* binfmt_aout */
EXPORT_SYMBOL(get_write_access);
+EXPORT_SYMBOL(deny_write_access);
/* time */
EXPORT_SYMBOL(get_fast_time);

View File

@@ -0,0 +1,31 @@
--- linux-2.4.14/drivers/md/Makefile Mon Sep 24 16:29:14 2001
+++ linux/drivers/md/Makefile Wed Nov 7 10:48:26 2001
@@ -4,9 +4,12 @@
O_TARGET := mddev.o
-export-objs := md.o xor.o
+export-objs := md.o xor.o dm-table.o dm-target.o
list-multi := lvm-mod.o
lvm-mod-objs := lvm.o lvm-snap.o
+dm-mod-objs := dm.o dm-table.o dm-target.o dmfs-super.o dmfs-root.o \
+ dmfs-tdir.o dmfs-table.o dmfs-error.o dmfs-status.o \
+ dmfs-lv.o seq_file.o
# Note: link order is important. All raid personalities
# and xor.o must come before md.o, as they each initialise
@@ -20,8 +23,14 @@
obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_BLK_DEV_MD) += md.o
obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o
+obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
+obj-$(CONFIG_BLK_DEV_DM_LINEAR) += dm-linear.o
include $(TOPDIR)/Rules.make
lvm-mod.o: $(lvm-mod-objs)
$(LD) -r -o $@ $(lvm-mod-objs)
+
+dm-mod.o: $(dm-mod-objs)
+ $(LD) -r -o $@ $(dm-mod-objs)
+

View File

@@ -0,0 +1,13 @@
00_latest Latest source - I only tend to update this before
making a release. So if you got this from CVS copy
or link the source files in by hand.
00_config Add device-mapper to the MD section
00_config_uml only apply for uml, turns on the md section
00_makefile Add device-mapper to the MD Makefile.
00_bh-async-3 AA's async bh patch so we can hook b_end_io
to keep track of pending io.

View File

@@ -0,0 +1,6 @@
List of projects, ideas and pending bug fixes
===============================================
o Check tables are an integer multiple of the underlying block size at
table load time.

View File

@@ -0,0 +1,240 @@
/*
* linux/fs/seq_file.c
*
* helper functions for making syntetic files from sequences of records.
* initial implementation -- AV, Oct 2001.
*
* Slight modifications to the original. Will be passed on to Al Viro in
* the next few days.
*/
#include <linux/malloc.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
/**
* seq_open - initialize sequential file
* @file: file we initialize
* @op: method table describing the sequence
*
* seq_open() sets @file, associating it with a sequence described
* by @op. @op->start() sets the iterator up and returns the first
* element of sequence. @op->stop() shuts it down. @op->next()
* returns the next element of sequence. @op->show() prints element
* into the buffer. In case of error ->start() and ->next() return
* ERR_PTR(error). In the end of sequence they return %NULL. ->show()
* returns 0 in case of success and negative number in case of error.
*/
int seq_open(struct file *file, struct seq_operations *op, void *context)
{
struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
memset(p, 0, sizeof(*p));
sema_init(&p->sem, 1);
p->op = op;
p->context = context;
file->private_data = p;
return 0;
}
/**
* seq_read - ->read() method for sequential files.
* @file, @buf, @size, @ppos: see file_operations method
*
* Ready-made ->f_op->read()
*/
ssize_t seq_read(struct file *file, char *buf, size_t size, loff_t *ppos)
{
struct seq_file *m = (struct seq_file *)file->private_data;
size_t copied = 0;
loff_t pos;
size_t n;
void *p;
int err = 0;
if (ppos != &file->f_pos)
return -EPIPE;
down(&m->sem);
/* grab buffer if we didn't have one */
if (!m->buf) {
m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
if (!m->buf)
goto Enomem;
}
/* if not empty - flush it first */
if (m->count) {
n = min(m->count, size);
err = copy_to_user(buf, m->buf + m->from, n);
if (err)
goto Efault;
m->count -= n;
m->from += n;
size -= n;
buf += n;
copied += n;
if (!m->count)
(*ppos)++;
if (!size)
goto Done;
}
/* we need at least one record in buffer */
while (1) {
pos = *ppos;
p = m->op->start(m->context, &pos);
err = PTR_ERR(p);
if (!p || IS_ERR(p))
break;
err = m->op->show(m, p);
if (err)
break;
if (m->count < m->size)
goto Fill;
m->op->stop(m->context, p);
kfree(m->buf);
m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
if (!m->buf)
goto Enomem;
}
m->op->stop(m->context, p);
goto Done;
Fill:
/* they want more? let's try to get some more */
while (m->count < size) {
size_t offs = m->count;
loff_t next = pos;
p = m->op->next(m->context, p, &next);
if (!p || IS_ERR(p)) {
err = PTR_ERR(p);
break;
}
err = m->op->show(m, p);
if (err || m->count == m->size) {
m->count = offs;
break;
}
pos = next;
}
m->op->stop(m->context, p);
n = min(m->count, size);
err = copy_to_user(buf, m->buf, n);
if (err)
goto Efault;
copied += n;
m->count -= n;
if (m->count)
m->from = n;
else
pos++;
*ppos = pos;
Done:
if (!copied)
copied = err;
up(&m->sem);
return copied;
Enomem:
err = -ENOMEM;
goto Done;
Efault:
err = -EFAULT;
goto Done;
}
/**
* seq_lseek - ->llseek() method for sequential files.
* @file, @offset, @origin: see file_operations method
*
* Ready-made ->f_op->llseek()
*/
loff_t seq_lseek(struct file *file, loff_t offset, int origin)
{
struct seq_file *m = (struct seq_file *)file->private_data;
long long retval = -EINVAL;
down(&m->sem);
switch (origin) {
case 1:
offset += file->f_pos;
case 0:
if (offset < 0)
break;
if (offset != file->f_pos) {
file->f_pos = offset;
m->count = 0;
}
retval = offset;
}
up(&m->sem);
return retval;
}
/**
* seq_release - free the structures associated with sequential file.
* @file: file in question
* @inode: file->f_dentry->d_inode
*
* Frees the structures associated with sequential file; can be used
* as ->f_op->release() if you don't have private data to destroy.
*/
int seq_release(struct inode *inode, struct file *file)
{
struct seq_file *m = (struct seq_file *)file->private_data;
kfree(m->buf);
kfree(m);
return 0;
}
/**
* seq_escape - print string into buffer, escaping some characters
* @m: target buffer
* @s: string
* @esc: set of characters that need escaping
*
* Puts string into buffer, replacing each occurence of character from
* @esc with usual octal escape. Returns 0 in case of success, -1 - in
* case of overflow.
*/
int seq_escape(struct seq_file *m, const char *s, const char *esc)
{
char *end = m->buf + m->size;
char *p;
char c;
for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
if (!strchr(esc, c)) {
*p++ = c;
continue;
}
if (p + 3 < end) {
*p++ = '\\';
*p++ = '0' + ((c & 0300) >> 6);
*p++ = '0' + ((c & 070) >> 3);
*p++ = '0' + (c & 07);
continue;
}
m->count = m->size;
return -1;
}
m->count = p - m->buf;
return 0;
}
int seq_printf(struct seq_file *m, const char *f, ...)
{
va_list args;
int len;
if (m->count < m->size) {
va_start(args, f);
len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
va_end(args);
if (m->count + len < m->size) {
m->count += len;
return 0;
}
}
m->count = m->size;
return -1;
}

View File

@@ -0,0 +1,60 @@
/*
* Original file by Al Viro. This is a slight modification which I'll
* pass on to Al Viro shortly.
*/
#ifndef _LINUX_SEQ_FILE_H
#define _LINUX_SEQ_FILE_H
#ifdef __KERNEL__
struct seq_operations;
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
struct semaphore sem;
struct seq_operations *op;
void *context;
};
struct seq_operations {
void * (*start) (void *context, loff_t *pos);
void (*stop) (void *context, void *v);
void * (*next) (void *context, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};
int seq_open(struct file *, struct seq_operations *, void *context);
ssize_t seq_read(struct file *, char *, size_t, loff_t *);
loff_t seq_lseek(struct file *, loff_t, int);
int seq_release(struct inode *, struct file *);
int seq_escape(struct seq_file *, const char *, const char *);
static inline int seq_putc(struct seq_file *m, char c)
{
if (m->count < m->size) {
m->buf[m->count++] = c;
return 0;
}
return -1;
}
static inline int seq_puts(struct seq_file *m, const char *s)
{
int len = strlen(s);
if (m->count + len < m->size) {
memcpy(m->buf + m->count, s, len);
m->count += len;
return 0;
}
m->count = m->size;
return -1;
}
int seq_printf(struct seq_file *, const char *, ...)
__attribute__ ((format (printf,2,3)));
#endif
#endif

View File

@@ -0,0 +1,30 @@
FILES
-----
setup-uml - script to build a user mode linux system, with the lvm driver
symbolically linked back to the LVM dir so I can work from CVS.
uml-lvm.patch - patch to enable lvm in the uml configuration
uml.patch.bz2 - uml patch from
http://sourceforge.net/project/showfiles.php?group_id=429
config-uml - .config which turns on LVM
RUNNING
-------
o checkout an LVM dir for use with uml
make sure you've got a copy of a root filesystem kicking about
o edit the variables at the top of 'setup'
o run setup-uml
o move to your uml dir and run lvm-install as root
o then run the 'up' script to run uml
o if you want to rebuild uml after changing the LVM driver just change into
the linux directory and do a 'make linux ARCH=um'. Don't forget the ARCH=um.

View File

@@ -0,0 +1,299 @@
#
# Automatically generated by make menuconfig: don't edit
#
CONFIG_USERMODE=y
# CONFIG_ISA is not set
# CONFIG_SBUS is not set
# CONFIG_PCI is not set
CONFIG_UID16=y
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
#
# Multi-device support (RAID and LVM)
#
CONFIG_MD=y
# CONFIG_BLK_DEV_MD is not set
# CONFIG_MD_LINEAR is not set
# CONFIG_MD_RAID0 is not set
# CONFIG_MD_RAID1 is not set
# CONFIG_MD_RAID5 is not set
# CONFIG_BLK_DEV_LVM is not set
CONFIG_BLK_DEV_DM=y
#
# General Setup
#
CONFIG_STDIO_CONSOLE=y
CONFIG_NET=y
CONFIG_SYSVIPC=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_SYSCTL=y
CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_MISC=y
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
CONFIG_SSL=y
CONFIG_HOSTFS=m
CONFIG_MCONSOLE=y
CONFIG_MAGIC_SYSRQ=y
#
# Loadable module support
#
CONFIG_MODULES=y
# CONFIG_KMOD is not set
#
# Devices
#
CONFIG_BLK_DEV_UBD=y
# CONFIG_BLK_DEV_UBD_SYNC is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BLK_DEV_INITRD=y
# CONFIG_MMAPPER is not set
#
# Networking options
#
# CONFIG_PACKET is not set
CONFIG_NETLINK=y
# CONFIG_RTNETLINK is not set
# CONFIG_NETLINK_DEV is not set
# CONFIG_NETFILTER is not set
# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_INET_ECN is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_IPV6 is not set
# CONFIG_KHTTPD is not set
# CONFIG_ATM is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_DECNET is not set
# CONFIG_BRIDGE is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_LLC is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
#
# Network drivers
#
#
# ARCnet devices
#
# CONFIG_ARCNET is not set
CONFIG_DUMMY=y
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
CONFIG_TUN=y
CONFIG_ETHERTAP=y
#
# Ethernet (10 or 100Mbit)
#
# CONFIG_NET_ETHERNET is not set
#
# Ethernet (1000 Mbit)
#
# CONFIG_ACENIC is not set
# CONFIG_ACENIC_OMIT_TIGON_I is not set
# CONFIG_DL2K is not set
# CONFIG_MYRI_SBUS is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_SK98LIN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PLIP is not set
CONFIG_PPP=y
# CONFIG_PPP_MULTILINK is not set
# CONFIG_PPP_FILTER is not set
# CONFIG_PPP_ASYNC is not set
# CONFIG_PPP_SYNC_TTY is not set
# CONFIG_PPP_DEFLATE is not set
# CONFIG_PPP_BSDCOMP is not set
# CONFIG_PPPOE is not set
CONFIG_SLIP=y
# CONFIG_SLIP_COMPRESSED is not set
# CONFIG_SLIP_SMART is not set
# CONFIG_SLIP_MODE_SLIP6 is not set
#
# Wireless LAN (non-hamradio)
#
# CONFIG_NET_RADIO is not set
#
# Token Ring devices
#
# CONFIG_TR is not set
# CONFIG_NET_FC is not set
# CONFIG_RCPCI is not set
# CONFIG_SHAPER is not set
#
# Wan interfaces
#
# CONFIG_WAN is not set
#
# Network device support
#
CONFIG_NETDEVICES=y
CONFIG_UML_NET=y
CONFIG_UML_NET_ETHERTAP=y
CONFIG_UML_NET_SLIP=y
CONFIG_UML_NET_DAEMON=y
CONFIG_UML_NET_MCAST=y
CONFIG_ETHERTAP=y
CONFIG_TUN=y
#
# File systems
#
CONFIG_QUOTA=y
CONFIG_AUTOFS_FS=m
CONFIG_AUTOFS4_FS=m
CONFIG_REISERFS_FS=m
# CONFIG_REISERFS_CHECK is not set
# CONFIG_ADFS_FS is not set
# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_BFS_FS is not set
CONFIG_FAT_FS=m
CONFIG_MSDOS_FS=m
CONFIG_UMSDOS_FS=m
CONFIG_VFAT_FS=m
# CONFIG_EFS_FS is not set
# CONFIG_JFFS_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_TMPFS is not set
# CONFIG_RAMFS is not set
CONFIG_ISO9660_FS=m
# CONFIG_JOLIET is not set
CONFIG_MINIX_FS=m
# CONFIG_VXFS_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_NTFS_RW is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
CONFIG_DEVFS_FS=y
CONFIG_DEVFS_MOUNT=y
# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_QNX4FS_RW is not set
# CONFIG_ROMFS_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_SYSV_FS is not set
# CONFIG_UDF_FS is not set
# CONFIG_UDF_RW is not set
# CONFIG_UFS_FS is not set
# CONFIG_UFS_FS_WRITE is not set
#
# Network File Systems
#
# CONFIG_CODA_FS is not set
# CONFIG_NFS_FS is not set
# CONFIG_NFS_V3 is not set
# CONFIG_ROOT_NFS is not set
# CONFIG_NFSD is not set
# CONFIG_NFSD_V3 is not set
# CONFIG_SUNRPC is not set
# CONFIG_LOCKD is not set
# CONFIG_SMB_FS is not set
# CONFIG_NCP_FS is not set
# CONFIG_NCPFS_PACKET_SIGNING is not set
# CONFIG_NCPFS_IOCTL_LOCKING is not set
# CONFIG_NCPFS_STRONG is not set
# CONFIG_NCPFS_NFS_NS is not set
# CONFIG_NCPFS_OS2_NS is not set
# CONFIG_NCPFS_SMALLDOS is not set
# CONFIG_NCPFS_NLS is not set
# CONFIG_NCPFS_EXTRAS is not set
#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
# CONFIG_SMB_NLS is not set
CONFIG_NLS=y
#
# Native Language Support
#
CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
# CONFIG_NLS_ISO8859_1 is not set
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set
#
# Kernel hacking
#
CONFIG_DEBUGSYM=y
CONFIG_PT_PROXY=y
# CONFIG_GPROF is not set
# CONFIG_GCOV is not set

179
driver/user_mode_linux/setup-uml Executable file
View File

@@ -0,0 +1,179 @@
#! /usr/bin/perl -w
use Cwd;
##############################
# Start user configurable bit
##############################
# Please use absolute paths too.
# you can't use ~'s in your paths here.
# the directory where you've checked out lvm, keep a seperate copy for uml,
# the uml kernel will have links to these files
$lvm_src="/home/thornber/sistina/LVM2";
# the debian root image, get it from here:
# http://prdownloads.sourceforge.net/user-mode-linux/root_fs_debian2.2_small.bz2
# unzip it once you've downloaded it
$root_fs="/home/thornber/uml/root_fs_debian2.2_small";
# these are 100 Meg files created with dd
# these become our PV's /dev/ubd/[1-4]
# I sometimes use ubd/1 as swap though.
@block_devices = ("/home/thornber/uml/scratch1",
"/home/thornber/uml/scratch2",
"/home/thornber/uml/scratch3",
"/home/thornber/uml/scratch4");
# directory where uml will be built, and the up, lvm-install scripts will
# be placed
$dest_dir="/home/thornber/builds/uml-lvm2";
# It must be 2.4.8, can be .gz or .bz2
$kernel_tarball="/home/thornber/packages/2.4/linux-2.4.9.tar";
###############################
# end of user configurable bit
###############################
$wd = cwd;
$uml_patch = $wd . "/uml.patch.bz2";
$lvm_uml_patch = $wd . "/uml-lvm.patch";
$driver = $lvm_src . "/driver/device-mapper";
# check we've got everything we need
&check_file($root_fs);
&check_dir($lvm_src);
&check_file($kernel_tarball);
&check_dir($dest_dir);
&check_file($uml_patch);
&check_file($lvm_uml_patch);
chdir($dest_dir);
&extract_kernel($dest_dir, $kernel_tarball);
chdir("linux");
&run_command("bzip2 -dc $uml_patch | patch -p1", "patching kernel with uml");
&run_command("patch -p1 < $lvm_uml_patch", "enabling LVM driver");
chdir("$dest_dir/linux");
&run_command("cd include/linux; ln -s $driver/device-mapper.h",
"linking device-mapper.h");
&run_command("cd drivers/md; ln -s $driver/dm.h", "linking dm.h");
&run_command("cd drivers/md; ln -s $driver/dm-fs.c", "linking dm-fs.c");
&run_command("cd drivers/md; ln -s $driver/dm-table.c", "linking dm-table.c");
&run_command("cd drivers/md; ln -s $driver/dm-target.c",
"linking dm-target.c");
&run_command("cd drivers/md; ln -s $driver/dm.c", "linking dm.c");
chdir("$dest_dir/linux");
&run_command("make oldconfig ARCH=um", "making oldconfig ARCH=um");
&run_command("make dep ARCH=um", "making dependencies");
&run_command("make linux ARCH=um", "building linux uml");
chdir($dest_dir);
&run_command("ln -s $dest_dir/linux/linux uml", "creating link for linux");
chdir($dest_dir);
&run_command("ln -s $root_fs ./root_fs", "linking root filesystem");
chdir($dest_dir);
&link_devices();
&write_up();
&run_command("mkdir root_fs_mnt");
&write_lvm_install();
print "Dont forget to run $dest_dir/lvm-install as root\n";
sub write_lvm_install {
open(OUT, "> lvm-install");
print OUT "#! /bin/sh\n\n";
print OUT <<"end";
mount root_fs root_fs_mnt -o loop
cd $lvm_src; make install; cd $dest_dir
umount root_fs_mnt
end
close OUT;
system "chmod +x lvm-install";
}
sub write_up {
open(UP, "> up");
print UP "#! /bin/sh\n\n./uml ";
$count = 1;
for $d (@block_devices) {
print UP "ubd$count=ubd$count ";
$count++;
}
print UP "\n";
close UP;
system("chmod +x up");
}
sub link_devices {
$count = 1;
foreach $d (@block_devices) {
&run_command("ln -s $d ubd$count",
"linking block device ubd$count");
$count++;
}
}
sub extract_kernel {
my($dest, $tb) = @_;
my($cmd);
if($tb =~ m/\.bz2/) {
$cmd = "tar Ixf $tb";
} elsif($tb =~ m/\.gz/) {
$cmd = "tar zxf $tb";
} else {
$cmd = "tar xf $tb";
}
&run_command($cmd, "extracting kernel");
}
sub run_command {
my($cmd) = shift;
my($desc) = shift;
my($r);
print STDERR $desc, " ... ";
$r = system("$cmd > /dev/null");
if(!$r) {
print STDERR "done.\n";
return;
} else {
print STDERR "failed.\n";
exit(1);
}
}
sub check_file {
$f = shift;
if(! -e $f) {
print STDERR "couldn't find $f\n";
exit;
}
}
sub check_dir {
$f = shift;
if(! -e $f || ! -d $f) {
print STDERR "couldn't find a directory called $f\n";
exit;
}
}

View File

@@ -0,0 +1,30 @@
--- uml_build/arch/um/config.in.orig Tue Jan 2 14:33:42 2001
+++ uml_build/arch/um/config.in Tue Jan 2 14:35:42 2001
@@ -15,6 +15,8 @@
bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
endmenu
+source drivers/md/Config.in
+
mainmenu_option next_comment
comment 'Processor features'
bool 'Symmetric multi-processing support' CONFIG_SMP
--- linux/drivers/md/Config.in.orig Tue Aug 21 14:18:30 2001
+++ linux/drivers/md/Config.in Tue Aug 21 14:19:08 2001
@@ -14,4 +14,6 @@
dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD
+dep_tristate ' Device mapper support' CONFIG_BLK_DEV_DM $CONFIG_MD
+
endmenu
--- linux/drivers/md/Makefile.orig Tue Aug 21 14:19:14 2001
+++ linux/drivers/md/Makefile Tue Aug 21 14:20:06 2001
@@ -19,6 +19,7 @@
obj-$(CONFIG_MD_RAID5) += raid5.o xor.o
obj-$(CONFIG_BLK_DEV_MD) += md.o
obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o
+obj-$(CONFIG_BLK_DEV_DM) += dm.o dm-table.o dm-target.o dm-fs.o dm-parse.o
include $(TOPDIR)/Rules.make

Binary file not shown.

View File

@@ -1,8 +1,5 @@
../lib/activate/activate.h
../lib/commands/errors.h
../lib/commands/toolcontext.h
../lib/config/config.h
../lib/config/defaults.h
../lib/datastruct/bitset.h
../lib/datastruct/btree.h
../lib/datastruct/hash.h
@@ -16,18 +13,11 @@
../lib/filters/filter-regex.h
../lib/filters/filter.h
../lib/format1/format1.h
../lib/format1/lvm1_label.h
../lib/format_text/format-text.h
../lib/label/label.h
../lib/label/uuid-map.h
../lib/locking/locking.h
../lib/log/log.h
../lib/metadata/metadata.h
../lib/mm/dbg_malloc.h
../lib/mm/pool.h
../lib/mm/xlate.h
../lib/misc/lvm-file.h
../lib/misc/lvm-string.h
../lib/regex/matcher.h
../lib/uuid/uuid.h
../lib/vgcache/vgcache.h
../lib/cmgr/cmgr.h

View File

@@ -31,13 +31,11 @@ all: .symlinks_created
for i in `cat .symlinks`; do $(LN_S) $$i ; done
touch $@
clean:
distclean:
find . -maxdepth 1 -type l -exec $(RM) \{\} \;
$(RM) Makefile .include_symlinks .symlinks_created
clean:
install:
.PHONY: clean distclean all install
.PHONY: clean distclean all

View File

@@ -10,7 +10,6 @@ VPATH = @srcdir@
SOURCES=\
activate/activate.c \
activate/dev_manager.c \
activate/fs.c \
config/config.c \
datastruct/bitset.c \
@@ -27,37 +26,21 @@ SOURCES=\
format1/disk-rep.c \
format1/format1.c \
format1/import-export.c \
format1/import-extents.c \
format1/layout.c \
format1/lvm1_label.c \
format1/vg_number.c \
format_text/archive.c \
format_text/export.c \
format_text/flags.c \
format_text/format-text.c \
format_text/import.c \
label/label.c \
label/uuid-map.c \
locking/external_locking.c \
locking/file_locking.c \
locking/locking.c \
locking/no_locking.c \
log/log.c \
metadata/lv_manip.c \
metadata/merge.c \
metadata/metadata.c \
metadata/pv_map.c \
metadata/snapshot_manip.c \
misc/lvm-file.c \
mm/dbg_malloc.c \
mm/pool.c \
regex/matcher.c \
regex/parse_rx.c \
regex/ttree.c \
uuid/uuid.c \
vgcache/vgcache.c
cmgr/cmgr.c \
uuid/uuid.c
TARGETS=liblvm.a
TARGETS=liblvm.a $(SLIB_SOV) $(SLIB_SOM) $(SLIB_SO)
include ../make.tmpl
@@ -66,3 +49,12 @@ liblvm.a: $(OBJECTS)
$(AR) r $@ $(OBJECTS)
$(RANLIB) $@
$(SLIB_SOV): $(POBJECTS)
$(CC) -shared -Wl,-soname,$(SLIB_SOM) -o $(SLIB_SOV) $(POBJECTS)
$(SLIB_SOM): $(SLIB_SOV)
ln -sf $(SLIB_SOV) $(SLIB_SOM)
$(SLIB_SO): $(SLIB_SOV)
ln -sf $(SLIB_SOV) $(SLIB_SO)

1
lib/README Normal file
View File

@@ -0,0 +1 @@
Base library directory

View File

@@ -1,101 +0,0 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "ll-activate.h"
#include "lvm-string.h"
#include "log.h"
#include <libdevmapper.h>
/*
* Emit a target for a given segment.
* FIXME: tidy this function.
*/
static int _emit_target(struct dm_task *dmt, struct stripe_segment *seg)
{
char params[1024];
uint64_t esize = seg->lv->vg->extent_size;
uint32_t s, stripes = seg->stripes;
int w = 0, tw = 0, error = 0;
const char *no_space =
"Insufficient space to write target parameters.";
char *filler = "/dev/ioerror";
char *target;
if (stripes == 1) {
if (!seg->area[0].pv) {
target = "error";
error = 1;
}
else
target = "linear";
}
if (stripes > 1) {
target = "striped";
tw = lvm_snprintf(params, sizeof(params), "%u %u ",
stripes, seg->stripe_size);
if (tw < 0) {
log_err(no_space);
return 0;
}
w = tw;
}
if (!error) {
for (s = 0; s < stripes; s++, w += tw) {
if (!seg->area[s].pv)
tw = lvm_snprintf(
params + w, sizeof(params) - w,
"%s 0%s", filler,
s == (stripes - 1) ? "" : " ");
else
tw = lvm_snprintf(
params + w, sizeof(params) - w,
"%s %" PRIu64 "%s",
dev_name(seg->area[s].pv->dev),
(seg->area[s].pv->pe_start +
(esize * seg->area[s].pe)),
s == (stripes - 1) ? "" : " ");
if (tw < 0) {
log_err(no_space);
return 0;
}
}
}
log_very_verbose("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
esize * seg->le, esize * seg->len,
target, params);
if (!dm_task_add_target(dmt, esize * seg->le, esize * seg->len,
target, params)) {
stack;
return 0;
}
return 1;
}
int device_populate_lv(struct dm_task *dmt, struct logical_volume *lv)
{
struct list *segh;
struct stripe_segment *seg;
log_very_verbose("Generating devmapper table for %s", lv->name);
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
if (!_emit_target(dmt, seg)) {
log_error("Unable to build table for '%s'", lv->name);
return 0;
}
}
return 1;
}

View File

@@ -9,152 +9,274 @@
#include "display.h"
#include "log.h"
#include "fs.h"
#include "lvm-string.h"
#include "pool.h"
#include "toolcontext.h"
#include "dev_manager.h"
/* FIXME Temporary */
#include "vgcache.h"
#include <limits.h>
#include <linux/kdev_t.h>
#include <fcntl.h>
#define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args)
int library_version(char *version, size_t size)
static void _build_lv_name(char *buffer, size_t s, struct logical_volume *lv)
{
if (!dm_get_library_version(version, size))
return 0;
return 1;
snprintf(buffer, s, "%s_%s", lv->vg->name, lv->name);
}
int driver_version(char *version, size_t size)
static struct dm_task *_setup_task(struct logical_volume *lv, int task)
{
char name[128];
struct dm_task *dmt;
if (!(dmt = dm_task_create(task))) {
stack;
return NULL;
}
_build_lv_name(name, sizeof(name), lv);
dm_task_set_name(dmt, name);
return dmt;
}
int lv_info(struct logical_volume *lv, struct dm_info *info)
{
int r = 0;
struct dm_task *dmt;
log_very_verbose("Getting driver version");
if (!(dmt = dm_task_create(DM_DEVICE_VERSION))) {
if (!(dmt = _setup_task(lv, DM_DEVICE_INFO))) {
stack;
return 0;
}
if (!dm_task_run(dmt))
log_error("Failed to get driver version");
if (!dm_task_get_driver_version(dmt, version, size))
if (!dm_task_run(dmt)) {
stack;
goto out;
}
if (!dm_task_get_info(dmt, info)) {
stack;
goto out;
}
r = 1;
out:
out:
dm_task_destroy(dmt);
return r;
}
/*
* Returns 1 if info structure populated, else 0 on failure.
*/
int lv_info(struct logical_volume *lv, struct dm_info *info)
{
int r;
struct dev_manager *dm;
if (!(dm = dev_manager_create(lv->vg->name))) {
stack;
return 0;
}
if (!(r = dev_manager_info(dm, lv, info)))
stack;
dev_manager_destroy(dm);
return r;
}
static int _lv_active(struct logical_volume *lv)
int lv_active(struct logical_volume *lv)
{
int r = -1;
struct dm_info info;
if (!lv_info(lv, &info)) {
stack;
return -1;
return r;
}
return info.exists;
}
static int _lv_open_count(struct logical_volume *lv)
int lv_open_count(struct logical_volume *lv)
{
int r = -1;
struct dm_info info;
if (!lv_info(lv, &info)) {
stack;
return -1;
return r;
}
return info.open_count;
}
/* FIXME Need to detect and handle an lv rename */
static int _lv_activate(struct logical_volume *lv)
{
int r;
struct dev_manager *dm;
if (!(dm = dev_manager_create(lv->vg->name))) {
stack;
return 0;
}
if (!(r = dev_manager_activate(dm, lv)))
stack;
dev_manager_destroy(dm);
return r;
}
static int _lv_deactivate(struct logical_volume *lv)
{
int r;
struct dev_manager *dm;
if (!(dm = dev_manager_create(lv->vg->name))) {
stack;
return 0;
}
if (!(r = dev_manager_deactivate(dm, lv)))
stack;
dev_manager_destroy(dm);
return r;
}
static int _lv_suspend(struct logical_volume *lv)
{
int r;
struct dev_manager *dm;
if (!(dm = dev_manager_create(lv->vg->name))) {
stack;
return 0;
}
if (!(r = dev_manager_suspend(dm, lv)))
stack;
dev_manager_destroy(dm);
return r;
}
/*
* These two functions return the number of LVs in the state,
* or -1 on error.
* Creates a target for the next contiguous run of
* extents.
*/
static int _emit_target(struct dm_task *dmt, struct logical_volume *lv,
unsigned int *ple)
{
char params[1024];
unsigned int le = *ple;
uint64_t esize = lv->vg->extent_size;
int i, count = 0;
struct pe_specifier *pes, *first = NULL;
for (i = le; i < lv->le_count; i++) {
pes = lv->map + i;
if (!first)
first = pes;
/*
* check that we're still contiguous.
*/
else if ((pes->pv != first->pv) ||
(pes->pe != first->pe + count))
break;
count++;
}
snprintf(params, sizeof(params), "%s %" PRIu64,
dev_name(first->pv->dev),
first->pv->pe_start + (esize * first->pe));
if (!dm_task_add_target(dmt, esize * le, esize * count,
"linear", params)) {
stack;
return 0;
}
*ple = i;
return 1;
}
int _load(struct logical_volume *lv, int task)
{
int r = 0;
uint32_t le = 0;
struct dm_task *dmt;
if (!(dmt = _setup_task(lv, task))) {
stack;
return 0;
}
/*
* Merge adjacent extents.
*/
while (le < lv->le_count) {
if (!_emit_target(dmt, lv, &le)) {
log_error("Unable to activate logical volume '%s'",
lv->name);
goto out;
}
}
if (!(r = dm_task_run(dmt)))
stack;
log_verbose("Logical volume %s activated", lv->name);
out:
dm_task_destroy(dmt);
return r;
}
/* FIXME: Always display error msg */
int lv_activate(struct logical_volume *lv)
{
return _load(lv, DM_DEVICE_CREATE) && fs_add_lv(lv);
}
int _suspend(struct logical_volume *lv, int sus)
{
int r;
struct dm_task *dmt;
int task = sus ? DM_DEVICE_SUSPEND : DM_DEVICE_RESUME;
if (!(dmt = _setup_task(lv, task))) {
stack;
return 0;
}
if (!(r = dm_task_run(dmt)))
log_err("Couldn't %s device '%s'", sus ? "suspend" : "resume",
lv->name);
dm_task_destroy(dmt);
return r;
}
int lv_reactivate(struct logical_volume *lv)
{
int r;
if (!_suspend(lv, 1)) {
stack;
return 0;
}
r = _load(lv, DM_DEVICE_RELOAD);
if (!_suspend(lv, 0)) {
stack;
return 0;
}
return r;
}
int lv_deactivate(struct logical_volume *lv)
{
int r;
struct dm_task *dmt;
if (!(dmt = _setup_task(lv, DM_DEVICE_REMOVE))) {
stack;
return 0;
}
if (!(r = dm_task_run(dmt)))
stack;
dm_task_destroy(dmt);
fs_del_lv(lv);
return r;
}
int lv_suspend(struct logical_volume *lv, int sus)
{
return _suspend(lv, sus);
}
int suspend_lvs_in_vg(struct volume_group *vg, int sus)
{
struct list *lvh;
struct logical_volume *lv;
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = &(list_item(lvh, struct lv_list)->lv);
count += (_suspend(lv, sus));
}
return count;
}
int activate_lvs_in_vg(struct volume_group *vg)
{
struct list *lvh;
struct logical_volume *lv;
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = &(list_item(lvh, struct lv_list)->lv);
count += (!lv_active(lv) && lv_activate(lv));
}
return count;
}
int lv_update_write_access(struct logical_volume *lv)
{
return 0;
}
int deactivate_lvs_in_vg(struct volume_group *vg)
{
struct list *lvh;
struct logical_volume *lv;
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = &(list_item(lvh, struct lv_list)->lv);
count += ((lv_active(lv) == 1) && lv_deactivate(lv));
}
return count;
}
int lvs_in_vg_activated(struct volume_group *vg)
{
struct list *lvh;
@@ -162,8 +284,9 @@ int lvs_in_vg_activated(struct volume_group *vg)
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
count += (_lv_active(lv) == 1);
lv = &(list_item(lvh, struct lv_list)->lv);
count += (lv_active(lv) == 1);
}
return count;
@@ -176,141 +299,11 @@ int lvs_in_vg_opened(struct volume_group *vg)
int count = 0;
list_iterate(lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
count += (_lv_open_count(lv) == 1);
lv = &(list_item(lvh, struct lv_list)->lv);
count += (lv_open_count(lv) == 1);
}
return count;
}
static struct logical_volume *_lv_from_lvid(struct cmd_context *cmd,
const char *lvid_s)
{
struct lv_list *lvl;
struct volume_group *vg;
union lvid *lvid;
char *vgname;
lvid = (union lvid *) lvid_s;
/* FIXME Change vgread to accept vgid directly - can't rely on cache */
if (!(vgname = vgname_from_vgid(cmd, &lvid->id[0]))) {
log_error("Volume group for uuid not found: %s", lvid_s);
return NULL;
}
log_verbose("Finding volume group \"%s\"", vgname);
if (!(vg = cmd->fid->ops->vg_read(cmd->fid, vgname))) {
log_error("Volume group \"%s\" doesn't exist", vgname);
return NULL;
}
if (vg->status & EXPORTED_VG) {
log_error("Volume group \"%s\" is exported", vgname);
return NULL;
}
if (!(lvl = find_lv_in_vg_by_lvid(vg, lvid))) {
log_very_verbose("Can't find logical volume id %s", lvid_s);
return NULL;
}
return lvl->lv;
}
/* These return success if the device is not active */
int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s)
{
struct logical_volume *lv;
struct dm_info info;
if (!(lv = _lv_from_lvid(cmd, lvid_s)))
return 0;
if (test_mode()) {
_skip("Suspending '%s'.", lv->name);
return 0;
}
if (!lv_info(lv, &info)) {
stack;
return 0;
}
if (info.exists && !info.suspended)
return _lv_suspend(lv);
return 1;
}
int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s)
{
struct logical_volume *lv;
struct dm_info info;
if (!(lv = _lv_from_lvid(cmd, lvid_s)))
return 0;
if (test_mode()) {
_skip("Resuming '%s'.", lv->name);
return 0;
}
if (!lv_info(lv, &info)) {
stack;
return 0;
}
if (info.exists && info.suspended)
return _lv_activate(lv);
return 1;
}
int lv_deactivate(struct cmd_context *cmd, const char *lvid_s)
{
struct logical_volume *lv;
struct dm_info info;
if (!(lv = _lv_from_lvid(cmd, lvid_s)))
return 0;
if (test_mode()) {
_skip("Deactivating '%s'.", lv->name);
return 0;
}
if (!lv_info(lv, &info)) {
stack;
return 0;
}
if (info.exists)
return _lv_deactivate(lv);
return 1;
}
int lv_activate(struct cmd_context *cmd, const char *lvid_s)
{
struct logical_volume *lv;
struct dm_info info;
if (!(lv = _lv_from_lvid(cmd, lvid_s)))
return 0;
if (test_mode()) {
_skip("Activating '%s'.", lv->name);
return 0;
}
if (!lv_info(lv, &info)) {
stack;
return 0;
}
if (!info.exists || info.suspended)
return _lv_activate(lv);
return 1;
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
* This file is released under the GPL.
*/
#ifndef LVM_ACTIVATE_H
@@ -9,34 +9,45 @@
#include <libdevmapper.h>
int driver_version(char *version, size_t size);
int library_version(char *version, size_t size);
/* FIXME Snapshot handling? */
/*
* Returns 1 if info structure has been populated, else 0.
*/
int lv_active(struct logical_volume *lv);
int lv_open_count(struct logical_volume *lv);
int lv_info(struct logical_volume *lv, struct dm_info *info);
/*
* These should eventually use config file
* to determine whether or not to activate
*/
int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s);
int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s);
int lv_activate(struct cmd_context *cmd, const char *lvid_s);
int lv_deactivate(struct cmd_context *cmd, const char *lvid_s);
int lv_activate(struct logical_volume *lv);
int lv_reactivate(struct logical_volume *lv);
int lv_deactivate(struct logical_volume *lv);
int lv_suspend(struct logical_volume *lv, int sus);
/*
* FIXME:
* I don't like the *lvs_in_vg* function names.
*/
/*
* Return number of LVs in the VG that are active.
* Return number of LVs in the VG that are
* active.
*/
int lvs_in_vg_activated(struct volume_group *vg);
int lvs_in_vg_opened(struct volume_group *vg);
int lv_setup_cow_store(struct logical_volume *lv);
/*
* Test for (lv->status & LVM_WRITE)
*/
int lv_update_write_access(struct logical_volume *lv);
/*
* Activate all LVs in the VG. Ignore any that
* are already active. Return number
* activated.
*/
int activate_lvs_in_vg(struct volume_group *vg);
/*
* Deactivate all LVs in the VG
*/
int deactivate_lvs_in_vg(struct volume_group *vg);
/*
*
* Suspend/resume
*/
int suspend_lvs_in_vg(struct volume_group *vg, int sus);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_DEV_MANAGER_H
#define _LVM_DEV_MANAGER_H
#include "metadata.h"
#include <libdevmapper.h>
struct dev_manager;
/*
* Constructor and destructor.
*/
struct dev_manager *dev_manager_create(const char *vg_name);
void dev_manager_destroy(struct dev_manager *dm);
/*
* The device handler is responsible for creating all the layered
* dm devices, and ensuring that all constraints are maintained
* (eg, an origin is created before its snapshot, but is not
* unsuspended until the snapshot is also created.)
*/
int dev_manager_info(struct dev_manager *dm, struct logical_volume *lv,
struct dm_info *info);
int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv);
int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv);
int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv);
/*
* Put the desired changes into effect.
*/
int dev_manager_execute(struct dev_manager *dm);
#endif

View File

@@ -4,12 +4,6 @@
* This file is released under the LGPL.
*/
#include "fs.h"
#include "log.h"
#include "toolcontext.h"
#include "lvm-string.h"
#include "lvm-file.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -18,107 +12,100 @@
#include <stdio.h>
#include <string.h>
#include <libdevmapper.h>
#include "fs.h"
#include "log.h"
static int _mk_dir(struct volume_group *vg)
#include <devmapper/libdevmapper.h>
/*
* FIXME: copied straight from LVM1.
*
* This should run through /proc/mounts once only,
* storing devfs mount points in a hash table.
*/
static int _check_devfs(const char *dev_dir)
{
char vg_path[PATH_MAX];
int r = 0, len;
char dir[PATH_MAX], line[512];
char type[32];
FILE *mounts = NULL;
if (lvm_snprintf(vg_path, sizeof(vg_path), "%s%s",
vg->cmd->dev_dir, vg->name) == -1) {
log_error("Couldn't construct name of volume "
"group directory.");
return 0;
if (!(mounts = fopen("/proc/mounts", "r")))
goto out;
/* trim the trailing slash off dev_dir, yuck */
len = strlen(dev_dir) - 1;
while(len && dev_dir[len] == '/')
len--;
while (!feof(mounts)) {
fgets(line, sizeof(line) - 1, mounts);
if (sscanf(line, "%*s %s %s %*s", dir, type) != 2)
continue;
if (!strcmp(type, "devfs") && !strncmp(dir, dev_dir, len)) {
r = 1;
break;
}
}
fclose(mounts);
if (dir_exists(vg_path))
out:
return r;
}
void _build_lv_path(char *buffer, size_t len, struct logical_volume *lv)
{
snprintf(buffer, len, "%s%s/%s_%s",
lv->vg->cmd->dev_dir, dm_dir(), lv->vg->name, lv->name);
}
void _build_vg_path(char *buffer, size_t len, struct volume_group *vg)
{
snprintf(buffer, len, "%s/%s", vg->cmd->dev_dir, vg->name);
}
void _build_link_path(char *buffer, size_t len, struct logical_volume *lv)
{
snprintf(buffer, len, "%s/%s/%s", lv->vg->cmd->dev_dir,
lv->vg->name, lv->name);
}
static int _mk_node(struct logical_volume *lv)
{
char lv_path[PATH_MAX];
char dm_path[PATH_MAX];
dev_t dev;
const char *dev_dir = lv->vg->cmd->dev_dir;
if (_check_devfs(dev_dir))
return 1;
log_very_verbose("Creating directory %s", vg_path);
if (mkdir(vg_path, 0555)) {
log_sys_error("mkdir", vg_path);
snprintf(dm_path, PATH_MAX, "%s%s", dev_dir, dm_dir());
if (mkdir(dm_path, 0555) && errno != EEXIST) {
log_sys_error("mkdir", dm_path);
return 0;
}
_build_lv_path(lv_path, sizeof(lv_path), lv);
if (mknod(lv_path, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, dev) < 0) {
log_sys_error("mknod", lv_path);
return 0;
}
return 1;
}
static int _rm_dir(struct volume_group *vg)
static int _rm_node(struct logical_volume *lv)
{
char vg_path[PATH_MAX];
if (lvm_snprintf(vg_path, sizeof(vg_path), "%s%s",
vg->cmd->dev_dir, vg->name) == -1) {
log_error("Couldn't construct name of volume "
"group directory.");
return 0;
}
log_very_verbose("Removing directory %s", vg_path);
if (is_empty_dir(vg_path))
rmdir(vg_path);
return 1;
}
static int _mk_link(struct logical_volume *lv, const char *dev)
{
char lv_path[PATH_MAX], link_path[PATH_MAX];
struct stat buf;
if (lvm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s",
lv->vg->cmd->dev_dir, lv->vg->name, lv->name) == -1) {
log_error("Couldn't create source pathname for "
"logical volume link %s", lv->name);
return 0;
}
if (lvm_snprintf(link_path, sizeof(link_path), "%s/%s",
dm_dir(), dev) == -1) {
log_error("Couldn't create destination pathname for "
"logical volume link for %s", lv->name);
return 0;
}
if (!lstat(lv_path, &buf)) {
if (!S_ISLNK(buf.st_mode)) {
log_error("Symbolic link %s not created: file exists",
link_path);
return 0;
}
if (unlink(lv_path) < 0) {
log_sys_error("unlink", lv_path);
return 0;
}
}
log_very_verbose("Linking %s -> %s", lv_path, link_path);
if (symlink(link_path, lv_path) < 0) {
log_sys_error("symlink", lv_path);
return 0;
}
return 1;
}
static int _rm_link(struct logical_volume *lv, const char *lv_name)
{
struct stat buf;
char lv_path[PATH_MAX];
const char *dev_dir = lv->vg->cmd->dev_dir;
if (lvm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s",
lv->vg->cmd->dev_dir, lv->vg->name, lv_name) == -1) {
log_error("Couldn't determine link pathname.");
return 0;
}
if (_check_devfs(dev_dir))
return 1;
log_very_verbose("Removing link %s", lv_path);
if (lstat(lv_path, &buf) || !S_ISLNK(buf.st_mode)) {
log_error("%s not symbolic link - not removing", lv_path);
return 0;
}
_build_lv_path(lv_path, sizeof(lv_path), lv);
if (unlink(lv_path) < 0) {
log_sys_error("unlink", lv_path);
@@ -128,9 +115,71 @@ static int _rm_link(struct logical_volume *lv, const char *lv_name)
return 1;
}
int fs_add_lv(struct logical_volume *lv, const char *dev)
/*
* Lazy programmer: I'm just going to always try
* and create/remove the vg directory, and not say
* anything if it fails.
*/
static int _mk_dir(struct volume_group *vg)
{
if (!_mk_dir(lv->vg) || !_mk_link(lv, dev)) {
char vg_path[PATH_MAX];
_build_vg_path(vg_path, sizeof(vg_path), vg);
mkdir(vg_path, 0555);
return 1;
}
static int _rm_dir(struct volume_group *vg)
{
char vg_path[PATH_MAX];
_build_vg_path(vg_path, sizeof(vg_path), vg);
rmdir(vg_path);
return 1;
}
static int _mk_link(struct logical_volume *lv)
{
char lv_path[PATH_MAX], link_path[PATH_MAX];
_build_lv_path(lv_path, sizeof(lv_path), lv);
_build_link_path(link_path, sizeof(link_path), lv);
if (symlink(lv_path, link_path) < 0) {
log_sys_error("symlink", link_path);
return 0;
}
return 1;
}
static int _rm_link(struct logical_volume *lv)
{
char link_path[PATH_MAX];
_build_link_path(link_path, sizeof(link_path), lv);
if (unlink(link_path) < 0) {
log_sys_error("unlink", link_path);
return 0;
}
return 1;
}
int fs_add_lv(struct logical_volume *lv)
{
if (!_mk_node(lv)) {
stack;
return 0;
}
if (!_mk_dir(lv->vg)) {
stack;
return 0;
}
if (!_mk_link(lv)) {
stack;
return 0;
}
@@ -140,7 +189,17 @@ int fs_add_lv(struct logical_volume *lv, const char *dev)
int fs_del_lv(struct logical_volume *lv)
{
if (!_rm_link(lv, lv->name) || !_rm_dir(lv->vg)) {
if (!_rm_link(lv)) {
stack;
return 0;
}
if (!_rm_dir(lv->vg)) {
stack;
return 0;
}
if (!_rm_node(lv)) {
stack;
return 0;
}
@@ -148,15 +207,3 @@ int fs_del_lv(struct logical_volume *lv)
return 1;
}
/* FIXME Use rename() */
int fs_rename_lv(struct logical_volume *lv,
const char *dev, const char *old_name)
{
if (old_name && !_rm_link(lv, old_name))
stack;
if (!_mk_link(lv, dev))
stack;
return 1;
}

View File

@@ -9,15 +9,7 @@
#include "metadata.h"
/*
* These calls, private to the activate unit, set
* up the volume group directory in /dev and the
* symbolic links to the dm device.
*/
int fs_add_lv(struct logical_volume *lv, const char *dev);
int fs_add_lv(struct logical_volume *lv);
int fs_del_lv(struct logical_volume *lv);
int fs_rename_lv(struct logical_volume *lv,
const char *dev, const char *old_name);
#endif

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "table-build.c"
static void _print_run(FILE *fp, struct logical_volume *lv)
{
}
int build_table(struct volume_group *vg, struct logical_volume *lv,
const char *file)
{
int i;
uint64_t sector = 0;
uint64_t pe_size = vg->extent_size;
uint64_t dest;
struct pe_specifier *pes;
FILE *fp = fopen(file, "w");
if (!fp) {
log_err("couldn't open '%s' to write table", file);
return 0;
}
for (i = 0; i < lv->le_count; i++) {
pes = lv->map + i;
dest = pes->pv->pe_start + (pe_size * pes->pe);
fprintf(fp, "%ull %ull linear %s %ull\n",
sector, pe_size, pes->pv->dev->name, dest);
sector += pe_size;
}
fclose(fp);
return 1;
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef TABLE_BUILD_H
#define TABLE_BUILD_H
int build_table(struct volume_group *vg, struct logical_volume *lv,
const char *file);
#endif

46
lib/cmgr/clvm.h Normal file
View File

@@ -0,0 +1,46 @@
/* Definitions for CLVMD server and clients */
/* The protocol spoken over the cluster and across the local
socket
*/
struct clvm_header
{
unsigned char cmd; /* See below */
unsigned char flags; /* See below */
unsigned short pad; /* To keep alignment sane */
unsigned int clientid; /* Only used in Daemon->Daemon comms */
int status; /* For replies, whether the request suceeded or not */
unsigned int arglen; /* Length of argument below. if >1500 then it will be passed around the
cluster in the system LV */
char node[1]; /* Actually a NUL-terminated string, node name, if this is empty then
the command is forwarded to all cluster nodes unless FLAG_LOCAL is
also set. */
char args[1]; /* Arguments for the command follow the node name, This member is only
valid if the node name is empty */
} __attribute__((packed));
/* Flags */
#define CLVMD_FLAG_LOCAL 1 /* Only do this on the local node */
#define CLVMD_FLAG_SYSTEMLV 2 /* Data is in system LV under my node name */
/* Name of the local socket to communicate between libclvm and clvmd */
#define CLVMD_SOCKNAME "/var/run/clvmd"
/* Command numbers */
#define CLVMD_CMD_TEST 4
/* Lock/Unlock commands */
#define CLVMD_CMD_LOCK 30
#define CLVMD_CMD_UNLOCK 31
#define CLVMD_CMD_LOCK_SUSPEND 32
#define CLVMD_CMD_UNLOCK_RESUME 33
/* Info Commands */
#define CLVMD_CMD_LVDISPLAY 40
#define CLVMD_CMD_LVCHECK 41
#define CLVMD_CMD_VGDISPLAY 42
#define CLVMD_CMD_VGCHECK 43

874
lib/cmgr/cmgr.c Normal file
View File

@@ -0,0 +1,874 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
/* Locking functions for LVM
* The main purpose of this part of the library is to serialise LVM
* management operations (across a cluster if necessary)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <syslog.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <search.h>
#include <errno.h>
#include "metadata.h"
#include "activate.h"
#include "log.h"
#include "clvm.h"
#include "cmgr.h"
/* This gets stuck at the start of memory we allocate so we
can sanity-check it at deallocation time */
#define LVM_SIGNATURE 0x434C564D
#define MAX_CLUSTER_MEMBER_NAME_LEN 255
#define LVM_GLOBAL_LOCK "LVM_GLOBAL"
#define LOCKFILE_DIR "/var/lock/lvm"
/* NOTE: the LVMD uses the socket FD as the client ID, this means
that any client that calls fork() will inherit the context of
it's parent. */
static int clvmd_sock = -1;
/* Open connection to the Cluster Manager daemon */
static int open_local_sock(void)
{
int local_socket;
struct sockaddr_un sockaddr;
/* Open local socket */
local_socket = socket(PF_UNIX, SOCK_STREAM, 0);
if (local_socket < 0)
{
perror("Can't create local socket");
return -1;
}
fcntl(local_socket, F_SETFD, !FD_CLOEXEC);
strcpy(sockaddr.sun_path, CLVMD_SOCKNAME);
sockaddr.sun_family = AF_UNIX;
if (connect(local_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)))
{
int saved_errno = errno;
close(local_socket);
errno = saved_errno;
return -1;
}
return local_socket;
}
/* Send a request and return the status */
static int send_request(char *inbuf, int inlen, char **retbuf)
{
char outbuf[PIPE_BUF];
struct clvm_header *outheader = (struct clvm_header *)outbuf;
int len;
int off;
fd_set fds;
FD_ZERO(&fds);
FD_SET(clvmd_sock, &fds);
/* Send it to CLVMD */
if (write(clvmd_sock, inbuf, inlen) != inlen)
{
perror("Error writing to CLVMD");
return -1;
}
/* Get the response */
if ( (len = read(clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0)
{
perror("Error reading CLVMD");
return -1;
}
if (len == 0)
{
fprintf(stderr, "EOF reading CLVMD");
errno = ENOTCONN;
return -1;
}
/* Allocate buffer */
*retbuf = malloc(len + outheader->arglen);
if (!*retbuf)
{
errno = ENOMEM;
return -1;
}
/* Copy the header */
memcpy(*retbuf, outbuf, len);
outheader = (struct clvm_header *)*retbuf;
/* Read the returned values */
off = 1; /* we've already read the first byte */
while (off < outheader->arglen && len > 0)
{
len = read(clvmd_sock, outheader->args+off, PIPE_BUF);
if (len > 0)
off += len;
}
/* Was it an error ? */
if (outheader->status < 0)
{
errno = -outheader->status;
return -2;
}
return 0;
}
/* Build the structure header and parse-out wildcard node names */
static void build_header(struct clvm_header *head, int cmd, char *node, void *data, int len)
{
head->cmd = cmd;
head->status = 0;
head->flags = 0;
head->clientid = 0;
head->arglen = len;
if (node)
{
/* Allow a couple of special node names:
"*" for all nodes,
"." for the local node only
*/
if (strcmp(node, "*") == 0)
{
head->node[0] = '\0';
}
else if (strcmp(node, ".") == 0)
{
head->node[0] = '\0';
head->flags = CLVMD_FLAG_LOCAL;
}
else
{
strcpy(head->node, node);
}
}
else
{
head->node[0] = '\0';
}
}
/* Send a message to a(or all) node(s) in the cluster */
int cluster_write(char cmd, char *node, void *data, int len)
{
char outbuf[sizeof(struct clvm_header)+len+strlen(node)+1];
char *retbuf = NULL;
int status;
struct clvm_header *head = (struct clvm_header *)outbuf;
if (clvmd_sock == -1)
clvmd_sock = open_local_sock();
if (clvmd_sock == -1)
return -1;
build_header(head, cmd, node, data, len);
memcpy(head->node+strlen(head->node)+1, data, len);
status = send_request(outbuf, sizeof(struct clvm_header)+strlen(head->node)+len, &retbuf);
if (retbuf) free(retbuf);
return status;
}
/* API: Send a message to a(or all) node(s) in the cluster
and wait for replies */
int cluster_request(char cmd, char *node, void *data, int len,
lvm_response_t **response, int *num)
{
char outbuf[sizeof(struct clvm_header)+len+strlen(node)+1];
int *outptr;
char *inptr;
char *retbuf = NULL;
int status;
int i;
int num_responses=0;
struct clvm_header *head = (struct clvm_header *)outbuf;
lvm_response_t *rarray;
*num = 0;
if (clvmd_sock == -1)
clvmd_sock = open_local_sock();
if (clvmd_sock == -1)
return -1;
build_header(head, cmd, node, data, len);
memcpy(head->node+strlen(head->node)+1, data, len);
status = send_request(outbuf, sizeof(struct clvm_header)+strlen(head->node)+len, &retbuf);
if (status == 0 || status == -2)
{
/* Count the number of responses we got */
head = (struct clvm_header *)retbuf;
inptr = head->args;
while (inptr[0])
{
num_responses++;
inptr += strlen(inptr)+1;
inptr += sizeof(int);
inptr += strlen(inptr)+1;
}
/* Allocate response array. With an extra pair of INTs on the front to sanity
check the pointer when we are given it back to free */
outptr = malloc(sizeof(lvm_response_t) * num_responses + sizeof(int)*2);
if (!outptr)
{
if (retbuf) free(retbuf);
errno = ENOMEM;
return -1;
}
*response = (lvm_response_t *)(outptr+2);
outptr[0] = LVM_SIGNATURE;
outptr[1] = num_responses;
rarray = *response;
/* Unpack the response into an lvm_response_t array */
inptr = head->args;
i = 0;
while (inptr[0])
{
strcpy(rarray[i].node, inptr);
inptr += strlen(inptr)+1;
rarray[i].status = *(int *)inptr;
inptr += sizeof(int);
rarray[i].response = malloc(strlen(inptr)+1);
if (rarray[i].response == NULL)
{
/* Free up everything else and return error */
int j;
for (j=0; j<i; j++)
free(rarray[i].response);
free(outptr);
errno = ENOMEM;
return -1;
}
strcpy(rarray[i].response, inptr);
rarray[i].len = strlen(inptr);
inptr += strlen(inptr)+1;
i++;
}
*num = num_responses;
*response = rarray;
}
if (retbuf) free(retbuf);
return status;
}
/* API: Free reply array */
int cluster_free_request(lvm_response_t *response)
{
int *ptr = (int *)response-2;
int i;
int num;
/* Check it's ours to free */
if (response == NULL || *ptr != LVM_SIGNATURE)
{
errno = EINVAL;
return -1;
}
num = ptr[1];
for (i = 0; i<num; i++)
{
free(response[i].response);
}
free(ptr);
return 0;
}
static pid_t locked_by(char *lockfile_name)
{
/* Check lock is not stale - the file should contain
the owners PID */
FILE *f = fopen(lockfile_name, "r");
pid_t pid = 0;
if (f)
{
char proc_pid[PATH_MAX];
struct stat st;
/* Normal practice to to kill -0 the process at this point
but we may not have the privilege */
fscanf(f, "%d\n", &pid);
fclose(f);
snprintf(proc_pid, sizeof(proc_pid), "/proc/%d", pid);
if (stat(proc_pid, &st) == 0)
{
/* Process exists - lock is valid. */
return pid;
}
/* Remove stale lock file */
unlink(lockfile_name);
}
/* Not locked */
return -1;
}
/* LOCK resource using a file */
static int lock_resource(char *resource, int mode, int flags, int *lockid)
{
struct stat;
int fd;
char lockfile_name[PATH_MAX];
mode_t old_umask;
FILE *pidfile;
int ret = -1;
int ret_errno;
if (mode != LKM_EXMODE)
{
ret_errno = EINVAL;
goto lock_finish;
}
old_umask = umask(000);
ret_errno = -EPERM;
if (mkdir(LOCKFILE_DIR, 0777) != 0)
if (errno != EEXIST)
goto lock_finish;
/* Make the lockfile name */
snprintf(lockfile_name, sizeof(lockfile_name), LOCKFILE_DIR "/%s", resource);
/* Keep trying to lock untill we succeed
unless LKM_NONBLOCK was requested */
do
{
fd = open(lockfile_name, O_CREAT|O_EXCL|O_WRONLY, 0666);
if (fd == -1)
{
pid_t owner_pid;
/* Is the permission on the directory correct ? */
if (errno == EPERM)
goto lock_finish;
owner_pid = locked_by(lockfile_name);
/* If it's locked and the caller doesn't want to block then return */
if (owner_pid > 0 && (flags & O_NONBLOCK))
{
ret_errno = EAGAIN;
goto lock_finish;
}
/* If it's locked, then wait and try again in a second,
Ugh, need directrory notification */
if (owner_pid > 0)
{
sleep(1);
}
}
}
while (fd < 0);
/* OK - lock it */
pidfile = fdopen(fd, "w");
if (pidfile)
{
fprintf(pidfile, "%d\n", getpid());
fclose(pidfile);
ret = 0;
}
lock_finish:
umask(old_umask);
errno = ret_errno;
return ret;
}
static int unlock_resource(char *resource, int lockid)
{
char lockfile_name[PATH_MAX];
pid_t owner_pid;
snprintf(lockfile_name, sizeof(lockfile_name), LOCKFILE_DIR "/%s", resource);
owner_pid = locked_by(lockfile_name);
/* Is it locked by us ? */
if (owner_pid != getpid())
{
errno = EINVAL;
return -1;
}
unlink(lockfile_name);
return 0;
}
/* These are a "higher-level" API providing black-box lock/unlock
functions for cluster LVM...maybe */
/* Set by lock(), used by unlock() */
static int num_responses;
static lvm_response_t *response;
int lock_for_cluster(char scope, char *name, int namelen, int suspend)
{
int status;
int i;
char *args;
int len;
int cmd;
/* Validate scope */
if (scope != 'V' && scope != 'L' && scope != 'G')
{
errno = EINVAL;
return -1;
}
/* Allow for NULL in name field */
if (name && namelen)
{
len = namelen + 2;
args = alloca(namelen);
memcpy(args+1, name, namelen);
}
else
{
len = 2;
args = alloca(len);
args[1] = '\0';
}
args[0] = scope;
cmd = (suspend)?CLVMD_CMD_LOCK_SUSPEND:CLVMD_CMD_LOCK;
status = cluster_request(cmd,
"", args, len,
&response, &num_responses);
/* If any nodes were down then display them and return an error */
for (i=0; i<num_responses; i++)
{
if (response[i].status == -EHOSTDOWN)
{
log_verbose("clvmd not running on node %s\n", response[i].node);
status = -1;
}
}
/* If there was an error then free the memory now as the caller won't
want to do the unlock */
if (status)
{
int saved_errno = errno;
cluster_free_request(response);
num_responses = 0;
errno = saved_errno;
}
return status;
}
int unlock_for_cluster(char scope, char *name, int namelen, int suspend)
{
int status;
int i;
int len;
int failed;
int cmd;
int num_unlock_responses;
char *args;
lvm_response_t *unlock_response;
/* We failed - this should not have been called */
if (num_responses == 0)
return 0;
/* Validate scope */
if (scope != 'V' && scope != 'L' && scope != 'G' &&
scope != 'v' && scope != 'l' && scope != 'g')
{
errno = EINVAL;
return -1;
}
/* Allow for NULL in name field */
if (name && namelen)
{
len = namelen + 2;
args = alloca(namelen);
memcpy(args+1, name, namelen);
}
else
{
len = 2;
args = alloca(len);
args[1] = '\0';
}
args[0] = scope;
cmd = (suspend)?CLVMD_CMD_UNLOCK_RESUME:CLVMD_CMD_UNLOCK;
/* See if it failed anywhere */
failed = 0;
for (i=0; i<num_responses; i++)
{
if (response[i].status != 0)
failed++;
}
/* If it failed on any nodes then we only unlock on
the nodes that succeeded */
if (failed)
{
for (i=0; i<num_responses; i++)
{
/* Unlock the ones that succeeded */
if (response[i].status == 0)
{
status = cluster_request(cmd,
response[i].node,
args, len,
&unlock_response, &num_unlock_responses);
if (status)
{
log_verbose("cluster command to node %s failed: %s\n",
response[i].node, strerror(errno));
}
else if (unlock_response[0].status != 0)
{
log_verbose("unlock on node %s failed: %s\n",
response[i].node, strerror(unlock_response[0].status));
}
cluster_free_request(unlock_response);
}
else
{
log_verbose("command on node %s failed: '%s' - will be left locked\n",
response[i].node, strerror(response[i].status));
}
}
}
else
{
/* All OK, we can do a full cluster unlock */
status = cluster_request(cmd,
"",
args, len,
&unlock_response, &num_unlock_responses);
if (status)
{
log_verbose("cluster command failed: %s\n",
strerror(errno));
}
else
{
for (i=0; i<num_unlock_responses; i++)
{
if (unlock_response[i].status != 0)
{
log_verbose("unlock on node %s failed: %s\n",
response[i].node, strerror(unlock_response[0].status));
}
}
}
cluster_free_request(unlock_response);
}
cluster_free_request(response);
return 0;
}
/* Keep track of the current request state */
static int clustered = 0;
static int suspended = 0;
static int lockid;
/* Lock the whole system */
int lock_lvm(int suspend)
{
int status;
suspended = suspend;
status = lock_for_cluster('G', NULL, 0, suspend);
if (status == -1)
{
/* ENOENT means clvmd is not running - assume we are not clustered */
/* TODO: May need some way of forcing this to fail in the future */
if (errno == ENOENT)
{
clustered = 0;
status = lock_resource(LVM_GLOBAL_LOCK, LKM_EXMODE, 0, &lockid);
if (!status)
return status;
/* TODO: suspend? */
return 0;
}
clustered = 1;
return -1;
}
clustered = 1;
return status;
}
int unlock_lvm(int cmd_status)
{
if (!clustered)
{
/* TODO: resume? */
return unlock_resource(LVM_GLOBAL_LOCK, lockid);
}
else
{
char cmd = (cmd_status==0)?'G':'g';
return unlock_for_cluster(cmd, NULL, 0, suspended);
}
}
/* Lock a whole Volume group and all its LVs */
int lock_vg(struct volume_group *vg, int suspend)
{
int status;
suspended = suspend;
status = lock_for_cluster('V', vg->name, strlen(vg->name)+1, suspend);
if (status == -1)
{
/* ENOENT means clvmd is not running - assume we are not clustered */
/* TODO: May need some way of forcing this to fail in the future */
if (errno == ENOENT)
{
clustered = 0;
/* Get LVM lock */
status = lock_resource(LVM_GLOBAL_LOCK, LKM_EXMODE, 0, &lockid);
if (!status)
return status;
if (suspend) suspend_lvs_in_vg(vg, 1);
return 0;
}
clustered = 1;
return -1;
}
clustered = 1;
return status;
}
int unlock_vg(struct volume_group *vg, int cmd_status)
{
if (!clustered)
{
activate_lvs_in_vg(vg);
return unlock_resource(LVM_GLOBAL_LOCK, lockid);
}
else
{
char cmd = (cmd_status==0)?'V':'v';
return unlock_for_cluster(cmd, vg->name, strlen(vg->name)+1, suspended);
}
}
/* Just lock a Logical volume */
int lock_lv(struct logical_volume *lv, int suspend)
{
int status;
char full_lv_name[strlen(lv->name)+strlen(lv->vg->name)+2];
sprintf(full_lv_name, "%s/%s", lv->vg->name, lv->name);
suspended = suspend;
status = lock_for_cluster('L', full_lv_name, strlen(full_lv_name)+1, suspend);
if (status == -1)
{
/* ENOENT means clvmd is not running - assume we are not clustered */
/* TODO: May need some way of forcing this to fail in the future */
if (errno == ENOENT)
{
clustered = 0;
/* Get LVM lock */
status = lock_resource(LVM_GLOBAL_LOCK, LKM_EXMODE, 0, &lockid);
if (!status)
return status;
if (suspend) lv_suspend(lv, 1);
return 0;
}
clustered = 1;
return -1;
}
clustered = 1;
return status;
}
int unlock_lv(struct logical_volume *lv, int cmd_status)
{
char full_lv_name[strlen(lv->name)+strlen(lv->vg->name)+2];
sprintf(full_lv_name, "%s/%s", lv->vg->name, lv->name);
if (!clustered)
{
lv_reactivate(lv);
return unlock_resource(LVM_GLOBAL_LOCK, lockid);
}
else
{
char cmd = (cmd_status==0)?'L':'l';
return unlock_for_cluster(cmd, full_lv_name, strlen(full_lv_name)+1, suspended);
}
}
/*
Maybe should replace lv_open_count() (Which we use!)
*/
int get_lv_open_count(struct logical_volume *lv, int *open_count)
{
int status;
int num_lv_responses;
lvm_response_t *lv_response;
int count = 0;
int i;
/* Do cluster check */
status = cluster_request(CLVMD_CMD_LVCHECK, "",
lv->name, strlen(lv->name)+1,
&lv_response, &num_lv_responses);
/* Are we sngle-node only ?*/
if (status == -1 && errno == ENOENT)
{
*open_count = lv_open_count(lv);
return 0;
}
if (status)
{
int saved_errno = errno;
cluster_free_request(lv_response);
errno = saved_errno;
return status;
}
for (i=0; i<num_lv_responses; i++)
{
if (lv_response[i].status != 0)
{
log_verbose("lv_open_count on node %s failed: %s\n",
lv_response[i].node, strerror(lv_response[i].status));
}
else
{
if (lv_response[i].response[0] != '\0')
{
count++;
log_verbose("Logical volume %s is open on node %s\n",
lv->name, lv_response[i].node);
}
}
}
cluster_free_request(lv_response);
*open_count = count;
return 0;
}
/*
Get the number of nodes the VG is active on.
*/
int get_vg_active_count(struct volume_group *vg, int *active_count)
{
int status;
int num_vg_responses;
lvm_response_t *vg_response;
int count = 0;
int i;
/* Do cluster check */
status = cluster_request(CLVMD_CMD_VGCHECK, "",
vg->name, strlen(vg->name)+1,
&vg_response, &num_vg_responses);
/* Are we sngle-node only ?*/
if (status == -1 && errno == ENOENT)
{
*active_count = 1; //vg_active(vg);
return 0;
}
if (status)
{
int saved_errno = errno;
cluster_free_request(vg_response);
errno = saved_errno;
return status;
}
for (i=0; i<num_vg_responses; i++)
{
if (vg_response[i].status != 0)
{
log_verbose("vg_active_count on node %s failed: %s\n",
vg_response[i].node, strerror(vg_response[i].status));
}
else
{
if (vg_response[i].response[0] != '\0')
{
count++;
log_verbose("Volume group %s is active on node %s\n",
vg->name, vg_response[i].node);
}
}
}
cluster_free_request(vg_response);
*active_count = count;
return 0;
}

52
lib/cmgr/cmgr.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*
* This is the interface to the locking and cluster manager
*/
typedef struct lvm_response
{
char node[255];
char *response;
int status;
int len;
} lvm_response_t;
/* The "direct to cluster manager" API */
extern int cluster_request(char cmd, char *node, void *data, int len,
lvm_response_t **response, int *num);
extern int cluster_write(char cmd, char *node, void *data, int len);
extern int cluster_free_request(lvm_response_t *response);
/* The "high-level" API */
extern int lock_for_cluster(char scope, char *name, int namelen, int suspend);
extern int unlock_for_cluster(char scope, char *name, int namelen, int suspend);
/* The "even higher-level" API that also copes with
non-clustered environment. */
extern int lock_lvm(int suspend);
extern int unlock_lvm(int cmd_status);
extern int lock_lv(struct logical_volume *lv, int suspend);
extern int unlock_lv(struct logical_volume *lv, int cmd_status);
extern int lock_vg(struct volume_group *vg, int suspend);
extern int unlock_vg(struct volume_group *vg, int cmd_status);
/* The system must be locked by one of the above calls before
using these */
extern int get_lv_open_count(struct logical_volume *lv, int *open_count);
extern int get_vg_active_count(struct volume_group *vg, int *active_count);
/* Lock modes: these are taken from the IBM DLM,
but we only support EX! */
#define LKM_NLMODE 0 /* null lock */
#define LKM_CRMODE 1 /* concurrent read */
#define LKM_CWMODE 2 /* concurrent write */
#define LKM_PRMODE 3 /* protected read */
#define LKM_PWMODE 4 /* protected write */
#define LKM_EXMODE 5 /* exclusive */

View File

@@ -1,16 +0,0 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_ERRORS_H
#define _LVM_ERRORS_H
#define EINVALID_CMD_LINE 1
#define ENO_SUCH_CMD 3
#define ECMD_PROCESSED 4
#define ECMD_FAILED 5
#endif

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*
*/
#ifndef _LVM_TOOLCONTEXT_H
#define _LVM_TOOLCONTEXT_H
#include "dev-cache.h"
#include "config.h"
#include "pool.h"
#include "metadata.h"
/* command-instance-related variables needed by library */
struct cmd_context {
/* format handler allocates all objects from here */
struct pool *mem;
struct format_instance *fid;
char *cmd_line;
char *dev_dir;
struct dev_filter *filter;
struct config_file *cf;
struct command *command;
struct uuid_map *um;
struct arg *args;
};
#endif

View File

@@ -58,6 +58,7 @@ static int _match_aux(struct parser *p, int t);
static struct config_value *_create_value(struct parser *p);
static struct config_node *_create_node(struct parser *p);
static char *_dup_tok(struct parser *p);
static int _tok_match(const char *str, const char *b, const char *e);
#define MAX_INDENT 32
@@ -68,18 +69,6 @@ static char *_dup_tok(struct parser *p);
} \
} while(0);
static int _tok_match(const char *str, const char *b, const char *e)
{
while (*str && (b != e)) {
if (*str++ != *b++)
return 0;
}
return !(*str || (b != e));
}
/*
* public interface
*/
@@ -336,7 +325,7 @@ static struct config_value *_type(struct parser *p)
switch (p->t) {
case TOK_INT:
v->type = CFG_INT;
v->v.i = strtol(p->tb, 0, 0); /* FIXME: check error */
v->v.i = strtol(p->tb, 0, 10); /* FIXME: check error */
match(TOK_INT);
break;
@@ -553,13 +542,11 @@ find_config_str(struct config_node *cn,
{
struct config_node *n = find_config_node(cn, path, sep);
if (n && n->v->type == CFG_STRING) {
log_very_verbose("Setting %s to %s", path, n->v->v.str);
if (n && n->v->type == CFG_STRING)
return n->v->v.str;
}
if (fail)
log_very_verbose("%s not found in config: defaulting to %s",
log_debug("%s not found in config: defaulting to %s",
path, fail);
return fail;
}
@@ -569,13 +556,9 @@ int find_config_int(struct config_node *cn, const char *path,
{
struct config_node *n = find_config_node(cn, path, sep);
if (n && n->v->type == CFG_INT) {
log_very_verbose("Setting %s to %d", path, n->v->v.i);
if (n && n->v->type == CFG_INT)
return n->v->v.i;
}
log_very_verbose("%s not found in config: defaulting to %d",
path, fail);
return fail;
}
@@ -584,91 +567,25 @@ float find_config_float(struct config_node *cn, const char *path,
{
struct config_node *n = find_config_node(cn, path, sep);
if (n && n->v->type == CFG_FLOAT) {
log_very_verbose("Setting %s to %f", path, n->v->v.r);
if (n && n->v->type == CFG_FLOAT)
return n->v->v.r;
return fail;
}
static int _tok_match(const char *str, const char *b, const char *e)
{
while (*str && (b != e)) {
if (*str++ != *b++)
return 0;
}
log_very_verbose("%s not found in config: defaulting to %f",
path, fail);
return fail;
}
static int _str_in_array(const char *str, const char *values[])
{
int i;
for (i = 0; values[i]; i++)
if (!strcasecmp(str, values[i]))
return 1;
return 0;
}
static int _str_to_bool(const char *str, int fail)
{
static const char *_true_values[] = {"y", "yes", "on", "true", NULL};
static const char *_false_values[] = {"n", "no", "off", "false", NULL};
if (_str_in_array(str, _true_values))
return 1;
if (_str_in_array(str, _false_values))
return 0;
return fail;
}
int find_config_bool(struct config_node *cn, const char *path,
char sep, int fail)
{
struct config_node *n = find_config_node(cn, path, sep);
struct config_value *v;
if (!n)
return fail;
v = n->v;
switch (v->type) {
case CFG_INT:
return v->v.i ? 1 : 0;
case CFG_STRING:
return _str_to_bool(v->v.str, fail);
}
return fail;
}
int get_config_uint32(struct config_node *cn, const char *path,
char sep, uint32_t *result)
{
struct config_node *n;
n = find_config_node(cn, path, sep);
if (!n || !n->v || n->v->type != CFG_INT)
return 0;
*result = n->v->v.i;
return 1;
}
int get_config_uint64(struct config_node *cn, const char *path,
char sep, uint64_t *result)
{
struct config_node *n;
n = find_config_node(cn, path, sep);
if (!n || !n->v || n->v->type != CFG_INT)
return 0;
/* FIXME Support 64-bit value! */
*result = (uint64_t) n->v->v.i;
return 1;
return !(*str || (b != e));
}
/*
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -1,13 +1,12 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
* This file is released under the GPL.
*/
#ifndef _LVM_CONFIG_H
#define _LVM_CONFIG_H
#include <inttypes.h>
enum {
CFG_STRING,
@@ -53,20 +52,10 @@ int find_config_int(struct config_node *cn, const char *path,
float find_config_float(struct config_node *cn, const char *path,
char sep, float fail);
/*
* Understands (0, ~0), (y, n), (yes, no), (on,
* off), (true, false).
*/
int find_config_bool(struct config_node *cn, const char *path,
char sep, int fail);
int get_config_uint32(struct config_node *cn, const char *path,
char sep, uint32_t *result);
int get_config_uint64(struct config_node *cn, const char *path,
char sep, uint64_t *result);
#endif
/*
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -1,34 +0,0 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
*/
#ifndef _LVM_DEFAULTS_H
#define _LVM_DEFAULTS_H
#define DEFAULT_SYS_DIR "/etc/lvm"
#define DEFAULT_ARCHIVE_ENABLED 1
#define DEFAULT_BACKUP_ENABLED 1
#define DEFAULT_ARCHIVE_SUBDIR "archive"
#define DEFAULT_BACKUP_SUBDIR "backup"
#define DEFAULT_ARCHIVE_DAYS 30
#define DEFAULT_ARCHIVE_NUMBER 10
#define DEFAULT_DEV_DIR "/dev"
#define DEFAULT_PROC_DIR "/proc"
#define DEFAULT_LOCK_DIR "/var/lock/lvm"
#define DEFAULT_UMASK 0077
#ifdef READLINE_SUPPORT
#define DEFAULT_MAX_HISTORY 100
#endif
#endif /* _LVM_DEFAULTS_H */

View File

@@ -175,7 +175,7 @@ unsigned hash_get_num_entries(struct hash_table *t)
return t->num_nodes;
}
void hash_iter(struct hash_table *t, iterate_fn f)
void hash_iterate(struct hash_table *t, iterate_fn f)
{
struct hash_node *c;
int i;

View File

@@ -21,16 +21,12 @@ int hash_insert(struct hash_table *t, const char *key, void *data);
void hash_remove(struct hash_table *t, const char *key);
unsigned hash_get_num_entries(struct hash_table *t);
void hash_iter(struct hash_table *t, iterate_fn f);
void hash_iterate(struct hash_table *t, iterate_fn f);
char *hash_get_key(struct hash_table *t, struct hash_node *n);
void *hash_get_data(struct hash_table *t, struct hash_node *n);
struct hash_node *hash_get_first(struct hash_table *t);
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n);
#define hash_iterate(v, h) \
for (v = hash_get_first(h); v; \
v = hash_get_next(h, v))
#endif

View File

@@ -47,20 +47,7 @@ static inline int list_empty(struct list *head) {
}
#define list_iterate(v, head) \
for (v = (head)->n; v != head; v = v->n)
#define list_iterate_safe(v, t, head) \
for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
static inline int list_size(struct list *head) {
int s = 0;
struct list *v;
list_iterate(v, head)
s++;
return s;
}
for (v = (head)->n; v != head; v = v->n)
#define list_item(v, t) \
((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->list))

View File

@@ -19,7 +19,6 @@
#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#include <linux/kdev_t.h>
/*
* FIXME: really need to seperate names from the devices since
@@ -109,18 +108,40 @@ static int _insert_dev(const char *path, dev_t d)
}
if (!_add_alias(dev, path)) {
log_err("Couldn't add alias to dev cache.");
log_err("Couldn't add alias to dir cache.");
return 0;
}
if (!hash_insert(_cache.names, path, dev)) {
log_err("Couldn't add name to hash in dev cache.");
log_err("Couldn't add name to hash in dir cache.");
return 0;
}
return 1;
}
/*
* return a new path for the destination of the link.
*/
static char *_follow_link(const char *path, struct stat *info)
{
char buffer[PATH_MAX + 1];
int n;
n = readlink(path, buffer, sizeof(buffer) - 1);
if (n <= 0)
return NULL;
buffer[n] = '\0';
if (stat(buffer, info) < 0) {
log_sys_very_verbose("stat", buffer);
return NULL;
}
return pool_strdup(_cache.mem, buffer);
}
static char *_join(const char *dir, const char *name)
{
int len = strlen(dir) + strlen(name) + 2;
@@ -187,6 +208,7 @@ static int _insert_dir(const char *dir)
static int _insert(const char *path, int rec)
{
struct stat info;
char *actual_path;
int r = 0;
if (stat(path, &info) < 0) {
@@ -194,6 +216,22 @@ static int _insert(const char *path, int rec)
return 0;
}
/* follow symlinks if neccessary. */
if (S_ISLNK(info.st_mode)) {
log_debug("%s: Following symbolic link", path);
if (!(actual_path = _follow_link(path, &info))) {
stack;
return 0;
}
if (stat(actual_path, &info) < 0) {
log_sys_very_verbose("stat", actual_path);
return 0;
}
dbg_free(actual_path);
}
if (S_ISDIR(info.st_mode)) { /* add a directory */
if (rec)
r = _insert_dir(path);
@@ -260,15 +298,25 @@ int dev_cache_init(void)
return 0;
}
void _check_closed(struct device *dev)
{
if (dev->fd >= 0)
log_err("Device '%s' has been left open.", dev_name(dev));
}
static inline void _check_for_open_devices(void)
{
hash_iter(_cache.names, (iterate_fn)_check_closed);
#ifndef NDEBUG
struct dev_iter *i = dev_iter_create(NULL);
struct device *dev;
if (!i) {
stack;
return;
}
while ((dev = dev_iter_get(i))) {
if (dev->fd >= 0)
log_err("Device '%s' has been left open.",
dev_name(dev));
}
dev_iter_destroy(i);
#endif
}
void dev_cache_exit(void)
@@ -304,56 +352,10 @@ int dev_cache_add_dir(const char *path)
return 1;
}
/* Check cached device name is still valid before returning it */
/* This should be a rare occurrence */
/* FIXME Make rest of code pass/cache struct device instead of dev_name */
const char *dev_name_confirmed(struct device *dev)
{
struct stat buf;
char *name;
int r;
while ((r = stat(name = list_item(dev->aliases.n,
struct str_list)->str, &buf)) ||
(buf.st_rdev != dev->dev)) {
if (r < 0)
log_sys_error("stat", name);
log_error("Path %s no longer valid for device(%d,%d)",
name, (int) MAJOR(dev->dev), (int) MINOR(dev->dev));
/* Remove the incorrect hash entry */
hash_remove(_cache.names, name);
/* Leave list alone if there isn't an alternative name */
/* so dev_name will always find something to return. */
/* Otherwise add the name to the correct device. */
if (list_size(&dev->aliases) > 1) {
list_del(dev->aliases.n);
if (!r)
_insert(name, 0);
continue;
}
log_error("Aborting - please provide new pathname for what "
"used to be %s", name);
return NULL;
}
return dev_name(dev);
}
struct device *dev_cache_get(const char *name, struct dev_filter *f)
{
struct stat buf;
struct device *d = (struct device *) hash_lookup(_cache.names, name);
/* If the entry's wrong, remove it */
if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) {
hash_remove(_cache.names, name);
d = NULL;
}
if (!d) {
_insert(name, 0);
d = (struct device *) hash_lookup(_cache.names, name);
@@ -400,3 +402,5 @@ struct device *dev_iter_get(struct dev_iter *iter)
return NULL;
}

View File

@@ -13,7 +13,8 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h> // UGH!!! for BLKSSZGET
#include <sys/mount.h>
int dev_get_size(struct device *dev, uint64_t *size)
{
@@ -39,69 +40,24 @@ int dev_get_size(struct device *dev, uint64_t *size)
return 1;
}
int dev_get_sectsize(struct device *dev, uint32_t *size)
{
int fd;
int s;
const char *name = dev_name(dev);
log_very_verbose("Getting size of %s", name);
if ((fd = open(name, O_RDONLY)) < 0) {
log_sys_error("open", name);
return 0;
}
if (ioctl(fd, BLKSSZGET, &s) < 0) {
log_sys_error("ioctl BLKSSZGET", name);
close(fd);
return 0;
}
close(fd);
*size = (uint32_t) s;
return 1;
}
int dev_open(struct device *dev, int flags)
{
struct stat buf;
const char *name = dev_name_confirmed(dev);
if (!name) {
stack;
return 0;
}
const char *name = dev_name(dev);
/* FIXME Check flags (eg is write now reqd?) */
if (dev->fd >= 0) {
log_error("Device '%s' has already been opened", name);
return 0;
}
if ((stat(name, &buf) < 0) || (buf.st_rdev != dev->dev)) {
log_error("%s: stat failed: Has device name changed?", name);
return 0;
}
if ((dev->fd = open(name, flags)) < 0) {
log_sys_error("open", name);
return 0;
}
if ((fstat(dev->fd, &buf) < 0) || (buf.st_rdev != dev->dev)) {
log_error("%s: fstat failed: Has device name changed?", name);
dev_close(dev);
return 0;
}
return 1;
}
static void _flush(int fd)
{
if (ioctl(fd, BLKFLSBUF, 0))
log_error("couldn't flush device.");
}
int dev_close(struct device *dev)
{
if (dev->fd < 0) {
@@ -110,8 +66,6 @@ int dev_close(struct device *dev)
return 0;
}
_flush(dev->fd);
if (close(dev->fd))
log_sys_error("close", dev_name(dev));
@@ -167,10 +121,6 @@ int _write(int fd, const void *buf, size_t count)
size_t n = 0;
int tot = 0;
/* Skip all writes */
if (test_mode())
return count;
while (tot < count) {
do
n = write(fd, buf, count - tot);
@@ -213,7 +163,7 @@ int dev_zero(struct device *dev, uint64_t offset, int64_t len)
int fd = dev->fd;
if (fd < 0) {
log_error("Attempt to zero part of an unopened device %s",
log_error("Attempt to zero part of an unopened device %s",
name);
return 0;
}
@@ -238,6 +188,5 @@ int dev_zero(struct device *dev, uint64_t offset, int64_t len)
}
}
/* FIXME: Always display error */
return (len == 0);
}

View File

@@ -22,16 +22,10 @@ struct device {
int fd;
};
struct device_list {
struct list list;
struct device *dev;
};
/*
* All io should use these routines.
*/
int dev_get_size(struct device *dev, uint64_t *size);
int dev_get_sectsize(struct device *dev, uint32_t *size);
int dev_open(struct device *dev, int flags);
int dev_close(struct device *dev);
@@ -47,8 +41,6 @@ static inline const char *dev_name(struct device *dev) {
return list_item(dev->aliases.n, struct str_list)->str;
}
/* Return a valid device name from the alias list; NULL otherwise */
const char *dev_name_confirmed(struct device *dev);
static inline int is_lvm_partition(const char *name) {
return 1;

View File

@@ -18,20 +18,17 @@
*
*/
#include <sys/types.h>
#include <string.h>
#include "metadata.h"
#include "dbg_malloc.h"
#include "log.h"
#include "display.h"
#include "activate.h"
#include "uuid.h"
#include "toolcontext.h"
#include <sys/types.h>
#include <string.h>
#define SIZE_BUF 128
char *display_size(uint64_t size, size_len_t sl)
char *display_size(unsigned long long size, size_len_t sl)
{
int s;
ulong byte = 1024 * 1024 * 1024;
@@ -63,35 +60,68 @@ char *display_size(uint64_t size, size_len_t sl)
return size_buf;
}
/*
* FIXME: this function is badly named, it doesn't display the data it
* creates a new uuid string with -'s in it. It would be better if
* the destination was passed in as well. EJT
*/
char *display_uuid(char *uuidstr)
{
int i, j;
char *uuid;
if ((!uuidstr) || !(uuid = dbg_malloc(NAME_LEN))) {
log_error("no memory for uuid display buffer");
return NULL;
}
memset(uuid, 0, NAME_LEN);
i = 6;
memcpy(uuid, uuidstr, i);
uuidstr += i;
for (j = 0; j < 6; j++) {
uuid[i++] = '-';
memcpy(&uuid[i], uuidstr, 4);
uuidstr += 4;
i += 4;
}
memcpy(&uuid[i], uuidstr, 2);
/* Caller must free */
return uuid;
}
void pvdisplay_colons(struct physical_volume *pv)
{
char uuid[64];
char *uuid;
if (!pv)
return;
if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
stack;
return;
}
uuid = display_uuid(pv->id.uuid);
log_print("%s:%s:%" PRIu64 ":-1:%u:%u:-1:%" PRIu64 ":%u:%u:%u:%s",
dev_name(pv->dev), pv->vg_name, pv->size,
/* FIXME pv->pv_number, Derive or remove? */
pv->status, /* FIXME Support old or new format here? */
pv->status & ALLOCATABLE_PV, /* FIXME remove? */
pv->status & ALLOCATED_PV, /* FIXME Remove? */
/* FIXME pv->lv_cur, Remove? */
pv->pe_size / 2,
pv->pe_count,
pv->pe_count - pv->pe_allocated,
pv->pe_allocated, *uuid ? uuid : "none");
dbg_free(uuid);
return;
}
void pvdisplay_full(struct physical_volume *pv)
{
char uuid[64];
char *uuid;
char *size, *size1; /*, *size2; */
uint64_t pe_free;
@@ -99,22 +129,18 @@ void pvdisplay_full(struct physical_volume *pv)
if (!pv)
return;
if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
stack;
return;
}
uuid = display_uuid(pv->id.uuid);
log_print("--- %sPhysical volume ---", pv->pe_size ? "" : "NEW ");
log_print("PV Name %s", dev_name(pv->dev));
log_print("VG Name %s%s", pv->vg_name,
pv->status & EXPORTED_VG ? " (exported)" : "");
log_print("VG Name %s", pv->vg_name);
size = display_size((uint64_t) pv->size / 2, SIZE_SHORT);
size = display_size(pv->size / 2, SIZE_SHORT);
if (pv->pe_size && pv->pe_count) {
size1 = display_size((pv->size - pv->pe_count * pv->pe_size)
/ 2, SIZE_SHORT);
/******** FIXME display LVM on-disk data size
/******** FIXME display LVM on-disk data size
size2 = display_size(pv->size / 2, SIZE_SHORT);
********/
@@ -131,8 +157,11 @@ void pvdisplay_full(struct physical_volume *pv)
log_print("PV# %u", pv->pv_number);
**********/
log_print("PV Status %savailable",
(pv->status & ACTIVE) ? "" : "NOT ");
pe_free = pv->pe_count - pv->pe_allocated;
if (pv->pe_count && (pv->status & ALLOCATABLE_PV))
if (pv->pe_count && (pv->status & ALLOCATED_PV))
log_print("Allocatable yes %s",
(!pe_free && pv->pe_count) ? "(but full)" : "");
else
@@ -153,79 +182,60 @@ void pvdisplay_full(struct physical_volume *pv)
log_print("PV UUID %s", *uuid ? uuid : "none");
log_print(" ");
dbg_free(uuid);
return;
}
int pvdisplay_short(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv)
void pv_display_short(struct physical_volume *pv)
{
if (!pv)
return 0;
return;
log_print("PV Name %s ", dev_name(pv->dev));
/* FIXME pv->pv_number); */
log_print("PV Status %sallocatable",
(pv->status & ALLOCATABLE_PV) ? "" : "NOT ");
log_print("PV Status %savailable / %sallocatable",
(pv->status & ACTIVE) ? "" : "NOT ",
(pv->status & ALLOCATED_PV) ? "" : "NOT ");
log_print("Total PE / Free PE %u / %u",
pv->pe_count, pv->pe_count - pv->pe_allocated);
log_print(" ");
return 0;
return;
}
void lvdisplay_colons(struct logical_volume *lv)
{
int inkernel;
struct dm_info info;
inkernel = lv_info(lv, &info) && info.exists;
log_print("%s%s/%s:%s:%d:%d:-1:%d:%" PRIu64 ":%d:-1:%d:%d:%d:%d",
lv->vg->cmd->dev_dir,
log_print("%s/%s:%s:%d:%d:-1:%d:%" PRIu64 ":%d:-1:%d:%d:-1:-1",
/* FIXME Prefix - attach to struct volume_group? */
lv->vg->name,
lv->name,
lv->vg->name,
(lv->status & (LVM_READ | LVM_WRITE)) >> 8,
inkernel ? 1 : 0,
lv->status & ACTIVE,
/* FIXME lv->lv_number, */
inkernel ? info.open_count : 0, lv->size, lv->le_count,
lvs_in_vg_opened(lv->vg), lv->size, lv->le_count,
/* FIXME Add num allocated to struct! lv->lv_allocated_le, */
((lv->status & ALLOC_STRICT) +
(lv->status & ALLOC_CONTIGUOUS) * 2), lv->read_ahead,
inkernel ? info.major : -1,
inkernel ? info.minor : -1
(lv->status & ALLOC_CONTIGUOUS) * 2), lv->read_ahead
/* FIXME device num MAJOR(lv->lv_dev), MINOR(lv->lv_dev) */
);
return;
}
int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv)
void lvdisplay_full(struct logical_volume *lv)
{
char *size;
uint32_t alloc;
struct dm_info info;
int inkernel;
char uuid[64];
struct snapshot *snap;
if (!id_write_format(&lv->lvid.id[1], uuid, sizeof(uuid))) {
stack;
return 0;
}
inkernel = lv_info(lv, &info) && info.exists;
log_print("--- Logical volume ---");
log_print("LV Name %s%s/%s", lv->vg->cmd->dev_dir,
lv->vg->name, lv->name);
/* FIXME Add dev_dir */
log_print("LV Name %s/%s", lv->vg->name, lv->name);
log_print("VG Name %s", lv->vg->name);
log_print("LV UUID %s", uuid);
log_print("LV Write Access %s",
(lv->status & LVM_WRITE) ? "read/write" : "read only");
if ((snap = find_cow(lv)))
log_print("Snapshot of %s", snap->origin->name);
/******* FIXME Snapshot
if (lv->status & (LVM_SNAPSHOT_ORG | LVM_SNAPSHOT)) {
if (lvm_tab_vg_read_with_pv_and_lv(vg_name, &vg) < 0) {
@@ -271,19 +281,16 @@ int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv)
}
***********/
if (inkernel && info.suspended)
log_print("LV Status suspended");
else
log_print("LV Status %savailable",
inkernel ? "" : "NOT ");
log_print("LV Status %savailable",
(lv->status & ACTIVE) ? "" : "NOT ");
/********* FIXME lv_number
log_print("LV # %u", lv->lv_number + 1);
************/
if (inkernel)
log_print("# open %u", info.open_count);
/********* FIXME lv_open
log_print("# open %u\n", lv->lv_open);
**********/
/********
#ifdef LVM_FUTURE
printf("Mirror copies %u\n", lv->lv_mirror_copies);
@@ -338,11 +345,12 @@ int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv)
}
******************/
log_print("Segments %u", list_size(&lv->segments));
/********* FIXME Stripes & stripesize for each segment
if (lv->stripes > 1) {
log_print("Stripes %u", lv->stripes);
/*********** FIXME stripesize
log_print("Stripe size (KByte) %u", lv->stripesize / 2);
***********/
}
/**************
#ifdef LVM_FUTURE
@@ -364,10 +372,7 @@ int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv)
(ALLOC_STRICT | ALLOC_CONTIGUOUS)) ? "strict/contiguous" :
"");
log_print("Read ahead sectors %u", lv->read_ahead);
if (lv->status & FIXED_MINOR)
log_print("Persistent minor %d", lv->minor);
log_print("Read ahead sectors %u\n", lv->read_ahead);
/****************
#ifdef LVM_FUTURE
@@ -379,160 +384,51 @@ int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv)
#endif
*************/
if (inkernel)
log_print("Block device %d:%d", info.major,
info.minor);
/********* FIXME blockdev
printf("Block device %d:%d\n",
MAJOR(lv->lv_dev), MINOR(lv->lv_dev));
*************/
log_print(" ");
return 0;
return;
}
void _display_stripe(struct stripe_segment *seg, int s, const char *pre)
void lvdisplay_extents(struct logical_volume *lv)
{
uint32_t len = seg->len / seg->stripes;
int le;
struct list *pvh;
struct physical_volume *pv;
log_print("%sphysical volume\t%s", pre,
seg->area[s].pv ? dev_name(seg->area[s].pv->dev) : "Missing");
log_verbose("--- Distribution of logical volume on physical "
"volumes ---");
log_verbose("PV Name PE on PV ");
if (seg->area[s].pv)
log_print("%sphysical extents\t%d to %d", pre,
seg->area[s].pe, seg->area[s].pe + len - 1);
}
int lvdisplay_segments(struct logical_volume *lv)
{
int s;
struct list *segh;
struct stripe_segment *seg;
log_print("--- Segments ---");
list_iterate (segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
log_print("logical extent %d to %d:",
seg->le, seg->le + seg->len - 1);
if (seg->stripes == 1)
_display_stripe(seg, 0, " ");
else {
log_print(" stripes\t\t%d", seg->stripes);
log_print(" stripe size\t\t%d", seg->stripe_size);
for (s = 0; s < seg->stripes; s++) {
log_print(" stripe %d:", s);
_display_stripe(seg, s, " ");
}
}
log_print(" ");
list_iterate(pvh, &lv->vg->pvs) {
int count = 0;
pv = &list_item(pvh, struct pv_list)->pv;
for (le = 0; le < lv->le_count; le++)
if (lv->map[le].pv->dev == pv->dev)
count++;
if (count)
log_verbose("%-25s %d", dev_name(pv->dev), count);
}
log_print(" ");
return 1;
}
/********* FIXME "reads writes"
printf("\n --- logical volume i/o statistic ---\n"
" %d reads %d writes\n", sum_reads, sum_writes);
******* */
void vgdisplay_extents(struct volume_group *vg)
{
return;
}
log_verbose(" ");
log_verbose("--- Logical extents ---");
log_verbose("LE PV PE");
void vgdisplay_full(struct volume_group *vg)
{
uint32_t access;
char *s1;
char uuid[64];
log_print("--- Volume group ---");
log_print("VG Name %s", vg->name);
log_print("System ID %s", vg->system_id);
access = vg->status & (LVM_READ | LVM_WRITE);
log_print("VG Access %s%s%s%s",
access == (LVM_READ | LVM_WRITE) ? "read/write" : "",
access == LVM_READ ? "read" : "",
access == LVM_WRITE ? "write" : "",
access == 0 ? "error" : "");
log_print("VG Status %s%sresizable",
vg->status & EXPORTED_VG ? "exported/" : "",
vg->status & RESIZEABLE_VG ? "" : "NOT ");
/******* FIXME vg number
log_print ("VG # %u\n", vg->vg_number);
********/
if (vg->status & CLUSTERED) {
log_print("Clustered yes");
log_print("Shared %s",
vg->status & SHARED ? "yes" : "no");
}
log_print("MAX LV %u", vg->max_lv);
log_print("Cur LV %u", vg->lv_count);
/****** FIXME Open LVs
log_print ( "Open LV %u", vg->lv_open);
*******/
/****** FIXME Max LV Size
log_print ( "MAX LV Size %s",
( s1 = display_size ( LVM_LV_SIZE_MAX(vg) / 2, SIZE_SHORT)));
free ( s1);
*********/
log_print("Max PV %u", vg->max_pv);
log_print("Cur PV %u", vg->pv_count);
/******* FIXME act PVs
log_print ( "Act PV %u", vg->pv_act);
*********/
s1 = display_size((uint64_t) vg->extent_count * (vg->extent_size / 2), SIZE_SHORT);
log_print("VG Size %s", s1);
dbg_free(s1);
s1 = display_size(vg->extent_size / 2, SIZE_SHORT);
log_print("PE Size %s", s1);
dbg_free(s1);
log_print("Total PE %u", vg->extent_count);
s1 =
display_size(((uint64_t)
vg->extent_count - vg->free_count) *
(vg->extent_size / 2), SIZE_SHORT);
log_print("Alloc PE / Size %u / %s",
vg->extent_count - vg->free_count, s1);
dbg_free(s1);
s1 = display_size((uint64_t) vg->free_count * (vg->extent_size / 2), SIZE_SHORT);
log_print("Free PE / Size %u / %s", vg->free_count, s1);
dbg_free(s1);
if (!id_write_format(&vg->id, uuid, sizeof(uuid))) {
stack;
return;
for (le = 0; le < lv->le_count; le++) {
log_verbose("%05d %-25s %05u ", le,
dev_name(lv->map[le].pv->dev), lv->map[le].pe);
}
log_print("VG UUID %s", uuid);
log_print(" ");
return;
}
void vgdisplay_colons(struct volume_group *vg)
{
return;
}
void vgdisplay_short(struct volume_group *vg)
{
char *s1, *s2, *s3;
s1 = display_size(vg->extent_count * vg->extent_size / 2, SIZE_SHORT);
s2 =
display_size((vg->extent_count - vg->free_count) * vg->extent_size /
2, SIZE_SHORT);
s3 = display_size(vg->free_count * vg->extent_size / 2, SIZE_SHORT);
log_print("\"%s\" %-9s [%-9s used / %s free]", vg->name,
/********* FIXME if "open" print "/used" else print "/idle"??? ******/
s1, s2, s3);
dbg_free(s1);
dbg_free(s2);
dbg_free(s3);
return;
}

View File

@@ -28,20 +28,26 @@
typedef enum {SIZE_LONG=0, SIZE_SHORT=1} size_len_t;
/* Specify size in KB */
char *display_size(uint64_t size, size_len_t sl);
char *display_size(unsigned long long size, size_len_t sl);
char *display_uuid(char *uuidstr);
void pvdisplay_colons(struct physical_volume *pv);
void pvdisplay_full(struct physical_volume *pv);
int pvdisplay_short(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv);
void lvdisplay_colons(struct logical_volume *lv);
int lvdisplay_segments(struct logical_volume *lv);
int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv);
void lvdisplay_extents(struct logical_volume *lv);
void lvdisplay_full(struct logical_volume *lv);
void vgdisplay_extents(struct volume_group *vg);
void vgdisplay_full(struct volume_group *vg);
void vgdisplay_colons(struct volume_group *vg);
void vgdisplay_short(struct volume_group *vg);
#if 0
void pv_show_short(pv_t * pv);
void pv_display_pe(pv_t * pv, pe_disk_t * pe);
void pv_display_pe_free(int pe_free, int p);
void pv_display_pe_text(pv_t * pv, pe_disk_t * pe, lv_disk_t * lvs);
static inline unsigned long get_pe_offset(ulong p, pv_t *pv)
{
return pv->pe_start + (p * pv->pe_size);
}
#endif
#endif

View File

@@ -141,12 +141,7 @@ int persistent_filter_dump(struct dev_filter *f)
struct pfilter *pf = (struct pfilter *) f->private;
FILE *fp;
if (!hash_get_num_entries(pf->devices)) {
log_very_verbose("Internal persistent device cache empty "
"- not writing to %s", pf->file);
return 0;
}
log_very_verbose("Dumping persistent device cache to %s", pf->file);
fp = fopen(pf->file, "w");

View File

@@ -22,7 +22,6 @@
#include "log.h"
#include "dev-cache.h"
#include "filter.h"
#include "lvm-string.h"
#include <stdlib.h>
#include <dirent.h>
@@ -30,7 +29,6 @@
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <linux/kdev_t.h>
@@ -41,8 +39,6 @@ typedef struct {
int max_partitions;
} device_info_t;
static int _md_major = -1;
static device_info_t device_info[] = {
{"ide", 16}, /* IDE disk */
{"sd", 16}, /* SCSI disk */
@@ -54,11 +50,10 @@ static device_info_t device_info[] = {
{"ida", 16}, /* Compaq SMART2 */
{"cciss", 16}, /* Compaq CCISS array */
{"ubd", 16}, /* User-mode virtual block device */
{"ataraid", 16}, /* ATA Raid */
{NULL, 0}
};
static int *scan_proc_dev(const char *proc);
static int *scan_proc_dev(void);
static int passes_lvm_type_device_filter(struct dev_filter *f,
struct device *dev)
@@ -81,7 +76,7 @@ static int passes_lvm_type_device_filter(struct dev_filter *f,
return 1;
}
struct dev_filter *lvm_type_filter_create(const char *proc)
struct dev_filter *lvm_type_filter_create()
{
struct dev_filter *f;
@@ -93,17 +88,12 @@ struct dev_filter *lvm_type_filter_create(const char *proc)
f->passes_filter = passes_lvm_type_device_filter;
f->destroy = lvm_type_filter_destroy;
if (!(f->private = scan_proc_dev(proc)))
if (!(f->private = scan_proc_dev()))
return NULL;
return f;
}
int md_major(void)
{
return _md_major;
}
void lvm_type_filter_destroy(struct dev_filter *f)
{
dbg_free(f->private);
@@ -111,11 +101,10 @@ void lvm_type_filter_destroy(struct dev_filter *f)
return;
}
static int *scan_proc_dev(const char *proc)
static int *scan_proc_dev(void)
{
char line[80];
char proc_devices[PATH_MAX];
FILE *pd = NULL;
FILE *procdevices = NULL;
int ret = 0;
int i, j = 0;
int line_maj = 0;
@@ -129,19 +118,13 @@ static int *scan_proc_dev(const char *proc)
return NULL;
}
if (lvm_snprintf(proc_devices, sizeof(proc_devices),
"%s/devices", proc) < 0) {
log_error("Failed to create /proc/devices string");
return NULL;
}
if (!(pd = fopen(proc_devices, "r"))) {
log_sys_error("fopen", proc_devices);
if (!(procdevices = fopen("/proc/devices", "r"))) {
log_error("Failed to open /proc/devices: %s", strerror(errno));
return NULL;
}
memset(max_partitions_by_major, 0, sizeof (int) * NUMBER_OF_MAJORS);
while (fgets(line, 80, pd) != NULL) {
while (fgets(line, 80, procdevices) != NULL) {
i = 0;
while (line[i] == ' ' && line[i] != '\0')
i++;
@@ -163,10 +146,6 @@ static int *scan_proc_dev(const char *proc)
while (line[i] == ' ' && line[i] != '\0')
i++;
/* Look for md device */
if (!strncmp("md", line + i, 2) && isspace(*(line + i + 2)))
_md_major = line_maj;
/* Go through the valid device names and if there is a
match store max number of partitions */
for (j = 0; device_info[j].name != NULL; j++) {
@@ -182,6 +161,6 @@ static int *scan_proc_dev(const char *proc)
}
}
}
fclose(pd);
fclose(procdevices);
return max_partitions_by_major;
}

View File

@@ -21,11 +21,9 @@
#ifndef _LVM_FILTER_H
#define _LVM_FILTER_H
struct dev_filter *lvm_type_filter_create(const char *);
struct dev_filter *lvm_type_filter_create();
void lvm_type_filter_destroy(struct dev_filter *f);
int md_major(void);
#endif

View File

@@ -5,17 +5,13 @@
*/
#include "disk-rep.h"
#include "dbg_malloc.h"
#include "pool.h"
#include "xlate.h"
#include "log.h"
#include "vgcache.h"
#include "filter.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/kdev_t.h>
#define fail do {stack; return 0;} while(0)
#define xx16(v) disk->v = xlate16(disk->v)
@@ -29,71 +25,66 @@
*/
static void _xlate_pvd(struct pv_disk *disk)
{
xx16(version);
xx16(version);
xx32(pv_on_disk.base);
xx32(pv_on_disk.size);
xx32(vg_on_disk.base);
xx32(vg_on_disk.size);
xx32(pv_uuidlist_on_disk.base);
xx32(pv_uuidlist_on_disk.size);
xx32(lv_on_disk.base);
xx32(lv_on_disk.size);
xx32(pe_on_disk.base);
xx32(pe_on_disk.size);
xx32(pv_on_disk.base); xx32(pv_on_disk.size);
xx32(vg_on_disk.base); xx32(vg_on_disk.size);
xx32(pv_uuidlist_on_disk.base); xx32(pv_uuidlist_on_disk.size);
xx32(lv_on_disk.base); xx32(lv_on_disk.size);
xx32(pe_on_disk.base); xx32(pe_on_disk.size);
xx32(pv_major);
xx32(pv_number);
xx32(pv_status);
xx32(pv_allocatable);
xx32(pv_size);
xx32(lv_cur);
xx32(pe_size);
xx32(pe_total);
xx32(pe_allocated);
xx32(pv_major);
xx32(pv_number);
xx32(pv_status);
xx32(pv_allocatable);
xx32(pv_size);
xx32(lv_cur);
xx32(pe_size);
xx32(pe_total);
xx32(pe_allocated);
xx32(pe_start);
}
static void _xlate_lvd(struct lv_disk *disk)
{
xx32(lv_access);
xx32(lv_status);
xx32(lv_open);
xx32(lv_dev);
xx32(lv_number);
xx32(lv_mirror_copies);
xx32(lv_recovery);
xx32(lv_schedule);
xx32(lv_size);
xx32(lv_snapshot_minor);
xx16(lv_chunk_size);
xx16(dummy);
xx32(lv_allocated_le);
xx32(lv_stripes);
xx32(lv_stripesize);
xx32(lv_badblock);
xx32(lv_allocation);
xx32(lv_io_timeout);
xx32(lv_read_ahead);
xx32(lv_access);
xx32(lv_status);
xx32(lv_open);
xx32(lv_dev);
xx32(lv_number);
xx32(lv_mirror_copies);
xx32(lv_recovery);
xx32(lv_schedule);
xx32(lv_size);
xx32(lv_snapshot_minor);
xx16(lv_chunk_size);
xx16(dummy);
xx32(lv_allocated_le);
xx32(lv_stripes);
xx32(lv_stripesize);
xx32(lv_badblock);
xx32(lv_allocation);
xx32(lv_io_timeout);
xx32(lv_read_ahead);
}
static void _xlate_vgd(struct vg_disk *disk)
{
xx32(vg_number);
xx32(vg_access);
xx32(vg_status);
xx32(lv_max);
xx32(lv_cur);
xx32(lv_open);
xx32(pv_max);
xx32(pv_cur);
xx32(pv_act);
xx32(dummy);
xx32(vgda);
xx32(pe_size);
xx32(pe_total);
xx32(pe_allocated);
xx32(pvg_total);
xx32(vg_number);
xx32(vg_access);
xx32(vg_status);
xx32(lv_max);
xx32(lv_cur);
xx32(lv_open);
xx32(pv_max);
xx32(pv_cur);
xx32(pv_act);
xx32(dummy);
xx32(vgda);
xx32(pe_size);
xx32(pe_total);
xx32(pe_allocated);
xx32(pvg_total);
}
static void _xlate_extents(struct pe_disk *extents, int count)
@@ -132,28 +123,13 @@ static int _munge_formats(struct pv_disk *pvd)
return 1;
}
int read_pvd(struct device *dev, struct pv_disk *pvd)
static int _read_pvd(struct disk_list *data)
{
if (dev_read(dev, 0, sizeof(*pvd), pvd) != sizeof(*pvd)) {
log_very_verbose("Failed to read PV data from %s",
dev_name(dev));
return 0;
}
struct pv_disk *pvd = &data->pvd;
if (dev_read(data->dev, 0, sizeof(*pvd), pvd) != sizeof(*pvd))
fail;
_xlate_pvd(pvd);
if (pvd->id[0] != 'H' || pvd->id[1] != 'M') {
log_very_verbose("%s does not have a valid PV identifier",
dev_name(dev));
return 0;
}
if (!_munge_formats(pvd)) {
log_very_verbose("Unknown metadata version %d found on %s",
pvd->version, dev_name(dev));
return 0;
}
return 1;
}
@@ -170,7 +146,7 @@ static int _read_lvd(struct device *dev, ulong pos, struct lv_disk *disk)
static int _read_vgd(struct disk_list *data)
{
struct vg_disk *vgd = &data->vgd;
ulong pos = data->pvd.vg_on_disk.base;
unsigned long pos = data->pvd.vg_on_disk.base;
if (dev_read(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
fail;
@@ -187,7 +163,7 @@ static int _read_uuids(struct disk_list *data)
ulong pos = data->pvd.pv_uuidlist_on_disk.base;
ulong end = pos + data->pvd.pv_uuidlist_on_disk.size;
while (pos < end && num_read < data->vgd.pv_cur) {
while(pos < end && num_read < data->vgd.pv_cur) {
if (dev_read(data->dev, pos, sizeof(buffer), buffer) !=
sizeof(buffer))
fail;
@@ -196,7 +172,7 @@ static int _read_uuids(struct disk_list *data)
fail;
memcpy(ul->uuid, buffer, NAME_LEN);
ul->uuid[NAME_LEN - 1] = '\0';
ul->uuid[NAME_LEN] = '\0';
list_add(&data->uuids, &ul->list);
@@ -215,11 +191,11 @@ static inline int _check_lvd(struct lv_disk *lvd)
static int _read_lvs(struct disk_list *data)
{
int i, read = 0;
ulong pos;
unsigned long pos;
struct lvd_list *ll;
struct vg_disk *vgd = &data->vgd;
for (i = 0; (i < vgd->lv_max) && (read < vgd->lv_cur); i++) {
for(i = 0; (i < vgd->lv_max) && (read < vgd->lv_cur); i++) {
pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
ll = pool_alloc(data->mem, sizeof(*ll));
@@ -243,7 +219,7 @@ static int _read_extents(struct disk_list *data)
{
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
struct pe_disk *extents = pool_alloc(data->mem, len);
ulong pos = data->pvd.pe_on_disk.base;
unsigned long pos = data->pvd.pe_on_disk.base;
if (!extents)
fail;
@@ -257,26 +233,6 @@ static int _read_extents(struct disk_list *data)
return 1;
}
/*
* If exported, remove "PV_EXP" from end of VG name
*/
static void _munge_exported_vg(struct disk_list *data)
{
int l, s;
/* Return if PV not in a VG or VG not exported */
if ((!*data->pvd.vg_name) ||
!(data->vgd.vg_status & VG_EXPORTED))
return;
l = strlen(data->pvd.vg_name);
s = sizeof(EXPORTED_TAG);
if (!strncmp(data->pvd.vg_name + l - s + 1, EXPORTED_TAG, s))
data->pvd.vg_name[l - s + 1] = '\0';
data->pvd.pv_status |= VG_EXPORTED;
}
static struct disk_list *__read_disk(struct device *dev, struct pool *mem,
const char *vg_name)
{
@@ -293,40 +249,42 @@ static struct disk_list *__read_disk(struct device *dev, struct pool *mem,
list_init(&data->uuids);
list_init(&data->lvds);
if (!read_pvd(dev, &data->pvd)) {
stack;
if (!_read_pvd(data)) {
log_debug("Failed to read PV data from %s", name);
goto bad;
}
if (data->pvd.id[0] != 'H' || data->pvd.id[1] != 'M') {
log_very_verbose("%s does not have a valid PV identifier",
name);
goto bad;
}
if (!_munge_formats(&data->pvd)) {
log_very_verbose("Unknown metadata version %d found on %s",
data->pvd.version, name);
goto bad;
}
/*
* is it an orphan ?
*/
if (!*data->pvd.vg_name) {
if (data->pvd.vg_name == '\0') {
log_very_verbose("%s is not a member of any VG", name);
/* Update VG cache */
vgcache_add(data->pvd.vg_name, NULL, dev);
return (vg_name) ? NULL : data;
return data;
}
if (!_read_vgd(data)) {
log_error("Failed to read VG data from PV (%s)", name);
goto bad;
}
/* If VG is exported, set VG name back to the real name */
_munge_exported_vg(data);
/* Update VG cache with what we found */
vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, dev);
if (vg_name && strcmp(vg_name, data->pvd.vg_name)) {
log_very_verbose("%s is not a member of the VG %s",
name, vg_name);
goto bad;
}
if (!_read_vgd(data)) {
log_error("Failed to read VG data from PV (%s)", name);
goto bad;
}
if (!_read_uuids(data)) {
log_error("Failed to read PV uuid list from %s", name);
goto bad;
@@ -342,13 +300,11 @@ static struct disk_list *__read_disk(struct device *dev, struct pool *mem,
goto bad;
}
log_very_verbose("Found %s in %sVG %s", name,
(data->vgd.vg_status & VG_EXPORTED) ? "exported " : "",
data->pvd.vg_name);
log_very_verbose("Found %s in VG %s", name, data->pvd.vg_name);
return data;
bad:
bad:
pool_free(data->mem, data);
return NULL;
}
@@ -371,74 +327,23 @@ struct disk_list *read_disk(struct device *dev, struct pool *mem,
return r;
}
static void _add_pv_to_list(struct list *head, struct disk_list *data)
{
struct list *pvdh;
struct pv_disk *pvd;
list_iterate(pvdh, head) {
pvd = &list_item(pvdh, struct disk_list)->pvd;
if (!strncmp(data->pvd.pv_uuid, pvd->pv_uuid,
sizeof(pvd->pv_uuid))) {
if (MAJOR(data->dev->dev) != md_major()) {
log_very_verbose("Ignoring duplicate PV %s on "
"%s", pvd->pv_uuid,
dev_name(data->dev));
return;
}
log_very_verbose("Duplicate PV %s - using md %s",
pvd->pv_uuid, dev_name(data->dev));
list_del(pvdh);
break;
}
}
list_add(head, &data->list);
}
/*
* Build a list of pv_d's structures, allocated from mem.
* We keep track of the first object allocated form the pool
* so we can free off all the memory if something goes wrong.
* Build a list of pv_d's structures, allocated
* from mem. We keep track of the first object
* allocated form the pool so we can free off all
* the memory if something goes wrong.
*/
int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
struct pool *mem, struct list *head)
{
struct dev_iter *iter;
struct dev_iter *iter = dev_iter_create(filter);
struct device *dev;
struct disk_list *data = NULL;
struct list *pvdh, *pvdh2;
/* Fast path if we already saw this VG and cached the list of PVs */
if ((pvdh = vgcache_find(vg_name))) {
list_iterate(pvdh2, pvdh) {
dev = list_item(pvdh2, struct pvdev_list)->dev;
if (!(data = read_disk(dev, mem, vg_name)))
break;
_add_pv_to_list(head, data);
}
/* Did we find the whole VG? */
if (!vg_name || !*vg_name ||
(data && *data->pvd.vg_name &&
list_size(head) == data->vgd.pv_cur))
return 1;
/* Something changed. Remove the hints. */
list_init(head);
vgcache_del(vg_name);
}
if (!(iter = dev_iter_create(filter))) {
log_error("read_pvs_in_vg: dev_iter_create failed");
return 0;
}
/* Otherwise do a complete scan */
for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
if ((data = read_disk(dev, mem, vg_name))) {
_add_pv_to_list(head, data);
}
if ((data = read_disk(dev, mem, vg_name)))
list_add(head, &data->list);
}
dev_iter_destroy(iter);
@@ -448,10 +353,11 @@ int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
return 1;
}
static int _write_vgd(struct disk_list *data)
{
struct vg_disk *vgd = &data->vgd;
ulong pos = data->pvd.vg_on_disk.base;
unsigned long pos = data->pvd.vg_on_disk.base;
_xlate_vgd(vgd);
if (dev_write(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
@@ -500,13 +406,13 @@ static int _write_lvd(struct device *dev, ulong pos, struct lv_disk *disk)
static int _write_lvs(struct disk_list *data)
{
struct list *lvh;
ulong pos;
unsigned long pos;
pos = data->pvd.lv_on_disk.base;
if (!dev_zero(data->dev, pos, data->pvd.lv_on_disk.size)) {
log_error("Couldn't zero lv area on device '%s'",
dev_name(data->dev));
dev_name(data->dev));
return 0;
}
@@ -526,7 +432,7 @@ static int _write_extents(struct disk_list *data)
{
size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
struct pe_disk *extents = data->extents;
ulong pos = data->pvd.pe_on_disk.base;
unsigned long pos = data->pvd.pe_on_disk.base;
_xlate_extents(extents, data->pvd.pe_total);
if (dev_write(data->dev, pos, len, extents) != len)
@@ -539,34 +445,14 @@ static int _write_extents(struct disk_list *data)
static int _write_pvd(struct disk_list *data)
{
char *buf;
ulong pos = data->pvd.pv_on_disk.base;
ulong size = data->pvd.pv_on_disk.size;
struct pv_disk *disk = &data->pvd;
if(size < sizeof(struct pv_disk)) {
log_error("Invalid PV structure size.");
return 0;
}
/* Make sure that the gap between the PV structure and
the next one is zeroed in order to make non LVM tools
happy (idea from AED) */
buf = dbg_malloc(size);
if(!buf) {
log_err("Couldn't allocate temporary PV buffer.");
return 0;
}
memset(buf, 0, size);
memcpy(buf, &data->pvd, sizeof(struct pv_disk));
_xlate_pvd((struct pv_disk *)buf);
if (dev_write(data->dev, pos, size, buf) != size) {
dbg_free(buf);
_xlate_pvd(disk);
if (dev_write(data->dev, 0, sizeof(*disk), disk) != sizeof(*disk))
fail;
}
dbg_free(buf);
_xlate_pvd(disk);
return 1;
}
@@ -582,18 +468,11 @@ static int __write_all_pvd(struct disk_list *data)
return 0;
}
vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev);
/*
* Stop here for orphan pv's.
*/
if (data->pvd.vg_name[0] == '\0') {
if (!test_mode())
vgcache_add(data->pvd.vg_name, NULL, data->dev);
if (data->pvd.vg_name[0] == '\0')
return 1;
}
if (!test_mode())
vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev);
if (!_write_vgd(data)) {
log_error("Failed to write VG data to %s", pv_name);
@@ -638,6 +517,7 @@ static int _write_all_pvd(struct disk_list *data)
return r;
}
/*
* Writes all the given pv's to disk. Does very
* little sanity checking, so make sure correct
@@ -653,8 +533,7 @@ int write_disks(struct list *pvs)
if (!(_write_all_pvd(dl)))
fail;
log_very_verbose("Successfully wrote data to %s",
dev_name(dl->dev));
log_debug("Successfully wrote data to %s", dev_name(dl->dev));
}
return 1;

View File

@@ -18,13 +18,9 @@
#define MAX_LV 256
#define MAX_VG 99
#define MAX_PV_SIZE ((uint32_t) -1) /* 2TB in sectors - 1 */
#define MIN_PE_SIZE (8192L / SECTOR_SIZE) /* 8 KB in sectors */
#define MAX_PE_SIZE (16L * 1024L * 1024L / SECTOR_SIZE * 1024)
#define MIN_PE_SIZE ( 8192L / SECTOR_SIZE) /* 8 KB in sectors */
#define MAX_PE_SIZE ( 16L * 1024L * 1024L / SECTOR_SIZE * 1024)
#define PE_SIZE_PV_SIZE_REL 5 /* PV size must be at least 5 times PE size */
#define MAX_LE_TOTAL 65534 /* 2^16 - 2 */
#define MAX_PE_TOTAL ((uint32_t) -2)
#define UNMAPPED_EXTENT 0
@@ -41,7 +37,6 @@
/* logical volume */
#define LV_ACTIVE 0x01 /* lv_status */
#define LV_SPINDOWN 0x02 /* " */
#define LV_PERSISTENT_MINOR 0x04 /* " */
#define LV_READ 0x01 /* lv_access */
#define LV_WRITE 0x02 /* " */
@@ -57,9 +52,6 @@
#define PV_ACTIVE 0x01 /* pv_status */
#define PV_ALLOCATABLE 0x02 /* pv_allocatable */
#define EXPORTED_TAG "PV_EXP" /* Identifier for exported PV */
#define IMPORTED_TAG "PV_IMP" /* Identifier for imported PV */
struct data_area {
uint32_t base;
@@ -186,8 +178,6 @@ int calculate_extent_count(struct physical_volume *pv);
* Low level io routines which read/write
* disk_lists.
*/
int read_pvd(struct device *dev, struct pv_disk *pvd);
struct disk_list *read_disk(struct device *dev, struct pool *mem,
const char *vg_name);
@@ -202,14 +192,11 @@ int write_disks(struct list *pvds);
* core structures.
*/
int import_pv(struct pool *mem, struct device *dev,
struct volume_group *vg,
struct physical_volume *pv, struct pv_disk *pvd);
int export_pv(struct pool *mem, struct volume_group *vg,
struct pv_disk *pvd, struct physical_volume *pv);
int export_pv(struct pv_disk *pvd, struct physical_volume *pv);
int import_vg(struct pool *mem,
struct volume_group *vg, struct disk_list *dl,
int partial);
struct volume_group *vg, struct disk_list *dl);
int export_vg(struct vg_disk *vgd, struct volume_group *vg);
int import_lv(struct pool *mem, struct logical_volume *lv,
@@ -217,23 +204,20 @@ int import_lv(struct pool *mem, struct logical_volume *lv,
void export_lv(struct lv_disk *lvd, struct volume_group *vg,
struct logical_volume *lv, const char *dev_dir);
int import_extents(struct pool *mem, struct volume_group *vg,
int import_extents(struct pool *mem, struct volume_group *vg,
struct list *pvds);
int export_extents(struct disk_list *dl, int lv_num,
struct logical_volume *lv,
struct physical_volume *pv);
int import_pvs(struct pool *mem, struct volume_group *vg,
struct list *pvds, struct list *results, int *count);
int import_pvs(struct pool *mem, struct list *pvds,
struct list *results, int *count);
int import_lvs(struct pool *mem, struct volume_group *vg,
struct list *pvds);
int export_lvs(struct disk_list *dl, struct volume_group *vg,
struct physical_volume *pv, const char *dev_dir);
int import_snapshots(struct pool *mem, struct volume_group *vg,
struct list *pvds);
int export_uuids(struct disk_list *dl, struct volume_group *vg);
void export_numbers(struct list *pvds, struct volume_group *vg);

View File

@@ -8,88 +8,48 @@
#include "dbg_malloc.h"
#include "pool.h"
#include "hash.h"
#include "limits.h"
#include "list.h"
#include "log.h"
#include "display.h"
#include "toolcontext.h"
/* VG consistency checks */
static int _check_vgs(struct list *pvs, int *partial)
static int _check_vgs(struct list *pvs)
{
struct list *pvh, *t;
struct list *pvh;
struct disk_list *dl = NULL;
struct disk_list *first = NULL;
int pv_count = 0;
int exported = -1;
*partial = 0;
/*
* If there are exported and unexported PVs, ignore exported ones.
* This means an active VG won't be affected if disks are inserted
* bearing an exported VG with the same name.
*/
/* check all the vg's are the same */
list_iterate(pvh, pvs) {
dl = list_item(pvh, struct disk_list);
if (exported < 0) {
exported = dl->pvd.pv_status & VG_EXPORTED;
continue;
}
if (exported != (dl->pvd.pv_status & VG_EXPORTED)) {
/* Remove exported PVs */
list_iterate_safe(pvh, t, pvs) {
dl = list_item(pvh, struct disk_list);
if (dl->pvd.pv_status & VG_EXPORTED)
list_del(pvh);
}
break;
}
}
/* Remove any PVs with VG structs that differ from the first */
list_iterate_safe(pvh, t, pvs) {
dl = list_item(pvh, struct disk_list);
if (!first)
first = dl;
else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) {
log_error("VG data differs between PVs %s and %s",
dev_name(first->dev), dev_name(dl->dev));
list_del(pvh);
if (partial_mode()) {
*partial = 1;
continue;
}
log_err("VG data differs between PVs %s and %s",
dev_name(first->dev), dev_name(dl->dev));
return 0;
}
pv_count++;
}
/* On entry to fn, list known to be non-empty */
if (pv_count != dl->vgd.pv_cur) {
log_error("%d PV(s) found for VG %s: expected %d",
pv_count, dl->pvd.vg_name, dl->vgd.pv_cur);
if (!partial_mode())
return 0;
*partial = 1;
if (!(pv_count == dl->vgd.pv_cur)) {
log_error("Only %d out of %d PV(s) found for VG %s",
pv_count, dl->vgd.pv_cur, dl->pvd.vg_name);
return 0;
}
return 1;
}
static struct volume_group *_build_vg(struct cmd_context *cmd,
struct list *pvs)
static struct volume_group *_build_vg(struct pool *mem, struct list *pvs)
{
struct pool *mem = cmd->mem;
struct volume_group *vg = pool_alloc(mem, sizeof(*vg));
struct disk_list *dl;
int partial;
if (!vg)
goto bad;
@@ -97,22 +57,20 @@ static struct volume_group *_build_vg(struct cmd_context *cmd,
if (list_empty(pvs))
goto bad;
memset(vg, 0, sizeof(*vg));
vg->cmd = cmd;
list_init(&vg->pvs);
list_init(&vg->lvs);
list_init(&vg->snapshots);
if (!_check_vgs(pvs, &partial))
goto bad;
dl = list_item(pvs->n, struct disk_list);
if (!import_vg(mem, vg, dl, partial))
memset(vg, 0, sizeof(*vg));
list_init(&vg->pvs);
list_init(&vg->lvs);
if (!_check_vgs(pvs))
goto bad;
if (!import_pvs(mem, vg, pvs, &vg->pvs, &vg->pv_count))
if (!import_vg(mem, vg, dl))
goto bad;
if (!import_pvs(mem, pvs, &vg->pvs, &vg->pv_count))
goto bad;
if (!import_lvs(mem, vg, pvs))
@@ -121,9 +79,6 @@ static struct volume_group *_build_vg(struct cmd_context *cmd,
if (!import_extents(mem, vg, pvs))
goto bad;
if (!import_snapshots(mem, vg, pvs))
goto bad;
return vg;
bad:
@@ -153,11 +108,13 @@ static struct volume_group *_vg_read(struct format_instance *fi,
goto bad;
}
if (!(vg = _build_vg(fi->cmd, &pvs))) {
if (!(vg = _build_vg(fi->cmd->mem, &pvs))) {
stack;
goto bad;
}
vg->cmd = fi->cmd;
bad:
pool_destroy(mem);
return vg;
@@ -180,7 +137,7 @@ static struct disk_list *_flatten_pv(struct pool *mem, struct volume_group *vg,
list_init(&dl->uuids);
list_init(&dl->lvds);
if (!export_pv(mem, vg, &dl->pvd, pv) ||
if (!export_pv(&dl->pvd, pv) ||
!export_vg(&dl->vgd, vg) ||
!export_uuids(dl, vg) ||
!export_lvs(dl, vg, pv, dev_dir) ||
@@ -204,7 +161,7 @@ static int _flatten_vg(struct pool *mem, struct volume_group *vg,
list_iterate(pvh, &vg->pvs) {
pvl = list_item(pvh, struct pv_list);
if (!(data = _flatten_pv(mem, vg, pvl->pv, dev_dir))) {
if (!(data = _flatten_pv(mem, vg, &pvl->pv, dev_dir))) {
stack;
return 0;
}
@@ -234,12 +191,6 @@ static int _vg_write(struct format_instance *fi, struct volume_group *vg)
return 0;
}
if (vg->status & PARTIAL_VG) {
log_error("Cannot change metadata for partial volume group %s",
vg->name);
return 0;
}
list_init(&pvds);
r = (_flatten_vg(mem, vg, &pvds, fi->cmd->dev_dir, fi->cmd->filter) &&
@@ -278,7 +229,7 @@ static struct physical_volume *_pv_read(struct format_instance *fi,
goto out;
}
if (!import_pv(fi->cmd->mem, dl->dev, NULL, pv, &dl->pvd)) {
if (!import_pv(fi->cmd->mem, dl->dev, pv, &dl->pvd)) {
stack;
pool_free(fi->cmd->mem, pv);
pv = NULL;
@@ -314,7 +265,7 @@ static struct list *_get_pvs(struct format_instance *fi)
goto bad;
}
if (!import_pvs(fi->cmd->mem, NULL, &pvs, results, &count)) {
if (!import_pvs(fi->cmd->mem, &pvs, results, &count)) {
stack;
goto bad;
}
@@ -363,8 +314,8 @@ static struct list *_get_vgs(struct format_instance *fi)
list_iterate(pvh, pvs) {
struct pv_list *pvl = list_item(pvh, struct pv_list);
if (!(*pvl->pv->vg_name) ||
_find_vg_name(names, pvl->pv->vg_name))
if (!(*pvl->pv.vg_name) ||
_find_vg_name(names, pvl->pv.vg_name))
continue;
if (!(nl = pool_alloc(fi->cmd->mem, sizeof(*nl)))) {
@@ -372,8 +323,7 @@ static struct list *_get_vgs(struct format_instance *fi)
goto bad;
}
if (!(nl->name = pool_strdup(fi->cmd->mem,
pvl->pv->vg_name))) {
if (!(nl->name = pool_strdup(fi->cmd->mem, pvl->pv.vg_name))) {
stack;
goto bad;
}
@@ -394,19 +344,6 @@ static struct list *_get_vgs(struct format_instance *fi)
static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
struct volume_group *vg)
{
/* setup operations for the PV structure */
if (pv->size > MAX_PV_SIZE)
pv->size--;
if (pv->size > MAX_PV_SIZE) {
/* FIXME Limit hardcoded */
log_error("Physical volumes cannot be bigger than 2TB");
return 0;
}
/* Nothing more to do if pe_size isn't known */
if (!vg)
return 1;
/*
* This works out pe_start and pe_count.
*/
@@ -418,52 +355,6 @@ static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
return 1;
}
static int _find_free_lvnum(struct logical_volume *lv)
{
int lvnum_used[MAX_LV];
int i = 0;
struct list *lvh;
struct lv_list *lvl;
memset(&lvnum_used, 0, sizeof(lvnum_used));
list_iterate(lvh, &lv->vg->lvs) {
lvl = list_item(lvh, struct lv_list);
lvnum_used[lvnum_from_lvid(&lvl->lv->lvid)] = 1;
}
while (lvnum_used[i])
i++;
return i;
}
/*
* Validate/populate LV structure for format1.
* Supplied LV structure can be for a new LV or for an already-existing one.
*/
static int _lv_setup(struct format_instance *fi, struct logical_volume *lv)
{
uint64_t max_size = UINT_MAX;
if (!*lv->lvid.s)
lvid_from_lvnum(&lv->lvid, &lv->vg->id, _find_free_lvnum(lv));
if (lv->le_count > MAX_LE_TOTAL) {
log_error("logical volumes cannot contain more than "
"%d extents.", MAX_LE_TOTAL);
return 0;
}
if (lv->size > max_size) {
char *dummy = display_size(max_size, SIZE_SHORT);
log_error("logical volumes cannot be larger than %s", dummy);
dbg_free(dummy);
return 0;
}
return 1;
}
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
{
struct pool *mem;
@@ -480,12 +371,14 @@ static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
/* Ensure any residual PE structure is gone */
pv->pe_size = pv->pe_count = pv->pe_start = 0;
pv->status &= ~ALLOCATED_PV;
if (!(mem = pool_create(1024))) {
stack;
return 0;
}
if (!(dl = pool_alloc(mem, sizeof(*dl)))) {
stack;
goto bad;
@@ -493,16 +386,11 @@ static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
dl->mem = mem;
dl->dev = pv->dev;
if (!export_pv(mem, NULL, &dl->pvd, pv)) {
if (!export_pv(&dl->pvd, pv)) {
stack;
goto bad;
}
/* must be set to be able to zero gap after PV structure in
dev_write in order to make other disk tools happy */
dl->pvd.pv_on_disk.base = METADATA_BASE;
dl->pvd.pv_on_disk.size = PV_SIZE;
list_add(&pvs, &dl->list);
if (!write_disks(&pvs)) {
stack;
@@ -567,7 +455,6 @@ static struct format_handler _format1_ops = {
pv_read: _pv_read,
pv_setup: _pv_setup,
pv_write: _pv_write,
lv_setup: _lv_setup,
vg_read: _vg_read,
vg_setup: _vg_setup,
vg_write: _vg_write,

View File

@@ -12,11 +12,9 @@
#include "hash.h"
#include "list.h"
#include "log.h"
#include "lvm-string.h"
#include <time.h>
#include <sys/utsname.h>
#include <linux/kdev_t.h>
static int _check_vg_name(const char *name)
{
@@ -38,8 +36,29 @@ static char *_create_lv_name(struct pool *mem, const char *full_name)
return pool_strdup(mem, ptr);
}
static int _fill_lv_array(struct logical_volume **lvs,
struct volume_group *vg, struct disk_list *dl)
{
struct list *lvh;
struct logical_volume *lv;
int i = 0;
list_iterate(lvh, &dl->lvds) {
struct lvd_list *ll = list_item(lvh, struct lvd_list);
if (!(lv = find_lv(vg, ll->lvd.lv_name))) {
stack;
return 0;
}
lvs[i] = lv;
i++;
}
return 1;
}
int import_pv(struct pool *mem, struct device *dev,
struct volume_group *vg,
struct physical_volume *pv, struct pv_disk *pvd)
{
memset(pv, 0, sizeof(*pv));
@@ -51,36 +70,21 @@ int import_pv(struct pool *mem, struct device *dev,
return 0;
}
/* Store system_id from first PV if PV belongs to a VG */
if (vg && !*vg->system_id)
strncpy(vg->system_id, pvd->system_id, NAME_LEN);
if (vg &&
strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id)))
log_very_verbose("System ID %s on %s differs from %s for "
"volume group", pvd->system_id,
dev_name(pv->dev), vg->system_id);
/*
* If exported, we still need to flag in pv->status too because
* we don't always have a struct volume_group when we need this.
*/
if (pvd->pv_status & VG_EXPORTED)
pv->status |= EXPORTED_VG;
if (pvd->pv_status & PV_ACTIVE)
pv->status |= ACTIVE;
if (pvd->pv_allocatable)
pv->status |= ALLOCATABLE_PV;
pv->status |= ALLOCATED_PV;
pv->size = pvd->pv_size;
pv->pe_size = pvd->pe_size;
pv->pe_start = pvd->pe_start;
pv->pe_count = pvd->pe_total;
pv->pe_allocated = pvd->pe_allocated;
return 1;
}
int _system_id(char *s, const char *prefix)
static int _system_id(char *system_id)
{
struct utsname uts;
@@ -89,17 +93,11 @@ int _system_id(char *s, const char *prefix)
return 0;
}
if (lvm_snprintf(s, NAME_LEN, "%s%s%lu",
prefix, uts.nodename, time(NULL)) < 0) {
log_error("Generated system_id too long");
return 0;
}
sprintf(system_id, "%s%lu", uts.nodename, time(NULL));
return 1;
}
int export_pv(struct pool *mem, struct volume_group *vg,
struct pv_disk *pvd, struct physical_volume *pv)
int export_pv(struct pv_disk *pvd, struct physical_volume *pv)
{
memset(pvd, 0, sizeof(*pvd));
@@ -119,55 +117,12 @@ int export_pv(struct pool *mem, struct volume_group *vg,
if (pv->vg_name)
strncpy(pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name));
/* Preserve existing system_id if it exists */
if (vg && *vg->system_id)
strncpy(pvd->system_id, vg->system_id, sizeof(pvd->system_id));
/* Is VG already exported or being exported? */
if (vg && (vg->status & EXPORTED_VG)) {
/* Does system_id need setting? */
if (!*vg->system_id ||
strncmp(vg->system_id, EXPORTED_TAG,
sizeof(EXPORTED_TAG) - 1)) {
if (!_system_id(pvd->system_id, EXPORTED_TAG)) {
stack;
return 0;
}
}
if (strlen(pvd->vg_name) + sizeof(EXPORTED_TAG) >
sizeof(pvd->vg_name)) {
log_error("Volume group name %s too long to export",
pvd->vg_name);
return 0;
}
strcat(pvd->vg_name, EXPORTED_TAG);
}
/* Is VG being imported? */
if (vg && !(vg->status & EXPORTED_VG) && *vg->system_id &&
!strncmp(vg->system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) {
if (!_system_id(pvd->system_id, IMPORTED_TAG)) {
stack;
return 0;
}
}
/* Generate system_id if PV is in VG */
if (!pvd->system_id || !*pvd->system_id)
if (!_system_id(pvd->system_id, "")) {
stack;
return 0;
}
/* Update internal system_id if we changed it */
if (vg &&
(!*vg->system_id ||
strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id))))
strncpy(vg->system_id, pvd->system_id, NAME_LEN);
//pvd->pv_major = MAJOR(pv->dev);
if (pv->status & ALLOCATABLE_PV)
if (pv->status & ACTIVE)
pvd->pv_status |= PV_ACTIVE;
if (pv->status & ALLOCATED_PV)
pvd->pv_allocatable = PV_ALLOCATABLE;
pvd->pv_size = pv->size;
@@ -177,12 +132,16 @@ int export_pv(struct pool *mem, struct volume_group *vg,
pvd->pe_allocated = pv->pe_allocated;
pvd->pe_start = pv->pe_start;
if (!_system_id(pvd->system_id)) {
stack;
return 0;
}
return 1;
}
int import_vg(struct pool *mem,
struct volume_group *vg, struct disk_list *dl,
int partial)
struct volume_group *vg, struct disk_list *dl)
{
struct vg_disk *vgd = &dl->vgd;
memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
@@ -197,23 +156,19 @@ int import_vg(struct pool *mem,
return 0;
}
if (!(vg->system_id = pool_alloc(mem, NAME_LEN))) {
stack;
return 0;
}
*vg->system_id = '\0';
if (vgd->vg_status & VG_ACTIVE)
vg->status |= ACTIVE;
if (vgd->vg_status & VG_EXPORTED)
vg->status |= EXPORTED_VG;
if (vgd->vg_status & VG_EXTENDABLE)
vg->status |= RESIZEABLE_VG;
vg->status |= EXTENDABLE_VG;
if (partial || (vgd->vg_access & VG_READ))
if (vgd->vg_access & VG_READ)
vg->status |= LVM_READ;
if (!partial && (vgd->vg_access & VG_WRITE))
if (vgd->vg_access & VG_WRITE)
vg->status |= LVM_WRITE;
if (vgd->vg_access & VG_CLUSTERED)
@@ -227,10 +182,6 @@ int import_vg(struct pool *mem,
vg->free_count = vgd->pe_total - vgd->pe_allocated;
vg->max_lv = vgd->lv_max;
vg->max_pv = vgd->pv_max;
if (partial)
vg->status |= PARTIAL_VG;
return 1;
}
@@ -251,10 +202,13 @@ int export_vg(struct vg_disk *vgd, struct volume_group *vg)
if (vg->status & SHARED)
vgd->vg_access |= VG_SHARED;
if (vg->status & ACTIVE)
vgd->vg_status |= VG_ACTIVE;
if (vg->status & EXPORTED_VG)
vgd->vg_status |= VG_EXPORTED;
if (vg->status & RESIZEABLE_VG)
if (vg->status & EXTENDABLE_VG)
vgd->vg_status |= VG_EXTENDABLE;
vgd->lv_max = vg->max_lv;
@@ -272,28 +226,31 @@ int export_vg(struct vg_disk *vgd, struct volume_group *vg)
int import_lv(struct pool *mem, struct logical_volume *lv, struct lv_disk *lvd)
{
lvid_from_lvnum(&lv->lvid, &lv->vg->id, lvd->lv_number);
int len;
memset(&lv->id, 0, sizeof(lv->id));
if (!(lv->name = _create_lv_name(mem, lvd->lv_name))) {
stack;
return 0;
}
if (lvd->lv_status & LV_ACTIVE)
lv->status |= ACTIVE;
if (lvd->lv_status & LV_SPINDOWN)
lv->status |= SPINDOWN_LV;
if (lvd->lv_status & LV_PERSISTENT_MINOR) {
lv->status |= FIXED_MINOR;
lv->minor = MINOR(lvd->lv_dev);
} else
lv->minor = -1;
if (lvd->lv_access & LV_READ)
lv->status |= LVM_READ;
if (lvd->lv_access & LV_WRITE)
lv->status |= LVM_WRITE;
if (lvd->lv_access & LV_SNAPSHOT)
lv->status |= SNAPSHOT;
if (lvd->lv_access & LV_SNAPSHOT_ORG)
lv->status |= SNAPSHOT_ORG;
if (lvd->lv_badblock)
lv->status |= BADBLOCK_ON;
@@ -306,10 +263,17 @@ int import_lv(struct pool *mem, struct logical_volume *lv, struct lv_disk *lvd)
lv->status |= ALLOC_SIMPLE;
lv->read_ahead = lvd->lv_read_ahead;
lv->stripes = lvd->lv_stripes;
lv->size = lvd->lv_size;
lv->le_count = lvd->lv_allocated_le;
list_init(&lv->segments);
len = sizeof(struct pe_specifier) * lv->le_count;
if (!(lv->map = pool_alloc(mem, len))) {
stack;
return 0;
}
memset(lv->map, 0, len);
return 1;
}
@@ -331,19 +295,20 @@ void export_lv(struct lv_disk *lvd, struct volume_group *vg,
if (lv->status & LVM_WRITE)
lvd->lv_access |= LV_WRITE;
if (lv->status & SNAPSHOT)
lvd->lv_access |= LV_SNAPSHOT;
if (lv->status & SNAPSHOT_ORG)
lvd->lv_access |= LV_SNAPSHOT_ORG;
if (lv->status & ACTIVE)
lvd->lv_status |= LV_ACTIVE;
if (lv->status & SPINDOWN_LV)
lvd->lv_status |= LV_SPINDOWN;
if (lv->status & FIXED_MINOR) {
lvd->lv_status |= LV_PERSISTENT_MINOR;
lvd->lv_dev = MKDEV(0, lv->minor);
}
lvd->lv_read_ahead = lv->read_ahead;
lvd->lv_stripes = list_item(lv->segments.n,
struct stripe_segment)->stripes;
lvd->lv_stripesize = list_item(lv->segments.n,
struct stripe_segment)->stripe_size;
lvd->lv_stripes = lv->stripes;
lvd->lv_size = lv->size;
lvd->lv_allocated_le = lv->le_count;
@@ -358,27 +323,50 @@ void export_lv(struct lv_disk *lvd, struct volume_group *vg,
lvd->lv_allocation |= LV_CONTIGUOUS;
}
int export_extents(struct disk_list *dl, int lv_num,
struct logical_volume *lv,
struct physical_volume *pv)
int import_extents(struct pool *mem, struct volume_group *vg, struct list *pvds)
{
struct list *segh;
struct pe_disk *ped;
struct stripe_segment *seg;
uint32_t pe, s;
struct disk_list *dl;
struct logical_volume *lv, *lvs[MAX_LV];
struct physical_volume *pv;
struct pe_disk *e;
int i;
uint32_t lv_num, le;
struct list *pvdh;
list_iterate (segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
pv = _find_pv(vg, dl->dev);
e = dl->extents;
for (s = 0; s < seg->stripes; s++) {
if (seg->area[s].pv != pv)
continue; /* not our pv */
/* build an array of lv's for this pv */
if (!_fill_lv_array(lvs, vg, dl)) {
stack;
return 0;
}
for (pe = 0; pe < (seg->len / seg->stripes); pe++) {
ped = &dl->extents[pe + seg->area[s].pe];
ped->lv_num = lv_num;
ped->le_num = (seg->le / seg->stripes) + pe +
s * (lv->le_count / seg->stripes);
for (i = 0; i < dl->pvd.pe_total; i++) {
lv_num = e[i].lv_num;
if (lv_num == UNMAPPED_EXTENT)
continue;
else if(lv_num > dl->pvd.lv_cur) {
log_err("invalid lv in extent map\n");
return 0;
} else {
lv_num--;
lv = lvs[lv_num];
le = e[i].le_num;
if (le >= lv->le_count) {
log_err("logical extent number "
"out of bounds");
return 0;
}
lv->map[le].pv = pv;
lv->map[le].pe = i;
}
}
}
@@ -386,8 +374,25 @@ int export_extents(struct disk_list *dl, int lv_num,
return 1;
}
int import_pvs(struct pool *mem, struct volume_group *vg,
struct list *pvds, struct list *results, int *count)
int export_extents(struct disk_list *dl, int lv_num,
struct logical_volume *lv,
struct physical_volume *pv)
{
struct pe_disk *ped;
int le;
for (le = 0; le < lv->le_count; le++) {
if (lv->map[le].pv == pv) {
ped = &dl->extents[lv->map[le].pe];
ped->lv_num = lv_num;
ped->le_num = le;
}
}
return 1;
}
int import_pvs(struct pool *mem, struct list *pvds,
struct list *results, int *count)
{
struct list *pvdh;
struct disk_list *dl;
@@ -395,16 +400,15 @@ int import_pvs(struct pool *mem, struct volume_group *vg,
*count = 0;
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
pvl = pool_alloc(mem, sizeof(*pvl));
if (!(pvl = pool_alloc(mem, sizeof(*pvl))) ||
!(pvl->pv = pool_alloc(mem, sizeof(*pvl->pv)))) {
if (!pvl) {
stack;
return 0;
}
if (!import_pv(mem, dl->dev, vg, pvl->pv, &dl->pvd)) {
if (!import_pv(mem, dl->dev, &pvl->pv, &dl->pvd)) {
stack;
return 0;
}
@@ -420,23 +424,22 @@ static struct logical_volume *_add_lv(struct pool *mem,
struct volume_group *vg,
struct lv_disk *lvd)
{
struct lv_list *ll;
struct lv_list *ll = pool_zalloc(mem, sizeof(*ll));
struct logical_volume *lv;
if (!(ll = pool_zalloc(mem, sizeof(*ll))) ||
!(ll->lv = pool_zalloc(mem, sizeof(*ll->lv)))) {
if (!ll) {
stack;
return NULL;
}
lv = ll->lv;
lv->vg = vg;
lv = &ll->lv;
if (!import_lv(mem, lv, lvd)) {
if (!import_lv(mem, &ll->lv, lvd)) {
stack;
return NULL;
}
list_add(&vg->lvs, &ll->list);
lv->vg = vg;
vg->lv_count++;
return lv;
@@ -467,21 +470,13 @@ int import_lvs(struct pool *mem, struct volume_group *vg,
return 1;
}
/* FIXME: tidy */
int export_lvs(struct disk_list *dl, struct volume_group *vg,
struct physical_volume *pv, const char *dev_dir)
{
int r = 0;
struct list *lvh, *sh;
struct list *lvh;
struct lv_list *ll;
struct lvd_list *lvdl;
int lv_num, len;
struct hash_table *lvd_hash;
if (!(lvd_hash = hash_create(32))) {
stack;
return 0;
}
int lv_num = 0, len;
/*
* setup the pv's extents array
@@ -489,147 +484,32 @@ int export_lvs(struct disk_list *dl, struct volume_group *vg,
len = sizeof(struct pe_disk) * dl->pvd.pe_total;
if (!(dl->extents = pool_alloc(dl->mem, len))) {
stack;
goto out;
return 0;
}
memset(dl->extents, 0, len);
list_iterate (lvh, &vg->lvs) {
list_iterate(lvh, &vg->lvs) {
ll = list_item(lvh, struct lv_list);
if (!(lvdl = pool_alloc(dl->mem, sizeof(*lvdl)))) {
stack;
goto out;
return 0;
}
export_lv(&lvdl->lvd, vg, ll->lv, dev_dir);
lv_num = lvnum_from_lvid(&ll->lv->lvid);
export_lv(&lvdl->lvd, vg, &ll->lv, dev_dir);
lvdl->lvd.lv_number = lv_num;
if (!hash_insert(lvd_hash, ll->lv->name, &lvdl->lvd)) {
if (!export_extents(dl, lv_num + 1, &ll->lv, pv)) {
stack;
goto out;
}
if (!export_extents(dl, lv_num + 1, ll->lv, pv)) {
stack;
goto out;
return 0;
}
list_add(&dl->lvds, &lvdl->list);
dl->pvd.lv_cur++;
lv_num++;
}
/*
* Now we need to run through the snapshots, exporting
* the SNAPSHOT_ORG flags etc.
*/
list_iterate (sh, &vg->snapshots) {
struct lv_disk *org, *cow;
struct snapshot *s = list_item(sh,
struct snapshot_list)->snapshot;
if (!(org = hash_lookup(lvd_hash, s->origin->name))) {
log_err("Couldn't find snapshot origin '%s'.",
s->origin->name);
goto out;
}
if (!(cow = hash_lookup(lvd_hash, s->cow->name))) {
log_err("Couldn't find snapshot cow store '%s'.",
s->cow->name);
goto out;
}
org->lv_access |= LV_SNAPSHOT_ORG;
cow->lv_access |= LV_SNAPSHOT;
cow->lv_snapshot_minor = org->lv_number;
cow->lv_chunk_size = s->chunk_size;
}
r = 1;
out:
hash_destroy(lvd_hash);
return r;
}
/*
* FIXME: More inefficient code.
*/
int import_snapshots(struct pool *mem, struct volume_group *vg,
struct list *pvds)
{
struct logical_volume *lvs[MAX_LV];
struct list *pvdh, *lvdh;
struct disk_list *dl;
struct lv_disk *lvd;
int lvnum;
struct logical_volume *org, *cow;
/* build an index of lv numbers */
memset(lvs, 0, sizeof(lvs));
list_iterate (pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
list_iterate (lvdh, &dl->lvds) {
lvd = &(list_item(lvdh, struct lvd_list)->lvd);
lvnum = lvd->lv_number;
if (lvnum > MAX_LV) {
log_err("Logical volume number "
"out of bounds.");
return 0;
}
if (!lvs[lvnum] &&
!(lvs[lvnum] = find_lv(vg, lvd->lv_name))) {
log_err("Couldn't find logical volume '%s'.",
lvd->lv_name);
return 0;
}
}
}
/*
* Now iterate through yet again adding the snapshots.
*/
list_iterate (pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
list_iterate (lvdh, &dl->lvds) {
lvd = &(list_item(lvdh, struct lvd_list)->lvd);
if (!(lvd->lv_access & LV_SNAPSHOT))
continue;
lvnum = lvd->lv_number;
cow = lvs[lvnum];
if (!(org = lvs[lvd->lv_snapshot_minor])) {
log_err("Couldn't find origin logical volume "
"for snapshot '%s'.", lvd->lv_name);
return 0;
}
/* we may have already added this snapshot */
if (lv_is_cow(cow))
continue;
/* insert the snapshot */
if (!vg_add_snapshot(org, cow, 1,
lvd->lv_chunk_size)) {
log_err("Couldn't add snapshot.");
return 0;
}
}
}
return 1;
}
int export_uuids(struct disk_list *dl, struct volume_group *vg)
{
struct uuid_list *ul;
@@ -644,7 +524,7 @@ int export_uuids(struct disk_list *dl, struct volume_group *vg)
}
memset(ul->uuid, 0, sizeof(ul->uuid));
memcpy(ul->uuid, pvl->pv->id.uuid, ID_LEN);
memcpy(ul->uuid, pvl->pv.id.uuid, ID_LEN);
list_add(&dl->uuids, &ul->list);
}
@@ -652,8 +532,9 @@ int export_uuids(struct disk_list *dl, struct volume_group *vg)
}
/*
* This calculates the nasty pv_number field
* used by LVM1.
* This calculates the nasty pv_number and
* lv_number fields used by LVM1. Very
* inefficient code.
*/
void export_numbers(struct list *pvds, struct volume_group *vg)
{

View File

@@ -1,369 +0,0 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "metadata.h"
#include "hash.h"
#include "dbg_malloc.h"
#include "log.h"
#include "pool.h"
#include "disk-rep.h"
/*
* After much thought I have decided it is easier,
* and probably no less efficient, to convert the
* pe->le map to a full le->pe map, and then
* process this to get the segments form that
* we're after. Any code which goes directly from
* the pe->le map to segments would be gladly
* accepted, if it is less complicated than this
* file.
*/
struct pe_specifier {
struct physical_volume *pv;
uint32_t pe;
};
struct lv_map {
struct logical_volume *lv;
uint32_t stripes;
uint32_t stripe_size;
struct pe_specifier *map;
};
static struct hash_table *_create_lv_maps(struct pool *mem,
struct volume_group *vg)
{
struct hash_table *maps = hash_create(32);
struct list *llh;
struct lv_list *ll;
struct lv_map *lvm;
if (!maps) {
log_err("Unable to create hash table for holding "
"extent maps.");
return NULL;
}
list_iterate(llh, &vg->lvs) {
ll = list_item(llh, struct lv_list);
if (!(lvm = pool_alloc(mem, sizeof(*lvm)))) {
stack;
goto bad;
}
lvm->lv = ll->lv;
if (!(lvm->map = pool_zalloc(mem, sizeof(*lvm->map)
* ll->lv->le_count))) {
stack;
goto bad;
}
if (!hash_insert(maps, ll->lv->name, lvm)) {
stack;
goto bad;
}
}
return maps;
bad:
hash_destroy(maps);
return NULL;
}
static int _fill_lv_array(struct lv_map **lvs,
struct hash_table *maps, struct disk_list *dl)
{
struct list *lvh;
struct lv_map *lvm;
memset(lvs, 0, sizeof(*lvs) * MAX_LV);
list_iterate(lvh, &dl->lvds) {
struct lvd_list *ll = list_item(lvh, struct lvd_list);
if (!(lvm = hash_lookup(maps, strrchr(ll->lvd.lv_name, '/')
+ 1))) {
log_err("Physical volume (%s) contains an "
"unknown logical volume (%s).",
dev_name(dl->dev), ll->lvd.lv_name);
return 0;
}
lvm->stripes = ll->lvd.lv_stripes;
lvm->stripe_size = ll->lvd.lv_stripesize;
lvs[ll->lvd.lv_number] = lvm;
}
return 1;
}
static int _fill_maps(struct hash_table *maps, struct volume_group *vg,
struct list *pvds)
{
struct list *pvdh;
struct disk_list *dl;
struct physical_volume *pv;
struct lv_map *lvms[MAX_LV], *lvm;
struct pe_disk *e;
uint32_t i, lv_num, le;
list_iterate(pvdh, pvds) {
dl = list_item(pvdh, struct disk_list);
pv = find_pv(vg, dl->dev);
e = dl->extents;
/* build an array of lv's for this pv */
if (!_fill_lv_array(lvms, maps, dl)) {
stack;
return 0;
}
for (i = 0; i < dl->pvd.pe_total; i++) {
lv_num = e[i].lv_num;
if (lv_num == UNMAPPED_EXTENT)
continue;
else {
lv_num--;
lvm = lvms[lv_num];
if (!lvm) {
log_err("invalid lv in extent map");
return 0;
}
le = e[i].le_num;
if (le >= lvm->lv->le_count) {
log_err("logical extent number "
"out of bounds");
return 0;
}
if (lvm->map[le].pv) {
log_err("logical extent (%u) "
"already mapped.", le);
return 0;
}
lvm->map[le].pv = pv;
lvm->map[le].pe = i;
}
}
}
return 1;
}
static int _check_single_map(struct lv_map *lvm)
{
uint32_t i;
for (i = 0; i < lvm->lv->le_count; i++) {
if (!lvm->map[i].pv) {
log_err("Logical volume (%s) contains an incomplete "
"mapping table.", lvm->lv->name);
return 0;
}
}
return 1;
}
static int _check_maps_are_complete(struct hash_table *maps)
{
struct hash_node *n;
struct lv_map *lvm;
for (n = hash_get_first(maps); n; n = hash_get_next(maps, n)) {
lvm = (struct lv_map *) hash_get_data(maps, n);
if (!_check_single_map(lvm)) {
stack;
return 0;
}
}
return 1;
}
static struct stripe_segment *_alloc_seg(struct pool *mem, uint32_t stripes)
{
struct stripe_segment *seg;
uint32_t len = sizeof(*seg) + (stripes * sizeof(seg->area[0]));
if (!(seg = pool_zalloc(mem, len))) {
stack;
return NULL;
}
return seg;
}
static int _read_linear(struct pool *mem, struct lv_map *lvm)
{
uint32_t le = 0;
struct stripe_segment *seg;
while (le < lvm->lv->le_count) {
seg = _alloc_seg(mem, 1);
seg->lv = lvm->lv;
seg->le = le;
seg->len = 0;
seg->stripe_size = 0;
seg->stripes = 1;
seg->area[0].pv = lvm->map[le].pv;
seg->area[0].pe = lvm->map[le].pe;
do
seg->len++;
while ((lvm->map[le + seg->len].pv == seg->area[0].pv) &&
(lvm->map[le + seg->len].pe == seg->area[0].pe +
seg->len));
le += seg->len;
list_add(&lvm->lv->segments, &seg->list);
}
return 1;
}
static int _check_stripe(struct lv_map *lvm, struct stripe_segment *seg,
uint32_t base_le, uint32_t len)
{
uint32_t le, st;
le = base_le + seg->len;
/*
* Is the next physical extent in every stripe adjacent to the last?
*/
for (st = 0; st < seg->stripes; st++)
if ((lvm->map[le + st * len].pv != seg->area[st].pv) ||
(lvm->map[le + st * len].pe != seg->area[st].pe + seg->len))
return 0;
return 1;
}
static int _read_stripes(struct pool *mem, struct lv_map *lvm)
{
uint32_t st, le = 0, len;
struct stripe_segment *seg;
/*
* Work out overall striped length
*/
if (lvm->lv->le_count % lvm->stripes) {
log_error("Number of stripes (%u) incompatible "
"with logical extent count (%u) for %s",
lvm->stripes, lvm->lv->le_count,
lvm->lv->name);
}
len = lvm->lv->le_count / lvm->stripes;
while (le < len) {
if (!(seg = _alloc_seg(mem, lvm->stripes))) {
stack;
return 0;
}
seg->lv = lvm->lv;
seg->stripe_size = lvm->stripe_size;
seg->stripes = lvm->stripes;
seg->le = seg->stripes * le;
seg->len = 1;
/*
* Set up start positions of each stripe in this segment
*/
for (st = 0; st < seg->stripes; st++) {
seg->area[st].pv = lvm->map[le + st * len].pv;
seg->area[st].pe = lvm->map[le + st * len].pe;
}
/*
* Find how many blocks are contiguous in all stripes
* and so can form part of this segment
*/
while (_check_stripe(lvm, seg, le, len))
seg->len++;
le += seg->len;
seg->len *= seg->stripes;
list_add(&lvm->lv->segments, &seg->list);
}
return 1;
}
static int _build_segments(struct pool *mem, struct lv_map *lvm)
{
return (lvm->stripes > 1 ? _read_stripes(mem, lvm) :
_read_linear(mem, lvm));
}
static int _build_all_segments(struct pool *mem, struct hash_table *maps)
{
struct hash_node *n;
struct lv_map *lvm;
for (n = hash_get_first(maps); n; n = hash_get_next(maps, n)) {
lvm = (struct lv_map *) hash_get_data(maps, n);
if (!_build_segments(mem, lvm)) {
stack;
return 0;
}
}
return 1;
}
int import_extents(struct pool *mem, struct volume_group *vg, struct list *pvds)
{
int r = 0;
struct pool *scratch = pool_create(10 * 1024);
struct hash_table *maps;
if (!scratch) {
stack;
return 0;
}
if (!(maps = _create_lv_maps(scratch, vg))) {
log_err("Couldn't allocate logical volume maps.");
goto out;
}
if (!_fill_maps(maps, vg, pvds)) {
log_err("Couldn't fill logical volume maps.");
goto out;
}
if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG)) {
stack;
goto out;
}
if (!_build_all_segments(mem, maps)) {
log_err("Couldn't build extent segments.");
goto out;
}
r = 1;
out:
if (maps)
hash_destroy(maps);
pool_destroy(scratch);
return r;
}

View File

@@ -66,16 +66,16 @@ static void _calc_simple_layout(struct pv_disk *pvd)
int _check_vg_limits(struct disk_list *dl)
{
if (dl->vgd.lv_max > MAX_LV) {
if (dl->vgd.lv_max >= MAX_LV) {
log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
"for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
"for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
dl->pvd.vg_name);
return 0;
}
if (dl->vgd.pv_max > MAX_PV) {
if (dl->vgd.pv_max >= MAX_PV) {
log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
"for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
"for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
dl->pvd.vg_name);
return 0;
}
@@ -105,8 +105,9 @@ int calculate_layout(struct disk_list *dl)
/*
* It may seem strange to have a struct physical_volume in here,
* but the number of extents that can fit on a disk *is* metadata
* It may seem strange to have a struct
* physical_volume in here, but the number of
* extents that can fit on a disk *is* metadata
* format dependant.
*/
int calculate_extent_count(struct physical_volume *pv)
@@ -120,38 +121,29 @@ int calculate_extent_count(struct physical_volume *pv)
}
/*
* Guess how many extents will fit, bearing in mind that
* one is going to be knocked off at the start of the
* next loop.
* Guess how many extents will fit,
* bearing in mind that one is going to be
* knocked off at the start of the next
* loop.
*/
pvd->pe_total = (pv->size / pv->pe_size);
if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
log_error("Insufficient space for extents on %s",
dev_name(pv->dev));
dbg_free(pvd);
return 0;
}
do {
pvd->pe_total--;
_calc_simple_layout(pvd);
end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size + \
SECTOR_SIZE - 1) / SECTOR_SIZE);
end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size) /
SECTOR_SIZE);
pvd->pe_start = _round_up(end, PE_ALIGN);
} while((pvd->pe_start + (pvd->pe_total * pv->pe_size)) > pv->size);
if (pvd->pe_total > MAX_PE_TOTAL) {
log_error("Metadata extent limit (%u) exceeded for %s - "
"%u required", MAX_PE_TOTAL, dev_name(pv->dev),
pvd->pe_total);
dbg_free(pvd);
return 0;
}
pv->pe_count = pvd->pe_total;
pv->pe_start = pvd->pe_start;
dbg_free(pvd);

View File

@@ -1,145 +0,0 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "lvm1_label.h"
#include "dbg_malloc.h"
#include "disk-rep.h"
#include "log.h"
#include "label.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static void _not_supported(const char *op)
{
log_err("The '%s' operation is not supported for the lvm1 labeller.",
op);
}
static int _can_handle(struct labeller *l, struct device *dev)
{
struct pv_disk pvd;
int r;
if (!dev_open(dev, O_RDONLY)) {
stack;
return 0;
}
r = read_pvd(dev, &pvd);
if (!dev_close(dev))
stack;
return r;
}
static int _write(struct labeller *l,
struct device *dev, struct label *label)
{
_not_supported("write");
return 0;
}
static int _remove(struct labeller *l, struct device *dev)
{
_not_supported("remove");
return 0;
}
static struct label *_to_label(struct pv_disk *pvd)
{
struct label *l;
struct lvm_label_info *info;
if (!(l = dbg_malloc(sizeof(*l)))) {
log_err("Couldn't allocate label.");
return NULL;
}
if (!(info = (struct lvm_label_info *) dbg_strdup(pvd->vg_name))) {
dbg_free(l);
return NULL;
}
memcpy(&l->id, &pvd->pv_uuid, sizeof(l->id));
strcpy(l->volume_type, "lvm");
l->version[0] = 1;
l->version[0] = 0;
l->version[0] = 0;
l->extra_info = info;
return l;
}
static int _read(struct labeller *l, struct device *dev, struct label **label)
{
struct pv_disk pvd;
int r = 0;
if (!dev_open(dev, O_RDONLY)) {
stack;
return 0;
}
r = read_pvd(dev, &pvd);
if (!dev_close(dev))
stack;
if (!r) {
stack;
return 0;
}
/*
* Convert the disk_list into a label structure.
*/
if (!(*label = _to_label(&pvd))) {
stack;
return 0;
}
return 1;
}
static void _destroy_label(struct labeller *l, struct label *label)
{
dbg_free(label->extra_info);
dbg_free(label);
}
static void _destroy(struct labeller *l)
{
dbg_free(l);
}
struct label_ops _lvm1_ops = {
can_handle: _can_handle,
write: _write,
remove: _remove,
read: _read,
verify: _can_handle,
destroy_label: _destroy_label,
destroy: _destroy
};
struct labeller *lvm1_labeller_create(void)
{
struct labeller *l;
if (!(l = dbg_malloc(sizeof(*l)))) {
log_err("Couldn't allocate labeller object.");
return NULL;
}
l->ops = &_lvm1_ops;
l->private = NULL;
return l;
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright (C) 2002 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#ifndef _LVM_LVM1_LABEL_H
#define _LVM_LVM1_LABEL_H
/*
* This is what the 'extra_info' field of the label will point to
* if the label type is lvm1.
*/
struct lvm_label_info {
char volume_group[0];
};
struct labeller *lvm1_labeller_create(void);
#endif

View File

@@ -12,7 +12,7 @@
* FIXME: Quick hack. We can use caching to
* prevent a total re-read, even so vg_number
* causes the tools to check *every* pv. Yuck.
* Put in separate file so it wouldn't contaminate
* Put in seperate file so it wouldn't contaminate
* other code.
*/
int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
@@ -40,8 +40,7 @@ int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
list_iterate(pvh, &all_pvs) {
dl = list_item(pvh, struct disk_list);
if (!*dl->pvd.vg_name ||
!strcmp(dl->pvd.vg_name, candidate_vg))
if (!strcmp(dl->pvd.vg_name, candidate_vg))
continue;
numbers[dl->vgd.vg_number] = 1;

View File

@@ -1,305 +0,0 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "format-text.h"
#include "log.h"
#include "pool.h"
#include "config.h"
#include "hash.h"
#include "import-export.h"
#include "lvm-string.h"
#include "lvm-file.h"
#include "toolcontext.h"
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include <time.h>
/*
* The format instance is given a directory path upon creation.
* Each file in this directory whose name is of the form
* '(.*)_[0-9]*.vg' is a config file (see lib/config.[hc]), which
* contains a description of a single volume group.
*
* The prefix ($1 from the above regex) of the config file gives
* the volume group name.
*
* Backup files that have expired will be removed.
*/
/*
* A list of these is built up for our volume group. Ordered
* with the least recent at the head.
*/
struct archive_file {
struct list list;
char *path;
int index;
};
/*
* Extract vg name and version number from a filename.
*/
static int _split_vg(const char *filename, char *vg, size_t vg_size,
uint32_t *index)
{
int len, vg_len;
char *dot, *underscore;
len = strlen(filename);
if (len < 7)
return 0;
dot = (char *) (filename + len - 3);
if (strcmp(".vg", dot))
return 0;
if (!(underscore = rindex(filename, '_')))
return 0;
if (sscanf(underscore + 1, "%u", index) != 1)
return 0;
vg_len = underscore - filename;
if (vg_len + 1 > vg_size)
return 0;
strncpy(vg, filename, vg_len);
vg[vg_len] = '\0';
return 1;
}
static void _insert_file(struct list *head, struct archive_file *b)
{
struct list *bh;
struct archive_file *bf;
if (list_empty(head)) {
list_add(head, &b->list);
return;
}
/* index increases through list */
list_iterate (bh, head) {
bf = list_item(bh, struct archive_file);
if (bf->index > b->index) {
list_add(&bf->list, &b->list);
return;
}
}
list_add_h(&bf->list, &b->list);
}
static char *_join(struct pool *mem, const char *dir, const char *name)
{
if (!pool_begin_object(mem, 32) ||
!pool_grow_object(mem, dir, strlen(dir)) ||
!pool_grow_object(mem, "/", 1) ||
!pool_grow_object(mem, name, strlen(name)) ||
!pool_grow_object(mem, "\0", 1)) {
stack;
return NULL;
}
return pool_end_object(mem);
}
/*
* Returns a list of archive_files.
*/
static struct list *_scan_archive(struct pool *mem,
const char *vg, const char *dir)
{
int i, count, index;
char vg_name[64], *path;
struct dirent **dirent;
struct archive_file *af;
struct list *results;
if (!(results = pool_alloc(mem, sizeof(*results)))) {
stack;
return NULL;
}
list_init(results);
if ((count = scandir(dir, &dirent, NULL, alphasort)) < 0) {
log_err("Couldn't scan archive directory.");
return 0;
}
for (i = 0; i < count; i++) {
/* ignore dot files */
if (dirent[i]->d_name[0] == '.')
continue;
/* check the name is the correct format */
if (!_split_vg(dirent[i]->d_name, vg_name,
sizeof(vg_name), &index))
continue;
/* is it the vg we're interested in ? */
if (strcmp(vg, vg_name))
continue;
if (!(path = _join(mem, dir, dirent[i]->d_name))) {
stack;
goto out;
}
/*
* Create a new archive_file.
*/
if (!(af = pool_alloc(mem, sizeof(*af)))) {
log_err("Couldn't create new archive file.");
results = NULL;
goto out;
}
af->index = index;
af->path = path;
/*
* Insert it to the correct part of the list.
*/
_insert_file(results, af);
}
out:
for (i = 0; i < count; i++)
free(dirent[i]);
free(dirent);
return results;
}
int archive_vg(struct volume_group *vg,
const char *dir, const char *desc,
uint32_t retain_days, uint32_t min_archive)
{
int i, fd;
unsigned int index = 0;
struct archive_file *last;
FILE *fp = NULL;
char temp_file[PATH_MAX], archive_name[PATH_MAX];
struct list *archives;
/*
* Write the vg out to a temporary file.
*/
if (!create_temp_name(dir, temp_file, sizeof(temp_file), &fd)) {
log_err("Couldn't create temporary archive name.");
return 0;
}
if (!(fp = fdopen(fd, "w"))) {
log_err("Couldn't create FILE object for archive.");
close(fd);
return 0;
}
if (!text_vg_export(fp, vg, desc)) {
stack;
fclose(fp);
return 0;
}
fclose(fp);
/*
* Now we want to rename this file to <vg>_index.vg.
*/
if (!(archives = _scan_archive(vg->cmd->mem, vg->name, dir))) {
log_err("Couldn't scan the archive directory (%s).", dir);
return 0;
}
if (list_empty(archives))
index = 0;
else {
last = list_item(archives->p, struct archive_file);
index = last->index + 1;
}
for (i = 0; i < 10; i++) {
if (lvm_snprintf(archive_name, sizeof(archive_name),
"%s/%s_%05d.vg",
dir, vg->name, index) < 0) {
log_err("archive file name too long.");
return 0;
}
if (lvm_rename(temp_file, archive_name))
break;
index++;
}
return 1;
}
static void _display_archive(struct cmd_context *cmd, struct uuid_map *um,
struct archive_file *af)
{
struct volume_group *vg;
time_t when;
char *desc;
log_print("path:\t\t%s", af->path);
/*
* Read the archive file to ensure that it is valid, and
* retrieve the archive time and description.
*/
if (!(vg = text_vg_import(cmd, af->path, um, &when, &desc))) {
log_print("Unable to read archive file.");
return;
}
log_print("description:\t%s", desc ? desc : "<No description>");
log_print("time:\t\t%s", ctime(&when));
pool_free(cmd->mem, vg);
}
int archive_list(struct cmd_context *cmd, struct uuid_map *um,
const char *dir, const char *vg)
{
struct list *archives, *ah;
struct archive_file *af;
if (!(archives = _scan_archive(cmd->mem, vg, dir))) {
log_err("Couldn't scan the archive directory (%s).", dir);
return 0;
}
if (list_empty(archives))
log_print("No archives found.");
list_iterate (ah, archives) {
af = list_item(ah, struct archive_file);
_display_archive(cmd, um, af);
log_print(" ");
}
pool_free(cmd->mem, archives);
return 1;
}

View File

@@ -1,558 +0,0 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "import-export.h"
#include "metadata.h"
#include "log.h"
#include "hash.h"
#include "pool.h"
#include "dbg_malloc.h"
#include "lvm-string.h"
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
/*
* The first half of this file deals with
* exporting the vg, ie. writing it to a file.
*/
struct formatter {
struct pool *mem; /* pv names allocated from here */
struct hash_table *pv_names; /* dev_name -> pv_name (eg, pv1) */
FILE *fp; /* where we're writing to */
int indent; /* current level of indentation */
int error;
};
/*
* Formatting functions.
*/
static void _out_size(struct formatter *f, uint64_t size,
const char *fmt, ...)
__attribute__ (( format (printf, 3, 4) ));
static void _out_hint(struct formatter *f, const char *fmt, ...)
__attribute__ (( format (printf, 2, 3) ));
static void _out(struct formatter *f, const char *fmt, ...)
__attribute__ (( format (printf, 2, 3) ));
#define MAX_INDENT 5
static void _inc_indent(struct formatter *f)
{
if (++f->indent > MAX_INDENT)
f->indent = MAX_INDENT;
}
static void _dec_indent(struct formatter *f)
{
if (!f->indent--) {
log_error("Internal error tracking indentation");
f->indent = 0;
}
}
/*
* Newline function for prettier layout.
*/
static void _nl(struct formatter *f)
{
fprintf(f->fp, "\n");
}
#define COMMENT_TAB 6
static void _out_with_comment(struct formatter *f, const char *comment,
const char *fmt, va_list ap)
{
int i;
char white_space[MAX_INDENT + 1];
if (ferror(f->fp))
return;
for (i = 0; i < f->indent; i++)
white_space[i] = '\t';
white_space[i] = '\0';
fprintf(f->fp, white_space);
i = vfprintf(f->fp, fmt, ap);
if (comment) {
/*
* line comments up if possible.
*/
i += 8 * f->indent;
i /= 8;
i++;
do
fputc('\t', f->fp);
while (++i < COMMENT_TAB);
fprintf(f->fp, comment);
}
fputc('\n', f->fp);
}
/*
* Formats a string, converting a size specified
* in 512-byte sectors to a more human readable
* form (eg, megabytes). We may want to lift this
* for other code to use.
*/
static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
{
static char *_units[] = {
"Kilobytes",
"Megabytes",
"Gigabytes",
"Terrabytes",
NULL
};
int i;
double d = (double) sectors;
/* to convert to K */
d /= 2.0;
for (i = 0; (d > 1024.0) && _units[i]; i++)
d /= 1024.0;
return lvm_snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
}
/*
* Appends a comment giving a size in more easily
* readable form (eg, 4M instead of 8096).
*/
static void _out_size(struct formatter *f, uint64_t size,
const char *fmt, ...)
{
char buffer[64];
va_list ap;
_sectors_to_units(size, buffer, sizeof(buffer));
va_start(ap, fmt);
_out_with_comment(f, buffer, fmt, ap);
va_end(ap);
}
/*
* Appends a comment indicating that the line is
* only a hint.
*/
static void _out_hint(struct formatter *f, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_out_with_comment(f, "# Hint only", fmt, ap);
va_end(ap);
}
/*
* The normal output function.
*/
static void _out(struct formatter *f, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_out_with_comment(f, NULL, fmt, ap);
va_end(ap);
}
static int _print_header(struct formatter *f,
struct volume_group *vg, const char *desc)
{
time_t t;
t = time(NULL);
_out(f,
"# This file was originally generated by the LVM2 library\n"
"# Generated: %s\n", ctime(&t));
_out(f, "description = \"%s\"", desc);
_out(f, "creation_time = %lu\n", t);
return 1;
}
static int _print_vg(struct formatter *f, struct volume_group *vg)
{
char buffer[256];
if (!id_write_format(&vg->id, buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "id = \"%s\"", buffer);
if (!print_flags(vg->status, VG_FLAGS, buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "status = %s", buffer);
if (vg->system_id && *vg->system_id)
_out(f, "system_id = \"%s\"", vg->system_id);
_out_size(f, vg->extent_size, "extent_size = %u", vg->extent_size);
_out(f, "max_lv = %u", vg->max_lv);
_out(f, "max_pv = %u", vg->max_pv);
return 1;
}
/*
* Get the pv%d name from the formatters hash
* table.
*/
static inline const char *
_get_pv_name(struct formatter *f, struct physical_volume *pv)
{
return (pv) ? (const char *)
hash_lookup(f->pv_names, dev_name(pv->dev)) :
"Missing";
}
static int _print_pvs(struct formatter *f, struct volume_group *vg)
{
struct list *pvh;
struct physical_volume *pv;
char buffer[256];
const char *name;
_out(f, "physical_volumes {");
_inc_indent(f);
list_iterate (pvh, &vg->pvs) {
pv = list_item(pvh, struct pv_list)->pv;
if (!(name = _get_pv_name(f, pv))) {
stack;
return 0;
}
_nl(f);
_out(f, "%s {", name);
_inc_indent(f);
if (!id_write_format(&pv->id, buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "id = \"%s\"", buffer);
_out_hint(f, "device = \"%s\"", dev_name(pv->dev));
_nl(f);
if (!print_flags(pv->status, PV_FLAGS,
buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "status = %s", buffer);
_out(f, "pe_start = %llu", pv->pe_start);
_out_size(f, vg->extent_size * (uint64_t) pv->pe_count,
"pe_count = %u", pv->pe_count);
_dec_indent(f);
_out(f, "}");
}
_dec_indent(f);
_out(f, "}");
return 1;
}
static int _print_segment(struct formatter *f, struct volume_group *vg,
int count, struct stripe_segment *seg)
{
int s;
const char *name;
_out(f, "segment%u {", count);
_inc_indent(f);
_out(f, "start_extent = %u", seg->le);
_out_size(f, seg->len * vg->extent_size, "extent_count = %u", seg->len);
_out(f, "stripes = %u", seg->stripes);
if (seg->stripes > 1)
_out_size(f, seg->stripe_size,
"stripe_size = %u", seg->stripe_size);
_nl(f);
_out(f, "areas = [");
_inc_indent(f);
for (s = 0; s < seg->stripes; s++) {
if (!(name = _get_pv_name(f, seg->area[s].pv))) {
stack;
return 0;
}
_out(f, "\"%s\", %u%s", name, seg->area[s].pe,
(s == seg->stripes - 1) ? "" : ",");
}
_dec_indent(f);
_out(f, "]");
_dec_indent(f);
_out(f, "}");
return 1;
}
static int _count_segments(struct logical_volume *lv)
{
int r = 0;
struct list *segh;
list_iterate (segh, &lv->segments)
r++;
return r;
}
static int _print_lvs(struct formatter *f, struct volume_group *vg)
{
struct list *lvh, *segh;
struct logical_volume *lv;
struct stripe_segment *seg;
char buffer[256];
int seg_count;
/*
* Don't bother with an lv section if there are no lvs.
*/
if (list_empty(&vg->lvs))
return 1;
_out(f, "logical_volumes {");
_inc_indent(f);
list_iterate (lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
_nl(f);
_out(f, "%s {", lv->name);
_inc_indent(f);
/* FIXME: Write full lvid */
if (!id_write_format(&lv->lvid.id[1], buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "id = \"%s\"", buffer);
if (!print_flags(lv->status, LV_FLAGS,
buffer, sizeof(buffer))) {
stack;
return 0;
}
_out(f, "status = %s", buffer);
_out(f, "read_ahead = %u", lv->read_ahead);
if (lv->minor >= 0)
_out(f, "minor = %d", lv->minor);
_out(f, "segment_count = %u", _count_segments(lv));
_nl(f);
seg_count = 1;
list_iterate (segh, &lv->segments) {
seg = list_item(segh, struct stripe_segment);
if (!_print_segment(f, vg, seg_count++, seg)) {
stack;
return 0;
}
}
_dec_indent(f);
_out(f, "}");
}
_dec_indent(f);
_out(f, "}");
return 1;
}
static int _print_snapshot(struct formatter *f, struct snapshot *s,
unsigned int count)
{
_nl(f);
_out(f, "snapshot%u {", count);
_inc_indent(f);
_out(f, "chunk_size = %u", s->chunk_size);
_out(f, "origin = \"%s\"", s->origin->name);
_out(f, "cow_store = \"%s\"", s->cow->name);
_dec_indent(f);
_out(f, "}");
return 1;
}
static int _print_snapshots(struct formatter *f, struct volume_group *vg)
{
struct list *sh;
struct snapshot *s;
unsigned int count = 0;
/*
* Don't bother with a snapshot section if there are no
* snapshots.
*/
if (list_empty(&vg->snapshots))
return 1;
_out(f, "snapshots {");
_inc_indent(f);
list_iterate (sh, &vg->snapshots) {
s = list_item(sh, struct snapshot_list)->snapshot;
if (!_print_snapshot(f, s, count++)) {
stack;
return 0;
}
}
_dec_indent(f);
_out(f, "}");
return 1;
}
/*
* In the text format we refer to pv's as 'pv1',
* 'pv2' etc. This function builds a hash table
* to enable a quick lookup from device -> name.
*/
static int _build_pv_names(struct formatter *f,
struct volume_group *vg)
{
int count = 0;
struct list *pvh;
struct physical_volume *pv;
char buffer[32], *name;
if (!(f->mem = pool_create(512))) {
stack;
goto bad;
}
if (!(f->pv_names = hash_create(128))) {
stack;
goto bad;
}
list_iterate (pvh, &vg->pvs) {
pv = list_item(pvh, struct pv_list)->pv;
if (lvm_snprintf(buffer, sizeof(buffer),
"pv%d", count++) < 0) {
stack;
goto bad;
}
if (!(name = pool_strdup(f->mem, buffer))) {
stack;
goto bad;
}
if (!hash_insert(f->pv_names, dev_name(pv->dev), name)) {
stack;
goto bad;
}
}
return 1;
bad:
if (f->mem)
pool_destroy(f->mem);
if (f->pv_names)
hash_destroy(f->pv_names);
return 0;
}
int text_vg_export(FILE *fp, struct volume_group *vg, const char *desc)
{
int r = 0;
struct formatter *f;
if (!(f = dbg_malloc(sizeof(*f)))) {
stack;
return 0;
}
memset(f, 0, sizeof(*f));
f->fp = fp;
f->indent = 0;
if (!_build_pv_names(f, vg)) {
stack;
goto out;
}
#define fail do {stack; goto out;} while(0)
if (!_print_header(f, vg, desc))
fail;
_out(f, "%s {", vg->name);
_inc_indent(f);
if (!_print_vg(f, vg))
fail;
_nl(f);
if (!_print_pvs(f, vg))
fail;
_nl(f);
if (!_print_lvs(f, vg))
fail;
_nl(f);
if (!_print_snapshots(f, vg))
fail;
#undef fail
_dec_indent(f);
_out(f, "}");
r = !ferror(f->fp);
out:
if (f->mem)
pool_destroy(f->mem);
if (f->pv_names)
hash_destroy(f->pv_names);
dbg_free(f);
return r;
}

View File

@@ -1,160 +0,0 @@
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
*
* This file is released under the LGPL.
*/
#include "log.h"
#include "metadata.h"
#include "import-export.h"
#include "lvm-string.h"
/*
* Bitsets held in the 'status' flags get
* converted into arrays of strings.
*/
struct flag {
int mask;
char *description;
};
static struct flag _vg_flags[] = {
{EXPORTED_VG, "EXPORTED"},
{RESIZEABLE_VG, "RESIZEABLE"},
{PARTIAL_VG, "PARTIAL"},
{LVM_READ, "READ"},
{LVM_WRITE, "WRITE"},
{CLUSTERED, "CLUSTERED"},
{SHARED, "SHARED"},
{0, NULL}
};
static struct flag _pv_flags[] = {
{ALLOCATABLE_PV, "ALLOCATABLE"},
{EXPORTED_VG, "EXPORTED"},
{0, NULL}
};
static struct flag _lv_flags[] = {
{LVM_READ, "READ"},
{LVM_WRITE, "WRITE"},
{ALLOC_SIMPLE, "ALLOC_SIMPLE"},
{ALLOC_STRICT, "ALLOC_STRICT"},
{ALLOC_CONTIGUOUS, "ALLOC_CONTIGUOUS"},
{FIXED_MINOR, "FIXED_MINOR"},
{0, NULL}
};
static struct flag *_get_flags(int type)
{
switch (type) {
case VG_FLAGS:
return _vg_flags;
case PV_FLAGS:
return _pv_flags;
case LV_FLAGS:
return _lv_flags;
}
log_err("Unknown flag set requested.");
return NULL;
}
static int _emit(char **buffer, size_t *size, const char *fmt, ...)
{
size_t n;
va_list ap;
va_start(ap, fmt);
n = vsnprintf(*buffer, *size, fmt, ap);
va_end(ap);
if (n < 0 || (n == *size))
return 0;
*buffer += n;
*size -= n;
return 1;
}
/*
* Converts a bitset to an array of string values,
* using one of the tables defined at the top of
* the file.
*/
int print_flags(uint32_t status, int type, char *buffer, size_t size)
{
int f, first = 1;
struct flag *flags;
if (!(flags = _get_flags(type))) {
stack;
return 0;
}
if (!_emit(&buffer, &size, "["))
return 0;
for (f = 0; flags[f].mask; f++) {
if (status & flags[f].mask) {
if (!first) {
if (!_emit(&buffer, &size, ", "))
return 0;
} else
first = 0;
if (!_emit(&buffer, &size, "\"%s\"",
flags[f].description))
return 0;
status &= ~flags[f].mask;
}
}
if (!_emit(&buffer, &size, "]"))
return 0;
if (status)
log_error("Metadata inconsistency: Not all flags successfully "
"exported.");
return 1;
}
int read_flags(uint32_t *status, int type, struct config_value *cv)
{
int f;
uint32_t s = 0;
struct flag *flags;
if (!(flags = _get_flags(type))) {
stack;
return 0;
}
while (cv) {
if (cv->type != CFG_STRING) {
log_err("Status value is not a string.");
return 0;
}
for (f = 0; flags[f].description; f++)
if (!strcmp(flags[f].description, cv->v.str)) {
s |= flags[f].mask;
break;
}
if (!flags[f].description) {
log_err("Unknown status flag '%s'.", cv->v.str);
return 0;
}
cv = cv->next;
}
*status = s;
return 1;
}

Some files were not shown because too many files have changed in this diff Show More