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); }
/** * 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; }
/** * Walk directory tree and call given callback function to process each file/directory. * * @param start_dir path to the directory to walk recursively * @param options the options specifying how to walk the directory tree */ int find_file(file_t* start_dir, find_file_options* options) { dir_entry *dirs_stack = NULL; /* root of the dir_list */ dir_iterator* it; int level = 1; int max_depth = options->max_depth; int flags = options->options; dir_entry* entry; file_t file; if(max_depth < 0 || max_depth >= MAX_DIRS_DEPTH) { max_depth = MAX_DIRS_DEPTH; } /* skip the directory if max_depth == 0 */ if(max_depth == 0) { return 0; } memset(&file, 0, sizeof(file)); if((start_dir->mode & FILE_IFDIR) == 0) { errno = ENOTDIR; return -1; } /* check if we should descend into the root directory */ if((flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == 0) { if(!options->call_back(start_dir, options->call_back_data)) return 0; } it = (dir_iterator*)malloc(MAX_DIRS_DEPTH * sizeof(dir_iterator)); if(!it) return 0; /* push root directory into dirs_stack */ it[0].left = 1; it[0].prev_dir = rsh_strdup(start_dir->path); it[1].prev_dir = NULL; if(!it[0].prev_dir) { errno = ENOMEM; return -1; } entry = dir_entry_insert(&dirs_stack, NULL, 0); if(!entry) { free(it[0].prev_dir); free(it); errno = ENOMEM; return -1; } while(!(options->options & FIND_CANCEL)) { dir_entry *dir, **insert_at; char* dir_path; DIR *dp; struct dirent *de; /* walk back */ while((--level) >= 0 && it[level].left <= 0) free(it[level+1].prev_dir); if(level < 0) break; assert(dirs_stack != NULL); /* on the first cycle: level == 0, stack[0] == 0; */ dir = dirs_stack; /* take last dir from the list */ dirs_stack = dirs_stack->next; /* remove last dir from the list */ it[level].left--; dir_path = (!dir->filename ? rsh_strdup(it[level].prev_dir) : make_path(it[level].prev_dir, dir->filename) ); dir_entry_free(dir); if(!dir_path) continue; level++; it[level].left = 0; it[level].prev_dir = dir_path; if((flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == FIND_WALK_DEPTH_FIRST) { rsh_file_cleanup(&file); file.path = dir_path; /* check if we should skip the directory */ if(!options->call_back(&file, options->call_back_data)) continue; } /* read dir */ dp = opendir(dir_path); if(dp == NULL) continue; insert_at = &dirs_stack; while((de = readdir(dp)) != NULL) { int res; /* skip "." and ".." dirs */ if(de->d_name[0] == '.' && (de->d_name[1] == 0 || (de->d_name[1] == '.' && de->d_name[2] == 0 ))) continue; if( !(file.path = make_path(dir_path, de->d_name)) ) continue; res = rsh_file_stat2(&file, USE_LSTAT); /* process */ if(res >= 0) { if((file.mode & FILE_IFDIR) && (flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS))) res = 1; else { /* handle file by callback function */ res = options->call_back(&file, options->call_back_data); } /* if file is a directory and we need to walk it */ if((file.mode & FILE_IFDIR) && res && level < max_depth) { /* don't go deeper if max_depth reached */ /* add directory to dirs_stack */ if(dir_entry_insert(insert_at, de->d_name, file.mode)) { /* if really added */ insert_at = &((*insert_at)->next); it[level].left++; } } } else if (options->options & FIND_LOG_ERRORS) { log_file_error(file.path); } rsh_file_cleanup(&file); free(file.path); } closedir(dp); if(it[level].left > 0) level++; } assert(dirs_stack == NULL); free(it); return 0; }