casefs.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. // Copyright (C) 2020 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. "fmt"
  11. "path/filepath"
  12. "strings"
  13. "sync"
  14. "time"
  15. )
  16. const (
  17. // How long to consider cached dirnames valid
  18. caseCacheTimeout = time.Second
  19. )
  20. type ErrCaseConflict struct {
  21. Given, Real string
  22. }
  23. func (e *ErrCaseConflict) Error() string {
  24. return fmt.Sprintf(`given name "%v" differs from name in filesystem "%v"`, e.Given, e.Real)
  25. }
  26. func IsErrCaseConflict(err error) bool {
  27. e := &ErrCaseConflict{}
  28. return errors.As(err, &e)
  29. }
  30. type realCaser interface {
  31. realCase(name string) (string, error)
  32. dropCache()
  33. }
  34. type fskey struct {
  35. fstype FilesystemType
  36. uri string
  37. }
  38. // caseFilesystemRegistry caches caseFilesystems and runs a routine to drop
  39. // their cache every now and then.
  40. type caseFilesystemRegistry struct {
  41. fss map[fskey]*caseFilesystem
  42. mut sync.Mutex
  43. startCleaner sync.Once
  44. }
  45. func (r *caseFilesystemRegistry) get(fs Filesystem) *caseFilesystem {
  46. r.mut.Lock()
  47. defer r.mut.Unlock()
  48. k := fskey{fs.Type(), fs.URI()}
  49. caseFs, ok := r.fss[k]
  50. if !ok {
  51. caseFs = &caseFilesystem{
  52. Filesystem: fs,
  53. realCaser: newDefaultRealCaser(fs),
  54. }
  55. r.fss[k] = caseFs
  56. r.startCleaner.Do(func() {
  57. go r.cleaner()
  58. })
  59. }
  60. return caseFs
  61. }
  62. func (r *caseFilesystemRegistry) cleaner() {
  63. for range time.NewTicker(time.Minute).C {
  64. r.mut.Lock()
  65. for _, caseFs := range r.fss {
  66. caseFs.dropCache()
  67. }
  68. r.mut.Unlock()
  69. }
  70. }
  71. var globalCaseFilesystemRegistry = caseFilesystemRegistry{fss: make(map[fskey]*caseFilesystem)}
  72. // caseFilesystem is a BasicFilesystem with additional checks to make a
  73. // potentially case insensitive underlying FS behave like it's case-sensitive.
  74. type caseFilesystem struct {
  75. Filesystem
  76. realCaser
  77. }
  78. // NewCaseFilesystem ensures that the given, potentially case-insensitive filesystem
  79. // behaves like a case-sensitive filesystem. Meaning that it takes into account
  80. // the real casing of a path and returns ErrCaseConflict if the given path differs
  81. // from the real path. It is safe to use with any filesystem, i.e. also a
  82. // case-sensitive one. However it will add some overhead and thus shouldn't be
  83. // used if the filesystem is known to already behave case-sensitively.
  84. func NewCaseFilesystem(fs Filesystem) Filesystem {
  85. return globalCaseFilesystemRegistry.get(fs)
  86. }
  87. func (f *caseFilesystem) Chmod(name string, mode FileMode) error {
  88. if err := f.checkCase(name); err != nil {
  89. return err
  90. }
  91. return f.Filesystem.Chmod(name, mode)
  92. }
  93. func (f *caseFilesystem) Lchown(name string, uid, gid int) error {
  94. if err := f.checkCase(name); err != nil {
  95. return err
  96. }
  97. return f.Filesystem.Lchown(name, uid, gid)
  98. }
  99. func (f *caseFilesystem) Chtimes(name string, atime time.Time, mtime time.Time) error {
  100. if err := f.checkCase(name); err != nil {
  101. return err
  102. }
  103. return f.Filesystem.Chtimes(name, atime, mtime)
  104. }
  105. func (f *caseFilesystem) Mkdir(name string, perm FileMode) error {
  106. if err := f.checkCase(name); err != nil {
  107. return err
  108. }
  109. if err := f.Filesystem.Mkdir(name, perm); err != nil {
  110. return err
  111. }
  112. f.dropCache()
  113. return nil
  114. }
  115. func (f *caseFilesystem) MkdirAll(path string, perm FileMode) error {
  116. if err := f.checkCase(path); err != nil {
  117. return err
  118. }
  119. if err := f.Filesystem.MkdirAll(path, perm); err != nil {
  120. return err
  121. }
  122. f.dropCache()
  123. return nil
  124. }
  125. func (f *caseFilesystem) Lstat(name string) (FileInfo, error) {
  126. var err error
  127. if name, err = Canonicalize(name); err != nil {
  128. return nil, err
  129. }
  130. stat, err := f.Filesystem.Lstat(name)
  131. if err != nil {
  132. return nil, err
  133. }
  134. if err = f.checkCaseExisting(name); err != nil {
  135. return nil, err
  136. }
  137. return stat, nil
  138. }
  139. func (f *caseFilesystem) Remove(name string) error {
  140. if err := f.checkCase(name); err != nil {
  141. return err
  142. }
  143. if err := f.Filesystem.Remove(name); err != nil {
  144. return err
  145. }
  146. f.dropCache()
  147. return nil
  148. }
  149. func (f *caseFilesystem) RemoveAll(name string) error {
  150. if err := f.checkCase(name); err != nil {
  151. return err
  152. }
  153. if err := f.Filesystem.RemoveAll(name); err != nil {
  154. return err
  155. }
  156. f.dropCache()
  157. return nil
  158. }
  159. func (f *caseFilesystem) Rename(oldpath, newpath string) error {
  160. if err := f.checkCase(oldpath); err != nil {
  161. return err
  162. }
  163. if err := f.Filesystem.Rename(oldpath, newpath); err != nil {
  164. return err
  165. }
  166. f.dropCache()
  167. return nil
  168. }
  169. func (f *caseFilesystem) Stat(name string) (FileInfo, error) {
  170. var err error
  171. if name, err = Canonicalize(name); err != nil {
  172. return nil, err
  173. }
  174. stat, err := f.Filesystem.Stat(name)
  175. if err != nil {
  176. return nil, err
  177. }
  178. if err = f.checkCaseExisting(name); err != nil {
  179. return nil, err
  180. }
  181. return stat, nil
  182. }
  183. func (f *caseFilesystem) DirNames(name string) ([]string, error) {
  184. if err := f.checkCase(name); err != nil {
  185. return nil, err
  186. }
  187. return f.Filesystem.DirNames(name)
  188. }
  189. func (f *caseFilesystem) Open(name string) (File, error) {
  190. if err := f.checkCase(name); err != nil {
  191. return nil, err
  192. }
  193. return f.Filesystem.Open(name)
  194. }
  195. func (f *caseFilesystem) OpenFile(name string, flags int, mode FileMode) (File, error) {
  196. if err := f.checkCase(name); err != nil {
  197. return nil, err
  198. }
  199. file, err := f.Filesystem.OpenFile(name, flags, mode)
  200. if err != nil {
  201. return nil, err
  202. }
  203. f.dropCache()
  204. return file, nil
  205. }
  206. func (f *caseFilesystem) ReadSymlink(name string) (string, error) {
  207. if err := f.checkCase(name); err != nil {
  208. return "", err
  209. }
  210. return f.Filesystem.ReadSymlink(name)
  211. }
  212. func (f *caseFilesystem) Create(name string) (File, error) {
  213. if err := f.checkCase(name); err != nil {
  214. return nil, err
  215. }
  216. file, err := f.Filesystem.Create(name)
  217. if err != nil {
  218. return nil, err
  219. }
  220. f.dropCache()
  221. return file, nil
  222. }
  223. func (f *caseFilesystem) CreateSymlink(target, name string) error {
  224. if err := f.checkCase(name); err != nil {
  225. return err
  226. }
  227. if err := f.Filesystem.CreateSymlink(target, name); err != nil {
  228. return err
  229. }
  230. f.dropCache()
  231. return nil
  232. }
  233. func (f *caseFilesystem) Walk(root string, walkFn WalkFunc) error {
  234. // Walking the filesystem is likely (in Syncthing's case certainly) done
  235. // to pick up external changes, for which caching is undesirable.
  236. f.dropCache()
  237. if err := f.checkCase(root); err != nil {
  238. return err
  239. }
  240. return f.Filesystem.Walk(root, walkFn)
  241. }
  242. func (f *caseFilesystem) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
  243. if err := f.checkCase(path); err != nil {
  244. return nil, nil, err
  245. }
  246. return f.Filesystem.Watch(path, ignore, ctx, ignorePerms)
  247. }
  248. func (f *caseFilesystem) Hide(name string) error {
  249. if err := f.checkCase(name); err != nil {
  250. return err
  251. }
  252. return f.Filesystem.Hide(name)
  253. }
  254. func (f *caseFilesystem) Unhide(name string) error {
  255. if err := f.checkCase(name); err != nil {
  256. return err
  257. }
  258. return f.Filesystem.Unhide(name)
  259. }
  260. func (f *caseFilesystem) checkCase(name string) error {
  261. var err error
  262. if name, err = Canonicalize(name); err != nil {
  263. return err
  264. }
  265. // Stat is necessary for case sensitive FS, as it's then not a conflict
  266. // if name is e.g. "foo" and on dir there is "Foo".
  267. if _, err := f.Filesystem.Lstat(name); err != nil {
  268. if IsNotExist(err) {
  269. return nil
  270. }
  271. return err
  272. }
  273. return f.checkCaseExisting(name)
  274. }
  275. // checkCaseExisting must only be called after successfully canonicalizing and
  276. // stating the file.
  277. func (f *caseFilesystem) checkCaseExisting(name string) error {
  278. realName, err := f.realCase(name)
  279. if IsNotExist(err) {
  280. // It did exist just before -> cache is outdated, try again
  281. f.dropCache()
  282. realName, err = f.realCase(name)
  283. }
  284. if err != nil {
  285. return err
  286. }
  287. if realName != name {
  288. return &ErrCaseConflict{name, realName}
  289. }
  290. return nil
  291. }
  292. type defaultRealCaser struct {
  293. fs Filesystem
  294. root *caseNode
  295. mut sync.RWMutex
  296. }
  297. func newDefaultRealCaser(fs Filesystem) *defaultRealCaser {
  298. caser := &defaultRealCaser{
  299. fs: fs,
  300. root: &caseNode{name: "."},
  301. }
  302. return caser
  303. }
  304. func (r *defaultRealCaser) realCase(name string) (string, error) {
  305. out := "."
  306. if name == out {
  307. return out, nil
  308. }
  309. r.mut.Lock()
  310. defer r.mut.Unlock()
  311. node := r.root
  312. for _, comp := range strings.Split(name, string(PathSeparator)) {
  313. if node.dirNames == nil || node.expires.Before(time.Now()) {
  314. // Haven't called DirNames yet, or the node has expired
  315. var err error
  316. node.dirNames, err = r.fs.DirNames(out)
  317. if err != nil {
  318. return "", err
  319. }
  320. node.dirNamesLower = make([]string, len(node.dirNames))
  321. for i, n := range node.dirNames {
  322. node.dirNamesLower[i] = UnicodeLowercase(n)
  323. }
  324. node.expires = time.Now().Add(caseCacheTimeout)
  325. node.child = nil
  326. }
  327. // If we don't already have a correct cached child, try to find it.
  328. if node.child == nil || node.child.name != comp {
  329. // Actually loop dirNames to search for a match.
  330. n, err := findCaseInsensitiveMatch(comp, node.dirNames, node.dirNamesLower)
  331. if err != nil {
  332. return "", err
  333. }
  334. node.child = &caseNode{name: n}
  335. }
  336. node = node.child
  337. out = filepath.Join(out, node.name)
  338. }
  339. return out, nil
  340. }
  341. func (r *defaultRealCaser) dropCache() {
  342. r.mut.Lock()
  343. r.root = &caseNode{name: "."}
  344. r.mut.Unlock()
  345. }
  346. // Both name and the key to children are "Real", case resolved names of the path
  347. // component this node represents (i.e. containing no path separator).
  348. // The key to results is also a path component, but as given to RealCase, not
  349. // case resolved.
  350. type caseNode struct {
  351. name string
  352. expires time.Time
  353. dirNames []string
  354. dirNamesLower []string
  355. child *caseNode
  356. }
  357. func findCaseInsensitiveMatch(name string, names, namesLower []string) (string, error) {
  358. lower := UnicodeLowercase(name)
  359. candidate := ""
  360. for i, n := range names {
  361. if n == name {
  362. return n, nil
  363. }
  364. if candidate == "" && namesLower[i] == lower {
  365. candidate = n
  366. }
  367. }
  368. if candidate == "" {
  369. return "", ErrNotExist
  370. }
  371. return candidate, nil
  372. }