op_file.c revision 10e23eebca4175a8dfe3a788b2bebacb1fcfce54
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
26int 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
33time_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
44int 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
57int 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
78inline 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. */
88static 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		free(name);
99		name = NULL;
100	}
101	return name;
102}
103
104
105int get_matching_pathnames(void * name_list, get_pathname_callback getpathname,
106			   char const * base_dir, char const * filter,
107			   enum recursion_type recursion)
108{
109/* The algorithm below depends on recursion type (of which there are 3)
110 * and whether the current dirent matches the filter.  There are 6 possible
111 * different behaviors, which is why we define 6 case below in the switch
112 * statement of the algorithm.  Actually, when the recursion type is
113 * MATCH_DIR_ONLY_RECURSION, the behavior is the same, whether or not the dir
114 * entry matches the filter.  However, the behavior of the recursion types
115 * NO_RECURSION and MATCH_ANY_ENTRY_RECURSION do depend on the dir entry
116 * filter match, so for simplicity, we perform this match for all recursion
117 * types and logically OR the match result with the  value of the passed
118 * recursion_type.
119 */
120#define NO_MATCH 0
121#define MATCH 1
122
123	DIR * dir;
124	struct dirent * ent;
125	struct stat stat_buffer;
126	int match;
127	char * name = NULL;
128
129	if (!(dir = opendir(base_dir)))
130		return -1;
131	while ((ent = readdir(dir)) != 0) {
132		if (is_dot_or_dotdot(ent->d_name))
133			continue;
134		if (fnmatch(filter, ent->d_name, 0) == 0)
135			match = 1;
136		else
137			match = 0;
138
139		switch (recursion | match) {
140		case NO_RECURSION + NO_MATCH:
141		case MATCH_ANY_ENTRY_RECURSION + NO_MATCH:
142			// nothing to do but continue the loop
143			break;
144		case NO_RECURSION + MATCH:
145			getpathname(ent->d_name, name_list);
146			break;
147		case MATCH_ANY_ENTRY_RECURSION + MATCH:
148			name = make_pathname_from_dirent(base_dir, ent,
149						       &stat_buffer);
150			if (name && S_ISDIR(stat_buffer.st_mode) &&
151			    !S_ISLNK(stat_buffer.st_mode)) {
152				get_matching_pathnames(
153					name_list, getpathname,
154					name, filter, recursion);
155			} else {
156				getpathname(name, name_list);
157			}
158			free(name);
159			break;
160		case MATCH_DIR_ONLY_RECURSION + NO_MATCH:
161		case MATCH_DIR_ONLY_RECURSION + MATCH:
162			name = make_pathname_from_dirent(base_dir, ent,
163						       &stat_buffer);
164			if (name && S_ISDIR(stat_buffer.st_mode) &&
165			    !S_ISLNK(stat_buffer.st_mode)) {
166				/* Check if full directory name contains
167				 * match to the filter; if so, add it to
168				 * name_list and quit; else, recurse.
169				 */
170				if (!fnmatch(filter, name, 0)) {
171					getpathname(name, name_list);
172				} else {
173					get_matching_pathnames(
174						name_list, getpathname,
175						name, filter, recursion);
176				}
177			}
178			free(name);
179			break;
180		}
181	}
182	closedir(dir);
183
184	return 0;
185}
186