Skip to content

Commit 45e7902

Browse files
committed
squash! 0c52540
backup_target: A structured data type to represent a directory or a stream. On Microsoft Windows, we must use directory paths because there is no variant of CopyFileEx() that would work on file handles. copy_file(): A file copying service for POSIX systems. On Windows, we will use CopyFileEx().
1 parent 0c52540 commit 45e7902

5 files changed

Lines changed: 254 additions & 214 deletions

File tree

sql/handler.h

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,27 @@ struct transaction_participant
14961496
ulonglong (*prepare_commit_versioned)(THD *thd, ulonglong *trx_id);
14971497
};
14981498

1499+
/** BACKUP SERVER target */
1500+
struct backup_target
1501+
{
1502+
#ifdef _WIN32
1503+
/** Target directory path name */
1504+
const char *path;
1505+
union
1506+
{
1507+
/** Target pipe, if path==reinterpret_cast<const char*>(-1) */
1508+
HANDLE pipe;
1509+
/** Target socket, if path==nullptr */
1510+
SOCKET socket;
1511+
};
1512+
#else
1513+
/** Target file descriptor */
1514+
int fd;
1515+
/** whether the fd is a directory handle */
1516+
bool directory;
1517+
#endif
1518+
};
1519+
14991520
/*
15001521
handlerton is a singleton structure - one instance per storage engine -
15011522
to provide access to storage engine functionality that works on the
@@ -1901,11 +1922,11 @@ struct handlerton : public transaction_participant
19011922
/**
19021923
Start of BACKUP SERVER: collect all files to be backed up
19031924
@param thd current session
1904-
@param target target directory
1925+
@param target backup target
19051926
@return error code
19061927
@retval 0 on success
19071928
*/
1908-
int (*backup_start)(THD *thd, IF_WIN(const char*,int) target);
1929+
int (*backup_start)(THD *thd, backup_target target);
19091930
/**
19101931
Process a file that was collected in backup_start().
19111932
@param thd current session
@@ -1924,11 +1945,11 @@ struct handlerton : public transaction_participant
19241945
/**
19251946
Clean up after any backup_end().
19261947
@param thd the parameter on which backup_end() was invoked
1927-
@param target target directory
1948+
@param target backup target
19281949
@return error code
19291950
@retval 0 on success
19301951
*/
1931-
int (*backup_finalize)(THD *thd, IF_WIN(const char*,int) target);
1952+
int (*backup_finalize)(THD *thd, backup_target target);
19321953

19331954
/**********************************************************************
19341955
WSREP specific

sql/sql_backup.cc

Lines changed: 156 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,159 @@
1818
#include "mysys_err.h"
1919
#include "sql_class.h"
2020
#include "sql_backup.h"
21+
#include "sql_backup_interface.h"
2122
#include "sql_parse.h"
2223

24+
#ifdef _WIN32
25+
#elif defined __APPLE__
26+
# include <sys/attr.h>
27+
# include <sys/clonefile.h>
28+
# include <copyfile.h>
29+
# define copy_file(src, dst, off) \
30+
fcopyfile(src, dst, nullptr, COPYFILE_ALL | COPYFILE_CLONE)
31+
#else
32+
using copying_step= ssize_t(int,int,size_t,off_t*);
33+
template<copying_step step>
34+
static ssize_t copy(int in_fd, int out_fd, off_t c) noexcept
35+
{
36+
ssize_t ret;
37+
for (off_t offset{0};;)
38+
{
39+
off_t count= c;
40+
if (count > INT_MAX >> 20 << 20)
41+
count = INT_MAX >> 20 << 20;
42+
ret= step(in_fd, out_fd, size_t(count), &offset);
43+
if (ret < 0)
44+
break;
45+
c-= ret;
46+
if (!c)
47+
return 0;
48+
if (!ret)
49+
return -1;
50+
}
51+
return ret;
52+
}
53+
# if defined __linux__ || defined __FreeBSD__
54+
/* Copy between files in a single (type of) file system */
55+
static inline ssize_t
56+
copy_step(int in_fd, int out_fd, size_t count, off_t *offset) noexcept
57+
{
58+
return copy_file_range(in_fd, offset, out_fd, nullptr, count, 0);
59+
}
60+
# define cfr(src,dst,size) copy<copy_step>(src, dst, size)
61+
# endif
62+
# ifdef __linux__
63+
# include <sys/sendfile.h>
64+
/* Copy a file to a stream or to a regular file. */
65+
static inline ssize_t
66+
send_step(int in_fd, int out_fd, size_t count, off_t *offset) noexcept
67+
{
68+
return sendfile(out_fd, in_fd, offset, count);
69+
}
70+
# else
71+
# include <sys/mman.h>
72+
/** Copy a file using a memory mapping.
73+
@param in_fd source file
74+
@param out_fd destination
75+
@param count number of bytes to copy
76+
@return error code
77+
@retval 0 on success
78+
@retval 1 if a memory mapping failed */
79+
static ssize_t mmap_copy(int in_fd, int out_fd, off_t count)
80+
{
81+
#if SIZEOF_SIZE_T < 8
82+
if (count != ssize_t(count))
83+
return 1;
84+
#endif
85+
void *p= mmap(nullptr, count, PROT_READ, MAP_SHARED, in_fd, 0);
86+
if (p == MAP_FAILED)
87+
return 1;
88+
ssize_t ret;
89+
size_t c= size_t(count);
90+
for (const char *b= static_cast<const char*>(p);; b+= ret)
91+
{
92+
ret= write(out_fd, b, std::min(c, size_t(INT_MAX >> 20 << 20)));
93+
if (ret < 0)
94+
break;
95+
c-= ret;
96+
if (!c)
97+
{
98+
ret= 0;
99+
break;
100+
}
101+
if (!ret)
102+
{
103+
ret= -1;
104+
break;
105+
}
106+
}
107+
munmap(p, count);
108+
return ret;
109+
}
110+
111+
static ssize_t pread_write(int in_fd, int out_fd, off_t count) noexcept
112+
{
113+
constexpr size_t READ_WRITE_SIZE= 65536;
114+
char *b= static_cast<char*>(aligned_malloc(READ_WRITE_SIZE, 4096));
115+
if (!b)
116+
return -1;
117+
ssize_t ret;
118+
for (off_t o= 0;; o+= ret)
119+
{
120+
ret= pread(in_fd, b, ssize_t(std::min(count, off_t{READ_WRITE_SIZE})), o);
121+
if (ret > 0)
122+
ret= write(out_fd, b, ret);
123+
if (ret < 0)
124+
break;
125+
count-= ret;
126+
if (!count)
127+
{
128+
ret= 0;
129+
break;
130+
}
131+
if (!ret)
132+
{
133+
ret= -1;
134+
break;
135+
}
136+
}
137+
aligned_free(b);
138+
return ret;
139+
}
140+
# endif
141+
142+
/** Copy a file.
143+
@param src source file descriptor
144+
@param dst target to append src to
145+
@param size amount of data to be copied
146+
@return error code (negative)
147+
@retval 0 on success */
148+
extern "C" int copy_file(int src, int dst, off_t size)
149+
{
150+
ssize_t ret;
151+
# ifdef cfr
152+
if (!(ret= cfr(src, dst, size)))
153+
return int(ret);
154+
# ifdef __linux__
155+
if (errno == EOPNOTSUPP)
156+
# endif
157+
# endif
158+
# ifdef __linux__ // starting with Linux 2.6.33, we can rely on sendfile(2)
159+
ret= copy<send_step>(src, dst, size);
160+
# else
161+
if ((ret= mmap_copy(src, dst, size)) == 1)
162+
ret= pread_write(src, dst, size);
163+
# endif
164+
assert(ret <= 0);
165+
return int(ret);
166+
}
167+
#endif
168+
23169
static my_bool backup_start(THD *thd, plugin_ref plugin, void *dst) noexcept
24170
{
25171
handlerton *hton= plugin_hton(plugin);
26172
if (hton->backup_start)
27-
return hton->backup_start(thd,
28-
IF_WIN(static_cast<const char*>(dst),
29-
int(reinterpret_cast<uintptr_t>(dst))));
173+
return hton->backup_start(thd, *static_cast<backup_target*>(dst));
30174
return false;
31175
}
32176

@@ -53,9 +197,7 @@ static my_bool backup_finalize(THD *thd, plugin_ref plugin, void *dst) noexcept
53197
{
54198
handlerton *hton= plugin_hton(plugin);
55199
if (hton->backup_step)
56-
return hton->backup_finalize
57-
(thd, IF_WIN(static_cast<const char*>(dst),
58-
int(reinterpret_cast<uintptr_t>(dst))));
200+
return hton->backup_finalize(thd, *static_cast<backup_target*>(dst));
59201
return 0;
60202
}
61203

@@ -90,9 +232,11 @@ bool Sql_cmd_backup::execute(THD *thd)
90232
return true;
91233
}
92234

93-
#ifndef _WIN32
94-
int dir= open(target.str, O_DIRECTORY);
95-
if (dir < 0)
235+
#ifdef _WIN32
236+
backup_target dir{target.str, INVALID_HANDLE_VALUE};
237+
#else
238+
backup_target dir{open(target.str, O_DIRECTORY), true};
239+
if (dir.fd < 0)
96240
{
97241
my_error(EE_CANT_MKDIR, MYF(ME_BELL), target.str, errno);
98242
goto err_exit;
@@ -101,9 +245,7 @@ bool Sql_cmd_backup::execute(THD *thd)
101245

102246
bool fail= plugin_foreach_with_mask(thd, backup_start,
103247
MYSQL_STORAGE_ENGINE_PLUGIN,
104-
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
105-
IF_WIN(const_cast<char*>(target.str),
106-
reinterpret_cast<void*>(dir)));
248+
PLUGIN_IS_DELETED|PLUGIN_IS_READY, &dir);
107249

108250
/* The backup_step may be invoked in multiple concurrent threads.
109251
At the time backup_end is invoked, all backup_step will have to complete. */
@@ -122,11 +264,9 @@ bool Sql_cmd_backup::execute(THD *thd)
122264
Release the locks. */
123265
thd->mdl_context.release_lock(mdl_request.ticket);
124266
plugin_foreach_with_mask(thd, backup_finalize, MYSQL_STORAGE_ENGINE_PLUGIN,
125-
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
126-
IF_WIN(const_cast<char*>(target.str),
127-
reinterpret_cast<void*>(dir)));
267+
PLUGIN_IS_DELETED|PLUGIN_IS_READY, &dir);
128268
#ifndef _WIN32
129-
close(dir);
269+
close(dir.fd);
130270
#endif
131271

132272
if (!fail)

sql/sql_backup_interface.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Copyright (c) 2026, MariaDB plc
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation; version 2 of the License.
6+
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
12+
You should have received a copy of the GNU General Public License
13+
along with this program; if not, write to the Free Software
14+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
#ifndef _WIN32
20+
/** Copy a file.
21+
@param src source file descriptor
22+
@param dst target to append src to
23+
@param size amount of data to be copied
24+
@return error code (negative)
25+
@retval 0 on success */
26+
int copy_file(int src, int dst, off_t size);
27+
#endif
28+
#ifdef __cplusplus
29+
};
30+
#endif

0 commit comments

Comments
 (0)