diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM index d6fb80702..3c0636856 100644 --- a/WHATS_NEW_DM +++ b/WHATS_NEW_DM @@ -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 ============================= diff --git a/libdm/.exported_symbols b/libdm/.exported_symbols index 689378460..eadcd465b 100644 --- a/libdm/.exported_symbols +++ b/libdm/.exported_symbols @@ -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 diff --git a/libdm/Makefile.in b/libdm/Makefile.in index 4753fbfa3..d90a77bd2 100644 --- a/libdm/Makefile.in +++ b/libdm/Makefile.in @@ -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) diff --git a/libdm/datastruct/bitset.c b/libdm/datastruct/bitset.c new file mode 100644 index 000000000..4962245c8 --- /dev/null +++ b/libdm/datastruct/bitset.c @@ -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); +} diff --git a/libdm/datastruct/bitset.h b/libdm/datastruct/bitset.h new file mode 100644 index 000000000..939e4932b --- /dev/null +++ b/libdm/datastruct/bitset.h @@ -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 + +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 diff --git a/libdm/datastruct/hash.c b/libdm/datastruct/hash.c new file mode 100644 index 000000000..6753ca7ab --- /dev/null +++ b/libdm/datastruct/hash.c @@ -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); +} diff --git a/libdm/datastruct/hash.h b/libdm/datastruct/hash.h new file mode 100644 index 000000000..0f745e532 --- /dev/null +++ b/libdm/datastruct/hash.h @@ -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 diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c index f7700d11c..b0340386b 100644 --- a/libdm/ioctl/libdm-iface.c +++ b/libdm/ioctl/libdm-iface.c @@ -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; } diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h index adb60ae03..cfee2625c 100644 --- a/libdm/libdevmapper.h +++ b/libdm/libdevmapper.h @@ -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 */ diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c index 20f2702aa..e9ea1bcc0 100644 --- a/libdm/libdm-common.c +++ b/libdm/libdm-common.c @@ -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); } } diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c new file mode 100644 index 000000000..daa593616 --- /dev/null +++ b/libdm/libdm-deptree.c @@ -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 +#include + +#include + +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; +} + diff --git a/libdm/mm/dbg_malloc.c b/libdm/mm/dbg_malloc.c new file mode 100644 index 000000000..6c2b4d127 --- /dev/null +++ b/libdm/mm/dbg_malloc.c @@ -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 +#include + +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 diff --git a/libdm/mm/dbg_malloc.h b/libdm/mm/dbg_malloc.h new file mode 100644 index 000000000..3f6d08ddb --- /dev/null +++ b/libdm/mm/dbg_malloc.h @@ -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 +#include + +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 diff --git a/libdm/mm/pool-debug.c b/libdm/mm/pool-debug.c new file mode 100644 index 000000000..de3525dce --- /dev/null +++ b/libdm/mm/pool-debug.c @@ -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; +} diff --git a/libdm/mm/pool-fast.c b/libdm/mm/pool-fast.c new file mode 100644 index 000000000..56102e570 --- /dev/null +++ b/libdm/mm/pool-fast.c @@ -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; +} diff --git a/libdm/mm/pool.c b/libdm/mm/pool.c new file mode 100644 index 000000000..daefcf875 --- /dev/null +++ b/libdm/mm/pool.c @@ -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; +} diff --git a/libdm/mm/pool.h b/libdm/mm/pool.h new file mode 100644 index 000000000..01700460a --- /dev/null +++ b/libdm/mm/pool.h @@ -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 +#include + +/* + * 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 diff --git a/tools/dmsetup.c b/tools/dmsetup.c index 5740823b7..366d68eb7 100644 --- a/tools/dmsetup.c +++ b/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 #include #include +#include +#include + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#if HAVE_TERMIOS_H +# include +#endif #ifdef HAVE_GETOPTLONG # include @@ -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", " []", 0, 2, _load}, {"rename", " ", 1, 2, _rename}, {"message", " ", 2, -1, _message}, - {"ls", "[--target ] [--exec ]", 0, 0, _ls}, + {"ls", "[--target ] [--exec ] [--tree]", 0, 0, _ls}, {"info", "[]", 0, 1, _info}, {"deps", "[]", 0, 1, _deps}, {"status", "[] [--target ]", 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; }