1 /*-
2 * Public Domain 2014-2018 MongoDB, Inc.
3 * Public Domain 2008-2014 WiredTiger, Inc.
4 *
5 * This is free and unencumbered software released into the public domain.
6 *
7 * Anyone is free to copy, modify, publish, use, compile, sell, or
8 * distribute this software, either in source code form or as a compiled
9 * binary, for any purpose, commercial or non-commercial, and by any
10 * means.
11 *
12 * In jurisdictions that recognize copyright laws, the author or authors
13 * of this software dedicate any and all copyright interest in the
14 * software to the public domain. We make this dedication for the benefit
15 * of the public at large and to the detriment of our heirs and
16 * successors. We intend this dedication to be an overt act of
17 * relinquishment in perpetuity of all present and future rights to this
18 * software under copyright law.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 * OTHER DEALINGS IN THE SOFTWARE.
27 *
28 * ex_file_system.c
29 * demonstrates how to use the custom file system interface
30 */
31 #include <test_util.h>
32
33 /*
34 * This example code uses pthread functions for portable locking, we ignore
35 * errors for simplicity.
36 */
37 static void
allocate_file_system_lock(pthread_rwlock_t * lockp)38 allocate_file_system_lock(pthread_rwlock_t *lockp)
39 {
40 error_check(pthread_rwlock_init(lockp, NULL));
41 }
42
43 static void
destroy_file_system_lock(pthread_rwlock_t * lockp)44 destroy_file_system_lock(pthread_rwlock_t *lockp)
45 {
46 error_check(pthread_rwlock_destroy(lockp));
47 }
48
49 static void
lock_file_system(pthread_rwlock_t * lockp)50 lock_file_system(pthread_rwlock_t *lockp)
51 {
52 error_check(pthread_rwlock_wrlock(lockp));
53 }
54
55 static void
unlock_file_system(pthread_rwlock_t * lockp)56 unlock_file_system(pthread_rwlock_t *lockp)
57 {
58 error_check(pthread_rwlock_unlock(lockp));
59 }
60
61 /*
62 * Example file system implementation, using memory buffers to represent files.
63 */
64 typedef struct {
65 WT_FILE_SYSTEM iface;
66
67 /*
68 * WiredTiger performs schema and I/O operations in parallel, all file
69 * system and file handle access must be thread-safe. This example uses
70 * a single, global file system lock for simplicity; real applications
71 * might require finer granularity, for example, a single lock for the
72 * file system handle list and per-handle locks serializing I/O.
73 */
74 pthread_rwlock_t lock; /* Lock */
75
76 int opened_file_count;
77 int opened_unique_file_count;
78 int closed_file_count;
79 int read_ops;
80 int write_ops;
81
82 /* Queue of file handles */
83 TAILQ_HEAD(demo_file_handle_qh, demo_file_handle) fileq;
84
85 WT_EXTENSION_API *wtext; /* Extension functions */
86
87 } DEMO_FILE_SYSTEM;
88
89 typedef struct demo_file_handle {
90 WT_FILE_HANDLE iface;
91
92 /*
93 * Add custom file handle fields after the interface.
94 */
95 DEMO_FILE_SYSTEM *demo_fs; /* Enclosing file system */
96
97 TAILQ_ENTRY(demo_file_handle) q; /* Queue of handles */
98 uint32_t ref; /* Reference count */
99
100 char *buf; /* In-memory contents */
101 size_t bufsize; /* In-memory buffer size */
102
103 size_t size; /* Read/write data size */
104 } DEMO_FILE_HANDLE;
105
106 /*
107 * Extension initialization function.
108 */
109 #ifdef _WIN32
110 /*
111 * Explicitly export this function so it is visible when loading extensions.
112 */
113 __declspec(dllexport)
114 #endif
115 int demo_file_system_create(WT_CONNECTION *, WT_CONFIG_ARG *);
116
117 /*
118 * Forward function declarations for file system API implementation
119 */
120 static int demo_fs_open(WT_FILE_SYSTEM *, WT_SESSION *,
121 const char *, WT_FS_OPEN_FILE_TYPE, uint32_t, WT_FILE_HANDLE **);
122 static int demo_fs_directory_list(WT_FILE_SYSTEM *, WT_SESSION *,
123 const char *, const char *, char ***, uint32_t *);
124 static int demo_fs_directory_list_free(
125 WT_FILE_SYSTEM *, WT_SESSION *, char **, uint32_t);
126 static int demo_fs_exist(WT_FILE_SYSTEM *, WT_SESSION *, const char *, bool *);
127 static int demo_fs_remove(
128 WT_FILE_SYSTEM *, WT_SESSION *, const char *, uint32_t);
129 static int demo_fs_rename(
130 WT_FILE_SYSTEM *, WT_SESSION *, const char *, const char *, uint32_t);
131 static int demo_fs_size(
132 WT_FILE_SYSTEM *, WT_SESSION *, const char *, wt_off_t *);
133 static int demo_fs_terminate(WT_FILE_SYSTEM *, WT_SESSION *);
134
135 /*
136 * Forward function declarations for file handle API implementation
137 */
138 static int demo_file_close(WT_FILE_HANDLE *, WT_SESSION *);
139 static int demo_file_lock(WT_FILE_HANDLE *, WT_SESSION *, bool);
140 static int demo_file_read(
141 WT_FILE_HANDLE *, WT_SESSION *, wt_off_t, size_t, void *);
142 static int demo_file_size(WT_FILE_HANDLE *, WT_SESSION *, wt_off_t *);
143 static int demo_file_sync(WT_FILE_HANDLE *, WT_SESSION *);
144 static int demo_file_truncate(WT_FILE_HANDLE *, WT_SESSION *, wt_off_t);
145 static int demo_file_write(
146 WT_FILE_HANDLE *, WT_SESSION *, wt_off_t, size_t, const void *);
147
148 /*
149 * Forward function declarations for internal functions
150 */
151 static int demo_handle_remove(WT_SESSION *, DEMO_FILE_HANDLE *);
152 static DEMO_FILE_HANDLE *demo_handle_search(WT_FILE_SYSTEM *, const char *);
153
154 #define DEMO_FILE_SIZE_INCREMENT 32768
155
156 /*
157 * string_match --
158 * Return if a string matches a byte string of len bytes.
159 */
160 static bool
byte_string_match(const char * str,const char * bytes,size_t len)161 byte_string_match(const char *str, const char *bytes, size_t len)
162 {
163 return (strncmp(str, bytes, len) == 0 && (str)[(len)] == '\0');
164 }
165
166 /*
167 * demo_file_system_create --
168 * Initialization point for demo file system
169 */
170 int
demo_file_system_create(WT_CONNECTION * conn,WT_CONFIG_ARG * config)171 demo_file_system_create(WT_CONNECTION *conn, WT_CONFIG_ARG *config)
172 {
173 DEMO_FILE_SYSTEM *demo_fs;
174 WT_CONFIG_ITEM k, v;
175 WT_CONFIG_PARSER *config_parser;
176 WT_EXTENSION_API *wtext;
177 WT_FILE_SYSTEM *file_system;
178 int ret = 0;
179
180 wtext = conn->get_extension_api(conn);
181
182 if ((demo_fs = calloc(1, sizeof(DEMO_FILE_SYSTEM))) == NULL) {
183 (void)wtext->err_printf(wtext, NULL,
184 "demo_file_system_create: %s",
185 wtext->strerror(wtext, NULL, ENOMEM));
186 return (ENOMEM);
187 }
188 demo_fs->wtext = wtext;
189 file_system = (WT_FILE_SYSTEM *)demo_fs;
190
191 /*
192 * Applications may have their own configuration information to pass to
193 * the underlying filesystem implementation. See the main function for
194 * the setup of those configuration strings; here we parse configuration
195 * information as passed in by main, through WiredTiger.
196 */
197 if ((ret = wtext->config_parser_open_arg(
198 wtext, NULL, config, &config_parser)) != 0) {
199 (void)wtext->err_printf(wtext, NULL,
200 "WT_EXTENSION_API.config_parser_open: config: %s",
201 wtext->strerror(wtext, NULL, ret));
202 goto err;
203 }
204
205 /* Step through our configuration values. */
206 printf("Custom file system configuration\n");
207 while ((ret = config_parser->next(config_parser, &k, &v)) == 0) {
208 if (byte_string_match("config_string", k.str, k.len)) {
209 printf("\t" "key %.*s=\"%.*s\"\n",
210 (int)k.len, k.str, (int)v.len, v.str);
211 continue;
212 }
213 if (byte_string_match("config_value", k.str, k.len)) {
214 printf("\t" "key %.*s=%" PRId64 "\n",
215 (int)k.len, k.str, v.val);
216 continue;
217 }
218 ret = EINVAL;
219 (void)wtext->err_printf(wtext, NULL,
220 "WT_CONFIG_PARSER.next: unexpected configuration "
221 "information: %.*s=%.*s: %s",
222 (int)k.len, k.str, (int)v.len, v.str,
223 wtext->strerror(wtext, NULL, ret));
224 goto err;
225 }
226
227 /* Check for expected parser termination and close the parser. */
228 if (ret != WT_NOTFOUND) {
229 (void)wtext->err_printf(wtext, NULL,
230 "WT_CONFIG_PARSER.next: config: %s",
231 wtext->strerror(wtext, NULL, ret));
232 goto err;
233 }
234 if ((ret = config_parser->close(config_parser)) != 0) {
235 (void)wtext->err_printf(wtext, NULL,
236 "WT_CONFIG_PARSER.close: config: %s",
237 wtext->strerror(wtext, NULL, ret));
238 goto err;
239 }
240
241 allocate_file_system_lock(&demo_fs->lock);
242
243 /* Initialize the in-memory jump table. */
244 file_system->fs_directory_list = demo_fs_directory_list;
245 file_system->fs_directory_list_free = demo_fs_directory_list_free;
246 file_system->fs_exist = demo_fs_exist;
247 file_system->fs_open_file = demo_fs_open;
248 file_system->fs_remove = demo_fs_remove;
249 file_system->fs_rename = demo_fs_rename;
250 file_system->fs_size = demo_fs_size;
251 file_system->terminate = demo_fs_terminate;
252
253 if ((ret = conn->set_file_system(conn, file_system, NULL)) != 0) {
254 (void)wtext->err_printf(wtext, NULL,
255 "WT_CONNECTION.set_file_system: %s",
256 wtext->strerror(wtext, NULL, ret));
257 goto err;
258 }
259 return (0);
260
261 err: free(demo_fs);
262 /* An error installing the file system is fatal. */
263 exit(1);
264 }
265
266 /*
267 * demo_fs_open --
268 * fopen for our demo file system
269 */
270 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)271 demo_fs_open(WT_FILE_SYSTEM *file_system, WT_SESSION *session,
272 const char *name, WT_FS_OPEN_FILE_TYPE file_type, uint32_t flags,
273 WT_FILE_HANDLE **file_handlep)
274 {
275 DEMO_FILE_HANDLE *demo_fh;
276 DEMO_FILE_SYSTEM *demo_fs;
277 WT_EXTENSION_API *wtext;
278 WT_FILE_HANDLE *file_handle;
279 int ret = 0;
280
281 (void)file_type; /* Unused */
282 (void)flags; /* Unused */
283
284 *file_handlep = NULL;
285
286 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
287 demo_fh = NULL;
288 wtext = demo_fs->wtext;
289
290 lock_file_system(&demo_fs->lock);
291 ++demo_fs->opened_file_count;
292
293 /*
294 * First search the file queue, if we find it, assert there's only a
295 * single reference, we only support a single handle on any file.
296 */
297 demo_fh = demo_handle_search(file_system, name);
298 if (demo_fh != NULL) {
299 if (demo_fh->ref != 0) {
300 (void)wtext->err_printf(wtext, session,
301 "demo_fs_open: %s: file already open", name);
302 ret = EBUSY;
303 goto err;
304 }
305
306 demo_fh->ref = 1;
307
308 *file_handlep = (WT_FILE_HANDLE *)demo_fh;
309
310 unlock_file_system(&demo_fs->lock);
311 return (0);
312 }
313
314 /* The file hasn't been opened before, create a new one. */
315 if ((demo_fh = calloc(1, sizeof(DEMO_FILE_HANDLE))) == NULL) {
316 ret = ENOMEM;
317 goto err;
318 }
319
320 /* Initialize private information. */
321 demo_fh->demo_fs = demo_fs;
322 demo_fh->ref = 1;
323 if ((demo_fh->buf = calloc(1, DEMO_FILE_SIZE_INCREMENT)) == NULL) {
324 ret = ENOMEM;
325 goto err;
326 }
327 demo_fh->bufsize = DEMO_FILE_SIZE_INCREMENT;
328 demo_fh->size = 0;
329
330 /* Initialize public information. */
331 file_handle = (WT_FILE_HANDLE *)demo_fh;
332 if ((file_handle->name = strdup(name)) == NULL) {
333 ret = ENOMEM;
334 goto err;
335 }
336
337 /*
338 * Setup the function call table for our custom file system. Set the
339 * function pointer to NULL where our implementation doesn't support
340 * the functionality.
341 */
342 file_handle->close = demo_file_close;
343 file_handle->fh_advise = NULL;
344 file_handle->fh_extend = NULL;
345 file_handle->fh_extend_nolock = NULL;
346 file_handle->fh_lock = demo_file_lock;
347 file_handle->fh_map = NULL;
348 file_handle->fh_map_discard = NULL;
349 file_handle->fh_map_preload = NULL;
350 file_handle->fh_unmap = NULL;
351 file_handle->fh_read = demo_file_read;
352 file_handle->fh_size = demo_file_size;
353 file_handle->fh_sync = demo_file_sync;
354 file_handle->fh_sync_nowait = NULL;
355 file_handle->fh_truncate = demo_file_truncate;
356 file_handle->fh_write = demo_file_write;
357
358 TAILQ_INSERT_HEAD(&demo_fs->fileq, demo_fh, q);
359 ++demo_fs->opened_unique_file_count;
360
361 *file_handlep = file_handle;
362
363 if (0) {
364 err: free(demo_fh->buf);
365 free(demo_fh);
366 }
367
368 unlock_file_system(&demo_fs->lock);
369 return (ret);
370 }
371
372 /*
373 * demo_fs_directory_list --
374 * Return a list of files in a given sub-directory.
375 */
376 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)377 demo_fs_directory_list(WT_FILE_SYSTEM *file_system,
378 WT_SESSION *session, const char *directory,
379 const char *prefix, char ***dirlistp, uint32_t *countp)
380 {
381 DEMO_FILE_HANDLE *demo_fh;
382 DEMO_FILE_SYSTEM *demo_fs;
383 size_t len, prefix_len;
384 uint32_t allocated, count;
385 int ret = 0;
386 char *name, **entries;
387 void *p;
388
389 (void)session; /* Unused */
390
391 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
392
393 *dirlistp = NULL;
394 *countp = 0;
395
396 entries = NULL;
397 allocated = count = 0;
398 len = strlen(directory);
399 prefix_len = prefix == NULL ? 0 : strlen(prefix);
400
401 lock_file_system(&demo_fs->lock);
402 TAILQ_FOREACH(demo_fh, &demo_fs->fileq, q) {
403 name = demo_fh->iface.name;
404 if (strncmp(name, directory, len) != 0 ||
405 (prefix != NULL && strncmp(name, prefix, prefix_len) != 0))
406 continue;
407
408 /*
409 * Increase the list size in groups of 10, it doesn't
410 * matter if the list is a bit longer than necessary.
411 */
412 if (count >= allocated) {
413 p = realloc(
414 entries, (allocated + 10) * sizeof(*entries));
415 if (p == NULL) {
416 ret = ENOMEM;
417 goto err;
418 }
419
420 entries = p;
421 memset(entries + allocated * sizeof(*entries),
422 0, 10 * sizeof(*entries));
423 allocated += 10;
424 }
425 entries[count++] = strdup(name);
426 }
427
428 *dirlistp = entries;
429 *countp = count;
430
431 err: unlock_file_system(&demo_fs->lock);
432 if (ret == 0)
433 return (0);
434
435 if (entries != NULL) {
436 while (count > 0)
437 free(entries[--count]);
438 free(entries);
439 }
440
441 return (ret);
442 }
443
444 /*
445 * demo_fs_directory_list_free --
446 * Free memory allocated by demo_fs_directory_list.
447 */
448 static int
demo_fs_directory_list_free(WT_FILE_SYSTEM * file_system,WT_SESSION * session,char ** dirlist,uint32_t count)449 demo_fs_directory_list_free(WT_FILE_SYSTEM *file_system,
450 WT_SESSION *session, char **dirlist, uint32_t count)
451 {
452 (void)file_system;
453 (void)session;
454
455 if (dirlist != NULL) {
456 while (count > 0)
457 free(dirlist[--count]);
458 free(dirlist);
459 }
460 return (0);
461 }
462
463 /*
464 * demo_fs_exist --
465 * Return if the file exists.
466 */
467 static int
demo_fs_exist(WT_FILE_SYSTEM * file_system,WT_SESSION * session,const char * name,bool * existp)468 demo_fs_exist(WT_FILE_SYSTEM *file_system,
469 WT_SESSION *session, const char *name, bool *existp)
470 {
471 DEMO_FILE_SYSTEM *demo_fs;
472
473 (void)session; /* Unused */
474
475 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
476
477 lock_file_system(&demo_fs->lock);
478 *existp = demo_handle_search(file_system, name) != NULL;
479 unlock_file_system(&demo_fs->lock);
480
481 return (0);
482 }
483
484 /*
485 * demo_fs_remove --
486 * POSIX remove.
487 */
488 static int
demo_fs_remove(WT_FILE_SYSTEM * file_system,WT_SESSION * session,const char * name,uint32_t flags)489 demo_fs_remove(WT_FILE_SYSTEM *file_system,
490 WT_SESSION *session, const char *name, uint32_t flags)
491 {
492 DEMO_FILE_SYSTEM *demo_fs;
493 DEMO_FILE_HANDLE *demo_fh;
494 int ret = 0;
495
496 (void)session; /* Unused */
497 (void)flags; /* Unused */
498
499 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
500
501 ret = ENOENT;
502 lock_file_system(&demo_fs->lock);
503 if ((demo_fh = demo_handle_search(file_system, name)) != NULL)
504 ret = demo_handle_remove(session, demo_fh);
505 unlock_file_system(&demo_fs->lock);
506
507 return (ret);
508 }
509
510 /*
511 * demo_fs_rename --
512 * POSIX rename.
513 */
514 static int
demo_fs_rename(WT_FILE_SYSTEM * file_system,WT_SESSION * session,const char * from,const char * to,uint32_t flags)515 demo_fs_rename(WT_FILE_SYSTEM *file_system,
516 WT_SESSION *session, const char *from, const char *to, uint32_t flags)
517 {
518 DEMO_FILE_HANDLE *demo_fh;
519 DEMO_FILE_SYSTEM *demo_fs;
520 char *copy;
521 int ret = 0;
522
523 (void)session; /* Unused */
524 (void)flags; /* Unused */
525
526 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
527
528 lock_file_system(&demo_fs->lock);
529 if ((demo_fh = demo_handle_search(file_system, from)) == NULL)
530 ret = ENOENT;
531 else if ((copy = strdup(to)) == NULL)
532 ret = ENOMEM;
533 else {
534 free(demo_fh->iface.name);
535 demo_fh->iface.name = copy;
536 }
537 unlock_file_system(&demo_fs->lock);
538 return (ret);
539 }
540
541 /*
542 * demo_fs_size --
543 * Get the size of a file in bytes, by file name.
544 */
545 static int
demo_fs_size(WT_FILE_SYSTEM * file_system,WT_SESSION * session,const char * name,wt_off_t * sizep)546 demo_fs_size(WT_FILE_SYSTEM *file_system,
547 WT_SESSION *session, const char *name, wt_off_t *sizep)
548 {
549 DEMO_FILE_SYSTEM *demo_fs;
550 DEMO_FILE_HANDLE *demo_fh;
551 int ret = 0;
552
553 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
554
555 ret = ENOENT;
556 lock_file_system(&demo_fs->lock);
557 if ((demo_fh = demo_handle_search(file_system, name)) != NULL)
558 ret = demo_file_size((WT_FILE_HANDLE *)demo_fh, session, sizep);
559 unlock_file_system(&demo_fs->lock);
560
561 return (ret);
562 }
563
564 /*
565 * demo_fs_terminate --
566 * Discard any resources on termination
567 */
568 static int
demo_fs_terminate(WT_FILE_SYSTEM * file_system,WT_SESSION * session)569 demo_fs_terminate(WT_FILE_SYSTEM *file_system, WT_SESSION *session)
570 {
571 DEMO_FILE_HANDLE *demo_fh, *demo_fh_tmp;
572 DEMO_FILE_SYSTEM *demo_fs;
573 int ret = 0, tret;
574
575 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
576
577 TAILQ_FOREACH_SAFE(demo_fh, &demo_fs->fileq, q, demo_fh_tmp)
578 if ((tret =
579 demo_handle_remove(session, demo_fh)) != 0 && ret == 0)
580 ret = tret;
581
582 printf("Custom file system\n");
583 printf("\t%d unique file opens\n", demo_fs->opened_unique_file_count);
584 printf("\t%d files opened\n", demo_fs->opened_file_count);
585 printf("\t%d files closed\n", demo_fs->closed_file_count);
586 printf("\t%d reads, %d writes\n",
587 demo_fs->read_ops, demo_fs->write_ops);
588
589 destroy_file_system_lock(&demo_fs->lock);
590 free(demo_fs);
591
592 return (ret);
593 }
594
595 /*
596 * demo_file_close --
597 * ANSI C close.
598 */
599 static int
demo_file_close(WT_FILE_HANDLE * file_handle,WT_SESSION * session)600 demo_file_close(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
601 {
602 DEMO_FILE_HANDLE *demo_fh;
603 DEMO_FILE_SYSTEM *demo_fs;
604
605 (void)session; /* Unused */
606
607 demo_fh = (DEMO_FILE_HANDLE *)file_handle;
608 demo_fs = demo_fh->demo_fs;
609
610 lock_file_system(&demo_fs->lock);
611 if (--demo_fh->ref == 0)
612 ++demo_fs->closed_file_count;
613 unlock_file_system(&demo_fs->lock);
614
615 return (0);
616 }
617
618 /*
619 * demo_file_lock --
620 * Lock/unlock a file.
621 */
622 static int
demo_file_lock(WT_FILE_HANDLE * file_handle,WT_SESSION * session,bool lock)623 demo_file_lock(WT_FILE_HANDLE *file_handle, WT_SESSION *session, bool lock)
624 {
625 /* Locks are always granted. */
626 (void)file_handle; /* Unused */
627 (void)session; /* Unused */
628 (void)lock; /* Unused */
629 return (0);
630 }
631
632 /*
633 * demo_file_read --
634 * POSIX pread.
635 */
636 static int
demo_file_read(WT_FILE_HANDLE * file_handle,WT_SESSION * session,wt_off_t offset,size_t len,void * buf)637 demo_file_read(WT_FILE_HANDLE *file_handle,
638 WT_SESSION *session, wt_off_t offset, size_t len, void *buf)
639 {
640 DEMO_FILE_HANDLE *demo_fh;
641 DEMO_FILE_SYSTEM *demo_fs;
642 WT_EXTENSION_API *wtext;
643 size_t off;
644 int ret = 0;
645
646 demo_fh = (DEMO_FILE_HANDLE *)file_handle;
647 demo_fs = demo_fh->demo_fs;
648 wtext = demo_fs->wtext;
649 off = (size_t)offset;
650
651 lock_file_system(&demo_fs->lock);
652 ++demo_fs->read_ops;
653 if (off < demo_fh->size) {
654 if (len > demo_fh->size - off)
655 len = demo_fh->size - off;
656 memcpy(buf, (uint8_t *)demo_fh->buf + off, len);
657 } else
658 ret = EIO; /* EOF */
659 unlock_file_system(&demo_fs->lock);
660 if (ret == 0)
661 return (0);
662
663 (void)wtext->err_printf(wtext, session,
664 "%s: handle-read: failed to read %zu bytes at offset %zu: %s",
665 demo_fh->iface.name, len, off, wtext->strerror(wtext, NULL, ret));
666 return (ret);
667 }
668
669 /*
670 * demo_file_size --
671 * Get the size of a file in bytes, by file handle.
672 */
673 static int
demo_file_size(WT_FILE_HANDLE * file_handle,WT_SESSION * session,wt_off_t * sizep)674 demo_file_size(
675 WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t *sizep)
676 {
677 DEMO_FILE_HANDLE *demo_fh;
678 DEMO_FILE_SYSTEM *demo_fs;
679
680 (void)session; /* Unused */
681
682 demo_fh = (DEMO_FILE_HANDLE *)file_handle;
683 demo_fs = demo_fh->demo_fs;
684
685 lock_file_system(&demo_fs->lock);
686 *sizep = (wt_off_t)demo_fh->size;
687 unlock_file_system(&demo_fs->lock);
688 return (0);
689 }
690
691 /*
692 * demo_file_sync --
693 * Ensure the content of the file is stable. This is a no-op in our
694 * memory backed file system.
695 */
696 static int
demo_file_sync(WT_FILE_HANDLE * file_handle,WT_SESSION * session)697 demo_file_sync(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
698 {
699 (void)file_handle; /* Unused */
700 (void)session; /* Unused */
701
702 return (0);
703 }
704
705 /*
706 * demo_buffer_resize --
707 * Resize the write buffer.
708 */
709 static int
demo_buffer_resize(WT_SESSION * session,DEMO_FILE_HANDLE * demo_fh,wt_off_t offset)710 demo_buffer_resize(
711 WT_SESSION *session, DEMO_FILE_HANDLE *demo_fh, wt_off_t offset)
712 {
713 DEMO_FILE_SYSTEM *demo_fs;
714 WT_EXTENSION_API *wtext;
715 size_t off;
716 void *p;
717
718 demo_fs = demo_fh->demo_fs;
719 wtext = demo_fs->wtext;
720 off = (size_t)offset;
721
722 /* Grow the buffer as necessary and clear any new space in the file. */
723 if (demo_fh->bufsize >= off)
724 return (0);
725
726 if ((p = realloc(demo_fh->buf, off)) == NULL) {
727 (void)wtext->err_printf(wtext, session,
728 "%s: failed to resize buffer",
729 demo_fh->iface.name, wtext->strerror(wtext, NULL, ENOMEM));
730 return (ENOMEM);
731 }
732 memset((uint8_t *)p + demo_fh->bufsize, 0, off - demo_fh->bufsize);
733 demo_fh->buf = p;
734 demo_fh->bufsize = off;
735
736 return (0);
737 }
738
739 /*
740 * demo_file_truncate --
741 * POSIX ftruncate.
742 */
743 static int
demo_file_truncate(WT_FILE_HANDLE * file_handle,WT_SESSION * session,wt_off_t offset)744 demo_file_truncate(
745 WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset)
746 {
747 DEMO_FILE_HANDLE *demo_fh;
748 DEMO_FILE_SYSTEM *demo_fs;
749 int ret = 0;
750
751 demo_fh = (DEMO_FILE_HANDLE *)file_handle;
752 demo_fs = demo_fh->demo_fs;
753
754 lock_file_system(&demo_fs->lock);
755 if ((ret = demo_buffer_resize(session, demo_fh, offset)) == 0)
756 demo_fh->size = (size_t)offset;
757 unlock_file_system(&demo_fs->lock);
758 return (ret);
759 }
760
761 /*
762 * demo_file_write --
763 * POSIX pwrite.
764 */
765 static int
demo_file_write(WT_FILE_HANDLE * file_handle,WT_SESSION * session,wt_off_t offset,size_t len,const void * buf)766 demo_file_write(WT_FILE_HANDLE *file_handle, WT_SESSION *session,
767 wt_off_t offset, size_t len, const void *buf)
768 {
769 DEMO_FILE_HANDLE *demo_fh;
770 DEMO_FILE_SYSTEM *demo_fs;
771 WT_EXTENSION_API *wtext;
772 size_t off;
773 int ret = 0;
774
775 demo_fh = (DEMO_FILE_HANDLE *)file_handle;
776 demo_fs = demo_fh->demo_fs;
777 wtext = demo_fs->wtext;
778 off = (size_t)offset;
779
780 lock_file_system(&demo_fs->lock);
781 ++demo_fs->write_ops;
782 if ((ret = demo_buffer_resize(session, demo_fh,
783 offset + (wt_off_t)(len + DEMO_FILE_SIZE_INCREMENT))) == 0) {
784 memcpy((uint8_t *)demo_fh->buf + off, buf, len);
785 if (off + len > demo_fh->size)
786 demo_fh->size = off + len;
787 }
788 unlock_file_system(&demo_fs->lock);
789 if (ret == 0)
790 return (0);
791
792 (void)wtext->err_printf(wtext, session,
793 "%s: handle-write: failed to write %zu bytes at offset %zu: %s",
794 demo_fh->iface.name, len, off, wtext->strerror(wtext, NULL, ret));
795 return (ret);
796 }
797
798 /*
799 * demo_handle_remove --
800 * Destroy an in-memory file handle. Should only happen on remove or
801 * shutdown.
802 */
803 static int
demo_handle_remove(WT_SESSION * session,DEMO_FILE_HANDLE * demo_fh)804 demo_handle_remove(WT_SESSION *session, DEMO_FILE_HANDLE *demo_fh)
805 {
806 DEMO_FILE_SYSTEM *demo_fs;
807 WT_EXTENSION_API *wtext;
808
809 demo_fs = demo_fh->demo_fs;
810 wtext = demo_fs->wtext;
811
812 if (demo_fh->ref != 0) {
813 (void)wtext->err_printf(wtext, session,
814 "demo_handle_remove: %s: file is currently open",
815 demo_fh->iface.name, wtext->strerror(wtext, NULL, EBUSY));
816 return (EBUSY);
817 }
818
819 TAILQ_REMOVE(&demo_fs->fileq, demo_fh, q);
820
821 /* Clean up private information. */
822 free(demo_fh->buf);
823
824 /* Clean up public information. */
825 free(demo_fh->iface.name);
826
827 free(demo_fh);
828
829 return (0);
830 }
831
832 /*
833 * demo_handle_search --
834 * Return a matching handle, if one exists.
835 */
836 static DEMO_FILE_HANDLE *
demo_handle_search(WT_FILE_SYSTEM * file_system,const char * name)837 demo_handle_search(WT_FILE_SYSTEM *file_system, const char *name)
838 {
839 DEMO_FILE_HANDLE *demo_fh;
840 DEMO_FILE_SYSTEM *demo_fs;
841
842 demo_fs = (DEMO_FILE_SYSTEM *)file_system;
843
844 TAILQ_FOREACH(demo_fh, &demo_fs->fileq, q)
845 if (strcmp(demo_fh->iface.name, name) == 0)
846 break;
847 return (demo_fh);
848 }
849
850 static const char *home;
851
852 int
main(void)853 main(void)
854 {
855 WT_CONNECTION *conn;
856 WT_CURSOR *cursor;
857 WT_SESSION *session;
858 const char *key, *open_config, *uri;
859 int i;
860 int ret = 0;
861 char kbuf[64];
862
863 /*
864 * Create a clean test directory for this run of the test program if the
865 * environment variable isn't already set (as is done by make check).
866 */
867 if (getenv("WIREDTIGER_HOME") == NULL) {
868 home = "WT_HOME";
869 ret = system("rm -rf WT_HOME && mkdir WT_HOME");
870 } else
871 home = NULL;
872
873 /*! [WT_FILE_SYSTEM register] */
874 /*
875 * Setup a configuration string that will load our custom file system.
876 * Use the special local extension to indicate that the entry point is
877 * in the same executable. Also enable early load for this extension,
878 * since WiredTiger needs to be able to find it before doing any file
879 * operations. Finally, pass in two pieces of configuration information
880 * to our initialization function as the "config" value.
881 */
882 open_config = "create,log=(enabled=true),extensions=(local={"
883 "entry=demo_file_system_create,early_load=true,"
884 "config={config_string=\"demo-file-system\",config_value=37}"
885 "})";
886 /* Open a connection to the database, creating it if necessary. */
887 if ((ret = wiredtiger_open(home, NULL, open_config, &conn)) != 0) {
888 fprintf(stderr, "Error connecting to %s: %s\n",
889 home == NULL ? "." : home, wiredtiger_strerror(ret));
890 return (EXIT_FAILURE);
891 }
892 /*! [WT_FILE_SYSTEM register] */
893
894 if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0) {
895 fprintf(stderr, "WT_CONNECTION.open_session: %s\n",
896 wiredtiger_strerror(ret));
897 return (EXIT_FAILURE);
898 }
899 uri = "table:fs";
900 if ((ret = session->create(
901 session, uri, "key_format=S,value_format=S")) != 0) {
902 fprintf(stderr, "WT_SESSION.create: %s: %s\n",
903 uri, wiredtiger_strerror(ret));
904 return (EXIT_FAILURE);
905 }
906 if ((ret = session->open_cursor(
907 session, uri, NULL, NULL, &cursor)) != 0) {
908 fprintf(stderr, "WT_SESSION.open_cursor: %s: %s\n",
909 uri, wiredtiger_strerror(ret));
910 return (EXIT_FAILURE);
911 }
912 for (i = 0; i < 1000; ++i) {
913 (void)snprintf(kbuf, sizeof(kbuf), "%010d KEY -----", i);
914 cursor->set_key(cursor, kbuf);
915 cursor->set_value(cursor, "--- VALUE ---");
916 if ((ret = cursor->insert(cursor)) != 0) {
917 fprintf(stderr, "WT_CURSOR.insert: %s: %s\n",
918 kbuf, wiredtiger_strerror(ret));
919 return (EXIT_FAILURE);
920 }
921 }
922 if ((ret = cursor->close(cursor)) != 0) {
923 fprintf(stderr, "WT_CURSOR.close: %s\n",
924 wiredtiger_strerror(ret));
925 return (EXIT_FAILURE);
926 }
927 if ((ret = session->open_cursor(
928 session, uri, NULL, NULL, &cursor)) != 0) {
929 fprintf(stderr, "WT_SESSION.open_cursor: %s: %s\n",
930 uri, wiredtiger_strerror(ret));
931 return (EXIT_FAILURE);
932 }
933 for (i = 0; i < 1000; ++i) {
934 if ((ret = cursor->next(cursor)) != 0) {
935 fprintf(stderr, "WT_CURSOR.insert: %s: %s\n",
936 kbuf, wiredtiger_strerror(ret));
937 return (EXIT_FAILURE);
938 }
939 (void)snprintf(kbuf, sizeof(kbuf), "%010d KEY -----", i);
940 if ((ret = cursor->get_key(cursor, &key)) != 0) {
941 fprintf(stderr, "WT_CURSOR.get_key: %s\n",
942 wiredtiger_strerror(ret));
943 return (EXIT_FAILURE);
944 }
945 if (strcmp(kbuf, key) != 0) {
946 fprintf(stderr, "Key mismatch: %s, %s\n", kbuf, key);
947 return (EXIT_FAILURE);
948 }
949 }
950 if ((ret = cursor->next(cursor)) != WT_NOTFOUND) {
951 fprintf(stderr,
952 "WT_CURSOR.insert: expected WT_NOTFOUND, got %s\n",
953 wiredtiger_strerror(ret));
954 return (EXIT_FAILURE);
955 }
956
957 if ((ret = conn->close(conn, NULL)) != 0) {
958 fprintf(stderr, "Error closing connection to %s: %s\n",
959 home == NULL ? "." : home, wiredtiger_strerror(ret));
960 return (EXIT_FAILURE);
961 }
962
963 return (EXIT_SUCCESS);
964 }
965