bep_extensions.go 15 KB

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