Tizen_4.0 base
[platform/upstream/docker-engine.git] / pkg / symlink / fs_unix_test.go
1 // +build !windows
2
3 // Licensed under the Apache License, Version 2.0; See LICENSE.APACHE
4
5 package symlink
6
7 import (
8         "fmt"
9         "io/ioutil"
10         "os"
11         "path/filepath"
12         "testing"
13 )
14
15 // TODO Windows: This needs some serious work to port to Windows. For now,
16 // turning off testing in this package.
17
18 type dirOrLink struct {
19         path   string
20         target string
21 }
22
23 func makeFs(tmpdir string, fs []dirOrLink) error {
24         for _, s := range fs {
25                 s.path = filepath.Join(tmpdir, s.path)
26                 if s.target == "" {
27                         os.MkdirAll(s.path, 0755)
28                         continue
29                 }
30                 if err := os.MkdirAll(filepath.Dir(s.path), 0755); err != nil {
31                         return err
32                 }
33                 if err := os.Symlink(s.target, s.path); err != nil && !os.IsExist(err) {
34                         return err
35                 }
36         }
37         return nil
38 }
39
40 func testSymlink(tmpdir, path, expected, scope string) error {
41         rewrite, err := FollowSymlinkInScope(filepath.Join(tmpdir, path), filepath.Join(tmpdir, scope))
42         if err != nil {
43                 return err
44         }
45         expected, err = filepath.Abs(filepath.Join(tmpdir, expected))
46         if err != nil {
47                 return err
48         }
49         if expected != rewrite {
50                 return fmt.Errorf("Expected %q got %q", expected, rewrite)
51         }
52         return nil
53 }
54
55 func TestFollowSymlinkAbsolute(t *testing.T) {
56         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkAbsolute")
57         if err != nil {
58                 t.Fatal(err)
59         }
60         defer os.RemoveAll(tmpdir)
61         if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil {
62                 t.Fatal(err)
63         }
64         if err := testSymlink(tmpdir, "testdata/fs/a/d/c/data", "testdata/b/c/data", "testdata"); err != nil {
65                 t.Fatal(err)
66         }
67 }
68
69 func TestFollowSymlinkRelativePath(t *testing.T) {
70         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativePath")
71         if err != nil {
72                 t.Fatal(err)
73         }
74         defer os.RemoveAll(tmpdir)
75         if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/i", target: "a"}}); err != nil {
76                 t.Fatal(err)
77         }
78         if err := testSymlink(tmpdir, "testdata/fs/i", "testdata/fs/a", "testdata"); err != nil {
79                 t.Fatal(err)
80         }
81 }
82
83 func TestFollowSymlinkSkipSymlinksOutsideScope(t *testing.T) {
84         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkSkipSymlinksOutsideScope")
85         if err != nil {
86                 t.Fatal(err)
87         }
88         defer os.RemoveAll(tmpdir)
89         if err := makeFs(tmpdir, []dirOrLink{
90                 {path: "linkdir", target: "realdir"},
91                 {path: "linkdir/foo/bar"},
92         }); err != nil {
93                 t.Fatal(err)
94         }
95         if err := testSymlink(tmpdir, "linkdir/foo/bar", "linkdir/foo/bar", "linkdir/foo"); err != nil {
96                 t.Fatal(err)
97         }
98 }
99
100 func TestFollowSymlinkInvalidScopePathPair(t *testing.T) {
101         if _, err := FollowSymlinkInScope("toto", "testdata"); err == nil {
102                 t.Fatal("expected an error")
103         }
104 }
105
106 func TestFollowSymlinkLastLink(t *testing.T) {
107         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkLastLink")
108         if err != nil {
109                 t.Fatal(err)
110         }
111         defer os.RemoveAll(tmpdir)
112         if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil {
113                 t.Fatal(err)
114         }
115         if err := testSymlink(tmpdir, "testdata/fs/a/d", "testdata/b", "testdata"); err != nil {
116                 t.Fatal(err)
117         }
118 }
119
120 func TestFollowSymlinkRelativeLinkChangeScope(t *testing.T) {
121         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativeLinkChangeScope")
122         if err != nil {
123                 t.Fatal(err)
124         }
125         defer os.RemoveAll(tmpdir)
126         if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/e", target: "../b"}}); err != nil {
127                 t.Fatal(err)
128         }
129         if err := testSymlink(tmpdir, "testdata/fs/a/e/c/data", "testdata/fs/b/c/data", "testdata"); err != nil {
130                 t.Fatal(err)
131         }
132         // avoid letting allowing symlink e lead us to ../b
133         // normalize to the "testdata/fs/a"
134         if err := testSymlink(tmpdir, "testdata/fs/a/e", "testdata/fs/a/b", "testdata/fs/a"); err != nil {
135                 t.Fatal(err)
136         }
137 }
138
139 func TestFollowSymlinkDeepRelativeLinkChangeScope(t *testing.T) {
140         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkDeepRelativeLinkChangeScope")
141         if err != nil {
142                 t.Fatal(err)
143         }
144         defer os.RemoveAll(tmpdir)
145
146         if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/f", target: "../../../../test"}}); err != nil {
147                 t.Fatal(err)
148         }
149         // avoid letting symlink f lead us out of the "testdata" scope
150         // we don't normalize because symlink f is in scope and there is no
151         // information leak
152         if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/test", "testdata"); err != nil {
153                 t.Fatal(err)
154         }
155         // avoid letting symlink f lead us out of the "testdata/fs" scope
156         // we don't normalize because symlink f is in scope and there is no
157         // information leak
158         if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/fs/test", "testdata/fs"); err != nil {
159                 t.Fatal(err)
160         }
161 }
162
163 func TestFollowSymlinkRelativeLinkChain(t *testing.T) {
164         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativeLinkChain")
165         if err != nil {
166                 t.Fatal(err)
167         }
168         defer os.RemoveAll(tmpdir)
169
170         // avoid letting symlink g (pointed at by symlink h) take out of scope
171         // TODO: we should probably normalize to scope here because ../[....]/root
172         // is out of scope and we leak information
173         if err := makeFs(tmpdir, []dirOrLink{
174                 {path: "testdata/fs/b/h", target: "../g"},
175                 {path: "testdata/fs/g", target: "../../../../../../../../../../../../root"},
176         }); err != nil {
177                 t.Fatal(err)
178         }
179         if err := testSymlink(tmpdir, "testdata/fs/b/h", "testdata/root", "testdata"); err != nil {
180                 t.Fatal(err)
181         }
182 }
183
184 func TestFollowSymlinkBreakoutPath(t *testing.T) {
185         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkBreakoutPath")
186         if err != nil {
187                 t.Fatal(err)
188         }
189         defer os.RemoveAll(tmpdir)
190
191         // avoid letting symlink -> ../directory/file escape from scope
192         // normalize to "testdata/fs/j"
193         if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/j/k", target: "../i/a"}}); err != nil {
194                 t.Fatal(err)
195         }
196         if err := testSymlink(tmpdir, "testdata/fs/j/k", "testdata/fs/j/i/a", "testdata/fs/j"); err != nil {
197                 t.Fatal(err)
198         }
199 }
200
201 func TestFollowSymlinkToRoot(t *testing.T) {
202         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkToRoot")
203         if err != nil {
204                 t.Fatal(err)
205         }
206         defer os.RemoveAll(tmpdir)
207
208         // make sure we don't allow escaping to /
209         // normalize to dir
210         if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/"}}); err != nil {
211                 t.Fatal(err)
212         }
213         if err := testSymlink(tmpdir, "foo", "", ""); err != nil {
214                 t.Fatal(err)
215         }
216 }
217
218 func TestFollowSymlinkSlashDotdot(t *testing.T) {
219         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkSlashDotdot")
220         if err != nil {
221                 t.Fatal(err)
222         }
223         defer os.RemoveAll(tmpdir)
224         tmpdir = filepath.Join(tmpdir, "dir", "subdir")
225
226         // make sure we don't allow escaping to /
227         // normalize to dir
228         if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/../../"}}); err != nil {
229                 t.Fatal(err)
230         }
231         if err := testSymlink(tmpdir, "foo", "", ""); err != nil {
232                 t.Fatal(err)
233         }
234 }
235
236 func TestFollowSymlinkDotdot(t *testing.T) {
237         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkDotdot")
238         if err != nil {
239                 t.Fatal(err)
240         }
241         defer os.RemoveAll(tmpdir)
242         tmpdir = filepath.Join(tmpdir, "dir", "subdir")
243
244         // make sure we stay in scope without leaking information
245         // this also checks for escaping to /
246         // normalize to dir
247         if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "../../"}}); err != nil {
248                 t.Fatal(err)
249         }
250         if err := testSymlink(tmpdir, "foo", "", ""); err != nil {
251                 t.Fatal(err)
252         }
253 }
254
255 func TestFollowSymlinkRelativePath2(t *testing.T) {
256         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativePath2")
257         if err != nil {
258                 t.Fatal(err)
259         }
260         defer os.RemoveAll(tmpdir)
261
262         if err := makeFs(tmpdir, []dirOrLink{{path: "bar/foo", target: "baz/target"}}); err != nil {
263                 t.Fatal(err)
264         }
265         if err := testSymlink(tmpdir, "bar/foo", "bar/baz/target", ""); err != nil {
266                 t.Fatal(err)
267         }
268 }
269
270 func TestFollowSymlinkScopeLink(t *testing.T) {
271         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkScopeLink")
272         if err != nil {
273                 t.Fatal(err)
274         }
275         defer os.RemoveAll(tmpdir)
276
277         if err := makeFs(tmpdir, []dirOrLink{
278                 {path: "root2"},
279                 {path: "root", target: "root2"},
280                 {path: "root2/foo", target: "../bar"},
281         }); err != nil {
282                 t.Fatal(err)
283         }
284         if err := testSymlink(tmpdir, "root/foo", "root/bar", "root"); err != nil {
285                 t.Fatal(err)
286         }
287 }
288
289 func TestFollowSymlinkRootScope(t *testing.T) {
290         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRootScope")
291         if err != nil {
292                 t.Fatal(err)
293         }
294         defer os.RemoveAll(tmpdir)
295
296         expected, err := filepath.EvalSymlinks(tmpdir)
297         if err != nil {
298                 t.Fatal(err)
299         }
300         rewrite, err := FollowSymlinkInScope(tmpdir, "/")
301         if err != nil {
302                 t.Fatal(err)
303         }
304         if rewrite != expected {
305                 t.Fatalf("expected %q got %q", expected, rewrite)
306         }
307 }
308
309 func TestFollowSymlinkEmpty(t *testing.T) {
310         res, err := FollowSymlinkInScope("", "")
311         if err != nil {
312                 t.Fatal(err)
313         }
314         wd, err := os.Getwd()
315         if err != nil {
316                 t.Fatal(err)
317         }
318         if res != wd {
319                 t.Fatalf("expected %q got %q", wd, res)
320         }
321 }
322
323 func TestFollowSymlinkCircular(t *testing.T) {
324         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkCircular")
325         if err != nil {
326                 t.Fatal(err)
327         }
328         defer os.RemoveAll(tmpdir)
329
330         if err := makeFs(tmpdir, []dirOrLink{{path: "root/foo", target: "foo"}}); err != nil {
331                 t.Fatal(err)
332         }
333         if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil {
334                 t.Fatal("expected an error for foo -> foo")
335         }
336
337         if err := makeFs(tmpdir, []dirOrLink{
338                 {path: "root/bar", target: "baz"},
339                 {path: "root/baz", target: "../bak"},
340                 {path: "root/bak", target: "/bar"},
341         }); err != nil {
342                 t.Fatal(err)
343         }
344         if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil {
345                 t.Fatal("expected an error for bar -> baz -> bak -> bar")
346         }
347 }
348
349 func TestFollowSymlinkComplexChainWithTargetPathsContainingLinks(t *testing.T) {
350         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkComplexChainWithTargetPathsContainingLinks")
351         if err != nil {
352                 t.Fatal(err)
353         }
354         defer os.RemoveAll(tmpdir)
355
356         if err := makeFs(tmpdir, []dirOrLink{
357                 {path: "root2"},
358                 {path: "root", target: "root2"},
359                 {path: "root/a", target: "r/s"},
360                 {path: "root/r", target: "../root/t"},
361                 {path: "root/root/t/s/b", target: "/../u"},
362                 {path: "root/u/c", target: "."},
363                 {path: "root/u/x/y", target: "../v"},
364                 {path: "root/u/v", target: "/../w"},
365         }); err != nil {
366                 t.Fatal(err)
367         }
368         if err := testSymlink(tmpdir, "root/a/b/c/x/y/z", "root/w/z", "root"); err != nil {
369                 t.Fatal(err)
370         }
371 }
372
373 func TestFollowSymlinkBreakoutNonExistent(t *testing.T) {
374         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkBreakoutNonExistent")
375         if err != nil {
376                 t.Fatal(err)
377         }
378         defer os.RemoveAll(tmpdir)
379
380         if err := makeFs(tmpdir, []dirOrLink{
381                 {path: "root/slash", target: "/"},
382                 {path: "root/sym", target: "/idontexist/../slash"},
383         }); err != nil {
384                 t.Fatal(err)
385         }
386         if err := testSymlink(tmpdir, "root/sym/file", "root/file", "root"); err != nil {
387                 t.Fatal(err)
388         }
389 }
390
391 func TestFollowSymlinkNoLexicalCleaning(t *testing.T) {
392         tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkNoLexicalCleaning")
393         if err != nil {
394                 t.Fatal(err)
395         }
396         defer os.RemoveAll(tmpdir)
397
398         if err := makeFs(tmpdir, []dirOrLink{
399                 {path: "root/sym", target: "/foo/bar"},
400                 {path: "root/hello", target: "/sym/../baz"},
401         }); err != nil {
402                 t.Fatal(err)
403         }
404         if err := testSymlink(tmpdir, "root/hello", "root/foo/baz", "root"); err != nil {
405                 t.Fatal(err)
406         }
407 }