blob: 091036b57b9aea2fd7d5503cf42aaa08e663e60b [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package wal
16
17import (
18 "io"
19 "os"
20 "path/filepath"
21
22 "github.com/coreos/etcd/pkg/fileutil"
23 "github.com/coreos/etcd/wal/walpb"
24)
25
26// Repair tries to repair ErrUnexpectedEOF in the
27// last wal file by truncating.
28func Repair(dirpath string) bool {
29 f, err := openLast(dirpath)
30 if err != nil {
31 return false
32 }
33 defer f.Close()
34
35 rec := &walpb.Record{}
36 decoder := newDecoder(f)
37 for {
38 lastOffset := decoder.lastOffset()
39 err := decoder.decode(rec)
40 switch err {
41 case nil:
42 // update crc of the decoder when necessary
43 switch rec.Type {
44 case crcType:
45 crc := decoder.crc.Sum32()
46 // current crc of decoder must match the crc of the record.
47 // do no need to match 0 crc, since the decoder is a new one at this case.
48 if crc != 0 && rec.Validate(crc) != nil {
49 return false
50 }
51 decoder.updateCRC(rec.Crc)
52 }
53 continue
54 case io.EOF:
55 return true
56 case io.ErrUnexpectedEOF:
57 plog.Noticef("repairing %v", f.Name())
58 bf, bferr := os.Create(f.Name() + ".broken")
59 if bferr != nil {
60 plog.Errorf("could not repair %v, failed to create backup file", f.Name())
61 return false
62 }
63 defer bf.Close()
64
65 if _, err = f.Seek(0, io.SeekStart); err != nil {
66 plog.Errorf("could not repair %v, failed to read file", f.Name())
67 return false
68 }
69
70 if _, err = io.Copy(bf, f); err != nil {
71 plog.Errorf("could not repair %v, failed to copy file", f.Name())
72 return false
73 }
74
75 if err = f.Truncate(int64(lastOffset)); err != nil {
76 plog.Errorf("could not repair %v, failed to truncate file", f.Name())
77 return false
78 }
79 if err = fileutil.Fsync(f.File); err != nil {
80 plog.Errorf("could not repair %v, failed to sync file", f.Name())
81 return false
82 }
83 return true
84 default:
85 plog.Errorf("could not repair error (%v)", err)
86 return false
87 }
88 }
89}
90
91// openLast opens the last wal file for read and write.
92func openLast(dirpath string) (*fileutil.LockedFile, error) {
93 names, err := readWalNames(dirpath)
94 if err != nil {
95 return nil, err
96 }
97 last := filepath.Join(dirpath, names[len(names)-1])
98 return fileutil.LockFile(last, os.O_RDWR, fileutil.PrivateFileMode)
99}