1 /******************************************************
2 MariaBackup: hot backup tool for InnoDB
3 (c) 2009-2013 Percona LLC and/or its affiliates.
4 Originally Created 3/3/2009 Yasufumi Kinoshita
5 Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
6 Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
7 
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; version 2 of the License.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
20 
21 *******************************************************/
22 
23 /* Page write filters implementation */
24 
25 #include <my_global.h>
26 #include <my_base.h>
27 #include "common.h"
28 #include "write_filt.h"
29 #include "fil_cur.h"
30 #include <os0proc.h>
31 
32 /************************************************************************
33 Write-through page write filter. */
34 static my_bool wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
35 			  xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
36 static my_bool wf_wt_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile);
37 
38 xb_write_filt_t wf_write_through = {
39 	&wf_wt_init,
40 	&wf_wt_process,
41 	NULL,
42 	NULL
43 };
44 
45 /************************************************************************
46 Incremental page write filter. */
47 static my_bool wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
48 				   xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
49 static my_bool wf_incremental_process(xb_write_filt_ctxt_t *ctxt,
50 				      ds_file_t *dstfile);
51 static my_bool wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt,
52 				       ds_file_t *dstfile);
53 static void wf_incremental_deinit(xb_write_filt_ctxt_t *ctxt);
54 
55 xb_write_filt_t wf_incremental = {
56 	&wf_incremental_init,
57 	&wf_incremental_process,
58 	&wf_incremental_finalize,
59 	&wf_incremental_deinit
60 };
61 
62 /************************************************************************
63 Initialize incremental page write filter.
64 
65 @return TRUE on success, FALSE on error. */
66 static my_bool
wf_incremental_init(xb_write_filt_ctxt_t * ctxt,char * dst_name,xb_fil_cur_t * cursor,CorruptedPages * corrupted_pages)67 wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
68 		    xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages)
69 {
70 	char				meta_name[FN_REFLEN];
71 	xb_wf_incremental_ctxt_t	*cp =
72 		&(ctxt->wf_incremental_ctxt);
73 
74 	ctxt->cursor = cursor;
75 
76 	/* allocate buffer for incremental backup (4096 pages) */
77 	cp->delta_buf_size = (cursor->page_size.physical() / 4)
78                 * cursor->page_size.physical();
79 	cp->delta_buf = (unsigned char *)os_mem_alloc_large(&cp->delta_buf_size);
80 
81 	if (!cp->delta_buf) {
82 		msg(cursor->thread_n,"Can't allocate %zu bytes",
83 			(size_t) cp->delta_buf_size);
84 		return (FALSE);
85 	}
86 
87 	/* write delta meta info */
88 	snprintf(meta_name, sizeof(meta_name), "%s%s", dst_name,
89 		 XB_DELTA_INFO_SUFFIX);
90 	const xb_delta_info_t	info(cursor->page_size, cursor->space_id);
91 	if (!xb_write_delta_metadata(meta_name, &info)) {
92 		msg(cursor->thread_n,"Error: "
93 		    "failed to write meta info for %s",
94 		    cursor->rel_path);
95 		return(FALSE);
96 	}
97 
98 	/* change the target file name, since we are only going to write
99 	delta pages */
100 	strcat(dst_name, ".delta");
101 
102 	mach_write_to_4(cp->delta_buf, 0x78747261UL); /*"xtra"*/
103 
104 	cp->npages = 1;
105 	cp->corrupted_pages = corrupted_pages;
106 
107 	return(TRUE);
108 }
109 
110 /************************************************************************
111 Run the next batch of pages through incremental page write filter.
112 
113 @return TRUE on success, FALSE on error. */
114 static my_bool
wf_incremental_process(xb_write_filt_ctxt_t * ctxt,ds_file_t * dstfile)115 wf_incremental_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
116 {
117 	ulint				i;
118 	xb_fil_cur_t			*cursor = ctxt->cursor;
119 	byte				*page;
120 	const ulint			page_size
121 		= cursor->page_size.physical();
122 	xb_wf_incremental_ctxt_t	*cp = &(ctxt->wf_incremental_ctxt);
123 
124 	for (i = 0, page = cursor->buf; i < cursor->buf_npages;
125 	     i++, page += page_size) {
126 
127 		if ((!cp->corrupted_pages ||
128 				!cp->corrupted_pages->contains(cursor->node->space->id,
129 					cursor->buf_page_no + i)) &&
130 				incremental_lsn >= mach_read_from_8(page + FIL_PAGE_LSN))
131 			continue;
132 
133 		/* updated page */
134 		if (cp->npages == page_size / 4) {
135 			/* flush buffer */
136 			if (ds_write(dstfile, cp->delta_buf,
137 				     cp->npages * page_size)) {
138 				return(FALSE);
139 			}
140 
141 			/* clear buffer */
142 			memset(cp->delta_buf, 0, page_size / 4 * page_size);
143 			/*"xtra"*/
144 			mach_write_to_4(cp->delta_buf, 0x78747261UL);
145 			cp->npages = 1;
146 		}
147 
148 		mach_write_to_4(cp->delta_buf + cp->npages * 4,
149 				cursor->buf_page_no + i);
150 		memcpy(cp->delta_buf + cp->npages * page_size, page,
151 		       page_size);
152 
153 		cp->npages++;
154 	}
155 
156 	return(TRUE);
157 }
158 
159 /************************************************************************
160 Flush the incremental page write filter's buffer.
161 
162 @return TRUE on success, FALSE on error. */
163 static my_bool
wf_incremental_finalize(xb_write_filt_ctxt_t * ctxt,ds_file_t * dstfile)164 wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
165 {
166 	xb_fil_cur_t			*cursor = ctxt->cursor;
167 	const ulint			page_size
168 		= cursor->page_size.physical();
169 	xb_wf_incremental_ctxt_t	*cp = &(ctxt->wf_incremental_ctxt);
170 
171 	if (cp->npages != page_size / 4) {
172 		mach_write_to_4(cp->delta_buf + cp->npages * 4, 0xFFFFFFFFUL);
173 	}
174 
175 	/* Mark the final block */
176 	mach_write_to_4(cp->delta_buf, 0x58545241UL); /*"XTRA"*/
177 
178 	/* flush buffer */
179 	if (ds_write(dstfile, cp->delta_buf, cp->npages * page_size)) {
180 		return(FALSE);
181 	}
182 
183 	return(TRUE);
184 }
185 
186 /************************************************************************
187 Free the incremental page write filter's buffer. */
188 static void
wf_incremental_deinit(xb_write_filt_ctxt_t * ctxt)189 wf_incremental_deinit(xb_write_filt_ctxt_t *ctxt)
190 {
191 	xb_wf_incremental_ctxt_t	*cp = &(ctxt->wf_incremental_ctxt);
192 	os_mem_free_large(cp->delta_buf, cp->delta_buf_size);
193 }
194 
195 /************************************************************************
196 Initialize the write-through page write filter.
197 
198 @return TRUE on success, FALSE on error. */
199 static my_bool
wf_wt_init(xb_write_filt_ctxt_t * ctxt,char * dst_name,xb_fil_cur_t * cursor,CorruptedPages *)200 wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name __attribute__((unused)),
201 	   xb_fil_cur_t *cursor, CorruptedPages *)
202 {
203 	ctxt->cursor = cursor;
204 
205 	return(TRUE);
206 }
207 
208 /************************************************************************
209 Write the next batch of pages to the destination datasink.
210 
211 @return TRUE on success, FALSE on error. */
212 static my_bool
wf_wt_process(xb_write_filt_ctxt_t * ctxt,ds_file_t * dstfile)213 wf_wt_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
214 {
215 	xb_fil_cur_t			*cursor = ctxt->cursor;
216 
217 	if (ds_write(dstfile, cursor->buf, cursor->buf_read)) {
218 		return(FALSE);
219 	}
220 
221 	return(TRUE);
222 }
223