1// 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} 100