| /** |
| * @file op_file.c |
| * Useful file management helpers |
| * |
| * @remark Copyright 2002 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author John Levon |
| * @author Philippe Elie |
| */ |
| |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <dirent.h> |
| #include <fnmatch.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <limits.h> |
| |
| #include "op_file.h" |
| #include "op_libiberty.h" |
| |
| int op_file_readable(char const * file) |
| { |
| struct stat st; |
| return !stat(file, &st) && S_ISREG(st.st_mode) && !access(file, R_OK); |
| } |
| |
| |
| time_t op_get_mtime(char const * file) |
| { |
| struct stat st; |
| |
| if (stat(file, &st)) |
| return 0; |
| |
| return st.st_mtime; |
| } |
| |
| |
| int create_dir(char const * dir) |
| { |
| if (mkdir(dir, 0755)) { |
| /* FIXME: Does not verify existing is a dir */ |
| if (errno == EEXIST) |
| return 0; |
| return errno; |
| } |
| |
| return 0; |
| } |
| |
| |
| int create_path(char const * path) |
| { |
| int ret = 0; |
| |
| char * str = xstrdup(path); |
| |
| char * pos = str[0] == '/' ? str + 1 : str; |
| |
| for ( ; (pos = strchr(pos, '/')) != NULL; ++pos) { |
| *pos = '\0'; |
| ret = create_dir(str); |
| *pos = '/'; |
| if (ret) |
| break; |
| } |
| |
| free(str); |
| return ret; |
| } |
| |
| |
| inline static int is_dot_or_dotdot(char const * name) |
| { |
| return name[0] == '.' && |
| (name[1] == '\0' || |
| (name[1] == '.' && name[2] == '\0')); |
| } |
| |
| |
| /* If non-null is returned, the caller is responsible for freeing |
| * the memory allocated for the return value. */ |
| static char * make_pathname_from_dirent(char const * basedir, |
| struct dirent * ent, |
| struct stat * st_buf) |
| { |
| int name_len; |
| char * name; |
| name_len = strlen(basedir) + strlen("/") + strlen(ent->d_name) + 1; |
| name = xmalloc(name_len); |
| sprintf(name, "%s/%s", basedir, ent->d_name); |
| if (stat(name, st_buf) != 0) |
| { |
| struct stat lstat_buf; |
| int err = errno; |
| if (lstat(name, &lstat_buf) == 0 && |
| S_ISLNK(lstat_buf.st_mode)) { |
| // dangling symlink -- silently ignore |
| } else { |
| fprintf(stderr, "stat failed for %s (%s)\n", |
| name, strerror(err)); |
| } |
| free(name); |
| name = NULL; |
| } |
| return name; |
| } |
| |
| |
| int get_matching_pathnames(void * name_list, get_pathname_callback getpathname, |
| char const * base_dir, char const * filter, |
| enum recursion_type recursion) |
| { |
| /* The algorithm below depends on recursion type (of which there are 3) |
| * and whether the current dirent matches the filter. There are 6 possible |
| * different behaviors, which is why we define 6 case below in the switch |
| * statement of the algorithm. Actually, when the recursion type is |
| * MATCH_DIR_ONLY_RECURSION, the behavior is the same, whether or not the dir |
| * entry matches the filter. However, the behavior of the recursion types |
| * NO_RECURSION and MATCH_ANY_ENTRY_RECURSION do depend on the dir entry |
| * filter match, so for simplicity, we perform this match for all recursion |
| * types and logically OR the match result with the value of the passed |
| * recursion_type. |
| */ |
| #define NO_MATCH 0 |
| #define MATCH 1 |
| |
| DIR * dir; |
| struct dirent * ent; |
| struct stat stat_buffer; |
| int match; |
| char * name = NULL; |
| |
| if (!(dir = opendir(base_dir))) |
| return -1; |
| while ((ent = readdir(dir)) != 0) { |
| if (is_dot_or_dotdot(ent->d_name)) |
| continue; |
| if (fnmatch(filter, ent->d_name, 0) == 0) |
| match = 1; |
| else |
| match = 0; |
| |
| switch (recursion | match) { |
| case NO_RECURSION + NO_MATCH: |
| case MATCH_ANY_ENTRY_RECURSION + NO_MATCH: |
| // nothing to do but continue the loop |
| break; |
| case NO_RECURSION + MATCH: |
| getpathname(ent->d_name, name_list); |
| break; |
| case MATCH_ANY_ENTRY_RECURSION + MATCH: |
| name = make_pathname_from_dirent(base_dir, ent, |
| &stat_buffer); |
| if (name) { |
| if (S_ISDIR(stat_buffer.st_mode)) { |
| get_matching_pathnames( |
| name_list, getpathname, |
| name, filter, recursion); |
| } else { |
| getpathname(name, name_list); |
| } |
| } |
| free(name); |
| break; |
| case MATCH_DIR_ONLY_RECURSION + NO_MATCH: |
| case MATCH_DIR_ONLY_RECURSION + MATCH: |
| name = make_pathname_from_dirent(base_dir, ent, |
| &stat_buffer); |
| if (name && S_ISDIR(stat_buffer.st_mode)) { |
| /* Check if full directory name contains |
| * match to the filter; if so, add it to |
| * name_list and quit; else, recurse. |
| */ |
| if (!fnmatch(filter, name, 0)) { |
| getpathname(name, name_list); |
| } else { |
| get_matching_pathnames( |
| name_list, getpathname, |
| name, filter, recursion); |
| } |
| } |
| free(name); |
| break; |
| } |
| } |
| closedir(dir); |
| |
| return 0; |
| } |