commit - ec35c1e1623d39f20e381d9e4bb784235d520f31
commit + ceeda00d6db63395d8c9c2d4f289c6597577c7f0
blob - 222ce6a9e9c4b8d3f17726582891fd99082eca7b
blob + 978f192c0f2d859bb5a8ad0b851fc070616c3cd6
--- CMakeLists.txt
+++ CMakeLists.txt
-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)
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})
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
-/* 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
+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
+/* 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
+/* 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
+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
+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
+/*
+ * 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
+[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
+#!/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
+#!/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
+.\" 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
+#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
+.\" 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
+#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
+#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(®, regex, REG_ICASE | REG_EXTENDED);
+ if (rc != 0) {
+ perror("regcomp");
+ regfree(®);
+ return rc;
+ }
+ rc = regexec(®, string, 1, match, 0);
+ if (rc != 0) {
+ perror("regexec");
+ }
+ regfree(®);
+ return rc;
+}
blob - /dev/null
blob + 2dcc775ca5c9a010de8149d51d0bc8a92deee037 (mode 644)
--- /dev/null
+++ unreliablefs/unreliablefs_errinj.h
+#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
+#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
+#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
+#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
+#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
+#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
+#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
-/* 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
-project(unreliablefs_test)
-
-add_executable(fsx "fsx.c")
blob - 6808b548e4f50ad2155271235aea6670b54d4b0e (mode 644)
blob + /dev/null
--- tests/conftest.py
+++ /dev/null
-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
-/*
- * 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
-[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
-#!/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
-#!/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
-.\" 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
-#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
-.\" 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
-#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
-#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(®, regex, REG_ICASE | REG_EXTENDED);
- if (rc != 0) {
- perror("regcomp");
- regfree(®);
- return rc;
- }
- rc = regexec(®, string, 1, match, 0);
- if (rc != 0) {
- perror("regexec");
- }
- regfree(®);
- return rc;
-}
blob - 2dcc775ca5c9a010de8149d51d0bc8a92deee037 (mode 644)
blob + /dev/null
--- unreliablefs_errinj.h
+++ /dev/null
-#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
-#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
-#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
-#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
-#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
-#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
-#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 */