Home | History | Annotate | Download | only in libutil
      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