1 /** 2 * @file op_file.c 3 * Useful file management helpers 4 * 5 * @remark Copyright 2002 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author John Levon 9 * @author Philippe Elie 10 */ 11 12 #include <sys/stat.h> 13 #include <unistd.h> 14 #include <fcntl.h> 15 #include <dirent.h> 16 #include <fnmatch.h> 17 #include <stdlib.h> 18 #include <stdio.h> 19 #include <errno.h> 20 #include <string.h> 21 #include <limits.h> 22 23 #include "op_file.h" 24 #include "op_libiberty.h" 25 26 int op_file_readable(char const * file) 27 { 28 struct stat st; 29 return !stat(file, &st) && S_ISREG(st.st_mode) && !access(file, R_OK); 30 } 31 32 33 time_t op_get_mtime(char const * file) 34 { 35 struct stat st; 36 37 if (stat(file, &st)) 38 return 0; 39 40 return st.st_mtime; 41 } 42 43 44 int create_dir(char const * dir) 45 { 46 if (mkdir(dir, 0755)) { 47 /* FIXME: Does not verify existing is a dir */ 48 if (errno == EEXIST) 49 return 0; 50 return errno; 51 } 52 53 return 0; 54 } 55 56 57 int create_path(char const * path) 58 { 59 int ret = 0; 60 61 char * str = xstrdup(path); 62 63 char * pos = str[0] == '/' ? str + 1 : str; 64 65 for ( ; (pos = strchr(pos, '/')) != NULL; ++pos) { 66 *pos = '\0'; 67 ret = create_dir(str); 68 *pos = '/'; 69 if (ret) 70 break; 71 } 72 73 free(str); 74 return ret; 75 } 76 77 78 inline static int is_dot_or_dotdot(char const * name) 79 { 80 return name[0] == '.' && 81 (name[1] == '\0' || 82 (name[1] == '.' && name[2] == '\0')); 83 } 84 85 86 /* If non-null is returned, the caller is responsible for freeing 87 * the memory allocated for the return value. */ 88 static char * make_pathname_from_dirent(char const * basedir, 89 struct dirent * ent, 90 struct stat * st_buf) 91 { 92 int name_len; 93 char * name; 94 name_len = strlen(basedir) + strlen("/") + strlen(ent->d_name) + 1; 95 name = xmalloc(name_len); 96 sprintf(name, "%s/%s", basedir, ent->d_name); 97 if (stat(name, st_buf) != 0) 98 { 99 struct stat lstat_buf; 100 int err = errno; 101 if (lstat(name, &lstat_buf) == 0 && 102 S_ISLNK(lstat_buf.st_mode)) { 103 // dangling symlink -- silently ignore 104 } else { 105 fprintf(stderr, "stat failed for %s (%s)\n", 106 name, strerror(err)); 107 } 108 free(name); 109 name = NULL; 110 } 111 return name; 112 } 113 114 115 int get_matching_pathnames(void * name_list, get_pathname_callback getpathname, 116 char const * base_dir, char const * filter, 117 enum recursion_type recursion) 118 { 119 /* The algorithm below depends on recursion type (of which there are 3) 120 * and whether the current dirent matches the filter. There are 6 possible 121 * different behaviors, which is why we define 6 case below in the switch 122 * statement of the algorithm. Actually, when the recursion type is 123 * MATCH_DIR_ONLY_RECURSION, the behavior is the same, whether or not the dir 124 * entry matches the filter. However, the behavior of the recursion types 125 * NO_RECURSION and MATCH_ANY_ENTRY_RECURSION do depend on the dir entry 126 * filter match, so for simplicity, we perform this match for all recursion 127 * types and logically OR the match result with the value of the passed 128 * recursion_type. 129 */ 130 #define NO_MATCH 0 131 #define MATCH 1 132 133 DIR * dir; 134 struct dirent * ent; 135 struct stat stat_buffer; 136 int match; 137 char * name = NULL; 138 139 if (!(dir = opendir(base_dir))) 140 return -1; 141 while ((ent = readdir(dir)) != 0) { 142 if (is_dot_or_dotdot(ent->d_name)) 143 continue; 144 if (fnmatch(filter, ent->d_name, 0) == 0) 145 match = 1; 146 else 147 match = 0; 148 149 switch (recursion | match) { 150 case NO_RECURSION + NO_MATCH: 151 case MATCH_ANY_ENTRY_RECURSION + NO_MATCH: 152 // nothing to do but continue the loop 153 break; 154 case NO_RECURSION + MATCH: 155 getpathname(ent->d_name, name_list); 156 break; 157 case MATCH_ANY_ENTRY_RECURSION + MATCH: 158 name = make_pathname_from_dirent(base_dir, ent, 159 &stat_buffer); 160 if (name) { 161 if (S_ISDIR(stat_buffer.st_mode)) { 162 get_matching_pathnames( 163 name_list, getpathname, 164 name, filter, recursion); 165 } else { 166 getpathname(name, name_list); 167 } 168 } 169 free(name); 170 break; 171 case MATCH_DIR_ONLY_RECURSION + NO_MATCH: 172 case MATCH_DIR_ONLY_RECURSION + MATCH: 173 name = make_pathname_from_dirent(base_dir, ent, 174 &stat_buffer); 175 if (name && S_ISDIR(stat_buffer.st_mode)) { 176 /* Check if full directory name contains 177 * match to the filter; if so, add it to 178 * name_list and quit; else, recurse. 179 */ 180 if (!fnmatch(filter, name, 0)) { 181 getpathname(name, name_list); 182 } else { 183 get_matching_pathnames( 184 name_list, getpathname, 185 name, filter, recursion); 186 } 187 } 188 free(name); 189 break; 190 } 191 } 192 closedir(dir); 193 194 return 0; 195 } 196