mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
New APIs: lvm-set-filter and lvm-clear-filter.
These APIs allow you to change the device filter, the list of block devices that LVM "sees". Either you can set it to a fixed list of devices / partitions, or you can clear it so that LVM sees everything.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -201,6 +201,7 @@ python/guestfs-py.c
|
||||
python/guestfs.pyc
|
||||
regressions/rhbz501893
|
||||
regressions/test1.img
|
||||
regressions/test2.img
|
||||
regressions/test.err
|
||||
regressions/test.out
|
||||
ruby/bindtests.rb
|
||||
|
||||
@@ -99,6 +99,7 @@ guestfsd_SOURCES = \
|
||||
link.c \
|
||||
ls.c \
|
||||
lvm.c \
|
||||
lvm-filter.c \
|
||||
mkfs.c \
|
||||
mknod.c \
|
||||
modprobe.c \
|
||||
|
||||
244
daemon/lvm-filter.c
Normal file
244
daemon/lvm-filter.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/* libguestfs - the guestfsd daemon
|
||||
* Copyright (C) 2010 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "c-ctype.h"
|
||||
#include "actions.h"
|
||||
|
||||
/* Does the current line match the regexp /^\s*filter\s*=/ */
|
||||
static int
|
||||
is_filter_line (const char *line)
|
||||
{
|
||||
while (*line && c_isspace (*line))
|
||||
line++;
|
||||
if (!*line)
|
||||
return 0;
|
||||
|
||||
if (! STRPREFIX (line, "filter"))
|
||||
return 0;
|
||||
line += 6;
|
||||
|
||||
while (*line && c_isspace (*line))
|
||||
line++;
|
||||
if (!*line)
|
||||
return 0;
|
||||
|
||||
if (*line != '=')
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Rewrite the 'filter = [ ... ]' line in /etc/lvm/lvm.conf. */
|
||||
static int
|
||||
set_filter (const char *filter)
|
||||
{
|
||||
if (verbose)
|
||||
fprintf (stderr, "LVM: setting device filter to %s\n", filter);
|
||||
|
||||
FILE *ifp = fopen ("/etc/lvm/lvm.conf", "r");
|
||||
if (ifp == NULL) {
|
||||
reply_with_perror ("open: /etc/lvm/lvm.conf");
|
||||
return -1;
|
||||
}
|
||||
FILE *ofp = fopen ("/etc/lvm/lvm.conf.new", "w");
|
||||
if (ofp == NULL) {
|
||||
reply_with_perror ("open: /etc/lvm/lvm.conf.new");
|
||||
fclose (ifp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
while (getline (&line, &len, ifp) != -1) {
|
||||
int r;
|
||||
if (is_filter_line (line)) {
|
||||
if (verbose)
|
||||
fprintf (stderr, "LVM: replacing config line:\n%s", line);
|
||||
r = fprintf (ofp, " filter = [ %s ]\n", filter);
|
||||
} else {
|
||||
r = fprintf (ofp, "%s", line);
|
||||
}
|
||||
if (r < 0) {
|
||||
/* NB. fprintf doesn't set errno on error. */
|
||||
reply_with_error ("/etc/lvm/lvm.conf.new: write failed");
|
||||
fclose (ifp);
|
||||
fclose (ofp);
|
||||
free (line);
|
||||
unlink ("/etc/lvm/lvm.conf.new");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
free (line);
|
||||
|
||||
if (fclose (ifp) == EOF) {
|
||||
reply_with_perror ("/etc/lvm/lvm.conf.new");
|
||||
unlink ("/etc/lvm/lvm.conf.new");
|
||||
fclose (ofp);
|
||||
return -1;
|
||||
}
|
||||
if (fclose (ofp) == EOF) {
|
||||
reply_with_perror ("/etc/lvm/lvm.conf.new");
|
||||
unlink ("/etc/lvm/lvm.conf.new");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rename ("/etc/lvm/lvm.conf.new", "/etc/lvm/lvm.conf") == -1) {
|
||||
reply_with_perror ("rename: /etc/lvm/lvm.conf");
|
||||
unlink ("/etc/lvm/lvm.conf.new");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vgchange (const char *vgchange_flag)
|
||||
{
|
||||
char *err;
|
||||
int r = command (NULL, &err, "lvm", "vgchange", vgchange_flag, NULL);
|
||||
if (r == -1) {
|
||||
reply_with_error ("vgscan: %s", err);
|
||||
free (err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free (err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deactivate all VGs. */
|
||||
static int
|
||||
deactivate (void)
|
||||
{
|
||||
return vgchange ("-an");
|
||||
}
|
||||
|
||||
/* Reactivate all VGs. */
|
||||
static int
|
||||
reactivate (void)
|
||||
{
|
||||
return vgchange ("-ay");
|
||||
}
|
||||
|
||||
/* Clear the cache and rescan. */
|
||||
static int
|
||||
rescan (void)
|
||||
{
|
||||
unlink ("/etc/lvm/cache/.cache");
|
||||
|
||||
char *err;
|
||||
int r = command (NULL, &err, "lvm", "vgscan", NULL);
|
||||
if (r == -1) {
|
||||
reply_with_error ("vgscan: %s", err);
|
||||
free (err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free (err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Construct the new, specific filter string. We can assume that
|
||||
* the 'devices' array does not contain any regexp metachars,
|
||||
* because it's already been checked by the stub code.
|
||||
*/
|
||||
static char *
|
||||
make_filter_string (char *const *devices)
|
||||
{
|
||||
size_t i;
|
||||
size_t len = 64;
|
||||
for (i = 0; devices[i] != NULL; ++i)
|
||||
len += strlen (devices[i]) + 16;
|
||||
|
||||
char *filter = malloc (len);
|
||||
if (filter == NULL) {
|
||||
reply_with_perror ("malloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *p = filter;
|
||||
for (i = 0; devices[i] != NULL; ++i) {
|
||||
/* Because of the way matching works in LVM, each match clause
|
||||
* should be either:
|
||||
* "a|^/dev/sda|", for whole block devices, or
|
||||
* "a|^/dev/sda1$|", for single partitions
|
||||
* (the assumption being we have <= 26 block devices XXX).
|
||||
*/
|
||||
size_t slen = strlen (devices[i]);
|
||||
char str[slen+16];
|
||||
|
||||
if (c_isdigit (devices[i][slen-1]))
|
||||
snprintf (str, slen+16, "\"a|^%s$|\", ", devices[i]);
|
||||
else
|
||||
snprintf (str, slen+16, "\"a|^%s|\", ", devices[i]);
|
||||
|
||||
strcpy (p, str);
|
||||
p += strlen (str);
|
||||
}
|
||||
strcpy (p, "\"r|.*|\"");
|
||||
|
||||
return filter; /* Caller must free. */
|
||||
}
|
||||
|
||||
int
|
||||
do_lvm_set_filter (char *const *devices)
|
||||
{
|
||||
char *filter = make_filter_string (devices);
|
||||
if (filter == NULL)
|
||||
return -1;
|
||||
|
||||
if (deactivate () == -1) {
|
||||
free (filter);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int r = set_filter (filter);
|
||||
free (filter);
|
||||
if (r == -1)
|
||||
return -1;
|
||||
|
||||
if (rescan () == -1)
|
||||
return -1;
|
||||
|
||||
return reactivate ();
|
||||
}
|
||||
|
||||
int
|
||||
do_lvm_clear_filter (void)
|
||||
{
|
||||
if (deactivate () == -1)
|
||||
return -1;
|
||||
|
||||
if (set_filter ("\"a/.*/\"") == -1)
|
||||
return -1;
|
||||
|
||||
if (rescan () == -1)
|
||||
return -1;
|
||||
|
||||
return reactivate ();
|
||||
}
|
||||
@@ -33,6 +33,7 @@ daemon/initrd.c
|
||||
daemon/inotify.c
|
||||
daemon/link.c
|
||||
daemon/ls.c
|
||||
daemon/lvm-filter.c
|
||||
daemon/lvm.c
|
||||
daemon/mkfs.c
|
||||
daemon/mknod.c
|
||||
|
||||
@@ -34,6 +34,7 @@ TESTS = \
|
||||
test-cancellation-download-librarycancels.sh \
|
||||
test-cancellation-upload-daemoncancels.sh \
|
||||
test-find0.sh \
|
||||
test-lvm-filtering.sh \
|
||||
test-lvm-mapping.pl \
|
||||
test-noexec-stack.pl \
|
||||
test-qemudie-killsub.sh \
|
||||
|
||||
92
regressions/test-lvm-filtering.sh
Executable file
92
regressions/test-lvm-filtering.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs
|
||||
# Copyright (C) 2010 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
# Test LVM device filtering.
|
||||
|
||||
set -e
|
||||
|
||||
rm -f test1.img test2.img
|
||||
|
||||
actual=$(../fish/guestfish <<'EOF'
|
||||
sparse test1.img 1G
|
||||
sparse test2.img 1G
|
||||
|
||||
run
|
||||
|
||||
part-disk /dev/sda mbr
|
||||
part-disk /dev/sdb mbr
|
||||
|
||||
pvcreate /dev/sda1
|
||||
pvcreate /dev/sdb1
|
||||
|
||||
vgcreate VG1 /dev/sda1
|
||||
vgcreate VG2 /dev/sdb1
|
||||
|
||||
# Should see VG1 and VG2
|
||||
vgs
|
||||
|
||||
# Should see just VG1
|
||||
lvm-set-filter /dev/sda
|
||||
vgs
|
||||
lvm-set-filter /dev/sda1
|
||||
vgs
|
||||
|
||||
# Should see just VG2
|
||||
lvm-set-filter /dev/sdb
|
||||
vgs
|
||||
lvm-set-filter /dev/sdb1
|
||||
vgs
|
||||
|
||||
# Should see VG1 and VG2
|
||||
lvm-set-filter "/dev/sda /dev/sdb"
|
||||
vgs
|
||||
lvm-set-filter "/dev/sda1 /dev/sdb1"
|
||||
vgs
|
||||
lvm-set-filter "/dev/sda /dev/sdb1"
|
||||
vgs
|
||||
lvm-set-filter "/dev/sda1 /dev/sdb"
|
||||
vgs
|
||||
lvm-clear-filter
|
||||
vgs
|
||||
EOF
|
||||
)
|
||||
|
||||
expected="VG1
|
||||
VG2
|
||||
VG1
|
||||
VG1
|
||||
VG2
|
||||
VG2
|
||||
VG1
|
||||
VG2
|
||||
VG1
|
||||
VG2
|
||||
VG1
|
||||
VG2
|
||||
VG1
|
||||
VG2
|
||||
VG1
|
||||
VG2"
|
||||
|
||||
rm -f test1.img test2.img
|
||||
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
echo "LVM filter test failed. Actual output was:"
|
||||
echo "$actual"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1 +1 @@
|
||||
254
|
||||
256
|
||||
|
||||
@@ -4832,6 +4832,47 @@ C<device>.
|
||||
|
||||
If the filesystem does not have a UUID, this returns the empty string.");
|
||||
|
||||
("lvm_set_filter", (RErr, [DeviceList "devices"]), 255, [Optional "lvm2"],
|
||||
(* Can't be tested with the current framework because
|
||||
* the VG is being used by the mounted filesystem, so
|
||||
* the vgchange -an command we do first will fail.
|
||||
*)
|
||||
[],
|
||||
"set LVM device filter",
|
||||
"\
|
||||
This sets the LVM device filter so that LVM will only be
|
||||
able to \"see\" the block devices in the list C<devices>,
|
||||
and will ignore all other attached block devices.
|
||||
|
||||
Where disk image(s) contain duplicate PVs or VGs, this
|
||||
command is useful to get LVM to ignore the duplicates, otherwise
|
||||
LVM can get confused. Note also there are two types
|
||||
of duplication possible: either cloned PVs/VGs which have
|
||||
identical UUIDs; or VGs that are not cloned but just happen
|
||||
to have the same name. In normal operation you cannot
|
||||
create this situation, but you can do it outside LVM, eg.
|
||||
by cloning disk images or by bit twiddling inside the LVM
|
||||
metadata.
|
||||
|
||||
This command also clears the LVM cache and performs a volume
|
||||
group scan.
|
||||
|
||||
You can filter whole block devices or individual partitions.
|
||||
|
||||
You cannot use this if any VG is currently in use (eg.
|
||||
contains a mounted filesystem), even if you are not
|
||||
filtering out that VG.");
|
||||
|
||||
("lvm_clear_filter", (RErr, []), 256, [],
|
||||
[], (* see note on lvm_set_filter *)
|
||||
"clear LVM device filter",
|
||||
"\
|
||||
This undoes the effect of C<guestfs_lvm_set_filter>. LVM
|
||||
will be able to see every block device.
|
||||
|
||||
This command also clears the LVM cache and performs a volume
|
||||
group scan.");
|
||||
|
||||
]
|
||||
|
||||
let all_functions = non_daemon_functions @ daemon_functions
|
||||
|
||||
Reference in New Issue
Block a user