📄 libdm-deptree.c
字号:
/* * Copyright (C) 2005-2007 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 <stdarg.h>#include <sys/param.h>#include <linux/dm-ioctl.h>#define MAX_TARGET_PARAMSIZE 500000/* FIXME Fix interface so this is used only by LVM */#define UUID_PREFIX "LVM-"/* Supported segment types */enum { SEG_ERROR, SEG_LINEAR, SEG_MIRRORED, SEG_SNAPSHOT, SEG_SNAPSHOT_ORIGIN, SEG_STRIPED, SEG_ZERO,};/* FIXME Add crypt and multipath support */struct { unsigned type; const char *target;} dm_segtypes[] = { { SEG_ERROR, "error" }, { SEG_LINEAR, "linear" }, { SEG_MIRRORED, "mirror" }, { SEG_SNAPSHOT, "snapshot" }, { SEG_SNAPSHOT_ORIGIN, "snapshot-origin" }, { SEG_STRIPED, "striped" }, { SEG_ZERO, "zero"},};/* Some segment types have a list of areas of other devices attached */struct seg_area { struct list list; struct dm_tree_node *dev_node; uint64_t offset;};/* Per-segment properties */struct load_segment { struct list list; unsigned type; uint64_t size; unsigned area_count; /* Linear + Striped + Mirrored */ struct list areas; /* Linear + Striped + Mirrored */ uint32_t stripe_size; /* Striped */ int persistent; /* Snapshot */ uint32_t chunk_size; /* Snapshot */ struct dm_tree_node *cow; /* Snapshot */ struct dm_tree_node *origin; /* Snapshot + Snapshot origin */ struct dm_tree_node *log; /* Mirror */ uint32_t region_size; /* Mirror */ unsigned clustered; /* Mirror */ unsigned mirror_area_count; /* Mirror */ uint32_t flags; /* Mirror log */ char *uuid; /* Clustered mirror log */};/* Per-device properties */struct load_properties { int read_only; uint32_t major; uint32_t minor; unsigned segment_count; struct list segs; const char *new_name;};/* Two of these used to join two nodes with uses and used_by. */struct dm_tree_link { struct list list; struct dm_tree_node *node;};struct dm_tree_node { struct dm_tree *dtree; 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 */ int activation_priority; /* 0 gets activated first */ void *context; /* External supplied context */ struct load_properties props; /* For creation/table (re)load */};struct dm_tree { struct dm_pool *mem; struct dm_hash_table *devs; struct dm_hash_table *uuids; struct dm_tree_node root; int skip_lockfs; /* 1 skips lockfs (for non-snapshots) */ int no_flush; /* 1 sets noflush (mirrors/multipath) */};/* FIXME Consider exporting this */static int _dm_snprintf(char *buf, size_t bufsize, const char *format, ...){ int n; va_list ap; va_start(ap, format); n = vsnprintf(buf, bufsize, format, ap); va_end(ap); if (n < 0 || (n > (int) bufsize - 1)) return -1; return n;}struct dm_tree *dm_tree_create(void){ struct dm_tree *dtree; if (!(dtree = dm_malloc(sizeof(*dtree)))) { log_error("dm_tree_create malloc failed"); return NULL; } memset(dtree, 0, sizeof(*dtree)); dtree->root.dtree = dtree; list_init(&dtree->root.uses); list_init(&dtree->root.used_by); dtree->skip_lockfs = 0; dtree->no_flush = 0; if (!(dtree->mem = dm_pool_create("dtree", 1024))) { log_error("dtree pool creation failed"); dm_free(dtree); return NULL; } if (!(dtree->devs = dm_hash_create(8))) { log_error("dtree hash creation failed"); dm_pool_destroy(dtree->mem); dm_free(dtree); return NULL; } if (!(dtree->uuids = dm_hash_create(32))) { log_error("dtree uuid hash creation failed"); dm_hash_destroy(dtree->devs); dm_pool_destroy(dtree->mem); dm_free(dtree); return NULL; } return dtree;}void dm_tree_free(struct dm_tree *dtree){ if (!dtree) return; dm_hash_destroy(dtree->uuids); dm_hash_destroy(dtree->devs); dm_pool_destroy(dtree->mem); dm_free(dtree);}static int _nodes_are_linked(struct dm_tree_node *parent, struct dm_tree_node *child){ struct dm_tree_link *dlink; list_iterate_items(dlink, &parent->uses) if (dlink->node == child) return 1; return 0;}static int _link(struct list *list, struct dm_tree_node *node){ struct dm_tree_link *dlink; if (!(dlink = dm_pool_alloc(node->dtree->mem, sizeof(*dlink)))) { log_error("dtree link allocation failed"); return 0; } dlink->node = node; list_add(list, &dlink->list); return 1;}static int _link_nodes(struct dm_tree_node *parent, struct dm_tree_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 dm_tree_node *node){ struct dm_tree_link *dlink; list_iterate_items(dlink, list) if (dlink->node == node) { list_del(&dlink->list); break; }}static void _unlink_nodes(struct dm_tree_node *parent, struct dm_tree_node *child){ if (!_nodes_are_linked(parent, child)) return; _unlink(&parent->uses, child); _unlink(&child->used_by, parent);}static int _add_to_toplevel(struct dm_tree_node *node){ return _link_nodes(&node->dtree->root, node);}static void _remove_from_toplevel(struct dm_tree_node *node){ return _unlink_nodes(&node->dtree->root, node);}static int _add_to_bottomlevel(struct dm_tree_node *node){ return _link_nodes(node, &node->dtree->root);}static void _remove_from_bottomlevel(struct dm_tree_node *node){ return _unlink_nodes(node, &node->dtree->root);}static int _link_tree_nodes(struct dm_tree_node *parent, struct dm_tree_node *child){ /* Don't link to root node if child already has a parent */ if ((parent == &parent->dtree->root)) { if (dm_tree_node_num_children(child, 1)) return 1; } else _remove_from_toplevel(child); if ((child == &child->dtree->root)) { if (dm_tree_node_num_children(parent, 0)) return 1; } else _remove_from_bottomlevel(parent); return _link_nodes(parent, child);}static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree, const char *name, const char *uuid, struct dm_info *info, void *context){ struct dm_tree_node *node; uint64_t dev; if (!(node = dm_pool_zalloc(dtree->mem, sizeof(*node)))) { log_error("_create_dm_tree_node alloc failed"); return NULL; } node->dtree = dtree; node->name = name; node->uuid = uuid; node->info = *info; node->context = context; node->activation_priority = 0; list_init(&node->uses); list_init(&node->used_by); list_init(&node->props.segs); dev = MKDEV(info->major, info->minor); if (!dm_hash_insert_binary(dtree->devs, (const char *) &dev, sizeof(dev), node)) { log_error("dtree node hash insertion failed"); dm_pool_free(dtree->mem, node); return NULL; } if (uuid && *uuid && !dm_hash_insert(dtree->uuids, uuid, node)) { log_error("dtree uuid hash insertion failed"); dm_hash_remove_binary(dtree->devs, (const char *) &dev, sizeof(dev)); dm_pool_free(dtree->mem, node); return NULL; } return node;}static struct dm_tree_node *_find_dm_tree_node(struct dm_tree *dtree, uint32_t major, uint32_t minor){ uint64_t dev = MKDEV(major, minor); return dm_hash_lookup_binary(dtree->devs, (const char *) &dev, sizeof(dev));}static struct dm_tree_node *_find_dm_tree_node_by_uuid(struct dm_tree *dtree, const char *uuid){ struct dm_tree_node *node; if ((node = dm_hash_lookup(dtree->uuids, uuid))) return node; if (strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) return NULL; return dm_hash_lookup(dtree->uuids, uuid + sizeof(UUID_PREFIX) - 1);}static int _deps(struct dm_task **dmt, struct dm_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; info->live_table = 0; info->inactive_table = 0; info->read_only = 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)) { log_error("_deps: failed to set major for (%" PRIu32 ":%" PRIu32 ")", major, minor); goto failed; } if (!dm_task_set_minor(*dmt, minor)) { log_error("_deps: failed to set minor for (%" PRIu32 ":%" PRIu32 ")", major, minor); goto failed; } if (!dm_task_run(*dmt)) { log_error("_deps: task run failed for (%" PRIu32 ":%" PRIu32 ")", major, minor); goto failed; } if (!dm_task_get_info(*dmt, info)) { log_error("_deps: failed to get info for (%" PRIu32 ":%" PRIu32 ")", major, minor); goto failed; } if (!info->exists) { *name = ""; *uuid = ""; *deps = NULL; } else { if (info->major != major) { log_error("Inconsistent dtree major number: %u != %u", major, info->major); goto failed; } if (info->minor != minor) { log_error("Inconsistent dtree minor number: %u != %u", minor, info->minor); goto failed; } if (!(*name = dm_pool_strdup(mem, dm_task_get_name(*dmt)))) { log_error("name pool_strdup failed"); goto failed; } if (!(*uuid = dm_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 struct dm_tree_node *_add_dev(struct dm_tree *dtree, struct dm_tree_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 dm_tree_node *node = NULL; uint32_t i; int new = 0; /* Already in tree? */ if (!(node = _find_dm_tree_node(dtree, major, minor))) { if (!_deps(&dmt, dtree->mem, major, minor, &name, &uuid, &info, &deps)) return_NULL; if (!(node = _create_dm_tree_node(dtree, name, uuid, &info, NULL))) goto_out; new = 1; } if (!_link_tree_nodes(parent, node)) { node = NULL; goto_out; } /* If node was already in tree, no need to recurse. */ if (!new) goto out; /* Can't recurse if not a mapped device or there are no dependencies */ if (!node->info.exists || !deps->count) { if (!_add_to_bottomlevel(node)) { stack; node = NULL; } goto out; } /* Add dependencies to tree */ for (i = 0; i < deps->count; i++) if (!_add_dev(dtree, node, MAJOR(deps->device[i]), MINOR(deps->device[i]))) { node = NULL; goto_out; }out: if (dmt) dm_task_destroy(dmt); return node;}static int _node_clear_table(struct dm_tree_node *dnode){ struct dm_task *dmt; struct dm_info *info; const char *name; int r; if (!(info = &dnode->info)) { log_error("_node_clear_table failed: missing info"); return 0; } if (!(name = dm_tree_node_get_name(dnode))) { log_error("_node_clear_table failed: missing name"); return 0; } /* Is there a table? */ if (!info->exists || !info->inactive_table) return 1; log_verbose("Clearing inactive table %s (%" PRIu32 ":%" PRIu32 ")", name, info->major, info->minor); if (!(dmt = dm_task_create(DM_DEVICE_CLEAR))) { dm_task_destroy(dmt); log_error("Table clear dm_task creation failed for %s", name); return 0; } if (!dm_task_set_major(dmt, info->major) || !dm_task_set_minor(dmt, info->minor)) { log_error("Failed to set device number for %s table clear", name); dm_task_destroy(dmt); return 0; } r = dm_task_run(dmt); if (!dm_task_get_info(dmt, info)) { log_error("_node_clear_table failed: info missing after running task for %s", name); r = 0; } dm_task_destroy(dmt); return r;}struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *dtree, const char *name, const char *uuid, uint32_t major, uint32_t minor, int read_only, int clear_inactive, void *context){ struct dm_tree_node *dnode; struct dm_info info; const char *name2; const char *uuid2; /* Do we need to add node to tree? */ if (!(dnode = dm_tree_find_node_by_uuid(dtree, uuid))) { if (!(name2 = dm_pool_strdup(dtree->mem, name))) { log_error("name pool_strdup failed"); return NULL; } if (!(uuid2 = dm_pool_strdup(dtree->mem, uuid))) { log_error("uuid pool_strdup failed"); return NULL; } info.major = 0; info.minor = 0; info.exists = 0; info.live_table = 0; info.inactive_table = 0; info.read_only = 0; if (!(dnode = _create_dm_tree_node(dtree, name2, uuid2, &info, context))) return_NULL; /* Attach to root node until a table is supplied */ if (!_add_to_toplevel(dnode) || !_add_to_bottomlevel(dnode)) return_NULL; dnode->props.major = major; dnode->props.minor = minor; dnode->props.new_name = NULL; } else if (strcmp(name, dnode->name)) { /* Do we need to rename node? */ if (!(dnode->props.new_name = dm_pool_strdup(dtree->mem, name))) { log_error("name pool_strdup failed"); return 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -