basicfs_xattr_unix.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // Copyright (C) 2022 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. //go:build !windows && !dragonfly && !illumos && !solaris && !openbsd
  7. // +build !windows,!dragonfly,!illumos,!solaris,!openbsd
  8. package fs
  9. import (
  10. "bytes"
  11. "errors"
  12. "fmt"
  13. "syscall"
  14. "github.com/syncthing/syncthing/lib/protocol"
  15. "golang.org/x/sys/unix"
  16. )
  17. func (f *BasicFilesystem) GetXattr(path string, xattrFilter XattrFilter) ([]protocol.Xattr, error) {
  18. path, err := f.rooted(path)
  19. if err != nil {
  20. return nil, fmt.Errorf("get xattr %s: %w", path, err)
  21. }
  22. attrs, err := listXattr(path)
  23. if err != nil {
  24. return nil, fmt.Errorf("get xattr %s: %w", path, err)
  25. }
  26. res := make([]protocol.Xattr, 0, len(attrs))
  27. var val, buf []byte
  28. var totSize int
  29. for _, attr := range attrs {
  30. if !xattrFilter.Permit(attr) {
  31. l.Debugf("get xattr %s: skipping attribute %q denied by filter", path, attr)
  32. continue
  33. }
  34. val, buf, err = getXattr(path, attr, buf)
  35. var errNo syscall.Errno
  36. if errors.As(err, &errNo) && errNo == 0x5d {
  37. // ENOATTR, returned on BSD when asking for an attribute that
  38. // doesn't exist (any more?)
  39. continue
  40. } else if err != nil {
  41. return nil, fmt.Errorf("get xattr %s: %w", path, err)
  42. }
  43. if max := xattrFilter.GetMaxSingleEntrySize(); max > 0 && len(attr)+len(val) > max {
  44. l.Debugf("get xattr %s: attribute %q exceeds max size", path, attr)
  45. continue
  46. }
  47. totSize += len(attr) + len(val)
  48. if max := xattrFilter.GetMaxTotalSize(); max > 0 && totSize > max {
  49. l.Debugf("get xattr %s: attribute %q would cause max size to be exceeded", path, attr)
  50. continue
  51. }
  52. res = append(res, protocol.Xattr{
  53. Name: attr,
  54. Value: val,
  55. })
  56. }
  57. return res, nil
  58. }
  59. func getXattr(path, name string, buf []byte) (val []byte, rest []byte, err error) {
  60. if len(buf) == 0 {
  61. buf = make([]byte, 1024)
  62. }
  63. size, err := unix.Lgetxattr(path, name, buf)
  64. if errors.Is(err, unix.ERANGE) {
  65. // Buffer was too small. Figure out how large it needs to be, and
  66. // allocate.
  67. size, err = unix.Lgetxattr(path, name, nil)
  68. if err != nil {
  69. return nil, nil, fmt.Errorf("Lgetxattr %s %q: %w", path, name, err)
  70. }
  71. if size > len(buf) {
  72. buf = make([]byte, size)
  73. }
  74. size, err = unix.Lgetxattr(path, name, buf)
  75. }
  76. if err != nil {
  77. return nil, buf, fmt.Errorf("Lgetxattr %s %q: %w", path, name, err)
  78. }
  79. return buf[:size], buf[size:], nil
  80. }
  81. func (f *BasicFilesystem) SetXattr(path string, xattrs []protocol.Xattr, xattrFilter XattrFilter) error {
  82. // Index the new attribute set.
  83. xattrsIdx := make(map[string]int)
  84. for i, xa := range xattrs {
  85. xattrsIdx[xa.Name] = i
  86. }
  87. // Get and index the existing attribute set
  88. current, err := f.GetXattr(path, xattrFilter)
  89. if err != nil {
  90. return fmt.Errorf("set xattrs %s: GetXattr: %w", path, err)
  91. }
  92. currentIdx := make(map[string]int)
  93. for i, xa := range current {
  94. currentIdx[xa.Name] = i
  95. }
  96. path, err = f.rooted(path)
  97. if err != nil {
  98. return fmt.Errorf("set xattrs %s: %w", path, err)
  99. }
  100. // Remove all existing xattrs that are not in the new set
  101. for _, xa := range current {
  102. if _, ok := xattrsIdx[xa.Name]; !ok {
  103. if err := unix.Lremovexattr(path, xa.Name); err != nil {
  104. return fmt.Errorf("set xattrs %s: Removexattr %q: %w", path, xa.Name, err)
  105. }
  106. }
  107. }
  108. // Set all xattrs that are different in the new set
  109. for _, xa := range xattrs {
  110. if old, ok := currentIdx[xa.Name]; ok && bytes.Equal(xa.Value, current[old].Value) {
  111. continue
  112. }
  113. if err := unix.Lsetxattr(path, xa.Name, xa.Value, 0); err != nil {
  114. return fmt.Errorf("set xattrs %s: Setxattr %q: %w", path, xa.Name, err)
  115. }
  116. }
  117. return nil
  118. }
  119. func compact(ss []string) []string {
  120. i := 0
  121. for _, s := range ss {
  122. if s != "" {
  123. ss[i] = s
  124. i++
  125. }
  126. }
  127. return ss[:i]
  128. }