subpath_linux_test.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284
  1. // +build linux
  2. /*
  3. Copyright 2014 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. "net"
  19. "os"
  20. "path/filepath"
  21. "reflect"
  22. "strconv"
  23. "syscall"
  24. "testing"
  25. "k8s.io/klog"
  26. "k8s.io/utils/mount"
  27. )
  28. func TestSafeMakeDir(t *testing.T) {
  29. defaultPerm := os.FileMode(0750) + os.ModeDir
  30. tests := []struct {
  31. name string
  32. // Function that prepares directory structure for the test under given
  33. // base.
  34. prepare func(base string) error
  35. path string
  36. checkPath string
  37. perm os.FileMode
  38. expectError bool
  39. }{
  40. {
  41. "directory-does-not-exist",
  42. func(base string) error {
  43. return nil
  44. },
  45. "test/directory",
  46. "test/directory",
  47. defaultPerm,
  48. false,
  49. },
  50. {
  51. "directory-with-sgid",
  52. func(base string) error {
  53. return nil
  54. },
  55. "test/directory",
  56. "test/directory",
  57. os.FileMode(0777) + os.ModeDir + os.ModeSetgid,
  58. false,
  59. },
  60. {
  61. "directory-with-suid",
  62. func(base string) error {
  63. return nil
  64. },
  65. "test/directory",
  66. "test/directory",
  67. os.FileMode(0777) + os.ModeDir + os.ModeSetuid,
  68. false,
  69. },
  70. {
  71. "directory-with-sticky-bit",
  72. func(base string) error {
  73. return nil
  74. },
  75. "test/directory",
  76. "test/directory",
  77. os.FileMode(0777) + os.ModeDir + os.ModeSticky,
  78. false,
  79. },
  80. {
  81. "directory-exists",
  82. func(base string) error {
  83. return os.MkdirAll(filepath.Join(base, "test/directory"), 0750)
  84. },
  85. "test/directory",
  86. "test/directory",
  87. defaultPerm,
  88. false,
  89. },
  90. {
  91. "create-base",
  92. func(base string) error {
  93. return nil
  94. },
  95. "",
  96. "",
  97. defaultPerm,
  98. false,
  99. },
  100. {
  101. "escape-base-using-dots",
  102. func(base string) error {
  103. return nil
  104. },
  105. "..",
  106. "",
  107. defaultPerm,
  108. true,
  109. },
  110. {
  111. "escape-base-using-dots-2",
  112. func(base string) error {
  113. return nil
  114. },
  115. "test/../../..",
  116. "",
  117. defaultPerm,
  118. true,
  119. },
  120. {
  121. "follow-symlinks",
  122. func(base string) error {
  123. if err := os.MkdirAll(filepath.Join(base, "destination"), defaultPerm); err != nil {
  124. return err
  125. }
  126. return os.Symlink("destination", filepath.Join(base, "test"))
  127. },
  128. "test/directory",
  129. "destination/directory",
  130. defaultPerm,
  131. false,
  132. },
  133. {
  134. "follow-symlink-loop",
  135. func(base string) error {
  136. return os.Symlink("test", filepath.Join(base, "test"))
  137. },
  138. "test/directory",
  139. "",
  140. defaultPerm,
  141. true,
  142. },
  143. {
  144. "follow-symlink-multiple follow",
  145. func(base string) error {
  146. /* test1/dir points to test2 and test2/dir points to test1 */
  147. if err := os.MkdirAll(filepath.Join(base, "test1"), defaultPerm); err != nil {
  148. return err
  149. }
  150. if err := os.MkdirAll(filepath.Join(base, "test2"), defaultPerm); err != nil {
  151. return err
  152. }
  153. if err := os.Symlink(filepath.Join(base, "test2"), filepath.Join(base, "test1/dir")); err != nil {
  154. return err
  155. }
  156. if err := os.Symlink(filepath.Join(base, "test1"), filepath.Join(base, "test2/dir")); err != nil {
  157. return err
  158. }
  159. return nil
  160. },
  161. "test1/dir/dir/dir/dir/dir/dir/dir/foo",
  162. "test2/foo",
  163. defaultPerm,
  164. false,
  165. },
  166. {
  167. "danglink-symlink",
  168. func(base string) error {
  169. return os.Symlink("non-existing", filepath.Join(base, "test"))
  170. },
  171. "test/directory",
  172. "",
  173. defaultPerm,
  174. true,
  175. },
  176. {
  177. "non-directory",
  178. func(base string) error {
  179. return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm)
  180. },
  181. "test/directory",
  182. "",
  183. defaultPerm,
  184. true,
  185. },
  186. {
  187. "non-directory-final",
  188. func(base string) error {
  189. return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm)
  190. },
  191. "test",
  192. "",
  193. defaultPerm,
  194. true,
  195. },
  196. {
  197. "escape-with-relative-symlink",
  198. func(base string) error {
  199. if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil {
  200. return err
  201. }
  202. if err := os.MkdirAll(filepath.Join(base, "exists"), defaultPerm); err != nil {
  203. return err
  204. }
  205. return os.Symlink("../exists", filepath.Join(base, "dir/test"))
  206. },
  207. "dir/test",
  208. "",
  209. defaultPerm,
  210. false,
  211. },
  212. {
  213. "escape-with-relative-symlink-not-exists",
  214. func(base string) error {
  215. if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil {
  216. return err
  217. }
  218. return os.Symlink("../not-exists", filepath.Join(base, "dir/test"))
  219. },
  220. "dir/test",
  221. "",
  222. defaultPerm,
  223. true,
  224. },
  225. {
  226. "escape-with-symlink",
  227. func(base string) error {
  228. return os.Symlink("/", filepath.Join(base, "test"))
  229. },
  230. "test/directory",
  231. "",
  232. defaultPerm,
  233. true,
  234. },
  235. }
  236. for i := range tests {
  237. test := tests[i]
  238. t.Run(test.name, func(t *testing.T) {
  239. base, err := ioutil.TempDir("", "safe-make-dir-"+test.name+"-")
  240. if err != nil {
  241. t.Fatalf(err.Error())
  242. }
  243. defer os.RemoveAll(base)
  244. test.prepare(base)
  245. pathToCreate := filepath.Join(base, test.path)
  246. err = doSafeMakeDir(pathToCreate, base, test.perm)
  247. if err != nil && !test.expectError {
  248. t.Fatal(err)
  249. }
  250. if err != nil {
  251. t.Logf("got error: %s", err)
  252. }
  253. if err == nil && test.expectError {
  254. t.Fatalf("expected error, got none")
  255. }
  256. if test.checkPath != "" {
  257. st, err := os.Stat(filepath.Join(base, test.checkPath))
  258. if err != nil {
  259. t.Fatalf("cannot read path %s", test.checkPath)
  260. }
  261. actualMode := st.Mode()
  262. if actualMode != test.perm {
  263. if actualMode^test.perm == os.ModeSetgid && test.perm&os.ModeSetgid == 0 {
  264. // when TMPDIR is a kubernetes emptydir, the sticky gid bit is set due to fsgroup
  265. t.Logf("masking bit from %o", actualMode)
  266. } else {
  267. t.Errorf("expected permissions %o, got %o (%b)", test.perm, actualMode, test.perm^actualMode)
  268. }
  269. }
  270. }
  271. })
  272. }
  273. }
  274. func TestRemoveEmptyDirs(t *testing.T) {
  275. defaultPerm := os.FileMode(0750)
  276. tests := []struct {
  277. name string
  278. // Function that prepares directory structure for the test under given
  279. // base.
  280. prepare func(base string) error
  281. // Function that validates directory structure after the test
  282. validate func(base string) error
  283. baseDir string
  284. endDir string
  285. expectError bool
  286. }{
  287. {
  288. name: "all-empty",
  289. prepare: func(base string) error {
  290. return os.MkdirAll(filepath.Join(base, "a/b/c"), defaultPerm)
  291. },
  292. validate: func(base string) error {
  293. return validateDirEmpty(filepath.Join(base, "a"))
  294. },
  295. baseDir: "a",
  296. endDir: "a/b/c",
  297. expectError: false,
  298. },
  299. {
  300. name: "dir-not-empty",
  301. prepare: func(base string) error {
  302. if err := os.MkdirAll(filepath.Join(base, "a/b/c"), defaultPerm); err != nil {
  303. return err
  304. }
  305. return os.Mkdir(filepath.Join(base, "a/b/d"), defaultPerm)
  306. },
  307. validate: func(base string) error {
  308. if err := validateDirNotExists(filepath.Join(base, "a/b/c")); err != nil {
  309. return err
  310. }
  311. return validateDirExists(filepath.Join(base, "a/b"))
  312. },
  313. baseDir: "a",
  314. endDir: "a/b/c",
  315. expectError: false,
  316. },
  317. {
  318. name: "path-not-within-base",
  319. prepare: func(base string) error {
  320. return os.MkdirAll(filepath.Join(base, "a/b/c"), defaultPerm)
  321. },
  322. validate: func(base string) error {
  323. return validateDirExists(filepath.Join(base, "a"))
  324. },
  325. baseDir: "a",
  326. endDir: "b/c",
  327. expectError: true,
  328. },
  329. {
  330. name: "path-already-deleted",
  331. prepare: func(base string) error {
  332. return nil
  333. },
  334. validate: func(base string) error {
  335. return nil
  336. },
  337. baseDir: "a",
  338. endDir: "a/b/c",
  339. expectError: false,
  340. },
  341. {
  342. name: "path-not-dir",
  343. prepare: func(base string) error {
  344. if err := os.MkdirAll(filepath.Join(base, "a/b"), defaultPerm); err != nil {
  345. return err
  346. }
  347. return ioutil.WriteFile(filepath.Join(base, "a/b", "c"), []byte{}, defaultPerm)
  348. },
  349. validate: func(base string) error {
  350. if err := validateDirExists(filepath.Join(base, "a/b")); err != nil {
  351. return err
  352. }
  353. return validateFileExists(filepath.Join(base, "a/b/c"))
  354. },
  355. baseDir: "a",
  356. endDir: "a/b/c",
  357. expectError: true,
  358. },
  359. }
  360. for _, test := range tests {
  361. klog.V(4).Infof("test %q", test.name)
  362. base, err := ioutil.TempDir("", "remove-empty-dirs-"+test.name+"-")
  363. if err != nil {
  364. t.Fatalf(err.Error())
  365. }
  366. if err = test.prepare(base); err != nil {
  367. os.RemoveAll(base)
  368. t.Fatalf("failed to prepare test %q: %v", test.name, err.Error())
  369. }
  370. err = removeEmptyDirs(filepath.Join(base, test.baseDir), filepath.Join(base, test.endDir))
  371. if err != nil && !test.expectError {
  372. t.Errorf("test %q failed: %v", test.name, err)
  373. }
  374. if err == nil && test.expectError {
  375. t.Errorf("test %q failed: expected error, got success", test.name)
  376. }
  377. if err = test.validate(base); err != nil {
  378. t.Errorf("test %q failed validation: %v", test.name, err)
  379. }
  380. os.RemoveAll(base)
  381. }
  382. }
  383. func TestCleanSubPaths(t *testing.T) {
  384. defaultPerm := os.FileMode(0750)
  385. testVol := "vol1"
  386. tests := []struct {
  387. name string
  388. // Function that prepares directory structure for the test under given
  389. // base.
  390. prepare func(base string) ([]mount.MountPoint, error)
  391. // Function that validates directory structure after the test
  392. validate func(base string) error
  393. expectError bool
  394. unmount func(path string) error
  395. }{
  396. {
  397. name: "not-exists",
  398. prepare: func(base string) ([]mount.MountPoint, error) {
  399. return nil, nil
  400. },
  401. validate: func(base string) error {
  402. return nil
  403. },
  404. expectError: false,
  405. },
  406. {
  407. name: "subpath-not-mount",
  408. prepare: func(base string) ([]mount.MountPoint, error) {
  409. return nil, os.MkdirAll(filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0"), defaultPerm)
  410. },
  411. validate: func(base string) error {
  412. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  413. },
  414. expectError: false,
  415. },
  416. {
  417. name: "subpath-file",
  418. prepare: func(base string) ([]mount.MountPoint, error) {
  419. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1")
  420. if err := os.MkdirAll(path, defaultPerm); err != nil {
  421. return nil, err
  422. }
  423. return nil, ioutil.WriteFile(filepath.Join(path, "0"), []byte{}, defaultPerm)
  424. },
  425. validate: func(base string) error {
  426. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  427. },
  428. expectError: false,
  429. },
  430. {
  431. name: "subpath-container-not-dir",
  432. prepare: func(base string) ([]mount.MountPoint, error) {
  433. path := filepath.Join(base, containerSubPathDirectoryName, testVol)
  434. if err := os.MkdirAll(path, defaultPerm); err != nil {
  435. return nil, err
  436. }
  437. return nil, ioutil.WriteFile(filepath.Join(path, "container1"), []byte{}, defaultPerm)
  438. },
  439. validate: func(base string) error {
  440. return validateDirExists(filepath.Join(base, containerSubPathDirectoryName, testVol))
  441. },
  442. expectError: true,
  443. },
  444. {
  445. name: "subpath-multiple-container-not-dir",
  446. prepare: func(base string) ([]mount.MountPoint, error) {
  447. path := filepath.Join(base, containerSubPathDirectoryName, testVol)
  448. if err := os.MkdirAll(filepath.Join(path, "container1"), defaultPerm); err != nil {
  449. return nil, err
  450. }
  451. return nil, ioutil.WriteFile(filepath.Join(path, "container2"), []byte{}, defaultPerm)
  452. },
  453. validate: func(base string) error {
  454. path := filepath.Join(base, containerSubPathDirectoryName, testVol)
  455. if err := validateDirNotExists(filepath.Join(path, "container1")); err != nil {
  456. return err
  457. }
  458. return validateFileExists(filepath.Join(path, "container2"))
  459. },
  460. expectError: true,
  461. },
  462. {
  463. name: "subpath-mount",
  464. prepare: func(base string) ([]mount.MountPoint, error) {
  465. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0")
  466. if err := os.MkdirAll(path, defaultPerm); err != nil {
  467. return nil, err
  468. }
  469. mounts := []mount.MountPoint{{Device: "/dev/sdb", Path: path}}
  470. return mounts, nil
  471. },
  472. validate: func(base string) error {
  473. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  474. },
  475. },
  476. {
  477. name: "subpath-mount-multiple",
  478. prepare: func(base string) ([]mount.MountPoint, error) {
  479. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0")
  480. path2 := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "1")
  481. path3 := filepath.Join(base, containerSubPathDirectoryName, testVol, "container2", "1")
  482. if err := os.MkdirAll(path, defaultPerm); err != nil {
  483. return nil, err
  484. }
  485. if err := os.MkdirAll(path2, defaultPerm); err != nil {
  486. return nil, err
  487. }
  488. if err := os.MkdirAll(path3, defaultPerm); err != nil {
  489. return nil, err
  490. }
  491. mounts := []mount.MountPoint{
  492. {Device: "/dev/sdb", Path: path},
  493. {Device: "/dev/sdb", Path: path3},
  494. }
  495. return mounts, nil
  496. },
  497. validate: func(base string) error {
  498. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  499. },
  500. },
  501. {
  502. name: "subpath-mount-multiple-vols",
  503. prepare: func(base string) ([]mount.MountPoint, error) {
  504. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0")
  505. path2 := filepath.Join(base, containerSubPathDirectoryName, "vol2", "container1", "1")
  506. if err := os.MkdirAll(path, defaultPerm); err != nil {
  507. return nil, err
  508. }
  509. if err := os.MkdirAll(path2, defaultPerm); err != nil {
  510. return nil, err
  511. }
  512. mounts := []mount.MountPoint{
  513. {Device: "/dev/sdb", Path: path},
  514. }
  515. return mounts, nil
  516. },
  517. validate: func(base string) error {
  518. baseSubdir := filepath.Join(base, containerSubPathDirectoryName)
  519. if err := validateDirNotExists(filepath.Join(baseSubdir, testVol)); err != nil {
  520. return err
  521. }
  522. return validateDirExists(baseSubdir)
  523. },
  524. },
  525. {
  526. name: "subpath-with-files",
  527. prepare: func(base string) ([]mount.MountPoint, error) {
  528. containerPath := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1")
  529. if err := os.MkdirAll(containerPath, defaultPerm); err != nil {
  530. return nil, err
  531. }
  532. file0 := filepath.Join(containerPath, "0")
  533. if err := ioutil.WriteFile(file0, []byte{}, defaultPerm); err != nil {
  534. return nil, err
  535. }
  536. dir1 := filepath.Join(containerPath, "1")
  537. if err := os.MkdirAll(filepath.Join(dir1, "my-dir-1"), defaultPerm); err != nil {
  538. return nil, err
  539. }
  540. dir2 := filepath.Join(containerPath, "2")
  541. if err := os.MkdirAll(filepath.Join(dir2, "my-dir-2"), defaultPerm); err != nil {
  542. return nil, err
  543. }
  544. file3 := filepath.Join(containerPath, "3")
  545. if err := ioutil.WriteFile(file3, []byte{}, defaultPerm); err != nil {
  546. return nil, err
  547. }
  548. mounts := []mount.MountPoint{
  549. {Device: "/dev/sdb", Path: file0},
  550. {Device: "/dev/sdc", Path: dir1},
  551. {Device: "/dev/sdd", Path: dir2},
  552. {Device: "/dev/sde", Path: file3},
  553. }
  554. return mounts, nil
  555. },
  556. unmount: func(mountpath string) error {
  557. err := filepath.Walk(mountpath, func(path string, info os.FileInfo, err error) error {
  558. if path == mountpath {
  559. // Skip top level directory
  560. return nil
  561. }
  562. if err = os.Remove(path); err != nil {
  563. return err
  564. }
  565. return filepath.SkipDir
  566. })
  567. if err != nil {
  568. return fmt.Errorf("error processing %s: %s", mountpath, err)
  569. }
  570. return nil
  571. },
  572. validate: func(base string) error {
  573. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  574. },
  575. },
  576. }
  577. for _, test := range tests {
  578. klog.V(4).Infof("test %q", test.name)
  579. base, err := ioutil.TempDir("", "clean-subpaths-"+test.name+"-")
  580. if err != nil {
  581. t.Fatalf(err.Error())
  582. }
  583. mounts, err := test.prepare(base)
  584. if err != nil {
  585. os.RemoveAll(base)
  586. t.Fatalf("failed to prepare test %q: %v", test.name, err.Error())
  587. }
  588. fm := mount.NewFakeMounter(mounts)
  589. fm.UnmountFunc = test.unmount
  590. err = doCleanSubPaths(fm, base, testVol)
  591. if err != nil && !test.expectError {
  592. t.Errorf("test %q failed: %v", test.name, err)
  593. }
  594. if err == nil && test.expectError {
  595. t.Errorf("test %q failed: expected error, got success", test.name)
  596. }
  597. if err = test.validate(base); err != nil {
  598. t.Errorf("test %q failed validation: %v", test.name, err)
  599. }
  600. os.RemoveAll(base)
  601. }
  602. }
  603. var (
  604. testVol = "vol1"
  605. testPod = "pod0"
  606. testContainer = "container0"
  607. testSubpath = 1
  608. )
  609. func setupFakeMounter(testMounts []string) *mount.FakeMounter {
  610. mounts := []mount.MountPoint{}
  611. for _, mountPoint := range testMounts {
  612. mounts = append(mounts, mount.MountPoint{Device: "/foo", Path: mountPoint})
  613. }
  614. return mount.NewFakeMounter(mounts)
  615. }
  616. func getTestPaths(base string) (string, string) {
  617. return filepath.Join(base, testVol),
  618. filepath.Join(base, testPod, containerSubPathDirectoryName, testVol, testContainer, strconv.Itoa(testSubpath))
  619. }
  620. func TestBindSubPath(t *testing.T) {
  621. defaultPerm := os.FileMode(0750)
  622. tests := []struct {
  623. name string
  624. // Function that prepares directory structure for the test under given
  625. // base.
  626. prepare func(base string) ([]string, string, string, error)
  627. expectError bool
  628. }{
  629. {
  630. name: "subpath-dir",
  631. prepare: func(base string) ([]string, string, string, error) {
  632. volpath, _ := getTestPaths(base)
  633. subpath := filepath.Join(volpath, "dir0")
  634. return nil, volpath, subpath, os.MkdirAll(subpath, defaultPerm)
  635. },
  636. expectError: false,
  637. },
  638. {
  639. name: "subpath-dir-symlink",
  640. prepare: func(base string) ([]string, string, string, error) {
  641. volpath, _ := getTestPaths(base)
  642. subpath := filepath.Join(volpath, "dir0")
  643. if err := os.MkdirAll(subpath, defaultPerm); err != nil {
  644. return nil, "", "", err
  645. }
  646. subpathLink := filepath.Join(volpath, "dirLink")
  647. return nil, volpath, subpath, os.Symlink(subpath, subpathLink)
  648. },
  649. expectError: false,
  650. },
  651. {
  652. name: "subpath-file",
  653. prepare: func(base string) ([]string, string, string, error) {
  654. volpath, _ := getTestPaths(base)
  655. subpath := filepath.Join(volpath, "file0")
  656. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  657. return nil, "", "", err
  658. }
  659. return nil, volpath, subpath, ioutil.WriteFile(subpath, []byte{}, defaultPerm)
  660. },
  661. expectError: false,
  662. },
  663. {
  664. name: "subpath-not-exists",
  665. prepare: func(base string) ([]string, string, string, error) {
  666. volpath, _ := getTestPaths(base)
  667. subpath := filepath.Join(volpath, "file0")
  668. return nil, volpath, subpath, nil
  669. },
  670. expectError: true,
  671. },
  672. {
  673. name: "subpath-outside",
  674. prepare: func(base string) ([]string, string, string, error) {
  675. volpath, _ := getTestPaths(base)
  676. subpath := filepath.Join(volpath, "dir0")
  677. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  678. return nil, "", "", err
  679. }
  680. return nil, volpath, subpath, os.Symlink(base, subpath)
  681. },
  682. expectError: true,
  683. },
  684. {
  685. name: "subpath-symlink-child-outside",
  686. prepare: func(base string) ([]string, string, string, error) {
  687. volpath, _ := getTestPaths(base)
  688. subpathDir := filepath.Join(volpath, "dir0")
  689. subpath := filepath.Join(subpathDir, "child0")
  690. if err := os.MkdirAll(subpathDir, defaultPerm); err != nil {
  691. return nil, "", "", err
  692. }
  693. return nil, volpath, subpath, os.Symlink(base, subpath)
  694. },
  695. expectError: true,
  696. },
  697. {
  698. name: "subpath-child-outside-exists",
  699. prepare: func(base string) ([]string, string, string, error) {
  700. volpath, _ := getTestPaths(base)
  701. subpathDir := filepath.Join(volpath, "dir0")
  702. child := filepath.Join(base, "child0")
  703. subpath := filepath.Join(subpathDir, "child0")
  704. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  705. return nil, "", "", err
  706. }
  707. // touch file outside
  708. if err := ioutil.WriteFile(child, []byte{}, defaultPerm); err != nil {
  709. return nil, "", "", err
  710. }
  711. // create symlink for subpath dir
  712. return nil, volpath, subpath, os.Symlink(base, subpathDir)
  713. },
  714. expectError: true,
  715. },
  716. {
  717. name: "subpath-child-outside-not-exists",
  718. prepare: func(base string) ([]string, string, string, error) {
  719. volpath, _ := getTestPaths(base)
  720. subpathDir := filepath.Join(volpath, "dir0")
  721. subpath := filepath.Join(subpathDir, "child0")
  722. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  723. return nil, "", "", err
  724. }
  725. // create symlink for subpath dir
  726. return nil, volpath, subpath, os.Symlink(base, subpathDir)
  727. },
  728. expectError: true,
  729. },
  730. {
  731. name: "subpath-child-outside-exists-middle-dir-symlink",
  732. prepare: func(base string) ([]string, string, string, error) {
  733. volpath, _ := getTestPaths(base)
  734. subpathDir := filepath.Join(volpath, "dir0")
  735. symlinkDir := filepath.Join(subpathDir, "linkDir0")
  736. child := filepath.Join(base, "child0")
  737. subpath := filepath.Join(symlinkDir, "child0")
  738. if err := os.MkdirAll(subpathDir, defaultPerm); err != nil {
  739. return nil, "", "", err
  740. }
  741. // touch file outside
  742. if err := ioutil.WriteFile(child, []byte{}, defaultPerm); err != nil {
  743. return nil, "", "", err
  744. }
  745. // create symlink for middle dir
  746. return nil, volpath, subpath, os.Symlink(base, symlinkDir)
  747. },
  748. expectError: true,
  749. },
  750. {
  751. name: "subpath-backstepping",
  752. prepare: func(base string) ([]string, string, string, error) {
  753. volpath, _ := getTestPaths(base)
  754. subpath := filepath.Join(volpath, "dir0")
  755. symlinkBase := filepath.Join(volpath, "..")
  756. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  757. return nil, "", "", err
  758. }
  759. // create symlink for subpath
  760. return nil, volpath, subpath, os.Symlink(symlinkBase, subpath)
  761. },
  762. expectError: true,
  763. },
  764. {
  765. name: "subpath-mountdir-already-exists",
  766. prepare: func(base string) ([]string, string, string, error) {
  767. volpath, subpathMount := getTestPaths(base)
  768. if err := os.MkdirAll(subpathMount, defaultPerm); err != nil {
  769. return nil, "", "", err
  770. }
  771. subpath := filepath.Join(volpath, "dir0")
  772. return nil, volpath, subpath, os.MkdirAll(subpath, defaultPerm)
  773. },
  774. expectError: false,
  775. },
  776. {
  777. name: "subpath-mount-already-exists",
  778. prepare: func(base string) ([]string, string, string, error) {
  779. volpath, subpathMount := getTestPaths(base)
  780. mounts := []string{subpathMount}
  781. if err := os.MkdirAll(subpathMount, defaultPerm); err != nil {
  782. return nil, "", "", err
  783. }
  784. subpath := filepath.Join(volpath, "dir0")
  785. return mounts, volpath, subpath, os.MkdirAll(subpath, defaultPerm)
  786. },
  787. expectError: false,
  788. },
  789. {
  790. name: "mount-unix-socket",
  791. prepare: func(base string) ([]string, string, string, error) {
  792. volpath, subpathMount := getTestPaths(base)
  793. mounts := []string{subpathMount}
  794. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  795. return nil, "", "", err
  796. }
  797. socketFile, socketCreateError := createSocketFile(volpath)
  798. return mounts, volpath, socketFile, socketCreateError
  799. },
  800. expectError: false,
  801. },
  802. {
  803. name: "subpath-mounting-fifo",
  804. prepare: func(base string) ([]string, string, string, error) {
  805. volpath, subpathMount := getTestPaths(base)
  806. mounts := []string{subpathMount}
  807. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  808. return nil, "", "", err
  809. }
  810. testFifo := filepath.Join(volpath, "mount_test.fifo")
  811. err := syscall.Mkfifo(testFifo, 0)
  812. return mounts, volpath, testFifo, err
  813. },
  814. expectError: false,
  815. },
  816. }
  817. for _, test := range tests {
  818. klog.V(4).Infof("test %q", test.name)
  819. base, err := ioutil.TempDir("", "bind-subpath-"+test.name+"-")
  820. if err != nil {
  821. t.Fatalf(err.Error())
  822. }
  823. mounts, volPath, subPath, err := test.prepare(base)
  824. if err != nil {
  825. os.RemoveAll(base)
  826. t.Fatalf("failed to prepare test %q: %v", test.name, err.Error())
  827. }
  828. fm := setupFakeMounter(mounts)
  829. subpath := Subpath{
  830. VolumeMountIndex: testSubpath,
  831. Path: subPath,
  832. VolumeName: testVol,
  833. VolumePath: volPath,
  834. PodDir: filepath.Join(base, "pod0"),
  835. ContainerName: testContainer,
  836. }
  837. _, subpathMount := getTestPaths(base)
  838. bindPathTarget, err := doBindSubPath(fm, subpath)
  839. if test.expectError {
  840. if err == nil {
  841. t.Errorf("test %q failed: expected error, got success", test.name)
  842. }
  843. if bindPathTarget != "" {
  844. t.Errorf("test %q failed: expected empty bindPathTarget, got %v", test.name, bindPathTarget)
  845. }
  846. if err = validateDirNotExists(subpathMount); err != nil {
  847. t.Errorf("test %q failed: %v", test.name, err)
  848. }
  849. }
  850. if !test.expectError {
  851. if err != nil {
  852. t.Errorf("test %q failed: %v", test.name, err)
  853. }
  854. if bindPathTarget != subpathMount {
  855. t.Errorf("test %q failed: expected bindPathTarget %v, got %v", test.name, subpathMount, bindPathTarget)
  856. }
  857. if err = validateFileExists(subpathMount); err != nil {
  858. t.Errorf("test %q failed: %v", test.name, err)
  859. }
  860. }
  861. os.RemoveAll(base)
  862. }
  863. }
  864. func TestSafeOpen(t *testing.T) {
  865. defaultPerm := os.FileMode(0750)
  866. tests := []struct {
  867. name string
  868. // Function that prepares directory structure for the test under given
  869. // base.
  870. prepare func(base string) error
  871. path string
  872. expectError bool
  873. }{
  874. {
  875. "directory-does-not-exist",
  876. func(base string) error {
  877. return nil
  878. },
  879. "test/directory",
  880. true,
  881. },
  882. {
  883. "directory-exists",
  884. func(base string) error {
  885. return os.MkdirAll(filepath.Join(base, "test/directory"), 0750)
  886. },
  887. "test/directory",
  888. false,
  889. },
  890. {
  891. "escape-base-using-dots",
  892. func(base string) error {
  893. return nil
  894. },
  895. "..",
  896. true,
  897. },
  898. {
  899. "escape-base-using-dots-2",
  900. func(base string) error {
  901. return os.MkdirAll(filepath.Join(base, "test"), 0750)
  902. },
  903. "test/../../..",
  904. true,
  905. },
  906. {
  907. "symlink",
  908. func(base string) error {
  909. if err := os.MkdirAll(filepath.Join(base, "destination"), defaultPerm); err != nil {
  910. return err
  911. }
  912. return os.Symlink("destination", filepath.Join(base, "test"))
  913. },
  914. "test",
  915. true,
  916. },
  917. {
  918. "symlink-nested",
  919. func(base string) error {
  920. if err := os.MkdirAll(filepath.Join(base, "dir1/dir2"), defaultPerm); err != nil {
  921. return err
  922. }
  923. return os.Symlink("dir1", filepath.Join(base, "dir1/dir2/test"))
  924. },
  925. "test",
  926. true,
  927. },
  928. {
  929. "symlink-loop",
  930. func(base string) error {
  931. return os.Symlink("test", filepath.Join(base, "test"))
  932. },
  933. "test",
  934. true,
  935. },
  936. {
  937. "symlink-not-exists",
  938. func(base string) error {
  939. return os.Symlink("non-existing", filepath.Join(base, "test"))
  940. },
  941. "test",
  942. true,
  943. },
  944. {
  945. "non-directory",
  946. func(base string) error {
  947. return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm)
  948. },
  949. "test/directory",
  950. true,
  951. },
  952. {
  953. "non-directory-final",
  954. func(base string) error {
  955. return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm)
  956. },
  957. "test",
  958. false,
  959. },
  960. {
  961. "escape-with-relative-symlink",
  962. func(base string) error {
  963. if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil {
  964. return err
  965. }
  966. if err := os.MkdirAll(filepath.Join(base, "exists"), defaultPerm); err != nil {
  967. return err
  968. }
  969. return os.Symlink("../exists", filepath.Join(base, "dir/test"))
  970. },
  971. "dir/test",
  972. true,
  973. },
  974. {
  975. "escape-with-relative-symlink-not-exists",
  976. func(base string) error {
  977. if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil {
  978. return err
  979. }
  980. return os.Symlink("../not-exists", filepath.Join(base, "dir/test"))
  981. },
  982. "dir/test",
  983. true,
  984. },
  985. {
  986. "escape-with-symlink",
  987. func(base string) error {
  988. return os.Symlink("/", filepath.Join(base, "test"))
  989. },
  990. "test",
  991. true,
  992. },
  993. {
  994. "mount-unix-socket",
  995. func(base string) error {
  996. socketFile, socketError := createSocketFile(base)
  997. if socketError != nil {
  998. return fmt.Errorf("Error preparing socket file %s with %v", socketFile, socketError)
  999. }
  1000. return nil
  1001. },
  1002. "mt.sock",
  1003. false,
  1004. },
  1005. {
  1006. "mounting-unix-socket-in-middle",
  1007. func(base string) error {
  1008. testSocketFile, socketError := createSocketFile(base)
  1009. if socketError != nil {
  1010. return fmt.Errorf("Error preparing socket file %s with %v", testSocketFile, socketError)
  1011. }
  1012. return nil
  1013. },
  1014. "mt.sock/bar",
  1015. true,
  1016. },
  1017. }
  1018. for _, test := range tests {
  1019. klog.V(4).Infof("test %q", test.name)
  1020. base, err := ioutil.TempDir("", "safe-open-"+test.name+"-")
  1021. if err != nil {
  1022. t.Fatalf(err.Error())
  1023. }
  1024. test.prepare(base)
  1025. pathToCreate := filepath.Join(base, test.path)
  1026. fd, err := doSafeOpen(pathToCreate, base)
  1027. if err != nil && !test.expectError {
  1028. t.Errorf("test %q: %s", test.name, err)
  1029. }
  1030. if err != nil {
  1031. klog.Infof("got error: %s", err)
  1032. }
  1033. if err == nil && test.expectError {
  1034. t.Errorf("test %q: expected error, got none", test.name)
  1035. }
  1036. syscall.Close(fd)
  1037. os.RemoveAll(base)
  1038. }
  1039. }
  1040. func createSocketFile(socketDir string) (string, error) {
  1041. testSocketFile := filepath.Join(socketDir, "mt.sock")
  1042. // Switch to volume path and create the socket file
  1043. // socket file can not have length of more than 108 character
  1044. // and hence we must use relative path
  1045. oldDir, _ := os.Getwd()
  1046. err := os.Chdir(socketDir)
  1047. if err != nil {
  1048. return "", err
  1049. }
  1050. defer func() {
  1051. os.Chdir(oldDir)
  1052. }()
  1053. _, socketCreateError := net.Listen("unix", "mt.sock")
  1054. return testSocketFile, socketCreateError
  1055. }
  1056. func TestFindExistingPrefix(t *testing.T) {
  1057. defaultPerm := os.FileMode(0750)
  1058. tests := []struct {
  1059. name string
  1060. // Function that prepares directory structure for the test under given
  1061. // base.
  1062. prepare func(base string) error
  1063. path string
  1064. expectedPath string
  1065. expectedDirs []string
  1066. expectError bool
  1067. }{
  1068. {
  1069. "directory-does-not-exist",
  1070. func(base string) error {
  1071. return nil
  1072. },
  1073. "directory",
  1074. "",
  1075. []string{"directory"},
  1076. false,
  1077. },
  1078. {
  1079. "directory-exists",
  1080. func(base string) error {
  1081. return os.MkdirAll(filepath.Join(base, "test/directory"), 0750)
  1082. },
  1083. "test/directory",
  1084. "test/directory",
  1085. []string{},
  1086. false,
  1087. },
  1088. {
  1089. "follow-symlinks",
  1090. func(base string) error {
  1091. if err := os.MkdirAll(filepath.Join(base, "destination/directory"), defaultPerm); err != nil {
  1092. return err
  1093. }
  1094. return os.Symlink("destination", filepath.Join(base, "test"))
  1095. },
  1096. "test/directory",
  1097. "test/directory",
  1098. []string{},
  1099. false,
  1100. },
  1101. {
  1102. "follow-symlink-loop",
  1103. func(base string) error {
  1104. return os.Symlink("test", filepath.Join(base, "test"))
  1105. },
  1106. "test/directory",
  1107. "",
  1108. nil,
  1109. true,
  1110. },
  1111. {
  1112. "follow-symlink-multiple follow",
  1113. func(base string) error {
  1114. /* test1/dir points to test2 and test2/dir points to test1 */
  1115. if err := os.MkdirAll(filepath.Join(base, "test1"), defaultPerm); err != nil {
  1116. return err
  1117. }
  1118. if err := os.MkdirAll(filepath.Join(base, "test2"), defaultPerm); err != nil {
  1119. return err
  1120. }
  1121. if err := os.Symlink(filepath.Join(base, "test2"), filepath.Join(base, "test1/dir")); err != nil {
  1122. return err
  1123. }
  1124. if err := os.Symlink(filepath.Join(base, "test1"), filepath.Join(base, "test2/dir")); err != nil {
  1125. return err
  1126. }
  1127. return nil
  1128. },
  1129. "test1/dir/dir/foo/bar",
  1130. "test1/dir/dir",
  1131. []string{"foo", "bar"},
  1132. false,
  1133. },
  1134. {
  1135. "danglink-symlink",
  1136. func(base string) error {
  1137. return os.Symlink("non-existing", filepath.Join(base, "test"))
  1138. },
  1139. // OS returns IsNotExist error both for dangling symlink and for
  1140. // non-existing directory.
  1141. "test/directory",
  1142. "",
  1143. []string{"test", "directory"},
  1144. false,
  1145. },
  1146. {
  1147. "with-fifo-in-middle",
  1148. func(base string) error {
  1149. testFifo := filepath.Join(base, "mount_test.fifo")
  1150. return syscall.Mkfifo(testFifo, 0)
  1151. },
  1152. "mount_test.fifo/directory",
  1153. "",
  1154. nil,
  1155. true,
  1156. },
  1157. }
  1158. for _, test := range tests {
  1159. klog.V(4).Infof("test %q", test.name)
  1160. base, err := ioutil.TempDir("", "find-prefix-"+test.name+"-")
  1161. if err != nil {
  1162. t.Fatalf(err.Error())
  1163. }
  1164. test.prepare(base)
  1165. path := filepath.Join(base, test.path)
  1166. existingPath, dirs, err := findExistingPrefix(base, path)
  1167. if err != nil && !test.expectError {
  1168. t.Errorf("test %q: %s", test.name, err)
  1169. }
  1170. if err != nil {
  1171. klog.Infof("got error: %s", err)
  1172. }
  1173. if err == nil && test.expectError {
  1174. t.Errorf("test %q: expected error, got none", test.name)
  1175. }
  1176. fullExpectedPath := filepath.Join(base, test.expectedPath)
  1177. if existingPath != fullExpectedPath {
  1178. t.Errorf("test %q: expected path %q, got %q", test.name, fullExpectedPath, existingPath)
  1179. }
  1180. if !reflect.DeepEqual(dirs, test.expectedDirs) {
  1181. t.Errorf("test %q: expected dirs %v, got %v", test.name, test.expectedDirs, dirs)
  1182. }
  1183. os.RemoveAll(base)
  1184. }
  1185. }
  1186. func validateDirEmpty(dir string) error {
  1187. files, err := ioutil.ReadDir(dir)
  1188. if err != nil {
  1189. return err
  1190. }
  1191. if len(files) != 0 {
  1192. return fmt.Errorf("Directory %q is not empty", dir)
  1193. }
  1194. return nil
  1195. }
  1196. func validateDirExists(dir string) error {
  1197. _, err := ioutil.ReadDir(dir)
  1198. if err != nil {
  1199. return err
  1200. }
  1201. return nil
  1202. }
  1203. func validateDirNotExists(dir string) error {
  1204. _, err := ioutil.ReadDir(dir)
  1205. if os.IsNotExist(err) {
  1206. return nil
  1207. }
  1208. if err != nil {
  1209. return err
  1210. }
  1211. return fmt.Errorf("dir %q still exists", dir)
  1212. }
  1213. func validateFileExists(file string) error {
  1214. if _, err := os.Stat(file); err != nil {
  1215. return err
  1216. }
  1217. return nil
  1218. }