From f6aebb44e85767963b73e878ca532ec4b16a526e Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sat, 16 Sep 2017 18:17:16 -0700 Subject: [PATCH] hammer2 - Add directive to destroy bad directory entries * 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 | 2 +- sbin/hammer2/hammer2.8 | 25 ++++++++-- sbin/hammer2/hammer2.h | 1 + sbin/hammer2/main.c | 8 ++++ sys/vfs/hammer2/hammer2.h | 4 ++ sys/vfs/hammer2/hammer2_ioctl.c | 98 +++++++++++++++++++++++++++++++++++++++- sys/vfs/hammer2/hammer2_ioctl.h | 18 ++++++++ sys/vfs/hammer2/hammer2_vfsops.c | 2 +- sys/vfs/hammer2/hammer2_xops.c | 7 +-- 9 files changed, 154 insertions(+), 11 deletions(-) diff --git a/sbin/hammer2/Makefile b/sbin/hammer2/Makefile index 8fcb418d88..a647f3c9c9 100644 --- a/sbin/hammer2/Makefile +++ b/sbin/hammer2/Makefile @@ -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 diff --git a/sbin/hammer2/hammer2.8 b/sbin/hammer2/hammer2.8 index c27b2061fb..7c489de5c0 100644 --- a/sbin/hammer2/hammer2.8 +++ b/sbin/hammer2/hammer2.8 @@ -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 diff --git a/sbin/hammer2/hammer2.h b/sbin/hammer2/hammer2.h index 3938483810..8a76b4d80e 100644 --- a/sbin/hammer2/hammer2.h +++ b/sbin/hammer2/hammer2.h @@ -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); diff --git a/sbin/hammer2/main.c b/sbin/hammer2/main.c index 27cfc7c85b..1d10e57ab4 100644 --- a/sbin/hammer2/main.c +++ b/sbin/hammer2/main.c @@ -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 " "Dump in-memory chain topo at\n" + " destroy * " + "Destroy a directory entry (only use if inode bad)\n" " disconnect " "Del cluster link\n" " hash filename* " diff --git a/sys/vfs/hammer2/hammer2.h b/sys/vfs/hammer2/hammer2.h index 57205b8d4f..5d87d7f42f 100644 --- a/sys/vfs/hammer2/hammer2.h +++ b/sys/vfs/hammer2/hammer2.h @@ -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; diff --git a/sys/vfs/hammer2/hammer2_ioctl.c b/sys/vfs/hammer2/hammer2_ioctl.c index 06687b43f5..5e077829fc 100644 --- a/sys/vfs/hammer2/hammer2_ioctl.c +++ b/sys/vfs/hammer2/hammer2_ioctl.c @@ -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; +} diff --git a/sys/vfs/hammer2/hammer2_ioctl.h b/sys/vfs/hammer2/hammer2_ioctl.h index 7664c7b181..36a51b1058 100644 --- a/sys/vfs/hammer2/hammer2_ioctl.h +++ b/sys/vfs/hammer2/hammer2_ioctl.h @@ -138,6 +138,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_ */ diff --git a/sys/vfs/hammer2/hammer2_vfsops.c b/sys/vfs/hammer2/hammer2_vfsops.c index 90551d15d3..10bba740bc 100644 --- a/sys/vfs/hammer2/hammer2_vfsops.c +++ b/sys/vfs/hammer2/hammer2_vfsops.c @@ -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; diff --git a/sys/vfs/hammer2/hammer2_xops.c b/sys/vfs/hammer2/hammer2_xops.c index e87ce002c5..cbbcc51ea7 100644 --- a/sys/vfs/hammer2/hammer2_xops.c +++ b/sys/vfs/hammer2/hammer2_xops.c @@ -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; -- 2.11.4.GIT