mariadb/extra/mariabackup/ds_local.cc

260 lines
6.4 KiB
C++
Raw Normal View History

/******************************************************
Copyright (c) 2011-2013 Percona LLC and/or its affiliates.
Local datasink implementation for XtraBackup.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
2019-05-11 22:19:05 +03:00
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
*******************************************************/
2018-03-28 17:06:27 +02:00
#include <my_global.h>
#include <my_base.h>
#include <mysys_err.h>
#include "common.h"
#include "datasink.h"
#include "fsp0fsp.h"
#ifdef _WIN32
#include <winioctl.h>
#endif
typedef struct {
File fd;
my_bool init_ibd_done;
my_bool is_ibd;
my_bool compressed;
size_t pagesize;
} ds_local_file_t;
static ds_ctxt_t *local_init(const char *root);
static ds_file_t *local_open(ds_ctxt_t *ctxt, const char *path,
MY_STAT *mystat);
static int local_write(ds_file_t *file, const uchar *buf, size_t len);
static int local_close(ds_file_t *file);
static void local_deinit(ds_ctxt_t *ctxt);
extern "C" {
datasink_t datasink_local = {
&local_init,
&local_open,
&local_write,
&local_close,
&local_deinit
};
}
static
ds_ctxt_t *
local_init(const char *root)
{
ds_ctxt_t *ctxt;
if (my_mkdir(root, 0777, MYF(0)) < 0
&& my_errno != EEXIST && my_errno != EISDIR)
{
char errbuf[MYSYS_STRERROR_SIZE];
my_strerror(errbuf, sizeof(errbuf),my_errno);
2018-05-29 23:00:51 +02:00
my_error(EE_CANT_MKDIR, MYF(ME_BELL),
root, my_errno,errbuf, my_errno);
return NULL;
}
ctxt = (ds_ctxt_t *)my_malloc(sizeof(ds_ctxt_t), MYF(MY_FAE));
ctxt->root = my_strdup(root, MYF(MY_FAE));
return ctxt;
}
static
ds_file_t *
local_open(ds_ctxt_t *ctxt, const char *path,
MY_STAT *mystat __attribute__((unused)))
{
char fullpath[FN_REFLEN];
char dirpath[FN_REFLEN];
size_t dirpath_len;
size_t path_len;
ds_local_file_t *local_file;
ds_file_t *file;
File fd;
fn_format(fullpath, path, ctxt->root, "", MYF(MY_RELATIVE_PATH));
/* Create the directory if needed */
dirname_part(dirpath, fullpath, &dirpath_len);
if (my_mkdir(dirpath, 0777, MYF(0)) < 0 && my_errno != EEXIST) {
char errbuf[MYSYS_STRERROR_SIZE];
my_strerror(errbuf, sizeof(errbuf), my_errno);
2018-05-29 23:00:51 +02:00
my_error(EE_CANT_MKDIR, MYF(ME_BELL),
dirpath, my_errno, errbuf);
return NULL;
}
fd = my_create(fullpath, 0, O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
MYF(MY_WME));
if (fd < 0) {
return NULL;
}
path_len = strlen(fullpath) + 1; /* terminating '\0' */
file = (ds_file_t *) my_malloc(sizeof(ds_file_t) +
sizeof(ds_local_file_t) +
path_len,
MYF(MY_FAE));
local_file = (ds_local_file_t *) (file + 1);
local_file->fd = fd;
local_file->init_ibd_done = 0;
local_file->is_ibd = (path_len > 5) && !strcmp(fullpath + path_len - 5, ".ibd");
local_file->compressed = 0;
local_file->pagesize = 0;
file->path = (char *) local_file + sizeof(ds_local_file_t);
memcpy(file->path, fullpath, path_len);
file->ptr = local_file;
return file;
}
/* Calculate size of data without trailing zero bytes. */
static size_t trim_binary_zeros(uchar *buf, size_t pagesize)
{
size_t i;
for (i = pagesize; (i > 0) && (buf[i - 1] == 0); i--) {};
return i;
}
/* Write data to the output file, and punch "holes" if needed. */
static int write_compressed(File fd, uchar *data, size_t len, size_t pagesize)
{
uchar *ptr = data;
for (size_t written= 0; written < len;)
{
size_t n_bytes = MY_MIN(pagesize, len - written);
size_t datasize= trim_binary_zeros(ptr,n_bytes);
if (datasize > 0) {
if (!my_write(fd, ptr, datasize, MYF(MY_WME | MY_NABP)))
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
else
return 1;
}
if (datasize < n_bytes) {
/* This punches a "hole" in the file. */
size_t hole_bytes = n_bytes - datasize;
if (my_seek(fd, hole_bytes, MY_SEEK_CUR, MYF(MY_WME | MY_NABP))
== MY_FILEPOS_ERROR)
return 1;
}
written += n_bytes;
ptr += n_bytes;
}
return 0;
}
/* Calculate Innodb tablespace specific data, when first page is written.
We're interested in page compression and page size.
*/
static void init_ibd_data(ds_local_file_t *local_file, const uchar *buf, size_t len)
{
if (len < FIL_PAGE_DATA + FSP_SPACE_FLAGS) {
/* Weird, bail out.*/
return;
}
ulint flags = mach_read_from_4(&buf[FIL_PAGE_DATA + FSP_SPACE_FLAGS]);
ulint ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
local_file->pagesize= ssize == 0 ? UNIV_PAGE_SIZE_ORIG : ((UNIV_ZIP_SIZE_MIN >> 1) << ssize);
MDEV-18644: Support full_crc32 for page_compressed This is a follow-up task to MDEV-12026, which introduced innodb_checksum_algorithm=full_crc32 and a simpler page format. MDEV-12026 did not enable full_crc32 for page_compressed tables, which we will be doing now. This is joint work with Thirunarayanan Balathandayuthapani. For innodb_checksum_algorithm=full_crc32 we change the page_compressed format as follows: FIL_PAGE_TYPE: The most significant bit will be set to indicate page_compressed format. The least significant bits will contain the compressed page size, rounded up to a multiple of 256 bytes. The checksum will be stored in the last 4 bytes of the page (whether it is the full page or a page_compressed page whose size is determined by FIL_PAGE_TYPE), covering all preceding bytes of the page. If encryption is used, then the page will be encrypted between compression and computing the checksum. For page_compressed, FIL_PAGE_LSN will not be repeated at the end of the page. FSP_SPACE_FLAGS (already implemented as part of MDEV-12026): We will store the innodb_compression_algorithm that may be used to compress pages. Previously, the choice of algorithm was written to each compressed data page separately, and one would be unable to know in advance which compression algorithm(s) are used. fil_space_t::full_crc32_page_compressed_len(): Determine if the page_compressed algorithm of the tablespace needs to know the exact length of the compressed data. If yes, we will reserve and write an extra byte for this right before the checksum. buf_page_is_compressed(): Determine if a page uses page_compressed (in any innodb_checksum_algorithm). fil_page_decompress(): Pass also fil_space_t::flags so that the format can be determined. buf_page_is_zeroes(): Check if a page is full of zero bytes. buf_page_full_crc32_is_corrupted(): Renamed from buf_encrypted_full_crc32_page_is_corrupted(). For full_crc32, we always simply validate the checksum to the page contents, while the physical page size is explicitly specified by an unencrypted part of the page header. buf_page_full_crc32_size(): Determine the size of a full_crc32 page. buf_dblwr_check_page_lsn(): Make this a debug-only function, because it involves potentially costly lookups of fil_space_t. create_table_info_t::check_table_options(), ha_innobase::check_if_supported_inplace_alter(): Do allow the creation of SPATIAL INDEX with full_crc32 also when page_compressed is used. commit_cache_norebuild(): Preserve the compression algorithm when updating the page_compression_level. dict_tf_to_fsp_flags(): Set the flags for page compression algorithm. FIXME: Maybe there should be a table option page_compression_algorithm and a session variable to back it?
2019-03-18 14:08:43 +02:00
local_file->compressed = fil_space_t::full_crc32(flags)
? fil_space_t::is_compressed(flags)
: bool(FSP_FLAGS_HAS_PAGE_COMPRESSION(flags));
#if defined(_WIN32) && (MYSQL_VERSION_ID > 100200)
/* Make compressed file sparse, on Windows.
In 10.1, we do not use sparse files. */
if (local_file->compressed) {
HANDLE handle= my_get_osfhandle(local_file->fd);
if (!DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, NULL, 0)) {
fprintf(stderr, "Warning: cannot make file sparse");
local_file->compressed = 0;
}
}
#endif
}
static
int
local_write(ds_file_t *file, const uchar *buf, size_t len)
{
uchar *b = (uchar*)buf;
ds_local_file_t *local_file= (ds_local_file_t *)file->ptr;
File fd = local_file->fd;
if (local_file->is_ibd && !local_file->init_ibd_done) {
init_ibd_data(local_file, b , len);
local_file->init_ibd_done= 1;
}
if (local_file->compressed) {
return write_compressed(fd, b, len, local_file->pagesize);
}
if (!my_write(fd, b , len, MYF(MY_WME | MY_NABP))) {
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
return 0;
}
return 1;
}
/* Set EOF at file's current position.*/
static int set_eof(File fd)
{
#ifdef _WIN32
return !SetEndOfFile(my_get_osfhandle(fd));
#elif defined(HAVE_FTRUNCATE)
return ftruncate(fd, my_tell(fd, MYF(MY_WME)));
#else
#error no ftruncate
#endif
}
static
int
local_close(ds_file_t *file)
{
ds_local_file_t *local_file= (ds_local_file_t *)file->ptr;
File fd = local_file->fd;
int ret= 0;
if (local_file->compressed) {
ret = set_eof(fd);
}
my_close(fd, MYF(MY_WME));
my_free(file);
return ret;
}
static
void
local_deinit(ds_ctxt_t *ctxt)
{
my_free(ctxt->root);
my_free(ctxt);
}