summaryrefslogtreecommitdiff
path: root/lib/device/online.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/device/online.c')
-rw-r--r--lib/device/online.c306
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);
+
+
+}