bep_extensions.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. // Copyright (C) 2014 The Protocol Authors.
  2. package protocol
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "runtime"
  9. "time"
  10. "github.com/syncthing/syncthing/lib/rand"
  11. "github.com/syncthing/syncthing/lib/sha256"
  12. )
  13. const (
  14. SyntheticDirectorySize = 128
  15. HelloMessageMagic uint32 = 0x2EA7D90B
  16. Version13HelloMagic uint32 = 0x9F79BC40 // old
  17. )
  18. // FileIntf is the set of methods implemented by both FileInfo and
  19. // db.FileInfoTruncated.
  20. type FileIntf interface {
  21. FileSize() int64
  22. FileName() string
  23. FileLocalFlags() uint32
  24. IsDeleted() bool
  25. IsInvalid() bool
  26. IsIgnored() bool
  27. IsUnsupported() bool
  28. MustRescan() bool
  29. IsReceiveOnlyChanged() bool
  30. IsDirectory() bool
  31. IsSymlink() bool
  32. ShouldConflict() bool
  33. HasPermissionBits() bool
  34. SequenceNo() int64
  35. BlockSize() int
  36. FileVersion() Vector
  37. FileType() FileInfoType
  38. FilePermissions() uint32
  39. FileModifiedBy() ShortID
  40. ModTime() time.Time
  41. }
  42. func (m Hello) Magic() uint32 {
  43. return HelloMessageMagic
  44. }
  45. func (f FileInfo) String() string {
  46. switch f.Type {
  47. case FileInfoTypeDirectory:
  48. return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v}",
  49. f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions)
  50. case FileInfoTypeFile:
  51. return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d, Blocks:%v, BlocksHash:%x}",
  52. f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.RawBlockSize, f.Blocks, f.BlocksHash)
  53. case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile:
  54. return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q}",
  55. f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.SymlinkTarget)
  56. default:
  57. panic("mystery file type detected")
  58. }
  59. }
  60. func (f FileInfo) IsDeleted() bool {
  61. return f.Deleted
  62. }
  63. func (f FileInfo) IsInvalid() bool {
  64. return f.RawInvalid || f.LocalFlags&LocalInvalidFlags != 0
  65. }
  66. func (f FileInfo) IsUnsupported() bool {
  67. return f.LocalFlags&FlagLocalUnsupported != 0
  68. }
  69. func (f FileInfo) IsIgnored() bool {
  70. return f.LocalFlags&FlagLocalIgnored != 0
  71. }
  72. func (f FileInfo) MustRescan() bool {
  73. return f.LocalFlags&FlagLocalMustRescan != 0
  74. }
  75. func (f FileInfo) IsReceiveOnlyChanged() bool {
  76. return f.LocalFlags&FlagLocalReceiveOnly != 0
  77. }
  78. func (f FileInfo) IsDirectory() bool {
  79. return f.Type == FileInfoTypeDirectory
  80. }
  81. func (f FileInfo) ShouldConflict() bool {
  82. return f.LocalFlags&LocalConflictFlags != 0
  83. }
  84. func (f FileInfo) IsSymlink() bool {
  85. switch f.Type {
  86. case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile:
  87. return true
  88. default:
  89. return false
  90. }
  91. }
  92. func (f FileInfo) HasPermissionBits() bool {
  93. return !f.NoPermissions
  94. }
  95. func (f FileInfo) FileSize() int64 {
  96. if f.Deleted {
  97. return 0
  98. }
  99. if f.IsDirectory() || f.IsSymlink() {
  100. return SyntheticDirectorySize
  101. }
  102. return f.Size
  103. }
  104. func (f FileInfo) BlockSize() int {
  105. if f.RawBlockSize == 0 {
  106. return MinBlockSize
  107. }
  108. return int(f.RawBlockSize)
  109. }
  110. func (f FileInfo) FileName() string {
  111. return f.Name
  112. }
  113. func (f FileInfo) FileLocalFlags() uint32 {
  114. return f.LocalFlags
  115. }
  116. func (f FileInfo) ModTime() time.Time {
  117. return time.Unix(f.ModifiedS, int64(f.ModifiedNs))
  118. }
  119. func (f FileInfo) SequenceNo() int64 {
  120. return f.Sequence
  121. }
  122. func (f FileInfo) FileVersion() Vector {
  123. return f.Version
  124. }
  125. func (f FileInfo) FileType() FileInfoType {
  126. return f.Type
  127. }
  128. func (f FileInfo) FilePermissions() uint32 {
  129. return f.Permissions
  130. }
  131. func (f FileInfo) FileModifiedBy() ShortID {
  132. return f.ModifiedBy
  133. }
  134. // WinsConflict returns true if "f" is the one to choose when it is in
  135. // conflict with "other".
  136. func WinsConflict(f, other FileIntf) bool {
  137. // If only one of the files is invalid, that one loses.
  138. if f.IsInvalid() != other.IsInvalid() {
  139. return !f.IsInvalid()
  140. }
  141. // If a modification is in conflict with a delete, we pick the
  142. // modification.
  143. if !f.IsDeleted() && other.IsDeleted() {
  144. return true
  145. }
  146. if f.IsDeleted() && !other.IsDeleted() {
  147. return false
  148. }
  149. // The one with the newer modification time wins.
  150. if f.ModTime().After(other.ModTime()) {
  151. return true
  152. }
  153. if f.ModTime().Before(other.ModTime()) {
  154. return false
  155. }
  156. // The modification times were equal. Use the device ID in the version
  157. // vector as tie breaker.
  158. return f.FileVersion().Compare(other.FileVersion()) == ConcurrentGreater
  159. }
  160. func (f FileInfo) IsEmpty() bool {
  161. return f.Version.Counters == nil
  162. }
  163. func (f FileInfo) IsEquivalent(other FileInfo, modTimeWindow time.Duration) bool {
  164. return f.isEquivalent(other, modTimeWindow, false, false, 0)
  165. }
  166. func (f FileInfo) IsEquivalentOptional(other FileInfo, modTimeWindow time.Duration, ignorePerms bool, ignoreBlocks bool, ignoreFlags uint32) bool {
  167. return f.isEquivalent(other, modTimeWindow, ignorePerms, ignoreBlocks, ignoreFlags)
  168. }
  169. // isEquivalent checks that the two file infos represent the same actual file content,
  170. // i.e. it does purposely not check only selected (see below) struct members.
  171. // Permissions (config) and blocks (scanning) can be excluded from the comparison.
  172. // Any file info is not "equivalent", if it has different
  173. // - type
  174. // - deleted flag
  175. // - invalid flag
  176. // - permissions, unless they are ignored
  177. // A file is not "equivalent", if it has different
  178. // - modification time (difference bigger than modTimeWindow)
  179. // - size
  180. // - blocks, unless there are no blocks to compare (scanning)
  181. // A symlink is not "equivalent", if it has different
  182. // - target
  183. // A directory does not have anything specific to check.
  184. func (f FileInfo) isEquivalent(other FileInfo, modTimeWindow time.Duration, ignorePerms bool, ignoreBlocks bool, ignoreFlags uint32) bool {
  185. if f.MustRescan() || other.MustRescan() {
  186. // These are per definition not equivalent because they don't
  187. // represent a valid state, even if both happen to have the
  188. // MustRescan bit set.
  189. return false
  190. }
  191. // Mask out the ignored local flags before checking IsInvalid() below
  192. f.LocalFlags &^= ignoreFlags
  193. other.LocalFlags &^= ignoreFlags
  194. if f.Name != other.Name || f.Type != other.Type || f.Deleted != other.Deleted || f.IsInvalid() != other.IsInvalid() {
  195. return false
  196. }
  197. if !ignorePerms && !f.NoPermissions && !other.NoPermissions && !PermsEqual(f.Permissions, other.Permissions) {
  198. return false
  199. }
  200. switch f.Type {
  201. case FileInfoTypeFile:
  202. return f.Size == other.Size && ModTimeEqual(f.ModTime(), other.ModTime(), modTimeWindow) && (ignoreBlocks || f.BlocksEqual(other))
  203. case FileInfoTypeSymlink:
  204. return f.SymlinkTarget == other.SymlinkTarget
  205. case FileInfoTypeDirectory:
  206. return true
  207. }
  208. return false
  209. }
  210. func ModTimeEqual(a, b time.Time, modTimeWindow time.Duration) bool {
  211. if a.Equal(b) {
  212. return true
  213. }
  214. diff := a.Sub(b)
  215. if diff < 0 {
  216. diff *= -1
  217. }
  218. return diff < modTimeWindow
  219. }
  220. func PermsEqual(a, b uint32) bool {
  221. switch runtime.GOOS {
  222. case "windows":
  223. // There is only writeable and read only, represented for user, group
  224. // and other equally. We only compare against user.
  225. return a&0600 == b&0600
  226. default:
  227. // All bits count
  228. return a&0777 == b&0777
  229. }
  230. }
  231. // BlocksEqual returns true when the two files have identical block lists.
  232. func (f FileInfo) BlocksEqual(other FileInfo) bool {
  233. // If both sides have blocks hashes and they match, we are good. If they
  234. // don't match still check individual block hashes to catch differences
  235. // in weak hashes only (e.g. after switching weak hash algo).
  236. if len(f.BlocksHash) > 0 && len(other.BlocksHash) > 0 && bytes.Equal(f.BlocksHash, other.BlocksHash) {
  237. return true
  238. }
  239. // Actually compare the block lists in full.
  240. return blocksEqual(f.Blocks, other.Blocks)
  241. }
  242. // blocksEqual returns whether two slices of blocks are exactly the same hash
  243. // and index pair wise.
  244. func blocksEqual(a, b []BlockInfo) bool {
  245. if len(b) != len(a) {
  246. return false
  247. }
  248. for i, sblk := range a {
  249. if !bytes.Equal(sblk.Hash, b[i].Hash) {
  250. return false
  251. }
  252. }
  253. return true
  254. }
  255. func (f *FileInfo) SetMustRescan(by ShortID) {
  256. f.setLocalFlags(by, FlagLocalMustRescan)
  257. }
  258. func (f *FileInfo) SetIgnored(by ShortID) {
  259. f.setLocalFlags(by, FlagLocalIgnored)
  260. }
  261. func (f *FileInfo) SetUnsupported(by ShortID) {
  262. f.setLocalFlags(by, FlagLocalUnsupported)
  263. }
  264. func (f *FileInfo) SetDeleted(by ShortID) {
  265. f.ModifiedBy = by
  266. f.Deleted = true
  267. f.Version = f.Version.Update(by)
  268. f.ModifiedS = time.Now().Unix()
  269. f.setNoContent()
  270. }
  271. func (f *FileInfo) setLocalFlags(by ShortID, flags uint32) {
  272. f.RawInvalid = false
  273. f.LocalFlags = flags
  274. f.ModifiedBy = by
  275. f.setNoContent()
  276. }
  277. func (f *FileInfo) setNoContent() {
  278. f.Blocks = nil
  279. f.BlocksHash = nil
  280. f.Size = 0
  281. }
  282. func (b BlockInfo) String() string {
  283. return fmt.Sprintf("Block{%d/%d/%d/%x}", b.Offset, b.Size, b.WeakHash, b.Hash)
  284. }
  285. // IsEmpty returns true if the block is a full block of zeroes.
  286. func (b BlockInfo) IsEmpty() bool {
  287. if v, ok := sha256OfEmptyBlock[int(b.Size)]; ok {
  288. return bytes.Equal(b.Hash, v[:])
  289. }
  290. return false
  291. }
  292. type IndexID uint64
  293. func (i IndexID) String() string {
  294. return fmt.Sprintf("0x%016X", uint64(i))
  295. }
  296. func (i IndexID) Marshal() ([]byte, error) {
  297. bs := make([]byte, 8)
  298. binary.BigEndian.PutUint64(bs, uint64(i))
  299. return bs, nil
  300. }
  301. func (i *IndexID) Unmarshal(bs []byte) error {
  302. if len(bs) != 8 {
  303. return errors.New("incorrect IndexID length")
  304. }
  305. *i = IndexID(binary.BigEndian.Uint64(bs))
  306. return nil
  307. }
  308. func NewIndexID() IndexID {
  309. return IndexID(rand.Uint64())
  310. }
  311. func (f Folder) Description() string {
  312. // used by logging stuff
  313. if f.Label == "" {
  314. return f.ID
  315. }
  316. return fmt.Sprintf("%q (%s)", f.Label, f.ID)
  317. }
  318. func BlocksHash(bs []BlockInfo) []byte {
  319. h := sha256.New()
  320. for _, b := range bs {
  321. _, _ = h.Write(b.Hash)
  322. _ = binary.Write(h, binary.BigEndian, b.WeakHash)
  323. }
  324. return h.Sum(nil)
  325. }
  326. func VectorHash(v Vector) []byte {
  327. h := sha256.New()
  328. for _, c := range v.Counters {
  329. if err := binary.Write(h, binary.BigEndian, c.ID); err != nil {
  330. panic("impossible: failed to write c.ID to hash function: " + err.Error())
  331. }
  332. if err := binary.Write(h, binary.BigEndian, c.Value); err != nil {
  333. panic("impossible: failed to write c.Value to hash function: " + err.Error())
  334. }
  335. }
  336. return h.Sum(nil)
  337. }