1/******************************************************
2Copyright (c) 2011-2013 Percona LLC and/or its affiliates.
3
4Local datasink implementation for XtraBackup.
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18
19*******************************************************/
20
21#include <my_global.h>
22#include <my_base.h>
23#include <mysys_err.h>
24#include "common.h"
25#include "datasink.h"
26#include "univ.i"
27#include "fsp0fsp.h"
28#ifdef _WIN32
29#include <winioctl.h>
30#endif
31
32typedef struct {
33 File fd;
34 my_bool init_ibd_done;
35 my_bool is_ibd;
36 my_bool compressed;
37 size_t pagesize;
38} ds_local_file_t;
39
40static ds_ctxt_t *local_init(const char *root);
41static ds_file_t *local_open(ds_ctxt_t *ctxt, const char *path,
42 MY_STAT *mystat);
43static int local_write(ds_file_t *file, const uchar *buf, size_t len);
44static int local_close(ds_file_t *file);
45static void local_deinit(ds_ctxt_t *ctxt);
46
47extern "C" {
48datasink_t datasink_local = {
49 &local_init,
50 &local_open,
51 &local_write,
52 &local_close,
53 &local_deinit
54};
55}
56
57static
58ds_ctxt_t *
59local_init(const char *root)
60{
61 ds_ctxt_t *ctxt;
62
63 if (my_mkdir(root, 0777, MYF(0)) < 0
64 && my_errno != EEXIST && my_errno != EISDIR)
65 {
66 char errbuf[MYSYS_STRERROR_SIZE];
67 my_strerror(errbuf, sizeof(errbuf),my_errno);
68 my_error(EE_CANT_MKDIR, MYF(ME_BELL | ME_WAITTANG),
69 root, my_errno,errbuf, my_errno);
70 return NULL;
71 }
72
73 ctxt = (ds_ctxt_t *)my_malloc(sizeof(ds_ctxt_t), MYF(MY_FAE));
74
75 ctxt->root = my_strdup(root, MYF(MY_FAE));
76
77 return ctxt;
78}
79
80static
81ds_file_t *
82local_open(ds_ctxt_t *ctxt, const char *path,
83 MY_STAT *mystat __attribute__((unused)))
84{
85 char fullpath[FN_REFLEN];
86 char dirpath[FN_REFLEN];
87 size_t dirpath_len;
88 size_t path_len;
89 ds_local_file_t *local_file;
90 ds_file_t *file;
91 File fd;
92
93 fn_format(fullpath, path, ctxt->root, "", MYF(MY_RELATIVE_PATH));
94
95 /* Create the directory if needed */
96 dirname_part(dirpath, fullpath, &dirpath_len);
97 if (my_mkdir(dirpath, 0777, MYF(0)) < 0 && my_errno != EEXIST) {
98 char errbuf[MYSYS_STRERROR_SIZE];
99 my_strerror(errbuf, sizeof(errbuf), my_errno);
100 my_error(EE_CANT_MKDIR, MYF(ME_BELL | ME_WAITTANG),
101 dirpath, my_errno, errbuf);
102 return NULL;
103 }
104
105 fd = my_create(fullpath, 0, O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
106 MYF(MY_WME));
107 if (fd < 0) {
108 return NULL;
109 }
110
111 path_len = strlen(fullpath) + 1; /* terminating '\0' */
112
113 file = (ds_file_t *) my_malloc(sizeof(ds_file_t) +
114 sizeof(ds_local_file_t) +
115 path_len,
116 MYF(MY_FAE));
117 local_file = (ds_local_file_t *) (file + 1);
118
119 local_file->fd = fd;
120 local_file->init_ibd_done = 0;
121 local_file->is_ibd = (path_len > 5) && !strcmp(fullpath + path_len - 5, ".ibd");
122 local_file->compressed = 0;
123 local_file->pagesize = 0;
124 file->path = (char *) local_file + sizeof(ds_local_file_t);
125 memcpy(file->path, fullpath, path_len);
126
127 file->ptr = local_file;
128
129 return file;
130}
131
132/* Calculate size of data without trailing zero bytes. */
133static size_t trim_binary_zeros(uchar *buf, size_t pagesize)
134{
135 size_t i;
136 for (i = pagesize; (i > 0) && (buf[i - 1] == 0); i--) {};
137 return i;
138}
139
140
141/* Write data to the output file, and punch "holes" if needed. */
142static int write_compressed(File fd, uchar *data, size_t len, size_t pagesize)
143{
144 uchar *ptr = data;
145 for (size_t written= 0; written < len;)
146 {
147 size_t n_bytes = MY_MIN(pagesize, len - written);
148 size_t datasize= trim_binary_zeros(ptr,n_bytes);
149 if (datasize > 0) {
150 if (!my_write(fd, ptr, datasize, MYF(MY_WME | MY_NABP)))
151 posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
152 else
153 return 1;
154 }
155 if (datasize < n_bytes) {
156 /* This punches a "hole" in the file. */
157 size_t hole_bytes = n_bytes - datasize;
158 if (my_seek(fd, hole_bytes, MY_SEEK_CUR, MYF(MY_WME | MY_NABP))
159 == MY_FILEPOS_ERROR)
160 return 1;
161 }
162 written += n_bytes;
163 ptr += n_bytes;
164 }
165 return 0;
166}
167
168
169/* Calculate Innodb tablespace specific data, when first page is written.
170 We're interested in page compression and page size.
171*/
172static void init_ibd_data(ds_local_file_t *local_file, const uchar *buf, size_t len)
173{
174 if (len < FIL_PAGE_DATA + FSP_SPACE_FLAGS) {
175 /* Weird, bail out.*/
176 return;
177 }
178
179 ulint flags = mach_read_from_4(&buf[FIL_PAGE_DATA + FSP_SPACE_FLAGS]);
180 ulint ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
181 local_file->pagesize= ssize == 0 ? UNIV_PAGE_SIZE_ORIG : ((UNIV_ZIP_SIZE_MIN >> 1) << ssize);
182 local_file->compressed = (my_bool)FSP_FLAGS_HAS_PAGE_COMPRESSION(flags);
183
184#if defined(_WIN32) && (MYSQL_VERSION_ID > 100200)
185 /* Make compressed file sparse, on Windows.
186 In 10.1, we do not use sparse files. */
187 if (local_file->compressed) {
188 HANDLE handle= my_get_osfhandle(local_file->fd);
189 if (!DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, NULL, 0)) {
190 fprintf(stderr, "Warning: cannot make file sparse");
191 local_file->compressed = 0;
192 }
193 }
194#endif
195}
196
197
198static
199int
200local_write(ds_file_t *file, const uchar *buf, size_t len)
201{
202 uchar *b = (uchar*)buf;
203 ds_local_file_t *local_file= (ds_local_file_t *)file->ptr;
204 File fd = local_file->fd;
205
206 if (local_file->is_ibd && !local_file->init_ibd_done) {
207 init_ibd_data(local_file, b , len);
208 local_file->init_ibd_done= 1;
209 }
210
211 if (local_file->compressed) {
212 return write_compressed(fd, b, len, local_file->pagesize);
213 }
214
215 if (!my_write(fd, b , len, MYF(MY_WME | MY_NABP))) {
216 posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
217 return 0;
218 }
219 return 1;
220}
221
222/* Set EOF at file's current position.*/
223static int set_eof(File fd)
224{
225#ifdef _WIN32
226 return !SetEndOfFile(my_get_osfhandle(fd));
227#elif defined(HAVE_FTRUNCATE)
228 return ftruncate(fd, my_tell(fd, MYF(MY_WME)));
229#else
230#error no ftruncate
231#endif
232}
233
234
235static
236int
237local_close(ds_file_t *file)
238{
239 ds_local_file_t *local_file= (ds_local_file_t *)file->ptr;
240 File fd = local_file->fd;
241 int ret= 0;
242
243 if (local_file->compressed) {
244 ret = set_eof(fd);
245 }
246
247 my_close(fd, MYF(MY_WME));
248 my_free(file);
249 return ret;
250}
251
252static
253void
254local_deinit(ds_ctxt_t *ctxt)
255{
256 my_free(ctxt->root);
257 my_free(ctxt);
258}
259