You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
3.6 KiB

  1. package shell
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "os"
  7. "time"
  8. "github.com/chrislusf/seaweedfs/weed/filer2"
  9. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  10. "github.com/chrislusf/seaweedfs/weed/util"
  11. "github.com/golang/protobuf/proto"
  12. )
  13. func init() {
  14. Commands = append(Commands, &commandFsMetaSave{})
  15. }
  16. type commandFsMetaSave struct {
  17. }
  18. func (c *commandFsMetaSave) Name() string {
  19. return "fs.meta.save"
  20. }
  21. func (c *commandFsMetaSave) Help() string {
  22. return `save all directory and file meta data to a local file for metadata backup.
  23. fs.meta.save / # save from the root
  24. fs.meta.save /path/to/save # save from the directory /path/to/save
  25. fs.meta.save . # save from current directory
  26. fs.meta.save # save from current directory
  27. The meta data will be saved into a local <filer_host>-<port>-<time>.meta file.
  28. These meta data can be later loaded by fs.meta.load command,
  29. This assumes there are no deletions, so this is different from taking a snapshot.
  30. `
  31. }
  32. func (c *commandFsMetaSave) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
  33. filerServer, filerPort, path, err := commandEnv.parseUrl(findInputDirectory(args))
  34. if err != nil {
  35. return err
  36. }
  37. ctx := context.Background()
  38. return commandEnv.withFilerClient(ctx, filerServer, filerPort, func(client filer_pb.SeaweedFilerClient) error {
  39. t := time.Now()
  40. fileName := fmt.Sprintf("%s-%d-%4d%02d%02d-%02d%02d%02d.meta",
  41. filerServer, filerPort, t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
  42. dst, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  43. if err != nil {
  44. return nil
  45. }
  46. defer dst.Close()
  47. var dirCount, fileCount uint64
  48. sizeBuf := make([]byte, 4)
  49. err = doTraverse(ctx, writer, client, filer2.FullPath(path), func(parentPath filer2.FullPath, entry *filer_pb.Entry) error {
  50. protoMessage := &filer_pb.FullEntry{
  51. Dir: string(parentPath),
  52. Entry: entry,
  53. }
  54. bytes, err := proto.Marshal(protoMessage)
  55. if err != nil {
  56. return fmt.Errorf("marshall error: %v", err)
  57. }
  58. util.Uint32toBytes(sizeBuf, uint32(len(bytes)))
  59. dst.Write(sizeBuf)
  60. dst.Write(bytes)
  61. if entry.IsDirectory {
  62. dirCount++
  63. } else {
  64. fileCount++
  65. }
  66. println(parentPath.Child(entry.Name))
  67. return nil
  68. })
  69. if err == nil {
  70. fmt.Fprintf(writer, "\ntotal %d directories, %d files", dirCount, fileCount)
  71. fmt.Fprintf(writer, "\nmeta data for http://%s:%d%s is saved to %s\n", filerServer, filerPort, path, fileName)
  72. }
  73. return err
  74. })
  75. }
  76. func doTraverse(ctx context.Context, writer io.Writer, client filer_pb.SeaweedFilerClient, parentPath filer2.FullPath, fn func(parentPath filer2.FullPath, entry *filer_pb.Entry) error) (err error) {
  77. paginatedCount := -1
  78. startFromFileName := ""
  79. paginateSize := 1000
  80. for paginatedCount == -1 || paginatedCount == paginateSize {
  81. resp, listErr := client.ListEntries(ctx, &filer_pb.ListEntriesRequest{
  82. Directory: string(parentPath),
  83. Prefix: "",
  84. StartFromFileName: startFromFileName,
  85. InclusiveStartFrom: false,
  86. Limit: uint32(paginateSize),
  87. })
  88. if listErr != nil {
  89. err = listErr
  90. return
  91. }
  92. paginatedCount = len(resp.Entries)
  93. for _, entry := range resp.Entries {
  94. if err = fn(parentPath, entry); err != nil {
  95. return err
  96. }
  97. if entry.IsDirectory {
  98. subDir := fmt.Sprintf("%s/%s", parentPath, entry.Name)
  99. if parentPath == "/" {
  100. subDir = "/" + entry.Name
  101. }
  102. if err = doTraverse(ctx, writer, client, filer2.FullPath(subDir), fn); err != nil {
  103. return err
  104. }
  105. }
  106. startFromFileName = entry.Name
  107. }
  108. }
  109. return
  110. }