sharedpullerstate.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. //
  3. // This program is free software: you can redistribute it and/or modify it
  4. // under the terms of the GNU General Public License as published by the Free
  5. // Software Foundation, either version 3 of the License, or (at your option)
  6. // any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful, but WITHOUT
  9. // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. // more details.
  12. //
  13. // You should have received a copy of the GNU General Public License along
  14. // with this program. If not, see <http://www.gnu.org/licenses/>.
  15. package model
  16. import (
  17. "os"
  18. "path/filepath"
  19. "sync"
  20. "github.com/syncthing/syncthing/internal/protocol"
  21. )
  22. // A sharedPullerState is kept for each file that is being synced and is kept
  23. // updated along the way.
  24. type sharedPullerState struct {
  25. // Immutable, does not require locking
  26. file protocol.FileInfo
  27. folder string
  28. tempName string
  29. realName string
  30. reuse bool
  31. // Mutable, must be locked for access
  32. err error // The first error we hit
  33. fd *os.File // The fd of the temp file
  34. copyNeeded int // Number of copy actions we expect to happen
  35. pullNeeded int // Number of block pulls we expect to happen
  36. closed bool // Set when the file has been closed
  37. mut sync.Mutex // Protects the above
  38. }
  39. // tempFile returns the fd for the temporary file, reusing an open fd
  40. // or creating the file as necessary.
  41. func (s *sharedPullerState) tempFile() (*os.File, error) {
  42. s.mut.Lock()
  43. defer s.mut.Unlock()
  44. // If we've already hit an error, return early
  45. if s.err != nil {
  46. return nil, s.err
  47. }
  48. // If the temp file is already open, return the file descriptor
  49. if s.fd != nil {
  50. return s.fd, nil
  51. }
  52. // Ensure that the parent directory is writable. This is
  53. // osutil.InWritableDir except we need to do more stuff so we duplicate it
  54. // here.
  55. dir := filepath.Dir(s.tempName)
  56. if info, err := os.Stat(dir); err != nil {
  57. s.earlyCloseLocked("dst stat dir", err)
  58. return nil, err
  59. } else if info.Mode()&04 == 0 {
  60. err := os.Chmod(dir, 0755)
  61. if err == nil {
  62. defer func() {
  63. err := os.Chmod(dir, info.Mode().Perm())
  64. if err != nil {
  65. panic(err)
  66. }
  67. }()
  68. }
  69. }
  70. // Attempt to create the temp file
  71. flags := os.O_WRONLY
  72. if !s.reuse {
  73. flags |= os.O_CREATE | os.O_EXCL
  74. }
  75. fd, err := os.OpenFile(s.tempName, flags, 0644)
  76. if err != nil {
  77. s.earlyCloseLocked("dst create", err)
  78. return nil, err
  79. }
  80. // Same fd will be used by all writers
  81. s.fd = fd
  82. return fd, nil
  83. }
  84. // sourceFile opens the existing source file for reading
  85. func (s *sharedPullerState) sourceFile() (*os.File, error) {
  86. s.mut.Lock()
  87. defer s.mut.Unlock()
  88. // If we've already hit an error, return early
  89. if s.err != nil {
  90. return nil, s.err
  91. }
  92. // Attempt to open the existing file
  93. fd, err := os.Open(s.realName)
  94. if err != nil {
  95. s.earlyCloseLocked("src open", err)
  96. return nil, err
  97. }
  98. return fd, nil
  99. }
  100. // earlyClose prints a warning message composed of the context and
  101. // error, and marks the sharedPullerState as failed. Is a no-op when called on
  102. // an already failed state.
  103. func (s *sharedPullerState) earlyClose(context string, err error) {
  104. s.mut.Lock()
  105. defer s.mut.Unlock()
  106. s.earlyCloseLocked(context, err)
  107. }
  108. func (s *sharedPullerState) earlyCloseLocked(context string, err error) {
  109. if s.err != nil {
  110. return
  111. }
  112. l.Infof("Puller (folder %q, file %q): %s: %v", s.folder, s.file.Name, context, err)
  113. s.err = err
  114. if s.fd != nil {
  115. s.fd.Close()
  116. os.Remove(s.tempName)
  117. }
  118. s.closed = true
  119. }
  120. func (s *sharedPullerState) failed() error {
  121. s.mut.Lock()
  122. defer s.mut.Unlock()
  123. return s.err
  124. }
  125. func (s *sharedPullerState) copyDone() {
  126. s.mut.Lock()
  127. s.copyNeeded--
  128. if debug {
  129. l.Debugln("sharedPullerState", s.folder, s.file.Name, "copyNeeded ->", s.pullNeeded)
  130. }
  131. s.mut.Unlock()
  132. }
  133. func (s *sharedPullerState) pullDone() {
  134. s.mut.Lock()
  135. s.pullNeeded--
  136. if debug {
  137. l.Debugln("sharedPullerState", s.folder, s.file.Name, "pullNeeded ->", s.pullNeeded)
  138. }
  139. s.mut.Unlock()
  140. }
  141. // finalClose atomically closes and returns closed status of a file. A true
  142. // first return value means the file was closed and should be finished, with
  143. // the error indicating the success or failure of the close. A false first
  144. // return value indicates the file is not ready to be closed, or is already
  145. // closed and should in either case not be finished off now.
  146. func (s *sharedPullerState) finalClose() (bool, error) {
  147. s.mut.Lock()
  148. defer s.mut.Unlock()
  149. if s.pullNeeded+s.copyNeeded != 0 {
  150. // Not done yet.
  151. return false, nil
  152. }
  153. if s.closed {
  154. // Already handled.
  155. return false, nil
  156. }
  157. s.closed = true
  158. if fd := s.fd; fd != nil {
  159. s.fd = nil
  160. return true, fd.Close()
  161. }
  162. return true, nil
  163. }