diff options
Diffstat (limited to 'lib/device/online.c')
-rw-r--r-- | lib/device/online.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/lib/device/online.c b/lib/device/online.c new file mode 100644 index 000000000..9e459f935 --- /dev/null +++ b/lib/device/online.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2021 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 "base/memory/zalloc.h" +#include "lib/misc/lib.h" +#include "lib/commands/toolcontext.h" +#include "lib/device/device.h" +#include "lib/device/device_id.h" +#include "lib/device/online.h" + +#include <dirent.h> + +static char *_vgname_in_pvid_file_buf(char *buf) +{ + char *p, *n; + + /* + * file contains: + * <major>:<minor>\n + * vg:<vgname>\n\0 + */ + + if (!(p = strchr(buf, '\n'))) + return NULL; + + p++; /* skip \n */ + + if (*p && !strncmp(p, "vg:", 3)) { + if ((n = strchr(p, '\n'))) + *n = '\0'; + return p + 3; + } + return NULL; +} + +#define MAX_PVID_FILE_SIZE 512 + +int online_pvid_file_read(char *path, int *major, int *minor, char *vgname) +{ + char buf[MAX_PVID_FILE_SIZE] = { 0 }; + char *name; + int fd, rv; + + fd = open(path, O_RDONLY); + if (fd < 0) { + log_warn("Failed to open %s", path); + return 0; + } + + rv = read(fd, buf, sizeof(buf) - 1); + if (close(fd)) + log_sys_debug("close", path); + if (!rv || rv < 0) { + log_warn("No info in %s", path); + return 0; + } + buf[rv] = 0; /* \0 terminated buffer */ + + if (sscanf(buf, "%d:%d", major, minor) != 2) { + log_warn("No device numbers in %s", path); + return 0; + } + + /* vgname points to an offset in buf */ + if ((name = _vgname_in_pvid_file_buf(buf))) + strncpy(vgname, name, NAME_LEN); + else + log_debug("No vgname in %s", path); + + return 1; +} + +/* + * When a PV goes offline, remove the vg online file for that VG + * (even if other PVs for the VG are still online). This means + * that the vg will be activated again when it becomes complete. + */ + +void online_vg_file_remove(const char *vgname) +{ + char path[PATH_MAX]; + + if (dm_snprintf(path, sizeof(path), "%s/%s", VGS_ONLINE_DIR, vgname) < 0) { + log_error("Path %s/%s is too long.", VGS_ONLINE_DIR, vgname); + return; + } + + log_debug("Unlink vg online: %s", path); + + if (unlink(path) && (errno != ENOENT)) + log_sys_debug("unlink", path); +} + +int online_vg_file_create(struct cmd_context *cmd, const char *vgname) +{ + char path[PATH_MAX]; + int fd; + + if (dm_snprintf(path, sizeof(path), "%s/%s", VGS_ONLINE_DIR, vgname) < 0) { + log_error_pvscan(cmd, "Path %s/%s is too long.", VGS_ONLINE_DIR, vgname); + return 0; + } + + log_debug("Create vg online: %s", path); + + fd = open(path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) { + log_debug("Failed to create %s: %d", path, errno); + return 0; + } + + /* We don't care about syncing, these files are not even persistent. */ + + if (close(fd)) + log_sys_debug("close", path); + + return 1; +} + +int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname) +{ + char path[PATH_MAX]; + char buf[MAX_PVID_FILE_SIZE] = { 0 }; + char file_vgname[NAME_LEN]; + int file_major = 0, file_minor = 0; + int major, minor; + int fd; + int rv; + int len; + int len1 = 0; + int len2 = 0; + + major = (int)MAJOR(dev->dev); + minor = (int)MINOR(dev->dev); + + if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, dev->pvid) < 0) { + log_error_pvscan(cmd, "Path %s/%s is too long.", PVS_ONLINE_DIR, dev->pvid); + return 0; + } + + if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) { + log_error_pvscan(cmd, "Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor); + return 0; + } + + if (vgname) { + if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) { + log_print_pvscan(cmd, "Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname); + /* can still continue without vgname */ + len2 = 0; + } + } + + len = len1 + len2; + + log_debug("Create pv online: %s %d:%d %s", path, major, minor, dev_name(dev)); + + fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) { + if (errno == EEXIST) + goto check_duplicate; + log_error_pvscan(cmd, "Failed to create online file for %s path %s error %d", dev_name(dev), path, errno); + return 0; + } + + while (len > 0) { + rv = write(fd, buf, len); + if (rv < 0) { + /* file exists so it still works in part */ + log_warn("Cannot write online file for %s to %s error %d", + dev_name(dev), path, errno); + if (close(fd)) + log_sys_debug("close", path); + return 1; + } + len -= rv; + } + + /* We don't care about syncing, these files are not even persistent. */ + + if (close(fd)) + log_sys_debug("close", path); + + return 1; + +check_duplicate: + + /* + * If a PVID online file already exists for this PVID, check if the + * file contains a different device number, and if so we may have a + * duplicate PV. + * + * FIXME: disable autoactivation of the VG somehow? + * The VG may or may not already be activated when a dupicate appears. + * Perhaps write a new field in the pv online or vg online file? + */ + + memset(file_vgname, 0, sizeof(file_vgname)); + + online_pvid_file_read(path, &file_major, &file_minor, file_vgname); + + if ((file_major == major) && (file_minor == minor)) { + log_debug("Existing online file for %d:%d", major, minor); + return 1; + } + + /* Don't know how vgname might not match, but it's not good so fail. */ + + if ((file_major != major) || (file_minor != minor)) + log_error_pvscan(cmd, "PV %s is duplicate for PVID %s on %d:%d and %d:%d.", + dev_name(dev), dev->pvid, major, minor, file_major, file_minor); + + if (file_vgname[0] && vgname && strcmp(file_vgname, vgname)) + log_error_pvscan(cmd, "PV %s has unexpected VG %s vs %s.", + dev_name(dev), vgname, file_vgname); + + return 0; +} + +int online_pvid_file_exists(const char *pvid) +{ + char path[PATH_MAX] = { 0 }; + struct stat buf; + int rv; + + if (dm_snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid) < 0) { + log_debug(INTERNAL_ERROR "Path %s/%s is too long.", PVS_ONLINE_DIR, pvid); + return 0; + } + + log_debug("Check pv online: %s", path); + + rv = stat(path, &buf); + if (!rv) { + log_debug("Check pv online %s: yes", pvid); + return 1; + } + log_debug("Check pv online %s: no", pvid); + return 0; +} + +void online_dir_setup(struct cmd_context *cmd) +{ + struct stat st; + int rv; + + if (!stat(DEFAULT_RUN_DIR, &st)) + goto do_pvs; + + log_debug("Creating run_dir."); + dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR); + rv = mkdir(DEFAULT_RUN_DIR, 0755); + dm_prepare_selinux_context(NULL, 0); + + if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st)) + log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno); + +do_pvs: + if (!stat(PVS_ONLINE_DIR, &st)) + goto do_vgs; + + log_debug("Creating pvs_online_dir."); + dm_prepare_selinux_context(PVS_ONLINE_DIR, S_IFDIR); + rv = mkdir(PVS_ONLINE_DIR, 0755); + dm_prepare_selinux_context(NULL, 0); + + if ((rv < 0) && stat(PVS_ONLINE_DIR, &st)) + log_error_pvscan(cmd, "Failed to create %s %d", PVS_ONLINE_DIR, errno); + +do_vgs: + if (!stat(VGS_ONLINE_DIR, &st)) + goto do_lookup; + + log_debug("Creating vgs_online_dir."); + dm_prepare_selinux_context(VGS_ONLINE_DIR, S_IFDIR); + rv = mkdir(VGS_ONLINE_DIR, 0755); + dm_prepare_selinux_context(NULL, 0); + + if ((rv < 0) && stat(VGS_ONLINE_DIR, &st)) + log_error_pvscan(cmd, "Failed to create %s %d", VGS_ONLINE_DIR, errno); + +do_lookup: + if (!stat(PVS_LOOKUP_DIR, &st)) + return; + + log_debug("Creating pvs_lookup_dir."); + dm_prepare_selinux_context(PVS_LOOKUP_DIR, S_IFDIR); + rv = mkdir(PVS_LOOKUP_DIR, 0755); + dm_prepare_selinux_context(NULL, 0); + + if ((rv < 0) && stat(PVS_LOOKUP_DIR, &st)) + log_error_pvscan(cmd, "Failed to create %s %d", PVS_LOOKUP_DIR, errno); + + +} |