/*- * 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 /* * 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 { WT_FILE_SYSTEM iface; /* * 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 { WT_FILE_HANDLE iface; /* * 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_ITEM k, v; WT_CONFIG_PARSER *config_parser; WT_EXTENSION_API *wtext; 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_EXTENSION_API *wtext; 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) ret = demo_file_size((WT_FILE_HANDLE *)demo_fh, session, sizep); 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; WT_EXTENSION_API *wtext; 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; WT_EXTENSION_API *wtext; 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; WT_EXTENSION_API *wtext; 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; WT_EXTENSION_API *wtext; 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_CONNECTION *conn; 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; /*! [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. 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, wiredtiger_strerror(ret)); return (EXIT_FAILURE); } /*! [WT_FILE_SYSTEM register] */ 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, wiredtiger_strerror(ret)); return (EXIT_FAILURE); } return (EXIT_SUCCESS); }