util.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package model
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "runtime"
  7. "github.com/calmh/syncthing/protocol"
  8. "github.com/calmh/syncthing/scanner"
  9. )
  10. func Rename(from, to string) error {
  11. if runtime.GOOS == "windows" {
  12. os.Chmod(to, 0666) // Make sure the file is user writeable
  13. err := os.Remove(to)
  14. if err != nil && !os.IsNotExist(err) {
  15. l.Warnln(err)
  16. }
  17. }
  18. defer os.Remove(from) // Don't leave a dangling temp file in case of rename error
  19. return os.Rename(from, to)
  20. }
  21. func fileFromFileInfo(f protocol.FileInfo) scanner.File {
  22. var blocks = make([]scanner.Block, len(f.Blocks))
  23. var offset int64
  24. for i, b := range f.Blocks {
  25. blocks[i] = scanner.Block{
  26. Offset: offset,
  27. Size: b.Size,
  28. Hash: b.Hash,
  29. }
  30. offset += int64(b.Size)
  31. }
  32. return scanner.File{
  33. // Name is with native separator and normalization
  34. Name: filepath.FromSlash(f.Name),
  35. Size: offset,
  36. Flags: f.Flags &^ protocol.FlagInvalid,
  37. Modified: f.Modified,
  38. Version: f.Version,
  39. Blocks: blocks,
  40. Suppressed: f.Flags&protocol.FlagInvalid != 0,
  41. }
  42. }
  43. func fileInfoFromFile(f scanner.File) protocol.FileInfo {
  44. var blocks = make([]protocol.BlockInfo, len(f.Blocks))
  45. for i, b := range f.Blocks {
  46. blocks[i] = protocol.BlockInfo{
  47. Size: b.Size,
  48. Hash: b.Hash,
  49. }
  50. }
  51. pf := protocol.FileInfo{
  52. Name: filepath.ToSlash(f.Name),
  53. Flags: f.Flags,
  54. Modified: f.Modified,
  55. Version: f.Version,
  56. Blocks: blocks,
  57. }
  58. if f.Suppressed {
  59. pf.Flags |= protocol.FlagInvalid
  60. }
  61. return pf
  62. }
  63. func cmMap(cm protocol.ClusterConfigMessage) map[string]map[string]uint32 {
  64. m := make(map[string]map[string]uint32)
  65. for _, repo := range cm.Repositories {
  66. m[repo.ID] = make(map[string]uint32)
  67. for _, node := range repo.Nodes {
  68. m[repo.ID][node.ID] = node.Flags
  69. }
  70. }
  71. return m
  72. }
  73. type ClusterConfigMismatch error
  74. // compareClusterConfig returns nil for two equivalent configurations,
  75. // otherwise a decriptive error
  76. func compareClusterConfig(local, remote protocol.ClusterConfigMessage) error {
  77. lm := cmMap(local)
  78. rm := cmMap(remote)
  79. for repo, lnodes := range lm {
  80. _ = lnodes
  81. if rnodes, ok := rm[repo]; ok {
  82. for node, lflags := range lnodes {
  83. if rflags, ok := rnodes[node]; ok {
  84. if lflags&protocol.FlagShareBits != rflags&protocol.FlagShareBits {
  85. return ClusterConfigMismatch(fmt.Errorf("remote has different sharing flags for node %q in repository %q", node, repo))
  86. }
  87. }
  88. }
  89. } else {
  90. return ClusterConfigMismatch(fmt.Errorf("remote is missing repository %q", repo))
  91. }
  92. }
  93. for repo := range rm {
  94. if _, ok := lm[repo]; !ok {
  95. return ClusterConfigMismatch(fmt.Errorf("remote has extra repository %q", repo))
  96. }
  97. }
  98. return nil
  99. }
  100. func permsEqual(a, b uint32) bool {
  101. switch runtime.GOOS {
  102. case "windows":
  103. // There is only writeable and read only, represented for user, group
  104. // and other equally. We only compare against user.
  105. return a&0600 == b&0600
  106. default:
  107. // All bits count
  108. return a&0777 == b&0777
  109. }
  110. }