From b849de1d18b592ab4baaa6666448ebaa4a1b6036 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Thu, 24 Jan 2002 23:16:19 +0000 Subject: [PATCH] Fix the device cache to cope reasonably safely with device name changes. This should be a rare occurrence so the aim is to recover if it's straightforward to do so, otherwise just to abort the operation. If people *knowingly* change device names, they should always run vgscan afterwards. A few bytes of memory gets leaked inside a pool each time an alias has to be discarded - it's not worth restructuring the code to reuse it. More of LVM2 needs updating to pass device objects (or uuids) about instead of pathnames so that resolution of pathname->object only happens once per operation. dev_cache_get() should now always return the *current* device at the path given dev_name_confirmed() replaces dev_name() whenever it's important to know that name for the device is still current (ie when opening it). If the cache doesn't know a current name, the function fails. dev_open() guarantees that the file descriptor returned is for the dev_t of the device structure it was passed. --- lib/device/dev-cache.c | 49 ++++++++++++++++++++++++++++++++++++++++-- lib/device/dev-io.c | 7 +++++- lib/device/device.h | 2 ++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c index 5a5daf112..d8278d964 100644 --- a/lib/device/dev-cache.c +++ b/lib/device/dev-cache.c @@ -19,6 +19,7 @@ #include #include #include +#include /* * FIXME: really need to seperate names from the devices since @@ -303,10 +304,56 @@ int dev_cache_add_dir(const char *path) return 1; } +/* Check cached device name is still valid before returning it */ +/* This should be a rare occurrence */ +/* FIXME Make rest of code pass/cache struct device instead of dev_name */ +const char *dev_name_confirmed(struct device *dev) +{ + struct stat buf; + char *name; + int r; + + while ((r = stat(name = list_item(dev->aliases.n, + struct str_list)->str, &buf)) || + (buf.st_rdev != dev->dev)) { + if (r < 0) + log_sys_error("stat", name); + log_error("Path %s no longer valid for device(%d,%d)", + name, (int) MAJOR(dev->dev), (int) MINOR(dev->dev)); + + /* Remove the incorrect hash entry */ + hash_remove(_cache.names, name); + + /* Leave list alone if there isn't an alternative name */ + /* so dev_name will always find something to return. */ + /* Otherwise add the name to the correct device. */ + if (list_size(&dev->aliases) > 1) { + list_del(dev->aliases.n); + if (!r) + _insert(name, 0); + continue; + } + + log_error("Aborting - please provide new pathname for what " + "used to be %s", name); + return NULL; + } + + return dev_name(dev); +} + + struct device *dev_cache_get(const char *name, struct dev_filter *f) { + struct stat buf; struct device *d = (struct device *) hash_lookup(_cache.names, name); + /* If the entry's wrong, remove it */ + if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) { + hash_remove(_cache.names, name); + d = NULL; + } + if (!d) { _insert(name, 0); d = (struct device *) hash_lookup(_cache.names, name); @@ -353,5 +400,3 @@ struct device *dev_iter_get(struct dev_iter *iter) return NULL; } - - diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c index 8e1995c43..6f49165ff 100644 --- a/lib/device/dev-io.c +++ b/lib/device/dev-io.c @@ -65,7 +65,12 @@ int dev_get_sectsize(struct device *dev, uint32_t *size) int dev_open(struct device *dev, int flags) { struct stat buf; - const char *name = dev_name(dev); + const char *name = dev_name_confirmed(dev); + + if (!name) { + stack; + return 0; + } if (dev->fd >= 0) { log_error("Device '%s' has already been opened", name); diff --git a/lib/device/device.h b/lib/device/device.h index 427a640fb..794f7eff6 100644 --- a/lib/device/device.h +++ b/lib/device/device.h @@ -47,6 +47,8 @@ static inline const char *dev_name(struct device *dev) { return list_item(dev->aliases.n, struct str_list)->str; } +/* Return a valid device name from the alias list; NULL otherwise */ +const char *dev_name_confirmed(struct device *dev); static inline int is_lvm_partition(const char *name) { return 1;