/* * Copyright (c) 2019 Tomohiro Kusumi * Copyright (c) 2019 The DragonFly Project * All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Matthew Dillon * * 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. */ // # gcc -Wall -g -I../../sys -I../hammer2 ../../sys/libkern/icrc32.c ../hammer2/subs.c ../hammer2/ondisk.c ./destroy.c -o destroy #include #include #include #include #include #include #include #include #include #include #include #include #include "hammer2_subs.h" static int modify_blockref(const hammer2_volume_data_t *, int, hammer2_blockref_t *, hammer2_blockref_t *); static int modify_inode(const hammer2_blockref_t *, hammer2_media_data_t *, size_t); static int modify_dirent_embedded(int, hammer2_blockref_t *); static int modify_dirent(int, hammer2_blockref_t *, const hammer2_blockref_t *, hammer2_media_data_t *, size_t); static hammer2_tid_t src_inode = 0; static hammer2_tid_t dst_inode = 0; static const char *src_dirent = NULL; static const char *dst_dirent = NULL; static bool ForceOpt = false; static int destroy_blockref(uint8_t type) { bool failed = false; int i; for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { hammer2_volume_data_t voldata; hammer2_blockref_t broot; hammer2_off_t off; ssize_t ret; memset(&broot, 0, sizeof(broot)); broot.type = type; broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; off = broot.data_off & ~HAMMER2_OFF_MASK_RADIX; if (lseek(hammer2_get_root_volume_fd(), off - hammer2_get_root_volume_offset(), SEEK_SET) == -1) { perror("lseek"); return -1; } ret = read(hammer2_get_root_volume_fd(), &voldata, HAMMER2_PBUFSIZE); if (ret == HAMMER2_PBUFSIZE) { fprintf(stdout, "zone.%d %016jx\n", i, (uintmax_t)broot.data_off); if (modify_blockref(&voldata, -1, &broot, NULL) == -1) failed = true; } else if (ret == -1) { perror("read"); return -1; } else { fprintf(stderr, "Failed to read volume header\n"); return -1; } } return failed ? -1 : 0; } static int read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media, size_t *media_bytes) { hammer2_off_t io_off, io_base; size_t bytes, io_bytes, boff; ssize_t ret; int fd; bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); if (bytes) bytes = (size_t)1 << bytes; if (media_bytes) *media_bytes = bytes; if (!bytes) return 0; io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1); boff = io_off - io_base; io_bytes = HAMMER2_LBUFSIZE; while (io_bytes + boff < bytes) io_bytes <<= 1; if (io_bytes > sizeof(*media)) { fprintf(stderr, "Bad I/O bytes\n"); return -1; } fd = hammer2_get_volume_fd(io_off); if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET) == -1) { perror("lseek"); return -1; } ret = read(fd, media, io_bytes); if (ret == -1) { perror("read"); return -1; } else if (ret != (ssize_t)io_bytes) { fprintf(stderr, "Failed to read media\n"); return -1; } if (boff) memmove(media, (char *)media + boff, bytes); return 0; } static int write_media(const hammer2_blockref_t *bref, const hammer2_media_data_t *media, size_t media_bytes) { hammer2_off_t io_off, io_base; char buf[HAMMER2_PBUFSIZE]; size_t bytes, io_bytes, boff; ssize_t ret; int fd; bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); if (bytes) bytes = (size_t)1 << bytes; assert(bytes != 0); assert(bytes == media_bytes); io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1); boff = io_off - io_base; io_bytes = HAMMER2_LBUFSIZE; while (io_bytes + boff < bytes) io_bytes <<= 1; if (io_bytes > sizeof(buf)) { fprintf(stderr, "Bad I/O bytes\n"); return -1; } fd = hammer2_get_volume_fd(io_off); if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET) == -1) { perror("lseek"); return -1; } if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) { perror("read"); return -1; } memcpy(buf + boff, media, media_bytes); if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET) == -1) { perror("lseek"); return -1; } ret = write(fd, buf, io_bytes); if (ret == -1) { perror("write"); return -1; } else if (ret != (ssize_t)io_bytes) { fprintf(stderr, "Failed to write media\n"); return -1; } if (fsync(fd) == -1) { perror("fsync"); return -1; } return 0; } static int modify_blockref(const hammer2_volume_data_t *voldata, int bi, hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref) { hammer2_media_data_t media; hammer2_blockref_t *bscan; int i, bcount, namlen; size_t bytes; if (read_media(bref, &media, &bytes) == -1) return -1; switch (bref->type) { case HAMMER2_BREF_TYPE_INODE: if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) { bscan = &media.ipdata.u.blockset.blockref[0]; bcount = HAMMER2_SET_COUNT; } else { bscan = NULL; bcount = 0; } if (src_inode && media.ipdata.meta.inum == src_inode) if (modify_inode(bref, &media, bytes) == -1) return -1; break; case HAMMER2_BREF_TYPE_INDIRECT: bscan = &media.npdata[0]; bcount = bytes / sizeof(hammer2_blockref_t); break; case HAMMER2_BREF_TYPE_DIRENT: bscan = NULL; bcount = 0; namlen = bref->embed.dirent.namlen; if (src_dirent && namlen == strlen(src_dirent)) { if (namlen <= sizeof(bref->check.buf) && !memcmp(bref->check.buf, src_dirent, namlen)) { if (modify_dirent_embedded(bi, prev_bref) == -1) return -1; } else if (!memcmp(media.buf, src_dirent, namlen)) { if (modify_dirent(bi, prev_bref, bref, &media, bytes) == -1) return -1; } } break; case HAMMER2_BREF_TYPE_VOLUME: bscan = &media.voldata.sroot_blockset.blockref[0]; bcount = HAMMER2_SET_COUNT; break; default: bscan = NULL; bcount = 0; break; } for (i = 0; i < bcount; ++i) if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) if (modify_blockref(voldata, i, &bscan[i], bref) == -1) return -1; return 0; } static int modify_inode(const hammer2_blockref_t *bref, hammer2_media_data_t *media, size_t media_bytes) { assert(src_inode == media->ipdata.meta.inum); if (ForceOpt) { media->ipdata.meta.inum = dst_inode; if (write_media(bref, media, media_bytes) == -1) return -1; } printf("%sinode# 0x%016jx -> 0x%016jx\n", ForceOpt ? "Modified " : "", src_inode, dst_inode); return 0; } static int modify_dirent_embedded(int bi, hammer2_blockref_t *prev_bref) { hammer2_media_data_t bscan_media; hammer2_blockref_t *bscan; size_t bytes; if (read_media(prev_bref, &bscan_media, &bytes) == -1) return -1; assert(bytes); switch (prev_bref->type) { case HAMMER2_BREF_TYPE_INODE: bscan = &bscan_media.ipdata.u.blockset.blockref[bi]; break; case HAMMER2_BREF_TYPE_INDIRECT: bscan = &bscan_media.npdata[bi]; break; default: assert(0); break; } assert(!memcmp(src_dirent, bscan->check.buf, strlen(src_dirent))); if (strlen(dst_dirent) > sizeof(bscan->check.buf)) { fprintf(stderr, "embedded dirent %s (%d bytes) can't exceed " "%lu bytes\n", dst_dirent, (int)strlen(dst_dirent), sizeof(bscan->check.buf)); return -1; } if (ForceOpt) { memset(bscan->check.buf, 0, sizeof(bscan->check.buf)); memcpy(bscan->check.buf, dst_dirent, strlen(dst_dirent)); bscan->embed.dirent.namlen = strlen(dst_dirent); bscan->key = dirhash((const unsigned char*)dst_dirent, strlen(dst_dirent)); if (write_media(prev_bref, &bscan_media, bytes) == -1) return -1; } printf("%sembedded dirent %s (%d bytes) -> %s (%d bytes)\n", ForceOpt ? "Modified " : "", src_dirent, (int)strlen(src_dirent), dst_dirent, (int)strlen(dst_dirent)); return 0; } static int modify_dirent(int bi, hammer2_blockref_t *prev_bref, const hammer2_blockref_t *bref, hammer2_media_data_t *media, size_t media_bytes) { hammer2_media_data_t bscan_media; hammer2_blockref_t *bscan; size_t bytes; assert(!memcmp(src_dirent, media->buf, strlen(src_dirent))); if (read_media(prev_bref, &bscan_media, &bytes) == -1) return -1; assert(bytes); switch (prev_bref->type) { case HAMMER2_BREF_TYPE_INODE: bscan = &bscan_media.ipdata.u.blockset.blockref[bi]; break; case HAMMER2_BREF_TYPE_INDIRECT: bscan = &bscan_media.npdata[bi]; break; default: assert(0); break; } if (memcmp(bref, bscan, sizeof(*bref))) { fprintf(stderr, "Blockref contents mismatch\n"); return -1; } if (strlen(dst_dirent) > sizeof(media->buf)) { fprintf(stderr, "dirent %s (%d bytes) can't exceed %lu bytes\n", dst_dirent, (int)strlen(dst_dirent), sizeof(media->buf)); return -1; } if (strlen(dst_dirent) <= sizeof(bscan->check.buf)) { fprintf(stderr, "dirent %s (%d bytes) must exceed %lu bytes\n", dst_dirent, (int)strlen(dst_dirent), sizeof(bscan->check.buf)); return -1; } if (ForceOpt) { memset(media->buf, 0, sizeof(media->buf)); memcpy(media->buf, dst_dirent, strlen(dst_dirent)); bscan->embed.dirent.namlen = strlen(dst_dirent); bscan->key = dirhash((const unsigned char*)dst_dirent, strlen(dst_dirent)); if (write_media(bref, media, media_bytes) == -1) return -1; if (write_media(prev_bref, &bscan_media, bytes) == -1) { memset(media->buf, 0, sizeof(media->buf)); memcpy(media->buf, src_dirent, strlen(src_dirent)); if (write_media(bref, media, media_bytes) == -1) return -1; return -1; } } printf("%sdirent %s (%d bytes) -> %s (%d bytes)\n", ForceOpt ? "Modified " : "", src_dirent, (int)strlen(src_dirent), dst_dirent, (int)strlen(dst_dirent)); return 0; } static int init_args(int argc, char **argv, const char **devpathp) { const char *devpath, *type; *devpathp = devpath = argv[0]; type = argv[1]; if (!strcmp(type, "inode")) { errno = 0; src_inode = strtoull(argv[2], NULL, 16); if (errno == ERANGE && src_inode == ULLONG_MAX) { perror("strtoull"); return -1; } if (src_inode == 0) { fprintf(stderr, "Invalid src inode# %ju\n", (uintmax_t)src_inode); return -1; } errno = 0; dst_inode = strtoull(argv[3], NULL, 16); if (errno == ERANGE && dst_inode == ULLONG_MAX) { perror("strtoull"); return -1; } if (dst_inode == 0) { fprintf(stderr, "Invalid dst inode# %ju\n", (uintmax_t)dst_inode); return -1; } if (src_inode == dst_inode) { fprintf(stderr, "src equals dst\n"); return -1; } printf("%s 0x%016jx 0x%016jx\n", devpath, (uintmax_t)src_inode, (uintmax_t)dst_inode); } else if (!strcmp(type, "dirent")) { src_dirent = argv[2]; if (strlen(src_dirent) > HAMMER2_PBUFSIZE) { fprintf(stderr, "src dirent too long\n"); return -1; } dst_dirent = argv[3]; if (strlen(dst_dirent) > HAMMER2_PBUFSIZE) { fprintf(stderr, "dst dirent too long\n"); return -1; } if (!strcmp(src_dirent, dst_dirent)) { fprintf(stderr, "src equals dst\n"); return -1; } printf("%s %s %s\n", devpath, src_dirent, dst_dirent); } else { fprintf(stderr, "Invalid blockref type %s\n", type); return -1; } return 0; } int main(int argc, char **argv) { int ch; const char *binpath = argv[0]; const char *devpath; while ((ch = getopt(argc, argv, "f")) != -1) { switch(ch) { case 'f': ForceOpt = true; break; default: break; } } argc -= optind; argv += optind; if (argc < 4) { fprintf(stderr, "%s [-f] special type src dst\n", binpath); exit(1); } if (init_args(argc, argv, &devpath) == -1) exit(1); hammer2_init_volumes(devpath, 0); if (destroy_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1) exit(1); hammer2_cleanup_volumes(); return 0; }