github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfs/leveldb_storage.go (about) 1 // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com> 2 // All rights reservefs. 3 // 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 // This is a modified version of 8 // github.com/syndtr/goleveldb/leveldb/storage/file_storage.go. 9 // 10 // Modifications: Copyright 2019 Keybase Inc. All rights reserved. 11 // Use of this source code is governed by a BSD 12 // license that can be found in the LICENSE file. 13 14 package libfs 15 16 import ( 17 "errors" 18 "fmt" 19 "io" 20 "os" 21 "runtime" 22 "sort" 23 "strconv" 24 "strings" 25 "sync" 26 "time" 27 28 "github.com/syndtr/goleveldb/leveldb/storage" 29 billy "gopkg.in/src-d/go-billy.v4" 30 ) 31 32 var ( 33 errReadOnly = errors.New("leveldb/storage: storage is read-only") 34 ) 35 36 type levelDBStorageLock struct { 37 fs *levelDBStorage 38 } 39 40 func (lock *levelDBStorageLock) Unlock() { 41 if lock.fs != nil { 42 lock.fs.mu.Lock() 43 defer lock.fs.mu.Unlock() 44 if lock.fs.slock == lock { 45 lock.fs.slock = nil 46 } 47 } 48 } 49 50 type int64Slice []int64 51 52 func (p int64Slice) Len() int { return len(p) } 53 func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] } 54 func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 55 56 const logSizeThreshold = 1024 * 1024 // 1 MiB 57 58 // levelDBStorage is a billy-filesystem-backed storage. 59 type levelDBStorage struct { 60 fs billy.Filesystem 61 readOnly bool 62 63 mu sync.Mutex 64 flock billy.File 65 slock *levelDBStorageLock 66 logw billy.File 67 logSize int64 68 buf []byte 69 // Opened file counter; if open < 0 means closed. 70 open int 71 day int 72 73 syncLock sync.RWMutex // sync takes write lock, modifiers take read lock 74 } 75 76 var _ storage.Storage = (*levelDBStorage)(nil) 77 78 // OpenLevelDBStorage returns a new billy-filesystem-backed storage 79 // implementation of the levelDB storage interface. This also acquires 80 // a file lock, so any subsequent attempt to open the same path will 81 // fail. 82 func OpenLevelDBStorage(bfs billy.Filesystem, readOnly bool) ( 83 s storage.Storage, err error) { 84 flock, err := bfs.OpenFile("LOCK", os.O_CREATE|os.O_TRUNC, 0600) 85 if err != nil { 86 return nil, err 87 } 88 defer func() { 89 if err != nil { 90 flock.Close() 91 } 92 }() 93 err = flock.Lock() 94 if err != nil { 95 return nil, err 96 } 97 98 var ( 99 logw billy.File 100 logSize int64 101 ) 102 if !readOnly { 103 logw, err = bfs.OpenFile("LOG", os.O_WRONLY|os.O_CREATE, 0644) 104 if err != nil { 105 return nil, err 106 } 107 logSize, err = logw.Seek(0, os.SEEK_END) 108 if err != nil { 109 logw.Close() 110 return nil, err 111 } 112 } 113 114 fs := &levelDBStorage{ 115 fs: bfs, 116 readOnly: readOnly, 117 flock: flock, 118 logw: logw, 119 logSize: logSize, 120 } 121 runtime.SetFinalizer(fs, (*levelDBStorage).Close) 122 return fs, nil 123 } 124 125 func (fs *levelDBStorage) writeFileSyncedRLocked( 126 filename string, data []byte, perm os.FileMode) error { 127 f, err := fs.fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) 128 if err != nil { 129 return err 130 } 131 n, err := f.Write(data) 132 if err == nil && n < len(data) { 133 err = io.ErrShortWrite 134 } 135 if err1 := f.Close(); err == nil { 136 err = err1 137 } 138 if err != nil { 139 return err 140 } 141 return fs.syncRLocked() 142 } 143 144 func (fs *levelDBStorage) Lock() (storage.Locker, error) { 145 fs.mu.Lock() 146 defer fs.mu.Unlock() 147 if fs.open < 0 { 148 return nil, storage.ErrClosed 149 } 150 if fs.readOnly { 151 return &levelDBStorageLock{}, nil 152 } 153 if fs.slock != nil { 154 return nil, storage.ErrLocked 155 } 156 fs.slock = &levelDBStorageLock{fs: fs} 157 return fs.slock, nil 158 } 159 160 func itoa(buf []byte, i int, wid int) []byte { 161 u := uint(i) 162 if u == 0 && wid <= 1 { 163 return append(buf, '0') 164 } 165 166 // Assemble decimal in reverse order. 167 var b [32]byte 168 bp := len(b) 169 for ; u > 0 || wid > 0; u /= 10 { 170 bp-- 171 wid-- 172 b[bp] = byte(u%10) + '0' 173 } 174 return append(buf, b[bp:]...) 175 } 176 177 func (fs *levelDBStorage) printDay(t time.Time) { 178 if fs.day == t.Day() { 179 return 180 } 181 fs.day = t.Day() 182 _, _ = fs.logw.Write([]byte("=============== " + t.Format("Jan 2, 2006 (MST)") + " ===============\n")) 183 } 184 185 func (fs *levelDBStorage) doLogRLocked(t time.Time, str string) { 186 if fs.logSize > logSizeThreshold { 187 // Rotate log file. 188 fs.logw.Close() 189 fs.logw = nil 190 fs.logSize = 0 191 err := fs.fs.Rename("LOG", "LOG.old") 192 if err != nil { 193 return 194 } 195 } 196 if fs.logw == nil { 197 var err error 198 fs.logw, err = fs.fs.OpenFile("LOG", os.O_WRONLY|os.O_CREATE, 0644) 199 if err != nil { 200 return 201 } 202 // Force printDay on new log file. 203 fs.day = 0 204 } 205 fs.printDay(t) 206 hour, min, sec := t.Clock() 207 msec := t.Nanosecond() / 1e3 208 // time 209 fs.buf = itoa(fs.buf[:0], hour, 2) 210 fs.buf = append(fs.buf, ':') 211 fs.buf = itoa(fs.buf, min, 2) 212 fs.buf = append(fs.buf, ':') 213 fs.buf = itoa(fs.buf, sec, 2) 214 fs.buf = append(fs.buf, '.') 215 fs.buf = itoa(fs.buf, msec, 6) 216 fs.buf = append(fs.buf, ' ') 217 // write 218 fs.buf = append(fs.buf, []byte(str)...) 219 fs.buf = append(fs.buf, '\n') 220 n, _ := fs.logw.Write(fs.buf) 221 fs.logSize += int64(n) 222 } 223 224 func (fs *levelDBStorage) Log(str string) { 225 if !fs.readOnly { 226 t := time.Now() 227 fs.mu.Lock() 228 defer fs.mu.Unlock() 229 if fs.open < 0 { 230 return 231 } 232 fs.syncLock.RLock() 233 defer fs.syncLock.RUnlock() 234 fs.doLogRLocked(t, str) 235 } 236 } 237 238 func (fs *levelDBStorage) logRLocked(str string) { 239 if !fs.readOnly { 240 fs.doLogRLocked(time.Now(), str) 241 } 242 } 243 244 func (fs *levelDBStorage) log(str string) { 245 if !fs.readOnly { 246 fs.syncLock.RLock() 247 defer fs.syncLock.RUnlock() 248 fs.doLogRLocked(time.Now(), str) 249 } 250 } 251 252 func (fs *levelDBStorage) syncLocked() (err error) { 253 // Force a sync with a lock/unlock cycle, since the billy 254 // interface doesn't have an explicit sync call. 255 const syncLockName = "sync.lock" 256 f, err := fs.fs.OpenFile(syncLockName, os.O_CREATE|os.O_TRUNC, 0600) 257 if err != nil { 258 return err 259 } 260 defer func() { 261 closeErr := f.Close() 262 if err == nil { 263 err = closeErr 264 } 265 }() 266 return f.Lock() 267 } 268 269 func (fs *levelDBStorage) sync() (err error) { 270 fs.syncLock.Lock() 271 defer fs.syncLock.Unlock() 272 return fs.syncLocked() 273 } 274 275 func (fs *levelDBStorage) syncRLocked() (err error) { 276 fs.syncLock.RUnlock() 277 defer fs.syncLock.RLock() 278 return fs.sync() 279 } 280 281 func (fs *levelDBStorage) setMetaRLocked(fd storage.FileDesc) error { 282 content := fsGenName(fd) + "\n" 283 // Check and backup old CURRENT file. 284 currentPath := "CURRENT" 285 if _, err := fs.fs.Stat(currentPath); err == nil { 286 f, err := fs.fs.Open(currentPath) 287 if err != nil { 288 return err 289 } 290 defer f.Close() 291 b, err := io.ReadAll(f) 292 if err != nil { 293 fs.logRLocked(fmt.Sprintf("backup CURRENT: %v", err)) 294 return err 295 } 296 if string(b) == content { 297 // Content not changed, do nothing. 298 return nil 299 } 300 if err := fs.writeFileSyncedRLocked( 301 currentPath+".bak", b, 0644); err != nil { 302 fs.logRLocked(fmt.Sprintf("backup CURRENT: %v", err)) 303 return err 304 } 305 } else if !os.IsNotExist(err) { 306 return err 307 } 308 path := fmt.Sprintf("CURRENT.%d", fd.Num) 309 if err := fs.writeFileSyncedRLocked( 310 path, []byte(content), 0644); err != nil { 311 fs.logRLocked(fmt.Sprintf("create CURRENT.%d: %v", fd.Num, err)) 312 return err 313 } 314 // Replace CURRENT file. 315 if err := fs.fs.Rename(path, currentPath); err != nil { 316 fs.logRLocked(fmt.Sprintf("rename CURRENT.%d: %v", fd.Num, err)) 317 return err 318 } 319 return fs.syncRLocked() 320 } 321 322 func (fs *levelDBStorage) setMeta(fd storage.FileDesc) error { 323 fs.syncLock.RLock() 324 defer fs.syncLock.RUnlock() 325 return fs.setMetaRLocked(fd) 326 } 327 328 func (fs *levelDBStorage) SetMeta(fd storage.FileDesc) error { 329 fs.syncLock.RLock() 330 defer fs.syncLock.RUnlock() 331 332 if !storage.FileDescOk(fd) { 333 return storage.ErrInvalidFile 334 } 335 if fs.readOnly { 336 return errReadOnly 337 } 338 339 fs.mu.Lock() 340 defer fs.mu.Unlock() 341 if fs.open < 0 { 342 return storage.ErrClosed 343 } 344 return fs.setMetaRLocked(fd) 345 } 346 347 func isCorrupted(err error) bool { 348 switch err.(type) { 349 case *storage.ErrCorrupted: 350 return true 351 default: 352 return false 353 } 354 } 355 356 func (fs *levelDBStorage) GetMeta() (storage.FileDesc, error) { 357 fs.mu.Lock() 358 defer fs.mu.Unlock() 359 if fs.open < 0 { 360 return storage.FileDesc{}, storage.ErrClosed 361 } 362 fis, err := fs.fs.ReadDir("") 363 if err != nil { 364 return storage.FileDesc{}, err 365 } 366 // Try this in order: 367 // - CURRENT.[0-9]+ ('pending rename' file, descending order) 368 // - CURRENT 369 // - CURRENT.bak 370 // 371 // Skip corrupted file or file that point to a missing target file. 372 type currentFile struct { 373 name string 374 fd storage.FileDesc 375 } 376 tryCurrent := func(name string) (*currentFile, error) { 377 f, err := fs.fs.Open(name) 378 if err != nil { 379 if os.IsNotExist(err) { 380 err = os.ErrNotExist 381 } 382 return nil, err 383 } 384 defer f.Close() 385 b, err := io.ReadAll(f) 386 if err != nil { 387 return nil, err 388 } 389 var fd storage.FileDesc 390 if len(b) < 1 || b[len(b)-1] != '\n' || !fsParseNamePtr(string(b[:len(b)-1]), &fd) { 391 fs.logRLocked(fmt.Sprintf("%s: corrupted content: %q", name, b)) 392 err := &storage.ErrCorrupted{ 393 Err: fmt.Errorf("leveldb/storage: corrupted or incomplete CURRENT file %s %q", name, b), 394 } 395 return nil, err 396 } 397 if _, err := fs.fs.Stat(fsGenName(fd)); err != nil { 398 if os.IsNotExist(err) { 399 fs.logRLocked( 400 fmt.Sprintf("%s: missing target file: %s", name, fd)) 401 err = os.ErrNotExist 402 } 403 return nil, err 404 } 405 return ¤tFile{name: name, fd: fd}, nil 406 } 407 tryCurrents := func(names []string) (*currentFile, error) { 408 var ( 409 cur *currentFile 410 // Last corruption error. 411 lastCerr error 412 ) 413 for _, name := range names { 414 var err error 415 cur, err = tryCurrent(name) 416 if err == nil { 417 break 418 } else if err == os.ErrNotExist { 419 // Fallback to the next file. 420 } else if isCorrupted(err) { 421 lastCerr = err 422 // Fallback to the next file. 423 } else { 424 // In case the error is due to permission, etc. 425 return nil, err 426 } 427 } 428 if cur == nil { 429 err := os.ErrNotExist 430 if lastCerr != nil { 431 err = lastCerr 432 } 433 return nil, err 434 } 435 return cur, nil 436 } 437 438 // Try 'pending rename' files. 439 var nums []int64 440 for _, fi := range fis { 441 name := fi.Name() 442 if strings.HasPrefix(name, "CURRENT.") && name != "CURRENT.bak" { 443 i, err := strconv.ParseInt(name[8:], 10, 64) 444 if err == nil { 445 nums = append(nums, i) 446 } 447 } 448 } 449 var ( 450 pendCur *currentFile 451 pendErr = os.ErrNotExist 452 pendNames []string 453 ) 454 if len(nums) > 0 { 455 sort.Sort(sort.Reverse(int64Slice(nums))) 456 pendNames = make([]string, len(nums)) 457 for i, num := range nums { 458 pendNames[i] = fmt.Sprintf("CURRENT.%d", num) 459 } 460 pendCur, pendErr = tryCurrents(pendNames) 461 if pendErr != nil && pendErr != os.ErrNotExist && !isCorrupted(pendErr) { 462 return storage.FileDesc{}, pendErr 463 } 464 } 465 466 // Try CURRENT and CURRENT.bak. 467 curCur, curErr := tryCurrents([]string{"CURRENT", "CURRENT.bak"}) 468 if curErr != nil && curErr != os.ErrNotExist && !isCorrupted(curErr) { 469 return storage.FileDesc{}, curErr 470 } 471 472 // pendCur takes precedence, but guards against obsolete pendCur. 473 if pendCur != nil && (curCur == nil || pendCur.fd.Num > curCur.fd.Num) { 474 curCur = pendCur 475 } 476 477 if curCur != nil { 478 // Restore CURRENT file to proper state. 479 if !fs.readOnly && (curCur.name != "CURRENT" || len(pendNames) != 0) { 480 // Ignore setMeta errors, however don't delete obsolete files if we 481 // catch error. 482 if err := fs.setMeta(curCur.fd); err == nil { 483 // Remove 'pending rename' files. 484 for _, name := range pendNames { 485 if err := fs.fs.Remove(name); err != nil { 486 fs.logRLocked(fmt.Sprintf("remove %s: %v", name, err)) 487 } 488 } 489 } 490 } 491 return curCur.fd, nil 492 } 493 494 // Nothing found. 495 if isCorrupted(pendErr) { 496 return storage.FileDesc{}, pendErr 497 } 498 return storage.FileDesc{}, curErr 499 } 500 501 func (fs *levelDBStorage) List(ft storage.FileType) (fds []storage.FileDesc, err error) { 502 fs.mu.Lock() 503 defer fs.mu.Unlock() 504 if fs.open < 0 { 505 return nil, storage.ErrClosed 506 } 507 fis, err := fs.fs.ReadDir("") 508 if err != nil { 509 return nil, err 510 } 511 if err == nil { 512 for _, fi := range fis { 513 if fd, ok := fsParseName(fi.Name()); ok && fd.Type&ft != 0 { 514 fds = append(fds, fd) 515 } 516 } 517 } 518 return 519 } 520 521 func (fs *levelDBStorage) Open(fd storage.FileDesc) (storage.Reader, error) { 522 if !storage.FileDescOk(fd) { 523 return nil, storage.ErrInvalidFile 524 } 525 526 fs.mu.Lock() 527 defer fs.mu.Unlock() 528 if fs.open < 0 { 529 return nil, storage.ErrClosed 530 } 531 of, err := fs.fs.OpenFile(fsGenName(fd), os.O_RDONLY, 0) 532 if err != nil { 533 if fsHasOldName(fd) && os.IsNotExist(err) { 534 of, err = fs.fs.OpenFile(fsGenOldName(fd), os.O_RDONLY, 0) 535 if err == nil { 536 goto ok 537 } 538 } 539 return nil, err 540 } 541 ok: 542 fs.open++ 543 return &fileWrap{File: of, fs: fs, fd: fd}, nil 544 } 545 546 func (fs *levelDBStorage) Create(fd storage.FileDesc) (storage.Writer, error) { 547 fs.syncLock.RLock() 548 defer fs.syncLock.RUnlock() 549 550 if !storage.FileDescOk(fd) { 551 return nil, storage.ErrInvalidFile 552 } 553 if fs.readOnly { 554 return nil, errReadOnly 555 } 556 557 fs.mu.Lock() 558 defer fs.mu.Unlock() 559 if fs.open < 0 { 560 return nil, storage.ErrClosed 561 } 562 of, err := fs.fs.OpenFile(fsGenName(fd), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 563 if err != nil { 564 return nil, err 565 } 566 fs.open++ 567 return &fileWrap{File: of, fs: fs, fd: fd}, nil 568 } 569 570 func (fs *levelDBStorage) Remove(fd storage.FileDesc) error { 571 fs.syncLock.RLock() 572 defer fs.syncLock.RUnlock() 573 574 if !storage.FileDescOk(fd) { 575 return storage.ErrInvalidFile 576 } 577 if fs.readOnly { 578 return errReadOnly 579 } 580 581 fs.mu.Lock() 582 defer fs.mu.Unlock() 583 if fs.open < 0 { 584 return storage.ErrClosed 585 } 586 err := fs.fs.Remove(fsGenName(fd)) 587 if err != nil { 588 if fsHasOldName(fd) && os.IsNotExist(err) { 589 if e1 := fs.fs.Remove(fsGenOldName(fd)); !os.IsNotExist(e1) { 590 fs.logRLocked(fmt.Sprintf("remove %s: %v (old name)", fd, err)) 591 err = e1 592 } 593 } else { 594 fs.logRLocked(fmt.Sprintf("remove %s: %v", fd, err)) 595 } 596 } 597 return err 598 } 599 600 func (fs *levelDBStorage) Rename(oldfd, newfd storage.FileDesc) error { 601 fs.syncLock.RLock() 602 defer fs.syncLock.RUnlock() 603 604 if !storage.FileDescOk(oldfd) || !storage.FileDescOk(newfd) { 605 return storage.ErrInvalidFile 606 } 607 if oldfd == newfd { 608 return nil 609 } 610 if fs.readOnly { 611 return errReadOnly 612 } 613 614 fs.mu.Lock() 615 defer fs.mu.Unlock() 616 if fs.open < 0 { 617 return storage.ErrClosed 618 } 619 return fs.fs.Rename(fsGenName(oldfd), fsGenName(newfd)) 620 } 621 622 func (fs *levelDBStorage) Close() error { 623 fs.mu.Lock() 624 defer fs.mu.Unlock() 625 if fs.open < 0 { 626 return storage.ErrClosed 627 } 628 // Clear the finalizer. 629 runtime.SetFinalizer(fs, nil) 630 631 if fs.open > 0 { 632 fs.log(fmt.Sprintf("close: warning, %d files still open", fs.open)) 633 } 634 fs.open = -1 635 if fs.logw != nil { 636 fs.logw.Close() 637 } 638 return fs.flock.Close() 639 } 640 641 type fileWrap struct { 642 billy.File 643 fs *levelDBStorage 644 fd storage.FileDesc 645 closed bool 646 } 647 648 func (fw *fileWrap) Write(p []byte) (n int, err error) { 649 fw.fs.syncLock.RLock() 650 defer fw.fs.syncLock.RUnlock() 651 return fw.File.Write(p) 652 } 653 654 func (fw *fileWrap) Sync() error { 655 return fw.fs.sync() 656 } 657 658 func (fw *fileWrap) Close() error { 659 fw.fs.mu.Lock() 660 defer fw.fs.mu.Unlock() 661 if fw.closed { 662 return storage.ErrClosed 663 } 664 fw.closed = true 665 fw.fs.open-- 666 err := fw.File.Close() 667 if err != nil { 668 fw.fs.log(fmt.Sprintf("close %s: %v", fw.fd, err)) 669 } 670 return err 671 } 672 673 func fsGenName(fd storage.FileDesc) string { 674 switch fd.Type { 675 case storage.TypeManifest: 676 return fmt.Sprintf("MANIFEST-%06d", fd.Num) 677 case storage.TypeJournal: 678 return fmt.Sprintf("%06d.log", fd.Num) 679 case storage.TypeTable: 680 return fmt.Sprintf("%06d.ldb", fd.Num) 681 case storage.TypeTemp: 682 return fmt.Sprintf("%06d.tmp", fd.Num) 683 default: 684 panic("invalid file type") 685 } 686 } 687 688 func fsHasOldName(fd storage.FileDesc) bool { 689 return fd.Type == storage.TypeTable 690 } 691 692 func fsGenOldName(fd storage.FileDesc) string { 693 switch fd.Type { 694 case storage.TypeTable: 695 return fmt.Sprintf("%06d.sst", fd.Num) 696 default: 697 return fsGenName(fd) 698 } 699 } 700 701 func fsParseName(name string) (fd storage.FileDesc, ok bool) { 702 var tail string 703 _, err := fmt.Sscanf(name, "%d.%s", &fd.Num, &tail) 704 if err == nil { 705 switch tail { 706 case "log": 707 fd.Type = storage.TypeJournal 708 case "ldb", "sst": 709 fd.Type = storage.TypeTable 710 case "tmp": 711 fd.Type = storage.TypeTemp 712 default: 713 return 714 } 715 return fd, true 716 } 717 n, _ := fmt.Sscanf(name, "MANIFEST-%d%s", &fd.Num, &tail) 718 if n == 1 { 719 fd.Type = storage.TypeManifest 720 return fd, true 721 } 722 return 723 } 724 725 func fsParseNamePtr(name string, fd *storage.FileDesc) bool { 726 _fd, ok := fsParseName(name) 727 if fd != nil { 728 *fd = _fd 729 } 730 return ok 731 }