Skip to content

Commit bdec600

Browse files
committed
Use directory handles also on Windows
my_mkdir_open(): Create and open a directory handle on Windows by invoking NtCreateFile(). InnoDB_backup::targetPath(): Determine the target directory path from a HANDLE. Yes, this is somewhat awkward, but the basic operations such as CopyFileEx() work on file names, not file handles. We may implement lower-level file copying / block cloning primitives later as well as openat(2) like functionality, as part of implementing streaming backup.
1 parent 0c52540 commit bdec600

4 files changed

Lines changed: 94 additions & 42 deletions

File tree

sql/handler.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,7 +1905,7 @@ struct handlerton : public transaction_participant
19051905
@return error code
19061906
@retval 0 on success
19071907
*/
1908-
int (*backup_start)(THD *thd, IF_WIN(const char*,int) target);
1908+
int (*backup_start)(THD *thd, IF_WIN(HANDLE,int) target);
19091909
/**
19101910
Process a file that was collected in backup_start().
19111911
@param thd current session
@@ -1928,7 +1928,7 @@ struct handlerton : public transaction_participant
19281928
@return error code
19291929
@retval 0 on success
19301930
*/
1931-
int (*backup_finalize)(THD *thd, IF_WIN(const char*,int) target);
1931+
int (*backup_finalize)(THD *thd, IF_WIN(HANDLE,int) target);
19321932

19331933
/**********************************************************************
19341934
WSREP specific

sql/sql_backup.cc

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,54 @@
2020
#include "sql_backup.h"
2121
#include "sql_parse.h"
2222

23+
#ifdef _WIN32
24+
# include<winternl.h>
25+
/** Create a directory.
26+
@param path path name
27+
@return directory handle
28+
@retval nullptr on error */
29+
static HANDLE my_mkdir_open(const char *path) noexcept
30+
{
31+
OBJECT_ATTRIBUTES attr;
32+
UNICODE_STRING upath;
33+
size_t pathlen= strlen(path);
34+
upath.Length= upath.MaximumLength= 2 * pathlen;
35+
upath.Buffer= static_cast<PWSTR>
36+
(my_malloc(PSI_NOT_INSTRUMENTED, upath.MaximumLength, MYF(MY_WME)));
37+
if (!upath.Buffer)
38+
return nullptr;
39+
40+
uint errors= 0;
41+
strconvert(&my_charset_ucs2_bin, path, pathlen, system_charset_info,
42+
reinterpret_cast<char*>(upath.Buffer), upath.Length, &errors);
43+
if (errors)
44+
{
45+
my_free(upath.Buffer);
46+
errno= EINVAL;
47+
return nullptr;
48+
}
49+
50+
InitializeObjectAttributes(&attr, &upath, 0, /*dir=*/ nullptr, nullptr);
51+
IO_STATUS_BLOCK io_status_block;
52+
HANDLE h= nullptr;
53+
if (NtCreateFile(&h, FILE_READ_DATA | SYNCHRONIZE, &attr, &io_status_block,
54+
nullptr, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
55+
FILE_CREATE,
56+
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
57+
nullptr, 0))
58+
errno= EINVAL; /* FIXME: determine a more precise error */
59+
my_free(upath.Buffer);
60+
return h;
61+
}
62+
#endif
63+
2364
static my_bool backup_start(THD *thd, plugin_ref plugin, void *dst) noexcept
2465
{
2566
handlerton *hton= plugin_hton(plugin);
2667
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))));
68+
return hton->backup_start
69+
(thd, IF_WIN(reinterpret_cast<HANDLE>(dst),
70+
int(reinterpret_cast<uintptr_t>(dst))));
3071
return false;
3172
}
3273

@@ -49,12 +90,13 @@ static my_bool backup_step(THD *thd, plugin_ref plugin, void *) noexcept
4990
return res != 0;
5091
}
5192

52-
static my_bool backup_finalize(THD *thd, plugin_ref plugin, void *dst) noexcept
93+
static my_bool backup_finalize(THD *thd, plugin_ref plugin,
94+
void *dst) noexcept
5395
{
5496
handlerton *hton= plugin_hton(plugin);
5597
if (hton->backup_step)
5698
return hton->backup_finalize
57-
(thd, IF_WIN(static_cast<const char*>(dst),
99+
(thd, IF_WIN(reinterpret_cast<HANDLE>(dst),
58100
int(reinterpret_cast<uintptr_t>(dst))));
59101
return 0;
60102
}
@@ -81,16 +123,22 @@ bool Sql_cmd_backup::execute(THD *thd)
81123
thd->variables.lock_wait_timeout))
82124
return true;
83125

126+
#ifdef _WIN32
127+
HANDLE dir= my_mkdir_open(target.str);
128+
if (!dir)
129+
{
130+
my_error(EE_CANT_MKDIR, MYF(ME_BELL), target.str, errno);
131+
thd->mdl_context.release_lock(mdl_request.ticket);
132+
return true;
133+
}
134+
#else
84135
if (my_mkdir(target.str, 0755, MYF(MY_WME)))
85136
{
86-
#ifndef _WIN32
87137
err_exit:
88-
#endif
89138
thd->mdl_context.release_lock(mdl_request.ticket);
90139
return true;
91140
}
92141

93-
#ifndef _WIN32
94142
int dir= open(target.str, O_DIRECTORY);
95143
if (dir < 0)
96144
{
@@ -102,8 +150,7 @@ bool Sql_cmd_backup::execute(THD *thd)
102150
bool fail= plugin_foreach_with_mask(thd, backup_start,
103151
MYSQL_STORAGE_ENGINE_PLUGIN,
104152
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
105-
IF_WIN(const_cast<char*>(target.str),
106-
reinterpret_cast<void*>(dir)));
153+
IF_WIN(, reinterpret_cast<void*>)(dir));
107154

108155
/* The backup_step may be invoked in multiple concurrent threads.
109156
At the time backup_end is invoked, all backup_step will have to complete. */
@@ -123,11 +170,8 @@ bool Sql_cmd_backup::execute(THD *thd)
123170
thd->mdl_context.release_lock(mdl_request.ticket);
124171
plugin_foreach_with_mask(thd, backup_finalize, MYSQL_STORAGE_ENGINE_PLUGIN,
125172
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
126-
IF_WIN(const_cast<char*>(target.str),
127-
reinterpret_cast<void*>(dir)));
128-
#ifndef _WIN32
129-
close(dir);
130-
#endif
173+
IF_WIN(, reinterpret_cast<void*>)(dir));
174+
IF_WIN(CloseHandle(dir), close(dir));
131175

132176
if (!fail)
133177
my_ok(thd);

storage/innobase/handler/backup_innodb.cc

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ class InnoDB_backup
186186
hard-linked, copied, or moved */
187187
std::vector<lsn_t> logs;
188188

189-
/** target directory name or handle */
190-
IF_WIN(const char*,int) target;
189+
/** target directory handle handle */
190+
IF_WIN(HANDLE,int) target;
191191

192192
/** the checkpoint from which the backup starts */
193193
lsn_t checkpoint;
@@ -429,15 +429,13 @@ class InnoDB_backup
429429
trx->start_time_micro= 0;
430430
/* Copy our clone of the last log until the final LSN */
431431
#ifdef _WIN32
432-
std::string src{target};
433-
src.push_back('/');
434-
std::string dst{src};
432+
std::string src= targetPath(), dst= src;
435433
src.append("ib_logfile101");
436434
log_sys.append_archive_name(dst, first_lsn);
437435
const char *s= src.c_str(), *d= dst.c_str();
438-
439-
if (!CopyFileExA(s, d, nullptr, nullptr, nullptr,
440-
COPY_FILE_NO_BUFFERING) ||
436+
if (!CopyFileEx(s, d, nullptr, nullptr, nullptr,
437+
COPY_FILE_NO_BUFFERING) ||
438+
!SetFileAttributes(s, FILE_ATTRIBUTE_NORMAL) ||
441439
!MoveFileEx(d, s, MOVEFILE_REPLACE_EXISTING))
442440
{
443441
my_osmaperr(GetLastError());
@@ -509,6 +507,19 @@ class InnoDB_backup
509507
}
510508

511509
private:
510+
#ifdef _WIN32
511+
/** @return the target directory path */
512+
std::string targetPath() const
513+
{
514+
size_t dirlen=
515+
GetFinalPathNameByHandle(target, 0, 0, FILE_NAME_NORMALIZED);
516+
TCHAR dir[dirlen + 1];
517+
ut_a(dirlen == GetFinalPathNameByHandle(target, dir, dirlen,
518+
FILE_NAME_NORMALIZED));
519+
dir[dirlen]= '\\';
520+
return std::string{dir, dirlen};
521+
}
522+
#endif
512523
/** Safely start backing up a tablespace file */
513524
static void backup_start(fil_space_t *space) noexcept
514525
{
@@ -537,11 +548,14 @@ class InnoDB_backup
537548
{
538549
#ifdef _WIN32
539550
backup_start(node->space);
540-
std::string path{target};
541-
path.push_back('/');
542-
path.append(node->name);
543-
bool ok= CopyFileExA(node->name, path.c_str(), nullptr, nullptr, nullptr,
544-
COPY_FILE_NO_BUFFERING);
551+
std::string path= targetPath();
552+
{
553+
size_t size= path.size();
554+
path.append(node->name);
555+
std::replace(s.begin() + size, s.end(), '/', '\\');
556+
}
557+
bool ok= CopyFileEx(node->name, path.c_str(), nullptr, nullptr, nullptr,
558+
COPY_FILE_NO_BUFFERING);
545559
backup_stop(node->space);
546560
if (!ok)
547561
{
@@ -645,14 +659,8 @@ class InnoDB_backup
645659

646660
#ifdef _WIN32
647661
const bool closed{clone && log_sys.close_file_if_at(lsn)};
648-
std::string b{target};
649-
if (clone)
650-
b.append("/ib_logfile101");
651-
else
652-
{
653-
b.push_back('/');
654-
b.append(basename);
655-
}
662+
std::string b= targetPath();
663+
b.append(clone ? "ib_logfile101" : basename);
656664
const char *destname= b.c_str();
657665

658666
unsigned long err;
@@ -680,8 +688,8 @@ class InnoDB_backup
680688
b.append(basename);
681689
destname= b.c_str();
682690

683-
if (!CopyFileExA(path, destname, nullptr, nullptr, nullptr,
684-
COPY_FILE_NO_BUFFERING))
691+
if (!CopyFileEx(path, destname, nullptr, nullptr, nullptr,
692+
COPY_FILE_NO_BUFFERING))
685693
goto fail;
686694
}
687695
else if (clone)

storage/innobase/handler/backup_innodb.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
@return error code
2121
@retval 0 on success
2222
*/
23-
int innodb_backup_start(THD *thd, IF_WIN(const char*,int) target) noexcept;
23+
int innodb_backup_start(THD *thd, IF_WIN(HANDLE,int) target) noexcept;
2424

2525
/**
2626
Process a file that was collected in backup_start().
@@ -46,7 +46,7 @@ int innodb_backup_end(THD *thd, bool abort) noexcept;
4646
@return error code
4747
@retval 0 on success
4848
*/
49-
int innodb_backup_finalize(THD *thd, IF_WIN(const char*,int) target) noexcept;
49+
int innodb_backup_finalize(THD *thd, IF_WIN(HANDLE,int) target) noexcept;
5050

5151
/**
5252
Complete the first checkpoint in a new archive log file.

0 commit comments

Comments
 (0)