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