bep_extensions.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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, NumBlocks:%d, 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, len(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 care about either ownership or xattrs, are recording inode change
  210. // times and it changed, they are not equal.
  211. if !(comp.IgnoreOwnership && comp.IgnoreXattrs) && 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. if !comp.IgnoreOwnership && f.Platform != other.Platform {
  221. if !unixOwnershipEqual(f.Platform.Unix, other.Platform.Unix) {
  222. return false
  223. }
  224. if !windowsOwnershipEqual(f.Platform.Windows, other.Platform.Windows) {
  225. return false
  226. }
  227. }
  228. if !comp.IgnoreXattrs && f.Platform != other.Platform {
  229. if !xattrsEqual(f.Platform.Linux, other.Platform.Linux) {
  230. return false
  231. }
  232. if !xattrsEqual(f.Platform.Darwin, other.Platform.Darwin) {
  233. return false
  234. }
  235. if !xattrsEqual(f.Platform.FreeBSD, other.Platform.FreeBSD) {
  236. return false
  237. }
  238. if !xattrsEqual(f.Platform.NetBSD, other.Platform.NetBSD) {
  239. return false
  240. }
  241. }
  242. if !comp.IgnorePerms && !f.NoPermissions && !other.NoPermissions && !PermsEqual(f.Permissions, other.Permissions) {
  243. return false
  244. }
  245. switch f.Type {
  246. case FileInfoTypeFile:
  247. return f.Size == other.Size && ModTimeEqual(f.ModTime(), other.ModTime(), comp.ModTimeWindow) && (comp.IgnoreBlocks || f.BlocksEqual(other))
  248. case FileInfoTypeSymlink:
  249. return f.SymlinkTarget == other.SymlinkTarget
  250. case FileInfoTypeDirectory:
  251. return true
  252. }
  253. return false
  254. }
  255. func ModTimeEqual(a, b time.Time, modTimeWindow time.Duration) bool {
  256. if a.Equal(b) {
  257. return true
  258. }
  259. diff := a.Sub(b)
  260. if diff < 0 {
  261. diff *= -1
  262. }
  263. return diff < modTimeWindow
  264. }
  265. func PermsEqual(a, b uint32) bool {
  266. if build.IsWindows {
  267. // There is only writeable and read only, represented for user, group
  268. // and other equally. We only compare against user.
  269. return a&0600 == b&0600
  270. }
  271. // All bits count
  272. return a&0777 == b&0777
  273. }
  274. // BlocksEqual returns true when the two files have identical block lists.
  275. func (f FileInfo) BlocksEqual(other FileInfo) bool {
  276. // If both sides have blocks hashes and they match, we are good. If they
  277. // don't match still check individual block hashes to catch differences
  278. // in weak hashes only (e.g. after switching weak hash algo).
  279. if len(f.BlocksHash) > 0 && len(other.BlocksHash) > 0 && bytes.Equal(f.BlocksHash, other.BlocksHash) {
  280. return true
  281. }
  282. // Actually compare the block lists in full.
  283. return blocksEqual(f.Blocks, other.Blocks)
  284. }
  285. // Xattrs is a convenience method to return the extended attributes of the
  286. // file for the current platform.
  287. func (f *PlatformData) Xattrs() []Xattr {
  288. switch {
  289. case build.IsLinux && f.Linux != nil:
  290. return f.Linux.Xattrs
  291. case build.IsDarwin && f.Darwin != nil:
  292. return f.Darwin.Xattrs
  293. case build.IsFreeBSD && f.FreeBSD != nil:
  294. return f.FreeBSD.Xattrs
  295. case build.IsNetBSD && f.NetBSD != nil:
  296. return f.NetBSD.Xattrs
  297. default:
  298. return nil
  299. }
  300. }
  301. // SetXattrs is a convenience method to set the extended attributes of the
  302. // file for the current platform.
  303. func (p *PlatformData) SetXattrs(xattrs []Xattr) {
  304. switch {
  305. case build.IsLinux:
  306. if p.Linux == nil {
  307. p.Linux = &XattrData{}
  308. }
  309. p.Linux.Xattrs = xattrs
  310. case build.IsDarwin:
  311. if p.Darwin == nil {
  312. p.Darwin = &XattrData{}
  313. }
  314. p.Darwin.Xattrs = xattrs
  315. case build.IsFreeBSD:
  316. if p.FreeBSD == nil {
  317. p.FreeBSD = &XattrData{}
  318. }
  319. p.FreeBSD.Xattrs = xattrs
  320. case build.IsNetBSD:
  321. if p.NetBSD == nil {
  322. p.NetBSD = &XattrData{}
  323. }
  324. p.NetBSD.Xattrs = xattrs
  325. }
  326. }
  327. // MergeWith copies platform data from other, for platforms where it's not
  328. // already set on p.
  329. func (p *PlatformData) MergeWith(other *PlatformData) {
  330. if p.Unix == nil {
  331. p.Unix = other.Unix
  332. }
  333. if p.Windows == nil {
  334. p.Windows = other.Windows
  335. }
  336. if p.Linux == nil {
  337. p.Linux = other.Linux
  338. }
  339. if p.Darwin == nil {
  340. p.Darwin = other.Darwin
  341. }
  342. if p.FreeBSD == nil {
  343. p.FreeBSD = other.FreeBSD
  344. }
  345. if p.NetBSD == nil {
  346. p.NetBSD = other.NetBSD
  347. }
  348. }
  349. // blocksEqual returns whether two slices of blocks are exactly the same hash
  350. // and index pair wise.
  351. func blocksEqual(a, b []BlockInfo) bool {
  352. if len(b) != len(a) {
  353. return false
  354. }
  355. for i, sblk := range a {
  356. if !bytes.Equal(sblk.Hash, b[i].Hash) {
  357. return false
  358. }
  359. }
  360. return true
  361. }
  362. func (f *FileInfo) SetMustRescan() {
  363. f.setLocalFlags(FlagLocalMustRescan)
  364. }
  365. func (f *FileInfo) SetIgnored() {
  366. f.setLocalFlags(FlagLocalIgnored)
  367. }
  368. func (f *FileInfo) SetUnsupported() {
  369. f.setLocalFlags(FlagLocalUnsupported)
  370. }
  371. func (f *FileInfo) SetDeleted(by ShortID) {
  372. f.ModifiedBy = by
  373. f.Deleted = true
  374. f.Version = f.Version.Update(by)
  375. f.ModifiedS = time.Now().Unix()
  376. f.setNoContent()
  377. }
  378. func (f *FileInfo) setLocalFlags(flags uint32) {
  379. f.RawInvalid = false
  380. f.LocalFlags = flags
  381. f.setNoContent()
  382. }
  383. func (f *FileInfo) setNoContent() {
  384. f.Blocks = nil
  385. f.BlocksHash = nil
  386. f.Size = 0
  387. }
  388. func (b BlockInfo) String() string {
  389. return fmt.Sprintf("Block{%d/%d/%d/%x}", b.Offset, b.Size, b.WeakHash, b.Hash)
  390. }
  391. // IsEmpty returns true if the block is a full block of zeroes.
  392. func (b BlockInfo) IsEmpty() bool {
  393. if v, ok := sha256OfEmptyBlock[int(b.Size)]; ok {
  394. return bytes.Equal(b.Hash, v[:])
  395. }
  396. return false
  397. }
  398. type IndexID uint64
  399. func (i IndexID) String() string {
  400. return fmt.Sprintf("0x%016X", uint64(i))
  401. }
  402. func (i IndexID) Marshal() ([]byte, error) {
  403. bs := make([]byte, 8)
  404. binary.BigEndian.PutUint64(bs, uint64(i))
  405. return bs, nil
  406. }
  407. func (i *IndexID) Unmarshal(bs []byte) error {
  408. if len(bs) != 8 {
  409. return errors.New("incorrect IndexID length")
  410. }
  411. *i = IndexID(binary.BigEndian.Uint64(bs))
  412. return nil
  413. }
  414. func NewIndexID() IndexID {
  415. return IndexID(rand.Uint64())
  416. }
  417. func (f Folder) Description() string {
  418. // used by logging stuff
  419. if f.Label == "" {
  420. return f.ID
  421. }
  422. return fmt.Sprintf("%q (%s)", f.Label, f.ID)
  423. }
  424. func BlocksHash(bs []BlockInfo) []byte {
  425. h := sha256.New()
  426. for _, b := range bs {
  427. _, _ = h.Write(b.Hash)
  428. _ = binary.Write(h, binary.BigEndian, b.WeakHash)
  429. }
  430. return h.Sum(nil)
  431. }
  432. func VectorHash(v Vector) []byte {
  433. h := sha256.New()
  434. for _, c := range v.Counters {
  435. if err := binary.Write(h, binary.BigEndian, c.ID); err != nil {
  436. panic("impossible: failed to write c.ID to hash function: " + err.Error())
  437. }
  438. if err := binary.Write(h, binary.BigEndian, c.Value); err != nil {
  439. panic("impossible: failed to write c.Value to hash function: " + err.Error())
  440. }
  441. }
  442. return h.Sum(nil)
  443. }
  444. func (x *FileInfoType) MarshalJSON() ([]byte, error) {
  445. return json.Marshal(x.String())
  446. }
  447. func (x *FileInfoType) UnmarshalJSON(data []byte) error {
  448. var s string
  449. if err := json.Unmarshal(data, &s); err != nil {
  450. return err
  451. }
  452. n, ok := FileInfoType_value[s]
  453. if !ok {
  454. return errors.New("invalid value: " + s)
  455. }
  456. *x = FileInfoType(n)
  457. return nil
  458. }
  459. func xattrsEqual(a, b *XattrData) bool {
  460. aEmpty := a == nil || len(a.Xattrs) == 0
  461. bEmpty := b == nil || len(b.Xattrs) == 0
  462. if aEmpty && bEmpty {
  463. return true
  464. }
  465. if aEmpty || bEmpty {
  466. // Only one side is empty, so they can't be equal.
  467. return false
  468. }
  469. if len(a.Xattrs) != len(b.Xattrs) {
  470. return false
  471. }
  472. for i := range a.Xattrs {
  473. if a.Xattrs[i].Name != b.Xattrs[i].Name {
  474. return false
  475. }
  476. if !bytes.Equal(a.Xattrs[i].Value, b.Xattrs[i].Value) {
  477. return false
  478. }
  479. }
  480. return true
  481. }
  482. func unixOwnershipEqual(a, b *UnixData) bool {
  483. if a == nil && b == nil {
  484. return true
  485. }
  486. if a == nil || b == nil {
  487. return false
  488. }
  489. return a.UID == b.UID && a.GID == b.GID && a.OwnerName == b.OwnerName && a.GroupName == b.GroupName
  490. }
  491. func windowsOwnershipEqual(a, b *WindowsData) bool {
  492. if a == nil && b == nil {
  493. return true
  494. }
  495. if a == nil || b == nil {
  496. return false
  497. }
  498. return a.OwnerName == b.OwnerName && a.OwnerIsGroup == b.OwnerIsGroup
  499. }