filesystem.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright (C) 2016 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. package fs
  7. import (
  8. "context"
  9. "errors"
  10. "io"
  11. "os"
  12. "path/filepath"
  13. "strings"
  14. "time"
  15. )
  16. // The Filesystem interface abstracts access to the file system.
  17. type Filesystem interface {
  18. Chmod(name string, mode FileMode) error
  19. Lchown(name string, uid, gid int) error
  20. Chtimes(name string, atime time.Time, mtime time.Time) error
  21. Create(name string) (File, error)
  22. CreateSymlink(target, name string) error
  23. DirNames(name string) ([]string, error)
  24. Lstat(name string) (FileInfo, error)
  25. Mkdir(name string, perm FileMode) error
  26. MkdirAll(name string, perm FileMode) error
  27. Open(name string) (File, error)
  28. OpenFile(name string, flags int, mode FileMode) (File, error)
  29. ReadSymlink(name string) (string, error)
  30. Remove(name string) error
  31. RemoveAll(name string) error
  32. Rename(oldname, newname string) error
  33. Stat(name string) (FileInfo, error)
  34. SymlinksSupported() bool
  35. Walk(name string, walkFn WalkFunc) error
  36. Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, error)
  37. Hide(name string) error
  38. Unhide(name string) error
  39. Glob(pattern string) ([]string, error)
  40. Roots() ([]string, error)
  41. Usage(name string) (Usage, error)
  42. Type() FilesystemType
  43. URI() string
  44. SameFile(fi1, fi2 FileInfo) bool
  45. }
  46. // The File interface abstracts access to a regular file, being a somewhat
  47. // smaller interface than os.File
  48. type File interface {
  49. io.Closer
  50. io.Reader
  51. io.ReaderAt
  52. io.Seeker
  53. io.Writer
  54. io.WriterAt
  55. Name() string
  56. Truncate(size int64) error
  57. Stat() (FileInfo, error)
  58. Sync() error
  59. }
  60. // The FileInfo interface is almost the same as os.FileInfo, but with the
  61. // Sys method removed (as we don't want to expose whatever is underlying)
  62. // and with a couple of convenience methods added.
  63. type FileInfo interface {
  64. // Standard things present in os.FileInfo
  65. Name() string
  66. Mode() FileMode
  67. Size() int64
  68. ModTime() time.Time
  69. IsDir() bool
  70. // Extensions
  71. IsRegular() bool
  72. IsSymlink() bool
  73. Owner() int
  74. Group() int
  75. }
  76. // FileMode is similar to os.FileMode
  77. type FileMode uint32
  78. func (fm FileMode) String() string {
  79. return os.FileMode(fm).String()
  80. }
  81. // Usage represents filesystem space usage
  82. type Usage struct {
  83. Free int64
  84. Total int64
  85. }
  86. type Matcher interface {
  87. ShouldIgnore(name string) bool
  88. SkipIgnoredDirs() bool
  89. }
  90. type MatchResult interface {
  91. IsIgnored() bool
  92. }
  93. type Event struct {
  94. Name string
  95. Type EventType
  96. }
  97. type EventType int
  98. const (
  99. NonRemove EventType = 1 + iota
  100. Remove
  101. Mixed // Should probably not be necessary to be used in filesystem interface implementation
  102. )
  103. // Merge returns Mixed, except if evType and other are the same and not Mixed.
  104. func (evType EventType) Merge(other EventType) EventType {
  105. return evType | other
  106. }
  107. func (evType EventType) String() string {
  108. switch {
  109. case evType == NonRemove:
  110. return "non-remove"
  111. case evType == Remove:
  112. return "remove"
  113. case evType == Mixed:
  114. return "mixed"
  115. default:
  116. panic("bug: Unknown event type")
  117. }
  118. }
  119. var ErrWatchNotSupported = errors.New("watching is not supported")
  120. // Equivalents from os package.
  121. const ModePerm = FileMode(os.ModePerm)
  122. const ModeSetgid = FileMode(os.ModeSetgid)
  123. const ModeSetuid = FileMode(os.ModeSetuid)
  124. const ModeSticky = FileMode(os.ModeSticky)
  125. const ModeSymlink = FileMode(os.ModeSymlink)
  126. const ModeType = FileMode(os.ModeType)
  127. const PathSeparator = os.PathSeparator
  128. const OptAppend = os.O_APPEND
  129. const OptCreate = os.O_CREATE
  130. const OptExclusive = os.O_EXCL
  131. const OptReadOnly = os.O_RDONLY
  132. const OptReadWrite = os.O_RDWR
  133. const OptSync = os.O_SYNC
  134. const OptTruncate = os.O_TRUNC
  135. const OptWriteOnly = os.O_WRONLY
  136. // SkipDir is used as a return value from WalkFuncs to indicate that
  137. // the directory named in the call is to be skipped. It is not returned
  138. // as an error by any function.
  139. var SkipDir = filepath.SkipDir
  140. // IsExist is the equivalent of os.IsExist
  141. var IsExist = os.IsExist
  142. // IsNotExist is the equivalent of os.IsNotExist
  143. var IsNotExist = os.IsNotExist
  144. // IsPermission is the equivalent of os.IsPermission
  145. var IsPermission = os.IsPermission
  146. // IsPathSeparator is the equivalent of os.IsPathSeparator
  147. var IsPathSeparator = os.IsPathSeparator
  148. func NewFilesystem(fsType FilesystemType, uri string) Filesystem {
  149. var fs Filesystem
  150. switch fsType {
  151. case FilesystemTypeBasic:
  152. fs = newBasicFilesystem(uri)
  153. case FilesystemTypeFake:
  154. fs = newFakeFilesystem(uri)
  155. default:
  156. l.Debugln("Unknown filesystem", fsType, uri)
  157. fs = &errorFilesystem{
  158. fsType: fsType,
  159. uri: uri,
  160. err: errors.New("filesystem with type " + fsType.String() + " does not exist."),
  161. }
  162. }
  163. if l.ShouldDebug("walkfs") {
  164. return NewWalkFilesystem(&logFilesystem{fs})
  165. }
  166. if l.ShouldDebug("fs") {
  167. return &logFilesystem{NewWalkFilesystem(fs)}
  168. }
  169. return NewWalkFilesystem(fs)
  170. }
  171. // IsInternal returns true if the file, as a path relative to the folder
  172. // root, represents an internal file that should always be ignored. The file
  173. // path must be clean (i.e., in canonical shortest form).
  174. func IsInternal(file string) bool {
  175. // fs cannot import config, so we hard code .stfolder here (config.DefaultMarkerName)
  176. internals := []string{".stfolder", ".stignore", ".stversions"}
  177. for _, internal := range internals {
  178. if file == internal {
  179. return true
  180. }
  181. if IsParent(file, internal) {
  182. return true
  183. }
  184. }
  185. return false
  186. }
  187. // Canonicalize checks that the file path is valid and returns it in the "canonical" form:
  188. // - /foo/bar -> foo/bar
  189. // - / -> "."
  190. func Canonicalize(file string) (string, error) {
  191. pathSep := string(PathSeparator)
  192. if strings.HasPrefix(file, pathSep+pathSep) {
  193. // The relative path may pretend to be an absolute path within
  194. // the root, but the double path separator on Windows implies
  195. // something else and is out of spec.
  196. return "", ErrNotRelative
  197. }
  198. // The relative path should be clean from internal dotdots and similar
  199. // funkyness.
  200. file = filepath.Clean(file)
  201. // It is not acceptable to attempt to traverse upwards.
  202. switch file {
  203. case "..":
  204. return "", ErrNotRelative
  205. }
  206. if strings.HasPrefix(file, ".."+pathSep) {
  207. return "", ErrNotRelative
  208. }
  209. if strings.HasPrefix(file, pathSep) {
  210. if file == pathSep {
  211. return ".", nil
  212. }
  213. return file[1:], nil
  214. }
  215. return file, nil
  216. }