From 0d4092f4eaeafc6d556bdab3802a1813aeed7ecb Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Mon, 16 Sep 2019 14:16:14 +0300 Subject: [PATCH] mdbx: add to env_copy() support for pipe/socket. Change-Id: Ib2fc0249b494b885f28265f877de9953f089b403 --- src/elements/core.c | 266 ++++++++++++++++++++++++++++-------------- src/elements/osal.c | 69 ++++++++++- src/elements/osal.h | 11 ++ src/tools/mdbx_copy.c | 17 ++- 4 files changed, 265 insertions(+), 98 deletions(-) diff --git a/src/elements/core.c b/src/elements/core.c index 83c9c7fa..173f12c5 100644 --- a/src/elements/core.c +++ b/src/elements/core.c @@ -11743,6 +11743,8 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, return rc; } +/**** COPYING *****************************************************************/ + #ifndef MDBX_WBUF #define MDBX_WBUF ((size_t)1024 * 1024) #endif @@ -11772,7 +11774,6 @@ static THREAD_RESULT __cold THREAD_CALL mdbx_env_copythr(void *arg) { uint8_t *ptr; int toggle = 0; int rc; - size_t offset = pgno2bytes(my->mc_env, NUM_METAS); mdbx_condmutex_lock(&my->mc_condmutex); while (!my->mc_error) { @@ -11784,12 +11785,11 @@ static THREAD_RESULT __cold THREAD_CALL mdbx_env_copythr(void *arg) { ptr = my->mc_wbuf[toggle]; again: if (wsize > 0 && !my->mc_error) { - rc = mdbx_pwrite(my->mc_fd, ptr, wsize, offset); + rc = mdbx_write(my->mc_fd, ptr, wsize); if (rc != MDBX_SUCCESS) { my->mc_error = rc; break; } - offset += wsize; } /* If there's an overflow page tail, write it too */ @@ -11984,9 +11984,42 @@ done: return rc; } +static void compact_fixup_meta(MDBX_env *env, MDBX_page *meta) { + /* Calculate filesize taking in account shrink/growing thresholds */ + if (meta->mp_meta.mm_geo.next > meta->mp_meta.mm_geo.now) { + const pgno_t aligned = + pgno_align2os_pgno(env, pgno_add(meta->mp_meta.mm_geo.next, + meta->mp_meta.mm_geo.grow - + meta->mp_meta.mm_geo.next % + meta->mp_meta.mm_geo.grow)); + meta->mp_meta.mm_geo.now = aligned; + } else if (meta->mp_meta.mm_geo.next < meta->mp_meta.mm_geo.now) { + meta->mp_meta.mm_geo.now = meta->mp_meta.mm_geo.next; + const pgno_t aligner = meta->mp_meta.mm_geo.grow + ? meta->mp_meta.mm_geo.grow + : meta->mp_meta.mm_geo.shrink; + const pgno_t aligned = + pgno_align2os_pgno(env, meta->mp_meta.mm_geo.next + aligner - + meta->mp_meta.mm_geo.next % aligner); + meta->mp_meta.mm_geo.now = aligned; + } + + if (meta->mp_meta.mm_geo.now < meta->mp_meta.mm_geo.lower) + meta->mp_meta.mm_geo.now = meta->mp_meta.mm_geo.lower; + if (meta->mp_meta.mm_geo.now > meta->mp_meta.mm_geo.upper) + meta->mp_meta.mm_geo.now = meta->mp_meta.mm_geo.upper; + + /* Update signature */ + assert(meta->mp_meta.mm_geo.now >= meta->mp_meta.mm_geo.next); + meta->mp_meta.mm_datasync_sign = mdbx_meta_sign(&meta->mp_meta); +} + /* Copy environment with compaction. */ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn, - mdbx_filehandle_t fd, uint8_t *buffer) { + mdbx_filehandle_t fd, uint8_t *buffer, + const bool dest_is_pipe) { + const size_t meta_bytes = pgno2bytes(env, NUM_METAS); + uint8_t *const data_buffer = buffer + meta_bytes; MDBX_page *const meta = mdbx_init_metas(env, buffer); /* copy canary sequenses if present */ if (read_txn->mt_canary.v) { @@ -12001,6 +12034,12 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn, * fix any breakage like page leaks from ITS#8174. */ meta->mp_meta.mm_dbs[MAIN_DBI].md_flags = read_txn->mt_dbs[MAIN_DBI].md_flags; + compact_fixup_meta(env, meta); + if (dest_is_pipe) { + int rc = mdbx_write(fd, buffer, meta_bytes); + if (rc != MDBX_SUCCESS) + return rc; + } } else { /* Count free pages + GC pages. Subtract from last_pg * to find the new last_pg, which also becomes the new root. */ @@ -12031,9 +12070,9 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn, if (unlikely(rc != MDBX_SUCCESS)) return rc; - ctx.mc_wbuf[0] = buffer + pgno2bytes(env, NUM_METAS); - memset(ctx.mc_wbuf[0], 0, MDBX_WBUF * 2); - ctx.mc_wbuf[1] = ctx.mc_wbuf[0] + MDBX_WBUF; + memset(data_buffer, 0, MDBX_WBUF * 2); + ctx.mc_wbuf[0] = data_buffer; + ctx.mc_wbuf[1] = data_buffer + MDBX_WBUF; ctx.mc_next_pgno = NUM_METAS; ctx.mc_env = env; ctx.mc_fd = fd; @@ -12042,7 +12081,12 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn, mdbx_thread_t thread; int thread_err = mdbx_thread_create(&thread, mdbx_env_copythr, &ctx); if (likely(thread_err == MDBX_SUCCESS)) { - rc = mdbx_env_cwalk(&ctx, &root, 0); + if (dest_is_pipe) { + compact_fixup_meta(env, meta); + rc = mdbx_write(fd, buffer, meta_bytes); + } + if (rc == MDBX_SUCCESS) + rc = mdbx_env_cwalk(&ctx, &root, 0); mdbx_env_cthr_toggle(&ctx, 1 | MDBX_EOF); thread_err = mdbx_thread_join(thread); mdbx_condmutex_destroy(&ctx.mc_condmutex); @@ -12054,59 +12098,58 @@ static int __cold mdbx_env_compact(MDBX_env *env, MDBX_txn *read_txn, if (unlikely(ctx.mc_error != MDBX_SUCCESS)) return ctx.mc_error; - if (root > new_root) { - mdbx_error("post-compactification root %" PRIaPGNO - " GT expected %" PRIaPGNO " (source DB corrupted)", - root, new_root); - return MDBX_CORRUPTED; /* page leak or corrupt DB */ - } - if (root < new_root) { - mdbx_notice("post-compactification root %" PRIaPGNO - " LT expected %" PRIaPGNO " (page leak(s) in source DB)", - root, new_root); - /* fixup meta */ - meta->mp_meta.mm_dbs[MAIN_DBI].md_root = root; - meta->mp_meta.mm_geo.next = root + 1; + if (dest_is_pipe) { + if (root != new_root) { + mdbx_error("post-compactification root %" PRIaPGNO + " NE expected %" PRIaPGNO + " (source DB corrupted or has a page leak(s))", + root, new_root); + return MDBX_CORRUPTED; /* page leak or corrupt DB */ + } + } else { + if (root > new_root) { + mdbx_error("post-compactification root %" PRIaPGNO + " GT expected %" PRIaPGNO " (source DB corrupted)", + root, new_root); + return MDBX_CORRUPTED; /* page leak or corrupt DB */ + } + if (root < new_root) { + mdbx_notice("post-compactification root %" PRIaPGNO + " LT expected %" PRIaPGNO " (page leak(s) in source DB)", + root, new_root); + /* fixup meta */ + meta->mp_meta.mm_dbs[MAIN_DBI].md_root = root; + meta->mp_meta.mm_geo.next = root + 1; + } + compact_fixup_meta(env, meta); } } - /* Calculate filesize taking in account shrink/growing thresholds */ - if (meta->mp_meta.mm_geo.next > meta->mp_meta.mm_geo.now) { - const pgno_t aligned = - pgno_align2os_pgno(env, pgno_add(meta->mp_meta.mm_geo.next, - meta->mp_meta.mm_geo.grow - - meta->mp_meta.mm_geo.next % - meta->mp_meta.mm_geo.grow)); - meta->mp_meta.mm_geo.now = aligned; - } else if (meta->mp_meta.mm_geo.next < meta->mp_meta.mm_geo.now) { - meta->mp_meta.mm_geo.now = meta->mp_meta.mm_geo.next; - const pgno_t aligner = meta->mp_meta.mm_geo.grow - ? meta->mp_meta.mm_geo.grow - : meta->mp_meta.mm_geo.shrink; - const pgno_t aligned = - pgno_align2os_pgno(env, meta->mp_meta.mm_geo.next + aligner - - meta->mp_meta.mm_geo.next % aligner); - meta->mp_meta.mm_geo.now = aligned; - } - - if (meta->mp_meta.mm_geo.now < meta->mp_meta.mm_geo.lower) - meta->mp_meta.mm_geo.now = meta->mp_meta.mm_geo.lower; - if (meta->mp_meta.mm_geo.now > meta->mp_meta.mm_geo.upper) - meta->mp_meta.mm_geo.now = meta->mp_meta.mm_geo.upper; - - /* Update signature */ - assert(meta->mp_meta.mm_geo.now >= meta->mp_meta.mm_geo.next); - meta->mp_meta.mm_datasync_sign = mdbx_meta_sign(&meta->mp_meta); - /* Extend file if required */ - return (meta->mp_meta.mm_geo.now != meta->mp_meta.mm_geo.next) - ? mdbx_ftruncate(fd, pgno2bytes(env, meta->mp_meta.mm_geo.now)) - : MDBX_SUCCESS; + if (meta->mp_meta.mm_geo.now != meta->mp_meta.mm_geo.next) { + const size_t whole_size = pgno2bytes(env, meta->mp_meta.mm_geo.now); + if (!dest_is_pipe) + return mdbx_ftruncate(fd, whole_size); + + const size_t used_size = pgno2bytes(env, meta->mp_meta.mm_geo.next); + memset(data_buffer, 0, MDBX_WBUF); + for (size_t offset = used_size; offset < whole_size;) { + const size_t chunk = + (MDBX_WBUF < whole_size - offset) ? MDBX_WBUF : whole_size - offset; + /* copy to avoit EFAULT in case swapped-out */ + int rc = mdbx_write(fd, data_buffer, chunk); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + offset += chunk; + } + } + return MDBX_SUCCESS; } /* Copy environment as-is. */ static int __cold mdbx_env_copy_asis(MDBX_env *env, MDBX_txn *read_txn, - mdbx_filehandle_t fd, uint8_t *buffer) { + mdbx_filehandle_t fd, uint8_t *buffer, + const bool dest_is_pipe) { /* We must start the actual read txn after blocking writers */ int rc = mdbx_txn_end(read_txn, MDBX_END_RESET_TMP); if (unlikely(rc != MDBX_SUCCESS)) @@ -12135,35 +12178,67 @@ static int __cold mdbx_env_copy_asis(MDBX_env *env, MDBX_txn *read_txn, mdbx_txn_unlock(env); /* Copy the data */ - const uint64_t whole_size = + const size_t whole_size = mdbx_roundup2(pgno2bytes(env, read_txn->mt_end_pgno), env->me_os_psize); const size_t used_size = pgno2bytes(env, read_txn->mt_next_pgno); mdbx_jitter4testing(false); + + if (dest_is_pipe) + rc = mdbx_write(fd, buffer, meta_bytes); + + uint8_t *const data_buffer = buffer + meta_bytes; + for (size_t offset = meta_bytes; rc == MDBX_SUCCESS && offset < used_size;) { + if (dest_is_pipe) { +#if defined(__linux__) || defined(__gnu_linux__) + off_t in_offset = offset; + const intptr_t written = + sendfile(fd, env->me_fd, &in_offset, used_size - offset); + if (unlikely(written <= 0)) { + rc = written ? errno : MDBX_ENODATA; + break; + } + offset = in_offset; + continue; +#endif + } else { #if __GLIBC_PREREQ(2, 27) && defined(_GNU_SOURCE) - for (off_t in_offset = meta_bytes; in_offset < (off_t)used_size;) { - off_t out_offset = in_offset; - ssize_t bytes_copied = copy_file_range( - env->me_fd, &in_offset, fd, &out_offset, used_size - in_offset, 0); - if (unlikely(bytes_copied <= 0)) { - rc = bytes_copied ? errno : MDBX_ENODATA; - break; + off_t in_offset = offset, out_offset = offset; + ssize_t bytes_copied = copy_file_range( + env->me_fd, &in_offset, fd, &out_offset, used_size - offset, 0); + if (unlikely(bytes_copied <= 0)) { + rc = bytes_copied ? errno : MDBX_ENODATA; + break; + } + offset = in_offset; + continue; +#endif } - } -#else - uint8_t *data_buffer = buffer + meta_bytes; - for (size_t offset = meta_bytes; offset < used_size;) { + + /* fallback to portable */ const size_t chunk = (MDBX_WBUF < used_size - offset) ? MDBX_WBUF : used_size - offset; + /* copy to avoit EFAULT in case swapped-out */ memcpy(data_buffer, env->me_map + offset, chunk); - rc = mdbx_pwrite(fd, data_buffer, chunk, offset); - if (unlikely(rc != MDBX_SUCCESS)) - break; + rc = mdbx_write(fd, data_buffer, chunk); offset += chunk; } -#endif - if (likely(rc == MDBX_SUCCESS) && whole_size != used_size) - rc = mdbx_ftruncate(fd, whole_size); + /* Extend file if required */ + if (likely(rc == MDBX_SUCCESS) && whole_size != used_size) { + if (!dest_is_pipe) + rc = mdbx_ftruncate(fd, whole_size); + else { + memset(data_buffer, 0, MDBX_WBUF); + for (size_t offset = used_size; + rc == MDBX_SUCCESS && offset < whole_size;) { + const size_t chunk = + (MDBX_WBUF < whole_size - offset) ? MDBX_WBUF : whole_size - offset; + /* copy to avoit EFAULT in case swapped-out */ + rc = mdbx_write(fd, data_buffer, chunk); + offset += chunk; + } + } + } return rc; } @@ -12176,49 +12251,60 @@ int __cold mdbx_env_copy2fd(MDBX_env *env, mdbx_filehandle_t fd, if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; - int rc = mdbx_fseek(fd, 0); - if (unlikely(rc != MDBX_SUCCESS)) - return rc; + const int dest_is_pipe = mdbx_is_pipe(fd); + if (MDBX_IS_ERROR(dest_is_pipe)) + return dest_is_pipe; + + if (!dest_is_pipe) { + int rc = mdbx_fseek(fd, 0); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + } const size_t buffer_size = pgno2bytes(env, NUM_METAS) + ((flags & MDBX_CP_COMPACT) ? MDBX_WBUF * 2 : MDBX_WBUF); uint8_t *buffer = NULL; - rc = mdbx_memalign_alloc(env->me_os_psize, buffer_size, (void **)&buffer); + int rc = mdbx_memalign_alloc(env->me_os_psize, buffer_size, (void **)&buffer); if (unlikely(rc != MDBX_SUCCESS)) return rc; MDBX_txn *read_txn = NULL; /* Do the lock/unlock of the reader mutex before starting the - * write txn. Otherwise other read txns could block writers. */ + * write txn. Otherwise other read txns could block writers. */ rc = mdbx_txn_begin(env, NULL, MDBX_RDONLY, &read_txn); if (unlikely(rc != MDBX_SUCCESS)) { mdbx_memalign_free(buffer); return rc; } - /* Firstly write a stub to meta-pages. - * Now we sure to incomplete copy will not be used. */ - memset(buffer, -1, pgno2bytes(env, NUM_METAS)); - rc = mdbx_pwrite(fd, buffer, pgno2bytes(env, NUM_METAS), 0); + if (!dest_is_pipe) { + /* Firstly write a stub to meta-pages. + * Now we sure to incomplete copy will not be used. */ + memset(buffer, -1, pgno2bytes(env, NUM_METAS)); + rc = mdbx_write(fd, buffer, pgno2bytes(env, NUM_METAS)); + } + if (likely(rc == MDBX_SUCCESS)) { memset(buffer, 0, pgno2bytes(env, NUM_METAS)); rc = (flags & MDBX_CP_COMPACT) - ? mdbx_env_compact(env, read_txn, fd, buffer) - : mdbx_env_copy_asis(env, read_txn, fd, buffer); + ? mdbx_env_compact(env, read_txn, fd, buffer, dest_is_pipe) + : mdbx_env_copy_asis(env, read_txn, fd, buffer, dest_is_pipe); } mdbx_txn_abort(read_txn); - if (likely(rc == MDBX_SUCCESS)) - rc = mdbx_filesync(fd, MDBX_SYNC_DATA | MDBX_SYNC_SIZE); + if (!dest_is_pipe) { + if (likely(rc == MDBX_SUCCESS)) + rc = mdbx_filesync(fd, MDBX_SYNC_DATA | MDBX_SYNC_SIZE | MDBX_SYNC_IODQ); - /* Write actual meta */ - if (likely(rc == MDBX_SUCCESS)) - rc = mdbx_pwrite(fd, buffer, pgno2bytes(env, NUM_METAS), 0); + /* Write actual meta */ + if (likely(rc == MDBX_SUCCESS)) + rc = mdbx_pwrite(fd, buffer, pgno2bytes(env, NUM_METAS), 0); - if (likely(rc == MDBX_SUCCESS)) - rc = mdbx_filesync(fd, MDBX_SYNC_DATA | MDBX_SYNC_IODQ); + if (likely(rc == MDBX_SUCCESS)) + rc = mdbx_filesync(fd, MDBX_SYNC_DATA | MDBX_SYNC_IODQ); + } mdbx_memalign_free(buffer); return rc; @@ -12277,6 +12363,8 @@ int __cold mdbx_env_copy(MDBX_env *env, const char *dest_path, unsigned flags) { return rc; } +/******************************************************************************/ + int __cold mdbx_env_set_flags(MDBX_env *env, unsigned flags, int onoff) { if (unlikely(!env)) return MDBX_EINVAL; diff --git a/src/elements/osal.c b/src/elements/osal.c index 8c19d75a..51af33a1 100644 --- a/src/elements/osal.c +++ b/src/elements/osal.c @@ -636,9 +636,9 @@ MDBX_INTERNAL_FUNC int mdbx_pwrite(mdbx_filehandle_t fd, const void *buf, ov.OffsetHigh = HIGH_DWORD(offset); DWORD written; - if (unlikely(!WriteFile(fd, buf, - (bytes <= MAX_WRITE) ? (DWORD)bytes : MAX_WRITE, - &written, &ov))) + if (unlikely(!WriteFile( + fd, buf, likely(bytes <= MAX_WRITE) ? (DWORD)bytes : MAX_WRITE, + &written, &ov))) return GetLastError(); if (likely(bytes == written)) return MDBX_SUCCESS; @@ -646,7 +646,7 @@ MDBX_INTERNAL_FUNC int mdbx_pwrite(mdbx_filehandle_t fd, const void *buf, STATIC_ASSERT_MSG(sizeof(off_t) >= sizeof(size_t), "libmdbx requires 64-bit file I/O on 64-bit systems"); const intptr_t written = - pwrite(fd, buf, (bytes <= MAX_WRITE) ? bytes : MAX_WRITE, offset); + pwrite(fd, buf, likely(bytes <= MAX_WRITE) ? bytes : MAX_WRITE, offset); if (likely(bytes == (size_t)written)) return MDBX_SUCCESS; if (written < 0) { @@ -662,6 +662,36 @@ MDBX_INTERNAL_FUNC int mdbx_pwrite(mdbx_filehandle_t fd, const void *buf, } } +MDBX_INTERNAL_FUNC int mdbx_write(mdbx_filehandle_t fd, const void *buf, + size_t bytes) { + while (true) { +#if defined(_WIN32) || defined(_WIN64) + DWORD written; + if (unlikely(!WriteFile( + fd, buf, likely(bytes <= MAX_WRITE) ? (DWORD)bytes : MAX_WRITE, + &written, nullptr))) + return GetLastError(); + if (likely(bytes == written)) + return MDBX_SUCCESS; +#else + STATIC_ASSERT_MSG(sizeof(off_t) >= sizeof(size_t), + "libmdbx requires 64-bit file I/O on 64-bit systems"); + const intptr_t written = + write(fd, buf, likely(bytes <= MAX_WRITE) ? bytes : MAX_WRITE); + if (likely(bytes == (size_t)written)) + return MDBX_SUCCESS; + if (written < 0) { + const int rc = errno; + if (rc != EINTR) + return rc; + continue; + } +#endif + bytes -= written; + buf = (char *)buf + written; + } +} + int mdbx_pwritev(mdbx_filehandle_t fd, struct iovec *iov, int iovcnt, uint64_t offset, size_t expected_written) { #if defined(_WIN32) || defined(_WIN64) || defined(__APPLE__) @@ -750,6 +780,37 @@ int mdbx_filesize(mdbx_filehandle_t fd, uint64_t *length) { return MDBX_SUCCESS; } +MDBX_INTERNAL_FUNC int mdbx_is_pipe(mdbx_filehandle_t fd) { +#if defined(_WIN32) || defined(_WIN64) + switch (GetFileType(fd)) { + case FILE_TYPE_DISK: + return MDBX_RESULT_FALSE; + case FILE_TYPE_CHAR: + case FILE_TYPE_PIPE: + return MDBX_RESULT_TRUE; + default: + return GetLastError(); + } +#else + struct stat info; + if (fstat(fd, &info)) + return errno; + switch (info.st_mode & S_IFMT) { + case S_IFBLK: + case S_IFREG: + return MDBX_RESULT_FALSE; + case S_IFCHR: + case S_IFIFO: + case S_IFSOCK: + return MDBX_RESULT_TRUE; + case S_IFDIR: + case S_IFLNK: + default: + return MDBX_INCOMPATIBLE; + } +#endif +} + MDBX_INTERNAL_FUNC int mdbx_ftruncate(mdbx_filehandle_t fd, uint64_t length) { #if defined(_WIN32) || defined(_WIN64) if (mdbx_SetFileInformationByHandle) { diff --git a/src/elements/osal.h b/src/elements/osal.h index a58d6aa2..9e6764c3 100644 --- a/src/elements/osal.h +++ b/src/elements/osal.h @@ -85,6 +85,10 @@ #endif #endif /* !xBSD */ +#if defined(__linux__) || defined(__gnu_linux__) +#include +#endif /* Linux */ + #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 0 #endif @@ -494,7 +498,11 @@ MDBX_INTERNAL_FUNC int mdbx_vasprintf(char **strp, const char *fmt, va_list ap); /* OS abstraction layer stuff */ /* max bytes to write in one call */ +#if defined(_WIN32) || defined(_WIN64) +#define MAX_WRITE UINT32_C(0x01000000) +#else #define MAX_WRITE UINT32_C(0x3fff0000) +#endif #if defined(__linux__) || defined(__gnu_linux__) MDBX_INTERNAL_VAR uint32_t mdbx_linux_kernel_version; @@ -553,6 +561,8 @@ MDBX_INTERNAL_FUNC int mdbx_pread(mdbx_filehandle_t fd, void *buf, size_t count, uint64_t offset); MDBX_INTERNAL_FUNC int mdbx_pwrite(mdbx_filehandle_t fd, const void *buf, size_t count, uint64_t offset); +MDBX_INTERNAL_FUNC int mdbx_write(mdbx_filehandle_t fd, const void *buf, + size_t count); MDBX_INTERNAL_FUNC int mdbx_thread_create(mdbx_thread_t *thread, @@ -576,6 +586,7 @@ MDBX_INTERNAL_FUNC int mdbx_openfile(const char *pathname, int flags, bool exclusive); MDBX_INTERNAL_FUNC int mdbx_closefile(mdbx_filehandle_t fd); MDBX_INTERNAL_FUNC int mdbx_removefile(const char *pathname); +MDBX_INTERNAL_FUNC int mdbx_is_pipe(mdbx_filehandle_t fd); typedef struct mdbx_mmap_param { union { diff --git a/src/tools/mdbx_copy.c b/src/tools/mdbx_copy.c index 29191848..a58fd740 100644 --- a/src/tools/mdbx_copy.c +++ b/src/tools/mdbx_copy.c @@ -49,12 +49,15 @@ int main(int argc, char *argv[]) { const char *progname = argv[0], *act; unsigned flags = MDBX_RDONLY; unsigned cpflags = 0; + bool quiet = false; for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) { if (argv[1][1] == 'n' && argv[1][2] == '\0') flags |= MDBX_NOSUBDIR; else if (argv[1][1] == 'c' && argv[1][2] == '\0') cpflags |= MDBX_CP_COMPACT; + else if (argv[1][1] == 'q' && argv[1][2] == '\0') + quiet = true; else if (argv[1][1] == 'V' && argv[1][2] == '\0') { printf("mdbx_copy version %d.%d.%d.%d\n" " - source: %s %s, commit %s, tree %s\n" @@ -74,7 +77,8 @@ int main(int argc, char *argv[]) { } if (argc < 2 || argc > 3) { - fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname); + fprintf(stderr, "usage: %s [-V] [-q] [-c] [-n] srcpath [dstpath]\n", + progname); exit(EXIT_FAILURE); } @@ -91,10 +95,13 @@ int main(int argc, char *argv[]) { signal(SIGTERM, signal_handler); #endif /* !WINDOWS */ - printf("mdbx_copy %s (%s, T-%s)\nRunning for copy %s to %s...\n", - mdbx_version.git.describe, mdbx_version.git.datetime, - mdbx_version.git.tree, argv[1], (argc == 2) ? "stdout" : argv[2]); - fflush(NULL); + if (!quiet) { + fprintf((argc == 2) ? stderr : stdout, + "mdbx_copy %s (%s, T-%s)\nRunning for copy %s to %s...\n", + mdbx_version.git.describe, mdbx_version.git.datetime, + mdbx_version.git.tree, argv[1], (argc == 2) ? "stdout" : argv[2]); + fflush(NULL); + } act = "opening environment"; rc = mdbx_env_create(&env);