mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-25 03:33:16 +03:00
Compare commits
254 Commits
dev-dct-cl
...
PRE_DMFS
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8686d0e75 | ||
|
|
549e3c8f9d | ||
|
|
bb56225b95 | ||
|
|
f00be261ba | ||
|
|
9cd94f26d0 | ||
|
|
4d8f5c80a7 | ||
|
|
24a23acc3d | ||
|
|
ca8703cfd8 | ||
|
|
6dcbb5b2f8 | ||
|
|
a84fdddb2a | ||
|
|
38bb2f8ceb | ||
|
|
23f5ef4345 | ||
|
|
ef8a2a9054 | ||
|
|
96103d0e36 | ||
|
|
ff5f6748df | ||
|
|
1c1fd6c366 | ||
|
|
32d37d00cb | ||
|
|
82f6cda966 | ||
|
|
f1ff8ff0d0 | ||
|
|
756c72902f | ||
|
|
73f8f0bbd0 | ||
|
|
18ed528f5d | ||
|
|
8fd2f136bc | ||
|
|
0524b1bf67 | ||
|
|
15716f65ce | ||
|
|
d46bdba332 | ||
|
|
760728110a | ||
|
|
12d0a194ca | ||
|
|
4104543508 | ||
|
|
5c211db015 | ||
|
|
2dc6180f8d | ||
|
|
e222a34b69 | ||
|
|
ef17d95063 | ||
|
|
853502e5d7 | ||
|
|
c18e297e77 | ||
|
|
c5a49599ba | ||
|
|
df9da9edf5 | ||
|
|
e2200fd050 | ||
|
|
c6207f5d9c | ||
|
|
4302b7ff6b | ||
|
|
50a7923438 | ||
|
|
ab416445c8 | ||
|
|
a54698d43c | ||
|
|
c5a77cc1c0 | ||
|
|
a9ffa811fc | ||
|
|
080a2608e0 | ||
|
|
57f2e83d6a | ||
|
|
5b030139d3 | ||
|
|
0da1ff42d1 | ||
|
|
2c599b7baa | ||
|
|
5c8af8d21a | ||
|
|
026f3cfde2 | ||
|
|
f6349180e8 | ||
|
|
aa6421921c | ||
|
|
7d41d2dab2 | ||
|
|
f0b4d18f93 | ||
|
|
6750f06e10 | ||
|
|
b2bd38fa9e | ||
|
|
3482a01e22 | ||
|
|
6335467552 | ||
|
|
4a39e65b62 | ||
|
|
c50a23e918 | ||
|
|
1e76b72b98 | ||
|
|
b94cf39eef | ||
|
|
fef254ffff | ||
|
|
e5495863a2 | ||
|
|
3b4df2abf0 | ||
|
|
aef2aee6a4 | ||
|
|
d0d9519149 | ||
|
|
685df1d2c5 | ||
|
|
08e6b6f2e7 | ||
|
|
66c887d0f3 | ||
|
|
22e9960697 | ||
|
|
64aa6e1f2d | ||
|
|
7a93ed9d04 | ||
|
|
a905e922e9 | ||
|
|
f9f08fc720 | ||
|
|
8d402d76d0 | ||
|
|
46fda6281c | ||
|
|
a14dbe1ea6 | ||
|
|
18810a4c16 | ||
|
|
147bc80dba | ||
|
|
c7a484195a | ||
|
|
4968eb6503 | ||
|
|
a6f2d698a9 | ||
|
|
ea5ed93ea5 | ||
|
|
e1140134c6 | ||
|
|
5ed11e012e | ||
|
|
5380bd39ca | ||
|
|
2ee2685688 | ||
|
|
782002245b | ||
|
|
7fc0905843 | ||
|
|
72ecb99e54 | ||
|
|
c863507d08 | ||
|
|
cff86c9093 | ||
|
|
0479dfcc54 | ||
|
|
68dd67f21c | ||
|
|
540f6858b5 | ||
|
|
b61e791a4f | ||
|
|
d0986f9482 | ||
|
|
112cb0dc28 | ||
|
|
0d3d7fdcf2 | ||
|
|
5d6b89ef3b | ||
|
|
ed0b26c09e | ||
|
|
ae292bd920 | ||
|
|
6c85a90723 | ||
|
|
852592066c | ||
|
|
96e1bc9b44 | ||
|
|
b41d81ed31 | ||
|
|
e241ec2244 | ||
|
|
16e1f1a94c | ||
|
|
7a68c42b26 | ||
|
|
37ccc2e118 | ||
|
|
4192fe1ab2 | ||
|
|
d5c743d7bb | ||
|
|
11814d63e8 | ||
|
|
b753656d50 | ||
|
|
f7e87611fc | ||
|
|
1fb0e1900e | ||
|
|
954a9731e0 | ||
|
|
65c3364ad8 | ||
|
|
3d72b7dccc | ||
|
|
13ee569f06 | ||
|
|
d79ef23a75 | ||
|
|
5d0797d4ba | ||
|
|
47a8d7475f | ||
|
|
4939053121 | ||
|
|
b525bf554e | ||
|
|
f00285d2b2 | ||
|
|
040f8d6eda | ||
|
|
66d905325c | ||
|
|
8b0cd95e73 | ||
|
|
d867cca6d9 | ||
|
|
a28f736369 | ||
|
|
5c3a71cc59 | ||
|
|
cef6dadb08 | ||
|
|
36be817a3e | ||
|
|
02f571f081 | ||
|
|
157159e487 | ||
|
|
02ada9f800 | ||
|
|
6fcf9a97bb | ||
|
|
17a5d8799f | ||
|
|
31f3fe7a22 | ||
|
|
89e46d3d83 | ||
|
|
885795e67d | ||
|
|
92bfb53dd4 | ||
|
|
4cecbeb115 | ||
|
|
5971480f55 | ||
|
|
05bebea511 | ||
|
|
76fa6c5cfb | ||
|
|
633b68b518 | ||
|
|
6913d8e995 | ||
|
|
f3654e6f8d | ||
|
|
d685dbcf22 | ||
|
|
6a57fa079e | ||
|
|
0a91d145ba | ||
|
|
c2866e799d | ||
|
|
d8cffcaae7 | ||
|
|
30abca7be2 | ||
|
|
edce87f3fb | ||
|
|
66bac98fc2 | ||
|
|
59156de92b | ||
|
|
e0d7d10600 | ||
|
|
daaf862257 | ||
|
|
9de53d4b59 | ||
|
|
f1571e2d46 | ||
|
|
bd28d06298 | ||
|
|
a24e4655eb | ||
|
|
20a6c8d8e5 | ||
|
|
98d264faf4 | ||
|
|
321902a9b5 | ||
|
|
8df5d06f9a | ||
|
|
e69ea529cc | ||
|
|
15405b1119 | ||
|
|
d2f97ce2da | ||
|
|
543ca631e9 | ||
|
|
f184886db1 | ||
|
|
8432ab4324 | ||
|
|
6c05b37ca3 | ||
|
|
35f4beeb47 | ||
|
|
cbad7caa68 | ||
|
|
b0388a4012 | ||
|
|
df3fab4d55 | ||
|
|
da49f88a03 | ||
|
|
e28feceb06 | ||
|
|
50496a164d | ||
|
|
6f1dce1572 | ||
|
|
6847776ae7 | ||
|
|
67bd53bdd8 | ||
|
|
e735abfdfd | ||
|
|
1de93a2d6d | ||
|
|
36f9e7c742 | ||
|
|
9462763bbb | ||
|
|
4ae0880ea6 | ||
|
|
6ae2b6c835 | ||
|
|
a0f180fd48 | ||
|
|
bf1cf89914 | ||
|
|
297a047fb4 | ||
|
|
52ffc15ffc | ||
|
|
e478c9c693 | ||
|
|
d004f28074 | ||
|
|
bc68ed8b1d | ||
|
|
04555ae650 | ||
|
|
e8f62085be | ||
|
|
f430bffe2a | ||
|
|
1f0520634f | ||
|
|
902d4c31fb | ||
|
|
17364ac09f | ||
|
|
0b889f8f81 | ||
|
|
40e349ff35 | ||
|
|
c943b1b1df | ||
|
|
912bc1d4e1 | ||
|
|
cacb1533a3 | ||
|
|
f0feaca9d7 | ||
|
|
b6656f171b | ||
|
|
6206ab3931 | ||
|
|
c35fc58b1f | ||
|
|
deed8abed7 | ||
|
|
7151ad23f0 | ||
|
|
0166d938af | ||
|
|
6194aeddb0 | ||
|
|
903dbf2c30 | ||
|
|
9380f9ff57 | ||
|
|
259ed95486 | ||
|
|
2ebc92681e | ||
|
|
195a1ffe13 | ||
|
|
a8c2978185 | ||
|
|
140f97a457 | ||
|
|
7f94445a1e | ||
|
|
82a89aec65 | ||
|
|
7e95110232 | ||
|
|
ec4aaaad89 | ||
|
|
1b790fde24 | ||
|
|
aaccea731e | ||
|
|
29e31d7610 | ||
|
|
aa51f4a98f | ||
|
|
e6ccd12f00 | ||
|
|
b134315df1 | ||
|
|
7f34dffa13 | ||
|
|
fa239e78c9 | ||
|
|
707a6c4d6a | ||
|
|
e5da303b43 | ||
|
|
84ccd66331 | ||
|
|
ad8cc2baea | ||
|
|
7c4cf70309 | ||
|
|
c3211e9b4f | ||
|
|
268d94c983 | ||
|
|
0bcacbba58 | ||
|
|
8cdc26add9 | ||
|
|
e0b2238886 | ||
|
|
369a2e4029 | ||
|
|
c4089e3b51 | ||
|
|
9e2e9bc5b8 | ||
|
|
a9e44426ed |
26
Makefile.in
Normal file
26
Makefile.in
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software
|
||||
#
|
||||
# This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
# MA 02111-1307, USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SUBDIRS = include man lib tools
|
||||
|
||||
include make.tmpl
|
||||
|
||||
141
configure.in
Normal file
141
configure.in
Normal file
@@ -0,0 +1,141 @@
|
||||
################################################################################
|
||||
##
|
||||
## Copyright 1999-2000 Sistina Software, Inc.
|
||||
##
|
||||
## This is free software released under the GNU General Public License.
|
||||
## There is no warranty for this software. See the file COPYING for
|
||||
## details.
|
||||
##
|
||||
## See the file CONTRIBUTORS for a list of contributors.
|
||||
##
|
||||
## This file is maintained by:
|
||||
## AJ Lewis <lewis@sistina.com>
|
||||
##
|
||||
## File name: configure.in
|
||||
##
|
||||
## Description: Input file for autoconf. Generates the configure script
|
||||
## that tries to keep everything nice and portable. It also
|
||||
## simplifies distribution package building considerably.
|
||||
################################################################################
|
||||
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(lib/device/dev-cache.h)
|
||||
|
||||
dnl setup the directory where autoconf has auxilary files
|
||||
AC_CONFIG_AUX_DIR(autoconf)
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_AWK
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_RANLIB
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h unistd.h)
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_STRUCT_ST_RDEV
|
||||
AC_HEADER_TIME
|
||||
|
||||
dnl -- prefix is /usr by default, the exec_prefix default is setup later
|
||||
AC_PREFIX_DEFAULT(/usr)
|
||||
|
||||
dnl -- setup the ownership of the files
|
||||
AC_ARG_WITH(user,
|
||||
[ --with-user=USER Set the owner of installed files ],
|
||||
[ OWNER="$withval" ],
|
||||
[ OWNER="root" ])
|
||||
|
||||
dnl -- setup the group ownership of the files
|
||||
AC_ARG_WITH(group,
|
||||
[ --with-group=GROUP Set the group owner of installed files ],
|
||||
[ 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
|
||||
AC_ARG_ENABLE(static_link, [ --enable-static_link Use this to link the tools to the liblvm library
|
||||
statically. Default is dynamic linking], STATIC_LINK=$enableval, STATIC_LINK=no)
|
||||
|
||||
dnl Disable readline
|
||||
AC_ARG_ENABLE(readline, [ --disable-readline Disable readline support], \
|
||||
READLINE=$enableval, READLINE=yes)
|
||||
|
||||
dnl Mess with default exec_prefix
|
||||
if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]];
|
||||
then exec_prefix="";
|
||||
fi;
|
||||
|
||||
dnl Checks for library functions.
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_TYPE_SIGNAL
|
||||
AC_FUNC_VPRINTF
|
||||
AC_CHECK_FUNCS(mkdir rmdir uname)
|
||||
|
||||
dnl check for termcap (Shamelessly copied from parted 1.4.17)
|
||||
if test x$READLINE = xyes; then
|
||||
AC_SEARCH_LIBS(tgetent, ncurses curses termcap termlib, ,
|
||||
AC_MSG_ERROR(
|
||||
termcap could not be found which is required for the
|
||||
--enable-readline option (which is enabled by default). Either disable readline
|
||||
support with --disable-readline or download and install termcap from:
|
||||
ftp.gnu.org/gnu/termcap
|
||||
Note: if you are using precompiled packages you will also need the development
|
||||
package as well (which may be called termcap-devel or something similar).
|
||||
Note: (n)curses also seems to work as a substitute for termcap. This was
|
||||
not found either - but you could try installing that as well.
|
||||
)
|
||||
exit
|
||||
)
|
||||
fi
|
||||
|
||||
dnl Check for readline (Shamelessly copied from parted 1.4.17)
|
||||
if test x$READLINE = xyes; then
|
||||
AC_CHECK_LIB(readline, readline, ,
|
||||
AC_MSG_ERROR(
|
||||
GNU Readline could not be found which is required for the
|
||||
--enable-readline option (which is enabled by default). Either disable readline
|
||||
support with --disable-readline or download and install readline from:
|
||||
ftp.gnu.org/gnu/readline
|
||||
Note: if you are using precompiled packages you will also need the development
|
||||
package as well (which may be called readline-devel or something similar).
|
||||
)
|
||||
exit
|
||||
)
|
||||
fi
|
||||
|
||||
AC_SUBST(JOBS)
|
||||
AC_SUBST(STATIC_LINK)
|
||||
AC_SUBST(READLINE)
|
||||
AC_SUBST(kernel_dir)
|
||||
AC_SUBST(OWNER)
|
||||
AC_SUBST(GROUP)
|
||||
dnl First and last lines should not contain files to generate in order to
|
||||
dnl keep utility scripts running properly
|
||||
AC_OUTPUT( \
|
||||
Makefile \
|
||||
make.tmpl \
|
||||
include/Makefile \
|
||||
lib/Makefile \
|
||||
man/Makefile \
|
||||
tools/Makefile \
|
||||
test/mm/Makefile \
|
||||
test/device/Makefile \
|
||||
test/format1/Makefile \
|
||||
)
|
||||
104
driver/device-mapper/README
Normal file
104
driver/device-mapper/README
Normal 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.
|
||||
|
||||
|
||||
|
||||
|
||||
88
driver/device-mapper/device-mapper.h
Normal file
88
driver/device-mapper/device-mapper.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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;
|
||||
struct text_region;
|
||||
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,
|
||||
struct text_region *args, void **context,
|
||||
dm_error_fn err, void *e_private);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
int dm_register_target(struct target_type *t);
|
||||
int dm_unregister_target(struct target_type *t);
|
||||
|
||||
/*
|
||||
* These may be useful for people writing target
|
||||
* types.
|
||||
*/
|
||||
struct text_region {
|
||||
const char *b;
|
||||
const char *e;
|
||||
};
|
||||
|
||||
int dm_get_number(struct text_region *txt, unsigned int *n);
|
||||
int dm_get_line(struct text_region *txt, struct text_region *line);
|
||||
int dm_get_word(struct text_region *txt, struct text_region *word);
|
||||
void dm_txt_copy(char *dest, size_t max, struct text_region *txt);
|
||||
void dm_eat_space(struct text_region *txt);
|
||||
|
||||
|
||||
#endif /* DEVICE_MAPPER_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
553
driver/device-mapper/dm-fs.c
Normal file
553
driver/device-mapper/dm-fs.c
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* *very* heavily based on ramfs
|
||||
*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/locks.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
/* some magic number */
|
||||
#define DM_MAGIC 0x444D4653
|
||||
|
||||
static struct super_operations dm_ops;
|
||||
static struct address_space_operations dm_aops;
|
||||
static struct file_operations dm_dir_operations;
|
||||
static struct file_operations dm_file_operations;
|
||||
static struct inode_operations dm_dir_inode_operations;
|
||||
|
||||
struct vfsmount *_mnt;
|
||||
|
||||
static int _unlink(struct inode *dir, struct dentry *dentry);
|
||||
|
||||
#define NOT_A_TABLE ((struct dm_table *) 1)
|
||||
|
||||
/*
|
||||
* context for the line splitter and error function.
|
||||
*/
|
||||
struct line_c {
|
||||
unsigned int line_num;
|
||||
loff_t next_read;
|
||||
char data[MAX_TARGET_LINE];
|
||||
|
||||
struct file *in;
|
||||
struct file *out;
|
||||
};
|
||||
|
||||
static int is_identifier(const char *str, int len)
|
||||
{
|
||||
if (len > DM_NAME_LEN - 1)
|
||||
return 0;
|
||||
|
||||
while(len--) {
|
||||
if (!isalnum(*str) && *str != '_')
|
||||
return 0;
|
||||
str++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Grabs lines one at a time from the table file.
|
||||
*/
|
||||
int extract_line(struct text_region *line, void *private)
|
||||
{
|
||||
struct line_c *lc = (struct line_c *) private;
|
||||
struct text_region text;
|
||||
ssize_t n;
|
||||
loff_t off = lc->next_read;
|
||||
const char *read_begin;
|
||||
mm_segment_t fs;
|
||||
|
||||
fs = get_fs();
|
||||
set_fs(get_ds());
|
||||
|
||||
n = lc->in->f_op->read(lc->in, lc->data, sizeof (lc->data), &off);
|
||||
|
||||
set_fs(fs);
|
||||
|
||||
if (n <= 0)
|
||||
return 0;
|
||||
|
||||
read_begin = text.b = lc->data;
|
||||
text.e = lc->data + n;
|
||||
|
||||
if (!dm_get_line(&text, line))
|
||||
return 0;
|
||||
|
||||
lc->line_num++;
|
||||
lc->next_read += line->e - read_begin;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct file *open_error_file(struct file *table)
|
||||
{
|
||||
struct file *f;
|
||||
char *name, *buffer;
|
||||
int bufsize = PATH_MAX + 1;
|
||||
|
||||
if (bufsize < PAGE_SIZE)
|
||||
bufsize = PAGE_SIZE;
|
||||
|
||||
/* Get space to append ".err" */
|
||||
buffer = (char *) kmalloc(bufsize + 4, GFP_KERNEL);
|
||||
|
||||
if (!buffer)
|
||||
return 0;
|
||||
|
||||
/* Get path name */
|
||||
name = d_path(table->f_dentry, table->f_vfsmnt, buffer, bufsize);
|
||||
|
||||
if (!name) {
|
||||
kfree(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create error file */
|
||||
strcat(name, ".err");
|
||||
f = filp_open(name, O_WRONLY | O_TRUNC | O_CREAT, S_IRUGO);
|
||||
|
||||
kfree(buffer);
|
||||
|
||||
if (f)
|
||||
f->f_dentry->d_inode->u.generic_ip = NOT_A_TABLE;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static void close_error_file(struct file *out)
|
||||
{
|
||||
fput(out);
|
||||
}
|
||||
|
||||
static void parse_error(const char *message, void *private)
|
||||
{
|
||||
struct line_c *lc = (struct line_c *) private;
|
||||
char buffer[32];
|
||||
|
||||
#define emit(b, l) lc->out->f_op->write(lc->out, (b), (l), &lc->out->f_pos)
|
||||
|
||||
emit(lc->in->f_dentry->d_name.name, lc->in->f_dentry->d_name.len);
|
||||
sprintf(buffer, "(%d): ", lc->line_num);
|
||||
emit(buffer, strlen(buffer));
|
||||
emit(message, strlen(message));
|
||||
emit("\n", 1);
|
||||
|
||||
#undef emit
|
||||
}
|
||||
|
||||
static int _release(struct inode *inode, struct file *f)
|
||||
{
|
||||
/* FIXME: we should lock the inode to
|
||||
prevent someone else opening it while
|
||||
we are parsing */
|
||||
struct line_c *lc;
|
||||
struct dm_table *table = (struct dm_table *) inode->u.generic_ip;
|
||||
|
||||
/* noop for files without tables (.err files) */
|
||||
if (table == NOT_A_TABLE)
|
||||
return 0;
|
||||
|
||||
/* only bother parsing if it was open for a write */
|
||||
if (!(f->f_mode & S_IWUGO))
|
||||
return 0;
|
||||
|
||||
/* free off the old table */
|
||||
if (table) {
|
||||
dm_table_destroy(table);
|
||||
inode->u.generic_ip = 0;
|
||||
}
|
||||
|
||||
if (!(lc = kmalloc(sizeof (*lc), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
memset(lc, 0, sizeof (*lc));
|
||||
lc->in = f;
|
||||
|
||||
if (!(lc->out = open_error_file(lc->in)))
|
||||
return -ENOMEM;
|
||||
|
||||
table = dm_parse(extract_line, lc, parse_error, lc);
|
||||
close_error_file(lc->out);
|
||||
|
||||
kfree(lc);
|
||||
inode->u.generic_ip = table;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _put_inode(struct inode *inode)
|
||||
{
|
||||
struct mapped_device *md =
|
||||
(struct mapped_device *) inode->u.generic_ip;
|
||||
struct dm_table *table = (struct dm_table *) inode->u.generic_ip;
|
||||
|
||||
if (inode->i_mode & S_IFDIR) {
|
||||
if (md)
|
||||
dm_remove(md);
|
||||
|
||||
} else {
|
||||
if (table)
|
||||
dm_table_destroy(table);
|
||||
|
||||
}
|
||||
|
||||
inode->u.generic_ip = 0;
|
||||
force_delete(inode);
|
||||
}
|
||||
|
||||
static int _statfs(struct super_block *sb, struct statfs *buf)
|
||||
{
|
||||
buf->f_type = DM_MAGIC;
|
||||
buf->f_bsize = PAGE_CACHE_SIZE;
|
||||
buf->f_namelen = 255;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the data. This is trivial - if the dentry didn't already
|
||||
* exist, we know it is negative.
|
||||
*/
|
||||
static struct dentry *_lookup(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
d_add(dentry, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a page. Again trivial. If it didn't already exist
|
||||
* in the page cache, it is zero-filled.
|
||||
*/
|
||||
static int _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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writing: just make sure the page gets marked dirty, so that
|
||||
* the page stealer won't grab it.
|
||||
*/
|
||||
static int _writepage(struct page *page)
|
||||
{
|
||||
SetPageDirty(page);
|
||||
UnlockPage(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _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 _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;
|
||||
}
|
||||
|
||||
struct inode *_get_inode(struct super_block *sb, int mode, int dev)
|
||||
{
|
||||
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_mapping->a_ops = &dm_aops;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
init_special_inode(inode, mode, dev);
|
||||
break;
|
||||
case S_IFREG:
|
||||
inode->i_fop = &dm_file_operations;
|
||||
break;
|
||||
case S_IFDIR:
|
||||
inode->i_op = &dm_dir_inode_operations;
|
||||
inode->i_fop = &dm_dir_operations;
|
||||
break;
|
||||
case S_IFLNK:
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
break;
|
||||
default:
|
||||
make_bad_inode(inode);
|
||||
}
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* File creation. Allocate an inode, and we're done..
|
||||
*/
|
||||
static int _mknod(struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
int error = -ENOSPC;
|
||||
struct inode *inode = _get_inode(dir->i_sb, mode, 0);
|
||||
|
||||
if (inode) {
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry); /* Extra count - pin the dentry in core */
|
||||
error = 0;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int _mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
int r;
|
||||
const char *name = (const char *) dentry->d_name.name;
|
||||
struct mapped_device *md;
|
||||
|
||||
if (!is_identifier(name, dentry->d_name.len))
|
||||
return -EPERM; /* or EINVAL ? */
|
||||
|
||||
md = dm_create(name, -1);
|
||||
if (IS_ERR(md))
|
||||
return PTR_ERR(md);
|
||||
|
||||
r = _mknod(dir, dentry, mode | S_IFDIR);
|
||||
if (r) {
|
||||
dm_remove(md);
|
||||
return r;
|
||||
}
|
||||
|
||||
dentry->d_inode->u.generic_ip = md;
|
||||
md->inode = dentry->d_inode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int r = _unlink(dir, dentry);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _create(struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = _mknod(dir, dentry, mode | S_IFREG)))
|
||||
return r;
|
||||
|
||||
dentry->d_inode->u.generic_ip = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int positive(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_inode && !d_unhashed(dentry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that a directory is empty (this works
|
||||
* for regular files too, they'll just always be
|
||||
* considered empty..).
|
||||
*
|
||||
* Note that an empty directory can still have
|
||||
* children, they just all have to be negative..
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* This works for both directories and regular files.
|
||||
* (non-directories will always have empty subdirs)
|
||||
*/
|
||||
static int _unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int retval = -ENOTEMPTY;
|
||||
|
||||
if (_empty(dentry)) {
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
inode->i_nlink--;
|
||||
dput(dentry); /* Undo the count from "create" - this does all the work */
|
||||
retval = 0;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* The VFS layer already does all the dentry stuff for rename,
|
||||
* we just have to decrement the usage count for the target if
|
||||
* it exists so that the VFS layer correctly free's it when it
|
||||
* gets overwritten.
|
||||
*/
|
||||
static int _rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
{
|
||||
struct inode *inode = new_dentry->d_inode;
|
||||
struct mapped_device *md = old_dir->u.generic_ip;
|
||||
struct dm_table *table = old_dentry->d_inode->u.generic_ip;
|
||||
|
||||
if (!md || !table)
|
||||
return -EINVAL;
|
||||
|
||||
if (!_empty(new_dentry))
|
||||
return -ENOTEMPTY;
|
||||
|
||||
if (!strcmp(new_dentry->d_name.name, "ACTIVE")) {
|
||||
/* activate the table */
|
||||
dm_activate(md, table);
|
||||
|
||||
} else if (!strcmp(old_dentry->d_name.name, "ACTIVE")) {
|
||||
dm_suspend(md);
|
||||
|
||||
}
|
||||
|
||||
if (inode) {
|
||||
inode->i_nlink--;
|
||||
dput(new_dentry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sync_file(struct file *file, struct dentry *dentry,
|
||||
int datasync)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct address_space_operations dm_aops = {
|
||||
readpage: _readpage,
|
||||
writepage: _writepage,
|
||||
prepare_write: _prepare_write,
|
||||
commit_write: _commit_write
|
||||
};
|
||||
|
||||
static struct file_operations dm_file_operations = {
|
||||
read: generic_file_read,
|
||||
write: generic_file_write,
|
||||
fsync: _sync_file,
|
||||
release: _release,
|
||||
};
|
||||
|
||||
static struct file_operations dm_dir_operations = {
|
||||
read: generic_read_dir,
|
||||
readdir: dcache_readdir,
|
||||
fsync: _sync_file,
|
||||
};
|
||||
|
||||
static struct inode_operations root_dir_inode_operations = {
|
||||
lookup: _lookup,
|
||||
mkdir: _mkdir,
|
||||
rmdir: _rmdir,
|
||||
rename: _rename,
|
||||
};
|
||||
|
||||
static struct inode_operations dm_dir_inode_operations = {
|
||||
create: _create,
|
||||
lookup: _lookup,
|
||||
unlink: _unlink,
|
||||
rename: _rename,
|
||||
};
|
||||
|
||||
static struct super_operations dm_ops = {
|
||||
statfs: _statfs,
|
||||
put_inode: _put_inode,
|
||||
};
|
||||
|
||||
static struct super_block *_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 = DM_MAGIC;
|
||||
sb->s_op = &dm_ops;
|
||||
inode = _get_inode(sb, S_IFDIR | 0755, 0);
|
||||
inode->i_op = &root_dir_inode_operations;
|
||||
if (!inode)
|
||||
return NULL;
|
||||
|
||||
root = d_alloc_root(inode);
|
||||
if (!root) {
|
||||
iput(inode);
|
||||
return NULL;
|
||||
}
|
||||
sb->s_root = root;
|
||||
return sb;
|
||||
}
|
||||
|
||||
static DECLARE_FSTYPE(_fs_type, "dmfs", _read_super, FS_SINGLE);
|
||||
|
||||
int __init dm_fs_init(void)
|
||||
{
|
||||
int r;
|
||||
if ((r = register_filesystem(&_fs_type)))
|
||||
return r;
|
||||
|
||||
_mnt = kern_mount(&_fs_type);
|
||||
|
||||
if (IS_ERR(_mnt)) {
|
||||
unregister_filesystem(&_fs_type);
|
||||
return PTR_ERR(_mnt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit dm_fs_exit(void)
|
||||
{
|
||||
unregister_filesystem(&_fs_type);
|
||||
}
|
||||
125
driver/device-mapper/dm-linear.c
Normal file
125
driver/device-mapper/dm-linear.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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,
|
||||
struct text_region *args, void **context,
|
||||
dm_error_fn err, void *e_private)
|
||||
{
|
||||
struct linear_c *lc;
|
||||
unsigned int start;
|
||||
struct text_region word;
|
||||
char path[256]; /* FIXME: magic */
|
||||
int r = -EINVAL;
|
||||
|
||||
if (!(lc = kmalloc(sizeof(lc), GFP_KERNEL))) {
|
||||
err("couldn't allocate memory for linear context", e_private);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!dm_get_word(args, &word)) {
|
||||
err("couldn't get device path", e_private);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
dm_txt_copy(path, sizeof(path) - 1, &word);
|
||||
|
||||
if (!dm_get_number(args, &start)) {
|
||||
err("destination start not given", e_private);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if ((r = dm_table_get_device(t, path, &lc->dev))) {
|
||||
err("couldn't lookup device", e_private);
|
||||
r = -ENXIO;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
lc->delta = (int) start - (int) b;
|
||||
*context = lc;
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
kfree(lc);
|
||||
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;
|
||||
}
|
||||
|
||||
static struct target_type linear_target = {
|
||||
name: "linear",
|
||||
module: THIS_MODULE,
|
||||
ctr: linear_ctr,
|
||||
dtr: linear_dtr,
|
||||
map: linear_map,
|
||||
};
|
||||
|
||||
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");
|
||||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
||||
201
driver/device-mapper/dm-parse.c
Normal file
201
driver/device-mapper/dm-parse.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* dm-parse.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.
|
||||
*
|
||||
* 4/09/2001 - First version [Joe Thornber]
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
struct dm_table *dm_parse(extract_line_fn line_fn, void *l_private,
|
||||
dm_error_fn err_fn, void *e_private)
|
||||
{
|
||||
struct text_region line, word;
|
||||
struct dm_table *table = dm_table_create();
|
||||
struct target_type *ttype;
|
||||
offset_t start, size, high;
|
||||
char target_name[64];
|
||||
void *context;
|
||||
int last_line_good = 1, was_error = 0;
|
||||
|
||||
if (!table)
|
||||
return 0;
|
||||
|
||||
#define PARSE_ERROR(msg) {\
|
||||
last_line_good = 0;\
|
||||
was_error = 1;\
|
||||
err_fn(msg, e_private);\
|
||||
continue;}
|
||||
|
||||
while (line_fn(&line, l_private)) {
|
||||
|
||||
/*
|
||||
* each line is of the format:
|
||||
* <sector start> <length (sectors)> <target type> <args...>
|
||||
*/
|
||||
|
||||
/* the line may be blank ... */
|
||||
dm_eat_space(&line);
|
||||
if (dm_empty_tok(&line) || (*line.b == '#'))
|
||||
continue;
|
||||
|
||||
/* sector start */
|
||||
if (!dm_get_number(&line, &start))
|
||||
PARSE_ERROR("expecting a number for sector start");
|
||||
|
||||
/* length */
|
||||
if (!dm_get_number(&line, &size))
|
||||
PARSE_ERROR("expecting a number for region length");
|
||||
|
||||
/* target type */
|
||||
if (!dm_get_word(&line, &word))
|
||||
PARSE_ERROR("target type missing");
|
||||
|
||||
/* we have to copy the target type to a C str */
|
||||
dm_txt_copy(target_name, sizeof(target_name), &word);
|
||||
|
||||
/* lookup the target type */
|
||||
if (!(ttype = dm_get_target_type(target_name)))
|
||||
PARSE_ERROR("unable to find target type");
|
||||
|
||||
/* check there isn't a gap, but only if the last target
|
||||
parsed ok. */
|
||||
if (last_line_good &&
|
||||
|
||||
((table->num_targets &&
|
||||
start != table->highs[table->num_targets - 1] + 1) ||
|
||||
(!table->num_targets && start)))
|
||||
PARSE_ERROR("gap in target ranges");
|
||||
|
||||
/* build the target */
|
||||
if (ttype->ctr(table, start, size, &line, &context,
|
||||
err_fn, e_private))
|
||||
PARSE_ERROR("target constructor failed");
|
||||
|
||||
/* no point registering the target
|
||||
if there was an error. */
|
||||
if (was_error) {
|
||||
ttype->dtr(table, context);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* add the target to the table */
|
||||
high = start + (size - 1);
|
||||
if (dm_table_add_target(table, high, ttype, context))
|
||||
PARSE_ERROR("internal error adding target to table");
|
||||
}
|
||||
|
||||
#undef PARSE_ERROR
|
||||
|
||||
if (was_error || dm_table_complete(table)) {
|
||||
dm_table_destroy(table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert the text in txt to an unsigned int,
|
||||
* returns 0 on failure.
|
||||
*/
|
||||
int dm_get_number(struct text_region *txt, unsigned int *n)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
dm_eat_space(txt);
|
||||
if (dm_empty_tok(txt))
|
||||
return 0;
|
||||
|
||||
*n = simple_strtoul(txt->b, &ptr, 10);
|
||||
if (ptr == txt->b)
|
||||
return 0;
|
||||
|
||||
txt->b = ptr;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* extracts text up to the next '\n'.
|
||||
*/
|
||||
int dm_get_line(struct text_region *txt, struct text_region *line)
|
||||
{
|
||||
const char *ptr;
|
||||
|
||||
dm_eat_space(txt);
|
||||
if (dm_empty_tok(txt))
|
||||
return 0;
|
||||
|
||||
ptr = line->b = txt->b;
|
||||
while((ptr != txt->e) && (*ptr != '\n'))
|
||||
ptr++;
|
||||
|
||||
txt->b = line->e = ptr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* extracts the next non-whitespace token from the file.
|
||||
*/
|
||||
int dm_get_word(struct text_region *txt, struct text_region *word)
|
||||
{
|
||||
const char *ptr;
|
||||
|
||||
dm_eat_space(txt);
|
||||
|
||||
if (dm_empty_tok(txt))
|
||||
return 0;
|
||||
|
||||
word->b = txt->b;
|
||||
for (ptr = word->b = txt->b;
|
||||
ptr != txt->e && !isspace((int) *ptr); ptr++)
|
||||
;
|
||||
|
||||
word->e = txt->b = ptr;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy a text region into a traditional C str.
|
||||
*/
|
||||
void dm_txt_copy(char *dest, size_t max, struct text_region *txt)
|
||||
{
|
||||
size_t len = txt->e - txt->b;
|
||||
if (len > --max)
|
||||
len = max;
|
||||
strncpy(dest, txt->b, len);
|
||||
dest[len] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* skip leading whitespace
|
||||
*/
|
||||
void dm_eat_space(struct text_region *txt)
|
||||
{
|
||||
while(txt->b != txt->e && isspace((int) *txt->b))
|
||||
(txt->b)++;
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(dm_get_number);
|
||||
EXPORT_SYMBOL(dm_get_word);
|
||||
EXPORT_SYMBOL(dm_txt_copy);
|
||||
EXPORT_SYMBOL(dm_eat_space);
|
||||
338
driver/device-mapper/dm-table.c
Normal file
338
driver/device-mapper/dm-table.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* 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]);
|
||||
vfree(t->highs);
|
||||
|
||||
/* free the targets */
|
||||
for (i = 0; i < t->num_targets; i++) {
|
||||
struct target *tgt = &t->targets[i];
|
||||
if (tgt->private)
|
||||
tgt->type->dtr(t, tgt->private);
|
||||
}
|
||||
|
||||
/* 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_bdev->bd_dev;
|
||||
|
||||
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);
|
||||
181
driver/device-mapper/dm-target.c
Normal file
181
driver/device-mapper/dm-target.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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,
|
||||
struct text_region *args, void **context,
|
||||
dm_error_fn err, void *e_private)
|
||||
{
|
||||
*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);
|
||||
|
||||
920
driver/device-mapper/dm.c
Normal file
920
driver/device-mapper/dm.c
Normal file
@@ -0,0 +1,920 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
/* defines for blk.h */
|
||||
#define MAJOR_NR DM_BLK_MAJOR
|
||||
#define DEVICE_NR(device) MINOR(device) /* has no partition bits */
|
||||
#define DEVICE_NAME "device-mapper" /* name for messaging */
|
||||
#define DEVICE_NO_RANDOM /* no entropy to contribute */
|
||||
#define DEVICE_OFF(d) /* do-nothing */
|
||||
|
||||
#include <linux/blk.h>
|
||||
#include <linux/blkpg.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
|
||||
|
||||
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 dm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
init_rwsem(&_dev_lock);
|
||||
|
||||
if (!_io_hook_cache)
|
||||
_io_hook_cache = kmem_cache_create("dm io hooks",
|
||||
sizeof(struct io_hook),
|
||||
0, 0, NULL, NULL);
|
||||
|
||||
if (!_io_hook_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((ret = dm_fs_init()) || (ret = dm_target_init()))
|
||||
return ret;
|
||||
|
||||
/* set up the arrays */
|
||||
read_ahead[MAJOR_NR] = DEFAULT_READ_AHEAD;
|
||||
blk_size[MAJOR_NR] = _block_size;
|
||||
blksize_size[MAJOR_NR] = _blksize_size;
|
||||
hardsect_size[MAJOR_NR] = _hardsect_size;
|
||||
|
||||
if (devfs_register_blkdev(MAJOR_NR, _name, &dm_blk_dops) < 0) {
|
||||
printk(KERN_ERR "%s -- register_blkdev failed\n", _name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), 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;
|
||||
}
|
||||
|
||||
static void dm_exit(void)
|
||||
{
|
||||
if (kmem_cache_destroy(_io_hook_cache))
|
||||
WARN("it looks like there are still some io_hooks allocated");
|
||||
_io_hook_cache = 0;
|
||||
|
||||
dm_fs_exit();
|
||||
|
||||
if (devfs_unregister_blkdev(MAJOR_NR, _name) < 0)
|
||||
printk(KERN_ERR "%s -- unregister_blkdev failed\n", _name);
|
||||
|
||||
read_ahead[MAJOR_NR] = 0;
|
||||
blk_size[MAJOR_NR] = 0;
|
||||
blksize_size[MAJOR_NR] = 0;
|
||||
hardsect_size[MAJOR_NR] = 0;
|
||||
|
||||
printk(KERN_INFO "%s %d.%d.%d cleaned up\n", _name,
|
||||
_version[0], _version[1], _version[2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
MOD_INC_USE_COUNT;
|
||||
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;
|
||||
|
||||
MOD_DEC_USE_COUNT;
|
||||
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 BLKROGET:
|
||||
case BLKROSET:
|
||||
#if 0
|
||||
case BLKELVSET:
|
||||
case BLKELVGET:
|
||||
#endif
|
||||
return blk_ioctl(inode->i_dev, command, a);
|
||||
break;
|
||||
|
||||
case BLKGETSIZE:
|
||||
size = VOLUME_SIZE(minor);
|
||||
if (copy_to_user((void *) a, &size, sizeof (long)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
case BLKFLSBUF:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
fsync_dev(inode->i_rdev);
|
||||
invalidate_buffers(inode->i_rdev);
|
||||
return 0;
|
||||
|
||||
case BLKRAGET:
|
||||
if (copy_to_user
|
||||
((void *) a, &read_ahead[MAJOR(inode->i_rdev)],
|
||||
sizeof (long)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case BLKRASET:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
read_ahead[MAJOR(inode->i_rdev)] = a;
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
||||
if (!md->devfs_entry)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
close_devices(&md->map->devices);
|
||||
md->map = 0;
|
||||
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
|
||||
};
|
||||
|
||||
/*
|
||||
* module hooks
|
||||
*/
|
||||
module_init(dm_init);
|
||||
module_exit(dm_exit);
|
||||
|
||||
MODULE_DESCRIPTION("device-mapper driver");
|
||||
MODULE_AUTHOR("Joe Thornber <thornber@btconnect.com>");
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
276
driver/device-mapper/dm.h
Normal file
276
driver/device-mapper/dm.h
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* 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];
|
||||
struct inode *inode;
|
||||
|
||||
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);
|
||||
|
||||
/* dm-parse.c */
|
||||
typedef int (*extract_line_fn)(struct text_region *line,
|
||||
void *private);
|
||||
|
||||
struct dm_table *dm_parse(extract_line_fn line_fn, void *line_private,
|
||||
dm_error_fn err_fn, void *err_private);
|
||||
|
||||
|
||||
static inline int dm_empty_tok(struct text_region *txt)
|
||||
{
|
||||
return txt->b >= txt->e;
|
||||
}
|
||||
|
||||
/* dm-fs.c */
|
||||
int dm_fs_init(void);
|
||||
void dm_fs_exit(void);
|
||||
|
||||
|
||||
|
||||
#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
|
||||
156
driver/device-mapper/dmfs-error.c
Normal file
156
driver/device-mapper/dmfs-error.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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 "dm.h"
|
||||
|
||||
struct dmfs_error {
|
||||
struct list_head list;
|
||||
unsigned len;
|
||||
char *msg;
|
||||
};
|
||||
|
||||
void dmfs_add_error(struct dm_table *t, unsigned num, char *str)
|
||||
{
|
||||
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, &t->errors);
|
||||
}
|
||||
}
|
||||
|
||||
void dmfs_zap_errors(struct dm_table *t)
|
||||
{
|
||||
struct dmfs_error *e;
|
||||
|
||||
while(!list_empty(&t->errors)) {
|
||||
e = list_entry(t->errors.next, struct dmfs_error, list);
|
||||
list_del(&e->list);
|
||||
kfree(e);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dmfs_error *find_initial_message(struct dm_table *t, loff_t *pos)
|
||||
{
|
||||
struct dmfs_error *e;
|
||||
struct list_head *tmp, *head;
|
||||
|
||||
tmp = head = &t->errors;
|
||||
for(;;) {
|
||||
tmp = tmp->next;
|
||||
if (tmp == head)
|
||||
break;
|
||||
e = list_entry(tmp, struct dmfs_error, list);
|
||||
if (*pos < e->len)
|
||||
return e;
|
||||
(*pos) -= e->len;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int copy_sequence(struct dm_table *t, struct dmfs_error *e, char *buf,
|
||||
size_t size, loff_t offset)
|
||||
{
|
||||
char *from;
|
||||
int amount;
|
||||
int copied = 0;
|
||||
|
||||
do {
|
||||
from = e->msg + offset;
|
||||
amount = e->len - offset;
|
||||
|
||||
if (copy_to_user(buf, from, amount))
|
||||
return -EFAULT;
|
||||
|
||||
buf += amount;
|
||||
copied += amount;
|
||||
size -= amount;
|
||||
offset = 0;
|
||||
|
||||
if (e->list.next == &t->errors)
|
||||
break;
|
||||
e = list_entry(e->list.next, struct dmfs_error, list);
|
||||
} while(size > 0);
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
static ssize_t dmfs_error_read(struct file *file, char *buf, size_t size, loff_t *pos)
|
||||
{
|
||||
struct dmfs_i *dmi = DMFS_I(file->f_dentry->d_parent->d_inode);
|
||||
struct dm_table *t = dmi->table;
|
||||
int copied = 0;
|
||||
loff_t offset = *pos;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, buf, size))
|
||||
return -EFAULT;
|
||||
|
||||
down(&dmi->sem);
|
||||
if (dmi->table) {
|
||||
struct dmfs_error *e = find_initial_message(t, &offset);
|
||||
if (e) {
|
||||
copied = copy_sequence(t, e, buf, size, offset);
|
||||
if (copied > 0)
|
||||
(*pos) += copied;
|
||||
}
|
||||
}
|
||||
up(&dmi->sem);
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int dmfs_error_sync(struct file *file, struct dentry *dentry, int datasync)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations dmfs_error_file_operations = {
|
||||
read: dmfs_error_read,
|
||||
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 = new_inode(dir->i_sb);
|
||||
|
||||
if (inode) {
|
||||
inode->i_mode = mode | S_IFREG;
|
||||
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;
|
||||
inode->i_fop = &dmfs_error_file_operations;
|
||||
inode->i_op = &dmfs_error_inode_operations;
|
||||
}
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
282
driver/device-mapper/dmfs-lv.c
Normal file
282
driver/device-mapper/dmfs-lv.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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)->table->errors)))
|
||||
goto err_out;
|
||||
|
||||
dentry = nd.dentry;
|
||||
file.f_dentry = nd.dentry->d_parent;
|
||||
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_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: dentry->d_parent };
|
||||
|
||||
if (!(inode->i_mode & S_IFLNK))
|
||||
return -EINVAL;
|
||||
|
||||
dm_suspend(DMFS_I(dir)->md);
|
||||
allow_write_access(&file);
|
||||
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:
|
||||
DMFS_I(inode)->dentry = NULL;
|
||||
out_allow_write:
|
||||
{
|
||||
struct file file = { f_dentry: de->d_parent };
|
||||
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_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;
|
||||
}
|
||||
|
||||
|
||||
164
driver/device-mapper/dmfs-root.c
Normal file
164
driver/device-mapper/dmfs-root.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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 = new_inode(sb);
|
||||
|
||||
if (inode) {
|
||||
inode->i_mode = mode | S_IFDIR;
|
||||
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;
|
||||
inode->i_fop = &dmfs_root_file_operations;
|
||||
inode->i_op = &dmfs_root_inode_operations;
|
||||
}
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
|
||||
63
driver/device-mapper/dmfs-status.c
Normal file
63
driver/device-mapper/dmfs-status.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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 = new_inode(dir->i_sb);
|
||||
|
||||
if (inode) {
|
||||
inode->i_mode = mode | S_IFREG;
|
||||
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;
|
||||
inode->i_fop = &dmfs_status_file_operations;
|
||||
inode->i_op = &dmfs_status_inode_operations;
|
||||
}
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
112
driver/device-mapper/dmfs-super.c
Normal file
112
driver/device-mapper/dmfs-super.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 "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);
|
||||
kfree(dmi);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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);
|
||||
struct dmfs_i *dmi;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
inode->u.generic_ip = dmi;
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
379
driver/device-mapper/dmfs-table.c
Normal file
379
driver/device-mapper/dmfs-table.c
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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 void dmfs_parse_line(struct dm_table *t, unsigned num, char *str)
|
||||
{
|
||||
char *p = str;
|
||||
const char *tok;
|
||||
offset_t start, size, high;
|
||||
void *context;
|
||||
struct target_type *ttype;
|
||||
int rv = 0;
|
||||
char *msg;
|
||||
|
||||
printk("dmfs_parse_line: (%s)\n", str);
|
||||
|
||||
msg = "No start argument";
|
||||
tok = next_token(&p);
|
||||
if (!tok)
|
||||
goto out;
|
||||
start = simple_strtoul(tok, NULL, 10);
|
||||
|
||||
msg = "No size argument";
|
||||
tok = next_token(&p);
|
||||
if (!tok)
|
||||
goto out;
|
||||
size = simple_strtoul(tok, NULL, 10);
|
||||
|
||||
msg = "Gap in table";
|
||||
if (start != start_of_next_range(t))
|
||||
goto out;
|
||||
|
||||
msg = "No target type";
|
||||
tok = next_token(&p);
|
||||
if (!tok)
|
||||
goto out;
|
||||
|
||||
msg = "Target type unknown";
|
||||
ttype = dm_get_target_type(tok);
|
||||
if (ttype) {
|
||||
msg = "This message should never appear (constructor error)";
|
||||
rv = ttype->ctr(t, start, size, p, &context);
|
||||
msg = context;
|
||||
if (rv == 0) {
|
||||
printk("dmfs_parse: %ul %ul %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;
|
||||
ttype->dtr(t, context);
|
||||
}
|
||||
dm_put_target_type(ttype);
|
||||
}
|
||||
out:
|
||||
dmfs_add_error(t, num, 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;
|
||||
}
|
||||
|
||||
static int dmfs_parse_page(struct dm_table *t, char *buf, int end, unsigned long end_index, char *tmp, unsigned long *tmpl, int *num)
|
||||
{
|
||||
int copied;
|
||||
unsigned long len = end ? end_index : PAGE_CACHE_SIZE - 1;
|
||||
|
||||
do {
|
||||
int flag = 0;
|
||||
copied = dmfs_copy(tmp + *tmpl, PAGE_SIZE - *tmpl - 1, buf, len, &flag);
|
||||
buf += copied;
|
||||
len -= copied;
|
||||
if (*tmpl + copied == PAGE_SIZE - 1)
|
||||
goto line_too_long;
|
||||
(*tmpl) += copied;
|
||||
if (flag || (len == 0 && end)) {
|
||||
*(tmp + *tmpl) = 0;
|
||||
if (dmfs_line_is_not_comment(tmp))
|
||||
dmfs_parse_line(t, *num, tmp);
|
||||
(*num)++;
|
||||
*tmpl = 0;
|
||||
}
|
||||
} while(len > 0);
|
||||
return 0;
|
||||
|
||||
line_too_long:
|
||||
dmfs_add_error(t, *num, "Line too long");
|
||||
/* FIXME: Add code to recover from this */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct dm_table *dmfs_parse(struct inode *inode)
|
||||
{
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
unsigned long index = 0;
|
||||
unsigned long end_index, end_offset;
|
||||
unsigned long page;
|
||||
unsigned long rem = 0;
|
||||
struct dm_table *t;
|
||||
struct page *pg;
|
||||
int num = 0;
|
||||
|
||||
if (inode->i_size == 0)
|
||||
return NULL;
|
||||
|
||||
page = __get_free_page(GFP_KERNEL);
|
||||
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
t = dm_table_create();
|
||||
if (!t) {
|
||||
free_page(page);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
end_index = inode->i_size >> PAGE_CACHE_SHIFT;
|
||||
end_offset = inode->i_size & (PAGE_CACHE_SIZE - 1);
|
||||
|
||||
do {
|
||||
pg = find_get_page(mapping, index);
|
||||
|
||||
if (pg) {
|
||||
char *kaddr;
|
||||
int rv;
|
||||
|
||||
if (!Page_Uptodate(pg))
|
||||
goto broken;
|
||||
|
||||
kaddr = kmap(pg);
|
||||
rv = dmfs_parse_page(t, kaddr, (index == end_index), end_offset, (char *)page, &rem, &num);
|
||||
kunmap(pg);
|
||||
page_cache_release(pg);
|
||||
|
||||
if (rv)
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
index++;
|
||||
} while(index <= end_index);
|
||||
|
||||
free_page(page);
|
||||
if (list_empty(&t->errors)) {
|
||||
dm_table_complete(t);
|
||||
}
|
||||
|
||||
return t;
|
||||
|
||||
broken:
|
||||
printk(KERN_ERR "dmfs_parse: Page not uptodate\n");
|
||||
page_cache_release(pg);
|
||||
free_page(page);
|
||||
dm_table_destroy(t);
|
||||
return NULL;
|
||||
|
||||
parse_error:
|
||||
printk(KERN_ERR "dmfs_parse: Parse error\n");
|
||||
free_page(page);
|
||||
dm_table_destroy(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
table = dmfs_parse(dentry->d_parent->d_inode);
|
||||
|
||||
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_writepage(struct page *page)
|
||||
{
|
||||
SetPageDirty(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: dmfs_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 = new_inode(dir->i_sb);
|
||||
|
||||
if (inode) {
|
||||
inode->i_mode = mode | S_IFREG;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
137
driver/device-mapper/dmfs-tdir.c
Normal file
137
driver/device-mapper/dmfs-tdir.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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_inode(sb, mode | S_IFDIR);
|
||||
|
||||
if (inode) {
|
||||
inode->i_fop = &dmfs_tdir_file_operations;
|
||||
inode->i_op = &dmfs_tdir_inode_operations;
|
||||
}
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
|
||||
88
driver/device-mapper/patches/00_bh-async-3
Normal file
88
driver/device-mapper/patches/00_bh-async-3
Normal file
@@ -0,0 +1,88 @@
|
||||
diff -urN 2.4.7pre6/fs/buffer.c bh_async/fs/buffer.c
|
||||
--- 2.4.7pre6/fs/buffer.c Wed Jul 11 06:03:18 2001
|
||||
+++ bh_async/fs/buffer.c Thu Jul 12 07:55:08 2001
|
||||
@@ -827,10 +827,11 @@
|
||||
* that unlock the page..
|
||||
*/
|
||||
spin_lock_irqsave(&page_uptodate_lock, flags);
|
||||
+ mark_buffer_async(bh, 0);
|
||||
unlock_buffer(bh);
|
||||
tmp = bh->b_this_page;
|
||||
while (tmp != bh) {
|
||||
- if (tmp->b_end_io == end_buffer_io_async && buffer_locked(tmp))
|
||||
+ if (buffer_async(tmp) && buffer_locked(tmp))
|
||||
goto still_busy;
|
||||
tmp = tmp->b_this_page;
|
||||
}
|
||||
@@ -862,8 +863,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
-void set_buffer_async_io(struct buffer_head *bh) {
|
||||
+inline void set_buffer_async_io(struct buffer_head *bh) {
|
||||
bh->b_end_io = end_buffer_io_async ;
|
||||
+ mark_buffer_async(bh, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1553,7 +1555,7 @@
|
||||
/* Stage 2: lock the buffers, mark them clean */
|
||||
do {
|
||||
lock_buffer(bh);
|
||||
- bh->b_end_io = end_buffer_io_async;
|
||||
+ set_buffer_async_io(bh);
|
||||
get_bh(bh);
|
||||
set_bit(BH_Uptodate, &bh->b_state);
|
||||
clear_bit(BH_Dirty, &bh->b_state);
|
||||
@@ -1755,7 +1757,7 @@
|
||||
for (i = 0; i < nr; i++) {
|
||||
struct buffer_head * bh = arr[i];
|
||||
lock_buffer(bh);
|
||||
- bh->b_end_io = end_buffer_io_async;
|
||||
+ set_buffer_async_io(bh);
|
||||
get_bh(bh);
|
||||
}
|
||||
|
||||
@@ -2200,7 +2202,7 @@
|
||||
lock_buffer(bh);
|
||||
bh->b_blocknr = *(b++);
|
||||
set_bit(BH_Mapped, &bh->b_state);
|
||||
- bh->b_end_io = end_buffer_io_async;
|
||||
+ set_buffer_async_io(bh);
|
||||
get_bh(bh);
|
||||
bh = bh->b_this_page;
|
||||
} while (bh != head);
|
||||
diff -urN 2.4.7pre6/include/linux/fs.h bh_async/include/linux/fs.h
|
||||
--- 2.4.7pre6/include/linux/fs.h Wed Jul 11 06:03:19 2001
|
||||
+++ bh_async/include/linux/fs.h Thu Jul 12 07:54:26 2001
|
||||
@@ -215,6 +215,7 @@
|
||||
BH_New, /* 1 if the buffer is new and not yet written out */
|
||||
BH_Protected, /* 1 if the buffer is protected */
|
||||
BH_JBD, /* 1 if it has an attached journal_head */
|
||||
+ BH_Async, /* 1 if the buffer is under end_buffer_io_async I/O */
|
||||
|
||||
BH_PrivateStart,/* not a state bit, but the first bit available
|
||||
* for private allocation by other entities
|
||||
@@ -275,6 +276,7 @@
|
||||
#define buffer_mapped(bh) __buffer_state(bh,Mapped)
|
||||
#define buffer_new(bh) __buffer_state(bh,New)
|
||||
#define buffer_protected(bh) __buffer_state(bh,Protected)
|
||||
+#define buffer_async(bh) __buffer_state(bh,Async)
|
||||
|
||||
#define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK)
|
||||
|
||||
@@ -1110,6 +1112,14 @@
|
||||
extern void FASTCALL(mark_buffer_dirty(struct buffer_head *bh));
|
||||
|
||||
#define atomic_set_buffer_dirty(bh) test_and_set_bit(BH_Dirty, &(bh)->b_state)
|
||||
+
|
||||
+static inline void mark_buffer_async(struct buffer_head * bh, int on)
|
||||
+{
|
||||
+ if (on)
|
||||
+ set_bit(BH_Async, &bh->b_state);
|
||||
+ else
|
||||
+ clear_bit(BH_Async, &bh->b_state);
|
||||
+}
|
||||
|
||||
/*
|
||||
* If an error happens during the make_request, this function
|
||||
10
driver/device-mapper/patches/00_config
Normal file
10
driver/device-mapper/patches/00_config
Normal file
@@ -0,0 +1,10 @@
|
||||
--- linux-2.4.9-ac5/drivers/md/Config.in Sun Mar 11 13:33:24 2001
|
||||
+++ linux/drivers/md/Config.in Thu Sep 13 18:02:17 2001
|
||||
@@ -13,5 +13,7 @@
|
||||
dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $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
|
||||
13
driver/device-mapper/patches/00_config_uml
Normal file
13
driver/device-mapper/patches/00_config_uml
Normal 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
|
||||
|
||||
|
||||
1732
driver/device-mapper/patches/00_latest
Normal file
1732
driver/device-mapper/patches/00_latest
Normal file
File diff suppressed because it is too large
Load Diff
25
driver/device-mapper/patches/00_makefile
Normal file
25
driver/device-mapper/patches/00_makefile
Normal file
@@ -0,0 +1,25 @@
|
||||
--- linux-2.4.9-ac5/drivers/md/Makefile Sat Sep 1 16:24:46 2001
|
||||
+++ linux/drivers/md/Makefile Fri Sep 14 09:12:39 2001
|
||||
@@ -7,6 +7,7 @@
|
||||
export-objs := md.o xor.o
|
||||
list-multi := lvm-mod.o
|
||||
lvm-mod-objs := lvm.o lvm-snap.o lvm-fs.o
|
||||
+dm-mod-objs := dm.o dm-table.o dm-target.o dm-fs.o dm-parse.o dm-blkdev.o
|
||||
|
||||
# Note: link order is important. All raid personalities
|
||||
# and xor.o must come before md.o, as they each initialise
|
||||
@@ -19,8 +20,14 @@
|
||||
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-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)
|
||||
+
|
||||
13
driver/device-mapper/patches/INDEX
Normal file
13
driver/device-mapper/patches/INDEX
Normal 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.
|
||||
|
||||
30
driver/user_mode_linux/README
Normal file
30
driver/user_mode_linux/README
Normal 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.
|
||||
299
driver/user_mode_linux/config-uml
Normal file
299
driver/user_mode_linux/config-uml
Normal 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
179
driver/user_mode_linux/setup-uml
Executable 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
30
driver/user_mode_linux/uml-lvm.patch
Normal file
30
driver/user_mode_linux/uml-lvm.patch
Normal 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
|
||||
|
||||
BIN
driver/user_mode_linux/uml.patch.bz2
Normal file
BIN
driver/user_mode_linux/uml.patch.bz2
Normal file
Binary file not shown.
16
include/.symlinks
Normal file
16
include/.symlinks
Normal file
@@ -0,0 +1,16 @@
|
||||
../lib/activate/activate.h
|
||||
../lib/config/config.h
|
||||
../lib/datastruct/hash.h
|
||||
../lib/datastruct/list.h
|
||||
../lib/datastruct/lvm-types.h
|
||||
../lib/device/dev-cache.h
|
||||
../lib/device/device.h
|
||||
../lib/display/display.h
|
||||
../lib/filters/filter.h
|
||||
../lib/format1/format1.h
|
||||
../lib/log/log.h
|
||||
../lib/metadata/metadata.h
|
||||
../lib/mm/dbg_malloc.h
|
||||
../lib/mm/pool.h
|
||||
../lib/mm/xlate.h
|
||||
../lib/uuid/uuid.h
|
||||
41
include/Makefile.in
Normal file
41
include/Makefile.in
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software
|
||||
#
|
||||
# This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
# MA 02111-1307, USA
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
LN_S = @LN_S@
|
||||
|
||||
all: .symlinks_created
|
||||
|
||||
.symlinks_created: .symlinks
|
||||
find . -maxdepth 1 -type l -exec $(RM) \{\} \;
|
||||
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
|
||||
|
||||
.PHONY: clean distclean all
|
||||
|
||||
39
lib/Makefile.in
Normal file
39
lib/Makefile.in
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
#
|
||||
# This file is released under the GPL.
|
||||
#
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES=\
|
||||
activate/activate.c \
|
||||
config/config.c \
|
||||
datastruct/hash.c \
|
||||
device/dev-cache.c \
|
||||
device/dev-io.c \
|
||||
device/device.c \
|
||||
display/display.c \
|
||||
filters/filter.c \
|
||||
format1/disk-rep.c \
|
||||
format1/format1.c \
|
||||
format1/import-export.c \
|
||||
format1/layout.c \
|
||||
format1/vg_number.c \
|
||||
log/log.c \
|
||||
metadata/metadata.c \
|
||||
mm/dbg_malloc.c \
|
||||
mm/pool.c \
|
||||
uuid/uuid.c
|
||||
|
||||
TARGETS=liblvm.a
|
||||
|
||||
include ../make.tmpl
|
||||
|
||||
liblvm.a: $(OBJECTS)
|
||||
$(RM) $@
|
||||
$(AR) r $@ $(OBJECTS)
|
||||
$(RANLIB) $@
|
||||
|
||||
35
lib/activate/activate.c
Normal file
35
lib/activate/activate.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "metadata.h"
|
||||
#include "activate.h"
|
||||
|
||||
int lv_activate(struct volume_group *vg, struct logical_volume *lv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lv_deactivate(struct volume_group *vg, struct logical_volume *lv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lvs_in_vg_activated(struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int activate_lvs_in_vg(struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int deactivate_lvs_in_vg(struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
28
lib/activate/activate.h
Normal file
28
lib/activate/activate.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef LVM_ACTIVATE_H
|
||||
#define LVM_ACTIVATE_H
|
||||
|
||||
/* FIXME Snapshot handling? */
|
||||
|
||||
int lv_activate(struct volume_group *vg,
|
||||
struct logical_volume *lv);
|
||||
|
||||
int lv_deactivate(struct volume_group *vg,
|
||||
struct logical_volume *lv);
|
||||
|
||||
/* Return number of LVs in the VG that are active */
|
||||
int lvs_in_vg_activated(struct volume_group *vg);
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif
|
||||
31
lib/activate/dmfs-driver.h
Normal file
31
lib/activate/dmfs-driver.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef DMFS_INTERFACE_H
|
||||
#define DMFS_INTERFACE_H
|
||||
|
||||
struct dmfs;
|
||||
|
||||
struct dmfs *dmfs_create(void);
|
||||
void dmfs_destroy(struct dmfs *dm);
|
||||
|
||||
int dmfs_dev_is_present(struct dmfs *dm, const char *dev);
|
||||
int dmfs_dev_is_active(struct dmfs *dm, const char *dev);
|
||||
|
||||
int dmfs_table_is_present(struct dmfs *dm, const char *dev, const char *table);
|
||||
int dmfs_table_is_active(struct dmfs *dm, const char *dev, const char *table);
|
||||
|
||||
int dmfs_dev_create(struct dmfs *dm, const char *name);
|
||||
int dmfs_dev_load_table(struct dmfs *dm, const char *dev,
|
||||
const char *table, const char *file);
|
||||
int dmfs_dev_drop_table(struct dmfs *dm, const char *dev, const char *table);
|
||||
|
||||
int dmfs_dev_activate_table(struct dmfs *dm, const char *dev,
|
||||
const char *table);
|
||||
|
||||
int dmfs_dev_deactivate(struct dmfs *dm, const char *dev);
|
||||
|
||||
#endif
|
||||
35
lib/activate/table-build.c
Normal file
35
lib/activate/table-build.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "table-build.c"
|
||||
|
||||
/* FIXME: optimise linear runs */
|
||||
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;
|
||||
}
|
||||
13
lib/activate/table-build.h
Normal file
13
lib/activate/table-build.h
Normal 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
|
||||
595
lib/config/config.c
Normal file
595
lib/config/config.c
Normal file
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "pool.h"
|
||||
#include "log.h"
|
||||
|
||||
enum {
|
||||
TOK_INT,
|
||||
TOK_FLOAT,
|
||||
TOK_STRING,
|
||||
TOK_EQ,
|
||||
TOK_SECTION_B,
|
||||
TOK_SECTION_E,
|
||||
TOK_ARRAY_B,
|
||||
TOK_ARRAY_E,
|
||||
TOK_IDENTIFIER,
|
||||
TOK_COMMA,
|
||||
TOK_EOF
|
||||
};
|
||||
|
||||
struct parser {
|
||||
const char *fb, *fe; /* file limits */
|
||||
|
||||
int t; /* token limits and type */
|
||||
const char *tb, *te;
|
||||
|
||||
int fd; /* descriptor for file being parsed */
|
||||
int line; /* line number we are on */
|
||||
|
||||
struct pool *mem;
|
||||
};
|
||||
|
||||
struct cs {
|
||||
struct config_file cf;
|
||||
struct pool *mem;
|
||||
};
|
||||
|
||||
static void _get_token(struct parser *p);
|
||||
static void _eat_space(struct parser *p);
|
||||
static struct config_node *_file(struct parser *p);
|
||||
static struct config_node *_section(struct parser *p);
|
||||
static struct config_value *_value(struct parser *p);
|
||||
static struct config_value *_type(struct parser *p);
|
||||
static void _parse_error(struct parser *p, const char *file, int line,
|
||||
const char *mess);
|
||||
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
|
||||
|
||||
#define match(t) do {\
|
||||
if (!_match_aux(p, (t))) {\
|
||||
_parse_error(p, __FILE__, __LINE__, "unexpected token"); \
|
||||
return 0;\
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
/*
|
||||
* public interface
|
||||
*/
|
||||
struct config_file *create_config_file()
|
||||
{
|
||||
struct cs *c;
|
||||
struct pool *mem = pool_create(10 * 1024);
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(c = pool_alloc(mem, sizeof(*c)))) {
|
||||
stack;
|
||||
pool_destroy(mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->mem = mem;
|
||||
c->cf.root = (struct config_node *)NULL;
|
||||
return &c->cf;
|
||||
}
|
||||
|
||||
void destroy_config_file(struct config_file *cf)
|
||||
{
|
||||
pool_destroy(((struct cs *) cf)->mem);
|
||||
}
|
||||
|
||||
int read_config(struct config_file *cf, const char *file)
|
||||
{
|
||||
struct cs *c = (struct cs *) cf;
|
||||
struct parser *p;
|
||||
struct stat info;
|
||||
int r = 1, fd;
|
||||
|
||||
if (!(p = pool_alloc(c->mem, sizeof(*p)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
p->mem = c->mem;
|
||||
|
||||
/* memory map the file */
|
||||
if (stat(file, &info) || S_ISDIR(info.st_mode)) {
|
||||
log_sys_error("stat", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((fd = open(file, O_RDONLY)) < 0) {
|
||||
log_sys_error("open", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->fb = mmap((caddr_t) 0, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (p->fb == MAP_FAILED) {
|
||||
log_sys_error("mmap", file);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
p->fe = p->fb + info.st_size;
|
||||
|
||||
/* parse */
|
||||
p->tb = p->te = p->fb;
|
||||
p->line = 1;
|
||||
_get_token(p);
|
||||
if (!(cf->root = _file(p))) {
|
||||
stack;
|
||||
r = 0;
|
||||
}
|
||||
|
||||
/* unmap the file */
|
||||
if (munmap((char *) p->fb, info.st_size)) {
|
||||
log_sys_error("munmap", file);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _write_value(FILE *fp, struct config_value *v)
|
||||
{
|
||||
switch (v->type) {
|
||||
case CFG_STRING:
|
||||
fprintf(fp, "\"%s\"", v->v.str);
|
||||
break;
|
||||
|
||||
case CFG_FLOAT:
|
||||
fprintf(fp, "%f", v->v.r);
|
||||
break;
|
||||
|
||||
case CFG_INT:
|
||||
fprintf(fp, "%d", v->v.i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int _write_config(struct config_node *n, FILE *fp, int level)
|
||||
{
|
||||
char space[MAX_INDENT + 1];
|
||||
int l = (level < MAX_INDENT) ? level : MAX_INDENT;
|
||||
int i;
|
||||
|
||||
if (!n)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
space[i] = ' ';
|
||||
space[i] = '\0';
|
||||
|
||||
while (n) {
|
||||
fprintf(fp, "%s%s", space, n->key);
|
||||
if (!n->v) {
|
||||
/* it's a sub section */
|
||||
fprintf(fp, " {\n");
|
||||
_write_config(n->child, fp, level + 1);
|
||||
fprintf(fp, "%s}", space);
|
||||
} else {
|
||||
/* it's a value */
|
||||
struct config_value *v = n->v;
|
||||
fprintf(fp, "=");
|
||||
if (v->next) {
|
||||
fprintf(fp, "[");
|
||||
while (v) {
|
||||
_write_value(fp, v);
|
||||
v = v->next;
|
||||
if (v)
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
fprintf(fp, "]");
|
||||
} else
|
||||
_write_value(fp, v);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
n = n->sib;
|
||||
}
|
||||
/* FIXME: add error checking */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int write_config(struct config_file *cf, const char *file)
|
||||
{
|
||||
int r = 1;
|
||||
FILE *fp = fopen(file, "w");
|
||||
if (!fp) {
|
||||
log_sys_error("open", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_config(cf->root, fp, 0)) {
|
||||
stack;
|
||||
r = 0;
|
||||
}
|
||||
fclose(fp);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* parser
|
||||
*/
|
||||
static struct config_node *_file(struct parser *p)
|
||||
{
|
||||
struct config_node *root = 0, *n, *l;
|
||||
while (p->t != TOK_EOF) {
|
||||
if (!(n = _section(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!root)
|
||||
root = n;
|
||||
else
|
||||
l->sib = n;
|
||||
l = n;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
static struct config_node *_section(struct parser *p)
|
||||
{
|
||||
/* IDENTIFIER '{' VALUE* '}' */
|
||||
struct config_node *root, *n, *l;
|
||||
if (!(root = _create_node(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(root->key = _dup_tok(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
match (TOK_IDENTIFIER);
|
||||
|
||||
if (p->t == TOK_SECTION_B) {
|
||||
match(TOK_SECTION_B);
|
||||
while (p->t != TOK_SECTION_E) {
|
||||
if (!(n = _section(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!root->child)
|
||||
root->child = n;
|
||||
else
|
||||
l->sib = n;
|
||||
l = n;
|
||||
}
|
||||
match(TOK_SECTION_E);
|
||||
} else {
|
||||
match(TOK_EQ);
|
||||
if (!(root->v = _value(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static struct config_value *_value(struct parser *p)
|
||||
{
|
||||
/* '[' TYPE* ']' | TYPE */
|
||||
struct config_value *h = 0, *l, *ll = 0;
|
||||
if (p->t == TOK_ARRAY_B) {
|
||||
match (TOK_ARRAY_B);
|
||||
while (p->t != TOK_ARRAY_E) {
|
||||
if (!(l = _type(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!h)
|
||||
h = l;
|
||||
else
|
||||
ll->next = l;
|
||||
ll = l;
|
||||
|
||||
if (p->t == TOK_COMMA)
|
||||
match(TOK_COMMA);
|
||||
}
|
||||
match(TOK_ARRAY_E);
|
||||
} else
|
||||
h = _type(p);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static struct config_value *_type(struct parser *p) {
|
||||
/* [0-9]+ | [0-9]*\.[0-9]* | ".*" */
|
||||
struct config_value *v = _create_value(p);
|
||||
|
||||
switch (p->t) {
|
||||
case TOK_INT:
|
||||
v->type = CFG_INT;
|
||||
v->v.i = strtol(p->tb, 0, 10); /* FIXME: check error */
|
||||
match(TOK_INT);
|
||||
break;
|
||||
|
||||
case TOK_FLOAT:
|
||||
v->type = CFG_FLOAT;
|
||||
v->v.r = strtod(p->tb, 0); /* FIXME: check error */
|
||||
match(TOK_FLOAT);
|
||||
break;
|
||||
|
||||
case TOK_STRING:
|
||||
v->type = CFG_STRING;
|
||||
|
||||
p->tb++, p->te--; /* strip "'s */
|
||||
if (!(v->v.str = _dup_tok(p))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
p->te++;
|
||||
match(TOK_STRING);
|
||||
break;
|
||||
|
||||
default:
|
||||
_parse_error(p, __FILE__, __LINE__, "expected a value");
|
||||
return 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static void _parse_error(struct parser *p, const char *file, int line,
|
||||
const char *mess)
|
||||
{
|
||||
plog(_LOG_ERR, file, line, "parse error at %d: %s", p->line, mess);
|
||||
}
|
||||
|
||||
static int _match_aux(struct parser *p, int t)
|
||||
{
|
||||
if (p->t != t)
|
||||
return 0;
|
||||
|
||||
_get_token(p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* tokeniser
|
||||
*/
|
||||
static void _get_token(struct parser *p)
|
||||
{
|
||||
p->tb = p->te;
|
||||
_eat_space(p);
|
||||
if (p->tb == p->fe) {
|
||||
p->t = TOK_EOF;
|
||||
return;
|
||||
}
|
||||
|
||||
p->t = TOK_INT; /* fudge so the fall through for
|
||||
floats works */
|
||||
switch (*p->te) {
|
||||
case '{':
|
||||
p->t = TOK_SECTION_B;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
p->t = TOK_SECTION_E;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
p->t = TOK_ARRAY_B;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case ']':
|
||||
p->t = TOK_ARRAY_E;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
p->t = TOK_COMMA;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '=':
|
||||
p->t = TOK_EQ;
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
p->t = TOK_STRING;
|
||||
p->te++;
|
||||
while ((p->te != p->fe) && (*p->te != '"')) {
|
||||
if ((*p->te == '\\') && (p->te + 1 != p->fe))
|
||||
p->te++;
|
||||
p->te++;
|
||||
}
|
||||
|
||||
if (p->te != p->fe)
|
||||
p->te++;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
p->t = TOK_FLOAT;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
p->te++;
|
||||
while (p->te != p->fe) {
|
||||
if (*p->te == '.') {
|
||||
if (p->t == TOK_FLOAT)
|
||||
break;
|
||||
p->t = TOK_FLOAT;
|
||||
} else if (!isdigit((int) *p->te))
|
||||
break;
|
||||
p->te++;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
p->t = TOK_IDENTIFIER;
|
||||
while ((p->te != p->fe) && !isspace(*p->te) &&
|
||||
(*p->te != '#') && (*p->te != '='))
|
||||
p->te++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _eat_space(struct parser *p)
|
||||
{
|
||||
while (p->tb != p->fe) {
|
||||
if (*p->te == '#') {
|
||||
while ((p->te != p->fe) && (*p->te != '\n'))
|
||||
p->te++;
|
||||
p->line++;
|
||||
}
|
||||
|
||||
else if (isspace(*p->te)) {
|
||||
while ((p->te != p->fe) && isspace(*p->te)) {
|
||||
if (*p->te == '\n')
|
||||
p->line++;
|
||||
p->te++;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
return;
|
||||
|
||||
p->tb = p->te;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* memory management
|
||||
*/
|
||||
static struct config_value *_create_value(struct parser *p)
|
||||
{
|
||||
struct config_value *v = pool_alloc(p->mem, sizeof(*v));
|
||||
memset(v, 0, sizeof(*v));
|
||||
return v;
|
||||
}
|
||||
|
||||
static struct config_node *_create_node(struct parser *p)
|
||||
{
|
||||
struct config_node *n = pool_alloc(p->mem, sizeof(*n));
|
||||
memset(n, 0, sizeof(*n));
|
||||
return n;
|
||||
}
|
||||
|
||||
static char *_dup_tok(struct parser *p)
|
||||
{
|
||||
int len = p->te - p->tb;
|
||||
char *str = pool_alloc(p->mem, len + 1);
|
||||
if (!str) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
strncpy(str, p->tb, len);
|
||||
str[len] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
struct config_node *find_config_node(struct config_node *cn,
|
||||
const char *path, char sep)
|
||||
{
|
||||
const char *e;
|
||||
|
||||
while (cn) {
|
||||
/* trim any leading slashes */
|
||||
while (*path && (*path == sep))
|
||||
path++;
|
||||
|
||||
/* find the end of this segment */
|
||||
for (e = path; *e && (*e != sep); e++)
|
||||
;
|
||||
|
||||
/* hunt for the node */
|
||||
while (cn) {
|
||||
if (_tok_match(cn->key, path, e))
|
||||
break;
|
||||
|
||||
cn = cn->sib;
|
||||
}
|
||||
|
||||
if (cn && *e)
|
||||
cn = cn->child;
|
||||
else
|
||||
break; /* don't move into the last node */
|
||||
|
||||
path = e;
|
||||
}
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
const char *
|
||||
find_config_str(struct config_node *cn,
|
||||
const char *path, char sep, const char *fail)
|
||||
{
|
||||
struct config_node *n = find_config_node(cn, path, sep);
|
||||
|
||||
if (n && n->v->type == CFG_STRING)
|
||||
return n->v->v.str;
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
int find_config_int(struct config_node *cn, const char *path,
|
||||
char sep, int fail)
|
||||
{
|
||||
struct config_node *n = find_config_node(cn, path, sep);
|
||||
|
||||
if (n && n->v->type == CFG_INT)
|
||||
return n->v->v.i;
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
float find_config_float(struct config_node *cn, const char *path,
|
||||
char sep, float fail)
|
||||
{
|
||||
struct config_node *n = find_config_node(cn, path, sep);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return !(*str || (b != e));
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
61
lib/config/config.h
Normal file
61
lib/config/config.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_CONFIG_H
|
||||
#define _LVM_CONFIG_H
|
||||
|
||||
|
||||
enum {
|
||||
CFG_STRING,
|
||||
CFG_FLOAT,
|
||||
CFG_INT,
|
||||
};
|
||||
|
||||
struct config_value {
|
||||
int type;
|
||||
union {
|
||||
int i;
|
||||
float r;
|
||||
char *str;
|
||||
} v;
|
||||
struct config_value *next; /* for arrays */
|
||||
};
|
||||
|
||||
struct config_node {
|
||||
char *key;
|
||||
struct config_node *sib, *child;
|
||||
struct config_value *v;
|
||||
};
|
||||
|
||||
struct config_file {
|
||||
struct config_node *root;
|
||||
};
|
||||
|
||||
struct config_file *create_config_file();
|
||||
void destroy_config_file(struct config_file *cf);
|
||||
|
||||
int read_config(struct config_file *cf, const char *file);
|
||||
int write_config(struct config_file *cf, const char *file);
|
||||
|
||||
struct config_node *find_config_node(struct config_node *cn,
|
||||
const char *path, char seperator);
|
||||
|
||||
const char *find_config_str(struct config_node *cn,
|
||||
const char *path, char sep, const char *fail);
|
||||
|
||||
int find_config_int(struct config_node *cn, const char *path,
|
||||
char sep, int fail);
|
||||
|
||||
float find_config_float(struct config_node *cn, const char *path,
|
||||
char sep, float fail);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
210
lib/datastruct/hash.c
Normal file
210
lib/datastruct/hash.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "hash.h"
|
||||
#include "log.h"
|
||||
|
||||
struct hash_node {
|
||||
struct hash_node *next;
|
||||
void *data;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
struct hash_table {
|
||||
int num_nodes;
|
||||
int num_slots;
|
||||
struct hash_node **slots;
|
||||
};
|
||||
|
||||
/* Permutation of the Integers 0 through 255 */
|
||||
static unsigned char _nums[] = {
|
||||
1, 14,110, 25, 97,174,132,119,138,170,125,118, 27,233,140, 51,
|
||||
87,197,177,107,234,169, 56, 68, 30, 7,173, 73,188, 40, 36, 65,
|
||||
49,213,104,190, 57,211,148,223, 48,115, 15, 2, 67,186,210, 28,
|
||||
12,181,103, 70, 22, 58, 75, 78,183,167,238,157,124,147,172,144,
|
||||
176,161,141, 86, 60, 66,128, 83,156,241, 79, 46,168,198, 41,254,
|
||||
178, 85,253,237,250,154,133, 88, 35,206, 95,116,252,192, 54,221,
|
||||
102,218,255,240, 82,106,158,201, 61, 3, 89, 9, 42,155,159, 93,
|
||||
166, 80, 50, 34,175,195,100, 99, 26,150, 16,145, 4, 33, 8,189,
|
||||
121, 64, 77, 72,208,245,130,122,143, 55,105,134, 29,164,185,194,
|
||||
193,239,101,242, 5,171,126, 11, 74, 59,137,228,108,191,232,139,
|
||||
6, 24, 81, 20,127, 17, 91, 92,251,151,225,207, 21, 98,113,112,
|
||||
84,226, 18,214,199,187, 13, 32, 94,220,224,212,247,204,196, 43,
|
||||
249,236, 45,244,111,182,153,136,129, 90,217,202, 19,165,231, 71,
|
||||
230,142, 96,227, 62,179,246,114,162, 53,160,215,205,180, 47,109,
|
||||
44, 38, 31,149,135, 0,216, 52, 63, 23, 37, 69, 39,117,146,184,
|
||||
163,200,222,235,248,243,219, 10,152,131,123,229,203, 76,120,209
|
||||
};
|
||||
|
||||
static struct hash_node *_create_node(const char *str)
|
||||
{
|
||||
/* remember sizeof(n) includes an extra char from key[1],
|
||||
so not adding 1 to the strlen as you would expect */
|
||||
struct hash_node *n = dbg_malloc(sizeof(*n) + strlen(str));
|
||||
|
||||
if (n)
|
||||
strcpy(n->key, str);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned _hash(const char *str)
|
||||
{
|
||||
unsigned long int h = 0, g;
|
||||
while (*str) {
|
||||
h <<= 4;
|
||||
h += _nums[(int) *str++];
|
||||
g = h & ((unsigned long) 0xf << 16u);
|
||||
if (g) {
|
||||
h ^= g >> 16u;
|
||||
h ^= g >> 5u;
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
struct hash_table *hash_create(unsigned size_hint)
|
||||
{
|
||||
size_t len;
|
||||
unsigned new_size = 16u;
|
||||
struct hash_table *hc = dbg_malloc(sizeof(*hc));
|
||||
|
||||
if (!hc) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(hc, 0, sizeof(*hc));
|
||||
|
||||
/* round size hint up to a power of two */
|
||||
while (new_size < size_hint)
|
||||
new_size = new_size << 1;
|
||||
|
||||
hc->num_slots = new_size;
|
||||
len = sizeof(*(hc->slots)) * new_size;
|
||||
if (!(hc->slots = dbg_malloc(len))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
memset(hc->slots, 0, len);
|
||||
return hc;
|
||||
|
||||
bad:
|
||||
dbg_free(hc->slots);
|
||||
dbg_free(hc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hash_destroy(struct hash_table *t)
|
||||
{
|
||||
struct hash_node *c, *n;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < t->num_slots; i++)
|
||||
for (c = t->slots[i]; c; c = n) {
|
||||
n = c->next;
|
||||
dbg_free(c);
|
||||
}
|
||||
|
||||
dbg_free(t->slots);
|
||||
dbg_free(t);
|
||||
}
|
||||
|
||||
static inline struct hash_node **_find(struct hash_table *t, const char *key)
|
||||
{
|
||||
unsigned h = _hash(key) & (t->num_slots - 1);
|
||||
struct hash_node **c;
|
||||
|
||||
for(c = &t->slots[h]; *c; c = &((*c)->next))
|
||||
if(!strcmp(key, (*c)->key))
|
||||
break;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
char *hash_lookup(struct hash_table *t, const char *key)
|
||||
{
|
||||
struct hash_node **c = _find(t, key);
|
||||
return *c ? (*c)->data : 0;
|
||||
}
|
||||
|
||||
int hash_insert(struct hash_table *t, const char *key, void *data)
|
||||
{
|
||||
struct hash_node **c = _find(t, key);
|
||||
|
||||
if(*c)
|
||||
(*c)->data = data;
|
||||
else {
|
||||
struct hash_node *n = _create_node(key);
|
||||
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
n->data = data;
|
||||
n->next = 0;
|
||||
*c = n;
|
||||
t->num_nodes++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void hash_remove(struct hash_table *t, const char *key)
|
||||
{
|
||||
struct hash_node **c = _find(t, key);
|
||||
|
||||
if (*c) {
|
||||
struct hash_node *old = *c;
|
||||
*c = (*c)->next;
|
||||
dbg_free(old);
|
||||
t->num_nodes--;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned hash_get_num_entries(struct hash_table *t)
|
||||
{
|
||||
return t->num_nodes;
|
||||
}
|
||||
|
||||
void hash_iterate(struct hash_table *t, iterate_fn f)
|
||||
{
|
||||
struct hash_node *c;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < t->num_slots; i++)
|
||||
for (c = t->slots[i]; c; c = c->next)
|
||||
f(c->data);
|
||||
}
|
||||
|
||||
void *hash_get_data(struct hash_table *t, struct hash_node *n)
|
||||
{
|
||||
return n->data;
|
||||
}
|
||||
|
||||
static struct hash_node *_next_slot(struct hash_table *t, unsigned int s)
|
||||
{
|
||||
struct hash_node *c = NULL;
|
||||
int i;
|
||||
|
||||
for (i = s; i < t->num_slots && !c; i++)
|
||||
c = t->slots[i];
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
struct hash_node *hash_get_first(struct hash_table *t)
|
||||
{
|
||||
return _next_slot(t, 0);
|
||||
}
|
||||
|
||||
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n)
|
||||
{
|
||||
unsigned int h = _hash(n->key) & (t->num_slots - 1);
|
||||
return n->next ? n->next : _next_slot(t, h + 1);
|
||||
}
|
||||
|
||||
30
lib/datastruct/hash.h
Normal file
30
lib/datastruct/hash.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_HASH_H
|
||||
#define _LVM_HASH_H
|
||||
|
||||
struct hash_table;
|
||||
struct hash_node;
|
||||
|
||||
typedef void (*iterate_fn)(void *data);
|
||||
|
||||
struct hash_table *hash_create(unsigned size_hint);
|
||||
void hash_destroy(struct hash_table *t);
|
||||
|
||||
char *hash_lookup(struct hash_table *t, const char *key);
|
||||
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_iterate(struct hash_table *t, iterate_fn f);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
||||
110
lib/datastruct/list.h
Normal file
110
lib/datastruct/list.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/* stolen from the Linux kernel. */
|
||||
|
||||
#ifndef _LVM_LIST_H
|
||||
#define _LVM_LIST_H
|
||||
|
||||
/*
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = { &name, &name }
|
||||
|
||||
#define INIT_LIST_HEAD(ptr) do { \
|
||||
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static __inline__ void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry after the specified head..
|
||||
*/
|
||||
static __inline__ void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry at the tail
|
||||
*/
|
||||
static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static __inline__ void __list_del(struct list_head * prev,
|
||||
struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static __inline__ void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
}
|
||||
|
||||
static __inline__ int list_empty(struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Splice in "list" into "head"
|
||||
*/
|
||||
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
|
||||
if (first != list) {
|
||||
struct list_head *last = list->prev;
|
||||
struct list_head *at = head->next;
|
||||
|
||||
first->prev = head;
|
||||
head->next = first;
|
||||
|
||||
last->next = at;
|
||||
at->prev = last;
|
||||
}
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
||||
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
#endif
|
||||
24
lib/datastruct/lvm-types.h
Normal file
24
lib/datastruct/lvm-types.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_TYPES_H
|
||||
#define _LVM_TYPES_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef __uint8_t uint8_t;
|
||||
typedef __uint16_t uint16_t;
|
||||
typedef __uint32_t uint32_t;
|
||||
typedef __uint64_t uint64_t;
|
||||
|
||||
#if 0
|
||||
typedef __int8_t int8_t;
|
||||
typedef __int16_t int16_t;
|
||||
typedef __int32_t int32_t;
|
||||
typedef __int64_t int64_t;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
301
lib/device/dev-cache.c
Normal file
301
lib/device/dev-cache.c
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dev-cache.h"
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "hash.h"
|
||||
#include "list.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <dirent.h>
|
||||
|
||||
/*
|
||||
* FIXME: really need to seperate names from the devices since
|
||||
* multiple names can point to the same device.
|
||||
*/
|
||||
|
||||
struct dev_iter {
|
||||
struct hash_node *current;
|
||||
struct dev_filter *filter;
|
||||
};
|
||||
|
||||
struct dir_list {
|
||||
struct list_head list;
|
||||
char dir[0];
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct pool *mem;
|
||||
struct hash_table *devices;
|
||||
|
||||
int has_scanned;
|
||||
struct list_head dirs;
|
||||
|
||||
} _cache;
|
||||
|
||||
|
||||
#define _alloc(x) pool_alloc(_cache.mem, (x))
|
||||
#define _free(x) pool_free(_cache.mem, (x))
|
||||
|
||||
static int _dir_scan(const char *dir);
|
||||
|
||||
/*
|
||||
* return a new path for the destination of the path.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get rid of extra slashes in the path string.
|
||||
*/
|
||||
static void _collapse_slashes(char *str)
|
||||
{
|
||||
char *ptr;
|
||||
int was_slash = 0;
|
||||
|
||||
for (ptr = str; *ptr; ptr++) {
|
||||
if (*ptr == '/') {
|
||||
if (was_slash)
|
||||
continue;
|
||||
|
||||
was_slash = 1;
|
||||
} else
|
||||
was_slash = 0;
|
||||
*str++ = *ptr;
|
||||
}
|
||||
|
||||
*str = *ptr;
|
||||
}
|
||||
|
||||
static struct device *_create_dev(const char *path, struct stat *info)
|
||||
{
|
||||
struct device *dev;
|
||||
char *name = pool_strdup(_cache.mem, path);
|
||||
|
||||
if (!name) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_collapse_slashes(name);
|
||||
|
||||
if (!(dev = _alloc(sizeof(*dev)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
dev->name = name;
|
||||
dev->dev = info->st_rdev;
|
||||
return dev;
|
||||
|
||||
bad:
|
||||
_free(name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _insert(const char *path, int recurse)
|
||||
{
|
||||
struct stat info;
|
||||
struct device *dev;
|
||||
|
||||
log_very_verbose("dev-cache adding %s", path);
|
||||
|
||||
if (stat(path, &info) < 0) {
|
||||
log_sys_very_verbose("stat", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (S_ISDIR(info.st_mode)) {
|
||||
if (recurse)
|
||||
return _dir_scan(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (S_ISLNK(info.st_mode)) {
|
||||
log_debug("%s is a symbolic link, following", path);
|
||||
if (!(path = _follow_link(path, &info))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!S_ISBLK(info.st_mode)) {
|
||||
log_debug("%s is not a block device", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(dev = _create_dev(path, &info))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
hash_insert(_cache.devices, path, dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *_join(const char *dir, const char *name)
|
||||
{
|
||||
int len = strlen(dir) + strlen(name) + 2;
|
||||
char *r = dbg_malloc(len);
|
||||
if (r)
|
||||
snprintf(r, len, "%s/%s", dir, name);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _dir_scan(const char *dir)
|
||||
{
|
||||
int n, dirent_count;
|
||||
struct dirent **dirent;
|
||||
char *path;
|
||||
|
||||
dirent_count = scandir(dir, &dirent, NULL, alphasort);
|
||||
if (dirent_count > 0) {
|
||||
for (n = 0; n < dirent_count; n++) {
|
||||
if (dirent[n]->d_name[0] == '.') {
|
||||
free(dirent[n]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((path = _join(dir, dirent[n]->d_name)))
|
||||
_insert(path, 1);
|
||||
|
||||
dbg_free(path);
|
||||
free(dirent[n]);
|
||||
}
|
||||
free(dirent);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _full_scan(void)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
|
||||
if (_cache.has_scanned)
|
||||
return;
|
||||
|
||||
list_for_each(tmp, &_cache.dirs) {
|
||||
struct dir_list *dl = list_entry(tmp, struct dir_list, list);
|
||||
_dir_scan(dl->dir);
|
||||
}
|
||||
|
||||
_cache.has_scanned = 1;
|
||||
}
|
||||
|
||||
int dev_cache_init(void)
|
||||
{
|
||||
if (!(_cache.mem = pool_create(10 * 1024))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(_cache.devices = hash_create(128))) {
|
||||
stack;
|
||||
pool_destroy(_cache.mem);
|
||||
_cache.mem = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&_cache.dirs);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dev_cache_exit(void)
|
||||
{
|
||||
pool_destroy(_cache.mem);
|
||||
hash_destroy(_cache.devices);
|
||||
}
|
||||
|
||||
int dev_cache_add_dir(const char *path)
|
||||
{
|
||||
struct dir_list *dl;
|
||||
|
||||
if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1)))
|
||||
return 0;
|
||||
|
||||
strcpy(dl->dir, path);
|
||||
list_add(&dl->list, &_cache.dirs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct device *dev_cache_get(const char *name, struct dev_filter *f)
|
||||
{
|
||||
struct device *d = (struct device *) hash_lookup(_cache.devices, name);
|
||||
|
||||
if (!d) {
|
||||
_insert(name, 0);
|
||||
d = (struct device *) hash_lookup(_cache.devices, name);
|
||||
}
|
||||
|
||||
return (d && (!f || f->passes_filter(f, d))) ? d : NULL;
|
||||
}
|
||||
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f)
|
||||
{
|
||||
struct dev_iter *di = dbg_malloc(sizeof(*di));
|
||||
|
||||
if (!di)
|
||||
return NULL;
|
||||
|
||||
_full_scan();
|
||||
di->current = hash_get_first(_cache.devices);
|
||||
di->filter = f;
|
||||
|
||||
return di;
|
||||
}
|
||||
|
||||
void dev_iter_destroy(struct dev_iter *iter)
|
||||
{
|
||||
dbg_free(iter);
|
||||
}
|
||||
|
||||
static inline struct device *_iter_next(struct dev_iter *iter)
|
||||
{
|
||||
struct device *d = hash_get_data(_cache.devices, iter->current);
|
||||
iter->current = hash_get_next(_cache.devices, iter->current);
|
||||
return d;
|
||||
}
|
||||
|
||||
struct device *dev_iter_get(struct dev_iter *iter)
|
||||
{
|
||||
while (iter->current) {
|
||||
struct device *d = _iter_next(iter);
|
||||
if (!iter->filter ||
|
||||
iter->filter->passes_filter(iter->filter, d))
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
41
lib/device/dev-cache.h
Normal file
41
lib/device/dev-cache.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DEV_CACHE_H
|
||||
#define _LVM_DEV_CACHE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "lvm-types.h"
|
||||
#include "device.h"
|
||||
|
||||
/*
|
||||
* predicate for devices.
|
||||
*/
|
||||
struct dev_filter {
|
||||
int (*passes_filter)(struct dev_filter *f, struct device *dev);
|
||||
void *private;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The global device cache.
|
||||
*/
|
||||
int dev_cache_init(void);
|
||||
void dev_cache_exit(void);
|
||||
|
||||
int dev_cache_add_dir(const char *path);
|
||||
struct device *dev_cache_get(const char *name, struct dev_filter *f);
|
||||
|
||||
|
||||
/*
|
||||
* Object for iterating through the cache.
|
||||
*/
|
||||
struct dev_iter;
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f);
|
||||
void dev_iter_destroy(struct dev_iter *iter);
|
||||
struct device *dev_iter_get(struct dev_iter *iter);
|
||||
|
||||
#endif
|
||||
121
lib/device/dev-io.c
Normal file
121
lib/device/dev-io.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
#include "lvm-types.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
int dev_get_size(struct device *dev, uint64_t *size)
|
||||
{
|
||||
int fd;
|
||||
long s;
|
||||
|
||||
log_very_verbose("Getting size of %s", dev->name);
|
||||
if ((fd = open(dev->name, O_RDONLY)) < 0) {
|
||||
log_sys_error("open", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: add 64 bit ioctl */
|
||||
if (ioctl(fd, BLKGETSIZE, &s) < 0) {
|
||||
log_sys_error("ioctl BLKGETSIZE", dev->name);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
*size = (uint64_t) s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: factor common code out.
|
||||
*/
|
||||
|
||||
int _read(int fd, void *buf, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
int tot = 0;
|
||||
|
||||
while (tot < count) {
|
||||
n = read(fd, buf, count - tot);
|
||||
|
||||
if (n <= 0)
|
||||
return tot ? tot : n;
|
||||
|
||||
tot += n;
|
||||
buf += n;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
int64_t dev_read(struct device *dev, uint64_t offset,
|
||||
int64_t len, void *buffer)
|
||||
{
|
||||
int64_t r;
|
||||
int fd = open(dev->name, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
log_sys_very_verbose("open", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) < 0) {
|
||||
log_sys_error("lseek", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = _read(fd, buffer, len);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
int _write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
int tot = 0;
|
||||
|
||||
while (tot < count) {
|
||||
n = write(fd, buf, count - tot);
|
||||
|
||||
if (n <= 0)
|
||||
return tot ? tot : n;
|
||||
|
||||
tot += n;
|
||||
buf += n;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
int64_t dev_write(struct device *dev, uint64_t offset,
|
||||
int64_t len, void *buffer)
|
||||
{
|
||||
int64_t r;
|
||||
int fd = open(dev->name, O_WRONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
log_sys_error("open", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) < 0) {
|
||||
log_sys_error("lseek", dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = _write(fd, buffer, len);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
214
lib/device/device.c
Normal file
214
lib/device/device.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "dev-cache.h"
|
||||
#include "metadata.h"
|
||||
#include "device.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/genhd.h>
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
int _get_partition_type(struct dev_filter *filter, struct device *d);
|
||||
|
||||
#define MINOR_PART(dm, d) (MINOR((d)->dev) % dev_max_partitions(dm, (d)->dev))
|
||||
|
||||
int is_whole_disk(struct dev_filter *filter, struct device *d)
|
||||
{
|
||||
return (MINOR_PART(dm, d)) ? 0 : 1;
|
||||
}
|
||||
|
||||
int is_extended_partition(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
return (MINOR_PART(dm, d) > 4) ? 1 : 0;
|
||||
}
|
||||
|
||||
struct device *dev_primary(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
struct device *ret;
|
||||
|
||||
ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
|
||||
/* FIXME: Needs replacing with a 'refresh' */
|
||||
if (!ret) {
|
||||
init_dev_scan(dm);
|
||||
ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
int pt;
|
||||
|
||||
pt = _get_partition_type(dm, d);
|
||||
|
||||
if (!pt) {
|
||||
if (is_whole_disk(dm, d))
|
||||
/* FIXME: Overloaded pt=0 in error cases */
|
||||
return 1;
|
||||
else {
|
||||
log_error
|
||||
("%s: missing partition table "
|
||||
"on partitioned device", d->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_whole_disk(dm, d)) {
|
||||
log_error("%s: looks to possess partition table", d->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check part type */
|
||||
if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
|
||||
log_error("%s: invalid partition type 0x%x "
|
||||
"(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pt == LVM_PARTITION) {
|
||||
log_error
|
||||
("%s: old LVM partition type found - please change to 0x%x",
|
||||
d->name, LVM_NEW_PARTITION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _get_partition_type(struct dev_mgr *dm, struct device *d)
|
||||
{
|
||||
int pv_handle = -1;
|
||||
struct device *primary;
|
||||
ssize_t read_ret;
|
||||
ssize_t bytes_read = 0;
|
||||
char *buffer;
|
||||
unsigned short *s_buffer;
|
||||
struct partition *part;
|
||||
loff_t offset = 0;
|
||||
loff_t extended_offset = 0;
|
||||
int part_sought;
|
||||
int part_found = 0;
|
||||
int first_partition = 1;
|
||||
int extended_partition = 0;
|
||||
int p;
|
||||
|
||||
if (!(primary = dev_primary(dm, d))) {
|
||||
log_error
|
||||
("Failed to find main device containing partition %s",
|
||||
d->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(buffer = dbg_malloc(SECTOR_SIZE))) {
|
||||
log_error("Failed to allocate partition table buffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get partition table */
|
||||
if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
|
||||
log_error("%s: open failed: %s", primary->name,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
s_buffer = (unsigned short *) buffer;
|
||||
part = (struct partition *) (buffer + 0x1be);
|
||||
part_sought = MINOR_PART(dm, d);
|
||||
|
||||
do {
|
||||
bytes_read = 0;
|
||||
|
||||
if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
|
||||
log_error("%s: llseek failed: %s",
|
||||
primary->name, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((bytes_read < SECTOR_SIZE) &&
|
||||
(read_ret =
|
||||
read(pv_handle, buffer + bytes_read,
|
||||
SECTOR_SIZE - bytes_read)) != -1)
|
||||
bytes_read += read_ret;
|
||||
|
||||
if (read_ret == -1) {
|
||||
log_error("%s: read failed: %s", primary->name,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s_buffer[255] == 0xAA55) {
|
||||
if (is_whole_disk(dm, d))
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
extended_partition = 0;
|
||||
|
||||
/* Loop through primary partitions */
|
||||
for (p = 0; p < 4; p++) {
|
||||
if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
|
||||
part[p].sys_ind == LINUX_EXTENDED_PARTITION
|
||||
|| part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
|
||||
extended_partition = 1;
|
||||
offset = extended_offset + part[p].start_sect;
|
||||
if (extended_offset == 0)
|
||||
extended_offset = part[p].start_sect;
|
||||
if (first_partition == 1)
|
||||
part_found++;
|
||||
} else if (first_partition == 1) {
|
||||
if (p == part_sought) {
|
||||
if (part[p].sys_ind == 0) {
|
||||
/* missing primary? */
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
part_found++;
|
||||
} else if (!part[p].sys_ind)
|
||||
part_found++;
|
||||
|
||||
if (part_sought == part_found)
|
||||
return part[p].sys_ind;
|
||||
|
||||
}
|
||||
first_partition = 0;
|
||||
}
|
||||
while (extended_partition == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
42
lib/device/device.h
Normal file
42
lib/device/device.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DEVICE_H
|
||||
#define _LVM_DEVICE_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
|
||||
/*
|
||||
* All devices in LVM will be represented by one of these.
|
||||
* pointer comparisons are valid.
|
||||
*/
|
||||
struct device {
|
||||
char *name;
|
||||
dev_t dev;
|
||||
};
|
||||
|
||||
/*
|
||||
* All io should use these routines, rather than opening the devices
|
||||
* by hand. You do not have to call an open routine. ATM all io is
|
||||
* immediately flushed.
|
||||
*/
|
||||
int dev_get_size(struct device *dev, uint64_t *size);
|
||||
int64_t dev_read(struct device *dev,
|
||||
uint64_t offset, int64_t len, void *buffer);
|
||||
int64_t dev_write(struct device *dev,
|
||||
uint64_t offset, int64_t len, void *buffer);
|
||||
|
||||
|
||||
static inline int is_lvm_partition(const char *name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define LVM_DEFAULT_DIR_PREFIX "/dev/"
|
||||
/* FIXME Allow config file override */
|
||||
static inline char *lvm_dir_prefix(void) { return LVM_DEFAULT_DIR_PREFIX; }
|
||||
|
||||
#endif
|
||||
|
||||
94
lib/display/display.c
Normal file
94
lib/display/display.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include "display.h"
|
||||
#include "metadata.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
#define SIZE_BUF 128
|
||||
|
||||
char *display_size(unsigned long long size, size_len_t sl)
|
||||
{
|
||||
int s;
|
||||
ulong byte = 1024 * 1024 * 1024;
|
||||
char *size_buf = NULL;
|
||||
char *size_str[][2] = {
|
||||
{"Terabyte", "TB"},
|
||||
{"Gigabyte", "GB"},
|
||||
{"Megabyte", "MB"},
|
||||
{"Kilobyte", "KB"},
|
||||
{"", ""}
|
||||
};
|
||||
|
||||
if (!(size_buf = dbg_malloc(SIZE_BUF))) {
|
||||
log_error("no memory for size display buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size == 0LL)
|
||||
sprintf(size_buf, "0");
|
||||
else {
|
||||
s = 0;
|
||||
while (size_str[s] && size < byte)
|
||||
s++, byte /= 1024;
|
||||
snprintf(size_buf, SIZE_BUF - 1,
|
||||
"%.2f %s", (float) size / byte, size_str[s][sl]);
|
||||
}
|
||||
|
||||
/* Caller to deallocate */
|
||||
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;
|
||||
}
|
||||
|
||||
31
lib/display/display.h
Normal file
31
lib/display/display.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DISPLAY_H
|
||||
#define _LVM_DISPLAY_H
|
||||
|
||||
typedef enum {SIZE_LONG=0, SIZE_SHORT=1} size_len_t;
|
||||
|
||||
/* Specify size in KB */
|
||||
char *display_size(unsigned long long size, size_len_t sl);
|
||||
|
||||
char *display_uuid(char *uuidstr);
|
||||
|
||||
#endif
|
||||
278
lib/display/metadata.c
Normal file
278
lib/display/metadata.c
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include "display.h"
|
||||
#include "metadata.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
void pv_display_colons(pv_t * pv)
|
||||
{
|
||||
char *uuid;
|
||||
|
||||
if (!pv)
|
||||
return;
|
||||
|
||||
uuid = display_uuid(pv->pv_uuid);
|
||||
|
||||
printf("%s:%s:%d:%d:%d:%d:%d:%d:%d:%d:%d:%s\n",
|
||||
pv->pv_name,
|
||||
pv->vg_name,
|
||||
pv->pv_size,
|
||||
pv->pv_number,
|
||||
pv->pv_status,
|
||||
pv->pv_allocatable,
|
||||
pv->lv_cur,
|
||||
pv->pe_size / 2,
|
||||
pv->pe_total,
|
||||
pv->pe_total - pv->pe_allocated,
|
||||
pv->pe_allocated, uuid ? uuid : "none");
|
||||
|
||||
dbg_free(uuid);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void pv_display_full(pv_t * pv)
|
||||
{
|
||||
ulong pe_free;
|
||||
char *size = NULL;
|
||||
char *uuid;
|
||||
|
||||
if (!pv)
|
||||
return;
|
||||
|
||||
uuid = display_uuid(pv->pv_uuid);
|
||||
|
||||
printf("--- %sPhysical volume ---\n", pv->pe_size ? "" : "NEW ");
|
||||
printf("PV Name %s\n", pv->pv_name);
|
||||
printf("VG Name %s\n", pv->vg_name);
|
||||
|
||||
size = display_size(pv->pv_size / 2, SIZE_SHORT);
|
||||
printf("PV Size %s", size);
|
||||
dbg_free(size);
|
||||
|
||||
if (pv->pe_size && pv->pe_total) {
|
||||
size =
|
||||
display_size((pv->pv_size - pv->pe_size * pv->pe_total) / 2,
|
||||
SIZE_SHORT);
|
||||
printf(" / NOT usable %s ", size);
|
||||
dbg_free(size);
|
||||
|
||||
size =
|
||||
display_size(
|
||||
(pv->pe_on_disk.base +
|
||||
pv->pe_total * sizeof (pe_disk_t)) / 1024,
|
||||
SIZE_SHORT);
|
||||
printf("[LVM: %s]", size);
|
||||
dbg_free(size);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
printf("PV# %u\n", pv->pv_number);
|
||||
printf("PV Status %savailable\n",
|
||||
(pv->pv_status & PV_ACTIVE) ? "" : "NOT ");
|
||||
|
||||
printf("Allocatable ");
|
||||
pe_free = pv->pe_total - pv->pe_allocated;
|
||||
if (pv->pe_total > 0 && (pv->pv_allocatable & PV_ALLOCATABLE)) {
|
||||
printf("yes");
|
||||
if (pe_free == 0 && pv->pe_total > 0)
|
||||
printf(" (but full)");
|
||||
printf("\n");
|
||||
} else
|
||||
printf("NO\n");
|
||||
|
||||
printf("Cur LV %u\n", pv->lv_cur);
|
||||
printf("PE Size (KByte) %u\n", pv->pe_size / 2);
|
||||
printf("Total PE %u\n", pv->pe_total);
|
||||
printf("Free PE %lu\n", pe_free);
|
||||
printf("Allocated PE %u\n", pv->pe_allocated);
|
||||
|
||||
#ifdef LVM_FUTURE
|
||||
printf("Stale PE %u\n", pv->pe_stale);
|
||||
#endif
|
||||
|
||||
printf("PV UUID %s\n", uuid ? uuid : "none");
|
||||
printf("\n");
|
||||
|
||||
dbg_free(uuid);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*******
|
||||
void pv_display_short(pv_t * pv)
|
||||
{
|
||||
|
||||
if (pv != NULL) {
|
||||
printf("PV Name (#) %s (%u)\n", pv->pv_name,
|
||||
pv->pv_number);
|
||||
printf("PV Status ");
|
||||
if (!(pv->pv_status & PV_ACTIVE))
|
||||
printf("NOT ");
|
||||
printf("available / ");
|
||||
if (!(pv->pv_allocatable & PV_ALLOCATABLE))
|
||||
printf("NOT ");
|
||||
printf("allocatable\n");
|
||||
printf("Total PE / Free PE %u / %u\n",
|
||||
pv->pe_total, pv->pe_total - pv->pe_allocated);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void pv_display_pe(pv_t * pv, pe_disk_t * pe)
|
||||
{
|
||||
int p;
|
||||
|
||||
for (p = 0; p < pv->pe_total; p++)
|
||||
printf("pe#: %4d vg: %s lv: %d le: %d\n",
|
||||
p, pv->vg_name, pe[p].lv_num, pe[p].le_num);
|
||||
|
||||
return;
|
||||
}
|
||||
*******/
|
||||
|
||||
void pv_display_pe_text(pv_t * pv, pe_disk_t * pe, lv_disk_t * lvs)
|
||||
{
|
||||
int flag = 0;
|
||||
int lv_num_last = 0;
|
||||
int p = 0;
|
||||
int pe_free = 0;
|
||||
int *pe_this_count = NULL;
|
||||
int pt = 0;
|
||||
int pt_count = 0;
|
||||
lv_disk_t *lv;
|
||||
char *lv_name_this = NULL;
|
||||
char *lv_names = NULL;
|
||||
char *lv_names_sav = NULL;
|
||||
pe_disk_t *pe_this = NULL;
|
||||
|
||||
if ((pe_this = dbg_malloc(pv->pe_total * sizeof (pe_disk_t))) == NULL) {
|
||||
log_error("pe_this allocation failed");
|
||||
goto pv_display_pe_text_out;
|
||||
}
|
||||
|
||||
if ((pe_this_count = dbg_malloc(pv->pe_total * sizeof (int))) == NULL) {
|
||||
log_error("pe_this_count allocation failed");
|
||||
goto pv_display_pe_text_out;
|
||||
}
|
||||
|
||||
memset(pe_this, 0, pv->pe_total * sizeof (pe_disk_t));
|
||||
memset(pe_this_count, 0, pv->pe_total * sizeof (int));
|
||||
|
||||
/* get PE and LE summaries */
|
||||
pt_count = 0;
|
||||
for (p = pt_count; p < pv->pe_total; p++) {
|
||||
if (pe[p].lv_num != 0) {
|
||||
flag = 0;
|
||||
for (pt = 0; pt < pt_count; pt++) {
|
||||
if (pe_this[pt].lv_num == pe[p].lv_num) {
|
||||
flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag == 0) {
|
||||
pe_this[pt_count].lv_num = pe[p].lv_num;
|
||||
for (pt = 0; pt < pv->pe_total; pt++)
|
||||
if (pe_this[pt_count].lv_num ==
|
||||
pe[pt].lv_num)
|
||||
pe_this_count[pt_count]++;
|
||||
pt_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lv = lvs;
|
||||
printf(" --- Distribution of physical volume ---\n"
|
||||
" LV Name LE of LV PE for LV\n");
|
||||
for (pt = 0; pt < pt_count; pt++) {
|
||||
printf(" %-25s ", lv->lv_name);
|
||||
if (strlen(lv->lv_name) > 25)
|
||||
printf("\n ");
|
||||
printf("%-8u %-8d\n",
|
||||
lv->lv_allocated_le,
|
||||
pe_this_count[pt]);
|
||||
if (pe_this[pt].lv_num > lv_num_last) {
|
||||
lv_num_last = pe_this[pt].lv_num;
|
||||
lv_names_sav = lv_names;
|
||||
if ((lv_names = dbg_realloc(lv_names,
|
||||
lv_num_last * NAME_LEN)) ==
|
||||
NULL) {
|
||||
log_error("realloc error in %s [line %d]",
|
||||
__FILE__, __LINE__);
|
||||
goto pv_display_pe_text_out;
|
||||
} else
|
||||
lv_names_sav = NULL;
|
||||
}
|
||||
strcpy(&lv_names[(pe_this[pt].lv_num - 1) * NAME_LEN],
|
||||
lv->lv_name);
|
||||
lv++;
|
||||
}
|
||||
|
||||
printf("\n --- Physical extents ---\n"
|
||||
" PE LV LE Disk sector\n");
|
||||
pe_free = -1;
|
||||
for (p = 0; p < pv->pe_total; p++) {
|
||||
if (pe[p].lv_num != 0) {
|
||||
if (pe_free > -1) {
|
||||
pv_display_pe_free(pe_free, p);
|
||||
pe_free = -1;
|
||||
}
|
||||
lv_name_this = &lv_names[(pe[p].lv_num - 1) * NAME_LEN];
|
||||
printf(" %05d %-25s ", p, lv_name_this);
|
||||
if (strlen(lv_name_this) > 25)
|
||||
printf("\n ");
|
||||
printf("%05d %ld\n", pe[p].le_num,
|
||||
get_pe_offset(p, pv));
|
||||
|
||||
} else if (pe_free == -1)
|
||||
pe_free = p;
|
||||
}
|
||||
|
||||
if (pe_free > 0)
|
||||
pv_display_pe_free(pe_free, p);
|
||||
|
||||
pv_display_pe_text_out:
|
||||
if (lv_names != NULL)
|
||||
dbg_free(lv_names);
|
||||
else if (lv_names_sav != NULL)
|
||||
dbg_free(lv_names_sav);
|
||||
if (pe_this != NULL)
|
||||
dbg_free(pe_this);
|
||||
if (pe_this_count != NULL)
|
||||
dbg_free(pe_this_count);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void pv_display_pe_free(int pe_free, int p)
|
||||
{
|
||||
printf(" %05d free\n", pe_free);
|
||||
|
||||
if (p - pe_free > 1)
|
||||
printf(" .....\n %05d free\n", p - 1);
|
||||
|
||||
return;
|
||||
}
|
||||
39
lib/display/metadata.h
Normal file
39
lib/display/metadata.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DISPLAY_METADATA_H
|
||||
#define _LVM_DISPLAY_METADATA_H
|
||||
|
||||
#if 0
|
||||
#include "metadata/metadata.h"
|
||||
|
||||
void pv_display_colons(pv_t * pv);
|
||||
void pv_display_full(pv_t * pv);
|
||||
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
|
||||
154
lib/filters/filter.c
Normal file
154
lib/filters/filter.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* lvm 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.
|
||||
*
|
||||
* lvm 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 "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
#include "dev-cache.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <linux/kdev_t.h>
|
||||
|
||||
#define NUMBER_OF_MAJORS 256
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int max_partitions;
|
||||
} device_info_t;
|
||||
|
||||
static device_info_t device_info[] = {
|
||||
{"ide", 16}, /* IDE disk */
|
||||
{"sd", 16}, /* SCSI disk */
|
||||
{"md", 16}, /* Multiple Disk driver (SoftRAID) */
|
||||
{"loop", 16}, /* Loop device */
|
||||
{"dasd", 4}, /* DASD disk (IBM S/390, zSeries) */
|
||||
{"dac960", 8}, /* DAC960 */
|
||||
{"nbd", 16}, /* Network Block Device */
|
||||
{"ida", 16}, /* Compaq SMART2 */
|
||||
{"cciss", 16}, /* Compaq CCISS array */
|
||||
{"ubd", 16}, /* User-mode virtual block device */
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
static int *scan_proc_dev(void);
|
||||
|
||||
static int passes_config_device_filter(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
/* FIXME Check against config file scan/reject entries */
|
||||
|
||||
/* Is this a recognised device type? */
|
||||
if (!(((int *) f->private)[MAJOR(dev->dev)]))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dev_filter *config_filter_create()
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!(f = dbg_malloc(sizeof (struct dev_filter)))) {
|
||||
log_error("lvm_v1_filter allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->passes_filter = passes_config_device_filter;
|
||||
|
||||
if (!(f->private = scan_proc_dev()))
|
||||
return NULL;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void config_filter_destroy(struct dev_filter *f)
|
||||
{
|
||||
dbg_free(f->private);
|
||||
dbg_free(f);
|
||||
return;
|
||||
}
|
||||
|
||||
static int *scan_proc_dev(void)
|
||||
{
|
||||
char line[80];
|
||||
FILE *procdevices = NULL;
|
||||
int ret = 0;
|
||||
int i, j = 0;
|
||||
int line_maj = 0;
|
||||
int blocksection = 0;
|
||||
int dev_len = 0;
|
||||
|
||||
int *max_partitions_by_major;
|
||||
|
||||
if (!(max_partitions_by_major = dbg_malloc(sizeof (int) * NUMBER_OF_MAJORS))) {
|
||||
log_error("Filter failed to allocate max_partitions_by_major");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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, procdevices) != NULL) {
|
||||
i = 0;
|
||||
while (line[i] == ' ' && line[i] != '\0')
|
||||
i++;
|
||||
|
||||
/* If it's not a number it may be name of section */
|
||||
line_maj = atoi(((char *) (line + i)));
|
||||
if (!line_maj) {
|
||||
blocksection = (line[i] == 'B') ? 1 : 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We only want block devices ... */
|
||||
if (!blocksection)
|
||||
continue;
|
||||
|
||||
/* Find the start of the device major name */
|
||||
while (line[i] != ' ' && line[i] != '\0')
|
||||
i++;
|
||||
while (line[i] == ' ' && line[i] != '\0')
|
||||
i++;
|
||||
|
||||
/* 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++) {
|
||||
|
||||
dev_len = strlen(device_info[j].name);
|
||||
if (dev_len <= strlen(line + i)
|
||||
&& !strncmp(device_info[j].name, line + i, dev_len)
|
||||
&& (line_maj < NUMBER_OF_MAJORS)) {
|
||||
max_partitions_by_major[line_maj] =
|
||||
device_info[j].max_partitions;
|
||||
ret++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(procdevices);
|
||||
return max_partitions_by_major;
|
||||
}
|
||||
29
lib/filters/filter.h
Normal file
29
lib/filters/filter.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* lvm 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.
|
||||
*
|
||||
* lvm 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_FILTER_H
|
||||
#define _LVM_FILTER_H
|
||||
|
||||
struct dev_filter *config_filter_create();
|
||||
|
||||
void config_filter_destroy(struct dev_filter *f);
|
||||
|
||||
#endif
|
||||
|
||||
481
lib/format1/disk-rep.c
Normal file
481
lib/format1/disk-rep.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "pool.h"
|
||||
#include "xlate.h"
|
||||
#include "log.h"
|
||||
|
||||
#define fail do {stack; return 0;} while(0)
|
||||
#define xx16(v) disk->v = xlate16(disk->v)
|
||||
#define xx32(v) disk->v = xlate32(disk->v)
|
||||
#define xx64(v) disk->v = xlate64(disk->v)
|
||||
|
||||
/*
|
||||
* Functions to perform the endian conversion
|
||||
* between disk and core. The same code works
|
||||
* both ways of course.
|
||||
*/
|
||||
static void _xlate_pv(struct pv_disk *disk)
|
||||
{
|
||||
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_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_lv(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);
|
||||
}
|
||||
|
||||
static void _xlate_vg(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);
|
||||
}
|
||||
|
||||
static void _xlate_extents(struct pe_disk *extents, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
extents[i].lv_num = xlate16(extents[i].lv_num);
|
||||
extents[i].le_num = xlate16(extents[i].le_num);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle both minor metadata formats.
|
||||
*/
|
||||
static int _munge_formats(struct pv_disk *pvd)
|
||||
{
|
||||
uint32_t pe_start;
|
||||
|
||||
switch (pvd->version) {
|
||||
case 1:
|
||||
pvd->pe_start = ((pvd->pe_on_disk.base +
|
||||
pvd->pe_on_disk.size) / SECTOR_SIZE);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pvd->version = 1;
|
||||
pe_start = pvd->pe_start * SECTOR_SIZE;
|
||||
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_pv(struct disk_list *data)
|
||||
{
|
||||
struct pv_disk *pvd = &data->pv;
|
||||
if (dev_read(data->dev, 0, sizeof(*pvd), pvd) != sizeof(*pvd))
|
||||
fail;
|
||||
_xlate_pv(pvd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_lv(struct device *dev, ulong pos, struct lv_disk *disk)
|
||||
{
|
||||
if (dev_read(dev, pos, sizeof(*disk), disk) != sizeof(*disk))
|
||||
fail;
|
||||
|
||||
_xlate_lv(disk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_vg(struct disk_list *data)
|
||||
{
|
||||
struct vg_disk *vgd = &data->vg;
|
||||
unsigned long pos = data->pv.vg_on_disk.base;
|
||||
if (dev_read(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
|
||||
fail;
|
||||
|
||||
_xlate_vg(vgd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_uuids(struct disk_list *data)
|
||||
{
|
||||
int num_read = 0;
|
||||
struct uuid_list *ul;
|
||||
char buffer[NAME_LEN];
|
||||
ulong pos = data->pv.pv_uuidlist_on_disk.base;
|
||||
ulong end = pos + data->pv.pv_uuidlist_on_disk.size;
|
||||
|
||||
while(pos < end && num_read < data->vg.pv_cur) {
|
||||
if (dev_read(data->dev, pos, sizeof(buffer), buffer) !=
|
||||
sizeof(buffer))
|
||||
fail;
|
||||
|
||||
if (!(ul = pool_alloc(data->mem, sizeof(*ul))))
|
||||
fail;
|
||||
|
||||
memcpy(ul->uuid, buffer, NAME_LEN);
|
||||
ul->uuid[NAME_LEN] = '\0';
|
||||
|
||||
list_add(&ul->list, &data->uuids);
|
||||
|
||||
pos += NAME_LEN;
|
||||
num_read++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _check_lv(struct lv_disk *lvd)
|
||||
{
|
||||
/* FIXME: add more checks */
|
||||
if (lvd->lv_name[0] == '\0') {
|
||||
log_debug("lv has no name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_lvs(struct disk_list *data)
|
||||
{
|
||||
int i;
|
||||
unsigned long pos;
|
||||
struct lvd_list *ll;
|
||||
|
||||
for(i = 0; i < data->vg.lv_cur; i++) {
|
||||
pos = data->pv.lv_on_disk.base + (i * sizeof(struct lv_disk));
|
||||
ll = pool_alloc(data->mem, sizeof(*ll));
|
||||
|
||||
if (!ll)
|
||||
fail;
|
||||
|
||||
if (!_read_lv(data->dev, pos, &ll->lv))
|
||||
fail;
|
||||
|
||||
if (!_check_lv(&ll->lv))
|
||||
fail;
|
||||
|
||||
list_add(&ll->list, &data->lvs);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_extents(struct disk_list *data)
|
||||
{
|
||||
size_t len = sizeof(struct pe_disk) * data->pv.pe_total;
|
||||
struct pe_disk *extents = pool_alloc(data->mem, len);
|
||||
unsigned long pos = data->pv.pe_on_disk.base;
|
||||
|
||||
if (!extents)
|
||||
fail;
|
||||
|
||||
if (dev_read(data->dev, pos, len, extents) != len)
|
||||
fail;
|
||||
|
||||
_xlate_extents(extents, data->pv.pe_total);
|
||||
data->extents = extents;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct disk_list *read_pv(struct device *dev, struct pool *mem,
|
||||
const char *vg_name)
|
||||
{
|
||||
struct disk_list *data = pool_alloc(mem, sizeof(*data));
|
||||
data->dev = dev;
|
||||
data->mem = mem;
|
||||
INIT_LIST_HEAD(&data->uuids);
|
||||
INIT_LIST_HEAD(&data->lvs);
|
||||
|
||||
if (!_read_pv(data)) {
|
||||
log_debug("Failed to read PV data from %s", dev->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (data->pv.id[0] != 'H' || data->pv.id[1] != 'M') {
|
||||
log_very_verbose("%s does not have a valid PV identifier",
|
||||
dev->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_munge_formats(&data->pv)) {
|
||||
log_very_verbose("Unknown metadata version %d found on %s",
|
||||
data->pv.version, dev->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* is it an orphan ?
|
||||
*/
|
||||
if (data->pv.vg_name == '\0') {
|
||||
log_very_verbose("%s is not a member of any VG", dev->name);
|
||||
return data;
|
||||
}
|
||||
|
||||
if (vg_name && strcmp(vg_name, data->pv.vg_name)) {
|
||||
log_very_verbose("%s is not a member of the VG %s",
|
||||
dev->name, vg_name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_vg(data)) {
|
||||
log_error("Failed to read VG data from PV (%s)", dev->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_uuids(data)) {
|
||||
log_error("Failed to read PV uuid list from %s", dev->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_lvs(data)) {
|
||||
log_error("Failed to read LV's from %s", dev->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_extents(data)) {
|
||||
log_error("Failed to read extents from %s", dev->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
bad:
|
||||
pool_free(data->mem, data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *head)
|
||||
{
|
||||
struct dev_iter *iter = dev_iter_create(filter);
|
||||
struct device *dev;
|
||||
struct disk_list *data = NULL;
|
||||
|
||||
for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
|
||||
if ((data = read_pv(dev, mem, vg_name)))
|
||||
list_add(&data->list, head);
|
||||
}
|
||||
dev_iter_destroy(iter);
|
||||
|
||||
if (list_empty(head))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int _write_vg(struct disk_list *data)
|
||||
{
|
||||
struct vg_disk *vgd = &data->vg;
|
||||
unsigned long pos = data->pv.vg_on_disk.base;
|
||||
|
||||
_xlate_vg(vgd);
|
||||
if (dev_write(data->dev, pos, sizeof(*vgd), vgd) != sizeof(*vgd))
|
||||
fail;
|
||||
|
||||
_xlate_vg(vgd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_uuids(struct disk_list *data)
|
||||
{
|
||||
struct uuid_list *ul;
|
||||
struct list_head *tmp;
|
||||
ulong pos = data->pv.pv_uuidlist_on_disk.base;
|
||||
ulong end = pos + data->pv.pv_uuidlist_on_disk.size;
|
||||
|
||||
list_for_each(tmp, &data->uuids) {
|
||||
if (pos >= end) {
|
||||
log_error("Too many uuids to fit on %s",
|
||||
data->dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ul = list_entry(tmp, struct uuid_list, list);
|
||||
if (dev_write(data->dev, pos, NAME_LEN, ul->uuid) != NAME_LEN)
|
||||
fail;
|
||||
|
||||
pos += NAME_LEN;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_lv(struct device *dev, ulong pos, struct lv_disk *disk)
|
||||
{
|
||||
_xlate_lv(disk);
|
||||
if (dev_write(dev, pos, sizeof(*disk), disk) != sizeof(*disk))
|
||||
fail;
|
||||
|
||||
_xlate_lv(disk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_lvs(struct disk_list *data)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
unsigned long pos;
|
||||
|
||||
list_for_each(tmp, &data->lvs) {
|
||||
struct lvd_list *ll = list_entry(tmp, struct lvd_list, list);
|
||||
pos = data->pv.lv_on_disk.base;
|
||||
|
||||
if (!_write_lv(data->dev, pos, &ll->lv))
|
||||
fail;
|
||||
|
||||
pos += sizeof(struct lv_disk);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_extents(struct disk_list *data)
|
||||
{
|
||||
size_t len = sizeof(struct pe_disk) * data->pv.pe_total;
|
||||
struct pe_disk *extents = data->extents;
|
||||
unsigned long pos = data->pv.pe_on_disk.base;
|
||||
|
||||
_xlate_extents(extents, data->pv.pe_total);
|
||||
if (dev_write(data->dev, pos, len, extents) != len)
|
||||
fail;
|
||||
|
||||
_xlate_extents(extents, data->pv.pe_total);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_pv(struct disk_list *data)
|
||||
{
|
||||
struct pv_disk *disk = &data->pv;
|
||||
|
||||
_xlate_pv(disk);
|
||||
if (dev_write(data->dev, 0, sizeof(*disk), disk) != sizeof(*disk))
|
||||
fail;
|
||||
|
||||
_xlate_pv(disk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _write_all_pv(struct disk_list *data)
|
||||
{
|
||||
const char *pv_name = data->dev->name;
|
||||
|
||||
if (!_write_pv(data)) {
|
||||
log_error("Failed to write PV structure onto %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop here for orphan pv's.
|
||||
*/
|
||||
if (data->pv.vg_name[0] == '\0')
|
||||
return 1;
|
||||
|
||||
if (!_write_vg(data)) {
|
||||
log_error("Failed to write VG data to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_uuids(data)) {
|
||||
log_error("Failed to write PV uuid list to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_lvs(data)) {
|
||||
log_error("Failed to write LV's to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_write_extents(data)) {
|
||||
log_error("Failed to write extents to %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes all the given pv's to disk. Does very
|
||||
* little sanity checking, so make sure correct
|
||||
* data is passed to here.
|
||||
*/
|
||||
int write_pvs(struct list_head *pvs)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct disk_list *dl;
|
||||
|
||||
list_for_each(tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
if (!(_write_all_pv(dl)))
|
||||
fail;
|
||||
|
||||
log_debug("Successfully wrote data to %s", dl->dev->name);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
234
lib/format1/disk-rep.h
Normal file
234
lib/format1/disk-rep.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef DISK_REP_FORMAT1_H
|
||||
#define DISK_REP_FORMAT1_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
#include "metadata.h"
|
||||
#include "pool.h"
|
||||
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
#define MAX_PV 256
|
||||
#define MAX_LV 256
|
||||
#define MAX_VG 99
|
||||
|
||||
#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 UNMAPPED_EXTENT 0
|
||||
|
||||
/* volume group */
|
||||
#define VG_ACTIVE 0x01 /* vg_status */
|
||||
#define VG_EXPORTED 0x02 /* " */
|
||||
#define VG_EXTENDABLE 0x04 /* " */
|
||||
|
||||
#define VG_READ 0x01 /* vg_access */
|
||||
#define VG_WRITE 0x02 /* " */
|
||||
#define VG_CLUSTERED 0x04 /* " */
|
||||
#define VG_SHARED 0x08 /* " */
|
||||
|
||||
/* logical volume */
|
||||
#define LV_ACTIVE 0x01 /* lv_status */
|
||||
#define LV_SPINDOWN 0x02 /* " */
|
||||
|
||||
#define LV_READ 0x01 /* lv_access */
|
||||
#define LV_WRITE 0x02 /* " */
|
||||
#define LV_SNAPSHOT 0x04 /* " */
|
||||
#define LV_SNAPSHOT_ORG 0x08 /* " */
|
||||
|
||||
#define LV_BADBLOCK_ON 0x01 /* lv_badblock */
|
||||
|
||||
#define LV_STRICT 0x01 /* lv_allocation */
|
||||
#define LV_CONTIGUOUS 0x02 /* " */
|
||||
|
||||
/* physical volume */
|
||||
#define PV_ACTIVE 0x01 /* pv_status */
|
||||
#define PV_ALLOCATABLE 0x02 /* pv_allocatable */
|
||||
|
||||
|
||||
struct data_area {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct pv_disk {
|
||||
uint8_t id[2];
|
||||
uint16_t version; /* lvm version */
|
||||
struct data_area pv_on_disk;
|
||||
struct data_area vg_on_disk;
|
||||
struct data_area pv_uuidlist_on_disk;
|
||||
struct data_area lv_on_disk;
|
||||
struct data_area pe_on_disk;
|
||||
uint8_t pv_uuid[NAME_LEN];
|
||||
uint8_t vg_name[NAME_LEN];
|
||||
uint8_t system_id[NAME_LEN]; /* for vgexport/vgimport */
|
||||
uint32_t pv_major;
|
||||
uint32_t pv_number;
|
||||
uint32_t pv_status;
|
||||
uint32_t pv_allocatable;
|
||||
uint32_t pv_size;
|
||||
uint32_t lv_cur;
|
||||
uint32_t pe_size;
|
||||
uint32_t pe_total;
|
||||
uint32_t pe_allocated;
|
||||
|
||||
/* only present on version == 2 pv's */
|
||||
uint32_t pe_start;
|
||||
};
|
||||
|
||||
struct lv_disk {
|
||||
uint8_t lv_name[NAME_LEN];
|
||||
uint8_t vg_name[NAME_LEN];
|
||||
uint32_t lv_access;
|
||||
uint32_t lv_status;
|
||||
uint32_t lv_open;
|
||||
uint32_t lv_dev;
|
||||
uint32_t lv_number;
|
||||
uint32_t lv_mirror_copies; /* for future use */
|
||||
uint32_t lv_recovery; /* " */
|
||||
uint32_t lv_schedule; /* " */
|
||||
uint32_t lv_size;
|
||||
uint32_t lv_snapshot_minor; /* minor number of original */
|
||||
uint16_t lv_chunk_size; /* chunk size of snapshot */
|
||||
uint16_t dummy;
|
||||
uint32_t lv_allocated_le;
|
||||
uint32_t lv_stripes;
|
||||
uint32_t lv_stripesize;
|
||||
uint32_t lv_badblock; /* for future use */
|
||||
uint32_t lv_allocation;
|
||||
uint32_t lv_io_timeout; /* for future use */
|
||||
uint32_t lv_read_ahead;
|
||||
};
|
||||
|
||||
struct vg_disk {
|
||||
uint8_t vg_uuid[ID_LEN]; /* volume group UUID */
|
||||
uint8_t vg_name_dummy[NAME_LEN - ID_LEN]; /* rest of v1 VG name */
|
||||
uint32_t vg_number; /* volume group number */
|
||||
uint32_t vg_access; /* read/write */
|
||||
uint32_t vg_status; /* active or not */
|
||||
uint32_t lv_max; /* maximum logical volumes */
|
||||
uint32_t lv_cur; /* current logical volumes */
|
||||
uint32_t lv_open; /* open logical volumes */
|
||||
uint32_t pv_max; /* maximum physical volumes */
|
||||
uint32_t pv_cur; /* current physical volumes FU */
|
||||
uint32_t pv_act; /* active physical volumes */
|
||||
uint32_t dummy;
|
||||
uint32_t vgda; /* volume group descriptor arrays FU */
|
||||
uint32_t pe_size; /* physical extent size in sectors */
|
||||
uint32_t pe_total; /* total of physical extents */
|
||||
uint32_t pe_allocated; /* allocated physical extents */
|
||||
uint32_t pvg_total; /* physical volume groups FU */
|
||||
};
|
||||
|
||||
struct pe_disk {
|
||||
uint16_t lv_num;
|
||||
uint16_t le_num;
|
||||
};
|
||||
|
||||
|
||||
struct uuid_list {
|
||||
struct list_head list;
|
||||
char uuid[NAME_LEN];
|
||||
};
|
||||
|
||||
struct lvd_list {
|
||||
struct list_head list;
|
||||
struct lv_disk lv;
|
||||
};
|
||||
|
||||
struct disk_list {
|
||||
struct pool *mem;
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
|
||||
struct pv_disk pv;
|
||||
struct vg_disk vg;
|
||||
struct list_head uuids;
|
||||
struct list_head lvs;
|
||||
struct pe_disk *extents;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Layout constants.
|
||||
*/
|
||||
#define METADATA_ALIGN 4096UL
|
||||
#define PE_ALIGN (65536UL / SECTOR_SIZE)
|
||||
|
||||
#define METADATA_BASE 0UL
|
||||
#define PV_SIZE 1024UL
|
||||
#define VG_SIZE 4096UL
|
||||
|
||||
|
||||
/*
|
||||
* Functions to calculate layout info.
|
||||
*/
|
||||
int calculate_layout(struct disk_list *dl);
|
||||
int calculate_extent_count(struct physical_volume *pv);
|
||||
|
||||
|
||||
/*
|
||||
* Low level io routines which read/write
|
||||
* disk_lists.
|
||||
*/
|
||||
struct disk_list *read_pv(struct device *dev, struct pool *mem,
|
||||
const char *vg_name);
|
||||
|
||||
int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter,
|
||||
struct pool *mem, struct list_head *results);
|
||||
|
||||
int write_pvs(struct list_head *pvs);
|
||||
|
||||
|
||||
/*
|
||||
* Functions to translate to between disk and in
|
||||
* core structures.
|
||||
*/
|
||||
int import_pv(struct pool *mem, struct device *dev,
|
||||
struct physical_volume *pv, struct pv_disk *pvd);
|
||||
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 export_vg(struct vg_disk *vgd, struct volume_group *vg);
|
||||
|
||||
int import_lv(struct pool *mem, struct logical_volume *lv,
|
||||
struct lv_disk *lvd);
|
||||
void export_lv(struct lv_disk *lvd, struct volume_group *vg,
|
||||
struct logical_volume *lv, const char *prefix);
|
||||
|
||||
int import_extents(struct pool *mem, struct volume_group *vg,
|
||||
struct list_head *pvs);
|
||||
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 list_head *pvs,
|
||||
struct list_head *results, int *count);
|
||||
|
||||
int import_lvs(struct pool *mem, struct volume_group *vg,
|
||||
struct list_head *pvs);
|
||||
int export_lvs(struct disk_list *dl, struct volume_group *vg,
|
||||
struct physical_volume *pv, const char *prefix);
|
||||
|
||||
int export_uuids(struct disk_list *dl, struct volume_group *vg);
|
||||
|
||||
void export_numbers(struct list_head *pvs, struct volume_group *vg);
|
||||
|
||||
void export_pv_act(struct list_head *pvs);
|
||||
|
||||
/* blech */
|
||||
int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
|
||||
int *result);
|
||||
int export_vg_number(struct list_head *pvs, const char *vg_name,
|
||||
struct dev_filter *filter);
|
||||
|
||||
|
||||
#endif
|
||||
471
lib/format1/format1.c
Normal file
471
lib/format1/format1.c
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "hash.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "display.h"
|
||||
|
||||
/* VG consistency checks */
|
||||
static int _check_vgs(struct list_head *pvs)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct disk_list *dl;
|
||||
struct disk_list *first = NULL;
|
||||
|
||||
int pv_count = 0;
|
||||
|
||||
/* check all the vg's are the same */
|
||||
list_for_each(tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
|
||||
if (!first)
|
||||
first = dl;
|
||||
else if (memcmp(&first->vg, &dl->vg, sizeof(first->vg))) {
|
||||
log_err("VG data differs between PVs %s and %s",
|
||||
first->dev->name, dl->dev->name);
|
||||
return 0;
|
||||
}
|
||||
pv_count++;
|
||||
}
|
||||
|
||||
/* On entry to fn, list known to be non-empty */
|
||||
if (!(pv_count == dl->vg.pv_cur)) {
|
||||
log_error("Only %d out of %d PV(s) found for VG %s",
|
||||
pv_count, dl->vg.pv_cur, dl->pv.vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct volume_group *_build_vg(struct pool *mem, struct list_head *pvs)
|
||||
{
|
||||
struct volume_group *vg = pool_alloc(mem, sizeof(*vg));
|
||||
struct disk_list *dl;
|
||||
|
||||
if (!vg)
|
||||
goto bad;
|
||||
|
||||
if (list_empty(pvs))
|
||||
goto bad;
|
||||
|
||||
dl = list_entry(pvs->next, struct disk_list, list);
|
||||
|
||||
memset(vg, 0, sizeof(*vg));
|
||||
|
||||
INIT_LIST_HEAD(&vg->pvs);
|
||||
INIT_LIST_HEAD(&vg->lvs);
|
||||
|
||||
if (!_check_vgs(pvs))
|
||||
goto bad;
|
||||
|
||||
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))
|
||||
goto bad;
|
||||
|
||||
if (!import_extents(mem, vg, pvs))
|
||||
goto bad;
|
||||
|
||||
return vg;
|
||||
|
||||
bad:
|
||||
stack;
|
||||
pool_free(mem, vg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct volume_group *_vg_read(struct io_space *is, const char *vg_name)
|
||||
{
|
||||
struct pool *mem = pool_create(1024 * 10);
|
||||
struct list_head pvs;
|
||||
struct volume_group *vg;
|
||||
INIT_LIST_HEAD(&pvs);
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Strip prefix if present */
|
||||
if (!strncmp(vg_name, is->prefix, strlen(is->prefix)))
|
||||
vg_name += strlen(is->prefix);
|
||||
|
||||
if (!read_pvs_in_vg(vg_name, is->filter, mem, &pvs)) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(vg = _build_vg(is->mem, &pvs)))
|
||||
stack;
|
||||
|
||||
pool_destroy(mem);
|
||||
return vg;
|
||||
}
|
||||
|
||||
static struct disk_list *_flatten_pv(struct pool *mem, struct volume_group *vg,
|
||||
struct physical_volume *pv,
|
||||
const char *prefix)
|
||||
{
|
||||
struct disk_list *dl = pool_alloc(mem, sizeof(*dl));
|
||||
|
||||
if (!dl) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dl->mem = mem;
|
||||
dl->dev = pv->dev;
|
||||
|
||||
INIT_LIST_HEAD(&dl->uuids);
|
||||
INIT_LIST_HEAD(&dl->lvs);
|
||||
|
||||
if (!export_pv(&dl->pv, pv) ||
|
||||
!export_vg(&dl->vg, vg) ||
|
||||
!export_uuids(dl, vg) ||
|
||||
!export_lvs(dl, vg, pv, prefix) ||
|
||||
!calculate_layout(dl)) {
|
||||
stack;
|
||||
pool_free(mem, dl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dl;
|
||||
}
|
||||
|
||||
static int _flatten_vg(struct pool *mem, struct volume_group *vg,
|
||||
struct list_head *pvs, const char *prefix,
|
||||
struct dev_filter *filter)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct pv_list *pvl;
|
||||
struct disk_list *data;
|
||||
|
||||
list_for_each(tmp, &vg->pvs) {
|
||||
pvl = list_entry(tmp, struct pv_list, list);
|
||||
|
||||
if (!(data = _flatten_pv(mem, vg, &pvl->pv, prefix))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(&data->list, pvs);
|
||||
}
|
||||
|
||||
export_numbers(pvs, vg);
|
||||
export_pv_act(pvs);
|
||||
|
||||
if (!export_vg_number(pvs, vg->name, filter)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _vg_write(struct io_space *is, struct volume_group *vg)
|
||||
{
|
||||
struct pool *mem = pool_create(1024 * 10);
|
||||
struct list_head pvs;
|
||||
int r = 0;
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&pvs);
|
||||
|
||||
r = (_flatten_vg(mem, vg, &pvs, is->prefix, is->filter) &&
|
||||
write_pvs(&pvs));
|
||||
pool_destroy(mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct physical_volume *_pv_read(struct io_space *is,
|
||||
const char *name)
|
||||
{
|
||||
struct pool *mem = pool_create(1024);
|
||||
struct physical_volume *pv;
|
||||
struct disk_list *dl;
|
||||
struct device *dev;
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(dev = dev_cache_get(name, is->filter))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(dl = read_pv(dev, mem, NULL))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(pv = pool_alloc(is->mem, sizeof(*pv)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!import_pv(is->mem, dl->dev, pv, &dl->pv)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pool_destroy(mem);
|
||||
return pv;
|
||||
|
||||
bad:
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct list_head *_get_pvs(struct io_space *is)
|
||||
{
|
||||
struct pool *mem = pool_create(1024 * 10);
|
||||
struct list_head pvs, *results;
|
||||
uint32_t count;
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(results = pool_alloc(is->mem, sizeof(*results)))) {
|
||||
stack;
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&pvs);
|
||||
INIT_LIST_HEAD(results);
|
||||
|
||||
if (!read_pvs_in_vg(NULL, is->filter, mem, &pvs)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!import_pvs(is->mem, &pvs, results, &count)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pool_destroy(mem);
|
||||
return results;
|
||||
|
||||
bad:
|
||||
pool_free(mem, results);
|
||||
pool_destroy(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _find_vg_name(struct list_head *names, const char *vg)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct name_list *nl;
|
||||
|
||||
list_for_each(tmp, names) {
|
||||
nl = list_entry(tmp, struct name_list, list);
|
||||
if (!strcmp(nl->name, vg))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct list_head *_get_vgs(struct io_space *is)
|
||||
{
|
||||
struct list_head *tmp, *pvs;
|
||||
struct list_head *names = pool_alloc(is->mem, sizeof(*names));
|
||||
struct name_list *nl;
|
||||
|
||||
if (!names) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(names);
|
||||
|
||||
if (!(pvs = _get_pvs(is))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_for_each(tmp, pvs) {
|
||||
struct pv_list *pvl = list_entry(tmp, struct pv_list, list);
|
||||
|
||||
if (!(*pvl->pv.vg_name) ||
|
||||
_find_vg_name(names, pvl->pv.vg_name))
|
||||
continue;
|
||||
|
||||
if (!(nl = pool_alloc(is->mem, sizeof(*nl)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(nl->name = pool_strdup(is->mem, pvl->pv.vg_name))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_add(&nl->list, names);
|
||||
}
|
||||
|
||||
if (list_empty(names))
|
||||
goto bad;
|
||||
|
||||
return names;
|
||||
|
||||
bad:
|
||||
pool_free(is->mem, names);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _pv_setup(struct io_space *is, struct physical_volume *pv,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
/*
|
||||
* This works out pe_start and pe_count.
|
||||
*/
|
||||
if (!calculate_extent_count(pv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _pv_write(struct io_space *is, struct physical_volume *pv)
|
||||
{
|
||||
struct pool *mem;
|
||||
struct disk_list *dl;
|
||||
struct list_head pvs;
|
||||
|
||||
INIT_LIST_HEAD(&pvs);
|
||||
|
||||
if (*pv->vg_name) {
|
||||
log_error("Assertion failed: can't _pv_write non-orphan PV "
|
||||
"(in VG %s)", pv->vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(mem = pool_create(1024))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!(dl = pool_alloc(mem, sizeof(*dl)))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
dl->mem = mem;
|
||||
dl->dev = pv->dev;
|
||||
|
||||
if (!export_pv(&dl->pv, pv)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
list_add(&dl->list, &pvs);
|
||||
if (!write_pvs(&pvs)) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pool_destroy(mem);
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
pool_destroy(mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _vg_setup(struct io_space *is, struct volume_group *vg)
|
||||
{
|
||||
/* just check max_pv and max_lv */
|
||||
if (vg->max_lv >= MAX_LV)
|
||||
vg->max_lv = MAX_LV - 1;
|
||||
|
||||
if (vg->max_pv >= MAX_PV)
|
||||
vg->max_pv = MAX_PV - 1;
|
||||
|
||||
if (vg->extent_size > MAX_PE_SIZE || vg->extent_size < MIN_PE_SIZE) {
|
||||
char *dummy, *dummy2;
|
||||
|
||||
log_error("Extent size must be between %s and %s",
|
||||
(dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT)),
|
||||
(dummy2 = display_size(MAX_PE_SIZE / 2, SIZE_SHORT)));
|
||||
|
||||
dbg_free(dummy);
|
||||
dbg_free(dummy2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vg->extent_size % MIN_PE_SIZE) {
|
||||
char *dummy;
|
||||
log_error("Extent size must be multiple of %s",
|
||||
(dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT)));
|
||||
dbg_free(dummy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Redundant? */
|
||||
if (vg->extent_size & (vg->extent_size - 1)) {
|
||||
log_error("Extent size must be power of 2");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void _destroy(struct io_space *ios)
|
||||
{
|
||||
dbg_free(ios->prefix);
|
||||
pool_destroy(ios->mem);
|
||||
dbg_free(ios);
|
||||
}
|
||||
|
||||
struct io_space *create_lvm1_format(const char *prefix, struct pool *mem,
|
||||
struct dev_filter *filter)
|
||||
{
|
||||
struct io_space *ios = dbg_malloc(sizeof(*ios));
|
||||
|
||||
ios->get_vgs = _get_vgs;
|
||||
ios->get_pvs = _get_pvs;
|
||||
ios->pv_read = _pv_read;
|
||||
ios->pv_setup = _pv_setup;
|
||||
ios->pv_write = _pv_write;
|
||||
ios->vg_read = _vg_read;
|
||||
ios->vg_setup = _vg_setup;
|
||||
ios->vg_write = _vg_write;
|
||||
ios->destroy = _destroy;
|
||||
|
||||
ios->prefix = dbg_malloc(strlen(prefix) + 1);
|
||||
if (!ios->prefix) {
|
||||
stack;
|
||||
dbg_free(ios);
|
||||
return 0;
|
||||
}
|
||||
strcpy(ios->prefix, prefix);
|
||||
|
||||
ios->mem = mem;
|
||||
ios->filter = filter;
|
||||
ios->private = NULL;
|
||||
|
||||
return ios;
|
||||
}
|
||||
15
lib/format1/format1.h
Normal file
15
lib/format1/format1.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef LVM_FORMAT1_H
|
||||
#define LVM_FORMAT1_H
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
struct io_space *create_lvm1_format(const char *prefix, struct pool *mem,
|
||||
struct dev_filter *filter);
|
||||
|
||||
#endif
|
||||
628
lib/format1/import-export.c
Normal file
628
lib/format1/import-export.c
Normal file
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "hash.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
static int _check_vg_name(const char *name)
|
||||
{
|
||||
return strlen(name) < NAME_LEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts the last part of a path.
|
||||
*/
|
||||
static char *_create_lv_name(struct pool *mem, const char *full_name)
|
||||
{
|
||||
const char *ptr = strrchr(full_name, '/');
|
||||
|
||||
if (!ptr)
|
||||
ptr = full_name;
|
||||
else
|
||||
ptr++;
|
||||
|
||||
return pool_strdup(mem, ptr);
|
||||
}
|
||||
|
||||
static struct logical_volume *_find_lv(struct volume_group *vg,
|
||||
const char *name)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct logical_volume *lv;
|
||||
struct lv_list *ll;
|
||||
const char *ptr = strrchr(name, '/') + 1;
|
||||
|
||||
list_for_each(tmp, &vg->lvs) {
|
||||
ll = list_entry(tmp, struct lv_list, list);
|
||||
lv = &ll->lv;
|
||||
|
||||
if (!strcmp(ptr, lv->name))
|
||||
return lv;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct physical_volume *_find_pv(struct volume_group *vg,
|
||||
struct device *dev)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct physical_volume *pv;
|
||||
struct pv_list *pl;
|
||||
|
||||
list_for_each(tmp, &vg->pvs) {
|
||||
pl = list_entry(tmp, struct pv_list, list);
|
||||
pv = &pl->pv;
|
||||
if (dev == pv->dev)
|
||||
return pv;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _fill_lv_array(struct logical_volume **lvs,
|
||||
struct volume_group *vg, struct disk_list *dl)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct logical_volume *lv;
|
||||
int i = 0;
|
||||
|
||||
list_for_each(tmp, &dl->lvs) {
|
||||
struct lvd_list *ll = list_entry(tmp, struct lvd_list, list);
|
||||
|
||||
if (!(lv = _find_lv(vg, ll->lv.lv_name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lvs[i] = lv;
|
||||
i++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_pv(struct pool *mem, struct device *dev,
|
||||
struct physical_volume *pv, struct pv_disk *pvd)
|
||||
{
|
||||
memset(pv, 0, sizeof(*pv));
|
||||
memcpy(&pv->id, pvd->pv_uuid, ID_LEN);
|
||||
|
||||
pv->dev = dev;
|
||||
if (!(pv->vg_name = pool_strdup(mem, pvd->vg_name))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pvd->pv_status & PV_ACTIVE)
|
||||
pv->status |= ACTIVE;
|
||||
|
||||
if (pvd->pv_allocatable)
|
||||
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;
|
||||
}
|
||||
|
||||
static int _system_id(char *system_id)
|
||||
{
|
||||
struct utsname uts;
|
||||
|
||||
if (uname(&uts) != 0) {
|
||||
log_sys_error("uname", "_system_id");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sprintf(system_id, "%s%lu", uts.nodename, time(NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_pv(struct pv_disk *pvd, struct physical_volume *pv)
|
||||
{
|
||||
memset(pvd, 0, sizeof(*pvd));
|
||||
|
||||
pvd->id[0] = 'H';
|
||||
pvd->id[1] = 'M';
|
||||
pvd->version = 1;
|
||||
|
||||
memcpy(pvd->pv_uuid, pv->id.uuid, ID_LEN);
|
||||
|
||||
if (!_check_vg_name(pv->vg_name)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(pvd->vg_name, 0, sizeof(pvd->vg_name));
|
||||
|
||||
if (pv->vg_name)
|
||||
strncpy(pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name));
|
||||
|
||||
//pvd->pv_major = MAJOR(pv->dev);
|
||||
|
||||
if (pv->status & ACTIVE)
|
||||
pvd->pv_status |= PV_ACTIVE;
|
||||
|
||||
if (pv->status & ALLOCATED_PV)
|
||||
pvd->pv_allocatable = PV_ALLOCATABLE;
|
||||
|
||||
pvd->pv_size = pv->size;
|
||||
pvd->lv_cur = 0; /* this is set when exporting the lv list */
|
||||
pvd->pe_size = pv->pe_size;
|
||||
pvd->pe_total = pv->pe_count;
|
||||
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)
|
||||
{
|
||||
struct vg_disk *vgd = &dl->vg;
|
||||
memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
|
||||
|
||||
if (!_check_vg_name(dl->pv.vg_name)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(vg->name = pool_strdup(mem, dl->pv.vg_name))) {
|
||||
stack;
|
||||
return 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 |= EXTENDABLE_VG;
|
||||
|
||||
if (vgd->vg_access & VG_READ)
|
||||
vg->status |= LVM_READ;
|
||||
|
||||
if (vgd->vg_access & VG_WRITE)
|
||||
vg->status |= LVM_WRITE;
|
||||
|
||||
if (vgd->vg_access & VG_CLUSTERED)
|
||||
vg->status |= CLUSTERED;
|
||||
|
||||
if (vgd->vg_access & VG_SHARED)
|
||||
vg->status |= SHARED;
|
||||
|
||||
vg->extent_size = vgd->pe_size;
|
||||
vg->extent_count = vgd->pe_total;
|
||||
vg->free_count = vgd->pe_total - vgd->pe_allocated;
|
||||
vg->max_lv = vgd->lv_max;
|
||||
vg->max_pv = vgd->pv_max;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_vg(struct vg_disk *vgd, struct volume_group *vg)
|
||||
{
|
||||
memset(vgd, 0, sizeof(*vgd));
|
||||
memcpy(vgd->vg_uuid, vg->id.uuid, ID_LEN);
|
||||
|
||||
if (vg->status & LVM_READ)
|
||||
vgd->vg_access |= VG_READ;
|
||||
|
||||
if (vg->status & LVM_WRITE)
|
||||
vgd->vg_access |= VG_WRITE;
|
||||
|
||||
if (vg->status & CLUSTERED)
|
||||
vgd->vg_access |= VG_CLUSTERED;
|
||||
|
||||
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 & EXTENDABLE_VG)
|
||||
vgd->vg_status |= VG_EXTENDABLE;
|
||||
|
||||
vgd->lv_max = vg->max_lv;
|
||||
vgd->lv_cur = vg->lv_count;
|
||||
|
||||
vgd->pv_max = vg->max_pv;
|
||||
vgd->pv_cur = vg->pv_count;
|
||||
|
||||
vgd->pe_size = vg->extent_size;
|
||||
vgd->pe_total = vg->extent_count;
|
||||
vgd->pe_allocated = vg->extent_count - vg->free_count;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_lv(struct pool *mem, struct logical_volume *lv, struct lv_disk *lvd)
|
||||
{
|
||||
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_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;
|
||||
|
||||
if (lvd->lv_allocation == LV_STRICT)
|
||||
lv->status |= ALLOC_STRICT;
|
||||
else
|
||||
lv->status |= ALLOC_CONTIGUOUS;
|
||||
|
||||
lv->size = lvd->lv_size;
|
||||
lv->le_count = lvd->lv_allocated_le;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void export_lv(struct lv_disk *lvd, struct volume_group *vg,
|
||||
struct logical_volume *lv, const char *prefix)
|
||||
{
|
||||
memset(lvd, 0, sizeof(*lvd));
|
||||
snprintf(lvd->lv_name, sizeof(lvd->lv_name), "%s/%s",
|
||||
prefix, lv->name);
|
||||
|
||||
_check_vg_name(vg->name);
|
||||
strcpy(lvd->vg_name, vg->name);
|
||||
|
||||
if (lv->status & LVM_READ)
|
||||
lvd->lv_access |= LV_READ;
|
||||
|
||||
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;
|
||||
|
||||
lvd->lv_size = lv->size;
|
||||
lvd->lv_allocated_le = lv->le_count;
|
||||
|
||||
if (lv->status & BADBLOCK_ON)
|
||||
lvd->lv_badblock = LV_BADBLOCK_ON;
|
||||
|
||||
if (lv->status & ALLOC_STRICT)
|
||||
lvd->lv_allocation = LV_STRICT;
|
||||
else
|
||||
lvd->lv_allocation = LV_CONTIGUOUS;
|
||||
}
|
||||
|
||||
int import_extents(struct pool *mem, struct volume_group *vg,
|
||||
struct list_head *pvs)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
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;
|
||||
|
||||
list_for_each(tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
pv = _find_pv(vg, dl->dev);
|
||||
e = dl->extents;
|
||||
|
||||
/* build an array of lv's for this pv */
|
||||
if (!_fill_lv_array(lvs, vg, dl)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < dl->pv.pe_total; i++) {
|
||||
lv_num = e[i].lv_num;
|
||||
|
||||
if (lv_num == UNMAPPED_EXTENT)
|
||||
continue;
|
||||
|
||||
else if(lv_num > dl->pv.lv_cur) {
|
||||
log_err("invalid lv in extent map\n");
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
lv_num--;
|
||||
lv = lvs[lv_num];
|
||||
le = e[i].le_num;
|
||||
|
||||
lv->map[le].pv = pv;
|
||||
lv->map[le].pe = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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_head *pvs,
|
||||
struct list_head *results, int *count)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct disk_list *dl;
|
||||
struct pv_list *pvl;
|
||||
|
||||
*count = 0;
|
||||
list_for_each(tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
pvl = pool_alloc(mem, sizeof(*pvl));
|
||||
|
||||
if (!pvl) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!import_pv(mem, dl->dev, &pvl->pv, &dl->pv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(&pvl->list, results);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct logical_volume *_add_lv(struct pool *mem,
|
||||
struct volume_group *vg,
|
||||
struct lv_disk *lvd)
|
||||
{
|
||||
struct lv_list *ll = pool_alloc(mem, sizeof(*ll));
|
||||
struct logical_volume *lv;
|
||||
|
||||
if (!ll) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
lv = &ll->lv;
|
||||
|
||||
if (!import_lv(mem, &ll->lv, lvd)) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add(&ll->list, &vg->lvs);
|
||||
vg->lv_count++;
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
int import_lvs(struct pool *mem, struct volume_group *vg,
|
||||
struct list_head *pvs)
|
||||
{
|
||||
struct list_head *tmp, *tmp2;
|
||||
struct disk_list *dl;
|
||||
struct lvd_list *ll;
|
||||
struct lv_disk *lvd;
|
||||
|
||||
list_for_each(tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
list_for_each(tmp2, &dl->lvs) {
|
||||
ll = list_entry(tmp2, struct lvd_list, list);
|
||||
lvd = &ll->lv;
|
||||
|
||||
if (!_find_lv(vg, lvd->lv_name) &&
|
||||
!_add_lv(mem, vg, lvd)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_lvs(struct disk_list *dl, struct volume_group *vg,
|
||||
struct physical_volume *pv, const char *prefix)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct lv_list *ll;
|
||||
struct lvd_list *lvdl;
|
||||
int lv_num = 1, len;
|
||||
|
||||
/*
|
||||
* setup the pv's extents array
|
||||
*/
|
||||
len = sizeof(struct pe_disk) * dl->pv.pe_total;
|
||||
if (!(dl->extents = pool_alloc(dl->mem, len))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
memset(dl->extents, 0, len);
|
||||
|
||||
|
||||
list_for_each(tmp, &vg->lvs) {
|
||||
ll = list_entry(tmp, struct lv_list, list);
|
||||
if (!(lvdl = pool_alloc(dl->mem, sizeof(*lvdl)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export_lv(&lvdl->lv, vg, &ll->lv, prefix);
|
||||
if (!export_extents(dl, lv_num++, &ll->lv, pv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add(&lvdl->list, &dl->lvs);
|
||||
dl->pv.lv_cur++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int export_uuids(struct disk_list *dl, struct volume_group *vg)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct uuid_list *ul;
|
||||
struct pv_list *pvl;
|
||||
|
||||
list_for_each(tmp, &vg->pvs) {
|
||||
pvl = list_entry(tmp, struct pv_list, list);
|
||||
if (!(ul = pool_alloc(dl->mem, sizeof(*ul)))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(ul->uuid, 0, sizeof(ul->uuid));
|
||||
memcpy(ul->uuid, pvl->pv.id.uuid, ID_LEN);
|
||||
|
||||
list_add(&ul->list, &dl->uuids);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _get_lv_number(struct volume_group *vg, const char *name)
|
||||
{
|
||||
/* FIXME: inefficient */
|
||||
struct list_head *tmp;
|
||||
struct lv_list *ll;
|
||||
int r = 0;
|
||||
|
||||
list_for_each (tmp, &vg->lvs) {
|
||||
ll = list_entry(tmp, struct lv_list, list);
|
||||
if (!strcmp(ll->lv.name, name))
|
||||
break;
|
||||
r++;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* This calculates the nasty pv_number and
|
||||
* lv_number fields used by LVM1. Very
|
||||
* inefficient code.
|
||||
*/
|
||||
void export_numbers(struct list_head *pvs, struct volume_group *vg)
|
||||
{
|
||||
struct list_head *tmp, *tmp2;
|
||||
struct disk_list *dl;
|
||||
struct lvd_list *ll;
|
||||
int pv_num = 1;
|
||||
|
||||
list_for_each (tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
dl->pv.pv_number = pv_num++;
|
||||
|
||||
list_for_each (tmp2, &dl->lvs) {
|
||||
ll = list_entry(tmp2, struct lvd_list, list);
|
||||
ll->lv.lv_number = _get_lv_number(vg, ll->lv.lv_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate vg_disk->pv_act.
|
||||
*/
|
||||
void export_pv_act(struct list_head *pvs)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct disk_list *dl;
|
||||
int act = 0;
|
||||
|
||||
list_for_each (tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
if (dl->pv.pv_status & PV_ACTIVE)
|
||||
act++;
|
||||
}
|
||||
|
||||
list_for_each (tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
dl->vg.pv_act = act;
|
||||
}
|
||||
}
|
||||
|
||||
int export_vg_number(struct list_head *pvs, const char *vg_name,
|
||||
struct dev_filter *filter)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct disk_list *dl;
|
||||
int vg_num;
|
||||
|
||||
if (!get_free_vg_number(filter, vg_name, &vg_num)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each (tmp, pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
dl->vg.vg_number = vg_num;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
151
lib/format1/layout.c
Normal file
151
lib/format1/layout.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "disk-rep.h"
|
||||
#include "log.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
|
||||
/*
|
||||
* Only works with powers of 2.
|
||||
*/
|
||||
static inline ulong _round_up(ulong n, ulong size)
|
||||
{
|
||||
size--;
|
||||
return (n + size) & ~size;
|
||||
}
|
||||
|
||||
static inline ulong _div_up(ulong n, ulong size)
|
||||
{
|
||||
return _round_up(n, size) / size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each chunk of metadata should be aligned to
|
||||
* METADATA_ALIGN.
|
||||
*/
|
||||
static uint32_t _next_base(struct data_area *area)
|
||||
{
|
||||
return _round_up(area->base + area->size, METADATA_ALIGN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick calculation based on pe_start.
|
||||
*/
|
||||
static int _adjust_pe_on_disk(struct pv_disk *pvd)
|
||||
{
|
||||
uint32_t pe_start = pvd->pe_start * SECTOR_SIZE;
|
||||
|
||||
if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size)
|
||||
return 0;
|
||||
|
||||
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _calc_simple_layout(struct pv_disk *pvd)
|
||||
{
|
||||
pvd->pv_on_disk.base = METADATA_BASE;
|
||||
pvd->pv_on_disk.size = PV_SIZE;
|
||||
|
||||
pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk);
|
||||
pvd->vg_on_disk.size = VG_SIZE;
|
||||
|
||||
pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk);
|
||||
pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN;
|
||||
|
||||
pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk);
|
||||
pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk);
|
||||
|
||||
pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk);
|
||||
pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk);
|
||||
}
|
||||
|
||||
int _check_vg_limits(struct disk_list *dl)
|
||||
{
|
||||
if (dl->vg.lv_max >= MAX_LV) {
|
||||
log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
|
||||
"for VG '%s'", dl->vg.lv_max, MAX_LV - 1,
|
||||
dl->pv.vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dl->vg.pv_max >= MAX_PV) {
|
||||
log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
|
||||
"for VG '%s'", dl->vg.pv_max, MAX_PV - 1,
|
||||
dl->pv.vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This assumes pe_count and pe_start have already
|
||||
* been calculated correctly.
|
||||
*/
|
||||
int calculate_layout(struct disk_list *dl)
|
||||
{
|
||||
struct pv_disk *pvd = &dl->pv;
|
||||
|
||||
_calc_simple_layout(pvd);
|
||||
if (!_adjust_pe_on_disk(pvd)) {
|
||||
log_error("Insufficient space for metadata and PE's.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_check_vg_limits(dl))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct pv_disk *pvd = dbg_malloc(sizeof(*pvd));
|
||||
uint32_t end;
|
||||
|
||||
if (!pvd) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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",
|
||||
pv->dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
pvd->pe_total--;
|
||||
_calc_simple_layout(pvd);
|
||||
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);
|
||||
|
||||
pv->pe_count = pvd->pe_total;
|
||||
pv->pe_start = pvd->pe_start;
|
||||
dbg_free(pvd);
|
||||
return 1;
|
||||
}
|
||||
59
lib/format1/vg_number.c
Normal file
59
lib/format1/vg_number.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "disk-rep.h"
|
||||
|
||||
/*
|
||||
* 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 seperate file so it wouldn't contaminate
|
||||
* other code.
|
||||
*/
|
||||
int get_free_vg_number(struct dev_filter *filter, const char *candidate_vg,
|
||||
int *result)
|
||||
{
|
||||
struct list_head all_pvs, *tmp;
|
||||
struct disk_list *dl;
|
||||
struct pool *mem = pool_create(10 * 1024);
|
||||
int numbers[MAX_VG], i, r = 0;
|
||||
|
||||
INIT_LIST_HEAD(&all_pvs);
|
||||
|
||||
if (!mem) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!read_pvs_in_vg(NULL, filter, mem, &all_pvs)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(numbers, 0, sizeof(numbers));
|
||||
|
||||
list_for_each (tmp, &all_pvs) {
|
||||
dl = list_entry(tmp, struct disk_list, list);
|
||||
if (!strcmp(dl->pv.vg_name, candidate_vg))
|
||||
continue;
|
||||
|
||||
numbers[dl->vg.vg_number] = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_VG; i++) {
|
||||
if (!numbers[i]) {
|
||||
r = 1;
|
||||
*result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
pool_destroy(mem);
|
||||
return r;
|
||||
}
|
||||
128
lib/log/log.c
Normal file
128
lib/log/log.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
|
||||
static FILE *_log = 0;
|
||||
|
||||
static int _verbose_level = 0;
|
||||
static int _test = 0;
|
||||
static int _debug_level = 0;
|
||||
static int _syslog = 0;
|
||||
|
||||
void init_log(FILE *fp) {
|
||||
_log = fp;
|
||||
}
|
||||
|
||||
void init_syslog(int facility) {
|
||||
openlog("lvm", LOG_PID, facility);
|
||||
_syslog = 1;
|
||||
}
|
||||
|
||||
void fin_log() {
|
||||
_log = 0;
|
||||
}
|
||||
|
||||
void fin_syslog() {
|
||||
closelog();
|
||||
_syslog = 0;
|
||||
}
|
||||
|
||||
void init_verbose(int level) {
|
||||
_verbose_level = level;
|
||||
}
|
||||
|
||||
void init_test(int level) {
|
||||
_test = level;
|
||||
}
|
||||
|
||||
int test_mode() {
|
||||
return _test;
|
||||
}
|
||||
|
||||
void init_debug(int level) {
|
||||
_debug_level = level;
|
||||
}
|
||||
|
||||
int debug_level() {
|
||||
return _debug_level;
|
||||
}
|
||||
|
||||
void print_log(int level, const char *file, int line, const char *format, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
switch(level) {
|
||||
case _LOG_DEBUG:
|
||||
if (_verbose_level > 2 && format[1]) {
|
||||
printf(" ");
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
}
|
||||
break;
|
||||
|
||||
case _LOG_INFO:
|
||||
if (_verbose_level > 1) {
|
||||
printf(" ");
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
}
|
||||
break;
|
||||
case _LOG_NOTICE:
|
||||
if (_verbose_level) {
|
||||
printf(" ");
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
}
|
||||
break;
|
||||
case _LOG_WARN:
|
||||
printf(" ");
|
||||
vprintf(format, ap);
|
||||
putchar('\n');
|
||||
break;
|
||||
case _LOG_ERR:
|
||||
fprintf(stderr, " ");
|
||||
vfprintf(stderr, format, ap);
|
||||
fputc('\n',stderr);
|
||||
break;
|
||||
case _LOG_FATAL:
|
||||
vfprintf(stderr, format, ap);
|
||||
fputc('\n',stderr);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (level > _debug_level)
|
||||
return;
|
||||
|
||||
if (_log) {
|
||||
fprintf(_log, "%s:%d ", file, line);
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(_log, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(_log, "\n");
|
||||
fflush(_log);
|
||||
}
|
||||
|
||||
if (_syslog) {
|
||||
va_start(ap, format);
|
||||
vsyslog(level, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
|
||||
79
lib/log/log.h
Normal file
79
lib/log/log.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LOG_H
|
||||
#define _LVM_LOG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define _LOG_DEBUG 7
|
||||
#define _LOG_INFO 6
|
||||
#define _LOG_NOTICE 5
|
||||
#define _LOG_WARN 4
|
||||
#define _LOG_ERR 3
|
||||
#define _LOG_FATAL 2
|
||||
|
||||
void init_log(FILE *fp);
|
||||
void fin_log(void);
|
||||
|
||||
void init_syslog(int facility);
|
||||
void fin_syslog(void);
|
||||
|
||||
void init_verbose(int level);
|
||||
void init_test(int level);
|
||||
void init_debug(int level);
|
||||
|
||||
int test_mode(void);
|
||||
int debug_level(void);
|
||||
|
||||
void print_log(int level, const char *file, int line, const char *format, ...)
|
||||
__attribute__ (( format (printf, 4, 5) ));
|
||||
|
||||
#define plog(l, x...) print_log(l, __FILE__, __LINE__ , ## x)
|
||||
|
||||
#define log_debug(x...) plog(_LOG_DEBUG, x)
|
||||
#define log_info(x...) plog(_LOG_INFO, x)
|
||||
#define log_notice(x...) plog(_LOG_NOTICE, x)
|
||||
#define log_warn(x...) plog(_LOG_WARN, x)
|
||||
#define log_err(x...) plog(_LOG_ERR, x)
|
||||
#define log_fatal(x...) plog(_LOG_FATAL, x)
|
||||
|
||||
#define stack log_debug( "s" )
|
||||
|
||||
/*
|
||||
* Macros to use for messages:
|
||||
*
|
||||
* log_error - always print to stderr
|
||||
* log_print - always print to stdout
|
||||
* log_verbose - print to stdout if verbose is set (-v)
|
||||
* log_very_verbose - print to stdout if verbose is set twice (-vv)
|
||||
* log_debug - print to stdout if verbose is set three times (-vvv)
|
||||
* (suppressed if single-character string such as with 'stack')
|
||||
*
|
||||
* In addition, messages will be logged to file or syslog if they
|
||||
* are more serious than the log level specified with -d.
|
||||
*/
|
||||
|
||||
#define log_error(fmt, args...) log_err(fmt , ## args)
|
||||
#define log_print(fmt, args...) log_warn(fmt , ## args)
|
||||
#define log_verbose(fmt, args...) log_notice(fmt , ## args)
|
||||
#define log_very_verbose(fmt, args...) log_info(fmt , ## args)
|
||||
|
||||
/* System call equivalents */
|
||||
#define log_sys_error(x, y) \
|
||||
log_err("%s: %s failed: %s", y, x, strerror(errno))
|
||||
#define log_sys_very_verbose(x, y) \
|
||||
log_info("%s: %s failed: %s", y, x, strerror(errno))
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
501
lib/metadata/lvm_v1.c
Normal file
501
lib/metadata/lvm_v1.c
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "dev-manager.h"
|
||||
#include "log.h"
|
||||
#include "metadata.h"
|
||||
|
||||
/*
|
||||
* FIXME: these should not allocate memory
|
||||
*/
|
||||
|
||||
pv_t *pv_copy_from_disk(pv_disk_t * pv_disk)
|
||||
{
|
||||
pv_t *pv;
|
||||
|
||||
if (!pv_disk || !(pv = (pv_t *) dbg_malloc(sizeof (*pv))))
|
||||
return 0;
|
||||
|
||||
#define xx16(v) pv->v = LVM_TO_CORE16(pv_disk->v)
|
||||
#define xx32(v) pv->v = LVM_TO_CORE32(pv_disk->v)
|
||||
|
||||
memset(pv, 0, sizeof (*pv));
|
||||
strncpy(pv->id, pv_disk->id, sizeof (pv->id));
|
||||
|
||||
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);
|
||||
|
||||
memset(pv->pv_name, 0, sizeof (pv->pv_name));
|
||||
memset(pv->pv_uuid, 0, sizeof (pv->pv_uuid));
|
||||
memcpy(pv->pv_uuid, pv_disk->pv_uuid, UUID_LEN);
|
||||
strncpy(pv->vg_name, pv_disk->vg_name, sizeof (pv->vg_name));
|
||||
strncpy(pv->system_id, pv_disk->system_id, sizeof (pv->system_id));
|
||||
|
||||
pv->pv_dev = LVM_TO_CORE32(pv_disk->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);
|
||||
pv->pe_stale = 0;
|
||||
xx32(pe_start);
|
||||
|
||||
#undef xx16
|
||||
#undef xx32
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
pv_disk_t *pv_copy_to_disk(pv_t * pv_core)
|
||||
{
|
||||
pv_disk_t *pv;
|
||||
|
||||
if (!pv_core || !(pv = dbg_malloc(sizeof (*pv))))
|
||||
return 0;
|
||||
|
||||
#define xx16(v) pv->v = LVM_TO_DISK16(pv_core->v)
|
||||
#define xx32(v) pv->v = LVM_TO_DISK32(pv_core->v)
|
||||
|
||||
memset(pv, 0, sizeof (*pv));
|
||||
strncpy(pv->id, pv_core->id, sizeof (pv->id));
|
||||
|
||||
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);
|
||||
|
||||
memcpy(pv->pv_uuid, pv_core->pv_uuid, UUID_LEN);
|
||||
strncpy(pv->vg_name, pv_core->vg_name, sizeof (pv->vg_name));
|
||||
strncpy(pv->system_id, pv_core->system_id, sizeof (pv->system_id));
|
||||
|
||||
/* core type is kdev_t; but no matter what it is,
|
||||
only store major for check in pv_read() */
|
||||
pv->pv_major = LVM_TO_DISK32(MAJOR(pv_core->pv_dev));
|
||||
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);
|
||||
|
||||
#undef xx16
|
||||
#undef xx32
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
lv_t *lv_copy_from_disk(lv_disk_t * lv_disk)
|
||||
{
|
||||
lv_t *lv;
|
||||
|
||||
if (!lv_disk || !(lv = dbg_malloc(sizeof (*lv))))
|
||||
return 0;
|
||||
|
||||
memset(lv, 0, sizeof (*lv));
|
||||
|
||||
#define xx16(v) lv->v = LVM_TO_CORE16(lv_disk->v)
|
||||
#define xx32(v) lv->v = LVM_TO_CORE32(lv_disk->v)
|
||||
|
||||
strncpy(lv->lv_name, lv_disk->lv_name, sizeof (lv->lv_name));
|
||||
strncpy(lv->vg_name, lv_disk->vg_name, sizeof (lv->vg_name));
|
||||
|
||||
xx32(lv_access);
|
||||
xx32(lv_status);
|
||||
|
||||
lv->lv_open = 0;
|
||||
|
||||
xx32(lv_dev);
|
||||
xx32(lv_number);
|
||||
xx32(lv_mirror_copies);
|
||||
xx32(lv_recovery);
|
||||
xx32(lv_schedule);
|
||||
xx32(lv_size);
|
||||
|
||||
lv->lv_current_pe = NULL;
|
||||
|
||||
xx32(lv_allocated_le);
|
||||
|
||||
lv->lv_current_le = lv->lv_allocated_le;
|
||||
|
||||
xx32(lv_stripes);
|
||||
xx32(lv_stripesize);
|
||||
xx32(lv_badblock);
|
||||
xx32(lv_allocation);
|
||||
xx32(lv_io_timeout);
|
||||
xx32(lv_read_ahead);
|
||||
|
||||
lv->lv_snapshot_org = NULL;
|
||||
lv->lv_snapshot_prev = NULL;
|
||||
lv->lv_snapshot_next = NULL;
|
||||
lv->lv_block_exception = NULL;
|
||||
lv->lv_remap_ptr = 0;
|
||||
lv->lv_remap_end = 0;
|
||||
|
||||
xx32(lv_snapshot_minor);
|
||||
xx16(lv_chunk_size);
|
||||
|
||||
#undef xx32
|
||||
#undef xx16
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
lv_disk_t *lv_copy_to_disk(lv_t * lv_core)
|
||||
{
|
||||
lv_disk_t *lv;
|
||||
|
||||
if (!lv_core || !(lv = dbg_malloc(sizeof (*lv))))
|
||||
return 0;
|
||||
|
||||
memset(lv, 0, sizeof (*lv));
|
||||
|
||||
#define xx16(v) lv->v = LVM_TO_DISK16(lv_core->v)
|
||||
#define xx32(v) lv->v = LVM_TO_DISK32(lv_core->v)
|
||||
|
||||
strncpy(lv->lv_name, lv_core->lv_name, sizeof (lv->lv_name));
|
||||
strncpy(lv->vg_name, lv_core->vg_name, sizeof (lv->vg_name));
|
||||
|
||||
xx32(lv_access);
|
||||
xx32(lv_status);
|
||||
|
||||
lv->lv_open = 0;
|
||||
|
||||
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);
|
||||
|
||||
lv->dummy = 0;
|
||||
|
||||
xx32(lv_allocated_le);
|
||||
xx32(lv_stripes);
|
||||
xx32(lv_stripesize);
|
||||
xx32(lv_badblock);
|
||||
xx32(lv_allocation);
|
||||
xx32(lv_io_timeout);
|
||||
xx32(lv_read_ahead);
|
||||
|
||||
#undef xx32
|
||||
#undef xx16
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
vg_t *vg_copy_from_disk(vg_disk_t * vg_disk)
|
||||
{
|
||||
vg_t *vg;
|
||||
|
||||
if (!vg_disk || !(vg = dbg_malloc(sizeof (*vg))))
|
||||
return 0;
|
||||
|
||||
#define xx32(v) vg->v = LVM_TO_CORE32(vg_disk->v)
|
||||
|
||||
memset(vg, 0, sizeof (vg_t));
|
||||
|
||||
xx32(vg_number);
|
||||
xx32(vg_access);
|
||||
xx32(vg_status);
|
||||
xx32(lv_max);
|
||||
xx32(lv_cur);
|
||||
|
||||
vg->lv_open = 0;
|
||||
|
||||
xx32(pv_max);
|
||||
xx32(pv_cur);
|
||||
xx32(pv_act);
|
||||
|
||||
vg->dummy = 0;
|
||||
|
||||
xx32(vgda);
|
||||
xx32(pe_size);
|
||||
xx32(pe_total);
|
||||
xx32(pe_allocated);
|
||||
xx32(pvg_total);
|
||||
|
||||
#undef xx32
|
||||
|
||||
memset(&vg->pv, 0, sizeof (vg->pv));
|
||||
memset(&vg->lv, 0, sizeof (vg->lv));
|
||||
|
||||
memset(vg->vg_uuid, 0, sizeof (vg->vg_uuid));
|
||||
memcpy(vg->vg_uuid, vg_disk->vg_uuid, UUID_LEN);
|
||||
|
||||
return vg;
|
||||
}
|
||||
|
||||
vg_disk_t *vg_copy_to_disk(vg_t * vg_core)
|
||||
{
|
||||
vg_disk_t *vg;
|
||||
|
||||
if (!vg_core ||
|
||||
/* FIXME: vg_check_consistency(vg_core) || */
|
||||
!(vg = dbg_malloc(sizeof (*vg))))
|
||||
return 0;
|
||||
|
||||
memset(vg, 0, sizeof (*vg));
|
||||
|
||||
#define xx32(v) vg->v = LVM_TO_DISK32(vg_core->v)
|
||||
|
||||
xx32(vg_number);
|
||||
xx32(vg_access);
|
||||
xx32(vg_status);
|
||||
xx32(lv_max);
|
||||
xx32(lv_cur);
|
||||
|
||||
vg->lv_open = 0;
|
||||
|
||||
xx32(pv_max);
|
||||
xx32(pv_cur);
|
||||
xx32(pv_act);
|
||||
|
||||
vg->dummy = 0;
|
||||
|
||||
xx32(vgda);
|
||||
xx32(pe_size);
|
||||
xx32(pe_total);
|
||||
xx32(pe_allocated);
|
||||
xx32(pvg_total);
|
||||
|
||||
#undef xx32
|
||||
|
||||
memcpy(vg->vg_uuid, vg_core->vg_uuid, UUID_LEN);
|
||||
|
||||
return vg;
|
||||
}
|
||||
|
||||
pe_disk_t *pe_copy_from_disk(pe_disk_t * pe_file, int count)
|
||||
{
|
||||
int i;
|
||||
pe_disk_t *pe;
|
||||
size_t s = sizeof (*pe) * count;
|
||||
|
||||
if (!pe_file || count <= 0 || !(pe = dbg_malloc(s)))
|
||||
return 0;
|
||||
|
||||
memset(pe, 0, s);
|
||||
for (i = 0; i < count; i++) {
|
||||
pe[i].lv_num = LVM_TO_CORE16(pe_file[i].lv_num);
|
||||
pe[i].le_num = LVM_TO_CORE16(pe_file[i].le_num);
|
||||
}
|
||||
|
||||
return pe;
|
||||
}
|
||||
|
||||
pe_disk_t *pe_copy_to_disk(pe_disk_t * pe_core, int count)
|
||||
{
|
||||
int i;
|
||||
pe_disk_t *pe;
|
||||
size_t s = sizeof (*pe) * count;
|
||||
|
||||
if (!pe_core || count <= 0 || !(pe = dbg_malloc(s)))
|
||||
return 0;
|
||||
|
||||
memset(pe, 0, s);
|
||||
for (i = 0; i < count; i++) {
|
||||
pe[i].lv_num = LVM_TO_DISK16(pe_core[i].lv_num);
|
||||
pe[i].le_num = LVM_TO_DISK16(pe_core[i].le_num);
|
||||
}
|
||||
|
||||
return pe;
|
||||
}
|
||||
|
||||
pv_t *pv_read_lvm_v1(struct dev_mgr * dm, const char *pv_name)
|
||||
{
|
||||
int pv_handle = -1;
|
||||
ssize_t read_ret;
|
||||
ssize_t bytes_read = 0;
|
||||
static pv_disk_t pv_this;
|
||||
struct stat stat_b;
|
||||
pv_t *pv = NULL;
|
||||
struct device *pv_dev;
|
||||
|
||||
if ((pv_handle = open(pv_name, O_RDONLY)) == -1) {
|
||||
log_error("%s: open failed: %s", pv_name, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((fstat(pv_handle, &stat_b))) {
|
||||
log_error("%s: fstat failed: %s", pv_name, strerror(errno));
|
||||
goto pv_read_lvm_v1_out;
|
||||
}
|
||||
|
||||
while ((bytes_read < sizeof (pv_this) &&
|
||||
(read_ret = read(pv_handle, &pv_this + bytes_read,
|
||||
sizeof (pv_this) - bytes_read)) != -1))
|
||||
bytes_read += read_ret;
|
||||
|
||||
if (read_ret == -1) {
|
||||
log_error("%s: read failed: %s", pv_name, strerror(errno));
|
||||
goto pv_read_lvm_v1_out;
|
||||
}
|
||||
|
||||
pv = pv_copy_from_disk(&pv_this);
|
||||
|
||||
/* correct for imported/moved volumes */
|
||||
if (!(pv_dev = dev_by_dev(dm, stat_b.st_rdev))) {
|
||||
log_error("Device missing from cache");
|
||||
goto pv_read_lvm_v1_out;
|
||||
}
|
||||
|
||||
memset(pv->pv_name, 0, sizeof (pv->pv_name));
|
||||
strncpy(pv->pv_name, pv_dev->name, sizeof (pv->pv_name) - 1);
|
||||
|
||||
/* FIXME: Deleted version / consistency / export checks! */
|
||||
|
||||
pv->pv_dev = stat_b.st_rdev;
|
||||
|
||||
pv_read_lvm_v1_out:
|
||||
if (pv_handle != -1)
|
||||
close(pv_handle);
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
pe_disk_t *pv_read_pe_lvm_v1(const char *pv_name, const pv_t * pv)
|
||||
{
|
||||
int pv_handle = -1;
|
||||
uint size = 0;
|
||||
ssize_t read_ret;
|
||||
ssize_t bytes_read = 0;
|
||||
pe_disk_t *pe = NULL;
|
||||
pe_disk_t *pe_this;
|
||||
|
||||
size = pv->pe_total * sizeof (pe_disk_t);
|
||||
if (size > pv->pe_on_disk.size) {
|
||||
log_error("PEs extend beyond end of volume group!");
|
||||
return pe; /*NULL*/
|
||||
}
|
||||
|
||||
if ((pv_handle = open(pv_name, O_RDONLY)) == -1) {
|
||||
log_error("%s: open failed: %s", pv_name, strerror(errno));
|
||||
goto pv_read_pe_out;
|
||||
}
|
||||
|
||||
if (lseek(pv_handle, pv->pe_on_disk.base, SEEK_SET) !=
|
||||
pv->pe_on_disk.base) {
|
||||
log_error("%s: lseek to PE failed: %s", pv_name, strerror(errno));
|
||||
goto pv_read_pe_out;
|
||||
}
|
||||
|
||||
if (!(pe_this = dbg_malloc(size))) {
|
||||
log_error("PE malloc failed");
|
||||
goto pv_read_pe_out;
|
||||
}
|
||||
|
||||
while ((bytes_read < size &&
|
||||
(read_ret = read(pv_handle, (void *)pe_this + bytes_read,
|
||||
size - bytes_read)) != -1))
|
||||
bytes_read += read_ret;
|
||||
|
||||
if (read_ret == -1) {
|
||||
log_error("%s: read failed: %s", pv_name, strerror(errno));
|
||||
goto pv_read_pe_out;
|
||||
}
|
||||
|
||||
pe = pe_copy_from_disk(pe_this, pv->pe_total);
|
||||
|
||||
pv_read_pe_out:
|
||||
if (pv_handle != -1)
|
||||
close(pv_handle);
|
||||
|
||||
dbg_free(pe_this);
|
||||
|
||||
return pe;
|
||||
}
|
||||
|
||||
lv_disk_t *pv_read_lvs_lvm_v1(const pv_t *pv)
|
||||
{
|
||||
int pv_handle = -1;
|
||||
uint size = 0;
|
||||
ssize_t read_ret;
|
||||
ssize_t bytes_read = 0;
|
||||
lv_disk_t *lvs;
|
||||
|
||||
/* FIXME: replace lv_cur? */
|
||||
size = pv->lv_cur * sizeof (lv_disk_t);
|
||||
|
||||
if ((pv_handle = open(pv->pv_name, O_RDONLY)) == -1) {
|
||||
log_error("%s: open failed: %s", pv->pv_name, strerror(errno));
|
||||
goto pv_read_lvs_out;
|
||||
}
|
||||
|
||||
if (lseek(pv_handle, pv->lv_on_disk.base, SEEK_SET) !=
|
||||
pv->lv_on_disk.base) {
|
||||
log_error("%s: lseek to LV failed: %s", pv->pv_name, strerror(errno));
|
||||
goto pv_read_lvs_out;
|
||||
}
|
||||
|
||||
if (!(lvs = dbg_malloc(size))) {
|
||||
log_error("PE malloc failed");
|
||||
goto pv_read_lvs_out;
|
||||
}
|
||||
|
||||
while ((bytes_read < size &&
|
||||
(read_ret = read(pv_handle, (void *) lvs + bytes_read,
|
||||
size - bytes_read)) != -1))
|
||||
bytes_read += read_ret;
|
||||
|
||||
if (read_ret == -1) {
|
||||
log_error("%s: read failed: %s", pv->pv_name, strerror(errno));
|
||||
goto pv_read_lvs_out;
|
||||
}
|
||||
|
||||
pv_read_lvs_out:
|
||||
if (pv_handle != -1)
|
||||
close(pv_handle);
|
||||
|
||||
/* Caller frees */
|
||||
return lvs;
|
||||
}
|
||||
|
||||
205
lib/metadata/lvm_v1.h
Normal file
205
lib/metadata/lvm_v1.h
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* lvm 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.
|
||||
*
|
||||
* lvm 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_V1_H_INCLUDE
|
||||
#define _LVM_V1_H_INCLUDE
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/list.h>
|
||||
#else
|
||||
#define __KERNEL__
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/list.h>
|
||||
#undef __KERNEL__
|
||||
#endif /* #ifndef __KERNEL__ */
|
||||
|
||||
#include <linux/major.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
typedef unsigned long blkoff_t;
|
||||
|
||||
#ifndef uint8_t
|
||||
# define uint8_t unsigned char
|
||||
#endif
|
||||
#ifndef uint16_t
|
||||
# define uint16_t unsigned short int
|
||||
#endif
|
||||
#ifndef uint32_t
|
||||
# define uint32_t unsigned int
|
||||
#endif
|
||||
#ifndef uint64_t
|
||||
# define uint64_t unsigned long long int
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is basically the on disk metadata layout version.
|
||||
*/
|
||||
#define LVM_STRUCT_VERSION 2
|
||||
|
||||
/*
|
||||
* Limits for VGs, PVs per VG and LVs per VG. The ABS_* limit's are
|
||||
* used in defining the data structures, MAX_* are the real limits
|
||||
* used.
|
||||
*/
|
||||
#define MAX_VG 99
|
||||
#define MAX_PV 256
|
||||
#define MAX_LV 256 /* caused by 8 bit minor */
|
||||
|
||||
#define NAME_LEN 128 /* the maximum length of various names */
|
||||
#define UUID_LEN 32 /* some components have unique identifiers */
|
||||
|
||||
|
||||
/*
|
||||
* A little struct to hold a sector range on a block device. Used to
|
||||
* hold the location of on disk metadata
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
} lvm_disk_data_t;
|
||||
|
||||
|
||||
/***********************************************************
|
||||
* On disk representation:
|
||||
*
|
||||
* The beginning of each PV contains metadata, known int the Volume
|
||||
* Group Data Area (VGDA). This metadata has the following structure:
|
||||
*
|
||||
* offset description size
|
||||
* --------------- ---------------------------------- ------------
|
||||
* 0 physical volume structure ~500 byte
|
||||
* 1K volume group structure ~200 byte
|
||||
* 6K namelist of physical volumes 128 byte each
|
||||
* + n * ~300byte n logical volume structures ~300 byte each
|
||||
* + m * 4byte m physical extent alloc. structs 4 byte each
|
||||
* + ~ 1 PE size physical extents total * size
|
||||
*
|
||||
* Spaces are left between the structures for later extensions.
|
||||
***********************************************************/
|
||||
|
||||
/*
|
||||
* physical volume - disk
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t id[2]; /* identifier */
|
||||
uint16_t version; /* lvm struct version */
|
||||
|
||||
/* these define the locations of various bits of on disk metadata */
|
||||
lvm_disk_data_t pv_on_disk; /* pv_disk_t location */
|
||||
lvm_disk_data_t vg_on_disk; /* vg_disk_t location */
|
||||
lvm_disk_data_t pv_uuidlist_on_disk; /* uuid list location */
|
||||
lvm_disk_data_t lv_on_disk; /* lv_disk_t locations */
|
||||
lvm_disk_data_t pe_on_disk; /* pe mapping table location */
|
||||
|
||||
uint8_t pv_uuid[NAME_LEN]; /* uuid for this PV */
|
||||
uint8_t vg_name[NAME_LEN]; /* which vg it belongs to */
|
||||
uint8_t system_id[NAME_LEN]; /* for vgexport/vgimport */
|
||||
uint32_t pv_major;
|
||||
uint32_t pv_number;
|
||||
uint32_t pv_status;
|
||||
uint32_t pv_allocatable;
|
||||
uint32_t pv_size;
|
||||
uint32_t lv_cur;
|
||||
uint32_t pe_size;
|
||||
uint32_t pe_total;
|
||||
uint32_t pe_allocated;
|
||||
|
||||
/* data_location.base holds the start of the pe's in sectors */
|
||||
uint32_t pe_start;
|
||||
} pv_disk_t;
|
||||
|
||||
/*
|
||||
* logical volume - disk
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t lv_name[NAME_LEN];
|
||||
uint8_t vg_name[NAME_LEN];
|
||||
uint32_t lv_access;
|
||||
uint32_t lv_status;
|
||||
uint32_t lv_open;
|
||||
uint32_t lv_dev;
|
||||
uint32_t lv_number;
|
||||
uint32_t lv_mirror_copies; /* for future use */
|
||||
uint32_t lv_recovery; /* " */
|
||||
uint32_t lv_schedule; /* " */
|
||||
uint32_t lv_size;
|
||||
uint32_t lv_snapshot_minor; /* minor number of origin */
|
||||
uint16_t lv_chunk_size; /* chunk size of snapshot */
|
||||
uint16_t dummy;
|
||||
uint32_t lv_allocated_le;
|
||||
uint32_t lv_stripes;
|
||||
uint32_t lv_stripesize;
|
||||
uint32_t lv_badblock; /* for future use */
|
||||
uint32_t lv_allocation;
|
||||
uint32_t lv_io_timeout; /* for future use */
|
||||
uint32_t lv_read_ahead;
|
||||
} lv_disk_t;
|
||||
|
||||
/*
|
||||
* volume group - disk
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t vg_uuid[UUID_LEN]; /* volume group UUID */
|
||||
|
||||
/* rest of v1 VG name */
|
||||
uint8_t vg_name_dummy[NAME_LEN-UUID_LEN];
|
||||
uint32_t vg_number; /* volume group number */
|
||||
uint32_t vg_access; /* read/write */
|
||||
uint32_t vg_status; /* active or not */
|
||||
uint32_t lv_max; /* maximum logical volumes */
|
||||
uint32_t lv_cur; /* current logical volumes */
|
||||
uint32_t lv_open; /* open logical volumes */
|
||||
uint32_t pv_max; /* maximum physical volumes */
|
||||
uint32_t pv_cur; /* current physical volumes FU */
|
||||
uint32_t pv_act; /* active physical volumes */
|
||||
uint32_t dummy;
|
||||
uint32_t vgda; /* volume group descriptor arrays FU */
|
||||
uint32_t pe_size; /* physical extent size in sectors */
|
||||
uint32_t pe_total; /* total of physical extents */
|
||||
uint32_t pe_allocated; /* allocated physical extents */
|
||||
uint32_t pvg_total; /* physical volume groups FU */
|
||||
} vg_disk_t;
|
||||
|
||||
/*
|
||||
* pe mapping - disk
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t lv_num;
|
||||
uint16_t le_num;
|
||||
} pe_disk_t;
|
||||
|
||||
/*
|
||||
* copy on write tables - disk
|
||||
*/
|
||||
typedef struct lv_COW_table_disk_v1 {
|
||||
uint64_t pv_org_number;
|
||||
uint64_t pv_org_rsector;
|
||||
uint64_t pv_snap_number;
|
||||
uint64_t pv_snap_rsector;
|
||||
} lv_COW_table_disk_t;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
229
lib/metadata/metadata.c
Normal file
229
lib/metadata/metadata.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "pool.h"
|
||||
#include "device.h"
|
||||
#include "dev-cache.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int _add_pv_to_vg(struct io_space *ios, struct volume_group *vg,
|
||||
const char *pv_name)
|
||||
{
|
||||
struct pv_list *pvl;
|
||||
struct physical_volume *pv;
|
||||
|
||||
log_verbose("Adding physical volume '%s' to volume group '%s'",
|
||||
pv_name, vg->name);
|
||||
|
||||
if (!(pvl = pool_alloc(ios->mem, sizeof (*pvl)))) {
|
||||
log_error("pv_list allocation for '%s' failed", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(pv = ios->pv_read(ios, pv_name))) {
|
||||
log_error("Failed to read existing physical volume '%s'",
|
||||
pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*pv->vg_name) {
|
||||
log_error("Physical volume '%s' is already in volume group "
|
||||
"'%s'", pv_name, pv->vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME For LVM2, set on PV creation instead of here? */
|
||||
pv->status |= ALLOCATED_PV;
|
||||
|
||||
/* FIXME check this */
|
||||
pv->exported = NULL;
|
||||
|
||||
if (!(pv->vg_name = pool_strdup(ios->mem, vg->name))) {
|
||||
log_error("vg->name allocation failed for '%s'", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME Tie this to activation or not? */
|
||||
pv->status |= ACTIVE;
|
||||
|
||||
/* Units of 512-byte sectors */
|
||||
if (!dev_get_size(pv->dev, &pv->size)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Units of 512-byte sectors */
|
||||
pv->pe_size = vg->extent_size;
|
||||
|
||||
/*
|
||||
* The next two fields should be corrected
|
||||
* by ios->pv_setup.
|
||||
*/
|
||||
pv->pe_start = 0;
|
||||
pv->pe_count = pv->size / pv->pe_size;
|
||||
|
||||
pv->pe_allocated = 0;
|
||||
|
||||
if (!ios->pv_setup(ios, pv, vg)) {
|
||||
log_debug("Format-specific setup of physical volume '%s' "
|
||||
"failed.", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (find_pv_in_vg(vg, pv_name)) {
|
||||
log_error("Physical volume '%s' listed more than once.",
|
||||
pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vg->pv_count == vg->max_pv) {
|
||||
log_error("No space for '%s' - volume group '%s' "
|
||||
"holds max %d physical volume(s).", pv_name,
|
||||
vg->name, vg->max_pv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&pvl->pv, pv, sizeof (struct physical_volume));
|
||||
|
||||
list_add(&pvl->list, &vg->pvs);
|
||||
vg->pv_count++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vg_extend(struct io_space *ios, struct volume_group *vg, int pv_count,
|
||||
char **pv_names)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* attach each pv */
|
||||
for (i = 0; i < pv_count; i++)
|
||||
if (!_add_pv_to_vg(ios, vg, pv_names[i])) {
|
||||
log_error("Unable to add physical volume '%s' to "
|
||||
"volume group '%s'.", pv_names[i], vg->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct volume_group *vg_create(struct io_space *ios, const char *vg_name,
|
||||
uint64_t extent_size, int max_pv, int max_lv,
|
||||
int pv_count, char **pv_names)
|
||||
{
|
||||
struct volume_group *vg;
|
||||
|
||||
if (!(vg = pool_alloc(ios->mem, sizeof (*vg)))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* is this vg name already in use ? */
|
||||
if (ios->vg_read(ios, vg_name)) {
|
||||
log_err("A volume group called '%s' already exists.", vg_name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!id_create(&vg->id)) {
|
||||
log_err("Couldn't create uuid for volume group '%s'.", vg_name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* Strip prefix if present */
|
||||
if (!strncmp(vg_name, ios->prefix, strlen(ios->prefix)))
|
||||
vg_name += strlen(ios->prefix);
|
||||
|
||||
if (!(vg->name = pool_strdup(ios->mem, vg_name))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
vg->status = (ACTIVE | EXTENDABLE_VG | LVM_READ | LVM_WRITE);
|
||||
|
||||
vg->extent_size = extent_size;
|
||||
vg->extent_count = 0;
|
||||
vg->free_count = 0;
|
||||
|
||||
vg->max_lv = max_lv;
|
||||
vg->max_pv = max_pv;
|
||||
|
||||
vg->pv_count = 0;
|
||||
INIT_LIST_HEAD(&vg->pvs);
|
||||
|
||||
vg->lv_count = 0;
|
||||
INIT_LIST_HEAD(&vg->lvs);
|
||||
|
||||
if (!ios->vg_setup(ios, vg)) {
|
||||
log_error("Format specific setup of volume group '%s' failed.",
|
||||
vg_name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* attach the pv's */
|
||||
if (!vg_extend(ios, vg, pv_count, pv_names))
|
||||
goto bad;
|
||||
|
||||
return vg;
|
||||
|
||||
bad:
|
||||
pool_free(ios->mem, vg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct physical_volume *pv_create(struct io_space *ios, const char *name)
|
||||
{
|
||||
struct physical_volume *pv = pool_alloc(ios->mem, sizeof (*pv));
|
||||
|
||||
if (!pv) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id_create(&pv->id);
|
||||
if (!(pv->dev = dev_cache_get(name, ios->filter))) {
|
||||
log_err("Couldn't find device '%s'", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(pv->vg_name = pool_alloc(ios->mem, NAME_LEN))) {
|
||||
stack;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
*pv->vg_name = 0;
|
||||
pv->exported = NULL;
|
||||
pv->status = 0;
|
||||
|
||||
if (!dev_get_size(pv->dev, &pv->size)) {
|
||||
log_err("Couldn't get size of device '%s'", name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pv->pe_size = 0;
|
||||
pv->pe_start = 0;
|
||||
pv->pe_count = 0;
|
||||
pv->pe_allocated = 0;
|
||||
return pv;
|
||||
|
||||
bad:
|
||||
pool_free(ios->mem, pv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct list_head *find_pv_in_vg(struct volume_group *vg, const char *pv_name)
|
||||
{
|
||||
struct list_head *pvh;
|
||||
list_for_each(pvh, &vg->pvs) {
|
||||
if (!strcmp(list_entry(pvh, struct pv_list, list)->pv.dev->name,
|
||||
pv_name)) return pvh;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
247
lib/metadata/metadata.h
Normal file
247
lib/metadata/metadata.h
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*
|
||||
* This is the in core representation of a volume group and its
|
||||
* associated physical and logical volumes.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_METADATA_H
|
||||
#define _LVM_METADATA_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "dev-cache.h"
|
||||
#include "list.h"
|
||||
#include "uuid.h"
|
||||
|
||||
#define NAME_LEN 128
|
||||
|
||||
/* Various flags */
|
||||
/* Note that the bits no longer necessarily correspond to LVM1 disk format */
|
||||
|
||||
#define ACTIVE 0x00000001 /* PV VG LV */
|
||||
#define EXPORTED_VG 0x00000002 /* VG */ /* And PV too perhaps? */
|
||||
#define EXTENDABLE_VG 0x00000004 /* VG */
|
||||
#define ALLOCATED_PV 0x00000008 /* PV */
|
||||
|
||||
#define SPINDOWN_LV 0x00000010 /* LV */
|
||||
#define BADBLOCK_ON 0x00000020 /* LV */
|
||||
|
||||
#define LVM_READ 0x00000100 /* LV VG */
|
||||
#define LVM_WRITE 0x00000200 /* LV VG */
|
||||
#define CLUSTERED 0x00000400 /* VG */
|
||||
#define SHARED 0x00000800 /* VG */
|
||||
|
||||
#define ALLOC_STRICT 0x00001000 /* LV */
|
||||
#define ALLOC_CONTIGUOUS 0x00002000 /* LV */
|
||||
#define SNAPSHOT 0x00004000 /* LV */
|
||||
#define SNAPSHOT_ORG 0x00008000 /* LV */
|
||||
|
||||
|
||||
#define EXPORTED_TAG "PV_EXP" /* Identifier of exported PV */
|
||||
#define IMPORTED_TAG "PV_IMP" /* Identifier of imported PV */
|
||||
|
||||
|
||||
struct physical_volume {
|
||||
struct id id;
|
||||
struct device *dev;
|
||||
char *vg_name;
|
||||
char *exported;
|
||||
|
||||
uint32_t status;
|
||||
uint64_t size;
|
||||
|
||||
/* physical extents */
|
||||
uint64_t pe_size;
|
||||
uint64_t pe_start;
|
||||
uint32_t pe_count;
|
||||
uint32_t pe_allocated;
|
||||
};
|
||||
|
||||
struct pe_specifier {
|
||||
struct physical_volume *pv;
|
||||
uint32_t pe;
|
||||
};
|
||||
|
||||
struct logical_volume {
|
||||
/* disk */
|
||||
struct id id;
|
||||
char *name;
|
||||
|
||||
uint32_t status;
|
||||
|
||||
uint64_t size;
|
||||
uint32_t le_count;
|
||||
|
||||
/* le -> pe mapping array */
|
||||
struct pe_specifier *map;
|
||||
};
|
||||
|
||||
struct volume_group {
|
||||
struct id id;
|
||||
char *name;
|
||||
|
||||
uint32_t status;
|
||||
|
||||
uint64_t extent_size;
|
||||
uint32_t extent_count;
|
||||
uint32_t free_count;
|
||||
|
||||
uint32_t max_lv;
|
||||
uint32_t max_pv;
|
||||
|
||||
/* physical volumes */
|
||||
uint32_t pv_count;
|
||||
struct list_head pvs;
|
||||
|
||||
/* logical volumes */
|
||||
uint32_t lv_count;
|
||||
struct list_head lvs;
|
||||
};
|
||||
|
||||
struct name_list {
|
||||
struct list_head list;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct pv_list {
|
||||
struct list_head list;
|
||||
struct physical_volume pv;
|
||||
};
|
||||
|
||||
struct lv_list {
|
||||
struct list_head list;
|
||||
struct logical_volume lv;
|
||||
};
|
||||
|
||||
/*
|
||||
* Ownership of objects passes to caller.
|
||||
*/
|
||||
struct io_space {
|
||||
/*
|
||||
* Returns a name_list of vg's.
|
||||
*/
|
||||
struct list_head *(*get_vgs)(struct io_space *is);
|
||||
|
||||
/*
|
||||
* Returns pv_list of fully-populated pv structures.
|
||||
*/
|
||||
struct list_head *(*get_pvs)(struct io_space *is);
|
||||
|
||||
/*
|
||||
* Return PV with given path.
|
||||
*/
|
||||
struct physical_volume *(*pv_read)(struct io_space *is,
|
||||
const char *pv_name);
|
||||
|
||||
/*
|
||||
* Tweak an already filled out a pv ready
|
||||
* for importing into a vg. eg. pe_count
|
||||
* is format specific.
|
||||
*/
|
||||
int (*pv_setup)(struct io_space *is, struct physical_volume *pv,
|
||||
struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Write a PV structure to disk. Fails if
|
||||
* the PV is in a VG ie pv->vg_name must
|
||||
* be null.
|
||||
*/
|
||||
int (*pv_write)(struct io_space *is, struct physical_volume *pv);
|
||||
|
||||
/*
|
||||
* Tweak an already filled out vg. eg,
|
||||
* max_pv is format specific.
|
||||
*/
|
||||
int (*vg_setup)(struct io_space *is, struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* If vg_name doesn't contain any slash,
|
||||
* this function adds prefix.
|
||||
*/
|
||||
struct volume_group *(*vg_read)(struct io_space *is,
|
||||
const char *vg_name);
|
||||
|
||||
/*
|
||||
* Write out complete VG metadata. Ensure
|
||||
* *internal* consistency before writing
|
||||
* anything. eg. PEs can't refer to PVs
|
||||
* not part of the VG. Order write sequence
|
||||
* to aid recovery if process is aborted
|
||||
* (eg flush entire set of changes to each
|
||||
* disk in turn) It is the responsibility
|
||||
* of the caller to ensure external
|
||||
* consistency, eg by calling pv_write()
|
||||
* if removing PVs from a VG or calling
|
||||
* vg_write() a second time if splitting a
|
||||
* VG into two. vg_write() must not read
|
||||
* or write from any PVs not included in
|
||||
* the volume_group structure it is
|
||||
* handed.
|
||||
*/
|
||||
int (*vg_write)(struct io_space *is, struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Destructor for this object.
|
||||
*/
|
||||
void (*destroy)(struct io_space *is);
|
||||
|
||||
/*
|
||||
* Current volume group prefix.
|
||||
*/
|
||||
char *prefix;
|
||||
struct pool *mem;
|
||||
struct dev_filter *filter;
|
||||
void *private;
|
||||
};
|
||||
|
||||
/*
|
||||
* Utility functions
|
||||
*/
|
||||
struct volume_group *vg_create(struct io_space *ios, const char *name,
|
||||
uint64_t extent_size, int max_pv, int max_lv,
|
||||
int pv_count, char **pv_names);
|
||||
struct physical_volume *pv_create(struct io_space *ios, const char *name);
|
||||
|
||||
|
||||
|
||||
int vg_extend(struct io_space *ios, struct volume_group *vg, int pv_count,
|
||||
char **pv_names);
|
||||
|
||||
|
||||
|
||||
/* FIXME: Move to other files */
|
||||
struct io_space *create_text_format(struct dev_filter *filter,
|
||||
const char *text_file);
|
||||
|
||||
int id_eq(struct id *op1, struct id *op2);
|
||||
|
||||
/* Create consistent new empty structures, populated with defaults */
|
||||
struct volume_group *vg_create();
|
||||
|
||||
int vg_destroy(struct volume_group *vg);
|
||||
|
||||
/* Manipulate PV structures */
|
||||
int pv_add(struct volume_group *vg, struct physical_volume *pv);
|
||||
int pv_remove(struct volume_group *vg, struct physical_volume *pv);
|
||||
struct physical_volume *pv_find(struct volume_group *vg,
|
||||
const char *pv_name);
|
||||
|
||||
/* Add an LV to a given VG */
|
||||
int lv_add(struct volume_group *vg, struct logical_volume *lv);
|
||||
|
||||
/* Remove an LV from a given VG */
|
||||
int lv_remove(struct volume_group *vg, struct logical_volume *lv);
|
||||
|
||||
/* ? Return the VG that contains a given LV (based on path given in lv_name) */
|
||||
/* (or environment var?) */
|
||||
struct volume_group *vg_find(const char *lv_name);
|
||||
|
||||
/* Find an LV within a given VG */
|
||||
struct logical_volume *lv_find(struct volume_group *vg, const char *lv_name);
|
||||
|
||||
/* Find a PV within a given VG */
|
||||
struct list_head *find_pv_in_vg(struct volume_group *vg, const char *pv_name);
|
||||
|
||||
#endif
|
||||
192
lib/mm/dbg_malloc.c
Normal file
192
lib/mm/dbg_malloc.c
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
struct memblock {
|
||||
struct memblock *prev, *next; /* All allocated blocks are linked */
|
||||
size_t length; /* Size of the requested block */
|
||||
int id; /* Index of the block */
|
||||
const char *file; /* File that allocated */
|
||||
int line; /* Line that allocated */
|
||||
void *magic; /* Address of this block */
|
||||
};
|
||||
|
||||
static struct {
|
||||
unsigned int blocks, mblocks;
|
||||
unsigned int bytes, mbytes;
|
||||
|
||||
} _mem_stats = {0, 0, 0, 0};
|
||||
|
||||
static struct memblock *_head = 0;
|
||||
static struct memblock *_tail = 0;
|
||||
|
||||
void *malloc_aux(size_t s, const char *file, int line)
|
||||
{
|
||||
struct memblock *nb;
|
||||
size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);
|
||||
|
||||
if (!(nb = malloc(tsize))) {
|
||||
log_error("couldn't allocate any memory, size = %u", s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set up the file and line info */
|
||||
nb->file = file;
|
||||
nb->line = line;
|
||||
|
||||
#ifdef BOUNDS_CHECK
|
||||
bounds_check();
|
||||
#endif
|
||||
|
||||
/* setup fields */
|
||||
nb->magic = nb + 1;
|
||||
nb->length = s;
|
||||
nb->id = ++_mem_stats.blocks;
|
||||
nb->next = 0;
|
||||
nb->prev = _tail;
|
||||
|
||||
/* link to tail of the list */
|
||||
if (!_head)
|
||||
_head = _tail = nb;
|
||||
else {
|
||||
_tail->next = nb;
|
||||
_tail = nb;
|
||||
}
|
||||
|
||||
/* stomp a pretty pattern across the new memory
|
||||
and fill in the boundary bytes */
|
||||
{
|
||||
char *ptr = (char *) (nb + 1);
|
||||
int i;
|
||||
for (i = 0; i < s; i++)
|
||||
*ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;
|
||||
|
||||
for (i = 0; i < sizeof(unsigned long); i++)
|
||||
*ptr++ = (char) nb->id;
|
||||
}
|
||||
|
||||
if (_mem_stats.blocks > _mem_stats.mblocks)
|
||||
_mem_stats.mblocks = _mem_stats.blocks;
|
||||
|
||||
_mem_stats.bytes += s;
|
||||
if (_mem_stats.bytes > _mem_stats.mbytes)
|
||||
_mem_stats.mbytes = _mem_stats.bytes;
|
||||
|
||||
return nb + 1;
|
||||
}
|
||||
|
||||
void free_aux(void *p)
|
||||
{
|
||||
char *ptr;
|
||||
int i;
|
||||
struct memblock *mb = ((struct memblock *) p) - 1;
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
#ifdef BOUNDS_CHECK
|
||||
bounds_check();
|
||||
#endif
|
||||
|
||||
/* sanity check */
|
||||
assert(mb->magic == p);
|
||||
|
||||
/* check data at the far boundary */
|
||||
ptr = ((char *) mb) + sizeof(struct memblock) + mb->length;
|
||||
for (i = 0; i < sizeof(unsigned long); i++)
|
||||
if(*ptr++ != (char) mb->id)
|
||||
assert(!"Damage at far end of block");
|
||||
|
||||
/* have we freed this before ? */
|
||||
assert(mb->id != 0);
|
||||
mb->id = 0;
|
||||
|
||||
/* stomp a different pattern across the memory */
|
||||
ptr = ((char *) mb) + sizeof(struct memblock);
|
||||
for (i = 0; i < mb->length; i++)
|
||||
*ptr++ = i & 1 ? (char) 0xde : (char) 0xad;
|
||||
|
||||
/* unlink */
|
||||
if (mb->prev)
|
||||
mb->prev->next = mb->next;
|
||||
else
|
||||
_head = mb->next;
|
||||
|
||||
if (mb->next)
|
||||
mb->next->prev = mb->prev;
|
||||
else
|
||||
_tail = mb->prev;
|
||||
|
||||
assert(_mem_stats.blocks);
|
||||
_mem_stats.blocks--;
|
||||
_mem_stats.bytes -= mb->length;
|
||||
|
||||
/* free the memory */
|
||||
free(mb);
|
||||
}
|
||||
|
||||
void *realloc_aux(void *p, unsigned int s, const char *file, int line)
|
||||
{
|
||||
void *r;
|
||||
struct memblock *mb = ((struct memblock *) p) - 1;
|
||||
|
||||
r = malloc_aux(s, file, line);
|
||||
|
||||
if (p) {
|
||||
memcpy(r, p, mb->length);
|
||||
free_aux(p);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MEM
|
||||
int dump_memory(void)
|
||||
{
|
||||
unsigned long tot = 0;
|
||||
struct memblock *mb;
|
||||
if (_head)
|
||||
log_very_verbose("You have a memory leak:");
|
||||
|
||||
for (mb = _head; mb; mb = mb->next) {
|
||||
print_log(_LOG_INFO, mb->file, mb->line,
|
||||
"block %d at %p, size %d",
|
||||
mb->id, mb->magic, mb->length);
|
||||
tot += mb->length;
|
||||
}
|
||||
|
||||
if (_head)
|
||||
log_very_verbose("%ld bytes leaked in total", tot);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void bounds_check(void)
|
||||
{
|
||||
struct memblock *mb = _head;
|
||||
while (mb) {
|
||||
int i;
|
||||
char *ptr = ((char *) (mb + 1)) + mb->length;
|
||||
for (i = 0; i < sizeof(unsigned long); i++)
|
||||
if (*ptr++ != (char) mb->id)
|
||||
assert(!"Memory smash");
|
||||
|
||||
mb = mb->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
36
lib/mm/dbg_malloc.h
Normal file
36
lib/mm/dbg_malloc.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DBG_MALLOC_H
|
||||
#define _LVM_DBG_MALLOC_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef DEBUG_MEM
|
||||
void *malloc_aux(size_t s, const char *file, int line);
|
||||
void free_aux(void *p);
|
||||
void *realloc_aux(void *p, unsigned int s, const char *file, int line);
|
||||
int dump_memory(void);
|
||||
void bounds_check(void);
|
||||
|
||||
#define dbg_malloc(s) malloc_aux((s), __FILE__, __LINE__)
|
||||
#define dbg_free(p) free_aux(p)
|
||||
#define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__)
|
||||
#else
|
||||
#define dbg_malloc(s) malloc(s)
|
||||
#define dbg_free(p) free(p)
|
||||
#define dbg_realloc(p, s) realloc(p, s)
|
||||
#define dump_memory()
|
||||
#define bounds_check()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
222
lib/mm/pool.c
Normal file
222
lib/mm/pool.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pool.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
struct chunk {
|
||||
char *begin, *end;
|
||||
struct chunk *prev;
|
||||
};
|
||||
|
||||
struct pool {
|
||||
struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free
|
||||
list to stop 'bobbling' */
|
||||
size_t chunk_size;
|
||||
size_t object_len;
|
||||
unsigned object_alignment;
|
||||
};
|
||||
|
||||
void _align_chunk(struct chunk *c, unsigned alignment);
|
||||
struct chunk *_new_chunk(struct pool *p, size_t s);
|
||||
|
||||
/* by default things come out aligned for doubles */
|
||||
#define DEFAULT_ALIGNMENT __alignof__ (double)
|
||||
|
||||
struct pool *pool_create(size_t chunk_hint)
|
||||
{
|
||||
size_t new_size = 1024;
|
||||
struct pool *p = dbg_malloc(sizeof(*p));
|
||||
|
||||
if (!p) {
|
||||
log_err("couldn't create pool");
|
||||
return 0;
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
/* round chunk_hint up to the next power of 2 */
|
||||
p->chunk_size = chunk_hint + sizeof(struct chunk);
|
||||
while (new_size < p->chunk_size)
|
||||
new_size <<= 1;
|
||||
p->chunk_size = new_size;
|
||||
return p;
|
||||
}
|
||||
|
||||
void pool_destroy(struct pool *p)
|
||||
{
|
||||
struct chunk *c, *pr;
|
||||
dbg_free(p->spare_chunk);
|
||||
c = p->chunk;
|
||||
while (c) {
|
||||
pr = c->prev;
|
||||
dbg_free(c);
|
||||
c = pr;
|
||||
}
|
||||
|
||||
dbg_free(p);
|
||||
}
|
||||
|
||||
void *pool_alloc(struct pool *p, size_t s)
|
||||
{
|
||||
return pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
void *r;
|
||||
|
||||
/* realign begin */
|
||||
if (c)
|
||||
_align_chunk(c, alignment);
|
||||
|
||||
/* have we got room ? */
|
||||
if(!c || (c->begin > c->end) || (c->end - c->begin < s)) {
|
||||
/* allocate new chunk */
|
||||
int needed = s + alignment + sizeof(struct chunk);
|
||||
c = _new_chunk(p, (needed > p->chunk_size) ?
|
||||
needed : p->chunk_size);
|
||||
_align_chunk(c, alignment);
|
||||
}
|
||||
|
||||
r = c->begin;
|
||||
c->begin += s;
|
||||
return r;
|
||||
}
|
||||
|
||||
void pool_empty(struct pool *p)
|
||||
{
|
||||
if (p->chunk)
|
||||
pool_free(p, p->chunk->begin);
|
||||
}
|
||||
|
||||
void pool_free(struct pool *p, void *ptr)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
|
||||
while (c) {
|
||||
if (((char *) c < (char *) ptr) &&
|
||||
((char *) c->end > (char *) ptr)) {
|
||||
c->begin = ptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p->spare_chunk)
|
||||
dbg_free(p->spare_chunk);
|
||||
p->spare_chunk = c;
|
||||
c = c->prev;
|
||||
}
|
||||
|
||||
if (!c)
|
||||
log_warn("pool_free asked to free a pointer "
|
||||
"that wasn't in the pool, doing nothing");
|
||||
else
|
||||
p->chunk = c;
|
||||
}
|
||||
|
||||
void *pool_begin_object(struct pool *p, size_t hint, unsigned align)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
|
||||
p->object_len = 0;
|
||||
p->object_alignment = align;
|
||||
|
||||
_align_chunk(c, align);
|
||||
if (c->end - c->begin < hint) {
|
||||
/* allocate a new chunk */
|
||||
c = _new_chunk(p,
|
||||
hint > (p->chunk_size - sizeof(struct chunk)) ?
|
||||
hint + sizeof(struct chunk) + align :
|
||||
p->chunk_size);
|
||||
_align_chunk(c, align);
|
||||
}
|
||||
|
||||
return c->begin;
|
||||
}
|
||||
|
||||
void *pool_grow_object(struct pool *p, unsigned char *buffer, size_t n)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
|
||||
if (c->end - (c->begin + p->object_len) < n) {
|
||||
/* move into a new chunk */
|
||||
if (p->object_len + n > (p->chunk_size / 2))
|
||||
_new_chunk(p, (p->object_len + n) * 2);
|
||||
else
|
||||
_new_chunk(p, p->chunk_size);
|
||||
|
||||
_align_chunk(p->chunk, p->object_alignment);
|
||||
memcpy(p->chunk->begin, c->begin, p->object_len);
|
||||
c = p->chunk;
|
||||
}
|
||||
|
||||
memcpy(c->begin + p->object_len, buffer, n);
|
||||
p->object_len += n;
|
||||
return c->begin;
|
||||
}
|
||||
|
||||
void *pool_end_object(struct pool *p)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
void *r = c->begin;
|
||||
c->begin += p->object_len;
|
||||
p->object_len = 0u;
|
||||
p->object_alignment = DEFAULT_ALIGNMENT;
|
||||
return r;
|
||||
}
|
||||
|
||||
void pool_abandon_object(struct pool *p)
|
||||
{
|
||||
p->object_len = 0;
|
||||
p->object_alignment = DEFAULT_ALIGNMENT;
|
||||
}
|
||||
|
||||
char *pool_strdup(struct pool *p, const char *str)
|
||||
{
|
||||
char *ret = pool_alloc(p, strlen(str) + 1);
|
||||
|
||||
if (ret)
|
||||
strcpy(ret, str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _align_chunk(struct chunk *c, unsigned alignment)
|
||||
{
|
||||
c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
|
||||
}
|
||||
|
||||
struct chunk *_new_chunk(struct pool *p, size_t s)
|
||||
{
|
||||
struct chunk *c;
|
||||
|
||||
if (p->spare_chunk &&
|
||||
((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) {
|
||||
/* reuse old chunk */
|
||||
c = p->spare_chunk;
|
||||
p->spare_chunk = 0;
|
||||
} else {
|
||||
c = dbg_malloc(s);
|
||||
c->end = (char *) c + s;
|
||||
}
|
||||
|
||||
c->prev = p->chunk;
|
||||
c->begin = (char *) (c + 1);
|
||||
p->chunk = c;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
* vim:ai cin ts=8
|
||||
*/
|
||||
50
lib/mm/pool.h
Normal file
50
lib/mm/pool.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_POOL_H
|
||||
#define _LVM_POOL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct pool;
|
||||
|
||||
/* constructor and destructor */
|
||||
struct pool *pool_create(size_t chunk_hint);
|
||||
void pool_destroy(struct pool *p);
|
||||
|
||||
/* simple allocation/free routines */
|
||||
void *pool_alloc(struct pool *p, size_t s);
|
||||
void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment);
|
||||
void pool_empty(struct pool *p);
|
||||
void pool_free(struct pool *p, void *ptr);
|
||||
|
||||
/* object building routines */
|
||||
void *pool_begin_object(struct pool *p, size_t hint, unsigned align);
|
||||
void *pool_grow_object(struct pool *p, unsigned char *buffer, size_t n);
|
||||
void *pool_end_object(struct pool *p);
|
||||
void pool_abandon_object(struct pool *p);
|
||||
|
||||
/* utilities */
|
||||
char *pool_strdup(struct pool *p, const char *str);
|
||||
|
||||
static inline void *pool_zalloc(struct pool *p, size_t s) {
|
||||
void *ptr = pool_alloc(p, s);
|
||||
|
||||
if (ptr)
|
||||
memset(ptr, 0, s);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
* vim:ai cin ts=4
|
||||
*/
|
||||
|
||||
32
lib/mm/xlate.h
Normal file
32
lib/mm/xlate.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LVM_XLATE_H
|
||||
#define _LVM_XLATE_H
|
||||
|
||||
/* FIXME: finish these as inlines */
|
||||
|
||||
uint16_t shuffle16(uint16_t n);
|
||||
uint32_t shuffle32(uint32_t n);
|
||||
uint64_t shuffle64(uint64_t n);
|
||||
|
||||
/* xlate functions move data between core and disk */
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define xlate16(x) shuffle16(x)
|
||||
# define xlate32(x) shuffle32(x)
|
||||
# define xlate64(x) shuffle64(x)
|
||||
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define xlate16(x) (x)
|
||||
# define xlate32(x) (x)
|
||||
# define xlate64(x) (x)
|
||||
|
||||
#else
|
||||
# error "__BYTE_ORDER must be defined as __LITTLE_ENDIAN or __BIG_ENDIAN"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
49
lib/uuid/uuid.c
Normal file
49
lib/uuid/uuid.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "uuid.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static unsigned char _c[] =
|
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
int id_create(struct id *id)
|
||||
{
|
||||
int random, i, len = sizeof(id->uuid);
|
||||
|
||||
memset(id->uuid, 0, len);
|
||||
if ((random = open("/dev/urandom", O_RDONLY)) < 0) {
|
||||
log_sys_error("open", "id_create");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read(random, id->uuid, len) != len) {
|
||||
log_sys_error("read", "id_create");
|
||||
return 0;
|
||||
}
|
||||
close(random);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
id->uuid[i] = _c[id->uuid[i] % (sizeof(_c) - 1)];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int id_valid(struct id *id)
|
||||
{
|
||||
log_err("Joe hasn't written id_valid yet");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int id_cmp(struct id *lhs, struct id *rhs)
|
||||
{
|
||||
return memcmp(lhs->uuid, rhs->uuid, sizeof(lhs->uuid));
|
||||
}
|
||||
22
lib/uuid/uuid.h
Normal file
22
lib/uuid/uuid.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_UUID_H
|
||||
#define _LVM_UUID_H
|
||||
|
||||
#include "lvm-types.h"
|
||||
|
||||
#define ID_LEN 32
|
||||
|
||||
struct id {
|
||||
uint8_t uuid[ID_LEN];
|
||||
};
|
||||
|
||||
int id_create(struct id *id);
|
||||
int id_valid(struct id *id);
|
||||
int id_cmp(struct id *lhs, struct id *rhs);
|
||||
|
||||
#endif
|
||||
110
libdm/datastruct/list.h
Normal file
110
libdm/datastruct/list.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/* stolen from the Linux kernel. */
|
||||
|
||||
#ifndef _LVM_LIST_H
|
||||
#define _LVM_LIST_H
|
||||
|
||||
/*
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = { &name, &name }
|
||||
|
||||
#define INIT_LIST_HEAD(ptr) do { \
|
||||
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static __inline__ void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry after the specified head..
|
||||
*/
|
||||
static __inline__ void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry at the tail
|
||||
*/
|
||||
static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static __inline__ void __list_del(struct list_head * prev,
|
||||
struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static __inline__ void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
}
|
||||
|
||||
static __inline__ int list_empty(struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Splice in "list" into "head"
|
||||
*/
|
||||
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
|
||||
if (first != list) {
|
||||
struct list_head *last = list->prev;
|
||||
struct list_head *at = head->next;
|
||||
|
||||
first->prev = head;
|
||||
head->next = first;
|
||||
|
||||
last->next = at;
|
||||
at->prev = last;
|
||||
}
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
||||
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
#endif
|
||||
112
make.tmpl.in
Normal file
112
make.tmpl.in
Normal file
@@ -0,0 +1,112 @@
|
||||
# @configure_input@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software
|
||||
#
|
||||
# This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
# MA 02111-1307, USA
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
CC = @CC@
|
||||
RANLIB = @RANLIB@
|
||||
SHELL = /bin/sh
|
||||
INSTALL = @INSTALL@
|
||||
LN_S = @LN_S@
|
||||
|
||||
# Setup directory variables
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
staticlibdir = ${prefix}/lib
|
||||
libdir = @libdir@
|
||||
sbindir = @sbindir@
|
||||
infodir = @infodir@
|
||||
mandir = @mandir@
|
||||
|
||||
# setup misc variables
|
||||
# define the ownership variables for the binaries and man pages
|
||||
OWNER=@OWNER@
|
||||
GROUP=@GROUP@
|
||||
|
||||
# The number of jobs to run, if blank, defaults to the make standard
|
||||
ifndef MAKEFLAGS
|
||||
MAKEFLAGS = @JOBS@
|
||||
endif
|
||||
|
||||
SUFFIXES=
|
||||
SUFFIXES=.c .d .o
|
||||
|
||||
CFLAGS+=-g -Wall -DDEBUG_MEM -DBOUNDS_CHECK -DDEBUG
|
||||
INCLUDES+=-I. -I$(top_srcdir)/include
|
||||
INC_LNS=$(top_srcdir)/include/.symlinks_created
|
||||
|
||||
ifeq ("@READLINE@", "yes")
|
||||
CFLAGS += -DREADLINE_SUPPORT
|
||||
EXTRA_LIBS += -lreadline
|
||||
endif
|
||||
|
||||
OBJECTS=$(SOURCES:%.c=%.o)
|
||||
|
||||
SUBDIRS.install := $(SUBDIRS:=.install)
|
||||
SUBDIRS.clean := $(SUBDIRS:=.clean)
|
||||
SUBDIRS.distclean := $(SUBDIRS:=.distclean)
|
||||
|
||||
.PHONY: all install distclean clean
|
||||
.PHONY: $(SUBDIRS) $(SUBDIRS.install) $(SUBDIRS.clean) $(SUBDIRS.distclean)
|
||||
|
||||
all: $(SUBDIRS) $(TARGETS)
|
||||
|
||||
install: all $(SUBDIRS.install)
|
||||
|
||||
$(SUBDIRS):
|
||||
$(MAKE) -C $@
|
||||
|
||||
$(SUBDIRS.install):
|
||||
$(MAKE) -C $(@:.install=) install
|
||||
|
||||
$(SUBDIRS.clean):
|
||||
$(MAKE) -C $(@:.clean=) clean
|
||||
|
||||
$(SUBDIRS.distclean):
|
||||
$(MAKE) -C $(@:.distclean=) distclean
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(INCLUDES) $(CFLAGS) $< -o $@
|
||||
|
||||
%.d: %.c
|
||||
set -e; FILE=`echo $@ | sed 's/\\//\\\\\\//g;s/\\.d//g'`; \
|
||||
INC_LNS=`echo $(INC_LNS) | sed -e 's/\\//\\\\\\//g'`; \
|
||||
$(CC) -MM $(INCLUDES) $< | \
|
||||
sed "s/\(.*\)\.o[ :]*/$$FILE.o $$FILE.d : $$INC_LNS /g" > $@; \
|
||||
[ -s $@ ] || $(RM) $@
|
||||
|
||||
clean: $(SUBDIRS.clean)
|
||||
$(RM) $(OBJECTS) $(TARGETS) $(SOURCES:%.c=%.d)
|
||||
|
||||
distclean: $(SUBDIRS.distclean)
|
||||
$(RM) $(OBJECTS) $(TARGETS) $(SOURCES:%.c=%.d)
|
||||
$(RM) config.cache config.log config.status
|
||||
$(RM) Makefile make.tmpl
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),distclean)
|
||||
ifdef SOURCES
|
||||
-include $(SOURCES:.c=.d)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
35
man/Makefile.in
Normal file
35
man/Makefile.in
Normal file
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software
|
||||
#
|
||||
# This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
# MA 02111-1307, USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
MANUALS=$(wildcard *.8)
|
||||
MAN8DIR=${mandir}/man8
|
||||
|
||||
include ../make.tmpl
|
||||
|
||||
install:
|
||||
@echo "*** Installing $(ALL_MANUALS) in $(DESTDIR)$(MAN8DIR) ***"
|
||||
@for f in $(MANUALS); \
|
||||
do \
|
||||
$(RM) $(DESTDIR)$(MAN8DIR)/$$f; \
|
||||
@INSTALL@ -D -o $(OWNER) -g $(GROUP) -m 444 $$f $(DESTDIR)$(MAN8DIR)/$$f; \
|
||||
done
|
||||
|
||||
67
man/pvdisplay.8
Normal file
67
man/pvdisplay.8
Normal file
@@ -0,0 +1,67 @@
|
||||
.TH PVDISPLAY 8 "LVM TOOLS" "Heinz Mauelshagen" \" -*- nroff -*-
|
||||
.SH NAME
|
||||
pvdisplay \- display attributes of a physical volume
|
||||
.SH SYNOPSIS
|
||||
.B pvdisplay
|
||||
[\-c/\-\-colon] [\-d/\-\-debug] [\-h/\-?/\-\-help] [\-s/\-\-short]
|
||||
[\-v[v]/\-\-verbose [\-\-verbose]]
|
||||
PhysicalVolumePath [PhysicalVolumePath...]
|
||||
.SH DESCRIPTION
|
||||
pvdisplay allows you to see the attributes of one or more physical volumes
|
||||
like size, physical extent size, space used for the volume group descriptor
|
||||
area and so on.
|
||||
.SS OPTIONS
|
||||
.TP
|
||||
.I \-c, \-\-colon
|
||||
Generate colon seperated output for easier parsing in scripts or programs.
|
||||
.nf
|
||||
|
||||
The values are:
|
||||
|
||||
* physical volume device name
|
||||
* volume group name
|
||||
* physical volume size in kilobytes
|
||||
* internal physical volume number
|
||||
* physical volume status
|
||||
* physical volume (not) allocatable
|
||||
* current number of logical volumes on this physical volume
|
||||
* physical extent size in kilobytes
|
||||
* total number of physical extents
|
||||
* free number of physical extents
|
||||
* allocated number of physical extents
|
||||
|
||||
.fi
|
||||
.TP
|
||||
.I \-d, \-\-debug
|
||||
Enables additional debugging output (if compiled with DEBUG).
|
||||
.TP
|
||||
.I \-h, \-?, \-\-help
|
||||
Print a usage message on standard output and exit successfully.
|
||||
.TP
|
||||
.I \-s, \-\-short
|
||||
Only display the size of the given physical volumes.
|
||||
.TP
|
||||
.I \-v, \-\-verbose
|
||||
Display the mapping of physical extents to logical volumes and
|
||||
logical extents.
|
||||
.TP
|
||||
.I \-vv, \-\-verbose \-\-verbose
|
||||
Like \-v with verbose runtime information about pvdisplay's activities.
|
||||
.SH DIAGNOSTICS
|
||||
pvdisplay returns an code state of 0 for success and > 0 for error:
|
||||
.nf
|
||||
|
||||
1 no physical volume name on command line
|
||||
2 error checking consistency of physical volume
|
||||
3 error reading physical extent information from physical volume
|
||||
|
||||
95 driver/module not in kernel
|
||||
96 invalid I/O protocol version
|
||||
97 error locking logical volume manager
|
||||
98 invalid lvmtab (run vgscan(8))
|
||||
99 invalid command line
|
||||
.fi
|
||||
.SH See also
|
||||
lvm(8), pvcreate(8), lvcreate(8), vgcreate(8)
|
||||
.SH AUTHOR
|
||||
Heinz Mauelshagen <Linux-LVM@Sistina.com>
|
||||
100
old-tests/datastruct/hash_t.c
Normal file
100
old-tests/datastruct/hash_t.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "hash.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void _help(FILE *fp, const char *prog)
|
||||
{
|
||||
fprintf(fp, "Usage : %s <table size> <num_entries>\n", prog);
|
||||
}
|
||||
|
||||
struct key_list {
|
||||
struct key_list *next;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
static struct key_list *_create_word(int n)
|
||||
{
|
||||
struct key_list *kl = dbg_malloc(sizeof(*kl) + 32);
|
||||
snprintf(kl->key, 32, "abc%ddef%d", n, n);
|
||||
kl->next = 0;
|
||||
return kl;
|
||||
}
|
||||
|
||||
static struct key_list *_create_word_from_file(int n)
|
||||
{
|
||||
char word[128], *ptr;
|
||||
struct key_list *kl;
|
||||
|
||||
if (!fgets(word, sizeof(word), stdin))
|
||||
return 0;
|
||||
|
||||
for (ptr = word; *ptr; ptr++) {
|
||||
if (*ptr == '\n') {
|
||||
*ptr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kl = dbg_malloc(sizeof(*kl) + 32);
|
||||
snprintf(kl->key, 32, "%s", word);
|
||||
kl->next = 0;
|
||||
return kl;
|
||||
}
|
||||
|
||||
static void _do_test(int table_size, int num_entries)
|
||||
{
|
||||
int i;
|
||||
hash_table_t ht = hash_create(table_size);
|
||||
struct key_list *tmp, *key, *all = 0;
|
||||
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
/* make up a word */
|
||||
if (!(key = _create_word_from_file(i))) {
|
||||
log_error("Ran out of words !\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* insert it */
|
||||
hash_insert(ht, key->key, key);
|
||||
key->next = all;
|
||||
all = key;
|
||||
}
|
||||
|
||||
for (key = all; key; key = key->next) {
|
||||
tmp = (struct key_list *) hash_lookup(ht, key->key);
|
||||
if (!tmp || (tmp != key)) {
|
||||
log_error("lookup failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
for (key = all; key; key = tmp) {
|
||||
tmp = key->next;
|
||||
dbg_free(key);
|
||||
}
|
||||
|
||||
hash_destroy(ht);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
init_log();
|
||||
|
||||
if (argc != 3) {
|
||||
_help(stderr, argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_do_test(atoi(argv[1]), atoi(argv[2]));
|
||||
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
45
old-tests/dev-mgr/dev_cache_t.c
Normal file
45
old-tests/dev-mgr/dev_cache_t.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dev-cache.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
struct device *dev;
|
||||
struct dev_iter *iter;
|
||||
|
||||
init_log();
|
||||
if (!dev_cache_init()) {
|
||||
log_error("couldn't initialise dev_cache_init failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!dev_cache_add_dir(argv[i])) {
|
||||
log_error("couldn't add '%s' to dev_cache\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(iter = dev_iter_create(NULL))) {
|
||||
log_error("couldn't create iterator\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((dev = dev_iter_next(iter)))
|
||||
printf("%s\n", dev->name);
|
||||
|
||||
dev_iter_destroy(iter):
|
||||
dev_cache_exit();
|
||||
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
20
old-tests/device/Makefile.in
Normal file
20
old-tests/device/Makefile.in
Normal file
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
#
|
||||
# This file is released under the GPL.
|
||||
#
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES=\
|
||||
dev_cache_t.c
|
||||
|
||||
TARGETS=dev_cache_t
|
||||
|
||||
include ../../make.tmpl
|
||||
|
||||
dev_cache_t: dev_cache_t.o $(top_srcdir)/lib/liblvm.a
|
||||
$(CC) -o dev_cache_t dev_cache_t.o -L$(top_srcdir)/lib -llvm
|
||||
|
||||
48
old-tests/device/dev_cache_t.c
Normal file
48
old-tests/device/dev_cache_t.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "dev-cache.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
struct device *dev;
|
||||
struct dev_iter *iter;
|
||||
|
||||
init_log(stderr);
|
||||
init_debug(_LOG_INFO);
|
||||
|
||||
if (!dev_cache_init()) {
|
||||
log_err("couldn't initialise dev_cache_init failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!dev_cache_add_dir(argv[i])) {
|
||||
log_err("couldn't add '%s' to dev_cache", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(iter = dev_iter_create(NULL))) {
|
||||
log_err("couldn't create iterator");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((dev = dev_iter_get(iter)))
|
||||
printf("%s\n", dev->name);
|
||||
|
||||
dev_iter_destroy(iter);
|
||||
dev_cache_exit();
|
||||
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
43
old-tests/format1/Makefile.in
Normal file
43
old-tests/format1/Makefile.in
Normal file
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
#
|
||||
# This file is released under the GPL.
|
||||
#
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES=\
|
||||
read_vg_t.c \
|
||||
write_vg_t.c \
|
||||
pretty_print.c \
|
||||
get_pvs_t.c \
|
||||
read_pv_t.c \
|
||||
get_vgs_t.c
|
||||
|
||||
TARGETS=\
|
||||
read_vg_t \
|
||||
write_vg_t \
|
||||
get_pvs_t \
|
||||
read_pv_t \
|
||||
get_vgs_t
|
||||
|
||||
include ../../make.tmpl
|
||||
|
||||
read_vg_t: read_vg_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a
|
||||
$(CC) -o read_vg_t read_vg_t.o pretty_print.o -L$(top_srcdir)/lib -llvm
|
||||
|
||||
write_vg_t: write_vg_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a
|
||||
$(CC) -o write_vg_t write_vg_t.o pretty_print.o \
|
||||
-L$(top_srcdir)/lib -llvm
|
||||
|
||||
get_pvs_t: get_pvs_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a
|
||||
$(CC) -o get_pvs_t get_pvs_t.o pretty_print.o -L$(top_srcdir)/lib -llvm
|
||||
|
||||
read_pv_t: read_pv_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a
|
||||
$(CC) -o read_pv_t read_pv_t.o pretty_print.o -L$(top_srcdir)/lib -llvm
|
||||
|
||||
get_vgs_t: get_vgs_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a
|
||||
$(CC) -o get_vgs_t get_vgs_t.o pretty_print.o -L$(top_srcdir)/lib -llvm
|
||||
|
||||
66
old-tests/format1/get_pvs_t.c
Normal file
66
old-tests/format1/get_pvs_t.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "format1.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "pretty_print.h"
|
||||
#include "list.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct io_space *ios;
|
||||
struct list_head *pvs, *tmp;
|
||||
struct pool *mem;
|
||||
|
||||
init_log(stderr);
|
||||
init_debug(_LOG_INFO);
|
||||
|
||||
if (!dev_cache_init()) {
|
||||
fprintf(stderr, "init of dev-cache failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!dev_cache_add_dir("/dev/loop")) {
|
||||
fprintf(stderr, "couldn't add /dev to dir-cache\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!(mem = pool_create(10 * 1024))) {
|
||||
fprintf(stderr, "couldn't create pool\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ios = create_lvm1_format("/dev", mem, NULL);
|
||||
|
||||
if (!ios) {
|
||||
fprintf(stderr, "failed to create io_space for format1\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pvs = ios->get_pvs(ios);
|
||||
|
||||
if (!pvs) {
|
||||
fprintf(stderr, "couldn't read vg %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
list_for_each(tmp, pvs) {
|
||||
struct pv_list *pvl = list_entry(tmp, struct pv_list, list);
|
||||
dump_pv(&pvl->pv, stdout);
|
||||
}
|
||||
|
||||
ios->destroy(ios);
|
||||
|
||||
pool_destroy(mem);
|
||||
dev_cache_exit();
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
62
old-tests/format1/get_vgs_t.c
Normal file
62
old-tests/format1/get_vgs_t.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "format1.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "pretty_print.h"
|
||||
#include "list.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct io_space *ios;
|
||||
struct list_head *vgs;
|
||||
struct pool *mem;
|
||||
|
||||
init_log(stderr);
|
||||
init_debug(_LOG_INFO);
|
||||
|
||||
if (!dev_cache_init()) {
|
||||
fprintf(stderr, "init of dev-cache failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!dev_cache_add_dir("/dev/loop")) {
|
||||
fprintf(stderr, "couldn't add /dev to dir-cache\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!(mem = pool_create(10 * 1024))) {
|
||||
fprintf(stderr, "couldn't create pool\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ios = create_lvm1_format("/dev", mem, NULL);
|
||||
|
||||
if (!ios) {
|
||||
fprintf(stderr, "failed to create io_space for format1\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
vgs = ios->get_vgs(ios);
|
||||
|
||||
if (!vgs) {
|
||||
fprintf(stderr, "couldn't read vg names\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dump_vg_names(vgs, stdout);
|
||||
ios->destroy(ios);
|
||||
|
||||
pool_destroy(mem);
|
||||
dev_cache_exit();
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
77
old-tests/format1/pretty_print.c
Normal file
77
old-tests/format1/pretty_print.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "pretty_print.h"
|
||||
|
||||
void dump_pv(struct physical_volume *pv, FILE *fp)
|
||||
{
|
||||
fprintf(fp, "physical_volume {\n");
|
||||
fprintf(fp, "\tname = '%s'\n", pv->dev->name);
|
||||
fprintf(fp, "\tvg_name = '%s'\n", pv->vg_name);
|
||||
fprintf(fp, "\tsize = %llu\n", pv->size);
|
||||
fprintf(fp, "\tpe_size = %llu\n", pv->pe_size);
|
||||
fprintf(fp, "\tpe_start = %llu\n", pv->pe_start);
|
||||
fprintf(fp, "\tpe_count = %u\n", pv->pe_count);
|
||||
fprintf(fp, "\tpe_allocated = %u\n", pv->pe_allocated);
|
||||
fprintf(fp, "}\n\n");
|
||||
}
|
||||
|
||||
void dump_lv(struct logical_volume *lv, FILE *fp)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf(fp, "logical_volume {\n");
|
||||
fprintf(fp, "\tname = '%s'\n", lv->name);
|
||||
fprintf(fp, "\tsize = %llu\n", lv->size);
|
||||
fprintf(fp, "\tle_count = %u\n", lv->le_count);
|
||||
|
||||
fprintf(fp, "\tmap {\n");
|
||||
for (i = 0; i < lv->le_count; i++) {
|
||||
struct physical_volume *pv = lv->map[i].pv;
|
||||
|
||||
fprintf(fp, "\t\tpv = '%s', ",
|
||||
pv ? pv->dev->name : "null ???");
|
||||
fprintf(fp, "\textent = %u\n", lv->map[i].pe);
|
||||
}
|
||||
fprintf(fp, "\t}\n}\n\n");
|
||||
}
|
||||
|
||||
void dump_vg(struct volume_group *vg, FILE *fp)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
|
||||
fprintf(fp, "volume_group {\n");
|
||||
fprintf(fp, "\tname = '%s'\n", vg->name);
|
||||
fprintf(fp, "\textent_size = %llu\n", vg->extent_size);
|
||||
fprintf(fp, "\textent_count = %d\n", vg->extent_count);
|
||||
fprintf(fp, "\tfree_count = %d\n", vg->free_count);
|
||||
fprintf(fp, "\tmax_lv = %d\n", vg->max_lv);
|
||||
fprintf(fp, "\tmax_pv = %d\n", vg->max_pv);
|
||||
fprintf(fp, "\tpv_count = %d\n", vg->pv_count);
|
||||
fprintf(fp, "\tlv_count = %d\n", vg->lv_count);
|
||||
fprintf(fp, "}\n\n");
|
||||
|
||||
list_for_each(tmp, &vg->pvs) {
|
||||
struct pv_list *pvl = list_entry(tmp, struct pv_list, list);
|
||||
dump_pv(&pvl->pv, fp);
|
||||
}
|
||||
|
||||
list_for_each(tmp, &vg->lvs) {
|
||||
struct lv_list *lvl = list_entry(tmp, struct lv_list, list);
|
||||
dump_lv(&lvl->lv, fp);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_vg_names(struct list_head *vg_names, FILE *fp)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct name_list *nl;
|
||||
|
||||
list_for_each(tmp, vg_names) {
|
||||
nl = list_entry(tmp, struct name_list, list);
|
||||
fprintf(fp, "%s\n", nl->name);
|
||||
}
|
||||
}
|
||||
19
old-tests/format1/pretty_print.h
Normal file
19
old-tests/format1/pretty_print.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_PRETTY_PRINT
|
||||
#define _LVM_PRETTY_PRINT
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void dump_pv(struct physical_volume *pv, FILE *fp);
|
||||
void dump_lv(struct logical_volume *lv, FILE *fp);
|
||||
void dump_vg(struct volume_group *vg, FILE *fp);
|
||||
void dump_vg_names(struct list_head *vg_names, FILE *fp);
|
||||
|
||||
#endif
|
||||
68
old-tests/format1/read_pv_t.c
Normal file
68
old-tests/format1/read_pv_t.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "format1.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "pretty_print.h"
|
||||
#include "list.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct io_space *ios;
|
||||
struct physical_volume *pv;
|
||||
struct pool *mem;
|
||||
struct device *dev;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: read_pv_t <device>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
init_log(stderr);
|
||||
init_debug(_LOG_INFO);
|
||||
|
||||
if (!dev_cache_init()) {
|
||||
fprintf(stderr, "init of dev-cache failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!dev_cache_add_dir("/dev/loop")) {
|
||||
fprintf(stderr, "couldn't add /dev to dir-cache\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!(mem = pool_create(10 * 1024))) {
|
||||
fprintf(stderr, "couldn't create pool\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ios = create_lvm1_format("/dev", mem, NULL);
|
||||
|
||||
if (!ios) {
|
||||
fprintf(stderr, "failed to create io_space for format1\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pv = ios->pv_read(ios, argv[1]);
|
||||
|
||||
if (!pv) {
|
||||
fprintf(stderr, "couldn't read pv %s\n", dev->name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dump_pv(pv, stdout);
|
||||
ios->destroy(ios);
|
||||
|
||||
pool_destroy(mem);
|
||||
dev_cache_exit();
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
67
old-tests/format1/read_vg_t.c
Normal file
67
old-tests/format1/read_vg_t.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "format1.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "pretty_print.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct io_space *ios;
|
||||
struct volume_group *vg;
|
||||
struct pool *mem;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: read_vg_t <vg_name>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
init_log(stderr);
|
||||
init_debug(_LOG_INFO);
|
||||
|
||||
if (!dev_cache_init()) {
|
||||
fprintf(stderr, "init of dev-cache failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!dev_cache_add_dir("/dev/loop")) {
|
||||
fprintf(stderr, "couldn't add /dev to dir-cache\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!(mem = pool_create(10 * 1024))) {
|
||||
fprintf(stderr, "couldn't create pool\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ios = create_lvm1_format("/dev", mem, NULL);
|
||||
|
||||
if (!ios) {
|
||||
fprintf(stderr, "failed to create io_space for format1\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
vg = ios->vg_read(ios, argv[1]);
|
||||
|
||||
if (!vg) {
|
||||
fprintf(stderr, "couldn't read vg %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dump_vg(vg, stdout);
|
||||
|
||||
ios->destroy(ios);
|
||||
|
||||
pool_destroy(mem);
|
||||
dev_cache_exit();
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
70
old-tests/format1/write_vg_t.c
Normal file
70
old-tests/format1/write_vg_t.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "format1.h"
|
||||
#include "dbg_malloc.h"
|
||||
#include "pool.h"
|
||||
#include "pretty_print.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct io_space *ios;
|
||||
struct volume_group *vg;
|
||||
struct pool *mem;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: read_vg_t <vg_name>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
init_log(stderr);
|
||||
init_debug(_LOG_INFO);
|
||||
|
||||
if (!dev_cache_init()) {
|
||||
fprintf(stderr, "init of dev-cache failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!dev_cache_add_dir("/dev/loop")) {
|
||||
fprintf(stderr, "couldn't add /dev to dir-cache\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!(mem = pool_create(10 * 1024))) {
|
||||
fprintf(stderr, "couldn't create pool\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ios = create_lvm1_format("/dev", mem, NULL);
|
||||
|
||||
if (!ios) {
|
||||
fprintf(stderr, "failed to create io_space for format1\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
vg = ios->vg_read(ios, argv[1]);
|
||||
|
||||
if (!vg) {
|
||||
fprintf(stderr, "couldn't read vg %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!ios->vg_write(ios, vg)) {
|
||||
fprintf(stderr, "couldn't write vg\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ios->destroy(ios);
|
||||
|
||||
pool_destroy(mem);
|
||||
dev_cache_exit();
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
20
old-tests/mm/Makefile.in
Normal file
20
old-tests/mm/Makefile.in
Normal file
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
#
|
||||
# This file is released under the GPL.
|
||||
#
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES=\
|
||||
dbg_malloc_t.c
|
||||
|
||||
TARGETS=dbg_malloc_t
|
||||
|
||||
include ../../make.tmpl
|
||||
|
||||
dbg_malloc_t: dbg_malloc_t.o
|
||||
$(CC) -o dbg_malloc_t dbg_malloc_t.o -L$(top_srcdir)/lib -llvm
|
||||
|
||||
148
old-tests/mm/dbg_malloc_t.c
Normal file
148
old-tests/mm/dbg_malloc_t.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dbg_malloc.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
static void _print_help(FILE *out, const char *prog)
|
||||
{
|
||||
fprintf(out, "usage : %s [-hlbufd]\n\n", prog);
|
||||
fprintf(out, " h : this message\n");
|
||||
fprintf(out, " l : cause memory leak\n");
|
||||
fprintf(out, " b : overrun memory block\n");
|
||||
fprintf(out, " u : underrun memory block\n");
|
||||
fprintf(out, " f : free random pointer\n");
|
||||
fprintf(out, " d : free block twice\n");
|
||||
}
|
||||
|
||||
struct block_list {
|
||||
struct block_list *next;
|
||||
char dummy[9];
|
||||
};
|
||||
|
||||
static void _leak_memory(void)
|
||||
{
|
||||
int i;
|
||||
struct block_list *b, *head, **l = &head, *n;
|
||||
|
||||
/* allocate a list of blocks */
|
||||
for (i = 0; i < 1000; i++) {
|
||||
|
||||
if (!(b = dbg_malloc(sizeof(*b)))) {
|
||||
log_fatal("Couldn't allocate memory");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
b->next = 0;
|
||||
*l = b;
|
||||
l = &b->next;
|
||||
}
|
||||
|
||||
/* free off every other block */
|
||||
for (b = head, i = 0; b; b = n, i++) {
|
||||
n = b->next;
|
||||
if(i & 0x1)
|
||||
dbg_free(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void _bounds_overrun(void)
|
||||
{
|
||||
char *b;
|
||||
|
||||
/* allocate a block */
|
||||
b = dbg_malloc(534);
|
||||
|
||||
/* overrun */
|
||||
b[534] = 56;
|
||||
|
||||
/* free it, which should trigger the bounds error */
|
||||
dbg_free(b);
|
||||
}
|
||||
|
||||
static void _bounds_underrun(void)
|
||||
{
|
||||
char *b;
|
||||
|
||||
/* allocate a block */
|
||||
b = dbg_malloc(534);
|
||||
|
||||
/* underrun */
|
||||
*(b - 1) = 56;
|
||||
|
||||
/* free it, which should trigger the bounds error */
|
||||
dbg_free(b);
|
||||
}
|
||||
|
||||
static void _free_dud(void)
|
||||
{
|
||||
char *b;
|
||||
|
||||
/* allocate a block */
|
||||
b = dbg_malloc(534);
|
||||
|
||||
/* free it, which should trigger the bounds error */
|
||||
dbg_free(b + 100);
|
||||
}
|
||||
|
||||
static void _free_twice(void)
|
||||
{
|
||||
char *b;
|
||||
|
||||
/* allocate a block */
|
||||
b = dbg_malloc(534);
|
||||
|
||||
/* free it, which should trigger the bounds error */
|
||||
dbg_free(b);
|
||||
dbg_free(b);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char opt;
|
||||
|
||||
init_log(stderr);
|
||||
init_debug(_LOG_DEBUG);
|
||||
opt = getopt(argc, argv, "hlbufd");
|
||||
switch(opt) {
|
||||
case EOF:
|
||||
case 'h':
|
||||
_print_help(stdout, argv[0]);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
_leak_memory();
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
_bounds_overrun();
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
_bounds_underrun();
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
_free_dud();
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
_free_twice();
|
||||
break;
|
||||
|
||||
case '?':
|
||||
fprintf(stderr, "Unknown option -%c\n", opt);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dump_memory();
|
||||
fin_log();
|
||||
return 0;
|
||||
}
|
||||
57
tools/Makefile.in
Normal file
57
tools/Makefile.in
Normal file
@@ -0,0 +1,57 @@
|
||||
#
|
||||
# Copyright (C) 2001 Sistina Software
|
||||
#
|
||||
# This LVM 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 LVM 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 LVM library; if not, write to the Free
|
||||
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
# MA 02111-1307, USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES=\
|
||||
lvm.c \
|
||||
lvmchange.c \
|
||||
pvcreate.c \
|
||||
pvscan.c \
|
||||
toollib.c \
|
||||
vgck.c \
|
||||
vgchange.c \
|
||||
vgcreate.c \
|
||||
vgextend.c \
|
||||
vgreduce.c \
|
||||
vgrename.c \
|
||||
vgremove.c \
|
||||
vgscan.c
|
||||
|
||||
TARGETS=\
|
||||
lvm
|
||||
|
||||
include ../make.tmpl
|
||||
|
||||
lvm: $(OBJECTS) $(top_srcdir)/lib/liblvm.a
|
||||
$(CC) -o lvm $(OBJECTS) -L$(top_srcdir)/lib -llvm $(EXTRA_LIBS)
|
||||
|
||||
install:
|
||||
$(INSTALL) -c -d $(sbindir);
|
||||
$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 555 -s lvm $(sbindir)/lvm
|
||||
( cd $(sbindir); \
|
||||
for v in ${SOURCES:.c=}; do \
|
||||
if [ "x$$v" != "xlvm" ]; then \
|
||||
$(LN_S) -f lvm $$v; \
|
||||
fi \
|
||||
done; \
|
||||
)
|
||||
|
||||
|
||||
73
tools/args.h
Normal file
73
tools/args.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* LVM 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.
|
||||
*
|
||||
* LVM 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 LVM; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
xx(available_ARG, 'a', "available", yes_no_arg)
|
||||
xx(all_ARG, 'a', "all", NULL)
|
||||
xx(autobackup_ARG, 'A', "autobackup", yes_no_arg)
|
||||
xx(activevolumegroups_ARG, 'A', "activevolumegroups", NULL)
|
||||
xx(blockdevice_ARG, 'b', "blockdevice", NULL)
|
||||
xx(chunksize_ARG, 'c', "chunksize", size_arg)
|
||||
xx(colon_ARG, 'c', "colon", NULL)
|
||||
xx(contiguous_ARG, 'C', "contiguous", yes_no_arg)
|
||||
xx(debug_ARG, 'd', "debug", NULL)
|
||||
xx(disk_ARG, 'D', "disk", NULL)
|
||||
xx(exported_ARG, 'e', "exported", NULL)
|
||||
xx(physicalextent_ARG, 'E', "physicalextent", NULL)
|
||||
xx(file_ARG, 'f', "file", NULL)
|
||||
xx(force_ARG, 'f', "force", NULL)
|
||||
xx(full_ARG, 'f', "full", NULL)
|
||||
xx(help_ARG, 'h', "help", NULL)
|
||||
xx(stripesize_ARG, 'I', "stripesize", size_arg)
|
||||
xx(stripes_ARG, 'i', "stripes", int_arg)
|
||||
xx(iop_version_ARG, 'i', "iop_version", NULL)
|
||||
xx(logicalvolume_ARG, 'l', "logicalvolume", int_arg)
|
||||
xx(maxlogicalvolumes_ARG, 'l', "maxlogicalvolumes", int_arg)
|
||||
xx(extents_ARG, 'l', "extents", int_arg)
|
||||
xx(lvmpartition_ARG, 'l', "lvmpartition", NULL)
|
||||
xx(list_ARG, 'l', "list", NULL)
|
||||
xx(size_ARG, 'L', "size", size_arg)
|
||||
xx(logicalextent_ARG, 'L', "logicalextent", int_arg)
|
||||
xx(name_ARG, 'n', "name", string_arg)
|
||||
xx(oldpath_ARG, 'n', "oldpath", NULL)
|
||||
xx(nofsck_ARG, 'n', "nofsck", NULL)
|
||||
xx(novolumegroup_ARG, 'n', "novolumegroup", NULL)
|
||||
xx(permission_ARG, 'p', "permission", permission_arg)
|
||||
xx(maxphysicalvolumes_ARG, 'p', "maxphysicalvolumes", int_arg)
|
||||
xx(physicalvolume_ARG, 'P', "physicalvolume", NULL)
|
||||
xx(readahead_ARG, 'r', "readahead", int_arg)
|
||||
xx(reset_ARG, 'R', "reset", NULL)
|
||||
xx(physicalextentsize_ARG, 's', "physicalextentsize", size_arg)
|
||||
xx(stdin_ARG, 's', "stdin", NULL)
|
||||
xx(snapshot_ARG, 's', "snapshot", NULL)
|
||||
xx(short_ARG, 's', "short", NULL)
|
||||
xx(test_ARG, 't', "test", NULL)
|
||||
xx(uuid_ARG, 'u', "uuid", NULL)
|
||||
xx(uuidlist_ARG, 'U', "uuidlist", NULL)
|
||||
xx(verbose_ARG, 'v', "verbose", NULL)
|
||||
xx(volumegroup_ARG, 'V', "volumegroup", NULL)
|
||||
xx(version_ARG, '\0', "version", NULL)
|
||||
xx(allocation_ARG, 'x', "allocation", yes_no_arg)
|
||||
xx(yes_ARG, 'y', "yes", NULL)
|
||||
xx(zero_ARG, 'Z', "zero", yes_no_arg)
|
||||
xx(suspend_ARG, 'z', "suspend", NULL)
|
||||
|
||||
/* this should always be last */
|
||||
xx(ARG_COUNT, '-', "", NULL)
|
||||
|
||||
488
tools/commands.h
Normal file
488
tools/commands.h
Normal file
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* LVM 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.
|
||||
*
|
||||
* LVM 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 LVM; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
xx(e2fsadm,
|
||||
"Resize logical volume and ext2 filesystem",
|
||||
"e2fsadm "
|
||||
"[-d|--debug] "
|
||||
"[-h|--help] "
|
||||
"[-n|--nofsck]\n\t"
|
||||
"{[-l|--extents] [+|-]LogicalExtentsNumber |\n\t"
|
||||
" [-L|--size] [+|-]LogicalVolumeSize[kKmMgGtT]}\n\t"
|
||||
"[-t|--test] "
|
||||
"[-v|--verbose] "
|
||||
"[--version] "
|
||||
"LogicalVolumePath\n",
|
||||
|
||||
extents_ARG, size_ARG, nofsck_ARG, test_ARG)
|
||||
|
||||
xx(help,
|
||||
"Display help for commands",
|
||||
"help <command>\n")
|
||||
|
||||
/*********
|
||||
xx(lvactivate,
|
||||
"Activate logical volume on given partition(s)",
|
||||
"lvactivate "
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"Logical Volume(s)\n")
|
||||
***********/
|
||||
|
||||
xx(lvchange,
|
||||
"Change the attributes of logical volume(s)",
|
||||
"lvchange\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-a/--available y/n]\n"
|
||||
"\t[-C/--contiguous y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-p/--permission r/rw]\n"
|
||||
"\t[-r/--readahead ReadAheadSectors]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
|
||||
|
||||
autobackup_ARG, available_ARG, contiguous_ARG,
|
||||
permission_ARG, readahead_ARG)
|
||||
|
||||
xx(lvcreate,
|
||||
"Create a logical volume",
|
||||
"lvcreate "
|
||||
"[-A|--autobackup {y|n}] "
|
||||
"[-C|--contiguous {y|n}] "
|
||||
"[-d|--debug]\n"
|
||||
"\t[-h|--help] "
|
||||
"[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n\t"
|
||||
"{-l|--extents LogicalExtentsNumber |\n\t"
|
||||
" -L|--size LogicalVolumeSize[kKmMgGtT]} "
|
||||
"[-n|--name LogicalVolumeName]\n\t"
|
||||
"[-p|--permission {r|rw}] "
|
||||
"[-r|--readahead ReadAheadSectors]\n\t"
|
||||
"[-v|--verbose] "
|
||||
"[-Z|--zero {y|n}] "
|
||||
"[--version]\n\t"
|
||||
"VolumeGroupName [PhysicalVolumePath...]\n\n"
|
||||
"lvcreate "
|
||||
"-s|--snapshot "
|
||||
"[-c|--chunksize ChunkSize]\n\t"
|
||||
"{-l|--extents LogicalExtentsNumber |\n\t"
|
||||
" -L|--size LogicalVolumeSize[kKmMgGtT]}\n\t"
|
||||
"-n|--name SnapshotLogicalVolumeName\n\t"
|
||||
"LogicalVolume[Path] [PhysicalVolumePath...]\n",
|
||||
|
||||
autobackup_ARG, chunksize_ARG, contiguous_ARG,
|
||||
stripes_ARG, stripesize_ARG, extents_ARG, size_ARG, name_ARG,
|
||||
permission_ARG, readahead_ARG, snapshot_ARG, zero_ARG)
|
||||
|
||||
xx(lvdisplay,
|
||||
"Display information about a logical volume",
|
||||
"lvdisplay\n"
|
||||
"\t[-c/--colon]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-D/--disk]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v[v]/--verbose [--verbose]]\n"
|
||||
"\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
|
||||
|
||||
colon_ARG, disk_ARG)
|
||||
|
||||
xx(lvextend,
|
||||
"Add space to a logical volume",
|
||||
"lvextend\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t{-l/--extents [+]LogicalExtentsNumber |\n"
|
||||
"\t -L/--size [+]LogicalVolumeSize[kKmMgGtT]}\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n",
|
||||
|
||||
autobackup_ARG, extents_ARG, size_ARG)
|
||||
|
||||
xx(lvmchange,
|
||||
"With the device mapper, lvmchange is obsolete and does nothing.",
|
||||
"lvmchange\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-f/--force]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-i/-?/--iop_version]\n"
|
||||
"\t[-R/--reset]\n"
|
||||
"\t[-v/--verbose]\n",
|
||||
|
||||
force_ARG, reset_ARG)
|
||||
|
||||
xx(lvmdiskscan,
|
||||
"List devices that may be used as physical volumes",
|
||||
"lvmdiskscan\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-l/--lvmpartition]\n"
|
||||
"\t[-v/--verbose]\n",
|
||||
|
||||
lvmpartition_ARG)
|
||||
|
||||
xx(lvmsadc,
|
||||
"Collect activity data",
|
||||
"lvmsadc\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\t[LogFilePath]\n" )
|
||||
|
||||
xx(lvmsar,
|
||||
"Create activity report",
|
||||
"lvmsar\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-f/--full]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-s/--stdin]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tLogFilePath\n",
|
||||
|
||||
full_ARG, stdin_ARG)
|
||||
|
||||
xx(lvreduce,
|
||||
"Reduce the size of a logical volume",
|
||||
"lvreduce\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-f/--force]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t{-l/--extents [-]LogicalExtentsNumber |\n"
|
||||
"\t -L/--size [-]LogicalVolumeSize[kKmMgGtT]}\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tLogicalVolume[Path]\n",
|
||||
|
||||
autobackup_ARG, force_ARG, extents_ARG,
|
||||
size_ARG, yes_ARG)
|
||||
|
||||
xx(lvremove,
|
||||
"Remove logical volume(s) from the system",
|
||||
"lvremove\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-f/--force]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
|
||||
|
||||
autobackup_ARG, force_ARG)
|
||||
|
||||
xx(lvrename,
|
||||
"Rename a logical volume",
|
||||
"lvrename "
|
||||
"[-A|--autobackup {y|n}] "
|
||||
"[-d|--debug] "
|
||||
"[-h|--help] "
|
||||
"[-v|--verbose]\n\t"
|
||||
"[--version] "
|
||||
"{ OldLogicalVolumePath NewLogicalVolumePath |\n\t"
|
||||
" VolumeGroupName OldLogicalVolumeName NewLogicalVolumeName }\n",
|
||||
|
||||
autobackup_ARG)
|
||||
|
||||
xx(lvscan,
|
||||
"List all logical volumes in all volume groups",
|
||||
"lvscan "
|
||||
"[-b|--blockdevice] "
|
||||
"[-d|--debug] "
|
||||
"[-D|--disk]\n\t"
|
||||
"[-h|--help] "
|
||||
"[-v|--verbose] "
|
||||
"[--version]\n",
|
||||
|
||||
blockdevice_ARG, disk_ARG)
|
||||
|
||||
xx(pvchange,
|
||||
"Change attributes of physical volume(s)",
|
||||
"pvchange\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\t[-a/--all]\n"
|
||||
"\t[-x/--allocation y/n]\n"
|
||||
"\t[PhysicalVolumePath...]\n",
|
||||
|
||||
all_ARG, autobackup_ARG, allocation_ARG)
|
||||
|
||||
xx(pvcreate,
|
||||
"Initialize physical volume(s) for use by LVM",
|
||||
"pvcreate "
|
||||
"[-d|--debug]"
|
||||
"[-f[f]|--force [--force]] "
|
||||
"[-h|--help] "
|
||||
"[-y|--yes]\n\t"
|
||||
"[-v|--verbose] "
|
||||
"[--version] "
|
||||
"PhysicalVolume [PhysicalVolume...]\n",
|
||||
|
||||
force_ARG, yes_ARG)
|
||||
|
||||
xx(pvdata,
|
||||
"Display the on-disk metadata for physical volume(s)",
|
||||
"pvdata "
|
||||
"[-a|--all] "
|
||||
"[-d|--debug] "
|
||||
"[-E|--physicalextent] "
|
||||
"[-h|--help]\n\t"
|
||||
"[-L|--logicalvolume] "
|
||||
"[-P[P]|--physicalvolume [--physicalvolume]]\n\t"
|
||||
"[-U|--uuidlist] "
|
||||
"[-v[v]|--verbose [--verbose]] "
|
||||
"[-V|--volumegroup]\n\t"
|
||||
"[--version] "
|
||||
"PhysicalVolume [PhysicalVolume...]\n",
|
||||
|
||||
all_ARG, logicalextent_ARG, physicalextent_ARG,
|
||||
physicalvolume_ARG, uuidlist_ARG, volumegroup_ARG)
|
||||
|
||||
xx(pvdisplay,
|
||||
"Display various attributes of logical volume(s)",
|
||||
"pvdisplay\n"
|
||||
"\t[-c/--colon]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-s/--short]\n"
|
||||
"\t[-v[v]/--verbose [--verbose]]\n"
|
||||
"\tPhysicalVolumePath [PhysicalVolumePath...]\n",
|
||||
|
||||
colon_ARG, short_ARG)
|
||||
|
||||
#if 0
|
||||
xx(pvmove,
|
||||
"Move extents from one physical volume to another",
|
||||
"pvmove "
|
||||
"[-A|--autobackup {y|n}] "
|
||||
"[-d|--debug] "
|
||||
"[-f|--force]"
|
||||
"[-h|--help]\n\t"
|
||||
"[-t|--test] "
|
||||
"[-v[v]|--verbose [--verbose]] "
|
||||
"[--version]\n\t"
|
||||
"[{-n|--name} LogicalVolume[:LogicalExtent[-LogicalExtent]...]]\n\t"
|
||||
"SourcePhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]}\n\t"
|
||||
"[DestinationPhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]...]\n",
|
||||
|
||||
autobackup_ARG, force_ARG, name_ARG, test_ARG)
|
||||
#endif
|
||||
|
||||
xx(pvscan,
|
||||
"List all physical volumes",
|
||||
"pvscan "
|
||||
"[-d|--debug] "
|
||||
"{-e|--exported | -n/--novolumegroup} "
|
||||
"[-h|--help]\n\t"
|
||||
"[-s|--short] "
|
||||
"[-u|--uuid] "
|
||||
"[-v[v]|--verbose [--verbose]] "
|
||||
"[--version]\n",
|
||||
|
||||
exported_ARG, novolumegroup_ARG, short_ARG, uuid_ARG)
|
||||
|
||||
xx(vgcfgbackup,
|
||||
"Backup volume group configuration(s)",
|
||||
"vgcfgbackup "
|
||||
"[-d|--debug] "
|
||||
"[-h|--help] "
|
||||
"[-v|--verbose]\n\t"
|
||||
"[-V|--version] "
|
||||
"[VolumeGroupName...]\n" )
|
||||
|
||||
xx(vgcfgrestore,
|
||||
"Restore volume group configuration",
|
||||
"vgcfgrestore "
|
||||
"[-d|--debug] "
|
||||
"[-f|--file VGConfPath] "
|
||||
"[-l[l]|--list [--list]]\n\t"
|
||||
"[-n|--name VolumeGroupName] "
|
||||
"[-h|--help]\n\t"
|
||||
"[-o|--oldpath OldPhysicalVolumePath] "
|
||||
"[-t|--test] "
|
||||
"[-v|--verbose]\n\t"
|
||||
"[--version] "
|
||||
"[PhysicalVolumePath]\n",
|
||||
|
||||
file_ARG, list_ARG, name_ARG, oldpath_ARG, test_ARG)
|
||||
|
||||
xx(vgchange,
|
||||
"Change volume group attributes",
|
||||
"vgchange\n\t"
|
||||
"[-A|--autobackup {y|n}] "
|
||||
"[-d|--debug] "
|
||||
"[-h|--help]\n\t"
|
||||
"[-v|--verbose] "
|
||||
"[--version]\n\t"
|
||||
"{-a|--available {y|n} |\n\t"
|
||||
" -x|--allocation {y|n} |\n\t"
|
||||
" -l|--logicalvolume MaxLogicalVolumes}\n\t"
|
||||
"[VolumeGroupName...]\n",
|
||||
|
||||
autobackup_ARG, available_ARG, logicalvolume_ARG, allocation_ARG )
|
||||
|
||||
xx(vgck,
|
||||
"Check the consistency of volume group(s)",
|
||||
"vgck "
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\t[VolumeGroupName...]\n" )
|
||||
|
||||
xx(vgcreate,
|
||||
"Create a volume group",
|
||||
"vgcreate"
|
||||
"[-A|--autobackup {y|n}] "
|
||||
"[-d|--debug]\n\t"
|
||||
"[-l|--maxlogicalvolumes MaxLogicalVolumes]\n\t"
|
||||
"[-p|--maxphysicalvolumes MaxPhysicalVolumes] "
|
||||
"[-h|--help]\n\t"
|
||||
"[-s|--physicalextentsize PhysicalExtentSize[kKmMgGtT]] "
|
||||
"[-v|--verbose]\n\t"
|
||||
"[--version] "
|
||||
"VolumeGroupName "
|
||||
"PhysicalVolume [PhysicalVolume...]\n",
|
||||
|
||||
autobackup_ARG, maxlogicalvolumes_ARG, maxphysicalvolumes_ARG,
|
||||
physicalextentsize_ARG)
|
||||
|
||||
xx(vgdisplay,
|
||||
"Display volume group information",
|
||||
"vgdisplay "
|
||||
"[-c|--colon | -s|--short | -v[v]|--verbose [--verbose]]\n\t"
|
||||
"[-d|--debug] "
|
||||
"[-h|--help] "
|
||||
"[--version]\n\t"
|
||||
"[-A|--activevolumegroups | [-D|--disk] [VolumeGroupName...] ]\n",
|
||||
|
||||
activevolumegroups_ARG, colon_ARG, disk_ARG, short_ARG)
|
||||
|
||||
xx(vgexport,
|
||||
"Unregister volume group(s) from the system",
|
||||
"vgexport "
|
||||
"[-a|--all] "
|
||||
"[-d|--debug] "
|
||||
"[-h|--help]\n\t"
|
||||
"[-v|--verbose] "
|
||||
"[--version] "
|
||||
"VolumeGroupName [VolumeGroupName...]\n",
|
||||
|
||||
all_ARG)
|
||||
|
||||
xx(vgextend,
|
||||
"Add physical volumes to a volume group",
|
||||
"vgextend\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tVolumeGroupName\n"
|
||||
"\tPhysicalDevicePath [PhysicalDevicePath...]\n",
|
||||
|
||||
autobackup_ARG)
|
||||
|
||||
xx(vgimport,
|
||||
"Register exported volume group with system",
|
||||
"vgimport "
|
||||
"[-d|--debug] "
|
||||
"[-f|--force] "
|
||||
"[-h|--help] "
|
||||
"[-v|--verbose]\n\t"
|
||||
"VolumeGroupName PhysicalVolumePath "
|
||||
"[PhysicalVolumePath...]\n",
|
||||
|
||||
force_ARG)
|
||||
|
||||
xx(vgmerge,
|
||||
"Merge volume groups",
|
||||
"vgmerge\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-l/--list]\n"
|
||||
"\t[-t/--test]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tDestinationVolumeGroupName SourceVolumeGroupName\n",
|
||||
|
||||
autobackup_ARG, list_ARG, test_ARG)
|
||||
|
||||
xx(vgmknodes,
|
||||
"Create the special files for volume group devices in /dev",
|
||||
"vgmknodes\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\t[VolumeGroupName...]\n" )
|
||||
|
||||
xx(vgreduce,
|
||||
"Remove physical volume(s) from a volume group",
|
||||
"vgreduce\n"
|
||||
"\t[-a/--all]\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tVolumeGroupName\n"
|
||||
"\t[PhysicalVolumePath...]\n",
|
||||
|
||||
all_ARG, autobackup_ARG)
|
||||
|
||||
xx(vgremove,
|
||||
"Remove volume group(s)",
|
||||
"vgremove\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tVolumeGroupName [VolumeGroupName...]\n" )
|
||||
|
||||
xx(vgrename,
|
||||
"Rename a volume group",
|
||||
"vgrename\n"
|
||||
"\t[-A/--autobackup y/n]\n"
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-f/--force]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n"
|
||||
"\tOldVolumeGroupPath NewVolumeGroupPath /\n"
|
||||
"\tOldVolumeGroupName NewVolumeGroupName\n",
|
||||
|
||||
autobackup_ARG, force_ARG)
|
||||
|
||||
xx(vgscan,
|
||||
"Search for all volume groups",
|
||||
"vgscan "
|
||||
"\t[-d/--debug]\n"
|
||||
"\t[-h/-?/--help]\n"
|
||||
"\t[-v/--verbose]\n" )
|
||||
|
||||
xx(vgsplit,
|
||||
"Move physical volumes into a new volume group",
|
||||
"vgsplit "
|
||||
"[-A|--autobackup {y|n}] "
|
||||
"[-d|--debug] "
|
||||
"[-h|--help] "
|
||||
"[-l|--list]\n\t"
|
||||
"[-t|--test] "
|
||||
"[-v|--verbose] "
|
||||
"[--version]\n\t"
|
||||
"ExistingVolumeGroupName NewVolumeGroupName\n\t"
|
||||
"PhysicalVolumePath [PhysicalVolumePath...]\n",
|
||||
|
||||
autobackup_ARG, list_ARG, test_ARG)
|
||||
30
tools/errors.h
Normal file
30
tools/errors.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* LVM 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.
|
||||
*
|
||||
* LVM 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 LVM; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 /* #ifndef _LVM_ERROR_H_INCLUDE */
|
||||
|
||||
95
tools/lvactivate.c
Normal file
95
tools/lvactivate.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* LVM 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.
|
||||
*
|
||||
* LVM 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 LVM; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
int lvactivate(int argc, char **argv)
|
||||
{
|
||||
int p;
|
||||
|
||||
struct device *pv_dev;
|
||||
|
||||
char *lv_name;
|
||||
char *pv_name;
|
||||
|
||||
struct physical_volume *pv = NULL;
|
||||
struct logical_volume *lv = NULL;
|
||||
|
||||
if (argc < 2) {
|
||||
log_error("please enter logical volume & physical volume(s)");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
lv_name = argv[0];
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
while (argc--) {
|
||||
pv_name = argv[argc];
|
||||
if (!(pv_dev = dev_cache_get(pv_name))) {
|
||||
log_error("device \"%s\" not found", pv_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(pv = pv_read(ios, pv_dev))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pv->status & ALLOCATED_PV) {
|
||||
if (!(pv->pe = pv_read_pe(pv_name, pv)))
|
||||
goto pvdisplay_device_out;
|
||||
if (!(lvs = pv_read_lvs(pv))) {
|
||||
log_error("Failed to read LVs on %s",
|
||||
pv->pv_name);
|
||||
goto pvdisplay_device_out;
|
||||
}
|
||||
} else
|
||||
log_print("no logical volume on physical volume %s",
|
||||
pv_name);
|
||||
|
||||
for (p = 0; p < pv->pe_total; p++) {
|
||||
int l = pv->pe[p].lv_num;
|
||||
int le = pv->pe[p].le_num;
|
||||
long pe_size_guess = lvs[l - 1].lv_size /
|
||||
lvs[l - 1].lv_allocated_le;
|
||||
|
||||
if (l && !strcmp(lv, lvs[l - 1].lv_name))
|
||||
printf("%012ld %ld linear %s %012ld\n",
|
||||
pe_size_guess * le,
|
||||
pe_size_guess,
|
||||
pv_name, get_pe_offset(p, pv));
|
||||
}
|
||||
|
||||
if (pv)
|
||||
dbg_free(pv->pe);
|
||||
dbg_free(pv);
|
||||
dbg_free(lvs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
pvdisplay_device_out:
|
||||
if (pv)
|
||||
dbg_free(pv->pe);
|
||||
dbg_free(pv);
|
||||
dbg_free(lvs);
|
||||
|
||||
return -1;
|
||||
}
|
||||
865
tools/lvm.c
Normal file
865
tools/lvm.c
Normal file
@@ -0,0 +1,865 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "stub.h"
|
||||
|
||||
#ifdef READLINE_SUPPORT
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#endif
|
||||
|
||||
/* define the table of valid switches */
|
||||
struct arg the_args[ARG_COUNT + 1] = {
|
||||
|
||||
#define xx(a, b, c, d) {b, "--" c, d, 0, NULL},
|
||||
#include "args.h"
|
||||
#undef xx
|
||||
|
||||
};
|
||||
|
||||
/* a register of the lvm commands */
|
||||
struct command {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
const char *usage;
|
||||
command_fn fn;
|
||||
|
||||
int num_args;
|
||||
int *valid_args;
|
||||
};
|
||||
|
||||
static int _array_size;
|
||||
static int _num_commands;
|
||||
static struct command *_commands;
|
||||
|
||||
/* Exported */
|
||||
struct io_space *ios;
|
||||
|
||||
static struct dev_filter *_filter;
|
||||
static struct config_file *_cf;
|
||||
|
||||
static int _interactive;
|
||||
static FILE *_log;
|
||||
static int _debug_level;
|
||||
|
||||
/* static functions */
|
||||
static void register_commands(void);
|
||||
static struct command *find_command(const char *name);
|
||||
static void register_command(const char *name, command_fn fn,
|
||||
const char *desc, const char *usage, ...);
|
||||
static void create_new_command(const char *name, command_fn command,
|
||||
const char *desc, const char *usage,
|
||||
int nargs, int *args);
|
||||
|
||||
static void alloc_command(void);
|
||||
static void add_getopt_arg(int arg, char **ptr, struct option **o);
|
||||
static int process_command_line(struct command *com, int *argc, char ***argv);
|
||||
static struct arg *find_arg(struct command *com, int a);
|
||||
static int process_common_commands(struct command *com);
|
||||
static int run_command(int argc, char **argv);
|
||||
static int init(void);
|
||||
static void fin(void);
|
||||
static int run_script(int argc, char **argv);
|
||||
|
||||
#ifdef READLINE_SUPPORT
|
||||
static int shell(void);
|
||||
static char **lvm_completion(char *text, int start_pos, int end_pos);
|
||||
static char *list_cmds(char *text, int state);
|
||||
static char *list_args(char *text, int state);
|
||||
#endif
|
||||
|
||||
static void display_help(void);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *namebase, *base;
|
||||
int ret, alias = 0;
|
||||
|
||||
if (!init())
|
||||
return -1;
|
||||
|
||||
namebase = strdup(argv[0]);
|
||||
base = basename(namebase);
|
||||
if (strcmp(base, "lvm"))
|
||||
alias = 1;
|
||||
free(namebase);
|
||||
|
||||
register_commands();
|
||||
|
||||
#ifdef READLINE_SUPPORT
|
||||
if (!alias && argc == 1) {
|
||||
ret = shell();
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!alias) {
|
||||
if (argc < 2) {
|
||||
log_fatal("Please supply an LVM command.");
|
||||
display_help();
|
||||
ret = EINVALID_CMD_LINE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
ret = run_command(argc, argv);
|
||||
if ((ret == ENO_SUCH_CMD) && (!alias))
|
||||
ret = run_script(argc, argv);
|
||||
if (ret == ENO_SUCH_CMD)
|
||||
log_error("No such command. Try 'help'.");
|
||||
|
||||
out:
|
||||
fin();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void usage(const char *name)
|
||||
{
|
||||
struct command *com = find_command(name);
|
||||
|
||||
if (!com)
|
||||
return;
|
||||
|
||||
log_error("%s: %s\n\n%s", com->name, com->desc, com->usage);
|
||||
}
|
||||
|
||||
int yes_no_arg(struct arg *a)
|
||||
{
|
||||
if (!strcmp(a->value, "y"))
|
||||
a->i_value = 1;
|
||||
|
||||
else if (!strcmp(a->value, "n"))
|
||||
a->i_value = 0;
|
||||
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int size_arg(struct arg *a)
|
||||
{
|
||||
static char *suffixes = "kmgt";
|
||||
|
||||
char *ptr;
|
||||
int i;
|
||||
long v = strtol(a->value, &ptr, 10);
|
||||
|
||||
if (ptr == a->value)
|
||||
return 0;
|
||||
|
||||
if (*ptr) {
|
||||
for (i = strlen(suffixes) - 1; i >= 0; i--)
|
||||
if (suffixes[i] == tolower((int) *ptr))
|
||||
break;
|
||||
|
||||
if (i < 0)
|
||||
return 0;
|
||||
|
||||
while (i-- > 0)
|
||||
v *= 1024;
|
||||
}
|
||||
|
||||
a->i_value = (int) v;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
int int_arg(struct arg *a)
|
||||
{
|
||||
char *ptr;
|
||||
long v = strtol(a->value, &ptr, 10);
|
||||
|
||||
if (ptr == a->value || *ptr)
|
||||
return 0;
|
||||
|
||||
a->i_value = (int) v;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int string_arg(struct arg *a)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int permission_arg(struct arg *a)
|
||||
{
|
||||
if ((!strcmp(a->value, "rw")) || (!strcmp(a->value, "wr")))
|
||||
a->i_value = LVM_READ | LVM_WRITE;
|
||||
|
||||
else if (!strcmp(a->value, "r"))
|
||||
a->i_value = LVM_READ;
|
||||
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char yes_no_prompt(const char *prompt, ...)
|
||||
{
|
||||
int c = 0;
|
||||
va_list ap;
|
||||
|
||||
while (c != 'y' && c != 'n') {
|
||||
if (c == '\n' || c == 0) {
|
||||
va_start(ap, prompt);
|
||||
vprintf(prompt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
c = tolower(getchar());
|
||||
}
|
||||
|
||||
while (getchar() != '\n')
|
||||
;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static void register_commands()
|
||||
{
|
||||
#define xx(a, b, c...) register_command(# a, a, b, ## c, \
|
||||
debug_ARG, help_ARG, suspend_ARG, \
|
||||
version_ARG, verbose_ARG, -1);
|
||||
#include "commands.h"
|
||||
#undef xx
|
||||
}
|
||||
|
||||
static void register_command(const char *name, command_fn fn,
|
||||
const char *desc, const char *usage, ...)
|
||||
{
|
||||
int nargs = 0, i;
|
||||
int *args;
|
||||
va_list ap;
|
||||
|
||||
/* count how many arguments we have */
|
||||
va_start(ap, usage);
|
||||
while (va_arg(ap, int) >= 0)
|
||||
nargs++;
|
||||
va_end(ap);
|
||||
|
||||
/* allocate space for them */
|
||||
if (!(args = dbg_malloc(sizeof (*args) * nargs))) {
|
||||
log_fatal("Out of memory.");
|
||||
exit(ECMD_FAILED);
|
||||
}
|
||||
|
||||
/* fill them in */
|
||||
va_start(ap, usage);
|
||||
for (i = 0; i < nargs; i++)
|
||||
args[i] = va_arg(ap, int);
|
||||
va_end(ap);
|
||||
|
||||
/* enter the command in the register */
|
||||
create_new_command(name, fn, desc, usage, nargs, args);
|
||||
}
|
||||
|
||||
static struct command *find_command(const char *name)
|
||||
{
|
||||
int i;
|
||||
char *namebase, *base;
|
||||
|
||||
namebase = strdup(name);
|
||||
base = basename(namebase);
|
||||
|
||||
for (i = 0; i < _num_commands; i++) {
|
||||
if (!strcmp(base, _commands[i].name))
|
||||
break;
|
||||
}
|
||||
|
||||
free(namebase);
|
||||
|
||||
if (i >= _num_commands)
|
||||
return 0;
|
||||
|
||||
return _commands + i;
|
||||
}
|
||||
|
||||
static void create_new_command(const char *name, command_fn command,
|
||||
const char *desc, const char *usage,
|
||||
int nargs, int *args)
|
||||
{
|
||||
struct command *nc;
|
||||
|
||||
alloc_command();
|
||||
|
||||
nc = _commands + _num_commands++;
|
||||
|
||||
nc->name = name;
|
||||
nc->desc = desc;
|
||||
nc->usage = usage;
|
||||
nc->fn = command;
|
||||
nc->num_args = nargs;
|
||||
nc->valid_args = args;
|
||||
}
|
||||
|
||||
static void __alloc(int size)
|
||||
{
|
||||
if (!(_commands = dbg_realloc(_commands, sizeof (*_commands) * size))) {
|
||||
log_fatal("Couldn't allocate memory.");
|
||||
exit(ECMD_FAILED);
|
||||
}
|
||||
|
||||
_array_size = size;
|
||||
}
|
||||
|
||||
static void alloc_command(void)
|
||||
{
|
||||
if (!_array_size)
|
||||
__alloc(32);
|
||||
|
||||
if (_array_size <= _num_commands)
|
||||
__alloc(2 * _array_size);
|
||||
}
|
||||
|
||||
static void add_getopt_arg(int arg, char **ptr, struct option **o)
|
||||
{
|
||||
struct arg *a = the_args + arg;
|
||||
|
||||
if (a->short_arg) {
|
||||
*(*ptr)++ = a->short_arg;
|
||||
|
||||
if (a->fn)
|
||||
*(*ptr)++ = ':';
|
||||
}
|
||||
|
||||
if (*(a->long_arg + 2)) {
|
||||
(*o)->name = a->long_arg + 2;
|
||||
(*o)->has_arg = a->fn ? 1 : 0;
|
||||
(*o)->flag = NULL;
|
||||
(*o)->val = a->short_arg ? a->short_arg : (int) a;
|
||||
(*o)++;
|
||||
}
|
||||
}
|
||||
|
||||
static int process_command_line(struct command *com, int *argc, char ***argv)
|
||||
{
|
||||
int i, opt;
|
||||
char str[((ARG_COUNT + 1) * 2) + 1], *ptr = str;
|
||||
struct option opts[ARG_COUNT + 1], *o = opts;
|
||||
struct arg *a;
|
||||
|
||||
for (i = 0; i < ARG_COUNT; i++) {
|
||||
struct arg *a = the_args + i;
|
||||
|
||||
/* zero the count and arg */
|
||||
a->count = 0;
|
||||
a->value = 0;
|
||||
a->i_value = 0;
|
||||
}
|
||||
|
||||
/* fill in the short and long opts */
|
||||
for (i = 0; i < com->num_args; i++)
|
||||
add_getopt_arg(com->valid_args[i], &ptr, &o);
|
||||
|
||||
*ptr = '\0';
|
||||
memset(o, 0, sizeof (*o));
|
||||
|
||||
/* initialise getopt_long & scan for command line switches */
|
||||
optarg = 0;
|
||||
optind = 0;
|
||||
while ((opt = getopt_long(*argc, *argv, str, opts, NULL)) >= 0) {
|
||||
|
||||
a = find_arg(com, opt);
|
||||
|
||||
if (!a) {
|
||||
log_fatal("Unrecognised option.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->fn) {
|
||||
if (!optarg) {
|
||||
log_error("Option requires argument.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
a->value = optarg;
|
||||
|
||||
if (!a->fn(a)) {
|
||||
log_error("Invalid argument %s", optarg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
a->count++;
|
||||
}
|
||||
|
||||
*argc -= optind;
|
||||
*argv += optind;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct arg *find_arg(struct command *com, int opt)
|
||||
{
|
||||
struct arg *a;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < com->num_args; i++) {
|
||||
a = the_args + com->valid_args[i];
|
||||
|
||||
if ((opt == a->short_arg) || (opt == (int) a))
|
||||
return a;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_common_commands(struct command *com)
|
||||
{
|
||||
int l;
|
||||
|
||||
if (arg_count(suspend_ARG))
|
||||
kill(getpid(), SIGSTOP);
|
||||
|
||||
l = arg_count(debug_ARG);
|
||||
init_debug(l ? l : _debug_level);
|
||||
|
||||
init_verbose(arg_count(verbose_ARG));
|
||||
|
||||
init_test(arg_count(test_ARG));
|
||||
|
||||
if (arg_count(help_ARG)) {
|
||||
usage(com->name);
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
if (arg_count(version_ARG)) {
|
||||
/* FIXME: Add driver and software version */
|
||||
log_error("%s: ", com->name);
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
/* Set autobackup if command takes this option */
|
||||
for (l = 0; l < com->num_args; l++)
|
||||
if (com->valid_args[l] == autobackup_ARG) {
|
||||
if (init_autobackup())
|
||||
return EINVALID_CMD_LINE;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int help(int argc, char **argv)
|
||||
{
|
||||
if (!argc)
|
||||
display_help();
|
||||
else {
|
||||
int i;
|
||||
for (i = 0; i < argc; i++)
|
||||
usage(argv[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void display_help()
|
||||
{
|
||||
int i;
|
||||
|
||||
log_error("Available lvm commands:");
|
||||
log_error("Use 'lvm help <command>' for more information");
|
||||
log_error(" ");
|
||||
|
||||
for (i = 0; i < _num_commands; i++) {
|
||||
struct command *com = _commands + i;
|
||||
|
||||
log_error("%-16.16s%s", com->name, com->desc);
|
||||
}
|
||||
}
|
||||
|
||||
static int run_command(int argc, char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
struct command *com;
|
||||
|
||||
if (!(com = find_command(argv[0])))
|
||||
return ENO_SUCH_CMD;
|
||||
|
||||
if (!process_command_line(com, &argc, &argv)) {
|
||||
log_error("Error during parsing of command line.");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if ((ret = process_common_commands(com)))
|
||||
return ret;
|
||||
|
||||
ret = com->fn(argc, argv);
|
||||
|
||||
/*
|
||||
* free off any memory the command used.
|
||||
*/
|
||||
pool_empty(ios->mem);
|
||||
|
||||
if (ret == EINVALID_CMD_LINE && !_interactive)
|
||||
usage(com->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int split(char *str, int *argc, char **argv, int max)
|
||||
{
|
||||
char *b = str, *e;
|
||||
*argc = 0;
|
||||
|
||||
while (*b) {
|
||||
while (*b && isspace(*b))
|
||||
b++;
|
||||
|
||||
if ((!*b) || (*b == '#'))
|
||||
break;
|
||||
|
||||
e = b;
|
||||
while (*e && !isspace(*e))
|
||||
e++;
|
||||
|
||||
argv[(*argc)++] = b;
|
||||
if (!*e)
|
||||
break;
|
||||
*e++ = '\0';
|
||||
b = e;
|
||||
if (*argc == max)
|
||||
break;
|
||||
}
|
||||
|
||||
return *argc;
|
||||
}
|
||||
|
||||
struct config_file *active_config_file(void)
|
||||
{
|
||||
return _cf;
|
||||
}
|
||||
|
||||
struct dev_filter *active_filter(void)
|
||||
{
|
||||
return _filter;
|
||||
}
|
||||
|
||||
static void __init_log(struct config_file *cf)
|
||||
{
|
||||
const char *log_file = find_config_str(cf->root, "log/file", '/', 0);
|
||||
|
||||
if (log_file) {
|
||||
/* set up the logging */
|
||||
if (!(_log = fopen(log_file, "w")))
|
||||
log_error("couldn't open log file %s\n", log_file);
|
||||
else
|
||||
init_log(_log);
|
||||
}
|
||||
|
||||
_debug_level = find_config_int(cf->root, "log/level", '/', 0);
|
||||
init_debug(_debug_level);
|
||||
}
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *e = getenv("LVM_CONFIG_FILE");
|
||||
struct stat info;
|
||||
struct pool *ios_pool;
|
||||
|
||||
/* FIXME: Override from config file */
|
||||
char *prefix = "/dev/";
|
||||
|
||||
if (!(_cf = create_config_file())) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Use LOG_USER for syslog messages by default */
|
||||
init_syslog(LOG_USER);
|
||||
|
||||
/* send log messages to stderr for now */
|
||||
init_log(stderr);
|
||||
|
||||
e = e ? e : "/etc/lvm/lvm.conf";
|
||||
if (stat(e, &info) != -1) {
|
||||
/* we've found a config file */
|
||||
if (!read_config(_cf, e)) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
__init_log(_cf);
|
||||
}
|
||||
|
||||
if (!dev_cache_init()) {
|
||||
stack;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dev_cache_add_dir("/dev")) {
|
||||
log_error("Failed to add %s to internal device cache", prefix);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(_filter = config_filter_create())) {
|
||||
/* Add scan & rejects from _cf->root */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(ios_pool = pool_create(4 * 1024))) {
|
||||
log_error("ios pool creation failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(ios = create_lvm1_format(prefix, ios_pool, _filter))) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __fin_commands(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _num_commands; i++)
|
||||
dbg_free(_commands[i].valid_args);
|
||||
|
||||
dbg_free(_commands);
|
||||
}
|
||||
|
||||
static void fin(void)
|
||||
{
|
||||
ios->destroy(ios);
|
||||
config_filter_destroy(_filter);
|
||||
dev_cache_exit();
|
||||
destroy_config_file(_cf);
|
||||
__fin_commands();
|
||||
dump_memory();
|
||||
fin_log();
|
||||
|
||||
if (_log)
|
||||
fclose(_log);
|
||||
}
|
||||
|
||||
static int run_script(int argc, char **argv)
|
||||
{
|
||||
FILE *script;
|
||||
|
||||
char buffer[CMD_LEN];
|
||||
int ret = 0;
|
||||
int magic_number = 0;
|
||||
|
||||
if ((script = fopen(argv[0], "r")) == NULL)
|
||||
return ENO_SUCH_CMD;
|
||||
|
||||
while (fgets(buffer, sizeof (buffer), script) != NULL) {
|
||||
if (!magic_number) {
|
||||
if (buffer[0] == '#' && buffer[1] == '!')
|
||||
magic_number = 1;
|
||||
else
|
||||
return ENO_SUCH_CMD;
|
||||
}
|
||||
if ((strlen(buffer) == sizeof (buffer) - 1)
|
||||
&& (buffer[sizeof (buffer) - 1] - 2 != '\n')) {
|
||||
buffer[50] = '\0';
|
||||
log_error("Line too long (max 255) beginning: %s",
|
||||
buffer);
|
||||
ret = EINVALID_CMD_LINE;
|
||||
break;
|
||||
}
|
||||
if (split(buffer, &argc, argv, MAX_ARGS) == MAX_ARGS) {
|
||||
buffer[50] = '\0';
|
||||
log_error("Too many arguments: %s", buffer);
|
||||
ret = EINVALID_CMD_LINE;
|
||||
break;
|
||||
}
|
||||
if (!argc)
|
||||
continue;
|
||||
if (!strcmp(argv[0], "quit"))
|
||||
break;
|
||||
run_command(argc, argv);
|
||||
}
|
||||
|
||||
fclose(script);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef READLINE_SUPPORT
|
||||
/* Custom completion function */
|
||||
static char **lvm_completion(char *text, int start_pos, int end_pos)
|
||||
{
|
||||
char **match_list = NULL;
|
||||
int p = 0;
|
||||
|
||||
while (isspace((int) *(rl_line_buffer + p)))
|
||||
p++;
|
||||
|
||||
/* First word should be one of our commands */
|
||||
if (start_pos == p)
|
||||
match_list = completion_matches(text, list_cmds);
|
||||
else if (*text == '-')
|
||||
match_list = completion_matches(text, list_args);
|
||||
/* else other args */
|
||||
|
||||
/* No further completion */
|
||||
rl_attempted_completion_over = 1;
|
||||
return match_list;
|
||||
}
|
||||
|
||||
/* List matching commands */
|
||||
static char *list_cmds(char *text, int state)
|
||||
{
|
||||
static int i = 0;
|
||||
static int len = 0;
|
||||
|
||||
/* Initialise if this is a new completion attempt */
|
||||
if (!state) {
|
||||
i = 0;
|
||||
len = strlen(text);
|
||||
}
|
||||
|
||||
while (i < _num_commands)
|
||||
if (!strncmp(text, _commands[i++].name, len))
|
||||
return strdup(_commands[i - 1].name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* List matching arguments */
|
||||
static char *list_args(char *text, int state)
|
||||
{
|
||||
static int match_no = 0;
|
||||
static int len = 0;
|
||||
static struct command *com;
|
||||
|
||||
/* Initialise if this is a new completion attempt */
|
||||
if (!state) {
|
||||
char *s = rl_line_buffer;
|
||||
int j = 0;
|
||||
|
||||
match_no = 0;
|
||||
com = NULL;
|
||||
len = strlen(text);
|
||||
|
||||
/* Find start of first word in line buffer */
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
|
||||
/* Look for word in list of commands */
|
||||
for (j = 0; j < _num_commands; j++) {
|
||||
char *p;
|
||||
char *q = s;
|
||||
|
||||
p = (char *) _commands[j].name;
|
||||
while (*p == *q) {
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if ((!*p) && *q == ' ') {
|
||||
com = _commands + j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!com)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Short form arguments */
|
||||
if (len < 3) {
|
||||
while (match_no < com->num_args) {
|
||||
char s[3];
|
||||
char c;
|
||||
if (!(c = (the_args +
|
||||
com->valid_args[match_no++])->short_arg))
|
||||
continue;
|
||||
|
||||
sprintf(s, "-%c", c);
|
||||
if (!strncmp(text, s, len))
|
||||
return strdup(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Long form arguments */
|
||||
if (match_no < com->num_args)
|
||||
match_no = com->num_args;
|
||||
|
||||
while (match_no - com->num_args < com->num_args) {
|
||||
char *l;
|
||||
l = (the_args +
|
||||
com->valid_args[match_no++ - com->num_args])->long_arg;
|
||||
if (!strncmp(text, l, len))
|
||||
return strdup(l);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int shell(void)
|
||||
{
|
||||
int argc, ret;
|
||||
char *input = NULL, *args[MAX_ARGS], **argv;
|
||||
|
||||
rl_readline_name = "lvm";
|
||||
rl_attempted_completion_function = (CPPFunction *) lvm_completion;
|
||||
|
||||
_interactive = 1;
|
||||
while (1) {
|
||||
free(input);
|
||||
input = readline("lvm> ");
|
||||
|
||||
/* EOF */
|
||||
if (!input) {
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* empty line */
|
||||
if (!*input)
|
||||
continue;
|
||||
|
||||
add_history(input);
|
||||
|
||||
argv = args;
|
||||
|
||||
if (split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
|
||||
log_error("Too many arguments, sorry.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "lvm")) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (!argc)
|
||||
continue;
|
||||
|
||||
if (!strcmp(argv[0], "quit")) {
|
||||
log_error("Exiting.");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = run_command(argc, argv);
|
||||
if (ret == ENO_SUCH_CMD)
|
||||
log_error("No such command '%s'. Try 'help'.",
|
||||
argv[0]);
|
||||
}
|
||||
|
||||
free(input);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
27
tools/lvmchange.c
Normal file
27
tools/lvmchange.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* LVM 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.
|
||||
*
|
||||
* LVM 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 LVM; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
int lvmchange(int argc, char **argv)
|
||||
{
|
||||
log_print("With the device mapper, this program is obsolete.");
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user