Version 10.0.2
ex_file_system.c

Shows how to extend WiredTiger with a custom file-system implementation.

/*-
* Public Domain 2014-present 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 <test_util.h>
/*
* This example code uses pthread functions for portable locking, we ignore errors for simplicity.
*/
static void
allocate_file_system_lock(pthread_rwlock_t *lockp)
{
error_check(pthread_rwlock_init(lockp, NULL));
}
static void
destroy_file_system_lock(pthread_rwlock_t *lockp)
{
error_check(pthread_rwlock_destroy(lockp));
}
static void
lock_file_system(pthread_rwlock_t *lockp)
{
error_check(pthread_rwlock_wrlock(lockp));
}
static void
unlock_file_system(pthread_rwlock_t *lockp)
{
error_check(pthread_rwlock_unlock(lockp));
}
/*
* Example file system implementation, using memory buffers to represent files.
*/
typedef struct {
/*
* WiredTiger performs schema and I/O operations in parallel, all file system and file handle
* access must be thread-safe. This example uses a single, global file system lock for
* simplicity; real applications might require finer granularity, for example, a single lock for
* the file system handle list and per-handle locks serializing I/O.
*/
pthread_rwlock_t lock; /* Lock */
int opened_file_count;
int opened_unique_file_count;
int closed_file_count;
int read_ops;
int write_ops;
/* Queue of file handles */
TAILQ_HEAD(demo_file_handle_qh, demo_file_handle) fileq;
WT_EXTENSION_API *wtext; /* Extension functions */
} DEMO_FILE_SYSTEM;
typedef struct demo_file_handle {
/*
* Add custom file handle fields after the interface.
*/
DEMO_FILE_SYSTEM *demo_fs; /* Enclosing file system */
TAILQ_ENTRY(demo_file_handle) q; /* Queue of handles */
uint32_t ref; /* Reference count */
char *buf; /* In-memory contents */
size_t bufsize; /* In-memory buffer size */
size_t size; /* Read/write data size */
} 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_FS_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_exist(WT_FILE_SYSTEM *, WT_SESSION *, const char *, bool *);
static int demo_fs_remove(WT_FILE_SYSTEM *, WT_SESSION *, const char *, uint32_t);
static int demo_fs_rename(WT_FILE_SYSTEM *, WT_SESSION *, const char *, const char *, uint32_t);
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_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
/*
* string_match --
* Return if a string matches a byte string of len bytes.
*/
static bool
byte_string_match(const char *str, const char *bytes, size_t len)
{
return (strncmp(str, bytes, len) == 0 && (str)[(len)] == '\0');
}
/*
* demo_file_system_create --
* Initialization point for demo file system
*/
int
demo_file_system_create(WT_CONNECTION *conn, WT_CONFIG_ARG *config)
{
DEMO_FILE_SYSTEM *demo_fs;
WT_CONFIG_PARSER *config_parser;
WT_FILE_SYSTEM *file_system;
int ret = 0;
wtext = conn->get_extension_api(conn);
if ((demo_fs = calloc(1, sizeof(DEMO_FILE_SYSTEM))) == NULL) {
(void)wtext->err_printf(
wtext, NULL, "demo_file_system_create: %s", wtext->strerror(wtext, NULL, ENOMEM));
return (ENOMEM);
}
demo_fs->wtext = wtext;
file_system = (WT_FILE_SYSTEM *)demo_fs;
/*
* Applications may have their own configuration information to pass to the underlying
* filesystem implementation. See the main function for the setup of those configuration
* strings; here we parse configuration information as passed in by main, through WiredTiger.
*/
if ((ret = wtext->config_parser_open_arg(wtext, NULL, config, &config_parser)) != 0) {
(void)wtext->err_printf(wtext, NULL, "WT_EXTENSION_API.config_parser_open: config: %s",
wtext->strerror(wtext, NULL, ret));
goto err;
}
/* Step through our configuration values. */
printf("Custom file system configuration\n");
while ((ret = config_parser->next(config_parser, &k, &v)) == 0) {
if (byte_string_match("config_string", k.str, k.len)) {
printf(
"\t"
"key %.*s=\"%.*s\"\n",
(int)k.len, k.str, (int)v.len, v.str);
continue;
}
if (byte_string_match("config_value", k.str, k.len)) {
printf(
"\t"
"key %.*s=%" PRId64 "\n",
(int)k.len, k.str, v.val);
continue;
}
ret = EINVAL;
(void)wtext->err_printf(wtext, NULL,
"WT_CONFIG_PARSER.next: unexpected configuration "
"information: %.*s=%.*s: %s",
(int)k.len, k.str, (int)v.len, v.str, wtext->strerror(wtext, NULL, ret));
goto err;
}
/* Check for expected parser termination and close the parser. */
if (ret != WT_NOTFOUND) {
(void)wtext->err_printf(
wtext, NULL, "WT_CONFIG_PARSER.next: config: %s", wtext->strerror(wtext, NULL, ret));
goto err;
}
if ((ret = config_parser->close(config_parser)) != 0) {
(void)wtext->err_printf(
wtext, NULL, "WT_CONFIG_PARSER.close: config: %s", wtext->strerror(wtext, NULL, ret));
goto err;
}
allocate_file_system_lock(&demo_fs->lock);
/* Initialize the in-memory jump table. */
file_system->fs_directory_list = demo_fs_directory_list;
file_system->fs_directory_list_free = demo_fs_directory_list_free;
file_system->fs_exist = demo_fs_exist;
file_system->fs_open_file = demo_fs_open;
file_system->fs_remove = demo_fs_remove;
file_system->fs_rename = demo_fs_rename;
file_system->fs_size = demo_fs_size;
file_system->terminate = demo_fs_terminate;
if ((ret = conn->set_file_system(conn, file_system, NULL)) != 0) {
(void)wtext->err_printf(
wtext, NULL, "WT_CONNECTION.set_file_system: %s", wtext->strerror(wtext, NULL, 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_FS_OPEN_FILE_TYPE file_type, uint32_t flags, WT_FILE_HANDLE **file_handlep)
{
DEMO_FILE_HANDLE *demo_fh;
DEMO_FILE_SYSTEM *demo_fs;
WT_FILE_HANDLE *file_handle;
int ret = 0;
(void)file_type; /* Unused */
(void)flags; /* Unused */
*file_handlep = NULL;
demo_fs = (DEMO_FILE_SYSTEM *)file_system;
demo_fh = NULL;
wtext = demo_fs->wtext;
lock_file_system(&demo_fs->lock);
++demo_fs->opened_file_count;
/*
* First search the file queue, if we find it, assert there's only a single reference, we only
* support a single handle on any file.
*/
demo_fh = demo_handle_search(file_system, name);
if (demo_fh != NULL) {
if (demo_fh->ref != 0) {
(void)wtext->err_printf(wtext, session, "demo_fs_open: %s: file already open", name);
ret = EBUSY;
goto err;
}
demo_fh->ref = 1;
*file_handlep = (WT_FILE_HANDLE *)demo_fh;
unlock_file_system(&demo_fs->lock);
return (0);
}
/* The file hasn't been opened before, create a new one. */
if ((demo_fh = calloc(1, sizeof(DEMO_FILE_HANDLE))) == NULL) {
ret = ENOMEM;
goto err;
}
/* Initialize private information. */
demo_fh->demo_fs = demo_fs;
demo_fh->ref = 1;
if ((demo_fh->buf = calloc(1, DEMO_FILE_SIZE_INCREMENT)) == NULL) {
ret = ENOMEM;
goto err;
}
demo_fh->bufsize = DEMO_FILE_SIZE_INCREMENT;
demo_fh->size = 0;
/* Initialize public information. */
file_handle = (WT_FILE_HANDLE *)demo_fh;
if ((file_handle->name = strdup(name)) == NULL) {
ret = ENOMEM;
goto err;
}
/*
* 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->fh_advise = NULL;
file_handle->fh_extend = NULL;
file_handle->fh_extend_nolock = NULL;
file_handle->fh_lock = demo_file_lock;
file_handle->fh_map = NULL;
file_handle->fh_map_discard = NULL;
file_handle->fh_map_preload = NULL;
file_handle->fh_unmap = NULL;
file_handle->fh_read = demo_file_read;
file_handle->fh_size = demo_file_size;
file_handle->fh_sync = demo_file_sync;
file_handle->fh_sync_nowait = NULL;
file_handle->fh_truncate = demo_file_truncate;
file_handle->fh_write = demo_file_write;
TAILQ_INSERT_HEAD(&demo_fs->fileq, demo_fh, q);
++demo_fs->opened_unique_file_count;
*file_handlep = file_handle;
if (0) {
err:
free(demo_fh->buf);
free(demo_fh);
}
unlock_file_system(&demo_fs->lock);
return (ret);
}
/*
* 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 len, prefix_len;
uint32_t allocated, count;
int ret = 0;
char *name, **entries;
void *p;
(void)session; /* Unused */
demo_fs = (DEMO_FILE_SYSTEM *)file_system;
*dirlistp = NULL;
*countp = 0;
entries = NULL;
allocated = count = 0;
len = strlen(directory);
prefix_len = prefix == NULL ? 0 : strlen(prefix);
lock_file_system(&demo_fs->lock);
TAILQ_FOREACH (demo_fh, &demo_fs->fileq, q) {
name = demo_fh->iface.name;
if (strncmp(name, directory, 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) {
p = realloc(entries, (allocated + 10) * sizeof(*entries));
if (p == NULL) {
ret = ENOMEM;
goto err;
}
entries = p;
memset(entries + allocated * sizeof(*entries), 0, 10 * sizeof(*entries));
allocated += 10;
}
entries[count++] = strdup(name);
}
*dirlistp = entries;
*countp = count;
err:
unlock_file_system(&demo_fs->lock);
if (ret == 0)
return (0);
if (entries != NULL) {
while (count > 0)
free(entries[--count]);
free(entries);
}
return (ret);
}
/*
* 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_exist --
* Return if the file exists.
*/
static int
demo_fs_exist(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name, bool *existp)
{
DEMO_FILE_SYSTEM *demo_fs;
(void)session; /* Unused */
demo_fs = (DEMO_FILE_SYSTEM *)file_system;
lock_file_system(&demo_fs->lock);
*existp = demo_handle_search(file_system, name) != NULL;
unlock_file_system(&demo_fs->lock);
return (0);
}
/*
* demo_fs_remove --
* POSIX remove.
*/
static int
demo_fs_remove(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name, uint32_t flags)
{
DEMO_FILE_SYSTEM *demo_fs;
DEMO_FILE_HANDLE *demo_fh;
int ret = 0;
(void)session; /* Unused */
(void)flags; /* Unused */
demo_fs = (DEMO_FILE_SYSTEM *)file_system;
ret = ENOENT;
lock_file_system(&demo_fs->lock);
if ((demo_fh = demo_handle_search(file_system, name)) != NULL)
ret = demo_handle_remove(session, demo_fh);
unlock_file_system(&demo_fs->lock);
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,
uint32_t flags)
{
DEMO_FILE_HANDLE *demo_fh;
DEMO_FILE_SYSTEM *demo_fs;
char *copy;
int ret = 0;
(void)session; /* Unused */
(void)flags; /* Unused */
demo_fs = (DEMO_FILE_SYSTEM *)file_system;
lock_file_system(&demo_fs->lock);
if ((demo_fh = demo_handle_search(file_system, from)) == NULL)
ret = ENOENT;
else if ((copy = strdup(to)) == NULL)
ret = ENOMEM;
else {
free(demo_fh->iface.name);
demo_fh->iface.name = copy;
}
unlock_file_system(&demo_fs->lock);
return (ret);
}
/*
* 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_SYSTEM *demo_fs;
DEMO_FILE_HANDLE *demo_fh;
int ret = 0;
demo_fs = (DEMO_FILE_SYSTEM *)file_system;
ret = ENOENT;
lock_file_system(&demo_fs->lock);
if ((demo_fh = demo_handle_search(file_system, name)) != NULL) {
unlock_file_system(&demo_fs->lock);
ret = demo_file_size((WT_FILE_HANDLE *)demo_fh, session, sizep);
} else {
unlock_file_system(&demo_fs->lock);
}
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_fh_tmp;
DEMO_FILE_SYSTEM *demo_fs;
int ret = 0, tret;
demo_fs = (DEMO_FILE_SYSTEM *)file_system;
TAILQ_FOREACH_SAFE(demo_fh, &demo_fs->fileq, q, demo_fh_tmp)
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 files opened\n", demo_fs->opened_file_count);
printf("\t%d files closed\n", demo_fs->closed_file_count);
printf("\t%d reads, %d writes\n", demo_fs->read_ops, demo_fs->write_ops);
destroy_file_system_lock(&demo_fs->lock);
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;
DEMO_FILE_SYSTEM *demo_fs;
(void)session; /* Unused */
demo_fh = (DEMO_FILE_HANDLE *)file_handle;
demo_fs = demo_fh->demo_fs;
lock_file_system(&demo_fs->lock);
if (--demo_fh->ref == 0)
++demo_fs->closed_file_count;
unlock_file_system(&demo_fs->lock);
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;
DEMO_FILE_SYSTEM *demo_fs;
size_t off;
int ret = 0;
demo_fh = (DEMO_FILE_HANDLE *)file_handle;
demo_fs = demo_fh->demo_fs;
wtext = demo_fs->wtext;
off = (size_t)offset;
lock_file_system(&demo_fs->lock);
++demo_fs->read_ops;
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);
} else
ret = EIO; /* EOF */
unlock_file_system(&demo_fs->lock);
if (ret == 0)
return (0);
(void)wtext->err_printf(wtext, session,
"%s: handle-read: failed to read %zu bytes at offset %zu: %s", demo_fh->iface.name, len, off,
wtext->strerror(wtext, NULL, ret));
return (ret);
}
/*
* 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;
DEMO_FILE_SYSTEM *demo_fs;
(void)session; /* Unused */
demo_fh = (DEMO_FILE_HANDLE *)file_handle;
demo_fs = demo_fh->demo_fs;
lock_file_system(&demo_fs->lock);
*sizep = (wt_off_t)demo_fh->size;
unlock_file_system(&demo_fs->lock);
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_buffer_resize --
* Resize the write buffer.
*/
static int
demo_buffer_resize(WT_SESSION *session, DEMO_FILE_HANDLE *demo_fh, wt_off_t offset)
{
DEMO_FILE_SYSTEM *demo_fs;
size_t off;
void *p;
demo_fs = demo_fh->demo_fs;
wtext = demo_fs->wtext;
off = (size_t)offset;
/* Grow the buffer as necessary and clear any new space in the file. */
if (demo_fh->bufsize >= off)
return (0);
if ((p = realloc(demo_fh->buf, off)) == NULL) {
(void)wtext->err_printf(wtext, session, "%s: failed to resize buffer", demo_fh->iface.name,
wtext->strerror(wtext, NULL, ENOMEM));
return (ENOMEM);
}
memset((uint8_t *)p + demo_fh->bufsize, 0, off - demo_fh->bufsize);
demo_fh->buf = p;
demo_fh->bufsize = off;
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;
DEMO_FILE_SYSTEM *demo_fs;
int ret = 0;
demo_fh = (DEMO_FILE_HANDLE *)file_handle;
demo_fs = demo_fh->demo_fs;
lock_file_system(&demo_fs->lock);
if ((ret = demo_buffer_resize(session, demo_fh, offset)) == 0)
demo_fh->size = (size_t)offset;
unlock_file_system(&demo_fs->lock);
return (ret);
}
/*
* 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;
DEMO_FILE_SYSTEM *demo_fs;
size_t off;
int ret = 0;
demo_fh = (DEMO_FILE_HANDLE *)file_handle;
demo_fs = demo_fh->demo_fs;
wtext = demo_fs->wtext;
off = (size_t)offset;
lock_file_system(&demo_fs->lock);
++demo_fs->write_ops;
if ((ret = demo_buffer_resize(
session, demo_fh, offset + (wt_off_t)(len + DEMO_FILE_SIZE_INCREMENT))) == 0) {
memcpy((uint8_t *)demo_fh->buf + off, buf, len);
if (off + len > demo_fh->size)
demo_fh->size = off + len;
}
unlock_file_system(&demo_fs->lock);
if (ret == 0)
return (0);
(void)wtext->err_printf(wtext, session,
"%s: handle-write: failed to write %zu bytes at offset %zu: %s", demo_fh->iface.name, len,
off, wtext->strerror(wtext, NULL, ret));
return (ret);
}
/*
* 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;
demo_fs = demo_fh->demo_fs;
wtext = demo_fs->wtext;
if (demo_fh->ref != 0) {
(void)wtext->err_printf(wtext, session, "demo_handle_remove: %s: file is currently open",
demo_fh->iface.name, wtext->strerror(wtext, NULL, EBUSY));
return (EBUSY);
}
TAILQ_REMOVE(&demo_fs->fileq, demo_fh, q);
/* Clean up private information. */
free(demo_fh->buf);
/* 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);
}
static const char *home;
int
main(void)
{
WT_CURSOR *cursor;
WT_SESSION *session;
const char *key, *open_config, *uri;
int i;
int ret = 0;
char kbuf[64];
/*
* 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;
/*
* 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. Finally, pass in two pieces of configuration information to our initialization
* function as the "config" value.
*/
open_config =
"create,log=(enabled=true),extensions=(local={entry=demo_file_system_create,early_load=true,"
"config={config_string=\"demo-file-system\",config_value=37}})";
/* 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 == NULL ? "." : home,
return (EXIT_FAILURE);
}
if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0) {
fprintf(stderr, "WT_CONNECTION.open_session: %s\n", wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
uri = "table:fs";
if ((ret = session->create(session, uri, "key_format=S,value_format=S")) != 0) {
fprintf(stderr, "WT_SESSION.create: %s: %s\n", uri, wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
if ((ret = session->open_cursor(session, uri, NULL, NULL, &cursor)) != 0) {
fprintf(stderr, "WT_SESSION.open_cursor: %s: %s\n", uri, wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
for (i = 0; i < 1000; ++i) {
(void)snprintf(kbuf, sizeof(kbuf), "%010d KEY -----", i);
cursor->set_key(cursor, kbuf);
cursor->set_value(cursor, "--- VALUE ---");
if ((ret = cursor->insert(cursor)) != 0) {
fprintf(stderr, "WT_CURSOR.insert: %s: %s\n", kbuf, wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
}
if ((ret = cursor->close(cursor)) != 0) {
fprintf(stderr, "WT_CURSOR.close: %s\n", wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
if ((ret = session->open_cursor(session, uri, NULL, NULL, &cursor)) != 0) {
fprintf(stderr, "WT_SESSION.open_cursor: %s: %s\n", uri, wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
for (i = 0; i < 1000; ++i) {
if ((ret = cursor->next(cursor)) != 0) {
fprintf(stderr, "WT_CURSOR.insert: %s: %s\n", kbuf, wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
(void)snprintf(kbuf, sizeof(kbuf), "%010d KEY -----", i);
if ((ret = cursor->get_key(cursor, &key)) != 0) {
fprintf(stderr, "WT_CURSOR.get_key: %s\n", wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
if (strcmp(kbuf, key) != 0) {
fprintf(stderr, "Key mismatch: %s, %s\n", kbuf, key);
return (EXIT_FAILURE);
}
}
if ((ret = cursor->next(cursor)) != WT_NOTFOUND) {
fprintf(
stderr, "WT_CURSOR.insert: expected WT_NOTFOUND, got %s\n", wiredtiger_strerror(ret));
return (EXIT_FAILURE);
}
if ((ret = conn->close(conn, NULL)) != 0) {
fprintf(stderr, "Error closing connection to %s: %s\n", home == NULL ? "." : home,
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
WT_CONNECTION::set_file_system
int set_file_system(WT_CONNECTION *connection, WT_FILE_SYSTEM *fs, const char *config)
Configure a custom file system.
WT_FILE_HANDLE::fh_size
int(* fh_size)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t *sizep)
Return the size of a file.
Definition: wiredtiger.in:4880
WT_CONFIG_ITEM::val
int64_t val
The numeric value of a configuration boolean or integer.
Definition: wiredtiger.in:3564
WT_FILE_SYSTEM::fs_rename
int(* fs_rename)(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *from, const char *to, uint32_t flags)
Rename a named file system object.
Definition: wiredtiger.in:4632
WT_EXTENSION_API
Table of WiredTiger extension methods.
Definition: wiredtiger_ext.h:58
WT_FILE_SYSTEM
The interface implemented by applications to provide a custom file system implementation.
Definition: wiredtiger.in:4512
WT_SESSION::create
int create(WT_SESSION *session, const char *name, const char *config)
Create a table, column group, index or file.
WT_FILE_HANDLE::fh_extend_nolock
int(* fh_extend_nolock)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset)
Extend the file.
Definition: wiredtiger.in:4764
WT_SESSION::open_cursor
int open_cursor(WT_SESSION *session, const char *uri, WT_CURSOR *to_dup, const char *config, WT_CURSOR **cursorp)
Open a new cursor on a data source or duplicate an existing cursor.
WT_FILE_SYSTEM::fs_exist
int(* fs_exist)(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name, bool *existp)
Return if the named file system object exists.
Definition: wiredtiger.in:4564
WT_CURSOR::get_key
int get_key(WT_CURSOR *cursor,...)
Get the key for the current record.
wiredtiger_strerror
const char * wiredtiger_strerror(int error)
Return information about a WiredTiger error as a string (see WT_SESSION::strerror for a thread-safe A...
WT_CURSOR
A WT_CURSOR handle is the interface to a cursor.
Definition: wiredtiger.in:199
WT_FILE_HANDLE::fh_truncate
int(* fh_truncate)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset)
Truncate the file.
Definition: wiredtiger.in:4926
WT_FILE_HANDLE::fh_unmap
int(* fh_unmap)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, void *mapped_region, size_t length, void *mapped_cookie)
Unmap a memory mapped file, based on the POSIX 1003.1 standard munmap.
Definition: wiredtiger.in:4854
WT_FILE_HANDLE::fh_extend
int(* fh_extend)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset)
Extend the file.
Definition: wiredtiger.in:4739
WT_FILE_SYSTEM::terminate
int(* terminate)(WT_FILE_SYSTEM *file_system, WT_SESSION *session)
A callback performed when the file system is closed and will no longer be accessed by the WiredTiger ...
Definition: wiredtiger.in:4658
WT_FILE_SYSTEM::fs_open_file
int(* fs_open_file)(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name, WT_FS_OPEN_FILE_TYPE file_type, uint32_t flags, WT_FILE_HANDLE **file_handlep)
Open a handle for a named file system object.
Definition: wiredtiger.in:4598
WT_FILE_HANDLE::fh_sync
int(* fh_sync)(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
Make outstanding file writes durable and do not return until writes are complete.
Definition: wiredtiger.in:4895
WT_CONFIG_PARSER::close
int close(WT_CONFIG_PARSER *config_parser)
Close the configuration scanner releasing any resources.
WT_CONFIG_ARG
struct WT_CONFIG_ARG WT_CONFIG_ARG
Definition: wiredtiger.in:3893
WT_CONNECTION::get_extension_api
WT_EXTENSION_API * get_extension_api(WT_CONNECTION *wt_conn)
Return a reference to the WiredTiger extension functions.
WT_CONFIG_ITEM::str
const char * str
The value of a configuration string.
Definition: wiredtiger.in:3549
WT_EXTENSION_API::strerror
const char *(* strerror)(WT_EXTENSION_API *, WT_SESSION *session, int error)
Return information about an error as a string.
Definition: wiredtiger_ext.h:100
WT_CONNECTION::open_session
int open_session(WT_CONNECTION *connection, WT_EVENT_HANDLER *event_handler, const char *config, WT_SESSION **sessionp)
Open a session.
WT_FILE_HANDLE::fh_map
int(* fh_map)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, void *mapped_regionp, size_t *lengthp, void *mapped_cookiep)
Map a file into memory, based on the POSIX 1003.1 standard mmap.
Definition: wiredtiger.in:4798
WT_FILE_HANDLE
A file handle implementation returned by WT_FILE_SYSTEM::open_file.
Definition: wiredtiger.in:4676
WT_CURSOR::next
int next(WT_CURSOR *cursor)
Return the next record.
WT_CONFIG_PARSER
A handle that can be used to search and traverse configuration strings compatible with WiredTiger API...
Definition: wiredtiger.in:3665
WT_FILE_SYSTEM::fs_remove
int(* fs_remove)(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name, uint32_t flags)
Remove a named file system object.
Definition: wiredtiger.in:4615
WT_FILE_HANDLE::fh_map_preload
int(* fh_map_preload)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, const void *map, size_t length, void *mapped_cookie)
Preload part of a memory mapped file, based on the POSIX 1003.1 standard madvise.
Definition: wiredtiger.in:4836
WT_CONNECTION
A connection to a WiredTiger database.
Definition: wiredtiger.in:2040
WT_CONFIG_PARSER::next
int next(WT_CONFIG_PARSER *config_parser, WT_CONFIG_ITEM *key, WT_CONFIG_ITEM *value)
Return the next key/value pair.
WT_FILE_HANDLE::fh_write
int(* fh_write)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset, size_t length, const void *buf)
Write to a file, based on the POSIX 1003.1 standard pwrite.
Definition: wiredtiger.in:4943
WT_CONFIG_ITEM::len
size_t len
The number of bytes in the value referenced by str.
Definition: wiredtiger.in:3552
WT_CURSOR::close
int close(WT_CURSOR *cursor)
Close the cursor.
WT_FS_OPEN_FILE_TYPE
WT_FS_OPEN_FILE_TYPE
WT_FILE_SYSTEM::open_file file types.
Definition: wiredtiger.in:4450
WT_CURSOR::set_value
void set_value(WT_CURSOR *cursor,...)
Set the value for the next operation.
WT_FILE_HANDLE::fh_advise
int(* fh_advise)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset, wt_off_t len, int advice)
Indicate expected future use of file ranges, based on the POSIX 1003.1 standard fadvise.
Definition: wiredtiger.in:4714
WT_FILE_SYSTEM::fs_directory_list
int(* fs_directory_list)(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *directory, const char *prefix, char ***dirlist, uint32_t *countp)
Return a list of file names for the named directory.
Definition: wiredtiger.in:4528
WT_EXTENSION_API::err_printf
int(* err_printf)(WT_EXTENSION_API *wt_api, WT_SESSION *session, const char *fmt,...)
Insert an error message into the WiredTiger error stream.
Definition: wiredtiger_ext.h:76
WT_FILE_SYSTEM::fs_size
int(* fs_size)(WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name, wt_off_t *sizep)
Return the size of a named file system object.
Definition: wiredtiger.in:4645
wiredtiger_open
int wiredtiger_open(const char *home, WT_EVENT_HANDLER *event_handler, const char *config, WT_CONNECTION **connectionp)
Open a connection to a database.
WT_CURSOR::set_key
void set_key(WT_CURSOR *cursor,...)
Set the key for the next operation.
WT_FILE_HANDLE::fh_sync_nowait
int(* fh_sync_nowait)(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
Schedule the outstanding file writes required for durability and return immediately.
Definition: wiredtiger.in:4909
WT_EXTENSION_API::config_parser_open_arg
int(* config_parser_open_arg)(WT_EXTENSION_API *wt_api, WT_SESSION *session, WT_CONFIG_ARG *config, WT_CONFIG_PARSER **config_parserp)
Create a handle that can be used to parse or create configuration strings compatible with WiredTiger ...
Definition: wiredtiger_ext.h:211
WT_NOTFOUND
#define WT_NOTFOUND
Item not found.
Definition: wiredtiger.in:3828
WT_SESSION
All data operations are performed in the context of a WT_SESSION.
Definition: wiredtiger.in:751
WT_CONNECTION::close
int close(WT_CONNECTION *connection, const char *config)
Close a connection.
WT_FILE_HANDLE::fh_lock
int(* fh_lock)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, bool lock)
Lock/unlock a file from the perspective of other processes running in the system, where necessary.
Definition: wiredtiger.in:4777
WT_FILE_HANDLE::fh_read
int(* fh_read)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset, size_t len, void *buf)
Read from a file, based on the POSIX 1003.1 standard pread.
Definition: wiredtiger.in:4868
WT_FILE_HANDLE::name
char * name
The name of the file, set by WT_FILE_SYSTEM::open_file.
Definition: wiredtiger.in:4685
WT_FILE_HANDLE::fh_map_discard
int(* fh_map_discard)(WT_FILE_HANDLE *file_handle, WT_SESSION *session, void *map, size_t length, void *mapped_cookie)
Unmap part of a memory mapped file, based on the POSIX 1003.1 standard madvise.
Definition: wiredtiger.in:4817
WT_FILE_SYSTEM::fs_directory_list_free
int(* fs_directory_list_free)(WT_FILE_SYSTEM *file_system, WT_SESSION *session, char **dirlist, uint32_t count)
Free memory allocated by WT_FILE_SYSTEM::directory_list.
Definition: wiredtiger.in:4551
WT_FILE_HANDLE::close
int(* close)(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
Close a file handle, the handle will not be further accessed by WiredTiger.
Definition: wiredtiger.in:4696
WT_CURSOR::insert
int insert(WT_CURSOR *cursor)
Insert a record and optionally update an existing record.
WT_CONFIG_ITEM
The configuration information returned by the WiredTiger configuration parsing functions in the WT_EX...
Definition: wiredtiger.in:3538