Initial commit

Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
Robert Baldyga
2019-03-29 08:39:34 +01:00
commit 94e8ca09e0
140 changed files with 37144 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "ocf"]
path = ocf
url = git@github.com:Open-CAS/ocf.git

15
Makefile Normal file
View File

@@ -0,0 +1,15 @@
#
# Copyright(c) 2012-2019 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
default: all
DIRS:=modules casadm utils
.PHONY: default all clean distclean $(DIRS)
all $(MAKECMDGOALS): $(DIRS)
$(DIRS):
cd $@ && $(MAKE) $(MAKECMDGOALS)

167
casadm/Makefile Normal file
View File

@@ -0,0 +1,167 @@
#
# Copyright(c) 2012-2019 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
PWD:=$(shell pwd)
MODULESDIR:=$(PWD)/../modules
UTILS_DIR:=$(PWD)/../utils
BINARY_PATH = /sbin
VERSION_FILE := $(MODULESDIR)/CAS_VERSION
#
# Section below enables creating build with experimental features
#
ifeq ($(CAS_EXT_EXP),1)
DEFINES = WI_AVAILABLE
endif
#
# Add defines for version
#
-include $(VERSION_FILE)
DEFINES += CAS_VERSION_MAIN=$(CAS_VERSION_MAIN)
DEFINES += CAS_VERSION_MAJOR=$(CAS_VERSION_MAJOR)
DEFINES += CAS_VERSION_MINOR=$(CAS_VERSION_MINOR)
DEFINES += CAS_BUILD_NO=\"$(CAS_BUILD_NO)\"
#
# Additional git version
#
ifneq ($(strip $(CAS_BUILD_FLAG)),)
DEFINES += CAS_BUILD_FLAG=\"$(CAS_BUILD_FLAG)\"
endif
#
# Include directories
#
INCLUDES = .
INCLUDES += $(MODULESDIR)/include
OBJDIR = .obj/
TARGET = casadm
TARGETS = $(TARGET)
#
# Source to be complied
#
OBJS = cas_lib.o
OBJS += cas_main.o
OBJS += argp.o
OBJS += statistics_view_csv.o
OBJS += cas_lib_utils.o
OBJS += statistics_model.o
OBJS += table.o
OBJS += psort.o
OBJS += statistics_view_text.o
OBJS += intvector.o
OBJS += statistics_view.o
OBJS += statistics_view_raw_csv.o
OBJS += csvparse.o
OBJS += extended_err_msg.o
OBJS += upgrade.o
OBJS += safeclib/memmove_s.o
OBJS += safeclib/memcpy_s.o
OBJS += safeclib/memset_s.o
OBJS += safeclib/strncpy_s.o
OBJS += safeclib/strtok_s.o
OBJS += safeclib/safe_str_constraint.o
OBJS += safeclib/ignore_handler_s.o
OBJS += safeclib/safe_mem_constraint.o
OBJS += safeclib/mem_primitives_lib.o
OBJS += safeclib/strnlen_s.o
#
# Flags for C compilation
#
CFLAGS = $(patsubst %,-I%,$(INCLUDES))
CFLAGS += $(patsubst %,-D%,$(DEFINES))
ifdef DEBUG
CFLAGS += -O0 -g
else
CFLAGS += -O2 -D_FORTIFY_SOURCE=2
endif
CFLAGS += -Wall -z relro -z now -fstack-protector -fPIC -Wformat -Wformat-security -fno-strict-aliasing
#
# Flags for linking
#
LDFLAGS = -z noexecstack -z relro -z now -pie -pthread
#
# Targets
#
all: sync
$(MAKE) build
build: $(VERSION_FILE) $(TARGETS)
sync:
@cd $(MODULESDIR) && $(MAKE) sync
#
# Include dependencies file
#
$(TARGET): $(TARGET).a
@echo " LD " $@
@$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $<
$(TARGET).a: $(patsubst %,$(OBJDIR)%,$(OBJS))
@echo " AR " $@
@ar rcs $@ $^
@echo " AR " libcas.a
@cp -f $@ libcas.a
@ar d libcas.a $(OBJDIR)argp.o $(OBJDIR)cas_main.c
#
# Generic target for C file
#
$(OBJDIR)%.o: %.c
@echo " CC " $<
@mkdir -p $(dir $@)
ifeq ($(strip $(CAS_VERSION_MAIN)),)
$(error "No version file")
endif
@$(CC) -c $(CFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<"
$(VERSION_FILE):
@echo " VERSION " $@
@cd $(MODULESDIR) && ./CAS_VERSION_GEN
clean:
@echo " CLEAN "
@rm -f *.a $(TARGETS)
@rm -f $(shell find -name \*.d) $(shell find -name \*.o)
distclean: clean
@rm -f $(VERSION_FILE)
install:
@echo "Installing casadm"
@install -m 755 $(TARGET) $(BINARY_PATH)/$(TARGET)
@install -m 644 $(UTILS_DIR)/$(TARGET).8 /usr/share/man/man8/$(TARGET).8
@install -m 755 -d /etc/opencas
@install -m 644 $(UTILS_DIR)/opencas.conf /etc/opencas/opencas.conf
@install -m 444 $(UTILS_DIR)/ioclass-config.csv /etc/opencas/ioclass-config.csv
@install -m 444 $(UTILS_DIR)/ext3-config.csv /etc/opencas/ext3-config.csv
@install -m 444 $(UTILS_DIR)/ext4-config.csv /etc/opencas/ext4-config.csv
@install -m 644 $(UTILS_DIR)/opencas.conf.5 /usr/share/man/man5/opencas.conf.5
uninstall:
@echo "Uninstalling casadm"
@rm $(BINARY_PATH)/$(TARGET)
@rm /usr/share/man/man8/$(TARGET).8
@rm /etc/opencas/opencas.conf
@rm /etc/opencas/ioclass-config.csv
@rm /etc/opencas/ext3-config.csv
@rm /etc/opencas/ext4-config.csv
@rm -rf /etc/opencas
@rm /usr/share/man/man5/opencas.conf.5
.PHONY: clean distclean all sync build install uninstall

803
casadm/argp.c Normal file
View File

@@ -0,0 +1,803 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdbool.h>
#include <string.h>
#include "argp.h"
#include "cas_lib.h"
#include "cas_lib_utils.h"
#include <sys/timeb.h>
#include <sys/stat.h>
#define PADDING " "
#define MAX_OPT_HELP_LEN 30
extern cas_printf_t cas_printf;
static int is_su_requied(const cli_command* commands, int cmd)
{
return commands[cmd].flags & CLI_SU_REQUIRED;
}
static int is_command_hidden(const cli_command* commands, int cmd)
{
return commands[cmd].flags & CLI_COMMAND_HIDDEN;
}
static void print_short_usage(const app *app_values)
{
cas_printf(LOG_INFO, "Usage: %s %s\n", app_values->name, app_values->info);
}
static void print_info(const app *app_values)
{
cas_printf(LOG_INFO, "Try `%s --help | -H' for more information.\n", app_values->name);
}
char *get_short_name_string(const char short_name, char *buf)
{
if (short_name) {
snprintf(buf, 3, "-%c", short_name);
} else {
buf[0] = 0;
}
return buf;
}
char *command_name_with_slash(char *buf, size_t buf_size, char short_name, char *long_name) {
if (short_name) {
snprintf(buf, buf_size, "-%c/--%s", short_name, long_name);
} else {
snprintf(buf, buf_size, "--%s", long_name);
}
return buf;
}
char *command_name_in_brackets(char *buf, size_t buf_size, char short_name, char *long_name) {
if (short_name) {
snprintf(buf, buf_size, "--%s (-%c)", long_name, short_name);
} else {
snprintf(buf, buf_size, "--%s", long_name);
}
return buf;
}
void print_options_usage(cli_option* options, const char *separator,
int (*view)(cli_option* options, int flag), int flag)
{
int print_separator = 0;
int i;
if (NULL == options) {
return;
}
for (i = 0; options[i].long_name != NULL; ++i) {
if (0 == view(&options[i], flag)) {
continue;
}
if (print_separator) {
/* Separator */
cas_printf(LOG_INFO, "%s", separator);
}
print_separator = 1;
/* Long option name */
cas_printf(LOG_INFO, "--%s", options[i].long_name);
/* Parameter */
if (options[i].arg != NULL) {
cas_printf(LOG_INFO, " <%s>",
options[i].arg);
}
}
}
void print_command_header(const app *app_values, const cli_command *cmd)
{
cas_printf(LOG_INFO, "%s%s\n\n", PADDING,
cmd->long_desc != NULL ? cmd->long_desc : cmd->desc);
}
void print_list_options(cli_option* options, int flag,
int (*view)(cli_option* options, int flag))
{
char buffer[2048];
for (; options->long_name != NULL; options++) {
char *desc = options->desc;
char short_name[3];
if (0 == view(options, flag)) {
continue;
}
if ((options->flags & CLI_OPTION_RANGE_INT)
|| (options->flags & CLI_OPTION_DEFAULT_INT)) {
desc = buffer;
if ((options->flags & CLI_OPTION_RANGE_INT)
&& (options->flags
& CLI_OPTION_DEFAULT_INT)) {
snprintf(buffer, sizeof(buffer), options->desc,
options->min_value,
options->max_value,
options->default_value);
} else if (options->flags & CLI_OPTION_DEFAULT_INT) {
snprintf(buffer, sizeof(buffer), options->desc,
options->default_value);
} else if (options->flags & CLI_OPTION_RANGE_INT) {
snprintf(buffer, sizeof(buffer), options->desc,
options->min_value,
options->max_value);
}
}
get_short_name_string(options->short_name, short_name);
if (options->arg != NULL) {
char buf[MAX_OPT_HELP_LEN];
if (options->flags & CLI_OPTION_OPTIONAL_ARG) {
snprintf(buf, MAX_OPT_HELP_LEN, "--%s [<%s>]",
options->long_name, options->arg);
} else {
snprintf(buf, MAX_OPT_HELP_LEN, "--%s <%s>",
options->long_name, options->arg);
}
cas_printf(LOG_INFO, "%s%-4s%-32s%s\n", PADDING,
short_name, buf, desc);
} else {
cas_printf(LOG_INFO, "%s%-4s--%-30s%s\n", PADDING,
short_name, options->long_name, desc);
}
}
}
static void print_options_help(cli_option *options)
{
char buffer[2048];
int i;
for (i = 0; options[i].long_name != NULL; ++i) {
char *desc = options[i].desc;
char short_name[3];
if (options[i].flags & CLI_OPTION_HIDDEN) {
continue;
}
if ((options[i].flags & CLI_OPTION_RANGE_INT)
|| (options[i].flags & CLI_OPTION_DEFAULT_INT) ) {
desc = buffer;
if ((options[i].flags & CLI_OPTION_RANGE_INT)
&& (options[i].flags & CLI_OPTION_DEFAULT_INT) ) {
snprintf(buffer, sizeof(buffer), options[i].desc,
options[i].min_value,
options[i].max_value,
options[i].default_value);
} else if (options[i].flags & CLI_OPTION_DEFAULT_INT) {
snprintf(buffer, sizeof(buffer), options[i].desc,
options[i].default_value);
} else if (options[i].flags & CLI_OPTION_RANGE_INT) {
snprintf(buffer, sizeof(buffer), options[i].desc,
options[i].min_value,
options[i].max_value);
}
}
get_short_name_string(options[i].short_name, short_name);
if (options[i].arg != NULL) {
char buf[MAX_OPT_HELP_LEN];
if (options[i].flags & CLI_OPTION_OPTIONAL_ARG) {
snprintf(buf, MAX_OPT_HELP_LEN, "--%s [<%s>]",
options[i].long_name,
options[i].arg);
} else {
snprintf(buf, MAX_OPT_HELP_LEN, "--%s <%s>",
options[i].long_name,
options[i].arg);
}
cas_printf(LOG_INFO, "%s%-4s%-32s%s\n", PADDING,
short_name, buf, desc);
} else {
cas_printf(LOG_INFO, "%s%-4s--%-30s%s\n", PADDING,
short_name, options[i].long_name,
desc);
}
}
}
static void print_namespace_help(app *app_values, cli_command *cmd)
{
char command_name[MAX_STR_LEN];
char option_name[MAX_STR_LEN];
cli_namespace *ns = cmd->namespace;
int i;
cas_printf(LOG_INFO, "Usage: %s --%s --%s <NAME>\n\n", app_values->name,
cmd->name, ns->long_name);
print_command_header(app_values, cmd);
command_name_in_brackets(command_name, MAX_STR_LEN, cmd->short_name, cmd->name);
command_name_in_brackets(option_name, MAX_STR_LEN, ns->short_name, ns->long_name);
cas_printf(LOG_INFO, "Valid values of NAME are:\n");
for (i = 0; ns->entries[i].name; ++i)
cas_printf(LOG_INFO, "%s%s - %s\n", PADDING, ns->entries[i].name, ns->entries[i].desc);
cas_printf(LOG_INFO, "\n");
for (i = 0; ns->entries[i].name; ++i) {
cas_printf(LOG_INFO, "Options that are valid with %s %s %s are:\n",
command_name, option_name, ns->entries[i].name);
print_options_help(ns->entries[i].options);
if (ns->entries[i + 1].name)
cas_printf(LOG_INFO, "\n");
}
}
static void print_command_help(app *app_values, cli_command *cmd)
{
int all_mandatory = 1;
int all_hidden = 1;
int i;
if (cmd->help) {
(cmd->help)(app_values, cmd);
return;
}
if (cmd->namespace) {
print_namespace_help(app_values, cmd);
return;
}
cas_printf(LOG_INFO, "Usage: %s --%s", app_values->name, cmd->name);
if (cmd->options != NULL) {
for (i = 0; cmd->options[i].long_name != NULL; ++i) {
if (cmd->options[i].flags & CLI_OPTION_HIDDEN) {
continue;
}
all_hidden = 0;
if (cmd->options[i].flags & CLI_OPTION_REQUIRED) {
cas_printf(LOG_INFO, " --%s", cmd->options[i].long_name);
if (cmd->options[i].arg != NULL) {
if (cmd->options[i].flags & CLI_OPTION_OPTIONAL_ARG) {
cas_printf(LOG_INFO, " [<%s>]", cmd->options[i].arg);
} else {
cas_printf(LOG_INFO, " <%s>", cmd->options[i].arg);
}
}
} else {
all_mandatory = 0;
}
}
if (!all_mandatory) {
cas_printf(LOG_INFO, " [option...]");
}
}
cas_printf(LOG_INFO, "\n\n");
print_command_header(app_values, cmd);
if (cmd->options && !all_hidden) {
char option_name[MAX_STR_LEN];
command_name_in_brackets(option_name, MAX_STR_LEN, cmd->short_name, cmd->name);
cas_printf(LOG_INFO, "Options that are valid with %s are:\n", option_name);
print_options_help(cmd->options);
}
}
void print_help(const app *app_values, const cli_command *commands)
{
int i;
cas_printf(LOG_INFO, "%s\n\n", app_values->title);
print_short_usage(app_values);
cas_printf(LOG_INFO, "\nAvailable commands:\n");
for (i = 0;; ++i) {
char short_name[3];
if (commands[i].name == NULL) {
break;
}
if (is_command_hidden(commands, i))
continue;
get_short_name_string(commands[i].short_name, short_name);
cas_printf(LOG_INFO, "%s%-4s--%-25s%s\n", PADDING, short_name,
commands[i].name, commands[i].desc);
}
cas_printf(LOG_INFO, "\nFor detailed help on the above commands use --help after the command.\n"
"e.g.\n%s%s --%s --help\n",
PADDING, app_values->name, commands[0].name);
if (app_values->man != NULL) {
cas_printf(LOG_INFO,
"For more information, please refer to manual, Admin Guide (man %s)\n"
"or go to support page <http://www.intel.com/support/go/cas>.\n",
app_values->man);
} else {
cas_printf(LOG_INFO,
"For more information, please refer to manual, Admin Guide\n"
"or go to support page <http://www.intel.com/support/go/cas>.\n");
}
}
static int args_is_unrecognized(const char *cmd)
{
if (strempty(cmd)) {
return 1;
}
if ('-' == cmd[0]) {
char c = cmd[1];
/* Check if short option (command) is proper */
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
if ('\0' == cmd[2]) {
return 0;
} else {
return 1;
}
}
if ('-' == cmd[1]) {
char c = cmd[2];
/* Long option (command), check if it is valid */
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
return 0;
}
}
}
return 1;
}
static int args_is(const char *in, const char *arg, const char c)
{
if (strempty(in)) {
return 0;
}
if ('-' == in[0]) {
if (0 != c && c == in[1]) {
if ('\0' == in[2]) {
return 1;
}
}
if ('-' == in[1]) {
/* Long option */
if (0 == strncmp(&(in[2]), arg, MAX_STR_LEN)) {
return 1;
}
}
}
return 0;
}
static int is_help(const char* cmd)
{
return args_is(cmd, "help", 'H');
}
static int get_help_position(int argc, const char **argv)
{
int i;
for (i = 2; i < argc; i++) {
if (is_help(argv[i])) {
return i;
}
}
return -1;
}
static int get_option(const cli_option *options, const char* opt)
{
int i;
for (i = 0; options[i].long_name; ++i) {
if (args_is(opt, options[i].long_name, options[i].short_name)) {
return i;
}
}
return -1;
}
/**
* log command as it was entered in CLI
*/
void log_command(int argc, const char **argv, int result, long long int timespan)
{
const int def_cmd_buf_len = 100;
int cmd_buf_len = def_cmd_buf_len;
int cmd_len = 0;
int i = 0;
char *command = malloc(cmd_buf_len);
if (!command) {
cas_printf(LOG_ERR, "Memory allocation failed for logging.");
return;
}
for (i = 0 ; i != argc ; ++i) {
int tok_len = strnlen_s(argv[i], MAX_STR_LEN);
/* if reconstructed command width is longer than current
* cmd_buf_len (length of command buffer), than resize it
* to make it twice as big.
*/
if (tok_len + 1 + cmd_len > cmd_buf_len) {
cmd_buf_len = (tok_len + 1 + cmd_buf_len) * 2;
char *tmp = realloc(command, cmd_buf_len);
/* if reallocation failed, cancel logging */
if (!tmp) {
cas_printf(LOG_ERR,
"Memory allocation failed for logging.");
free(command);
return;
}
command = tmp;
}
/* append additional token to a string */
memcpy_s(command + cmd_len, cmd_buf_len - cmd_len,
argv[i], tok_len);
cmd_len += tok_len;
/* either a space or a null terminator */
command[cmd_len] = (i == argc - 1) ? 0 : ' ';
cmd_len++;
}
caslog(LOG_DEBUG, "Casadm invoked with: \"%s\". "
"Exit status is %d (%s). Command took %lld.%02lld s.",
command, result, result? "failure" : "success",
timespan / 1000, (timespan % 1000) / 10);
free(command);
}
/**
* run command. Additionally log its execution and report any errors if
* they've happened
*/
int run_command(cli_command *commands, int cmd, int argc, const char **argv)
{
int result;
const char *syslog_path = "/var/log/messages";
/* time buffer and stat buffer after running command */
struct timeb t0;
FILE *messages_f;
/* time buffer and stat buffer after running command */
struct timeb t1;
long long int timespan;
/* collect time */
ftime(&t0);
/* collect stat buffer for syslog */
messages_f = fopen(syslog_path, "r");
if (messages_f) {
fseek(messages_f, 0, SEEK_END);
/* if opening file failed, don't stop command execution.
* - just omit checking for /var/log/messages at the end
*/
} else {
/* ubuntu case*/
syslog_path = "/var/log/syslog";
messages_f = fopen(syslog_path, "r");
if (messages_f) {
fseek(messages_f, 0, SEEK_END);
}
}
/* execute CAS command */
result = commands[cmd].handle();
ftime(&t1);
timespan = (1000 * (t1.time - t0.time) + t1.millitm - t0.millitm);
if (commands[cmd].short_name != 'V') {
log_command(argc, argv, result, timespan);
}
/* print extra warning message IF command ended with failure and
* syslog contains something */
if (FAILURE == result && messages_f) {
char line_buf[MAX_STR_LEN];
bool kernel_said = false; /* set to true if CAS kernel module
* said anything during command
* execution */
while (fgets(line_buf, MAX_STR_LEN - 1, messages_f)) {
line_buf[MAX_STR_LEN - 1] = 0;
if (strstr(line_buf, "CAS") &&
strstr(line_buf, "kernel")) {
kernel_said = true;
}
}
if (kernel_said) {
fprintf(stderr, "Error occurred, "
"please see syslog (%s) for details. \n",
syslog_path);
}
}
if (messages_f) {
fclose(messages_f);
}
return result;
}
static int count_arg_params(const char **argv, int argc)
{
int i = 0;
for (i = 0; i < argc; i++) {
if ('-' == argv[i][0] && 0 != argv[i][1]) {
break;
}
}
return i;
}
void configure_cli_commands(cli_command *commands)
{
cli_command *cmd = commands;
int ret;
while(cmd->name) {
if (cmd->configure) {
ret = cmd->configure(cmd);
if (ret < 0) {
cmd->flags |= CLI_COMMAND_HIDDEN;
}
}
cmd++;
}
}
int args_parse(app *app_values, cli_command *commands, int argc, const char **argv)
{
int i, j, k, status = SUCCESS;
int args_count, args_offset;
const char **args_list = NULL;
const char* cmd_name = argv[1];
cli_ns_entry *entry = NULL;
cli_option *options;
int cmd, first_opt;
if (argc < 2) {
cas_printf(LOG_ERR, "No command given.\n");
print_info(app_values);
return FAILURE;
}
if (args_is_unrecognized(cmd_name)) {
cas_printf(LOG_ERR, "Unrecognized command %s\n", cmd_name);
print_info(app_values);
return FAILURE;
}
for (i = 0;; ++i) {
if (commands[i].name == NULL) {
if (is_help(cmd_name)) {
print_help(app_values, commands);
return SUCCESS;
} else {
cas_printf(LOG_ERR, "Unrecognized command %s\n",
cmd_name);
print_info(app_values);
}
return FAILURE;
} else if (args_is(cmd_name, commands[i].name, commands[i].short_name)) {
cmd = i;
break;
}
}
configure_cli_commands(commands);
if (argc >= 3 && get_help_position(argc, argv) != -1) {
if (!is_command_hidden(commands, i)) {
print_command_help(app_values, &commands[i]);
}
return SUCCESS;
}
if (is_su_requied(commands, cmd)) {
if (getuid() != 0) {
cas_printf(LOG_ERR, "Must be run as root.\n");
return FAILURE;
}
}
if (commands[cmd].options) {
options = commands[cmd].options;
first_opt = 2;
} else if (commands[cmd].namespace) {
if (argc < 3) {
cas_printf(LOG_ERR, "Missing namespace option.\n");
print_info(app_values);
return FAILURE;
}
if (argc < 4) {
cas_printf(LOG_ERR, "Missing namespace name.\n");
print_info(app_values);
return FAILURE;
}
if (!args_is(argv[2], commands[cmd].namespace->long_name,
commands[cmd].namespace->short_name)) {
cas_printf(LOG_ERR, "Unrecognized option.\n");
print_info(app_values);
return FAILURE;
}
entry = commands[cmd].namespace->entries;
while (true) {
if (!strcmp(argv[3], entry->name))
break;
if (!(++entry)->name) {
cas_printf(LOG_ERR, "Unrecognized namespace entry.\n");
print_info(app_values);
return FAILURE;
}
}
options = entry->options;
first_opt = 4;
} else {
return run_command(commands, cmd, argc, argv);
}
/* for each possible option:
* - if it is required, check if it is supplied exactly once
* - if it is not required, check if it is supplied at most once
*/
for (i = 0; options[i].long_name; ++i) {
char option_name[MAX_STR_LEN];
/* count occurrences of an option (k as counter) */
k = 0;
for (j = first_opt; j < argc; ++j) {
if (args_is(argv[j], options[i].long_name,
options[i].short_name)) {
k++;
}
}
command_name_with_slash(option_name, MAX_STR_LEN,
options[i].short_name, options[i].long_name);
if (options[i].flags & CLI_OPTION_REQUIRED) {
if (!k) {
cas_printf(LOG_ERR, "Missing required option %s\n", option_name);
print_info(app_values);
return FAILURE;
}
}
if (k > 1) {
cas_printf(LOG_ERR, "Option supplied more than once %s\n", option_name);
print_info(app_values);
return FAILURE;
}
}
/* Store parameters for arguments. Terminate each list with NULL element.
* Accomodate for max no of parameters */
args_list = malloc(sizeof(*args_list) * (argc + 1));
if (args_list == NULL) {
return FAILURE;
}
/* iterate over all arguments that were actually passed to the CLI */
args_count = args_offset = 0;
for (i = first_opt; i < argc; ++i) {
int opt;
if (args_is_unrecognized(argv[i])) {
cas_printf(LOG_ERR, "Invalid format %s\n",
argv[i]);
print_info(app_values);
status = FAILURE;
goto free_args;
}
opt = get_option(options, argv[i]);
if (opt == -1) {
cas_printf(LOG_ERR, "Unrecognized option %s\n",
argv[i]);
print_info(app_values);
status = FAILURE;
goto free_args;
}
if (options[opt].arg != NULL) {
/* Count params for current argument. */
args_count = count_arg_params(&argv[i + 1], argc - i - 1);
if (args_count == 0 && !(options[opt].flags & CLI_OPTION_OPTIONAL_ARG)) {
cas_printf(LOG_ERR, "Missing required argument in %s\n",
argv[i]);
print_info(app_values);
status = FAILURE;
goto free_args;
}
if (-1 != options[opt].args_count &&
args_count != options[opt].args_count &&
(0 != args_count || !(options[opt].flags & CLI_OPTION_OPTIONAL_ARG))) {
cas_printf(LOG_ERR, "Invalid number of arguments for %s\n",
argv[i]);
print_info(app_values);
status = FAILURE;
goto free_args;
}
/* Add params for current argument.
* Terminate list with NULL element.*/
for (k = args_offset, j = 0; j < args_count; j++) {
args_list[k++] = argv[j + i + 1];
}
args_list[args_offset + args_count] = NULL;
i += args_count;
}
if (commands[cmd].command_handle_opts) {
status = commands[cmd].command_handle_opts(
options[opt].long_name,
&args_list[args_offset]);
} else if (commands[cmd].namespace_handle_opts) {
status = commands[cmd].namespace_handle_opts(
entry->name,
options[opt].long_name,
&args_list[args_offset]);
} else {
cas_printf(LOG_ERR, "Internal error\n");
status = FAILURE;
goto free_args;
}
args_offset += args_count;
if (0 != status) {
cas_printf(LOG_ERR, "Error during options handling\n");
print_info(app_values);
status = FAILURE;
goto free_args;
}
}
status = run_command(commands, cmd, argc, argv);
free_args:
if (NULL != args_list) {
free(args_list);
args_list = NULL;
}
return status;
}

133
casadm/argp.h Normal file
View File

@@ -0,0 +1,133 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef _ARGP_H
#define _ARGP_H
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
enum CLI_OPTION_FLAGS {
CLI_OPTION_REQUIRED = 1 << 0,
CLI_OPTION_HIDDEN = 1 << 1,
CLI_OPTION_RANGE_INT = 1 << 2, /*! if option has a min/max value */
CLI_OPTION_DEFAULT_INT = 1 << 3, /*! if option has a default value */
CLI_OPTION_OPTIONAL_ARG = 1 << 4 /*! if option argument is optional */
};
enum CLI_COMMAND_FLAGS {
CLI_SU_REQUIRED = 1 << 0,
CLI_COMMAND_HIDDEN = 1 << 1
};
#define ERROR -1
#define SUCCESS 0
#define FAILURE 1
/**
* structure repsesenting each single option for CLI command (i.e. -i, -j for -R)
*/
typedef struct {
char short_name; /*!< short option name, one-letter. i.e. 'i' representing -i
*!< as --cache-id */
char* long_name; /*!< long option name (in above described case it would be
*!< "cache-id" */
char* desc; /*!< description of an option (longer text...
*!< may contain single %d for default value and/or pair of %d marks
*!< for range of correct values. If it has both, default must come
*!< after the range, so be careful about wording such messages) */
int args_count; /*!< number of arguments (0 - no arguments, -1 - unspecified) */
char* arg; /*!< type of an argument, descriptive. i.e. "NUM", "NAME" */
int flags; /*!< as per CLI_OPTION_FLAGS */
int min_value; /*!< min parameter value. (optional) */
int max_value; /*!< max parameter value. (optional) */
int default_value; /*!< default parameter value. (optional) */
int priv; /*!< Private filed for command handler */
} cli_option;
/*
* In namespace entries options array is nested in another flexible array
* (array of entries), so it cannot be flexible array itself. Because of that
* we make it static array of options with reasonable lenght.
*/
#define MAX_OPTIONS 32
typedef struct {
char* name; /*!< namespace entry name */
char* desc; /*!< description of an namespace entry */
cli_option options[MAX_OPTIONS];
/*!< pointer to first element in null-terminated array of cli_option */
} cli_ns_entry;
typedef struct {
char short_name; /*!< short name of namespace */
char* long_name; /*!< long name of namespace */
cli_ns_entry entries[]; /*!< null-terminated array of namespace entries */
} cli_namespace;
typedef struct {
const char* name;
char* info;
char* title;
char* doc;
char* man;
int block;
} app;
struct _cli_command;
typedef struct _cli_command cli_command;
/**
* structure representing each CLI command, i.e. -S, -T...
*/
struct _cli_command {
char* name; /*!< name of command (i.e. "start-cache" for --start-cache) */
char short_name; /*!< short name of command (i.e. "S" for -S/--start-cache) */
char* desc; /*!< description that appears with "casadm -H" invocation */
char* long_desc; /*!< option descripotion that appears with "casadm -O -H invocation */
cli_option* options; /*!< pointer to first element in null-terminated array of cli_option */
int (*command_handle_opts)(char*, const char**);
/*! function pointer to function that processes options to command */
cli_namespace* namespace;
/*! namespace description */
int (*namespace_handle_opts)(char*, char*, const char**);
/*! function pointer to function that processes options to namespace */
int (*handle)(void); /*! function pointer to function that executes a command */
int flags; /*! command flags, as per CLI_COMMAND_FLAGS */
void (*help)(app *app_values, cli_command *cmd);
/*! Custom help provider */
int (*configure)(cli_command *cmd);
/*! function pointer to function that configures command */
};
char *command_name_in_brackets(char *buf, size_t buf_size, char short_name, char *long_name);
void print_help(const app *app_values, const cli_command *commands);
void print_options_usage(cli_option* options, const char *separator,
int (*view)(cli_option* options, int flag), int flag);
void print_list_options(cli_option* options, int flag,
int (*view)(cli_option* options, int flag));
void print_command_header(const app *app_values, const cli_command *cmd);
void configure_cli_commands(cli_command *commands);
int args_parse(app *app_values, cli_command *commands, int argc, const char **argv);
#endif

2860
casadm/cas_lib.c Normal file

File diff suppressed because it is too large Load Diff

297
casadm/cas_lib.h Normal file
View File

@@ -0,0 +1,297 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __CAS_LIB_H__
#define __CAS_LIB_H__
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <syslog.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdarg.h>
#include "safeclib/safe_str_lib.h"
#include <cas_ioctl_codes.h>
#include <sys/utsname.h>
#define CTRL_DEV_PATH "/dev/cas_ctrl"
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#define FAILURE 1 /**< default non-zero exit code. */
#define INTERRUPTED 2 /**< if command is interrupted */
#define SUCCESS 0 /**< 0 exit code from majority of our functions
stands for success */
struct core_device {
int id;
int cache_id;
char path[MAX_STR_LEN];
struct kcas_core_info info;
};
struct cache_device {
int id;
int state;
int expected_core_count;
char device[MAX_STR_LEN];
int mode;
int eviction_policy;
int cleaning_policy;
int dirty;
int flushed;
unsigned size;
int core_count;
struct core_device cores[];
};
struct cas_param {
char *name;
char *unit;
char **value_names;
uint32_t (*transform_value)(uint32_t value);
uint32_t value;
bool select;
};
enum output_format_t {
OUTPUT_FORMAT_INVALID = 0,
OUTPUT_FORMAT_TABLE = 1,
OUTPUT_FORMAT_CSV = 2,
OUTPUT_FORMAT_DEFAULT = OUTPUT_FORMAT_TABLE
};
enum metadata_mode_t {
METADATA_MODE_INVALID = 0,
METADATA_MODE_NORMAL,
METADATA_MODE_ATOMIC,
METADATA_MODE_DEFAULT = METADATA_MODE_NORMAL,
};
#define STATS_FILTER_INVALID 0
#define STATS_FILTER_CONF (1 << 0)
#define STATS_FILTER_USAGE (1 << 1)
#define STATS_FILTER_REQ (1 << 2)
#define STATS_FILTER_BLK (1 << 3)
#define STATS_FILTER_ERR (1 << 4)
#define STATS_FILTER_IOCLASS (1 << 5)
#define STATS_FILTER_ALL (STATS_FILTER_CONF | \
STATS_FILTER_USAGE | \
STATS_FILTER_REQ | \
STATS_FILTER_BLK | \
STATS_FILTER_ERR)
#define STATS_FILTER_DEFAULT STATS_FILTER_ALL
#define STATS_FILTER_COUNTERS (STATS_FILTER_REQ | STATS_FILTER_BLK | STATS_FILTER_ERR)
const char *eviction_policy_to_name(uint8_t policy);
const char *cleaning_policy_to_name(uint8_t policy);
const char *cache_mode_to_name(uint8_t cache_mode);
const char *get_cache_state_name(int cache_state);
const char *get_core_state_name(int core_state);
const char *metadata_variant_to_name(uint8_t variant);
const char *metadata_mode_to_name(uint8_t metadata_mode);
const char *seq_cutoff_policy_to_name(uint8_t seq_cutoff_policy);
__attribute__((format(printf, 2, 3)))
typedef int (*cas_printf_t)(int log_level, const char *format, ...);
extern cas_printf_t cas_printf;
__attribute__((format(printf, 2, 3)))
int caslog(int log_level, const char *template, ...);
#define CAS_CLI_HELP_METADATA_VARIANTS \
CAS_METADATA_VARIANT_MAX"|" \
CAS_METADATA_VARIANT_MIX"|" \
CAS_METADATA_VARIANT_MIN
/* for CLI commands arguments */
#define YES 1
#define NO 0
#define UNDEFINED -1
void metadata_memory_footprint(uint64_t size, float *footprint, const char **units);
int start_cache(ocf_cache_id_t cache_id, unsigned int cache_init,
const char *cache_device, ocf_cache_mode_t cache_mode,
ocf_eviction_t eviction_policy_type,
ocf_cache_line_size_t line_size, int force);
int stop_cache(ocf_cache_id_t cache_id, int flush);
#ifdef WI_AVAILABLE
#define CAS_CLI_HELP_START_CACHE_MODES "wt|wb|wa|pt|wi"
#define CAS_CLI_HELP_SET_CACHE_MODES "wt|wb|wa|pt|wi"
#define CAS_CLI_HELP_SET_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through, Write-Invalidate"
#define CAS_CLI_HELP_START_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through, Write-Invalidate"
#else
#define CAS_CLI_HELP_START_CACHE_MODES "wt|wb|wa|pt"
#define CAS_CLI_HELP_SET_CACHE_MODES "wt|wb|wa|pt"
#define CAS_CLI_HELP_START_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through"
#define CAS_CLI_HELP_SET_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through"
#endif
/**
* @brief handle set cache param command
* @param cache_id id of cache device
* @param params parameter array
* @return exit code of successful completion is 0;
* nonzero exit code means failure
*/
int cache_params_set(unsigned int cache_id, struct cas_param *params);
/**
* @brief get cache param value
* @param cache_id id of cache device
* @param param_id id of cache parameter to retrive
* @param param variable to pass value to caller
* @return exit code of successful completion is 0;
* nonzero exit code means failure
*/
int cache_get_param(unsigned int cache_id, unsigned int param_id,
struct cas_param *param);
/**
* @brief handle get cache param command
* @param cache_id id of cache device
* @param params parameter array
* @return exit code of successful completion is 0;
* nonzero exit code means failure
*/
int cache_params_get(unsigned int cache_id, struct cas_param *params,
unsigned int output_format);
/**
* @brief handle set core param command
* @param cache_id id of cache device
* @param core_id id of core device
* @param params parameter array
* @return exit code of successful completion is 0;
* nonzero exit code means failure
*/
int core_params_set(unsigned int cache_id, unsigned int core_id,
struct cas_param *params);
/**
* @brief handle get core param command
* @param cache_id id of cache device
* @param core_id id of core device
* @param params parameter array
* @return exit code of successful completion is 0;
* nonzero exit code means failure
*/
int core_params_get(unsigned int cache_id, unsigned int core_id,
struct cas_param *params, unsigned int output_format);
/**
* @brief handle set cache mode (-Q) command
* @param in cache mode identifier of cache mode (WRITE_BACK, WRITE_THROUGH etc...)
* @param cache_id id of cache device
* @param flush whenever we should flush cache during execution of command. Options: YES, NO, UNDEFINED.
* (UNDEFINED is illegal when transitioning from Write-Back mode to any other mode)
*/
int set_cache_mode(unsigned int cache_state, unsigned int cache_id, int flush);
/**
* @brief add core device to a cache
*
* @param cache_id cache to which new core is being added
* @param core_device path to a core device that is being added
* @param iogroup_id id of iogroup (this parameter is not exposed in user CLI)
* @param try_add try add core to earlier loaded cache or add to core pool
* @param update_path try update path to core device
* @return 0 upon successful core addition, 1 upon failure
*/
int add_core(unsigned int cache_id, unsigned int core_id, const char *core_device, int try_add, int update_path);
int get_core_info(int fd, int cache_id, int core_id, struct kcas_core_info *info);
int remove_core(unsigned int cache_id, unsigned int core_id,
bool detach, bool force_no_flush);
int core_pool_remove(const char *core_device);
int get_core_pool_count(int fd);
int reset_counters(unsigned int cache_id, unsigned int core_id);
int flush_cache(unsigned int cache_id);
int flush_core(unsigned int cache_id, unsigned int core_id);
int get_cas_capabilites_quiet(struct kcas_capabilites *caps);
int get_cas_capabilites(struct kcas_capabilites *caps);
int nvme_format(const char *device_path, int metadata_mode, int force);
int check_cache_device(const char *device_path);
int partition_list(unsigned int cache_id, unsigned int output_format);
int partition_setup(unsigned int cache_id, const char *file);
int partition_is_name_valid(const char *name);
int cas_module_version(char *buff, int size);
int disk_module_version(char *buff, int size);
int list_caches(unsigned int list_format);
int cache_status(unsigned int cache_id, unsigned int core_id, int io_class_id,
unsigned int stats_filters, unsigned int stats_format);
int get_inactive_core_count(const struct kcas_cache_info *cache_info);
int open_ctrl_device_quiet();
int open_ctrl_device();
int *get_cache_ids(int *cache_count);
struct cache_device *get_cache_device_by_id_fd(int cache_id, int fd);
struct cache_device **get_cache_devices(int *caches_count);
void free_cache_devices_list(struct cache_device **caches, int caches_count);
int validate_dev(const char *dev_path);
int validate_str_num(const char *source_str, const char *msg, long long int min, long long int max);
int validate_str_num_sbd(const char *source_str, const char *msg, int min, int max);
int validate_str_unum(const char *source_str, const char *msg, unsigned int min,
unsigned int max);
int validate_path(const char *path, int exist);
int validate_str_cache_mode(const char *s);
int validate_str_ev_policy(const char *s);
int validate_str_cln_policy(const char *s);
int validate_str_meta_variant(const char *s);
int validate_str_stats_filters(const char* s);
int validate_str_output_format(const char* s);
int validate_str_metadata_mode(const char* s);
/**
* @brief calculate flush progress
*
* @param[in] dirty number of dirty blocks
* @param[in] flush number of flushed blocks
* @return flush progress or 0 if no flush is ongoing
*/
float calculate_flush_progress(unsigned dirty, unsigned flushed);
/**
* @brief calculate flush progress of given cache
*
* @param[in] cache_id cache to which calculation applies
* @param[out] progress flush progress
* @return 0 on success, nonzero on failure
*/
int get_flush_progress(int unsigned cache_id, float *progress);
/**
* @brief print error message corresponding with CAS extended error code.
*/
void print_err(int error_code);
/**
* @brief get special device file path (/dev/sdX) for disk.
*/
int get_dev_path(const char* disk, char* buf, size_t num);
/**
* @brief convert string to int
*/
bool str_to_int(const char* start, char** end, int *val);
#endif

535
casadm/cas_lib_utils.c Normal file
View File

@@ -0,0 +1,535 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <time.h>
#include <stdbool.h>
#include "cas_lib.h"
#include "extended_err_msg.h"
#include "cas_lib_utils.h"
#include "safeclib/safe_str_lib.h"
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <poll.h>
#include <execinfo.h>
extern cas_printf_t cas_printf;
#define IOCTL_RETRIES 3 /* this is how many times ioctl is called */
#define VT100_CLEARLINE "[K"
#define ESCAPE 0x1b
#define CARRIAGE_RETURN 0xd
#define INVALID_DIRTY_NO ((uint64_t) (-1))
/* file descriptors for pipe */
/* 1 is writing end, 0 is reading end of a pipe */
int fdspipe[2];
/* these must be global for handling signals */
volatile static int interrupted = 0; /*!< signal was caught, interrupt the
*!< management operation now! */
static int finished = 0; /*!< if management operation has finished
*!< (so that progressbar drawing thread
*!< can either display "100%" or exit quietly */
static int device_id = 0; /*!< id of caching device to which management
*!< operation that is underway, applies */
/**
* Default signal handling function exists so that SIGINT wan't interrupt management
* operations in unpredicted/disallowed way.
*/
void sig_handler_default(int x)
{
static int inter_counter = 0;
inter_counter++;
if (inter_counter>4) {
cas_printf(LOG_ERR,
"Can't interrupt CAS management process\n");
}
}
/**
* If management operation was interrupted due to user action (SIGINT)
*/
int was_ioctl_interrupted()
{
return interrupted;
}
void sig_handler_interrupt_flushing(int x)
{
struct kcas_interrupt_flushing cmd_info;
int fd = open(CTRL_DEV_PATH, 0);
close(fdspipe[1]);
interrupted = 1;
if (fd < 0) {
cas_printf(LOG_ERR, "Device " CTRL_DEV_PATH " not found\n");
return;
}
memset(&cmd_info, 0, sizeof(cmd_info));
cmd_info.cache_id = device_id;
int res =
run_ioctl(fd, KCAS_IOCTL_INTERRUPT_FLUSHING, &cmd_info);
close(fd);
if (!res) {
set_default_sig_handler();
}
}
/**
* print current backtrace
*/
void dump_stack()
{
const int sym_max = 512;
void *sym_buf[sym_max];
int nsym;
nsym = backtrace(sym_buf, sym_max);
backtrace_symbols_fd(sym_buf, nsym, 2);
}
/**
* Sad CAS :(
* dump stack to allow debugging.
*/
void segv_handler_default(int i)
{
cas_printf(LOG_ERR, "Segmentation fault\n");
dump_stack();
exit(EXIT_FAILURE);
}
/**
* register default signal handling function
*/
void set_default_sig_handler()
{
signal(SIGINT, sig_handler_default);
signal(SIGSEGV, segv_handler_default);
}
/**
* handle errors of cafe c library (wrong parameters passed)
*/
static void safe_lib_constraint_handler(const char *msg, void *ptr, errno_t error)
{
cas_printf(LOG_ERR, "Safe C lib error\n");
if (msg) {
cas_printf(LOG_ERR, "%s (%d)\n", msg, error);
}
dump_stack();
exit(EXIT_FAILURE);
}
/**
* Register constraint handler for safe_string_library
*/
void set_safe_lib_constraint_handler()
{
set_mem_constraint_handler_s(safe_lib_constraint_handler);
set_str_constraint_handler_s(safe_lib_constraint_handler);
}
int _open_ctrl_device(int quiet)
{
int fd;
fd = open(CTRL_DEV_PATH, 0);
if (fd < 0) {
if (!quiet) {
cas_printf(LOG_ERR, "Device " CTRL_DEV_PATH
" not found\n");
cas_printf(LOG_INFO, "Is the kernel module loaded?\n");
}
return -1;
}
return fd;
}
int open_ctrl_device_quiet()
{
return _open_ctrl_device(true);
}
/**
* calls open on control device; returns either error (-1) or a valid file descriptor
*/
int open_ctrl_device()
{
return _open_ctrl_device(false);
}
/**
* @brief print spinning wheel
*/
void print_progress_indicator(float prog, struct progress_status *ps)
{
static const char prog_indicator[] = { '|', '/', '-', '\\', '|', '/', '-', '\\'};
/*!< set of characters implementing "spinning wheel" progress indicator */
static int max_i = ARRAY_SIZE(prog_indicator);
static int i = 0;
printf("%c%s... [%c]%c" VT100_CLEARLINE,
CARRIAGE_RETURN, ps->friendly_name, prog_indicator[i], ESCAPE);
if (50 < prog) {
/* we're almost there. Ignore all signals at this stage */
set_default_sig_handler();
}
i = (i + 1) % max_i;
fflush(stdout);
}
/**
* @brief print progress bar once
* @param prog degree of progress (0-100)
* @param ps structure holding status between progressbar and caller
*/
void print_progress_bar(float prog, struct progress_status *ps)
{
/* constants affecting look of progressbar/activity indicator */
static const char progress_full = '='; /*!< represents progress_step of progress */
static const char progress_partial = '-';/*!< represents progress of more than 0
*!< but less than progress_step */
static const char progress_empty = ' '; /*!< progressbar didn't reach here */
static const char delimiter_left = '['; /*!< left delimiter of progress bar */
static const char delimiter_right = ']';/*!< right delimiter of progress bar */
static const int progress_step = 2; /*!< progress step - percentage of progress to
*!< be represented by one character. i.e. if
*!< progress stepis set to 2, entire
*!< progressbar is 50 chars wide+2 chars for
*!< delimiters */
int i, remaining_m;
time_t elapsed, remaining_s;
printf("%c%s... ", CARRIAGE_RETURN, ps->friendly_name);
/* carriage return and "name of op"*/
putchar(delimiter_left);
/* make sure, progressbar always moves forward and never backward */
if (prog < ps->progress_accumulated) {
prog = ps->progress_accumulated;
} else {
ps->progress_accumulated = prog;
}
/* print actual progress bar */
for (i = progress_step; i <= prog; i += progress_step){
putchar(progress_full);
}
if (((int)prog) % progress_step) {
putchar(progress_partial);
i += progress_step;
}
for (; i <= 100; i += progress_step){
putchar(progress_empty);
}
elapsed = time(NULL) - ps->time_started;
remaining_s = ((100 - prog) * elapsed) / (prog ?: 1);
remaining_m = remaining_s / 60;
remaining_s -= remaining_m * 60;
if (remaining_m) {
/* ESCAPE VT100_CLEARLINE is terminal control sequence to clear "rest
* of the line */
printf("%c %3.1f%% [%dm%02lds remaining]%c" VT100_CLEARLINE,
delimiter_right, prog, remaining_m, remaining_s, ESCAPE);
} else {
printf("%c %3.1f%% [%lds remaining]%c" VT100_CLEARLINE,
delimiter_right, prog, remaining_s, ESCAPE);
}
fflush(stdout);
}
/**
* @brief either print a progressbar or spinning wheel depending on prog
*/
void print_progress_bar_or_indicator(float prog, struct progress_status *ps)
{
if (0.01 > prog || 99.99 < prog) {
print_progress_indicator(prog, ps);
} else {
print_progress_bar(prog, ps);
}
}
/**
* initialize progressbar structure;
*/
void init_progress_bar(struct progress_status *ps)
{
if (NULL != ps) {
memset(ps, 0, sizeof(*ps));
ps->dirty_clines_curr = INVALID_DIRTY_NO;
ps->dirty_clines_initial = INVALID_DIRTY_NO;
ps->time_started = time(NULL);
}
}
void get_core_flush_progress(int fd, int cache_id, int core_id, float *prog)
{
struct kcas_core_info cmd_info;
memset(&cmd_info, 0, sizeof(cmd_info));
cmd_info.cache_id = cache_id;
cmd_info.core_id = core_id;
if (0 == ioctl(fd, KCAS_IOCTL_CORE_INFO, &cmd_info)) {
*prog = calculate_flush_progress(cmd_info.stats.dirty,
cmd_info.stats.flushed);
}
}
void get_cache_flush_progress(int fd, int cache_id, float *prog)
{
struct kcas_cache_info cmd_info;
memset(&cmd_info, 0, sizeof(cmd_info));
cmd_info.cache_id = cache_id;
if (0 == ioctl(fd, KCAS_IOCTL_CACHE_INFO, &cmd_info)) {
*prog = calculate_flush_progress(cmd_info.info.dirty,
cmd_info.info.flushed);
}
}
/**
* pthread thread handling function - runs during proper ioctl execution. Prints command progress
*/
void *print_command_progress(void *th_arg)
{
static const int
show_progressbar_after = 2; /*!< threshold in seconds */
int do_print_progress_bar = 0;
int mseconds = 0; /*< milliseconds */
int fd;
float prog = 0.;
struct progress_status *ps = th_arg;
/* argument of command progress of which is monitored */
/*1,2,0 are descriptors of stdout, err and in respectively*/
int running_tty = isatty(1) && isatty(2) && isatty(0);
struct sigaction new_action, old_action;
fd = open(CTRL_DEV_PATH, 0);
if (fd < 0) {
cas_printf(LOG_ERR, "Device " CTRL_DEV_PATH " not found\n");
return NULL; /* FAILURE; */
}
device_id = ps->cache_id;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
new_action.sa_handler = sig_handler_interrupt_flushing;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
sigaction(SIGINT, &new_action, NULL);
}
sched_yield();
while (1) {
struct pollfd pfd;
struct timespec ts;
sigset_t sigmask;
int ppoll_res;
sigemptyset(&sigmask);
ts.tv_sec = 1;
ts.tv_nsec = 0;
pfd.fd = fdspipe[0];
pfd.events = POLLIN | POLLRDHUP;
ppoll_res = ppoll(&pfd, 1, &ts, &sigmask);
if (ppoll_res < 0) {
if (ENOMEM == errno) {
sleep(1);
/* ppoll call failed due to insufficient memory */
} else if (EINTR == errno) {
interrupted = 1;
} else { /* other error conditions are EFAULT or EINVAL
* cannot happen in realistic conditions,
* and are likely to refer to OS errors, which
* cannot possibly be handled. Perform abortion.
*/
cas_printf(LOG_ERR, "Failed ppoll");
abort();
}
}
mseconds += 1000;
if (interrupted) {
/* if flushing is interrupted by signal, don't proceed with displaying
* any kind of progress bar. if bar was previously printed,
* print indicator instead */
if (do_print_progress_bar) {
print_progress_indicator(100, ps);
}
break;
} else if (finished) {
if (do_print_progress_bar) {
print_progress_bar_or_indicator(100., ps);
}
break;
}
if (ps->core_id == OCF_CORE_ID_INVALID) {
get_cache_flush_progress(fd, ps->cache_id, &prog);
} else {
get_core_flush_progress(fd, ps->cache_id, ps->core_id, &prog);
}
/* it is normal that ioctl to get statistics
* fails from time to time. Most common cases
* of it are:
* - during --start-cache when cache isn't added
* - during --stopping-cache, when progress is
* supposed to read "100%", but cache is actually
* already removed and its stopping progress can't
* be queried at all.
*/
if (mseconds >= show_progressbar_after * 1000
&& running_tty && prog < 50) {
do_print_progress_bar = 1;
}
if (do_print_progress_bar) {
print_progress_bar_or_indicator(prog, ps);
}
}
close(fdspipe[0]);
close(fd);
/* if progressbar was displayed at least one, clear line */
if (do_print_progress_bar) {
printf("%c%c" VT100_CLEARLINE, CARRIAGE_RETURN, ESCAPE);
}
fflush(stdout);
return NULL;
}
/*
* Run ioctl in a way that displays progressbar (if flushing operation takes longer)
* Catch SIGINT signal.
* @param friendly_name name of management operation that shall
* be displayed in command prompt
*/
int run_ioctl_interruptible(int fd, int command, void *cmd,
char *friendly_name, int cache_id, int core_id)
{
pthread_t thread;
int ioctl_res;
struct progress_status ps;
sigset_t sigset;
init_progress_bar(&ps);
ps.friendly_name = friendly_name;
ps.cache_id = cache_id;
ps.core_id = core_id;
if (pipe(fdspipe)) {
cas_printf(LOG_ERR,"Failed to allocate pipes.\n");
return -1;
}
interrupted = 0;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
pthread_create(&thread, 0, print_command_progress, &ps);
ioctl_res = run_ioctl(fd, command, cmd);
if (!interrupted) {
close(fdspipe[1]);
}
finished = 1;
pthread_join(thread, 0);
return ioctl_res;
}
/*
* @brief ioctl wrapper that retries ioctl attempts within one second timeouts
* @param[in] fd as for IOCTL(2)
* @param[in] command as for IOCTL(2)
* @param[inout] cmd_info as for IOCTL(2)
*/
int run_ioctl(int fd, int command, void *cmd)
{
int i, ret;
struct timespec timeout = {
.tv_sec = 1,
.tv_nsec = 0,
};
for (i = 0; i < IOCTL_RETRIES; i++) {
ret = ioctl(fd, command, cmd);
if (ret < 0) {
if (interrupted) {
return -EINTR;
} if (EINTR == errno) {
return -EINTR;
} else if (EBUSY == errno) {
int nret = nanosleep(&timeout, NULL);
if (nret) {
return -EINTR;
}
} else {
return ret;
}
} else {
break;
}
}
return ret;
}
int create_pipe_pair(FILE **intermediate_file)
{
/* 1 is writing end, 0 is reading end of a pipe */
int pipefd[2];
if (pipe(pipefd)) {
cas_printf(LOG_ERR,"Failed to create unidirectional pipe.\n");
return FAILURE;
}
intermediate_file[0] = fdopen(pipefd[0], "r");
if (!intermediate_file[0]) {
cas_printf(LOG_ERR,"Failed to open reading end of an unidirectional pipe.\n");
close(pipefd[0]);
close(pipefd[1]);
return FAILURE;
}
intermediate_file[1] = fdopen(pipefd[1], "w");
if (!intermediate_file[1]) {
cas_printf(LOG_ERR,"Failed to open reading end of an unidirectional pipe.\n");
fclose(intermediate_file[0]);
close(pipefd[1]);
return FAILURE;
}
return SUCCESS;
}

68
casadm/cas_lib_utils.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __CAS_LIB_UTILS_H__
#define __CAS_LIB_UTILS_H__
struct progress_status {
uint64_t
dirty_clines_initial; /*!< amount of dirty clines when command is initiated */
uint64_t
dirty_clines_curr; /*!< amount of dirty clines at current progress level */
int progress_accumulated; /*!< this is to ensure that progressbar is always
*!< from 0 to 100% and progress indicated by it
*!< never actually drops. */
int time_started; /*!< time when particular long running
*!< operation was started */
char *friendly_name; /*!< name of management operation that shall
*!< be displayed in command prompt */
int cache_id; /*!< cache id */
int core_id; /*!< core id */
};
void init_progress_bar(struct progress_status *ps);
void print_progress_bar_or_indicator(float prog, struct progress_status *ps);
int run_ioctl(int fd, int command, void *cmd);
int run_ioctl_interruptible(int fd, int command, void *cmd,
char *friendly_name, int cache_id, int core_id);
int open_ctrl_device();
int was_ioctl_interrupted();
void set_default_sig_handler();
void set_safe_lib_constraint_handler();
/**
* function creates pair files representing an unnamed pipe.
* this is highlevel counterpart to pipe syscall.
*
* null is returned upon failure;
*
* FILE *pipes[2] is returned upon success.
* 1 is writing end, 0 is reading end of a pipe
*/
int create_pipe_pair(FILE **);
/**
* Check if string is empty
*
* @param str - reference to the string
* @retval 1 string is empty
* @retval 0 string is not empty
*/
static inline int strempty(const char *str)
{
if (NULL == str) {
return 1;
} else if ('\0' == str[0]) {
return 1;
} else {
return 0;
}
}
#endif

2024
casadm/cas_main.c Normal file

File diff suppressed because it is too large Load Diff

483
casadm/csvparse.c Normal file
View File

@@ -0,0 +1,483 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include "csvparse.h"
#include "cas_lib_utils.h"
#include "safeclib/safe_lib.h"
#include <cas_ioctl_codes.h>
#define SUCCESS 0
#define FAILURE 1
struct CSVFILE_t {
FILE *f; /**< underlying byte stream*/
int num_columns; /**< number of columns in recently read
line of CSV file */
int alloc_column_ptrs; /**< number of pointers to columns
that can be fit in columns buffer */
char **columns; /**< buffer contains exactly one pointer to each
column of a csv file */
char *buffer; /**< buffer to which recently read line of a csv file
is stored */
int buffer_size; /**< size of a buffer */
char csv_comment; /**< character markng whole line comment. if set to null,
comments in file are not respected */
char csv_separator; /**< csv separator (by default coma, but in some csv formats
it is something different */
};
#define DEF_ALLOC_COL_PTRS 2
#define DEF_CSV_FILE_BUFFER_SIZE 20
/* return error when input dataset size exceeds some common sense limitations */
#define MAX_NUM_COLUMNS 100
#define MAX_LINE_LENGTH 8192
CSVFILE *csv_open(const char *path, const char *mode)
{
CSVFILE *csv;
if (!path || !mode) {
return NULL;
}
/* open underlying file as a character stream */
FILE *f = fopen(path, mode);
if (!f) {
return NULL;
}
csv = csv_fopen(f);
if (NULL == csv) {
fclose(f);
return NULL;
}
return csv;
}
CSVFILE *csv_fopen(FILE *f)
{
CSVFILE *cf = malloc(sizeof(*cf));
if (!cf) {
return NULL;
}
/* allocate storage for columns of CSV file */
cf->num_columns = 0;
cf->alloc_column_ptrs = DEF_ALLOC_COL_PTRS;
cf->columns = malloc(cf->alloc_column_ptrs * sizeof(char *));
if (!cf->columns) {
free(cf);
return NULL;
}
/* allocate storage for line of CSV file */
cf->buffer_size = DEF_CSV_FILE_BUFFER_SIZE;
cf->buffer = malloc(cf->buffer_size);
if (!cf->buffer) {
free(cf->columns);
free(cf);
return NULL;
}
/* assign underlying file as a character stream */
cf->f = f;
cf->csv_separator = ',';
cf->csv_comment = 0;
return cf;
}
void csv_close(CSVFILE *cf)
{
fclose(cf->f);
csv_close_nu(cf);
}
void csv_close_nu(CSVFILE *cf)
{
free(cf->columns);
free(cf->buffer);
memset(cf, 0, sizeof(*cf));
free(cf);
}
/**
* internal helper function for the library.
*/
static int ensure_items_array(CSVFILE *cf)
{
if (cf->num_columns > MAX_NUM_COLUMNS) {
return FAILURE;
} else if (cf->num_columns < cf->alloc_column_ptrs) {
return SUCCESS;
} else {
char **tmp;
cf->alloc_column_ptrs = cf->num_columns * 2;
tmp =
realloc(cf->columns,
cf->alloc_column_ptrs * sizeof(char *));
if (!tmp) {
return FAILURE;
} else {
cf->columns = tmp;
return SUCCESS;
}
}
}
/**
* Function checks if CSV file is a valid one.
*/
bool csv_is_valid(CSVFILE *cf)
{
if (!cf) {
return false;
} else if (!cf->f) {
return false;
} else if (!cf->columns) {
return false;
} else if (!cf->buffer) {
return false;
} else {
return true;
}
}
static int csv_read_line(CSVFILE *cf)
{
char *line;
char *c;
int i, len;
int already_read = 0;
/* fgets reads at most buffer_size-1 characters and always places NULL
* at the end. */
while (true) {
line = fgets(cf->buffer + already_read,
cf->buffer_size - already_read, cf->f);
if (!line) {
return FAILURE;
}
line = cf->buffer;
/* check that entire line was read; if failed, expand buffer and retry
* or (in case of eof) be happy with what we have */
c = line;
i = 0;
while (*c && *c != '\n') {
c++;
i++;
}
len = i;
if (len > MAX_LINE_LENGTH) {
return FAILURE;
}
/* buffer ends with 0 while it is not an EOF - sign that we have NOT read entire line
* - try to expand buffer*/
if (!*c && !feof(cf->f)) {
already_read = cf->buffer_size - 1;
cf->buffer_size *= 2;
char *tmp = realloc(cf->buffer, cf->buffer_size);
if (tmp) {
cf->buffer = tmp;
continue;
} else {
return FAILURE;
}
}
if (cf->buffer[i] == '\n') {
cf->buffer[i] = 0;
}
break;
}
return SUCCESS;
}
int csv_read(CSVFILE *cf)
{
int i, j, spaces_at_end;
bool parsing_token = false; /* if false, "cursor" is over whitespace, otherwise
* it is over part of token */
bool quotation = false;
if (!csv_is_valid(cf)) {
return FAILURE;
}
if (csv_read_line(cf)) {
return FAILURE;
}
i = 0;
cf->num_columns = 0;
cf->columns[0] = 0;
spaces_at_end = 0;
while (cf->buffer[i]) {
if (quotation) { /* handling text within quotation marks -
* ignore commas in this kind of text and don't strip spaces */
if (cf->buffer[i] == '"' && cf->buffer[i + 1] == '"') {
/* double quotation mark is considered escaped quotation by
* Micros~1 Excel. We should do likewise */
if (!parsing_token) { /* start of an cf->buffer */
cf->columns[cf->num_columns] =
&cf->buffer[i];
parsing_token = true;
}
++i;
memmove_s(cf->columns[cf->num_columns] + 1,
cf->buffer_size - (cf->columns[cf->num_columns] - cf->buffer),
cf->columns[cf->num_columns],
&cf->buffer[i] - cf->columns[cf->num_columns]);
cf->columns[cf->num_columns]++;
} else if (cf->buffer[i] == '"') {
quotation = false;
parsing_token = false;
cf->buffer[i] = 0;
} else if (!parsing_token) { /* start of an cf->buffer */
cf->columns[cf->num_columns] = &cf->buffer[i];
parsing_token = true;
}
} else { /* handling text outside quotation mark */
if (cf->buffer[i] == cf->csv_separator) {
(cf->num_columns)++;
if (ensure_items_array(cf)) {
return FAILURE;
}
cf->columns[cf->num_columns] = 0;
parsing_token = false;
cf->buffer[i] = 0;
for (j = i - spaces_at_end; j != i; ++j) {
cf->buffer[j] = 0;
}
} else if (cf->buffer[i] == '"') {
quotation = true;
spaces_at_end = 0;
} else if (cf->csv_comment
&& cf->buffer[i] == cf->csv_comment) {
cf->buffer[i] = 0;
break;
} else if (!isspace(cf->buffer[i])) {
if (!parsing_token) { /* start of an cf->buffer */
if (!cf->columns[cf->num_columns]) {
cf->columns[cf->num_columns] =
&cf->buffer[i];
}
parsing_token = true;
}
spaces_at_end = 0;
} else { /* no token.; clear spaces, possibly */
parsing_token = false;
spaces_at_end++;
}
}
++i;
}
for (j = i - spaces_at_end; j != i; ++j) {
cf->buffer[j] = 0;
}
/*always consider empty line to have exactly one empty column */
cf->num_columns++;
for (j = 0; j != cf->num_columns; ++j) {
/* if no columns were detected during parse, make sure that columns[x]
* points to an empty string and not into (NULL) */
if (!cf->columns[j]) { /* so that empty columns will return empty string and
not a null-pointer */
cf->columns[j] = &cf->buffer[i];
}
}
return SUCCESS;
}
unsigned int csv_count_cols(CSVFILE *line)
{
return line->num_columns;
}
int csv_empty_line(CSVFILE *cf)
{
if (!csv_is_valid(cf)) {
return FAILURE;
}
if (0 == csv_count_cols(cf)) {
return 1;
} else if (1 == csv_count_cols(cf)) {
const char *value = csv_get_col(cf, 0);
if (strempty(value)) {
return 1;
}
}
return 0;
}
char *csv_get_col(CSVFILE *cf, int coln)
{
if (!csv_is_valid(cf)) {
return NULL;
}
return cf->columns[coln];
}
char **csv_get_col_ptr(CSVFILE *cf)
{
return cf->columns;
}
void csv_seek_beg(CSVFILE *cf)
{
fseek(cf->f, 0, SEEK_SET);
}
int csv_feof(CSVFILE *cf)
{
return feof(cf->f);
}
int csv_print(const char *path)
{
int i, j, k; /* column, line, row, within column */
int num_col_lengths = DEF_ALLOC_COL_PTRS;
static const int def_col_len = 5;
int actual_num_cols = 1;
CSVFILE *cf = csv_open(path, "r");
if (!cf) {
return FAILURE;
}
int *col_lengths = malloc(num_col_lengths * sizeof(int));
if (!col_lengths) {
csv_close(cf);
return FAILURE;
}
for (i = 0; i != num_col_lengths; ++i) {
col_lengths[i] = def_col_len;
}
/*calculate length of each column */
i = j = 0;
while (!csv_read(cf)) {
int num_cols = csv_count_cols(cf);
if (num_cols > actual_num_cols) {
actual_num_cols = num_cols;
}
if (num_cols > num_col_lengths) {
/* CSV file happens to have more columns, than we have allocated
* memory for */
int *tmp =
realloc(col_lengths, num_cols * 2 * sizeof(int));
if (!tmp) {
free(col_lengths);
csv_close(cf);
return FAILURE;
}
/* reallocation successful */
col_lengths = tmp;
for (i = num_col_lengths; i != num_cols * 2; ++i) {
col_lengths[i] = def_col_len;
}
num_col_lengths = num_cols * 2;
}
for (i = 0; i != csv_count_cols(cf); ++i) {
int len = strnlen(csv_get_col(cf, i), MAX_STR_LEN);
if (col_lengths[i] < len) {
col_lengths[i] = len;
}
}
++j;
}
/*actually format pretty table */
csv_seek_beg(cf);
printf(" | ");
for (i = 0; i != actual_num_cols; ++i) {
int before = col_lengths[i] / 2;
for (k = 0; k != before; ++k) {
putchar(' ');
}
putchar(i + 'A');
for (k = 0; k != col_lengths[i] - before - 1; ++k) {
putchar(' ');
}
printf(" | ");
}
printf("\n-----|-");
for (i = 0; i != actual_num_cols; ++i) {
for (k = 0; k != col_lengths[i]; ++k) {
putchar('-');
}
printf("-|-");
}
printf("\n");
j = 1;
while (!csv_read(cf)) {
printf("%4d | ", j);
int num_cols = csv_count_cols(cf);
for (i = 0; i != actual_num_cols; ++i) {
if (i < num_cols) {
char *c = csv_get_col(cf, i);
for (k = 0; c[k]; k++) {
putchar(c[k]);
}
} else {
k = 0;
}
for (; k != col_lengths[i]; ++k) {
putchar(' ');
}
printf(" | ");
}
++j;
putchar('\n');
}
free(col_lengths);
csv_close(cf);
return SUCCESS;
}
#ifdef __CSV_SAMPLE__
/**
* usage example for csvparse library
* gcc -ggdb csvparse.c -I../common -D__CSV_SAMPLE__ -ocsvsample
*/
int main()
{
puts("Validated configurations to run Intel CAS");
csv_print("../../tools/build_installer/utils/validated_configurations.csv");
putchar('\n');
puts("IO Classes for Intel CAS");
csv_print("../../tools/build_installer/utils/default_ioclasses.csv");
putchar('\n');
}
#endif

103
casadm/csvparse.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __CSVPARSE_H_
#define __CSVPARSE_H_
#include <stdio.h>
/**
* @file
* @brief Generic CSV input/output library
*
*/
/**
* data structure holding info about CSV file being read.
* @note there is no need to directly manipulate any field of this structure.
* Csvparse library handles everything.
*/
struct CSVFILE_t;
/**
* This is to mimic semantics of stdio FILE*, which also is a typedef for a structure.
*/
typedef struct CSVFILE_t CSVFILE;
CSVFILE *csv_open(const char *path, const char *mode);
CSVFILE *csv_fopen(FILE *f);
CSVFILE *csv_fdopen(int fd);
/**
* close csv file. this is a direct counterpart to csv_open
*/
void csv_close(CSVFILE *cf);
/**
* close a csv without closing underlying plain fle object (so that all
* structures allocated by csv parsere are freed but syscall close(2) isn't issued
* - this is designed as counterpart to csv_fopen or csv_fdopen
*/
void csv_close_nu(CSVFILE *cf);
/**
* @param cf csv file handle to read
*
* Read line from CSV file; return 0 if line was successfully read
* return nonzero if eof or error was observed
* Error may mean end of file or i.e. memory allocation error for temporary buffers
*/
int csv_read(CSVFILE *cf);
/**
* @return true if end of file occured.
*/
int csv_feof(CSVFILE *cf);
/**
* return number of columns
* @return # of columns in a csv file
*/
unsigned int csv_count_cols(CSVFILE *line);
/**
* return given column of recently read row
* @param coln - column number
* @return pointer to field of csv file as a string; no range checking is performed,
* so if coln given exceeds actual number of columns defined in this row, error will occur
*/
char* csv_get_col(CSVFILE *cf, int coln);
/**
* return entire row as a set of pointers to individual columns (unchecked function
* returns internal representation. state is guaranteed to be correct only when
* csv_read returned success;
*/
char** csv_get_col_ptr(CSVFILE *cf);
/**
* Check if current line is empty
*
* @param cf - CVS file instance
* @retval 1 - empty line
* @retval 0 - no empty line
*/
int csv_empty_line(CSVFILE *cf);
/**
* Seek to the begining of CSV file; this allows reading file again, from the begining
*/
void csv_seek_beg(CSVFILE *cf);
/**
* This function prints CVS file in human readable format to the STD output
*
* @param path - Path to the CVS file
* @return Operation status. 0 - Success, otherwise error during printing
*/
int csv_print(const char *path);
#endif

244
casadm/extended_err_msg.c Normal file
View File

@@ -0,0 +1,244 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include "safeclib/safe_lib.h"
#include <cas_ioctl_codes.h>
#include "extended_err_msg.h"
struct {
int cas_error;
const char *msg;
} static cas_error_code_map[] = {
/* IOC error mappings*/
{
OCF_ERR_INVAL,
"Invalid input parameter"
},
{
OCF_ERR_INVAL_VOLUME_TYPE,
"Invalid volume type"
},
{
OCF_ERR_INTR,
"Interrupted by a signal"
},
{
OCF_ERR_UNKNOWN,
"Unknown error occurred"
},
{
OCF_ERR_TOO_MANY_CACHES,
"Too many caches"
},
{
OCF_ERR_NO_MEM,
"Not enough memory to allocate a new cache device"
},
{
OCF_ERR_NO_FREE_RAM,
"Not enough free RAM for cache metadata to start cache"
},
{
OCF_ERR_START_CACHE_FAIL,
"Failed to insert cache"
},
{
OCF_ERR_CACHE_IN_USE,
"At least one cas device is still in use"
},
{
OCF_ERR_CACHE_NOT_EXIST,
"Cache ID does not exist"
},
{
OCF_ERR_CACHE_EXIST,
"Cache ID already exists"
},
{
OCF_ERR_TOO_MANY_CORES,
"Too many core devices in cache"
},
{
OCF_ERR_CORE_NOT_AVAIL,
"Core device not available"
},
{
OCF_ERR_CACHE_NOT_AVAIL,
"Cache device not available"
},
{
OCF_ERR_IO_CLASS_NOT_EXIST,
"No such IO class ID in the cache"
},
{
OCF_ERR_WRITE_CACHE,
"Error while writing to cache device"
},
{
OCF_ERR_WRITE_CORE,
"Error while writing to core device"
},
{
OCF_ERR_DIRTY_SHUTDOWN,
"Please use --load option to restore previous cache state "
"(Warning: data corruption may happen)\nOr initialize your "
"cache using --force option. Warning: All dirty data will be "
"lost!\n"
},
{
OCF_ERR_DIRTY_EXISTS,
"Cache closed with dirty data.\nPlease start cache using "
"--load or --force option.\n"
},
{
OCF_ERR_FLUSHING_INTERRUPTED,
"Flushing of core interrupted"
},
{
OCF_ERR_CANNOT_ADD_CORE_TO_POOL,
"Error occurred during adding core device to core pool"
},
{
OCF_ERR_CACHE_IN_INCOMPLETE_STATE,
"Cache is in incomplete state - at least one core is inactive"
},
{
OCF_ERR_CORE_IN_INACTIVE_STATE,
"Core device is in inactive state"
},
{
OCF_ERR_NOT_OPEN_EXC,
"Cannot open device exclusively"
},
/* CAS kernel error mappings*/
{
KCAS_ERR_ROOT,
"Must be root"
},
{
KCAS_ERR_SYSTEM,
"System Error"
},
{
KCAS_ERR_BAD_RANGE,
"Range parameters are invalid"
},
{
KCAS_ERR_DEV_SPACE,
"Illegal range, out of device space"
},
{
KCAS_ERR_INV_IOCTL,
"Invalid ioctl"
},
{
KCAS_ERR_DEV_PENDING,
"Device opens or mount are pending to this cache"
},
{
KCAS_ERR_DIRTY_EXISTS_NVME,
"Cache device contains dirty data.\nIf you want to format it, "
"please use --force option.\nWarning: all data will be lost!"
},
{
KCAS_ERR_FILE_EXISTS,
"Could not create exported object because file in /dev "
"directory exists"
},
{
KCAS_ERR_IN_UPGRADE,
"Operation not allowed. CAS is in upgrade state"
},
{
KCAS_ERR_UNALIGNED,
"Cache device logical sector size is greater than core device "
"logical sector size.\nConsider changing logical sector size "
"on current cache device \nor try other device with the same "
"logical sector size as core device."
},
{
KCAS_ERR_NO_STORED_CONF,
"Internal kernel module error" },
{
KCAS_ERR_ROLLBACK,
"Cannot restore previous configuration"
},
{
KCAS_ERR_NOT_NVME,
"Given block device is not NVMe"
},
{
KCAS_ERR_FORMAT_FAILED,
"Failed to format NVMe device"
},
{
KCAS_ERR_NVME_BAD_FORMAT,
"NVMe is formatted to unsupported format"
},
{
KCAS_ERR_CONTAINS_PART,
"Device contains partitions.\nIf you want to continue, "
"please use --force option.\nWarning: all data will be lost!"
},
{
KCAS_ERR_A_PART,
"Formatting of partition is unsupported."
},
{
KCAS_ERR_REMOVED_DIRTY,
"Flush error occured. Core has been set to detached state.\n"
"Warning: Core device may contain inconsistent data.\n"
"To access your data please add core back to the cache."
},
{
KCAS_ERR_STOPPED_DIRTY,
"Cache has been stopped with flushing error.\n"
"Warning: Core devices may contain inconsistent data.\n"
"To access your data, please start cache with --load option."
},
{
KCAS_ERR_NO_CACHE_ATTACHED,
"Operation not allowed. Caching device is not attached."
},
{
KCAS_ERR_CORE_POOL_NOT_EMPTY,
"Operation not allowed. Core pool is not empty."
},
{
KCAS_ERR_CLS_RULE_UNKNOWN_CONDITION,
"Unexpected classification rule condition"
},
{
KCAS_ERR_CLS_RULE_INVALID_SYNTAX,
"Invalid classification rule syntax"
},
};
const char *cas_strerr(int cas_error_code)
{
int i;
int count = sizeof(cas_error_code_map) / sizeof(cas_error_code_map[0]);
if (cas_error_code == 0)
return NULL; /* No Error */
cas_error_code = abs(cas_error_code);
for (i = 0; i < count; i++) {
if (cas_error_code_map[i].cas_error == cas_error_code)
return cas_error_code_map[i].msg;
}
return strerror(cas_error_code);
}

View File

@@ -0,0 +1,6 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
const char *cas_strerr(int cas_error_code);

110
casadm/intvector.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdlib.h>
#include <string.h>
#include "intvector.h"
#define DEFAULT_CAPACITY 11
struct intvector *vector_alloc()
{
struct intvector *v = malloc(sizeof(struct intvector));
if (!v) {
return 0;
}
if (vector_alloc_placement(v)) {
free(v);
return 0;
}
return v;
}
int vector_alloc_placement(struct intvector *v)
{
v->content = malloc(sizeof(int) * DEFAULT_CAPACITY);
if (!v->content) {
return 1;
}
v->size = 0;
v->capacity = DEFAULT_CAPACITY;
return 0;
}
int vector_reserve(struct intvector *v, int s)
{
if (s < DEFAULT_CAPACITY || s < v->capacity) {
return 0;
}
void *tmp = realloc(v->content, s*sizeof(int));
if (!tmp) {
return 1;
}
v->content = tmp;
v->capacity = s;
return 0;
}
void vector_free_placement(struct intvector *v)
{
free(v->content);
}
void vector_free(struct intvector *v)
{
vector_free_placement(v);
free(v);
}
int vector_get(struct intvector *v, int i)
{
return v->content[i];
}
int vector_set(struct intvector *v, int i, int x)
{
v->content[i]=x;
return 0;
}
int vector_zero(struct intvector *v)
{
memset(v->content, 0, sizeof(int) * v->size);
return 0;
}
int vector_push_back(struct intvector *v, int x)
{
if (vector_capacity(v) == vector_size(v)) {
if (vector_reserve(v, v->size*2)) {
return 1;
}
}
vector_set(v, v->size, x);
v->size++;
return 0;
}
int vector_size(struct intvector *v)
{
return v->size;
}
int vector_capacity(struct intvector *v)
{
return v->capacity;
}
int vector_resize(struct intvector *v, int s)
{
if (vector_reserve(v, s)) {
return 1;
}
v->size = s;
return 0;
}

40
casadm/intvector.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __INTVECTOR_H
#define __INTVECTOR_H
struct intvector
{
int capacity;
int size;
int *content;
};
/* names of these functions (mostly) correspond to std::vector */
struct intvector *vector_alloc();
int vector_alloc_placement(struct intvector *v);
int vector_reserve(struct intvector *v, int s);
void vector_free(struct intvector *v);
void vector_free_placement(struct intvector *v);
int vector_get(struct intvector *v, int i);
int vector_set(struct intvector *v, int i, int x);
int vector_zero(struct intvector *v);
int vector_push_back(struct intvector *v, int x);
int vector_size(struct intvector *v);
int vector_capacity(struct intvector *v);
int vector_resize(struct intvector *v, int s);
#endif

27
casadm/ocf_env.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef OCF_ENV_H_
#define OCF_ENV_H_
#include <string.h>
#include <stdlib.h>
#include "safeclib/safe_lib.h"
#define min(x, y) ({ x < y ? x : y; })
#define ENV_BUG_ON(cond) ({ if (cond) exit(1); })
/* *** STRING OPERATIONS *** */
#define env_memset memset_s
#define env_memcpy memcpy_s
#define env_memcmp memcmp_s
#define env_strnlen strnlen_s
#define env_strncmp strncmp
#define env_strncpy strncpy_s
#endif /* OCF_ENV_H_ */

22
casadm/ocf_env_headers.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __OCF_ENV_HEADERS_H__
#define __OCF_ENV_HEADERS_H__
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/* TODO: Move prefix printing to context logger. */
#define OCF_LOGO "CAS"
#define OCF_PREFIX_SHORT "[" OCF_LOGO "] "
#define OCF_PREFIX_LONG "Cache Acceleration Software Linux"
#define OCF_VERSION_MAIN CAS_VERSION_MAIN
#define OCF_VERSION_MAJOR CAS_VERSION_MAJOR
#define OCF_VERSION_MINOR CAS_VERSION_MINOR
#endif /* __OCF_ENV_HEADERS_H__ */

198
casadm/psort.c Normal file
View File

@@ -0,0 +1,198 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/timeb.h>
#include <pthread.h>
#include <string.h>
#include <stdbool.h>
#include "safeclib/safe_mem_lib.h"
#include "psort.h"
/**
* internal state of parallel sort algorithm (entire task for main thread,
* subtask for children).
*/
struct psort_state
{
void *defbase; /*!< base of master buffer sort
* - needed to compute offsets*/
int spawn_threads;
void *base; /*!< base of subtask */
size_t nmemb; /*!< number of members in subtask */
size_t size; /*!< of each member */
compar_t compar;
int result; /*!< partial result */
void *tmpbuf; /*!< temporary buffer for purpose of merge algorithm */
};
void memcpy_replacement(void *dst, void *src, size_t size)
{
/**
* Avoid this if possible. memcpy_s leads to crappy performance
* defeating purpose of entire optimized sort.
*/
memcpy_s(dst, size, src, size);
}
/**
* merge algorithm has O(N) spatial complexity and O(N) time complexity
*/
void merge_ranges(void *base, size_t nmemb1, size_t nmemb2, size_t size,
compar_t compar, void *tmpbuf)
{
void *target_buf = tmpbuf;
int i1, i2;
for (i1 = i2 = 0; i1 < nmemb1 || i2 < nmemb2;) {
bool lil; /* lil means "left is less" */
if (i1 == nmemb1) {
lil = false;
} else if (i2 ==nmemb2) {
lil = true;
} else if (compar(base + i1 * size,
base + (nmemb1 +i2) * size) < 0) {
lil = true;
} else {
lil = false;
}
if (lil) {
memcpy_replacement(target_buf + (i1 + i2) * size,
base + i1 * size,
size);
i1++;
} else {
memcpy_replacement(target_buf + (i1 + i2) * size,
base + (nmemb1 + i2) * size,
size);
i2++;
}
}
memcpy_replacement(base, target_buf, (nmemb1 + nmemb2) * size);
}
/**
* Execute quicksort on part or entirety of subrange. If subranges taken into
* account, than merge partial sort results.
*
* Complexity | time | spatial
* --------------------+-------------------+-----------
* Quick Sort | O(n*lg(n)/ncpu) | O(1)
* Merging | O(n) | O(N)
* --------------------+-------------------+-----------
* Entire algorithm | O(n+n*lg(n)/ncpu) | O(N)
*
* Effectively for suficiently large number of CPUs, sorting time
* becomes linear to dataset:
* \lim{ncpu \rightarrow \infty} O(n+\frac{n*lg(n)}{ncpu}) = O(n + 0^+) = O(n)
* Less can't be achieved, as last merge can't be parallelized.
*/
void *psort_thread_fun(void *arg_v)
{
pthread_t thread;
struct psort_state* arg = arg_v;
struct psort_state base_state;
struct psort_state child_state;
memcpy_replacement(&base_state, arg, sizeof(base_state));
if (arg->spawn_threads > 1) {
/* local state (assume, input state is unmodifiable) */
memcpy_replacement(&child_state, arg, sizeof(base_state));
base_state.spawn_threads /= 2;
child_state.spawn_threads = arg->spawn_threads
- base_state.spawn_threads;
base_state.nmemb /= 2;
child_state.nmemb = arg->nmemb - base_state.nmemb;
child_state.base += base_state.size *
base_state.nmemb;
/* spawn child */
if (pthread_create(&thread, 0, psort_thread_fun, &child_state)) {
/* failed to create thread */
arg->result = -errno;
return arg_v;
}
}
if (1 == base_state.spawn_threads) {
qsort(base_state.base, base_state.nmemb,
base_state.size, base_state.compar);
} else {
psort_thread_fun(&base_state);
if (base_state.result) {
arg->result = base_state.result;
}
}
if (arg->spawn_threads > 1) {
if (pthread_join(thread, 0)) {
arg->result = -errno;
return arg_v;
}
if (child_state.result) {
arg->result = child_state.result;
return arg_v;
}
if (!arg->result) {
merge_ranges(arg->base, base_state.nmemb,
child_state.nmemb, arg->size,
arg->compar,
arg->tmpbuf + (base_state.base
- base_state.defbase));
}
}
return arg_v;
}
/**
* actual parallel sorting entry point
*/
int psort_main(void *base, size_t nmemb, size_t size,
compar_t compar, int ncpu)
{
struct psort_state base_state;
/* use half the number of logical CPUs for purpose of sorting */
base_state.spawn_threads = ncpu;
/* current num of CPUs */
base_state.defbase = base;
base_state.base = base;
base_state.nmemb = nmemb;
base_state.size = size;
base_state.compar = compar;
base_state.tmpbuf = malloc(size * nmemb);
base_state.result = 0;
if (!base_state.tmpbuf) {
return -1;
}
psort_thread_fun(&base_state);
free(base_state.tmpbuf);
return base_state.result;
}
void psort(void *base, size_t nmemb, size_t size,
compar_t compar)
{
/* entry point to psort */
int ncpu = sysconf(_SC_NPROCESSORS_ONLN)/2;
int maxncpu = nmemb / 1024;
if (maxncpu < ncpu) {
ncpu = maxncpu;
}
/* don't invoke actual psort when less than 2 threads are needed */
if (ncpu < 2) {
qsort(base, nmemb, size, compar);
} else {
if (psort_main(base, nmemb, size, compar, ncpu)) {
/* if parallel sorting failed (i.e. due to failed thread
* creation, fall back to single threaded operation */
qsort(base, nmemb, size, compar);
}
}
}

22
casadm/psort.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef PSORT_H
#define PSORT_H
typedef int (*compar_t)(const void *, const void *);
/**
* function does exactly the same thing as qsort, except, that it sorts
* using many CPU cores, not just one.
*
* number of CPU cores is configured as half of the number of online
* CPUs in the system.
*/
void psort(void *base, size_t nmemb, size_t size,
compar_t compar);
#endif

View File

@@ -0,0 +1,72 @@
/*------------------------------------------------------------------
* ignore_handler_s.c
*
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2012 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
/**
* NAME
* ignore_handler_s
*
* SYNOPSIS
* #include "safe_lib.h"
* void ignore_handler_s(const char *msg, void *ptr, errno_t error)
*
* DESCRIPTION
* This function simply returns to the caller.
*
* SPECIFIED IN
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* msg Pointer to the message describing the error
*
* ptr Pointer to aassociated data. Can be NULL.
*
* error The error code encountered.
*
* RETURN VALUE
* Returns no value.
*
* ALSO SEE
* abort_handler_s()
*
*/
void ignore_handler_s(const char *msg, void *ptr, errno_t error)
{
sldebug_printf("IGNORE CONSTRAINT HANDLER: (%u) %s\n", error,
(msg) ? msg : "Null message");
return;
}
EXPORT_SYMBOL(ignore_handler_s);

View File

@@ -0,0 +1,853 @@
/*------------------------------------------------------------------
* mem_primitives_lib.c - Unguarded Memory Copy Routines
*
* February 2005, Bo Berry
*
* Copyright (c) 2005-2009 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "mem_primitives_lib.h"
/*
* mem_primitives_lib.c provides unguarded memory routines
* that are used by the safe_mem_library. These routines
* may also be used by an application, but the application
* is responsible for all parameter validation and alignment.
*/
/**
* NAME
* mem_prim_set - Sets memory to value
*
* SYNOPSIS
* #include "mem_primitives_lib.h"
* void
* mem_prim_set(void *dest, uint32_t len, uint8_t value)
*
* DESCRIPTION
* Sets len bytes starting at dest to the specified value
*
* INPUT PARAMETERS
* dest - pointer to memory that will be set to value
*
* len - number of bytes to be set
*
* value - byte value
*
* OUTPUT PARAMETERS
* dest - is updated
*
* RETURN VALUE
* none
*
*/
void
mem_prim_set (void *dest, uint32_t len, uint8_t value)
{
uint8_t *dp;
uint32_t count;
uint32_t lcount;
uint32_t *lp;
uint32_t value32;
count = len;
dp = dest;
value32 = value | (value << 8) | (value << 16) | (value << 24);
/*
* First, do the few bytes to get uint32_t aligned.
*/
for (; count && ( (uintptr_t)dp & (sizeof(uint32_t)-1) ); count--) {
*dp++ = value;
}
/*
* Then do the uint32_ts, unrolled the loop for performance
*/
lp = (uint32_t *)dp;
lcount = count >> 2;
while (lcount != 0) {
switch (lcount) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
lcount -= 16;
break;
case 15: *lp++ = value32;
case 14: *lp++ = value32;
case 13: *lp++ = value32;
case 12: *lp++ = value32;
case 11: *lp++ = value32;
case 10: *lp++ = value32;
case 9: *lp++ = value32;
case 8: *lp++ = value32;
case 7: *lp++ = value32;
case 6: *lp++ = value32;
case 5: *lp++ = value32;
case 4: *lp++ = value32;
case 3: *lp++ = value32;
case 2: *lp++ = value32;
case 1: *lp++ = value32;
lcount = 0;
break;
}
} /* end while */
dp = (uint8_t *)lp;
/*
* compute the number of remaining bytes
*/
count &= (sizeof(uint32_t)-1);
/*
* remaining bytes
*/
for (; count; dp++, count--) {
*dp = value;
}
return;
}
/**
* NAME
* mem_prim_set16 - Sets memory to value
*
* SYNOPSIS
* #include "mem_primitives_lib.h"
* void
* mem_prim_set16(uint16_t *dp, uint32_t len, uint16_t value)
*
* DESCRIPTION
* Sets len uint16_ts starting at dest to the specified value.
* Pointers must meet system alignment requirements.
*
* INPUT PARAMETERS
* dest - pointer to memory that will be set to value
*
* len - number of uint16_ts to be set
*
* value - uint16_t value
*
* OUTPUT PARAMETERS
* dest - is updated
*
* RETURN VALUE
* none
*
*/
void
mem_prim_set16 (uint16_t *dp, uint32_t len, uint16_t value)
{
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
len -= 16;
break;
case 15: *dp++ = value;
case 14: *dp++ = value;
case 13: *dp++ = value;
case 12: *dp++ = value;
case 11: *dp++ = value;
case 10: *dp++ = value;
case 9: *dp++ = value;
case 8: *dp++ = value;
case 7: *dp++ = value;
case 6: *dp++ = value;
case 5: *dp++ = value;
case 4: *dp++ = value;
case 3: *dp++ = value;
case 2: *dp++ = value;
case 1: *dp++ = value;
len = 0;
break;
}
} /* end while */
return;
}
/**
* NAME
* mem_prim_set32 - Sets memory to the uint32_t value
*
* SYNOPSIS
* #include "mem_primitives_lib.h"
* void
* mem_prim_set32(uint32_t *dp, uint32_t len, uint32_t value)
*
* DESCRIPTION
* Sets len uint32_ts starting at dest to the specified value
* Pointers must meet system alignment requirements.
*
* INPUT PARAMETERS
* dest - pointer to memory that will be set to value
*
* len - number of uint32_ts to be set
*
* value - uint32_t value
*
* OUTPUT PARAMETERS
* dest - is updated
*
* RETURN VALUE
* none
*
*/
void
mem_prim_set32 (uint32_t *dp, uint32_t len, uint32_t value)
{
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
len -= 16;
break;
case 15: *dp++ = value;
case 14: *dp++ = value;
case 13: *dp++ = value;
case 12: *dp++ = value;
case 11: *dp++ = value;
case 10: *dp++ = value;
case 9: *dp++ = value;
case 8: *dp++ = value;
case 7: *dp++ = value;
case 6: *dp++ = value;
case 5: *dp++ = value;
case 4: *dp++ = value;
case 3: *dp++ = value;
case 2: *dp++ = value;
case 1: *dp++ = value;
len = 0;
break;
}
} /* end while */
return;
}
/**
* NAME
* mem_prim_move - Move (handles overlap) memory
*
* SYNOPSIS
* #include "mem_primitives_lib.h"
* void
* mem_prim_move(void *dest, const void *src, uint32_t len)
*
* DESCRIPTION
* Moves at most slen bytes from src to dest, up to dmax
* bytes. Dest may overlap with src.
*
* INPUT PARAMETERS
* dest - pointer to the memory that will be replaced by src.
*
* src - pointer to the memory that will be copied
* to dest
*
* len - maximum number bytes of src that can be copied
*
* OUTPUT PARAMETERS
* dest - is updated
*
* RETURN VALUE
* none
*
*/
void
mem_prim_move (void *dest, const void *src, uint32_t len)
{
#define wsize sizeof(uint32_t)
#define wmask (wsize - 1)
uint8_t *dp = dest;
const uint8_t *sp = src;
uint32_t tsp;
/*
* Determine if we need to copy forward or backward (overlap)
*/
if ((uintptr_t)dp < (uintptr_t)sp) {
/*
* Copy forward.
*/
/*
* get a working copy of src for bit operations
*/
tsp = (uintptr_t)sp;
/*
* Try to align both operands. This cannot be done
* unless the low bits match.
*/
if ((tsp | (uintptr_t)dp) & wmask) {
/*
* determine how many bytes to copy to align operands
*/
if ((tsp ^ (uintptr_t)dp) & wmask || len < wsize) {
tsp = len;
} else {
tsp = wsize - (tsp & wmask);
}
len -= tsp;
/*
* make the alignment
*/
do {
*dp++ = *sp++;
} while (--tsp);
}
/*
* Now copy, then mop up any trailing bytes.
*/
tsp = len / wsize;
if (tsp > 0) {
do {
*(uint32_t *)dp = *(uint32_t *)sp;
sp += wsize;
dp += wsize;
} while (--tsp);
}
/*
* copy over the remaining bytes and we're done
*/
tsp = len & wmask;
if (tsp > 0) {
do {
*dp++ = *sp++;
} while (--tsp);
}
} else {
/*
* This section is used to copy backwards, to handle any
* overlap. The alignment requires (tps&wmask) bytes to
* align.
*/
/*
* go to end of the memory to copy
*/
sp += len;
dp += len;
/*
* get a working copy of src for bit operations
*/
tsp = (uintptr_t)sp;
/*
* Try to align both operands.
*/
if ((tsp | (uintptr_t)dp) & wmask) {
if ((tsp ^ (uintptr_t)dp) & wmask || len <= wsize) {
tsp = len;
} else {
tsp &= wmask;
}
len -= tsp;
/*
* make the alignment
*/
do {
*--dp = *--sp;
} while (--tsp);
}
/*
* Now copy in uint32_t units, then mop up any trailing bytes.
*/
tsp = len / wsize;
if (tsp > 0) {
do {
sp -= wsize;
dp -= wsize;
*(uint32_t *)dp = *(uint32_t *)sp;
} while (--tsp);
}
/*
* copy over the remaining bytes and we're done
*/
tsp = len & wmask;
if (tsp > 0) {
tsp = len & wmask;
do {
*--dp = *--sp;
} while (--tsp);
}
}
return;
}
/**
* NAME
* mem_prim_move8 - Move (handles overlap) memory
*
* SYNOPSIS
* #include "mem_primitives_lib.h"
* void
* mem_prim_move8(void *dest, const void *src, uint32_t len)
*
* DESCRIPTION
* Moves at most len uint8_ts from sp to dp.
* The destination may overlap with source.
*
* INPUT PARAMETERS
* dp - pointer to the memory that will be replaced by sp.
*
* sp - pointer to the memory that will be copied
* to dp
*
* len - maximum number uint8_t of sp that can be copied
*
* OUTPUT PARAMETERS
* dp - pointer to the memory that will be replaced by sp.
*
* RETURN VALUE
* none
*
*/
void
mem_prim_move8 (uint8_t *dp, const uint8_t *sp, uint32_t len)
{
/*
* Determine if we need to copy forward or backward (overlap)
*/
if (dp < sp) {
/*
* Copy forward.
*/
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
len -= 16;
break;
case 15: *dp++ = *sp++;
case 14: *dp++ = *sp++;
case 13: *dp++ = *sp++;
case 12: *dp++ = *sp++;
case 11: *dp++ = *sp++;
case 10: *dp++ = *sp++;
case 9: *dp++ = *sp++;
case 8: *dp++ = *sp++;
case 7: *dp++ = *sp++;
case 6: *dp++ = *sp++;
case 5: *dp++ = *sp++;
case 4: *dp++ = *sp++;
case 3: *dp++ = *sp++;
case 2: *dp++ = *sp++;
case 1: *dp++ = *sp++;
len = 0;
break;
}
} /* end while */
} else {
/*
* This section is used to copy backwards, to handle any
* overlap. The alignment requires (tps&wmask) bytes to
* align.
*/
/*
* go to end of the memory to copy
*/
sp += len;
dp += len;
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
len -= 16;
break;
case 15: *--dp = *--sp;
case 14: *--dp = *--sp;
case 13: *--dp = *--sp;
case 12: *--dp = *--sp;
case 11: *--dp = *--sp;
case 10: *--dp = *--sp;
case 9: *--dp = *--sp;
case 8: *--dp = *--sp;
case 7: *--dp = *--sp;
case 6: *--dp = *--sp;
case 5: *--dp = *--sp;
case 4: *--dp = *--sp;
case 3: *--dp = *--sp;
case 2: *--dp = *--sp;
case 1: *--dp = *--sp;
len = 0;
break;
}
} /* end while */
}
return;
}
/**
* NAME
* mem_prim_move16 - Move (handles overlap) memory
*
* SYNOPSIS
* #include "mem_primitives_lib.h"
* void
* mem_prim_move16(void *dest, const void *src, uint32_t len)
*
* DESCRIPTION
* Moves at most len uint16_ts from sp to dp.
* The destination may overlap with source.
*
* INPUT PARAMETERS
* dp - pointer to the memory that will be replaced by sp.
*
* sp - pointer to the memory that will be copied
* to dp
*
* len - maximum number uint16_t of sp that can be copied
*
* OUTPUT PARAMETERS
* dp - is updated
*
* RETURN VALUE
* none
*
*/
void
mem_prim_move16 (uint16_t *dp, const uint16_t *sp, uint32_t len)
{
/*
* Determine if we need to copy forward or backward (overlap)
*/
if (dp < sp) {
/*
* Copy forward.
*/
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
len -= 16;
break;
case 15: *dp++ = *sp++;
case 14: *dp++ = *sp++;
case 13: *dp++ = *sp++;
case 12: *dp++ = *sp++;
case 11: *dp++ = *sp++;
case 10: *dp++ = *sp++;
case 9: *dp++ = *sp++;
case 8: *dp++ = *sp++;
case 7: *dp++ = *sp++;
case 6: *dp++ = *sp++;
case 5: *dp++ = *sp++;
case 4: *dp++ = *sp++;
case 3: *dp++ = *sp++;
case 2: *dp++ = *sp++;
case 1: *dp++ = *sp++;
len = 0;
break;
}
} /* end while */
} else {
/*
* This section is used to copy backwards, to handle any
* overlap. The alignment requires (tps&wmask) bytes to
* align.
*/
/*
* go to end of the memory to copy
*/
sp += len;
dp += len;
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
len -= 16;
break;
case 15: *--dp = *--sp;
case 14: *--dp = *--sp;
case 13: *--dp = *--sp;
case 12: *--dp = *--sp;
case 11: *--dp = *--sp;
case 10: *--dp = *--sp;
case 9: *--dp = *--sp;
case 8: *--dp = *--sp;
case 7: *--dp = *--sp;
case 6: *--dp = *--sp;
case 5: *--dp = *--sp;
case 4: *--dp = *--sp;
case 3: *--dp = *--sp;
case 2: *--dp = *--sp;
case 1: *--dp = *--sp;
len = 0;
break;
}
} /* end while */
}
return;
}
/**
* NAME
* mem_prim_move32 - Move (handles overlap) memory
*
* SYNOPSIS
* #include "mem_primitives_lib.h"
* void
* mem_prim_move32(void *dest, const void *src, uint32_t len)
*
* DESCRIPTION
* Moves at most len uint32_ts from sp to dp.
* The destination may overlap with source.
*
* INPUT PARAMETERS
* dp - pointer to the memory that will be replaced by sp.
*
* sp - pointer to the memory that will be copied
* to dp
*
* len - maximum number uint32_t of sp that can be copied
*
* OUTPUT PARAMETERS
* dp - is updated
*
* RETURN VALUE
* none
*
*/
void
mem_prim_move32 (uint32_t *dp, const uint32_t *sp, uint32_t len)
{
/*
* Determine if we need to copy forward or backward (overlap)
*/
if (dp < sp) {
/*
* Copy forward.
*/
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
len -= 16;
break;
case 15: *dp++ = *sp++;
case 14: *dp++ = *sp++;
case 13: *dp++ = *sp++;
case 12: *dp++ = *sp++;
case 11: *dp++ = *sp++;
case 10: *dp++ = *sp++;
case 9: *dp++ = *sp++;
case 8: *dp++ = *sp++;
case 7: *dp++ = *sp++;
case 6: *dp++ = *sp++;
case 5: *dp++ = *sp++;
case 4: *dp++ = *sp++;
case 3: *dp++ = *sp++;
case 2: *dp++ = *sp++;
case 1: *dp++ = *sp++;
len = 0;
break;
}
} /* end while */
} else {
/*
* This section is used to copy backwards, to handle any
* overlap.
*/
/*
* go to end of the memory to copy
*/
sp += len;
dp += len;
while (len != 0) {
switch (len) {
/*
* Here we do blocks of 8. Once the remaining count
* drops below 8, take the fast track to finish up.
*/
default:
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
len -= 16;
break;
case 15: *--dp = *--sp;
case 14: *--dp = *--sp;
case 13: *--dp = *--sp;
case 12: *--dp = *--sp;
case 11: *--dp = *--sp;
case 10: *--dp = *--sp;
case 9: *--dp = *--sp;
case 8: *--dp = *--sp;
case 7: *--dp = *--sp;
case 6: *--dp = *--sp;
case 5: *--dp = *--sp;
case 4: *--dp = *--sp;
case 3: *--dp = *--sp;
case 2: *--dp = *--sp;
case 1: *--dp = *--sp;
len = 0;
break;
}
} /* end while */
}
return;
}

View File

@@ -0,0 +1,74 @@
/*------------------------------------------------------------------
* mem_primitives_lib.h - Unguarded Memory Copy Routines
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __MEM_PRIMITIVES_LIB_H__
#define __MEM_PRIMITIVES_LIB_H__
#include "safeclib_private.h"
/*
* These are prototypes for _unguarded_ memory routines. The caller must
* validate all parameters prior to invocation. Useful for diagnostics
* and system initialization processing.
*/
/* moves (handles overlap) memory */
extern void
mem_prim_move(void *dest, const void *src, uint32_t length);
/* uint8_t moves (handles overlap) memory */
extern void
mem_prim_move8(uint8_t *dest, const uint8_t *src, uint32_t length);
/* uint16_t moves (handles overlap) memory */
extern void
mem_prim_move16(uint16_t *dest, const uint16_t *src, uint32_t length);
/* uint32_t moves (handles overlap) memory */
extern void
mem_prim_move32(uint32_t *dest, const uint32_t *src, uint32_t length);
/* set bytes */
extern void
mem_prim_set(void *dest, uint32_t dmax, uint8_t value);
/* set uint16_ts */
extern void
mem_prim_set16(uint16_t *dest, uint32_t dmax, uint16_t value);
/* set uint32_ts */
extern void
mem_prim_set32(uint32_t *dest, uint32_t dmax, uint32_t value);
#endif /* __MEM_PRIMITIVES_LIB_H__ */

157
casadm/safeclib/memcpy_s.c Normal file
View File

@@ -0,0 +1,157 @@
/*------------------------------------------------------------------
* memcpy_s
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_mem_constraint.h"
#include "mem_primitives_lib.h"
#include "safe_mem_lib.h"
/**
* NAME
* memcpy_s
*
* SYNOPSIS
* #include "safe_mem_lib.h"
* errno_t
* memcpy_s(void *dest, rsize_t dmax, const void *src, rsize_t smax)
*
* DESCRIPTION
* This function copies at most smax bytes from src to dest, up to
* dmax.
*
* SPECIFIED IN
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* dest pointer to memory that will be replaced by src.
*
* dmax maximum length of the resulting dest
*
* src pointer to the memory that will be copied to dest
*
* smax maximum number bytes of src to copy
*
* OUTPUT PARAMETERS
* dest is updated
*
* RUNTIME CONSTRAINTS
* Neither dest nor src shall be a null pointer.
* Neither dmax nor smax shall be zero.
* dmax shall not be greater than RSIZE_MAX_MEM.
* smax shall not be greater than dmax.
* Copying shall not take place between regions that overlap.
* If there is a runtime-constraint violation, the memcpy_s function
* stores zeros in the first dmax bytes of the region pointed to
* by dest if dest is not a null pointer and smax is valid.
*
* RETURN VALUE
* EOK successful operation
* ESNULLP NULL pointer
* ESZEROL zero length
* ESLEMAX length exceeds max limit
* ESOVRLP source memory overlaps destination
*
* ALSO SEE
* memcpy16_s(), memcpy32_s(), memmove_s(), memmove16_s(),
* memmove32_s()
*
*/
errno_t
memcpy_s (void *dest, rsize_t dmax, const void *src, rsize_t smax)
{
uint8_t *dp;
const uint8_t *sp;
dp = dest;
sp = src;
if (dp == NULL) {
invoke_safe_mem_constraint_handler("memcpy_s: dest is NULL",
NULL, ESNULLP);
return RCNEGATE(ESNULLP);
}
if (dmax == 0) {
invoke_safe_mem_constraint_handler("memcpy_s: dmax is 0",
NULL, ESZEROL);
return RCNEGATE(ESZEROL);
}
if (dmax > RSIZE_MAX_MEM) {
invoke_safe_mem_constraint_handler("memcpy_s: dmax exceeds max",
NULL, ESLEMAX);
return RCNEGATE(ESLEMAX);
}
if (smax == 0) {
mem_prim_set(dp, dmax, 0);
invoke_safe_mem_constraint_handler("memcpy_s: smax is 0",
NULL, ESZEROL);
return RCNEGATE(ESZEROL);
}
if (smax > dmax) {
mem_prim_set(dp, dmax, 0);
invoke_safe_mem_constraint_handler("memcpy_s: smax exceeds dmax",
NULL, ESLEMAX);
return RCNEGATE(ESLEMAX);
}
if (sp == NULL) {
mem_prim_set(dp, dmax, 0);
invoke_safe_mem_constraint_handler("memcpy_s: src is NULL",
NULL, ESNULLP);
return RCNEGATE(ESNULLP);
}
/*
* overlap is undefined behavior, do not allow
*/
if( ((dp > sp) && (dp < (sp+smax))) ||
((sp > dp) && (sp < (dp+dmax))) ) {
mem_prim_set(dp, dmax, 0);
invoke_safe_mem_constraint_handler("memcpy_s: overlap undefined",
NULL, ESOVRLP);
return RCNEGATE(ESOVRLP);
}
/*
* now perform the copy
*/
mem_prim_move(dp, sp, smax);
return RCNEGATE(EOK);
}
EXPORT_SYMBOL(memcpy_s);

148
casadm/safeclib/memmove_s.c Normal file
View File

@@ -0,0 +1,148 @@
/*------------------------------------------------------------------
* memmove_s.c
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_mem_constraint.h"
#include "mem_primitives_lib.h"
#include "safe_mem_lib.h"
/**
* NAME
* memmove_s
*
* SYNOPSIS
* #include "safe_mem_lib.h"
* errno_t
* memmove_s(void *dest, rsize_t dmax,
* const void *src, rsize_t smax)
*
* DESCRIPTION
* The memmove_s function copies smax bytes from the region pointed
* to by src into the region pointed to by dest. This copying takes place
* as if the smax bytes from the region pointed to by src are first copied
* into a temporary array of smax bytes that does not overlap the region
* pointed to by dest or src, and then the smax bytes from the temporary
* array are copied into the object region to by dest.
*
* SPECIFIED IN
* ISO/IEC TR 24731, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* dest pointer to the memory that will be replaced by src.
*
* dmax maximum length of the resulting dest, in bytes
*
* src pointer to the memory that will be copied
* to dest
*
* smax maximum number bytes of src that can be copied
*
* OUTPUT PARAMETERS
* dest is updated
*
* RUNTIME CONSTRAINTS
* Neither dest nor src shall be a null pointer.
* Neither dmax nor smax shall be 0.
* dmax shall not be greater than RSIZE_MAX_MEM.
* smax shall not be greater than dmax.
* If there is a runtime-constraint violation, the memmove_s function
* stores zeros in the first dmax characters of the regionpointed to
* by dest if dest is not a null pointer and dmax is not greater
* than RSIZE_MAX_MEM.
*
* RETURN VALUE
* EOK successful operation
* ESNULLP NULL pointer
* ESZEROL zero length
* ESLEMAX length exceeds max limit
*
* ALSO SEE
* memmove16_s(), memmove32_s(), memcpy_s(), memcpy16_s() memcpy32_s()
*
*/
errno_t
memmove_s (void *dest, rsize_t dmax, const void *src, rsize_t smax)
{
uint8_t *dp;
const uint8_t *sp;
dp= dest;
sp = src;
if (dp == NULL) {
invoke_safe_mem_constraint_handler("memmove_s: dest is null",
NULL, ESNULLP);
return (RCNEGATE(ESNULLP));
}
if (dmax == 0) {
invoke_safe_mem_constraint_handler("memmove_s: dmax is 0",
NULL, ESZEROL);
return (RCNEGATE(ESZEROL));
}
if (dmax > RSIZE_MAX_MEM) {
invoke_safe_mem_constraint_handler("memmove_s: dmax exceeds max",
NULL, ESLEMAX);
return (RCNEGATE(ESLEMAX));
}
if (smax == 0) {
mem_prim_set(dp, dmax, 0);
invoke_safe_mem_constraint_handler("memmove_s: smax is 0",
NULL, ESZEROL);
return (RCNEGATE(ESZEROL));
}
if (smax > dmax) {
mem_prim_set(dp, dmax, 0);
invoke_safe_mem_constraint_handler("memmove_s: smax exceeds max",
NULL, ESLEMAX);
return (RCNEGATE(ESLEMAX));
}
if (sp == NULL) {
mem_prim_set(dp, dmax, 0);
invoke_safe_mem_constraint_handler("memmove_s: src is null",
NULL, ESNULLP);
return (RCNEGATE(ESNULLP));
}
/*
* now perform the copy
*/
mem_prim_move(dp, sp, smax);
return (RCNEGATE(EOK));
}
EXPORT_SYMBOL(memmove_s);

105
casadm/safeclib/memset_s.c Normal file
View File

@@ -0,0 +1,105 @@
/*------------------------------------------------------------------
* memset_s
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_mem_constraint.h"
#include "mem_primitives_lib.h"
#include "safe_mem_lib.h"
/**
* NAME
* memset_s
*
* SYNOPSIS
* #include "safe_mem_lib.h"
* errno_t
* memset_s(void *dest, rsize_t len, uint8_t value)
*
* DESCRIPTION
* Sets len bytes starting at dest to the specified value.
*
* SPECIFIED IN
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* dest pointer to memory that will be set to the value
*
* len number of bytes to be set
*
* value byte value
*
* OUTPUT PARAMETERS
* dest is updated
*
* RUNTIME CONSTRAINTS
* dest shall not be a null pointer.
* len shall not be 0 nor greater than RSIZE_MAX_MEM.
* If there is a runtime constraint, the operation is not performed.
*
* RETURN VALUE
* EOK successful operation
* ESNULLP NULL pointer
* ESZEROL zero length
* ESLEMAX length exceeds max limit
*
* ALSO SEE
* memset16_s(), memset32_s()
*
*/
errno_t
memset_s (void *dest, rsize_t len, uint8_t value)
{
if (dest == NULL) {
invoke_safe_mem_constraint_handler("memset_s: dest is null",
NULL, ESNULLP);
return (RCNEGATE(ESNULLP));
}
if (len == 0) {
invoke_safe_mem_constraint_handler("memset_s: len is 0",
NULL, ESZEROL);
return (RCNEGATE(ESZEROL));
}
if (len > RSIZE_MAX_MEM) {
invoke_safe_mem_constraint_handler("memset_s: len exceeds max",
NULL, ESLEMAX);
return (RCNEGATE(ESLEMAX));
}
mem_prim_set(dest, len, value);
return (RCNEGATE(EOK));
}
EXPORT_SYMBOL(memset_s);

View File

@@ -0,0 +1,61 @@
/*------------------------------------------------------------------
* safe_lib.h -- Safe C Library
*
* October 2008, Bo Berry
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2008-2013 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFE_LIB_H__
#define __SAFE_LIB_H__
#include "safe_types.h"
#include "safe_lib_errno.h"
/* C11 appendix K types - specific for bounds checking */
typedef size_t rsize_t;
/*
* We depart from the standard and allow memory and string operations to
* have different max sizes. See the respective safe_mem_lib.h or
* safe_str_lib.h files.
*/
/* #define RSIZE_MAX (~(rsize_t)0) - leave here for completeness */
typedef void (*constraint_handler_t) (const char * /* msg */,
void * /* ptr */,
errno_t /* error */);
extern void abort_handler_s(const char *msg, void *ptr, errno_t error);
extern void ignore_handler_s(const char *msg, void *ptr, errno_t error);
#define sl_default_handler ignore_handler_s
#include "safe_mem_lib.h"
#include "safe_str_lib.h"
#endif /* __SAFE_LIB_H__ */

View File

@@ -0,0 +1,91 @@
/*------------------------------------------------------------------
* safe_lib_errno.h -- Safe C Lib Error codes
*
* October 2008, Bo Berry
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2008-2013 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFE_LIB_ERRNO_H__
#define __SAFE_LIB_ERRNO_H__
#ifdef __KERNEL__
# include <linux/errno.h>
#else
#include <errno.h>
#endif /* __KERNEL__ */
/*
* Safe Lib specific errno codes. These can be added to the errno.h file
* if desired.
*/
#ifndef ESNULLP
#define ESNULLP ( 400 ) /* null ptr */
#endif
#ifndef ESZEROL
#define ESZEROL ( 401 ) /* length is zero */
#endif
#ifndef ESLEMIN
#define ESLEMIN ( 402 ) /* length is below min */
#endif
#ifndef ESLEMAX
#define ESLEMAX ( 403 ) /* length exceeds max */
#endif
#ifndef ESOVRLP
#define ESOVRLP ( 404 ) /* overlap undefined */
#endif
#ifndef ESEMPTY
#define ESEMPTY ( 405 ) /* empty string */
#endif
#ifndef ESNOSPC
#define ESNOSPC ( 406 ) /* not enough space for s2 */
#endif
#ifndef ESUNTERM
#define ESUNTERM ( 407 ) /* unterminated string */
#endif
#ifndef ESNODIFF
#define ESNODIFF ( 408 ) /* no difference */
#endif
#ifndef ESNOTFND
#define ESNOTFND ( 409 ) /* not found */
#endif
/* EOK may or may not be defined in errno.h */
#ifndef EOK
#define EOK ( 0 )
#endif
#endif /* __SAFE_LIB_ERRNO_H__ */

View File

@@ -0,0 +1,142 @@
/*------------------------------------------------------------------
* safe_mem_constraint.c
*
* October 2008, Bo Berry
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2008-2012 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_mem_constraint.h"
#include "safe_mem_lib.h"
static constraint_handler_t mem_handler = NULL;
/**
* NAME
* set_mem_constraint_handler_s
*
* SYNOPSIS
* #include "safe_mem_lib.h"
* constraint_handler_t
* set_mem_constraint_handler_straint_handler_t handler)
*
* DESCRIPTION
* The set_mem_constraint_handler_s function sets the runtime-constraint
* handler to be handler. The runtime-constraint handler is the function to
* be called when a library function detects a runtime-constraint
* order:
* 1. A pointer to a character string describing the
* runtime-constraint violation.
* 2. A null pointer or a pointer to an implementation defined
* object.
* 3. If the function calling the handler has a return type declared
* as errno_t, the return value of the function is passed.
* Otherwise, a positive value of type errno_t is passed.
* The implementation has a default constraint handler that is used if no
* calls to the set_constraint_handler_s function have been made. The
* behavior of the default handler is implementation-defined, and it may
* cause the program to exit or abort. If the handler argument to
* set_constraint_handler_s is a null pointer, the implementation default
* handler becomes the current constraint handler.
*
* SPECIFIED IN
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* *msg Pointer to the message describing the error
*
* *ptr Pointer to aassociated data. Can be NULL.
*
* error The error code encountered.
*
* OUTPUT PARAMETERS
* none
*
* RETURN VALUE
* none
*
* ALSO SEE
* set_str_constraint_handler_s()
*/
constraint_handler_t
set_mem_constraint_handler_s (constraint_handler_t handler)
{
constraint_handler_t prev_handler = mem_handler;
if (NULL == handler) {
mem_handler = sl_default_handler;
} else {
mem_handler = handler;
}
return prev_handler;
}
EXPORT_SYMBOL(set_mem_constraint_handler_s);
/**
* NAME
* invoke_safe_mem_constraint_handler
*
* SYNOPSIS
* #include "safe_mem_constraint.h"
* void
* invoke_safe_mem_constraint_handler(const char *msg,
* void *ptr,
* errno_t error)
*
* DESCRIPTION
* Invokes the currently set constraint handler or the default.
*
* INPUT PARAMETERS
* *msg Pointer to the message describing the error
*
* *ptr Pointer to aassociated data. Can be NULL.
*
* error The error code encountered.
*
* OUTPUT PARAMETERS
* none
*
* RETURN VALUE
* none
*
*/
void
invoke_safe_mem_constraint_handler (const char *msg,
void *ptr,
errno_t error)
{
if (NULL != mem_handler) {
mem_handler(msg, ptr, error);
} else {
sl_default_handler(msg, ptr, error);
}
}

View File

@@ -0,0 +1,46 @@
/*------------------------------------------------------------------
* safe_mem_constraint.h
*
* October 2008, Bo Berry
*
* Copyright (c) 2008, 2009 by Cisco Systems, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFE_MEM_CONSTRAINT_H__
#define __SAFE_MEM_CONSTRAINT_H__
#include "safeclib_private.h"
/*
* Function used by the libraries to invoke the registered
* runtime-constraint handler. Always needed.
*/
extern void invoke_safe_mem_constraint_handler(
const char *msg,
void *ptr,
errno_t error);
#endif /* __SAFE_MEM_CONSTRAINT_H__ */

View File

@@ -0,0 +1,57 @@
/*------------------------------------------------------------------
* safe_mem_lib.h -- Safe C Library Memory APIs
*
* October 2008, Bo Berry
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2008-2012 by Cisco Systems, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFE_MEM_LIB_H__
#define __SAFE_MEM_LIB_H__
#include "safe_lib.h"
#define RSIZE_MAX_MEM ( 256UL << 20 ) /* 256MB */
#define RSIZE_MAX_MEM16 ( RSIZE_MAX_MEM/2 )
#define RSIZE_MAX_MEM32 ( RSIZE_MAX_MEM/4 )
/* set memory constraint handler */
extern constraint_handler_t
set_mem_constraint_handler_s(constraint_handler_t handler);
/* copy memory */
extern errno_t memcpy_s(void *dest, rsize_t dmax,
const void *src, rsize_t slen);
/* set bytes */
extern errno_t memset_s(void *dest, rsize_t dmax, uint8_t value);
/* move memory, including overlapping memory */
extern errno_t memmove_s(void *dest, rsize_t dmax,
const void *src, rsize_t slen);
#endif /* __SAFE_MEM_LIB_H__ */

View File

@@ -0,0 +1,146 @@
/*------------------------------------------------------------------
* safe_str_constraint.c
*
* October 2008, Bo Berry
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2008, 2009, 2012 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_str_constraint.h"
#include "safe_str_lib.h"
static constraint_handler_t str_handler = NULL;
/**
* NAME
* set_str_constraint_handler_s
*
* SYNOPSIS
* #include "safe_str_lib.h"
* constraint_handler_t
* set_str_constraint_handler_s(constraint_handler_t handler)
*
* DESCRIPTION
* The set_str_constraint_handler_s function sets the runtime-constraint
* handler to be handler. The runtime-constraint handler is the function to
* be called when a library function detects a runtime-constraint
* violation. Only the most recent handler registered with
* set_str_constraint_handler_s is called when a runtime-constraint
* violation occurs.
* When the handler is called, it is passed the following arguments in
* the following order:
* 1. A pointer to a character string describing the
* runtime-constraint violation.
* 2. A null pointer or a pointer to an implementation defined
* object.
* 3. If the function calling the handler has a return type declared
* as errno_t, the return value of the function is passed.
* Otherwise, a positive value of type errno_t is passed.
* The implementation has a default constraint handler that is used if no
* calls to the set_constraint_handler_s function have been made. The
* behavior of the default handler is implementation-defined, and it may
* cause the program to exit or abort. If the handler argument to
* set_constraint_handler_s is a null pointer, the implementation default
* handler becomes the current constraint handler.
*
* SPECIFIED IN
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* *msg Pointer to the message describing the error
*
* *ptr Pointer to aassociated data. Can be NULL.
*
* error The error code encountered.
*
* OUTPUT PARAMETERS
* none
*
* RETURN VALUE
* none
*
* ALSO SEE
* set_str_constraint_handler_s()
*/
constraint_handler_t
set_str_constraint_handler_s (constraint_handler_t handler)
{
constraint_handler_t prev_handler = str_handler;
if (NULL == handler) {
str_handler = sl_default_handler;
} else {
str_handler = handler;
}
return prev_handler;
}
EXPORT_SYMBOL(set_str_constraint_handler_s);
/**
* NAME
* invoke_safe_str_constraint_handler
*
* SYNOPSIS
* #include "safe_str_constraint.h"
* void
* invoke_safe_str_constraint_handler (const char *msg,
* void *ptr,
* errno_t error)
*
* DESCRIPTION
* Invokes the currently set constraint handler or the default.
*
* INPUT PARAMETERS
* *msg Pointer to the message describing the error
*
* *ptr Pointer to aassociated data. Can be NULL.
*
* error The error code encountered.
*
* OUTPUT PARAMETERS
* none
*
* RETURN VALUE
* none
*
*/
void
invoke_safe_str_constraint_handler (const char *msg,
void *ptr,
errno_t error)
{
if (NULL != str_handler) {
str_handler(msg, ptr, error);
} else {
sl_default_handler(msg, ptr, error);
}
}

View File

@@ -0,0 +1,64 @@
/*------------------------------------------------------------------
* safe_str_constraint.h
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 Cisco Systems
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFE_STR_CONSTRAINT_H__
#define __SAFE_STR_CONSTRAINT_H__
#include "safeclib_private.h"
/*
* Function used by the libraries to invoke the registered
* runtime-constraint handler. Always needed.
*/
extern void invoke_safe_str_constraint_handler(
const char *msg,
void *ptr,
errno_t error);
/*
* Safe C Lib internal string routine to consolidate error handling
*/
static inline void handle_error(char *orig_dest, rsize_t orig_dmax,
char *err_msg, errno_t err_code)
{
#ifdef SAFECLIB_STR_NULL_SLACK
/* null string to eliminate partial copy */
while (orig_dmax) { *orig_dest = '\0'; orig_dmax--; orig_dest++; }
#else
*orig_dest = '\0';
#endif
invoke_safe_str_constraint_handler(err_msg, NULL, err_code);
return;
}
#endif /* __SAFE_STR_CONSTRAINT_H__ */

View File

@@ -0,0 +1,71 @@
/*------------------------------------------------------------------
* safe_str_lib.h -- Safe C Library String APIs
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011, 2013 by Cisco Systems, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFE_STR_LIB_H__
#define __SAFE_STR_LIB_H__
#include "safe_lib.h"
/*
* The shortest string is a null string!!
*/
#define RSIZE_MIN_STR ( 1 )
/* maximum sring length */
#define RSIZE_MAX_STR ( 4UL << 10 ) /* 4KB */
/* The makeup of a password */
#define SAFE_STR_MIN_LOWERCASE ( 2 )
#define SAFE_STR_MIN_UPPERCASE ( 2 )
#define SAFE_STR_MIN_NUMBERS ( 1 )
#define SAFE_STR_MIN_SPECIALS ( 1 )
#define SAFE_STR_PASSWORD_MIN_LENGTH ( 6 )
#define SAFE_STR_PASSWORD_MAX_LENGTH ( 32 )
/* set string constraint handler */
extern constraint_handler_t
set_str_constraint_handler_s(constraint_handler_t handler);
/* fitted string copy */
extern errno_t
strncpy_s(char *dest, rsize_t dmax, const char *src, rsize_t slen);
/* string length */
extern rsize_t
strnlen_s (const char *s, rsize_t smax);
/* string tokenizer */
extern char *
strtok_s(char *s1, rsize_t *s1max, const char *src, char **ptr);
#endif /* __SAFE_STR_LIB_H__ */

View File

@@ -0,0 +1,59 @@
/*------------------------------------------------------------------
* safe_types.h - C99 std types & defs or Linux kernel equivalents
*
* March 2007, Bo Berry
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2007-2013 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFE_TYPES_H__
#define __SAFE_TYPES_H__
#ifdef __KERNEL__
/* linux kernel environment */
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/errno.h>
/* errno_t isn't defined in the kernel */
typedef int errno_t;
#else
#include <stdio.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdint.h>
#include <errno.h>
typedef int errno_t;
#include <stdbool.h>
#endif /* __KERNEL__ */
#endif /* __SAFE_TYPES_H__ */

View File

@@ -0,0 +1,93 @@
/*------------------------------------------------------------------
* safeclib_private.h - Internal library references
*
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
*
* Copyright (c) 2012, 2013 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#ifndef __SAFECLIB_PRIVATE_H__
#define __SAFECLIB_PRIVATE_H__
#ifdef __KERNEL__
/* linux kernel environment */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ctype.h>
#define RCNEGATE(x) ( -(x) )
#define slprintf(...) printk(KERN_EMERG __VA_ARGS__)
#define slabort()
#ifdef DEBUG
#define sldebug_printf(...) printk(KERN_DEBUG __VA_ARGS__)
#endif
#else /* !__KERNEL__ */
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#ifdef STDC_HEADERS
# include <ctype.h>
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
# include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#define EXPORT_SYMBOL(sym)
#define RCNEGATE(x) (x)
#define slprintf(...) fprintf(stderr, __VA_ARGS__)
#define slabort() abort()
#ifdef DEBUG
#define sldebug_printf(...) printf(__VA_ARGS__)
#endif
#endif /* __KERNEL__ */
#ifndef sldebug_printf
#define sldebug_printf(...)
#endif
#include "safe_lib.h"
#endif /* __SAFECLIB_PRIVATE_H__ */

238
casadm/safeclib/strncpy_s.c Normal file
View File

@@ -0,0 +1,238 @@
/*------------------------------------------------------------------
* strncpy_s.c
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_str_constraint.h"
#include "safe_str_lib.h"
/*
* NAME
* strncpy_s
*
* SYNOPSIS
* #include "safe_str_lib.h"
* errno_t
* strncpy_s(char *dest, rsize_t dmax, const char *src, rsize_t slen)
*
* DESCRIPTION
* The strncpy_s function copies not more than slen successive characters
* (characters that follow a null character are not copied) from the
* array pointed to by src to the array pointed to by dest. If no null
* character was copied from src, then dest[n] is set to a null character.
*
* All elements following the terminating null character (if any)
* written by strncpy_s in the array of dmax characters pointed to
* by dest take on the null value when strncpy_s returns.
*
* Specicified in:
* ISO/IEC TR 24731-1, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* dest pointer to string that will be replaced by src.
* The resulting string is null terminated.
*
* dmax restricted maximum length of the resulting dest,
* including the null
*
* src pointer to the string that will be copied
* to string dest
*
* slen the maximum number of characters to copy from src
*
* OUTPUT PARAMETERS
* dest updated with src string
*
* RUNTIME CONSTRAINTS
* Neither dmax nor slen shall be equal to zero.
* Neither dmax nor slen shall be equal zero.
* Neither dmax nor slen shall be greater than RSIZE_MAX_STR.
* If slen is either greater than or equal to dmax, then dmax
* should be more than strnlen_s(src,dmax)
* Copying shall not take place between objects that overlap.
* If there is a runtime-constraint violation, then if dest
* is not a null pointer and dmax greater than RSIZE_MAX_STR,
* then strncpy_s nulls dest.
*
* RETURN VALUE
* EOK successful operation, the characters in src were copied
* to dest and the result is null terminated.
* ESNULLP NULL pointer
* ESZEROL zero length
* ESLEMAX length exceeds max limit
* ESOVRLP strings overlap
* ESNOSPC not enough space to copy src
*
* ALSO SEE
* strcat_s(), strncat_s(), strcpy_s()
*-
*/
errno_t
strncpy_s (char *dest, rsize_t dmax, const char *src, rsize_t slen)
{
rsize_t orig_dmax;
char *orig_dest;
const char *overlap_bumper;
if (dest == NULL) {
invoke_safe_str_constraint_handler("strncpy_s: dest is null",
NULL, ESNULLP);
return RCNEGATE(ESNULLP);
}
if (dmax == 0) {
invoke_safe_str_constraint_handler("strncpy_s: dmax is 0",
NULL, ESZEROL);
return RCNEGATE(ESZEROL);
}
if (dmax > RSIZE_MAX_STR) {
invoke_safe_str_constraint_handler("strncpy_s: dmax exceeds max",
NULL, ESLEMAX);
return RCNEGATE(ESLEMAX);
}
/* hold base in case src was not copied */
orig_dmax = dmax;
orig_dest = dest;
if (src == NULL) {
handle_error(orig_dest, orig_dmax, "strncpy_s: "
"src is null",
ESNULLP);
return RCNEGATE(ESNULLP);
}
if (slen == 0) {
handle_error(orig_dest, orig_dmax, "strncpy_s: "
"slen is zero",
ESZEROL);
return RCNEGATE(ESZEROL);
}
if (slen > RSIZE_MAX_STR) {
handle_error(orig_dest, orig_dmax, "strncpy_s: "
"slen exceeds max",
ESLEMAX);
return RCNEGATE(ESLEMAX);
}
if (dest < src) {
overlap_bumper = src;
while (dmax > 0) {
if (dest == overlap_bumper) {
handle_error(orig_dest, orig_dmax, "strncpy_s: "
"overlapping objects",
ESOVRLP);
return RCNEGATE(ESOVRLP);
}
if (slen == 0) {
/*
* Copying truncated to slen chars. Note that the TR says to
* copy slen chars plus the null char. We null the slack.
*/
#ifdef SAFECLIB_STR_NULL_SLACK
while (dmax) { *dest = '\0'; dmax--; dest++; }
#else
*dest = '\0';
#endif
return RCNEGATE(EOK);
}
*dest = *src;
if (*dest == '\0') {
#ifdef SAFECLIB_STR_NULL_SLACK
/* null slack */
while (dmax) { *dest = '\0'; dmax--; dest++; }
#endif
return RCNEGATE(EOK);
}
dmax--;
slen--;
dest++;
src++;
}
} else {
overlap_bumper = dest;
while (dmax > 0) {
if (src == overlap_bumper) {
handle_error(orig_dest, orig_dmax, "strncpy_s: "
"overlapping objects",
ESOVRLP);
return RCNEGATE(ESOVRLP);
}
if (slen == 0) {
/*
* Copying truncated to slen chars. Note that the TR says to
* copy slen chars plus the null char. We null the slack.
*/
#ifdef SAFECLIB_STR_NULL_SLACK
while (dmax) { *dest = '\0'; dmax--; dest++; }
#else
*dest = '\0';
#endif
return RCNEGATE(EOK);
}
*dest = *src;
if (*dest == '\0') {
#ifdef SAFECLIB_STR_NULL_SLACK
/* null slack */
while (dmax) { *dest = '\0'; dmax--; dest++; }
#endif
return RCNEGATE(EOK);
}
dmax--;
slen--;
dest++;
src++;
}
}
/*
* the entire src was not copied, so zero the string
*/
handle_error(orig_dest, orig_dmax, "strncpy_s: not enough "
"space for src",
ESNOSPC);
return RCNEGATE(ESNOSPC);
}
EXPORT_SYMBOL(strncpy_s);

117
casadm/safeclib/strnlen_s.c Normal file
View File

@@ -0,0 +1,117 @@
/*------------------------------------------------------------------
* strnlen_s.c
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_str_constraint.h"
#include "safe_str_lib.h"
/**
* NAME
* strnlen_s
*
* SYNOPSIS
* #include "safe_str_lib.h"
* rsize_t
* strnlen_s(const char *dest, rsize_t dmax)
*
* DESCRIPTION
* The strnlen_s function computes the length of the string pointed
* to by dest.
*
* SPECIFIED IN
* ISO/IEC TR 24731-1, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* dest pointer to string
*
* dmax restricted maximum length (including null character).
*
* OUTPUT PARAMETERS
* none
*
* RUNTIME CONSTRAINTS
* dest shall not be a null pointer
* dmax shall not be greater than RSIZE_MAX_STR
* dmax shall not equal zero
* null character shall be in first dmax characters of dest
*
* RETURN VALUE
* The function returns the string length, excluding the terminating
* null character. If dest is NULL, then strnlen_s returns 0.
*
* Otherwise, the strnlen_s function returns the number of characters
* that precede the terminating null character.
* At most the first dmax characters of dest are accessed by strnlen_s.
*
* ALSO SEE
* strnterminate_s()
*
*/
rsize_t
strnlen_s (const char *dest, rsize_t dmax)
{
rsize_t count;
rsize_t orig_dmax = dmax;
if (dest == NULL) {
return RCNEGATE(0);
}
if (dmax == 0) {
invoke_safe_str_constraint_handler("strnlen_s: dmax is 0",
NULL, ESZEROL);
return RCNEGATE(0);
}
if (dmax > RSIZE_MAX_STR) {
invoke_safe_str_constraint_handler("strnlen_s: dmax exceeds max",
NULL, ESLEMAX);
return RCNEGATE(0);
}
count = 0;
while (*dest && dmax) {
count++;
dmax--;
dest++;
}
if (count == orig_dmax) {
invoke_safe_str_constraint_handler("strnlen_s: string length exceeds dmax",
NULL, ESLEMAX);
return RCNEGATE(0);
}
return RCNEGATE(count);
}
EXPORT_SYMBOL(strnlen_s);

323
casadm/safeclib/strtok_s.c Normal file
View File

@@ -0,0 +1,323 @@
/*------------------------------------------------------------------
* strtok_s.c
*
* October 2008, Bo Berry
*
* Copyright (c) 2008-2011 by Cisco Systems, Inc
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*------------------------------------------------------------------
*/
#include "safeclib_private.h"
#include "safe_str_constraint.h"
#include "safe_str_lib.h"
/**
* NAME
* strtok_s
*
* SYNOPSIS
* #include "safe_str_lib.h"
* char *
* strtok_s(char *dest, rsize_t *dmax, char *src, char **ptr)
*
* DESCRIPTION
* A sequence of calls to the strtok_s function breaks the string
* pointed to by dest into a sequence of tokens, each of which is
* delimited by a character from the string pointed to by src. The
* fourth argument points to a caller-provided char pointer into
* which the strtok_s function stores information necessary for
* it to continue scanning the same string.
*
* The first call in a sequence has a non-null first argument and
* dmax points to an object whose value is the number of elements
* in the character array pointed to by the first argument. The
* first call stores an initial value in the object pointed to by
* ptr and updates the value pointed to by dmax to reflect the
* number of elements that remain in relation to ptr. Subsequent
* calls in the sequence have a null first argument and the objects
* pointed to by dmax and ptr are required to have the values
* stored by the previous call in the sequence, which are then
* updated. The separator string pointed to by src may be different
* from call to call.
*
* The first call in the sequence searches the string pointed to
* by dest for the first character that is not contained in the
* current separator string pointed to by src. If no such character
* is found, then there are no tokens in the string pointed to
* by dest and the strtok_s function returns a null pointer. If
* such a character is found, it is the start of the first token.
*
* The strtok_s function then searches from there for the first
* character in dest that is contained in the current separator
* string. If no such character is found, the current token
* extends to the end of the string pointed to by dest, and
* subsequent searches in the same string for a token return
* a null pointer. If such a character is found, it is
* overwritten by a null character, which terminates the
* current token.
*
* In all cases, the strtok_s function stores sufficient information
* in the pointer pointed to by ptr so that subsequent calls,
* with a null pointer for dest and the unmodified pointer value
* for ptr, shall start searching just past the element overwritten
* by a null character (if any).
*
* SPECIFIED IN
* ISO/IEC TR 24731-1, Programming languages, environments
* and system software interfaces, Extensions to the C Library,
* Part I: Bounds-checking interfaces
*
* INPUT PARAMETERS
* dest pointer to string to tokenize
*
* dmax restricted maximum length of dest string
*
* src pointer to delimiter string (len < 255)
*
* ptr returned pointer to token
*
* OUTPUT PARAMETERS
* dmax update length
*
* ptr update pointer to token
*
* RUNTIME CONSTRAINTS
* src shall not be a null pointer.
* ptr shall not be a null pointer.
* dmax shall not be a null pointer.
* *dmax shall not be 0.
*
* If dest is a null pointer, then *ptr shall not be a null pointer.
*
* dest must not be unterminated.
*
* The value of *dmax shall not be greater than RSIZE_MAX_STR. The
* end of the token found shall occur within the first *dmax
* characters of dest for the first call, and shall occur within
* the first *dmax characters of where searching resumes on
* subsequent calls.
*
* RETURN VALUE
* The strtok_s function returns a pointer to the first character
* of a token; or a null pointer if there is no token or there
* is a runtime-constraint violation.
*
* EOK
* ESNULLP NULL pointer
* ESZEROL zero length
* ESLEMAX length exceeds max limit
* ESUNTERM unterminated string
*
* EXAMPLES
* [1] Sequencial strtok_s() calls to tokenize a string
*
* String to tokenize str1 = ",.:*one,two;three,;four*.*.five-six***"
* len=38
* String of delimiters str2 = ",.;*"
*
* p2tok = strtok_s(str1, &len, str2, &p2str);
* token -one- remaining -two;three,;four*.*.five-six***- len=30
*
* p2tok = strtok_s(NULL, &len, str2, &p2str);
* token -two- remaining -three,;four*.*.five-six***- len=26
*
* p2tok = strtok_s(NULL, &len, str2, &p2str);
* token -three- remaining -;four*.*.five-six***- len=20
*
* p2tok = strtok_s(NULL, &len, str2, &p2str);
* token -four- remaining -.*.five-six***- len=14
*
* p2tok = strtok_s(NULL, &len, str2, &p2str);
* token -five-six- remaining -**- len=2
*
* p2tok = strtok_s(NULL, &len, str2, &p2str);
* token -(null)- remaining -**- len=0
*
*
* [2] While loop with same entry data as [1]
*
* p2tok = str1;
* while (p2tok && len) {
* p2tok = strtok_s(NULL, &len, str2, &p2str);
* printf(" token -- remaining -- len=0 \n",
* p2tok, p2str, (int)len );
* }
*
*-
*/
char *
strtok_s(char *dest, rsize_t *dmax, const char *src, char **ptr)
{
/*
* CONFIGURE: The spec does not call out a maximum for the src
* string, so one is defined here.
*/
#define STRTOK_DELIM_MAX_LEN ( 16 )
const char *pt;
char *ptoken;
rsize_t dlen;
rsize_t slen;
if (dmax == NULL) {
invoke_safe_str_constraint_handler("strtok_s: dmax is NULL",
NULL, ESNULLP);
return (NULL);
}
if (*dmax == 0) {
invoke_safe_str_constraint_handler("strtok_s: dmax is 0",
NULL, ESZEROL);
return (NULL);
}
if (*dmax > RSIZE_MAX_STR) {
invoke_safe_str_constraint_handler("strtok_s: dmax exceeds max",
NULL, ESLEMAX);
return (NULL);
}
if (src == NULL) {
invoke_safe_str_constraint_handler("strtok_s: src is null",
NULL, ESNULLP);
return (NULL);
}
if (ptr == NULL) {
invoke_safe_str_constraint_handler("strtok_s: ptr is null",
NULL, ESNULLP);
return (NULL);
}
/* if the source was NULL, use the tokenizer context */
if (dest == NULL) {
dest = *ptr;
}
/*
* scan dest for a delimiter
*/
dlen = *dmax;
ptoken = NULL;
while (*dest != '\0' && !ptoken) {
if (dlen == 0) {
*ptr = NULL;
invoke_safe_str_constraint_handler(
"strtok_s: dest is unterminated",
NULL, ESUNTERM);
return (NULL);
}
/*
* must scan the entire delimiter list
* ISO should have included a delimiter string limit!!
*/
slen = STRTOK_DELIM_MAX_LEN;
pt = src;
while (*pt != '\0') {
if (slen == 0) {
*ptr = NULL;
invoke_safe_str_constraint_handler(
"strtok_s: src is unterminated",
NULL, ESUNTERM);
return (NULL);
}
slen--;
if (*dest == *pt) {
ptoken = NULL;
break;
} else {
pt++;
ptoken = dest;
}
}
dest++;
dlen--;
}
/*
* if the beginning of a token was not found, then no
* need to continue the scan.
*/
if (ptoken == NULL) {
*dmax = dlen;
return (ptoken);
}
/*
* Now we need to locate the end of the token
*/
while (*dest != '\0') {
if (dlen == 0) {
*ptr = NULL;
invoke_safe_str_constraint_handler(
"strtok_s: dest is unterminated",
NULL, ESUNTERM);
return (NULL);
}
slen = STRTOK_DELIM_MAX_LEN;
pt = src;
while (*pt != '\0') {
if (slen == 0) {
*ptr = NULL;
invoke_safe_str_constraint_handler(
"strtok_s: src is unterminated",
NULL, ESUNTERM);
return (NULL);
}
slen--;
if (*dest == *pt) {
/*
* found a delimiter, set to null
* and return context ptr to next char
*/
*dest = '\0';
*ptr = (dest + 1); /* return pointer for next scan */
*dmax = dlen - 1; /* account for the nulled delimiter */
return (ptoken);
} else {
/*
* simply scanning through the delimiter string
*/
pt++;
}
}
dest++;
dlen--;
}
*dmax = dlen;
return (ptoken);
}

1308
casadm/statistics_model.c Normal file

File diff suppressed because it is too large Load Diff

117
casadm/statistics_view.c Normal file
View File

@@ -0,0 +1,117 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdlib.h>
#include <stdio.h>
#include "cas_lib.h"
#include "csvparse.h"
#include "string.h"
#include "statistics_view.h"
#include "statistics_view_structs.h"
#include "statistics_view_text.h"
#include "statistics_view_csv.h"
#include "statistics_view_raw_csv.h"
static struct view_t *construct_view(int format, FILE *outfile)
{
struct view_t *out = calloc(1, sizeof(*out));
if (!out) {
return NULL;
}
switch (format) {
case CSV:
out->process_row = csv_process_row;
out->end_input = csv_end_input;
out->construct = csv_construct;
out->destruct = csv_destruct;
break;
case RAW_CSV:
out->process_row = raw_csv_process_row;
out->end_input = raw_csv_end_input;
out->construct = raw_csv_construct;
out->destruct = raw_csv_destruct;
break;
case TEXT:
out->process_row = text_process_row;
out->end_input = text_end_input;
out->construct = text_construct;
out->destruct = text_destruct;
break;
}
out->outfile = outfile;
out->construct(out);
return out;
};
void destruct_view(struct view_t* v)
{
v->destruct(v);
free(v);
}
#define RECOGNIZE_TYPE(t) if (!strcmp(cols[0], TAG_NAME(t))) {type = t;}
int stat_print_intermediate(FILE *infile, FILE *outfile)
{
char buf[MAX_STR_LEN] = { 0 };
while (fgets(buf, MAX_STR_LEN, infile)) {
fprintf(outfile, "%s", buf);
}
return 0;
}
int stat_format_output(FILE *infile, FILE *outfile, int format)
{
int result = 0;
if (format == PLAIN) {
return stat_print_intermediate(infile, outfile);
}
struct view_t *view = construct_view(format, outfile);
if (!view) {
cas_printf(LOG_ERR, "Failed to allocate memory for output generator\n");
return 1;
}
CSVFILE *cf = csv_fopen(infile);
if (!cf) {
cas_printf(LOG_ERR, "Failed to allocate memory for CSV parser\n");
destruct_view(view);
return 1;
}
while (!csv_read(cf)) {
int num_cols = csv_count_cols(cf);
char **cols = csv_get_col_ptr(cf);
int type = UNDEFINED_TAG;
if (num_cols<1) {
continue;
}
RECOGNIZE_TYPE(FREEFORM);
RECOGNIZE_TYPE(KV_PAIR);
RECOGNIZE_TYPE(TABLE_ROW);
RECOGNIZE_TYPE(TABLE_HEADER);
RECOGNIZE_TYPE(TABLE_SECTION);
RECOGNIZE_TYPE(TREE_HEADER);
RECOGNIZE_TYPE(TREE_BRANCH);
RECOGNIZE_TYPE(TREE_LEAF);
RECOGNIZE_TYPE(RECORD);
RECOGNIZE_TYPE(DATA_SET);
if (type == UNDEFINED_TAG) {
cas_printf(LOG_ERR, "Unrecognized tag: %s\n", cols[0]);
result = 1;
break;
}
if (view->process_row(view, type, num_cols-1, cols+1)) {
cas_printf(LOG_ERR, "Failed to process row starting with: %s\n", cols[0]);
result = 1;
break;
}
}
view->end_input(view);
csv_close_nu(cf);
destruct_view(view);
return result;
}

79
casadm/statistics_view.h Normal file
View File

@@ -0,0 +1,79 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __STAT_VIEW
#define __STAT_VIEW
#include <stdio.h>
/* each line of statistics may be assigned one fo these semantic formats,
* to which it will be converted */
enum tag_type {
FREEFORM, /**< free form text */
KV_PAIR, /**< key value pair. sequence of kv-pairs will be aligned to
columns, but no table-styleborders will be drawn */
TABLE_ROW, /**< regular table row */
TABLE_HEADER, /**< table header */
TABLE_SECTION, /**< first row of a table section */
DATA_SET, /**< set of records */
RECORD, /**< one record of data */
TREE_HEADER,
TREE_BRANCH,
TREE_LEAF,
UNDEFINED_TAG /**< occurence of this (or anything else out of
above tags) will immediately break processing */
};
#define TAG(x) #x ","
#define TAG_NAME(x) #x
enum format {
TEXT, /**< output in text (formatted tables) form */
CSV, /**< output in csv form */
RAW_CSV, /**< csv form without transformations */
PLAIN /**<debug setting: print intermediate format */
};
/**
* @param infile - file in statistics_view intermediate format
* @param outfile - file to which statistics need to be printed
* @param format - desired format of an output.
*/
int stat_format_output(FILE *infile, FILE *outfile, int format);
/*
* EXAMPLE OF AN INTERMEDIATE FORMAT:
*
* DATA_SET,
* RECORD,
* KV_PAIR,Cache Id, 1
* KV_PAIR,Cache Size, 5425999, [4KiB Blocks], 20.70, [GiB]
* KV_PAIR,Cache Occupancy, 1340, [4KiB Blocks], 0.01, [GiB], 0.02, [%]
* KV_PAIR,Metadata end offset, 79025
* KV_PAIR,Dirty cache lines, 0
* KV_PAIR,Clean cache lines, 1340
* KV_PAIR,Cache Device, /dev/sdb
* KV_PAIR,Core Devices, 15
* KV_PAIR,Write Policy, wt
* KV_PAIR,Eviction Policy, lru
* KV_PAIR,Cleaning Policy, alru
* KV_PAIR,Metadata Variant, "max (Maximum Performance, default)"
* KV_PAIR,Metadata Memory Footprint, 345.4, [MiB]
* KV_PAIR,Status, Running
* TABLE_HEADER,Request statistics,Count,%
* TABLE_SECTION,"Read hits", 180, 11.6
* TABLE_ROW,"Read partial misses", 1, 0.1
* TABLE_ROW,"Read full misses", 1370, 88.3
* TABLE_ROW,"Read total", 1551, 100.0
* TABLE_SECTION,"Write hits", 0, 0.0
* TABLE_ROW,"Write partial misses", 0, 0.0
* TABLE_ROW,"Write full misses", 0, 0.0
* TABLE_ROW,"Write total", 0, 0.0
*
* In each of output formats, first CSV column (referred to as a "tag")
* will be removed from output and used by formatter thread as a hint
*/
#endif /*__STAT_VIEW */

View File

@@ -0,0 +1,320 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "statistics_view.h"
#include "statistics_view_structs.h"
#include "statistics_view_csv.h"
#define VALS_BUFFER_INIT_SIZE 10
/**
* private data of CSV output formatter
*/
struct csv_out_prv {
int data_set; /* current data set number */
int record; /* current record number */
int column; /* current column number */
char **vals;
char **titles;
int max_vals;
int cur_val;
int max_titles;
int cur_title;
};
static inline int csv_is_first_record(struct view_t *this)
{
return 1 == this->ctx.csv_prv->record;
}
static inline int csv_is_unit_string(const char *s)
{
return NULL != s && '[' == s[0];
}
static void csv_output_column(struct view_t *this, const char *s)
{
struct csv_out_prv *prv = this->ctx.csv_prv;
if (prv->column) {
putc(',', this->outfile);
}
if (strstr(s, ",")) {
fprintf(this->outfile, "\"%s\"", s);
} else {
fprintf(this->outfile, "%s", s);
}
prv->column++;
}
static char **csv_check_container(char **container, int *max_vals,
int cur_val)
{
if (!container) {
*max_vals = VALS_BUFFER_INIT_SIZE;
container = calloc(sizeof(char *), *max_vals);
if (!container) {
return NULL;
}
}
/* Resize val pointers array if needed */
if (*max_vals < cur_val) {
*max_vals = *max_vals * 2;
if (*max_vals < cur_val) {
*max_vals = cur_val;
}
container = realloc(container, sizeof(char *) * (*max_vals));
if (!container) {
return NULL;
}
}
return container;
}
static int csv_output_data(struct view_t *this, const char *s)
{
struct csv_out_prv *prv = this->ctx.csv_prv;
if (csv_is_first_record(this)) {
prv->vals = csv_check_container(prv->vals, &prv->max_vals,
prv->cur_val+1);
if (!prv->vals) {
return 1;
}
/* Store value */
prv->vals[prv->cur_val] = strdup(s);
if (!prv->vals[prv->cur_val]) {
return 1;
}
prv->cur_val++;
} else {
csv_output_column(this, s);
}
return 0;
}
static int csv_add_column_subtitle(struct view_t *this, const char *s)
{
struct csv_out_prv *prv = this->ctx.csv_prv;
prv->titles = csv_check_container(prv->titles, &prv->max_titles,
prv->cur_title+1);
if (!prv->titles) {
return 1;
}
/* Store value */
prv->titles[prv->cur_title] = strdup(s);
if (!prv->titles[prv->cur_title]) {
return 1;
}
prv->cur_title++;
return 0;
}
static void csv_output_header(struct view_t *this, const char *title,
const char *unit)
{
static char buff[64];
if (unit) {
if (csv_is_unit_string(unit)) {
snprintf(buff, sizeof(buff), "%s %s", title, unit);
} else {
snprintf(buff, sizeof(buff), "%s [%s]", title, unit);
}
csv_output_column(this, buff);
} else {
csv_output_column(this, title);
}
}
static void csv_finish_record(struct view_t *this)
{
struct csv_out_prv *prv = this->ctx.csv_prv;
int i;
if (prv->column) {
putc('\n', this->outfile);
}
/*
* For first record we need to output stored data values
*/
if (csv_is_first_record(this)) {
prv->column = 0;
for (i = 0; i < prv->cur_val; ++i) {
csv_output_column(this, prv->vals[i]);
}
if (prv->column) {
putc('\n', this->outfile);
}
}
fflush(this->outfile);
}
static void csv_free_vals(struct view_t *this)
{
struct csv_out_prv *prv = this->ctx.csv_prv;
int i;
if (prv->vals) {
for (i = 0; i < prv->cur_val; ++i) {
free(prv->vals[i]);
}
free(prv->vals);
prv->vals = NULL;
prv->cur_val = 0;
prv->max_vals = 0;
}
}
static void csv_free_titles(struct view_t *this)
{
struct csv_out_prv *prv = this->ctx.csv_prv;
int i;
if (prv->titles) {
for (i = 0; i < prv->cur_title; ++i) {
free(prv->titles[i]);
}
free(prv->titles);
prv->titles = NULL;
prv->cur_title = 0;
prv->max_titles = 0;
}
}
int csv_process_row(struct view_t *this, int type, int num_fields, char *fields[])
{
int i;
struct csv_out_prv *prv = this->ctx.csv_prv;
const char *unit = NULL;
switch (type) {
case DATA_SET:
if (prv->record) {
csv_finish_record(this);
}
csv_free_titles(this);
csv_free_vals(this);
if (prv->data_set) {
putc('\n', this->outfile);
}
if (num_fields > 0) {
fprintf(this->outfile, "%s\n", fields[0]);
}
prv->record = 0;
prv->data_set++;
break;
case RECORD:
if (prv->record) {
csv_finish_record(this);
}
prv->column = 0;
prv->record++;
break;
/*
* For KV pair assume that values are interleaved
* with units, so output every second value,
* and use units to construct column headers.
* For example:
* KV_PAIR,Cache Size,10347970,[4KiB blocks],39.47,[GiB]
* will result in:
* data row: 10347970,39.47
* header row: Cache Size [4KiB blocks],Cache Size [GiB]
*/
case KV_PAIR:
for (i = 1; i < num_fields; i += 2) {
if (csv_is_first_record(this)) {
if (i + 1 < num_fields) {
csv_output_header(this, fields[0],
fields[i+1]);
} else {
csv_output_header(this, fields[0], NULL);
}
}
if (csv_output_data(this, fields[i])) {
return 1;
}
}
break;
/*
* For table rows assume the following format:
* TABLE_{ROW,SECTION},Title,value1,value2,value3,...,unit
* This will result in:
* data row: value1,value2,value3,...
* header row: Title [unit],Title [col1_title],Title [col2_title],...
*/
case TABLE_HEADER:
csv_free_titles(this);
csv_add_column_subtitle(this, "");
for (i = 2; i < num_fields; i++) {
if (csv_add_column_subtitle(this, fields[i])) {
return 1;
}
}
break;
case TABLE_SECTION:
case TABLE_ROW:
if (csv_is_first_record(this)) {
unit = NULL;
if (csv_is_unit_string(fields[num_fields-1])) {
unit = fields[num_fields-1];
}
csv_output_header(this, fields[0], unit);
for (i = 2; i < num_fields; i++) {
if (!csv_is_unit_string(prv->titles[i-1])) {
csv_output_header(this, fields[0],
prv->titles[i-1]);
}
}
}
for (i = 1; i < num_fields; i++) {
if (!csv_is_unit_string(prv->titles[i-1])) {
if (csv_output_data(this, fields[i])) {
return 1;
}
}
}
break;
}
return 0;
}
int csv_end_input(struct view_t *this)
{
csv_finish_record(this);
return 0;
}
int csv_construct(struct view_t *this)
{
struct csv_out_prv *prv = calloc(sizeof(struct csv_out_prv), 1);
if (!prv) {
return 1;
}
this->ctx.csv_prv = prv;
return 0;
}
int csv_destruct(struct view_t *this)
{
csv_free_vals(this);
csv_free_titles(this);
free(this->ctx.csv_prv);
return 0;
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __STATS_VIEW_CSV
#define __STATS_VIEW_CSV
int csv_process_row(struct view_t *this, int type, int num_fields, char *fields[]);
int csv_end_input(struct view_t *this);
int csv_construct(struct view_t *this);
int csv_destruct(struct view_t *this);
#endif

View File

@@ -0,0 +1,49 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "statistics_view.h"
#include "statistics_view_structs.h"
#include "statistics_view_raw_csv.h"
#define VALS_BUFFER_INIT_SIZE 10
int raw_csv_process_row(struct view_t *this, int type, int num_fields, char *fields[])
{
int i;
if (RECORD != type && DATA_SET != type) {
for (i = 0; i < num_fields; i++) {
if (i) {
fputc(',', this->outfile);
}
if (strstr(fields[i], ",")) {
fprintf(this->outfile, "\"%s\"", fields[i]);
} else {
fprintf(this->outfile, "%s", fields[i]);
}
}
fputc('\n', this->outfile);
}
return 0;
}
int raw_csv_end_input(struct view_t *this)
{
return 0;
}
int raw_csv_construct(struct view_t *this)
{
return 0;
}
int raw_csv_destruct(struct view_t *this)
{
return 0;
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __STATS_VIEW_RAW_CSV
#define __STATS_VIEW_RAW_CSV
int raw_csv_process_row(struct view_t *this, int type, int num_fields, char *fields[]);
int raw_csv_end_input(struct view_t *this);
int raw_csv_construct(struct view_t *this);
int raw_csv_destruct(struct view_t *this);
#endif

View File

@@ -0,0 +1,29 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __STATS_VIEW_S_H
#define __STATS_VIEW_S_H
struct csv_out_prv;
struct text_out_prv;
struct view_t
{
FILE *outfile;
union {
struct csv_out_prv *csv_prv;
struct text_out_prv *text_prv;
} ctx;
/* type specific init */
int (*construct)(struct view_t *this);
int (*process_row)(struct view_t *this, int type, int num_fields, char *fields[]);
int (*end_input)(struct view_t *this);
int (*destruct)(struct view_t *this);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __STATS_VIEW_TEXT
#define __STATS_VIEW_TEXT
int text_process_row(struct view_t *this, int type, int num_fields, char *fields[]);
int text_end_input(struct view_t *this);
int text_construct(struct view_t *this);
int text_destruct(struct view_t *this);
#endif

231
casadm/table.c Normal file
View File

@@ -0,0 +1,231 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "table.h"
#include "safeclib/safe_str_lib.h"
#include <cas_ioctl_codes.h>
#define MIN_STR_SIZE 64
struct table_row
{
int width;
int max_width;
char **cells;
};
struct table
{
int width;
int height;
int max_height;
struct table_row *r;
};
struct table *table_alloc()
{
struct table *out = malloc(sizeof(*out));
if (!out) {
return NULL;
}
out->width = 0;
out->height = 0;
out->max_height = 0;
out->r = 0;
return out;
}
void table_free(struct table *t)
{
int i, j;
if (t->r) {
for (i = 0 ; i!= t->max_height; ++i) {
if (t->r[i].cells) {
for (j = 0 ; j!= t->r[i].max_width; ++j) {
if (t->r[i].cells[j]) {
free(t->r[i].cells[j]);
}
}
free(t->r[i].cells);
}
}
free(t->r);
}
free(t);
}
int table_reset(struct table *t)
{
int i,j;
if (t->r) {
for (i = 0 ; i!= t->max_height; ++i) {
if (t->r[i].cells) {
for (j = 0 ; j!= t->r[i].max_width; ++j) {
if (t->r[i].cells[j]) {
(t->r[i].cells[j])[0] = 0;
}
}
}
t->r[i].width = 0;
}
}
t->width = 0;
t->height = 0;
return 0;
}
int maxi(int x, int y)
{
if (x > y) {
return x;
} else {
return y;
}
}
char *table_get(struct table *t,int y, int x)
{
static const char * empty="";
if (y >= t->height || x >= t->width) {
assert(0);
abort();
return (char*)empty;
}
/* within assigned boundaries but without allocated boundaries */
if (y >= t->max_height) {
return (char*)empty;
}
if (x >= t->r[y].max_width) {
return (char*)empty;
}
if (!t->r[y].cells) {
return (char*)empty;
}
if (!t->r[y].cells[x]) {
return (char*)empty;
}
return t->r[y].cells[x];
}
int table_set(struct table *t, int y, int x, char *c)
{
int i;
int len = strnlen(c, MAX_STR_LEN);
if (len >= MAX_STR_LEN) {
return 1;
}
/* step 1: ensure that space for row y is allocated */
if (!t->r) {
t->r = calloc(sizeof(struct table_row), y + 1);
if (!t->r) {
return 1;
}
t->max_height = y + 1;
} else if (t->max_height <= y) {
struct table_row *tmp;
int new_m_h = t->max_height*2;
if (new_m_h <= y) {
new_m_h = y+1;
}
tmp = realloc(t->r, sizeof(struct table_row)*new_m_h);
if (!tmp) {
return 1;
}
t->r=tmp;
for (i = t->max_height; i!= new_m_h; ++i) {
t->r[i].width = t->r[i].max_width = 0;
t->r[i].cells = 0;
}
t->max_height = new_m_h;
} /* else everything is OK */
/* step 2: ensure that column x within row y is allocated */
if (!t->r[y].cells) {
t->r[y].cells = calloc(sizeof(char*), x + 1);
t->r[y].max_width = x + 1;
} else if (t->r[y].max_width <= x) {
char **tmp;
int new_m_w = t->r[y].max_width*2;
if (new_m_w <= x) {
new_m_w = x+1;
}
tmp = realloc(t->r[y].cells, sizeof(char*)*new_m_w);
if (!tmp) {
return 1;
}
t->r[y].cells = tmp;
memset(&tmp[t->r[y].max_width], 0,
sizeof(char*)*(new_m_w-t->r[y].max_width));
t->r[y].max_width = new_m_w;
}
/* step 3: allocate space for string to be contained in cell */
if (t->r[y].cells[x] && len+1>MIN_STR_SIZE) {
char *tmp = realloc(t->r[y].cells[x], len+1);
if (!tmp) {
return 1;
}
t->r[y].cells[x] = tmp;
} else if (!t->r[y].cells[x]){
t->r[y].cells[x] = malloc(maxi(MIN_STR_SIZE,len+1));
if (!t->r[y].cells[x]) {
return 1;
}
}
/* step 4: actually overwrite contents of a cell */
strncpy_s(t->r[y].cells[x], len + 1, c, len);
/* step 5: update width and height of a table */
t->height = maxi(t->height, y + 1);
t->width = maxi(t->width, x + 1);
t->r[y].width = maxi(t->r[y].width, x + 1);
return 0;
}
/**
* get last available row of table that was added either via
*/
int table_get_width(struct table *t)
{
return t->width;
}
int table_get_height(struct table *t)
{
return t->height;
}
int table_set_height(struct table *t, int h)
{
t->height = h;
return 0;
}
int table_set_width(struct table *t, int h)
{
t->width = h;
return 0;
}

58
casadm/table.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __TABLE_H
#define __TABLE_H
struct table;
/**
* setup "table" structure.
*/
struct table *table_alloc();
/**
* deallocate table.
*/
void table_free(struct table *t);
/**
* max value of two integers
*/
int maxi(int x, int y);
/**
* retrieve a field of a table
*/
char *table_get(struct table *t,int y, int x);
int table_set(struct table *t, int y, int x, char *c);
/**
* reduce number of columns and rows to 0;
*/
int table_reset(struct table *t);
/**
* get last available column of table that was added via table_set
*/
int table_get_width(struct table *t);
/**
* get last available row of table that was added either via table_set or table_set_height
*/
int table_get_height(struct table *t);
/**
* set height of a table (additional rows will contain empty strings
*/
int table_set_height(struct table *t, int h);
/**
* set with of a table (additional rows will contain empty strings
*/
int table_set_width(struct table *t, int h);
#endif

40
casadm/upgrade.c Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "cas_lib.h"
#include "cas_lib_utils.h"
#include <cas_ioctl_codes.h>
extern cas_printf_t cas_printf;
int upgrade_start()
{
int fd;
struct kcas_upgrade cmd_info;
if ((fd = open_ctrl_device()) == -1) {
return -1;
}
if (run_ioctl_interruptible(fd, KCAS_IOCTL_UPGRADE, &cmd_info,
"Starting upgrade", 0, OCF_CORE_ID_INVALID) < 0) {
close(fd);
if (OCF_ERR_FLUSHING_INTERRUPTED == cmd_info.ext_err_code) {
return INTERRUPTED;
} else {
cas_printf(LOG_ERR, "Error starting upgrade\n");
print_err(cmd_info.ext_err_code);
return FAILURE;
}
}
close(fd);
return SUCCESS;
}

11
casadm/upgrade.h Normal file
View File

@@ -0,0 +1,11 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef _UPGRADE_H
#define _UPGRADE_H
int upgrade_start();
#endif

119
casadm/vt100codes.h Normal file
View File

@@ -0,0 +1,119 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
/* General setup */
#define RESET_DEVICE "\033c"
//! enable line wrapping
#define ENABLE_LINE_WRAP "\x1b[7h"
//! disable it
#define DISABLE_LINE_WRAP "\x1b[7l"
/* Scrolling options. Note: there is no way to disable scrolling */
//! Whole screen is scrolled on SCROLL_UP/SCROLL_DOWN
#define SCROLL_ENTIRE_SCREEN "\x1b[r"
//! Only rows from A to B are scrolled on SCROLL_UP/SCROLL_DOWN, anything above A or below B is not scrolled
#define SCROLL_SCREEN_REGION(A,B) "\x1b[" (A) ";" (B) "r"
//! scroll up
#define SCROLL_UP "\x1b[M"
//! scroll down
#define SCROLL_DOWN "\x1b[D"
//! make cursor invisible - xterm
#define HIDE_CURSOR "\x1b[?25l"
//! restore it -xterm
#define SHOW_CURSOR "\x1b[?25h"
/* Absolute cursor positioning. */
//! Set cursor position to left-top position
#define CURSOR_HOME "\x1b[H"
//! Set cursor position to specific y/x (note: y = 1..height, x = 1..width)
#define CURSOR_YX "\x1b[%d;%dH"
/* Relative cursor positioning. */
//! move cursor one position up
#define CURSOR_UP "\x1b[A"
//! move cursor n positions up
#define CURSOR_UP_N "\x1b[%dA"
//! move cursor one position down
#define CURSOR_DOWN "\x1b[B"
//! move cursor n positions down
#define CURSOR_DOWN_N "\x1b[%dB"
//! move cursor one position forward
#define CURSOR_FORWARD "\x1b[C"
//! move cursor n positions forward
#define CURSOR_FORWARD_N "\x1b[%dC"
//! move cursor one position backward
#define CURSOR_BACKWARD "\x1b[D"
//! move cursor n positions backward
#define CURSOR_BACKWARD_N "\x1b[%dD"
/* Unsave restores position after last save. */
//! One cursor position may be saved
#define SAVE_CURSOR "\x1b[s"
//! and restored
#define UNSAVE_CURSOR "\x1b[u"
/* Erase screen. */
//! Erase whole screen
#define ERASE "\x1b[2J"
//! same as above
#define ERASE_SCREEN ERASE
//! erase above cursor
#define ERASE_UP "\x1b[1J"
//! erase below cursor
#define ERASE_DOWN "\x1b[J"
#define INSERT_MODE "\x1b[4h"
#define REPLACE_MODE "\x1b[4l"
/* Erase line. */
//! erase current line
#define ERASE_LINE "\x1b[K"
//! erase current line left from the cursor
#define ERASE_START_OF_LINE "\x1b[1K"
//! erase current line right from the cursor
#define ERASE_END_OF_LINE "\x1b[K"
/* a = one of following 23 attributes*/
//! set specific attribute
#define SET_ATTR "\x1b[%dm"
//! if you have to set more attributes, separate them by ";"
#define AND_ATTR ";"
/*generalattributes (0-8 without 3 and 6) */
//!resets terminal defaults
#define ATTR_RESET 0
//!sets brighter fg color
#define ATTR_BRIGHT 1
//!turns off bright (sets darker fg color) note: not supported by most of platforms
#define ATTR_DIM 2
//!turns on text underline (not supported by MS Windows)
#define ATTR_UNDERSCORE 4
//!turns on blink (Not supported by MS Windows, most of other implementations incompatible)
#define ATTR_BLINK 5
//! Inverts bg and fg color (incompatible implementation on MS windows)*/
#define ATTR_REVERSE 7
#define ATTR_HIDDEN 8 /*???*/
/*Foreground (text) colours*/
#define FG_COLOR_BLACK 30
#define FG_COLOR_RED 31
#define FG_COLOR_GREEN 32
#define FG_COLOR_YELLOW 33
#define FG_COLOR_BLUE 34
#define FG_COLOR_MAGENTA 35
#define FG_COLOR_CYAN 36
#define FG_COLOR_WHITE 37
/*Background colors*/
#define BG_COLOR_BLACK 40
#define BG_COLOR_RED 41
#define BG_COLOR_GREEN 42
#define BG_COLOR_YELLOW 43
#define BG_COLOR_BLUE 44
#define BG_COLOR_MAGENTA 45
#define BG_COLOR_CYAN 46
#define BG_COLOR_WHITE 47

51
modules/CAS_VERSION_GEN Executable file
View File

@@ -0,0 +1,51 @@
#!/bin/bash
#
# Copyright(c) 2012-2019 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
VER_FILE=CAS_VERSION
which git > /dev/null 2>&1
if [ $? -eq 0 ] && [ -e ../../../.git ]; then
echo "Generating ${VER_FILE} from git revision."
echo ""
VERSION=`git describe HEAD 2>/dev/null`
CAS_VERSION_MAIN=`echo ${VERSION} | cut -d '.' -f 1 | awk '{print substr($0, 2)}'`
CAS_VERSION_MAJOR=`echo ${VERSION} | cut -d '.' -f 2 | awk '{print substr($0, 2)}'`
CAS_VERSION_MINOR=`echo ${VERSION} | cut -d '.' -f 3 | awk '{print substr($0, 2)}'`
CAS_BUILD_NO=`echo ${VERSION} | cut -d '.' -f 4 | cut -d '-' -f 1`
CAS_BUILD_FLAG=`echo ${VERSION} | cut -d '.' -f 4 | cut -s -d '-' -f 3`
rm -f ${VER_FILE}
touch ${VER_FILE}
echo "CAS_VERSION_MAIN=${CAS_VERSION_MAIN}" >> ${VER_FILE}
echo "CAS_VERSION_MAJOR=${CAS_VERSION_MAJOR}" >> ${VER_FILE}
echo "CAS_VERSION_MINOR=${CAS_VERSION_MINOR}" >> ${VER_FILE}
echo "CAS_BUILD_NO=${CAS_BUILD_NO}" >> ${VER_FILE}
echo "CAS_BUILD_FLAG=${CAS_BUILD_FLAG}" >> ${VER_FILE}
elif [ -f ${VER_FILE} ]; then
echo "Using existing ${VER_FILE} version file."
echo ""
else
echo "No ${VER_FILE} found. Preparing default version file."
echo ""
CAS_VERSION_MAIN=19
CAS_VERSION_MAJOR=3
CAS_VERSION_MINOR=0
CAS_BUILD_NO=0000`date +%m%d`
CAS_BUILD_FLAG=
touch ${VER_FILE}
echo "CAS_VERSION_MAIN=${CAS_VERSION_MAIN}" >> ${VER_FILE}
echo "CAS_VERSION_MAJOR=${CAS_VERSION_MAJOR}" >> ${VER_FILE}
echo "CAS_VERSION_MINOR=${CAS_VERSION_MINOR}" >> ${VER_FILE}
echo "CAS_BUILD_NO=${CAS_BUILD_NO}" >> ${VER_FILE}
echo "CAS_BUILD_FLAG=${CAS_BUILD_FLAG}" >> ${VER_FILE}
fi
cat ${VER_FILE}

76
modules/Makefile Normal file
View File

@@ -0,0 +1,76 @@
#
# Copyright(c) 2012-2019 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
include $(M)/config.mk
obj-y += cas_cache/
obj-y += cas_disk/
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
VERSION_FILE=$(PWD)/CAS_VERSION
OCFDIR=$(PWD)/../ocf
KERNEL_DIR ?= "/lib/modules/$(shell uname -r)/build"
PWD=$(shell pwd)
KERNEL_VERSION := $(shell uname -r)
MODULES_DIR=/lib/modules/$(KERNEL_VERSION)/extra
DISK_MODULE = cas_disk
CACHE_MODULE = cas_cache
DEPMOD:=$(shell which depmod)
RMMOD :=$(shell which rmmod)
MODPROBE:=$(shell which modprobe)
all: default
$(VERSION_FILE):
./CAS_VERSION_GEN
# Extra targets and file configuration
ifneq ($(wildcard $(PWD)/extra.mk),)
include $(PWD)/extra.mk
else
sync distsync:
endif
default: $(VERSION_FILE) sync
cd $(KERNEL_DIR) && $(MAKE) M=$(PWD) modules
clean:
cd $(KERNEL_DIR) && make M=$(PWD) clean
distclean: clean distsync
install:
@echo "Installing Open-CAS modules"
@install -m 755 -d $(MODULES_DIR)
@install -m 744 cas_disk/$(DISK_MODULE).ko $(MODULES_DIR)/$(DISK_MODULE).ko
@install -m 744 cas_cache/$(CACHE_MODULE).ko $(MODULES_DIR)/$(CACHE_MODULE).ko
@$(DEPMOD)
@$(MODPROBE) $(CACHE_MODULE)
uninstall:
@echo "Uninstalling Open-CAS modules"
@$(RMMOD) $(CACHE_MODULE)
@$(RMMOD) $(DISK_MODULE)
@rm $(MODULES_DIR)/$(CACHE_MODULE).ko
@rm $(MODULES_DIR)/$(DISK_MODULE).ko
@$(DEPMOD)
reinstall: uninstall install
.PHONY: all default clean distclean sync distsync install uninstall
endif

17
modules/README Normal file
View File

@@ -0,0 +1,17 @@
Open CAS accelerates Linux applications by caching active (hot) data to
a local flash device inside servers. Open CAS implements caching at the
server level, utilizing local high-performance flash media as the cache drive
media inside the application server as close as possible to the CPU, thus
reducing storage latency as much as possible.
The Open Cache Acceleration Software installs into the GNU/Linux operating
system itself, as a kernel module. The nature of the integration provides a
cache solution that is transparent to users and applications, and your
existing storage infrastructure. No storage migration effort or application
changes are required.
Open CAS is distributed on Dual BSD-2-Clause-Patent/GPLv2 license (see
https://opensource.org/licenses/BSDplusPatent and
https://opensource.org/licenses/GPL-2.0 for for full license texts).
Open CAS uses Safe string library (safeclib) that is MIT licensed.

3
modules/cas_cache/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
include/
src/

View File

@@ -0,0 +1,10 @@
#
# Copyright(c) 2012-2019 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
include $(M)/config.mk
obj-m := cas_cache.o
cas_cache-c = $(shell find $(M)/cas_cache -name \*.c)
cas_cache-objs = $(patsubst $(M)/cas_cache/%.c,%.o,$(cas_cache-c))

View File

@@ -0,0 +1,97 @@
/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __CAS_CACHE_H__
#define __CAS_CACHE_H__
#include "ocf/ocf.h"
#include "ocf_env.h"
#include <cas_version.h>
#include <cas_ioctl_codes.h>
#include "linux_kernel_version.h"
#include "layer_upgrade.h"
#include "control.h"
#include "layer_cache_management.h"
#include "service_ui_ioctl.h"
#include "utils/cas_cache_utils.h"
#include "volume/vol_blk_utils.h"
#include "classifier.h"
#include "context.h"
#include <linux/kallsyms.h>
#define CAS_KERN_EMERG KERN_EMERG OCF_PREFIX_SHORT
#define CAS_KERN_ALERT KERN_ALERT OCF_PREFIX_SHORT
#define CAS_KERN_CRIT KERN_CRIT OCF_PREFIX_SHORT
#define CAS_KERN_ERR KERN_ERR OCF_PREFIX_SHORT
#define CAS_KERN_WARNING KERN_WARNING OCF_PREFIX_SHORT
#define CAS_KERN_NOTICE KERN_NOTICE OCF_PREFIX_SHORT
#define CAS_KERN_INFO KERN_INFO OCF_PREFIX_SHORT
#define CAS_KERN_DEBUG KERN_DEBUG OCF_PREFIX_SHORT
#ifndef SECTOR_SHIFT
#define SECTOR_SHIFT 9
#endif
#ifndef SECTOR_SIZE
#define SECTOR_SIZE (1<<SECTOR_SHIFT)
#endif
#define MAX_LINES_PER_IO 16
/**
* cache/core object types */
enum {
BLOCK_DEVICE_VOLUME = 1, /**< block device volume */
ATOMIC_DEVICE_VOLUME, /**< block device volume with atomic
metadata support */
/** \cond SKIP_IN_DOC */
OBJECT_TYPE_MAX,
NVME_CONTROLLER
/** \endcond */
};
struct cas_classifier;
struct cache_priv {
struct cas_classifier *classifier;
ocf_queue_t mngt_queue;
ocf_queue_t io_queues[];
};
extern ocf_ctx_t cas_ctx;
extern struct casdsk_functions_mapper casdisk_functions;
struct casdsk_functions_mapper {
int (*casdsk_disk_dettach)(struct casdsk_disk *dsk);
int (*casdsk_exp_obj_destroy)(struct casdsk_disk *dsk);
int (*casdsk_exp_obj_create)(struct casdsk_disk *dsk, const char *dev_name,
struct module *owner, struct casdsk_exp_obj_ops *ops);
struct request_queue *(*casdsk_disk_get_queue)(struct casdsk_disk *dsk);
void (*casdsk_store_config)(size_t n_blobs, struct casdsk_props_conf *blobs);
struct block_device *(*casdsk_disk_get_blkdev)(struct casdsk_disk *dsk);
struct request_queue *(*casdsk_exp_obj_get_queue)(struct casdsk_disk *dsk);
uint32_t (*casdsk_get_version)(void);
void (*casdsk_disk_close)(struct casdsk_disk *dsk);
struct casdsk_disk *(*casdsk_disk_claim)(const char *path, void *private);
int (*casdsk_exp_obj_unlock)(struct casdsk_disk *dsk);
int (*casdsk_disk_set_pt)(struct casdsk_disk *dsk);
size_t (*casdsk_get_stored_config)(struct casdsk_props_conf **blobs);
struct gendisk *(*casdsk_disk_get_gendisk)(struct casdsk_disk *dsk);
int (*casdsk_disk_attach) (struct casdsk_disk *dsk, struct module *owner,
struct casdsk_exp_obj_ops *ops);
int (*casdsk_disk_set_attached)(struct casdsk_disk *dsk);
int (*casdsk_exp_obj_activate)(struct casdsk_disk *dsk);
bool (*casdsk_exp_obj_activated)(struct casdsk_disk *ds);
int (*casdsk_exp_obj_lock)(struct casdsk_disk *dsk);
void (*casdsk_free_stored_config)(void);
struct casdsk_disk *(*casdsk_disk_open)(const char *path, void *private);
int (*casdsk_disk_clear_pt)(struct casdsk_disk *dsk);
struct gendisk *(*casdsk_exp_obj_get_gendisk)(struct casdsk_disk *dsk);
};
#endif

View File

@@ -0,0 +1,967 @@
/*
* Copyright(c) 2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "cas_cache.h"
#include "linux_kernel_version.h"
#include "classifier.h"
#include "classifier_defs.h"
#include <linux/namei.h>
/* Kernel log prefix */
#define CAS_CLS_LOG_PREFIX OCF_PREFIX_SHORT"[Classifier]"
/* Production version logs */
#define CAS_CLS_MSG(severity, format, ...) \
printk(severity CAS_CLS_LOG_PREFIX " " format, ##__VA_ARGS__);
/* Set to 1 to enable debug logs */
#define CAS_CLASSIFIER_CLS_DEBUG 0
#if 1 == CAS_CLASSIFIER_CLS_DEBUG
/* Debug log */
#define CAS_CLS_DEBUG_MSG(format, ...) \
CAS_CLS_MSG(KERN_INFO, format, ##__VA_ARGS__)
/* Trace log */
#define CAS_CLS_DEBUG_TRACE(format, ...) \
trace_printk(format, ##__VA_ARGS__)
#else
#define CAS_CLS_DEBUG_MSG(format, ...)
#define CAS_CLS_DEBUG_TRACE(format, ...)
#endif
/* Done condition test - always accepts and stops evaluation */
static cas_cls_eval_t _cas_cls_done_test(struct cas_classifier *cls,
struct cas_cls_condition *c, struct cas_cls_io *io,
ocf_part_id_t part_id)
{
cas_cls_eval_t ret = {.yes = 1, .stop = 1};
return ret;
}
/* Metadata condition test */
static cas_cls_eval_t _cas_cls_metadata_test(struct cas_classifier *cls,
struct cas_cls_condition *c, struct cas_cls_io *io,
ocf_part_id_t part_id)
{
if (!io->page)
return cas_cls_eval_no;
if (PageAnon(io->page))
return cas_cls_eval_no;
if (PageSlab(io->page) || PageCompound(io->page)) {
/* A filesystem issues IO on pages that does not belongs
* to the file page cache. It means that it is a
* part of metadata
*/
return cas_cls_eval_yes;
}
if (!io->page->mapping) {
/* XFS case, page are allocated internally and do not
* have references into inode
*/
return cas_cls_eval_yes;
}
if (!io->inode)
return cas_cls_eval_no;
if (S_ISBLK(io->inode->i_mode)) {
/* EXT3 and EXT4 case. Metadata IO is performed into pages
* of block device cache
*/
return cas_cls_eval_yes;
}
if (S_ISDIR(io->inode->i_mode)) {
return cas_cls_eval_yes;
}
return cas_cls_eval_no;
}
/* Direct I/O condition test function */
static cas_cls_eval_t _cas_cls_direct_test(struct cas_classifier *cls,
struct cas_cls_condition *c, struct cas_cls_io *io,
ocf_part_id_t part_id)
{
if (!io->page)
return cas_cls_eval_no;
if (PageAnon(io->page))
return cas_cls_eval_yes;
return cas_cls_eval_no;
}
/* Generic condition constructor for conditions without operands (e.g. direct,
* metadata) */
static int _cas_cls_generic_ctr(struct cas_classifier *cls,
struct cas_cls_condition *c, char *data)
{
if (data) {
CAS_CLS_MSG(KERN_ERR, "Unexpected operand in condition\n");
return -EINVAL;
}
return 0;
}
/* Generic condition destructor */
static void _cas_cls_generic_dtr(struct cas_classifier *cls,
struct cas_cls_condition *c)
{
if (c->context)
kfree(c->context);
c->context = NULL;
}
/* Numeric condition constructor. @data is expected to contain either
* plain number string or range specifier (e.g. "gt:4096"). */
static int _cas_cls_numeric_ctr(struct cas_classifier* cls,
struct cas_cls_condition *c, char *data)
{
struct cas_cls_numeric *ctx;
int result;
char *ptr;
if (!data || strlen(data) == 0) {
CAS_CLS_MSG(KERN_ERR, "Missing numeric condition operand\n");
return -EINVAL;
}
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->operator = cas_cls_numeric_eq;
ptr = strpbrk(data, ":");
if (ptr) {
/* Terminate sub-string containing arithmetic operator */
*ptr = '\0';
++ptr;
if (!strcmp(data, "eq")) {
ctx->operator = cas_cls_numeric_eq;
} else if (!strcmp(data, "ne")) {
ctx->operator = cas_cls_numeric_ne;
} else if (!strcmp(data, "lt")) {
ctx->operator = cas_cls_numeric_lt;
} else if (!strcmp(data, "gt")) {
ctx->operator = cas_cls_numeric_gt;
} else if (!strcmp(data, "le")) {
ctx->operator = cas_cls_numeric_le;
} else if (!strcmp(data, "ge")) {
ctx->operator = cas_cls_numeric_ge;
} else {
CAS_CLS_MSG(KERN_ERR, "Invalid numeric operator \n");
result = -EINVAL;
goto error;
}
} else {
/* Plain number case */
ptr = data;
}
result = kstrtou64(ptr, 10, &ctx->v_u64);
if (result) {
CAS_CLS_MSG(KERN_ERR, "Invalid numeric operand\n");
goto error;
}
CAS_CLS_DEBUG_MSG("\t\t - Using operator %d with value %llu\n",
ctx->operator, ctx->v_u64);
c->context = ctx;
return 0;
error:
kfree(ctx);
return result;
}
/* Unsigned int numeric test function */
static cas_cls_eval_t _cas_cls_numeric_test_u(
struct cas_cls_condition *c, uint64_t val)
{
struct cas_cls_numeric *ctx = c->context;
switch (ctx->operator) {
case cas_cls_numeric_eq:
return val == ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
case cas_cls_numeric_ne:
return val != ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
case cas_cls_numeric_lt:
return val < ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
case cas_cls_numeric_gt:
return val > ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
case cas_cls_numeric_le:
return val <= ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
case cas_cls_numeric_ge:
return val >= ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
}
return cas_cls_eval_no;
}
/* Io class test function */
static cas_cls_eval_t _cas_cls_io_class_test(struct cas_classifier *cls,
struct cas_cls_condition *c, struct cas_cls_io *io,
ocf_part_id_t part_id)
{
return _cas_cls_numeric_test_u(c, part_id);
}
/* File size test function */
static cas_cls_eval_t _cas_cls_file_size_test(
struct cas_classifier *cls, struct cas_cls_condition *c,
struct cas_cls_io *io, ocf_part_id_t part_id)
{
if (!io->inode)
return cas_cls_eval_no;
if (S_ISBLK(io->inode->i_mode))
return cas_cls_eval_no;
if (!S_ISREG(io->inode->i_mode))
return cas_cls_eval_no;
return _cas_cls_numeric_test_u(c, i_size_read(io->inode));
}
/* Resolve path to inode */
static void _cas_cls_directory_resolve(struct cas_classifier *cls,
struct cas_cls_directory *ctx)
{
struct path path;
struct inode *inode;
int error;
int o_res;
unsigned long o_ino;
o_res = ctx->resolved;
o_ino = ctx->i_ino;
error = kern_path(ctx->pathname, LOOKUP_FOLLOW, &path);
if (error) {
ctx->resolved = 0;
if (o_res) {
CAS_CLS_DEBUG_MSG("Removed inode resolution for %s\n",
ctx->pathname);
}
return;
}
inode = path.dentry->d_inode;
ctx->i_ino = inode->i_ino;
ctx->resolved = 1;
path_put(&path);
if (!o_res) {
CAS_CLS_DEBUG_MSG("Resolved %s to inode: %lu\n", ctx->pathname,
ctx->i_ino);
} else if (o_ino != ctx->i_ino) {
CAS_CLS_DEBUG_MSG("Changed inode resolution for %s: %lu => %lu"
"\n", ctx->pathname, o_ino, ctx->i_ino);
}
}
/* Inode resolving work entry point */
static void _cas_cls_directory_resolve_work(struct work_struct *work)
{
struct cas_cls_directory *ctx;
ctx = container_of(work, struct cas_cls_directory, d_work.work);