/* This fixes up a filesystem which has bad data in the last block * of the file out beyond eof. This confuses some applications. * * Copyright Stephen Lord 2002, All Rights Reserved. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define SCANDEPTH 42 int numfs = 32; char *fs = NULL; int verbose = 0, readonly = 0, quiet = 0, force = 0; int filecount = 0, fixedcount = 0, problemcount = 0; char *fixedmsg; static struct option options[] = { {"help", 0, 0, 'h'}, {"version", 0, 0, 'V'}, {"verbose", 0, 0, 'v'}, {"quiet", 0, 0, 'q'}, {"force", 0, 0, 'f'}, {"no-act", 0, 0, 'n'}, {"all", 0, 0, 'a'}, {0, 0, 0, 0} }; void *xalloc(void *m, size_t n) { m = realloc(m, n); if(!m) { perror("malloc()"); exit(2); } return m; } char *xstrdup(const char *s) { char *m; if(!s) s = ""; m = strdup(s); if(!m) { perror("malloc()"); exit(2); } return m; } static int checker( const char *file, const struct stat64 *st, int flag, struct FTW *_) { int fd, blk; char *ptr, *ptr2, *ptr3; off64_t off, len; if(flag != FTW_F) return 0; blk = st->st_blksize; len = st->st_size % blk; off = st->st_size - len; filecount++; if(!len) return 0; fd = open(file, O_RDWR|O_LARGEFILE); if(fd < 0) { if(verbose) perror(file); return problemcount++, 0; } ptr = mmap64(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, off); close(fd); if(ptr == MAP_FAILED) { if(verbose) perror(file); return problemcount++, 0; } ptr2 = ptr + len; ptr3 = ptr + blk; for(; ptr2 < ptr3; ptr2++) { if(*ptr2 != 0) { if(verbose) printf("%s\n", file); if(!readonly) memset(ptr + len, 0, blk - len); fixedcount++; break; } } munmap(ptr, len); return 0; } void catcher(int sig) { printf("\rInterrupted: %d files scanned %d files %s %d errors\n", filecount, fixedcount, fixedmsg, problemcount); exit(128+sig); } void printer(int sig) { printf("\r%d files scanned %d files %s %d errors\033[K\r", filecount, fixedcount, fixedmsg, problemcount); if(!readonly) sync(); alarm(1); } char *isbadfs(struct fstab *fs) { struct stat64 st; if(lstat64(fs->fs_file, &st)) return strerror(errno); if(!strcasecmp(fs->fs_type, "sw")) return "swap partition"; if(strcasecmp(fs->fs_vfstype, "xfs")) return "not an XFS filesystem"; if(!strcasecmp(fs->fs_type, "ro")) return "readonly filesystem"; if(!strcasecmp(fs->fs_type, "xx")) return "to be ignored"; return NULL; } char *isbadfile(char *f) { struct fstab *fs = NULL; char *s, *reason = NULL; if(!f || !*f) f = "."; s = xalloc(NULL, PATH_MAX); if(realpath(f, s)) { f = xalloc(s, strlen(s)+1); for(;;) { fs = getfsfile(f); if(fs) break; s = strrchr(f, '/'); if(!s) break; s[s == f && s[1]] = '\0'; } } else { reason = strerror(errno); } free(f); if(reason) return reason; if(!fs) return "can't find the filesystem"; if(force) return NULL; return isbadfs(fs); } int main(int argc, char **argv) { int i, all = 0, stop = 0; FILE *fh; struct fstab *fs; char *reason; optind = 0; while((i = getopt_long(argc, argv, "afhnqvV", options, NULL)) != EOF) { switch(i) { case 'a': all = 1; break; case 'n': readonly = 1; break; case 'q': quiet = 1; break; case 'v': verbose = 1; break; case 'V': printf("$Id:$\n"); stop = 1; break; default: i = i != 'h'; fh = i ? stderr : stdout; stop = i + 1; if(!i) fprintf(fh, "mapcheck: a program to fix a very specific XFS problem.\n"); fprintf(fh, "Usage: %s [-afhnqvV] [file [...]] [dir [...]]\n", argv[0]); fprintf(fh, " -a, --all Fix all XFS filesystems\n"); fprintf(fh, " -f, --force Force application\n"); fprintf(fh, " -h, --help Display this message\n"); fprintf(fh, " -n, --no-act Scan but don't change any files\n"); fprintf(fh, " -q, --quiet Don't output anything except errors\n"); fprintf(fh, " -v, --verbose Write verbose info to stdout\n"); fprintf(fh, " -V, --version Write version to stdout\n"); } } if(stop) return stop - 1; setbuf(stdout, NULL); signal(SIGINT, catcher); fixedmsg = readonly ? "found" : "fixed"; if(!quiet) { signal(SIGALRM, printer); alarm(1); } if(all) { if(!setfsent()) return perror("reading fstab"), 2; while((fs = getfsent())) { if((reason = isbadfs(fs))) { if(verbose) fprintf(stderr, "Skipping %s %s: %s\n", fs->fs_spec, fs->fs_file, reason); } else { if(!quiet) fprintf(stderr, "Scanning %s\n", fs->fs_file); nftw64(fs->fs_file, checker, SCANDEPTH, FTW_MOUNT|FTW_PHYS); } } endfsent(); } else if(argc == optind) { if((reason = isbadfile("."))) return fprintf(stderr, "Can't scan .: %s\n", reason), 1; if(!quiet) fprintf(stderr, "Scanning current directory\n"); nftw64(".", checker, SCANDEPTH, FTW_MOUNT|FTW_PHYS); } for(i = optind; i < argc; i++) { if((reason = isbadfile(argv[i]))) return fprintf(stderr, "Can't scan %s: %s\n", argv[i], reason), 1; nftw64(argv[i], checker, SCANDEPTH, FTW_MOUNT|FTW_PHYS); } if(!quiet) { alarm(0); printer(0); putchar('\n'); } return 0; }