fix.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package command
  2. import (
  3. "fmt"
  4. "io/fs"
  5. "os"
  6. "path"
  7. "strconv"
  8. "strings"
  9. "github.com/seaweedfs/seaweedfs/weed/glog"
  10. "github.com/seaweedfs/seaweedfs/weed/storage"
  11. "github.com/seaweedfs/seaweedfs/weed/storage/backend"
  12. "github.com/seaweedfs/seaweedfs/weed/storage/needle"
  13. "github.com/seaweedfs/seaweedfs/weed/storage/needle_map"
  14. "github.com/seaweedfs/seaweedfs/weed/storage/super_block"
  15. "github.com/seaweedfs/seaweedfs/weed/storage/types"
  16. "github.com/seaweedfs/seaweedfs/weed/util"
  17. )
  18. func init() {
  19. cmdFix.Run = runFix // break init cycle
  20. }
  21. var cmdFix = &Command{
  22. UsageLine: "fix [-remoteFile=false] [-volumeId=234] [-collection=bigData] /tmp",
  23. Short: "run weed tool fix on files or whole folders to recreate index file(s) if corrupted",
  24. Long: `Fix runs the SeaweedFS fix command on local dat files ( or remote files) or whole folders to re-create the index .idx file. If fixing remote files, you need to synchronize master.toml to the same directory on the current node as on the master node.
  25. You Need to stop the volume server when running this command.
  26. `,
  27. }
  28. var (
  29. fixVolumeCollection = cmdFix.Flag.String("collection", "", "an optional volume collection name, if specified only it will be processed")
  30. fixVolumeId = cmdFix.Flag.Int64("volumeId", 0, "an optional volume id, if not 0 (default) only it will be processed")
  31. fixIncludeDeleted = cmdFix.Flag.Bool("includeDeleted", true, "include deleted entries in the index file")
  32. fixIgnoreError = cmdFix.Flag.Bool("ignoreError", false, "an optional, if true will be processed despite errors")
  33. fixRemoteFile = cmdFix.Flag.Bool("remoteFile", false, "an optional, if true will not try to load the local .dat file, but only the remote file")
  34. )
  35. type VolumeFileScanner4Fix struct {
  36. version needle.Version
  37. nm *needle_map.MemDb
  38. nmDeleted *needle_map.MemDb
  39. includeDeleted bool
  40. }
  41. func (scanner *VolumeFileScanner4Fix) VisitSuperBlock(superBlock super_block.SuperBlock) error {
  42. scanner.version = superBlock.Version
  43. return nil
  44. }
  45. func (scanner *VolumeFileScanner4Fix) ReadNeedleBody() bool {
  46. return false
  47. }
  48. func (scanner *VolumeFileScanner4Fix) VisitNeedle(n *needle.Needle, offset int64, needleHeader, needleBody []byte) error {
  49. glog.V(2).Infof("key %v offset %d size %d disk_size %d compressed %v", n.Id, offset, n.Size, n.DiskSize(scanner.version), n.IsCompressed())
  50. if n.Size.IsValid() {
  51. if pe := scanner.nm.Set(n.Id, types.ToOffset(offset), n.Size); pe != nil {
  52. return fmt.Errorf("saved %d with error %v", n.Size, pe)
  53. }
  54. } else {
  55. if scanner.includeDeleted {
  56. if pe := scanner.nmDeleted.Set(n.Id, types.ToOffset(offset), types.TombstoneFileSize); pe != nil {
  57. return fmt.Errorf("saved deleted %d with error %v", n.Size, pe)
  58. }
  59. } else {
  60. glog.V(2).Infof("skipping deleted file ...")
  61. return scanner.nm.Delete(n.Id)
  62. }
  63. }
  64. return nil
  65. }
  66. func runFix(cmd *Command, args []string) bool {
  67. for _, arg := range args {
  68. basePath, f := path.Split(util.ResolvePath(arg))
  69. if util.FolderExists(arg) {
  70. basePath = arg
  71. f = ""
  72. }
  73. files := []fs.DirEntry{}
  74. if f == "" {
  75. fileInfo, err := os.ReadDir(basePath)
  76. if err != nil {
  77. fmt.Println(err)
  78. return false
  79. }
  80. files = fileInfo
  81. } else {
  82. fileInfo, err := os.Stat(arg)
  83. if err != nil {
  84. fmt.Println(err)
  85. return false
  86. }
  87. files = []fs.DirEntry{fs.FileInfoToDirEntry(fileInfo)}
  88. }
  89. ext := ".dat"
  90. if *fixRemoteFile {
  91. ext = ".idx"
  92. util.LoadConfiguration("master", false)
  93. backend.LoadConfiguration(util.GetViper())
  94. }
  95. for _, file := range files {
  96. if !strings.HasSuffix(file.Name(), ext) {
  97. continue
  98. }
  99. if *fixVolumeCollection != "" {
  100. if !strings.HasPrefix(file.Name(), *fixVolumeCollection+"_") {
  101. continue
  102. }
  103. }
  104. baseFileName := file.Name()[:len(file.Name())-4]
  105. collection, volumeIdStr := "", baseFileName
  106. if sepIndex := strings.LastIndex(baseFileName, "_"); sepIndex > 0 {
  107. collection = baseFileName[:sepIndex]
  108. volumeIdStr = baseFileName[sepIndex+1:]
  109. }
  110. volumeId, parseErr := strconv.ParseInt(volumeIdStr, 10, 64)
  111. if parseErr != nil {
  112. fmt.Printf("Failed to parse volume id from %s: %v\n", baseFileName, parseErr)
  113. return false
  114. }
  115. if *fixVolumeId != 0 && *fixVolumeId != volumeId {
  116. continue
  117. }
  118. doFixOneVolume(basePath, baseFileName, collection, volumeId, *fixIncludeDeleted)
  119. }
  120. }
  121. return true
  122. }
  123. func SaveToIdx(scaner *VolumeFileScanner4Fix, idxName string) (ret error) {
  124. idxFile, err := os.OpenFile(idxName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  125. if err != nil {
  126. return
  127. }
  128. defer func() {
  129. idxFile.Close()
  130. }()
  131. return scaner.nm.AscendingVisit(func(value needle_map.NeedleValue) error {
  132. _, err := idxFile.Write(value.ToBytes())
  133. if scaner.includeDeleted && err == nil {
  134. if deleted, ok := scaner.nmDeleted.Get(value.Key); ok {
  135. _, err = idxFile.Write(deleted.ToBytes())
  136. }
  137. }
  138. return err
  139. })
  140. }
  141. func doFixOneVolume(basepath string, baseFileName string, collection string, volumeId int64, fixIncludeDeleted bool) {
  142. indexFileName := path.Join(basepath, baseFileName+".idx")
  143. nm := needle_map.NewMemDb()
  144. nmDeleted := needle_map.NewMemDb()
  145. defer nm.Close()
  146. defer nmDeleted.Close()
  147. vid := needle.VolumeId(volumeId)
  148. scanner := &VolumeFileScanner4Fix{
  149. nm: nm,
  150. nmDeleted: nmDeleted,
  151. includeDeleted: fixIncludeDeleted,
  152. }
  153. if err := storage.ScanVolumeFile(basepath, collection, vid, storage.NeedleMapInMemory, scanner); err != nil {
  154. err := fmt.Errorf("scan .dat File: %w", err)
  155. if *fixIgnoreError {
  156. glog.Error(err)
  157. } else {
  158. glog.Fatal(err)
  159. }
  160. }
  161. if err := SaveToIdx(scanner, indexFileName); err != nil {
  162. err := fmt.Errorf("save to .idx File: %w", err)
  163. if *fixIgnoreError {
  164. glog.Error(err)
  165. } else {
  166. os.Remove(indexFileName)
  167. glog.Fatal(err)
  168. }
  169. }
  170. }