casefs.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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. "sync"
  13. "time"
  14. lru "github.com/hashicorp/golang-lru"
  15. )
  16. const (
  17. // How long to consider cached dirnames valid
  18. caseCacheTimeout = time.Second
  19. caseCacheItemLimit = 4 << 10
  20. )
  21. type ErrCaseConflict struct {
  22. Given, Real string
  23. }
  24. func (e *ErrCaseConflict) Error() string {
  25. return fmt.Sprintf(`given name "%v" differs from name in filesystem "%v"`, e.Given, e.Real)
  26. }
  27. func IsErrCaseConflict(err error) bool {
  28. e := &ErrCaseConflict{}
  29. return errors.As(err, &e)
  30. }
  31. type realCaser interface {
  32. realCase(name string) (string, error)
  33. dropCache()
  34. }
  35. type fskey struct {
  36. fstype FilesystemType
  37. uri, opts string
  38. }
  39. // caseFilesystemRegistry caches caseFilesystems and runs a routine to drop
  40. // their cache every now and then.
  41. type caseFilesystemRegistry struct {
  42. fss map[fskey]*caseFilesystem
  43. mut sync.RWMutex
  44. startCleaner sync.Once
  45. }
  46. func newFSKey(fs Filesystem) fskey {
  47. k := fskey{
  48. fstype: fs.Type(),
  49. uri: fs.URI(),
  50. }
  51. if opts := fs.Options(); len(opts) > 0 {
  52. k.opts = opts[0].String()
  53. for _, o := range opts[1:] {
  54. k.opts += "&" + o.String()
  55. }
  56. }
  57. return k
  58. }
  59. func (r *caseFilesystemRegistry) get(fs Filesystem) Filesystem {
  60. k := newFSKey(fs)
  61. // Use double locking when getting a caseFs. In the common case it will
  62. // already exist and we take the read lock fast path. If it doesn't, we
  63. // take a write lock and try again.
  64. r.mut.RLock()
  65. caseFs, ok := r.fss[k]
  66. r.mut.RUnlock()
  67. if !ok {
  68. r.mut.Lock()
  69. caseFs, ok = r.fss[k]
  70. if !ok {
  71. caseFs = &caseFilesystem{
  72. Filesystem: fs,
  73. realCaser: newDefaultRealCaser(fs),
  74. }
  75. r.fss[k] = caseFs
  76. r.startCleaner.Do(func() {
  77. go r.cleaner()
  78. })
  79. }
  80. r.mut.Unlock()
  81. }
  82. return caseFs
  83. }
  84. func (r *caseFilesystemRegistry) cleaner() {
  85. for range time.NewTicker(time.Minute).C {
  86. // We need to not hold this lock for a long time, as it blocks
  87. // creating new filesystems in get(), which is needed to do things
  88. // like add new folders. The (*caseFs).dropCache() method can take
  89. // an arbitrarily long time to kick in because it in turn waits for
  90. // locks held by things performing I/O. So we can't call that from
  91. // within the loop.
  92. r.mut.RLock()
  93. toProcess := make([]*caseFilesystem, 0, len(r.fss))
  94. for _, caseFs := range r.fss {
  95. toProcess = append(toProcess, caseFs)
  96. }
  97. r.mut.RUnlock()
  98. for _, caseFs := range toProcess {
  99. caseFs.dropCache()
  100. }
  101. }
  102. }
  103. var globalCaseFilesystemRegistry = caseFilesystemRegistry{fss: make(map[fskey]*caseFilesystem)}
  104. // caseFilesystem is a BasicFilesystem with additional checks to make a
  105. // potentially case insensitive underlying FS behave like it's case-sensitive.
  106. type caseFilesystem struct {
  107. Filesystem
  108. realCaser
  109. }
  110. // NewCaseFilesystem ensures that the given, potentially case-insensitive filesystem
  111. // behaves like a case-sensitive filesystem. Meaning that it takes into account
  112. // the real casing of a path and returns ErrCaseConflict if the given path differs
  113. // from the real path. It is safe to use with any filesystem, i.e. also a
  114. // case-sensitive one. However it will add some overhead and thus shouldn't be
  115. // used if the filesystem is known to already behave case-sensitively.
  116. func NewCaseFilesystem(fs Filesystem) Filesystem {
  117. return wrapFilesystem(fs, globalCaseFilesystemRegistry.get)
  118. }
  119. func (f *caseFilesystem) Chmod(name string, mode FileMode) error {
  120. if err := f.checkCase(name); err != nil {
  121. return err
  122. }
  123. return f.Filesystem.Chmod(name, mode)
  124. }
  125. func (f *caseFilesystem) Lchown(name string, uid, gid int) error {
  126. if err := f.checkCase(name); err != nil {
  127. return err
  128. }
  129. return f.Filesystem.Lchown(name, uid, gid)
  130. }
  131. func (f *caseFilesystem) Chtimes(name string, atime time.Time, mtime time.Time) error {
  132. if err := f.checkCase(name); err != nil {
  133. return err
  134. }
  135. return f.Filesystem.Chtimes(name, atime, mtime)
  136. }
  137. func (f *caseFilesystem) Mkdir(name string, perm FileMode) error {
  138. if err := f.checkCase(name); err != nil {
  139. return err
  140. }
  141. if err := f.Filesystem.Mkdir(name, perm); err != nil {
  142. return err
  143. }
  144. f.dropCache()
  145. return nil
  146. }
  147. func (f *caseFilesystem) MkdirAll(path string, perm FileMode) error {
  148. if err := f.checkCase(path); err != nil {
  149. return err
  150. }
  151. if err := f.Filesystem.MkdirAll(path, perm); err != nil {
  152. return err
  153. }
  154. f.dropCache()
  155. return nil
  156. }
  157. func (f *caseFilesystem) Lstat(name string) (FileInfo, error) {
  158. var err error
  159. if name, err = Canonicalize(name); err != nil {
  160. return nil, err
  161. }
  162. stat, err := f.Filesystem.Lstat(name)
  163. if err != nil {
  164. return nil, err
  165. }
  166. if err = f.checkCaseExisting(name); err != nil {
  167. return nil, err
  168. }
  169. return stat, nil
  170. }
  171. func (f *caseFilesystem) Remove(name string) error {
  172. if err := f.checkCase(name); err != nil {
  173. return err
  174. }
  175. if err := f.Filesystem.Remove(name); err != nil {
  176. return err
  177. }
  178. f.dropCache()
  179. return nil
  180. }
  181. func (f *caseFilesystem) RemoveAll(name string) error {
  182. if err := f.checkCase(name); err != nil {
  183. return err
  184. }
  185. if err := f.Filesystem.RemoveAll(name); err != nil {
  186. return err
  187. }
  188. f.dropCache()
  189. return nil
  190. }
  191. func (f *caseFilesystem) Rename(oldpath, newpath string) error {
  192. if err := f.checkCase(oldpath); err != nil {
  193. return err
  194. }
  195. if err := f.checkCase(newpath); err != nil {
  196. // Case-only rename is ok
  197. e := &ErrCaseConflict{}
  198. if !errors.As(err, &e) || e.Real != oldpath {
  199. return err
  200. }
  201. }
  202. if err := f.Filesystem.Rename(oldpath, newpath); err != nil {
  203. return err
  204. }
  205. f.dropCache()
  206. return nil
  207. }
  208. func (f *caseFilesystem) Stat(name string) (FileInfo, error) {
  209. var err error
  210. if name, err = Canonicalize(name); err != nil {
  211. return nil, err
  212. }
  213. stat, err := f.Filesystem.Stat(name)
  214. if err != nil {
  215. return nil, err
  216. }
  217. if err = f.checkCaseExisting(name); err != nil {
  218. return nil, err
  219. }
  220. return stat, nil
  221. }
  222. func (f *caseFilesystem) DirNames(name string) ([]string, error) {
  223. if err := f.checkCase(name); err != nil {
  224. return nil, err
  225. }
  226. return f.Filesystem.DirNames(name)
  227. }
  228. func (f *caseFilesystem) Open(name string) (File, error) {
  229. if err := f.checkCase(name); err != nil {
  230. return nil, err
  231. }
  232. return f.Filesystem.Open(name)
  233. }
  234. func (f *caseFilesystem) OpenFile(name string, flags int, mode FileMode) (File, error) {
  235. if err := f.checkCase(name); err != nil {
  236. return nil, err
  237. }
  238. file, err := f.Filesystem.OpenFile(name, flags, mode)
  239. if err != nil {
  240. return nil, err
  241. }
  242. f.dropCache()
  243. return file, nil
  244. }
  245. func (f *caseFilesystem) ReadSymlink(name string) (string, error) {
  246. if err := f.checkCase(name); err != nil {
  247. return "", err
  248. }
  249. return f.Filesystem.ReadSymlink(name)
  250. }
  251. func (f *caseFilesystem) Create(name string) (File, error) {
  252. if err := f.checkCase(name); err != nil {
  253. return nil, err
  254. }
  255. file, err := f.Filesystem.Create(name)
  256. if err != nil {
  257. return nil, err
  258. }
  259. f.dropCache()
  260. return file, nil
  261. }
  262. func (f *caseFilesystem) CreateSymlink(target, name string) error {
  263. if err := f.checkCase(name); err != nil {
  264. return err
  265. }
  266. if err := f.Filesystem.CreateSymlink(target, name); err != nil {
  267. return err
  268. }
  269. f.dropCache()
  270. return nil
  271. }
  272. func (f *caseFilesystem) Walk(root string, walkFn WalkFunc) error {
  273. // Walking the filesystem is likely (in Syncthing's case certainly) done
  274. // to pick up external changes, for which caching is undesirable.
  275. f.dropCache()
  276. if err := f.checkCase(root); err != nil {
  277. return err
  278. }
  279. return f.Filesystem.Walk(root, walkFn)
  280. }
  281. func (f *caseFilesystem) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
  282. if err := f.checkCase(path); err != nil {
  283. return nil, nil, err
  284. }
  285. return f.Filesystem.Watch(path, ignore, ctx, ignorePerms)
  286. }
  287. func (f *caseFilesystem) Hide(name string) error {
  288. if err := f.checkCase(name); err != nil {
  289. return err
  290. }
  291. return f.Filesystem.Hide(name)
  292. }
  293. func (f *caseFilesystem) Unhide(name string) error {
  294. if err := f.checkCase(name); err != nil {
  295. return err
  296. }
  297. return f.Filesystem.Unhide(name)
  298. }
  299. func (f *caseFilesystem) checkCase(name string) error {
  300. var err error
  301. if name, err = Canonicalize(name); err != nil {
  302. return err
  303. }
  304. // Stat is necessary for case sensitive FS, as it's then not a conflict
  305. // if name is e.g. "foo" and on dir there is "Foo".
  306. if _, err := f.Filesystem.Lstat(name); err != nil {
  307. if IsNotExist(err) {
  308. return nil
  309. }
  310. return err
  311. }
  312. return f.checkCaseExisting(name)
  313. }
  314. // checkCaseExisting must only be called after successfully canonicalizing and
  315. // stating the file.
  316. func (f *caseFilesystem) checkCaseExisting(name string) error {
  317. realName, err := f.realCase(name)
  318. if IsNotExist(err) {
  319. // It did exist just before -> cache is outdated, try again
  320. f.dropCache()
  321. realName, err = f.realCase(name)
  322. }
  323. if err != nil {
  324. return err
  325. }
  326. if realName != name {
  327. return &ErrCaseConflict{name, realName}
  328. }
  329. return nil
  330. }
  331. type defaultRealCaser struct {
  332. fs Filesystem
  333. cache caseCache
  334. }
  335. func newDefaultRealCaser(fs Filesystem) *defaultRealCaser {
  336. cache, err := lru.New2Q(caseCacheItemLimit)
  337. // New2Q only errors if given invalid parameters, which we don't.
  338. if err != nil {
  339. panic(err)
  340. }
  341. caser := &defaultRealCaser{
  342. fs: fs,
  343. cache: caseCache{
  344. TwoQueueCache: cache,
  345. },
  346. }
  347. return caser
  348. }
  349. func (r *defaultRealCaser) realCase(name string) (string, error) {
  350. realName := "."
  351. if name == realName {
  352. return realName, nil
  353. }
  354. for _, comp := range PathComponents(name) {
  355. node := r.cache.getExpireAdd(realName)
  356. node.once.Do(func() {
  357. dirNames, err := r.fs.DirNames(realName)
  358. if err != nil {
  359. r.cache.Remove(realName)
  360. node.err = err
  361. return
  362. }
  363. num := len(dirNames)
  364. node.children = make(map[string]struct{}, num)
  365. node.lowerToReal = make(map[string]string, num)
  366. lastLower := ""
  367. for _, n := range dirNames {
  368. node.children[n] = struct{}{}
  369. lower := UnicodeLowercase(n)
  370. if lower != lastLower {
  371. node.lowerToReal[lower] = n
  372. lastLower = n
  373. }
  374. }
  375. })
  376. if node.err != nil {
  377. return "", node.err
  378. }
  379. // Try to find a direct or case match
  380. if _, ok := node.children[comp]; !ok {
  381. comp, ok = node.lowerToReal[UnicodeLowercase(comp)]
  382. if !ok {
  383. return "", ErrNotExist
  384. }
  385. }
  386. realName = filepath.Join(realName, comp)
  387. }
  388. return realName, nil
  389. }
  390. func (r *defaultRealCaser) dropCache() {
  391. r.cache.Purge()
  392. }
  393. func newCaseNode() *caseNode {
  394. return &caseNode{
  395. expires: time.Now().Add(caseCacheTimeout),
  396. }
  397. }
  398. // The keys to children are "real", case resolved names of the path
  399. // component this node represents (i.e. containing no path separator).
  400. // lowerToReal is a map of lowercase path components (as in UnicodeLowercase)
  401. // to their corresponding "real", case resolved names.
  402. // A node is created empty and populated using once. If an error occurs the node
  403. // is removed from cache and the error stored in err, such that anyone that
  404. // already got the node doesn't try to access the nil maps.
  405. type caseNode struct {
  406. expires time.Time
  407. lowerToReal map[string]string
  408. children map[string]struct{}
  409. once sync.Once
  410. err error
  411. }
  412. type caseCache struct {
  413. *lru.TwoQueueCache
  414. mut sync.Mutex
  415. }
  416. // getExpireAdd gets an entry for the given key. If no entry exists, or it is
  417. // expired a new one is created and added to the cache.
  418. func (c *caseCache) getExpireAdd(key string) *caseNode {
  419. c.mut.Lock()
  420. defer c.mut.Unlock()
  421. v, ok := c.Get(key)
  422. if !ok {
  423. node := newCaseNode()
  424. c.Add(key, node)
  425. return node
  426. }
  427. node := v.(*caseNode)
  428. if node.expires.Before(time.Now()) {
  429. node = newCaseNode()
  430. c.Add(key, node)
  431. }
  432. return node
  433. }