github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/errorfs/errorfs.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 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 15 package errorfs 16 17 import ( 18 "fmt" 19 "io" 20 "math/rand" 21 "os" 22 "sync" 23 "sync/atomic" 24 "time" 25 26 "github.com/zuoyebang/bitalosdb/internal/vfs" 27 28 "github.com/cockroachdb/errors" 29 "github.com/cockroachdb/errors/oserror" 30 ) 31 32 var ErrInjected = errors.New("injected error") 33 34 type Op int 35 36 const ( 37 OpCreate Op = iota 38 OpLink 39 OpOpen 40 OpOpenDir 41 OpRemove 42 OpRemoveAll 43 OpRename 44 OpReuseForRewrite 45 OpOpenForWrite 46 OpMkdirAll 47 OpLock 48 OpList 49 OpStat 50 OpGetDiskUsage 51 OpFileClose 52 OpFileRead 53 OpFileReadAt 54 OpFileWrite 55 OpFileSeek 56 OpFileStat 57 OpFileSync 58 OpFileFlush 59 ) 60 61 func (o Op) OpKind() OpKind { 62 switch o { 63 case OpOpen, OpOpenDir, OpList, OpStat, OpGetDiskUsage, OpFileRead, OpFileReadAt, OpFileStat, OpFileSeek: 64 return OpKindRead 65 case OpCreate, OpLink, OpRemove, OpRemoveAll, OpRename, OpReuseForRewrite, OpMkdirAll, OpLock, OpFileClose, OpFileWrite, OpFileSync, OpFileFlush: 66 return OpKindWrite 67 default: 68 panic(fmt.Sprintf("unrecognized op %v\n", o)) 69 } 70 } 71 72 type OpKind int 73 74 const ( 75 OpKindRead OpKind = iota 76 OpKindWrite 77 ) 78 79 func OnIndex(index int32) *InjectIndex { 80 return &InjectIndex{index: index} 81 } 82 83 type InjectIndex struct { 84 index int32 85 } 86 87 func (ii *InjectIndex) Index() int32 { return atomic.LoadInt32(&ii.index) } 88 89 func (ii *InjectIndex) SetIndex(v int32) { atomic.StoreInt32(&ii.index, v) } 90 91 func (ii *InjectIndex) MaybeError(_ Op, _ string) error { 92 if atomic.AddInt32(&ii.index, -1) == -1 { 93 return errors.WithStack(ErrInjected) 94 } 95 return nil 96 } 97 98 func WithProbability(op OpKind, p float64) Injector { 99 mu := new(sync.Mutex) 100 rnd := rand.New(rand.NewSource(time.Now().UnixNano())) 101 return InjectorFunc(func(currOp Op, _ string) error { 102 mu.Lock() 103 defer mu.Unlock() 104 if currOp.OpKind() == op && rnd.Float64() < p { 105 return errors.WithStack(ErrInjected) 106 } 107 return nil 108 }) 109 } 110 111 type InjectorFunc func(Op, string) error 112 113 func (f InjectorFunc) MaybeError(op Op, path string) error { return f(op, path) } 114 115 type Injector interface { 116 MaybeError(op Op, path string) error 117 } 118 119 type FS struct { 120 fs vfs.FS 121 inj Injector 122 } 123 124 func Wrap(fs vfs.FS, inj Injector) *FS { 125 return &FS{ 126 fs: fs, 127 inj: inj, 128 } 129 } 130 131 func WrapFile(f vfs.File, inj Injector) vfs.File { 132 return &errorFile{file: f, inj: inj} 133 } 134 135 func (fs *FS) Unwrap() vfs.FS { 136 return fs.fs 137 } 138 139 func (fs *FS) Create(name string) (vfs.File, error) { 140 if err := fs.inj.MaybeError(OpCreate, name); err != nil { 141 return nil, err 142 } 143 f, err := fs.fs.Create(name) 144 if err != nil { 145 return nil, err 146 } 147 return &errorFile{name, f, fs.inj}, nil 148 } 149 150 func (fs *FS) Link(oldname, newname string) error { 151 if err := fs.inj.MaybeError(OpLink, oldname); err != nil { 152 return err 153 } 154 return fs.fs.Link(oldname, newname) 155 } 156 157 func (fs *FS) Open(name string, opts ...vfs.OpenOption) (vfs.File, error) { 158 if err := fs.inj.MaybeError(OpOpen, name); err != nil { 159 return nil, err 160 } 161 f, err := fs.fs.Open(name) 162 if err != nil { 163 return nil, err 164 } 165 ef := &errorFile{name, f, fs.inj} 166 for _, opt := range opts { 167 opt.Apply(ef) 168 } 169 return ef, nil 170 } 171 172 func (fs *FS) OpenDir(name string) (vfs.File, error) { 173 if err := fs.inj.MaybeError(OpOpenDir, name); err != nil { 174 return nil, err 175 } 176 f, err := fs.fs.OpenDir(name) 177 if err != nil { 178 return nil, err 179 } 180 return &errorFile{name, f, fs.inj}, nil 181 } 182 183 func (fs *FS) GetDiskUsage(path string) (vfs.DiskUsage, error) { 184 if err := fs.inj.MaybeError(OpGetDiskUsage, path); err != nil { 185 return vfs.DiskUsage{}, err 186 } 187 return fs.fs.GetDiskUsage(path) 188 } 189 190 func (fs *FS) PathBase(p string) string { 191 return fs.fs.PathBase(p) 192 } 193 194 func (fs *FS) PathDir(p string) string { 195 return fs.fs.PathDir(p) 196 } 197 198 func (fs *FS) PathJoin(elem ...string) string { 199 return fs.fs.PathJoin(elem...) 200 } 201 202 func (fs *FS) Remove(name string) error { 203 if _, err := fs.fs.Stat(name); oserror.IsNotExist(err) { 204 return nil 205 } 206 207 if err := fs.inj.MaybeError(OpRemove, name); err != nil { 208 return err 209 } 210 return fs.fs.Remove(name) 211 } 212 213 func (fs *FS) RemoveAll(fullname string) error { 214 if err := fs.inj.MaybeError(OpRemoveAll, fullname); err != nil { 215 return err 216 } 217 return fs.fs.RemoveAll(fullname) 218 } 219 220 func (fs *FS) Rename(oldname, newname string) error { 221 if err := fs.inj.MaybeError(OpRename, oldname); err != nil { 222 return err 223 } 224 return fs.fs.Rename(oldname, newname) 225 } 226 227 func (fs *FS) ReuseForWrite(oldname, newname string) (vfs.File, error) { 228 if err := fs.inj.MaybeError(OpReuseForRewrite, oldname); err != nil { 229 return nil, err 230 } 231 return fs.fs.ReuseForWrite(oldname, newname) 232 } 233 234 func (fs *FS) OpenForWrite(name string) (vfs.File, error) { 235 if err := fs.inj.MaybeError(OpOpenForWrite, name); err != nil { 236 return nil, err 237 } 238 return fs.fs.OpenForWrite(name) 239 } 240 241 func (fs *FS) OpenWR(name string) (vfs.File, error) { 242 if err := fs.inj.MaybeError(OpOpenForWrite, name); err != nil { 243 return nil, err 244 } 245 return fs.fs.OpenWR(name) 246 } 247 248 func (fs *FS) MkdirAll(dir string, perm os.FileMode) error { 249 if err := fs.inj.MaybeError(OpMkdirAll, dir); err != nil { 250 return err 251 } 252 return fs.fs.MkdirAll(dir, perm) 253 } 254 255 func (fs *FS) Lock(name string) (io.Closer, error) { 256 if err := fs.inj.MaybeError(OpLock, name); err != nil { 257 return nil, err 258 } 259 return fs.fs.Lock(name) 260 } 261 262 func (fs *FS) List(dir string) ([]string, error) { 263 if err := fs.inj.MaybeError(OpList, dir); err != nil { 264 return nil, err 265 } 266 return fs.fs.List(dir) 267 } 268 269 func (fs *FS) Stat(name string) (os.FileInfo, error) { 270 if err := fs.inj.MaybeError(OpStat, name); err != nil { 271 return nil, err 272 } 273 return fs.fs.Stat(name) 274 } 275 276 type errorFile struct { 277 path string 278 file vfs.File 279 inj Injector 280 } 281 282 func (f *errorFile) Close() error { 283 return f.file.Close() 284 } 285 286 func (f *errorFile) Read(p []byte) (int, error) { 287 if err := f.inj.MaybeError(OpFileRead, f.path); err != nil { 288 return 0, err 289 } 290 return f.file.Read(p) 291 } 292 293 func (f *errorFile) ReadAt(p []byte, off int64) (int, error) { 294 if err := f.inj.MaybeError(OpFileReadAt, f.path); err != nil { 295 return 0, err 296 } 297 return f.file.ReadAt(p, off) 298 } 299 300 func (f *errorFile) Write(p []byte) (int, error) { 301 if err := f.inj.MaybeError(OpFileWrite, f.path); err != nil { 302 return 0, err 303 } 304 return f.file.Write(p) 305 } 306 307 func (f *errorFile) Seek(offset int64, whence int) (int64, error) { 308 if err := f.inj.MaybeError(OpFileSeek, f.path); err != nil { 309 return 0, err 310 } 311 return f.file.Seek(offset, whence) 312 } 313 314 func (f *errorFile) Stat() (os.FileInfo, error) { 315 if err := f.inj.MaybeError(OpFileStat, f.path); err != nil { 316 return nil, err 317 } 318 return f.file.Stat() 319 } 320 321 func (f *errorFile) Sync() error { 322 if err := f.inj.MaybeError(OpFileSync, f.path); err != nil { 323 return err 324 } 325 return f.file.Sync() 326 }