sharedpullerstate.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. reused int // Number of blocks reused from temporary file
  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. copyTotal int // Total number of copy actions for the whole job
  35. pullTotal int // Total number of pull actions for the whole job
  36. copyNeeded int // Number of copy actions still pending
  37. pullNeeded int // Number of block pulls still pending
  38. copyOrigin int // Number of blocks copied from the original file
  39. closed bool // Set when the file has been closed
  40. mut sync.Mutex // Protects the above
  41. }
  42. // tempFile returns the fd for the temporary file, reusing an open fd
  43. // or creating the file as necessary.
  44. func (s *sharedPullerState) tempFile() (*os.File, error) {
  45. s.mut.Lock()
  46. defer s.mut.Unlock()
  47. // If we've already hit an error, return early
  48. if s.err != nil {
  49. return nil, s.err
  50. }
  51. // If the temp file is already open, return the file descriptor
  52. if s.fd != nil {
  53. return s.fd, nil
  54. }
  55. // Ensure that the parent directory is writable. This is
  56. // osutil.InWritableDir except we need to do more stuff so we duplicate it
  57. // here.
  58. dir := filepath.Dir(s.tempName)
  59. if info, err := os.Stat(dir); err != nil {
  60. s.earlyCloseLocked("dst stat dir", err)
  61. return nil, err
  62. } else if info.Mode()&04 == 0 {
  63. err := os.Chmod(dir, 0755)
  64. if err == nil {
  65. defer func() {
  66. err := os.Chmod(dir, info.Mode().Perm())
  67. if err != nil {
  68. panic(err)
  69. }
  70. }()
  71. }
  72. }
  73. // Attempt to create the temp file
  74. flags := os.O_WRONLY
  75. if s.reused == 0 {
  76. flags |= os.O_CREATE | os.O_EXCL
  77. }
  78. fd, err := os.OpenFile(s.tempName, flags, 0644)
  79. if err != nil {
  80. s.earlyCloseLocked("dst create", err)
  81. return nil, err
  82. }
  83. // Same fd will be used by all writers
  84. s.fd = fd
  85. return fd, nil
  86. }
  87. // sourceFile opens the existing source file for reading
  88. func (s *sharedPullerState) sourceFile() (*os.File, error) {
  89. s.mut.Lock()
  90. defer s.mut.Unlock()
  91. // If we've already hit an error, return early
  92. if s.err != nil {
  93. return nil, s.err
  94. }
  95. // Attempt to open the existing file
  96. fd, err := os.Open(s.realName)
  97. if err != nil {
  98. s.earlyCloseLocked("src open", err)
  99. return nil, err
  100. }
  101. return fd, nil
  102. }
  103. // earlyClose prints a warning message composed of the context and
  104. // error, and marks the sharedPullerState as failed. Is a no-op when called on
  105. // an already failed state.
  106. func (s *sharedPullerState) earlyClose(context string, err error) {
  107. s.mut.Lock()
  108. defer s.mut.Unlock()
  109. s.earlyCloseLocked(context, err)
  110. }
  111. func (s *sharedPullerState) earlyCloseLocked(context string, err error) {
  112. if s.err != nil {
  113. return
  114. }
  115. l.Infof("Puller (folder %q, file %q): %s: %v", s.folder, s.file.Name, context, err)
  116. s.err = err
  117. if s.fd != nil {
  118. s.fd.Close()
  119. os.Remove(s.tempName)
  120. }
  121. s.closed = true
  122. }
  123. func (s *sharedPullerState) failed() error {
  124. s.mut.Lock()
  125. defer s.mut.Unlock()
  126. return s.err
  127. }
  128. func (s *sharedPullerState) copyDone() {
  129. s.mut.Lock()
  130. s.copyNeeded--
  131. if debug {
  132. l.Debugln("sharedPullerState", s.folder, s.file.Name, "copyNeeded ->", s.copyNeeded)
  133. }
  134. s.mut.Unlock()
  135. }
  136. func (s *sharedPullerState) copiedFromOrigin() {
  137. s.mut.Lock()
  138. s.copyOrigin++
  139. s.mut.Unlock()
  140. }
  141. func (s *sharedPullerState) pullStarted() {
  142. s.mut.Lock()
  143. s.copyTotal--
  144. s.copyNeeded--
  145. s.pullTotal++
  146. s.pullNeeded++
  147. if debug {
  148. l.Debugln("sharedPullerState", s.folder, s.file.Name, "pullNeeded start ->", s.pullNeeded)
  149. }
  150. s.mut.Unlock()
  151. }
  152. func (s *sharedPullerState) pullDone() {
  153. s.mut.Lock()
  154. s.pullNeeded--
  155. if debug {
  156. l.Debugln("sharedPullerState", s.folder, s.file.Name, "pullNeeded done ->", s.pullNeeded)
  157. }
  158. s.mut.Unlock()
  159. }
  160. // finalClose atomically closes and returns closed status of a file. A true
  161. // first return value means the file was closed and should be finished, with
  162. // the error indicating the success or failure of the close. A false first
  163. // return value indicates the file is not ready to be closed, or is already
  164. // closed and should in either case not be finished off now.
  165. func (s *sharedPullerState) finalClose() (bool, error) {
  166. s.mut.Lock()
  167. defer s.mut.Unlock()
  168. if s.pullNeeded+s.copyNeeded != 0 {
  169. // Not done yet.
  170. return false, nil
  171. }
  172. if s.closed {
  173. // Already handled.
  174. return false, nil
  175. }
  176. s.closed = true
  177. if fd := s.fd; fd != nil {
  178. s.fd = nil
  179. return true, fd.Close()
  180. }
  181. return true, nil
  182. }