hammer2 - Add directive to destroy bad directory entries
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 17 Sep 2017 01:17:16 +0000 (18:17 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 17 Sep 2017 01:17:16 +0000 (18:17 -0700)
* Add a directive and ioctl that is capable of destroying bad hammer2
  directory entries.  If topological corruption occurs due to a crash
  (which theoretically shouldn't be possible with HAMMER2), this directive
  allows you to destroy directory entries which do not have working inodes
  and cannot otherwise be destroyed with 'rm'.

* Sysops should only use this directive when absolutely necessary.

sbin/hammer2/Makefile
sbin/hammer2/hammer2.8
sbin/hammer2/hammer2.h
sbin/hammer2/main.c
sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_ioctl.c
sys/vfs/hammer2/hammer2_ioctl.h
sys/vfs/hammer2/hammer2_vfsops.c
sys/vfs/hammer2/hammer2_xops.c

index 8fcb418..a647f3c 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 cmd_cleanup.c cmd_info.c
+SRCS+= cmd_bulkfree.c cmd_cleanup.c cmd_info.c cmd_destroy.c
 SRCS+= print_inode.c
 MAN=   hammer2.8
 #NOMAN=        TRUE
index c27b206..7c489de 100644 (file)
@@ -88,6 +88,18 @@ The mount also enables PFS configuration scanning for that filesystem.
 Add a cluster link entry to the volume header.
 The volume header can support up to 255 link entries.
 This feature is not currently used.
+.\" ==== destroy ====
+.It Cm destroy Ar path
+Destroy the specified directory entry in a hammer2 filesystem.  This bypasses
+all normal checks and will unconditionally destroy the directory entry.
+The underlying inode is not checked and, if it does exist, its nlinks count
+is not decremented.
+This directive should only be used to destroy a corrupted directory entry
+which no longer has a working inode.
+.Pp
+Note that this command may desynchronize the system namecache for the
+specified entry.  If this happens, you may have to unmount and remount the
+filesystem.
 .\" ==== disconnect ====
 .It Cm disconnect Ar target
 Delete a cluster link entry from the volume header.
@@ -337,11 +349,14 @@ repeatability.
 .It Va vfs.hammer2.cluster_meta_read (default 1)
 Set the amount of read-ahead clustering to perform on data and meta-data
 blocks.
-.It Va vfs.hammer2.cluster_write (default 0)
-Set the amount of write-behind clustering to perform.  This is disabled by
-default in order to give temporary files a chance to be deleted before
-media writes are committed.  Enabling this reduces buffer cache stress
-but causes file writes to flush to media more quickly.
+.It Va vfs.hammer2.cluster_write (default 4)
+Set the amount of write-behind clustering to perform in buffers.  Each
+buffer represents 64KB.  The default is 4 and higher values typically do
+not improve performance.  A value of 0 disables clustered writes.
+This variable applies to the underlying media device, not to logical
+file writes, so it should not interfere with temporary file optimization.
+Generally speaking you want this enabled to generate smoothly pipelined
+writes to the media.
 .It Va vfs.hammer2.bulkfree_tps (default 5000)
 Set bulkfree's maximum scan rate.  This is primarily intended to limit
 I/O utilization on SSDs and cpu utilization when the meta-data is mostly
index 3938483..8a76b4d 100644 (file)
@@ -142,6 +142,7 @@ int cmd_stat(int ac, const char **av);
 int cmd_leaf(const char *sel_path);
 int cmd_shell(const char *hostname);
 int cmd_debugspan(const char *hostname);
+int cmd_destroy_path(int ac, const char **av);
 int cmd_chaindump(const char *path);
 int cmd_show(const char *devpath, int dofreemap);
 int cmd_rsainit(const char *dir_path);
index 27cfc7c..1d10e57 100644 (file)
@@ -173,6 +173,12 @@ main(int ac, char **av)
                        usage(1);
                }
                ecode = cmd_remote_disconnect(sel_path, av[1]);
+       } else if (strcmp(av[0], "destroy") == 0) {
+               if (ac < 2) {
+                       fprintf(stderr,
+                               "Specify one or more paths to destroy\n");
+               }
+               ecode = cmd_destroy_path(ac - 1, (const char **)(void *)&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) {
@@ -468,6 +474,8 @@ usage(int code)
                        "Debug spanning tree\n"
                "    chaindump <path>             "
                        "Dump in-memory chain topo at\n"
+               "    destroy <path>*             "
+                       "Destroy a directory entry (only use if inode bad)\n"
                "    disconnect <target>          "
                        "Del cluster link\n"
                "    hash filename*               "
index 57205b8..5d87d7f 100644 (file)
@@ -917,6 +917,10 @@ struct hammer2_xop_unlink {
        int                     dopermanent;
 };
 
+#define H2DOPERM_PERMANENT     0x01
+#define H2DOPERM_FORCE         0x02
+#define H2DOPERM_IGNINO                0x04
+
 struct hammer2_xop_nrename {
        hammer2_xop_head_t      head;
        hammer2_tid_t           lhc;
index 06687b4..5e07782 100644 (file)
@@ -62,6 +62,7 @@ static int hammer2_ioctl_debug_dump(hammer2_inode_t *ip);
 //static int hammer2_ioctl_inode_comp_rec_set(hammer2_inode_t *ip, void *data);
 //static int hammer2_ioctl_inode_comp_rec_set2(hammer2_inode_t *ip, void *data);
 static int hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data);
+static int hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data);
 
 int
 hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag,
@@ -149,6 +150,10 @@ hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag,
        case HAMMER2IOC_INODE_COMP_REC_SET2:
                error = hammer2_ioctl_inode_comp_rec_set2(ip, data);
                break;*/
+       case HAMMER2IOC_DESTROY:
+               if (error == 0)
+                       error = hammer2_ioctl_destroy(ip, data);
+               break;
        case HAMMER2IOC_DEBUG_DUMP:
                error = hammer2_ioctl_debug_dump(ip);
                break;
@@ -748,7 +753,7 @@ hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
        xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
        hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name));
        xop->isdir = 2;
-       xop->dopermanent = 2 | 1;       /* FORCE | PERMANENT */
+       xop->dopermanent = H2DOPERM_PERMANENT | H2DOPERM_FORCE;
        hammer2_xop_start(&xop->head, hammer2_xop_unlink);
 
        error = hammer2_xop_collect(&xop->head, 0);
@@ -1014,3 +1019,94 @@ failed:
        lockmgr(&hmp->bflock, LK_RELEASE);
        return error;
 }
+
+/*
+ * Unconditionally delete meta-data in a hammer2 filesystem
+ */
+static
+int
+hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data)
+{
+       hammer2_ioc_destroy_t *iocd = data;
+       hammer2_pfs_t *pmp = ip->pmp;
+       int error;
+
+       if (pmp->ronly) {
+               error = EROFS;
+               return error;
+       }
+
+       switch(iocd->cmd) {
+       case HAMMER2_DELETE_FILE:
+               /*
+                * Destroy a bad directory entry by name.  Caller must
+                * pass the directory as fd.
+                */
+               {
+               hammer2_xop_unlink_t *xop;
+
+               if (iocd->path[sizeof(iocd->path)-1]) {
+                       error = EINVAL;
+                       break;
+               }
+               if (ip->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
+                       error = EINVAL;
+                       break;
+               }
+               hammer2_pfs_memory_wait(pmp);
+               hammer2_trans_init(pmp, 0);
+               hammer2_inode_lock(ip, 0);
+
+               xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
+               hammer2_xop_setname(&xop->head, iocd->path, strlen(iocd->path));
+               xop->isdir = -1;
+               xop->dopermanent = H2DOPERM_PERMANENT |
+                                  H2DOPERM_FORCE |
+                                  H2DOPERM_IGNINO;
+               hammer2_xop_start(&xop->head, hammer2_xop_unlink);
+
+               error = hammer2_xop_collect(&xop->head, 0);
+               error = hammer2_error_to_errno(error);
+               hammer2_inode_unlock(ip);
+               hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
+               hammer2_trans_done(pmp);
+               }
+               break;
+       case HAMMER2_DELETE_INUM:
+               /*
+                * Destroy a bad inode by inode number.
+                */
+               {
+               hammer2_xop_lookup_t *xop;
+
+               if (iocd->inum < 1) {
+                       error = EINVAL;
+                       break;
+               }
+               hammer2_pfs_memory_wait(pmp);
+               hammer2_trans_init(pmp, 0);
+
+               xop = hammer2_xop_alloc(pmp->iroot, 0);
+               xop->lhc = iocd->inum;
+               hammer2_xop_start(&xop->head, hammer2_xop_lookup);
+               error = hammer2_xop_collect(&xop->head, 0);
+               if (error == 0) {
+                       ip = hammer2_inode_get(pmp, NULL,
+                                              &xop->head.cluster, -1);
+                       hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
+                       if (ip) {
+                               ip->meta.nlinks = 1;
+                               hammer2_inode_unlink_finisher(ip, 0);
+                               hammer2_inode_unlock(ip);
+                       }
+               } else {
+                       hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
+               }
+               }
+               break;
+       default:
+               error = EINVAL;
+               break;
+       }
+       return error;
+}
index 7664c7b..36a51b1 100644 (file)
@@ -137,6 +137,19 @@ struct hammer2_ioc_bulkfree {
 
 typedef struct hammer2_ioc_bulkfree hammer2_ioc_bulkfree_t;
 
+/*
+ * Unconditionally delete a hammer2 directory entry or inode number
+ */
+struct hammer2_ioc_destroy {
+       enum { HAMMER2_DELETE_NOP,
+              HAMMER2_DELETE_FILE,
+              HAMMER2_DELETE_INUM } cmd;
+       char                    path[HAMMER2_INODE_MAXNAME];
+       hammer2_key_t           inum;
+};
+
+typedef struct hammer2_ioc_destroy hammer2_ioc_destroy_t;
+
 /*
  * Ioctl list
  */
@@ -168,4 +181,9 @@ typedef struct hammer2_ioc_bulkfree hammer2_ioc_bulkfree_t;
 #define HAMMER2IOC_BULKFREE_SCAN _IOWR('h', 92, struct hammer2_ioc_bulkfree)
 #define HAMMER2IOC_BULKFREE_ASYNC _IOWR('h', 93, struct hammer2_ioc_bulkfree)
 
+/*
+ * Delete a directory entry or inode number unconditionally.
+ */
+#define HAMMER2IOC_DESTROY     _IOWR('h', 94, struct hammer2_ioc_destroy)
+
 #endif /* !_VFS_HAMMER2_IOCTL_H_ */
index 90551d1..10bba74 100644 (file)
@@ -80,7 +80,7 @@ struct lock hammer2_mntlk;
 int hammer2_debug;
 int hammer2_cluster_meta_read = 1;     /* physical read-ahead */
 int hammer2_cluster_data_read = 4;     /* physical read-ahead */
-int hammer2_cluster_write = 0;         /* bdwrite() so later inval works */
+int hammer2_cluster_write = 4;         /* physical clustering (not file vp) */
 int hammer2_dedup_enable = 1;
 int hammer2_always_compress = 0;       /* always try to compress */
 int hammer2_inval_enable = 0;
index e87ce00..cbbcc51 100644 (file)
@@ -351,8 +351,8 @@ again:
         * synchronization.
         */
        if (chain && chain->error == 0) {
-               int dopermanent = xop->dopermanent & 1;
-               int doforce = xop->dopermanent & 2;
+               int dopermanent = xop->dopermanent & H2DOPERM_PERMANENT;
+               int doforce = xop->dopermanent & H2DOPERM_FORCE;
                uint8_t type;
 
                /*
@@ -425,7 +425,8 @@ again:
         * the frontend hammer2_inode_t, nor do we try to lookup the
         * frontend hammer2_inode_t here (we are the backend!).
         */
-       if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
+       if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT &&
+           (xop->dopermanent & H2DOPERM_IGNINO) == 0) {
                int error2;
 
                lhc = chain->bref.embed.dirent.inum;