basicfs_xattr_unix.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. "sync"
  14. "syscall"
  15. "github.com/syncthing/syncthing/lib/protocol"
  16. "golang.org/x/sys/unix"
  17. )
  18. func (f *BasicFilesystem) GetXattr(path string, xattrFilter XattrFilter) ([]protocol.Xattr, error) {
  19. path, err := f.rooted(path)
  20. if err != nil {
  21. return nil, fmt.Errorf("get xattr %s: %w", path, err)
  22. }
  23. attrs, err := listXattr(path)
  24. if err != nil {
  25. return nil, fmt.Errorf("get xattr %s: %w", path, err)
  26. }
  27. res := make([]protocol.Xattr, 0, len(attrs))
  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, err := getXattr(path, attr)
  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. var xattrBufPool = sync.Pool{
  60. New: func() any { return make([]byte, 1024) },
  61. }
  62. func getXattr(path, name string) ([]byte, error) {
  63. buf := xattrBufPool.Get().([]byte) //nolint:forcetypeassert
  64. defer func() {
  65. // Put the buffer back in the pool, or not if we're not supposed to
  66. // (we returned it to the caller).
  67. if buf != nil {
  68. xattrBufPool.Put(buf)
  69. }
  70. }()
  71. size, err := unix.Lgetxattr(path, name, buf)
  72. if errors.Is(err, unix.ERANGE) {
  73. // Buffer was too small. Figure out how large it needs to be, and
  74. // allocate.
  75. size, err = unix.Lgetxattr(path, name, nil)
  76. if err != nil {
  77. return nil, fmt.Errorf("Lgetxattr %s %q: %w", path, name, err)
  78. }
  79. if size > len(buf) {
  80. xattrBufPool.Put(buf)
  81. buf = make([]byte, size)
  82. }
  83. size, err = unix.Lgetxattr(path, name, buf)
  84. }
  85. if err != nil {
  86. return nil, fmt.Errorf("Lgetxattr %s %q: %w", path, name, err)
  87. }
  88. if size >= len(buf)/4*3 {
  89. // The buffer is adequately sized (at least three quarters of it is
  90. // used), return it as-is.
  91. val := buf[:size]
  92. buf = nil // Don't put it back in the pool.
  93. return val, nil
  94. }
  95. // The buffer is larger than required, copy the data to a new buffer of
  96. // the correct size. This avoids having lots of 1024-sized allocations
  97. // sticking around when 24 bytes or whatever would be enough.
  98. val := make([]byte, size)
  99. copy(val, buf)
  100. return val, nil
  101. }
  102. func (f *BasicFilesystem) SetXattr(path string, xattrs []protocol.Xattr, xattrFilter XattrFilter) error {
  103. // Index the new attribute set.
  104. xattrsIdx := make(map[string]int)
  105. for i, xa := range xattrs {
  106. xattrsIdx[xa.Name] = i
  107. }
  108. // Get and index the existing attribute set
  109. current, err := f.GetXattr(path, xattrFilter)
  110. if err != nil {
  111. return fmt.Errorf("set xattrs %s: GetXattr: %w", path, err)
  112. }
  113. currentIdx := make(map[string]int)
  114. for i, xa := range current {
  115. currentIdx[xa.Name] = i
  116. }
  117. path, err = f.rooted(path)
  118. if err != nil {
  119. return fmt.Errorf("set xattrs %s: %w", path, err)
  120. }
  121. // Remove all existing xattrs that are not in the new set
  122. for _, xa := range current {
  123. if _, ok := xattrsIdx[xa.Name]; !ok {
  124. if err := unix.Lremovexattr(path, xa.Name); err != nil {
  125. return fmt.Errorf("set xattrs %s: Removexattr %q: %w", path, xa.Name, err)
  126. }
  127. }
  128. }
  129. // Set all xattrs that are different in the new set
  130. for _, xa := range xattrs {
  131. if old, ok := currentIdx[xa.Name]; ok && bytes.Equal(xa.Value, current[old].Value) {
  132. continue
  133. }
  134. if err := unix.Lsetxattr(path, xa.Name, xa.Value, 0); err != nil {
  135. return fmt.Errorf("set xattrs %s: Setxattr %q: %w", path, xa.Name, err)
  136. }
  137. }
  138. return nil
  139. }
  140. func compact(ss []string) []string {
  141. i := 0
  142. for _, s := range ss {
  143. if s != "" {
  144. ss[i] = s
  145. i++
  146. }
  147. }
  148. return ss[:i]
  149. }