diff options
author | Keith Bostic <keith@wiredtiger.com> | 2016-03-27 12:47:02 -0400 |
---|---|---|
committer | Keith Bostic <keith@wiredtiger.com> | 2016-03-27 12:47:02 -0400 |
commit | 45aa4385e43957970bfb8f768c63bf05afb5d5f8 (patch) | |
tree | d66a42e6b7cb812f0ac4f8a2c9662ea73152c6a0 /src | |
parent | 69b26f1f8338eee18fe0abdba43264e4cbfd443f (diff) | |
download | mongo-45aa4385e43957970bfb8f768c63bf05afb5d5f8.tar.gz |
WT-2330: in-memory configurations should not create on-disk collection files
The wt utility isn't syncing the enclosing directory on a backup file
which leaves us potentially vulnerable on a crash on Linux. WiredTiger
knows how to do the magic, so use a WiredTiger function to do the backup
file copy. This also gets rid of some Windows-specific code in the wt
utility.
Diffstat (limited to 'src')
-rw-r--r-- | src/include/extern.h | 1 | ||||
-rw-r--r-- | src/support/filename.c | 66 | ||||
-rw-r--r-- | src/utilities/util_backup.c | 102 |
3 files changed, 91 insertions, 78 deletions
diff --git a/src/include/extern.h b/src/include/extern.h index f1fbdb4d30a..27e2ccb22de 100644 --- a/src/include/extern.h +++ b/src/include/extern.h @@ -609,6 +609,7 @@ extern int __wt_nfilename( WT_SESSION_IMPL *session, const char *name, size_t na extern int __wt_remove_if_exists(WT_SESSION_IMPL *session, const char *name); extern int __wt_rename_and_sync_directory( WT_SESSION_IMPL *session, const char *from, const char *to); extern int __wt_sync_handle_and_rename( WT_SESSION_IMPL *session, WT_FH **fhp, const char *from, const char *to); +extern int __wt_copy_and_sync(WT_SESSION *wt_session, const char *from, const char *to); extern int __wt_library_init(void); extern int __wt_breakpoint(void); extern void __wt_attach(WT_SESSION_IMPL *session); diff --git a/src/support/filename.c b/src/support/filename.c index ac0aee5686e..b4858d2e982 100644 --- a/src/support/filename.c +++ b/src/support/filename.c @@ -123,3 +123,69 @@ __wt_sync_handle_and_rename( return (__wt_rename_and_sync_directory(session, from, to)); } + +/* + * __wt_copy_and_sync -- + * Copy a file safely; here to support the wt utility. + */ +int +__wt_copy_and_sync(WT_SESSION *wt_session, const char *from, const char *to) +{ + WT_DECL_ITEM(tmp); + WT_DECL_RET; + WT_FH *ffh, *tfh; + WT_SESSION_IMPL *session; + size_t n; + wt_off_t offset, size; + char *buf; + + session = (WT_SESSION_IMPL *)wt_session; + ffh = tfh = NULL; + buf = NULL; + + /* + * Remove the target file if it exists, then create a temporary file, + * copy the original into it and rename it into place. I don't think + * its necessary to remove the file, or create a copy and do a rename, + * it's likely safe to overwrite the backup file directly. I'm doing + * the remove and rename to insulate us from errors in other programs + * that might not detect a corrupted backup file; it's cheap insurance + * in a path where undetected failure is very bad. + */ + WT_RET(__wt_remove_if_exists(session, to)); + + WT_ERR(__wt_scr_alloc(session, 0, &tmp)); + WT_ERR(__wt_buf_fmt(session, tmp, "%s.copy", to)); + + /* Open the from and temporary file handles. */ + WT_ERR(__wt_open(session, from, + WT_FILE_TYPE_REGULAR, WT_OPEN_READONLY, &ffh)); + WT_ERR(__wt_open(session, tmp->data, + WT_FILE_TYPE_REGULAR, WT_OPEN_CREATE | WT_OPEN_EXCLUSIVE, &tfh)); + + /* + * Allocate a copy buffer. Don't use a scratch buffer, this thing is + * big, and we don't want it hanging around. + */ +#define WT_BACKUP_COPY_SIZE (128 * 1024) + WT_ERR(__wt_malloc(session, WT_BACKUP_COPY_SIZE, &buf)); + + /* Get the file's size, then copy the bytes. */ + WT_ERR(__wt_filesize(session, ffh, &size)); + for (offset = 0; size > 0; size -= n, offset += n) { + n = (size_t)WT_MIN(size, WT_BACKUP_COPY_SIZE); + WT_ERR(__wt_read(session, ffh, offset, n, buf)); + WT_ERR(__wt_write(session, tfh, offset, n, buf)); + } + + /* Close the from handle, then swap the temporary file into place. */ + WT_ERR(__wt_close(session, &ffh)); + ret = __wt_sync_handle_and_rename(session, &tfh, tmp->data, to); + +err: WT_TRET(__wt_close(session, &ffh)); + WT_TRET(__wt_close(session, &tfh)); + + __wt_free(session, buf); + __wt_scr_free(session, &tmp); + return (ret); +} diff --git a/src/utilities/util_backup.c b/src/utilities/util_backup.c index b3afc78e9e8..55c0e336111 100644 --- a/src/utilities/util_backup.c +++ b/src/utilities/util_backup.c @@ -8,12 +8,9 @@ #include "util.h" -static int copy(const char *, const char *); +static int copy(WT_SESSION *, const char *, const char *); static int usage(void); -#define CBUF_LEN (128 * 1024) /* Copy buffer and size. */ -static char *cbuf; - /* * append_target -- * Build a list of comma-separated targets. @@ -86,7 +83,7 @@ util_backup(WT_SESSION *session, int argc, char *argv[]) while ( (ret = cursor->next(cursor)) == 0 && (ret = cursor->get_key(cursor, &name)) == 0) - if ((ret = copy(name, directory)) != 0) + if ((ret = copy(session, name, directory)) != 0) goto err; if (ret == WT_NOTFOUND) ret = 0; @@ -98,97 +95,46 @@ util_backup(WT_SESSION *session, int argc, char *argv[]) } err: free(config); - free(cbuf); - return (ret); } static int -copy(const char *name, const char *directory) +copy(WT_SESSION *session, const char *name, const char *directory) { WT_DECL_RET; - ssize_t n; - int ifd, ofd; + size_t len; + char *from, *to; - ret = 1; - ifd = ofd = -1; + from = to = NULL; - if (verbose && - printf("Backing up %s/%s to %s\n", home, name, directory) < 0) { - fprintf(stderr, "%s: %s\n", progname, strerror(errno)); - return (1); - } - - /* Allocate a large copy buffer (use it to build pathnames as well. */ - if (cbuf == NULL && (cbuf = malloc(CBUF_LEN)) == NULL) + /* Build the 2 pathnames we need. */ + len = strlen(home) + strlen(name) + 2; + if ((from = malloc(len)) == NULL) goto memerr; - - /* Open the read file. */ - if (snprintf(cbuf, CBUF_LEN, "%s/%s", home, name) >= CBUF_LEN) + (void)snprintf(from, len, "%s/%s", home, name); + len = strlen(directory) + strlen(name) + 2; + if ((to = malloc(len)) == NULL) goto memerr; - if ((ifd = open(cbuf, O_BINARY | O_RDONLY, 0)) < 0) - goto readerr; + (void)snprintf(to, len, "%s/%s", directory, name); - /* Open the write file. */ - if (snprintf(cbuf, CBUF_LEN, "%s/%s", directory, name) >= CBUF_LEN) - goto memerr; - if ((ofd = open( - cbuf, O_BINARY | O_CREAT | O_WRONLY | O_TRUNC, 0666)) < 0) - goto writerr; - - /* Copy the file. */ - while ((n = read(ifd, cbuf, CBUF_LEN)) > 0) - if (write(ofd, cbuf, (size_t)n) != n) - goto writerr; - if (n != 0) - goto readerr; - - /* - * Close file descriptors (forcing a flush on the write side), and - * check for any errors. - */ - ret = close(ifd); - ifd = -1; - if (ret != 0) - goto readerr; + if (verbose && printf("Backing up %s to %s\n", from, to) < 0) { + fprintf(stderr, "%s: %s\n", progname, strerror(EIO)); + goto err; + } /* - * We need to know this file was successfully written, it's a backup. + * Use WiredTiger to copy the file: ensuring stability of the copied + * file on disk requires care, and WiredTiger knows how to do it. */ -#ifdef _WIN32 - if (FlushFileBuffers((HANDLE)_get_osfhandle(ofd)) == 0) { - DWORD err = GetLastError(); - ret = err; - goto writerr; - } -#else - if (fsync(ofd)) - goto writerr; -#endif - ret = close(ofd); - ofd = -1; - if (ret != 0) - goto writerr; - - /* Success. */ - ret = 0; + if ((ret = __wt_copy_and_sync(session, from, to)) != 0) + fprintf(stderr, "%s to %s: backup copy: %s\n", + from, to, session->strerror(session, ret)); if (0) { -readerr: fprintf(stderr, - "%s: %s/%s: %s\n", progname, home, name, strerror(errno)); - } - if (0) { -writerr: fprintf(stderr, "%s: %s/%s: %s\n", - progname, directory, name, strerror(errno)); - } - if (0) { memerr: fprintf(stderr, "%s: %s\n", progname, strerror(errno)); } - - if (ifd >= 0) - (void)close(ifd); - if (ofd >= 0) - (void)close(ofd); +err: free(from); + free(to); return (ret); } |