Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
18static 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
27void perf_data__close_dir(struct perf_data *data)
28{
29 close_dir(data->dir.files, data->dir.nr);
30}
31
32int 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
59out_err:
60 close_dir(files, i);
61 return ret;
62}
63
64int 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
116out_err:
117 close_dir(files, nr);
118 return ret;
119}
120
121static 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
142static 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
176static 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
217static 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
232static 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
248static 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
257int 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
271void perf_data__close(struct perf_data *data)
272{
273 zfree(&data->file.path);
274 close(data->file.fd);
275}
276
277ssize_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
283ssize_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
289int 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;
325out:
326 free(new_filepath);
327 return ret;
328}