subpath_linux_test.go 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225
  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/kubernetes/pkg/util/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. }{
  395. {
  396. name: "not-exists",
  397. prepare: func(base string) ([]mount.MountPoint, error) {
  398. return nil, nil
  399. },
  400. validate: func(base string) error {
  401. return nil
  402. },
  403. expectError: false,
  404. },
  405. {
  406. name: "subpath-not-mount",
  407. prepare: func(base string) ([]mount.MountPoint, error) {
  408. return nil, os.MkdirAll(filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0"), defaultPerm)
  409. },
  410. validate: func(base string) error {
  411. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  412. },
  413. expectError: false,
  414. },
  415. {
  416. name: "subpath-file",
  417. prepare: func(base string) ([]mount.MountPoint, error) {
  418. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1")
  419. if err := os.MkdirAll(path, defaultPerm); err != nil {
  420. return nil, err
  421. }
  422. return nil, ioutil.WriteFile(filepath.Join(path, "0"), []byte{}, defaultPerm)
  423. },
  424. validate: func(base string) error {
  425. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  426. },
  427. expectError: false,
  428. },
  429. {
  430. name: "subpath-container-not-dir",
  431. prepare: func(base string) ([]mount.MountPoint, error) {
  432. path := filepath.Join(base, containerSubPathDirectoryName, testVol)
  433. if err := os.MkdirAll(path, defaultPerm); err != nil {
  434. return nil, err
  435. }
  436. return nil, ioutil.WriteFile(filepath.Join(path, "container1"), []byte{}, defaultPerm)
  437. },
  438. validate: func(base string) error {
  439. return validateDirExists(filepath.Join(base, containerSubPathDirectoryName, testVol))
  440. },
  441. expectError: true,
  442. },
  443. {
  444. name: "subpath-multiple-container-not-dir",
  445. prepare: func(base string) ([]mount.MountPoint, error) {
  446. path := filepath.Join(base, containerSubPathDirectoryName, testVol)
  447. if err := os.MkdirAll(filepath.Join(path, "container1"), defaultPerm); err != nil {
  448. return nil, err
  449. }
  450. return nil, ioutil.WriteFile(filepath.Join(path, "container2"), []byte{}, defaultPerm)
  451. },
  452. validate: func(base string) error {
  453. path := filepath.Join(base, containerSubPathDirectoryName, testVol)
  454. if err := validateDirNotExists(filepath.Join(path, "container1")); err != nil {
  455. return err
  456. }
  457. return validateFileExists(filepath.Join(path, "container2"))
  458. },
  459. expectError: true,
  460. },
  461. {
  462. name: "subpath-mount",
  463. prepare: func(base string) ([]mount.MountPoint, error) {
  464. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0")
  465. if err := os.MkdirAll(path, defaultPerm); err != nil {
  466. return nil, err
  467. }
  468. mounts := []mount.MountPoint{{Device: "/dev/sdb", Path: path}}
  469. return mounts, nil
  470. },
  471. validate: func(base string) error {
  472. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  473. },
  474. },
  475. {
  476. name: "subpath-mount-multiple",
  477. prepare: func(base string) ([]mount.MountPoint, error) {
  478. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0")
  479. path2 := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "1")
  480. path3 := filepath.Join(base, containerSubPathDirectoryName, testVol, "container2", "1")
  481. if err := os.MkdirAll(path, defaultPerm); err != nil {
  482. return nil, err
  483. }
  484. if err := os.MkdirAll(path2, defaultPerm); err != nil {
  485. return nil, err
  486. }
  487. if err := os.MkdirAll(path3, defaultPerm); err != nil {
  488. return nil, err
  489. }
  490. mounts := []mount.MountPoint{
  491. {Device: "/dev/sdb", Path: path},
  492. {Device: "/dev/sdb", Path: path3},
  493. }
  494. return mounts, nil
  495. },
  496. validate: func(base string) error {
  497. return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName))
  498. },
  499. },
  500. {
  501. name: "subpath-mount-multiple-vols",
  502. prepare: func(base string) ([]mount.MountPoint, error) {
  503. path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0")
  504. path2 := filepath.Join(base, containerSubPathDirectoryName, "vol2", "container1", "1")
  505. if err := os.MkdirAll(path, defaultPerm); err != nil {
  506. return nil, err
  507. }
  508. if err := os.MkdirAll(path2, defaultPerm); err != nil {
  509. return nil, err
  510. }
  511. mounts := []mount.MountPoint{
  512. {Device: "/dev/sdb", Path: path},
  513. }
  514. return mounts, nil
  515. },
  516. validate: func(base string) error {
  517. baseSubdir := filepath.Join(base, containerSubPathDirectoryName)
  518. if err := validateDirNotExists(filepath.Join(baseSubdir, testVol)); err != nil {
  519. return err
  520. }
  521. return validateDirExists(baseSubdir)
  522. },
  523. },
  524. }
  525. for _, test := range tests {
  526. klog.V(4).Infof("test %q", test.name)
  527. base, err := ioutil.TempDir("", "clean-subpaths-"+test.name+"-")
  528. if err != nil {
  529. t.Fatalf(err.Error())
  530. }
  531. mounts, err := test.prepare(base)
  532. if err != nil {
  533. os.RemoveAll(base)
  534. t.Fatalf("failed to prepare test %q: %v", test.name, err.Error())
  535. }
  536. fm := &mount.FakeMounter{MountPoints: mounts}
  537. err = doCleanSubPaths(fm, base, testVol)
  538. if err != nil && !test.expectError {
  539. t.Errorf("test %q failed: %v", test.name, err)
  540. }
  541. if err == nil && test.expectError {
  542. t.Errorf("test %q failed: expected error, got success", test.name)
  543. }
  544. if err = test.validate(base); err != nil {
  545. t.Errorf("test %q failed validation: %v", test.name, err)
  546. }
  547. os.RemoveAll(base)
  548. }
  549. }
  550. var (
  551. testVol = "vol1"
  552. testPod = "pod0"
  553. testContainer = "container0"
  554. testSubpath = 1
  555. )
  556. func setupFakeMounter(testMounts []string) *mount.FakeMounter {
  557. mounts := []mount.MountPoint{}
  558. for _, mountPoint := range testMounts {
  559. mounts = append(mounts, mount.MountPoint{Device: "/foo", Path: mountPoint})
  560. }
  561. return &mount.FakeMounter{MountPoints: mounts}
  562. }
  563. func getTestPaths(base string) (string, string) {
  564. return filepath.Join(base, testVol),
  565. filepath.Join(base, testPod, containerSubPathDirectoryName, testVol, testContainer, strconv.Itoa(testSubpath))
  566. }
  567. func TestBindSubPath(t *testing.T) {
  568. defaultPerm := os.FileMode(0750)
  569. tests := []struct {
  570. name string
  571. // Function that prepares directory structure for the test under given
  572. // base.
  573. prepare func(base string) ([]string, string, string, error)
  574. expectError bool
  575. }{
  576. {
  577. name: "subpath-dir",
  578. prepare: func(base string) ([]string, string, string, error) {
  579. volpath, _ := getTestPaths(base)
  580. subpath := filepath.Join(volpath, "dir0")
  581. return nil, volpath, subpath, os.MkdirAll(subpath, defaultPerm)
  582. },
  583. expectError: false,
  584. },
  585. {
  586. name: "subpath-dir-symlink",
  587. prepare: func(base string) ([]string, string, string, error) {
  588. volpath, _ := getTestPaths(base)
  589. subpath := filepath.Join(volpath, "dir0")
  590. if err := os.MkdirAll(subpath, defaultPerm); err != nil {
  591. return nil, "", "", err
  592. }
  593. subpathLink := filepath.Join(volpath, "dirLink")
  594. return nil, volpath, subpath, os.Symlink(subpath, subpathLink)
  595. },
  596. expectError: false,
  597. },
  598. {
  599. name: "subpath-file",
  600. prepare: func(base string) ([]string, string, string, error) {
  601. volpath, _ := getTestPaths(base)
  602. subpath := filepath.Join(volpath, "file0")
  603. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  604. return nil, "", "", err
  605. }
  606. return nil, volpath, subpath, ioutil.WriteFile(subpath, []byte{}, defaultPerm)
  607. },
  608. expectError: false,
  609. },
  610. {
  611. name: "subpath-not-exists",
  612. prepare: func(base string) ([]string, string, string, error) {
  613. volpath, _ := getTestPaths(base)
  614. subpath := filepath.Join(volpath, "file0")
  615. return nil, volpath, subpath, nil
  616. },
  617. expectError: true,
  618. },
  619. {
  620. name: "subpath-outside",
  621. prepare: func(base string) ([]string, string, string, error) {
  622. volpath, _ := getTestPaths(base)
  623. subpath := filepath.Join(volpath, "dir0")
  624. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  625. return nil, "", "", err
  626. }
  627. return nil, volpath, subpath, os.Symlink(base, subpath)
  628. },
  629. expectError: true,
  630. },
  631. {
  632. name: "subpath-symlink-child-outside",
  633. prepare: func(base string) ([]string, string, string, error) {
  634. volpath, _ := getTestPaths(base)
  635. subpathDir := filepath.Join(volpath, "dir0")
  636. subpath := filepath.Join(subpathDir, "child0")
  637. if err := os.MkdirAll(subpathDir, defaultPerm); err != nil {
  638. return nil, "", "", err
  639. }
  640. return nil, volpath, subpath, os.Symlink(base, subpath)
  641. },
  642. expectError: true,
  643. },
  644. {
  645. name: "subpath-child-outside-exists",
  646. prepare: func(base string) ([]string, string, string, error) {
  647. volpath, _ := getTestPaths(base)
  648. subpathDir := filepath.Join(volpath, "dir0")
  649. child := filepath.Join(base, "child0")
  650. subpath := filepath.Join(subpathDir, "child0")
  651. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  652. return nil, "", "", err
  653. }
  654. // touch file outside
  655. if err := ioutil.WriteFile(child, []byte{}, defaultPerm); err != nil {
  656. return nil, "", "", err
  657. }
  658. // create symlink for subpath dir
  659. return nil, volpath, subpath, os.Symlink(base, subpathDir)
  660. },
  661. expectError: true,
  662. },
  663. {
  664. name: "subpath-child-outside-not-exists",
  665. prepare: func(base string) ([]string, string, string, error) {
  666. volpath, _ := getTestPaths(base)
  667. subpathDir := filepath.Join(volpath, "dir0")
  668. subpath := filepath.Join(subpathDir, "child0")
  669. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  670. return nil, "", "", err
  671. }
  672. // create symlink for subpath dir
  673. return nil, volpath, subpath, os.Symlink(base, subpathDir)
  674. },
  675. expectError: true,
  676. },
  677. {
  678. name: "subpath-child-outside-exists-middle-dir-symlink",
  679. prepare: func(base string) ([]string, string, string, error) {
  680. volpath, _ := getTestPaths(base)
  681. subpathDir := filepath.Join(volpath, "dir0")
  682. symlinkDir := filepath.Join(subpathDir, "linkDir0")
  683. child := filepath.Join(base, "child0")
  684. subpath := filepath.Join(symlinkDir, "child0")
  685. if err := os.MkdirAll(subpathDir, defaultPerm); err != nil {
  686. return nil, "", "", err
  687. }
  688. // touch file outside
  689. if err := ioutil.WriteFile(child, []byte{}, defaultPerm); err != nil {
  690. return nil, "", "", err
  691. }
  692. // create symlink for middle dir
  693. return nil, volpath, subpath, os.Symlink(base, symlinkDir)
  694. },
  695. expectError: true,
  696. },
  697. {
  698. name: "subpath-backstepping",
  699. prepare: func(base string) ([]string, string, string, error) {
  700. volpath, _ := getTestPaths(base)
  701. subpath := filepath.Join(volpath, "dir0")
  702. symlinkBase := filepath.Join(volpath, "..")
  703. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  704. return nil, "", "", err
  705. }
  706. // create symlink for subpath
  707. return nil, volpath, subpath, os.Symlink(symlinkBase, subpath)
  708. },
  709. expectError: true,
  710. },
  711. {
  712. name: "subpath-mountdir-already-exists",
  713. prepare: func(base string) ([]string, string, string, error) {
  714. volpath, subpathMount := getTestPaths(base)
  715. if err := os.MkdirAll(subpathMount, defaultPerm); err != nil {
  716. return nil, "", "", err
  717. }
  718. subpath := filepath.Join(volpath, "dir0")
  719. return nil, volpath, subpath, os.MkdirAll(subpath, defaultPerm)
  720. },
  721. expectError: false,
  722. },
  723. {
  724. name: "subpath-mount-already-exists",
  725. prepare: func(base string) ([]string, string, string, error) {
  726. volpath, subpathMount := getTestPaths(base)
  727. mounts := []string{subpathMount}
  728. if err := os.MkdirAll(subpathMount, defaultPerm); err != nil {
  729. return nil, "", "", err
  730. }
  731. subpath := filepath.Join(volpath, "dir0")
  732. return mounts, volpath, subpath, os.MkdirAll(subpath, defaultPerm)
  733. },
  734. expectError: false,
  735. },
  736. {
  737. name: "mount-unix-socket",
  738. prepare: func(base string) ([]string, string, string, error) {
  739. volpath, subpathMount := getTestPaths(base)
  740. mounts := []string{subpathMount}
  741. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  742. return nil, "", "", err
  743. }
  744. socketFile, socketCreateError := createSocketFile(volpath)
  745. return mounts, volpath, socketFile, socketCreateError
  746. },
  747. expectError: false,
  748. },
  749. {
  750. name: "subpath-mounting-fifo",
  751. prepare: func(base string) ([]string, string, string, error) {
  752. volpath, subpathMount := getTestPaths(base)
  753. mounts := []string{subpathMount}
  754. if err := os.MkdirAll(volpath, defaultPerm); err != nil {
  755. return nil, "", "", err
  756. }
  757. testFifo := filepath.Join(volpath, "mount_test.fifo")
  758. err := syscall.Mkfifo(testFifo, 0)
  759. return mounts, volpath, testFifo, err
  760. },
  761. expectError: false,
  762. },
  763. }
  764. for _, test := range tests {
  765. klog.V(4).Infof("test %q", test.name)
  766. base, err := ioutil.TempDir("", "bind-subpath-"+test.name+"-")
  767. if err != nil {
  768. t.Fatalf(err.Error())
  769. }
  770. mounts, volPath, subPath, err := test.prepare(base)
  771. if err != nil {
  772. os.RemoveAll(base)
  773. t.Fatalf("failed to prepare test %q: %v", test.name, err.Error())
  774. }
  775. fm := setupFakeMounter(mounts)
  776. subpath := Subpath{
  777. VolumeMountIndex: testSubpath,
  778. Path: subPath,
  779. VolumeName: testVol,
  780. VolumePath: volPath,
  781. PodDir: filepath.Join(base, "pod0"),
  782. ContainerName: testContainer,
  783. }
  784. _, subpathMount := getTestPaths(base)
  785. bindPathTarget, err := doBindSubPath(fm, subpath)
  786. if test.expectError {
  787. if err == nil {
  788. t.Errorf("test %q failed: expected error, got success", test.name)
  789. }
  790. if bindPathTarget != "" {
  791. t.Errorf("test %q failed: expected empty bindPathTarget, got %v", test.name, bindPathTarget)
  792. }
  793. if err = validateDirNotExists(subpathMount); err != nil {
  794. t.Errorf("test %q failed: %v", test.name, err)
  795. }
  796. }
  797. if !test.expectError {
  798. if err != nil {
  799. t.Errorf("test %q failed: %v", test.name, err)
  800. }
  801. if bindPathTarget != subpathMount {
  802. t.Errorf("test %q failed: expected bindPathTarget %v, got %v", test.name, subpathMount, bindPathTarget)
  803. }
  804. if err = validateFileExists(subpathMount); err != nil {
  805. t.Errorf("test %q failed: %v", test.name, err)
  806. }
  807. }
  808. os.RemoveAll(base)
  809. }
  810. }
  811. func TestSafeOpen(t *testing.T) {
  812. defaultPerm := os.FileMode(0750)
  813. tests := []struct {
  814. name string
  815. // Function that prepares directory structure for the test under given
  816. // base.
  817. prepare func(base string) error
  818. path string
  819. expectError bool
  820. }{
  821. {
  822. "directory-does-not-exist",
  823. func(base string) error {
  824. return nil
  825. },
  826. "test/directory",
  827. true,
  828. },
  829. {
  830. "directory-exists",
  831. func(base string) error {
  832. return os.MkdirAll(filepath.Join(base, "test/directory"), 0750)
  833. },
  834. "test/directory",
  835. false,
  836. },
  837. {
  838. "escape-base-using-dots",
  839. func(base string) error {
  840. return nil
  841. },
  842. "..",
  843. true,
  844. },
  845. {
  846. "escape-base-using-dots-2",
  847. func(base string) error {
  848. return os.MkdirAll(filepath.Join(base, "test"), 0750)
  849. },
  850. "test/../../..",
  851. true,
  852. },
  853. {
  854. "symlink",
  855. func(base string) error {
  856. if err := os.MkdirAll(filepath.Join(base, "destination"), defaultPerm); err != nil {
  857. return err
  858. }
  859. return os.Symlink("destination", filepath.Join(base, "test"))
  860. },
  861. "test",
  862. true,
  863. },
  864. {
  865. "symlink-nested",
  866. func(base string) error {
  867. if err := os.MkdirAll(filepath.Join(base, "dir1/dir2"), defaultPerm); err != nil {
  868. return err
  869. }
  870. return os.Symlink("dir1", filepath.Join(base, "dir1/dir2/test"))
  871. },
  872. "test",
  873. true,
  874. },
  875. {
  876. "symlink-loop",
  877. func(base string) error {
  878. return os.Symlink("test", filepath.Join(base, "test"))
  879. },
  880. "test",
  881. true,
  882. },
  883. {
  884. "symlink-not-exists",
  885. func(base string) error {
  886. return os.Symlink("non-existing", filepath.Join(base, "test"))
  887. },
  888. "test",
  889. true,
  890. },
  891. {
  892. "non-directory",
  893. func(base string) error {
  894. return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm)
  895. },
  896. "test/directory",
  897. true,
  898. },
  899. {
  900. "non-directory-final",
  901. func(base string) error {
  902. return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm)
  903. },
  904. "test",
  905. false,
  906. },
  907. {
  908. "escape-with-relative-symlink",
  909. func(base string) error {
  910. if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil {
  911. return err
  912. }
  913. if err := os.MkdirAll(filepath.Join(base, "exists"), defaultPerm); err != nil {
  914. return err
  915. }
  916. return os.Symlink("../exists", filepath.Join(base, "dir/test"))
  917. },
  918. "dir/test",
  919. true,
  920. },
  921. {
  922. "escape-with-relative-symlink-not-exists",
  923. func(base string) error {
  924. if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil {
  925. return err
  926. }
  927. return os.Symlink("../not-exists", filepath.Join(base, "dir/test"))
  928. },
  929. "dir/test",
  930. true,
  931. },
  932. {
  933. "escape-with-symlink",
  934. func(base string) error {
  935. return os.Symlink("/", filepath.Join(base, "test"))
  936. },
  937. "test",
  938. true,
  939. },
  940. {
  941. "mount-unix-socket",
  942. func(base string) error {
  943. socketFile, socketError := createSocketFile(base)
  944. if socketError != nil {
  945. return fmt.Errorf("Error preparing socket file %s with %v", socketFile, socketError)
  946. }
  947. return nil
  948. },
  949. "mt.sock",
  950. false,
  951. },
  952. {
  953. "mounting-unix-socket-in-middle",
  954. func(base string) error {
  955. testSocketFile, socketError := createSocketFile(base)
  956. if socketError != nil {
  957. return fmt.Errorf("Error preparing socket file %s with %v", testSocketFile, socketError)
  958. }
  959. return nil
  960. },
  961. "mt.sock/bar",
  962. true,
  963. },
  964. }
  965. for _, test := range tests {
  966. klog.V(4).Infof("test %q", test.name)
  967. base, err := ioutil.TempDir("", "safe-open-"+test.name+"-")
  968. if err != nil {
  969. t.Fatalf(err.Error())
  970. }
  971. test.prepare(base)
  972. pathToCreate := filepath.Join(base, test.path)
  973. fd, err := doSafeOpen(pathToCreate, base)
  974. if err != nil && !test.expectError {
  975. t.Errorf("test %q: %s", test.name, err)
  976. }
  977. if err != nil {
  978. klog.Infof("got error: %s", err)
  979. }
  980. if err == nil && test.expectError {
  981. t.Errorf("test %q: expected error, got none", test.name)
  982. }
  983. syscall.Close(fd)
  984. os.RemoveAll(base)
  985. }
  986. }
  987. func createSocketFile(socketDir string) (string, error) {
  988. testSocketFile := filepath.Join(socketDir, "mt.sock")
  989. // Switch to volume path and create the socket file
  990. // socket file can not have length of more than 108 character
  991. // and hence we must use relative path
  992. oldDir, _ := os.Getwd()
  993. err := os.Chdir(socketDir)
  994. if err != nil {
  995. return "", err
  996. }
  997. defer func() {
  998. os.Chdir(oldDir)
  999. }()
  1000. _, socketCreateError := net.Listen("unix", "mt.sock")
  1001. return testSocketFile, socketCreateError
  1002. }
  1003. func TestFindExistingPrefix(t *testing.T) {
  1004. defaultPerm := os.FileMode(0750)
  1005. tests := []struct {
  1006. name string
  1007. // Function that prepares directory structure for the test under given
  1008. // base.
  1009. prepare func(base string) error
  1010. path string
  1011. expectedPath string
  1012. expectedDirs []string
  1013. expectError bool
  1014. }{
  1015. {
  1016. "directory-does-not-exist",
  1017. func(base string) error {
  1018. return nil
  1019. },
  1020. "directory",
  1021. "",
  1022. []string{"directory"},
  1023. false,
  1024. },
  1025. {
  1026. "directory-exists",
  1027. func(base string) error {
  1028. return os.MkdirAll(filepath.Join(base, "test/directory"), 0750)
  1029. },
  1030. "test/directory",
  1031. "test/directory",
  1032. []string{},
  1033. false,
  1034. },
  1035. {
  1036. "follow-symlinks",
  1037. func(base string) error {
  1038. if err := os.MkdirAll(filepath.Join(base, "destination/directory"), defaultPerm); err != nil {
  1039. return err
  1040. }
  1041. return os.Symlink("destination", filepath.Join(base, "test"))
  1042. },
  1043. "test/directory",
  1044. "test/directory",
  1045. []string{},
  1046. false,
  1047. },
  1048. {
  1049. "follow-symlink-loop",
  1050. func(base string) error {
  1051. return os.Symlink("test", filepath.Join(base, "test"))
  1052. },
  1053. "test/directory",
  1054. "",
  1055. nil,
  1056. true,
  1057. },
  1058. {
  1059. "follow-symlink-multiple follow",
  1060. func(base string) error {
  1061. /* test1/dir points to test2 and test2/dir points to test1 */
  1062. if err := os.MkdirAll(filepath.Join(base, "test1"), defaultPerm); err != nil {
  1063. return err
  1064. }
  1065. if err := os.MkdirAll(filepath.Join(base, "test2"), defaultPerm); err != nil {
  1066. return err
  1067. }
  1068. if err := os.Symlink(filepath.Join(base, "test2"), filepath.Join(base, "test1/dir")); err != nil {
  1069. return err
  1070. }
  1071. if err := os.Symlink(filepath.Join(base, "test1"), filepath.Join(base, "test2/dir")); err != nil {
  1072. return err
  1073. }
  1074. return nil
  1075. },
  1076. "test1/dir/dir/foo/bar",
  1077. "test1/dir/dir",
  1078. []string{"foo", "bar"},
  1079. false,
  1080. },
  1081. {
  1082. "danglink-symlink",
  1083. func(base string) error {
  1084. return os.Symlink("non-existing", filepath.Join(base, "test"))
  1085. },
  1086. // OS returns IsNotExist error both for dangling symlink and for
  1087. // non-existing directory.
  1088. "test/directory",
  1089. "",
  1090. []string{"test", "directory"},
  1091. false,
  1092. },
  1093. {
  1094. "with-fifo-in-middle",
  1095. func(base string) error {
  1096. testFifo := filepath.Join(base, "mount_test.fifo")
  1097. return syscall.Mkfifo(testFifo, 0)
  1098. },
  1099. "mount_test.fifo/directory",
  1100. "",
  1101. nil,
  1102. true,
  1103. },
  1104. }
  1105. for _, test := range tests {
  1106. klog.V(4).Infof("test %q", test.name)
  1107. base, err := ioutil.TempDir("", "find-prefix-"+test.name+"-")
  1108. if err != nil {
  1109. t.Fatalf(err.Error())
  1110. }
  1111. test.prepare(base)
  1112. path := filepath.Join(base, test.path)
  1113. existingPath, dirs, err := findExistingPrefix(base, path)
  1114. if err != nil && !test.expectError {
  1115. t.Errorf("test %q: %s", test.name, err)
  1116. }
  1117. if err != nil {
  1118. klog.Infof("got error: %s", err)
  1119. }
  1120. if err == nil && test.expectError {
  1121. t.Errorf("test %q: expected error, got none", test.name)
  1122. }
  1123. fullExpectedPath := filepath.Join(base, test.expectedPath)
  1124. if existingPath != fullExpectedPath {
  1125. t.Errorf("test %q: expected path %q, got %q", test.name, fullExpectedPath, existingPath)
  1126. }
  1127. if !reflect.DeepEqual(dirs, test.expectedDirs) {
  1128. t.Errorf("test %q: expected dirs %v, got %v", test.name, test.expectedDirs, dirs)
  1129. }
  1130. os.RemoveAll(base)
  1131. }
  1132. }
  1133. func validateDirEmpty(dir string) error {
  1134. files, err := ioutil.ReadDir(dir)
  1135. if err != nil {
  1136. return err
  1137. }
  1138. if len(files) != 0 {
  1139. return fmt.Errorf("Directory %q is not empty", dir)
  1140. }
  1141. return nil
  1142. }
  1143. func validateDirExists(dir string) error {
  1144. _, err := ioutil.ReadDir(dir)
  1145. if err != nil {
  1146. return err
  1147. }
  1148. return nil
  1149. }
  1150. func validateDirNotExists(dir string) error {
  1151. _, err := ioutil.ReadDir(dir)
  1152. if os.IsNotExist(err) {
  1153. return nil
  1154. }
  1155. if err != nil {
  1156. return err
  1157. }
  1158. return fmt.Errorf("dir %q still exists", dir)
  1159. }
  1160. func validateFileExists(file string) error {
  1161. if _, err := os.Stat(file); err != nil {
  1162. return err
  1163. }
  1164. return nil
  1165. }