commit ceeda00d6db63395d8c9c2d4f289c6597577c7f0 from: Sergey Bronnikov date: Wed Jan 18 11:16:46 2023 UTC cmake: change a file layout commit - ec35c1e1623d39f20e381d9e4bb784235d520f31 commit + ceeda00d6db63395d8c9c2d4f289c6597577c7f0 blob - 222ce6a9e9c4b8d3f17726582891fd99082eca7b blob + 978f192c0f2d859bb5a8ad0b851fc070616c3cd6 --- CMakeLists.txt +++ CMakeLists.txt @@ -1,55 +1,12 @@ -cmake_minimum_required (VERSION 3.10.2) +cmake_minimum_required(VERSION 3.10.2) -include(CheckFunctionExists) - project (unreliablefs DESCRIPTION "A FUSE-based fault injection filesystem.") -if(${CMAKE_VERSION} VERSION_GREATER "3.12.0") - find_package(Python3 COMPONENTS Interpreter) - find_package(Python COMPONENTS Interpreter) -endif() - -set(PYTHON_PATH "/usr/bin/python3.6") -if(${Python3_FOUND}) - set(PYTHON_PATH ${Python3_EXECUTABLE}) -elseif(${Python_FOUND}) - set(PYTHON_PATH ${Python_EXECUTABLE}) -endif() - -set(UNRELIABLEFS_SRC conf.c - unreliablefs.c - unreliablefs_errinj.c - unreliablefs_ops.c) - set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") -find_package(FUSE 2.9 REQUIRED) -add_definitions(${FUSE_DEFINITIONS}) -add_executable(${PROJECT_NAME} ${UNRELIABLEFS_SRC}) -target_include_directories(${PROJECT_NAME} PRIVATE ${FUSE_INCLUDE_DIRS}) -target_link_libraries(${PROJECT_NAME} ${FUSE_LIBRARIES}) +add_subdirectory(unreliablefs) -target_compile_options(${PROJECT_NAME} PUBLIC -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Wno-unused-parameter) -target_compile_options(${PROJECT_NAME} PUBLIC $<$:-std=c++11 -D_FILE_OFFSET_BITS=64>) - -check_function_exists(fallocate HAVE_FALLOCATE) -check_function_exists(fallocate HAVE_FLOCK) -check_function_exists(utimensat HAVE_UTIMENSAT) -check_function_exists(setxattr HAVE_XATTR) -if (${HAVE_FALLOCATE}) - target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_FALLOCATE) -endif () -if (${HAVE_FLOCK}) - target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_FLOCK) -endif () -if (${HAVE_UTIMENSAT}) - target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_UTIMENSAT) -endif () -if (${HAVE_XATTR}) - target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_XATTR) -endif () - option(ENABLE_ASAN "Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF) if(ENABLE_ASAN) @@ -64,19 +21,7 @@ if(ENABLE_UBSAN) target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=undefined) endif() -add_subdirectory(tests) -add_custom_target(pytest - COMMAND ${PYTHON_PATH} -m pytest -c ${PROJECT_SOURCE_DIR}/tests/pytest.ini ${PROJECT_SOURCE_DIR}/tests/ - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - COMMENT "Run regression test suite") - -set(MANUAL_PAGES "unreliablefs.1;unreliablefs.conf.5") add_custom_target(check DEPENDS check-mandoc check-rockspec) -add_custom_target(check-mandoc DEPENDS ${MANUAL_PAGES}) -add_custom_command(TARGET check-mandoc - COMMAND mandoc -T lint -W warning,stop ${MANUAL_PAGES} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - COMMENT "Run mandoc static analysis") set(ROCKSPEC "unreliablefs-scm-1.rockspec") add_custom_target(check-rockspec DEPENDS ${ROCKSPEC}) @@ -85,6 +30,9 @@ add_custom_command(TARGET check-rockspec WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMENT "Build Lua rockspec") -install(TARGETS unreliablefs DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) -install(FILES "unreliablefs.1" DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) -install(FILES "unreliablefs.conf.5" DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man5) +set(MANUAL_PAGES "unreliablefs/unreliablefs.1;unreliablefs/unreliablefs.conf.5") +add_custom_target(check-mandoc DEPENDS ${MANUAL_PAGES}) +add_custom_command(TARGET check-mandoc + COMMAND mandoc -T lint -W warning,stop ${MANUAL_PAGES} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Run mandoc static analysis") blob - b3c6dbcdf02d42abf6748168bd8a8ba57122e615 (mode 644) blob + /dev/null --- conf.c +++ /dev/null @@ -1,298 +0,0 @@ -/* inih -- simple .INI file parser - -SPDX-License-Identifier: BSD-3-Clause - -Copyright (C) 2009-2020, Ben Hoyt - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include - -#include "conf.h" - -#if !INI_USE_STACK -#if INI_CUSTOM_ALLOCATOR -#include -void* ini_malloc(size_t size); -void ini_free(void* ptr); -void* ini_realloc(void* ptr, size_t size); -#else -#include -#define ini_malloc malloc -#define ini_free free -#define ini_realloc realloc -#endif -#endif - -#define MAX_SECTION 50 -#define MAX_NAME 50 - -/* Used by ini_parse_string() to keep track of string parsing state. */ -typedef struct { - const char* ptr; - size_t num_left; -} ini_parse_string_ctx; - -/* Strip whitespace chars off end of given string, in place. Return s. */ -static char* rstrip(char* s) -{ - char* p = s + strlen(s); - while (p > s && isspace((unsigned char)(*--p))) - *p = '\0'; - return s; -} - -/* Return pointer to first non-whitespace char in given string. */ -static char* lskip(const char* s) -{ - while (*s && isspace((unsigned char)(*s))) - s++; - return (char*)s; -} - -/* Return pointer to first char (of chars) or inline comment in given string, - or pointer to NUL at end of string if neither found. Inline comment must - be prefixed by a whitespace character to register as a comment. */ -static char* find_chars_or_comment(const char* s, const char* chars) -{ -#if INI_ALLOW_INLINE_COMMENTS - int was_space = 0; - while (*s && (!chars || !strchr(chars, *s)) && - !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { - was_space = isspace((unsigned char)(*s)); - s++; - } -#else - while (*s && (!chars || !strchr(chars, *s))) { - s++; - } -#endif - return (char*)s; -} - -/* Similar to strncpy, but ensures dest (size bytes) is - NUL-terminated, and doesn't pad with NULs. */ -static char* strncpy0(char* dest, const char* src, size_t size) -{ - /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ - size_t i; - for (i = 0; i < size - 1 && src[i]; i++) - dest[i] = src[i]; - dest[i] = '\0'; - return dest; -} - -/* See documentation in header file. */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user) -{ - /* Uses a fair bit of stack (use heap instead if you need to) */ -#if INI_USE_STACK - char line[INI_MAX_LINE]; - int max_line = INI_MAX_LINE; -#else - char* line; - size_t max_line = INI_INITIAL_ALLOC; -#endif -#if INI_ALLOW_REALLOC && !INI_USE_STACK - char* new_line; - size_t offset; -#endif - char section[MAX_SECTION] = ""; - char prev_name[MAX_NAME] = ""; - - char* start; - char* end; - char* name; - char* value; - int lineno = 0; - int error = 0; - -#if !INI_USE_STACK - line = (char*)ini_malloc(INI_INITIAL_ALLOC); - if (!line) { - return -2; - } -#endif - -#if INI_HANDLER_LINENO -#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) -#else -#define HANDLER(u, s, n, v) handler(u, s, n, v) -#endif - - /* Scan through stream line by line */ - while (reader(line, (int)max_line, stream) != NULL) { -#if INI_ALLOW_REALLOC && !INI_USE_STACK - offset = strlen(line); - while (offset == max_line - 1 && line[offset - 1] != '\n') { - max_line *= 2; - if (max_line > INI_MAX_LINE) - max_line = INI_MAX_LINE; - new_line = ini_realloc(line, max_line); - if (!new_line) { - ini_free(line); - return -2; - } - line = new_line; - if (reader(line + offset, (int)(max_line - offset), stream) == NULL) - break; - if (max_line >= INI_MAX_LINE) - break; - offset += strlen(line + offset); - } -#endif - - lineno++; - - start = line; -#if INI_ALLOW_BOM - if (lineno == 1 && (unsigned char)start[0] == 0xEF && - (unsigned char)start[1] == 0xBB && - (unsigned char)start[2] == 0xBF) { - start += 3; - } -#endif - start = lskip(rstrip(start)); - - if (strchr(INI_START_COMMENT_PREFIXES, *start)) { - /* Start-of-line comment */ - } -#if INI_ALLOW_MULTILINE - else if (*prev_name && *start && start > line) { - /* Non-blank line with leading whitespace, treat as continuation - of previous name's value (as per Python configparser). */ - if (!HANDLER(user, section, prev_name, start) && !error) - error = lineno; - } -#endif - else if (*start == '[') { - /* A "[section]" line */ - end = find_chars_or_comment(start + 1, "]"); - if (*end == ']') { - *end = '\0'; - strncpy0(section, start + 1, sizeof(section)); - *prev_name = '\0'; -#if INI_CALL_HANDLER_ON_NEW_SECTION - if (!HANDLER(user, section, NULL, NULL) && !error) - error = lineno; -#endif - } - else if (!error) { - /* No ']' found on section line */ - error = lineno; - } - } - else if (*start) { - /* Not a comment, must be a name[=:]value pair */ - end = find_chars_or_comment(start, "=:"); - if (*end == '=' || *end == ':') { - *end = '\0'; - name = rstrip(start); - value = end + 1; -#if INI_ALLOW_INLINE_COMMENTS - end = find_chars_or_comment(value, NULL); - if (*end) - *end = '\0'; -#endif - value = lskip(value); - rstrip(value); - - /* Valid name[=:]value pair found, call handler */ - strncpy0(prev_name, name, sizeof(prev_name)); - if (!HANDLER(user, section, name, value) && !error) - error = lineno; - } - else if (!error) { - /* No '=' or ':' found on name[=:]value line */ -#if INI_ALLOW_NO_VALUE - *end = '\0'; - name = rstrip(start); - if (!HANDLER(user, section, name, NULL) && !error) - error = lineno; -#else - error = lineno; -#endif - } - } - -#if INI_STOP_ON_FIRST_ERROR - if (error) - break; -#endif - } - -#if !INI_USE_STACK - ini_free(line); -#endif - - return error; -} - -/* See documentation in header file. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user) -{ - return ini_parse_stream((ini_reader)fgets, file, handler, user); -} - -/* See documentation in header file. */ -int ini_parse(const char* filename, ini_handler handler, void* user) -{ - FILE* file; - int error; - - file = fopen(filename, "r"); - if (!file) - return -1; - error = ini_parse_file(file, handler, user); - fclose(file); - return error; -} - -/* An ini_reader function to read the next line from a string buffer. This - is the fgets() equivalent used by ini_parse_string(). */ -static char* ini_reader_string(char* str, int num, void* stream) { - ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; - const char* ctx_ptr = ctx->ptr; - size_t ctx_num_left = ctx->num_left; - char* strp = str; - char c; - - if (ctx_num_left == 0 || num < 2) - return NULL; - - while (num > 1 && ctx_num_left != 0) { - c = *ctx_ptr++; - ctx_num_left--; - *strp++ = c; - if (c == '\n') - break; - num--; - } - - *strp = '\0'; - ctx->ptr = ctx_ptr; - ctx->num_left = ctx_num_left; - return str; -} - -/* See documentation in header file. */ -int ini_parse_string(const char* string, ini_handler handler, void* user) { - ini_parse_string_ctx ctx; - - ctx.ptr = string; - ctx.num_left = strlen(string); - return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, - user); -} blob - /dev/null blob + d17e565dbf9ffd5971e39af4e69e3a7adb76a81f (mode 644) --- /dev/null +++ unreliablefs/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.10.2) + +include(CheckFunctionExists) + +set(UNRELIABLEFS_SRC conf.c + unreliablefs.c + unreliablefs_errinj.c + unreliablefs_ops.c) + +find_package(FUSE 2.9 REQUIRED) + +add_definitions(${FUSE_DEFINITIONS}) +add_executable(${PROJECT_NAME} ${UNRELIABLEFS_SRC}) +target_include_directories(${PROJECT_NAME} PRIVATE ${FUSE_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${FUSE_LIBRARIES}) + +target_compile_options(${PROJECT_NAME} PUBLIC -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Wno-unused-parameter) +target_compile_options(${PROJECT_NAME} PUBLIC $<$:-std=c++11 -D_FILE_OFFSET_BITS=64>) + +check_function_exists(fallocate HAVE_FALLOCATE) +check_function_exists(fallocate HAVE_FLOCK) +check_function_exists(utimensat HAVE_UTIMENSAT) +check_function_exists(setxattr HAVE_XATTR) +if (${HAVE_FALLOCATE}) + target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_FALLOCATE) +endif () +if (${HAVE_FLOCK}) + target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_FLOCK) +endif () +if (${HAVE_UTIMENSAT}) + target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_UTIMENSAT) +endif () +if (${HAVE_XATTR}) + target_compile_definitions(${PROJECT_NAME} PUBLIC HAVE_XATTR) +endif () + +add_subdirectory(tests) + +install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +install(FILES "unreliablefs.1" DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) +install(FILES "unreliablefs.conf.5" DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man5) blob - /dev/null blob + b3c6dbcdf02d42abf6748168bd8a8ba57122e615 (mode 644) --- /dev/null +++ unreliablefs/conf.c @@ -0,0 +1,298 @@ +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "conf.h" + +#if !INI_USE_STACK +#if INI_CUSTOM_ALLOCATOR +#include +void* ini_malloc(size_t size); +void ini_free(void* ptr); +void* ini_realloc(void* ptr, size_t size); +#else +#include +#define ini_malloc malloc +#define ini_free free +#define ini_realloc realloc +#endif +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Used by ini_parse_string() to keep track of string parsing state. */ +typedef struct { + const char* ptr; + size_t num_left; +} ini_parse_string_ctx; + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to NUL at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Similar to strncpy, but ensures dest (size bytes) is + NUL-terminated, and doesn't pad with NULs. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ + size_t i; + for (i = 0; i < size - 1 && src[i]; i++) + dest[i] = src[i]; + dest[i] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; + int max_line = INI_MAX_LINE; +#else + char* line; + size_t max_line = INI_INITIAL_ALLOC; +#endif +#if INI_ALLOW_REALLOC && !INI_USE_STACK + char* new_line; + size_t offset; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)ini_malloc(INI_INITIAL_ALLOC); + if (!line) { + return -2; + } +#endif + +#if INI_HANDLER_LINENO +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) +#else +#define HANDLER(u, s, n, v) handler(u, s, n, v) +#endif + + /* Scan through stream line by line */ + while (reader(line, (int)max_line, stream) != NULL) { +#if INI_ALLOW_REALLOC && !INI_USE_STACK + offset = strlen(line); + while (offset == max_line - 1 && line[offset - 1] != '\n') { + max_line *= 2; + if (max_line > INI_MAX_LINE) + max_line = INI_MAX_LINE; + new_line = ini_realloc(line, max_line); + if (!new_line) { + ini_free(line); + return -2; + } + line = new_line; + if (reader(line + offset, (int)(max_line - offset), stream) == NULL) + break; + if (max_line >= INI_MAX_LINE) + break; + offset += strlen(line + offset); + } +#endif + + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (strchr(INI_START_COMMENT_PREFIXES, *start)) { + /* Start-of-line comment */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!HANDLER(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; +#if INI_CALL_HANDLER_ON_NEW_SECTION + if (!HANDLER(user, section, NULL, NULL) && !error) + error = lineno; +#endif + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + value = lskip(value); + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!HANDLER(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ +#if INI_ALLOW_NO_VALUE + *end = '\0'; + name = rstrip(start); + if (!HANDLER(user, section, name, NULL) && !error) + error = lineno; +#else + error = lineno; +#endif + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + ini_free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)fgets, file, handler, user); +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, ini_handler handler, void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} + +/* An ini_reader function to read the next line from a string buffer. This + is the fgets() equivalent used by ini_parse_string(). */ +static char* ini_reader_string(char* str, int num, void* stream) { + ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; + const char* ctx_ptr = ctx->ptr; + size_t ctx_num_left = ctx->num_left; + char* strp = str; + char c; + + if (ctx_num_left == 0 || num < 2) + return NULL; + + while (num > 1 && ctx_num_left != 0) { + c = *ctx_ptr++; + ctx_num_left--; + *strp++ = c; + if (c == '\n') + break; + num--; + } + + *strp = '\0'; + ctx->ptr = ctx_ptr; + ctx->num_left = ctx_num_left; + return str; +} + +/* See documentation in header file. */ +int ini_parse_string(const char* string, ini_handler handler, void* user) { + ini_parse_string_ctx ctx; + + ctx.ptr = string; + ctx.num_left = strlen(string); + return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, + user); +} blob - /dev/null blob + 78015d141e6878398e001790f28c480d66679edd (mode 644) --- /dev/null +++ unreliablefs/conf.h @@ -0,0 +1,157 @@ +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef INI_H +#define INI_H + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Nonzero if ini_handler callback should accept lineno parameter. */ +#ifndef INI_HANDLER_LINENO +#define INI_HANDLER_LINENO 0 +#endif + +/* Typedef for prototype of handler function. */ +#if INI_HANDLER_LINENO +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value, + int lineno); +#else +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value); +#endif + +/* Typedef for prototype of fgets-style reader function. */ +typedef char* (*ini_reader)(char* str, int num, void* stream); + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O (see also + ini_parse_string). */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + +/* Same as ini_parse(), but takes a zero-terminated string with the INI data +instead of a file. Useful for parsing INI data from a network socket or +already in memory. */ +int ini_parse_string(const char* string, ini_handler handler, void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See https://github.com/benhoyt/inih/issues/21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Chars that begin a start-of-line comment. Per Python configparser, allow + both ; and # comments at the start of a line by default. */ +#ifndef INI_START_COMMENT_PREFIXES +#define INI_START_COMMENT_PREFIXES ";#" +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file (stack or heap). Note that + this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +/* Nonzero to allow heap line buffer to grow via realloc(), zero for a + fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is + zero. */ +#ifndef INI_ALLOW_REALLOC +#define INI_ALLOW_REALLOC 0 +#endif + +/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK + is zero. */ +#ifndef INI_INITIAL_ALLOC +#define INI_INITIAL_ALLOC 200 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +/* Nonzero to call the handler at the start of each new section (with + name and value NULL). Default is to only call the handler on + each name=value pair. */ +#ifndef INI_CALL_HANDLER_ON_NEW_SECTION +#define INI_CALL_HANDLER_ON_NEW_SECTION 0 +#endif + +/* Nonzero to allow a name without a value (no '=' or ':' on the line) and + call the handler with value NULL in this case. Default is to treat + no-value lines as an error. */ +#ifndef INI_ALLOW_NO_VALUE +#define INI_ALLOW_NO_VALUE 0 +#endif + +/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory + allocation functions (INI_USE_STACK must also be 0). These functions must + have the same signatures as malloc/free/realloc and behave in a similar + way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ +#ifndef INI_CUSTOM_ALLOCATOR +#define INI_CUSTOM_ALLOCATOR 0 +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* INI_H */ blob - /dev/null blob + 29b7ff9be8becde59433bd005a2529e96a2a876a (mode 644) --- /dev/null +++ unreliablefs/tests/CMakeLists.txt @@ -0,0 +1,17 @@ +if(${CMAKE_VERSION} VERSION_GREATER "3.12.0") + find_package(Python3 COMPONENTS Interpreter) + find_package(Python COMPONENTS Interpreter) +endif() + +set(PYTHON_PATH "/usr/bin/python3.6") +if(${Python3_FOUND}) + set(PYTHON_PATH ${Python3_EXECUTABLE}) +elseif(${Python_FOUND}) + set(PYTHON_PATH ${Python_EXECUTABLE}) +endif() + +add_executable(fsx "fsx.c") +add_custom_target(pytest + COMMAND ${PYTHON_PATH} -m pytest -c ${PROJECT_SOURCE_DIR}/unreliablefs/tests/pytest.ini ${PROJECT_SOURCE_DIR}/unreliablefs/tests/ + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Run regression test suite") blob - /dev/null blob + 6808b548e4f50ad2155271235aea6670b54d4b0e (mode 644) --- /dev/null +++ unreliablefs/tests/conftest.py @@ -0,0 +1,99 @@ +import sys +import pytest +import time +import re + +# If a test fails, wait a moment before retrieving the captured +# stdout/stderr. When using a server process, this makes sure that we capture +# any potential output of the server that comes *after* a test has failed. For +# example, if a request handler raises an exception, the server first signals an +# error to FUSE (causing the test to fail), and then logs the exception. Without +# the extra delay, the exception will go into nowhere. +@pytest.mark.hookwrapper +def pytest_pyfunc_call(pyfuncitem): + outcome = yield + failed = outcome.excinfo is not None + if failed: + time.sleep(1) + +@pytest.fixture() +def pass_capfd(request, capfd): + '''Provide capfd object to UnitTest instances''' + request.instance.capfd = capfd + +def check_test_output(capfd): + (stdout, stderr) = capfd.readouterr() + + # Write back what we've read (so that it will still be printed. + sys.stdout.write(stdout) + sys.stderr.write(stderr) + + # Strip out false positives + for (pattern, flags, count) in capfd.false_positives: + cp = re.compile(pattern, flags) + (stdout, cnt) = cp.subn('', stdout, count=count) + if count == 0 or count - cnt > 0: + stderr = cp.sub('', stderr, count=count - cnt) + + patterns = [ r'\b{}\b'.format(x) for x in + ('exception', 'error', 'warning', 'fatal', 'traceback', + 'fault', 'crash(?:ed)?', 'abort(?:ed)', + 'uninitiali[zs]ed') ] + patterns += ['^==[0-9]+== '] + for pattern in patterns: + cp = re.compile(pattern, re.IGNORECASE | re.MULTILINE) + # FIXME + """ + hit = cp.search(stderr) + if hit: + raise AssertionError('Suspicious output to stderr (matched "%s")' % hit.group(0)) + hit = cp.search(stdout) + if hit: + raise AssertionError('Suspicious output to stdout (matched "%s")' % hit.group(0)) + """ + +def register_output(self, pattern, count=1, flags=re.MULTILINE): + '''Register *pattern* as false positive for output checking + + This prevents the test from failing because the output otherwise + appears suspicious. + ''' + + self.false_positives.append((pattern, flags, count)) + +# This is a terrible hack that allows us to access the fixtures from the +# pytest_runtest_call hook. Among a lot of other hidden assumptions, it probably +# relies on tests running sequential (i.e., don't dare to use e.g. the xdist +# plugin) +current_capfd = None +@pytest.fixture(autouse=True) +def save_cap_fixtures(request, capfd): + global current_capfd + capfd.false_positives = [] + + type(capfd).register_output = register_output + + if request.config.getoption('capture') == 'no': + capfd = None + current_capfd = capfd + bak = current_capfd + yield + + # Try to catch problems with this hack (e.g. when running tests + # simultaneously) + assert bak is current_capfd + current_capfd = None + +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item): + capfd = current_capfd + if capfd is not None: + check_test_output(capfd) + +def pytest_configure(config): + config.addinivalue_line( + "markers", "uses_fuse: mark test to run only with FUSE subsystem" + ) + config.addinivalue_line( + "markers", "long: mark test that run longer than others" + ) blob - /dev/null blob + 384c3f486bc6312cb289664f15b4f512a1d7f11e (mode 644) --- /dev/null +++ unreliablefs/tests/fsx.c @@ -0,0 +1,1230 @@ +/* + * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 2.0 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * File: fsx.c + * Author: Avadis Tevanian, Jr. + * + * File system exerciser. + * + * Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com + * + * Various features from Joe Sokol, Pat Dirks, and Clark Warner. + * + * Small changes to work under Linux -- davej@suse.de + * + * Sundry porting patches from Guy Harris 12/2001 + * + * Checks for mmap last-page zero fill. + * + * Updated license to APSL 2.0, 2004/7/27 - Jordan Hubbard + * + * $FreeBSD$ + * + */ + +#include +#include +#ifdef _UWIN +# include +# include +# include +# include +#endif +#include +#include +#ifndef MAP_FILE +# define MAP_FILE 0 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */ + +/* + * A log entry is an operation and a bunch of arguments. + */ + +struct log_entry { + int operation; + int args[3]; +}; + +#define LOGSIZE 1000 + +struct log_entry oplog[LOGSIZE]; /* the log */ +int logptr = 0; /* current position in log */ +int logcount = 0; /* total ops */ + +/* + * Define operations + */ + +#define OP_READ 1 +#define OP_WRITE 2 +#define OP_TRUNCATE 3 +#define OP_CLOSEOPEN 4 +#define OP_MAPREAD 5 +#define OP_MAPWRITE 6 +#define OP_SKIPPED 7 +#define OP_INVALIDATE 8 + +int page_size; +int page_mask; + +char *original_buf; /* a pointer to the original data */ +char *good_buf; /* a pointer to the correct data */ +char *temp_buf; /* a pointer to the current data */ +char *fname; /* name of our test file */ +int fd; /* fd for our test file */ + +off_t file_size = 0; +off_t biggest = 0; +char state[256]; +unsigned long testcalls = 0; /* calls to function "test" */ + +unsigned long simulatedopcount = 0; /* -b flag */ +int closeprob = 0; /* -c flag */ +int invlprob = 0; /* -i flag */ +int debug = 0; /* -d flag */ +unsigned long debugstart = 0; /* -D flag */ +unsigned long maxfilelen = 256 * 1024; /* -l flag */ +int sizechecks = 1; /* -n flag disables them */ +int maxoplen = 64 * 1024; /* -o flag */ +int quiet = 0; /* -q flag */ +unsigned long progressinterval = 0; /* -p flag */ +int readbdy = 1; /* -r flag */ +int style = 0; /* -s flag */ +int truncbdy = 1; /* -t flag */ +int writebdy = 1; /* -w flag */ +long monitorstart = -1; /* -m flag */ +long monitorend = -1; /* -m flag */ +int lite = 0; /* -L flag */ +long numops = -1; /* -N flag */ +int randomoplen = 1; /* -O flag disables it */ +int seed = 1; /* -S flag */ +int mapped_writes = 1; /* -W flag disables */ +int mapped_reads = 1; /* -R flag disables it */ +int mapped_msync = 1; /* -U flag disables */ +int fsxgoodfd = 0; +FILE * fsxlogf = NULL; +int badoff = -1; +int closeopen = 0; +int invl = 0; + + +void +vwarnc(code, fmt, ap) + int code; + const char *fmt; + va_list ap; +{ + fprintf(stderr, "fsx: "); + if (fmt != NULL) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + } + fprintf(stderr, "%s\n", strerror(code)); +} + + +void +warn(const char * fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnc(errno, fmt, ap); + va_end(ap); +} + + +void +prt(char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); + + if (fsxlogf) { + va_start(args, fmt); + vfprintf(fsxlogf, fmt, args); + va_end(args); + } +} + +void +prterr(char *prefix) +{ + prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); +} + + +void +do_log4(int operation, int arg0, int arg1, int arg2) +{ + struct log_entry *le; + + le = &oplog[logptr]; + le->operation = operation; + le->args[0] = arg0; + le->args[1] = arg1; + le->args[2] = arg2; + logptr++; + logcount++; + if (logptr >= LOGSIZE) + logptr = 0; +} + + +void +log4(int operation, int arg0, int arg1, int arg2) +{ + do_log4(operation, arg0, arg1, arg2); + if (closeopen) + do_log4(OP_CLOSEOPEN, 0, 0, 0); + if (invl) + do_log4(OP_INVALIDATE, 0, 0, 0); +} + + +void +logdump(void) +{ + struct log_entry *lp; + int i, count, down, opnum; + + prt("LOG DUMP (%d total operations):\n", logcount); + if (logcount < LOGSIZE) { + i = 0; + count = logcount; + } else { + i = logptr; + count = LOGSIZE; + } + + opnum = i + 1 + (logcount/LOGSIZE)*LOGSIZE; + for ( ; count > 0; count--) { + lp = &oplog[i]; + + if (lp->operation == OP_CLOSEOPEN || + lp->operation == OP_INVALIDATE) { + switch (lp->operation) { + case OP_CLOSEOPEN: + prt("\t\tCLOSE/OPEN\n"); + break; + case OP_INVALIDATE: + prt("\t\tMS_INVALIDATE\n"); + break; + } + i++; + if (i == LOGSIZE) + i = 0; + continue; + } + + prt("%d(%d mod 256): ", opnum, opnum%256); + switch (lp->operation) { + case OP_MAPREAD: + prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (badoff >= lp->args[0] && badoff < + lp->args[0] + lp->args[1]) + prt("\t***RRRR***"); + break; + case OP_MAPWRITE: + prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (badoff >= lp->args[0] && badoff < + lp->args[0] + lp->args[1]) + prt("\t******WWWW"); + break; + case OP_READ: + prt("READ\t0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (badoff >= lp->args[0] && + badoff < lp->args[0] + lp->args[1]) + prt("\t***RRRR***"); + break; + case OP_WRITE: + prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (lp->args[0] > lp->args[2]) + prt(" HOLE"); + else if (lp->args[0] + lp->args[1] > lp->args[2]) + prt(" EXTEND"); + if ((badoff >= lp->args[0] || badoff >=lp->args[2]) && + badoff < lp->args[0] + lp->args[1]) + prt("\t***WWWW"); + break; + case OP_TRUNCATE: + down = lp->args[0] < lp->args[1]; + prt("TRUNCATE %s\tfrom 0x%x to 0x%x", + down ? "DOWN" : "UP", lp->args[1], lp->args[0]); + if (badoff >= lp->args[!down] && + badoff < lp->args[!!down]) + prt("\t******WWWW"); + break; + case OP_SKIPPED: + prt("SKIPPED (no operation)"); + break; + default: + prt("BOGUS LOG ENTRY (operation code = %d)!", + lp->operation); + } + prt("\n"); + opnum++; + i++; + if (i == LOGSIZE) + i = 0; + } +} + + +void +save_buffer(char *buffer, off_t bufferlength, int fd) +{ + off_t ret; + ssize_t byteswritten; + + if (fd <= 0 || bufferlength == 0) + return; + + if (bufferlength > SSIZE_MAX) { + prt("fsx flaw: overflow in save_buffer\n"); + exit(67); + } + if (lite) { + off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END); + if (size_by_seek == (off_t)-1) + prterr("save_buffer: lseek eof"); + else if (bufferlength > size_by_seek) { + warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek, + (unsigned long long)bufferlength); + bufferlength = size_by_seek; + } + } + + ret = lseek(fd, (off_t)0, SEEK_SET); + if (ret == (off_t)-1) + prterr("save_buffer: lseek 0"); + + byteswritten = write(fd, buffer, (size_t)bufferlength); + if (byteswritten != bufferlength) { + if (byteswritten == -1) + prterr("save_buffer write"); + else + warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n", + (unsigned)byteswritten, + (unsigned long long)bufferlength); + } +} + + +void +report_failure(int status) +{ + logdump(); + + if (fsxgoodfd) { + if (good_buf) { + save_buffer(good_buf, file_size, fsxgoodfd); + prt("Correct content saved for comparison\n"); + prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", + fname, fname); + } + close(fsxgoodfd); + } + exit(status); +} + + +#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \ + *(((unsigned char *)(cp)) + 1))) + +void +check_buffers(unsigned offset, unsigned size) +{ + unsigned char c, t; + unsigned i = 0; + unsigned n = 0; + unsigned op = 0; + unsigned bad = 0; + + if (memcmp(good_buf + offset, temp_buf, size) != 0) { + prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n", + offset, size); + prt("OFFSET\tGOOD\tBAD\tRANGE\n"); + while (size > 0) { + c = good_buf[offset]; + t = temp_buf[i]; + if (c != t) { + if (n == 0) { + bad = short_at(&temp_buf[i]); + prt("0x%5x\t0x%04x\t0x%04x", offset, + short_at(&good_buf[offset]), bad); + op = temp_buf[offset & 1 ? i+1 : i]; + } + n++; + badoff = offset; + } + offset++; + i++; + size--; + } + if (n) { + prt("\t0x%5x\n", n); + if (bad) + prt("operation# (mod 256) for the bad data may be %u\n", ((unsigned)op & 0xff)); + else + prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n"); + } else + prt("????????????????\n"); + report_failure(110); + } +} + + +void +check_size(void) +{ + struct stat statbuf; + off_t size_by_seek; + + if (fstat(fd, &statbuf)) { + prterr("check_size: fstat"); + statbuf.st_size = -1; + } + size_by_seek = lseek(fd, (off_t)0, SEEK_END); + if (file_size != statbuf.st_size || file_size != size_by_seek) { + prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n", + (unsigned long long)file_size, + (unsigned long long)statbuf.st_size, + (unsigned long long)size_by_seek); + report_failure(120); + } +} + + +void +check_trunc_hack(void) +{ + struct stat statbuf; + + ftruncate(fd, (off_t)0); + ftruncate(fd, (off_t)100000); + fstat(fd, &statbuf); + if (statbuf.st_size != (off_t)100000) { + prt("no extend on truncate! not posix!\n"); + exit(130); + } + ftruncate(fd, (off_t)0); +} + + +void +doread(unsigned offset, unsigned size) +{ + off_t ret; + unsigned iret; + + offset -= offset % readbdy; + if (size == 0) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping zero size read\n"); + log4(OP_SKIPPED, OP_READ, offset, size); + return; + } + if (size + offset > file_size) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping seek/read past end of file\n"); + log4(OP_SKIPPED, OP_READ, offset, size); + return; + } + + log4(OP_READ, offset, size, 0); + + if (testcalls <= simulatedopcount) + return; + + if (!quiet && ((progressinterval && + testcalls % progressinterval == 0) || + (debug && + (monitorstart == -1 || + (offset + size > monitorstart && + (monitorend == -1 || offset <= monitorend)))))) + prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, + offset, offset + size - 1, size); + ret = lseek(fd, (off_t)offset, SEEK_SET); + if (ret == (off_t)-1) { + prterr("doread: lseek"); + report_failure(140); + } + iret = read(fd, temp_buf, size); + if (iret != size) { + if (iret == -1) + prterr("doread: read"); + else + prt("short read: 0x%x bytes instead of 0x%x\n", + iret, size); + report_failure(141); + } + check_buffers(offset, size); +} + + +void +check_eofpage(char *s, unsigned offset, char *p, int size) +{ + uintptr_t last_page, should_be_zero; + + if (offset + size <= (file_size & ~page_mask)) + return; + /* + * we landed in the last page of the file + * test to make sure the VM system provided 0's + * beyond the true end of the file mapping + * (as required by mmap def in 1996 posix 1003.1) + */ + last_page = ((uintptr_t)p + (offset & page_mask) + size) & ~page_mask; + + for (should_be_zero = last_page + (file_size & page_mask); + should_be_zero < last_page + page_size; + should_be_zero++) + if (*(char *)should_be_zero) { + prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n", + s, file_size - 1, should_be_zero & page_mask, + short_at(should_be_zero)); + report_failure(205); + } +} + + +void +domapread(unsigned offset, unsigned size) +{ + unsigned pg_offset; + unsigned map_size; + char *p; + + offset -= offset % readbdy; + if (size == 0) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping zero size read\n"); + log4(OP_SKIPPED, OP_MAPREAD, offset, size); + return; + } + if (size + offset > file_size) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping seek/read past end of file\n"); + log4(OP_SKIPPED, OP_MAPREAD, offset, size); + return; + } + + log4(OP_MAPREAD, offset, size, 0); + + if (testcalls <= simulatedopcount) + return; + + if (!quiet && ((progressinterval && + testcalls % progressinterval == 0) || + (debug && + (monitorstart == -1 || + (offset + size > monitorstart && + (monitorend == -1 || offset <= monitorend)))))) + prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, + offset, offset + size - 1, size); + + pg_offset = offset & page_mask; + map_size = pg_offset + size; + + if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, + (off_t)(offset - pg_offset))) == (char *)-1) { + prterr("domapread: mmap"); + report_failure(190); + } + memcpy(temp_buf, p + pg_offset, size); + + check_eofpage("Read", offset, p, size); + + if (munmap(p, map_size) != 0) { + prterr("domapread: munmap"); + report_failure(191); + } + + check_buffers(offset, size); +} + + +void +gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size) +{ + while (size--) { + good_buf[offset] = testcalls % 256; + if (offset % 2) + good_buf[offset] += original_buf[offset]; + offset++; + } +} + + +void +dowrite(unsigned offset, unsigned size) +{ + off_t ret; + unsigned iret; + + offset -= offset % writebdy; + if (size == 0) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping zero size write\n"); + log4(OP_SKIPPED, OP_WRITE, offset, size); + return; + } + + log4(OP_WRITE, offset, size, file_size); + + gendata(original_buf, good_buf, offset, size); + if (file_size < offset + size) { + if (file_size < offset) + memset(good_buf + file_size, '\0', offset - file_size); + file_size = offset + size; + if (lite) { + warn("Lite file size bug in fsx!"); + report_failure(149); + } + } + + if (testcalls <= simulatedopcount) + return; + + if (!quiet && ((progressinterval && + testcalls % progressinterval == 0) || + (debug && + (monitorstart == -1 || + (offset + size > monitorstart && + (monitorend == -1 || offset <= monitorend)))))) + prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, + offset, offset + size - 1, size); + ret = lseek(fd, (off_t)offset, SEEK_SET); + if (ret == (off_t)-1) { + prterr("dowrite: lseek"); + report_failure(150); + } + iret = write(fd, good_buf + offset, size); + if (iret != size) { + if (iret == -1) + prterr("dowrite: write"); + else + prt("short write: 0x%x bytes instead of 0x%x\n", + iret, size); + report_failure(151); + } +} + + +void +domapwrite(unsigned offset, unsigned size) +{ + unsigned pg_offset; + unsigned map_size; + off_t cur_filesize; + char *p; + + offset -= offset % writebdy; + if (size == 0) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping zero size write\n"); + log4(OP_SKIPPED, OP_MAPWRITE, offset, size); + return; + } + cur_filesize = file_size; + + log4(OP_MAPWRITE, offset, size, 0); + + gendata(original_buf, good_buf, offset, size); + if (file_size < offset + size) { + if (file_size < offset) + memset(good_buf + file_size, '\0', offset - file_size); + file_size = offset + size; + if (lite) { + warn("Lite file size bug in fsx!"); + report_failure(200); + } + } + + if (testcalls <= simulatedopcount) + return; + + if (!quiet && ((progressinterval && + testcalls % progressinterval == 0) || + (debug && + (monitorstart == -1 || + (offset + size > monitorstart && + (monitorend == -1 || offset <= monitorend)))))) + prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, + offset, offset + size - 1, size); + + if (file_size > cur_filesize) { + if (ftruncate(fd, file_size) == -1) { + prterr("domapwrite: ftruncate"); + exit(201); + } + } + pg_offset = offset & page_mask; + map_size = pg_offset + size; + + if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, + (off_t)(offset - pg_offset))) == MAP_FAILED) { + prterr("domapwrite: mmap"); + report_failure(202); + } + memcpy(p + pg_offset, good_buf + offset, size); + if (mapped_msync && msync(p, map_size, MS_SYNC) != 0) { + prterr("domapwrite: msync"); + report_failure(203); + } + + check_eofpage("Write", offset, p, size); + + if (munmap(p, map_size) != 0) { + prterr("domapwrite: munmap"); + report_failure(204); + } +} + + +void +dotruncate(unsigned size) +{ + int oldsize = file_size; + + size -= size % truncbdy; + if (size > biggest) { + biggest = size; + if (!quiet && testcalls > simulatedopcount) + prt("truncating to largest ever: 0x%x\n", size); + } + + log4(OP_TRUNCATE, size, (unsigned)file_size, 0); + + if (size > file_size) + memset(good_buf + file_size, '\0', size - file_size); + file_size = size; + + if (testcalls <= simulatedopcount) + return; + + if ((progressinterval && testcalls % progressinterval == 0) || + (debug && (monitorstart == -1 || monitorend == -1 || + size <= monitorend))) + prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size); + if (ftruncate(fd, (off_t)size) == -1) { + prt("ftruncate1: %x\n", size); + prterr("dotruncate: ftruncate"); + report_failure(160); + } +} + + +void +writefileimage() +{ + ssize_t iret; + + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { + prterr("writefileimage: lseek"); + report_failure(171); + } + iret = write(fd, good_buf, file_size); + if ((off_t)iret != file_size) { + if (iret == -1) + prterr("writefileimage: write"); + else + prt("short write: 0x%x bytes instead of 0x%llx\n", + iret, (unsigned long long)file_size); + report_failure(172); + } + if (lite ? 0 : ftruncate(fd, file_size) == -1) { + prt("ftruncate2: %llx\n", (unsigned long long)file_size); + prterr("writefileimage: ftruncate"); + report_failure(173); + } +} + + +void +docloseopen(void) +{ + if (testcalls <= simulatedopcount) + return; + + if (debug) + prt("%lu close/open\n", testcalls); + if (close(fd)) { + prterr("docloseopen: close"); + report_failure(180); + } + fd = open(fname, O_RDWR, 0); + if (fd < 0) { + prterr("docloseopen: open"); + report_failure(181); + } +} + + +void +doinvl(void) +{ + char *p; + + if (file_size == 0) + return; + if (testcalls <= simulatedopcount) + return; + if (debug) + prt("%lu msync(MS_INVALIDATE)\n", testcalls); + + if ((p = (char *)mmap(0, file_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0)) == MAP_FAILED) { + prterr("doinvl: mmap"); + report_failure(205); + } + + if (msync(p, 0, MS_SYNC | MS_INVALIDATE) != 0) { + prterr("doinvl: msync"); + report_failure(206); + } + + if (munmap(p, file_size) != 0) { + prterr("doinvl: munmap"); + report_failure(207); + } +} + + +void +test(void) +{ + unsigned long offset; + unsigned long size = maxoplen; + unsigned long rv = random(); + unsigned long op = rv % (3 + !lite + mapped_writes); + + /* turn off the map read if necessary */ + + if (op == 2 && !mapped_reads) + op = 0; + + if (simulatedopcount > 0 && testcalls == simulatedopcount) + writefileimage(); + + testcalls++; + + if (closeprob) + closeopen = (rv >> 3) < (1 << 28) / closeprob; + if (invlprob) + invl = (rv >> 3) < (1 << 28) / invlprob; + + if (debugstart > 0 && testcalls >= debugstart) + debug = 1; + + if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) + prt("%lu...\n", testcalls); + + /* + * READ: op = 0 + * WRITE: op = 1 + * MAPREAD: op = 2 + * TRUNCATE: op = 3 + * MAPWRITE: op = 3 or 4 + */ + if (lite ? 0 : op == 3 && style == 0) /* vanilla truncate? */ + dotruncate(random() % maxfilelen); + else { + if (randomoplen) + size = random() % (maxoplen+1); + if (lite ? 0 : op == 3) + dotruncate(size); + else { + offset = random(); + if (op == 1 || op == (lite ? 3 : 4)) { + offset %= maxfilelen; + if (offset + size > maxfilelen) + size = maxfilelen - offset; + if (op != 1) + domapwrite(offset, size); + else + dowrite(offset, size); + } else { + if (file_size) + offset %= file_size; + else + offset = 0; + if (offset + size > file_size) + size = file_size - offset; + if (op != 0) + domapread(offset, size); + else + doread(offset, size); + } + } + } + if (sizechecks && testcalls > simulatedopcount) + check_size(); + if (invl) + doinvl(); + if (closeopen) + docloseopen(); +} + + +void +cleanup(sig) + int sig; +{ + if (sig) + prt("signal %d\n", sig); + prt("testcalls = %lu\n", testcalls); + exit(sig); +} + + +void +usage(void) +{ + fprintf(stdout, "usage: %s", + "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\ + -b opnum: beginning operation number (default 1)\n\ + -c P: 1 in P chance of file close+open at each op (default infinity)\n\ + -d: debug output for all operations\n\ + -i P: 1 in P chance of calling msync(MS_INVALIDATE) (default infinity)\n\ + -l flen: the upper bound on file size (default 262144)\n\ + -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\ + -n: no verifications of file size\n\ + -o oplen: the upper bound on operation size (default 65536)\n\ + -p progressinterval: debug output at specified operation interval\n\ + -q: quieter operation\n\ + -r readbdy: 4096 would make reads page aligned (default 1)\n\ + -s style: 1 gives smaller truncates (default 0)\n\ + -t truncbdy: 4096 would make truncates page aligned (default 1)\n\ + -w writebdy: 4096 would make writes page aligned (default 1)\n\ + -D startingop: debug output starting at specified operation\n\ + -L: fsxLite - no file creations & no file size changes\n\ + -N numops: total # operations to do (default infinity)\n\ + -O: use oplen (see -o flag) for every op (default random)\n\ + -P dirpath: save .fsxlog and .fsxgood files in dirpath (default ./)\n\ + -S seed: for random # generator (default 1) 0 gets timestamp\n\ + -W: mapped write operations DISabled\n\ + -R: mapped read operations DISabled)\n\ + -U: msync after mapped write operations DISabled\n\ + fname: this filename is REQUIRED (no default)\n"); + exit(90); +} + + +int +getnum(char *s, char **e) +{ + int ret = -1; + + *e = (char *) 0; + ret = strtol(s, e, 0); + if (*e) + switch (**e) { + case 'b': + case 'B': + ret *= 512; + *e = *e + 1; + break; + case 'k': + case 'K': + ret *= 1024; + *e = *e + 1; + break; + case 'm': + case 'M': + ret *= 1024*1024; + *e = *e + 1; + break; + case 'w': + case 'W': + ret *= 4; + *e = *e + 1; + break; + } + return (ret); +} + + +int +main(int argc, char **argv) +{ + int i, ch; + char *endp; + char goodfile[1024]; + char logfile[1024]; + + goodfile[0] = 0; + logfile[0] = 0; + + page_size = getpagesize(); + page_mask = page_size - 1; + + setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ + + while ((ch = getopt(argc, argv, + "b:c:di:l:m:no:p:qr:s:t:w:D:LN:OP:RS:UW")) != -1) + switch (ch) { + case 'b': + simulatedopcount = getnum(optarg, &endp); + if (!quiet) + fprintf(stdout, "Will begin at operation %ld\n", + simulatedopcount); + if (simulatedopcount == 0) + usage(); + simulatedopcount -= 1; + break; + case 'c': + closeprob = getnum(optarg, &endp); + if (!quiet) + fprintf(stdout, + "Chance of close/open is 1 in %d\n", + closeprob); + if (closeprob <= 0) + usage(); + break; + case 'd': + debug = 1; + break; + case 'i': + invlprob = getnum(optarg, &endp); + if (!quiet) + fprintf(stdout, + "Chance of MS_INVALIDATE is 1 in %d\n", + invlprob); + if (invlprob <= 0) + usage(); + break; + case 'l': + maxfilelen = getnum(optarg, &endp); + if (maxfilelen <= 0) + usage(); + break; + case 'm': + monitorstart = getnum(optarg, &endp); + if (monitorstart < 0) + usage(); + if (!endp || *endp++ != ':') + usage(); + monitorend = getnum(endp, &endp); + if (monitorend < 0) + usage(); + if (monitorend == 0) + monitorend = -1; /* aka infinity */ + debug = 1; + case 'n': + sizechecks = 0; + break; + case 'o': + maxoplen = getnum(optarg, &endp); + if (maxoplen <= 0) + usage(); + break; + case 'p': + progressinterval = getnum(optarg, &endp); + if (progressinterval < 0) + usage(); + break; + case 'q': + quiet = 1; + break; + case 'r': + readbdy = getnum(optarg, &endp); + if (readbdy <= 0) + usage(); + break; + case 's': + style = getnum(optarg, &endp); + if (style < 0 || style > 1) + usage(); + break; + case 't': + truncbdy = getnum(optarg, &endp); + if (truncbdy <= 0) + usage(); + break; + case 'w': + writebdy = getnum(optarg, &endp); + if (writebdy <= 0) + usage(); + break; + case 'D': + debugstart = getnum(optarg, &endp); + if (debugstart < 1) + usage(); + break; + case 'L': + lite = 1; + break; + case 'N': + numops = getnum(optarg, &endp); + if (numops < 0) + usage(); + break; + case 'O': + randomoplen = 0; + break; + case 'P': + strncpy(goodfile, optarg, sizeof(goodfile)); + strcat(goodfile, "/"); + strncpy(logfile, optarg, sizeof(logfile)); + strcat(logfile, "/"); + break; + case 'R': + mapped_reads = 0; + break; + case 'S': + seed = getnum(optarg, &endp); + if (seed == 0) + seed = time(0) % 10000; + if (!quiet) + fprintf(stdout, "Seed set to %d\n", seed); + if (seed < 0) + usage(); + break; + case 'W': + mapped_writes = 0; + if (!quiet) + fprintf(stdout, "mapped writes DISABLED\n"); + break; + case 'U': + mapped_msync = 0; + if (!quiet) + fprintf(stdout, "mapped msync DISABLED\n"); + break; + + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + if (argc != 1) + usage(); + fname = argv[0]; + + signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGPIPE, cleanup); + signal(SIGALRM, cleanup); + signal(SIGTERM, cleanup); + signal(SIGXCPU, cleanup); + signal(SIGXFSZ, cleanup); + signal(SIGVTALRM, cleanup); + signal(SIGUSR1, cleanup); + signal(SIGUSR2, cleanup); + + initstate(seed, state, 256); + setstate(state); + fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666); + if (fd < 0) { + prterr(fname); + exit(91); + } + strncat(goodfile, fname, 256); + strcat (goodfile, ".fsxgood"); + fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666); + if (fsxgoodfd < 0) { + prterr(goodfile); + exit(92); + } + strncat(logfile, fname, 256); + strcat (logfile, ".fsxlog"); + fsxlogf = fopen(logfile, "w"); + if (fsxlogf == NULL) { + prterr(logfile); + exit(93); + } + if (lite) { + off_t ret; + file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END); + if (file_size == (off_t)-1) { + prterr(fname); + warn("main: lseek eof"); + exit(94); + } + ret = lseek(fd, (off_t)0, SEEK_SET); + if (ret == (off_t)-1) { + prterr(fname); + warn("main: lseek 0"); + exit(95); + } + } + original_buf = (char *) malloc(maxfilelen); + for (i = 0; i < maxfilelen; i++) + original_buf[i] = random() % 256; + good_buf = (char *) malloc(maxfilelen); + memset(good_buf, '\0', maxfilelen); + temp_buf = (char *) malloc(maxoplen); + memset(temp_buf, '\0', maxoplen); + if (lite) { /* zero entire existing file */ + ssize_t written; + + written = write(fd, good_buf, (size_t)maxfilelen); + if (written != maxfilelen) { + if (written == -1) { + prterr(fname); + warn("main: error on write"); + } else + warn("main: short write, 0x%x bytes instead of 0x%x\n", + (unsigned)written, maxfilelen); + exit(98); + } + } else + check_trunc_hack(); + + while (numops == -1 || numops--) + test(); + + if (close(fd)) { + prterr("close"); + report_failure(99); + } + prt("All operations completed A-OK!\n"); + + exit(0); + return 0; +} + blob - /dev/null blob + 74e46657d2344758e9f0202d09fc6584efa4057c (mode 644) --- /dev/null +++ unreliablefs/tests/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +addopts = --verbose --tb=native -x --junit-xml=report.xml +markers = + uses_fuse: Indicates that FUSE is supported. + long: Test that run longer than others. blob - /dev/null blob + f2e57e025e1f1b451d11e622b0de242f65444ff5 (mode 644) --- /dev/null +++ unreliablefs/tests/test_unreliablefs.py @@ -0,0 +1,584 @@ +#!/usr/bin/env python3 + +if __name__ == '__main__': + import pytest + import sys + sys.exit(pytest.main([__file__] + sys.argv[1:])) + +import binascii +import fcntl +import subprocess +import os +import sys +import shutil +import pytest +import stat +import shutil +import subprocess +import filecmp +import errno +from contextlib import contextmanager +from tempfile import NamedTemporaryFile +from util import (wait_for_mount, umount, cleanup, base_cmdline, + basedir, fuse_test_marker, safe_sleep) +from os.path import join as pjoin + +def is_no_xattr_support(): + return sys.platform.startswith("freebsd") or \ + sys.platform == "openbsd" + +no_xattr_support = is_no_xattr_support() + +TEST_FILE = __file__ + +pytestmark = fuse_test_marker() + +with open(TEST_FILE, 'rb') as fh: + TEST_DATA = fh.read() + +def name_generator(__ctr=[0]): + __ctr[0] += 1 + return 'testfile_%d' % __ctr[0] + + +@pytest.mark.uses_fuse +@pytest.fixture(scope="function") +def setup_unreliablefs(tmpdir): + mnt_dir = str(tmpdir.mkdir('mnt')) + src_dir = str(tmpdir.mkdir('src')) + + options = "-basedir={}".format(src_dir) + cmdline = base_cmdline + [ pjoin(basedir, 'build/unreliablefs/unreliablefs'), mnt_dir, options ] + mount_process = subprocess.Popen(cmdline) + wait_for_mount(mount_process, mnt_dir) + + yield mnt_dir, src_dir + + umount(mount_process, mnt_dir) + cleanup(mount_process, mnt_dir) + +@contextmanager +def os_open(name, flags): + fd = os.open(name, flags) + try: + yield fd + finally: + os.close(fd) + +def os_create(name): + os.close(os.open(name, os.O_CREAT | os.O_RDWR)) + +def test_unlink(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + fullname = mnt_dir + "/" + name + with open(pjoin(src_dir, name), 'wb') as fh: + fh.write(b'hello') + assert name in os.listdir(mnt_dir) + os.unlink(fullname) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + assert name not in os.listdir(src_dir) + +def test_mkdir(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + dirname = name_generator() + fullname = mnt_dir + "/" + dirname + os.mkdir(fullname) + fstat = os.stat(fullname) + assert stat.S_ISDIR(fstat.st_mode) + assert os.listdir(fullname) == [] + assert fstat.st_nlink in (1,2) + assert dirname in os.listdir(mnt_dir) + +def test_rmdir(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + fullname = mnt_dir + "/" + name + os.mkdir(pjoin(src_dir, name)) + assert name in os.listdir(mnt_dir) + os.rmdir(fullname) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + assert name not in os.listdir(src_dir) + +def test_symlink(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + linkname = name_generator() + link_path = os.path.join(mnt_dir, linkname) + + targetname = name_generator() + target_path = os.path.join("/tmp", targetname) + with open(target_path, 'w+'): + pass + assert os.path.exists(target_path) + + os.symlink(target_path, link_path) + fstat = os.lstat(link_path) + assert stat.S_ISLNK(fstat.st_mode) + assert os.readlink(link_path) == target_path + assert fstat.st_nlink == 1 + assert linkname in os.listdir(mnt_dir) + +def test_create(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + fullname = pjoin(mnt_dir, name) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + + fd = os.open(fullname, os.O_CREAT | os.O_RDWR) + os.close(fd) + + assert name in os.listdir(mnt_dir) + fstat = os.lstat(fullname) + assert stat.S_ISREG(fstat.st_mode) + assert fstat.st_nlink == 1 + assert fstat.st_size == 0 + +@pytest.mark.xfail(reason="gh-39") +def test_chown(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + filename = pjoin(mnt_dir, name_generator()) + os.mkdir(filename) + fstat = os.lstat(filename) + uid = fstat.st_uid + gid = fstat.st_gid + + uid_new = uid + 1 + os.chown(filename, uid_new, -1) + fstat = os.lstat(filename) + assert fstat.st_uid == uid_new + assert fstat.st_gid == gid + + gid_new = gid + 1 + os.chown(filename, -1, gid_new) + fstat = os.lstat(filename) + assert fstat.st_uid == uid_new + assert fstat.st_gid == gid_new + +def test_open_read(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + with open(pjoin(src_dir, name), 'wb') as fh_out, \ + open(TEST_FILE, 'rb') as fh_in: + shutil.copyfileobj(fh_in, fh_out) + assert len(os.listdir(mnt_dir)) == 1 + + assert filecmp.cmp(pjoin(mnt_dir, name), TEST_FILE, False) + +def test_open_write(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + content = b'AABBCC' + fd = os.open(pjoin(src_dir, name), + os.O_CREAT | os.O_RDWR) + os.write(fd, content) + os.close(fd) + + fullname = pjoin(mnt_dir, name) + with open(fullname, 'rb') as fh: + assert fh.read() == content + +@pytest.mark.xfail(sys.platform.startswith("freebsd"), reason="gh-44") +def test_append(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + os_create(pjoin(src_dir, name)) + fullname = pjoin(mnt_dir, name) + with os_open(fullname, os.O_WRONLY) as fd: + os.write(fd, b'foo\n') + with os_open(fullname, os.O_WRONLY|os.O_APPEND) as fd: + os.write(fd, b'bar\n') + + with open(fullname, 'rb') as fh: + assert fh.read() == b'foo\nbar\n' + +@pytest.mark.xfail(sys.platform.startswith("freebsd"), reason="gh-42") +def test_seek(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + os_create(pjoin(src_dir, name)) + fullname = pjoin(mnt_dir, name) + with os_open(fullname, os.O_WRONLY) as fd: + os.lseek(fd, 1, os.SEEK_SET) + os.write(fd, b'foobar\n') + with os_open(fullname, os.O_WRONLY) as fd: + os.lseek(fd, 4, os.SEEK_SET) + os.write(fd, b'com') + + with open(fullname, 'rb') as fh: + assert fh.read() == b'\0foocom\n' + +def test_open_unlink(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = pjoin(mnt_dir, name_generator()) + data1 = b'foo' + data2 = b'bar' + fullname = pjoin(mnt_dir, name) + with open(fullname, 'wb+', buffering=0) as fh: + fh.write(data1) + os.unlink(fullname) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + fh.write(data2) + fh.seek(0) + assert fh.read() == data1+data2 + +def test_open_gh_14(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = pjoin(mnt_dir, name_generator()) + fullname = pjoin(mnt_dir, name) + + with open(fullname, "w+") as fh: + hs = "123456789ABCDEF1" + hb = binascii.a2b_hex(hs) + fh.write(str(hb)) + fh.seek(0) + fh.read() + +def test_statvfs(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + os.statvfs(mnt_dir) + +def test_link(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name1 = pjoin(mnt_dir, name_generator()) + name2 = pjoin(mnt_dir, name_generator()) + shutil.copyfile(TEST_FILE, name1) + assert filecmp.cmp(name1, TEST_FILE, False) + + fstat1 = os.lstat(name1) + assert fstat1.st_nlink == 1 + + os.link(name1, name2) + + fstat1 = os.lstat(name1) + fstat2 = os.lstat(name2) + for attr in ('st_mode', 'st_dev', 'st_uid', 'st_gid', + 'st_size', 'st_atime', 'st_mtime', 'st_ctime'): + assert getattr(fstat1, attr) == getattr(fstat2, attr) + assert os.path.basename(name2) in os.listdir(mnt_dir) + assert filecmp.cmp(name1, name2, False) + + os.unlink(name2) + + assert os.path.basename(name2) not in os.listdir(mnt_dir) + with pytest.raises(FileNotFoundError): + os.lstat(name2) + + os.unlink(name1) + +def test_readdir(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + newdir = name_generator() + src_newdir = pjoin(src_dir, newdir) + mnt_newdir = pjoin(mnt_dir, newdir) + file_ = src_newdir + "/" + name_generator() + subdir = src_newdir + "/" + name_generator() + subfile = subdir + "/" + name_generator() + + os.mkdir(src_newdir) + shutil.copyfile(TEST_FILE, file_) + os.mkdir(subdir) + shutil.copyfile(TEST_FILE, subfile) + + listdir_is = os.listdir(mnt_newdir) + listdir_is.sort() + listdir_should = [ os.path.basename(file_), os.path.basename(subdir) ] + listdir_should.sort() + assert listdir_is == listdir_should + + os.unlink(file_) + os.unlink(subfile) + os.rmdir(subdir) + os.rmdir(src_newdir) + +def test_readdir_big(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + + # Add enough entries so that readdir needs to be called + # multiple times. + fnames = [] + for i in range(500): + fname = ('A rather long filename to make sure that we ' + 'fill up the buffer - ' * 3) + str(i) + with open(pjoin(src_dir, fname), 'w') as fh: + fh.write('File %d' % i) + fnames.append(fname) + + listdir_is = sorted(os.listdir(mnt_dir)) + listdir_should = sorted(os.listdir(src_dir)) + assert listdir_is == listdir_should + + for fname in fnames: + stat_src = os.stat(pjoin(src_dir, fname)) + stat_mnt = os.stat(pjoin(mnt_dir, fname)) + assert stat_src.st_mtime == stat_mnt.st_mtime + assert stat_src.st_ctime == stat_mnt.st_ctime + assert stat_src.st_size == stat_mnt.st_size + os.unlink(pjoin(src_dir, fname)) + +def test_truncate_path(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + assert len(TEST_DATA) > 1024 + + filename = pjoin(mnt_dir, name_generator()) + with open(filename, 'wb') as fh: + fh.write(TEST_DATA) + + fstat = os.stat(filename) + size = fstat.st_size + assert size == len(TEST_DATA) + + # Add zeros at the end + os.truncate(filename, size + 1024) + assert os.stat(filename).st_size == size + 1024 + with open(filename, 'rb') as fh: + assert fh.read(size) == TEST_DATA + assert fh.read(1025) == b'\0' * 1024 + + # Truncate data + os.truncate(filename, size - 1024) + assert os.stat(filename).st_size == size - 1024 + with open(filename, 'rb') as fh: + assert fh.read(size) == TEST_DATA[:size-1024] + + os.unlink(filename) + +def test_truncate_fd(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + assert len(TEST_DATA) > 1024 + with NamedTemporaryFile('w+b', 0, dir=mnt_dir) as fh: + fd = fh.fileno() + fh.write(TEST_DATA) + fstat = os.fstat(fd) + size = fstat.st_size + assert size == len(TEST_DATA) + + # Add zeros at the end + os.ftruncate(fd, size + 1024) + assert os.fstat(fd).st_size == size + 1024 + fh.seek(0) + assert fh.read(size) == TEST_DATA + assert fh.read(1025) == b'\0' * 1024 + + # Truncate data + os.ftruncate(fd, size - 1024) + assert os.fstat(fd).st_size == size - 1024 + fh.seek(0) + assert fh.read(size) == TEST_DATA[:size-1024] + +def test_passthrough(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + assert name not in os.listdir(src_dir) + assert name not in os.listdir(mnt_dir) + with open(src_name, 'w') as fh: + fh.write('Hello, world') + assert name in os.listdir(src_dir) + assert name in os.listdir(mnt_dir) + assert os.stat(src_name) == os.stat(mnt_name) + + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + assert name not in os.listdir(src_dir) + assert name not in os.listdir(mnt_dir) + with open(mnt_name, 'w') as fh: + fh.write('Hello, world') + assert name in os.listdir(src_dir) + assert name in os.listdir(mnt_dir) + assert os.stat(src_name) == os.stat(mnt_name) + +@pytest.mark.skipif(no_xattr_support, reason="no xattr support") +@pytest.mark.parametrize("symlink", (False, True)) +def test_listxattr(setup_unreliablefs, symlink): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + os_create(mnt_name) + linkname = name_generator() + link_path = os.path.join(mnt_dir, linkname) + os.symlink(mnt_dir, link_path) + if symlink: + target = link_path + else: + target = mnt_name + + attr1_name = b"user.aa" + attr1_value = b"a" + attr2_name = b"user.bb" + attr2_value = b"b" + + num_attrs = len(os.listxattr(target)) + os.setxattr(target, attr1_name, attr1_value) + assert attr1_name.decode("utf-8") in os.listxattr(target) + os.setxattr(target, attr2_name, attr2_value) + assert attr2_name.decode("utf-8") in os.listxattr(target) + assert num_attrs + 2 == len(os.listxattr(target)) + +@pytest.mark.skipif(no_xattr_support, reason="no xattr support") +@pytest.mark.parametrize("symlink", + (False, + pytest.param(True, marks=pytest.mark.xfail(reason="gh-50")), + )) +def test_getxattr(setup_unreliablefs, symlink): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + os_create(mnt_name) + linkname = name_generator() + link_path = os.path.join(mnt_dir, linkname) + os.symlink(mnt_dir, link_path) + if symlink: + target = link_path + else: + target = mnt_name + + attr_value = b"unreliablefs" + attr_name = b"user.fsname" + + os.setxattr(target, attr_name, attr_value) + assert attr_name.decode("utf-8") in os.listxattr(target) + assert os.getxattr(target, attr_name) == attr_value + os.setxattr(target, attr_name, b"hello") + assert os.getxattr(target, attr_name) == b"hello" + +@pytest.mark.skipif(no_xattr_support, reason="no xattr support") +@pytest.mark.parametrize("symlink", (False, True)) +def test_setxattr(setup_unreliablefs, symlink): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + os_create(mnt_name) + linkname = name_generator() + link_path = os.path.join(mnt_dir, linkname) + os.symlink(mnt_dir, link_path) + if symlink: + target = link_path + else: + target = mnt_name + + attr_value = b"unreliablefs" + attr_name = b"user.fsname" + + os.setxattr(target, attr_name, attr_value) + assert attr_name.decode("utf-8") in os.listxattr(target) + +@pytest.mark.skipif(no_xattr_support, reason="no xattr support") +@pytest.mark.parametrize("symlink", + (pytest.param(True, marks=pytest.mark.xfail(reason="gh-50")), + pytest.param(False, marks=pytest.mark.xfail(reason="gh-50")), + )) +def test_removexattr(setup_unreliablefs, symlink): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(mnt_dir, name) + os_create(mnt_name) + linkname = name_generator() + link_path = os.path.join(mnt_dir, linkname) + os.symlink(mnt_dir, link_path) + if symlink: + target = link_path + else: + target = mnt_name + + attr_value = b"unreliablefs" + attr_name = b"user.fsname" + + os.setxattr(target, attr_name, attr_value) + assert attr_name.decode("utf-8") in os.listxattr(target) + assert os.getxattr(target, attr_name) == attr_value + os.removexattr(target, attr_name) + try: + assert os.getxattr(target, attr_name) == None + except OSError: + pass + +def test_flock(setup_unreliablefs): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + os_create(mnt_name) + + with open(mnt_name, 'w') as fh: + fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) + fcntl.flock(fh, fcntl.LOCK_UN) + +@pytest.mark.parametrize("symlink", (False, True)) +def test_utimens(setup_unreliablefs, symlink): + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(mnt_dir, name) + os_create(mnt_name) + linkname = name_generator() + link_path = os.path.join(mnt_dir, linkname) + os.symlink(mnt_name, link_path) + if symlink: + target = link_path + else: + target = mnt_name + + fstat = os.lstat(link_path) + link_atime = fstat.st_atime + link_mtime = fstat.st_mtime + + fstat = os.lstat(mnt_name) + mnt_name_atime = fstat.st_atime + 10 + mnt_name_mtime = fstat.st_mtime + 10 + os.utime(target, (mnt_name_atime, mnt_name_mtime)) + + fstat = os.lstat(mnt_name) + assert fstat.st_atime == mnt_name_atime + assert fstat.st_mtime == mnt_name_mtime + + if symlink: + fstat = os.lstat(link_path) + assert fstat.st_atime == link_atime + assert fstat.st_mtime == link_mtime + +@pytest.mark.long +def test_fsx(setup_unreliablefs): + fsx_bin = shutil.which('fsx') or pjoin(basedir, 'build/unreliablefs/tests/fsx') + if not fsx_bin: + pytest.skip('fsx is required to execute testcase') + + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(mnt_dir, name) + os_create(mnt_name) + cmd_line = '{} -N 10000 -d -W -c 4 {}'.format(fsx_bin, mnt_name) + subprocess.check_call(cmd_line.split(' ')) + +@pytest.mark.long +def test_fio(setup_unreliablefs): + if not shutil.which('fio'): + pytest.skip('fio is required to execute testcase') + + mnt_dir, src_dir = setup_unreliablefs + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(mnt_dir, name) + os_create(mnt_name) + cmd_line = "fio --name=random-write --ioengine=sync --rw=randwrite " \ + "--bs=1m --size=1G --numjobs=1 --iodepth=1 --runtime=60 " \ + "--time_based --end_fsync=1 --filename={}".format(mnt_name) + subprocess.check_call(cmd_line.split(' ')) blob - /dev/null blob + 97cfa9e4ae0d808aabb5a1cf5e610aa80749e07c (mode 644) --- /dev/null +++ unreliablefs/tests/util.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +import subprocess +import pytest +import os +import sys +import stat +import time +from os.path import join as pjoin + +basedir = pjoin(os.path.dirname(__file__), '../..') + +def is_no_fusermount(): + return sys.platform.startswith("freebsd") or \ + sys.platform == "darwin" + +no_fusermount_support = is_no_fusermount() + +def wait_for_mount(mount_process, mnt_dir, + test_fn=os.path.ismount): + elapsed = 0 + while elapsed < 30: + if test_fn(mnt_dir): + return True + if mount_process.poll() is not None: + pytest.fail('file system process terminated prematurely') + time.sleep(0.1) + elapsed += 0.1 + pytest.fail("mountpoint failed to come up") + +def cleanup(mount_process, mnt_dir): + if no_fusermount_support: + subprocess.call(['umount', mnt_dir], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT) + else: + subprocess.call(['fusermount', '-u', '-z', mnt_dir], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT) + mount_process.terminate() + try: + mount_process.wait(1) + except subprocess.TimeoutExpired: + mount_process.kill() + +def umount(mount_process, mnt_dir): + if no_fusermount_support: + subprocess.check_call(['umount', mnt_dir]) + else: + subprocess.check_call(['fusermount', '-u', '-z', mnt_dir]) + assert not os.path.ismount(mnt_dir) + + # Give mount process a little while to terminate. Popen.wait(timeout) + # was only added in 3.3... + elapsed = 0 + while elapsed < 30: + code = mount_process.poll() + if code is not None: + if code == 0: + return + pytest.fail('file system process terminated with code %s' % (code,)) + time.sleep(0.1) + elapsed += 0.1 + pytest.fail('mount process did not terminate') + +def safe_sleep(secs): + '''Like time.sleep(), but sleep for at least *secs* + + `time.sleep` may sleep less than the given period if a signal is + received. This function ensures that we sleep for at least the + desired time. + ''' + + now = time.time() + end = now + secs + while now < end: + time.sleep(end - now) + now = time.time() + +def fuse_test_marker(): + '''Return a pytest.marker that indicates FUSE availability + + If system/user/environment does not support FUSE, return + a `pytest.mark.skip` object with more details. If FUSE is + supported, return `pytest.mark.uses_fuse()`. + ''' + + skip = lambda x: pytest.mark.skip(reason=x) + + if not no_fusermount_support: + which = subprocess.Popen(['which', 'fusermount'], stdout=subprocess.PIPE, universal_newlines=True) + fusermount_path = which.communicate()[0].strip() + + if not fusermount_path or which.returncode != 0: + return skip("Can't find fusermount executable") + + mode = os.stat(fusermount_path).st_mode + if mode & stat.S_ISUID == 0: + return skip('fusermount executable not setuid, and we are not root.') + + if not os.path.exists('/dev/fuse'): + return skip("FUSE kernel module does not seem to be loaded") + + if os.getuid() == 0: + return pytest.mark.uses_fuse() + + try: + fd = os.open('/dev/fuse', os.O_RDWR) + except OSError as exc: + return skip('Unable to open /dev/fuse: %s' % exc.strerror) + else: + os.close(fd) + + return pytest.mark.uses_fuse() + +if os.environ.get('TEST_WITH_VALGRIND', 'no').lower().strip() \ + not in ('no', 'false', '0'): + base_cmdline = [ 'valgrind', '-q', '--' ] +else: + base_cmdline = [] blob - /dev/null blob + 51fdc822a71ffae9f3a772beaa456a669f63adc8 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs.1 @@ -0,0 +1,126 @@ +.\" Copyright (c) 2020 Sergey Bronnikov +.\" +.Dd $Mdocdate: November 03 2020 $ +.Dt UNRELIABLEFS 1 +.Os +.Sh NAME +.Nm unreliablefs +.Nd a FUSE-based fault-injecting filesystem +.Sh SYNOPSIS +.Nm +mountpoint +.Op Fl basedir Ar path +.Op Fl seed Ar number +.Op Fl hvdf +.Sh DESCRIPTION +The +.Nm +is a filesystem that allows to inject errors on file operations. +Without configuration it works as pass-through filesystem and redirects file +operations to a file objects on a real filesystem. +.Pp +.Nm +uses Filesystem in Userspace (FUSE) that allows easy setup without requiring a +kernel recompile or new kernel modules. +Without any other configuration, any files in a mounted directory will be +available unchanged. +To mount filesystem it is required to specify mountpoint and after mount it +will contain the same file tree as a root filesystem. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl basedir Ar path +Specify path to a directory that should be mount. +.It Fl seed Ar number +Specify a seed. +.It Fl f +Do not daemonize. +.It Fl d +Do not daemonize. +If this option is specified, +.Nm +will run in the foreground and log to +.Em stderr . +.It Fl v +Show version. +.It Fl h +Show usage. +.El +.Pp +Supported file operations are: +.Xr access 2 , +.Xr chmod 2 , +.Xr chown 2 , +.Xr creat 2 , +.Xr fallocate 2 , +.Xr flock 2 , +.Xr fsync 2 , +.Xr ftruncate 2 , +.Xr getxattr 2 , +.Xr ioctl 2 , +.Xr link 2 , +.Xr listxattr 2 , +.Xr lock 2 , +.Xr lstat 2 , +.Xr mkdir 2 , +.Xr mknod 2 , +.Xr open 2 , +.Xr read 2 , +.Xr readdir 2 , +.Xr readlink 2 , +.Xr removexattr 2 , +.Xr rename 2 , +.Xr rmdir 2 , +.Xr setxattr 2 , +.Xr statfs 2 , +.Xr symlink 2 , +.Xr truncate 2 , +.Xr unlink 2 , +.Xr utimensat 2 , +.Xr write 2 . +.Pp +Following functions are unsupported on OpenBSD: +.Xr removexattr 2 , +.Xr setxattr 2 , +.Xr getxattr 2 , +.Xr listxattr 2 , +.Xr flock 2 , +.Xr fallocate 2 . +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +.Bd -literal + +$ mkdir /tmp/fs +$ unreliablefs /tmp/fs -basedir=/tmp -seed=1618680646 +$ cat << EOF > /tmp/fs/unreliablefs.conf +[errinj_noop] +op_regexp = .* +path_regexp = .* +probability = 30 +EOF +$ ls -la +$ umount /tmp/fs + +.Ed +.Sh SEE ALSO +.Xr fusermount 1 , +.Xr errno 2 , +.Xr fuse 4 , +.Xr fuse 8 , +.Xr unreliablefs.conf 5 , +.Xr mount.fuse 8 +.Sh AUTHORS +.An -nosplit +The +.Nm +utility was written by +.An Sergey +.An Bronnikov . +.Sh CAVEATS +Faults can be injected before a start of a file operation, and cannot be +injected say in a middle of file operation. +So if a file operation has been successfully started then +.Nm +will not affect it and final result entirely depends on a base filesystem and +application that started file operation. blob - /dev/null blob + 5a189875711f07639a788e8f85cc069c83bf8154 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs.c @@ -0,0 +1,190 @@ +#define FUSE_USE_VERSION 29 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "unreliablefs_ops.h" +#include "unreliablefs.h" + +extern struct err_inj_q *config_init(const char* conf_path); +extern void config_delete(struct err_inj_q *config); + +struct unreliablefs_config conf; + +static struct fuse_operations unreliable_ops = { + .getattr = unreliable_getattr, + .readlink = unreliable_readlink, + .mknod = unreliable_mknod, + .mkdir = unreliable_mkdir, + .unlink = unreliable_unlink, + .rmdir = unreliable_rmdir, + .symlink = unreliable_symlink, + .rename = unreliable_rename, + .link = unreliable_link, + .chmod = unreliable_chmod, + .chown = unreliable_chown, + .truncate = unreliable_truncate, + .open = unreliable_open, + .read = unreliable_read, + .write = unreliable_write, + .statfs = unreliable_statfs, + .flush = unreliable_flush, + .release = unreliable_release, + .fsync = unreliable_fsync, +#ifdef HAVE_XATTR + .setxattr = unreliable_setxattr, + .getxattr = unreliable_getxattr, + .listxattr = unreliable_listxattr, + .removexattr = unreliable_removexattr, +#endif /* HAVE_XATTR */ + .opendir = unreliable_opendir, + .readdir = unreliable_readdir, + .releasedir = unreliable_releasedir, + .fsyncdir = unreliable_fsyncdir, + + .init = unreliable_init, + .destroy = unreliable_destroy, + + .access = unreliable_access, + .create = unreliable_create, + .ftruncate = unreliable_ftruncate, + .fgetattr = unreliable_fgetattr, + .lock = unreliable_lock, +#if !defined(__OpenBSD__) + .ioctl = unreliable_ioctl, +#endif /* __OpenBSD__ */ +#ifdef HAVE_FLOCK + .flock = unreliable_flock, +#endif /* HAVE_FLOCK */ +#ifdef HAVE_FALLOCATE + .fallocate = unreliable_fallocate, +#endif /* HAVE_FALLOCATE */ +#ifdef HAVE_UTIMENSAT + .utimens = unreliable_utimens, +#endif /* HAVE_UTIMENSAT */ +}; + +enum { + KEY_HELP, + KEY_VERSION, + KEY_DEBUG, +}; + +#define UNRELIABLEFS_OPT(t, p, v) { t, offsetof(struct unreliablefs_config, p), v } +#define UNRELIABLEFS_VERSION "0.1" + +static struct fuse_opt unreliablefs_opts[] = { + UNRELIABLEFS_OPT("-seed=%u", seed, 0), + UNRELIABLEFS_OPT("-basedir=%s", basedir, 0), + + FUSE_OPT_KEY("-d", KEY_DEBUG), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("-v", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("subdir", FUSE_OPT_KEY_DISCARD), + FUSE_OPT_KEY("modules=", FUSE_OPT_KEY_DISCARD), + FUSE_OPT_END +}; + +static int unreliablefs_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) +{ + switch (key) { + case KEY_HELP: + fprintf(stderr, + "usage: unreliablefs mountpoint [options]\n\n" + "general options:\n" + " -h --help print help\n" + " -v --version print version\n" + " -d enable debug output (implies -f)\n" + " -f foreground operation\n\n" + "unreliablefs options:\n" + " -seed=NUM random seed\n" + " -basedir=STRING directory to mount\n\n"); + exit(1); + + case KEY_VERSION: + fprintf(stderr, "unreliablefs version %s\n", UNRELIABLEFS_VERSION); + fuse_opt_add_arg(outargs, "--version"); + fuse_main(outargs->argc, outargs->argv, &unreliable_ops, NULL); + exit(1); + } + return 1; +} + +int is_dir(const char *path) { + struct stat statbuf; + if (stat(path, &statbuf) != 0) { + return 0; + } + + return S_ISDIR(statbuf.st_mode); +} + +int main(int argc, char *argv[]) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + memset(&conf, 0, sizeof(conf)); + conf.seed = time(0); + conf.basedir = "/"; + fuse_opt_parse(&args, &conf, unreliablefs_opts, unreliablefs_opt_proc); + srand(conf.seed); + fprintf(stdout, "random seed = %d\n", conf.seed); + + if (is_dir(conf.basedir) == 0) { + fprintf(stderr, "basedir ('%s') is not a directory\n", conf.basedir); + fuse_opt_free_args(&args); + return EXIT_FAILURE; + } + char subdir_option[PATH_MAX]; + sprintf(subdir_option, "-omodules=subdir,subdir=%s", conf.basedir); + fuse_opt_add_arg(&args, subdir_option); + /* build config_path */ + char *real_path = realpath(conf.basedir, NULL); + if (!real_path) { + perror("realpath"); + fuse_opt_free_args(&args); + return EXIT_FAILURE; + } + conf.basedir = real_path; + size_t sz = strlen(DEFAULT_CONF_NAME) + strlen(conf.basedir) + 2; + conf.config_path = malloc(sz); + if (!conf.config_path) { + perror("malloc"); + fuse_opt_free_args(&args); + return EXIT_FAILURE; + } + /* read configuration file on start */ + snprintf(conf.config_path, sz, "%s/%s", conf.basedir, DEFAULT_CONF_NAME); + conf.errors = config_init(conf.config_path); + if (!conf.errors) { + fprintf(stdout, "error injections are not configured!\n"); + } + if (pthread_mutex_init(&conf.mutex, NULL) != 0) { + fuse_opt_free_args(&args); + perror("pthread_mutex_init"); + return EXIT_FAILURE; + } + + fprintf(stdout, "starting FUSE filesystem unreliablefs\n"); + int ret = fuse_main(args.argc, args.argv, &unreliable_ops, NULL); + + /* cleanup */ + fuse_opt_free_args(&args); + config_delete(conf.errors); + if (conf.config_path) + free(conf.config_path); + if (!ret) { + fprintf(stdout, "random seed = %d\n", conf.seed); + } + + return ret; +} blob - /dev/null blob + 624bc25cbc29c1d549c4793cb4762e4fffb54cb9 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs.conf.5 @@ -0,0 +1,87 @@ +.\" Copyright (c) 2021 Sergey Bronnikov +.\" +.Dd $Mdocdate: April 15 2021 $ +.Dt UNRELIABLEFS.CONF 5 +.Os +.Sh NAME +.Nm unreliablefs.conf +.Nd format of the configuration file used by +.Xr unreliablefs 1 +.Sh DESCRIPTION +The configuration file format is quite simple. +Sections are delimited by square brackets: +.Pp +.Rs +[Section] +.Re +.Pp +And options within brackets sections are simple key value pairs: +.Pp +.Rs +Option = Value +.Re +.Sh OPTIONS +Per-fault-injection customizable variables are specified within sections +with section names matching the fault-injection name. +.Pp +Supported fault injections are: +.Bl -tag -width Ds +.It Cm errinj_noop +File operation replaced with no-op. +.It Cm errinj_kill_caller +Send SIGKILL signal to a process that invoked file operation. +.It Cm errinj_errno +Set random errno. +.Xr errno 2 +limited by supported errno's. +.It Cm errinj_slowdown +File operation slowdown for nanoseconds specified by duration parameter. +.El +.Pp +The options are: +.Bl -tag -width Ds +.It Cm op_regexp +Sets the regular expression that matches file operation for what fault injection is applicable. +Option uses format of regular expressions described in +.Xr re_format 7 . +POSIX Extended Regular Expression syntax is supported and regular expressions do not differentiate case. +.It Cm path_regexp +Sets the regular expression that matches paths where fault injection is applicable. +Option uses format of regular expressions described in +.Xr re_format 7 . +POSIX Extended Regular Expression syntax is supported and regular expressions do not differentiate case. +.It Cm probability +Sets the probability in percents. +Probability equal to 0 means that error injection will never happen. +Probability equal to 100 means that error injection will happen on each file operation. +.It Cm duration +Sets the duration of file operation slowdown. Applicable to errinj_slowdown only. +.El +.Sh EXAMPLES +.Bd -literal + +[errinj_noop] +path_regexp = .* +op_regexp = .* +probability = 70 + +[errinj_errno] +path_regexp = *.xlog +probability = 4 + +.Ed +.Sh SEE ALSO +.Xr unreliablefs 1 , +.Xr errno 2 , +.Xr syslog 3 , +.Xr re_format 7 +.Sh AUTHORS +.An -nosplit +The +.Xr unreliablefs 1 +utility was written by +.An Sergey +.An Bronnikov . +.\" .Sh HISTORY +.\" .Sh BUGS +.\" .Sh CAVEATS blob - /dev/null blob + 89c638be28aab091ffdb3908720f955f39fa9ec2 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs.h @@ -0,0 +1,18 @@ +#ifndef UNRELIABLEFS_HH +#define UNRELIABLEFS_HH + +#include /* PATH_MAX */ +#include + +#define DEFAULT_CONF_NAME "unreliablefs.conf" + +typedef struct unreliablefs_config { + struct err_inj_q *errors; + char *basedir; + char *config_path; + unsigned int seed; + unsigned int debug; + pthread_mutex_t mutex; +} unreliablefs_config; + +#endif /* UNRELIABLEFS_HH */ blob - /dev/null blob + a71c63da063644a19fa0b53b3485807c6d165773 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_errinj.c @@ -0,0 +1,358 @@ +#define FUSE_USE_VERSION 29 + +#include +#include /* basename() and dirname() */ +#include +#include +#include +#include +#include + +#include +#include + +#include "conf.h" +#include "unreliablefs.h" +#include "unreliablefs_errinj.h" + +static int rand_range(int, int); +int error_inject(const char* path, fuse_op operation); + +extern struct unreliablefs_config conf; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define RANDOM_ELEMENT(arr) \ + (arr[rand_range(0, ARRAY_SIZE(arr))]); + +static int op_random_errno(int op_n) +{ + int rc = -1; + switch (op_n) { + case OP_LSTAT: + rc = RANDOM_ELEMENT(errno_lstat); + break; + case OP_GETATTR: + rc = RANDOM_ELEMENT(errno_lstat); + break; + case OP_READLINK: + rc = RANDOM_ELEMENT(errno_readlink); + break; + case OP_MKNOD: + rc = RANDOM_ELEMENT(errno_mknod); + break; + case OP_MKDIR: + rc = RANDOM_ELEMENT(errno_mkdir); + break; + case OP_UNLINK: + rc = RANDOM_ELEMENT(errno_unlink); + break; + case OP_RMDIR: + rc = RANDOM_ELEMENT(errno_rmdir); + break; + case OP_SYMLINK: + rc = RANDOM_ELEMENT(errno_symlink); + break; + case OP_RENAME: + rc = RANDOM_ELEMENT(errno_rename); + break; + case OP_LINK: + rc = RANDOM_ELEMENT(errno_link); + break; + case OP_CHMOD: + rc = RANDOM_ELEMENT(errno_chmod); + break; + case OP_CHOWN: + rc = RANDOM_ELEMENT(errno_chmod); + break; + case OP_TRUNCATE: + rc = RANDOM_ELEMENT(errno_truncate); + break; + case OP_OPEN: + rc = RANDOM_ELEMENT(errno_creat); + break; + case OP_READ: + rc = RANDOM_ELEMENT(errno_read); + break; + case OP_WRITE: + rc = RANDOM_ELEMENT(errno_write); + break; + case OP_STATFS: + rc = RANDOM_ELEMENT(errno_statfs); + break; + case OP_FLUSH: + rc = RANDOM_ELEMENT(errno_close); + break; + case OP_RELEASE: + rc = RANDOM_ELEMENT(errno_close); + break; + case OP_FSYNC: + rc = RANDOM_ELEMENT(errno_fsync); + break; +#ifdef HAVE_XATTR + case OP_SETXATTR: + rc = RANDOM_ELEMENT(errno_setxattr); + break; + case OP_GETXATTR: + rc = RANDOM_ELEMENT(errno_getxattr); + break; + case OP_LISTXATTR: + rc = RANDOM_ELEMENT(errno_listxattr); + break; + case OP_REMOVEXATTR: + rc = RANDOM_ELEMENT(errno_removexattr); + break; +#endif /* HAVE_XATTR */ + case OP_OPENDIR: + rc = RANDOM_ELEMENT(errno_opendir); + break; + case OP_READDIR: + rc = RANDOM_ELEMENT(errno_readdir); + break; + case OP_RELEASEDIR: + rc = RANDOM_ELEMENT(errno_close); + break; + case OP_FSYNCDIR: + rc = RANDOM_ELEMENT(errno_fsync); + break; + case OP_ACCESS: + rc = RANDOM_ELEMENT(errno_access); + break; + case OP_CREAT: + rc = RANDOM_ELEMENT(errno_creat); + break; + case OP_FTRUNCATE: + rc = RANDOM_ELEMENT(errno_ftruncate); + break; + case OP_FGETATTR: + rc = RANDOM_ELEMENT(errno_lstat); + break; + case OP_LOCK: + rc = RANDOM_ELEMENT(errno_fcntl); + break; +#if !defined(__OpenBSD__) + case OP_IOCTL: + rc = RANDOM_ELEMENT(errno_ioctl); + break; +#endif /* __OpenBSD__ */ +#ifdef HAVE_FLOCK + case OP_FLOCK: + rc = RANDOM_ELEMENT(errno_flock); + break; +#endif /* HAVE_FLOCK */ +#ifdef HAVE_FALLOCATE + case OP_FALLOCATE: + rc = RANDOM_ELEMENT(errno_fallocate); + break; +#endif /* HAVE_FALLOCATE */ +#ifdef HAVE_UTIMENSAT + case OP_UTIMENS: + rc = RANDOM_ELEMENT(errno_utimensat); + break; +#endif /* HAVE_UTIMENSAT */ + default: + fprintf(stderr, "Unsupported operation (%s)\n", fuse_op_name[op_n]); + } + + return rc; +} + +static int rand_range(int min_n, int max_n) +{ + return rand() % (max_n - min_n + 1) + min_n; +} + +int error_inject(const char* path, fuse_op operation) +{ + /* instead of returning an error in 'errno', the operation should return + * the negated error value (-errno) directly. + */ + int rc = -0; + struct errinj_conf *err; + /* read configuration file on change */ + pthread_mutex_lock(&conf.mutex); + if (strcmp(path, conf.config_path) == 0) { + config_delete(conf.errors); + conf.errors = config_init(path); + goto cleanup; + } + if (!conf.errors) { + goto cleanup; + } + + /* apply error injections defined in configuration one by one */ + TAILQ_FOREACH(err, conf.errors, entries) { + unsigned int p = rand_range(MIN_PROBABLITY, MAX_PROBABLITY); + if (!(p <= err->probability)) { + fprintf(stderr, "errinj '%s' skipped: probability (%d) is not matched\n", + errinj_name[err->type], err->probability); + continue; + } + const char* op_name = fuse_op_name[operation]; + if (is_regex_matched(err->path_regexp, path) != 0) { + fprintf(stderr, "errinj '%s' skipped: path_regexp (%s) is not matched\n", + errinj_name[err->type], err->path_regexp); + continue; + } + if (is_regex_matched(err->op_regexp, op_name) != 0) { + fprintf(stderr, "errinj '%s' skipped: op_regexp (%s) is not matched\n", + errinj_name[err->type], err->op_regexp); + continue; + } + fprintf(stdout, "%s triggered on operation '%s', %s\n", + errinj_name[err->type], op_name, path); + switch (err->type) { + case ERRINJ_NOOP: + rc = -ERRNO_NOOP; + break; + case ERRINJ_KILL_CALLER: ; + struct fuse_context *cxt = fuse_get_context(); + if (cxt) { + int ret = kill(cxt->pid, DEFAULT_SIGNAL_NAME); + if (ret == -1) { + perror("kill"); + } + fprintf(stdout, "send signal %s to TID %d\n", + strsignal(DEFAULT_SIGNAL_NAME), cxt->pid); + } + break; + case ERRINJ_ERRNO: + rc = op_random_errno(operation); + fprintf(stdout, "errno '%s'\n", strerror(rc)); + rc = -rc; + break; + case ERRINJ_SLOWDOWN: ; + struct timespec ts = {}; + ts.tv_nsec = err->duration; + fprintf(stdout, "start of '%s' slowdown for '%d' ns\n", op_name, err->duration); + if (nanosleep(&ts, NULL) != 0) { + perror("nanosleep"); + } else { + fprintf(stdout, "end of '%s' slowdown with '%d' ns\n", op_name, err->duration); + } + break; + } + } + +cleanup: + pthread_mutex_unlock(&conf.mutex); + return rc; +} + +static errinj_type errinj_type_by_name(const char *name) +{ + int idx = -1; + int n_elem = sizeof(errinj_name)/sizeof(errinj_name[0]); + for (int i = 0; i < n_elem; i++) { + if (strcmp(errinj_name[i], name) == 0) + idx = i; + } + + return idx; +} + +struct err_inj_q *config_init(const char* conf_path) { + fprintf(stdout, "read configuration %s\n", conf_path); + struct err_inj_q *errors = calloc(1, sizeof(struct err_inj_q)); + if (!errors) { + perror("calloc"); + return NULL; + } + TAILQ_INIT(errors); /* initialize queue */ + if (access(conf_path, F_OK ) == 0) { + if (ini_parse(conf_path, conf_option_handler, errors) < 0) { + fprintf(stderr, "can't load '%s'\n", conf_path); + return NULL; + } + } + + return errors; +} + +void config_delete(struct err_inj_q *errors) { + if (!errors) { + return; + } + errinj_conf *err; + /* delete configuration of error injections */ + while ((err= TAILQ_FIRST(errors))) { + if (err->path_regexp) + free((char*)err->path_regexp); + if (err->op_regexp) + free((char*)err->op_regexp); + TAILQ_REMOVE(errors, err, entries); + free(err); + } + free(errors); +} + +int conf_option_handler(void* cfg, const char* section, + const char* key, const char* value) +{ + errinj_conf *err = NULL; + int cur_type = errinj_type_by_name(section); + if (cur_type == -1) { + fprintf(stderr, "unsupported error injection type '%s'", section); + return -1; + } + errinj_conf *np; + int is_errinj_found = 0; + TAILQ_FOREACH(np, (struct err_inj_q *)cfg, entries) { + if (np->type == (errinj_type)cur_type) { + err = np; + is_errinj_found = 1; + break; + } + } + if (!err) { + if ((err = calloc(1, sizeof(struct errinj_conf))) == NULL) { + perror("calloc"); + return -1; + } + err->type = cur_type; + } + + if (is_errinj_found != 1) { + TAILQ_INSERT_TAIL((struct err_inj_q *)cfg, err, entries); + fprintf(stdout, "enabled error injection %s\n", section); + } + fprintf(stdout, "[%s] %s = %s\n", section, key, value); + if (strcmp(key, "path_regexp") == 0) { + if (err->path_regexp) + free(err->path_regexp); + err->path_regexp = strdup(value); + } else if (strcmp(key, "op_regexp") == 0) { + if (err->op_regexp) + free(err->op_regexp); + err->op_regexp = strdup(value); + } else if (strcmp(key, "probability") == 0) { + err->probability = atoi(value); + } else if (strcmp(key, "duration") == 0) { + err->duration = atoi(value); + } else { + fprintf(stderr, "unknown option '%s' in configuration file\n", key); + return 0; + } + + return 1; +} + +int is_regex_matched(const char *regex, const char *string) { + if (!regex || !string) + return 0; + regex_t reg; + regmatch_t match[1]; + int rc = 0; + rc = regcomp(®, regex, REG_ICASE | REG_EXTENDED); + if (rc != 0) { + perror("regcomp"); + regfree(®); + return rc; + } + rc = regexec(®, string, 1, match, 0); + if (rc != 0) { + perror("regexec"); + } + regfree(®); + return rc; +} blob - /dev/null blob + 2dcc775ca5c9a010de8149d51d0bc8a92deee037 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_errinj.h @@ -0,0 +1,65 @@ +#ifndef ERRINJ_HH +#define ERRINJ_HH + +#include +#include + +#include "unreliablefs_ops.h" + +#if defined(__linux__) + #include "unreliablefs_errno_linux.h" +#elif defined(__FreeBSD__) + #include "unreliablefs_errno_freebsd.h" +#elif defined(__OpenBSD__) + #include "unreliablefs_errno_openbsd.h" +#elif defined(__APPLE__) + #include "unreliablefs_errno_macosx.h" +#else +#error Unsupported platform +#endif + +#define MAX_ERRINJ_NAME_LENGTH 20 +#define MIN_PROBABLITY 0 +#define MAX_PROBABLITY 100 +#define ERRNO_NOOP -999 +#define DEFAULT_SIGNAL_NAME SIGKILL + +int error_inject(const char* path, fuse_op operation); +struct err_inj_q *config_init(const char* conf_path); +void config_delete(struct err_inj_q *config); +int conf_option_handler(void* cfg, const char* section, + const char* name, const char* value); +int is_regex_matched(const char *regex, const char *string); + +const char *errinj_name[] = +{ + "errinj_errno", + "errinj_kill_caller", + "errinj_noop", + "errinj_slowdown", +}; + +typedef enum { + ERRINJ_ERRNO, + ERRINJ_KILL_CALLER, + ERRINJ_NOOP, + ERRINJ_SLOWDOWN, +} errinj_type; + +typedef struct errinj_conf errinj_conf; + +struct errinj_conf { + char *err_injection_name; + char *op_regexp; + char *path_regexp; + char *errno_regexp; + unsigned int probability; + unsigned int duration; + errinj_type type; + + TAILQ_ENTRY(errinj_conf) entries; +}; + +TAILQ_HEAD(err_inj_q, errinj_conf); + +#endif /* ERRINJ_HH */ blob - /dev/null blob + 711e8b618bdef4265233295bd82628ee76bed207 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_errno_freebsd.h @@ -0,0 +1,405 @@ +#ifndef ERRNO_HH +#define ERRNO_HH + +#include +#include + +static const int errno_access[] = { + EACCES, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EROFS, + ETXTBSY, +}; + +static const int errno_chmod[] = { + EACCES, + EFAULT, + EFTYPE, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EPERM, + EROFS, +}; + +static const int errno_creat[] = { + EACCES, + EBADF, + ECAPMODE, + EDQUOT, + EEXIST, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINTR, + EINVAL, + EIO, + EISDIR, + ELOOP, + EMFILE, + EMLINK, + ENAMETOOLONG, + ENFILE, + ENOENT, + ENOSPC, + ENOTCAPABLE, + ENOTDIR, + ENXIO, + EOPNOTSUPP, + EPERM, + EROFS, + ETXTBSY, + EWOULDBLOCK, +}; + +static const int errno_ftruncate[] = { + EACCES, + EBADF, + EFAULT, + EFBIG, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, + ETXTBSY, +}; + +static const int errno_link[] = { + EACCES, + EBADF, + EDQUOT, + EEXIST, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EOPNOTSUPP, + EPERM, + EROFS, + EXDEV, +}; + +static const int errno_mkdir[] = { + EACCES, + EBADF, + EDQUOT, + EEXIST, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EIO, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_mknod[] = { + EACCES, + EBADF, + EDQUOT, + EEXIST, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_opendir[] = { + EACCES, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_readdir[] = { + EBADF, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, +}; + +static const int errno_read[] = { + EAGAIN, + EBADF, + EBUSY, + ECONNRESET, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINTR, + EINVAL, + EIO, + EISDIR, + EOPNOTSUPP, + EOVERFLOW, +}; + +static const int errno_readlink[] = { + EACCES, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_rename[] = { + EACCES, + ECAPMODE, + EDQUOT, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, + EXDEV, +}; + +static const int errno_rmdir[] = { + EACCES, + EBUSY, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, +}; + +static const int errno_symlink[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_truncate[] = { + EACCES, + EFAULT, + EFBIG, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, + ETXTBSY, +}; + +static const int errno_unlink[] = { + EACCES, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_write[] = { + EAGAIN, + EBADF, + EDQUOT, + EFAULT, + EFBIG, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINTR, + EINVAL, + EIO, + ENOSPC, + EPIPE, + EROFS, +}; + +static const int errno_lstat[] = { + EACCES, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EOVERFLOW, +}; + +static const int errno_statfs[] = { + EACCES, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_close[] = { + EBADF, + EINTR, + ENOSPC, + ECONNRESET, +}; + +static const int errno_utimensat[] = { + EACCES, + EBADF, + EFAULT, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, + ESRCH, +}; + +static const int errno_fsync[] = { + EBADF, +#if __FreeBSD__ >= 12 + EINTEGRITY, +#endif + EINVAL, + EIO, +}; + +static int errno_flock[] = { + EBADF, + EINVAL, + ENOLCK, + EOPNOTSUPP, + EWOULDBLOCK, +}; + +static int errno_ioctl[] = { + EBADF, + EFAULT, + EINVAL, + ENOTTY, +}; + +static const int errno_fcntl[] = { + EAGAIN, + EBADF, + EDEADLK, + EINTR, + EINVAL, + EMFILE, + ENOLCK, + ENOTTY, + EOPNOTSUPP, + EOVERFLOW, + EPERM, + ESRCH, +}; + +#endif /* ERRNO_HH */ blob - /dev/null blob + 3f92badc7e061cce8680ba07a4ebad1fa4a3b550 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_errno_linux.h @@ -0,0 +1,431 @@ +#ifndef ERRNO_HH +#define ERRNO_HH + +#include + +static const int errno_access[] = { + EACCES, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EROFS, + EFAULT, + EINVAL, + EIO, + ENOMEM, + ETXTBSY, +}; + +static const int errno_chmod[] = { + EACCES, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_creat[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EFBIG, + EINTR, + EINVAL, + EISDIR, + ELOOP, + EMFILE, + ENAMETOOLONG, + ENFILE, + ENODEV, + ENOENT, + ENOMEM, + ENOSPC, + ENOTDIR, + ENXIO, + EOPNOTSUPP, + EOVERFLOW, + EPERM, + EROFS, + ETXTBSY, + EWOULDBLOCK, +}; + +static const int errno_ftruncate[] = { + EACCES, + EBADF, + EFAULT, + EFBIG, + EINTR, + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOTDIR, + EPERM, + EROFS, + ETXTBSY, +}; + +static const int errno_link[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, + EXDEV, +}; + +static const int errno_mkdir[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EINVAL, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_mknod[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EINVAL, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOSPC, + ENOTDIR, + EPERM, + EROFS , +}; + +static const int errno_opendir[] = { + EACCES, + EBADF, + EMFILE, + ENOENT, + ENOMEM, + ENOTDIR, +}; + +static const int errno_readdir[] = { + EBADF, +}; + +static const int errno_read[] = { + EAGAIN, + EWOULDBLOCK, + EBADF, + EFAULT, + EINTR, + EINVAL, + EIO, + EISDIR, +}; + +static const int errno_readlink[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, +}; + +static const int errno_rename[] = { + EACCES, + EBUSY, + EDQUOT, + EEXIST, + EFAULT, + EINVAL, + EISDIR, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOSPC, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, +}; + +static const int errno_rmdir[] = { + EACCES, + EBUSY, + EFAULT, + EINVAL, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, +}; + +static const int errno_symlink[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_truncate[] = { + EACCES, + EFAULT, + EFBIG, + EINTR, + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, + ETXTBSY, +}; + +static const int errno_unlink[] = { + EACCES, + EBUSY, + EFAULT, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, +}; + +static const int errno_write[] = { + EAGAIN, + EWOULDBLOCK, + EBADF, + EDESTADDRREQ, + EDQUOT, + EFAULT, + EFBIG, + EINTR, + EINVAL, + ENOSPC, + EPERM, + EPIPE, +}; + +static const int errno_lstat[] = { + EACCES, + EBADF, + EFAULT, + EINVAL, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, + EOVERFLOW, +}; + +static const int errno_statfs[] = { + EACCES, + EFAULT, + EINTR, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOSYS, + ENOTDIR, + EOVERFLOW, +}; + +static const int errno_close[] = { + EBADF, + EDQUOT, + EINTR, + EIO, + ENOSPC, +}; + +static const int errno_utimensat[] = { + EACCES, + EBADF, + EFAULT, + EINVAL, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, + ESRCH, +}; + +static const int errno_fsync[] = { + EBADF, + EDQUOT, + EINVAL, + EIO, + ENOSPC, + EROFS, +}; + +static const int errno_setxattr[] = { + EDQUOT, + EEXIST, + ENODATA, + ENOSPC, + ENOTSUP, + EPERM, + ERANGE, + /* In addition, the errors documented in stat(2) can also occur: */ + EACCES, + EBADF, + EFAULT, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, + EOVERFLOW, +}; + +static const int errno_getxattr[] = { + E2BIG, + ENODATA, + ENOTSUP, + ERANGE, + /* In addition, the errors documented in stat(2) can also occur: */ + EACCES, + EBADF, + EFAULT, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, + EOVERFLOW, +}; + +static const int errno_listxattr[] = { + E2BIG, + ENOTSUP, + ERANGE, + /* In addition, the errors documented in stat(2) can also occur: */ + EACCES, + EBADF, + EFAULT, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, + EOVERFLOW, +}; + +static const int errno_removexattr[] = { + ENODATA, + ENOTSUP, + /* In addition, the errors documented in stat(2) can also occur: */ + EACCES, + EBADF, + EFAULT, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOMEM, + ENOTDIR, + EOVERFLOW, +}; + +static int errno_flock[] = { + EBADF, + EINTR, + EINVAL, + ENOLCK, + EWOULDBLOCK, +}; + +static int errno_fallocate[] = { + EBADF, + EFBIG, + EINTR, + EINVAL, + EIO, + ENODEV, + ENOSPC, + ENOSYS, + EOPNOTSUPP, + EPERM, + ESPIPE, + ETXTBSY, +}; + +static int errno_ioctl[] = { + EBADF, + EFAULT, + EINVAL, + ENOTTY, +}; + +static const int errno_fcntl[] = { + EACCES, + EAGAIN, + EBADF, + EBUSY, + EDEADLK, + EFAULT, + EINTR, + EINVAL, + EMFILE, + ENOLCK, + ENOTDIR, + EPERM, +}; + +#endif /* ERRNO_HH */ blob - /dev/null blob + fb43f0c628d54629cb1357cf32effbc0989aff9d (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_errno_macosx.h @@ -0,0 +1,375 @@ +#ifndef ERRNO_HH +#define ERRNO_HH + +#include + +static const int errno_access[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EROFS, + ETXTBSY, +}; + +static const int errno_chmod[] = { + EACCES, + EFAULT, + EINTR, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_creat[] = { + EACCES, + EAGAIN, + EDQUOT, + EEXIST, + EFAULT, + EINTR, + EINVAL, + EIO, + EISDIR, + ELOOP, + EMFILE, + ENAMETOOLONG, + ENFILE, + ENOENT, + ENOSPC, + ENOTDIR, + ENXIO, + EOPNOTSUPP, + EOVERFLOW, + EROFS, + ETXTBSY, +}; + +static const int errno_ftruncate[] = { + EBADF, + EFBIG, + EINTR, + EINVAL, + EIO, + EROFS, +}; + +static const int errno_link[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, + EXDEV, +}; + +static const int errno_mkdir[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EROFS, +}; + +static const int errno_mknod[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_opendir[] = { + /* TODO */ +}; + +static const int errno_readdir[] = { + /* TODO */ +}; + +static const int errno_read[] = { + EAGAIN, + EBADF, + ECONNRESET, + EFAULT, + EINTR, + EINVAL, + EIO, + EISDIR, + ENOBUFS, + ENOMEM, + ENOTCONN, + ENXIO, + ETIMEDOUT, +}; + +static const int errno_readlink[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_rename[] = { + EACCES, + EDQUOT, + EFAULT, + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, + EXDEV, +}; + +static const int errno_rmdir[] = { + EACCES, + EBUSY, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, +}; + +static const int errno_symlink[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EROFS, +}; + +static const int errno_truncate[] = { + EACCES, + EFAULT, + EFBIG, + EINTR, + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EROFS, + ETXTBSY, +}; + +static const int errno_unlink[] = { + EACCES, + EBUSY, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_write[] = { + EAGAIN, + EBADF, + ECONNRESET, + EDQUOT, + EFAULT, + EFBIG, + EINTR, + EINVAL, + EIO, + ENETDOWN, + ENETUNREACH, + ENOSPC, + ENXIO, + EPIPE, + EWOULDBLOCK, +}; + +static const int errno_lstat[] = { + EACCES, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EOVERFLOW, +}; + +static const int errno_statfs[] = { + EACCES, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_close[] = { + EBADF, + EINTR, + EIO, +}; + +static const int errno_utimensat[] = { + EACCES, + EBADF, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, + ESRCH, +}; + +static const int errno_fsync[] = { + EBADF, + EINTR, + EINVAL, + EIO, +}; + +static const int errno_setxattr[] = { + E2BIG, + EACCES, + EEXIST, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOATTR, + ENOSPC, + ENOTDIR, + ENOTSUP, + EPERM, + ERANGE, + EROFS, +}; + +static const int errno_getxattr[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOATTR, + ENOTDIR, + ENOTSUP, + EPERM, + ERANGE, +}; + +static const int errno_listxattr[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOTDIR, + ENOTSUP, + EPERM, + ERANGE, +}; + +static const int errno_removexattr[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOATTR, + ENOTDIR, + ENOTSUP, + EPERM, + EROFS, +}; + +static int errno_flock[] = { + EBADF, + EINVAL, + ENOTSUP, + EWOULDBLOCK, +}; + +static int errno_ioctl[] = { + EBADF, + EINVAL, + ENOTTY, +}; + +static const int errno_fcntl[] = { + EACCES, + EBADF, + EDEADLK, + EINTR, + EINVAL, + EMFILE, + ENOLCK, + EOVERFLOW, + ESRCH, +}; + +#endif /* ERRNO_HH */ blob - /dev/null blob + 1c3de8c666a38829824f51691dc2dae07c010a33 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_errno_openbsd.h @@ -0,0 +1,342 @@ +#ifndef ERRNO_HH +#define ERRNO_HH + +#include + +static const int errno_access[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, + ETXTBSY, +}; + +static const int errno_chmod[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_creat[] = { + EACCES, + EBUSY, + EDQUOT, + EEXIST, + EFAULT, + EINTR, + EINVAL, + EIO, + EISDIR, + ELOOP, + EMFILE, + ENAMETOOLONG, + ENFILE, + ENOENT, + ENOSPC, + ENOTDIR, + ENXIO, + EOPNOTSUPP, + EPERM, + EROFS, + ETXTBSY, + EWOULDBLOCK, +}; + +static const int errno_ftruncate[] = { + EBADF, + EFBIG, + EINVAL, + EIO, +}; + +static const int errno_link[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EOPNOTSUPP, + EPERM, + EROFS, + EXDEV, +}; + +static const int errno_mkdir[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EROFS, +}; + +static const int errno_mknod[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_opendir[] = { + /* opendir(2) */ + ENOTDIR, + /* fcntl(2) */ + EAGAIN, + EBADF, + EDEADLK, + EINTR, + EINVAL, + EMFILE, + ENOLCK, + EOVERFLOW, + ESRCH, + /* fcstat(2) */ + EBADF, + EFAULT, + EIO, + /* open(2) */ + EACCES, + EBUSY, + EDQUOT, + EEXIST, + EFAULT, + EINTR, + EINVAL, + EIO, + EISDIR, + ELOOP, + EMFILE, + ENAMETOOLONG, + ENFILE, + ENOENT, + ENOSPC, + ENOTDIR, + ENXIO, + EOPNOTSUPP, + EPERM, + EROFS, + ETXTBSY, + EWOULDBLOCK, + /* malloc(2) */ + ENOMEM, +}; + +static const int errno_readdir[] = { + EBADF, + EFAULT, + EINVAL, + EIO, +}; + +static const int errno_read[] = { + EAGAIN, + EBADF, + EFAULT, + EINTR, + EINVAL, + EIO, + EISDIR, + ENOTCONN, +}; + +static const int errno_readlink[] = { + EACCES, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_rename[] = { + EACCES, + EDQUOT, + EFAULT, + EINVAL, + EIO, + EISDIR, + ELOOP, + EMLINK, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, + EXDEV, +}; + +static const int errno_rmdir[] = { + EACCES, + EBUSY, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + ENOTEMPTY, + EPERM, + EROFS, +}; + +static const int errno_symlink[] = { + EACCES, + EDQUOT, + EEXIST, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOSPC, + ENOTDIR, + EROFS, +}; + +static const int errno_truncate[] = { + EACCES, + EFAULT, + EFBIG, + EINVAL, + EIO, + EISDIR, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EROFS, + ETXTBSY, +}; + +static const int errno_unlink[] = { + EACCES, + EBUSY, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_write[] = { + EAGAIN, + EBADF, + EDESTADDRREQ, + EDQUOT, + EFAULT, + EFBIG, + EINTR, + EINVAL, + EIO, + ENETDOWN, + ENOSPC, + EPIPE, +}; + +static const int errno_lstat[] = { + EACCES, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_statfs[] = { + EACCES, + EFAULT, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, +}; + +static const int errno_close[] = { + EBADF, + EINTR, + EIO, +}; + +static const int errno_utimensat[] = { + EACCES, + EBADF, + EFAULT, + EINVAL, + EIO, + ELOOP, + ENAMETOOLONG, + ENOENT, + ENOTDIR, + EPERM, + EROFS, +}; + +static const int errno_fsync[] = { + EBADF, + EINVAL, + EIO, +}; + +static int errno_flock[] = { + EBADF, + EINVAL, + EOPNOTSUPP, + EWOULDBLOCK, +}; + +static const int errno_fcntl[] = { + EAGAIN, + EBADF, + EDEADLK, + EINTR, + EINVAL, + EMFILE, + ENOLCK, + EOVERFLOW, + ESRCH, +}; + +#endif /* ERRNO_HH */ blob - /dev/null blob + 8c628a99b38a49bcf68f45d5e432af82b9801e70 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_ops.c @@ -0,0 +1,846 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_XATTR +#include +#endif /* HAVE_XATTR */ + +#ifdef linux +/* For pread()/pwrite()/utimensat() */ +#define _XOPEN_SOURCE 700 +#endif + +#define ERRNO_NOOP -999 + +#include "unreliablefs_ops.h" + +const char *fuse_op_name[] = { + "getattr", + "readlink", + "mknod", + "mkdir", + "unlink", + "rmdir", + "symlink", + "rename", + "link", + "chmod", + "chown", + "truncate", + "open", + "read", + "write", + "statfs", + "flush", + "release", + "fsync", +#ifdef HAVE_XATTR + "setxattr", + "getxattr", + "listxattr", + "removexattr", +#endif /* HAVE_XATTR */ + "opendir", + "readdir", + "releasedir", + "fsyncdir", + "access", + "creat", + "ftruncate", + "fgetattr", + "lock", +#if !defined(__OpenBSD__) + "ioctl", +#endif /* __OpenBSD__ */ +#ifdef HAVE_FLOCK + "flock", +#endif /* HAVE_FLOCK */ +#ifdef HAVE_FALLOCATE + "fallocate", +#endif /* HAVE_FALLOCATE */ +#ifdef HAVE_UTIMENSAT + "utimens", +#endif /* HAVE_UTIMENSAT */ + "lstat" +}; + +extern int error_inject(const char* path, fuse_op operation); + +int unreliable_lstat(const char *path, struct stat *buf) +{ + int ret = error_inject(path, OP_LSTAT); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + memset(buf, 0, sizeof(struct stat)); + if (lstat(path, buf) == -1) { + return -errno; + } + + return 0; +} + +int unreliable_getattr(const char *path, struct stat *buf) +{ + int ret = error_inject(path, OP_GETATTR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + memset(buf, 0, sizeof(struct stat)); + if (lstat(path, buf) == -1) { + return -errno; + } + + return 0; +} + +int unreliable_readlink(const char *path, char *buf, size_t bufsiz) +{ + int ret = error_inject(path, OP_READLINK); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = readlink(path, buf, bufsiz); + if (ret == -1) { + return -errno; + } + buf[ret] = 0; + + return 0; +} + +int unreliable_mknod(const char *path, mode_t mode, dev_t dev) +{ + int ret = error_inject(path, OP_MKNOD); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = mknod(path, mode, dev); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_mkdir(const char *path, mode_t mode) +{ + int ret = error_inject(path, OP_MKDIR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = mkdir(path, mode); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_unlink(const char *path) +{ + int ret = error_inject(path, OP_UNLINK); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = unlink(path); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_rmdir(const char *path) +{ + int ret = error_inject(path, OP_RMDIR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = rmdir(path); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_symlink(const char *target, const char *linkpath) +{ + int ret = error_inject(target, OP_SYMLINK); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = symlink(target, linkpath); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_rename(const char *oldpath, const char *newpath) +{ + int ret = error_inject(oldpath, OP_RENAME); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = rename(oldpath, newpath); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_link(const char *oldpath, const char *newpath) +{ + int ret = error_inject(oldpath, OP_LINK); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = link(oldpath, newpath); + if (ret < 0) { + return -errno; + } + + return 0; +} + +int unreliable_chmod(const char *path, mode_t mode) +{ + int ret = error_inject(path, OP_CHMOD); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = chmod(path, mode); + if (ret < 0) { + return -errno; + } + + return 0; +} + +int unreliable_chown(const char *path, uid_t owner, gid_t group) +{ + int ret = error_inject(path, OP_CHOWN); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = chown(path, owner, group); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_truncate(const char *path, off_t length) +{ + int ret = error_inject(path, OP_TRUNCATE); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = truncate(path, length); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_open(const char *path, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_OPEN); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = open(path, fi->flags); + if (ret == -1) { + return -errno; + } + fi->fh = ret; + + return 0; +} + +int unreliable_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_READ); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + int fd; + + if (fi == NULL) { + fd = open(path, O_RDONLY); + } else { + fd = fi->fh; + } + + if (fd == -1) { + return -errno; + } + + ret = pread(fd, buf, size, offset); + if (ret == -1) { + ret = -errno; + } + + if (fi == NULL) { + close(fd); + } + + return ret; +} + +int unreliable_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_WRITE); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + int fd; + (void) fi; + if(fi == NULL) { + fd = open(path, O_WRONLY); + } else { + fd = fi->fh; + } + + if (fd == -1) { + return -errno; + } + + ret = pwrite(fd, buf, size, offset); + if (ret == -1) { + ret = -errno; + } + + if(fi == NULL) { + close(fd); + } + + return ret; +} + +int unreliable_statfs(const char *path, struct statvfs *buf) +{ + int ret = error_inject(path, OP_STATFS); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = statvfs(path, buf); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_flush(const char *path, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_FLUSH); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = close(dup(fi->fh)); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_release(const char *path, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_RELEASE); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = close(fi->fh); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_fsync(const char *path, int datasync, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_FSYNC); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + if (datasync) { + ret = fdatasync(fi->fh); + if (ret == -1) { + return -errno; + } + } else { + ret = fsync(fi->fh); + if (ret == -1) { + return -errno; + } + } + + return 0; +} + +#ifdef HAVE_XATTR +int unreliable_setxattr(const char *path, const char *name, + const char *value, size_t size, int flags) +{ + int ret = error_inject(path, OP_SETXATTR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + +#ifdef __APPLE__ + ret = setxattr(path, name, value, size, 0, flags); +#else + ret = setxattr(path, name, value, size, flags); +#endif /* __APPLE__ */ + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_getxattr(const char *path, const char *name, + char *value, size_t size) +{ + int ret = error_inject(path, OP_GETXATTR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + +#ifdef __APPLE__ + ret = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); +#else + ret = getxattr(path, name, value, size); +#endif /* __APPLE__ */ + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_listxattr(const char *path, char *list, size_t size) +{ + int ret = error_inject(path, OP_LISTXATTR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + +#ifdef __APPLE__ + ret = listxattr(path, list, size, XATTR_NOFOLLOW); +#else + ret = listxattr(path, list, size); +#endif /* __APPLE__ */ + if (ret == -1) { + return -errno; + } + + return ret; +} + +int unreliable_removexattr(const char *path, const char *name) +{ + int ret = error_inject(path, OP_REMOVEXATTR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + +#ifdef __APPLE__ + ret = removexattr(path, name, XATTR_NOFOLLOW); +#else + ret = removexattr(path, name); +#endif /* __APPLE__ */ + if (ret == -1) { + return -errno; + } + + return 0; +} +#endif /* HAVE_XATTR */ + +int unreliable_opendir(const char *path, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_OPENDIR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + DIR *dir = opendir(path); + + if (!dir) { + return -errno; + } + fi->fh = (int64_t) dir; + + return 0; +} + +int unreliable_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_READDIR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + DIR *dp = opendir(path); + if (dp == NULL) { + return -errno; + } + struct dirent *de; + + (void) offset; + (void) fi; + + while ((de = readdir(dp)) != NULL) { + struct stat st; + memset(&st, 0, sizeof(st)); + st.st_ino = de->d_ino; + st.st_mode = de->d_type << 12; + if (filler(buf, de->d_name, &st, 0)) + break; + } + closedir(dp); + + return 0; +} + +int unreliable_releasedir(const char *path, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_RELEASEDIR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + DIR *dir = (DIR *) fi->fh; + + ret = closedir(dir); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_fsyncdir(const char *path, int datasync, struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_FSYNCDIR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + DIR *dir = opendir(path); + if (!dir) { + return -errno; + } + + if (datasync) { + ret = fdatasync(dirfd(dir)); + if (ret == -1) { + return -errno; + } + } else { + ret = fsync(dirfd(dir)); + if (ret == -1) { + return -errno; + } + } + closedir(dir); + + return 0; +} + +void *unreliable_init(struct fuse_conn_info *conn) +{ + return NULL; +} + +void unreliable_destroy(void *private_data) +{ + +} + +int unreliable_access(const char *path, int mode) +{ + int ret = error_inject(path, OP_ACCESS); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = access(path, mode); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_create(const char *path, mode_t mode, + struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_CREAT); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = open(path, fi->flags, mode); + if (ret == -1) { + return -errno; + } + fi->fh = ret; + + return 0; +} + +int unreliable_ftruncate(const char *path, off_t length, + struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_FTRUNCATE); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = truncate(path, length); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_fgetattr(const char *path, struct stat *buf, + struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_FGETATTR); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = fstat((int) fi->fh, buf); + if (ret == -1) { + return -errno; + } + + return 0; +} + +int unreliable_lock(const char *path, struct fuse_file_info *fi, int cmd, + struct flock *fl) +{ + int ret = error_inject(path, OP_LOCK); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = fcntl((int) fi->fh, cmd, fl); + if (ret == -1) { + return -errno; + } + + return 0; +} + +#if !defined(__OpenBSD__) +int unreliable_ioctl(const char *path, int cmd, void *arg, + struct fuse_file_info *fi, + unsigned int flags, void *data) +{ + int ret = error_inject(path, OP_IOCTL); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = ioctl(fi->fh, cmd, arg); + if (ret == -1) { + return -errno; + } + + return ret; +} +#endif /* __OpenBSD__ */ + +#ifdef HAVE_FLOCK +int unreliable_flock(const char *path, struct fuse_file_info *fi, int op) +{ + int ret = error_inject(path, OP_FLOCK); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + ret = flock(((int) fi->fh), op); + if (ret == -1) { + return -errno; + } + + return 0; +} +#endif /* HAVE_FLOCK */ + +#ifdef HAVE_FALLOCATE +int unreliable_fallocate(const char *path, int mode, + off_t offset, off_t len, + struct fuse_file_info *fi) +{ + int ret = error_inject(path, OP_FALLOCATE); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + int fd; + (void) fi; + + if (mode) { + return -EOPNOTSUPP; + } + + if(fi == NULL) { + fd = open(path, O_WRONLY); + } else { + fd = fi->fh; + } + + if (fd == -1) { + return -errno; + } + + ret = fallocate((int) fi->fh, mode, offset, len); + if (ret == -1) { + return -errno; + } + + if(fi == NULL) { + close(fd); + } + + return 0; +} +#endif /* HAVE_FALLOCATE */ + +#ifdef HAVE_UTIMENSAT +int unreliable_utimens(const char *path, const struct timespec ts[2]) +{ + int ret = error_inject(path, OP_UTIMENS); + if (ret == -ERRNO_NOOP) { + return 0; + } else if (ret) { + return ret; + } + + /* don't use utime/utimes since they follow symlinks */ + ret = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); + if (ret == -1) { + return -errno; + } + + return 0; +} +#endif /* HAVE_UTIMENSAT */ blob - /dev/null blob + b23f58f43f8313d2c6a2396adb3d7cde20daf3b7 (mode 644) --- /dev/null +++ unreliablefs/unreliablefs_ops.h @@ -0,0 +1,125 @@ +#ifndef UNRELIABLEFS_OPS_HH +#define UNRELIABLEFS_OPS_HH + +#define FUSE_USE_VERSION 29 + +#include + +int unreliable_getattr(const char *, struct stat *); +int unreliable_readlink(const char *, char *, size_t); +int unreliable_mknod(const char *, mode_t, dev_t); +int unreliable_mkdir(const char *, mode_t); +int unreliable_unlink(const char *); +int unreliable_rmdir(const char *); +int unreliable_symlink(const char *, const char *); +int unreliable_rename(const char *, const char *); +int unreliable_link(const char *, const char *); +int unreliable_chmod(const char *, mode_t); +int unreliable_chown(const char *, uid_t, gid_t); +int unreliable_truncate(const char *, off_t); +int unreliable_open(const char *, struct fuse_file_info *); +int unreliable_read(const char *, char *, size_t, off_t, + struct fuse_file_info *); +int unreliable_write(const char *, const char *, size_t, off_t, + struct fuse_file_info *); +int unreliable_statfs(const char *, struct statvfs *); +int unreliable_flush(const char *, struct fuse_file_info *); +int unreliable_release(const char *, struct fuse_file_info *); +int unreliable_fsync(const char *, int, struct fuse_file_info *); + +#ifdef HAVE_XATTR +int unreliable_setxattr(const char *, const char *, const char *, size_t, int); +int unreliable_getxattr(const char *, const char *, char *, size_t); +int unreliable_listxattr(const char *, char *, size_t); +int unreliable_removexattr(const char *, const char *); +#endif /* HAVE_XATTR */ + +int unreliable_opendir(const char *, struct fuse_file_info *); +int unreliable_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi); +int unreliable_releasedir(const char *, struct fuse_file_info *); +int unreliable_fsyncdir(const char *, int, struct fuse_file_info *); + +void *unreliable_init(struct fuse_conn_info *conn); +void unreliable_destroy(void *private_data); + +int unreliable_lstat(const char *path, struct stat *statbuf); +int unreliable_access(const char *, int); +int unreliable_create(const char *, mode_t, struct fuse_file_info *); +int unreliable_ftruncate(const char *, off_t, struct fuse_file_info *); +int unreliable_fgetattr(const char *, struct stat *, struct fuse_file_info *); +int unreliable_lock(const char *, struct fuse_file_info *, int cmd, + struct flock *); +#if !defined(__OpenBSD__) +int unreliable_ioctl(const char *, int cmd, void *arg, + struct fuse_file_info *, unsigned int flags, void *data); +#endif +int unreliable_write_buf(const char *, struct fuse_bufvec *buf, off_t off, + struct fuse_file_info *); +int unreliable_read_buf(const char *, struct fuse_bufvec **bufp, + size_t size, off_t off, struct fuse_file_info *); +#ifdef HAVE_FLOCK +int unreliable_flock(const char *, struct fuse_file_info *, int op); +#endif /* HAVE_FLOCK */ +#ifdef HAVE_FALLOCATE +int unreliable_fallocate(const char *, int, off_t, off_t, + struct fuse_file_info *); +#endif /* HAVE_FALLOCATE */ + +#ifdef HAVE_UTIMENSAT +int unreliable_utimens(const char *path, const struct timespec ts[2]); +#endif /* HAVE_UTIMENSAT */ + +typedef enum { + OP_GETATTR, + OP_READLINK, + OP_MKNOD, + OP_MKDIR, + OP_UNLINK, + OP_RMDIR, + OP_SYMLINK, + OP_RENAME, + OP_LINK, + OP_CHMOD, + OP_CHOWN, + OP_TRUNCATE, + OP_OPEN, + OP_READ, + OP_WRITE, + OP_STATFS, + OP_FLUSH, + OP_RELEASE, + OP_FSYNC, +#ifdef HAVE_XATTR + OP_SETXATTR, + OP_GETXATTR, + OP_LISTXATTR, + OP_REMOVEXATTR, +#endif /* HAVE_XATTR */ + OP_OPENDIR, + OP_READDIR, + OP_RELEASEDIR, + OP_FSYNCDIR, + OP_ACCESS, + OP_CREAT, + OP_FTRUNCATE, + OP_FGETATTR, + OP_LOCK, +#if !defined(__OpenBSD__) + OP_IOCTL, +#endif /* __OpenBSD__ */ +#ifdef HAVE_FLOCK + OP_FLOCK, +#endif /* HAVE_FLOCK */ +#ifdef HAVE_FALLOCATE + OP_FALLOCATE, +#endif /* HAVE_FALLOCATE */ +#ifdef HAVE_UTIMENSAT + OP_UTIMENS, +#endif /* HAVE_UTIMENSAT */ + OP_LSTAT +} fuse_op; + +extern const char *fuse_op_name[]; + +#endif /* UNRELIABLEFS_OPS_HH */ blob - 78015d141e6878398e001790f28c480d66679edd (mode 644) blob + /dev/null --- conf.h +++ /dev/null @@ -1,157 +0,0 @@ -/* inih -- simple .INI file parser - -SPDX-License-Identifier: BSD-3-Clause - -Copyright (C) 2009-2020, Ben Hoyt - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#ifndef INI_H -#define INI_H - -/* Make this header file easier to include in C++ code */ -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Nonzero if ini_handler callback should accept lineno parameter. */ -#ifndef INI_HANDLER_LINENO -#define INI_HANDLER_LINENO 0 -#endif - -/* Typedef for prototype of handler function. */ -#if INI_HANDLER_LINENO -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value, - int lineno); -#else -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value); -#endif - -/* Typedef for prototype of fgets-style reader function. */ -typedef char* (*ini_reader)(char* str, int num, void* stream); - -/* Parse given INI-style file. May have [section]s, name=value pairs - (whitespace stripped), and comments starting with ';' (semicolon). Section - is "" if name=value pair parsed before any section heading. name:value - pairs are also supported as a concession to Python's configparser. - - For each name=value pair parsed, call handler function with given user - pointer as well as section, name, and value (data only valid for duration - of handler call). Handler should return nonzero on success, zero on error. - - Returns 0 on success, line number of first error on parse error (doesn't - stop on first error), -1 on file open error, or -2 on memory allocation - error (only when INI_USE_STACK is zero). -*/ -int ini_parse(const char* filename, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't - close the file when it's finished -- the caller must do that. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes an ini_reader function pointer instead of - filename. Used for implementing custom or string-based I/O (see also - ini_parse_string). */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user); - -/* Same as ini_parse(), but takes a zero-terminated string with the INI data -instead of a file. Useful for parsing INI data from a network socket or -already in memory. */ -int ini_parse_string(const char* string, ini_handler handler, void* user); - -/* Nonzero to allow multi-line value parsing, in the style of Python's - configparser. If allowed, ini_parse() will call the handler with the same - name for each subsequent line parsed. */ -#ifndef INI_ALLOW_MULTILINE -#define INI_ALLOW_MULTILINE 1 -#endif - -/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of - the file. See https://github.com/benhoyt/inih/issues/21 */ -#ifndef INI_ALLOW_BOM -#define INI_ALLOW_BOM 1 -#endif - -/* Chars that begin a start-of-line comment. Per Python configparser, allow - both ; and # comments at the start of a line by default. */ -#ifndef INI_START_COMMENT_PREFIXES -#define INI_START_COMMENT_PREFIXES ";#" -#endif - -/* Nonzero to allow inline comments (with valid inline comment characters - specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match - Python 3.2+ configparser behaviour. */ -#ifndef INI_ALLOW_INLINE_COMMENTS -#define INI_ALLOW_INLINE_COMMENTS 1 -#endif -#ifndef INI_INLINE_COMMENT_PREFIXES -#define INI_INLINE_COMMENT_PREFIXES ";" -#endif - -/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ -#ifndef INI_USE_STACK -#define INI_USE_STACK 1 -#endif - -/* Maximum line length for any line in INI file (stack or heap). Note that - this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ -#ifndef INI_MAX_LINE -#define INI_MAX_LINE 200 -#endif - -/* Nonzero to allow heap line buffer to grow via realloc(), zero for a - fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is - zero. */ -#ifndef INI_ALLOW_REALLOC -#define INI_ALLOW_REALLOC 0 -#endif - -/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK - is zero. */ -#ifndef INI_INITIAL_ALLOC -#define INI_INITIAL_ALLOC 200 -#endif - -/* Stop parsing on first error (default is to keep parsing). */ -#ifndef INI_STOP_ON_FIRST_ERROR -#define INI_STOP_ON_FIRST_ERROR 0 -#endif - -/* Nonzero to call the handler at the start of each new section (with - name and value NULL). Default is to only call the handler on - each name=value pair. */ -#ifndef INI_CALL_HANDLER_ON_NEW_SECTION -#define INI_CALL_HANDLER_ON_NEW_SECTION 0 -#endif - -/* Nonzero to allow a name without a value (no '=' or ':' on the line) and - call the handler with value NULL in this case. Default is to treat - no-value lines as an error. */ -#ifndef INI_ALLOW_NO_VALUE -#define INI_ALLOW_NO_VALUE 0 -#endif - -/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory - allocation functions (INI_USE_STACK must also be 0). These functions must - have the same signatures as malloc/free/realloc and behave in a similar - way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ -#ifndef INI_CUSTOM_ALLOCATOR -#define INI_CUSTOM_ALLOCATOR 0 -#endif - - -#ifdef __cplusplus -} -#endif - -#endif /* INI_H */ blob - 4b8269a5fed7ce6874fc3737e4cbccf41287c45b (mode 644) blob + /dev/null --- tests/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -project(unreliablefs_test) - -add_executable(fsx "fsx.c") blob - 6808b548e4f50ad2155271235aea6670b54d4b0e (mode 644) blob + /dev/null --- tests/conftest.py +++ /dev/null @@ -1,99 +0,0 @@ -import sys -import pytest -import time -import re - -# If a test fails, wait a moment before retrieving the captured -# stdout/stderr. When using a server process, this makes sure that we capture -# any potential output of the server that comes *after* a test has failed. For -# example, if a request handler raises an exception, the server first signals an -# error to FUSE (causing the test to fail), and then logs the exception. Without -# the extra delay, the exception will go into nowhere. -@pytest.mark.hookwrapper -def pytest_pyfunc_call(pyfuncitem): - outcome = yield - failed = outcome.excinfo is not None - if failed: - time.sleep(1) - -@pytest.fixture() -def pass_capfd(request, capfd): - '''Provide capfd object to UnitTest instances''' - request.instance.capfd = capfd - -def check_test_output(capfd): - (stdout, stderr) = capfd.readouterr() - - # Write back what we've read (so that it will still be printed. - sys.stdout.write(stdout) - sys.stderr.write(stderr) - - # Strip out false positives - for (pattern, flags, count) in capfd.false_positives: - cp = re.compile(pattern, flags) - (stdout, cnt) = cp.subn('', stdout, count=count) - if count == 0 or count - cnt > 0: - stderr = cp.sub('', stderr, count=count - cnt) - - patterns = [ r'\b{}\b'.format(x) for x in - ('exception', 'error', 'warning', 'fatal', 'traceback', - 'fault', 'crash(?:ed)?', 'abort(?:ed)', - 'uninitiali[zs]ed') ] - patterns += ['^==[0-9]+== '] - for pattern in patterns: - cp = re.compile(pattern, re.IGNORECASE | re.MULTILINE) - # FIXME - """ - hit = cp.search(stderr) - if hit: - raise AssertionError('Suspicious output to stderr (matched "%s")' % hit.group(0)) - hit = cp.search(stdout) - if hit: - raise AssertionError('Suspicious output to stdout (matched "%s")' % hit.group(0)) - """ - -def register_output(self, pattern, count=1, flags=re.MULTILINE): - '''Register *pattern* as false positive for output checking - - This prevents the test from failing because the output otherwise - appears suspicious. - ''' - - self.false_positives.append((pattern, flags, count)) - -# This is a terrible hack that allows us to access the fixtures from the -# pytest_runtest_call hook. Among a lot of other hidden assumptions, it probably -# relies on tests running sequential (i.e., don't dare to use e.g. the xdist -# plugin) -current_capfd = None -@pytest.fixture(autouse=True) -def save_cap_fixtures(request, capfd): - global current_capfd - capfd.false_positives = [] - - type(capfd).register_output = register_output - - if request.config.getoption('capture') == 'no': - capfd = None - current_capfd = capfd - bak = current_capfd - yield - - # Try to catch problems with this hack (e.g. when running tests - # simultaneously) - assert bak is current_capfd - current_capfd = None - -@pytest.hookimpl(trylast=True) -def pytest_runtest_call(item): - capfd = current_capfd - if capfd is not None: - check_test_output(capfd) - -def pytest_configure(config): - config.addinivalue_line( - "markers", "uses_fuse: mark test to run only with FUSE subsystem" - ) - config.addinivalue_line( - "markers", "long: mark test that run longer than others" - ) blob - 384c3f486bc6312cb289664f15b4f512a1d7f11e (mode 644) blob + /dev/null --- tests/fsx.c +++ /dev/null @@ -1,1230 +0,0 @@ -/* - * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 2.0 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - * - * File: fsx.c - * Author: Avadis Tevanian, Jr. - * - * File system exerciser. - * - * Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com - * - * Various features from Joe Sokol, Pat Dirks, and Clark Warner. - * - * Small changes to work under Linux -- davej@suse.de - * - * Sundry porting patches from Guy Harris 12/2001 - * - * Checks for mmap last-page zero fill. - * - * Updated license to APSL 2.0, 2004/7/27 - Jordan Hubbard - * - * $FreeBSD$ - * - */ - -#include -#include -#ifdef _UWIN -# include -# include -# include -# include -#endif -#include -#include -#ifndef MAP_FILE -# define MAP_FILE 0 -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */ - -/* - * A log entry is an operation and a bunch of arguments. - */ - -struct log_entry { - int operation; - int args[3]; -}; - -#define LOGSIZE 1000 - -struct log_entry oplog[LOGSIZE]; /* the log */ -int logptr = 0; /* current position in log */ -int logcount = 0; /* total ops */ - -/* - * Define operations - */ - -#define OP_READ 1 -#define OP_WRITE 2 -#define OP_TRUNCATE 3 -#define OP_CLOSEOPEN 4 -#define OP_MAPREAD 5 -#define OP_MAPWRITE 6 -#define OP_SKIPPED 7 -#define OP_INVALIDATE 8 - -int page_size; -int page_mask; - -char *original_buf; /* a pointer to the original data */ -char *good_buf; /* a pointer to the correct data */ -char *temp_buf; /* a pointer to the current data */ -char *fname; /* name of our test file */ -int fd; /* fd for our test file */ - -off_t file_size = 0; -off_t biggest = 0; -char state[256]; -unsigned long testcalls = 0; /* calls to function "test" */ - -unsigned long simulatedopcount = 0; /* -b flag */ -int closeprob = 0; /* -c flag */ -int invlprob = 0; /* -i flag */ -int debug = 0; /* -d flag */ -unsigned long debugstart = 0; /* -D flag */ -unsigned long maxfilelen = 256 * 1024; /* -l flag */ -int sizechecks = 1; /* -n flag disables them */ -int maxoplen = 64 * 1024; /* -o flag */ -int quiet = 0; /* -q flag */ -unsigned long progressinterval = 0; /* -p flag */ -int readbdy = 1; /* -r flag */ -int style = 0; /* -s flag */ -int truncbdy = 1; /* -t flag */ -int writebdy = 1; /* -w flag */ -long monitorstart = -1; /* -m flag */ -long monitorend = -1; /* -m flag */ -int lite = 0; /* -L flag */ -long numops = -1; /* -N flag */ -int randomoplen = 1; /* -O flag disables it */ -int seed = 1; /* -S flag */ -int mapped_writes = 1; /* -W flag disables */ -int mapped_reads = 1; /* -R flag disables it */ -int mapped_msync = 1; /* -U flag disables */ -int fsxgoodfd = 0; -FILE * fsxlogf = NULL; -int badoff = -1; -int closeopen = 0; -int invl = 0; - - -void -vwarnc(code, fmt, ap) - int code; - const char *fmt; - va_list ap; -{ - fprintf(stderr, "fsx: "); - if (fmt != NULL) { - vfprintf(stderr, fmt, ap); - fprintf(stderr, ": "); - } - fprintf(stderr, "%s\n", strerror(code)); -} - - -void -warn(const char * fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vwarnc(errno, fmt, ap); - va_end(ap); -} - - -void -prt(char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vfprintf(stdout, fmt, args); - va_end(args); - - if (fsxlogf) { - va_start(args, fmt); - vfprintf(fsxlogf, fmt, args); - va_end(args); - } -} - -void -prterr(char *prefix) -{ - prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); -} - - -void -do_log4(int operation, int arg0, int arg1, int arg2) -{ - struct log_entry *le; - - le = &oplog[logptr]; - le->operation = operation; - le->args[0] = arg0; - le->args[1] = arg1; - le->args[2] = arg2; - logptr++; - logcount++; - if (logptr >= LOGSIZE) - logptr = 0; -} - - -void -log4(int operation, int arg0, int arg1, int arg2) -{ - do_log4(operation, arg0, arg1, arg2); - if (closeopen) - do_log4(OP_CLOSEOPEN, 0, 0, 0); - if (invl) - do_log4(OP_INVALIDATE, 0, 0, 0); -} - - -void -logdump(void) -{ - struct log_entry *lp; - int i, count, down, opnum; - - prt("LOG DUMP (%d total operations):\n", logcount); - if (logcount < LOGSIZE) { - i = 0; - count = logcount; - } else { - i = logptr; - count = LOGSIZE; - } - - opnum = i + 1 + (logcount/LOGSIZE)*LOGSIZE; - for ( ; count > 0; count--) { - lp = &oplog[i]; - - if (lp->operation == OP_CLOSEOPEN || - lp->operation == OP_INVALIDATE) { - switch (lp->operation) { - case OP_CLOSEOPEN: - prt("\t\tCLOSE/OPEN\n"); - break; - case OP_INVALIDATE: - prt("\t\tMS_INVALIDATE\n"); - break; - } - i++; - if (i == LOGSIZE) - i = 0; - continue; - } - - prt("%d(%d mod 256): ", opnum, opnum%256); - switch (lp->operation) { - case OP_MAPREAD: - prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && badoff < - lp->args[0] + lp->args[1]) - prt("\t***RRRR***"); - break; - case OP_MAPWRITE: - prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && badoff < - lp->args[0] + lp->args[1]) - prt("\t******WWWW"); - break; - case OP_READ: - prt("READ\t0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && - badoff < lp->args[0] + lp->args[1]) - prt("\t***RRRR***"); - break; - case OP_WRITE: - prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (lp->args[0] > lp->args[2]) - prt(" HOLE"); - else if (lp->args[0] + lp->args[1] > lp->args[2]) - prt(" EXTEND"); - if ((badoff >= lp->args[0] || badoff >=lp->args[2]) && - badoff < lp->args[0] + lp->args[1]) - prt("\t***WWWW"); - break; - case OP_TRUNCATE: - down = lp->args[0] < lp->args[1]; - prt("TRUNCATE %s\tfrom 0x%x to 0x%x", - down ? "DOWN" : "UP", lp->args[1], lp->args[0]); - if (badoff >= lp->args[!down] && - badoff < lp->args[!!down]) - prt("\t******WWWW"); - break; - case OP_SKIPPED: - prt("SKIPPED (no operation)"); - break; - default: - prt("BOGUS LOG ENTRY (operation code = %d)!", - lp->operation); - } - prt("\n"); - opnum++; - i++; - if (i == LOGSIZE) - i = 0; - } -} - - -void -save_buffer(char *buffer, off_t bufferlength, int fd) -{ - off_t ret; - ssize_t byteswritten; - - if (fd <= 0 || bufferlength == 0) - return; - - if (bufferlength > SSIZE_MAX) { - prt("fsx flaw: overflow in save_buffer\n"); - exit(67); - } - if (lite) { - off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END); - if (size_by_seek == (off_t)-1) - prterr("save_buffer: lseek eof"); - else if (bufferlength > size_by_seek) { - warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek, - (unsigned long long)bufferlength); - bufferlength = size_by_seek; - } - } - - ret = lseek(fd, (off_t)0, SEEK_SET); - if (ret == (off_t)-1) - prterr("save_buffer: lseek 0"); - - byteswritten = write(fd, buffer, (size_t)bufferlength); - if (byteswritten != bufferlength) { - if (byteswritten == -1) - prterr("save_buffer write"); - else - warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n", - (unsigned)byteswritten, - (unsigned long long)bufferlength); - } -} - - -void -report_failure(int status) -{ - logdump(); - - if (fsxgoodfd) { - if (good_buf) { - save_buffer(good_buf, file_size, fsxgoodfd); - prt("Correct content saved for comparison\n"); - prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", - fname, fname); - } - close(fsxgoodfd); - } - exit(status); -} - - -#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \ - *(((unsigned char *)(cp)) + 1))) - -void -check_buffers(unsigned offset, unsigned size) -{ - unsigned char c, t; - unsigned i = 0; - unsigned n = 0; - unsigned op = 0; - unsigned bad = 0; - - if (memcmp(good_buf + offset, temp_buf, size) != 0) { - prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n", - offset, size); - prt("OFFSET\tGOOD\tBAD\tRANGE\n"); - while (size > 0) { - c = good_buf[offset]; - t = temp_buf[i]; - if (c != t) { - if (n == 0) { - bad = short_at(&temp_buf[i]); - prt("0x%5x\t0x%04x\t0x%04x", offset, - short_at(&good_buf[offset]), bad); - op = temp_buf[offset & 1 ? i+1 : i]; - } - n++; - badoff = offset; - } - offset++; - i++; - size--; - } - if (n) { - prt("\t0x%5x\n", n); - if (bad) - prt("operation# (mod 256) for the bad data may be %u\n", ((unsigned)op & 0xff)); - else - prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n"); - } else - prt("????????????????\n"); - report_failure(110); - } -} - - -void -check_size(void) -{ - struct stat statbuf; - off_t size_by_seek; - - if (fstat(fd, &statbuf)) { - prterr("check_size: fstat"); - statbuf.st_size = -1; - } - size_by_seek = lseek(fd, (off_t)0, SEEK_END); - if (file_size != statbuf.st_size || file_size != size_by_seek) { - prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n", - (unsigned long long)file_size, - (unsigned long long)statbuf.st_size, - (unsigned long long)size_by_seek); - report_failure(120); - } -} - - -void -check_trunc_hack(void) -{ - struct stat statbuf; - - ftruncate(fd, (off_t)0); - ftruncate(fd, (off_t)100000); - fstat(fd, &statbuf); - if (statbuf.st_size != (off_t)100000) { - prt("no extend on truncate! not posix!\n"); - exit(130); - } - ftruncate(fd, (off_t)0); -} - - -void -doread(unsigned offset, unsigned size) -{ - off_t ret; - unsigned iret; - - offset -= offset % readbdy; - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size read\n"); - log4(OP_SKIPPED, OP_READ, offset, size); - return; - } - if (size + offset > file_size) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping seek/read past end of file\n"); - log4(OP_SKIPPED, OP_READ, offset, size); - return; - } - - log4(OP_READ, offset, size, 0); - - if (testcalls <= simulatedopcount) - return; - - if (!quiet && ((progressinterval && - testcalls % progressinterval == 0) || - (debug && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend)))))) - prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, - offset, offset + size - 1, size); - ret = lseek(fd, (off_t)offset, SEEK_SET); - if (ret == (off_t)-1) { - prterr("doread: lseek"); - report_failure(140); - } - iret = read(fd, temp_buf, size); - if (iret != size) { - if (iret == -1) - prterr("doread: read"); - else - prt("short read: 0x%x bytes instead of 0x%x\n", - iret, size); - report_failure(141); - } - check_buffers(offset, size); -} - - -void -check_eofpage(char *s, unsigned offset, char *p, int size) -{ - uintptr_t last_page, should_be_zero; - - if (offset + size <= (file_size & ~page_mask)) - return; - /* - * we landed in the last page of the file - * test to make sure the VM system provided 0's - * beyond the true end of the file mapping - * (as required by mmap def in 1996 posix 1003.1) - */ - last_page = ((uintptr_t)p + (offset & page_mask) + size) & ~page_mask; - - for (should_be_zero = last_page + (file_size & page_mask); - should_be_zero < last_page + page_size; - should_be_zero++) - if (*(char *)should_be_zero) { - prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n", - s, file_size - 1, should_be_zero & page_mask, - short_at(should_be_zero)); - report_failure(205); - } -} - - -void -domapread(unsigned offset, unsigned size) -{ - unsigned pg_offset; - unsigned map_size; - char *p; - - offset -= offset % readbdy; - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size read\n"); - log4(OP_SKIPPED, OP_MAPREAD, offset, size); - return; - } - if (size + offset > file_size) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping seek/read past end of file\n"); - log4(OP_SKIPPED, OP_MAPREAD, offset, size); - return; - } - - log4(OP_MAPREAD, offset, size, 0); - - if (testcalls <= simulatedopcount) - return; - - if (!quiet && ((progressinterval && - testcalls % progressinterval == 0) || - (debug && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend)))))) - prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, - offset, offset + size - 1, size); - - pg_offset = offset & page_mask; - map_size = pg_offset + size; - - if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, - (off_t)(offset - pg_offset))) == (char *)-1) { - prterr("domapread: mmap"); - report_failure(190); - } - memcpy(temp_buf, p + pg_offset, size); - - check_eofpage("Read", offset, p, size); - - if (munmap(p, map_size) != 0) { - prterr("domapread: munmap"); - report_failure(191); - } - - check_buffers(offset, size); -} - - -void -gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size) -{ - while (size--) { - good_buf[offset] = testcalls % 256; - if (offset % 2) - good_buf[offset] += original_buf[offset]; - offset++; - } -} - - -void -dowrite(unsigned offset, unsigned size) -{ - off_t ret; - unsigned iret; - - offset -= offset % writebdy; - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size write\n"); - log4(OP_SKIPPED, OP_WRITE, offset, size); - return; - } - - log4(OP_WRITE, offset, size, file_size); - - gendata(original_buf, good_buf, offset, size); - if (file_size < offset + size) { - if (file_size < offset) - memset(good_buf + file_size, '\0', offset - file_size); - file_size = offset + size; - if (lite) { - warn("Lite file size bug in fsx!"); - report_failure(149); - } - } - - if (testcalls <= simulatedopcount) - return; - - if (!quiet && ((progressinterval && - testcalls % progressinterval == 0) || - (debug && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend)))))) - prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, - offset, offset + size - 1, size); - ret = lseek(fd, (off_t)offset, SEEK_SET); - if (ret == (off_t)-1) { - prterr("dowrite: lseek"); - report_failure(150); - } - iret = write(fd, good_buf + offset, size); - if (iret != size) { - if (iret == -1) - prterr("dowrite: write"); - else - prt("short write: 0x%x bytes instead of 0x%x\n", - iret, size); - report_failure(151); - } -} - - -void -domapwrite(unsigned offset, unsigned size) -{ - unsigned pg_offset; - unsigned map_size; - off_t cur_filesize; - char *p; - - offset -= offset % writebdy; - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size write\n"); - log4(OP_SKIPPED, OP_MAPWRITE, offset, size); - return; - } - cur_filesize = file_size; - - log4(OP_MAPWRITE, offset, size, 0); - - gendata(original_buf, good_buf, offset, size); - if (file_size < offset + size) { - if (file_size < offset) - memset(good_buf + file_size, '\0', offset - file_size); - file_size = offset + size; - if (lite) { - warn("Lite file size bug in fsx!"); - report_failure(200); - } - } - - if (testcalls <= simulatedopcount) - return; - - if (!quiet && ((progressinterval && - testcalls % progressinterval == 0) || - (debug && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend)))))) - prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, - offset, offset + size - 1, size); - - if (file_size > cur_filesize) { - if (ftruncate(fd, file_size) == -1) { - prterr("domapwrite: ftruncate"); - exit(201); - } - } - pg_offset = offset & page_mask; - map_size = pg_offset + size; - - if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, fd, - (off_t)(offset - pg_offset))) == MAP_FAILED) { - prterr("domapwrite: mmap"); - report_failure(202); - } - memcpy(p + pg_offset, good_buf + offset, size); - if (mapped_msync && msync(p, map_size, MS_SYNC) != 0) { - prterr("domapwrite: msync"); - report_failure(203); - } - - check_eofpage("Write", offset, p, size); - - if (munmap(p, map_size) != 0) { - prterr("domapwrite: munmap"); - report_failure(204); - } -} - - -void -dotruncate(unsigned size) -{ - int oldsize = file_size; - - size -= size % truncbdy; - if (size > biggest) { - biggest = size; - if (!quiet && testcalls > simulatedopcount) - prt("truncating to largest ever: 0x%x\n", size); - } - - log4(OP_TRUNCATE, size, (unsigned)file_size, 0); - - if (size > file_size) - memset(good_buf + file_size, '\0', size - file_size); - file_size = size; - - if (testcalls <= simulatedopcount) - return; - - if ((progressinterval && testcalls % progressinterval == 0) || - (debug && (monitorstart == -1 || monitorend == -1 || - size <= monitorend))) - prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size); - if (ftruncate(fd, (off_t)size) == -1) { - prt("ftruncate1: %x\n", size); - prterr("dotruncate: ftruncate"); - report_failure(160); - } -} - - -void -writefileimage() -{ - ssize_t iret; - - if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { - prterr("writefileimage: lseek"); - report_failure(171); - } - iret = write(fd, good_buf, file_size); - if ((off_t)iret != file_size) { - if (iret == -1) - prterr("writefileimage: write"); - else - prt("short write: 0x%x bytes instead of 0x%llx\n", - iret, (unsigned long long)file_size); - report_failure(172); - } - if (lite ? 0 : ftruncate(fd, file_size) == -1) { - prt("ftruncate2: %llx\n", (unsigned long long)file_size); - prterr("writefileimage: ftruncate"); - report_failure(173); - } -} - - -void -docloseopen(void) -{ - if (testcalls <= simulatedopcount) - return; - - if (debug) - prt("%lu close/open\n", testcalls); - if (close(fd)) { - prterr("docloseopen: close"); - report_failure(180); - } - fd = open(fname, O_RDWR, 0); - if (fd < 0) { - prterr("docloseopen: open"); - report_failure(181); - } -} - - -void -doinvl(void) -{ - char *p; - - if (file_size == 0) - return; - if (testcalls <= simulatedopcount) - return; - if (debug) - prt("%lu msync(MS_INVALIDATE)\n", testcalls); - - if ((p = (char *)mmap(0, file_size, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, fd, 0)) == MAP_FAILED) { - prterr("doinvl: mmap"); - report_failure(205); - } - - if (msync(p, 0, MS_SYNC | MS_INVALIDATE) != 0) { - prterr("doinvl: msync"); - report_failure(206); - } - - if (munmap(p, file_size) != 0) { - prterr("doinvl: munmap"); - report_failure(207); - } -} - - -void -test(void) -{ - unsigned long offset; - unsigned long size = maxoplen; - unsigned long rv = random(); - unsigned long op = rv % (3 + !lite + mapped_writes); - - /* turn off the map read if necessary */ - - if (op == 2 && !mapped_reads) - op = 0; - - if (simulatedopcount > 0 && testcalls == simulatedopcount) - writefileimage(); - - testcalls++; - - if (closeprob) - closeopen = (rv >> 3) < (1 << 28) / closeprob; - if (invlprob) - invl = (rv >> 3) < (1 << 28) / invlprob; - - if (debugstart > 0 && testcalls >= debugstart) - debug = 1; - - if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) - prt("%lu...\n", testcalls); - - /* - * READ: op = 0 - * WRITE: op = 1 - * MAPREAD: op = 2 - * TRUNCATE: op = 3 - * MAPWRITE: op = 3 or 4 - */ - if (lite ? 0 : op == 3 && style == 0) /* vanilla truncate? */ - dotruncate(random() % maxfilelen); - else { - if (randomoplen) - size = random() % (maxoplen+1); - if (lite ? 0 : op == 3) - dotruncate(size); - else { - offset = random(); - if (op == 1 || op == (lite ? 3 : 4)) { - offset %= maxfilelen; - if (offset + size > maxfilelen) - size = maxfilelen - offset; - if (op != 1) - domapwrite(offset, size); - else - dowrite(offset, size); - } else { - if (file_size) - offset %= file_size; - else - offset = 0; - if (offset + size > file_size) - size = file_size - offset; - if (op != 0) - domapread(offset, size); - else - doread(offset, size); - } - } - } - if (sizechecks && testcalls > simulatedopcount) - check_size(); - if (invl) - doinvl(); - if (closeopen) - docloseopen(); -} - - -void -cleanup(sig) - int sig; -{ - if (sig) - prt("signal %d\n", sig); - prt("testcalls = %lu\n", testcalls); - exit(sig); -} - - -void -usage(void) -{ - fprintf(stdout, "usage: %s", - "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\ - -b opnum: beginning operation number (default 1)\n\ - -c P: 1 in P chance of file close+open at each op (default infinity)\n\ - -d: debug output for all operations\n\ - -i P: 1 in P chance of calling msync(MS_INVALIDATE) (default infinity)\n\ - -l flen: the upper bound on file size (default 262144)\n\ - -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\ - -n: no verifications of file size\n\ - -o oplen: the upper bound on operation size (default 65536)\n\ - -p progressinterval: debug output at specified operation interval\n\ - -q: quieter operation\n\ - -r readbdy: 4096 would make reads page aligned (default 1)\n\ - -s style: 1 gives smaller truncates (default 0)\n\ - -t truncbdy: 4096 would make truncates page aligned (default 1)\n\ - -w writebdy: 4096 would make writes page aligned (default 1)\n\ - -D startingop: debug output starting at specified operation\n\ - -L: fsxLite - no file creations & no file size changes\n\ - -N numops: total # operations to do (default infinity)\n\ - -O: use oplen (see -o flag) for every op (default random)\n\ - -P dirpath: save .fsxlog and .fsxgood files in dirpath (default ./)\n\ - -S seed: for random # generator (default 1) 0 gets timestamp\n\ - -W: mapped write operations DISabled\n\ - -R: mapped read operations DISabled)\n\ - -U: msync after mapped write operations DISabled\n\ - fname: this filename is REQUIRED (no default)\n"); - exit(90); -} - - -int -getnum(char *s, char **e) -{ - int ret = -1; - - *e = (char *) 0; - ret = strtol(s, e, 0); - if (*e) - switch (**e) { - case 'b': - case 'B': - ret *= 512; - *e = *e + 1; - break; - case 'k': - case 'K': - ret *= 1024; - *e = *e + 1; - break; - case 'm': - case 'M': - ret *= 1024*1024; - *e = *e + 1; - break; - case 'w': - case 'W': - ret *= 4; - *e = *e + 1; - break; - } - return (ret); -} - - -int -main(int argc, char **argv) -{ - int i, ch; - char *endp; - char goodfile[1024]; - char logfile[1024]; - - goodfile[0] = 0; - logfile[0] = 0; - - page_size = getpagesize(); - page_mask = page_size - 1; - - setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ - - while ((ch = getopt(argc, argv, - "b:c:di:l:m:no:p:qr:s:t:w:D:LN:OP:RS:UW")) != -1) - switch (ch) { - case 'b': - simulatedopcount = getnum(optarg, &endp); - if (!quiet) - fprintf(stdout, "Will begin at operation %ld\n", - simulatedopcount); - if (simulatedopcount == 0) - usage(); - simulatedopcount -= 1; - break; - case 'c': - closeprob = getnum(optarg, &endp); - if (!quiet) - fprintf(stdout, - "Chance of close/open is 1 in %d\n", - closeprob); - if (closeprob <= 0) - usage(); - break; - case 'd': - debug = 1; - break; - case 'i': - invlprob = getnum(optarg, &endp); - if (!quiet) - fprintf(stdout, - "Chance of MS_INVALIDATE is 1 in %d\n", - invlprob); - if (invlprob <= 0) - usage(); - break; - case 'l': - maxfilelen = getnum(optarg, &endp); - if (maxfilelen <= 0) - usage(); - break; - case 'm': - monitorstart = getnum(optarg, &endp); - if (monitorstart < 0) - usage(); - if (!endp || *endp++ != ':') - usage(); - monitorend = getnum(endp, &endp); - if (monitorend < 0) - usage(); - if (monitorend == 0) - monitorend = -1; /* aka infinity */ - debug = 1; - case 'n': - sizechecks = 0; - break; - case 'o': - maxoplen = getnum(optarg, &endp); - if (maxoplen <= 0) - usage(); - break; - case 'p': - progressinterval = getnum(optarg, &endp); - if (progressinterval < 0) - usage(); - break; - case 'q': - quiet = 1; - break; - case 'r': - readbdy = getnum(optarg, &endp); - if (readbdy <= 0) - usage(); - break; - case 's': - style = getnum(optarg, &endp); - if (style < 0 || style > 1) - usage(); - break; - case 't': - truncbdy = getnum(optarg, &endp); - if (truncbdy <= 0) - usage(); - break; - case 'w': - writebdy = getnum(optarg, &endp); - if (writebdy <= 0) - usage(); - break; - case 'D': - debugstart = getnum(optarg, &endp); - if (debugstart < 1) - usage(); - break; - case 'L': - lite = 1; - break; - case 'N': - numops = getnum(optarg, &endp); - if (numops < 0) - usage(); - break; - case 'O': - randomoplen = 0; - break; - case 'P': - strncpy(goodfile, optarg, sizeof(goodfile)); - strcat(goodfile, "/"); - strncpy(logfile, optarg, sizeof(logfile)); - strcat(logfile, "/"); - break; - case 'R': - mapped_reads = 0; - break; - case 'S': - seed = getnum(optarg, &endp); - if (seed == 0) - seed = time(0) % 10000; - if (!quiet) - fprintf(stdout, "Seed set to %d\n", seed); - if (seed < 0) - usage(); - break; - case 'W': - mapped_writes = 0; - if (!quiet) - fprintf(stdout, "mapped writes DISABLED\n"); - break; - case 'U': - mapped_msync = 0; - if (!quiet) - fprintf(stdout, "mapped msync DISABLED\n"); - break; - - default: - usage(); - /* NOTREACHED */ - } - argc -= optind; - argv += optind; - if (argc != 1) - usage(); - fname = argv[0]; - - signal(SIGHUP, cleanup); - signal(SIGINT, cleanup); - signal(SIGPIPE, cleanup); - signal(SIGALRM, cleanup); - signal(SIGTERM, cleanup); - signal(SIGXCPU, cleanup); - signal(SIGXFSZ, cleanup); - signal(SIGVTALRM, cleanup); - signal(SIGUSR1, cleanup); - signal(SIGUSR2, cleanup); - - initstate(seed, state, 256); - setstate(state); - fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666); - if (fd < 0) { - prterr(fname); - exit(91); - } - strncat(goodfile, fname, 256); - strcat (goodfile, ".fsxgood"); - fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666); - if (fsxgoodfd < 0) { - prterr(goodfile); - exit(92); - } - strncat(logfile, fname, 256); - strcat (logfile, ".fsxlog"); - fsxlogf = fopen(logfile, "w"); - if (fsxlogf == NULL) { - prterr(logfile); - exit(93); - } - if (lite) { - off_t ret; - file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END); - if (file_size == (off_t)-1) { - prterr(fname); - warn("main: lseek eof"); - exit(94); - } - ret = lseek(fd, (off_t)0, SEEK_SET); - if (ret == (off_t)-1) { - prterr(fname); - warn("main: lseek 0"); - exit(95); - } - } - original_buf = (char *) malloc(maxfilelen); - for (i = 0; i < maxfilelen; i++) - original_buf[i] = random() % 256; - good_buf = (char *) malloc(maxfilelen); - memset(good_buf, '\0', maxfilelen); - temp_buf = (char *) malloc(maxoplen); - memset(temp_buf, '\0', maxoplen); - if (lite) { /* zero entire existing file */ - ssize_t written; - - written = write(fd, good_buf, (size_t)maxfilelen); - if (written != maxfilelen) { - if (written == -1) { - prterr(fname); - warn("main: error on write"); - } else - warn("main: short write, 0x%x bytes instead of 0x%x\n", - (unsigned)written, maxfilelen); - exit(98); - } - } else - check_trunc_hack(); - - while (numops == -1 || numops--) - test(); - - if (close(fd)) { - prterr("close"); - report_failure(99); - } - prt("All operations completed A-OK!\n"); - - exit(0); - return 0; -} - blob - 74e46657d2344758e9f0202d09fc6584efa4057c (mode 644) blob + /dev/null --- tests/pytest.ini +++ /dev/null @@ -1,5 +0,0 @@ -[pytest] -addopts = --verbose --tb=native -x --junit-xml=report.xml -markers = - uses_fuse: Indicates that FUSE is supported. - long: Test that run longer than others. blob - 76d85a6defd52ed04050fe5ca16e0c0bda71a2df (mode 644) blob + /dev/null --- tests/test_unreliablefs.py +++ /dev/null @@ -1,584 +0,0 @@ -#!/usr/bin/env python3 - -if __name__ == '__main__': - import pytest - import sys - sys.exit(pytest.main([__file__] + sys.argv[1:])) - -import binascii -import fcntl -import subprocess -import os -import sys -import shutil -import pytest -import stat -import shutil -import subprocess -import filecmp -import errno -from contextlib import contextmanager -from tempfile import NamedTemporaryFile -from util import (wait_for_mount, umount, cleanup, base_cmdline, - basename, fuse_test_marker, safe_sleep) -from os.path import join as pjoin - -def is_no_xattr_support(): - return sys.platform.startswith("freebsd") or \ - sys.platform == "openbsd" - -no_xattr_support = is_no_xattr_support() - -TEST_FILE = __file__ - -pytestmark = fuse_test_marker() - -with open(TEST_FILE, 'rb') as fh: - TEST_DATA = fh.read() - -def name_generator(__ctr=[0]): - __ctr[0] += 1 - return 'testfile_%d' % __ctr[0] - - -@pytest.mark.uses_fuse -@pytest.fixture(scope="function") -def setup_unreliablefs(tmpdir): - mnt_dir = str(tmpdir.mkdir('mnt')) - src_dir = str(tmpdir.mkdir('src')) - - options = "-basedir={}".format(src_dir) - cmdline = base_cmdline + [ pjoin(basename, 'build/unreliablefs'), mnt_dir, options ] - mount_process = subprocess.Popen(cmdline) - wait_for_mount(mount_process, mnt_dir) - - yield mnt_dir, src_dir - - umount(mount_process, mnt_dir) - cleanup(mount_process, mnt_dir) - -@contextmanager -def os_open(name, flags): - fd = os.open(name, flags) - try: - yield fd - finally: - os.close(fd) - -def os_create(name): - os.close(os.open(name, os.O_CREAT | os.O_RDWR)) - -def test_unlink(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - fullname = mnt_dir + "/" + name - with open(pjoin(src_dir, name), 'wb') as fh: - fh.write(b'hello') - assert name in os.listdir(mnt_dir) - os.unlink(fullname) - with pytest.raises(OSError) as exc_info: - os.stat(fullname) - assert exc_info.value.errno == errno.ENOENT - assert name not in os.listdir(mnt_dir) - assert name not in os.listdir(src_dir) - -def test_mkdir(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - dirname = name_generator() - fullname = mnt_dir + "/" + dirname - os.mkdir(fullname) - fstat = os.stat(fullname) - assert stat.S_ISDIR(fstat.st_mode) - assert os.listdir(fullname) == [] - assert fstat.st_nlink in (1,2) - assert dirname in os.listdir(mnt_dir) - -def test_rmdir(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - fullname = mnt_dir + "/" + name - os.mkdir(pjoin(src_dir, name)) - assert name in os.listdir(mnt_dir) - os.rmdir(fullname) - with pytest.raises(OSError) as exc_info: - os.stat(fullname) - assert exc_info.value.errno == errno.ENOENT - assert name not in os.listdir(mnt_dir) - assert name not in os.listdir(src_dir) - -def test_symlink(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - linkname = name_generator() - link_path = os.path.join(mnt_dir, linkname) - - targetname = name_generator() - target_path = os.path.join("/tmp", targetname) - with open(target_path, 'w+'): - pass - assert os.path.exists(target_path) - - os.symlink(target_path, link_path) - fstat = os.lstat(link_path) - assert stat.S_ISLNK(fstat.st_mode) - assert os.readlink(link_path) == target_path - assert fstat.st_nlink == 1 - assert linkname in os.listdir(mnt_dir) - -def test_create(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - fullname = pjoin(mnt_dir, name) - with pytest.raises(OSError) as exc_info: - os.stat(fullname) - assert exc_info.value.errno == errno.ENOENT - assert name not in os.listdir(mnt_dir) - - fd = os.open(fullname, os.O_CREAT | os.O_RDWR) - os.close(fd) - - assert name in os.listdir(mnt_dir) - fstat = os.lstat(fullname) - assert stat.S_ISREG(fstat.st_mode) - assert fstat.st_nlink == 1 - assert fstat.st_size == 0 - -@pytest.mark.xfail(reason="gh-39") -def test_chown(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - filename = pjoin(mnt_dir, name_generator()) - os.mkdir(filename) - fstat = os.lstat(filename) - uid = fstat.st_uid - gid = fstat.st_gid - - uid_new = uid + 1 - os.chown(filename, uid_new, -1) - fstat = os.lstat(filename) - assert fstat.st_uid == uid_new - assert fstat.st_gid == gid - - gid_new = gid + 1 - os.chown(filename, -1, gid_new) - fstat = os.lstat(filename) - assert fstat.st_uid == uid_new - assert fstat.st_gid == gid_new - -def test_open_read(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - with open(pjoin(src_dir, name), 'wb') as fh_out, \ - open(TEST_FILE, 'rb') as fh_in: - shutil.copyfileobj(fh_in, fh_out) - assert len(os.listdir(mnt_dir)) == 1 - - assert filecmp.cmp(pjoin(mnt_dir, name), TEST_FILE, False) - -def test_open_write(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - content = b'AABBCC' - fd = os.open(pjoin(src_dir, name), - os.O_CREAT | os.O_RDWR) - os.write(fd, content) - os.close(fd) - - fullname = pjoin(mnt_dir, name) - with open(fullname, 'rb') as fh: - assert fh.read() == content - -@pytest.mark.xfail(sys.platform.startswith("freebsd"), reason="gh-44") -def test_append(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - os_create(pjoin(src_dir, name)) - fullname = pjoin(mnt_dir, name) - with os_open(fullname, os.O_WRONLY) as fd: - os.write(fd, b'foo\n') - with os_open(fullname, os.O_WRONLY|os.O_APPEND) as fd: - os.write(fd, b'bar\n') - - with open(fullname, 'rb') as fh: - assert fh.read() == b'foo\nbar\n' - -@pytest.mark.xfail(sys.platform.startswith("freebsd"), reason="gh-42") -def test_seek(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - os_create(pjoin(src_dir, name)) - fullname = pjoin(mnt_dir, name) - with os_open(fullname, os.O_WRONLY) as fd: - os.lseek(fd, 1, os.SEEK_SET) - os.write(fd, b'foobar\n') - with os_open(fullname, os.O_WRONLY) as fd: - os.lseek(fd, 4, os.SEEK_SET) - os.write(fd, b'com') - - with open(fullname, 'rb') as fh: - assert fh.read() == b'\0foocom\n' - -def test_open_unlink(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = pjoin(mnt_dir, name_generator()) - data1 = b'foo' - data2 = b'bar' - fullname = pjoin(mnt_dir, name) - with open(fullname, 'wb+', buffering=0) as fh: - fh.write(data1) - os.unlink(fullname) - with pytest.raises(OSError) as exc_info: - os.stat(fullname) - assert exc_info.value.errno == errno.ENOENT - assert name not in os.listdir(mnt_dir) - fh.write(data2) - fh.seek(0) - assert fh.read() == data1+data2 - -def test_open_gh_14(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = pjoin(mnt_dir, name_generator()) - fullname = pjoin(mnt_dir, name) - - with open(fullname, "w+") as fh: - hs = "123456789ABCDEF1" - hb = binascii.a2b_hex(hs) - fh.write(str(hb)) - fh.seek(0) - fh.read() - -def test_statvfs(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - os.statvfs(mnt_dir) - -def test_link(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name1 = pjoin(mnt_dir, name_generator()) - name2 = pjoin(mnt_dir, name_generator()) - shutil.copyfile(TEST_FILE, name1) - assert filecmp.cmp(name1, TEST_FILE, False) - - fstat1 = os.lstat(name1) - assert fstat1.st_nlink == 1 - - os.link(name1, name2) - - fstat1 = os.lstat(name1) - fstat2 = os.lstat(name2) - for attr in ('st_mode', 'st_dev', 'st_uid', 'st_gid', - 'st_size', 'st_atime', 'st_mtime', 'st_ctime'): - assert getattr(fstat1, attr) == getattr(fstat2, attr) - assert os.path.basename(name2) in os.listdir(mnt_dir) - assert filecmp.cmp(name1, name2, False) - - os.unlink(name2) - - assert os.path.basename(name2) not in os.listdir(mnt_dir) - with pytest.raises(FileNotFoundError): - os.lstat(name2) - - os.unlink(name1) - -def test_readdir(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - newdir = name_generator() - src_newdir = pjoin(src_dir, newdir) - mnt_newdir = pjoin(mnt_dir, newdir) - file_ = src_newdir + "/" + name_generator() - subdir = src_newdir + "/" + name_generator() - subfile = subdir + "/" + name_generator() - - os.mkdir(src_newdir) - shutil.copyfile(TEST_FILE, file_) - os.mkdir(subdir) - shutil.copyfile(TEST_FILE, subfile) - - listdir_is = os.listdir(mnt_newdir) - listdir_is.sort() - listdir_should = [ os.path.basename(file_), os.path.basename(subdir) ] - listdir_should.sort() - assert listdir_is == listdir_should - - os.unlink(file_) - os.unlink(subfile) - os.rmdir(subdir) - os.rmdir(src_newdir) - -def test_readdir_big(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - - # Add enough entries so that readdir needs to be called - # multiple times. - fnames = [] - for i in range(500): - fname = ('A rather long filename to make sure that we ' - 'fill up the buffer - ' * 3) + str(i) - with open(pjoin(src_dir, fname), 'w') as fh: - fh.write('File %d' % i) - fnames.append(fname) - - listdir_is = sorted(os.listdir(mnt_dir)) - listdir_should = sorted(os.listdir(src_dir)) - assert listdir_is == listdir_should - - for fname in fnames: - stat_src = os.stat(pjoin(src_dir, fname)) - stat_mnt = os.stat(pjoin(mnt_dir, fname)) - assert stat_src.st_mtime == stat_mnt.st_mtime - assert stat_src.st_ctime == stat_mnt.st_ctime - assert stat_src.st_size == stat_mnt.st_size - os.unlink(pjoin(src_dir, fname)) - -def test_truncate_path(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - assert len(TEST_DATA) > 1024 - - filename = pjoin(mnt_dir, name_generator()) - with open(filename, 'wb') as fh: - fh.write(TEST_DATA) - - fstat = os.stat(filename) - size = fstat.st_size - assert size == len(TEST_DATA) - - # Add zeros at the end - os.truncate(filename, size + 1024) - assert os.stat(filename).st_size == size + 1024 - with open(filename, 'rb') as fh: - assert fh.read(size) == TEST_DATA - assert fh.read(1025) == b'\0' * 1024 - - # Truncate data - os.truncate(filename, size - 1024) - assert os.stat(filename).st_size == size - 1024 - with open(filename, 'rb') as fh: - assert fh.read(size) == TEST_DATA[:size-1024] - - os.unlink(filename) - -def test_truncate_fd(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - assert len(TEST_DATA) > 1024 - with NamedTemporaryFile('w+b', 0, dir=mnt_dir) as fh: - fd = fh.fileno() - fh.write(TEST_DATA) - fstat = os.fstat(fd) - size = fstat.st_size - assert size == len(TEST_DATA) - - # Add zeros at the end - os.ftruncate(fd, size + 1024) - assert os.fstat(fd).st_size == size + 1024 - fh.seek(0) - assert fh.read(size) == TEST_DATA - assert fh.read(1025) == b'\0' * 1024 - - # Truncate data - os.ftruncate(fd, size - 1024) - assert os.fstat(fd).st_size == size - 1024 - fh.seek(0) - assert fh.read(size) == TEST_DATA[:size-1024] - -def test_passthrough(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(src_dir, name) - assert name not in os.listdir(src_dir) - assert name not in os.listdir(mnt_dir) - with open(src_name, 'w') as fh: - fh.write('Hello, world') - assert name in os.listdir(src_dir) - assert name in os.listdir(mnt_dir) - assert os.stat(src_name) == os.stat(mnt_name) - - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(src_dir, name) - assert name not in os.listdir(src_dir) - assert name not in os.listdir(mnt_dir) - with open(mnt_name, 'w') as fh: - fh.write('Hello, world') - assert name in os.listdir(src_dir) - assert name in os.listdir(mnt_dir) - assert os.stat(src_name) == os.stat(mnt_name) - -@pytest.mark.skipif(no_xattr_support, reason="no xattr support") -@pytest.mark.parametrize("symlink", (False, True)) -def test_listxattr(setup_unreliablefs, symlink): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(src_dir, name) - os_create(mnt_name) - linkname = name_generator() - link_path = os.path.join(mnt_dir, linkname) - os.symlink(mnt_dir, link_path) - if symlink: - target = link_path - else: - target = mnt_name - - attr1_name = b"user.aa" - attr1_value = b"a" - attr2_name = b"user.bb" - attr2_value = b"b" - - num_attrs = len(os.listxattr(target)) - os.setxattr(target, attr1_name, attr1_value) - assert attr1_name.decode("utf-8") in os.listxattr(target) - os.setxattr(target, attr2_name, attr2_value) - assert attr2_name.decode("utf-8") in os.listxattr(target) - assert num_attrs + 2 == len(os.listxattr(target)) - -@pytest.mark.skipif(no_xattr_support, reason="no xattr support") -@pytest.mark.parametrize("symlink", - (False, - pytest.param(True, marks=pytest.mark.xfail(reason="gh-50")), - )) -def test_getxattr(setup_unreliablefs, symlink): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(src_dir, name) - os_create(mnt_name) - linkname = name_generator() - link_path = os.path.join(mnt_dir, linkname) - os.symlink(mnt_dir, link_path) - if symlink: - target = link_path - else: - target = mnt_name - - attr_value = b"unreliablefs" - attr_name = b"user.fsname" - - os.setxattr(target, attr_name, attr_value) - assert attr_name.decode("utf-8") in os.listxattr(target) - assert os.getxattr(target, attr_name) == attr_value - os.setxattr(target, attr_name, b"hello") - assert os.getxattr(target, attr_name) == b"hello" - -@pytest.mark.skipif(no_xattr_support, reason="no xattr support") -@pytest.mark.parametrize("symlink", (False, True)) -def test_setxattr(setup_unreliablefs, symlink): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(src_dir, name) - os_create(mnt_name) - linkname = name_generator() - link_path = os.path.join(mnt_dir, linkname) - os.symlink(mnt_dir, link_path) - if symlink: - target = link_path - else: - target = mnt_name - - attr_value = b"unreliablefs" - attr_name = b"user.fsname" - - os.setxattr(target, attr_name, attr_value) - assert attr_name.decode("utf-8") in os.listxattr(target) - -@pytest.mark.skipif(no_xattr_support, reason="no xattr support") -@pytest.mark.parametrize("symlink", - (pytest.param(True, marks=pytest.mark.xfail(reason="gh-50")), - pytest.param(False, marks=pytest.mark.xfail(reason="gh-50")), - )) -def test_removexattr(setup_unreliablefs, symlink): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(mnt_dir, name) - os_create(mnt_name) - linkname = name_generator() - link_path = os.path.join(mnt_dir, linkname) - os.symlink(mnt_dir, link_path) - if symlink: - target = link_path - else: - target = mnt_name - - attr_value = b"unreliablefs" - attr_name = b"user.fsname" - - os.setxattr(target, attr_name, attr_value) - assert attr_name.decode("utf-8") in os.listxattr(target) - assert os.getxattr(target, attr_name) == attr_value - os.removexattr(target, attr_name) - try: - assert os.getxattr(target, attr_name) == None - except OSError: - pass - -def test_flock(setup_unreliablefs): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(src_dir, name) - os_create(mnt_name) - - with open(mnt_name, 'w') as fh: - fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) - fcntl.flock(fh, fcntl.LOCK_UN) - -@pytest.mark.parametrize("symlink", (False, True)) -def test_utimens(setup_unreliablefs, symlink): - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(mnt_dir, name) - os_create(mnt_name) - linkname = name_generator() - link_path = os.path.join(mnt_dir, linkname) - os.symlink(mnt_name, link_path) - if symlink: - target = link_path - else: - target = mnt_name - - fstat = os.lstat(link_path) - link_atime = fstat.st_atime - link_mtime = fstat.st_mtime - - fstat = os.lstat(mnt_name) - mnt_name_atime = fstat.st_atime + 10 - mnt_name_mtime = fstat.st_mtime + 10 - os.utime(target, (mnt_name_atime, mnt_name_mtime)) - - fstat = os.lstat(mnt_name) - assert fstat.st_atime == mnt_name_atime - assert fstat.st_mtime == mnt_name_mtime - - if symlink: - fstat = os.lstat(link_path) - assert fstat.st_atime == link_atime - assert fstat.st_mtime == link_mtime - -@pytest.mark.long -def test_fsx(setup_unreliablefs): - fsx_bin = shutil.which('fsx') or 'build/tests/fsx' - if not fsx_bin: - pytest.skip('fsx is required to execute testcase') - - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(mnt_dir, name) - os_create(mnt_name) - cmd_line = '{} -N 10000 -d -W -c 4 {}'.format(fsx_bin, mnt_name) - subprocess.check_call(cmd_line.split(' ')) - -@pytest.mark.long -def test_fio(setup_unreliablefs): - if not shutil.which('fio'): - pytest.skip('fio is required to execute testcase') - - mnt_dir, src_dir = setup_unreliablefs - name = name_generator() - src_name = pjoin(src_dir, name) - mnt_name = pjoin(mnt_dir, name) - os_create(mnt_name) - cmd_line = "fio --name=random-write --ioengine=sync --rw=randwrite " \ - "--bs=1m --size=1G --numjobs=1 --iodepth=1 --runtime=60 " \ - "--time_based --end_fsync=1 --filename={}".format(mnt_name) - subprocess.check_call(cmd_line.split(' ')) blob - 53d9fee708b2651d9a10a13f96fedb3f21d468c9 (mode 644) blob + /dev/null --- tests/util.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 - -import subprocess -import pytest -import os -import sys -import stat -import time -from os.path import join as pjoin - -basename = pjoin(os.path.dirname(__file__), '..') - -def is_no_fusermount(): - return sys.platform.startswith("freebsd") or \ - sys.platform == "darwin" - -no_fusermount_support = is_no_fusermount() - -def wait_for_mount(mount_process, mnt_dir, - test_fn=os.path.ismount): - elapsed = 0 - while elapsed < 30: - if test_fn(mnt_dir): - return True - if mount_process.poll() is not None: - pytest.fail('file system process terminated prematurely') - time.sleep(0.1) - elapsed += 0.1 - pytest.fail("mountpoint failed to come up") - -def cleanup(mount_process, mnt_dir): - if no_fusermount_support: - subprocess.call(['umount', mnt_dir], - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT) - else: - subprocess.call(['fusermount', '-u', '-z', mnt_dir], - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT) - mount_process.terminate() - try: - mount_process.wait(1) - except subprocess.TimeoutExpired: - mount_process.kill() - -def umount(mount_process, mnt_dir): - if no_fusermount_support: - subprocess.check_call(['umount', mnt_dir]) - else: - subprocess.check_call(['fusermount', '-u', '-z', mnt_dir]) - assert not os.path.ismount(mnt_dir) - - # Give mount process a little while to terminate. Popen.wait(timeout) - # was only added in 3.3... - elapsed = 0 - while elapsed < 30: - code = mount_process.poll() - if code is not None: - if code == 0: - return - pytest.fail('file system process terminated with code %s' % (code,)) - time.sleep(0.1) - elapsed += 0.1 - pytest.fail('mount process did not terminate') - -def safe_sleep(secs): - '''Like time.sleep(), but sleep for at least *secs* - - `time.sleep` may sleep less than the given period if a signal is - received. This function ensures that we sleep for at least the - desired time. - ''' - - now = time.time() - end = now + secs - while now < end: - time.sleep(end - now) - now = time.time() - -def fuse_test_marker(): - '''Return a pytest.marker that indicates FUSE availability - - If system/user/environment does not support FUSE, return - a `pytest.mark.skip` object with more details. If FUSE is - supported, return `pytest.mark.uses_fuse()`. - ''' - - skip = lambda x: pytest.mark.skip(reason=x) - - if not no_fusermount_support: - which = subprocess.Popen(['which', 'fusermount'], stdout=subprocess.PIPE, universal_newlines=True) - fusermount_path = which.communicate()[0].strip() - - if not fusermount_path or which.returncode != 0: - return skip("Can't find fusermount executable") - - mode = os.stat(fusermount_path).st_mode - if mode & stat.S_ISUID == 0: - return skip('fusermount executable not setuid, and we are not root.') - - if not os.path.exists('/dev/fuse'): - return skip("FUSE kernel module does not seem to be loaded") - - if os.getuid() == 0: - return pytest.mark.uses_fuse() - - try: - fd = os.open('/dev/fuse', os.O_RDWR) - except OSError as exc: - return skip('Unable to open /dev/fuse: %s' % exc.strerror) - else: - os.close(fd) - - return pytest.mark.uses_fuse() - -if os.environ.get('TEST_WITH_VALGRIND', 'no').lower().strip() \ - not in ('no', 'false', '0'): - base_cmdline = [ 'valgrind', '-q', '--' ] -else: - base_cmdline = [] blob - 51fdc822a71ffae9f3a772beaa456a669f63adc8 (mode 644) blob + /dev/null --- unreliablefs.1 +++ /dev/null @@ -1,126 +0,0 @@ -.\" Copyright (c) 2020 Sergey Bronnikov -.\" -.Dd $Mdocdate: November 03 2020 $ -.Dt UNRELIABLEFS 1 -.Os -.Sh NAME -.Nm unreliablefs -.Nd a FUSE-based fault-injecting filesystem -.Sh SYNOPSIS -.Nm -mountpoint -.Op Fl basedir Ar path -.Op Fl seed Ar number -.Op Fl hvdf -.Sh DESCRIPTION -The -.Nm -is a filesystem that allows to inject errors on file operations. -Without configuration it works as pass-through filesystem and redirects file -operations to a file objects on a real filesystem. -.Pp -.Nm -uses Filesystem in Userspace (FUSE) that allows easy setup without requiring a -kernel recompile or new kernel modules. -Without any other configuration, any files in a mounted directory will be -available unchanged. -To mount filesystem it is required to specify mountpoint and after mount it -will contain the same file tree as a root filesystem. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl basedir Ar path -Specify path to a directory that should be mount. -.It Fl seed Ar number -Specify a seed. -.It Fl f -Do not daemonize. -.It Fl d -Do not daemonize. -If this option is specified, -.Nm -will run in the foreground and log to -.Em stderr . -.It Fl v -Show version. -.It Fl h -Show usage. -.El -.Pp -Supported file operations are: -.Xr access 2 , -.Xr chmod 2 , -.Xr chown 2 , -.Xr creat 2 , -.Xr fallocate 2 , -.Xr flock 2 , -.Xr fsync 2 , -.Xr ftruncate 2 , -.Xr getxattr 2 , -.Xr ioctl 2 , -.Xr link 2 , -.Xr listxattr 2 , -.Xr lock 2 , -.Xr lstat 2 , -.Xr mkdir 2 , -.Xr mknod 2 , -.Xr open 2 , -.Xr read 2 , -.Xr readdir 2 , -.Xr readlink 2 , -.Xr removexattr 2 , -.Xr rename 2 , -.Xr rmdir 2 , -.Xr setxattr 2 , -.Xr statfs 2 , -.Xr symlink 2 , -.Xr truncate 2 , -.Xr unlink 2 , -.Xr utimensat 2 , -.Xr write 2 . -.Pp -Following functions are unsupported on OpenBSD: -.Xr removexattr 2 , -.Xr setxattr 2 , -.Xr getxattr 2 , -.Xr listxattr 2 , -.Xr flock 2 , -.Xr fallocate 2 . -.Sh EXIT STATUS -.Ex -std -.Sh EXAMPLES -.Bd -literal - -$ mkdir /tmp/fs -$ unreliablefs /tmp/fs -basedir=/tmp -seed=1618680646 -$ cat << EOF > /tmp/fs/unreliablefs.conf -[errinj_noop] -op_regexp = .* -path_regexp = .* -probability = 30 -EOF -$ ls -la -$ umount /tmp/fs - -.Ed -.Sh SEE ALSO -.Xr fusermount 1 , -.Xr errno 2 , -.Xr fuse 4 , -.Xr fuse 8 , -.Xr unreliablefs.conf 5 , -.Xr mount.fuse 8 -.Sh AUTHORS -.An -nosplit -The -.Nm -utility was written by -.An Sergey -.An Bronnikov . -.Sh CAVEATS -Faults can be injected before a start of a file operation, and cannot be -injected say in a middle of file operation. -So if a file operation has been successfully started then -.Nm -will not affect it and final result entirely depends on a base filesystem and -application that started file operation. blob - 5a189875711f07639a788e8f85cc069c83bf8154 (mode 644) blob + /dev/null --- unreliablefs.c +++ /dev/null @@ -1,190 +0,0 @@ -#define FUSE_USE_VERSION 29 - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "unreliablefs_ops.h" -#include "unreliablefs.h" - -extern struct err_inj_q *config_init(const char* conf_path); -extern void config_delete(struct err_inj_q *config); - -struct unreliablefs_config conf; - -static struct fuse_operations unreliable_ops = { - .getattr = unreliable_getattr, - .readlink = unreliable_readlink, - .mknod = unreliable_mknod, - .mkdir = unreliable_mkdir, - .unlink = unreliable_unlink, - .rmdir = unreliable_rmdir, - .symlink = unreliable_symlink, - .rename = unreliable_rename, - .link = unreliable_link, - .chmod = unreliable_chmod, - .chown = unreliable_chown, - .truncate = unreliable_truncate, - .open = unreliable_open, - .read = unreliable_read, - .write = unreliable_write, - .statfs = unreliable_statfs, - .flush = unreliable_flush, - .release = unreliable_release, - .fsync = unreliable_fsync, -#ifdef HAVE_XATTR - .setxattr = unreliable_setxattr, - .getxattr = unreliable_getxattr, - .listxattr = unreliable_listxattr, - .removexattr = unreliable_removexattr, -#endif /* HAVE_XATTR */ - .opendir = unreliable_opendir, - .readdir = unreliable_readdir, - .releasedir = unreliable_releasedir, - .fsyncdir = unreliable_fsyncdir, - - .init = unreliable_init, - .destroy = unreliable_destroy, - - .access = unreliable_access, - .create = unreliable_create, - .ftruncate = unreliable_ftruncate, - .fgetattr = unreliable_fgetattr, - .lock = unreliable_lock, -#if !defined(__OpenBSD__) - .ioctl = unreliable_ioctl, -#endif /* __OpenBSD__ */ -#ifdef HAVE_FLOCK - .flock = unreliable_flock, -#endif /* HAVE_FLOCK */ -#ifdef HAVE_FALLOCATE - .fallocate = unreliable_fallocate, -#endif /* HAVE_FALLOCATE */ -#ifdef HAVE_UTIMENSAT - .utimens = unreliable_utimens, -#endif /* HAVE_UTIMENSAT */ -}; - -enum { - KEY_HELP, - KEY_VERSION, - KEY_DEBUG, -}; - -#define UNRELIABLEFS_OPT(t, p, v) { t, offsetof(struct unreliablefs_config, p), v } -#define UNRELIABLEFS_VERSION "0.1" - -static struct fuse_opt unreliablefs_opts[] = { - UNRELIABLEFS_OPT("-seed=%u", seed, 0), - UNRELIABLEFS_OPT("-basedir=%s", basedir, 0), - - FUSE_OPT_KEY("-d", KEY_DEBUG), - FUSE_OPT_KEY("-V", KEY_VERSION), - FUSE_OPT_KEY("-v", KEY_VERSION), - FUSE_OPT_KEY("--version", KEY_VERSION), - FUSE_OPT_KEY("-h", KEY_HELP), - FUSE_OPT_KEY("--help", KEY_HELP), - FUSE_OPT_KEY("subdir", FUSE_OPT_KEY_DISCARD), - FUSE_OPT_KEY("modules=", FUSE_OPT_KEY_DISCARD), - FUSE_OPT_END -}; - -static int unreliablefs_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) -{ - switch (key) { - case KEY_HELP: - fprintf(stderr, - "usage: unreliablefs mountpoint [options]\n\n" - "general options:\n" - " -h --help print help\n" - " -v --version print version\n" - " -d enable debug output (implies -f)\n" - " -f foreground operation\n\n" - "unreliablefs options:\n" - " -seed=NUM random seed\n" - " -basedir=STRING directory to mount\n\n"); - exit(1); - - case KEY_VERSION: - fprintf(stderr, "unreliablefs version %s\n", UNRELIABLEFS_VERSION); - fuse_opt_add_arg(outargs, "--version"); - fuse_main(outargs->argc, outargs->argv, &unreliable_ops, NULL); - exit(1); - } - return 1; -} - -int is_dir(const char *path) { - struct stat statbuf; - if (stat(path, &statbuf) != 0) { - return 0; - } - - return S_ISDIR(statbuf.st_mode); -} - -int main(int argc, char *argv[]) -{ - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - memset(&conf, 0, sizeof(conf)); - conf.seed = time(0); - conf.basedir = "/"; - fuse_opt_parse(&args, &conf, unreliablefs_opts, unreliablefs_opt_proc); - srand(conf.seed); - fprintf(stdout, "random seed = %d\n", conf.seed); - - if (is_dir(conf.basedir) == 0) { - fprintf(stderr, "basedir ('%s') is not a directory\n", conf.basedir); - fuse_opt_free_args(&args); - return EXIT_FAILURE; - } - char subdir_option[PATH_MAX]; - sprintf(subdir_option, "-omodules=subdir,subdir=%s", conf.basedir); - fuse_opt_add_arg(&args, subdir_option); - /* build config_path */ - char *real_path = realpath(conf.basedir, NULL); - if (!real_path) { - perror("realpath"); - fuse_opt_free_args(&args); - return EXIT_FAILURE; - } - conf.basedir = real_path; - size_t sz = strlen(DEFAULT_CONF_NAME) + strlen(conf.basedir) + 2; - conf.config_path = malloc(sz); - if (!conf.config_path) { - perror("malloc"); - fuse_opt_free_args(&args); - return EXIT_FAILURE; - } - /* read configuration file on start */ - snprintf(conf.config_path, sz, "%s/%s", conf.basedir, DEFAULT_CONF_NAME); - conf.errors = config_init(conf.config_path); - if (!conf.errors) { - fprintf(stdout, "error injections are not configured!\n"); - } - if (pthread_mutex_init(&conf.mutex, NULL) != 0) { - fuse_opt_free_args(&args); - perror("pthread_mutex_init"); - return EXIT_FAILURE; - } - - fprintf(stdout, "starting FUSE filesystem unreliablefs\n"); - int ret = fuse_main(args.argc, args.argv, &unreliable_ops, NULL); - - /* cleanup */ - fuse_opt_free_args(&args); - config_delete(conf.errors); - if (conf.config_path) - free(conf.config_path); - if (!ret) { - fprintf(stdout, "random seed = %d\n", conf.seed); - } - - return ret; -} blob - 624bc25cbc29c1d549c4793cb4762e4fffb54cb9 (mode 644) blob + /dev/null --- unreliablefs.conf.5 +++ /dev/null @@ -1,87 +0,0 @@ -.\" Copyright (c) 2021 Sergey Bronnikov -.\" -.Dd $Mdocdate: April 15 2021 $ -.Dt UNRELIABLEFS.CONF 5 -.Os -.Sh NAME -.Nm unreliablefs.conf -.Nd format of the configuration file used by -.Xr unreliablefs 1 -.Sh DESCRIPTION -The configuration file format is quite simple. -Sections are delimited by square brackets: -.Pp -.Rs -[Section] -.Re -.Pp -And options within brackets sections are simple key value pairs: -.Pp -.Rs -Option = Value -.Re -.Sh OPTIONS -Per-fault-injection customizable variables are specified within sections -with section names matching the fault-injection name. -.Pp -Supported fault injections are: -.Bl -tag -width Ds -.It Cm errinj_noop -File operation replaced with no-op. -.It Cm errinj_kill_caller -Send SIGKILL signal to a process that invoked file operation. -.It Cm errinj_errno -Set random errno. -.Xr errno 2 -limited by supported errno's. -.It Cm errinj_slowdown -File operation slowdown for nanoseconds specified by duration parameter. -.El -.Pp -The options are: -.Bl -tag -width Ds -.It Cm op_regexp -Sets the regular expression that matches file operation for what fault injection is applicable. -Option uses format of regular expressions described in -.Xr re_format 7 . -POSIX Extended Regular Expression syntax is supported and regular expressions do not differentiate case. -.It Cm path_regexp -Sets the regular expression that matches paths where fault injection is applicable. -Option uses format of regular expressions described in -.Xr re_format 7 . -POSIX Extended Regular Expression syntax is supported and regular expressions do not differentiate case. -.It Cm probability -Sets the probability in percents. -Probability equal to 0 means that error injection will never happen. -Probability equal to 100 means that error injection will happen on each file operation. -.It Cm duration -Sets the duration of file operation slowdown. Applicable to errinj_slowdown only. -.El -.Sh EXAMPLES -.Bd -literal - -[errinj_noop] -path_regexp = .* -op_regexp = .* -probability = 70 - -[errinj_errno] -path_regexp = *.xlog -probability = 4 - -.Ed -.Sh SEE ALSO -.Xr unreliablefs 1 , -.Xr errno 2 , -.Xr syslog 3 , -.Xr re_format 7 -.Sh AUTHORS -.An -nosplit -The -.Xr unreliablefs 1 -utility was written by -.An Sergey -.An Bronnikov . -.\" .Sh HISTORY -.\" .Sh BUGS -.\" .Sh CAVEATS blob - 89c638be28aab091ffdb3908720f955f39fa9ec2 (mode 644) blob + /dev/null --- unreliablefs.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef UNRELIABLEFS_HH -#define UNRELIABLEFS_HH - -#include /* PATH_MAX */ -#include - -#define DEFAULT_CONF_NAME "unreliablefs.conf" - -typedef struct unreliablefs_config { - struct err_inj_q *errors; - char *basedir; - char *config_path; - unsigned int seed; - unsigned int debug; - pthread_mutex_t mutex; -} unreliablefs_config; - -#endif /* UNRELIABLEFS_HH */ blob - a71c63da063644a19fa0b53b3485807c6d165773 (mode 644) blob + /dev/null --- unreliablefs_errinj.c +++ /dev/null @@ -1,358 +0,0 @@ -#define FUSE_USE_VERSION 29 - -#include -#include /* basename() and dirname() */ -#include -#include -#include -#include -#include - -#include -#include - -#include "conf.h" -#include "unreliablefs.h" -#include "unreliablefs_errinj.h" - -static int rand_range(int, int); -int error_inject(const char* path, fuse_op operation); - -extern struct unreliablefs_config conf; - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -#define RANDOM_ELEMENT(arr) \ - (arr[rand_range(0, ARRAY_SIZE(arr))]); - -static int op_random_errno(int op_n) -{ - int rc = -1; - switch (op_n) { - case OP_LSTAT: - rc = RANDOM_ELEMENT(errno_lstat); - break; - case OP_GETATTR: - rc = RANDOM_ELEMENT(errno_lstat); - break; - case OP_READLINK: - rc = RANDOM_ELEMENT(errno_readlink); - break; - case OP_MKNOD: - rc = RANDOM_ELEMENT(errno_mknod); - break; - case OP_MKDIR: - rc = RANDOM_ELEMENT(errno_mkdir); - break; - case OP_UNLINK: - rc = RANDOM_ELEMENT(errno_unlink); - break; - case OP_RMDIR: - rc = RANDOM_ELEMENT(errno_rmdir); - break; - case OP_SYMLINK: - rc = RANDOM_ELEMENT(errno_symlink); - break; - case OP_RENAME: - rc = RANDOM_ELEMENT(errno_rename); - break; - case OP_LINK: - rc = RANDOM_ELEMENT(errno_link); - break; - case OP_CHMOD: - rc = RANDOM_ELEMENT(errno_chmod); - break; - case OP_CHOWN: - rc = RANDOM_ELEMENT(errno_chmod); - break; - case OP_TRUNCATE: - rc = RANDOM_ELEMENT(errno_truncate); - break; - case OP_OPEN: - rc = RANDOM_ELEMENT(errno_creat); - break; - case OP_READ: - rc = RANDOM_ELEMENT(errno_read); - break; - case OP_WRITE: - rc = RANDOM_ELEMENT(errno_write); - break; - case OP_STATFS: - rc = RANDOM_ELEMENT(errno_statfs); - break; - case OP_FLUSH: - rc = RANDOM_ELEMENT(errno_close); - break; - case OP_RELEASE: - rc = RANDOM_ELEMENT(errno_close); - break; - case OP_FSYNC: - rc = RANDOM_ELEMENT(errno_fsync); - break; -#ifdef HAVE_XATTR - case OP_SETXATTR: - rc = RANDOM_ELEMENT(errno_setxattr); - break; - case OP_GETXATTR: - rc = RANDOM_ELEMENT(errno_getxattr); - break; - case OP_LISTXATTR: - rc = RANDOM_ELEMENT(errno_listxattr); - break; - case OP_REMOVEXATTR: - rc = RANDOM_ELEMENT(errno_removexattr); - break; -#endif /* HAVE_XATTR */ - case OP_OPENDIR: - rc = RANDOM_ELEMENT(errno_opendir); - break; - case OP_READDIR: - rc = RANDOM_ELEMENT(errno_readdir); - break; - case OP_RELEASEDIR: - rc = RANDOM_ELEMENT(errno_close); - break; - case OP_FSYNCDIR: - rc = RANDOM_ELEMENT(errno_fsync); - break; - case OP_ACCESS: - rc = RANDOM_ELEMENT(errno_access); - break; - case OP_CREAT: - rc = RANDOM_ELEMENT(errno_creat); - break; - case OP_FTRUNCATE: - rc = RANDOM_ELEMENT(errno_ftruncate); - break; - case OP_FGETATTR: - rc = RANDOM_ELEMENT(errno_lstat); - break; - case OP_LOCK: - rc = RANDOM_ELEMENT(errno_fcntl); - break; -#if !defined(__OpenBSD__) - case OP_IOCTL: - rc = RANDOM_ELEMENT(errno_ioctl); - break; -#endif /* __OpenBSD__ */ -#ifdef HAVE_FLOCK - case OP_FLOCK: - rc = RANDOM_ELEMENT(errno_flock); - break; -#endif /* HAVE_FLOCK */ -#ifdef HAVE_FALLOCATE - case OP_FALLOCATE: - rc = RANDOM_ELEMENT(errno_fallocate); - break; -#endif /* HAVE_FALLOCATE */ -#ifdef HAVE_UTIMENSAT - case OP_UTIMENS: - rc = RANDOM_ELEMENT(errno_utimensat); - break; -#endif /* HAVE_UTIMENSAT */ - default: - fprintf(stderr, "Unsupported operation (%s)\n", fuse_op_name[op_n]); - } - - return rc; -} - -static int rand_range(int min_n, int max_n) -{ - return rand() % (max_n - min_n + 1) + min_n; -} - -int error_inject(const char* path, fuse_op operation) -{ - /* instead of returning an error in 'errno', the operation should return - * the negated error value (-errno) directly. - */ - int rc = -0; - struct errinj_conf *err; - /* read configuration file on change */ - pthread_mutex_lock(&conf.mutex); - if (strcmp(path, conf.config_path) == 0) { - config_delete(conf.errors); - conf.errors = config_init(path); - goto cleanup; - } - if (!conf.errors) { - goto cleanup; - } - - /* apply error injections defined in configuration one by one */ - TAILQ_FOREACH(err, conf.errors, entries) { - unsigned int p = rand_range(MIN_PROBABLITY, MAX_PROBABLITY); - if (!(p <= err->probability)) { - fprintf(stderr, "errinj '%s' skipped: probability (%d) is not matched\n", - errinj_name[err->type], err->probability); - continue; - } - const char* op_name = fuse_op_name[operation]; - if (is_regex_matched(err->path_regexp, path) != 0) { - fprintf(stderr, "errinj '%s' skipped: path_regexp (%s) is not matched\n", - errinj_name[err->type], err->path_regexp); - continue; - } - if (is_regex_matched(err->op_regexp, op_name) != 0) { - fprintf(stderr, "errinj '%s' skipped: op_regexp (%s) is not matched\n", - errinj_name[err->type], err->op_regexp); - continue; - } - fprintf(stdout, "%s triggered on operation '%s', %s\n", - errinj_name[err->type], op_name, path); - switch (err->type) { - case ERRINJ_NOOP: - rc = -ERRNO_NOOP; - break; - case ERRINJ_KILL_CALLER: ; - struct fuse_context *cxt = fuse_get_context(); - if (cxt) { - int ret = kill(cxt->pid, DEFAULT_SIGNAL_NAME); - if (ret == -1) { - perror("kill"); - } - fprintf(stdout, "send signal %s to TID %d\n", - strsignal(DEFAULT_SIGNAL_NAME), cxt->pid); - } - break; - case ERRINJ_ERRNO: - rc = op_random_errno(operation); - fprintf(stdout, "errno '%s'\n", strerror(rc)); - rc = -rc; - break; - case ERRINJ_SLOWDOWN: ; - struct timespec ts = {}; - ts.tv_nsec = err->duration; - fprintf(stdout, "start of '%s' slowdown for '%d' ns\n", op_name, err->duration); - if (nanosleep(&ts, NULL) != 0) { - perror("nanosleep"); - } else { - fprintf(stdout, "end of '%s' slowdown with '%d' ns\n", op_name, err->duration); - } - break; - } - } - -cleanup: - pthread_mutex_unlock(&conf.mutex); - return rc; -} - -static errinj_type errinj_type_by_name(const char *name) -{ - int idx = -1; - int n_elem = sizeof(errinj_name)/sizeof(errinj_name[0]); - for (int i = 0; i < n_elem; i++) { - if (strcmp(errinj_name[i], name) == 0) - idx = i; - } - - return idx; -} - -struct err_inj_q *config_init(const char* conf_path) { - fprintf(stdout, "read configuration %s\n", conf_path); - struct err_inj_q *errors = calloc(1, sizeof(struct err_inj_q)); - if (!errors) { - perror("calloc"); - return NULL; - } - TAILQ_INIT(errors); /* initialize queue */ - if (access(conf_path, F_OK ) == 0) { - if (ini_parse(conf_path, conf_option_handler, errors) < 0) { - fprintf(stderr, "can't load '%s'\n", conf_path); - return NULL; - } - } - - return errors; -} - -void config_delete(struct err_inj_q *errors) { - if (!errors) { - return; - } - errinj_conf *err; - /* delete configuration of error injections */ - while ((err= TAILQ_FIRST(errors))) { - if (err->path_regexp) - free((char*)err->path_regexp); - if (err->op_regexp) - free((char*)err->op_regexp); - TAILQ_REMOVE(errors, err, entries); - free(err); - } - free(errors); -} - -int conf_option_handler(void* cfg, const char* section, - const char* key, const char* value) -{ - errinj_conf *err = NULL; - int cur_type = errinj_type_by_name(section); - if (cur_type == -1) { - fprintf(stderr, "unsupported error injection type '%s'", section); - return -1; - } - errinj_conf *np; - int is_errinj_found = 0; - TAILQ_FOREACH(np, (struct err_inj_q *)cfg, entries) { - if (np->type == (errinj_type)cur_type) { - err = np; - is_errinj_found = 1; - break; - } - } - if (!err) { - if ((err = calloc(1, sizeof(struct errinj_conf))) == NULL) { - perror("calloc"); - return -1; - } - err->type = cur_type; - } - - if (is_errinj_found != 1) { - TAILQ_INSERT_TAIL((struct err_inj_q *)cfg, err, entries); - fprintf(stdout, "enabled error injection %s\n", section); - } - fprintf(stdout, "[%s] %s = %s\n", section, key, value); - if (strcmp(key, "path_regexp") == 0) { - if (err->path_regexp) - free(err->path_regexp); - err->path_regexp = strdup(value); - } else if (strcmp(key, "op_regexp") == 0) { - if (err->op_regexp) - free(err->op_regexp); - err->op_regexp = strdup(value); - } else if (strcmp(key, "probability") == 0) { - err->probability = atoi(value); - } else if (strcmp(key, "duration") == 0) { - err->duration = atoi(value); - } else { - fprintf(stderr, "unknown option '%s' in configuration file\n", key); - return 0; - } - - return 1; -} - -int is_regex_matched(const char *regex, const char *string) { - if (!regex || !string) - return 0; - regex_t reg; - regmatch_t match[1]; - int rc = 0; - rc = regcomp(®, regex, REG_ICASE | REG_EXTENDED); - if (rc != 0) { - perror("regcomp"); - regfree(®); - return rc; - } - rc = regexec(®, string, 1, match, 0); - if (rc != 0) { - perror("regexec"); - } - regfree(®); - return rc; -} blob - 2dcc775ca5c9a010de8149d51d0bc8a92deee037 (mode 644) blob + /dev/null --- unreliablefs_errinj.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef ERRINJ_HH -#define ERRINJ_HH - -#include -#include - -#include "unreliablefs_ops.h" - -#if defined(__linux__) - #include "unreliablefs_errno_linux.h" -#elif defined(__FreeBSD__) - #include "unreliablefs_errno_freebsd.h" -#elif defined(__OpenBSD__) - #include "unreliablefs_errno_openbsd.h" -#elif defined(__APPLE__) - #include "unreliablefs_errno_macosx.h" -#else -#error Unsupported platform -#endif - -#define MAX_ERRINJ_NAME_LENGTH 20 -#define MIN_PROBABLITY 0 -#define MAX_PROBABLITY 100 -#define ERRNO_NOOP -999 -#define DEFAULT_SIGNAL_NAME SIGKILL - -int error_inject(const char* path, fuse_op operation); -struct err_inj_q *config_init(const char* conf_path); -void config_delete(struct err_inj_q *config); -int conf_option_handler(void* cfg, const char* section, - const char* name, const char* value); -int is_regex_matched(const char *regex, const char *string); - -const char *errinj_name[] = -{ - "errinj_errno", - "errinj_kill_caller", - "errinj_noop", - "errinj_slowdown", -}; - -typedef enum { - ERRINJ_ERRNO, - ERRINJ_KILL_CALLER, - ERRINJ_NOOP, - ERRINJ_SLOWDOWN, -} errinj_type; - -typedef struct errinj_conf errinj_conf; - -struct errinj_conf { - char *err_injection_name; - char *op_regexp; - char *path_regexp; - char *errno_regexp; - unsigned int probability; - unsigned int duration; - errinj_type type; - - TAILQ_ENTRY(errinj_conf) entries; -}; - -TAILQ_HEAD(err_inj_q, errinj_conf); - -#endif /* ERRINJ_HH */ blob - 711e8b618bdef4265233295bd82628ee76bed207 (mode 644) blob + /dev/null --- unreliablefs_errno_freebsd.h +++ /dev/null @@ -1,405 +0,0 @@ -#ifndef ERRNO_HH -#define ERRNO_HH - -#include -#include - -static const int errno_access[] = { - EACCES, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EROFS, - ETXTBSY, -}; - -static const int errno_chmod[] = { - EACCES, - EFAULT, - EFTYPE, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EPERM, - EROFS, -}; - -static const int errno_creat[] = { - EACCES, - EBADF, - ECAPMODE, - EDQUOT, - EEXIST, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINTR, - EINVAL, - EIO, - EISDIR, - ELOOP, - EMFILE, - EMLINK, - ENAMETOOLONG, - ENFILE, - ENOENT, - ENOSPC, - ENOTCAPABLE, - ENOTDIR, - ENXIO, - EOPNOTSUPP, - EPERM, - EROFS, - ETXTBSY, - EWOULDBLOCK, -}; - -static const int errno_ftruncate[] = { - EACCES, - EBADF, - EFAULT, - EFBIG, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, - ETXTBSY, -}; - -static const int errno_link[] = { - EACCES, - EBADF, - EDQUOT, - EEXIST, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EOPNOTSUPP, - EPERM, - EROFS, - EXDEV, -}; - -static const int errno_mkdir[] = { - EACCES, - EBADF, - EDQUOT, - EEXIST, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EIO, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_mknod[] = { - EACCES, - EBADF, - EDQUOT, - EEXIST, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_opendir[] = { - EACCES, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_readdir[] = { - EBADF, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, -}; - -static const int errno_read[] = { - EAGAIN, - EBADF, - EBUSY, - ECONNRESET, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINTR, - EINVAL, - EIO, - EISDIR, - EOPNOTSUPP, - EOVERFLOW, -}; - -static const int errno_readlink[] = { - EACCES, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_rename[] = { - EACCES, - ECAPMODE, - EDQUOT, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, - EXDEV, -}; - -static const int errno_rmdir[] = { - EACCES, - EBUSY, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, -}; - -static const int errno_symlink[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_truncate[] = { - EACCES, - EFAULT, - EFBIG, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, - ETXTBSY, -}; - -static const int errno_unlink[] = { - EACCES, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_write[] = { - EAGAIN, - EBADF, - EDQUOT, - EFAULT, - EFBIG, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINTR, - EINVAL, - EIO, - ENOSPC, - EPIPE, - EROFS, -}; - -static const int errno_lstat[] = { - EACCES, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EOVERFLOW, -}; - -static const int errno_statfs[] = { - EACCES, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_close[] = { - EBADF, - EINTR, - ENOSPC, - ECONNRESET, -}; - -static const int errno_utimensat[] = { - EACCES, - EBADF, - EFAULT, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, - ESRCH, -}; - -static const int errno_fsync[] = { - EBADF, -#if __FreeBSD__ >= 12 - EINTEGRITY, -#endif - EINVAL, - EIO, -}; - -static int errno_flock[] = { - EBADF, - EINVAL, - ENOLCK, - EOPNOTSUPP, - EWOULDBLOCK, -}; - -static int errno_ioctl[] = { - EBADF, - EFAULT, - EINVAL, - ENOTTY, -}; - -static const int errno_fcntl[] = { - EAGAIN, - EBADF, - EDEADLK, - EINTR, - EINVAL, - EMFILE, - ENOLCK, - ENOTTY, - EOPNOTSUPP, - EOVERFLOW, - EPERM, - ESRCH, -}; - -#endif /* ERRNO_HH */ blob - 3f92badc7e061cce8680ba07a4ebad1fa4a3b550 (mode 644) blob + /dev/null --- unreliablefs_errno_linux.h +++ /dev/null @@ -1,431 +0,0 @@ -#ifndef ERRNO_HH -#define ERRNO_HH - -#include - -static const int errno_access[] = { - EACCES, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EROFS, - EFAULT, - EINVAL, - EIO, - ENOMEM, - ETXTBSY, -}; - -static const int errno_chmod[] = { - EACCES, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_creat[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EFBIG, - EINTR, - EINVAL, - EISDIR, - ELOOP, - EMFILE, - ENAMETOOLONG, - ENFILE, - ENODEV, - ENOENT, - ENOMEM, - ENOSPC, - ENOTDIR, - ENXIO, - EOPNOTSUPP, - EOVERFLOW, - EPERM, - EROFS, - ETXTBSY, - EWOULDBLOCK, -}; - -static const int errno_ftruncate[] = { - EACCES, - EBADF, - EFAULT, - EFBIG, - EINTR, - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOTDIR, - EPERM, - EROFS, - ETXTBSY, -}; - -static const int errno_link[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, - EXDEV, -}; - -static const int errno_mkdir[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EINVAL, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_mknod[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EINVAL, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOSPC, - ENOTDIR, - EPERM, - EROFS , -}; - -static const int errno_opendir[] = { - EACCES, - EBADF, - EMFILE, - ENOENT, - ENOMEM, - ENOTDIR, -}; - -static const int errno_readdir[] = { - EBADF, -}; - -static const int errno_read[] = { - EAGAIN, - EWOULDBLOCK, - EBADF, - EFAULT, - EINTR, - EINVAL, - EIO, - EISDIR, -}; - -static const int errno_readlink[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, -}; - -static const int errno_rename[] = { - EACCES, - EBUSY, - EDQUOT, - EEXIST, - EFAULT, - EINVAL, - EISDIR, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOSPC, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, -}; - -static const int errno_rmdir[] = { - EACCES, - EBUSY, - EFAULT, - EINVAL, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, -}; - -static const int errno_symlink[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_truncate[] = { - EACCES, - EFAULT, - EFBIG, - EINTR, - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, - ETXTBSY, -}; - -static const int errno_unlink[] = { - EACCES, - EBUSY, - EFAULT, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, -}; - -static const int errno_write[] = { - EAGAIN, - EWOULDBLOCK, - EBADF, - EDESTADDRREQ, - EDQUOT, - EFAULT, - EFBIG, - EINTR, - EINVAL, - ENOSPC, - EPERM, - EPIPE, -}; - -static const int errno_lstat[] = { - EACCES, - EBADF, - EFAULT, - EINVAL, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, - EOVERFLOW, -}; - -static const int errno_statfs[] = { - EACCES, - EFAULT, - EINTR, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOSYS, - ENOTDIR, - EOVERFLOW, -}; - -static const int errno_close[] = { - EBADF, - EDQUOT, - EINTR, - EIO, - ENOSPC, -}; - -static const int errno_utimensat[] = { - EACCES, - EBADF, - EFAULT, - EINVAL, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, - ESRCH, -}; - -static const int errno_fsync[] = { - EBADF, - EDQUOT, - EINVAL, - EIO, - ENOSPC, - EROFS, -}; - -static const int errno_setxattr[] = { - EDQUOT, - EEXIST, - ENODATA, - ENOSPC, - ENOTSUP, - EPERM, - ERANGE, - /* In addition, the errors documented in stat(2) can also occur: */ - EACCES, - EBADF, - EFAULT, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, - EOVERFLOW, -}; - -static const int errno_getxattr[] = { - E2BIG, - ENODATA, - ENOTSUP, - ERANGE, - /* In addition, the errors documented in stat(2) can also occur: */ - EACCES, - EBADF, - EFAULT, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, - EOVERFLOW, -}; - -static const int errno_listxattr[] = { - E2BIG, - ENOTSUP, - ERANGE, - /* In addition, the errors documented in stat(2) can also occur: */ - EACCES, - EBADF, - EFAULT, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, - EOVERFLOW, -}; - -static const int errno_removexattr[] = { - ENODATA, - ENOTSUP, - /* In addition, the errors documented in stat(2) can also occur: */ - EACCES, - EBADF, - EFAULT, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOMEM, - ENOTDIR, - EOVERFLOW, -}; - -static int errno_flock[] = { - EBADF, - EINTR, - EINVAL, - ENOLCK, - EWOULDBLOCK, -}; - -static int errno_fallocate[] = { - EBADF, - EFBIG, - EINTR, - EINVAL, - EIO, - ENODEV, - ENOSPC, - ENOSYS, - EOPNOTSUPP, - EPERM, - ESPIPE, - ETXTBSY, -}; - -static int errno_ioctl[] = { - EBADF, - EFAULT, - EINVAL, - ENOTTY, -}; - -static const int errno_fcntl[] = { - EACCES, - EAGAIN, - EBADF, - EBUSY, - EDEADLK, - EFAULT, - EINTR, - EINVAL, - EMFILE, - ENOLCK, - ENOTDIR, - EPERM, -}; - -#endif /* ERRNO_HH */ blob - fb43f0c628d54629cb1357cf32effbc0989aff9d (mode 644) blob + /dev/null --- unreliablefs_errno_macosx.h +++ /dev/null @@ -1,375 +0,0 @@ -#ifndef ERRNO_HH -#define ERRNO_HH - -#include - -static const int errno_access[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EROFS, - ETXTBSY, -}; - -static const int errno_chmod[] = { - EACCES, - EFAULT, - EINTR, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_creat[] = { - EACCES, - EAGAIN, - EDQUOT, - EEXIST, - EFAULT, - EINTR, - EINVAL, - EIO, - EISDIR, - ELOOP, - EMFILE, - ENAMETOOLONG, - ENFILE, - ENOENT, - ENOSPC, - ENOTDIR, - ENXIO, - EOPNOTSUPP, - EOVERFLOW, - EROFS, - ETXTBSY, -}; - -static const int errno_ftruncate[] = { - EBADF, - EFBIG, - EINTR, - EINVAL, - EIO, - EROFS, -}; - -static const int errno_link[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, - EXDEV, -}; - -static const int errno_mkdir[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EROFS, -}; - -static const int errno_mknod[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_opendir[] = { - /* TODO */ -}; - -static const int errno_readdir[] = { - /* TODO */ -}; - -static const int errno_read[] = { - EAGAIN, - EBADF, - ECONNRESET, - EFAULT, - EINTR, - EINVAL, - EIO, - EISDIR, - ENOBUFS, - ENOMEM, - ENOTCONN, - ENXIO, - ETIMEDOUT, -}; - -static const int errno_readlink[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_rename[] = { - EACCES, - EDQUOT, - EFAULT, - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, - EXDEV, -}; - -static const int errno_rmdir[] = { - EACCES, - EBUSY, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, -}; - -static const int errno_symlink[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EROFS, -}; - -static const int errno_truncate[] = { - EACCES, - EFAULT, - EFBIG, - EINTR, - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EROFS, - ETXTBSY, -}; - -static const int errno_unlink[] = { - EACCES, - EBUSY, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_write[] = { - EAGAIN, - EBADF, - ECONNRESET, - EDQUOT, - EFAULT, - EFBIG, - EINTR, - EINVAL, - EIO, - ENETDOWN, - ENETUNREACH, - ENOSPC, - ENXIO, - EPIPE, - EWOULDBLOCK, -}; - -static const int errno_lstat[] = { - EACCES, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EOVERFLOW, -}; - -static const int errno_statfs[] = { - EACCES, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_close[] = { - EBADF, - EINTR, - EIO, -}; - -static const int errno_utimensat[] = { - EACCES, - EBADF, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, - ESRCH, -}; - -static const int errno_fsync[] = { - EBADF, - EINTR, - EINVAL, - EIO, -}; - -static const int errno_setxattr[] = { - E2BIG, - EACCES, - EEXIST, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOATTR, - ENOSPC, - ENOTDIR, - ENOTSUP, - EPERM, - ERANGE, - EROFS, -}; - -static const int errno_getxattr[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOATTR, - ENOTDIR, - ENOTSUP, - EPERM, - ERANGE, -}; - -static const int errno_listxattr[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOTDIR, - ENOTSUP, - EPERM, - ERANGE, -}; - -static const int errno_removexattr[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOATTR, - ENOTDIR, - ENOTSUP, - EPERM, - EROFS, -}; - -static int errno_flock[] = { - EBADF, - EINVAL, - ENOTSUP, - EWOULDBLOCK, -}; - -static int errno_ioctl[] = { - EBADF, - EINVAL, - ENOTTY, -}; - -static const int errno_fcntl[] = { - EACCES, - EBADF, - EDEADLK, - EINTR, - EINVAL, - EMFILE, - ENOLCK, - EOVERFLOW, - ESRCH, -}; - -#endif /* ERRNO_HH */ blob - 1c3de8c666a38829824f51691dc2dae07c010a33 (mode 644) blob + /dev/null --- unreliablefs_errno_openbsd.h +++ /dev/null @@ -1,342 +0,0 @@ -#ifndef ERRNO_HH -#define ERRNO_HH - -#include - -static const int errno_access[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, - ETXTBSY, -}; - -static const int errno_chmod[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_creat[] = { - EACCES, - EBUSY, - EDQUOT, - EEXIST, - EFAULT, - EINTR, - EINVAL, - EIO, - EISDIR, - ELOOP, - EMFILE, - ENAMETOOLONG, - ENFILE, - ENOENT, - ENOSPC, - ENOTDIR, - ENXIO, - EOPNOTSUPP, - EPERM, - EROFS, - ETXTBSY, - EWOULDBLOCK, -}; - -static const int errno_ftruncate[] = { - EBADF, - EFBIG, - EINVAL, - EIO, -}; - -static const int errno_link[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EOPNOTSUPP, - EPERM, - EROFS, - EXDEV, -}; - -static const int errno_mkdir[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EROFS, -}; - -static const int errno_mknod[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_opendir[] = { - /* opendir(2) */ - ENOTDIR, - /* fcntl(2) */ - EAGAIN, - EBADF, - EDEADLK, - EINTR, - EINVAL, - EMFILE, - ENOLCK, - EOVERFLOW, - ESRCH, - /* fcstat(2) */ - EBADF, - EFAULT, - EIO, - /* open(2) */ - EACCES, - EBUSY, - EDQUOT, - EEXIST, - EFAULT, - EINTR, - EINVAL, - EIO, - EISDIR, - ELOOP, - EMFILE, - ENAMETOOLONG, - ENFILE, - ENOENT, - ENOSPC, - ENOTDIR, - ENXIO, - EOPNOTSUPP, - EPERM, - EROFS, - ETXTBSY, - EWOULDBLOCK, - /* malloc(2) */ - ENOMEM, -}; - -static const int errno_readdir[] = { - EBADF, - EFAULT, - EINVAL, - EIO, -}; - -static const int errno_read[] = { - EAGAIN, - EBADF, - EFAULT, - EINTR, - EINVAL, - EIO, - EISDIR, - ENOTCONN, -}; - -static const int errno_readlink[] = { - EACCES, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_rename[] = { - EACCES, - EDQUOT, - EFAULT, - EINVAL, - EIO, - EISDIR, - ELOOP, - EMLINK, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, - EXDEV, -}; - -static const int errno_rmdir[] = { - EACCES, - EBUSY, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - ENOTEMPTY, - EPERM, - EROFS, -}; - -static const int errno_symlink[] = { - EACCES, - EDQUOT, - EEXIST, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOSPC, - ENOTDIR, - EROFS, -}; - -static const int errno_truncate[] = { - EACCES, - EFAULT, - EFBIG, - EINVAL, - EIO, - EISDIR, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EROFS, - ETXTBSY, -}; - -static const int errno_unlink[] = { - EACCES, - EBUSY, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_write[] = { - EAGAIN, - EBADF, - EDESTADDRREQ, - EDQUOT, - EFAULT, - EFBIG, - EINTR, - EINVAL, - EIO, - ENETDOWN, - ENOSPC, - EPIPE, -}; - -static const int errno_lstat[] = { - EACCES, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_statfs[] = { - EACCES, - EFAULT, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, -}; - -static const int errno_close[] = { - EBADF, - EINTR, - EIO, -}; - -static const int errno_utimensat[] = { - EACCES, - EBADF, - EFAULT, - EINVAL, - EIO, - ELOOP, - ENAMETOOLONG, - ENOENT, - ENOTDIR, - EPERM, - EROFS, -}; - -static const int errno_fsync[] = { - EBADF, - EINVAL, - EIO, -}; - -static int errno_flock[] = { - EBADF, - EINVAL, - EOPNOTSUPP, - EWOULDBLOCK, -}; - -static const int errno_fcntl[] = { - EAGAIN, - EBADF, - EDEADLK, - EINTR, - EINVAL, - EMFILE, - ENOLCK, - EOVERFLOW, - ESRCH, -}; - -#endif /* ERRNO_HH */ blob - 8c628a99b38a49bcf68f45d5e432af82b9801e70 (mode 644) blob + /dev/null --- unreliablefs_ops.c +++ /dev/null @@ -1,846 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -#include -#include -#ifdef HAVE_XATTR -#include -#endif /* HAVE_XATTR */ - -#ifdef linux -/* For pread()/pwrite()/utimensat() */ -#define _XOPEN_SOURCE 700 -#endif - -#define ERRNO_NOOP -999 - -#include "unreliablefs_ops.h" - -const char *fuse_op_name[] = { - "getattr", - "readlink", - "mknod", - "mkdir", - "unlink", - "rmdir", - "symlink", - "rename", - "link", - "chmod", - "chown", - "truncate", - "open", - "read", - "write", - "statfs", - "flush", - "release", - "fsync", -#ifdef HAVE_XATTR - "setxattr", - "getxattr", - "listxattr", - "removexattr", -#endif /* HAVE_XATTR */ - "opendir", - "readdir", - "releasedir", - "fsyncdir", - "access", - "creat", - "ftruncate", - "fgetattr", - "lock", -#if !defined(__OpenBSD__) - "ioctl", -#endif /* __OpenBSD__ */ -#ifdef HAVE_FLOCK - "flock", -#endif /* HAVE_FLOCK */ -#ifdef HAVE_FALLOCATE - "fallocate", -#endif /* HAVE_FALLOCATE */ -#ifdef HAVE_UTIMENSAT - "utimens", -#endif /* HAVE_UTIMENSAT */ - "lstat" -}; - -extern int error_inject(const char* path, fuse_op operation); - -int unreliable_lstat(const char *path, struct stat *buf) -{ - int ret = error_inject(path, OP_LSTAT); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - memset(buf, 0, sizeof(struct stat)); - if (lstat(path, buf) == -1) { - return -errno; - } - - return 0; -} - -int unreliable_getattr(const char *path, struct stat *buf) -{ - int ret = error_inject(path, OP_GETATTR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - memset(buf, 0, sizeof(struct stat)); - if (lstat(path, buf) == -1) { - return -errno; - } - - return 0; -} - -int unreliable_readlink(const char *path, char *buf, size_t bufsiz) -{ - int ret = error_inject(path, OP_READLINK); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = readlink(path, buf, bufsiz); - if (ret == -1) { - return -errno; - } - buf[ret] = 0; - - return 0; -} - -int unreliable_mknod(const char *path, mode_t mode, dev_t dev) -{ - int ret = error_inject(path, OP_MKNOD); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = mknod(path, mode, dev); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_mkdir(const char *path, mode_t mode) -{ - int ret = error_inject(path, OP_MKDIR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = mkdir(path, mode); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_unlink(const char *path) -{ - int ret = error_inject(path, OP_UNLINK); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = unlink(path); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_rmdir(const char *path) -{ - int ret = error_inject(path, OP_RMDIR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = rmdir(path); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_symlink(const char *target, const char *linkpath) -{ - int ret = error_inject(target, OP_SYMLINK); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = symlink(target, linkpath); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_rename(const char *oldpath, const char *newpath) -{ - int ret = error_inject(oldpath, OP_RENAME); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = rename(oldpath, newpath); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_link(const char *oldpath, const char *newpath) -{ - int ret = error_inject(oldpath, OP_LINK); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = link(oldpath, newpath); - if (ret < 0) { - return -errno; - } - - return 0; -} - -int unreliable_chmod(const char *path, mode_t mode) -{ - int ret = error_inject(path, OP_CHMOD); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = chmod(path, mode); - if (ret < 0) { - return -errno; - } - - return 0; -} - -int unreliable_chown(const char *path, uid_t owner, gid_t group) -{ - int ret = error_inject(path, OP_CHOWN); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = chown(path, owner, group); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_truncate(const char *path, off_t length) -{ - int ret = error_inject(path, OP_TRUNCATE); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = truncate(path, length); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_open(const char *path, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_OPEN); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = open(path, fi->flags); - if (ret == -1) { - return -errno; - } - fi->fh = ret; - - return 0; -} - -int unreliable_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_READ); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - int fd; - - if (fi == NULL) { - fd = open(path, O_RDONLY); - } else { - fd = fi->fh; - } - - if (fd == -1) { - return -errno; - } - - ret = pread(fd, buf, size, offset); - if (ret == -1) { - ret = -errno; - } - - if (fi == NULL) { - close(fd); - } - - return ret; -} - -int unreliable_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_WRITE); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - int fd; - (void) fi; - if(fi == NULL) { - fd = open(path, O_WRONLY); - } else { - fd = fi->fh; - } - - if (fd == -1) { - return -errno; - } - - ret = pwrite(fd, buf, size, offset); - if (ret == -1) { - ret = -errno; - } - - if(fi == NULL) { - close(fd); - } - - return ret; -} - -int unreliable_statfs(const char *path, struct statvfs *buf) -{ - int ret = error_inject(path, OP_STATFS); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = statvfs(path, buf); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_flush(const char *path, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_FLUSH); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = close(dup(fi->fh)); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_release(const char *path, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_RELEASE); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = close(fi->fh); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_fsync(const char *path, int datasync, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_FSYNC); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - if (datasync) { - ret = fdatasync(fi->fh); - if (ret == -1) { - return -errno; - } - } else { - ret = fsync(fi->fh); - if (ret == -1) { - return -errno; - } - } - - return 0; -} - -#ifdef HAVE_XATTR -int unreliable_setxattr(const char *path, const char *name, - const char *value, size_t size, int flags) -{ - int ret = error_inject(path, OP_SETXATTR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - -#ifdef __APPLE__ - ret = setxattr(path, name, value, size, 0, flags); -#else - ret = setxattr(path, name, value, size, flags); -#endif /* __APPLE__ */ - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_getxattr(const char *path, const char *name, - char *value, size_t size) -{ - int ret = error_inject(path, OP_GETXATTR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - -#ifdef __APPLE__ - ret = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); -#else - ret = getxattr(path, name, value, size); -#endif /* __APPLE__ */ - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_listxattr(const char *path, char *list, size_t size) -{ - int ret = error_inject(path, OP_LISTXATTR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - -#ifdef __APPLE__ - ret = listxattr(path, list, size, XATTR_NOFOLLOW); -#else - ret = listxattr(path, list, size); -#endif /* __APPLE__ */ - if (ret == -1) { - return -errno; - } - - return ret; -} - -int unreliable_removexattr(const char *path, const char *name) -{ - int ret = error_inject(path, OP_REMOVEXATTR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - -#ifdef __APPLE__ - ret = removexattr(path, name, XATTR_NOFOLLOW); -#else - ret = removexattr(path, name); -#endif /* __APPLE__ */ - if (ret == -1) { - return -errno; - } - - return 0; -} -#endif /* HAVE_XATTR */ - -int unreliable_opendir(const char *path, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_OPENDIR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - DIR *dir = opendir(path); - - if (!dir) { - return -errno; - } - fi->fh = (int64_t) dir; - - return 0; -} - -int unreliable_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_READDIR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - DIR *dp = opendir(path); - if (dp == NULL) { - return -errno; - } - struct dirent *de; - - (void) offset; - (void) fi; - - while ((de = readdir(dp)) != NULL) { - struct stat st; - memset(&st, 0, sizeof(st)); - st.st_ino = de->d_ino; - st.st_mode = de->d_type << 12; - if (filler(buf, de->d_name, &st, 0)) - break; - } - closedir(dp); - - return 0; -} - -int unreliable_releasedir(const char *path, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_RELEASEDIR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - DIR *dir = (DIR *) fi->fh; - - ret = closedir(dir); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_fsyncdir(const char *path, int datasync, struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_FSYNCDIR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - DIR *dir = opendir(path); - if (!dir) { - return -errno; - } - - if (datasync) { - ret = fdatasync(dirfd(dir)); - if (ret == -1) { - return -errno; - } - } else { - ret = fsync(dirfd(dir)); - if (ret == -1) { - return -errno; - } - } - closedir(dir); - - return 0; -} - -void *unreliable_init(struct fuse_conn_info *conn) -{ - return NULL; -} - -void unreliable_destroy(void *private_data) -{ - -} - -int unreliable_access(const char *path, int mode) -{ - int ret = error_inject(path, OP_ACCESS); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = access(path, mode); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_create(const char *path, mode_t mode, - struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_CREAT); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = open(path, fi->flags, mode); - if (ret == -1) { - return -errno; - } - fi->fh = ret; - - return 0; -} - -int unreliable_ftruncate(const char *path, off_t length, - struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_FTRUNCATE); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = truncate(path, length); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_fgetattr(const char *path, struct stat *buf, - struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_FGETATTR); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = fstat((int) fi->fh, buf); - if (ret == -1) { - return -errno; - } - - return 0; -} - -int unreliable_lock(const char *path, struct fuse_file_info *fi, int cmd, - struct flock *fl) -{ - int ret = error_inject(path, OP_LOCK); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = fcntl((int) fi->fh, cmd, fl); - if (ret == -1) { - return -errno; - } - - return 0; -} - -#if !defined(__OpenBSD__) -int unreliable_ioctl(const char *path, int cmd, void *arg, - struct fuse_file_info *fi, - unsigned int flags, void *data) -{ - int ret = error_inject(path, OP_IOCTL); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = ioctl(fi->fh, cmd, arg); - if (ret == -1) { - return -errno; - } - - return ret; -} -#endif /* __OpenBSD__ */ - -#ifdef HAVE_FLOCK -int unreliable_flock(const char *path, struct fuse_file_info *fi, int op) -{ - int ret = error_inject(path, OP_FLOCK); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - ret = flock(((int) fi->fh), op); - if (ret == -1) { - return -errno; - } - - return 0; -} -#endif /* HAVE_FLOCK */ - -#ifdef HAVE_FALLOCATE -int unreliable_fallocate(const char *path, int mode, - off_t offset, off_t len, - struct fuse_file_info *fi) -{ - int ret = error_inject(path, OP_FALLOCATE); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - int fd; - (void) fi; - - if (mode) { - return -EOPNOTSUPP; - } - - if(fi == NULL) { - fd = open(path, O_WRONLY); - } else { - fd = fi->fh; - } - - if (fd == -1) { - return -errno; - } - - ret = fallocate((int) fi->fh, mode, offset, len); - if (ret == -1) { - return -errno; - } - - if(fi == NULL) { - close(fd); - } - - return 0; -} -#endif /* HAVE_FALLOCATE */ - -#ifdef HAVE_UTIMENSAT -int unreliable_utimens(const char *path, const struct timespec ts[2]) -{ - int ret = error_inject(path, OP_UTIMENS); - if (ret == -ERRNO_NOOP) { - return 0; - } else if (ret) { - return ret; - } - - /* don't use utime/utimes since they follow symlinks */ - ret = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); - if (ret == -1) { - return -errno; - } - - return 0; -} -#endif /* HAVE_UTIMENSAT */ blob - b23f58f43f8313d2c6a2396adb3d7cde20daf3b7 (mode 644) blob + /dev/null --- unreliablefs_ops.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef UNRELIABLEFS_OPS_HH -#define UNRELIABLEFS_OPS_HH - -#define FUSE_USE_VERSION 29 - -#include - -int unreliable_getattr(const char *, struct stat *); -int unreliable_readlink(const char *, char *, size_t); -int unreliable_mknod(const char *, mode_t, dev_t); -int unreliable_mkdir(const char *, mode_t); -int unreliable_unlink(const char *); -int unreliable_rmdir(const char *); -int unreliable_symlink(const char *, const char *); -int unreliable_rename(const char *, const char *); -int unreliable_link(const char *, const char *); -int unreliable_chmod(const char *, mode_t); -int unreliable_chown(const char *, uid_t, gid_t); -int unreliable_truncate(const char *, off_t); -int unreliable_open(const char *, struct fuse_file_info *); -int unreliable_read(const char *, char *, size_t, off_t, - struct fuse_file_info *); -int unreliable_write(const char *, const char *, size_t, off_t, - struct fuse_file_info *); -int unreliable_statfs(const char *, struct statvfs *); -int unreliable_flush(const char *, struct fuse_file_info *); -int unreliable_release(const char *, struct fuse_file_info *); -int unreliable_fsync(const char *, int, struct fuse_file_info *); - -#ifdef HAVE_XATTR -int unreliable_setxattr(const char *, const char *, const char *, size_t, int); -int unreliable_getxattr(const char *, const char *, char *, size_t); -int unreliable_listxattr(const char *, char *, size_t); -int unreliable_removexattr(const char *, const char *); -#endif /* HAVE_XATTR */ - -int unreliable_opendir(const char *, struct fuse_file_info *); -int unreliable_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi); -int unreliable_releasedir(const char *, struct fuse_file_info *); -int unreliable_fsyncdir(const char *, int, struct fuse_file_info *); - -void *unreliable_init(struct fuse_conn_info *conn); -void unreliable_destroy(void *private_data); - -int unreliable_lstat(const char *path, struct stat *statbuf); -int unreliable_access(const char *, int); -int unreliable_create(const char *, mode_t, struct fuse_file_info *); -int unreliable_ftruncate(const char *, off_t, struct fuse_file_info *); -int unreliable_fgetattr(const char *, struct stat *, struct fuse_file_info *); -int unreliable_lock(const char *, struct fuse_file_info *, int cmd, - struct flock *); -#if !defined(__OpenBSD__) -int unreliable_ioctl(const char *, int cmd, void *arg, - struct fuse_file_info *, unsigned int flags, void *data); -#endif -int unreliable_write_buf(const char *, struct fuse_bufvec *buf, off_t off, - struct fuse_file_info *); -int unreliable_read_buf(const char *, struct fuse_bufvec **bufp, - size_t size, off_t off, struct fuse_file_info *); -#ifdef HAVE_FLOCK -int unreliable_flock(const char *, struct fuse_file_info *, int op); -#endif /* HAVE_FLOCK */ -#ifdef HAVE_FALLOCATE -int unreliable_fallocate(const char *, int, off_t, off_t, - struct fuse_file_info *); -#endif /* HAVE_FALLOCATE */ - -#ifdef HAVE_UTIMENSAT -int unreliable_utimens(const char *path, const struct timespec ts[2]); -#endif /* HAVE_UTIMENSAT */ - -typedef enum { - OP_GETATTR, - OP_READLINK, - OP_MKNOD, - OP_MKDIR, - OP_UNLINK, - OP_RMDIR, - OP_SYMLINK, - OP_RENAME, - OP_LINK, - OP_CHMOD, - OP_CHOWN, - OP_TRUNCATE, - OP_OPEN, - OP_READ, - OP_WRITE, - OP_STATFS, - OP_FLUSH, - OP_RELEASE, - OP_FSYNC, -#ifdef HAVE_XATTR - OP_SETXATTR, - OP_GETXATTR, - OP_LISTXATTR, - OP_REMOVEXATTR, -#endif /* HAVE_XATTR */ - OP_OPENDIR, - OP_READDIR, - OP_RELEASEDIR, - OP_FSYNCDIR, - OP_ACCESS, - OP_CREAT, - OP_FTRUNCATE, - OP_FGETATTR, - OP_LOCK, -#if !defined(__OpenBSD__) - OP_IOCTL, -#endif /* __OpenBSD__ */ -#ifdef HAVE_FLOCK - OP_FLOCK, -#endif /* HAVE_FLOCK */ -#ifdef HAVE_FALLOCATE - OP_FALLOCATE, -#endif /* HAVE_FALLOCATE */ -#ifdef HAVE_UTIMENSAT - OP_UTIMENS, -#endif /* HAVE_UTIMENSAT */ - OP_LSTAT -} fuse_op; - -extern const char *fuse_op_name[]; - -#endif /* UNRELIABLEFS_OPS_HH */