1 /******************************************************
2 Copyright (c) 2011-2019 Percona LLC and/or its affiliates.
3
4 Local datasink implementation for XtraBackup.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; version 2 of the License.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18
19 *******************************************************/
20
21 #include <my_base.h>
22 #include <my_io.h>
23 #include <my_sys.h>
24 #include <my_thread_local.h>
25 #include <mysql/service_mysql_alloc.h>
26 #include <mysql_version.h>
27 #include <mysys_err.h>
28 #include "common.h"
29 #include "datasink.h"
30
31 typedef struct {
32 File fd;
33 size_t last_seek; // to track last page sparse_file
34 } ds_local_file_t;
35
36 static ds_ctxt_t *local_init(const char *root);
37 static ds_file_t *local_open(ds_ctxt_t *ctxt, const char *path,
38 MY_STAT *mystat);
39 static int local_write(ds_file_t *file, const void *buf, size_t len);
40 static int local_write_sparse(ds_file_t *file, const void *buf, size_t len,
41 size_t sparse_map_size,
42 const ds_sparse_chunk_t *sparse_map);
43 static int local_close(ds_file_t *file);
44 static void local_deinit(ds_ctxt_t *ctxt);
45
46 datasink_t datasink_local = {&local_init, &local_open, &local_write,
47 &local_write_sparse, &local_close, &local_deinit};
48
local_init(const char * root)49 static ds_ctxt_t *local_init(const char *root) {
50 ds_ctxt_t *ctxt;
51
52 if (my_mkdir(root, 0777, MYF(0)) < 0 && my_errno() != EEXIST &&
53 my_errno() != EISDIR) {
54 char errbuf[MYSYS_STRERROR_SIZE];
55 my_error(EE_CANT_MKDIR, MYF(ME_BELL), root, my_errno(),
56 my_strerror(errbuf, sizeof(errbuf), my_errno()));
57 return NULL;
58 }
59
60 ctxt = static_cast<ds_ctxt_t *>(
61 my_malloc(PSI_NOT_INSTRUMENTED, sizeof(ds_ctxt_t), MYF(MY_FAE)));
62
63 ctxt->root = my_strdup(PSI_NOT_INSTRUMENTED, root, MYF(MY_FAE));
64
65 return ctxt;
66 }
67
local_open(ds_ctxt_t * ctxt,const char * path,MY_STAT * mystat)68 static ds_file_t *local_open(ds_ctxt_t *ctxt, const char *path,
69 MY_STAT *mystat __attribute__((unused))) {
70 char fullpath[FN_REFLEN];
71 char dirpath[FN_REFLEN];
72 size_t dirpath_len;
73 size_t path_len;
74 ds_local_file_t *local_file;
75 ds_file_t *file;
76 File fd;
77
78 fn_format(fullpath, path, ctxt->root, "", MYF(MY_RELATIVE_PATH));
79
80 /* Create the directory if needed */
81 dirname_part(dirpath, fullpath, &dirpath_len);
82 if (my_mkdir(dirpath, 0777, MYF(0)) < 0 && my_errno() != EEXIST) {
83 char errbuf[MYSYS_STRERROR_SIZE];
84 my_error(EE_CANT_MKDIR, MYF(ME_BELL), dirpath, my_errno(),
85 my_strerror(errbuf, sizeof(errbuf), my_errno()));
86 return NULL;
87 }
88
89 fd = my_create(fullpath, 0, O_WRONLY | O_EXCL | O_NOFOLLOW, MYF(MY_WME));
90 if (fd < 0) {
91 return NULL;
92 }
93
94 path_len = strlen(fullpath) + 1; /* terminating '\0' */
95
96 file = (ds_file_t *)my_malloc(
97 PSI_NOT_INSTRUMENTED,
98 sizeof(ds_file_t) + sizeof(ds_local_file_t) + path_len, MYF(MY_FAE));
99 local_file = (ds_local_file_t *)(file + 1);
100
101 local_file->fd = fd;
102 local_file->last_seek = 0;
103
104 file->path = (char *)local_file + sizeof(ds_local_file_t);
105 memcpy(file->path, fullpath, path_len);
106
107 file->ptr = local_file;
108
109 return file;
110 }
111
local_write(ds_file_t * file,const void * buf,size_t len)112 static int local_write(ds_file_t *file, const void *buf, size_t len) {
113 auto local_file = ((ds_local_file_t *)file->ptr);
114 File fd = local_file->fd;
115 local_file->last_seek = 0;
116
117 if (!my_write(fd, static_cast<const uchar *>(buf), len,
118 MYF(MY_WME | MY_NABP))) {
119 posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
120 return 0;
121 }
122
123 return 1;
124 }
125
local_write_sparse(ds_file_t * file,const void * buf,size_t len,size_t sparse_map_size,const ds_sparse_chunk_t * sparse_map)126 static int local_write_sparse(ds_file_t *file, const void *buf, size_t len,
127 size_t sparse_map_size,
128 const ds_sparse_chunk_t *sparse_map) {
129 auto local_file = ((ds_local_file_t *)file->ptr);
130 File fd = local_file->fd;
131
132 const uchar *ptr = static_cast<const uchar *>(buf);
133
134 for (size_t i = 0; i < sparse_map_size; ++i) {
135 size_t rc;
136
137 rc = my_seek(fd, sparse_map[i].skip, MY_SEEK_CUR, MYF(MY_WME));
138 if (rc == MY_FILEPOS_ERROR) {
139 return 1;
140 }
141
142 rc = my_write(fd, ptr, sparse_map[i].len, MYF(MY_WME | MY_NABP));
143 if (rc != 0) {
144 return 1;
145 }
146
147 ptr += sparse_map[i].len;
148 }
149 /* to track if last page is sparse */
150 if (sparse_map[sparse_map_size - 1].len == 0) {
151 local_file->last_seek = sparse_map[sparse_map_size - 1].skip;
152 } else
153 local_file->last_seek = 0;
154
155 return 0;
156 }
157
local_close(ds_file_t * file)158 static int local_close(ds_file_t *file) {
159 auto local_file = ((ds_local_file_t *)file->ptr);
160 File fd = local_file->fd;
161
162 /* Write the last page complete in full size. We achieve this by writing the
163 * last byte of page as zero, this can only happen in case of sparse file */
164 if (local_file->last_seek > 0) {
165 size_t rc;
166 rc = my_seek(fd, -1, MY_SEEK_CUR, MYF(MY_WME));
167 if (rc == MY_FILEPOS_ERROR) {
168 return 1;
169 }
170 unsigned char b = 0;
171 rc = my_write(fd, &b, 1, MYF(MY_WME | MY_NABP));
172 if (rc != 0) {
173 return 1;
174 }
175 }
176
177 my_free(file);
178
179 my_sync(fd, MYF(MY_WME));
180
181 return my_close(fd, MYF(MY_WME));
182 }
183
local_deinit(ds_ctxt_t * ctxt)184 static void local_deinit(ds_ctxt_t *ctxt) {
185 my_free(ctxt->root);
186 my_free(ctxt);
187 }
188