cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

data.c (9921B)


      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			ret = -ENOMEM;
     53			goto out_err;
     54		}
     55
     56		ret = open(file->path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
     57		if (ret < 0) {
     58			ret = -errno;
     59			goto out_err;
     60		}
     61
     62		file->fd = ret;
     63	}
     64
     65	data->dir.version = PERF_DIR_VERSION;
     66	data->dir.files   = files;
     67	data->dir.nr      = nr;
     68	return 0;
     69
     70out_err:
     71	close_dir(files, i);
     72	return ret;
     73}
     74
     75int perf_data__open_dir(struct perf_data *data)
     76{
     77	struct perf_data_file *files = NULL;
     78	struct dirent *dent;
     79	int ret = -1;
     80	DIR *dir;
     81	int nr = 0;
     82
     83	/*
     84	 * Directory containing a single regular perf data file which is already
     85	 * open, means there is nothing more to do here.
     86	 */
     87	if (perf_data__is_single_file(data))
     88		return 0;
     89
     90	if (WARN_ON(!data->is_dir))
     91		return -EINVAL;
     92
     93	/* The version is provided by DIR_FORMAT feature. */
     94	if (WARN_ON(data->dir.version != PERF_DIR_VERSION))
     95		return -1;
     96
     97	dir = opendir(data->path);
     98	if (!dir)
     99		return -EINVAL;
    100
    101	while ((dent = readdir(dir)) != NULL) {
    102		struct perf_data_file *file;
    103		char path[PATH_MAX];
    104		struct stat st;
    105
    106		snprintf(path, sizeof(path), "%s/%s", data->path, dent->d_name);
    107		if (stat(path, &st))
    108			continue;
    109
    110		if (!S_ISREG(st.st_mode) || strncmp(dent->d_name, "data.", 5))
    111			continue;
    112
    113		ret = -ENOMEM;
    114
    115		file = realloc(files, (nr + 1) * sizeof(*files));
    116		if (!file)
    117			goto out_err;
    118
    119		files = file;
    120		file = &files[nr++];
    121
    122		file->path = strdup(path);
    123		if (!file->path)
    124			goto out_err;
    125
    126		ret = open(file->path, O_RDONLY);
    127		if (ret < 0)
    128			goto out_err;
    129
    130		file->fd = ret;
    131		file->size = st.st_size;
    132	}
    133
    134	if (!files)
    135		return -EINVAL;
    136
    137	data->dir.files = files;
    138	data->dir.nr    = nr;
    139	return 0;
    140
    141out_err:
    142	close_dir(files, nr);
    143	return ret;
    144}
    145
    146int perf_data__update_dir(struct perf_data *data)
    147{
    148	int i;
    149
    150	if (WARN_ON(!data->is_dir))
    151		return -EINVAL;
    152
    153	for (i = 0; i < data->dir.nr; i++) {
    154		struct perf_data_file *file = &data->dir.files[i];
    155		struct stat st;
    156
    157		if (fstat(file->fd, &st))
    158			return -1;
    159
    160		file->size = st.st_size;
    161	}
    162
    163	return 0;
    164}
    165
    166static bool check_pipe(struct perf_data *data)
    167{
    168	struct stat st;
    169	bool is_pipe = false;
    170	int fd = perf_data__is_read(data) ?
    171		 STDIN_FILENO : STDOUT_FILENO;
    172
    173	if (!data->path) {
    174		if (!fstat(fd, &st) && S_ISFIFO(st.st_mode))
    175			is_pipe = true;
    176	} else {
    177		if (!strcmp(data->path, "-"))
    178			is_pipe = true;
    179	}
    180
    181	if (is_pipe) {
    182		if (data->use_stdio) {
    183			const char *mode;
    184
    185			mode = perf_data__is_read(data) ? "r" : "w";
    186			data->file.fptr = fdopen(fd, mode);
    187
    188			if (data->file.fptr == NULL) {
    189				data->file.fd = fd;
    190				data->use_stdio = false;
    191			}
    192		} else {
    193			data->file.fd = fd;
    194		}
    195	}
    196
    197	return data->is_pipe = is_pipe;
    198}
    199
    200static int check_backup(struct perf_data *data)
    201{
    202	struct stat st;
    203
    204	if (perf_data__is_read(data))
    205		return 0;
    206
    207	if (!stat(data->path, &st) && st.st_size) {
    208		char oldname[PATH_MAX];
    209		int ret;
    210
    211		snprintf(oldname, sizeof(oldname), "%s.old",
    212			 data->path);
    213
    214		ret = rm_rf_perf_data(oldname);
    215		if (ret) {
    216			pr_err("Can't remove old data: %s (%s)\n",
    217			       ret == -2 ?
    218			       "Unknown file found" : strerror(errno),
    219			       oldname);
    220			return -1;
    221		}
    222
    223		if (rename(data->path, oldname)) {
    224			pr_err("Can't move data: %s (%s to %s)\n",
    225			       strerror(errno),
    226			       data->path, oldname);
    227			return -1;
    228		}
    229	}
    230
    231	return 0;
    232}
    233
    234static bool is_dir(struct perf_data *data)
    235{
    236	struct stat st;
    237
    238	if (stat(data->path, &st))
    239		return false;
    240
    241	return (st.st_mode & S_IFMT) == S_IFDIR;
    242}
    243
    244static int open_file_read(struct perf_data *data)
    245{
    246	int flags = data->in_place_update ? O_RDWR : O_RDONLY;
    247	struct stat st;
    248	int fd;
    249	char sbuf[STRERR_BUFSIZE];
    250
    251	fd = open(data->file.path, flags);
    252	if (fd < 0) {
    253		int err = errno;
    254
    255		pr_err("failed to open %s: %s", data->file.path,
    256			str_error_r(err, sbuf, sizeof(sbuf)));
    257		if (err == ENOENT && !strcmp(data->file.path, "perf.data"))
    258			pr_err("  (try 'perf record' first)");
    259		pr_err("\n");
    260		return -err;
    261	}
    262
    263	if (fstat(fd, &st) < 0)
    264		goto out_close;
    265
    266	if (!data->force && st.st_uid && (st.st_uid != geteuid())) {
    267		pr_err("File %s not owned by current user or root (use -f to override)\n",
    268		       data->file.path);
    269		goto out_close;
    270	}
    271
    272	if (!st.st_size) {
    273		pr_info("zero-sized data (%s), nothing to do!\n",
    274			data->file.path);
    275		goto out_close;
    276	}
    277
    278	data->file.size = st.st_size;
    279	return fd;
    280
    281 out_close:
    282	close(fd);
    283	return -1;
    284}
    285
    286static int open_file_write(struct perf_data *data)
    287{
    288	int fd;
    289	char sbuf[STRERR_BUFSIZE];
    290
    291	fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC,
    292		  S_IRUSR|S_IWUSR);
    293
    294	if (fd < 0)
    295		pr_err("failed to open %s : %s\n", data->file.path,
    296			str_error_r(errno, sbuf, sizeof(sbuf)));
    297
    298	return fd;
    299}
    300
    301static int open_file(struct perf_data *data)
    302{
    303	int fd;
    304
    305	fd = perf_data__is_read(data) ?
    306	     open_file_read(data) : open_file_write(data);
    307
    308	if (fd < 0) {
    309		zfree(&data->file.path);
    310		return -1;
    311	}
    312
    313	data->file.fd = fd;
    314	return 0;
    315}
    316
    317static int open_file_dup(struct perf_data *data)
    318{
    319	data->file.path = strdup(data->path);
    320	if (!data->file.path)
    321		return -ENOMEM;
    322
    323	return open_file(data);
    324}
    325
    326static int open_dir(struct perf_data *data)
    327{
    328	int ret;
    329
    330	/*
    331	 * So far we open only the header, so we can read the data version and
    332	 * layout.
    333	 */
    334	if (asprintf(&data->file.path, "%s/data", data->path) < 0)
    335		return -1;
    336
    337	if (perf_data__is_write(data) &&
    338	    mkdir(data->path, S_IRWXU) < 0)
    339		return -1;
    340
    341	ret = open_file(data);
    342
    343	/* Cleanup whatever we managed to create so far. */
    344	if (ret && perf_data__is_write(data))
    345		rm_rf_perf_data(data->path);
    346
    347	return ret;
    348}
    349
    350int perf_data__open(struct perf_data *data)
    351{
    352	if (check_pipe(data))
    353		return 0;
    354
    355	/* currently it allows stdio for pipe only */
    356	data->use_stdio = false;
    357
    358	if (!data->path)
    359		data->path = "perf.data";
    360
    361	if (check_backup(data))
    362		return -1;
    363
    364	if (perf_data__is_read(data))
    365		data->is_dir = is_dir(data);
    366
    367	return perf_data__is_dir(data) ?
    368	       open_dir(data) : open_file_dup(data);
    369}
    370
    371void perf_data__close(struct perf_data *data)
    372{
    373	if (perf_data__is_dir(data))
    374		perf_data__close_dir(data);
    375
    376	zfree(&data->file.path);
    377
    378	if (data->use_stdio)
    379		fclose(data->file.fptr);
    380	else
    381		close(data->file.fd);
    382}
    383
    384ssize_t perf_data__read(struct perf_data *data, void *buf, size_t size)
    385{
    386	if (data->use_stdio) {
    387		if (fread(buf, size, 1, data->file.fptr) == 1)
    388			return size;
    389		return feof(data->file.fptr) ? 0 : -1;
    390	}
    391	return readn(data->file.fd, buf, size);
    392}
    393
    394ssize_t perf_data_file__write(struct perf_data_file *file,
    395			      void *buf, size_t size)
    396{
    397	return writen(file->fd, buf, size);
    398}
    399
    400ssize_t perf_data__write(struct perf_data *data,
    401			      void *buf, size_t size)
    402{
    403	if (data->use_stdio) {
    404		if (fwrite(buf, size, 1, data->file.fptr) == 1)
    405			return size;
    406		return -1;
    407	}
    408	return perf_data_file__write(&data->file, buf, size);
    409}
    410
    411int perf_data__switch(struct perf_data *data,
    412			   const char *postfix,
    413			   size_t pos, bool at_exit,
    414			   char **new_filepath)
    415{
    416	int ret;
    417
    418	if (check_pipe(data))
    419		return -EINVAL;
    420	if (perf_data__is_read(data))
    421		return -EINVAL;
    422
    423	if (asprintf(new_filepath, "%s.%s", data->path, postfix) < 0)
    424		return -ENOMEM;
    425
    426	/*
    427	 * Only fire a warning, don't return error, continue fill
    428	 * original file.
    429	 */
    430	if (rename(data->path, *new_filepath))
    431		pr_warning("Failed to rename %s to %s\n", data->path, *new_filepath);
    432
    433	if (!at_exit) {
    434		close(data->file.fd);
    435		ret = perf_data__open(data);
    436		if (ret < 0)
    437			goto out;
    438
    439		if (lseek(data->file.fd, pos, SEEK_SET) == (off_t)-1) {
    440			ret = -errno;
    441			pr_debug("Failed to lseek to %zu: %s",
    442				 pos, strerror(errno));
    443			goto out;
    444		}
    445	}
    446	ret = data->file.fd;
    447out:
    448	return ret;
    449}
    450
    451unsigned long perf_data__size(struct perf_data *data)
    452{
    453	u64 size = data->file.size;
    454	int i;
    455
    456	if (perf_data__is_single_file(data))
    457		return size;
    458
    459	for (i = 0; i < data->dir.nr; i++) {
    460		struct perf_data_file *file = &data->dir.files[i];
    461
    462		size += file->size;
    463	}
    464
    465	return size;
    466}
    467
    468int perf_data__make_kcore_dir(struct perf_data *data, char *buf, size_t buf_sz)
    469{
    470	int ret;
    471
    472	if (!data->is_dir)
    473		return -1;
    474
    475	ret = snprintf(buf, buf_sz, "%s/kcore_dir", data->path);
    476	if (ret < 0 || (size_t)ret >= buf_sz)
    477		return -1;
    478
    479	return mkdir(buf, S_IRWXU);
    480}
    481
    482bool has_kcore_dir(const char *path)
    483{
    484	char *kcore_dir;
    485	int ret;
    486
    487	if (asprintf(&kcore_dir, "%s/kcore_dir", path) < 0)
    488		return false;
    489
    490	ret = access(kcore_dir, F_OK);
    491
    492	free(kcore_dir);
    493	return !ret;
    494}
    495
    496char *perf_data__kallsyms_name(struct perf_data *data)
    497{
    498	char *kallsyms_name;
    499	struct stat st;
    500
    501	if (!data->is_dir)
    502		return NULL;
    503
    504	if (asprintf(&kallsyms_name, "%s/kcore_dir/kallsyms", data->path) < 0)
    505		return NULL;
    506
    507	if (stat(kallsyms_name, &st)) {
    508		free(kallsyms_name);
    509		return NULL;
    510	}
    511
    512	return kallsyms_name;
    513}
    514
    515bool is_perf_data(const char *path)
    516{
    517	bool ret = false;
    518	FILE *file;
    519	u64 magic;
    520
    521	file = fopen(path, "r");
    522	if (!file)
    523		return false;
    524
    525	if (fread(&magic, 1, 8, file) < 8)
    526		goto out;
    527
    528	ret = is_perf_magic(magic);
    529out:
    530	fclose(file);
    531	return ret;
    532}