1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     PerconaFT is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     PerconaFT is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ----------------------------------------
23 
24     PerconaFT is free software: you can redistribute it and/or modify
25     it under the terms of the GNU Affero General Public License, version 3,
26     as published by the Free Software Foundation.
27 
28     PerconaFT is distributed in the hope that it will be useful,
29     but WITHOUT ANY WARRANTY; without even the implied warranty of
30     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31     GNU Affero General Public License for more details.
32 
33     You should have received a copy of the GNU Affero General Public License
34     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36 
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38 
39 #include <my_global.h>
40 #include "log-internal.h"
41 #include "logger/logcursor.h"
42 #include <limits.h>
43 #include <unistd.h>
44 
45 enum lc_direction { LC_FORWARD, LC_BACKWARD, LC_FIRST, LC_LAST };
46 
47 struct toku_logcursor {
48     char *logdir;         // absolute directory name
49     char **logfiles;
50     int n_logfiles;
51     int cur_logfiles_index;
52     FILE *cur_fp;
53     size_t buffer_size;
54     void *buffer;
55     bool is_open;
56     struct log_entry entry;
57     bool entry_valid;
58     LSN cur_lsn;
59     enum lc_direction last_direction;
60 };
61 
62 #define LC_LSN_ERROR (DB_RUNRECOVERY)
63 
toku_logcursor_print(TOKULOGCURSOR lc)64 void toku_logcursor_print(TOKULOGCURSOR lc)  {
65     printf("lc = %p\n", lc);
66     printf("  logdir = %s\n", lc->logdir);
67     printf("  logfiles = %p\n", lc->logfiles);
68     for (int lf=0;lf<lc->n_logfiles;lf++) {
69         printf("    logfile[%d] = %p (%s)\n", lf, lc->logfiles[lf], lc->logfiles[lf]);
70     }
71     printf("  n_logfiles = %d\n", lc->n_logfiles);
72     printf("  cur_logfiles_index = %d\n", lc->cur_logfiles_index);
73     printf("  cur_fp = %p\n", lc->cur_fp);
74     printf("  cur_lsn = %" PRIu64 "\n", lc->cur_lsn.lsn);
75     printf("  last_direction = %d\n", (int) lc->last_direction);
76 }
77 
lc_close_cur_logfile(TOKULOGCURSOR lc)78 static int lc_close_cur_logfile(TOKULOGCURSOR lc) {
79     int r=0;
80     if ( lc->is_open ) {
81         r = fclose(lc->cur_fp);
82         assert(0==r);
83         lc->is_open = false;
84     }
85     return 0;
86 }
87 
lc_file_len(const char * name)88 static toku_off_t lc_file_len(const char *name) {
89     toku_struct_stat buf;
90     int r = toku_stat(name, &buf, *tokudb_file_data_key);
91     assert(r == 0);
92     return buf.st_size;
93 }
94 
95 // Cat the file and throw away the contents.  This brings the file into the file system cache
96 // and makes subsequent accesses to it fast.  The intention is to speed up backward scans of the
97 // file.
lc_catfile(const char * fname,void * buffer,size_t buffer_size)98 static void lc_catfile(const char *fname, void *buffer, size_t buffer_size) {
99     int fd = open(fname, O_RDONLY);
100     if (fd >= 0) {
101         while (1) {
102             ssize_t r = read(fd, buffer, buffer_size);
103             if ((int)r <= 0)
104                 break;
105         }
106         close(fd);
107     }
108 }
109 
lc_open_logfile(TOKULOGCURSOR lc,int index)110 static int lc_open_logfile(TOKULOGCURSOR lc, int index) {
111     int r=0;
112     assert( !lc->is_open );
113     if( index == -1 || index >= lc->n_logfiles) return DB_NOTFOUND;
114     lc_catfile(lc->logfiles[index], lc->buffer, lc->buffer_size);
115     lc->cur_fp = fopen(lc->logfiles[index], "rb");
116     if ( lc->cur_fp == NULL )
117         return DB_NOTFOUND;
118     r = setvbuf(lc->cur_fp, (char *) lc->buffer, _IOFBF, lc->buffer_size);
119     assert(r == 0);
120     // position fp past header, ignore 0 length file (t:2384)
121     unsigned int version=0;
122     if ( lc_file_len(lc->logfiles[index]) >= 12 ) {
123         r = toku_read_logmagic(lc->cur_fp, &version);
124         if (r!=0)
125             return DB_BADFORMAT;
126         if (version < TOKU_LOG_MIN_SUPPORTED_VERSION || version > TOKU_LOG_VERSION)
127             return DB_BADFORMAT;
128     }
129     // mark as open
130     lc->is_open = true;
131     return r;
132 }
133 
lc_check_lsn(TOKULOGCURSOR lc,int dir)134 static int lc_check_lsn(TOKULOGCURSOR lc, int dir) {
135     int r=0;
136     LSN lsn = toku_log_entry_get_lsn(&(lc->entry));
137     if (((dir == LC_FORWARD)  && ( lsn.lsn != lc->cur_lsn.lsn + 1 )) ||
138         ((dir == LC_BACKWARD) && ( lsn.lsn != lc->cur_lsn.lsn - 1 ))) {
139 //        int index = lc->cur_logfiles_index;
140 //        fprintf(stderr, "Bad LSN: %d %s direction = %d, lsn.lsn = %" PRIu64 ", cur_lsn.lsn=%" PRIu64 "\n",
141 //                index, lc->logfiles[index], dir, lsn.lsn, lc->cur_lsn.lsn);
142         if (tokuft_recovery_trace)
143             printf("DB_RUNRECOVERY: %s:%d r=%d\n", __FUNCTION__, __LINE__, 0);
144         return LC_LSN_ERROR;
145     }
146     lc->cur_lsn.lsn = lsn.lsn;
147     return r;
148 }
149 
150 // toku_logcursor_create()
151 //   - returns a pointer to a logcursor
152 
lc_create(TOKULOGCURSOR * lc,const char * log_dir)153 static int lc_create(TOKULOGCURSOR *lc, const char *log_dir) {
154 
155     // malloc a cursor
156     TOKULOGCURSOR cursor = (TOKULOGCURSOR) toku_xmalloc(sizeof(struct toku_logcursor));
157     // find logfiles in logdir
158     cursor->is_open = false;
159     cursor->cur_logfiles_index = 0;
160     cursor->entry_valid = false;
161     cursor->buffer_size = 1<<20;                       // use a 1MB stream buffer (setvbuf)
162     cursor->buffer = toku_malloc(cursor->buffer_size); // it does not matter if it failes
163     // cursor->logdir must be an absolute path
164     if (toku_os_is_absolute_name(log_dir)) {
165         cursor->logdir = (char *) toku_xmalloc(strlen(log_dir)+1);
166         sprintf(cursor->logdir, "%s", log_dir);
167     } else {
168         char cwdbuf[PATH_MAX];
169         char *cwd = getcwd(cwdbuf, PATH_MAX);
170         assert(cwd);
171         cursor->logdir = (char *) toku_xmalloc(strlen(cwd)+strlen(log_dir)+2);
172         sprintf(cursor->logdir, "%s/%s", cwd, log_dir);
173     }
174     cursor->logfiles = NULL;
175     cursor->n_logfiles = 0;
176     cursor->cur_fp = NULL;
177     cursor->cur_lsn.lsn=0;
178     cursor->last_direction=LC_FIRST;
179 
180     *lc = cursor;
181     return 0;
182 }
183 
184 static int lc_fix_bad_logfile(TOKULOGCURSOR lc);
185 
toku_logcursor_create(TOKULOGCURSOR * lc,const char * log_dir)186 int toku_logcursor_create(TOKULOGCURSOR *lc, const char *log_dir) {
187     TOKULOGCURSOR cursor;
188     int r = lc_create(&cursor, log_dir);
189     if ( r!=0 )
190         return r;
191 
192     r = toku_logger_find_logfiles(cursor->logdir, &(cursor->logfiles), &(cursor->n_logfiles));
193     if (r!=0) {
194         toku_logcursor_destroy(&cursor);
195     } else {
196 	*lc = cursor;
197     }
198     return r;
199 }
200 
toku_logcursor_create_for_file(TOKULOGCURSOR * lc,const char * log_dir,const char * log_file)201 int toku_logcursor_create_for_file(TOKULOGCURSOR *lc, const char *log_dir, const char *log_file) {
202     int r = lc_create(lc, log_dir);
203     if ( r!=0 )
204         return r;
205 
206     TOKULOGCURSOR cursor = *lc;
207     int fullnamelen = strlen(cursor->logdir) + strlen(log_file) + 3;
208     char *XMALLOC_N(fullnamelen, log_file_fullname);
209     sprintf(log_file_fullname, "%s/%s", cursor->logdir, log_file);
210 
211     cursor->n_logfiles=1;
212 
213     char **XMALLOC(logfiles);
214     cursor->logfiles = logfiles;
215     cursor->logfiles[0] = log_file_fullname;
216     *lc = cursor;
217     return 0;
218 }
219 
toku_logcursor_destroy(TOKULOGCURSOR * lc)220 int toku_logcursor_destroy(TOKULOGCURSOR *lc) {
221     int r=0;
222     if ( *lc ) {
223         if ( (*lc)->entry_valid ) {
224             toku_log_free_log_entry_resources(&((*lc)->entry));
225             (*lc)->entry_valid = false;
226         }
227         r = lc_close_cur_logfile(*lc);
228         toku_logger_free_logfiles((*lc)->logfiles, (*lc)->n_logfiles);
229         if ( (*lc)->logdir )   toku_free((*lc)->logdir);
230         if ( (*lc)->buffer )   toku_free((*lc)->buffer);
231         toku_free(*lc);
232         *lc = NULL;
233     }
234     return r;
235 }
236 
lc_log_read(TOKULOGCURSOR lc)237 static int lc_log_read(TOKULOGCURSOR lc)
238 {
239     int r = toku_log_fread(lc->cur_fp, &(lc->entry));
240     while ( r == EOF ) {
241         // move to next file
242         r = lc_close_cur_logfile(lc);
243         if (r!=0) return r;
244         if ( lc->cur_logfiles_index == lc->n_logfiles-1) return DB_NOTFOUND;
245         lc->cur_logfiles_index++;
246         r = lc_open_logfile(lc, lc->cur_logfiles_index);
247         if (r!=0) return r;
248         r = toku_log_fread(lc->cur_fp, &(lc->entry));
249     }
250     if (r!=0) {
251         toku_log_free_log_entry_resources(&(lc->entry));
252         time_t tnow = time(NULL);
253         if (r==DB_BADFORMAT) {
254             fprintf(stderr, "%.24s PerconaFT bad log format in %s\n", ctime(&tnow), lc->logfiles[lc->cur_logfiles_index]);
255         }
256         else {
257             fprintf(stderr, "%.24s PerconaFT unexpected log format error '%s' in %s\n", ctime(&tnow), strerror(r), lc->logfiles[lc->cur_logfiles_index]);
258         }
259     }
260     return r;
261 }
262 
lc_log_read_backward(TOKULOGCURSOR lc)263 static int lc_log_read_backward(TOKULOGCURSOR lc)
264 {
265     int r = toku_log_fread_backward(lc->cur_fp, &(lc->entry));
266     while ( -1 == r) { // if within header length of top of file
267         // move to previous file
268         r = lc_close_cur_logfile(lc);
269         if (r!=0)
270             return r;
271         if ( lc->cur_logfiles_index == 0 )
272             return DB_NOTFOUND;
273         lc->cur_logfiles_index--;
274         r = lc_open_logfile(lc, lc->cur_logfiles_index);
275         if (r!=0)
276             return r;
277         // seek to end
278         r = fseek(lc->cur_fp, 0, SEEK_END);
279         assert(0==r);
280         r = toku_log_fread_backward(lc->cur_fp, &(lc->entry));
281     }
282     if (r!=0) {
283         toku_log_free_log_entry_resources(&(lc->entry));
284         time_t tnow = time(NULL);
285         if (r==DB_BADFORMAT) {
286             fprintf(stderr, "%.24s PerconaFT bad log format in %s\n", ctime(&tnow), lc->logfiles[lc->cur_logfiles_index]);
287         }
288         else {
289             fprintf(stderr, "%.24s PerconaFT uUnexpected log format error '%s' in %s\n", ctime(&tnow), strerror(r), lc->logfiles[lc->cur_logfiles_index]);
290         }
291     }
292     return r;
293 }
294 
toku_logcursor_next(TOKULOGCURSOR lc,struct log_entry ** le)295 int toku_logcursor_next(TOKULOGCURSOR lc, struct log_entry **le) {
296     int r=0;
297     if ( lc->entry_valid ) {
298         toku_log_free_log_entry_resources(&(lc->entry));
299         lc->entry_valid = false;
300         if (lc->last_direction == LC_BACKWARD) {
301             struct log_entry junk;
302             r = toku_log_fread(lc->cur_fp, &junk);
303             assert(r == 0);
304             toku_log_free_log_entry_resources(&junk);
305         }
306     } else {
307         r = toku_logcursor_first(lc, le);
308         return r;
309     }
310     // read the entry
311     r = lc_log_read(lc);
312     if (r!=0) return r;
313     r = lc_check_lsn(lc, LC_FORWARD);
314     if (r!=0) return r;
315     lc->last_direction = LC_FORWARD;
316     lc->entry_valid = true;
317     *le = &(lc->entry);
318     return r;
319 }
320 
toku_logcursor_prev(TOKULOGCURSOR lc,struct log_entry ** le)321 int toku_logcursor_prev(TOKULOGCURSOR lc, struct log_entry **le) {
322     int r=0;
323     if ( lc->entry_valid ) {
324         toku_log_free_log_entry_resources(&(lc->entry));
325         lc->entry_valid = false;
326         if (lc->last_direction == LC_FORWARD) {
327             struct log_entry junk;
328             r = toku_log_fread_backward(lc->cur_fp, &junk);
329             assert(r == 0);
330             toku_log_free_log_entry_resources(&junk);
331         }
332     } else {
333         r = toku_logcursor_last(lc, le);
334         return r;
335     }
336     // read the entry
337     r = lc_log_read_backward(lc);
338     if (r!=0) return r;
339     r = lc_check_lsn(lc, LC_BACKWARD);
340     if (r!=0) return r;
341     lc->last_direction = LC_BACKWARD;
342     lc->entry_valid = true;
343     *le = &(lc->entry);
344     return r;
345 }
346 
toku_logcursor_first(TOKULOGCURSOR lc,struct log_entry ** le)347 int toku_logcursor_first(TOKULOGCURSOR lc, struct log_entry **le) {
348     int r=0;
349     if ( lc->entry_valid ) {
350         toku_log_free_log_entry_resources(&(lc->entry));
351         lc->entry_valid = false;
352     }
353     // close any but the first log file
354     if ( lc->cur_logfiles_index != 0 ) {
355         lc_close_cur_logfile(lc);
356     }
357     // open first log file if needed
358     if ( !lc->is_open ) {
359         r = lc_open_logfile(lc, 0);
360         if (r!=0)
361             return r;
362         lc->cur_logfiles_index = 0;
363     }
364     // read the entry
365     r = lc_log_read(lc);
366     if (r!=0) return r;
367 
368     r = lc_check_lsn(lc, LC_FIRST);
369     if (r!=0) return r;
370     lc->last_direction = LC_FIRST;
371     lc->entry_valid = true;
372     *le = &(lc->entry);
373     return r;
374 }
375 
376 //get last entry in the logfile specified by logcursor
toku_logcursor_last(TOKULOGCURSOR lc,struct log_entry ** le)377 int toku_logcursor_last(TOKULOGCURSOR lc, struct log_entry **le) {
378     int r=0;
379     if ( lc->entry_valid ) {
380         toku_log_free_log_entry_resources(&(lc->entry));
381         lc->entry_valid = false;
382     }
383     // close any but last log file
384     if ( lc->cur_logfiles_index != lc->n_logfiles-1 ) {
385         lc_close_cur_logfile(lc);
386     }
387     // open last log file if needed
388     if ( !lc->is_open ) {
389         r = lc_open_logfile(lc, lc->n_logfiles-1);
390         if (r!=0)
391             return r;
392         lc->cur_logfiles_index = lc->n_logfiles-1;
393     }
394     while (1) {
395         // seek to end
396         r = fseek(lc->cur_fp, 0, SEEK_END);    assert(r==0);
397         // read backward
398         r = toku_log_fread_backward(lc->cur_fp, &(lc->entry));
399         if (r==0) // got a good entry
400             break;
401         if (r>0) {
402             toku_log_free_log_entry_resources(&(lc->entry));
403             // got an error,
404             // probably a corrupted last log entry due to a crash
405             // try scanning forward from the beginning to find the last good entry
406             time_t tnow = time(NULL);
407             fprintf(stderr, "%.24s PerconaFT recovery repairing log\n", ctime(&tnow));
408             r = lc_fix_bad_logfile(lc);
409             if ( r != 0 ) {
410                 fprintf(stderr, "%.24s PerconaFT recovery repair unsuccessful\n", ctime(&tnow));
411                 return DB_BADFORMAT;
412             }
413             // try reading again
414             r = toku_log_fread_backward(lc->cur_fp, &(lc->entry));
415             if (r==0) // got a good entry
416                 break;
417         }
418         // move to previous file
419         r = lc_close_cur_logfile(lc);
420         if (r!=0)
421             return r;
422         if ( lc->cur_logfiles_index == 0 )
423             return DB_NOTFOUND;
424         lc->cur_logfiles_index--;
425         r = lc_open_logfile(lc, lc->cur_logfiles_index);
426         if (r!=0)
427             return r;
428     }
429     r = lc_check_lsn(lc, LC_LAST);
430     if (r!=0)
431         return r;
432     lc->last_direction = LC_LAST;
433     lc->entry_valid = true;
434     *le = &(lc->entry);
435     return r;
436 }
437 
438 // return 0 if log exists, ENOENT if no log
439 int
toku_logcursor_log_exists(const TOKULOGCURSOR lc)440 toku_logcursor_log_exists(const TOKULOGCURSOR lc) {
441     int r;
442 
443     if (lc->n_logfiles)
444 	r = 0;
445     else
446 	r = ENOENT;
447 
448     return r;
449 }
450 
451 // fix a logfile with a bad last entry
452 //  - return with fp pointing to end-of-file so that toku_logcursor_last can be retried
lc_fix_bad_logfile(TOKULOGCURSOR lc)453 static int lc_fix_bad_logfile(TOKULOGCURSOR lc) {
454     struct log_entry le;
455     unsigned int version=0;
456     int r = 0;
457 
458     r = fseek(lc->cur_fp, 0, SEEK_SET);
459     if ( r!=0 )
460         return r;
461     r = toku_read_logmagic(lc->cur_fp, &version);
462     if ( r!=0 )
463         return r;
464     if (version != TOKU_LOG_VERSION)
465         return -1;
466 
467     toku_off_t last_good_pos;
468     last_good_pos = ftello(lc->cur_fp);
469     while (1) {
470         // initialize le
471         //  - reading incomplete entries can result in fields that cannot be freed
472         memset(&le, 0, sizeof(le));
473         r = toku_log_fread(lc->cur_fp, &le);
474         toku_log_free_log_entry_resources(&le);
475         if ( r!=0 )
476             break;
477         last_good_pos = ftello(lc->cur_fp);
478     }
479     // now have position of last good entry
480     // 1) close the file
481     // 2) truncate the file to remove the error
482     // 3) reopen the file
483     // 4) set the pos to last
484     r = lc_close_cur_logfile(lc);
485     if ( r!=0 )
486         return r;
487     r = truncate(lc->logfiles[lc->n_logfiles - 1], last_good_pos);
488     if ( r!=0 )
489         return r;
490     r = lc_open_logfile(lc, lc->n_logfiles-1);
491     if ( r!=0 )
492         return r;
493     r = fseek(lc->cur_fp, 0, SEEK_END);
494     if ( r!=0 )
495         return r;
496     return 0;
497 }
498