hammer2 - Add 'info' and 'mountall' directives
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 18 Nov 2015 07:02:00 +0000 (23:02 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 18 Nov 2015 07:02:00 +0000 (23:02 -0800)
* hammer2 info - scans all block devices with hammer2 partitions and
  displays available super-root entries.

* hammer2 mountall - scans all block devices with hammer2 partitions
  and mounts their @LOCAL PFS in /var/hammer2/LOCAL.serno.s%d%c
  in the background, waiting until the mounts complete or 15 seconds
  without a status change.

  The idea here is to bring all local block devices online, allowing
  all PFS's related to the H2 block devices to become active (mounting
  one makes them all available), but still giving the system operator
  the ability to idle a block device for maintenance by dismounting
  all of its actively-mounted PFSs.

  A bit confusing, the feature will be ironed out later.

sbin/hammer2/Makefile
sbin/hammer2/cmd_info.c [new file with mode: 0644]
sbin/hammer2/cmd_pfs.c
sbin/hammer2/hammer2.8
sbin/hammer2/hammer2.h
sbin/hammer2/main.c

index f05746f..124b29e 100644 (file)
@@ -3,7 +3,7 @@ SRCS=   main.c subs.c icrc.c
 SRCS+= cmd_remote.c cmd_snapshot.c cmd_pfs.c
 SRCS+= cmd_service.c cmd_leaf.c cmd_debug.c
 SRCS+= cmd_rsa.c cmd_stat.c cmd_setcomp.c cmd_setcheck.c
-SRCS+= cmd_bulkfree.c
+SRCS+= cmd_bulkfree.c cmd_info.c
 SRCS+= print_inode.c
 MAN=   hammer2.8
 #NOMAN=        TRUE
diff --git a/sbin/hammer2/cmd_info.c b/sbin/hammer2/cmd_info.c
new file mode 100644 (file)
index 0000000..8b1b98c
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2015 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@dragonflybsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "hammer2.h"
+
+static void h2disk_check(const char *devpath,
+                   void (*callback1)(const char *, hammer2_blockref_t *, int));
+static void h2pfs_check(int fd, hammer2_blockref_t *bref,
+                   void (*callback2)(const char *, hammer2_blockref_t *, int));
+
+static void info_callback1(const char *, hammer2_blockref_t *, int);
+static void info_callback2(const char *, hammer2_blockref_t *, int);
+
+int
+cmd_info(int ac, const char **av)
+{
+       struct dirent *den;
+       char *devpath;
+       DIR *dir;
+       int i;
+
+       for (i = 0; i < ac; ++i)
+               h2disk_check(av[i], info_callback1);
+       if (ac == 0 && (dir = opendir("/dev/serno")) != NULL) {
+               while ((den = readdir(dir)) != NULL) {
+                       const char *ptr;
+                       int slice;
+                       char part;
+
+                       ptr = strrchr(den->d_name, '.');
+                       if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
+                               asprintf(&devpath, "/dev/serno/%s",
+                                        den->d_name);
+                               h2disk_check(devpath, info_callback1);
+                               free(devpath);
+                       }
+               }
+               closedir(dir);
+       }
+       return 0;
+}
+
+static
+void
+info_callback1(const char *path, hammer2_blockref_t *bref, int fd)
+{
+       printf("%s:\n", path);
+       h2pfs_check(fd, bref, info_callback2);
+}
+
+static
+void
+info_callback2(const char *pfsname,
+              hammer2_blockref_t *bref __unused, int fd __unused)
+{
+       printf("    %s\n", pfsname);
+}
+
+static void mount_callback1(const char *, hammer2_blockref_t *, int);
+static void mount_callback2(const char *, hammer2_blockref_t *, int);
+static void cmd_mountall_alarm(int signo);
+
+static int DidAlarm;
+
+int
+cmd_mountall(int ac, const char **av)
+{
+       struct dirent *den;
+       char *devpath;
+       DIR *dir;
+       int i;
+       pid_t pid;
+
+       for (i = 0; i < ac; ++i)
+               h2disk_check(av[i], mount_callback1);
+       if (ac == 0 && (dir = opendir("/dev/serno")) != NULL) {
+               while ((den = readdir(dir)) != NULL) {
+                       const char *ptr;
+                       int slice;
+                       char part;
+
+                       ptr = strrchr(den->d_name, '.');
+                       if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
+                               asprintf(&devpath, "/dev/serno/%s",
+                                        den->d_name);
+                               h2disk_check(devpath, mount_callback1);
+                               free(devpath);
+                       }
+               }
+               closedir(dir);
+       }
+       signal(SIGALRM, cmd_mountall_alarm);
+       for (;;) {
+               alarm(15);
+               pid = wait3(NULL, 0, NULL);
+               if (pid < 0 && errno == ECHILD)
+                       break;
+               if (pid < 0 && DidAlarm) {
+                       printf("Timeout waiting for mounts to complete\n");
+                       break;
+               }
+       }
+       alarm(0);
+
+       return 0;
+}
+
+static
+void
+cmd_mountall_alarm(int signo __unused)
+{
+       DidAlarm = 1;
+}
+
+static const char *mount_path;
+static const char *mount_comp;
+
+static
+void
+mount_callback1(const char *devpath, hammer2_blockref_t *bref, int fd)
+{
+       mount_path = devpath;
+       mount_comp = strrchr(devpath, '/');
+       if (mount_comp) {
+               ++mount_comp;
+               h2pfs_check(fd, bref, mount_callback2);
+       }
+}
+
+static
+void
+mount_callback2(const char *pfsname,
+               hammer2_blockref_t *bref __unused, int fd)
+{
+       char *tmp_path;
+       char *label;
+       int tfd;
+
+       if (strcmp(pfsname, "LOCAL") == 0) {
+               if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
+                       dup2(tfd, fd);
+                       close(tfd);
+               } else {
+                       perror("open(/dev/null)");
+                       exit(1);
+               }
+               asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp);
+               asprintf(&label, "%s@LOCAL", mount_path);
+               mkdir("/var/hammer2", 0700);
+               mkdir(tmp_path, 0700);
+               printf("mount %s\n", tmp_path);
+               if (fork() == 0) {
+                       execl("/sbin/mount_hammer2",
+                             "mount",
+                             label,
+                             tmp_path,
+                             NULL);
+               }
+               free(label);
+               free(tmp_path);
+       }
+}
+
+/*
+ * Support
+ */
+static
+void
+h2disk_check(const char *devpath,
+            void (*callback1)(const char *, hammer2_blockref_t *, int))
+{
+       hammer2_blockref_t broot;
+       hammer2_blockref_t best;
+       hammer2_media_data_t media;
+       struct partinfo partinfo;
+       int fd;
+       int i;
+       int best_i;
+
+       fd = open(devpath, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "Unable to open \"%s\"\n", devpath);
+               return;
+       }
+       if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
+               fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
+               goto done;
+       }
+       if (partinfo.fstype != FS_HAMMER2)
+               goto done;
+
+       /*
+        * Find the best volume header.
+        */
+       best_i = -1;
+       bzero(&best, sizeof(best));
+       for (i = 0; i < 4; ++i) {
+               bzero(&broot, sizeof(broot));
+               broot.type = HAMMER2_BREF_TYPE_VOLUME;
+               broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
+                                HAMMER2_PBUFRADIX;
+               lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 0);
+               if (read(fd, &media, HAMMER2_PBUFSIZE) ==
+                   (ssize_t)HAMMER2_PBUFSIZE &&
+                   media.voldata.magic == HAMMER2_VOLUME_ID_HBO) {
+                       broot.mirror_tid = media.voldata.mirror_tid;
+                       if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
+                               best_i = i;
+                               best = broot;
+                       }
+               }
+       }
+       if (best_i >= 0)
+               callback1(devpath, &best, fd);
+done:
+       close(fd);
+}
+
+static
+void
+h2pfs_check(int fd, hammer2_blockref_t *bref,
+           void (*callback2)(const char *, hammer2_blockref_t *, int))
+{
+       hammer2_media_data_t media;
+       hammer2_blockref_t *bscan;
+       int bcount;
+       int i;
+       size_t bytes;
+       uint32_t cv;
+
+       bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX);
+
+       {
+               hammer2_off_t io_off;
+               hammer2_off_t io_base;
+               size_t io_bytes;
+               size_t boff;
+
+               io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
+               io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
+               io_bytes = bytes;
+               boff = io_off - io_base;
+
+               io_bytes = HAMMER2_MINIOSIZE;
+               while (io_bytes + boff < bytes)
+                       io_bytes <<= 1;
+
+               if (io_bytes > sizeof(media)) {
+                       printf("(bad block size %zd)\n", bytes);
+                       return;
+               }
+               if (bref->type != HAMMER2_BREF_TYPE_DATA) {
+                       lseek(fd, io_base, 0);
+                       if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
+                               printf("(media read failed)\n");
+                               return;
+                       }
+                       if (boff)
+                               bcopy((char *)&media + boff, &media, bytes);
+               }
+       }
+
+       bscan = NULL;
+       bcount = 0;
+
+       /*
+        * Check data integrity in verbose mode, otherwise we are just doing
+        * a quick meta-data scan.  Meta-data integrity is always checked.
+        * (Also see the check above that ensures the media data is loaded,
+        * otherwise there's no data to check!).
+        */
+       if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
+               switch(HAMMER2_DEC_CHECK(bref->methods)) {
+               case HAMMER2_CHECK_NONE:
+                       break;
+               case HAMMER2_CHECK_DISABLED:
+                       break;
+               case HAMMER2_CHECK_ISCSI32:
+                       cv = hammer2_icrc32(&media, bytes);
+                       if (bref->check.iscsi32.value != cv) {
+                               printf("\t(icrc failed %02x:%08x/%08x)\n",
+                                      bref->methods,
+                                      bref->check.iscsi32.value,
+                                      cv);
+                       }
+                       break;
+               case HAMMER2_CHECK_CRC64:
+                       break;
+               case HAMMER2_CHECK_SHA192:
+                       break;
+               case HAMMER2_CHECK_FREEMAP:
+                       cv = hammer2_icrc32(&media, bytes);
+                       if (bref->check.freemap.icrc32 != cv) {
+                               printf("\t(fcrc %02x:%08x/%08x)\n",
+                                       bref->methods,
+                                       bref->check.freemap.icrc32,
+                                       cv);
+                       }
+                       break;
+               }
+       }
+
+       switch(bref->type) {
+       case HAMMER2_BREF_TYPE_EMPTY:
+               break;
+       case HAMMER2_BREF_TYPE_INODE:
+               if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
+                       if ((media.ipdata.meta.op_flags &
+                            HAMMER2_OPFLAG_DIRECTDATA) == 0) {
+                               bscan = &media.ipdata.u.blockset.blockref[0];
+                               bcount = HAMMER2_SET_COUNT;
+                       }
+               } else
+               if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
+                       callback2(media.ipdata.filename, bref, fd);
+                       bscan = NULL;
+                       bcount = 0;
+               } else {
+                       bscan = NULL;
+                       bcount = 0;
+               }
+               break;
+       case HAMMER2_BREF_TYPE_INDIRECT:
+               bscan = &media.npdata[0];
+               bcount = bytes / sizeof(hammer2_blockref_t);
+               break;
+       case HAMMER2_BREF_TYPE_DATA:
+               break;
+       case HAMMER2_BREF_TYPE_VOLUME:
+               bscan = &media.voldata.sroot_blockset.blockref[0];
+               bcount = HAMMER2_SET_COUNT;
+               break;
+       default:
+               break;
+       }
+       for (i = 0; i < bcount; ++i) {
+               if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
+                       h2pfs_check(fd, &bscan[i], callback2);
+       }
+}
index 41e2055..4e623c8 100644 (file)
 #include "hammer2.h"
 
 int
-cmd_pfs_list(const char *sel_path)
+cmd_pfs_list(int ac, const char **av)
 {
        hammer2_ioc_pfs_t pfs;
        int ecode = 0;
        int count = 0;
        int fd;
+       int i;
        uint32_t status;
        char *pfs_id_str = NULL;
 
-       if ((fd = hammer2_ioctl_handle(sel_path)) < 0)
-               return(1);
-       bzero(&pfs, sizeof(pfs));
+       for (i = 0; i < ac; ++i) {
+               if ((fd = hammer2_ioctl_handle(av[i])) < 0)
+                       return(1);
+               bzero(&pfs, sizeof(pfs));
 
-       while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
-               if (ioctl(fd, HAMMER2IOC_PFS_GET, &pfs) < 0) {
-                       perror("ioctl");
-                       ecode = 1;
-                       break;
-               }
-               if (count == 0) {
-                       printf("Type        "
-                              "ClusterId (pfs_clid)                 "
-                              "Label\n");
-               }
-               switch(pfs.pfs_type) {
-               case HAMMER2_PFSTYPE_NONE:
-                       printf("NONE        ");
-                       break;
-               case HAMMER2_PFSTYPE_CACHE:
-                       printf("CACHE       ");
-                       break;
-               case HAMMER2_PFSTYPE_SLAVE:
-                       printf("SLAVE       ");
-                       break;
-               case HAMMER2_PFSTYPE_SOFT_SLAVE:
-                       printf("SOFT_SLAVE  ");
-                       break;
-               case HAMMER2_PFSTYPE_SOFT_MASTER:
-                       printf("SOFT_MASTER ");
-                       break;
-               case HAMMER2_PFSTYPE_MASTER:
-                       switch (pfs.pfs_subtype) {
-                       case HAMMER2_PFSSUBTYPE_NONE:
-                               printf("MASTER      ");
+               while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
+                       if (ioctl(fd, HAMMER2IOC_PFS_GET, &pfs) < 0) {
+                               perror("ioctl");
+                               ecode = 1;
                                break;
-                       case HAMMER2_PFSSUBTYPE_SNAPSHOT:
-                               printf("SNAPSHOT    ");
+                       }
+                       if (count == 0) {
+                               printf("Type        "
+                                      "ClusterId (pfs_clid)                 "
+                                      "Label\n");
+                       }
+                       switch(pfs.pfs_type) {
+                       case HAMMER2_PFSTYPE_NONE:
+                               printf("NONE        ");
                                break;
-                       case HAMMER2_PFSSUBTYPE_AUTOSNAP:
-                               printf("AUTOSNAP    ");
+                       case HAMMER2_PFSTYPE_CACHE:
+                               printf("CACHE       ");
+                               break;
+                       case HAMMER2_PFSTYPE_SLAVE:
+                               printf("SLAVE       ");
+                               break;
+                       case HAMMER2_PFSTYPE_SOFT_SLAVE:
+                               printf("SOFT_SLAVE  ");
+                               break;
+                       case HAMMER2_PFSTYPE_SOFT_MASTER:
+                               printf("SOFT_MASTER ");
+                               break;
+                       case HAMMER2_PFSTYPE_MASTER:
+                               switch (pfs.pfs_subtype) {
+                               case HAMMER2_PFSSUBTYPE_NONE:
+                                       printf("MASTER      ");
+                                       break;
+                               case HAMMER2_PFSSUBTYPE_SNAPSHOT:
+                                       printf("SNAPSHOT    ");
+                                       break;
+                               case HAMMER2_PFSSUBTYPE_AUTOSNAP:
+                                       printf("AUTOSNAP    ");
+                                       break;
+                               default:
+                                       printf("MASTER(sub?)");
+                                       break;
+                               }
                                break;
                        default:
-                               printf("MASTER(sub?)");
+                               printf("%02x          ", pfs.pfs_type);
                                break;
                        }
-                       break;
-               default:
-                       printf("%02x          ", pfs.pfs_type);
-                       break;
+                       uuid_to_string(&pfs.pfs_clid, &pfs_id_str, &status);
+                       printf("%s ", pfs_id_str);
+                       free(pfs_id_str);
+                       pfs_id_str = NULL;
+                       printf("%s\n", pfs.name);
+                       ++count;
                }
-               uuid_to_string(&pfs.pfs_clid, &pfs_id_str, &status);
-               printf("%s ", pfs_id_str);
-               free(pfs_id_str);
-               pfs_id_str = NULL;
-               printf("%s\n", pfs.name);
-               ++count;
+               close(fd);
        }
-       close(fd);
 
        return (ecode);
 }
index 934ae59..86acf8a 100644 (file)
@@ -92,6 +92,21 @@ This feature is not currently used.
 .It Cm disconnect Ar target
 Delete a cluster link entry from the volume header.
 This feature is not currently used.
+.\" ==== info ====
+.It Cm info Op devpath
+Access and print the status and super-root entries for all HAMMER2
+partitions found in /dev/serno or the specified device path(s).
+The partitions do not have to be mounted.
+Note that only mounted partitions will be under active management.
+This is accomplished by mounting at least one PFS within the partition.
+Typically at least the @LOCAL PFS is mounted.
+.\" ==== mountall ====
+.It Cm mountall Op devpath
+This directive mounts the @LOCAL PFS on all HAMMER2 partitions found
+in /dev/serno, or the specified device path(s).
+The partitions are mounted as /var/hammer2/LOCAL.<id>.
+Mounts are executed in the background and this command will wait a
+limited amount of time for the mounts to complete before returning.
 .\" ==== status ====
 .It Cm status Ar path...
 Dump a list of all cluster link entries configured in the volume header.
index bc92042..71b6d8a 100644 (file)
@@ -49,6 +49,7 @@
 #include <sys/sysctl.h>
 #include <sys/udev.h>
 #include <sys/diskslice.h>
+#include <sys/dtype.h>
 #include <dmsg.h>
 #include <dirent.h>
 
@@ -123,7 +124,7 @@ int cmd_remote_disconnect(const char *sel_path, const char *url);
 int cmd_remote_status(const char *sel_path, int all_opt);
 
 int cmd_pfs_getid(const char *sel_path, const char *name, int privateid);
-int cmd_pfs_list(const char *sel_path);
+int cmd_pfs_list(int ac, const char **av);
 int cmd_pfs_create(const char *sel_path, const char *name,
                        uint8_t pfs_type, const char *uuid_str);
 int cmd_pfs_delete(const char *sel_path, const char *name);
@@ -131,6 +132,8 @@ int cmd_pfs_snapshot(const char *sel_path, const char *name, const char *label);
 
 int cmd_service(void);
 int cmd_hash(int ac, const char **av);
+int cmd_info(int ac, const char **av);
+int cmd_mountall(int ac, const char **av);
 int cmd_stat(int ac, const char **av);
 int cmd_leaf(const char *sel_path);
 int cmd_shell(const char *hostname);
index e69fc26..d21c8e3 100644 (file)
@@ -175,6 +175,10 @@ main(int ac, char **av)
                ecode = cmd_remote_disconnect(sel_path, av[1]);
        } else if (strcmp(av[0], "hash") == 0) {
                ecode = cmd_hash(ac - 1, (const char **)(void *)&av[1]);
+       } else if (strcmp(av[0], "info") == 0) {
+               ecode = cmd_info(ac - 1, (const char **)(void *)&av[1]);
+       } else if (strcmp(av[0], "mountall") == 0) {
+               ecode = cmd_mountall(ac - 1, (const char **)(void *)&av[1]);
        } else if (strcmp(av[0], "status") == 0) {
                /*
                 * Get status of PFS and its connections (-a for all PFSs)
@@ -209,11 +213,12 @@ main(int ac, char **av)
                /*
                 * List all PFSs
                 */
-               if (ac > 2) {
-                       fprintf(stderr, "pfs-list: too many arguments\n");
-                       usage(1);
+               if (ac >= 2) {
+                       ecode = cmd_pfs_list(ac - 1,
+                                            (const char **)(void *)&av[1]);
+               } else {
+                       ecode = cmd_pfs_list(1, &sel_path);
                }
-               ecode = cmd_pfs_list((ac == 2) ? av[1] : sel_path);
        } else if (strcmp(av[0], "pfs-create") == 0) {
                /*
                 * Create new PFS using pfs_type
@@ -451,9 +456,13 @@ usage(int code)
                        "Del cluster link\n"
                "    hash filename*               "
                        "Print directory hash\n"
+               "    info [devpath...]            "
+                       "Info on all offline or online H2 partitions\n"
+               "    mountall [devpath...]        "
+                       "Mount @LOCAL for all H2 partitions\n"
                "    status [<path>...]           "
-                       "Report cluster status\n"
-               "    pfs-list [<path>]            "
+                       "Report active cluster status\n"
+               "    pfs-list [<path>...]         "
                        "List PFSs\n"
                "    pfs-clid <label>             "
                        "Print cluster id for specific PFS\n"