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