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.

142 lines
3.2 KiB

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