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