xref: /kernel/linux/linux-5.10/tools/perf/util/data.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/compiler.h>
3#include <linux/kernel.h>
4#include <linux/string.h>
5#include <linux/zalloc.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <unistd.h>
11#include <string.h>
12#include <asm/bug.h>
13#include <dirent.h>
14
15#include "data.h"
16#include "util.h" // rm_rf_perf_data()
17#include "debug.h"
18#include "header.h"
19#include <internal/lib.h>
20
21static void close_dir(struct perf_data_file *files, int nr)
22{
23	while (--nr >= 0) {
24		close(files[nr].fd);
25		zfree(&files[nr].path);
26	}
27	free(files);
28}
29
30void perf_data__close_dir(struct perf_data *data)
31{
32	close_dir(data->dir.files, data->dir.nr);
33}
34
35int perf_data__create_dir(struct perf_data *data, int nr)
36{
37	struct perf_data_file *files = NULL;
38	int i, ret;
39
40	if (WARN_ON(!data->is_dir))
41		return -EINVAL;
42
43	files = zalloc(nr * sizeof(*files));
44	if (!files)
45		return -ENOMEM;
46
47	for (i = 0; i < nr; i++) {
48		struct perf_data_file *file = &files[i];
49
50		ret = asprintf(&file->path, "%s/data.%d", data->path, i);
51		if (ret < 0)
52			goto out_err;
53
54		ret = open(file->path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
55		if (ret < 0)
56			goto out_err;
57
58		file->fd = ret;
59	}
60
61	data->dir.version = PERF_DIR_VERSION;
62	data->dir.files   = files;
63	data->dir.nr      = nr;
64	return 0;
65
66out_err:
67	close_dir(files, i);
68	return ret;
69}
70
71int perf_data__open_dir(struct perf_data *data)
72{
73	struct perf_data_file *files = NULL;
74	struct dirent *dent;
75	int ret = -1;
76	DIR *dir;
77	int nr = 0;
78
79	/*
80	 * Directory containing a single regular perf data file which is already
81	 * open, means there is nothing more to do here.
82	 */
83	if (perf_data__is_single_file(data))
84		return 0;
85
86	if (WARN_ON(!data->is_dir))
87		return -EINVAL;
88
89	/* The version is provided by DIR_FORMAT feature. */
90	if (WARN_ON(data->dir.version != PERF_DIR_VERSION))
91		return -1;
92
93	dir = opendir(data->path);
94	if (!dir)
95		return -EINVAL;
96
97	while ((dent = readdir(dir)) != NULL) {
98		struct perf_data_file *file;
99		char path[PATH_MAX];
100		struct stat st;
101
102		snprintf(path, sizeof(path), "%s/%s", data->path, dent->d_name);
103		if (stat(path, &st))
104			continue;
105
106		if (!S_ISREG(st.st_mode) || strncmp(dent->d_name, "data.", 5))
107			continue;
108
109		ret = -ENOMEM;
110
111		file = realloc(files, (nr + 1) * sizeof(*files));
112		if (!file)
113			goto out_err;
114
115		files = file;
116		file = &files[nr++];
117
118		file->path = strdup(path);
119		if (!file->path)
120			goto out_err;
121
122		ret = open(file->path, O_RDONLY);
123		if (ret < 0)
124			goto out_err;
125
126		file->fd = ret;
127		file->size = st.st_size;
128	}
129
130	closedir(dir);
131	if (!files)
132		return -EINVAL;
133
134	data->dir.files = files;
135	data->dir.nr    = nr;
136	return 0;
137
138out_err:
139	closedir(dir);
140	close_dir(files, nr);
141	return ret;
142}
143
144int perf_data__update_dir(struct perf_data *data)
145{
146	int i;
147
148	if (WARN_ON(!data->is_dir))
149		return -EINVAL;
150
151	for (i = 0; i < data->dir.nr; i++) {
152		struct perf_data_file *file = &data->dir.files[i];
153		struct stat st;
154
155		if (fstat(file->fd, &st))
156			return -1;
157
158		file->size = st.st_size;
159	}
160
161	return 0;
162}
163
164static bool check_pipe(struct perf_data *data)
165{
166	struct stat st;
167	bool is_pipe = false;
168	int fd = perf_data__is_read(data) ?
169		 STDIN_FILENO : STDOUT_FILENO;
170
171	if (!data->path) {
172		if (!fstat(fd, &st) && S_ISFIFO(st.st_mode))
173			is_pipe = true;
174	} else {
175		if (!strcmp(data->path, "-"))
176			is_pipe = true;
177	}
178
179	if (is_pipe)
180		data->file.fd = fd;
181
182	return data->is_pipe = is_pipe;
183}
184
185static int check_backup(struct perf_data *data)
186{
187	struct stat st;
188
189	if (perf_data__is_read(data))
190		return 0;
191
192	if (!stat(data->path, &st) && st.st_size) {
193		char oldname[PATH_MAX];
194		int ret;
195
196		snprintf(oldname, sizeof(oldname), "%s.old",
197			 data->path);
198
199		ret = rm_rf_perf_data(oldname);
200		if (ret) {
201			pr_err("Can't remove old data: %s (%s)\n",
202			       ret == -2 ?
203			       "Unknown file found" : strerror(errno),
204			       oldname);
205			return -1;
206		}
207
208		if (rename(data->path, oldname)) {
209			pr_err("Can't move data: %s (%s to %s)\n",
210			       strerror(errno),
211			       data->path, oldname);
212			return -1;
213		}
214	}
215
216	return 0;
217}
218
219static bool is_dir(struct perf_data *data)
220{
221	struct stat st;
222
223	if (stat(data->path, &st))
224		return false;
225
226	return (st.st_mode & S_IFMT) == S_IFDIR;
227}
228
229static int open_file_read(struct perf_data *data)
230{
231	struct stat st;
232	int fd;
233	char sbuf[STRERR_BUFSIZE];
234
235	fd = open(data->file.path, O_RDONLY);
236	if (fd < 0) {
237		int err = errno;
238
239		pr_err("failed to open %s: %s", data->file.path,
240			str_error_r(err, sbuf, sizeof(sbuf)));
241		if (err == ENOENT && !strcmp(data->file.path, "perf.data"))
242			pr_err("  (try 'perf record' first)");
243		pr_err("\n");
244		return -err;
245	}
246
247	if (fstat(fd, &st) < 0)
248		goto out_close;
249
250	if (!data->force && st.st_uid && (st.st_uid != geteuid())) {
251		pr_err("File %s not owned by current user or root (use -f to override)\n",
252		       data->file.path);
253		goto out_close;
254	}
255
256	if (!st.st_size) {
257		pr_info("zero-sized data (%s), nothing to do!\n",
258			data->file.path);
259		goto out_close;
260	}
261
262	data->file.size = st.st_size;
263	return fd;
264
265 out_close:
266	close(fd);
267	return -1;
268}
269
270static int open_file_write(struct perf_data *data)
271{
272	int fd;
273	char sbuf[STRERR_BUFSIZE];
274
275	fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC,
276		  S_IRUSR|S_IWUSR);
277
278	if (fd < 0)
279		pr_err("failed to open %s : %s\n", data->file.path,
280			str_error_r(errno, sbuf, sizeof(sbuf)));
281
282	return fd;
283}
284
285static int open_file(struct perf_data *data)
286{
287	int fd;
288
289	fd = perf_data__is_read(data) ?
290	     open_file_read(data) : open_file_write(data);
291
292	if (fd < 0) {
293		zfree(&data->file.path);
294		return -1;
295	}
296
297	data->file.fd = fd;
298	return 0;
299}
300
301static int open_file_dup(struct perf_data *data)
302{
303	data->file.path = strdup(data->path);
304	if (!data->file.path)
305		return -ENOMEM;
306
307	return open_file(data);
308}
309
310static int open_dir(struct perf_data *data)
311{
312	int ret;
313
314	/*
315	 * So far we open only the header, so we can read the data version and
316	 * layout.
317	 */
318	if (asprintf(&data->file.path, "%s/data", data->path) < 0)
319		return -1;
320
321	if (perf_data__is_write(data) &&
322	    mkdir(data->path, S_IRWXU) < 0)
323		return -1;
324
325	ret = open_file(data);
326
327	/* Cleanup whatever we managed to create so far. */
328	if (ret && perf_data__is_write(data))
329		rm_rf_perf_data(data->path);
330
331	return ret;
332}
333
334int perf_data__open(struct perf_data *data)
335{
336	if (check_pipe(data))
337		return 0;
338
339	if (!data->path)
340		data->path = "perf.data";
341
342	if (check_backup(data))
343		return -1;
344
345	if (perf_data__is_read(data))
346		data->is_dir = is_dir(data);
347
348	return perf_data__is_dir(data) ?
349	       open_dir(data) : open_file_dup(data);
350}
351
352void perf_data__close(struct perf_data *data)
353{
354	if (perf_data__is_dir(data))
355		perf_data__close_dir(data);
356
357	zfree(&data->file.path);
358	close(data->file.fd);
359}
360
361ssize_t perf_data_file__write(struct perf_data_file *file,
362			      void *buf, size_t size)
363{
364	return writen(file->fd, buf, size);
365}
366
367ssize_t perf_data__write(struct perf_data *data,
368			      void *buf, size_t size)
369{
370	return perf_data_file__write(&data->file, buf, size);
371}
372
373int perf_data__switch(struct perf_data *data,
374			   const char *postfix,
375			   size_t pos, bool at_exit,
376			   char **new_filepath)
377{
378	int ret;
379
380	if (check_pipe(data))
381		return -EINVAL;
382	if (perf_data__is_read(data))
383		return -EINVAL;
384
385	if (asprintf(new_filepath, "%s.%s", data->path, postfix) < 0)
386		return -ENOMEM;
387
388	/*
389	 * Only fire a warning, don't return error, continue fill
390	 * original file.
391	 */
392	if (rename(data->path, *new_filepath))
393		pr_warning("Failed to rename %s to %s\n", data->path, *new_filepath);
394
395	if (!at_exit) {
396		close(data->file.fd);
397		ret = perf_data__open(data);
398		if (ret < 0)
399			goto out;
400
401		if (lseek(data->file.fd, pos, SEEK_SET) == (off_t)-1) {
402			ret = -errno;
403			pr_debug("Failed to lseek to %zu: %s",
404				 pos, strerror(errno));
405			goto out;
406		}
407	}
408	ret = data->file.fd;
409out:
410	return ret;
411}
412
413unsigned long perf_data__size(struct perf_data *data)
414{
415	u64 size = data->file.size;
416	int i;
417
418	if (perf_data__is_single_file(data))
419		return size;
420
421	for (i = 0; i < data->dir.nr; i++) {
422		struct perf_data_file *file = &data->dir.files[i];
423
424		size += file->size;
425	}
426
427	return size;
428}
429
430int perf_data__make_kcore_dir(struct perf_data *data, char *buf, size_t buf_sz)
431{
432	int ret;
433
434	if (!data->is_dir)
435		return -1;
436
437	ret = snprintf(buf, buf_sz, "%s/kcore_dir", data->path);
438	if (ret < 0 || (size_t)ret >= buf_sz)
439		return -1;
440
441	return mkdir(buf, S_IRWXU);
442}
443
444char *perf_data__kallsyms_name(struct perf_data *data)
445{
446	char *kallsyms_name;
447	struct stat st;
448
449	if (!data->is_dir)
450		return NULL;
451
452	if (asprintf(&kallsyms_name, "%s/kcore_dir/kallsyms", data->path) < 0)
453		return NULL;
454
455	if (stat(kallsyms_name, &st)) {
456		free(kallsyms_name);
457		return NULL;
458	}
459
460	return kallsyms_name;
461}
462