/** * Hash, check or update files according to work mode. */ static void process_files(void) { timedelta_t timer; struct rsh_stat_struct stat_buf; int i; rhash_timer_start(&timer); rhash_data.processed = 0; /* process filenames */ for(i = 0; i < opt.n_files; i++) { int res = 0; char* filepath = opt.files[i]; stat_buf.st_mode = 0; if(!IS_DASH_STR(filepath) && rsh_stat(filepath, &stat_buf) < 0) { log_file_error(filepath); continue; } if(opt.flags & OPT_RECURSIVE) { if(S_ISDIR(stat_buf.st_mode)) { find_file(filepath, find_file_callback, 0, opt.find_max_depth, NULL); continue; } } else { if(S_ISDIR(stat_buf.st_mode)){ if(opt.flags & OPT_VERBOSE){ log_warning(_("%s: is a directory\n"), filepath); } continue; } } if(opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED)) { res = check_hash_file(filepath, 0); } else if(opt.mode & MODE_UPDATE) { res = update_hash_file(filepath); } else { res = calculate_and_print_sums(rhash_data.out, filepath, filepath, &stat_buf); rhash_data.processed++; } if(res < 0) rhash_data.error_flag = 1; } if((opt.mode & MODE_CHECK_EMBEDDED) && rhash_data.processed > 1) { print_check_stats(); } if((opt.flags & OPT_SPEED) && !(opt.mode&(MODE_CHECK | MODE_UPDATE)) && rhash_data.processed > 1) { double time = rhash_timer_stop(&timer); print_time_stats(time, rhash_data.total_size, 1); } }
/** * Verify hash sums of the file. * * @param info structure file path to process * @return zero on success, -1 on file error, -2 if hash sums are different */ static int verify_sums(struct file_info *info) { timedelta_t timer; int res = 0; errno = 0; /* initialize percents output */ init_percents(info); rhash_timer_start(&timer); if(calc_sums(info) < 0) { finish_percents(info, -1); return -1; } info->time = rhash_timer_stop(&timer); if(rhash_data.interrupted) { report_interrupted(); return 0; } if((opt.flags & OPT_EMBED_CRC) && find_embedded_crc32( info->print_path, &info->hc.embedded_crc32_be)) { info->hc.flags |= HC_HAS_EMBCRC32; assert(info->hc.hash_mask & RHASH_CRC32); } if(!hash_check_verify(&info->hc, info->rctx)) { res = -2; } finish_percents(info, res); if((opt.flags & OPT_SPEED) && info->sums_flags) { print_file_time_stats(info); } return res; }
/** * Check hash sums in a hash file. * Lines beginning with ';' and '#' are ignored. * * @param hash_file_path - the path of the file with hash sums to verify. * @param chdir - true if function should emulate chdir to directory of filepath before checking it. * @return zero on success, -1 on fail */ int check_hash_file(file_t* file, int chdir) { FILE *fd; char buf[2048]; size_t pos; const char *ralign; timedelta_t timer; struct file_info info; const char* hash_file_path = file->path; int res = 0, line_num = 0; double time; /* process --check-embedded option */ if(opt.mode & MODE_CHECK_EMBEDDED) { unsigned crc32_be; if(find_embedded_crc32(hash_file_path, &crc32_be)) { /* initialize file_info structure */ memset(&info, 0, sizeof(info)); info.full_path = rsh_strdup(hash_file_path); info.file = file; file_info_set_print_path(&info, info.full_path); info.sums_flags = info.hc.hash_mask = RHASH_CRC32; info.hc.flags = HC_HAS_EMBCRC32; info.hc.embedded_crc32_be = crc32_be; res = verify_sums(&info); fflush(rhash_data.out); if(!rhash_data.interrupted) { if(res == 0) rhash_data.ok++; else if(res == -1 && errno == ENOENT) rhash_data.miss++; rhash_data.processed++; } free(info.full_path); file_info_destroy(&info); } else { log_warning(_("file name doesn't contain a CRC32: %s\n"), hash_file_path); return -1; } return 0; } /* initialize statistics */ rhash_data.processed = rhash_data.ok = rhash_data.miss = 0; rhash_data.total_size = 0; if(file->mode & FILE_IFSTDIN) { fd = stdin; hash_file_path = "<stdin>"; } else if( !(fd = rsh_fopen_bin(hash_file_path, "rb") )) { log_file_error(hash_file_path); return -1; } pos = strlen(hash_file_path)+16; ralign = str_set(buf, '-', (pos < 80 ? 80 - (int)pos : 2)); fprintf(rhash_data.out, _("\n--( Verifying %s )%s\n"), hash_file_path, ralign); fflush(rhash_data.out); rhash_timer_start(&timer); /* mark the directory part of the path, by setting the pos index */ if(chdir) { pos = strlen(hash_file_path); for(; pos > 0 && !IS_PATH_SEPARATOR(hash_file_path[pos]); pos--); if(IS_PATH_SEPARATOR(hash_file_path[pos])) pos++; } else pos = 0; /* read crc file line by line */ for(line_num = 0; fgets(buf, 2048, fd); line_num++) { char* line = buf; char* path_without_ext = NULL; /* skip unicode BOM */ if(line_num == 0 && buf[0] == (char)0xEF && buf[1] == (char)0xBB && buf[2] == (char)0xBF) line += 3; if(*line == 0) continue; /* skip empty lines */ if(is_binary_string(line)) { log_error(_("file is binary: %s\n"), hash_file_path); if(fd != stdin) fclose(fd); return -1; } /* skip comments and empty lines */ if(IS_COMMENT(*line) || *line == '\r' || *line == '\n') continue; memset(&info, 0, sizeof(info)); if(!hash_check_parse_line(line, &info.hc, !feof(fd))) continue; if(info.hc.hash_mask == 0) continue; info.print_path = info.hc.file_path; info.sums_flags = info.hc.hash_mask; /* see if crc file contains a hash sum without a filename */ if(info.print_path == NULL) { char* point; path_without_ext = rsh_strdup(hash_file_path); point = strrchr(path_without_ext, '.'); if(point) { *point = '\0'; file_info_set_print_path(&info, path_without_ext); } } if(info.print_path != NULL) { file_t file_to_check; int is_absolute = IS_PATH_SEPARATOR(info.print_path[0]); IF_WINDOWS(is_absolute = is_absolute || (info.print_path[0] && info.print_path[1] == ':')); /* if filename shall be prepended by a directory path */ if(pos && !is_absolute) { size_t len = strlen(info.print_path); info.full_path = (char*)rsh_malloc(pos + len + 1); memcpy(info.full_path, hash_file_path, pos); strcpy(info.full_path + pos, info.print_path); } else { info.full_path = rsh_strdup(info.print_path); } memset(&file_to_check, 0, sizeof(file_t)); file_to_check.path = info.full_path; rsh_file_stat(&file_to_check); info.file = &file_to_check; /* verify hash sums of the file */ res = verify_sums(&info); fflush(rhash_data.out); rsh_file_cleanup(&file_to_check); file_info_destroy(&info); if(rhash_data.interrupted) { free(path_without_ext); break; } /* update statistics */ if(res == 0) rhash_data.ok++; else if(res == -1 && errno == ENOENT) rhash_data.miss++; rhash_data.processed++; } free(path_without_ext); } time = rhash_timer_stop(&timer); fprintf(rhash_data.out, "%s\n", str_set(buf, '-', 80)); print_check_stats(); if(rhash_data.processed != rhash_data.ok) rhash_data.error_flag = 1; if(opt.flags & OPT_SPEED && rhash_data.processed > 1) { print_time_stats(time, rhash_data.total_size, 1); } rhash_data.processed = 0; res = ferror(fd); /* check that crc file has been read without errors */ if(fd != stdin) fclose(fd); return (res == 0 ? 0 : -1); }
/** * Calculate and print file hash sums using printf format. * * @param out a stream to print to * @param file the file to calculate sums for * @param print_path the path to print * @return 0 on success, -1 on fail */ int calculate_and_print_sums(FILE* out, file_t* file, const char *print_path) { struct file_info info; timedelta_t timer; int res = 0; memset(&info, 0, sizeof(info)); info.file = file; info.full_path = rsh_strdup(file->path); file_info_set_print_path(&info, print_path); info.size = 0; info.sums_flags = opt.sum_flags; if(file->mode & FILE_IFSTDIN) { print_path = "(stdin)"; } else { if(file->mode & FILE_IFDIR) return 0; /* don't handle directories */ info.size = file->size; /* total size, in bytes */ } /* initialize percents output */ init_percents(&info); rhash_timer_start(&timer); if(info.sums_flags) { /* calculate sums */ if(calc_sums(&info) < 0) { /* print error unless sharing access error occurred */ if(errno == EACCES) return 0; log_file_error(file->path); res = -1; } if(rhash_data.interrupted) { report_interrupted(); return 0; } } info.time = rhash_timer_stop(&timer); finish_percents(&info, res); if(opt.flags & OPT_EMBED_CRC) { /* rename the file */ rename_file_by_embeding_crc32(&info); } if((opt.mode & MODE_TORRENT) && !opt.bt_batch_file) { save_torrent(&info); } if((opt.mode & MODE_UPDATE) && opt.fmt == FMT_SFV) { /* updating SFV file: print SFV header line */ print_sfv_header_line(rhash_data.upd_fd, file, 0); if(opt.flags & OPT_VERBOSE) { print_sfv_header_line(rhash_data.log, file, 0); fflush(rhash_data.log); } rsh_file_cleanup(file); } if(rhash_data.print_list && res >= 0) { if (!opt.bt_batch_file) { print_line(out, rhash_data.print_list, &info); fflush(out); /* print calculated line to stderr or log-file if verbose */ if((opt.mode & MODE_UPDATE) && (opt.flags & OPT_VERBOSE)) { print_line(rhash_data.log, rhash_data.print_list, &info); fflush(rhash_data.log); } } if((opt.flags & OPT_SPEED) && info.sums_flags) { print_file_time_stats(&info); } } free(info.full_path); file_info_destroy(&info); return res; }
/** * RHash program entry point. * * @param argc number of program arguments including the program path * @param argv program arguments * @return the program exit code, zero on success and 1 on error */ int main(int argc, char *argv[]) { find_file_options search_opt; timedelta_t timer; int sfv; i18n_initialize(); /* initialize locale and translation */ memset(&rhash_data, 0, sizeof(rhash_data)); rhash_data.out = stdout; /* set initial output streams */ rhash_data.log = stderr; /* can be altered by options later */ rhash_data.search_opt = &search_opt; init_hash_info_table(); read_options(argc, argv); /* load config and parse command line options */ prev_sigint_handler = signal(SIGINT, ctrl_c_handler); /* install SIGINT handler */ rhash_library_init(); /* in benchmark mode just run benchmark and exit */ if(opt.mode & MODE_BENCHMARK) { unsigned flags = (opt.flags & OPT_BENCH_RAW ? RHASH_BENCHMARK_CPB | RHASH_BENCHMARK_RAW : RHASH_BENCHMARK_CPB); if((opt.flags & OPT_BENCH_RAW) == 0) { fprintf(rhash_data.out, _("%s v%s benchmarking...\n"), PROGRAM_NAME, VERSION); } rhash_run_benchmark(opt.sum_flags, flags, rhash_data.out); rsh_exit(0); } if(opt.n_files == 0) { if(argc > 1) { log_warning(_("no files/directories were specified at command line\n")); } /* print short usage help */ log_msg(_("Usage: %s [OPTION...] <FILE>...\n\n" "Run `%s --help' for more help.\n"), CMD_FILENAME, CMD_FILENAME); rsh_exit(0); } /* setup printf formating string */ rhash_data.printf_str = opt.printf_str; if(opt.template_file) { if(!load_printf_template()) rsh_exit(2); } else if(!rhash_data.printf_str && !(opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED))) { /* initialize printf output format according to '--<hashname>' options */ init_printf_format( (rhash_data.template_text = rsh_str_new()) ); rhash_data.printf_str = rhash_data.template_text->str; if(opt.flags & OPT_VERBOSE) { char* str = rsh_strdup(rhash_data.printf_str); log_msg(_("Format string is: %s\n"), str_trim(str)); free(str); } } if(rhash_data.printf_str) { rhash_data.print_list = parse_print_string(rhash_data.printf_str, &opt.sum_flags); } memset(&search_opt, 0, sizeof(search_opt)); search_opt.max_depth = (opt.flags & OPT_RECURSIVE ? opt.find_max_depth : 0); search_opt.options = FIND_SKIP_DIRS; search_opt.call_back = find_file_callback; if((sfv = (opt.fmt == FMT_SFV && !opt.mode))) { print_sfv_banner(rhash_data.out); } /* pre-process files */ if(sfv || opt.bt_batch_file) { /* note: errors are not reported on pre-processing */ search_opt.call_back_data = (void*)1; process_files((const char**)opt.files, opt.n_files, &search_opt); fflush(rhash_data.out); } /* measure total processing time */ rhash_timer_start(&timer); rhash_data.processed = 0; /* process files */ search_opt.options |= FIND_LOG_ERRORS; search_opt.call_back_data = (void*)0; process_files((const char**)opt.files, opt.n_files, &search_opt); if((opt.mode & MODE_CHECK_EMBEDDED) && rhash_data.processed > 1) { print_check_stats(); } if(!rhash_data.interrupted) { if(opt.bt_batch_file && rhash_data.rctx) { rhash_final(rhash_data.rctx, 0); save_torrent_to(opt.bt_batch_file, rhash_data.rctx); } if((opt.flags & OPT_SPEED) && !(opt.mode & (MODE_CHECK | MODE_UPDATE)) && rhash_data.processed > 1) { double time = rhash_timer_stop(&timer); print_time_stats(time, rhash_data.total_size, 1); } } else { /* check if interruption was not reported yet */ if(rhash_data.interrupted == 1) report_interrupted(); } options_destroy(&opt); rhash_destroy(&rhash_data); /* return non-zero error code if error occurred */ return (rhash_data.error_flag ? 1 : search_opt.errors_count ? 2 : rhash_data.interrupted ? 3 : 0); }
/** * Benchmark a hash algorithm. * * @deprecated This function shall be removed soon, since * it is not related to the hashing library main functionality. * * @param hash_id hash algorithm identifier * @param flags benchmark flags, can be RHASH_BENCHMARK_QUIET and RHASH_BENCHMARK_CPB * @param output the stream to print results */ void rhash_run_benchmark(unsigned hash_id, unsigned flags, FILE* output) { unsigned char ALIGN_ATTR(16) message[8192]; /* 8 KiB */ timedelta_t timer; int i, j; size_t sz_mb, msg_size; double time, total_time = 0; const int rounds = 4; const char* hash_name; unsigned char out[130]; #ifdef HAVE_TSC double cpb = 0; #endif /* HAVE_TSC */ #ifdef _WIN32 benchmark_cpu_init(); /* set cpu affinity to improve test results */ #endif /* set message size for fast and slow hash functions */ msg_size = 1073741824 / 2; if (hash_id & (RHASH_WHIRLPOOL | RHASH_SNEFRU128 | RHASH_SNEFRU256 | RHASH_SHA3_224 | RHASH_SHA3_256 | RHASH_SHA3_384 | RHASH_SHA3_512)) { msg_size /= 8; } else if (hash_id & (RHASH_GOST | RHASH_GOST_CRYPTOPRO | RHASH_SHA384 | RHASH_SHA512)) { msg_size /= 2; } sz_mb = msg_size / (1 << 20); /* size in MiB */ hash_name = rhash_get_name(hash_id); if (!hash_name) hash_name = ""; /* benchmarking several hashes*/ for (i = 0; i < (int)sizeof(message); i++) message[i] = i & 0xff; for (j = 0; j < rounds; j++) { rhash_timer_start(&timer); hash_in_loop(hash_id, message, sizeof(message), (int)(msg_size / sizeof(message)), out); time = rhash_timer_stop(&timer); total_time += time; if ((flags & (RHASH_BENCHMARK_QUIET | RHASH_BENCHMARK_RAW)) == 0) { fprintf(output, "%s %u MiB calculated in %.3f sec, %.3f MBps\n", hash_name, (unsigned)sz_mb, time, (double)sz_mb / time); fflush(output); } } #if defined(HAVE_TSC) /* measure the CPU "clocks per byte" speed */ if (flags & RHASH_BENCHMARK_CPB) { unsigned int c1 = -1, c2 = -1; unsigned volatile long long cy0, cy1, cy2; int msg_size = 128 * 1024; /* make 200 tries */ for (i = 0; i < 200; i++) { cy0 = read_tsc(); hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out); cy1 = read_tsc(); hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out); hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out); cy2 = read_tsc(); cy2 -= cy1; cy1 -= cy0; c1 = (unsigned int)(c1 > cy1 ? cy1 : c1); c2 = (unsigned int)(c2 > cy2 ? cy2 : c2); } cpb = ((c2 - c1) + 1) / (double)msg_size; } #endif /* HAVE_TSC */ if (flags & RHASH_BENCHMARK_RAW) { /* output result in a "raw" machine-readable format */ fprintf(output, "%s\t%u\t%.3f\t%.3f", hash_name, ((unsigned)sz_mb * rounds), total_time, (double)(sz_mb * rounds) / total_time); #if defined(HAVE_TSC) if (flags & RHASH_BENCHMARK_CPB) fprintf(output, "\t%.2f", cpb); #endif /* HAVE_TSC */ fprintf(output, "\n"); } else { fprintf(output, "%s %u MiB total in %.3f sec, %.3f MBps", hash_name, ((unsigned)sz_mb * rounds), total_time, (double)(sz_mb * rounds) / total_time); #if defined(HAVE_TSC) if (flags & RHASH_BENCHMARK_CPB) fprintf(output, ", CPB=%.2f", cpb); #endif /* HAVE_TSC */ fprintf(output, "\n"); } }