Commit Diff


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 $<$<COMPILE_LANGUAGE:CXX>:-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 <stdio.h>
-#include <ctype.h>
-#include <string.h>
-
-#include "conf.h"
-
-#if !INI_USE_STACK
-#if INI_CUSTOM_ALLOCATOR
-#include <stddef.h>
-void* ini_malloc(size_t size);
-void ini_free(void* ptr);
-void* ini_realloc(void* ptr, size_t size);
-#else
-#include <stdlib.h>
-#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 $<$<COMPILE_LANGUAGE:CXX>:-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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "conf.h"
+
+#if !INI_USE_STACK
+#if INI_CUSTOM_ALLOCATOR
+#include <stddef.h>
+void* ini_malloc(size_t size);
+void ini_free(void* ptr);
+void* ini_realloc(void* ptr, size_t size);
+#else
+#include <stdlib.h>
+#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 <stdio.h>
+
+/* 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 <sys/types.h>
+#include <sys/stat.h>
+#ifdef _UWIN
+# include <sys/param.h>
+# include <limits.h>
+# include <time.h>
+# include <strings.h>
+#endif
+#include <fcntl.h>
+#include <sys/mman.h>
+#ifndef MAP_FILE
+# define MAP_FILE 0
+#endif
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+
+#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 <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <fuse.h>
+
+#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 <limits.h> /* PATH_MAX */
+#include <pthread.h>
+
+#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 <fuse.h>
+#include <libgen.h> /* basename() and dirname() */
+#include <regex.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#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(&reg, regex, REG_ICASE | REG_EXTENDED);
+    if (rc != 0) {
+        perror("regcomp");
+        regfree(&reg);
+        return rc;
+    }
+    rc = regexec(&reg, string, 1, match, 0);
+    if (rc != 0) {
+        perror("regexec");
+    }
+    regfree(&reg);
+    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 <errno.h>
+#include <signal.h>
+
+#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 <sys/param.h>
+#include <errno.h>
+
+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 <errno.h>
+
+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 <errno.h>
+
+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 <errno.h>
+
+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 <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#ifdef HAVE_XATTR
+#include <sys/xattr.h>
+#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 <fuse.h>
+
+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 <stdio.h>
-
-/* 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 <sys/types.h>
-#include <sys/stat.h>
-#ifdef _UWIN
-# include <sys/param.h>
-# include <limits.h>
-# include <time.h>
-# include <strings.h>
-#endif
-#include <fcntl.h>
-#include <sys/mman.h>
-#ifndef MAP_FILE
-# define MAP_FILE 0
-#endif
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <time.h>
-
-#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 <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-#include <stdlib.h>
-
-#include <fuse.h>
-
-#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 <limits.h> /* PATH_MAX */
-#include <pthread.h>
-
-#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 <fuse.h>
-#include <libgen.h> /* basename() and dirname() */
-#include <regex.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/types.h>
-#include <sys/queue.h>
-
-#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(&reg, regex, REG_ICASE | REG_EXTENDED);
-    if (rc != 0) {
-        perror("regcomp");
-        regfree(&reg);
-        return rc;
-    }
-    rc = regexec(&reg, string, 1, match, 0);
-    if (rc != 0) {
-        perror("regexec");
-    }
-    regfree(&reg);
-    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 <errno.h>
-#include <signal.h>
-
-#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 <sys/param.h>
-#include <errno.h>
-
-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 <errno.h>
-
-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 <errno.h>
-
-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 <errno.h>
-
-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 <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <sys/ioctl.h>
-#include <sys/file.h>
-#ifdef HAVE_XATTR
-#include <sys/xattr.h>
-#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 <fuse.h>
-
-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 */