Signed-off-by: Andrew Page <admwiggin@gmail.com>
Tianon Gravi authored on 2014/12/06 06:58:461 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,191 @@ |
0 |
+ |
|
1 |
+ Apache License |
|
2 |
+ Version 2.0, January 2004 |
|
3 |
+ http://www.apache.org/licenses/ |
|
4 |
+ |
|
5 |
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|
6 |
+ |
|
7 |
+ 1. Definitions. |
|
8 |
+ |
|
9 |
+ "License" shall mean the terms and conditions for use, reproduction, |
|
10 |
+ and distribution as defined by Sections 1 through 9 of this document. |
|
11 |
+ |
|
12 |
+ "Licensor" shall mean the copyright owner or entity authorized by |
|
13 |
+ the copyright owner that is granting the License. |
|
14 |
+ |
|
15 |
+ "Legal Entity" shall mean the union of the acting entity and all |
|
16 |
+ other entities that control, are controlled by, or are under common |
|
17 |
+ control with that entity. For the purposes of this definition, |
|
18 |
+ "control" means (i) the power, direct or indirect, to cause the |
|
19 |
+ direction or management of such entity, whether by contract or |
|
20 |
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|
21 |
+ outstanding shares, or (iii) beneficial ownership of such entity. |
|
22 |
+ |
|
23 |
+ "You" (or "Your") shall mean an individual or Legal Entity |
|
24 |
+ exercising permissions granted by this License. |
|
25 |
+ |
|
26 |
+ "Source" form shall mean the preferred form for making modifications, |
|
27 |
+ including but not limited to software source code, documentation |
|
28 |
+ source, and configuration files. |
|
29 |
+ |
|
30 |
+ "Object" form shall mean any form resulting from mechanical |
|
31 |
+ transformation or translation of a Source form, including but |
|
32 |
+ not limited to compiled object code, generated documentation, |
|
33 |
+ and conversions to other media types. |
|
34 |
+ |
|
35 |
+ "Work" shall mean the work of authorship, whether in Source or |
|
36 |
+ Object form, made available under the License, as indicated by a |
|
37 |
+ copyright notice that is included in or attached to the work |
|
38 |
+ (an example is provided in the Appendix below). |
|
39 |
+ |
|
40 |
+ "Derivative Works" shall mean any work, whether in Source or Object |
|
41 |
+ form, that is based on (or derived from) the Work and for which the |
|
42 |
+ editorial revisions, annotations, elaborations, or other modifications |
|
43 |
+ represent, as a whole, an original work of authorship. For the purposes |
|
44 |
+ of this License, Derivative Works shall not include works that remain |
|
45 |
+ separable from, or merely link (or bind by name) to the interfaces of, |
|
46 |
+ the Work and Derivative Works thereof. |
|
47 |
+ |
|
48 |
+ "Contribution" shall mean any work of authorship, including |
|
49 |
+ the original version of the Work and any modifications or additions |
|
50 |
+ to that Work or Derivative Works thereof, that is intentionally |
|
51 |
+ submitted to Licensor for inclusion in the Work by the copyright owner |
|
52 |
+ or by an individual or Legal Entity authorized to submit on behalf of |
|
53 |
+ the copyright owner. For the purposes of this definition, "submitted" |
|
54 |
+ means any form of electronic, verbal, or written communication sent |
|
55 |
+ to the Licensor or its representatives, including but not limited to |
|
56 |
+ communication on electronic mailing lists, source code control systems, |
|
57 |
+ and issue tracking systems that are managed by, or on behalf of, the |
|
58 |
+ Licensor for the purpose of discussing and improving the Work, but |
|
59 |
+ excluding communication that is conspicuously marked or otherwise |
|
60 |
+ designated in writing by the copyright owner as "Not a Contribution." |
|
61 |
+ |
|
62 |
+ "Contributor" shall mean Licensor and any individual or Legal Entity |
|
63 |
+ on behalf of whom a Contribution has been received by Licensor and |
|
64 |
+ subsequently incorporated within the Work. |
|
65 |
+ |
|
66 |
+ 2. Grant of Copyright License. Subject to the terms and conditions of |
|
67 |
+ this License, each Contributor hereby grants to You a perpetual, |
|
68 |
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|
69 |
+ copyright license to reproduce, prepare Derivative Works of, |
|
70 |
+ publicly display, publicly perform, sublicense, and distribute the |
|
71 |
+ Work and such Derivative Works in Source or Object form. |
|
72 |
+ |
|
73 |
+ 3. Grant of Patent License. Subject to the terms and conditions of |
|
74 |
+ this License, each Contributor hereby grants to You a perpetual, |
|
75 |
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|
76 |
+ (except as stated in this section) patent license to make, have made, |
|
77 |
+ use, offer to sell, sell, import, and otherwise transfer the Work, |
|
78 |
+ where such license applies only to those patent claims licensable |
|
79 |
+ by such Contributor that are necessarily infringed by their |
|
80 |
+ Contribution(s) alone or by combination of their Contribution(s) |
|
81 |
+ with the Work to which such Contribution(s) was submitted. If You |
|
82 |
+ institute patent litigation against any entity (including a |
|
83 |
+ cross-claim or counterclaim in a lawsuit) alleging that the Work |
|
84 |
+ or a Contribution incorporated within the Work constitutes direct |
|
85 |
+ or contributory patent infringement, then any patent licenses |
|
86 |
+ granted to You under this License for that Work shall terminate |
|
87 |
+ as of the date such litigation is filed. |
|
88 |
+ |
|
89 |
+ 4. Redistribution. You may reproduce and distribute copies of the |
|
90 |
+ Work or Derivative Works thereof in any medium, with or without |
|
91 |
+ modifications, and in Source or Object form, provided that You |
|
92 |
+ meet the following conditions: |
|
93 |
+ |
|
94 |
+ (a) You must give any other recipients of the Work or |
|
95 |
+ Derivative Works a copy of this License; and |
|
96 |
+ |
|
97 |
+ (b) You must cause any modified files to carry prominent notices |
|
98 |
+ stating that You changed the files; and |
|
99 |
+ |
|
100 |
+ (c) You must retain, in the Source form of any Derivative Works |
|
101 |
+ that You distribute, all copyright, patent, trademark, and |
|
102 |
+ attribution notices from the Source form of the Work, |
|
103 |
+ excluding those notices that do not pertain to any part of |
|
104 |
+ the Derivative Works; and |
|
105 |
+ |
|
106 |
+ (d) If the Work includes a "NOTICE" text file as part of its |
|
107 |
+ distribution, then any Derivative Works that You distribute must |
|
108 |
+ include a readable copy of the attribution notices contained |
|
109 |
+ within such NOTICE file, excluding those notices that do not |
|
110 |
+ pertain to any part of the Derivative Works, in at least one |
|
111 |
+ of the following places: within a NOTICE text file distributed |
|
112 |
+ as part of the Derivative Works; within the Source form or |
|
113 |
+ documentation, if provided along with the Derivative Works; or, |
|
114 |
+ within a display generated by the Derivative Works, if and |
|
115 |
+ wherever such third-party notices normally appear. The contents |
|
116 |
+ of the NOTICE file are for informational purposes only and |
|
117 |
+ do not modify the License. You may add Your own attribution |
|
118 |
+ notices within Derivative Works that You distribute, alongside |
|
119 |
+ or as an addendum to the NOTICE text from the Work, provided |
|
120 |
+ that such additional attribution notices cannot be construed |
|
121 |
+ as modifying the License. |
|
122 |
+ |
|
123 |
+ You may add Your own copyright statement to Your modifications and |
|
124 |
+ may provide additional or different license terms and conditions |
|
125 |
+ for use, reproduction, or distribution of Your modifications, or |
|
126 |
+ for any such Derivative Works as a whole, provided Your use, |
|
127 |
+ reproduction, and distribution of the Work otherwise complies with |
|
128 |
+ the conditions stated in this License. |
|
129 |
+ |
|
130 |
+ 5. Submission of Contributions. Unless You explicitly state otherwise, |
|
131 |
+ any Contribution intentionally submitted for inclusion in the Work |
|
132 |
+ by You to the Licensor shall be under the terms and conditions of |
|
133 |
+ this License, without any additional terms or conditions. |
|
134 |
+ Notwithstanding the above, nothing herein shall supersede or modify |
|
135 |
+ the terms of any separate license agreement you may have executed |
|
136 |
+ with Licensor regarding such Contributions. |
|
137 |
+ |
|
138 |
+ 6. Trademarks. This License does not grant permission to use the trade |
|
139 |
+ names, trademarks, service marks, or product names of the Licensor, |
|
140 |
+ except as required for reasonable and customary use in describing the |
|
141 |
+ origin of the Work and reproducing the content of the NOTICE file. |
|
142 |
+ |
|
143 |
+ 7. Disclaimer of Warranty. Unless required by applicable law or |
|
144 |
+ agreed to in writing, Licensor provides the Work (and each |
|
145 |
+ Contributor provides its Contributions) on an "AS IS" BASIS, |
|
146 |
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|
147 |
+ implied, including, without limitation, any warranties or conditions |
|
148 |
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
|
149 |
+ PARTICULAR PURPOSE. You are solely responsible for determining the |
|
150 |
+ appropriateness of using or redistributing the Work and assume any |
|
151 |
+ risks associated with Your exercise of permissions under this License. |
|
152 |
+ |
|
153 |
+ 8. Limitation of Liability. In no event and under no legal theory, |
|
154 |
+ whether in tort (including negligence), contract, or otherwise, |
|
155 |
+ unless required by applicable law (such as deliberate and grossly |
|
156 |
+ negligent acts) or agreed to in writing, shall any Contributor be |
|
157 |
+ liable to You for damages, including any direct, indirect, special, |
|
158 |
+ incidental, or consequential damages of any character arising as a |
|
159 |
+ result of this License or out of the use or inability to use the |
|
160 |
+ Work (including but not limited to damages for loss of goodwill, |
|
161 |
+ work stoppage, computer failure or malfunction, or any and all |
|
162 |
+ other commercial damages or losses), even if such Contributor |
|
163 |
+ has been advised of the possibility of such damages. |
|
164 |
+ |
|
165 |
+ 9. Accepting Warranty or Additional Liability. While redistributing |
|
166 |
+ the Work or Derivative Works thereof, You may choose to offer, |
|
167 |
+ and charge a fee for, acceptance of support, warranty, indemnity, |
|
168 |
+ or other liability obligations and/or rights consistent with this |
|
169 |
+ License. However, in accepting such obligations, You may act only |
|
170 |
+ on Your own behalf and on Your sole responsibility, not on behalf |
|
171 |
+ of any other Contributor, and only if You agree to indemnify, |
|
172 |
+ defend, and hold each Contributor harmless for any liability |
|
173 |
+ incurred by, or claims asserted against, such Contributor by reason |
|
174 |
+ of your accepting any such warranty or additional liability. |
|
175 |
+ |
|
176 |
+ END OF TERMS AND CONDITIONS |
|
177 |
+ |
|
178 |
+ Copyright 2014 Docker, Inc. |
|
179 |
+ |
|
180 |
+ Licensed under the Apache License, Version 2.0 (the "License"); |
|
181 |
+ you may not use this file except in compliance with the License. |
|
182 |
+ You may obtain a copy of the License at |
|
183 |
+ |
|
184 |
+ http://www.apache.org/licenses/LICENSE-2.0 |
|
185 |
+ |
|
186 |
+ Unless required by applicable law or agreed to in writing, software |
|
187 |
+ distributed under the License is distributed on an "AS IS" BASIS, |
|
188 |
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
189 |
+ See the License for the specific language governing permissions and |
|
190 |
+ limitations under the License. |
0 | 191 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,27 @@ |
0 |
+Copyright (c) 2014 The Docker & Go Authors. All rights reserved. |
|
1 |
+ |
|
2 |
+Redistribution and use in source and binary forms, with or without |
|
3 |
+modification, are permitted provided that the following conditions are |
|
4 |
+met: |
|
5 |
+ |
|
6 |
+ * Redistributions of source code must retain the above copyright |
|
7 |
+notice, this list of conditions and the following disclaimer. |
|
8 |
+ * Redistributions in binary form must reproduce the above |
|
9 |
+copyright notice, this list of conditions and the following disclaimer |
|
10 |
+in the documentation and/or other materials provided with the |
|
11 |
+distribution. |
|
12 |
+ * Neither the name of Google Inc. nor the names of its |
|
13 |
+contributors may be used to endorse or promote products derived from |
|
14 |
+this software without specific prior written permission. |
|
15 |
+ |
|
16 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
17 |
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
18 |
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
19 |
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
20 |
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
23 |
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
24 |
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
25 |
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
26 |
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
3 | 4 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,5 @@ |
0 |
+Package symlink implements EvalSymlinksInScope which is an extension of filepath.EvalSymlinks |
|
1 |
+from the [Go standard library](https://golang.org/pkg/path/filepath). |
|
2 |
+ |
|
3 |
+The code from filepath.EvalSymlinks has been adapted in fs.go. |
|
4 |
+Please read the LICENSE.BSD file that governs fs.go and LICENSE.APACHE for fs_test.go. |
... | ... |
@@ -1,101 +1,131 @@ |
1 |
+// Copyright 2012 The Go Authors. All rights reserved. |
|
2 |
+// Use of this source code is governed by a BSD-style |
|
3 |
+// license that can be found in the LICENSE.BSD file. |
|
4 |
+ |
|
5 |
+// This code is a modified version of path/filepath/symlink.go from the Go standard library. |
|
6 |
+ |
|
1 | 7 |
package symlink |
2 | 8 |
|
3 | 9 |
import ( |
4 |
- "fmt" |
|
10 |
+ "bytes" |
|
11 |
+ "errors" |
|
5 | 12 |
"os" |
6 |
- "path" |
|
7 | 13 |
"path/filepath" |
8 | 14 |
"strings" |
9 | 15 |
) |
10 | 16 |
|
11 |
-const maxLoopCounter = 100 |
|
12 |
- |
|
13 |
-// FollowSymlink will follow an existing link and scope it to the root |
|
14 |
-// path provided. |
|
15 |
-// The role of this function is to return an absolute path in the root |
|
16 |
-// or normalize to the root if the symlink leads to a path which is |
|
17 |
-// outside of the root. |
|
18 |
-// Errors encountered while attempting to follow the symlink in path |
|
19 |
-// will be reported. |
|
20 |
-// Normalizations to the root don't constitute errors. |
|
21 |
-func FollowSymlinkInScope(link, root string) (string, error) { |
|
22 |
- root, err := filepath.Abs(root) |
|
17 |
+// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an absolute path |
|
18 |
+func FollowSymlinkInScope(path, root string) (string, error) { |
|
19 |
+ path, err := filepath.Abs(path) |
|
23 | 20 |
if err != nil { |
24 | 21 |
return "", err |
25 | 22 |
} |
26 |
- |
|
27 |
- link, err = filepath.Abs(link) |
|
23 |
+ root, err = filepath.Abs(root) |
|
28 | 24 |
if err != nil { |
29 | 25 |
return "", err |
30 | 26 |
} |
27 |
+ return evalSymlinksInScope(path, root) |
|
28 |
+} |
|
31 | 29 |
|
32 |
- if link == root { |
|
33 |
- return root, nil |
|
30 |
+// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return |
|
31 |
+// a result guaranteed to be contained within the scope `root`, at the time of the call. |
|
32 |
+// Symlinks in `root` are not evaluated and left as-is. |
|
33 |
+// Errors encountered while attempting to evaluate symlinks in path will be returned. |
|
34 |
+// Non-existing paths are valid and do not constitute an error. |
|
35 |
+// `path` has to contain `root` as a prefix, or else an error will be returned. |
|
36 |
+// Trying to break out from `root` does not constitute an error. |
|
37 |
+// |
|
38 |
+// Example: |
|
39 |
+// If /foo/bar -> /outside, |
|
40 |
+// FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/oustide" |
|
41 |
+// |
|
42 |
+// IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks |
|
43 |
+// are created and not to create subsequently, additional symlinks that could potentially make a |
|
44 |
+// previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo") |
|
45 |
+// would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should |
|
46 |
+// no longer be considered safely contained in "/foo". |
|
47 |
+func evalSymlinksInScope(path, root string) (string, error) { |
|
48 |
+ root = filepath.Clean(root) |
|
49 |
+ if path == root { |
|
50 |
+ return path, nil |
|
34 | 51 |
} |
35 |
- |
|
36 |
- if !strings.HasPrefix(filepath.Dir(link), root) { |
|
37 |
- return "", fmt.Errorf("%s is not within %s", link, root) |
|
52 |
+ if !strings.HasPrefix(path, root) { |
|
53 |
+ return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root) |
|
38 | 54 |
} |
55 |
+ const maxIter = 255 |
|
56 |
+ originalPath := path |
|
57 |
+ // given root of "/a" and path of "/a/b/../../c" we want path to be "/b/../../c" |
|
58 |
+ path = path[len(root):] |
|
59 |
+ if root == string(filepath.Separator) { |
|
60 |
+ path = string(filepath.Separator) + path |
|
61 |
+ } |
|
62 |
+ if !strings.HasPrefix(path, string(filepath.Separator)) { |
|
63 |
+ return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root) |
|
64 |
+ } |
|
65 |
+ path = filepath.Clean(path) |
|
66 |
+ // consume path by taking each frontmost path element, |
|
67 |
+ // expanding it if it's a symlink, and appending it to b |
|
68 |
+ var b bytes.Buffer |
|
69 |
+ // b here will always be considered to be the "current absolute path inside |
|
70 |
+ // root" when we append paths to it, we also append a slash and use |
|
71 |
+ // filepath.Clean after the loop to trim the trailing slash |
|
72 |
+ for n := 0; path != ""; n++ { |
|
73 |
+ if n > maxIter { |
|
74 |
+ return "", errors.New("evalSymlinksInScope: too many links in " + originalPath) |
|
75 |
+ } |
|
39 | 76 |
|
40 |
- prev := "/" |
|
41 |
- |
|
42 |
- for _, p := range strings.Split(link, "/") { |
|
43 |
- prev = filepath.Join(prev, p) |
|
44 |
- |
|
45 |
- loopCounter := 0 |
|
46 |
- for { |
|
47 |
- loopCounter++ |
|
48 |
- |
|
49 |
- if loopCounter >= maxLoopCounter { |
|
50 |
- return "", fmt.Errorf("loopCounter reached MAX: %v", loopCounter) |
|
51 |
- } |
|
52 |
- |
|
53 |
- if !strings.HasPrefix(prev, root) { |
|
54 |
- // Don't resolve symlinks outside of root. For example, |
|
55 |
- // we don't have to check /home in the below. |
|
56 |
- // |
|
57 |
- // /home -> usr/home |
|
58 |
- // FollowSymlinkInScope("/home/bob/foo/bar", "/home/bob/foo") |
|
59 |
- break |
|
60 |
- } |
|
61 |
- |
|
62 |
- stat, err := os.Lstat(prev) |
|
63 |
- if err != nil { |
|
64 |
- if os.IsNotExist(err) { |
|
65 |
- break |
|
66 |
- } |
|
67 |
- return "", err |
|
68 |
- } |
|
69 |
- |
|
70 |
- // let's break if we're not dealing with a symlink |
|
71 |
- if stat.Mode()&os.ModeSymlink != os.ModeSymlink { |
|
72 |
- break |
|
73 |
- } |
|
77 |
+ // find next path component, p |
|
78 |
+ i := strings.IndexRune(path, filepath.Separator) |
|
79 |
+ var p string |
|
80 |
+ if i == -1 { |
|
81 |
+ p, path = path, "" |
|
82 |
+ } else { |
|
83 |
+ p, path = path[:i], path[i+1:] |
|
84 |
+ } |
|
74 | 85 |
|
75 |
- // process the symlink |
|
76 |
- dest, err := os.Readlink(prev) |
|
77 |
- if err != nil { |
|
78 |
- return "", err |
|
79 |
- } |
|
86 |
+ if p == "" { |
|
87 |
+ continue |
|
88 |
+ } |
|
80 | 89 |
|
81 |
- if path.IsAbs(dest) { |
|
82 |
- prev = filepath.Join(root, dest) |
|
83 |
- } else { |
|
84 |
- prev, _ = filepath.Abs(prev) |
|
90 |
+ // this takes a b.String() like "b/../" and a p like "c" and turns it |
|
91 |
+ // into "/b/../c" which then gets filepath.Cleaned into "/c" and then |
|
92 |
+ // root gets prepended and we Clean again (to remove any trailing slash |
|
93 |
+ // if the first Clean gave us just "/") |
|
94 |
+ cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p) |
|
95 |
+ if cleanP == string(filepath.Separator) { |
|
96 |
+ // never Lstat "/" itself |
|
97 |
+ b.Reset() |
|
98 |
+ continue |
|
99 |
+ } |
|
100 |
+ fullP := filepath.Clean(root + cleanP) |
|
101 |
+ |
|
102 |
+ fi, err := os.Lstat(fullP) |
|
103 |
+ if os.IsNotExist(err) { |
|
104 |
+ // if p does not exist, accept it |
|
105 |
+ b.WriteString(p) |
|
106 |
+ b.WriteRune(filepath.Separator) |
|
107 |
+ continue |
|
108 |
+ } |
|
109 |
+ if err != nil { |
|
110 |
+ return "", err |
|
111 |
+ } |
|
112 |
+ if fi.Mode()&os.ModeSymlink == 0 { |
|
113 |
+ b.WriteString(p + string(filepath.Separator)) |
|
114 |
+ continue |
|
115 |
+ } |
|
85 | 116 |
|
86 |
- dir := filepath.Dir(prev) |
|
87 |
- prev = filepath.Join(dir, dest) |
|
88 |
- if dir == root && !strings.HasPrefix(prev, root) { |
|
89 |
- prev = root |
|
90 |
- } |
|
91 |
- if len(prev) < len(root) || (len(prev) == len(root) && prev != root) { |
|
92 |
- prev = filepath.Join(root, filepath.Base(dest)) |
|
93 |
- } |
|
94 |
- } |
|
117 |
+ // it's a symlink, put it at the front of path |
|
118 |
+ dest, err := os.Readlink(fullP) |
|
119 |
+ if err != nil { |
|
120 |
+ return "", err |
|
95 | 121 |
} |
122 |
+ if filepath.IsAbs(dest) { |
|
123 |
+ b.Reset() |
|
124 |
+ } |
|
125 |
+ path = dest + string(filepath.Separator) + path |
|
96 | 126 |
} |
97 |
- if prev == "/" { |
|
98 |
- prev = root |
|
99 |
- } |
|
100 |
- return prev, nil |
|
127 |
+ |
|
128 |
+ // see note above on "fullP := ..." for why this is double-cleaned and |
|
129 |
+ // what's happening here |
|
130 |
+ return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil |
|
101 | 131 |
} |