From def89e9f2091840ecdd676794405418c29e66362 Mon Sep 17 00:00:00 2001 From: Fabian Keil Date: Thu, 4 Nov 2021 16:35:00 +0100 Subject: [PATCH 289/310] geli: Implement a "search" subcommand ... which can be used to make a provider accessible again if, for example, the partition data is lost but the geli metadata on disk is still valid. Example usage: [fk@steffen ~]$ sudo geli search /dev/ada1 Searching for GEOM::ELI metadata on /dev/ada1. Found GEOM::ELI meta data at offset 4000785927680. Metadata found on /dev/ada1: magic: GEOM::ELI version: 7 flags: 0x0 ealgo: AES-XTS keylen: 128 provsize: 3985542778880 sectorsize: 512 keys: 0x01 iterations: 447024 Salt: 23[...]05 Master Key: 20[...]29 MD5 hash: 9fdffeb97ca6b34379512a9191cbfaeb Try making the data attachable with: gnop create -o 15243149312 -s 3985542778880 /dev/ada1 [fk@steffen ~]$ sudo gnop create -o 15243149312 -s 3985542778880 /dev/ada1 [fk@steffen ~]$ sudo geli attach /dev/ada1.nop Enter passphrase: [fk@steffen ~]$ sudo zpool import dpool [fk@steffen ~]$ sudo zpool status -v dpool pool: dpool state: ONLINE status: One or more devices has experienced an error resulting in data corruption. Applications may be affected. action: Restore the file in question if possible. Otherwise restore the entire pool from backup. see: https://illumos.org/msg/ZFS-8000-8A scan: scrub repaired 0 in 1 days 09:00:47 with 66 errors on 2021-12-07 18:40:55 config: NAME STATE READ WRITE CKSUM dpool ONLINE 0 0 0 ada1.nop.eli ONLINE 0 0 0 errors: Permanent errors have been detected in the following files: dpool/ggated/cloudia2:<0x1> dpool/ggated/cloudia2@2017-04-20_21:27:<0x1> Obtained from: ElectroBSD --- lib/geom/eli/geom_eli.c | 103 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/lib/geom/eli/geom_eli.c b/lib/geom/eli/geom_eli.c index 49f0b2bd5c3d..0c6fe547a3f3 100644 --- a/lib/geom/eli/geom_eli.c +++ b/lib/geom/eli/geom_eli.c @@ -84,6 +84,7 @@ static void eli_resize(struct gctl_req *req); static void eli_version(struct gctl_req *req); static void eli_clear(struct gctl_req *req); static void eli_dump(struct gctl_req *req); +static void eli_search(struct gctl_req *req); static int eli_backup_create(struct gctl_req *req, const char *prov, const char *file); @@ -109,6 +110,7 @@ static int eli_backup_create(struct gctl_req *req, const char *prov, * version [prov ...] * clear [-v] prov ... * dump [-v] prov ... + * search [-v] prov ... */ struct g_command class_commands[] = { { "init", G_FLAG_VERBOSE, eli_main, @@ -283,6 +285,9 @@ struct g_command class_commands[] = { { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, "[-v] prov ..." }, + { "search", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, + "[-v] prov ..." + }, G_CMD_SENTINEL }; @@ -345,6 +350,8 @@ eli_main(struct gctl_req *req, unsigned int flags) eli_restore(req); else if (strcmp(name, "resize") == 0) eli_resize(req); + else if (strcmp(name, "search") == 0) + eli_search(req); else if (strcmp(name, "version") == 0) eli_version(req); else if (strcmp(name, "dump") == 0) @@ -656,6 +663,62 @@ eli_metadata_read(struct gctl_req *req, const char *prov, return (0); } +static int +eli_metadata_search(struct gctl_req *req, const char *prov, + struct g_eli_metadata *md, off_t *metadata_offset) +{ + unsigned char sector[sizeof(struct g_eli_metadata)]; + int error; + + if (g_get_sectorsize(prov) == 0) { + int fd; + + /* This is a file probably. */ + fd = open(prov, O_RDONLY); + if (fd == -1) { + gctl_error(req, "Cannot open %s: %s.", prov, + strerror(errno)); + return (-1); + } + if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { + gctl_error(req, "Cannot read metadata from %s: %s.", + prov, strerror(errno)); + close(fd); + return (-1); + } + close(fd); + } else { + /* This is a GEOM provider. */ + error = g_metadata_search(prov, sector, sizeof(sector), + G_ELI_MAGIC, metadata_offset); + if (error != 0) { + gctl_error(req, "Cannot read metadata from %s: %s.", + prov, strerror(error)); + return (-1); + } + } + error = eli_metadata_decode(sector, md); + switch (error) { + case 0: + break; + case EOPNOTSUPP: + gctl_error(req, + "Provider's %s metadata version %u is too new.\n" + "geli: The highest supported version is %u.", + prov, (unsigned int)md->md_version, G_ELI_VERSION); + return (-1); + case EINVAL: + gctl_error(req, "Inconsistent provider's %s metadata.", prov); + return (-1); + default: + gctl_error(req, + "Unexpected error while decoding provider's %s metadata: %s.", + prov, strerror(error)); + return (-1); + } + return (0); +} + static int eli_metadata_store(struct gctl_req *req, const char *prov, struct g_eli_metadata *md) @@ -2012,3 +2075,43 @@ eli_dump(struct gctl_req *req) printf("\n"); } } + +static void +eli_suggest_gnop_command(const char *name, struct g_eli_metadata *md, + off_t metadata_offset) +{ + long long unsigned gnop_size = md->md_provsize; + long long unsigned gnop_offset = metadata_offset - md->md_provsize + + md->md_sectorsize; + + printf("\nTry making the data attachable with: " + "gnop create -o %llu -s %llu %s", + gnop_offset, gnop_size, name); +} + +static void +eli_search(struct gctl_req *req) +{ + struct g_eli_metadata md; + const char *name; + off_t metadata_offset; + int i, nargs; + + nargs = gctl_get_int(req, "nargs"); + if (nargs < 1) { + gctl_error(req, "Too few arguments."); + return; + } + + for (i = 0; i < nargs; i++) { + name = gctl_get_ascii(req, "arg%d", i); + if (eli_metadata_search(NULL, name, &md, &metadata_offset) == -1) { + gctl_error(req, "Not fully done."); + continue; + } + printf("Metadata found on %s:\n", name); + eli_metadata_dump(&md); + eli_suggest_gnop_command(name, &md, metadata_offset); + printf("\n"); + } +} -- 2.37.1