void scan_files(file_search_data* data) { size_t i; size_t count = data->root_files.size; int skip_symlink_dirs = !(data->options & FIND_FOLLOW_SYMLINKS); for (i = 0; i < count && !(data->options & FIND_CANCEL); i++) { file_t* file = get_root_file(data, i); assert(!!(file->mode & FILE_IFROOT)); /* check if file is a directory */ if (FILE_ISDIR(file)) { /* silently skip symlinks to directories if required */ if (skip_symlink_dirs && FILE_ISLNK(file)) { continue; } if (data->max_depth != 0) { dir_scan(file, data); } else if ((data->options & FIND_LOG_ERRORS) != 0) { errno = EISDIR; log_file_error(file->path); } } else { /* process a regular file or a dash '-' path */ data->call_back(file, data->call_back_data); } } }
/** * 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; assert(info->file); if(info->file->mode & FILE_IFSTDIN) { 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 { if((opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED)) && FILE_ISDIR(info->file)) { errno = EISDIR; return -1; } info->size = info->file->size; /* total size, in bytes */ if(!info->sums_flags) return 0; /* skip without reporting an error the files * opened exclusively by another process */ fd = rsh_fopen_bin(info->full_path, "rb"); if(!fd) { return -1; } } re_init_rhash_context(info); /* save initial msg_size, for correct calculation of percents */ info->msg_offset = 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 */ } } /* calculate real file size */ info->size = info->rctx->msg_size - info->msg_offset; rhash_data.total_size += info->size; if(fd != stdin) fclose(fd); return res; }
/** * Callback function to process files while recursively traversing a directory. * It hashes, checks or updates a file according to the current work mode. * * @param file the file to process * @param preprocess non-zero when preprocessing files, zero for actual processing. */ static int find_file_callback(file_t* file, int preprocess) { int res = 0; assert(!FILE_ISDIR(file)); assert(opt.search_data); if (rhash_data.interrupted) { opt.search_data->options |= FIND_CANCEL; return 0; } if (preprocess) { if (!file_mask_match(opt.files_accept, file->path) || must_skip_file(file)) { return 0; } if (opt.fmt & FMT_SFV) { print_sfv_header_line(rhash_data.out, file, 0); } rhash_data.batch_size += file->size; } else { int not_root = !(file->mode & FILE_IFROOT); if (not_root) { /* only check and update modes use the crc_accept mask */ file_mask_array* masks = (opt.mode & (MODE_CHECK | MODE_UPDATE) ? opt.crc_accept : opt.files_accept); if (!file_mask_match(masks, file->path)) return 0; } if (must_skip_file(file)) return 0; if (opt.mode & (MODE_CHECK | MODE_CHECK_EMBEDDED)) { res = check_hash_file(file, not_root); } else { if (opt.mode & MODE_UPDATE) { res = update_hash_file(file); } else { /* default mode: calculate hash */ const char* print_path = file->path; if (print_path[0] == '.' && IS_PATH_SEPARATOR(print_path[1])) print_path += 2; res = calculate_and_print_sums(rhash_data.out, file, print_path); if (rhash_data.interrupted) return 0; rhash_data.processed++; } } } if (res < 0) rhash_data.error_flag = 1; return 1; }
/** * Walk directory tree and call given callback function to process each file/directory. * * @param start_dir path to the directory to walk recursively * @param data the options specifying how to walk the directory tree * @return 0 on success, -1 on error */ static int dir_scan(file_t* start_dir, file_search_data* data) { dir_entry *dirs_stack = NULL; /* root of the dir_list */ dir_iterator* it; int level = 0; int max_depth = data->max_depth; int options = data->options; file_t file; if (max_depth < 0 || max_depth >= MAX_DIRS_DEPTH) { max_depth = MAX_DIRS_DEPTH - 1; } /* skip the directory if max_depth == 0 */ if (!max_depth) return 0; if (!FILE_ISDIR(start_dir)) { errno = ENOTDIR; return -1; } /* check if we should descend into the root directory */ if ((options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == 0) { if (!data->call_back(start_dir, data->call_back_data)) return 0; } /* allocate array of counters of directory elements */ it = (dir_iterator*)malloc((MAX_DIRS_DEPTH + 1) * sizeof(dir_iterator)); if (!it) { return -1; } /* push dummy counter for the root element */ it[0].count = 1; it[0].dir_path = 0; memset(&file, 0, sizeof(file)); while (!(data->options & FIND_CANCEL)) { dir_entry **insert_at; char* dir_path; DIR *dp; struct dirent *de; /* climb down from the tree */ while (--it[level].count < 0) { /* do not need this dir_path anymore */ free(it[level].dir_path); if (--level < 0) { /* walked the whole tree */ assert(!dirs_stack); free(it); return 0; } } assert(level >= 0 && it[level].count >= 0); /* take a filename from dirs_stack and construct the next path */ if (level) { assert(dirs_stack != NULL); dir_path = make_path(it[level].dir_path, dirs_stack->filename); dir_entry_drop_head(&dirs_stack); } else { /* the first cycle: start from a root directory */ dir_path = rsh_strdup(start_dir->path); } if (!dir_path) continue; /* fill the next level of directories */ level++; assert(level < MAX_DIRS_DEPTH); it[level].count = 0; it[level].dir_path = dir_path; if ((options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == FIND_WALK_DEPTH_FIRST) { int res; file_init(&file, dir_path, 1); res = file_stat2(&file, USE_LSTAT); /* check if we should skip the directory */ if (res < 0 || !data->call_back(&file, data->call_back_data)) { if (res < 0 && (options & FIND_LOG_ERRORS)) { data->errors_count++; } file_cleanup(&file); continue; } } file_cleanup(&file); /* step into directory */ dp = opendir(dir_path); if (!dp) continue; insert_at = &dirs_stack; while ((de = readdir(dp)) != NULL) { int res; /* skip the "." and ".." directories */ if (IS_CURRENT_OR_PARENT_DIR(de->d_name)) continue; file.mode = 0; file.path = make_path(dir_path, de->d_name); if (!file.path) continue; res = file_stat2(&file, USE_LSTAT); if (res >= 0) { /* process the file or directory */ if (FILE_ISDIR(&file) && (options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS))) { res = ((options & FIND_FOLLOW_SYMLINKS) || !FILE_ISLNK(&file)); } else if (FILE_ISREG(&file)) { /* handle file by callback function */ res = data->call_back(&file, data->call_back_data); } /* check if file is a directory and we need to walk it, */ /* but don't go deeper than max_depth */ if (FILE_ISDIR(&file) && res && level < max_depth && ((options & FIND_FOLLOW_SYMLINKS) || !FILE_ISLNK(&file))) { /* add the directory name to the dirs_stack */ if (dir_entry_insert(insert_at, de->d_name, file.mode)) { /* the directory name was successfully inserted */ insert_at = &((*insert_at)->next); it[level].count++; } } } else if (options & FIND_LOG_ERRORS) { /* report error only if FIND_LOG_ERRORS option is set */ log_file_error(file.path); data->errors_count++; } file_cleanup(&file); } closedir(dp); } while (dirs_stack) { dir_entry_drop_head(&dirs_stack); } while (level) { free(it[level--].dir_path); } free(it); assert(file.path == 0); return 0; }