/* * @(#CDWverify.cpp * * This file is part of webCDwriter - Network CD/DVD Writing. * * Copyright (C) 2001-2005 Jörg P. M. Haeger * * webCDwriter is free software. See CDWserver.cpp for details. * * Author: Jörg P. M. Haeger, 28.02.2001 */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #ifdef FreeBSD #include #endif #include #include #include #include #include "Error.h" #include "Version.h" typedef unsigned short uint16; typedef unsigned long uint32; typedef unsigned long long uint64; /** * Compare a directory or an image file to the contents of a CD * * @version 20050913 * @author Jörg P. M. Haeger */ class Verify { public: static bool cancel; private: char *buf1, *buf2; uint64 bytesCounter, bufferSize; int checkTooLong; int MBytes0; uint64 time0; int newLine; int numOfFiles, numOfErrors; int existing, missing; uint64 origSize; int maxSpeed; public: Verify(int maxSpeedArg) { bufferSize = 64 * 1024; buf1 = new char[bufferSize]; buf2 = new char[bufferSize]; bytesCounter = 0; checkTooLong = 1; MBytes0 = 0; time0 = 0; newLine = 1; numOfFiles = numOfErrors = 0; maxSpeed = maxSpeedArg; } ~Verify() { delete[] buf1; delete[] buf2; } void compare(const char *orig, const char *copy); void compareFile(const char *orig, const char *copy, uint64 size = 0); void compareTracks(const char *orig, const char *copy); Error *createActionError(const char *action) { return new Error("%s, action: %s", strerror(errno), action); } int getExisting() { return existing; } int getMissing() { return missing; } int getNumOfErrors() { return numOfErrors; } int getNumOfFiles() { return numOfFiles; } int getOrigSizeMB() { return (int) (origSize / 1024 / 1024); } static uint64 getTime() { struct timeval tv; gettimeofday(&tv, NULL); uint64 t = tv.tv_sec; return 1000 * 1000 * t + tv.tv_usec; } public: static int mount(const char *specialfile, const char *dir) { #ifdef FreeBSD // return ::mount(specialfile, dir, MNT_RDONLY, NULL); char command[1024]; snprintf(command, sizeof command, "mount -t cd9660 -o ro %s %s", specialfile, dir); return system(command); #else return ::mount(specialfile, dir, "iso9660", MS_RDONLY, NULL); #endif } /** * Print an ID of the ISO 9660 primary volume descriptor. */ static void printID(unsigned char *buf, const char *name, int start, int length) { int i = start + length - 1; while (i >= start && buf[i] <= 32) buf[i--] = 0; printf("%s = %s\n", name, &buf[start]); } void printStatus(bool force = false) { int MBytes = bytesCounter / 1024 / 1024; if (force || (MBytes > MBytes0 && getTime() > time0 + 1000 * 1000)) { if (!newLine) printf("\r"); float speed = speedometer.getSpeed(); printf("%d MB, %d files, %d errors, %4.1fx (%4.0f KB/s)", MBytes, numOfFiles, numOfErrors, speed, 150.0 * speed); fflush(stdout); MBytes0 = MBytes; time0 = getTime(); newLine = 0; } } static uint64 readISOsize(const char *imageFile, int sector0 = 0); public: void scan(const char *orig, const char *copy) { existing = missing = 0; origSize = 0; scan2(orig, copy); } private: void scan2(const char *orig, const char *copy) { struct stat statBuf; if (stat(orig, &statBuf) != 0) return; if (!S_ISDIR(statBuf.st_mode)) origSize += statBuf.st_size; if (!S_ISDIR(statBuf.st_mode)) { if (stat(copy, &statBuf) == 0) existing++; else missing++; return; } DIR *dir = opendir(orig); if (dir == NULL) return; struct dirent *dirEntry; while ((dirEntry = readdir(dir)) != NULL) { if (strcmp(dirEntry->d_name, ".") == 0 || strcmp(dirEntry->d_name, "..") == 0) continue; char tmpOrig[strlen(orig) + 1 + strlen(dirEntry->d_name) + 1]; sprintf(tmpOrig, "%s/%s", orig, dirEntry->d_name); char tmpCopy[strlen(copy) + 1 + strlen(dirEntry->d_name) + 1]; sprintf(tmpCopy, "%s/%s", copy, dirEntry->d_name); scan2(tmpOrig, tmpCopy); if (cancel) break; } closedir(dir); } public: static int unmount(const char *dir) { seteuid(0); /* char command[1024]; snprintf(command, sizeof command, "umount %s", dir); return system(command); */ int res; #ifdef FreeBSD res = ::unmount(dir, MNT_FORCE); #else res = ::umount(dir); #endif if (res != 0) printf("\nCannot unmount %s: %s\n", dir, strerror(errno)); seteuid(getuid()); return res; } public: static void unmountDevice(const char *device) { FILE *in = fopen("/proc/mounts", "r"); if (in == NULL) return; while (true) { char line[1024 + 1]; if (fgets(line, sizeof line, in) == NULL) break; char dev[sizeof line], dir[sizeof line]; if (sscanf(line, "%s %s", dev, dir) != 2) continue; if (strcmp(dev, device) == 0) { printf("Unmounting %s...", dir); fflush(stdout); unmount(dir); printf(" done\n"); } } fclose(in); } class Speedometer { uint64 t0, t0Next; uint64 counter, counterNext; float speed; public: Speedometer() { t0 = t0Next = getTime(); counter = counterNext = 0; speed = 0.0; } private: void calibrate(uint64 t) { if (t - t0Next > 1 * 1000 * 1000) { t0 = t0Next; counter = counterNext; t0Next = t; counterNext = 0; } } public: float getSpeed() { uint64 t = getTime(); calibrate(t); if (t - t0 > 1 * 1000 * 1000) speed = 1000 * 1000 * counter / (t - t0) / (150.0 * 1024); return speed; } public: void inc(int n) { counter += n; counterNext += n; } public: void slowDown(int maxSpeed) { while (true) { uint64 t = getTime(); calibrate(t); uint64 limit = (t - t0) * (maxSpeed * 150 * 1024) / 1000 / 1000; if (counter <= limit) break; usleep(1000 * 1000 * (counter - limit) / (maxSpeed * 150 * 1024)); } } }; private: Speedometer speedometer; }; void Verify::compare(const char *orig, const char *copy) { struct stat statBuf; if (stat(orig, &statBuf) != 0) return; if (!S_ISDIR(statBuf.st_mode)) { compareFile(orig, copy); return; } DIR *dir = opendir(orig); if (dir == NULL) return; struct dirent *dirEntry; while ((dirEntry = readdir(dir)) != NULL) { if (strcmp(dirEntry->d_name, ".") == 0 || strcmp(dirEntry->d_name, "..") == 0) continue; char tmpOrig[strlen(orig) + 1 + strlen(dirEntry->d_name) + 1]; sprintf(tmpOrig, "%s/%s", orig, dirEntry->d_name); char tmpCopy[strlen(copy) + 1 + strlen(dirEntry->d_name) + 1]; sprintf(tmpCopy, "%s/%s", copy, dirEntry->d_name); compare(tmpOrig, tmpCopy); if (cancel) break; } closedir(dir); } void Verify::compareFile(const char *orig, const char *copy, uint64 size) { int in1 = -1, in2 = -1; try { struct stat statBuf; if (stat(copy, &statBuf) != 0) throw new Error("missing"); in1 = open(orig, O_RDONLY); if (in1 == -1) throw createActionError("open original"); in2 = open(copy, O_RDONLY); if (in2 == -1) throw createActionError("open copy"); uint64 perFileBytesCounter = 0; for (int i = 0; !cancel; ) { { char cancelFile[1024]; sprintf(cancelFile, "/tmp/CDWcancel-%d", getpid()); struct stat statBuf; if (stat(cancelFile, &statBuf) == 0) { cancel = true; break; } } printStatus(); int nMax = bufferSize; if (size > 0 && perFileBytesCounter + nMax > size) // just compare the first size bytes nMax = size - perFileBytesCounter; int n = 0; while (n < nMax) { int blockSize = nMax - n; if (blockSize > 2 * 1024) blockSize = 2 * 1024; int l = read(in1, &buf1[n], blockSize); if (l == -1) if (errno == EINTR) continue; else throw createActionError("read original"); if (l == 0) break; n += l; } if (n == 0) if (checkTooLong && read(in2, buf2, 1) > 0) throw new Error( "too long (> %llu bytes)", perFileBytesCounter); else break; int m = 0; while (1) { int l = read(in2, &buf2[m], n - m); if (l == -1 && errno != EINTR) throw new Error("%s while reading byte %llu", strerror(errno), perFileBytesCounter + m); if (l == 0) break; m += l; } if (memcmp(buf1, buf2, m) != 0) { int i = 0; while (buf1[i] == buf2[i]) i++; throw new Error( "byte %llu differs", perFileBytesCounter + i + 1); } if (m < n) throw new Error( "too short (%llu bytes)", perFileBytesCounter + m); bytesCounter += n; perFileBytesCounter += n; speedometer.inc(n); speedometer.slowDown(maxSpeed); } } catch (Error *err) { if (!newLine) { printf("\n"); newLine = 1; } const char *name = copy; if (strstr(name, "/tmp/cdrom") == name) name += 4 + 6 + 5; printf("error: %s, file: %s\n", err->toString(), name); delete err; numOfErrors++; } numOfFiles++; if (in2 != -1) close(in2); if (in1 != -1) close(in1); } void Verify::compareTracks(const char *orig, const char *copy) { checkTooLong = 0; compareFile(orig, copy, readISOsize(orig)); } uint64 Verify::readISOsize(const char *imageFile, int sector0) { uint64 size = 0; struct stat statBuf; if (stat(imageFile, &statBuf) == 0) size = statBuf.st_size; FILE *in = fopen(imageFile, "r"); if (in != NULL) { fseek(in, 2048 * (sector0 + 16), SEEK_SET); unsigned char buf[2048]; for (int i = 0; i < 32; i++) { int n = fread(buf, 1, sizeof buf, in); if (n < sizeof buf || buf[0] == 255) break; if (buf[0] == 1) { size = *(uint32 *)&buf[80]; size *= *(uint16 *)&buf[128]; printf("ISO image size = %llu\n", size); printID(buf, "volume ID", 40, 32); printID(buf, "publisher ID", 318, 128); printID(buf, "preparer ID", 446, 128); } } fclose(in); } return size; } bool Verify::cancel = false; void sigTermHandler(int sig) { Verify::cancel = true; printf("\nCancelled\n"); fflush(stdout); } int main(int argc, char *args[]) { printf("CDWverify %s (part of %s)\n", version, project); fflush(stdout); signal(SIGTERM, sigTermHandler); seteuid(getuid()); if (argc == 4 && strcmp(args[1], "-readISOHeader") == 0) { int n = 0; sscanf(args[2], "%d", &n); Verify::readISOsize(args[3], n); return 0; } int invalidArg = 0; int maxSpeed = 64; for (int i = 1; i < argc - 2; i++) if (sscanf(args[i], "maxSpeed=%d", &maxSpeed) == 1) ; else invalidArg = 1; if (argc < 3 || invalidArg) { printf("%s compares a directory " "or an image file to the contents of a CD\n\n" "Usage:\n" "\t\t%s [options] directory rawDevice\n" "\t\t%s [options] imageFile rawDevice\n" "\t\t%s -readISOHeader sector0 rawDevice\n\n" "Options:\n" "\t\tmaxSpeed=%dx" "\tdo not read faster than %dx (default)\n\n", args[0], args[0], args[0], args[0], maxSpeed, maxSpeed); return 1; } const char *orig = args[argc - 2]; const char *copy = args[argc - 1]; struct stat statBuf; if (stat(orig, &statBuf) != 0) { printf("Cannot stat %s (%s)\n", orig, strerror(errno)); return 1; } // make sure the device isn't already mounted Verify::unmountDevice(copy); /* const char *readcd = "/usr/bin/readcd"; const char *dev = "0,0,0"; if (stat(readcd, &statBuf) != 0) readcd = "/usr/local/bin/readcd"; if (stat(readcd, &statBuf) == 0) { printf("Trying to set drive to %dx speed...", maxSpeed); fflush(stdout); char cmd[4 * 1024]; sprintf(cmd, "%s dev=%s speed=%d sectors=0-1 f=/tmp/cdrom%05dS0", readcd, dev, maxSpeed, getpid()); if (system(cmd) == 0) printf(" done\n"); else printf(" failed\n"); } */ Verify verify(maxSpeed); if (S_ISDIR(statBuf.st_mode)) { char dir[1024]; sprintf(dir, "/tmp/cdrom%05d", getpid()); mkdir(dir, S_IRWXU); // mounting the CD/DVD seteuid(0); for (int i = 1; ; i++) { if (i > 1) printf("\r"); printf("Mounting the CD/DVD on %s...", dir); fflush(stdout); if (i > 1) { if (i == 2) printf(" 2nd try"); else if (i == 3) printf(" 3rd try"); else if (i == 5) { printf(" error: %s\n", strerror(errno)); rmdir(dir); return 1; } else if (i > 101) { if (i > 102) printf(" "); break; } else printf(" %dth try", i); fflush(stdout); sleep(2); } if (Verify::mount(copy, dir) == 0) i += 100; } seteuid(getuid()); printf(" done\n"); printf("Scanning the directories..."); fflush(stdout); verify.scan(orig, dir); printf(" %d of %d files found\n", verify.getExisting(), verify.getExisting() + verify.getMissing()); printf("%d MB total\n", verify.getOrigSizeMB()); fflush(stdout); if (verify.getExisting() == 0 && verify.getMissing() > 0) { Verify::unmount(dir); rmdir(dir); return 1; } printf("Comparing the files...\n"); fflush(stdout); verify.compare(orig, dir); verify.printStatus(true); Verify::unmount(dir); rmdir(dir); printf("\n%d files are identical, %d files differ\n", verify.getNumOfFiles() - verify.getNumOfErrors(), verify.getNumOfErrors()); if (verify.getNumOfErrors() == 0) return 0; else return 1; } else { // opening the raw device of the CD/DVD for (int i = 1; ; i++) { if (i > 1) printf("\r"); printf("Opening %s...", copy); fflush(stdout); if (i > 1) { if (i == 2) printf(" 2nd try"); else if (i == 3) printf(" 3rd try"); else if (i == 5) { printf(" error: %s\n", strerror(errno)); return 1; } else if (i > 101) { if (i > 102) printf(" "); break; } else printf(" %dth try", i); fflush(stdout); sleep(2); } if (open(copy, O_RDONLY) != -1) i += 100; } printf(" done\n"); verify.compareTracks(orig, copy); printf("\n"); if (verify.getNumOfErrors() == 0) { printf("OK\n"); return 0; } else return 1; } }