void process_files(const char* paths[], size_t count, find_file_options* opt) { struct file_t file; size_t i; memset(&file, 0, sizeof(file)); for(i = 0; i < count && !(opt->options & FIND_CANCEL); i++) { file.path = (char*)paths[i]; if(!IS_DASH_STR(file.path) && rsh_file_stat2(&file, USE_LSTAT) < 0) { if((opt->options & FIND_LOG_ERRORS) != 0) { log_file_error(file.path); opt->errors_count++; } continue; } if(!IS_DASH_STR(file.path) && (file.mode & FILE_IFDIR) != 0) { find_file(&file, opt); } else { file.mode |= FILE_ISROOT; opt->call_back(&file, opt->call_back_data); } } rsh_file_cleanup(&file); }
/** * Calculate hash sums simultaneously, according to the info->sums_flags. * Calculated hashes are stored in info->rctx. * * @param info file data. The info->full_path can be "-" to denote stdin * @return 0 on success, -1 on fail with error code stored in errno */ static int calc_sums(struct file_info *info) { FILE* fd = stdin; /* stdin */ int res; uint64_t initial_size; if(IS_DASH_STR(info->full_path)) { info->print_path = "(stdin)"; #ifdef _WIN32 /* using 0 instead of _fileno(stdin). _fileno() is undefined under 'gcc -ansi' */ if(setmode(0, _O_BINARY) < 0) { return -1; } #endif } else { struct rsh_stat_struct stat_buf; /* skip non-existing files */ if(rsh_stat(info->full_path, &stat_buf) < 0) { return -1; } if((opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED)) && S_ISDIR(stat_buf.st_mode)) { errno = EISDIR; return -1; } info->size = stat_buf.st_size; /* total size, in bytes */ IF_WINDOWS(win32_set_filesize64(info->full_path, &info->size)); /* set correct filesize for large files under win32 */ if(!info->sums_flags) return 0; /* skip files opened with exclusive rights without reporting an error */ fd = rsh_fopen_bin(info->full_path, "rb"); if(!fd) { return -1; } } re_init_rhash_context(info); initial_size = info->rctx->msg_size; if(percents_output->update != 0) { rhash_set_callback(info->rctx, (rhash_callback_t)percents_output->update, info); } /* read and hash file content */ if((res = rhash_file_update(info->rctx, fd)) != -1) { if(!opt.bt_batch_file) { rhash_final(info->rctx, 0); /* finalize hashing */ } } info->size = info->rctx->msg_size - initial_size; rhash_data.total_size += info->size; if(fd != stdin) fclose(fd); return res; }
/** * 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); } }
/* allocate and fill the file_search_data */ file_search_data* create_file_search_data(rsh_tchar** paths, size_t count, int max_depth) { size_t i; file_search_data* data = (file_search_data*)rsh_malloc(sizeof(file_search_data)); memset(data, 0, sizeof(file_search_data)); rsh_blocks_vector_init(&data->root_files); data->max_depth = max_depth; #ifdef _WIN32 /* expand wildcards and fill the root_files */ for (i = 0; i < count; i++) { int added = 0; size_t length, index; wchar_t* path = paths[i]; wchar_t* p = wcschr(path, L'\0') - 1; /* strip trailing '\','/' symbols (if not preceded by ':') */ for (; p > path && IS_PATH_SEPARATOR_W(*p) && p[-1] != L':'; p--) *p = 0; /* Expand a wildcard in the current file path and store results into data->root_files. * If a wildcard is not found then just the file path is stored. * NB, only wildcards in the last filename of the path are expanded. */ length = p - path + 1; index = wcscspn(path, L"*?"); if (index < length && wcscspn(path + index, L"/\\") >= (length - index)) { /* a wildcard is found without a directory separator after it */ wchar_t* parent; WIN32_FIND_DATAW d; HANDLE handle; /* find a directory separator before the file name */ for (; index > 0 && !IS_PATH_SEPARATOR(path[index]); index--); parent = (IS_PATH_SEPARATOR(path[index]) ? path : 0); handle = FindFirstFileW(path, &d); if (INVALID_HANDLE_VALUE != handle) { do { file_t file; int failed; if (IS_CURRENT_OR_PARENT_DIRW(d.cFileName)) continue; memset(&file, 0, sizeof(file)); file.wpath = make_pathw(parent, index + 1, d.cFileName); if (!file.wpath) continue; /* skip directories if not in recursive mode */ if (data->max_depth == 0 && (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue; /* convert file name */ file.path = wchar_to_cstr(file.wpath, WIN_DEFAULT_ENCODING, &failed); if (!failed) { failed = (file_statw(&file) < 0); } /* quietly skip unconvertible file names */ if (!file.path || failed) { if (failed) { data->errors_count++; } free(file.path); free(file.wpath); continue; } /* fill the file information */ file.mode |= FILE_IFROOT; add_root_file(data, &file); added++; } while (FindNextFileW(handle, &d)); FindClose(handle); } else { /* report error on the specified wildcard */ char * cpath = wchar_to_cstr(path, WIN_DEFAULT_ENCODING, NULL); set_errno_from_last_file_error(); log_file_error(cpath); free(cpath); data->errors_count++; } } else { int failed; file_t file; memset(&file, 0, sizeof(file)); /* if filepath is a dash string "-" */ if ((path[0] == L'-' && path[1] == L'\0')) { file.mode = FILE_IFSTDIN; file.path = rsh_strdup("(stdin)"); } else { file.path = wchar_to_cstr(path, WIN_DEFAULT_ENCODING, &failed); if (failed) { log_error(_("Can't convert the path to local encoding: %s\n"), file.path); free(file.path); data->errors_count++; continue; } file.wpath = path; if (file_statw(&file) < 0) { log_file_error(file.path); free(file.path); data->errors_count++; continue; } } /* mark the file as obtained from the command line */ file.mode |= FILE_IFROOT; file.wpath = rsh_wcsdup(path); add_root_file(data, &file); } } /* for */ #else /* copy file paths */ for (i = 0; i < count; i++) { file_t file; file_init(&file, paths[i], 0); if (IS_DASH_STR(file.path)) { file.mode = FILE_IFSTDIN; } else if (file_stat2(&file, USE_LSTAT) < 0) { log_file_error(file.path); file_cleanup(&file); data->errors_count++; continue; } file.mode |= FILE_IFROOT; add_root_file(data, &file); } #endif return data; }
/** * 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); 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( IS_DASH_STR(hash_file_path) ) { 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) { 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 prepent 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); } /* verify hash sums of the file */ res = verify_sums(&info); fflush(rhash_data.out); free(info.full_path); 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.full_path = rsh_strdup(file->path); file_info_set_print_path(&info, print_path); info.size = 0; info.sums_flags = opt.sum_flags; if(IS_DASH_STR(info.full_path)) { print_path = "(stdin)"; memset(&info.stat_buf, 0, sizeof(info.stat_buf)); } 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.mode & MODE_TORRENT) && !opt.bt_batch_file) { save_torrent(&info); } if(opt.flags & OPT_EMBED_CRC) { /* rename the file */ rename_file_to_embed_crc32(&info); } if((opt.mode & MODE_UPDATE) && opt.fmt == FMT_SFV) { file_t file; file.path = info.full_path; file.wpath = 0; rsh_file_stat2(&file, 0); print_sfv_header_line(rhash_data.upd_fd, &file, info.full_path); if(opt.flags & OPT_VERBOSE) { print_sfv_header_line(rhash_data.log, &file, info.full_path); 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; }