bep_extensions.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. // Copyright (C) 2014 The Protocol Authors.
  2. package protocol
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "time"
  10. "github.com/syncthing/syncthing/lib/build"
  11. "github.com/syncthing/syncthing/lib/rand"
  12. "github.com/syncthing/syncthing/lib/sha256"
  13. )
  14. const (
  15. SyntheticDirectorySize = 128
  16. HelloMessageMagic uint32 = 0x2EA7D90B
  17. Version13HelloMagic uint32 = 0x9F79BC40 // old
  18. )
  19. // FileIntf is the set of methods implemented by both FileInfo and
  20. // db.FileInfoTruncated.
  21. type FileIntf interface {
  22. FileSize() int64
  23. FileName() string
  24. FileLocalFlags() uint32
  25. IsDeleted() bool
  26. IsInvalid() bool
  27. IsIgnored() bool
  28. IsUnsupported() bool
  29. MustRescan() bool
  30. IsReceiveOnlyChanged() bool
  31. IsDirectory() bool
  32. IsSymlink() bool
  33. ShouldConflict() bool
  34. HasPermissionBits() bool
  35. SequenceNo() int64
  36. BlockSize() int
  37. FileVersion() Vector
  38. FileType() FileInfoType
  39. FilePermissions() uint32
  40. FileModifiedBy() ShortID
  41. ModTime() time.Time
  42. PlatformData() PlatformData
  43. InodeChangeTime() time.Time
  44. }
  45. func (Hello) Magic() uint32 {
  46. return HelloMessageMagic
  47. }
  48. func (f FileInfo) String() string {
  49. switch f.Type {
  50. case FileInfoTypeDirectory:
  51. return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, VersionHash:%x, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, Platform:%v, InodeChangeTime:%v}",
  52. f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.VersionHash, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.Platform, f.InodeChangeTime())
  53. case FileInfoTypeFile:
  54. return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, VersionHash:%x, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d, Blocks:%v, BlocksHash:%x, Platform:%v, InodeChangeTime:%v}",
  55. f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.VersionHash, f.Size, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.RawBlockSize, f.Blocks, f.BlocksHash, f.Platform, f.InodeChangeTime())
  56. case FileInfoTypeSymlink, FileInfoTypeSymlinkDirectory, FileInfoTypeSymlinkFile:
  57. return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, VersionHash:%x, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q, Platform:%v, InodeChangeTime:%v}",
  58. f.Name, f.Type, f.Sequence, f.Version, f.VersionHash, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.SymlinkTarget, f.Platform, f.InodeChangeTime())
  59. default:
  60. panic("mystery file type detected")
  61. }
  62. }
  63. func (f FileInfo) IsDeleted() bool {
  64. return f.Deleted
  65. }
  66. func (f FileInfo) IsInvalid() bool {
  67. return f.RawInvalid || f.LocalFlags&LocalInvalidFlags != 0
  68. }
  69. func (f FileInfo) IsUnsupported() bool {
  70. return f.LocalFlags&FlagLocalUnsupported != 0
  71. }
  72. func (f FileInfo) IsIgnored() bool {
  73. return f.LocalFlags&FlagLocalIgnored != 0
  74. }
  75. func (f FileInfo) MustRescan() bool {
  76. return f.LocalFlags&FlagLocalMustRescan != 0
  77. }
  78. func (f FileInfo) IsReceiveOnlyChanged() bool {
  79. return f.LocalFlags&FlagLocalReceiveOnly != 0
  80. }
  81. func (f FileInfo) IsDirectory() bool {
  82. return f.Type == FileInfoTypeDirectory
  83. }
  84. func (f FileInfo) ShouldConflict() bool {
  85. return f.LocalFlags&LocalConflictFlags != 0
  86. }
  87. func (f FileInfo) IsSymlink() bool {
  88. switch f.Type {
  89. case FileInfoTypeSymlink, FileInfoTypeSymlinkDirectory, FileInfoTypeSymlinkFile:
  90. return true
  91. default:
  92. return false
  93. }
  94. }
  95. func (f FileInfo) HasPermissionBits() bool {
  96. return !f.NoPermissions
  97. }
  98. func (f FileInfo) FileSize() int64 {
  99. if f.Deleted {
  100. return 0
  101. }
  102. if f.IsDirectory() || f.IsSymlink() {
  103. return SyntheticDirectorySize
  104. }
  105. return f.Size
  106. }
  107. func (f FileInfo) BlockSize() int {
  108. if f.RawBlockSize < MinBlockSize {
  109. return MinBlockSize
  110. }
  111. return f.RawBlockSize
  112. }
  113. func (f FileInfo) FileName() string {
  114. return f.Name
  115. }
  116. func (f FileInfo) FileLocalFlags() uint32 {
  117. return f.LocalFlags
  118. }
  119. func (f FileInfo) ModTime() time.Time {
  120. return time.Unix(f.ModifiedS, int64(f.ModifiedNs))
  121. }
  122. func (f FileInfo) SequenceNo() int64 {
  123. return f.Sequence
  124. }
  125. func (f FileInfo) FileVersion() Vector {
  126. return f.Version
  127. }
  128. func (f FileInfo) FileType() FileInfoType {
  129. return f.Type
  130. }
  131. func (f FileInfo) FilePermissions() uint32 {
  132. return f.Permissions
  133. }
  134. func (f FileInfo) FileModifiedBy() ShortID {
  135. return f.ModifiedBy
  136. }
  137. func (f FileInfo) PlatformData() PlatformData {
  138. return f.Platform
  139. }
  140. func (f FileInfo) InodeChangeTime() time.Time {
  141. return time.Unix(0, f.InodeChangeNs)
  142. }
  143. // WinsConflict returns true if "f" is the one to choose when it is in
  144. // conflict with "other".
  145. func WinsConflict(f, other FileIntf) bool {
  146. // If only one of the files is invalid, that one loses.
  147. if f.IsInvalid() != other.IsInvalid() {
  148. return !f.IsInvalid()
  149. }
  150. // If a modification is in conflict with a delete, we pick the
  151. // modification.
  152. if !f.IsDeleted() && other.IsDeleted() {
  153. return true
  154. }
  155. if f.IsDeleted() && !other.IsDeleted() {
  156. return false
  157. }
  158. // The one with the newer modification time wins.
  159. if f.ModTime().After(other.ModTime()) {
  160. return true
  161. }
  162. if f.ModTime().Before(other.ModTime()) {
  163. return false
  164. }
  165. // The modification times were equal. Use the device ID in the version
  166. // vector as tie breaker.
  167. return f.FileVersion().Compare(other.FileVersion()) == ConcurrentGreater
  168. }
  169. type FileInfoComparison struct {
  170. ModTimeWindow time.Duration
  171. IgnorePerms bool
  172. IgnoreBlocks bool
  173. IgnoreFlags uint32
  174. IgnoreOwnership bool
  175. IgnoreXattrs bool
  176. }
  177. func (f FileInfo) IsEquivalent(other FileInfo, modTimeWindow time.Duration) bool {
  178. return f.isEquivalent(other, FileInfoComparison{ModTimeWindow: modTimeWindow})
  179. }
  180. func (f FileInfo) IsEquivalentOptional(other FileInfo, comp FileInfoComparison) bool {
  181. return f.isEquivalent(other, comp)
  182. }
  183. // isEquivalent checks that the two file infos represent the same actual file content,
  184. // i.e. it does purposely not check only selected (see below) struct members.
  185. // Permissions (config) and blocks (scanning) can be excluded from the comparison.
  186. // Any file info is not "equivalent", if it has different
  187. // - type
  188. // - deleted flag
  189. // - invalid flag
  190. // - permissions, unless they are ignored
  191. //
  192. // A file is not "equivalent", if it has different
  193. // - modification time (difference bigger than modTimeWindow)
  194. // - size
  195. // - blocks, unless there are no blocks to compare (scanning)
  196. // - os data
  197. //
  198. // A symlink is not "equivalent", if it has different
  199. // - target
  200. //
  201. // A directory does not have anything specific to check.
  202. func (f FileInfo) isEquivalent(other FileInfo, comp FileInfoComparison) bool {
  203. if f.MustRescan() || other.MustRescan() {
  204. // These are per definition not equivalent because they don't
  205. // represent a valid state, even if both happen to have the
  206. // MustRescan bit set.
  207. return false
  208. }
  209. // If we are recording inode change times and it changed, they are not
  210. // equal.
  211. if f.InodeChangeNs != 0 && other.InodeChangeNs != 0 && f.InodeChangeNs != other.InodeChangeNs {
  212. return false
  213. }
  214. // Mask out the ignored local flags before checking IsInvalid() below
  215. f.LocalFlags &^= comp.IgnoreFlags
  216. other.LocalFlags &^= comp.IgnoreFlags
  217. if f.Name != other.Name || f.Type != other.Type || f.Deleted != other.Deleted || f.IsInvalid() != other.IsInvalid() {
  218. return false
  219. }
  220. // OS data comparison is special: we consider a difference only if an
  221. // entry for the same OS exists on both sides and they are different.
  222. // Otherwise a file would become different as soon as it's synced from
  223. // Windows to Linux, as Linux would add a new POSIX entry for the file.
  224. if !comp.IgnoreOwnership && f.Platform != other.Platform {
  225. if f.Platform.Unix != nil && other.Platform.Unix != nil {
  226. if *f.Platform.Unix != *other.Platform.Unix {
  227. return false
  228. }
  229. }
  230. if f.Platform.Windows != nil && other.Platform.Windows != nil {
  231. if f.Platform.Windows.OwnerName != other.Platform.Windows.OwnerName ||
  232. f.Platform.Windows.OwnerIsGroup != other.Platform.Windows.OwnerIsGroup {
  233. return false
  234. }
  235. }
  236. }
  237. if !comp.IgnoreXattrs && f.Platform != other.Platform {
  238. if !xattrsEqual(f.Platform.Linux, other.Platform.Linux) {
  239. return false
  240. }
  241. if !xattrsEqual(f.Platform.Darwin, other.Platform.Darwin) {
  242. return false
  243. }
  244. if !xattrsEqual(f.Platform.FreeBSD, other.Platform.FreeBSD) {
  245. return false
  246. }
  247. if !xattrsEqual(f.Platform.NetBSD, other.Platform.NetBSD) {
  248. return false
  249. }
  250. }
  251. if !comp.IgnorePerms && !f.NoPermissions && !other.NoPermissions && !PermsEqual(f.Permissions, other.Permissions) {
  252. return false
  253. }
  254. switch f.Type {
  255. case FileInfoTypeFile:
  256. return f.Size == other.Size && ModTimeEqual(f.ModTime(), other.ModTime(), comp.ModTimeWindow) && (comp.IgnoreBlocks || f.BlocksEqual(other))
  257. case FileInfoTypeSymlink:
  258. return f.SymlinkTarget == other.SymlinkTarget
  259. case FileInfoTypeDirectory:
  260. return true
  261. }
  262. return false
  263. }
  264. func ModTimeEqual(a, b time.Time, modTimeWindow time.Duration) bool {
  265. if a.Equal(b) {
  266. return true
  267. }
  268. diff := a.Sub(b)
  269. if diff < 0 {
  270. diff *= -1
  271. }
  272. return diff < modTimeWindow
  273. }
  274. func PermsEqual(a, b uint32) bool {
  275. if build.IsWindows {
  276. // There is only writeable and read only, represented for user, group
  277. // and other equally. We only compare against user.
  278. return a&0600 == b&0600
  279. }
  280. // All bits count
  281. return a&0777 == b&0777
  282. }
  283. // BlocksEqual returns true when the two files have identical block lists.
  284. func (f FileInfo) BlocksEqual(other FileInfo) bool {
  285. // If both sides have blocks hashes and they match, we are good. If they
  286. // don't match still check individual block hashes to catch differences
  287. // in weak hashes only (e.g. after switching weak hash algo).
  288. if len(f.BlocksHash) > 0 && len(other.BlocksHash) > 0 && bytes.Equal(f.BlocksHash, other.BlocksHash) {
  289. return true
  290. }
  291. // Actually compare the block lists in full.
  292. return blocksEqual(f.Blocks, other.Blocks)
  293. }
  294. // Xattrs is a convenience method to return the extended attributes of the
  295. // file for the current platform.
  296. func (f *PlatformData) Xattrs() []Xattr {
  297. switch {
  298. case build.IsLinux && f.Linux != nil:
  299. return f.Linux.Xattrs
  300. case build.IsDarwin && f.Darwin != nil:
  301. return f.Darwin.Xattrs
  302. case build.IsFreeBSD && f.FreeBSD != nil:
  303. return f.FreeBSD.Xattrs
  304. case build.IsNetBSD && f.NetBSD != nil:
  305. return f.NetBSD.Xattrs
  306. default:
  307. return nil
  308. }
  309. }
  310. // SetXattrs is a convenience method to set the extended attributes of the
  311. // file for the current platform.
  312. func (p *PlatformData) SetXattrs(xattrs []Xattr) {
  313. switch {
  314. case build.IsLinux:
  315. if p.Linux == nil {
  316. p.Linux = &XattrData{}
  317. }
  318. p.Linux.Xattrs = xattrs
  319. case build.IsDarwin:
  320. if p.Darwin == nil {
  321. p.Darwin = &XattrData{}
  322. }
  323. p.Darwin.Xattrs = xattrs
  324. case build.IsFreeBSD:
  325. if p.FreeBSD == nil {
  326. p.FreeBSD = &XattrData{}
  327. }
  328. p.FreeBSD.Xattrs = xattrs
  329. case build.IsNetBSD:
  330. if p.NetBSD == nil {
  331. p.NetBSD = &XattrData{}
  332. }
  333. p.NetBSD.Xattrs = xattrs
  334. }
  335. }
  336. // MergeWith copies platform data from other, for platforms where it's not
  337. // already set on p.
  338. func (p *PlatformData) MergeWith(other *PlatformData) {
  339. if p.Unix == nil {
  340. p.Unix = other.Unix
  341. }
  342. if p.Windows == nil {
  343. p.Windows = other.Windows
  344. }
  345. if p.Linux == nil {
  346. p.Linux = other.Linux
  347. }
  348. if p.Darwin == nil {
  349. p.Darwin = other.Darwin
  350. }
  351. if p.FreeBSD == nil {
  352. p.FreeBSD = other.FreeBSD
  353. }
  354. if p.NetBSD == nil {
  355. p.NetBSD = other.NetBSD
  356. }
  357. }
  358. // blocksEqual returns whether two slices of blocks are exactly the same hash
  359. // and index pair wise.
  360. func blocksEqual(a, b []BlockInfo) bool {
  361. if len(b) != len(a) {
  362. return false
  363. }
  364. for i, sblk := range a {
  365. if !bytes.Equal(sblk.Hash, b[i].Hash) {
  366. return false
  367. }
  368. }
  369. return true
  370. }
  371. func (f *FileInfo) SetMustRescan() {
  372. f.setLocalFlags(FlagLocalMustRescan)
  373. }
  374. func (f *FileInfo) SetIgnored() {
  375. f.setLocalFlags(FlagLocalIgnored)
  376. }
  377. func (f *FileInfo) SetUnsupported() {
  378. f.setLocalFlags(FlagLocalUnsupported)
  379. }
  380. func (f *FileInfo) SetDeleted(by ShortID) {
  381. f.ModifiedBy = by
  382. f.Deleted = true
  383. f.Version = f.Version.Update(by)
  384. f.ModifiedS = time.Now().Unix()
  385. f.setNoContent()
  386. }
  387. func (f *FileInfo) setLocalFlags(flags uint32) {
  388. f.RawInvalid = false
  389. f.LocalFlags = flags
  390. f.setNoContent()
  391. }
  392. func (f *FileInfo) setNoContent() {
  393. f.Blocks = nil
  394. f.BlocksHash = nil
  395. f.Size = 0
  396. }
  397. func (b BlockInfo) String() string {
  398. return fmt.Sprintf("Block{%d/%d/%d/%x}", b.Offset, b.Size, b.WeakHash, b.Hash)
  399. }
  400. // IsEmpty returns true if the block is a full block of zeroes.
  401. func (b BlockInfo) IsEmpty() bool {
  402. if v, ok := sha256OfEmptyBlock[int(b.Size)]; ok {
  403. return bytes.Equal(b.Hash, v[:])
  404. }
  405. return false
  406. }
  407. type IndexID uint64
  408. func (i IndexID) String() string {
  409. return fmt.Sprintf("0x%016X", uint64(i))
  410. }
  411. func (i IndexID) Marshal() ([]byte, error) {
  412. bs := make([]byte, 8)
  413. binary.BigEndian.PutUint64(bs, uint64(i))
  414. return bs, nil
  415. }
  416. func (i *IndexID) Unmarshal(bs []byte) error {
  417. if len(bs) != 8 {
  418. return errors.New("incorrect IndexID length")
  419. }
  420. *i = IndexID(binary.BigEndian.Uint64(bs))
  421. return nil
  422. }
  423. func NewIndexID() IndexID {
  424. return IndexID(rand.Uint64())
  425. }
  426. func (f Folder) Description() string {
  427. // used by logging stuff
  428. if f.Label == "" {
  429. return f.ID
  430. }
  431. return fmt.Sprintf("%q (%s)", f.Label, f.ID)
  432. }
  433. func BlocksHash(bs []BlockInfo) []byte {
  434. h := sha256.New()
  435. for _, b := range bs {
  436. _, _ = h.Write(b.Hash)
  437. _ = binary.Write(h, binary.BigEndian, b.WeakHash)
  438. }
  439. return h.Sum(nil)
  440. }
  441. func VectorHash(v Vector) []byte {
  442. h := sha256.New()
  443. for _, c := range v.Counters {
  444. if err := binary.Write(h, binary.BigEndian, c.ID); err != nil {
  445. panic("impossible: failed to write c.ID to hash function: " + err.Error())
  446. }
  447. if err := binary.Write(h, binary.BigEndian, c.Value); err != nil {
  448. panic("impossible: failed to write c.Value to hash function: " + err.Error())
  449. }
  450. }
  451. return h.Sum(nil)
  452. }
  453. func (x *FileInfoType) MarshalJSON() ([]byte, error) {
  454. return json.Marshal(x.String())
  455. }
  456. func (x *FileInfoType) UnmarshalJSON(data []byte) error {
  457. var s string
  458. if err := json.Unmarshal(data, &s); err != nil {
  459. return err
  460. }
  461. n, ok := FileInfoType_value[s]
  462. if !ok {
  463. return errors.New("invalid value: " + s)
  464. }
  465. *x = FileInfoType(n)
  466. return nil
  467. }
  468. func xattrsEqual(a, b *XattrData) bool {
  469. if a == nil || b == nil {
  470. // Having no data on either side means we have nothing to compare
  471. // to, and we consider that equal.
  472. return true
  473. }
  474. if len(a.Xattrs) != len(b.Xattrs) {
  475. return false
  476. }
  477. for i := range a.Xattrs {
  478. if a.Xattrs[i].Name != b.Xattrs[i].Name {
  479. return false
  480. }
  481. if !bytes.Equal(a.Xattrs[i].Value, b.Xattrs[i].Value) {
  482. return false
  483. }
  484. }
  485. return true
  486. }