mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
25b733809a
Lots of changes/very little testing so far => there'll be bugs! Use 'vgcreate -M text' to create a volume group with its metadata stored in text files. Text format metadata changes should be reasonably atomic, with a (basic) automatic recovery mechanism if the system crashes while a change is in progress. Add a metadata section to lvm.conf to specify multiple directories if you want (recommended) to keep multiple copies of the metadata (eg on different filesystems). e.g. metadata { dirs = ["/etc/lvm/metadata1","/usr/local/lvm/metadata2"] } Plenty of refinements still in the pipeline.
296 lines
5.8 KiB
C
296 lines
5.8 KiB
C
/*
|
|
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
|
*
|
|
* This file is released under the LGPL.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "vgcache.h"
|
|
#include "hash.h"
|
|
#include "dbg_malloc.h"
|
|
#include "log.h"
|
|
#include "uuid.h"
|
|
#include "toolcontext.h"
|
|
|
|
static struct hash_table *_vghash;
|
|
static struct hash_table *_vgidhash;
|
|
static struct hash_table *_pvhash;
|
|
|
|
const char *all_devices = "\0";
|
|
|
|
int vgcache_init()
|
|
{
|
|
if (!(_vghash = hash_create(128)))
|
|
return 0;
|
|
|
|
if (!(_vgidhash = hash_create(128)))
|
|
return 0;
|
|
|
|
if (!(_pvhash = hash_create(128)))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* A vg_name of NULL returns all_devices */
|
|
struct list *vgcache_find(const char *vg_name)
|
|
{
|
|
struct vgname_entry *vgn;
|
|
|
|
if (!_vghash)
|
|
return NULL;
|
|
|
|
if (!vg_name)
|
|
vg_name = all_devices;
|
|
|
|
if (!(vgn = hash_lookup(_vghash, vg_name)))
|
|
return NULL;
|
|
|
|
return &vgn->pvdevs;
|
|
}
|
|
|
|
struct format_type *vgcache_find_format(const char *vg_name)
|
|
{
|
|
struct vgname_entry *vgn;
|
|
|
|
if (!_vghash)
|
|
return NULL;
|
|
|
|
if (!vg_name)
|
|
vg_name = all_devices;
|
|
|
|
if (!(vgn = hash_lookup(_vghash, vg_name)))
|
|
return NULL;
|
|
|
|
return vgn->fmt;
|
|
}
|
|
|
|
struct list *vgcache_find_by_vgid(const char *vgid)
|
|
{
|
|
struct vgname_entry *vgn;
|
|
char vgid_s[ID_LEN + 1];
|
|
|
|
if (!_vgidhash || !vgid)
|
|
return NULL;
|
|
|
|
memcpy(vgid_s, vgid, ID_LEN);
|
|
vgid_s[ID_LEN] = '\0';
|
|
|
|
if (!(vgn = hash_lookup(_vgidhash, vgid_s)))
|
|
return NULL;
|
|
|
|
return &vgn->pvdevs;
|
|
}
|
|
|
|
void vgcache_del_orphan(struct device *dev)
|
|
{
|
|
struct pvdev_list *pvdev;
|
|
|
|
if (_pvhash && ((pvdev = hash_lookup(_pvhash, dev_name(dev))))) {
|
|
list_del(&pvdev->list);
|
|
hash_remove(_pvhash, dev_name(pvdev->dev));
|
|
dbg_free(pvdev);
|
|
}
|
|
}
|
|
|
|
int vgcache_add_entry(const char *vg_name, const char *vgid, struct device *dev,
|
|
struct format_type *fmt)
|
|
{
|
|
const char *pv_name;
|
|
struct vgname_entry *vgn;
|
|
struct pvdev_list *pvdev;
|
|
struct list *pvdh, *pvdevs;
|
|
|
|
if (!(pvdevs = vgcache_find(vg_name))) {
|
|
if (!(vgn = dbg_malloc(sizeof(struct vgname_entry)))) {
|
|
log_error("struct vgname_entry allocation failed");
|
|
return 0;
|
|
}
|
|
memset(vgn, 0, sizeof(struct vgname_entry));
|
|
|
|
vgn->fmt = fmt;
|
|
pvdevs = &vgn->pvdevs;
|
|
list_init(pvdevs);
|
|
|
|
if (!(vgn->vgname = dbg_strdup(vg_name))) {
|
|
log_error("vgcache_add: strdup vg_name failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!hash_insert(_vghash, vg_name, vgn)) {
|
|
log_error("vgcache_add: VG hash insertion failed");
|
|
return 0;
|
|
}
|
|
} else if (!(vgn = hash_lookup(_vghash, vg_name))) {
|
|
log_error("vgcache_add: VG name entry %s not found", vg_name);
|
|
return 0;
|
|
}
|
|
|
|
if (vgid && strncmp(vgid, vgn->vgid, ID_LEN)) {
|
|
hash_remove(_vgidhash, vgn->vgid);
|
|
|
|
memcpy(vgn->vgid, vgid, ID_LEN);
|
|
vgn->vgid[ID_LEN] = '\0';
|
|
|
|
if (!hash_insert(_vgidhash, vgn->vgid, vgn)) {
|
|
log_error("vgcache_add: vgid hash insertion " "failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!dev)
|
|
return 1;
|
|
|
|
list_iterate(pvdh, pvdevs) {
|
|
pvdev = list_item(pvdh, struct pvdev_list);
|
|
if (dev == pvdev->dev)
|
|
return 1;
|
|
}
|
|
|
|
/* Remove PV from any existing VG unless an all_devices request */
|
|
pvdev = NULL;
|
|
pv_name = dev_name(dev);
|
|
if (*vg_name && _pvhash && ((pvdev = hash_lookup(_pvhash, pv_name)))) {
|
|
list_del(&pvdev->list);
|
|
hash_remove(_pvhash, dev_name(pvdev->dev));
|
|
}
|
|
|
|
/* Allocate new pvdev_list if there isn't an existing one to reuse */
|
|
if (!pvdev && !(pvdev = dbg_malloc(sizeof(struct pvdev_list)))) {
|
|
log_error("struct pvdev_list allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
pvdev->dev = dev;
|
|
list_add(pvdevs, &pvdev->list);
|
|
|
|
if (*vg_name && _pvhash && !hash_insert(_pvhash, pv_name, pvdev)) {
|
|
log_error("vgcache_add: PV hash insertion for %s "
|
|
"failed", pv_name);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* vg_name of "\0" is an orphan PV; NULL means only add to all_devices */
|
|
int vgcache_add(const char *vg_name, const char *vgid, struct device *dev,
|
|
struct format_type *fmt)
|
|
{
|
|
if (!_vghash && !vgcache_init())
|
|
return 0;
|
|
|
|
/* If orphan PV remove it */
|
|
if (dev && vg_name && !*vg_name)
|
|
vgcache_del_orphan(dev);
|
|
|
|
/* Add PV if vg_name supplied */
|
|
if (vg_name && *vg_name && !vgcache_add_entry(vg_name, vgid, dev, fmt))
|
|
return 0;
|
|
|
|
/* Add to all_devices */
|
|
if (dev)
|
|
return vgcache_add_entry(all_devices, NULL, dev, fmt);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void vgcache_destroy_entry(struct vgname_entry *vgn)
|
|
{
|
|
struct list *pvdh;
|
|
struct pvdev_list *pvdev;
|
|
|
|
if (vgn) {
|
|
pvdh = vgn->pvdevs.n;
|
|
while (pvdh != &vgn->pvdevs) {
|
|
pvdev = list_item(pvdh, struct pvdev_list);
|
|
pvdh = pvdh->n;
|
|
if (_pvhash)
|
|
hash_remove(_pvhash, dev_name(pvdev->dev));
|
|
dbg_free(pvdev);
|
|
}
|
|
dbg_free(vgn->vgname);
|
|
if (_vgidhash && vgn->vgid[0])
|
|
hash_remove(_vgidhash, vgn->vgid);
|
|
}
|
|
dbg_free(vgn);
|
|
}
|
|
|
|
void vgcache_del(const char *vg_name)
|
|
{
|
|
struct vgname_entry *vgn;
|
|
|
|
if (!_vghash)
|
|
return;
|
|
|
|
if (!vg_name)
|
|
vg_name = all_devices;
|
|
|
|
if (!(vgn = hash_lookup(_vghash, vg_name)))
|
|
return;
|
|
|
|
hash_remove(_vghash, vg_name);
|
|
if (vgn->vgid[0])
|
|
hash_remove(_vgidhash, vgn->vgid);
|
|
|
|
vgcache_destroy_entry(vgn);
|
|
}
|
|
|
|
void vgcache_del_by_vgid(const char *vgid)
|
|
{
|
|
struct vgname_entry *vgn;
|
|
char vgid_s[ID_LEN + 1];
|
|
|
|
if (!_vgidhash || !vgid)
|
|
return;
|
|
|
|
memcpy(vgid_s, vgid, ID_LEN);
|
|
vgid_s[ID_LEN] = '\0';
|
|
|
|
if (!(vgn = hash_lookup(_vghash, vgid_s)))
|
|
return;
|
|
|
|
hash_remove(_vgidhash, vgn->vgid);
|
|
if (vgn->vgname[0])
|
|
hash_remove(_vghash, vgn->vgname);
|
|
|
|
vgcache_destroy_entry(vgn);
|
|
}
|
|
|
|
void vgcache_destroy()
|
|
{
|
|
if (_vghash) {
|
|
hash_iter(_vghash, (iterate_fn) vgcache_destroy_entry);
|
|
hash_destroy(_vghash);
|
|
_vghash = NULL;
|
|
}
|
|
|
|
if (_vgidhash) {
|
|
hash_destroy(_vgidhash);
|
|
_vgidhash = NULL;
|
|
}
|
|
|
|
if (_pvhash) {
|
|
hash_destroy(_pvhash);
|
|
_pvhash = NULL;
|
|
}
|
|
}
|
|
|
|
char *vgname_from_vgid(struct cmd_context *cmd, struct id *vgid)
|
|
{
|
|
struct vgname_entry *vgn;
|
|
char vgid_s[ID_LEN + 1];
|
|
|
|
if (!_vgidhash || !vgid)
|
|
return NULL;
|
|
|
|
memcpy(vgid_s, vgid->uuid, ID_LEN);
|
|
vgid_s[ID_LEN] = '\0';
|
|
|
|
if (!(vgn = hash_lookup(_vgidhash, vgid_s)))
|
|
return NULL;
|
|
|
|
return pool_strdup(cmd->mem, vgn->vgname);
|
|
}
|