xref: /openbmc/linux/tools/perf/util/data.c (revision ed1666f6)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
3 #include <linux/kernel.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <asm/bug.h>
11 #include <sys/types.h>
12 #include <dirent.h>
13 
14 #include "data.h"
15 #include "util.h"
16 #include "debug.h"
17 
18 static void close_dir(struct perf_data_file *files, int nr)
19 {
20 	while (--nr >= 1) {
21 		close(files[nr].fd);
22 		free(files[nr].path);
23 	}
24 	free(files);
25 }
26 
27 void perf_data__close_dir(struct perf_data *data)
28 {
29 	close_dir(data->dir.files, data->dir.nr);
30 }
31 
32 int perf_data__create_dir(struct perf_data *data, int nr)
33 {
34 	struct perf_data_file *files = NULL;
35 	int i, ret = -1;
36 
37 	files = zalloc(nr * sizeof(*files));
38 	if (!files)
39 		return -ENOMEM;
40 
41 	data->dir.files = files;
42 	data->dir.nr    = nr;
43 
44 	for (i = 0; i < nr; i++) {
45 		struct perf_data_file *file = &files[i];
46 
47 		if (asprintf(&file->path, "%s/data.%d", data->path, i) < 0)
48 			goto out_err;
49 
50 		ret = open(file->path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
51 		if (ret < 0)
52 			goto out_err;
53 
54 		file->fd = ret;
55 	}
56 
57 	return 0;
58 
59 out_err:
60 	close_dir(files, i);
61 	return ret;
62 }
63 
64 int perf_data__open_dir(struct perf_data *data)
65 {
66 	struct perf_data_file *files = NULL;
67 	struct dirent *dent;
68 	int ret = -1;
69 	DIR *dir;
70 	int nr = 0;
71 
72 	dir = opendir(data->path);
73 	if (!dir)
74 		return -EINVAL;
75 
76 	while ((dent = readdir(dir)) != NULL) {
77 		struct perf_data_file *file;
78 		char path[PATH_MAX];
79 		struct stat st;
80 
81 		snprintf(path, sizeof(path), "%s/%s", data->path, dent->d_name);
82 		if (stat(path, &st))
83 			continue;
84 
85 		if (!S_ISREG(st.st_mode) || strncmp(dent->d_name, "data", 4))
86 			continue;
87 
88 		ret = -ENOMEM;
89 
90 		file = realloc(files, (nr + 1) * sizeof(*files));
91 		if (!file)
92 			goto out_err;
93 
94 		files = file;
95 		file = &files[nr++];
96 
97 		file->path = strdup(path);
98 		if (!file->path)
99 			goto out_err;
100 
101 		ret = open(file->path, O_RDONLY);
102 		if (ret < 0)
103 			goto out_err;
104 
105 		file->fd = ret;
106 		file->size = st.st_size;
107 	}
108 
109 	if (!files)
110 		return -EINVAL;
111 
112 	data->dir.files = files;
113 	data->dir.nr    = nr;
114 	return 0;
115 
116 out_err:
117 	close_dir(files, nr);
118 	return ret;
119 }
120 
121 static bool check_pipe(struct perf_data *data)
122 {
123 	struct stat st;
124 	bool is_pipe = false;
125 	int fd = perf_data__is_read(data) ?
126 		 STDIN_FILENO : STDOUT_FILENO;
127 
128 	if (!data->path) {
129 		if (!fstat(fd, &st) && S_ISFIFO(st.st_mode))
130 			is_pipe = true;
131 	} else {
132 		if (!strcmp(data->path, "-"))
133 			is_pipe = true;
134 	}
135 
136 	if (is_pipe)
137 		data->file.fd = fd;
138 
139 	return data->is_pipe = is_pipe;
140 }
141 
142 static int check_backup(struct perf_data *data)
143 {
144 	struct stat st;
145 
146 	if (perf_data__is_read(data))
147 		return 0;
148 
149 	if (!stat(data->path, &st) && st.st_size) {
150 		char oldname[PATH_MAX];
151 		int ret;
152 
153 		snprintf(oldname, sizeof(oldname), "%s.old",
154 			 data->path);
155 
156 		ret = rm_rf_perf_data(oldname);
157 		if (ret) {
158 			pr_err("Can't remove old data: %s (%s)\n",
159 			       ret == -2 ?
160 			       "Unknown file found" : strerror(errno),
161 			       oldname);
162 			return -1;
163 		}
164 
165 		if (rename(data->path, oldname)) {
166 			pr_err("Can't move data: %s (%s to %s)\n",
167 			       strerror(errno),
168 			       data->path, oldname);
169 			return -1;
170 		}
171 	}
172 
173 	return 0;
174 }
175 
176 static int open_file_read(struct perf_data *data)
177 {
178 	struct stat st;
179 	int fd;
180 	char sbuf[STRERR_BUFSIZE];
181 
182 	fd = open(data->file.path, O_RDONLY);
183 	if (fd < 0) {
184 		int err = errno;
185 
186 		pr_err("failed to open %s: %s", data->file.path,
187 			str_error_r(err, sbuf, sizeof(sbuf)));
188 		if (err == ENOENT && !strcmp(data->file.path, "perf.data"))
189 			pr_err("  (try 'perf record' first)");
190 		pr_err("\n");
191 		return -err;
192 	}
193 
194 	if (fstat(fd, &st) < 0)
195 		goto out_close;
196 
197 	if (!data->force && st.st_uid && (st.st_uid != geteuid())) {
198 		pr_err("File %s not owned by current user or root (use -f to override)\n",
199 		       data->file.path);
200 		goto out_close;
201 	}
202 
203 	if (!st.st_size) {
204 		pr_info("zero-sized data (%s), nothing to do!\n",
205 			data->file.path);
206 		goto out_close;
207 	}
208 
209 	data->file.size = st.st_size;
210 	return fd;
211 
212  out_close:
213 	close(fd);
214 	return -1;
215 }
216 
217 static int open_file_write(struct perf_data *data)
218 {
219 	int fd;
220 	char sbuf[STRERR_BUFSIZE];
221 
222 	fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC,
223 		  S_IRUSR|S_IWUSR);
224 
225 	if (fd < 0)
226 		pr_err("failed to open %s : %s\n", data->file.path,
227 			str_error_r(errno, sbuf, sizeof(sbuf)));
228 
229 	return fd;
230 }
231 
232 static int open_file(struct perf_data *data)
233 {
234 	int fd;
235 
236 	fd = perf_data__is_read(data) ?
237 	     open_file_read(data) : open_file_write(data);
238 
239 	if (fd < 0) {
240 		zfree(&data->file.path);
241 		return -1;
242 	}
243 
244 	data->file.fd = fd;
245 	return 0;
246 }
247 
248 static int open_file_dup(struct perf_data *data)
249 {
250 	data->file.path = strdup(data->path);
251 	if (!data->file.path)
252 		return -ENOMEM;
253 
254 	return open_file(data);
255 }
256 
257 int perf_data__open(struct perf_data *data)
258 {
259 	if (check_pipe(data))
260 		return 0;
261 
262 	if (!data->path)
263 		data->path = "perf.data";
264 
265 	if (check_backup(data))
266 		return -1;
267 
268 	return open_file_dup(data);
269 }
270 
271 void perf_data__close(struct perf_data *data)
272 {
273 	zfree(&data->file.path);
274 	close(data->file.fd);
275 }
276 
277 ssize_t perf_data_file__write(struct perf_data_file *file,
278 			      void *buf, size_t size)
279 {
280 	return writen(file->fd, buf, size);
281 }
282 
283 ssize_t perf_data__write(struct perf_data *data,
284 			      void *buf, size_t size)
285 {
286 	return perf_data_file__write(&data->file, buf, size);
287 }
288 
289 int perf_data__switch(struct perf_data *data,
290 			   const char *postfix,
291 			   size_t pos, bool at_exit)
292 {
293 	char *new_filepath;
294 	int ret;
295 
296 	if (check_pipe(data))
297 		return -EINVAL;
298 	if (perf_data__is_read(data))
299 		return -EINVAL;
300 
301 	if (asprintf(&new_filepath, "%s.%s", data->path, postfix) < 0)
302 		return -ENOMEM;
303 
304 	/*
305 	 * Only fire a warning, don't return error, continue fill
306 	 * original file.
307 	 */
308 	if (rename(data->path, new_filepath))
309 		pr_warning("Failed to rename %s to %s\n", data->path, new_filepath);
310 
311 	if (!at_exit) {
312 		close(data->file.fd);
313 		ret = perf_data__open(data);
314 		if (ret < 0)
315 			goto out;
316 
317 		if (lseek(data->file.fd, pos, SEEK_SET) == (off_t)-1) {
318 			ret = -errno;
319 			pr_debug("Failed to lseek to %zu: %s",
320 				 pos, strerror(errno));
321 			goto out;
322 		}
323 	}
324 	ret = data->file.fd;
325 out:
326 	free(new_filepath);
327 	return ret;
328 }
329