#include #include #include #include #include #include #include #include #include #include const char *prog_name = "nukerg"; static void usage(void) { printf("%s zeroes a list of gfs2 resource groups or rindex entries.\n", prog_name); printf("\n"); printf("Usage:\n"); printf(" %s -r |-i /dev/your/device\n", prog_name); printf("\n"); printf(" -r: Destroy resource groups\n"); printf(" -i: Destroy resource group index (rindex) entries\n"); printf("\n"); printf("num_list: A comma- or space-separated list of resource group or rindex\n"); printf(" numbers (0-indexed) to be destroyed. Use '*' for all.\n"); printf("\n"); printf("Both -r and -i can be specified. Resource groups will necessarily\n"); printf("be destroyed before rindex entries.\n"); } #define RGNUMS_SIZE (256) struct opts { const char *device; unsigned rgnums[RGNUMS_SIZE]; size_t rgnum_count; unsigned rinums[RGNUMS_SIZE]; size_t rinum_count; unsigned got_help:1; unsigned got_device:1; unsigned got_rgnums:1; unsigned got_rinums:1; }; static int parse_uint(char *str, unsigned *uint) { long long tmpll; char *endptr; if (str == NULL || *str == '\0') return 1; errno = 0; tmpll = strtoll(str, &endptr, 10); if (errno || tmpll < 0 || tmpll > UINT_MAX || *endptr != '\0') return 1; *uint = (unsigned)tmpll; return 0; } #define ALL_RGS ((unsigned)-1) static size_t parse_uint_list(char *str, unsigned *list, char **tokp, size_t nleft) { const char *delim = " ,"; size_t i; for (i = 0, *tokp = strsep(&str, delim); *tokp != NULL; i++, *tokp = strsep(&str, delim)) { int ret; if (i >= nleft) return (size_t)-1; /* List would overflow */ /* Allow * to denote "all" */ if (strcmp(*tokp, "*") == 0) { list[0] = ALL_RGS; return 1; } ret = parse_uint(*tokp, &list[i]); if (ret != 0) return 0; /* Invalid token, *tokp points to it */ } return i; /* Will be 0 if str is NULL or empty (*tokp is NULL in that case) */ } static int parse_ri_list(char *str, struct opts *opts) { unsigned *rinums = opts->rinums + opts->rinum_count; size_t nleft = RGNUMS_SIZE - opts->rinum_count; char *errtok = NULL; size_t n; n = parse_uint_list(str, rinums, &errtok, nleft); if (n == 0) { if (errtok == NULL) fprintf(stderr, "No rindex entries given\n"); else fprintf(stderr, "Invalid rindex entry: '%s'\n", errtok); return 1; } else if (n == (size_t)-1) { fprintf(stderr, "Too many rindex entries\n"); return 1; } opts->rinum_count += n; opts->got_rinums = 1; return 0; } static int parse_rg_list(char *str, struct opts *opts) { unsigned *rgnums = opts->rgnums + opts->rgnum_count; size_t nleft = RGNUMS_SIZE - opts->rgnum_count; char *errtok = NULL; size_t n; n = parse_uint_list(optarg, rgnums, &errtok, nleft); if (n == 0) { if (errtok == NULL) fprintf(stderr, "No resource groups given\n"); else fprintf(stderr, "Invalid resource group number: '%s'\n", errtok); return 1; } else if (n == (size_t)-1) { fprintf(stderr, "Too many resource group numbers\n"); return 1; } opts->rgnum_count += n; opts->got_rgnums = 1; return 0; } static int opts_get(int argc, char *argv[], struct opts *opts) { int c; memset(opts, 0, sizeof(*opts)); while (1) { c = getopt(argc, argv, "-hi:r:"); if (c == -1) break; switch (c) { case 'h': opts->got_help = 1; usage(); return 0; case 'i': if (parse_ri_list(optarg, opts)) return 1; break; case 'r': if (parse_rg_list(optarg, opts)) return 1; break; case 1: if (opts->got_device) { fprintf(stderr, "More than one device specified. "); fprintf(stderr, "Try -h for help.\n"); return 1; } opts->device = optarg; opts->got_device = 1; break; case '?': default: usage(); return 1; } } return 0; } static int nuke_rgs(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, unsigned *rgnums, size_t count) { struct gfs2_rgrp blankrg; lgfs2_rgrp_t rg; unsigned i; memset(&blankrg, 0, sizeof(blankrg)); for (rg = lgfs2_rgrp_first(rgs), i = 0; rg; rg = lgfs2_rgrp_next(rg), i++) { const struct gfs2_rindex *ri = lgfs2_rgrp_index(rg); unsigned j; for (j = 0; j < count; j++) { uint64_t addr = ri->ri_addr; off_t off = addr * sdp->bsize; ssize_t bytes; if (i != rgnums[j] && rgnums[j] != ALL_RGS) continue; printf("Nuking rg #%u at block 0x%"PRIx64"\n", i, addr); bytes = pwrite(sdp->device_fd, &blankrg, sizeof(blankrg), off); if (bytes != sizeof(blankrg)) { fprintf(stderr, "Write failed (%u bytes): %s", (unsigned)bytes, strerror(errno)); return 1; } } } return 0; } static int nuke_ris(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, unsigned *rinums, size_t count) { struct gfs2_rindex blankri; lgfs2_rgrp_t rg; unsigned i; memset(&blankri, 0, sizeof(blankri)); for (rg = lgfs2_rgrp_first(rgs), i = 0; rg; rg = lgfs2_rgrp_next(rg), i++) { unsigned j; for (j = 0; j < count; j++) { int bytes = 0; uint64_t off; if (i != rinums[j] && rinums[j] != ALL_RGS) continue; printf("Nuking rindex entry %u.\n", i); off = i * sizeof(struct gfs2_rindex); bytes = gfs2_writei(sdp->md.riinode, &blankri, off, sizeof(struct gfs2_rindex)); if (bytes != sizeof(struct gfs2_rindex)) { fprintf(stderr, "Write failed (%d bytes): %s", bytes, strerror(errno)); return 1; } } } return 0; } static lgfs2_rgrps_t read_rindex(struct gfs2_sbd *sdp) { lgfs2_rgrps_t rgs; unsigned rgcount; unsigned i; gfs2_lookupi(sdp->master_dir, "rindex", 6, &sdp->md.riinode); if (sdp->md.riinode == NULL) { perror("Failed to look up rindex"); return NULL; } rgs = lgfs2_rgrps_init(sdp, 0, 0); if (rgs == NULL) { fprintf(stderr, "Failed to initialize resource group set: %s\n", strerror(errno)); return NULL; } rgcount = sdp->md.riinode->i_di.di_size / sizeof(struct gfs2_rindex); for (i = 0; i < rgcount; i++) { const struct gfs2_rindex *ri; ri = lgfs2_rindex_read_one(sdp->md.riinode, rgs, i); if (ri == NULL) { fprintf(stderr, "Failed to read rindex entry %u: %s\n", i, strerror(errno)); return NULL; } } printf("%u rindex entries found.\n", i); return rgs; } static int fill_super_block(struct gfs2_sbd *sdp) { sdp->sd_sb.sb_bsize = GFS2_BASIC_BLOCK; sdp->bsize = sdp->sd_sb.sb_bsize; if (compute_constants(sdp) != 0) { fprintf(stderr, "Failed to compute file system constants.\n"); return 1; } if (read_sb(sdp) != 0) { perror("Failed to read superblock\n"); return 1; } sdp->master_dir = lgfs2_inode_read(sdp, sdp->sd_sb.sb_master_dir.no_addr); if (sdp->master_dir == NULL) { fprintf(stderr, "Failed to read master directory inode.\n"); return 1; } return 0; } int main(int argc, char **argv) { struct gfs2_sbd sbd; lgfs2_rgrps_t rgs; struct opts opts; int ret; memset(&sbd, 0, sizeof(sbd)); ret = opts_get(argc, argv, &opts); if (ret != 0 || opts.got_help) exit(ret); if (!opts.got_device) { fprintf(stderr, "No device specified.\n"); usage(); exit(1); } if (!opts.got_rgnums && !opts.got_rinums) { fprintf(stderr, "No resource groups or rindex entries specified.\n"); usage(); exit(1); } if ((sbd.device_fd = open(opts.device, O_RDWR)) < 0) { perror(opts.device); exit(1); } if (fill_super_block(&sbd) != 0) exit(1); rgs = read_rindex(&sbd); if (rgs == NULL) exit(1); if (opts.got_rgnums && nuke_rgs(&sbd, rgs, opts.rgnums, opts.rgnum_count) != 0) exit(1); if (opts.got_rinums && nuke_ris(&sbd, rgs, opts.rinums, opts.rinum_count) != 0) exit(1); inode_put(&sbd.md.riinode); inode_put(&sbd.master_dir); lgfs2_rgrps_free(&rgs); fsync(sbd.device_fd); close(sbd.device_fd); exit(0); } /* This function is for libgfs2's sake. */ void print_it(const char *label, const char *fmt, const char *fmt2, ...) {}