diff options
Diffstat (limited to 'examples/c/ex_file_system.c')
-rw-r--r-- | examples/c/ex_file_system.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/examples/c/ex_file_system.c b/examples/c/ex_file_system.c new file mode 100644 index 00000000000..18ea9b7242e --- /dev/null +++ b/examples/c/ex_file_system.c @@ -0,0 +1,721 @@ +/*- + * Public Domain 2014-2016 MongoDB, Inc. + * Public Domain 2008-2014 WiredTiger, Inc. + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ex_file_system.c + * demonstrates how to use the custom file system interface + */ +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#include <wiredtiger.h> +#include "queue_example.h" + +static const char *home; + +/* + * Example file system implementation. Using memory buffers to represent files. + * + * WARNING: This implementation isn't thread safe: WiredTiger performs schema + * and I/O operations in parallel, so all access to the handle must be thread- + * safe. + */ +typedef struct { + WT_FILE_SYSTEM iface; + + int opened_file_count; + int opened_unique_file_count; + int closed_file_count; + + /* Queue of file handles */ + TAILQ_HEAD(demo_file_handle_qh, demo_file_handle) fileq; + +} DEMO_FILE_SYSTEM; + +typedef struct demo_file_handle { + WT_FILE_HANDLE iface; + + /* + * Add custom file handle fields after the interface. + */ + DEMO_FILE_SYSTEM *demo_fs; + + TAILQ_ENTRY(demo_file_handle) q; + uint32_t ref; /* Reference count */ + + char *buf; /* In-memory contents */ + size_t size; + size_t off; /* Read/write offset */ +} DEMO_FILE_HANDLE; + +/* + * Extension initialization function. + */ +#ifdef _WIN32 +/* + * Explicitly export this function so it is visible when loading extensions. + */ +__declspec(dllexport) +#endif +int demo_file_system_create(WT_CONNECTION *, WT_CONFIG_ARG *); + +/* + * Forward function declarations for file system API implementation + */ +static int demo_fs_open(WT_FILE_SYSTEM *, + WT_SESSION *, const char *, WT_OPEN_FILE_TYPE, uint32_t, WT_FILE_HANDLE **); +static int demo_fs_directory_list(WT_FILE_SYSTEM *, WT_SESSION *, + const char *, const char *, char ***, uint32_t *); +static int demo_fs_directory_list_free( + WT_FILE_SYSTEM *, WT_SESSION *, char **, uint32_t); +static int demo_fs_directory_sync(WT_FILE_SYSTEM *file_system, + WT_SESSION *session, const char *directory); +static int demo_fs_exist(WT_FILE_SYSTEM *, WT_SESSION *, const char *, bool *); +static int demo_fs_remove(WT_FILE_SYSTEM *, WT_SESSION *, const char *); +static int demo_fs_rename( + WT_FILE_SYSTEM *, WT_SESSION *, const char *, const char *); +static int demo_fs_size( + WT_FILE_SYSTEM *, WT_SESSION *, const char *, wt_off_t *); +static int demo_fs_terminate(WT_FILE_SYSTEM *, WT_SESSION *); + +/* + * Forward function declarations for file handle API implementation + */ +static int demo_file_close(WT_FILE_HANDLE *, WT_SESSION *); +static int demo_file_lock(WT_FILE_HANDLE *, WT_SESSION *, bool); +static int demo_file_read( + WT_FILE_HANDLE *, WT_SESSION *, wt_off_t, size_t, void *); +static int demo_file_size(WT_FILE_HANDLE *, WT_SESSION *, wt_off_t *); +static int demo_file_sync(WT_FILE_HANDLE *, WT_SESSION *); +static int demo_file_sync_nowait(WT_FILE_HANDLE *, WT_SESSION *); +static int demo_file_truncate(WT_FILE_HANDLE *, WT_SESSION *, wt_off_t); +static int demo_file_write( + WT_FILE_HANDLE *, WT_SESSION *, wt_off_t, size_t, const void *); + +/* + * Forward function declarations for internal functions + */ +static int demo_handle_remove(WT_SESSION *, DEMO_FILE_HANDLE *); +static DEMO_FILE_HANDLE *demo_handle_search(WT_FILE_SYSTEM *, const char *); + +#define DEMO_FILE_SIZE_INCREMENT 32768 + +/* + * demo_file_system_create -- + * Initialization point for demo file system + */ +int +demo_file_system_create(WT_CONNECTION *conn, WT_CONFIG_ARG *config) +{ + WT_FILE_SYSTEM *file_system; + DEMO_FILE_SYSTEM *demo_fs; + int ret = 0; + + (void)config; /* Unused */ + + if ((demo_fs = calloc(1, sizeof(DEMO_FILE_SYSTEM))) == NULL) + return (ENOMEM); + file_system = (WT_FILE_SYSTEM *)demo_fs; + + /* Initialize the in-memory jump table. */ + file_system->directory_list = demo_fs_directory_list; + file_system->directory_list_free = demo_fs_directory_list_free; + file_system->directory_sync = demo_fs_directory_sync; + file_system->exist = demo_fs_exist; + file_system->open_file = demo_fs_open; + file_system->remove = demo_fs_remove; + file_system->rename = demo_fs_rename; + file_system->size = demo_fs_size; + file_system->terminate = demo_fs_terminate; + + if ((ret = conn->set_file_system(conn, file_system, NULL)) != 0) { + fprintf(stderr, "Error setting custom file system: %s\n", + wiredtiger_strerror(ret)); + goto err; + } + + return (0); + +err: free(demo_fs); + /* An error installing the file system is fatal. */ + exit(1); +} + +/* + * demo_fs_open -- + * fopen for our demo file system + */ +static int +demo_fs_open(WT_FILE_SYSTEM *file_system, WT_SESSION *session, + const char *name, WT_OPEN_FILE_TYPE file_type, uint32_t flags, + WT_FILE_HANDLE **file_handlep) +{ + WT_FILE_HANDLE *file_handle; + DEMO_FILE_HANDLE *demo_fh; + DEMO_FILE_SYSTEM *demo_fs; + + (void)file_type; /* Unused */ + (void)session; /* Unused */ + (void)flags; /* Unused */ + + demo_fs = (DEMO_FILE_SYSTEM *)file_system; + demo_fh = NULL; + + ++demo_fs->opened_file_count; + + /* + * First search the file queue, if we find it, assert there's only a + * single reference, we only supports a single handle on any file. + */ + demo_fh = demo_handle_search(file_system, name); + if (demo_fh != NULL) { + if (demo_fh->ref != 0) { + fprintf(stderr, + "demo_file_open of already open file %s\n", + name); + return (EBUSY); + } + + demo_fh->ref = 1; + demo_fh->off = 0; + + *file_handlep = (WT_FILE_HANDLE *)demo_fh; + return (0); + } + + /* The file hasn't been opened before, create a new one. */ + if ((demo_fh = calloc(1, sizeof(DEMO_FILE_HANDLE))) == NULL) + return (ENOMEM); + + /* Initialize private information. */ + demo_fh->ref = 1; + demo_fh->off = 0; + demo_fh->demo_fs = demo_fs; + if ((demo_fh->buf = calloc(1, DEMO_FILE_SIZE_INCREMENT)) == NULL) + goto enomem; + demo_fh->size = DEMO_FILE_SIZE_INCREMENT; + + /* Initialize public information. */ + file_handle = (WT_FILE_HANDLE *)demo_fh; + if ((file_handle->name = strdup(name)) == NULL) + goto enomem; + + /* + * Setup the function call table for our custom file system. Set the + * function pointer to NULL where our implementation doesn't support + * the functionality. + */ + file_handle->close = demo_file_close; + file_handle->fadvise = NULL; + file_handle->fallocate = NULL; + file_handle->fallocate_nolock = NULL; + file_handle->lock = demo_file_lock; + file_handle->map = NULL; + file_handle->map_discard = NULL; + file_handle->map_preload = NULL; + file_handle->unmap = NULL; + file_handle->read = demo_file_read; + file_handle->size = demo_file_size; + file_handle->sync = demo_file_sync; + file_handle->sync_nowait = demo_file_sync_nowait; + file_handle->truncate = demo_file_truncate; + file_handle->write = demo_file_write; + + TAILQ_INSERT_HEAD(&demo_fs->fileq, demo_fh, q); + ++demo_fs->opened_unique_file_count; + + *file_handlep = file_handle; + return (0); + +enomem: free(demo_fh->buf); + free(demo_fh); + return (ENOMEM); +} + +/* + * demo_fs_directory_list -- + * Return a list of files in a given sub-directory. + */ +static int +demo_fs_directory_list(WT_FILE_SYSTEM *file_system, + WT_SESSION *session, const char *directory, + const char *prefix, char ***dirlistp, uint32_t *countp) +{ + DEMO_FILE_HANDLE *demo_fh; + DEMO_FILE_SYSTEM *demo_fs; + size_t dir_len, prefix_len; + char *name, **entries; + uint32_t allocated, count; + + (void)session; /* Unused */ + + demo_fs = (DEMO_FILE_SYSTEM *)file_system; + entries = NULL; + allocated = count = 0; + dir_len = strlen(directory); + prefix_len = prefix == NULL ? 0 : strlen(prefix); + + TAILQ_FOREACH(demo_fh, &demo_fs->fileq, q) { + name = demo_fh->iface.name; + if (strncmp(name, directory, dir_len) != 0 || + (prefix != NULL && strncmp(name, prefix, prefix_len) != 0)) + continue; + + /* + * Increase the list size in groups of 10, it doesn't + * matter if the list is a bit longer than necessary. + */ + if (count >= allocated) { + entries = realloc( + entries, (allocated + 10) * sizeof(char *)); + if (entries == NULL) + return (ENOMEM); + memset(entries + allocated * sizeof(char *), + 0, 10 * sizeof(char *)); + allocated += 10; + } + entries[count++] = strdup(name); + } + + *dirlistp = entries; + *countp = count; + + return (0); +} + +/* + * demo_fs_directory_list_free -- + * Free memory allocated by demo_fs_directory_list. + */ +static int +demo_fs_directory_list_free(WT_FILE_SYSTEM *file_system, + WT_SESSION *session, char **dirlist, uint32_t count) +{ + (void)file_system; + (void)session; + + if (dirlist != NULL) { + while (count > 0) + free(dirlist[--count]); + free(dirlist); + } + return (0); +} + +/* + * demo_fs_directory_sync -- + * Directory sync for our demo file system, which is a no-op. + */ +static int +demo_fs_directory_sync(WT_FILE_SYSTEM *file_system, + WT_SESSION *session, const char *directory) +{ + (void)file_system; /* Unused */ + (void)session; /* Unused */ + (void)directory; /* Unused */ + + return (0); +} + +/* + * demo_fs_exist -- + * Return if the file exists. + */ +static int +demo_fs_exist(WT_FILE_SYSTEM *file_system, + WT_SESSION *session, const char *name, bool *existp) +{ + (void)session; /* Unused */ + + *existp = + demo_handle_search(file_system, name) != NULL; + + return (0); +} + +/* + * demo_fs_remove -- + * POSIX remove. + */ +static int +demo_fs_remove( + WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name) +{ + DEMO_FILE_HANDLE *demo_fh; + int ret; + + ret = ENOENT; + if ((demo_fh = demo_handle_search(file_system, name)) != NULL) + ret = demo_handle_remove(session, demo_fh); + + return (ret); +} + +/* + * demo_fs_rename -- + * POSIX rename. + */ +static int +demo_fs_rename(WT_FILE_SYSTEM *file_system, + WT_SESSION *session, const char *from, const char *to) +{ + DEMO_FILE_HANDLE *demo_fh; + char *copy; + + (void)session; /* Unused */ + + if ((demo_fh = demo_handle_search(file_system, from)) == NULL) + return (ENOENT); + + if ((copy = strdup(to)) == NULL) + return (ENOMEM); + + free(demo_fh->iface.name); + demo_fh->iface.name = copy; + return (0); +} + +/* + * demo_fs_size -- + * Get the size of a file in bytes, by file name. + */ +static int +demo_fs_size(WT_FILE_SYSTEM *file_system, + WT_SESSION *session, const char *name, wt_off_t *sizep) +{ + DEMO_FILE_HANDLE *demo_fh; + int ret = 0; + + ret = ENOENT; + if ((demo_fh = demo_handle_search(file_system, name)) != NULL) + ret = demo_file_size( + (WT_FILE_HANDLE *)demo_fh, session, sizep); + + return (ret); +} + +/* + * demo_fs_terminate -- + * Discard any resources on termination + */ +static int +demo_fs_terminate(WT_FILE_SYSTEM *file_system, WT_SESSION *session) +{ + DEMO_FILE_HANDLE *demo_fh; + DEMO_FILE_SYSTEM *demo_fs; + int ret = 0, tret; + + (void)session; /* Unused */ + + demo_fs = (DEMO_FILE_SYSTEM *)file_system; + + while ((demo_fh = TAILQ_FIRST(&demo_fs->fileq)) != NULL) + if ((tret = + demo_handle_remove(session, demo_fh)) != 0 && ret == 0) + ret = tret; + + printf("Custom file system\n"); + printf("\t%d unique file opens\n", demo_fs->opened_unique_file_count); + printf("\t%d opened\n", demo_fs->opened_file_count); + printf("\t%d closed\n", demo_fs->closed_file_count); + free(demo_fs); + + return (ret); +} + +/* + * demo_file_close -- + * ANSI C close. + */ +static int +demo_file_close(WT_FILE_HANDLE *file_handle, WT_SESSION *session) +{ + DEMO_FILE_HANDLE *demo_fh; + + (void)session; /* Unused */ + + demo_fh = (DEMO_FILE_HANDLE *)file_handle; + if (demo_fh->ref < 1) { + fprintf(stderr, "Closing already closed handle: %s\n", + demo_fh->iface.name); + return (EINVAL); + } + --demo_fh->ref; + + if (demo_fh->ref == 0) + ++demo_fh->demo_fs->closed_file_count; + + return (0); +} + +/* + * demo_file_lock -- + * Lock/unlock a file. + */ +static int +demo_file_lock(WT_FILE_HANDLE *file_handle, WT_SESSION *session, bool lock) +{ + /* Locks are always granted. */ + (void)file_handle; /* Unused */ + (void)session; /* Unused */ + (void)lock; /* Unused */ + return (0); +} + +/* + * demo_file_read -- + * POSIX pread. + */ +static int +demo_file_read(WT_FILE_HANDLE *file_handle, + WT_SESSION *session, wt_off_t offset, size_t len, void *buf) +{ + DEMO_FILE_HANDLE *demo_fh; + int ret = 0; + size_t off; + + (void)session; /* Unused */ + demo_fh = (DEMO_FILE_HANDLE *)file_handle; + + off = (size_t)offset; + if (off < demo_fh->size) { + if (len > demo_fh->size - off) + len = demo_fh->size - off; + memcpy(buf, (uint8_t *)demo_fh->buf + off, len); + demo_fh->off = off + len; + } else + ret = EINVAL; + + if (ret == 0) + return (0); + /* + * WiredTiger should never request data past the end of a file, so + * flag an error if it does. + */ + fprintf(stderr, + "%s: handle-read: failed to read %zu bytes at offset %zu\n", + demo_fh->iface.name, len, off); + return (EINVAL); +} + +/* + * demo_file_size -- + * Get the size of a file in bytes, by file handle. + */ +static int +demo_file_size( + WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t *sizep) +{ + DEMO_FILE_HANDLE *demo_fh; + + (void)session; /* Unused */ + demo_fh = (DEMO_FILE_HANDLE *)file_handle; + + assert(demo_fh->size != 0); + *sizep = (wt_off_t)demo_fh->size; + return (0); +} + +/* + * demo_file_sync -- + * Ensure the content of the file is stable. This is a no-op in our + * memory backed file system. + */ +static int +demo_file_sync(WT_FILE_HANDLE *file_handle, WT_SESSION *session) +{ + (void)file_handle; /* Unused */ + (void)session; /* Unused */ + + return (0); +} + +/* + * demo_file_sync_nowait -- + * Ensure the content of the file is stable. This is a no-op in our + * memory backed file system. + */ +static int +demo_file_sync_nowait(WT_FILE_HANDLE *file_handle, WT_SESSION *session) +{ + (void)file_handle; /* Unused */ + (void)session; /* Unused */ + + return (0); +} + +/* + * demo_file_truncate -- + * POSIX ftruncate. + */ +static int +demo_file_truncate( + WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset) +{ + DEMO_FILE_HANDLE *demo_fh; + size_t off; + + (void)session; /* Unused */ + demo_fh = (DEMO_FILE_HANDLE *)file_handle; + + /* + * Grow the buffer as necessary, clear any new space in the file, + * and reset the file's data length. + */ + off = (size_t)offset; + demo_fh->buf = realloc(demo_fh->buf, off); + if (demo_fh->buf == NULL) { + fprintf(stderr, "Failed to resize buffer in truncate\n"); + return (ENOSPC); + } + if (demo_fh->size < off) + memset((uint8_t *)demo_fh->buf + demo_fh->size, + 0, off - demo_fh->size); + demo_fh->size = off; + + return (0); +} + +/* + * demo_file_write -- + * POSIX pwrite. + */ +static int +demo_file_write(WT_FILE_HANDLE *file_handle, WT_SESSION *session, + wt_off_t offset, size_t len, const void *buf) +{ + DEMO_FILE_HANDLE *demo_fh; + int ret = 0; + + demo_fh = (DEMO_FILE_HANDLE *)file_handle; + + /* Make sure the buffer is large enough for the write */ + if ((ret = demo_file_truncate(file_handle, session, + offset + (wt_off_t)(len + DEMO_FILE_SIZE_INCREMENT))) != 0) + return (ret); + + memcpy((uint8_t *)demo_fh->buf + offset, buf, len); + demo_fh->off = (size_t)offset + len; + + return (0); +} + +/* + * demo_handle_remove -- + * Destroy an in-memory file handle. Should only happen on remove or + * shutdown. + */ +static int +demo_handle_remove(WT_SESSION *session, DEMO_FILE_HANDLE *demo_fh) +{ + DEMO_FILE_SYSTEM *demo_fs; + + (void)session; /* Unused */ + demo_fs = demo_fh->demo_fs; + + if (demo_fh->ref != 0) { + fprintf(stderr, + "demo_handle_remove on file %s with non-zero reference " + "count of %u\n", + demo_fh->iface.name, demo_fh->ref); + return (EINVAL); + } + + TAILQ_REMOVE(&demo_fs->fileq, demo_fh, q); + + /* Clean up private information. */ + free(demo_fh->buf); + demo_fh->buf = NULL; + + /* Clean up public information. */ + free(demo_fh->iface.name); + + free(demo_fh); + + return (0); +} + +/* + * demo_handle_search -- + * Return a matching handle, if one exists. + */ +static DEMO_FILE_HANDLE * +demo_handle_search(WT_FILE_SYSTEM *file_system, const char *name) +{ + DEMO_FILE_HANDLE *demo_fh; + DEMO_FILE_SYSTEM *demo_fs; + + demo_fs = (DEMO_FILE_SYSTEM *)file_system; + + TAILQ_FOREACH(demo_fh, &demo_fs->fileq, q) + if (strcmp(demo_fh->iface.name, name) == 0) + break; + return (demo_fh); +} + +int +main(void) +{ + WT_CONNECTION *conn; + const char *open_config; + int ret = 0; + + /* + * Create a clean test directory for this run of the test program if the + * environment variable isn't already set (as is done by make check). + */ + if (getenv("WIREDTIGER_HOME") == NULL) { + home = "WT_HOME"; + ret = system("rm -rf WT_HOME && mkdir WT_HOME"); + } else + home = NULL; + + /*! [WT_FILE_SYSTEM register] */ + /* + * Setup a configuration string that will load our custom file system. + * Use the special local extension to indicate that the entry point is + * in the same executable. Also enable early load for this extension, + * since WiredTiger needs to be able to find it before doing any file + * operations. + */ + open_config = "create,log=(enabled=true),extensions=(local=" + "{entry=demo_file_system_create,early_load=true})"; + /* Open a connection to the database, creating it if necessary. */ + if ((ret = wiredtiger_open(home, NULL, open_config, &conn)) != 0) { + fprintf(stderr, "Error connecting to %s: %s\n", + home, wiredtiger_strerror(ret)); + return (ret); + } + /*! [WT_FILE_SYSTEM register] */ + + if ((ret = conn->close(conn, NULL)) != 0) + fprintf(stderr, "Error closing connection to %s: %s\n", + home, wiredtiger_strerror(ret)); + + return (ret); +} |