• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
op_file_readable(char const * file)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 
op_get_mtime(char const * file)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 
create_dir(char const * dir)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 
create_path(char const * path)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 
is_dot_or_dotdot(char const * name)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. */
make_pathname_from_dirent(char const * basedir,struct dirent * ent,struct stat * st_buf)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 
get_matching_pathnames(void * name_list,get_pathname_callback getpathname,char const * base_dir,char const * filter,enum recursion_type recursion)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