basicfs_windows_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright (C) 2018 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. //go:build windows
  7. // +build windows
  8. package fs
  9. import (
  10. "os"
  11. "path/filepath"
  12. "runtime"
  13. "strings"
  14. "syscall"
  15. "testing"
  16. )
  17. func TestWindowsPaths(t *testing.T) {
  18. type testCase struct {
  19. input string
  20. expectedRoot string
  21. expectedURI string
  22. }
  23. testCases := []testCase{
  24. {`e:`, `\\?\e:\`, `e:\`},
  25. {`e:\`, `\\?\e:\`, `e:\`},
  26. {`e:\\`, `\\?\e:\`, `e:\`},
  27. {`\\?\e:`, `\\?\e:\`, `e:\`},
  28. {`\\?\e:\`, `\\?\e:\`, `e:\`},
  29. {`\\?\e:\\`, `\\?\e:\`, `e:\`},
  30. {`e:\x`, `\\?\e:\x`, `e:\x`},
  31. {`e:\x\`, `\\?\e:\x`, `e:\x`},
  32. {`e:\x\\`, `\\?\e:\x`, `e:\x`},
  33. {`\\192.0.2.22\network\share`, `\\192.0.2.22\network\share`, `\\192.0.2.22\network\share`},
  34. }
  35. if runtime.Version() >= "go1.20" {
  36. testCases = append(testCases,
  37. testCase{`\\.\e:`, `\\.\e:\`, `e:\`},
  38. testCase{`\\.\e:\`, `\\.\e:\`, `e:\`},
  39. testCase{`\\.\e:\\`, `\\.\e:\`, `e:\`},
  40. )
  41. }
  42. for i, testCase := range testCases {
  43. fs := newBasicFilesystem(testCase.input)
  44. if fs.root != testCase.expectedRoot {
  45. t.Errorf("test %d: root: expected `%s`, got `%s`", i, testCase.expectedRoot, fs.root)
  46. }
  47. if fs.URI() != testCase.expectedURI {
  48. t.Errorf("test %d: uri: expected `%s`, got `%s`", i, testCase.expectedURI, fs.URI())
  49. }
  50. }
  51. fs := newBasicFilesystem(`relative\path`)
  52. if fs.root == `relative\path` || !strings.HasPrefix(fs.root, "\\\\?\\") {
  53. t.Errorf("%q == %q, expected absolutification", fs.root, `relative\path`)
  54. }
  55. }
  56. func TestResolveWindows83(t *testing.T) {
  57. fs, dir := setup(t)
  58. if isMaybeWin83(dir) {
  59. dir = fs.resolveWin83(dir)
  60. fs = newBasicFilesystem(dir)
  61. }
  62. shortAbs, _ := fs.rooted("LFDATA~1")
  63. long := "LFDataTool"
  64. longAbs, _ := fs.rooted(long)
  65. deleted, _ := fs.rooted(filepath.Join("foo", "LFDATA~1"))
  66. notShort, _ := fs.rooted(filepath.Join("foo", "bar", "baz"))
  67. fd, err := fs.Create(long)
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. fd.Close()
  72. if res := fs.resolveWin83(shortAbs); res != longAbs {
  73. t.Errorf(`Resolving for 8.3 names of "%v" resulted in "%v", expected "%v"`, shortAbs, res, longAbs)
  74. }
  75. if res := fs.resolveWin83(deleted); res != filepath.Dir(deleted) {
  76. t.Errorf(`Resolving for 8.3 names of "%v" resulted in "%v", expected "%v"`, deleted, res, filepath.Dir(deleted))
  77. }
  78. if res := fs.resolveWin83(notShort); res != notShort {
  79. t.Errorf(`Resolving for 8.3 names of "%v" resulted in "%v", expected "%v"`, notShort, res, notShort)
  80. }
  81. }
  82. func TestIsWindows83(t *testing.T) {
  83. fs, dir := setup(t)
  84. if isMaybeWin83(dir) {
  85. dir = fs.resolveWin83(dir)
  86. fs = newBasicFilesystem(dir)
  87. }
  88. tempTop, _ := fs.rooted(TempName("baz"))
  89. tempBelow, _ := fs.rooted(filepath.Join("foo", "bar", TempName("baz")))
  90. short, _ := fs.rooted(filepath.Join("LFDATA~1", TempName("baz")))
  91. tempAndShort, _ := fs.rooted(filepath.Join("LFDATA~1", TempName("baz")))
  92. for _, f := range []string{tempTop, tempBelow} {
  93. if isMaybeWin83(f) {
  94. t.Errorf(`"%v" is not a windows 8.3 path"`, f)
  95. }
  96. }
  97. for _, f := range []string{short, tempAndShort} {
  98. if !isMaybeWin83(f) {
  99. t.Errorf(`"%v" is not a windows 8.3 path"`, f)
  100. }
  101. }
  102. }
  103. func TestRelUnrootedCheckedWindows(t *testing.T) {
  104. testCases := []struct {
  105. root string
  106. abs string
  107. expectedRel string
  108. }{
  109. {`c:\`, `c:\foo`, `foo`},
  110. {`C:\`, `c:\foo`, `foo`},
  111. {`C:\`, `C:\foo`, `foo`},
  112. {`c:\`, `C:\foo`, `foo`},
  113. {`\\?c:\`, `\\?c:\foo`, `foo`},
  114. {`\\?C:\`, `\\?c:\foo`, `foo`},
  115. {`\\?C:\`, `\\?C:\foo`, `foo`},
  116. {`\\?c:\`, `\\?C:\foo`, `foo`},
  117. {`c:\foo`, `c:\foo\bar`, `bar`},
  118. {`c:\foo`, `c:\foo\bAr`, `bAr`},
  119. {`c:\foO`, `c:\Foo\bar`, `bar`},
  120. {`c:\foO`, `c:\fOo\bAr`, `bAr`},
  121. {`c:\foO`, `c:\fOo`, ``},
  122. {`C:\foO`, `c:\fOo`, ``},
  123. }
  124. for _, tc := range testCases {
  125. if res := rel(tc.abs, tc.root); res != tc.expectedRel {
  126. t.Errorf(`rel("%v", "%v") == "%v", expected "%v"`, tc.abs, tc.root, res, tc.expectedRel)
  127. }
  128. // unrootedChecked really just wraps rel, and does not care about
  129. // the actual root of that filesystem, but should not return an error
  130. // on these test cases.
  131. for _, root := range []string{tc.root, strings.ToLower(tc.root), strings.ToUpper(tc.root)} {
  132. fs := BasicFilesystem{root: root}
  133. if res, err := fs.unrootedChecked(tc.abs, []string{tc.root}); err != nil {
  134. t.Errorf(`Unexpected error from unrootedChecked("%v", "%v"): %v (fs.root: %v)`, tc.abs, tc.root, err, root)
  135. } else if res != tc.expectedRel {
  136. t.Errorf(`unrootedChecked("%v", "%v") == "%v", expected "%v" (fs.root: %v)`, tc.abs, tc.root, res, tc.expectedRel, root)
  137. }
  138. }
  139. }
  140. }
  141. // TestMultipleRoot checks that fs.unrootedChecked returns the correct path
  142. // when given more than one possible root path.
  143. func TestMultipleRoot(t *testing.T) {
  144. root := `c:\foO`
  145. roots := []string{root, `d:\`}
  146. rel := `bar`
  147. path := filepath.Join(root, rel)
  148. fs := BasicFilesystem{root: root}
  149. if res, err := fs.unrootedChecked(path, roots); err != nil {
  150. t.Errorf(`Unexpected error from unrootedChecked("%v", "%v"): %v (fs.root: %v)`, path, roots, err, root)
  151. } else if res != rel {
  152. t.Errorf(`unrootedChecked("%v", "%v") == "%v", expected "%v" (fs.root: %v)`, path, roots, res, rel, root)
  153. }
  154. }
  155. func TestGetFinalPath(t *testing.T) {
  156. testCases := []struct {
  157. input string
  158. expectedPath string
  159. eqToEvalSyml bool
  160. ignoreMissing bool
  161. }{
  162. {`c:\`, `C:\`, true, false},
  163. {`\\?\c:\`, `C:\`, false, false},
  164. {`c:\wInDows\sYstEm32`, `C:\Windows\System32`, true, false},
  165. {`c:\parent\child`, `C:\parent\child`, false, true},
  166. }
  167. for _, testCase := range testCases {
  168. out, err := getFinalPathName(testCase.input)
  169. if err != nil {
  170. if testCase.ignoreMissing && os.IsNotExist(err) {
  171. continue
  172. }
  173. t.Errorf("getFinalPathName failed at %q with error %s", testCase.input, err)
  174. }
  175. // Trim UNC prefix
  176. if strings.HasPrefix(out, `\\?\UNC\`) {
  177. out = `\` + out[7:]
  178. } else {
  179. out = strings.TrimPrefix(out, `\\?\`)
  180. }
  181. if out != testCase.expectedPath {
  182. t.Errorf("getFinalPathName got wrong path: %q (expected %q)", out, testCase.expectedPath)
  183. }
  184. if testCase.eqToEvalSyml {
  185. evlPath, err1 := filepath.EvalSymlinks(testCase.input)
  186. if err1 != nil || out != evlPath {
  187. t.Errorf("EvalSymlinks got different results %q %s", evlPath, err1)
  188. }
  189. }
  190. }
  191. }
  192. func TestRemoveWindowsDirIcon(t *testing.T) {
  193. // Try to delete a folder with a custom icon with os.Remove (simulated by the readonly file attribute)
  194. fs, dir := setup(t)
  195. relativePath := "folder_with_icon"
  196. path := filepath.Join(dir, relativePath)
  197. if err := os.Mkdir(path, os.ModeDir); err != nil {
  198. t.Fatal(err)
  199. }
  200. ptr, err := syscall.UTF16PtrFromString(path)
  201. if err != nil {
  202. t.Fatal(err)
  203. }
  204. if err := syscall.SetFileAttributes(ptr, uint32(syscall.FILE_ATTRIBUTE_DIRECTORY+syscall.FILE_ATTRIBUTE_READONLY)); err != nil {
  205. t.Fatal(err)
  206. }
  207. if err := fs.Remove(relativePath); err != nil {
  208. t.Fatal(err)
  209. }
  210. }