mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
Code to build and display device dependency tree.
This commit is contained in:
parent
72a444276f
commit
3d0480ed27
@ -1,6 +1,8 @@
|
||||
Version 1.01.06 -
|
||||
Version 1.02.00 -
|
||||
=============================
|
||||
Add dmsetup --nolockfs support for suspend/reload.
|
||||
Added dependency tree functions to library.
|
||||
Added ls --tree to dmsetup.
|
||||
Added dmsetup --nolockfs support for suspend/reload.
|
||||
|
||||
Version 1.01.05 - 26 Sep 2005
|
||||
=============================
|
||||
|
@ -29,3 +29,15 @@ dm_dir
|
||||
dm_format_dev
|
||||
dm_lib_release
|
||||
dm_lib_exit
|
||||
dm_deptree_create
|
||||
dm_deptree_free
|
||||
dm_deptree_add_dev
|
||||
dm_deptree_node_get_name
|
||||
dm_deptree_node_get_uuid
|
||||
dm_deptree_node_get_info
|
||||
dm_deptree_node_num_children
|
||||
dm_deptree_node_num_parents
|
||||
dm_deptree_find_node
|
||||
dm_deptree_next_child
|
||||
dm_deptree_next_parent
|
||||
dm_is_dm_major
|
||||
|
@ -25,7 +25,15 @@ ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS += event
|
||||
endif
|
||||
|
||||
SOURCES = libdm-common.c libdm-file.c $(interface)/libdm-iface.c
|
||||
SOURCES =\
|
||||
datastruct/bitset.c \
|
||||
datastruct/hash.c \
|
||||
libdm-common.c \
|
||||
libdm-file.c \
|
||||
libdm-deptree.c \
|
||||
mm/dbg_malloc.c \
|
||||
mm/pool.c \
|
||||
$(interface)/libdm-iface.c
|
||||
|
||||
INCLUDES = -I$(interface)
|
||||
|
||||
|
95
libdm/datastruct/bitset.c
Normal file
95
libdm/datastruct/bitset.c
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "bitset.h"
|
||||
|
||||
/* FIXME: calculate this. */
|
||||
#define INT_SHIFT 5
|
||||
|
||||
bitset_t bitset_create(struct pool *mem, unsigned num_bits)
|
||||
{
|
||||
unsigned n = (num_bits / BITS_PER_INT) + 2;
|
||||
size_t size = sizeof(int) * n;
|
||||
bitset_t bs;
|
||||
|
||||
if (mem)
|
||||
bs = pool_zalloc(mem, size);
|
||||
else
|
||||
bs = dbg_malloc(size);
|
||||
|
||||
if (!bs)
|
||||
return NULL;
|
||||
|
||||
*bs = num_bits;
|
||||
|
||||
if (!mem)
|
||||
bit_clear_all(bs);
|
||||
|
||||
return bs;
|
||||
}
|
||||
|
||||
void bitset_destroy(bitset_t bs)
|
||||
{
|
||||
dbg_free(bs);
|
||||
}
|
||||
|
||||
void bit_union(bitset_t out, bitset_t in1, bitset_t in2)
|
||||
{
|
||||
int i;
|
||||
for (i = (in1[0] / BITS_PER_INT) + 1; i; i--)
|
||||
out[i] = in1[i] | in2[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: slow
|
||||
*/
|
||||
static inline int _test_word(uint32_t test, int bit)
|
||||
{
|
||||
while (bit < BITS_PER_INT) {
|
||||
if (test & (0x1 << bit))
|
||||
return bit;
|
||||
bit++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bit_get_next(bitset_t bs, int last_bit)
|
||||
{
|
||||
int bit, word;
|
||||
uint32_t test;
|
||||
|
||||
last_bit++; /* otherwise we'll return the same bit again */
|
||||
|
||||
while (last_bit < bs[0]) {
|
||||
word = last_bit >> INT_SHIFT;
|
||||
test = bs[word + 1];
|
||||
bit = last_bit & (BITS_PER_INT - 1);
|
||||
|
||||
if ((bit = _test_word(test, bit)) >= 0)
|
||||
return (word * BITS_PER_INT) + bit;
|
||||
|
||||
last_bit = last_bit - (last_bit & (BITS_PER_INT - 1)) +
|
||||
BITS_PER_INT;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bit_get_first(bitset_t bs)
|
||||
{
|
||||
return bit_get_next(bs, -1);
|
||||
}
|
52
libdm/datastruct/bitset.h
Normal file
52
libdm/datastruct/bitset.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_BITSET_H
|
||||
#define _LVM_BITSET_H
|
||||
|
||||
#include "pool.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
typedef uint32_t *bitset_t;
|
||||
|
||||
bitset_t bitset_create(struct pool *mem, unsigned num_bits);
|
||||
void bitset_destroy(bitset_t bs);
|
||||
|
||||
void bit_union(bitset_t out, bitset_t in1, bitset_t in2);
|
||||
int bit_get_first(bitset_t bs);
|
||||
int bit_get_next(bitset_t bs, int last_bit);
|
||||
|
||||
#define BITS_PER_INT (sizeof(int) * CHAR_BIT)
|
||||
|
||||
#define bit(bs, i) \
|
||||
(bs[(i / BITS_PER_INT) + 1] & (0x1 << (i & (BITS_PER_INT - 1))))
|
||||
|
||||
#define bit_set(bs, i) \
|
||||
(bs[(i / BITS_PER_INT) + 1] |= (0x1 << (i & (BITS_PER_INT - 1))))
|
||||
|
||||
#define bit_clear(bs, i) \
|
||||
(bs[(i / BITS_PER_INT) + 1] &= ~(0x1 << (i & (BITS_PER_INT - 1))))
|
||||
|
||||
#define bit_set_all(bs) \
|
||||
memset(bs + 1, -1, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
|
||||
|
||||
#define bit_clear_all(bs) \
|
||||
memset(bs + 1, 0, ((*bs / BITS_PER_INT) + 1) * sizeof(int))
|
||||
|
||||
#define bit_copy(bs1, bs2) \
|
||||
memcpy(bs1 + 1, bs2 + 1, ((*bs1 / BITS_PER_INT) + 1) * sizeof(int))
|
||||
|
||||
#endif
|
262
libdm/datastruct/hash.c
Normal file
262
libdm/datastruct/hash.c
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "hash.h"
|
||||
|
||||
struct hash_node {
|
||||
struct hash_node *next;
|
||||
void *data;
|
||||
int keylen;
|
||||
char key[0];
|
||||
};
|
||||
|
||||
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, int len)
|
||||
{
|
||||
struct hash_node *n = dbg_malloc(sizeof(*n) + len);
|
||||
|
||||
if (n) {
|
||||
memcpy(n->key, str, len);
|
||||
n->keylen = len;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned _hash(const char *str, uint32_t len)
|
||||
{
|
||||
unsigned long h = 0, g, i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
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;
|
||||
}
|
||||
|
||||
static void _free_nodes(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);
|
||||
}
|
||||
}
|
||||
|
||||
void hash_destroy(struct hash_table *t)
|
||||
{
|
||||
_free_nodes(t);
|
||||
dbg_free(t->slots);
|
||||
dbg_free(t);
|
||||
}
|
||||
|
||||
static inline struct hash_node **_find(struct hash_table *t, const char *key,
|
||||
uint32_t len)
|
||||
{
|
||||
unsigned h = _hash(key, len) & (t->num_slots - 1);
|
||||
struct hash_node **c;
|
||||
|
||||
for (c = &t->slots[h]; *c; c = &((*c)->next))
|
||||
if (!memcmp(key, (*c)->key, len))
|
||||
break;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void *hash_lookup_binary(struct hash_table *t, const char *key,
|
||||
uint32_t len)
|
||||
{
|
||||
struct hash_node **c = _find(t, key, len);
|
||||
return *c ? (*c)->data : 0;
|
||||
}
|
||||
|
||||
int hash_insert_binary(struct hash_table *t, const char *key,
|
||||
uint32_t len, void *data)
|
||||
{
|
||||
struct hash_node **c = _find(t, key, len);
|
||||
|
||||
if (*c)
|
||||
(*c)->data = data;
|
||||
else {
|
||||
struct hash_node *n = _create_node(key, len);
|
||||
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
n->data = data;
|
||||
n->next = 0;
|
||||
*c = n;
|
||||
t->num_nodes++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void hash_remove_binary(struct hash_table *t, const char *key,
|
||||
uint32_t len)
|
||||
{
|
||||
struct hash_node **c = _find(t, key, len);
|
||||
|
||||
if (*c) {
|
||||
struct hash_node *old = *c;
|
||||
*c = (*c)->next;
|
||||
dbg_free(old);
|
||||
t->num_nodes--;
|
||||
}
|
||||
}
|
||||
|
||||
void *hash_lookup(struct hash_table *t, const char *key)
|
||||
{
|
||||
return hash_lookup_binary(t, key, strlen(key) + 1);
|
||||
}
|
||||
|
||||
int hash_insert(struct hash_table *t, const char *key, void *data)
|
||||
{
|
||||
return hash_insert_binary(t, key, strlen(key) + 1, data);
|
||||
}
|
||||
|
||||
void hash_remove(struct hash_table *t, const char *key)
|
||||
{
|
||||
hash_remove_binary(t, key, strlen(key) + 1);
|
||||
}
|
||||
|
||||
unsigned hash_get_num_entries(struct hash_table *t)
|
||||
{
|
||||
return t->num_nodes;
|
||||
}
|
||||
|
||||
void hash_iter(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_wipe(struct hash_table *t)
|
||||
{
|
||||
_free_nodes(t);
|
||||
memset(t->slots, 0, sizeof(struct hash_node *) * t->num_slots);
|
||||
t->num_nodes = 0;
|
||||
}
|
||||
|
||||
char *hash_get_key(struct hash_table *t, struct hash_node *n)
|
||||
{
|
||||
return n->key;
|
||||
}
|
||||
|
||||
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 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 h = _hash(n->key, n->keylen) & (t->num_slots - 1);
|
||||
return n->next ? n->next : _next_slot(t, h + 1);
|
||||
}
|
49
libdm/datastruct/hash.h
Normal file
49
libdm/datastruct/hash.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _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);
|
||||
void hash_wipe(struct hash_table *t);
|
||||
|
||||
void *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);
|
||||
|
||||
void *hash_lookup_binary(struct hash_table *t, const char *key, uint32_t len);
|
||||
int hash_insert_binary(struct hash_table *t, const char *key, uint32_t len,
|
||||
void *data);
|
||||
void hash_remove_binary(struct hash_table *t, const char *key, uint32_t len);
|
||||
|
||||
unsigned hash_get_num_entries(struct hash_table *t);
|
||||
void hash_iter(struct hash_table *t, iterate_fn f);
|
||||
|
||||
char *hash_get_key(struct hash_table *t, struct hash_node *n);
|
||||
void *hash_get_data(struct hash_table *t, struct hash_node *n);
|
||||
struct hash_node *hash_get_first(struct hash_table *t);
|
||||
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n);
|
||||
|
||||
#define hash_iterate(v, h) \
|
||||
for (v = hash_get_first(h); v; \
|
||||
v = hash_get_next(h, v))
|
||||
|
||||
#endif
|
@ -17,6 +17,7 @@
|
||||
#include "libdm-targets.h"
|
||||
#include "libdm-common.h"
|
||||
#include "libdm-file.h"
|
||||
#include "bitset.h"
|
||||
|
||||
#ifdef DM_COMPAT
|
||||
# include "libdm-compat.h"
|
||||
@ -56,10 +57,13 @@
|
||||
#define PROC_DEVICES "/proc/devices"
|
||||
#define MISC_NAME "misc"
|
||||
|
||||
#define NUMBER_OF_MAJORS 4096
|
||||
|
||||
/* dm major version no for running kernel */
|
||||
static int _dm_version = DM_VERSION_MAJOR;
|
||||
static int _log_suppress = 0;
|
||||
|
||||
static bitset_t _dm_bitset = NULL;
|
||||
static int _control_fd = -1;
|
||||
static int _version_checked = 0;
|
||||
static int _version_ok = 1;
|
||||
@ -119,12 +123,17 @@ static void *_align(void *ptr, unsigned int a)
|
||||
}
|
||||
|
||||
#ifdef DM_IOCTLS
|
||||
/*
|
||||
* Set number to NULL to populate _dm_bitset - otherwise first
|
||||
* match is returned.
|
||||
*/
|
||||
static int _get_proc_number(const char *file, const char *name,
|
||||
uint32_t *number)
|
||||
{
|
||||
FILE *fl;
|
||||
char nm[256];
|
||||
int c;
|
||||
uint32_t num;
|
||||
|
||||
if (!(fl = fopen(file, "r"))) {
|
||||
log_error("%s: fopen failed: %s", file, strerror(errno));
|
||||
@ -132,10 +141,14 @@ static int _get_proc_number(const char *file, const char *name,
|
||||
}
|
||||
|
||||
while (!feof(fl)) {
|
||||
if (fscanf(fl, "%d %255s\n", number, &nm[0]) == 2) {
|
||||
if (fscanf(fl, "%d %255s\n", &num, &nm[0]) == 2) {
|
||||
if (!strcmp(name, nm)) {
|
||||
fclose(fl);
|
||||
return 1;
|
||||
if (number) {
|
||||
*number = num;
|
||||
fclose(fl);
|
||||
return 1;
|
||||
}
|
||||
bit_set(_dm_bitset, num);
|
||||
}
|
||||
} else do {
|
||||
c = fgetc(fl);
|
||||
@ -143,8 +156,12 @@ static int _get_proc_number(const char *file, const char *name,
|
||||
}
|
||||
fclose(fl);
|
||||
|
||||
log_error("%s: No entry for %s found", file, name);
|
||||
return 0;
|
||||
if (number) {
|
||||
log_error("%s: No entry for %s found", file, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _control_device_number(uint32_t *major, uint32_t *minor)
|
||||
@ -230,6 +247,35 @@ static int _create_control(const char *control, uint32_t major, uint32_t minor)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _create_dm_bitset(void)
|
||||
{
|
||||
#ifdef DM_IOCTLS
|
||||
if (_dm_bitset)
|
||||
return 1;
|
||||
|
||||
if (!(_dm_bitset = bitset_create(NULL, NUMBER_OF_MAJORS)))
|
||||
return 0;
|
||||
|
||||
if (!_get_proc_number(PROC_DEVICES, DM_NAME, NULL)) {
|
||||
bitset_destroy(_dm_bitset);
|
||||
_dm_bitset = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int dm_is_dm_major(uint32_t major)
|
||||
{
|
||||
if (!_create_dm_bitset())
|
||||
return 0;
|
||||
|
||||
return bit(_dm_bitset, major) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int _open_control(void)
|
||||
{
|
||||
#ifdef DM_IOCTLS
|
||||
@ -253,6 +299,11 @@ static int _open_control(void)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!_create_dm_bitset()) {
|
||||
log_error("Failed to set up list of device-mapper major numbers");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
error:
|
||||
@ -269,27 +320,27 @@ void dm_task_destroy(struct dm_task *dmt)
|
||||
|
||||
for (t = dmt->head; t; t = n) {
|
||||
n = t->next;
|
||||
free(t->params);
|
||||
free(t->type);
|
||||
free(t);
|
||||
dbg_free(t->params);
|
||||
dbg_free(t->type);
|
||||
dbg_free(t);
|
||||
}
|
||||
|
||||
if (dmt->dev_name)
|
||||
free(dmt->dev_name);
|
||||
dbg_free(dmt->dev_name);
|
||||
|
||||
if (dmt->newname)
|
||||
free(dmt->newname);
|
||||
dbg_free(dmt->newname);
|
||||
|
||||
if (dmt->message)
|
||||
free(dmt->message);
|
||||
dbg_free(dmt->message);
|
||||
|
||||
if (dmt->dmi.v4)
|
||||
free(dmt->dmi.v4);
|
||||
dbg_free(dmt->dmi.v4);
|
||||
|
||||
if (dmt->uuid)
|
||||
free(dmt->uuid);
|
||||
dbg_free(dmt->uuid);
|
||||
|
||||
free(dmt);
|
||||
dbg_free(dmt);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -470,7 +521,7 @@ static struct dm_ioctl_v1 *_flatten_v1(struct dm_task *dmt)
|
||||
if (len < min_size)
|
||||
len = min_size;
|
||||
|
||||
if (!(dmi = malloc(len)))
|
||||
if (!(dmi = dbg_malloc(len)))
|
||||
return NULL;
|
||||
|
||||
memset(dmi, 0, len);
|
||||
@ -519,7 +570,7 @@ static struct dm_ioctl_v1 *_flatten_v1(struct dm_task *dmt)
|
||||
return dmi;
|
||||
|
||||
bad:
|
||||
free(dmi);
|
||||
dbg_free(dmi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -670,7 +721,7 @@ static int _dm_task_run_v1(struct dm_task *dmt)
|
||||
dmt->type = DM_DEVICE_INFO;
|
||||
if (!dm_task_run(dmt))
|
||||
goto bad;
|
||||
free(dmi); /* We'll use what info returned */
|
||||
dbg_free(dmi); /* We'll use what info returned */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -678,7 +729,7 @@ static int _dm_task_run_v1(struct dm_task *dmt)
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
free(dmi);
|
||||
dbg_free(dmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -918,7 +969,7 @@ int dm_task_set_ro(struct dm_task *dmt)
|
||||
|
||||
int dm_task_set_newname(struct dm_task *dmt, const char *newname)
|
||||
{
|
||||
if (!(dmt->newname = strdup(newname))) {
|
||||
if (!(dmt->newname = dbg_strdup(newname))) {
|
||||
log_error("dm_task_set_newname: strdup(%s) failed", newname);
|
||||
return 0;
|
||||
}
|
||||
@ -928,7 +979,7 @@ int dm_task_set_newname(struct dm_task *dmt, const char *newname)
|
||||
|
||||
int dm_task_set_message(struct dm_task *dmt, const char *message)
|
||||
{
|
||||
if (!(dmt->message = strdup(message))) {
|
||||
if (!(dmt->message = dbg_strdup(message))) {
|
||||
log_error("dm_task_set_message: strdup(%s) failed", message);
|
||||
return 0;
|
||||
}
|
||||
@ -967,7 +1018,7 @@ int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr)
|
||||
struct target *create_target(uint64_t start, uint64_t len, const char *type,
|
||||
const char *params)
|
||||
{
|
||||
struct target *t = malloc(sizeof(*t));
|
||||
struct target *t = dbg_malloc(sizeof(*t));
|
||||
|
||||
if (!t) {
|
||||
log_error("create_target: malloc(%d) failed", sizeof(*t));
|
||||
@ -976,12 +1027,12 @@ struct target *create_target(uint64_t start, uint64_t len, const char *type,
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
|
||||
if (!(t->params = strdup(params))) {
|
||||
if (!(t->params = dbg_strdup(params))) {
|
||||
log_error("create_target: strdup(params) failed");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(t->type = strdup(type))) {
|
||||
if (!(t->type = dbg_strdup(type))) {
|
||||
log_error("create_target: strdup(type) failed");
|
||||
goto bad;
|
||||
}
|
||||
@ -991,9 +1042,9 @@ struct target *create_target(uint64_t start, uint64_t len, const char *type,
|
||||
return t;
|
||||
|
||||
bad:
|
||||
free(t->params);
|
||||
free(t->type);
|
||||
free(t);
|
||||
dbg_free(t->params);
|
||||
dbg_free(t->type);
|
||||
dbg_free(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1091,7 +1142,7 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
|
||||
while (repeat_count--)
|
||||
len *= 2;
|
||||
|
||||
if (!(dmi = malloc(len)))
|
||||
if (!(dmi = dbg_malloc(len)))
|
||||
return NULL;
|
||||
|
||||
memset(dmi, 0, len);
|
||||
@ -1149,7 +1200,7 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
|
||||
return dmi;
|
||||
|
||||
bad:
|
||||
free(dmi);
|
||||
dbg_free(dmi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1284,7 +1335,7 @@ static int _create_and_load_v4(struct dm_task *dmt)
|
||||
|
||||
/* Use the original structure last so the info will be correct */
|
||||
dmt->type = DM_DEVICE_RESUME;
|
||||
free(dmt->uuid);
|
||||
dbg_free(dmt->uuid);
|
||||
dmt->uuid = NULL;
|
||||
|
||||
r = dm_task_run(dmt);
|
||||
@ -1332,7 +1383,7 @@ static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
|
||||
log_error("device-mapper ioctl "
|
||||
"cmd %d failed: %s",
|
||||
_IOC_NR(command), strerror(errno));
|
||||
free(dmi);
|
||||
dbg_free(dmi);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -1384,7 +1435,7 @@ repeat_ioctl:
|
||||
case DM_DEVICE_TABLE:
|
||||
case DM_DEVICE_WAITEVENT:
|
||||
_ioctl_buffer_double_factor++;
|
||||
free(dmi);
|
||||
dbg_free(dmi);
|
||||
goto repeat_ioctl;
|
||||
default:
|
||||
log_error("Warning: libdevmapper buffer too small for data");
|
||||
@ -1430,7 +1481,7 @@ repeat_ioctl:
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
free(dmi);
|
||||
dbg_free(dmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1446,6 +1497,10 @@ void dm_lib_release(void)
|
||||
void dm_lib_exit(void)
|
||||
{
|
||||
dm_lib_release();
|
||||
if (_dm_bitset)
|
||||
bitset_destroy(_dm_bitset);
|
||||
_dm_bitset = NULL;
|
||||
dump_memory();
|
||||
_version_ok = 1;
|
||||
_version_checked = 0;
|
||||
}
|
||||
|
@ -165,8 +165,71 @@ int dm_task_run(struct dm_task *dmt);
|
||||
int dm_set_dev_dir(const char *dir);
|
||||
const char *dm_dir(void);
|
||||
|
||||
/* Release library resources */
|
||||
/*
|
||||
* Determine whether a major number belongs to device-mapper or not.
|
||||
*/
|
||||
int dm_is_dm_major(uint32_t major);
|
||||
|
||||
/*
|
||||
* Release library resources
|
||||
*/
|
||||
void dm_lib_release(void);
|
||||
void dm_lib_exit(void) __attribute((destructor));
|
||||
|
||||
/*****************************
|
||||
* Dependency tree functions *
|
||||
*****************************/
|
||||
struct deptree;
|
||||
struct deptree_node;
|
||||
|
||||
/*
|
||||
* Initialise an empty dependency tree.
|
||||
*
|
||||
* The tree consists of a root node together with one node for each mapped
|
||||
* device which has child nodes for each device referenced in its table.
|
||||
*
|
||||
* Every node in the tree has one or more children and one or more parents.
|
||||
*
|
||||
* The root node is the parent/child of every node that doesn't have other
|
||||
* parents/children.
|
||||
*/
|
||||
struct deptree *dm_deptree_create(void);
|
||||
void dm_deptree_free(struct deptree *deptree);
|
||||
|
||||
/*
|
||||
* Add nodes to the tree for a given device and all the devices it uses.
|
||||
*/
|
||||
int dm_deptree_add_dev(struct deptree *deptree, uint32_t major, uint32_t minor);
|
||||
|
||||
/*
|
||||
* Search for a node in the tree.
|
||||
* Set major and minor to 0 to get the root node.
|
||||
*/
|
||||
struct deptree_node *dm_deptree_find_node(struct deptree *deptree,
|
||||
uint32_t major,
|
||||
uint32_t minor);
|
||||
|
||||
/*
|
||||
* Use this to walk through all children of a given node.
|
||||
* Set handle to NULL in first call.
|
||||
* Returns NULL after the last child.
|
||||
* Set inverted to use inverted tree.
|
||||
*/
|
||||
struct deptree_node *dm_deptree_next_child(void **handle,
|
||||
struct deptree_node *parent,
|
||||
uint32_t inverted);
|
||||
|
||||
/*
|
||||
* Get properties of a node.
|
||||
*/
|
||||
const char *dm_deptree_node_get_name(struct deptree_node *node);
|
||||
const char *dm_deptree_node_get_uuid(struct deptree_node *node);
|
||||
const struct dm_info *dm_deptree_node_get_info(struct deptree_node *node);
|
||||
|
||||
/*
|
||||
* Returns the number of children of the given node (excluding the root node).
|
||||
* Set inverted for the number of parents.
|
||||
*/
|
||||
int dm_deptree_node_num_children(struct deptree_node *node, uint32_t inverted);
|
||||
|
||||
#endif /* LIB_DEVICE_MAPPER_H */
|
||||
|
@ -93,7 +93,7 @@ int dm_get_library_version(char *version, size_t size)
|
||||
|
||||
struct dm_task *dm_task_create(int type)
|
||||
{
|
||||
struct dm_task *dmt = malloc(sizeof(*dmt));
|
||||
struct dm_task *dmt = dbg_malloc(sizeof(*dmt));
|
||||
|
||||
if (!dmt) {
|
||||
log_error("dm_task_create: malloc(%d) failed", sizeof(*dmt));
|
||||
@ -123,7 +123,7 @@ int dm_task_set_name(struct dm_task *dmt, const char *name)
|
||||
struct stat st1, st2;
|
||||
|
||||
if (dmt->dev_name) {
|
||||
free(dmt->dev_name);
|
||||
dbg_free(dmt->dev_name);
|
||||
dmt->dev_name = NULL;
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ int dm_task_set_name(struct dm_task *dmt, const char *name)
|
||||
name = pos + 1;
|
||||
}
|
||||
|
||||
if (!(dmt->dev_name = strdup(name))) {
|
||||
if (!(dmt->dev_name = dbg_strdup(name))) {
|
||||
log_error("dm_task_set_name: strdup(%s) failed", name);
|
||||
return 0;
|
||||
}
|
||||
@ -154,11 +154,11 @@ int dm_task_set_name(struct dm_task *dmt, const char *name)
|
||||
int dm_task_set_uuid(struct dm_task *dmt, const char *uuid)
|
||||
{
|
||||
if (dmt->uuid) {
|
||||
free(dmt->uuid);
|
||||
dbg_free(dmt->uuid);
|
||||
dmt->uuid = NULL;
|
||||
}
|
||||
|
||||
if (!(dmt->uuid = strdup(uuid))) {
|
||||
if (!(dmt->uuid = dbg_strdup(uuid))) {
|
||||
log_error("dm_task_set_uuid: strdup(%s) failed", uuid);
|
||||
return 0;
|
||||
}
|
||||
@ -381,7 +381,7 @@ static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
|
||||
size_t len = strlen(dev_name) + strlen(old_name) + 2;
|
||||
char *pos;
|
||||
|
||||
if (!(nop = malloc(sizeof(*nop) + len))) {
|
||||
if (!(nop = dbg_malloc(sizeof(*nop) + len))) {
|
||||
log_error("Insufficient memory to stack mknod operation");
|
||||
return 0;
|
||||
}
|
||||
@ -412,7 +412,7 @@ static void _pop_node_ops(void)
|
||||
_do_node_op(nop->type, nop->dev_name, nop->major, nop->minor,
|
||||
nop->uid, nop->gid, nop->mode, nop->old_name);
|
||||
list_del(&nop->list);
|
||||
free(nop);
|
||||
dbg_free(nop);
|
||||
}
|
||||
}
|
||||
|
||||
|
403
libdm/libdm-deptree.c
Normal file
403
libdm/libdm-deptree.c
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "libdm-targets.h"
|
||||
#include "libdm-common.h"
|
||||
#include "list.h"
|
||||
#include "kdev_t.h"
|
||||
#include "pool.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <linux/dm-ioctl.h>
|
||||
|
||||
struct deptree_node {
|
||||
struct deptree *deptree;
|
||||
|
||||
const char *name;
|
||||
const char *uuid;
|
||||
struct dm_info info;
|
||||
|
||||
struct list uses; /* Nodes this node uses */
|
||||
struct list used_by; /* Nodes that use this node */
|
||||
};
|
||||
|
||||
struct deptree {
|
||||
struct pool *mem;
|
||||
struct hash_table *devs;
|
||||
struct deptree_node root;
|
||||
};
|
||||
|
||||
struct deptree_link {
|
||||
struct list list;
|
||||
struct deptree_node *node;
|
||||
};
|
||||
|
||||
struct deptree *dm_deptree_create(void)
|
||||
{
|
||||
struct deptree *deptree;
|
||||
|
||||
if (!(deptree = dbg_malloc(sizeof(*deptree)))) {
|
||||
log_error("dm_deptree_create malloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(deptree, 0, sizeof(*deptree));
|
||||
deptree->root.deptree = deptree;
|
||||
list_init(&deptree->root.uses);
|
||||
list_init(&deptree->root.used_by);
|
||||
|
||||
if (!(deptree->mem = pool_create("deptree", 1024))) {
|
||||
log_error("deptree pool creation failed");
|
||||
dbg_free(deptree);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(deptree->devs = hash_create(8))) {
|
||||
log_error("deptree hash creation failed");
|
||||
pool_destroy(deptree->mem);
|
||||
dbg_free(deptree);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return deptree;
|
||||
}
|
||||
|
||||
void dm_deptree_free(struct deptree *deptree)
|
||||
{
|
||||
if (!deptree)
|
||||
return;
|
||||
|
||||
hash_destroy(deptree->devs);
|
||||
pool_destroy(deptree->mem);
|
||||
dbg_free(deptree);
|
||||
}
|
||||
|
||||
static int _nodes_are_linked(struct deptree_node *parent,
|
||||
struct deptree_node *child)
|
||||
{
|
||||
struct deptree_link *dlink;
|
||||
|
||||
list_iterate_items(dlink, &parent->uses) {
|
||||
if (dlink->node == child)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _link(struct list *list, struct deptree_node *node)
|
||||
{
|
||||
struct deptree_link *dlink;
|
||||
|
||||
if (!(dlink = pool_alloc(node->deptree->mem, sizeof(*dlink)))) {
|
||||
log_error("deptree link allocation failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlink->node = node;
|
||||
list_add(list, &dlink->list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _link_nodes(struct deptree_node *parent,
|
||||
struct deptree_node *child)
|
||||
{
|
||||
if (_nodes_are_linked(parent, child))
|
||||
return 1;
|
||||
|
||||
if (!_link(&parent->uses, child))
|
||||
return 0;
|
||||
|
||||
if (!_link(&child->used_by, parent))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _unlink(struct list *list, struct deptree_node *node)
|
||||
{
|
||||
struct deptree_link *dlink;
|
||||
|
||||
list_iterate_items(dlink, list) {
|
||||
if (dlink->node == node) {
|
||||
list_del(&dlink->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _unlink_nodes(struct deptree_node *parent,
|
||||
struct deptree_node *child)
|
||||
{
|
||||
if (!_nodes_are_linked(parent, child))
|
||||
return;
|
||||
|
||||
_unlink(&parent->uses, child);
|
||||
_unlink(&child->used_by, parent);
|
||||
}
|
||||
|
||||
static void _remove_from_toplevel(struct deptree_node *node)
|
||||
{
|
||||
return _unlink_nodes(&node->deptree->root, node);
|
||||
}
|
||||
|
||||
static int _add_to_bottomlevel(struct deptree_node *node)
|
||||
{
|
||||
return _link_nodes(node, &node->deptree->root);
|
||||
}
|
||||
|
||||
static struct deptree_node *_create_deptree_node(struct deptree *deptree,
|
||||
struct deptree_node *parent,
|
||||
const char *name,
|
||||
const char *uuid,
|
||||
struct dm_info *info)
|
||||
{
|
||||
struct deptree_node *node;
|
||||
uint64_t dev;
|
||||
|
||||
if (!(node = pool_zalloc(deptree->mem, sizeof(*node)))) {
|
||||
log_error("_create_deptree_node alloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->deptree = deptree;
|
||||
|
||||
node->name = name;
|
||||
node->uuid = uuid;
|
||||
node->info = *info;
|
||||
|
||||
list_init(&node->uses);
|
||||
list_init(&node->used_by);
|
||||
|
||||
dev = MKDEV(info->major, info->minor);
|
||||
|
||||
if (!hash_insert_binary(deptree->devs, (const char *) &dev,
|
||||
sizeof(dev), node)) {
|
||||
log_error("deptree node hash insertion failed");
|
||||
pool_free(deptree->mem, node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static struct deptree_node *_find_deptree_node(struct deptree *deptree,
|
||||
uint32_t major, uint32_t minor)
|
||||
{
|
||||
uint64_t dev = MKDEV(major, minor);
|
||||
|
||||
return hash_lookup_binary(deptree->devs, (const char *) &dev,
|
||||
sizeof(dev));
|
||||
}
|
||||
|
||||
static int _deps(struct dm_task **dmt, struct pool *mem, uint32_t major, uint32_t minor,
|
||||
const char **name, const char **uuid,
|
||||
struct dm_info *info, struct dm_deps **deps)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
if (!dm_is_dm_major(major)) {
|
||||
*name = "";
|
||||
*uuid = "";
|
||||
*deps = NULL;
|
||||
info->major = major;
|
||||
info->minor = minor;
|
||||
info->exists = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(*dmt = dm_task_create(DM_DEVICE_DEPS))) {
|
||||
log_error("deps dm_task creation failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_task_set_major(*dmt, major))
|
||||
goto failed;
|
||||
|
||||
if (!dm_task_set_minor(*dmt, minor))
|
||||
goto failed;
|
||||
|
||||
if (!dm_task_run(*dmt))
|
||||
goto failed;
|
||||
|
||||
if (!dm_task_get_info(*dmt, info))
|
||||
goto failed;
|
||||
|
||||
if (!info->exists) {
|
||||
*name = "";
|
||||
*uuid = "";
|
||||
*deps = NULL;
|
||||
} else {
|
||||
if (info->major != major) {
|
||||
log_error("Inconsistent deptree major number: %u != %u",
|
||||
major, info->major);
|
||||
goto failed;
|
||||
}
|
||||
if (info->minor != minor) {
|
||||
log_error("Inconsistent deptree minor number: %u != %u",
|
||||
minor, info->minor);
|
||||
goto failed;
|
||||
}
|
||||
if (!(*name = pool_strdup(mem, dm_task_get_name(*dmt)))) {
|
||||
log_error("name pool_strdup failed");
|
||||
goto failed;
|
||||
}
|
||||
if (!(*uuid = pool_strdup(mem, dm_task_get_uuid(*dmt)))) {
|
||||
log_error("uuid pool_strdup failed");
|
||||
goto failed;
|
||||
}
|
||||
*deps = dm_task_get_deps(*dmt);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
failed:
|
||||
dm_task_destroy(*dmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _add_dev(struct deptree *deptree, struct deptree_node *parent,
|
||||
uint32_t major, uint32_t minor)
|
||||
{
|
||||
struct dm_task *dmt = NULL;
|
||||
struct dm_info info;
|
||||
struct dm_deps *deps = NULL;
|
||||
const char *name = NULL;
|
||||
const char *uuid = NULL;
|
||||
struct deptree_node *node;
|
||||
uint32_t i;
|
||||
int r = 0;
|
||||
int new = 0;
|
||||
|
||||
/* Already in tree? */
|
||||
if (!(node = _find_deptree_node(deptree, major, minor))) {
|
||||
if (!_deps(&dmt, deptree->mem, major, minor, &name, &uuid, &info, &deps))
|
||||
return 0;
|
||||
|
||||
if (!(node = _create_deptree_node(deptree, node, name, uuid,
|
||||
&info)))
|
||||
goto out;
|
||||
new = 1;
|
||||
}
|
||||
|
||||
/* If new parent not root node, remove any existing root node parent */
|
||||
if (parent != &deptree->root)
|
||||
_remove_from_toplevel(node);
|
||||
|
||||
/* Create link to parent. Use root node only if no other parents. */
|
||||
if ((parent != &deptree->root) || !dm_deptree_node_num_children(node, 1))
|
||||
if (!_link_nodes(parent, node))
|
||||
goto out;
|
||||
|
||||
/* If node was already in tree, no need to recurse. */
|
||||
if (!new)
|
||||
return 1;
|
||||
|
||||
/* Can't recurse if not a mapped device or there are no dependencies */
|
||||
if (!node->info.exists || !deps->count) {
|
||||
if (!_add_to_bottomlevel(node))
|
||||
goto out;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Add dependencies to tree */
|
||||
for (i = 0; i < deps->count; i++)
|
||||
if (!_add_dev(deptree, node, MAJOR(deps->device[i]),
|
||||
MINOR(deps->device[i])))
|
||||
goto out;
|
||||
|
||||
r = 1;
|
||||
out:
|
||||
if (dmt)
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dm_deptree_add_dev(struct deptree *deptree, uint32_t major, uint32_t minor)
|
||||
{
|
||||
return _add_dev(deptree, &deptree->root, major, minor);
|
||||
}
|
||||
|
||||
const char *dm_deptree_node_get_name(struct deptree_node *node)
|
||||
{
|
||||
return node->info.exists ? node->name : "";
|
||||
}
|
||||
|
||||
const char *dm_deptree_node_get_uuid(struct deptree_node *node)
|
||||
{
|
||||
return node->info.exists ? node->uuid : "";
|
||||
}
|
||||
|
||||
const struct dm_info *dm_deptree_node_get_info(struct deptree_node *node)
|
||||
{
|
||||
return &node->info;
|
||||
}
|
||||
|
||||
int dm_deptree_node_num_children(struct deptree_node *node, uint32_t inverted)
|
||||
{
|
||||
if (inverted) {
|
||||
if (_nodes_are_linked(&node->deptree->root, node))
|
||||
return 0;
|
||||
return list_size(&node->used_by);
|
||||
}
|
||||
|
||||
if (_nodes_are_linked(node, &node->deptree->root))
|
||||
return 0;
|
||||
|
||||
return list_size(&node->uses);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set major and minor to zero for root of tree.
|
||||
*/
|
||||
struct deptree_node *dm_deptree_find_node(struct deptree *deptree,
|
||||
uint32_t major,
|
||||
uint32_t minor)
|
||||
{
|
||||
if (!major && !minor)
|
||||
return &deptree->root;
|
||||
|
||||
return _find_deptree_node(deptree, major, minor);
|
||||
}
|
||||
|
||||
/*
|
||||
* First time set *handle to NULL.
|
||||
* Set inverted to invert the tree.
|
||||
*/
|
||||
struct deptree_node *dm_deptree_next_child(void **handle,
|
||||
struct deptree_node *parent,
|
||||
uint32_t inverted)
|
||||
{
|
||||
struct list **dlink = (struct list **) handle;
|
||||
struct list *use_list;
|
||||
|
||||
if (inverted)
|
||||
use_list = &parent->used_by;
|
||||
else
|
||||
use_list = &parent->uses;
|
||||
|
||||
if (!*dlink)
|
||||
*dlink = list_first(use_list);
|
||||
else
|
||||
*dlink = list_next(use_list, *dlink);
|
||||
|
||||
return (*dlink) ? list_item(*dlink, struct deptree_link)->node : NULL;
|
||||
}
|
||||
|
247
libdm/mm/dbg_malloc.c
Normal file
247
libdm/mm/dbg_malloc.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "dbg_malloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
char *dbg_strdup(const char *str)
|
||||
{
|
||||
char *ret = dbg_malloc(strlen(str) + 1);
|
||||
|
||||
if (ret)
|
||||
strcpy(ret, str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MEM
|
||||
|
||||
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 block_serialno;/* Non-decreasing serialno of block */
|
||||
unsigned blocks_allocated; /* Current number of blocks allocated */
|
||||
unsigned blocks_max; /* Max no of concurrently-allocated blocks */
|
||||
unsigned int bytes, mbytes;
|
||||
|
||||
} _mem_stats = {
|
||||
0, 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 (s > 50000000) {
|
||||
log_error("Huge memory allocation (size %" PRIsize_t
|
||||
") rejected - metadata corruption?", s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(nb = malloc(tsize))) {
|
||||
log_error("couldn't allocate any memory, size = %" PRIsize_t,
|
||||
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.block_serialno;
|
||||
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);
|
||||
size_t 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;
|
||||
}
|
||||
|
||||
_mem_stats.blocks_allocated++;
|
||||
if (_mem_stats.blocks_allocated > _mem_stats.blocks_max)
|
||||
_mem_stats.blocks_max = _mem_stats.blocks_allocated;
|
||||
|
||||
_mem_stats.bytes += s;
|
||||
if (_mem_stats.bytes > _mem_stats.mbytes)
|
||||
_mem_stats.mbytes = _mem_stats.bytes;
|
||||
|
||||
/* log_debug("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated,
|
||||
_mem_stats.bytes); */
|
||||
|
||||
return nb + 1;
|
||||
}
|
||||
|
||||
void free_aux(void *p)
|
||||
{
|
||||
char *ptr;
|
||||
size_t 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_allocated);
|
||||
_mem_stats.blocks_allocated--;
|
||||
_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;
|
||||
}
|
||||
|
||||
int dump_memory(void)
|
||||
{
|
||||
unsigned long tot = 0;
|
||||
struct memblock *mb;
|
||||
char str[32];
|
||||
size_t c;
|
||||
|
||||
if (_head)
|
||||
log_very_verbose("You have a memory leak:");
|
||||
|
||||
for (mb = _head; mb; mb = mb->next) {
|
||||
for (c = 0; c < sizeof(str) - 1; c++) {
|
||||
if (c >= mb->length)
|
||||
str[c] = ' ';
|
||||
else if (*(char *)(mb->magic + c) == '\0')
|
||||
str[c] = '\0';
|
||||
else if (*(char *)(mb->magic + c) < ' ')
|
||||
str[c] = '?';
|
||||
else
|
||||
str[c] = *(char *)(mb->magic + c);
|
||||
}
|
||||
str[sizeof(str) - 1] = '\0';
|
||||
|
||||
_log(_LOG_INFO, mb->file, mb->line,
|
||||
"block %d at %p, size %" PRIsize_t "\t [%s]",
|
||||
mb->id, mb->magic, mb->length, str);
|
||||
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) {
|
||||
size_t 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;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *malloc_aux(size_t s, const char *file, int line)
|
||||
{
|
||||
if (s > 50000000) {
|
||||
log_error("Huge memory allocation (size %" PRIsize_t
|
||||
") rejected - metadata corruption?", s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return malloc(s);
|
||||
}
|
||||
|
||||
#endif
|
46
libdm/mm/dbg_malloc.h
Normal file
46
libdm/mm/dbg_malloc.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DBG_MALLOC_H
|
||||
#define _LVM_DBG_MALLOC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void *malloc_aux(size_t s, const char *file, int line);
|
||||
#define dbg_malloc(s) malloc_aux((s), __FILE__, __LINE__)
|
||||
|
||||
char *dbg_strdup(const char *str);
|
||||
|
||||
#ifdef DEBUG_MEM
|
||||
|
||||
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_free(p) free_aux(p)
|
||||
# define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__)
|
||||
|
||||
#else
|
||||
|
||||
# define dbg_free(p) free(p)
|
||||
# define dbg_realloc(p, s) realloc(p, s)
|
||||
# define dump_memory()
|
||||
# define bounds_check()
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
263
libdm/mm/pool-debug.c
Normal file
263
libdm/mm/pool-debug.c
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "pool.h"
|
||||
|
||||
struct block {
|
||||
struct block *next;
|
||||
size_t size;
|
||||
void *data;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
unsigned block_serialno; /* Non-decreasing serialno of block */
|
||||
unsigned blocks_allocated; /* Current number of blocks allocated */
|
||||
unsigned blocks_max; /* Max no of concurrently-allocated blocks */
|
||||
unsigned int bytes, maxbytes;
|
||||
} pool_stats;
|
||||
|
||||
struct pool {
|
||||
const char *name;
|
||||
|
||||
int begun;
|
||||
struct block *object;
|
||||
|
||||
struct block *blocks;
|
||||
struct block *tail;
|
||||
|
||||
pool_stats stats;
|
||||
};
|
||||
|
||||
/* by default things come out aligned for doubles */
|
||||
#define DEFAULT_ALIGNMENT __alignof__ (double)
|
||||
|
||||
struct pool *pool_create(const char *name, size_t chunk_hint)
|
||||
{
|
||||
struct pool *mem = dbg_malloc(sizeof(*mem));
|
||||
|
||||
if (!mem) {
|
||||
log_error("Couldn't create memory pool %s (size %"
|
||||
PRIsize_t ")", name, sizeof(*mem));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mem->name = name;
|
||||
mem->begun = 0;
|
||||
mem->object = 0;
|
||||
mem->blocks = mem->tail = NULL;
|
||||
|
||||
mem->stats.block_serialno = 0;
|
||||
mem->stats.blocks_allocated = 0;
|
||||
mem->stats.blocks_max = 0;
|
||||
mem->stats.bytes = 0;
|
||||
mem->stats.maxbytes = 0;
|
||||
|
||||
#ifdef DEBUG_POOL
|
||||
log_debug("Created mempool %s", name);
|
||||
#endif
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void _free_blocks(struct pool *p, struct block *b)
|
||||
{
|
||||
struct block *n;
|
||||
|
||||
while (b) {
|
||||
p->stats.bytes -= b->size;
|
||||
p->stats.blocks_allocated--;
|
||||
|
||||
n = b->next;
|
||||
dbg_free(b->data);
|
||||
dbg_free(b);
|
||||
b = n;
|
||||
}
|
||||
}
|
||||
|
||||
static void _pool_stats(struct pool *p, const char *action)
|
||||
{
|
||||
#ifdef DEBUG_POOL
|
||||
log_debug("%s mempool %s: %u/%u bytes, %u/%u blocks, "
|
||||
"%u allocations)", action, p->name, p->stats.bytes,
|
||||
p->stats.maxbytes, p->stats.blocks_allocated,
|
||||
p->stats.blocks_max, p->stats.block_serialno);
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
}
|
||||
|
||||
void pool_destroy(struct pool *p)
|
||||
{
|
||||
_pool_stats(p, "Destroying");
|
||||
_free_blocks(p, p->blocks);
|
||||
dbg_free(p);
|
||||
}
|
||||
|
||||
void *pool_alloc(struct pool *p, size_t s)
|
||||
{
|
||||
return pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
static void _append_block(struct pool *p, struct block *b)
|
||||
{
|
||||
if (p->tail) {
|
||||
p->tail->next = b;
|
||||
p->tail = b;
|
||||
} else
|
||||
p->blocks = p->tail = b;
|
||||
|
||||
p->stats.block_serialno++;
|
||||
p->stats.blocks_allocated++;
|
||||
if (p->stats.blocks_allocated > p->stats.blocks_max)
|
||||
p->stats.blocks_max = p->stats.blocks_allocated;
|
||||
|
||||
p->stats.bytes += b->size;
|
||||
if (p->stats.bytes > p->stats.maxbytes)
|
||||
p->stats.maxbytes = p->stats.bytes;
|
||||
}
|
||||
|
||||
static struct block *_new_block(size_t s, unsigned alignment)
|
||||
{
|
||||
static const char *_oom = "Out of memory";
|
||||
|
||||
/* FIXME: I'm currently ignoring the alignment arg. */
|
||||
size_t len = sizeof(struct block) + s;
|
||||
struct block *b = dbg_malloc(len);
|
||||
|
||||
/*
|
||||
* Too lazy to implement alignment for debug version, and
|
||||
* I don't think LVM will use anything but default
|
||||
* align.
|
||||
*/
|
||||
assert(alignment == DEFAULT_ALIGNMENT);
|
||||
|
||||
if (!b) {
|
||||
log_err(_oom);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(b->data = dbg_malloc(s))) {
|
||||
log_err(_oom);
|
||||
dbg_free(b);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b->next = NULL;
|
||||
b->size = s;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment)
|
||||
{
|
||||
struct block *b = _new_block(s, alignment);
|
||||
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
_append_block(p, b);
|
||||
|
||||
return b->data;
|
||||
}
|
||||
|
||||
void pool_empty(struct pool *p)
|
||||
{
|
||||
_pool_stats(p, "Emptying");
|
||||
_free_blocks(p, p->blocks);
|
||||
p->blocks = p->tail = NULL;
|
||||
}
|
||||
|
||||
void pool_free(struct pool *p, void *ptr)
|
||||
{
|
||||
struct block *b, *prev = NULL;
|
||||
|
||||
_pool_stats(p, "Freeing (before)");
|
||||
|
||||
for (b = p->blocks; b; b = b->next) {
|
||||
if (b->data == ptr)
|
||||
break;
|
||||
prev = b;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this fires then you tried to free a
|
||||
* pointer that either wasn't from this
|
||||
* pool, or isn't the start of a block.
|
||||
*/
|
||||
assert(b);
|
||||
|
||||
_free_blocks(p, b);
|
||||
|
||||
if (prev) {
|
||||
p->tail = prev;
|
||||
prev->next = NULL;
|
||||
} else
|
||||
p->blocks = p->tail = NULL;
|
||||
|
||||
_pool_stats(p, "Freeing (after)");
|
||||
}
|
||||
|
||||
int pool_begin_object(struct pool *p, size_t init_size)
|
||||
{
|
||||
assert(!p->begun);
|
||||
p->begun = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pool_grow_object(struct pool *p, const void *buffer, size_t delta)
|
||||
{
|
||||
struct block *new;
|
||||
size_t size = delta;
|
||||
|
||||
assert(p->begun);
|
||||
|
||||
if (p->object)
|
||||
size += p->object->size;
|
||||
|
||||
if (!(new = _new_block(size, DEFAULT_ALIGNMENT))) {
|
||||
log_err("Couldn't extend object.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (p->object) {
|
||||
memcpy(new->data, p->object->data, p->object->size);
|
||||
dbg_free(p->object->data);
|
||||
dbg_free(p->object);
|
||||
}
|
||||
p->object = new;
|
||||
|
||||
memcpy(new->data + size - delta, buffer, delta);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *pool_end_object(struct pool *p)
|
||||
{
|
||||
assert(p->begun);
|
||||
_append_block(p, p->object);
|
||||
|
||||
p->begun = 0;
|
||||
p->object = NULL;
|
||||
return p->tail->data;
|
||||
}
|
||||
|
||||
void pool_abandon_object(struct pool *p)
|
||||
{
|
||||
assert(p->begun);
|
||||
dbg_free(p->object);
|
||||
p->begun = 0;
|
||||
p->object = NULL;
|
||||
}
|
235
libdm/mm/pool-fast.c
Normal file
235
libdm/mm/pool-fast.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "pool.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(const char *name, size_t chunk_hint)
|
||||
{
|
||||
size_t new_size = 1024;
|
||||
struct pool *p = dbg_malloc(sizeof(*p));
|
||||
|
||||
if (!p) {
|
||||
log_error("Couldn't create memory pool %s (size %"
|
||||
PRIsize_t ")", name, sizeof(*p));
|
||||
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);
|
||||
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
_align_chunk(c, alignment);
|
||||
}
|
||||
|
||||
r = c->begin;
|
||||
c->begin += s;
|
||||
return r;
|
||||
}
|
||||
|
||||
void pool_empty(struct pool *p)
|
||||
{
|
||||
struct chunk *c;
|
||||
|
||||
for (c = p->chunk; c && c->prev; c = c->prev)
|
||||
;
|
||||
|
||||
if (c)
|
||||
pool_free(p, (char *) (c + 1));
|
||||
}
|
||||
|
||||
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_error("Internal error: pool_free asked to free pointer "
|
||||
"not in pool");
|
||||
else
|
||||
p->chunk = c;
|
||||
}
|
||||
|
||||
int pool_begin_object(struct pool *p, size_t hint)
|
||||
{
|
||||
struct chunk *c = p->chunk;
|
||||
const size_t align = DEFAULT_ALIGNMENT;
|
||||
|
||||
p->object_len = 0;
|
||||
p->object_alignment = align;
|
||||
|
||||
if (c)
|
||||
_align_chunk(c, align);
|
||||
|
||||
if (!c || (c->begin > c->end) || (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);
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
_align_chunk(c, align);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pool_grow_object(struct pool *p, const void *extra, size_t n)
|
||||
{
|
||||
struct chunk *c = p->chunk, *nc;
|
||||
|
||||
if (c->end - (c->begin + p->object_len) < n) {
|
||||
/* move into a new chunk */
|
||||
if (p->object_len + n > (p->chunk_size / 2))
|
||||
nc = _new_chunk(p, (p->object_len + n) * 2);
|
||||
else
|
||||
nc = _new_chunk(p, p->chunk_size);
|
||||
|
||||
if (!nc)
|
||||
return 0;
|
||||
|
||||
_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, extra, n);
|
||||
p->object_len += n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (!(c = dbg_malloc(s))) {
|
||||
log_error("Out of memory. Requested %" PRIsize_t
|
||||
" bytes.", s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c->end = (char *) c + s;
|
||||
}
|
||||
|
||||
c->prev = p->chunk;
|
||||
c->begin = (char *) (c + 1);
|
||||
p->chunk = c;
|
||||
|
||||
return c;
|
||||
}
|
52
libdm/mm/pool.c
Normal file
52
libdm/mm/pool.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_POOL
|
||||
#include "pool-debug.c"
|
||||
#else
|
||||
#include "pool-fast.c"
|
||||
#endif
|
||||
|
||||
char *pool_strdup(struct pool *p, const char *str)
|
||||
{
|
||||
char *ret = pool_alloc(p, strlen(str) + 1);
|
||||
|
||||
if (ret)
|
||||
strcpy(ret, str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *pool_strndup(struct pool *p, const char *str, size_t n)
|
||||
{
|
||||
char *ret = pool_alloc(p, n + 1);
|
||||
|
||||
if (ret) {
|
||||
strncpy(ret, str, n);
|
||||
ret[n] = '\0';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *pool_zalloc(struct pool *p, size_t s)
|
||||
{
|
||||
void *ptr = pool_alloc(p, s);
|
||||
|
||||
if (ptr)
|
||||
memset(ptr, 0, s);
|
||||
|
||||
return ptr;
|
||||
}
|
121
libdm/mm/pool.h
Normal file
121
libdm/mm/pool.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_POOL_H
|
||||
#define _LVM_POOL_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* The pool allocator is useful when you are going to allocate
|
||||
* lots of memory, use the memory for a bit, and then free the
|
||||
* memory in one go. A surprising amount of code has this usage
|
||||
* profile.
|
||||
*
|
||||
* You should think of the pool as an infinite, contiguous chunk
|
||||
* of memory. The front of this chunk of memory contains
|
||||
* allocated objects, the second half is free. pool_alloc grabs
|
||||
* the next 'size' bytes from the free half, in effect moving it
|
||||
* into the allocated half. This operation is very efficient.
|
||||
*
|
||||
* pool_free frees the allocated object *and* all objects
|
||||
* allocated after it. It is important to note this semantic
|
||||
* difference from malloc/free. This is also extremely
|
||||
* efficient, since a single pool_free can dispose of a large
|
||||
* complex object.
|
||||
*
|
||||
* pool_destroy frees all allocated memory.
|
||||
*
|
||||
* eg, If you are building a binary tree in your program, and
|
||||
* know that you are only ever going to insert into your tree,
|
||||
* and not delete (eg, maintaining a symbol table for a
|
||||
* compiler). You can create yourself a pool, allocate the nodes
|
||||
* from it, and when the tree becomes redundant call pool_destroy
|
||||
* (no nasty iterating through the tree to free nodes).
|
||||
*
|
||||
* eg, On the other hand if you wanted to repeatedly insert and
|
||||
* remove objects into the tree, you would be better off
|
||||
* allocating the nodes from a free list; you cannot free a
|
||||
* single arbitrary node with pool.
|
||||
*/
|
||||
|
||||
struct pool;
|
||||
|
||||
/* constructor and destructor */
|
||||
struct pool *pool_create(const char *name, 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:
|
||||
*
|
||||
* These allow you to 'grow' an object, useful for
|
||||
* building strings, or filling in dynamic
|
||||
* arrays.
|
||||
*
|
||||
* It's probably best explained with an example:
|
||||
*
|
||||
* char *build_string(struct pool *mem)
|
||||
* {
|
||||
* int i;
|
||||
* char buffer[16];
|
||||
*
|
||||
* if (!pool_begin_object(mem, 128))
|
||||
* return NULL;
|
||||
*
|
||||
* for (i = 0; i < 50; i++) {
|
||||
* snprintf(buffer, sizeof(buffer), "%d, ", i);
|
||||
* if (!pool_grow_object(mem, buffer, strlen(buffer)))
|
||||
* goto bad;
|
||||
* }
|
||||
*
|
||||
* // add null
|
||||
* if (!pool_grow_object(mem, "\0", 1))
|
||||
* goto bad;
|
||||
*
|
||||
* return pool_end_object(mem);
|
||||
*
|
||||
* bad:
|
||||
*
|
||||
* pool_abandon_object(mem);
|
||||
* return NULL;
|
||||
*}
|
||||
*
|
||||
* So start an object by calling pool_begin_object
|
||||
* with a guess at the final object size - if in
|
||||
* doubt make the guess too small.
|
||||
*
|
||||
* Then append chunks of data to your object with
|
||||
* pool_grow_object. Finally get your object with
|
||||
* a call to pool_end_object.
|
||||
*
|
||||
*/
|
||||
int pool_begin_object(struct pool *p, size_t hint);
|
||||
int pool_grow_object(struct pool *p, const void *extra, size_t delta);
|
||||
void *pool_end_object(struct pool *p);
|
||||
void pool_abandon_object(struct pool *p);
|
||||
|
||||
/* utilities */
|
||||
char *pool_strdup(struct pool *p, const char *str);
|
||||
char *pool_strndup(struct pool *p, const char *str, size_t n);
|
||||
void *pool_zalloc(struct pool *p, size_t s);
|
||||
|
||||
#endif
|
449
tools/dmsetup.c
449
tools/dmsetup.c
@ -1,9 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 NEC Corperation
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
@ -30,6 +33,16 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <locale.h>
|
||||
#include <langinfo.h>
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_TERMIOS_H
|
||||
# include <termios.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETOPTLONG
|
||||
# include <getopt.h>
|
||||
@ -81,6 +94,7 @@ enum {
|
||||
NOTABLE_ARG,
|
||||
OPTIONS_ARG,
|
||||
TARGET_ARG,
|
||||
TREE_ARG,
|
||||
UUID_ARG,
|
||||
VERBOSE_ARG,
|
||||
VERSION_ARG,
|
||||
@ -93,6 +107,7 @@ static char *_uuid;
|
||||
static char *_fields;
|
||||
static char *_target;
|
||||
static char *_command;
|
||||
static struct deptree *_dtree;
|
||||
|
||||
/*
|
||||
* Commands
|
||||
@ -951,11 +966,348 @@ static int _display_name(int argc, char **argv, void *data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tree drawing code
|
||||
*/
|
||||
|
||||
enum {
|
||||
TR_DEVICE=0, /* display device major:minor number */
|
||||
TR_TABLE,
|
||||
TR_STATUS,
|
||||
TR_ACTIVE,
|
||||
TR_RW,
|
||||
TR_OPENCOUNT,
|
||||
TR_UUID,
|
||||
TR_COMPACT,
|
||||
TR_TRUNCATE,
|
||||
TR_BOTTOMUP,
|
||||
NUM_TREEMODE,
|
||||
};
|
||||
|
||||
static int _tree_switches[NUM_TREEMODE];
|
||||
|
||||
#define TR_PRINT_ATTRIBUTE ( _tree_switches[TR_ACTIVE] || \
|
||||
_tree_switches[TR_RW] || \
|
||||
_tree_switches[TR_OPENCOUNT] || \
|
||||
_tree_switches[TR_UUID] )
|
||||
|
||||
#define TR_PRINT_TARGETS ( _tree_switches[TR_TABLE] || \
|
||||
_tree_switches[TR_STATUS] )
|
||||
|
||||
/* Compact - fewer newlines */
|
||||
#define TR_PRINT_COMPACT (_tree_switches[TR_COMPACT] && \
|
||||
!TR_PRINT_ATTRIBUTE && \
|
||||
!TR_PRINT_TARGETS)
|
||||
|
||||
/* FIXME Get rid of this */
|
||||
#define MAX_DEPTH 100
|
||||
|
||||
/* Drawing character definition from pstree */
|
||||
/* [pstree comment] UTF-8 defines by Johan Myreen, updated by Ben Winslow */
|
||||
#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */
|
||||
#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */
|
||||
#define UTF_H "\342\224\200" /* U+2500, Horizontal */
|
||||
#define UTF_UR "\342\224\224" /* U+2514, Up and right */
|
||||
#define UTF_HD "\342\224\254" /* U+252C, Horizontal and down */
|
||||
|
||||
#define VT_BEG "\033(0\017" /* use graphic chars */
|
||||
#define VT_END "\033(B" /* back to normal char set */
|
||||
#define VT_V "x" /* see UTF definitions above */
|
||||
#define VT_VR "t"
|
||||
#define VT_H "q"
|
||||
#define VT_UR "m"
|
||||
#define VT_HD "w"
|
||||
|
||||
static struct {
|
||||
const char *empty_2; /* */
|
||||
const char *branch_2; /* |- */
|
||||
const char *vert_2; /* | */
|
||||
const char *last_2; /* `- */
|
||||
const char *single_3; /* --- */
|
||||
const char *first_3; /* -+- */
|
||||
}
|
||||
_tsym_ascii = {
|
||||
" ",
|
||||
"|-",
|
||||
"| ",
|
||||
"`-",
|
||||
"---",
|
||||
"-+-"
|
||||
},
|
||||
_tsym_utf = {
|
||||
" ",
|
||||
UTF_VR UTF_H,
|
||||
UTF_V " ",
|
||||
UTF_UR UTF_H,
|
||||
UTF_H UTF_H UTF_H,
|
||||
UTF_H UTF_HD UTF_H
|
||||
},
|
||||
_tsym_vt100 = {
|
||||
" ",
|
||||
VT_BEG VT_VR VT_H VT_END,
|
||||
VT_BEG VT_V VT_END " ",
|
||||
VT_BEG VT_UR VT_H VT_END,
|
||||
VT_BEG VT_H VT_H VT_H VT_END,
|
||||
VT_BEG VT_H VT_HD VT_H VT_END
|
||||
},
|
||||
*_tsym = &_tsym_ascii;
|
||||
|
||||
/*
|
||||
* Tree drawing functions.
|
||||
*/
|
||||
/* FIXME Get rid of these statics - use dynamic struct */
|
||||
/* FIXME Explain what these vars are for */
|
||||
static int _tree_width[MAX_DEPTH], _tree_more[MAX_DEPTH];
|
||||
static int _termwidth = 80; /* Maximum output width */
|
||||
static int _cur_x = 1; /* Current horizontal output position */
|
||||
static char _last_char = 0;
|
||||
|
||||
static void _out_char(char c)
|
||||
{
|
||||
/* Only first UTF-8 char counts */
|
||||
_cur_x += ((c & 0xc0) != 0x80);
|
||||
|
||||
if (!_tree_switches[TR_TRUNCATE]) {
|
||||
putchar(c);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Truncation? */
|
||||
if (_cur_x <= _termwidth)
|
||||
putchar(c);
|
||||
|
||||
if (_cur_x == _termwidth + 1 && ((c & 0xc0) != 0x80)) {
|
||||
if (_last_char || (c & 0x80)) {
|
||||
putchar('.');
|
||||
putchar('.');
|
||||
putchar('.');
|
||||
} else {
|
||||
_last_char = c;
|
||||
_cur_x--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _out_string(const char *str)
|
||||
{
|
||||
while (*str)
|
||||
_out_char(*str++);
|
||||
}
|
||||
|
||||
/* non-negative integers only */
|
||||
static unsigned _out_int(unsigned num)
|
||||
{
|
||||
unsigned digits = 0;
|
||||
unsigned divi;
|
||||
|
||||
if (!num) {
|
||||
_out_char('0');
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* non zero case */
|
||||
for (divi = 1; num / divi; divi *= 10)
|
||||
digits++;
|
||||
|
||||
for (divi /= 10; divi; divi /= 10)
|
||||
_out_char('0' + (num / divi) % 10);
|
||||
|
||||
return digits;
|
||||
}
|
||||
|
||||
static void _out_newline(void)
|
||||
{
|
||||
if (_last_char && _cur_x == _termwidth)
|
||||
putchar(_last_char);
|
||||
_last_char = 0;
|
||||
putchar('\n');
|
||||
_cur_x = 1;
|
||||
}
|
||||
|
||||
static void _out_prefix(int depth)
|
||||
{
|
||||
int x, d;
|
||||
|
||||
for (d = 0; d < depth; d++) {
|
||||
for (x = _tree_width[d] + 1; x > 0; x--)
|
||||
_out_char(' ');
|
||||
|
||||
_out_string(d == depth - 1 ?
|
||||
!_tree_more[depth] ? _tsym->last_2 : _tsym->branch_2
|
||||
: _tree_more[d + 1] ?
|
||||
_tsym->vert_2 : _tsym->empty_2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Display tree
|
||||
*/
|
||||
static void _display_tree_attributes(struct deptree_node *node)
|
||||
{
|
||||
int attr = 0;
|
||||
const char *uuid;
|
||||
const struct dm_info *info;
|
||||
|
||||
uuid = dm_deptree_node_get_uuid(node);
|
||||
info = dm_deptree_node_get_info(node);
|
||||
|
||||
if (!info->exists)
|
||||
return;
|
||||
|
||||
if (_tree_switches[TR_ACTIVE]) {
|
||||
_out_string(attr++ ? ", " : " [");
|
||||
_out_string(info->suspended ? "SUSPENDED" : "ACTIVE");
|
||||
}
|
||||
|
||||
if (_tree_switches[TR_RW]) {
|
||||
_out_string(attr++ ? ", " : " [");
|
||||
_out_string(info->read_only ? "RO" : "RW");
|
||||
}
|
||||
|
||||
if (_tree_switches[TR_OPENCOUNT]) {
|
||||
_out_string(attr++ ? ", " : " [");
|
||||
(void) _out_int(info->open_count);
|
||||
}
|
||||
|
||||
if (_tree_switches[TR_UUID]) {
|
||||
_out_string(attr++ ? ", " : " [");
|
||||
_out_string(uuid && *uuid ? uuid : "");
|
||||
}
|
||||
|
||||
if (attr)
|
||||
_out_char(']');
|
||||
}
|
||||
|
||||
static void _display_tree_node(struct deptree_node *node, unsigned depth,
|
||||
unsigned first_child, unsigned last_child,
|
||||
unsigned has_children)
|
||||
{
|
||||
int offset;
|
||||
const char *name;
|
||||
const struct dm_info *info;
|
||||
int first_on_line = 0;
|
||||
|
||||
/* Sub-tree for targets has 2 more depth */
|
||||
if (depth + 2 > MAX_DEPTH)
|
||||
return;
|
||||
|
||||
name = dm_deptree_node_get_name(node);
|
||||
|
||||
if ((!name || !*name) && !_tree_switches[TR_DEVICE])
|
||||
return;
|
||||
|
||||
/* Indicate whether there are more nodes at this depth */
|
||||
_tree_more[depth] = !last_child;
|
||||
_tree_width[depth] = 0;
|
||||
|
||||
if (_cur_x == 1)
|
||||
first_on_line = 1;
|
||||
|
||||
if (!TR_PRINT_COMPACT || first_on_line)
|
||||
_out_prefix(depth);
|
||||
|
||||
/* Remember the starting point for compact */
|
||||
offset = _cur_x;
|
||||
|
||||
if (TR_PRINT_COMPACT && !first_on_line)
|
||||
_out_string(_tree_more[depth] ? _tsym->first_3 : _tsym->single_3);
|
||||
|
||||
/* display node */
|
||||
if (name)
|
||||
_out_string(name);
|
||||
|
||||
info = dm_deptree_node_get_info(node);
|
||||
|
||||
if (_tree_switches[TR_DEVICE]) {
|
||||
_out_string(name ? " (" : "(");
|
||||
(void) _out_int(info->major);
|
||||
_out_char(':');
|
||||
(void) _out_int(info->minor);
|
||||
_out_char(')');
|
||||
}
|
||||
|
||||
/* display additional info */
|
||||
if (TR_PRINT_ATTRIBUTE)
|
||||
_display_tree_attributes(node);
|
||||
|
||||
if (TR_PRINT_COMPACT)
|
||||
_tree_width[depth] = _cur_x - offset;
|
||||
|
||||
if (!TR_PRINT_COMPACT || !has_children)
|
||||
_out_newline();
|
||||
|
||||
if (TR_PRINT_TARGETS) {
|
||||
_tree_more[depth + 1] = has_children;
|
||||
// FIXME _display_tree_targets(name, depth + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the dependency tree
|
||||
*/
|
||||
static void _tree_walk_children(struct deptree_node *node, unsigned depth)
|
||||
{
|
||||
struct deptree_node *child, *next_child;
|
||||
void *handle = NULL;
|
||||
uint32_t inverted = _tree_switches[TR_BOTTOMUP];
|
||||
unsigned first_child = 1;
|
||||
unsigned has_children;
|
||||
|
||||
next_child = dm_deptree_next_child(&handle, node, inverted);
|
||||
|
||||
while ((child = next_child)) {
|
||||
next_child = dm_deptree_next_child(&handle, node, inverted);
|
||||
has_children =
|
||||
dm_deptree_node_num_children(child, inverted) ? 1 : 0;
|
||||
|
||||
_display_tree_node(child, depth, first_child,
|
||||
next_child ? 0 : 1, has_children);
|
||||
|
||||
if (has_children)
|
||||
_tree_walk_children(child, depth + 1);
|
||||
|
||||
first_child = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int _add_dep(int argc, char **argv, void *data)
|
||||
{
|
||||
struct dm_names *names = (struct dm_names *) data;
|
||||
|
||||
if (!dm_deptree_add_dev(_dtree, MAJOR(names->dev), MINOR(names->dev)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create and walk dependency tree
|
||||
*/
|
||||
static int _tree(int argc, char **argv, void *data)
|
||||
{
|
||||
if (!(_dtree = dm_deptree_create()))
|
||||
return 0;
|
||||
|
||||
if (!_process_all(argc, argv, _add_dep))
|
||||
return 0;
|
||||
|
||||
_tree_walk_children(dm_deptree_find_node(_dtree, 0, 0), 0);
|
||||
|
||||
dm_deptree_free(_dtree);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* List devices
|
||||
*/
|
||||
static int _ls(int argc, char **argv, void *data)
|
||||
{
|
||||
if ((_switches[TARGET_ARG] && _target) ||
|
||||
(_switches[EXEC_ARG] && _command))
|
||||
return _status(argc, argv, data);
|
||||
else if ((_switches[TREE_ARG]))
|
||||
return _tree(argc, argv, data);
|
||||
else
|
||||
return _process_all(argc, argv, _display_name);
|
||||
}
|
||||
@ -986,7 +1338,7 @@ static struct command _commands[] = {
|
||||
{"reload", "<device> [<table_file>]", 0, 2, _load},
|
||||
{"rename", "<device> <new_name>", 1, 2, _rename},
|
||||
{"message", "<device> <sector> <message>", 2, -1, _message},
|
||||
{"ls", "[--target <target_type>] [--exec <command>]", 0, 0, _ls},
|
||||
{"ls", "[--target <target_type>] [--exec <command>] [--tree]", 0, 0, _ls},
|
||||
{"info", "[<device>]", 0, 1, _info},
|
||||
{"deps", "[<device>]", 0, 1, _deps},
|
||||
{"status", "[<device>] [--target <target_type>]", 0, 1, _status},
|
||||
@ -1024,6 +1376,72 @@ static struct command *_find_command(const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _process_tree_options(const char *options)
|
||||
{
|
||||
const char *s, *end;
|
||||
struct winsize winsz;
|
||||
int len;
|
||||
|
||||
/* Symbol set default */
|
||||
if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
|
||||
_tsym = &_tsym_utf;
|
||||
else
|
||||
_tsym = &_tsym_ascii;
|
||||
|
||||
/* Default */
|
||||
_tree_switches[TR_DEVICE] = 1;
|
||||
_tree_switches[TR_TRUNCATE] = 1;
|
||||
|
||||
/* parse */
|
||||
for (s = options; s && *s; s++) {
|
||||
len = 0;
|
||||
for (end = s; *end && *end != ','; end++, len++)
|
||||
;
|
||||
if (!strncmp(s, "device", len))
|
||||
_tree_switches[TR_DEVICE] = 1;
|
||||
else if (!strncmp(s, "nodevice", len))
|
||||
_tree_switches[TR_DEVICE] = 0;
|
||||
else if (!strncmp(s, "status", len))
|
||||
_tree_switches[TR_STATUS] = 1;
|
||||
else if (!strncmp(s, "table", len))
|
||||
_tree_switches[TR_TABLE] = 1;
|
||||
else if (!strncmp(s, "active", len))
|
||||
_tree_switches[TR_ACTIVE] = 1;
|
||||
else if (!strncmp(s, "open", len))
|
||||
_tree_switches[TR_OPENCOUNT] = 1;
|
||||
else if (!strncmp(s, "uuid", len))
|
||||
_tree_switches[TR_UUID] = 1;
|
||||
else if (!strncmp(s, "rw", len))
|
||||
_tree_switches[TR_RW] = 1;
|
||||
else if (!strncmp(s, "utf", len))
|
||||
_tsym = &_tsym_utf;
|
||||
else if (!strncmp(s, "vt100", len))
|
||||
_tsym = &_tsym_vt100;
|
||||
else if (!strncmp(s, "ascii", len))
|
||||
_tsym = &_tsym_ascii;
|
||||
else if (!strncmp(s, "inverted", len))
|
||||
_tree_switches[TR_BOTTOMUP] = 1;
|
||||
else if (!strncmp(s, "compact", len))
|
||||
_tree_switches[TR_COMPACT] = 1;
|
||||
else if (!strncmp(s, "notrunc", len))
|
||||
_tree_switches[TR_TRUNCATE] = 0;
|
||||
else {
|
||||
fprintf(stderr, "Tree options not recognised: %s\n", s);
|
||||
return 0;
|
||||
}
|
||||
if (!*end)
|
||||
break;
|
||||
s = end;
|
||||
}
|
||||
|
||||
/* Truncation doesn't work well with vt100 drawing char */
|
||||
if (_tsym != &_tsym_vt100)
|
||||
if (ioctl(1, TIOCGWINSZ, &winsz) >= 0 && winsz.ws_col > 3)
|
||||
_termwidth = winsz.ws_col - 3;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _process_switches(int *argc, char ***argv)
|
||||
{
|
||||
char *base, *namebase;
|
||||
@ -1043,6 +1461,7 @@ static int _process_switches(int *argc, char ***argv)
|
||||
{"notable", 0, &ind, NOTABLE_ARG},
|
||||
{"options", 1, &ind, OPTIONS_ARG},
|
||||
{"target", 1, &ind, TARGET_ARG},
|
||||
{"tree", 0, &ind, TREE_ARG},
|
||||
{"uuid", 1, &ind, UUID_ARG},
|
||||
{"verbose", 1, &ind, VERBOSE_ARG},
|
||||
{"version", 0, &ind, VERSION_ARG},
|
||||
@ -1134,6 +1553,8 @@ static int _process_switches(int *argc, char ***argv)
|
||||
_switches[NOLOCKFS_ARG]++;
|
||||
if ((ind == NOOPENCOUNT_ARG))
|
||||
_switches[NOOPENCOUNT_ARG]++;
|
||||
if ((ind == TREE_ARG))
|
||||
_switches[TREE_ARG]++;
|
||||
if ((ind == VERSION_ARG))
|
||||
_switches[VERSION_ARG]++;
|
||||
}
|
||||
@ -1148,11 +1569,15 @@ static int _process_switches(int *argc, char ***argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_switches[OPTIONS_ARG] && strcmp(_fields, "name")) {
|
||||
if (_switches[COLS_ARG] && _switches[OPTIONS_ARG] &&
|
||||
strcmp(_fields, "name")) {
|
||||
fprintf(stderr, "Only -o name is supported so far.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_switches[TREE_ARG] && !_process_tree_options(_fields))
|
||||
return 0;
|
||||
|
||||
*argv += optind;
|
||||
*argc -= optind;
|
||||
return 1;
|
||||
@ -1161,10 +1586,13 @@ static int _process_switches(int *argc, char ***argv)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct command *c;
|
||||
int r = 1;
|
||||
|
||||
(void) setlocale(LC_ALL, "");
|
||||
|
||||
if (!_process_switches(&argc, &argv)) {
|
||||
fprintf(stderr, "Couldn't process command line.\n");
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (_switches[VERSION_ARG]) {
|
||||
@ -1174,27 +1602,30 @@ int main(int argc, char **argv)
|
||||
|
||||
if (argc == 0) {
|
||||
_usage(stderr);
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(c = _find_command(argv[0]))) {
|
||||
fprintf(stderr, "Unknown command\n");
|
||||
_usage(stderr);
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (argc < c->min_args + 1 ||
|
||||
(c->max_args >= 0 && argc > c->max_args + 1)) {
|
||||
fprintf(stderr, "Incorrect number of arguments\n");
|
||||
_usage(stderr);
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
doit:
|
||||
if (!c->fn(argc, argv, NULL)) {
|
||||
fprintf(stderr, "Command failed\n");
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
r = 0;
|
||||
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user