github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/base/filenames.go (about) 1 // Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package base 6 7 import ( 8 "fmt" 9 "strconv" 10 "strings" 11 12 "github.com/cockroachdb/errors/oserror" 13 "github.com/cockroachdb/redact" 14 "github.com/zuoyebang/bitalostable/vfs" 15 ) 16 17 // FileNum is an internal DB identifier for a file. 18 type FileNum uint64 19 20 // String returns a string representation of the file number. 21 func (fn FileNum) String() string { return fmt.Sprintf("%06d", fn) } 22 23 // FileType enumerates the types of files found in a DB. 24 type FileType int 25 26 // The FileType enumeration. 27 const ( 28 FileTypeLog FileType = iota 29 FileTypeLock 30 FileTypeTable 31 FileTypeManifest 32 FileTypeCurrent 33 FileTypeOptions 34 FileTypeOldTemp 35 FileTypeTemp 36 ) 37 38 // MakeFilename builds a filename from components. 39 func MakeFilename(fileType FileType, fileNum FileNum) string { 40 switch fileType { 41 case FileTypeLog: 42 return fmt.Sprintf("%s.log", fileNum) 43 case FileTypeLock: 44 return "LOCK" 45 case FileTypeTable: 46 return fmt.Sprintf("%s.sst", fileNum) 47 case FileTypeManifest: 48 return fmt.Sprintf("MANIFEST-%s", fileNum) 49 case FileTypeCurrent: 50 return "CURRENT" 51 case FileTypeOptions: 52 return fmt.Sprintf("OPTIONS-%s", fileNum) 53 case FileTypeOldTemp: 54 return fmt.Sprintf("CURRENT.%s.dbtmp", fileNum) 55 case FileTypeTemp: 56 return fmt.Sprintf("temporary.%s.dbtmp", fileNum) 57 } 58 panic("unreachable") 59 } 60 61 // MakeFilepath builds a filepath from components. 62 func MakeFilepath(fs vfs.FS, dirname string, fileType FileType, fileNum FileNum) string { 63 return fs.PathJoin(dirname, MakeFilename(fileType, fileNum)) 64 } 65 66 // ParseFilename parses the components from a filename. 67 func ParseFilename(fs vfs.FS, filename string) (fileType FileType, fileNum FileNum, ok bool) { 68 filename = fs.PathBase(filename) 69 switch { 70 case filename == "CURRENT": 71 return FileTypeCurrent, 0, true 72 case filename == "LOCK": 73 return FileTypeLock, 0, true 74 case strings.HasPrefix(filename, "MANIFEST-"): 75 fileNum, ok = parseFileNum(filename[len("MANIFEST-"):]) 76 if !ok { 77 break 78 } 79 return FileTypeManifest, fileNum, true 80 case strings.HasPrefix(filename, "OPTIONS-"): 81 fileNum, ok = parseFileNum(filename[len("OPTIONS-"):]) 82 if !ok { 83 break 84 } 85 return FileTypeOptions, fileNum, ok 86 case strings.HasPrefix(filename, "CURRENT.") && strings.HasSuffix(filename, ".dbtmp"): 87 s := strings.TrimSuffix(filename[len("CURRENT."):], ".dbtmp") 88 fileNum, ok = parseFileNum(s) 89 if !ok { 90 break 91 } 92 return FileTypeOldTemp, fileNum, ok 93 case strings.HasPrefix(filename, "temporary.") && strings.HasSuffix(filename, ".dbtmp"): 94 s := strings.TrimSuffix(filename[len("temporary."):], ".dbtmp") 95 fileNum, ok = parseFileNum(s) 96 if !ok { 97 break 98 } 99 return FileTypeTemp, fileNum, ok 100 default: 101 i := strings.IndexByte(filename, '.') 102 if i < 0 { 103 break 104 } 105 fileNum, ok = parseFileNum(filename[:i]) 106 if !ok { 107 break 108 } 109 switch filename[i+1:] { 110 case "sst": 111 return FileTypeTable, fileNum, true 112 case "log": 113 return FileTypeLog, fileNum, true 114 } 115 } 116 return 0, fileNum, false 117 } 118 119 func parseFileNum(s string) (fileNum FileNum, ok bool) { 120 u, err := strconv.ParseUint(s, 10, 64) 121 if err != nil { 122 return fileNum, false 123 } 124 return FileNum(u), true 125 } 126 127 // A Fataler fatals a process with a message when called. 128 type Fataler interface { 129 Fatalf(format string, args ...interface{}) 130 } 131 132 // MustExist checks if err is an error indicating a file does not exist. 133 // If it is, it lists the containing directory's files to annotate the error 134 // with counts of the various types of files and invokes the provided fataler. 135 // See cockroachdb/cockroach#56490. 136 func MustExist(fs vfs.FS, filename string, fataler Fataler, err error) { 137 if err == nil || !oserror.IsNotExist(err) { 138 return 139 } 140 141 ls, lsErr := fs.List(fs.PathDir(filename)) 142 if lsErr != nil { 143 // TODO(jackson): if oserror.IsNotExist(lsErr), the the data directory 144 // doesn't exist anymore. Another process likely deleted it before 145 // killing the process. We want to fatal the process, but without 146 // triggering error reporting like Sentry. 147 fataler.Fatalf("%s:\norig err: %s\nlist err: %s", redact.Safe(fs.PathBase(filename)), err, lsErr) 148 } 149 var total, unknown, tables, logs, manifests int 150 total = len(ls) 151 for _, f := range ls { 152 typ, _, ok := ParseFilename(fs, f) 153 if !ok { 154 unknown++ 155 continue 156 } 157 switch typ { 158 case FileTypeTable: 159 tables++ 160 case FileTypeLog: 161 logs++ 162 case FileTypeManifest: 163 manifests++ 164 } 165 } 166 167 fataler.Fatalf("%s:\n%s\ndirectory contains %d files, %d unknown, %d tables, %d logs, %d manifests", 168 fs.PathBase(filename), err, total, unknown, tables, logs, manifests) 169 }