<commit_before>// SPDX-License-Identifier: LGPL-2.1-or-later
/*
 * libefiboot - library for the manipulation of EFI boot variables
 * Copyright 2012-2019 Red Hat, Inc.
 * Copyright (C) 2000-2001 Dell Computer Corporation <<EMAIL>>
 */

#include "fix_coverity.h"

#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h>
#include <unistd.h>

#include "efiboot.h"

typedef struct {
	int error;
	char *filename;
	char *function;
	int line;
	char *message;
} error_table_entry;

static error_table_entry *error_table;
static unsigned int current;

int PUBLIC NONNULL(2, 3, 4, 5, 6)
efi_error_get(unsigned int n,
	      char ** const filename,
	      char ** const function,
	      int *line,
	      char ** const message,
	      int *error
	      )
{
	if (!filename || !function || !line || !message || !error) {
		errno = EINVAL;
		return -1;
	}

	if (n >= current)
		return 0;

	*filename = error_table[n].filename;
	*function = error_table[n].function;
	*line = error_table[n].line;
	*message = error_table[n].message;
	*error = error_table[n].error;

	return 1;
}

static inline UNUSED void
clear_error_entry(error_table_entry *et)
{
	if (!et)
		return;

	if (et->filename)
		free(et->filename);
	if (et->function)
		free(et->function);
	if (et->message)
		free(et->message);

	memset(et, '\0', sizeof(*et));
}

int PUBLIC NONNULL(1, 2, 5) PRINTF(5, 6)
efi_error_set(const char *filename,
	      const char *function,
	      int line,
	      int error,
	      const char *fmt, ...)
{
	error_table_entry et = { 0, };
	error_table_entry *table;
	char *tmp;

	table = realloc(error_table, sizeof(et) * (current +1));
	if (!table)
		goto err;
	error_table = table;

	et.error = error;
	et.line = line;
	tmp = filename ? strdup(filename) : NULL;
	if (!tmp)
		goto err;
	et.filename = tmp;

	tmp = function ? strdup(function) : NULL;
	if (!tmp)
		goto err;
	et.function = tmp;

	if (fmt) {
		int rc;
		int saved_errno;
		va_list ap;

		tmp = NULL;
		va_start(ap, fmt);
		rc = vasprintf(&tmp, fmt, ap);
		saved_errno = errno;
		va_end(ap);
		errno = saved_errno;
		if (rc < 0)
			goto err;
		et.message = tmp;
	}

	memcpy(&error_table[current], &et, sizeof(et));
	current += 1;
	return current;
err:
	if (et.filename)
		free(et.filename);
	if (et.function)
		free(et.function);
	if (et.message)
		free(et.message);
	errno = ENOMEM;
	return -1;
}

void PUBLIC
efi_error_pop(void)
{
	if (current <= 0)
		return;

	current -= 1;
	clear_error_entry(&error_table[current]);
}

static int efi_verbose;
static FILE *efi_errlog, *efi_dbglog;
#ifndef ANDROID
static int efi_dbglog_fd = -1;
static intptr_t efi_dbglog_cookie;
static char efi_dbglog_buf[4096];
static bool efi_dbglog_memfd = false;
#endif
static int log_level;

void PUBLIC
efi_set_loglevel(int level)
{
	log_level = level;
}

#ifndef ANDROID
static ssize_t
dbglog_write(void *cookie, const char *buf, size_t size)
{
	FILE *log = efi_errlog ? efi_errlog : stderr;
	ssize_t ret = size;

	if (efi_get_verbose() >= log_level) {
		ret = fwrite(buf, 1, size, log);
	} else if (efi_dbglog_fd >= 0) {
		if (efi_dbglog_memfd)
			lseek(efi_dbglog_fd, 0, SEEK_SET);
		if ((intptr_t)cookie != 0 &&
		    (intptr_t)cookie == efi_dbglog_cookie &&
		    size > 0 &&
		    buf[size-1] == '\n')
			size -= 1;
		ret = write(efi_dbglog_fd, buf, size);
	}
	return ret;
}

static int
dbglog_seek(void *cookie UNUSED, off64_t *offset, int whence)
{
	FILE *log = efi_errlog ? efi_errlog : stderr;
	int rc;

	rc = fseek(log, *offset, whence);
	if (rc < 0)
		return rc;
	*offset = ftell(log);
	return 0;
}

static int
dbglog_close(void *cookie UNUSED)
{
	if (efi_dbglog_fd >= 0) {
		close(efi_dbglog_fd);
		efi_dbglog_fd = -1;
	}
	if (efi_errlog) {
		int ret = fclose(efi_errlog);
		efi_errlog = NULL;
		return ret;
	}

	errno = EBADF;
	return -1;
}
#endif

void PUBLIC
efi_error_clear(void)
{
	if (error_table) {
		for (unsigned int i = 0; i < current; i++) {
			error_table_entry *et = &error_table[i];

			clear_error_entry(et);
		}
		free(error_table);
	}
	error_table = NULL;
	current = 0;
}

void DESTRUCTOR
efi_error_fini(void)
{
	efi_error_clear();
	if (efi_dbglog) {
		fclose(efi_dbglog);
		efi_dbglog = NULL;
	}
}

static void CONSTRUCTOR
efi_error_init(void)
{
#ifndef ANDROID
	ssize_t bytes;
	cookie_io_functions_t io_funcs = {
		.write = dbglog_write,
		.seek = dbglog_seek,
		.close = dbglog_close,
	};

	efi_dbglog_fd = open("/dev/null", O_WRONLY|O_APPEND|O_CLOEXEC);
	if (efi_dbglog_fd < 0) {
		efi_dbglog_fd = memfd_create("efivar-debug.log", MFD_CLOEXEC);
		if (efi_dbglog_fd >= 0)
			efi_dbglog_memfd = true;
	}
	if (efi_dbglog_fd == -1)
		return;

	bytes = getrandom(&efi_dbglog_cookie, sizeof(efi_dbglog_cookie), 0);
	if (bytes < (ssize_t)sizeof(efi_dbglog_cookie))
		efi_dbglog_cookie = 0;

	efi_dbglog = fopencookie((void *)efi_dbglog_cookie, "a", io_funcs);
	if (efi_dbglog)
		setvbuf(efi_dbglog, efi_dbglog_buf, _IOLBF,
			sizeof(efi_dbglog_buf));
#endif
}

FILE PUBLIC *
efi_get_logfile(void)
{
	return efi_dbglog;
}

void PUBLIC
efi_set_verbose(int verbosity, FILE *errlog)
{
	efi_verbose = verbosity;
	if (errlog)
		efi_errlog = errlog;
}

int PUBLIC
efi_get_verbose(void)
{
	return efi_verbose;<commit_msg>Limit the writes in the error log to 32-bytes writes...<commit_after>// SPDX-License-Identifier: LGPL-2.1-or-later
/*
 * libefiboot - library for the manipulation of EFI boot variables
 * Copyright 2012-2019 Red Hat, Inc.
 * Copyright (C) 2000-2001 Dell Computer Corporation <<EMAIL>>
 */

#include "fix_coverity.h"

#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/random.h>
#include <unistd.h>

#include "efiboot.h"

typedef struct {
	int error;
	char *filename;
	char *function;
	int line;
	char *message;
} error_table_entry;

static error_table_entry *error_table;
static unsigned int current;

int PUBLIC NONNULL(2, 3, 4, 5, 6)
efi_error_get(unsigned int n,
	      char ** const filename,
	      char ** const function,
	      int *line,
	      char ** const message,
	      int *error
	      )
{
	if (!filename || !function || !line || !message || !error) {
		errno = EINVAL;
		return -1;
	}

	if (n >= current)
		return 0;

	*filename = error_table[n].filename;
	*function = error_table[n].function;
	*line = error_table[n].line;
	*message = error_table[n].message;
	*error = error_table[n].error;

	return 1;
}

static inline UNUSED void
clear_error_entry(error_table_entry *et)
{
	if (!et)
		return;

	if (et->filename)
		free(et->filename);
	if (et->function)
		free(et->function);
	if (et->message)
		free(et->message);

	memset(et, '\0', sizeof(*et));
}

int PUBLIC NONNULL(1, 2, 5) PRINTF(5, 6)
efi_error_set(const char *filename,
	      const char *function,
	      int line,
	      int error,
	      const char *fmt, ...)
{
	error_table_entry et = { 0, };
	error_table_entry *table;
	char *tmp;

	table = realloc(error_table, sizeof(et) * (current +1));
	if (!table)
		goto err;
	error_table = table;

	et.error = error;
	et.line = line;
	tmp = filename ? strdup(filename) : NULL;
	if (!tmp)
		goto err;
	et.filename = tmp;

	tmp = function ? strdup(function) : NULL;
	if (!tmp)
		goto err;
	et.function = tmp;

	if (fmt) {
		int rc;
		int saved_errno;
		va_list ap;

		tmp = NULL;
		va_start(ap, fmt);
		rc = vasprintf(&tmp, fmt, ap);
		saved_errno = errno;
		va_end(ap);
		errno = saved_errno;
		if (rc < 0)
			goto err;
		et.message = tmp;
	}

	memcpy(&error_table[current], &et, sizeof(et));
	current += 1;
	return current;
err:
	if (et.filename)
		free(et.filename);
	if (et.function)
		free(et.function);
	if (et.message)
		free(et.message);
	errno = ENOMEM;
	return -1;
}

void PUBLIC
efi_error_pop(void)
{
	if (current <= 0)
		return;

	current -= 1;
	clear_error_entry(&error_table[current]);
}

static int efi_verbose;
static FILE *efi_errlog, *efi_dbglog;
#ifndef ANDROID
static int efi_dbglog_fd = -1;
static intptr_t efi_dbglog_cookie;
static char efi_dbglog_buf[4096];
static bool efi_dbglog_memfd = false;
#endif
static int log_level;

void PUBLIC
efi_set_loglevel(int level)
{
	log_level = level;
}

#ifndef ANDROID
static ssize_t
dbglog_write(void *cookie, const char *buf, size_t size)
{
	FILE *log = efi_errlog ? efi_errlog : stderr;
	ssize_t ret = 0;

	while (ret < (ssize_t)size) {
		/*
		 * This is limited to 32 characters per write because if
		 * the only place this is going is strace logs, that's the
		 * default character buffer display size.  If it's going
		 * anywhere else, you won't really notice the difference,
		 * since we're not inserting newlines.
		 */
		ssize_t sz = MIN(size - ret, 32);

		if (efi_get_verbose() >= log_level) {
			sz = fwrite(buf + ret, 1, sz, log);
			if (sz < 1 && (ferror(log) || feof(log)))
				break;
		} else if (efi_dbglog_fd >= 0 && sz > 0) {
			if (efi_dbglog_memfd)
				lseek(efi_dbglog_fd, 0, SEEK_SET);
			if ((intptr_t)cookie != 0 &&
			    (intptr_t)cookie == efi_dbglog_cookie &&
			    (ret + sz) < 0 &&
			    buf[ret + sz - 1] == '\n')
				sz -= 1;
			sz = write(efi_dbglog_fd, buf + ret, sz);
			if (sz < 0)
				break;
		}
		ret += sz;
	}
	return ret;
}

static int
dbglog_seek(void *cookie UNUSED, off64_t *offset, int whence)
{
	FILE *log = efi_errlog ? efi_errlog : stderr;
	int rc;

	rc = fseek(log, *offset, whence);
	if (rc < 0)
		return rc;
	*offset = ftell(log);
	return 0;
}

static int
dbglog_close(void *cookie UNUSED)
{
	if (efi_dbglog_fd >= 0) {
		close(efi_dbglog_fd);
		efi_dbglog_fd = -1;
	}
	if (efi_errlog) {
		int ret = fclose(efi_errlog);
		efi_errlog = NULL;
		return ret;
	}

	errno = EBADF;
	return -1;
}
#endif

void PUBLIC
efi_error_clear(void)
{
	if (error_table) {
		for (unsigned int i = 0; i < current; i++) {
			error_table_entry *et = &error_table[i];

			clear_error_entry(et);
		}
		free(error_table);
	}
	error_table = NULL;
	current = 0;
}

void DESTRUCTOR
efi_error_fini(void)
{
	efi_error_clear();
	if (efi_dbglog) {
		fclose(efi_dbglog);
		efi_dbglog = NULL;
	}
}

static void CONSTRUCTOR
efi_error_init(void)
{
#ifndef ANDROID
	ssize_t bytes;
	cookie_io_functions_t io_funcs = {
		.write = dbglog_write,
		.seek = dbglog_seek,
		.close = dbglog_close,
	};

	efi_dbglog_fd = open("/dev/null", O_WRONLY|O_APPEND|O_CLOEXEC);
	if (efi_dbglog_fd < 0) {
		efi_dbglog_fd = memfd_create("efivar-debug.log", MFD_CLOEXEC);
		if (efi_dbglog_fd >= 0)
			efi_dbglog_memfd = true;
	}
	if (efi_dbglog_fd == -1)
		return;

	bytes = getrandom(&efi_dbglog_cookie, sizeof(efi_dbglog_cookie), 0);
	if (bytes < (ssize_t)sizeof(efi_dbglog_cookie))
		efi_dbglog_cookie = 0;

	efi_dbglog = fopencookie((void *)efi_dbglog_cookie, "a", io_funcs);
	if (efi_dbglog && efi_dbglog_memfd)
		setvbuf(efi_dbglog, efi_dbglog_buf, _IOLBF,
			sizeof(efi_dbglog_buf));
#endif
}

FILE PUBLIC *
efi_get_logfile(void)
{
	return efi_dbglog;
}

void PUBLIC
efi_set_verbose(int verbosity, FILE *errlog)
{
	efi_verbose = verbosity;
	if (errlog)
		efi_errlog = errlog;
}

int PUBLIC
efi_get_verbose(void)
{
	return efi_verbose;
{'fraction_non_alphanumeric': 0.13657302863336646, 'fraction_numerical': 0.012645650799385783, 'mean_word_length': 3.826503923278117, 'pattern_counts': {'":': 0, '<': 40, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7406671', 'n_tokens_mistral': 4943, 'n_tokens_neox': 4537, 'n_words': 1088}