subpath_windows_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. // +build windows
  2. /*
  3. Copyright 2017 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package subpath
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "os/exec"
  20. "path/filepath"
  21. "testing"
  22. "github.com/stretchr/testify/assert"
  23. )
  24. func makeLink(link, target string) error {
  25. if output, err := exec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput(); err != nil {
  26. return fmt.Errorf("mklink failed: %v, link(%q) target(%q) output: %q", err, link, target, string(output))
  27. }
  28. return nil
  29. }
  30. func TestDoSafeMakeDir(t *testing.T) {
  31. base, err := ioutil.TempDir("", "TestDoSafeMakeDir")
  32. if err != nil {
  33. t.Fatalf(err.Error())
  34. }
  35. defer os.RemoveAll(base)
  36. testingVolumePath := filepath.Join(base, "testingVolumePath")
  37. os.MkdirAll(testingVolumePath, 0755)
  38. defer os.RemoveAll(testingVolumePath)
  39. tests := []struct {
  40. volumePath string
  41. subPath string
  42. expectError bool
  43. symlinkTarget string
  44. }{
  45. {
  46. volumePath: testingVolumePath,
  47. subPath: ``,
  48. expectError: true,
  49. symlinkTarget: "",
  50. },
  51. {
  52. volumePath: testingVolumePath,
  53. subPath: filepath.Join(testingVolumePath, `x`),
  54. expectError: false,
  55. symlinkTarget: "",
  56. },
  57. {
  58. volumePath: testingVolumePath,
  59. subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
  60. expectError: false,
  61. symlinkTarget: "",
  62. },
  63. {
  64. volumePath: testingVolumePath,
  65. subPath: filepath.Join(testingVolumePath, `symlink`),
  66. expectError: false,
  67. symlinkTarget: base,
  68. },
  69. {
  70. volumePath: testingVolumePath,
  71. subPath: filepath.Join(testingVolumePath, `symlink\c\d`),
  72. expectError: true,
  73. symlinkTarget: "",
  74. },
  75. {
  76. volumePath: testingVolumePath,
  77. subPath: filepath.Join(testingVolumePath, `symlink\y926`),
  78. expectError: true,
  79. symlinkTarget: "",
  80. },
  81. {
  82. volumePath: testingVolumePath,
  83. subPath: filepath.Join(testingVolumePath, `a\b\symlink`),
  84. expectError: false,
  85. symlinkTarget: base,
  86. },
  87. {
  88. volumePath: testingVolumePath,
  89. subPath: filepath.Join(testingVolumePath, `a\x\symlink`),
  90. expectError: false,
  91. symlinkTarget: filepath.Join(testingVolumePath, `a`),
  92. },
  93. }
  94. for _, test := range tests {
  95. if len(test.volumePath) > 0 && len(test.subPath) > 0 && len(test.symlinkTarget) > 0 {
  96. // make all parent sub directories
  97. if parent := filepath.Dir(test.subPath); parent != "." {
  98. os.MkdirAll(parent, 0755)
  99. }
  100. // make last element as symlink
  101. linkPath := test.subPath
  102. if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
  103. if err := makeLink(linkPath, test.symlinkTarget); err != nil {
  104. t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
  105. }
  106. }
  107. }
  108. err := doSafeMakeDir(test.subPath, test.volumePath, os.FileMode(0755))
  109. if test.expectError {
  110. assert.NotNil(t, err, "Expect error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
  111. continue
  112. }
  113. assert.Nil(t, err, "Expect no error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
  114. if _, err := os.Stat(test.subPath); os.IsNotExist(err) {
  115. t.Errorf("subPath should exists after doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
  116. }
  117. }
  118. }
  119. func TestLockAndCheckSubPath(t *testing.T) {
  120. base, err := ioutil.TempDir("", "TestLockAndCheckSubPath")
  121. if err != nil {
  122. t.Fatalf(err.Error())
  123. }
  124. defer os.RemoveAll(base)
  125. testingVolumePath := filepath.Join(base, "testingVolumePath")
  126. tests := []struct {
  127. volumePath string
  128. subPath string
  129. expectedHandleCount int
  130. expectError bool
  131. symlinkTarget string
  132. }{
  133. {
  134. volumePath: `c:\`,
  135. subPath: ``,
  136. expectedHandleCount: 0,
  137. expectError: false,
  138. symlinkTarget: "",
  139. },
  140. {
  141. volumePath: ``,
  142. subPath: `a`,
  143. expectedHandleCount: 0,
  144. expectError: false,
  145. symlinkTarget: "",
  146. },
  147. {
  148. volumePath: testingVolumePath,
  149. subPath: filepath.Join(testingVolumePath, `a`),
  150. expectedHandleCount: 1,
  151. expectError: false,
  152. symlinkTarget: "",
  153. },
  154. {
  155. volumePath: testingVolumePath,
  156. subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
  157. expectedHandleCount: 4,
  158. expectError: false,
  159. symlinkTarget: "",
  160. },
  161. {
  162. volumePath: testingVolumePath,
  163. subPath: filepath.Join(testingVolumePath, `symlink`),
  164. expectedHandleCount: 0,
  165. expectError: true,
  166. symlinkTarget: base,
  167. },
  168. {
  169. volumePath: testingVolumePath,
  170. subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
  171. expectedHandleCount: 0,
  172. expectError: true,
  173. symlinkTarget: base,
  174. },
  175. {
  176. volumePath: testingVolumePath,
  177. subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
  178. expectedHandleCount: 2,
  179. expectError: false,
  180. symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
  181. },
  182. }
  183. for _, test := range tests {
  184. if len(test.volumePath) > 0 && len(test.subPath) > 0 {
  185. os.MkdirAll(test.volumePath, 0755)
  186. if len(test.symlinkTarget) == 0 {
  187. // make all intermediate sub directories
  188. os.MkdirAll(test.subPath, 0755)
  189. } else {
  190. // make all parent sub directories
  191. if parent := filepath.Dir(test.subPath); parent != "." {
  192. os.MkdirAll(parent, 0755)
  193. }
  194. // make last element as symlink
  195. linkPath := test.subPath
  196. if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
  197. if err := makeLink(linkPath, test.symlinkTarget); err != nil {
  198. t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
  199. }
  200. }
  201. }
  202. }
  203. fileHandles, err := lockAndCheckSubPath(test.volumePath, test.subPath)
  204. unlockPath(fileHandles)
  205. assert.Equal(t, test.expectedHandleCount, len(fileHandles))
  206. if test.expectError {
  207. assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
  208. continue
  209. }
  210. assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
  211. }
  212. // remove dir will happen after closing all file handles
  213. assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
  214. }
  215. func TestLockAndCheckSubPathWithoutSymlink(t *testing.T) {
  216. base, err := ioutil.TempDir("", "TestLockAndCheckSubPathWithoutSymlink")
  217. if err != nil {
  218. t.Fatalf(err.Error())
  219. }
  220. defer os.RemoveAll(base)
  221. testingVolumePath := filepath.Join(base, "testingVolumePath")
  222. tests := []struct {
  223. volumePath string
  224. subPath string
  225. expectedHandleCount int
  226. expectError bool
  227. symlinkTarget string
  228. }{
  229. {
  230. volumePath: `c:\`,
  231. subPath: ``,
  232. expectedHandleCount: 0,
  233. expectError: false,
  234. symlinkTarget: "",
  235. },
  236. {
  237. volumePath: ``,
  238. subPath: `a`,
  239. expectedHandleCount: 0,
  240. expectError: false,
  241. symlinkTarget: "",
  242. },
  243. {
  244. volumePath: testingVolumePath,
  245. subPath: filepath.Join(testingVolumePath, `a`),
  246. expectedHandleCount: 1,
  247. expectError: false,
  248. symlinkTarget: "",
  249. },
  250. {
  251. volumePath: testingVolumePath,
  252. subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
  253. expectedHandleCount: 4,
  254. expectError: false,
  255. symlinkTarget: "",
  256. },
  257. {
  258. volumePath: testingVolumePath,
  259. subPath: filepath.Join(testingVolumePath, `symlink`),
  260. expectedHandleCount: 1,
  261. expectError: true,
  262. symlinkTarget: base,
  263. },
  264. {
  265. volumePath: testingVolumePath,
  266. subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
  267. expectedHandleCount: 4,
  268. expectError: true,
  269. symlinkTarget: base,
  270. },
  271. {
  272. volumePath: testingVolumePath,
  273. subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
  274. expectedHandleCount: 5,
  275. expectError: true,
  276. symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
  277. },
  278. }
  279. for _, test := range tests {
  280. if len(test.volumePath) > 0 && len(test.subPath) > 0 {
  281. os.MkdirAll(test.volumePath, 0755)
  282. if len(test.symlinkTarget) == 0 {
  283. // make all intermediate sub directories
  284. os.MkdirAll(test.subPath, 0755)
  285. } else {
  286. // make all parent sub directories
  287. if parent := filepath.Dir(test.subPath); parent != "." {
  288. os.MkdirAll(parent, 0755)
  289. }
  290. // make last element as symlink
  291. linkPath := test.subPath
  292. if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
  293. if err := makeLink(linkPath, test.symlinkTarget); err != nil {
  294. t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
  295. }
  296. }
  297. }
  298. }
  299. fileHandles, err := lockAndCheckSubPathWithoutSymlink(test.volumePath, test.subPath)
  300. unlockPath(fileHandles)
  301. assert.Equal(t, test.expectedHandleCount, len(fileHandles))
  302. if test.expectError {
  303. assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
  304. continue
  305. }
  306. assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
  307. }
  308. // remove dir will happen after closing all file handles
  309. assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
  310. }
  311. func TestFindExistingPrefix(t *testing.T) {
  312. base, err := ioutil.TempDir("", "TestFindExistingPrefix")
  313. if err != nil {
  314. t.Fatalf(err.Error())
  315. }
  316. defer os.RemoveAll(base)
  317. testingVolumePath := filepath.Join(base, "testingVolumePath")
  318. tests := []struct {
  319. base string
  320. pathname string
  321. expectError bool
  322. expectedExistingPath string
  323. expectedToCreateDirs []string
  324. createSubPathBeforeTest bool
  325. }{
  326. {
  327. base: `c:\tmp\a`,
  328. pathname: `c:\tmp\b`,
  329. expectError: true,
  330. expectedExistingPath: "",
  331. expectedToCreateDirs: []string{},
  332. createSubPathBeforeTest: false,
  333. },
  334. {
  335. base: ``,
  336. pathname: `c:\tmp\b`,
  337. expectError: true,
  338. expectedExistingPath: "",
  339. expectedToCreateDirs: []string{},
  340. createSubPathBeforeTest: false,
  341. },
  342. {
  343. base: `c:\tmp\a`,
  344. pathname: `d:\tmp\b`,
  345. expectError: true,
  346. expectedExistingPath: "",
  347. expectedToCreateDirs: []string{},
  348. createSubPathBeforeTest: false,
  349. },
  350. {
  351. base: testingVolumePath,
  352. pathname: testingVolumePath,
  353. expectError: false,
  354. expectedExistingPath: testingVolumePath,
  355. expectedToCreateDirs: []string{},
  356. createSubPathBeforeTest: false,
  357. },
  358. {
  359. base: testingVolumePath,
  360. pathname: filepath.Join(testingVolumePath, `a\b`),
  361. expectError: false,
  362. expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
  363. expectedToCreateDirs: []string{},
  364. createSubPathBeforeTest: true,
  365. },
  366. {
  367. base: testingVolumePath,
  368. pathname: filepath.Join(testingVolumePath, `a\b\c\`),
  369. expectError: false,
  370. expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
  371. expectedToCreateDirs: []string{`c`},
  372. createSubPathBeforeTest: false,
  373. },
  374. {
  375. base: testingVolumePath,
  376. pathname: filepath.Join(testingVolumePath, `a\b\c\d`),
  377. expectError: false,
  378. expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
  379. expectedToCreateDirs: []string{`c`, `d`},
  380. createSubPathBeforeTest: false,
  381. },
  382. }
  383. for _, test := range tests {
  384. if test.createSubPathBeforeTest {
  385. os.MkdirAll(test.pathname, 0755)
  386. }
  387. existingPath, toCreate, err := findExistingPrefix(test.base, test.pathname)
  388. if test.expectError {
  389. assert.NotNil(t, err, "Expect error during findExistingPrefix(%s, %s)", test.base, test.pathname)
  390. continue
  391. }
  392. assert.Nil(t, err, "Expect no error during findExistingPrefix(%s, %s)", test.base, test.pathname)
  393. assert.Equal(t, test.expectedExistingPath, existingPath, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
  394. test.base, test.pathname, existingPath, test.expectedExistingPath)
  395. assert.Equal(t, test.expectedToCreateDirs, toCreate, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
  396. test.base, test.pathname, toCreate, test.expectedToCreateDirs)
  397. }
  398. // remove dir will happen after closing all file handles
  399. assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
  400. }