| /* |
| * Copyright (C) 2014 Red Hat, Inc. All rights reserved. |
| * |
| * This file is part of LVM2. |
| * |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "lib.h" |
| #include "filter.h" |
| #include "activate.h" /* device_is_usable */ |
| #ifdef UDEV_SYNC_SUPPORT |
| #include <libudev.h> |
| #include "dev-ext-udev-constants.h" |
| #endif |
| |
| static const char *_too_small_to_hold_pv_msg = "Too small to hold a PV"; |
| |
| static int _native_check_pv_min_size(struct device *dev) |
| { |
| uint64_t size; |
| int ret = 0; |
| |
| /* Check it's accessible */ |
| if (!dev_open_readonly_quiet(dev)) { |
| log_debug_devs("%s: Skipping: open failed", dev_name(dev)); |
| return 0; |
| } |
| |
| /* Check it's not too small */ |
| if (!dev_get_size(dev, &size)) { |
| log_debug_devs("%s: Skipping: dev_get_size failed", dev_name(dev)); |
| goto out; |
| } |
| |
| if (size < pv_min_size()) { |
| log_debug_devs("%s: Skipping: %s", dev_name(dev), |
| _too_small_to_hold_pv_msg); |
| goto out; |
| } |
| |
| ret = 1; |
| out: |
| if (!dev_close(dev)) |
| stack; |
| |
| return ret; |
| } |
| |
| #ifdef UDEV_SYNC_SUPPORT |
| static int _udev_check_pv_min_size(struct device *dev) |
| { |
| struct dev_ext *ext; |
| const char *size_str; |
| char *endp; |
| uint64_t size; |
| |
| if (!(ext = dev_ext_get(dev))) |
| return_0; |
| |
| if (!(size_str = udev_device_get_sysattr_value((struct udev_device *)ext->handle, DEV_EXT_UDEV_SYSFS_ATTR_SIZE))) { |
| log_debug_devs("%s: Skipping: failed to get size from sysfs [%s:%p]", |
| dev_name(dev), dev_ext_name(dev), dev->ext.handle); |
| return 0; |
| } |
| |
| errno = 0; |
| size = strtoull(size_str, &endp, 10); |
| if (errno || !endp || *endp) { |
| log_debug_devs("%s: Skipping: failed to parse size from sysfs [%s:%p]", |
| dev_name(dev), dev_ext_name(dev), dev->ext.handle); |
| return 0; |
| } |
| |
| if (size < pv_min_size()) { |
| log_debug_devs("%s: Skipping: %s [%s:%p]", dev_name(dev), |
| _too_small_to_hold_pv_msg, |
| dev_ext_name(dev), dev->ext.handle); |
| return 0; |
| } |
| |
| return 1; |
| } |
| #else |
| static int _udev_check_pv_min_size(struct device *dev) |
| { |
| return 1; |
| } |
| #endif |
| |
| static int _check_pv_min_size(struct device *dev) |
| { |
| if (dev->ext.src == DEV_EXT_NONE) |
| return _native_check_pv_min_size(dev); |
| |
| if (dev->ext.src == DEV_EXT_UDEV) |
| return _udev_check_pv_min_size(dev); |
| |
| log_error(INTERNAL_ERROR "Missing hook for PV min size check " |
| "using external device info source %s", dev_ext_name(dev)); |
| |
| return 0; |
| } |
| |
| static int _passes_usable_filter(struct dev_filter *f, struct device *dev) |
| { |
| filter_mode_t mode = *((filter_mode_t *) f->private); |
| struct dev_usable_check_params ucp = {0}; |
| int r = 1; |
| |
| /* further checks are done on dm devices only */ |
| if (dm_is_dm_major(MAJOR(dev->dev))) { |
| switch (mode) { |
| case FILTER_MODE_NO_LVMETAD: |
| ucp.check_empty = 1; |
| ucp.check_blocked = 1; |
| ucp.check_suspended = ignore_suspended_devices(); |
| ucp.check_error_target = 1; |
| ucp.check_reserved = 1; |
| break; |
| case FILTER_MODE_PRE_LVMETAD: |
| ucp.check_empty = 1; |
| ucp.check_blocked = 1; |
| ucp.check_suspended = 0; |
| ucp.check_error_target = 1; |
| ucp.check_reserved = 1; |
| break; |
| case FILTER_MODE_POST_LVMETAD: |
| ucp.check_empty = 0; |
| ucp.check_blocked = 1; |
| ucp.check_suspended = ignore_suspended_devices(); |
| ucp.check_error_target = 0; |
| ucp.check_reserved = 0; |
| break; |
| } |
| |
| if (!(r = device_is_usable(dev, ucp))) |
| log_debug_devs("%s: Skipping unusable device.", dev_name(dev)); |
| } |
| |
| if (r) { |
| /* check if the device is not too small to hold a PV */ |
| switch (mode) { |
| case FILTER_MODE_NO_LVMETAD: |
| /* fall through */ |
| case FILTER_MODE_PRE_LVMETAD: |
| r = _check_pv_min_size(dev); |
| break; |
| case FILTER_MODE_POST_LVMETAD: |
| /* nothing to do here */ |
| break; |
| } |
| } |
| |
| return r; |
| } |
| |
| static void _usable_filter_destroy(struct dev_filter *f) |
| { |
| if (f->use_count) |
| log_error(INTERNAL_ERROR "Destroying usable device filter while in use %u times.", f->use_count); |
| |
| dm_free(f->private); |
| dm_free(f); |
| } |
| |
| struct dev_filter *usable_filter_create(struct dev_types *dt __attribute__((unused)), filter_mode_t mode) |
| { |
| struct dev_filter *f; |
| |
| if (!(f = dm_zalloc(sizeof(struct dev_filter)))) { |
| log_error("Usable device filter allocation failed"); |
| return NULL; |
| } |
| |
| f->passes_filter = _passes_usable_filter; |
| f->destroy = _usable_filter_destroy; |
| f->use_count = 0; |
| if (!(f->private = dm_zalloc(sizeof(filter_mode_t)))) { |
| log_error("Usable device filter mode allocation failed"); |
| dm_free(f); |
| return NULL; |
| } |
| *((filter_mode_t *) f->private) = mode; |
| |
| log_debug_devs("Usable device filter initialised."); |
| |
| return f; |
| } |